snippet: Add a hook for the layer texture coordinate transformation

This adds a hook called COGL_SNIPPET_HOOK_TEXTURE_COORD_TRANSFORM.
This can be used to alter the application of the layer user matrix to
a texture coordinate or it can bypass it altogether.

This is the first per-layer hook that affects the vertex shader state
so the patch includes the boilerplate needed to get that to work.

Reviewed-by: Robert Bragg <robert@linux.intel.com>
This commit is contained in:
Neil Roberts 2011-11-28 19:58:15 +00:00 committed by Robert Bragg
parent b7e15929b6
commit 68b3643b25
11 changed files with 268 additions and 12 deletions

View File

@ -79,6 +79,7 @@ typedef enum
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_VERTEX_SNIPPETS_INDEX,
COGL_PIPELINE_LAYER_STATE_FRAGMENT_SNIPPETS_INDEX,
/* note: layers don't currently have any non-sparse state */
@ -117,6 +118,8 @@ typedef enum
COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS =
1L<<COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS_INDEX,
COGL_PIPELINE_LAYER_STATE_VERTEX_SNIPPETS =
1L<<COGL_PIPELINE_LAYER_STATE_VERTEX_SNIPPETS_INDEX,
COGL_PIPELINE_LAYER_STATE_FRAGMENT_SNIPPETS =
1L<<COGL_PIPELINE_LAYER_STATE_FRAGMENT_SNIPPETS_INDEX,
@ -139,15 +142,18 @@ typedef enum
COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT | \
COGL_PIPELINE_LAYER_STATE_USER_MATRIX | \
COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS | \
COGL_PIPELINE_LAYER_STATE_VERTEX_SNIPPETS | \
COGL_PIPELINE_LAYER_STATE_FRAGMENT_SNIPPETS)
#define COGL_PIPELINE_LAYER_STATE_MULTI_PROPERTY \
(COGL_PIPELINE_LAYER_STATE_FILTERS | \
COGL_PIPELINE_LAYER_STATE_WRAP_MODES | \
COGL_PIPELINE_LAYER_STATE_COMBINE | \
COGL_PIPELINE_LAYER_STATE_VERTEX_SNIPPETS | \
COGL_PIPELINE_LAYER_STATE_FRAGMENT_SNIPPETS)
#define COGL_PIPELINE_LAYER_STATE_AFFECTS_VERTEX_CODEGEN 0
#define COGL_PIPELINE_LAYER_STATE_AFFECTS_VERTEX_CODEGEN \
COGL_PIPELINE_LAYER_STATE_VERTEX_SNIPPETS
typedef enum
{
@ -200,6 +206,7 @@ typedef struct
gboolean point_sprite_coords;
CoglPipelineSnippetList vertex_snippets;
CoglPipelineSnippetList fragment_snippets;
} CoglPipelineLayerBigState;

View File

