1 /*
2  * Cogl
3  *
4  * A Low Level GPU Graphics and Utilities API
5  *
6  * Copyright (C) 2007,2008,2009,2010,2011,2012 Intel Corporation.
7  *
8  * Permission is hereby granted, free of charge, to any person
9  * obtaining a copy of this software and associated documentation
10  * files (the "Software"), to deal in the Software without
11  * restriction, including without limitation the rights to use, copy,
12  * modify, merge, publish, distribute, sublicense, and/or sell copies
13  * of the Software, and to permit persons to whom the Software is
14  * furnished to do so, subject to the following conditions:
15  *
16  * The above copyright notice and this permission notice shall be
17  * included in all copies or substantial portions of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
23  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
24  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
25  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26  * SOFTWARE.
27  *
28  *
29  *
30  * Authors:
31  *  Neil Roberts   <neil@linux.intel.com>
32  *  Robert Bragg   <robert@linux.intel.com>
33  */
34 
35 #include "cogl-config.h"
36 
37 #include "cogl-context-private.h"
38 #include "cogl-graphene.h"
39 #include "cogl-primitives-private.h"
40 #include "cogl-primitive-private.h"
41 #include "driver/gl/cogl-util-gl-private.h"
42 #include "driver/gl/cogl-pipeline-opengl-private.h"
43 #include "driver/gl/cogl-clip-stack-gl-private.h"
44 
45 static void
add_stencil_clip_rectangle(CoglFramebuffer * framebuffer,CoglMatrixEntry * modelview_entry,float x_1,float y_1,float x_2,float y_2,gboolean merge)46 add_stencil_clip_rectangle (CoglFramebuffer *framebuffer,
47                             CoglMatrixEntry *modelview_entry,
48                             float x_1,
49                             float y_1,
50                             float x_2,
51                             float y_2,
52                             gboolean merge)
53 {
54   CoglMatrixStack *projection_stack =
55     _cogl_framebuffer_get_projection_stack (framebuffer);
56   CoglContext *ctx = cogl_framebuffer_get_context (framebuffer);
57   CoglMatrixEntry *old_projection_entry, *old_modelview_entry;
58 
59   /* NB: This can be called while flushing the journal so we need
60    * to be very conservative with what state we change.
61    */
62   old_projection_entry = g_steal_pointer (&ctx->current_projection_entry);
63   old_modelview_entry = g_steal_pointer (&ctx->current_modelview_entry);
64 
65   ctx->current_projection_entry = projection_stack->last_entry;
66   ctx->current_modelview_entry = modelview_entry;
67 
68   GE( ctx, glColorMask (FALSE, FALSE, FALSE, FALSE) );
69   GE( ctx, glDepthMask (FALSE) );
70   GE( ctx, glStencilMask (0x3) );
71 
72   if (merge)
73     {
74       /* Add one to every pixel of the stencil buffer in the
75 	 rectangle */
76       GE( ctx, glStencilFunc (GL_NEVER, 0x1, 0x3) );
77       GE( ctx, glStencilOp (GL_INCR, GL_INCR, GL_INCR) );
78       _cogl_rectangle_immediate (framebuffer,
79                                  ctx->stencil_pipeline,
80                                  x_1, y_1, x_2, y_2);
81 
82       /* Subtract one from all pixels in the stencil buffer so that
83 	 only pixels where both the original stencil buffer and the
84 	 rectangle are set will be valid */
85       GE( ctx, glStencilOp (GL_DECR, GL_DECR, GL_DECR) );
86 
87       ctx->current_projection_entry = &ctx->identity_entry;
88       ctx->current_modelview_entry = &ctx->identity_entry;
89 
90       _cogl_rectangle_immediate (framebuffer,
91                                  ctx->stencil_pipeline,
92                                  -1.0, -1.0, 1.0, 1.0);
93     }
94   else
95     {
96       GE( ctx, glEnable (GL_STENCIL_TEST) );
97 
98       /* Initially disallow everything */
99       GE( ctx, glClearStencil (0) );
100       GE( ctx, glClear (GL_STENCIL_BUFFER_BIT) );
101 
102       /* Punch out a hole to allow the rectangle */
103       GE( ctx, glStencilFunc (GL_ALWAYS, 0x1, 0x1) );
104       GE( ctx, glStencilOp (GL_KEEP, GL_KEEP, GL_REPLACE) );
105       _cogl_rectangle_immediate (framebuffer,
106                                  ctx->stencil_pipeline,
107                                  x_1, y_1, x_2, y_2);
108     }
109 
110   ctx->current_projection_entry = old_projection_entry;
111   ctx->current_modelview_entry = old_modelview_entry;
112 
113   /* Restore the stencil mode */
114   GE( ctx, glDepthMask (TRUE) );
115   GE( ctx, glColorMask (TRUE, TRUE, TRUE, TRUE) );
116   GE( ctx, glStencilMask (0x0) );
117   GE( ctx, glStencilFunc (GL_EQUAL, 0x1, 0x1) );
118   GE( ctx, glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) );
119 }
120 
121 static void
add_stencil_clip_region(CoglFramebuffer * framebuffer,cairo_region_t * region,gboolean merge)122 add_stencil_clip_region (CoglFramebuffer *framebuffer,
123                          cairo_region_t  *region,
124                          gboolean         merge)
125 {
126   CoglContext *ctx = cogl_framebuffer_get_context (framebuffer);
127   CoglMatrixEntry *old_projection_entry, *old_modelview_entry;
128   graphene_matrix_t matrix;
129   int num_rectangles = cairo_region_num_rectangles (region);
130   int i;
131   CoglVertexP2 *vertices;
132   graphene_point3d_t p;
133 
134   /* NB: This can be called while flushing the journal so we need
135    * to be very conservative with what state we change.
136    */
137   old_projection_entry = g_steal_pointer (&ctx->current_projection_entry);
138   old_modelview_entry = g_steal_pointer (&ctx->current_modelview_entry);
139 
140   ctx->current_projection_entry = &ctx->identity_entry;
141   ctx->current_modelview_entry = &ctx->identity_entry;
142 
143   /* The coordinates in the region are meant to be window coordinates,
144    * make a matrix that translates those across the viewport, and into
145    * the default [-1, -1, 1, 1] range.
146    */
147   graphene_point3d_init (&p,
148                          - cogl_framebuffer_get_viewport_x (framebuffer),
149                          - cogl_framebuffer_get_viewport_y (framebuffer),
150                          0);
151 
152   graphene_matrix_init_translate (&matrix, &p);
153   graphene_matrix_scale (&matrix,
154                          2.0 / cogl_framebuffer_get_viewport_width (framebuffer),
155                          - 2.0 / cogl_framebuffer_get_viewport_height (framebuffer),
156                          1);
157   graphene_matrix_translate (&matrix, &GRAPHENE_POINT3D_INIT (-1.f, 1.f, 0.f));
158 
159   GE( ctx, glColorMask (FALSE, FALSE, FALSE, FALSE) );
160   GE( ctx, glDepthMask (FALSE) );
161   GE( ctx, glStencilMask (0x3) );
162 
163   if (merge)
164     {
165       GE( ctx, glStencilFunc (GL_ALWAYS, 0x1, 0x3) );
166       GE( ctx, glStencilOp (GL_KEEP, GL_KEEP, GL_INCR) );
167     }
168   else
169     {
170       GE( ctx, glEnable (GL_STENCIL_TEST) );
171 
172       /* Initially disallow everything */
173       GE( ctx, glClearStencil (0) );
174       GE( ctx, glClear (GL_STENCIL_BUFFER_BIT) );
175 
176       /* Punch out holes to allow the rectangles */
177       GE( ctx, glStencilFunc (GL_ALWAYS, 0x1, 0x1) );
178       GE( ctx, glStencilOp (GL_KEEP, GL_KEEP, GL_REPLACE) );
179     }
180 
181   vertices = g_alloca (sizeof (CoglVertexP2) * num_rectangles * 6);
182 
183   for (i = 0; i < num_rectangles; i++)
184     {
185       cairo_rectangle_int_t rect;
186       float x1, y1, z1, w1;
187       float x2, y2, z2, w2;
188       CoglVertexP2 *v = vertices + i * 6;
189 
190       cairo_region_get_rectangle (region, i, &rect);
191 
192       x1 = rect.x;
193       y1 = rect.y;
194       z1 = 0.f;
195       w1 = 1.f;
196 
197       x2 = rect.x + rect.width;
198       y2 = rect.y + rect.height;
199       z2 = 0.f;
200       w2 = 1.f;
201 
202       cogl_graphene_matrix_project_point (&matrix, &x1, &y1, &z1, &w1);
203       cogl_graphene_matrix_project_point (&matrix, &x2, &y2, &z2, &w2);
204 
205       v[0].x = x1;
206       v[0].y = y1;
207       v[1].x = x1;
208       v[1].y = y2;
209       v[2].x = x2;
210       v[2].y = y1;
211       v[3].x = x1;
212       v[3].y = y2;
213       v[4].x = x2;
214       v[4].y = y2;
215       v[5].x = x2;
216       v[5].y = y1;
217     }
218 
219   cogl_2d_primitives_immediate (framebuffer,
220                                 ctx->stencil_pipeline,
221                                 COGL_VERTICES_MODE_TRIANGLES,
222                                 vertices,
223                                 6 * num_rectangles);
224 
225   if (merge)
226     {
227       /* Subtract one from all pixels in the stencil buffer so that
228        * only pixels where both the original stencil buffer and the
229        * region are set will be valid
230        */
231       GE( ctx, glStencilOp (GL_KEEP, GL_KEEP, GL_DECR) );
232       _cogl_rectangle_immediate (framebuffer,
233                                  ctx->stencil_pipeline,
234                                  -1.0, -1.0, 1.0, 1.0);
235     }
236 
237   ctx->current_projection_entry = old_projection_entry;
238   ctx->current_modelview_entry = old_modelview_entry;
239 
240   /* Restore the stencil mode */
241   GE (ctx, glDepthMask (TRUE));
242   GE (ctx, glColorMask (TRUE, TRUE, TRUE, TRUE));
243   GE( ctx, glStencilMask (0x0) );
244   GE( ctx, glStencilFunc (GL_EQUAL, 0x1, 0x1) );
245   GE( ctx, glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) );
246 }
247 
248 typedef void (*SilhouettePaintCallback) (CoglFramebuffer *framebuffer,
249                                          CoglPipeline *pipeline,
250                                          void *user_data);
251 
252 static void
add_stencil_clip_silhouette(CoglFramebuffer * framebuffer,SilhouettePaintCallback silhouette_callback,CoglMatrixEntry * modelview_entry,float bounds_x1,float bounds_y1,float bounds_x2,float bounds_y2,gboolean merge,gboolean need_clear,void * user_data)253 add_stencil_clip_silhouette (CoglFramebuffer *framebuffer,
254                              SilhouettePaintCallback silhouette_callback,
255                              CoglMatrixEntry *modelview_entry,
256                              float bounds_x1,
257                              float bounds_y1,
258                              float bounds_x2,
259                              float bounds_y2,
260                              gboolean merge,
261                              gboolean need_clear,
262                              void *user_data)
263 {
264   CoglMatrixStack *projection_stack =
265     _cogl_framebuffer_get_projection_stack (framebuffer);
266   CoglContext *ctx = cogl_framebuffer_get_context (framebuffer);
267   CoglMatrixEntry *old_projection_entry, *old_modelview_entry;
268 
269   /* NB: This can be called while flushing the journal so we need
270    * to be very conservative with what state we change.
271    */
272   old_projection_entry = g_steal_pointer (&ctx->current_projection_entry);
273   old_modelview_entry = g_steal_pointer (&ctx->current_modelview_entry);
274 
275   ctx->current_projection_entry = projection_stack->last_entry;
276   ctx->current_modelview_entry = modelview_entry;
277 
278   _cogl_pipeline_flush_gl_state (ctx, ctx->stencil_pipeline,
279                                  framebuffer, FALSE, FALSE);
280 
281   GE( ctx, glEnable (GL_STENCIL_TEST) );
282 
283   GE( ctx, glColorMask (FALSE, FALSE, FALSE, FALSE) );
284   GE( ctx, glDepthMask (FALSE) );
285 
286   if (merge)
287     {
288       GE (ctx, glStencilMask (2));
289       GE (ctx, glStencilFunc (GL_LEQUAL, 0x2, 0x6));
290     }
291   else
292     {
293       /* If we're not using the stencil buffer for clipping then we
294          don't need to clear the whole stencil buffer, just the area
295          that will be drawn */
296       if (need_clear)
297         /* If this is being called from the clip stack code then it
298            will have set up a scissor for the minimum bounding box of
299            all of the clips. That box will likely mean that this
300            _cogl_clear won't need to clear the entire
301            buffer. _cogl_framebuffer_clear_without_flush4f is used instead
302            of cogl_clear because it won't try to flush the journal */
303         _cogl_framebuffer_clear_without_flush4f (framebuffer,
304                                                  COGL_BUFFER_BIT_STENCIL,
305                                                  0, 0, 0, 0);
306       else
307         {
308           /* Just clear the bounding box */
309           GE( ctx, glStencilMask (~(GLuint) 0) );
310           GE( ctx, glStencilOp (GL_ZERO, GL_ZERO, GL_ZERO) );
311           _cogl_rectangle_immediate (framebuffer,
312                                      ctx->stencil_pipeline,
313                                      bounds_x1, bounds_y1,
314                                      bounds_x2, bounds_y2);
315         }
316       GE (ctx, glStencilMask (1));
317       GE (ctx, glStencilFunc (GL_LEQUAL, 0x1, 0x3));
318     }
319 
320   GE (ctx, glStencilOp (GL_INVERT, GL_INVERT, GL_INVERT));
321 
322   silhouette_callback (framebuffer, ctx->stencil_pipeline, user_data);
323 
324   if (merge)
325     {
326       /* Now we have the new stencil buffer in bit 1 and the old
327          stencil buffer in bit 0 so we need to intersect them */
328       GE (ctx, glStencilMask (3));
329       GE (ctx, glStencilFunc (GL_NEVER, 0x2, 0x3));
330       GE (ctx, glStencilOp (GL_DECR, GL_DECR, GL_DECR));
331       /* Decrement all of the bits twice so that only pixels where the
332          value is 3 will remain */
333 
334       ctx->current_projection_entry = &ctx->identity_entry;
335       ctx->current_modelview_entry = &ctx->identity_entry;
336 
337       _cogl_rectangle_immediate (framebuffer, ctx->stencil_pipeline,
338                                  -1.0, -1.0, 1.0, 1.0);
339       _cogl_rectangle_immediate (framebuffer, ctx->stencil_pipeline,
340                                  -1.0, -1.0, 1.0, 1.0);
341     }
342 
343   ctx->current_projection_entry = old_projection_entry;
344   ctx->current_modelview_entry = old_modelview_entry;
345 
346   GE (ctx, glStencilMask (~(GLuint) 0));
347   GE (ctx, glDepthMask (TRUE));
348   GE (ctx, glColorMask (TRUE, TRUE, TRUE, TRUE));
349 
350   GE (ctx, glStencilFunc (GL_EQUAL, 0x1, 0x1));
351   GE (ctx, glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP));
352 }
353 
354 static void
paint_primitive_silhouette(CoglFramebuffer * framebuffer,CoglPipeline * pipeline,void * user_data)355 paint_primitive_silhouette (CoglFramebuffer *framebuffer,
356                             CoglPipeline *pipeline,
357                             void *user_data)
358 {
359   _cogl_primitive_draw (user_data,
360                         framebuffer,
361                         pipeline,
362                         COGL_DRAW_SKIP_JOURNAL_FLUSH |
363                         COGL_DRAW_SKIP_PIPELINE_VALIDATION |
364                         COGL_DRAW_SKIP_FRAMEBUFFER_FLUSH);
365 }
366 
367 static void
add_stencil_clip_primitive(CoglFramebuffer * framebuffer,CoglMatrixEntry * modelview_entry,CoglPrimitive * primitive,float bounds_x1,float bounds_y1,float bounds_x2,float bounds_y2,gboolean merge,gboolean need_clear)368 add_stencil_clip_primitive (CoglFramebuffer *framebuffer,
369                             CoglMatrixEntry *modelview_entry,
370                             CoglPrimitive *primitive,
371                             float bounds_x1,
372                             float bounds_y1,
373                             float bounds_x2,
374                             float bounds_y2,
375                             gboolean merge,
376                             gboolean need_clear)
377 {
378   add_stencil_clip_silhouette (framebuffer,
379                                paint_primitive_silhouette,
380                                modelview_entry,
381                                bounds_x1,
382                                bounds_y1,
383                                bounds_x2,
384                                bounds_y2,
385                                merge,
386                                need_clear,
387                                primitive);
388 }
389 
390 void
_cogl_clip_stack_gl_flush(CoglClipStack * stack,CoglFramebuffer * framebuffer)391 _cogl_clip_stack_gl_flush (CoglClipStack *stack,
392                            CoglFramebuffer *framebuffer)
393 {
394   CoglContext *ctx = cogl_framebuffer_get_context (framebuffer);
395   gboolean using_stencil_buffer = FALSE;
396   int scissor_x0;
397   int scissor_y0;
398   int scissor_x1;
399   int scissor_y1;
400   CoglClipStack *entry;
401   int scissor_y_start;
402 
403   /* If we have already flushed this state then we don't need to do
404      anything */
405   if (ctx->current_clip_stack_valid)
406     {
407       if (ctx->current_clip_stack == stack)
408         return;
409 
410       _cogl_clip_stack_unref (ctx->current_clip_stack);
411     }
412 
413   ctx->current_clip_stack_valid = TRUE;
414   ctx->current_clip_stack = _cogl_clip_stack_ref (stack);
415 
416   GE( ctx, glDisable (GL_STENCIL_TEST) );
417 
418   /* If the stack is empty then there's nothing else to do
419    */
420   if (stack == NULL)
421     {
422       COGL_NOTE (CLIPPING, "Flushed empty clip stack");
423 
424       GE (ctx, glDisable (GL_SCISSOR_TEST));
425       return;
426     }
427 
428   /* Calculate the scissor rect first so that if we eventually have to
429      clear the stencil buffer then the clear will be clipped to the
430      intersection of all of the bounding boxes. This saves having to
431      clear the whole stencil buffer */
432   _cogl_clip_stack_get_bounds (stack,
433                                &scissor_x0, &scissor_y0,
434                                &scissor_x1, &scissor_y1);
435 
436   /* Enable scissoring as soon as possible */
437   if (scissor_x0 >= scissor_x1 || scissor_y0 >= scissor_y1)
438     scissor_x0 = scissor_y0 = scissor_x1 = scissor_y1 = scissor_y_start = 0;
439   else
440     {
441       /* We store the entry coordinates in Cogl coordinate space
442        * but OpenGL requires the window origin to be the bottom
443        * left so we may need to convert the incoming coordinates.
444        *
445        * NB: Cogl forces all offscreen rendering to be done upside
446        * down so in this case no conversion is needed.
447        */
448 
449       if (cogl_framebuffer_is_y_flipped (framebuffer))
450         {
451           scissor_y_start = scissor_y0;
452         }
453       else
454         {
455           int framebuffer_height =
456             cogl_framebuffer_get_height (framebuffer);
457 
458           scissor_y_start = framebuffer_height - scissor_y1;
459         }
460     }
461 
462   COGL_NOTE (CLIPPING, "Flushing scissor to (%i, %i, %i, %i)",
463              scissor_x0, scissor_y0,
464              scissor_x1, scissor_y1);
465 
466   GE (ctx, glEnable (GL_SCISSOR_TEST));
467   GE (ctx, glScissor (scissor_x0, scissor_y_start,
468                       scissor_x1 - scissor_x0,
469                       scissor_y1 - scissor_y0));
470 
471   /* Add all of the entries. This will end up adding them in the
472      reverse order that they were specified but as all of the clips
473      are intersecting it should work out the same regardless of the
474      order */
475   for (entry = stack; entry; entry = entry->parent)
476     {
477       switch (entry->type)
478         {
479         case COGL_CLIP_STACK_PRIMITIVE:
480             {
481               CoglClipStackPrimitive *primitive_entry =
482                 (CoglClipStackPrimitive *) entry;
483 
484               COGL_NOTE (CLIPPING, "Adding stencil clip for primitive");
485 
486               add_stencil_clip_primitive (framebuffer,
487                                           primitive_entry->matrix_entry,
488                                           primitive_entry->primitive,
489                                           primitive_entry->bounds_x1,
490                                           primitive_entry->bounds_y1,
491                                           primitive_entry->bounds_x2,
492                                           primitive_entry->bounds_y2,
493                                           using_stencil_buffer,
494                                           TRUE);
495 
496               using_stencil_buffer = TRUE;
497               break;
498             }
499         case COGL_CLIP_STACK_RECT:
500             {
501               CoglClipStackRect *rect = (CoglClipStackRect *) entry;
502 
503               /* We don't need to do anything extra if the clip for this
504                  rectangle was entirely described by its scissor bounds */
505               if (!rect->can_be_scissor ||
506                   G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_STENCILLING)))
507                 {
508                   COGL_NOTE (CLIPPING, "Adding stencil clip for rectangle");
509 
510                   add_stencil_clip_rectangle (framebuffer,
511                                               rect->matrix_entry,
512                                               rect->x0,
513                                               rect->y0,
514                                               rect->x1,
515                                               rect->y1,
516                                               using_stencil_buffer);
517                   using_stencil_buffer = TRUE;
518                 }
519               break;
520             }
521         case COGL_CLIP_STACK_REGION:
522             {
523               CoglClipStackRegion *region = (CoglClipStackRegion *) entry;
524 
525               /* If nrectangles <= 1, it can be fully represented with the
526                * scissor clip.
527                */
528               if (cairo_region_num_rectangles (region->region) > 1 ||
529                   G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_STENCILLING)))
530                 {
531                   COGL_NOTE (CLIPPING, "Adding stencil clip for region");
532 
533                   add_stencil_clip_region (framebuffer, region->region,
534                                            using_stencil_buffer);
535                   using_stencil_buffer = TRUE;
536                 }
537               break;
538             }
539         case COGL_CLIP_STACK_WINDOW_RECT:
540           break;
541           /* We don't need to do anything for window space rectangles because
542            * their functionality is entirely implemented by the entry bounding
543            * box */
544         }
545     }
546 }
547