update to 1.10.4
[profile/ivi/clutter.git] / clutter / clutter-shader-effect.c
1 /*
2  * Clutter.
3  *
4  * An OpenGL based 'interactive canvas' library.
5  *
6  * Copyright (C) 2010  Intel Corporation.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
20  *
21  * Author:
22  *   Emmanuele Bassi <ebassi@linux.intel.com>
23  */
24
25 /**
26  * SECTION:clutter-shader-effect
27  * @short_description: Base class for shader effects
28  * @See_Also: #ClutterEffect, #ClutterOffscreenEffect
29  *
30  * #ClutterShaderEffect is a class that implements all the plumbing for
31  * creating #ClutterEffect<!-- -->s using GLSL shaders.
32  *
33  * #ClutterShaderEffect creates an offscreen buffer and then applies the
34  * GLSL shader (after checking whether the compilation and linking were
35  * successfull) to the buffer before painting it on screen.
36  *
37  * <refsect2 id="ClutterShaderEffect-implementing">
38  *   <title>Implementing a ClutterShaderEffect</title>
39  *   <para>Creating a sub-class of #ClutterShaderEffect requires the
40  *   overriding of the <function>paint_target()</function> virtual
41  *   function from the #ClutterOffscreenEffect class as well as the
42  *   <function>get_static_shader_source()</function> virtual from the
43  *   #ClutterShaderEffect class.</para>
44  *   <para>The <function>get_static_shader_source()</function>
45  *   function should return a copy of the shader source to use. This
46  *   function is only called once per subclass of #ClutterShaderEffect
47  *   regardless of how many instances of the effect are created. The
48  *   source for the shader is typically stored in a static const
49  *   string which is returned from this function via
50  *   g_strdup().</para>
51  *   <para>The <function>paint_target()</function> should set the
52  *   shader's uniforms if any. This is done by calling
53  *   clutter_shader_effect_set_uniform_value() or
54  *   clutter_shader_effect_set_uniform(). The sub-class should then
55  *   chain up to the #ClutterShaderEffect implementation.</para>
56  *   <example id="ClutterShaderEffect-example-uniforms">
57  *     <title>Setting uniforms on a ClutterShaderEffect</title>
58  *     <para>The example below shows a typical implementation of the
59  *     <function>get_static_shader_source()</function> and
60  *     <function>paint_target()</function> phases of a
61  *     #ClutterShaderEffect sub-class.</para>
62  *     <programlisting>
63  *  static gchar *
64  *  my_effect_get_static_shader_source (ClutterShaderEffect *effect)
65  *  {
66  *    return g_strdup (shader_source);
67  *  }
68  *
69  *  static gboolean
70  *  my_effect_paint_target (ClutterOffscreenEffect *effect)
71  *  {
72  *    MyEffect *self = MY_EFFECT (effect);
73  *    ClutterShaderEffect *shader = CLUTTER_SHADER_EFFECT (effect);
74  *    ClutterEffectClass *parent_class;
75  *    gfloat component_r, component_g, component_b;
76  *
77  *    /&ast; the "tex" uniform is declared in the shader as:
78  *     &ast;
79  *     &ast;   uniform int tex;
80  *     &ast;
81  *     &ast; and it is passed a constant value of 0
82  *     &ast;/
83  *    clutter_shader_effect_set_uniform (shader, "tex", G_TYPE_INT, 1, 0);
84  *
85  *    /&ast; the "component" uniform is declared in the shader as:
86  *     &ast;
87  *     &ast;   uniform vec3 component;
88  *     &ast;
89  *     &ast; and it's defined to contain the normalized components
90  *     &ast; of a #ClutterColor
91  *     &ast;/
92  *    component_r = self->color.red   / 255.0f;
93  *    component_g = self->color.green / 255.0f;
94  *    component_b = self->color.blue  / 255.0f;
95  *    clutter_shader_effect_set_uniform (shader, "component",
96  *                                       G_TYPE_FLOAT, 3,
97  *                                       component_r,
98  *                                       component_g,
99  *                                       component_b);
100  *
101  *    /&ast; chain up to the parent's implementation &ast;/
102  *    parent_class = CLUTTER_OFFSCREEN_EFFECT_CLASS (my_effect_parent_class);
103  *    return parent_class->paint_target (effect);
104  *  }
105  *     </programlisting>
106  *   </example>
107  * </refsect2>
108  *
109  * #ClutterShaderEffect is available since Clutter 1.4
110  */
111
112 #ifdef HAVE_CONFIG_H
113 #include "config.h"
114 #endif
115
116 /* XXX: This file depends on the cogl_program_ api with has been
117  * removed for Cogl 2.0 so we undef COGL_ENABLE_EXPERIMENTAL_2_0_API
118  * for this file for now */
119 #undef COGL_ENABLE_EXPERIMENTAL_2_0_API
120 #include "cogl/cogl.h"
121
122 #include "clutter-shader-effect.h"
123
124 #include "clutter-debug.h"
125 #include "clutter-enum-types.h"
126 #include "clutter-feature.h"
127 #include "clutter-private.h"
128 #include "clutter-shader-types.h"
129
130 typedef struct _ShaderUniform
131 {
132   gchar *name;
133   GType type;
134   GValue value;
135   GLint location;
136 } ShaderUniform;
137
138 struct _ClutterShaderEffectPrivate
139 {
140   ClutterActor *actor;
141
142   ClutterShaderType shader_type;
143
144   CoglHandle program;
145   CoglHandle shader;
146
147   GHashTable *uniforms;
148 };
149
150 typedef struct _ClutterShaderEffectClassPrivate
151 {
152   /* These are the per-class pre-compiled shader and program which is
153      used when the class implements get_static_shader_source without
154      calling set_shader_source. They will be shared by all instances
155      of this class */
156   CoglHandle program;
157   CoglHandle shader;
158 } ClutterShaderEffectClassPrivate;
159
160 enum
161 {
162   PROP_0,
163
164   PROP_SHADER_TYPE,
165
166   PROP_LAST
167 };
168
169 static GParamSpec *obj_props[PROP_LAST];
170
171 G_DEFINE_TYPE_WITH_CODE (ClutterShaderEffect,
172                          clutter_shader_effect,
173                          CLUTTER_TYPE_OFFSCREEN_EFFECT,
174                          g_type_add_class_private (g_define_type_id,
175                                                    sizeof (ClutterShaderEffectClassPrivate)))
176
177 static inline void
178 clutter_shader_effect_clear (ClutterShaderEffect *self,
179                              gboolean             reset_uniforms)
180 {
181   ClutterShaderEffectPrivate *priv = self->priv;
182
183   if (priv->shader != COGL_INVALID_HANDLE)
184     {
185       cogl_handle_unref (priv->shader);
186
187       priv->shader = COGL_INVALID_HANDLE;
188     }
189
190   if (priv->program != COGL_INVALID_HANDLE)
191     {
192       cogl_handle_unref (priv->program);
193
194       priv->program = COGL_INVALID_HANDLE;
195     }
196
197   if (reset_uniforms && priv->uniforms != NULL)
198     {
199       g_hash_table_destroy (priv->uniforms);
200       priv->uniforms = NULL;
201     }
202
203   priv->actor = NULL;
204 }
205
206 static void
207 clutter_shader_effect_update_uniforms (ClutterShaderEffect *effect)
208 {
209   ClutterShaderEffectPrivate *priv = effect->priv;
210   GHashTableIter iter;
211   gpointer key, value;
212   gsize size;
213
214   if (priv->program == COGL_INVALID_HANDLE)
215     return;
216
217   if (priv->uniforms == NULL)
218     return;
219
220   key = value = NULL;
221   g_hash_table_iter_init (&iter, priv->uniforms);
222   while (g_hash_table_iter_next (&iter, &key, &value))
223     {
224       ShaderUniform *uniform = value;
225
226       if (uniform->location == -1)
227         uniform->location = cogl_program_get_uniform_location (priv->program,
228                                                                uniform->name);
229
230       if (CLUTTER_VALUE_HOLDS_SHADER_FLOAT (&uniform->value))
231         {
232           const GLfloat *floats;
233
234           floats = clutter_value_get_shader_float (&uniform->value, &size);
235           cogl_program_set_uniform_float (priv->program, uniform->location,
236                                           size, 1,
237                                           floats);
238         }
239       else if (CLUTTER_VALUE_HOLDS_SHADER_INT (&uniform->value))
240         {
241           const GLint *ints;
242
243           ints = clutter_value_get_shader_int (&uniform->value, &size);
244           cogl_program_set_uniform_int (priv->program, uniform->location,
245                                         size, 1,
246                                         ints);
247         }
248       else if (CLUTTER_VALUE_HOLDS_SHADER_MATRIX (&uniform->value))
249         {
250           const GLfloat *matrix;
251
252           matrix = clutter_value_get_shader_matrix (&uniform->value, &size);
253           cogl_program_set_uniform_matrix (priv->program, uniform->location,
254                                            size, 1,
255                                            FALSE,
256                                            matrix);
257         }
258       else if (G_VALUE_HOLDS_FLOAT (&uniform->value))
259         {
260           const GLfloat float_val = g_value_get_float (&uniform->value);
261
262           cogl_program_set_uniform_float (priv->program, uniform->location,
263                                           1, 1,
264                                           &float_val);
265         }
266       else if (G_VALUE_HOLDS_DOUBLE (&uniform->value))
267         {
268           const GLfloat float_val =
269             (GLfloat) g_value_get_double (&uniform->value);
270
271           cogl_program_set_uniform_float (priv->program, uniform->location,
272                                           1, 1,
273                                           &float_val);
274         }
275       else if (G_VALUE_HOLDS_INT (&uniform->value))
276         {
277           const GLint int_val = g_value_get_int (&uniform->value);
278
279           cogl_program_set_uniform_int (priv->program, uniform->location,
280                                         1, 1,
281                                         &int_val);
282         }
283       else
284         g_warning ("Invalid uniform of type '%s' for name '%s'",
285                    g_type_name (G_VALUE_TYPE (&uniform->value)),
286                    uniform->name);
287     }
288 }
289
290 static void
291 clutter_shader_effect_set_actor (ClutterActorMeta *meta,
292                                  ClutterActor     *actor)
293 {
294   ClutterShaderEffect *self = CLUTTER_SHADER_EFFECT (meta);
295   ClutterShaderEffectPrivate *priv = self->priv;
296   ClutterActorMetaClass *parent;
297
298   if (!clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL))
299     {
300       /* if we don't have support for GLSL shaders then we
301        * forcibly disable the ActorMeta
302        */
303       g_warning ("Unable to use the ShaderEffect: the graphics hardware "
304                  "or the current GL driver does not implement support "
305                  "for the GLSL shading language.");
306       clutter_actor_meta_set_enabled (meta, FALSE);
307       return;
308     }
309
310   parent = CLUTTER_ACTOR_META_CLASS (clutter_shader_effect_parent_class);
311   parent->set_actor (meta, actor);
312
313   /* we keep a back pointer here */
314   priv->actor = clutter_actor_meta_get_actor (meta);
315   if (priv->actor == NULL)
316     return;
317
318   CLUTTER_NOTE (SHADER, "Preparing shader effect of type '%s'",
319                 G_OBJECT_TYPE_NAME (meta));
320 }
321
322 static CoglHandle
323 clutter_shader_effect_create_shader (ClutterShaderEffect *self)
324 {
325   ClutterShaderEffectPrivate *priv = self->priv;
326
327   switch (priv->shader_type)
328     {
329     case CLUTTER_FRAGMENT_SHADER:
330       return cogl_create_shader (COGL_SHADER_TYPE_FRAGMENT);
331       break;
332
333     case CLUTTER_VERTEX_SHADER:
334       return cogl_create_shader (COGL_SHADER_TYPE_VERTEX);
335       break;
336
337     default:
338       g_assert_not_reached ();
339     }
340 }
341
342 static void
343 clutter_shader_effect_try_static_source (ClutterShaderEffect *self)
344 {
345   ClutterShaderEffectPrivate *priv = self->priv;
346   ClutterShaderEffectClass *shader_effect_class =
347     CLUTTER_SHADER_EFFECT_GET_CLASS (self);
348
349   if (shader_effect_class->get_static_shader_source != NULL)
350     {
351       ClutterShaderEffectClassPrivate *class_priv;
352
353       class_priv =
354         G_TYPE_CLASS_GET_PRIVATE (shader_effect_class,
355                                   CLUTTER_TYPE_SHADER_EFFECT,
356                                   ClutterShaderEffectClassPrivate);
357
358       if (class_priv->shader == COGL_INVALID_HANDLE)
359         {
360           gchar *source;
361
362           class_priv->shader = clutter_shader_effect_create_shader (self);
363
364           source = shader_effect_class->get_static_shader_source (self);
365
366           cogl_shader_source (class_priv->shader, source);
367
368           g_free (source);
369
370           CLUTTER_NOTE (SHADER, "Compiling shader effect");
371
372           cogl_shader_compile (class_priv->shader);
373
374           if (cogl_shader_is_compiled (class_priv->shader))
375             {
376               class_priv->program = cogl_create_program ();
377
378               cogl_program_attach_shader (class_priv->program,
379                                           class_priv->shader);
380
381               cogl_program_link (class_priv->program);
382             }
383           else
384             {
385               gchar *log_buf = cogl_shader_get_info_log (class_priv->shader);
386
387               g_warning (G_STRLOC ": Unable to compile the GLSL shader: %s", log_buf);
388               g_free (log_buf);
389             }
390         }
391
392       priv->shader = cogl_handle_ref (class_priv->shader);
393
394       if (class_priv->program != COGL_INVALID_HANDLE)
395         priv->program = cogl_handle_ref (class_priv->program);
396     }
397 }
398
399 static void
400 clutter_shader_effect_paint_target (ClutterOffscreenEffect *effect)
401 {
402   ClutterShaderEffect *self = CLUTTER_SHADER_EFFECT (effect);
403   ClutterShaderEffectPrivate *priv = self->priv;
404   ClutterOffscreenEffectClass *parent;
405   CoglHandle material;
406
407   /* If the source hasn't been set then we'll try to get it from the
408      static source instead */
409   if (priv->shader == COGL_INVALID_HANDLE)
410     clutter_shader_effect_try_static_source (self);
411
412   /* we haven't been prepared or we don't have support for
413    * GLSL shaders in Clutter
414    */
415   if (priv->program == COGL_INVALID_HANDLE)
416     goto out;
417
418   CLUTTER_NOTE (SHADER, "Applying the shader effect of type '%s'",
419                 G_OBJECT_TYPE_NAME (effect));
420
421   clutter_shader_effect_update_uniforms (CLUTTER_SHADER_EFFECT (effect));
422
423   /* associate the program to the offscreen target material */
424   material = clutter_offscreen_effect_get_target (effect);
425   cogl_pipeline_set_user_program (material, priv->program);
426
427 out:
428   /* paint the offscreen buffer */
429   parent = CLUTTER_OFFSCREEN_EFFECT_CLASS (clutter_shader_effect_parent_class);
430   parent->paint_target (effect);
431
432 }
433
434 static void
435 clutter_shader_effect_set_property (GObject      *gobject,
436                                     guint         prop_id,
437                                     const GValue *value,
438                                     GParamSpec   *pspec)
439 {
440   ClutterShaderEffectPrivate *priv = CLUTTER_SHADER_EFFECT (gobject)->priv;
441
442   switch (prop_id)
443     {
444     case PROP_SHADER_TYPE:
445       priv->shader_type = g_value_get_enum (value);
446       break;
447
448     default:
449       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
450       break;
451     }
452 }
453
454 static void
455 clutter_shader_effect_finalize (GObject *gobject)
456 {
457   ClutterShaderEffect *effect = CLUTTER_SHADER_EFFECT (gobject);
458
459   clutter_shader_effect_clear (effect, TRUE);
460
461   G_OBJECT_CLASS (clutter_shader_effect_parent_class)->finalize (gobject);
462 }
463
464 static void
465 clutter_shader_effect_class_init (ClutterShaderEffectClass *klass)
466 {
467   ClutterActorMetaClass *meta_class = CLUTTER_ACTOR_META_CLASS (klass);
468   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
469   ClutterOffscreenEffectClass *offscreen_class;
470
471   offscreen_class = CLUTTER_OFFSCREEN_EFFECT_CLASS (klass);
472
473   g_type_class_add_private (klass, sizeof (ClutterShaderEffectPrivate));
474
475   /**
476    * ClutterShaderEffect:shader-type:
477    *
478    * The type of shader that is used by the effect. This property
479    * should be set by the constructor of #ClutterShaderEffect
480    * sub-classes.
481    *
482    * Since: 1.4
483    */
484   obj_props[PROP_SHADER_TYPE] =
485     g_param_spec_enum ("shader-type",
486                        P_("Shader Type"),
487                        P_("The type of shader used"),
488                        CLUTTER_TYPE_SHADER_TYPE,
489                        CLUTTER_FRAGMENT_SHADER,
490                        CLUTTER_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
491
492   gobject_class->set_property = clutter_shader_effect_set_property;
493   gobject_class->finalize = clutter_shader_effect_finalize;
494   g_object_class_install_properties (gobject_class,
495                                      PROP_LAST,
496                                      obj_props);
497
498   meta_class->set_actor = clutter_shader_effect_set_actor;
499
500   offscreen_class->paint_target = clutter_shader_effect_paint_target;
501 }
502
503 static void
504 clutter_shader_effect_init (ClutterShaderEffect *effect)
505 {
506   effect->priv = G_TYPE_INSTANCE_GET_PRIVATE (effect,
507                                               CLUTTER_TYPE_SHADER_EFFECT,
508                                               ClutterShaderEffectPrivate);
509 }
510
511 /**
512  * clutter_shader_effect_new:
513  * @shader_type: the type of the shader, either %CLUTTER_FRAGMENT_SHADER,
514  *   or %CLUTTER_VERTEX_SHADER
515  *
516  * Creates a new #ClutterShaderEffect, to be applied to an actor using
517  * clutter_actor_add_effect().
518  *
519  * The effect will be empty until clutter_shader_effect_set_shader_source()
520  * is called.
521  *
522  * Return value: (transfer full): the newly created #ClutterShaderEffect.
523  *   Use g_object_unref() when done.
524  *
525  * Since: 1.8
526  */
527 ClutterEffect *
528 clutter_shader_effect_new (ClutterShaderType shader_type)
529 {
530   return g_object_new (CLUTTER_TYPE_SHADER_EFFECT,
531                        "shader-type", shader_type,
532                        NULL);
533 }
534
535 /**
536  * clutter_shader_effect_get_shader:
537  * @effect: a #ClutterShaderEffect
538  *
539  * Retrieves a pointer to the shader's handle
540  *
541  * Return value: (transfer none): a pointer to the shader's handle,
542  *   or %COGL_INVALID_HANDLE
543  *
544  * Since: 1.4
545  */
546 CoglHandle
547 clutter_shader_effect_get_shader (ClutterShaderEffect *effect)
548 {
549   g_return_val_if_fail (CLUTTER_IS_SHADER_EFFECT (effect),
550                         COGL_INVALID_HANDLE);
551
552   return effect->priv->shader;
553 }
554
555 /**
556  * clutter_shader_effect_get_program:
557  * @effect: a #ClutterShaderEffect
558  *
559  * Retrieves a pointer to the program's handle
560  *
561  * Return value: (transfer none): a pointer to the program's handle,
562  *   or %COGL_INVALID_HANDLE
563  *
564  * Since: 1.4
565  */
566 CoglHandle
567 clutter_shader_effect_get_program (ClutterShaderEffect *effect)
568 {
569   g_return_val_if_fail (CLUTTER_IS_SHADER_EFFECT (effect),
570                         COGL_INVALID_HANDLE);
571
572   return effect->priv->program;
573 }
574
575 static void
576 shader_uniform_free (gpointer data)
577 {
578   if (data != NULL)
579     {
580       ShaderUniform *uniform = data;
581
582       g_value_unset (&uniform->value);
583       g_free (uniform->name);
584
585       g_slice_free (ShaderUniform, uniform);
586     }
587 }
588
589 static ShaderUniform *
590 shader_uniform_new (const gchar  *name,
591                     const GValue *value)
592 {
593   ShaderUniform *retval;
594
595   retval = g_slice_new0 (ShaderUniform);
596   retval->name = g_strdup (name);
597   retval->type = G_VALUE_TYPE (value);
598   retval->location = -1;
599
600   g_value_init (&retval->value, retval->type);
601   g_value_copy (value, &retval->value);
602
603   return retval;
604 }
605
606 static void
607 shader_uniform_update (ShaderUniform *uniform,
608                        const GValue  *value)
609 {
610   g_value_unset (&uniform->value);
611
612   g_value_init (&uniform->value, G_VALUE_TYPE (value));
613   g_value_copy (value, &uniform->value);
614 }
615
616 static inline void
617 clutter_shader_effect_add_uniform (ClutterShaderEffect *effect,
618                                    const gchar         *name,
619                                    const GValue        *value)
620 {
621   ClutterShaderEffectPrivate *priv = effect->priv;
622   ShaderUniform *uniform;
623
624   if (priv->uniforms == NULL)
625     {
626       priv->uniforms = g_hash_table_new_full (g_str_hash, g_str_equal,
627                                               NULL,
628                                               shader_uniform_free);
629     }
630
631   uniform = g_hash_table_lookup (priv->uniforms, name);
632   if (uniform == NULL)
633     {
634       uniform = shader_uniform_new (name, value);
635       g_hash_table_insert (priv->uniforms, uniform->name, uniform);
636     }
637   else
638     shader_uniform_update (uniform, value);
639
640   if (priv->actor != NULL && !CLUTTER_ACTOR_IN_PAINT (priv->actor))
641     clutter_effect_queue_repaint (CLUTTER_EFFECT (effect));
642 }
643
644 /**
645  * clutter_shader_effect_set_uniform_value:
646  * @effect: a #ClutterShaderEffect
647  * @name: the name of the uniform to set
648  * @value: a #GValue with the value of the uniform to set
649  *
650  * Sets @value as the payload for the uniform @name inside the shader
651  * effect
652  *
653  * The #GType of the @value must be one of: %G_TYPE_INT, for a single
654  * integer value; %G_TYPE_FLOAT, for a single floating point value;
655  * %CLUTTER_TYPE_SHADER_INT, for an array of integer values;
656  * %CLUTTER_TYPE_SHADER_FLOAT, for an array of floating point values;
657  * and %CLUTTER_TYPE_SHADER_MATRIX, for a matrix of floating point
658  * values. It also accepts %G_TYPE_DOUBLE for compatibility with other
659  * languages than C.
660  *
661  * Since: 1.4
662  */
663 void
664 clutter_shader_effect_set_uniform_value (ClutterShaderEffect *effect,
665                                          const gchar         *name,
666                                          const GValue        *value)
667 {
668   g_return_if_fail (CLUTTER_IS_SHADER_EFFECT (effect));
669   g_return_if_fail (name != NULL);
670   g_return_if_fail (value != NULL);
671
672   clutter_shader_effect_add_uniform (effect, name, value);
673 }
674
675 static void
676 clutter_shader_effect_set_uniform_valist (ClutterShaderEffect *effect,
677                                           const gchar         *name,
678                                           GType                value_type,
679                                           gsize                n_values,
680                                           va_list             *args)
681 {
682   GValue value = G_VALUE_INIT;
683
684   if (value_type == CLUTTER_TYPE_SHADER_INT)
685     {
686       gint *int_values = va_arg (*args, gint*);
687
688       g_value_init (&value, CLUTTER_TYPE_SHADER_INT);
689       clutter_value_set_shader_int (&value, n_values, int_values);
690
691       goto add_uniform;
692     }
693
694   if (value_type == CLUTTER_TYPE_SHADER_FLOAT)
695     {
696       gfloat *float_values = va_arg (*args, gfloat*);
697
698       g_value_init (&value, CLUTTER_TYPE_SHADER_FLOAT);
699       clutter_value_set_shader_float (&value, n_values, float_values);
700
701       goto add_uniform;
702     }
703
704   if (value_type == CLUTTER_TYPE_SHADER_MATRIX)
705     {
706       gfloat *float_values = va_arg (*args, gfloat*);
707
708       g_value_init (&value, CLUTTER_TYPE_SHADER_MATRIX);
709       clutter_value_set_shader_matrix (&value, n_values, float_values);
710
711       goto add_uniform;
712     }
713
714   if (value_type == G_TYPE_INT)
715     {
716       g_return_if_fail (n_values <= 4);
717
718       /* if we only have one value we can go through the fast path
719        * of using G_TYPE_INT, otherwise we create a vector of integers
720        * from the passed values
721        */
722       if (n_values == 1)
723         {
724           gint int_val = va_arg (*args, gint);
725
726           g_value_init (&value, G_TYPE_INT);
727           g_value_set_int (&value, int_val);
728         }
729       else
730         {
731           gint *int_values = g_new (gint, n_values);
732           gint i;
733
734           for (i = 0; i < n_values; i++)
735             int_values[i] = va_arg (*args, gint);
736
737           g_value_init (&value, CLUTTER_TYPE_SHADER_INT);
738           clutter_value_set_shader_int (&value, n_values, int_values);
739
740           g_free (int_values);
741         }
742
743       goto add_uniform;
744     }
745
746   if (value_type == G_TYPE_FLOAT)
747     {
748       g_return_if_fail (n_values <= 4);
749
750       /* if we only have one value we can go through the fast path
751        * of using G_TYPE_FLOAT, otherwise we create a vector of floats
752        * from the passed values
753        */
754       if (n_values == 1)
755         {
756           gfloat float_val = (gfloat) va_arg (*args, gdouble);
757
758           g_value_init (&value, G_TYPE_FLOAT);
759           g_value_set_float (&value, float_val);
760         }
761       else
762         {
763           gfloat *float_values = g_new (gfloat, n_values);
764           gint i;
765
766           for (i = 0; i < n_values; i++)
767             float_values[i] = (gfloat) va_arg (*args, double);
768
769           g_value_init (&value, CLUTTER_TYPE_SHADER_FLOAT);
770           clutter_value_set_shader_float (&value, n_values, float_values);
771
772           g_free (float_values);
773         }
774
775       goto add_uniform;
776     }
777
778   g_warning ("Unrecognized type '%s' (values: %d) for uniform name '%s'",
779              g_type_name (value_type),
780              (int) n_values,
781              name);
782   return;
783
784 add_uniform:
785   clutter_shader_effect_add_uniform (effect, name, &value);
786   g_value_unset (&value);
787 }
788
789 /**
790  * clutter_shader_effect_set_uniform:
791  * @effect: a #ClutterShaderEffect
792  * @name: the name of the uniform to set
793  * @gtype: the type of the uniform to set
794  * @n_values: the number of values
795  * @...: a list of values
796  *
797  * Sets a list of values as the payload for the uniform @name inside
798  * the shader effect
799  *
800  * The @gtype must be one of: %G_TYPE_INT, for 1 or more integer values;
801  * %G_TYPE_FLOAT, for 1 or more floating point values;
802  * %CLUTTER_TYPE_SHADER_INT, for a pointer to an array of integer values;
803  * %CLUTTER_TYPE_SHADER_FLOAT, for a pointer to an array of floating point
804  * values; and %CLUTTER_TYPE_SHADER_MATRIX, for a pointer to an array of
805  * floating point values mapping a matrix
806  *
807  * The number of values interepreted is defined by the @n_value
808  * argument, and by the @gtype argument. For instance, a uniform named
809  * "sampler0" and containing a single integer value is set using:
810  *
811  * |[
812  *   clutter_shader_effect_set_uniform (effect, "sampler0",
813  *                                      G_TYPE_INT, 1,
814  *                                      0);
815  * ]|
816  *
817  * While a uniform named "components" and containing a 3-elements vector
818  * of floating point values (a "vec3") can be set using:
819  *
820  * |[
821  *   gfloat component_r, component_g, component_b;
822  *
823  *   clutter_shader_effect_set_uniform (effect, "components",
824  *                                      G_TYPE_FLOAT, 3,
825  *                                      component_r,
826  *                                      component_g,
827  *                                      component_b);
828  * ]|
829  *
830  * or can be set using:
831  *
832  * |[
833  *   gfloat component_vec[3];
834  *
835  *   clutter_shader_effect_set_uniform (effect, "components",
836  *                                      CLUTTER_TYPE_SHADER_FLOAT, 3,
837  *                                      component_vec);
838  * ]|
839  *
840  * Finally, a uniform named "map" and containing a matrix can be set using:
841  *
842  * |[
843  *   clutter_shader_effect_set_uniform (effect, "map",
844  *                                      CLUTTER_TYPE_SHADER_MATRIX, 1,
845  *                                      cogl_matrix_get_array (&matrix));
846  * ]|
847  *
848  * Since: 1.4
849  */
850 void
851 clutter_shader_effect_set_uniform (ClutterShaderEffect *effect,
852                                    const gchar         *name,
853                                    GType                gtype,
854                                    gsize                n_values,
855                                    ...)
856 {
857   va_list args;
858
859   g_return_if_fail (CLUTTER_IS_SHADER_EFFECT (effect));
860   g_return_if_fail (name != NULL);
861   g_return_if_fail (gtype != G_TYPE_INVALID);
862   g_return_if_fail (n_values > 0);
863
864   va_start (args, n_values);
865   clutter_shader_effect_set_uniform_valist (effect, name,
866                                             gtype,
867                                             n_values,
868                                             &args);
869   va_end (args);
870 }
871
872 /**
873  * clutter_shader_effect_set_shader_source:
874  * @effect: a #ClutterShaderEffect
875  * @source: the source of a GLSL shader
876  *
877  * Sets the source of the GLSL shader used by @effect
878  *
879  * This function should only be called by implementations of
880  * the #ClutterShaderEffect class, and not by application code.
881  *
882  * This function can only be called once; subsequent calls will
883  * yield no result.
884  *
885  * Return value: %TRUE if the source was set
886  *
887  * Since: 1.4
888  */
889 gboolean
890 clutter_shader_effect_set_shader_source (ClutterShaderEffect *effect,
891                                          const gchar         *source)
892 {
893   ClutterShaderEffectPrivate *priv;
894
895   g_return_val_if_fail (CLUTTER_IS_SHADER_EFFECT (effect), FALSE);
896   g_return_val_if_fail (source != NULL && *source != '\0', FALSE);
897
898   priv = effect->priv;
899
900   if (priv->shader != COGL_INVALID_HANDLE)
901     return TRUE;
902
903   priv->shader = clutter_shader_effect_create_shader (effect);
904
905   cogl_shader_source (priv->shader, source);
906
907   CLUTTER_NOTE (SHADER, "Compiling shader effect");
908
909   cogl_shader_compile (priv->shader);
910
911   if (cogl_shader_is_compiled (priv->shader))
912     {
913       priv->program = cogl_create_program ();
914
915       cogl_program_attach_shader (priv->program, priv->shader);
916
917       cogl_program_link (priv->program);
918     }
919   else
920     {
921       gchar *log_buf = cogl_shader_get_info_log (priv->shader);
922
923       g_warning (G_STRLOC ": Unable to compile the GLSL shader: %s", log_buf);
924       g_free (log_buf);
925     }
926
927   return TRUE;
928 }