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