[Cogl] [PATCH 1/3] pipeline: improve real_blend_enable checks

Robert Bragg robert at sixbynine.org
Thu May 30 07:40:33 PDT 2013


From: Robert Bragg <robert at linux.intel.com>

Since _cogl_pipeline_update_blend_enable() can sometimes show up quite
high in profiles; instead of calling
_cogl_pipeline_update_blend_enable() whenever we change pipeline state
that may affect blending we now just set a dirty flag and when we flush
a pipeline we check this dirty flag and lazily calculate whether blender
really needs to be enabled if it's set.

Since it turns out we were too optimistic in assuming most GL drivers
would recognize blending with ADD(src,0) is equivalent to disabling
GL_BLEND we now check this case ourselves so we can always explicitly
disable GL_BLEND if we know we don't need blending.

This introduces the idea of an 'unknown_color_alpha' boolean to the
pipeline flush code which is set whenever we can't guarantee that the
color attribute is opaque. For example this is set whenever a user
specifies a color attribute with 4 components when drawing a primitive.
This boolean needs to be cached along with every pipeline because
pipeline::real_blend_enabled depends on this and so we need to also call
_cogl_pipeline_update_blend_enable() if the status of this changes.

Incidentally with this patch we now no longer ever use
_cogl_pipeline_set_blend_enable() internally. For now the internal api
hasn't been removed though since we might want to consider re-purposing
it as a public api since it will now not conflict with our own internal
state tracking and could provide a more convenient way to disable
blending than setting a blend string.
---
 cogl/cogl-attribute-private.h                 |   3 +
 cogl/cogl-attribute.c                         |   9 +
 cogl/cogl-context-private.h                   |   3 +-
 cogl/cogl-context.c                           |   2 +-
 cogl/cogl-pipeline-layer-state.c              |   8 +-
 cogl/cogl-pipeline-private.h                  |  22 ++-
 cogl/cogl-pipeline-state.c                    |   8 +-
 cogl/cogl-pipeline.c                          | 250 ++++++++++++++++----------
 cogl/driver/gl/cogl-attribute-gl.c            |  28 ++-
 cogl/driver/gl/cogl-clip-stack-gl.c           |   3 +-
 cogl/driver/gl/cogl-pipeline-opengl-private.h |   6 +-
 cogl/driver/gl/cogl-pipeline-opengl.c         |  92 +++++++---
 12 files changed, 282 insertions(+), 152 deletions(-)

diff --git a/cogl/cogl-attribute-private.h b/cogl/cogl-attribute-private.h
index 943934f..cf4f636 100644
--- a/cogl/cogl-attribute-private.h
+++ b/cogl/cogl-attribute-private.h
@@ -125,5 +125,8 @@ _cogl_flush_attributes_state (CoglFramebuffer *framebuffer,
                               CoglAttribute **attributes,
                               int n_attributes);
 
+int
+_cogl_attribute_get_n_components (CoglAttribute *attribute);
+
 #endif /* __COGL_ATTRIBUTE_PRIVATE_H */
 
diff --git a/cogl/cogl-attribute.c b/cogl/cogl-attribute.c
index 60ce52c..6808547 100644
--- a/cogl/cogl-attribute.c
+++ b/cogl/cogl-attribute.c
@@ -644,3 +644,12 @@ _cogl_flush_attributes_state (CoglFramebuffer *framebuffer,
                                               attributes,
                                               n_attributes);
 }
+
+int
+_cogl_attribute_get_n_components (CoglAttribute *attribute)
+{
+  if (attribute->is_buffered)
+    return attribute->d.buffered.n_components;
+  else
+    return attribute->d.constant.boxed.size;
+}
diff --git a/cogl/cogl-context-private.h b/cogl/cogl-context-private.h
index 5eeea80..7978232 100644
--- a/cogl/cogl-context-private.h
+++ b/cogl/cogl-context-private.h
@@ -153,7 +153,8 @@ struct _CoglContext
   /* Some simple caching, to minimize state changes... */
   CoglPipeline     *current_pipeline;
   unsigned long     current_pipeline_changes_since_flush;
-  CoglBool          current_pipeline_skip_gl_color;
+  CoglBool          current_pipeline_with_color_attrib;
+  CoglBool          current_pipeline_unknown_color_alpha;
   unsigned long     current_pipeline_age;
 
   CoglBool          gl_blend_enable_cache;
