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