Refined the ClutterShader API based on a patch from Emmanuele Bassi.
authorØyvind Kolås <pippin@openedhand.com>
Tue, 11 Dec 2007 14:49:41 +0000 (14:49 +0000)
committerØyvind Kolås <pippin@openedhand.com>
Tue, 11 Dec 2007 14:49:41 +0000 (14:49 +0000)
* clutter/clutter-actor.c: (clutter_actor_paint),
(clutter_actor_apply_shader): queue a redraw of actor when shader is
set.
* clutter/clutter-actor.h: indentation.
* clutter/clutter-shader.[ch]: made the fragment and vertex shader
sources properties. Report error through GError when binding shaders.
* tests/test-shader.c: modified to use newer API.

ChangeLog
clutter/clutter-actor.c
clutter/clutter-actor.h
clutter/clutter-shader.c
clutter/clutter-shader.h
tests/test-shader.c

index 089c75b..7671520 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+2007-12-11  Øyvind Kolås  <pippin@o-hand.com>
+
+       Refined the ClutterShader API based on a patch from Emmanuele Bassi.
+
+       * clutter/clutter-actor.c: (clutter_actor_paint),
+       (clutter_actor_apply_shader): queue a redraw of actor when shader is
+       set.
+       * clutter/clutter-actor.h: indentation.
+       * clutter/clutter-shader.[ch]: made the fragment and vertex shader
+       sources properties. Report error through GError when binding shaders.
+       * tests/test-shader.c: modified to use newer API.
+
 2007-12-10  Emmanuele Bassi  <ebassi@openedhand.com>
 
        * clutter/clutter-model.c:
index 0a46acf..53b7cdc 100644 (file)
@@ -928,8 +928,10 @@ clutter_actor_paint (ClutterActor *self)
   else
     {
       clutter_actor_shader_pre_paint (self, FALSE);
-      if (G_LIKELY(klass->paint))
-       (klass->paint) (self);
+
+      if (G_LIKELY (klass->paint))
+         klass->paint (self);
+
       clutter_actor_shader_post_paint (self);
     }
 
@@ -4766,6 +4768,8 @@ clutter_actor_apply_shader (ClutterActor  *self,
   if (shader)
     shader_data->shader = g_object_ref (shader);
 
+  clutter_actor_queue_redraw (self);
+
   return TRUE;
 }
 
