Convert all of the internal shader-based effects to use snippets
authorNeil Roberts <neil@linux.intel.com>
Mon, 28 Nov 2011 13:46:30 +0000 (13:46 +0000)
committerNeil Roberts <neil@linux.intel.com>
Tue, 14 Feb 2012 12:09:32 +0000 (12:09 +0000)
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
clutter/clutter-colorize-effect.c
clutter/clutter-desaturate-effect.c

index 6bf644a..a7a7c09 100644 (file)
 /* 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");
 }
 
 /**
index f1c8d32..2ccd068 100644 (file)
@@ -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]);
index 7387ff5..9bdb70a 100644 (file)
@@ -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));