[cogl] rework enabling of attributes, removing _cogl_enable()



commit 7283e0a49c5eeee9242ccd5857e2a51d6934b792
Author: Robert Bragg <robert linux intel com>
Date:   Thu Nov 24 18:09:53 2011 +0000

    rework enabling of attributes, removing _cogl_enable()
    
    This removes the limited caching of enabled attributes done by
    _cogl_enable() and replaces it with a more generalized set of bitmasks
    associated with the context that allow us to efficiently compare the set
    of attribute locations that are currently enabled vs the new locations
    that need enabling so we only have to inform OpenGL of the changes in
    which locations are enabled/disabled.
    
    This also adds a per-context hash table for mapping attribute names to
    global name-state structs which includes a unique name-index for any
    name as well as pre-validated information about builtin "cogl_"
    attribute names including whether the attribute is normalized and what
    texture unit a texture attribute corresponds too.
    
    The name-state hash table means that cogl_attribute_new() now only needs
    to validate names the first time they are seen.
    
    CoglAttributes now reference a name-state structure instead of just the
    attribute name, so now we can efficiently get the name-index for any
    attribute and we can use that to index into a per-glsl-program cache
    that maps name indices to real GL attribute locations so when we get
    asked to draw a set of attributes we can very quickly determine what GL
    attributes need to be setup and enabled. If we don't have a cached
    location though we can still quickly access the string name so we can
    query OpenGL.
    
    Reviewed-by: Neil Roberts <neil linux intel com>

 cogl/cogl-attribute-private.h             |   23 +-
 cogl/cogl-attribute.c                     |  690 +++++++++++++----------------
 cogl/cogl-context-private.h               |   27 +-
 cogl/cogl-context.c                       |   37 ++-
 cogl/cogl-pipeline-opengl.c               |    4 +-
 cogl/cogl-pipeline-progend-glsl-private.h |   17 +-
 cogl/cogl-pipeline-progend-glsl.c         |  162 ++-----
 cogl/cogl.c                               |   66 ---
 8 files changed, 416 insertions(+), 610 deletions(-)
---
diff --git a/cogl/cogl-attribute-private.h b/cogl/cogl-attribute-private.h
index 040784e..68ed52d 100644
--- a/cogl/cogl-attribute-private.h
+++ b/cogl/cogl-attribute-private.h
@@ -40,19 +40,26 @@ typedef enum
   COGL_ATTRIBUTE_NAME_ID_CUSTOM_ARRAY
 } CoglAttributeNameID;
 
+typedef struct _CoglAttributeNameState
+{
+  char *name;
+  CoglAttributeNameID name_id;
+  int name_index;
+  gboolean normalized_default;
+  int texture_unit;
+} CoglAttributeNameState;
+
 struct _CoglAttribute
 {
   CoglObject _parent;
 
   CoglAttributeBuffer *attribute_buffer;
-  const char *name;
-  CoglAttributeNameID name_id;
+  const CoglAttributeNameState *name_state;
   gsize stride;
   gsize offset;
   int n_components;
   CoglAttributeType type;
   gboolean normalized;
-  unsigned int texture_unit;
 
   int immutable_ref;
 };
@@ -71,6 +78,16 @@ typedef enum
   COGL_DRAW_COLOR_ATTRIBUTE_IS_OPAQUE = 1 << 3
 } CoglDrawFlags;
 
+/* During CoglContext initialization we register the "cogl_color_in"
+ * attribute name so it gets a global name_index of 0. We need to know
+ * the name_index for "cogl_color_in" in
+ * _cogl_pipeline_flush_gl_state() */
+#define COGL_ATTRIBUTE_COLOR_NAME_INDEX 0
+
+CoglAttributeNameState *
+_cogl_attribute_register_attribute_name (CoglContext *context,
+                                         const char *name);
+
 CoglAttribute *
 _cogl_attribute_immutable_ref (CoglAttribute *attribute);
 
diff --git a/cogl/cogl-attribute.c b/cogl/cogl-attribute.c
index ad525f6..1c73bc9 100644
--- a/cogl/cogl-attribute.c
+++ b/cogl/cogl-attribute.c
@@ -59,80 +59,11 @@ static void _cogl_attribute_free (CoglAttribute *attribute);
 
 COGL_OBJECT_DEFINE (Attribute, attribute);
 
