cogl-journal: Log the clip state in the journal
authorNeil Roberts <neil@linux.intel.com>
Tue, 2 Nov 2010 17:35:17 +0000 (17:35 +0000)
committerNeil Roberts <neil@linux.intel.com>
Thu, 4 Nov 2010 18:10:09 +0000 (18:10 +0000)
When adding a new entry to the journal a reference is now taken on the
current clip stack. Modifying the current clip state no longer causes
a journal flush. The journal flushing code now has an extra stage to
compare the clip state of each entry. The comparison can simply be
done by comparing the pointers. Although different clip states will
still end up with multiple draw calls this at leasts allows a scene
comprising of multiple different clips to be upload with one vbo. It
also lays the groundwork to do certain tricks when drawing clipped
rectangles such as modifying the geometry instead of setting a clip
state.

clutter/cogl/cogl/cogl-clip-state.c
clutter/cogl/cogl/cogl-journal-private.h
clutter/cogl/cogl/cogl-journal.c

index f7fd7d3..e261405 100644 (file)
@@ -51,10 +51,6 @@ cogl_clip_push_window_rectangle (int x_offset,
 
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
-  /* We don't log clip stack changes in the journal so we must flush
-   * it before making modifications */
-  _cogl_journal_flush ();
-
   framebuffer = _cogl_get_framebuffer ();
   clip_state = _cogl_framebuffer_get_clip_state (framebuffer);
 
@@ -136,10 +132,6 @@ cogl_clip_push_rectangle (float x_1,
 
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
-  /* We don't log clip stack changes in the journal so we must flush
-   * it before making modifications */
-  _cogl_journal_flush ();
-
   /* Try and catch window space rectangles so we can redirect to
    * cogl_clip_push_window_rect which will use scissoring. */
   if (try_pushing_rect_as_window_rect (x_1, y_1, x_2, y_2))
@@ -178,10 +170,6 @@ cogl_clip_push_from_path_preserve (void)
 
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
-  /* We don't log clip stack changes in the journal so we must flush
-   * it before making modifications */
-  _cogl_journal_flush ();
-
   framebuffer = _cogl_get_framebuffer ();
   clip_state = _cogl_framebuffer_get_clip_state (framebuffer);
 
@@ -203,10 +191,6 @@ cogl_clip_push_from_path (void)
 static void
 _cogl_clip_pop_real (CoglClipState *clip_state)
 {
-  /* We don't log clip stack changes in the journal so we must flush
-   * it before making modifications */
-  _cogl_journal_flush ();
-
   clip_state->stacks->data = _cogl_clip_stack_pop (clip_state->stacks->data);
 }
 
@@ -250,10 +234,6 @@ cogl_clip_ensure (void)
 static void
 _cogl_clip_stack_save_real (CoglClipState *clip_state)
 {
-  /* We don't log clip stack changes in the journal so we must flush
-   * it before making modifications */
-  _cogl_journal_flush ();
-
   clip_state->stacks = g_slist_prepend (clip_state->stacks, NULL);
 }
 
@@ -278,10 +258,6 @@ _cogl_clip_stack_restore_real (CoglClipState *clip_state)
 
   g_return_if_fail (clip_state->stacks != NULL);
 
-  /* We don't log clip stack changes in the journal so we must flush
-   * it before making modifications */
-  _cogl_journal_flush ();
-
   stack = clip_state->stacks->data;
 
   _cogl_clip_stack_unref (stack);
index a38fa80..977866e 100644 (file)
@@ -25,6 +25,7 @@
 #define __COGL_JOURNAL_PRIVATE_H
 
 #include "cogl-handle.h"
+#include "cogl-clip-stack.h"
 
 /* To improve batching of geometry when submitting vertices to OpenGL we
  * log the texture rectangles we want to draw to a journal, so when we
@@ -34,6 +35,7 @@ typedef struct _CoglJournalEntry
   CoglPipeline            *pipeline;
   int                      n_layers;
   CoglMatrix               model_view;
+  CoglClipStack           *clip_stack;
   /* XXX: These entries are pretty big now considering the padding in
    * CoglPipelineFlushOptions and CoglMatrix, so we might need to optimize this
    * later. */
index e320ebf..cb1e2e2 100644 (file)
@@ -84,6 +84,7 @@ typedef struct _CoglJournalFlushState
   gsize                indices_type_size;
 #endif
   CoglMatrixStack     *modelview_stack;
+  CoglMatrixStack     *projection_stack;
 
   CoglPipeline        *source;
 } CoglJournalFlushState;
@@ -194,7 +195,7 @@ _cogl_journal_flush_modelview_and_entries (CoglJournalEntry *batch_start,
   COGL_TIMER_START (_cogl_uprof_context, time_flush_modelview_and_entries);
 
   if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_BATCHING))
