[cogl] Improving Cogl journal to minimize driver overheads + GPU state changes
authorRobert Bragg <robert@linux.intel.com>
Wed, 17 Jun 2009 17:46:42 +0000 (18:46 +0100)
committerRobert Bragg <robert@linux.intel.com>
Tue, 30 Jun 2009 16:13:34 +0000 (17:13 +0100)
Previously the journal was always flushed at the end of
_cogl_rectangles_with_multitexture_coords, (i.e.  the end of any
cogl_rectangle* calls) but now we have broadened the potential for batching
geometry.  In ideal circumstances we will only flush once per scene.

In summary the journal works like this:

When you use any of the cogl_rectangle* APIs then nothing is emitted to the
GPU at this point, we just log one or more quads into the journal.  A
journal entry consists of the quad coordinates, an associated material
reference, and a modelview matrix.  Ideally the journal only gets flushed
once at the end of a scene, but in fact there are things to consider that
may cause unwanted flushing, including:

- modifying materials mid-scene
    This is because each quad in the journal has an associated material
    reference (i.e. not copy), so if you try and modify a material that is
    already referenced in the journal we force a flush first)

    NOTE: For now this means you should avoid using cogl_set_source_color()
      since that currently uses a single shared material. Later we
  should change it to use a pool of materials that is recycled
  when the journal is flushed.

- modifying any state that isn't currently logged, such as depth, fog and
  backface culling enables.

The first thing that happens when flushing, is to upload all the vertex data
associated with the journal into a single VBO.

We then go through a process of splitting up the journal into batches that
have compatible state so they can be emitted to the GPU together.  This is
currently broken up into 3 levels so we can stagger the state changes:

1) we break the journal up according to changes in the number of material layers
   associated with logged quads. The number of layers in a material determines
   the stride of the associated vertices, so we have to update our vertex
   array offsets at this level. (i.e. calling gl{Vertex,Color},Pointer etc)
2) we further split batches up according to material compatability. (e.g.
   materials with different textures) We flush material state at this level.
3) Finally we split batches up according to modelview changes. At this level
   we update the modelview matrix and actually emit the actual draw command.

This commit is largely about putting the initial design in-place; this will be
followed by other changes that take advantage of the extended batching.

26 files changed:
clutter/clutter-main.c
clutter/cogl/cogl-debug.h
clutter/cogl/cogl-material.h
clutter/cogl/cogl.h.in
clutter/cogl/common/cogl-clip-stack.c
clutter/cogl/common/cogl-debug.c
clutter/cogl/common/cogl-material-private.h
clutter/cogl/common/cogl-material.c
clutter/cogl/common/cogl-primitives.c
clutter/cogl/common/cogl-primitives.h
clutter/cogl/common/cogl-vertex-buffer.c
clutter/cogl/common/cogl.c
clutter/cogl/gl/cogl-context.c
clutter/cogl/gl/cogl-context.h
clutter/cogl/gl/cogl-fbo.c
clutter/cogl/gl/cogl-primitives.c
clutter/cogl/gl/cogl-program.c
clutter/cogl/gl/cogl-texture-private.h
clutter/cogl/gl/cogl-texture.c
clutter/eglnative/clutter-backend-egl.c
clutter/eglx/clutter-backend-egl.c
clutter/fruity/clutter-backend-fruity.c
clutter/glx/clutter-backend-glx.c
clutter/osx/clutter-stage-osx.c
clutter/sdl/clutter-backend-sdl.c
clutter/win32/clutter-backend-win32.c

index 5285f4d..ae66792 100644 (file)
@@ -409,6 +409,9 @@ _clutter_do_pick (ClutterStage   *stage,
   /* Calls should work under both GL and GLES, note GLES needs RGBA */
   glGetIntegerv(GL_VIEWPORT, viewport);
 
+  /* Make sure Cogl flushes any batched geometry to the GPU driver */
+  _cogl_flush ();
+
   /* Read the color of the screen co-ords pixel */
   glReadPixels (x, viewport[3] - y -1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
 
index 8aa1e8f..1b42358 100644 (file)
 G_BEGIN_DECLS
 
 typedef enum {
-  COGL_DEBUG_MISC           = 1 << 0,
-  COGL_DEBUG_TEXTURE        = 1 << 1,
-  COGL_DEBUG_MATERIAL       = 1 << 2,
-  COGL_DEBUG_SHADER         = 1 << 3,
-  COGL_DEBUG_OFFSCREEN      = 1 << 4,
-  COGL_DEBUG_DRAW           = 1 << 5,
-  COGL_DEBUG_PANGO          = 1 << 6,
-  COGL_DEBUG_RECTANGLES     = 1 << 7,
-  COGL_DEBUG_HANDLE         = 1 << 8,
-  COGL_DEBUG_BLEND_STRINGS  = 1 << 9
+  COGL_DEBUG_MISC             = 1 << 0,
+  COGL_DEBUG_TEXTURE          = 1 << 1,
+  COGL_DEBUG_MATERIAL         = 1 << 2,
+  COGL_DEBUG_SHADER           = 1 << 3,
+  COGL_DEBUG_OFFSCREEN        = 1 << 4,
+  COGL_DEBUG_DRAW             = 1 << 5,
+  COGL_DEBUG_PANGO            = 1 << 6,
+  COGL_DEBUG_RECTANGLES       = 1 << 7,
+  COGL_DEBUG_HANDLE           = 1 << 8,
+  COGL_DEBUG_BLEND_STRINGS    = 1 << 9,
+  COGL_DEBUG_DISABLE_BATCHING = 1 << 10
 } CoglDebugFlags;
 
 #ifdef COGL_ENABLE_DEBUG
index 786ff34..94ea5b2 100644 (file)
@@ -674,6 +674,16 @@ void cogl_material_set_layer_matrix (CoglHandle  material,
 const GList *cogl_material_get_layers (CoglHandle material);
 
 /**
+ * cogl_material_get_n_layers:
+ * @material: A CoglMaterial object
+ *
+ * Returns: The number of layers defined for the given material
+ *
+ * Since: 1.0
+ */
+int cogl_material_get_n_layers (CoglHandle material);
+
+/**
  * CoglMaterialLayerType:
  * @COGL_MATERIAL_LAYER_TYPE_TEXTURE: The layer represents a
  * <link linkend="cogl-Textures">Cogl texture</link>
@@ -713,15 +723,6 @@ CoglMaterialLayerType cogl_material_layer_get_type (CoglHandle layer_handle);
  *       likely return COGL_INVALID_HANDLE if you try to get the texture.
  *       Considering this, you can call cogl_material_layer_get_type first,
  *       to check it is of type COGL_MATERIAL_LAYER_TYPE_TEXTURE.
- *
- * Note: It is possible for a layer object of type
- *       COGL_MATERIAL_LAYER_TYPE_TEXTURE to be realized before a texture
- *       object has been associated with the layer. For example this happens
- *       if you setup layer combining for a given layer index before calling
- *       cogl_material_set_layer for that index.
- *
- * Returns: A CoglHandle to the layers texture object or COGL_INVALID_HANDLE
- *          if a texture has not been set yet.
  */
 CoglHandle cogl_material_layer_get_texture (CoglHandle layer_handle);
 
index adc3292..cbd70f0 100644 (file)
@@ -760,6 +760,20 @@ void            cogl_flush_gl_state         (int      flags);
 /* private */
 void            _cogl_set_indirect_context  (gboolean indirect);
 
+/* private
+ *
+ * cogl_flush:
+ *
+ * As an optimization Cogl drawing functions may batch up primitives
+ * internally, so you need to call _cogl_flush to ensure that the
+ * drawing operations you have submitted for the current frame get
+ * flushed through to the driver and GPU.
+ *
+ * This must be called before issuing a swap buffers.
+ */
+void            _cogl_flush (void);
+
+
 G_END_DECLS
 
 #undef __COGL_H_INSIDE__
index 3ef1b2b..9efa5c2 100644 (file)
@@ -324,6 +324,10 @@ _cogl_clip_stack_rebuild (void)
 
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
+  /* The current primitive journal does not support tracking changes to the
+   * clip stack...  */
+  _cogl_journal_flush ();
+
   stack = (CoglClipStack *) ctx->clip.stacks->data;
 
   ctx->clip.stack_dirty = FALSE;
index b6dfd75..7eb8561 100644 (file)
@@ -40,7 +40,8 @@ static const GDebugKey cogl_debug_keys[] = {
   { "pango", COGL_DEBUG_PANGO },
   { "rectangles", COGL_DEBUG_RECTANGLES },
   { "handle", COGL_DEBUG_HANDLE },
-  { "blend-strings", COGL_DEBUG_BLEND_STRINGS }
+  { "blend-strings", COGL_DEBUG_BLEND_STRINGS },
+  { "disable-batching", COGL_DEBUG_DISABLE_BATCHING }
 };
 
 static const gint n_cogl_debug_keys = G_N_ELEMENTS (cogl_debug_keys);
index e61ab97..6fe52be 100644 (file)
 typedef struct _CoglMaterial         CoglMaterial;
 typedef struct _CoglMaterialLayer     CoglMaterialLayer;
 
+typedef enum _CoglMaterialEqualFlags
+{
+  /* Return FALSE if any component of either material isn't set to its
+   * default value. (Note: if the materials have corresponding flush
+   * options indicating that e.g. the material color won't be flushed then
+   * this will not assert a default color value.) */
+  COGL_MATERIAL_EQUAL_FLAGS_ASSERT_ALL_DEFAULTS   = 1L<<0,
+
+} CoglMaterialEqualFlags;
+
 /* XXX: I don't think gtk-doc supports having private enums so these aren't
  * bundled in with CoglMaterialLayerFlags */
 typedef enum _CoglMaterialLayerPrivFlags
@@ -90,17 +100,19 @@ struct _CoglMaterialLayer
 
 typedef enum _CoglMaterialFlags
 {
-  COGL_MATERIAL_FLAG_ENABLE_BLEND          = 1L<<0,
-  COGL_MATERIAL_FLAG_SHOWN_SAMPLER_WARNING  = 1L<<1,
-  COGL_MATERIAL_FLAG_DEFAULT_COLOR          = 1L<<2,
-  COGL_MATERIAL_FLAG_DEFAULT_GL_MATERIAL    = 1L<<3,
-  COGL_MATERIAL_FLAG_DEFAULT_ALPHA_FUNC     = 1L<<4,
-  COGL_MATERIAL_FLAG_DEFAULT_BLEND_FUNC     = 1L<<5
+  COGL_MATERIAL_FLAG_SHOWN_SAMPLER_WARNING  = 1L<<0,
+
+  COGL_MATERIAL_FLAG_DEFAULT_COLOR          = 1L<<1,
+  COGL_MATERIAL_FLAG_DEFAULT_GL_MATERIAL    = 1L<<2,
+  COGL_MATERIAL_FLAG_DEFAULT_ALPHA_FUNC     = 1L<<3,
+  COGL_MATERIAL_FLAG_ENABLE_BLEND          = 1L<<4,
+  COGL_MATERIAL_FLAG_DEFAULT_BLEND          = 1L<<5
 } CoglMaterialFlags;
 
 struct _CoglMaterial
 {
   CoglHandleObject _parent;
+  gulong           journal_ref_count;
 
   gulong    flags;
 
@@ -130,6 +142,7 @@ struct _CoglMaterial
   GLint blend_dst_factor_rgb;
 
   GList           *layers;
+  guint     n_layers;
 };
 
 /*
@@ -183,39 +196,69 @@ typedef enum _CoglMaterialLayerFlags
 gulong _cogl_material_layer_get_flags (CoglHandle layer_handle);
 
 /*
- * CoglMaterialFlushOption:
- * @COGL_MATERIAL_FLUSH_FALLBACK_MASK: Follow this by a guin32 mask
- *      of the layers that can't be supported with the user supplied texture
- *      and need to be replaced with fallback textures. (1 = fallback, and the
- *      least significant bit = layer 0)
- * @COGL_MATERIAL_FLUSH_DISABLE_MASK: Follow this by a guint32 mask
- *      of the layers that you want to completly disable texturing for
- *      (1 = fallback, and the least significant bit = layer 0)
- * @COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE: Follow this by a GLuint OpenGL texture
- *      name to override the texture used for layer 0 of the material. This is
- *      intended for dealing with sliced textures where you will need to point
- *      to each of the texture slices in turn when drawing your geometry.
- *      Passing a value of 0 is the same as not passing the option at all.
+ * CoglMaterialFlushFlag:
+ * @COGL_MATERIAL_FLUSH_FALLBACK_MASK: The fallback_layers member is set to
+ *      a guint32 mask of the layers that can't be supported with the user
+ *      supplied texture and need to be replaced with fallback textures. (1 =
+ *      fallback, and the least significant bit = layer 0)
+ * @COGL_MATERIAL_FLUSH_DISABLE_MASK: The disable_layers member is set to
+ *      a guint32 mask of the layers that you want to completly disable
+ *      texturing for (1 = fallback, and the least significant bit = layer 0)
+ * @COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE: The layer0_override_texture member is
+ *      set to a GLuint OpenGL texture name to override the texture used for
+ *      layer 0 of the material. This is intended for dealing with sliced
+ *      textures where you will need to point to each of the texture slices in
+ *      turn when drawing your geometry.  Passing a value of 0 is the same as
+ *      not passing the option at all.
+ * @COGL_MATERIAL_FLUSH_SKIP_GL_COLOR: When flushing the GL state for the
+ *      material don't call glColor.
+ */
+typedef enum _CoglMaterialFlushFlag
+{
+  COGL_MATERIAL_FLUSH_FALLBACK_MASK     = 1L<<0,
+  COGL_MATERIAL_FLUSH_DISABLE_MASK      = 1L<<1,
+  COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE   = 1L<<2,
+  COGL_MATERIAL_FLUSH_SKIP_GL_COLOR     = 1L<<3
+} CoglMaterialFlushFlag;
+
+/*
+ * CoglMaterialFlushOptions:
+ *
  */
-typedef enum _CoglMaterialFlushOption
+typedef struct _CoglMaterialFlushOptions
 {
-  COGL_MATERIAL_FLUSH_FALLBACK_MASK = 1,
-  COGL_MATERIAL_FLUSH_DISABLE_MASK,
-  COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE,
-} CoglMaterialFlushOption;
+  CoglMaterialFlushFlag flags;
+
+  guint32               fallback_layers;
+  guint32               disable_layers;
+  GLuint                layer0_override_texture;
+} CoglMaterialFlushOptions;
 
 /*
  * cogl_material_flush_gl_state:
  * @material: A CoglMaterial object
  * @...: A NULL terminated list of (CoglMaterialFlushOption, data) pairs
  *
- * This function commits the state of the specified CoglMaterial - including
- * the texture state for all the layers - to the OpenGL[ES] driver.
+ * Note: It is possible for a layer object of type
+ *       COGL_MATERIAL_LAYER_TYPE_TEXTURE to be realized before a texture
+ *       object has been associated with the layer. For example this happens
+ *       if you setup layer combining for a given layer index before calling
+ *       cogl_material_set_layer for that index.
  *
- * Since 1.0
+ * Returns: A CoglHandle to the layers texture object or COGL_INVALID_HANDLE
+ *          if a texture has not been set yet.
  */
 void _cogl_material_flush_gl_state (CoglHandle material,
-                                    ...) G_GNUC_NULL_TERMINATED;
+                                    CoglMaterialFlushOptions *options);
+
+gboolean _cogl_material_equal (CoglHandle material0_handle,
+                               CoglMaterialFlushOptions *material0_flush_options,
+                               CoglHandle material1_handle,
+                               CoglMaterialFlushOptions *material1_flush_options,
+                               CoglMaterialEqualFlags flags);
+
+CoglHandle _cogl_material_journal_ref (CoglHandle material_handle);
+void _cogl_material_journal_unref (CoglHandle material_handle);
 
 
 #endif /* __COGL_MATERIAL_PRIVATE_H */
index d39120b..a2ad30f 100644 (file)
@@ -111,9 +111,10 @@ cogl_material_new (void)
 #endif
   material->blend_src_factor_rgb = GL_ONE;
   material->blend_dst_factor_rgb = GL_ONE_MINUS_SRC_ALPHA;
-  material->flags |= COGL_MATERIAL_FLAG_DEFAULT_BLEND_FUNC;
+  material->flags |= COGL_MATERIAL_FLAG_DEFAULT_BLEND;
 
   material->layers = NULL;
+  material->n_layers = 0;
 
   return _cogl_material_handle_new (material);
 }
