material: splits out all the state flushing code
authorRobert Bragg <robert@linux.intel.com>
Sat, 10 Jul 2010 02:40:47 +0000 (03:40 +0100)
committerRobert Bragg <robert@linux.intel.com>
Tue, 13 Jul 2010 18:26:58 +0000 (19:26 +0100)
This moves the code supporting _cogl_material_flush_gl_state into
cogl-material-opengl.c as part of an effort to reduce the size of
cogl-material.c to keep it manageable.

clutter/cogl/cogl/cogl-journal.c
clutter/cogl/cogl/cogl-material-opengl-private.h
clutter/cogl/cogl/cogl-material-opengl.c
clutter/cogl/cogl/cogl-material-private.h
clutter/cogl/cogl/cogl-material.c
clutter/cogl/cogl/cogl-path.c
clutter/cogl/cogl/cogl-primitives.c
clutter/cogl/cogl/cogl-vertex-buffer.c
clutter/cogl/cogl/cogl.c
clutter/cogl/cogl/driver/gl/cogl-program.c

index f8ee004..0484611 100644 (file)
@@ -31,6 +31,7 @@
 #include "cogl-journal-private.h"
 #include "cogl-texture-private.h"
 #include "cogl-material-private.h"
+#include "cogl-material-opengl-private.h"
 #include "cogl-vertex-buffer-private.h"
 #include "cogl-framebuffer-private.h"
 #include "cogl-profile.h"
index 279f8bd..fdd5a19 100644 (file)
@@ -144,5 +144,12 @@ _cogl_bind_gl_texture_transient (GLenum gl_target,
 void
 _cogl_delete_gl_texture (GLuint gl_texture);
 
+void
+_cogl_gl_use_program_wrapper (GLuint program);
+
+void
+_cogl_material_flush_gl_state (CoglMaterial *material,
+                               gboolean skip_gl_state);
+
 #endif /* __COGL_MATERIAL_OPENGL_PRIVATE_H */
 
index 06be8c2..236196d 100644 (file)
 #include "cogl-material-opengl-private.h"
 #include "cogl-material-private.h"
 #include "cogl-context.h"
+#include "cogl-texture-private.h"
+#ifndef HAVE_COGL_GLES
+#include "cogl-program.h"
+#endif
+
+#ifdef COGL_MATERIAL_BACKEND_GLSL
+#include "cogl-material-glsl-private.h"
+#endif
+#ifdef COGL_MATERIAL_BACKEND_ARBFP
+#include "cogl-material-arbfp-private.h"
+#endif
+#ifdef COGL_MATERIAL_BACKEND_FIXED
+#include "cogl-material-fixed-private.h"
+#endif
+
+#include <glib.h>
+#include <string.h>
+
+/*
+ * GL/GLES compatability defines for material thingies:
+ */
+
+#ifdef HAVE_COGL_GLES2
+#include "../gles/cogl-gles2-wrapper.h"
+#endif
+
+#ifdef HAVE_COGL_GL
+#define glActiveTexture ctx->drv.pf_glActiveTexture
+#define glClientActiveTexture ctx->drv.pf_glClientActiveTexture
+#define glBlendFuncSeparate ctx->drv.pf_glBlendFuncSeparate
+#define glBlendEquation ctx->drv.pf_glBlendEquation
+#define glBlendColor ctx->drv.pf_glBlendColor
+#define glBlendEquationSeparate ctx->drv.pf_glBlendEquationSeparate
+
+#define glProgramString ctx->drv.pf_glProgramString
+#define glBindProgram ctx->drv.pf_glBindProgram
+#define glDeletePrograms ctx->drv.pf_glDeletePrograms
+#define glGenPrograms ctx->drv.pf_glGenPrograms
+#define glProgramLocalParameter4fv ctx->drv.pf_glProgramLocalParameter4fv
+#define glUseProgram ctx->drv.pf_glUseProgram
+#endif
+
+/* These aren't defined in the GLES headers */
+#ifndef GL_POINT_SPRITE
+#define GL_POINT_SPRITE 0x8861
+#endif
+#ifndef GL_COORD_REPLACE
+#define GL_COORD_REPLACE 0x8862
+#endif
+#ifndef GL_CLAMP_TO_BORDER
+#define GL_CLAMP_TO_BORDER 0x812d
+#endif
+
 
 static void
 texture_unit_init (CoglTextureUnit *unit, int index_)
@@ -227,4 +280,965 @@ _cogl_material_texture_storage_change_notify (CoglHandle texture)
     }
 }
 
