[clutter] Convert all of the internal shader-based effects to use snippets



commit 3218cd686580d0c0e15d4ef88199e6f90e6e23b1
Author: Neil Roberts <neil linux intel com>
Date:   Mon Nov 28 13:46:30 2011 +0000

    Convert all of the internal shader-based effects to use snippets
    
    This converts the blur, colorize and desaturate effects to use
    snippets instead of CoglPrograms. Cogl can handle the snippets much
    more efficiently than programs so this should be a performance win. It
    also fixes the problem that Cogl would end up recompiling the program
    for every instance of the effects because Clutter was not reusing the
    same program.
    
    Reviewed-by: Emmanuele Bassi <ebassi linux intel com>

 clutter/clutter-blur-effect.c       |  197 +++++++++++++++--------------------
 clutter/clutter-colorize-effect.c   |  178 ++++++++++++++++----------------
 clutter/clutter-desaturate-effect.c |  175 +++++++++++++++----------------
 3 files changed, 260 insertions(+), 290 deletions(-)
---
diff --git a/clutter/clutter-blur-effect.c b/clutter/clutter-blur-effect.c
index 6bf644a..a7a7c09 100644
--- a/clutter/clutter-blur-effect.c
+++ b/clutter/clutter-blur-effect.c
@@ -54,29 +54,26 @@
 /* FIXME - lame shader; we should really have a decoupled
  * horizontal/vertical two pass shader for the gaussian blur
  */
+static const gchar *box_blur_glsl_declarations =
+"uniform vec2 pixel_step;\n";
+/* FIXME: Is this shader right? It is doing 10 samples (ie, sampling
+   the middle texel twice) and then only dividing by 9 */
+#define SAMPLE(offx, offy) \
+  "cogl_texel += texture2D (cogl_sampler, cogl_tex_coord.st + pixel_step * " \
+  "vec2 (" G_STRINGIFY (offx) ", " G_STRINGIFY (offy) ") * 2.0);\n"
 static const gchar *box_blur_glsl_shader =
