Support for shaders in clutter. At the moment limited to drivers
authorØyvind Kolås <pippin@openedhand.com>
Mon, 3 Dec 2007 16:29:18 +0000 (16:29 +0000)
committerØyvind Kolås <pippin@openedhand.com>
Mon, 3 Dec 2007 16:29:18 +0000 (16:29 +0000)
providing GLSL support.
* clutter/cogl/cogl.h: added rather direct binding of needed for glsl
shaders.
* clutter/cogl/gl/cogl-defines.h.in:
* clutter/cogl/gl/cogl.c:
* clutter/cogl/gles/cogl-defines.h: added stubs.
* clutter/cogl/gles/cogl.c: added stubs.
* clutter/glx/clutter-stage-glx.c:
(clutter_stage_glx_realize): unrelated memory management sanity fix.
(clutter_stage_glx_unrealize): unbind all shaders on stage unrealize.
* clutter/Makefile.am: added clutter-shader.[ch]
* clutter/clutter-actor.[ch]: adding shader capability to
actors.
* clutter/clutter-feature.h: added CLUTTER_FEATURE_SHADERS_GLSL
* clutter/clutter-private.h: added stack of shaders to context.
* clutter/clutter-shader.[ch]: new.
* tests/Makefile.am: added shader test.
* tests/test-shader.c: (frame_cb), (main): simple shader test,
cycle through the inline shader with right/left mouse buttons.

16 files changed:
ChangeLog
clutter/Makefile.am
clutter/clutter-actor.c
clutter/clutter-actor.h
clutter/clutter-feature.h
clutter/clutter-private.h
clutter/clutter-shader.c [new file with mode: 0644]
clutter/clutter-shader.h [new file with mode: 0644]
clutter/cogl/cogl.h
clutter/cogl/gl/cogl-defines.h.in
clutter/cogl/gl/cogl.c
clutter/cogl/gles/cogl-defines.h
clutter/cogl/gles/cogl.c
clutter/glx/clutter-stage-glx.c
tests/Makefile.am
tests/test-shader.c [new file with mode: 0644]

index 215c9c7..dc54ec2 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,32 @@
 2007-12-03  Øyvind Kolås  <pippin@o-hand.com>
 
+       Support for shaders in clutter. At the moment limited to drivers
+       providing GLSL support.
+
+       * clutter/cogl/cogl.h: added rather direct binding of needed for glsl
+       shaders.
+       * clutter/cogl/gl/cogl-defines.h.in:
+       * clutter/cogl/gl/cogl.c:
+       * clutter/cogl/gles/cogl-defines.h: added stubs.
+       * clutter/cogl/gles/cogl.c: added stubs.
+
+       * clutter/glx/clutter-stage-glx.c:
+       (clutter_stage_glx_realize): unrelated memory management sanity fix.
+       (clutter_stage_glx_unrealize): unbind all shaders on stage unrealize.
+
+       * clutter/Makefile.am: added clutter-shader.[ch]
+       * clutter/clutter-actor.[ch]: adding shader capability to 
+       actors.
+       * clutter/clutter-feature.h: added CLUTTER_FEATURE_SHADERS_GLSL
+       * clutter/clutter-private.h: added stack of shaders to context.
+       * clutter/clutter-shader.[ch]: new.
+
+       * tests/Makefile.am: added shader test.
+       * tests/test-shader.c: (frame_cb), (main): simple shader test,
+       cycle through the inline shader with right/left mouse buttons.
+
+2007-12-03  Øyvind Kolås  <pippin@o-hand.com>
+
        * clutter/glx/clutter-stage-glx.c: for extra sanity, set
        stage_x11->xvisinfo = NULL after XFree. Makes using the root window
        as foreign window possible.
index f260c1d..bc1c566 100644 (file)
@@ -69,6 +69,7 @@ source_h =                                    \
        $(srcdir)/clutter-score.h               \
        $(srcdir)/clutter-script.h              \
        $(srcdir)/clutter-scriptable.h          \
+       $(srcdir)/clutter-shader.h              \       
        $(srcdir)/clutter-stage.h               \
        $(srcdir)/clutter-texture.h             \
        $(srcdir)/clutter-timeline.h            \
@@ -153,6 +154,7 @@ source_c = \
        clutter-script.c                \
        clutter-script-parser.c         \
        clutter-scriptable.c            \
+       clutter-shader.c                \       
        clutter-stage.c                 \
        clutter-texture.c               \
        clutter-timeline.c              \
index 2a467ee..8e48d65 100644 (file)
@@ -156,6 +156,8 @@ static guint32 __id = 0;
 #define CLUTTER_ACTOR_GET_PRIVATE(obj) \
 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_ACTOR, ClutterActorPrivate))
 
+typedef struct _ShaderData ShaderData;
+
 struct _ClutterActorPrivate
 {
   ClutterActorBox coords;
@@ -170,6 +172,8 @@ struct _ClutterActorPrivate
   gchar          *name;
   ClutterFixed    scale_x, scale_y;
   guint32         id; /* Unique ID */
+
+  ShaderData     *shader_data;
   ClutterUnit     anchor_x, anchor_y;
 };
 
@@ -221,6 +225,11 @@ static void clutter_scriptable_iface_init (ClutterScriptableIface *iface);
 static void _clutter_actor_apply_modelview_transform           (ClutterActor *self);
 static void _clutter_actor_apply_modelview_transform_recursive (ClutterActor *self);
 
+static void clutter_actor_shader_pre_paint (ClutterActor *actor,
+                                            gboolean      repeat);
+static void clutter_actor_shader_post_paint (ClutterActor *actor);
+static void destroy_shader_data (ClutterActor *self);
+
 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (ClutterActor,
                                   clutter_actor,
                                   G_TYPE_INITIALLY_UNOWNED,
@@ -907,8 +916,10 @@ clutter_actor_paint (ClutterActor *self)
     }
   else
     {
+      clutter_actor_shader_pre_paint (self, FALSE);
       if (G_LIKELY(klass->paint))
        (klass->paint) (self);
+      clutter_actor_shader_post_paint (self);
     }
 
   if (priv->has_clip)
@@ -1175,6 +1186,8 @@ clutter_actor_dispose (GObject *object)
                g_type_name (G_OBJECT_TYPE (self)),
                 object->ref_count);
 
+  destroy_shader_data (self);
+
  if (!(CLUTTER_PRIVATE_FLAGS (self) & CLUTTER_ACTOR_IN_DESTRUCTION))
     {
       CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_ACTOR_IN_DESTRUCTION);