@ -78,6 +78,10 @@ gboolean
_cogl_pipeline_layer_point_sprite_coords_equal (CoglPipelineLayer *authority0,
CoglPipelineLayer *authority1);
gboolean
_cogl_pipeline_layer_vertex_snippets_equal (CoglPipelineLayer *authority0,
CoglPipelineLayer *authority1);
gboolean
_cogl_pipeline_layer_fragment_snippets_equal (CoglPipelineLayer *authority0,
CoglPipelineLayer *authority1);
@ -127,6 +131,11 @@ _cogl_pipeline_layer_hash_point_sprite_state (CoglPipelineLayer *authority,
CoglPipelineLayer **authorities,
CoglPipelineHashState *state);
void
_cogl_pipeline_layer_hash_vertex_snippets_state (CoglPipelineLayer *authority,
CoglPipelineLayer **authorities,
CoglPipelineHashState *state);
void
_cogl_pipeline_layer_hash_fragment_snippets_state (CoglPipelineLayer *authority,
CoglPipelineLayer **authorities,

View File

@ -775,6 +775,42 @@ cogl_pipeline_get_layer_point_sprite_coords_enabled (CoglPipeline *pipeline,
return authority->big_state->point_sprite_coords;
}
static void
_cogl_pipeline_layer_add_vertex_snippet (CoglPipeline *pipeline,
int layer_index,
CoglSnippet *snippet)
{
CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_VERTEX_SNIPPETS;
CoglPipelineLayer *layer, *authority;
/* Note: this will ensure that the layer exists, creating one if it
* doesn't already.
*
* Note: If the layer already existed it's possibly owned by another
* pipeline. If the layer is created then it will be owned by
* pipeline. */
layer = _cogl_pipeline_get_layer (pipeline, layer_index);
/* Now find the ancestor of the layer that is the authority for the
* state we want to change */
authority = _cogl_pipeline_layer_get_authority (layer, change);
layer = _cogl_pipeline_layer_pre_change_notify (pipeline, layer, change);
_cogl_pipeline_snippet_list_add (&layer->big_state->vertex_snippets,
snippet);
/* If we weren't previously the authority on this state then we need
* to extended our differences mask and so it's possible that some
* of our ancestry will now become redundant, so we aim to reparent
* ourselves if that's true... */
if (layer != authority)
{
layer->differences |= change;
_cogl_pipeline_layer_prune_redundant_ancestry (layer);
}
}
static void
_cogl_pipeline_layer_add_fragment_snippet (CoglPipeline *pipeline,
int layer_index,
@ -821,8 +857,9 @@ cogl_pipeline_add_layer_snippet (CoglPipeline *pipeline,
_COGL_RETURN_IF_FAIL (snippet->hook >= COGL_SNIPPET_FIRST_LAYER_HOOK);
if (snippet->hook < COGL_SNIPPET_FIRST_LAYER_FRAGMENT_HOOK)
/* TODO */
g_assert_not_reached ();
_cogl_pipeline_layer_add_vertex_snippet (pipeline,
layer_index,
snippet);
else
_cogl_pipeline_layer_add_fragment_snippet (pipeline,
layer_index,
@ -966,6 +1003,16 @@ _cogl_pipeline_layer_point_sprite_coords_equal (CoglPipelineLayer *authority0,
return big_state0->point_sprite_coords == big_state1->point_sprite_coords;
}
gboolean
_cogl_pipeline_layer_vertex_snippets_equal (CoglPipelineLayer *authority0,
CoglPipelineLayer *authority1)
{
return _cogl_pipeline_snippet_list_equal (&authority0->big_state->
vertex_snippets,
&authority1->big_state->
vertex_snippets);
}
gboolean
_cogl_pipeline_layer_fragment_snippets_equal (CoglPipelineLayer *authority0,
CoglPipelineLayer *authority1)
@ -1729,6 +1776,15 @@ _cogl_pipeline_layer_hash_point_sprite_state (CoglPipelineLayer *authority,
sizeof (big_state->point_sprite_coords));
}
void
_cogl_pipeline_layer_hash_vertex_snippets_state (CoglPipelineLayer *authority,
CoglPipelineLayer **authorities,
CoglPipelineHashState *state)
{
_cogl_pipeline_snippet_list_hash (&authority->big_state->vertex_snippets,
&state->hash);
}
void
_cogl_pipeline_layer_hash_fragment_snippets_state (CoglPipelineLayer *authority,
CoglPipelineLayer **authorities,

View File

@ -114,6 +114,10 @@ _cogl_pipeline_layer_has_alpha (CoglPipelineLayer *layer)
}
/* All bets are off if the layer contains any snippets */
snippets_authority = _cogl_pipeline_layer_get_authority
(layer, COGL_PIPELINE_LAYER_STATE_VERTEX_SNIPPETS);
if (!COGL_LIST_EMPTY (&snippets_authority->big_state->vertex_snippets))
return TRUE;
snippets_authority = _cogl_pipeline_layer_get_authority
(layer, COGL_PIPELINE_LAYER_STATE_FRAGMENT_SNIPPETS);
if (!COGL_LIST_EMPTY (&snippets_authority->big_state->fragment_snippets))
@ -211,6 +215,11 @@ _cogl_pipeline_layer_init_multi_property_sparse_state (
}
break;
}
case COGL_PIPELINE_LAYER_STATE_VERTEX_SNIPPETS:
_cogl_pipeline_snippet_list_copy (&layer->big_state->vertex_snippets,
&authority->big_state->
vertex_snippets);
break;
case COGL_PIPELINE_LAYER_STATE_FRAGMENT_SNIPPETS:
_cogl_pipeline_snippet_list_copy (&layer->big_state->fragment_snippets,
&authority->big_state->
@ -591,6 +600,12 @@ _cogl_pipeline_layer_equal (CoglPipelineLayer *layer0,
_cogl_pipeline_layer_point_sprite_coords_equal))
return FALSE;
if (layers_difference & COGL_PIPELINE_LAYER_STATE_VERTEX_SNIPPETS &&
!layer_state_equal (COGL_PIPELINE_LAYER_STATE_VERTEX_SNIPPETS_INDEX,
authorities0, authorities1,
_cogl_pipeline_layer_vertex_snippets_equal))
return FALSE;
if (layers_difference & COGL_PIPELINE_LAYER_STATE_FRAGMENT_SNIPPETS &&
!layer_state_equal (COGL_PIPELINE_LAYER_STATE_FRAGMENT_SNIPPETS_INDEX,
authorities0, authorities1,
@ -609,6 +624,9 @@ _cogl_pipeline_layer_free (CoglPipelineLayer *layer)
layer->texture != NULL)
cogl_object_unref (layer->texture);
if (layer->differences & COGL_PIPELINE_LAYER_STATE_VERTEX_SNIPPETS)
_cogl_pipeline_snippet_list_free (&layer->big_state->vertex_snippets);
if (layer->differences & COGL_PIPELINE_LAYER_STATE_FRAGMENT_SNIPPETS)
_cogl_pipeline_snippet_list_free (&layer->big_state->fragment_snippets);

View File

@ -68,6 +68,12 @@ typedef struct
NULL */
const char *return_variable;
/* If this is TRUE then it won't allocate a separate variable for
the return value. Instead it is expected that the snippet will
modify one of the argument variables directly and that will be
returned */
gboolean return_variable_is_argument;
/* The argument names or NULL if there are none */
const char *arguments;

View File

@ -131,7 +131,7 @@ _cogl_pipeline_snippet_generate_code (const CoglPipelineSnippetData *data)
")\n"
"{\n");
if (data->return_type)
if (data->return_type && !data->return_variable_is_argument)
g_string_append_printf (data->source_buf,
" %s %s;\n"
"\n",

View File

@ -1627,10 +1627,37 @@ _cogl_pipeline_has_non_layer_vertex_snippets (CoglPipeline *pipeline)
return !COGL_LIST_EMPTY (&authority->big_state->vertex_snippets);
}
static gboolean
check_layer_has_vertex_snippet (CoglPipelineLayer *layer,
void *user_data)
{
unsigned long state = COGL_PIPELINE_LAYER_STATE_VERTEX_SNIPPETS;
CoglPipelineLayer *authority =
_cogl_pipeline_layer_get_authority (layer, state);
gboolean *found_vertex_snippet = user_data;
if (!COGL_LIST_EMPTY (&authority->big_state->vertex_snippets))
{
*found_vertex_snippet = TRUE;
return FALSE;
}
return TRUE;
}
gboolean
_cogl_pipeline_has_vertex_snippets (CoglPipeline *pipeline)
{
return _cogl_pipeline_has_non_layer_vertex_snippets (pipeline);
gboolean found_vertex_snippet = FALSE;
if (_cogl_pipeline_has_non_layer_vertex_snippets (pipeline))
return TRUE;
_cogl_pipeline_foreach_layer_internal (pipeline,
check_layer_has_vertex_snippet,
&found_vertex_snippet);
return found_vertex_snippet;
}
gboolean

View File

@ -140,6 +140,15 @@ get_vertex_snippets (CoglPipeline *pipeline)
return &pipeline->big_state->vertex_snippets;
}
static CoglPipelineSnippetList *
get_layer_vertex_snippets (CoglPipelineLayer *layer)
{
unsigned long state = COGL_PIPELINE_LAYER_STATE_VERTEX_SNIPPETS;
layer = _cogl_pipeline_layer_get_authority (layer, state);
return &layer->big_state->vertex_snippets;
}
static gboolean
_cogl_pipeline_vertend_glsl_start (CoglPipeline *pipeline,
int n_layers,
@ -293,6 +302,7 @@ _cogl_pipeline_vertend_glsl_add_layer (CoglPipeline *pipeline,
unsigned long layers_difference)
{
CoglPipelineShaderState *shader_state;
CoglPipelineSnippetData snippet_data;
int unit_index;
_COGL_GET_CONTEXT (ctx, FALSE);
@ -338,10 +348,47 @@ _cogl_pipeline_vertend_glsl_add_layer (CoglPipeline *pipeline,
* avoid setting them if not
*/
g_string_append_printf (shader_state->header,
"vec4\n"
"cogl_real_transform_layer%i (mat4 matrix, "
"vec4 tex_coord)\n"
"{\n"
" return matrix * tex_coord;\n"
"}\n",
unit_index);
/* Wrap the layer code in any snippets that have been hooked */
memset (&snippet_data, 0, sizeof (snippet_data));
snippet_data.snippets = get_layer_vertex_snippets (layer);
snippet_data.hook = COGL_SNIPPET_HOOK_TEXTURE_COORD_TRANSFORM;
snippet_data.chain_function = g_strdup_printf ("cogl_real_transform_layer%i",
unit_index);
snippet_data.final_name = g_strdup_printf ("cogl_transform_layer%i",
unit_index);
snippet_data.function_prefix = g_strdup_printf ("cogl_transform_layer%i",
unit_index);
snippet_data.return_type = "vec4";
snippet_data.return_variable = "cogl_tex_coord";
snippet_data.return_variable_is_argument = TRUE;
snippet_data.arguments = "cogl_matrix, cogl_tex_coord";
snippet_data.argument_declarations = "mat4 cogl_matrix, vec4 cogl_tex_coord";
snippet_data.source_buf = shader_state->header;
_cogl_pipeline_snippet_generate_code (&snippet_data);
g_free ((char *) snippet_data.chain_function);
g_free ((char *) snippet_data.final_name);
g_free ((char *) snippet_data.function_prefix);
g_string_append_printf (shader_state->source,
" cogl_tex_coord_out[%i] = "
"cogl_texture_matrix[%i] * cogl_tex_coord%i_in;\n",
unit_index, unit_index, unit_index);
"cogl_transform_layer%i (cogl_texture_matrix[%i],\n"
" "
" cogl_tex_coord%i_in);\n",
unit_index,
unit_index,
unit_index,
unit_index);
return TRUE;
}

View File

@ -2612,6 +2612,9 @@ _cogl_pipeline_init_layer_state_hash_functions (void)
layer_state_hash_functions[COGL_PIPELINE_LAYER_STATE_USER_MATRIX_INDEX] =
_cogl_pipeline_layer_hash_user_matrix_state;
_index = COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS_INDEX;
layer_state_hash_functions[_index] =
_cogl_pipeline_layer_hash_point_sprite_state;
_index = COGL_PIPELINE_LAYER_STATE_VERTEX_SNIPPETS_INDEX;
layer_state_hash_functions[_index] =
_cogl_pipeline_layer_hash_point_sprite_state;
_index = COGL_PIPELINE_LAYER_STATE_FRAGMENT_SNIPPETS_INDEX;
@ -2619,7 +2622,7 @@ _cogl_pipeline_init_layer_state_hash_functions (void)
_cogl_pipeline_layer_hash_fragment_snippets_state;
/* So we get a big error if we forget to update this code! */
g_assert (COGL_PIPELINE_LAYER_STATE_SPARSE_COUNT == 10);
g_assert (COGL_PIPELINE_LAYER_STATE_SPARSE_COUNT == 11);
}
static gboolean

View File

@ -54,6 +54,8 @@ typedef struct _CoglSnippet CoglSnippet;
* stage of the pipeline.
* @COGL_SNIPPET_HOOK_FRAGMENT: A hook for the entire fragment
* processing stage of the pipeline.
* @COGL_SNIPPET_HOOK_TEXTURE_COORD_TRANSFORM: A hook for applying the
* layer matrix to a texture coordinate for a layer.
* @COGL_SNIPPET_HOOK_LAYER_FRAGMENT: A hook for the fragment
* processing of a particular layer.
* @COGL_SNIPPET_HOOK_TEXTURE_LOOKUP: A hook for the texture lookup
@ -129,6 +131,46 @@ typedef struct _CoglSnippet CoglSnippet;
* </glossdef>
* </glossentry>
* <glossentry>
* <glossterm>%COGL_SNIPPET_HOOK_TEXTURE_COORD_TRANSFORM</glossterm>
* Adds a shader snippet that will hook on to the texture coordinate
* transformation of a particular layer. This can be used to replace
* the processing for a layer or to modify the results.
* </para>
* <para>
* Within the snippet code for this hook there are two extra
* variables. The first is a mat4 called cogl_matrix which represents
* the user matrix for this layer. The second is called cogl_tex_coord
* and represents the incoming and outgoing texture coordinate. On
* entry to the hook, cogl_tex_coord contains the value of the
* corresponding texture coordinate attribute for this layer. The hook
* is expected to modify this variable. The output will be passed as a
* varying to the fragment processing stage. The default code will
* just multiply cogl_matrix by cogl_tex_coord and store the result in
* cogl_tex_coord.
* </para>
* <para>
* The declarations string in @snippet will be inserted in the
* global scope of the shader. Use this to declare any uniforms,
* attributes or functions that the snippet requires.
* </para>
* <para>
* The pre string in @snippet will be inserted just before the
* fragment processing for this layer. At this point cogl_tex_coord
* still contains the value of the texture coordinate attribute.
* </para>
* <para>
* If a replace string is given then this will be used instead of
* the default fragment processing for this layer. The snippet can
* modify cogl_tex_coord or leave it as is to apply no transformation.
* </para>
* <para>
* The post string in @snippet will be inserted just after the
* transformation. At this point cogl_tex_coord will contain the
* results of the transformation but it can be further modified by the
* snippet.
* </para>
* </glossentry>
* <glossentry>
* <glossterm>%COGL_SNIPPET_HOOK_LAYER_FRAGMENT</glossterm>
* Adds a shader snippet that will hook on to the fragment processing
* of a particular layer. This can be used to replace the processing
@ -209,8 +251,7 @@ typedef enum {
COGL_SNIPPET_HOOK_FRAGMENT = 2048,
/* Per layer vertex hooks */
/* TODO */
/* ... = 4096 */
COGL_SNIPPET_HOOK_TEXTURE_COORD_TRANSFORM = 4096,
/* Per layer fragment hooks */
COGL_SNIPPET_HOOK_LAYER_FRAGMENT = 6144,

View File

@ -45,6 +45,7 @@ paint (TestState *state)
{
CoglPipeline *pipeline;
CoglSnippet *snippet;
CoglMatrix matrix;
CoglColor color;
int location;
int i;
@ -268,7 +269,7 @@ paint (TestState *state)
cogl_pop_source ();
cogl_object_unref (pipeline);
/* Test replacing the layer code */
/* Test replacing the fragment layer code */
pipeline = create_texture_pipeline ();
snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_LAYER_FRAGMENT, NULL, NULL);
@ -291,7 +292,7 @@ paint (TestState *state)
cogl_pop_source ();
cogl_object_unref (pipeline);
/* Test modifying the layer code */
/* Test modifying the fragment layer code */
pipeline = cogl_pipeline_new ();
cogl_pipeline_set_uniform_1f (pipeline,
@ -311,6 +312,45 @@ paint (TestState *state)
cogl_pop_source ();
cogl_object_unref (pipeline);
/* Test modifying the vertex layer code */
pipeline = create_texture_pipeline ();
cogl_matrix_init_identity (&matrix);
cogl_matrix_translate (&matrix, 0.0f, 1.0f, 0.0f);
cogl_pipeline_set_layer_matrix (pipeline, 0, &matrix);
snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_TEXTURE_COORD_TRANSFORM,
NULL,
"cogl_tex_coord.x = 1.0;");
cogl_pipeline_add_layer_snippet (pipeline, 0, snippet);
cogl_object_unref (snippet);
cogl_push_source (pipeline);
cogl_rectangle_with_texture_coords (130, 0, 140, 10,
0, 0, 0, 0);
cogl_pop_source ();
cogl_object_unref (pipeline);
/* Test replacing the vertex layer code */
pipeline = create_texture_pipeline ();
cogl_matrix_init_identity (&matrix);
cogl_matrix_translate (&matrix, 0.0f, 1.0f, 0.0f);
cogl_pipeline_set_layer_matrix (pipeline, 0, &matrix);
snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_TEXTURE_COORD_TRANSFORM,
NULL,
NULL);
cogl_snippet_set_replace (snippet, "cogl_tex_coord.x = 1.0;\n");
cogl_pipeline_add_layer_snippet (pipeline, 0, snippet);
cogl_object_unref (snippet);
cogl_push_source (pipeline);
cogl_rectangle_with_texture_coords (140, 0, 150, 10,
0, 0, 0, 0);
cogl_pop_source ();
cogl_object_unref (pipeline);
/* Sanity check modifying the snippet */
snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, "foo", "bar");
g_assert_cmpstr (cogl_snippet_get_declarations (snippet), ==, "foo");
@ -363,6 +403,8 @@ validate_result (void)
test_utils_check_pixel (105, 5, 0xff0000ff);
test_utils_check_pixel (115, 5, 0xff00ffff);
test_utils_check_pixel (125, 5, 0xff80ffff);
test_utils_check_pixel (135, 5, 0xffff00ff);
test_utils_check_pixel (145, 5, 0x00ff00ff);
}
void