-"uniform sampler2D tex;\n"
-"uniform float x_step, y_step;\n"
-"\n"
-"vec4 get_rgba_rel (sampler2D source, float dx, float dy)\n"
-"{\n"
-"  return texture2D (tex, cogl_tex_coord_in[0].st + vec2 (dx, dy) * 2.0);\n"
-"}\n"
-"\n"
-"void main ()\n"
-"{\n"
-"  vec4 color = cogl_color_in * texture2D (tex, vec2 (cogl_tex_coord_in[0].xy));\n"
-"  color += get_rgba_rel (tex, -x_step, -y_step);\n"
-"  color += get_rgba_rel (tex,  0.0,    -y_step);\n"
-"  color += get_rgba_rel (tex,  x_step, -y_step);\n"
-"  color += get_rgba_rel (tex, -x_step,  0.0);\n"
-"  color += get_rgba_rel (tex,  0.0,     0.0);\n"
-"  color += get_rgba_rel (tex,  x_step,  0.0);\n"
-"  color += get_rgba_rel (tex, -x_step,  y_step);\n"
-"  color += get_rgba_rel (tex,  0.0,     y_step);\n"
-"  color += get_rgba_rel (tex,  x_step,  y_step);\n"
-"  cogl_color_out = color / 9.0;\n"
-"}";
+"  cogl_texel = texture2D (cogl_sampler, cogl_tex_coord.st);\n"
+  SAMPLE (-1.0, -1.0)
+  SAMPLE ( 0.0, -1.0)
+  SAMPLE (+1.0, -1.0)
+  SAMPLE (-1.0,  0.0)
+  SAMPLE ( 0.0,  0.0)
+  SAMPLE (+1.0,  0.0)
+  SAMPLE (-1.0, +1.0)
+  SAMPLE ( 0.0, +1.0)
+  SAMPLE (+1.0, +1.0)
+"  cogl_texel /= 9.0;\n";
+#undef SAMPLE
 
 struct _ClutterBlurEffect
 {
@@ -85,19 +82,19 @@ struct _ClutterBlurEffect
   /* a back pointer to our actor, so that we can query it */
   ClutterActor *actor;
 
-  CoglHandle shader;
-  CoglHandle program;
+  gint pixel_step_uniform;
 
-  gint tex_uniform;
-  gint x_step_uniform;
-  gint y_step_uniform;
+  gint tex_width;
+  gint tex_height;
 
-  guint is_compiled : 1;
+  CoglPipeline *pipeline;
 };
 
 struct _ClutterBlurEffectClass
 {
   ClutterOffscreenEffectClass parent_class;
+
+  CoglPipeline *base_pipeline;
 };
 
 G_DEFINE_TYPE (ClutterBlurEffect,
@@ -109,8 +106,6 @@ clutter_blur_effect_pre_paint (ClutterEffect *effect)
 {
   ClutterBlurEffect *self = CLUTTER_BLUR_EFFECT (effect);
   ClutterEffectClass *parent_class;
-  ClutterActorBox allocation;
-  gfloat width, height;
 
   if (!clutter_actor_meta_get_enabled (CLUTTER_ACTOR_META (effect)))
     return FALSE;
@@ -131,103 +126,57 @@ clutter_blur_effect_pre_paint (ClutterEffect *effect)
       return FALSE;
     }
 
-  clutter_actor_get_allocation_box (self->actor, &allocation);
-  clutter_actor_box_get_size (&allocation, &width, &height);
-
-  if (self->shader == COGL_INVALID_HANDLE)
+  parent_class = CLUTTER_EFFECT_CLASS (clutter_blur_effect_parent_class);
+  if (parent_class->pre_paint (effect))
     {
-      self->shader = cogl_create_shader (COGL_SHADER_TYPE_FRAGMENT);
-      cogl_shader_source (self->shader, box_blur_glsl_shader);
-
-      self->is_compiled = FALSE;
-      self->tex_uniform = -1;
-      self->x_step_uniform = -1;
-      self->y_step_uniform = -1;
-    }
-
-  if (self->program == COGL_INVALID_HANDLE)
-    self->program = cogl_create_program ();
+      ClutterOffscreenEffect *offscreen_effect =
+        CLUTTER_OFFSCREEN_EFFECT (effect);
+      CoglHandle texture;
 
-  if (!self->is_compiled)
-    {
-      g_assert (self->shader != COGL_INVALID_HANDLE);
-      g_assert (self->program != COGL_INVALID_HANDLE);
+      texture = clutter_offscreen_effect_get_texture (offscreen_effect);
+      self->tex_width = cogl_texture_get_width (texture);
+      self->tex_height = cogl_texture_get_height (texture);
 
-      cogl_shader_compile (self->shader);
-      if (!cogl_shader_is_compiled (self->shader))
+      if (self->pixel_step_uniform > -1)
         {
-          gchar *log_buf = cogl_shader_get_info_log (self->shader);
-
-          g_warning (G_STRLOC ": Unable to compile the box blur shader: %s",
-                     log_buf);
-          g_free (log_buf);
+          gfloat pixel_step[2];
 
-          cogl_handle_unref (self->shader);
-          cogl_handle_unref (self->program);
+          pixel_step[0] = 1.0f / self->tex_width;
+          pixel_step[1] = 1.0f / self->tex_height;
 
-          self->shader = COGL_INVALID_HANDLE;
-          self->program = COGL_INVALID_HANDLE;
+          cogl_pipeline_set_uniform_float (self->pipeline,
+                                           self->pixel_step_uniform,
+                                           2, /* n_components */
+                                           1, /* count */
+                                           pixel_step);
         }
-      else
-        {
-          cogl_program_attach_shader (self->program, self->shader);
-          cogl_program_link (self->program);
-
-          cogl_handle_unref (self->shader);
 
-          self->is_compiled = TRUE;
+      cogl_pipeline_set_layer_texture (self->pipeline, 0, texture);
 
-          self->tex_uniform =
-            cogl_program_get_uniform_location (self->program, "tex");
-          self->x_step_uniform =
-            cogl_program_get_uniform_location (self->program, "x_step");
-          self->y_step_uniform =
-            cogl_program_get_uniform_location (self->program, "y_step");
-        }
+      return TRUE;
     }
-
-  parent_class = CLUTTER_EFFECT_CLASS (clutter_blur_effect_parent_class);
-  return parent_class->pre_paint (effect);
+  else
+    return FALSE;
 }
 
 static void
 clutter_blur_effect_paint_target (ClutterOffscreenEffect *effect)
 {
   ClutterBlurEffect *self = CLUTTER_BLUR_EFFECT (effect);
-  ClutterOffscreenEffectClass *parent;
-  CoglHandle material;
-  CoglHandle texture;
+  guint8 paint_opacity;
 
-  if (self->program == COGL_INVALID_HANDLE)
-    goto out;
+  paint_opacity = clutter_actor_get_paint_opacity (self->actor);
 
-  texture = clutter_offscreen_effect_get_texture (effect);
+  cogl_pipeline_set_color4ub (self->pipeline,
+                              paint_opacity,
+                              paint_opacity,
+                              paint_opacity,
+                              paint_opacity);
+  cogl_push_source (self->pipeline);
 
-  if (self->tex_uniform > -1)
-    cogl_program_set_uniform_1i (self->program, self->tex_uniform, 0);
+  cogl_rectangle (0, 0, self->tex_width, self->tex_height);
 
-  if (self->x_step_uniform > -1)
-    {
-      int width = cogl_texture_get_width (texture);
-      cogl_program_set_uniform_1f (self->program,
-                                   self->x_step_uniform,
-                                   1.0f / width);
-    }
-
-  if (self->y_step_uniform > -1)
-    {
-      int height = cogl_texture_get_height (texture);
-      cogl_program_set_uniform_1f (self->program,
-                                   self->y_step_uniform,
-                                   1.0f / height);
-    }
-
-  material = clutter_offscreen_effect_get_target (effect);
-  cogl_material_set_user_program (material, self->program);
-
-out:
-  parent = CLUTTER_OFFSCREEN_EFFECT_CLASS (clutter_blur_effect_parent_class);
-  parent->paint_target (effect);
+  cogl_pop_source ();
 }
 
 static gboolean
