shader-effect: Simplify setting the shader source

Sub-classes of ShaderEffect currently have to get the handle for the
Cogl shader and call cogl_shader_source(); this makes it awkward to
implement a ShaderEffect, and it exposes handles and Cogl API that we
might want to change in the future.

We should provide a ClutterShaderEffect method that allows to (safely)
set the shader source at the right time for sub-classes to use.
This commit is contained in:
Emmanuele Bassi 2010-05-17 12:55:46 +01:00
parent f2caafa127
commit 0e9a1dee6d
5 changed files with 81 additions and 101 deletions

View File

@ -94,8 +94,6 @@ struct _ClutterBlurEffect
*/ */
gfloat x_step; gfloat x_step;
gfloat y_step; gfloat y_step;
guint source_set : 1;
}; };
struct _ClutterBlurEffectClass struct _ClutterBlurEffectClass
@ -147,16 +145,7 @@ clutter_blur_effect_pre_paint (ClutterEffect *effect)
shader_effect = CLUTTER_SHADER_EFFECT (effect); shader_effect = CLUTTER_SHADER_EFFECT (effect);
if (!self->source_set) clutter_shader_effect_set_shader_source (shader_effect, box_blur_glsl_shader);
{
CoglHandle shader = clutter_shader_effect_get_shader (shader_effect);
if (shader == COGL_INVALID_HANDLE)
return FALSE;
cogl_shader_source (shader, box_blur_glsl_shader);
self->source_set = TRUE;
}
clutter_shader_effect_set_uniform (shader_effect, clutter_shader_effect_set_uniform (shader_effect,
"tex", "tex",

View File

@ -65,8 +65,6 @@ struct _ClutterColorizeEffect
/* the tint of the colorization */ /* the tint of the colorization */
ClutterColor tint; ClutterColor tint;
guint source_set : 1;
}; };
struct _ClutterColorizeEffectClass struct _ClutterColorizeEffectClass
@ -114,17 +112,12 @@ clutter_colorize_effect_pre_paint (ClutterEffect *effect)
ClutterEffectClass *parent_class; ClutterEffectClass *parent_class;
float tint_r, tint_g, tint_b; float tint_r, tint_g, tint_b;
if (!clutter_actor_meta_get_enabled (CLUTTER_ACTOR_META (effect)))
return FALSE;
shader_effect = CLUTTER_SHADER_EFFECT (effect); shader_effect = CLUTTER_SHADER_EFFECT (effect);
if (!self->source_set)
{
CoglHandle shader = clutter_shader_effect_get_shader (shader_effect);
if (shader == COGL_INVALID_HANDLE) clutter_shader_effect_set_shader_source (shader_effect, colorize_glsl_shader);
return FALSE;
cogl_shader_source (shader, colorize_glsl_shader);
self->source_set = TRUE;
}
/* we want normalized values here */ /* we want normalized values here */
tint_r = self->tint.red / 255.0f; tint_r = self->tint.red / 255.0f;

View File

@ -67,8 +67,6 @@ struct _ClutterDesaturateEffect
/* the desaturation factor, also known as "strength" */ /* the desaturation factor, also known as "strength" */
gdouble factor; gdouble factor;
guint source_set : 1;
}; };
struct _ClutterDesaturateEffectClass struct _ClutterDesaturateEffectClass
@ -129,18 +127,7 @@ clutter_desaturate_effect_pre_paint (ClutterEffect *effect)
shader_effect = CLUTTER_SHADER_EFFECT (effect); shader_effect = CLUTTER_SHADER_EFFECT (effect);
if (!self->source_set) clutter_shader_effect_set_shader_source (shader_effect, desaturate_glsl_shader);
{
CoglHandle shader;
/* get the CoglShader handle to set the source */
shader = clutter_shader_effect_get_shader (shader_effect);
if (shader == COGL_INVALID_HANDLE)
return FALSE;
cogl_shader_source (shader, desaturate_glsl_shader);
self->source_set = TRUE;
}
factor = (float) self->factor; factor = (float) self->factor;

View File

