diff options
author | Robert Bragg <robert@linux.intel.com> | 2011-09-08 00:42:22 +0100 |
---|---|---|
committer | Robert Bragg <robert@linux.intel.com> | 2011-09-19 16:40:00 +0100 |
commit | db6c452aaa7920c418ba90418fd047d3c53804d0 (patch) | |
tree | bad6b3c99387b99053804d763c24c8a47e114e18 /cogl/cogl-pipeline-layer-state.c | |
parent | 9b58b6f4729b7d359a5b01f47bb2f7647c977d98 (diff) | |
download | cogl-db6c452aaa7920c418ba90418fd047d3c53804d0.tar.gz |
pipeline: split out all layer state apis
As part of an on-going effort to get cogl-pipeline.c into a more
maintainable state this splits out all the apis relating just to
layer state. This just leaves code relating to the core CoglPipeline
and CoglPipelineLayer design left in cogl-pipeline.c.
This splits out around 2k more lines from cogl-pipeline.c although we
are still left with nearly 4k lines so we still have some way to go!
Reviewed-by: Neil Roberts <neil@linux.intel.com>
Diffstat (limited to 'cogl/cogl-pipeline-layer-state.c')
-rw-r--r-- | cogl/cogl-pipeline-layer-state.c | 1664 |
1 files changed, 1664 insertions, 0 deletions
diff --git a/cogl/cogl-pipeline-layer-state.c b/cogl/cogl-pipeline-layer-state.c new file mode 100644 index 00000000..ae897827 --- /dev/null +++ b/cogl/cogl-pipeline-layer-state.c @@ -0,0 +1,1664 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2008,2009,2010 Intel Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * + * + * Authors: + * Robert Bragg <robert@linux.intel.com> + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl-pipeline-private.h" +#include "cogl-blend-string.h" +#include "cogl-util.h" +#include "cogl-matrix.h" + +#include "string.h" +#if 0 +#include "cogl-context-private.h" +#include "cogl-color-private.h" + +#endif + +/* + * XXX: consider special casing layer->unit_index so it's not a sparse + * property so instead we can assume it's valid for all layer + * instances. + * - We would need to initialize ->unit_index in + * _cogl_pipeline_layer_copy (). + * + * XXX: If you use this API you should consider that the given layer + * might not be writeable and so a new derived layer will be allocated + * and modified instead. The layer modified will be returned so you + * can identify when this happens. + */ +CoglPipelineLayer * +_cogl_pipeline_set_layer_unit (CoglPipeline *required_owner, + CoglPipelineLayer *layer, + int unit_index) +{ + CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_UNIT; + CoglPipelineLayer *authority = + _cogl_pipeline_layer_get_authority (layer, change); + CoglPipelineLayer *new; + + if (authority->unit_index == unit_index) + return layer; + + new = + _cogl_pipeline_layer_pre_change_notify (required_owner, + layer, + change); + if (new != layer) + layer = new; + else + { + /* If the layer we found is currently the authority on the state + * we are changing see if we can revert to one of our ancestors + * being the authority. */ + if (layer == authority && + _cogl_pipeline_layer_get_parent (authority) != NULL) + { + CoglPipelineLayer *parent = + _cogl_pipeline_layer_get_parent (authority); + CoglPipelineLayer *old_authority = + _cogl_pipeline_layer_get_authority (parent, change); + + if (old_authority->unit_index == unit_index) + { + layer->differences &= ~change; + return layer; + } + } + } + + layer->unit_index = unit_index; + + /* If we weren't previously the authority on this state then we need + * to extended our differences mask and so it's possible that some + * of our ancestry will now become redundant, so we aim to reparent + * ourselves if that's true... */ + if (layer != authority) + { + layer->differences |= change; + _cogl_pipeline_layer_prune_redundant_ancestry (layer); + } + + return layer; +} + +CoglHandle +_cogl_pipeline_layer_get_texture_real (CoglPipelineLayer *layer) +{ + CoglPipelineLayer *authority = + _cogl_pipeline_layer_get_authority (layer, + COGL_PIPELINE_LAYER_STATE_TEXTURE_DATA); + + return authority->texture; +} + +CoglHandle +_cogl_pipeline_get_layer_texture (CoglPipeline *pipeline, + int layer_index) +{ + CoglPipelineLayer *layer = + _cogl_pipeline_get_layer (pipeline, layer_index); + return _cogl_pipeline_layer_get_texture (layer); +} + +static void +_cogl_pipeline_set_layer_texture_target (CoglPipeline *pipeline, + int layer_index, + GLenum target) +{ + CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_TEXTURE_TARGET; + CoglPipelineLayer *layer; + CoglPipelineLayer *authority; + CoglPipelineLayer *new; + + /* Note: this will ensure that the layer exists, creating one if it + * doesn't already. + * + * Note: If the layer already existed it's possibly owned by another + * pipeline. If the layer is created then it will be owned by + * pipeline. */ + layer = _cogl_pipeline_get_layer (pipeline, layer_index); + + /* Now find the ancestor of the layer that is the authority for the + * state we want to change */ + authority = _cogl_pipeline_layer_get_authority (layer, change); + + if (target == authority->target) + return; + + new = _cogl_pipeline_layer_pre_change_notify (pipeline, layer, change); + if (new != layer) + layer = new; + else + { + /* If the original layer we found is currently the authority on + * the state we are changing see if we can revert to one of our + * ancestors being the authority. */ + if (layer == authority && + _cogl_pipeline_layer_get_parent (authority) != NULL) + { + CoglPipelineLayer *parent = + _cogl_pipeline_layer_get_parent (authority); + CoglPipelineLayer *old_authority = + _cogl_pipeline_layer_get_authority (parent, change); + + if (old_authority->target == target) + { + layer->differences &= ~change; + + g_assert (layer->owner == pipeline); + if (layer->differences == 0) + _cogl_pipeline_prune_empty_layer_difference (pipeline, + layer); + goto changed; + } + } + } + + layer->target = target; + + /* If we weren't previously the authority on this state then we need + * to extended our differences mask and so it's possible that some + * of our ancestry will now become redundant, so we aim to reparent + * ourselves if that's true... */ + if (layer != authority) + { + layer->differences |= change; + _cogl_pipeline_layer_prune_redundant_ancestry (layer); + } + +changed: + + _cogl_pipeline_update_blend_enable (pipeline, COGL_PIPELINE_STATE_LAYERS); +} + +static void +_cogl_pipeline_set_layer_texture_data (CoglPipeline *pipeline, + int layer_index, + CoglHandle texture) +{ + CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_TEXTURE_DATA; + CoglPipelineLayer *layer; + CoglPipelineLayer *authority; + CoglPipelineLayer *new; + + /* Note: this will ensure that the layer exists, creating one if it + * doesn't already. + * + * Note: If the layer already existed it's possibly owned by another + * pipeline. If the layer is created then it will be owned by + * pipeline. */ + layer = _cogl_pipeline_get_layer (pipeline, layer_index); + + /* Now find the ancestor of the layer that is the authority for the + * state we want to change */ + authority = _cogl_pipeline_layer_get_authority (layer, change); + + if (authority->texture == texture) + return; + + new = _cogl_pipeline_layer_pre_change_notify (pipeline, layer, change); + if (new != layer) + layer = new; + else + { + /* If the original layer we found is currently the authority on + * the state we are changing see if we can revert to one of our + * ancestors being the authority. */ + if (layer == authority && + _cogl_pipeline_layer_get_parent (authority) != NULL) + { + CoglPipelineLayer *parent = + _cogl_pipeline_layer_get_parent (authority); + CoglPipelineLayer *old_authority = + _cogl_pipeline_layer_get_authority (parent, change); + + if (old_authority->texture == texture) + { + layer->differences &= ~change; + + if (layer->texture != NULL) + cogl_object_unref (layer->texture); + + g_assert (layer->owner == pipeline); + if (layer->differences == 0) + _cogl_pipeline_prune_empty_layer_difference (pipeline, + layer); + goto changed; + } + } + } + + if (texture != NULL) + cogl_object_ref (texture); + if (layer == authority && + layer->texture != NULL) + cogl_object_unref (layer->texture); + layer->texture = texture; + + /* If we weren't previously the authority on this state then we need + * to extended our differences mask and so it's possible that some + * of our ancestry will now become redundant, so we aim to reparent + * ourselves if that's true... */ + if (layer != authority) + { + layer->differences |= change; + _cogl_pipeline_layer_prune_redundant_ancestry (layer); + } + +changed: + + _cogl_pipeline_update_blend_enable (pipeline, COGL_PIPELINE_STATE_LAYERS); +} + +/* A convenience for querying the target of a given texture that + * notably returns 0 for NULL textures - so we can say that a layer + * with no associated CoglTexture will have a texture target of 0. + */ +static GLenum +get_texture_target (CoglHandle texture) +{ + GLuint ignore_handle; + GLenum gl_target; + + g_return_val_if_fail (texture, 0); + + cogl_texture_get_gl_texture (texture, &ignore_handle, &gl_target); + + return gl_target; +} + +void +cogl_pipeline_set_layer_texture (CoglPipeline *pipeline, + int layer_index, + CoglHandle texture) +{ + /* For the convenience of fragend code we separate texture state + * into the "target" and the "data", and setting a layer texture + * updates both of these properties. + * + * One example for why this is helpful is that the fragends may + * cache programs they generate and want to re-use those programs + * with all pipelines having equivalent fragment processing state. + * For the sake of determining if pipelines have equivalent fragment + * processing state we don't need to compare that the same + * underlying texture objects are referenced by the pipelines but we + * do need to see if they use the same texture targets. Making this + * distinction is much simpler if they are in different state + * groups. + * + * Note: if a NULL texture is set then we leave the target unchanged + * so we can avoid needlessly invalidating any associated fragment + * program. + */ + if (texture) + _cogl_pipeline_set_layer_texture_target (pipeline, layer_index, + get_texture_target (texture)); + _cogl_pipeline_set_layer_texture_data (pipeline, layer_index, texture); +} + +void +_cogl_pipeline_set_layer_wrap_modes (CoglPipeline *pipeline, + CoglPipelineLayer *layer, + CoglPipelineLayer *authority, + CoglPipelineWrapModeInternal wrap_mode_s, + CoglPipelineWrapModeInternal wrap_mode_t, + CoglPipelineWrapModeInternal wrap_mode_p) +{ + CoglPipelineLayer *new; + CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_WRAP_MODES; + + if (authority->wrap_mode_s == wrap_mode_s && + authority->wrap_mode_t == wrap_mode_t && + authority->wrap_mode_p == wrap_mode_p) + return; + + new = _cogl_pipeline_layer_pre_change_notify (pipeline, layer, change); + if (new != layer) + layer = new; + else + { + /* If the original layer we found is currently the authority on + * the state we are changing see if we can revert to one of our + * ancestors being the authority. */ + if (layer == authority && + _cogl_pipeline_layer_get_parent (authority) != NULL) + { + CoglPipelineLayer *parent = + _cogl_pipeline_layer_get_parent (authority); + CoglPipelineLayer *old_authority = + _cogl_pipeline_layer_get_authority (parent, change); + + if (old_authority->wrap_mode_s == wrap_mode_s && + old_authority->wrap_mode_t == wrap_mode_t && + old_authority->wrap_mode_p == wrap_mode_p) + { + layer->differences &= ~change; + + g_assert (layer->owner == pipeline); + if (layer->differences == 0) + _cogl_pipeline_prune_empty_layer_difference (pipeline, + layer); + return; + } + } + } + + layer->wrap_mode_s = wrap_mode_s; + layer->wrap_mode_t = wrap_mode_t; + layer->wrap_mode_p = wrap_mode_p; + + /* If we weren't previously the authority on this state then we need + * to extended our differences mask and so it's possible that some + * of our ancestry will now become redundant, so we aim to reparent + * ourselves if that's true... */ + if (layer != authority) + { + layer->differences |= change; + _cogl_pipeline_layer_prune_redundant_ancestry (layer); + } +} + +static CoglPipelineWrapModeInternal +public_to_internal_wrap_mode (CoglPipelineWrapMode mode) +{ + return (CoglPipelineWrapModeInternal)mode; +} + +static CoglPipelineWrapMode +internal_to_public_wrap_mode (CoglPipelineWrapModeInternal internal_mode) +{ + g_return_val_if_fail (internal_mode != + COGL_PIPELINE_WRAP_MODE_INTERNAL_CLAMP_TO_BORDER, + COGL_PIPELINE_WRAP_MODE_AUTOMATIC); + return (CoglPipelineWrapMode)internal_mode; +} + +void +cogl_pipeline_set_layer_wrap_mode_s (CoglPipeline *pipeline, + int layer_index, + CoglPipelineWrapMode mode) +{ + CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_WRAP_MODES; + CoglPipelineLayer *layer; + CoglPipelineLayer *authority; + CoglPipelineWrapModeInternal internal_mode = + public_to_internal_wrap_mode (mode); + + g_return_if_fail (cogl_is_pipeline (pipeline)); + + /* Note: this will ensure that the layer exists, creating one if it + * doesn't already. + * + * Note: If the layer already existed it's possibly owned by another + * pipeline. If the layer is created then it will be owned by + * pipeline. */ + layer = _cogl_pipeline_get_layer (pipeline, layer_index); + + /* Now find the ancestor of the layer that is the authority for the + * state we want to change */ + authority = _cogl_pipeline_layer_get_authority (layer, change); + + _cogl_pipeline_set_layer_wrap_modes (pipeline, layer, authority, + internal_mode, + authority->wrap_mode_t, + authority->wrap_mode_p); +} + +void +cogl_pipeline_set_layer_wrap_mode_t (CoglPipeline *pipeline, + int layer_index, + CoglPipelineWrapMode mode) +{ + CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_WRAP_MODES; + CoglPipelineLayer *layer; + CoglPipelineLayer *authority; + CoglPipelineWrapModeInternal internal_mode = + public_to_internal_wrap_mode (mode); + + g_return_if_fail (cogl_is_pipeline (pipeline)); + + /* Note: this will ensure that the layer exists, creating one if it + * doesn't already. + * + * Note: If the layer already existed it's possibly owned by another + * pipeline. If the layer is created then it will be owned by + * pipeline. */ + layer = _cogl_pipeline_get_layer (pipeline, layer_index); + + /* Now find the ancestor of the layer that is the authority for the + * state we want to change */ + authority = _cogl_pipeline_layer_get_authority (layer, change); + + _cogl_pipeline_set_layer_wrap_modes (pipeline, layer, authority, + authority->wrap_mode_s, + internal_mode, + authority->wrap_mode_p); +} + +/* The rationale for naming the third texture coordinate 'p' instead + of OpenGL's usual 'r' is that 'r' conflicts with the usual naming + of the 'red' component when treating a vector as a color. Under + GLSL this is awkward because the texture swizzling for a vector + uses a single letter for each component and the names for colors, + textures and positions are synonymous. GLSL works around this by + naming the components of the texture s, t, p and q. Cogl already + effectively already exposes this naming because it exposes GLSL so + it makes sense to use that naming consistently. Another alternative + could be u, v and w. This is what Blender and Direct3D use. However + the w component conflicts with the w component of a position + vertex. */ +void +cogl_pipeline_set_layer_wrap_mode_p (CoglPipeline *pipeline, + int layer_index, + CoglPipelineWrapMode mode) +{ + CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_WRAP_MODES; + CoglPipelineLayer *layer; + CoglPipelineLayer *authority; + CoglPipelineWrapModeInternal internal_mode = + public_to_internal_wrap_mode (mode); + + g_return_if_fail (cogl_is_pipeline (pipeline)); + + /* Note: this will ensure that the layer exists, creating one if it + * doesn't already. + * + * Note: If the layer already existed it's possibly owned by another + * pipeline. If the layer is created then it will be owned by + * pipeline. */ + layer = _cogl_pipeline_get_layer (pipeline, layer_index); + + /* Now find the ancestor of the layer that is the authority for the + * state we want to change */ + authority = _cogl_pipeline_layer_get_authority (layer, change); + + _cogl_pipeline_set_layer_wrap_modes (pipeline, layer, authority, + authority->wrap_mode_s, + authority->wrap_mode_t, + internal_mode); +} + +void +cogl_pipeline_set_layer_wrap_mode (CoglPipeline *pipeline, + int layer_index, + CoglPipelineWrapMode mode) +{ + CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_WRAP_MODES; + CoglPipelineLayer *layer; + CoglPipelineLayer *authority; + CoglPipelineWrapModeInternal internal_mode = + public_to_internal_wrap_mode (mode); + + g_return_if_fail (cogl_is_pipeline (pipeline)); + + /* Note: this will ensure that the layer exists, creating one if it + * doesn't already. + * + * Note: If the layer already existed it's possibly owned by another + * pipeline. If the layer is created then it will be owned by + * pipeline. */ + layer = _cogl_pipeline_get_layer (pipeline, layer_index); + + /* Now find the ancestor of the layer that is the authority for the + * state we want to change */ + authority = _cogl_pipeline_layer_get_authority (layer, change); + + _cogl_pipeline_set_layer_wrap_modes (pipeline, layer, authority, + internal_mode, + internal_mode, + internal_mode); + /* XXX: I wonder if we should really be duplicating the mode into + * the 'r' wrap mode too? */ +} + +/* FIXME: deprecate this API */ +CoglPipelineWrapMode +_cogl_pipeline_layer_get_wrap_mode_s (CoglPipelineLayer *layer) +{ + CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_WRAP_MODES; + CoglPipelineLayer *authority; + + g_return_val_if_fail (_cogl_is_pipeline_layer (layer), FALSE); + + /* Now find the ancestor of the layer that is the authority for the + * state we want to change */ + authority = _cogl_pipeline_layer_get_authority (layer, change); + + return internal_to_public_wrap_mode (authority->wrap_mode_s); +} + +CoglPipelineWrapMode +cogl_pipeline_get_layer_wrap_mode_s (CoglPipeline *pipeline, int layer_index) +{ + CoglPipelineLayer *layer; + + g_return_val_if_fail (cogl_is_pipeline (pipeline), FALSE); + + /* Note: this will ensure that the layer exists, creating one if it + * doesn't already. + * + * Note: If the layer already existed it's possibly owned by another + * pipeline. If the layer is created then it will be owned by + * pipeline. */ + layer = _cogl_pipeline_get_layer (pipeline, layer_index); + /* FIXME: we shouldn't ever construct a layer in a getter function */ + + return _cogl_pipeline_layer_get_wrap_mode_s (layer); +} + +/* FIXME: deprecate this API */ +CoglPipelineWrapMode +_cogl_pipeline_layer_get_wrap_mode_t (CoglPipelineLayer *layer) +{ + CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_WRAP_MODES; + CoglPipelineLayer *authority; + + g_return_val_if_fail (_cogl_is_pipeline_layer (layer), FALSE); + + /* Now find the ancestor of the layer that is the authority for the + * state we want to change */ + authority = _cogl_pipeline_layer_get_authority (layer, change); + + return internal_to_public_wrap_mode (authority->wrap_mode_t); +} + +CoglPipelineWrapMode +cogl_pipeline_get_layer_wrap_mode_t (CoglPipeline *pipeline, int layer_index) +{ + CoglPipelineLayer *layer; + + g_return_val_if_fail (cogl_is_pipeline (pipeline), FALSE); + + /* Note: this will ensure that the layer exists, creating one if it + * doesn't already. + * + * Note: If the layer already existed it's possibly owned by another + * pipeline. If the layer is created then it will be owned by + * pipeline. */ + layer = _cogl_pipeline_get_layer (pipeline, layer_index); + /* FIXME: we shouldn't ever construct a layer in a getter function */ + + return _cogl_pipeline_layer_get_wrap_mode_t (layer); +} + +CoglPipelineWrapMode +_cogl_pipeline_layer_get_wrap_mode_p (CoglPipelineLayer *layer) +{ + CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_WRAP_MODES; + CoglPipelineLayer *authority = + _cogl_pipeline_layer_get_authority (layer, change); + + return internal_to_public_wrap_mode (authority->wrap_mode_p); +} + +CoglPipelineWrapMode +cogl_pipeline_get_layer_wrap_mode_p (CoglPipeline *pipeline, int layer_index) +{ + CoglPipelineLayer *layer; + + g_return_val_if_fail (cogl_is_pipeline (pipeline), FALSE); + + /* Note: this will ensure that the layer exists, creating one if it + * doesn't already. + * + * Note: If the layer already existed it's possibly owned by another + * pipeline. If the layer is created then it will be owned by + * pipeline. */ + layer = _cogl_pipeline_get_layer (pipeline, layer_index); + + return _cogl_pipeline_layer_get_wrap_mode_p (layer); +} + +void +_cogl_pipeline_layer_get_wrap_modes (CoglPipelineLayer *layer, + CoglPipelineWrapModeInternal *wrap_mode_s, + CoglPipelineWrapModeInternal *wrap_mode_t, + CoglPipelineWrapModeInternal *wrap_mode_p) +{ + CoglPipelineLayer *authority = + _cogl_pipeline_layer_get_authority (layer, + COGL_PIPELINE_LAYER_STATE_WRAP_MODES); + + *wrap_mode_s = authority->wrap_mode_s; + *wrap_mode_t = authority->wrap_mode_t; + *wrap_mode_p = authority->wrap_mode_p; +} + +gboolean +cogl_pipeline_set_layer_point_sprite_coords_enabled (CoglPipeline *pipeline, + int layer_index, + gboolean enable, + GError **error) +{ + CoglPipelineLayerState change = + COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS; + CoglPipelineLayer *layer; + CoglPipelineLayer *new; + CoglPipelineLayer *authority; + + g_return_val_if_fail (cogl_is_pipeline (pipeline), FALSE); + + /* Don't allow point sprite coordinates to be enabled if the driver + doesn't support it */ + if (enable && !cogl_features_available (COGL_FEATURE_POINT_SPRITE)) + { + if (error) + { + g_set_error (error, COGL_ERROR, COGL_ERROR_UNSUPPORTED, + "Point sprite texture coordinates are enabled " + "for a layer but the GL driver does not support it."); + } + else + { + static gboolean warning_seen = FALSE; + if (!warning_seen) + g_warning ("Point sprite texture coordinates are enabled " + "for a layer but the GL driver does not support it."); + warning_seen = TRUE; + } + + return FALSE; + } + + /* Note: this will ensure that the layer exists, creating one if it + * doesn't already. + * + * Note: If the layer already existed it's possibly owned by another + * pipeline. If the layer is created then it will be owned by + * pipeline. */ + layer = _cogl_pipeline_get_layer (pipeline, layer_index); + + /* Now find the ancestor of the layer that is the authority for the + * state we want to change */ + authority = _cogl_pipeline_layer_get_authority (layer, change); + + if (authority->big_state->point_sprite_coords == enable) + return TRUE; + + new = _cogl_pipeline_layer_pre_change_notify (pipeline, layer, change); + if (new != layer) + layer = new; + else + { + /* If the original layer we found is currently the authority on + * the state we are changing see if we can revert to one of our + * ancestors being the authority. */ + if (layer == authority && + _cogl_pipeline_layer_get_parent (authority) != NULL) + { + CoglPipelineLayer *parent = + _cogl_pipeline_layer_get_parent (authority); + CoglPipelineLayer *old_authority = + _cogl_pipeline_layer_get_authority (parent, change); + + if (old_authority->big_state->point_sprite_coords == enable) + { + layer->differences &= ~change; + + g_assert (layer->owner == pipeline); + if (layer->differences == 0) + _cogl_pipeline_prune_empty_layer_difference (pipeline, + layer); + return TRUE; + } + } + } + + layer->big_state->point_sprite_coords = enable; + + /* If we weren't previously the authority on this state then we need + * to extended our differences mask and so it's possible that some + * of our ancestry will now become redundant, so we aim to reparent + * ourselves if that's true... */ + if (layer != authority) + { + layer->differences |= change; + _cogl_pipeline_layer_prune_redundant_ancestry (layer); + } + + return TRUE; +} + +gboolean +cogl_pipeline_get_layer_point_sprite_coords_enabled (CoglPipeline *pipeline, + int layer_index) +{ + CoglPipelineLayerState change = + COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS; + CoglPipelineLayer *layer; + CoglPipelineLayer *authority; + + g_return_val_if_fail (cogl_is_pipeline (pipeline), FALSE); + + /* Note: this will ensure that the layer exists, creating one if it + * doesn't already. + * + * Note: If the layer already existed it's possibly owned by another + * pipeline. If the layer is created then it will be owned by + * pipeline. */ + layer = _cogl_pipeline_get_layer (pipeline, layer_index); + /* FIXME: we shouldn't ever construct a layer in a getter function */ + + authority = _cogl_pipeline_layer_get_authority (layer, change); + + return authority->big_state->point_sprite_coords; +} + +gboolean +_cogl_pipeline_layer_texture_target_equal (CoglPipelineLayer *authority0, + CoglPipelineLayer *authority1, + CoglPipelineEvalFlags flags) +{ + return authority0->target == authority1->target; +} + +gboolean +_cogl_pipeline_layer_texture_data_equal (CoglPipelineLayer *authority0, + CoglPipelineLayer *authority1, + CoglPipelineEvalFlags flags) +{ + GLuint gl_handle0, gl_handle1; + + cogl_texture_get_gl_texture (authority0->texture, &gl_handle0, NULL); + cogl_texture_get_gl_texture (authority1->texture, &gl_handle1, NULL); + + return gl_handle0 == gl_handle1; +} + +gboolean +_cogl_pipeline_layer_combine_state_equal (CoglPipelineLayer *authority0, + CoglPipelineLayer *authority1) +{ + CoglPipelineLayerBigState *big_state0 = authority0->big_state; + CoglPipelineLayerBigState *big_state1 = authority1->big_state; + int n_args; + int i; + + if (big_state0->texture_combine_rgb_func != + big_state1->texture_combine_rgb_func) + return FALSE; + + if (big_state0->texture_combine_alpha_func != + big_state1->texture_combine_alpha_func) + return FALSE; + + n_args = + _cogl_get_n_args_for_combine_func (big_state0->texture_combine_rgb_func); + for (i = 0; i < n_args; i++) + { + if ((big_state0->texture_combine_rgb_src[i] != + big_state1->texture_combine_rgb_src[i]) || + (big_state0->texture_combine_rgb_op[i] != + big_state1->texture_combine_rgb_op[i])) + return FALSE; + } + + n_args = + _cogl_get_n_args_for_combine_func (big_state0->texture_combine_alpha_func); + for (i = 0; i < n_args; i++) + { + if ((big_state0->texture_combine_alpha_src[i] != + big_state1->texture_combine_alpha_src[i]) || + (big_state0->texture_combine_alpha_op[i] != + big_state1->texture_combine_alpha_op[i])) + return FALSE; + } + + return TRUE; +} + +gboolean +_cogl_pipeline_layer_combine_constant_equal (CoglPipelineLayer *authority0, + CoglPipelineLayer *authority1) +{ + return memcmp (authority0->big_state->texture_combine_constant, + authority1->big_state->texture_combine_constant, + sizeof (float) * 4) == 0 ? TRUE : FALSE; +} + +gboolean +_cogl_pipeline_layer_filters_equal (CoglPipelineLayer *authority0, + CoglPipelineLayer *authority1) +{ + if (authority0->mag_filter != authority1->mag_filter) + return FALSE; + if (authority0->min_filter != authority1->min_filter) + return FALSE; + + return TRUE; +} + +static gboolean +compare_wrap_mode_equal (CoglPipelineWrapMode wrap_mode0, + CoglPipelineWrapMode wrap_mode1) +{ + /* We consider AUTOMATIC to be equivalent to CLAMP_TO_EDGE because + the primitives code is expected to override this to something + else if it wants it to be behave any other way */ + if (wrap_mode0 == COGL_PIPELINE_WRAP_MODE_AUTOMATIC) + wrap_mode0 = COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE; + if (wrap_mode1 == COGL_PIPELINE_WRAP_MODE_AUTOMATIC) + wrap_mode1 = COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE; + + return wrap_mode0 == wrap_mode1; +} + +gboolean +_cogl_pipeline_layer_wrap_modes_equal (CoglPipelineLayer *authority0, + CoglPipelineLayer *authority1) +{ + if (!compare_wrap_mode_equal (authority0->wrap_mode_s, + authority1->wrap_mode_s) || + !compare_wrap_mode_equal (authority0->wrap_mode_t, + authority1->wrap_mode_t) || + !compare_wrap_mode_equal (authority0->wrap_mode_p, + authority1->wrap_mode_p)) + return FALSE; + + return TRUE; +} + +gboolean +_cogl_pipeline_layer_user_matrix_equal (CoglPipelineLayer *authority0, + CoglPipelineLayer *authority1) +{ + CoglPipelineLayerBigState *big_state0 = authority0->big_state; + CoglPipelineLayerBigState *big_state1 = authority1->big_state; + + if (!cogl_matrix_equal (&big_state0->matrix, &big_state1->matrix)) + return FALSE; + + return TRUE; +} + +gboolean +_cogl_pipeline_layer_point_sprite_coords_equal (CoglPipelineLayer *authority0, + CoglPipelineLayer *authority1) +{ + CoglPipelineLayerBigState *big_state0 = authority0->big_state; + CoglPipelineLayerBigState *big_state1 = authority1->big_state; + + return big_state0->point_sprite_coords == big_state1->point_sprite_coords; +} + +static void +setup_texture_combine_state (CoglBlendStringStatement *statement, + CoglPipelineCombineFunc *texture_combine_func, + CoglPipelineCombineSource *texture_combine_src, + CoglPipelineCombineOp *texture_combine_op) +{ + int i; + + switch (statement->function->type) + { + case COGL_BLEND_STRING_FUNCTION_REPLACE: + *texture_combine_func = COGL_PIPELINE_COMBINE_FUNC_REPLACE; + break; + case COGL_BLEND_STRING_FUNCTION_MODULATE: + *texture_combine_func = COGL_PIPELINE_COMBINE_FUNC_MODULATE; + break; + case COGL_BLEND_STRING_FUNCTION_ADD: + *texture_combine_func = COGL_PIPELINE_COMBINE_FUNC_ADD; + break; + case COGL_BLEND_STRING_FUNCTION_ADD_SIGNED: + *texture_combine_func = COGL_PIPELINE_COMBINE_FUNC_ADD_SIGNED; + break; + case COGL_BLEND_STRING_FUNCTION_INTERPOLATE: + *texture_combine_func = COGL_PIPELINE_COMBINE_FUNC_INTERPOLATE; + break; + case COGL_BLEND_STRING_FUNCTION_SUBTRACT: + *texture_combine_func = COGL_PIPELINE_COMBINE_FUNC_SUBTRACT; + break; + case COGL_BLEND_STRING_FUNCTION_DOT3_RGB: + *texture_combine_func = COGL_PIPELINE_COMBINE_FUNC_DOT3_RGB; + break; + case COGL_BLEND_STRING_FUNCTION_DOT3_RGBA: + *texture_combine_func = COGL_PIPELINE_COMBINE_FUNC_DOT3_RGBA; + break; + } + + for (i = 0; i < statement->function->argc; i++) + { + CoglBlendStringArgument *arg = &statement->args[i]; + + switch (arg->source.info->type) + { + case COGL_BLEND_STRING_COLOR_SOURCE_CONSTANT: + texture_combine_src[i] = COGL_PIPELINE_COMBINE_SOURCE_CONSTANT; + break; + case COGL_BLEND_STRING_COLOR_SOURCE_TEXTURE: + texture_combine_src[i] = COGL_PIPELINE_COMBINE_SOURCE_TEXTURE; + break; + case COGL_BLEND_STRING_COLOR_SOURCE_TEXTURE_N: + texture_combine_src[i] = + COGL_PIPELINE_COMBINE_SOURCE_TEXTURE0 + arg->source.texture; + break; + case COGL_BLEND_STRING_COLOR_SOURCE_PRIMARY: + texture_combine_src[i] = COGL_PIPELINE_COMBINE_SOURCE_PRIMARY_COLOR; + break; + case COGL_BLEND_STRING_COLOR_SOURCE_PREVIOUS: + texture_combine_src[i] = COGL_PIPELINE_COMBINE_SOURCE_PREVIOUS; + break; + default: + g_warning ("Unexpected texture combine source"); + texture_combine_src[i] = COGL_PIPELINE_COMBINE_SOURCE_TEXTURE; + } + + if (arg->source.mask == COGL_BLEND_STRING_CHANNEL_MASK_RGB) + { + if (statement->args[i].source.one_minus) + texture_combine_op[i] = + COGL_PIPELINE_COMBINE_OP_ONE_MINUS_SRC_COLOR; + else + texture_combine_op[i] = COGL_PIPELINE_COMBINE_OP_SRC_COLOR; + } + else + { + if (statement->args[i].source.one_minus) + texture_combine_op[i] = + COGL_PIPELINE_COMBINE_OP_ONE_MINUS_SRC_ALPHA; + else + texture_combine_op[i] = COGL_PIPELINE_COMBINE_OP_SRC_ALPHA; + } + } +} + +gboolean +cogl_pipeline_set_layer_combine (CoglPipeline *pipeline, + int layer_index, + const char *combine_description, + GError **error) +{ + CoglPipelineLayerState state = COGL_PIPELINE_LAYER_STATE_COMBINE; + CoglPipelineLayer *authority; + CoglPipelineLayer *layer; + CoglBlendStringStatement statements[2]; + CoglBlendStringStatement split[2]; + CoglBlendStringStatement *rgb; + CoglBlendStringStatement *a; + GError *internal_error = NULL; + int count; + + g_return_val_if_fail (cogl_is_pipeline (pipeline), FALSE); + + /* Note: this will ensure that the layer exists, creating one if it + * doesn't already. + * + * Note: If the layer already existed it's possibly owned by another + * pipeline. If the layer is created then it will be owned by + * pipeline. */ + layer = _cogl_pipeline_get_layer (pipeline, layer_index); + + /* Now find the ancestor of the layer that is the authority for the + * state we want to change */ + authority = _cogl_pipeline_layer_get_authority (layer, state); + + count = + _cogl_blend_string_compile (combine_description, + COGL_BLEND_STRING_CONTEXT_TEXTURE_COMBINE, + statements, + &internal_error); + if (!count) + { + if (error) + g_propagate_error (error, internal_error); + else + { + g_warning ("Cannot compile combine description: %s\n", + internal_error->message); + g_error_free (internal_error); + } + return FALSE; + } + + if (statements[0].mask == COGL_BLEND_STRING_CHANNEL_MASK_RGBA) + { + _cogl_blend_string_split_rgba_statement (statements, + &split[0], &split[1]); + rgb = &split[0]; + a = &split[1]; + } + else + { + rgb = &statements[0]; + a = &statements[1]; + } + + /* FIXME: compare the new state with the current state! */ + + /* possibly flush primitives referencing the current state... */ + layer = _cogl_pipeline_layer_pre_change_notify (pipeline, layer, state); + + setup_texture_combine_state (rgb, + &layer->big_state->texture_combine_rgb_func, + layer->big_state->texture_combine_rgb_src, + layer->big_state->texture_combine_rgb_op); + + setup_texture_combine_state (a, + &layer->big_state->texture_combine_alpha_func, + layer->big_state->texture_combine_alpha_src, + layer->big_state->texture_combine_alpha_op); + + /* If the original layer we found is currently the authority on + * the state we are changing see if we can revert to one of our + * ancestors being the authority. */ + if (layer == authority && + _cogl_pipeline_layer_get_parent (authority) != NULL) + { + CoglPipelineLayer *parent = _cogl_pipeline_layer_get_parent (authority); + CoglPipelineLayer *old_authority = + _cogl_pipeline_layer_get_authority (parent, state); + + if (_cogl_pipeline_layer_combine_state_equal (authority, + old_authority)) + { + layer->differences &= ~state; + + g_assert (layer->owner == pipeline); + if (layer->differences == 0) + _cogl_pipeline_prune_empty_layer_difference (pipeline, + layer); + goto changed; + } + } + + /* If we weren't previously the authority on this state then we need + * to extended our differences mask and so it's possible that some + * of our ancestry will now become redundant, so we aim to reparent + * ourselves if that's true... */ + if (layer != authority) + { + layer->differences |= state; + _cogl_pipeline_layer_prune_redundant_ancestry (layer); + } + +changed: + + _cogl_pipeline_update_blend_enable (pipeline, COGL_PIPELINE_STATE_LAYERS); + return TRUE; +} + +void +cogl_pipeline_set_layer_combine_constant (CoglPipeline *pipeline, + int layer_index, + const CoglColor *constant_color) +{ + CoglPipelineLayerState state = COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT; + CoglPipelineLayer *layer; + CoglPipelineLayer *authority; + CoglPipelineLayer *new; + float color_as_floats[4]; + + g_return_if_fail (cogl_is_pipeline (pipeline)); + + /* Note: this will ensure that the layer exists, creating one if it + * doesn't already. + * + * Note: If the layer already existed it's possibly owned by another + * pipeline. If the layer is created then it will be owned by + * pipeline. */ + layer = _cogl_pipeline_get_layer (pipeline, layer_index); + + /* Now find the ancestor of the layer that is the authority for the + * state we want to change */ + authority = _cogl_pipeline_layer_get_authority (layer, state); + + color_as_floats[0] = cogl_color_get_red_float (constant_color); + color_as_floats[1] = cogl_color_get_green_float (constant_color); + color_as_floats[2] = cogl_color_get_blue_float (constant_color); + color_as_floats[3] = cogl_color_get_alpha_float (constant_color); + + if (memcmp (authority->big_state->texture_combine_constant, + color_as_floats, sizeof (float) * 4) == 0) + return; + + new = _cogl_pipeline_layer_pre_change_notify (pipeline, layer, state); + if (new != layer) + layer = new; + else + { + /* If the original layer we found is currently the authority on + * the state we are changing see if we can revert to one of our + * ancestors being the authority. */ + if (layer == authority && + _cogl_pipeline_layer_get_parent (authority) != NULL) + { + CoglPipelineLayer *parent = + _cogl_pipeline_layer_get_parent (authority); + CoglPipelineLayer *old_authority = + _cogl_pipeline_layer_get_authority (parent, state); + CoglPipelineLayerBigState *old_big_state = old_authority->big_state; + + if (memcmp (old_big_state->texture_combine_constant, + color_as_floats, sizeof (float) * 4) == 0) + { + layer->differences &= ~state; + + g_assert (layer->owner == pipeline); + if (layer->differences == 0) + _cogl_pipeline_prune_empty_layer_difference (pipeline, + layer); + goto changed; + } + } + } + + memcpy (layer->big_state->texture_combine_constant, + color_as_floats, + sizeof (color_as_floats)); + + /* If we weren't previously the authority on this state then we need + * to extended our differences mask and so it's possible that some + * of our ancestry will now become redundant, so we aim to reparent + * ourselves if that's true... */ + if (layer != authority) + { + layer->differences |= state; + _cogl_pipeline_layer_prune_redundant_ancestry (layer); + } + +changed: + + _cogl_pipeline_update_blend_enable (pipeline, COGL_PIPELINE_STATE_LAYERS); +} + +void +_cogl_pipeline_get_layer_combine_constant (CoglPipeline *pipeline, + int layer_index, + float *constant) +{ + CoglPipelineLayerState change = + COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT; + CoglPipelineLayer *layer; + CoglPipelineLayer *authority; + + g_return_if_fail (cogl_is_pipeline (pipeline)); + + /* Note: this will ensure that the layer exists, creating one if it + * doesn't already. + * + * Note: If the layer already existed it's possibly owned by another + * pipeline. If the layer is created then it will be owned by + * pipeline. */ + layer = _cogl_pipeline_get_layer (pipeline, layer_index); + /* FIXME: we shouldn't ever construct a layer in a getter function */ + + authority = _cogl_pipeline_layer_get_authority (layer, change); + memcpy (constant, authority->big_state->texture_combine_constant, + sizeof (float) * 4); +} + +/* We should probably make a public API version of this that has a + matrix out-param. For an internal API it's good to be able to avoid + copying the matrix */ +const CoglMatrix * +_cogl_pipeline_get_layer_matrix (CoglPipeline *pipeline, int layer_index) +{ + CoglPipelineLayerState change = + COGL_PIPELINE_LAYER_STATE_USER_MATRIX; + CoglPipelineLayer *layer; + CoglPipelineLayer *authority; + + g_return_val_if_fail (cogl_is_pipeline (pipeline), NULL); + + layer = _cogl_pipeline_get_layer (pipeline, layer_index); + + authority = _cogl_pipeline_layer_get_authority (layer, change); + return &authority->big_state->matrix; +} + +void +cogl_pipeline_set_layer_matrix (CoglPipeline *pipeline, + int layer_index, + const CoglMatrix *matrix) +{ + CoglPipelineLayerState state = COGL_PIPELINE_LAYER_STATE_USER_MATRIX; + CoglPipelineLayer *layer; + CoglPipelineLayer *authority; + CoglPipelineLayer *new; + + g_return_if_fail (cogl_is_pipeline (pipeline)); + + /* Note: this will ensure that the layer exists, creating one if it + * doesn't already. + * + * Note: If the layer already existed it's possibly owned by another + * pipeline. If the layer is created then it will be owned by + * pipeline. */ + layer = _cogl_pipeline_get_layer (pipeline, layer_index); + + /* Now find the ancestor of the layer that is the authority for the + * state we want to change */ + authority = _cogl_pipeline_layer_get_authority (layer, state); + + if (cogl_matrix_equal (matrix, &authority->big_state->matrix)) + return; + + new = _cogl_pipeline_layer_pre_change_notify (pipeline, layer, state); + if (new != layer) + layer = new; + else + { + /* If the original layer we found is currently the authority on + * the state we are changing see if we can revert to one of our + * ancestors being the authority. */ + if (layer == authority && + _cogl_pipeline_layer_get_parent (authority) != NULL) + { + CoglPipelineLayer *parent = + _cogl_pipeline_layer_get_parent (authority); + CoglPipelineLayer *old_authority = + _cogl_pipeline_layer_get_authority (parent, state); + + if (cogl_matrix_equal (matrix, &old_authority->big_state->matrix)) + { + layer->differences &= ~state; + + g_assert (layer->owner == pipeline); + if (layer->differences == 0) + _cogl_pipeline_prune_empty_layer_difference (pipeline, + layer); + return; + } + } + } + + layer->big_state->matrix = *matrix; + + /* If we weren't previously the authority on this state then we need + * to extended our differences mask and so it's possible that some + * of our ancestry will now become redundant, so we aim to reparent + * ourselves if that's true... */ + if (layer != authority) + { + layer->differences |= state; + _cogl_pipeline_layer_prune_redundant_ancestry (layer); + } +} + +/* FIXME: deprecate and replace with + * cogl_pipeline_get_layer_texture() instead. */ +CoglHandle +_cogl_pipeline_layer_get_texture (CoglPipelineLayer *layer) +{ + g_return_val_if_fail (_cogl_is_pipeline_layer (layer), NULL); + + return _cogl_pipeline_layer_get_texture_real (layer); +} + +gboolean +_cogl_pipeline_layer_has_user_matrix (CoglPipeline *pipeline, + int layer_index) +{ + CoglPipelineLayer *layer; + CoglPipelineLayer *authority; + + layer = _cogl_pipeline_get_layer (pipeline, layer_index); + + authority = + _cogl_pipeline_layer_get_authority (layer, + COGL_PIPELINE_LAYER_STATE_USER_MATRIX); + + /* If the authority is the default pipeline then no, otherwise yes */ + return _cogl_pipeline_layer_get_parent (authority) ? TRUE : FALSE; +} + +void +_cogl_pipeline_layer_get_filters (CoglPipelineLayer *layer, + CoglPipelineFilter *min_filter, + CoglPipelineFilter *mag_filter) +{ + CoglPipelineLayer *authority = + _cogl_pipeline_layer_get_authority (layer, + COGL_PIPELINE_LAYER_STATE_FILTERS); + + *min_filter = authority->min_filter; + *mag_filter = authority->mag_filter; +} + +void +_cogl_pipeline_get_layer_filters (CoglPipeline *pipeline, + int layer_index, + CoglPipelineFilter *min_filter, + CoglPipelineFilter *mag_filter) +{ + CoglPipelineLayer *layer; + CoglPipelineLayer *authority; + + g_return_if_fail (cogl_is_pipeline (pipeline)); + + layer = _cogl_pipeline_get_layer (pipeline, layer_index); + + authority = + _cogl_pipeline_layer_get_authority (layer, + COGL_PIPELINE_LAYER_STATE_FILTERS); + + *min_filter = authority->min_filter; + *mag_filter = authority->mag_filter; +} + +CoglPipelineFilter +_cogl_pipeline_get_layer_min_filter (CoglPipeline *pipeline, + int layer_index) +{ + CoglPipelineFilter min_filter; + CoglPipelineFilter mag_filter; + + _cogl_pipeline_get_layer_filters (pipeline, layer_index, + &min_filter, &mag_filter); + return min_filter; +} + +CoglPipelineFilter +_cogl_pipeline_get_layer_mag_filter (CoglPipeline *pipeline, + int layer_index) +{ + CoglPipelineFilter min_filter; + CoglPipelineFilter mag_filter; + + _cogl_pipeline_get_layer_filters (pipeline, layer_index, + &min_filter, &mag_filter); + return mag_filter; +} + +CoglPipelineFilter +_cogl_pipeline_layer_get_min_filter (CoglPipelineLayer *layer) +{ + CoglPipelineLayer *authority; + + g_return_val_if_fail (_cogl_is_pipeline_layer (layer), 0); + + authority = + _cogl_pipeline_layer_get_authority (layer, + COGL_PIPELINE_LAYER_STATE_FILTERS); + + return authority->min_filter; +} + +CoglPipelineFilter +_cogl_pipeline_layer_get_mag_filter (CoglPipelineLayer *layer) +{ + CoglPipelineLayer *authority; + + g_return_val_if_fail (_cogl_is_pipeline_layer (layer), 0); + + authority = + _cogl_pipeline_layer_get_authority (layer, + COGL_PIPELINE_LAYER_STATE_FILTERS); + + return authority->mag_filter; +} + +void +cogl_pipeline_set_layer_filters (CoglPipeline *pipeline, + int layer_index, + CoglPipelineFilter min_filter, + CoglPipelineFilter mag_filter) +{ + CoglPipelineLayerState state = COGL_PIPELINE_LAYER_STATE_FILTERS; + CoglPipelineLayer *layer; + CoglPipelineLayer *authority; + CoglPipelineLayer *new; + + g_return_if_fail (cogl_is_pipeline (pipeline)); + + /* Note: this will ensure that the layer exists, creating one if it + * doesn't already. + * + * Note: If the layer already existed it's possibly owned by another + * pipeline. If the layer is created then it will be owned by + * pipeline. */ + layer = _cogl_pipeline_get_layer (pipeline, layer_index); + + /* Now find the ancestor of the layer that is the authority for the + * state we want to change */ + authority = _cogl_pipeline_layer_get_authority (layer, state); + + if (authority->min_filter == min_filter && + authority->mag_filter == mag_filter) + return; + + new = _cogl_pipeline_layer_pre_change_notify (pipeline, layer, state); + if (new != layer) + layer = new; + else + { + /* If the original layer we found is currently the authority on + * the state we are changing see if we can revert to one of our + * ancestors being the authority. */ + if (layer == authority && + _cogl_pipeline_layer_get_parent (authority) != NULL) + { + CoglPipelineLayer *parent = + _cogl_pipeline_layer_get_parent (authority); + CoglPipelineLayer *old_authority = + _cogl_pipeline_layer_get_authority (parent, state); + + if (old_authority->min_filter == min_filter && + old_authority->mag_filter == mag_filter) + { + layer->differences &= ~state; + + g_assert (layer->owner == pipeline); + if (layer->differences == 0) + _cogl_pipeline_prune_empty_layer_difference (pipeline, + layer); + return; + } + } + } + + layer->min_filter = min_filter; + layer->mag_filter = mag_filter; + + /* If we weren't previously the authority on this state then we need + * to extended our differences mask and so it's possible that some + * of our ancestry will now become redundant, so we aim to reparent + * ourselves if that's true... */ + if (layer != authority) + { + layer->differences |= state; + _cogl_pipeline_layer_prune_redundant_ancestry (layer); + } +} + +void +_cogl_pipeline_layer_hash_unit_state (CoglPipelineLayer *authority, + CoglPipelineLayer **authorities, + CoglPipelineHashState *state) +{ + int unit = authority->unit_index; + state->hash = + _cogl_util_one_at_a_time_hash (state->hash, &unit, sizeof (unit)); +} + +void +_cogl_pipeline_layer_hash_texture_target_state (CoglPipelineLayer *authority, + CoglPipelineLayer **authorities, + CoglPipelineHashState *state) +{ + GLenum gl_target = authority->target; + + state->hash = + _cogl_util_one_at_a_time_hash (state->hash, &gl_target, sizeof (gl_target)); +} + +void +_cogl_pipeline_layer_hash_texture_data_state (CoglPipelineLayer *authority, + CoglPipelineLayer **authorities, + CoglPipelineHashState *state) +{ + GLuint gl_handle; + + cogl_texture_get_gl_texture (authority->texture, &gl_handle, NULL); + + state->hash = + _cogl_util_one_at_a_time_hash (state->hash, &gl_handle, sizeof (gl_handle)); +} + +void +_cogl_pipeline_layer_hash_filters_state (CoglPipelineLayer *authority, + CoglPipelineLayer **authorities, + CoglPipelineHashState *state) +{ + unsigned int hash = state->hash; + hash = _cogl_util_one_at_a_time_hash (hash, &authority->mag_filter, + sizeof (authority->mag_filter)); + hash = _cogl_util_one_at_a_time_hash (hash, &authority->min_filter, + sizeof (authority->min_filter)); + state->hash = hash; +} + +void +_cogl_pipeline_layer_hash_wrap_modes_state (CoglPipelineLayer *authority, + CoglPipelineLayer **authorities, + CoglPipelineHashState *state) +{ + unsigned int hash = state->hash; + hash = _cogl_util_one_at_a_time_hash (hash, &authority->wrap_mode_s, + sizeof (authority->wrap_mode_s)); + hash = _cogl_util_one_at_a_time_hash (hash, &authority->wrap_mode_t, + sizeof (authority->wrap_mode_t)); + hash = _cogl_util_one_at_a_time_hash (hash, &authority->wrap_mode_p, + sizeof (authority->wrap_mode_p)); + state->hash = hash; +} + +void +_cogl_pipeline_layer_hash_combine_state (CoglPipelineLayer *authority, + CoglPipelineLayer **authorities, + CoglPipelineHashState *state) +{ + unsigned int hash = state->hash; + CoglPipelineLayerBigState *b = authority->big_state; + int n_args; + int i; + + hash = _cogl_util_one_at_a_time_hash (hash, &b->texture_combine_rgb_func, + sizeof (b->texture_combine_rgb_func)); + n_args = _cogl_get_n_args_for_combine_func (b->texture_combine_rgb_func); + for (i = 0; i < n_args; i++) + { + hash = + _cogl_util_one_at_a_time_hash (hash, &b->texture_combine_rgb_src[i], + sizeof (b->texture_combine_rgb_src[i])); + hash = + _cogl_util_one_at_a_time_hash (hash, &b->texture_combine_rgb_op[i], + sizeof (b->texture_combine_rgb_op[i])); + } + + hash = _cogl_util_one_at_a_time_hash (hash, &b->texture_combine_alpha_func, + sizeof (b->texture_combine_alpha_func)); + n_args = _cogl_get_n_args_for_combine_func (b->texture_combine_alpha_func); + for (i = 0; i < n_args; i++) + { + hash = + _cogl_util_one_at_a_time_hash (hash, &b->texture_combine_alpha_src[i], + sizeof (b->texture_combine_alpha_src[i])); + hash = + _cogl_util_one_at_a_time_hash (hash, &b->texture_combine_alpha_op[i], + sizeof (b->texture_combine_alpha_op[i])); + } + + state->hash = hash; +} + +void +_cogl_pipeline_layer_hash_combine_constant_state (CoglPipelineLayer *authority, + CoglPipelineLayer **authorities, + CoglPipelineHashState *state) +{ + CoglPipelineLayerBigState *b = authority->big_state; + gboolean need_hash = FALSE; + int n_args; + int i; + + /* XXX: If the user also asked to hash the ALPHA_FUNC_STATE then it + * would be nice if we could combine the n_args loops in this + * function and _cogl_pipeline_layer_hash_combine_state. + */ + + n_args = _cogl_get_n_args_for_combine_func (b->texture_combine_rgb_func); + for (i = 0; i < n_args; i++) + { + if (b->texture_combine_rgb_src[i] == + COGL_PIPELINE_COMBINE_SOURCE_CONSTANT) + { + /* XXX: should we be careful to only hash the alpha + * component in the COGL_PIPELINE_COMBINE_OP_SRC_ALPHA case? */ + need_hash = TRUE; + goto done; + } + } + + n_args = _cogl_get_n_args_for_combine_func (b->texture_combine_alpha_func); + for (i = 0; i < n_args; i++) + { + if (b->texture_combine_alpha_src[i] == + COGL_PIPELINE_COMBINE_SOURCE_CONSTANT) + { + /* XXX: should we be careful to only hash the alpha + * component in the COGL_PIPELINE_COMBINE_OP_SRC_ALPHA case? */ + need_hash = TRUE; + goto done; + } + } + +done: + if (need_hash) + { + float *constant = b->texture_combine_constant; + state->hash = _cogl_util_one_at_a_time_hash (state->hash, constant, + sizeof (float) * 4); + } +} + +void +_cogl_pipeline_layer_hash_user_matrix_state (CoglPipelineLayer *authority, + CoglPipelineLayer **authorities, + CoglPipelineHashState *state) +{ + CoglPipelineLayerBigState *big_state = authority->big_state; + state->hash = _cogl_util_one_at_a_time_hash (state->hash, &big_state->matrix, + sizeof (float) * 16); +} + +void +_cogl_pipeline_layer_hash_point_sprite_state (CoglPipelineLayer *authority, + CoglPipelineLayer **authorities, + CoglPipelineHashState *state) +{ + CoglPipelineLayerBigState *big_state = authority->big_state; + state->hash = + _cogl_util_one_at_a_time_hash (state->hash, &big_state->point_sprite_coords, + sizeof (big_state->point_sprite_coords)); +} + + |