@@ -1730,6 +1743,7 @@ clutter_actor_init (ClutterActor *self)
   priv->id           = __id++;
   priv->scale_x      = CFX_ONE;
   priv->scale_y      = CFX_ONE;
+  priv->shader_data     = NULL;
 
   clutter_actor_request_coords (self, &box);
 }
@@ -4036,3 +4050,175 @@ clutter_actor_box_get_type (void)
                                     (GBoxedFreeFunc) clutter_actor_box_free);
   return our_type;
 }
+
+/******************************************************************************/
+
+typedef struct _BoxedFloat BoxedFloat;
+struct _BoxedFloat
+{
+  gfloat value;
+};
+
+struct _ShaderData
+{
+  ClutterShader *shader;
+  GHashTable    *float1f_hash; /*< list of values that should be set
+                                *  on the shader before each paint cycle
+                                */
+};
+
+static void
+destroy_shader_data (ClutterActor *self)
+{
+  ClutterActorPrivate *actor_priv = self->priv;
+  ShaderData          *shader_data   = actor_priv->shader_data;
+
+  if (!shader_data)
+    return;
+
+  if (shader_data->shader)
+    {
+      g_object_unref (shader_data->shader);
+    }
+  shader_data->shader = NULL;
+  if (shader_data->float1f_hash)
+    {
+      g_hash_table_destroy (shader_data->float1f_hash);
+      shader_data->float1f_hash = NULL;
+    }
+  g_free (shader_data);
+  actor_priv->shader_data = NULL;
+}
+
+gboolean clutter_actor_apply_shader (ClutterActor  *self,
+                                     ClutterShader *shader)
+{
+  ClutterActorPrivate *actor_priv;
+  ShaderData     *shader_data;
+
+  g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
+  actor_priv = self->priv;
+  shader_data = actor_priv->shader_data;
+
+  if (shader_data == NULL)
+    {
+      actor_priv->shader_data = shader_data = g_new0 (ShaderData, 1);
+      shader_data->float1f_hash = g_hash_table_new_full (
+                                        g_str_hash, g_str_equal,
+                                        g_free, g_free);
+    }
+  if (shader_data->shader)
+    {
+      g_object_unref (shader_data->shader);
+    }
+  if (shader)
+    {
+      shader_data->shader = g_object_ref (shader);
+    }
+  else
+    {
+      shader_data->shader = NULL;
+    }
+  return TRUE;
+}
+
+static void
+each_param (gpointer key,
+            gpointer value,
+            gpointer user_data)
+{
+  ClutterShader *shader = CLUTTER_SHADER (user_data);
+  BoxedFloat *box = value;
+  clutter_shader_set_uniform_1f (shader, key, box->value);
+}
+
+static void
+clutter_actor_shader_pre_paint (ClutterActor *actor,
+                                gboolean      repeat)
+{
+  ClutterActorPrivate *actor_priv;
+  ShaderData          *shader_data;
+  ClutterShader       *shader;
+  ClutterMainContext  *context;
+
+  g_return_if_fail (CLUTTER_IS_ACTOR (actor));
+  actor_priv = actor->priv;
+  shader_data = actor_priv->shader_data;
+
+  if (!shader_data)
+    return;
+
+  context = clutter_context_get_default ();
+  shader = shader_data->shader;
+
+  if (shader)
+    {
+      clutter_shader_enable (shader);
+
+      g_hash_table_foreach (shader_data->float1f_hash, each_param, shader);
+
+        if (!repeat)
+        {
+          context->shaders = g_slist_prepend (context->shaders, actor);
+        }
+    }
+}
+
+static void
+clutter_actor_shader_post_paint (ClutterActor *actor)
+{
+  ClutterActorPrivate *actor_priv;
+  ShaderData          *shader_data;
+  ClutterShader       *shader;
+  ClutterMainContext  *context;
+
+  g_return_if_fail (CLUTTER_IS_ACTOR (actor));
+  actor_priv = actor->priv;
+  shader_data = actor_priv->shader_data;
+
+  if (!shader_data)
+    return;
+
+  context = clutter_context_get_default ();
+  shader = shader_data->shader;
+
+  if (shader)
+    {
+      clutter_shader_disable (shader);
+
+      context->shaders = g_slist_remove (context->shaders, actor);
+      if (context->shaders)
+        {
+          /* call pre-paint again, this time with the second argument being
+           * TRUE, indicating that we are reapplying the shader and thus
+           * should not be prepended to the stack
+           */
+          clutter_actor_shader_pre_paint (context->shaders->data, TRUE);
+        }
+    }
+}
+
+void
+clutter_actor_set_shader_param (ClutterActor *actor,
+                                const gchar  *param,
+                                gfloat        value)
+{
+  ClutterActorPrivate *actor_priv;
+  ShaderData *shader_data;
+  BoxedFloat             *box;
+
+  g_return_if_fail (CLUTTER_IS_ACTOR (actor));
+  actor_priv = actor->priv;
+  shader_data = actor_priv->shader_data;
+
+  if (!shader_data)
+    return;
+
+  box        = g_malloc (sizeof (BoxedFloat));
+  box->value = value;
+  g_hash_table_insert (shader_data->float1f_hash, g_strdup (param), box);
+}
index a0b139f..96769b0 100644 (file)
@@ -34,6 +34,7 @@
 #include <clutter/clutter-types.h>
 #include <clutter/clutter-units.h>
 #include <clutter/clutter-event.h>
+#include <clutter/clutter-shader.h>
 
 G_BEGIN_DECLS
 
@@ -216,9 +217,10 @@ struct _ClutterActorClass
   void     (* focus_in)             (ClutterActor         *actor);
   void     (* focus_out)            (ClutterActor         *actor);
 
+  gboolean shadable;
   /*< private >*/
   /* padding for future expansion */
-  gpointer _padding_dummy[32];
+  gpointer _padding_dummy[31];
 };
 
 GType                 clutter_actor_get_type         (void) G_GNUC_CONST;
@@ -390,6 +392,14 @@ ClutterActor *        clutter_get_actor_by_gid     (guint32       id);
 
 gboolean              clutter_actor_should_pick_paint (ClutterActor *self);
 
+gboolean              clutter_actor_apply_shader (ClutterActor  *self,
+                                                  ClutterShader *shader);
+
+void                  clutter_actor_set_shader_param (ClutterActor *self,
+                                                      const gchar  *param,
+                                                      gfloat        value);
+
+
 void                  clutter_actor_set_anchor_point  (ClutterActor          *self,
                                                       gint                   anchor_x,
                                                        gint                   anchor_y);
