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