[cogl] pipeline: split out all layer state apis



commit db6c452aaa7920c418ba90418fd047d3c53804d0
Author: Robert Bragg <robert linux intel com>
Date:   Thu Sep 8 00:42:22 2011 +0100

    pipeline: split out all layer state apis
    
    As part of an on-going effort to get cogl-pipeline.c into a more
    maintainable state this splits out all the apis relating just to
    layer state. This just leaves code relating to the core CoglPipeline
    and CoglPipelineLayer design left in cogl-pipeline.c.
    
    This splits out around 2k more lines from cogl-pipeline.c although we
    are still left with nearly 4k lines so we still have some way to go!
    
    Reviewed-by: Neil Roberts <neil linux intel com>

 cogl/Makefile.am                                |    3 +
 cogl/cogl-pipeline-layer-state-private.h        |  115 ++
 cogl/cogl-pipeline-layer-state.c                | 1664 +++++++++++++++++++
 cogl/cogl-pipeline-layer-state.h                |  503 ++++++
 cogl/cogl-pipeline-private.h                    |   31 +
 cogl/cogl-pipeline.c                            | 1970 ++---------------------
 cogl/cogl-pipeline.h                            |  433 -----
 cogl/cogl.h                                     |    1 +
 doc/reference/cogl-2.0-experimental/Makefile.am |    1 +
 doc/reference/cogl/Makefile.am                  |    1 +
 10 files changed, 2487 insertions(+), 2235 deletions(-)
---
diff --git a/cogl/Makefile.am b/cogl/Makefile.am
index be9fd16..c0c048b 100644
--- a/cogl/Makefile.am
+++ b/cogl/Makefile.am
@@ -100,6 +100,7 @@ cogl_experimental_h = \
 	$(srcdir)/cogl-context.h 		\
 	$(srcdir)/cogl-pipeline.h 		\
 	$(srcdir)/cogl-pipeline-state.h 	\
+	$(srcdir)/cogl-pipeline-layer-state.h 	\
 	$(srcdir)/cogl2-path.h 			\
 	$(srcdir)/cogl2-clip-state.h		\
 	$(srcdir)/cogl2-experimental.h		\
@@ -246,6 +247,8 @@ cogl_sources_c = \
 	$(srcdir)/cogl-pipeline.c			\
 	$(srcdir)/cogl-pipeline-private.h		\
 	$(srcdir)/cogl-pipeline-state.c			\
+	$(srcdir)/cogl-pipeline-layer-state-private.h	\
+	$(srcdir)/cogl-pipeline-layer-state.c		\
 	$(srcdir)/cogl-pipeline-state-private.h		\
 	$(srcdir)/cogl-pipeline-opengl.c		\
 	$(srcdir)/cogl-pipeline-opengl-private.h	\
