[cogl/wip/neil/snippets: 20/27] snippet: Add a hook for the layer fragment processing



commit 5e76d555ac3754b4570dc78b11a1d6f78085e203
Author: Neil Roberts <neil linux intel com>
Date:   Fri Nov 25 23:16:31 2011 +0000

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

 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 b7500eb..890e3b1 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:
@@ -668,6 +672,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,
                        int previous_layer_index,
@@ -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;
-
-  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]);
+  GString *shader_source = shader_state->header;
 
-  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 81817d7..0fe61e5 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;
  *   </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
diff --git a/tests/conform/test-snippets.c b/tests/conform/test-snippets.c
index bb0cf34..5533f2e 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



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]