@@ -257,12 +206,10 @@ clutter_blur_effect_dispose (GObject *gobject)
 {
   ClutterBlurEffect *self = CLUTTER_BLUR_EFFECT (gobject);
 
-  if (self->program != COGL_INVALID_HANDLE)
+  if (self->pipeline != NULL)
     {
-      cogl_handle_unref (self->program);
-
-      self->program = COGL_INVALID_HANDLE;
-      self->shader = COGL_INVALID_HANDLE;
+      cogl_object_unref (self->pipeline);
+      self->pipeline = NULL;
     }
 
   G_OBJECT_CLASS (clutter_blur_effect_parent_class)->dispose (gobject);
@@ -282,11 +229,37 @@ clutter_blur_effect_class_init (ClutterBlurEffectClass *klass)
 
   offscreen_class = CLUTTER_OFFSCREEN_EFFECT_CLASS (klass);
   offscreen_class->paint_target = clutter_blur_effect_paint_target;
+
+  klass->base_pipeline = cogl_pipeline_new ();
+
+  if (clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL))
+    {
+      CoglSnippet *snippet;
+
+      snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_TEXTURE_LOOKUP,
+                                  box_blur_glsl_declarations,
+                                  NULL);
+      cogl_snippet_set_replace (snippet,
+                                box_blur_glsl_shader);
+      cogl_pipeline_add_layer_snippet (klass->base_pipeline, 0, snippet);
+      cogl_object_unref (snippet);
+    }
+
+  cogl_pipeline_set_layer_null_texture (klass->base_pipeline,
+                                        0, /* layer number */
+                                        COGL_TEXTURE_TYPE_2D);
 }
 
 static void
 clutter_blur_effect_init (ClutterBlurEffect *self)
 {
+  CoglPipeline *base_pipeline =
+    CLUTTER_BLUR_EFFECT_GET_CLASS (self)->base_pipeline;
+
+  self->pipeline = cogl_pipeline_copy (base_pipeline);
+
+  self->pixel_step_uniform =
+    cogl_pipeline_get_uniform_location (self->pipeline, "pixel_step");
 }
 
 /**
diff --git a/clutter/clutter-colorize-effect.c b/clutter/clutter-colorize-effect.c
index f1c8d32..2ccd068 100644
--- a/clutter/clutter-colorize-effect.c
+++ b/clutter/clutter-colorize-effect.c
@@ -57,18 +57,19 @@ struct _ClutterColorizeEffect
   /* the tint of the colorization */
   ClutterColor tint;
 