@ -38,51 +38,14 @@
* <refsect2 id="ClutterShaderEffect-implementing"> * <refsect2 id="ClutterShaderEffect-implementing">
* <title>Implementing a ClutterShaderEffect</title> * <title>Implementing a ClutterShaderEffect</title>
* <para>Creating a sub-class of #ClutterShaderEffect requires the * <para>Creating a sub-class of #ClutterShaderEffect requires the
* overriding of the <function>set_actor()</function> virtual function * overriding of the <function>pre_paint()</function> virtual function
* from the #ClutterActorMeta class, and the <function>pre_paint()</function> * from the #ClutterEffect class.</para>
* virtual functions from the #ClutterEffect class.</para> * <para>The <function>pre_paint()</function> should set the shader's
* <para>The <function>set_actor()</function> must chain up to the * source and eventually set the uniforms. The sub-class should call
* #ClutterShaderEffect implementation; if the effect has not been disabled * clutter_shader_effect_set_shader_source() to set the shader source
* by the super class implementation, then the sub-class should get the * code, and clutter_shader_effect_set_uniform_value() or
* #CoglHandle for the fragment shader using * clutter_shader_effect_set_uniform() to set the values of the shader
* clutter_shader_effect_get_shader() and call cogl_shader_source() to set * uniforms, if any; the sub-class should then chain up to the
* the source of the program.</para>
* <example id="ClutterShaderEffect-example-preparing">
* <title>Preparing a ClutterShaderEffect</title>
* <para>The example below shows the typical implementation of the
* <function>set_actor()</function> phase of a #ClutterShaderEffect
* sub-class.</para>
* <programlisting>
* static void
* my_effect_set_actor (ClutterActorMeta *meta,
* ClutterActor *actor)
* {
* MyEffect *self = MY_EFFECT (meta);
* ClutterActorMetaClass *parent_class;
* CoglHandle handle;
*
* /&ast; chain up to the parent's implementation &ast;/
* parent_class = CLUTTER_EFFECT_CLASS (my_effect_parent_class);
* parent_class->set_actor (meta, actor));
*
* /&ast; if the parent class disabled the effect then we return &ast;
* if (!clutter_actor_meta_get_enabled (meta))
* return;
*
* /&ast; we should have a handle to the shader at this point &ast;/
* handle = clutter_shader_effect_get_shader (CLUTTER_SHADER_EFFECT (self));
* if (handle == COGL_INVALID_HANDLE)
* return;
*
* /&ast; set the source of the shader &ast;/
* cogl_shader_source (handle, my_effect_glsl_source);
* }
* </programlisting>
* </example>
* <para>The <function>pre_paint()</function> is optional and it depends on
* whether the fragment shader has uniforms to set. The sub-class should call
* clutter_shader_effect_set_uniform_value() or
* clutter_shader_effect_set_uniform() and then chain up to the
* #ClutterShaderEffect implementation.</para> * #ClutterShaderEffect implementation.</para>
* <example id="ClutterShaderEffect-example-uniforms"> * <example id="ClutterShaderEffect-example-uniforms">
* <title>Setting uniforms on a ClutterShaderEffect</title> * <title>Setting uniforms on a ClutterShaderEffect</title>
@ -98,11 +61,13 @@
* ClutterEffectClass *parent_class; * ClutterEffectClass *parent_class;
* gfloat component_r, component_g, component_b; * gfloat component_r, component_g, component_b;
* *
* /&ast; chain up to the parent's implementation &ast;/ * /&ast; if the effect is not enabled we can bail out now &ast;/
* parent_class = CLUTTER_EFFECT_CLASS (my_effect_parent_class); * if (!clutter_actor_meta_get_enabled (CLUTTER_ACTOR_META (effect)))
* if (!parent_class->pre_paint (effect))
* return FALSE; * return FALSE;
* *
* /&ast; this function is a no-op after the first call &ast;/
* clutter_shader_effect_set_shader_source (shader, shader_source);
*
* /&ast; the "tex" uniform is declared in the shader as: * /&ast; the "tex" uniform is declared in the shader as:
* &ast; * &ast;
* &ast; uniform int tex; * &ast; uniform int tex;
@ -127,7 +92,9 @@
* component_g, * component_g,
* component_b); * component_b);
* *
* return TRUE; * /&ast; chain up to the parent's implementation &ast;/
* parent_class = CLUTTER_EFFECT_CLASS (my_effect_parent_class);
* return parent_class->pre_paint (effect);
* } * }
* </programlisting> * </programlisting>
* </example> * </example>
@ -167,6 +134,7 @@ struct _ClutterShaderEffectPrivate
GHashTable *uniforms; GHashTable *uniforms;
guint is_compiled : 1; guint is_compiled : 1;
guint source_set : 1;
}; };
G_DEFINE_ABSTRACT_TYPE (ClutterShaderEffect, G_DEFINE_ABSTRACT_TYPE (ClutterShaderEffect,
@ -199,6 +167,7 @@ clutter_shader_effect_clear (ClutterShaderEffect *effect,
priv->actor = NULL; priv->actor = NULL;
priv->is_compiled = FALSE; priv->is_compiled = FALSE;
priv->source_set = FALSE;
} }
static void static void
@ -289,8 +258,6 @@ clutter_shader_effect_set_actor (ClutterActorMeta *meta,
ClutterShaderEffect *self = CLUTTER_SHADER_EFFECT (meta); ClutterShaderEffect *self = CLUTTER_SHADER_EFFECT (meta);
ClutterShaderEffectPrivate *priv = self->priv; ClutterShaderEffectPrivate *priv = self->priv;
ClutterActorMetaClass *parent; ClutterActorMetaClass *parent;
ClutterActorBox allocation;
gfloat width, height;
if (!clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL)) if (!clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL))
{ {
@ -318,9 +285,6 @@ clutter_shader_effect_set_actor (ClutterActorMeta *meta,
CLUTTER_NOTE (SHADER, "Preparing shader effect of type '%s'", CLUTTER_NOTE (SHADER, "Preparing shader effect of type '%s'",
G_OBJECT_TYPE_NAME (meta)); G_OBJECT_TYPE_NAME (meta));
clutter_actor_get_allocation_box (priv->actor, &allocation);
clutter_actor_box_get_size (&allocation, &width, &height);
priv->program = cogl_create_program (); priv->program = cogl_create_program ();
priv->shader = cogl_create_shader (COGL_SHADER_TYPE_FRAGMENT); priv->shader = cogl_create_shader (COGL_SHADER_TYPE_FRAGMENT);
@ -340,6 +304,9 @@ clutter_shader_effect_paint_target (ClutterOffscreenEffect *effect)
priv->shader == COGL_INVALID_HANDLE) priv->shader == COGL_INVALID_HANDLE)
return; return;
if (!priv->source_set)
return;
if (!priv->is_compiled) if (!priv->is_compiled)
{ {
CLUTTER_NOTE (SHADER, "Compiling shader effect"); CLUTTER_NOTE (SHADER, "Compiling shader effect");
@ -757,3 +724,44 @@ clutter_shader_effect_set_uniform (ClutterShaderEffect *effect,
&args); &args);
va_end (args); va_end (args);
} }
/**
* clutter_shader_effect_set_shader_source:
* @effect: a #ClutterShaderEffect
* @source: the source of a GLSL shader
*
* Sets the source of the GLSL shader used by @effect
*
* This function should only be called by implementations of
* the #ClutterShaderEffect class, and not by application code.
*
* This function can only be called once; subsequent calls will
* yield no result.
*
* Return value: %TRUE if the source was set
*
* Since: 1.4
*/
gboolean
clutter_shader_effect_set_shader_source (ClutterShaderEffect *effect,
const gchar *source)
{
ClutterShaderEffectPrivate *priv;
g_return_val_if_fail (CLUTTER_IS_SHADER_EFFECT (effect), FALSE);
g_return_val_if_fail (source != NULL && *source != '\0', FALSE);
priv = effect->priv;
if (priv->source_set)
return TRUE;
if (priv->shader == COGL_INVALID_HANDLE)
return FALSE;
cogl_shader_source (priv->shader, source);
priv->source_set = TRUE;
return TRUE;
}

View File

@ -84,17 +84,20 @@ struct _ClutterShaderEffectClass
GType clutter_shader_effect_get_type (void) G_GNUC_CONST; GType clutter_shader_effect_get_type (void) G_GNUC_CONST;
void clutter_shader_effect_set_uniform (ClutterShaderEffect *effect, gboolean clutter_shader_effect_set_shader_source (ClutterShaderEffect *effect,
const gchar *name, const gchar *source);
GType gtype,
gsize size,
...);
void clutter_shader_effect_set_uniform_value (ClutterShaderEffect *effect,
const gchar *name,
const GValue *value);
CoglHandle clutter_shader_effect_get_shader (ClutterShaderEffect *effect); void clutter_shader_effect_set_uniform (ClutterShaderEffect *effect,
CoglHandle clutter_shader_effect_get_program (ClutterShaderEffect *effect); const gchar *name,
GType gtype,
gsize size,
...);
void clutter_shader_effect_set_uniform_value (ClutterShaderEffect *effect,
const gchar *name,
const GValue *value);
CoglHandle clutter_shader_effect_get_shader (ClutterShaderEffect *effect);
CoglHandle clutter_shader_effect_get_program (ClutterShaderEffect *effect);
G_END_DECLS G_END_DECLS