@@ -4802,7 +4806,7 @@ clutter_actor_shader_pre_paint (ClutterActor *actor,
 
   if (shader)
     {
-      clutter_shader_enable (shader);
+      clutter_shader_set_is_enabled (shader, TRUE);
 
       g_hash_table_foreach (shader_data->float1f_hash, set_each_param, shader);
 
@@ -4832,7 +4836,7 @@ clutter_actor_shader_post_paint (ClutterActor *actor)
 
   if (shader)
     {
-      clutter_shader_disable (shader);
+      clutter_shader_set_is_enabled (shader, FALSE);
 
       context->shaders = g_slist_remove (context->shaders, actor);
       if (context->shaders)
index ec80e84..d883b16 100644 (file)
@@ -398,12 +398,11 @@ 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);
+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,
index 56f3ed8..00775f9 100644 (file)
  * Boston, MA 02111-1307, USA.
  */
 
-#include "clutter.h"
-#include "clutter-private.h"
-#include "clutter-shader.h"
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+#include <stdlib.h>
 
+#ifdef HAVE_UNISTD_H
 #include <unistd.h>
+#endif
+
 #include <glib.h>
+
 #include <cogl/cogl.h>
-#include <string.h>
-#include <stdlib.h>
+
+#include "clutter-debug.h"
+#include "clutter-private.h"
+#include "clutter-shader.h"
+
+static GList *clutter_shaders_list = NULL;
 
 #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);
+typedef enum {
+  CLUTTER_VERTEX_SHADER,
+  CLUTTER_FRAGMENT_SHADER
+} ClutterShaderType;
 
 struct _ClutterShaderPrivate
 {
-  gboolean  glsl;   /* The shader is a GLSL shader */
-  gboolean  bound;  /* The shader is bound to the GL context */
+  guint     bound            : 1;  /* The shader is bound to the GL context */
+  guint     is_enabled       : 1;
 
-  gchar    *vertex_shader_source;  /* source (or asm) for vertex shader  */
-  gchar    *fragment_shader_source;/* source (or asm) for fragment shader*/
+  guint     vertex_is_glsl   : 1;
+  guint     fragment_is_glsl : 1;
+
+  gchar    *vertex_source;         /* source (or asm) for vertex shader */
+  gchar    *fragment_source;       /* source (or asm) for fragment shader */
 
   COGLint   program;
 
@@ -72,40 +74,180 @@ struct _ClutterShaderPrivate
 enum 
 {
   PROP_0,
+
   PROP_VERTEX_SOURCE,
-  PROP_FRAGMENT_SOURCE
+  PROP_FRAGMENT_SOURCE,
+  PROP_BOUND,
+  PROP_ENABLED
 };
 
+
+
 G_DEFINE_TYPE (ClutterShader, clutter_shader, G_TYPE_OBJECT);
 
+G_CONST_RETURN gchar *clutter_shader_get_source (ClutterShader      *shader,
+                                                 ClutterShaderType   type);
+
+static void
+clutter_shader_finalize (GObject *object)
+{
+  ClutterShader        *shader;
+  ClutterShaderPrivate *priv;
+
+  shader = CLUTTER_SHADER (object);
+  priv   = shader->priv;
+
+  clutter_shader_release (shader);
+
+  clutter_shaders_list = g_list_remove (clutter_shaders_list, object);
+
+  g_free (priv->fragment_source);
+  g_free (priv->vertex_source);
+
+  G_OBJECT_CLASS (clutter_shader_parent_class)->finalize (object);
+}
+
+static void
+clutter_shader_set_property (GObject      *object,
+                             guint         prop_id,
+                             const GValue *value,
+                             GParamSpec   *pspec)
+{
+  ClutterShader *shader = CLUTTER_SHADER(object);
+
+  switch (prop_id)
+    {
+    case PROP_VERTEX_SOURCE:
+      clutter_shader_set_vertex_source (shader, g_value_get_string (value), -1);
+      break;
+    case PROP_FRAGMENT_SOURCE:
+      clutter_shader_set_fragment_source (shader, g_value_get_string (value), -1);
+      break;
+    case PROP_ENABLED:
+      clutter_shader_set_is_enabled (shader, g_value_get_boolean (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_source);
+      break;
+    case PROP_FRAGMENT_SOURCE:
+      g_value_set_string (value, priv->fragment_source);
+      break;
+    case PROP_BOUND:
+      g_value_set_boolean (value, priv->bound);
+      break;
+    case PROP_ENABLED:
+      g_value_set_boolean (value, priv->is_enabled);
+      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;
+
+  object = G_OBJECT_CLASS (clutter_shader_parent_class)->constructor (type, n_params, params);
+
+  /* add this instance to the global list of shaders */
+  clutter_shaders_list = g_list_prepend (clutter_shaders_list, object);
+
+  return 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));
 
+  /**
+   * ClutterShader:vertex-source:
+   *
+   * FIXME
+   *
+   * Since: 0.6
+   */
   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));
+                                                        CLUTTER_PARAM_READWRITE));
+  /**
+   * ClutterShader:fragment-source:
+   *
+   * FIXME
+   *
+   * Since: 0.6
+   */
   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));
+                                                        CLUTTER_PARAM_READWRITE));
+  /**
+   * ClutterShader:bound:
+   *
+   * FIXME
+   *
+   * Since: 0.6
+   */
+  g_object_class_install_property (object_class,
+                                   PROP_BOUND,
+                                   g_param_spec_boolean ("bound",
+                                                         "Bound",
+                                                         "Whether the shader is bound",
+                                                         FALSE,
+                                                         CLUTTER_PARAM_READABLE));
+  /**
+   * ClutterShader:enabled:
+   *
+   * FIXME
+   *
+   * Since: 0.6
+   */
+  g_object_class_install_property (object_class,
+                                   PROP_ENABLED,
+                                   g_param_spec_boolean ("enabled",
+                                                         "Enabled",
+                                                         "Whether the shader is enabled",
+                                                         FALSE,
+                                                         CLUTTER_PARAM_READWRITE));
 }
 
 static void