-  CoglHandle shader;
-  CoglHandle program;
-
-  gint tex_uniform;
   gint tint_uniform;
 
-  guint is_compiled : 1;
+  gint tex_width;
+  gint tex_height;
+
+  CoglPipeline *pipeline;
 };
 
 struct _ClutterColorizeEffectClass
 {
   ClutterOffscreenEffectClass parent_class;
+
+  CoglPipeline *base_pipeline;
 };
 
 /* the magic gray vec3 has been taken from the NTSC conversion weights
@@ -78,16 +79,12 @@ struct _ClutterColorizeEffectClass
  *   -- Richard S. Wright Jr, Benjamin Lipchak, Nicholas Haemel
  *   Addison-Wesley
  */
-static const gchar *colorize_glsl_shader =
-"uniform sampler2D tex;\n"
-"uniform vec3 tint;\n"
-"\n"
-"void main ()\n"
-"{\n"
-"  vec4 color = cogl_color_in * texture2D (tex, vec2 (cogl_tex_coord_in[0].xy));\n"
-"  float gray = dot (color.rgb, vec3 (0.299, 0.587, 0.114));\n"
-"  cogl_color_out = vec4 (gray * tint, color.a);\n"
-"}\n";
+static const gchar *colorize_glsl_declarations =
+"uniform vec3 tint;\n";
+
+static const gchar *colorize_glsl_source =
+"float gray = dot (cogl_color_out.rgb, vec3 (0.299, 0.587, 0.114));\n"
+"cogl_color_out.rgb = gray * tint;\n";
 
 /* a lame sepia */
 static const ClutterColor default_tint = { 255, 204, 153, 255 };
@@ -128,91 +125,45 @@ clutter_colorize_effect_pre_paint (ClutterEffect *effect)
       return FALSE;
     }
 
-  if (self->shader == COGL_INVALID_HANDLE)
+  parent_class = CLUTTER_EFFECT_CLASS (clutter_colorize_effect_parent_class);
+  if (parent_class->pre_paint (effect))
     {
-      self->shader = cogl_create_shader (COGL_SHADER_TYPE_FRAGMENT);
-      cogl_shader_source (self->shader, colorize_glsl_shader);
+      ClutterOffscreenEffect *offscreen_effect =
+        CLUTTER_OFFSCREEN_EFFECT (effect);
+      CoglHandle texture;
 
-      self->is_compiled = FALSE;
-      self->tex_uniform = -1;
-      self->tint_uniform = -1;
-    }
+      texture = clutter_offscreen_effect_get_texture (offscreen_effect);
+      self->tex_width = cogl_texture_get_width (texture);
+      self->tex_height = cogl_texture_get_height (texture);
 
-  if (self->program == COGL_INVALID_HANDLE)
-    self->program = cogl_create_program ();
+      cogl_pipeline_set_layer_texture (self->pipeline, 0, texture);
 