+void
+_cogl_gl_use_program_wrapper (GLuint program)
+{
+#ifdef COGL_MATERIAL_BACKEND_GLSL
+  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+  if (ctx->current_gl_program == program)
+    return;
+
+  if (program)
+    {
+      GLenum gl_error;
+
+      while ((gl_error = glGetError ()) != GL_NO_ERROR)
+        ;
+      glUseProgram (program);
+      if (glGetError () != GL_NO_ERROR)
+        {
+          GE (glUseProgram (0));
+          ctx->current_gl_program = 0;
+          return;
+        }
+    }
+  else
+    GE (glUseProgram (0));
+
+  ctx->current_gl_program = program;
+#endif
+}
+
+static void
+disable_glsl (void)
+{
+#ifdef COGL_MATERIAL_BACKEND_GLSL
+  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+  if (ctx->current_use_program_type == COGL_MATERIAL_PROGRAM_TYPE_GLSL)
+    _cogl_gl_use_program_wrapper (0);
+#endif
+}
+
+static void
+disable_arbfp (void)
+{
+#ifdef COGL_MATERIAL_BACKEND_ARBFP
+  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+  if (ctx->current_use_program_type == COGL_MATERIAL_PROGRAM_TYPE_ARBFP)
+    GE (glDisable (GL_FRAGMENT_PROGRAM_ARB));
+#endif
+}
+
+void
+_cogl_use_program (CoglHandle program_handle, CoglMaterialProgramType type)
+{
+  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+  switch (type)
+    {
+#ifdef COGL_MATERIAL_BACKEND_GLSL
+    case COGL_MATERIAL_PROGRAM_TYPE_GLSL:
+      {
+        /* The GLES2 backend currently manages its own codegen for
+         * fixed function API fallbacks and manages its own shader
+         * state.  */
+#ifndef HAVE_COGL_GLES2
+        CoglProgram *program =
+          _cogl_program_pointer_from_handle (program_handle);
+
+        _cogl_gl_use_program_wrapper (program->gl_handle);
+        disable_arbfp ();
+#endif
+
+        ctx->current_use_program_type = type;
+        break;
+      }
+#else
+    case COGL_MATERIAL_PROGRAM_TYPE_GLSL:
+      g_warning ("Unexpected use of GLSL backend!");
+      break;
+#endif
+#ifdef COGL_MATERIAL_BACKEND_ARBFP
+    case COGL_MATERIAL_PROGRAM_TYPE_ARBFP:
+
+      /* _cogl_gl_use_program_wrapper can be called by cogl-program.c
+       * so we can't bailout without making sure we glUseProgram (0)
+       * first. */
+      disable_glsl ();
+
+      if (ctx->current_use_program_type == COGL_MATERIAL_PROGRAM_TYPE_ARBFP)
+        break;
+
+      GE (glEnable (GL_FRAGMENT_PROGRAM_ARB));
+
+      ctx->current_use_program_type = type;
+      break;
+#else
+    case COGL_MATERIAL_PROGRAM_TYPE_ARBFP:
+      g_warning ("Unexpected use of GLSL backend!");
+      break;
+#endif
+#ifdef COGL_MATERIAL_BACKEND_FIXED
+    case COGL_MATERIAL_PROGRAM_TYPE_FIXED:
+
+      /* _cogl_gl_use_program_wrapper can be called by cogl-program.c
+       * so we can't bailout without making sure we glUseProgram (0)
+       * first. */
+      disable_glsl ();
+
+      if (ctx->current_use_program_type == COGL_MATERIAL_PROGRAM_TYPE_FIXED)
+        break;
+
+      disable_arbfp ();
+
+      ctx->current_use_program_type = type;
+#endif
+    }
+}
+
+#if defined (COGL_MATERIAL_BACKEND_GLSL) || \
+    defined (COGL_MATERIAL_BACKEND_ARBFP)
+int
+_cogl_get_max_texture_image_units (void)
+{
+  _COGL_GET_CONTEXT (ctx, 0);
+
+  /* This function is called quite often so we cache the value to
+     avoid too many GL calls */
+  if (G_UNLIKELY (ctx->max_texture_image_units == -1))
+    {
+      ctx->max_texture_image_units = 1;
+      GE (glGetIntegerv (GL_MAX_TEXTURE_IMAGE_UNITS,
+                         &ctx->max_texture_image_units));
+    }
+
+  return ctx->max_texture_image_units;
+}
+#endif
+
+static void
+_cogl_material_layer_get_texture_info (CoglMaterialLayer *layer,
+                                       CoglHandle *texture,
+                                       GLuint *gl_texture,
+                                       GLuint *gl_target)
+{
+  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+  *texture = layer->texture;
+  if (G_UNLIKELY (*texture == COGL_INVALID_HANDLE))
+    *texture = ctx->default_gl_texture_2d_tex;
+  if (layer->texture_overridden)
+    {
+      *gl_texture = layer->slice_gl_texture;
+      *gl_target = layer->slice_gl_target;
+    }
+  else
+    cogl_texture_get_gl_texture (*texture, gl_texture, gl_target);
+}
+
+#ifndef HAVE_COGL_GLES
+
+static gboolean
+blend_factor_uses_constant (GLenum blend_factor)
+{
+  return (blend_factor == GL_CONSTANT_COLOR ||
+          blend_factor == GL_ONE_MINUS_CONSTANT_COLOR ||
+          blend_factor == GL_CONSTANT_ALPHA ||
+          blend_factor == GL_ONE_MINUS_CONSTANT_ALPHA);
+}
+
+#endif
+
+static void
+flush_depth_state (CoglMaterialDepthState *depth_state)
+{
+  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+  if (ctx->depth_test_function_cache != depth_state->depth_test_function)
+    {
+      GE (glDepthFunc (depth_state->depth_test_function));
+      ctx->depth_test_function_cache = depth_state->depth_test_function;
+    }
+
+  if (ctx->depth_writing_enabled_cache != depth_state->depth_writing_enabled)
+    {
+      GE (glDepthMask (depth_state->depth_writing_enabled ?
+                       GL_TRUE : GL_FALSE));
+      ctx->depth_writing_enabled_cache = depth_state->depth_writing_enabled;
+    }
+
+#ifndef COGL_HAS_GLES
+  if (ctx->depth_range_near_cache != depth_state->depth_range_near ||
+      ctx->depth_range_far_cache != depth_state->depth_range_far)
+    {
+#ifdef COGL_HAS_GLES2
+      GE (glDepthRangef (depth_state->depth_range_near,
+                         depth_state->depth_range_far));
+#else
+      GE (glDepthRange (depth_state->depth_range_near,
+                        depth_state->depth_range_far));
+#endif
+      ctx->depth_range_near_cache = depth_state->depth_range_near;
+      ctx->depth_range_far_cache = depth_state->depth_range_far;
+    }
+#endif /* COGL_HAS_GLES */
+}
+
+static void
+_cogl_material_flush_color_blend_alpha_depth_state (
+                                            CoglMaterial *material,
+                                            unsigned long materials_difference,
+                                            gboolean      skip_gl_color)
+{
+  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+  if (!skip_gl_color)
+    {
+      if ((materials_difference & COGL_MATERIAL_STATE_COLOR) ||
+          /* Assume if we were previously told to skip the color, then
+           * the current color needs updating... */
+          ctx->current_material_skip_gl_color)
+        {
+          CoglMaterial *authority =
+            _cogl_material_get_authority (material, COGL_MATERIAL_STATE_COLOR);
+          GE (glColor4ub (cogl_color_get_red_byte (&authority->color),
+                          cogl_color_get_green_byte (&authority->color),
+                          cogl_color_get_blue_byte (&authority->color),
+                          cogl_color_get_alpha_byte (&authority->color)));
+        }
+    }
+
+  if (materials_difference & COGL_MATERIAL_STATE_LIGHTING)
+    {
+      CoglMaterial *authority =
+        _cogl_material_get_authority (material, COGL_MATERIAL_STATE_LIGHTING);
+      CoglMaterialLightingState *lighting_state =
+        &authority->big_state->lighting_state;
+
+      /* FIXME - we only need to set these if lighting is enabled... */
+      GLfloat shininess = lighting_state->shininess * 128.0f;
+
+      GE (glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT, lighting_state->ambient));
+      GE (glMaterialfv (GL_FRONT_AND_BACK, GL_DIFFUSE, lighting_state->diffuse));
+      GE (glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, lighting_state->specular));
+      GE (glMaterialfv (GL_FRONT_AND_BACK, GL_EMISSION, lighting_state->emission));
+      GE (glMaterialfv (GL_FRONT_AND_BACK, GL_SHININESS, &shininess));
+    }
+
+  if (materials_difference & COGL_MATERIAL_STATE_BLEND)
+    {
+      CoglMaterial *authority =
+        _cogl_material_get_authority (material, COGL_MATERIAL_STATE_BLEND);
+      CoglMaterialBlendState *blend_state =
+        &authority->big_state->blend_state;
+
+#if defined (HAVE_COGL_GLES2)
+      gboolean have_blend_equation_seperate = TRUE;
+      gboolean have_blend_func_separate = TRUE;
+#elif defined (HAVE_COGL_GL)
+      gboolean have_blend_equation_seperate = FALSE;
+      gboolean have_blend_func_separate = FALSE;
+      if (ctx->drv.pf_glBlendEquationSeparate) /* Only GL 2.0 + */
+        have_blend_equation_seperate = TRUE;
+      if (ctx->drv.pf_glBlendFuncSeparate) /* Only GL 1.4 + */
+        have_blend_func_separate = TRUE;
+#endif
+
+#ifndef HAVE_COGL_GLES /* GLES 1 only has glBlendFunc */
+      if (blend_factor_uses_constant (blend_state->blend_src_factor_rgb) ||
+          blend_factor_uses_constant (blend_state->blend_src_factor_alpha) ||
+          blend_factor_uses_constant (blend_state->blend_dst_factor_rgb) ||
+          blend_factor_uses_constant (blend_state->blend_dst_factor_alpha))
+        {
+          float red =
+            cogl_color_get_red_float (&blend_state->blend_constant);
+          float green =
+            cogl_color_get_green_float (&blend_state->blend_constant);
+          float blue =
+            cogl_color_get_blue_float (&blend_state->blend_constant);
+          float alpha =
+            cogl_color_get_alpha_float (&blend_state->blend_constant);
+
+
+          GE (glBlendColor (red, green, blue, alpha));
+        }
+
+      if (have_blend_equation_seperate &&
+          blend_state->blend_equation_rgb != blend_state->blend_equation_alpha)
+        GE (glBlendEquationSeparate (blend_state->blend_equation_rgb,
+                                     blend_state->blend_equation_alpha));
+      else
+        GE (glBlendEquation (blend_state->blend_equation_rgb));
+
+      if (have_blend_func_separate &&
+          (blend_state->blend_src_factor_rgb != blend_state->blend_src_factor_alpha ||
+           (blend_state->blend_src_factor_rgb !=
+            blend_state->blend_src_factor_alpha)))
+        GE (glBlendFuncSeparate (blend_state->blend_src_factor_rgb,
+                                 blend_state->blend_dst_factor_rgb,
+                                 blend_state->blend_src_factor_alpha,
+                                 blend_state->blend_dst_factor_alpha));
+      else
+#endif
+        GE (glBlendFunc (blend_state->blend_src_factor_rgb,
+                         blend_state->blend_dst_factor_rgb));
+    }
+
+  if (materials_difference & COGL_MATERIAL_STATE_ALPHA_FUNC)
+    {
+      CoglMaterial *authority =
+        _cogl_material_get_authority (material, COGL_MATERIAL_STATE_ALPHA_FUNC);
+      CoglMaterialAlphaFuncState *alpha_state =
+        &authority->big_state->alpha_state;
+
+      /* NB: Currently the Cogl defines are compatible with the GL ones: */
+      GE (glAlphaFunc (alpha_state->alpha_func,
+                       alpha_state->alpha_func_reference));
+    }
+
+  if (materials_difference & COGL_MATERIAL_STATE_DEPTH)
+    {
+      CoglMaterial *authority =
+        _cogl_material_get_authority (material, COGL_MATERIAL_STATE_DEPTH);
+      CoglMaterialDepthState *depth_state = &authority->big_state->depth_state;
+
+      if (depth_state->depth_test_enabled)
+        {
+          if (ctx->depth_test_enabled_cache != TRUE)
+            {
+              GE (glEnable (GL_DEPTH_TEST));
+              ctx->depth_test_enabled_cache = depth_state->depth_test_enabled;
+            }
+          flush_depth_state (depth_state);
+        }
+      else if (ctx->depth_test_enabled_cache != FALSE)
+        {
+          GE (glDisable (GL_DEPTH_TEST));
+          ctx->depth_test_enabled_cache = depth_state->depth_test_enabled;
+        }
+    }
+
+  if (materials_difference & COGL_MATERIAL_STATE_POINT_SIZE)
+    {
+      CoglMaterial *authority =
+        _cogl_material_get_authority (material, COGL_MATERIAL_STATE_POINT_SIZE);
+
+      if (ctx->point_size_cache != authority->big_state->point_size)
+        {
+          GE( glPointSize (authority->big_state->point_size) );
+          ctx->point_size_cache = authority->big_state->point_size;
+        }
+    }
+
+  if (material->real_blend_enable != ctx->gl_blend_enable_cache)
+    {
+      if (material->real_blend_enable)
+        GE (glEnable (GL_BLEND));
+      else
+        GE (glDisable (GL_BLEND));
+      /* XXX: we shouldn't update any other blend state if blending
+       * is disabled! */
+      ctx->gl_blend_enable_cache = material->real_blend_enable;
+    }
+}
+
+static int
+get_max_activateable_texture_units (void)
+{
+  _COGL_GET_CONTEXT (ctx, 0);
+
+  if (G_UNLIKELY (ctx->max_activateable_texture_units == -1))
+    {
+#ifdef HAVE_COGL_GL
+      GLint max_tex_coords;
+      GLint max_combined_tex_units;
+      GE (glGetIntegerv (GL_MAX_TEXTURE_COORDS, &max_tex_coords));
+      GE (glGetIntegerv (GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS,
+                         &max_combined_tex_units));
+      ctx->max_activateable_texture_units =
+        MAX (max_tex_coords - 1, max_combined_tex_units);
+#else
+      GE (glGetIntegerv (GL_MAX_TEXTURE_UNITS,
+                         &ctx->max_activateable_texture_units));
+#endif
+    }
+
+  return ctx->max_activateable_texture_units;
+}
+
+typedef struct
+{
+  int i;
+  unsigned long *layer_differences;
+} CoglMaterialFlushLayerState;
+
+static gboolean
+flush_layers_common_gl_state_cb (CoglMaterialLayer *layer, void *user_data)
+{
+  CoglMaterialFlushLayerState *flush_state = user_data;
+  int                          unit_index = flush_state->i;
+  CoglTextureUnit             *unit = _cogl_get_texture_unit (unit_index);
+  unsigned long                layers_difference =
+    flush_state->layer_differences[unit_index];
+
+  /* There may not be enough texture units so we can bail out if
+   * that's the case...
+   */
+  if (G_UNLIKELY (unit_index >= get_max_activateable_texture_units ()))
+    {
+      static gboolean shown_warning = FALSE;
+
+      if (!shown_warning)
+        {
+          g_warning ("Your hardware does not have enough texture units"
+                     "to handle this many texture layers");
+          shown_warning = TRUE;
+        }
+      return FALSE;
+    }
+
+  if (layers_difference & COGL_MATERIAL_LAYER_STATE_TEXTURE)
+    {
+      CoglMaterialLayer *authority =
+        _cogl_material_layer_get_authority (layer,
+                                            COGL_MATERIAL_LAYER_STATE_TEXTURE);
+      CoglHandle texture = NULL;
+      GLuint     gl_texture;
+      GLenum     gl_target;
+
+      _cogl_material_layer_get_texture_info (authority,
+                                             &texture,
+                                             &gl_texture,
+                                             &gl_target);
+
+      _cogl_set_active_texture_unit (unit_index);
+
+      /* NB: There are several Cogl components and some code in
+       * Clutter that will temporarily bind arbitrary GL textures to
+       * query and modify texture object parameters. If you look at
+       * _cogl_bind_gl_texture_transient() you can see we make sure
+       * that such code always binds to texture unit 1 which means we
+       * can't rely on the unit->gl_texture state if unit->index == 1.
+       *
+       * Because texture unit 1 is a bit special we actually defer any
+       * necessary glBindTexture for it until the end of
+       * _cogl_material_flush_gl_state().
+       *
+       * NB: we get notified whenever glDeleteTextures is used (see
+       * _cogl_delete_gl_texture()) where we invalidate
+       * unit->gl_texture references to deleted textures so it's safe
+       * to compare unit->gl_texture with gl_texture.  (Without the
+       * hook it would be possible to delete a GL texture and create a
+       * new one with the same name and comparing unit->gl_texture and
+       * gl_texture wouldn't detect that.)
+       *
+       * NB: for foreign textures we don't know how the deletion of
+       * the GL texture objects correspond to the deletion of the
+       * CoglTextures so if there was previously a foreign texture
+       * associated with the texture unit then we can't assume that we
+       * aren't seeing a recycled texture name so we have to bind.
+       */
+      if (unit->gl_texture != gl_texture || unit->is_foreign)
+        {
+          if (unit_index != 1)
+            GE (glBindTexture (gl_target, gl_texture));
+          unit->gl_texture = gl_texture;
+        }
+
+      unit->is_foreign = _cogl_texture_is_foreign (texture);
+
+      /* Disable the previous target if it was different and it's
+       * still enabled */
+      if (unit->enabled && unit->current_gl_target != gl_target)
+        GE (glDisable (unit->current_gl_target));
+
+      if (!G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DISABLE_TEXTURING) &&
+          (!unit->enabled || unit->current_gl_target != gl_target))
+        {
+          GE (glEnable (gl_target));
+          unit->enabled = TRUE;
+          unit->current_gl_target = gl_target;
+        }
+
+      /* The texture_storage_changed boolean indicates if the
+       * CoglTexture's underlying GL texture storage has changed since
+       * it was flushed to the texture unit. We've just flushed the
+       * latest state so we can reset this. */
+      unit->texture_storage_changed = FALSE;
+    }
+  else
+    {
+      /* Even though there may be no difference between the last flushed
+       * texture state and the current layers texture state it may be that the
+       * texture unit has been disabled for some time so we need to assert that
+       * it's enabled now.
+       */
+      if (!G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DISABLE_TEXTURING) &&
+          !unit->enabled)
+        {
+          GE (glEnable (unit->current_gl_target));
+          unit->enabled = TRUE;
+        }
+    }
+
+  if (layers_difference & COGL_MATERIAL_LAYER_STATE_USER_MATRIX)
+    {
+      CoglMaterialLayerState state = COGL_MATERIAL_LAYER_STATE_USER_MATRIX;
+      CoglMaterialLayer *authority =
+        _cogl_material_layer_get_authority (layer, state);
+
+      _cogl_matrix_stack_set (unit->matrix_stack,
+                              &authority->big_state->matrix);
+
+      _cogl_matrix_stack_flush_to_gl (unit->matrix_stack, COGL_MATRIX_TEXTURE);
+    }
+
+  if (layers_difference & COGL_MATERIAL_LAYER_STATE_POINT_SPRITE_COORDS)
+    {
+      CoglMaterialState change = COGL_MATERIAL_LAYER_STATE_POINT_SPRITE_COORDS;
+      CoglMaterialLayer *authority =
+        _cogl_material_layer_get_authority (layer, change);
+      CoglMaterialLayerBigState *big_state = authority->big_state;
+
+      _cogl_set_active_texture_unit (unit_index);
+
+      GE (glTexEnvi (GL_POINT_SPRITE, GL_COORD_REPLACE,
+                     big_state->point_sprite_coords));
+    }
+
+  cogl_handle_ref (layer);
+  if (unit->layer != COGL_INVALID_HANDLE)
+    cogl_handle_unref (unit->layer);
+
+  unit->layer = layer;
+  unit->layer_changes_since_flush = 0;
+
+  flush_state->i++;
+
+  return TRUE;
+}
+
+static void
+_cogl_material_flush_common_gl_state (CoglMaterial  *material,
+                                      unsigned long  materials_difference,
+                                      unsigned long *layer_differences,
+                                      gboolean       skip_gl_color)
+{
+  CoglMaterialFlushLayerState state;
+
+  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+  _cogl_material_flush_color_blend_alpha_depth_state (material,
+                                                      materials_difference,
+                                                      skip_gl_color);
+
+  state.i = 0;
+  state.layer_differences = layer_differences;
+  _cogl_material_foreach_layer (material,
+                                flush_layers_common_gl_state_cb,
+                                &state);
+
+  /* Disable additional texture units that may have previously been in use.. */
+  for (; state.i < ctx->texture_units->len; state.i++)
+    _cogl_disable_texture_unit (state.i);
+}
+
+/* Re-assert the layer's wrap modes on the given CoglTexture.
+ *
+ * Note: we don't simply forward the wrap modes to layer->texture
+ * since the actual texture being used may have been overridden.
+ */
+static void
+_cogl_material_layer_forward_wrap_modes (CoglMaterialLayer *layer,
+                                         CoglHandle texture)
+{
+  CoglMaterialWrapModeInternal wrap_mode_s, wrap_mode_t, wrap_mode_p;
+  GLenum gl_wrap_mode_s, gl_wrap_mode_t, gl_wrap_mode_p;
+
+  if (texture == COGL_INVALID_HANDLE)
+    return;
+
+  _cogl_material_layer_get_wrap_modes (layer,
+                                       &wrap_mode_s,
+                                       &wrap_mode_t,
+                                       &wrap_mode_p);
+
+  /* Update the wrap mode on the texture object. The texture backend
+     should cache the value so that it will be a no-op if the object
+     already has the same wrap mode set. The backend is best placed to
+     do this because it knows how many of the coordinates will
+     actually be used (ie, a 1D texture only cares about the 's'
+     coordinate but a 3D texture would use all three). GL uses the
+     wrap mode as part of the texture object state but we are
+     pretending it's part of the per-layer environment state. This
+     will break if the application tries to use different modes in
+     different layers using the same texture. */
+
+  if (wrap_mode_s == COGL_MATERIAL_WRAP_MODE_INTERNAL_AUTOMATIC)
+    gl_wrap_mode_s = GL_CLAMP_TO_EDGE;
+  else
+    gl_wrap_mode_s = wrap_mode_s;
+
+  if (wrap_mode_t == COGL_MATERIAL_WRAP_MODE_INTERNAL_AUTOMATIC)
+    gl_wrap_mode_t = GL_CLAMP_TO_EDGE;
+  else
+    gl_wrap_mode_t = wrap_mode_t;
+
+  if (wrap_mode_p == COGL_MATERIAL_WRAP_MODE_INTERNAL_AUTOMATIC)
+    gl_wrap_mode_p = GL_CLAMP_TO_EDGE;
+  else
+    gl_wrap_mode_p = wrap_mode_p;
+
+  _cogl_texture_set_wrap_mode_parameters (texture,
+                                          gl_wrap_mode_s,
+                                          gl_wrap_mode_t,
+                                          gl_wrap_mode_p);
+}
+
+/* OpenGL associates the min/mag filters and repeat modes with the
+ * texture object not the texture unit so we always have to re-assert
+ * the filter and repeat modes whenever we use a texture since it may
+ * be referenced by multiple materials with different modes.
+ *
+ * XXX: GL_ARB_sampler_objects fixes this in OpenGL so we should
+ * eventually look at using this extension when available.
+ */
+static void
+foreach_texture_unit_update_filter_and_wrap_modes (void)
+{
+  int i;
+
+  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+  for (i = 0; i < ctx->texture_units->len; i++)
+    {
+      CoglTextureUnit *unit =
+        &g_array_index (ctx->texture_units, CoglTextureUnit, i);
+
+      if (!unit->enabled)
+        break;
+
+      if (unit->layer)
+        {
+          CoglHandle texture = _cogl_material_layer_get_texture (unit->layer);
+          CoglMaterialFilter min;
+          CoglMaterialFilter mag;
+
+          _cogl_material_layer_get_filters (unit->layer, &min, &mag);
+          _cogl_texture_set_filters (texture, min, mag);
+
+          _cogl_material_layer_forward_wrap_modes (unit->layer, texture);
+        }
+    }
+}
+
+typedef struct
+{
+  int i;
+  unsigned long *layer_differences;
+} CoglMaterialCompareLayersState;
+
+static gboolean
+compare_layer_differences_cb (CoglMaterialLayer *layer, void *user_data)
+{
+  CoglMaterialCompareLayersState *state = user_data;
+  CoglTextureUnit *unit = _cogl_get_texture_unit (state->i);
+
+  if (unit->layer == layer)
+    state->layer_differences[state->i] = unit->layer_changes_since_flush;
+  else if (unit->layer)
+    {
+      state->layer_differences[state->i] = unit->layer_changes_since_flush;
+      state->layer_differences[state->i] |=
+        _cogl_material_layer_compare_differences (layer, unit->layer);
+    }
+  else
+    state->layer_differences[state->i] = COGL_MATERIAL_LAYER_STATE_ALL_SPARSE;
+
+  /* XXX: There is always a possibility that a CoglTexture's
+   * underlying GL texture storage has been changed since it was last
+   * bound to a texture unit which is why we have a callback into
+   * _cogl_material_texture_storage_change_notify whenever a textures
+   * underlying GL texture storage changes which will set the
+   * unit->texture_intern_changed flag. If we see that's been set here
+   * then we force an update of the texture state...
+   */
+  if (unit->texture_storage_changed)
+    state->layer_differences[state->i] |= COGL_MATERIAL_LAYER_STATE_TEXTURE;
+
+  state->i++;
+
+  return TRUE;
+}
+
+typedef struct
+{
+  const CoglMaterialBackend *backend;
+  CoglMaterial *material;
+  unsigned long *layer_differences;
+  gboolean error_adding_layer;
+  gboolean added_layer;
+} CoglMaterialBackendAddLayerState;
+
+
+static gboolean
+backend_add_layer_cb (CoglMaterialLayer *layer,
+                      void *user_data)
+{
+  CoglMaterialBackendAddLayerState *state = user_data;
+  const CoglMaterialBackend *backend = state->backend;
+  CoglMaterial *material = state->material;
+  int unit_index = _cogl_material_layer_get_unit_index (layer);
+  CoglTextureUnit *unit = _cogl_get_texture_unit (unit_index);
+
+  _COGL_GET_CONTEXT (ctx, FALSE);
+
+  /* NB: We don't support the random disabling of texture
+   * units, so as soon as we hit a disabled unit we know all
+   * subsequent units are also disabled */
+  if (!unit->enabled)
+    return FALSE;
+
+  if (G_UNLIKELY (unit_index >= backend->get_max_texture_units ()))
+    {
+      int j;
+      for (j = unit_index; j < ctx->texture_units->len; j++)
+        _cogl_disable_texture_unit (j);
+      /* TODO: although this isn't considered an error that
+       * warrants falling back to a different backend we
+       * should print a warning here. */
+      return FALSE;
+    }
+
+  /* Either generate per layer code snippets or setup the
+   * fixed function glTexEnv for each layer... */
+  if (G_LIKELY (backend->add_layer (material,
+                                    layer,
+                                    state->layer_differences[unit_index])))
+    state->added_layer = TRUE;
+  else
+    {
+      state->error_adding_layer = TRUE;
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+/*
+ * _cogl_material_flush_gl_state:
+ *
+ * Details of override options:
+ * ->fallback_mask: is a bitmask of the material layers that need to be
+ *    replaced with the default, fallback textures. The fallback textures are
+ *    fully transparent textures so they hopefully wont contribute to the
+ *    texture combining.
+ *
+ *    The intention of fallbacks is to try and preserve
+ *    the number of layers the user is expecting so that texture coordinates
+ *    they gave will mostly still correspond to the textures they intended, and
+ *    have a fighting chance of looking close to their originally intended
+ *    result.
+ *
+ * ->disable_mask: is a bitmask of the material layers that will simply have
+ *    texturing disabled. It's only really intended for disabling all layers
+ *    > X; i.e. we'd expect to see a contiguous run of 0 starting from the LSB
+ *    and at some point the remaining bits flip to 1. It might work to disable
+ *    arbitrary layers; though I'm not sure a.t.m how OpenGL would take to
+ *    that.
+ *
+ *    The intention of the disable_mask is for emitting geometry when the user
+ *    hasn't supplied enough texture coordinates for all the layers and it's
+ *    not possible to auto generate default texture coordinates for those
+ *    layers.
+ *
+ * ->layer0_override_texture: forcibly tells us to bind this GL texture name for
+ *    layer 0 instead of plucking the gl_texture from the CoglTexture of layer
+ *    0.
+ *
+ *    The intention of this is for any primitives that supports sliced textures.
+ *    The code will can iterate each of the slices and re-flush the material
+ *    forcing the GL texture of each slice in turn.
+ *
+ * ->wrap_mode_overrides: overrides the wrap modes set on each
+ *    layer. This is used to implement the automatic wrap mode.
+ *
+ * XXX: It might also help if we could specify a texture matrix for code
+ *    dealing with slicing that would be multiplied with the users own matrix.
+ *
+ *    Normaly texture coords in the range [0, 1] refer to the extents of the
+ *    texture, but when your GL texture represents a slice of the real texture
+ *    (from the users POV) then a texture matrix would be a neat way of
+ *    transforming the mapping for each slice.
+ *
+ *    Currently for textured rectangles we manually calculate the texture
+ *    coords for each slice based on the users given coords, but this solution
+ *    isn't ideal, and can't be used with CoglVertexBuffers.
+ */
+void
+_cogl_material_flush_gl_state (CoglMaterial *material,
+                               gboolean skip_gl_color)
+{
+  unsigned long    materials_difference;
+  int              n_layers;
+  unsigned long   *layer_differences = NULL;
+  int              i;
+  CoglTextureUnit *unit1;
+
+  COGL_STATIC_TIMER (material_flush_timer,
+                     "Mainloop", /* parent */
+                     "Material Flush",
+                     "The time spent flushing material state",
+                     0 /* no application private data */);
+
+  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+  COGL_TIMER_START (_cogl_uprof_context, material_flush_timer);
+
+  if (ctx->current_material == material)
+    materials_difference = ctx->current_material_changes_since_flush;
+  else if (ctx->current_material)
+    {
+      materials_difference = ctx->current_material_changes_since_flush;
+      materials_difference |=
+        _cogl_material_compare_differences (ctx->current_material,
+                                            material);
+    }
+  else
+    materials_difference = COGL_MATERIAL_STATE_ALL_SPARSE;
+
+  /* Get a layer_differences mask for each layer to be flushed */
+  n_layers = cogl_material_get_n_layers (material);
+  if (n_layers)
+    {
+      CoglMaterialCompareLayersState state;
+      layer_differences = g_alloca (sizeof (unsigned long *) * n_layers);
+      memset (layer_differences, 0, sizeof (layer_differences));
+      state.i = 0;
+      state.layer_differences = layer_differences;
+      _cogl_material_foreach_layer (material,
+                                    compare_layer_differences_cb,
+                                    &state);
+    }
+
+  /* First flush everything that's the same regardless of which
+   * material backend is being used...
+   *
+   * 1) top level state:
+   *  glColor (or skip if a vertex attribute is being used for color)
+   *  blend state
+   *  alpha test state (except for GLES 2.0)
+   *
+   * 2) then foreach layer:
+   *  determine gl_target/gl_texture
+   *  bind texture
+   *  enable/disable target
+   *  flush user matrix
+   *
+   *  Note: After _cogl_material_flush_common_gl_state you can expect
+   *  all state of the layers corresponding texture unit to be
+   *  updated.
+   */
+  _cogl_material_flush_common_gl_state (material,
+                                        materials_difference,
+                                        layer_differences,
+                                        skip_gl_color);
+
+  /* Now flush the fragment processing state according to the current
+   * fragment processing backend.
+   *
+   * Note: Some of the backends may not support the current material
+   * configuration and in that case it will report an error and we
+   * will fallback to a different backend.
+   *
+   * NB: if material->backend != COGL_MATERIAL_BACKEND_UNDEFINED then
+   * we have previously managed to successfully flush this material
+   * with the given backend so we will simply use that to avoid
+   * fallback code paths.
+   */
+
+  if (material->backend == COGL_MATERIAL_BACKEND_UNDEFINED)
+    _cogl_material_set_backend (material, COGL_MATERIAL_BACKEND_DEFAULT);
+
+  for (i = material->backend;
+       i < G_N_ELEMENTS (_cogl_material_backends);
+       i++, _cogl_material_set_backend (material, i))
+    {
+      const CoglMaterialBackend *backend = _cogl_material_backends[i];
+      CoglMaterialBackendAddLayerState state;
+
+      /* E.g. For backends generating code they can setup their
+       * scratch buffers here... */
+      if (G_UNLIKELY (!backend->start (material,
+                                       n_layers,
+                                       materials_difference)))
+        continue;
+
+      state.backend = backend;
+      state.material = material;
+      state.layer_differences = layer_differences;
+      state.error_adding_layer = FALSE;
+      state.added_layer = FALSE;
+      _cogl_material_foreach_layer (material,
+                                    backend_add_layer_cb,
+                                    &state);
+
+      if (G_UNLIKELY (state.error_adding_layer))
+        continue;
+
+      if (!state.added_layer &&
+          backend->passthrough &&
+          G_UNLIKELY (!backend->passthrough (material)))
+        continue;
+
+      /* For backends generating code they may compile and link their
+       * programs here, update any uniforms and tell OpenGL to use
+       * that program.
+       */
+      if (G_UNLIKELY (!backend->end (material, materials_difference)))
+        continue;
+
+      break;
+    }
+
+  /* FIXME: This reference is actually resulting in lots of
+   * copy-on-write reparenting because one-shot materials end up
+   * living for longer than necessary and so any later modification of
+   * the parent will cause a copy-on-write.
+   *
+   * XXX: The issue should largely go away when we switch to using
+   * weak materials for overrides.
+   */
+  cogl_object_ref (material);
+  if (ctx->current_material != NULL)
+    cogl_object_unref (ctx->current_material);
+  ctx->current_material = material;
+  ctx->current_material_changes_since_flush = 0;
+  ctx->current_material_skip_gl_color = skip_gl_color;
+
+  /* Handle the fact that OpenGL associates texture filter and wrap
+   * modes with the texture objects not the texture units... */
+  foreach_texture_unit_update_filter_and_wrap_modes ();
+
+  /* If this material has more than one layer then we always need
+   * to make sure we rebind the texture for unit 1.
+   *
+   * NB: various components of Cogl may temporarily bind arbitrary
+   * textures to texture unit 1 so they can query and modify texture
+   * object parameters. cogl-material.c (See
+   * _cogl_bind_gl_texture_transient)
+   */
+  unit1 = _cogl_get_texture_unit (1);
+  if (unit1->enabled && unit1->dirty_gl_texture)
+    {
+      _cogl_set_active_texture_unit (1);
+      GE (glBindTexture (unit1->current_gl_target, unit1->gl_texture));
+      unit1->dirty_gl_texture = FALSE;
+    }
+
+  COGL_TIMER_STOP (_cogl_uprof_context, material_flush_timer);
+}
 
index 54fb380..54b14b8 100644 (file)
@@ -33,6 +33,7 @@
 #include "cogl-material.h"
 #include "cogl-matrix.h"
 #include "cogl-handle.h"
+#include "cogl-profile.h"
 
 #include <glib.h>
 
@@ -551,6 +552,9 @@ typedef enum
   COGL_MATERIAL_PROGRAM_TYPE_FIXED
 } CoglMaterialProgramType;
 
+extern const CoglMaterialBackend *
+_cogl_material_backends[COGL_MATERIAL_N_BACKENDS];
+
 void
 _cogl_material_init_default_material (void);
 
@@ -681,6 +685,8 @@ _cogl_use_program (CoglHandle program_handle, CoglMaterialProgramType type);
 unsigned int
 _cogl_get_n_args_for_combine_func (GLint func);
 
+void
+_cogl_material_set_backend (CoglMaterial *material, int backend);
 
 CoglMaterial *
 _cogl_material_get_parent (CoglMaterial *material);
@@ -689,9 +695,9 @@ void
 _cogl_material_get_colorubv (CoglMaterial *material,
                              guint8       *color);
 
-void
-_cogl_material_flush_gl_state (CoglMaterial *material,
-                               gboolean skip_gl_state);
+unsigned long
+_cogl_material_compare_differences (CoglMaterial *material0,
+                                    CoglMaterial *material1);
 
 gboolean
 _cogl_material_equal (CoglMaterial *material0,
@@ -705,6 +711,17 @@ void
 _cogl_material_journal_unref (CoglMaterial *material);
 
 void
+_cogl_material_layer_get_wrap_modes (CoglMaterialLayer *layer,
+                                     CoglMaterialWrapModeInternal *wrap_mode_s,
+                                     CoglMaterialWrapModeInternal *wrap_mode_t,
+                                     CoglMaterialWrapModeInternal *wrap_mode_r);
+
+void
+_cogl_material_layer_get_filters (CoglMaterialLayer *layer,
+                                  CoglMaterialFilter *min_filter,
+                                  CoglMaterialFilter *mag_filter);
+
+void
 _cogl_material_set_user_program (CoglMaterial *material,
                                  CoglHandle program);
 
@@ -715,9 +732,6 @@ void
 _cogl_material_apply_legacy_state (CoglMaterial *material);
 
 void
-_cogl_gl_use_program_wrapper (GLuint program);
-
-void
 _cogl_material_apply_overrides (CoglMaterial *material,
                                 CoglMaterialFlushOptions *options);
 
index a32ce1e..f266105 100644 (file)
 #include "cogl-journal-private.h"
 #include "cogl-color-private.h"
 #include "cogl-profile.h"
-#ifndef HAVE_COGL_GLES
-#include "cogl-program.h"
-#endif
 
 #include <glib.h>
 #include <glib/gprintf.h>
 #include <string.h>
 
-/*
- * GL/GLES compatability defines for material thingies:
- */
-
-#ifdef HAVE_COGL_GLES2
-#include "../gles/cogl-gles2-wrapper.h"
-#endif
-
-#ifdef HAVE_COGL_GL
-#define glActiveTexture ctx->drv.pf_glActiveTexture
-#define glClientActiveTexture ctx->drv.pf_glClientActiveTexture
-#define glBlendFuncSeparate ctx->drv.pf_glBlendFuncSeparate
-#define glBlendEquation ctx->drv.pf_glBlendEquation
-#define glBlendColor ctx->drv.pf_glBlendColor
-#define glBlendEquationSeparate ctx->drv.pf_glBlendEquationSeparate
-
-#define glProgramString ctx->drv.pf_glProgramString
-#define glBindProgram ctx->drv.pf_glBindProgram
-#define glDeletePrograms ctx->drv.pf_glDeletePrograms
-#define glGenPrograms ctx->drv.pf_glGenPrograms
-#define glProgramLocalParameter4fv ctx->drv.pf_glProgramLocalParameter4fv
-#define glUseProgram ctx->drv.pf_glUseProgram
-#endif
-
-/* These aren't defined in the GLES headers */
-#ifndef GL_POINT_SPRITE
-#define GL_POINT_SPRITE 0x8861
-#endif
-#ifndef GL_COORD_REPLACE
-#define GL_COORD_REPLACE 0x8862
-#endif
-#ifndef GL_CLAMP_TO_BORDER
-#define GL_CLAMP_TO_BORDER 0x812d
-#endif
-
 #define COGL_MATERIAL_LAYER(X) ((CoglMaterialLayer *)(X))
 
 typedef gboolean (*CoglMaterialStateComparitor) (CoglMaterial *authority0,
@@ -100,7 +62,7 @@ static void handle_automatic_blend_enable (CoglMaterial *material,
                                            CoglMaterialState changes);
 static void recursively_free_layer_caches (CoglMaterial *material);
 
-static const CoglMaterialBackend *backends[COGL_MATERIAL_N_BACKENDS];
+const CoglMaterialBackend *_cogl_material_backends[COGL_MATERIAL_N_BACKENDS];
 
 #ifdef COGL_MATERIAL_BACKEND_GLSL
 #include "cogl-material-glsl-private.h"
@@ -220,13 +182,16 @@ _cogl_material_init_default_material (void)
 
   /* Take this opportunity to setup the fragment processing backends... */
 #ifdef COGL_MATERIAL_BACKEND_GLSL
-  backends[COGL_MATERIAL_BACKEND_GLSL] = &_cogl_material_glsl_backend;
+  _cogl_material_backends[COGL_MATERIAL_BACKEND_GLSL] =
+    &_cogl_material_glsl_backend;
 #endif
 #ifdef COGL_MATERIAL_BACKEND_ARBFP
-  backends[COGL_MATERIAL_BACKEND_ARBFP] = &_cogl_material_arbfp_backend;
+  _cogl_material_backends[COGL_MATERIAL_BACKEND_ARBFP] =
+    &_cogl_material_arbfp_backend;
 #endif
 #ifdef COGL_MATERIAL_BACKEND_FIXED
-  backends[COGL_MATERIAL_BACKEND_FIXED] = &_cogl_material_fixed_backend;
+  _cogl_material_backends[COGL_MATERIAL_BACKEND_FIXED] =
+    &_cogl_material_fixed_backend;
 #endif
 
   _cogl_material_node_init (COGL_MATERIAL_NODE (material));
@@ -361,8 +326,12 @@ _cogl_material_set_parent (CoglMaterial *material, CoglMaterial *parent)
    * may be notified here...
    */
   if (material->backend != COGL_MATERIAL_BACKEND_UNDEFINED &&
-      backends[material->backend]->material_set_parent_notify)
-    backends[material->backend]->material_set_parent_notify (material);
+      _cogl_material_backends[material->backend]->material_set_parent_notify)
+    {
+      const CoglMaterialBackend *backend =
+        _cogl_material_backends[material->backend];
+      backend->material_set_parent_notify (material);
+    }
 }
 
 /* XXX: Always have an eye out for opportunities to lower the cost of
@@ -443,8 +412,12 @@ static void
 _cogl_material_backend_free_priv (CoglMaterial *material)
 {
   if (material->backend != COGL_MATERIAL_BACKEND_UNDEFINED &&
-      backends[material->backend]->free_priv)
-    backends[material->backend]->free_priv (material);
+      _cogl_material_backends[material->backend]->free_priv)
+    {
+      const CoglMaterialBackend *backend =
+        _cogl_material_backends[material->backend];
+      backend->free_priv (material);
+    }
 }
 
 static void
@@ -818,7 +791,7 @@ _cogl_material_needs_blending_enabled (CoglMaterial    *material,
   return FALSE;
 }
 
-static void
+void
 _cogl_material_set_backend (CoglMaterial *material, int backend)
 {
   _cogl_material_backend_free_priv (material);
@@ -1042,10 +1015,12 @@ _cogl_material_pre_change_notify (CoglMaterial     *material,
     _cogl_material_set_backend (material, COGL_MATERIAL_BACKEND_UNDEFINED);
 
   if (material->backend != COGL_MATERIAL_BACKEND_UNDEFINED &&
-      backends[material->backend]->material_pre_change_notify)
-    backends[material->backend]->material_pre_change_notify (material,
-                                                             change,
-                                                             new_color);
+      _cogl_material_backends[material->backend]->material_pre_change_notify)
+    {
+      const CoglMaterialBackend *backend =
+        _cogl_material_backends[material->backend];
+      backend->material_pre_change_notify (material, change, new_color);
+    }
 
   /*
    * There is an arbitrary tree of descendants of this material; any of
@@ -1327,8 +1302,12 @@ _cogl_material_backend_layer_change_notify (CoglMaterialLayer *layer,
    */
   for (i = 0; i < COGL_MATERIAL_N_BACKENDS; i++)
     {
-      if (layer->backend_priv[i] && backends[i]->layer_pre_change_notify)
-        backends[i]->layer_pre_change_notify (layer, change);
+      if (layer->backend_priv[i] &&
+          _cogl_material_backends[i]->layer_pre_change_notify)
+        {
+          const CoglMaterialBackend *backend = _cogl_material_backends[i];
+          backend->layer_pre_change_notify (layer, change);
+        }
     }
 }
 
