diff --git a/clutter/cogl/cogl/cogl-atlas-texture.c b/clutter/cogl/cogl/cogl-atlas-texture.c
index b414b46f3..cf09a6490 100644
--- a/clutter/cogl/cogl/cogl-atlas-texture.c
+++ b/clutter/cogl/cogl/cogl-atlas-texture.c
@@ -374,6 +374,11 @@ _cogl_atlas_texture_migrate_out_of_atlas (CoglAtlasTexture *atlas_tex)
*/
_cogl_journal_flush ();
+ /* Notify cogl-material.c that the texture's underlying GL texture
+ * storage is changing so it knows it may need to bind a new texture
+ * if the CoglTexture is reused with the same texture unit. */
+ _cogl_material_texture_storage_change_notify (atlas_tex);
+
cogl_handle_unref (atlas_tex->sub_texture);
/* Create a new texture at the right size, not including the
@@ -686,6 +691,11 @@ _cogl_atlas_texture_migrate (unsigned int n_textures,
for (i = 0; i < n_textures; i++)
{
+ /* Notify cogl-material.c that the texture's underlying GL texture
+ * storage is changing so it knows it may need to bind a new texture
+ * if the CoglTexture is reused with the same texture unit. */
+ _cogl_material_texture_storage_change_notify (textures[i].texture);
+
/* Skip the texture that is being added because it doesn't contain
any data yet */
if (textures[i].texture != skip_texture)
diff --git a/clutter/cogl/cogl/cogl-context.c b/clutter/cogl/cogl/cogl-context.c
index 830c69eda..a01d74007 100644
--- a/clutter/cogl/cogl/cogl-context.c
+++ b/clutter/cogl/cogl/cogl-context.c
@@ -67,9 +67,9 @@ cogl_create_context (void)
_cogl_features_init ();
_cogl_material_init_default_material ();
+ _cogl_material_init_default_layers ();
_context->enable_flags = 0;
- _context->color_alpha = 0;
_context->fog_enabled = FALSE;
_context->enable_backface_culling = FALSE;
@@ -104,12 +104,14 @@ cogl_create_context (void)
_context->logged_vertices = g_array_new (FALSE, FALSE, sizeof (GLfloat));
_context->current_material = NULL;
- _context->current_material_flags = COGL_MATERIAL_FLAGS_INIT;
- _context->current_material_fallback_layers = 0;
- _context->current_material_disable_layers = 0;
- _context->current_material_layer0_override = 0;
+ _context->current_material_changes_since_flush = 0;
_context->current_material_skip_gl_color = FALSE;
+ _context->material0_nodes =
+ g_array_sized_new (FALSE, FALSE, sizeof (CoglHandle), 20);
+ _context->material1_nodes =
+ g_array_sized_new (FALSE, FALSE, sizeof (CoglHandle), 20);
+
_cogl_bitmask_init (&_context->texcoord_arrays_enabled);
_cogl_bitmask_init (&_context->temp_bitmask);
_cogl_bitmask_init (&_context->texcoord_arrays_to_disable);
@@ -123,6 +125,8 @@ cogl_create_context (void)
_context->current_use_program_type = COGL_MATERIAL_PROGRAM_TYPE_FIXED;
_context->current_gl_program = 0;
+ _context->gl_blend_enable_cache = FALSE;
+
_context->framebuffer_stack = _cogl_create_framebuffer_stack ();
window_buffer = _cogl_onscreen_new ();
@@ -173,8 +177,6 @@ cogl_create_context (void)
cogl_set_source (_context->simple_material);
_cogl_material_flush_gl_state (_context->source_material, NULL);
- enable_flags =
- _cogl_material_get_cogl_enable_flags (_context->source_material);
_cogl_enable (enable_flags);
_cogl_flush_face_winding ();
@@ -221,6 +223,13 @@ _cogl_destroy_context (void)
if (_context->default_material)
cogl_handle_unref (_context->default_material);
+ if (_context->dummy_layer_dependant)
+ cogl_handle_unref (_context->dummy_layer_dependant);
+ if (_context->default_layer_n)
+ cogl_handle_unref (_context->default_layer_n);
+ if (_context->default_layer_0)
+ cogl_handle_unref (_context->default_layer_0);
+
if (_context->atlas)
_cogl_atlas_free (_context->atlas);
if (_context->atlas_texture)
diff --git a/clutter/cogl/cogl/cogl-context.h b/clutter/cogl/cogl/cogl-context.h
index 148bbaab4..530184f0b 100644
--- a/clutter/cogl/cogl/cogl-context.h
+++ b/clutter/cogl/cogl/cogl-context.h
@@ -49,10 +49,12 @@ typedef struct
gboolean features_cached;
CoglHandle default_material;
+ CoglHandle default_layer_0;
+ CoglHandle default_layer_n;
+ CoglHandle dummy_layer_dependant;
/* Enable cache */
unsigned long enable_flags;
- guint8 color_alpha;
gboolean fog_enabled;
gboolean enable_backface_culling;
@@ -91,11 +93,12 @@ typedef struct
/* Some simple caching, to minimize state changes... */
CoglHandle current_material;
- unsigned long current_material_flags;
- gboolean current_material_fallback_layers;
- gboolean current_material_disable_layers;
- GLuint current_material_layer0_override;
+ unsigned long current_material_changes_since_flush;
gboolean current_material_skip_gl_color;
+
+ GArray *material0_nodes;
+ GArray *material1_nodes;
+
/* Bitmask of texture coordinates arrays that are enabled */
CoglBitmask texcoord_arrays_enabled;
/* These are temporary bitmasks that are used when disabling
@@ -104,6 +107,8 @@ typedef struct
CoglBitmask texcoord_arrays_to_disable;
CoglBitmask temp_bitmask;
+ gboolean gl_blend_enable_cache;
+
/* PBOs */
/* This can be used to check if a pbo is bound */
CoglBuffer *current_pbo;
@@ -143,9 +148,11 @@ typedef struct
GLint max_texture_image_units;
GLint max_activateable_texture_units;
- CoglHandle current_program;
+ /* Fragment processing programs */
+ CoglHandle current_program;
+
CoglMaterialProgramType current_use_program_type;
- GLuint current_gl_program;
+ GLuint current_gl_program;
CoglContextDriver drv;
} CoglContext;
diff --git a/clutter/cogl/cogl/cogl-debug.c b/clutter/cogl/cogl/cogl-debug.c
index df985fa4b..68ca4c38e 100644
--- a/clutter/cogl/cogl/cogl-debug.c
+++ b/clutter/cogl/cogl/cogl-debug.c
@@ -66,7 +66,8 @@ static const GDebugKey cogl_behavioural_debug_keys[] = {
{ "disable-atlas", COGL_DEBUG_DISABLE_ATLAS },
{ "disable-texturing", COGL_DEBUG_DISABLE_TEXTURING},
{ "disable-arbfp", COGL_DEBUG_DISABLE_ARBFP},
- { "disable-glsl", COGL_DEBUG_DISABLE_GLSL}
+ { "disable-glsl", COGL_DEBUG_DISABLE_GLSL},
+ { "disable-blending", COGL_DEBUG_DISABLE_BLENDING}
};
static const int n_cogl_behavioural_debug_keys =
G_N_ELEMENTS (cogl_behavioural_debug_keys);
@@ -123,6 +124,7 @@ _cogl_parse_debug_string (const char *value,
OPT ("disable-texturing:", "disable texturing primitives");
OPT ("disable-arbfp:", "disable use of ARBfp");
OPT ("disable-glsl:", "disable use of GLSL");
+ OPT ("disable-blending:", "disable use of blending");
OPT ("show-source:", "show generated ARBfp/GLSL");
OPT ("opengl:", "traces some select OpenGL calls");
OPT ("offscreen:", "debug offscreen support");
diff --git a/clutter/cogl/cogl/cogl-debug.h b/clutter/cogl/cogl/cogl-debug.h
index 393373791..8341f68a2 100644
--- a/clutter/cogl/cogl/cogl-debug.h
+++ b/clutter/cogl/cogl/cogl-debug.h
@@ -50,7 +50,8 @@ typedef enum {
COGL_DEBUG_DISABLE_TEXTURING = 1 << 19,
COGL_DEBUG_DISABLE_ARBFP = 1 << 20,
COGL_DEBUG_DISABLE_GLSL = 1 << 21,
- COGL_DEBUG_SHOW_SOURCE = 1 << 22
+ COGL_DEBUG_SHOW_SOURCE = 1 << 22,
+ COGL_DEBUG_DISABLE_BLENDING = 1 << 23
} CoglDebugFlags;
#ifdef COGL_ENABLE_DEBUG
diff --git a/clutter/cogl/cogl/cogl-internal.h b/clutter/cogl/cogl/cogl-internal.h
index 7b1253a07..aa32f04c5 100644
--- a/clutter/cogl/cogl/cogl-internal.h
+++ b/clutter/cogl/cogl/cogl-internal.h
@@ -97,11 +97,10 @@ cogl_gl_error_to_string (GLenum error_code);
#endif /* COGL_GL_DEBUG */
-#define COGL_ENABLE_BLEND (1<<1)
-#define COGL_ENABLE_ALPHA_TEST (1<<2)
-#define COGL_ENABLE_VERTEX_ARRAY (1<<3)
-#define COGL_ENABLE_COLOR_ARRAY (1<<4)
-#define COGL_ENABLE_BACKFACE_CULLING (1<<5)
+#define COGL_ENABLE_ALPHA_TEST (1<<1)
+#define COGL_ENABLE_VERTEX_ARRAY (1<<2)
+#define COGL_ENABLE_COLOR_ARRAY (1<<3)
+#define COGL_ENABLE_BACKFACE_CULLING (1<<4)
void
_cogl_features_init (void);
diff --git a/clutter/cogl/cogl/cogl-journal.c b/clutter/cogl/cogl/cogl-journal.c
index 9bedf6b15..e850a2779 100644
--- a/clutter/cogl/cogl/cogl-journal.c
+++ b/clutter/cogl/cogl/cogl-journal.c
@@ -318,10 +318,6 @@ _cogl_journal_flush_material_and_entries (CoglJournalEntry *batch_start,
_cogl_material_flush_gl_state (batch_start->material,
&batch_start->flush_options);
- /* FIXME: This api is a bit yukky, ideally it will be removed if we
- * re-work the _cogl_enable mechanism */
- enable_flags |= _cogl_material_get_cogl_enable_flags (batch_start->material);
-
if (ctx->enable_backface_culling)
enable_flags |= COGL_ENABLE_BACKFACE_CULLING;
@@ -356,7 +352,8 @@ compare_entry_materials (CoglJournalEntry *entry0, CoglJournalEntry *entry1)
if (_cogl_material_equal (entry0->material,
&entry0->flush_options,
entry1->material,
- &entry1->flush_options))
+ &entry1->flush_options,
+ TRUE))
return TRUE;
else
return FALSE;
diff --git a/clutter/cogl/cogl/cogl-material-private.h b/clutter/cogl/cogl/cogl-material-private.h
index 08ea01338..a1029fe84 100644
--- a/clutter/cogl/cogl/cogl-material-private.h
+++ b/clutter/cogl/cogl/cogl-material-private.h
@@ -3,7 +3,7 @@
*
* An object oriented GL/GLES Abstraction/Utility Layer
*
- * Copyright (C) 2008,2009 Intel Corporation.
+ * 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
@@ -16,7 +16,8 @@
* 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 .
+ * License along with this library. If not, see
+ * .
*
*
*
@@ -62,8 +63,9 @@ typedef struct _CoglTextureUnit
/* Whether or not the corresponding gl_target has been glEnabled */
gboolean enabled;
- /* The GL target currently glEnabled or 0 if .enabled == FALSE */
- GLenum enabled_gl_target;
+ /* The GL target currently glEnabled or the target last enabled
+ * if .enabled == FALSE */
+ GLenum current_gl_target;
/* The raw GL texture object name for which we called glBindTexture when
* we flushed the last layer. (NB: The CoglTexture associated
@@ -80,13 +82,13 @@ typedef struct _CoglTextureUnit
/* We have many components in Cogl that need to temporarily bind arbitrary
* textures e.g. to query texture object parameters and since we don't
* want that to result in too much redundant reflushing of layer state
- * when all that's needed is to re-bind the layers gl_texture we use this
- * to track when the .layer_gl_texture state is invalid.
+ * when all that's needed is to re-bind the layer's gl_texture we use this
+ * to track when the unit->gl_texture state is out of sync with the GL
+ * texture object really bound too (GL_TEXTURE0+unit->index).
*
* XXX: as a further optimization cogl-material.c uses a convention
- * of always leaving texture unit 1 active when not dealing with the
- * flushing of layer state, so we can assume this is only ever TRUE
- * for unit 1.
+ * of always using texture unit 1 for these transient bindings so we
+ * can assume this is only ever TRUE for unit 1.
*/
gboolean dirty_gl_texture;
@@ -116,24 +118,16 @@ typedef struct _CoglTextureUnit
* invalidated though these flags can be used to optimize the state
* flush of the next layer
*/
- unsigned long layer_differences;
+ unsigned long layer_changes_since_flush;
- /* The options that may have affected how the layer state updated
- * this texture unit. */
- gboolean fallback;
- gboolean layer0_overridden;
-
- /* When flushing a layers state, fallback options may mean that a
- * different CoglTexture is used than layer->texture.
- *
- * Once a layers state has been flushed we have to keep track of
- * changes to that layer so if we are asked to re-flush the same
- * layer later we will know what work is required. This also means
- * we need to keep track of changes to the CoglTexture of that layer
- * so we need to explicitly keep a reference to the final texture
- * chosen.
- */
- CoglHandle texture;
+ /* Whenever a CoglTexture's internal GL texture storage changes
+ * cogl-material.c is notified with a call to
+ * _cogl_material_texture_storage_change_notify which inturn sets
+ * this to TRUE for each texture unit that it is currently bound
+ * too. When we later come to flush some material state then we will
+ * always check this to potentially force an update of the texture
+ * state even if the material hasn't changed. */
+ gboolean texture_storage_changed;
} CoglTextureUnit;
@@ -148,63 +142,50 @@ _cogl_bind_gl_texture_transient (GLenum gl_target,
GLuint gl_texture,
gboolean is_foreign);
-typedef enum _CoglMaterialEqualFlags
+#if defined (HAVE_COGL_GL)
+/* glsl, arbfp, fixed */
+#define COGL_MATERIAL_N_BACKENDS 3
+#elif defined (HAVE_COGL_GLES2)
+/* glsl, fixed */
+#define COGL_MATERIAL_N_BACKENDS 2
+#else /* HAVE_COGL_GLES */
+/* fixed */
+#define COGL_MATERIAL_N_BACKENDS 1
+#endif
+
+typedef enum
{
- /* Return FALSE if any component of either material isn't set to its
- * default value. (Note: if the materials have corresponding flush
- * options indicating that e.g. the material color won't be flushed then
- * this will not assert a default color value.) */
- COGL_MATERIAL_EQUAL_FLAGS_ASSERT_ALL_DEFAULTS = 1L<<0,
+ COGL_MATERIAL_LAYER_STATE_UNIT = 1L<<0,
+ COGL_MATERIAL_LAYER_STATE_TEXTURE = 1L<<1,
+ COGL_MATERIAL_LAYER_STATE_FILTERS = 1L<<2,
+ COGL_MATERIAL_LAYER_STATE_WRAP_MODES = 1L<<3,
-} CoglMaterialEqualFlags;
+ COGL_MATERIAL_LAYER_STATE_COMBINE = 1L<<4,
+ COGL_MATERIAL_LAYER_STATE_COMBINE_CONSTANT = 1L<<5,
+ COGL_MATERIAL_LAYER_STATE_USER_MATRIX = 1L<<6,
-typedef enum _CoglMaterialLayerDifferenceFlags
+ /* COGL_MATERIAL_LAYER_STATE_TEXTURE_INTERN = 1L<<7, */
+
+ COGL_MATERIAL_LAYER_STATE_ALL_SPARSE =
+ COGL_MATERIAL_LAYER_STATE_UNIT |
+ COGL_MATERIAL_LAYER_STATE_TEXTURE |
+ COGL_MATERIAL_LAYER_STATE_FILTERS |
+ COGL_MATERIAL_LAYER_STATE_WRAP_MODES |
+ COGL_MATERIAL_LAYER_STATE_COMBINE |
+ COGL_MATERIAL_LAYER_STATE_COMBINE_CONSTANT |
+ COGL_MATERIAL_LAYER_STATE_USER_MATRIX,
+
+ COGL_MATERIAL_LAYER_STATE_NEEDS_BIG_STATE =
+ COGL_MATERIAL_LAYER_STATE_COMBINE |
+ COGL_MATERIAL_LAYER_STATE_COMBINE_CONSTANT |
+ COGL_MATERIAL_LAYER_STATE_USER_MATRIX
+
+} CoglMaterialLayerState;
+
+typedef struct
{
- COGL_MATERIAL_LAYER_DIFFERENCE_TEXTURE = 1L<<0,
- COGL_MATERIAL_LAYER_DIFFERENCE_COMBINE = 1L<<1,
- COGL_MATERIAL_LAYER_DIFFERENCE_COMBINE_CONSTANT = 1L<<2,
- COGL_MATERIAL_LAYER_DIFFERENCE_USER_MATRIX = 1L<<3,
- COGL_MATERIAL_LAYER_DIFFERENCE_FILTERS = 1L<<4
-} CoglMaterialLayerDifferenceFlags;
-
-typedef enum _CoglMaterialLayerChangeFlags
-{
- COGL_MATERIAL_LAYER_CHANGE_TEXTURE = 1L<<0,
- COGL_MATERIAL_LAYER_CHANGE_COMBINE = 1L<<1,
- COGL_MATERIAL_LAYER_CHANGE_COMBINE_CONSTANT = 1L<<2,
- COGL_MATERIAL_LAYER_CHANGE_USER_MATRIX = 1L<<3,
- COGL_MATERIAL_LAYER_CHANGE_FILTERS = 1L<<4,
-
- COGL_MATERIAL_LAYER_CHANGE_TEXTURE_INTERN = 1L<<5,
- COGL_MATERIAL_LAYER_CHANGE_UNIT = 1L<<6
-} CoglMaterialLayerChangeFlags;
-
-struct _CoglMaterialLayer
-{
- CoglHandleObject _parent;
-
- /* Parent material */
- CoglMaterial *material;
-
- unsigned int index; /*!< lowest index is blended first then others on
- top */
-
- int unit_index;
-
- unsigned long differences;
-
- CoglHandle texture; /*!< The texture for this layer, or
- COGL_INVALID_HANDLE for an empty layer */
-
- CoglMaterialFilter mag_filter;
- CoglMaterialFilter min_filter;
-
- CoglMaterialWrapMode wrap_mode_s;
- CoglMaterialWrapMode wrap_mode_t;
- CoglMaterialWrapMode wrap_mode_r;
-
- /* Determines how the color of individual texture fragments
- * are calculated. */
+ /* The texture combine state determines how the color of individual
+ * texture fragments are calculated. */
GLint texture_combine_rgb_func;
GLint texture_combine_rgb_src[3];
GLint texture_combine_rgb_op[3];
@@ -213,104 +194,363 @@ struct _CoglMaterialLayer
GLint texture_combine_alpha_src[3];
GLint texture_combine_alpha_op[3];
- GLfloat texture_combine_constant[4];
-
- /* TODO: Support purely GLSL based material layers */
+ float texture_combine_constant[4];
+ /* The texture matrix dscribes how to transform texture coordinates */
CoglMatrix matrix;
+} CoglMaterialLayerBigState;
+
+struct _CoglMaterialLayer
+{
+ /* XXX: Please think twice about adding members that *have* be
+ * initialized during a _cogl_material_layer_copy. We are aiming
+ * to have copies be as cheap as possible and copies may be
+ * done by the primitives APIs which means they may happen
+ * in performance critical code paths.
+ *
+ * XXX: If you are extending the state we track please consider if
+ * the state is expected to vary frequently across many materials or
+ * if the state can be shared among many derived materials instead.
+ * This will determine if the state should be added directly to this
+ * structure which will increase the memory overhead for *all*
+ * layers or if instead it can go under ->big_state.
+ */
+
+ /* the parent in terms of class hierarchy */
+ CoglHandleObject _parent;
+
+ /* Some layers have a material owner, which is to say that the layer
+ * is referenced in that materials->layer_differences list. A layer
+ * doesn't always have an owner and may simply be an ancestor for
+ * other layers that keeps track of some shared state. */
+ CoglMaterial *owner;
+
+ /* Layers are sparse structures defined as a diff against
+ * their parent... */
+ CoglMaterialLayer *parent;
+
+ /* As an optimization for creating leaf node layers (the most
+ * common) we don't require any list node allocations to link
+ * to a single descendant. */
+ CoglMaterialLayer *first_child;
+
+ /* Layers are sparse structures defined as a diff against
+ * their parent and may have multiple children which depend
+ * on them to define the values of properties which they don't
+ * change. */
+ GList *children;
+
+ /* The lowest index is blended first then others on top */
+ int index;
+
/* Different material backends (GLSL/ARBfp/Fixed Function) may
- * want to associate private data with a layer... */
- void *backend_priv;
+ * want to associate private data with a layer...
+ *
+ * NB: we have per backend pointers because a layer may be
+ * associated with multiple materials with different backends.
+ */
+ void *backend_priv[COGL_MATERIAL_N_BACKENDS];
+
+ /* A mask of which state groups are different in this layer
+ * in comparison to its parent. */
+ unsigned long differences;
+
+ /* Common differences
+ *
+ * As a basic way to reduce memory usage we divide the layer
+ * state into two groups; the minimal state modified in 90% of
+ * all layers and the rest, so that the second group can
+ * be allocated dynamically when required.
+ */
+
+ /* Each layer is directly associated with a single texture unit */
+ int unit_index;
+
+ /* The texture for this layer, or COGL_INVALID_HANDLE for an empty
+ * layer */
+ CoglHandle texture;
+ gboolean texture_overridden;
+ /* If ->texture_overridden == TRUE then the texture is instead
+ * defined by these... */
+ GLuint slice_gl_texture;
+ GLenum slice_gl_target;
+
+ CoglMaterialFilter mag_filter;
+ CoglMaterialFilter min_filter;
+
+ CoglMaterialWrapMode wrap_mode_s;
+ CoglMaterialWrapMode wrap_mode_t;
+ CoglMaterialWrapMode wrap_mode_r;
+
+ /* Infrequent differences aren't currently tracked in
+ * a separate, dynamically allocated structure as they are
+ * for materials... */
+ CoglMaterialLayerBigState *big_state;
+
+ /* bitfields */
+
+ /* Determines if layer->first_child and layer->children are
+ * initialized pointers. */
+ unsigned int has_children:1;
+
+ /* Determines if layer->big_state is valid */
+ unsigned int has_big_state:1;
+
};
-typedef enum _CoglMaterialFlags
+/* Used in material->differences masks and for notifying material
+ * state changes... */
+typedef enum _CoglMaterialState
{
- COGL_MATERIAL_FLAG_DEFAULT_COLOR = 1L<<1,
- COGL_MATERIAL_FLAG_DEFAULT_GL_MATERIAL = 1L<<2,
- COGL_MATERIAL_FLAG_DEFAULT_ALPHA_FUNC = 1L<<3,
- COGL_MATERIAL_FLAG_ENABLE_BLEND = 1L<<4,
- COGL_MATERIAL_FLAG_DEFAULT_BLEND = 1L<<5,
- COGL_MATERIAL_FLAG_DEFAULT_USER_SHADER = 1L<<6,
- COGL_MATERIAL_FLAG_DEFAULT_LAYERS = 1L<<7
-} CoglMaterialFlags;
+ COGL_MATERIAL_STATE_COLOR = 1L<<0,
+ COGL_MATERIAL_STATE_BLEND_ENABLE = 1L<<1,
+ COGL_MATERIAL_STATE_LAYERS = 1L<<2,
-/* This defines the initialization state for
- * ctx->current_material_flags which should result in the first
- * material flush explicitly initializing everything
- */
-#define COGL_MATERIAL_FLAGS_INIT \
- COGL_MATERIAL_FLAG_DEFAULT_USER_SHADER
+ COGL_MATERIAL_STATE_LIGHTING = 1L<<3,
+ COGL_MATERIAL_STATE_ALPHA_FUNC = 1L<<4,
+ COGL_MATERIAL_STATE_BLEND = 1L<<5,
+ COGL_MATERIAL_STATE_USER_SHADER = 1L<<6,
-typedef enum _CoglMaterialChangeFlag
+ COGL_MATERIAL_STATE_REAL_BLEND_ENABLE = 1L<<7,
+
+ COGL_MATERIAL_STATE_ALL_SPARSE =
+ COGL_MATERIAL_STATE_COLOR |
+ COGL_MATERIAL_STATE_BLEND_ENABLE |
+ COGL_MATERIAL_STATE_LAYERS |
+ COGL_MATERIAL_STATE_LIGHTING |
+ COGL_MATERIAL_STATE_ALPHA_FUNC |
+ COGL_MATERIAL_STATE_BLEND |
+ COGL_MATERIAL_STATE_USER_SHADER,
+
+ COGL_MATERIAL_STATE_AFFECTS_BLENDING =
+ COGL_MATERIAL_STATE_COLOR |
+ COGL_MATERIAL_STATE_BLEND_ENABLE |
+ COGL_MATERIAL_STATE_LAYERS |
+ COGL_MATERIAL_STATE_LIGHTING |
+ COGL_MATERIAL_STATE_BLEND |
+ COGL_MATERIAL_STATE_USER_SHADER,
+
+ COGL_MATERIAL_STATE_NEEDS_BIG_STATE =
+ COGL_MATERIAL_STATE_LIGHTING |
+ COGL_MATERIAL_STATE_ALPHA_FUNC |
+ COGL_MATERIAL_STATE_BLEND |
+ COGL_MATERIAL_STATE_USER_SHADER
+
+} CoglMaterialState;
+
+typedef enum
{
- COGL_MATERIAL_CHANGE_COLOR = 1L<<1,
- COGL_MATERIAL_CHANGE_GL_MATERIAL = 1L<<2,
- COGL_MATERIAL_CHANGE_ALPHA_FUNC = 1L<<3,
- COGL_MATERIAL_CHANGE_ENABLE_BLEND = 1L<<4,
- COGL_MATERIAL_CHANGE_BLEND = 1L<<5,
- COGL_MATERIAL_CHANGE_USER_SHADER = 1L<<6,
- COGL_MATERIAL_CHANGE_LAYERS = 1L<<7
-} CoglMaterialChangeFlag;
+ COGL_MATERIAL_LIGHTING_STATE_PROPERTY_AMBIENT = 1,
+ COGL_MATERIAL_LIGHTING_STATE_PROPERTY_DIFFUSE,
+ COGL_MATERIAL_LIGHTING_STATE_PROPERTY_SPECULAR,
+ COGL_MATERIAL_LIGHTING_STATE_PROPERTY_EMISSION,
+ COGL_MATERIAL_LIGHTING_STATE_PROPERTY_SHININESS
+} CoglMaterialLightingStateProperty;
-struct _CoglMaterial
+typedef struct
{
- CoglHandleObject _parent;
- unsigned long journal_ref_count;
-
- int backend;
-
- unsigned long flags;
-
- /* If no lighting is enabled; this is the basic material color */
- GLubyte unlit[4];
-
/* Standard OpenGL lighting model attributes */
- GLfloat ambient[4];
- GLfloat diffuse[4];
- GLfloat specular[4];
- GLfloat emission[4];
- GLfloat shininess;
+ float ambient[4];
+ float diffuse[4];
+ float specular[4];
+ float emission[4];
+ float shininess;
+} CoglMaterialLightingState;
+typedef struct
+{
/* Determines what fragments are discarded based on their alpha */
CoglMaterialAlphaFunc alpha_func;
GLfloat alpha_func_reference;
+} CoglMaterialAlphaFuncState;
+typedef enum _CoglMaterialBlendEnable
+{
+ /* XXX: we want to detect users mistakenly using TRUE or FALSE
+ * so start the enum at 2. */
+ COGL_MATERIAL_BLEND_ENABLE_ENABLED = 2,
+ COGL_MATERIAL_BLEND_ENABLE_DISABLED,
+ COGL_MATERIAL_BLEND_ENABLE_AUTOMATIC
+} CoglMaterialBlendEnable;
+
+typedef struct
+{
/* Determines how this material is blended with other primitives */
#ifndef HAVE_COGL_GLES
- GLenum blend_equation_rgb;
- GLenum blend_equation_alpha;
- GLint blend_src_factor_alpha;
- GLint blend_dst_factor_alpha;
- GLfloat blend_constant[4];
+ GLenum blend_equation_rgb;
+ GLenum blend_equation_alpha;
+ GLint blend_src_factor_alpha;
+ GLint blend_dst_factor_alpha;
+ CoglColor blend_constant;
#endif
- GLint blend_src_factor_rgb;
- GLint blend_dst_factor_rgb;
+ GLint blend_src_factor_rgb;
+ GLint blend_dst_factor_rgb;
+} CoglMaterialBlendState;
+typedef struct
+{
+ CoglMaterialLightingState lighting_state;
+ CoglMaterialAlphaFuncState alpha_state;
+ CoglMaterialBlendState blend_state;
CoglHandle user_program;
+} CoglMaterialBigState;
- GList *layers;
- unsigned int n_layers;
+typedef enum
+{
+ COGL_MATERIAL_FLAG_DIRTY_LAYERS_CACHE = 1L<<0,
+ COGL_MATERIAL_FLAG_DIRTY_GET_LAYERS_LIST = 1L<<1
+} CoglMaterialFlag;
- void *backend_priv;
+typedef struct
+{
+ CoglMaterial *owner;
+ CoglMaterialLayer *layer;
+} CoglMaterialLayerCacheEntry;
+
+struct _CoglMaterial
+{
+ /* XXX: Please think twice about adding members that *have* be
+ * initialized during a cogl_material_copy. We are aiming to have
+ * copies be as cheap as possible and copies may be done by the
+ * primitives APIs which means they may happen in performance
+ * critical code paths.
+ *
+ * XXX: If you are extending the state we track please consider if
+ * the state is expected to vary frequently across many materials or
+ * if the state can be shared among many derived materials instead.
+ * This will determine if the state should be added directly to this
+ * structure which will increase the memory overhead for *all*
+ * materials or if instead it can go under ->big_state.
+ */
+
+ /* the parent in terms of class hierarchy */
+ CoglHandleObject _parent;
+
+ /* We need to track if a material is referenced in the journal
+ * because we can't allow modification to these materials without
+ * flushing the journal first */
+ unsigned long journal_ref_count;
+
+ /* Materials are sparse structures defined as a diff against
+ * their parent. */
+ CoglMaterial *parent;
+
+ /* As an optimization for creating leaf node materials (the most
+ * common) we don't require any list node allocations to link
+ * to a single descendant.
+ *
+ * Only valid if ->has_children bitfield is set */
+ CoglMaterial *first_child;
+
+ /* Materials are sparse structures defined as a diff against
+ * their parent and may have multiple children which depend
+ * on them to define the values of properties which they don't
+ * change.
+ *
+ * Only valid if ->has_children bitfield is set */
+ GList *children;
+
+ /* A mask of which sparse state groups are different in this
+ * material in comparison to its parent. */
+ unsigned long differences;
+
+ /* The fragment processing backend identified by the ->backend
+ * bitfield can associate private data with a material. */
+ void *backend_priv;
+
+ /* This is the primary color of the material.
+ *
+ * This is a sparse property, ref COGL_MATERIAL_STATE_COLOR */
+ CoglColor color;
+
+ /* A material may be made up with multiple layers used to combine
+ * textures together.
+ *
+ * This is sparse state, ref COGL_MATERIAL_STATE_LAYERS */
+ GList *layer_differences;
+ unsigned int n_layers;
+
+ /* As a basic way to reduce memory usage we divide the material
+ * state into two groups; the minimal state modified in 90% of
+ * all materials and the rest, so that the second group can
+ * be allocated dynamically when required... */
+ CoglMaterialBigState *big_state;
+
+ /* Cached state... */
+
+ /* A cached, complete list of the layers this material depends
+ * on sorted by layer->unit_index. */
+ CoglMaterialLayer **layers_cache;
+ /* To avoid a separate ->layers_cache allocation for common
+ * materials with only a few layers... */
+ CoglMaterialLayer *short_layers_cache[3];
+
+ /* The deprecated cogl_material_get_layers() API returns a
+ * const GList of layers, which we track here... */
+ GList *deprecated_get_layers_list;
+
+ /* XXX: consider adding an authorities cache to speed up sparse
+ * property value lookups:
+ * CoglMaterial *authorities_cache[COGL_MATERIAL_N_SPARSE_PROPERTIES];
+ * and corresponding authorities_cache_dirty:1 bitfield
+ */
+
+ /* bitfields */
+
+ /* Determines if material->big_state is valid */
+ unsigned int has_big_state:1;
+
+ /* By default blending is enabled automatically depending on the
+ * unlit color, the lighting colors or the texture format. The user
+ * can override this to explicitly enable or disable blending.
+ *
+ * This is a sparse property */
+ unsigned int blend_enable:3;
+
+ /* There are many factors that can determine if we need to enable
+ * blending, this holds our final decision */
+ unsigned int real_blend_enable:1;
+
+ /* Determines if material->first_child and material->children are
+ * initialized pointers. */
+ unsigned int has_children:1;
+
+ unsigned int layers_cache_dirty:1;
+ unsigned int deprecated_get_layers_list_dirty:1;
+
+ /* There are multiple fragment processing backends for CoglMaterial,
+ * glsl, arbfp and fixed. This identifies the backend being used for
+ * the material and any private state the backend has associated
+ * with the material. */
+ unsigned int backend:3;
+
+ /* Determines if ->backend_priv has been initialized */
+ unsigned int backend_priv_set:1;
};
typedef struct _CoglMaterialBackend
{
int (*get_max_texture_units) (void);
- gboolean (*start) (CoglMaterial *material);
- gboolean (*add_layer) (CoglMaterialLayer *layer);
+ gboolean (*start) (CoglMaterial *material,
+ int n_layers,
+ unsigned long materials_difference);
+ gboolean (*add_layer) (CoglMaterial *material,
+ CoglMaterialLayer *layer,
+ unsigned long layers_difference);
gboolean (*passthrough) (CoglMaterial *material);
- gboolean (*end) (CoglMaterial *material);
+ gboolean (*end) (CoglMaterial *material,
+ unsigned long materials_difference);
- void (*material_change_notify) (CoglMaterial *material,
- unsigned long changes,
- GLubyte *new_color);
- void (*layer_change_notify) (CoglMaterialLayer *layer,
- unsigned long changes);
+ void (*material_pre_change_notify) (CoglMaterial *material,
+ CoglMaterialState change,
+ const CoglColor *new_color);
+ void (*layer_pre_change_notify) (CoglMaterialLayer *layer,
+ CoglMaterialLayerState change);
void (*free_priv) (CoglMaterial *material);
+ void (*free_layer_priv) (CoglMaterialLayer *layer);
} CoglMaterialBackend;
typedef enum
@@ -320,6 +560,12 @@ typedef enum
COGL_MATERIAL_PROGRAM_TYPE_FIXED
} CoglMaterialProgramType;
+void
+_cogl_material_init_default_material (void);
+
+void
+_cogl_material_init_default_layers (void);
+
/*
* SECTION:cogl-material-internals
* @short_description: Functions for creating custom primitives that make use
@@ -331,31 +577,8 @@ typedef enum
* able to fill your geometry according to a given Cogl material.
*/
-/*
- * _cogl_material_init_default_material:
- *
- * This initializes the first material owned by the Cogl context. All
- * subsequently instantiated materials created via the cogl_material_new()
- * API will initially be a copy of this material.
- */
-void
-_cogl_material_init_default_material (void);
-
-/*
- * cogl_material_get_cogl_enable_flags:
- * @material: A CoglMaterial object
- *
- * This determines what flags need to be passed to cogl_enable before this
- * material can be used. Normally you shouldn't need to use this function
- * directly since Cogl will do this internally, but if you are developing
- * custom primitives directly with OpenGL you may want to use this.
- *
- * Note: This API is hopfully just a stop-gap solution. Ideally cogl_enable
- * will be replaced.
- */
-/* TODO: find a nicer solution! */
-unsigned long
-_cogl_material_get_cogl_enable_flags (CoglHandle handle);
+gboolean
+_cogl_material_get_real_blend_enabled (CoglHandle handle);
gboolean
_cogl_material_layer_has_user_matrix (CoglHandle layer_handle);
@@ -397,11 +620,35 @@ typedef enum _CoglMaterialFlushFlag
COGL_MATERIAL_FLUSH_WRAP_MODE_OVERRIDES = 1L<<4
} CoglMaterialFlushFlag;
-/* These constants are used to fill in wrap_mode_overrides */
-#define COGL_MATERIAL_WRAP_MODE_OVERRIDE_NONE 0 /* no override */
-#define COGL_MATERIAL_WRAP_MODE_OVERRIDE_REPEAT 1
-#define COGL_MATERIAL_WRAP_MODE_OVERRIDE_CLAMP_TO_EDGE 2
-#define COGL_MATERIAL_WRAP_MODE_OVERRIDE_CLAMP_TO_BORDER 3
+/* This isn't defined in the GLES headers */
+#ifndef GL_CLAMP_TO_BORDER
+#define GL_CLAMP_TO_BORDER 0x812d
+#endif
+
+/* 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 CoglMaterialWrapMode enum
+ * so no conversion is actually needed.
+ */
+typedef enum _CoglMaterialWrapModeInternal
+{
+ COGL_MATERIAL_WRAP_MODE_INTERNAL_REPEAT = GL_REPEAT,
+ COGL_MATERIAL_WRAP_MODE_INTERNAL_CLAMP_TO_EDGE = GL_CLAMP_TO_EDGE,
+ COGL_MATERIAL_WRAP_MODE_INTERNAL_CLAMP_TO_BORDER = GL_CLAMP_TO_BORDER,
+ COGL_MATERIAL_WRAP_MODE_INTERNAL_AUTOMATIC = GL_ALWAYS
+} CoglMaterialWrapModeInternal;
+
+typedef enum _CoglMaterialWrapModeOverride
+{
+ COGL_MATERIAL_WRAP_MODE_OVERRIDE_NONE = 0,
+ COGL_MATERIAL_WRAP_MODE_OVERRIDE_REPEAT =
+ COGL_MATERIAL_WRAP_MODE_INTERNAL_REPEAT,
+ COGL_MATERIAL_WRAP_MODE_OVERRIDE_CLAMP_TO_EDGE =
+ COGL_MATERIAL_WRAP_MODE_INTERNAL_CLAMP_TO_EDGE,
+ COGL_MATERIAL_WRAP_MODE_OVERRIDE_CLAMP_TO_BORDER =
+ COGL_MATERIAL_WRAP_MODE_INTERNAL_CLAMP_TO_BORDER,
+} CoglMaterialWrapModeOverride;
/* There can't be more than 32 layers because we need to fit a bitmask
of the layers into a guint32 */
@@ -411,9 +658,9 @@ typedef struct _CoglMaterialWrapModeOverrides
{
struct
{
- unsigned long s : 2;
- unsigned long t : 2;
- unsigned long r : 2;
+ CoglMaterialWrapModeOverride s;
+ CoglMaterialWrapModeOverride t;
+ CoglMaterialWrapModeOverride r;
} values[COGL_MATERIAL_MAX_LAYERS];
} CoglMaterialWrapModeOverrides;
@@ -443,7 +690,8 @@ gboolean
_cogl_material_equal (CoglHandle material0_handle,
CoglMaterialFlushOptions *material0_flush_options,
CoglHandle material1_handle,
- CoglMaterialFlushOptions *material1_flush_options);
+ CoglMaterialFlushOptions *material1_flush_options,
+ gboolean skip_gl_color);
CoglHandle
_cogl_material_journal_ref (CoglHandle material_handle);
@@ -468,11 +716,21 @@ _cogl_material_set_user_program (CoglHandle handle,
void
_cogl_delete_gl_texture (GLuint gl_texture);
+void
+_cogl_material_texture_storage_change_notify (CoglHandle texture);
+
void
_cogl_material_apply_legacy_state (CoglHandle handle);
void
_cogl_gl_use_program_wrapper (GLuint program);
+CoglMaterialBlendEnable
+_cogl_material_get_blend_enabled (CoglHandle handle);
+
+void
+_cogl_material_set_blend_enabled (CoglHandle handle,
+ CoglMaterialBlendEnable enable);
+
#endif /* __COGL_MATERIAL_PRIVATE_H */
diff --git a/clutter/cogl/cogl/cogl-material.c b/clutter/cogl/cogl/cogl-material.c
index 11cd52e75..f8f366fca 100644
--- a/clutter/cogl/cogl/cogl-material.c
+++ b/clutter/cogl/cogl/cogl-material.c
@@ -16,7 +16,8 @@
* 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 .
+ * License along with this library. If not, see
+ * .
*
*
*
@@ -37,6 +38,8 @@
#include "cogl-texture-private.h"
#include "cogl-blend-string.h"
#include "cogl-journal-private.h"
+#include "cogl-color-private.h"
+#include "cogl-profile.h"
#ifndef HAVE_COGL_GLES
#include "cogl-program.h"
#endif
@@ -74,6 +77,9 @@
#define GL_CLAMP_TO_BORDER 0x812d
#endif
+#define COGL_MATERIAL(X) ((CoglMaterial *)(X))
+#define COGL_MATERIAL_LAYER(X) ((CoglMaterialLayer *)(X))
+
typedef struct _CoglMaterialBackendARBfpPrivate
{
GString *source;
@@ -82,10 +88,18 @@ typedef struct _CoglMaterialBackendARBfpPrivate
int next_constant_id;
} CoglMaterialBackendARBfpPrivate;
-static CoglHandle _cogl_material_layer_copy (CoglHandle layer_handle);
+typedef gboolean (*CoglMaterialStateComparitor) (CoglMaterial *authority0,
+ CoglMaterial *authority1);
+
+static CoglMaterialLayer *_cogl_material_layer_copy (CoglMaterialLayer *layer);
static void _cogl_material_free (CoglMaterial *tex);
static void _cogl_material_layer_free (CoglMaterialLayer *layer);
+static void _cogl_material_add_layer_difference (CoglMaterial *material,
+ CoglMaterialLayer *layer,
+ gboolean inc_n_layers);
+static void handle_automatic_blend_enable (CoglMaterial *material,
+ CoglMaterialState changes);
#if defined (HAVE_COGL_GL)
@@ -99,6 +113,7 @@ static const CoglMaterialBackend *backends[] =
&_cogl_material_arbfp_backend,
&_cogl_material_fixed_backend
};
+/* NB: material->backend is currently a 3bit unsigned int bitfield */
#define COGL_MATERIAL_BACKEND_GLSL 0
#define COGL_MATERIAL_BACKEND_ARBFP 1
#define COGL_MATERIAL_BACKEND_FIXED 2
@@ -130,12 +145,185 @@ static const CoglMaterialBackend *backends[] =
#endif
#define COGL_MATERIAL_BACKEND_DEFAULT 0
-#define COGL_MATERIAL_BACKEND_UNDEFINED -1
+#define COGL_MATERIAL_BACKEND_UNDEFINED 3
COGL_HANDLE_DEFINE (Material, material);
COGL_HANDLE_DEFINE (MaterialLayer, material_layer);
-/* #define DISABLE_MATERIAL_CACHE 1 */
+static void
+texture_unit_init (CoglTextureUnit *unit, int index_)
+{
+ unit->index = index_;
+ unit->enabled = FALSE;
+ unit->current_gl_target = 0;
+ unit->gl_texture = 0;
+ unit->is_foreign = FALSE;
+ unit->dirty_gl_texture = FALSE;
+ unit->matrix_stack = _cogl_matrix_stack_new ();
+
+ unit->layer = NULL;
+ unit->layer_changes_since_flush = 0;
+ unit->texture_storage_changed = FALSE;
+}
+
+static void
+texture_unit_free (CoglTextureUnit *unit)
+{
+ if (unit->layer)
+ cogl_handle_unref (unit->layer);
+ _cogl_matrix_stack_destroy (unit->matrix_stack);
+}
+
+CoglTextureUnit *
+_cogl_get_texture_unit (int index_)
+{
+ _COGL_GET_CONTEXT (ctx, NULL);
+
+ if (ctx->texture_units->len < (index_ + 1))
+ {
+ int i;
+ int prev_len = ctx->texture_units->len;
+ ctx->texture_units = g_array_set_size (ctx->texture_units, index_ + 1);
+ for (i = prev_len; i <= index_; i++)
+ {
+ CoglTextureUnit *unit =
+ &g_array_index (ctx->texture_units, CoglTextureUnit, i);
+
+ texture_unit_init (unit, i);
+ }
+ }
+
+ return &g_array_index (ctx->texture_units, CoglTextureUnit, index_);
+}
+
+void
+_cogl_destroy_texture_units (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);
+ texture_unit_free (unit);
+ }
+ g_array_free (ctx->texture_units, TRUE);
+}
+
+static void
+set_active_texture_unit (int unit_index)
+{
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ if (ctx->active_texture_unit != unit_index)
+ {
+ GE (glActiveTexture (GL_TEXTURE0 + unit_index));
+ ctx->active_texture_unit = unit_index;
+ }
+}
+
+/* Note: _cogl_bind_gl_texture_transient conceptually has slightly
+ * different semantics to OpenGL's glBindTexture because Cogl never
+ * cares about tracking multiple textures bound to different targets
+ * on the same texture unit.
+ *
+ * glBindTexture lets you bind multiple textures to a single texture
+ * unit if they are bound to different targets. So it does something
+ * like:
+ * unit->current_texture[target] = texture;
+ *
+ * Cogl only lets you associate one texture with the currently active
+ * texture unit, so the target is basically a redundant parameter
+ * that's implicitly set on that texture.
+ *
+ * Technically this is just a thin wrapper around glBindTexture so
+ * actually it does have the GL semantics but it seems worth
+ * mentioning the conceptual difference in case anyone wonders why we
+ * don't associate the gl_texture with a gl_target in the
+ * CoglTextureUnit.
+ */
+void
+_cogl_bind_gl_texture_transient (GLenum gl_target,
+ GLuint gl_texture,
+ gboolean is_foreign)
+{
+ CoglTextureUnit *unit;
+
+ _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+ /* We choose to always make texture unit 1 active for transient
+ * binds so that in the common case where multitexturing isn't used
+ * we can simply ignore the state of this texture unit. Notably we
+ * didn't use a large texture unit (.e.g. (GL_MAX_TEXTURE_UNITS - 1)
+ * in case the driver doesn't have a sparse data structure for
+ * texture units.
+ */
+ set_active_texture_unit (1);
+ unit = _cogl_get_texture_unit (1);
+
+ /* NB: If we have previously bound a foreign texture to this texture
+ * unit we don't know if that texture has since been deleted and we
+ * are seeing the texture name recycled */
+ if (unit->gl_texture == gl_texture &&
+ !unit->dirty_gl_texture &&
+ !unit->is_foreign)
+ return;
+
+ GE (glBindTexture (gl_target, gl_texture));
+
+ unit->dirty_gl_texture = TRUE;
+ unit->is_foreign = is_foreign;
+}
+
+void
+_cogl_delete_gl_texture (GLuint gl_texture)
+{
+ 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->gl_texture == gl_texture)
+ {
+ unit->gl_texture = 0;
+ unit->dirty_gl_texture = FALSE;
+ }
+ }
+
+ GE (glDeleteTextures (1, &gl_texture));
+}
+
+/* Whenever the underlying GL texture storage of a CoglTexture is
+ * changed (e.g. due to migration out of a texture atlas) then we are
+ * notified. This lets us ensure that we reflush that texture's state
+ * if it reused again with the same texture unit.
+ */
+void
+_cogl_material_texture_storage_change_notify (CoglHandle texture)
+{
+ 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->layer &&
+ unit->layer->texture == texture)
+ unit->texture_storage_changed = TRUE;
+
+ /* NB: the texture may be bound to multiple texture units so
+ * we continue to check the rest */
+ }
+}
GQuark
_cogl_material_error_quark (void)
@@ -143,75 +331,130 @@ _cogl_material_error_quark (void)
return g_quark_from_static_string ("cogl-material-error-quark");
}
+/*
+ * This initializes the first material owned by the Cogl context. All
+ * subsequently instantiated materials created via the cogl_material_new()
+ * API will initially be a copy of this material.
+ *
+ * The default material is the topmost ancester for all materials.
+ */
void
_cogl_material_init_default_material (void)
{
/* Create new - blank - material */
CoglMaterial *material = g_slice_new0 (CoglMaterial);
- GLubyte *unlit = material->unlit;
- GLfloat *ambient = material->ambient;
- GLfloat *diffuse = material->diffuse;
- GLfloat *specular = material->specular;
- GLfloat *emission = material->emission;
+ CoglMaterialBigState *big_state = g_slice_new0 (CoglMaterialBigState);
+ CoglMaterialLightingState *lighting_state = &big_state->lighting_state;
+ CoglMaterialAlphaFuncState *alpha_state = &big_state->alpha_state;
+ CoglMaterialBlendState *blend_state = &big_state->blend_state;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
+ material->journal_ref_count = 0;
+ material->parent = NULL;
material->backend = COGL_MATERIAL_BACKEND_UNDEFINED;
+ material->differences = COGL_MATERIAL_STATE_ALL_SPARSE;
+
+ material->real_blend_enable = FALSE;
+
+ material->blend_enable = COGL_MATERIAL_BLEND_ENABLE_AUTOMATIC;
+ material->layer_differences = NULL;
+ material->n_layers = 0;
+
+ material->big_state = big_state;
+ material->has_big_state = TRUE;
/* Use the same defaults as the GL spec... */
- unlit[0] = 0xff; unlit[1] = 0xff; unlit[2] = 0xff; unlit[3] = 0xff;
- material->flags |= COGL_MATERIAL_FLAG_DEFAULT_COLOR;
+ cogl_color_init_from_4ub (&material->color, 0xff, 0xff, 0xff, 0xff);
/* Use the same defaults as the GL spec... */
- ambient[0] = 0.2; ambient[1] = 0.2; ambient[2] = 0.2; ambient[3] = 1.0;
- diffuse[0] = 0.8; diffuse[1] = 0.8; diffuse[2] = 0.8; diffuse[3] = 1.0;
- specular[0] = 0; specular[1] = 0; specular[2] = 0; specular[3] = 1.0;
- emission[0] = 0; emission[1] = 0; emission[2] = 0; emission[3] = 1.0;
- material->flags |= COGL_MATERIAL_FLAG_DEFAULT_GL_MATERIAL;
+ lighting_state->ambient[0] = 0.2;
+ lighting_state->ambient[1] = 0.2;
+ lighting_state->ambient[2] = 0.2;
+ lighting_state->ambient[3] = 1.0;
+
+ lighting_state->diffuse[0] = 0.8;
+ lighting_state->diffuse[1] = 0.8;
+ lighting_state->diffuse[2] = 0.8;
+ lighting_state->diffuse[3] = 1.0;
+
+ lighting_state->specular[0] = 0;
+ lighting_state->specular[1] = 0;
+ lighting_state->specular[2] = 0;
+ lighting_state->specular[3] = 1.0;
+
+ lighting_state->emission[0] = 0;
+ lighting_state->emission[1] = 0;
+ lighting_state->emission[2] = 0;
+ lighting_state->emission[3] = 1.0;
/* Use the same defaults as the GL spec... */
- material->alpha_func = COGL_MATERIAL_ALPHA_FUNC_ALWAYS;
- material->alpha_func_reference = 0.0;
- material->flags |= COGL_MATERIAL_FLAG_DEFAULT_ALPHA_FUNC;
+ alpha_state->alpha_func = COGL_MATERIAL_ALPHA_FUNC_ALWAYS;
+ alpha_state->alpha_func_reference = 0.0;
/* Not the same as the GL default, but seems saner... */
#ifndef HAVE_COGL_GLES
- material->blend_equation_rgb = GL_FUNC_ADD;
- material->blend_equation_alpha = GL_FUNC_ADD;
- material->blend_src_factor_alpha = GL_ONE;
- material->blend_dst_factor_alpha = GL_ONE_MINUS_SRC_ALPHA;
- material->blend_constant[0] = 0;
- material->blend_constant[1] = 0;
- material->blend_constant[2] = 0;
- material->blend_constant[3] = 0;
+ blend_state->blend_equation_rgb = GL_FUNC_ADD;
+ blend_state->blend_equation_alpha = GL_FUNC_ADD;
+ blend_state->blend_src_factor_alpha = GL_ONE;
+ blend_state->blend_dst_factor_alpha = GL_ONE_MINUS_SRC_ALPHA;
+ cogl_color_init_from_4ub (&blend_state->blend_constant,
+ 0x00, 0x00, 0x00, 0x00);
#endif
- material->blend_src_factor_rgb = GL_ONE;
- material->blend_dst_factor_rgb = GL_ONE_MINUS_SRC_ALPHA;
- material->flags |= COGL_MATERIAL_FLAG_DEFAULT_BLEND;
+ blend_state->blend_src_factor_rgb = GL_ONE;
+ blend_state->blend_dst_factor_rgb = GL_ONE_MINUS_SRC_ALPHA;
- material->user_program = COGL_INVALID_HANDLE;
- material->flags |= COGL_MATERIAL_FLAG_DEFAULT_USER_SHADER;
-
- material->layers = NULL;
- material->n_layers = 0;
- material->flags |= COGL_MATERIAL_FLAG_DEFAULT_LAYERS;
+ big_state->user_program = COGL_INVALID_HANDLE;
ctx->default_material = _cogl_material_handle_new (material);
}
+/* XXX: Always have an eye out for opportunities to lower the cost of
+ * cogl_material_copy. */
CoglHandle
cogl_material_copy (CoglHandle handle)
{
+ CoglMaterial *src = COGL_MATERIAL (handle);
CoglMaterial *material = g_slice_new (CoglMaterial);
- GList *l;
- _COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE);
+ cogl_handle_ref (handle);
- memcpy (material, handle, sizeof (CoglMaterial));
+ material->_parent = src->_parent;
- material->layers = g_list_copy (material->layers);
- for (l = material->layers; l; l = l->next)
- l->data = _cogl_material_layer_copy (l->data);
+ material->journal_ref_count = 0;
+
+ material->parent = cogl_handle_ref (src);
+ if (src->has_children)
+ src->children = g_list_prepend (src->children, material);
+ else
+ {
+ src->has_children = TRUE;
+ src->first_child = material;
+ src->children = NULL;
+ }
+
+ material->has_children = FALSE;
+
+ material->differences = 0;
+
+ material->has_big_state = FALSE;
+
+ /* NB: real_blend_enable isn't a sparse property, it's valid for
+ * every material node so we have fast access to it. */
+ material->real_blend_enable = src->real_blend_enable;
+
+ /* XXX:
+ * consider generalizing the idea of "cached" properties. These
+ * would still have an authority like other sparse properties but
+ * you wouldn't have to walk up the ancestry to find the authority
+ * because the value would be cached directly in each material.
+ */
+
+ material->layers_cache_dirty = TRUE;
+ material->deprecated_get_layers_list_dirty = TRUE;
+
+ material->backend = src->backend;
+ material->backend_priv_set = FALSE;
return _cogl_material_handle_new (material);
}
@@ -224,6 +467,33 @@ cogl_material_new (void)
return cogl_material_copy (ctx->default_material);
}
+static void
+_cogl_material_unparent (CoglMaterial *material)
+{
+ CoglMaterial *parent = material->parent;
+
+ if (parent == NULL)
+ return;
+
+ g_return_if_fail (parent->has_children);
+
+ if (parent->first_child == material)
+ {
+ if (parent->children)
+ {
+ parent->first_child = parent->children->data;
+ parent->children =
+ g_list_delete_link (parent->children, parent->children);
+ }
+ else
+ parent->has_children = FALSE;
+ }
+ else
+ parent->children = g_list_remove (parent->children, material);
+
+ cogl_handle_unref (parent);
+}
+
static void
_cogl_material_backend_free_priv (CoglMaterial *material)
{
@@ -235,97 +505,566 @@ _cogl_material_backend_free_priv (CoglMaterial *material)
static void
_cogl_material_free (CoglMaterial *material)
{
- _COGL_GET_CONTEXT (ctx, NO_RETVAL);
-
_cogl_material_backend_free_priv (material);
- /* Invalidate the ->current_material reference to this material since
- * it will no longer represent the current state.
- *
- * NB: we would also invalidate this if the material we being
- * modified.
- */
- ctx->current_material = COGL_INVALID_HANDLE;
+ _cogl_material_unparent (material);
+
+ if (material->differences & COGL_MATERIAL_STATE_USER_SHADER &&
+ material->big_state->user_program)
+ cogl_handle_unref (material->big_state->user_program);
+
+ if (material->differences & COGL_MATERIAL_STATE_NEEDS_BIG_STATE)
+ g_slice_free (CoglMaterialBigState, material->big_state);
+
+ if (material->differences & COGL_MATERIAL_STATE_LAYERS)
+ {
+ g_list_foreach (material->layer_differences,
+ (GFunc)cogl_handle_unref, NULL);
+ g_list_free (material->layer_differences);
+ }
- g_list_foreach (material->layers,
- (GFunc)cogl_handle_unref, NULL);
- g_list_free (material->layers);
g_slice_free (CoglMaterial, material);
}
-static gboolean
-_cogl_material_needs_blending_enabled (CoglMaterial *material,
- GLubyte *override_color)
+gboolean
+_cogl_material_get_real_blend_enabled (CoglHandle handle)
{
- GList *tmp;
+ CoglMaterial *material = COGL_MATERIAL (handle);
- /* XXX: If we expose manual control over ENABLE_BLEND, we'll add
- * a flag to know when it's user configured, so we don't trash it */
+ g_return_val_if_fail (cogl_is_material (handle), FALSE);
- /* XXX: Uncomment this to disable all blending */
-#if 0
- return;
-#endif
+ return material->real_blend_enable;
+}
- if ((override_color && override_color[3] != 0xff) ||
- material->unlit[3] != 0xff ||
- material->ambient[3] != 1.0f ||
- material->diffuse[3] != 1.0f ||
- material->specular[3] != 1.0f ||
- material->emission[3] != 1.0f)
- return TRUE;
+static CoglMaterial *
+_cogl_material_get_authority (CoglMaterial *material,
+ unsigned long difference)
+{
+ CoglMaterial *authority = material;
+ while (!(authority->differences & difference))
+ authority = authority->parent;
+ return authority;
+}
- for (tmp = material->layers; tmp != NULL; tmp = tmp->next)
+static CoglMaterialLayer *
+_cogl_material_layer_get_authority (CoglMaterialLayer *layer,
+ unsigned long difference)
+{
+ CoglMaterialLayer *authority = layer;
+ while (!(authority->differences & difference))
+ authority = authority->parent;
+ return authority;
+}
+
+static int
+_cogl_material_layer_get_unit_index (CoglMaterialLayer *layer)
+{
+ CoglMaterialLayer *authority =
+ _cogl_material_layer_get_authority (layer, COGL_MATERIAL_LAYER_STATE_UNIT);
+ return authority->unit_index;
+}
+
+static void
+_cogl_material_update_layers_cache (CoglMaterial *material)
+{
+ /* Note: we assume this material is a _LAYERS authority */
+ int n_layers;
+ CoglMaterial *current;
+ int layers_found;
+
+ if (G_LIKELY (!material->layers_cache_dirty) ||
+ material->n_layers == 0)
+ return;
+
+ material->layers_cache_dirty = FALSE;
+
+ n_layers = material->n_layers;
+ if (G_LIKELY (n_layers < G_N_ELEMENTS (material->short_layers_cache)))
{
- CoglMaterialLayer *layer = tmp->data;
+ material->layers_cache = material->short_layers_cache;
+ memset (material->layers_cache, 0,
+ sizeof (CoglMaterialLayer *) *
+ G_N_ELEMENTS (material->short_layers_cache));
+ }
+ else
+ {
+ material->layers_cache =
+ g_slice_alloc0 (sizeof (CoglMaterialLayer *) * n_layers);
+ }
- /* NB: A layer may have a combine mode set on it but not yet have an
- * associated texture. */
- if (!layer->texture)
+ /* Notes:
+ *
+ * Each material doesn't have to contain a complete list of the layers
+ * it depends on, some of them are indirectly referenced through the
+ * material's ancestors.
+ *
+ * material->layer_differences only contains a list of layers that
+ * have changed in relation to its parent.
+ *
+ * material->layer_differences is not maintained sorted, but it
+ * won't contain multiple layers corresponding to a particular
+ * ->unit_index.
+ *
+ * Some of the ancestor materials may reference layers with
+ * ->unit_index values >= n_layers so we ignore them.
+ *
+ * As we ascend through the ancestors we are searching for any
+ * CoglMaterialLayers corresponding to the texture ->unit_index
+ * values in the range [0,n_layers-1]. As soon as a pointer is found
+ * we ignore layers of further ancestors with the same ->unit_index
+ * values.
+ */
+
+ layers_found = 0;
+ for (current = material; current->parent; current = current->parent)
+ {
+ GList *l;
+
+ if (!(current->differences & COGL_MATERIAL_STATE_LAYERS))
continue;
- if (cogl_texture_get_format (layer->texture) & COGL_A_BIT)
- return TRUE;
+ for (l = current->layer_differences; l; l = l->next)
+ {
+ CoglMaterialLayer *layer = l->data;
+ int unit_index = _cogl_material_layer_get_unit_index (layer);
+
+ if (unit_index < n_layers && !material->layers_cache[unit_index])
+ {
+ material->layers_cache[unit_index] = layer;
+ layers_found++;
+ if (layers_found == n_layers)
+ return;
+ }
+ }
}
+ g_warn_if_reached ();
+}
+
+/* This recursively frees the layers_cache of a material and all of
+ * its descendants.
+ *
+ * For instance if we change a materials ->layer_differences list
+ * then that material and all of its descendants may now have
+ * incorrect layer caches. */
+static void
+recursively_free_layer_caches (CoglMaterial *material)
+{
+ GList *l;
+
+ /* Note: we maintain the invariable that if a material already has a
+ * dirty layers_cache then so do all of its descendants. */
+ if (material->layers_cache_dirty)
+ return;
+
+ if (G_UNLIKELY (material->layers_cache != material->short_layers_cache))
+ g_slice_free1 (sizeof (CoglMaterialLayer *) * material->n_layers,
+ material->layers_cache);
+ material->layers_cache_dirty = TRUE;
+
+ if (material->has_children)
+ {
+ recursively_free_layer_caches (material->first_child);
+ for (l = material->children; l; l = l->next)
+ recursively_free_layer_caches (l->data);
+ }
+}
+
+typedef gboolean (*CoglMaterialLayerCallback) (CoglMaterialLayer *layer,
+ void *user_data);
+
+/* TODO: add public cogl_material_foreach_layer but instead of passing
+ * a CoglMaterialLayer pointer to the callback we should pass a
+ * layer_index instead. */
+
+static void
+_cogl_material_foreach_layer (CoglHandle handle,
+ CoglMaterialLayerCallback callback,
+ void *user_data)
+{
+ CoglMaterial *material = COGL_MATERIAL (handle);
+ CoglMaterial *authority =
+ _cogl_material_get_authority (material, COGL_MATERIAL_STATE_LAYERS);
+ int n_layers;
+ int i;
+ gboolean cont;
+
+ n_layers = authority->n_layers;
+ if (n_layers == 0)
+ return;
+
+ _cogl_material_update_layers_cache (authority);
+
+ for (i = 0, cont = TRUE; i < n_layers && cont == TRUE; i++)
+ cont = callback (authority->layers_cache[i], user_data);
+}
+
+static gboolean
+layer_has_alpha_cb (CoglMaterialLayer *layer, void *data)
+{
+ CoglMaterialLayer *combine_authority =
+ _cogl_material_layer_get_authority (layer,
+ COGL_MATERIAL_LAYER_STATE_COMBINE);
+ CoglMaterialLayerBigState *big_state = combine_authority->big_state;
+ CoglMaterialLayer *tex_authority;
+ gboolean *has_alpha = data;
+
+ /* has_alpha maintains the alpha status for the GL_PREVIOUS layer */
+
+ /* For anything but the default texture combine we currently just
+ * assume it may result in an alpha value < 1
+ *
+ * FIXME: we could do better than this. */
+ if (big_state->texture_combine_alpha_func != GL_MODULATE ||
+ big_state->texture_combine_alpha_src[0] != GL_PREVIOUS ||
+ big_state->texture_combine_alpha_op[0] != GL_SRC_ALPHA ||
+ big_state->texture_combine_alpha_src[0] != GL_TEXTURE ||
+ big_state->texture_combine_alpha_op[0] != GL_SRC_ALPHA)
+ {
+ *has_alpha = TRUE;
+ /* return FALSE to stop iterating layers... */
+ return FALSE;
+ }
+
+ /* NB: A layer may have a combine mode set on it but not yet
+ * have an associated texture which would mean we'd fallback
+ * to the default texture which doesn't have an alpha component
+ */
+ tex_authority =
+ _cogl_material_layer_get_authority (layer,
+ COGL_MATERIAL_LAYER_STATE_TEXTURE);
+ if (tex_authority->texture &&
+ cogl_texture_get_format (tex_authority->texture) & COGL_A_BIT)
+ {
+ *has_alpha = TRUE;
+ /* return FALSE to stop iterating layers... */
+ return FALSE;
+ }
+
+ *has_alpha = FALSE;
+ /* return FALSE to continue iterating layers... */
+ return TRUE;
+}
+
+static CoglHandle
+_cogl_material_get_user_program (CoglHandle handle)
+{
+ CoglMaterial *material = COGL_MATERIAL (handle);
+ CoglMaterial *authority;
+
+ g_return_val_if_fail (cogl_is_material (handle), COGL_INVALID_HANDLE);
+
+ authority =
+ _cogl_material_get_authority (material, COGL_MATERIAL_STATE_USER_SHADER);
+
+ return authority->big_state->user_program;
+}
+
+static gboolean
+_cogl_material_needs_blending_enabled (CoglMaterial *material,
+ unsigned long changes,
+ const CoglColor *override_color)
+{
+ CoglMaterial *enable_authority;
+ CoglMaterial *blend_authority;
+ CoglMaterialBlendState *blend_state;
+ CoglMaterialBlendEnable enabled;
+ unsigned long other_state;
+
+ if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DISABLE_BLENDING))
+ return FALSE;
+
+ enable_authority =
+ _cogl_material_get_authority (material, COGL_MATERIAL_STATE_BLEND_ENABLE);
+
+ enabled = enable_authority->blend_enable;
+ if (enabled != COGL_MATERIAL_BLEND_ENABLE_AUTOMATIC)
+ return enabled == COGL_MATERIAL_BLEND_ENABLE_ENABLED ? TRUE : FALSE;
+
+ blend_authority =
+ _cogl_material_get_authority (material, COGL_MATERIAL_STATE_BLEND);
+
+ blend_state = &blend_authority->big_state->blend_state;
+
+ /* We are trying to identify awkward cases that are equivalent to
+ * blending being disable, where the output is simply GL_SRC_COLOR.
+ *
+ * Note: we assume that all OpenGL drivers will identify the simple
+ * case of ADD (ONE, ZERO) as equivalent to blending being disabled.
+ *
+ * We should update this when we add support for more blend
+ * functions...
+ */
+
+#ifndef HAVE_COGL_GLES
+ /* GLES 1 can't change the function or have separate alpha factors */
+ if (blend_state->blend_equation_rgb != GL_FUNC_ADD ||
+ blend_state->blend_equation_alpha != GL_FUNC_ADD)
+ return TRUE;
+
+ if (blend_state->blend_src_factor_alpha != GL_ONE ||
+ blend_state->blend_dst_factor_alpha != GL_ONE_MINUS_SRC_ALPHA)
+ return TRUE;
+#endif
+
+ if (blend_state->blend_src_factor_rgb != GL_ONE ||
+ blend_state->blend_dst_factor_rgb != GL_ONE_MINUS_SRC_ALPHA)
+ return TRUE;
+
+ /* Given the above constraints, it's now a case of finding any
+ * SRC_ALPHA that != 1 */
+
+ /* In the case of a layer state change we need to check everything
+ * else first since they contribute to the has_alpha status of the
+ * GL_PREVIOUS layer. */
+ if (changes & COGL_MATERIAL_STATE_LAYERS)
+ changes = COGL_MATERIAL_STATE_AFFECTS_BLENDING;
+
+ /* XXX: we don't currently handle specific changes in an optimal way*/
+ changes = COGL_MATERIAL_STATE_AFFECTS_BLENDING;
+
+ if ((override_color && cogl_color_get_alpha_byte (override_color) != 0xff))
+ return TRUE;
+
+ if (changes & COGL_MATERIAL_STATE_COLOR)
+ {
+ CoglColor tmp;
+ cogl_material_get_color (material, &tmp);
+ if (cogl_color_get_alpha_byte (&tmp) != 0xff)
+ return TRUE;
+ }
+
+ /* We can't make any assumptions about the alpha channel if the user
+ * is using an unknown fragment shader.
+ *
+ * TODO: check that it isn't just a vertex shader!
+ */
+ if (changes & COGL_MATERIAL_STATE_USER_SHADER)
+ {
+ if (_cogl_material_get_user_program (material) != COGL_INVALID_HANDLE)
+ return TRUE;
+ }
+
+ /* XXX: we should only need to look at these if lighting is enabled
+ */
+ if (changes & COGL_MATERIAL_STATE_LIGHTING)
+ {
+ CoglColor tmp;
+
+ cogl_material_get_ambient (material, &tmp);
+ if (cogl_color_get_alpha_byte (&tmp) != 0xff)
+ return TRUE;
+ cogl_material_get_diffuse (material, &tmp);
+ if (cogl_color_get_alpha_byte (&tmp) != 0xff)
+ return TRUE;
+ cogl_material_get_specular (material, &tmp);
+ if (cogl_color_get_alpha_byte (&tmp) != 0xff)
+ return TRUE;
+ cogl_material_get_emission (material, &tmp);
+ if (cogl_color_get_alpha_byte (&tmp) != 0xff)
+ return TRUE;
+ }
+
+ if (changes & COGL_MATERIAL_STATE_LAYERS)
+ {
+ /* has_alpha tracks the alpha status of the GL_PREVIOUS layer.
+ * To start with that's defined by the material color which
+ * must be fully opaque if we got this far. */
+ gboolean has_alpha = FALSE;
+ _cogl_material_foreach_layer (material,
+ layer_has_alpha_cb,
+ &has_alpha);
+ if (has_alpha)
+ return TRUE;
+ }
+
+ /* So far we have only checked the property that has been changed so
+ * we now need to check all the other properties too. */
+ other_state = COGL_MATERIAL_STATE_AFFECTS_BLENDING & ~changes;
+ if (other_state &&
+ _cogl_material_needs_blending_enabled (material,
+ other_state,
+ NULL))
+ return TRUE;
+
return FALSE;
}
static void
_cogl_material_set_backend (CoglMaterial *material, int backend)
{
- if (material->backend != COGL_MATERIAL_BACKEND_UNDEFINED &&
- backends[material->backend]->free_priv)
- backends[material->backend]->free_priv (material);
+ _cogl_material_backend_free_priv (material);
material->backend = backend;
}
-/* If primitives have been logged in the journal referencing the current
- * state of this material we need to flush the journal before we can
- * modify it... */
static void
-_cogl_material_pre_change_notify (CoglMaterial *material,
- unsigned long changes,
- GLubyte *new_color)
+_cogl_material_copy_differences (CoglMaterial *dest,
+ CoglMaterial *src,
+ unsigned long differences)
{
+ CoglMaterialBigState *big_state;
+
+ if (differences & COGL_MATERIAL_STATE_COLOR)
+ dest->color = src->color;
+
+ if (differences & COGL_MATERIAL_STATE_BLEND_ENABLE)
+ dest->blend_enable = src->blend_enable;
+
+ if (differences & COGL_MATERIAL_STATE_LAYERS)
+ {
+ GList *l;
+
+ if (dest->differences & COGL_MATERIAL_STATE_LAYERS &&
+ dest->layer_differences)
+ {
+ g_list_foreach (dest->layer_differences,
+ (GFunc)cogl_handle_unref,
+ NULL);
+ g_list_free (dest->layer_differences);
+ }
+
+ dest->n_layers = src->n_layers;
+ dest->layer_differences = g_list_copy (src->layer_differences);
+
+ for (l = src->layer_differences; l; l = l->next)
+ {
+ /* NB: a layer can't have more than one ->owner so we can't
+ * simply take a references on each of the original
+ * layer_differences, we have to derive new layers from the
+ * originals instead. */
+ CoglMaterialLayer *copy = _cogl_material_layer_copy (l->data);
+ _cogl_material_add_layer_difference (dest, copy, FALSE);
+ cogl_handle_unref (copy);
+ }
+ }
+
+ if (differences & COGL_MATERIAL_STATE_NEEDS_BIG_STATE)
+ {
+ if (!dest->has_big_state)
+ {
+ dest->big_state = g_slice_new (CoglMaterialBigState);
+ dest->has_big_state = TRUE;
+ }
+ big_state = dest->big_state;
+ }
+ else
+ goto check_for_blending_change;
+
+ if (differences & COGL_MATERIAL_STATE_LIGHTING)
+ {
+ memcpy (&big_state->lighting_state,
+ &src->big_state->lighting_state,
+ sizeof (CoglMaterialLightingState));
+ }
+
+ if (differences & COGL_MATERIAL_STATE_ALPHA_FUNC)
+ {
+ memcpy (&big_state->alpha_state,
+ &src->big_state->alpha_state,
+ sizeof (CoglMaterialAlphaFuncState));
+ }
+
+ if (differences & COGL_MATERIAL_STATE_BLEND)
+ {
+ memcpy (&big_state->blend_state,
+ &src->big_state->blend_state,
+ sizeof (CoglMaterialBlendState));
+ }
+
+ if (differences & COGL_MATERIAL_STATE_USER_SHADER)
+ {
+ if (src->big_state->user_program)
+ big_state->user_program =
+ cogl_handle_ref (src->big_state->user_program);
+ else
+ big_state->user_program = COGL_INVALID_HANDLE;
+ }
+
+ /* XXX: we shouldn't bother doing this in most cases since
+ * _copy_differences is typically used to initialize material state
+ * by copying it from the current authority, so it's not actually
+ * *changing* anything.
+ */
+check_for_blending_change:
+ if (differences & COGL_MATERIAL_STATE_AFFECTS_BLENDING)
+ handle_automatic_blend_enable (dest, differences);
+
+ dest->differences |= differences;
+}
+
+static void
+_cogl_material_initialize_state (CoglMaterial *dest,
+ CoglMaterial *src,
+ CoglMaterialState state)
+{
+ if (dest == src)
+ return;
+
+ if (state != COGL_MATERIAL_STATE_LAYERS)
+ _cogl_material_copy_differences (dest, src, state);
+ else
+ {
+ dest->n_layers = src->n_layers;
+ dest->layer_differences = NULL;
+ }
+}
+
+typedef gboolean (*CoglMaterialChildCallback) (CoglMaterial *child,
+ void *user_data);
+
+static void
+_cogl_material_foreach_child (CoglMaterial *material,
+ CoglMaterialChildCallback callback,
+ void *user_data)
+{
+ if (material->has_children)
+ {
+ callback (material->first_child, user_data);
+ g_list_foreach (material->children, (GFunc)callback, user_data);
+ }
+}
+
+static gboolean
+change_parent_cb (CoglMaterial *child,
+ void *user_data)
+{
+ child->parent = user_data;
+ return TRUE;
+}
+
+static void
+_cogl_material_pre_change_notify (CoglMaterial *material,
+ CoglMaterialState change,
+ const CoglColor *new_color)
+{
+ CoglMaterial *authority;
+
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
+ /* If primitives have been logged in the journal referencing the
+ * current state of this material we need to flush the journal
+ * before we can modify it... */
if (material->journal_ref_count)
{
+ gboolean skip_journal_flush = FALSE;
+
/* XXX: We don't usually need to flush the journal just due to
* color changes since material colors are logged in the
- * journals vertex buffer. The exception is when the change in
+ * journal's vertex buffer. The exception is when the change in
* color enables or disables the need for blending. */
- if (changes == COGL_MATERIAL_CHANGE_COLOR)
+ if (change == COGL_MATERIAL_STATE_COLOR)
{
gboolean will_need_blending =
- _cogl_material_needs_blending_enabled (material, new_color);
- if (will_need_blending !=
- ((material->flags & COGL_MATERIAL_FLAG_ENABLE_BLEND) ?
- TRUE : FALSE))
- _cogl_journal_flush ();
+ _cogl_material_needs_blending_enabled (material,
+ change,
+ new_color);
+ gboolean blend_enable = material->real_blend_enable ? TRUE : FALSE;
+
+ if (will_need_blending == blend_enable)
+ skip_journal_flush = TRUE;
}
- else
+
+ if (!skip_journal_flush)
_cogl_journal_flush ();
}
@@ -342,87 +1081,2080 @@ _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_change_notify)
- backends[material->backend]->material_change_notify (material,
- changes,
- new_color);
+ backends[material->backend]->material_pre_change_notify)
+ backends[material->backend]->material_pre_change_notify (material,
+ change,
+ new_color);
- /* Invalidate any ->current_material reference to this material since
- * it will no longer represent the current state.
+ /*
+ * There is an arbitrary tree of descendants of this material; any of
+ * which may indirectly depend on this material as the authority for
+ * some set of properties. (Meaning for example that one of its
+ * descendants derives its color or blending state from this
+ * material.)
*
- * NB: we also invalidate this if the material is freed
+ * We can't modify any property that this material is the authority
+ * for unless we create another material to take its place first and
+ * make sure descendants reference this new material instead.
+ */
+ if (material->has_children)
+ {
+ CoglMaterial *new_authority;
+ COGL_STATIC_COUNTER (material_copy_on_write_counter,
+ "material copy on write counter",
+ "Increments each time a material "
+ "must be copied to allow modification",
+ 0 /* no application private data */);
+
+ COGL_COUNTER_INC (_cogl_uprof_context, material_copy_on_write_counter);
+
+ new_authority = cogl_material_copy (material->parent);
+
+ /* We could explicitly walk the descendants, OR together the set
+ * of differences that we determine this material is the
+ * authority on and only copy those differences copied across.
+ *
+ * Or, if we don't explicitly walk the descendants we at least
+ * know that material->differences represents the largest set of
+ * differences that this material could possibly be an authority
+ * on.
+ *
+ * We do the later just because it's simplest, but we might need
+ * to come back to this later...
+ */
+ _cogl_material_copy_differences (new_authority, material,
+ material->differences);
+
+ /* Reparent the children of material to be children of
+ * new_authority instead... */
+ new_authority->has_children = TRUE;
+ new_authority->first_child = material->first_child;
+ new_authority->children = material->children;
+ material->has_children = FALSE;
+ _cogl_material_foreach_child (new_authority,
+ change_parent_cb,
+ new_authority);
+
+ /* If the new_authority has any
+ * ->layer_differences then we dirty any layer_caches for all
+ * its descendants since _cogl_material_copy_differences will
+ * have created new layers which won't be correctly referenced
+ * in the caches.
+ */
+ if (new_authority->differences & COGL_MATERIAL_STATE_LAYERS)
+ {
+ /* NB: we can't call recursively_free_layer_caches
+ * directly for the material since it will assume if
+ * new_authority->layers_cache == NULL then all the
+ * descendants already have invalidated caches. */
+ _cogl_material_foreach_child (new_authority,
+ (CoglMaterialChildCallback)
+ recursively_free_layer_caches,
+ NULL);
+ }
+ }
+
+ /* At this point we know we have a material with no dependants so we
+ * are now free to modify the material. */
+ g_assert (!material->has_children);
+
+ /* If the material isn't already an authority for the state group
+ * being modified then we need to initialize the corresponding
+ * state. */
+ if (change & COGL_MATERIAL_STATE_ALL_SPARSE)
+ authority = _cogl_material_get_authority (material, change);
+ else
+ authority = material;
+ _cogl_material_initialize_state (material, authority, change);
+
+ /* Each material has a sorted cache of the layers it depends on
+ * which will need updating via _cogl_material_update_layers_cache
+ * if a material's layers are changed. */
+ if (change == COGL_MATERIAL_STATE_LAYERS)
+ recursively_free_layer_caches (material);
+
+ /* If the material being changed is the same as the last material we
+ * flushed then we keep a track of the changes so we can try to
+ * minimize redundant OpenGL calls if the same material is flushed
+ * again.
*/
if (ctx->current_material == material)
- ctx->current_material = COGL_INVALID_HANDLE;
+ ctx->current_material_changes_since_flush |= change;
+}
+
+
+static void
+_cogl_material_add_layer_difference (CoglMaterial *material,
+ CoglMaterialLayer *layer,
+ gboolean inc_n_layers)
+{
+ g_return_if_fail (layer->owner == NULL);
+
+ layer->owner = material;
+ cogl_handle_ref ((CoglHandle)layer);
+
+ /* - Flush journal primitives referencing the current state.
+ * - Make sure the material has no dependants so it may be modified.
+ * - If the material isn't currently an authority for the state being
+ * changed, then initialize that state from the current authority.
+ */
+ _cogl_material_pre_change_notify (material,
+ COGL_MATERIAL_STATE_LAYERS,
+ NULL);
+
+ material->differences |= COGL_MATERIAL_STATE_LAYERS;
+
+ material->layer_differences =
+ g_list_prepend (material->layer_differences, layer);
+
+ if (inc_n_layers)
+ material->n_layers++;
+}
+
+/* NB: If you are calling this it's your responsibility to have
+ * already called:
+ * _cogl_material_pre_change_notify (m, _CHANGE_LAYERS, NULL);
+ */
+static void
+_cogl_material_remove_layer_difference (CoglMaterial *material,
+ CoglMaterialLayer *layer,
+ gboolean dec_n_layers)
+{
+ g_return_if_fail (layer->owner == material);
+
+ /* - Flush journal primitives referencing the current state.
+ * - Make sure the material has no dependants so it may be modified.
+ * - If the material isn't currently an authority for the state being
+ * changed, then initialize that state from the current authority.
+ */
+ _cogl_material_pre_change_notify (material,
+ COGL_MATERIAL_STATE_LAYERS,
+ NULL);
+
+ layer->owner = NULL;
+ cogl_handle_unref ((CoglHandle)layer);
+
+ material->differences |= COGL_MATERIAL_STATE_LAYERS;
+
+ material->layer_differences =
+ g_list_remove (material->layer_differences, layer);
+
+ if (dec_n_layers)
+ material->n_layers--;
}
static void
-handle_automatic_blend_enable (CoglMaterial *material)
+_cogl_material_try_reverting_layers_authority (CoglMaterial *authority,
+ CoglMaterial *old_authority)
{
- gboolean needs_blending_enabled =
- _cogl_material_needs_blending_enabled (material, NULL);
-
- if (needs_blending_enabled !=
- !!(material->flags & COGL_MATERIAL_FLAG_ENABLE_BLEND))
+ if (authority->layer_differences == NULL && authority->parent)
{
+ /* If the previous _STATE_LAYERS authority has the same
+ * ->n_layers then we can revert to that being the authority
+ * again. */
+ if (!old_authority)
+ {
+ old_authority =
+ _cogl_material_get_authority (authority->parent,
+ COGL_MATERIAL_STATE_LAYERS);
+ }
+
+ if (old_authority->n_layers == authority->n_layers)
+ authority->differences &= ~COGL_MATERIAL_STATE_LAYERS;
+ }
+}
+
+
+static void
+handle_automatic_blend_enable (CoglMaterial *material,
+ CoglMaterialState change)
+{
+ gboolean blend_enable =
+ _cogl_material_needs_blending_enabled (material, change, NULL);
+
+ if (blend_enable != material->real_blend_enable)
+ {
+ /* - Flush journal primitives referencing the current state.
+ * - Make sure the material has no dependants so it may be
+ * modified.
+ * - If the material isn't currently an authority for the state
+ * being changed, then initialize that state from the current
+ * authority.
+ */
_cogl_material_pre_change_notify (material,
- COGL_MATERIAL_CHANGE_ENABLE_BLEND,
+ COGL_MATERIAL_STATE_REAL_BLEND_ENABLE,
NULL);
- if (needs_blending_enabled)
- material->flags |= COGL_MATERIAL_FLAG_ENABLE_BLEND;
- else
- material->flags &= ~COGL_MATERIAL_FLAG_ENABLE_BLEND;
+ material->real_blend_enable = blend_enable;
+ }
+}
+
+typedef struct
+{
+ int keep_n;
+ int current_pos;
+ gboolean needs_pruning;
+ int first_index_to_prune;
+} CoglMaterialPruneLayersInfo;
+
+static gboolean
+update_prune_layers_info_cb (CoglMaterialLayer *layer, void *user_data)
+{
+ CoglMaterialPruneLayersInfo *state = user_data;
+
+ if (state->current_pos == state->keep_n)
+ {
+ state->needs_pruning = TRUE;
+ state->first_index_to_prune = layer->index;
+ return FALSE;
+ }
+ state->current_pos++;
+ return TRUE;
+}
+
+void
+_cogl_material_prune_to_n_layers (CoglMaterial *material, int n)
+{
+ CoglMaterialPruneLayersInfo state;
+ gboolean notified_change = TRUE;
+ GList *l;
+ GList *next;
+
+ state.keep_n = n;
+ state.current_pos = 0;
+ state.needs_pruning = FALSE;
+ _cogl_material_foreach_layer (material,
+ update_prune_layers_info_cb,
+ &state);
+
+ material->n_layers = n;
+
+ if (!state.needs_pruning)
+ return;
+
+ if (!(material->differences & COGL_MATERIAL_STATE_LAYERS))
+ return;
+
+ /* It's possible that this material owns some of the layers being
+ * discarded, so we'll need to unlink them... */
+ for (l = material->layer_differences; l; l = next)
+ {
+ CoglMaterialLayer *layer = l->data;
+ next = l->next; /* we're modifying the list we're iterating */
+
+ if (layer->index > state.first_index_to_prune)
+ {
+ if (!notified_change)
+ {
+ /* - Flush journal primitives referencing the current
+ * state.
+ * - Make sure the material has no dependants so it may
+ * be modified.
+ * - If the material isn't currently an authority for
+ * the state being changed, then initialize that state
+ * from the current authority.
+ */
+ _cogl_material_pre_change_notify (material,
+ COGL_MATERIAL_STATE_LAYERS,
+ NULL);
+ notified_change = TRUE;
+ }
+
+ material->layer_differences =
+ g_list_delete_link (material->layer_differences, l);
+ }
}
}
static void
_cogl_material_backend_layer_change_notify (CoglMaterialLayer *layer,
- unsigned long changes)
+ CoglMaterialLayerState change)
{
- int backend = layer->material->backend;
- if (backend == COGL_MATERIAL_BACKEND_UNDEFINED)
- return;
+ int i;
- if (backends[backend]->layer_change_notify)
- backends[backend]->layer_change_notify (layer, changes);
+ /* NB: layers may be used by multiple materials which may be using
+ * different backends, therefore we determine which backends to
+ * notify based on the private state pointers for each backend...
+ */
+ 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);
+ }
+}
+
+static unsigned int
+get_n_args_for_combine_func (GLint func)
+{
+ switch (func)
+ {
+ case GL_REPLACE:
+ return 1;
+ case GL_MODULATE:
+ case GL_ADD:
+ case GL_ADD_SIGNED:
+ case GL_SUBTRACT:
+ case GL_DOT3_RGB:
+ case GL_DOT3_RGBA:
+ return 2;
+ case GL_INTERPOLATE:
+ return 3;
+ }
+ return 0;
}
static void
-_cogl_material_layer_pre_change_notify (CoglMaterialLayer *layer,
- CoglMaterialLayerChangeFlags changes)
+_cogl_material_layer_initialize_state (CoglMaterialLayer *dest,
+ CoglMaterialLayer *src,
+ unsigned long differences)
{
- CoglTextureUnit *unit = _cogl_get_texture_unit (layer->unit_index);
+ CoglMaterialLayerBigState *big_state;
- /* Look at the texture unit corresponding to this layer, if it
- * currently has a back reference to this layer then invalidate it
- * so that next time we come to flush this layer we'll see that the
- * texture unit no longer corresponds to this layer's state.
+ dest->differences |= differences;
+
+ if (differences & COGL_MATERIAL_LAYER_STATE_UNIT)
+ dest->unit_index = src->unit_index;
+
+ if (differences & COGL_MATERIAL_LAYER_STATE_TEXTURE)
+ dest->texture = src->texture;
+
+ if (differences & COGL_MATERIAL_LAYER_STATE_FILTERS)
+ {
+ dest->min_filter = src->min_filter;
+ dest->mag_filter = src->mag_filter;
+ }
+
+ if (differences & COGL_MATERIAL_LAYER_STATE_WRAP_MODES)
+ {
+ dest->wrap_mode_s = src->wrap_mode_s;
+ dest->wrap_mode_t = src->wrap_mode_t;
+ dest->wrap_mode_r = src->wrap_mode_r;
+ }
+
+ if (differences & COGL_MATERIAL_LAYER_STATE_NEEDS_BIG_STATE)
+ {
+ if (!dest->has_big_state)
+ {
+ dest->big_state = g_slice_new (CoglMaterialLayerBigState);
+ dest->has_big_state = TRUE;
+ }
+ big_state = dest->big_state;
+ }
+ else
+ return;
+
+ if (differences & COGL_MATERIAL_LAYER_STATE_COMBINE)
+ {
+ int n_args;
+ int i;
+ GLint func = src->big_state->texture_combine_rgb_func;
+ big_state->texture_combine_rgb_func = func;
+ n_args = get_n_args_for_combine_func (func);
+ for (i = 0; i < n_args; i++)
+ {
+ big_state->texture_combine_rgb_src[i] =
+ src->big_state->texture_combine_rgb_src[i];
+ big_state->texture_combine_rgb_op[i] =
+ src->big_state->texture_combine_rgb_op[i];
+ }
+
+ func = src->big_state->texture_combine_alpha_func;
+ big_state->texture_combine_alpha_func = func;
+ n_args = get_n_args_for_combine_func (func);
+ for (i = 0; i < n_args; i++)
+ {
+ big_state->texture_combine_alpha_src[i] =
+ src->big_state->texture_combine_alpha_src[i];
+ big_state->texture_combine_alpha_op[i] =
+ src->big_state->texture_combine_alpha_op[i];
+ }
+ }
+
+ if (differences & COGL_MATERIAL_LAYER_STATE_COMBINE_CONSTANT)
+ memcpy (dest->big_state->texture_combine_constant,
+ src->big_state->texture_combine_constant,
+ sizeof (float) * 4);
+
+ if (differences & COGL_MATERIAL_LAYER_STATE_USER_MATRIX)
+ dest->big_state->matrix = src->big_state->matrix;
+}
+
+/* NB: This function will allocate a new derived layer if you are
+ * trying to change the state of a layer with dependants so you must
+ * always check the return value.
+ *
+ * If a new layer is returned it will be owned by required_owner.
+ *
+ * required_owner can only by NULL for new, currently unowned layers
+ * with no dependants.
+ */
+static CoglMaterialLayer *
+_cogl_material_layer_pre_change_notify (CoglMaterial *required_owner,
+ CoglMaterialLayer *layer,
+ CoglMaterialLayerState change)
+{
+ CoglTextureUnit *unit;
+ CoglMaterialLayer *authority;
+
+ /* Identify the case where the layer is new with no owner or
+ * dependants and so we don't need to do anything. */
+ if (layer->has_children == FALSE && layer->owner == NULL)
+ goto init_layer_state;
+
+ /* We only allow a NULL required_owner for new layers */
+ g_return_val_if_fail (required_owner != NULL, layer);
+
+ /* Unlike materials; layers are simply considered immutable once
+ * they have dependants - either children or another material owner.
*/
+ if (layer->has_children || layer->owner != required_owner)
+ {
+ CoglMaterialLayer *new = _cogl_material_layer_copy (layer);
+ _cogl_material_add_layer_difference (required_owner, new, FALSE);
+ cogl_handle_unref (new);
+ layer = new;
+ goto init_layer_state;
+ }
+
+ /* Note: At this point we know there is only one material dependant on
+ * this layer (required_owner), and there are no other layers
+ * dependant on this layer so it's ok to modify it. */
+
+ if (required_owner->journal_ref_count)
+ _cogl_journal_flush ();
+
+ _cogl_material_backend_layer_change_notify (layer, change);
+
+ /* If the layer being changed is the same as the last layer we
+ * flushed to the corresponding texture unit then we keep a track of
+ * the changes so we can try to minimize redundant OpenGL calls if
+ * the same layer is flushed again.
+ */
+ unit = _cogl_get_texture_unit (_cogl_material_layer_get_unit_index (layer));
if (unit->layer == layer)
- unit->layer = NULL;
+ unit->layer_changes_since_flush |= change;
- _cogl_material_backend_layer_change_notify (layer, changes);
+init_layer_state:
- _cogl_material_pre_change_notify (layer->material,
- COGL_MATERIAL_CHANGE_LAYERS,
- NULL);
+ /* If the material isn't already an authority for the state group
+ * being modified then we need to initialize the corresponding
+ * state. */
+ authority = _cogl_material_layer_get_authority (layer, change);
+ _cogl_material_layer_initialize_state (layer, authority, change);
+
+ return layer;
+}
+
+/* XXX: This is duplicated logic; the same as for
+ * _cogl_material_prune_redundant_ancestry it would be nice to find a
+ * way to consolidate these functions! */
+static void
+_cogl_material_layer_prune_redundant_ancestry (CoglMaterialLayer *layer)
+{
+ CoglMaterialLayer *new_parent = layer->parent;
+
+ /* walk up past ancestors that are now redundant and potentially
+ * reparent the layer. */
+ while (new_parent->parent &&
+ (new_parent->differences | layer->differences) ==
+ layer->differences)
+ new_parent = new_parent->parent;
+
+ if (new_parent != layer->parent)
+ {
+ CoglMaterialLayer *old_parent = layer->parent;
+ layer->parent = cogl_handle_ref (new_parent);
+ /* Note: the old parent may indirectly be keeping
+ the new parent alive so we have to ref the new
+ parent before unrefing the old */
+ cogl_handle_unref (old_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_material_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 CoglMaterialLayer *
+_cogl_material_set_layer_unit (CoglMaterial *required_owner,
+ CoglMaterialLayer *layer,
+ int unit_index)
+{
+ CoglMaterialLayerState change = COGL_MATERIAL_LAYER_STATE_UNIT;
+ CoglMaterialLayer *authority =
+ _cogl_material_layer_get_authority (layer, change);
+ CoglMaterialLayer *new;
+
+ if (authority->unit_index == unit_index)
+ return layer;
+
+ new =
+ _cogl_material_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 && authority->parent != NULL)
+ {
+ CoglMaterialLayer *old_authority =
+ _cogl_material_layer_get_authority (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_material_layer_prune_redundant_ancestry (layer);
+ }
+
+ return layer;
+}
+
+typedef struct
+{
+ /* The layer we are trying to find */
+ int layer_index;
+
+ /* The layer we find or untouched if not found */
+ CoglMaterialLayer *layer;
+
+ /* If the layer can't be found then a new layer should be
+ * inserted after this texture unit index... */
+ int insert_after;
+
+ /* When adding a layer we need the list of layers to shift up
+ * to a new texture unit. When removing we need the list of
+ * layers to shift down.
+ *
+ * Note: the list isn't sorted */
+ CoglMaterialLayer **layers_to_shift;
+ int n_layers_to_shift;
+
+ /* When adding a layer we don't need a complete list of
+ * layers_to_shift if we find a layer already corresponding to the
+ * layer_index. */
+ gboolean ignore_shift_layers_if_found;
+
+} CoglMaterialLayerInfo;
+
+/* Returns TRUE once we know there is nothing more to update */
+static gboolean
+update_layer_info (CoglMaterialLayer *layer,
+ CoglMaterialLayerInfo *layer_info)
+{
+ if (layer->index == layer_info->layer_index)
+ {
+ layer_info->layer = layer;
+ if (layer_info->ignore_shift_layers_if_found)
+ return TRUE;
+ }
+ else if (layer->index < layer_info->layer_index)
+ {
+ int unit_index = _cogl_material_layer_get_unit_index (layer);
+ layer_info->insert_after = unit_index;
+ }
+ else
+ layer_info->layers_to_shift[layer_info->n_layers_to_shift++] =
+ layer;
+
+ return FALSE;
+}
+
+/* Returns FALSE to break out of a _foreach_layer () iteration */
+static gboolean
+update_layer_info_cb (CoglMaterialLayer *layer,
+ void *user_data)
+{
+ CoglMaterialLayerInfo *layer_info = user_data;
+
+ if (update_layer_info (layer, layer_info))
+ return FALSE; /* break */
+ else
+ return TRUE; /* continue */
+}
+
+static void
+_cogl_material_get_layer_info (CoglMaterial *material,
+ CoglMaterialLayerInfo *layer_info)
+{
+ /* Note: we are assuming this material is a _STATE_LAYERS authority */
+ int n_layers = material->n_layers;
+ int i;
+
+ /* FIXME: _cogl_material_foreach_layer now calls
+ * _cogl_material_update_layers_cache anyway so this codepath is
+ * pointless! */
+ if (layer_info->ignore_shift_layers_if_found &&
+ material->layers_cache_dirty)
+ {
+ /* The expectation is that callers of
+ * _cogl_material_get_layer_info are likely to be modifying the
+ * list of layers associated with a material so in this case
+ * where we don't have a cache of the layers and we don't
+ * necessarily have to iterate all the layers of the material we
+ * use a foreach_layer callback instead of updating the cache
+ * and iterating that as below. */
+ _cogl_material_foreach_layer (material,
+ update_layer_info_cb,
+ layer_info);
+ return;
+ }
+
+ _cogl_material_update_layers_cache (material);
+ for (i = 0; i < n_layers; i++)
+ {
+ CoglMaterialLayer *layer = material->layers_cache[i];
+
+ if (update_layer_info (layer, layer_info))
+ return;
+ }
+}
+
+static CoglMaterialLayer *
+_cogl_material_get_layer (CoglMaterial *material,
+ int layer_index)
+{
+ CoglMaterial *authority =
+ _cogl_material_get_authority (material, COGL_MATERIAL_STATE_LAYERS);
+ CoglMaterialLayerInfo layer_info;
+ CoglMaterialLayer *layer;
+ int unit_index;
+ int i;
+
+ _COGL_GET_CONTEXT (ctx, NULL);
+
+ /* The layer index of the layer we want info about */
+ layer_info.layer_index = layer_index;
+
+ /* If a layer already exists with the given index this will be
+ * updated. */
+ layer_info.layer = NULL;
+
+ /* If a layer isn't found for the given index we'll need to know
+ * where to insert a new layer. */
+ layer_info.insert_after = -1;
+
+ /* If a layer can't be found then we'll need to insert a new layer
+ * and bump up the texture unit for all layers with an index
+ * > layer_index. */
+ layer_info.layers_to_shift =
+ g_alloca (sizeof (CoglMaterialLayer *) * authority->n_layers);
+ layer_info.n_layers_to_shift = 0;
+
+ /* If an exact match is found though we don't need a complete
+ * list of layers with indices > layer_index... */
+ layer_info.ignore_shift_layers_if_found = TRUE;
+
+ _cogl_material_get_layer_info (authority, &layer_info);
+
+ if (layer_info.layer)
+ return layer_info.layer;
+
+ unit_index = layer_info.insert_after + 1;
+ if (unit_index == 0)
+ layer = _cogl_material_layer_copy (ctx->default_layer_0);
+ else
+ {
+ CoglMaterialLayer *new;
+ layer = _cogl_material_layer_copy (ctx->default_layer_n);
+ new = _cogl_material_set_layer_unit (NULL, layer, unit_index);
+ /* Since we passed a newly allocated layer we wouldn't expect
+ * _set_layer_unit() to have to allocate *another* layer. */
+ g_assert (new == layer);
+ layer->index = layer_index;
+ }
+
+ for (i = 0; i < layer_info.n_layers_to_shift; i++)
+ {
+ CoglMaterialLayer *shift_layer = layer_info.layers_to_shift[i];
+
+ unit_index = _cogl_material_layer_get_unit_index (shift_layer);
+ _cogl_material_set_layer_unit (material, shift_layer, unit_index + 1);
+ /* NB: shift_layer may not be writeable so _set_layer_unit()
+ * will allocate a derived layer internally which will become
+ * owned by material. Check the return value if we need to do
+ * anything else with this layer. */
+ }
+
+ _cogl_material_add_layer_difference (material, layer, TRUE);
+
+ cogl_handle_unref (layer);
+
+ return layer;
+}
+
+static CoglHandle
+_cogl_material_layer_get_texture (CoglMaterialLayer *layer)
+{
+ CoglMaterialLayer *authority =
+ _cogl_material_layer_get_authority (layer,
+ COGL_MATERIAL_LAYER_STATE_TEXTURE);
+
+ return authority->texture;
+}
+
+static void
+_cogl_material_prune_empty_layer_difference (CoglMaterial *layers_authority,
+ CoglMaterialLayer *layer)
+{
+ /* Find the GList link that references the empty layer */
+ GList *link = g_list_find (layers_authority->layer_differences, layer);
+ /* No material directly owns the root node layer so this is safe... */
+ CoglMaterialLayer *layer_parent = layer->parent;
+ CoglMaterialLayerInfo layer_info;
+ CoglMaterial *old_layers_authority;
+
+ g_return_if_fail (link != NULL);
+
+ /* If the layer's parent doesn't have an owner then we can simply
+ * take ownership ourselves and drop our reference on the empty
+ * layer.
+ */
+ if (layer_parent->index == layer->index && layer_parent->owner == NULL)
+ {
+ cogl_handle_ref (layer_parent);
+ cogl_handle_unref (layer);
+ link->data = layer->parent;
+ recursively_free_layer_caches (layers_authority);
+ return;
+ }
+
+ /* Now we want to find the layer that would become the authority for
+ * layer->index if we were to remove layer from
+ * layers_authority->layer_differences
+ */
+
+ /* The layer index of the layer we want info about */
+ layer_info.layer_index = layer->index;
+
+ /* If a layer already exists with the given index this will be
+ * updated. */
+ layer_info.layer = NULL;
+
+ /* If a layer can't be found then we'll need to insert a new layer
+ * and bump up the texture unit for all layers with an index
+ * > layer_index. */
+ layer_info.layers_to_shift =
+ g_alloca (sizeof (CoglMaterialLayer *) * layers_authority->n_layers);
+ layer_info.n_layers_to_shift = 0;
+
+ /* If an exact match is found though we don't need a complete
+ * list of layers with indices > layer_index... */
+ layer_info.ignore_shift_layers_if_found = TRUE;
+
+ /* We know the default/root material isn't a LAYERS authority so
+ * it's safe to dereference layers_authority->parent. */
+ old_layers_authority =
+ _cogl_material_get_authority (layers_authority->parent,
+ COGL_MATERIAL_STATE_LAYERS);
+
+ _cogl_material_get_layer_info (old_layers_authority, &layer_info);
+
+ /* If layer is the defining layer for the corresponding ->index then
+ * we can't get rid of it. */
+ if (!layer_info.layer)
+ return;
+
+ /* If the layer that would become the authority for layer->index
+ * is layer->parent then we can simply remove the layer difference. */
+ if (layer_info.layer == layer->parent)
+ {
+ _cogl_material_remove_layer_difference (layers_authority, layer, FALSE);
+ _cogl_material_try_reverting_layers_authority (layers_authority,
+ old_layers_authority);
+ }
+}
+
+static void
+_cogl_material_set_layer_texture (CoglMaterial *material,
+ int layer_index,
+ CoglHandle texture,
+ gboolean overriden,
+ GLuint slice_gl_texture,
+ GLenum slice_gl_target)
+{
+ CoglMaterialLayerState change = COGL_MATERIAL_LAYER_STATE_TEXTURE;
+ CoglMaterialLayer *layer;
+ CoglMaterialLayer *authority;
+ CoglMaterialLayer *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
+ * material. If the layer is created then it will be owned by
+ * material. */
+ layer = _cogl_material_get_layer (material, layer_index);
+
+ /* Now find the ancestor of the layer that is the authority for the
+ * state we want to change */
+ authority = _cogl_material_layer_get_authority (layer, change);
+
+ if (authority->texture_overridden == overriden &&
+ authority->texture == texture &&
+ (authority->texture_overridden == FALSE ||
+ (authority->slice_gl_texture == slice_gl_texture &&
+ authority->slice_gl_target == slice_gl_target)))
+ return;
+
+ new = _cogl_material_layer_pre_change_notify (material, 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 && authority->parent != NULL)
+ {
+ CoglMaterialLayer *old_authority =
+ _cogl_material_layer_get_authority (authority->parent, change);
+
+ if (old_authority->texture_overridden == overriden &&
+ old_authority->texture == texture &&
+ (old_authority->texture_overridden == FALSE ||
+ (old_authority->slice_gl_texture == slice_gl_texture &&
+ old_authority->slice_gl_target == slice_gl_target)))
+ {
+ layer->differences &= ~change;
+
+ g_assert (layer->owner == material);
+ if (layer->differences == 0)
+ _cogl_material_prune_empty_layer_difference (material,
+ 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;
+ layer->texture_overridden = FALSE;
+ layer->slice_gl_texture = slice_gl_texture;
+ layer->slice_gl_target = slice_gl_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_material_layer_prune_redundant_ancestry (layer);
+ }
+
+changed:
+
+ handle_automatic_blend_enable (material, COGL_MATERIAL_STATE_LAYERS);
+}
+
+static void
+_cogl_material_set_layer_gl_texture_slice (CoglHandle handle,
+ int layer_index,
+ CoglHandle texture,
+ GLuint slice_gl_texture,
+ GLenum slice_gl_target)
+{
+ CoglMaterial *material = COGL_MATERIAL (handle);
+
+ g_return_if_fail (cogl_is_material (handle));
+ /* GL texture overrides can only be set in association with a parent
+ * CoglTexture */
+ g_return_if_fail (cogl_is_texture (texture));
+
+ _cogl_material_set_layer_texture (material,
+ layer_index,
+ texture,
+ TRUE, /* slice override */
+ slice_gl_texture,
+ slice_gl_target);
+}
+
+/* XXX: deprecate and replace with cogl_material_set_layer_texture?
+ *
+ * Originally I was planning on allowing users to set shaders somehow
+ * on layers (thus the ambiguous name), but now I wonder if we will do
+ * that with a more explicit "snippets" API and materials will have
+ * hooks defined to receive these snippets.
+ */
+void
+cogl_material_set_layer (CoglHandle handle,
+ int layer_index,
+ CoglHandle texture)
+{
+ CoglMaterial *material = COGL_MATERIAL (handle);
+
+ g_return_if_fail (cogl_is_material (handle));
+ g_return_if_fail (texture == COGL_INVALID_HANDLE ||
+ cogl_is_texture (texture));
+
+ _cogl_material_set_layer_texture (material,
+ layer_index,
+ texture,
+ FALSE, /* slice override */
+ 0, /* slice_gl_texture */
+ 0); /* slice_gl_target */
+}
+
+typedef struct
+{
+ int i;
+ CoglMaterial *material;
+ unsigned long fallback_layers;
+} CoglMaterialFallbackState;
+
+static gboolean
+fallback_layer_cb (CoglMaterialLayer *layer, void *user_data)
+{
+ CoglMaterialFallbackState *state = user_data;
+ CoglMaterial *material = state->material;
+ CoglHandle texture = _cogl_material_layer_get_texture (layer);
+ GLenum gl_target;
+ COGL_STATIC_COUNTER (layer_fallback_counter,
+ "layer fallback counter",
+ "Increments each time a layer's texture is "
+ "forced to a fallback texture",
+ 0 /* no application private data */);
+
+ _COGL_GET_CONTEXT (ctx, FALSE);
+
+ if (!(state->fallback_layers & 1<i))
+ return TRUE;
+
+ COGL_COUNTER_INC (_cogl_uprof_context, layer_fallback_counter);
+
+ if (G_LIKELY (texture != COGL_INVALID_HANDLE))
+ cogl_texture_get_gl_texture (texture, NULL, &gl_target);
+ else
+ gl_target = GL_TEXTURE_2D;
+
+ if (gl_target == GL_TEXTURE_2D)
+ texture = ctx->default_gl_texture_2d_tex;
+#ifdef HAVE_COGL_GL
+ else if (gl_target == GL_TEXTURE_RECTANGLE_ARB)
+ texture = ctx->default_gl_texture_rect_tex;
+#endif
+ else
+ {
+ g_warning ("We don't have a fallback texture we can use to fill "
+ "in for an invalid material layer, since it was "
+ "using an unsupported texture target ");
+ /* might get away with this... */
+ texture = ctx->default_gl_texture_2d_tex;
+ }
+
+ cogl_material_set_layer (material, layer->index, texture);
+
+ state->i++;
+
+ return TRUE;
+}
+
+void
+_cogl_material_set_layer_wrap_modes (CoglMaterial *material,
+ CoglMaterialLayer *layer,
+ CoglMaterialLayer *authority,
+ CoglMaterialWrapModeInternal wrap_mode_s,
+ CoglMaterialWrapModeInternal wrap_mode_t,
+ CoglMaterialWrapModeInternal wrap_mode_r)
+{
+ CoglMaterialLayer *new;
+ CoglMaterialLayerState change = COGL_MATERIAL_LAYER_STATE_WRAP_MODES;
+
+ if (authority->wrap_mode_s == wrap_mode_s &&
+ authority->wrap_mode_t == wrap_mode_t &&
+ authority->wrap_mode_r == wrap_mode_r)
+ return;
+
+ new = _cogl_material_layer_pre_change_notify (material, 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 && authority->parent != NULL)
+ {
+ CoglMaterialLayer *old_authority =
+ _cogl_material_layer_get_authority (authority->parent, change);
+
+ if (old_authority->wrap_mode_s == wrap_mode_s &&
+ old_authority->wrap_mode_t == wrap_mode_t &&
+ old_authority->wrap_mode_r == wrap_mode_r)
+ {
+ layer->differences &= ~change;
+
+ g_assert (layer->owner == material);
+ if (layer->differences == 0)
+ _cogl_material_prune_empty_layer_difference (material,
+ layer);
+ return;
+ }
+ }
+ }
+
+ layer->wrap_mode_s = wrap_mode_s;
+ layer->wrap_mode_t = wrap_mode_t;
+ layer->wrap_mode_r = wrap_mode_r;
+
+ /* 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_material_layer_prune_redundant_ancestry (layer);
+ }
+}
+
+static CoglMaterialWrapModeInternal
+public_to_internal_wrap_mode (CoglMaterialWrapMode mode)
+{
+ return (CoglMaterialWrapModeInternal)mode;
+}
+
+static CoglMaterialWrapMode
+internal_to_public_wrap_mode (CoglMaterialWrapModeInternal internal_mode)
+{
+ g_return_val_if_fail (internal_mode !=
+ COGL_MATERIAL_WRAP_MODE_INTERNAL_CLAMP_TO_BORDER,
+ COGL_MATERIAL_WRAP_MODE_AUTOMATIC);
+ return (CoglMaterialWrapMode)internal_mode;
+}
+
+void
+cogl_material_set_layer_wrap_mode_s (CoglHandle handle,
+ int layer_index,
+ CoglMaterialWrapMode mode)
+{
+ CoglMaterial *material = COGL_MATERIAL (handle);
+ CoglMaterialLayerState change = COGL_MATERIAL_LAYER_STATE_WRAP_MODES;
+ CoglMaterialLayer *layer;
+ CoglMaterialLayer *authority;
+ CoglMaterialWrapModeInternal internal_mode =
+ public_to_internal_wrap_mode (mode);
+
+ g_return_if_fail (cogl_is_material (handle));
+
+ /* 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
+ * material. If the layer is created then it will be owned by
+ * material. */
+ layer = _cogl_material_get_layer (material, layer_index);
+
+ /* Now find the ancestor of the layer that is the authority for the
+ * state we want to change */
+ authority = _cogl_material_layer_get_authority (layer, change);
+
+ _cogl_material_set_layer_wrap_modes (material, layer, authority,
+ internal_mode,
+ authority->wrap_mode_t,
+ authority->wrap_mode_r);
+}
+
+void
+cogl_material_set_layer_wrap_mode_t (CoglHandle handle,
+ int layer_index,
+ CoglMaterialWrapMode mode)
+{
+ CoglMaterial *material = COGL_MATERIAL (handle);
+ CoglMaterialLayerState change = COGL_MATERIAL_LAYER_STATE_WRAP_MODES;
+ CoglMaterialLayer *layer;
+ CoglMaterialLayer *authority;
+ CoglMaterialWrapModeInternal internal_mode =
+ public_to_internal_wrap_mode (mode);
+
+ g_return_if_fail (cogl_is_material (handle));
+
+ /* 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
+ * material. If the layer is created then it will be owned by
+ * material. */
+ layer = _cogl_material_get_layer (material, layer_index);
+
+ /* Now find the ancestor of the layer that is the authority for the
+ * state we want to change */
+ authority = _cogl_material_layer_get_authority (layer, change);
+
+ _cogl_material_set_layer_wrap_modes (material, layer, authority,
+ authority->wrap_mode_s,
+ internal_mode,
+ authority->wrap_mode_r);
+}
+
+/* TODO: this should be made public once we add support for 3D
+ textures in Cogl */
+void
+_cogl_material_set_layer_wrap_mode_r (CoglHandle handle,
+ int layer_index,
+ CoglMaterialWrapMode mode)
+{
+ CoglMaterial *material = COGL_MATERIAL (handle);
+ CoglMaterialLayerState change = COGL_MATERIAL_LAYER_STATE_WRAP_MODES;
+ CoglMaterialLayer *layer;
+ CoglMaterialLayer *authority;
+ CoglMaterialWrapModeInternal internal_mode =
+ public_to_internal_wrap_mode (mode);
+
+ g_return_if_fail (cogl_is_material (handle));
+
+ /* 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
+ * material. If the layer is created then it will be owned by
+ * material. */
+ layer = _cogl_material_get_layer (material, layer_index);
+
+ /* Now find the ancestor of the layer that is the authority for the
+ * state we want to change */
+ authority = _cogl_material_layer_get_authority (layer, change);
+
+ _cogl_material_set_layer_wrap_modes (material, layer, authority,
+ authority->wrap_mode_s,
+ authority->wrap_mode_t,
+ internal_mode);
+}
+
+void
+cogl_material_set_layer_wrap_mode (CoglHandle handle,
+ int layer_index,
+ CoglMaterialWrapMode mode)
+{
+ CoglMaterial *material = COGL_MATERIAL (handle);
+ CoglMaterialLayerState change = COGL_MATERIAL_LAYER_STATE_WRAP_MODES;
+ CoglMaterialLayer *layer;
+ CoglMaterialLayer *authority;
+ CoglMaterialWrapModeInternal internal_mode =
+ public_to_internal_wrap_mode (mode);
+
+ g_return_if_fail (cogl_is_material (handle));
+
+ /* 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
+ * material. If the layer is created then it will be owned by
+ * material. */
+ layer = _cogl_material_get_layer (material, layer_index);
+
+ /* Now find the ancestor of the layer that is the authority for the
+ * state we want to change */
+ authority = _cogl_material_layer_get_authority (layer, change);
+
+ _cogl_material_set_layer_wrap_modes (material, 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 */
+CoglMaterialWrapMode
+cogl_material_layer_get_wrap_mode_s (CoglHandle handle)
+{
+ CoglMaterialLayer *layer = COGL_MATERIAL_LAYER (handle);
+ CoglMaterialLayerState change = COGL_MATERIAL_LAYER_STATE_WRAP_MODES;
+ CoglMaterialLayer *authority;
+
+ g_return_val_if_fail (cogl_is_material_layer (handle), FALSE);
+
+ /* Now find the ancestor of the layer that is the authority for the
+ * state we want to change */
+ authority = _cogl_material_layer_get_authority (layer, change);
+
+ return internal_to_public_wrap_mode (authority->wrap_mode_s);
+}
+
+CoglMaterialWrapMode
+cogl_material_get_layer_wrap_mode_s (CoglHandle handle, int layer_index)
+{
+ CoglMaterial *material = COGL_MATERIAL (handle);
+ CoglMaterialLayer *layer;
+
+ g_return_val_if_fail (cogl_is_material (handle), 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
+ * material. If the layer is created then it will be owned by
+ * material. */
+ layer = _cogl_material_get_layer (material, layer_index);
+ /* FIXME: we shouldn't ever construct a layer in a getter function */
+
+ return cogl_material_layer_get_wrap_mode_s ((CoglHandle)layer);
+}
+
+/* FIXME: deprecate this API */
+CoglMaterialWrapMode
+cogl_material_layer_get_wrap_mode_t (CoglHandle handle)
+{
+ CoglMaterialLayer *layer = COGL_MATERIAL_LAYER (handle);
+ CoglMaterialLayerState change = COGL_MATERIAL_LAYER_STATE_WRAP_MODES;
+ CoglMaterialLayer *authority;
+
+ g_return_val_if_fail (cogl_is_material_layer (handle), FALSE);
+
+ /* Now find the ancestor of the layer that is the authority for the
+ * state we want to change */
+ authority = _cogl_material_layer_get_authority (layer, change);
+
+ return internal_to_public_wrap_mode (authority->wrap_mode_t);
+}
+
+CoglMaterialWrapMode
+cogl_material_get_layer_wrap_mode_t (CoglHandle handle, int layer_index)
+{
+ CoglMaterial *material = COGL_MATERIAL (handle);
+ CoglMaterialLayer *layer;
+
+ g_return_val_if_fail (cogl_is_material (handle), 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
+ * material. If the layer is created then it will be owned by
+ * material. */
+ layer = _cogl_material_get_layer (material, layer_index);
+ /* FIXME: we shouldn't ever construct a layer in a getter function */
+
+ return cogl_material_layer_get_wrap_mode_t ((CoglHandle)layer);
+}
+
+CoglMaterialWrapMode
+_cogl_material_layer_get_wrap_mode_r (CoglHandle handle)
+{
+ CoglMaterialLayer *layer = COGL_MATERIAL_LAYER (handle);
+ CoglMaterialLayerState change = COGL_MATERIAL_LAYER_STATE_WRAP_MODES;
+ CoglMaterialLayer *authority =
+ _cogl_material_layer_get_authority (layer, change);
+
+ return internal_to_public_wrap_mode (authority->wrap_mode_r);
+}
+
+/* TODO: make this public when we expose 3D textures. */
+CoglMaterialWrapMode
+_cogl_material_get_layer_wrap_mode_r (CoglHandle handle, int layer_index)
+{
+ CoglMaterial *material = COGL_MATERIAL (handle);
+ CoglMaterialLayer *layer;
+
+ g_return_val_if_fail (cogl_is_material (handle), 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
+ * material. If the layer is created then it will be owned by
+ * material. */
+ layer = _cogl_material_get_layer (material, layer_index);
+
+ return _cogl_material_layer_get_wrap_mode_r (layer);
+}
+
+static void
+_cogl_material_layer_get_wrap_modes (CoglMaterialLayer *layer,
+ CoglMaterialWrapModeInternal *wrap_mode_s,
+ CoglMaterialWrapModeInternal *wrap_mode_t,
+ CoglMaterialWrapModeInternal *wrap_mode_r)
+{
+ CoglMaterialLayer *authority =
+ _cogl_material_layer_get_authority (layer,
+ COGL_MATERIAL_LAYER_STATE_WRAP_MODES);
+
+ *wrap_mode_s = authority->wrap_mode_s;
+ *wrap_mode_t = authority->wrap_mode_t;
+ *wrap_mode_r = authority->wrap_mode_r;
+}
+
+typedef struct
+{
+ CoglMaterial *material;
+ CoglMaterialWrapModeOverrides *wrap_mode_overrides;
+ int i;
+} CoglMaterialWrapModeOverridesState;
+
+static gboolean
+apply_wrap_mode_overrides_cb (CoglMaterialLayer *layer,
+ void *user_data)
+{
+ CoglMaterialWrapModeOverridesState *state = user_data;
+ CoglMaterialLayer *authority =
+ _cogl_material_layer_get_authority (layer,
+ COGL_MATERIAL_LAYER_STATE_WRAP_MODES);
+ CoglMaterialWrapModeInternal wrap_mode_s;
+ CoglMaterialWrapModeInternal wrap_mode_t;
+ CoglMaterialWrapModeInternal wrap_mode_r;
+
+ g_return_val_if_fail (state->i < 32, FALSE);
+
+ wrap_mode_s = state->wrap_mode_overrides->values[state->i].s;
+ if (wrap_mode_s == COGL_MATERIAL_WRAP_MODE_OVERRIDE_NONE)
+ wrap_mode_s = (CoglMaterialWrapModeInternal)authority->wrap_mode_s;
+ wrap_mode_t = state->wrap_mode_overrides->values[state->i].t;
+ if (wrap_mode_t == COGL_MATERIAL_WRAP_MODE_OVERRIDE_NONE)
+ wrap_mode_t = (CoglMaterialWrapModeInternal)authority->wrap_mode_t;
+ wrap_mode_r = state->wrap_mode_overrides->values[state->i].r;
+ if (wrap_mode_r == COGL_MATERIAL_WRAP_MODE_OVERRIDE_NONE)
+ wrap_mode_r = (CoglMaterialWrapModeInternal)authority->wrap_mode_r;
+
+ _cogl_material_set_layer_wrap_modes (state->material,
+ layer,
+ authority,
+ wrap_mode_s,
+ wrap_mode_t,
+ wrap_mode_r);
+
+ state->i++;
+
+ return TRUE;
+}
+
+typedef struct
+{
+ CoglMaterial *material;
+ GLuint gl_texture;
+} CoglMaterialOverrideLayerState;
+
+static gboolean
+override_layer_texture_cb (CoglMaterialLayer *layer, void *user_data)
+{
+ CoglMaterialOverrideLayerState *state = user_data;
+ CoglHandle texture;
+ GLenum gl_target;
+
+ texture = _cogl_material_layer_get_texture (layer);
+
+ if (texture != COGL_INVALID_HANDLE)
+ gl_target = cogl_texture_get_gl_texture (texture, NULL, &gl_target);
+ else
+ gl_target = GL_TEXTURE_2D;
+
+ _cogl_material_set_layer_gl_texture_slice (state->material,
+ layer->index,
+ texture,
+ state->gl_texture,
+ gl_target);
+ return TRUE;
+}
+
+static void
+_cogl_material_apply_overrides (CoglMaterial *material,
+ CoglMaterialFlushOptions *options)
+{
+ COGL_STATIC_COUNTER (apply_overrides_counter,
+ "material overrides counter",
+ "Increments each time we have to apply "
+ "override options to a material",
+ 0 /* no application private data */);
+
+ COGL_COUNTER_INC (_cogl_uprof_context, apply_overrides_counter);
+
+ if (options->flags & COGL_MATERIAL_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<flags & COGL_MATERIAL_FLUSH_FALLBACK_MASK)
+ {
+ CoglMaterialFallbackState state;
+
+ state.i = 0;
+ state.material = material;
+ state.fallback_layers = options->fallback_layers;
+
+ _cogl_material_foreach_layer (material,
+ fallback_layer_cb,
+ &state);
+ }
+
+ if (options->flags & COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE)
+ {
+ CoglMaterialOverrideLayerState state;
+
+ _cogl_material_prune_to_n_layers (material, 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_material_foreach_layer() here even though we know
+ * there's only one layer. */
+ state.material = material;
+ state.gl_texture = options->layer0_override_texture;
+ _cogl_material_foreach_layer (material,
+ override_layer_texture_cb,
+ &state);
+ }
+
+ if (options->flags & COGL_MATERIAL_FLUSH_WRAP_MODE_OVERRIDES)
+ {
+ CoglMaterialWrapModeOverridesState state;
+
+ state.material = material;
+ state.wrap_mode_overrides = &options->wrap_mode_overrides;
+ state.i = 0;
+ _cogl_material_foreach_layer (material,
+ apply_wrap_mode_overrides_cb,
+ &state);
+ }
+}
+
+static gboolean
+_cogl_material_layer_texture_equal (CoglMaterialLayer *authority0,
+ CoglMaterialLayer *authority1)
+{
+ if (authority0->texture != authority1->texture)
+ return FALSE;
+ return TRUE;
+}
+
+/* Determine the mask of differences between two layers.
+ *
+ * XXX: If layers and materials could both be cast to a common Tree
+ * type of some kind then we could have a unified
+ * compare_differences() function.
+ */
+static unsigned long
+_cogl_material_layer_compare_differences (CoglMaterialLayer *layer0,
+ CoglMaterialLayer *layer1)
+{
+ CoglMaterialLayer *node0;
+ CoglMaterialLayer *node1;
+ int len0;
+ int len1;
+ int len0_index;
+ int len1_index;
+ int count;
+ int i;
+ CoglMaterialLayer *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->material0_nodes, and ctx->material1_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
+ * material_differences mask.
+ */
+
+ g_array_set_size (ctx->material0_nodes, 0);
+ g_array_set_size (ctx->material1_nodes, 0);
+ for (node0 = layer0; node0; node0 = node0->parent)
+ g_array_append_vals (ctx->material0_nodes, &node0, 1);
+ for (node1 = layer1; node1; node1 = node1->parent)
+ g_array_append_vals (ctx->material1_nodes, &node1, 1);
+
+ len0 = ctx->material0_nodes->len;
+ len1 = ctx->material1_nodes->len;
+ /* There's no point looking at the last entries since we know both
+ * layers must have the same default layer as their root node. */
+ len0_index = len0 - 2;
+ len1_index = len1 - 2;
+ count = MIN (len0, len1) - 1;
+ for (i = 0; i < count; i++)
+ {
+ node0 = g_array_index (ctx->material0_nodes,
+ CoglMaterialLayer *, len0_index--);
+ node1 = g_array_index (ctx->material1_nodes,
+ CoglMaterialLayer *, len1_index--);
+ if (node0 != node1)
+ {
+ common_ancestor = node0->parent;
+ break;
+ }
+ }
+
+ /* If we didn't already find the first the common_ancestor ancestor
+ * that's because one material is a direct descendant of the other
+ * and in this case the first common ancestor is the last node we
+ * looked at. */
+ if (!common_ancestor)
+ common_ancestor = node0;
+
+ count = len0 - 1;
+ for (i = 0; i < count; i++)
+ {
+ node0 = g_array_index (ctx->material0_nodes, CoglMaterialLayer *, i);
+ if (node0 == common_ancestor)
+ break;
+ layers_difference |= node0->differences;
+ }
+
+ count = len1 - 1;
+ for (i = 0; i < count; i++)
+ {
+ node1 = g_array_index (ctx->material1_nodes, CoglMaterialLayer *, i);
+ if (node1 == common_ancestor)
+ break;
+ layers_difference |= node1->differences;
+ }
+
+ return layers_difference;
+}
+
+static gboolean
+_cogl_material_layer_combine_state_equal (CoglMaterialLayer *authority0,
+ CoglMaterialLayer *authority1)
+{
+ CoglMaterialLayerBigState *big_state0 = authority0->big_state;
+ CoglMaterialLayerBigState *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 =
+ 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 =
+ 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_material_layer_combine_constant_equal (CoglMaterialLayer *authority0,
+ CoglMaterialLayer *authority1)
+{
+ return memcmp (authority0->big_state->texture_combine_constant,
+ authority1->big_state->texture_combine_constant,
+ sizeof (float) * 4) == 0 ? TRUE : FALSE;
+}
+
+static gboolean
+_cogl_material_layer_filters_equal (CoglMaterialLayer *authority0,
+ CoglMaterialLayer *authority1)
+{
+ if (authority0->mag_filter != authority1->mag_filter)
+ return FALSE;
+ if (authority0->min_filter != authority1->min_filter)
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+_cogl_material_layer_wrap_modes_equal (CoglMaterialLayer *authority0,
+ CoglMaterialLayer *authority1)
+{
+ if (authority0->wrap_mode_s != authority1->wrap_mode_s ||
+ authority0->wrap_mode_t != authority1->wrap_mode_t ||
+ authority0->wrap_mode_r != authority1->wrap_mode_r)
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+_cogl_material_layer_user_matrix_equal (CoglMaterialLayer *authority0,
+ CoglMaterialLayer *authority1)
+{
+ CoglMaterialLayerBigState *big_state0 = authority0->big_state;
+ CoglMaterialLayerBigState *big_state1 = authority1->big_state;
+
+ if (!cogl_matrix_equal (&big_state0->matrix, &big_state1->matrix))
+ return FALSE;
+
+ return TRUE;
+}
+
+typedef gboolean
+(*CoglMaterialLayerStateComparitor) (CoglMaterialLayer *authority0,
+ CoglMaterialLayer *authority1);
+
+static gboolean
+layer_state_equal (CoglMaterialLayerState state,
+ CoglMaterialLayer *layer0,
+ CoglMaterialLayer *layer1,
+ CoglMaterialLayerStateComparitor comparitor)
+{
+ CoglMaterialLayer *authority0 =
+ _cogl_material_layer_get_authority (layer0, state);
+ CoglMaterialLayer *authority1 =
+ _cogl_material_layer_get_authority (layer1, state);
+
+ return comparitor (authority0, authority1);
+}
+
+static gboolean
+_cogl_material_layer_equal (CoglMaterialLayer *layer0,
+ CoglMaterialLayer *layer1)
+{
+ unsigned long layers_difference;
+
+ if (layer0 == layer1)
+ return TRUE;
+
+ layers_difference =
+ _cogl_material_layer_compare_differences (layer0, layer1);
+
+ if (layers_difference & COGL_MATERIAL_LAYER_STATE_TEXTURE &&
+ !layer_state_equal (COGL_MATERIAL_LAYER_STATE_TEXTURE,
+ layer0, layer1,
+ _cogl_material_layer_texture_equal))
+ return FALSE;
+
+ if (layers_difference & COGL_MATERIAL_LAYER_STATE_COMBINE &&
+ !layer_state_equal (COGL_MATERIAL_LAYER_STATE_COMBINE,
+ layer0, layer1,
+ _cogl_material_layer_combine_state_equal))
+ return FALSE;
+
+ if (layers_difference & COGL_MATERIAL_LAYER_STATE_COMBINE_CONSTANT &&
+ !layer_state_equal (COGL_MATERIAL_LAYER_STATE_COMBINE_CONSTANT,
+ layer0, layer1,
+ _cogl_material_layer_combine_constant_equal))
+ return FALSE;
+
+ if (layers_difference & COGL_MATERIAL_LAYER_STATE_FILTERS &&
+ !layer_state_equal (COGL_MATERIAL_LAYER_STATE_FILTERS,
+ layer0, layer1,
+ _cogl_material_layer_filters_equal))
+ return FALSE;
+
+ if (layers_difference & COGL_MATERIAL_LAYER_STATE_WRAP_MODES &&
+ !layer_state_equal (COGL_MATERIAL_LAYER_STATE_WRAP_MODES,
+ layer0, layer1,
+ _cogl_material_layer_wrap_modes_equal))
+ return FALSE;
+
+ if (layers_difference & COGL_MATERIAL_LAYER_STATE_USER_MATRIX &&
+ !layer_state_equal (COGL_MATERIAL_LAYER_STATE_USER_MATRIX,
+ layer0, layer1,
+ _cogl_material_layer_user_matrix_equal))
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+_cogl_material_color_equal (CoglMaterial *authority0,
+ CoglMaterial *authority1)
+{
+ return cogl_color_equal (&authority0->color, &authority1->color);
+}
+
+static gboolean
+_cogl_material_lighting_state_equal (CoglMaterial *authority0,
+ CoglMaterial *authority1)
+{
+ CoglMaterialLightingState *state0 = &authority0->big_state->lighting_state;
+ CoglMaterialLightingState *state1 = &authority1->big_state->lighting_state;
+
+ if (memcmp (state0->ambient, state1->ambient, sizeof (float) * 4) != 0)
+ return FALSE;
+ if (memcmp (state0->diffuse, state1->diffuse, sizeof (float) * 4) != 0)
+ return FALSE;
+ if (memcmp (state0->specular, state1->specular, sizeof (float) * 4) != 0)
+ return FALSE;
+ if (memcmp (state0->emission, state1->emission, sizeof (float) * 4) != 0)
+ return FALSE;
+ if (state0->shininess != state1->shininess)
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+_cogl_material_alpha_state_equal (CoglMaterial *authority0,
+ CoglMaterial *authority1)
+{
+ CoglMaterialAlphaFuncState *alpha_state0 =
+ &authority0->big_state->alpha_state;
+ CoglMaterialAlphaFuncState *alpha_state1 =
+ &authority1->big_state->alpha_state;
+
+ if (alpha_state0->alpha_func != alpha_state1->alpha_func ||
+ alpha_state0->alpha_func_reference != alpha_state1->alpha_func_reference)
+ return FALSE;
+ else
+ return TRUE;
+}
+
+static gboolean
+_cogl_material_blend_state_equal (CoglMaterial *authority0,
+ CoglMaterial *authority1)
+{
+ CoglMaterialBlendState *blend_state0 = &authority0->big_state->blend_state;
+ CoglMaterialBlendState *blend_state1 = &authority1->big_state->blend_state;
+
+#ifndef HAVE_COGL_GLES
+ if (blend_state0->blend_equation_rgb != blend_state1->blend_equation_rgb)
+ return FALSE;
+ if (blend_state0->blend_equation_alpha !=
+ blend_state1->blend_equation_alpha)
+ return FALSE;
+ if (blend_state0->blend_src_factor_alpha !=
+ blend_state1->blend_src_factor_alpha)
+ return FALSE;
+ if (blend_state0->blend_dst_factor_alpha !=
+ blend_state1->blend_dst_factor_alpha)
+ return FALSE;
+#endif
+ if (blend_state0->blend_src_factor_rgb !=
+ blend_state1->blend_src_factor_rgb)
+ return FALSE;
+ if (blend_state0->blend_dst_factor_rgb !=
+ blend_state1->blend_dst_factor_rgb)
+ return FALSE;
+#ifndef HAVE_COGL_GLES
+ if (!cogl_color_equal (&blend_state0->blend_constant,
+ &blend_state1->blend_constant))
+ return FALSE;
+#endif
+
+ return TRUE;
+}
+
+static gboolean
+_cogl_material_layers_equal (CoglMaterial *authority0,
+ CoglMaterial *authority1)
+{
+ int i;
+
+ if (authority0->n_layers != authority1->n_layers)
+ return FALSE;
+
+ _cogl_material_update_layers_cache (authority0);
+ _cogl_material_update_layers_cache (authority1);
+
+ for (i = 0; i < authority0->n_layers; i++)
+ {
+ if (!_cogl_material_layer_equal (authority0->layers_cache[i],
+ authority1->layers_cache[i]))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* Determine the mask of differences between two materials */
+static unsigned long
+_cogl_material_compare_differences (CoglMaterial *material0,
+ CoglMaterial *material1)
+{
+ CoglMaterial *node0;
+ CoglMaterial *node1;
+ int len0;
+ int len1;
+ int len0_index;
+ int len1_index;
+ int count;
+ int i;
+ CoglMaterial *common_ancestor = NULL;
+ unsigned long materials_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->material0_nodes, and ctx->material1_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
+ * material_differences mask.
+ */
+
+ g_array_set_size (ctx->material0_nodes, 0);
+ g_array_set_size (ctx->material1_nodes, 0);
+ for (node0 = material0; node0; node0 = node0->parent)
+ g_array_append_vals (ctx->material0_nodes, &node0, 1);
+ for (node1 = material1; node1; node1 = node1->parent)
+ g_array_append_vals (ctx->material1_nodes, &node1, 1);
+
+ len0 = ctx->material0_nodes->len;
+ len1 = ctx->material1_nodes->len;
+ /* There's no point looking at the last entries since we know both
+ * layers must have the same default layer as their root node. */
+ len0_index = len0 - 2;
+ len1_index = len1 - 2;
+ count = MIN (len0, len1) - 1;
+ for (i = 0; i < count; i++)
+ {
+ node0 = g_array_index (ctx->material0_nodes,
+ CoglMaterial *, len0_index--);
+ node1 = g_array_index (ctx->material1_nodes,
+ CoglMaterial *, len1_index--);
+ if (node0 != node1)
+ {
+ common_ancestor = node0->parent;
+ break;
+ }
+ }
+
+ /* If we didn't already find the first the common_ancestor ancestor
+ * that's because one material is a direct descendant of the other
+ * and in this case the first common ancestor is the last node we
+ * looked at. */
+ if (!common_ancestor)
+ common_ancestor = node0;
+
+ count = len0 - 1;
+ for (i = 0; i < count; i++)
+ {
+ node0 = g_array_index (ctx->material0_nodes, CoglMaterial *, i);
+ if (node0 == common_ancestor)
+ break;
+ materials_difference |= node0->differences;
+ }
+
+ count = len1 - 1;
+ for (i = 0; i < count; i++)
+ {
+ node1 = g_array_index (ctx->material1_nodes, CoglMaterial *, i);
+ if (node1 == common_ancestor)
+ break;
+ materials_difference |= node1->differences;
+ }
+
+ return materials_difference;
+
+}
+
+/* Comparison of two arbitrary materials is done by:
+ * 1) walking up the parents of each material until a common
+ * ancestor is found, and at each step ORing together the
+ * difference masks.
+ *
+ * 2) using the final difference mask to determine which state
+ * groups to compare.
+ *
+ * This is used by the Cogl journal to compare materials so that it
+ * can split up geometry that needs different OpenGL state.
+ *
+ * It is acceptable to have false negatives - although they will result
+ * in redundant OpenGL calls that try and update the state.
+ *
+ * False positives aren't allowed.
+ */
+gboolean
+_cogl_material_equal (CoglHandle material0_handle,
+ CoglMaterialFlushOptions *material0_flush_options,
+ CoglHandle material1_handle,
+ CoglMaterialFlushOptions *material1_flush_options,
+ gboolean skip_gl_color)
+{
+ CoglMaterial *material0;
+ CoglMaterial *material1;
+ gboolean material0_overridden = FALSE;
+ gboolean material1_overridden = FALSE;
+ unsigned long materials_difference;
+ gboolean equal = FALSE;
+
+ if (material0_flush_options->flags)
+ {
+ /* XXX: we really need to get rid of overrides! */
+ material0 = cogl_material_copy (material0_handle);
+ _cogl_material_apply_overrides (material0, material0_flush_options);
+ material0_overridden = TRUE;
+ }
+ else
+ material0 = COGL_MATERIAL (material0_handle);
+
+ if (material1_flush_options->flags)
+ {
+ /* XXX: we really need to get rid of overrides! */
+ material1 = cogl_material_copy (material1_handle);
+ _cogl_material_apply_overrides (material1, material1_flush_options);
+ material1_overridden = TRUE;
+ }
+ else
+ material1 = COGL_MATERIAL (material1_handle);
+
+ if (material0 == material1)
+ goto done;
+
+ /* First check non-sparse properties */
+
+ if (material0->real_blend_enable != material1->real_blend_enable)
+ goto done;
+
+ /* Then check sparse properties */
+
+ materials_difference =
+ _cogl_material_compare_differences (material0, material1);
+
+ if (materials_difference & COGL_MATERIAL_STATE_COLOR &&
+ !skip_gl_color)
+ {
+ CoglMaterial *authority0 =
+ _cogl_material_get_authority (material0,
+ COGL_MATERIAL_STATE_COLOR);
+ CoglMaterial *authority1 =
+ _cogl_material_get_authority (material1,
+ COGL_MATERIAL_STATE_COLOR);
+
+ if (!cogl_color_equal (&authority0->color, &authority1->color))
+ goto done;
+ }
+
+ if (materials_difference & COGL_MATERIAL_STATE_LIGHTING)
+ {
+ CoglMaterial *authority0 =
+ _cogl_material_get_authority (material0,
+ COGL_MATERIAL_STATE_LIGHTING);
+ CoglMaterial *authority1 =
+ _cogl_material_get_authority (material1,
+ COGL_MATERIAL_STATE_LIGHTING);
+
+ if (!_cogl_material_lighting_state_equal (authority0, authority1))
+ goto done;
+ }
+
+ if (materials_difference & COGL_MATERIAL_STATE_ALPHA_FUNC)
+ {
+ CoglMaterial *authority0 =
+ _cogl_material_get_authority (material0,
+ COGL_MATERIAL_STATE_ALPHA_FUNC);
+ CoglMaterial *authority1 =
+ _cogl_material_get_authority (material1,
+ COGL_MATERIAL_STATE_ALPHA_FUNC);
+
+ if (!_cogl_material_alpha_state_equal (authority0, authority1))
+ goto done;
+ }
+
+ if (materials_difference & COGL_MATERIAL_STATE_BLEND)
+ {
+ CoglMaterial *authority0 =
+ _cogl_material_get_authority (material0,
+ COGL_MATERIAL_STATE_BLEND);
+ CoglMaterial *authority1 =
+ _cogl_material_get_authority (material1,
+ COGL_MATERIAL_STATE_BLEND);
+
+ if (!_cogl_material_blend_state_equal (authority0, authority1))
+ goto done;
+ }
+
+ if (materials_difference & COGL_MATERIAL_STATE_BLEND_ENABLE)
+ {
+ CoglMaterial *authority0 =
+ _cogl_material_get_authority (material0,
+ COGL_MATERIAL_STATE_BLEND_ENABLE);
+ CoglMaterial *authority1 =
+ _cogl_material_get_authority (material1,
+ COGL_MATERIAL_STATE_BLEND_ENABLE);
+
+ if (authority0->blend_enable != authority1->blend_enable)
+ goto done;
+ }
+
+ if (materials_difference & COGL_MATERIAL_STATE_LAYERS)
+ {
+ CoglMaterial *authority0 =
+ _cogl_material_get_authority (material0,
+ COGL_MATERIAL_STATE_LAYERS);
+ CoglMaterial *authority1 =
+ _cogl_material_get_authority (material1,
+ COGL_MATERIAL_STATE_LAYERS);
+
+ if (!_cogl_material_layers_equal (authority0, authority1))
+ goto done;
+ }
+
+ equal = TRUE;
+
+done:
+
+ if (material0_overridden)
+ cogl_handle_unref (material0);
+ if (material1_overridden)
+ cogl_handle_unref (material1);
+
+ return equal;
}
void
cogl_material_get_color (CoglHandle handle,
CoglColor *color)
{
- CoglMaterial *material;
+ CoglMaterial *material = COGL_MATERIAL (handle);
+ CoglMaterial *authority;
g_return_if_fail (cogl_is_material (handle));
- material = _cogl_material_pointer_from_handle (handle);
+ authority =
+ _cogl_material_get_authority (material, COGL_MATERIAL_STATE_COLOR);
- cogl_color_set_from_4ub (color,
- material->unlit[0],
- material->unlit[1],
- material->unlit[2],
- material->unlit[3]);
+ *color = authority->color;
}
/* This is used heavily by the cogl journal when logging quads */
@@ -430,42 +3162,91 @@ void
_cogl_material_get_colorubv (CoglHandle handle,
guint8 *color)
{
- CoglMaterial *material = _cogl_material_pointer_from_handle (handle);
- memcpy (color, material->unlit, 4);
+ CoglMaterial *material = COGL_MATERIAL (handle);
+ CoglMaterial *authority =
+ _cogl_material_get_authority (material, COGL_MATERIAL_STATE_COLOR);
+
+ _cogl_color_get_rgba_4ubv (&authority->color, color);
+}
+
+static void
+_cogl_material_prune_redundant_ancestry (CoglMaterial *material)
+{
+ CoglMaterial *new_parent = material->parent;
+
+ /* walk up past ancestors that are now redundant and potentially
+ * reparent the material. */
+ while (new_parent->parent &&
+ (new_parent->differences | material->differences) ==
+ material->differences)
+ new_parent = new_parent->parent;
+
+ if (new_parent != material->parent)
+ {
+ CoglMaterial *old_parent = material->parent;
+ material->parent = cogl_handle_ref (new_parent);
+ /* Note: the old parent may indirectly be keeping
+ the new parent alive so we have to ref the new
+ parent before unrefing the old */
+ cogl_handle_unref (old_parent);
+ }
+}
+
+static void
+_cogl_material_update_authority (CoglMaterial *material,
+ CoglMaterial *authority,
+ CoglMaterialState state,
+ CoglMaterialStateComparitor comparitor)
+{
+ /* If we are the current authority see if we can revert to one of
+ * our ancestors being the authority */
+ if (material == authority && authority->parent != NULL)
+ {
+ CoglMaterial *old_authority =
+ _cogl_material_get_authority (authority->parent, state);
+
+ if (comparitor (authority, old_authority))
+ material->differences &= ~state;
+ }
+ else if (material != authority)
+ {
+ /* 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... */
+ material->differences |= state;
+ _cogl_material_prune_redundant_ancestry (material);
+ }
}
void
cogl_material_set_color (CoglHandle handle,
- const CoglColor *unlit_color)
+ const CoglColor *color)
{
- CoglMaterial *material;
- GLubyte unlit[4];
+ CoglMaterial *material = COGL_MATERIAL (handle);
+ CoglMaterialState state = COGL_MATERIAL_STATE_COLOR;
+ CoglMaterial *authority;
g_return_if_fail (cogl_is_material (handle));
- material = _cogl_material_pointer_from_handle (handle);
+ authority = _cogl_material_get_authority (material, state);
- unlit[0] = cogl_color_get_red_byte (unlit_color);
- unlit[1] = cogl_color_get_green_byte (unlit_color);
- unlit[2] = cogl_color_get_blue_byte (unlit_color);
- unlit[3] = cogl_color_get_alpha_byte (unlit_color);
- if (memcmp (unlit, material->unlit, sizeof (unlit)) == 0)
+ if (cogl_color_equal (color, &authority->color))
return;
- /* possibly flush primitives referencing the current state... */
- _cogl_material_pre_change_notify (material, COGL_MATERIAL_CHANGE_COLOR,
- unlit);
+ /* - Flush journal primitives referencing the current state.
+ * - Make sure the material has no dependants so it may be modified.
+ * - If the material isn't currently an authority for the state being
+ * changed, then initialize that state from the current authority.
+ */
+ _cogl_material_pre_change_notify (material, state, color);
- memcpy (material->unlit, unlit, sizeof (unlit));
+ material->color = *color;
- material->flags &= ~COGL_MATERIAL_FLAG_DEFAULT_COLOR;
- if (unlit[0] == 0xff &&
- unlit[1] == 0xff &&
- unlit[2] == 0xff &&
- unlit[3] == 0xff)
- material->flags |= COGL_MATERIAL_FLAG_DEFAULT_COLOR;
+ _cogl_material_update_authority (material, authority, state,
+ _cogl_material_color_equal);
- handle_automatic_blend_enable (material);
+ handle_automatic_blend_enable (material, state);
}
void
@@ -492,92 +3273,161 @@ cogl_material_set_color4f (CoglHandle handle,
cogl_material_set_color (handle, &color);
}
+CoglMaterialBlendEnable
+_cogl_material_get_blend_enabled (CoglHandle handle)
+{
+ CoglMaterial *material = COGL_MATERIAL (handle);;
+ CoglMaterial *authority;
+
+ g_return_val_if_fail (cogl_is_material (handle), FALSE);
+
+ authority =
+ _cogl_material_get_authority (material, COGL_MATERIAL_STATE_BLEND_ENABLE);
+ return authority->blend_enable;
+}
+
+static gboolean
+_cogl_material_blend_enable_equal (CoglMaterial *authority0,
+ CoglMaterial *authority1)
+{
+ return authority0->blend_enable == authority1->blend_enable ? TRUE : FALSE;
+}
+
+void
+_cogl_material_set_blend_enabled (CoglHandle handle,
+ CoglMaterialBlendEnable enable)
+{
+ CoglMaterial *material = COGL_MATERIAL (handle);
+ CoglMaterialState state = COGL_MATERIAL_STATE_BLEND_ENABLE;
+ CoglMaterial *authority;
+
+ g_return_if_fail (cogl_is_material (handle));
+ g_return_if_fail (enable > 1 &&
+ "don't pass TRUE or FALSE to _set_blend_enabled!");
+
+ authority = _cogl_material_get_authority (material, state);
+
+ if (authority->blend_enable == enable)
+ return;
+
+ /* - Flush journal primitives referencing the current state.
+ * - Make sure the material has no dependants so it may be modified.
+ * - If the material isn't currently an authority for the state being
+ * changed, then initialize that state from the current authority.
+ */
+ _cogl_material_pre_change_notify (material, state, NULL);
+
+ material->blend_enable = enable;
+
+ _cogl_material_update_authority (material, authority, state,
+ _cogl_material_blend_enable_equal);
+
+ handle_automatic_blend_enable (material, state);
+}
+
void
cogl_material_get_ambient (CoglHandle handle,
CoglColor *ambient)
{
- CoglMaterial *material;
+ CoglMaterial *material = COGL_MATERIAL (handle);
+ CoglMaterial *authority;
g_return_if_fail (cogl_is_material (handle));
- material = _cogl_material_pointer_from_handle (handle);
+ authority =
+ _cogl_material_get_authority (material, COGL_MATERIAL_STATE_LIGHTING);
- cogl_color_set_from_4f (ambient,
- material->ambient[0],
- material->ambient[1],
- material->ambient[2],
- material->ambient[3]);
+ cogl_color_init_from_4fv (ambient,
+ authority->big_state->lighting_state.ambient);
}
void
cogl_material_set_ambient (CoglHandle handle,
- const CoglColor *ambient_color)
+ const CoglColor *ambient)
{
- CoglMaterial *material;
- GLfloat *ambient;
+ CoglMaterial *material = COGL_MATERIAL (handle);
+ CoglMaterialState state = COGL_MATERIAL_STATE_LIGHTING;
+ CoglMaterial *authority;
+ CoglMaterialLightingState *lighting_state;
g_return_if_fail (cogl_is_material (handle));
- material = _cogl_material_pointer_from_handle (handle);
+ authority = _cogl_material_get_authority (material, state);
- /* possibly flush primitives referencing the current state... */
- _cogl_material_pre_change_notify (material,
- COGL_MATERIAL_CHANGE_GL_MATERIAL,
- NULL);
+ lighting_state = &authority->big_state->lighting_state;
+ if (cogl_color_equal (ambient, &lighting_state->ambient))
+ return;
- ambient = material->ambient;
- ambient[0] = cogl_color_get_red_float (ambient_color);
- ambient[1] = cogl_color_get_green_float (ambient_color);
- ambient[2] = cogl_color_get_blue_float (ambient_color);
- ambient[3] = cogl_color_get_alpha_float (ambient_color);
+ /* - Flush journal primitives referencing the current state.
+ * - Make sure the material has no dependants so it may be modified.
+ * - If the material isn't currently an authority for the state being
+ * changed, then initialize that state from the current authority.
+ */
+ _cogl_material_pre_change_notify (material, state, NULL);
- material->flags &= ~COGL_MATERIAL_FLAG_DEFAULT_GL_MATERIAL;
+ lighting_state = &material->big_state->lighting_state;
+ lighting_state->ambient[0] = cogl_color_get_red_float (ambient);
+ lighting_state->ambient[1] = cogl_color_get_green_float (ambient);
+ lighting_state->ambient[2] = cogl_color_get_blue_float (ambient);
+ lighting_state->ambient[3] = cogl_color_get_alpha_float (ambient);
- handle_automatic_blend_enable (material);
+ _cogl_material_update_authority (material, authority, state,
+ _cogl_material_lighting_state_equal);
+
+ handle_automatic_blend_enable (material, state);
}
void
cogl_material_get_diffuse (CoglHandle handle,
CoglColor *diffuse)
{
- CoglMaterial *material;
+ CoglMaterial *material = COGL_MATERIAL (handle);
+ CoglMaterial *authority;
g_return_if_fail (cogl_is_material (handle));
- material = _cogl_material_pointer_from_handle (handle);
+ authority =
+ _cogl_material_get_authority (material, COGL_MATERIAL_STATE_LIGHTING);
- cogl_color_set_from_4f (diffuse,
- material->diffuse[0],
- material->diffuse[1],
- material->diffuse[2],
- material->diffuse[3]);
+ cogl_color_init_from_4fv (diffuse,
+ authority->big_state->lighting_state.diffuse);
}
void
cogl_material_set_diffuse (CoglHandle handle,
- const CoglColor *diffuse_color)
+ const CoglColor *diffuse)
{
- CoglMaterial *material;
- GLfloat *diffuse;
+ CoglMaterial *material = COGL_MATERIAL (handle);
+ CoglMaterialState state = COGL_MATERIAL_STATE_LIGHTING;
+ CoglMaterial *authority;
+ CoglMaterialLightingState *lighting_state;
g_return_if_fail (cogl_is_material (handle));
- material = _cogl_material_pointer_from_handle (handle);
+ authority = _cogl_material_get_authority (material, state);
- /* possibly flush primitives referencing the current state... */
- _cogl_material_pre_change_notify (material,
- COGL_MATERIAL_CHANGE_GL_MATERIAL,
- NULL);
+ lighting_state = &authority->big_state->lighting_state;
+ if (cogl_color_equal (diffuse, &lighting_state->diffuse))
+ return;
- diffuse = material->diffuse;
- diffuse[0] = cogl_color_get_red_float (diffuse_color);
- diffuse[1] = cogl_color_get_green_float (diffuse_color);
- diffuse[2] = cogl_color_get_blue_float (diffuse_color);
- diffuse[3] = cogl_color_get_alpha_float (diffuse_color);
+ /* - Flush journal primitives referencing the current state.
+ * - Make sure the material has no dependants so it may be modified.
+ * - If the material isn't currently an authority for the state being
+ * changed, then initialize that state from the current authority.
+ */
+ _cogl_material_pre_change_notify (material, state, NULL);
- material->flags &= ~COGL_MATERIAL_FLAG_DEFAULT_GL_MATERIAL;
+ lighting_state = &material->big_state->lighting_state;
+ lighting_state->diffuse[0] = cogl_color_get_red_float (diffuse);
+ lighting_state->diffuse[1] = cogl_color_get_green_float (diffuse);
+ lighting_state->diffuse[2] = cogl_color_get_blue_float (diffuse);
+ lighting_state->diffuse[3] = cogl_color_get_alpha_float (diffuse);
- handle_automatic_blend_enable (material);
+
+ _cogl_material_update_authority (material, authority, state,
+ _cogl_material_lighting_state_equal);
+
+ handle_automatic_blend_enable (material, state);
}
void
@@ -592,124 +3442,154 @@ void
cogl_material_get_specular (CoglHandle handle,
CoglColor *specular)
{
- CoglMaterial *material;
+ CoglMaterial *authority = COGL_MATERIAL (handle);
g_return_if_fail (cogl_is_material (handle));
- material = _cogl_material_pointer_from_handle (handle);
+ while (!(authority->differences & COGL_MATERIAL_STATE_LIGHTING))
+ authority = authority->parent;
- cogl_color_set_from_4f (specular,
- material->specular[0],
- material->specular[1],
- material->specular[2],
- material->specular[3]);
+ cogl_color_init_from_4fv (specular,
+ authority->big_state->lighting_state.specular);
}
void
-cogl_material_set_specular (CoglHandle handle,
- const CoglColor *specular_color)
+cogl_material_set_specular (CoglHandle handle, const CoglColor *specular)
{
- CoglMaterial *material;
- GLfloat *specular;
+ CoglMaterial *material = COGL_MATERIAL (handle);
+ CoglMaterial *authority;
+ CoglMaterialState state = COGL_MATERIAL_STATE_LIGHTING;
+ CoglMaterialLightingState *lighting_state;
g_return_if_fail (cogl_is_material (handle));
- material = _cogl_material_pointer_from_handle (handle);
+ authority = _cogl_material_get_authority (material, state);
- /* possibly flush primitives referencing the current state... */
- _cogl_material_pre_change_notify (material,
- COGL_MATERIAL_CHANGE_GL_MATERIAL,
- NULL);
+ lighting_state = &authority->big_state->lighting_state;
+ if (cogl_color_equal (specular, &lighting_state->specular))
+ return;
- specular = material->specular;
- specular[0] = cogl_color_get_red_float (specular_color);
- specular[1] = cogl_color_get_green_float (specular_color);
- specular[2] = cogl_color_get_blue_float (specular_color);
- specular[3] = cogl_color_get_alpha_float (specular_color);
+ /* - Flush journal primitives referencing the current state.
+ * - Make sure the material has no dependants so it may be modified.
+ * - If the material isn't currently an authority for the state being
+ * changed, then initialize that state from the current authority.
+ */
+ _cogl_material_pre_change_notify (material, state, NULL);
- material->flags &= ~COGL_MATERIAL_FLAG_DEFAULT_GL_MATERIAL;
+ lighting_state = &material->big_state->lighting_state;
+ lighting_state->specular[0] = cogl_color_get_red_float (specular);
+ lighting_state->specular[1] = cogl_color_get_green_float (specular);
+ lighting_state->specular[2] = cogl_color_get_blue_float (specular);
+ lighting_state->specular[3] = cogl_color_get_alpha_float (specular);
- handle_automatic_blend_enable (material);
+ _cogl_material_update_authority (material, authority, state,
+ _cogl_material_lighting_state_equal);
+
+ handle_automatic_blend_enable (material, state);
}
float
cogl_material_get_shininess (CoglHandle handle)
{
- CoglMaterial *material;
+ CoglMaterial *material = COGL_MATERIAL (handle);
+ CoglMaterial *authority;
g_return_val_if_fail (cogl_is_material (handle), 0);
- material = _cogl_material_pointer_from_handle (handle);
+ authority =
+ _cogl_material_get_authority (material, COGL_MATERIAL_STATE_LIGHTING);
- return material->shininess;
+ return authority->big_state->lighting_state.shininess;
}
void
cogl_material_set_shininess (CoglHandle handle,
float shininess)
{
- CoglMaterial *material;
+ CoglMaterial *material = COGL_MATERIAL (handle);
+ CoglMaterial *authority;
+ CoglMaterialState state = COGL_MATERIAL_STATE_LIGHTING;
+ CoglMaterialLightingState *lighting_state;
g_return_if_fail (cogl_is_material (handle));
if (shininess < 0.0 || shininess > 1.0)
- g_warning ("Out of range shininess %f supplied for material\n",
- shininess);
+ {
+ g_warning ("Out of range shininess %f supplied for material\n",
+ shininess);
+ return;
+ }
- material = _cogl_material_pointer_from_handle (handle);
+ authority = _cogl_material_get_authority (material, state);
- /* possibly flush primitives referencing the current state... */
- _cogl_material_pre_change_notify (material,
- COGL_MATERIAL_CHANGE_GL_MATERIAL,
- NULL);
+ lighting_state = &authority->big_state->lighting_state;
- material->shininess = (GLfloat)shininess * 128.0;
+ if (lighting_state->shininess == shininess)
+ return;
- material->flags &= ~COGL_MATERIAL_FLAG_DEFAULT_GL_MATERIAL;
+ /* - Flush journal primitives referencing the current state.
+ * - Make sure the material has no dependants so it may be modified.
+ * - If the material isn't currently an authority for the state being
+ * changed, then initialize that state from the current authority.
+ */
+ _cogl_material_pre_change_notify (material, state, NULL);
+
+ lighting_state = &material->big_state->lighting_state;
+ lighting_state->shininess = shininess;
+
+ _cogl_material_update_authority (material, authority, state,
+ _cogl_material_lighting_state_equal);
}
void
cogl_material_get_emission (CoglHandle handle,
CoglColor *emission)
{
- CoglMaterial *material;
+ CoglMaterial *material = COGL_MATERIAL (handle);
+ CoglMaterial *authority;
g_return_if_fail (cogl_is_material (handle));
- material = _cogl_material_pointer_from_handle (handle);
+ authority =
+ _cogl_material_get_authority (material, COGL_MATERIAL_STATE_LIGHTING);
- cogl_color_set_from_4f (emission,
- material->emission[0],
- material->emission[1],
- material->emission[2],
- material->emission[3]);
+ cogl_color_init_from_4fv (emission,
+ authority->big_state->lighting_state.emission);
}
void
-cogl_material_set_emission (CoglHandle handle,
- const CoglColor *emission_color)
+cogl_material_set_emission (CoglHandle handle, const CoglColor *emission)
{
- CoglMaterial *material;
- GLfloat *emission;
+ CoglMaterial *material = COGL_MATERIAL (handle);
+ CoglMaterial *authority;
+ CoglMaterialState state = COGL_MATERIAL_STATE_LIGHTING;
+ CoglMaterialLightingState *lighting_state;
g_return_if_fail (cogl_is_material (handle));
- material = _cogl_material_pointer_from_handle (handle);
+ authority = _cogl_material_get_authority (material, state);
- /* possibly flush primitives referencing the current state... */
- _cogl_material_pre_change_notify (material,
- COGL_MATERIAL_CHANGE_GL_MATERIAL,
- NULL);
+ lighting_state = &authority->big_state->lighting_state;
+ if (cogl_color_equal (emission, &lighting_state->emission))
+ return;
- emission = material->emission;
- emission[0] = cogl_color_get_red_float (emission_color);
- emission[1] = cogl_color_get_green_float (emission_color);
- emission[2] = cogl_color_get_blue_float (emission_color);
- emission[3] = cogl_color_get_alpha_float (emission_color);
+ /* - Flush journal primitives referencing the current state.
+ * - Make sure the material has no dependants so it may be modified.
+ * - If the material isn't currently an authority for the state being
+ * changed, then initialize that state from the current authority.
+ */
+ _cogl_material_pre_change_notify (material, state, NULL);
- material->flags &= ~COGL_MATERIAL_FLAG_DEFAULT_GL_MATERIAL;
+ lighting_state = &material->big_state->lighting_state;
+ lighting_state->emission[0] = cogl_color_get_red_float (emission);
+ lighting_state->emission[1] = cogl_color_get_green_float (emission);
+ lighting_state->emission[2] = cogl_color_get_blue_float (emission);
+ lighting_state->emission[3] = cogl_color_get_alpha_float (emission);
- handle_automatic_blend_enable (material);
+ _cogl_material_update_authority (material, authority, state,
+ _cogl_material_lighting_state_equal);
+
+ handle_automatic_blend_enable (material, state);
}
void
@@ -717,21 +3597,33 @@ cogl_material_set_alpha_test_function (CoglHandle handle,
CoglMaterialAlphaFunc alpha_func,
float alpha_reference)
{
- CoglMaterial *material;
+ CoglMaterial *material = COGL_MATERIAL (handle);
+ CoglMaterialState state = COGL_MATERIAL_STATE_ALPHA_FUNC;
+ CoglMaterial *authority;
+ CoglMaterialAlphaFuncState *alpha_state;
g_return_if_fail (cogl_is_material (handle));
- material = _cogl_material_pointer_from_handle (handle);
+ authority = _cogl_material_get_authority (material, state);
- /* possibly flush primitives referencing the current state... */
- _cogl_material_pre_change_notify (material,
- COGL_MATERIAL_CHANGE_ALPHA_FUNC,
- NULL);
+ alpha_state = &authority->big_state->alpha_state;
+ if (alpha_state->alpha_func == alpha_func &&
+ alpha_state->alpha_func_reference == alpha_reference)
+ return;
- material->alpha_func = alpha_func;
- material->alpha_func_reference = (GLfloat)alpha_reference;
+ /* - Flush journal primitives referencing the current state.
+ * - Make sure the material has no dependants so it may be modified.
+ * - If the material isn't currently an authority for the state being
+ * changed, then initialize that state from the current authority.
+ */
+ _cogl_material_pre_change_notify (material, state, NULL);
- material->flags &= ~COGL_MATERIAL_FLAG_DEFAULT_ALPHA_FUNC;
+ alpha_state = &material->big_state->alpha_state;
+ alpha_state->alpha_func = alpha_func;
+ alpha_state->alpha_func_reference = alpha_reference;
+
+ _cogl_material_update_authority (material, authority, state,
+ _cogl_material_alpha_state_equal);
}
GLenum
@@ -832,17 +3724,18 @@ cogl_material_set_blend (CoglHandle handle,
const char *blend_description,
GError **error)
{
- CoglMaterial *material;
+ CoglMaterial *material = COGL_MATERIAL (handle);
+ CoglMaterialState state = COGL_MATERIAL_STATE_BLEND;
+ CoglMaterial *authority;
CoglBlendStringStatement statements[2];
CoglBlendStringStatement *rgb;
CoglBlendStringStatement *a;
GError *internal_error = NULL;
int count;
+ CoglMaterialBlendState *blend_state;
g_return_val_if_fail (cogl_is_material (handle), FALSE);
- material = _cogl_material_pointer_from_handle (handle);
-
count =
_cogl_blend_string_compile (blend_description,
COGL_BLEND_STRING_CONTEXT_BLENDING,
@@ -869,28 +3762,55 @@ cogl_material_set_blend (CoglHandle handle,
a = &statements[1];
}
- /* possibly flush primitives referencing the current state... */
- _cogl_material_pre_change_notify (material,
- COGL_MATERIAL_CHANGE_BLEND,
- NULL);
+ authority =
+ _cogl_material_get_authority (material, state);
+ /* - Flush journal primitives referencing the current state.
+ * - Make sure the material has no dependants so it may be modified.
+ * - If the material isn't currently an authority for the state being
+ * changed, then initialize that state from the current authority.
+ */
+ _cogl_material_pre_change_notify (material, state, NULL);
+
+ blend_state = &material->big_state->blend_state;
#ifndef HAVE_COGL_GLES
setup_blend_state (rgb,
- &material->blend_equation_rgb,
- &material->blend_src_factor_rgb,
- &material->blend_dst_factor_rgb);
+ &blend_state->blend_equation_rgb,
+ &blend_state->blend_src_factor_rgb,
+ &blend_state->blend_dst_factor_rgb);
setup_blend_state (a,
- &material->blend_equation_alpha,
- &material->blend_src_factor_alpha,
- &material->blend_dst_factor_alpha);
+ &blend_state->blend_equation_alpha,
+ &blend_state->blend_src_factor_alpha,
+ &blend_state->blend_dst_factor_alpha);
#else
setup_blend_state (rgb,
NULL,
- &material->blend_src_factor_rgb,
- &material->blend_dst_factor_rgb);
+ &blend_state->blend_src_factor_rgb,
+ &blend_state->blend_dst_factor_rgb);
#endif
- material->flags &= ~COGL_MATERIAL_FLAG_DEFAULT_BLEND;
+ /* If we are the current authority see if we can revert to one of our
+ * ancestors being the authority */
+ if (material == authority && authority->parent != NULL)
+ {
+ CoglMaterial *old_authority =
+ _cogl_material_get_authority (authority->parent, state);
+
+ if (_cogl_material_blend_state_equal (authority, old_authority))
+ material->differences &= ~state;
+ }
+
+ /* 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 (material != authority)
+ {
+ material->differences |= state;
+ _cogl_material_prune_redundant_ancestry (material);
+ }
+
+ handle_automatic_blend_enable (material, state);
return TRUE;
}
@@ -900,25 +3820,33 @@ cogl_material_set_blend_constant (CoglHandle handle,
CoglColor *constant_color)
{
#ifndef HAVE_COGL_GLES
- CoglMaterial *material;
- GLfloat *constant;
+ CoglMaterial *material = COGL_MATERIAL (handle);
+ CoglMaterialState state = COGL_MATERIAL_STATE_BLEND;
+ CoglMaterial *authority;
+ CoglMaterialBlendState *blend_state;
g_return_if_fail (cogl_is_material (handle));
- material = _cogl_material_pointer_from_handle (handle);
+ authority = _cogl_material_get_authority (material, state);
- /* possibly flush primitives referencing the current state... */
- _cogl_material_pre_change_notify (material,
- COGL_MATERIAL_CHANGE_BLEND,
- NULL);
+ blend_state = &authority->big_state->blend_state;
+ if (cogl_color_equal (constant_color, &blend_state->blend_constant))
+ return;
- constant = material->blend_constant;
- constant[0] = cogl_color_get_red_float (constant_color);
- constant[1] = cogl_color_get_green_float (constant_color);
- constant[2] = cogl_color_get_blue_float (constant_color);
- constant[3] = cogl_color_get_alpha_float (constant_color);
+ /* - Flush journal primitives referencing the current state.
+ * - Make sure the material has no dependants so it may be modified.
+ * - If the material isn't currently an authority for the state being
+ * changed, then initialize that state from the current authority.
+ */
+ _cogl_material_pre_change_notify (material, state, NULL);
- material->flags &= ~COGL_MATERIAL_FLAG_DEFAULT_BLEND;
+ blend_state = &material->big_state->blend_state;
+ blend_state->blend_constant = *constant_color;
+
+ _cogl_material_update_authority (material, authority, state,
+ _cogl_material_blend_state_equal);
+
+ handle_automatic_blend_enable (material, state);
#endif
}
@@ -932,284 +3860,251 @@ void
_cogl_material_set_user_program (CoglHandle handle,
CoglHandle program)
{
- CoglMaterial *material;
+ CoglMaterial *material = COGL_MATERIAL (handle);
+ CoglMaterialState state = COGL_MATERIAL_STATE_USER_SHADER;
+ CoglMaterial *authority;
g_return_if_fail (cogl_is_material (handle));
- material = _cogl_material_pointer_from_handle (handle);
+ authority = _cogl_material_get_authority (material, state);
- if (material->user_program == program)
+ if (authority->big_state->user_program == program)
return;
- /* possibly flush primitives referencing the current state... */
- _cogl_material_pre_change_notify (material,
- COGL_MATERIAL_CHANGE_USER_SHADER,
- NULL);
+ /* - Flush journal primitives referencing the current state.
+ * - Make sure the material has no dependants so it may be modified.
+ * - If the material isn't currently an authority for the state being
+ * changed, then initialize that state from the current authority.
+ */
+ _cogl_material_pre_change_notify (material, state, NULL);
- _cogl_material_set_backend (material, COGL_MATERIAL_BACKEND_DEFAULT);
+ if (program != COGL_INVALID_HANDLE)
+ _cogl_material_set_backend (material, COGL_MATERIAL_BACKEND_DEFAULT);
+
+ /* If we are the current authority see if we can revert to one of our
+ * ancestors being the authority */
+ if (material == authority && authority->parent != NULL)
+ {
+ CoglMaterial *old_authority =
+ _cogl_material_get_authority (authority->parent, state);
+
+ if (old_authority->big_state->user_program == program)
+ material->differences &= ~state;
+ }
+ else if (material != authority)
+ {
+ /* 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... */
+ material->differences |= state;
+ _cogl_material_prune_redundant_ancestry (material);
+ }
if (program != COGL_INVALID_HANDLE)
cogl_handle_ref (program);
- if (material->user_program != COGL_INVALID_HANDLE)
- cogl_handle_unref (material->user_program);
- material->user_program = program;
+ if (authority == material &&
+ material->big_state->user_program != COGL_INVALID_HANDLE)
+ cogl_handle_unref (material->big_state->user_program);
+ material->big_state->user_program = program;
- if (program == COGL_INVALID_HANDLE)
- material->flags |= COGL_MATERIAL_FLAG_DEFAULT_USER_SHADER;
- else
- material->flags &= ~COGL_MATERIAL_FLAG_DEFAULT_USER_SHADER;
+ handle_automatic_blend_enable (material, state);
}
-static void
-texture_unit_init (CoglTextureUnit *unit, int index_)
-{
- unit->index = index_;
- unit->enabled = FALSE;
- unit->enabled_gl_target = 0;
- unit->gl_texture = 0;
- unit->is_foreign = FALSE;
- unit->dirty_gl_texture = FALSE;
- unit->matrix_stack = _cogl_matrix_stack_new ();
-
- unit->layer = NULL;
- unit->layer_differences = COGL_MATERIAL_LAYER_DIFFERENCE_COMBINE;
- unit->fallback = FALSE;
- unit->layer0_overridden = FALSE;
- unit->texture = COGL_INVALID_HANDLE;
-}
-
-static void
-texture_unit_free (CoglTextureUnit *unit)
-{
- _cogl_matrix_stack_destroy (unit->matrix_stack);
-}
-
-CoglTextureUnit *
-_cogl_get_texture_unit (int index_)
-{
- _COGL_GET_CONTEXT (ctx, NULL);
-
- if (ctx->texture_units->len < (index_ + 1))
- {
- int i;
- int prev_len = ctx->texture_units->len;
- ctx->texture_units = g_array_set_size (ctx->texture_units, index_ + 1);
- for (i = prev_len; i <= index_; i++)
- {
- CoglTextureUnit *unit =
- &g_array_index (ctx->texture_units, CoglTextureUnit, i);
-
- texture_unit_init (unit, i);
- }
- }
-
- return &g_array_index (ctx->texture_units, CoglTextureUnit, index_);
-}
-
-void
-_cogl_destroy_texture_units (void)
+static CoglMaterialLayer *
+_cogl_material_layer_copy (CoglMaterialLayer *src)
{
+ CoglMaterialLayer *layer = g_slice_new (CoglMaterialLayer);
int i;
- _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+ cogl_handle_ref ((CoglHandle)src);
- for (i = 0; i < ctx->texture_units->len; i++)
+ layer->_parent = src->_parent;
+ layer->owner = NULL;
+ layer->parent = src;
+
+ if (src->has_children)
+ src->children = g_list_prepend (src->children, layer);
+ else
{
- CoglTextureUnit *unit =
- &g_array_index (ctx->texture_units, CoglTextureUnit, i);
- texture_unit_free (unit);
+ src->has_children = TRUE;
+ src->first_child = layer;
+ src->children = NULL;
}
- g_array_free (ctx->texture_units, TRUE);
+
+ layer->has_children = FALSE;
+ layer->index = src->index;
+ layer->differences = 0;
+ layer->has_big_state = FALSE;
+
+ for (i = 0; i < COGL_MATERIAL_N_BACKENDS; i++)
+ layer->backend_priv[i] = NULL;
+
+ return _cogl_material_layer_handle_new (layer);
}
static void
-set_active_texture_unit (int unit_index)
+_cogl_material_layer_unparent (CoglMaterialLayer *layer)
{
- _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+ CoglMaterialLayer *parent = layer->parent;
- if (ctx->active_texture_unit != unit_index)
- {
- GE (glActiveTexture (GL_TEXTURE0 + unit_index));
- ctx->active_texture_unit = unit_index;
- }
-}
-
-/* Note: this conceptually has slightly different semantics to
- * OpenGL's glBindTexture because Cogl never cares about tracking
- * multiple textures bound to different targets on the same texture
- * unit.
- *
- * glBindTexture lets you bind multiple textures to a single texture
- * unit if they are bound to different targets. So it does something
- * like:
- * unit->current_texture[target] = texture;
- *
- * Cogl only lets you associate one texture with the currently active
- * texture unit, so the target is basically a redundant parameter
- * that's implicitly set on that texture.
- *
- * Technically this is just a thin wrapper around glBindTexture so
- * actually it does have the GL semantics but it seems worth
- * mentioning the conceptual difference in case anyone wonders why we
- * don't associate the gl_texture with a gl_target in the
- * CoglTextureUnit.
- */
-void
-_cogl_bind_gl_texture_transient (GLenum gl_target,
- GLuint gl_texture,
- gboolean is_foreign)
-{
- CoglTextureUnit *unit;
-
- _COGL_GET_CONTEXT (ctx, NO_RETVAL);
-
- unit = _cogl_get_texture_unit (ctx->active_texture_unit);
-
- /* NB: If we have previously bound a foreign texture to this texture
- * unit we don't know if that texture has since been deleted and we
- * are seeing the texture name recycled */
- if (unit->gl_texture == gl_texture &&
- !unit->dirty_gl_texture &&
- !unit->is_foreign)
+ if (parent == NULL)
return;
- GE (glBindTexture (gl_target, gl_texture));
+ g_return_if_fail (parent->has_children);
- unit->dirty_gl_texture = TRUE;
- unit->is_foreign = is_foreign;
+ if (parent->first_child == layer)
+ {
+ if (parent->children)
+ {
+ parent->first_child = parent->children->data;
+ parent->children =
+ g_list_delete_link (parent->children, parent->children);
+ }
+ else
+ parent->has_children = FALSE;
+ }
+ else
+ parent->children = g_list_remove (parent->children, layer);
+
+ cogl_handle_unref (parent);
}
-void
-_cogl_delete_gl_texture (GLuint gl_texture)
+static void
+_cogl_material_layer_free (CoglMaterialLayer *layer)
{
int i;
+ _cogl_material_layer_unparent (layer);
+
+ /* NB: layers may be used by multiple materials which may be using
+ * different backends, therefore we determine which backends to
+ * notify based on the private state pointers for each backend...
+ */
+ 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->differences & COGL_MATERIAL_LAYER_STATE_TEXTURE)
+ cogl_handle_unref (layer->texture);
+
+ if (layer->differences & COGL_MATERIAL_LAYER_STATE_NEEDS_BIG_STATE)
+ g_slice_free (CoglMaterialLayerBigState, layer->big_state);
+
+ g_slice_free (CoglMaterialLayer, layer);
+}
+
+ /* If a layer has descendants we can't modify it freely
+ *
+ * If the layer is owned and the owner has descendants we can't
+ * modify it freely.
+ *
+ * In both cases when we can't freely modify a layer we can either:
+ * - create a new layer; splice it in to replace the layer so it can
+ * be directly modified.
+ * XXX: disadvantage is that we have to invalidate the layers_cache
+ * for the owner and its descendants.
+ * - create a new derived layer and modify that.
+ */
+
+ /* XXX: how is the caller expected to deal with ref-counting?
+ *
+ * If the layer can't be freely modified and we return a new layer
+ * then that will effectively make the caller own a new reference
+ * which doesn't happen if we simply modify the given layer.
+ *
+ * We could make it consistent by taking a reference on the layer if
+ * we don't create a new one. At least this way the caller could
+ * deal with it consistently, though the semantics are a bit
+ * strange.
+ *
+ * Alternatively we could leave it to the caller to check
+ * ...?
+ */
+
+void
+_cogl_material_init_default_layers (void)
+{
+ CoglMaterialLayer *layer = g_slice_new0 (CoglMaterialLayer);
+ CoglMaterialLayerBigState *big_state =
+ g_slice_new0 (CoglMaterialLayerBigState);
+ CoglMaterialLayer *new;
+ 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);
+ layer->has_children = FALSE;
+ layer->index = 0;
- if (unit->gl_texture == gl_texture)
- {
- unit->gl_texture = 0;
- unit->dirty_gl_texture = FALSE;
- }
- }
-}
+ for (i = 0; i < COGL_MATERIAL_N_BACKENDS; i++)
+ layer->backend_priv[i] = NULL;
-/* Asserts that a layer corresponding to the given index exists. If no
- * match is found, then a new empty layer is added.
- */
-static CoglMaterialLayer *
-_cogl_material_get_layer (CoglMaterial *material,
- int index_,
- gboolean create_if_not_found)
-{
- CoglMaterialLayer *layer;
- GList *l;
- CoglHandle layer_handle;
- int i;
+ layer->differences = COGL_MATERIAL_LAYER_STATE_ALL_SPARSE;
- for (l = material->layers, i = 0; l != NULL; l = l->next, i++)
- {
- layer = l->data;
- if (layer->index == index_)
- return layer;
+ layer->unit_index = 0;
- /* The layers are always sorted, so at this point we know this layer
- * doesn't exist */
- if (layer->index > index_)
- break;
- }
- /* NB: if we now insert a new layer before l, that will maintain order.
- */
+ layer->texture = COGL_INVALID_HANDLE;
+ layer->texture_overridden = FALSE;
- if (!create_if_not_found)
- return NULL;
-
- /* possibly flush primitives referencing the current state... */
- _cogl_material_pre_change_notify (material,
- COGL_MATERIAL_CHANGE_LAYERS,
- NULL);
-
- layer = g_slice_new0 (CoglMaterialLayer);
-
- layer_handle = _cogl_material_layer_handle_new (layer);
- layer->material = material;
- layer->index = index_;
- layer->differences = 0;
layer->mag_filter = COGL_MATERIAL_FILTER_LINEAR;
layer->min_filter = COGL_MATERIAL_FILTER_LINEAR;
+
layer->wrap_mode_s = COGL_MATERIAL_WRAP_MODE_AUTOMATIC;
layer->wrap_mode_t = COGL_MATERIAL_WRAP_MODE_AUTOMATIC;
layer->wrap_mode_r = COGL_MATERIAL_WRAP_MODE_AUTOMATIC;
- layer->texture = COGL_INVALID_HANDLE;
- layer->unit_index = i;
+ layer->big_state = big_state;
+ layer->has_big_state = TRUE;
/* Choose the same default combine mode as OpenGL:
- * MODULATE(PREVIOUS[RGBA],TEXTURE[RGBA]) */
- layer->texture_combine_rgb_func = GL_MODULATE;
- layer->texture_combine_rgb_src[0] = GL_PREVIOUS;
- layer->texture_combine_rgb_src[1] = GL_TEXTURE;
- layer->texture_combine_rgb_op[0] = GL_SRC_COLOR;
- layer->texture_combine_rgb_op[1] = GL_SRC_COLOR;
- layer->texture_combine_alpha_func = GL_MODULATE;
- layer->texture_combine_alpha_src[0] = GL_PREVIOUS;
- layer->texture_combine_alpha_src[1] = GL_TEXTURE;
- layer->texture_combine_alpha_op[0] = GL_SRC_ALPHA;
- layer->texture_combine_alpha_op[1] = GL_SRC_ALPHA;
+ * RGBA = MODULATE(PREVIOUS[RGBA],TEXTURE[RGBA]) */
+ big_state->texture_combine_rgb_func = GL_MODULATE;
+ big_state->texture_combine_rgb_src[0] = GL_PREVIOUS;
+ big_state->texture_combine_rgb_src[1] = GL_TEXTURE;
+ big_state->texture_combine_rgb_op[0] = GL_SRC_COLOR;
+ big_state->texture_combine_rgb_op[1] = GL_SRC_COLOR;
+ big_state->texture_combine_alpha_func = GL_MODULATE;
+ big_state->texture_combine_alpha_src[0] = GL_PREVIOUS;
+ big_state->texture_combine_alpha_src[1] = GL_TEXTURE;
+ big_state->texture_combine_alpha_op[0] = GL_SRC_ALPHA;
+ big_state->texture_combine_alpha_op[1] = GL_SRC_ALPHA;
- cogl_matrix_init_identity (&layer->matrix);
+ cogl_matrix_init_identity (&big_state->matrix);
- /* Note: see comment after for() loop above */
- material->layers =
- g_list_insert_before (material->layers, l, layer_handle);
+ ctx->default_layer_0 = _cogl_material_layer_handle_new (layer);
- material->flags &= ~COGL_MATERIAL_FLAG_DEFAULT_LAYERS;
+ /* 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_material_layer_copy (layer);
+ new = _cogl_material_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. */
- material->n_layers++;
-
- return layer;
-}
-
-void
-cogl_material_set_layer (CoglHandle material_handle,
- int layer_index,
- CoglHandle texture_handle)
-{
- CoglMaterial *material;
- CoglMaterialLayer *layer;
-
- g_return_if_fail (cogl_is_material (material_handle));
- g_return_if_fail (texture_handle == COGL_INVALID_HANDLE
- || cogl_is_texture (texture_handle));
-
- material = _cogl_material_pointer_from_handle (material_handle);
-
- layer = _cogl_material_get_layer (material, layer_index, TRUE);
-
- if (texture_handle == layer->texture)
- return;
-
- /* possibly flush primitives referencing the current state... */
- _cogl_material_pre_change_notify (material,
- COGL_MATERIAL_CHANGE_LAYERS,
- NULL);
-
- if (texture_handle)
- cogl_handle_ref (texture_handle);
-
- if (layer->texture)
- cogl_handle_unref (layer->texture);
-
- layer->texture = texture_handle;
-
- handle_automatic_blend_enable (material);
-
- layer->differences |= COGL_MATERIAL_LAYER_DIFFERENCE_TEXTURE;
+ /* 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_material_layer_copy (ctx->default_layer_n);
}
static void
@@ -1298,7 +4193,9 @@ cogl_material_set_layer_combine (CoglHandle handle,
const char *combine_description,
GError **error)
{
- CoglMaterial *material;
+ CoglMaterial *material = COGL_MATERIAL (handle);
+ CoglMaterialLayerState state = COGL_MATERIAL_LAYER_STATE_COMBINE;
+ CoglMaterialLayer *authority;
CoglMaterialLayer *layer;
CoglBlendStringStatement statements[2];
CoglBlendStringStatement split[2];
@@ -1309,8 +4206,17 @@ cogl_material_set_layer_combine (CoglHandle handle,
g_return_val_if_fail (cogl_is_material (handle), FALSE);
- material = _cogl_material_pointer_from_handle (handle);
- layer = _cogl_material_get_layer (material, layer_index, TRUE);
+ /* 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
+ * material. If the layer is created then it will be owned by
+ * material. */
+ layer = _cogl_material_get_layer (material, layer_index);
+
+ /* Now find the ancestor of the layer that is the authority for the
+ * state we want to change */
+ authority = _cogl_material_layer_get_authority (layer, state);
count =
_cogl_blend_string_compile (combine_description,
@@ -1343,23 +4249,55 @@ cogl_material_set_layer_combine (CoglHandle handle,
a = &statements[1];
}
+ /* FIXME: compare the new state with the current state! */
+
/* possibly flush primitives referencing the current state... */
- _cogl_material_layer_pre_change_notify (
- layer,
- COGL_MATERIAL_LAYER_CHANGE_COMBINE);
+ layer = _cogl_material_layer_pre_change_notify (material, layer, state);
setup_texture_combine_state (rgb,
- &layer->texture_combine_rgb_func,
- layer->texture_combine_rgb_src,
- layer->texture_combine_rgb_op);
+ &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->texture_combine_alpha_func,
- layer->texture_combine_alpha_src,
- layer->texture_combine_alpha_op);
+ &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 && authority->parent != NULL)
+ {
+ CoglMaterialLayer *old_authority =
+ _cogl_material_layer_get_authority (authority->parent, state);
- layer->differences |= COGL_MATERIAL_LAYER_DIFFERENCE_COMBINE;
+ if (_cogl_material_layer_combine_state_equal (authority,
+ old_authority))
+ {
+ layer->differences &= ~state;
+
+ g_assert (layer->owner == material);
+ if (layer->differences == 0)
+ _cogl_material_prune_empty_layer_difference (material,
+ 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_material_layer_prune_redundant_ancestry (layer);
+ }
+
+changed:
+
+ handle_automatic_blend_enable (material, COGL_MATERIAL_STATE_LAYERS);
return TRUE;
}
@@ -1368,228 +4306,289 @@ cogl_material_set_layer_combine_constant (CoglHandle handle,
int layer_index,
CoglColor *constant_color)
{
- CoglMaterial *material;
- CoglMaterialLayer *layer;
- GLfloat *constant;
+ CoglMaterial *material = COGL_MATERIAL (handle);
+ CoglMaterialLayerState state = COGL_MATERIAL_LAYER_STATE_COMBINE_CONSTANT;
+ CoglMaterialLayer *layer;
+ CoglMaterialLayer *authority;
+ CoglMaterialLayer *new;
g_return_if_fail (cogl_is_material (handle));
- material = _cogl_material_pointer_from_handle (handle);
- layer = _cogl_material_get_layer (material, layer_index, TRUE);
+ /* 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
+ * material. If the layer is created then it will be owned by
+ * material. */
+ layer = _cogl_material_get_layer (material, layer_index);
- /* possibly flush primitives referencing the current state... */
- _cogl_material_layer_pre_change_notify (
- layer,
- COGL_MATERIAL_LAYER_CHANGE_COMBINE_CONSTANT);
+ /* Now find the ancestor of the layer that is the authority for the
+ * state we want to change */
+ authority = _cogl_material_layer_get_authority (layer, state);
- constant = layer->texture_combine_constant;
- constant[0] = cogl_color_get_red_float (constant_color);
- constant[1] = cogl_color_get_green_float (constant_color);
- constant[2] = cogl_color_get_blue_float (constant_color);
- constant[3] = cogl_color_get_alpha_float (constant_color);
-
- layer->differences |= COGL_MATERIAL_LAYER_DIFFERENCE_COMBINE_CONSTANT;
-}
-
-void
-cogl_material_set_layer_matrix (CoglHandle material_handle,
- int layer_index,
- CoglMatrix *matrix)
-{
- CoglMaterial *material;
- CoglMaterialLayer *layer;
-
- static gboolean initialized_identity_matrix = FALSE;
- static CoglMatrix identity_matrix;
-
- g_return_if_fail (cogl_is_material (material_handle));
-
- material = _cogl_material_pointer_from_handle (material_handle);
- layer = _cogl_material_get_layer (material, layer_index, TRUE);
-
- if (cogl_matrix_equal (matrix, &layer->matrix))
+ if (memcmp (authority->big_state->texture_combine_constant,
+ constant_color, sizeof (float) * 4) == 0)
return;
- /* possibly flush primitives referencing the current state... */
- _cogl_material_layer_pre_change_notify (
- layer,
- COGL_MATERIAL_LAYER_CHANGE_USER_MATRIX);
- layer->matrix = *matrix;
-
- if (G_UNLIKELY (!initialized_identity_matrix))
- cogl_matrix_init_identity (&identity_matrix);
-
- if (cogl_matrix_equal (matrix, &identity_matrix))
- layer->differences &= ~COGL_MATERIAL_LAYER_DIFFERENCE_USER_MATRIX;
+ new = _cogl_material_layer_pre_change_notify (material, layer, state);
+ if (new != layer)
+ layer = new;
else
- layer->differences |= COGL_MATERIAL_LAYER_DIFFERENCE_USER_MATRIX;
-}
-
-static void
-_cogl_material_layer_free (CoglMaterialLayer *layer)
-{
- CoglTextureUnit *unit = _cogl_get_texture_unit (layer->unit_index);
-
- /* Since we're freeing the layer make sure the texture unit no
- * longer keeps a back reference to it */
- if (unit->layer == layer)
- unit->layer = NULL;
-
- if (layer->texture != COGL_INVALID_HANDLE)
- cogl_handle_unref (layer->texture);
- g_slice_free (CoglMaterialLayer, layer);
-}
-
-void
-cogl_material_remove_layer (CoglHandle material_handle,
- int layer_index)
-{
- CoglMaterial *material;
- CoglMaterialLayer *layer;
- GList *l;
- GList *l2;
- gboolean found = FALSE;
- int i;
-
- g_return_if_fail (cogl_is_material (material_handle));
-
- material = _cogl_material_pointer_from_handle (material_handle);
-
- for (l = material->layers, i = 0; l != NULL; l = l2, i++)
{
- /* were going to be modifying the list and continuing to iterate
- * it so we get the pointer to the next link now... */
- l2 = l->next;
-
- layer = l->data;
- if (layer->index == layer_index)
- {
- CoglHandle handle = (CoglHandle) layer;
-
- found = TRUE;
-
- /* possibly flush primitives referencing the current state... */
- _cogl_material_pre_change_notify (material,
- COGL_MATERIAL_CHANGE_LAYERS,
- NULL);
-
- cogl_handle_unref (handle);
- material->layers = g_list_delete_link (material->layers, l);
- material->n_layers--;
-
- /* We need to iterate through the rest of the layers
- * updating the texture unit that they reference. */
- continue;
- }
-
- /* All layers following a removed layer need to have their
- * associated texture unit updated... */
- if (found)
+ /* 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 && authority->parent != NULL)
{
- _cogl_material_layer_pre_change_notify (
- layer,
- COGL_MATERIAL_LAYER_CHANGE_UNIT);
- layer->unit_index = i;
+ CoglMaterialLayer *old_authority =
+ _cogl_material_layer_get_authority (authority->parent, state);
+ CoglMaterialLayerBigState *old_big_state = old_authority->big_state;
+
+ if (memcmp (old_big_state->texture_combine_constant,
+ constant_color, sizeof (float) * 4) == 0)
+ {
+ layer->differences &= ~state;
+
+ g_assert (layer->owner == material);
+ if (layer->differences == 0)
+ _cogl_material_prune_empty_layer_difference (material,
+ layer);
+ goto changed;
+ }
}
}
- handle_automatic_blend_enable (material);
+ layer->big_state->texture_combine_constant[0] =
+ cogl_color_get_red_float (constant_color);
+ layer->big_state->texture_combine_constant[1] =
+ cogl_color_get_green_float (constant_color);
+ layer->big_state->texture_combine_constant[2] =
+ cogl_color_get_blue_float (constant_color);
+ layer->big_state->texture_combine_constant[3] =
+ cogl_color_get_alpha_float (constant_color);
+
+ /* 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_material_layer_prune_redundant_ancestry (layer);
+ }
+
+changed:
+
+ handle_automatic_blend_enable (material, COGL_MATERIAL_STATE_LAYERS);
}
-/* XXX: This API is hopfully just a stop-gap solution. Ideally _cogl_enable
- * will be replaced. */
-unsigned long
-_cogl_material_get_cogl_enable_flags (CoglHandle material_handle)
+void
+cogl_material_set_layer_matrix (CoglHandle handle,
+ int layer_index,
+ CoglMatrix *matrix)
{
- CoglMaterial *material;
- unsigned long enable_flags = 0;
+ CoglMaterial *material = COGL_MATERIAL (handle);
+ CoglMaterialLayerState state = COGL_MATERIAL_LAYER_STATE_USER_MATRIX;
+ CoglMaterialLayer *layer;
+ CoglMaterialLayer *authority;
+ CoglMaterialLayer *new;
- _COGL_GET_CONTEXT (ctx, 0);
+ g_return_if_fail (cogl_is_material (handle));
- g_return_val_if_fail (cogl_is_material (material_handle), 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
+ * material. If the layer is created then it will be owned by
+ * material. */
+ layer = _cogl_material_get_layer (material, layer_index);
- material = _cogl_material_pointer_from_handle (material_handle);
+ /* Now find the ancestor of the layer that is the authority for the
+ * state we want to change */
+ authority = _cogl_material_layer_get_authority (layer, state);
- /* Enable blending if the geometry has an associated alpha color,
- * or the material wants blending enabled. */
- if (material->flags & COGL_MATERIAL_FLAG_ENABLE_BLEND)
- enable_flags |= COGL_ENABLE_BLEND;
+ if (cogl_matrix_equal (matrix, &authority->big_state->matrix))
+ return;
- return enable_flags;
+ new = _cogl_material_layer_pre_change_notify (material, 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 && authority->parent != NULL)
+ {
+ CoglMaterialLayer *old_authority =
+ _cogl_material_layer_get_authority (authority->parent, state);
+
+ if (cogl_matrix_equal (matrix, &old_authority->big_state->matrix))
+ {
+ layer->differences &= ~state;
+
+ g_assert (layer->owner == material);
+ if (layer->differences == 0)
+ _cogl_material_prune_empty_layer_difference (material,
+ 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_material_layer_prune_redundant_ancestry (layer);
+ }
}
-/* It's a bit out of the ordinary to return a const GList *, but it's
- * probably sensible to try and avoid list manipulation for every
- * primitive emitted in a scene, every frame.
- *
- * Alternatively; we could either add a _foreach function, or maybe
- * a function that gets a passed a buffer (that may be stack allocated)
- * by the caller.
+void
+cogl_material_remove_layer (CoglHandle handle, int layer_index)
+{
+ CoglMaterial *material = COGL_MATERIAL (handle);
+ CoglMaterial *authority;
+ CoglMaterialLayerInfo layer_info;
+ int i;
+
+ g_return_if_fail (cogl_is_material (handle));
+
+ authority =
+ _cogl_material_get_authority (material, COGL_MATERIAL_STATE_LAYERS);
+
+ /* The layer index of the layer we want info about */
+ layer_info.layer_index = layer_index;
+
+ /* This will be updated with a reference to the layer being removed
+ * if it can be found. */
+ layer_info.layer = NULL;
+
+ /* This will be filled in with a list of layers that need to be
+ * dropped down to a lower texture unit to fill the gap of the
+ * removed layer. */
+ layer_info.layers_to_shift =
+ g_alloca (sizeof (CoglMaterialLayer *) * authority->n_layers);
+ layer_info.n_layers_to_shift = 0;
+
+ /* Unlike when we query layer info when adding a layer we must
+ * always have a complete layers_to_shift list... */
+ layer_info.ignore_shift_layers_if_found = FALSE;
+
+ _cogl_material_get_layer_info (authority, &layer_info);
+
+ if (layer_info.layer == NULL)
+ return;
+
+ for (i = 0; i < layer_info.n_layers_to_shift; i++)
+ {
+ CoglMaterialLayer *shift_layer = layer_info.layers_to_shift[i];
+ int unit_index = _cogl_material_layer_get_unit_index (shift_layer);
+ _cogl_material_set_layer_unit (material, shift_layer, unit_index - 1);
+ /* NB: shift_layer may not be writeable so _set_layer_unit()
+ * will allocate a derived layer internally which will become
+ * owned by material. Check the return value if we need to do
+ * anything else with this layer. */
+ }
+
+ _cogl_material_remove_layer_difference (material, layer_info.layer, TRUE);
+ _cogl_material_try_reverting_layers_authority (material, NULL);
+
+ handle_automatic_blend_enable (material, COGL_MATERIAL_STATE_LAYERS);
+}
+
+static gboolean
+prepend_layer_to_list_cb (CoglMaterialLayer *layer,
+ void *user_data)
+{
+ GList **layers = user_data;
+
+ *layers = g_list_prepend (*layers, layer);
+ return TRUE;
+}
+
+/* TODO: deprecate this API and replace it with
+ * cogl_material_foreach_layer
+ * TODO: update the docs to note that if the user modifies any layers
+ * then the list may become invalid.
*/
const GList *
-cogl_material_get_layers (CoglHandle material_handle)
+cogl_material_get_layers (CoglHandle handle)
{
- CoglMaterial *material;
+ CoglMaterial *material = COGL_MATERIAL (handle);
- g_return_val_if_fail (cogl_is_material (material_handle), NULL);
+ g_return_val_if_fail (cogl_is_material (handle), NULL);
- material = _cogl_material_pointer_from_handle (material_handle);
+ if (!material->deprecated_get_layers_list_dirty)
+ g_list_free (material->deprecated_get_layers_list);
- return material->layers;
+ material->deprecated_get_layers_list = NULL;
+
+ _cogl_material_foreach_layer (material,
+ prepend_layer_to_list_cb,
+ &material->deprecated_get_layers_list);
+ material->deprecated_get_layers_list =
+ g_list_reverse (material->deprecated_get_layers_list);
+
+ material->deprecated_get_layers_list_dirty = 0;
+
+ return material->deprecated_get_layers_list;
}
int
-cogl_material_get_n_layers (CoglHandle material_handle)
+cogl_material_get_n_layers (CoglHandle handle)
{
- CoglMaterial *material;
+ CoglMaterial *material = COGL_MATERIAL (handle);
+ CoglMaterial *authority;
- g_return_val_if_fail (cogl_is_material (material_handle), 0);
+ g_return_val_if_fail (cogl_is_material (handle), 0);
- material = _cogl_material_pointer_from_handle (material_handle);
+ authority =
+ _cogl_material_get_authority (material, COGL_MATERIAL_STATE_LAYERS);
- return material->n_layers;
+ return authority->n_layers;
}
+/* FIXME: deprecate and replace with
+ * cogl_material_get_layer_type() instead. */
CoglMaterialLayerType
cogl_material_layer_get_type (CoglHandle layer_handle)
{
return COGL_MATERIAL_LAYER_TYPE_TEXTURE;
}
+/* FIXME: deprecate and replace with
+ * cogl_material_get_layer_texture() instead. */
CoglHandle
-cogl_material_layer_get_texture (CoglHandle layer_handle)
+cogl_material_layer_get_texture (CoglHandle handle)
{
- CoglMaterialLayer *layer;
+ CoglMaterialLayer *layer = COGL_MATERIAL_LAYER (handle);
- g_return_val_if_fail (cogl_is_material_layer (layer_handle),
+ g_return_val_if_fail (cogl_is_material_layer (handle),
COGL_INVALID_HANDLE);
- layer = _cogl_material_layer_pointer_from_handle (layer_handle);
- return layer->texture;
+ return _cogl_material_layer_get_texture (layer);
}
gboolean
-_cogl_material_layer_has_user_matrix (CoglHandle layer_handle)
+_cogl_material_layer_has_user_matrix (CoglHandle handle)
{
- CoglMaterialLayer *layer =
- _cogl_material_layer_pointer_from_handle (layer_handle);
- return layer->differences & COGL_MATERIAL_LAYER_CHANGE_USER_MATRIX ?
- TRUE : FALSE;
-}
+ CoglMaterialLayer *layer = COGL_MATERIAL_LAYER (handle);
+ CoglMaterialLayer *authority;
-static CoglHandle
-_cogl_material_layer_copy (CoglHandle layer_handle)
-{
- CoglMaterialLayer *layer =
- _cogl_material_layer_pointer_from_handle (layer_handle);
- CoglMaterialLayer *layer_copy = g_slice_new (CoglMaterialLayer);
+ g_return_val_if_fail (cogl_is_material_layer (handle), FALSE);
- memcpy (layer_copy, layer, sizeof (CoglMaterialLayer));
+ authority =
+ _cogl_material_layer_get_authority (layer,
+ COGL_MATERIAL_LAYER_STATE_USER_MATRIX);
- if (layer_copy->texture != COGL_INVALID_HANDLE)
- cogl_handle_ref (layer_copy->texture);
-
- return _cogl_material_layer_handle_new (layer_copy);
+ /* If the authority is the default material then no, otherwise yes */
+ return authority->parent ? TRUE : FALSE;
}
static gboolean
@@ -1601,24 +4600,139 @@ is_mipmap_filter (CoglMaterialFilter filter)
|| filter == COGL_MATERIAL_FILTER_LINEAR_MIPMAP_LINEAR);
}
-#ifndef HAVE_COGL_GLES
-static int
-get_max_texture_image_units (void)
+static void
+_cogl_material_layer_get_filters (CoglMaterialLayer *layer,
+ CoglMaterialFilter *min_filter,
+ CoglMaterialFilter *mag_filter)
{
- _COGL_GET_CONTEXT (ctx, 0);
+ CoglMaterialLayer *authority =
+ _cogl_material_layer_get_authority (layer,
+ COGL_MATERIAL_LAYER_STATE_FILTERS);
- /* 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))
+ *min_filter = authority->min_filter;
+ *mag_filter = authority->mag_filter;
+}
+
+void
+_cogl_material_layer_ensure_mipmaps (CoglHandle handle)
+{
+ CoglMaterialLayer *layer = COGL_MATERIAL_LAYER (handle);
+ CoglMaterialLayer *texture_authority;
+ CoglMaterialFilter min_filter;
+ CoglMaterialFilter mag_filter;
+
+ texture_authority =
+ _cogl_material_layer_get_authority (layer,
+ COGL_MATERIAL_LAYER_STATE_TEXTURE);
+
+ _cogl_material_layer_get_filters (layer, &min_filter, &mag_filter);
+
+ if (texture_authority->texture != COGL_INVALID_HANDLE &&
+ (is_mipmap_filter (min_filter) ||
+ is_mipmap_filter (mag_filter)))
+ _cogl_texture_ensure_mipmaps (texture_authority->texture);
+}
+
+CoglMaterialFilter
+cogl_material_layer_get_min_filter (CoglHandle handle)
+{
+ CoglMaterialLayer *layer = COGL_MATERIAL_LAYER (handle);
+ CoglMaterialLayer *authority;
+
+ g_return_val_if_fail (cogl_is_material_layer (handle), 0);
+
+ authority =
+ _cogl_material_layer_get_authority (layer,
+ COGL_MATERIAL_LAYER_STATE_FILTERS);
+
+ return authority->min_filter;
+}
+
+CoglMaterialFilter
+cogl_material_layer_get_mag_filter (CoglHandle handle)
+{
+ CoglMaterialLayer *layer = COGL_MATERIAL_LAYER (handle);
+ CoglMaterialLayer *authority;
+
+ g_return_val_if_fail (cogl_is_material_layer (handle), 0);
+
+ authority =
+ _cogl_material_layer_get_authority (layer,
+ COGL_MATERIAL_LAYER_STATE_FILTERS);
+
+ return authority->mag_filter;
+}
+
+void
+cogl_material_set_layer_filters (CoglHandle handle,
+ int layer_index,
+ CoglMaterialFilter min_filter,
+ CoglMaterialFilter mag_filter)
+{
+ CoglMaterial *material = COGL_MATERIAL (handle);
+ CoglMaterialLayerState change = COGL_MATERIAL_LAYER_STATE_FILTERS;
+ CoglMaterialLayer *layer;
+ CoglMaterialLayer *authority;
+ CoglMaterialLayer *new;
+
+ g_return_if_fail (cogl_is_material (handle));
+
+ /* 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
+ * material. If the layer is created then it will be owned by
+ * material. */
+ layer = _cogl_material_get_layer (material, layer_index);
+
+ /* Now find the ancestor of the layer that is the authority for the
+ * state we want to change */
+ authority = _cogl_material_layer_get_authority (layer, change);
+
+ if (authority->min_filter == min_filter &&
+ authority->mag_filter == mag_filter)
+ return;
+
+ new = _cogl_material_layer_pre_change_notify (material, layer, change);
+ if (new != layer)
+ layer = new;
+ else
{
- ctx->max_texture_image_units = 1;
- GE (glGetIntegerv (GL_MAX_TEXTURE_IMAGE_UNITS,
- &ctx->max_texture_image_units));
+ /* 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 && authority->parent != NULL)
+ {
+ CoglMaterialLayer *old_authority =
+ _cogl_material_layer_get_authority (authority->parent, change);
+
+ if (old_authority->min_filter == min_filter &&
+ old_authority->mag_filter == mag_filter)
+ {
+ layer->differences &= ~change;
+
+ g_assert (layer->owner == material);
+ if (layer->differences == 0)
+ _cogl_material_prune_empty_layer_difference (material,
+ layer);
+ return;
+ }
+ }
}
- return ctx->max_texture_image_units;
+ 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 |= change;
+ _cogl_material_layer_prune_redundant_ancestry (layer);
+ }
}
-#endif
static void
disable_texture_unit (int unit_index)
@@ -1629,57 +4743,14 @@ disable_texture_unit (int unit_index)
unit = &g_array_index (ctx->texture_units, CoglTextureUnit, unit_index);
-#ifndef DISABLE_MATERIAL_CACHE
if (unit->enabled)
-#endif
{
set_active_texture_unit (unit_index);
- GE (glDisable (unit->enabled_gl_target));
- unit->enabled_gl_target = 0;
+ GE (glDisable (unit->current_gl_target));
unit->enabled = FALSE;
-
- /* XXX: This implies that a lot of unneeded work will happen
- * if a given layer somehow simply gets disabled and enabled
- * without changing. Currently the public CoglMaterial API
- * doesn't give a way to disable layers but one day we may
- * want to avoid doing this if that changes... */
- unit->layer = NULL;
}
}
-void
-_cogl_material_layer_ensure_mipmaps (CoglHandle layer_handle)
-{
- CoglMaterialLayer *layer;
-
- layer = _cogl_material_layer_pointer_from_handle (layer_handle);
-
- if (layer->texture &&
- (is_mipmap_filter (layer->min_filter) ||
- is_mipmap_filter (layer->mag_filter)))
- _cogl_texture_ensure_mipmaps (layer->texture);
-}
-
-static unsigned int
-get_n_args_for_combine_func (GLint func)
-{
- switch (func)
- {
- case GL_REPLACE:
- return 1;
- case GL_MODULATE:
- case GL_ADD:
- case GL_ADD_SIGNED:
- case GL_SUBTRACT:
- case GL_DOT3_RGB:
- case GL_DOT3_RGBA:
- return 2;
- case GL_INTERPOLATE:
- return 3;
- }
- return 0;
-}
-
void
_cogl_gl_use_program_wrapper (GLuint program)
{
@@ -1799,6 +4870,26 @@ use_program (CoglHandle program_handle, CoglMaterialProgramType type)
}
}
+#if defined (COGL_MATERIAL_BACKEND_GLSL) || \
+ defined (COGL_MATERIAL_BACKEND_ARBFP)
+static int
+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
+
#ifdef COGL_MATERIAL_BACKEND_GLSL
static int
@@ -1808,7 +4899,9 @@ _cogl_material_backend_glsl_get_max_texture_units (void)
}
static gboolean
-_cogl_material_backend_glsl_start (CoglMaterial *material)
+_cogl_material_backend_glsl_start (CoglMaterial *material,
+ int n_layers,
+ unsigned long materials_difference)
{
_COGL_GET_CONTEXT (ctx, FALSE);
@@ -1818,10 +4911,13 @@ _cogl_material_backend_glsl_start (CoglMaterial *material)
/* FIXME: This will likely conflict with the GLES 2 backends use of
* glUseProgram.
*/
- if (!(ctx->current_material_flags & COGL_MATERIAL_FLAG_DEFAULT_USER_SHADER
- && material->flags & COGL_MATERIAL_FLAG_DEFAULT_USER_SHADER))
+ if (materials_difference & COGL_MATERIAL_STATE_USER_SHADER)
{
- CoglHandle program = material->user_program;
+ CoglMaterial *authority =
+ _cogl_material_get_authority (material,
+ COGL_MATERIAL_STATE_USER_SHADER);
+ CoglHandle program = authority->big_state->user_program;
+
if (program == COGL_INVALID_HANDLE)
return FALSE; /* XXX: change me when we support code generation here */
@@ -1835,7 +4931,9 @@ _cogl_material_backend_glsl_start (CoglMaterial *material)
}
gboolean
-_cogl_material_backend_glsl_add_layer (CoglMaterialLayer *layer)
+_cogl_material_backend_glsl_add_layer (CoglMaterial *material,
+ CoglMaterialLayer *layer,
+ unsigned long layers_difference)
{
return TRUE;
}
@@ -1847,7 +4945,8 @@ _cogl_material_backend_glsl_passthrough (CoglMaterial *material)
}
gboolean
-_cogl_material_backend_glsl_end (CoglMaterial *material)
+_cogl_material_backend_glsl_end (CoglMaterial *material,
+ unsigned long materials_difference)
{
return TRUE;
}
@@ -1875,7 +4974,9 @@ _cogl_material_backend_arbfp_get_max_texture_units (void)
}
static gboolean
-_cogl_material_backend_arbfp_start (CoglMaterial *material)
+_cogl_material_backend_arbfp_start (CoglMaterial *material,
+ int n_layers,
+ unsigned long materials_difference)
{
CoglMaterialBackendARBfpPrivate *priv;
@@ -1888,13 +4989,16 @@ _cogl_material_backend_arbfp_start (CoglMaterial *material)
if (ctx->fog_enabled)
return FALSE;
- if (!material->backend_priv)
- material->backend_priv = g_slice_new0 (CoglMaterialBackendARBfpPrivate);
+ if (!material->backend_priv_set)
+ {
+ material->backend_priv = g_slice_new0 (CoglMaterialBackendARBfpPrivate);
+ material->backend_priv_set = TRUE;
+ }
priv = material->backend_priv;
if (priv->gl_program == 0)
{
- /* Se reuse a single grow-only GString for ARBfp code-gen */
+ /* We reuse a single grow-only GString for ARBfp code-gen */
g_string_set_size (ctx->arbfp_source_buffer, 0);
priv->source = ctx->arbfp_source_buffer;
g_string_append (priv->source,
@@ -1905,7 +5009,7 @@ _cogl_material_backend_arbfp_start (CoglMaterial *material)
"PARAM one = {1, 1, 1, 1};\n"
"PARAM two = {2, 2, 2, 2};\n"
"PARAM minus_one = {-1, -1, -1, -1};\n");
- priv->sampled = g_new0 (gboolean, material->n_layers);
+ priv->sampled = g_new0 (gboolean, n_layers);
}
return TRUE;
@@ -1916,20 +5020,22 @@ _cogl_material_backend_arbfp_start (CoglMaterial *material)
* with the same arguments...
*/
static gboolean
-need_texture_combine_separate (CoglMaterialLayer *layer)
+need_texture_combine_separate (CoglMaterialLayer *combine_authority)
{
+ CoglMaterialLayerBigState *big_state = combine_authority->big_state;
int n_args;
int i;
- if (layer->texture_combine_rgb_func != layer->texture_combine_alpha_func)
+ if (big_state->texture_combine_rgb_func !=
+ big_state->texture_combine_alpha_func)
return TRUE;
- n_args = get_n_args_for_combine_func (layer->texture_combine_rgb_func);
+ n_args = get_n_args_for_combine_func (big_state->texture_combine_rgb_func);
for (i = 0; i < n_args; i++)
{
- if (layer->texture_combine_rgb_src[i] !=
- layer->texture_combine_alpha_src[i])
+ if (big_state->texture_combine_rgb_src[i] !=
+ big_state->texture_combine_alpha_src[i])
return TRUE;
/*
@@ -1952,10 +5058,10 @@ need_texture_combine_separate (CoglMaterialLayer *layer)
* "RGBA = REPLACE (1-CONSTANT)" or
* "RGBA = REPLACE (1-CONSTANT[A])"
*/
- switch (layer->texture_combine_alpha_op[i])
+ switch (big_state->texture_combine_alpha_op[i])
{
case GL_SRC_ALPHA:
- switch (layer->texture_combine_rgb_op[i])
+ switch (big_state->texture_combine_rgb_op[i])
{
case GL_SRC_COLOR:
case GL_SRC_ALPHA:
@@ -1965,7 +5071,7 @@ need_texture_combine_separate (CoglMaterialLayer *layer)
}
break;
case GL_ONE_MINUS_SRC_ALPHA:
- switch (layer->texture_combine_rgb_op[i])
+ switch (big_state->texture_combine_rgb_op[i])
{
case GL_ONE_MINUS_SRC_COLOR:
case GL_ONE_MINUS_SRC_ALPHA:
@@ -2079,36 +5185,45 @@ setup_arg (CoglMaterial *material,
CoglMaterialBackendARBfpPrivate *priv = material->backend_priv;
static const char *tmp_name[3] = { "tmp0", "tmp1", "tmp2" };
GLenum gl_target;
+ CoglHandle texture;
switch (src)
{
case GL_TEXTURE:
arg->type = COGL_MATERIAL_BACKEND_ARBFP_ARG_TYPE_TEXTURE;
arg->name = "texel%d";
- arg->texture_unit = layer->unit_index;
- cogl_texture_get_gl_texture (layer->texture, NULL, &gl_target);
+ arg->texture_unit = _cogl_material_layer_get_unit_index (layer);
+ texture = _cogl_material_layer_get_texture (layer);
+ cogl_texture_get_gl_texture (texture, NULL, &gl_target);
setup_texture_source (priv, arg->texture_unit, gl_target);
break;
case GL_CONSTANT:
- arg->type = COGL_MATERIAL_BACKEND_ARBFP_ARG_TYPE_CONSTANT;
- arg->name = "constant%d";
- arg->constant_id = priv->next_constant_id++;
- g_string_append_printf (priv->source,
- "PARAM constant%d = "
- " {%f, %f, %f, %f};\n",
- arg->constant_id,
- layer->texture_combine_constant[0],
- layer->texture_combine_constant[1],
- layer->texture_combine_constant[2],
- layer->texture_combine_constant[3]);
- break;
+ {
+ unsigned long state = COGL_MATERIAL_LAYER_STATE_COMBINE_CONSTANT;
+ CoglMaterialLayer *authority =
+ _cogl_material_layer_get_authority (layer, state);
+ CoglMaterialLayerBigState *big_state = authority->big_state;
+
+ arg->type = COGL_MATERIAL_BACKEND_ARBFP_ARG_TYPE_CONSTANT;
+ arg->name = "constant%d";
+ arg->constant_id = priv->next_constant_id++;
+ g_string_append_printf (priv->source,
+ "PARAM constant%d = "
+ " {%f, %f, %f, %f};\n",
+ arg->constant_id,
+ big_state->texture_combine_constant[0],
+ big_state->texture_combine_constant[1],
+ big_state->texture_combine_constant[2],
+ big_state->texture_combine_constant[3]);
+ break;
+ }
case GL_PRIMARY_COLOR:
arg->type = COGL_MATERIAL_BACKEND_ARBFP_ARG_TYPE_SIMPLE;
arg->name = "fragment.color.primary";
break;
case GL_PREVIOUS:
arg->type = COGL_MATERIAL_BACKEND_ARBFP_ARG_TYPE_SIMPLE;
- if (layer->unit_index == 0)
+ if (_cogl_material_layer_get_unit_index (layer) == 0)
arg->name = "fragment.color.primary";
else
arg->name = "output";
@@ -2117,7 +5232,8 @@ setup_arg (CoglMaterial *material,
arg->type = COGL_MATERIAL_BACKEND_ARBFP_ARG_TYPE_TEXTURE;
arg->name = "texture[%d]";
arg->texture_unit = src - GL_TEXTURE0;
- cogl_texture_get_gl_texture (layer->texture, NULL, &gl_target);
+ texture = _cogl_material_layer_get_texture (layer);
+ cogl_texture_get_gl_texture (texture, NULL, &gl_target);
setup_texture_source (priv, arg->texture_unit, gl_target);
}
@@ -2193,7 +5309,6 @@ backend_arbfp_args_equal (CoglMaterialBackendARBfpArg *arg0,
static void
append_function (CoglMaterial *material,
- CoglMaterialLayer *layer,
CoglBlendStringChannelMask mask,
GLint function,
CoglMaterialBackendARBfpArg *args,
@@ -2353,7 +5468,6 @@ append_masked_combine (CoglMaterial *material,
}
append_function (material,
- layer,
mask,
function,
args,
@@ -2361,10 +5475,15 @@ append_masked_combine (CoglMaterial *material,
}
static gboolean
-_cogl_material_backend_arbfp_add_layer (CoglMaterialLayer *layer)
+_cogl_material_backend_arbfp_add_layer (CoglMaterial *material,
+ CoglMaterialLayer *layer,
+ unsigned long layers_difference)
{
- CoglMaterial *material = layer->material;
CoglMaterialBackendARBfpPrivate *priv = material->backend_priv;
+ CoglMaterialLayer *combine_authority =
+ _cogl_material_layer_get_authority (layer,
+ COGL_MATERIAL_LAYER_STATE_COMBINE);
+ CoglMaterialLayerBigState *big_state = combine_authority->big_state;
/* Notes...
*
@@ -2398,16 +5517,16 @@ _cogl_material_backend_arbfp_add_layer (CoglMaterialLayer *layer)
if (!priv->source)
return TRUE;
- if (!need_texture_combine_separate (layer))
+ if (!need_texture_combine_separate (combine_authority))
{
append_masked_combine (material,
layer,
COGL_BLEND_STRING_CHANNEL_MASK_RGBA,
- layer->texture_combine_rgb_func,
- layer->texture_combine_rgb_src,
- layer->texture_combine_rgb_op);
+ big_state->texture_combine_rgb_func,
+ big_state->texture_combine_rgb_src,
+ big_state->texture_combine_rgb_op);
}
- else if (layer->texture_combine_rgb_func == GL_DOT3_RGBA)
+ else if (big_state->texture_combine_rgb_func == GL_DOT3_RGBA)
{
/* GL_DOT3_RGBA Is a bit weird as a GL_COMBINE_RGB function
* since if you use it, it overrides your ALPHA function...
@@ -2415,24 +5534,24 @@ _cogl_material_backend_arbfp_add_layer (CoglMaterialLayer *layer)
append_masked_combine (material,
layer,
COGL_BLEND_STRING_CHANNEL_MASK_RGBA,
- layer->texture_combine_rgb_func,
- layer->texture_combine_rgb_src,
- layer->texture_combine_rgb_op);
+ big_state->texture_combine_rgb_func,
+ big_state->texture_combine_rgb_src,
+ big_state->texture_combine_rgb_op);
}
else
{
append_masked_combine (material,
layer,
COGL_BLEND_STRING_CHANNEL_MASK_RGB,
- layer->texture_combine_rgb_func,
- layer->texture_combine_rgb_src,
- layer->texture_combine_rgb_op);
+ big_state->texture_combine_rgb_func,
+ big_state->texture_combine_rgb_src,
+ big_state->texture_combine_rgb_op);
append_masked_combine (material,
layer,
COGL_BLEND_STRING_CHANNEL_MASK_ALPHA,
- layer->texture_combine_alpha_func,
- layer->texture_combine_alpha_src,
- layer->texture_combine_alpha_op);
+ big_state->texture_combine_alpha_func,
+ big_state->texture_combine_alpha_src,
+ big_state->texture_combine_alpha_op);
}
return TRUE;
@@ -2451,7 +5570,8 @@ _cogl_material_backend_arbfp_passthrough (CoglMaterial *material)
}
static gboolean
-_cogl_material_backend_arbfp_end (CoglMaterial *material)
+_cogl_material_backend_arbfp_end (CoglMaterial *material,
+ unsigned long materials_difference)
{
CoglMaterialBackendARBfpPrivate *priv = material->backend_priv;
@@ -2498,29 +5618,33 @@ _cogl_material_backend_arbfp_end (CoglMaterial *material)
}
static void
-_cogl_material_backend_arbfp_material_change_notify (CoglMaterial *material,
- unsigned long changes,
- GLubyte *new_color)
+_cogl_material_backend_arbfp_material_pre_change_notify (
+ CoglMaterial *material,
+ CoglMaterialState change,
+ const CoglColor *new_color)
{
CoglMaterialBackendARBfpPrivate *priv = material->backend_priv;
static const unsigned long fragment_op_changes =
- COGL_MATERIAL_CHANGE_LAYERS;
- /* TODO: COGL_MATERIAL_CHANGE_FOG */
+ COGL_MATERIAL_STATE_LAYERS;
+ /* TODO: COGL_MATERIAL_STATE_FOG */
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
- if (priv &&
+ if (material->backend_priv_set &&
priv->gl_program &&
- changes & fragment_op_changes)
+ change & fragment_op_changes)
{
+ /* XXX: what if this is the currently bound program? we need to
+ * dirty our cache. */
GE (glDeletePrograms (1, &priv->gl_program));
priv->gl_program = 0;
}
}
static void
-_cogl_material_backend_arbfp_layer_change_notify (CoglMaterialLayer *layer,
- unsigned long changes)
+_cogl_material_backend_arbfp_layer_pre_change_notify (
+ CoglMaterialLayer *layer,
+ CoglMaterialLayerState changes)
{
/* TODO: we could be saving snippets of texture combine code along
* with each layer and then when a layer changes we would just free
@@ -2531,16 +5655,17 @@ _cogl_material_backend_arbfp_layer_change_notify (CoglMaterialLayer *layer,
static void
_cogl_material_backend_arbfp_free_priv (CoglMaterial *material)
{
- CoglMaterialBackendARBfpPrivate *priv = material->backend_priv;
-
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
- if (priv)
+ if (material->backend_priv_set)
{
+ CoglMaterialBackendARBfpPrivate *priv = material->backend_priv;
+
glDeletePrograms (1, &priv->gl_program);
if (priv->sampled)
g_free (priv->sampled);
g_slice_free (CoglMaterialBackendARBfpPrivate, material->backend_priv);
+ material->backend_priv_set = FALSE;
}
}
@@ -2551,9 +5676,10 @@ static const CoglMaterialBackend _cogl_material_arbfp_backend =
_cogl_material_backend_arbfp_add_layer,
_cogl_material_backend_arbfp_passthrough,
_cogl_material_backend_arbfp_end,
- _cogl_material_backend_arbfp_material_change_notify,
- _cogl_material_backend_arbfp_layer_change_notify,
- _cogl_material_backend_arbfp_free_priv
+ _cogl_material_backend_arbfp_material_pre_change_notify,
+ _cogl_material_backend_arbfp_layer_pre_change_notify,
+ _cogl_material_backend_arbfp_free_priv,
+ NULL
};
#endif /* COGL_MATERIAL_BACKEND_ARBFP */
@@ -2576,16 +5702,22 @@ _cogl_material_backend_fixed_get_max_texture_units (void)
}
static gboolean
-_cogl_material_backend_fixed_start (CoglMaterial *material)
+_cogl_material_backend_fixed_start (CoglMaterial *material,
+ int n_layers,
+ unsigned long materials_difference)
{
use_program (COGL_INVALID_HANDLE, COGL_MATERIAL_PROGRAM_TYPE_FIXED);
return TRUE;
}
static gboolean
-_cogl_material_backend_fixed_add_layer (CoglMaterialLayer *layer)
+_cogl_material_backend_fixed_add_layer (CoglMaterial *material,
+ CoglMaterialLayer *layer,
+ unsigned long layers_difference)
{
- CoglTextureUnit *unit = _cogl_get_texture_unit (layer->unit_index);
+ CoglTextureUnit *unit =
+ _cogl_get_texture_unit (_cogl_material_layer_get_unit_index (layer));
+ int unit_index = unit->index;
int n_rgb_func_args;
int n_alpha_func_args;
@@ -2593,26 +5725,28 @@ _cogl_material_backend_fixed_add_layer (CoglMaterialLayer *layer)
/* XXX: Beware that since we are changing the active texture unit we
* must make sure we don't call into other Cogl components that may
- * temporarily bind texture objects to query/modify parameters until
- * we restore texture unit 1 as the active unit. For more details
- * about this see the end of _cogl_material_flush_gl_state
+ * temporarily bind texture objects to query/modify parameters since
+ * they will end up binding texture unit 1. See
+ * _cogl_bind_gl_texture_transient for more details.
*/
- set_active_texture_unit (unit->index);
+ set_active_texture_unit (unit_index);
-#ifndef DISABLE_MATERIAL_CACHE
- if (unit->layer_differences & COGL_MATERIAL_LAYER_DIFFERENCE_COMBINE ||
- layer->differences & COGL_MATERIAL_LAYER_DIFFERENCE_COMBINE)
-#endif
+ if (layers_difference & COGL_MATERIAL_LAYER_STATE_COMBINE)
{
+ CoglMaterialLayer *authority =
+ _cogl_material_layer_get_authority (layer,
+ COGL_MATERIAL_LAYER_STATE_COMBINE);
+ CoglMaterialLayerBigState *big_state = authority->big_state;
+
GE (glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE));
/* Set the combiner functions... */
GE (glTexEnvi (GL_TEXTURE_ENV,
GL_COMBINE_RGB,
- layer->texture_combine_rgb_func));
+ big_state->texture_combine_rgb_func));
GE (glTexEnvi (GL_TEXTURE_ENV,
GL_COMBINE_ALPHA,
- layer->texture_combine_alpha_func));
+ big_state->texture_combine_alpha_func));
/*
* Setup the function arguments...
@@ -2620,68 +5754,69 @@ _cogl_material_backend_fixed_add_layer (CoglMaterialLayer *layer)
/* For the RGB components... */
n_rgb_func_args =
- get_n_args_for_combine_func (layer->texture_combine_rgb_func);
+ get_n_args_for_combine_func (big_state->texture_combine_rgb_func);
GE (glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB,
- layer->texture_combine_rgb_src[0]));
+ big_state->texture_combine_rgb_src[0]));
GE (glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB,
- layer->texture_combine_rgb_op[0]));
+ big_state->texture_combine_rgb_op[0]));
if (n_rgb_func_args > 1)
{
GE (glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_RGB,
- layer->texture_combine_rgb_src[1]));
+ big_state->texture_combine_rgb_src[1]));
GE (glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB,
- layer->texture_combine_rgb_op[1]));
+ big_state->texture_combine_rgb_op[1]));
}
if (n_rgb_func_args > 2)
{
GE (glTexEnvi (GL_TEXTURE_ENV, GL_SRC2_RGB,
- layer->texture_combine_rgb_src[2]));
+ big_state->texture_combine_rgb_src[2]));
GE (glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND2_RGB,
- layer->texture_combine_rgb_op[2]));
+ big_state->texture_combine_rgb_op[2]));
}
/* For the Alpha component */
n_alpha_func_args =
- get_n_args_for_combine_func (layer->texture_combine_alpha_func);
+ get_n_args_for_combine_func (big_state->texture_combine_alpha_func);
GE (glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_ALPHA,
- layer->texture_combine_alpha_src[0]));
+ big_state->texture_combine_alpha_src[0]));
GE (glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA,
- layer->texture_combine_alpha_op[0]));
+ big_state->texture_combine_alpha_op[0]));
if (n_alpha_func_args > 1)
{
GE (glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_ALPHA,
- layer->texture_combine_alpha_src[1]));
+ big_state->texture_combine_alpha_src[1]));
GE (glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_ALPHA,
- layer->texture_combine_alpha_op[1]));
+ big_state->texture_combine_alpha_op[1]));
}
if (n_alpha_func_args > 2)
{
GE (glTexEnvi (GL_TEXTURE_ENV, GL_SRC2_ALPHA,
- layer->texture_combine_alpha_src[2]));
+ big_state->texture_combine_alpha_src[2]));
GE (glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND2_ALPHA,
- layer->texture_combine_alpha_op[2]));
+ big_state->texture_combine_alpha_op[2]));
}
+ }
+
+ if (layers_difference & COGL_MATERIAL_LAYER_STATE_COMBINE)
+ {
+ CoglMaterialLayer *authority =
+ _cogl_material_layer_get_authority (layer,
+ COGL_MATERIAL_LAYER_STATE_COMBINE);
+ CoglMaterialLayerBigState *big_state = authority->big_state;
GE (glTexEnvfv (GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR,
- layer->texture_combine_constant));
+ big_state->texture_combine_constant));
}
return TRUE;
}
static gboolean
-_cogl_material_backend_fixed_end (CoglMaterial *material)
+_cogl_material_backend_fixed_end (CoglMaterial *material,
+ unsigned long materials_difference)
{
- /* There is a convention to always leave texture unit 1 active and
- * since we modify the active unit in
- * _cogl_material_backend_fixed_add_layer we need to restore it
- * here...
- *
- * (See the end of _cogl_material_flush_gl_state for more
- * details) */
- set_active_texture_unit (1);
return TRUE;
}
@@ -2690,65 +5825,26 @@ static const CoglMaterialBackend _cogl_material_fixed_backend =
_cogl_material_backend_fixed_get_max_texture_units,
_cogl_material_backend_fixed_start,
_cogl_material_backend_fixed_add_layer,
- NULL,
+ NULL, /* passthrough */
_cogl_material_backend_fixed_end,
NULL, /* material_change_notify */
NULL, /* layer_change_notify */
NULL /* free_priv */
};
-/* Here we resolve what low level GL texture we are *actually* going
- * to use. This can either be a layer0 override texture, it can be a
- * fallback texture or we can query the CoglTexture for the GL
- * texture.
- */
static void
_cogl_material_layer_get_texture_info (CoglMaterialLayer *layer,
- GLuint layer0_override_texture,
- gboolean fallback,
CoglHandle *texture,
GLuint *gl_texture,
GLuint *gl_target)
{
- gboolean layer0_overridden = layer0_override_texture ? TRUE : FALSE;
-
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
*texture = layer->texture;
- if (G_LIKELY (*texture != COGL_INVALID_HANDLE))
- cogl_texture_get_gl_texture (*texture, gl_texture, gl_target);
- else
- {
- fallback = TRUE;
- *gl_target = GL_TEXTURE_2D;
- }
-
- if (layer0_overridden && layer->unit_index == 0)
- {
- /* We assume that layer0 overrides are only used for sliced
- * textures where the GL texture is actually a sub component
- * of the layer->texture... */
- *texture = layer->texture;
- *gl_texture = layer0_override_texture;
- }
- else if (fallback)
- {
- if (*gl_target == GL_TEXTURE_2D)
- *texture = ctx->default_gl_texture_2d_tex;
-#ifdef HAVE_COGL_GL
- else if (*gl_target == GL_TEXTURE_RECTANGLE_ARB)
- *texture = ctx->default_gl_texture_rect_tex;
-#endif
- else
- {
- g_warning ("We don't have a default texture we can use to fill "
- "in for an invalid material layer, since it was "
- "using an unsupported texture target ");
- /* might get away with this... */
- *texture = ctx->default_gl_texture_2d_tex;
- }
- cogl_texture_get_gl_texture (*texture, gl_texture, NULL);
- }
+ if (G_UNLIKELY (*texture == COGL_INVALID_HANDLE))
+ *texture = ctx->default_gl_texture_2d_tex;
+ cogl_texture_get_gl_texture (*texture, gl_texture, gl_target);
+ return;
}
#ifndef HAVE_COGL_GLES
@@ -2766,47 +5862,51 @@ blend_factor_uses_constant (GLenum blend_factor)
static void
_cogl_material_flush_color_blend_alpha_state (CoglMaterial *material,
- gboolean skip_gl_color)
+ unsigned long materials_difference,
+ gboolean skip_gl_color)
{
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
if (!skip_gl_color)
{
- if (!(ctx->current_material_flags & COGL_MATERIAL_FLAG_DEFAULT_COLOR) ||
- !(material->flags & COGL_MATERIAL_FLAG_DEFAULT_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)
{
- GE (glColor4ub (material->unlit[0],
- material->unlit[1],
- material->unlit[2],
- material->unlit[3]));
+ 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)));
}
}
- /* XXX:
- * Currently we only don't update state when the flags indicate that the
- * current material uses the defaults, and the new material also uses the
- * defaults, but we could do deeper comparisons of state.
- */
-
- if (!(ctx->current_material_flags &
- COGL_MATERIAL_FLAG_DEFAULT_GL_MATERIAL) ||
- !(material->flags & COGL_MATERIAL_FLAG_DEFAULT_GL_MATERIAL))
+ 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... */
- GE (glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT, material->ambient));
- GE (glMaterialfv (GL_FRONT_AND_BACK, GL_DIFFUSE, material->diffuse));
- GE (glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, material->specular));
- GE (glMaterialfv (GL_FRONT_AND_BACK, GL_EMISSION, material->emission));
- GE (glMaterialfv (GL_FRONT_AND_BACK, GL_SHININESS,
- &material->shininess));
+ 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 (!(ctx->current_material_flags & COGL_MATERIAL_FLAG_DEFAULT_BLEND) ||
- !(material->flags & COGL_MATERIAL_FLAG_DEFAULT_BLEND))
+ 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;
@@ -2820,41 +5920,64 @@ _cogl_material_flush_color_blend_alpha_state (CoglMaterial *material,
#endif
#ifndef HAVE_COGL_GLES /* GLES 1 only has glBlendFunc */
- if (have_blend_equation_seperate &&
- material->blend_equation_rgb != material->blend_equation_alpha)
- GE (glBlendEquationSeparate (material->blend_equation_rgb,
- material->blend_equation_alpha));
- else
- GE (glBlendEquation (material->blend_equation_rgb));
+ 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);
- if (blend_factor_uses_constant (material->blend_src_factor_rgb) ||
- blend_factor_uses_constant (material->blend_src_factor_alpha) ||
- blend_factor_uses_constant (material->blend_dst_factor_rgb) ||
- blend_factor_uses_constant (material->blend_dst_factor_alpha))
- GE (glBlendColor (material->blend_constant[0],
- material->blend_constant[1],
- material->blend_constant[2],
- material->blend_constant[3]));
+
+ 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 &&
- (material->blend_src_factor_rgb != material->blend_src_factor_alpha ||
- (material->blend_src_factor_rgb !=
- material->blend_src_factor_alpha)))
- GE (glBlendFuncSeparate (material->blend_src_factor_rgb,
- material->blend_dst_factor_rgb,
- material->blend_src_factor_alpha,
- material->blend_dst_factor_alpha));
+ (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 (material->blend_src_factor_rgb,
- material->blend_dst_factor_rgb));
+ GE (glBlendFunc (blend_state->blend_src_factor_rgb,
+ blend_state->blend_dst_factor_rgb));
}
- if (!(ctx->current_material_flags & COGL_MATERIAL_FLAG_DEFAULT_ALPHA_FUNC) ||
- !(material->flags & COGL_MATERIAL_FLAG_DEFAULT_ALPHA_FUNC))
+ 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 (material->alpha_func, material->alpha_func_reference));
+ GE (glAlphaFunc (alpha_state->alpha_func,
+ alpha_state->alpha_func_reference));
+ }
+
+ if (material->real_blend_enable != ctx->gl_blend_enable_cache)
+ {
+ if (material->real_blend_enable)
+ GE (glEnable (GL_BLEND));
+ else
+ GE (glDisable (GL_BLEND));
+ ctx->gl_blend_enable_cache = material->real_blend_enable;
}
}
@@ -2882,126 +6005,60 @@ get_max_activateable_texture_units (void)
return ctx->max_activateable_texture_units;
}
-/*
- * _cogl_material_flush_common_gl_state:
- * @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.
- */
-static void
-_cogl_material_flush_common_gl_state (CoglMaterial *material,
- gboolean skip_gl_color,
- guint32 fallback_mask,
- guint32 disable_mask,
- GLuint layer0_override_texture,
- const CoglMaterialWrapModeOverrides *
- wrap_mode_overrides)
+typedef struct
{
- GList *l;
- int i;
+ int i;
+ unsigned long *layer_differences;
+} CoglMaterialFlushLayerState;
- _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+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];
- _cogl_material_flush_color_blend_alpha_state (material, skip_gl_color);
-
- for (l = material->layers, i = 0; l != NULL; l = l->next, i++)
+ /* 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 ()))
{
- CoglMaterialLayer *layer = l->data;
- CoglTextureUnit *unit;
- gboolean fallback;
- CoglHandle texture;
- GLuint gl_texture;
- GLenum gl_target;
+ static gboolean shown_warning = FALSE;
- unit = _cogl_get_texture_unit (layer->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 ()))
+ if (!shown_warning)
{
- 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;
- }
- break;
+ g_warning ("Your hardware does not have enough texture units"
+ "to handle this many texture layers");
+ shown_warning = TRUE;
}
+ return FALSE;
+ }
- /* Bail out as soon as we hit a bit set in the disable mask */
- if (G_UNLIKELY (disable_mask & (1<index)))
- break;
+ if (layers_difference & COGL_MATERIAL_LAYER_STATE_TEXTURE)
+ {
+ CoglMaterialLayer *authority =
+ _cogl_material_layer_get_authority (layer,
+ COGL_MATERIAL_LAYER_STATE_TEXTURE);
+ CoglHandle texture;
+ GLuint gl_texture;
+ GLenum gl_target;
- fallback = (fallback_mask & (1<index);
-
- _cogl_material_layer_get_texture_info (layer,
- layer0_override_texture,
- fallback,
+ _cogl_material_layer_get_texture_info (authority,
&texture,
&gl_texture,
&gl_target);
- /* NB: Due to fallbacks texture may not == layer->texture */
- unit->texture = texture;
- unit->layer0_overridden = layer0_override_texture ? TRUE : FALSE;
- unit->fallback = fallback;
+ 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
- * the end of _cogl_material_flush_gl_state() you can see we
- * make sure that such code always binds to texture unit 1 by
- * always leaving texture unit 1 active. This means we can't
- * rely on the unit->gl_texture state if unit->index == 1.
+ * _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().
@@ -3020,59 +6077,94 @@ _cogl_material_flush_common_gl_state (CoglMaterial *material,
* associated with the texture unit then we can't assume that we
* aren't seeing a recycled texture name so we have to bind.
*/
-#ifndef DISABLE_MATERIAL_CACHE
if (unit->gl_texture != gl_texture || unit->is_foreign)
{
- if (unit->index != 1)
+ if (unit_index != 1)
GE (glBindTexture (gl_target, gl_texture));
unit->gl_texture = gl_texture;
}
-#else
- GE (glBindTexture (gl_target, gl_texture));
-#endif
unit->is_foreign = _cogl_texture_is_foreign (texture);
/* Disable the previous target if it was different and it's
* still enabled */
- if (unit->enabled
-#ifndef DISABLE_MATERIAL_CACHE
- && unit->enabled_gl_target != gl_target
-#endif
- )
- GE (glDisable (unit->enabled_gl_target));
+ 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)
-#ifndef DISABLE_MATERIAL_CACHE
- && !(unit->enabled && unit->enabled_gl_target == gl_target)
-#endif
- )
+ 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->enabled_gl_target = gl_target;
+ unit->current_gl_target = gl_target;
}
- if (unit->layer_differences & COGL_MATERIAL_LAYER_DIFFERENCE_USER_MATRIX ||
- layer->differences & COGL_MATERIAL_LAYER_DIFFERENCE_USER_MATRIX)
+ /* 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)
{
- if (layer->differences & COGL_MATERIAL_LAYER_DIFFERENCE_USER_MATRIX)
- _cogl_matrix_stack_set (unit->matrix_stack, &layer->matrix);
- else
- _cogl_matrix_stack_load_identity (unit->matrix_stack);
-
- _cogl_matrix_stack_flush_to_gl (unit->matrix_stack,
- COGL_MATRIX_TEXTURE);
+ GE (glEnable (unit->current_gl_target));
+ unit->enabled = TRUE;
}
}
- /* Disable additional texture units that may have previously been in use.. */
- for (; i < ctx->texture_units->len; i++)
- disable_texture_unit (i);
+ 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);
- /* There is a convention to always leave texture unit 1 active..
- * (See the end of _cogl_material_flush_gl_state for more
- * details) */
- set_active_texture_unit (1);
+ _cogl_matrix_stack_set (unit->matrix_stack,
+ &authority->big_state->matrix);
+
+ _cogl_matrix_stack_flush_to_gl (unit->matrix_stack, COGL_MATRIX_TEXTURE);
+ }
+
+ 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_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++)
+ disable_texture_unit (state.i);
}
/* Re-assert the layer's wrap modes on the given CoglTexture.
@@ -3081,13 +6173,19 @@ _cogl_material_flush_common_gl_state (CoglMaterial *material,
* since the actual texture being used may have been overridden.
*/
static void
-_cogl_material_layer_forward_wrap_modes (
- CoglMaterialLayer *layer,
- const CoglMaterialWrapModeOverrides *wrap_mode_overrides,
- CoglHandle texture)
+_cogl_material_layer_forward_wrap_modes (CoglMaterialLayer *layer,
+ CoglHandle texture)
{
- GLenum wrap_mode_s, wrap_mode_t, wrap_mode_r;
- int unit_index = layer->unit_index;
+ CoglMaterialWrapModeInternal wrap_mode_s, wrap_mode_t, wrap_mode_r;
+ GLenum gl_wrap_mode_s, gl_wrap_mode_t, gl_wrap_mode_r;
+
+ if (texture == COGL_INVALID_HANDLE)
+ return;
+
+ _cogl_material_layer_get_wrap_modes (layer,
+ &wrap_mode_s,
+ &wrap_mode_t,
+ &wrap_mode_r);
/* 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
@@ -3100,49 +6198,25 @@ _cogl_material_layer_forward_wrap_modes (
will break if the application tries to use different modes in
different layers using the same texture. */
- if (wrap_mode_overrides && wrap_mode_overrides->values[unit_index].s)
- wrap_mode_s = (wrap_mode_overrides->values[unit_index].s ==
- COGL_MATERIAL_WRAP_MODE_OVERRIDE_REPEAT ?
- GL_REPEAT :
- wrap_mode_overrides->values[unit_index].s ==
- COGL_MATERIAL_WRAP_MODE_OVERRIDE_CLAMP_TO_EDGE ?
- GL_CLAMP_TO_EDGE :
- GL_CLAMP_TO_BORDER);
- else if (layer->wrap_mode_s == COGL_MATERIAL_WRAP_MODE_AUTOMATIC)
- wrap_mode_s = GL_CLAMP_TO_EDGE;
+ if (wrap_mode_s == COGL_MATERIAL_WRAP_MODE_INTERNAL_AUTOMATIC)
+ gl_wrap_mode_s = GL_CLAMP_TO_EDGE;
else
- wrap_mode_s = layer->wrap_mode_s;
+ gl_wrap_mode_s = wrap_mode_s;
- if (wrap_mode_overrides && wrap_mode_overrides->values[unit_index].t)
- wrap_mode_t = (wrap_mode_overrides->values[unit_index].t ==
- COGL_MATERIAL_WRAP_MODE_OVERRIDE_REPEAT ?
- GL_REPEAT :
- wrap_mode_overrides->values[unit_index].t ==
- COGL_MATERIAL_WRAP_MODE_OVERRIDE_CLAMP_TO_EDGE ?
- GL_CLAMP_TO_EDGE :
- GL_CLAMP_TO_BORDER);
- else if (layer->wrap_mode_t == COGL_MATERIAL_WRAP_MODE_AUTOMATIC)
- wrap_mode_t = GL_CLAMP_TO_EDGE;
+ if (wrap_mode_t == COGL_MATERIAL_WRAP_MODE_INTERNAL_AUTOMATIC)
+ gl_wrap_mode_t = GL_CLAMP_TO_EDGE;
else
- wrap_mode_t = layer->wrap_mode_t;
+ gl_wrap_mode_t = wrap_mode_t;
- if (wrap_mode_overrides && wrap_mode_overrides->values[unit_index].r)
- wrap_mode_r = (wrap_mode_overrides->values[unit_index].r ==
- COGL_MATERIAL_WRAP_MODE_OVERRIDE_REPEAT ?
- GL_REPEAT :
- wrap_mode_overrides->values[unit_index].r ==
- COGL_MATERIAL_WRAP_MODE_OVERRIDE_CLAMP_TO_EDGE ?
- GL_CLAMP_TO_EDGE :
- GL_CLAMP_TO_BORDER);
- else if (layer->wrap_mode_r == COGL_MATERIAL_WRAP_MODE_AUTOMATIC)
- wrap_mode_r = GL_CLAMP_TO_EDGE;
+ if (wrap_mode_r == COGL_MATERIAL_WRAP_MODE_INTERNAL_AUTOMATIC)
+ gl_wrap_mode_r = GL_CLAMP_TO_EDGE;
else
- wrap_mode_r = layer->wrap_mode_r;
+ gl_wrap_mode_r = wrap_mode_r;
_cogl_texture_set_wrap_mode_parameters (texture,
- wrap_mode_s,
- wrap_mode_t,
- wrap_mode_r);
+ gl_wrap_mode_s,
+ gl_wrap_mode_t,
+ gl_wrap_mode_r);
}
/* OpenGL associates the min/mag filters and repeat modes with the
@@ -3154,8 +6228,7 @@ _cogl_material_layer_forward_wrap_modes (
* eventually look at using this extension when available.
*/
static void
-foreach_texture_unit_update_filter_and_wrap_modes (
- const CoglMaterialWrapModeOverrides *wrap_mode_overrides)
+foreach_texture_unit_update_filter_and_wrap_modes (void)
{
int i;
@@ -3166,68 +6239,241 @@ foreach_texture_unit_update_filter_and_wrap_modes (
CoglTextureUnit *unit =
&g_array_index (ctx->texture_units, CoglTextureUnit, i);
- if (unit->enabled)
- {
- /* NB: we can't just look at unit->layer->texture because
- * _cogl_material_flush_gl_state may have chosen to flush a
- * different texture due to fallbacks. */
- _cogl_texture_set_filters (unit->texture,
- unit->layer->min_filter,
- unit->layer->mag_filter);
+ if (!unit->enabled)
+ break;
- _cogl_material_layer_forward_wrap_modes (unit->layer,
- wrap_mode_overrides,
- unit->texture);
+ 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++)
+ 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 (CoglHandle handle,
CoglMaterialFlushOptions *options)
{
- CoglMaterial *material;
- guint32 fallback_layers = 0;
- guint32 disable_layers = 0;
- GLuint layer0_override_texture = 0;
- gboolean skip_gl_color = FALSE;
- const CoglMaterialWrapModeOverrides *wrap_mode_overrides = NULL;
- int i;
- CoglTextureUnit *unit1;
- GList *tmp;
+ CoglMaterial *material;
+ gboolean material_overriden;
+ gboolean skip_gl_color = FALSE;
+ unsigned long materials_difference;
+ int n_layers;
+ unsigned long *layer_differences;
+ 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);
- material = _cogl_material_pointer_from_handle (handle);
+ COGL_TIMER_START (_cogl_uprof_context, material_flush_timer);
- if (options)
+ if (G_UNLIKELY (options &&
+ (options->flags & ~COGL_MATERIAL_FLUSH_SKIP_GL_COLOR)))
{
- if (options->flags & COGL_MATERIAL_FLUSH_FALLBACK_MASK)
- fallback_layers = options->fallback_layers;
- if (options->flags & COGL_MATERIAL_FLUSH_DISABLE_MASK)
- disable_layers = options->disable_layers;
- if (options->flags & COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE)
- layer0_override_texture = options->layer0_override_texture;
- if (options->flags & COGL_MATERIAL_FLUSH_SKIP_GL_COLOR)
- skip_gl_color = TRUE;
- if (options->flags & COGL_MATERIAL_FLUSH_WRAP_MODE_OVERRIDES)
- wrap_mode_overrides = &options->wrap_mode_overrides;
+ /* Create a oneshot material to handle the overrides.
+ *
+ * XXX: Note this is a stop-gap-solution: We are aiming to
+ * remove the overrides mechanism and move code like this out
+ * into the primitives code.
+ *
+ * Overrides were originally necessitated by the previously
+ * large cost of creating derived material, but they made things
+ * more complex and also introduced a limit of 32 layers.
+ *
+ * Although creating derived materials is now much cheaper it
+ * would be much better for primitives APIs to cache these
+ * derived materials as private data on the original material.
+ */
+ material = cogl_material_copy (handle);
+ _cogl_material_apply_overrides (material, options);
+ material_overriden = TRUE;
}
+ else
+ material = COGL_MATERIAL (handle);
- /* If the material we are flushing and the override options are the
- * same then try to bail out as quickly as possible.
- *
- * XXX: the more overrides we add the slower "quickly" will get; I
- * think we need to move towards cheap copy-on-write materials so
- * that exceptional fallbacks/overrides can be implemented simply by
- * copying a material and modifying it before flushing.
- */
- if (ctx->current_material == material &&
- ctx->current_material_fallback_layers == fallback_layers &&
- ctx->current_material_disable_layers == disable_layers &&
- ctx->current_material_layer0_override == layer0_override_texture &&
- ctx->current_material_skip_gl_color == skip_gl_color)
- goto done;
+ if (options && options->flags & COGL_MATERIAL_FLUSH_SKIP_GL_COLOR)
+ skip_gl_color = TRUE;
+
+ 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...
@@ -3248,11 +6494,9 @@ _cogl_material_flush_gl_state (CoglHandle handle,
* updated.
*/
_cogl_material_flush_common_gl_state (material,
- skip_gl_color,
- fallback_layers,
- disable_layers,
- layer0_override_texture,
- wrap_mode_overrides);
+ materials_difference,
+ layer_differences,
+ skip_gl_color);
/* Now flush the fragment processing state according to the current
* fragment processing backend.
@@ -3274,54 +6518,29 @@ _cogl_material_flush_gl_state (CoglHandle handle,
i < G_N_ELEMENTS (backends);
i++, _cogl_material_set_backend (material, i))
{
- const GList *l;
const CoglMaterialBackend *backend = backends[i];
- gboolean added_layer = FALSE;
- gboolean error_adding_layer = FALSE;
+ CoglMaterialBackendAddLayerState state;
/* E.g. For backends generating code they can setup their
* scratch buffers here... */
- if (G_UNLIKELY (!backend->start (material)))
+ if (G_UNLIKELY (!backend->start (material,
+ n_layers,
+ materials_difference)))
continue;
- for (l = cogl_material_get_layers (material); l; l = l->next)
- {
- CoglMaterialLayer *layer = l->data;
- CoglTextureUnit *unit = _cogl_get_texture_unit (layer->unit_index);
+ 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);
- /* 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)
- break;
-
- if (G_UNLIKELY (layer->unit_index >=
- backend->get_max_texture_units ()))
- {
- int j;
- for (j = layer->unit_index; j < ctx->texture_units->len; j++)
- 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. */
- break;
- }
-
- /* Either generate per layer code snippets or setup the
- * fixed function glTexEnv for each layer... */
- if (G_LIKELY (backend->add_layer (layer)))
- added_layer = TRUE;
- else
- {
- error_adding_layer = TRUE;
- break;
- }
- }
-
- if (G_UNLIKELY (error_adding_layer))
+ if (G_UNLIKELY (state.error_adding_layer))
continue;
- if (!added_layer &&
+ if (!state.added_layer &&
backend->passthrough &&
G_UNLIKELY (!backend->passthrough (material)))
continue;
@@ -3330,351 +6549,51 @@ _cogl_material_flush_gl_state (CoglHandle handle,
* programs here, update any uniforms and tell OpenGL to use
* that program.
*/
- if (G_UNLIKELY (!backend->end (material)))
+ if (G_UNLIKELY (!backend->end (material, materials_difference)))
continue;
break;
}
- for (tmp = material->layers; tmp != NULL; tmp = tmp->next)
- {
- CoglMaterialLayer *layer = tmp->data;
- CoglTextureUnit *unit = _cogl_get_texture_unit (layer->unit_index);
-
- unit->layer = layer;
- unit->layer_differences = layer->differences;
- }
-
- /* NB: _cogl_material_pre_change_notify and _cogl_material_free will
- * invalidate ctx->current_material (set it to COGL_INVALID_HANDLE)
- * if the material is changed/freed.
+ /* 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_handle_ref (handle);
+ if (ctx->current_material != COGL_INVALID_HANDLE)
+ cogl_handle_unref (ctx->current_material);
ctx->current_material = handle;
- ctx->current_material_flags = material->flags;
- ctx->current_material_fallback_layers = fallback_layers;
- ctx->current_material_disable_layers = disable_layers;
- ctx->current_material_layer0_override = layer0_override_texture;
+ ctx->current_material_changes_since_flush = 0;
ctx->current_material_skip_gl_color = skip_gl_color;
-done: /* well, almost... */
-
/* 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 (wrap_mode_overrides);
+ 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 the current texture unit so they can query and modify
- * texture object parameters. cogl-material.c will always leave
- * texture unit 1 active so we can ignore these temporary binds
- * unless multitexturing is being used.
+ * 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)
{
set_active_texture_unit (1);
- GE (glBindTexture (unit1->enabled_gl_target, unit1->gl_texture));
+ GE (glBindTexture (unit1->current_gl_target, unit1->gl_texture));
unit1->dirty_gl_texture = FALSE;
}
- /* Since there are several places where Cogl will temporarily bind a
- * GL texture so that it can query or modify texture objects we want
- * to make sure we know which texture unit state is being changed by
- * such code.
- *
- * We choose to always end up with texture unit 1 active so that in
- * the common case where multitexturing isn't used we can simply
- * ignore the state of this texture unit. Notably we didn't use a
- * large texture unit (.e.g. (GL_MAX_TEXTURE_UNITS - 1) in case the
- * driver doesn't have a sparse data structure for texture units.
- */
- set_active_texture_unit (1);
-}
+ if (material_overriden)
+ cogl_handle_unref (material);
-static gboolean
-_cogl_material_texture_equal (CoglHandle texture0, CoglHandle texture1)
-{
- GLenum gl_handle0, gl_handle1, gl_target0, gl_target1;
-
- /* If the texture handles are the same then the textures are
- definitely equal */
- if (texture0 == texture1)
- return TRUE;
-
- /* If neither texture is sliced then they could still be the same if
- the are referring to the same GL texture */
- if (cogl_texture_is_sliced (texture0) ||
- cogl_texture_is_sliced (texture1))
- return FALSE;
-
- cogl_texture_get_gl_texture (texture0, &gl_handle0, &gl_target0);
- cogl_texture_get_gl_texture (texture1, &gl_handle1, &gl_target1);
-
- return gl_handle0 == gl_handle1 && gl_target0 == gl_target1;
-}
-
-static gboolean
-_cogl_material_layer_equal (CoglMaterialLayer *material0_layer,
- CoglHandle material0_layer_texture,
- CoglMaterialLayer *material1_layer,
- CoglHandle material1_layer_texture)
-{
- if (!_cogl_material_texture_equal (material0_layer_texture,
- material1_layer_texture))
- return FALSE;
-
- if ((material0_layer->differences &
- COGL_MATERIAL_LAYER_DIFFERENCE_COMBINE) !=
- (material1_layer->differences &
- COGL_MATERIAL_LAYER_DIFFERENCE_COMBINE))
- return FALSE;
-
-#if 0 /* TODO */
- if (!_deep_are_layer_combines_equal ())
- return FALSE;
-#else
- if (!(material0_layer->differences & COGL_MATERIAL_LAYER_DIFFERENCE_COMBINE))
- return FALSE;
-#endif
-
- if (material0_layer->mag_filter != material1_layer->mag_filter)
- return FALSE;
- if (material0_layer->min_filter != material1_layer->min_filter)
- return FALSE;
-
- if (material0_layer->wrap_mode_s != material1_layer->wrap_mode_s ||
- material0_layer->wrap_mode_t != material1_layer->wrap_mode_t ||
- material0_layer->wrap_mode_r != material1_layer->wrap_mode_r)
- return FALSE;
-
- return TRUE;
-}
-
-/* This is used by the Cogl journal to compare materials so that it
- * can split up geometry that needs different OpenGL state.
- *
- * It is acceptable to have false negatives - although they will result
- * in redundant OpenGL calls that try and update the state.
- *
- * False positives aren't allowed.
- */
-gboolean
-_cogl_material_equal (CoglHandle material0_handle,
- CoglMaterialFlushOptions *material0_flush_options,
- CoglHandle material1_handle,
- CoglMaterialFlushOptions *material1_flush_options)
-{
- CoglMaterial *material0;
- CoglMaterial *material1;
- CoglMaterialFlushFlag flush_flags0 = material0_flush_options->flags;
- CoglMaterialFlushFlag flush_flags1 = material1_flush_options->flags;
- guint32 fallback_layers0;
- guint32 fallback_layers1;
- guint32 disable_layers0;
- guint32 disable_layers1;
- GList *l0, *l1;
- int i;
-
- /* Compare the flush options first; if they are equivalent then we
- * can potentially return quickly if the material handles then match. */
-
-
- /* The skip color option is used when the color of the material is being
- * submitted in a vertex array so cogl_material_flush_gl_state doesn't
- * need to call glColor.
- * - A skip gl color material following a non skip color material doesn't
- * need a state change since putting a color in a vertex array (as done
- * for skip color materials) would simply take precedence over one
- * previously specified by glColor (as done for non skip color materials)
- * - A non skip color material following a skip color material also doesn't
- * need a state change for the same reason.
- * - The problem is that a non skip color, followed by a skip color, followed
- * by a non skip color does require a state change. Since we don't have
- * enough contextual information here we currently return FALSE whenever
- * the skip color option changes. */
- if ((flush_flags0 & COGL_MATERIAL_FLUSH_SKIP_GL_COLOR) !=
- (flush_flags1 & COGL_MATERIAL_FLUSH_SKIP_GL_COLOR))
- return FALSE;
-
- fallback_layers0 = flush_flags0 & COGL_MATERIAL_FLUSH_FALLBACK_MASK ?
- material0_flush_options->fallback_layers : 0;
- fallback_layers1 = flush_flags1 & COGL_MATERIAL_FLUSH_FALLBACK_MASK ?
- material1_flush_options->fallback_layers : 0;
- if (fallback_layers0 != fallback_layers1)
- return FALSE;
-
- disable_layers0 = flush_flags0 & COGL_MATERIAL_FLUSH_DISABLE_MASK ?
- material0_flush_options->disable_layers : 0;
- disable_layers1 = flush_flags1 & COGL_MATERIAL_FLUSH_DISABLE_MASK ?
- material1_flush_options->disable_layers : 0;
- if (disable_layers0 != disable_layers1)
- return FALSE;
-
- /* NB: Some unlikely false negatives are possible here. */
- if ((flush_flags0 & COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE) !=
- (flush_flags1 & COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE))
- return FALSE;
-
- if ((flush_flags0 & COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE) &&
- material0_flush_options->layer0_override_texture !=
- material1_flush_options->layer0_override_texture)
- return FALSE;
-
- /* If one has wrap mode overrides and the other doesn't then the
- materials are different */
- if (((flush_flags0 ^ flush_flags1) & COGL_MATERIAL_FLUSH_WRAP_MODE_OVERRIDES))
- return FALSE;
- /* If they both have overrides then we need to compare them */
- if ((flush_flags0 & COGL_MATERIAL_FLUSH_WRAP_MODE_OVERRIDES) &&
- memcmp (&material0_flush_options->wrap_mode_overrides,
- &material1_flush_options->wrap_mode_overrides,
- sizeof (CoglMaterialWrapModeOverrides)))
- return FALSE;
-
- /* Since we know the flush options match at this point, if the material
- * handles match then we know they are equivalent. */
- if (material0_handle == material1_handle)
- return TRUE;
-
- /* Now we need to look in more detail... */
-
- material0 = _cogl_material_pointer_from_handle (material0_handle);
- material1 = _cogl_material_pointer_from_handle (material1_handle);
-
- if (!(material0_flush_options->flags & COGL_MATERIAL_FLUSH_SKIP_GL_COLOR) &&
- !memcmp (material0->unlit, material1->unlit, sizeof (material0->unlit)))
- return FALSE;
-
- /* First we simply try and find a difference according to default flags
- * for each material component to avoid deeper comparison. */
-
- if ((material0->flags & COGL_MATERIAL_FLAG_DEFAULT_GL_MATERIAL) !=
- (material1->flags & COGL_MATERIAL_FLAG_DEFAULT_GL_MATERIAL))
- return FALSE;
-
- if ((material0->flags & COGL_MATERIAL_FLAG_DEFAULT_ALPHA_FUNC) !=
- (material1->flags & COGL_MATERIAL_FLAG_DEFAULT_ALPHA_FUNC))
- return FALSE;
-
- /* Potentially blending could be "enabled" but the blend mode
- * could be equivalent to being disabled, but we accept those false
- * negatives for now. */
- if ((material0->flags & COGL_MATERIAL_FLAG_ENABLE_BLEND) !=
- (material1->flags & COGL_MATERIAL_FLAG_ENABLE_BLEND))
- return FALSE;
-
- if ((material0->flags & COGL_MATERIAL_FLAG_ENABLE_BLEND) &&
- (material0->flags & COGL_MATERIAL_FLAG_DEFAULT_BLEND) !=
- (material1->flags & COGL_MATERIAL_FLAG_DEFAULT_BLEND))
- return FALSE;
-
- /* If we still haven't found a difference then do a deeper comparison..
- *
- * Actually we don't currently do this; we simply assume anything
- * non default is different and accept the false negatives for now.
- */
-
-#if 0 /* TODO */
- if (!_deep_are_gl_materials_equal ())
- return FALSE;
-#else
- /* Just assume that all non default materials are different */
- if (!(material0->flags & COGL_MATERIAL_FLAG_DEFAULT_GL_MATERIAL))
- return FALSE;
-#endif
-
-#if 0 /* TODO */
- if (!_deep_are_alpha_funcs_equal ())
- return FALSE;
-#else
- /* Just assume that all non default alpha funcs are different */
- if (!(material0->flags & COGL_MATERIAL_FLAG_DEFAULT_ALPHA_FUNC))
- return FALSE;
-#endif
-
- if (material0->flags & COGL_MATERIAL_FLAG_ENABLE_BLEND)
- {
-#if 0 /* TODO */
- if (!_deep_is_blend_equal ())
- return FALSE;
-#else
- if (!(material0->flags & COGL_MATERIAL_FLAG_DEFAULT_BLEND))
- return FALSE;
-#endif
- }
-
-
- /* Finally compare each of the material layers ... */
-
- l0 = material0->layers;
- l1 = material1->layers;
- i = 0;
-
- /* NB: At this point we know if COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE is being
- * used then both materials are overriding with the same texture. */
- if (flush_flags0 & COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE &&
- l0 && l1)
- {
- /* We still need to check if the combine modes etc are equal, but we
- * simply pass COGL_INVALID_HANDLE for both texture handles so they will
- * be considered equal */
- if (!_cogl_material_layer_equal (l0->data, COGL_INVALID_HANDLE,
- l1->data, COGL_INVALID_HANDLE))
- return FALSE;
-
- l0 = l0->next;
- l1 = l1->next;
- i++;
- }
-
- while (l0 && l1)
- {
- CoglMaterialLayer *m0_layer;
- CoglMaterialLayer *m1_layer;
-
- if ((l0 == NULL && l1 != NULL) ||
- (l1 == NULL && l0 != NULL))
- return FALSE;
-
- /* NB: At this point we know that the fallback and disable masks for
- * both materials are equal. */
- if (disable_layers0 & (1<data;
- m1_layer = l1->data;
-
- /* NB: The use of a fallback texture doesn't imply that the combine
- * modes etc are the same.
- */
- if ((disable_layers0 & (1<texture,
- m1_layer, m1_layer->texture))
- return FALSE;
- }
-
-next_layer:
- l0 = l0->next;
- l1 = l1->next;
- i++;
- }
-
- if ((l0 == NULL && l1 != NULL) ||
- (l1 == NULL && l0 != NULL))
- return FALSE;
-
- return TRUE;
+ COGL_TIMER_STOP (_cogl_uprof_context, material_flush_timer);
}
/* While a material is referenced by the Cogl journal we can not allow
@@ -3683,8 +6602,8 @@ next_layer:
CoglHandle
_cogl_material_journal_ref (CoglHandle material_handle)
{
- CoglMaterial *material =
- material = _cogl_material_pointer_from_handle (material_handle);
+ CoglMaterial *material = COGL_MATERIAL (material_handle);
+
material->journal_ref_count++;
cogl_handle_ref (material_handle);
return material_handle;
@@ -3699,161 +6618,6 @@ _cogl_material_journal_unref (CoglHandle material_handle)
cogl_handle_unref (material_handle);
}
-CoglMaterialFilter
-cogl_material_layer_get_min_filter (CoglHandle layer_handle)
-{
- CoglMaterialLayer *layer;
-
- g_return_val_if_fail (cogl_is_material_layer (layer_handle), 0);
-
- layer = _cogl_material_layer_pointer_from_handle (layer_handle);
-
- return layer->min_filter;
-}
-
-CoglMaterialFilter
-cogl_material_layer_get_mag_filter (CoglHandle layer_handle)
-{
- CoglMaterialLayer *layer;
-
- g_return_val_if_fail (cogl_is_material_layer (layer_handle), 0);
-
- layer = _cogl_material_layer_pointer_from_handle (layer_handle);
-
- return layer->mag_filter;
-}
-
-void
-cogl_material_set_layer_filters (CoglHandle handle,
- int layer_index,
- CoglMaterialFilter min_filter,
- CoglMaterialFilter mag_filter)
-{
- CoglMaterial *material;
- CoglMaterialLayer *layer;
-
- g_return_if_fail (cogl_is_material (handle));
-
- material = _cogl_material_pointer_from_handle (handle);
- layer = _cogl_material_get_layer (material, layer_index, TRUE);
-
- /* possibly flush primitives referencing the current state... */
- _cogl_material_layer_pre_change_notify (
- layer,
- COGL_MATERIAL_LAYER_CHANGE_FILTERS);
-
- layer->min_filter = min_filter;
- layer->mag_filter = mag_filter;
-
- /* Note we don't have a layer->difference flag for the min/mag
- * filters since in GL terms this state is owned by the texture
- * object so they are dealt with slightly differently. */
-}
-
-void
-cogl_material_set_layer_wrap_mode_s (CoglHandle handle,
- int layer_index,
- CoglMaterialWrapMode mode)
-{
- CoglMaterial *material;
- CoglMaterialLayer *layer;
-
- g_return_if_fail (cogl_is_material (handle));
-
- material = _cogl_material_pointer_from_handle (handle);
- layer = _cogl_material_get_layer (material, layer_index, TRUE);
-
- if (layer->wrap_mode_s != mode)
- {
- /* possibly flush primitives referencing the current state... */
- _cogl_material_pre_change_notify (material, FALSE, NULL);
-
- layer->wrap_mode_s = mode;
- }
-}
-
-void
-cogl_material_set_layer_wrap_mode_t (CoglHandle handle,
- int layer_index,
- CoglMaterialWrapMode mode)
-{
- CoglMaterial *material;
- CoglMaterialLayer *layer;
-
- g_return_if_fail (cogl_is_material (handle));
-
- material = _cogl_material_pointer_from_handle (handle);
- layer = _cogl_material_get_layer (material, layer_index, TRUE);
-
- if (layer->wrap_mode_t != mode)
- {
- /* possibly flush primitives referencing the current state... */
- _cogl_material_pre_change_notify (material, FALSE, NULL);
-
- layer->wrap_mode_t = mode;
- }
-}
-
-/* TODO: this should be made public once we add support for 3D
- textures in Cogl */
-void
-_cogl_material_set_layer_wrap_mode_r (CoglHandle handle,
- int layer_index,
- CoglMaterialWrapMode mode)
-{
- CoglMaterial *material;
- CoglMaterialLayer *layer;
-
- g_return_if_fail (cogl_is_material (handle));
-
- material = _cogl_material_pointer_from_handle (handle);
- layer = _cogl_material_get_layer (material, layer_index, TRUE);
-
- if (layer->wrap_mode_r != mode)
- {
- /* possibly flush primitives referencing the current state... */
- _cogl_material_pre_change_notify (material, FALSE, NULL);
-
- layer->wrap_mode_r = mode;
- }
-}
-
-void
-cogl_material_set_layer_wrap_mode (CoglHandle material,
- int layer_index,
- CoglMaterialWrapMode mode)
-{
- cogl_material_set_layer_wrap_mode_s (material, layer_index, mode);
- cogl_material_set_layer_wrap_mode_t (material, layer_index, mode);
- _cogl_material_set_layer_wrap_mode_r (material, layer_index, mode);
-}
-
-CoglMaterialWrapMode
-cogl_material_layer_get_wrap_mode_s (CoglHandle handle)
-{
- g_return_val_if_fail (cogl_is_material_layer (handle), FALSE);
-
- return _cogl_material_layer_pointer_from_handle (handle)->wrap_mode_s;
-}
-
-CoglMaterialWrapMode
-cogl_material_layer_get_wrap_mode_t (CoglHandle handle)
-{
- g_return_val_if_fail (cogl_is_material_layer (handle), FALSE);
-
- return _cogl_material_layer_pointer_from_handle (handle)->wrap_mode_t;
-}
-
-/* TODO: this should be made public once we add support for 3D
- textures in Cogl */
-CoglMaterialWrapMode
-_cogl_material_layer_get_wrap_mode_r (CoglHandle handle)
-{
- g_return_val_if_fail (cogl_is_material_layer (handle), FALSE);
-
- return _cogl_material_layer_pointer_from_handle (handle)->wrap_mode_r;
-}
-
void
_cogl_material_apply_legacy_state (CoglHandle handle)
{
diff --git a/clutter/cogl/cogl/cogl-material.h b/clutter/cogl/cogl/cogl-material.h
index 425c7f77d..12c9b39a0 100644
--- a/clutter/cogl/cogl/cogl-material.h
+++ b/clutter/cogl/cogl/cogl-material.h
@@ -106,7 +106,11 @@ typedef enum {
* Since: 1.4
*/
/* GL_ALWAYS is just used here as a value that is known not to clash
- with any valid GL wrap modes */
+ * with any valid GL wrap modes
+ *
+ * XXX: keep the values in sync with the CoglMaterialWrapModeInternal
+ * enum so no conversion is actually needed.
+ */
typedef enum {
COGL_MATERIAL_WRAP_MODE_REPEAT = GL_REPEAT,
COGL_MATERIAL_WRAP_MODE_CLAMP_TO_EDGE = GL_CLAMP_TO_EDGE,
diff --git a/clutter/cogl/cogl/cogl-path.c b/clutter/cogl/cogl/cogl-path.c
index 46c6d59ef..e1c3b8f4d 100644
--- a/clutter/cogl/cogl/cogl-path.c
+++ b/clutter/cogl/cogl/cogl-path.c
@@ -145,7 +145,6 @@ _cogl_path_stroke_nodes (void)
* always be done first when preparing to draw. */
_cogl_framebuffer_flush_state (_cogl_get_framebuffer (), 0);
- enable_flags |= _cogl_material_get_cogl_enable_flags (ctx->source_material);
_cogl_enable (enable_flags);
options.flags = COGL_MATERIAL_FLUSH_DISABLE_MASK;
@@ -234,8 +233,6 @@ _cogl_add_path_to_stencil_buffer (CoglPath *path,
_cogl_material_flush_gl_state (ctx->source_material, NULL);
- enable_flags |=
- _cogl_material_get_cogl_enable_flags (ctx->source_material);
_cogl_enable (enable_flags);
GE( glEnable (GL_STENCIL_TEST) );
@@ -394,8 +391,8 @@ _cogl_path_fill_nodes_scanlines (CoglPathNode *path,
_cogl_material_flush_gl_state (ctx->source_material, NULL);
- _cogl_enable (COGL_ENABLE_VERTEX_ARRAY
- | (ctx->color_alpha < 255 ? COGL_ENABLE_BLEND : 0));
+ _cogl_enable (COGL_ENABLE_VERTEX_ARRAY);
+
/* clear scanline intersection lists */
for (i = 0; i < bounds_h; i++)
diff --git a/clutter/cogl/cogl/cogl-primitives.c b/clutter/cogl/cogl/cogl-primitives.c
index 84a498596..b24ff3af4 100644
--- a/clutter/cogl/cogl/cogl-primitives.c
+++ b/clutter/cogl/cogl/cogl-primitives.c
@@ -1073,6 +1073,8 @@ cogl_polygon (const CoglTextureVertex *vertices,
GLfloat *v;
CoglMaterialWrapModeOverrides wrap_mode_overrides;
CoglMaterialWrapModeOverrides *wrap_mode_overrides_p = NULL;
+ CoglHandle original_source_material;
+ gboolean overrode_material = FALSE;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
@@ -1206,18 +1208,31 @@ cogl_polygon (const CoglTextureVertex *vertices,
/* Prepare GL state */
enable_flags = COGL_ENABLE_VERTEX_ARRAY;
- enable_flags |= _cogl_material_get_cogl_enable_flags (ctx->source_material);
if (ctx->enable_backface_culling)
enable_flags |= COGL_ENABLE_BACKFACE_CULLING;
if (use_color)
{
- enable_flags |= COGL_ENABLE_COLOR_ARRAY | COGL_ENABLE_BLEND;
+ CoglHandle override;
+ enable_flags |= COGL_ENABLE_COLOR_ARRAY;
GE( glColorPointer (4, GL_UNSIGNED_BYTE,
stride_bytes,
/* NB: [X,Y,Z,TX,TY...,R,G,B,A,...] */
v + 3 + 2 * n_layers) );
+
+ if (!_cogl_material_get_real_blend_enabled (ctx->source_material))
+ {
+ CoglMaterialBlendEnable blend_enabled =
+ COGL_MATERIAL_BLEND_ENABLE_ENABLED;
+ original_source_material = ctx->source_material;
+ override = cogl_material_copy (original_source_material);
+ _cogl_material_set_blend_enabled (override, blend_enabled);
+
+ /* XXX: cogl_push_source () */
+ overrode_material = TRUE;
+ ctx->source_material = override;
+ }
}
_cogl_enable (enable_flags);
@@ -1253,6 +1268,16 @@ cogl_polygon (const CoglTextureVertex *vertices,
fallback_layers,
wrap_mode_overrides_p);
+ /* XXX: cogl_pop_source () */
+ if (overrode_material)
+ {
+ cogl_handle_unref (ctx->source_material);
+ ctx->source_material = original_source_material;
+ /* XXX: when we have weak materials then any override material
+ * should get associated with the original material so we don't
+ * create lots of one-shot materials! */
+ }
+
/* Reset the size of the logged vertex array because rendering
rectangles expects it to start at 0 */
g_array_set_size (ctx->logged_vertices, 0);
diff --git a/clutter/cogl/cogl/cogl-vertex-buffer.c b/clutter/cogl/cogl/cogl-vertex-buffer.c
index 0c9b7569c..a4af74f89 100644
--- a/clutter/cogl/cogl/cogl-vertex-buffer.c
+++ b/clutter/cogl/cogl/cogl-vertex-buffer.c
@@ -1525,6 +1525,8 @@ enable_state_for_drawing_buffer (CoglVertexBuffer *buffer)
_COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE);
+ source = ctx->source_material;
+
if (buffer->new_attributes)
cogl_vertex_buffer_submit_real (buffer);
@@ -1572,13 +1574,21 @@ enable_state_for_drawing_buffer (CoglVertexBuffer *buffer)
switch (type)
{
case COGL_VERTEX_BUFFER_ATTRIB_FLAG_COLOR_ARRAY:
- enable_flags |= COGL_ENABLE_COLOR_ARRAY | COGL_ENABLE_BLEND;
+ enable_flags |= COGL_ENABLE_COLOR_ARRAY;
/* GE (glEnableClientState (GL_COLOR_ARRAY)); */
pointer = (const GLvoid *)(base + attribute->u.vbo_offset);
GE (glColorPointer (attribute->n_components,
gl_type,
attribute->stride,
pointer));
+
+ if (!_cogl_material_get_real_blend_enabled (ctx->source_material))
+ {
+ CoglMaterialBlendEnable blend_enable =
+ COGL_MATERIAL_BLEND_ENABLE_ENABLED;
+ source = cogl_material_copy (ctx->source_material);
+ _cogl_material_set_blend_enabled (source, blend_enable);
+ }
break;
case COGL_VERTEX_BUFFER_ATTRIB_FLAG_NORMAL_ARRAY:
/* FIXME: go through cogl cache to enable normal array */
@@ -1634,7 +1644,7 @@ enable_state_for_drawing_buffer (CoglVertexBuffer *buffer)
}
}
- layers = cogl_material_get_layers (ctx->source_material);
+ layers = cogl_material_get_layers (source);
for (tmp = (GList *)layers, i = 0;
tmp != NULL;
tmp = tmp->next, i++)
@@ -1719,14 +1729,13 @@ enable_state_for_drawing_buffer (CoglVertexBuffer *buffer)
if (G_UNLIKELY (ctx->legacy_state_set))
{
- source = cogl_material_copy (ctx->source_material);
+ /* If we haven't already created a derived material... */
+ if (source == ctx->source_material)
+ source = cogl_material_copy (ctx->source_material);
_cogl_material_apply_legacy_state (source);
}
- else
- source = ctx->source_material;
_cogl_material_flush_gl_state (source, &options);
- enable_flags |= _cogl_material_get_cogl_enable_flags (source);
if (ctx->enable_backface_culling)
enable_flags |= COGL_ENABLE_BACKFACE_CULLING;
diff --git a/clutter/cogl/cogl/cogl.c b/clutter/cogl/cogl/cogl.c
index 617ed9182..41ccad873 100644
--- a/clutter/cogl/cogl/cogl.c
+++ b/clutter/cogl/cogl/cogl.c
@@ -266,10 +266,6 @@ _cogl_enable (unsigned long flags)
*/
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
- toggle_flag (ctx, flags,
- COGL_ENABLE_BLEND,
- GL_BLEND);
-
toggle_flag (ctx, flags,
COGL_ENABLE_BACKFACE_CULLING,
GL_CULL_FACE);
@@ -858,10 +854,6 @@ cogl_begin_gl (void)
options.flags = 0;
_cogl_material_flush_gl_state (ctx->source_material, &options);
- /* FIXME: This api is a bit yukky, ideally it will be removed if we
- * re-work the _cogl_enable mechanism */
- enable_flags |= _cogl_material_get_cogl_enable_flags (ctx->source_material);
-
if (ctx->enable_backface_culling)
enable_flags |= COGL_ENABLE_BACKFACE_CULLING;