-  if (!self->is_compiled)
-    {
-      g_assert (self->shader != COGL_INVALID_HANDLE);
-      g_assert (self->program != COGL_INVALID_HANDLE);
-
-      cogl_shader_compile (self->shader);
-      if (!cogl_shader_is_compiled (self->shader))
-        {
-          gchar *log_buf = cogl_shader_get_info_log (self->shader);
-
-          g_warning (G_STRLOC ": Unable to compile the colorize shader: %s",
-                     log_buf);
-          g_free (log_buf);
-
-          cogl_handle_unref (self->shader);
-          cogl_handle_unref (self->program);
-
-          self->shader = COGL_INVALID_HANDLE;
-          self->program = COGL_INVALID_HANDLE;
-        }
-      else
-        {
-          cogl_program_attach_shader (self->program, self->shader);
-          cogl_program_link (self->program);
-
-          cogl_handle_unref (self->shader);
-
-          self->is_compiled = TRUE;
-
-          self->tex_uniform =
-            cogl_program_get_uniform_location (self->program, "tex");
-          self->tint_uniform =
-            cogl_program_get_uniform_location (self->program, "tint");
-        }
+      return TRUE;
     }
-
-  parent_class = CLUTTER_EFFECT_CLASS (clutter_colorize_effect_parent_class);
-  return parent_class->pre_paint (effect);
+  else
+    return FALSE;
 }
 
 static void
 clutter_colorize_effect_paint_target (ClutterOffscreenEffect *effect)
 {
   ClutterColorizeEffect *self = CLUTTER_COLORIZE_EFFECT (effect);
-  ClutterOffscreenEffectClass *parent;
-  CoglHandle material;
+  ClutterActor *actor;
+  guint8 paint_opacity;
 
-  if (self->program == COGL_INVALID_HANDLE)
-    goto out;
+  actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (effect));
+  paint_opacity = clutter_actor_get_paint_opacity (actor);
 
-  if (self->tex_uniform > -1)
-    cogl_program_set_uniform_1i (self->program, self->tex_uniform, 0);
+  cogl_pipeline_set_color4ub (self->pipeline,
+                              paint_opacity,
+                              paint_opacity,
+                              paint_opacity,
+                              paint_opacity);
+  cogl_push_source (self->pipeline);
 
-  if (self->tint_uniform > -1)
-    {
-      float tint[3] = {
-        self->tint.red / 255.0,
-        self->tint.green / 255.0,
-        self->tint.blue / 255.0
-      };
+  cogl_rectangle (0, 0, self->tex_width, self->tex_height);
 
-      cogl_program_set_uniform_float (self->program, self->tint_uniform,
-                                      3, 1,
-                                      tint);
-    }
-
-  material = clutter_offscreen_effect_get_target (effect);
-  cogl_material_set_user_program (material, self->program);
-
-out:
-  parent = CLUTTER_OFFSCREEN_EFFECT_CLASS (clutter_colorize_effect_parent_class);
-  parent->paint_target (effect);
+  cogl_pop_source ();
 }
 
 static void
