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