mirror of
https://github.com/brl/mutter.git
synced 2024-11-26 18:11:05 -05:00
clutter-shader-effect: Add a get_static_shader_source virtual
This is used as an alternative to calling clutter_shader_effect_set_shader_source. A ClutterShaderEffect subclass is now expected to implement this method to return the source for the effect that will be used for all instances of this subclass. It is only called once regardless of the number of instances created. That way Clutter can avoid recompiling the shader source for every new instance of the effect. https://bugzilla.gnome.org/show_bug.cgi?id=660512 Reviewed-by: Emmanuele Bassi <ebassi@linux.intel.com>
This commit is contained in:
parent
e8ec7ebed0
commit
8b995a9457
@ -37,36 +37,43 @@
|
|||||||
* <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>pre_paint()</function> virtual function
|
* overriding of the <function>paint_target()</function> virtual
|
||||||
* from the #ClutterEffect class.</para>
|
* function from the #ClutterOffscreenEffect class as well as the
|
||||||
* <para>The <function>pre_paint()</function> should set the shader's
|
* <function>get_static_shader_source()</function> virtual from the
|
||||||
* source and eventually set the uniforms. The sub-class should call
|
* #ClutterShaderEffect class.</para>
|
||||||
* clutter_shader_effect_set_shader_source() to set the shader source
|
* <para>The <function>get_static_shader_source()</function>
|
||||||
* code, and clutter_shader_effect_set_uniform_value() or
|
* function should return a copy of the shader source to use. This
|
||||||
* clutter_shader_effect_set_uniform() to set the values of the shader
|
* function is only called once per subclass of #ClutterShaderEffect
|
||||||
* uniforms, if any; the sub-class should then chain up to the
|
* regardless of how many instances of the effect are created. The
|
||||||
* #ClutterShaderEffect implementation.</para>
|
* source for the shader is typically stored in a static const
|
||||||
|
* string which is returned from this function via
|
||||||
|
* g_strdup().</para>
|
||||||
|
* <para>The <function>paint_target()</function> should set the
|
||||||
|
* shader's uniforms if any. This is done by calling
|
||||||
|
* clutter_shader_effect_set_uniform_value() or
|
||||||
|
* clutter_shader_effect_set_uniform(). The sub-class should then
|
||||||
|
* chain up to the #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>
|
||||||
* <para>The example below shows a typical implementation of the
|
* <para>The example below shows a typical implementation of the
|
||||||
* <function>pre_paint()</function> phase of a #ClutterShaderEffect
|
* <function>get_static_shader_source()</function> and
|
||||||
* sub-class.</para>
|
* <function>paint_target()</function> phases of a
|
||||||
|
* #ClutterShaderEffect sub-class.</para>
|
||||||
* <programlisting>
|
* <programlisting>
|
||||||
|
* static gchar *
|
||||||
|
* my_effect_get_static_shader_source (ClutterShaderEffect *effect)
|
||||||
|
* {
|
||||||
|
* return g_strdup (shader_source);
|
||||||
|
* }
|
||||||
|
*
|
||||||
* static gboolean
|
* static gboolean
|
||||||
* my_effect_pre_paint (ClutterEffect *effect)
|
* my_effect_paint_target (ClutterOffscreenEffect *effect)
|
||||||
* {
|
* {
|
||||||
* MyEffect *self = MY_EFFECT (effect);
|
* MyEffect *self = MY_EFFECT (effect);
|
||||||
* ClutterShaderEffect *shader = CLUTTER_SHADER_EFFECT (effect);
|
* ClutterShaderEffect *shader = CLUTTER_SHADER_EFFECT (effect);
|
||||||
* ClutterEffectClass *parent_class;
|
* ClutterEffectClass *parent_class;
|
||||||
* gfloat component_r, component_g, component_b;
|
* gfloat component_r, component_g, component_b;
|
||||||
*
|
*
|
||||||
* /* if the effect is not enabled we can bail out now */
|
|
||||||
* if (!clutter_actor_meta_get_enabled (CLUTTER_ACTOR_META (effect)))
|
|
||||||
* return FALSE;
|
|
||||||
*
|
|
||||||
* /* this function is a no-op after the first call */
|
|
||||||
* clutter_shader_effect_set_shader_source (shader, shader_source);
|
|
||||||
*
|
|
||||||
* /* the "tex" uniform is declared in the shader as:
|
* /* the "tex" uniform is declared in the shader as:
|
||||||
* *
|
* *
|
||||||
* * uniform int tex;
|
* * uniform int tex;
|
||||||
@ -92,8 +99,8 @@
|
|||||||
* component_b);
|
* component_b);
|
||||||
*
|
*
|
||||||
* /* chain up to the parent's implementation */
|
* /* chain up to the parent's implementation */
|
||||||
* parent_class = CLUTTER_EFFECT_CLASS (my_effect_parent_class);
|
* parent_class = CLUTTER_OFFSCREEN_EFFECT_CLASS (my_effect_parent_class);
|
||||||
* return parent_class->pre_paint (effect);
|
* return parent_class->paint_target (effect);
|
||||||
* }
|
* }
|
||||||
* </programlisting>
|
* </programlisting>
|
||||||
* </example>
|
* </example>
|
||||||
@ -134,11 +141,18 @@ struct _ClutterShaderEffectPrivate
|
|||||||
CoglHandle shader;
|
CoglHandle shader;
|
||||||
|
|
||||||
GHashTable *uniforms;
|
GHashTable *uniforms;
|
||||||
|
|
||||||
guint is_compiled : 1;
|
|
||||||
guint source_set : 1;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef struct _ClutterShaderEffectClassPrivate
|
||||||
|
{
|
||||||
|
/* These are the per-class pre-compiled shader and program which is
|
||||||
|
used when the class implements get_static_shader_source without
|
||||||
|
calling set_shader_source. They will be shared by all instances
|
||||||
|
of this class */
|
||||||
|
CoglHandle program;
|
||||||
|
CoglHandle shader;
|
||||||
|
} ClutterShaderEffectClassPrivate;
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
PROP_0,
|
PROP_0,
|
||||||
@ -150,9 +164,11 @@ enum
|
|||||||
|
|
||||||
static GParamSpec *obj_props[PROP_LAST];
|
static GParamSpec *obj_props[PROP_LAST];
|
||||||
|
|
||||||
G_DEFINE_TYPE (ClutterShaderEffect,
|
G_DEFINE_TYPE_WITH_CODE (ClutterShaderEffect,
|
||||||
clutter_shader_effect,
|
clutter_shader_effect,
|
||||||
CLUTTER_TYPE_OFFSCREEN_EFFECT);
|
CLUTTER_TYPE_OFFSCREEN_EFFECT,
|
||||||
|
g_type_add_class_private (g_define_type_id,
|
||||||
|
sizeof (ClutterShaderEffectClassPrivate)))
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
clutter_shader_effect_clear (ClutterShaderEffect *self,
|
clutter_shader_effect_clear (ClutterShaderEffect *self,
|
||||||
@ -160,15 +176,18 @@ clutter_shader_effect_clear (ClutterShaderEffect *self,
|
|||||||
{
|
{
|
||||||
ClutterShaderEffectPrivate *priv = self->priv;
|
ClutterShaderEffectPrivate *priv = self->priv;
|
||||||
|
|
||||||
if (priv->shader != COGL_INVALID_HANDLE && !priv->is_compiled)
|
if (priv->shader != COGL_INVALID_HANDLE)
|
||||||
cogl_handle_unref (priv->shader);
|
{
|
||||||
|
cogl_handle_unref (priv->shader);
|
||||||
|
|
||||||
|
priv->shader = COGL_INVALID_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
if (priv->program != COGL_INVALID_HANDLE)
|
if (priv->program != COGL_INVALID_HANDLE)
|
||||||
{
|
{
|
||||||
cogl_handle_unref (priv->program);
|
cogl_handle_unref (priv->program);
|
||||||
|
|
||||||
priv->program = COGL_INVALID_HANDLE;
|
priv->program = COGL_INVALID_HANDLE;
|
||||||
priv->shader = COGL_INVALID_HANDLE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reset_uniforms && priv->uniforms != NULL)
|
if (reset_uniforms && priv->uniforms != NULL)
|
||||||
@ -178,8 +197,6 @@ clutter_shader_effect_clear (ClutterShaderEffect *self,
|
|||||||
}
|
}
|
||||||
|
|
||||||
priv->actor = NULL;
|
priv->actor = NULL;
|
||||||
priv->is_compiled = FALSE;
|
|
||||||
priv->source_set = FALSE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -298,52 +315,102 @@ clutter_shader_effect_set_actor (ClutterActorMeta *meta,
|
|||||||
G_OBJECT_TYPE_NAME (meta));
|
G_OBJECT_TYPE_NAME (meta));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static CoglHandle
|
||||||
|
clutter_shader_effect_create_shader (ClutterShaderEffect *self)
|
||||||
|
{
|
||||||
|
ClutterShaderEffectPrivate *priv = self->priv;
|
||||||
|
|
||||||
|
switch (priv->shader_type)
|
||||||
|
{
|
||||||
|
case CLUTTER_FRAGMENT_SHADER:
|
||||||
|
return cogl_create_shader (COGL_SHADER_TYPE_FRAGMENT);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CLUTTER_VERTEX_SHADER:
|
||||||
|
return cogl_create_shader (COGL_SHADER_TYPE_VERTEX);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
g_assert_not_reached ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
clutter_shader_effect_try_static_source (ClutterShaderEffect *self)
|
||||||
|
{
|
||||||
|
ClutterShaderEffectPrivate *priv = self->priv;
|
||||||
|
ClutterShaderEffectClass *shader_effect_class =
|
||||||
|
CLUTTER_SHADER_EFFECT_GET_CLASS (self);
|
||||||
|
|
||||||
|
if (shader_effect_class->get_static_shader_source != NULL)
|
||||||
|
{
|
||||||
|
ClutterShaderEffectClassPrivate *class_priv;
|
||||||
|
|
||||||
|
class_priv =
|
||||||
|
G_TYPE_CLASS_GET_PRIVATE (shader_effect_class,
|
||||||
|
CLUTTER_TYPE_SHADER_EFFECT,
|
||||||
|
ClutterShaderEffectClassPrivate);
|
||||||
|
|
||||||
|
if (class_priv->shader == COGL_INVALID_HANDLE)
|
||||||
|
{
|
||||||
|
gchar *source;
|
||||||
|
|
||||||
|
class_priv->shader = clutter_shader_effect_create_shader (self);
|
||||||
|
|
||||||
|
source = shader_effect_class->get_static_shader_source (self);
|
||||||
|
|
||||||
|
cogl_shader_source (class_priv->shader, source);
|
||||||
|
|
||||||
|
g_free (source);
|
||||||
|
|
||||||
|
CLUTTER_NOTE (SHADER, "Compiling shader effect");
|
||||||
|
|
||||||
|
cogl_shader_compile (class_priv->shader);
|
||||||
|
|
||||||
|
if (cogl_shader_is_compiled (class_priv->shader))
|
||||||
|
{
|
||||||
|
class_priv->program = cogl_create_program ();
|
||||||
|
|
||||||
|
cogl_program_attach_shader (class_priv->program,
|
||||||
|
class_priv->shader);
|
||||||
|
|
||||||
|
cogl_program_link (class_priv->program);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gchar *log_buf = cogl_shader_get_info_log (class_priv->shader);
|
||||||
|
|
||||||
|
g_warning ("Unable to compile the GLSL shader: %s", log_buf);
|
||||||
|
g_free (log_buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
priv->shader = cogl_handle_ref (class_priv->shader);
|
||||||
|
|
||||||
|
if (class_priv->program != COGL_INVALID_HANDLE)
|
||||||
|
priv->program = cogl_handle_ref (class_priv->program);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
clutter_shader_effect_paint_target (ClutterOffscreenEffect *effect)
|
clutter_shader_effect_paint_target (ClutterOffscreenEffect *effect)
|
||||||
{
|
{
|
||||||
ClutterShaderEffectPrivate *priv = CLUTTER_SHADER_EFFECT (effect)->priv;
|
ClutterShaderEffect *self = CLUTTER_SHADER_EFFECT (effect);
|
||||||
|
ClutterShaderEffectPrivate *priv = self->priv;
|
||||||
ClutterOffscreenEffectClass *parent;
|
ClutterOffscreenEffectClass *parent;
|
||||||
CoglHandle material;
|
CoglHandle material;
|
||||||
|
|
||||||
|
/* If the source hasn't been set then we'll try to get it from the
|
||||||
|
static source instead */
|
||||||
|
if (priv->shader == COGL_INVALID_HANDLE)
|
||||||
|
clutter_shader_effect_try_static_source (self);
|
||||||
|
|
||||||
/* we haven't been prepared or we don't have support for
|
/* we haven't been prepared or we don't have support for
|
||||||
* GLSL shaders in Clutter
|
* GLSL shaders in Clutter
|
||||||
*/
|
*/
|
||||||
if (priv->program == COGL_INVALID_HANDLE ||
|
if (priv->program == COGL_INVALID_HANDLE)
|
||||||
priv->shader == COGL_INVALID_HANDLE)
|
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (!priv->source_set)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
if (!priv->is_compiled)
|
|
||||||
{
|
|
||||||
CLUTTER_NOTE (SHADER, "Compiling shader effect");
|
|
||||||
|
|
||||||
cogl_shader_compile (priv->shader);
|
|
||||||
if (!cogl_shader_is_compiled (priv->shader))
|
|
||||||
{
|
|
||||||
gchar *log_buf = cogl_shader_get_info_log (priv->shader);
|
|
||||||
|
|
||||||
g_warning ("Unable to compile the GLSL shader: %s", log_buf);
|
|
||||||
g_free (log_buf);
|
|
||||||
|
|
||||||
cogl_handle_unref (priv->shader);
|
|
||||||
priv->shader = COGL_INVALID_HANDLE;
|
|
||||||
|
|
||||||
cogl_handle_unref (priv->program);
|
|
||||||
priv->shader = COGL_INVALID_HANDLE;
|
|
||||||
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
cogl_program_attach_shader (priv->program, priv->shader);
|
|
||||||
cogl_handle_unref (priv->shader);
|
|
||||||
|
|
||||||
cogl_program_link (priv->program);
|
|
||||||
|
|
||||||
priv->is_compiled = TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
CLUTTER_NOTE (SHADER, "Applying the shader effect of type '%s'",
|
CLUTTER_NOTE (SHADER, "Applying the shader effect of type '%s'",
|
||||||
G_OBJECT_TYPE_NAME (effect));
|
G_OBJECT_TYPE_NAME (effect));
|
||||||
|
|
||||||
@ -566,12 +633,6 @@ clutter_shader_effect_add_uniform (ClutterShaderEffect *effect,
|
|||||||
else
|
else
|
||||||
shader_uniform_update (uniform, value);
|
shader_uniform_update (uniform, value);
|
||||||
|
|
||||||
if (priv->is_compiled)
|
|
||||||
{
|
|
||||||
uniform->location =
|
|
||||||
cogl_program_get_uniform_location (priv->program, uniform->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (priv->actor != NULL && !CLUTTER_ACTOR_IN_PAINT (priv->actor))
|
if (priv->actor != NULL && !CLUTTER_ACTOR_IN_PAINT (priv->actor))
|
||||||
clutter_actor_queue_redraw (priv->actor);
|
clutter_actor_queue_redraw (priv->actor);
|
||||||
}
|
}
|
||||||
@ -832,35 +893,32 @@ clutter_shader_effect_set_shader_source (ClutterShaderEffect *effect,
|
|||||||
|
|
||||||
priv = effect->priv;
|
priv = effect->priv;
|
||||||
|
|
||||||
if (priv->source_set)
|
if (priv->shader != COGL_INVALID_HANDLE)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
if (priv->program == COGL_INVALID_HANDLE)
|
priv->shader = clutter_shader_effect_create_shader (effect);
|
||||||
priv->program = cogl_create_program ();
|
|
||||||
|
|
||||||
if (priv->shader == COGL_INVALID_HANDLE)
|
|
||||||
{
|
|
||||||
switch (priv->shader_type)
|
|
||||||
{
|
|
||||||
case CLUTTER_FRAGMENT_SHADER:
|
|
||||||
priv->shader = cogl_create_shader (COGL_SHADER_TYPE_FRAGMENT);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CLUTTER_VERTEX_SHADER:
|
|
||||||
priv->shader = cogl_create_shader (COGL_SHADER_TYPE_VERTEX);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
priv->shader = COGL_INVALID_HANDLE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
g_assert (priv->shader != COGL_INVALID_HANDLE);
|
|
||||||
|
|
||||||
cogl_shader_source (priv->shader, source);
|
cogl_shader_source (priv->shader, source);
|
||||||
|
|
||||||
priv->source_set = TRUE;
|
CLUTTER_NOTE (SHADER, "Compiling shader effect");
|
||||||
|
|
||||||
|
cogl_shader_compile (priv->shader);
|
||||||
|
|
||||||
|
if (cogl_shader_is_compiled (priv->shader))
|
||||||
|
{
|
||||||
|
priv->program = cogl_create_program ();
|
||||||
|
|
||||||
|
cogl_program_attach_shader (priv->program, priv->shader);
|
||||||
|
|
||||||
|
cogl_program_link (priv->program);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gchar *log_buf = cogl_shader_get_info_log (priv->shader);
|
||||||
|
|
||||||
|
g_warning ("Unable to compile the GLSL shader: %s", log_buf);
|
||||||
|
g_free (log_buf);
|
||||||
|
}
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
@ -62,6 +62,11 @@ struct _ClutterShaderEffect
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* ClutterShaderEffectClass:
|
* ClutterShaderEffectClass:
|
||||||
|
* @get_static_shader_source: Returns the GLSL source code to use for
|
||||||
|
* instances of this shader effect. Note that this function is only
|
||||||
|
* called once per subclass of #ClutterShaderEffect regardless of how
|
||||||
|
* many instances are used. It is expected that subclasses will return
|
||||||
|
* a copy of a static string from this function.
|
||||||
*
|
*
|
||||||
* The <structname>ClutterShaderEffectClass</structname> structure contains
|
* The <structname>ClutterShaderEffectClass</structname> structure contains
|
||||||
* only private data
|
* only private data
|
||||||
@ -73,13 +78,16 @@ struct _ClutterShaderEffectClass
|
|||||||
/*< private >*/
|
/*< private >*/
|
||||||
ClutterOffscreenEffectClass parent_class;
|
ClutterOffscreenEffectClass parent_class;
|
||||||
|
|
||||||
|
/*< public >*/
|
||||||
|
gchar * (* get_static_shader_source) (ClutterShaderEffect *effect);
|
||||||
|
|
||||||
|
/*< private >*/
|
||||||
/* padding */
|
/* padding */
|
||||||
void (*_clutter_shader1) (void);
|
void (*_clutter_shader1) (void);
|
||||||
void (*_clutter_shader2) (void);
|
void (*_clutter_shader2) (void);
|
||||||
void (*_clutter_shader3) (void);
|
void (*_clutter_shader3) (void);
|
||||||
void (*_clutter_shader4) (void);
|
void (*_clutter_shader4) (void);
|
||||||
void (*_clutter_shader5) (void);
|
void (*_clutter_shader5) (void);
|
||||||
void (*_clutter_shader6) (void);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
GType clutter_shader_effect_get_type (void) G_GNUC_CONST;
|
GType clutter_shader_effect_get_type (void) G_GNUC_CONST;
|
||||||
|
Loading…
Reference in New Issue
Block a user