index 9820e51..d9d225e 100644 (file)
@@ -46,6 +46,7 @@ G_BEGIN_DECLS
  * @CLUTTER_FEATURE_STAGE_STATIC: Set if stage size if fixed (i.e framebuffer)
  * @CLUTTER_FEATURE_STAGE_USER_RESIZE: Set if stage is able to be user resized.
  * @CLUTTER_FEATURE_STAGE_CURSOR: Set if stage has a graphical cursor.
+ * @CLUTTER_FEATURE_SHADER_GLSL: Set if the backend supports GLSL shaders.
  *
  * Runtime flags indicating specific features available via Clutter window
  * sysytem and graphics backend.
@@ -60,7 +61,8 @@ typedef enum
   CLUTTER_FEATURE_TEXTURE_READ_PIXELS    = (1 << 4),
   CLUTTER_FEATURE_STAGE_STATIC           = (1 << 5),
   CLUTTER_FEATURE_STAGE_USER_RESIZE      = (1 << 6),
-  CLUTTER_FEATURE_STAGE_CURSOR           = (1 << 7)
+  CLUTTER_FEATURE_STAGE_CURSOR           = (1 << 7),
+  CLUTTER_FEATURE_SHADERS_GLSL           = (1 << 8)
 } ClutterFeatureFlags;
 
 gboolean            clutter_feature_available       (ClutterFeatureFlags feature);
index 67738fb..d3ca933 100644 (file)
@@ -93,6 +93,7 @@ struct _ClutterMainContext
   ClutterActor    *keyboard_grab_actor; /* The actor having the pointer grab
                                           (or NULL if there is no pointer grab) 
                                         */
+  GSList          *shaders;            /* stack of overridden shaders */
 };
 
 #define CLUTTER_CONTEXT()      (clutter_context_get_default ())