@@ -115,16 +257,132 @@ clutter_shader_init (ClutterShader *self)
 
   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->vertex_source = NULL;
+  priv->fragment_source = NULL;
+
   priv->program = 0;
   priv->vertex_shader = 0;
   priv->fragment_shader = 0;
 }
 
-static gboolean bind_glsl_shader (ClutterShader *self)
+/**
+ * clutter_shader_new:
+ *
+ * FIXME
+ *
+ * Return value: FIXME
+ *
+ * Since: 0.6
+ */
+ClutterShader *
+clutter_shader_new (void)
+{
+  return g_object_new (CLUTTER_TYPE_SHADER, NULL);
+}
+
+
+/**
+ * clutter_shader_set_fragment_source:
+ * @shader: a #ClutterShader
+ * @data: FIXME
+ * @length: FIXME (currently ignored)
+ *
+ * FIXME
+ *
+ *
+ * Since: 0.6
+ */
+void
+clutter_shader_set_fragment_source (ClutterShader      *shader,
+                                    const gchar        *data,
+                                    gssize              length)
+{
+  ClutterShaderPrivate *priv;
+  gboolean is_glsl;
+
+  if (shader == NULL)
+    g_error ("quack!");
+
+  g_return_if_fail (CLUTTER_IS_SHADER (shader));
+  g_return_if_fail (data != NULL);
+
+  priv = shader->priv;
+
+  /* release shader if bound when changing the source, the shader will
+   * automatically be rebound on the next use.
+   */
+  if (clutter_shader_is_bound (shader))
+    clutter_shader_release (shader);
+
+  is_glsl = !g_str_has_prefix (data, "!!ARBfp");
+
+  if (priv->fragment_source)
+    {
+      g_free (priv->fragment_source);
+    }
+
+  CLUTTER_NOTE (SHADER, "setting fragment shader (GLSL:%s, len:%d)",
+                is_glsl ? "yes" : "no",
+                length);
+
+  priv->fragment_source = g_strdup (data);
+  priv->fragment_is_glsl = is_glsl;
+}
+
+
+/**
+ * clutter_shader_set_vertex_source:
+ * @shader: a #ClutterShader
+ * @data: FIXME
+ * @length: FIXME (currently ignored)
+ *
+ * FIXME
+ *
+ * Since: 0.6
+ */
+void
+clutter_shader_set_vertex_source (ClutterShader      *shader,
+                                  const gchar        *data,
+                                  gssize              length)
+{
+  ClutterShaderPrivate *priv;
+  gboolean is_glsl;
+
+  if (shader == NULL)
+    g_error ("quack!");
+
+  g_return_if_fail (CLUTTER_IS_SHADER (shader));
+  g_return_if_fail (data != NULL);
+
+  priv = shader->priv;
+
+  /* release shader if bound when changing the source, the shader will
+   * automatically be rebound on the next use.
+   */
+  if (clutter_shader_is_bound (shader))
+    clutter_shader_release (shader);
+
+
+  is_glsl = !g_str_has_prefix (data, "!!ARBvp");
+
+  if (priv->vertex_source)
+    {
+      g_free (priv->vertex_source);
+    }
+
+  CLUTTER_NOTE (SHADER, "setting vertex shader (GLSL:%s, len:%d)",
+                is_glsl ? "yes" : "no",
+                length);
+
+  priv->vertex_source = g_strdup (data);
+  priv->vertex_is_glsl = is_glsl;
+}
+
+static gboolean
+bind_glsl_shader (ClutterShader  *self,
+                  GError        **error)
 {
   ClutterShaderPrivate *priv;
   priv = self->priv;
@@ -132,21 +390,24 @@ static gboolean bind_glsl_shader (ClutterShader *self)
   cogl_enable (CGL_FRAGMENT_SHADER);
   cogl_enable (CGL_VERTEX_SHADER);
 
-  priv->glsl    = TRUE;
   priv->program = cogl_create_program ();
 
-  if (priv->vertex_shader_source)
+  if (priv->vertex_is_glsl && priv->vertex_source)
     {
      priv->vertex_shader = cogl_create_shader (CGL_VERTEX_SHADER);
-     cogl_shader_source (priv->vertex_shader, priv->vertex_shader_source);
+
+     cogl_shader_source (priv->vertex_shader, priv->vertex_source);
      cogl_shader_compile (priv->vertex_shader);
      cogl_program_attach_shader (priv->program, priv->vertex_shader);
     }
-  if (priv->fragment_shader_source)
+
+  if (priv->fragment_is_glsl && priv->fragment_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_source (priv->fragment_shader, priv->fragment_source);
       cogl_shader_compile (priv->fragment_shader);
 
       cogl_shader_get_parameteriv (priv->fragment_shader,
@@ -154,281 +415,283 @@ static gboolean bind_glsl_shader (ClutterShader *self)
                                    &compiled);
       if (compiled != CGL_TRUE)
         {
-          gchar *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);
+          gchar error_buf[512];
+
+          cogl_shader_get_info_log (priv->fragment_shader, 512, error_buf);
+
+          g_set_error (error, CLUTTER_SHADER_ERROR,
+                       CLUTTER_SHADER_ERROR_COMPILE,
+                       "Shader compilation failed: %s",
+                       error_buf);
+
           return FALSE;
         }
-      cogl_program_attach_shader (priv->program, priv->fragment_shader);
+      else
+        cogl_program_attach_shader (priv->program, priv->fragment_shader);
     }
