1 /*
2 * Cogl
3 *
4 * A Low Level GPU Graphics and Utilities API
5 *
6 * Copyright (C) 2007,2008,2009 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
31 #ifdef HAVE_CONFIG_H
32 #include "cogl-config.h"
33 #endif
34
35 #include "cogl-debug.h"
36 #include "cogl-context-private.h"
37 #include "cogl-journal-private.h"
38 #include "cogl-texture-private.h"
39 #include "cogl-pipeline-private.h"
40 #include "cogl-pipeline-opengl-private.h"
41 #include "cogl-vertex-buffer-private.h"
42 #include "cogl-framebuffer-private.h"
43 #include "cogl-profile.h"
44 #include "cogl-attribute-private.h"
45 #include "cogl-point-in-poly-private.h"
46 #include "cogl-private.h"
47 #include "cogl1-context.h"
48
49 #include <string.h>
50 #include <gmodule.h>
51 #include <math.h>
52
53 /* XXX NB:
54 * The data logged in logged_vertices is formatted as follows:
55 *
56 * Per entry:
57 * 4 RGBA GLubytes for the color
58 * 2 floats for the top left position
59 * 2 * n_layers floats for the top left texture coordinates
60 * 2 floats for the bottom right position
61 * 2 * n_layers floats for the bottom right texture coordinates
62 */
63 #define GET_JOURNAL_ARRAY_STRIDE_FOR_N_LAYERS(N_LAYERS) \
64 (N_LAYERS * 2 + 2)
65
66 /* XXX NB:
67 * Once in the vertex array, the journal's vertex data is arranged as follows:
68 * 4 vertices per quad:
69 * 2 or 3 GLfloats per position (3 when doing software transforms)
70 * 4 RGBA GLubytes,
71 * 2 GLfloats per tex coord * n_layers
72 *
73 * Where n_layers corresponds to the number of pipeline layers enabled
74 *
75 * To avoid frequent changes in the stride of our vertex data we always pad
76 * n_layers to be >= 2
77 *
78 * There will be four vertices per quad in the vertex array
79 *
80 * When we are transforming quads in software we need to also track the z
81 * coordinate of transformed vertices.
82 *
83 * So for a given number of layers this gets the stride in 32bit words:
84 */
85 #define SW_TRANSFORM (!(COGL_DEBUG_ENABLED \
86 (COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM)))
87 #define POS_STRIDE (SW_TRANSFORM ? 3 : 2) /* number of 32bit words */
88 #define N_POS_COMPONENTS POS_STRIDE
89 #define COLOR_STRIDE 1 /* number of 32bit words */
90 #define TEX_STRIDE 2 /* number of 32bit words */
91 #define MIN_LAYER_PADING 2
92 #define GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS(N_LAYERS) \
93 (POS_STRIDE + COLOR_STRIDE + \
94 TEX_STRIDE * (N_LAYERS < MIN_LAYER_PADING ? MIN_LAYER_PADING : N_LAYERS))
95
96 /* If a batch is longer than this threshold then we'll assume it's not
97 worth doing software clipping and it's cheaper to program the GPU
98 to do the clip */
99 #define COGL_JOURNAL_HARDWARE_CLIP_THRESHOLD 8
100
101 typedef struct _CoglJournalFlushState
102 {
103 CoglContext *ctx;
104
105 CoglJournal *journal;
106
107 CoglAttributeBuffer *attribute_buffer;
108 GArray *attributes;
109 int current_attribute;
110
111 size_t stride;
112 size_t array_offset;
113 GLuint current_vertex;
114
115 CoglIndices *indices;
116 size_t indices_type_size;
117
118 CoglPipeline *pipeline;
119 } CoglJournalFlushState;
120
121 typedef void (*CoglJournalBatchCallback) (CoglJournalEntry *start,
122 int n_entries,
123 void *data);
124 typedef CoglBool (*CoglJournalBatchTest) (CoglJournalEntry *entry0,
125 CoglJournalEntry *entry1);
126
127 static void _cogl_journal_free (CoglJournal *journal);
128
129 COGL_OBJECT_INTERNAL_DEFINE (Journal, journal);
130
131 static void
_cogl_journal_free(CoglJournal * journal)132 _cogl_journal_free (CoglJournal *journal)
133 {
134 int i;
135
136 if (journal->entries)
137 g_array_free (journal->entries, TRUE);
138 if (journal->vertices)
139 g_array_free (journal->vertices, TRUE);
140
141 for (i = 0; i < COGL_JOURNAL_VBO_POOL_SIZE; i++)
142 if (journal->vbo_pool[i])
143 cogl_object_unref (journal->vbo_pool[i]);
144
145 g_slice_free (CoglJournal, journal);
146 }
147
148 CoglJournal *
_cogl_journal_new(CoglFramebuffer * framebuffer)149 _cogl_journal_new (CoglFramebuffer *framebuffer)
150 {
151 CoglJournal *journal = g_slice_new0 (CoglJournal);
152
153 /* The journal keeps a pointer back to the framebuffer because there
154 is effectively a 1:1 mapping between journals and framebuffers.
155 However, to avoid a circular reference the journal doesn't take a
156 reference unless it is non-empty. The framebuffer has a special
157 unref implementation to ensure that the journal is flushed when
158 the journal is the only thing keeping it alive */
159 journal->framebuffer = framebuffer;
160
161 journal->entries = g_array_new (FALSE, FALSE, sizeof (CoglJournalEntry));
162 journal->vertices = g_array_new (FALSE, FALSE, sizeof (float));
163
164 _cogl_list_init (&journal->pending_fences);
165
166 return _cogl_journal_object_new (journal);
167 }
168
169 static void
_cogl_journal_dump_logged_quad(uint8_t * data,int n_layers)170 _cogl_journal_dump_logged_quad (uint8_t *data, int n_layers)
171 {
172 size_t stride = GET_JOURNAL_ARRAY_STRIDE_FOR_N_LAYERS (n_layers);
173 int i;
174
175 g_print ("n_layers = %d; rgba=0x%02X%02X%02X%02X\n",
176 n_layers, data[0], data[1], data[2], data[3]);
177
178 data += 4;
179
180 for (i = 0; i < 2; i++)
181 {
182 float *v = (float *)data + (i * stride);
183 int j;
184
185 g_print ("v%d: x = %f, y = %f", i, v[0], v[1]);
186
187 for (j = 0; j < n_layers; j++)
188 {
189 float *t = v + 2 + TEX_STRIDE * j;
190 g_print (", tx%d = %f, ty%d = %f", j, t[0], j, t[1]);
191 }
192 g_print ("\n");
193 }
194 }
195
196 static void
_cogl_journal_dump_quad_vertices(uint8_t * data,int n_layers)197 _cogl_journal_dump_quad_vertices (uint8_t *data, int n_layers)
198 {
199 size_t stride = GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS (n_layers);
200 int i;
201
202 g_print ("n_layers = %d; stride = %d; pos stride = %d; color stride = %d; "
203 "tex stride = %d; stride in bytes = %d\n",
204 n_layers, (int)stride, POS_STRIDE, COLOR_STRIDE,
205 TEX_STRIDE, (int)stride * 4);
206
207 for (i = 0; i < 4; i++)
208 {
209 float *v = (float *)data + (i * stride);
210 uint8_t *c = data + (POS_STRIDE * 4) + (i * stride * 4);
211 int j;
212
213 if (G_UNLIKELY (COGL_DEBUG_ENABLED
214 (COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM)))
215 g_print ("v%d: x = %f, y = %f, rgba=0x%02X%02X%02X%02X",
216 i, v[0], v[1], c[0], c[1], c[2], c[3]);
217 else
218 g_print ("v%d: x = %f, y = %f, z = %f, rgba=0x%02X%02X%02X%02X",
219 i, v[0], v[1], v[2], c[0], c[1], c[2], c[3]);
220 for (j = 0; j < n_layers; j++)
221 {
222 float *t = v + POS_STRIDE + COLOR_STRIDE + TEX_STRIDE * j;
223 g_print (", tx%d = %f, ty%d = %f", j, t[0], j, t[1]);
224 }
225 g_print ("\n");
226 }
227 }
228
229 static void
_cogl_journal_dump_quad_batch(uint8_t * data,int n_layers,int n_quads)230 _cogl_journal_dump_quad_batch (uint8_t *data, int n_layers, int n_quads)
231 {
232 size_t byte_stride = GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS (n_layers) * 4;
233 int i;
234
235 g_print ("_cogl_journal_dump_quad_batch: n_layers = %d, n_quads = %d\n",
236 n_layers, n_quads);
237 for (i = 0; i < n_quads; i++)
238 _cogl_journal_dump_quad_vertices (data + byte_stride * 2 * i, n_layers);
239 }
240
241 static void
batch_and_call(CoglJournalEntry * entries,int n_entries,CoglJournalBatchTest can_batch_callback,CoglJournalBatchCallback batch_callback,void * data)242 batch_and_call (CoglJournalEntry *entries,
243 int n_entries,
244 CoglJournalBatchTest can_batch_callback,
245 CoglJournalBatchCallback batch_callback,
246 void *data)
247 {
248 int i;
249 int batch_len = 1;
250 CoglJournalEntry *batch_start = entries;
251
252 if (n_entries < 1)
253 return;
254
255 for (i = 1; i < n_entries; i++)
256 {
257 CoglJournalEntry *entry0 = &entries[i - 1];
258 CoglJournalEntry *entry1 = entry0 + 1;
259
260 if (can_batch_callback (entry0, entry1))
261 {
262 batch_len++;
263 continue;
264 }
265
266 batch_callback (batch_start, batch_len, data);
267
268 batch_start = entry1;
269 batch_len = 1;
270 }
271
272 /* The last batch... */
273 batch_callback (batch_start, batch_len, data);
274 }
275
276 static void
_cogl_journal_flush_modelview_and_entries(CoglJournalEntry * batch_start,int batch_len,void * data)277 _cogl_journal_flush_modelview_and_entries (CoglJournalEntry *batch_start,
278 int batch_len,
279 void *data)
280 {
281 CoglJournalFlushState *state = data;
282 CoglContext *ctx = state->ctx;
283 CoglFramebuffer *framebuffer = state->journal->framebuffer;
284 CoglAttribute **attributes;
285 CoglDrawFlags draw_flags = (COGL_DRAW_SKIP_JOURNAL_FLUSH |
286 COGL_DRAW_SKIP_PIPELINE_VALIDATION |
287 COGL_DRAW_SKIP_FRAMEBUFFER_FLUSH |
288 COGL_DRAW_SKIP_LEGACY_STATE);
289
290 COGL_STATIC_TIMER (time_flush_modelview_and_entries,
291 "flush: pipeline+entries", /* parent */
292 "flush: modelview+entries",
293 "The time spent flushing modelview + entries",
294 0 /* no application private data */);
295
296 COGL_TIMER_START (_cogl_uprof_context, time_flush_modelview_and_entries);
297
298 if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_BATCHING)))
299 g_print ("BATCHING: modelview batch len = %d\n", batch_len);
300
301 if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM)))
302 _cogl_context_set_current_modelview_entry (ctx,
303 batch_start->modelview_entry);
304
305 attributes = (CoglAttribute **)state->attributes->data;
306
307 if (!_cogl_pipeline_get_real_blend_enabled (state->pipeline))
308 draw_flags |= COGL_DRAW_COLOR_ATTRIBUTE_IS_OPAQUE;
309
310 #ifdef HAVE_COGL_GL
311 if (_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_QUADS))
312 {
313 /* XXX: it's rather evil that we sneak in the GL_QUADS enum here... */
314 _cogl_framebuffer_draw_attributes (framebuffer,
315 state->pipeline,
316 GL_QUADS,
317 state->current_vertex, batch_len * 4,
318 attributes,
319 state->attributes->len,
320 draw_flags);
321 }
322 else
323 #endif /* HAVE_COGL_GL */
324 {
325 if (batch_len > 1)
326 {
327 CoglVerticesMode mode = COGL_VERTICES_MODE_TRIANGLES;
328 int first_vertex = state->current_vertex * 6 / 4;
329 _cogl_framebuffer_draw_indexed_attributes (framebuffer,
330 state->pipeline,
331 mode,
332 first_vertex,
333 batch_len * 6,
334 state->indices,
335 attributes,
336 state->attributes->len,
337 draw_flags);
338 }
339 else
340 {
341 _cogl_framebuffer_draw_attributes (framebuffer,
342 state->pipeline,
343 COGL_VERTICES_MODE_TRIANGLE_FAN,
344 state->current_vertex, 4,
345 attributes,
346 state->attributes->len,
347 draw_flags);
348 }
349 }
350
351 /* DEBUGGING CODE XXX: This path will cause all rectangles to be
352 * drawn with a coloured outline. Each batch will be rendered with
353 * the same color. This may e.g. help with debugging texture slicing
354 * issues, visually seeing what is batched and debugging blending
355 * issues, plus it looks quite cool.
356 */
357 if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_RECTANGLES)))
358 {
359 static CoglPipeline *outline = NULL;
360 uint8_t color_intensity;
361 int i;
362 CoglAttribute *loop_attributes[1];
363
364 if (outline == NULL)
365 outline = cogl_pipeline_new (ctx);
366
367 /* The least significant three bits represent the three
368 components so that the order of colours goes red, green,
369 yellow, blue, magenta, cyan. Black and white are skipped. The
370 next two bits give four scales of intensity for those colours
371 in the order 0xff, 0xcc, 0x99, and 0x66. This gives a total
372 of 24 colours. If there are more than 24 batches on the stage
373 then it will wrap around */
374 color_intensity = 0xff - 0x33 * (ctx->journal_rectangles_color >> 3);
375 cogl_pipeline_set_color4ub (outline,
376 (ctx->journal_rectangles_color & 1) ?
377 color_intensity : 0,
378 (ctx->journal_rectangles_color & 2) ?
379 color_intensity : 0,
380 (ctx->journal_rectangles_color & 4) ?
381 color_intensity : 0,
382 0xff);
383
384 loop_attributes[0] = attributes[0]; /* we just want the position */
385 for (i = 0; i < batch_len; i++)
386 _cogl_framebuffer_draw_attributes (framebuffer,
387 outline,
388 COGL_VERTICES_MODE_LINE_LOOP,
389 4 * i + state->current_vertex, 4,
390 loop_attributes,
391 1,
392 draw_flags);
393
394 /* Go to the next color */
395 do
396 ctx->journal_rectangles_color = ((ctx->journal_rectangles_color + 1) &
397 ((1 << 5) - 1));
398 /* We don't want to use black or white */
399 while ((ctx->journal_rectangles_color & 0x07) == 0
400 || (ctx->journal_rectangles_color & 0x07) == 0x07);
401 }
402
403 state->current_vertex += (4 * batch_len);
404
405 COGL_TIMER_STOP (_cogl_uprof_context, time_flush_modelview_and_entries);
406 }
407
408 static CoglBool
compare_entry_modelviews(CoglJournalEntry * entry0,CoglJournalEntry * entry1)409 compare_entry_modelviews (CoglJournalEntry *entry0,
410 CoglJournalEntry *entry1)
411 {
412 /* Batch together quads with the same model view matrix */
413 return entry0->modelview_entry == entry1->modelview_entry;
414 }
415
416 /* At this point we have a run of quads that we know have compatible
417 * pipelines, but they may not all have the same modelview matrix */
418 static void
_cogl_journal_flush_pipeline_and_entries(CoglJournalEntry * batch_start,int batch_len,void * data)419 _cogl_journal_flush_pipeline_and_entries (CoglJournalEntry *batch_start,
420 int batch_len,
421 void *data)
422 {
423 CoglJournalFlushState *state = data;
424 COGL_STATIC_TIMER (time_flush_pipeline_entries,
425 "flush: texcoords+pipeline+entries", /* parent */
426 "flush: pipeline+entries",
427 "The time spent flushing pipeline + entries",
428 0 /* no application private data */);
429
430 COGL_TIMER_START (_cogl_uprof_context, time_flush_pipeline_entries);
431
432 if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_BATCHING)))
433 g_print ("BATCHING: pipeline batch len = %d\n", batch_len);
434
435 state->pipeline = batch_start->pipeline;
436
437 /* If we haven't transformed the quads in software then we need to also break
438 * up batches according to changes in the modelview matrix... */
439 if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM)))
440 {
441 batch_and_call (batch_start,
442 batch_len,
443 compare_entry_modelviews,
444 _cogl_journal_flush_modelview_and_entries,
445 data);
446 }
447 else
448 _cogl_journal_flush_modelview_and_entries (batch_start, batch_len, data);
449
450 COGL_TIMER_STOP (_cogl_uprof_context, time_flush_pipeline_entries);
451 }
452
453 static CoglBool
compare_entry_pipelines(CoglJournalEntry * entry0,CoglJournalEntry * entry1)454 compare_entry_pipelines (CoglJournalEntry *entry0, CoglJournalEntry *entry1)
455 {
456 /* batch rectangles using compatible pipelines */
457
458 if (_cogl_pipeline_equal (entry0->pipeline,
459 entry1->pipeline,
460 (COGL_PIPELINE_STATE_ALL &
461 ~COGL_PIPELINE_STATE_COLOR),
462 COGL_PIPELINE_LAYER_STATE_ALL,
463 0))
464 return TRUE;
465 else
466 return FALSE;
467 }
468
469 typedef struct _CreateAttributeState
470 {
471 int current;
472 CoglJournalFlushState *flush_state;
473 } CreateAttributeState;
474
475 static CoglBool
create_attribute_cb(CoglPipeline * pipeline,int layer_number,void * user_data)476 create_attribute_cb (CoglPipeline *pipeline,
477 int layer_number,
478 void *user_data)
479 {
480 CreateAttributeState *state = user_data;
481 CoglJournalFlushState *flush_state = state->flush_state;
482 CoglAttribute **attribute_entry =
483 &g_array_index (flush_state->attributes,
484 CoglAttribute *,
485 state->current + 2);
486 const char *names[] = {
487 "cogl_tex_coord0_in",
488 "cogl_tex_coord1_in",
489 "cogl_tex_coord2_in",
490 "cogl_tex_coord3_in",
491 "cogl_tex_coord4_in",
492 "cogl_tex_coord5_in",
493 "cogl_tex_coord6_in",
494 "cogl_tex_coord7_in"
495 };
496 char *name;
497
498 /* XXX NB:
499 * Our journal's vertex data is arranged as follows:
500 * 4 vertices per quad:
501 * 2 or 3 floats per position (3 when doing software transforms)
502 * 4 RGBA bytes,
503 * 2 floats per tex coord * n_layers
504 * (though n_layers may be padded; see definition of
505 * GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS for details)
506 */
507 name = layer_number < 8 ? (char *)names[layer_number] :
508 g_strdup_printf ("cogl_tex_coord%d_in", layer_number);
509
510 /* XXX: it may be worth having some form of static initializer for
511 * attributes... */
512 *attribute_entry =
513 cogl_attribute_new (flush_state->attribute_buffer,
514 name,
515 flush_state->stride,
516 flush_state->array_offset +
517 (POS_STRIDE + COLOR_STRIDE) * 4 +
518 TEX_STRIDE * 4 * state->current,
519 2,
520 COGL_ATTRIBUTE_TYPE_FLOAT);
521
522 if (layer_number >= 8)
523 free (name);
524
525 state->current++;
526
527 return TRUE;
528 }
529
530 /* Since the stride may not reflect the number of texture layers in use
531 * (due to padding) we deal with texture coordinate offsets separately
532 * from vertex and color offsets... */
533 static void
_cogl_journal_flush_texcoord_vbo_offsets_and_entries(CoglJournalEntry * batch_start,int batch_len,void * data)534 _cogl_journal_flush_texcoord_vbo_offsets_and_entries (
535 CoglJournalEntry *batch_start,
536 int batch_len,
537 void *data)
538 {
539 CoglJournalFlushState *state = data;
540 CreateAttributeState create_attrib_state;
541 int i;
542 COGL_STATIC_TIMER (time_flush_texcoord_pipeline_entries,
543 "flush: vbo+texcoords+pipeline+entries", /* parent */
544 "flush: texcoords+pipeline+entries",
545 "The time spent flushing texcoord offsets + pipeline "
546 "+ entries",
547 0 /* no application private data */);
548
549 COGL_TIMER_START (_cogl_uprof_context, time_flush_texcoord_pipeline_entries);
550
551 /* NB: attributes 0 and 1 are position and color */
552
553 for (i = 2; i < state->attributes->len; i++)
554 cogl_object_unref (g_array_index (state->attributes, CoglAttribute *, i));
555
556 g_array_set_size (state->attributes, batch_start->n_layers + 2);
557
558 create_attrib_state.current = 0;
559 create_attrib_state.flush_state = state;
560
561 cogl_pipeline_foreach_layer (batch_start->pipeline,
562 create_attribute_cb,
563 &create_attrib_state);
564
565 batch_and_call (batch_start,
566 batch_len,
567 compare_entry_pipelines,
568 _cogl_journal_flush_pipeline_and_entries,
569 data);
570 COGL_TIMER_STOP (_cogl_uprof_context, time_flush_texcoord_pipeline_entries);
571 }
572
573 static CoglBool
compare_entry_layer_numbers(CoglJournalEntry * entry0,CoglJournalEntry * entry1)574 compare_entry_layer_numbers (CoglJournalEntry *entry0, CoglJournalEntry *entry1)
575 {
576 if (_cogl_pipeline_layer_numbers_equal (entry0->pipeline, entry1->pipeline))
577 return TRUE;
578 else
579 return FALSE;
580 }
581
582 /* At this point we know the stride has changed from the previous batch
583 * of journal entries */
584 static void
_cogl_journal_flush_vbo_offsets_and_entries(CoglJournalEntry * batch_start,int batch_len,void * data)585 _cogl_journal_flush_vbo_offsets_and_entries (CoglJournalEntry *batch_start,
586 int batch_len,
587 void *data)
588 {
589 CoglJournalFlushState *state = data;
590 CoglContext *ctx = state->journal->framebuffer->context;
591 size_t stride;
592 int i;
593 CoglAttribute **attribute_entry;
594 COGL_STATIC_TIMER (time_flush_vbo_texcoord_pipeline_entries,
595 "flush: clip+vbo+texcoords+pipeline+entries", /* parent */
596 "flush: vbo+texcoords+pipeline+entries",
597 "The time spent flushing vbo + texcoord offsets + "
598 "pipeline + entries",
599 0 /* no application private data */);
600
601 COGL_TIMER_START (_cogl_uprof_context,
602 time_flush_vbo_texcoord_pipeline_entries);
603
604 if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_BATCHING)))
605 g_print ("BATCHING: vbo offset batch len = %d\n", batch_len);
606
607 /* XXX NB:
608 * Our journal's vertex data is arranged as follows:
609 * 4 vertices per quad:
610 * 2 or 3 GLfloats per position (3 when doing software transforms)
611 * 4 RGBA GLubytes,
612 * 2 GLfloats per tex coord * n_layers
613 * (though n_layers may be padded; see definition of
614 * GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS for details)
615 */
616 stride = GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS (batch_start->n_layers);
617 stride *= sizeof (float);
618 state->stride = stride;
619
620 for (i = 0; i < state->attributes->len; i++)
621 cogl_object_unref (g_array_index (state->attributes, CoglAttribute *, i));
622
623 g_array_set_size (state->attributes, 2);
624
625 attribute_entry = &g_array_index (state->attributes, CoglAttribute *, 0);
626 *attribute_entry = cogl_attribute_new (state->attribute_buffer,
627 "cogl_position_in",
628 stride,
629 state->array_offset,
630 N_POS_COMPONENTS,
631 COGL_ATTRIBUTE_TYPE_FLOAT);
632
633 attribute_entry = &g_array_index (state->attributes, CoglAttribute *, 1);
634 *attribute_entry =
635 cogl_attribute_new (state->attribute_buffer,
636 "cogl_color_in",
637 stride,
638 state->array_offset + (POS_STRIDE * 4),
639 4,
640 COGL_ATTRIBUTE_TYPE_UNSIGNED_BYTE);
641
642 if (!_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_QUADS))
643 state->indices = cogl_get_rectangle_indices (ctx, batch_len);
644
645 /* We only create new Attributes when the stride within the
646 * AttributeBuffer changes. (due to a change in the number of pipeline
647 * layers) While the stride remains constant we walk forward through
648 * the above AttributeBuffer using a vertex offset passed to
649 * cogl_draw_attributes
650 */
651 state->current_vertex = 0;
652
653 if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_JOURNAL)) &&
654 cogl_has_feature (ctx, COGL_FEATURE_ID_MAP_BUFFER_FOR_READ))
655 {
656 uint8_t *verts;
657
658 /* Mapping a buffer for read is probably a really bad thing to
659 do but this will only happen during debugging so it probably
660 doesn't matter */
661 verts = ((uint8_t *)_cogl_buffer_map (COGL_BUFFER (state->attribute_buffer),
662 COGL_BUFFER_ACCESS_READ, 0,
663 NULL) +
664 state->array_offset);
665
666 _cogl_journal_dump_quad_batch (verts,
667 batch_start->n_layers,
668 batch_len);
669
670 cogl_buffer_unmap (COGL_BUFFER (state->attribute_buffer));
671 }
672
673 batch_and_call (batch_start,
674 batch_len,
675 compare_entry_layer_numbers,
676 _cogl_journal_flush_texcoord_vbo_offsets_and_entries,
677 data);
678
679 /* progress forward through the VBO containing all our vertices */
680 state->array_offset += (stride * 4 * batch_len);
681 if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_JOURNAL)))
682 g_print ("new vbo offset = %lu\n", (unsigned long)state->array_offset);
683
684 COGL_TIMER_STOP (_cogl_uprof_context,
685 time_flush_vbo_texcoord_pipeline_entries);
686 }
687
688 static CoglBool
compare_entry_strides(CoglJournalEntry * entry0,CoglJournalEntry * entry1)689 compare_entry_strides (CoglJournalEntry *entry0, CoglJournalEntry *entry1)
690 {
691 /* Currently the only thing that affects the stride for our vertex arrays
692 * is the number of pipeline layers. We need to update our VBO offsets
693 * whenever the stride changes. */
694 /* TODO: We should be padding the n_layers == 1 case as if it were
695 * n_layers == 2 so we can reduce the need to split batches. */
696 if (entry0->n_layers == entry1->n_layers ||
697 (entry0->n_layers <= MIN_LAYER_PADING &&
698 entry1->n_layers <= MIN_LAYER_PADING))
699 return TRUE;
700 else
701 return FALSE;
702 }
703
704 /* At this point we know the batch has a unique clip stack */
705 static void
_cogl_journal_flush_clip_stacks_and_entries(CoglJournalEntry * batch_start,int batch_len,void * data)706 _cogl_journal_flush_clip_stacks_and_entries (CoglJournalEntry *batch_start,
707 int batch_len,
708 void *data)
709 {
710 CoglJournalFlushState *state = data;
711 CoglFramebuffer *framebuffer = state->journal->framebuffer;
712 CoglContext *ctx = framebuffer->context;
713 CoglMatrixStack *projection_stack;
714
715 COGL_STATIC_TIMER (time_flush_clip_stack_pipeline_entries,
716 "Journal Flush", /* parent */
717 "flush: clip+vbo+texcoords+pipeline+entries",
718 "The time spent flushing clip + vbo + texcoord offsets + "
719 "pipeline + entries",
720 0 /* no application private data */);
721
722 COGL_TIMER_START (_cogl_uprof_context,
723 time_flush_clip_stack_pipeline_entries);
724
725 if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_BATCHING)))
726 g_print ("BATCHING: clip stack batch len = %d\n", batch_len);
727
728 _cogl_clip_stack_flush (batch_start->clip_stack, framebuffer);
729
730 /* XXX: Because we are manually flushing clip state here we need to
731 * make sure that the clip state gets updated the next time we flush
732 * framebuffer state by marking the current framebuffer's clip state
733 * as changed. */
734 ctx->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_CLIP;
735
736 /* If we have transformed all our quads at log time then we ensure
737 * no further model transform is applied by loading the identity
738 * matrix here. We need to do this after flushing the clip stack
739 * because the clip stack flushing code can modify the current
740 * modelview matrix entry */
741 if (G_LIKELY (!(COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM))))
742 _cogl_context_set_current_modelview_entry (ctx, &ctx->identity_entry);
743
744 /* Setting up the clip state can sometimes also update the current
745 * projection matrix entry so we should update it again. This will have
746 * no affect if the clip code didn't modify the projection */
747 projection_stack =
748 _cogl_framebuffer_get_projection_stack (framebuffer);
749 _cogl_context_set_current_projection_entry (ctx,
750 projection_stack->last_entry);
751
752 batch_and_call (batch_start,
753 batch_len,
754 compare_entry_strides,
755 _cogl_journal_flush_vbo_offsets_and_entries, /* callback */
756 data);
757
758 COGL_TIMER_STOP (_cogl_uprof_context,
759 time_flush_clip_stack_pipeline_entries);
760 }
761
762 typedef struct
763 {
764 float x_1, y_1;
765 float x_2, y_2;
766 } ClipBounds;
767
768 static CoglBool
can_software_clip_entry(CoglJournalEntry * journal_entry,CoglJournalEntry * prev_journal_entry,CoglClipStack * clip_stack,ClipBounds * clip_bounds_out)769 can_software_clip_entry (CoglJournalEntry *journal_entry,
770 CoglJournalEntry *prev_journal_entry,
771 CoglClipStack *clip_stack,
772 ClipBounds *clip_bounds_out)
773 {
774 CoglPipeline *pipeline = journal_entry->pipeline;
775 CoglClipStack *clip_entry;
776 int layer_num;
777
778 clip_bounds_out->x_1 = -G_MAXFLOAT;
779 clip_bounds_out->y_1 = -G_MAXFLOAT;
780 clip_bounds_out->x_2 = G_MAXFLOAT;
781 clip_bounds_out->y_2 = G_MAXFLOAT;
782
783 /* Check the pipeline is usable. We can short-cut here for
784 entries using the same pipeline as the previous entry */
785 if (prev_journal_entry == NULL || pipeline != prev_journal_entry->pipeline)
786 {
787 /* If the pipeline has a user program then we can't reliably modify
788 the texture coordinates */
789 if (cogl_pipeline_get_user_program (pipeline))
790 return FALSE;
791
792 /* If any of the pipeline layers have a texture matrix then we can't
793 reliably modify the texture coordinates */
794 for (layer_num = cogl_pipeline_get_n_layers (pipeline) - 1;
795 layer_num >= 0;
796 layer_num--)
797 if (_cogl_pipeline_layer_has_user_matrix (pipeline, layer_num))
798 return FALSE;
799 }
800
801 /* Now we need to verify that each clip entry's matrix is just a
802 translation of the journal entry's modelview matrix. We can
803 also work out the bounds of the clip in modelview space using
804 this translation */
805 for (clip_entry = clip_stack; clip_entry; clip_entry = clip_entry->parent)
806 {
807 float rect_x1, rect_y1, rect_x2, rect_y2;
808 CoglClipStackRect *clip_rect;
809 float tx, ty, tz;
810 CoglMatrixEntry *modelview_entry;
811
812 clip_rect = (CoglClipStackRect *) clip_entry;
813
814 modelview_entry = journal_entry->modelview_entry;
815 if (!cogl_matrix_entry_calculate_translation (clip_rect->matrix_entry,
816 modelview_entry,
817 &tx, &ty, &tz))
818 return FALSE;
819
820 if (clip_rect->x0 < clip_rect->x1)
821 {
822 rect_x1 = clip_rect->x0;
823 rect_x2 = clip_rect->x1;
824 }
825 else
826 {
827 rect_x1 = clip_rect->x1;
828 rect_x2 = clip_rect->x0;
829 }
830 if (clip_rect->y0 < clip_rect->y1)
831 {
832 rect_y1 = clip_rect->y0;
833 rect_y2 = clip_rect->y1;
834 }
835 else
836 {
837 rect_y1 = clip_rect->y1;
838 rect_y2 = clip_rect->y0;
839 }
840
841 clip_bounds_out->x_1 = MAX (clip_bounds_out->x_1, rect_x1 - tx);
842 clip_bounds_out->y_1 = MAX (clip_bounds_out->y_1, rect_y1 - ty);
843 clip_bounds_out->x_2 = MIN (clip_bounds_out->x_2, rect_x2 - tx);
844 clip_bounds_out->y_2 = MIN (clip_bounds_out->y_2, rect_y2 - ty);
845 }
846
847 if (clip_bounds_out->x_2 <= clip_bounds_out->x_1 ||
848 clip_bounds_out->y_2 <= clip_bounds_out->y_1)
849 memset (clip_bounds_out, 0, sizeof (ClipBounds));
850
851 return TRUE;
852 }
853
854 static void
software_clip_entry(CoglJournalEntry * journal_entry,float * verts,ClipBounds * clip_bounds)855 software_clip_entry (CoglJournalEntry *journal_entry,
856 float *verts,
857 ClipBounds *clip_bounds)
858 {
859 size_t stride =
860 GET_JOURNAL_ARRAY_STRIDE_FOR_N_LAYERS (journal_entry->n_layers);
861 float rx1, ry1, rx2, ry2;
862 float vx1, vy1, vx2, vy2;
863 int layer_num;
864
865 /* Remove the clip on the entry */
866 _cogl_clip_stack_unref (journal_entry->clip_stack);
867 journal_entry->clip_stack = NULL;
868
869 vx1 = verts[0];
870 vy1 = verts[1];
871 vx2 = verts[stride];
872 vy2 = verts[stride + 1];
873
874 if (vx1 < vx2)
875 {
876 rx1 = vx1;
877 rx2 = vx2;
878 }
879 else
880 {
881 rx1 = vx2;
882 rx2 = vx1;
883 }
884 if (vy1 < vy2)
885 {
886 ry1 = vy1;
887 ry2 = vy2;
888 }
889 else
890 {
891 ry1 = vy2;
892 ry2 = vy1;
893 }
894
895 rx1 = CLAMP (rx1, clip_bounds->x_1, clip_bounds->x_2);
896 ry1 = CLAMP (ry1, clip_bounds->y_1, clip_bounds->y_2);
897 rx2 = CLAMP (rx2, clip_bounds->x_1, clip_bounds->x_2);
898 ry2 = CLAMP (ry2, clip_bounds->y_1, clip_bounds->y_2);
899
900 /* Check if the rectangle intersects the clip at all */
901 if (rx1 == rx2 || ry1 == ry2)
902 /* Will set all of the vertex data to 0 in the hope that this
903 will create a degenerate rectangle and the GL driver will
904 be able to clip it quickly */
905 memset (verts, 0, sizeof (float) * stride * 2);
906 else
907 {
908 if (vx1 > vx2)
909 {
910 float t = rx1;
911 rx1 = rx2;
912 rx2 = t;
913 }
914 if (vy1 > vy2)
915 {
916 float t = ry1;
917 ry1 = ry2;
918 ry2 = t;
919 }
920
921 verts[0] = rx1;
922 verts[1] = ry1;
923 verts[stride] = rx2;
924 verts[stride + 1] = ry2;
925
926 /* Convert the rectangle coordinates to a fraction of the original
927 rectangle */
928 rx1 = (rx1 - vx1) / (vx2 - vx1);
929 ry1 = (ry1 - vy1) / (vy2 - vy1);
930 rx2 = (rx2 - vx1) / (vx2 - vx1);
931 ry2 = (ry2 - vy1) / (vy2 - vy1);
932
933 for (layer_num = 0; layer_num < journal_entry->n_layers; layer_num++)
934 {
935 float *t = verts + 2 + 2 * layer_num;
936 float tx1 = t[0], ty1 = t[1];
937 float tx2 = t[stride], ty2 = t[stride + 1];
938 t[0] = rx1 * (tx2 - tx1) + tx1;
939 t[1] = ry1 * (ty2 - ty1) + ty1;
940 t[stride] = rx2 * (tx2 - tx1) + tx1;
941 t[stride + 1] = ry2 * (ty2 - ty1) + ty1;
942 }
943 }
944 }
945
946 static void
maybe_software_clip_entries(CoglJournalEntry * batch_start,int batch_len,CoglJournalFlushState * state)947 maybe_software_clip_entries (CoglJournalEntry *batch_start,
948 int batch_len,
949 CoglJournalFlushState *state)
950 {
951 CoglContext *ctx;
952 CoglJournal *journal;
953 CoglClipStack *clip_stack, *clip_entry;
954 int entry_num;
955
956 /* This tries to find cases where the entry is logged with a clip
957 but it would be faster to modify the vertex and texture
958 coordinates rather than flush the clip so that it can batch
959 better */
960
961 /* If the batch is reasonably long then it's worthwhile programming
962 the GPU to do the clip */
963 if (batch_len >= COGL_JOURNAL_HARDWARE_CLIP_THRESHOLD)
964 return;
965
966 clip_stack = batch_start->clip_stack;
967
968 if (clip_stack == NULL)
969 return;
970
971 /* Verify that all of the clip stack entries are a simple rectangle
972 clip */
973 for (clip_entry = clip_stack; clip_entry; clip_entry = clip_entry->parent)
974 if (clip_entry->type != COGL_CLIP_STACK_RECT)
975 return;
976
977 ctx = state->ctx;
978 journal = state->journal;
979
980 /* This scratch buffer is used to store the translation for each
981 entry in the journal. We store it in a separate buffer because
982 it's expensive to calculate but at this point we still don't know
983 whether we can clip all of the entries so we don't want to do the
984 rest of the dependant calculations until we're sure we can. */
985 if (ctx->journal_clip_bounds == NULL)
986 ctx->journal_clip_bounds = g_array_new (FALSE, FALSE, sizeof (ClipBounds));
987 g_array_set_size (ctx->journal_clip_bounds, batch_len);
988
989 for (entry_num = 0; entry_num < batch_len; entry_num++)
990 {
991 CoglJournalEntry *journal_entry = batch_start + entry_num;
992 CoglJournalEntry *prev_journal_entry =
993 entry_num ? batch_start + (entry_num - 1) : NULL;
994 ClipBounds *clip_bounds = &g_array_index (ctx->journal_clip_bounds,
995 ClipBounds, entry_num);
996
997 if (!can_software_clip_entry (journal_entry, prev_journal_entry,
998 clip_stack,
999 clip_bounds))
1000 return;
1001 }
1002
1003 /* If we make it here then we know we can software clip the entire batch */
1004
1005 COGL_NOTE (CLIPPING, "Software clipping a batch of length %i", batch_len);
1006
1007 for (entry_num = 0; entry_num < batch_len; entry_num++)
1008 {
1009 CoglJournalEntry *journal_entry = batch_start + entry_num;
1010 float *verts = &g_array_index (journal->vertices, float,
1011 journal_entry->array_offset + 1);
1012 ClipBounds *clip_bounds = &g_array_index (ctx->journal_clip_bounds,
1013 ClipBounds, entry_num);
1014
1015 software_clip_entry (journal_entry, verts, clip_bounds);
1016 }
1017
1018 return;
1019 }
1020
1021 static void
_cogl_journal_maybe_software_clip_entries(CoglJournalEntry * batch_start,int batch_len,void * data)1022 _cogl_journal_maybe_software_clip_entries (CoglJournalEntry *batch_start,
1023 int batch_len,
1024 void *data)
1025 {
1026 CoglJournalFlushState *state = data;
1027
1028 COGL_STATIC_TIMER (time_check_software_clip,
1029 "Journal Flush", /* parent */
1030 "flush: software clipping",
1031 "Time spent software clipping",
1032 0 /* no application private data */);
1033
1034 COGL_TIMER_START (_cogl_uprof_context,
1035 time_check_software_clip);
1036
1037 maybe_software_clip_entries (batch_start, batch_len, state);
1038
1039 COGL_TIMER_STOP (_cogl_uprof_context,
1040 time_check_software_clip);
1041 }
1042
1043 static CoglBool
compare_entry_clip_stacks(CoglJournalEntry * entry0,CoglJournalEntry * entry1)1044 compare_entry_clip_stacks (CoglJournalEntry *entry0, CoglJournalEntry *entry1)
1045 {
1046 return entry0->clip_stack == entry1->clip_stack;
1047 }
1048
1049 /* Gets a new vertex array from the pool. A reference is taken on the
1050 array so it can be treated as if it was just newly allocated */
1051 static CoglAttributeBuffer *
create_attribute_buffer(CoglJournal * journal,size_t n_bytes)1052 create_attribute_buffer (CoglJournal *journal,
1053 size_t n_bytes)
1054 {
1055 CoglAttributeBuffer *vbo;
1056 CoglContext *ctx = journal->framebuffer->context;
1057
1058 /* If CoglBuffers are being emulated with malloc then there's not
1059 really any point in using the pool so we'll just allocate the
1060 buffer directly */
1061 if (!_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_VBOS))
1062 return cogl_attribute_buffer_new_with_size (ctx, n_bytes);
1063
1064 vbo = journal->vbo_pool[journal->next_vbo_in_pool];
1065
1066 if (vbo == NULL)
1067 {
1068 vbo = cogl_attribute_buffer_new_with_size (ctx, n_bytes);
1069 journal->vbo_pool[journal->next_vbo_in_pool] = vbo;
1070 }
1071 else if (cogl_buffer_get_size (COGL_BUFFER (vbo)) < n_bytes)
1072 {
1073 /* If the buffer is too small then we'll just recreate it */
1074 cogl_object_unref (vbo);
1075 vbo = cogl_attribute_buffer_new_with_size (ctx, n_bytes);
1076 journal->vbo_pool[journal->next_vbo_in_pool] = vbo;
1077 }
1078
1079 journal->next_vbo_in_pool = ((journal->next_vbo_in_pool + 1) %
1080 COGL_JOURNAL_VBO_POOL_SIZE);
1081
1082 return cogl_object_ref (vbo);
1083 }
1084
1085 static CoglAttributeBuffer *
upload_vertices(CoglJournal * journal,const CoglJournalEntry * entries,int n_entries,size_t needed_vbo_len,GArray * vertices)1086 upload_vertices (CoglJournal *journal,
1087 const CoglJournalEntry *entries,
1088 int n_entries,
1089 size_t needed_vbo_len,
1090 GArray *vertices)
1091 {
1092 CoglAttributeBuffer *attribute_buffer;
1093 CoglBuffer *buffer;
1094 const float *vin;
1095 float *vout;
1096 int entry_num;
1097 int i;
1098 CoglMatrixEntry *last_modelview_entry = NULL;
1099 CoglMatrix modelview;
1100
1101 g_assert (needed_vbo_len);
1102
1103 attribute_buffer = create_attribute_buffer (journal, needed_vbo_len * 4);
1104 buffer = COGL_BUFFER (attribute_buffer);
1105 cogl_buffer_set_update_hint (buffer, COGL_BUFFER_UPDATE_HINT_DYNAMIC);
1106
1107 vout = _cogl_buffer_map_range_for_fill_or_fallback (buffer,
1108 0, /* offset */
1109 needed_vbo_len * 4);
1110 vin = &g_array_index (vertices, float, 0);
1111
1112 /* Expand the number of vertices from 2 to 4 while uploading */
1113 for (entry_num = 0; entry_num < n_entries; entry_num++)
1114 {
1115 const CoglJournalEntry *entry = entries + entry_num;
1116 size_t vb_stride = GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS (entry->n_layers);
1117 size_t array_stride =
1118 GET_JOURNAL_ARRAY_STRIDE_FOR_N_LAYERS (entry->n_layers);
1119
1120 /* Copy the color to all four of the vertices */
1121 for (i = 0; i < 4; i++)
1122 memcpy (vout + vb_stride * i + POS_STRIDE, vin, 4);
1123 vin++;
1124
1125 if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM)))
1126 {
1127 vout[vb_stride * 0] = vin[0];
1128 vout[vb_stride * 0 + 1] = vin[1];
1129 vout[vb_stride * 1] = vin[0];
1130 vout[vb_stride * 1 + 1] = vin[array_stride + 1];
1131 vout[vb_stride * 2] = vin[array_stride];
1132 vout[vb_stride * 2 + 1] = vin[array_stride + 1];
1133 vout[vb_stride * 3] = vin[array_stride];
1134 vout[vb_stride * 3 + 1] = vin[1];
1135 }
1136 else
1137 {
1138 float v[8];
1139
1140 v[0] = vin[0];
1141 v[1] = vin[1];
1142 v[2] = vin[0];
1143 v[3] = vin[array_stride + 1];
1144 v[4] = vin[array_stride];
1145 v[5] = vin[array_stride + 1];
1146 v[6] = vin[array_stride];
1147 v[7] = vin[1];
1148
1149 if (entry->modelview_entry != last_modelview_entry)
1150 cogl_matrix_entry_get (entry->modelview_entry, &modelview);
1151 cogl_matrix_transform_points (&modelview,
1152 2, /* n_components */
1153 sizeof (float) * 2, /* stride_in */
1154 v, /* points_in */
1155 /* strideout */
1156 vb_stride * sizeof (float),
1157 vout, /* points_out */
1158 4 /* n_points */);
1159 }
1160
1161 for (i = 0; i < entry->n_layers; i++)
1162 {
1163 const float *tin = vin + 2;
1164 float *tout = vout + POS_STRIDE + COLOR_STRIDE;
1165
1166 tout[vb_stride * 0 + i * 2] = tin[i * 2];
1167 tout[vb_stride * 0 + 1 + i * 2] = tin[i * 2 + 1];
1168 tout[vb_stride * 1 + i * 2] = tin[i * 2];
1169 tout[vb_stride * 1 + 1 + i * 2] = tin[array_stride + i * 2 + 1];
1170 tout[vb_stride * 2 + i * 2] = tin[array_stride + i * 2];
1171 tout[vb_stride * 2 + 1 + i * 2] = tin[array_stride + i * 2 + 1];
1172 tout[vb_stride * 3 + i * 2] = tin[array_stride + i * 2];
1173 tout[vb_stride * 3 + 1 + i * 2] = tin[i * 2 + 1];
1174 }
1175
1176 vin += array_stride * 2;
1177 vout += vb_stride * 4;
1178 }
1179
1180 _cogl_buffer_unmap_for_fill_or_fallback (buffer);
1181
1182 return attribute_buffer;
1183 }
1184
1185 void
_cogl_journal_discard(CoglJournal * journal)1186 _cogl_journal_discard (CoglJournal *journal)
1187 {
1188 int i;
1189
1190 if (journal->entries->len <= 0)
1191 return;
1192
1193 for (i = 0; i < journal->entries->len; i++)
1194 {
1195 CoglJournalEntry *entry =
1196 &g_array_index (journal->entries, CoglJournalEntry, i);
1197 _cogl_pipeline_journal_unref (entry->pipeline);
1198 cogl_matrix_entry_unref (entry->modelview_entry);
1199 _cogl_clip_stack_unref (entry->clip_stack);
1200 }
1201
1202 g_array_set_size (journal->entries, 0);
1203 g_array_set_size (journal->vertices, 0);
1204 journal->needed_vbo_len = 0;
1205 journal->fast_read_pixel_count = 0;
1206
1207 /* The journal only holds a reference to the framebuffer while the
1208 journal is not empty */
1209 cogl_object_unref (journal->framebuffer);
1210 }
1211
1212 /* Note: A return value of FALSE doesn't mean 'no' it means
1213 * 'unknown' */
1214 CoglBool
_cogl_journal_all_entries_within_bounds(CoglJournal * journal,float clip_x0,float clip_y0,float clip_x1,float clip_y1)1215 _cogl_journal_all_entries_within_bounds (CoglJournal *journal,
1216 float clip_x0,
1217 float clip_y0,
1218 float clip_x1,
1219 float clip_y1)
1220 {
1221 CoglJournalEntry *entry = (CoglJournalEntry *)journal->entries->data;
1222 CoglClipStack *clip_entry;
1223 CoglClipStack *reference = NULL;
1224 int bounds_x0;
1225 int bounds_y0;
1226 int bounds_x1;
1227 int bounds_y1;
1228 int i;
1229
1230 if (journal->entries->len == 0)
1231 return TRUE;
1232
1233 /* Find the shortest clip_stack ancestry that leaves us in the
1234 * required bounds */
1235 for (clip_entry = entry->clip_stack;
1236 clip_entry;
1237 clip_entry = clip_entry->parent)
1238 {
1239 _cogl_clip_stack_get_bounds (clip_entry,
1240 &bounds_x0, &bounds_y0,
1241 &bounds_x1, &bounds_y1);
1242
1243 if (bounds_x0 >= clip_x0 && bounds_y0 >= clip_y0 &&
1244 bounds_x1 <= clip_x1 && bounds_y1 <= clip_y1)
1245 reference = clip_entry;
1246 else
1247 break;
1248 }
1249
1250 if (!reference)
1251 return FALSE;
1252
1253 /* For the remaining journal entries we will only verify they share
1254 * 'reference' as an ancestor in their clip stack since that's
1255 * enough to know that they would be within the required bounds.
1256 */
1257 for (i = 1; i < journal->entries->len; i++)
1258 {
1259 CoglBool found_reference = FALSE;
1260 entry = &g_array_index (journal->entries, CoglJournalEntry, i);
1261
1262 for (clip_entry = entry->clip_stack;
1263 clip_entry;
1264 clip_entry = clip_entry->parent)
1265 {
1266 if (clip_entry == reference)
1267 {
1268 found_reference = TRUE;
1269 break;
1270 }
1271 }
1272
1273 if (!found_reference)
1274 return FALSE;
1275 }
1276
1277 return TRUE;
1278 }
1279
1280 static void
post_fences(CoglJournal * journal)1281 post_fences (CoglJournal *journal)
1282 {
1283 CoglFenceClosure *fence, *tmp;
1284
1285 _cogl_list_for_each_safe (fence, tmp, &journal->pending_fences, link)
1286 {
1287 _cogl_list_remove (&fence->link);
1288 _cogl_fence_submit (fence);
1289 }
1290 }
1291
1292 /* XXX NB: When _cogl_journal_flush() returns all state relating
1293 * to pipelines, all glEnable flags and current matrix state
1294 * is undefined.
1295 */
1296 void
_cogl_journal_flush(CoglJournal * journal)1297 _cogl_journal_flush (CoglJournal *journal)
1298 {
1299 CoglFramebuffer *framebuffer;
1300 CoglContext *ctx;
1301 CoglJournalFlushState state;
1302 int i;
1303 COGL_STATIC_TIMER (flush_timer,
1304 "Mainloop", /* parent */
1305 "Journal Flush",
1306 "The time spent flushing the Cogl journal",
1307 0 /* no application private data */);
1308 COGL_STATIC_TIMER (discard_timer,
1309 "Journal Flush", /* parent */
1310 "flush: discard",
1311 "The time spent discarding the Cogl journal after a flush",
1312 0 /* no application private data */);
1313
1314 if (journal->entries->len == 0)
1315 {
1316 post_fences (journal);
1317 return;
1318 }
1319
1320 framebuffer = journal->framebuffer;
1321 ctx = framebuffer->context;
1322
1323 /* The entries in this journal may depend on images in other
1324 * framebuffers which may require that we flush the journals
1325 * associated with those framebuffers before we can flush
1326 * this journal... */
1327 _cogl_framebuffer_flush_dependency_journals (framebuffer);
1328
1329 /* Note: we start the timer after flushing dependency journals so
1330 * that the timer isn't started recursively. */
1331 COGL_TIMER_START (_cogl_uprof_context, flush_timer);
1332
1333 if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_BATCHING)))
1334 g_print ("BATCHING: journal len = %d\n", journal->entries->len);
1335
1336 /* NB: the journal deals with flushing the modelview stack and clip
1337 state manually */
1338 _cogl_framebuffer_flush_state (framebuffer,
1339 framebuffer,
1340 COGL_FRAMEBUFFER_STATE_ALL &
1341 ~(COGL_FRAMEBUFFER_STATE_MODELVIEW |
1342 COGL_FRAMEBUFFER_STATE_CLIP));
1343
1344 /* We need to mark the current modelview state of the framebuffer as
1345 * dirty because we are going to manually replace it */
1346 ctx->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_MODELVIEW;
1347
1348 state.ctx = ctx;
1349 state.journal = journal;
1350
1351 state.attributes = ctx->journal_flush_attributes_array;
1352
1353 if (G_UNLIKELY ((COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_SOFTWARE_CLIP)) == 0))
1354 {
1355 /* We do an initial walk of the journal to analyse the clip stack
1356 batches to see if we can do software clipping. We do this as a
1357 separate walk of the journal because we can modify entries and
1358 this may end up joining together clip stack batches in the next
1359 iteration. */
1360 batch_and_call ((CoglJournalEntry *)journal->entries->data, /* first entry */
1361 journal->entries->len, /* max number of entries to consider */
1362 compare_entry_clip_stacks,
1363 _cogl_journal_maybe_software_clip_entries, /* callback */
1364 &state); /* data */
1365 }
1366
1367 /* We upload the vertices after the clip stack pass in case it
1368 modifies the entries */
1369 state.attribute_buffer =
1370 upload_vertices (journal,
1371 &g_array_index (journal->entries, CoglJournalEntry, 0),
1372 journal->entries->len,
1373 journal->needed_vbo_len,
1374 journal->vertices);
1375 state.array_offset = 0;
1376
1377 /* batch_and_call() batches a list of journal entries according to some
1378 * given criteria and calls a callback once for each determined batch.
1379 *
1380 * The process of flushing the journal is staggered to reduce the amount
1381 * of driver/GPU state changes necessary:
1382 * 1) We split the entries according to the clip state.
1383 * 2) We split the entries according to the stride of the vertices:
1384 * Each time the stride of our vertex data changes we need to call
1385 * gl{Vertex,Color}Pointer to inform GL of new VBO offsets.
1386 * Currently the only thing that affects the stride of our vertex data
1387 * is the number of pipeline layers.
1388 * 3) We split the entries explicitly by the number of pipeline layers:
1389 * We pad our vertex data when the number of layers is < 2 so that we
1390 * can minimize changes in stride. Each time the number of layers
1391 * changes we need to call glTexCoordPointer to inform GL of new VBO
1392 * offsets.
1393 * 4) We then split according to compatible Cogl pipelines:
1394 * This is where we flush pipeline state
1395 * 5) Finally we split according to modelview matrix changes:
1396 * This is when we finally tell GL to draw something.
1397 * Note: Splitting by modelview changes is skipped when are doing the
1398 * vertex transformation in software at log time.
1399 */
1400 batch_and_call ((CoglJournalEntry *)journal->entries->data, /* first entry */
1401 journal->entries->len, /* max number of entries to consider */
1402 compare_entry_clip_stacks,
1403 _cogl_journal_flush_clip_stacks_and_entries, /* callback */
1404 &state); /* data */
1405
1406 for (i = 0; i < state.attributes->len; i++)
1407 cogl_object_unref (g_array_index (state.attributes, CoglAttribute *, i));
1408 g_array_set_size (state.attributes, 0);
1409
1410 cogl_object_unref (state.attribute_buffer);
1411
1412 COGL_TIMER_START (_cogl_uprof_context, discard_timer);
1413 _cogl_journal_discard (journal);
1414 COGL_TIMER_STOP (_cogl_uprof_context, discard_timer);
1415
1416 post_fences (journal);
1417
1418 COGL_TIMER_STOP (_cogl_uprof_context, flush_timer);
1419 }
1420
1421 static CoglBool
add_framebuffer_deps_cb(CoglPipelineLayer * layer,void * user_data)1422 add_framebuffer_deps_cb (CoglPipelineLayer *layer, void *user_data)
1423 {
1424 CoglFramebuffer *framebuffer = user_data;
1425 CoglTexture *texture = _cogl_pipeline_layer_get_texture_real (layer);
1426 const GList *l;
1427
1428 if (!texture)
1429 return TRUE;
1430
1431 for (l = _cogl_texture_get_associated_framebuffers (texture); l; l = l->next)
1432 _cogl_framebuffer_add_dependency (framebuffer, l->data);
1433
1434 return TRUE;
1435 }
1436
1437 void
_cogl_journal_log_quad(CoglJournal * journal,const float * position,CoglPipeline * pipeline,int n_layers,CoglTexture * layer0_override_texture,const float * tex_coords,unsigned int tex_coords_len)1438 _cogl_journal_log_quad (CoglJournal *journal,
1439 const float *position,
1440 CoglPipeline *pipeline,
1441 int n_layers,
1442 CoglTexture *layer0_override_texture,
1443 const float *tex_coords,
1444 unsigned int tex_coords_len)
1445 {
1446 CoglFramebuffer *framebuffer = journal->framebuffer;
1447 size_t stride;
1448 int next_vert;
1449 float *v;
1450 int i;
1451 int next_entry;
1452 uint32_t disable_layers;
1453 CoglJournalEntry *entry;
1454 CoglPipeline *final_pipeline;
1455 CoglClipStack *clip_stack;
1456 CoglPipelineFlushOptions flush_options;
1457 CoglMatrixStack *modelview_stack;
1458 COGL_STATIC_TIMER (log_timer,
1459 "Mainloop", /* parent */
1460 "Journal Log",
1461 "The time spent logging in the Cogl journal",
1462 0 /* no application private data */);
1463
1464 COGL_TIMER_START (_cogl_uprof_context, log_timer);
1465
1466 /* Adding something to the journal should mean that we are in the
1467 * middle of the scene. Although this will also end up being set
1468 * when the journal is actually flushed, we set it here explicitly
1469 * so that we will know sooner */
1470 _cogl_framebuffer_mark_mid_scene (framebuffer);
1471
1472 /* If the framebuffer was previously empty then we'll take a
1473 reference to the current framebuffer. This reference will be
1474 removed when the journal is flushed */
1475 if (journal->vertices->len == 0)
1476 cogl_object_ref (framebuffer);
1477
1478 /* The vertex data is logged into a separate array. The data needs
1479 to be copied into a vertex array before it's given to GL so we
1480 only store two vertices per quad and expand it to four while
1481 uploading. */
1482
1483 /* XXX: See definition of GET_JOURNAL_ARRAY_STRIDE_FOR_N_LAYERS for details
1484 * about how we pack our vertex data */
1485 stride = GET_JOURNAL_ARRAY_STRIDE_FOR_N_LAYERS (n_layers);
1486
1487 next_vert = journal->vertices->len;
1488 g_array_set_size (journal->vertices, next_vert + 2 * stride + 1);
1489 v = &g_array_index (journal->vertices, float, next_vert);
1490
1491 /* We calculate the needed size of the vbo as we go because it
1492 depends on the number of layers in each entry and it's not easy
1493 calculate based on the length of the logged vertices array */
1494 journal->needed_vbo_len += GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS (n_layers) * 4;
1495
1496 /* XXX: All the jumping around to fill in this strided buffer doesn't
1497 * seem ideal. */
1498
1499 /* FIXME: This is a hacky optimization, since it will break if we
1500 * change the definition of CoglColor: */
1501 _cogl_pipeline_get_colorubv (pipeline, (uint8_t *) v);
1502 v++;
1503
1504 memcpy (v, position, sizeof (float) * 2);
1505 memcpy (v + stride, position + 2, sizeof (float) * 2);
1506
1507 for (i = 0; i < n_layers; i++)
1508 {
1509 /* XXX: See definition of GET_JOURNAL_ARRAY_STRIDE_FOR_N_LAYERS
1510 * for details about how we pack our vertex data */
1511 GLfloat *t = v + 2 + i * 2;
1512
1513 memcpy (t, tex_coords + i * 4, sizeof (float) * 2);
1514 memcpy (t + stride, tex_coords + i * 4 + 2, sizeof (float) * 2);
1515 }
1516
1517 if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_JOURNAL)))
1518 {
1519 g_print ("Logged new quad:\n");
1520 v = &g_array_index (journal->vertices, float, next_vert);
1521 _cogl_journal_dump_logged_quad ((uint8_t *)v, n_layers);
1522 }
1523
1524 next_entry = journal->entries->len;
1525 g_array_set_size (journal->entries, next_entry + 1);
1526 entry = &g_array_index (journal->entries, CoglJournalEntry, next_entry);
1527
1528 entry->n_layers = n_layers;
1529 entry->array_offset = next_vert;
1530
1531 final_pipeline = pipeline;
1532
1533 flush_options.flags = 0;
1534 if (G_UNLIKELY (cogl_pipeline_get_n_layers (pipeline) != n_layers))
1535 {
1536 disable_layers = (1 << n_layers) - 1;
1537 disable_layers = ~disable_layers;
1538 flush_options.disable_layers = disable_layers;
1539 flush_options.flags |= COGL_PIPELINE_FLUSH_DISABLE_MASK;
1540 }
1541 if (G_UNLIKELY (layer0_override_texture))
1542 {
1543 flush_options.flags |= COGL_PIPELINE_FLUSH_LAYER0_OVERRIDE;
1544 flush_options.layer0_override_texture = layer0_override_texture;
1545 }
1546
1547 if (G_UNLIKELY (flush_options.flags))
1548 {
1549 final_pipeline = cogl_pipeline_copy (pipeline);
1550 _cogl_pipeline_apply_overrides (final_pipeline, &flush_options);
1551 }
1552
1553 entry->pipeline = _cogl_pipeline_journal_ref (final_pipeline);
1554
1555 clip_stack = _cogl_framebuffer_get_clip_stack (framebuffer);
1556 entry->clip_stack = _cogl_clip_stack_ref (clip_stack);
1557
1558 if (G_UNLIKELY (final_pipeline != pipeline))
1559 cogl_object_unref (final_pipeline);
1560
1561 modelview_stack =
1562 _cogl_framebuffer_get_modelview_stack (framebuffer);
1563 entry->modelview_entry = cogl_matrix_entry_ref (modelview_stack->last_entry);
1564
1565 _cogl_pipeline_foreach_layer_internal (pipeline,
1566 add_framebuffer_deps_cb,
1567 framebuffer);
1568
1569 if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_BATCHING)))
1570 _cogl_journal_flush (journal);
1571
1572 COGL_TIMER_STOP (_cogl_uprof_context, log_timer);
1573 }
1574
1575 static void
entry_to_screen_polygon(CoglFramebuffer * framebuffer,const CoglJournalEntry * entry,float * vertices,float * poly)1576 entry_to_screen_polygon (CoglFramebuffer *framebuffer,
1577 const CoglJournalEntry *entry,
1578 float *vertices,
1579 float *poly)
1580 {
1581 size_t array_stride =
1582 GET_JOURNAL_ARRAY_STRIDE_FOR_N_LAYERS (entry->n_layers);
1583 CoglMatrixStack *projection_stack;
1584 CoglMatrix projection;
1585 CoglMatrix modelview;
1586 int i;
1587 float viewport[4];
1588
1589 poly[0] = vertices[0];
1590 poly[1] = vertices[1];
1591 poly[2] = 0;
1592 poly[3] = 1;
1593
1594 poly[4] = vertices[0];
1595 poly[5] = vertices[array_stride + 1];
1596 poly[6] = 0;
1597 poly[7] = 1;
1598
1599 poly[8] = vertices[array_stride];
1600 poly[9] = vertices[array_stride + 1];
1601 poly[10] = 0;
1602 poly[11] = 1;
1603
1604 poly[12] = vertices[array_stride];
1605 poly[13] = vertices[1];
1606 poly[14] = 0;
1607 poly[15] = 1;
1608
1609 /* TODO: perhaps split the following out into a more generalized
1610 * _cogl_transform_points utility...
1611 */
1612
1613 cogl_matrix_entry_get (entry->modelview_entry, &modelview);
1614 cogl_matrix_transform_points (&modelview,
1615 2, /* n_components */
1616 sizeof (float) * 4, /* stride_in */
1617 poly, /* points_in */
1618 /* strideout */
1619 sizeof (float) * 4,
1620 poly, /* points_out */
1621 4 /* n_points */);
1622
1623 projection_stack =
1624 _cogl_framebuffer_get_projection_stack (framebuffer);
1625 cogl_matrix_stack_get (projection_stack, &projection);
1626
1627 cogl_matrix_project_points (&projection,
1628 3, /* n_components */
1629 sizeof (float) * 4, /* stride_in */
1630 poly, /* points_in */
1631 /* strideout */
1632 sizeof (float) * 4,
1633 poly, /* points_out */
1634 4 /* n_points */);
1635
1636 cogl_framebuffer_get_viewport4fv (framebuffer, viewport);
1637
1638 /* Scale from OpenGL normalized device coordinates (ranging from -1 to 1)
1639 * to Cogl window/framebuffer coordinates (ranging from 0 to buffer-size) with
1640 * (0,0) being top left. */
1641 #define VIEWPORT_TRANSFORM_X(x, vp_origin_x, vp_width) \
1642 ( ( ((x) + 1.0) * ((vp_width) / 2.0) ) + (vp_origin_x) )
1643 /* Note: for Y we first flip all coordinates around the X axis while in
1644 * normalized device coodinates */
1645 #define VIEWPORT_TRANSFORM_Y(y, vp_origin_y, vp_height) \
1646 ( ( ((-(y)) + 1.0) * ((vp_height) / 2.0) ) + (vp_origin_y) )
1647
1648 /* Scale from normalized device coordinates (in range [-1,1]) to
1649 * window coordinates ranging [0,window-size] ... */
1650 for (i = 0; i < 4; i++)
1651 {
1652 float w = poly[4 * i + 3];
1653
1654 /* Perform perspective division */
1655 poly[4 * i] /= w;
1656 poly[4 * i + 1] /= w;
1657
1658 /* Apply viewport transform */
1659 poly[4 * i] = VIEWPORT_TRANSFORM_X (poly[4 * i],
1660 viewport[0], viewport[2]);
1661 poly[4 * i + 1] = VIEWPORT_TRANSFORM_Y (poly[4 * i + 1],
1662 viewport[1], viewport[3]);
1663 }
1664
1665 #undef VIEWPORT_TRANSFORM_X
1666 #undef VIEWPORT_TRANSFORM_Y
1667 }
1668
1669 static CoglBool
try_checking_point_hits_entry_after_clipping(CoglFramebuffer * framebuffer,CoglJournalEntry * entry,float * vertices,float x,float y,CoglBool * hit)1670 try_checking_point_hits_entry_after_clipping (CoglFramebuffer *framebuffer,
1671 CoglJournalEntry *entry,
1672 float *vertices,
1673 float x,
1674 float y,
1675 CoglBool *hit)
1676 {
1677 CoglBool can_software_clip = TRUE;
1678 CoglBool needs_software_clip = FALSE;
1679 CoglClipStack *clip_entry;
1680
1681 *hit = TRUE;
1682
1683 /* Verify that all of the clip stack entries are simple rectangle
1684 * clips */
1685 for (clip_entry = entry->clip_stack;
1686 clip_entry;
1687 clip_entry = clip_entry->parent)
1688 {
1689 if (x < clip_entry->bounds_x0 ||
1690 x >= clip_entry->bounds_x1 ||
1691 y < clip_entry->bounds_y0 ||
1692 y >= clip_entry->bounds_y1)
1693 {
1694 *hit = FALSE;
1695 return TRUE;
1696 }
1697
1698 if (clip_entry->type == COGL_CLIP_STACK_WINDOW_RECT)
1699 {
1700 /* XXX: technically we could still run the software clip in
1701 * this case because for our purposes we know this clip
1702 * can be ignored now, but [can_]sofware_clip_entry() doesn't
1703 * know this and will bail out. */
1704 can_software_clip = FALSE;
1705 }
1706 else if (clip_entry->type == COGL_CLIP_STACK_RECT)
1707 {
1708 CoglClipStackRect *rect_entry = (CoglClipStackRect *)entry;
1709
1710 if (rect_entry->can_be_scissor == FALSE)
1711 needs_software_clip = TRUE;
1712 /* If can_be_scissor is TRUE then we know it's screen
1713 * aligned and the hit test we did above has determined
1714 * that we are inside this clip. */
1715 }
1716 else
1717 return FALSE;
1718 }
1719
1720 if (needs_software_clip)
1721 {
1722 ClipBounds clip_bounds;
1723 float poly[16];
1724
1725 if (!can_software_clip)
1726 return FALSE;
1727
1728 if (!can_software_clip_entry (entry, NULL,
1729 entry->clip_stack, &clip_bounds))
1730 return FALSE;
1731
1732 software_clip_entry (entry, vertices, &clip_bounds);
1733 entry_to_screen_polygon (framebuffer, entry, vertices, poly);
1734
1735 *hit = _cogl_util_point_in_screen_poly (x, y, poly, sizeof (float) * 4, 4);
1736 return TRUE;
1737 }
1738
1739 return TRUE;
1740 }
1741
1742 CoglBool
_cogl_journal_try_read_pixel(CoglJournal * journal,int x,int y,CoglBitmap * bitmap,CoglBool * found_intersection)1743 _cogl_journal_try_read_pixel (CoglJournal *journal,
1744 int x,
1745 int y,
1746 CoglBitmap *bitmap,
1747 CoglBool *found_intersection)
1748 {
1749 CoglContext *ctx;
1750 CoglPixelFormat format;
1751 int i;
1752
1753 /* XXX: this number has been plucked out of thin air, but the idea
1754 * is that if so many pixels are being read from the same un-changed
1755 * journal than we expect that it will be more efficient to fail
1756 * here so we end up flushing and rendering the journal so that
1757 * further reads can directly read from the framebuffer. There will
1758 * be a bit more lag to flush the render but if there are going to
1759 * continue being lots of arbitrary single pixel reads they will end
1760 * up faster in the end. */
1761 if (journal->fast_read_pixel_count > 50)
1762 return FALSE;
1763
1764 format = cogl_bitmap_get_format (bitmap);
1765
1766 if (format != COGL_PIXEL_FORMAT_RGBA_8888_PRE &&
1767 format != COGL_PIXEL_FORMAT_RGBA_8888)
1768 return FALSE;
1769
1770 ctx = _cogl_bitmap_get_context (bitmap);
1771
1772 *found_intersection = FALSE;
1773
1774 /* NB: The most recently added journal entry is the last entry, and
1775 * assuming this is a simple scene only comprised of opaque coloured
1776 * rectangles with no special pipelines involved (e.g. enabling
1777 * depth testing) then we can assume painter's algorithm for the
1778 * entries and so our fast read-pixel just needs to walk backwards
1779 * through the journal entries trying to intersect each entry with
1780 * the given point of interest. */
1781 for (i = journal->entries->len - 1; i >= 0; i--)
1782 {
1783 CoglJournalEntry *entry =
1784 &g_array_index (journal->entries, CoglJournalEntry, i);
1785 uint8_t *color = (uint8_t *)&g_array_index (journal->vertices, float,
1786 entry->array_offset);
1787 float *vertices = (float *)color + 1;
1788 float poly[16];
1789 CoglFramebuffer *framebuffer = journal->framebuffer;
1790 uint8_t *pixel;
1791 CoglError *ignore_error;
1792
1793 entry_to_screen_polygon (framebuffer, entry, vertices, poly);
1794
1795 if (!_cogl_util_point_in_screen_poly (x, y, poly, sizeof (float) * 4, 4))
1796 continue;
1797
1798 if (entry->clip_stack)
1799 {
1800 CoglBool hit;
1801
1802 if (!try_checking_point_hits_entry_after_clipping (framebuffer,
1803 entry,
1804 vertices,
1805 x, y, &hit))
1806 return FALSE; /* hit couldn't be determined */
1807
1808 if (!hit)
1809 continue;
1810 }
1811
1812 *found_intersection = TRUE;
1813
1814 /* If we find that the rectangle the point of interest
1815 * intersects has any state more complex than a constant opaque
1816 * color then we bail out. */
1817 if (!_cogl_pipeline_equal (ctx->opaque_color_pipeline, entry->pipeline,
1818 (COGL_PIPELINE_STATE_ALL &
1819 ~COGL_PIPELINE_STATE_COLOR),
1820 COGL_PIPELINE_LAYER_STATE_ALL,
1821 0))
1822 return FALSE;
1823
1824
1825 /* we currently only care about cases where the premultiplied or
1826 * unpremultipled colors are equivalent... */
1827 if (color[3] != 0xff)
1828 return FALSE;
1829
1830 pixel = _cogl_bitmap_map (bitmap,
1831 COGL_BUFFER_ACCESS_WRITE,
1832 COGL_BUFFER_MAP_HINT_DISCARD,
1833 &ignore_error);
1834 if (pixel == NULL)
1835 {
1836 cogl_error_free (ignore_error);
1837 return FALSE;
1838 }
1839
1840 pixel[0] = color[0];
1841 pixel[1] = color[1];
1842 pixel[2] = color[2];
1843 pixel[3] = color[3];
1844
1845 _cogl_bitmap_unmap (bitmap);
1846
1847 goto success;
1848 }
1849
1850 success:
1851 journal->fast_read_pixel_count++;
1852 return TRUE;
1853 }
1854