diff --git a/cogl/cogl-context.c b/cogl/cogl-context.c
index e3fae59..13b122f 100644
--- a/cogl/cogl-context.c
+++ b/cogl/cogl-context.c
@@ -298,7 +298,7 @@ cogl_context_new (CoglDisplay *display,
 
   context->current_pipeline = NULL;
   context->current_pipeline_changes_since_flush = 0;
-  context->current_pipeline_skip_gl_color = FALSE;
+  context->current_pipeline_with_color_attrib = FALSE;
 
   _cogl_bitmask_init (&context->enabled_builtin_attributes);
   _cogl_bitmask_init (&context->enable_builtin_attributes_tmp);
diff --git a/cogl/cogl-pipeline-layer-state.c b/cogl/cogl-pipeline-layer-state.c
index e0dd294..58886e5 100644
--- a/cogl/cogl-pipeline-layer-state.c
+++ b/cogl/cogl-pipeline-layer-state.c
@@ -210,7 +210,7 @@ _cogl_pipeline_set_layer_texture_type (CoglPipeline *pipeline,
 
 changed:
 
-  _cogl_pipeline_update_blend_enable (pipeline, COGL_PIPELINE_STATE_LAYERS);
+  pipeline->dirty_real_blend_enable = TRUE;
 }
 
 static void
@@ -289,7 +289,7 @@ _cogl_pipeline_set_layer_texture_data (CoglPipeline *pipeline,
 
 changed:
 
-  _cogl_pipeline_update_blend_enable (pipeline, COGL_PIPELINE_STATE_LAYERS);
+  pipeline->dirty_real_blend_enable = TRUE;
 }
 
 void
@@ -1254,7 +1254,7 @@ cogl_pipeline_set_layer_combine (CoglPipeline *pipeline,
 
 changed:
 
-  _cogl_pipeline_update_blend_enable (pipeline, COGL_PIPELINE_STATE_LAYERS);
+  pipeline->dirty_real_blend_enable = TRUE;
   return TRUE;
 }
 
@@ -1339,7 +1339,7 @@ cogl_pipeline_set_layer_combine_constant (CoglPipeline *pipeline,
 
 changed:
 
-  _cogl_pipeline_update_blend_enable (pipeline, COGL_PIPELINE_STATE_LAYERS);
+  pipeline->dirty_real_blend_enable = TRUE;
 }
 
 void
diff --git a/cogl/cogl-pipeline-private.h b/cogl/cogl-pipeline-private.h
index 67fe5e4..f17e2f3 100644
--- a/cogl/cogl-pipeline-private.h
+++ b/cogl/cogl-pipeline-private.h
@@ -417,6 +417,19 @@ struct _CoglPipeline
    * blending, this holds our final decision */
   unsigned int          real_blend_enable:1;
 
+  /* Since the code for deciding if blending really needs to be
+   * enabled for a particular pipeline is quite expensive we update
+   * the real_blend_enable flag lazily when flushing a pipeline if
+   * this dirty flag has been set. */
+  unsigned int          dirty_real_blend_enable:1;
+
+  /* Whenever a pipeline is flushed we keep track of whether the
+   * pipeline was used with a color attribute where we don't know
+   * whether the colors are opaque. The real_blend_enable state
+   * depends on this, and must be updated whenever this changes (even
+   * if dirty_real_blend_enable isn't set) */
+  unsigned int          unknown_color_alpha:1;
+
   unsigned int          layers_cache_dirty:1;
 
   /* For debugging purposes it's possible to associate a static const
@@ -543,8 +556,9 @@ _cogl_pipeline_pre_change_notify (CoglPipeline     *pipeline,
 void
 _cogl_pipeline_prune_redundant_ancestry (CoglPipeline *pipeline);
 
-void _cogl_pipeline_update_blend_enable (CoglPipeline *pipeline,
-                                         CoglPipelineState changes);
+void
+_cogl_pipeline_update_real_blend_enable (CoglPipeline *pipeline,
+                                         CoglBool unknown_color_alpha);
 
 typedef enum
 {
@@ -777,13 +791,13 @@ _cogl_pipeline_compare_differences (CoglPipeline *pipeline0,
 CoglBool
 _cogl_pipeline_equal (CoglPipeline *pipeline0,
                       CoglPipeline *pipeline1,
-                      unsigned long differences,
+                      unsigned int differences,
                       unsigned long layer_differences,
                       CoglPipelineEvalFlags flags);
 
 unsigned int
 _cogl_pipeline_hash (CoglPipeline *pipeline,
-                     unsigned long differences,
+                     unsigned int differences,
                      unsigned long layer_differences,
                      CoglPipelineEvalFlags flags);
 
diff --git a/cogl/cogl-pipeline-state.c b/cogl/cogl-pipeline-state.c
index 8a652e0..067c01f 100644
--- a/cogl/cogl-pipeline-state.c
+++ b/cogl/cogl-pipeline-state.c
@@ -337,7 +337,7 @@ cogl_pipeline_set_color (CoglPipeline    *pipeline,
   _cogl_pipeline_update_authority (pipeline, authority, state,
                                    _cogl_pipeline_color_equal);
 
-  _cogl_pipeline_update_blend_enable (pipeline, state);
+  pipeline->dirty_real_blend_enable = TRUE;
 }
 
 void
@@ -411,7 +411,7 @@ _cogl_pipeline_set_blend_enabled (CoglPipeline *pipeline,
   _cogl_pipeline_update_authority (pipeline, authority, state,
                                    _cogl_pipeline_blend_enable_equal);
 
-  _cogl_pipeline_update_blend_enable (pipeline, state);
+  pipeline->dirty_real_blend_enable = TRUE;
 }
 
 static void
@@ -679,7 +679,7 @@ cogl_pipeline_set_blend (CoglPipeline *pipeline,
       _cogl_pipeline_prune_redundant_ancestry (pipeline);
     }
 
-  _cogl_pipeline_update_blend_enable (pipeline, state);
+  pipeline->dirty_real_blend_enable = TRUE;
 
   return TRUE;
 }
@@ -720,7 +720,7 @@ cogl_pipeline_set_blend_constant (CoglPipeline *pipeline,
     _cogl_pipeline_update_authority (pipeline, authority, state,
                                      _cogl_pipeline_blend_state_equal);
 
-    _cogl_pipeline_update_blend_enable (pipeline, state);
+    pipeline->dirty_real_blend_enable = TRUE;
   }
 #endif
 }
diff --git a/cogl/cogl-pipeline.c b/cogl/cogl-pipeline.c
index c397eb5..8769f8b 100644
--- a/cogl/cogl-pipeline.c
+++ b/cogl/cogl-pipeline.c
@@ -671,71 +671,29 @@ layer_has_alpha_cb (CoglPipelineLayer *layer, void *data)
   return !(*has_alpha);
 }
 
+/* NB: If this pipeline returns FALSE that doesn't mean that the
+ * pipeline is definitely opaque, it just means that that the
+ * given changes dont imply transparency.
+ *
+ * If you want to find out of the pipeline is opaque then assuming
+ * this returns FALSE for a set of changes then you can follow
+ * up
+ */
 static CoglBool
-_cogl_pipeline_needs_blending_enabled (CoglPipeline    *pipeline,
-                                       unsigned long    changes,
-                                       const CoglColor *override_color)
+_cogl_pipeline_change_implies_transparency (CoglPipeline *pipeline,
+                                            unsigned int changes,
+                                            const CoglColor *override_color,
+                                            CoglBool unknown_color_alpha)
 {
-  CoglPipeline *enable_authority;
-  CoglPipeline *blend_authority;
-  CoglPipelineBlendState *blend_state;
-  CoglPipelineBlendEnable enabled;
-  unsigned long other_state;
-
-  _COGL_GET_CONTEXT (ctx, FALSE);
-
-  if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_BLENDING)))
-    return FALSE;
-
-  enable_authority =
-    _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_BLEND_ENABLE);
-
-  enabled = enable_authority->blend_enable;
-  if (enabled != COGL_PIPELINE_BLEND_ENABLE_AUTOMATIC)
-    return enabled == COGL_PIPELINE_BLEND_ENABLE_ENABLED ? TRUE : FALSE;
-
-  blend_authority =
-    _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_BLEND);
-
-  blend_state = &blend_authority->big_state->blend_state;
-
-  /* We are trying to identify awkward cases that are equivalent to
-   * blending being disable, where the output is simply GL_SRC_COLOR.
-   *
-   * Note: we assume that all OpenGL drivers will identify the simple
-   * case of ADD (ONE, ZERO) as equivalent to blending being disabled.
-   *
-   * We should update this when we add support for more blend
-   * functions...
-   */
-
-#if defined (HAVE_COGL_GLES2) || defined (HAVE_COGL_GL)
-  if (ctx->driver != COGL_DRIVER_GLES1)
-    {
-      /* GLES 1 can't change the function or have separate alpha factors */
-      if (blend_state->blend_equation_rgb != GL_FUNC_ADD ||
-          blend_state->blend_equation_alpha != GL_FUNC_ADD)
-        return TRUE;
-
-      if (blend_state->blend_src_factor_alpha != GL_ONE ||
-          blend_state->blend_dst_factor_alpha != GL_ONE_MINUS_SRC_ALPHA)
-        return TRUE;
-    }
-#endif
-
-  if (blend_state->blend_src_factor_rgb != GL_ONE ||
-      blend_state->blend_dst_factor_rgb != GL_ONE_MINUS_SRC_ALPHA)
-    return TRUE;
-
-  /* Given the above constraints, it's now a case of finding any
-   * SRC_ALPHA that != 1 */
-
   /* In the case of a layer state change we need to check everything
    * else first since they contribute to the has_alpha status of the
-   * GL_PREVIOUS layer. */
+   * "PREVIOUS" layer. */
   if (changes & COGL_PIPELINE_STATE_LAYERS)
     changes = COGL_PIPELINE_STATE_AFFECTS_BLENDING;
 