+
   cogl_program_link (priv->program);
+
   return TRUE;
 }
 
+/**
+ * clutter_shader_bind:
+ * @shader: a #ClutterShader
+ *
+ * FIXME
+ *
+ * Return value: FIXME
+ *
+ * Since: 0.6
+ */
 gboolean
-clutter_shader_bind (ClutterShader *self)
+clutter_shader_bind (ClutterShader  *shader,
+                     GError        **error)
 {
   ClutterShaderPrivate *priv;
-  priv = self->priv;
+
+  g_return_val_if_fail (CLUTTER_IS_SHADER (shader), FALSE);
+
+  priv = shader->priv;
+
   if (priv->bound)
     return priv->bound;
 
-  if (priv->glsl)
+  if ((priv->vertex_source   && !priv->vertex_is_glsl) ||
+      (priv->fragment_source && !priv->fragment_is_glsl))
+    {
+      /* XXX: maybe this error message should be about only GLSL
+shaders supportes as of now
+       */
+      g_set_error (error, CLUTTER_SHADER_ERROR,
+                   CLUTTER_SHADER_ERROR_NO_ASM,
+                   "ASM shaders not supported");
+      priv->bound = FALSE;
+      return priv->bound;
+    }
+
+  if (!clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL))
     {
-      priv->bound = bind_glsl_shader (self);
+      g_set_error (error, CLUTTER_SHADER_ERROR,
+                   CLUTTER_SHADER_ERROR_NO_GLSL,
+                   "GLSL shaders not supported");
+      priv->bound = FALSE;
+      return priv->bound;
     }
 
+  priv->bound = bind_glsl_shader (shader, error);
+
+  if (priv->bound)
+    g_object_notify (G_OBJECT (shader), "bound");
+
   return priv->bound;
 }
 
+/**
+ * clutter_shader_release:
+ * @shader: a #ClutterShader
+ *
+ * FIXME
+ *
+ * Since: 0.6
+ */
 void
-clutter_shader_release (ClutterShader *self)
+clutter_shader_release (ClutterShader *shader)
 {
   ClutterShaderPrivate *priv;
 
-  priv = self->priv;
+  g_return_if_fail (CLUTTER_IS_SHADER (shader));
+
+  priv = shader->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;
-    }
+  if (priv->vertex_is_glsl && priv->vertex_shader)
+    cogl_shader_destroy (priv->vertex_shader);
+
+  if (priv->fragment_is_glsl && 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;
+
+  g_object_notify (G_OBJECT (shader), "bound");
 }
 