-    g_print ("BATCHING:    modelview batch len = %d\n", batch_len);
+    g_print ("BATCHING:     modelview batch len = %d\n", batch_len);
 
   if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM))
     {
@@ -331,7 +332,7 @@ _cogl_journal_flush_pipeline_and_entries (CoglJournalEntry *batch_start,
   COGL_TIMER_START (_cogl_uprof_context, time_flush_pipeline_entries);
 
   if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_BATCHING))
-    g_print ("BATCHING:   pipeline batch len = %d\n", batch_len);
+    g_print ("BATCHING:    pipeline batch len = %d\n", batch_len);
 
   state->source = batch_start->pipeline;
 
@@ -471,7 +472,7 @@ _cogl_journal_flush_vbo_offsets_and_entries (CoglJournalEntry *batch_start,
   int                      i;
   CoglVertexAttribute    **attribute_entry;
   COGL_STATIC_TIMER (time_flush_vbo_texcoord_pipeline_entries,
-                     "Journal Flush", /* parent */
+                     "flush: clip+vbo+texcoords+pipeline+entries", /* parent */
                      "flush: vbo+texcoords+pipeline+entries",
                      "The time spent flushing vbo + texcoord offsets + "
                      "pipeline + entries",
@@ -483,7 +484,7 @@ _cogl_journal_flush_vbo_offsets_and_entries (CoglJournalEntry *batch_start,
                     time_flush_vbo_texcoord_pipeline_entries);
 
   if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_BATCHING))