+  if (unknown_color_alpha)
+    return TRUE;
+
   if ((override_color && cogl_color_get_alpha_byte (override_color) != 0xff))
     return TRUE;
 
@@ -772,24 +730,104 @@ _cogl_pipeline_needs_blending_enabled (CoglPipeline    *pipeline,
         return TRUE;
     }
 
+  return FALSE;
+}
+
+static CoglBool
+_cogl_pipeline_needs_blending_enabled (CoglPipeline *pipeline,
+                                       unsigned int changes,
+                                       const CoglColor *override_color,
+                                       CoglBool unknown_color_alpha)
+{
+  CoglPipeline *enable_authority;
+  CoglPipeline *blend_authority;
+  CoglPipelineBlendState *blend_state;
+  CoglPipelineBlendEnable enabled;
+
+  if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_BLENDING)))
+    return FALSE;
+
+  /* We unconditionally check the _BLEND_ENABLE state first because
+   * all the other changes are irrelevent if blend_enable != _AUTOMATIC
+   */
+  enable_authority =
+    _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_BLEND_ENABLE);
+
+  enabled = enable_authority->blend_enable;
+  if (enabled != COGL_PIPELINE_BLEND_ENABLE_AUTOMATIC)
+    return enabled == COGL_PIPELINE_BLEND_ENABLE_ENABLED ? TRUE : FALSE;
+
+  blend_authority =
+    _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_BLEND);
+
+  blend_state = &blend_authority->big_state->blend_state;
+
+  /* We are trying to identify some cases that are equivalent to
+   * blending being disable, where the output is simply GL_SRC_COLOR.
+   *
+   * Note: we currently only consider a few cases that can be
+   * optimized but there could be opportunities to special case more
+   * blend functions later.
+   */
+
+  /* As the most common way that we currently use to effectively
+   * disable blending is to use an equation of
+   * "RGBA=ADD(SRC_COLOR, 0)" that's the first thing we check
+   * for... */
+  if (blend_state->blend_equation_rgb == GL_FUNC_ADD &&
+      blend_state->blend_equation_alpha == GL_FUNC_ADD &&
+      blend_state->blend_src_factor_alpha == GL_ONE &&
+      blend_state->blend_dst_factor_alpha == GL_ZERO)
+    {
+      return FALSE;
+    }
+
+  /* NB: The default blending equation for Cogl is
+   * "RGBA=ADD(SRC_COLOR, DST_COLOR * (1-SRC_COLOR[A]))"
+   *
+   * Next we check if the default blending equation is being used.  If
+   * so then we follow that by looking for cases where SRC_COLOR[A] ==
+   * 1 since that simplifies "DST_COLOR * (1-SRC_COLOR[A])" to 0 which
+   * also effectively requires no blending.
+   */
+
+  if (blend_state->blend_equation_rgb != GL_FUNC_ADD ||
+      blend_state->blend_equation_alpha != GL_FUNC_ADD)
+    return TRUE;
+
+  if (blend_state->blend_src_factor_alpha != GL_ONE ||
+      blend_state->blend_dst_factor_alpha != GL_ONE_MINUS_SRC_ALPHA)
+    return TRUE;
+
+  if (blend_state->blend_src_factor_rgb != GL_ONE ||
+      blend_state->blend_dst_factor_rgb != GL_ONE_MINUS_SRC_ALPHA)
+    return TRUE;
+
+  /* Given the above constraints, it's now a case of finding any
+   * SRC_ALPHA that != 1 */
+
+  if (_cogl_pipeline_change_implies_transparency (pipeline, changes,
+                                                  override_color,
+                                                  unknown_color_alpha))
+    return TRUE;
+
   /* At this point, considering just the state that has changed it
    * looks like blending isn't needed. If blending was previously
    * enabled though it could be that some other state still requires
-   * that we have blending enabled. In this case we still need to
-   * go and check the other state...
+   * that we have blending enabled because it implies transparency.
+   * In this case we still need to go and check the other state...
    *
-   * FIXME: We should explicitly keep track of the mask of state
-   * groups that are currently causing blending to be enabled so that
-   * we never have to resort to checking *all* the state and can
-   * instead always limit the check to those in the mask.
+   * XXX: We could explicitly keep track of the mask of state groups
+   * that are currently causing blending to be enabled so that we
+   * never have to resort to checking *all* the state and can instead
+   * always limit the check to those in the mask.
    */
   if (pipeline->real_blend_enable)
     {
-      other_state = COGL_PIPELINE_STATE_AFFECTS_BLENDING & ~changes;
+      unsigned int other_state =
+        COGL_PIPELINE_STATE_AFFECTS_BLENDING & ~changes;
       if (other_state &&
-          _cogl_pipeline_needs_blending_enabled (pipeline,
-                                                 other_state,
-                                                 NULL))
+          _cogl_pipeline_change_implies_transparency (pipeline, other_state, NULL, FALSE))
         return TRUE;
     }
 
