mirror of
https://github.com/brl/mutter.git
synced 2024-12-23 19:42:05 +00:00
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:
parent
4581ce5f15
commit
b7e15929b6
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user