@@ -2363,7 +2342,7 @@ cogl_material_get_layer_wrap_mode_p (CoglMaterial *material, int layer_index)
   return cogl_material_layer_get_wrap_mode_p (layer);
 }
 
-static void
+void
 _cogl_material_layer_get_wrap_modes (CoglMaterialLayer *layer,
                                      CoglMaterialWrapModeInternal *wrap_mode_s,
                                      CoglMaterialWrapModeInternal *wrap_mode_t,
@@ -3058,7 +3037,7 @@ _cogl_material_layers_equal (CoglMaterial *authority0,
 }
 
 /* Determine the mask of differences between two materials */
-static unsigned long
+unsigned long
 _cogl_material_compare_differences (CoglMaterial *material0,
                                     CoglMaterial *material1)
 {
@@ -4286,8 +4265,12 @@ _cogl_material_layer_free (CoglMaterialLayer *layer)
    */
   for (i = 0; i < COGL_MATERIAL_N_BACKENDS; i++)
     {
-      if (layer->backend_priv[i] && backends[i]->free_layer_priv)
-        backends[i]->free_layer_priv (layer);
+      if (layer->backend_priv[i] &&
+          _cogl_material_backends[i]->free_layer_priv)
+        {
+          const CoglMaterialBackend *backend = _cogl_material_backends[i];
+          backend->free_layer_priv (layer);
+        }
     }
 
   if (layer->differences & COGL_MATERIAL_LAYER_STATE_TEXTURE)
@@ -4895,7 +4878,7 @@ _cogl_material_layer_has_user_matrix (CoglMaterialLayer *layer)
   return _cogl_material_layer_get_parent (authority) ? TRUE : FALSE;
 }
 
-static void
+void
 _cogl_material_layer_get_filters (CoglMaterialLayer *layer,
                                   CoglMaterialFilter *min_filter,
                                   CoglMaterialFilter *mag_filter)
@@ -5078,968 +5061,6 @@ cogl_material_set_point_size (CoglHandle handle,
                                    _cogl_material_point_size_equal);
 }
 
