From b7e15929b66ab5a0f6935a5cdcc82c616dbc2d77 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Fri, 25 Nov 2011 23:16:31 +0000 Subject: [PATCH] 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 --- cogl/cogl-pipeline-fragend-glsl.c | 171 +++++++++++++++++++++--------- cogl/cogl-snippet.h | 38 ++++++- tests/conform/test-snippets.c | 45 ++++++++ 3 files changed, 201 insertions(+), 53 deletions(-) diff --git a/cogl/cogl-pipeline-fragend-glsl.c b/cogl/cogl-pipeline-fragend-glsl.c index b7500ebe1..890e3b15d 100644 --- a/cogl/cogl-pipeline-fragend-glsl.c +++ b/cogl/cogl-pipeline-fragend-glsl.c @@ -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); } diff --git a/cogl/cogl-snippet.h b/cogl/cogl-snippet.h index 81817d76a..0fe61e529 100644 --- a/cogl/cogl-snippet.h +++ b/cogl/cogl-snippet.h @@ -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; * * * + * %COGL_SNIPPET_HOOK_LAYER_FRAGMENT + * 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. + * + * + * 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. + * + * + * 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. + * + * + * The ‘pre’ string in @snippet will be inserted just before the + * fragment processing for this layer. + * + * + * 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. + * + * + * 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. + * + * + * * %COGL_SNIPPET_HOOK_TEXTURE_LOOKUP * 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 diff --git a/tests/conform/test-snippets.c b/tests/conform/test-snippets.c index bb0cf3449..5533f2ebd 100644 --- a/tests/conform/test-snippets.c +++ b/tests/conform/test-snippets.c @@ -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