-#if 0
-gboolean
-validate_gl_attribute (const char *name,
-                       int n_components,
-                       CoglAttributeNameID *name_id,
-                       gboolean *normalized,
-                       unsigned int *texture_unit)
-{
-  name = name + 3; /* skip past "gl_" */
-
-  *normalized = FALSE;
-  *texture_unit = 0;
-
-  if (strcmp (name, "Vertex") == 0)
-    {
-      if (G_UNLIKELY (n_components == 1))
-        {
-          g_critical ("glVertexPointer doesn't allow 1 component vertex "
-                      "positions so we currently only support \"gl_Vertex\" "
-                      "attributes where n_components == 2, 3 or 4");
-          return FALSE;
-        }
-      *name_id = COGL_ATTRIBUTE_NAME_ID_POSITION_ARRAY;
-    }
-  else if (strcmp (name, "Color") == 0)
-    {
-      if (G_UNLIKELY (n_components != 3 && n_components != 4))
-        {
-          g_critical ("glColorPointer expects 3 or 4 component colors so we "
-                      "currently only support \"gl_Color\" attributes where "
-                      "n_components == 3 or 4");
-          return FALSE;
-        }
-      *name_id = COGL_ATTRIBUTE_NAME_ID_COLOR_ARRAY;
-      *normalized = TRUE;
-    }
-  else if (strncmp (name, "MultiTexCoord", strlen ("MultiTexCoord")) == 0)
-    {
-      if (sscanf (gl_attribute, "MultiTexCoord%u", texture_unit) != 1)
-	{
-	  g_warning ("gl_MultiTexCoord attributes should include a\n"
-		     "texture unit number, E.g. gl_MultiTexCoord0\n");
-	  unit = 0;
-	}
-      *name_id = COGL_ATTRIBUTE_NAME_ID_TEXTURE_COORD_ARRAY;
-    }
-  else if (strncmp (name, "Normal") == 0)
-    {
-      if (G_UNLIKELY (n_components != 3))
-        {
-          g_critical ("glNormalPointer expects 3 component normals so we "
-                      "currently only support \"gl_Normal\" attributes where "
-                      "n_components == 3");
-          return FALSE;
-        }
-      *name_id = COGL_ATTRIBUTE_NAME_ID_NORMAL_ARRAY;
-      *normalized = TRUE;
-    }
-  else
-    {
-      g_warning ("Unknown gl_* attribute name gl_%s\n", name);
-      return FALSE;
-    }
-
-  return TRUE;
-}
-#endif
-
-gboolean
-validate_cogl_attribute (const char *name,
-                         int n_components,
-                         CoglAttributeNameID *name_id,
-                         gboolean *normalized,
-                         unsigned int *texture_unit)
+static gboolean
+validate_cogl_attribute_name (const char *name,
+                              CoglAttributeNameID *name_id,
+                              gboolean *normalized,
+                              int *texture_unit)
 {
   name = name + 5; /* skip "cogl_" */
 
@@ -140,26 +71,11 @@ validate_cogl_attribute (const char *name,
   *texture_unit = 0;
 
   if (strcmp (name, "position_in") == 0)
-    {
-      if (G_UNLIKELY (n_components == 1))
-        {
-          g_critical ("glVertexPointer doesn't allow 1 component vertex "
-                      "positions so we currently only support \"cogl_vertex\" "
-                      "attributes where n_components == 2, 3 or 4");
-          return FALSE;
-        }
-      *name_id = COGL_ATTRIBUTE_NAME_ID_POSITION_ARRAY;
-    }
+    *name_id = COGL_ATTRIBUTE_NAME_ID_POSITION_ARRAY;
   else if (strcmp (name, "color_in") == 0)
     {
-      if (G_UNLIKELY (n_components != 3 && n_components != 4))
-        {
-          g_critical ("glColorPointer expects 3 or 4 component colors so we "
-                      "currently only support \"cogl_color\" attributes where "
-                      "n_components == 3 or 4");
-          return FALSE;
-        }
       *name_id = COGL_ATTRIBUTE_NAME_ID_COLOR_ARRAY;
+      *normalized = TRUE;
     }
   else if (strcmp (name, "tex_coord_in") == 0)
     *name_id = COGL_ATTRIBUTE_NAME_ID_TEXTURE_COORD_ARRAY;
@@ -178,13 +94,6 @@ validate_cogl_attribute (const char *name,
     }
   else if (strcmp (name, "normal_in") == 0)
     {
-      if (G_UNLIKELY (n_components != 3))
-        {
-          g_critical ("glNormalPointer expects 3 component normals so we "
-                      "currently only support \"cogl_normal\" attributes "
-                      "where n_components == 3");
-          return FALSE;
-        }
       *name_id = COGL_ATTRIBUTE_NAME_ID_NORMAL_ARRAY;
       *normalized = TRUE;
     }
@@ -197,6 +106,49 @@ validate_cogl_attribute (const char *name,
   return TRUE;
 }
 
+CoglAttributeNameState *
+_cogl_attribute_register_attribute_name (CoglContext *context,
+                                         const char *name)
+{
+  CoglAttributeNameState *name_state = g_new (CoglAttributeNameState, 1);
+  int name_index = context->n_attribute_names++;
+
+  name_state->name = g_strdup (name);
+  name_state->name_index = name_index;
+  if (strncmp (name, "cogl_", 5) == 0)
+    {
+      if (!validate_cogl_attribute_name (name,
+                                         &name_state->name_id,
+                                         &name_state->normalized_default,
+                                         &name_state->texture_unit))
+      goto error;
+    }
+  else
+    {
+      name_state->name_id = COGL_ATTRIBUTE_NAME_ID_CUSTOM_ARRAY;
+      name_state->normalized_default = FALSE;
+      name_state->texture_unit = 0;
+    }
+
+  g_hash_table_insert (context->attribute_name_states_hash,
+                       name_state->name, name_state);
+
+  if (G_UNLIKELY (context->attribute_name_index_map == NULL))
+    context->attribute_name_index_map =
+      g_array_new (FALSE, FALSE, sizeof (void *));
+
+  g_array_set_size (context->attribute_name_index_map, name_index + 1);
+
+  g_array_index (context->attribute_name_index_map,
+                 CoglAttributeNameState *, name_index) = name_state;
+
+  return name_state;
+
+error:
+  g_free (name_state);
+  return NULL;
+}
+
 CoglAttribute *
 cogl_attribute_new (CoglAttributeBuffer *attribute_buffer,
                     const char *name,
@@ -206,8 +158,20 @@ cogl_attribute_new (CoglAttributeBuffer *attribute_buffer,
                     CoglAttributeType type)
 {
   CoglAttribute *attribute = g_slice_new (CoglAttribute);
-  gboolean status;
 
+  /* FIXME: retrieve the context from the buffer */
+  _COGL_GET_CONTEXT (ctx, NULL);
+
+  attribute->name_state =
+    g_hash_table_lookup (ctx->attribute_name_states_hash, name);
+  if (!attribute->name_state)
+    {
+      CoglAttributeNameState *name_state =
+        _cogl_attribute_register_attribute_name (ctx, name);
+      if (!name_state)
+        goto error;
+      attribute->name_state = name_state;
+    }
   attribute->attribute_buffer = cogl_object_ref (attribute_buffer);
   attribute->stride = stride;
   attribute->offset = offset;
@@ -215,71 +179,52 @@ cogl_attribute_new (CoglAttributeBuffer *attribute_buffer,
   attribute->type = type;
   attribute->immutable_ref = 0;
 
-  if (strncmp (name, "cogl_", 5) == 0)
+  if (attribute->name_state->name_id != COGL_ATTRIBUTE_NAME_ID_CUSTOM_ARRAY)
     {
-      const char *common_tex_coord_names[8] = {
-          "cogl_tex_coord0_in",
-          "cogl_tex_coord1_in",
-          "cogl_tex_coord2_in",
-          "cogl_tex_coord3_in",
-          "cogl_tex_coord4_in",
-          "cogl_tex_coord5_in",
-          "cogl_tex_coord6_in",
-          "cogl_tex_coord7_in"
-      };
-      status = validate_cogl_attribute (name,
-                                        n_components,
-                                        &attribute->name_id,
-                                        &attribute->normalized,
-                                        &attribute->texture_unit);
-
-      /* Avoid even the cost of g_intern_string() for the very common case
-       * attribute names...*/
-      switch (attribute->name_id)
+      switch (attribute->name_state->name_id)
         {
         case COGL_ATTRIBUTE_NAME_ID_POSITION_ARRAY:
-          attribute->name = "cogl_position_in";
+          if (G_UNLIKELY (n_components == 1))
+            {
+              g_critical ("glVertexPointer doesn't allow 1 component vertex "
+                          "positions so we currently only support \"cogl_vertex\" "
+                          "attributes where n_components == 2, 3 or 4");
+              return FALSE;
+            }
           break;
         case COGL_ATTRIBUTE_NAME_ID_COLOR_ARRAY:
-          attribute->name = "cogl_color_in";
+          if (G_UNLIKELY (n_components != 3 && n_components != 4))
+            {
+              g_critical ("glColorPointer expects 3 or 4 component colors so we "
+                          "currently only support \"cogl_color\" attributes where "
+                          "n_components == 3 or 4");
+              return FALSE;
+            }
           break;
         case COGL_ATTRIBUTE_NAME_ID_TEXTURE_COORD_ARRAY:
-          if (attribute->texture_unit < 8)
-            attribute->name = common_tex_coord_names[attribute->texture_unit];
-          else
-            attribute->name = g_intern_string (name);
           break;
         case COGL_ATTRIBUTE_NAME_ID_NORMAL_ARRAY:
-          attribute->name = "cogl_normal_in";
+          if (G_UNLIKELY (n_components != 3))
+            {
+              g_critical ("glNormalPointer expects 3 component normals so we "
+                          "currently only support \"cogl_normal\" attributes "
+                          "where n_components == 3");
+              return FALSE;
+            }
           break;
         default:
           g_warn_if_reached ();
         }
+      attribute->normalized = attribute->name_state->normalized_default;
     }
-#if 0
-  else if (strncmp (name, "gl_", 3) == 0)
-    status = validate_gl_attribute (attribute->name,
-                                    n_components,
-                                    &attribute->name_id,
-                                    &attribute->normalized,
-                                    &attribute->texture_unit);
-#endif
   else
-    {
-      attribute->name = g_intern_string (name);
-      attribute->name_id = COGL_ATTRIBUTE_NAME_ID_CUSTOM_ARRAY;
-      attribute->normalized = FALSE;
-      attribute->texture_unit = 0;
-      status = TRUE;
-    }
-
-  if (!status)
-    {
-      _cogl_attribute_free (attribute);
-      return NULL;
-    }
+    attribute->normalized = FALSE;
 
   return _cogl_attribute_object_new (attribute);
+
+error:
+  _cogl_attribute_free (attribute);
+  return NULL;
 }
 
 gboolean
@@ -428,87 +373,216 @@ validated:
   return status;
 }
 
+typedef struct _ForeachChangedBitState
+{
+  CoglContext *context;
+  const CoglBitmask *new_bits;
+  CoglPipeline *pipeline;
+} ForeachChangedBitState;
+
 static gboolean
-toggle_enabled_cb (int bit_num, void *user_data)
+toggle_builtin_attribute_enabled_cb (int bit_num, void *user_data)
 {
-  const CoglBitmask *new_values = user_data;
-  gboolean enabled = _cogl_bitmask_get (new_values, bit_num);
+  ForeachChangedBitState *state = user_data;
+  CoglContext *context = state->context;
 
-  _COGL_GET_CONTEXT (ctx, FALSE);
+  _COGL_RETURN_VAL_IF_FAIL (context->driver == COGL_DRIVER_GL ||
+                            context->driver == COGL_DRIVER_GLES1,
+                            FALSE);
 
-  if (ctx->driver == COGL_DRIVER_GLES2)
-    {
-      if (enabled)
-        GE( ctx, glEnableVertexAttribArray (bit_num) );
-      else
-        GE( ctx, glDisableVertexAttribArray (bit_num) );
-    }
 #if defined (HAVE_COGL_GL) || defined (HAVE_COGL_GLES)
-  else
-    {
-      GE( ctx, glClientActiveTexture (GL_TEXTURE0 + bit_num) );
+  {
+    gboolean enabled = _cogl_bitmask_get (state->new_bits, bit_num);
+    GLenum cap;
 
-      if (enabled)
-        GE( ctx, glEnableClientState (GL_TEXTURE_COORD_ARRAY) );
-      else
-        GE( ctx, glDisableClientState (GL_TEXTURE_COORD_ARRAY) );
-    }
+    switch (bit_num)
+      {
+      case COGL_ATTRIBUTE_NAME_ID_COLOR_ARRAY:
+        cap = GL_COLOR_ARRAY;
+        break;
+      case COGL_ATTRIBUTE_NAME_ID_POSITION_ARRAY:
+        cap = GL_VERTEX_ARRAY;
+        break;
+      case COGL_ATTRIBUTE_NAME_ID_NORMAL_ARRAY:
+        cap = GL_NORMAL_ARRAY;
+        break;
+      }
+    if (enabled)
+      GE (context, glEnableClientState (cap));
+    else
+      GE (context, glDisableClientState (cap));
+  }
 #endif
 
   return TRUE;
 }
 
-static void
-set_enabled_arrays (CoglBitmask *value_cache,
-                    const CoglBitmask *new_values)
+static gboolean
+toggle_texcood_attribute_enabled_cb (int bit_num, void *user_data)
 {
-  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+  ForeachChangedBitState *state = user_data;
+  CoglContext *context = state->context;
+
+  _COGL_RETURN_VAL_IF_FAIL (context->driver == COGL_DRIVER_GL ||
+                            context->driver == COGL_DRIVER_GLES1,
+                            FALSE);
+
+#if defined (HAVE_COGL_GL) || defined (HAVE_COGL_GLES)
+  {
+    gboolean enabled = _cogl_bitmask_get (state->new_bits, bit_num);
+
+    GE( context, glClientActiveTexture (GL_TEXTURE0 + bit_num) );
+
+    if (enabled)
+      GE( context, glEnableClientState (GL_TEXTURE_COORD_ARRAY) );
+    else
+      GE( context, glDisableClientState (GL_TEXTURE_COORD_ARRAY) );
+  }
+#endif
+
+  return TRUE;
+}
 
+static gboolean
+toggle_custom_attribute_enabled_cb (int bit_num, void *user_data)
+{
+  ForeachChangedBitState *state = user_data;
+  gboolean enabled = _cogl_bitmask_get (state->new_bits, bit_num);
+  CoglContext *context = state->context;
+
+  if (enabled)
+    GE( context, glEnableVertexAttribArray (bit_num) );
+  else
+    GE( context, glDisableVertexAttribArray (bit_num) );
+
+  return TRUE;
+}
+
+static void
+foreach_changed_bit_and_save (CoglContext *context,
+                              CoglBitmask *current_bits,
+                              const CoglBitmask *new_bits,
+                              CoglBitmaskForeachFunc callback,
+                              ForeachChangedBitState *state)
+{
   /* Get the list of bits that are different */
-  _cogl_bitmask_clear_all (&ctx->arrays_to_change);
-  _cogl_bitmask_set_bits (&ctx->arrays_to_change, value_cache);
-  _cogl_bitmask_xor_bits (&ctx->arrays_to_change, new_values);
+  _cogl_bitmask_clear_all (&context->changed_bits_tmp);
+  _cogl_bitmask_set_bits (&context->changed_bits_tmp, current_bits);
+  _cogl_bitmask_xor_bits (&context->changed_bits_tmp, new_bits);
 
   /* Iterate over each bit to change */
-  _cogl_bitmask_foreach (&ctx->arrays_to_change,
-                         toggle_enabled_cb,
-                         (void *) new_values);
+  state->new_bits = new_bits;
+  _cogl_bitmask_foreach (&context->changed_bits_tmp,
+                         callback,
+                         state);
 
   /* Store the new values */
-  _cogl_bitmask_clear_all (value_cache);
-  _cogl_bitmask_set_bits (value_cache, new_values);
+  _cogl_bitmask_clear_all (current_bits);
+  _cogl_bitmask_set_bits (current_bits, new_bits);
+}
+
+static void
+setup_generic_attribute (CoglContext *context,
+                         CoglPipeline *pipeline,
+                         CoglAttribute *attribute,
+                         guint8 *base)
+{
+  int name_index = attribute->name_state->name_index;
+  int attrib_location =
+    _cogl_pipeline_progend_glsl_get_attrib_location (pipeline, name_index);
+  if (attrib_location != -1)
+    {
+      GE( context, glVertexAttribPointer (attrib_location,
+                                          attribute->n_components,
+                                          attribute->type,
+                                          attribute->normalized,
+                                          attribute->stride,
+                                          base + attribute->offset) );
+      _cogl_bitmask_set (&context->enable_custom_attributes_tmp,
+                         attrib_location, TRUE);
+    }
+}
+
+static void
+apply_attribute_enable_updates (CoglContext *context,
+                                CoglPipeline *pipeline)
+{
+  ForeachChangedBitState changed_bits_state;
+
+  changed_bits_state.context = context;
+  changed_bits_state.new_bits = &context->enable_builtin_attributes_tmp;
+  changed_bits_state.pipeline = pipeline;
+
+  foreach_changed_bit_and_save (context,
+                                &context->enabled_builtin_attributes,
+                                &context->enable_builtin_attributes_tmp,
+                                toggle_builtin_attribute_enabled_cb,
+                                &changed_bits_state);
+
+  changed_bits_state.new_bits = &context->enable_texcoord_attributes_tmp;
+  foreach_changed_bit_and_save (context,
+                                &context->enabled_texcoord_attributes,
+                                &context->enable_texcoord_attributes_tmp,
+                                toggle_texcood_attribute_enabled_cb,
+                                &changed_bits_state);
+
+  changed_bits_state.new_bits = &context->enable_custom_attributes_tmp;
+  foreach_changed_bit_and_save (context,
+                                &context->enabled_custom_attributes,
+                                &context->enable_custom_attributes_tmp,
+                                toggle_custom_attribute_enabled_cb,
+                                &changed_bits_state);
 }
 
-static CoglHandle
-enable_gl_state (CoglDrawFlags flags,
-                 CoglAttribute **attributes,
-                 int n_attributes,
-                 ValidateLayerState *state)
+static CoglPipeline *
+flush_state (CoglDrawFlags flags,
+             CoglAttribute **attributes,
+             int n_attributes,
+             ValidateLayerState *state)
 {
   CoglFramebuffer *framebuffer = cogl_get_draw_framebuffer ();
   int i;
-  GLuint generic_index = 0;
-  unsigned long enable_flags = 0;
   gboolean skip_gl_color = FALSE;
-  CoglPipeline *source;
+  CoglPipeline *source = cogl_get_source ();
   CoglPipeline *copy = NULL;
   int n_tex_coord_attribs = 0;
 
   _COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE);
 
+  if (!(flags & COGL_DRAW_SKIP_JOURNAL_FLUSH))
+    _cogl_journal_flush (framebuffer->journal, framebuffer);
+
+  state->unit = 0;
+  state->options.flags = 0;
+  state->fallback_layers = 0;
+
+  if (!(flags & COGL_DRAW_SKIP_PIPELINE_VALIDATION))
+    cogl_pipeline_foreach_layer (cogl_get_source (),
+                                 validate_layer_cb,
+                                 state);
+
+  /* NB: _cogl_framebuffer_flush_state may disrupt various state (such
+   * as the pipeline state) when flushing the clip stack, so should
+   * always be done first when preparing to draw. We need to do this
+   * before setting up the array pointers because setting up the clip
+   * stack can cause some drawing which would change the array
+   * pointers. */
+  if (!(flags & COGL_DRAW_SKIP_FRAMEBUFFER_FLUSH))
+    _cogl_framebuffer_flush_state (cogl_get_draw_framebuffer (),
+                                   _cogl_get_read_framebuffer (),
+                                   COGL_FRAMEBUFFER_STATE_ALL);
+
   /* In cogl_read_pixels we have a fast-path when reading a single
    * pixel and the scene is just comprised of simple rectangles still
    * in the journal. For this optimization to work we need to track
    * when the framebuffer really does get drawn to. */
   _cogl_framebuffer_dirty (framebuffer);
 
-  source = cogl_get_source ();
-
   /* 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. */
   for (i = 0; i < n_attributes; i++)
-    switch (attributes[i]->name_id)
+    switch (attributes[i]->name_state->name_id)
       {
       case COGL_ATTRIBUTE_NAME_ID_COLOR_ARRAY:
         if ((flags & COGL_DRAW_COLOR_ATTRIBUTE_IS_OPAQUE) == 0 &&
@@ -589,11 +663,13 @@ enable_gl_state (CoglDrawFlags flags,
 
   _cogl_pipeline_flush_gl_state (source, skip_gl_color, n_tex_coord_attribs);
 
-  _cogl_bitmask_clear_all (&ctx->temp_bitmask);
+  _cogl_bitmask_clear_all (&ctx->enable_builtin_attributes_tmp);
+  _cogl_bitmask_clear_all (&ctx->enable_texcoord_attributes_tmp);
+  _cogl_bitmask_clear_all (&ctx->enable_custom_attributes_tmp);
 
   /* Bind the attribute pointers. We need to do this after the
-     pipeline is flushed because on GLES2 that is the only point when
-     we can determine the attribute locations */
+   * pipeline is flushed because when using GLSL that is the only
+   * point when we can determine the attribute locations */
 
   for (i = 0; i < n_attributes; i++)
     {
@@ -601,156 +677,77 @@ enable_gl_state (CoglDrawFlags flags,
       CoglAttributeBuffer *attribute_buffer;
       CoglBuffer *buffer;
       guint8 *base;
-#ifdef HAVE_COGL_GLES2
-      int attrib_location;
-#endif
 
       attribute_buffer = cogl_attribute_get_buffer (attribute);
       buffer = COGL_BUFFER (attribute_buffer);
       base = _cogl_buffer_bind (buffer, COGL_BUFFER_BIND_TARGET_ATTRIBUTE_BUFFER);
 
-      switch (attribute->name_id)
+      switch (attribute->name_state->name_id)
         {
         case COGL_ATTRIBUTE_NAME_ID_COLOR_ARRAY:
 #ifdef HAVE_COGL_GLES2
           if (ctx->driver == COGL_DRIVER_GLES2)
-            {
-              attrib_location =
-                _cogl_pipeline_progend_glsl_get_color_attribute (source);
-              if (attrib_location != -1)
-                {
-                  GE( ctx,
-                      glVertexAttribPointer (attrib_location,
-                                             attribute->n_components,
-                                             attribute->type,
-                                             TRUE, /* normalize */
-                                             attribute->stride,
-                                             base + attribute->offset) );
-
-                  _cogl_bitmask_set (&ctx->temp_bitmask, attrib_location, TRUE);
-                }
-            }
+            setup_generic_attribute (ctx, source, attribute, base);
           else
 #endif
             {
-              enable_flags |= COGL_ENABLE_COLOR_ARRAY;
-              /* GE (ctx, glEnableClientState (GL_COLOR_ARRAY)); */
+              _cogl_bitmask_set (&ctx->enable_builtin_attributes_tmp,
+                                 COGL_ATTRIBUTE_NAME_ID_COLOR_ARRAY, TRUE);
               GE (ctx, glColorPointer (attribute->n_components,
                                        attribute->type,
                                        attribute->stride,
                                        base + attribute->offset));
-
             }
-
           break;
         case COGL_ATTRIBUTE_NAME_ID_NORMAL_ARRAY:
 #ifdef HAVE_COGL_GLES2
           if (ctx->driver == COGL_DRIVER_GLES2)
-            {
-              attrib_location =
-                _cogl_pipeline_progend_glsl_get_normal_attribute (source);
-              if (attrib_location != -1)
-                {
-                  GE( ctx,
-                      glVertexAttribPointer (attrib_location,
-                                             attribute->n_components,
-                                             attribute->type,
-                                             TRUE, /* normalize */
-                                             attribute->stride,
-                                             base + attribute->offset) );
-                  _cogl_bitmask_set (&ctx->temp_bitmask, attrib_location, TRUE);
-                }
-            }
+            setup_generic_attribute (ctx, source, attribute, base);
 #endif
-#if defined (HAVE_COGL_GL) || defined (HAVE_COGL_GLES)
-          if (ctx->driver != COGL_DRIVER_GLES2)
             {
-              /* FIXME: go through cogl cache to enable normal array */
-              GE (ctx, glEnableClientState (GL_NORMAL_ARRAY));
+              _cogl_bitmask_set (&ctx->enable_builtin_attributes_tmp,
+                                 COGL_ATTRIBUTE_NAME_ID_NORMAL_ARRAY, TRUE);
               GE (ctx, glNormalPointer (attribute->type,
                                         attribute->stride,
                                         base + attribute->offset));
-
             }
-#endif
           break;
         case COGL_ATTRIBUTE_NAME_ID_TEXTURE_COORD_ARRAY:
 #ifdef HAVE_COGL_GLES2
           if (ctx->driver == COGL_DRIVER_GLES2)
-            {
-              attrib_location =
-                _cogl_pipeline_progend_glsl_get_tex_coord_attribute
-                (source, attribute->texture_unit);
-              if (attrib_location != -1)
-                {
-                  GE( ctx,
-                      glVertexAttribPointer (attrib_location,
-                                             attribute->n_components,
-                                             attribute->type,
-                                             FALSE, /* normalize */
-                                             attribute->stride,
-                                             base + attribute->offset) );
-                  _cogl_bitmask_set (&ctx->temp_bitmask, attrib_location, TRUE);
-                }
-            }
+            setup_generic_attribute (ctx, source, attribute, base);
           else
 #endif
             {
-              GE (ctx, glClientActiveTexture (GL_TEXTURE0 +
-                                              attribute->texture_unit));
+              _cogl_bitmask_set (&ctx->enable_texcoord_attributes_tmp,
+                                 attribute->name_state->texture_unit, TRUE);
+              GE (ctx,
+                  glClientActiveTexture (GL_TEXTURE0 +
+                                         attribute->name_state->texture_unit));
               GE (ctx, glTexCoordPointer (attribute->n_components,
                                           attribute->type,
                                           attribute->stride,
                                           base + attribute->offset));
-              _cogl_bitmask_set (&ctx->temp_bitmask,
-                                 attribute->texture_unit, TRUE);
-
             }
           break;
         case COGL_ATTRIBUTE_NAME_ID_POSITION_ARRAY:
 #ifdef HAVE_COGL_GLES2
           if (ctx->driver == COGL_DRIVER_GLES2)
-            {
-              attrib_location =
-                _cogl_pipeline_progend_glsl_get_position_attribute (source);
-              if (attrib_location != -1)
-                {
-                  GE( ctx,
-                      glVertexAttribPointer (attrib_location,
-                                             attribute->n_components,
-                                             attribute->type,
-                                             FALSE, /* normalize */
-                                             attribute->stride,
-                                             base + attribute->offset) );
-                  _cogl_bitmask_set (&ctx->temp_bitmask, attrib_location, TRUE);
-                }
-            }
+            setup_generic_attribute (ctx, source, attribute, base);
           else
 #endif
             {
-              enable_flags |= COGL_ENABLE_VERTEX_ARRAY;
-              /* GE (ctx, glEnableClientState (GL_VERTEX_ARRAY)); */
+              _cogl_bitmask_set (&ctx->enable_builtin_attributes_tmp,
+                                 COGL_ATTRIBUTE_NAME_ID_POSITION_ARRAY, TRUE);
               GE (ctx, glVertexPointer (attribute->n_components,
                                         attribute->type,
                                         attribute->stride,
                                         base + attribute->offset));
-
             }
           break;
         case COGL_ATTRIBUTE_NAME_ID_CUSTOM_ARRAY:
           if (ctx->driver != COGL_DRIVER_GLES1)
-            {
-              /* FIXME: go through cogl cache to enable generic array. */
-              /* FIXME: this is going to end up just using the builtins
-                 on GLES 2 */
-              GE (ctx, glEnableVertexAttribArray (generic_index++));
-              GE (ctx, glVertexAttribPointer (generic_index,
-                                              attribute->n_components,
-                                              attribute->type,
-                                              attribute->normalized,
-                                              attribute->stride,
-                                              base + attribute->offset));
-            }
+            setup_generic_attribute (ctx, source, attribute, base);
           break;
         default:
           g_warning ("Unrecognised attribute type 0x%08x", attribute->type);
@@ -759,10 +756,7 @@ enable_gl_state (CoglDrawFlags flags,
       _cogl_buffer_unbind (buffer);
     }
 
-  /* Flush the state of the attribute arrays */
-  set_enabled_arrays (&ctx->arrays_enabled, &ctx->temp_bitmask);
-
-  _cogl_enable (enable_flags);
+  apply_attribute_enable_updates (ctx, source);
 
   return source;
 }
@@ -772,60 +766,14 @@ _cogl_attribute_disable_cached_arrays (void)
 {
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
-  _cogl_bitmask_clear_all (&ctx->temp_bitmask);
-  set_enabled_arrays (&ctx->arrays_enabled, &ctx->temp_bitmask);
-}
-
-/* FIXME: we shouldn't be disabling state after drawing we should
- * just disable the things not needed after enabling state. */
-static void
-disable_gl_state (CoglAttribute **attributes,
-                  int n_attributes,
-                  CoglPipeline *source)
-{
-  GLuint generic_index = 0;
-  int i;
+  _cogl_bitmask_clear_all (&ctx->enable_builtin_attributes_tmp);
+  _cogl_bitmask_clear_all (&ctx->enable_texcoord_attributes_tmp);
+  _cogl_bitmask_clear_all (&ctx->enable_custom_attributes_tmp);
 
-  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
-
-  if (G_UNLIKELY (source != cogl_get_source ()))
-    cogl_object_unref (source);
-
-  for (i = 0; i < n_attributes; i++)
-    {
-      CoglAttribute *attribute = attributes[i];
-
-      switch (attribute->name_id)
-        {
-        case COGL_ATTRIBUTE_NAME_ID_COLOR_ARRAY:
-          /* GE (ctx, glDisableClientState (GL_COLOR_ARRAY)); */
-          break;
-        case COGL_ATTRIBUTE_NAME_ID_NORMAL_ARRAY:
-          /* FIXME: go through cogl cache to enable normal array */
-#if defined(HAVE_COGL_GLES) || defined(HAVE_COGL_GL)
-          if (ctx->driver != COGL_DRIVER_GLES2)
-            GE (ctx, glDisableClientState (GL_NORMAL_ARRAY));
-#endif
-          break;
-        case COGL_ATTRIBUTE_NAME_ID_TEXTURE_COORD_ARRAY:
-          /* The enabled state of the texture coord arrays is
-             cached in ctx->enabled_texcoord_arrays so we don't
-             need to do anything here. The array will be disabled
-             by the next drawing primitive if it is not
-             required */
-          break;
-        case COGL_ATTRIBUTE_NAME_ID_POSITION_ARRAY:
-          /* GE (ctx, glDisableClientState (GL_VERTEX_ARRAY)); */
-          break;
-        case COGL_ATTRIBUTE_NAME_ID_CUSTOM_ARRAY:
-          if (ctx->driver != COGL_DRIVER_GLES1)
-            /* FIXME: go through cogl cache to enable generic array */
-            GE (ctx, glDisableVertexAttribArray (generic_index++));
-          break;
-        default:
-          g_warning ("Unrecognised attribute type 0x%08x", attribute->type);
-        }
-    }
+  /* XXX: we can pass a NULL source pipeline here because we know a
+   * source pipeline only needs to be referenced when enabling
+   * attributes. */
+  apply_attribute_enable_updates (ctx, NULL);
 }
 
 #ifdef COGL_ENABLE_DEBUG
@@ -1020,7 +968,8 @@ draw_wireframe (CoglVerticesMode mode,
 
   for (i = 0; i < n_attributes; i++)
     {
-      if (strcmp (attributes[i]->name, "cogl_position_in") == 0)
+      if (attributes[i]->name_state->name_id ==
+          COGL_ATTRIBUTE_NAME_ID_POSITION_ARRAY)
         {
           position = attributes[i];
           break;
@@ -1073,37 +1022,6 @@ draw_wireframe (CoglVerticesMode mode,
 }
 #endif
 
-static void
-flush_state (CoglDrawFlags flags,
-             ValidateLayerState *state)
-{
-  if (!(flags & COGL_DRAW_SKIP_JOURNAL_FLUSH))
-    {
-      CoglFramebuffer *framebuffer = cogl_get_draw_framebuffer ();
-      _cogl_journal_flush (framebuffer->journal, framebuffer);
-    }
-
-  state->unit = 0;
-  state->options.flags = 0;
-  state->fallback_layers = 0;
-
-  if (!(flags & COGL_DRAW_SKIP_PIPELINE_VALIDATION))
-    cogl_pipeline_foreach_layer (cogl_get_source (),
-                                 validate_layer_cb,
-                                 state);
-
-  /* NB: _cogl_framebuffer_flush_state may disrupt various state (such
-   * as the pipeline state) when flushing the clip stack, so should
-   * always be done first when preparing to draw. We need to do this
-   * before setting up the array pointers because setting up the clip
-   * stack can cause some drawing which would change the array
-   * pointers. */
-  if (!(flags & COGL_DRAW_SKIP_FRAMEBUFFER_FLUSH))
-    _cogl_framebuffer_flush_state (cogl_get_draw_framebuffer (),
-                                   _cogl_get_read_framebuffer (),
-                                   COGL_FRAMEBUFFER_STATE_ALL);
-}
-
 /* This can be called directly by the CoglJournal to draw attributes
  * skipping the implicit journal flush, the framebuffer flush and
  * pipeline validation. */
@@ -1120,15 +1038,12 @@ _cogl_draw_attributes (CoglVerticesMode mode,
 
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
-  flush_state (flags, &state);
-
-  source = enable_gl_state (flags, attributes, n_attributes, &state);
+  source = flush_state (flags, attributes, n_attributes, &state);
 
   GE (ctx, glDrawArrays ((GLenum)mode, first_vertex, n_vertices));
 
-  /* FIXME: we shouldn't be disabling state after drawing we should
-   * just disable the things not needed after enabling state. */
-  disable_gl_state (attributes, n_attributes, source);
+  if (G_UNLIKELY (source != cogl_get_source ()))
+    cogl_object_unref (source);
 
 #ifdef COGL_ENABLE_DEBUG
   if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_WIREFRAME)))
@@ -1212,9 +1127,7 @@ _cogl_draw_indexed_attributes (CoglVerticesMode mode,
 
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
-  flush_state (flags, &state);
-
-  source = enable_gl_state (flags, attributes, n_attributes, &state);
+  source = flush_state (flags, attributes, n_attributes, &state);
 
   buffer = COGL_BUFFER (cogl_indices_get_buffer (indices));
   base = _cogl_buffer_bind (buffer, COGL_BUFFER_BIND_TARGET_INDEX_BUFFER);
@@ -1241,9 +1154,8 @@ _cogl_draw_indexed_attributes (CoglVerticesMode mode,
 
   _cogl_buffer_unbind (buffer);
 
-  /* FIXME: we shouldn't be disabling state after drawing we should
-   * just disable the things not needed after enabling state. */
-  disable_gl_state (attributes, n_attributes, source);
+  if (G_UNLIKELY (source != cogl_get_source ()))
+    cogl_object_unref (source);
 
 #ifdef COGL_ENABLE_DEBUG
   if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_WIREFRAME)))
diff --git a/cogl/cogl-context-private.h b/cogl/cogl-context-private.h
index 9f2e178..f8b9cf0 100644
--- a/cogl/cogl-context-private.h
+++ b/cogl/cogl-context-private.h
@@ -72,8 +72,21 @@ struct _CoglContext
   CoglHandle        default_layer_n;
   CoglHandle        dummy_layer_dependant;
 
-  /* Enable cache */
-  unsigned long     enable_flags;
+  GHashTable *attribute_name_states_hash;
+  GArray *attribute_name_index_map;
+  int n_attribute_names;
+
+  CoglBitmask       enabled_builtin_attributes;
+  CoglBitmask       enabled_texcoord_attributes;
+  CoglBitmask       enabled_custom_attributes;
+
+  /* These are temporary bitmasks that are used when disabling
+   * builtin,texcoord and custom attribute arrays. They are here just
+   * to avoid allocating new ones each time */
+  CoglBitmask       enable_builtin_attributes_tmp;
+  CoglBitmask       enable_texcoord_attributes_tmp;
+  CoglBitmask       enable_custom_attributes_tmp;
+  CoglBitmask       changed_bits_tmp;
 
   gboolean          legacy_backface_culling_enabled;
 
@@ -129,16 +142,6 @@ struct _CoglContext
   gboolean          current_pipeline_skip_gl_color;
   unsigned long     current_pipeline_age;
 
-  /* Bitmask of attributes enabled. On GLES2 these are the vertex
-     attribute numbers and on regular GL these are only used for the
-     texture coordinate arrays */
-  CoglBitmask       arrays_enabled;
-  /* These are temporary bitmasks that are used when disabling
-     texcoord arrays. They are here just to avoid allocating new ones
-     each time */
-  CoglBitmask       arrays_to_change;
-  CoglBitmask       temp_bitmask;
-
   gboolean          gl_blend_enable_cache;
 
   gboolean              depth_test_enabled_cache;
diff --git a/cogl/cogl-context.c b/cogl/cogl-context.c
index f474538..08b527e 100644
--- a/cogl/cogl-context.c
+++ b/cogl/cogl-context.c
@@ -43,6 +43,7 @@
 #include "cogl-framebuffer-private.h"
 #include "cogl-onscreen-private.h"
 #include "cogl2-path.h"
+#include "cogl-attribute-private.h"
 
 #include <string.h>
 
@@ -129,7 +130,6 @@ cogl_context_new (CoglDisplay *display,
 {
   CoglContext *context;
   GLubyte default_texture_data[] = { 0xff, 0xff, 0xff, 0x0 };
-  unsigned long enable_flags = 0;
   const CoglWinsysVtable *winsys;
   int i;
 
@@ -221,6 +221,16 @@ cogl_context_new (CoglDisplay *display,
       g_assert_not_reached ();
     }
 
+  context->attribute_name_states_hash =
+    g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+  context->attribute_name_index_map = NULL;
+  context->n_attribute_names = 0;
+
+  /* The "cogl_color_in" attribute needs a deterministic name_index
+   * so we make sure it's the first attribute name we register */
+  _cogl_attribute_register_attribute_name (context, "cogl_color_in");
+
+
   context->uniform_names =
     g_ptr_array_new_with_free_func ((GDestroyNotify) g_free);
   context->uniform_name_hash = g_hash_table_new (g_str_hash, g_str_equal);
@@ -234,7 +244,6 @@ cogl_context_new (CoglDisplay *display,
   _cogl_pipeline_init_state_hash_functions ();
   _cogl_pipeline_init_layer_state_hash_functions ();
 
-  context->enable_flags = 0;
   context->current_clip_stack_valid = FALSE;
   context->current_clip_stack = NULL;
 
@@ -284,9 +293,13 @@ cogl_context_new (CoglDisplay *display,
   context->current_pipeline_changes_since_flush = 0;
   context->current_pipeline_skip_gl_color = FALSE;
 
-  _cogl_bitmask_init (&context->arrays_enabled);
-  _cogl_bitmask_init (&context->temp_bitmask);
-  _cogl_bitmask_init (&context->arrays_to_change);
+  _cogl_bitmask_init (&context->enabled_builtin_attributes);
+  _cogl_bitmask_init (&context->enable_builtin_attributes_tmp);
+  _cogl_bitmask_init (&context->enabled_texcoord_attributes);
+  _cogl_bitmask_init (&context->enable_texcoord_attributes_tmp);
+  _cogl_bitmask_init (&context->enabled_custom_attributes);
+  _cogl_bitmask_init (&context->enable_custom_attributes_tmp);
+  _cogl_bitmask_init (&context->changed_bits_tmp);
 
   context->max_texture_units = -1;
   context->max_activateable_texture_units = -1;
@@ -385,7 +398,6 @@ cogl_context_new (CoglDisplay *display,
 
   cogl_push_source (context->opaque_color_pipeline);
   _cogl_pipeline_flush_gl_state (context->opaque_color_pipeline, FALSE, 0);
-  _cogl_enable (enable_flags);
 
   context->atlases = NULL;
   g_hook_list_init (&context->atlas_reorganize_callbacks, sizeof (GHook));
@@ -468,9 +480,13 @@ _cogl_context_free (CoglContext *context)
   g_slist_free (context->atlases);
   g_hook_list_clear (&context->atlas_reorganize_callbacks);
 
-  _cogl_bitmask_destroy (&context->arrays_enabled);
-  _cogl_bitmask_destroy (&context->temp_bitmask);
-  _cogl_bitmask_destroy (&context->arrays_to_change);
+  _cogl_bitmask_destroy (&context->enabled_builtin_attributes);
+  _cogl_bitmask_destroy (&context->enable_builtin_attributes_tmp);
+  _cogl_bitmask_destroy (&context->enabled_texcoord_attributes);
+  _cogl_bitmask_destroy (&context->enable_texcoord_attributes_tmp);
+  _cogl_bitmask_destroy (&context->enabled_custom_attributes);
+  _cogl_bitmask_destroy (&context->enable_custom_attributes_tmp);
+  _cogl_bitmask_destroy (&context->changed_bits_tmp);
 
   g_slist_free (context->texture_types);
   g_slist_free (context->buffer_types);
@@ -490,6 +506,9 @@ _cogl_context_free (CoglContext *context)
   g_ptr_array_free (context->uniform_names, TRUE);
   g_hash_table_destroy (context->uniform_name_hash);
 
+  g_hash_table_destroy (context->attribute_name_states_hash);
+  g_array_free (context->attribute_name_index_map, TRUE);
+
   g_byte_array_free (context->buffer_map_fallback_array, TRUE);
 
   cogl_object_unref (context->display);
diff --git a/cogl/cogl-pipeline-opengl.c b/cogl/cogl-pipeline-opengl.c
index 92d75ea..aaf5ea8 100644
--- a/cogl/cogl-pipeline-opengl.c
+++ b/cogl/cogl-pipeline-opengl.c
@@ -1372,8 +1372,10 @@ done:
       int attribute;
       CoglPipeline *authority =
         _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_COLOR);
+      int name_index = COGL_ATTRIBUTE_COLOR_NAME_INDEX;
 
-      attribute = _cogl_pipeline_progend_glsl_get_color_attribute (pipeline);
+      attribute =
+        _cogl_pipeline_progend_glsl_get_attrib_location (pipeline, name_index);
       if (attribute != -1)
         GE (ctx,
             glVertexAttrib4f (attribute,
diff --git a/cogl/cogl-pipeline-progend-glsl-private.h b/cogl/cogl-pipeline-progend-glsl-private.h
index c100269..a080204 100644
--- a/cogl/cogl-pipeline-progend-glsl-private.h
+++ b/cogl/cogl-pipeline-progend-glsl-private.h
@@ -33,22 +33,9 @@
 
 extern const CoglPipelineProgend _cogl_pipeline_glsl_progend;
 
-#ifdef HAVE_COGL_GLES2
-
-int
-_cogl_pipeline_progend_glsl_get_position_attribute (CoglPipeline *pipeline);
-
-int
-_cogl_pipeline_progend_glsl_get_color_attribute (CoglPipeline *pipeline);
-
-int
-_cogl_pipeline_progend_glsl_get_normal_attribute (CoglPipeline *pipeline);
-
 int
-_cogl_pipeline_progend_glsl_get_tex_coord_attribute (CoglPipeline *pipeline,
-                                                     int unit);
-
-#endif /* HAVE_COGL_GLES2 */
+_cogl_pipeline_progend_glsl_get_attrib_location (CoglPipeline *pipeline,
+                                                 int name_index);
 
 #endif /* __COGL_PIPELINE_PROGEND_GLSL_PRIVATE_H */
 
diff --git a/cogl/cogl-pipeline-progend-glsl.c b/cogl/cogl-pipeline-progend-glsl.c
index 703cbea..bc96cb8 100644
--- a/cogl/cogl-pipeline-progend-glsl.c
+++ b/cogl/cogl-pipeline-progend-glsl.c
@@ -47,6 +47,7 @@
 #include "cogl-pipeline-vertend-glsl-private.h"
 #include "cogl-pipeline-cache.h"
 #include "cogl-pipeline-state-private.h"
+#include "cogl-attribute-private.h"
 
 #ifdef HAVE_COGL_GLES2
 
@@ -116,18 +117,6 @@ typedef struct
   unsigned long dirty_builtin_uniforms;
   GLint builtin_uniform_locations[G_N_ELEMENTS (builtin_uniforms)];
 
-  /* Under GLES2 we can't use the builtin functions to set attribute
-     pointers such as the vertex position. Instead the vertex
-     attribute code needs to query the attribute numbers from the
-     progend backend */
-  int position_attribute_location;
-  int color_attribute_location;
-  int normal_attribute_location;
-  int tex_coord0_attribute_location;
-  /* We only allocate this array if more than one tex coord attribute
-     is requested because most pipelines will only use one layer */
-  GArray *tex_coord_attribute_locations;
-
   GLint modelview_uniform;
   GLint projection_uniform;
   GLint mvp_uniform;
@@ -148,6 +137,9 @@ typedef struct
      uniform is actually set */
   GArray *uniform_locations;
 
+  /* Array of attribute locations. */
+  GArray *attribute_locations;
+
   UnitState *unit_state;
 } CoglPipelineProgramState;
 
@@ -161,8 +153,6 @@ get_program_state (CoglPipeline *pipeline)
 
 #define UNIFORM_LOCATION_UNKNOWN -2
 
-#ifdef HAVE_COGL_GLES2
-
 #define ATTRIBUTE_LOCATION_UNKNOWN -2
 
 /* Under GLES2 the vertex attribute API needs to query the attribute
@@ -172,125 +162,66 @@ get_program_state (CoglPipeline *pipeline)
    cache. This should always be called after the pipeline is flushed
    so they can assert that the gl program is valid */
 
-int
-_cogl_pipeline_progend_glsl_get_position_attribute (CoglPipeline *pipeline)
-{
-  CoglPipelineProgramState *program_state = get_program_state (pipeline);
-
-  _COGL_GET_CONTEXT (ctx, -1);
-
-  _COGL_RETURN_VAL_IF_FAIL (program_state != NULL, -1);
-  _COGL_RETURN_VAL_IF_FAIL (program_state->program != 0, -1);
-
-  if (program_state->position_attribute_location == ATTRIBUTE_LOCATION_UNKNOWN)
-    GE_RET( program_state->position_attribute_location,
-            ctx, glGetAttribLocation (program_state->program,
-                                      "cogl_position_in") );
-
-  return program_state->position_attribute_location;
-}
-
-int
-_cogl_pipeline_progend_glsl_get_color_attribute (CoglPipeline *pipeline)
-{
-  CoglPipelineProgramState *program_state = get_program_state (pipeline);
-
-  _COGL_GET_CONTEXT (ctx, -1);
-
-  _COGL_RETURN_VAL_IF_FAIL (program_state != NULL, -1);
-  _COGL_RETURN_VAL_IF_FAIL (program_state->program != 0, -1);
-
-  if (program_state->color_attribute_location == ATTRIBUTE_LOCATION_UNKNOWN)
-    GE_RET( program_state->color_attribute_location,
-            ctx, glGetAttribLocation (program_state->program,
-                                      "cogl_color_in") );
-
-  return program_state->color_attribute_location;
-}
+/* All attributes names get internally mapped to a global set of
+ * sequential indices when they are setup which we need to need to
+ * then be able to map to a GL attribute location once we have
+ * a linked GLSL program */
 
 int
-_cogl_pipeline_progend_glsl_get_normal_attribute (CoglPipeline *pipeline)
+_cogl_pipeline_progend_glsl_get_attrib_location (CoglPipeline *pipeline,
+                                                 int name_index)
 {
   CoglPipelineProgramState *program_state = get_program_state (pipeline);
+  int *locations;
 
   _COGL_GET_CONTEXT (ctx, -1);
 
   _COGL_RETURN_VAL_IF_FAIL (program_state != NULL, -1);
   _COGL_RETURN_VAL_IF_FAIL (program_state->program != 0, -1);
 
-  if (program_state->normal_attribute_location == ATTRIBUTE_LOCATION_UNKNOWN)
-    GE_RET( program_state->normal_attribute_location,
-            ctx, glGetAttribLocation (program_state->program,
-                                      "cogl_normal_in") );
+  if (G_UNLIKELY (program_state->attribute_locations == NULL))
+    program_state->attribute_locations =
+      g_array_new (FALSE, FALSE, sizeof (int));
 
-  return program_state->normal_attribute_location;
-}
-
-int
-_cogl_pipeline_progend_glsl_get_tex_coord_attribute (CoglPipeline *pipeline,
-                                                     int unit)
-{
-  CoglPipelineProgramState *program_state = get_program_state (pipeline);
-
-  _COGL_GET_CONTEXT (ctx, -1);
-
-  _COGL_RETURN_VAL_IF_FAIL (program_state != NULL, -1);
-  _COGL_RETURN_VAL_IF_FAIL (program_state->program != 0, -1);
-
-  if (unit == 0)
+  if (G_UNLIKELY (program_state->attribute_locations->len <= name_index))
     {
-      if (program_state->tex_coord0_attribute_location ==
-          ATTRIBUTE_LOCATION_UNKNOWN)
-        GE_RET( program_state->tex_coord0_attribute_location,
-                ctx, glGetAttribLocation (program_state->program,
-                                          "cogl_tex_coord0_in") );
-
-      return program_state->tex_coord0_attribute_location;
+      int i = program_state->attribute_locations->len;
+      g_array_set_size (program_state->attribute_locations, name_index + 1);
+      for (; i < program_state->attribute_locations->len; i++)
+        g_array_index (program_state->attribute_locations, int, i)
+          = ATTRIBUTE_LOCATION_UNKNOWN;
     }
-  else
-    {
-      char *name = g_strdup_printf ("cogl_tex_coord%i_in", unit);
-      int *locations;
 
-      if (program_state->tex_coord_attribute_locations == NULL)
-        program_state->tex_coord_attribute_locations =
-          g_array_new (FALSE, FALSE, sizeof (int));
-      if (program_state->tex_coord_attribute_locations->len <= unit - 1)
-        {
-          int i = program_state->tex_coord_attribute_locations->len;
-          g_array_set_size (program_state->tex_coord_attribute_locations, unit);
-          for (; i < unit; i++)
-            g_array_index (program_state->tex_coord_attribute_locations, int, i)
-              = ATTRIBUTE_LOCATION_UNKNOWN;
-        }
+  locations = &g_array_index (program_state->attribute_locations, int, 0);
 
-      locations = &g_array_index (program_state->tex_coord_attribute_locations,
-                                  int, 0);
-
-      if (locations[unit - 1] == ATTRIBUTE_LOCATION_UNKNOWN)
-        GE_RET( locations[unit - 1],
-                ctx, glGetAttribLocation (program_state->program, name) );
+  if (locations[name_index] == ATTRIBUTE_LOCATION_UNKNOWN)
+    {
+      CoglAttributeNameState *name_state =
+        g_array_index (ctx->attribute_name_index_map,
+                       CoglAttributeNameState *, name_index);
 
-      g_free (name);
+      _COGL_RETURN_VAL_IF_FAIL (name_state != NULL, 0);
 
-      return locations[unit - 1];
+      GE_RET( locations[name_index],
+              ctx, glGetAttribLocation (program_state->program,
+                                        name_state->name) );
     }
+
+  return locations[name_index];
 }
 
 static void
 clear_attribute_cache (CoglPipelineProgramState *program_state)
 {
-  program_state->position_attribute_location = ATTRIBUTE_LOCATION_UNKNOWN;
-  program_state->color_attribute_location = ATTRIBUTE_LOCATION_UNKNOWN;
-  program_state->normal_attribute_location = ATTRIBUTE_LOCATION_UNKNOWN;
-  program_state->tex_coord0_attribute_location = ATTRIBUTE_LOCATION_UNKNOWN;
-  if (program_state->tex_coord_attribute_locations)
+  if (program_state->attribute_locations)
     {
-      g_array_free (program_state->tex_coord_attribute_locations, TRUE);
-      program_state->tex_coord_attribute_locations = NULL;
+      g_array_free (program_state->attribute_locations, TRUE);
+      program_state->attribute_locations = NULL;
     }
 }
 
+#ifdef HAVE_COGL_GLES2
+
 static void
 clear_flushed_matrix_stacks (CoglPipelineProgramState *program_state)
 {
@@ -320,8 +251,8 @@ program_state_new (int n_layers)
   program_state->n_tex_coord_attribs = 0;
   program_state->unit_state = g_new (UnitState, n_layers);
   program_state->uniform_locations = NULL;
+  program_state->attribute_locations = NULL;
 #ifdef HAVE_COGL_GLES2
-  program_state->tex_coord_attribute_locations = NULL;
   program_state->flushed_modelview_stack = NULL;
   program_state->flushed_modelview_is_identity = FALSE;
   program_state->flushed_projection_stack = NULL;
@@ -347,12 +278,11 @@ destroy_program_state (void *user_data,
 
   if (--program_state->ref_count == 0)
     {
+      clear_attribute_cache (program_state);
+
 #ifdef HAVE_COGL_GLES2
       if (ctx->driver == COGL_DRIVER_GLES2)
-        {
-          clear_attribute_cache (program_state);
-          clear_flushed_matrix_stacks (program_state);
-        }
+        clear_flushed_matrix_stacks (program_state);
 #endif
 
       if (program_state->program)
@@ -858,9 +788,12 @@ _cogl_pipeline_progend_glsl_end (CoglPipeline *pipeline,
   state.program_state = program_state;
 
   if (program_changed)
-    cogl_pipeline_foreach_layer (pipeline,
-                                 get_uniform_cb,
-                                 &state);
+    {
+      cogl_pipeline_foreach_layer (pipeline,
+                                   get_uniform_cb,
+                                   &state);
+      clear_attribute_cache (program_state);
+    }
 
   state.unit = 0;
   state.update_all = (program_changed ||
@@ -877,7 +810,6 @@ _cogl_pipeline_progend_glsl_end (CoglPipeline *pipeline,
         {
           int i;
 
-          clear_attribute_cache (program_state);
           clear_flushed_matrix_stacks (program_state);
 
           for (i = 0; i < G_N_ELEMENTS (builtin_uniforms); i++)
diff --git a/cogl/cogl.c b/cogl/cogl.c
index 46fd45e..cba3de7 100644
--- a/cogl/cogl.c
+++ b/cogl/cogl.c
@@ -141,69 +141,6 @@ cogl_clear (const CoglColor *color, unsigned long buffers)
   cogl_framebuffer_clear (cogl_get_draw_framebuffer (), buffers, color);
 }
 
-#if defined (HAVE_COGL_GL) || defined (HAVE_COGL_GLES)
-
-static gboolean
-toggle_client_flag (CoglContext *ctx,
-		    unsigned long new_flags,
-		    unsigned long flag,
-		    GLenum gl_flag)
-{
-  _COGL_RETURN_VAL_IF_FAIL (ctx->driver != COGL_DRIVER_GLES2, FALSE);
-
-  /* Toggles and caches a single client-side enable flag
-   * on or off by comparing to current state
-   */
-  if (new_flags & flag)
-    {
-      if (!(ctx->enable_flags & flag))
-	{
-	  GE( ctx, glEnableClientState (gl_flag) );
-	  ctx->enable_flags |= flag;
-	  return TRUE;
-	}
-    }
-  else if (ctx->enable_flags & flag)
-    {
-      GE( ctx, glDisableClientState (gl_flag) );
-      ctx->enable_flags &= ~flag;
-    }
-
-  return FALSE;
-}
-
-#endif
-
-void
-_cogl_enable (unsigned long flags)
-{
-  /* This function essentially caches glEnable state() in the
-   * hope of lessening number GL traffic.
-  */
-  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
-
-#if defined (HAVE_COGL_GL) || defined (HAVE_COGL_GLES)
-  if (ctx->driver != COGL_DRIVER_GLES2)
-    {
-      toggle_client_flag (ctx, flags,
-                          COGL_ENABLE_VERTEX_ARRAY,
-                          GL_VERTEX_ARRAY);
-
-      toggle_client_flag (ctx, flags,
-                          COGL_ENABLE_COLOR_ARRAY,
-                          GL_COLOR_ARRAY);
-    }
-#endif
-}
-
-unsigned long
-_cogl_get_enable (void)
-{
-  _COGL_GET_CONTEXT (ctx, 0);
-
-  return ctx->enable_flags;
-}
-
 /* XXX: This API has been deprecated */
 void
 cogl_set_depth_test_enabled (gboolean setting)
@@ -645,7 +582,6 @@ cogl_read_pixels (int x,
 void
 cogl_begin_gl (void)
 {
-  unsigned long enable_flags = 0;
   CoglPipeline *pipeline;
 
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
@@ -697,8 +633,6 @@ cogl_begin_gl (void)
                                  FALSE,
                                  cogl_pipeline_get_n_layers (pipeline));
 
-  _cogl_enable (enable_flags);
-
   /* Disable any cached vertex arrays */
   _cogl_attribute_disable_cached_arrays ();
 }



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