diff --git a/clutter/clutter-shader.c b/clutter/clutter-shader.c
new file mode 100644 (file)
index 0000000..70eb814
--- /dev/null
@@ -0,0 +1,433 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Authored By Matthew Allum  <mallum@openedhand.com>
+ *             Øyvind Kolås   <pippin@o-hand.com>
+ *
+ * Copyright (C) 2007 OpenedHand
+ *
+ * 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "clutter.h"
+#include "clutter-private.h"
+#include "clutter-shader.h"
+
+#include <unistd.h>
+#include <glib.h>
+#include <cogl/cogl.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define CLUTTER_SHADER_GET_PRIVATE(obj) \
+  (G_TYPE_INSTANCE_GET_PRIVATE ((obj),  \
+   CLUTTER_TYPE_SHADER, ClutterShaderPrivate))
+
+static GList *shader_list;
+
+static void     clutter_shader_class_init   (ClutterShaderClass    *klass);
+static void     clutter_shader_init         (ClutterShader         *sp);
+static void     clutter_shader_finalize     (GObject               *object);
+static GObject *clutter_shader_constructor  (GType                  type,
+                                             guint                  n_params,
+                                             GObjectConstructParam *params);
+static void     clutter_shader_set_property (GObject               *object,
+                                             guint                  prop_id,
+                                             const GValue          *value,
+                                             GParamSpec            *pspec);
+static void     clutter_shader_get_property (GObject               *object,
+                                             guint                  prop_id,
+                                             GValue                *value,
+                                             GParamSpec            *pspec);
+
+struct _ClutterShaderPrivate
+{
+  gboolean     glsl;   /* The shader is a GLSL shader */
+  gboolean     bound;  /* The shader is bound to the GL context */
+
+  gchar       *vertex_shader_source;  /* source (or asm) for vertex shader  */
+  gchar       *fragment_shader_source;/* source (or asm) for fragment shader*/
+
+  GLhandleARB  program;
+
+  GLhandleARB  vertex_shader;
+  GLhandleARB  fragment_shader;
+};
+
+enum 
+{
+  PROP_0,
+  PROP_VERTEX_SOURCE,
+  PROP_FRAGMENT_SOURCE
+};
+
+G_DEFINE_TYPE (ClutterShader, clutter_shader, G_TYPE_OBJECT);
+
+static void
+clutter_shader_class_init (ClutterShaderClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  clutter_shader_parent_class = g_type_class_peek_parent (klass);
+  object_class->finalize      = clutter_shader_finalize;
+  object_class->set_property  = clutter_shader_set_property;
+  object_class->get_property  = clutter_shader_get_property;
+  object_class->constructor   = clutter_shader_constructor;
+  g_type_class_add_private (klass, sizeof (ClutterShaderPrivate));
+
+  g_object_class_install_property (object_class,
+                                   PROP_VERTEX_SOURCE,
+                                   g_param_spec_string ("vertex-source",
+                                                        "Vertex Source",
+                                                        "Source of vertex shader",
+                                                        NULL,
+                                                        CLUTTER_PARAM_READWRITE|
+                                                        G_PARAM_CONSTRUCT_ONLY));
+  g_object_class_install_property (object_class,
+                                   PROP_FRAGMENT_SOURCE,
+                                   g_param_spec_string ("fragment-source",
+                                                        "Fragment Source",
+                                                        "Source of fragment shader",
+                                                        NULL,
+                                                        CLUTTER_PARAM_READWRITE|
+                                                        G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+clutter_shader_init (ClutterShader *self)
+{
+  ClutterShaderPrivate *priv;
+
+  priv = self->priv = CLUTTER_SHADER_GET_PRIVATE (self);
+
+  priv->glsl = FALSE;
+  priv->bound = FALSE;
+  priv->vertex_shader_source = NULL;
+  priv->fragment_shader_source = NULL;
+  priv->program = 0;
+  priv->vertex_shader = 0;
+  priv->fragment_shader = 0;
+}
+
+static gboolean bind_glsl_shader (ClutterShader *self)
+{
+  ClutterShaderPrivate *priv;
+  priv = self->priv;
+
+  cogl_enable (CGL_FRAGMENT_SHADER);
+  cogl_enable (CGL_VERTEX_SHADER);
+
+  priv->glsl    = TRUE;
+  priv->program = cogl_create_program ();
+
+  if (priv->vertex_shader_source)
+    {
+     priv->vertex_shader = cogl_create_shader (CGL_VERTEX_SHADER);
+     cogl_shader_source (priv->vertex_shader, priv->vertex_shader_source);
+     cogl_shader_compile (priv->vertex_shader);
+     cogl_program_attach_shader (priv->program, priv->vertex_shader);
+    }
+  if (priv->fragment_shader_source)
+    {
+      GLint compiled = CGL_FALSE;
+      priv->fragment_shader = cogl_create_shader (CGL_FRAGMENT_SHADER);
+      cogl_shader_source (priv->fragment_shader, priv->fragment_shader_source);
+      cogl_shader_compile (priv->fragment_shader);
+
+      cogl_shader_get_parameteriv (priv->fragment_shader,
+                                   CGL_OBJECT_COMPILE_STATUS,
+                                   &compiled);
+      if (compiled != CGL_TRUE)
+        {
+          GLcharARB *buffer;
+          gint       max_length = 512;
+          buffer = g_malloc (max_length);
+          cogl_shader_get_info_log (priv->fragment_shader, max_length, buffer);
+          g_print ("Shader compilation failed:\n%s", buffer);
+          g_free (buffer);
+          g_object_unref (self);
+          return FALSE;
+        }
+      cogl_program_attach_shader (priv->program, priv->fragment_shader);
+    }
+  cogl_program_link (priv->program);
+  return TRUE;
+}
+
+gboolean
+clutter_shader_bind (ClutterShader *self)
+{
+  ClutterShaderPrivate *priv;
+  priv = self->priv;
+  if (priv->bound)
+    return priv->bound;
+
+  if (priv->glsl)
+    {
+      priv->bound = bind_glsl_shader (self);
+    }
+
+  return priv->bound;
+}
+
+void
+clutter_shader_release (ClutterShader *self)
+{
+  ClutterShaderPrivate *priv;
+
+  priv = self->priv;
+  if (!priv->bound)
+    return;
+
+  g_assert (priv->program);
+
+  if (priv->glsl)
+    {
+     if (priv->vertex_shader)
+       cogl_shader_destroy (priv->vertex_shader);
+     if (priv->fragment_shader)
+       cogl_shader_destroy (priv->fragment_shader);
+     if (priv->program)
+       cogl_program_destroy (priv->program);
+     priv->vertex_shader = 0;
+     priv->fragment_shader = 0;
+     priv->program = 0;
+    }
+  priv->bound = FALSE;
+}
+
+static void
+clutter_shader_finalize (GObject *object)
+{
+  ClutterShader        *shader;
+  ClutterShaderPrivate *priv;
+
+  shader = CLUTTER_SHADER (object);
+  priv   = shader->priv;
+
+  clutter_shader_release (shader);
+
+  shader_list = g_list_remove (shader_list, object);
+
+  if (priv->fragment_shader_source)
+    g_free (priv->fragment_shader_source);
+  if (priv->vertex_shader_source)
+    g_free (priv->vertex_shader_source);
+
+  G_OBJECT_CLASS (clutter_shader_parent_class)->finalize (object);
+}
+
+
+void
+clutter_shader_enable (ClutterShader *self)
+{
+  ClutterShaderPrivate *priv = self->priv;
+
+  clutter_shader_bind (self);
+
+  cogl_program_use (priv->program);
+}
+
+void
+clutter_shader_disable (ClutterShader *self)
+{
+  cogl_program_use (0);
+}
+
+void
+clutter_shader_set_uniform_1f (ClutterShader *self,
+                               const gchar   *name,
+                               gfloat         value)
+{
+  ClutterShaderPrivate *priv     = self->priv;
+  GLint                 location = 0;
+  GLfloat               foo      = value;
+
+  location =cogl_program_get_uniform_location (priv->program, name);
+  cogl_program_uniform_1f (location, foo);
+}
+
+void
+clutter_shader_release_all (void)
+{
+  GList *iter;
+  for (iter = shader_list; iter; iter = g_list_next (iter))
+    {
+      clutter_shader_release (iter->data);
+    }
+}
+
+static void
+clutter_shader_set_property (GObject      *object,
+                             guint         prop_id,
+                             const GValue *value,
+                             GParamSpec   *pspec)
+{
+  ClutterShader        *shader;
+  ClutterShaderPrivate *priv;
+
+  shader = CLUTTER_SHADER(object);
+  priv = shader->priv;
+
+  switch (prop_id)
+    {
+      case PROP_VERTEX_SOURCE:
+        if (priv->vertex_shader_source)
+          {
+            g_free (priv->vertex_shader_source);
+            priv->vertex_shader_source = NULL;
+          }
+        priv->vertex_shader_source = g_value_dup_string (value);
+        break;
+      case PROP_FRAGMENT_SOURCE:
+        if (priv->fragment_shader_source)
+          {
+            g_free (priv->fragment_shader_source);
+            priv->fragment_shader_source = NULL;
+          }
+        priv->fragment_shader_source = g_value_dup_string (value);
+        break;
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        break;
+    }
+}
+
+static void
+clutter_shader_get_property (GObject    *object,
+                             guint       prop_id,
+                             GValue     *value,
+                             GParamSpec *pspec)
+{
+  ClutterShader        *shader;
+  ClutterShaderPrivate *priv;
+
+  shader = CLUTTER_SHADER(object);
+  priv = shader->priv;
+
+  switch (prop_id)
+    {
+      case PROP_VERTEX_SOURCE:
+        g_value_set_string (value, priv->vertex_shader_source);
+        break;
+      case PROP_FRAGMENT_SOURCE:
+        g_value_set_string (value, priv->fragment_shader_source);
+        break;
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        break;
+    }
+}
+
+static GObject *
+clutter_shader_constructor (GType                  type,
+                            guint                  n_params,
+                            GObjectConstructParam *params)
+{
+  GObject       *object;
+  ClutterShader *shader;
+  ClutterShaderPrivate *priv;
+
+  object    = G_OBJECT_CLASS (clutter_shader_parent_class)->constructor (
+                                                      type, n_params, params);
+  shader = CLUTTER_SHADER (object);
+  priv = shader->priv;
+
+  priv->glsl = !((priv->vertex_shader_source &&
+               g_str_has_prefix (priv->vertex_shader_source, "!!ARBvp")) ||
+               (priv->fragment_shader_source &&
+               g_str_has_prefix (priv->fragment_shader_source, "!!ARBfp")));
+  if (!priv->glsl)
+    {
+      g_warning ("ASM shader support not available");
+      g_object_unref (object);
+      return NULL;
+    }
+  if (priv->glsl && !clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL))
+    {
+      g_warning ("GLSL shaders not supported\n");
+      g_object_unref (object);
+      return NULL;
+    }
+
+  shader_list = g_list_prepend (shader_list, object);
+  return object;
+}
+
+ClutterShader *
+clutter_shader_new_from_strings (const gchar *vertex_shader_program,
+                                 const gchar *fragment_shader_program)
+{
+  /* evil hack, since g_object_new would interpret a NULL passed as
+   * a argument termination
+   */
+  if (vertex_shader_program &&
+      fragment_shader_program)
+    return g_object_new (CLUTTER_TYPE_SHADER,
+                         "vertex-source", vertex_shader_program,
+                         "fragment-source", fragment_shader_program,
+                         NULL);
+  else if (fragment_shader_program)
+    return g_object_new (CLUTTER_TYPE_SHADER,
+                         "fragment-source", fragment_shader_program,
+                         NULL);
+  else if (vertex_shader_program)
+    return g_object_new (CLUTTER_TYPE_SHADER,
+                         "vertex-source", vertex_shader_program,
+                         NULL);
+  else {
+    g_warning ("neither fragment nor vertex shader provided");
+    return NULL;
+  }
+}
+
+ClutterShader *
+clutter_shader_new_from_files (const gchar *vertex_file,
+                               const gchar *fragment_file)
+{
+  ClutterShader *shader;
+  gchar         *vertex_shader_program   = NULL;
+  gchar         *fragment_shader_program = NULL;
+
+  g_assert (vertex_file != NULL ||
+            fragment_file != NULL);
+
+  if (vertex_file)
+    {
+      g_file_get_contents (vertex_file, &vertex_shader_program,
+                           NULL, NULL);
+    }
+  if (fragment_file)
+    {
+      g_file_get_contents (fragment_file, &fragment_shader_program,
+                           NULL, NULL);
+    }
+  shader = clutter_shader_new_from_strings (vertex_shader_program,
+                                            fragment_shader_program);
+
+  if (vertex_shader_program)
+    {
+      g_free (vertex_shader_program);
+    }
+  if (fragment_shader_program)
+    {
+      g_free (fragment_shader_program);
+    }
+  return shader;
+}
diff --git a/clutter/clutter-shader.h b/clutter/clutter-shader.h
new file mode 100644 (file)
index 0000000..2071749
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Authored By Matthew Allum  <mallum@openedhand.com>
+ *             Øyvind Kolås   <pippin@o-hand.com>
+ *
+ * Copyright (C) 2007 OpenedHand
+ *
+ * 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef CLUTTER_SHADER_H
+#define CLUTTER_SHADER_H
+
+#include <GL/gl.h>
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define CLUTTER_TYPE_SHADER         (clutter_shader_get_type ())
+#define CLUTTER_SHADER(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), CLUTTER_TYPE_SHADER, ClutterShader))
+#define CLUTTER_SHADER_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), CLUTTER_TYPE_SHADER, ClutterShaderClass))
+#define CLUTTER_IS_SHADER(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), CLUTTER_TYPE_SHADER))
+#define CLUTTER_IS_SHADER_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), CLUTTER_TYPE_SHADER))
+#define CLUTTER_SHADER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), CLUTTER_TYPE_SHADER, ClutterShaderClass))
+
+typedef struct _ClutterShader        ClutterShader;
+typedef struct _ClutterShaderPrivate ClutterShaderPrivate;
+typedef struct _ClutterShaderClass   ClutterShaderClass;
+
+struct _ClutterShader
+{
+  GObject               parent;
+  ClutterShaderPrivate *priv;
+};
+
+struct _ClutterShaderClass
+{
+  GObjectClass parent_class;
+};
+
+GType           clutter_shader_get_type         ();
+
+ClutterShader * clutter_shader_new_from_files   (const gchar *vertex_file,
+                                                 const gchar *fragment_file);
+ClutterShader * clutter_shader_new_from_strings (const gchar *vertex_file,
+                                                 const gchar *fragment_file);
+void            clutter_shader_enable           (ClutterShader *self);
+void            clutter_shader_disable          (ClutterShader *self);
+
+gboolean        clutter_shader_bind             (ClutterShader *self);
+void            clutter_shader_release          (ClutterShader *self);
+void            clutter_shader_set_uniform_1f   (ClutterShader *self, 
+                                                 const gchar   *name,
+                                                 gfloat         value);
+/* should be private and internal */
+void            clutter_shader_release_all    (void);
+gboolean        clutter_shader_has_glsl         (void);
+
+
+G_END_DECLS
+
+#endif /* CLUTTER_SHADER_H */
index 12b23dd..5ebf991 100644 (file)
@@ -207,6 +207,58 @@ cogl_fog_set (const ClutterColor *fog_color,
               ClutterFixed        z_near,
               ClutterFixed        z_far);
 
+
+COGLint
+cogl_create_shader               (COGLenum     shaderType);
+
+void
+cogl_shader_destroy              (COGLint      handle);
+
+
+void
+cogl_shader_source               (COGLint      shader,
+                                  const gchar *source);
+void
+cogl_shader_compile               (COGLint      shader_handle);
+
+void
+cogl_shader_get_info_log          (COGLint      handle,
+                                   guint        size,
+                                   gchar       *buffer);
+
+void
+cogl_shader_get_parameteriv       (COGLint      handle,
+                                   COGLenum     pname,
+                                   COGLint     *dest);
+
+
+COGLint
+cogl_create_program               (void);
+
+void
+cogl_program_destroy              (COGLint      handle);
+
+void
+cogl_program_attach_shader        (COGLint      program_handle,
+                                   COGLint      shader_handle);
+
+/* 0 to use none */
+void
+cogl_program_link                 (COGLint      program_handle);
+
+void
+cogl_program_use                  (COGLint      program_handle);
+
+COGLint
+cogl_program_get_uniform_location (COGLint      program_int,
+                                   const gchar *uniform_name);
+
+
+void
+cogl_program_uniform_1f           (COGLint      uniform_no,
+                                   gfloat       value);
+
+
 G_END_DECLS
 
 #endif /* __COGL_H__ */
index a8fbdd3..93abe43 100644 (file)
@@ -698,6 +698,12 @@ typedef GLuint COGLuint;
 #define CGL_UNSIGNED_SHORT_8_8_MESA 0
 #endif
 
+#define CGL_FRAGMENT_SHADER GL_FRAGMENT_SHADER_ARB
+#define CGL_VERTEX_SHADER   GL_VERTEX_SHADER_ARB
+
+#define CGL_OBJECT_COMPILE_STATUS  GL_OBJECT_COMPILE_STATUS_ARB
+
+
 G_END_DECLS
 
 #endif
index d96aff6..4a58634 100644 (file)
@@ -621,6 +621,12 @@ cogl_get_features ()
     }
 #endif
 
+  if (cogl_check_extension ("GL_ARB_vertex_shader", gl_extensions) &&
+      cogl_check_extension ("GL_ARB_fragment_shader", gl_extensions))
+    {
+      flags |= CLUTTER_FEATURE_SHADERS_GLSL;
+    }
+
   return flags;
 }
 
