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>
This commit is contained in:
Neil Roberts 2011-11-28 13:46:30 +00:00
parent b4887c3699
commit 3218cd6865
3 changed files with 277 additions and 307 deletions

View File

@ -54,29 +54,26 @@
/* FIXME - lame shader; we should really have a decoupled /* FIXME - lame shader; we should really have a decoupled
* horizontal/vertical two pass shader for the gaussian blur * 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 = static const gchar *box_blur_glsl_shader =
"uniform sampler2D tex;\n" " cogl_texel = texture2D (cogl_sampler, cogl_tex_coord.st);\n"
"uniform float x_step, y_step;\n" SAMPLE (-1.0, -1.0)
"\n" SAMPLE ( 0.0, -1.0)
"vec4 get_rgba_rel (sampler2D source, float dx, float dy)\n" SAMPLE (+1.0, -1.0)
"{\n" SAMPLE (-1.0, 0.0)
" return texture2D (tex, cogl_tex_coord_in[0].st + vec2 (dx, dy) * 2.0);\n" SAMPLE ( 0.0, 0.0)
"}\n" SAMPLE (+1.0, 0.0)
"\n" SAMPLE (-1.0, +1.0)
"void main ()\n" SAMPLE ( 0.0, +1.0)
"{\n" SAMPLE (+1.0, +1.0)
" vec4 color = cogl_color_in * texture2D (tex, vec2 (cogl_tex_coord_in[0].xy));\n" " cogl_texel /= 9.0;\n";
" color += get_rgba_rel (tex, -x_step, -y_step);\n" #undef SAMPLE
" 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"
"}";
struct _ClutterBlurEffect struct _ClutterBlurEffect
{ {
@ -85,19 +82,19 @@ struct _ClutterBlurEffect
/* a back pointer to our actor, so that we can query it */ /* a back pointer to our actor, so that we can query it */
ClutterActor *actor; ClutterActor *actor;
CoglHandle shader; gint pixel_step_uniform;
CoglHandle program;
gint tex_uniform; gint tex_width;
gint x_step_uniform; gint tex_height;
gint y_step_uniform;
guint is_compiled : 1; CoglPipeline *pipeline;
}; };
struct _ClutterBlurEffectClass struct _ClutterBlurEffectClass
{ {
ClutterOffscreenEffectClass parent_class; ClutterOffscreenEffectClass parent_class;
CoglPipeline *base_pipeline;
}; };
G_DEFINE_TYPE (ClutterBlurEffect, G_DEFINE_TYPE (ClutterBlurEffect,
@ -109,8 +106,6 @@ clutter_blur_effect_pre_paint (ClutterEffect *effect)
{ {
ClutterBlurEffect *self = CLUTTER_BLUR_EFFECT (effect); ClutterBlurEffect *self = CLUTTER_BLUR_EFFECT (effect);
ClutterEffectClass *parent_class; ClutterEffectClass *parent_class;
ClutterActorBox allocation;
gfloat width, height;
if (!clutter_actor_meta_get_enabled (CLUTTER_ACTOR_META (effect))) if (!clutter_actor_meta_get_enabled (CLUTTER_ACTOR_META (effect)))
return FALSE; return FALSE;
@ -131,103 +126,57 @@ clutter_blur_effect_pre_paint (ClutterEffect *effect)
return FALSE; return FALSE;
} }
clutter_actor_get_allocation_box (self->actor, &allocation); parent_class = CLUTTER_EFFECT_CLASS (clutter_blur_effect_parent_class);
clutter_actor_box_get_size (&allocation, &width, &height); if (parent_class->pre_paint (effect))
if (self->shader == COGL_INVALID_HANDLE)
{ {
self->shader = cogl_create_shader (COGL_SHADER_TYPE_FRAGMENT); ClutterOffscreenEffect *offscreen_effect =
cogl_shader_source (self->shader, box_blur_glsl_shader); CLUTTER_OFFSCREEN_EFFECT (effect);
CoglHandle texture;
self->is_compiled = FALSE; texture = clutter_offscreen_effect_get_texture (offscreen_effect);
self->tex_uniform = -1; self->tex_width = cogl_texture_get_width (texture);
self->x_step_uniform = -1; self->tex_height = cogl_texture_get_height (texture);
self->y_step_uniform = -1;
if (self->pixel_step_uniform > -1)
{
gfloat pixel_step[2];
pixel_step[0] = 1.0f / self->tex_width;
pixel_step[1] = 1.0f / self->tex_height;
cogl_pipeline_set_uniform_float (self->pipeline,
self->pixel_step_uniform,
2, /* n_components */
1, /* count */
pixel_step);
} }
if (self->program == COGL_INVALID_HANDLE) cogl_pipeline_set_layer_texture (self->pipeline, 0, texture);
self->program = cogl_create_program ();
if (!self->is_compiled) return TRUE;
{
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 box blur 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 else
{ return FALSE;
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->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");
}
}
parent_class = CLUTTER_EFFECT_CLASS (clutter_blur_effect_parent_class);
return parent_class->pre_paint (effect);
} }
static void static void
clutter_blur_effect_paint_target (ClutterOffscreenEffect *effect) clutter_blur_effect_paint_target (ClutterOffscreenEffect *effect)
{ {
ClutterBlurEffect *self = CLUTTER_BLUR_EFFECT (effect); ClutterBlurEffect *self = CLUTTER_BLUR_EFFECT (effect);
ClutterOffscreenEffectClass *parent; guint8 paint_opacity;
CoglHandle material;
CoglHandle texture;
if (self->program == COGL_INVALID_HANDLE) paint_opacity = clutter_actor_get_paint_opacity (self->actor);
goto out;
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_rectangle (0, 0, self->tex_width, self->tex_height);
cogl_program_set_uniform_1i (self->program, self->tex_uniform, 0);
if (self->x_step_uniform > -1) cogl_pop_source ();
{
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);
} }
static gboolean static gboolean
@ -257,12 +206,10 @@ clutter_blur_effect_dispose (GObject *gobject)
{ {
ClutterBlurEffect *self = CLUTTER_BLUR_EFFECT (gobject); ClutterBlurEffect *self = CLUTTER_BLUR_EFFECT (gobject);
if (self->program != COGL_INVALID_HANDLE) if (self->pipeline != NULL)
{ {
cogl_handle_unref (self->program); cogl_object_unref (self->pipeline);
self->pipeline = NULL;
self->program = COGL_INVALID_HANDLE;
self->shader = COGL_INVALID_HANDLE;
} }
G_OBJECT_CLASS (clutter_blur_effect_parent_class)->dispose (gobject); 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 = CLUTTER_OFFSCREEN_EFFECT_CLASS (klass);
offscreen_class->paint_target = clutter_blur_effect_paint_target; 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 static void
clutter_blur_effect_init (ClutterBlurEffect *self) 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");
} }
/** /**

View File

@ -57,18 +57,19 @@ struct _ClutterColorizeEffect
/* the tint of the colorization */ /* the tint of the colorization */
ClutterColor tint; ClutterColor tint;
CoglHandle shader;
CoglHandle program;
gint tex_uniform;
gint tint_uniform; gint tint_uniform;
guint is_compiled : 1; gint tex_width;
gint tex_height;
CoglPipeline *pipeline;
}; };
struct _ClutterColorizeEffectClass struct _ClutterColorizeEffectClass
{ {
ClutterOffscreenEffectClass parent_class; ClutterOffscreenEffectClass parent_class;
CoglPipeline *base_pipeline;
}; };
/* the magic gray vec3 has been taken from the NTSC conversion weights /* 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 * -- Richard S. Wright Jr, Benjamin Lipchak, Nicholas Haemel
* Addison-Wesley * Addison-Wesley
*/ */
static const gchar *colorize_glsl_shader = static const gchar *colorize_glsl_declarations =
"uniform sampler2D tex;\n" "uniform vec3 tint;\n";
"uniform vec3 tint;\n"
"\n" static const gchar *colorize_glsl_source =
"void main ()\n" "float gray = dot (cogl_color_out.rgb, vec3 (0.299, 0.587, 0.114));\n"
"{\n" "cogl_color_out.rgb = gray * tint;\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";
/* a lame sepia */ /* a lame sepia */
static const ClutterColor default_tint = { 255, 204, 153, 255 }; static const ClutterColor default_tint = { 255, 204, 153, 255 };
@ -128,91 +125,45 @@ clutter_colorize_effect_pre_paint (ClutterEffect *effect)
return FALSE; 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); ClutterOffscreenEffect *offscreen_effect =
cogl_shader_source (self->shader, colorize_glsl_shader); CLUTTER_OFFSCREEN_EFFECT (effect);
CoglHandle texture;
self->is_compiled = FALSE; texture = clutter_offscreen_effect_get_texture (offscreen_effect);
self->tex_uniform = -1; self->tex_width = cogl_texture_get_width (texture);
self->tint_uniform = -1; self->tex_height = cogl_texture_get_height (texture);
}
if (self->program == COGL_INVALID_HANDLE) cogl_pipeline_set_layer_texture (self->pipeline, 0, texture);
self->program = cogl_create_program ();
if (!self->is_compiled) return TRUE;
{
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 else
{ return FALSE;
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");
}
}
parent_class = CLUTTER_EFFECT_CLASS (clutter_colorize_effect_parent_class);
return parent_class->pre_paint (effect);
} }
static void static void
clutter_colorize_effect_paint_target (ClutterOffscreenEffect *effect) clutter_colorize_effect_paint_target (ClutterOffscreenEffect *effect)
{ {
ClutterColorizeEffect *self = CLUTTER_COLORIZE_EFFECT (effect); ClutterColorizeEffect *self = CLUTTER_COLORIZE_EFFECT (effect);
ClutterOffscreenEffectClass *parent; ClutterActor *actor;
CoglHandle material; guint8 paint_opacity;
if (self->program == COGL_INVALID_HANDLE) actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (effect));
goto out; paint_opacity = clutter_actor_get_paint_opacity (actor);
if (self->tex_uniform > -1) cogl_pipeline_set_color4ub (self->pipeline,
cogl_program_set_uniform_1i (self->program, self->tex_uniform, 0); paint_opacity,
paint_opacity,
paint_opacity,
paint_opacity);
cogl_push_source (self->pipeline);
if (self->tint_uniform > -1) cogl_rectangle (0, 0, self->tex_width, self->tex_height);
{
float tint[3] = {
self->tint.red / 255.0,
self->tint.green / 255.0,
self->tint.blue / 255.0
};
cogl_program_set_uniform_float (self->program, self->tint_uniform, cogl_pop_source ();
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);
} }
static void static void
@ -220,12 +171,10 @@ clutter_colorize_effect_dispose (GObject *gobject)
{ {
ClutterColorizeEffect *self = CLUTTER_COLORIZE_EFFECT (gobject); ClutterColorizeEffect *self = CLUTTER_COLORIZE_EFFECT (gobject);
if (self->program != COGL_INVALID_HANDLE) if (self->pipeline != NULL)
{ {
cogl_handle_unref (self->program); cogl_object_unref (self->pipeline);
self->pipeline = NULL;
self->program = COGL_INVALID_HANDLE;
self->shader = COGL_INVALID_HANDLE;
} }
G_OBJECT_CLASS (clutter_colorize_effect_parent_class)->dispose (gobject); 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, g_object_class_install_properties (gobject_class,
PROP_LAST, PROP_LAST,
obj_props); 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 static void
clutter_colorize_effect_init (ClutterColorizeEffect *self) 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; self->tint = default_tint;
update_tint_uniform (self);
} }
/** /**
@ -349,6 +345,8 @@ clutter_colorize_effect_set_tint (ClutterColorizeEffect *effect,
effect->tint = *tint; effect->tint = *tint;
update_tint_uniform (effect);
clutter_effect_queue_repaint (CLUTTER_EFFECT (effect)); clutter_effect_queue_repaint (CLUTTER_EFFECT (effect));
g_object_notify_by_pspec (G_OBJECT (effect), obj_props[PROP_TINT]); g_object_notify_by_pspec (G_OBJECT (effect), obj_props[PROP_TINT]);

View File

@ -61,18 +61,19 @@ struct _ClutterDesaturateEffect
/* the desaturation factor, also known as "strength" */ /* the desaturation factor, also known as "strength" */
gdouble factor; gdouble factor;
CoglHandle shader;
CoglHandle program;
gint tex_uniform;
gint factor_uniform; gint factor_uniform;
guint is_compiled : 1; gint tex_width;
gint tex_height;
CoglPipeline *pipeline;
}; };
struct _ClutterDesaturateEffectClass struct _ClutterDesaturateEffectClass
{ {
ClutterOffscreenEffectClass parent_class; ClutterOffscreenEffectClass parent_class;
CoglPipeline *base_pipeline;
}; };
/* the magic gray vec3 has been taken from the NTSC conversion weights /* the magic gray vec3 has been taken from the NTSC conversion weights
@ -82,8 +83,7 @@ struct _ClutterDesaturateEffectClass
* -- Richard S. Wright Jr, Benjamin Lipchak, Nicholas Haemel * -- Richard S. Wright Jr, Benjamin Lipchak, Nicholas Haemel
* Addison-Wesley * Addison-Wesley
*/ */
static const gchar *desaturate_glsl_shader = static const gchar *desaturate_glsl_declarations =
"uniform sampler2D tex;\n"
"uniform float factor;\n" "uniform float factor;\n"
"\n" "\n"
"vec3 desaturate (const vec3 color, const float desaturation)\n" "vec3 desaturate (const vec3 color, const float desaturation)\n"
@ -91,15 +91,11 @@ static const gchar *desaturate_glsl_shader =
" const vec3 gray_conv = vec3 (0.299, 0.587, 0.114);\n" " const vec3 gray_conv = vec3 (0.299, 0.587, 0.114);\n"
" vec3 gray = vec3 (dot (gray_conv, color));\n" " vec3 gray = vec3 (dot (gray_conv, color));\n"
" return vec3 (mix (color.rgb, gray, desaturation));\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"; "}\n";
static const gchar *desaturate_glsl_source =
" cogl_color_out.rgb = desaturate (cogl_color_out.rgb, factor);\n";
enum enum
{ {
PROP_0, PROP_0,
@ -136,83 +132,51 @@ clutter_desaturate_effect_pre_paint (ClutterEffect *effect)
return FALSE; 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); ClutterOffscreenEffect *offscreen_effect =
cogl_shader_source (self->shader, desaturate_glsl_shader); CLUTTER_OFFSCREEN_EFFECT (effect);
CoglHandle texture;
self->is_compiled = FALSE; texture = clutter_offscreen_effect_get_texture (offscreen_effect);
self->tex_uniform = -1; self->tex_width = cogl_texture_get_width (texture);
self->factor_uniform = -1; self->tex_height = cogl_texture_get_height (texture);
}
if (self->program == COGL_INVALID_HANDLE) cogl_pipeline_set_layer_texture (self->pipeline, 0, texture);
self->program = cogl_create_program ();
if (!self->is_compiled) return TRUE;
{
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 else
{ return FALSE;
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");
}
}
parent_class = CLUTTER_EFFECT_CLASS (clutter_desaturate_effect_parent_class);
return parent_class->pre_paint (effect);
} }
static void static void
clutter_desaturate_effect_paint_target (ClutterOffscreenEffect *effect) clutter_desaturate_effect_paint_target (ClutterOffscreenEffect *effect)
{ {
ClutterDesaturateEffect *self = CLUTTER_DESATURATE_EFFECT (effect); ClutterDesaturateEffect *self = CLUTTER_DESATURATE_EFFECT (effect);
ClutterOffscreenEffectClass *parent; ClutterActor *actor;
CoglHandle material; CoglHandle texture;
guint8 paint_opacity;
if (self->program == COGL_INVALID_HANDLE) texture = clutter_offscreen_effect_get_texture (effect);
goto out; cogl_pipeline_set_layer_texture (self->pipeline, 0, texture);
if (self->tex_uniform > -1) actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (effect));
cogl_program_set_uniform_1i (self->program, self->tex_uniform, 0); paint_opacity = clutter_actor_get_paint_opacity (actor);
if (self->factor_uniform > -1) cogl_pipeline_set_color4ub (self->pipeline,
cogl_program_set_uniform_1f (self->program, paint_opacity,
self->factor_uniform, paint_opacity,
self->factor); paint_opacity,
paint_opacity);
cogl_push_source (self->pipeline);
material = clutter_offscreen_effect_get_target (effect); cogl_rectangle (0, 0,
cogl_material_set_user_program (material, self->program); cogl_texture_get_width (texture),
cogl_texture_get_height (texture));
out: cogl_pop_source ();
parent = CLUTTER_OFFSCREEN_EFFECT_CLASS (clutter_desaturate_effect_parent_class);
parent->paint_target (effect);
} }
static void static void
@ -220,12 +184,10 @@ clutter_desaturate_effect_dispose (GObject *gobject)
{ {
ClutterDesaturateEffect *self = CLUTTER_DESATURATE_EFFECT (gobject); ClutterDesaturateEffect *self = CLUTTER_DESATURATE_EFFECT (gobject);
if (self->program != COGL_INVALID_HANDLE) if (self->pipeline != NULL)
{ {
cogl_handle_unref (self->program); cogl_object_unref (self->pipeline);
self->pipeline = NULL;
self->program = COGL_INVALID_HANDLE;
self->shader = COGL_INVALID_HANDLE;
} }
G_OBJECT_CLASS (clutter_desaturate_effect_parent_class)->dispose (gobject); G_OBJECT_CLASS (clutter_desaturate_effect_parent_class)->dispose (gobject);
@ -272,6 +234,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 static void
clutter_desaturate_effect_class_init (ClutterDesaturateEffectClass *klass) clutter_desaturate_effect_class_init (ClutterDesaturateEffectClass *klass)
{ {
@ -307,12 +278,39 @@ clutter_desaturate_effect_class_init (ClutterDesaturateEffectClass *klass)
g_object_class_install_properties (gobject_class, g_object_class_install_properties (gobject_class,
PROP_LAST, PROP_LAST,
obj_props); 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 static void
clutter_desaturate_effect_init (ClutterDesaturateEffect *self) 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; 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) if (fabsf (effect->factor - factor) >= 0.00001)
{ {
effect->factor = factor; effect->factor = factor;
update_factor_uniform (effect);
clutter_effect_queue_repaint (CLUTTER_EFFECT (effect)); clutter_effect_queue_repaint (CLUTTER_EFFECT (effect));