[cogl/cogl-1.16] pipeline: improve real_blend_enable checks



commit 8f9151303d4d744205cce5cf0235b3e83e6687ef
Author: Robert Bragg <robert linux intel com>
Date:   Thu May 16 15:19:30 2013 +0100

    pipeline: improve real_blend_enable checks
    
    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.
    
    Reviewed-by: Neil Roberts <neil linux intel com>
    
    (cherry picked from commit ab2ae18f3207514c91fa6fd9f2d3f2ed93a86497)

 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                    |   18 +-
 cogl/cogl-pipeline.c                          |  250 ++++++++++++++++---------
 cogl/cogl.c                                   |    4 +-
 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 +++++++---
 13 files changed, 290 insertions(+), 158 deletions(-)
---
diff --git a/cogl/cogl-attribute-private.h b/cogl/cogl-attribute-private.h
index d38f4e6..a27924b 100644
--- a/cogl/cogl-attribute-private.h
+++ b/cogl/cogl-attribute-private.h
@@ -126,5 +126,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 b0ac790..bc45399 100644
--- a/cogl/cogl-attribute.c
+++ b/cogl/cogl-attribute.c
@@ -657,3 +657,12 @@ _cogl_flush_attributes_state (CoglFramebuffer *framebuffer,
   if (copy)
     cogl_object_unref (copy);
 }
+
+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 d73928e..c800b3f 100644
--- a/cogl/cogl-context-private.h
+++ b/cogl/cogl-context-private.h
@@ -167,7 +167,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 070f2d9..96254b9 100644
--- a/cogl/cogl-context.c
+++ b/cogl/cogl-context.c
@@ -325,7 +325,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 606d121..8b8594d 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
@@ -1267,7 +1267,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;
 }
 
@@ -1352,7 +1352,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 acb5653..2679fbb 100644
--- a/cogl/cogl-pipeline-private.h
+++ b/cogl/cogl-pipeline-private.h
@@ -470,6 +470,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;
   unsigned int          deprecated_get_layers_list_dirty:1;
 
@@ -597,8 +610,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
 {
@@ -835,13 +849,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 528fabd..b3e6bde 100644
--- a/cogl/cogl-pipeline-state.c
+++ b/cogl/cogl-pipeline-state.c
@@ -408,7 +408,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
@@ -482,7 +482,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;
 }
 
 void
@@ -532,7 +532,7 @@ cogl_pipeline_set_ambient (CoglPipeline *pipeline,
   _cogl_pipeline_update_authority (pipeline, authority, state,
                                    _cogl_pipeline_lighting_state_equal);
 
-  _cogl_pipeline_update_blend_enable (pipeline, state);
+  pipeline->dirty_real_blend_enable = TRUE;
 }
 
 void
@@ -583,7 +583,7 @@ cogl_pipeline_set_diffuse (CoglPipeline *pipeline,
   _cogl_pipeline_update_authority (pipeline, authority, state,
                                    _cogl_pipeline_lighting_state_equal);
 
-  _cogl_pipeline_update_blend_enable (pipeline, state);
+  pipeline->dirty_real_blend_enable = TRUE;
 }
 
 void
@@ -640,7 +640,7 @@ cogl_pipeline_set_specular (CoglPipeline *pipeline, const CoglColor *specular)
   _cogl_pipeline_update_authority (pipeline, authority, state,
                                    _cogl_pipeline_lighting_state_equal);
 
-  _cogl_pipeline_update_blend_enable (pipeline, state);
+  pipeline->dirty_real_blend_enable = TRUE;
 }
 
 float
@@ -740,7 +740,7 @@ cogl_pipeline_set_emission (CoglPipeline *pipeline, const CoglColor *emission)
   _cogl_pipeline_update_authority (pipeline, authority, state,
                                    _cogl_pipeline_lighting_state_equal);
 
-  _cogl_pipeline_update_blend_enable (pipeline, state);
+  pipeline->dirty_real_blend_enable = TRUE;
 }
 
 static void
@@ -1008,7 +1008,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;
 }
@@ -1049,7 +1049,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
 }
@@ -1126,7 +1126,7 @@ cogl_pipeline_set_user_program (CoglPipeline *pipeline,
     cogl_handle_unref (pipeline->big_state->user_program);
   pipeline->big_state->user_program = program;
 
-  _cogl_pipeline_update_blend_enable (pipeline, state);
+  pipeline->dirty_real_blend_enable = TRUE;
 }
 
 CoglBool
diff --git a/cogl/cogl-pipeline.c b/cogl/cogl-pipeline.c
index a91ad25..6be22fb 100644
--- a/cogl/cogl-pipeline.c
+++ b/cogl/cogl-pipeline.c
@@ -740,71 +740,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;
 
@@ -878,24 +836,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;
     }
 
@@ -1066,7 +1104,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;
 }
@@ -1235,7 +1273,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)
@@ -1507,29 +1546,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
@@ -2145,7 +2206,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)
 {
@@ -2171,6 +2232,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 &&
@@ -2438,7 +2502,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;
 }
 
 static CoglBool
@@ -2718,12 +2782,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;
@@ -2732,6 +2796,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)
@@ -2748,7 +2814,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/cogl.c b/cogl/cogl.c
index 36421a5..3c72243 100644
--- a/cogl/cogl.c
+++ b/cogl/cogl.c
@@ -390,8 +390,10 @@ cogl_begin_gl (void)
    * values.
    */
   pipeline = cogl_get_source ();
-  _cogl_pipeline_flush_gl_state (pipeline,
+  _cogl_pipeline_flush_gl_state (ctx,
+                                 pipeline,
                                  cogl_get_draw_framebuffer (),
+                                 FALSE,
                                  FALSE);
 
   /* Disable any cached vertex arrays */
diff --git a/cogl/driver/gl/cogl-attribute-gl.c b/cogl/driver/gl/cogl-attribute-gl.c
index ba7e627..c46bcf2 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 e872267..e440256 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 81d1b28..86d6124 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);
@@ -889,7 +889,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;
 
@@ -897,7 +897,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;
@@ -1149,10 +1149,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;
@@ -1166,29 +1169,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);
@@ -1225,7 +1260,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.
@@ -1327,7 +1362,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:
@@ -1338,7 +1374,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 =


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]