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.
This commit is contained in:
Robert Bragg 2011-01-12 17:11:48 +00:00
parent 6e14b72284
commit a6628ca113
2 changed files with 264 additions and 148 deletions

View File

@ -130,6 +130,13 @@ typedef enum
COGL_PIPELINE_LAYER_STATE_COUNT = COGL_PIPELINE_LAYER_STATE_SPARSE_COUNT COGL_PIPELINE_LAYER_STATE_COUNT = COGL_PIPELINE_LAYER_STATE_SPARSE_COUNT
} CoglPipelineLayerStateIndex; } 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 typedef enum
{ {
COGL_PIPELINE_LAYER_STATE_UNIT = COGL_PIPELINE_LAYER_STATE_UNIT =
@ -153,34 +160,49 @@ typedef enum
/* COGL_PIPELINE_LAYER_STATE_TEXTURE_INTERN = 1L<<8, */ /* COGL_PIPELINE_LAYER_STATE_TEXTURE_INTERN = 1L<<8, */
COGL_PIPELINE_LAYER_STATE_ALL =
(1L<<COGL_PIPELINE_LAYER_STATE_COUNT) - 1,
COGL_PIPELINE_LAYER_STATE_ALL_SPARSE =
(1L<<COGL_PIPELINE_LAYER_STATE_COUNT) - 1,
COGL_PIPELINE_LAYER_STATE_NEEDS_BIG_STATE =
COGL_PIPELINE_LAYER_STATE_COMBINE |
COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT |
COGL_PIPELINE_LAYER_STATE_USER_MATRIX |
COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS,
COGL_PIPELINE_LAYER_STATE_AFFECTS_FRAGMENT_CODEGEN =
COGL_PIPELINE_LAYER_STATE_COMBINE |
/* FIXME: Only texture target changes should really affect the
codegen, but this is difficult to detect */
COGL_PIPELINE_LAYER_STATE_TEXTURE |
/* On GLES2 we need to use a different varying for the texture
lookups when point sprite coords are enabled */
#ifdef HAVE_COGL_GLES2
COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS |
#endif
COGL_PIPELINE_LAYER_STATE_UNIT,
COGL_PIPELINE_LAYER_STATE_AFFECTS_VERTEX_CODEGEN = 0
} CoglPipelineLayerState; } CoglPipelineLayerState;
/*
* Various special masks that tag state-groups in different ways...
*/
#define COGL_PIPELINE_LAYER_STATE_ALL \
((1L<<COGL_PIPELINE_LAYER_STATE_COUNT) - 1)
#define COGL_PIPELINE_LAYER_STATE_ALL_SPARSE \
COGL_PIPELINE_LAYER_STATE_ALL
#define COGL_PIPELINE_LAYER_STATE_NEEDS_BIG_STATE \
(COGL_PIPELINE_LAYER_STATE_COMBINE | \
COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT | \
COGL_PIPELINE_LAYER_STATE_USER_MATRIX | \
COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS)
#define COGL_PIPELINE_LAYER_STATE_MULTI_PROPERTY \
(COGL_PIPELINE_LAYER_STATE_FILTERS | \
COGL_PIPELINE_LAYER_STATE_WRAP_MODES | \
COGL_PIPELINE_LAYER_STATE_COMBINE)
/* FIXME: Only texture target changes should really affect the
* codegen, but this is difficult to detect */
#ifdef HAVE_COGL_GLES2
/* On GLES2 we need to use a different varying for the texture lookups
* when point sprite coords are enabled */
#define COGL_PIPELINE_LAYER_STATE_AFFECTS_FRAGMENT_CODEGEN \
(COGL_PIPELINE_LAYER_STATE_COMBINE | \
COGL_PIPELINE_LAYER_STATE_TEXTURE | \
COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS | \
COGL_PIPELINE_LAYER_STATE_UNIT)
#else
#define COGL_PIPELINE_LAYER_STATE_AFFECTS_FRAGMENT_CODEGEN \
(COGL_PIPELINE_LAYER_STATE_COMBINE | \
COGL_PIPELINE_LAYER_STATE_TEXTURE | \
COGL_PIPELINE_LAYER_STATE_UNIT)
#endif
#define COGL_PIPELINE_LAYER_STATE_AFFECTS_VERTEX_CODEGEN 0
typedef enum typedef enum
{ {
/* These are the same values as GL */ /* These are the same values as GL */
@ -394,7 +416,15 @@ typedef enum
} CoglPipelineStateIndex; } CoglPipelineStateIndex;
/* Used in pipeline->differences masks and for notifying pipeline /* Used in pipeline->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 typedef enum _CoglPipelineState
{ {
COGL_PIPELINE_STATE_COLOR = COGL_PIPELINE_STATE_COLOR =
@ -424,45 +454,60 @@ typedef enum _CoglPipelineState
COGL_PIPELINE_STATE_REAL_BLEND_ENABLE = COGL_PIPELINE_STATE_REAL_BLEND_ENABLE =
1L<<COGL_PIPELINE_STATE_REAL_BLEND_ENABLE_INDEX, 1L<<COGL_PIPELINE_STATE_REAL_BLEND_ENABLE_INDEX,
COGL_PIPELINE_STATE_ALL = } CoglPipelineState;
((1L<<COGL_PIPELINE_STATE_COUNT) - 1),
COGL_PIPELINE_STATE_ALL_SPARSE = /*
(COGL_PIPELINE_STATE_ALL * Various special masks that tag state-groups in different ways...
& ~COGL_PIPELINE_STATE_REAL_BLEND_ENABLE), */
COGL_PIPELINE_STATE_AFFECTS_BLENDING = #define COGL_PIPELINE_STATE_ALL \
COGL_PIPELINE_STATE_COLOR | ((1L<<COGL_PIPELINE_STATE_COUNT) - 1)
COGL_PIPELINE_STATE_BLEND_ENABLE |
COGL_PIPELINE_STATE_LAYERS |
COGL_PIPELINE_STATE_LIGHTING |
COGL_PIPELINE_STATE_BLEND |
COGL_PIPELINE_STATE_USER_SHADER,
COGL_PIPELINE_STATE_NEEDS_BIG_STATE = #define COGL_PIPELINE_STATE_ALL_SPARSE \
COGL_PIPELINE_STATE_LIGHTING | (COGL_PIPELINE_STATE_ALL \
COGL_PIPELINE_STATE_ALPHA_FUNC | & ~COGL_PIPELINE_STATE_REAL_BLEND_ENABLE)
COGL_PIPELINE_STATE_ALPHA_FUNC_REFERENCE |
COGL_PIPELINE_STATE_BLEND | #define COGL_PIPELINE_STATE_AFFECTS_BLENDING \
COGL_PIPELINE_STATE_USER_SHADER | (COGL_PIPELINE_STATE_COLOR | \
COGL_PIPELINE_STATE_DEPTH | COGL_PIPELINE_STATE_BLEND_ENABLE | \
COGL_PIPELINE_STATE_FOG | COGL_PIPELINE_STATE_LAYERS | \
COGL_PIPELINE_STATE_POINT_SIZE, COGL_PIPELINE_STATE_LIGHTING | \
COGL_PIPELINE_STATE_BLEND | \
COGL_PIPELINE_STATE_USER_SHADER)
#define COGL_PIPELINE_STATE_NEEDS_BIG_STATE \
(COGL_PIPELINE_STATE_LIGHTING | \
COGL_PIPELINE_STATE_ALPHA_FUNC | \
COGL_PIPELINE_STATE_ALPHA_FUNC_REFERENCE | \
COGL_PIPELINE_STATE_BLEND | \
COGL_PIPELINE_STATE_USER_SHADER | \
COGL_PIPELINE_STATE_DEPTH | \
COGL_PIPELINE_STATE_FOG | \
COGL_PIPELINE_STATE_POINT_SIZE)
#define COGL_PIPELINE_STATE_MULTI_PROPERTY \
(COGL_PIPELINE_STATE_LAYERS | \
COGL_PIPELINE_STATE_LIGHTING | \
COGL_PIPELINE_STATE_BLEND | \
COGL_PIPELINE_STATE_DEPTH | \
COGL_PIPELINE_STATE_FOG)
COGL_PIPELINE_STATE_AFFECTS_FRAGMENT_CODEGEN =
COGL_PIPELINE_STATE_LAYERS |
#ifdef HAVE_COGL_GLES2 #ifdef HAVE_COGL_GLES2
/* Under GLES2 the alpha func becomes part of the fragment program /* Under GLES2 the alpha func becomes part of the fragment program
so we can't share programs there */ so we can't share programs there */
COGL_PIPELINE_STATE_ALPHA_FUNC | #define COGL_PIPELINE_STATE_AFFECTS_FRAGMENT_CODEGEN \
(COGL_PIPELINE_STATE_LAYERS | \
COGL_PIPELINE_STATE_ALPHA_FUNC | \
COGL_PIPELINE_STATE_USER_SHADER)
#else
#define COGL_PIPELINE_STATE_AFFECTS_FRAGMENT_CODEGEN \
(COGL_PIPELINE_STATE_LAYERS | \
COGL_PIPELINE_STATE_USER_SHADER)
#endif #endif
COGL_PIPELINE_STATE_USER_SHADER,
COGL_PIPELINE_STATE_AFFECTS_VERTEX_CODEGEN = #define COGL_PIPELINE_STATE_AFFECTS_VERTEX_CODEGEN \
COGL_PIPELINE_STATE_LAYERS | (COGL_PIPELINE_STATE_LAYERS | \
COGL_PIPELINE_STATE_USER_SHADER COGL_PIPELINE_STATE_USER_SHADER)
} CoglPipelineState;
typedef enum typedef enum
{ {

View File

@ -1100,21 +1100,63 @@ check_for_blending_change:
} }
static void static void
_cogl_pipeline_initialize_sparse_state (CoglPipeline *dest, _cogl_pipeline_init_multi_property_sparse_state (CoglPipeline *pipeline,
CoglPipeline *src, CoglPipelineState change)
CoglPipelineState state)
{ {
if (dest == src) CoglPipeline *authority;
g_return_if_fail (change & COGL_PIPELINE_STATE_ALL_SPARSE);
if (!(change & COGL_PIPELINE_STATE_MULTI_PROPERTY))
return; return;
g_return_if_fail (state & COGL_PIPELINE_STATE_ALL_SPARSE); authority = _cogl_pipeline_get_authority (pipeline, change);
if (state != COGL_PIPELINE_STATE_LAYERS) switch (change)
_cogl_pipeline_copy_differences (dest, src, state);
else
{ {
dest->n_layers = src->n_layers; /* XXX: avoid using a default: label so we get a warning if we
dest->layer_differences = NULL; * 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, const CoglColor *new_color,
gboolean from_layer_change) gboolean from_layer_change)
{ {
CoglPipeline *authority;
int i; int i;
_COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_GET_CONTEXT (ctx, NO_RETVAL);
@ -1354,14 +1395,32 @@ _cogl_pipeline_pre_change_notify (CoglPipeline *pipeline,
pipeline->age++; pipeline->age++;
/* If changing a sparse property and if the pipeline isn't already an if (change & COGL_PIPELINE_STATE_NEEDS_BIG_STATE &&
* authority for the state group being modified then we need to !pipeline->has_big_state)
* initialize the corresponding 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 && if (change & COGL_PIPELINE_STATE_ALL_SPARSE &&
!(pipeline->differences & change)) !(pipeline->differences & change))
{ {
authority = _cogl_pipeline_get_authority (pipeline, change); _cogl_pipeline_init_multi_property_sparse_state (pipeline, change);
_cogl_pipeline_initialize_sparse_state (pipeline, authority, change); pipeline->differences |= change;
} }
/* Each pipeline has a sorted cache of the layers it depends on /* 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 static void
_cogl_pipeline_layer_initialize_state (CoglPipelineLayer *dest, _cogl_pipeline_layer_init_multi_property_sparse_state (
CoglPipelineLayer *src, CoglPipelineLayer *layer,
unsigned long differences) CoglPipelineLayerState change)
{ {
CoglPipelineLayerBigState *big_state; CoglPipelineLayer *authority;
dest->differences |= differences; /* Nothing to initialize in these cases since they are all comprised
* of one member which we expect to immediately be overwritten. */
if (differences & COGL_PIPELINE_LAYER_STATE_UNIT) if (!(change & COGL_PIPELINE_LAYER_STATE_MULTI_PROPERTY))
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
return; return;
if (differences & COGL_PIPELINE_LAYER_STATE_COMBINE) authority = _cogl_pipeline_layer_get_authority (layer, change);
switch (change)
{
/* 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 ();
/* 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 n_args;
int i; int i;
GLint func = src->big_state->texture_combine_rgb_func; CoglPipelineLayerBigState *big_state = layer->big_state;
GLint func = big_state->texture_combine_rgb_func;
big_state->texture_combine_rgb_func = func; big_state->texture_combine_rgb_func = func;
n_args = _cogl_get_n_args_for_combine_func (func); n_args = _cogl_get_n_args_for_combine_func (func);
for (i = 0; i < n_args; i++) for (i = 0; i < n_args; i++)
{ {
big_state->texture_combine_rgb_src[i] = big_state->texture_combine_rgb_src[i] =
src->big_state->texture_combine_rgb_src[i]; authority->big_state->texture_combine_rgb_src[i];
big_state->texture_combine_rgb_op[i] = big_state->texture_combine_rgb_op[i] =
src->big_state->texture_combine_rgb_op[i]; authority->big_state->texture_combine_rgb_op[i];
} }
func = src->big_state->texture_combine_alpha_func; func = authority->big_state->texture_combine_alpha_func;
big_state->texture_combine_alpha_func = func; big_state->texture_combine_alpha_func = func;
n_args = _cogl_get_n_args_for_combine_func (func); n_args = _cogl_get_n_args_for_combine_func (func);
for (i = 0; i < n_args; i++) for (i = 0; i < n_args; i++)
{ {
big_state->texture_combine_alpha_src[i] = big_state->texture_combine_alpha_src[i] =
src->big_state->texture_combine_alpha_src[i]; authority->big_state->texture_combine_alpha_src[i];
big_state->texture_combine_alpha_op[i] = big_state->texture_combine_alpha_op[i] =
src->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 /* 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) CoglPipelineLayerState change)
{ {
CoglTextureUnit *unit; CoglTextureUnit *unit;
CoglPipelineLayer *authority;
/* Identify the case where the layer is new with no owner or /* Identify the case where the layer is new with no owner or
* dependants and so we don't need to do anything. */ * dependants and so we don't need to do anything. */
@ -1792,11 +1841,33 @@ init_layer_state:
if (required_owner) if (required_owner)
required_owner->age++; required_owner->age++;
/* If the pipeline isn't already an authority for the state group if (change & COGL_PIPELINE_LAYER_STATE_NEEDS_BIG_STATE &&
* being modified then we need to initialize the corresponding !layer->has_big_state)
* state. */ {
authority = _cogl_pipeline_layer_get_authority (layer, change); layer->big_state = g_slice_new (CoglPipelineLayerBigState);
_cogl_pipeline_layer_initialize_state (layer, authority, change); 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; return layer;
} }