From a6628ca1130929feca77be9bf9774072af5b0b6b Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Wed, 12 Jan 2011 17:11:48 +0000 Subject: [PATCH] pipeline: optimize state init if changing property When notifying that a pipeline property is going to change, then at times a pipeline will take over being the authority of the corresponding state group. Some state groups can contain multiple properties and so to maintain the integrity of all of the properties we have to initialize all the property values in the new authority. For state groups with only one property we don't have to initialize anything during the pre_change_notify() because we can assume the value will be initialized as part of the change being notified. This patch optimizes how we handle this initialization of state groups in a couple of ways; firstly we no longer do anything to initialize state-groups with only one property, secondly we no longer use _cogl_pipeline_copy_differences - (we have a new _cogl_pipeline_init_multi_property_sparse_state() func) so we can avoid lots calls to handle_automatic_blend_enable() which is sometimes seen high in sysprof profiles. --- cogl/cogl-pipeline-private.h | 159 ++++++++++++++-------- cogl/cogl-pipeline.c | 253 ++++++++++++++++++++++------------- 2 files changed, 264 insertions(+), 148 deletions(-) diff --git a/cogl/cogl-pipeline-private.h b/cogl/cogl-pipeline-private.h index 2d5b1b312..78103aa83 100644 --- a/cogl/cogl-pipeline-private.h +++ b/cogl/cogl-pipeline-private.h @@ -130,6 +130,13 @@ typedef enum COGL_PIPELINE_LAYER_STATE_COUNT = COGL_PIPELINE_LAYER_STATE_SPARSE_COUNT } CoglPipelineLayerStateIndex; +/* XXX: If you add or remove state groups here you may need to update + * some of the state masks following this enum too! + * + * FIXME: perhaps it would be better to rename this enum to + * CoglPipelineLayerStateGroup to better convey the fact that a single + * enum here can map to multiple properties. + */ typedef enum { COGL_PIPELINE_LAYER_STATE_UNIT = @@ -153,34 +160,49 @@ typedef enum /* COGL_PIPELINE_LAYER_STATE_TEXTURE_INTERN = 1L<<8, */ - COGL_PIPELINE_LAYER_STATE_ALL = - (1L<differences masks and for notifying pipeline - * state changes... */ + * state changes. + * + * XXX: If you add or remove state groups here you may need to update + * some of the state masks following this enum too! + * + * FIXME: perhaps it would be better to rename this enum to + * CoglPipelineStateGroup to better convey the fact that a single enum + * here can map to multiple properties. + */ typedef enum _CoglPipelineState { COGL_PIPELINE_STATE_COLOR = @@ -424,45 +454,60 @@ typedef enum _CoglPipelineState COGL_PIPELINE_STATE_REAL_BLEND_ENABLE = 1L<n_layers = src->n_layers; - dest->layer_differences = NULL; + /* XXX: avoid using a default: label so we get a warning if we + * don't explicitly handle a newly defined state-group here. */ + case COGL_PIPELINE_STATE_COLOR: + case COGL_PIPELINE_STATE_BLEND_ENABLE: + case COGL_PIPELINE_STATE_ALPHA_FUNC: + case COGL_PIPELINE_STATE_ALPHA_FUNC_REFERENCE: + case COGL_PIPELINE_STATE_POINT_SIZE: + case COGL_PIPELINE_STATE_USER_SHADER: + case COGL_PIPELINE_STATE_REAL_BLEND_ENABLE: + g_return_if_reached (); + + case COGL_PIPELINE_STATE_LAYERS: + pipeline->n_layers = authority->n_layers; + pipeline->layer_differences = NULL; + break; + case COGL_PIPELINE_STATE_LIGHTING: + { + memcpy (&pipeline->big_state->lighting_state, + &authority->big_state->lighting_state, + sizeof (CoglPipelineLightingState)); + break; + } + case COGL_PIPELINE_STATE_BLEND: + { + memcpy (&pipeline->big_state->blend_state, + &authority->big_state->blend_state, + sizeof (CoglPipelineBlendState)); + break; + } + case COGL_PIPELINE_STATE_DEPTH: + { + memcpy (&pipeline->big_state->depth_state, + &authority->big_state->depth_state, + sizeof (CoglPipelineDepthState)); + break; + } + case COGL_PIPELINE_STATE_FOG: + { + memcpy (&pipeline->big_state->fog_state, + &authority->big_state->fog_state, + sizeof (CoglPipelineFogState)); + break; + } } } @@ -1170,7 +1212,6 @@ _cogl_pipeline_pre_change_notify (CoglPipeline *pipeline, const CoglColor *new_color, gboolean from_layer_change) { - CoglPipeline *authority; int i; _COGL_GET_CONTEXT (ctx, NO_RETVAL); @@ -1354,14 +1395,32 @@ _cogl_pipeline_pre_change_notify (CoglPipeline *pipeline, pipeline->age++; - /* If changing a sparse property and if the pipeline isn't already an - * authority for the state group being modified then we need to - * initialize the corresponding state. */ + if (change & COGL_PIPELINE_STATE_NEEDS_BIG_STATE && + !pipeline->has_big_state) + { + pipeline->big_state = g_slice_new (CoglPipelineBigState); + pipeline->has_big_state = TRUE; + } + + /* Note: conceptually we have just been notified that a single + * property value is about to change, but since some state-groups + * contain multiple properties and 'pipeline' is about to take over + * being the authority for the property's corresponding state-group + * we need to maintain the integrity of the other property values + * too. + * + * To ensure this we handle multi-property state-groups by copying + * all the values from the old-authority to the new... + * + * We don't have to worry about non-sparse property groups since + * we never take over being an authority for such properties so + * they automatically maintain integrity. + */ if (change & COGL_PIPELINE_STATE_ALL_SPARSE && !(pipeline->differences & change)) { - authority = _cogl_pipeline_get_authority (pipeline, change); - _cogl_pipeline_initialize_sparse_state (pipeline, authority, change); + _cogl_pipeline_init_multi_property_sparse_state (pipeline, change); + pipeline->differences |= change; } /* Each pipeline has a sorted cache of the layers it depends on @@ -1639,82 +1698,73 @@ _cogl_get_n_args_for_combine_func (CoglPipelineCombineFunc func) } static void -_cogl_pipeline_layer_initialize_state (CoglPipelineLayer *dest, - CoglPipelineLayer *src, - unsigned long differences) +_cogl_pipeline_layer_init_multi_property_sparse_state ( + CoglPipelineLayer *layer, + CoglPipelineLayerState change) { - CoglPipelineLayerBigState *big_state; + CoglPipelineLayer *authority; - dest->differences |= differences; - - if (differences & COGL_PIPELINE_LAYER_STATE_UNIT) - dest->unit_index = src->unit_index; - - if (differences & COGL_PIPELINE_LAYER_STATE_TEXTURE) - dest->texture = src->texture; - - if (differences & COGL_PIPELINE_LAYER_STATE_FILTERS) - { - dest->min_filter = src->min_filter; - dest->mag_filter = src->mag_filter; - } - - if (differences & COGL_PIPELINE_LAYER_STATE_WRAP_MODES) - { - dest->wrap_mode_s = src->wrap_mode_s; - dest->wrap_mode_t = src->wrap_mode_t; - dest->wrap_mode_p = src->wrap_mode_p; - } - - if (differences & COGL_PIPELINE_LAYER_STATE_NEEDS_BIG_STATE) - { - if (!dest->has_big_state) - { - dest->big_state = g_slice_new (CoglPipelineLayerBigState); - dest->has_big_state = TRUE; - } - big_state = dest->big_state; - } - else + /* Nothing to initialize in these cases since they are all comprised + * of one member which we expect to immediately be overwritten. */ + if (!(change & COGL_PIPELINE_LAYER_STATE_MULTI_PROPERTY)) return; - if (differences & COGL_PIPELINE_LAYER_STATE_COMBINE) + authority = _cogl_pipeline_layer_get_authority (layer, change); + + switch (change) { - int n_args; - int i; - GLint func = src->big_state->texture_combine_rgb_func; - big_state->texture_combine_rgb_func = func; - n_args = _cogl_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]; - } + /* XXX: avoid using a default: label so we get a warning if we + * don't explicitly handle a newly defined state-group here. */ + case COGL_PIPELINE_LAYER_STATE_UNIT: + case COGL_PIPELINE_LAYER_STATE_TEXTURE: + case COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS: + case COGL_PIPELINE_LAYER_STATE_USER_MATRIX: + case COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT: + g_return_if_reached (); - func = src->big_state->texture_combine_alpha_func; - big_state->texture_combine_alpha_func = func; - n_args = _cogl_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]; - } + /* XXX: technically we could probably even consider these as + * single property state-groups from the pov that currently the + * corresponding property setters always update all of the values + * at the same time. */ + case COGL_PIPELINE_LAYER_STATE_FILTERS: + layer->min_filter = authority->min_filter; + layer->mag_filter = authority->mag_filter; + break; + case COGL_PIPELINE_LAYER_STATE_WRAP_MODES: + layer->wrap_mode_s = authority->wrap_mode_s; + layer->wrap_mode_t = authority->wrap_mode_t; + layer->wrap_mode_p = authority->wrap_mode_p; + break; + case COGL_PIPELINE_LAYER_STATE_COMBINE: + { + int n_args; + int i; + CoglPipelineLayerBigState *big_state = layer->big_state; + GLint func = big_state->texture_combine_rgb_func; + + big_state->texture_combine_rgb_func = func; + n_args = _cogl_get_n_args_for_combine_func (func); + for (i = 0; i < n_args; i++) + { + big_state->texture_combine_rgb_src[i] = + authority->big_state->texture_combine_rgb_src[i]; + big_state->texture_combine_rgb_op[i] = + authority->big_state->texture_combine_rgb_op[i]; + } + + func = authority->big_state->texture_combine_alpha_func; + big_state->texture_combine_alpha_func = func; + n_args = _cogl_get_n_args_for_combine_func (func); + for (i = 0; i < n_args; i++) + { + big_state->texture_combine_alpha_src[i] = + authority->big_state->texture_combine_alpha_src[i]; + big_state->texture_combine_alpha_op[i] = + authority->big_state->texture_combine_alpha_op[i]; + } + break; + } } - - if (differences & COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT) - memcpy (dest->big_state->texture_combine_constant, - src->big_state->texture_combine_constant, - sizeof (float) * 4); - - if (differences & COGL_PIPELINE_LAYER_STATE_USER_MATRIX) - dest->big_state->matrix = src->big_state->matrix; - - if (differences & COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS) - dest->big_state->point_sprite_coords = src->big_state->point_sprite_coords; } /* NB: This function will allocate a new derived layer if you are @@ -1732,7 +1782,6 @@ _cogl_pipeline_layer_pre_change_notify (CoglPipeline *required_owner, CoglPipelineLayerState change) { CoglTextureUnit *unit; - CoglPipelineLayer *authority; /* Identify the case where the layer is new with no owner or * dependants and so we don't need to do anything. */ @@ -1792,11 +1841,33 @@ init_layer_state: if (required_owner) required_owner->age++; - /* If the pipeline isn't already an authority for the state group - * being modified then we need to initialize the corresponding - * state. */ - authority = _cogl_pipeline_layer_get_authority (layer, change); - _cogl_pipeline_layer_initialize_state (layer, authority, change); + if (change & COGL_PIPELINE_LAYER_STATE_NEEDS_BIG_STATE && + !layer->has_big_state) + { + layer->big_state = g_slice_new (CoglPipelineLayerBigState); + layer->has_big_state = TRUE; + } + + /* Note: conceptually we have just been notified that a single + * property value is about to change, but since some state-groups + * contain multiple properties and 'layer' is about to take over + * being the authority for the property's corresponding state-group + * we need to maintain the integrity of the other property values + * too. + * + * To ensure this we handle multi-property state-groups by copying + * all the values from the old-authority to the new... + * + * We don't have to worry about non-sparse property groups since + * we never take over being an authority for such properties so + * they automatically maintain integrity. + */ + if (change & COGL_PIPELINE_LAYER_STATE_ALL_SPARSE && + !(layer->differences & change)) + { + _cogl_pipeline_layer_init_multi_property_sparse_state (layer, change); + layer->differences |= change; + } return layer; }