@@ -220,12 +171,10 @@ clutter_colorize_effect_dispose (GObject *gobject)
 {
   ClutterColorizeEffect *self = CLUTTER_COLORIZE_EFFECT (gobject);
 
-  if (self->program != COGL_INVALID_HANDLE)
+  if (self->pipeline != NULL)
     {
-      cogl_handle_unref (self->program);
-
-      self->program = COGL_INVALID_HANDLE;
-      self->shader = COGL_INVALID_HANDLE;
+      cogl_object_unref (self->pipeline);
+      self->pipeline = NULL;
     }
 
   G_OBJECT_CLASS (clutter_colorize_effect_parent_class)->dispose (gobject);
@@ -305,12 +254,59 @@ clutter_colorize_effect_class_init (ClutterColorizeEffectClass *klass)
   g_object_class_install_properties (gobject_class,
                                      PROP_LAST,
                                      obj_props);
+
+
+  klass->base_pipeline = cogl_pipeline_new ();
+
+  if (clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL))
+    {
+      CoglSnippet *snippet;
+
+      snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT,
+                                  colorize_glsl_declarations,
+                                  colorize_glsl_source);
+      cogl_pipeline_add_snippet (klass->base_pipeline, snippet);
+      cogl_object_unref (snippet);
+    }
+
+  cogl_pipeline_set_layer_null_texture (klass->base_pipeline,
+                                        0, /* layer number */
+                                        COGL_TEXTURE_TYPE_2D);
+}
+
+static void
+update_tint_uniform (ClutterColorizeEffect *self)
+{
+  if (self->tint_uniform > -1)
+    {
+      float tint[3] = {
+        self->tint.red / 255.0,
+        self->tint.green / 255.0,
+        self->tint.blue / 255.0
+      };
+
+      cogl_pipeline_set_uniform_float (self->pipeline,
+                                       self->tint_uniform,
+                                       3, /* n_components */
+                                       1, /* count */
+                                       tint);
+    }
 }
 
 static void
 clutter_colorize_effect_init (ClutterColorizeEffect *self)
 {
+  CoglPipeline *base_pipeline =
+    CLUTTER_COLORIZE_EFFECT_GET_CLASS (self)->base_pipeline;
+
+  self->pipeline = cogl_pipeline_copy (base_pipeline);
+
+  self->tint_uniform =
+    cogl_pipeline_get_uniform_location (self->pipeline, "tint");
+
   self->tint = default_tint;
+
+  update_tint_uniform (self);
 }
 
 /**
@@ -349,6 +345,8 @@ clutter_colorize_effect_set_tint (ClutterColorizeEffect *effect,
 
   effect->tint = *tint;
 
+  update_tint_uniform (effect);
+
   clutter_effect_queue_repaint (CLUTTER_EFFECT (effect));
 
   g_object_notify_by_pspec (G_OBJECT (effect), obj_props[PROP_TINT]);
diff --git a/clutter/clutter-desaturate-effect.c b/clutter/clutter-desaturate-effect.c
index 7387ff5..9bdb70a 100644
--- a/clutter/clutter-desaturate-effect.c
+++ b/clutter/clutter-desaturate-effect.c
@@ -61,18 +61,19 @@ struct _ClutterDesaturateEffect
   /* the desaturation factor, also known as "strength" */
   gdouble factor;
 
-  CoglHandle shader;
-  CoglHandle program;
-
-  gint tex_uniform;
   gint factor_uniform;
 
-  guint is_compiled : 1;
+  gint tex_width;
+  gint tex_height;
+
+  CoglPipeline *pipeline;
 };
 
 struct _ClutterDesaturateEffectClass
 {
   ClutterOffscreenEffectClass parent_class;
+
+  CoglPipeline *base_pipeline;
 };
 
 /* the magic gray vec3 has been taken from the NTSC conversion weights
@@ -82,23 +83,18 @@ struct _ClutterDesaturateEffectClass
  *   -- Richard S. Wright Jr, Benjamin Lipchak, Nicholas Haemel
  *   Addison-Wesley
  */
-static const gchar *desaturate_glsl_shader =
-"uniform sampler2D tex;\n"
-"uniform float factor;\n"
-"\n"
-"vec3 desaturate (const vec3 color, const float desaturation)\n"
-"{\n"
-"  const vec3 gray_conv = vec3 (0.299, 0.587, 0.114);\n"
-"  vec3 gray = vec3 (dot (gray_conv, color));\n"
-"  return vec3 (mix (color.rgb, gray, desaturation));\n"
-"}\n"
-"\n"
-"void main ()\n"
-"{\n"
-"  vec4 color = cogl_color_in * texture2D (tex, vec2 (cogl_tex_coord_in[0].xy));\n"
-"  color.rgb = desaturate (color.rgb, factor);\n"
-"  cogl_color_out = color;\n"
-"}\n";
+static const gchar *desaturate_glsl_declarations =
+  "uniform float factor;\n"
+  "\n"
+  "vec3 desaturate (const vec3 color, const float desaturation)\n"
+  "{\n"
+  "  const vec3 gray_conv = vec3 (0.299, 0.587, 0.114);\n"
+  "  vec3 gray = vec3 (dot (gray_conv, color));\n"
+  "  return vec3 (mix (color.rgb, gray, desaturation));\n"
+  "}\n";
+
+static const gchar *desaturate_glsl_source =
+  "  cogl_color_out.rgb = desaturate (cogl_color_out.rgb, factor);\n";
 
 enum
 {
@@ -136,83 +132,51 @@ clutter_desaturate_effect_pre_paint (ClutterEffect *effect)
       return FALSE;
     }
 