@@ -130,6 +131,7 @@ _cogl_material_free (CoglMaterial *material)
   g_free (material);
 }
 
+
 static void
 handle_automatic_blend_enable (CoglMaterial *material)
 {
@@ -139,6 +141,12 @@ handle_automatic_blend_enable (CoglMaterial *material)
    * a flag to know when it's user configured, so we don't trash it */
 
   material->flags &= ~COGL_MATERIAL_FLAG_ENABLE_BLEND;
+
+  /* XXX: Uncomment this to disable all blending */
+#if 0
+  return;
+#endif
+
   for (tmp = material->layers; tmp != NULL; tmp = tmp->next)
     {
       CoglMaterialLayer *layer = tmp->data;
@@ -156,6 +164,16 @@ handle_automatic_blend_enable (CoglMaterial *material)
     material->flags |= COGL_MATERIAL_FLAG_ENABLE_BLEND;
 }
 
+/* If primitives have been logged in the journal referencing the current
+ * state of this material we need to flush the journal before we can
+ * modify it... */
+static void
+_cogl_material_pre_change_notify (CoglMaterial *material)
+{
+  if (material->journal_ref_count)
+    _cogl_journal_flush ();
+}
+
 void
 cogl_material_get_color (CoglHandle  handle,
                          CoglColor  *color)
@@ -191,6 +209,9 @@ cogl_material_set_color (CoglHandle       handle,
   if (memcmp (unlit, material->unlit, sizeof (unlit)) == 0)
     return;
 
+  /* possibly flush primitives referencing the current state... */
+  _cogl_material_pre_change_notify (material);
+
   memcpy (material->unlit, unlit, sizeof (unlit));
 
   material->flags &= ~COGL_MATERIAL_FLAG_DEFAULT_COLOR;
@@ -255,6 +276,9 @@ cogl_material_set_ambient (CoglHandle handle,
 
   material = _cogl_material_pointer_from_handle (handle);
 
+  /* possibly flush primitives referencing the current state... */
+  _cogl_material_pre_change_notify (material);
+
   ambient = material->ambient;
   ambient[0] = cogl_color_get_red_float (ambient_color);
   ambient[1] = cogl_color_get_green_float (ambient_color);
@@ -292,6 +316,9 @@ cogl_material_set_diffuse (CoglHandle handle,
 
   material = _cogl_material_pointer_from_handle (handle);
 
+  /* possibly flush primitives referencing the current state... */
+  _cogl_material_pre_change_notify (material);
+
   diffuse = material->diffuse;
   diffuse[0] = cogl_color_get_red_float (diffuse_color);
   diffuse[1] = cogl_color_get_green_float (diffuse_color);
@@ -337,6 +364,9 @@ cogl_material_set_specular (CoglHandle handle,
 
   material = _cogl_material_pointer_from_handle (handle);
 
+  /* possibly flush primitives referencing the current state... */
+  _cogl_material_pre_change_notify (material);
+
   specular = material->specular;
   specular[0] = cogl_color_get_red_float (specular_color);
   specular[1] = cogl_color_get_green_float (specular_color);
@@ -372,6 +402,9 @@ cogl_material_set_shininess (CoglHandle handle,
 
   material = _cogl_material_pointer_from_handle (handle);
 
+  /* possibly flush primitives referencing the current state... */
+  _cogl_material_pre_change_notify (material);
+
   material->shininess = (GLfloat)shininess * 128.0;
 
   material->flags &= ~COGL_MATERIAL_FLAG_DEFAULT_GL_MATERIAL;
@@ -405,6 +438,9 @@ cogl_material_set_emission (CoglHandle handle,
 
   material = _cogl_material_pointer_from_handle (handle);
 
+  /* possibly flush primitives referencing the current state... */
+  _cogl_material_pre_change_notify (material);
+
   emission = material->emission;
   emission[0] = cogl_color_get_red_float (emission_color);
   emission[1] = cogl_color_get_green_float (emission_color);
@@ -424,6 +460,10 @@ cogl_material_set_alpha_test_function (CoglHandle handle,
   g_return_if_fail (cogl_is_material (handle));
 
   material = _cogl_material_pointer_from_handle (handle);
+
+  /* possibly flush primitives referencing the current state... */
+  _cogl_material_pre_change_notify (material);
+
   material->alpha_func = alpha_func;
   material->alpha_func_reference = (GLfloat)alpha_reference;
 
@@ -571,6 +611,9 @@ cogl_material_set_blend (CoglHandle handle,
       a = &statements[1];
     }
 
+  /* possibly flush primitives referencing the current state... */
+  _cogl_material_pre_change_notify (material);
+
 #ifndef HAVE_COGL_GLES
   setup_blend_state (rgb,
                      &material->blend_equation_rgb,
@@ -587,7 +630,7 @@ cogl_material_set_blend (CoglHandle handle,
                      &material->blend_dst_factor_rgb);
 #endif
 
-  material->flags &= ~COGL_MATERIAL_FLAG_DEFAULT_BLEND_FUNC;
+  material->flags &= ~COGL_MATERIAL_FLAG_DEFAULT_BLEND;
 
   return TRUE;
 }
@@ -604,13 +647,16 @@ cogl_material_set_blend_constant (CoglHandle handle,
 
   material = _cogl_material_pointer_from_handle (handle);
 
+  /* possibly flush primitives referencing the current state... */
+  _cogl_material_pre_change_notify (material);
+
   constant = material->blend_constant;
   constant[0] = cogl_color_get_red_float (constant_color);
   constant[1] = cogl_color_get_green_float (constant_color);
   constant[2] = cogl_color_get_blue_float (constant_color);
   constant[3] = cogl_color_get_alpha_float (constant_color);
 
-  material->flags &= ~COGL_MATERIAL_FLAG_DEFAULT_BLEND_FUNC;
+  material->flags &= ~COGL_MATERIAL_FLAG_DEFAULT_BLEND;
 #endif
 }
 
@@ -682,20 +728,26 @@ cogl_material_set_layer (CoglHandle material_handle,
 {
   CoglMaterial     *material;
   CoglMaterialLayer *layer;
-  int               n_layers;
 
   g_return_if_fail (cogl_is_material (material_handle));
   g_return_if_fail (texture_handle == COGL_INVALID_HANDLE
                     || cogl_is_texture (texture_handle));
 
   material = _cogl_material_pointer_from_handle (material_handle);
+
+  /* possibly flush primitives referencing the current state... */
+  _cogl_material_pre_change_notify (material);
+
   layer = _cogl_material_get_layer (material_handle, layer_index, TRUE);
 
   if (texture_handle == layer->texture)
     return;
 
-  n_layers = g_list_length (material->layers);
-  if (n_layers >= CGL_MAX_COMBINED_TEXTURE_IMAGE_UNITS)
+  /* possibly flush primitives referencing the current state... */
+  _cogl_material_pre_change_notify (material);
+
+  material->n_layers = g_list_length (material->layers);
+  if (material->n_layers >= CGL_MAX_COMBINED_TEXTURE_IMAGE_UNITS)
     {
       if (!(material->flags & COGL_MATERIAL_FLAG_SHOWN_SAMPLER_WARNING))
        {
@@ -856,6 +908,9 @@ cogl_material_set_layer_combine (CoglHandle handle,
       a = &statements[1];
     }
 
+  /* possibly flush primitives referencing the current state... */
+  _cogl_material_pre_change_notify (material);
+
   setup_texture_combine_state (rgb,
                                &layer->texture_combine_rgb_func,
                                layer->texture_combine_rgb_src,
@@ -886,6 +941,9 @@ cogl_material_set_layer_combine_constant (CoglHandle handle,
   material = _cogl_material_pointer_from_handle (handle);
   layer = _cogl_material_get_layer (material, layer_index, TRUE);
 
+  /* possibly flush primitives referencing the current state... */
+  _cogl_material_pre_change_notify (material);
+
   constant = layer->texture_combine_constant;
   constant[0] = cogl_color_get_red_float (constant_color);
   constant[1] = cogl_color_get_green_float (constant_color);
@@ -909,6 +967,9 @@ cogl_material_set_layer_matrix (CoglHandle material_handle,
   material = _cogl_material_pointer_from_handle (material_handle);
   layer = _cogl_material_get_layer (material, layer_index, TRUE);
 
+  /* possibly flush primitives referencing the current state... */
+  _cogl_material_pre_change_notify (material);
+
   layer->matrix = *matrix;
 
   layer->flags |= COGL_MATERIAL_LAYER_FLAG_DIRTY;
@@ -935,6 +996,10 @@ cogl_material_remove_layer (CoglHandle material_handle,
   g_return_if_fail (cogl_is_material (material_handle));
 
   material = _cogl_material_pointer_from_handle (material_handle);
+
+  /* possibly flush primitives referencing the current state... */
+  _cogl_material_pre_change_notify (material);
+
   for (tmp = material->layers; tmp != NULL; tmp = tmp->next)
     {
       layer = tmp->data;
@@ -943,6 +1008,7 @@ cogl_material_remove_layer (CoglHandle material_handle,
          CoglHandle handle = (CoglHandle) layer;
          cogl_handle_unref (handle);
          material->layers = g_list_remove (material->layers, layer);
+          material->n_layers--;
          break;
        }
     }
@@ -992,6 +1058,18 @@ cogl_material_get_layers (CoglHandle material_handle)
   return material->layers;
 }
 
+int
+cogl_material_get_n_layers (CoglHandle material_handle)
+{
+  CoglMaterial *material;
+
+  g_return_val_if_fail (cogl_is_material (material_handle), 0);
+
+  material = _cogl_material_pointer_from_handle (material_handle);
+
+  return material->n_layers;
+}
+
 CoglMaterialLayerType
 cogl_material_layer_get_type (CoglHandle layer_handle)
 {
@@ -1318,7 +1396,10 @@ _cogl_material_flush_layers_gl_state (CoglMaterial *material,
                 !gl_layer_info->disabled))
 #endif
             {
+  /* XXX: Debug: Comment this out to disable all texturing: */
+#if 1
               GE (glEnable (gl_target));
+#endif
             }
         }
       else
@@ -1369,18 +1450,30 @@ _cogl_material_flush_layers_gl_state (CoglMaterial *material,
 }
 
 static void
-_cogl_material_flush_base_gl_state (CoglMaterial *material)
+_cogl_material_flush_base_gl_state (CoglMaterial *material,
+                                    gboolean skip_gl_color)
 {
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
-  if (!(ctx->current_material_flags & COGL_MATERIAL_FLAG_DEFAULT_COLOR
-        && material->flags & COGL_MATERIAL_FLAG_DEFAULT_COLOR))
+  /* XXX:
+   * Currently we only don't update state when the flags indicate that the
+   * current material uses the defaults, and the new material also uses the
+   * defaults, but we could do deeper comparisons of state. */
+
+  if (!skip_gl_color)
     {
-      /* GLES doesn't have glColor4fv... */
-      GE (glColor4f (material->unlit[0],
-                     material->unlit[1],
-                     material->unlit[2],
-                     material->unlit[3]));
+      if (!(ctx->current_material_flags & COGL_MATERIAL_FLAG_DEFAULT_COLOR
+            && material->flags & COGL_MATERIAL_FLAG_DEFAULT_COLOR) ||
+          /* Assume if we were previously told to skip the color, then
+           * the current color needs updating... */
+          ctx->current_material_flush_options.flags &
+          COGL_MATERIAL_FLUSH_SKIP_GL_COLOR)
+        {
+          GE (glColor4f (material->unlit[0],
+                         material->unlit[1],
+                         material->unlit[2],
+                         material->unlit[3]));
+        }
     }
 
   if (!(ctx->current_material_flags & COGL_MATERIAL_FLAG_DEFAULT_GL_MATERIAL
@@ -1401,8 +1494,8 @@ _cogl_material_flush_base_gl_state (CoglMaterial *material)
       GE (glAlphaFunc (material->alpha_func, material->alpha_func_reference));
     }
 
-  if (!(ctx->current_material_flags & COGL_MATERIAL_FLAG_DEFAULT_BLEND_FUNC
-        && material->flags & COGL_MATERIAL_FLAG_DEFAULT_BLEND_FUNC))
+  if (!(ctx->current_material_flags & COGL_MATERIAL_FLAG_DEFAULT_BLEND
+        && material->flags & COGL_MATERIAL_FLAG_DEFAULT_BLEND))
     {
 #if defined (HAVE_COGL_GLES2)
       gboolean have_blend_equation_seperate = TRUE;
@@ -1441,32 +1534,33 @@ _cogl_material_flush_base_gl_state (CoglMaterial *material)
 }
 
 void
-_cogl_material_flush_gl_state (CoglHandle handle, ...)
+_cogl_material_flush_gl_state (CoglHandle handle,
+                               CoglMaterialFlushOptions *options)
 {
-  CoglMaterial           *material;
-  va_list                 ap;
-  CoglMaterialFlushOption option;
-  guint32                 fallback_layers = 0;
-  guint32                 disable_layers = 0;
-  GLuint                  layer0_override_texture = 0;
+  CoglMaterial  *material;
+  guint32        fallback_layers = 0;
+  guint32        disable_layers = 0;
+  GLuint         layer0_override_texture = 0;
+  gboolean       skip_gl_color = FALSE;
 
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
   material = _cogl_material_pointer_from_handle (handle);
 
-  _cogl_material_flush_base_gl_state (material);
-
-  va_start (ap, handle);
-  while ((option = va_arg (ap, CoglMaterialFlushOption)))
+  if (options)
     {
-      if (option == COGL_MATERIAL_FLUSH_FALLBACK_MASK)
-        fallback_layers = va_arg (ap, guint32);
-      else if (option == COGL_MATERIAL_FLUSH_DISABLE_MASK)
-        disable_layers = va_arg (ap, guint32);
-      else if (option == COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE)
-        layer0_override_texture = va_arg (ap, GLuint);
+      if (options->flags & COGL_MATERIAL_FLUSH_FALLBACK_MASK)
+        fallback_layers = options->fallback_layers;
+      if (options->flags & COGL_MATERIAL_FLUSH_DISABLE_MASK)
+        disable_layers = options->disable_layers;
+      if (options->flags & COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE)
+        layer0_override_texture = options->layer0_override_texture;
+      if (options->flags & COGL_MATERIAL_FLUSH_SKIP_GL_COLOR)
+        skip_gl_color = TRUE;
     }
-  va_end (ap);
+
+  _cogl_material_flush_base_gl_state (material,
+                                      skip_gl_color);
 
   _cogl_material_flush_layers_gl_state (material,
                                         fallback_layers,
@@ -1483,6 +1577,158 @@ _cogl_material_flush_gl_state (CoglHandle handle, ...)
 
   ctx->current_material = handle;
   ctx->current_material_flags = material->flags;
+  if (options)
+    ctx->current_material_flush_options = *options;
+  else
+    memset (&ctx->current_material_flush_options,
+            0, sizeof (CoglMaterialFlushOptions));
+}
+
+gboolean
+_cogl_material_equal (CoglHandle material0_handle,
+                      CoglMaterialFlushOptions *material0_flush_options,
+                      CoglHandle material1_handle,
+                      CoglMaterialFlushOptions *material1_flush_options,
+                      CoglMaterialEqualFlags flags)
+{
+  CoglMaterial  *material0;
+  CoglMaterial  *material1;
+  GList         *l0, *l1;
+
+  if (!(flags & COGL_MATERIAL_EQUAL_FLAGS_ASSERT_ALL_DEFAULTS))
+    {
+      g_critical ("FIXME: _cogl_material_equal doesn't yet support "
+                  "deep comparisons of materials");
+      return FALSE;
+    }
+  /* Note: the following code is written with the assumption this
+   * constraint will go away*/
+
+  material0 = _cogl_material_pointer_from_handle (material0_handle);
+  material1 = _cogl_material_pointer_from_handle (material1_handle);
+
+  if (!((material0_flush_options->flags & COGL_MATERIAL_FLUSH_SKIP_GL_COLOR &&
+         material1_flush_options->flags & COGL_MATERIAL_FLUSH_SKIP_GL_COLOR)))
+    {
+      if ((material0->flags & COGL_MATERIAL_FLAG_DEFAULT_COLOR) !=
+          (material1->flags & COGL_MATERIAL_FLAG_DEFAULT_COLOR))
+        return FALSE;
+      else if (flags & COGL_MATERIAL_EQUAL_FLAGS_ASSERT_ALL_DEFAULTS &&
+               !(material0->flags & COGL_MATERIAL_FLAG_DEFAULT_COLOR))
+        return FALSE;
+      else if (!memcmp (material0->unlit, material1->unlit,
+                        sizeof (material0->unlit)))
+        return FALSE;
+    }
+
+  if ((material0->flags & COGL_MATERIAL_FLAG_DEFAULT_GL_MATERIAL) !=
+      (material1->flags & COGL_MATERIAL_FLAG_DEFAULT_GL_MATERIAL))
+    return FALSE;
+  else if (flags & COGL_MATERIAL_EQUAL_FLAGS_ASSERT_ALL_DEFAULTS &&
+           !(material0->flags & COGL_MATERIAL_FLAG_DEFAULT_GL_MATERIAL))
+    return FALSE;
+#if 0
+  else if (!_deep_are_gl_materials_equal ())
+    return FALSE;
+#endif
+
+  if ((material0->flags & COGL_MATERIAL_FLAG_DEFAULT_ALPHA_FUNC) !=
+      (material1->flags & COGL_MATERIAL_FLAG_DEFAULT_ALPHA_FUNC))
+    return FALSE;
+  else if (flags & COGL_MATERIAL_EQUAL_FLAGS_ASSERT_ALL_DEFAULTS &&
+           !(material0->flags & COGL_MATERIAL_FLAG_DEFAULT_ALPHA_FUNC))
+    return FALSE;
+#if 0
+  else if (!_deep_are_alpha_funcs_equal ())
+    return FALSE;
+#endif
+
+  if ((material0->flags & COGL_MATERIAL_FLAG_ENABLE_BLEND) !=
+      (material1->flags & COGL_MATERIAL_FLAG_ENABLE_BLEND))
+    return FALSE;
+  /* XXX: potentially blending could be "enabled" but the blend mode
+   * could be equivalent to being disabled. */
+
+  if (material0->flags & COGL_MATERIAL_FLAG_ENABLE_BLEND)
+    {
+      if ((material0->flags & COGL_MATERIAL_FLAG_DEFAULT_BLEND) !=
+          (material1->flags & COGL_MATERIAL_FLAG_DEFAULT_BLEND))
+        return FALSE;
+      else if (flags & COGL_MATERIAL_EQUAL_FLAGS_ASSERT_ALL_DEFAULTS &&
+               !(material0->flags & COGL_MATERIAL_FLAG_DEFAULT_BLEND))
+        return FALSE;
+#if 0
+      else if (!_deep_is_blend_equal ())
+        return FALSE;
+#endif
+    }
+
+  if (material0_flush_options->fallback_layers !=
+      material1_flush_options->fallback_layers ||
+      material0_flush_options->disable_layers !=
+      material1_flush_options->disable_layers)
+    return FALSE;
+
+  l0 = material0->layers;
+  l1 = material1->layers;
+
+  while (l0 && l1)
+    {
+      CoglMaterialLayer *layer0;
+      CoglMaterialLayer *layer1;
+
+      if ((l0 == NULL && l1 != NULL) ||
+          (l1 == NULL && l0 != NULL))
+        return FALSE;
+
+      layer0 = l0->data;
+      layer1 = l1->data;
+
+      if (layer0->texture != layer1->texture)
+        return FALSE;
+
+      if ((layer0->flags & COGL_MATERIAL_LAYER_FLAG_DEFAULT_COMBINE) !=
+          (layer1->flags & COGL_MATERIAL_LAYER_FLAG_DEFAULT_COMBINE))
+        return FALSE;
+      else if (flags & COGL_MATERIAL_EQUAL_FLAGS_ASSERT_ALL_DEFAULTS &&
+               !(layer0->flags & COGL_MATERIAL_LAYER_FLAG_DEFAULT_COMBINE))
+        return FALSE;
+#if 0
+      else if (!_deep_are_layer_combines_equal ())
+        return FALSE;
+#endif
+
+      l0 = l0->next;
+      l1 = l1->next;
+    }
+
+  if ((l0 == NULL && l1 != NULL) ||
+      (l1 == NULL && l0 != NULL))
+    return FALSE;
+
+  return TRUE;
+}
+
+/* While a material is referenced by the Cogl journal we can not allow
+ * modifications, so this gives us a mechanism to track journal
+ * references separately */
+CoglHandle
+_cogl_material_journal_ref (CoglHandle material_handle)
+{
+  CoglMaterial *material =
+    material = _cogl_material_pointer_from_handle (material_handle);
+  material->journal_ref_count++;
+  cogl_handle_ref (material_handle);
+  return material_handle;
+}
+
+void
+_cogl_material_journal_unref (CoglHandle material_handle)
+{
+  CoglMaterial *material =
+    material = _cogl_material_pointer_from_handle (material_handle);
+  material->journal_ref_count--;
+  cogl_handle_unref (material_handle);
 }
 
 /* TODO: Should go in cogl.c, but that implies duplication which is also
@@ -1556,6 +1802,10 @@ cogl_material_set_layer_filters (CoglHandle         handle,
   g_return_if_fail (cogl_is_material (handle));
 
   material = _cogl_material_pointer_from_handle (handle);
+
+  /* possibly flush primitives referencing the current state... */
+  _cogl_material_pre_change_notify (material);
+
   layer = _cogl_material_get_layer (material, layer_index, TRUE);
 
   layer->min_filter = min_filter;
index dafe92f..9e3ea89 100644 (file)
 
 #ifdef HAVE_COGL_GL
 
+#define glGenBuffers ctx->pf_glGenBuffersARB
+#define glBindBuffer ctx->pf_glBindBufferARB
+#define glBufferData ctx->pf_glBufferDataARB
+#define glBufferSubData ctx->pf_glBufferSubDataARB
+#define glDeleteBuffers ctx->pf_glDeleteBuffersARB
 #define glClientActiveTexture ctx->pf_glClientActiveTexture
 
+#elif defined (HAVE_COGL_GLES2)
+
+#include "../gles/cogl-gles2-wrapper.h"
+
+#endif
+
+
+/* XXX NB:
+ * Our journal's vertex data is arranged as follows:
+ * 4 vertices per quad:
+ *    2 or GLfloats per position
+ *    4 RGBA GLubytes,
+ *    2 GLfloats per tex coord * n_layers
+ *
+ * So for a given number of layers this gets the stride in
+ * 32bit words:
+ */
+#define GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS(N_LAYERS) \
+  (2 + 1 + 2 * (N_LAYERS))
+
+
+typedef void (*CoglJournalBatchCallback) (CoglJournalEntry *start,
+                                          int n_entries,
+                                          void *data);
+typedef gboolean (*CoglJournalBatchTest) (CoglJournalEntry *entry0,
+                                          CoglJournalEntry *entry1);
+
+typedef struct _CoglJournalFlushState
+{
+  /* Note: this is a pointer to handle fallbacks. It normally holds a VBO
+   * offset, but when the driver doesn't support VBOs then this points into
+   * our GArray of logged vertices. */
+  char *              vbo_offset;
+  GLuint              vertex_offset;
+#ifndef HAVE_COGL_GL
+  CoglJournalIndices *indices;
+  size_t              indices_type_size;
 #endif
+} CoglJournalFlushState;
 
 /* these are defined in the particular backend */
 void _cogl_path_add_node    (gboolean new_sub_path,
@@ -51,94 +94,107 @@ void _cogl_path_add_node    (gboolean new_sub_path,
 void _cogl_path_fill_nodes    ();
 void _cogl_path_stroke_nodes  ();
 
-static void
-_cogl_journal_flush_quad_batch (CoglJournalEntry *batch_start,
-                                gint              batch_len,
-                                GLfloat          *vertex_pointer)
+void
+_cogl_journal_dump_quad_vertices (guint8 *data, int n_layers)
 {
-  gsize   stride;
-  int     i;
-  gulong  enable_flags = 0;
-  guint32 disable_mask;
-  int     prev_n_texcoord_arrays_enabled;
+  size_t stride = GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS (n_layers);
+  int i;
 
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
-  /* XXX NB:
-   * Our vertex data is arranged as follows:
-   * 4 vertices per quad: 2 GLfloats per position,
-   *                      2 GLfloats per tex coord * n_layers
-   */
-  stride = 2 + 2 * batch_start->n_layers;
-  stride *= sizeof (GLfloat);
-
-  disable_mask = (1 << batch_start->n_layers) - 1;
-  disable_mask = ~disable_mask;
+  g_print ("stride = %d (%d bytes)\n", (int)stride, (int)stride * 4);
 
-  _cogl_material_flush_gl_state (ctx->source_material,
-                                 COGL_MATERIAL_FLUSH_FALLBACK_MASK,
-                                 batch_start->fallback_mask,
-                                 COGL_MATERIAL_FLUSH_DISABLE_MASK,
-                                 disable_mask,
-                                 /* Redundant when dealing with unsliced
-                                  * textures but does no harm... */
-                                 COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE,
-                                 batch_start->layer0_override_texture,
-                                 NULL);
-
-  for (i = 0; i < batch_start->n_layers; i++)
+  for (i = 0; i < 4; i++)
     {
-      GE (glClientActiveTexture (GL_TEXTURE0 + i));
-      GE (glEnableClientState (GL_TEXTURE_COORD_ARRAY));
-      GE (glTexCoordPointer (2, GL_FLOAT, stride, vertex_pointer + 2 + 2 * i));
+      float *v = (float *)data + (i * stride);
+      guint8 *c = data + 8 + (i * stride * 4);
+      int j;
+
+      g_print ("v%d: x = %f, y = %f, rgba=0x%02X%02X%02X%02X",
+               i, v[0], v[1], c[0], c[1], c[2], c[3]);
+      for (j = 0; j < n_layers; j++)
+        {
+          float *t = v + 3 + 2 * j;
+          g_print (", tx%d = %f, ty%d = %f", j, t[0], j, t[1]);
+        }
+      g_print ("\n");
     }
-  prev_n_texcoord_arrays_enabled =
-    ctx->n_texcoord_arrays_enabled;
-  ctx->n_texcoord_arrays_enabled = batch_start->n_layers;
-  for (; i < prev_n_texcoord_arrays_enabled; i++)
+}
+
+void
+_cogl_journal_dump_quad_batch (guint8 *data, int n_layers, int n_quads)
+{
+  size_t byte_stride = GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS (n_layers) * 4;
+  int i;
+
+  g_print ("_cogl_journal_dump_quad_batch: n_layers = %d, n_quads = %d\n",
+           n_layers, n_quads);
+  for (i = 0; i < n_quads; i++)
+    _cogl_journal_dump_quad_vertices (data + byte_stride * 4 * i, n_layers);
+}
+
+static void
+batch_and_call (CoglJournalEntry *entries,
+                int n_entries,
+                CoglJournalBatchTest can_batch_callback,
+                CoglJournalBatchCallback batch_callback,
+                void *data)
+{
+  int i;
+  int batch_len = 1;
+  CoglJournalEntry *batch_start = entries;
+
+  for (i = 1; i < n_entries; i++)
     {
-      GE (glClientActiveTexture (GL_TEXTURE0 + i));
-      GE (glDisableClientState (GL_TEXTURE_COORD_ARRAY));
-    }
+      CoglJournalEntry *entry0 = &entries[i - 1];
+      CoglJournalEntry *entry1 = entry0 + 1;
 
-  /* FIXME: This api is a bit yukky, ideally it will be removed if we
-   * re-work the cogl_enable mechanism */
-  enable_flags |= _cogl_material_get_cogl_enable_flags (ctx->source_material);
+      if (can_batch_callback (entry0, entry1))
+        {
+          batch_len++;
+          continue;
+        }
 
-  if (ctx->enable_backface_culling)
-    enable_flags |= COGL_ENABLE_BACKFACE_CULLING;
+      batch_callback (batch_start, batch_len, data);
 
-  enable_flags |= COGL_ENABLE_VERTEX_ARRAY;
-  cogl_enable (enable_flags);
+      batch_start = entry1;
+      batch_len = 1;
+    }
 
-  GE (glVertexPointer (2, GL_FLOAT, stride, vertex_pointer));
-  _cogl_current_matrix_state_flush ();
+  /* The last batch... */
+  batch_callback (batch_start, batch_len, data);
+}
+
+static void
+_cogl_journal_flush_modelview_and_entries (CoglJournalEntry *batch_start,
+                                           int               batch_len,
+                                           void             *data)
+{
+  CoglJournalFlushState *state = data;
+
+  GE (glLoadMatrixf ((GLfloat *)&batch_start->model_view));
 
 #ifdef HAVE_COGL_GL
 
-  GE( glDrawArrays (GL_QUADS, 0, batch_len * 4) );
+  GE (glDrawArrays (GL_QUADS, state->vertex_offset, batch_len * 4));
 
 #else /* HAVE_COGL_GL */
 
-  /* GLES doesn't support GL_QUADS so we will use GL_TRIANGLES and
-     indices */
-  {
-    int needed_indices = batch_len * 6;
-    CoglHandle indices_handle
-      = cogl_vertex_buffer_indices_get_for_quads (needed_indices);
-    CoglVertexBufferIndices *indices
-      = _cogl_vertex_buffer_indices_pointer_from_handle (indices_handle);
-
-    GE (glBindBuffer (GL_ELEMENT_ARRAY_BUFFER,
-                      GPOINTER_TO_UINT (indices->vbo_name)));
-    GE (glDrawElements (GL_TRIANGLES,
-                        6 * batch_len,
-                        indices->type,
-                        NULL));
-    GE (glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0));
-  }
-
-#endif /* HAVE_COGL_GL */
+  if (batch_len > 1)
+    {
+      int indices_offset = (state->vertex_offset / 4) * 6;
+      GE (glDrawElements (GL_TRIANGLES,
+                          6 * batch_len,
+                          indices->type,
+                          indices_offset * state->indices_type_size));
+    }
+  else
+    {
+      GE (glDrawArrays (GL_TRIANGLE_FAN,
+                        state->vertex_offset, /* first */
+                        4)); /* n vertices */
+    }
+#endif
 
   /* DEBUGGING CODE XXX:
    * This path will cause all rectangles to be drawn with a red, green
@@ -149,6 +205,7 @@ _cogl_journal_flush_quad_batch (CoglJournalEntry *batch_start,
     {
       static CoglHandle outline = COGL_INVALID_HANDLE;
       static int color = 0;
+      int i;
       if (outline == COGL_INVALID_HANDLE)
         outline = cogl_material_new ();
 
@@ -161,97 +218,293 @@ _cogl_journal_flush_quad_batch (CoglJournalEntry *batch_start,
                                       color == 2 ? 0xff : 0x00,
                                       0xff);
           _cogl_material_flush_gl_state (outline, NULL);
-          _cogl_current_matrix_state_flush ();
           GE( glDrawArrays (GL_LINE_LOOP, 4 * i, 4) );
         }
     }
+
+  state->vertex_offset += (4 * batch_len);
+}
+
+static gboolean
+compare_entry_modelviews (CoglJournalEntry *entry0,
+                          CoglJournalEntry *entry1)
+{
+  /* Batch together quads with the same model view matrix */
+
+  /* FIXME: this is nasty, there are much nicer ways to track this
+   * (at the add_quad_vertices level) without resorting to a memcmp!
+   *
+   * E.g. If the cogl-current-matrix code maintained an "age" for
+   * the modelview matrix we could simply check in add_quad_vertices
+   * if the age has increased, and if so record the change as a
+   * boolean in the journal.
+   */
+  if (memcmp (&entry0->model_view, &entry1->model_view,
+              sizeof (GLfloat) * 16) == 0)
+    return TRUE;
+  else
+    return FALSE;
+}
+
+/* At this point we have a run of quads that we know have compatible
+ * materials, but they may not all have the same modelview matrix */
+static void
+_cogl_journal_flush_material_and_entries (CoglJournalEntry *batch_start,
+                                          gint              batch_len,
+                                          void             *data)
+{
+  gulong                 enable_flags = 0;
+
+  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+#if 0
+  if (batch_len != 1)
+    g_debug ("batch len = %d", batch_len);
+#endif
+
+  _cogl_material_flush_gl_state (batch_start->material,
+                                 &batch_start->flush_options);
+
+  /* FIXME: This api is a bit yukky, ideally it will be removed if we
+   * re-work the cogl_enable mechanism */
+  enable_flags |= _cogl_material_get_cogl_enable_flags (batch_start->material);
+
+  if (ctx->enable_backface_culling)
+    enable_flags |= COGL_ENABLE_BACKFACE_CULLING;
+
+  enable_flags |= COGL_ENABLE_VERTEX_ARRAY;
+  enable_flags |= COGL_ENABLE_COLOR_ARRAY;
+  cogl_enable (enable_flags);
+
+  batch_and_call (batch_start,
+                  batch_len,
+                  compare_entry_modelviews,
+                  _cogl_journal_flush_modelview_and_entries,
+                  data);
+
 }
 
+static gboolean
+compare_entry_materials (CoglJournalEntry *entry0, CoglJournalEntry *entry1)
+{
+  /* batch rectangles using compatible materials */
+
+  /* XXX: _cogl_material_equal may give false negatives since it avoids
+   * deep comparisons as an optimization. It aims to compare enough so
+   * that we that we are able to batch the 90% common cases, but may not
+   * look at less common differences. */
+  if (_cogl_material_equal (entry0->material,
+                            &entry0->flush_options,
+                            entry1->material,
+                            &entry1->flush_options,
+                            COGL_MATERIAL_EQUAL_FLAGS_ASSERT_ALL_DEFAULTS))
+    return TRUE;
+  else
+    return FALSE;
+}
+
+/* At this point we know the stride has changed from the previous batch
+ * of journal entries */
+static void
+_cogl_journal_flush_vbo_offsets_and_entries (CoglJournalEntry *batch_start,
+                                             gint              batch_len,
+                                             void             *data)
+{
+  CoglJournalFlushState   *state = data;
+  size_t                   stride;
+  int                      i;
+  int                      prev_n_texcoord_arrays_enabled;
+#ifndef HAVE_COGL_GL
+  int                      needed_indices = batch_len * 6;
+  CoglHandle               indices_handle;
+  CoglVertexBufferIndices *indices;
+#endif
+
+  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+  /* XXX NB:
+   * Our vertex data is arranged as follows:
+   * 4 vertices per quad: 2 GLfloats per position,
+   *                      4 RGBA GLubytes,
+   *                      2 GLfloats per tex coord * n_layers
+   */
+  stride = GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS (batch_start->n_layers);
+  stride *= sizeof (GLfloat);
+
+  GE (glVertexPointer (2, GL_FLOAT, stride, (void *)state->vbo_offset));
+  GE (glColorPointer (4, GL_UNSIGNED_BYTE, stride,
+                      (void *)(state->vbo_offset + 8)));
+
+  for (i = 0; i < batch_start->n_layers; i++)
+    {
+      GE (glClientActiveTexture (GL_TEXTURE0 + i));
+      GE (glEnableClientState (GL_TEXTURE_COORD_ARRAY));
+      GE (glTexCoordPointer (2, GL_FLOAT, stride,
+                             (void *)(state->vbo_offset + 12 + 8 * i)));
+    }
+  prev_n_texcoord_arrays_enabled =
+    ctx->n_texcoord_arrays_enabled;
+  ctx->n_texcoord_arrays_enabled = batch_start->n_layers;
+  for (; i < prev_n_texcoord_arrays_enabled; i++)
+    {
+      GE (glClientActiveTexture (GL_TEXTURE0 + i));
+      GE (glDisableClientState (GL_TEXTURE_COORD_ARRAY));
+    }
+
+#ifndef HAVE_COGL_GL
+  indices_handle = cogl_vertex_buffer_indices_get_for_quads (needed_indices);
+  indices = _cogl_vertex_buffer_indices_pointer_from_handle (indices_handle);
+  state->indices = indices;
+
+  if (indices->type == GL_UNSIGNED_BYTE)
+    state->indices_type_size = 1;
+  else if (indices->type == GL_UNSIGNED_SHORT)
+    state->indices_type_size = 2;
+  else
+    g_critical ("unknown indices type %d", indices->type);
+
+  GE (glBindBuffer (GL_ELEMENT_ARRAY_BUFFER,
+                    GPOINTER_TO_UINT (indices->vbo_name)));
+#endif
+
+  /* We only call gl{Vertex,Color,Texture}Pointer when the stride within
+   * the VBO changes. (due to a change in the number of material layers)
+   * While the stride remains constant we walk forward through the above
+   * VBO use a vertex offset passed to glDraw{Arrays,Elements} */
+  state->vertex_offset = 0;
+
+  /* XXX: Uncomment for debugging */
+#if 0
+  g_assert (cogl_get_features () & COGL_FEATURE_VBOS);
+  _cogl_journal_dump_quad_batch (((guint8 *)ctx->logged_vertices->data) +
+                                 (size_t)state->vbo_offset,
+                                 batch_start->n_layers,
+                                 batch_len);
+#endif
+
+  batch_and_call (batch_start,
+                  batch_len,
+                  compare_entry_materials,
+                  _cogl_journal_flush_material_and_entries,
+                  data);
+
+#ifndef HAVE_COGL_GL
+  GE (glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0));
+#endif
+
+  /* progress forward through the VBO containing all our vertices */
+  state->vbo_offset += (stride * 4 * batch_len);
+}
+
+static gboolean
+compare_entry_strides (CoglJournalEntry *entry0, CoglJournalEntry *entry1)
+{
+  /* Currently the only thing that affects the stride for our vertex arrays
+   * is the number of material layers. We need to update our VBO offsets
+   * whenever the stride changes. */
+  /* TODO: We should be padding the n_layers == 1 case as if it were
+   * n_layers == 2 so we can reduce the need to split batches. */
+  if (entry0->n_layers == entry1->n_layers)
+    return TRUE;
+  else
+    return FALSE;
+}
+
+static void
+upload_vertices_to_vbo (GArray *vertices, CoglJournalFlushState *state)
+{
+  size_t needed_vbo_len;
+
+  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+  needed_vbo_len = vertices->len * sizeof (GLfloat);
+  if (ctx->journal_vbo_len < needed_vbo_len)
+    {
+      GE (glDeleteBuffers (1, &ctx->journal_vbo));
+
+      GE (glGenBuffers (1, &ctx->journal_vbo));
+      GE (glBindBuffer (GL_ARRAY_BUFFER, ctx->journal_vbo));
+      GE (glBufferData (GL_ARRAY_BUFFER,
+                        needed_vbo_len,
+                        vertices->data,
+                        GL_STATIC_DRAW));
+      ctx->journal_vbo_len = needed_vbo_len;
+    }
+  else
+    {
+      GE (glBindBuffer (GL_ARRAY_BUFFER, ctx->journal_vbo));
+      GE (glBufferData (GL_ARRAY_BUFFER,
+                        needed_vbo_len,
+                        NULL,
+                        GL_STATIC_DRAW));
+      GE (glBufferSubData (GL_ARRAY_BUFFER,
+                           0,
+                           needed_vbo_len,
+                           vertices->data));
+    }
+
+  /* As we flush the journal entries in batches we walk forward through the
+   * above VBO starting at offset 0... */
+  state->vbo_offset = 0;
+}
+
+/* XXX NB: When _cogl_journal_flush() returns all state relating
+ * to materials, all glEnable flags and current matrix state
+ * is undefined.
+ */
 void
 _cogl_journal_flush (void)
 {
-  GLfloat          *current_vertex_pointer;
-  GLfloat          *batch_vertex_pointer;
-  CoglJournalEntry *batch_start;
-  guint             batch_len;
-  int               i;
+  CoglJournalFlushState state;
+  int                   i;
+  gboolean              vbo_fallback =
+    (cogl_get_features () & COGL_FEATURE_VBOS) ? FALSE : TRUE;
 
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
   if (ctx->journal->len == 0)
     return;
 
-  /* Current non-variables / constraints:
-   *
-   * - We don't have to worry about much GL state changing between journal
-   *   entries since currently the journal never out lasts a single call to
-   *   _cogl_multitexture_multiple_rectangles. So the user doesn't get the
-   *   chance to fiddle with anything. (XXX: later this will be extended at
-   *   which point we can start logging certain state changes)
-   *
-   * - Implied from above: all entries will refer to the same material.
-   *
-   * - Although _cogl_multitexture_multiple_rectangles can cause the wrap mode
-   *   of textures to be modified, the journal is flushed if a wrap mode is
-   *   changed so we don't currently have to log wrap mode changes.
-   *
-   * - XXX - others?
-   */
+  /* Load all the vertex data we have accumulated so far into a single VBO
+   * to minimize memory management costs within the GL driver. */
+  if (!vbo_fallback)
+    upload_vertices_to_vbo (ctx->logged_vertices, &state);
+  else
+    state.vbo_offset = (char *)ctx->logged_vertices->data;
 
-  /* TODO: "compile" the journal to find ways of batching draw calls and vertex
-   * data.
-   *
-   * Simple E.g. given current constraints...
-   * pass 0 - load all data into a single CoglVertexBuffer
-   * pass 1 - batch gl draw calls according to entries that use the same
-   *          textures.
+  /* Since the journal deals with emitting the modelview matrices manually
+   * we need to dirty our client side matrix stack cache... */
+  _cogl_current_matrix_state_dirty ();
+
+  /* batch_and_call() batches a list of journal entries according to some
+   * given criteria and calls a callback once for each determined batch.
    *
-   * We will be able to do cooler stuff here when we extend the life of
-   * journals beyond _cogl_multitexture_multiple_rectangles.
+   * The process of flushing the journal is done by splitting the entries
+   * by three broad criteria:
+   * 1) We split the entries according the number of material layers.
+   *      Each time the number of material layers changes, then the stride
+   *      changes, so we need to call gl{Vertex,Color,Texture}Pointer to
+   *      inform GL of new VO offsets.
+   * 2) We then split according to compatible Cogl materials.
+   *      This is where we flush material state
+   * 3) Finally we split according to modelview matrix changes.
+   *      This is when we finally tell GL to draw something.
    */
+  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 */
+                  &state); /* data */
 
-  batch_vertex_pointer = (GLfloat *)ctx->logged_vertices->data;
-  batch_start = (CoglJournalEntry *)ctx->journal->data;
-  batch_len = 1;
-
-  current_vertex_pointer = batch_vertex_pointer;
-
-  for (i = 1; i < ctx->journal->len; i++)
+  for (i = 0; i < ctx->journal->len; i++)
     {
-      CoglJournalEntry *prev_entry =
-        &g_array_index (ctx->journal, CoglJournalEntry, i - 1);
-      CoglJournalEntry *current_entry = prev_entry + 1;
-      gsize             stride;
-
-      /* Progress the vertex pointer to the next quad */
-      stride = 2 + current_entry->n_layers * 2;
-      current_vertex_pointer += stride * 4;
-
-      /* batch rectangles using the same textures */
-      if (current_entry->material == prev_entry->material &&
-          current_entry->n_layers == prev_entry->n_layers &&
-          current_entry->fallback_mask == prev_entry->fallback_mask &&
-          current_entry->layer0_override_texture
-          == prev_entry->layer0_override_texture)
-        {
-          batch_len++;
-          continue;
-        }
-
-      _cogl_journal_flush_quad_batch (batch_start,
-                                      batch_len,
-                                      batch_vertex_pointer);
-
-      batch_start = current_entry;
-      batch_len = 1;
-      batch_vertex_pointer = current_vertex_pointer;
+      CoglJournalEntry *entry =
+        &g_array_index (ctx->journal, CoglJournalEntry, i);
+      _cogl_material_journal_unref (entry->material);
     }
 
-  /* The last batch... */
-  _cogl_journal_flush_quad_batch (batch_start,
-                                  batch_len,
-                                  batch_vertex_pointer);
-
+  if (!vbo_fallback)
+    GE (glBindBuffer (GL_ARRAY_BUFFER, 0));
 
   g_array_set_size (ctx->journal, 0);
   g_array_set_size (ctx->logged_vertices, 0);
@@ -263,18 +516,23 @@ _cogl_journal_log_quad (float       x_1,
                         float       x_2,
                         float       y_2,
                         CoglHandle  material,
-                        gint        n_layers,
-                        guint32     fallback_mask,
+                        int         n_layers,
+                        guint32     fallback_layers,
                         GLuint      layer0_override_texture,
                         float      *tex_coords,
                         guint       tex_coords_len)
 {
-  int               stride;
+  size_t            stride;
+  size_t            byte_stride;
   int               next_vert;
   GLfloat          *v;
+  GLubyte          *c;
   int               i;
   int               next_entry;
+  guint32           disable_layers;
   CoglJournalEntry *entry;
+  CoglColor         color;
+  guint8            r, g, b, a;
 
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
@@ -282,15 +540,16 @@ _cogl_journal_log_quad (float       x_1,
    * directly passed to OpenGL
    */
 
-  /* We pack the vertex data as 2 (x,y) GLfloats folowed by 2 (tx,ty) GLfloats
-   * for each texture being used, E.g.:
-   *  [X, Y, TX0, TY0, TX1, TY1, X, Y, TX0, TY0, X, Y, ...]
-   */
-  stride = 2 + n_layers * 2;
+  /* XXX: See definition of GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS for details
+   * about how we pack our vertex data */
+  stride = GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS (n_layers);
+  /* NB: stride is in 32bit words */
+  byte_stride = stride * 4;
 
   next_vert = ctx->logged_vertices->len;
   g_array_set_size (ctx->logged_vertices, next_vert + 4 * stride);
   v = &g_array_index (ctx->logged_vertices, GLfloat, next_vert);
+  c = (GLubyte *)(v + 2);
 
   /* XXX: All the jumping around to fill in this strided buffer doesn't
    * seem ideal. */
@@ -298,18 +557,32 @@ _cogl_journal_log_quad (float       x_1,
   /* XXX: we could defer expanding the vertex data for GL until we come
    * to flushing the journal. */
 
+  cogl_material_get_color (material, &color);
+  r = cogl_color_get_red_byte (&color);
+  g = cogl_color_get_green_byte (&color);
+  b = cogl_color_get_blue_byte (&color);
+  a = cogl_color_get_alpha_byte (&color);
+
   v[0] = x_1; v[1] = y_1;
+  c[0] = r; c[1] = g; c[2] = b; c[3] = a;
   v += stride;
+  c += byte_stride;
   v[0] = x_1; v[1] = y_2;
+  c[0] = r; c[1] = g; c[2] = b; c[3] = a;
   v += stride;
+  c += byte_stride;
   v[0] = x_2; v[1] = y_2;
+  c[0] = r; c[1] = g; c[2] = b; c[3] = a;
   v += stride;
+  c += byte_stride;
   v[0] = x_2; v[1] = y_1;
+  c[0] = r; c[1] = g; c[2] = b; c[3] = a;
 
   for (i = 0; i < n_layers; i++)
     {
-      GLfloat *t =
-        &g_array_index (ctx->logged_vertices, GLfloat, next_vert + 2 + 2 * i);
+      /* NB: See note at top about vertex buffer layout: */
+      GLfloat *t = &g_array_index (ctx->logged_vertices,
+                                   GLfloat, next_vert + 3 + 2 * i);
 
       t[0] = tex_coords[0]; t[1] = tex_coords[1];
       t += stride;
@@ -320,14 +593,34 @@ _cogl_journal_log_quad (float       x_1,
       t[0] = tex_coords[2]; t[1] = tex_coords[1];
     }
 
+  /* XXX: Uncomment for debugging */
+#if 0
+  v = &g_array_index (ctx->logged_vertices, GLfloat, next_vert);
+  _cogl_journal_dump_quad_vertices ((guint8 *)v, n_layers);
+#endif
+
   next_entry = ctx->journal->len;
   g_array_set_size (ctx->journal, next_entry + 1);
   entry = &g_array_index (ctx->journal, CoglJournalEntry, next_entry);
 
-  entry->material = material;
+  disable_layers = (1 << n_layers) - 1;
+  disable_layers = ~disable_layers;
+
+  entry->material = _cogl_material_journal_ref (material);
   entry->n_layers = n_layers;
-  entry->fallback_mask = fallback_mask;
-  entry->layer0_override_texture = layer0_override_texture;
+  entry->flush_options.flags =
+    COGL_MATERIAL_FLUSH_FALLBACK_MASK |
+    COGL_MATERIAL_FLUSH_DISABLE_MASK |
+    COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE |
+    COGL_MATERIAL_FLUSH_SKIP_GL_COLOR;
+  entry->flush_options.fallback_layers = fallback_layers;
+  entry->flush_options.disable_layers = disable_layers;
+  entry->flush_options.layer0_override_texture = layer0_override_texture;
+  cogl_get_modelview_matrix (&entry->model_view);
+
+  if (cogl_debug_flags & COGL_DEBUG_DISABLE_BATCHING
+      || cogl_debug_flags & COGL_DEBUG_RECTANGLES)
+    _cogl_journal_flush ();
 }
 
 static void
@@ -511,12 +804,12 @@ _cogl_multitexture_unsliced_quad (float        x_1,
                                   float        x_2,
                                   float        y_2,
                                   CoglHandle   material,
-                                  gint         n_layers,
-                                  guint32      fallback_mask,
+                                  guint32      fallback_layers,
                                   const float *user_tex_coords,
                                   gint         user_tex_coords_len)
 {
-  float   *final_tex_coords = alloca (sizeof (float) * 4 * n_layers);
+  int          n_layers = cogl_material_get_n_layers (material);
+  float       *final_tex_coords = alloca (sizeof (float) * 4 * n_layers);
   const GList *layers;
   GList       *tmp;
   int          i;
@@ -601,7 +894,7 @@ _cogl_multitexture_unsliced_quad (float        x_1,
 
               /* NB: marking for fallback will replace the layer with
                * a default transparent texture */
-              fallback_mask |= (1 << i);
+              fallback_layers |= (1 << i);
             }
         }
 
@@ -672,7 +965,7 @@ _cogl_multitexture_unsliced_quad (float        x_1,
                           y_2,
                           material,
                           n_layers,
-                          fallback_mask,
+                          fallback_layers,
                           0, /* don't replace the layer0 texture */
                           final_tex_coords,
                           n_layers * 4);
@@ -699,7 +992,7 @@ _cogl_rectangles_with_multitexture_coords (
   const GList  *layers;
   int           n_layers;
   const GList  *tmp;
-  guint32        fallback_mask = 0;
+  guint32        fallback_layers = 0;
   gboolean      all_use_sliced_quad_fallback = FALSE;
   int           i;
 
@@ -710,7 +1003,7 @@ _cogl_rectangles_with_multitexture_coords (
   material = ctx->source_material;
 
   layers = cogl_material_get_layers (material);
-  n_layers = g_list_length ((GList *)layers);
+  n_layers = cogl_material_get_n_layers (material);
 
   /*
    * Validate all the layers of the current source material...
@@ -740,7 +1033,7 @@ _cogl_rectangles_with_multitexture_coords (
        {
          if (i == 0)
            {
-              fallback_mask = ~1; /* fallback all except the first layer */
+              fallback_layers = ~1; /* fallback all except the first layer */
              all_use_sliced_quad_fallback = TRUE;
               if (tmp->next)
                 {
@@ -766,7 +1059,7 @@ _cogl_rectangles_with_multitexture_coords (
 
               /* NB: marking for fallback will replace the layer with
                * a default transparent texture */
-              fallback_mask |= (1 << i);
+              fallback_layers |= (1 << i);
              continue;
             }
        }
@@ -788,7 +1081,7 @@ _cogl_rectangles_with_multitexture_coords (
 
           /* NB: marking for fallback will replace the layer with
            * a default transparent texture */
-          fallback_mask |= (1 << i);
+          fallback_layers |= (1 << i);
           continue;
         }
     }
@@ -803,8 +1096,7 @@ _cogl_rectangles_with_multitexture_coords (
           || !_cogl_multitexture_unsliced_quad (rects[i].x_1, rects[i].y_1,
                                                 rects[i].x_2, rects[i].y_2,
                                                 material,
-                                                n_layers,
-                                                fallback_mask,
+                                                fallback_layers,
                                                 rects[i].tex_coords,
                                                 rects[i].tex_coords_len))
         {
@@ -832,7 +1124,11 @@ _cogl_rectangles_with_multitexture_coords (
         }
     }
 
+#if 0
+  /* XXX: The current journal doesn't handle changes to the model view matrix
+   * so for now we force a flush at the end of every primitive. */
   _cogl_journal_flush ();
+#endif
 }
 
 void
@@ -947,6 +1243,7 @@ _cogl_texture_sliced_polygon (CoglTextureVertex *vertices,
   int                  x, y, tex_num, i;
   GLuint               gl_handle;
   GLfloat             *v;
+  CoglMaterialFlushOptions options;
 
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
@@ -1027,13 +1324,14 @@ _cogl_texture_sliced_polygon (CoglTextureVertex *vertices,
               v += stride;
            }
 
-          _cogl_material_flush_gl_state (ctx->source_material,
-                                         COGL_MATERIAL_FLUSH_DISABLE_MASK,
-                                         (guint32)~1, /* disable all except the
-                                                        first layer */
-                                         COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE,
-                                         gl_handle,
-                                         NULL);
+          options.flags =
+            COGL_MATERIAL_FLUSH_DISABLE_MASK |
+            COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE;
+          /* disable all except the first layer */
+          options.disable_layers = (guint32)~1;
+          options.layer0_override_texture = gl_handle;
+
+          _cogl_material_flush_gl_state (ctx->source_material, &options);
           _cogl_current_matrix_state_flush ();
 
          GE( glDrawArrays (GL_TRIANGLE_FAN, 0, n_vertices) );
@@ -1047,7 +1345,7 @@ _cogl_multitexture_unsliced_polygon (CoglTextureVertex *vertices,
                                      guint              n_layers,
                                      guint              stride,
                                      gboolean           use_color,
-                                     guint32            fallback_mask)
+                                     guint32            fallback_layers)
 {
   CoglHandle           material;
   const GList         *layers;
@@ -1055,6 +1353,7 @@ _cogl_multitexture_unsliced_polygon (CoglTextureVertex *vertices,
   GList               *tmp;
   CoglTexSliceSpan    *y_span, *x_span;
   GLfloat             *v;
+  CoglMaterialFlushOptions options;
 
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
@@ -1125,10 +1424,11 @@ _cogl_multitexture_unsliced_polygon (CoglTextureVertex *vertices,
         }
     }
 
-  _cogl_material_flush_gl_state (ctx->source_material,
-                                 COGL_MATERIAL_FLUSH_FALLBACK_MASK,
-                                 fallback_mask,
-                                 NULL);
+  options.flags = COGL_MATERIAL_FLUSH_FALLBACK_MASK;
+  if (use_color)
+    options.flags |= COGL_MATERIAL_FLUSH_SKIP_GL_COLOR;
+  options.fallback_layers = fallback_layers;
+  _cogl_material_flush_gl_state (ctx->source_material, &options);
   _cogl_current_matrix_state_flush ();
 
   GE (glDrawArrays (GL_TRIANGLE_FAN, 0, n_vertices));
@@ -1144,7 +1444,7 @@ cogl_polygon (CoglTextureVertex *vertices,
   int                  n_layers;
   GList               *tmp;
   gboolean            use_sliced_polygon_fallback = FALSE;
-  guint32              fallback_mask = 0;
+  guint32              fallback_layers = 0;
   int                  i;
   gulong               enable_flags;
   guint                stride;
@@ -1154,6 +1454,7 @@ cogl_polygon (CoglTextureVertex *vertices,
 
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
+  _cogl_journal_flush ();
   cogl_clip_ensure ();
 
   material = ctx->source_material;
@@ -1228,7 +1529,7 @@ cogl_polygon (CoglTextureVertex *vertices,
                        "textures with waste\n", i);
           warning_seen = TRUE;
 
-          fallback_mask |= (1 << i);
+          fallback_layers |= (1 << i);
           continue;
         }
     }
@@ -1293,7 +1594,7 @@ cogl_polygon (CoglTextureVertex *vertices,
                                          n_layers,
                                          stride,
                                          use_color,
-                                         fallback_mask);
+                                         fallback_layers);
 
   /* Reset the size of the logged vertex array because rendering
      rectangles expects it to start at 0 */
@@ -1313,6 +1614,7 @@ cogl_path_fill_preserve (void)
 {
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
+  _cogl_journal_flush ();
   cogl_clip_ensure ();
 
   if (ctx->path_nodes->len == 0)
@@ -1334,11 +1636,12 @@ cogl_path_stroke_preserve (void)
 {
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
-  cogl_clip_ensure ();
-
   if (ctx->path_nodes->len == 0)
     return;
 
+  _cogl_journal_flush ();
+  cogl_clip_ensure ();
+
   _cogl_path_stroke_nodes();
 }
 
index 268fba3..dd5a73c 100644 (file)
@@ -57,4 +57,6 @@ struct _CoglBezCubic
   floatVec2 p4;
 };
 
+void _cogl_journal_flush (void);
+
 #endif /* __COGL_PRIMITIVES_H */
index a188520..da99aee 100644 (file)
 #include "cogl-vertex-buffer-private.h"
 #include "cogl-texture-private.h"
 #include "cogl-material-private.h"
+#include "cogl-primitives.h"
 
 #define PAD_FOR_ALIGNMENT(VAR, TYPE_SIZE) \
   (VAR = TYPE_SIZE + ((VAR - 1) & ~(TYPE_SIZE - 1)))
@@ -1500,9 +1501,10 @@ enable_state_for_drawing_buffer (CoglVertexBuffer *buffer)
   gulong       enable_flags = 0;
   guint        max_texcoord_attrib_unit = 0;
   const GList *layers;
-  guint32      fallback_mask = 0;
-  guint32      disable_mask = ~0;
+  guint32      fallback_layers = 0;
+  guint32      disable_layers = ~0;
   int          i;
+  CoglMaterialFlushOptions options;
 
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
@@ -1573,7 +1575,7 @@ enable_state_for_drawing_buffer (CoglVertexBuffer *buffer)
                                     pointer));
               if (attribute->texture_unit > max_texcoord_attrib_unit)
                 max_texcoord_attrib_unit = attribute->texture_unit;
-              disable_mask &= ~(1 << attribute->texture_unit);
+              disable_layers &= ~(1 << attribute->texture_unit);
              break;
            case COGL_VERTEX_BUFFER_ATTRIB_FLAG_VERTEX_ARRAY:
              enable_flags |= COGL_ENABLE_VERTEX_ARRAY;
@@ -1638,17 +1640,17 @@ enable_state_for_drawing_buffer (CoglVertexBuffer *buffer)
            * vertices once for each layer, each time with a fiddled texture
            * matrix.
            */
-          fallback_mask |= (1 << i);
+          fallback_layers |= (1 << i);
         }
     }
 
-  _cogl_material_flush_gl_state (ctx->source_material,
-                                 COGL_MATERIAL_FLUSH_FALLBACK_MASK,
-                                 fallback_mask,
-                                 COGL_MATERIAL_FLUSH_DISABLE_MASK,
-                                 disable_mask,
-                                 NULL);
+  options.flags =
+    COGL_MATERIAL_FLUSH_FALLBACK_MASK |
+    COGL_MATERIAL_FLUSH_DISABLE_MASK;
+  options.fallback_layers = fallback_layers;
+  options.disable_layers = disable_layers;
 
+  _cogl_material_flush_gl_state (ctx->source_material, &options);
   enable_flags |= _cogl_material_get_cogl_enable_flags (ctx->source_material);
 
   if (ctx->enable_backface_culling)
@@ -1730,6 +1732,9 @@ cogl_vertex_buffer_draw (CoglHandle       handle,
   if (!cogl_is_vertex_buffer (handle))
     return;
 
+  _cogl_journal_flush ();
+  cogl_clip_ensure ();
+
   buffer = _cogl_vertex_buffer_pointer_from_handle (handle);
 
   cogl_clip_ensure ();
@@ -1859,6 +1864,9 @@ cogl_vertex_buffer_draw_elements (CoglHandle       handle,
   if (!cogl_is_vertex_buffer (handle))
     return;
 
+  _cogl_journal_flush ();
+  cogl_clip_ensure ();
+
   buffer = _cogl_vertex_buffer_pointer_from_handle (handle);
 
   if (!cogl_is_vertex_buffer_indices (indices_handle))
index d06d8dd..944ff8d 100644 (file)
@@ -216,6 +216,9 @@ cogl_get_enable ()
 void
 cogl_set_depth_test_enabled (gboolean setting)
 {
+  /* Currently the journal can't track changes to depth state... */
+  _cogl_journal_flush ();
+
   if (setting)
     {
       glEnable (GL_DEPTH_TEST);
@@ -236,6 +239,9 @@ cogl_set_backface_culling_enabled (gboolean setting)
 {
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
+  /* Currently the journal can't track changes to backface culling state... */
+  _cogl_journal_flush ();
+
   ctx->enable_backface_culling = setting;
 }
 
@@ -388,9 +394,15 @@ _cogl_add_stencil_clip (float x_offset,
                        float height,
                        gboolean first)
 {
+  CoglHandle current_source;
+
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
-  _cogl_material_flush_gl_state (ctx->stencil_material, NULL);
+  _cogl_journal_flush ();
+
+  /* temporarily swap in our special stenciling material */
+  current_source = cogl_handle_ref (ctx->source_material);
+  cogl_set_source (ctx->stencil_material);
 
   if (first)
     {
@@ -443,9 +455,17 @@ _cogl_add_stencil_clip (float x_offset,
       _cogl_set_current_matrix (COGL_MATRIX_MODELVIEW);
     }
 
+  /* make sure our rectangles hit the stencil buffer before we restore
+   * the stencil function / operation */
+  _cogl_journal_flush ();
+
   /* Restore the stencil mode */
   GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) );
   GE( glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) );
+
+  /* restore the original source material */
+  cogl_set_source (current_source);
+  cogl_handle_unref (current_source);
 }
 
 void
@@ -630,6 +650,9 @@ cogl_set_fog (const CoglColor *fog_color,
   GLfloat fogColor[4];
   GLenum gl_mode = GL_LINEAR;
 
+  /* The cogl journal doesn't currently track fog state changes */
+  _cogl_journal_flush ();
+
   fogColor[0] = cogl_color_get_red_float (fog_color);
   fogColor[1] = cogl_color_get_green_float (fog_color);
   fogColor[2] = cogl_color_get_blue_float (fog_color);
@@ -667,6 +690,9 @@ cogl_set_fog (const CoglColor *fog_color,
 void
 cogl_disable_fog (void)
 {
+  /* Currently the journal can't track changes to fog state... */
+  _cogl_journal_flush ();
+
   glDisable (GL_FOG);
 }
 
@@ -679,6 +705,12 @@ cogl_flush_gl_state (int flags)
 #endif
 
 void
+_cogl_flush (void)
+{
+  _cogl_journal_flush ();
+}
+
+void
 cogl_read_pixels (int x,
                   int y,
                   int width,
@@ -711,6 +743,10 @@ cogl_read_pixels (int x,
   glPixelStorei (GL_PACK_SKIP_ROWS, 0);
 #endif /* HAVE_COGL_GL */
 
+  /* make sure any batched primitives get emitted to the GL driver before
+   * issuing our read pixels... */
+  _cogl_flush ();
+
   glReadPixels (x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
 
   /* TODO: consider using the GL_MESA_pack_invert extension in the future
index 1b6fab8..2266d95 100644 (file)
@@ -69,9 +69,13 @@ cogl_create_context ()
 
   _context->journal = g_array_new (FALSE, FALSE, sizeof (CoglJournalEntry));
   _context->logged_vertices = g_array_new (FALSE, FALSE, sizeof (GLfloat));
+  _context->journal_vbo = 0;
+  _context->journal_vbo_len = 0;
 
   _context->current_material = NULL;
   _context->current_material_flags = 0;
+  memset (&_context->current_material_flush_options,
+          0, sizeof (CoglMaterialFlushOptions));
   _context->current_layers = g_array_new (FALSE, FALSE,
                                           sizeof (CoglLayerInfo));
   _context->n_texcoord_arrays_enabled = 0;
index 9e1596e..fa1ccb6 100644 (file)
@@ -28,6 +28,7 @@
 #include "cogl-clip-stack.h"
 #include "cogl-matrix-stack.h"
 #include "cogl-current-matrix.h"
+#include "cogl-material-private.h"
 
 typedef struct
 {
@@ -78,10 +79,13 @@ typedef struct
    * can batch things together. */
   GArray           *journal;
   GArray           *logged_vertices;
+  GLuint            journal_vbo;
+  size_t            journal_vbo_len;
 
   /* Some simple caching, to minimize state changes... */
   CoglHandle       current_material;
   gulong            current_material_flags;
+  CoglMaterialFlushOptions current_material_flush_options;
   GArray           *current_layers;
   guint             n_texcoord_arrays_enabled;
 
index db99e6a..f133409 100644 (file)
@@ -169,6 +169,8 @@ cogl_set_draw_buffer (CoglBufferTarget target, CoglHandle offscreen)
 
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
+  _cogl_journal_flush ();
+
   g_assert (ctx->draw_buffer_stack != NULL);
   draw_buffer = ctx->draw_buffer_stack->data;
 
index 6c106f9..30b5553 100644 (file)
@@ -76,16 +76,18 @@ _cogl_path_stroke_nodes ()
 {
   guint   path_start = 0;
   gulong  enable_flags = COGL_ENABLE_VERTEX_ARRAY;
+  CoglMaterialFlushOptions options;
 
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
   enable_flags |= _cogl_material_get_cogl_enable_flags (ctx->source_material);
   cogl_enable (enable_flags);
 
-  _cogl_material_flush_gl_state (ctx->source_material,
-                                 COGL_MATERIAL_FLUSH_DISABLE_MASK,
-                                 (guint32)~0, /* disable all texture layers */
-                                 NULL);
+  options.flags = COGL_MATERIAL_FLUSH_DISABLE_MASK;
+  /* disable all texture layers */
+  options.disable_layers = (guint32)~0;
+
+  _cogl_material_flush_gl_state (ctx->source_material, &options);
   _cogl_current_matrix_state_flush ();
 
   while (path_start < ctx->path_nodes->len)
@@ -123,18 +125,24 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min,
                                   CoglPathNode *path,
                                   gboolean      merge)
 {
-  guint   path_start = 0;
-  guint   sub_path_num = 0;
-  float   bounds_x;
-  float   bounds_y;
-  float   bounds_w;
-  float   bounds_h;
-  gulong  enable_flags = COGL_ENABLE_VERTEX_ARRAY;
+  guint       path_start = 0;
+  guint       sub_path_num = 0;
+  float       bounds_x;
+  float       bounds_y;
+  float       bounds_w;
+  float       bounds_h;
+  gulong      enable_flags = COGL_ENABLE_VERTEX_ARRAY;
+  CoglHandle  prev_source;
 
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
+  _cogl_journal_flush ();
+
   /* Just setup a simple material that doesn't use texturing... */
-  _cogl_material_flush_gl_state (ctx->stencil_material, NULL);
+  prev_source = cogl_handle_ref (ctx->source_material);
+  cogl_set_source (ctx->stencil_material);
+
+  _cogl_material_flush_gl_state (ctx->source_material, NULL);
 
   enable_flags |=
     _cogl_material_get_cogl_enable_flags (ctx->source_material);
@@ -161,9 +169,12 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min,
   GE( glColorMask (FALSE, FALSE, FALSE, FALSE) );
   GE( glDepthMask (FALSE) );
 
-  _cogl_current_matrix_state_flush ();
   while (path_start < path_size)
     {
+      /* NB: after calling _cogl_journal_flush the current matrix
+       * state is undefined */
+      _cogl_current_matrix_state_flush ();
+
       GE( glVertexPointer (2, GL_FLOAT, sizeof (CoglPathNode),
                            (guchar *) path
                            + G_STRUCT_OFFSET (CoglPathNode, x)) );
@@ -177,7 +188,7 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min,
           GE( glStencilOp (GL_ZERO, GL_REPLACE, GL_REPLACE) );
           cogl_rectangle (bounds_x, bounds_y,
                           bounds_x + bounds_w, bounds_y + bounds_h);
-
+          _cogl_journal_flush ();
           GE( glStencilOp (GL_INVERT, GL_INVERT, GL_INVERT) );
         }
 
@@ -212,6 +223,7 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min,
 
       cogl_rectangle (-1.0, -1.0, 1.0, 1.0);
       cogl_rectangle (-1.0, -1.0, 1.0, 1.0);
+      _cogl_journal_flush ();
 
       _cogl_current_matrix_pop ();
 
@@ -227,6 +239,10 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min,
 
   GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) );
   GE( glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) );
+
+  /* restore the original material */
+  cogl_set_source (prev_source);
+  cogl_handle_unref (prev_source);
 }
 
 void
index 0a10cd4..75cf8e2 100644 (file)
@@ -128,6 +128,11 @@ cogl_program_use (CoglHandle handle)
   if (handle != COGL_INVALID_HANDLE && !cogl_is_program (handle))
     return;
 
+  /* The Cogl journal doesn't currently cope with the use of
+   * shaders so we have to flush all priitives whenever the
+   * current shader changes... */
+  _cogl_journal_flush ();
+
   if (handle == COGL_INVALID_HANDLE)
     gl_handle = 0;
   else
index 365a479..6a2ed7e 100644 (file)
@@ -26,6 +26,7 @@
 
 #include "cogl-bitmap-private.h"
 #include "cogl-handle.h"
+#include "cogl-material-private.h"
 
 typedef struct _CoglTexture       CoglTexture;
 typedef struct _CoglTexSliceSpan  CoglTexSliceSpan;
@@ -99,10 +100,13 @@ struct _CoglTexture
  * later flush the journal we aim to batch data, and gl draw calls. */
 typedef struct _CoglJournalEntry
 {
-  CoglHandle     material;
-  gint           n_layers;
-  guint32        fallback_mask;
-  GLuint         layer0_override_texture;
+  CoglHandle               material;
+  int                      n_layers;
+  CoglMaterialFlushOptions flush_options;
+  CoglMatrix               model_view;
+  /* XXX: These entries are pretty big now considering the padding in
+   * CoglMaterialFlushOptions and CoglMatrix, so we might need to optimize this
+   * later. */
 } CoglJournalEntry;
 
 CoglTexture*
index fb118c7..7721ea2 100644 (file)
@@ -39,6 +39,7 @@
 #include "cogl-material.h"
 #include "cogl-context.h"
 #include "cogl-handle.h"
+#include "cogl-primitives.h"
 
 #include <string.h>
 #include <stdlib.h>
@@ -60,8 +61,6 @@
 
 #endif
 
-extern void _cogl_journal_flush (void);
-
 static void _cogl_texture_free (CoglTexture *tex);
 
 COGL_HANDLE_DEFINE (Texture, texture);
index 8444453..adaf3cb 100644 (file)
@@ -81,6 +81,7 @@ clutter_backend_egl_redraw (ClutterBackend *backend,
 
   eglWaitNative (EGL_CORE_NATIVE_ENGINE);
   clutter_actor_paint (CLUTTER_ACTOR (stage));
+  _cogl_flush ();
 
   eglWaitGL();
   eglSwapBuffers (backend_egl->edpy,  stage_egl->egl_surface);
index d3c36d0..af06f97 100644 (file)
@@ -139,6 +139,7 @@ clutter_backend_egl_redraw (ClutterBackend *backend,
 
   /* this will cause the stage implementation to be painted as well */
   clutter_actor_paint (CLUTTER_ACTOR (stage));
+  _cogl_flush ();
 
   /* Why this paint is done in backend as likely GL windowing system
    * specific calls, like swapping buffers.
index c90809d..58c5e39 100644 (file)
@@ -72,6 +72,7 @@ clutter_backend_egl_redraw (ClutterBackend *backend,
 
   eglWaitNative (EGL_CORE_NATIVE_ENGINE);
   clutter_actor_paint (CLUTTER_ACTOR (stage));
+  _cogl_flush ();
   eglWaitGL();
   eglSwapBuffers (backend_egl->edpy,  stage_egl->egl_surface);
 }
index d9fef91..140038f 100644 (file)
@@ -498,6 +498,7 @@ clutter_backend_glx_redraw (ClutterBackend *backend,
 
   /* this will cause the stage implementation to be painted */
   clutter_actor_paint (CLUTTER_ACTOR (stage));
+  _cogl_flush ();
 
   if (stage_x11->xwin != None)
     {
index 00bbcb4..962d4a7 100644 (file)
@@ -128,6 +128,7 @@ clutter_stage_osx_state_update (ClutterStageOSX   *self,
 - (void) drawRect: (NSRect) bounds
 {
   clutter_actor_paint (CLUTTER_ACTOR (self->stage_osx->wrapper));
+  _cogl_flush ();
   [[self openGLContext] flushBuffer];
 }
 
index 70a1bc9..90c1167 100644 (file)
@@ -68,7 +68,8 @@ clutter_backend_sdl_redraw (ClutterBackend *backend,
                             ClutterStage   *stage)
 {
   clutter_actor_paint (CLUTTER_ACTOR (stage));
-  
+  _cogl_flush ();
+
   SDL_GL_SwapBuffers();
 }
 
index 6cc9155..f1531a0 100644 (file)
@@ -297,6 +297,7 @@ clutter_backend_win32_redraw (ClutterBackend *backend,
 
   /* this will cause the stage implementation to be painted */
   clutter_actor_paint (CLUTTER_ACTOR (stage));
+  _cogl_flush ();
 
   if (stage_win32->client_dc)
     SwapBuffers (stage_win32->client_dc);