-static void
-clutter_shader_finalize (GObject *object)
+/**
+ * clutter_shader_is_bound:
+ * @shader: a #ClutterShader
+ *
+ * FIXME
+ *
+ * Return value: FIXME
+ *
+ * Since: 0.6
+ */
+gboolean
+clutter_shader_is_bound (ClutterShader *shader)
 {
-  ClutterShader        *shader;
-  ClutterShaderPrivate *priv;
+  g_return_val_if_fail (CLUTTER_IS_SHADER (shader), FALSE);
 
-  shader = CLUTTER_SHADER (object);
-  priv   = shader->priv;
+  return shader->priv->bound;
+}
 
-  clutter_shader_release (shader);
+/**
+ * clutter_shader_set_is_enabled:
+ * @shader: a #ClutterShader
+ * @enabled: FIXME
+ *
+ * FIXME
+ *
+ * Since: 0.6
+ */
+void
+clutter_shader_set_is_enabled (ClutterShader *shader,
+                               gboolean       enabled)
+{
+  ClutterShaderPrivate *priv;
 
-  shader_list = g_list_remove (shader_list, object);
+  g_return_if_fail (CLUTTER_IS_SHADER (shader));
 
-  if (priv->fragment_shader_source)
-    g_free (priv->fragment_shader_source);
-  if (priv->vertex_shader_source)
-    g_free (priv->vertex_shader_source);
+  priv = shader->priv;
 
-  G_OBJECT_CLASS (clutter_shader_parent_class)->finalize (object);
-}
+  if (priv->is_enabled != enabled)
+    {
+      GError *error = NULL;
+      gboolean res;
 
+      res = clutter_shader_bind (shader, &error);
+      if (!res)
+        {
+          g_warning ("Unable to bind the shader: %s",
+                     error ? error->message : "unknown error");
+          if (error)
+            g_error_free (error);
 
-void
-clutter_shader_enable (ClutterShader *self)
-{
-  ClutterShaderPrivate *priv = self->priv;
+          return;
+        }
 
-  clutter_shader_bind (self);
+      priv->is_enabled = enabled;
 
-  cogl_program_use (priv->program);
+      if (priv->is_enabled)
+        cogl_program_use (priv->program);
+      else
+        cogl_program_use (0);
+
+      g_object_notify (G_OBJECT (shader), "enabled");
+    }
 }
 
-void
-clutter_shader_disable (ClutterShader *self)
+/**
+ * clutter_shader_get_is_enabled:
+ * @shader: a #ClutterShader
+ *
+ * FIXME
+ *
+ * Return value: FIXME
+ *
+ * Since: 0.6
+ */
+gboolean
+clutter_shader_get_is_enabled (ClutterShader *shader)
 {
-  cogl_program_use (0);
+  g_return_val_if_fail (CLUTTER_IS_SHADER (shader), FALSE);
+
+  return shader->priv->is_enabled;
 }
 
+/**
+ * clutter_shader_set_uniform_1f:
+ * @shader: a #ClutterShader
+ * @name: FIXME
+ * @value: FIXME
+ *
+ * FIXME
+ *
+ * Since: 0.6
+ */
 void
-clutter_shader_set_uniform_1f (ClutterShader *self,
+clutter_shader_set_uniform_1f (ClutterShader *shader,
                                const gchar   *name,
                                gfloat         value)
 {
-  ClutterShaderPrivate *priv     = self->priv;
+  ClutterShaderPrivate *priv;
   GLint                 location = 0;
   GLfloat               foo      = value;
 
-  location =cogl_program_get_uniform_location (priv->program, name);
+  g_return_if_fail (CLUTTER_IS_SHADER (shader));
+
+  priv = shader->priv;
+
+  location = cogl_program_get_uniform_location (priv->program, name);
   cogl_program_uniform_1f (location, foo);
 }
 
+/**
+ * clutter_shader_release_all:
+ *
+ * FIXME
+ *
+ * Since: 0.6
+ */
 void
 clutter_shader_release_all (void)
 {
-  GList *iter;
-  for (iter = shader_list; iter; iter = g_list_next (iter))
-    {
-      clutter_shader_release (iter->data);
-    }
+  g_list_foreach (clutter_shaders_list,
+                  (GFunc) clutter_shader_release,
+                  NULL);
 }
 
-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)
+/**
+ * clutter_shader_get_fragment_source:
+ * @shader: a #ClutterShader
+ *
+ * FIXME
+ *
+ * Return value: the source of the fragment shader for this ClutterShader object
+ * or %NULL. The returned string is owned by the shader object and should never
+ * be modified or freed
+ *
+ * Since: 0.6
+ */
+G_CONST_RETURN gchar *
+clutter_shader_get_fragment_source (ClutterShader *shader)
 {
-  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;
+  g_return_val_if_fail (CLUTTER_IS_SHADER (shader), NULL);
+  return shader->priv->fragment_source;
 }
 