@@ -937,7 +975,7 @@ _cogl_pipeline_copy_differences (CoglPipeline *dest,
    */
 check_for_blending_change:
   if (differences & COGL_PIPELINE_STATE_AFFECTS_BLENDING)
-    _cogl_pipeline_update_blend_enable (dest, differences);
+    dest->dirty_real_blend_enable = TRUE;
 
   dest->differences |= differences;
 }
@@ -1091,7 +1129,8 @@ _cogl_pipeline_pre_change_notify (CoglPipeline     *pipeline,
           CoglBool will_need_blending =
             _cogl_pipeline_needs_blending_enabled (pipeline,
                                                    change,
-                                                   new_color);
+                                                   new_color,
+                                                   FALSE);
           CoglBool blend_enable = pipeline->real_blend_enable ? TRUE : FALSE;
 
           if (will_need_blending == blend_enable)
@@ -1363,29 +1402,51 @@ _cogl_pipeline_try_reverting_layers_authority (CoglPipeline *authority,
     }
 }
 
-
 void
-_cogl_pipeline_update_blend_enable (CoglPipeline *pipeline,
-                                    CoglPipelineState change)
+_cogl_pipeline_update_real_blend_enable (CoglPipeline *pipeline,
+                                         CoglBool unknown_color_alpha)
 {
-  CoglBool blend_enable =
-    _cogl_pipeline_needs_blending_enabled (pipeline, change, NULL);
+  CoglPipeline *parent;
+  unsigned int differences;
+
+  if (pipeline->dirty_real_blend_enable == FALSE &&
+      pipeline->unknown_color_alpha == unknown_color_alpha)
+    return;
 
-  if (blend_enable != pipeline->real_blend_enable)
+  if (pipeline->dirty_real_blend_enable)
     {
-      /* - Flush journal primitives referencing the current state.
-       * - Make sure the pipeline has no dependants so it may be
-       *   modified.
-       * - If the pipeline isn't currently an authority for the state
-       *   being changed, then initialize that state from the current
-       *   authority.
+      differences = pipeline->differences;
+
+      parent = _cogl_pipeline_get_parent (pipeline);
+      while (parent->dirty_real_blend_enable)
+        {
+          differences |= parent->differences;
+          parent = _cogl_pipeline_get_parent (parent);
+        }
+
+      /* We initialize the pipeline's real_blend_enable with a known
+       * reference value from its nearest ancestor with clean state so
+       * we can then potentially reduce the work involved in checking
+       * if the pipeline really needs blending itself because we can
+       * just look at the things that differ between the ancestor and
+       * this pipeline.
        */
-      _cogl_pipeline_pre_change_notify (pipeline,
-                                        COGL_PIPELINE_STATE_REAL_BLEND_ENABLE,
-                                        NULL,
-                                        FALSE);
-      pipeline->real_blend_enable = blend_enable;
+      pipeline->real_blend_enable = parent->real_blend_enable;
     }
+  else /* pipeline->unknown_color_alpha != unknown_color_alpha */
+    differences = 0;
+
+  /* Note we don't call _cogl_pipeline_pre_change_notify() for this
+   * state change because ->real_blend_enable is lazily derived from
+   * other state while flushing the pipeline and we'd need to avoid
+   * recursion problems in cases where _pre_change_notify() flushes
+   * the journal if the pipeline is referenced by a journal.
+   */
+  pipeline->real_blend_enable =
+    _cogl_pipeline_needs_blending_enabled (pipeline, differences,
+                                           NULL, unknown_color_alpha);
+  pipeline->dirty_real_blend_enable = FALSE;
+  pipeline->unknown_color_alpha = unknown_color_alpha;
 }
 
 typedef struct
