From cc36e49ec5f06f608380fba24682a69e9b0d0a1a Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Fri, 3 Dec 2010 11:36:49 +0000 Subject: [PATCH] pipeline: generalize _cogl_pipeline_equal _cogl_pipeline_equal now accepts a mask of pipeline differences and layer differences to constrain what state will be compared. In addition a set of flags are passed that can tweak the comparison semantics for some state groups. For example when comparing layer textures we sometimes only need to compare the texture target and can ignore the data itself. In updating the code this patch also changes it so all required pipeline authorities are resolved in one step up-front instead of resolving the authority for each state group in turn and repeatedly having to traverse the pipeline's ancestry. This adds two new functions _cogl_pipeline_resolve_authorities and _cogl_pipeline_layer_resolve_authorities to handle resolving a set of authorities. --- clutter/cogl/cogl/cogl-journal.c | 9 +- clutter/cogl/cogl/cogl-pipeline-private.h | 149 ++++++++--- clutter/cogl/cogl/cogl-pipeline.c | 287 +++++++++++++++------- 3 files changed, 309 insertions(+), 136 deletions(-) diff --git a/clutter/cogl/cogl/cogl-journal.c b/clutter/cogl/cogl/cogl-journal.c index 6a78d27bb..8256788bb 100644 --- a/clutter/cogl/cogl/cogl-journal.c +++ b/clutter/cogl/cogl/cogl-journal.c @@ -406,13 +406,12 @@ compare_entry_pipelines (CoglJournalEntry *entry0, CoglJournalEntry *entry1) { /* batch rectangles using compatible pipelines */ - /* XXX: _cogl_pipeline_equal may give false negatives since it avoids - * deep comparisons as an optimization. It aims to compare enough so - * that we that we are able to batch the 90% common cases, but may not - * look at less common differences. */ if (_cogl_pipeline_equal (entry0->pipeline, entry1->pipeline, - TRUE)) + (COGL_PIPELINE_STATE_ALL & + ~COGL_PIPELINE_STATE_COLOR), + COGL_PIPELINE_LAYER_STATE_ALL, + 0)) return TRUE; else return FALSE; diff --git a/clutter/cogl/cogl/cogl-pipeline-private.h b/clutter/cogl/cogl/cogl-pipeline-private.h index 9e047e47a..4cc8489e8 100644 --- a/clutter/cogl/cogl/cogl-pipeline-private.h +++ b/clutter/cogl/cogl/cogl-pipeline-private.h @@ -71,30 +71,55 @@ typedef struct _CoglPipelineLayer CoglPipelineLayer; #define COGL_PIPELINE_BACKEND_DEFAULT 0 #define COGL_PIPELINE_BACKEND_UNDEFINED 3 +/* XXX: should I rename these as + * COGL_PIPELINE_LAYER_STATE_INDEX_XYZ... ? + */ typedef enum { - COGL_PIPELINE_LAYER_STATE_UNIT = 1L<<0, - COGL_PIPELINE_LAYER_STATE_TEXTURE = 1L<<1, - COGL_PIPELINE_LAYER_STATE_FILTERS = 1L<<2, - COGL_PIPELINE_LAYER_STATE_WRAP_MODES = 1L<<3, + /* sparse state */ + COGL_PIPELINE_LAYER_STATE_UNIT_INDEX, + COGL_PIPELINE_LAYER_STATE_TEXTURE_INDEX, + COGL_PIPELINE_LAYER_STATE_FILTERS_INDEX, + COGL_PIPELINE_LAYER_STATE_WRAP_MODES_INDEX, + COGL_PIPELINE_LAYER_STATE_COMBINE_INDEX, + COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT_INDEX, + COGL_PIPELINE_LAYER_STATE_USER_MATRIX_INDEX, + COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS_INDEX, - COGL_PIPELINE_LAYER_STATE_COMBINE = 1L<<4, - COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT = 1L<<5, - COGL_PIPELINE_LAYER_STATE_USER_MATRIX = 1L<<6, + /* note: layers don't currently have any non-sparse state */ - COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS = 1L<<7, + COGL_PIPELINE_LAYER_STATE_SPARSE_COUNT, + COGL_PIPELINE_LAYER_STATE_COUNT = COGL_PIPELINE_LAYER_STATE_SPARSE_COUNT +} CoglPipelineLayerStateIndex; + +typedef enum +{ + COGL_PIPELINE_LAYER_STATE_UNIT = + 1L<differences masks and for notifying pipeline * state changes... */ typedef enum _CoglPipelineState { - COGL_PIPELINE_STATE_COLOR = 1L<<0, - COGL_PIPELINE_STATE_BLEND_ENABLE = 1L<<1, - COGL_PIPELINE_STATE_LAYERS = 1L<<2, + COGL_PIPELINE_STATE_COLOR = + 1L<texture, &gl_handle0, &gl_target0); cogl_texture_get_gl_texture (authority1->texture, &gl_handle1, &gl_target1); - return (gl_handle0 == gl_handle1 && - gl_target0 == gl_target1); + if (flags & COGL_PIPELINE_EVAL_FLAG_IGNORE_TEXTURE_DATA) + return (gl_target0 == gl_target1); + else + return (gl_handle0 == gl_handle1 && gl_target0 == gl_target1); } /* Determine the mask of differences between two layers. @@ -2915,24 +2918,58 @@ typedef gboolean CoglPipelineLayer *authority1); static gboolean -layer_state_equal (CoglPipelineLayerState state, - CoglPipelineLayer *layer0, - CoglPipelineLayer *layer1, +layer_state_equal (CoglPipelineLayerStateIndex state_index, + CoglPipelineLayer **authorities0, + CoglPipelineLayer **authorities1, CoglPipelineLayerStateComparitor comparitor) { - CoglPipelineLayer *authority0 = - _cogl_pipeline_layer_get_authority (layer0, state); - CoglPipelineLayer *authority1 = - _cogl_pipeline_layer_get_authority (layer1, state); + return comparitor (authorities0[state_index], authorities1[state_index]); +} - return comparitor (authority0, authority1); +static void +_cogl_pipeline_layer_resolve_authorities (CoglPipelineLayer *layer, + unsigned long differences, + CoglPipelineLayer **authorities) +{ + unsigned long remaining = differences; + CoglPipelineLayer *authority = layer; + + do + { + unsigned long found = authority->differences & remaining; + int i; + + if (found == 0) + continue; + + for (i = 0; True; i++) + { + unsigned long state = (1L< found) + break; + } + + remaining &= ~found; + if (remaining == 0) + return; + } + while ((authority = _cogl_pipeline_layer_get_parent (authority))); + + g_assert (remaining == 0); } static gboolean _cogl_pipeline_layer_equal (CoglPipelineLayer *layer0, - CoglPipelineLayer *layer1) + CoglPipelineLayer *layer1, + unsigned long differences_mask, + CoglPipelineEvalFlags flags) { unsigned long layers_difference; + CoglPipelineLayer *authorities0[COGL_PIPELINE_LAYER_STATE_SPARSE_COUNT]; + CoglPipelineLayer *authorities1[COGL_PIPELINE_LAYER_STATE_SPARSE_COUNT]; if (layer0 == layer1) return TRUE; @@ -2940,45 +2977,59 @@ _cogl_pipeline_layer_equal (CoglPipelineLayer *layer0, layers_difference = _cogl_pipeline_layer_compare_differences (layer0, layer1); - if (layers_difference & COGL_PIPELINE_LAYER_STATE_TEXTURE && - !layer_state_equal (COGL_PIPELINE_LAYER_STATE_TEXTURE, - layer0, layer1, - _cogl_pipeline_layer_texture_equal)) - return FALSE; + /* Only compare the sparse state groups requested by the caller... */ + layers_difference &= differences_mask; + + _cogl_pipeline_layer_resolve_authorities (layer0, + layers_difference, + authorities0); + _cogl_pipeline_layer_resolve_authorities (layer1, + layers_difference, + authorities1); + + if (layers_difference & COGL_PIPELINE_LAYER_STATE_TEXTURE) + { + CoglPipelineLayerStateIndex state_index = + COGL_PIPELINE_LAYER_STATE_TEXTURE_INDEX; + if (!_cogl_pipeline_layer_texture_equal (authorities0[state_index], + authorities1[state_index], + flags)) + return FALSE; + } if (layers_difference & COGL_PIPELINE_LAYER_STATE_COMBINE && - !layer_state_equal (COGL_PIPELINE_LAYER_STATE_COMBINE, - layer0, layer1, + !layer_state_equal (COGL_PIPELINE_LAYER_STATE_COMBINE_INDEX, + authorities0, authorities1, _cogl_pipeline_layer_combine_state_equal)) return FALSE; if (layers_difference & COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT && - !layer_state_equal (COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT, - layer0, layer1, + !layer_state_equal (COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT_INDEX, + authorities0, authorities1, _cogl_pipeline_layer_combine_constant_equal)) return FALSE; if (layers_difference & COGL_PIPELINE_LAYER_STATE_FILTERS && - !layer_state_equal (COGL_PIPELINE_LAYER_STATE_FILTERS, - layer0, layer1, + !layer_state_equal (COGL_PIPELINE_LAYER_STATE_FILTERS_INDEX, + authorities0, authorities1, _cogl_pipeline_layer_filters_equal)) return FALSE; if (layers_difference & COGL_PIPELINE_LAYER_STATE_WRAP_MODES && - !layer_state_equal (COGL_PIPELINE_LAYER_STATE_WRAP_MODES, - layer0, layer1, + !layer_state_equal (COGL_PIPELINE_LAYER_STATE_WRAP_MODES_INDEX, + authorities0, authorities1, _cogl_pipeline_layer_wrap_modes_equal)) return FALSE; if (layers_difference & COGL_PIPELINE_LAYER_STATE_USER_MATRIX && - !layer_state_equal (COGL_PIPELINE_LAYER_STATE_USER_MATRIX, - layer0, layer1, + !layer_state_equal (COGL_PIPELINE_LAYER_STATE_USER_MATRIX_INDEX, + authorities0, authorities1, _cogl_pipeline_layer_user_matrix_equal)) return FALSE; if (layers_difference & COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS && - !layer_state_equal (COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS, - layer0, layer1, + !layer_state_equal (COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS_INDEX, + authorities0, authorities1, _cogl_pipeline_layer_point_sprite_coords_equal)) return FALSE; @@ -3065,9 +3116,15 @@ _cogl_pipeline_blend_state_equal (CoglPipeline *authority0, 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; + if (blend_state0->blend_src_factor_rgb == GL_ONE_MINUS_CONSTANT_COLOR || + blend_state0->blend_src_factor_rgb == GL_CONSTANT_COLOR || + blend_state0->blend_dst_factor_rgb == GL_ONE_MINUS_CONSTANT_COLOR || + blend_state0->blend_dst_factor_rgb == GL_CONSTANT_COLOR) + { + if (!cogl_color_equal (&blend_state0->blend_constant, + &blend_state1->blend_constant)) + return FALSE; + } #endif return TRUE; @@ -3121,7 +3178,9 @@ _cogl_pipeline_user_shader_equal (CoglPipeline *authority0, static gboolean _cogl_pipeline_layers_equal (CoglPipeline *authority0, - CoglPipeline *authority1) + CoglPipeline *authority1, + unsigned long differences, + CoglPipelineEvalFlags flags) { int i; @@ -3134,7 +3193,9 @@ _cogl_pipeline_layers_equal (CoglPipeline *authority0, for (i = 0; i < authority0->n_layers; i++) { if (!_cogl_pipeline_layer_equal (authority0->layers_cache[i], - authority1->layers_cache[i])) + authority1->layers_cache[i], + differences, + flags)) return FALSE; } return TRUE; @@ -3229,21 +3290,55 @@ _cogl_pipeline_compare_differences (CoglPipeline *pipeline0, } static gboolean -simple_property_equal (CoglPipeline *pipeline0, - CoglPipeline *pipeline1, +simple_property_equal (CoglPipeline **authorities0, + CoglPipeline **authorities1, unsigned long pipelines_difference, - CoglPipelineState state, + CoglPipelineStateIndex state_index, CoglPipelineStateComparitor comparitor) { - if (pipelines_difference & state) + if (pipelines_difference & (1L<differences & remaining; + int i; + + if (found == 0) + continue; + + for (i = 0; True; i++) + { + unsigned long state = (1L< found) + break; + } + + remaining &= ~found; + if (remaining == 0) + return; + } + while ((authority = _cogl_pipeline_get_parent (authority))); + + g_assert (remaining == 0); +} + /* Comparison of two arbitrary pipelines is done by: * 1) walking up the parents of each pipeline until a common * ancestor is found, and at each step ORing together the @@ -3252,30 +3347,29 @@ simple_property_equal (CoglPipeline *pipeline0, * 2) using the final difference mask to determine which state * groups to compare. * - * This is used by the Cogl journal to compare pipelines so that it - * can split up geometry that needs different OpenGL state. + * This is used, for example, by the Cogl journal to compare pipelines 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. - * - * When comparing texture layers, _cogl_pipeline_equal will actually - * compare the underlying GL texture handle that the Cogl texture uses - * so that atlas textures and sub textures will be considered equal if - * they point to the same texture. This is useful for comparing - * pipelines in the journal but it means that _cogl_pipeline_equal - * doesn't strictly compare whether the pipelines are the same. If we - * needed those semantics we could perhaps add another function or - * some flags to control the behaviour. - * - * False positives aren't allowed. + * XXX: When comparing texture layers, _cogl_pipeline_equal will actually + * compare the underlying GL texture handle that the Cogl texture uses so that + * atlas textures and sub textures will be considered equal if they point to + * the same texture. This is useful for comparing pipelines in the journal but + * it means that _cogl_pipeline_equal doesn't strictly compare whether the + * pipelines are the same. If we needed those semantics we could perhaps add + * another function or some flags to control the behaviour. */ gboolean _cogl_pipeline_equal (CoglPipeline *pipeline0, CoglPipeline *pipeline1, - gboolean skip_gl_color) + unsigned long differences, + unsigned long layer_differences, + CoglPipelineEvalFlags flags) { - unsigned long pipelines_difference; + unsigned long pipelines_difference; + CoglPipeline *authorities0[COGL_PIPELINE_STATE_SPARSE_COUNT]; + CoglPipeline *authorities1[COGL_PIPELINE_STATE_SPARSE_COUNT]; gboolean ret; + COGL_STATIC_TIMER (pipeline_equal_timer, "Mainloop", /* parent */ "_cogl_pipeline_equal", @@ -3294,7 +3388,8 @@ _cogl_pipeline_equal (CoglPipeline *pipeline0, /* First check non-sparse properties */ - if (pipeline0->real_blend_enable != pipeline1->real_blend_enable) + if (differences & COGL_PIPELINE_STATE_REAL_BLEND_ENABLE && + pipeline0->real_blend_enable != pipeline1->real_blend_enable) goto done; /* Then check sparse properties */ @@ -3302,34 +3397,43 @@ _cogl_pipeline_equal (CoglPipeline *pipeline0, pipelines_difference = _cogl_pipeline_compare_differences (pipeline0, pipeline1); - if (pipelines_difference & COGL_PIPELINE_STATE_COLOR && - !skip_gl_color) + /* Only compare the sparse state groups requested by the caller... */ + pipelines_difference &= differences; + + _cogl_pipeline_resolve_authorities (pipeline0, + pipelines_difference, + authorities0); + _cogl_pipeline_resolve_authorities (pipeline1, + pipelines_difference, + authorities1); + + /* FIXME: we should resolve all the required authorities up front since + * that should reduce some repeat ancestor traversals. */ + + if (pipelines_difference & COGL_PIPELINE_STATE_COLOR) { - CoglPipelineState state = COGL_PIPELINE_STATE_COLOR; - CoglPipeline *authority0 = - _cogl_pipeline_get_authority (pipeline0, state); - CoglPipeline *authority1 = - _cogl_pipeline_get_authority (pipeline1, state); + CoglPipeline *authority0 = authorities0[COGL_PIPELINE_STATE_COLOR_INDEX]; + CoglPipeline *authority1 = authorities1[COGL_PIPELINE_STATE_COLOR_INDEX]; if (!cogl_color_equal (&authority0->color, &authority1->color)) goto done; } - if (!simple_property_equal (pipeline0, pipeline1, + if (!simple_property_equal (authorities0, authorities1, pipelines_difference, - COGL_PIPELINE_STATE_LIGHTING, + COGL_PIPELINE_STATE_LIGHTING_INDEX, _cogl_pipeline_lighting_state_equal)) goto done; - if (!simple_property_equal (pipeline0, pipeline1, + if (!simple_property_equal (authorities0, authorities1, pipelines_difference, - COGL_PIPELINE_STATE_ALPHA_FUNC, + COGL_PIPELINE_STATE_ALPHA_FUNC_INDEX, _cogl_pipeline_alpha_func_state_equal)) goto done; - if (!simple_property_equal (pipeline0, pipeline1, + if (!simple_property_equal (authorities0, authorities1, pipelines_difference, - COGL_PIPELINE_STATE_ALPHA_FUNC_REFERENCE, + COGL_PIPELINE_STATE_ALPHA_FUNC_REFERENCE_INDEX, _cogl_pipeline_alpha_func_reference_state_equal)) goto done; @@ -3338,11 +3442,8 @@ _cogl_pipeline_equal (CoglPipeline *pipeline0, if (pipeline0->real_blend_enable && pipelines_difference & COGL_PIPELINE_STATE_BLEND) { - CoglPipelineState state = COGL_PIPELINE_STATE_BLEND; - CoglPipeline *authority0 = - _cogl_pipeline_get_authority (pipeline0, state); - CoglPipeline *authority1 = - _cogl_pipeline_get_authority (pipeline1, state); + CoglPipeline *authority0 = authorities0[COGL_PIPELINE_STATE_BLEND_INDEX]; + CoglPipeline *authority1 = authorities1[COGL_PIPELINE_STATE_BLEND_INDEX]; if (!_cogl_pipeline_blend_state_equal (authority0, authority1)) goto done; @@ -3351,42 +3452,46 @@ _cogl_pipeline_equal (CoglPipeline *pipeline0, /* XXX: we don't need to compare the BLEND_ENABLE state because it's * already reflected in ->real_blend_enable */ #if 0 - if (!simple_property_equal (pipeline0, pipeline1, + if (!simple_property_equal (authorities0, authorities1, pipelines_difference, - COGL_PIPELINE_STATE_BLEND, + COGL_PIPELINE_STATE_BLEND_INDEX, _cogl_pipeline_blend_enable_equal)) return FALSE; #endif - if (!simple_property_equal (pipeline0, pipeline1, + if (!simple_property_equal (authorities0, authorities1, pipelines_difference, - COGL_PIPELINE_STATE_DEPTH, + COGL_PIPELINE_STATE_DEPTH_INDEX, _cogl_pipeline_depth_state_equal)) goto done; - if (!simple_property_equal (pipeline0, pipeline1, + if (!simple_property_equal (authorities0, authorities1, pipelines_difference, - COGL_PIPELINE_STATE_FOG, + COGL_PIPELINE_STATE_FOG_INDEX, _cogl_pipeline_fog_state_equal)) goto done; - if (!simple_property_equal (pipeline0, pipeline1, + if (!simple_property_equal (authorities0, authorities1, pipelines_difference, - COGL_PIPELINE_STATE_POINT_SIZE, + COGL_PIPELINE_STATE_POINT_SIZE_INDEX, _cogl_pipeline_point_size_equal)) goto done; - if (!simple_property_equal (pipeline0, pipeline1, + if (!simple_property_equal (authorities0, authorities1, pipelines_difference, - COGL_PIPELINE_STATE_USER_SHADER, + COGL_PIPELINE_STATE_USER_SHADER_INDEX, _cogl_pipeline_user_shader_equal)) goto done; - if (!simple_property_equal (pipeline0, pipeline1, - pipelines_difference, - COGL_PIPELINE_STATE_LAYERS, - _cogl_pipeline_layers_equal)) - goto done; + if (pipelines_difference & COGL_PIPELINE_STATE_LAYERS) + { + CoglPipelineStateIndex state_index = COGL_PIPELINE_STATE_LAYERS_INDEX; + if (!_cogl_pipeline_layers_equal (authorities0[state_index], + authorities1[state_index], + layer_differences, + flags)) + goto done; + } ret = TRUE; done: