snippet: Add a hook for the layer fragment processing

This adds a hook to replace or wrap the fragment processing for a
particular layer.

Reviewed-by: Robert Bragg <robert@linux.intel.com>
This commit is contained in:
Neil Roberts 2011-11-25 23:16:31 +00:00 committed by Robert Bragg
parent 4581ce5f15
commit b7e15929b6
3 changed files with 201 additions and 53 deletions

View File

@ -369,16 +369,7 @@ add_constant_lookup (CoglPipelineShaderState *shader_state,
{
int unit_index = _cogl_pipeline_layer_get_unit_index (layer);
/* Create a sampler uniform for this layer if we haven't already */
if (!shader_state->unit_state[unit_index].combine_constant_used)
{
g_string_append_printf (shader_state->header,
"uniform vec4 _cogl_layer_constant_%i;\n",
unit_index);
shader_state->unit_state[unit_index].combine_constant_used = TRUE;
}
g_string_append_printf (shader_state->source,
g_string_append_printf (shader_state->header,
"_cogl_layer_constant_%i.%s",
unit_index, swizzle);
}
@ -532,7 +523,7 @@ add_arg (CoglPipelineShaderState *shader_state,
CoglPipelineCombineOp operand,
const char *swizzle)
{
GString *shader_source = shader_state->source;
GString *shader_source = shader_state->header;
char alpha_swizzle[5] = "aaaa";
g_string_append_c (shader_source, '(');
@ -630,9 +621,22 @@ ensure_arg_generated (CoglPipeline *pipeline,
switch (src)
{
case COGL_PIPELINE_COMBINE_SOURCE_CONSTANT:
case COGL_PIPELINE_COMBINE_SOURCE_PRIMARY_COLOR:
/* These don't involve any other layers */
/* This doesn't involve any other layers */
break;
case COGL_PIPELINE_COMBINE_SOURCE_CONSTANT:
{
int unit_index = _cogl_pipeline_layer_get_unit_index (layer);
/* Create a sampler uniform for this layer if we haven't already */
if (!shader_state->unit_state[unit_index].combine_constant_used)
{
g_string_append_printf (shader_state->header,
"uniform vec4 _cogl_layer_constant_%i;\n",
unit_index);
shader_state->unit_state[unit_index].combine_constant_used = TRUE;
}
}
break;
case COGL_PIPELINE_COMBINE_SOURCE_PREVIOUS:
@ -667,6 +671,20 @@ ensure_arg_generated (CoglPipeline *pipeline,
}
}
static void
ensure_args_for_func (CoglPipeline *pipeline,
CoglPipelineLayer *layer,
int previous_layer_index,
CoglPipelineCombineFunc function,
CoglPipelineCombineSource *src)
{
int n_args = _cogl_get_n_args_for_combine_func (function);
int i;
for (i = 0; i < n_args; i++)
ensure_arg_generated (pipeline, layer, previous_layer_index, src[i]);
}
static void
append_masked_combine (CoglPipeline *pipeline,
CoglPipelineLayer *layer,
@ -677,18 +695,10 @@ append_masked_combine (CoglPipeline *pipeline,
CoglPipelineCombineOp *op)
{
CoglPipelineShaderState *shader_state = get_shader_state (pipeline);
GString *shader_source = shader_state->source;
int n_args;
int i;
GString *shader_source = shader_state->header;
n_args = _cogl_get_n_args_for_combine_func (function);
for (i = 0; i < n_args; i++)
ensure_arg_generated (pipeline, layer, previous_layer_index, src[i]);
g_string_append_printf (shader_state->source,
" cogl_layer%i.%s = ",
layer->index,
g_string_append_printf (shader_state->header,
" cogl_layer.%s = ",
swizzle);
switch (function)
@ -785,6 +795,7 @@ ensure_layer_generated (CoglPipeline *pipeline,
CoglPipelineLayer *combine_authority;
CoglPipelineLayerBigState *big_state;
CoglPipelineLayer *layer;
CoglPipelineSnippetData snippet_data;
LayerData *layer_data;
/* Find the layer that corresponds to this layer_num */
@ -815,37 +826,93 @@ ensure_layer_generated (CoglPipeline *pipeline,
"vec4 cogl_layer%i;\n",
layer_index);
if (!_cogl_pipeline_layer_needs_combine_separate (combine_authority) ||
/* GL_DOT3_RGBA Is a bit weird as a GL_COMBINE_RGB function
* since if you use it, it overrides your ALPHA function...
*/
big_state->texture_combine_rgb_func ==
COGL_PIPELINE_COMBINE_FUNC_DOT3_RGBA)
append_masked_combine (pipeline,
layer,
layer_data->previous_layer_index,
"rgba",
big_state->texture_combine_rgb_func,
big_state->texture_combine_rgb_src,
big_state->texture_combine_rgb_op);
else
/* Skip the layer generation if there is a snippet that replaces the
default layer code. This is important because generating this
code may cause the code for other layers to be generated and
stored in the global variable. If this code isn't actually used
then the global variables would be uninitialised and they may be
used from other layers */
if (!has_replace_hook (layer, COGL_SNIPPET_HOOK_LAYER_FRAGMENT))
{
append_masked_combine (pipeline,
layer,
layer_data->previous_layer_index,
"rgb",
big_state->texture_combine_rgb_func,
big_state->texture_combine_rgb_src,
big_state->texture_combine_rgb_op);
append_masked_combine (pipeline,
layer,
layer_data->previous_layer_index,
"a",
big_state->texture_combine_alpha_func,
big_state->texture_combine_alpha_src,
big_state->texture_combine_alpha_op);
ensure_args_for_func (pipeline,
layer,
layer_data->previous_layer_index,
big_state->texture_combine_rgb_func,
big_state->texture_combine_rgb_src);
ensure_args_for_func (pipeline,
layer,
layer_data->previous_layer_index,
big_state->texture_combine_alpha_func,
big_state->texture_combine_alpha_src);
g_string_append_printf (shader_state->header,
"vec4\n"
"cogl_real_generate_layer%i ()\n"
"{\n"
" vec4 cogl_layer;\n",
layer_index);
if (!_cogl_pipeline_layer_needs_combine_separate (combine_authority) ||
/* GL_DOT3_RGBA Is a bit weird as a GL_COMBINE_RGB function
* since if you use it, it overrides your ALPHA function...
*/
big_state->texture_combine_rgb_func ==
COGL_PIPELINE_COMBINE_FUNC_DOT3_RGBA)
append_masked_combine (pipeline,
layer,
layer_data->previous_layer_index,
"rgba",
big_state->texture_combine_rgb_func,
big_state->texture_combine_rgb_src,
big_state->texture_combine_rgb_op);
else
{
append_masked_combine (pipeline,
layer,
layer_data->previous_layer_index,
"rgb",
big_state->texture_combine_rgb_func,
big_state->texture_combine_rgb_src,
big_state->texture_combine_rgb_op);
append_masked_combine (pipeline,
layer,
layer_data->previous_layer_index,
"a",
big_state->texture_combine_alpha_func,
big_state->texture_combine_alpha_src,
big_state->texture_combine_alpha_op);
}
g_string_append (shader_state->header,
" return cogl_layer;\n"
"}\n");
}
/* Wrap the layer code in any snippets that have been hooked */
memset (&snippet_data, 0, sizeof (snippet_data));
snippet_data.snippets = get_layer_fragment_snippets (layer);
snippet_data.hook = COGL_SNIPPET_HOOK_LAYER_FRAGMENT;
snippet_data.chain_function = g_strdup_printf ("cogl_real_generate_layer%i",
layer_index);
snippet_data.final_name = g_strdup_printf ("cogl_generate_layer%i",
layer_index);
snippet_data.function_prefix = g_strdup_printf ("cogl_generate_layer%i",
layer_index);
snippet_data.return_type = "vec4";
snippet_data.return_variable = "cogl_layer";
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_layer%i = cogl_generate_layer%i ();\n",
layer_index,
layer_index);
g_slice_free (LayerData, layer_data);
}

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_LAYER_FRAGMENT: A hook for the fragment
* processing of a particular layer.
* @COGL_SNIPPET_HOOK_TEXTURE_LOOKUP: A hook for the texture lookup
* stage of a given layer in a pipeline.
*
@ -127,6 +129,39 @@ typedef struct _CoglSnippet CoglSnippet;
* </glossdef>
* </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
* for a layer or to modify the results.
* </para>
* <para>
* Within the snippet code for this hook there is an extra vec4
* variable called cogl_layer. This contains the resulting color
* that will be used for the layer. This can be modified in the post
* section or it the default processing can be replaced entirely using
* the replace section.
* </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.
* </para>
* <para>
* If a replace string is given then this will be used instead of
* the default fragment processing for this layer. The snippet must write to
* the cogl_layer variable in that case.
* </para>
* <para>
* The post string in @snippet will be inserted just after the
* fragment processing for the layer. The results can be modified by changing
* the value of the cogl_layer variable.
* </para>
* </glossentry>
* <glossentry>
* <glossterm>%COGL_SNIPPET_HOOK_TEXTURE_LOOKUP</glossterm>
* Adds a shader snippet that will hook on to the texture lookup part
* of a given layer. This gives a chance for the application to modify
@ -178,7 +213,8 @@ typedef enum {
/* ... = 4096 */
/* Per layer fragment hooks */
COGL_SNIPPET_HOOK_TEXTURE_LOOKUP = 6144
COGL_SNIPPET_HOOK_LAYER_FRAGMENT = 6144,
COGL_SNIPPET_HOOK_TEXTURE_LOOKUP
} CoglSnippetHook;
#define cogl_snippet_new cogl_snippet_new_EXP

View File

@ -268,6 +268,49 @@ paint (TestState *state)
cogl_pop_source ();
cogl_object_unref (pipeline);
/* Test replacing the layer code */
pipeline = create_texture_pipeline ();
snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_LAYER_FRAGMENT, NULL, NULL);
cogl_snippet_set_replace (snippet, "cogl_layer = vec4 (0.0, 0.0, 1.0, 1.0);");
cogl_pipeline_add_layer_snippet (pipeline, 0, snippet);
cogl_object_unref (snippet);
/* Add a second layer which samples from the texture in the first
layer. The snippet override should cause the first layer not to
generate the code for the texture lookup but this second layer
should still be able to cause it to be generated */
cogl_pipeline_set_layer_combine (pipeline, 1,
"RGB = ADD(TEXTURE_0, PREVIOUS)"
"A = REPLACE(PREVIOUS)",
NULL);
cogl_push_source (pipeline);
cogl_rectangle_with_texture_coords (110, 0, 120, 10,
0, 0, 0, 0);
cogl_pop_source ();
cogl_object_unref (pipeline);
/* Test modifying the layer code */
pipeline = cogl_pipeline_new ();
cogl_pipeline_set_uniform_1f (pipeline,
cogl_pipeline_get_uniform_location (pipeline,
"a_value"),
0.5);
snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_LAYER_FRAGMENT,
"uniform float a_value;",
"cogl_layer.g = a_value;");
cogl_pipeline_add_layer_snippet (pipeline, 0, snippet);
cogl_object_unref (snippet);
cogl_push_source (pipeline);
cogl_rectangle_with_texture_coords (120, 0, 130, 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");
@ -318,6 +361,8 @@ validate_result (void)
test_utils_check_pixel (85, 5, 0x00ffffff);
test_utils_check_pixel (95, 5, 0x0000ffff);
test_utils_check_pixel (105, 5, 0xff0000ff);
test_utils_check_pixel (115, 5, 0xff00ffff);
test_utils_check_pixel (125, 5, 0xff80ffff);
}
void