@@ -746,3 +752,134 @@ cogl_fog_set (const ClutterColor *fog_color,
   glFogf (GL_FOG_START, CLUTTER_FIXED_TO_FLOAT (start));
   glFogf (GL_FOG_END, CLUTTER_FIXED_TO_FLOAT (stop));
 }
+
+#ifdef __GNUC__
+
+#define PROC(rettype, retval, procname, args...) \
+  static rettype (*proc) (args) = NULL;  \
+   if (proc == NULL) \
+     { \
+       proc = (void*)cogl_get_proc_address (#procname);\
+       if (!proc)\
+         {\
+           g_warning ("failed to lookup proc: %s", #procname);\
+           return retval;\
+         }\
+     }
+#else
+
+#define PROC(rettype, retval, procname, ...) \
+  static rettype (*proc) (__VA_ARGS__) = NULL;  \
+   if (proc == NULL) \
+     { \
+       proc = (void*)cogl_get_proc_address (#procname);\
+       if (!proc)\
+         {\
+           g_warning ("failed to lookup proc: %s", #procname);\
+           return retval;\
+         }\
+     }
+
+#endif
+
+COGLint
+cogl_create_program (void)
+{
+  PROC (GLhandleARB, 0, glCreateProgramObjectARB, void);
+  return proc ();
+}
+
+COGLint
+cogl_create_shader (COGLenum shaderType)
+{
+  PROC (GLhandleARB, 0, glCreateShaderObjectARB, GLenum);
+  return proc (shaderType);
+}
+
+void
+cogl_shader_source (COGLint      shader,
+                    const gchar *source)
+{
+  PROC (GLvoid,, glShaderSourceARB, GLhandleARB, GLsizei, const GLcharARB **, const GLint *)
+  proc (shader, 1, &source, NULL);
+}
+
+void
+cogl_shader_compile (COGLint shader_handle)
+{
+  PROC (GLvoid,, glCompileShaderARB, GLhandleARB);
+  proc (shader_handle);
+}
+
+void
+cogl_program_attach_shader (COGLint program_handle,
+                            COGLint shader_handle)
+{
+  PROC (GLvoid,, glAttachObjectARB, GLhandleARB, GLhandleARB);
+  proc (program_handle, shader_handle);
+}
+
+void
+cogl_program_link (COGLint program_handle)
+{
+  PROC (GLvoid,, glLinkProgramARB, GLhandleARB);
+  proc (program_handle);
+}
+
+void
+cogl_program_use (COGLint program_handle)
+{
+  PROC (GLvoid,, glUseProgramObjectARB, GLhandleARB);
+  proc (program_handle);
+}
+
+COGLint
+cogl_program_get_uniform_location (COGLint      program_handle,
+                                   const gchar *uniform_name)
+{
+  PROC (GLint,0, glGetUniformLocationARB, GLhandleARB, const GLcharARB *)
+  return proc (program_handle, uniform_name);
+}
+
+void
+cogl_program_destroy (COGLint      handle)
+{
+  PROC (GLvoid,, glDeleteObjectARB, GLhandleARB);
+  proc (handle);
+}
+
+void
+cogl_shader_destroy (COGLint handle)
+{
+  PROC (GLvoid,, glDeleteObjectARB, GLhandleARB);
+  proc (handle);
+}
+
+void
+cogl_shader_get_info_log (COGLint      handle,
+                          guint        size,
+                          gchar       *buffer)
+{
+  gint len;
+  PROC (GLvoid,, glGetInfoLogARB, GLhandleARB, GLsizei, GLsizei *, GLcharARB *);
+  proc (handle, size-1, &len, buffer);
+  buffer[len]='\0';
+}
+
+void
+cogl_shader_get_parameteriv (COGLint      handle,
+                             COGLenum     pname,
+                             COGLint     *dest)
+{
+  PROC (GLvoid,, glGetObjectParameterivARB, GLhandleARB, GLenum, GLint*)
+  proc (handle, pname, dest);
+}
+
+
+void
+cogl_program_uniform_1f (COGLint uniform_no,
+                         gfloat  value)
+{
+  PROC (GLvoid,, glUniform1fARB, GLint, GLfloat);
+  proc (uniform_no, value);
+}
index 41eac15..c8ad851 100644 (file)
@@ -460,6 +460,24 @@ typedef GLuint COGLuint;
 #define CGL_UNSIGNED_SHORT_8_8_REV_MESA 0
 #define CGL_UNSIGNED_SHORT_8_8_MESA 0
 
+#ifdef GL_FRAGMENT_SHADER
+#define CGL_FRAGMENT_SHADER GL_FRAGMENT_SHADER
+#else
+#define CGL_FRAGMENT_SHADER 0
+#endif
+
+#ifdef GL_VERTEX_SHADER
+#define CGL_VERTEX_SHADER GL_VERTEX_SHADER
+#else
+#define CGL_VERTEX_SHADER 0
+#endif
+
+#ifdef GL_OBJECT_COMPILE_STATUS
+#define CGL_OBJECT_COMPILE_STATUS  GL_OBJECT_COMPILE_STATUS
+#else
+#define CGL_OBJECT_COMPILE_STATUS 0
+#endif
+
 G_END_DECLS
 
 #endif
index 20021c6..e10efc5 100644 (file)
@@ -214,13 +214,13 @@ cogl_enable (gulong flags)
   if (flags & CGL_ENABLE_TEXTURE_RECT)
     {
       if (!(__enable_flags & CGL_ENABLE_TEXTURE_RECT))
-         glEnable (GL_TEXTURE_RECTANGLE_ARB);
+         glEnable (GL_TEXTURE_RECTANGLE_);
 
       __enable_flags |= CGL_ENABLE_TEXTURE_RECT;
     }
   else if (__enable_flags & CGL_ENABLE_TEXTURE_RECT)
     {
-      glDisable (GL_TEXTURE_RECTANGLE_ARB);
+      glDisable (GL_TEXTURE_RECTANGLE_);
       __enable_flags &= ~CGL_ENABLE_TEXTURE_RECT;
     }
 #endif
@@ -630,3 +630,67 @@ cogl_fog_set (const ClutterColor *fog_color,
   glFogx (GL_FOG_START, (GLfixed) z_near);
   glFogx (GL_FOG_END, (GLfixed) z_far);
 }
+
+COGLint cogl_create_program (void)
+{
+  return 0;
+}
+
+COGLint cogl_create_shader  (COGLenum     shaderType)
+{
+  return 0;
+}
+
+void    cogl_shader_source         (COGLint      shader,
+                                    const gchar *source)
+{
+}
+
+void    cogl_shader_compile        (COGLint      shader_handle)
+{
+}
+
+void    cogl_program_attach_shader (COGLint      program_handle,
+                                    COGLint      shader_handle)
+{
+}
+
+void    cogl_program_link          (COGLint      program_handle)
+{
+}
+
+void    cogl_program_use    (COGLint      program_handle)
+{
+}
+
+COGLint cogl_program_get_uniform_location  (COGLint      program_handle,
+                                            const gchar *uniform_name)
+{
+  return 0;
+}
+
+void    cogl_program_destroy         (COGLint      handle)
+{
+}
+
+void    cogl_shader_destroy         (COGLint      handle)
+{
+}
+
+void    cogl_shader_get_info_log   (COGLint      handle,
+                                    guint        size,
+                                    gchar       *buffer)
+{
+}
+
+void    cogl_shader_get_parameteriv  (COGLint      handle,
+                                      COGLenum     pname,
+                                      COGLint     *dest)
+{
+}
+
+
+void    cogl_program_uniform_1f (COGLint uniform_no,
+                         gfloat  value)
+{
+}
index eec8759..31d2fbe 100644 (file)
@@ -36,6 +36,7 @@
 #include "../clutter-private.h"
 #include "../clutter-debug.h"
 #include "../clutter-units.h"
+#include "../clutter-shader.h"
 
 #include "cogl.h"
 
@@ -60,6 +61,9 @@ clutter_stage_glx_unrealize (ClutterActor *actor)
 
   clutter_x11_trap_x_errors ();
 
+  /* Unrealize all shaders, since the GL context is going away */
+  clutter_shader_release_all ();
+
   if (G_UNLIKELY (was_offscreen))
     {
       if (stage_glx->glxpixmap)
index 3283263..8aef6be 100644 (file)
@@ -2,7 +2,7 @@ noinst_PROGRAMS = test-textures test-events test-offscreen test-scale \
                   test-actors test-behave test-text test-entry test-project \
                  test-perspective test-rotate test-depth \
                  test-threads test-timeline test-score test-script \
-                 test-model test-grab test-effects test-fullscreen
+                 test-model test-grab test-effects test-fullscreen test-shader
 
 INCLUDES = -I$(top_srcdir)/
 LDADD = $(top_builddir)/clutter/libclutter-@CLUTTER_FLAVOUR@-@CLUTTER_MAJORMINOR@.la
@@ -24,6 +24,7 @@ test_rotate_SOURCES      = test-rotate.c
 test_depth_SOURCES       = test-depth.c
 test_threads_SOURCES     = test-threads.c
 test_timeline_SOURCES    = test-timeline.c
+test_shader_SOURCES      = test-shader.c
 test_score_SOURCES       = test-score.c
 test_script_SOURCES      = test-script.c
 test_model_SOURCES       = test-model.c
diff --git a/tests/test-shader.c b/tests/test-shader.c
new file mode 100644 (file)
index 0000000..d4e3ecc
--- /dev/null
@@ -0,0 +1,246 @@
+/* #define TEST_GROUP 1 */
+
+#include <clutter/clutter.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <glib.h>
+
+typedef struct
+{
+  gchar *name;
+  gchar *source;
+} ShaderSource;
+
+static ShaderSource shaders[]=
+  {
+    {"brightness-contrast",
+
+      "uniform float brightness;"
+      "uniform float contrast;"
+      "uniform sampler2DRect pend_s3_tex;"
+      ""
+      "void main()"
+      "{"
+      "    vec4 pend_s4_result;"
+      "    pend_s4_result = texture2DRect(pend_s3_tex, gl_TexCoord[0].xy);"
+      "    pend_s4_result.x = (pend_s4_result.x - 0.5)*contrast + brightness + 0.5;"
+      "    pend_s4_result.y = (pend_s4_result.y - 0.5)*contrast + brightness + 0.5;"
+      "    pend_s4_result.z = (pend_s4_result.z - 0.5)*contrast + brightness + 0.5;"
+      "    gl_FragColor = pend_s4_result;"
+      "}",
+    },
+    {"box-blur",
+
+        "uniform float radius ;"
+        "uniform sampler2DRect rectTexture;"
+        ""
+        "void main()"
+        "{"
+        "    vec4 color = texture2DRect(rectTexture, gl_TexCoord[0].st);"
+        "    float u;"
+        "    float v;"
+        "    int count = 1;"
+        "    for (u=-radius;u<radius;u++)"
+        "      for (v=-radius;v<radius;v++)"
+        "        {"
+        "          color += texture2DRect(rectTexture, vec2(gl_TexCoord[0].s + u * 2, gl_TexCoord[0].t +v * 2));"
+        "          count ++;"
+        "        }"
+        ""
+        "    gl_FragColor = color / count;"
+        "}"
+    },
+    {"brightness-contrast.asm",
+
+      "!!ARBfp1.0\n"
+      "PARAM brightness = program.local[0];\n"
+      "PARAM contrast = program.local[1];\n"
+      "\n"
+      "TEMP R0;\n"
+      "TEX R0, fragment.texcoord[0], texture[0], RECT;\n"
+      "ADD R0.z, R0, -0.5;\n"
+      "MUL R0.z, R0, contrast.x;\n"
+      "ADD R0.z, R0, brightness.x;\n"
+      "ADD R0.y, R0, -0.5;\n"
+      "ADD R0.x, R0, -0.5;\n"
+      "MUL R0.y, R0, contrast.x;\n"
+      "MUL R0.x, R0, contrast.x;\n"
+      "ADD R0.y, R0, brightness.x;\n"
+      "ADD R0.x, R0, brightness.x;\n"
+      "ADD result.color.z, R0, 0.5;\n"
+      "ADD result.color.y, R0, 0.5;\n"
+      "ADD result.color.x, R0, 0.5;\n"
+      "MOV result.color.w, R0;\n"
+      "END ",
+    },
+    {"invert",
+
+      "uniform sampler2DRect tex;\n"
+      "void main ()\n"
+      "{\n"
+      "  vec4 color = texture2DRect (tex, vec2(gl_TexCoord[0].st));\n"
+      "  gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0) - color;\n"
+      "  gl_FragColor.a = color.a;\n"
+      "}"
+    },
+    {"brightness-contrast",
+
+        "uniform sampler2DRect tex;"
+        "uniform float brightness;"
+        "uniform float contrast;"
+        "void main ()"
+        "{"
+        "  vec4 color = texture2DRect (tex, vec2(gl_TexCoord[0].st));"
+        "  color.r = (color.r - 0.5) * contrast + brightness + 0.5;"
+        "  color.g = (color.g - 0.5) * contrast + brightness + 0.5;"
+        "  color.b = (color.b - 0.5) * contrast + brightness + 0.5;"
+        "  gl_FragColor = color;"
+        "}",
+    },
+    {"gray",
+      "uniform sampler2DRect tex;"
+      "void main ()"
+      "{"
+      "  vec4 color = texture2DRect (tex, vec2(gl_TexCoord[0].st));"
+      "  float avg = (color.r + color.g + color.b) / 3;"
+      "  color.r = avg;"
+      "  color.g = avg;"
+      "  color.b = avg;"
+      "  gl_FragColor = color;"
+      "}",
+    },
+    {"combined-mirror",
+      "uniform sampler2DRect tex;"
+      "void main ()"
+      "{"
+      "  vec4 color = texture2DRect (tex, vec2(gl_TexCoord[0].st));"
+      "  vec4 colorB = texture2DRect (tex, vec2(gl_TexCoord[0].ts));"
+      "  float avg = (color.r + color.g + color.b) / 3;"
+      "  color.r = avg;"
+      "  color.g = avg;"
+      "  color.b = avg;"
+      "  color = (color + colorB)/2;"
+      "  gl_FragColor = color;"
+      "}",
+    },
+    {NULL, NULL}
+};
+
+static gint shader_no=0;
+
+static gboolean
+button_release_cb (ClutterActor    *actor,
+                   ClutterEvent    *event,
+                   gpointer         data)
+{
+  gint new_no;
+  if (event->button.button == 1)
+    {
+      new_no = shader_no-1;
+    }
+  else
+    {
+      new_no = shader_no+1;
+    }
+
+  if (new_no >= 0 && shaders[new_no].name)
+    {
+      ClutterShader *shader;
+      shader_no = new_no;
+      
+      g_print ("setting shaders[%i] named '%s'\n", shader_no, shaders[shader_no].name);
+      shader = clutter_shader_new_from_strings (NULL, shaders[shader_no].source);
+      clutter_actor_apply_shader (actor, shader);
+      
+      clutter_actor_set_shader_param (actor, "radius", 3.0);
+      clutter_redraw();
+    }
+  return FALSE;
+}
+
+
+gint
+main (gint   argc,
+      gchar *argv[])
+{
+  ClutterTimeline  *timeline;
+  ClutterAlpha     *alpha;
+  ClutterActor     *actor;
+  ClutterActor     *stage;
+  ClutterColor      stage_color = { 0x61, 0x64, 0x8c, 0xff };
+  GdkPixbuf        *pixbuf;
+  GError           *error;
+  ClutterShader    *shader;
+
+  error = NULL;
+
+  clutter_init (&argc, &argv);
+  g_print ("applying shaders[%i] named '%s'\n", shader_no, shaders[shader_no].name);
+  shader = clutter_shader_new_from_strings (NULL, shaders[shader_no].source);
+
+  stage = clutter_stage_get_default ();
+  clutter_actor_set_size (stage, 512, 384);
+
+  pixbuf = gdk_pixbuf_new_from_file ("redhand.png", NULL);
+
+  if (!pixbuf)
+    g_error("pixbuf load failed");
+
+  clutter_stage_set_title (CLUTTER_STAGE (stage), "Shader Test");
+  clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
+
+  /* Create a timeline to manage animation */
+  timeline = clutter_timeline_new (360, 60); /* num frames, fps */
+  g_object_set (timeline, "loop", TRUE, NULL);   /* have it loop */
+
+#ifndef TEST_GROUP
+  actor = clutter_texture_new_from_pixbuf (pixbuf);
+#else
+  actor = clutter_group_new ();
+    {
+      ClutterActor *child1, *child2, *child3, *child4;
+      ClutterColor  color={0xff, 0x22, 0x66, 0x99};
+
+      child1 = clutter_texture_new_from_pixbuf (pixbuf);
+      child2 = clutter_texture_new_from_pixbuf (pixbuf);
+      child3 = clutter_rectangle_new ();
+      child4 = clutter_label_new_with_text ("Sans 20px", "Shady stuff");
+
+      clutter_rectangle_set_color (child3, &color);
+      clutter_actor_set_size (child3, 50, 50);
+      clutter_actor_set_position (child1, 0, 0);
+      clutter_actor_set_position (child2, 50, 100);
+      clutter_actor_set_position (child3, 30, -30);
+      clutter_actor_set_position (child4, -50, 20);
+
+      clutter_group_add (CLUTTER_GROUP (actor), child1);
+      clutter_group_add (CLUTTER_GROUP (actor), child2);
+      clutter_group_add (CLUTTER_GROUP (actor), child3);
+      clutter_group_add (CLUTTER_GROUP (actor), child4);
+      clutter_actor_show_all (actor);
+    }
+#endif
+  clutter_actor_set_position (actor, 100, 100);
+
+  clutter_container_add_actor (CLUTTER_CONTAINER (stage), actor);
+
+  clutter_actor_apply_shader (actor, shader);
+
+  clutter_actor_set_shader_param (actor, "brightness", 0.4);
+  clutter_actor_set_shader_param (actor, "contrast", -1.9);
+                                 
+  clutter_actor_set_reactive (actor, TRUE);
+  g_signal_connect (actor, "button-release-event",
+                    G_CALLBACK (button_release_cb), NULL);
+
+  /* Show everying ( and map window ) */
+  clutter_actor_show_all (stage);
+
+  /* and start it */
+  clutter_timeline_start (timeline);
+
+  clutter_main ();
+
+  return 0;
+}