@@ -2001,7 +2062,7 @@ _cogl_pipeline_resolve_authorities (CoglPipeline *pipeline,
 CoglBool
 _cogl_pipeline_equal (CoglPipeline *pipeline0,
                       CoglPipeline *pipeline1,
-                      unsigned long differences,
+                      unsigned int differences,
                       unsigned long layer_differences,
                       CoglPipelineEvalFlags flags)
 {
@@ -2027,6 +2088,9 @@ _cogl_pipeline_equal (CoglPipeline *pipeline0,
 
   ret = FALSE;
 
+  _cogl_pipeline_update_real_blend_enable (pipeline0, FALSE);
+  _cogl_pipeline_update_real_blend_enable (pipeline1, FALSE);
+
   /* First check non-sparse properties */
 
   if (differences & COGL_PIPELINE_STATE_REAL_BLEND_ENABLE &&
@@ -2267,7 +2331,7 @@ cogl_pipeline_remove_layer (CoglPipeline *pipeline, int layer_index)
   _cogl_pipeline_remove_layer_difference (pipeline, layer_info.layer, TRUE);
   _cogl_pipeline_try_reverting_layers_authority (pipeline, NULL);
 
-  _cogl_pipeline_update_blend_enable (pipeline, COGL_PIPELINE_STATE_LAYERS);
+  pipeline->dirty_real_blend_enable = TRUE;
 }
 
 int
@@ -2469,12 +2533,12 @@ _cogl_pipeline_init_state_hash_functions (void)
 
 unsigned int
 _cogl_pipeline_hash (CoglPipeline *pipeline,
-                     unsigned long differences,
+                     unsigned int differences,
                      unsigned long layer_differences,
                      CoglPipelineEvalFlags flags)
 {
   CoglPipeline *authorities[COGL_PIPELINE_STATE_SPARSE_COUNT];
-  unsigned long mask;
+  unsigned int mask;
   int i;
   CoglPipelineHashState state;
   unsigned int final_hash = 0;
@@ -2483,6 +2547,8 @@ _cogl_pipeline_hash (CoglPipeline *pipeline,
   state.layer_differences = layer_differences;
   state.flags = flags;
 
+  _cogl_pipeline_update_real_blend_enable (pipeline, FALSE);
+
   /* hash non-sparse state */
 
   if (differences & COGL_PIPELINE_STATE_REAL_BLEND_ENABLE)
@@ -2499,7 +2565,7 @@ _cogl_pipeline_hash (CoglPipeline *pipeline,
 
   for (i = 0; i < COGL_PIPELINE_STATE_SPARSE_COUNT; i++)
     {
-      unsigned long current_state = (1L<<i);
+      unsigned int current_state = (1<<i);
 
       /* XXX: we are hashing the un-mixed hash values of all the
        * individual state groups; we should provide a means to test
diff --git a/cogl/driver/gl/cogl-attribute-gl.c b/cogl/driver/gl/cogl-attribute-gl.c
index dd597d4..759857a 100644
--- a/cogl/driver/gl/cogl-attribute-gl.c
+++ b/cogl/driver/gl/cogl-attribute-gl.c
@@ -375,26 +375,22 @@ _cogl_gl_flush_attributes_state (CoglFramebuffer *framebuffer,
 {
   CoglContext *ctx = framebuffer->context;
   int i;
-  CoglBool skip_gl_color = FALSE;
+  CoglBool with_color_attrib = FALSE;
+  CoglBool unknown_color_alpha = FALSE;
   CoglPipeline *copy = NULL;
 
-  /* Iterate the attributes to work out whether blending needs to be
-     enabled and how many texture coords there are. We need to do this
-     before flushing the pipeline. */
+  /* Iterate the attributes to see if we have a color attribute which
+   * may affect our decision to enable blending or not.
+   *
+   * We need to do this before flushing the pipeline. */
   for (i = 0; i < n_attributes; i++)
     switch (attributes[i]->name_state->name_id)
       {
       case COGL_ATTRIBUTE_NAME_ID_COLOR_ARRAY:
         if ((flags & COGL_DRAW_COLOR_ATTRIBUTE_IS_OPAQUE) == 0 &&
-            !_cogl_pipeline_get_real_blend_enabled (pipeline))
-          {
-            CoglPipelineBlendEnable blend_enable =
-              COGL_PIPELINE_BLEND_ENABLE_ENABLED;
-            copy = cogl_pipeline_copy (pipeline);
-            _cogl_pipeline_set_blend_enabled (copy, blend_enable);
-            pipeline = copy;
-          }
-        skip_gl_color = TRUE;
+            _cogl_attribute_get_n_components (attributes[i]) == 4)
+          unknown_color_alpha = TRUE;
+        with_color_attrib = TRUE;
         break;
 
       default:
@@ -445,9 +441,11 @@ _cogl_gl_flush_attributes_state (CoglFramebuffer *framebuffer,
        */
     }
 
-  _cogl_pipeline_flush_gl_state (pipeline,
+  _cogl_pipeline_flush_gl_state (ctx,
+                                 pipeline,
                                  framebuffer,
-                                 skip_gl_color);
+                                 with_color_attrib,
+                                 unknown_color_alpha);
 
   _cogl_bitmask_clear_all (&ctx->enable_builtin_attributes_tmp);
   _cogl_bitmask_clear_all (&ctx->enable_texcoord_attributes_tmp);
diff --git a/cogl/driver/gl/cogl-clip-stack-gl.c b/cogl/driver/gl/cogl-clip-stack-gl.c
index 6fcd4c1..8f90cec 100644
--- a/cogl/driver/gl/cogl-clip-stack-gl.c
+++ b/cogl/driver/gl/cogl-clip-stack-gl.c
@@ -287,7 +287,8 @@ add_stencil_clip_silhouette (CoglFramebuffer *framebuffer,
                                               projection_stack->last_entry);
   _cogl_context_set_current_modelview_entry (ctx, modelview_entry);
 
-  _cogl_pipeline_flush_gl_state (ctx->stencil_pipeline, framebuffer, FALSE);
+  _cogl_pipeline_flush_gl_state (ctx, ctx->stencil_pipeline,
+                                 framebuffer, FALSE, FALSE);
 
   GE( ctx, glEnable (GL_STENCIL_TEST) );
 
diff --git a/cogl/driver/gl/cogl-pipeline-opengl-private.h b/cogl/driver/gl/cogl-pipeline-opengl-private.h
index 294b611..aa3a033 100644
--- a/cogl/driver/gl/cogl-pipeline-opengl-private.h
+++ b/cogl/driver/gl/cogl-pipeline-opengl-private.h
@@ -142,9 +142,11 @@ void
 _cogl_delete_gl_texture (GLuint gl_texture);
 
 void
-_cogl_pipeline_flush_gl_state (CoglPipeline *pipeline,
+_cogl_pipeline_flush_gl_state (CoglContext *context,
+                               CoglPipeline *pipeline,
                                CoglFramebuffer *framebuffer,
-                               CoglBool skip_gl_state);
+                               CoglBool skip_gl_state,
+                               CoglBool unknown_color_alpha);
 
 #endif /* __COGL_PIPELINE_OPENGL_PRIVATE_H */
 
diff --git a/cogl/driver/gl/cogl-pipeline-opengl.c b/cogl/driver/gl/cogl-pipeline-opengl.c
index e4d2976..cffff3a 100644
--- a/cogl/driver/gl/cogl-pipeline-opengl.c
+++ b/cogl/driver/gl/cogl-pipeline-opengl.c
@@ -448,18 +448,18 @@ static void
 _cogl_pipeline_flush_color_blend_alpha_depth_state (
                                             CoglPipeline *pipeline,
                                             unsigned long pipelines_difference,
-                                            CoglBool      skip_gl_color)
+                                            CoglBool      with_color_attrib)
 {
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
   /* On GLES2 we'll flush the color later */
   if ((ctx->private_feature_flags & COGL_PRIVATE_FEATURE_FIXED_FUNCTION) &&
-      !skip_gl_color)
+      !with_color_attrib)
     {
       if ((pipelines_difference & COGL_PIPELINE_STATE_COLOR) ||
           /* Assume if we were previously told to skip the color, then
            * the current color needs updating... */
-          ctx->current_pipeline_skip_gl_color)
+          ctx->current_pipeline_with_color_attrib)
         {
           CoglPipeline *authority =
             _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_COLOR);
@@ -867,7 +867,7 @@ static void
 _cogl_pipeline_flush_common_gl_state (CoglPipeline  *pipeline,
                                       unsigned long  pipelines_difference,
                                       unsigned long *layer_differences,
-                                      CoglBool       skip_gl_color)
+                                      CoglBool       with_color_attrib)
 {
   CoglPipelineFlushLayerState state;
 
@@ -875,7 +875,7 @@ _cogl_pipeline_flush_common_gl_state (CoglPipeline  *pipeline,
 
   _cogl_pipeline_flush_color_blend_alpha_depth_state (pipeline,
                                                       pipelines_difference,
-                                                      skip_gl_color);
+                                                      with_color_attrib);
 
   state.i = 0;
   state.layer_differences = layer_differences;
@@ -1127,10 +1127,13 @@ fragend_add_layer_cb (CoglPipelineLayer *layer,
  *    isn't ideal, and can't be used with CoglVertexBuffers.
  */
 void
-_cogl_pipeline_flush_gl_state (CoglPipeline *pipeline,
+_cogl_pipeline_flush_gl_state (CoglContext *ctx,
+                               CoglPipeline *pipeline,
                                CoglFramebuffer *framebuffer,
-                               CoglBool skip_gl_color)
+                               CoglBool with_color_attrib,
+                               CoglBool unknown_color_alpha)
 {
+  CoglPipeline *current_pipeline = ctx->current_pipeline;
   unsigned long pipelines_difference;
   int n_layers;
   unsigned long *layer_differences;
@@ -1144,29 +1147,61 @@ _cogl_pipeline_flush_gl_state (CoglPipeline *pipeline,
                      "The time spent flushing material state",
                      0 /* no application private data */);
 
-  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
-
   COGL_TIMER_START (_cogl_uprof_context, pipeline_flush_timer);
 
-  if (ctx->current_pipeline == pipeline)
+  /* Bail out asap if we've been asked to re-flush the already current
+   * pipeline and we can see the pipeline hasn't changed */
+  if (current_pipeline == pipeline &&
+      ctx->current_pipeline_age == pipeline->age &&
+      ctx->current_pipeline_with_color_attrib == with_color_attrib &&
+      ctx->current_pipeline_unknown_color_alpha == unknown_color_alpha)
+    goto done;
+  else
     {
-      /* Bail out asap if we've been asked to re-flush the already current
-       * pipeline and we can see the pipeline hasn't changed */
-      if (ctx->current_pipeline_age == pipeline->age &&
-          ctx->current_pipeline_skip_gl_color == skip_gl_color)
-        goto done;
+      /* Update derived state (currently just the 'real_blend_enable'
+       * state) and determine a mask of state that differs between the
+       * current pipeline and the one we are flushing.
+       *
+       * Note updating the derived state is done before doing any
+       * pipeline comparisons so that we can correctly compare the
+       * 'real_blend_enable' state itself.
+       */
 
-      pipelines_difference = ctx->current_pipeline_changes_since_flush;
-    }
-  else if (ctx->current_pipeline)
-    {
-      pipelines_difference = ctx->current_pipeline_changes_since_flush;
-      pipelines_difference |=
-        _cogl_pipeline_compare_differences (ctx->current_pipeline,
-                                            pipeline);
+      if (current_pipeline == pipeline)
+        {
+          pipelines_difference = ctx->current_pipeline_changes_since_flush;
+
+          if (pipelines_difference & COGL_PIPELINE_STATE_AFFECTS_BLENDING ||
+              pipeline->unknown_color_alpha != unknown_color_alpha)
+            {
+              CoglBool save_real_blend_enable = pipeline->real_blend_enable;
+
+              _cogl_pipeline_update_real_blend_enable (pipeline,
+                                                       unknown_color_alpha);
+
+              if (save_real_blend_enable != pipeline->real_blend_enable)
+                pipelines_difference |= COGL_PIPELINE_STATE_REAL_BLEND_ENABLE;
+            }
+        }
+      else if (current_pipeline)
+        {
+          pipelines_difference = ctx->current_pipeline_changes_since_flush;
+
+          _cogl_pipeline_update_real_blend_enable (pipeline,
+                                                   unknown_color_alpha);
+
+          pipelines_difference |=
+            _cogl_pipeline_compare_differences (ctx->current_pipeline,
+                                                pipeline);
+        }
+      else
+        {
+          _cogl_pipeline_update_real_blend_enable (pipeline,
+                                                   unknown_color_alpha);
+
+          pipelines_difference = COGL_PIPELINE_STATE_ALL;
+        }
     }
-  else
-    pipelines_difference = COGL_PIPELINE_STATE_ALL_SPARSE;
 
   /* Get a layer_differences mask for each layer to be flushed */
   n_layers = cogl_pipeline_get_n_layers (pipeline);
@@ -1203,7 +1238,7 @@ _cogl_pipeline_flush_gl_state (CoglPipeline *pipeline,
   _cogl_pipeline_flush_common_gl_state (pipeline,
                                         pipelines_difference,
                                         layer_differences,
-                                        skip_gl_color);
+                                        with_color_attrib);
 
   /* Now flush the fragment, vertex and program state according to the
    * current progend backend.
@@ -1305,7 +1340,8 @@ _cogl_pipeline_flush_gl_state (CoglPipeline *pipeline,
     cogl_object_unref (ctx->current_pipeline);
   ctx->current_pipeline = pipeline;
   ctx->current_pipeline_changes_since_flush = 0;
-  ctx->current_pipeline_skip_gl_color = skip_gl_color;
+  ctx->current_pipeline_with_color_attrib = with_color_attrib;
+  ctx->current_pipeline_unknown_color_alpha = unknown_color_alpha;
   ctx->current_pipeline_age = pipeline->age;
 
 done:
@@ -1316,7 +1352,7 @@ done:
    * using the glsl progend because the generic attribute values are
    * not stored as part of the program object so they could be
    * overridden by any attribute changes in another program */
-  if (pipeline->progend == COGL_PIPELINE_PROGEND_GLSL && !skip_gl_color)
+  if (pipeline->progend == COGL_PIPELINE_PROGEND_GLSL && !with_color_attrib)
     {
       int attribute;
       CoglPipeline *authority =
-- 
1.8.2.1



More information about the Cogl mailing list