diff --git a/cogl/cogl-pipeline-layer-state-private.h b/cogl/cogl-pipeline-layer-state-private.h
new file mode 100644
index 0000000..5c0aa64
--- /dev/null
+++ b/cogl/cogl-pipeline-layer-state-private.h
@@ -0,0 +1,115 @@
+/*
+ * Cogl
+ *
+ * An object oriented GL/GLES Abstraction/Utility Layer
+ *
+ * Copyright (C) 2011 Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ *
+ *
+ * Authors:
+ *   Robert Bragg <robert linux intel com>
+ */
+
+#ifndef __COGL_PIPELINE_LAYER_STATE_PRIVATE_H
+#define __COGL_PIPELINE_LAYER_STATE_PRIVATE_H
+
+CoglPipelineLayer *
+_cogl_pipeline_set_layer_unit (CoglPipeline *required_owner,
+                               CoglPipelineLayer *layer,
+                               int unit_index);
+
+gboolean
+_cogl_pipeline_layer_texture_target_equal (CoglPipelineLayer *authority0,
+                                           CoglPipelineLayer *authority1,
+                                           CoglPipelineEvalFlags flags);
+
+gboolean
+_cogl_pipeline_layer_texture_data_equal (CoglPipelineLayer *authority0,
+                                         CoglPipelineLayer *authority1,
+                                         CoglPipelineEvalFlags flags);
+
+gboolean
+_cogl_pipeline_layer_combine_state_equal (CoglPipelineLayer *authority0,
+                                          CoglPipelineLayer *authority1);
+
+gboolean
+_cogl_pipeline_layer_combine_constant_equal (CoglPipelineLayer *authority0,
+                                             CoglPipelineLayer *authority1);
+
+gboolean
+_cogl_pipeline_layer_filters_equal (CoglPipelineLayer *authority0,
+                                    CoglPipelineLayer *authority1);
+
+gboolean
+_cogl_pipeline_layer_wrap_modes_equal (CoglPipelineLayer *authority0,
+                                       CoglPipelineLayer *authority1);
+
+gboolean
+_cogl_pipeline_layer_user_matrix_equal (CoglPipelineLayer *authority0,
+                                        CoglPipelineLayer *authority1);
+
+gboolean
+_cogl_pipeline_layer_point_sprite_coords_equal (CoglPipelineLayer *authority0,
+                                                CoglPipelineLayer *authority1);
+
+void
+_cogl_pipeline_layer_hash_unit_state (CoglPipelineLayer *authority,
+                                      CoglPipelineLayer **authorities,
+                                      CoglPipelineHashState *state);
+
+void
+_cogl_pipeline_layer_hash_texture_target_state (CoglPipelineLayer *authority,
+                                                CoglPipelineLayer **authorities,
+                                                CoglPipelineHashState *state);
+
+void
+_cogl_pipeline_layer_hash_texture_data_state (CoglPipelineLayer *authority,
+                                              CoglPipelineLayer **authorities,
+                                              CoglPipelineHashState *state);
+
+void
+_cogl_pipeline_layer_hash_filters_state (CoglPipelineLayer *authority,
+                                         CoglPipelineLayer **authorities,
+                                         CoglPipelineHashState *state);
+
+void
+_cogl_pipeline_layer_hash_wrap_modes_state (CoglPipelineLayer *authority,
+                                            CoglPipelineLayer **authorities,
+                                            CoglPipelineHashState *state);
+
+void
+_cogl_pipeline_layer_hash_combine_state (CoglPipelineLayer *authority,
+                                         CoglPipelineLayer **authorities,
+                                         CoglPipelineHashState *state);
+
+void
+_cogl_pipeline_layer_hash_combine_constant_state (CoglPipelineLayer *authority,
+                                                  CoglPipelineLayer **authorities,
+                                                  CoglPipelineHashState *state);
+
+void
+_cogl_pipeline_layer_hash_user_matrix_state (CoglPipelineLayer *authority,
+                                             CoglPipelineLayer **authorities,
+                                             CoglPipelineHashState *state);
+
+void
+_cogl_pipeline_layer_hash_point_sprite_state (CoglPipelineLayer *authority,
+                                              CoglPipelineLayer **authorities,
+                                              CoglPipelineHashState *state);
+
+#endif /* __COGL_PIPELINE_LAYER_STATE_PRIVATE_H */
diff --git a/cogl/cogl-pipeline-layer-state.c b/cogl/cogl-pipeline-layer-state.c
new file mode 100644
index 0000000..ae89782
--- /dev/null
+++ b/cogl/cogl-pipeline-layer-state.c
@@ -0,0 +1,1664 @@
+/*
+ * Cogl
+ *
+ * An object oriented GL/GLES Abstraction/Utility Layer
+ *
+ * Copyright (C) 2008,2009,2010 Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ *
+ *
+ * Authors:
+ *   Robert Bragg <robert linux intel com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "cogl-pipeline-private.h"
+#include "cogl-blend-string.h"
+#include "cogl-util.h"
+#include "cogl-matrix.h"
+
+#include "string.h"
+#if 0
+#include "cogl-context-private.h"
+#include "cogl-color-private.h"
+
+#endif
+
+/*
+ * XXX: consider special casing layer->unit_index so it's not a sparse
+ * property so instead we can assume it's valid for all layer
+ * instances.
+ * - We would need to initialize ->unit_index in
+ *   _cogl_pipeline_layer_copy ().
+ *
+ * XXX: If you use this API you should consider that the given layer
+ * might not be writeable and so a new derived layer will be allocated
+ * and modified instead. The layer modified will be returned so you
+ * can identify when this happens.
+ */
+CoglPipelineLayer *
+_cogl_pipeline_set_layer_unit (CoglPipeline *required_owner,
+                               CoglPipelineLayer *layer,
+                               int unit_index)
+{
+  CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_UNIT;
+  CoglPipelineLayer *authority =
+    _cogl_pipeline_layer_get_authority (layer, change);
+  CoglPipelineLayer *new;
+
+  if (authority->unit_index == unit_index)
+    return layer;
+
+  new =
+    _cogl_pipeline_layer_pre_change_notify (required_owner,
+                                            layer,
+                                            change);
+  if (new != layer)
+    layer = new;
+  else
+    {
+      /* If the layer we found is currently the authority on the state
+       * we are changing see if we can revert to one of our ancestors
+       * being the authority. */
+      if (layer == authority &&
+          _cogl_pipeline_layer_get_parent (authority) != NULL)
+        {
+          CoglPipelineLayer *parent =
+            _cogl_pipeline_layer_get_parent (authority);
+          CoglPipelineLayer *old_authority =
+            _cogl_pipeline_layer_get_authority (parent, change);
+
+          if (old_authority->unit_index == unit_index)
+            {
+              layer->differences &= ~change;
+              return layer;
+            }
+        }
+    }
+
+  layer->unit_index = unit_index;
+
+  /* If we weren't previously the authority on this state then we need
+   * to extended our differences mask and so it's possible that some
+   * of our ancestry will now become redundant, so we aim to reparent
+   * ourselves if that's true... */
+  if (layer != authority)
+    {
+      layer->differences |= change;
+      _cogl_pipeline_layer_prune_redundant_ancestry (layer);
+    }
+
+  return layer;
+}
+
+CoglHandle
+_cogl_pipeline_layer_get_texture_real (CoglPipelineLayer *layer)
+{
+  CoglPipelineLayer *authority =
+    _cogl_pipeline_layer_get_authority (layer,
+                                        COGL_PIPELINE_LAYER_STATE_TEXTURE_DATA);
+
+  return authority->texture;
+}
+
+CoglHandle
+_cogl_pipeline_get_layer_texture (CoglPipeline *pipeline,
+                                  int layer_index)
+{
+  CoglPipelineLayer *layer =
+    _cogl_pipeline_get_layer (pipeline, layer_index);
+  return _cogl_pipeline_layer_get_texture (layer);
+}
+
+static void
+_cogl_pipeline_set_layer_texture_target (CoglPipeline *pipeline,
+                                         int layer_index,
+                                         GLenum target)
+{
+  CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_TEXTURE_TARGET;
+  CoglPipelineLayer *layer;
+  CoglPipelineLayer *authority;
+  CoglPipelineLayer *new;
+
+  /* Note: this will ensure that the layer exists, creating one if it
+   * doesn't already.
+   *
+   * Note: If the layer already existed it's possibly owned by another
+   * pipeline. If the layer is created then it will be owned by
+   * pipeline. */
+  layer = _cogl_pipeline_get_layer (pipeline, layer_index);
+
+  /* Now find the ancestor of the layer that is the authority for the
+   * state we want to change */
+  authority = _cogl_pipeline_layer_get_authority (layer, change);
+
+  if (target == authority->target)
+    return;
+
+  new = _cogl_pipeline_layer_pre_change_notify (pipeline, layer, change);
+  if (new != layer)
+    layer = new;
+  else
+    {
+      /* If the original layer we found is currently the authority on
+       * the state we are changing see if we can revert to one of our
+       * ancestors being the authority. */
+      if (layer == authority &&
+          _cogl_pipeline_layer_get_parent (authority) != NULL)
+        {
+          CoglPipelineLayer *parent =
+            _cogl_pipeline_layer_get_parent (authority);
+          CoglPipelineLayer *old_authority =
+            _cogl_pipeline_layer_get_authority (parent, change);
+
+          if (old_authority->target == target)
+            {
+              layer->differences &= ~change;
+
+              g_assert (layer->owner == pipeline);
+              if (layer->differences == 0)
+                _cogl_pipeline_prune_empty_layer_difference (pipeline,
+                                                             layer);
+              goto changed;
+            }
+        }
+    }
+
+  layer->target = target;
+
+  /* If we weren't previously the authority on this state then we need
+   * to extended our differences mask and so it's possible that some
+   * of our ancestry will now become redundant, so we aim to reparent
+   * ourselves if that's true... */
+  if (layer != authority)
+    {
+      layer->differences |= change;
+      _cogl_pipeline_layer_prune_redundant_ancestry (layer);
+    }
+
+changed:
+
+  _cogl_pipeline_update_blend_enable (pipeline, COGL_PIPELINE_STATE_LAYERS);
+}
+
+static void
+_cogl_pipeline_set_layer_texture_data (CoglPipeline *pipeline,
+                                       int layer_index,
+                                       CoglHandle texture)
+{
+  CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_TEXTURE_DATA;
+  CoglPipelineLayer *layer;
+  CoglPipelineLayer *authority;
+  CoglPipelineLayer *new;
+
+  /* Note: this will ensure that the layer exists, creating one if it
+   * doesn't already.
+   *
+   * Note: If the layer already existed it's possibly owned by another
+   * pipeline. If the layer is created then it will be owned by
+   * pipeline. */
+  layer = _cogl_pipeline_get_layer (pipeline, layer_index);
+
+  /* Now find the ancestor of the layer that is the authority for the
+   * state we want to change */
+  authority = _cogl_pipeline_layer_get_authority (layer, change);
+
+  if (authority->texture == texture)
+    return;
+
+  new = _cogl_pipeline_layer_pre_change_notify (pipeline, layer, change);
+  if (new != layer)
+    layer = new;
+  else
+    {
+      /* If the original layer we found is currently the authority on
+       * the state we are changing see if we can revert to one of our
+       * ancestors being the authority. */
+      if (layer == authority &&
+          _cogl_pipeline_layer_get_parent (authority) != NULL)
+        {
+          CoglPipelineLayer *parent =
+            _cogl_pipeline_layer_get_parent (authority);
+          CoglPipelineLayer *old_authority =
+            _cogl_pipeline_layer_get_authority (parent, change);
+
+          if (old_authority->texture == texture)
+            {
+              layer->differences &= ~change;
+
+              if (layer->texture != NULL)
+                cogl_object_unref (layer->texture);
+
+              g_assert (layer->owner == pipeline);
+              if (layer->differences == 0)
+                _cogl_pipeline_prune_empty_layer_difference (pipeline,
+                                                             layer);
+              goto changed;
+            }
+        }
+    }
+
+  if (texture != NULL)
+    cogl_object_ref (texture);
+  if (layer == authority &&
+      layer->texture != NULL)
+    cogl_object_unref (layer->texture);
+  layer->texture = texture;
+
+  /* If we weren't previously the authority on this state then we need
+   * to extended our differences mask and so it's possible that some
+   * of our ancestry will now become redundant, so we aim to reparent
+   * ourselves if that's true... */
+  if (layer != authority)
+    {
+      layer->differences |= change;
+      _cogl_pipeline_layer_prune_redundant_ancestry (layer);
+    }
+
+changed:
+
+  _cogl_pipeline_update_blend_enable (pipeline, COGL_PIPELINE_STATE_LAYERS);
+}
+
+/* A convenience for querying the target of a given texture that
+ * notably returns 0 for NULL textures - so we can say that a layer
+ * with no associated CoglTexture will have a texture target of 0.
+ */
+static GLenum
+get_texture_target (CoglHandle texture)
+{
+  GLuint ignore_handle;
+  GLenum gl_target;
+
+  g_return_val_if_fail (texture, 0);
+
+  cogl_texture_get_gl_texture (texture, &ignore_handle, &gl_target);
+
+  return gl_target;
+}
+
+void
+cogl_pipeline_set_layer_texture (CoglPipeline *pipeline,
+                                 int layer_index,
+                                 CoglHandle texture)
+{
+  /* For the convenience of fragend code we separate texture state
+   * into the "target" and the "data", and setting a layer texture
+   * updates both of these properties.
+   *
+   * One example for why this is helpful is that the fragends may
+   * cache programs they generate and want to re-use those programs
+   * with all pipelines having equivalent fragment processing state.
+   * For the sake of determining if pipelines have equivalent fragment
+   * processing state we don't need to compare that the same
+   * underlying texture objects are referenced by the pipelines but we
+   * do need to see if they use the same texture targets. Making this
+   * distinction is much simpler if they are in different state
+   * groups.
+   *
+   * Note: if a NULL texture is set then we leave the target unchanged
+   * so we can avoid needlessly invalidating any associated fragment
+   * program.
+   */
+  if (texture)
+    _cogl_pipeline_set_layer_texture_target (pipeline, layer_index,
+                                             get_texture_target (texture));
+  _cogl_pipeline_set_layer_texture_data (pipeline, layer_index, texture);
+}
+
+void
+_cogl_pipeline_set_layer_wrap_modes (CoglPipeline        *pipeline,
+                                     CoglPipelineLayer   *layer,
+                                     CoglPipelineLayer   *authority,
+                                     CoglPipelineWrapModeInternal wrap_mode_s,
+                                     CoglPipelineWrapModeInternal wrap_mode_t,
+                                     CoglPipelineWrapModeInternal wrap_mode_p)
+{
+  CoglPipelineLayer     *new;
+  CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_WRAP_MODES;
+
+  if (authority->wrap_mode_s == wrap_mode_s &&
+      authority->wrap_mode_t == wrap_mode_t &&
+      authority->wrap_mode_p == wrap_mode_p)
+    return;
+
+  new = _cogl_pipeline_layer_pre_change_notify (pipeline, layer, change);
+  if (new != layer)
+    layer = new;
+  else
+    {
+      /* If the original layer we found is currently the authority on
+       * the state we are changing see if we can revert to one of our
+       * ancestors being the authority. */
+      if (layer == authority &&
+          _cogl_pipeline_layer_get_parent (authority) != NULL)
+        {
+          CoglPipelineLayer *parent =
+            _cogl_pipeline_layer_get_parent (authority);
+          CoglPipelineLayer *old_authority =
+            _cogl_pipeline_layer_get_authority (parent, change);
+
+          if (old_authority->wrap_mode_s == wrap_mode_s &&
+              old_authority->wrap_mode_t == wrap_mode_t &&
+              old_authority->wrap_mode_p == wrap_mode_p)
+            {
+              layer->differences &= ~change;
+
+              g_assert (layer->owner == pipeline);
+              if (layer->differences == 0)
+                _cogl_pipeline_prune_empty_layer_difference (pipeline,
+                                                             layer);
+              return;
+            }
+        }
+    }
+
+  layer->wrap_mode_s = wrap_mode_s;
+  layer->wrap_mode_t = wrap_mode_t;
+  layer->wrap_mode_p = wrap_mode_p;
+
+  /* If we weren't previously the authority on this state then we need
+   * to extended our differences mask and so it's possible that some
+   * of our ancestry will now become redundant, so we aim to reparent
+   * ourselves if that's true... */
+  if (layer != authority)
+    {
+      layer->differences |= change;
+      _cogl_pipeline_layer_prune_redundant_ancestry (layer);
+    }
+}
+
+static CoglPipelineWrapModeInternal
+public_to_internal_wrap_mode (CoglPipelineWrapMode mode)
+{
+  return (CoglPipelineWrapModeInternal)mode;
+}
+
+static CoglPipelineWrapMode
+internal_to_public_wrap_mode (CoglPipelineWrapModeInternal internal_mode)
+{
+  g_return_val_if_fail (internal_mode !=
+                        COGL_PIPELINE_WRAP_MODE_INTERNAL_CLAMP_TO_BORDER,
+                        COGL_PIPELINE_WRAP_MODE_AUTOMATIC);
+  return (CoglPipelineWrapMode)internal_mode;
+}
+
+void
+cogl_pipeline_set_layer_wrap_mode_s (CoglPipeline *pipeline,
+                                     int layer_index,
+                                     CoglPipelineWrapMode mode)
+{
+  CoglPipelineLayerState       change = COGL_PIPELINE_LAYER_STATE_WRAP_MODES;
+  CoglPipelineLayer           *layer;
+  CoglPipelineLayer           *authority;
+  CoglPipelineWrapModeInternal internal_mode =
+    public_to_internal_wrap_mode (mode);
+
+  g_return_if_fail (cogl_is_pipeline (pipeline));
+
+  /* Note: this will ensure that the layer exists, creating one if it
+   * doesn't already.
+   *
+   * Note: If the layer already existed it's possibly owned by another
+   * pipeline. If the layer is created then it will be owned by
+   * pipeline. */
+  layer = _cogl_pipeline_get_layer (pipeline, layer_index);
+
+  /* Now find the ancestor of the layer that is the authority for the
+   * state we want to change */
+  authority = _cogl_pipeline_layer_get_authority (layer, change);
+
+  _cogl_pipeline_set_layer_wrap_modes (pipeline, layer, authority,
+                                       internal_mode,
+                                       authority->wrap_mode_t,
+                                       authority->wrap_mode_p);
+}
+
+void
+cogl_pipeline_set_layer_wrap_mode_t (CoglPipeline        *pipeline,
+                                     int                  layer_index,
+                                     CoglPipelineWrapMode mode)
+{
+  CoglPipelineLayerState       change = COGL_PIPELINE_LAYER_STATE_WRAP_MODES;
+  CoglPipelineLayer           *layer;
+  CoglPipelineLayer           *authority;
+  CoglPipelineWrapModeInternal internal_mode =
+    public_to_internal_wrap_mode (mode);
+
+  g_return_if_fail (cogl_is_pipeline (pipeline));
+
+  /* Note: this will ensure that the layer exists, creating one if it
+   * doesn't already.
+   *
+   * Note: If the layer already existed it's possibly owned by another
+   * pipeline. If the layer is created then it will be owned by
+   * pipeline. */
+  layer = _cogl_pipeline_get_layer (pipeline, layer_index);
+
+  /* Now find the ancestor of the layer that is the authority for the
+   * state we want to change */
+  authority = _cogl_pipeline_layer_get_authority (layer, change);
+
+  _cogl_pipeline_set_layer_wrap_modes (pipeline, layer, authority,
+                                       authority->wrap_mode_s,
+                                       internal_mode,
+                                       authority->wrap_mode_p);
+}
+
+/* The rationale for naming the third texture coordinate 'p' instead
+   of OpenGL's usual 'r' is that 'r' conflicts with the usual naming
+   of the 'red' component when treating a vector as a color. Under
+   GLSL this is awkward because the texture swizzling for a vector
+   uses a single letter for each component and the names for colors,
+   textures and positions are synonymous. GLSL works around this by
+   naming the components of the texture s, t, p and q. Cogl already
+   effectively already exposes this naming because it exposes GLSL so
+   it makes sense to use that naming consistently. Another alternative
+   could be u, v and w. This is what Blender and Direct3D use. However
+   the w component conflicts with the w component of a position
+   vertex.  */
+void
+cogl_pipeline_set_layer_wrap_mode_p (CoglPipeline        *pipeline,
+                                     int                  layer_index,
+                                     CoglPipelineWrapMode mode)
+{
+  CoglPipelineLayerState       change = COGL_PIPELINE_LAYER_STATE_WRAP_MODES;
+  CoglPipelineLayer           *layer;
+  CoglPipelineLayer           *authority;
+  CoglPipelineWrapModeInternal internal_mode =
+    public_to_internal_wrap_mode (mode);
+
+  g_return_if_fail (cogl_is_pipeline (pipeline));
+
+  /* Note: this will ensure that the layer exists, creating one if it
+   * doesn't already.
+   *
+   * Note: If the layer already existed it's possibly owned by another
+   * pipeline. If the layer is created then it will be owned by
+   * pipeline. */
+  layer = _cogl_pipeline_get_layer (pipeline, layer_index);
+
+  /* Now find the ancestor of the layer that is the authority for the
+   * state we want to change */
+  authority = _cogl_pipeline_layer_get_authority (layer, change);
+
+  _cogl_pipeline_set_layer_wrap_modes (pipeline, layer, authority,
+                                       authority->wrap_mode_s,
+                                       authority->wrap_mode_t,
+                                       internal_mode);
+}
+
+void
+cogl_pipeline_set_layer_wrap_mode (CoglPipeline        *pipeline,
+                                   int                  layer_index,
+                                   CoglPipelineWrapMode mode)
+{
+  CoglPipelineLayerState       change = COGL_PIPELINE_LAYER_STATE_WRAP_MODES;
+  CoglPipelineLayer           *layer;
+  CoglPipelineLayer           *authority;
+  CoglPipelineWrapModeInternal internal_mode =
+    public_to_internal_wrap_mode (mode);
+
+  g_return_if_fail (cogl_is_pipeline (pipeline));
+
+  /* Note: this will ensure that the layer exists, creating one if it
+   * doesn't already.
+   *
+   * Note: If the layer already existed it's possibly owned by another
+   * pipeline. If the layer is created then it will be owned by
+   * pipeline. */
+  layer = _cogl_pipeline_get_layer (pipeline, layer_index);
+
+  /* Now find the ancestor of the layer that is the authority for the
+   * state we want to change */
+  authority = _cogl_pipeline_layer_get_authority (layer, change);
+
+  _cogl_pipeline_set_layer_wrap_modes (pipeline, layer, authority,
+                                       internal_mode,
+                                       internal_mode,
+                                       internal_mode);
+  /* XXX: I wonder if we should really be duplicating the mode into
+   * the 'r' wrap mode too? */
+}
+
+/* FIXME: deprecate this API */
+CoglPipelineWrapMode
+_cogl_pipeline_layer_get_wrap_mode_s (CoglPipelineLayer *layer)
+{
+  CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_WRAP_MODES;
+  CoglPipelineLayer     *authority;
+
+  g_return_val_if_fail (_cogl_is_pipeline_layer (layer), FALSE);
+
+  /* Now find the ancestor of the layer that is the authority for the
+   * state we want to change */
+  authority = _cogl_pipeline_layer_get_authority (layer, change);
+
+  return internal_to_public_wrap_mode (authority->wrap_mode_s);
+}
+
+CoglPipelineWrapMode
+cogl_pipeline_get_layer_wrap_mode_s (CoglPipeline *pipeline, int layer_index)
+{
+  CoglPipelineLayer *layer;
+
+  g_return_val_if_fail (cogl_is_pipeline (pipeline), FALSE);
+
+  /* Note: this will ensure that the layer exists, creating one if it
+   * doesn't already.
+   *
+   * Note: If the layer already existed it's possibly owned by another
+   * pipeline. If the layer is created then it will be owned by
+   * pipeline. */
+  layer = _cogl_pipeline_get_layer (pipeline, layer_index);
+  /* FIXME: we shouldn't ever construct a layer in a getter function */
+
+  return _cogl_pipeline_layer_get_wrap_mode_s (layer);
+}
+
+/* FIXME: deprecate this API */
+CoglPipelineWrapMode
+_cogl_pipeline_layer_get_wrap_mode_t (CoglPipelineLayer *layer)
+{
+  CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_WRAP_MODES;
+  CoglPipelineLayer     *authority;
+
+  g_return_val_if_fail (_cogl_is_pipeline_layer (layer), FALSE);
+
+  /* Now find the ancestor of the layer that is the authority for the
+   * state we want to change */
+  authority = _cogl_pipeline_layer_get_authority (layer, change);
+
+  return internal_to_public_wrap_mode (authority->wrap_mode_t);
+}
+
+CoglPipelineWrapMode
+cogl_pipeline_get_layer_wrap_mode_t (CoglPipeline *pipeline, int layer_index)
+{
+  CoglPipelineLayer *layer;
+
+  g_return_val_if_fail (cogl_is_pipeline (pipeline), FALSE);
+
+  /* Note: this will ensure that the layer exists, creating one if it
+   * doesn't already.
+   *
+   * Note: If the layer already existed it's possibly owned by another
+   * pipeline. If the layer is created then it will be owned by
+   * pipeline. */
+  layer = _cogl_pipeline_get_layer (pipeline, layer_index);
+  /* FIXME: we shouldn't ever construct a layer in a getter function */
+
+  return _cogl_pipeline_layer_get_wrap_mode_t (layer);
+}
+
+CoglPipelineWrapMode
+_cogl_pipeline_layer_get_wrap_mode_p (CoglPipelineLayer *layer)
+{
+  CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_WRAP_MODES;
+  CoglPipelineLayer     *authority =
+    _cogl_pipeline_layer_get_authority (layer, change);
+
+  return internal_to_public_wrap_mode (authority->wrap_mode_p);
+}
+
+CoglPipelineWrapMode
+cogl_pipeline_get_layer_wrap_mode_p (CoglPipeline *pipeline, int layer_index)
+{
+  CoglPipelineLayer *layer;
+
+  g_return_val_if_fail (cogl_is_pipeline (pipeline), FALSE);
+
+  /* Note: this will ensure that the layer exists, creating one if it
+   * doesn't already.
+   *
+   * Note: If the layer already existed it's possibly owned by another
+   * pipeline. If the layer is created then it will be owned by
+   * pipeline. */
+  layer = _cogl_pipeline_get_layer (pipeline, layer_index);
+
+  return _cogl_pipeline_layer_get_wrap_mode_p (layer);
+}
+
+void
+_cogl_pipeline_layer_get_wrap_modes (CoglPipelineLayer *layer,
+                                     CoglPipelineWrapModeInternal *wrap_mode_s,
+                                     CoglPipelineWrapModeInternal *wrap_mode_t,
+                                     CoglPipelineWrapModeInternal *wrap_mode_p)
+{
+  CoglPipelineLayer *authority =
+    _cogl_pipeline_layer_get_authority (layer,
+                                        COGL_PIPELINE_LAYER_STATE_WRAP_MODES);
+
+  *wrap_mode_s = authority->wrap_mode_s;
+  *wrap_mode_t = authority->wrap_mode_t;
+  *wrap_mode_p = authority->wrap_mode_p;
+}
+
+gboolean
+cogl_pipeline_set_layer_point_sprite_coords_enabled (CoglPipeline *pipeline,
+                                                     int layer_index,
+                                                     gboolean enable,
+                                                     GError **error)
+{
+  CoglPipelineLayerState       change =
+    COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS;
+  CoglPipelineLayer           *layer;
+  CoglPipelineLayer           *new;
+  CoglPipelineLayer           *authority;
+
+  g_return_val_if_fail (cogl_is_pipeline (pipeline), FALSE);
+
+  /* Don't allow point sprite coordinates to be enabled if the driver
+     doesn't support it */
+  if (enable && !cogl_features_available (COGL_FEATURE_POINT_SPRITE))
+    {
+      if (error)
+        {
+          g_set_error (error, COGL_ERROR, COGL_ERROR_UNSUPPORTED,
+                       "Point sprite texture coordinates are enabled "
+                       "for a layer but the GL driver does not support it.");
+        }
+      else
+        {
+          static gboolean warning_seen = FALSE;
+          if (!warning_seen)
+            g_warning ("Point sprite texture coordinates are enabled "
+                       "for a layer but the GL driver does not support it.");
+          warning_seen = TRUE;
+        }
+
+      return FALSE;
+    }
+
+  /* Note: this will ensure that the layer exists, creating one if it
+   * doesn't already.
+   *
+   * Note: If the layer already existed it's possibly owned by another
+   * pipeline. If the layer is created then it will be owned by
+   * pipeline. */
+  layer = _cogl_pipeline_get_layer (pipeline, layer_index);
+
+  /* Now find the ancestor of the layer that is the authority for the
+   * state we want to change */
+  authority = _cogl_pipeline_layer_get_authority (layer, change);
+
+  if (authority->big_state->point_sprite_coords == enable)
+    return TRUE;
+
+  new = _cogl_pipeline_layer_pre_change_notify (pipeline, layer, change);
+  if (new != layer)
+    layer = new;
+  else
+    {
+      /* If the original layer we found is currently the authority on
+       * the state we are changing see if we can revert to one of our
+       * ancestors being the authority. */
+      if (layer == authority &&
+          _cogl_pipeline_layer_get_parent (authority) != NULL)
+        {
+          CoglPipelineLayer *parent =
+            _cogl_pipeline_layer_get_parent (authority);
+          CoglPipelineLayer *old_authority =
+            _cogl_pipeline_layer_get_authority (parent, change);
+
+          if (old_authority->big_state->point_sprite_coords == enable)
+            {
+              layer->differences &= ~change;
+
+              g_assert (layer->owner == pipeline);
+              if (layer->differences == 0)
+                _cogl_pipeline_prune_empty_layer_difference (pipeline,
+                                                             layer);
+              return TRUE;
+            }
+        }
+    }
+
+  layer->big_state->point_sprite_coords = enable;
+
+  /* If we weren't previously the authority on this state then we need
+   * to extended our differences mask and so it's possible that some
+   * of our ancestry will now become redundant, so we aim to reparent
+   * ourselves if that's true... */
+  if (layer != authority)
+    {
+      layer->differences |= change;
+      _cogl_pipeline_layer_prune_redundant_ancestry (layer);
+    }
+
+  return TRUE;
+}
+
+gboolean
+cogl_pipeline_get_layer_point_sprite_coords_enabled (CoglPipeline *pipeline,
+                                                     int layer_index)
+{
+  CoglPipelineLayerState       change =
+    COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS;
+  CoglPipelineLayer *layer;
+  CoglPipelineLayer *authority;
+
+  g_return_val_if_fail (cogl_is_pipeline (pipeline), FALSE);
+
+  /* Note: this will ensure that the layer exists, creating one if it
+   * doesn't already.
+   *
+   * Note: If the layer already existed it's possibly owned by another
+   * pipeline. If the layer is created then it will be owned by
+   * pipeline. */
+  layer = _cogl_pipeline_get_layer (pipeline, layer_index);
+  /* FIXME: we shouldn't ever construct a layer in a getter function */
+
+  authority = _cogl_pipeline_layer_get_authority (layer, change);
+
+  return authority->big_state->point_sprite_coords;
+}
+
+gboolean
+_cogl_pipeline_layer_texture_target_equal (CoglPipelineLayer *authority0,
+                                           CoglPipelineLayer *authority1,
+                                           CoglPipelineEvalFlags flags)
+{
+  return authority0->target == authority1->target;
+}
+
+gboolean
+_cogl_pipeline_layer_texture_data_equal (CoglPipelineLayer *authority0,
+                                         CoglPipelineLayer *authority1,
+                                         CoglPipelineEvalFlags flags)
+{
+  GLuint gl_handle0, gl_handle1;
+
+  cogl_texture_get_gl_texture (authority0->texture, &gl_handle0, NULL);
+  cogl_texture_get_gl_texture (authority1->texture, &gl_handle1, NULL);
+
+  return gl_handle0 == gl_handle1;
+}
+
+gboolean
+_cogl_pipeline_layer_combine_state_equal (CoglPipelineLayer *authority0,
+                                          CoglPipelineLayer *authority1)
+{
+  CoglPipelineLayerBigState *big_state0 = authority0->big_state;
+  CoglPipelineLayerBigState *big_state1 = authority1->big_state;
+  int n_args;
+  int i;
+
+  if (big_state0->texture_combine_rgb_func !=
+      big_state1->texture_combine_rgb_func)
+    return FALSE;
+
+  if (big_state0->texture_combine_alpha_func !=
+      big_state1->texture_combine_alpha_func)
+    return FALSE;
+
+  n_args =
+    _cogl_get_n_args_for_combine_func (big_state0->texture_combine_rgb_func);
+  for (i = 0; i < n_args; i++)
+    {
+      if ((big_state0->texture_combine_rgb_src[i] !=
+           big_state1->texture_combine_rgb_src[i]) ||
+          (big_state0->texture_combine_rgb_op[i] !=
+           big_state1->texture_combine_rgb_op[i]))
+        return FALSE;
+    }
+
+  n_args =
+    _cogl_get_n_args_for_combine_func (big_state0->texture_combine_alpha_func);
+  for (i = 0; i < n_args; i++)
+    {
+      if ((big_state0->texture_combine_alpha_src[i] !=
+           big_state1->texture_combine_alpha_src[i]) ||
+          (big_state0->texture_combine_alpha_op[i] !=
+           big_state1->texture_combine_alpha_op[i]))
+        return FALSE;
+    }
+
+  return TRUE;
+}
+
+gboolean
+_cogl_pipeline_layer_combine_constant_equal (CoglPipelineLayer *authority0,
+                                             CoglPipelineLayer *authority1)
+{
+  return memcmp (authority0->big_state->texture_combine_constant,
+                 authority1->big_state->texture_combine_constant,
+                 sizeof (float) * 4) == 0 ? TRUE : FALSE;
+}
+
+gboolean
+_cogl_pipeline_layer_filters_equal (CoglPipelineLayer *authority0,
+                                    CoglPipelineLayer *authority1)
+{
+  if (authority0->mag_filter != authority1->mag_filter)
+    return FALSE;
+  if (authority0->min_filter != authority1->min_filter)
+    return FALSE;
+
+  return TRUE;
+}
+
+static gboolean
+compare_wrap_mode_equal (CoglPipelineWrapMode wrap_mode0,
+                         CoglPipelineWrapMode wrap_mode1)
+{
+  /* We consider AUTOMATIC to be equivalent to CLAMP_TO_EDGE because
+     the primitives code is expected to override this to something
+     else if it wants it to be behave any other way */
+  if (wrap_mode0 == COGL_PIPELINE_WRAP_MODE_AUTOMATIC)
+    wrap_mode0 = COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE;
+  if (wrap_mode1 == COGL_PIPELINE_WRAP_MODE_AUTOMATIC)
+    wrap_mode1 = COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE;
+
+  return wrap_mode0 == wrap_mode1;
+}
+
+gboolean
+_cogl_pipeline_layer_wrap_modes_equal (CoglPipelineLayer *authority0,
+                                       CoglPipelineLayer *authority1)
+{
+  if (!compare_wrap_mode_equal (authority0->wrap_mode_s,
+                                authority1->wrap_mode_s) ||
+      !compare_wrap_mode_equal (authority0->wrap_mode_t,
+                                authority1->wrap_mode_t) ||
+      !compare_wrap_mode_equal (authority0->wrap_mode_p,
+                                authority1->wrap_mode_p))
+    return FALSE;
+
+  return TRUE;
+}
+
+gboolean
+_cogl_pipeline_layer_user_matrix_equal (CoglPipelineLayer *authority0,
+                                        CoglPipelineLayer *authority1)
+{
+  CoglPipelineLayerBigState *big_state0 = authority0->big_state;
+  CoglPipelineLayerBigState *big_state1 = authority1->big_state;
+
+  if (!cogl_matrix_equal (&big_state0->matrix, &big_state1->matrix))
+    return FALSE;
+
+  return TRUE;
+}
+
+gboolean
+_cogl_pipeline_layer_point_sprite_coords_equal (CoglPipelineLayer *authority0,
+                                                CoglPipelineLayer *authority1)
+{
+  CoglPipelineLayerBigState *big_state0 = authority0->big_state;
+  CoglPipelineLayerBigState *big_state1 = authority1->big_state;
+
+  return big_state0->point_sprite_coords == big_state1->point_sprite_coords;
+}
+
+static void
+setup_texture_combine_state (CoglBlendStringStatement *statement,
+                             CoglPipelineCombineFunc *texture_combine_func,
+                             CoglPipelineCombineSource *texture_combine_src,
+                             CoglPipelineCombineOp *texture_combine_op)
+{
+  int i;
+
+  switch (statement->function->type)
+    {
+    case COGL_BLEND_STRING_FUNCTION_REPLACE:
+      *texture_combine_func = COGL_PIPELINE_COMBINE_FUNC_REPLACE;
+      break;
+    case COGL_BLEND_STRING_FUNCTION_MODULATE:
+      *texture_combine_func = COGL_PIPELINE_COMBINE_FUNC_MODULATE;
+      break;
+    case COGL_BLEND_STRING_FUNCTION_ADD:
+      *texture_combine_func = COGL_PIPELINE_COMBINE_FUNC_ADD;
+      break;
+    case COGL_BLEND_STRING_FUNCTION_ADD_SIGNED:
+      *texture_combine_func = COGL_PIPELINE_COMBINE_FUNC_ADD_SIGNED;
+      break;
+    case COGL_BLEND_STRING_FUNCTION_INTERPOLATE:
+      *texture_combine_func = COGL_PIPELINE_COMBINE_FUNC_INTERPOLATE;
+      break;
+    case COGL_BLEND_STRING_FUNCTION_SUBTRACT:
+      *texture_combine_func = COGL_PIPELINE_COMBINE_FUNC_SUBTRACT;
+      break;
+    case COGL_BLEND_STRING_FUNCTION_DOT3_RGB:
+      *texture_combine_func = COGL_PIPELINE_COMBINE_FUNC_DOT3_RGB;
+      break;
+    case COGL_BLEND_STRING_FUNCTION_DOT3_RGBA:
+      *texture_combine_func = COGL_PIPELINE_COMBINE_FUNC_DOT3_RGBA;
+      break;
+    }
+
+  for (i = 0; i < statement->function->argc; i++)
+    {
+      CoglBlendStringArgument *arg = &statement->args[i];
+
+      switch (arg->source.info->type)
+        {
+        case COGL_BLEND_STRING_COLOR_SOURCE_CONSTANT:
+          texture_combine_src[i] = COGL_PIPELINE_COMBINE_SOURCE_CONSTANT;
+          break;
+        case COGL_BLEND_STRING_COLOR_SOURCE_TEXTURE:
+          texture_combine_src[i] = COGL_PIPELINE_COMBINE_SOURCE_TEXTURE;
+          break;
+        case COGL_BLEND_STRING_COLOR_SOURCE_TEXTURE_N:
+          texture_combine_src[i] =
+            COGL_PIPELINE_COMBINE_SOURCE_TEXTURE0 + arg->source.texture;
+          break;
+        case COGL_BLEND_STRING_COLOR_SOURCE_PRIMARY:
+          texture_combine_src[i] = COGL_PIPELINE_COMBINE_SOURCE_PRIMARY_COLOR;
+          break;
+        case COGL_BLEND_STRING_COLOR_SOURCE_PREVIOUS:
+          texture_combine_src[i] = COGL_PIPELINE_COMBINE_SOURCE_PREVIOUS;
+          break;
+        default:
+          g_warning ("Unexpected texture combine source");
+          texture_combine_src[i] = COGL_PIPELINE_COMBINE_SOURCE_TEXTURE;
+        }
+
+      if (arg->source.mask == COGL_BLEND_STRING_CHANNEL_MASK_RGB)
+        {
+          if (statement->args[i].source.one_minus)
+            texture_combine_op[i] =
+              COGL_PIPELINE_COMBINE_OP_ONE_MINUS_SRC_COLOR;
+          else
+            texture_combine_op[i] = COGL_PIPELINE_COMBINE_OP_SRC_COLOR;
+        }
+      else
+        {
+          if (statement->args[i].source.one_minus)
+            texture_combine_op[i] =
+              COGL_PIPELINE_COMBINE_OP_ONE_MINUS_SRC_ALPHA;
+          else
+            texture_combine_op[i] = COGL_PIPELINE_COMBINE_OP_SRC_ALPHA;
+        }
+    }
+}
+
+gboolean
+cogl_pipeline_set_layer_combine (CoglPipeline *pipeline,
+				 int layer_index,
+				 const char *combine_description,
+                                 GError **error)
+{
+  CoglPipelineLayerState state = COGL_PIPELINE_LAYER_STATE_COMBINE;
+  CoglPipelineLayer *authority;
+  CoglPipelineLayer *layer;
+  CoglBlendStringStatement statements[2];
+  CoglBlendStringStatement split[2];
+  CoglBlendStringStatement *rgb;
+  CoglBlendStringStatement *a;
+  GError *internal_error = NULL;
+  int count;
+
+  g_return_val_if_fail (cogl_is_pipeline (pipeline), FALSE);
+
+  /* Note: this will ensure that the layer exists, creating one if it
+   * doesn't already.
+   *
+   * Note: If the layer already existed it's possibly owned by another
+   * pipeline. If the layer is created then it will be owned by
+   * pipeline. */
+  layer = _cogl_pipeline_get_layer (pipeline, layer_index);
+
+  /* Now find the ancestor of the layer that is the authority for the
+   * state we want to change */
+  authority = _cogl_pipeline_layer_get_authority (layer, state);
+
+  count =
+    _cogl_blend_string_compile (combine_description,
+                                COGL_BLEND_STRING_CONTEXT_TEXTURE_COMBINE,
+                                statements,
+                                &internal_error);
+  if (!count)
+    {
+      if (error)
+	g_propagate_error (error, internal_error);
+      else
+	{
+	  g_warning ("Cannot compile combine description: %s\n",
+		     internal_error->message);
+	  g_error_free (internal_error);
+	}
+      return FALSE;
+    }
+
+  if (statements[0].mask == COGL_BLEND_STRING_CHANNEL_MASK_RGBA)
+    {
+      _cogl_blend_string_split_rgba_statement (statements,
+                                               &split[0], &split[1]);
+      rgb = &split[0];
+      a = &split[1];
+    }
+  else
+    {
+      rgb = &statements[0];
+      a = &statements[1];
+    }
+
+  /* FIXME: compare the new state with the current state! */
+
+  /* possibly flush primitives referencing the current state... */
+  layer = _cogl_pipeline_layer_pre_change_notify (pipeline, layer, state);
+
+  setup_texture_combine_state (rgb,
+                               &layer->big_state->texture_combine_rgb_func,
+                               layer->big_state->texture_combine_rgb_src,
+                               layer->big_state->texture_combine_rgb_op);
+
+  setup_texture_combine_state (a,
+                               &layer->big_state->texture_combine_alpha_func,
+                               layer->big_state->texture_combine_alpha_src,
+                               layer->big_state->texture_combine_alpha_op);
+
+  /* If the original layer we found is currently the authority on
+   * the state we are changing see if we can revert to one of our
+   * ancestors being the authority. */
+  if (layer == authority &&
+      _cogl_pipeline_layer_get_parent (authority) != NULL)
+    {
+      CoglPipelineLayer *parent = _cogl_pipeline_layer_get_parent (authority);
+      CoglPipelineLayer *old_authority =
+        _cogl_pipeline_layer_get_authority (parent, state);
+
+      if (_cogl_pipeline_layer_combine_state_equal (authority,
+                                                    old_authority))
+        {
+          layer->differences &= ~state;
+
+          g_assert (layer->owner == pipeline);
+          if (layer->differences == 0)
+            _cogl_pipeline_prune_empty_layer_difference (pipeline,
+                                                         layer);
+          goto changed;
+        }
+    }
+
+  /* If we weren't previously the authority on this state then we need
+   * to extended our differences mask and so it's possible that some
+   * of our ancestry will now become redundant, so we aim to reparent
+   * ourselves if that's true... */
+  if (layer != authority)
+    {
+      layer->differences |= state;
+      _cogl_pipeline_layer_prune_redundant_ancestry (layer);
+    }
+
+changed:
+
+  _cogl_pipeline_update_blend_enable (pipeline, COGL_PIPELINE_STATE_LAYERS);
+  return TRUE;
+}
+
+void
+cogl_pipeline_set_layer_combine_constant (CoglPipeline *pipeline,
+				          int layer_index,
+                                          const CoglColor *constant_color)
+{
+  CoglPipelineLayerState state = COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT;
+  CoglPipelineLayer     *layer;
+  CoglPipelineLayer     *authority;
+  CoglPipelineLayer     *new;
+  float                  color_as_floats[4];
+
+  g_return_if_fail (cogl_is_pipeline (pipeline));
+
+  /* Note: this will ensure that the layer exists, creating one if it
+   * doesn't already.
+   *
+   * Note: If the layer already existed it's possibly owned by another
+   * pipeline. If the layer is created then it will be owned by
+   * pipeline. */
+  layer = _cogl_pipeline_get_layer (pipeline, layer_index);
+
+  /* Now find the ancestor of the layer that is the authority for the
+   * state we want to change */
+  authority = _cogl_pipeline_layer_get_authority (layer, state);
+
+  color_as_floats[0] = cogl_color_get_red_float (constant_color);
+  color_as_floats[1] = cogl_color_get_green_float (constant_color);
+  color_as_floats[2] = cogl_color_get_blue_float (constant_color);
+  color_as_floats[3] = cogl_color_get_alpha_float (constant_color);
+
+  if (memcmp (authority->big_state->texture_combine_constant,
+              color_as_floats, sizeof (float) * 4) == 0)
+    return;
+
+  new = _cogl_pipeline_layer_pre_change_notify (pipeline, layer, state);
+  if (new != layer)
+    layer = new;
+  else
+    {
+      /* If the original layer we found is currently the authority on
+       * the state we are changing see if we can revert to one of our
+       * ancestors being the authority. */
+      if (layer == authority &&
+          _cogl_pipeline_layer_get_parent (authority) != NULL)
+        {
+          CoglPipelineLayer *parent =
+            _cogl_pipeline_layer_get_parent (authority);
+          CoglPipelineLayer *old_authority =
+            _cogl_pipeline_layer_get_authority (parent, state);
+          CoglPipelineLayerBigState *old_big_state = old_authority->big_state;
+
+          if (memcmp (old_big_state->texture_combine_constant,
+                      color_as_floats, sizeof (float) * 4) == 0)
+            {
+              layer->differences &= ~state;
+
+              g_assert (layer->owner == pipeline);
+              if (layer->differences == 0)
+                _cogl_pipeline_prune_empty_layer_difference (pipeline,
+                                                             layer);
+              goto changed;
+            }
+        }
+    }
+
+  memcpy (layer->big_state->texture_combine_constant,
+          color_as_floats,
+          sizeof (color_as_floats));
+
+  /* If we weren't previously the authority on this state then we need
+   * to extended our differences mask and so it's possible that some
+   * of our ancestry will now become redundant, so we aim to reparent
+   * ourselves if that's true... */
+  if (layer != authority)
+    {
+      layer->differences |= state;
+      _cogl_pipeline_layer_prune_redundant_ancestry (layer);
+    }
+
+changed:
+
+  _cogl_pipeline_update_blend_enable (pipeline, COGL_PIPELINE_STATE_LAYERS);
+}
+
+void
+_cogl_pipeline_get_layer_combine_constant (CoglPipeline *pipeline,
+                                           int layer_index,
+                                           float *constant)
+{
+  CoglPipelineLayerState       change =
+    COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT;
+  CoglPipelineLayer *layer;
+  CoglPipelineLayer *authority;
+
+  g_return_if_fail (cogl_is_pipeline (pipeline));
+
+  /* Note: this will ensure that the layer exists, creating one if it
+   * doesn't already.
+   *
+   * Note: If the layer already existed it's possibly owned by another
+   * pipeline. If the layer is created then it will be owned by
+   * pipeline. */
+  layer = _cogl_pipeline_get_layer (pipeline, layer_index);
+  /* FIXME: we shouldn't ever construct a layer in a getter function */
+
+  authority = _cogl_pipeline_layer_get_authority (layer, change);
+  memcpy (constant, authority->big_state->texture_combine_constant,
+          sizeof (float) * 4);
+}
+
+/* We should probably make a public API version of this that has a
+   matrix out-param. For an internal API it's good to be able to avoid
+   copying the matrix */
+const CoglMatrix *
+_cogl_pipeline_get_layer_matrix (CoglPipeline *pipeline, int layer_index)
+{
+  CoglPipelineLayerState       change =
+    COGL_PIPELINE_LAYER_STATE_USER_MATRIX;
+  CoglPipelineLayer *layer;
+  CoglPipelineLayer *authority;
+
+  g_return_val_if_fail (cogl_is_pipeline (pipeline), NULL);
+
+  layer = _cogl_pipeline_get_layer (pipeline, layer_index);
+
+  authority = _cogl_pipeline_layer_get_authority (layer, change);
+  return &authority->big_state->matrix;
+}
+
+void
+cogl_pipeline_set_layer_matrix (CoglPipeline *pipeline,
+				int layer_index,
+                                const CoglMatrix *matrix)
+{
+  CoglPipelineLayerState state = COGL_PIPELINE_LAYER_STATE_USER_MATRIX;
+  CoglPipelineLayer     *layer;
+  CoglPipelineLayer     *authority;
+  CoglPipelineLayer     *new;
+
+  g_return_if_fail (cogl_is_pipeline (pipeline));
+
+  /* Note: this will ensure that the layer exists, creating one if it
+   * doesn't already.
+   *
+   * Note: If the layer already existed it's possibly owned by another
+   * pipeline. If the layer is created then it will be owned by
+   * pipeline. */
+  layer = _cogl_pipeline_get_layer (pipeline, layer_index);
+
+  /* Now find the ancestor of the layer that is the authority for the
+   * state we want to change */
+  authority = _cogl_pipeline_layer_get_authority (layer, state);
+
+  if (cogl_matrix_equal (matrix, &authority->big_state->matrix))
+    return;
+
+  new = _cogl_pipeline_layer_pre_change_notify (pipeline, layer, state);
+  if (new != layer)
+    layer = new;
+  else
+    {
+      /* If the original layer we found is currently the authority on
+       * the state we are changing see if we can revert to one of our
+       * ancestors being the authority. */
+      if (layer == authority &&
+          _cogl_pipeline_layer_get_parent (authority) != NULL)
+        {
+          CoglPipelineLayer *parent =
+            _cogl_pipeline_layer_get_parent (authority);
+          CoglPipelineLayer *old_authority =
+            _cogl_pipeline_layer_get_authority (parent, state);
+
+          if (cogl_matrix_equal (matrix, &old_authority->big_state->matrix))
+            {
+              layer->differences &= ~state;
+
+              g_assert (layer->owner == pipeline);
+              if (layer->differences == 0)
+                _cogl_pipeline_prune_empty_layer_difference (pipeline,
+                                                             layer);
+              return;
+            }
+        }
+    }
+
+  layer->big_state->matrix = *matrix;
+
+  /* If we weren't previously the authority on this state then we need
+   * to extended our differences mask and so it's possible that some
+   * of our ancestry will now become redundant, so we aim to reparent
+   * ourselves if that's true... */
+  if (layer != authority)
+    {
+      layer->differences |= state;
+      _cogl_pipeline_layer_prune_redundant_ancestry (layer);
+    }
+}
+
+/* FIXME: deprecate and replace with
+ * cogl_pipeline_get_layer_texture() instead. */
+CoglHandle
+_cogl_pipeline_layer_get_texture (CoglPipelineLayer *layer)
+{
+  g_return_val_if_fail (_cogl_is_pipeline_layer (layer), NULL);
+
+  return _cogl_pipeline_layer_get_texture_real (layer);
+}
+
+gboolean
+_cogl_pipeline_layer_has_user_matrix (CoglPipeline *pipeline,
+                                      int layer_index)
+{
+  CoglPipelineLayer *layer;
+  CoglPipelineLayer *authority;
+
+  layer = _cogl_pipeline_get_layer (pipeline, layer_index);
+
+  authority =
+    _cogl_pipeline_layer_get_authority (layer,
+                                        COGL_PIPELINE_LAYER_STATE_USER_MATRIX);
+
+  /* If the authority is the default pipeline then no, otherwise yes */
+  return _cogl_pipeline_layer_get_parent (authority) ? TRUE : FALSE;
+}
+
+void
+_cogl_pipeline_layer_get_filters (CoglPipelineLayer *layer,
+                                  CoglPipelineFilter *min_filter,
+                                  CoglPipelineFilter *mag_filter)
+{
+  CoglPipelineLayer *authority =
+    _cogl_pipeline_layer_get_authority (layer,
+                                        COGL_PIPELINE_LAYER_STATE_FILTERS);
+
+  *min_filter = authority->min_filter;
+  *mag_filter = authority->mag_filter;
+}
+
+void
+_cogl_pipeline_get_layer_filters (CoglPipeline *pipeline,
+                                  int layer_index,
+                                  CoglPipelineFilter *min_filter,
+                                  CoglPipelineFilter *mag_filter)
+{
+  CoglPipelineLayer *layer;
+  CoglPipelineLayer *authority;
+
+  g_return_if_fail (cogl_is_pipeline (pipeline));
+
+  layer = _cogl_pipeline_get_layer (pipeline, layer_index);
+
+  authority =
+    _cogl_pipeline_layer_get_authority (layer,
+                                        COGL_PIPELINE_LAYER_STATE_FILTERS);
+
+  *min_filter = authority->min_filter;
+  *mag_filter = authority->mag_filter;
+}
+
+CoglPipelineFilter
+_cogl_pipeline_get_layer_min_filter (CoglPipeline *pipeline,
+                                     int layer_index)
+{
+  CoglPipelineFilter min_filter;
+  CoglPipelineFilter mag_filter;
+
+  _cogl_pipeline_get_layer_filters (pipeline, layer_index,
+                                    &min_filter, &mag_filter);
+  return min_filter;
+}
+
+CoglPipelineFilter
+_cogl_pipeline_get_layer_mag_filter (CoglPipeline *pipeline,
+                                     int layer_index)
+{
+  CoglPipelineFilter min_filter;
+  CoglPipelineFilter mag_filter;
+
+  _cogl_pipeline_get_layer_filters (pipeline, layer_index,
+                                    &min_filter, &mag_filter);
+  return mag_filter;
+}
+
+CoglPipelineFilter
+_cogl_pipeline_layer_get_min_filter (CoglPipelineLayer *layer)
+{
+  CoglPipelineLayer *authority;
+
+  g_return_val_if_fail (_cogl_is_pipeline_layer (layer), 0);
+
+  authority =
+    _cogl_pipeline_layer_get_authority (layer,
+                                        COGL_PIPELINE_LAYER_STATE_FILTERS);
+
+  return authority->min_filter;
+}
+
+CoglPipelineFilter
+_cogl_pipeline_layer_get_mag_filter (CoglPipelineLayer *layer)
+{
+  CoglPipelineLayer *authority;
+
+  g_return_val_if_fail (_cogl_is_pipeline_layer (layer), 0);
+
+  authority =
+    _cogl_pipeline_layer_get_authority (layer,
+                                        COGL_PIPELINE_LAYER_STATE_FILTERS);
+
+  return authority->mag_filter;
+}
+
+void
+cogl_pipeline_set_layer_filters (CoglPipeline      *pipeline,
+                                 int                layer_index,
+                                 CoglPipelineFilter min_filter,
+                                 CoglPipelineFilter mag_filter)
+{
+  CoglPipelineLayerState state = COGL_PIPELINE_LAYER_STATE_FILTERS;
+  CoglPipelineLayer     *layer;
+  CoglPipelineLayer     *authority;
+  CoglPipelineLayer     *new;
+
+  g_return_if_fail (cogl_is_pipeline (pipeline));
+
+  /* Note: this will ensure that the layer exists, creating one if it
+   * doesn't already.
+   *
+   * Note: If the layer already existed it's possibly owned by another
+   * pipeline. If the layer is created then it will be owned by
+   * pipeline. */
+  layer = _cogl_pipeline_get_layer (pipeline, layer_index);
+
+  /* Now find the ancestor of the layer that is the authority for the
+   * state we want to change */
+  authority = _cogl_pipeline_layer_get_authority (layer, state);
+
+  if (authority->min_filter == min_filter &&
+      authority->mag_filter == mag_filter)
+    return;
+
+  new = _cogl_pipeline_layer_pre_change_notify (pipeline, layer, state);
+  if (new != layer)
+    layer = new;
+  else
+    {
+      /* If the original layer we found is currently the authority on
+       * the state we are changing see if we can revert to one of our
+       * ancestors being the authority. */
+      if (layer == authority &&
+          _cogl_pipeline_layer_get_parent (authority) != NULL)
+        {
+          CoglPipelineLayer *parent =
+            _cogl_pipeline_layer_get_parent (authority);
+          CoglPipelineLayer *old_authority =
+            _cogl_pipeline_layer_get_authority (parent, state);
+
+          if (old_authority->min_filter == min_filter &&
+              old_authority->mag_filter == mag_filter)
+            {
+              layer->differences &= ~state;
+
+              g_assert (layer->owner == pipeline);
+              if (layer->differences == 0)
+                _cogl_pipeline_prune_empty_layer_difference (pipeline,
+                                                             layer);
+              return;
+            }
+        }
+    }
+
+  layer->min_filter = min_filter;
+  layer->mag_filter = mag_filter;
+
+  /* If we weren't previously the authority on this state then we need
+   * to extended our differences mask and so it's possible that some
+   * of our ancestry will now become redundant, so we aim to reparent
+   * ourselves if that's true... */
+  if (layer != authority)
+    {
+      layer->differences |= state;
+      _cogl_pipeline_layer_prune_redundant_ancestry (layer);
+    }
+}
+
+void
+_cogl_pipeline_layer_hash_unit_state (CoglPipelineLayer *authority,
+                                      CoglPipelineLayer **authorities,
+                                      CoglPipelineHashState *state)
+{
+  int unit = authority->unit_index;
+  state->hash =
+    _cogl_util_one_at_a_time_hash (state->hash, &unit, sizeof (unit));
+}
+
+void
+_cogl_pipeline_layer_hash_texture_target_state (CoglPipelineLayer *authority,
+                                                CoglPipelineLayer **authorities,
+                                                CoglPipelineHashState *state)
+{
+  GLenum gl_target = authority->target;
+
+  state->hash =
+    _cogl_util_one_at_a_time_hash (state->hash, &gl_target, sizeof (gl_target));
+}
+
+void
+_cogl_pipeline_layer_hash_texture_data_state (CoglPipelineLayer *authority,
+                                              CoglPipelineLayer **authorities,
+                                              CoglPipelineHashState *state)
+{
+  GLuint gl_handle;
+
+  cogl_texture_get_gl_texture (authority->texture, &gl_handle, NULL);
+
+  state->hash =
+    _cogl_util_one_at_a_time_hash (state->hash, &gl_handle, sizeof (gl_handle));
+}
+
+void
+_cogl_pipeline_layer_hash_filters_state (CoglPipelineLayer *authority,
+                                         CoglPipelineLayer **authorities,
+                                         CoglPipelineHashState *state)
+{
+  unsigned int hash = state->hash;
+  hash = _cogl_util_one_at_a_time_hash (hash, &authority->mag_filter,
+                                        sizeof (authority->mag_filter));
+  hash = _cogl_util_one_at_a_time_hash (hash, &authority->min_filter,
+                                        sizeof (authority->min_filter));
+  state->hash = hash;
+}
+
+void
+_cogl_pipeline_layer_hash_wrap_modes_state (CoglPipelineLayer *authority,
+                                            CoglPipelineLayer **authorities,
+                                            CoglPipelineHashState *state)
+{
+  unsigned int hash = state->hash;
+  hash = _cogl_util_one_at_a_time_hash (hash, &authority->wrap_mode_s,
+                                        sizeof (authority->wrap_mode_s));
+  hash = _cogl_util_one_at_a_time_hash (hash, &authority->wrap_mode_t,
+                                        sizeof (authority->wrap_mode_t));
+  hash = _cogl_util_one_at_a_time_hash (hash, &authority->wrap_mode_p,
+                                        sizeof (authority->wrap_mode_p));
+  state->hash = hash;
+}
+
+void
+_cogl_pipeline_layer_hash_combine_state (CoglPipelineLayer *authority,
+                                         CoglPipelineLayer **authorities,
+                                         CoglPipelineHashState *state)
+{
+  unsigned int hash = state->hash;
+  CoglPipelineLayerBigState *b = authority->big_state;
+  int n_args;
+  int i;
+
+  hash = _cogl_util_one_at_a_time_hash (hash, &b->texture_combine_rgb_func,
+                                        sizeof (b->texture_combine_rgb_func));
+  n_args = _cogl_get_n_args_for_combine_func (b->texture_combine_rgb_func);
+  for (i = 0; i < n_args; i++)
+    {
+      hash =
+        _cogl_util_one_at_a_time_hash (hash, &b->texture_combine_rgb_src[i],
+                                       sizeof (b->texture_combine_rgb_src[i]));
+      hash =
+        _cogl_util_one_at_a_time_hash (hash, &b->texture_combine_rgb_op[i],
+                                       sizeof (b->texture_combine_rgb_op[i]));
+    }
+
+  hash = _cogl_util_one_at_a_time_hash (hash, &b->texture_combine_alpha_func,
+                                        sizeof (b->texture_combine_alpha_func));
+  n_args = _cogl_get_n_args_for_combine_func (b->texture_combine_alpha_func);
+  for (i = 0; i < n_args; i++)
+    {
+      hash =
+        _cogl_util_one_at_a_time_hash (hash, &b->texture_combine_alpha_src[i],
+                                       sizeof (b->texture_combine_alpha_src[i]));
+      hash =
+        _cogl_util_one_at_a_time_hash (hash, &b->texture_combine_alpha_op[i],
+                                       sizeof (b->texture_combine_alpha_op[i]));
+    }
+
+  state->hash = hash;
+}
+
+void
+_cogl_pipeline_layer_hash_combine_constant_state (CoglPipelineLayer *authority,
+                                                  CoglPipelineLayer **authorities,
+                                                  CoglPipelineHashState *state)
+{
+  CoglPipelineLayerBigState *b = authority->big_state;
+  gboolean need_hash = FALSE;
+  int n_args;
+  int i;
+
+  /* XXX: If the user also asked to hash the ALPHA_FUNC_STATE then it
+   * would be nice if we could combine the n_args loops in this
+   * function and _cogl_pipeline_layer_hash_combine_state.
+   */
+
+  n_args = _cogl_get_n_args_for_combine_func (b->texture_combine_rgb_func);
+  for (i = 0; i < n_args; i++)
+    {
+      if (b->texture_combine_rgb_src[i] ==
+          COGL_PIPELINE_COMBINE_SOURCE_CONSTANT)
+        {
+          /* XXX: should we be careful to only hash the alpha
+           * component in the COGL_PIPELINE_COMBINE_OP_SRC_ALPHA case? */
+          need_hash = TRUE;
+          goto done;
+        }
+    }
+
+  n_args = _cogl_get_n_args_for_combine_func (b->texture_combine_alpha_func);
+  for (i = 0; i < n_args; i++)
+    {
+      if (b->texture_combine_alpha_src[i] ==
+          COGL_PIPELINE_COMBINE_SOURCE_CONSTANT)
+        {
+          /* XXX: should we be careful to only hash the alpha
+           * component in the COGL_PIPELINE_COMBINE_OP_SRC_ALPHA case? */
+          need_hash = TRUE;
+          goto done;
+        }
+    }
+
+done:
+  if (need_hash)
+    {
+      float *constant = b->texture_combine_constant;
+      state->hash = _cogl_util_one_at_a_time_hash (state->hash, constant,
+                                                   sizeof (float) * 4);
+    }
+}
+
+void
+_cogl_pipeline_layer_hash_user_matrix_state (CoglPipelineLayer *authority,
+                                             CoglPipelineLayer **authorities,
+                                             CoglPipelineHashState *state)
+{
+  CoglPipelineLayerBigState *big_state = authority->big_state;
+  state->hash = _cogl_util_one_at_a_time_hash (state->hash, &big_state->matrix,
+                                               sizeof (float) * 16);
+}
+
+void
+_cogl_pipeline_layer_hash_point_sprite_state (CoglPipelineLayer *authority,
+                                              CoglPipelineLayer **authorities,
+                                              CoglPipelineHashState *state)
+{
+  CoglPipelineLayerBigState *big_state = authority->big_state;
+  state->hash =
+    _cogl_util_one_at_a_time_hash (state->hash, &big_state->point_sprite_coords,
+                                   sizeof (big_state->point_sprite_coords));
+}
+
+
diff --git a/cogl/cogl-pipeline-layer-state.h b/cogl/cogl-pipeline-layer-state.h
new file mode 100644
index 0000000..415e544
--- /dev/null
+++ b/cogl/cogl-pipeline-layer-state.h
@@ -0,0 +1,503 @@
+/*
+ * Cogl
+ *
+ * An object oriented GL/GLES Abstraction/Utility Layer
+ *
+ * Copyright (C) 2007,2008,2009 Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ */
+
+#if !defined(__COGL_H_INSIDE__) && !defined(CLUTTER_COMPILATION)
+#error "Only <cogl/cogl.h> can be included directly."
+#endif
+
+#ifndef __COGL_PIPELINE_LAYER_STATE_H__
+#define __COGL_PIPELINE_LAYER_STATE_H__
+
+#include <cogl/cogl-pipeline.h>
+#include <cogl/cogl-color.h>
+#include <cogl/cogl-matrix.h>
+#include <cogl/cogl-texture.h>
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#ifdef COGL_ENABLE_EXPERIMENTAL_API
+
+/**
+ * CoglPipelineFilter:
+ * @COGL_PIPELINE_FILTER_NEAREST: Measuring in manhatten distance from the,
+ *   current pixel center, use the nearest texture texel
+ * @COGL_PIPELINE_FILTER_LINEAR: Use the weighted average of the 4 texels
+ *   nearest the current pixel center
+ * @COGL_PIPELINE_FILTER_NEAREST_MIPMAP_NEAREST: Select the mimap level whose
+ *   texel size most closely matches the current pixel, and use the
+ *   %COGL_PIPELINE_FILTER_NEAREST criterion
+ * @COGL_PIPELINE_FILTER_LINEAR_MIPMAP_NEAREST: Select the mimap level whose
+ *   texel size most closely matches the current pixel, and use the
+ *   %COGL_PIPELINE_FILTER_LINEAR criterion
+ * @COGL_PIPELINE_FILTER_NEAREST_MIPMAP_LINEAR: Select the two mimap levels
+ *   whose texel size most closely matches the current pixel, use
+ *   the %COGL_PIPELINE_FILTER_NEAREST criterion on each one and take
+ *   their weighted average
+ * @COGL_PIPELINE_FILTER_LINEAR_MIPMAP_LINEAR: Select the two mimap levels
+ *   whose texel size most closely matches the current pixel, use
+ *   the %COGL_PIPELINE_FILTER_LINEAR criterion on each one and take
+ *   their weighted average
+ *
+ * Texture filtering is used whenever the current pixel maps either to more
+ * than one texture element (texel) or less than one. These filter enums
+ * correspond to different strategies used to come up with a pixel color, by
+ * possibly referring to multiple neighbouring texels and taking a weighted
+ * average or simply using the nearest texel.
+ */
+typedef enum {
+  COGL_PIPELINE_FILTER_NEAREST = 0x2600,
+  COGL_PIPELINE_FILTER_LINEAR = 0x2601,
+  COGL_PIPELINE_FILTER_NEAREST_MIPMAP_NEAREST = 0x2700,
+  COGL_PIPELINE_FILTER_LINEAR_MIPMAP_NEAREST = 0x2701,
+  COGL_PIPELINE_FILTER_NEAREST_MIPMAP_LINEAR = 0x2702,
+  COGL_PIPELINE_FILTER_LINEAR_MIPMAP_LINEAR = 0x2703
+} CoglPipelineFilter;
+/* NB: these values come from the equivalents in gl.h */
+
+/**
+ * CoglPipelineWrapMode:
+ * @COGL_PIPELINE_WRAP_MODE_REPEAT: The texture will be repeated. This
+ *   is useful for example to draw a tiled background.
+ * @COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE: The coordinates outside the
+ *   range 0â1 will sample copies of the edge pixels of the
+ *   texture. This is useful to avoid artifacts if only one copy of
+ *   the texture is being rendered.
+ * @COGL_PIPELINE_WRAP_MODE_AUTOMATIC: Cogl will try to automatically
+ *   decide which of the above two to use. For cogl_rectangle(), it
+ *   will use repeat mode if any of the texture coordinates are
+ *   outside the range 0â1, otherwise it will use clamp to edge. For
+ *   cogl_polygon() it will always use repeat mode. For
+ *   cogl_vertex_buffer_draw() it will use repeat mode except for
+ *   layers that have point sprite coordinate generation enabled. This
+ *   is the default value.
+ *
+ * The wrap mode specifies what happens when texture coordinates
+ * outside the range 0â1 are used. Note that if the filter mode is
+ * anything but %COGL_PIPELINE_FILTER_NEAREST then texels outside the
+ * range 0â1 might be used even when the coordinate is exactly 0 or 1
+ * because OpenGL will try to sample neighbouring pixels. For example
+ * if you are trying to render the full texture then you may get
+ * artifacts around the edges when the pixels from the other side are
+ * merged in if the wrap mode is set to repeat.
+ *
+ * Since: 2.0
+ */
+/* GL_ALWAYS is just used here as a value that is known not to clash
+ * with any valid GL wrap modes
+ *
+ * XXX: keep the values in sync with the CoglPipelineWrapModeInternal
+ * enum so no conversion is actually needed.
+ */
+typedef enum {
+  COGL_PIPELINE_WRAP_MODE_REPEAT = 0x2901,
+  COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE = 0x812F,
+  COGL_PIPELINE_WRAP_MODE_AUTOMATIC = 0x0207
+} CoglPipelineWrapMode;
+/* NB: these values come from the equivalents in gl.h */
+
+#define cogl_pipeline_set_layer_texture cogl_pipeline_set_layer_texture_EXP
+/**
+ * cogl_pipeline_set_layer:
+ * @pipeline: A #CoglPipeline object
+ * @layer_index: the index of the layer
+ * @texture: a #CoglHandle for the layer object
+ *
+ * In addition to the standard OpenGL lighting model a Cogl pipeline may have
+ * one or more layers comprised of textures that can be blended together in
+ * order, with a number of different texture combine modes. This function
+ * defines a new texture layer.
+ *
+ * The index values of multiple layers do not have to be consecutive; it is
+ * only their relative order that is important.
+ *
+ * <note>In the future, we may define other types of pipeline layers, such
+ * as purely GLSL based layers.</note>
+ *
+ * Since: 2.0
+ */
+void
+cogl_pipeline_set_layer_texture (CoglPipeline *pipeline,
+                                 int           layer_index,
+                                 CoglHandle    texture);
+
+#define cogl_pipeline_remove_layer cogl_pipeline_remove_layer_EXP
+/**
+ * cogl_pipeline_remove_layer:
+ * @pipeline: A #CoglPipeline object
+ * @layer_index: Specifies the layer you want to remove
+ *
+ * This function removes a layer from your pipeline
+ */
+void
+cogl_pipeline_remove_layer (CoglPipeline *pipeline,
+			    int           layer_index);
+
+#define cogl_pipeline_set_layer_combine cogl_pipeline_set_layer_combine_EXP
+/**
+ * cogl_pipeline_set_layer_combine:
+ * @pipeline: A #CoglPipeline object
+ * @layer_index: Specifies the layer you want define a combine function for
+ * @blend_string: A <link linkend="cogl-Blend-Strings">Cogl blend string</link>
+ *    describing the desired texture combine function.
+ * @error: A #GError that may report parse errors or lack of GPU/driver
+ *   support. May be %NULL, in which case a warning will be printed out if an
+ *   error is encountered.
+ *
+ * If not already familiar; you can refer
+ * <link linkend="cogl-Blend-Strings">here</link> for an overview of what blend
+ * strings are and there syntax.
+ *
+ * These are all the functions available for texture combining:
+ * <itemizedlist>
+ *   <listitem>REPLACE(arg0) = arg0</listitem>
+ *   <listitem>MODULATE(arg0, arg1) = arg0 x arg1</listitem>
+ *   <listitem>ADD(arg0, arg1) = arg0 + arg1</listitem>
+ *   <listitem>ADD_SIGNED(arg0, arg1) = arg0 + arg1 - 0.5</listitem>
+ *   <listitem>INTERPOLATE(arg0, arg1, arg2) = arg0 x arg2 + arg1 x (1 - arg2)</listitem>
+ *   <listitem>SUBTRACT(arg0, arg1) = arg0 - arg1</listitem>
+ *   <listitem>
+ *     <programlisting>
+ *  DOT3_RGB(arg0, arg1) = 4 x ((arg0[R] - 0.5)) * (arg1[R] - 0.5) +
+ *                              (arg0[G] - 0.5)) * (arg1[G] - 0.5) +
+ *                              (arg0[B] - 0.5)) * (arg1[B] - 0.5))
+ *     </programlisting>
+ *   </listitem>
+ *   <listitem>
+ *     <programlisting>
+ *  DOT3_RGBA(arg0, arg1) = 4 x ((arg0[R] - 0.5)) * (arg1[R] - 0.5) +
+ *                               (arg0[G] - 0.5)) * (arg1[G] - 0.5) +
+ *                               (arg0[B] - 0.5)) * (arg1[B] - 0.5))
+ *     </programlisting>
+ *   </listitem>
+ * </itemizedlist>
+ *
+ * Refer to the
+ * <link linkend="cogl-Blend-String-syntax">color-source syntax</link> for
+ * describing the arguments. The valid source names for texture combining
+ * are:
+ * <variablelist>
+ *   <varlistentry>
+ *     <term>TEXTURE</term>
+ *     <listitem>Use the color from the current texture layer</listitem>
+ *   </varlistentry>
+ *   <varlistentry>
+ *     <term>TEXTURE_0, TEXTURE_1, etc</term>
+ *     <listitem>Use the color from the specified texture layer</listitem>
+ *   </varlistentry>
+ *   <varlistentry>
+ *     <term>CONSTANT</term>
+ *     <listitem>Use the color from the constant given with
+ *     cogl_pipeline_set_layer_constant()</listitem>
+ *   </varlistentry>
+ *   <varlistentry>
+ *     <term>PRIMARY</term>
+ *     <listitem>Use the color of the pipeline as set with
+ *     cogl_pipeline_set_color()</listitem>
+ *   </varlistentry>
+ *   <varlistentry>
+ *     <term>PREVIOUS</term>
+ *     <listitem>Either use the texture color from the previous layer, or
+ *     if this is layer 0, use the color of the pipeline as set with
+ *     cogl_pipeline_set_color()</listitem>
+ *   </varlistentry>
+ * </variablelist>
+ *
+ * <refsect2 id="cogl-Layer-Combine-Examples">
+ *   <title>Layer Combine Examples</title>
+ *   <para>This is effectively what the default blending is:</para>
+ *   <informalexample><programlisting>
+ *   RGBA = MODULATE (PREVIOUS, TEXTURE)
+ *   </programlisting></informalexample>
+ *   <para>This could be used to cross-fade between two images, using
+ *   the alpha component of a constant as the interpolator. The constant
+ *   color is given by calling cogl_pipeline_set_layer_constant.</para>
+ *   <informalexample><programlisting>
+ *   RGBA = INTERPOLATE (PREVIOUS, TEXTURE, CONSTANT[A])
+ *   </programlisting></informalexample>
+ * </refsect2>
+ *
+ * <note>You can't give a multiplication factor for arguments as you can
+ * with blending.</note>
+ *
+ * Return value: %TRUE if the blend string was successfully parsed, and the
+ *   described texture combining is supported by the underlying driver and
+ *   or hardware. On failure, %FALSE is returned and @error is set
+ *
+ * Since: 2.0
+ */
+gboolean
+cogl_pipeline_set_layer_combine (CoglPipeline *pipeline,
+				 int           layer_index,
+				 const char   *blend_string,
+                                 GError      **error);
+
+#define cogl_pipeline_set_layer_combine_constant \
+  cogl_pipeline_set_layer_combine_constant_EXP
+/**
+ * cogl_pipeline_set_layer_combine_constant:
+ * @pipeline: A #CoglPipeline object
+ * @layer_index: Specifies the layer you want to specify a constant used
+ *               for texture combining
+ * @constant: The constant color you want
+ *
+ * When you are using the 'CONSTANT' color source in a layer combine
+ * description then you can use this function to define its value.
+ *
+ * Since: 2.0
+ */
+void
+cogl_pipeline_set_layer_combine_constant (CoglPipeline    *pipeline,
+                                          int              layer_index,
+                                          const CoglColor *constant);
+
+#define cogl_pipeline_set_layer_matrix cogl_pipeline_set_layer_matrix_EXP
+/**
+ * cogl_pipeline_set_layer_matrix:
+ * @pipeline: A #CoglPipeline object
+ * @layer_index: the index for the layer inside @pipeline
+ * @matrix: the transformation matrix for the layer
+ *
+ * This function lets you set a matrix that can be used to e.g. translate
+ * and rotate a single layer of a pipeline used to fill your geometry.
+ */
+void
+cogl_pipeline_set_layer_matrix (CoglPipeline     *pipeline,
+				int               layer_index,
+				const CoglMatrix *matrix);
+
+#define cogl_pipeline_get_n_layers cogl_pipeline_get_n_layers_EXP
+/**
+ * cogl_pipeline_get_n_layers:
+ * @pipeline: A #CoglPipeline object
+ *
+ * Retrieves the number of layers defined for the given @pipeline
+ *
+ * Return value: the number of layers
+ *
+ * Since: 2.0
+ */
+int
+cogl_pipeline_get_n_layers (CoglPipeline *pipeline);
+
+#define cogl_pipeline_set_layer_filters cogl_pipeline_set_layer_filters_EXP
+/**
+ * cogl_pipeline_set_layer_filters:
+ * @pipeline: A #CoglPipeline object
+ * @layer_index: the layer number to change.
+ * @min_filter: the filter used when scaling a texture down.
+ * @mag_filter: the filter used when magnifying a texture.
+ *
+ * Changes the decimation and interpolation filters used when a texture is
+ * drawn at other scales than 100%.
+ */
+void
+cogl_pipeline_set_layer_filters (CoglPipeline      *pipeline,
+                                 int                layer_index,
+                                 CoglPipelineFilter min_filter,
+                                 CoglPipelineFilter mag_filter);
+
+#define cogl_pipeline_set_layer_point_sprite_coords_enabled \
+  cogl_pipeline_set_layer_point_sprite_coords_enabled_EXP
+/**
+ * cogl_pipeline_set_layer_point_sprite_coords_enabled:
+ * @pipeline: a #CoglHandle to a pipeline.
+ * @layer_index: the layer number to change.
+ * @enable: whether to enable point sprite coord generation.
+ * @error: A return location for a GError, or NULL to ignore errors.
+ *
+ * When rendering points, if @enable is %TRUE then the texture
+ * coordinates for this layer will be replaced with coordinates that
+ * vary from 0.0 to 1.0 across the primitive. The top left of the
+ * point will have the coordinates 0.0,0.0 and the bottom right will
+ * have 1.0,1.0. If @enable is %FALSE then the coordinates will be
+ * fixed for the entire point.
+ *
+ * This function will only work if %COGL_FEATURE_POINT_SPRITE is
+ * available. If the feature is not available then the function will
+ * return %FALSE and set @error.
+ *
+ * Return value: %TRUE if the function succeeds, %FALSE otherwise.
+ * Since: 2.0
+ */
+gboolean
+cogl_pipeline_set_layer_point_sprite_coords_enabled (CoglPipeline *pipeline,
+                                                     int           layer_index,
+                                                     gboolean      enable,
+                                                     GError      **error);
+
+#define cogl_pipeline_get_layer_point_sprite_coords_enabled \
+  cogl_pipeline_get_layer_point_sprite_coords_enabled_EXP
+/**
+ * cogl_pipeline_get_layer_point_sprite_coords_enabled:
+ * @pipeline: a #CoglHandle to a pipeline.
+ * @layer_index: the layer number to check.
+ *
+ * Gets whether point sprite coordinate generation is enabled for this
+ * texture layer.
+ *
+ * Return value: whether the texture coordinates will be replaced with
+ * point sprite coordinates.
+ *
+ * Since: 2.0
+ */
+gboolean
+cogl_pipeline_get_layer_point_sprite_coords_enabled (CoglPipeline *pipeline,
+                                                     int           layer_index);
+
+#define cogl_pipeline_get_layer_wrap_mode_s \
+  cogl_pipeline_get_layer_wrap_mode_s_EXP
+/**
+ * cogl_pipeline_get_layer_wrap_mode_s:
+ * @pipeline: A #CoglPipeline object
+ * @layer_index: the layer number to change.
+ *
+ * Returns the wrap mode for the 's' coordinate of texture lookups on this
+ * layer.
+ *
+ * Return value: the wrap mode for the 's' coordinate of texture lookups on
+ * this layer.
+ *
+ * Since: 1.6
+ */
+CoglPipelineWrapMode
+cogl_pipeline_get_layer_wrap_mode_s (CoglPipeline *pipeline,
+                                     int           layer_index);
+
+#define cogl_pipeline_set_layer_wrap_mode_s \
+  cogl_pipeline_set_layer_wrap_mode_s_EXP
+/**
+ * cogl_pipeline_set_layer_wrap_mode_s:
+ * @pipeline: A #CoglPipeline object
+ * @layer_index: the layer number to change.
+ * @mode: the new wrap mode
+ *
+ * Sets the wrap mode for the 's' coordinate of texture lookups on this layer.
+ *
+ * Since: 2.0
+ */
+void
+cogl_pipeline_set_layer_wrap_mode_s (CoglPipeline        *pipeline,
+                                     int                  layer_index,
+                                     CoglPipelineWrapMode mode);
+
+#define cogl_pipeline_get_layer_wrap_mode_t \
+  cogl_pipeline_get_layer_wrap_mode_t_EXP
+/**
+ * cogl_pipeline_get_layer_wrap_mode_t:
+ * @pipeline: A #CoglPipeline object
+ * @layer_index: the layer number to change.
+ *
+ * Returns the wrap mode for the 't' coordinate of texture lookups on this
+ * layer.
+ *
+ * Return value: the wrap mode for the 't' coordinate of texture lookups on
+ * this layer.
+ *
+ * Since: 1.6
+ */
+CoglPipelineWrapMode
+cogl_pipeline_get_layer_wrap_mode_t (CoglPipeline *pipeline,
+                                     int           layer_index);
+
+
+#define cogl_pipeline_set_layer_wrap_mode_t \
+  cogl_pipeline_set_layer_wrap_mode_t_EXP
+/**
+ * cogl_pipeline_set_layer_wrap_mode_t:
+ * @pipeline: A #CoglPipeline object
+ * @layer_index: the layer number to change.
+ * @mode: the new wrap mode
+ *
+ * Sets the wrap mode for the 't' coordinate of texture lookups on this layer.
+ *
+ * Since: 2.0
+ */
+void
+cogl_pipeline_set_layer_wrap_mode_t (CoglPipeline        *pipeline,
+                                     int                  layer_index,
+                                     CoglPipelineWrapMode mode);
+
+#define cogl_pipeline_get_layer_wrap_mode_p \
+  cogl_pipeline_get_layer_wrap_mode_p_EXP
+/**
+ * cogl_pipeline_get_layer_wrap_mode_p:
+ * @pipeline: A #CoglPipeline object
+ * @layer_index: the layer number to change.
+ *
+ * Returns the wrap mode for the 'p' coordinate of texture lookups on this
+ * layer.
+ *
+ * Return value: the wrap mode for the 'p' coordinate of texture lookups on
+ * this layer.
+ *
+ * Since: 1.6
+ */
+CoglPipelineWrapMode
+cogl_pipeline_get_layer_wrap_mode_p (CoglPipeline *pipeline,
+                                     int           layer_index);
+
+#define cogl_pipeline_set_layer_wrap_mode_p \
+  cogl_pipeline_set_layer_wrap_mode_p_EXP
+/**
+ * cogl_pipeline_set_layer_wrap_mode_p:
+ * @pipeline: A #CoglPipeline object
+ * @layer_index: the layer number to change.
+ * @mode: the new wrap mode
+ *
+ * Sets the wrap mode for the 'p' coordinate of texture lookups on
+ * this layer. 'p' is the third coordinate.
+ *
+ * Since: 2.0
+ */
+void
+cogl_pipeline_set_layer_wrap_mode_p (CoglPipeline        *pipeline,
+                                     int                  layer_index,
+                                     CoglPipelineWrapMode mode);
+
+#define cogl_pipeline_set_layer_wrap_mode \
+  cogl_pipeline_set_layer_wrap_mode_EXP
+/**
+ * cogl_pipeline_set_layer_wrap_mode:
+ * @pipeline: A #CoglPipeline object
+ * @layer_index: the layer number to change.
+ * @mode: the new wrap mode
+ *
+ * Sets the wrap mode for all three coordinates of texture lookups on
+ * this layer. This is equivalent to calling
+ * cogl_pipeline_set_layer_wrap_mode_s(),
+ * cogl_pipeline_set_layer_wrap_mode_t() and
+ * cogl_pipeline_set_layer_wrap_mode_p() separately.
+ *
+ * Since: 2.0
+ */
+void
+cogl_pipeline_set_layer_wrap_mode (CoglPipeline        *pipeline,
+                                   int                  layer_index,
+                                   CoglPipelineWrapMode mode);
+
+#endif /* COGL_ENABLE_EXPERIMENTAL_API */
+
+G_END_DECLS
+
+#endif /* __COGL_PIPELINE_LAYER_STATE_H__ */
diff --git a/cogl/cogl-pipeline-private.h b/cogl/cogl-pipeline-private.h
index 628ba35..7e019c2 100644
--- a/cogl/cogl-pipeline-private.h
+++ b/cogl/cogl-pipeline-private.h
@@ -860,6 +860,11 @@ _cogl_pipeline_get_authority (CoglPipeline *pipeline,
 typedef gboolean (*CoglPipelineStateComparitor) (CoglPipeline *authority0,
                                                  CoglPipeline *authority1);
 
+typedef gboolean
+(*CoglPipelineLayerStateComparitor) (CoglPipelineLayer *authority0,
+                                     CoglPipelineLayer *authority1);
+
+
 void
 _cogl_pipeline_update_authority (CoglPipeline *pipeline,
                                  CoglPipeline *authority,
@@ -878,6 +883,32 @@ _cogl_pipeline_prune_redundant_ancestry (CoglPipeline *pipeline);
 void _cogl_pipeline_update_blend_enable (CoglPipeline *pipeline,
                                          CoglPipelineState changes);
 
+CoglPipelineLayer *
+_cogl_pipeline_get_layer (CoglPipeline *pipeline,
+                          int layer_index);
+
+gboolean
+_cogl_is_pipeline_layer (void *object);
+
+static inline CoglPipelineLayer *
+_cogl_pipeline_layer_get_parent (CoglPipelineLayer *layer)
+{
+  CoglPipelineNode *parent_node = COGL_PIPELINE_NODE (layer)->parent;
+  return COGL_PIPELINE_LAYER (parent_node);
+}
+
+CoglPipelineLayer *
+_cogl_pipeline_layer_pre_change_notify (CoglPipeline *required_owner,
+                                        CoglPipelineLayer *layer,
+                                        CoglPipelineLayerState change);
+
+void
+_cogl_pipeline_layer_prune_redundant_ancestry (CoglPipelineLayer *layer);
+
+void
+_cogl_pipeline_prune_empty_layer_difference (CoglPipeline *layers_authority,
+                                             CoglPipelineLayer *layer);
+
 /*
  * SECTION:cogl-pipeline-internals
  * @short_description: Functions for creating custom primitives that make use
diff --git a/cogl/cogl-pipeline.c b/cogl/cogl-pipeline.c
index 384b646..70bda8f 100644
--- a/cogl/cogl-pipeline.c
+++ b/cogl/cogl-pipeline.c
@@ -38,6 +38,7 @@
 #include "cogl-pipeline-private.h"
 #include "cogl-pipeline-opengl-private.h"
 #include "cogl-pipeline-state-private.h"
+#include "cogl-pipeline-layer-state-private.h"
 #include "cogl-texture-private.h"
 #include "cogl-blend-string.h"
 #include "cogl-journal-private.h"
@@ -556,16 +557,6 @@ _cogl_pipeline_get_real_blend_enabled (CoglPipeline *pipeline)
   return pipeline->real_blend_enable;
 }
 
-/* XXX: Think twice before making this non static since it is used
- * heavily and we expect the compiler to inline it...
- */
-static CoglPipelineLayer *
-_cogl_pipeline_layer_get_parent (CoglPipelineLayer *layer)
-{
-  CoglPipelineNode *parent_node = COGL_PIPELINE_NODE (layer)->parent;
-  return COGL_PIPELINE_LAYER (parent_node);
-}
-
 CoglPipelineLayer *
 _cogl_pipeline_layer_get_authority (CoglPipelineLayer *layer,
                                     unsigned long difference)
@@ -1730,7 +1721,7 @@ _cogl_pipeline_layer_init_multi_property_sparse_state (
  * required_owner can only by NULL for new, currently unowned layers
  * with no dependants.
  */
-static CoglPipelineLayer *
+CoglPipelineLayer *
 _cogl_pipeline_layer_pre_change_notify (CoglPipeline *required_owner,
                                         CoglPipelineLayer *layer,
                                         CoglPipelineLayerState change)
@@ -1847,7 +1838,7 @@ _cogl_pipeline_layer_set_parent (CoglPipelineLayer *layer,
 /* XXX: This is duplicated logic; the same as for
  * _cogl_pipeline_prune_redundant_ancestry it would be nice to find a
  * way to consolidate these functions! */
-static void
+void
 _cogl_pipeline_layer_prune_redundant_ancestry (CoglPipelineLayer *layer)
 {
   CoglPipelineLayer *new_parent = _cogl_pipeline_layer_get_parent (layer);
@@ -1862,73 +1853,6 @@ _cogl_pipeline_layer_prune_redundant_ancestry (CoglPipelineLayer *layer)
   _cogl_pipeline_layer_set_parent (layer, new_parent);
 }
 
-/*
- * XXX: consider special casing layer->unit_index so it's not a sparse
- * property so instead we can assume it's valid for all layer
- * instances.
- * - We would need to initialize ->unit_index in
- *   _cogl_pipeline_layer_copy ().
- *
- * XXX: If you use this API you should consider that the given layer
- * might not be writeable and so a new derived layer will be allocated
- * and modified instead. The layer modified will be returned so you
- * can identify when this happens.
- */
-static CoglPipelineLayer *
-_cogl_pipeline_set_layer_unit (CoglPipeline *required_owner,
-                               CoglPipelineLayer *layer,
-                               int unit_index)
-{
-  CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_UNIT;
-  CoglPipelineLayer *authority =
-    _cogl_pipeline_layer_get_authority (layer, change);
-  CoglPipelineLayer *new;
-
-  if (authority->unit_index == unit_index)
-    return layer;
-
-  new =
-    _cogl_pipeline_layer_pre_change_notify (required_owner,
-                                            layer,
-                                            change);
-  if (new != layer)
-    layer = new;
-  else
-    {
-      /* If the layer we found is currently the authority on the state
-       * we are changing see if we can revert to one of our ancestors
-       * being the authority. */
-      if (layer == authority &&
-          _cogl_pipeline_layer_get_parent (authority) != NULL)
-        {
-          CoglPipelineLayer *parent =
-            _cogl_pipeline_layer_get_parent (authority);
-          CoglPipelineLayer *old_authority =
-            _cogl_pipeline_layer_get_authority (parent, change);
-
-          if (old_authority->unit_index == unit_index)
-            {
-              layer->differences &= ~change;
-              return layer;
-            }
-        }
-    }
-
-  layer->unit_index = unit_index;
-
-  /* If we weren't previously the authority on this state then we need
-   * to extended our differences mask and so it's possible that some
-   * of our ancestry will now become redundant, so we aim to reparent
-   * ourselves if that's true... */
-  if (layer != authority)
-    {
-      layer->differences |= change;
-      _cogl_pipeline_layer_prune_redundant_ancestry (layer);
-    }
-
-  return layer;
-}
-
 typedef struct
 {
   /* The layer we are trying to find */
@@ -2029,7 +1953,7 @@ _cogl_pipeline_get_layer_info (CoglPipeline *pipeline,
     }
 }
 
-static CoglPipelineLayer *
+CoglPipelineLayer *
 _cogl_pipeline_get_layer (CoglPipeline *pipeline,
                           int layer_index)
 {
@@ -2102,26 +2026,7 @@ _cogl_pipeline_get_layer (CoglPipeline *pipeline,
   return layer;
 }
 
-CoglHandle
-_cogl_pipeline_layer_get_texture_real (CoglPipelineLayer *layer)
-{
-  CoglPipelineLayer *authority =
-    _cogl_pipeline_layer_get_authority (layer,
-                                        COGL_PIPELINE_LAYER_STATE_TEXTURE_DATA);
-
-  return authority->texture;
-}
-
-CoglHandle
-_cogl_pipeline_get_layer_texture (CoglPipeline *pipeline,
-                                  int layer_index)
-{
-  CoglPipelineLayer *layer =
-    _cogl_pipeline_get_layer (pipeline, layer_index);
-  return _cogl_pipeline_layer_get_texture (layer);
-}
-
-static void
+void
 _cogl_pipeline_prune_empty_layer_difference (CoglPipeline *layers_authority,
                                              CoglPipelineLayer *layer)
 {
@@ -2199,202 +2104,6 @@ _cogl_pipeline_prune_empty_layer_difference (CoglPipeline *layers_authority,
     }
 }
 
-static void
-_cogl_pipeline_set_layer_texture_target (CoglPipeline *pipeline,
-                                         int layer_index,
-                                         GLenum target)
-{
-  CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_TEXTURE_TARGET;
-  CoglPipelineLayer *layer;
-  CoglPipelineLayer *authority;
-  CoglPipelineLayer *new;
-
-  /* Note: this will ensure that the layer exists, creating one if it
-   * doesn't already.
-   *
-   * Note: If the layer already existed it's possibly owned by another
-   * pipeline. If the layer is created then it will be owned by
-   * pipeline. */
-  layer = _cogl_pipeline_get_layer (pipeline, layer_index);
-
-  /* Now find the ancestor of the layer that is the authority for the
-   * state we want to change */
-  authority = _cogl_pipeline_layer_get_authority (layer, change);
-
-  if (target == authority->target)
-    return;
-
-  new = _cogl_pipeline_layer_pre_change_notify (pipeline, layer, change);
-  if (new != layer)
-    layer = new;
-  else
-    {
-      /* If the original layer we found is currently the authority on
-       * the state we are changing see if we can revert to one of our
-       * ancestors being the authority. */
-      if (layer == authority &&
-          _cogl_pipeline_layer_get_parent (authority) != NULL)
-        {
-          CoglPipelineLayer *parent =
-            _cogl_pipeline_layer_get_parent (authority);
-          CoglPipelineLayer *old_authority =
-            _cogl_pipeline_layer_get_authority (parent, change);
-
-          if (old_authority->target == target)
-            {
-              layer->differences &= ~change;
-
-              g_assert (layer->owner == pipeline);
-              if (layer->differences == 0)
-                _cogl_pipeline_prune_empty_layer_difference (pipeline,
-                                                             layer);
-              goto changed;
-            }
-        }
-    }
-
-  layer->target = target;
-
-  /* If we weren't previously the authority on this state then we need
-   * to extended our differences mask and so it's possible that some
-   * of our ancestry will now become redundant, so we aim to reparent
-   * ourselves if that's true... */
-  if (layer != authority)
-    {
-      layer->differences |= change;
-      _cogl_pipeline_layer_prune_redundant_ancestry (layer);
-    }
-
-changed:
-
-  _cogl_pipeline_update_blend_enable (pipeline, COGL_PIPELINE_STATE_LAYERS);
-}
-
-static void
-_cogl_pipeline_set_layer_texture_data (CoglPipeline *pipeline,
-                                       int layer_index,
-                                       CoglHandle texture)
-{
-  CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_TEXTURE_DATA;
-  CoglPipelineLayer *layer;
-  CoglPipelineLayer *authority;
-  CoglPipelineLayer *new;
-
-  /* Note: this will ensure that the layer exists, creating one if it
-   * doesn't already.
-   *
-   * Note: If the layer already existed it's possibly owned by another
-   * pipeline. If the layer is created then it will be owned by
-   * pipeline. */
-  layer = _cogl_pipeline_get_layer (pipeline, layer_index);
-
-  /* Now find the ancestor of the layer that is the authority for the
-   * state we want to change */
-  authority = _cogl_pipeline_layer_get_authority (layer, change);
-
-  if (authority->texture == texture)
-    return;
-
-  new = _cogl_pipeline_layer_pre_change_notify (pipeline, layer, change);
-  if (new != layer)
-    layer = new;
-  else
-    {
-      /* If the original layer we found is currently the authority on
-       * the state we are changing see if we can revert to one of our
-       * ancestors being the authority. */
-      if (layer == authority &&
-          _cogl_pipeline_layer_get_parent (authority) != NULL)
-        {
-          CoglPipelineLayer *parent =
-            _cogl_pipeline_layer_get_parent (authority);
-          CoglPipelineLayer *old_authority =
-            _cogl_pipeline_layer_get_authority (parent, change);
-
-          if (old_authority->texture == texture)
-            {
-              layer->differences &= ~change;
-
-              if (layer->texture != COGL_INVALID_HANDLE)
-                cogl_handle_unref (layer->texture);
-
-              g_assert (layer->owner == pipeline);
-              if (layer->differences == 0)
-                _cogl_pipeline_prune_empty_layer_difference (pipeline,
-                                                             layer);
-              goto changed;
-            }
-        }
-    }
-
-  if (texture != COGL_INVALID_HANDLE)
-    cogl_handle_ref (texture);
-  if (layer == authority &&
-      layer->texture != COGL_INVALID_HANDLE)
-    cogl_handle_unref (layer->texture);
-  layer->texture = texture;
-
-  /* If we weren't previously the authority on this state then we need
-   * to extended our differences mask and so it's possible that some
-   * of our ancestry will now become redundant, so we aim to reparent
-   * ourselves if that's true... */
-  if (layer != authority)
-    {
-      layer->differences |= change;
-      _cogl_pipeline_layer_prune_redundant_ancestry (layer);
-    }
-
-changed:
-
-  _cogl_pipeline_update_blend_enable (pipeline, COGL_PIPELINE_STATE_LAYERS);
-}
-
-/* A convenience for querying the target of a given texture that
- * notably returns 0 for NULL textures - so we can say that a layer
- * with no associated CoglTexture will have a texture target of 0.
- */
-static GLenum
-get_texture_target (CoglHandle texture)
-{
-  GLuint ignore_handle;
-  GLenum gl_target;
-
-  g_return_val_if_fail (texture, 0);
-
-  cogl_texture_get_gl_texture (texture, &ignore_handle, &gl_target);
-
-  return gl_target;
-}
-
-void
-cogl_pipeline_set_layer_texture (CoglPipeline *pipeline,
-                                 int layer_index,
-                                 CoglHandle texture)
-{
-  /* For the convenience of fragend code we separate texture state
-   * into the "target" and the "data", and setting a layer texture
-   * updates both of these properties.
-   *
-   * One example for why this is helpful is that the fragends may
-   * cache programs they generate and want to re-use those programs
-   * with all pipelines having equivalent fragment processing state.
-   * For the sake of determining if pipelines have equivalent fragment
-   * processing state we don't need to compare that the same
-   * underlying texture objects are referenced by the pipelines but we
-   * do need to see if they use the same texture targets. Making this
-   * distinction is much simpler if they are in different state
-   * groups.
-   *
-   * Note: if a NULL texture is set then we leave the target unchanged
-   * so we can avoid needlessly invalidating any associated fragment
-   * program.
-   */
-  if (texture)
-    _cogl_pipeline_set_layer_texture_target (pipeline, layer_index,
-                                             get_texture_target (texture));
-  _cogl_pipeline_set_layer_texture_data (pipeline, layer_index, texture);
-}
-
 typedef struct
 {
   int i;
@@ -2449,584 +2158,115 @@ fallback_layer_cb (CoglPipelineLayer *layer, void *user_data)
   return TRUE;
 }
 
-void
-_cogl_pipeline_set_layer_wrap_modes (CoglPipeline        *pipeline,
-                                     CoglPipelineLayer   *layer,
-                                     CoglPipelineLayer   *authority,
-                                     CoglPipelineWrapModeInternal wrap_mode_s,
-                                     CoglPipelineWrapModeInternal wrap_mode_t,
-                                     CoglPipelineWrapModeInternal wrap_mode_p)
-{
-  CoglPipelineLayer     *new;
-  CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_WRAP_MODES;
-
-  if (authority->wrap_mode_s == wrap_mode_s &&
-      authority->wrap_mode_t == wrap_mode_t &&
-      authority->wrap_mode_p == wrap_mode_p)
-    return;
-
-  new = _cogl_pipeline_layer_pre_change_notify (pipeline, layer, change);
-  if (new != layer)
-    layer = new;
-  else
-    {
-      /* If the original layer we found is currently the authority on
-       * the state we are changing see if we can revert to one of our
-       * ancestors being the authority. */
-      if (layer == authority &&
-          _cogl_pipeline_layer_get_parent (authority) != NULL)
-        {
-          CoglPipelineLayer *parent =
-            _cogl_pipeline_layer_get_parent (authority);
-          CoglPipelineLayer *old_authority =
-            _cogl_pipeline_layer_get_authority (parent, change);
-
-          if (old_authority->wrap_mode_s == wrap_mode_s &&
-              old_authority->wrap_mode_t == wrap_mode_t &&
-              old_authority->wrap_mode_p == wrap_mode_p)
-            {
-              layer->differences &= ~change;
-
-              g_assert (layer->owner == pipeline);
-              if (layer->differences == 0)
-                _cogl_pipeline_prune_empty_layer_difference (pipeline,
-                                                             layer);
-              return;
-            }
-        }
-    }
-
-  layer->wrap_mode_s = wrap_mode_s;
-  layer->wrap_mode_t = wrap_mode_t;
-  layer->wrap_mode_p = wrap_mode_p;
-
-  /* If we weren't previously the authority on this state then we need
-   * to extended our differences mask and so it's possible that some
-   * of our ancestry will now become redundant, so we aim to reparent
-   * ourselves if that's true... */
-  if (layer != authority)
-    {
-      layer->differences |= change;
-      _cogl_pipeline_layer_prune_redundant_ancestry (layer);
-    }
-}
-
-static CoglPipelineWrapModeInternal
-public_to_internal_wrap_mode (CoglPipelineWrapMode mode)
-{
-  return (CoglPipelineWrapModeInternal)mode;
-}
-
-static CoglPipelineWrapMode
-internal_to_public_wrap_mode (CoglPipelineWrapModeInternal internal_mode)
+typedef struct
 {
-  g_return_val_if_fail (internal_mode !=
-                        COGL_PIPELINE_WRAP_MODE_INTERNAL_CLAMP_TO_BORDER,
-                        COGL_PIPELINE_WRAP_MODE_AUTOMATIC);
-  return (CoglPipelineWrapMode)internal_mode;
-}
+  CoglPipeline *pipeline;
+  CoglHandle texture;
+} CoglPipelineOverrideLayerState;
 
-void
-cogl_pipeline_set_layer_wrap_mode_s (CoglPipeline *pipeline,
-                                     int layer_index,
-                                     CoglPipelineWrapMode mode)
+static gboolean
+override_layer_texture_cb (CoglPipelineLayer *layer, void *user_data)
 {
-  CoglPipelineLayerState       change = COGL_PIPELINE_LAYER_STATE_WRAP_MODES;
-  CoglPipelineLayer           *layer;
-  CoglPipelineLayer           *authority;
-  CoglPipelineWrapModeInternal internal_mode =
-    public_to_internal_wrap_mode (mode);
-
-  g_return_if_fail (cogl_is_pipeline (pipeline));
-
-  /* Note: this will ensure that the layer exists, creating one if it
-   * doesn't already.
-   *
-   * Note: If the layer already existed it's possibly owned by another
-   * pipeline. If the layer is created then it will be owned by
-   * pipeline. */
-  layer = _cogl_pipeline_get_layer (pipeline, layer_index);
+  CoglPipelineOverrideLayerState *state = user_data;
 
-  /* Now find the ancestor of the layer that is the authority for the
-   * state we want to change */
-  authority = _cogl_pipeline_layer_get_authority (layer, change);
+  cogl_pipeline_set_layer_texture (state->pipeline,
+                                   layer->index,
+                                   state->texture);
 
-  _cogl_pipeline_set_layer_wrap_modes (pipeline, layer, authority,
-                                       internal_mode,
-                                       authority->wrap_mode_t,
-                                       authority->wrap_mode_p);
+  return TRUE;
 }
 
 void
-cogl_pipeline_set_layer_wrap_mode_t (CoglPipeline        *pipeline,
-                                     int                  layer_index,
-                                     CoglPipelineWrapMode mode)
+_cogl_pipeline_apply_overrides (CoglPipeline *pipeline,
+                                CoglPipelineFlushOptions *options)
 {
-  CoglPipelineLayerState       change = COGL_PIPELINE_LAYER_STATE_WRAP_MODES;
-  CoglPipelineLayer           *layer;
-  CoglPipelineLayer           *authority;
-  CoglPipelineWrapModeInternal internal_mode =
-    public_to_internal_wrap_mode (mode);
+  COGL_STATIC_COUNTER (apply_overrides_counter,
+                       "pipeline overrides counter",
+                       "Increments each time we have to apply "
+                       "override options to a pipeline",
+                       0 /* no application private data */);
 
-  g_return_if_fail (cogl_is_pipeline (pipeline));
+  COGL_COUNTER_INC (_cogl_uprof_context, apply_overrides_counter);
 
-  /* Note: this will ensure that the layer exists, creating one if it
-   * doesn't already.
-   *
-   * Note: If the layer already existed it's possibly owned by another
-   * pipeline. If the layer is created then it will be owned by
-   * pipeline. */
-  layer = _cogl_pipeline_get_layer (pipeline, layer_index);
+  if (options->flags & COGL_PIPELINE_FLUSH_DISABLE_MASK)
+    {
+      int i;
 
-  /* Now find the ancestor of the layer that is the authority for the
-   * state we want to change */
-  authority = _cogl_pipeline_layer_get_authority (layer, change);
+      /* NB: we can assume that once we see one bit to disable
+       * a layer, all subsequent layers are also disabled. */
+      for (i = 0; i < 32 && options->disable_layers & (1<<i); i++)
+        ;
 
-  _cogl_pipeline_set_layer_wrap_modes (pipeline, layer, authority,
-                                       authority->wrap_mode_s,
-                                       internal_mode,
-                                       authority->wrap_mode_p);
-}
+      _cogl_pipeline_prune_to_n_layers (pipeline, i);
+    }
 
-/* The rationale for naming the third texture coordinate 'p' instead
-   of OpenGL's usual 'r' is that 'r' conflicts with the usual naming
-   of the 'red' component when treating a vector as a color. Under
-   GLSL this is awkward because the texture swizzling for a vector
-   uses a single letter for each component and the names for colors,
-   textures and positions are synonymous. GLSL works around this by
-   naming the components of the texture s, t, p and q. Cogl already
-   effectively already exposes this naming because it exposes GLSL so
-   it makes sense to use that naming consistently. Another alternative
-   could be u, v and w. This is what Blender and Direct3D use. However
-   the w component conflicts with the w component of a position
-   vertex.  */
-void
-cogl_pipeline_set_layer_wrap_mode_p (CoglPipeline        *pipeline,
-                                     int                  layer_index,
-                                     CoglPipelineWrapMode mode)
-{
-  CoglPipelineLayerState       change = COGL_PIPELINE_LAYER_STATE_WRAP_MODES;
-  CoglPipelineLayer           *layer;
-  CoglPipelineLayer           *authority;
-  CoglPipelineWrapModeInternal internal_mode =
-    public_to_internal_wrap_mode (mode);
+  if (options->flags & COGL_PIPELINE_FLUSH_FALLBACK_MASK)
+    {
+      CoglPipelineFallbackState state;
 
-  g_return_if_fail (cogl_is_pipeline (pipeline));
+      state.i = 0;
+      state.pipeline = pipeline;
+      state.fallback_layers = options->fallback_layers;
 
-  /* Note: this will ensure that the layer exists, creating one if it
-   * doesn't already.
-   *
-   * Note: If the layer already existed it's possibly owned by another
-   * pipeline. If the layer is created then it will be owned by
-   * pipeline. */
-  layer = _cogl_pipeline_get_layer (pipeline, layer_index);
+      _cogl_pipeline_foreach_layer_internal (pipeline,
+                                             fallback_layer_cb,
+                                             &state);
+    }
 
-  /* Now find the ancestor of the layer that is the authority for the
-   * state we want to change */
-  authority = _cogl_pipeline_layer_get_authority (layer, change);
+  if (options->flags & COGL_PIPELINE_FLUSH_LAYER0_OVERRIDE)
+    {
+      CoglPipelineOverrideLayerState state;
+
+      _cogl_pipeline_prune_to_n_layers (pipeline, 1);
 
-  _cogl_pipeline_set_layer_wrap_modes (pipeline, layer, authority,
-                                       authority->wrap_mode_s,
-                                       authority->wrap_mode_t,
-                                       internal_mode);
+      /* NB: we are overriding the first layer, but we don't know
+       * the user's given layer_index, which is why we use
+       * _cogl_pipeline_foreach_layer_internal() here even though we know
+       * there's only one layer. */
+      state.pipeline = pipeline;
+      state.texture = options->layer0_override_texture;
+      _cogl_pipeline_foreach_layer_internal (pipeline,
+                                             override_layer_texture_cb,
+                                             &state);
+    }
 }
 
-void
-cogl_pipeline_set_layer_wrap_mode (CoglPipeline        *pipeline,
-                                   int                  layer_index,
-                                   CoglPipelineWrapMode mode)
+/* Determine the mask of differences between two layers.
+ *
+ * XXX: If layers and pipelines could both be cast to a common Tree
+ * type of some kind then we could have a unified
+ * compare_differences() function.
+ */
+unsigned long
+_cogl_pipeline_layer_compare_differences (CoglPipelineLayer *layer0,
+                                          CoglPipelineLayer *layer1)
 {
-  CoglPipelineLayerState       change = COGL_PIPELINE_LAYER_STATE_WRAP_MODES;
-  CoglPipelineLayer           *layer;
-  CoglPipelineLayer           *authority;
-  CoglPipelineWrapModeInternal internal_mode =
-    public_to_internal_wrap_mode (mode);
+  CoglPipelineLayer *node0;
+  CoglPipelineLayer *node1;
+  int len0;
+  int len1;
+  int len0_index;
+  int len1_index;
+  int count;
+  int i;
+  CoglPipelineLayer *common_ancestor = NULL;
+  unsigned long layers_difference = 0;
 
-  g_return_if_fail (cogl_is_pipeline (pipeline));
+  _COGL_GET_CONTEXT (ctx, 0);
 
-  /* Note: this will ensure that the layer exists, creating one if it
-   * doesn't already.
+  /* Algorithm:
    *
-   * Note: If the layer already existed it's possibly owned by another
-   * pipeline. If the layer is created then it will be owned by
-   * pipeline. */
-  layer = _cogl_pipeline_get_layer (pipeline, layer_index);
-
-  /* Now find the ancestor of the layer that is the authority for the
-   * state we want to change */
-  authority = _cogl_pipeline_layer_get_authority (layer, change);
-
-  _cogl_pipeline_set_layer_wrap_modes (pipeline, layer, authority,
-                                       internal_mode,
-                                       internal_mode,
-                                       internal_mode);
-  /* XXX: I wonder if we should really be duplicating the mode into
-   * the 'r' wrap mode too? */
-}
-
-/* FIXME: deprecate this API */
-CoglPipelineWrapMode
-_cogl_pipeline_layer_get_wrap_mode_s (CoglPipelineLayer *layer)
-{
-  CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_WRAP_MODES;
-  CoglPipelineLayer     *authority;
-
-  g_return_val_if_fail (_cogl_is_pipeline_layer (layer), FALSE);
-
-  /* Now find the ancestor of the layer that is the authority for the
-   * state we want to change */
-  authority = _cogl_pipeline_layer_get_authority (layer, change);
-
-  return internal_to_public_wrap_mode (authority->wrap_mode_s);
-}
-
-CoglPipelineWrapMode
-cogl_pipeline_get_layer_wrap_mode_s (CoglPipeline *pipeline, int layer_index)
-{
-  CoglPipelineLayer *layer;
-
-  g_return_val_if_fail (cogl_is_pipeline (pipeline), FALSE);
-
-  /* Note: this will ensure that the layer exists, creating one if it
-   * doesn't already.
-   *
-   * Note: If the layer already existed it's possibly owned by another
-   * pipeline. If the layer is created then it will be owned by
-   * pipeline. */
-  layer = _cogl_pipeline_get_layer (pipeline, layer_index);
-  /* FIXME: we shouldn't ever construct a layer in a getter function */
-
-  return _cogl_pipeline_layer_get_wrap_mode_s (layer);
-}
-
-/* FIXME: deprecate this API */
-CoglPipelineWrapMode
-_cogl_pipeline_layer_get_wrap_mode_t (CoglPipelineLayer *layer)
-{
-  CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_WRAP_MODES;
-  CoglPipelineLayer     *authority;
-
-  g_return_val_if_fail (_cogl_is_pipeline_layer (layer), FALSE);
-
-  /* Now find the ancestor of the layer that is the authority for the
-   * state we want to change */
-  authority = _cogl_pipeline_layer_get_authority (layer, change);
-
-  return internal_to_public_wrap_mode (authority->wrap_mode_t);
-}
-
-CoglPipelineWrapMode
-cogl_pipeline_get_layer_wrap_mode_t (CoglPipeline *pipeline, int layer_index)
-{
-  CoglPipelineLayer *layer;
-
-  g_return_val_if_fail (cogl_is_pipeline (pipeline), FALSE);
-
-  /* Note: this will ensure that the layer exists, creating one if it
-   * doesn't already.
-   *
-   * Note: If the layer already existed it's possibly owned by another
-   * pipeline. If the layer is created then it will be owned by
-   * pipeline. */
-  layer = _cogl_pipeline_get_layer (pipeline, layer_index);
-  /* FIXME: we shouldn't ever construct a layer in a getter function */
-
-  return _cogl_pipeline_layer_get_wrap_mode_t (layer);
-}
-
-CoglPipelineWrapMode
-_cogl_pipeline_layer_get_wrap_mode_p (CoglPipelineLayer *layer)
-{
-  CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_WRAP_MODES;
-  CoglPipelineLayer     *authority =
-    _cogl_pipeline_layer_get_authority (layer, change);
-
-  return internal_to_public_wrap_mode (authority->wrap_mode_p);
-}
-
-CoglPipelineWrapMode
-cogl_pipeline_get_layer_wrap_mode_p (CoglPipeline *pipeline, int layer_index)
-{
-  CoglPipelineLayer *layer;
-
-  g_return_val_if_fail (cogl_is_pipeline (pipeline), FALSE);
-
-  /* Note: this will ensure that the layer exists, creating one if it
-   * doesn't already.
-   *
-   * Note: If the layer already existed it's possibly owned by another
-   * pipeline. If the layer is created then it will be owned by
-   * pipeline. */
-  layer = _cogl_pipeline_get_layer (pipeline, layer_index);
-
-  return _cogl_pipeline_layer_get_wrap_mode_p (layer);
-}
-
-void
-_cogl_pipeline_layer_get_wrap_modes (CoglPipelineLayer *layer,
-                                     CoglPipelineWrapModeInternal *wrap_mode_s,
-                                     CoglPipelineWrapModeInternal *wrap_mode_t,
-                                     CoglPipelineWrapModeInternal *wrap_mode_p)
-{
-  CoglPipelineLayer *authority =
-    _cogl_pipeline_layer_get_authority (layer,
-                                        COGL_PIPELINE_LAYER_STATE_WRAP_MODES);
-
-  *wrap_mode_s = authority->wrap_mode_s;
-  *wrap_mode_t = authority->wrap_mode_t;
-  *wrap_mode_p = authority->wrap_mode_p;
-}
-
-gboolean
-cogl_pipeline_set_layer_point_sprite_coords_enabled (CoglPipeline *pipeline,
-                                                     int layer_index,
-                                                     gboolean enable,
-                                                     GError **error)
-{
-  CoglPipelineLayerState       change =
-    COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS;
-  CoglPipelineLayer           *layer;
-  CoglPipelineLayer           *new;
-  CoglPipelineLayer           *authority;
-
-  g_return_val_if_fail (cogl_is_pipeline (pipeline), FALSE);
-
-  /* Don't allow point sprite coordinates to be enabled if the driver
-     doesn't support it */
-  if (enable && !cogl_features_available (COGL_FEATURE_POINT_SPRITE))
-    {
-      if (error)
-        {
-          g_set_error (error, COGL_ERROR, COGL_ERROR_UNSUPPORTED,
-                       "Point sprite texture coordinates are enabled "
-                       "for a layer but the GL driver does not support it.");
-        }
-      else
-        {
-          static gboolean warning_seen = FALSE;
-          if (!warning_seen)
-            g_warning ("Point sprite texture coordinates are enabled "
-                       "for a layer but the GL driver does not support it.");
-          warning_seen = TRUE;
-        }
-
-      return FALSE;
-    }
-
-  /* Note: this will ensure that the layer exists, creating one if it
-   * doesn't already.
-   *
-   * Note: If the layer already existed it's possibly owned by another
-   * pipeline. If the layer is created then it will be owned by
-   * pipeline. */
-  layer = _cogl_pipeline_get_layer (pipeline, layer_index);
-
-  /* Now find the ancestor of the layer that is the authority for the
-   * state we want to change */
-  authority = _cogl_pipeline_layer_get_authority (layer, change);
-
-  if (authority->big_state->point_sprite_coords == enable)
-    return TRUE;
-
-  new = _cogl_pipeline_layer_pre_change_notify (pipeline, layer, change);
-  if (new != layer)
-    layer = new;
-  else
-    {
-      /* If the original layer we found is currently the authority on
-       * the state we are changing see if we can revert to one of our
-       * ancestors being the authority. */
-      if (layer == authority &&
-          _cogl_pipeline_layer_get_parent (authority) != NULL)
-        {
-          CoglPipelineLayer *parent =
-            _cogl_pipeline_layer_get_parent (authority);
-          CoglPipelineLayer *old_authority =
-            _cogl_pipeline_layer_get_authority (parent, change);
-
-          if (old_authority->big_state->point_sprite_coords == enable)
-            {
-              layer->differences &= ~change;
-
-              g_assert (layer->owner == pipeline);
-              if (layer->differences == 0)
-                _cogl_pipeline_prune_empty_layer_difference (pipeline,
-                                                             layer);
-              return TRUE;
-            }
-        }
-    }
-
-  layer->big_state->point_sprite_coords = enable;
-
-  /* If we weren't previously the authority on this state then we need
-   * to extended our differences mask and so it's possible that some
-   * of our ancestry will now become redundant, so we aim to reparent
-   * ourselves if that's true... */
-  if (layer != authority)
-    {
-      layer->differences |= change;
-      _cogl_pipeline_layer_prune_redundant_ancestry (layer);
-    }
-
-  return TRUE;
-}
-
-gboolean
-cogl_pipeline_get_layer_point_sprite_coords_enabled (CoglPipeline *pipeline,
-                                                     int layer_index)
-{
-  CoglPipelineLayerState       change =
-    COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS;
-  CoglPipelineLayer *layer;
-  CoglPipelineLayer *authority;
-
-  g_return_val_if_fail (cogl_is_pipeline (pipeline), FALSE);
-
-  /* Note: this will ensure that the layer exists, creating one if it
-   * doesn't already.
-   *
-   * Note: If the layer already existed it's possibly owned by another
-   * pipeline. If the layer is created then it will be owned by
-   * pipeline. */
-  layer = _cogl_pipeline_get_layer (pipeline, layer_index);
-  /* FIXME: we shouldn't ever construct a layer in a getter function */
-
-  authority = _cogl_pipeline_layer_get_authority (layer, change);
-
-  return authority->big_state->point_sprite_coords;
-}
-
-typedef struct
-{
-  CoglPipeline *pipeline;
-  CoglHandle texture;
-} CoglPipelineOverrideLayerState;
-
-static gboolean
-override_layer_texture_cb (CoglPipelineLayer *layer, void *user_data)
-{
-  CoglPipelineOverrideLayerState *state = user_data;
-
-  cogl_pipeline_set_layer_texture (state->pipeline,
-                                   layer->index,
-                                   state->texture);
-
-  return TRUE;
-}
-
-void
-_cogl_pipeline_apply_overrides (CoglPipeline *pipeline,
-                                CoglPipelineFlushOptions *options)
-{
-  COGL_STATIC_COUNTER (apply_overrides_counter,
-                       "pipeline overrides counter",
-                       "Increments each time we have to apply "
-                       "override options to a pipeline",
-                       0 /* no application private data */);
-
-  COGL_COUNTER_INC (_cogl_uprof_context, apply_overrides_counter);
-
-  if (options->flags & COGL_PIPELINE_FLUSH_DISABLE_MASK)
-    {
-      int i;
-
-      /* NB: we can assume that once we see one bit to disable
-       * a layer, all subsequent layers are also disabled. */
-      for (i = 0; i < 32 && options->disable_layers & (1<<i); i++)
-        ;
-
-      _cogl_pipeline_prune_to_n_layers (pipeline, i);
-    }
-
-  if (options->flags & COGL_PIPELINE_FLUSH_FALLBACK_MASK)
-    {
-      CoglPipelineFallbackState state;
-
-      state.i = 0;
-      state.pipeline = pipeline;
-      state.fallback_layers = options->fallback_layers;
-
-      _cogl_pipeline_foreach_layer_internal (pipeline,
-                                             fallback_layer_cb,
-                                             &state);
-    }
-
-  if (options->flags & COGL_PIPELINE_FLUSH_LAYER0_OVERRIDE)
-    {
-      CoglPipelineOverrideLayerState state;
-
-      _cogl_pipeline_prune_to_n_layers (pipeline, 1);
-
-      /* NB: we are overriding the first layer, but we don't know
-       * the user's given layer_index, which is why we use
-       * _cogl_pipeline_foreach_layer_internal() here even though we know
-       * there's only one layer. */
-      state.pipeline = pipeline;
-      state.texture = options->layer0_override_texture;
-      _cogl_pipeline_foreach_layer_internal (pipeline,
-                                             override_layer_texture_cb,
-                                             &state);
-    }
-}
-
-static gboolean
-_cogl_pipeline_layer_texture_target_equal (CoglPipelineLayer *authority0,
-                                           CoglPipelineLayer *authority1,
-                                           CoglPipelineEvalFlags flags)
-{
-  return authority0->target == authority1->target;
-}
-
-static gboolean
-_cogl_pipeline_layer_texture_data_equal (CoglPipelineLayer *authority0,
-                                         CoglPipelineLayer *authority1,
-                                         CoglPipelineEvalFlags flags)
-{
-  GLuint gl_handle0, gl_handle1;
-
-  cogl_texture_get_gl_texture (authority0->texture, &gl_handle0, NULL);
-  cogl_texture_get_gl_texture (authority1->texture, &gl_handle1, NULL);
-
-  return gl_handle0 == gl_handle1;
-}
-
-/* Determine the mask of differences between two layers.
- *
- * XXX: If layers and pipelines could both be cast to a common Tree
- * type of some kind then we could have a unified
- * compare_differences() function.
- */
-unsigned long
-_cogl_pipeline_layer_compare_differences (CoglPipelineLayer *layer0,
-                                          CoglPipelineLayer *layer1)
-{
-  CoglPipelineLayer *node0;
-  CoglPipelineLayer *node1;
-  int len0;
-  int len1;
-  int len0_index;
-  int len1_index;
-  int count;
-  int i;
-  CoglPipelineLayer *common_ancestor = NULL;
-  unsigned long layers_difference = 0;
-
-  _COGL_GET_CONTEXT (ctx, 0);
-
-  /* Algorithm:
-   *
-   * 1) Walk the ancestors of each layer to the root node, adding a
-   *    pointer to each ancester node to two GArrays:
-   *    ctx->pipeline0_nodes, and ctx->pipeline1_nodes.
-   *
-   * 2) Compare the arrays to find the nodes where they stop to
-   *    differ.
-   *
-   * 3) For each array now iterate from index 0 to the first node of
-   *    difference ORing that nodes ->difference mask into the final
-   *    pipeline_differences mask.
-   */
+   * 1) Walk the ancestors of each layer to the root node, adding a
+   *    pointer to each ancester node to two GArrays:
+   *    ctx->pipeline0_nodes, and ctx->pipeline1_nodes.
+   *
+   * 2) Compare the arrays to find the nodes where they stop to
+   *    differ.
+   *
+   * 3) For each array now iterate from index 0 to the first node of
+   *    difference ORing that nodes ->difference mask into the final
+   *    pipeline_differences mask.
+   */
 
   g_array_set_size (ctx->pipeline0_nodes, 0);
   g_array_set_size (ctx->pipeline1_nodes, 0);
@@ -3084,126 +2324,6 @@ _cogl_pipeline_layer_compare_differences (CoglPipelineLayer *layer0,
 }
 
 static gboolean
-_cogl_pipeline_layer_combine_state_equal (CoglPipelineLayer *authority0,
-                                          CoglPipelineLayer *authority1)
-{
-  CoglPipelineLayerBigState *big_state0 = authority0->big_state;
-  CoglPipelineLayerBigState *big_state1 = authority1->big_state;
-  int n_args;
-  int i;
-
-  if (big_state0->texture_combine_rgb_func !=
-      big_state1->texture_combine_rgb_func)
-    return FALSE;
-
-  if (big_state0->texture_combine_alpha_func !=
-      big_state1->texture_combine_alpha_func)
-    return FALSE;
-
-  n_args =
-    _cogl_get_n_args_for_combine_func (big_state0->texture_combine_rgb_func);
-  for (i = 0; i < n_args; i++)
-    {
-      if ((big_state0->texture_combine_rgb_src[i] !=
-           big_state1->texture_combine_rgb_src[i]) ||
-          (big_state0->texture_combine_rgb_op[i] !=
-           big_state1->texture_combine_rgb_op[i]))
-        return FALSE;
-    }
-
-  n_args =
-    _cogl_get_n_args_for_combine_func (big_state0->texture_combine_alpha_func);
-  for (i = 0; i < n_args; i++)
-    {
-      if ((big_state0->texture_combine_alpha_src[i] !=
-           big_state1->texture_combine_alpha_src[i]) ||
-          (big_state0->texture_combine_alpha_op[i] !=
-           big_state1->texture_combine_alpha_op[i]))
-        return FALSE;
-    }
-
-  return TRUE;
-}
-
-static gboolean
-_cogl_pipeline_layer_combine_constant_equal (CoglPipelineLayer *authority0,
-                                             CoglPipelineLayer *authority1)
-{
-  return memcmp (authority0->big_state->texture_combine_constant,
-                 authority1->big_state->texture_combine_constant,
-                 sizeof (float) * 4) == 0 ? TRUE : FALSE;
-}
-
-static gboolean
-_cogl_pipeline_layer_filters_equal (CoglPipelineLayer *authority0,
-                                    CoglPipelineLayer *authority1)
-{
-  if (authority0->mag_filter != authority1->mag_filter)
-    return FALSE;
-  if (authority0->min_filter != authority1->min_filter)
-    return FALSE;
-
-  return TRUE;
-}
-
-static gboolean
-compare_wrap_mode_equal (CoglPipelineWrapMode wrap_mode0,
-                         CoglPipelineWrapMode wrap_mode1)
-{
-  /* We consider AUTOMATIC to be equivalent to CLAMP_TO_EDGE because
-     the primitives code is expected to override this to something
-     else if it wants it to be behave any other way */
-  if (wrap_mode0 == COGL_PIPELINE_WRAP_MODE_AUTOMATIC)
-    wrap_mode0 = COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE;
-  if (wrap_mode1 == COGL_PIPELINE_WRAP_MODE_AUTOMATIC)
-    wrap_mode1 = COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE;
-
-  return wrap_mode0 == wrap_mode1;
-}
-
-static gboolean
-_cogl_pipeline_layer_wrap_modes_equal (CoglPipelineLayer *authority0,
-                                       CoglPipelineLayer *authority1)
-{
-  if (!compare_wrap_mode_equal (authority0->wrap_mode_s,
-                                authority1->wrap_mode_s) ||
-      !compare_wrap_mode_equal (authority0->wrap_mode_t,
-                                authority1->wrap_mode_t) ||
-      !compare_wrap_mode_equal (authority0->wrap_mode_p,
-                                authority1->wrap_mode_p))
-    return FALSE;
-
-  return TRUE;
-}
-
-static gboolean
-_cogl_pipeline_layer_user_matrix_equal (CoglPipelineLayer *authority0,
-                                        CoglPipelineLayer *authority1)
-{
-  CoglPipelineLayerBigState *big_state0 = authority0->big_state;
-  CoglPipelineLayerBigState *big_state1 = authority1->big_state;
-
-  if (!cogl_matrix_equal (&big_state0->matrix, &big_state1->matrix))
-    return FALSE;
-
-  return TRUE;
-}
-
-static gboolean
-_cogl_pipeline_layer_point_sprite_coords_equal (CoglPipelineLayer *authority0,
-                                                CoglPipelineLayer *authority1)
-{
-  CoglPipelineLayerBigState *big_state0 = authority0->big_state;
-  CoglPipelineLayerBigState *big_state1 = authority1->big_state;
-
-  return big_state0->point_sprite_coords == big_state1->point_sprite_coords;
-}
-
-typedef gboolean
-(*CoglPipelineLayerStateComparitor) (CoglPipelineLayer *authority0,
-                                     CoglPipelineLayer *authority1);
-
-static gboolean
 layer_state_equal (CoglPipelineLayerStateIndex state_index,
                    CoglPipelineLayer **authorities0,
                    CoglPipelineLayer **authorities1,
@@ -3801,493 +2921,98 @@ _cogl_pipeline_layer_free (CoglPipelineLayer *layer)
    *
    * Alternatively we could leave it to the caller to check
    * ...?
-   */
-
-void
-_cogl_pipeline_init_default_layers (void)
-{
-  CoglPipelineLayer *layer = g_slice_new0 (CoglPipelineLayer);
-  CoglPipelineLayerBigState *big_state =
-    g_slice_new0 (CoglPipelineLayerBigState);
-  CoglPipelineLayer *new;
-
-  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
-
-  _cogl_pipeline_node_init (COGL_PIPELINE_NODE (layer));
-
-  layer->index = 0;
-
-  layer->differences = COGL_PIPELINE_LAYER_STATE_ALL_SPARSE;
-
-  layer->unit_index = 0;
-
-  layer->texture = NULL;
-  layer->target = 0;
-
-  layer->mag_filter = COGL_PIPELINE_FILTER_LINEAR;
-  layer->min_filter = COGL_PIPELINE_FILTER_LINEAR;
-
-  layer->wrap_mode_s = COGL_PIPELINE_WRAP_MODE_AUTOMATIC;
-  layer->wrap_mode_t = COGL_PIPELINE_WRAP_MODE_AUTOMATIC;
-  layer->wrap_mode_p = COGL_PIPELINE_WRAP_MODE_AUTOMATIC;
-
-  layer->big_state = big_state;
-  layer->has_big_state = TRUE;
-
-  /* Choose the same default combine mode as OpenGL:
-   * RGBA = MODULATE(PREVIOUS[RGBA],TEXTURE[RGBA]) */
-  big_state->texture_combine_rgb_func =
-    COGL_PIPELINE_COMBINE_FUNC_MODULATE;
-  big_state->texture_combine_rgb_src[0] =
-    COGL_PIPELINE_COMBINE_SOURCE_PREVIOUS;
-  big_state->texture_combine_rgb_src[1] =
-    COGL_PIPELINE_COMBINE_SOURCE_TEXTURE;
-  big_state->texture_combine_rgb_op[0] =
-    COGL_PIPELINE_COMBINE_OP_SRC_COLOR;
-  big_state->texture_combine_rgb_op[1] =
-    COGL_PIPELINE_COMBINE_OP_SRC_COLOR;
-  big_state->texture_combine_alpha_func =
-    COGL_PIPELINE_COMBINE_FUNC_MODULATE;
-  big_state->texture_combine_alpha_src[0] =
-    COGL_PIPELINE_COMBINE_SOURCE_PREVIOUS;
-  big_state->texture_combine_alpha_src[1] =
-    COGL_PIPELINE_COMBINE_SOURCE_TEXTURE;
-  big_state->texture_combine_alpha_op[0] =
-    COGL_PIPELINE_COMBINE_OP_SRC_ALPHA;
-  big_state->texture_combine_alpha_op[1] =
-    COGL_PIPELINE_COMBINE_OP_SRC_ALPHA;
-
-  big_state->point_sprite_coords = FALSE;
-
-  cogl_matrix_init_identity (&big_state->matrix);
-
-  ctx->default_layer_0 = _cogl_pipeline_layer_object_new (layer);
-
-  /* TODO: we should make default_layer_n comprise of two
-   * descendants of default_layer_0:
-   * - the first descendant should change the texture combine
-   *   to what we expect is most commonly used for multitexturing
-   * - the second should revert the above change.
-   *
-   * why? the documentation for how a new layer is initialized
-   * doesn't say that layers > 0 have different defaults so unless
-   * we change the documentation we can't use different defaults,
-   * but if the user does what we expect and changes the
-   * texture combine then we can revert the authority to the
-   * first descendant which means we can maximize the number
-   * of layers with a common ancestor.
-   *
-   * The main problem will be that we'll need to disable the
-   * optimizations for flattening the ancestry when we make
-   * the second descendant which reverts the state.
-   */
-  ctx->default_layer_n = _cogl_pipeline_layer_copy (layer);
-  new = _cogl_pipeline_set_layer_unit (NULL, ctx->default_layer_n, 1);
-  g_assert (new == ctx->default_layer_n);
-  /* Since we passed a newly allocated layer we don't expect that
-   * _set_layer_unit() will have to allocate *another* layer. */
-
-  /* Finally we create a dummy dependant for ->default_layer_n which
-   * effectively ensures that ->default_layer_n and ->default_layer_0
-   * remain immutable.
-   */
-  ctx->dummy_layer_dependant =
-    _cogl_pipeline_layer_copy (ctx->default_layer_n);
-}
-
-static void
-setup_texture_combine_state (CoglBlendStringStatement *statement,
-                             CoglPipelineCombineFunc *texture_combine_func,
-                             CoglPipelineCombineSource *texture_combine_src,
-                             CoglPipelineCombineOp *texture_combine_op)
-{
-  int i;
-
-  switch (statement->function->type)
-    {
-    case COGL_BLEND_STRING_FUNCTION_REPLACE:
-      *texture_combine_func = COGL_PIPELINE_COMBINE_FUNC_REPLACE;
-      break;
-    case COGL_BLEND_STRING_FUNCTION_MODULATE:
-      *texture_combine_func = COGL_PIPELINE_COMBINE_FUNC_MODULATE;
-      break;
-    case COGL_BLEND_STRING_FUNCTION_ADD:
-      *texture_combine_func = COGL_PIPELINE_COMBINE_FUNC_ADD;
-      break;
-    case COGL_BLEND_STRING_FUNCTION_ADD_SIGNED:
-      *texture_combine_func = COGL_PIPELINE_COMBINE_FUNC_ADD_SIGNED;
-      break;
-    case COGL_BLEND_STRING_FUNCTION_INTERPOLATE:
-      *texture_combine_func = COGL_PIPELINE_COMBINE_FUNC_INTERPOLATE;
-      break;
-    case COGL_BLEND_STRING_FUNCTION_SUBTRACT:
-      *texture_combine_func = COGL_PIPELINE_COMBINE_FUNC_SUBTRACT;
-      break;
-    case COGL_BLEND_STRING_FUNCTION_DOT3_RGB:
-      *texture_combine_func = COGL_PIPELINE_COMBINE_FUNC_DOT3_RGB;
-      break;
-    case COGL_BLEND_STRING_FUNCTION_DOT3_RGBA:
-      *texture_combine_func = COGL_PIPELINE_COMBINE_FUNC_DOT3_RGBA;
-      break;
-    }
-
-  for (i = 0; i < statement->function->argc; i++)
-    {
-      CoglBlendStringArgument *arg = &statement->args[i];
-
-      switch (arg->source.info->type)
-        {
-        case COGL_BLEND_STRING_COLOR_SOURCE_CONSTANT:
-          texture_combine_src[i] = COGL_PIPELINE_COMBINE_SOURCE_CONSTANT;
-          break;
-        case COGL_BLEND_STRING_COLOR_SOURCE_TEXTURE:
-          texture_combine_src[i] = COGL_PIPELINE_COMBINE_SOURCE_TEXTURE;
-          break;
-        case COGL_BLEND_STRING_COLOR_SOURCE_TEXTURE_N:
-          texture_combine_src[i] =
-            COGL_PIPELINE_COMBINE_SOURCE_TEXTURE0 + arg->source.texture;
-          break;
-        case COGL_BLEND_STRING_COLOR_SOURCE_PRIMARY:
-          texture_combine_src[i] = COGL_PIPELINE_COMBINE_SOURCE_PRIMARY_COLOR;
-          break;
-        case COGL_BLEND_STRING_COLOR_SOURCE_PREVIOUS:
-          texture_combine_src[i] = COGL_PIPELINE_COMBINE_SOURCE_PREVIOUS;
-          break;
-        default:
-          g_warning ("Unexpected texture combine source");
-          texture_combine_src[i] = COGL_PIPELINE_COMBINE_SOURCE_TEXTURE;
-        }
-
-      if (arg->source.mask == COGL_BLEND_STRING_CHANNEL_MASK_RGB)
-        {
-          if (statement->args[i].source.one_minus)
-            texture_combine_op[i] =
-              COGL_PIPELINE_COMBINE_OP_ONE_MINUS_SRC_COLOR;
-          else
-            texture_combine_op[i] = COGL_PIPELINE_COMBINE_OP_SRC_COLOR;
-        }
-      else
-        {
-          if (statement->args[i].source.one_minus)
-            texture_combine_op[i] =
-              COGL_PIPELINE_COMBINE_OP_ONE_MINUS_SRC_ALPHA;
-          else
-            texture_combine_op[i] = COGL_PIPELINE_COMBINE_OP_SRC_ALPHA;
-        }
-    }
-}
-
-gboolean
-cogl_pipeline_set_layer_combine (CoglPipeline *pipeline,
-				 int layer_index,
-				 const char *combine_description,
-                                 GError **error)
-{
-  CoglPipelineLayerState state = COGL_PIPELINE_LAYER_STATE_COMBINE;
-  CoglPipelineLayer *authority;
-  CoglPipelineLayer *layer;
-  CoglBlendStringStatement statements[2];
-  CoglBlendStringStatement split[2];
-  CoglBlendStringStatement *rgb;
-  CoglBlendStringStatement *a;
-  GError *internal_error = NULL;
-  int count;
-
-  g_return_val_if_fail (cogl_is_pipeline (pipeline), FALSE);
-
-  /* Note: this will ensure that the layer exists, creating one if it
-   * doesn't already.
-   *
-   * Note: If the layer already existed it's possibly owned by another
-   * pipeline. If the layer is created then it will be owned by
-   * pipeline. */
-  layer = _cogl_pipeline_get_layer (pipeline, layer_index);
-
-  /* Now find the ancestor of the layer that is the authority for the
-   * state we want to change */
-  authority = _cogl_pipeline_layer_get_authority (layer, state);
-
-  count =
-    _cogl_blend_string_compile (combine_description,
-                                COGL_BLEND_STRING_CONTEXT_TEXTURE_COMBINE,
-                                statements,
-                                &internal_error);
-  if (!count)
-    {
-      if (error)
-	g_propagate_error (error, internal_error);
-      else
-	{
-	  g_warning ("Cannot compile combine description: %s\n",
-		     internal_error->message);
-	  g_error_free (internal_error);
-	}
-      return FALSE;
-    }
-
-  if (statements[0].mask == COGL_BLEND_STRING_CHANNEL_MASK_RGBA)
-    {
-      _cogl_blend_string_split_rgba_statement (statements,
-                                               &split[0], &split[1]);
-      rgb = &split[0];
-      a = &split[1];
-    }
-  else
-    {
-      rgb = &statements[0];
-      a = &statements[1];
-    }
-
-  /* FIXME: compare the new state with the current state! */
-
-  /* possibly flush primitives referencing the current state... */
-  layer = _cogl_pipeline_layer_pre_change_notify (pipeline, layer, state);
-
-  setup_texture_combine_state (rgb,
-                               &layer->big_state->texture_combine_rgb_func,
-                               layer->big_state->texture_combine_rgb_src,
-                               layer->big_state->texture_combine_rgb_op);
-
-  setup_texture_combine_state (a,
-                               &layer->big_state->texture_combine_alpha_func,
-                               layer->big_state->texture_combine_alpha_src,
-                               layer->big_state->texture_combine_alpha_op);
-
-  /* If the original layer we found is currently the authority on
-   * the state we are changing see if we can revert to one of our
-   * ancestors being the authority. */
-  if (layer == authority &&
-      _cogl_pipeline_layer_get_parent (authority) != NULL)
-    {
-      CoglPipelineLayer *parent = _cogl_pipeline_layer_get_parent (authority);
-      CoglPipelineLayer *old_authority =
-        _cogl_pipeline_layer_get_authority (parent, state);
-
-      if (_cogl_pipeline_layer_combine_state_equal (authority,
-                                                    old_authority))
-        {
-          layer->differences &= ~state;
-
-          g_assert (layer->owner == pipeline);
-          if (layer->differences == 0)
-            _cogl_pipeline_prune_empty_layer_difference (pipeline,
-                                                         layer);
-          goto changed;
-        }
-    }
-
-  /* If we weren't previously the authority on this state then we need
-   * to extended our differences mask and so it's possible that some
-   * of our ancestry will now become redundant, so we aim to reparent
-   * ourselves if that's true... */
-  if (layer != authority)
-    {
-      layer->differences |= state;
-      _cogl_pipeline_layer_prune_redundant_ancestry (layer);
-    }
-
-changed:
-
-  _cogl_pipeline_update_blend_enable (pipeline, COGL_PIPELINE_STATE_LAYERS);
-  return TRUE;
-}
-
-void
-cogl_pipeline_set_layer_combine_constant (CoglPipeline *pipeline,
-				          int layer_index,
-                                          const CoglColor *constant_color)
-{
-  CoglPipelineLayerState state = COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT;
-  CoglPipelineLayer     *layer;
-  CoglPipelineLayer     *authority;
-  CoglPipelineLayer     *new;
-  float                  color_as_floats[4];
-
-  g_return_if_fail (cogl_is_pipeline (pipeline));
-
-  /* Note: this will ensure that the layer exists, creating one if it
-   * doesn't already.
-   *
-   * Note: If the layer already existed it's possibly owned by another
-   * pipeline. If the layer is created then it will be owned by
-   * pipeline. */
-  layer = _cogl_pipeline_get_layer (pipeline, layer_index);
-
-  /* Now find the ancestor of the layer that is the authority for the
-   * state we want to change */
-  authority = _cogl_pipeline_layer_get_authority (layer, state);
-
-  color_as_floats[0] = cogl_color_get_red_float (constant_color);
-  color_as_floats[1] = cogl_color_get_green_float (constant_color);
-  color_as_floats[2] = cogl_color_get_blue_float (constant_color);
-  color_as_floats[3] = cogl_color_get_alpha_float (constant_color);
-
-  if (memcmp (authority->big_state->texture_combine_constant,
-              color_as_floats, sizeof (float) * 4) == 0)
-    return;
-
-  new = _cogl_pipeline_layer_pre_change_notify (pipeline, layer, state);
-  if (new != layer)
-    layer = new;
-  else
-    {
-      /* If the original layer we found is currently the authority on
-       * the state we are changing see if we can revert to one of our
-       * ancestors being the authority. */
-      if (layer == authority &&
-          _cogl_pipeline_layer_get_parent (authority) != NULL)
-        {
-          CoglPipelineLayer *parent =
-            _cogl_pipeline_layer_get_parent (authority);
-          CoglPipelineLayer *old_authority =
-            _cogl_pipeline_layer_get_authority (parent, state);
-          CoglPipelineLayerBigState *old_big_state = old_authority->big_state;
-
-          if (memcmp (old_big_state->texture_combine_constant,
-                      color_as_floats, sizeof (float) * 4) == 0)
-            {
-              layer->differences &= ~state;
-
-              g_assert (layer->owner == pipeline);
-              if (layer->differences == 0)
-                _cogl_pipeline_prune_empty_layer_difference (pipeline,
-                                                             layer);
-              goto changed;
-            }
-        }
-    }
-
-  memcpy (layer->big_state->texture_combine_constant,
-          color_as_floats,
-          sizeof (color_as_floats));
-
-  /* If we weren't previously the authority on this state then we need
-   * to extended our differences mask and so it's possible that some
-   * of our ancestry will now become redundant, so we aim to reparent
-   * ourselves if that's true... */
-  if (layer != authority)
-    {
-      layer->differences |= state;
-      _cogl_pipeline_layer_prune_redundant_ancestry (layer);
-    }
-
-changed:
-
-  _cogl_pipeline_update_blend_enable (pipeline, COGL_PIPELINE_STATE_LAYERS);
-}
-
-void
-_cogl_pipeline_get_layer_combine_constant (CoglPipeline *pipeline,
-                                           int layer_index,
-                                           float *constant)
-{
-  CoglPipelineLayerState       change =
-    COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT;
-  CoglPipelineLayer *layer;
-  CoglPipelineLayer *authority;
-
-  g_return_if_fail (cogl_is_pipeline (pipeline));
-
-  /* Note: this will ensure that the layer exists, creating one if it
-   * doesn't already.
-   *
-   * Note: If the layer already existed it's possibly owned by another
-   * pipeline. If the layer is created then it will be owned by
-   * pipeline. */
-  layer = _cogl_pipeline_get_layer (pipeline, layer_index);
-  /* FIXME: we shouldn't ever construct a layer in a getter function */
-
-  authority = _cogl_pipeline_layer_get_authority (layer, change);
-  memcpy (constant, authority->big_state->texture_combine_constant,
-          sizeof (float) * 4);
-}
+   */
 
-/* We should probably make a public API version of this that has a
-   matrix out-param. For an internal API it's good to be able to avoid
-   copying the matrix */
-const CoglMatrix *
-_cogl_pipeline_get_layer_matrix (CoglPipeline *pipeline, int layer_index)
+void
+_cogl_pipeline_init_default_layers (void)
 {
-  CoglPipelineLayerState       change =
-    COGL_PIPELINE_LAYER_STATE_USER_MATRIX;
-  CoglPipelineLayer *layer;
-  CoglPipelineLayer *authority;
+  CoglPipelineLayer *layer = g_slice_new0 (CoglPipelineLayer);
+  CoglPipelineLayerBigState *big_state =
+    g_slice_new0 (CoglPipelineLayerBigState);
+  CoglPipelineLayer *new;
 
-  g_return_val_if_fail (cogl_is_pipeline (pipeline), NULL);
+  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
-  layer = _cogl_pipeline_get_layer (pipeline, layer_index);
+  _cogl_pipeline_node_init (COGL_PIPELINE_NODE (layer));
 
-  authority = _cogl_pipeline_layer_get_authority (layer, change);
-  return &authority->big_state->matrix;
-}
+  layer->index = 0;
 
-void
-cogl_pipeline_set_layer_matrix (CoglPipeline *pipeline,
-				int layer_index,
-                                const CoglMatrix *matrix)
-{
-  CoglPipelineLayerState state = COGL_PIPELINE_LAYER_STATE_USER_MATRIX;
-  CoglPipelineLayer     *layer;
-  CoglPipelineLayer     *authority;
-  CoglPipelineLayer     *new;
+  layer->differences = COGL_PIPELINE_LAYER_STATE_ALL_SPARSE;
 
-  g_return_if_fail (cogl_is_pipeline (pipeline));
+  layer->unit_index = 0;
 
-  /* Note: this will ensure that the layer exists, creating one if it
-   * doesn't already.
-   *
-   * Note: If the layer already existed it's possibly owned by another
-   * pipeline. If the layer is created then it will be owned by
-   * pipeline. */
-  layer = _cogl_pipeline_get_layer (pipeline, layer_index);
+  layer->texture = NULL;
+  layer->target = 0;
 
-  /* Now find the ancestor of the layer that is the authority for the
-   * state we want to change */
-  authority = _cogl_pipeline_layer_get_authority (layer, state);
+  layer->mag_filter = COGL_PIPELINE_FILTER_LINEAR;
+  layer->min_filter = COGL_PIPELINE_FILTER_LINEAR;
 
-  if (cogl_matrix_equal (matrix, &authority->big_state->matrix))
-    return;
+  layer->wrap_mode_s = COGL_PIPELINE_WRAP_MODE_AUTOMATIC;
+  layer->wrap_mode_t = COGL_PIPELINE_WRAP_MODE_AUTOMATIC;
+  layer->wrap_mode_p = COGL_PIPELINE_WRAP_MODE_AUTOMATIC;
 
-  new = _cogl_pipeline_layer_pre_change_notify (pipeline, layer, state);
-  if (new != layer)
-    layer = new;
-  else
-    {
-      /* If the original layer we found is currently the authority on
-       * the state we are changing see if we can revert to one of our
-       * ancestors being the authority. */
-      if (layer == authority &&
-          _cogl_pipeline_layer_get_parent (authority) != NULL)
-        {
-          CoglPipelineLayer *parent =
-            _cogl_pipeline_layer_get_parent (authority);
-          CoglPipelineLayer *old_authority =
-            _cogl_pipeline_layer_get_authority (parent, state);
+  layer->big_state = big_state;
+  layer->has_big_state = TRUE;
 
-          if (cogl_matrix_equal (matrix, &old_authority->big_state->matrix))
-            {
-              layer->differences &= ~state;
+  /* Choose the same default combine mode as OpenGL:
+   * RGBA = MODULATE(PREVIOUS[RGBA],TEXTURE[RGBA]) */
+  big_state->texture_combine_rgb_func =
+    COGL_PIPELINE_COMBINE_FUNC_MODULATE;
+  big_state->texture_combine_rgb_src[0] =
+    COGL_PIPELINE_COMBINE_SOURCE_PREVIOUS;
+  big_state->texture_combine_rgb_src[1] =
+    COGL_PIPELINE_COMBINE_SOURCE_TEXTURE;
+  big_state->texture_combine_rgb_op[0] =
+    COGL_PIPELINE_COMBINE_OP_SRC_COLOR;
+  big_state->texture_combine_rgb_op[1] =
+    COGL_PIPELINE_COMBINE_OP_SRC_COLOR;
+  big_state->texture_combine_alpha_func =
+    COGL_PIPELINE_COMBINE_FUNC_MODULATE;
+  big_state->texture_combine_alpha_src[0] =
+    COGL_PIPELINE_COMBINE_SOURCE_PREVIOUS;
+  big_state->texture_combine_alpha_src[1] =
+    COGL_PIPELINE_COMBINE_SOURCE_TEXTURE;
+  big_state->texture_combine_alpha_op[0] =
+    COGL_PIPELINE_COMBINE_OP_SRC_ALPHA;
+  big_state->texture_combine_alpha_op[1] =
+    COGL_PIPELINE_COMBINE_OP_SRC_ALPHA;
 
-              g_assert (layer->owner == pipeline);
-              if (layer->differences == 0)
-                _cogl_pipeline_prune_empty_layer_difference (pipeline,
-                                                             layer);
-              return;
-            }
-        }
-    }
+  big_state->point_sprite_coords = FALSE;
 
-  layer->big_state->matrix = *matrix;
+  cogl_matrix_init_identity (&big_state->matrix);
 
-  /* If we weren't previously the authority on this state then we need
-   * to extended our differences mask and so it's possible that some
-   * of our ancestry will now become redundant, so we aim to reparent
-   * ourselves if that's true... */
-  if (layer != authority)
-    {
-      layer->differences |= state;
-      _cogl_pipeline_layer_prune_redundant_ancestry (layer);
-    }
+  ctx->default_layer_0 = _cogl_pipeline_layer_object_new (layer);
+
+  /* TODO: we should make default_layer_n comprise of two
+   * descendants of default_layer_0:
+   * - the first descendant should change the texture combine
+   *   to what we expect is most commonly used for multitexturing
+   * - the second should revert the above change.
+   *
+   * why? the documentation for how a new layer is initialized
+   * doesn't say that layers > 0 have different defaults so unless
+   * we change the documentation we can't use different defaults,
+   * but if the user does what we expect and changes the
+   * texture combine then we can revert the authority to the
+   * first descendant which means we can maximize the number
+   * of layers with a common ancestor.
+   *
+   * The main problem will be that we'll need to disable the
+   * optimizations for flattening the ancestry when we make
+   * the second descendant which reverts the state.
+   */
+  ctx->default_layer_n = _cogl_pipeline_layer_copy (layer);
+  new = _cogl_pipeline_set_layer_unit (NULL, ctx->default_layer_n, 1);
+  g_assert (new == ctx->default_layer_n);
+  /* Since we passed a newly allocated layer we don't expect that
+   * _set_layer_unit() will have to allocate *another* layer. */
+
+  /* Finally we create a dummy dependant for ->default_layer_n which
+   * effectively ensures that ->default_layer_n and ->default_layer_0
+   * remain immutable.
+   */
+  ctx->dummy_layer_dependant =
+    _cogl_pipeline_layer_copy (ctx->default_layer_n);
 }
 
 void
@@ -4391,92 +3116,6 @@ cogl_pipeline_get_n_layers (CoglPipeline *pipeline)
   return authority->n_layers;
 }
 
-/* FIXME: deprecate and replace with
- * cogl_pipeline_get_layer_texture() instead. */
-CoglHandle
-_cogl_pipeline_layer_get_texture (CoglPipelineLayer *layer)
-{
-  g_return_val_if_fail (_cogl_is_pipeline_layer (layer),
-			COGL_INVALID_HANDLE);
-
-  return _cogl_pipeline_layer_get_texture_real (layer);
-}
-
-gboolean
-_cogl_pipeline_layer_has_user_matrix (CoglPipeline *pipeline,
-                                      int layer_index)
-{
-  CoglPipelineLayer *layer;
-  CoglPipelineLayer *authority;
-
-  layer = _cogl_pipeline_get_layer (pipeline, layer_index);
-
-  authority =
-    _cogl_pipeline_layer_get_authority (layer,
-                                        COGL_PIPELINE_LAYER_STATE_USER_MATRIX);
-
-  /* If the authority is the default pipeline then no, otherwise yes */
-  return _cogl_pipeline_layer_get_parent (authority) ? TRUE : FALSE;
-}
-
-void
-_cogl_pipeline_layer_get_filters (CoglPipelineLayer *layer,
-                                  CoglPipelineFilter *min_filter,
-                                  CoglPipelineFilter *mag_filter)
-{
-  CoglPipelineLayer *authority =
-    _cogl_pipeline_layer_get_authority (layer,
-                                        COGL_PIPELINE_LAYER_STATE_FILTERS);
-
-  *min_filter = authority->min_filter;
-  *mag_filter = authority->mag_filter;
-}
-
-void
-_cogl_pipeline_get_layer_filters (CoglPipeline *pipeline,
-                                  int layer_index,
-                                  CoglPipelineFilter *min_filter,
-                                  CoglPipelineFilter *mag_filter)
-{
-  CoglPipelineLayer *layer;
-  CoglPipelineLayer *authority;
-
-  g_return_if_fail (cogl_is_pipeline (pipeline));
-
-  layer = _cogl_pipeline_get_layer (pipeline, layer_index);
-
-  authority =
-    _cogl_pipeline_layer_get_authority (layer,
-                                        COGL_PIPELINE_LAYER_STATE_FILTERS);
-
-  *min_filter = authority->min_filter;
-  *mag_filter = authority->mag_filter;
-}
-
-CoglPipelineFilter
-_cogl_pipeline_get_layer_min_filter (CoglPipeline *pipeline,
-                                     int layer_index)
-{
-  CoglPipelineFilter min_filter;
-  CoglPipelineFilter mag_filter;
-
-  _cogl_pipeline_get_layer_filters (pipeline, layer_index,
-                                    &min_filter, &mag_filter);
-  return min_filter;
-}
-
-CoglPipelineFilter
-_cogl_pipeline_get_layer_mag_filter (CoglPipeline *pipeline,
-                                     int layer_index)
-{
-  CoglPipelineFilter min_filter;
-  CoglPipelineFilter mag_filter;
-
-  _cogl_pipeline_get_layer_filters (pipeline, layer_index,
-                                    &min_filter, &mag_filter);
-  return mag_filter;
-}
-
 void
 _cogl_pipeline_layer_pre_paint (CoglPipelineLayer *layer)
 {
@@ -4512,107 +3151,6 @@ _cogl_pipeline_pre_paint_for_layer (CoglPipeline *pipeline,
   _cogl_pipeline_layer_pre_paint (layer);
 }
 
-CoglPipelineFilter
-_cogl_pipeline_layer_get_min_filter (CoglPipelineLayer *layer)
-{
-  CoglPipelineLayer *authority;
-
-  g_return_val_if_fail (_cogl_is_pipeline_layer (layer), 0);
-
-  authority =
-    _cogl_pipeline_layer_get_authority (layer,
-                                        COGL_PIPELINE_LAYER_STATE_FILTERS);
-
-  return authority->min_filter;
-}
-
-CoglPipelineFilter
-_cogl_pipeline_layer_get_mag_filter (CoglPipelineLayer *layer)
-{
-  CoglPipelineLayer *authority;
-
-  g_return_val_if_fail (_cogl_is_pipeline_layer (layer), 0);
-
-  authority =
-    _cogl_pipeline_layer_get_authority (layer,
-                                        COGL_PIPELINE_LAYER_STATE_FILTERS);
-
-  return authority->mag_filter;
-}
-
-void
-cogl_pipeline_set_layer_filters (CoglPipeline      *pipeline,
-                                 int                layer_index,
-                                 CoglPipelineFilter min_filter,
-                                 CoglPipelineFilter mag_filter)
-{
-  CoglPipelineLayerState state = COGL_PIPELINE_LAYER_STATE_FILTERS;
-  CoglPipelineLayer     *layer;
-  CoglPipelineLayer     *authority;
-  CoglPipelineLayer     *new;
-
-  g_return_if_fail (cogl_is_pipeline (pipeline));
-
-  /* Note: this will ensure that the layer exists, creating one if it
-   * doesn't already.
-   *
-   * Note: If the layer already existed it's possibly owned by another
-   * pipeline. If the layer is created then it will be owned by
-   * pipeline. */
-  layer = _cogl_pipeline_get_layer (pipeline, layer_index);
-
-  /* Now find the ancestor of the layer that is the authority for the
-   * state we want to change */
-  authority = _cogl_pipeline_layer_get_authority (layer, state);
-
-  if (authority->min_filter == min_filter &&
-      authority->mag_filter == mag_filter)
-    return;
-
-  new = _cogl_pipeline_layer_pre_change_notify (pipeline, layer, state);
-  if (new != layer)
-    layer = new;
-  else
-    {
-      /* If the original layer we found is currently the authority on
-       * the state we are changing see if we can revert to one of our
-       * ancestors being the authority. */
-      if (layer == authority &&
-          _cogl_pipeline_layer_get_parent (authority) != NULL)
-        {
-          CoglPipelineLayer *parent =
-            _cogl_pipeline_layer_get_parent (authority);
-          CoglPipelineLayer *old_authority =
-            _cogl_pipeline_layer_get_authority (parent, state);
-
-          if (old_authority->min_filter == min_filter &&
-              old_authority->mag_filter == mag_filter)
-            {
-              layer->differences &= ~state;
-
-              g_assert (layer->owner == pipeline);
-              if (layer->differences == 0)
-                _cogl_pipeline_prune_empty_layer_difference (pipeline,
-                                                             layer);
-              return;
-            }
-        }
-    }
-
-  layer->min_filter = min_filter;
-  layer->mag_filter = mag_filter;
-
-  /* If we weren't previously the authority on this state then we need
-   * to extended our differences mask and so it's possible that some
-   * of our ancestry will now become redundant, so we aim to reparent
-   * ourselves if that's true... */
-  if (layer != authority)
-    {
-      layer->differences |= state;
-      _cogl_pipeline_layer_prune_redundant_ancestry (layer);
-    }
-}
-
 /* While a pipeline is referenced by the Cogl journal we can not allow
  * modifications, so this gives us a mechanism to track journal
  * references separately */
@@ -4668,178 +3206,6 @@ _cogl_pipeline_set_static_breadcrumb (CoglPipeline *pipeline,
   pipeline->static_breadcrumb = breadcrumb;
 }
 
-static void
-_cogl_pipeline_layer_hash_unit_state (CoglPipelineLayer *authority,
-                                      CoglPipelineLayer **authorities,
-                                      CoglPipelineHashState *state)
-{
-  int unit = authority->unit_index;
-  state->hash =
-    _cogl_util_one_at_a_time_hash (state->hash, &unit, sizeof (unit));
-}
-
-static void
-_cogl_pipeline_layer_hash_texture_target_state (CoglPipelineLayer *authority,
-                                                CoglPipelineLayer **authorities,
-                                                CoglPipelineHashState *state)
-{
-  GLenum gl_target = authority->target;
-
-  state->hash =
-    _cogl_util_one_at_a_time_hash (state->hash, &gl_target, sizeof (gl_target));
-}
-
-static void
-_cogl_pipeline_layer_hash_texture_data_state (CoglPipelineLayer *authority,
-                                              CoglPipelineLayer **authorities,
-                                              CoglPipelineHashState *state)
-{
-  GLuint gl_handle;
-
-  cogl_texture_get_gl_texture (authority->texture, &gl_handle, NULL);
-
-  state->hash =
-    _cogl_util_one_at_a_time_hash (state->hash, &gl_handle, sizeof (gl_handle));
-}
-
-static void
-_cogl_pipeline_layer_hash_filters_state (CoglPipelineLayer *authority,
-                                         CoglPipelineLayer **authorities,
-                                         CoglPipelineHashState *state)
-{
-  unsigned int hash = state->hash;
-  hash = _cogl_util_one_at_a_time_hash (hash, &authority->mag_filter,
-                                        sizeof (authority->mag_filter));
-  hash = _cogl_util_one_at_a_time_hash (hash, &authority->min_filter,
-                                        sizeof (authority->min_filter));
-  state->hash = hash;
-}
-
-static void
-_cogl_pipeline_layer_hash_wrap_modes_state (CoglPipelineLayer *authority,
-                                            CoglPipelineLayer **authorities,
-                                            CoglPipelineHashState *state)
-{
-  unsigned int hash = state->hash;
-  hash = _cogl_util_one_at_a_time_hash (hash, &authority->wrap_mode_s,
-                                        sizeof (authority->wrap_mode_s));
-  hash = _cogl_util_one_at_a_time_hash (hash, &authority->wrap_mode_t,
-                                        sizeof (authority->wrap_mode_t));
-  hash = _cogl_util_one_at_a_time_hash (hash, &authority->wrap_mode_p,
-                                        sizeof (authority->wrap_mode_p));
-  state->hash = hash;
-}
-
-static void
-_cogl_pipeline_layer_hash_combine_state (CoglPipelineLayer *authority,
-                                         CoglPipelineLayer **authorities,
-                                         CoglPipelineHashState *state)
-{
-  unsigned int hash = state->hash;
-  CoglPipelineLayerBigState *b = authority->big_state;
-  int n_args;
-  int i;
-
-  hash = _cogl_util_one_at_a_time_hash (hash, &b->texture_combine_rgb_func,
-                                        sizeof (b->texture_combine_rgb_func));
-  n_args = _cogl_get_n_args_for_combine_func (b->texture_combine_rgb_func);
-  for (i = 0; i < n_args; i++)
-    {
-      hash =
-        _cogl_util_one_at_a_time_hash (hash, &b->texture_combine_rgb_src[i],
-                                       sizeof (b->texture_combine_rgb_src[i]));
-      hash =
-        _cogl_util_one_at_a_time_hash (hash, &b->texture_combine_rgb_op[i],
-                                       sizeof (b->texture_combine_rgb_op[i]));
-    }
-
-  hash = _cogl_util_one_at_a_time_hash (hash, &b->texture_combine_alpha_func,
-                                        sizeof (b->texture_combine_alpha_func));
-  n_args = _cogl_get_n_args_for_combine_func (b->texture_combine_alpha_func);
-  for (i = 0; i < n_args; i++)
-    {
-      hash =
-        _cogl_util_one_at_a_time_hash (hash, &b->texture_combine_alpha_src[i],
-                                       sizeof (b->texture_combine_alpha_src[i]));
-      hash =
-        _cogl_util_one_at_a_time_hash (hash, &b->texture_combine_alpha_op[i],
-                                       sizeof (b->texture_combine_alpha_op[i]));
-    }
-
-  state->hash = hash;
-}
-
-static void
-_cogl_pipeline_layer_hash_combine_constant_state (CoglPipelineLayer *authority,
-                                                  CoglPipelineLayer **authorities,
-                                                  CoglPipelineHashState *state)
-{
-  CoglPipelineLayerBigState *b = authority->big_state;
-  gboolean need_hash = FALSE;
-  int n_args;
-  int i;
-
-  /* XXX: If the user also asked to hash the ALPHA_FUNC_STATE then it
-   * would be nice if we could combine the n_args loops in this
-   * function and _cogl_pipeline_layer_hash_combine_state.
-   */
-
-  n_args = _cogl_get_n_args_for_combine_func (b->texture_combine_rgb_func);
-  for (i = 0; i < n_args; i++)
-    {
-      if (b->texture_combine_rgb_src[i] ==
-          COGL_PIPELINE_COMBINE_SOURCE_CONSTANT)
-        {
-          /* XXX: should we be careful to only hash the alpha
-           * component in the COGL_PIPELINE_COMBINE_OP_SRC_ALPHA case? */
-          need_hash = TRUE;
-          goto done;
-        }
-    }
-
-  n_args = _cogl_get_n_args_for_combine_func (b->texture_combine_alpha_func);
-  for (i = 0; i < n_args; i++)
-    {
-      if (b->texture_combine_alpha_src[i] ==
-          COGL_PIPELINE_COMBINE_SOURCE_CONSTANT)
-        {
-          /* XXX: should we be careful to only hash the alpha
-           * component in the COGL_PIPELINE_COMBINE_OP_SRC_ALPHA case? */
-          need_hash = TRUE;
-          goto done;
-        }
-    }
-
-done:
-  if (need_hash)
-    {
-      float *constant = b->texture_combine_constant;
-      state->hash = _cogl_util_one_at_a_time_hash (state->hash, constant,
-                                                   sizeof (float) * 4);
-    }
-}
-
-static void
-_cogl_pipeline_layer_hash_user_matrix_state (CoglPipelineLayer *authority,
-                                             CoglPipelineLayer **authorities,
-                                             CoglPipelineHashState *state)
-{
-  CoglPipelineLayerBigState *big_state = authority->big_state;
-  state->hash = _cogl_util_one_at_a_time_hash (state->hash, &big_state->matrix,
-                                               sizeof (float) * 16);
-}
-
-static void
-_cogl_pipeline_layer_hash_point_sprite_state (CoglPipelineLayer *authority,
-                                              CoglPipelineLayer **authorities,
-                                              CoglPipelineHashState *state)
-{
-  CoglPipelineLayerBigState *big_state = authority->big_state;
-  state->hash =
-    _cogl_util_one_at_a_time_hash (state->hash, &big_state->point_sprite_coords,
-                                   sizeof (big_state->point_sprite_coords));
-}
-
 typedef void (*LayerStateHashFunction) (CoglPipelineLayer *authority,
                                         CoglPipelineLayer **authorities,
                                         CoglPipelineHashState *state);
diff --git a/cogl/cogl-pipeline.h b/cogl/cogl-pipeline.h
index 0bcbb35..0ea79e9 100644
--- a/cogl/cogl-pipeline.h
+++ b/cogl/cogl-pipeline.h
@@ -31,7 +31,6 @@
 G_BEGIN_DECLS
 
 #include <cogl/cogl-types.h>
-#include <cogl/cogl-matrix.h>
 
 /**
  * SECTION:cogl-pipeline
@@ -52,84 +51,6 @@ typedef struct _CoglPipeline	      CoglPipeline;
 #define COGL_PIPELINE(OBJECT) ((CoglPipeline *)OBJECT)
 
 /**
- * CoglPipelineFilter:
- * @COGL_PIPELINE_FILTER_NEAREST: Measuring in manhatten distance from the,
- *   current pixel center, use the nearest texture texel
- * @COGL_PIPELINE_FILTER_LINEAR: Use the weighted average of the 4 texels
- *   nearest the current pixel center
- * @COGL_PIPELINE_FILTER_NEAREST_MIPMAP_NEAREST: Select the mimap level whose
- *   texel size most closely matches the current pixel, and use the
- *   %COGL_PIPELINE_FILTER_NEAREST criterion
- * @COGL_PIPELINE_FILTER_LINEAR_MIPMAP_NEAREST: Select the mimap level whose
- *   texel size most closely matches the current pixel, and use the
- *   %COGL_PIPELINE_FILTER_LINEAR criterion
- * @COGL_PIPELINE_FILTER_NEAREST_MIPMAP_LINEAR: Select the two mimap levels
- *   whose texel size most closely matches the current pixel, use
- *   the %COGL_PIPELINE_FILTER_NEAREST criterion on each one and take
- *   their weighted average
- * @COGL_PIPELINE_FILTER_LINEAR_MIPMAP_LINEAR: Select the two mimap levels
- *   whose texel size most closely matches the current pixel, use
- *   the %COGL_PIPELINE_FILTER_LINEAR criterion on each one and take
- *   their weighted average
- *
- * Texture filtering is used whenever the current pixel maps either to more
- * than one texture element (texel) or less than one. These filter enums
- * correspond to different strategies used to come up with a pixel color, by
- * possibly referring to multiple neighbouring texels and taking a weighted
- * average or simply using the nearest texel.
- */
-typedef enum {
-  COGL_PIPELINE_FILTER_NEAREST = 0x2600,
-  COGL_PIPELINE_FILTER_LINEAR = 0x2601,
-  COGL_PIPELINE_FILTER_NEAREST_MIPMAP_NEAREST = 0x2700,
-  COGL_PIPELINE_FILTER_LINEAR_MIPMAP_NEAREST = 0x2701,
-  COGL_PIPELINE_FILTER_NEAREST_MIPMAP_LINEAR = 0x2702,
-  COGL_PIPELINE_FILTER_LINEAR_MIPMAP_LINEAR = 0x2703
-} CoglPipelineFilter;
-/* NB: these values come from the equivalents in gl.h */
-
-/**
- * CoglPipelineWrapMode:
- * @COGL_PIPELINE_WRAP_MODE_REPEAT: The texture will be repeated. This
- *   is useful for example to draw a tiled background.
- * @COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE: The coordinates outside the
- *   range 0â1 will sample copies of the edge pixels of the
- *   texture. This is useful to avoid artifacts if only one copy of
- *   the texture is being rendered.
- * @COGL_PIPELINE_WRAP_MODE_AUTOMATIC: Cogl will try to automatically
- *   decide which of the above two to use. For cogl_rectangle(), it
- *   will use repeat mode if any of the texture coordinates are
- *   outside the range 0â1, otherwise it will use clamp to edge. For
- *   cogl_polygon() it will always use repeat mode. For
- *   cogl_vertex_buffer_draw() it will use repeat mode except for
- *   layers that have point sprite coordinate generation enabled. This
- *   is the default value.
- *
- * The wrap mode specifies what happens when texture coordinates
- * outside the range 0â1 are used. Note that if the filter mode is
- * anything but %COGL_PIPELINE_FILTER_NEAREST then texels outside the
- * range 0â1 might be used even when the coordinate is exactly 0 or 1
- * because OpenGL will try to sample neighbouring pixels. For example
- * if you are trying to render the full texture then you may get
- * artifacts around the edges when the pixels from the other side are
- * merged in if the wrap mode is set to repeat.
- *
- * Since: 2.0
- */
-/* GL_ALWAYS is just used here as a value that is known not to clash
- * with any valid GL wrap modes
- *
- * XXX: keep the values in sync with the CoglPipelineWrapModeInternal
- * enum so no conversion is actually needed.
- */
-typedef enum {
-  COGL_PIPELINE_WRAP_MODE_REPEAT = 0x2901,
-  COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE = 0x812F,
-  COGL_PIPELINE_WRAP_MODE_AUTOMATIC = 0x0207
-} CoglPipelineWrapMode;
-/* NB: these values come from the equivalents in gl.h */
-
-/**
  * cogl_pipeline_new:
  *
  * Allocates and initializes a default simple pipeline that will color
@@ -172,360 +93,6 @@ cogl_pipeline_copy (CoglPipeline *source);
 gboolean
 cogl_is_pipeline (CoglHandle handle);
 
-/**
- * cogl_pipeline_set_layer:
- * @pipeline: A #CoglPipeline object
- * @layer_index: the index of the layer
- * @texture: a #CoglHandle for the layer object
- *
- * In addition to the standard OpenGL lighting model a Cogl pipeline may have
- * one or more layers comprised of textures that can be blended together in
- * order, with a number of different texture combine modes. This function
- * defines a new texture layer.
- *
- * The index values of multiple layers do not have to be consecutive; it is
- * only their relative order that is important.
- *
- * <note>In the future, we may define other types of pipeline layers, such
- * as purely GLSL based layers.</note>
- *
- * Since: 2.0
- */
-void
-cogl_pipeline_set_layer_texture (CoglPipeline *pipeline,
-                                 int           layer_index,
-                                 CoglHandle    texture);
-
-/**
- * cogl_pipeline_remove_layer:
- * @pipeline: A #CoglPipeline object
- * @layer_index: Specifies the layer you want to remove
- *
- * This function removes a layer from your pipeline
- */
-void
-cogl_pipeline_remove_layer (CoglPipeline *pipeline,
-			    int           layer_index);
-
-/**
- * cogl_pipeline_set_layer_combine:
- * @pipeline: A #CoglPipeline object
- * @layer_index: Specifies the layer you want define a combine function for
- * @blend_string: A <link linkend="cogl-Blend-Strings">Cogl blend string</link>
- *    describing the desired texture combine function.
- * @error: A #GError that may report parse errors or lack of GPU/driver
- *   support. May be %NULL, in which case a warning will be printed out if an
- *   error is encountered.
- *
- * If not already familiar; you can refer
- * <link linkend="cogl-Blend-Strings">here</link> for an overview of what blend
- * strings are and there syntax.
- *
- * These are all the functions available for texture combining:
- * <itemizedlist>
- *   <listitem>REPLACE(arg0) = arg0</listitem>
- *   <listitem>MODULATE(arg0, arg1) = arg0 x arg1</listitem>
- *   <listitem>ADD(arg0, arg1) = arg0 + arg1</listitem>
- *   <listitem>ADD_SIGNED(arg0, arg1) = arg0 + arg1 - 0.5</listitem>
- *   <listitem>INTERPOLATE(arg0, arg1, arg2) = arg0 x arg2 + arg1 x (1 - arg2)</listitem>
- *   <listitem>SUBTRACT(arg0, arg1) = arg0 - arg1</listitem>
- *   <listitem>
- *     <programlisting>
- *  DOT3_RGB(arg0, arg1) = 4 x ((arg0[R] - 0.5)) * (arg1[R] - 0.5) +
- *                              (arg0[G] - 0.5)) * (arg1[G] - 0.5) +
- *                              (arg0[B] - 0.5)) * (arg1[B] - 0.5))
- *     </programlisting>
- *   </listitem>
- *   <listitem>
- *     <programlisting>
- *  DOT3_RGBA(arg0, arg1) = 4 x ((arg0[R] - 0.5)) * (arg1[R] - 0.5) +
- *                               (arg0[G] - 0.5)) * (arg1[G] - 0.5) +
- *                               (arg0[B] - 0.5)) * (arg1[B] - 0.5))
- *     </programlisting>
- *   </listitem>
- * </itemizedlist>
- *
- * Refer to the
- * <link linkend="cogl-Blend-String-syntax">color-source syntax</link> for
- * describing the arguments. The valid source names for texture combining
- * are:
- * <variablelist>
- *   <varlistentry>
- *     <term>TEXTURE</term>
- *     <listitem>Use the color from the current texture layer</listitem>
- *   </varlistentry>
- *   <varlistentry>
- *     <term>TEXTURE_0, TEXTURE_1, etc</term>
- *     <listitem>Use the color from the specified texture layer</listitem>
- *   </varlistentry>
- *   <varlistentry>
- *     <term>CONSTANT</term>
- *     <listitem>Use the color from the constant given with
- *     cogl_pipeline_set_layer_constant()</listitem>
- *   </varlistentry>
- *   <varlistentry>
- *     <term>PRIMARY</term>
- *     <listitem>Use the color of the pipeline as set with
- *     cogl_pipeline_set_color()</listitem>
- *   </varlistentry>
- *   <varlistentry>
- *     <term>PREVIOUS</term>
- *     <listitem>Either use the texture color from the previous layer, or
- *     if this is layer 0, use the color of the pipeline as set with
- *     cogl_pipeline_set_color()</listitem>
- *   </varlistentry>
- * </variablelist>
- *
- * <refsect2 id="cogl-Layer-Combine-Examples">
- *   <title>Layer Combine Examples</title>
- *   <para>This is effectively what the default blending is:</para>
- *   <informalexample><programlisting>
- *   RGBA = MODULATE (PREVIOUS, TEXTURE)
- *   </programlisting></informalexample>
- *   <para>This could be used to cross-fade between two images, using
- *   the alpha component of a constant as the interpolator. The constant
- *   color is given by calling cogl_pipeline_set_layer_constant.</para>
- *   <informalexample><programlisting>
- *   RGBA = INTERPOLATE (PREVIOUS, TEXTURE, CONSTANT[A])
- *   </programlisting></informalexample>
- * </refsect2>
- *
- * <note>You can't give a multiplication factor for arguments as you can
- * with blending.</note>
- *
- * Return value: %TRUE if the blend string was successfully parsed, and the
- *   described texture combining is supported by the underlying driver and
- *   or hardware. On failure, %FALSE is returned and @error is set
- *
- * Since: 2.0
- */
-gboolean
-cogl_pipeline_set_layer_combine (CoglPipeline *pipeline,
-				 int           layer_index,
-				 const char   *blend_string,
-                                 GError      **error);
-
-/**
- * cogl_pipeline_set_layer_combine_constant:
- * @pipeline: A #CoglPipeline object
- * @layer_index: Specifies the layer you want to specify a constant used
- *               for texture combining
- * @constant: The constant color you want
- *
- * When you are using the 'CONSTANT' color source in a layer combine
- * description then you can use this function to define its value.
- *
- * Since: 2.0
- */
-void
-cogl_pipeline_set_layer_combine_constant (CoglPipeline    *pipeline,
-                                          int              layer_index,
-                                          const CoglColor *constant);
-
-/**
- * cogl_pipeline_set_layer_matrix:
- * @pipeline: A #CoglPipeline object
- * @layer_index: the index for the layer inside @pipeline
- * @matrix: the transformation matrix for the layer
- *
- * This function lets you set a matrix that can be used to e.g. translate
- * and rotate a single layer of a pipeline used to fill your geometry.
- */
-void
-cogl_pipeline_set_layer_matrix (CoglPipeline     *pipeline,
-				int               layer_index,
-				const CoglMatrix *matrix);
-
-/**
- * cogl_pipeline_get_n_layers:
- * @pipeline: A #CoglPipeline object
- *
- * Retrieves the number of layers defined for the given @pipeline
- *
- * Return value: the number of layers
- *
- * Since: 2.0
- */
-int
-cogl_pipeline_get_n_layers (CoglPipeline *pipeline);
-
-/**
- * cogl_pipeline_set_layer_filters:
- * @pipeline: A #CoglPipeline object
- * @layer_index: the layer number to change.
- * @min_filter: the filter used when scaling a texture down.
- * @mag_filter: the filter used when magnifying a texture.
- *
- * Changes the decimation and interpolation filters used when a texture is
- * drawn at other scales than 100%.
- */
-void
-cogl_pipeline_set_layer_filters (CoglPipeline      *pipeline,
-                                 int                layer_index,
-                                 CoglPipelineFilter min_filter,
-                                 CoglPipelineFilter mag_filter);
-
-/**
- * cogl_pipeline_set_layer_point_sprite_coords_enabled:
- * @pipeline: a #CoglHandle to a pipeline.
- * @layer_index: the layer number to change.
- * @enable: whether to enable point sprite coord generation.
- * @error: A return location for a GError, or NULL to ignore errors.
- *
- * When rendering points, if @enable is %TRUE then the texture
- * coordinates for this layer will be replaced with coordinates that
- * vary from 0.0 to 1.0 across the primitive. The top left of the
- * point will have the coordinates 0.0,0.0 and the bottom right will
- * have 1.0,1.0. If @enable is %FALSE then the coordinates will be
- * fixed for the entire point.
- *
- * This function will only work if %COGL_FEATURE_POINT_SPRITE is
- * available. If the feature is not available then the function will
- * return %FALSE and set @error.
- *
- * Return value: %TRUE if the function succeeds, %FALSE otherwise.
- * Since: 2.0
- */
-gboolean
-cogl_pipeline_set_layer_point_sprite_coords_enabled (CoglPipeline *pipeline,
-                                                     int           layer_index,
-                                                     gboolean      enable,
-                                                     GError      **error);
-
-/**
- * cogl_pipeline_get_layer_point_sprite_coords_enabled:
- * @pipeline: a #CoglHandle to a pipeline.
- * @layer_index: the layer number to check.
- *
- * Gets whether point sprite coordinate generation is enabled for this
- * texture layer.
- *
- * Return value: whether the texture coordinates will be replaced with
- * point sprite coordinates.
- *
- * Since: 2.0
- */
-gboolean
-cogl_pipeline_get_layer_point_sprite_coords_enabled (CoglPipeline *pipeline,
-                                                     int           layer_index);
-
-/**
- * cogl_pipeline_get_layer_wrap_mode_s:
- * @pipeline: A #CoglPipeline object
- * @layer_index: the layer number to change.
- *
- * Returns the wrap mode for the 's' coordinate of texture lookups on this
- * layer.
- *
- * Return value: the wrap mode for the 's' coordinate of texture lookups on
- * this layer.
- *
- * Since: 1.6
- */
-CoglPipelineWrapMode
-cogl_pipeline_get_layer_wrap_mode_s (CoglPipeline *pipeline,
-                                     int           layer_index);
-
-/**
- * cogl_pipeline_set_layer_wrap_mode_s:
- * @pipeline: A #CoglPipeline object
- * @layer_index: the layer number to change.
- * @mode: the new wrap mode
- *
- * Sets the wrap mode for the 's' coordinate of texture lookups on this layer.
- *
- * Since: 2.0
- */
-void
-cogl_pipeline_set_layer_wrap_mode_s (CoglPipeline        *pipeline,
-                                     int                  layer_index,
-                                     CoglPipelineWrapMode mode);
-
-/**
- * cogl_pipeline_get_layer_wrap_mode_t:
- * @pipeline: A #CoglPipeline object
- * @layer_index: the layer number to change.
- *
- * Returns the wrap mode for the 't' coordinate of texture lookups on this
- * layer.
- *
- * Return value: the wrap mode for the 't' coordinate of texture lookups on
- * this layer.
- *
- * Since: 1.6
- */
-CoglPipelineWrapMode
-cogl_pipeline_get_layer_wrap_mode_t (CoglPipeline *pipeline,
-                                     int           layer_index);
-
-
-/**
- * cogl_pipeline_set_layer_wrap_mode_t:
- * @pipeline: A #CoglPipeline object
- * @layer_index: the layer number to change.
- * @mode: the new wrap mode
- *
- * Sets the wrap mode for the 't' coordinate of texture lookups on this layer.
- *
- * Since: 2.0
- */
-void
-cogl_pipeline_set_layer_wrap_mode_t (CoglPipeline        *pipeline,
-                                     int                  layer_index,
-                                     CoglPipelineWrapMode mode);
-
-/**
- * cogl_pipeline_get_layer_wrap_mode_p:
- * @pipeline: A #CoglPipeline object
- * @layer_index: the layer number to change.
- *
- * Returns the wrap mode for the 'p' coordinate of texture lookups on this
- * layer.
- *
- * Return value: the wrap mode for the 'p' coordinate of texture lookups on
- * this layer.
- *
- * Since: 1.6
- */
-CoglPipelineWrapMode
-cogl_pipeline_get_layer_wrap_mode_p (CoglPipeline *pipeline,
-                                     int           layer_index);
-
-/**
- * cogl_pipeline_set_layer_wrap_mode_p:
- * @pipeline: A #CoglPipeline object
- * @layer_index: the layer number to change.
- * @mode: the new wrap mode
- *
- * Sets the wrap mode for the 'p' coordinate of texture lookups on
- * this layer. 'p' is the third coordinate.
- *
- * Since: 2.0
- */
-void
-cogl_pipeline_set_layer_wrap_mode_p (CoglPipeline        *pipeline,
-                                     int                  layer_index,
-                                     CoglPipelineWrapMode mode);
-
-/**
- * cogl_pipeline_set_layer_wrap_mode:
- * @pipeline: A #CoglPipeline object
- * @layer_index: the layer number to change.
- * @mode: the new wrap mode
- *
- * Sets the wrap mode for all three coordinates of texture lookups on
- * this layer. This is equivalent to calling
- * cogl_pipeline_set_layer_wrap_mode_s(),
- * cogl_pipeline_set_layer_wrap_mode_t() and
- * cogl_pipeline_set_layer_wrap_mode_p() separately.
- *
- * Since: 2.0
- */
-void
-cogl_pipeline_set_layer_wrap_mode (CoglPipeline        *pipeline,
-                                   int                  layer_index,
-                                   CoglPipelineWrapMode mode);
-
 #ifdef COGL_ENABLE_EXPERIMENTAL_API
 
 /**
diff --git a/cogl/cogl.h b/cogl/cogl.h
index 6d433c2..db4366b 100644
--- a/cogl/cogl.h
+++ b/cogl/cogl.h
@@ -89,6 +89,7 @@ typedef struct _CoglFramebuffer CoglFramebuffer;
 #include <cogl/cogl-depth-state.h>
 #include <cogl/cogl-pipeline.h>
 #include <cogl/cogl-pipeline-state.h>
+#include <cogl/cogl-pipeline-layer-state.h>
 #include <cogl/cogl-framebuffer.h>
 #ifdef COGL_HAS_XLIB
 #include <cogl/cogl-xlib.h>
diff --git a/doc/reference/cogl-2.0-experimental/Makefile.am b/doc/reference/cogl-2.0-experimental/Makefile.am
index b2b9b2f..e34e89b 100644
--- a/doc/reference/cogl-2.0-experimental/Makefile.am
+++ b/doc/reference/cogl-2.0-experimental/Makefile.am
@@ -75,6 +75,7 @@ IGNORE_HFILES=\
 	cogl-pipeline-opengl-private.h		\
 	cogl-pipeline-private.h			\
 	cogl-pipeline-state-private.h		\
+	cogl-pipeline-layer-state-private.h	\
 	cogl-pipeline-progend-glsl-private.h	\
 	cogl-pipeline-vertend-fixed-private.h	\
 	cogl-pipeline-vertend-glsl-private.h	\
diff --git a/doc/reference/cogl/Makefile.am b/doc/reference/cogl/Makefile.am
index e4cd24b..fb5dd3f 100644
--- a/doc/reference/cogl/Makefile.am
+++ b/doc/reference/cogl/Makefile.am
@@ -73,6 +73,7 @@ IGNORE_HFILES=\
 	cogl-pipeline-opengl-private.h		\
 	cogl-pipeline-private.h			\
 	cogl-pipeline-state-private.h		\
+	cogl-pipeline-layer-state-private.h	\
 	cogl-pipeline-progend-glsl-private.h	\
 	cogl-pipeline-vertend-fixed-private.h	\
 	cogl-pipeline-vertend-glsl-private.h	\



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