-    g_print ("BATCHING:  vbo offset batch len = %d\n", batch_len);
+    g_print ("BATCHING:   vbo offset batch len = %d\n", batch_len);
 
   /* XXX NB:
    * Our journal's vertex data is arranged as follows:
@@ -580,6 +581,68 @@ compare_entry_strides (CoglJournalEntry *entry0, CoglJournalEntry *entry1)
     return FALSE;
 }
 
+/* At this point we know the batch has a unique clip stack */
+static void
+_cogl_journal_flush_clip_stacks_and_entries (CoglJournalEntry *batch_start,
+                                             int               batch_len,
+                                             void             *data)
+{
+  CoglJournalFlushState *state = data;
+
+  COGL_STATIC_TIMER (time_flush_clip_stack_pipeline_entries,
+                     "Journal Flush", /* parent */
+                     "flush: clip+vbo+texcoords+pipeline+entries",
+                     "The time spent flushing clip + vbo + texcoord offsets + "
+                     "pipeline + entries",
+                     0 /* no application private data */);
+
+  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+  COGL_TIMER_START (_cogl_uprof_context,
+                    time_flush_clip_stack_pipeline_entries);
+
+  if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_BATCHING))
+    g_print ("BATCHING:  clip stack batch len = %d\n", batch_len);
+
+  _cogl_clip_stack_flush (batch_start->clip_stack);
+
+  _cogl_matrix_stack_push (state->modelview_stack);
+
+  /* If we have transformed all our quads at log time then we ensure
+   * no further model transform is applied by loading the identity
+   * matrix here. We need to do this after flushing the clip stack
+   * because the clip stack flushing code can modify the matrix */
+  if (G_LIKELY (!(cogl_debug_flags & COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM)))
+    {
+      _cogl_matrix_stack_load_identity (state->modelview_stack);
+      _cogl_matrix_stack_flush_to_gl (state->modelview_stack,
+                                      COGL_MATRIX_MODELVIEW);
+    }
+
+  /* Setting up the clip state can sometimes also flush the projection
+     matrix so we should flush it again. This will be a no-op if the
+     clip code didn't modify the projection */
+  _cogl_matrix_stack_flush_to_gl (state->projection_stack,
+                                  COGL_MATRIX_PROJECTION);
+
+  batch_and_call (batch_start,
+                  batch_len,
+                  compare_entry_strides,
+                  _cogl_journal_flush_vbo_offsets_and_entries, /* callback */
+                  data);
+
+  _cogl_matrix_stack_pop (state->modelview_stack);
+
+  COGL_TIMER_STOP (_cogl_uprof_context,
+                   time_flush_clip_stack_pipeline_entries);
+}
+
+static gboolean
+compare_entry_clip_stacks (CoglJournalEntry *entry0, CoglJournalEntry *entry1)
+{
+  return entry0->clip_stack == entry1->clip_stack;
+}
+
 static CoglVertexArray *
 upload_vertices (GArray *vertices, CoglJournalFlushState *state)
 {
@@ -638,53 +701,43 @@ _cogl_journal_flush (void)
   framebuffer = _cogl_get_framebuffer ();
   modelview_stack = _cogl_framebuffer_get_modelview_stack (framebuffer);
   state.modelview_stack = modelview_stack;
-
-  _cogl_matrix_stack_push (modelview_stack);
-
-  /* If we have transformed all our quads at log time then we ensure no
-   * further model transform is applied by loading the identity matrix
-   * here... */
-  if (G_LIKELY (!(cogl_debug_flags & COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM)))
-    {
-      _cogl_matrix_stack_load_identity (modelview_stack);
-      _cogl_matrix_stack_flush_to_gl (modelview_stack, COGL_MATRIX_MODELVIEW);
-    }
+  state.projection_stack = _cogl_framebuffer_get_projection_stack (framebuffer);
 
   /* batch_and_call() batches a list of journal entries according to some
    * given criteria and calls a callback once for each determined batch.
    *
    * The process of flushing the journal is staggered to reduce the amount
    * of driver/GPU state changes necessary:
-   * 1) We split the entries according to the stride of the vertices:
+   * 1) We split the entries according to the clip state.
+   * 2) We split the entries according to the stride of the vertices:
    *      Each time the stride of our vertex data changes we need to call
    *      gl{Vertex,Color}Pointer to inform GL of new VBO offsets.
    *      Currently the only thing that affects the stride of our vertex data
    *      is the number of pipeline layers.
-   * 2) We split the entries explicitly by the number of pipeline layers:
+   * 3) We split the entries explicitly by the number of pipeline layers:
    *      We pad our vertex data when the number of layers is < 2 so that we
    *      can minimize changes in stride. Each time the number of layers
    *      changes we need to call glTexCoordPointer to inform GL of new VBO
    *      offsets.
-   * 3) We then split according to compatible Cogl pipelines:
+   * 4) We then split according to compatible Cogl pipelines:
    *      This is where we flush pipeline state
-   * 4) Finally we split according to modelview matrix changes:
+   * 5) Finally we split according to modelview matrix changes:
    *      This is when we finally tell GL to draw something.
    *      Note: Splitting by modelview changes is skipped when are doing the
    *      vertex transformation in software at log time.
    */
   batch_and_call ((CoglJournalEntry *)ctx->journal->data, /* first entry */
                   ctx->journal->len, /* max number of entries to consider */
-                  compare_entry_strides,
-                  _cogl_journal_flush_vbo_offsets_and_entries, /* callback */
+                  compare_entry_clip_stacks,
+                  _cogl_journal_flush_clip_stacks_and_entries, /* callback */
                   &state); /* data */
 
-  _cogl_matrix_stack_pop (modelview_stack);
-
   for (i = 0; i < ctx->journal->len; i++)
     {
       CoglJournalEntry *entry =
         &g_array_index (ctx->journal, CoglJournalEntry, i);
       _cogl_pipeline_journal_unref (entry->pipeline);
+      _cogl_clip_stack_unref (entry->clip_stack);
     }
 
   g_array_set_size (ctx->journal, 0);
@@ -700,9 +753,11 @@ _cogl_journal_init (void)
    * next the the journal is flushed. Note: This lets up flush things
    * that themselves depend on the journal, such as clip state. */
 
-  /* NB: the journal deals with flushing the modelview stack manually */
+  /* NB: the journal deals with flushing the modelview stack and clip
+     state manually */
   _cogl_framebuffer_flush_state (_cogl_get_framebuffer (),
-                                 COGL_FRAMEBUFFER_FLUSH_SKIP_MODELVIEW);
+                                 COGL_FRAMEBUFFER_FLUSH_SKIP_MODELVIEW |
+                                 COGL_FRAMEBUFFER_FLUSH_SKIP_CLIP_STATE);
 }
 
 void
@@ -887,6 +942,7 @@ _cogl_journal_log_quad (const float  *position,
     }
 
   entry->pipeline = _cogl_pipeline_journal_ref (source);
+  entry->clip_stack = _cogl_clip_stack_ref (_cogl_get_clip_stack ());
 
   if (G_UNLIKELY (source != pipeline))
     cogl_handle_unref (source);