mutter/clutter/clutter-shader-effect.c
Emmanuele Bassi 53483896a8 shader-effect: Allow instantiating ShaderEffect
ClutterShaderEffect doesn't require to be sub-classed in order to be
useful. It is possible to just create an instance, set the source and
the uniforms, and attach it to an actor. This should effectively replace
ClutterShader for good.
2011-06-07 14:09:53 +01:00

867 lines
26 KiB
C

/*
* Clutter.
*
* An OpenGL based 'interactive canvas' library.
*
* Copyright (C) 2010 Intel Corporation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Author:
* Emmanuele Bassi <ebassi@linux.intel.com>
*/
/**
* SECTION:clutter-shader-effect
* @short_description: Base class for shader effects
* @See_Also: #ClutterEffect, #ClutterOffscreenEffect
*
* #ClutterShaderEffect is a class that implements all the plumbing for
* creating #ClutterEffect<!-- -->s using GLSL shaders.
*
* #ClutterShaderEffect creates an offscreen buffer and then applies the
* GLSL shader (after checking whether the compilation and linking were
* successfull) to the buffer before painting it on screen.
*
* <refsect2 id="ClutterShaderEffect-implementing">
* <title>Implementing a ClutterShaderEffect</title>
* <para>Creating a sub-class of #ClutterShaderEffect requires the
* overriding of the <function>pre_paint()</function> virtual function
* from the #ClutterEffect class.</para>
* <para>The <function>pre_paint()</function> should set the shader's
* source and eventually set the uniforms. The sub-class should call
* clutter_shader_effect_set_shader_source() to set the shader source
* code, and clutter_shader_effect_set_uniform_value() or
* clutter_shader_effect_set_uniform() to set the values of the shader
* uniforms, if any; the sub-class should then chain up to the
* #ClutterShaderEffect implementation.</para>
* <example id="ClutterShaderEffect-example-uniforms">
* <title>Setting uniforms on a ClutterShaderEffect</title>
* <para>The example below shows a typical implementation of the
* <function>pre_paint()</function> phase of a #ClutterShaderEffect
* sub-class.</para>
* <programlisting>
* static gboolean
* my_effect_pre_paint (ClutterEffect *effect)
* {
* MyEffect *self = MY_EFFECT (effect);
* ClutterShaderEffect *shader = CLUTTER_SHADER_EFFECT (effect);
* ClutterEffectClass *parent_class;
* gfloat component_r, component_g, component_b;
*
* /&ast; if the effect is not enabled we can bail out now &ast;/
* if (!clutter_actor_meta_get_enabled (CLUTTER_ACTOR_META (effect)))
* 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;
* &ast; uniform int tex;
* &ast;
* &ast; and it is passed a constant value of 0
* &ast;/
* clutter_shader_effect_set_uniform (shader, "tex", G_TYPE_INT, 1, 0);
*
* /&ast; the "component" uniform is declared in the shader as:
* &ast;
* &ast; uniform vec3 component;
* &ast;
* &ast; and it's defined to contain the normalized components
* &ast; of a #ClutterColor
* &ast;/
* component_r = self->color.red / 255.0f;
* component_g = self->color.green / 255.0f;
* component_b = self->color.blue / 255.0f;
* clutter_shader_effect_set_uniform (shader, "component",
* G_TYPE_FLOAT, 3,
* component_r,
* component_g,
* component_b);
*
* /&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>
* </example>
* </refsect2>
*
* #ClutterShaderEffect is available since Clutter 1.4
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "clutter-shader-effect.h"
#include "cogl/cogl.h"
#include "clutter-debug.h"
#include "clutter-enum-types.h"
#include "clutter-feature.h"
#include "clutter-private.h"
#include "clutter-shader-types.h"
typedef struct _ShaderUniform
{
gchar *name;
GType type;
GValue value;
GLint location;
} ShaderUniform;
struct _ClutterShaderEffectPrivate
{
ClutterActor *actor;
ClutterShaderType shader_type;
CoglHandle program;
CoglHandle shader;
GHashTable *uniforms;
guint is_compiled : 1;
guint source_set : 1;
};
enum
{
PROP_0,
PROP_SHADER_TYPE,
PROP_LAST
};
static GParamSpec *obj_props[PROP_LAST];
G_DEFINE_TYPE (ClutterShaderEffect,
clutter_shader_effect,
CLUTTER_TYPE_OFFSCREEN_EFFECT);
static inline void
clutter_shader_effect_clear (ClutterShaderEffect *self,
gboolean reset_uniforms)
{
ClutterShaderEffectPrivate *priv = self->priv;
if (priv->shader != COGL_INVALID_HANDLE && !priv->is_compiled)
cogl_handle_unref (priv->shader);
if (priv->program != COGL_INVALID_HANDLE)
{
cogl_handle_unref (priv->program);
priv->program = COGL_INVALID_HANDLE;
priv->shader = COGL_INVALID_HANDLE;
}
if (reset_uniforms && priv->uniforms != NULL)
{
g_hash_table_destroy (priv->uniforms);
priv->uniforms = NULL;
}
priv->actor = NULL;
priv->is_compiled = FALSE;
priv->source_set = FALSE;
}
static void
clutter_shader_effect_update_uniforms (ClutterShaderEffect *effect)
{
ClutterShaderEffectPrivate *priv = effect->priv;
GHashTableIter iter;
gpointer key, value;
gsize size;
if (priv->program == COGL_INVALID_HANDLE)
return;
if (priv->uniforms == NULL)
return;
key = value = NULL;
g_hash_table_iter_init (&iter, priv->uniforms);
while (g_hash_table_iter_next (&iter, &key, &value))
{
ShaderUniform *uniform = value;
if (uniform->location == -1)
uniform->location = cogl_program_get_uniform_location (priv->program,
uniform->name);
if (CLUTTER_VALUE_HOLDS_SHADER_FLOAT (&uniform->value))
{
const GLfloat *floats;
floats = clutter_value_get_shader_float (&uniform->value, &size);
cogl_program_set_uniform_float (priv->program, uniform->location,
size, 1,
floats);
}
else if (CLUTTER_VALUE_HOLDS_SHADER_INT (&uniform->value))
{
const GLint *ints;
ints = clutter_value_get_shader_int (&uniform->value, &size);
cogl_program_set_uniform_int (priv->program, uniform->location,
size, 1,
ints);
}
else if (CLUTTER_VALUE_HOLDS_SHADER_MATRIX (&uniform->value))
{
const GLfloat *matrix;
matrix = clutter_value_get_shader_matrix (&uniform->value, &size);
cogl_program_set_uniform_matrix (priv->program, uniform->location,
size, 1,
FALSE,
matrix);
}
else if (G_VALUE_HOLDS_FLOAT (&uniform->value))
{
const GLfloat float_val = g_value_get_float (&uniform->value);
cogl_program_set_uniform_float (priv->program, uniform->location,
1, 1,
&float_val);
}
else if (G_VALUE_HOLDS_DOUBLE (&uniform->value))
{
const GLfloat float_val =
(GLfloat) g_value_get_double (&uniform->value);
cogl_program_set_uniform_float (priv->program, uniform->location,
1, 1,
&float_val);
}
else if (G_VALUE_HOLDS_INT (&uniform->value))
{
const GLint int_val = g_value_get_int (&uniform->value);
cogl_program_set_uniform_int (priv->program, uniform->location,
1, 1,
&int_val);
}
else
g_warning ("Invalid uniform of type '%s' for name '%s'",
g_type_name (G_VALUE_TYPE (&uniform->value)),
uniform->name);
}
}
static void
clutter_shader_effect_set_actor (ClutterActorMeta *meta,
ClutterActor *actor)
{
ClutterShaderEffect *self = CLUTTER_SHADER_EFFECT (meta);
ClutterShaderEffectPrivate *priv = self->priv;
ClutterActorMetaClass *parent;
if (!clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL))
{
/* if we don't have support for GLSL shaders then we
* forcibly disable the ActorMeta
*/
g_warning ("Unable to use the ShaderEffect: the graphics hardware "
"or the current GL driver does not implement support "
"for the GLSL shading language.");
clutter_actor_meta_set_enabled (meta, FALSE);
return;
}
parent = CLUTTER_ACTOR_META_CLASS (clutter_shader_effect_parent_class);
parent->set_actor (meta, actor);
/* we keep a back pointer here */
priv->actor = clutter_actor_meta_get_actor (meta);
if (priv->actor == NULL)
return;
CLUTTER_NOTE (SHADER, "Preparing shader effect of type '%s'",
G_OBJECT_TYPE_NAME (meta));
}
static void
clutter_shader_effect_paint_target (ClutterOffscreenEffect *effect)
{
ClutterShaderEffectPrivate *priv = CLUTTER_SHADER_EFFECT (effect)->priv;
ClutterOffscreenEffectClass *parent;
CoglHandle material;
/* we haven't been prepared or we don't have support for
* GLSL shaders in Clutter
*/
if (priv->program == COGL_INVALID_HANDLE ||
priv->shader == COGL_INVALID_HANDLE)
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'",
G_OBJECT_TYPE_NAME (effect));
clutter_shader_effect_update_uniforms (CLUTTER_SHADER_EFFECT (effect));
/* associate the program to the offscreen target material */
material = clutter_offscreen_effect_get_target (effect);
cogl_material_set_user_program (material, priv->program);
out:
/* paint the offscreen buffer */
parent = CLUTTER_OFFSCREEN_EFFECT_CLASS (clutter_shader_effect_parent_class);
parent->paint_target (effect);
}
static void
clutter_shader_effect_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
ClutterShaderEffectPrivate *priv = CLUTTER_SHADER_EFFECT (gobject)->priv;
switch (prop_id)
{
case PROP_SHADER_TYPE:
priv->shader_type = g_value_get_enum (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
clutter_shader_effect_finalize (GObject *gobject)
{
ClutterShaderEffect *effect = CLUTTER_SHADER_EFFECT (gobject);
clutter_shader_effect_clear (effect, TRUE);
G_OBJECT_CLASS (clutter_shader_effect_parent_class)->finalize (gobject);
}
static void
clutter_shader_effect_class_init (ClutterShaderEffectClass *klass)
{
ClutterActorMetaClass *meta_class = CLUTTER_ACTOR_META_CLASS (klass);
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
ClutterOffscreenEffectClass *offscreen_class;
offscreen_class = CLUTTER_OFFSCREEN_EFFECT_CLASS (klass);
g_type_class_add_private (klass, sizeof (ClutterShaderEffectPrivate));
/**
* ClutterShaderEffect:shader-type:
*
* The type of shader that is used by the effect. This property
* should be set by the constructor of #ClutterShaderEffect
* sub-classes.
*
* Since: 1.4
*/
obj_props[PROP_SHADER_TYPE] =
g_param_spec_enum ("shader-type",
P_("Shader Type"),
P_("The type of shader used"),
CLUTTER_TYPE_SHADER_TYPE,
CLUTTER_FRAGMENT_SHADER,
CLUTTER_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
gobject_class->set_property = clutter_shader_effect_set_property;
gobject_class->finalize = clutter_shader_effect_finalize;
g_object_class_install_properties (gobject_class,
PROP_LAST,
obj_props);
meta_class->set_actor = clutter_shader_effect_set_actor;
offscreen_class->paint_target = clutter_shader_effect_paint_target;
}
static void
clutter_shader_effect_init (ClutterShaderEffect *effect)
{
effect->priv = G_TYPE_INSTANCE_GET_PRIVATE (effect,
CLUTTER_TYPE_SHADER_EFFECT,
ClutterShaderEffectPrivate);
}
/**
* clutter_shader_effect_new:
* @shader_type: the type of the shader, either %CLUTTER_FRAGMENT_SHADER,
* or %CLUTTER_VERTEX_SHADER
*
* Creates a new #ClutterShaderEffect, to be applied to an actor using
* clutter_actor_add_effect().
*
* The effect will be empty until clutter_shader_effect_set_shader_source()
* is called.
*
* Return value: (transfer full): the newly created #ClutterShaderEffect.
* Use g_object_unref() when done.
*
* Since: 1.8
*/
ClutterEffect *
clutter_shader_effect_new (ClutterShaderType shader_type)
{
return g_object_new (CLUTTER_TYPE_SHADER_EFFECT,
"shader-type", shader_type,
NULL);
}
/**
* clutter_shader_effect_get_shader:
* @effect: a #ClutterShaderEffect
*
* Retrieves a pointer to the shader's handle
*
* Return value: (transfer none): a pointer to the shader's handle,
* or %COGL_INVALID_HANDLE
*
* Since: 1.4
*/
CoglHandle
clutter_shader_effect_get_shader (ClutterShaderEffect *effect)
{
g_return_val_if_fail (CLUTTER_IS_SHADER_EFFECT (effect),
COGL_INVALID_HANDLE);
return effect->priv->shader;
}
/**
* clutter_shader_effect_get_program:
* @effect: a #ClutterShaderEffect
*
* Retrieves a pointer to the program's handle
*
* Return value: (transfer none): a pointer to the program's handle,
* or %COGL_INVALID_HANDLE
*
* Since: 1.4
*/
CoglHandle
clutter_shader_effect_get_program (ClutterShaderEffect *effect)
{
g_return_val_if_fail (CLUTTER_IS_SHADER_EFFECT (effect),
COGL_INVALID_HANDLE);
return effect->priv->program;
}
static void
shader_uniform_free (gpointer data)
{
if (data != NULL)
{
ShaderUniform *uniform = data;
g_value_unset (&uniform->value);
g_free (uniform->name);
g_slice_free (ShaderUniform, uniform);
}
}
static ShaderUniform *
shader_uniform_new (const gchar *name,
const GValue *value)
{
ShaderUniform *retval;
retval = g_slice_new0 (ShaderUniform);
retval->name = g_strdup (name);
retval->type = G_VALUE_TYPE (value);
retval->location = -1;
g_value_init (&retval->value, retval->type);
g_value_copy (value, &retval->value);
return retval;
}
static void
shader_uniform_update (ShaderUniform *uniform,
const GValue *value)
{
g_value_unset (&uniform->value);
g_value_init (&uniform->value, G_VALUE_TYPE (value));
g_value_copy (value, &uniform->value);
}
static inline void
clutter_shader_effect_add_uniform (ClutterShaderEffect *effect,
const gchar *name,
const GValue *value)
{
ClutterShaderEffectPrivate *priv = effect->priv;
ShaderUniform *uniform;
if (priv->uniforms == NULL)
{
priv->uniforms = g_hash_table_new_full (g_str_hash, g_str_equal,
NULL,
shader_uniform_free);
}
uniform = g_hash_table_lookup (priv->uniforms, name);
if (uniform == NULL)
{
uniform = shader_uniform_new (name, value);
g_hash_table_insert (priv->uniforms, uniform->name, uniform);
}
else
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))
clutter_actor_queue_redraw (priv->actor);
}
/**
* clutter_shader_effect_set_uniform_value:
* @effect: a #ClutterShaderEffect
* @name: the name of the uniform to set
* @value: a #GValue with the value of the uniform to set
*
* Sets @value as the payload for the uniform @name inside the shader
* effect
*
* The #GType of the @value must be one of: %G_TYPE_INT, for a single
* integer value; %G_TYPE_FLOAT, for a single floating point value;
* %CLUTTER_TYPE_SHADER_INT, for an array of integer values;
* %CLUTTER_TYPE_SHADER_FLOAT, for an array of floating point values;
* and %CLUTTER_TYPE_SHADER_MATRIX, for a matrix of floating point
* values. It also accepts %G_TYPE_DOUBLE for compatibility with other
* languages than C.
*
* Since: 1.4
*/
void
clutter_shader_effect_set_uniform_value (ClutterShaderEffect *effect,
const gchar *name,
const GValue *value)
{
g_return_if_fail (CLUTTER_IS_SHADER_EFFECT (effect));
g_return_if_fail (name != NULL);
g_return_if_fail (value != NULL);
clutter_shader_effect_add_uniform (effect, name, value);
}
static void
clutter_shader_effect_set_uniform_valist (ClutterShaderEffect *effect,
const gchar *name,
GType value_type,
gsize n_values,
va_list *args)
{
GValue value = { 0, };
if (value_type == CLUTTER_TYPE_SHADER_INT)
{
gint *int_values = va_arg (*args, gint*);
g_value_init (&value, CLUTTER_TYPE_SHADER_INT);
clutter_value_set_shader_int (&value, n_values, int_values);
goto add_uniform;
}
if (value_type == CLUTTER_TYPE_SHADER_FLOAT)
{
gfloat *float_values = va_arg (*args, gfloat*);
g_value_init (&value, CLUTTER_TYPE_SHADER_FLOAT);
clutter_value_set_shader_float (&value, n_values, float_values);
goto add_uniform;
}
if (value_type == CLUTTER_TYPE_SHADER_MATRIX)
{
gfloat *float_values = va_arg (*args, gfloat*);
g_value_init (&value, CLUTTER_TYPE_SHADER_MATRIX);
clutter_value_set_shader_matrix (&value, n_values, float_values);
goto add_uniform;
}
if (value_type == G_TYPE_INT)
{
g_return_if_fail (n_values <= 4);
/* if we only have one value we can go through the fast path
* of using G_TYPE_INT, otherwise we create a vector of integers
* from the passed values
*/
if (n_values == 1)
{
gint int_val = va_arg (*args, gint);
g_value_init (&value, G_TYPE_INT);
g_value_set_int (&value, int_val);
}
else
{
gint *int_values = g_new (gint, n_values);
gint i;
for (i = 0; i < n_values; i++)
int_values[i] = va_arg (*args, gint);
g_value_init (&value, CLUTTER_TYPE_SHADER_INT);
clutter_value_set_shader_int (&value, n_values, int_values);
g_free (int_values);
}
goto add_uniform;
}
if (value_type == G_TYPE_FLOAT)
{
g_return_if_fail (n_values <= 4);
/* if we only have one value we can go through the fast path
* of using G_TYPE_FLOAT, otherwise we create a vector of floats
* from the passed values
*/
if (n_values == 1)
{
gfloat float_val = (gfloat) va_arg (*args, gdouble);
g_value_init (&value, G_TYPE_FLOAT);
g_value_set_float (&value, float_val);
}
else
{
gfloat *float_values = g_new (gfloat, n_values);
gint i;
for (i = 0; i < n_values; i++)
float_values[i] = (gfloat) va_arg (*args, double);
g_value_init (&value, CLUTTER_TYPE_SHADER_FLOAT);
clutter_value_set_shader_float (&value, n_values, float_values);
g_free (float_values);
}
goto add_uniform;
}
g_warning ("Unrecognized type '%s' (values: %d) for uniform name '%s'",
g_type_name (value_type),
(int) n_values,
name);
return;
add_uniform:
clutter_shader_effect_add_uniform (effect, name, &value);
g_value_unset (&value);
}
/**
* clutter_shader_effect_set_uniform:
* @effect: a #ClutterShaderEffect
* @name: the name of the uniform to set
* @gtype: the type of the uniform to set
* @n_values: the number of values
* @Varargs: a list of values
*
* Sets a list of values as the payload for the uniform @name inside
* the shader effect
*
* The @gtype must be one of: %G_TYPE_INT, for 1 or more integer values;
* %G_TYPE_FLOAT, for 1 or more floating point values;
* %CLUTTER_TYPE_SHADER_INT, for a pointer to an array of integer values;
* %CLUTTER_TYPE_SHADER_FLOAT, for a pointer to an array of floating point
* values; and %CLUTTER_TYPE_SHADER_MATRIX, for a pointer to an array of
* floating point values mapping a matrix
*
* The number of values interepreted is defined by the @n_value
* argument, and by the @gtype argument. For instance, a uniform named
* "sampler0" and containing a single integer value is set using:
*
* |[
* clutter_shader_effect_set_uniform (effect, "sampler0",
* G_TYPE_INT, 1,
* 0);
* ]|
*
* While a uniform named "components" and containing a 3-elements vector
* of floating point values (a "vec3") can be set using:
*
* |[
* gfloat component_r, component_g, component_b;
*
* clutter_shader_effect_set_uniform (effect, "components",
* G_TYPE_FLOAT, 3,
* component_r,
* component_g,
* component_b);
* ]|
*
* or can be set using:
*
* |[
* gfloat component_vec[3];
*
* clutter_shader_effect_set_uniform (effect, "components",
* CLUTTER_TYPE_SHADER_FLOAT, 3,
* component_vec);
* ]|
*
* Finally, a uniform named "map" and containing a matrix can be set using:
*
* |[
* clutter_shader_effect_set_uniform (effect, "map",
* CLUTTER_TYPE_SHADER_MATRIX, 1,
* cogl_matrix_get_array (&matrix));
* ]|
*
* Since: 1.4
*/
void
clutter_shader_effect_set_uniform (ClutterShaderEffect *effect,
const gchar *name,
GType gtype,
gsize n_values,
...)
{
va_list args;
g_return_if_fail (CLUTTER_IS_SHADER_EFFECT (effect));
g_return_if_fail (name != NULL);
g_return_if_fail (gtype != G_TYPE_INVALID);
g_return_if_fail (n_values > 0);
va_start (args, n_values);
clutter_shader_effect_set_uniform_valist (effect, name,
gtype,
n_values,
&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->program == COGL_INVALID_HANDLE)
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);
priv->source_set = TRUE;
return TRUE;
}