From 636310a81ebb29be934ceea3933ebae1c6a7c52d Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Mon, 1 Nov 2010 18:33:17 +0000 Subject: [PATCH] _multitexture_quad_single_primitive: avoid wrap overrides This avoids using the wrap mode overrides mechanism to implement _cogl_multitexture_quad_single_primitive which requires memsetting a fairly large array. This updates it to use cogl_pipeline_foreach_layer() and we now derive an override_material to handle changes to the wrap modes instead of using the CoglPipelineWrapModeOverrides. --- cogl/cogl-primitives.c | 265 +++++++++++++++++++++++------------------ 1 file changed, 151 insertions(+), 114 deletions(-) diff --git a/cogl/cogl-primitives.c b/cogl/cogl-primitives.c index c9cf1993a..f8d9b222e 100644 --- a/cogl/cogl-primitives.c +++ b/cogl/cogl-primitives.c @@ -331,6 +331,138 @@ _cogl_texture_quad_multiple_primitives (CoglHandle tex_handle, &state); } +typedef struct _ValidateTexCoordsState +{ + int i; + int n_layers; + const float *user_tex_coords; + int user_tex_coords_len; + float *final_tex_coords; + CoglPipeline *override_pipeline; + gboolean needs_multiple_primitives; +} ValidateTexCoordsState; + +/* + * Validate the texture coordinates for this rectangle. + */ +static gboolean +validate_tex_coords_cb (CoglPipeline *pipeline, + int layer_index, + void *user_data) +{ + ValidateTexCoordsState *state = user_data; + CoglHandle texture; + const float *in_tex_coords; + float *out_tex_coords; + float default_tex_coords[4] = {0.0, 0.0, 1.0, 1.0}; + CoglTransformResult transform_result; + + state->i++; + + texture = _cogl_pipeline_get_layer_texture (pipeline, layer_index); + + /* NB: NULL textures are handled by _cogl_pipeline_flush_gl_state */ + if (!texture) + return TRUE; + + /* FIXME: we should be able to avoid this copying when no + * transform is required by the texture backend and the user + * has supplied enough coordinates for all the layers. + */ + + /* If the user didn't supply texture coordinates for this layer + then use the default coords */ + if (state->i >= state->user_tex_coords_len / 4) + in_tex_coords = default_tex_coords; + else + in_tex_coords = &state->user_tex_coords[state->i * 4]; + + out_tex_coords = &state->final_tex_coords[state->i * 4]; + + memcpy (out_tex_coords, in_tex_coords, sizeof (float) * 4); + + /* Convert the texture coordinates to GL. + */ + transform_result = + _cogl_texture_transform_quad_coords_to_gl (texture, + out_tex_coords); + /* If the texture has waste or we are using GL_TEXTURE_RECT we + * can't handle texture repeating so we can't use the layer if + * repeating is required. + * + * NB: We already know that no texture matrix is being used if the + * texture doesn't support hardware repeat. + */ + if (transform_result == COGL_TRANSFORM_SOFTWARE_REPEAT) + { + if (state->i == 0) + { + if (state->n_layers > 1) + { + static gboolean warning_seen = FALSE; + if (!warning_seen) + g_warning ("Skipping layers 1..n of your material since " + "the first layer doesn't support hardware " + "repeat (e.g. because of waste or use of " + "GL_TEXTURE_RECTANGLE_ARB) and you supplied " + "texture coordinates outside the range [0,1]." + "Falling back to software repeat assuming " + "layer 0 is the most important one keep"); + warning_seen = TRUE; + } + + if (state->override_pipeline) + cogl_object_unref (state->override_pipeline); + state->needs_multiple_primitives = TRUE; + return FALSE; + } + else + { + static gboolean warning_seen = FALSE; + if (!warning_seen) + g_warning ("Skipping layer %d of your material " + "since you have supplied texture coords " + "outside the range [0,1] but the texture " + "doesn't support hardware repeat (e.g. " + "because of waste or use of " + "GL_TEXTURE_RECTANGLE_ARB). This isn't " + "supported with multi-texturing.", state->i); + warning_seen = TRUE; + + cogl_pipeline_set_layer_texture (texture, layer_index, NULL); + } + } + + /* By default WRAP_MODE_AUTOMATIC becomes to CLAMP_TO_EDGE. If + the texture coordinates need repeating then we'll override + this to GL_REPEAT. Otherwise we'll leave it at CLAMP_TO_EDGE + so that it won't blend in pixels from the opposite side when + the full texture is drawn with GL_LINEAR filter mode */ + if (transform_result == COGL_TRANSFORM_HARDWARE_REPEAT) + { + if (cogl_pipeline_get_layer_wrap_mode_s (pipeline, layer_index) == + COGL_PIPELINE_WRAP_MODE_AUTOMATIC) + { + if (!state->override_pipeline) + state->override_pipeline = cogl_pipeline_copy (pipeline); + cogl_pipeline_set_layer_wrap_mode_s (state->override_pipeline, + layer_index, + COGL_PIPELINE_WRAP_MODE_REPEAT); + } + if (cogl_pipeline_get_layer_wrap_mode_t (pipeline, layer_index) == + COGL_PIPELINE_WRAP_MODE_AUTOMATIC) + { + if (!state->override_pipeline) + state->override_pipeline = cogl_pipeline_copy (pipeline); + cogl_pipeline_set_layer_wrap_mode_t (state->override_pipeline, + layer_index, + COGL_PIPELINE_WRAP_MODE_REPEAT); + } + } + + return TRUE; +} + /* This path supports multitexturing but only when each of the layers is * handled with a single GL texture. Also if repeating is necessary then * _cogl_texture_can_hardware_repeat() must return TRUE. @@ -354,131 +486,36 @@ _cogl_multitexture_quad_single_primitive (const float *position, const float *user_tex_coords, int user_tex_coords_len) { - int n_layers = cogl_pipeline_get_n_layers (pipeline); - float *final_tex_coords = alloca (sizeof (float) * 4 * n_layers); - const GList *layers; - GList *tmp; - int i; - CoglPipelineWrapModeOverrides wrap_mode_overrides; - /* This will be set to point to wrap_mode_overrides when an override - is needed */ - CoglPipelineWrapModeOverrides *wrap_mode_overrides_p = NULL; + int n_layers = cogl_pipeline_get_n_layers (pipeline); + ValidateTexCoordsState state; + float *final_tex_coords = alloca (sizeof (float) * 4 * n_layers); _COGL_GET_CONTEXT (ctx, FALSE); - memset (&wrap_mode_overrides, 0, sizeof (wrap_mode_overrides)); + state.i = -1; + state.n_layers = n_layers; + state.user_tex_coords = user_tex_coords; + state.user_tex_coords_len = user_tex_coords_len; + state.final_tex_coords = final_tex_coords; + state.override_pipeline = NULL; + state.needs_multiple_primitives = FALSE; - /* - * Validate the texture coordinates for this rectangle. - */ - layers = _cogl_pipeline_get_layers (pipeline); - for (tmp = (GList *)layers, i = 0; tmp != NULL; tmp = tmp->next, i++) - { - CoglHandle layer = (CoglHandle)tmp->data; - CoglHandle tex_handle; - const float *in_tex_coords; - float *out_tex_coords; - float default_tex_coords[4] = {0.0, 0.0, 1.0, 1.0}; - CoglTransformResult transform_result; + cogl_pipeline_foreach_layer (pipeline, + validate_tex_coords_cb, + &state); - tex_handle = _cogl_pipeline_layer_get_texture (layer); + if (state.needs_multiple_primitives) + return FALSE; - /* COGL_INVALID_HANDLE textures are handled by - * _cogl_pipeline_flush_gl_state */ - if (tex_handle == COGL_INVALID_HANDLE) - continue; - - /* If the user didn't supply texture coordinates for this layer - then use the default coords */ - if (i >= user_tex_coords_len / 4) - in_tex_coords = default_tex_coords; - else - in_tex_coords = &user_tex_coords[i * 4]; - - out_tex_coords = &final_tex_coords[i * 4]; - - memcpy (out_tex_coords, in_tex_coords, sizeof (GLfloat) * 4); - - /* Convert the texture coordinates to GL. - */ - transform_result = - _cogl_texture_transform_quad_coords_to_gl (tex_handle, - out_tex_coords); - /* If the texture has waste or we are using GL_TEXTURE_RECT we - * can't handle texture repeating so we can't use the layer if - * repeating is required. - * - * NB: We already know that no texture matrix is being used if the - * texture doesn't support hardware repeat. - */ - if (transform_result == COGL_TRANSFORM_SOFTWARE_REPEAT) - { - if (i == 0) - { - if (n_layers > 1) - { - static gboolean warning_seen = FALSE; - if (!warning_seen) - g_warning ("Skipping layers 1..n of your material since " - "the first layer doesn't support hardware " - "repeat (e.g. because of waste or use of " - "GL_TEXTURE_RECTANGLE_ARB) and you supplied " - "texture coordinates outside the range [0,1]." - "Falling back to software repeat assuming " - "layer 0 is the most important one keep"); - warning_seen = TRUE; - } - return FALSE; - } - else - { - static gboolean warning_seen = FALSE; - if (!warning_seen) - g_warning ("Skipping layer %d of your material " - "since you have supplied texture coords " - "outside the range [0,1] but the texture " - "doesn't support hardware repeat (e.g. " - "because of waste or use of " - "GL_TEXTURE_RECTANGLE_ARB). This isn't " - "supported with multi-texturing.", i); - warning_seen = TRUE; - - /* NB: marking for fallback will replace the layer with - * a default transparent texture */ - fallback_layers |= (1 << i); - } - } - - /* By default WRAP_MODE_AUTOMATIC becomes to CLAMP_TO_EDGE. If - the texture coordinates need repeating then we'll override - this to GL_REPEAT. Otherwise we'll leave it at CLAMP_TO_EDGE - so that it won't blend in pixels from the opposite side when - the full texture is drawn with GL_LINEAR filter mode */ - if (transform_result == COGL_TRANSFORM_HARDWARE_REPEAT) - { - if (_cogl_pipeline_layer_get_wrap_mode_s (layer) == - COGL_PIPELINE_WRAP_MODE_AUTOMATIC) - { - wrap_mode_overrides.values[i].s - = COGL_PIPELINE_WRAP_MODE_OVERRIDE_REPEAT; - wrap_mode_overrides_p = &wrap_mode_overrides; - } - if (_cogl_pipeline_layer_get_wrap_mode_t (layer) == - COGL_PIPELINE_WRAP_MODE_AUTOMATIC) - { - wrap_mode_overrides.values[i].t - = COGL_PIPELINE_WRAP_MODE_OVERRIDE_REPEAT; - wrap_mode_overrides_p = &wrap_mode_overrides; - } - } - } + if (state.override_pipeline) + pipeline = state.override_pipeline; _cogl_journal_log_quad (position, pipeline, n_layers, - fallback_layers, + 0, /* we don't need fallback layers */ 0, /* don't replace the layer0 texture */ - wrap_mode_overrides_p, + NULL, /* we never use wrap mode overrides */ final_tex_coords, n_layers * 4);