-void
-_cogl_gl_use_program_wrapper (GLuint program)
-{
-#ifdef COGL_MATERIAL_BACKEND_GLSL
-  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
-
-  if (ctx->current_gl_program == program)
-    return;
-
-  if (program)
-    {
-      GLenum gl_error;
-
-      while ((gl_error = glGetError ()) != GL_NO_ERROR)
-        ;
-      glUseProgram (program);
-      if (glGetError () != GL_NO_ERROR)
-        {
-          GE (glUseProgram (0));
-          ctx->current_gl_program = 0;
-          return;
-        }
-    }
-  else
-    GE (glUseProgram (0));
-
-  ctx->current_gl_program = program;
-#endif
-}
-
-static void
-disable_glsl (void)
-{
-#ifdef COGL_MATERIAL_BACKEND_GLSL
-  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
-
-  if (ctx->current_use_program_type == COGL_MATERIAL_PROGRAM_TYPE_GLSL)
-    _cogl_gl_use_program_wrapper (0);
-#endif
-}
-
-static void
-disable_arbfp (void)
-{
-#ifdef COGL_MATERIAL_BACKEND_ARBFP
-  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
-
-  if (ctx->current_use_program_type == COGL_MATERIAL_PROGRAM_TYPE_ARBFP)
-    GE (glDisable (GL_FRAGMENT_PROGRAM_ARB));
-#endif
-}
-
-void
-_cogl_use_program (CoglHandle program_handle, CoglMaterialProgramType type)
-{
-  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
-
-  switch (type)
-    {
-#ifdef COGL_MATERIAL_BACKEND_GLSL
-    case COGL_MATERIAL_PROGRAM_TYPE_GLSL:
-      {
-        /* The GLES2 backend currently manages its own codegen for
-         * fixed function API fallbacks and manages its own shader
-         * state.  */
-#ifndef HAVE_COGL_GLES2
-        CoglProgram *program =
-          _cogl_program_pointer_from_handle (program_handle);
-
-        _cogl_gl_use_program_wrapper (program->gl_handle);
-        disable_arbfp ();
-#endif
-
-        ctx->current_use_program_type = type;
-        break;
-      }
-#else
-    case COGL_MATERIAL_PROGRAM_TYPE_GLSL:
-      g_warning ("Unexpected use of GLSL backend!");
-      break;
-#endif
-#ifdef COGL_MATERIAL_BACKEND_ARBFP
-    case COGL_MATERIAL_PROGRAM_TYPE_ARBFP:
-
-      /* _cogl_gl_use_program_wrapper can be called by cogl-program.c
-       * so we can't bailout without making sure we glUseProgram (0)
-       * first. */
-      disable_glsl ();
-
-      if (ctx->current_use_program_type == COGL_MATERIAL_PROGRAM_TYPE_ARBFP)
-        break;
-
-      GE (glEnable (GL_FRAGMENT_PROGRAM_ARB));
-
-      ctx->current_use_program_type = type;
-      break;
-#else
-    case COGL_MATERIAL_PROGRAM_TYPE_ARBFP:
-      g_warning ("Unexpected use of GLSL backend!");
-      break;
-#endif
-#ifdef COGL_MATERIAL_BACKEND_FIXED
-    case COGL_MATERIAL_PROGRAM_TYPE_FIXED:
-
-      /* _cogl_gl_use_program_wrapper can be called by cogl-program.c
-       * so we can't bailout without making sure we glUseProgram (0)
-       * first. */
-      disable_glsl ();
-
-      if (ctx->current_use_program_type == COGL_MATERIAL_PROGRAM_TYPE_FIXED)
-        break;
-
-      disable_arbfp ();
-
-      ctx->current_use_program_type = type;
-#endif
-    }
-}
-
-#if defined (COGL_MATERIAL_BACKEND_GLSL) || \
-    defined (COGL_MATERIAL_BACKEND_ARBFP)
-int
-_cogl_get_max_texture_image_units (void)
-{
-  _COGL_GET_CONTEXT (ctx, 0);
-
-  /* This function is called quite often so we cache the value to
-     avoid too many GL calls */
-  if (G_UNLIKELY (ctx->max_texture_image_units == -1))
-    {
-      ctx->max_texture_image_units = 1;
-      GE (glGetIntegerv (GL_MAX_TEXTURE_IMAGE_UNITS,
-                         &ctx->max_texture_image_units));
-    }
-
-  return ctx->max_texture_image_units;
-}
-#endif
-
-static void
-_cogl_material_layer_get_texture_info (CoglMaterialLayer *layer,
-                                       CoglHandle *texture,
-                                       GLuint *gl_texture,
-                                       GLuint *gl_target)
-{
-  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
-
-  *texture = layer->texture;
-  if (G_UNLIKELY (*texture == COGL_INVALID_HANDLE))
-    *texture = ctx->default_gl_texture_2d_tex;
-  if (layer->texture_overridden)
-    {
-      *gl_texture = layer->slice_gl_texture;
-      *gl_target = layer->slice_gl_target;
-    }
-  else
-    cogl_texture_get_gl_texture (*texture, gl_texture, gl_target);
-}
-
-#ifndef HAVE_COGL_GLES
-
-static gboolean
-blend_factor_uses_constant (GLenum blend_factor)
-{
-  return (blend_factor == GL_CONSTANT_COLOR ||
-          blend_factor == GL_ONE_MINUS_CONSTANT_COLOR ||
-          blend_factor == GL_CONSTANT_ALPHA ||
-          blend_factor == GL_ONE_MINUS_CONSTANT_ALPHA);
-}
-
-#endif
-
-static void
-flush_depth_state (CoglMaterialDepthState *depth_state)
-{
-  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
-
-  if (ctx->depth_test_function_cache != depth_state->depth_test_function)
-    {
-      GE (glDepthFunc (depth_state->depth_test_function));
-      ctx->depth_test_function_cache = depth_state->depth_test_function;
-    }
-
-  if (ctx->depth_writing_enabled_cache != depth_state->depth_writing_enabled)
-    {
-      GE (glDepthMask (depth_state->depth_writing_enabled ?
-                       GL_TRUE : GL_FALSE));
-      ctx->depth_writing_enabled_cache = depth_state->depth_writing_enabled;
-    }
-
-#ifndef COGL_HAS_GLES
-  if (ctx->depth_range_near_cache != depth_state->depth_range_near ||
-      ctx->depth_range_far_cache != depth_state->depth_range_far)
-    {
-#ifdef COGL_HAS_GLES2
-      GE (glDepthRangef (depth_state->depth_range_near,
-                         depth_state->depth_range_far));
-#else
-      GE (glDepthRange (depth_state->depth_range_near,
-                        depth_state->depth_range_far));
-#endif
-      ctx->depth_range_near_cache = depth_state->depth_range_near;
-      ctx->depth_range_far_cache = depth_state->depth_range_far;
-    }
-#endif /* COGL_HAS_GLES */
-}
-
-static void
-_cogl_material_flush_color_blend_alpha_depth_state (
-                                            CoglMaterial *material,
-                                            unsigned long materials_difference,
-                                            gboolean      skip_gl_color)
-{
-  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
-
-  if (!skip_gl_color)
-    {
-      if ((materials_difference & COGL_MATERIAL_STATE_COLOR) ||
-          /* Assume if we were previously told to skip the color, then
-           * the current color needs updating... */
-          ctx->current_material_skip_gl_color)
-        {
-          CoglMaterial *authority =
-            _cogl_material_get_authority (material, COGL_MATERIAL_STATE_COLOR);
-          GE (glColor4ub (cogl_color_get_red_byte (&authority->color),
-                          cogl_color_get_green_byte (&authority->color),
-                          cogl_color_get_blue_byte (&authority->color),
-                          cogl_color_get_alpha_byte (&authority->color)));
-        }
-    }
-
-  if (materials_difference & COGL_MATERIAL_STATE_LIGHTING)
-    {
-      CoglMaterial *authority =
-        _cogl_material_get_authority (material, COGL_MATERIAL_STATE_LIGHTING);
-      CoglMaterialLightingState *lighting_state =
-        &authority->big_state->lighting_state;
-
-      /* FIXME - we only need to set these if lighting is enabled... */
-      GLfloat shininess = lighting_state->shininess * 128.0f;
-
-      GE (glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT, lighting_state->ambient));
-      GE (glMaterialfv (GL_FRONT_AND_BACK, GL_DIFFUSE, lighting_state->diffuse));
-      GE (glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, lighting_state->specular));
-      GE (glMaterialfv (GL_FRONT_AND_BACK, GL_EMISSION, lighting_state->emission));
-      GE (glMaterialfv (GL_FRONT_AND_BACK, GL_SHININESS, &shininess));
-    }
-
-  if (materials_difference & COGL_MATERIAL_STATE_BLEND)
-    {
-      CoglMaterial *authority =
-        _cogl_material_get_authority (material, COGL_MATERIAL_STATE_BLEND);
-      CoglMaterialBlendState *blend_state =
-        &authority->big_state->blend_state;
-
-#if defined (HAVE_COGL_GLES2)
-      gboolean have_blend_equation_seperate = TRUE;
-      gboolean have_blend_func_separate = TRUE;
-#elif defined (HAVE_COGL_GL)
-      gboolean have_blend_equation_seperate = FALSE;
-      gboolean have_blend_func_separate = FALSE;
-      if (ctx->drv.pf_glBlendEquationSeparate) /* Only GL 2.0 + */
-        have_blend_equation_seperate = TRUE;
-      if (ctx->drv.pf_glBlendFuncSeparate) /* Only GL 1.4 + */
-        have_blend_func_separate = TRUE;
-#endif
-
-#ifndef HAVE_COGL_GLES /* GLES 1 only has glBlendFunc */
-      if (blend_factor_uses_constant (blend_state->blend_src_factor_rgb) ||
-          blend_factor_uses_constant (blend_state->blend_src_factor_alpha) ||
-          blend_factor_uses_constant (blend_state->blend_dst_factor_rgb) ||
-          blend_factor_uses_constant (blend_state->blend_dst_factor_alpha))
-        {
-          float red =
-            cogl_color_get_red_float (&blend_state->blend_constant);
-          float green =
-            cogl_color_get_green_float (&blend_state->blend_constant);
-          float blue =
-            cogl_color_get_blue_float (&blend_state->blend_constant);
-          float alpha =
-            cogl_color_get_alpha_float (&blend_state->blend_constant);
-
-
-          GE (glBlendColor (red, green, blue, alpha));
-        }
-
-      if (have_blend_equation_seperate &&
-          blend_state->blend_equation_rgb != blend_state->blend_equation_alpha)
-        GE (glBlendEquationSeparate (blend_state->blend_equation_rgb,
-                                     blend_state->blend_equation_alpha));
-      else
-        GE (glBlendEquation (blend_state->blend_equation_rgb));
-
-      if (have_blend_func_separate &&
-          (blend_state->blend_src_factor_rgb != blend_state->blend_src_factor_alpha ||
-           (blend_state->blend_src_factor_rgb !=
-            blend_state->blend_src_factor_alpha)))
-        GE (glBlendFuncSeparate (blend_state->blend_src_factor_rgb,
-                                 blend_state->blend_dst_factor_rgb,
-                                 blend_state->blend_src_factor_alpha,
-                                 blend_state->blend_dst_factor_alpha));
-      else
-#endif
-        GE (glBlendFunc (blend_state->blend_src_factor_rgb,
-                         blend_state->blend_dst_factor_rgb));
-    }
-
-  if (materials_difference & COGL_MATERIAL_STATE_ALPHA_FUNC)
-    {
-      CoglMaterial *authority =
-        _cogl_material_get_authority (material, COGL_MATERIAL_STATE_ALPHA_FUNC);
-      CoglMaterialAlphaFuncState *alpha_state =
-        &authority->big_state->alpha_state;
-
-      /* NB: Currently the Cogl defines are compatible with the GL ones: */
-      GE (glAlphaFunc (alpha_state->alpha_func,
-                       alpha_state->alpha_func_reference));
-    }
-
-  if (materials_difference & COGL_MATERIAL_STATE_DEPTH)
-    {
-      CoglMaterial *authority =
-        _cogl_material_get_authority (material, COGL_MATERIAL_STATE_DEPTH);
-      CoglMaterialDepthState *depth_state = &authority->big_state->depth_state;
-
-      if (depth_state->depth_test_enabled)
-        {
-          if (ctx->depth_test_enabled_cache != TRUE)
-            {
-              GE (glEnable (GL_DEPTH_TEST));
-              ctx->depth_test_enabled_cache = depth_state->depth_test_enabled;
-            }
-          flush_depth_state (depth_state);
-        }
-      else if (ctx->depth_test_enabled_cache != FALSE)
-        {
-          GE (glDisable (GL_DEPTH_TEST));
-          ctx->depth_test_enabled_cache = depth_state->depth_test_enabled;
-        }
-    }
-
-  if (materials_difference & COGL_MATERIAL_STATE_POINT_SIZE)
-    {
-      CoglMaterial *authority =
-        _cogl_material_get_authority (material, COGL_MATERIAL_STATE_POINT_SIZE);
-
-      if (ctx->point_size_cache != authority->big_state->point_size)
-        {
-          GE( glPointSize (authority->big_state->point_size) );
-          ctx->point_size_cache = authority->big_state->point_size;
-        }
-    }
-
-  if (material->real_blend_enable != ctx->gl_blend_enable_cache)
-    {
-      if (material->real_blend_enable)
-        GE (glEnable (GL_BLEND));
-      else
-        GE (glDisable (GL_BLEND));
-      /* XXX: we shouldn't update any other blend state if blending
-       * is disabled! */
-      ctx->gl_blend_enable_cache = material->real_blend_enable;
-    }
-}
-
-static int
-get_max_activateable_texture_units (void)
-{
-  _COGL_GET_CONTEXT (ctx, 0);
-
-  if (G_UNLIKELY (ctx->max_activateable_texture_units == -1))
-    {
-#ifdef HAVE_COGL_GL
-      GLint max_tex_coords;
-      GLint max_combined_tex_units;
-      GE (glGetIntegerv (GL_MAX_TEXTURE_COORDS, &max_tex_coords));
-      GE (glGetIntegerv (GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS,
-                         &max_combined_tex_units));
-      ctx->max_activateable_texture_units =
-        MAX (max_tex_coords - 1, max_combined_tex_units);
-#else
-      GE (glGetIntegerv (GL_MAX_TEXTURE_UNITS,
-                         &ctx->max_activateable_texture_units));
-#endif
-    }
-
-  return ctx->max_activateable_texture_units;
-}
-
-typedef struct
-{
-  int i;
-  unsigned long *layer_differences;
-} CoglMaterialFlushLayerState;
-
-static gboolean
-flush_layers_common_gl_state_cb (CoglMaterialLayer *layer, void *user_data)
-{
-  CoglMaterialFlushLayerState *flush_state = user_data;
-  int                          unit_index = flush_state->i;
-  CoglTextureUnit             *unit = _cogl_get_texture_unit (unit_index);
-  unsigned long                layers_difference =
-    flush_state->layer_differences[unit_index];
-
-  /* There may not be enough texture units so we can bail out if
-   * that's the case...
-   */
-  if (G_UNLIKELY (unit_index >= get_max_activateable_texture_units ()))
-    {
-      static gboolean shown_warning = FALSE;
-
-      if (!shown_warning)
-        {
-          g_warning ("Your hardware does not have enough texture units"
-                     "to handle this many texture layers");
-          shown_warning = TRUE;
-        }
-      return FALSE;
-    }
-
-  if (layers_difference & COGL_MATERIAL_LAYER_STATE_TEXTURE)
-    {
-      CoglMaterialLayer *authority =
-        _cogl_material_layer_get_authority (layer,
-                                            COGL_MATERIAL_LAYER_STATE_TEXTURE);
-      CoglHandle texture = NULL;
-      GLuint     gl_texture;
-      GLenum     gl_target;
-
-      _cogl_material_layer_get_texture_info (authority,
-                                             &texture,
-                                             &gl_texture,
-                                             &gl_target);
-
-      _cogl_set_active_texture_unit (unit_index);
-
-      /* NB: There are several Cogl components and some code in
-       * Clutter that will temporarily bind arbitrary GL textures to
-       * query and modify texture object parameters. If you look at
-       * _cogl_bind_gl_texture_transient() you can see we make sure
-       * that such code always binds to texture unit 1 which means we
-       * can't rely on the unit->gl_texture state if unit->index == 1.
-       *
-       * Because texture unit 1 is a bit special we actually defer any
-       * necessary glBindTexture for it until the end of
-       * _cogl_material_flush_gl_state().
-       *
-       * NB: we get notified whenever glDeleteTextures is used (see
-       * _cogl_delete_gl_texture()) where we invalidate
-       * unit->gl_texture references to deleted textures so it's safe
-       * to compare unit->gl_texture with gl_texture.  (Without the
-       * hook it would be possible to delete a GL texture and create a
-       * new one with the same name and comparing unit->gl_texture and
-       * gl_texture wouldn't detect that.)
-       *
-       * NB: for foreign textures we don't know how the deletion of
-       * the GL texture objects correspond to the deletion of the
-       * CoglTextures so if there was previously a foreign texture
-       * associated with the texture unit then we can't assume that we
-       * aren't seeing a recycled texture name so we have to bind.
-       */
-      if (unit->gl_texture != gl_texture || unit->is_foreign)
-        {
-          if (unit_index != 1)
-            GE (glBindTexture (gl_target, gl_texture));
-          unit->gl_texture = gl_texture;
-        }
-
-      unit->is_foreign = _cogl_texture_is_foreign (texture);
-
-      /* Disable the previous target if it was different and it's
-       * still enabled */
-      if (unit->enabled && unit->current_gl_target != gl_target)
-        GE (glDisable (unit->current_gl_target));
-
-      if (!G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DISABLE_TEXTURING) &&
-          (!unit->enabled || unit->current_gl_target != gl_target))
-        {
-          GE (glEnable (gl_target));
-          unit->enabled = TRUE;
-          unit->current_gl_target = gl_target;
-        }
-
-      /* The texture_storage_changed boolean indicates if the
-       * CoglTexture's underlying GL texture storage has changed since
-       * it was flushed to the texture unit. We've just flushed the
-       * latest state so we can reset this. */
-      unit->texture_storage_changed = FALSE;
-    }
-  else
-    {
-      /* Even though there may be no difference between the last flushed
-       * texture state and the current layers texture state it may be that the
-       * texture unit has been disabled for some time so we need to assert that
-       * it's enabled now.
-       */
-      if (!G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DISABLE_TEXTURING) &&
-          !unit->enabled)
-        {
-          GE (glEnable (unit->current_gl_target));
-          unit->enabled = TRUE;
-        }
-    }
-
-  if (layers_difference & COGL_MATERIAL_LAYER_STATE_USER_MATRIX)
-    {
-      CoglMaterialLayerState state = COGL_MATERIAL_LAYER_STATE_USER_MATRIX;
-      CoglMaterialLayer *authority =
-        _cogl_material_layer_get_authority (layer, state);
-
-      _cogl_matrix_stack_set (unit->matrix_stack,
-                              &authority->big_state->matrix);
-
-      _cogl_matrix_stack_flush_to_gl (unit->matrix_stack, COGL_MATRIX_TEXTURE);
-    }
-
-  if (layers_difference & COGL_MATERIAL_LAYER_STATE_POINT_SPRITE_COORDS)
-    {
-      CoglMaterialState change = COGL_MATERIAL_LAYER_STATE_POINT_SPRITE_COORDS;
-      CoglMaterialLayer *authority =
-        _cogl_material_layer_get_authority (layer, change);
-      CoglMaterialLayerBigState *big_state = authority->big_state;
-
-      _cogl_set_active_texture_unit (unit_index);
-
-      GE (glTexEnvi (GL_POINT_SPRITE, GL_COORD_REPLACE,
-                     big_state->point_sprite_coords));
-    }
-
-  cogl_handle_ref (layer);
-  if (unit->layer != COGL_INVALID_HANDLE)
-    cogl_handle_unref (unit->layer);
-
-  unit->layer = layer;
-  unit->layer_changes_since_flush = 0;
-
-  flush_state->i++;
-
-  return TRUE;
-}
-
-static void
-_cogl_material_flush_common_gl_state (CoglMaterial  *material,
-                                      unsigned long  materials_difference,
-                                      unsigned long *layer_differences,
-                                      gboolean       skip_gl_color)
-{
-  CoglMaterialFlushLayerState state;
-
-  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
-
-  _cogl_material_flush_color_blend_alpha_depth_state (material,
-                                                      materials_difference,
-                                                      skip_gl_color);
-
-  state.i = 0;
-  state.layer_differences = layer_differences;
-  _cogl_material_foreach_layer (material,
-                                flush_layers_common_gl_state_cb,
-                                &state);
-
-  /* Disable additional texture units that may have previously been in use.. */
-  for (; state.i < ctx->texture_units->len; state.i++)
-    _cogl_disable_texture_unit (state.i);
-}
-
-/* Re-assert the layer's wrap modes on the given CoglTexture.
- *
- * Note: we don't simply forward the wrap modes to layer->texture
- * since the actual texture being used may have been overridden.
- */
-static void
-_cogl_material_layer_forward_wrap_modes (CoglMaterialLayer *layer,
-                                         CoglHandle texture)
-{
-  CoglMaterialWrapModeInternal wrap_mode_s, wrap_mode_t, wrap_mode_p;
-  GLenum gl_wrap_mode_s, gl_wrap_mode_t, gl_wrap_mode_p;
-
-  if (texture == COGL_INVALID_HANDLE)
-    return;
-
-  _cogl_material_layer_get_wrap_modes (layer,
-                                       &wrap_mode_s,
-                                       &wrap_mode_t,
-                                       &wrap_mode_p);
-
-  /* Update the wrap mode on the texture object. The texture backend
-     should cache the value so that it will be a no-op if the object
-     already has the same wrap mode set. The backend is best placed to
-     do this because it knows how many of the coordinates will
-     actually be used (ie, a 1D texture only cares about the 's'
-     coordinate but a 3D texture would use all three). GL uses the
-     wrap mode as part of the texture object state but we are
-     pretending it's part of the per-layer environment state. This
-     will break if the application tries to use different modes in
-     different layers using the same texture. */
-
-  if (wrap_mode_s == COGL_MATERIAL_WRAP_MODE_INTERNAL_AUTOMATIC)
-    gl_wrap_mode_s = GL_CLAMP_TO_EDGE;
-  else
-    gl_wrap_mode_s = wrap_mode_s;
-
-  if (wrap_mode_t == COGL_MATERIAL_WRAP_MODE_INTERNAL_AUTOMATIC)
-    gl_wrap_mode_t = GL_CLAMP_TO_EDGE;
-  else
-    gl_wrap_mode_t = wrap_mode_t;
-
-  if (wrap_mode_p == COGL_MATERIAL_WRAP_MODE_INTERNAL_AUTOMATIC)
-    gl_wrap_mode_p = GL_CLAMP_TO_EDGE;
-  else
-    gl_wrap_mode_p = wrap_mode_p;
-
-  _cogl_texture_set_wrap_mode_parameters (texture,
-                                          gl_wrap_mode_s,
-                                          gl_wrap_mode_t,
-                                          gl_wrap_mode_p);
-}
-
-/* OpenGL associates the min/mag filters and repeat modes with the
- * texture object not the texture unit so we always have to re-assert
- * the filter and repeat modes whenever we use a texture since it may
- * be referenced by multiple materials with different modes.
- *
- * XXX: GL_ARB_sampler_objects fixes this in OpenGL so we should
- * eventually look at using this extension when available.
- */
-static void
-foreach_texture_unit_update_filter_and_wrap_modes (void)
-{
-  int i;
-
-  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
-
-  for (i = 0; i < ctx->texture_units->len; i++)
-    {
-      CoglTextureUnit *unit =
-        &g_array_index (ctx->texture_units, CoglTextureUnit, i);
-
-      if (!unit->enabled)
-        break;
-
-      if (unit->layer)
-        {
-          CoglHandle texture = _cogl_material_layer_get_texture (unit->layer);
-          CoglMaterialFilter min;
-          CoglMaterialFilter mag;
-
-          _cogl_material_layer_get_filters (unit->layer, &min, &mag);
-          _cogl_texture_set_filters (texture, min, mag);
-
-          _cogl_material_layer_forward_wrap_modes (unit->layer, texture);
-        }
-    }
-}
-
-typedef struct
-{
-  int i;
-  unsigned long *layer_differences;
-} CoglMaterialCompareLayersState;
-
-static gboolean
-compare_layer_differences_cb (CoglMaterialLayer *layer, void *user_data)
-{
-  CoglMaterialCompareLayersState *state = user_data;
-  CoglTextureUnit *unit = _cogl_get_texture_unit (state->i);
-
-  if (unit->layer == layer)
-    state->layer_differences[state->i] = unit->layer_changes_since_flush;
-  else if (unit->layer)
-    {
-      state->layer_differences[state->i] = unit->layer_changes_since_flush;
-      state->layer_differences[state->i] |=
-        _cogl_material_layer_compare_differences (layer, unit->layer);
-    }
-  else
-    state->layer_differences[state->i] = COGL_MATERIAL_LAYER_STATE_ALL_SPARSE;
-
-  /* XXX: There is always a possibility that a CoglTexture's
-   * underlying GL texture storage has been changed since it was last
-   * bound to a texture unit which is why we have a callback into
-   * _cogl_material_texture_storage_change_notify whenever a textures
-   * underlying GL texture storage changes which will set the
-   * unit->texture_intern_changed flag. If we see that's been set here
-   * then we force an update of the texture state...
-   */
-  if (unit->texture_storage_changed)
-    state->layer_differences[state->i] |= COGL_MATERIAL_LAYER_STATE_TEXTURE;
-
-  state->i++;
-
-  return TRUE;
-}
-
-typedef struct
-{
-  const CoglMaterialBackend *backend;
-  CoglMaterial *material;
-  unsigned long *layer_differences;
-  gboolean error_adding_layer;
-  gboolean added_layer;
-} CoglMaterialBackendAddLayerState;
-
-
-static gboolean
-backend_add_layer_cb (CoglMaterialLayer *layer,
-                      void *user_data)
-{
-  CoglMaterialBackendAddLayerState *state = user_data;
-  const CoglMaterialBackend *backend = state->backend;
-  CoglMaterial *material = state->material;
-  int unit_index = _cogl_material_layer_get_unit_index (layer);
-  CoglTextureUnit *unit = _cogl_get_texture_unit (unit_index);
-
-  _COGL_GET_CONTEXT (ctx, FALSE);
-
-  /* NB: We don't support the random disabling of texture
-   * units, so as soon as we hit a disabled unit we know all
-   * subsequent units are also disabled */
-  if (!unit->enabled)
-    return FALSE;
-
-  if (G_UNLIKELY (unit_index >= backend->get_max_texture_units ()))
-    {
-      int j;
-      for (j = unit_index; j < ctx->texture_units->len; j++)
-        _cogl_disable_texture_unit (j);
-      /* TODO: although this isn't considered an error that
-       * warrants falling back to a different backend we
-       * should print a warning here. */
-      return FALSE;
-    }
-
-  /* Either generate per layer code snippets or setup the
-   * fixed function glTexEnv for each layer... */
-  if (G_LIKELY (backend->add_layer (material,
-                                    layer,
-                                    state->layer_differences[unit_index])))
-    state->added_layer = TRUE;
-  else
-    {
-      state->error_adding_layer = TRUE;
-      return FALSE;
-    }
-
-  return TRUE;
-}
-
-/*
- * _cogl_material_flush_gl_state:
- *
- * Details of override options:
- * ->fallback_mask: is a bitmask of the material layers that need to be
- *    replaced with the default, fallback textures. The fallback textures are
- *    fully transparent textures so they hopefully wont contribute to the
- *    texture combining.
- *
- *    The intention of fallbacks is to try and preserve
- *    the number of layers the user is expecting so that texture coordinates
- *    they gave will mostly still correspond to the textures they intended, and
- *    have a fighting chance of looking close to their originally intended
- *    result.
- *
- * ->disable_mask: is a bitmask of the material layers that will simply have
- *    texturing disabled. It's only really intended for disabling all layers
- *    > X; i.e. we'd expect to see a contiguous run of 0 starting from the LSB
- *    and at some point the remaining bits flip to 1. It might work to disable
- *    arbitrary layers; though I'm not sure a.t.m how OpenGL would take to
- *    that.
- *
- *    The intention of the disable_mask is for emitting geometry when the user
- *    hasn't supplied enough texture coordinates for all the layers and it's
- *    not possible to auto generate default texture coordinates for those
- *    layers.
- *
- * ->layer0_override_texture: forcibly tells us to bind this GL texture name for
- *    layer 0 instead of plucking the gl_texture from the CoglTexture of layer
- *    0.
- *
- *    The intention of this is for any primitives that supports sliced textures.
- *    The code will can iterate each of the slices and re-flush the material
- *    forcing the GL texture of each slice in turn.
- *
- * ->wrap_mode_overrides: overrides the wrap modes set on each
- *    layer. This is used to implement the automatic wrap mode.
- *
- * XXX: It might also help if we could specify a texture matrix for code
- *    dealing with slicing that would be multiplied with the users own matrix.
- *
- *    Normaly texture coords in the range [0, 1] refer to the extents of the
- *    texture, but when your GL texture represents a slice of the real texture
- *    (from the users POV) then a texture matrix would be a neat way of
- *    transforming the mapping for each slice.
- *
- *    Currently for textured rectangles we manually calculate the texture
- *    coords for each slice based on the users given coords, but this solution
- *    isn't ideal, and can't be used with CoglVertexBuffers.
- */
-void
-_cogl_material_flush_gl_state (CoglMaterial *material,
-                               gboolean skip_gl_color)
-{
-  unsigned long    materials_difference;
-  int              n_layers;
-  unsigned long   *layer_differences = NULL;
-  int              i;
-  CoglTextureUnit *unit1;
-
-  COGL_STATIC_TIMER (material_flush_timer,
-                     "Mainloop", /* parent */
-                     "Material Flush",
-                     "The time spent flushing material state",
-                     0 /* no application private data */);
-
-  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
-
-  COGL_TIMER_START (_cogl_uprof_context, material_flush_timer);
-
-  if (ctx->current_material == material)
-    materials_difference = ctx->current_material_changes_since_flush;
-  else if (ctx->current_material)
-    {
-      materials_difference = ctx->current_material_changes_since_flush;
-      materials_difference |=
-        _cogl_material_compare_differences (ctx->current_material,
-                                            material);
-    }
-  else
-    materials_difference = COGL_MATERIAL_STATE_ALL_SPARSE;
-
-  /* Get a layer_differences mask for each layer to be flushed */
-  n_layers = cogl_material_get_n_layers (material);
-  if (n_layers)
-    {
-      CoglMaterialCompareLayersState state;
-      layer_differences = g_alloca (sizeof (unsigned long *) * n_layers);
-      memset (layer_differences, 0, sizeof (layer_differences));
-      state.i = 0;
-      state.layer_differences = layer_differences;
-      _cogl_material_foreach_layer (material,
-                                    compare_layer_differences_cb,
-                                    &state);
-    }
-
-  /* First flush everything that's the same regardless of which
-   * material backend is being used...
-   *
-   * 1) top level state:
-   *  glColor (or skip if a vertex attribute is being used for color)
-   *  blend state
-   *  alpha test state (except for GLES 2.0)
-   *
-   * 2) then foreach layer:
-   *  determine gl_target/gl_texture
-   *  bind texture
-   *  enable/disable target
-   *  flush user matrix
-   *
-   *  Note: After _cogl_material_flush_common_gl_state you can expect
-   *  all state of the layers corresponding texture unit to be
-   *  updated.
-   */
-  _cogl_material_flush_common_gl_state (material,
-                                        materials_difference,
-                                        layer_differences,
-                                        skip_gl_color);
-
-  /* Now flush the fragment processing state according to the current
-   * fragment processing backend.
-   *
-   * Note: Some of the backends may not support the current material
-   * configuration and in that case it will report an error and we
-   * will fallback to a different backend.
-   *
-   * NB: if material->backend != COGL_MATERIAL_BACKEND_UNDEFINED then
-   * we have previously managed to successfully flush this material
-   * with the given backend so we will simply use that to avoid
-   * fallback code paths.
-   */
-
-  if (material->backend == COGL_MATERIAL_BACKEND_UNDEFINED)
-    _cogl_material_set_backend (material, COGL_MATERIAL_BACKEND_DEFAULT);
-
-  for (i = material->backend;
-       i < G_N_ELEMENTS (backends);
-       i++, _cogl_material_set_backend (material, i))
-    {
-      const CoglMaterialBackend *backend = backends[i];
-      CoglMaterialBackendAddLayerState state;
-
-      /* E.g. For backends generating code they can setup their
-       * scratch buffers here... */
-      if (G_UNLIKELY (!backend->start (material,
-                                       n_layers,
-                                       materials_difference)))
-        continue;
-
-      state.backend = backend;
-      state.material = material;
-      state.layer_differences = layer_differences;
-      state.error_adding_layer = FALSE;
-      state.added_layer = FALSE;
-      _cogl_material_foreach_layer (material,
-                                    backend_add_layer_cb,
-                                    &state);
-
-      if (G_UNLIKELY (state.error_adding_layer))
-        continue;
-
-      if (!state.added_layer &&
-          backend->passthrough &&
-          G_UNLIKELY (!backend->passthrough (material)))
-        continue;
-
-      /* For backends generating code they may compile and link their
-       * programs here, update any uniforms and tell OpenGL to use
-       * that program.
-       */
-      if (G_UNLIKELY (!backend->end (material, materials_difference)))
-        continue;
-
-      break;
-    }
-
-  /* FIXME: This reference is actually resulting in lots of
-   * copy-on-write reparenting because one-shot materials end up
-   * living for longer than necessary and so any later modification of
-   * the parent will cause a copy-on-write.
-   *
-   * XXX: The issue should largely go away when we switch to using
-   * weak materials for overrides.
-   */
-  cogl_object_ref (material);
-  if (ctx->current_material != NULL)
-    cogl_object_unref (ctx->current_material);
-  ctx->current_material = material;
-  ctx->current_material_changes_since_flush = 0;
-  ctx->current_material_skip_gl_color = skip_gl_color;
-
-  /* Handle the fact that OpenGL associates texture filter and wrap
-   * modes with the texture objects not the texture units... */
-  foreach_texture_unit_update_filter_and_wrap_modes ();
-
-  /* If this material has more than one layer then we always need
-   * to make sure we rebind the texture for unit 1.
-   *
-   * NB: various components of Cogl may temporarily bind arbitrary
-   * textures to texture unit 1 so they can query and modify texture
-   * object parameters. cogl-material.c (See
-   * _cogl_bind_gl_texture_transient)
-   */
-  unit1 = _cogl_get_texture_unit (1);
-  if (unit1->enabled && unit1->dirty_gl_texture)
-    {
-      _cogl_set_active_texture_unit (1);
-      GE (glBindTexture (unit1->current_gl_target, unit1->gl_texture));
-      unit1->dirty_gl_texture = FALSE;
-    }
-
-  COGL_TIMER_STOP (_cogl_uprof_context, material_flush_timer);
-}
-
 /* While a material is referenced by the Cogl journal we can not allow
  * modifications, so this gives us a mechanism to track journal
  * references separately */