-ClutterShader *
-clutter_shader_new_from_strings (const gchar *vertex_shader_program,
-                                 const gchar *fragment_shader_program)
+/**
+ * clutter_shader_get_vertex_source:
+ * @shader: a #ClutterShader
+ *
+ * FIXME
+ *
+ * Return value: the source of the vertex shader for this ClutterShader object
+ * or %NULL. The returned string is owned by the shader object and should never
+ * be modified or freed
+ *
+ * Since: 0.6
+ */
+G_CONST_RETURN gchar *
+clutter_shader_get_vertex_source (ClutterShader *shader)
 {
-  /* 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;
-  }
+  g_return_val_if_fail (CLUTTER_IS_SHADER (shader), NULL);
+  return shader->priv->vertex_source;
 }
 
-ClutterShader *
-clutter_shader_new_from_files (const gchar *vertex_file,
-                               const gchar *fragment_file)
+GQuark
+clutter_shader_error_quark (void)
 {
-  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;
+  return g_quark_from_static_string ("clutter-shader-error");
 }
index 848dae2..7aeedd7 100644 (file)
  */
 
 
-#ifndef CLUTTER_SHADER_H
-#define CLUTTER_SHADER_H
+#ifndef __CLUTTER_SHADER_H__
+#define __CLUTTER_SHADER_H__
 
-#include <glib.h>
 #include <glib-object.h>
 
 G_BEGIN_DECLS
@@ -40,39 +39,61 @@ G_BEGIN_DECLS
 #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))
 
+#define CLUTTER_SHADER_ERROR        (clutter_shader_error_quark ())
+
+typedef enum {
+  CLUTTER_SHADER_ERROR_NO_ASM,
+  CLUTTER_SHADER_ERROR_NO_GLSL,
+  CLUTTER_SHADER_ERROR_COMPILE
+} ClutterShaderError;
+
 typedef struct _ClutterShader        ClutterShader;
 typedef struct _ClutterShaderPrivate ClutterShaderPrivate;
 typedef struct _ClutterShaderClass   ClutterShaderClass;
 
 struct _ClutterShader
 {
+  /*< private >*/
   GObject               parent;
   ClutterShaderPrivate *priv;
 };
 
 struct _ClutterShaderClass
 {
+  /*< private >*/
   GObjectClass parent_class;
 };
 
-GType           clutter_shader_get_type         ();
+GQuark                clutter_shader_error_quark         (void);
+GType                 clutter_shader_get_type            (void) G_GNUC_CONST;
 
-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);
+ClutterShader *       clutter_shader_new                 (void);
 
-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);
+void                  clutter_shader_set_is_enabled      (ClutterShader      *shader,
+                                                          gboolean            enabled);
+gboolean              clutter_shader_get_is_enabled      (ClutterShader      *shader);
+
+gboolean              clutter_shader_bind                (ClutterShader      *shader,
+                                                          GError            **error);
+void                  clutter_shader_release             (ClutterShader      *shader);
+gboolean              clutter_shader_is_bound            (ClutterShader      *shader);
 
+void                  clutter_shader_set_vertex_source   (ClutterShader      *shader,
+                                                          const gchar        *data,
+                                                          gssize              length);
+void                  clutter_shader_set_fragment_source (ClutterShader      *shader,
+                                                          const gchar        *data,
+                                                          gssize              length);
+
+G_CONST_RETURN gchar *clutter_shader_get_vertex_source   (ClutterShader      *shader);
+G_CONST_RETURN gchar *clutter_shader_get_fragment_source (ClutterShader      *shader);
+
+void                  clutter_shader_set_uniform_1f      (ClutterShader      *shader,
+                                                          const gchar        *name,
+                                                          gfloat              value);
+/* should be private and internal */
+void                  clutter_shader_release_all         (void);
 
 G_END_DECLS
 