-  if (self->shader == COGL_INVALID_HANDLE)
+  parent_class = CLUTTER_EFFECT_CLASS (clutter_desaturate_effect_parent_class);
+  if (parent_class->pre_paint (effect))
     {
-      self->shader = cogl_create_shader (COGL_SHADER_TYPE_FRAGMENT);
-      cogl_shader_source (self->shader, desaturate_glsl_shader);
+      ClutterOffscreenEffect *offscreen_effect =
+        CLUTTER_OFFSCREEN_EFFECT (effect);
+      CoglHandle texture;
 
-      self->is_compiled = FALSE;
-      self->tex_uniform = -1;
-      self->factor_uniform = -1;
-    }
+      texture = clutter_offscreen_effect_get_texture (offscreen_effect);
+      self->tex_width = cogl_texture_get_width (texture);
+      self->tex_height = cogl_texture_get_height (texture);
 
-  if (self->program == COGL_INVALID_HANDLE)
-    self->program = cogl_create_program ();
+      cogl_pipeline_set_layer_texture (self->pipeline, 0, texture);
 
-  if (!self->is_compiled)
-    {
-      g_assert (self->shader != COGL_INVALID_HANDLE);
-      g_assert (self->program != COGL_INVALID_HANDLE);
-
-      cogl_shader_compile (self->shader);
-      if (!cogl_shader_is_compiled (self->shader))
-        {
-          gchar *log_buf = cogl_shader_get_info_log (self->shader);
-
-          g_warning (G_STRLOC ": Unable to compile the desaturate shader: %s",
-                     log_buf);
-          g_free (log_buf);
-
-          cogl_handle_unref (self->shader);
-          cogl_handle_unref (self->program);
-
-          self->shader = COGL_INVALID_HANDLE;
-          self->program = COGL_INVALID_HANDLE;
-        }
-      else
-        {
-          cogl_program_attach_shader (self->program, self->shader);
-          cogl_program_link (self->program);
-
-          cogl_handle_unref (self->shader);
-
-          self->is_compiled = TRUE;
-
-          self->tex_uniform =
-            cogl_program_get_uniform_location (self->program, "tex");
-          self->factor_uniform =
-            cogl_program_get_uniform_location (self->program, "factor");
-        }
+      return TRUE;
     }
-
-  parent_class = CLUTTER_EFFECT_CLASS (clutter_desaturate_effect_parent_class);
-  return parent_class->pre_paint (effect);
+  else
+    return FALSE;
 }
 
 static void
 clutter_desaturate_effect_paint_target (ClutterOffscreenEffect *effect)
 {
   ClutterDesaturateEffect *self = CLUTTER_DESATURATE_EFFECT (effect);
-  ClutterOffscreenEffectClass *parent;
-  CoglHandle material;
+  ClutterActor *actor;
+  CoglHandle texture;
+  guint8 paint_opacity;
 
-  if (self->program == COGL_INVALID_HANDLE)
-    goto out;
+  texture = clutter_offscreen_effect_get_texture (effect);
+  cogl_pipeline_set_layer_texture (self->pipeline, 0, texture);
 
-  if (self->tex_uniform > -1)
-    cogl_program_set_uniform_1i (self->program, self->tex_uniform, 0);
+  actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (effect));
+  paint_opacity = clutter_actor_get_paint_opacity (actor);
 