index e035ccb..904b7ef 100644 (file)
@@ -31,6 +31,7 @@
 #include "cogl-context.h"
 #include "cogl-journal-private.h"
 #include "cogl-material-private.h"
+#include "cogl-material-opengl-private.h"
 #include "cogl-framebuffer-private.h"
 #include "cogl-path-private.h"
 #include "cogl-texture-private.h"
index 708130a..e87231e 100644 (file)
@@ -31,6 +31,7 @@
 #include "cogl-journal-private.h"
 #include "cogl-texture-private.h"
 #include "cogl-material-private.h"
+#include "cogl-material-opengl-private.h"
 #include "cogl-vertex-buffer-private.h"
 #include "cogl-framebuffer-private.h"
 
index 54e4882..d91ddc0 100644 (file)
 #include "cogl-vertex-buffer-private.h"
 #include "cogl-texture-private.h"
 #include "cogl-material-private.h"
+#include "cogl-material-opengl-private.h"
 #include "cogl-primitives.h"
 #include "cogl-framebuffer-private.h"
 #include "cogl-journal-private.h"
index 8aa110e..5640204 100644 (file)
@@ -37,6 +37,7 @@
 #include "cogl-util.h"
 #include "cogl-context.h"
 #include "cogl-material-private.h"
+#include "cogl-material-opengl-private.h"
 #include "cogl-winsys.h"
 #include "cogl-framebuffer-private.h"
 #include "cogl-matrix-private.h"
index 73b1fd7..a51e4cf 100644 (file)
@@ -32,6 +32,7 @@
 #include "cogl-handle.h"
 #include "cogl-context.h"
 #include "cogl-journal-private.h"
+#include "cogl-material-opengl-private.h"
 
 #include <glib.h>