-#endif /* CLUTTER_SHADER_H */
+#endif /* __CLUTTER_SHADER_H__ */
index d4e3ecc..de278b1 100644 (file)
@@ -1,4 +1,4 @@
-/* #define TEST_GROUP 1 */
+/*#define TEST_GROUP */
 
 #include <clutter/clutter.h>
 
@@ -127,7 +127,8 @@ static ShaderSource shaders[]=
     {NULL, NULL}
 };
 
-static gint shader_no=0;
+static gint shader_no = 0;
+  ClutterShader    *shader;
 
 static gboolean
 button_release_cb (ClutterActor    *actor,
@@ -135,26 +136,51 @@ button_release_cb (ClutterActor    *actor,
                    gpointer         data)
 {
   gint new_no;
+
   if (event->button.button == 1)
     {
-      new_no = shader_no-1;
+      new_no = shader_no - 1;
     }
   else
     {
-      new_no = shader_no+1;
+      new_no = shader_no + 1;
     }
 
   if (new_no >= 0 && shaders[new_no].name)
     {
       ClutterShader *shader;
+      GError *error;
       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);
+      g_print ("setting shaders[%i] named '%s'\n",
+               shader_no,
+               shaders[shader_no].name);
+
+      shader = clutter_shader_new ();
       
-      clutter_actor_set_shader_param (actor, "radius", 3.0);
-      clutter_redraw();
+      error = NULL;
+      g_object_set (G_OBJECT (shader), "fragment-source", shaders[shader_no].source, NULL);
+
+      /* try to bind the shader, provoking an error we catch if there is issues
+       * with the shader sources we've provided. At a later stage it should be possible to
+       * iterate through a set of alternate shader sources (glsl -> asm -> cg?) and the one
+       * that succesfully compiles is used.
+       */
+      clutter_shader_bind (shader, &error);
+
+      if (error)
+        {
+          g_print ("unable to set shaders[%i] named '%s': %s",
+                   shader_no, shaders[shader_no].name,
+                   error->message);
+          g_error_free (error);
+          clutter_actor_apply_shader (actor, NULL);
+        }
+      else
+        {
+          clutter_actor_apply_shader (actor, shader);
+          clutter_actor_set_shader_param (actor, "radius", 3.0);
+        }
     }
   return FALSE;
 }
@@ -171,21 +197,35 @@ main (gint   argc,
   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);
+  g_print ("applying shaders[%i] named '%s'\n",
+           shader_no,
+           shaders[shader_no].name);
+
+  shader = clutter_shader_new ();
+
+  error = NULL;
+  clutter_shader_set_fragment_source (shader, shaders[shader_no].source, NULL);
+  clutter_shader_bind (shader, &error);
+  if (error)
+    {
+      g_print ("unable to load shaders[%d] named '%s': %s\n",
+               shader_no,
+               shaders[shader_no].name,
+               error->message);
+      g_error_free (error);
+
+      return EXIT_FAILURE;
+    }
 
+  pixbuf = gdk_pixbuf_new_from_file ("redhand.png", &error);
   if (!pixbuf)
-    g_error("pixbuf load failed");
+    g_error("pixbuf load failed: %s", error ? error->message : "Unknown");
 
   clutter_stage_set_title (CLUTTER_STAGE (stage), "Shader Test");
   clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
@@ -200,7 +240,7 @@ main (gint   argc,
   actor = clutter_group_new ();
     {
       ClutterActor *child1, *child2, *child3, *child4;
-      ClutterColor  color={0xff, 0x22, 0x66, 0x99};
+      ClutterColor  color = { 0xff, 0x22, 0x66, 0x99 };
 
       child1 = clutter_texture_new_from_pixbuf (pixbuf);
       child2 = clutter_texture_new_from_pixbuf (pixbuf);
@@ -218,14 +258,16 @@ main (gint   argc,
       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_apply_shader (actor, shader);
   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);
@@ -242,5 +284,5 @@ main (gint   argc,
 
   clutter_main ();
 
-  return 0;
+  return EXIT_SUCCESS;
 }