From 4307e65f937538a6251738a8450babce5ed9e8e6 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Thu, 11 Nov 2010 16:28:45 +0000 Subject: [PATCH] primitives: validate with _cogl_pipeline_foreach_layer Switch _cogl_rectangles_with_multitexture_coords to using _cogl_pipeline_foreach_layer to iterate the layers of a pipeline when validating instead of iterating the pipelines internal list, which is risky since any modifications to pipelines (even to an override pipeline derived from the original), could potentially corrupt the list as it is being iterated. --- clutter/cogl/cogl/cogl-pipeline-private.h | 3 +- clutter/cogl/cogl/cogl-pipeline.c | 6 +- clutter/cogl/cogl/cogl-primitives.c | 324 ++++++++++++---------- 3 files changed, 180 insertions(+), 153 deletions(-) diff --git a/clutter/cogl/cogl/cogl-pipeline-private.h b/clutter/cogl/cogl/cogl-pipeline-private.h index 611cd8a61..c0477c3d8 100644 --- a/clutter/cogl/cogl/cogl-pipeline-private.h +++ b/clutter/cogl/cogl/cogl-pipeline-private.h @@ -599,7 +599,8 @@ gboolean _cogl_pipeline_get_real_blend_enabled (CoglPipeline *pipeline); gboolean -_cogl_pipeline_layer_has_user_matrix (CoglPipelineLayer *layer); +_cogl_pipeline_layer_has_user_matrix (CoglPipeline *pipeline, + int layer_index); /* * Calls the pre_paint method on the layer texture if there is diff --git a/clutter/cogl/cogl/cogl-pipeline.c b/clutter/cogl/cogl/cogl-pipeline.c index 29250c5f8..149a4a67f 100644 --- a/clutter/cogl/cogl/cogl-pipeline.c +++ b/clutter/cogl/cogl/cogl-pipeline.c @@ -5046,11 +5046,13 @@ _cogl_pipeline_layer_get_texture (CoglPipelineLayer *layer) } gboolean -_cogl_pipeline_layer_has_user_matrix (CoglPipelineLayer *layer) +_cogl_pipeline_layer_has_user_matrix (CoglPipeline *pipeline, + int layer_index) { + CoglPipelineLayer *layer; CoglPipelineLayer *authority; - g_return_val_if_fail (_cogl_is_pipeline_layer (layer), FALSE); + layer = _cogl_pipeline_get_layer (pipeline, layer_index); authority = _cogl_pipeline_layer_get_authority (layer, diff --git a/clutter/cogl/cogl/cogl-primitives.c b/clutter/cogl/cogl/cogl-primitives.c index e3c69463b..eb487fad3 100644 --- a/clutter/cogl/cogl/cogl-primitives.c +++ b/clutter/cogl/cogl/cogl-primitives.c @@ -513,7 +513,6 @@ validate_tex_coords_cb (CoglPipeline *pipeline, static gboolean _cogl_multitexture_quad_single_primitive (const float *position, CoglPipeline *pipeline, - guint32 fallback_layers, const float *user_tex_coords, int user_tex_coords_len) { @@ -555,6 +554,153 @@ _cogl_multitexture_quad_single_primitive (const float *position, return TRUE; } +typedef struct _ValidateLayerState +{ + int i; + int first_layer; + CoglPipeline *override_source; + gboolean all_use_sliced_quad_fallback; +} ValidateLayerState; + +static gboolean +_cogl_rectangles_validate_layer_cb (CoglPipeline *pipeline, + int layer_index, + void *user_data) +{ + ValidateLayerState *state = user_data; + CoglHandle texture; + + state->i++; + + /* We need to ensure the mipmaps are ready before deciding + * anything else about the texture because the texture storage + * could completely change if it needs to be migrated out of the + * atlas and will affect how we validate the layer. + * + * FIXME: this needs to be generalized. There could be any + * number of things that might require a shuffling of the + * underlying texture storage. We could add two mechanisms to + * generalize this a bit... + * + * 1) add a _cogl_pipeline_layer_update_storage() function that + * would for instance consider if mipmapping is necessary and + * potentially migrate the texture from an atlas. + * + * 2) allow setting of transient primitive-flags on a pipeline + * that may affect the outcome of _update_storage(). One flag + * could indicate that we expect to sample beyond the bounds of + * the texture border. + * + * flags = COGL_PIPELINE_PRIMITIVE_FLAG_VALID_BORDERS; + * _cogl_pipeline_layer_assert_primitive_flags (layer, flags) + * _cogl_pipeline_layer_update_storage (layer) + * enqueue primitive in journal + * + * when the primitive is dequeued and drawn we should: + * _cogl_pipeline_flush_gl_state (pipeline) + * draw primitive + * _cogl_pipeline_unassert_primitive_flags (layer, flags); + * + * _cogl_pipeline_layer_update_storage should take into + * consideration all the asserted primitive requirements. (E.g. + * there could be multiple primitives in the journal - or in a + * renderlist in the future - that need mipmaps or that need + * valid contents beyond their borders (for cogl_polygon) + * meaning they can't work with textures in an atas, so + * _cogl_pipeline_layer_update_storage would pass on these + * requirements to the texture atlas backend which would make + * sure the referenced texture is migrated out of the atlas and + * mipmaps are generated.) + */ + _cogl_pipeline_pre_paint_for_layer (pipeline, layer_index); + + texture = _cogl_pipeline_get_layer_texture (pipeline, layer_index); + + /* COGL_INVALID_HANDLE textures are handled by + * _cogl_pipeline_flush_gl_state */ + if (texture == COGL_INVALID_HANDLE) + return TRUE; + + if (state->i == 0) + state->first_layer = layer_index; + + /* XXX: + * For now, if the first layer is sliced then all other layers are + * ignored since we currently don't support multi-texturing with + * sliced textures. If the first layer is not sliced then any other + * layers found to be sliced will be skipped. (with a warning) + * + * TODO: Add support for multi-texturing rectangles with sliced + * textures if no texture matrices are in use. + */ + if (cogl_texture_is_sliced (texture)) + { + if (state->i == 0) + { + static gboolean warning_seen = FALSE; + + if (!state->override_source) + state->override_source = cogl_pipeline_copy (pipeline); + _cogl_pipeline_prune_to_n_layers (state->override_source, 1); + state->all_use_sliced_quad_fallback = TRUE; + + if (!warning_seen) + g_warning ("Skipping layers 1..n of your pipeline since " + "the first layer is sliced. We don't currently " + "support any multi-texturing with sliced " + "textures but assume layer 0 is the most " + "important to keep"); + warning_seen = TRUE; + + return FALSE; + } + else + { + static gboolean warning_seen = FALSE; + + _COGL_GET_CONTEXT (ctx, FALSE); + + if (!warning_seen) + g_warning ("Skipping layer %d of your pipeline consisting of " + "a sliced texture (unsuported for multi texturing)", + state->i); + warning_seen = TRUE; + + /* Note: currently only 2D textures can be sliced. */ + cogl_pipeline_set_layer_texture (pipeline, layer_index, + ctx->default_gl_texture_2d_tex); + return TRUE; + } + } + +#ifdef COGL_ENABLE_DEBUG + /* If the texture can't be repeated with the GPU (e.g. because it has + * waste or if using GL_TEXTURE_RECTANGLE_ARB) then if a texture matrix + * is also in use we don't know if the result will end up trying + * to texture from the waste area. + * + * Note: we check can_hardware_repeat() first since it's cheaper. + * + * Note: cases where the texture coordinates will require repeating + * will be caught by later validation. + */ + if (!_cogl_texture_can_hardware_repeat (texture) && + _cogl_pipeline_layer_has_user_matrix (pipeline, layer_index)) + { + static gboolean warning_seen = FALSE; + if (!warning_seen) + g_warning ("layer %d of your pipeline uses a custom " + "texture matrix but because the texture doesn't " + "support hardware repeating you may see artefacts " + "due to sampling beyond the texture's bounds.", + state->i); + warning_seen = TRUE; + } +#endif + + return TRUE; +} + struct _CoglMutiTexturedRect { const float *position; /* x0,y0,x1,y1 */ @@ -568,144 +714,26 @@ _cogl_rectangles_with_multitexture_coords ( int n_rects) { CoglPipeline *pipeline; - const GList *layers; - int n_layers; - const GList *tmp; - guint32 fallback_layers = 0; - gboolean all_use_sliced_quad_fallback = FALSE; + ValidateLayerState state; int i; _COGL_GET_CONTEXT (ctx, NO_RETVAL); pipeline = cogl_get_source (); - layers = _cogl_pipeline_get_layers (pipeline); - n_layers = cogl_pipeline_get_n_layers (pipeline); - /* * Validate all the layers of the current source pipeline... */ + state.i = -1; + state.first_layer = 0; + state.override_source = NULL; + state.all_use_sliced_quad_fallback = FALSE; + cogl_pipeline_foreach_layer (pipeline, + _cogl_rectangles_validate_layer_cb, + &state); - for (tmp = layers, i = 0; tmp != NULL; tmp = tmp->next, i++) - { - CoglHandle layer = tmp->data; - CoglHandle tex_handle; - - /* We need to ensure the mipmaps are ready before deciding - * anything else about the texture because the texture storage - * could completely change if it needs to be migrated out of the - * atlas and will affect how we validate the layer. - * - * FIXME: this needs to be generalized. There could be any - * number of things that might require a shuffling of the - * underlying texture storage. We could add two mechanisms to - * generalize this a bit... - * - * 1) add a _cogl_pipeline_layer_update_storage() function that - * would for instance consider if mipmapping is necessary and - * potentially migrate the texture from an atlas. - * - * 2) allow setting of transient primitive-flags on a pipeline - * that may affect the outcome of _update_storage(). One flag - * could indicate that we expect to sample beyond the bounds of - * the texture border. - * - * flags = COGL_PIPELINE_PRIMITIVE_FLAG_VALID_BORDERS; - * _cogl_pipeline_layer_assert_primitive_flags (layer, flags) - * _cogl_pipeline_layer_update_storage (layer) - * enqueue primitive in journal - * - * when the primitive is dequeued and drawn we should: - * _cogl_pipeline_flush_gl_state (pipeline) - * draw primitive - * _cogl_pipeline_unassert_primitive_flags (layer, flags); - * - * _cogl_pipeline_layer_update_storage should take into - * consideration all the asserted primitive requirements. (E.g. - * there could be multiple primitives in the journal - or in a - * renderlist in the future - that need mipmaps or that need - * valid contents beyond their borders (for cogl_polygon) - * meaning they can't work with textures in an atas, so - * _cogl_pipeline_layer_update_storage would pass on these - * requirements to the texture atlas backend which would make - * sure the referenced texture is migrated out of the atlas and - * mipmaps are generated.) - */ - _cogl_pipeline_layer_pre_paint (layer); - - tex_handle = _cogl_pipeline_layer_get_texture (layer); - - /* COGL_INVALID_HANDLE textures are handled by - * _cogl_pipeline_flush_gl_state */ - if (tex_handle == COGL_INVALID_HANDLE) - continue; - - /* XXX: - * For now, if the first layer is sliced then all other layers are - * ignored since we currently don't support multi-texturing with - * sliced textures. If the first layer is not sliced then any other - * layers found to be sliced will be skipped. (with a warning) - * - * TODO: Add support for multi-texturing rectangles with sliced - * textures if no texture matrices are in use. - */ - if (cogl_texture_is_sliced (tex_handle)) - { - if (i == 0) - { - fallback_layers = ~1; /* fallback all except the first layer */ - all_use_sliced_quad_fallback = TRUE; - if (tmp->next) - { - static gboolean warning_seen = FALSE; - if (!warning_seen) - g_warning ("Skipping layers 1..n of your pipeline since " - "the first layer is sliced. We don't currently " - "support any multi-texturing with sliced " - "textures but assume layer 0 is the most " - "important to keep"); - warning_seen = TRUE; - } - break; - } - else - { - static gboolean warning_seen = FALSE; - if (!warning_seen) - g_warning ("Skipping layer %d of your pipeline consisting of " - "a sliced texture (unsuported for multi texturing)", - i); - warning_seen = TRUE; - - /* NB: marking for fallback will replace the layer with - * a default transparent texture */ - fallback_layers |= (1 << i); - continue; - } - } - - /* If the texture can't be repeated with the GPU (e.g. because it has - * waste or if using GL_TEXTURE_RECTANGLE_ARB) then we don't support - * multi texturing since we don't know if the result will end up trying - * to texture from the waste area. */ - if (_cogl_pipeline_layer_has_user_matrix (layer) - && !_cogl_texture_can_hardware_repeat (tex_handle)) - { - static gboolean warning_seen = FALSE; - if (!warning_seen) - g_warning ("Skipping layer %d of your pipeline since a custom " - "texture matrix was given for a texture that can't be " - "repeated using the GPU and the result may try to " - "sample beyond the bounds of the texture ", - i); - warning_seen = TRUE; - - /* NB: marking for fallback will replace the layer with - * a default transparent texture */ - fallback_layers |= (1 << i); - continue; - } - } + if (state.override_source) + pipeline = state.override_source; /* * Emit geometry for each of the rectangles... @@ -713,17 +741,16 @@ _cogl_rectangles_with_multitexture_coords ( for (i = 0; i < n_rects; i++) { - CoglHandle first_layer, tex_handle; - const float default_tex_coords[4] = {0.0, 0.0, 1.0, 1.0}; + CoglHandle texture; + const float default_tex_coords[4] = {0.0, 0.0, 1.0, 1.0}; const float *tex_coords; - gboolean clamp_s, clamp_t; + gboolean clamp_s, clamp_t; - if (!all_use_sliced_quad_fallback) + if (!state.all_use_sliced_quad_fallback) { gboolean success = _cogl_multitexture_quad_single_primitive (rects[i].position, pipeline, - fallback_layers, rects[i].tex_coords, rects[i].tex_coords_len); @@ -738,23 +765,23 @@ _cogl_rectangles_with_multitexture_coords ( /* If multitexturing failed or we are drawing with a sliced texture * then we only support a single layer so we pluck out the texture * from the first pipeline layer... */ - layers = _cogl_pipeline_get_layers (pipeline); - first_layer = layers->data; - tex_handle = _cogl_pipeline_layer_get_texture (first_layer); + texture = _cogl_pipeline_get_layer_texture (pipeline, state.first_layer); if (rects[i].tex_coords) tex_coords = rects[i].tex_coords; else tex_coords = default_tex_coords; - clamp_s = (_cogl_pipeline_layer_get_wrap_mode_s (first_layer) == + clamp_s = (cogl_pipeline_get_layer_wrap_mode_s (pipeline, + state.first_layer) == COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE); - clamp_t = (_cogl_pipeline_layer_get_wrap_mode_t (first_layer) == + clamp_t = (cogl_pipeline_get_layer_wrap_mode_t (pipeline, + state.first_layer) == COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE); COGL_NOTE (DRAW, "Drawing Tex Quad (Multi-Prim Mode)"); - _cogl_texture_quad_multiple_primitives (tex_handle, + _cogl_texture_quad_multiple_primitives (texture, pipeline, clamp_s, clamp_t, rects[i].position, @@ -764,11 +791,8 @@ _cogl_rectangles_with_multitexture_coords ( tex_coords[3]); } -#if 0 - /* XXX: The current journal doesn't handle changes to the model view matrix - * so for now we force a flush at the end of every primitive. */ - _cogl_journal_flush (); -#endif + if (state.override_source) + cogl_object_unref (pipeline); } void @@ -971,10 +995,10 @@ typedef struct _ValidateState CoglPipeline *pipeline; } ValidateState; -gboolean -validate_layer_cb (CoglPipeline *pipeline, - int layer_index, - void *user_data) +static gboolean +_cogl_polygon_validate_layer_cb (CoglPipeline *pipeline, + int layer_index, + void *user_data) { ValidateState *state = user_data; @@ -1029,7 +1053,7 @@ cogl_polygon (const CoglTextureVertex *vertices, validate_state.original_pipeline = pipeline; validate_state.pipeline = pipeline; cogl_pipeline_foreach_layer (pipeline, - validate_layer_cb, + _cogl_polygon_validate_layer_cb, &validate_state); pipeline = validate_state.pipeline;