-  if (self->factor_uniform > -1)
-    cogl_program_set_uniform_1f (self->program,
-                                 self->factor_uniform,
-                                 self->factor);
+  cogl_pipeline_set_color4ub (self->pipeline,
+                              paint_opacity,
+                              paint_opacity,
+                              paint_opacity,
+                              paint_opacity);
+  cogl_push_source (self->pipeline);
 
-  material = clutter_offscreen_effect_get_target (effect);
-  cogl_material_set_user_program (material, self->program);
+  cogl_rectangle (0, 0,
+                  cogl_texture_get_width (texture),
+                  cogl_texture_get_height (texture));
 
-out:
-  parent = CLUTTER_OFFSCREEN_EFFECT_CLASS (clutter_desaturate_effect_parent_class);
-  parent->paint_target (effect);
+  cogl_pop_source ();
 }
 
 static void
@@ -220,12 +184,10 @@ clutter_desaturate_effect_dispose (GObject *gobject)
 {
   ClutterDesaturateEffect *self = CLUTTER_DESATURATE_EFFECT (gobject);
 
-  if (self->program != COGL_INVALID_HANDLE)
+  if (self->pipeline != NULL)
     {
-      cogl_handle_unref (self->program);
-
-      self->program = COGL_INVALID_HANDLE;
-      self->shader = COGL_INVALID_HANDLE;
+      cogl_object_unref (self->pipeline);
+      self->pipeline = NULL;
     }
 
   G_OBJECT_CLASS (clutter_desaturate_effect_parent_class)->dispose (gobject);
@@ -273,6 +235,15 @@ clutter_desaturate_effect_get_property (GObject    *gobject,
 }
 
 static void
+update_factor_uniform (ClutterDesaturateEffect *self)
+{
+  if (self->factor_uniform > -1)
+    cogl_pipeline_set_uniform_1f (self->pipeline,
+                                  self->factor_uniform,
+                                  self->factor);
+}
+
+static void
 clutter_desaturate_effect_class_init (ClutterDesaturateEffectClass *klass)
 {
   ClutterEffectClass *effect_class = CLUTTER_EFFECT_CLASS (klass);
@@ -307,12 +278,39 @@ clutter_desaturate_effect_class_init (ClutterDesaturateEffectClass *klass)
   g_object_class_install_properties (gobject_class,
                                      PROP_LAST,
                                      obj_props);
+
+  klass->base_pipeline = cogl_pipeline_new ();
+
+  if (clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL))
+    {
+      CoglSnippet *snippet;
+
+      snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT,
+                                  desaturate_glsl_declarations,
+                                  desaturate_glsl_source);
+      cogl_pipeline_add_snippet (klass->base_pipeline, snippet);
+      cogl_object_unref (snippet);
+    }
+
+  cogl_pipeline_set_layer_null_texture (klass->base_pipeline,
+                                        0, /* layer number */
+                                        COGL_TEXTURE_TYPE_2D);
 }
 
 static void
 clutter_desaturate_effect_init (ClutterDesaturateEffect *self)
 {
+  CoglPipeline *base_pipeline =
+    CLUTTER_DESATURATE_EFFECT_GET_CLASS (self)->base_pipeline;
+
+  self->pipeline = cogl_pipeline_copy (base_pipeline);
+
+  self->factor_uniform =
+    cogl_pipeline_get_uniform_location (self->pipeline, "factor");
+
   self->factor = 1.0;
+
+  update_factor_uniform (self);
 }
 
 /**
@@ -356,6 +354,7 @@ clutter_desaturate_effect_set_factor (ClutterDesaturateEffect *effect,
   if (fabsf (effect->factor - factor) >= 0.00001)
     {
       effect->factor = factor;
+      update_factor_uniform (effect);
 
       clutter_effect_queue_repaint (CLUTTER_EFFECT (effect));
 



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