[shader] Unify code paths

ClutterShader contains a lot of duplication, as the vertex and fragment
shader code paths are mostly the same.

The code should be simplified by adding new internal functions that can
be called with a value from the already present ClutterShaderType
enumeration.

In the future it'll also be possible to deprecate the current split API
and expose the generic accessors instead.
This commit is contained in:
Emmanuele Bassi 2009-02-28 17:19:05 +00:00
parent 7d674db9d2
commit b030756dca
2 changed files with 200 additions and 145 deletions

View File

@ -3,10 +3,12 @@
* *
* An OpenGL based 'interactive canvas' library. * An OpenGL based 'interactive canvas' library.
* *
* Authored By Matthew Allum <mallum@openedhand.com> * Authored By: Matthew Allum <mallum@openedhand.com>
* Øyvind Kolås <pippin@o-hand.com> * Øyvind Kolås <pippin@o-hand.com>
* Emmanuele Bassi <ebassi@linux.intel.com>
* *
* Copyright (C) 2007 OpenedHand * Copyright (C) 2007, 2008 OpenedHand
* Copyright (C) 2009 Intel Corp
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
@ -48,6 +50,7 @@
#endif #endif
#include <glib.h> #include <glib.h>
#include <glib/gi18n-lib.h>
#include "cogl/cogl.h" #include "cogl/cogl.h"
@ -55,11 +58,10 @@
#include "clutter-private.h" #include "clutter-private.h"
#include "clutter-shader.h" #include "clutter-shader.h"
/* global list of shaders */
static GList *clutter_shaders_list = NULL; static GList *clutter_shaders_list = NULL;
#define CLUTTER_SHADER_GET_PRIVATE(obj) \ #define CLUTTER_SHADER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_SHADER, ClutterShaderPrivate))
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
CLUTTER_TYPE_SHADER, ClutterShaderPrivate))
typedef enum { typedef enum {
CLUTTER_VERTEX_SHADER, CLUTTER_VERTEX_SHADER,
@ -193,6 +195,7 @@ static void
clutter_shader_class_init (ClutterShaderClass *klass) clutter_shader_class_init (ClutterShaderClass *klass)
{ {
GObjectClass *object_class = G_OBJECT_CLASS (klass); GObjectClass *object_class = G_OBJECT_CLASS (klass);
GParamSpec *pspec = NULL;
object_class->finalize = clutter_shader_finalize; object_class->finalize = clutter_shader_finalize;
object_class->set_property = clutter_shader_set_property; object_class->set_property = clutter_shader_set_property;
@ -204,18 +207,18 @@ clutter_shader_class_init (ClutterShaderClass *klass)
/** /**
* ClutterShader:vertex-source: * ClutterShader:vertex-source:
* *
* GLSL source code for the vertex shader part of the shader program, if any. * GLSL source code for the vertex shader part of the shader
* program, if any
* *
* Since: 0.6 * Since: 0.6
*/ */
g_object_class_install_property pspec = g_param_spec_string ("vertex-source",
(object_class, "Vertex Source",
PROP_VERTEX_SOURCE, "Source of vertex shader",
g_param_spec_string ("vertex-source", NULL,
"Vertex Source", CLUTTER_PARAM_READWRITE);
"Source of vertex shader", g_object_class_install_property (object_class, PROP_VERTEX_SOURCE, pspec);
NULL,
CLUTTER_PARAM_READWRITE));
/** /**
* ClutterShader:fragment-source: * ClutterShader:fragment-source:
* *
@ -223,14 +226,13 @@ clutter_shader_class_init (ClutterShaderClass *klass)
* *
* Since: 0.6 * Since: 0.6
*/ */
g_object_class_install_property pspec = g_param_spec_string ("fragment-source",
(object_class, "Fragment Source",
PROP_FRAGMENT_SOURCE, "Source of fragment shader",
g_param_spec_string ("fragment-source", NULL,
"Fragment Source", CLUTTER_PARAM_READWRITE);
"Source of fragment shader", g_object_class_install_property (object_class, PROP_FRAGMENT_SOURCE, pspec);
NULL,
CLUTTER_PARAM_READWRITE));
/** /**
* ClutterShader:compiled: * ClutterShader:compiled:
* *
@ -239,14 +241,13 @@ clutter_shader_class_init (ClutterShaderClass *klass)
* *
* Since: 0.8 * Since: 0.8
*/ */
g_object_class_install_property pspec = g_param_spec_boolean ("compiled",
(object_class, "Compiled",
PROP_COMPILED, "Whether the shader is compiled and linked",
g_param_spec_boolean ("compiled", FALSE,
"Compiled", CLUTTER_PARAM_READABLE);
"Whether the shader is compiled and linked", g_object_class_install_property (object_class, PROP_COMPILED, pspec);
FALSE,
CLUTTER_PARAM_READABLE));
/** /**
* ClutterShader:enabled: * ClutterShader:enabled:
* *
@ -254,14 +255,12 @@ clutter_shader_class_init (ClutterShaderClass *klass)
* *
* Since: 0.6 * Since: 0.6
*/ */
g_object_class_install_property pspec = g_param_spec_boolean ("enabled",
(object_class, "Enabled",
PROP_ENABLED, "Whether the shader is enabled",
g_param_spec_boolean ("enabled", FALSE,
"Enabled", CLUTTER_PARAM_READWRITE);
"Whether the shader is enabled", g_object_class_install_property (object_class, PROP_ENABLED, pspec);
FALSE,
CLUTTER_PARAM_READWRITE));
} }
static void static void
@ -296,6 +295,55 @@ clutter_shader_new (void)
return g_object_new (CLUTTER_TYPE_SHADER, NULL); return g_object_new (CLUTTER_TYPE_SHADER, NULL);
} }
static inline void
clutter_shader_set_source (ClutterShader *shader,
ClutterShaderType shader_type,
const gchar *data,
gssize length)
{
ClutterShaderPrivate *priv = shader->priv;
gboolean is_glsl = FALSE;
if (length < 0)
length = strlen (data);
g_object_freeze_notify (G_OBJECT (shader));
/* release shader if bound when changing the source, the shader will
* automatically be rebound on the next use.
*/
if (clutter_shader_is_compiled (shader))
clutter_shader_release (shader);
is_glsl = !g_str_has_prefix (data, "!!ARBfp");
CLUTTER_NOTE (SHADER,
"setting %s shader (GLSL:%s, len:%" G_GSSIZE_FORMAT ")",
shader_type == CLUTTER_VERTEX_SHADER ? "vertex" : "fragment",
is_glsl ? "yes" : "no",
length);
switch (shader_type)
{
case CLUTTER_FRAGMENT_SHADER:
g_free (priv->fragment_source);
priv->fragment_source = g_strndup (data, length);
priv->fragment_is_glsl = is_glsl;
g_object_notify (G_OBJECT (shader), "fragment-source");
break;
case CLUTTER_VERTEX_SHADER:
g_free (priv->vertex_source);
priv->vertex_source = g_strndup (data, length);
priv->vertex_is_glsl = is_glsl;
g_object_notify (G_OBJECT (shader), "vertex-source");
break;
}
g_object_thaw_notify (G_OBJECT (shader));
}
/** /**
* clutter_shader_set_fragment_source: * clutter_shader_set_fragment_source:
@ -313,36 +361,10 @@ clutter_shader_set_fragment_source (ClutterShader *shader,
const gchar *data, const gchar *data,
gssize length) gssize length)
{ {
ClutterShaderPrivate *priv;
gboolean is_glsl;
/* FIXME: do not ignore length, since we are exposing it in the API */
if (shader == NULL)
g_error ("quack!");
g_return_if_fail (CLUTTER_IS_SHADER (shader)); g_return_if_fail (CLUTTER_IS_SHADER (shader));
g_return_if_fail (data != NULL); g_return_if_fail (data != NULL);
priv = shader->priv; clutter_shader_set_source (shader, CLUTTER_FRAGMENT_SHADER, data, length);
/* release shader if bound when changing the source, the shader will
* automatically be rebound on the next use.
*/
if (clutter_shader_is_compiled (shader))
clutter_shader_release (shader);
is_glsl = !g_str_has_prefix (data, "!!ARBfp");
g_free (priv->fragment_source);
CLUTTER_NOTE (SHADER,
"setting fragment shader (GLSL:%s, len:%" G_GSSIZE_FORMAT ")",
is_glsl ? "yes" : "no",
length);
priv->fragment_source = g_strdup (data);
priv->fragment_is_glsl = is_glsl;
} }
/** /**
@ -361,101 +383,132 @@ clutter_shader_set_vertex_source (ClutterShader *shader,
const gchar *data, const gchar *data,
gssize length) 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 (CLUTTER_IS_SHADER (shader));
g_return_if_fail (data != NULL); g_return_if_fail (data != NULL);
priv = shader->priv; clutter_shader_set_source (shader, CLUTTER_VERTEX_SHADER, data, length);
}
/* release shader if bound when changing the source, the shader will static const gchar *
* automatically be rebound on the next use. clutter_shader_get_source (ClutterShader *shader,
*/ ClutterShaderType shader_type)
if (clutter_shader_is_compiled (shader)) {
clutter_shader_release (shader); switch (shader_type)
{
case CLUTTER_FRAGMENT_SHADER:
return shader->priv->fragment_source;
is_glsl = !g_str_has_prefix (data, "!!ARBvp"); case CLUTTER_VERTEX_SHADER:
return shader->priv->vertex_source;
}
g_free (priv->vertex_source); return NULL;
}
CLUTTER_NOTE (SHADER, static CoglHandle
"setting vertex shader (GLSL:%s, len:%" G_GSSIZE_FORMAT ")", clutter_shader_get_cogl_shader (ClutterShader *shader,
is_glsl ? "yes" : "no", ClutterShaderType shader_type)
length); {
switch (shader_type)
{
case CLUTTER_FRAGMENT_SHADER:
return shader->priv->fragment_shader;
priv->vertex_source = g_strdup (data); case CLUTTER_VERTEX_SHADER:
priv->vertex_is_glsl = is_glsl; return shader->priv->vertex_shader;
}
return COGL_INVALID_HANDLE;
}
static gboolean
clutter_shader_glsl_bind (ClutterShader *self,
ClutterShaderType shader_type,
GError **error)
{
ClutterShaderPrivate *priv = self->priv;
GLint is_compiled = CGL_FALSE;
CoglHandle shader = COGL_INVALID_HANDLE;
switch (shader_type)
{
case CLUTTER_VERTEX_SHADER:
shader = cogl_create_shader (CGL_VERTEX_SHADER);
cogl_shader_source (shader, priv->vertex_source);
priv->vertex_shader = shader;
break;
case CLUTTER_FRAGMENT_SHADER:
shader = cogl_create_shader (CGL_FRAGMENT_SHADER);
cogl_shader_source (shader, priv->fragment_source);
priv->fragment_shader = shader;
break;
}
g_assert (shader != COGL_INVALID_HANDLE);
cogl_shader_compile (shader);
cogl_shader_get_parameteriv (shader,
CGL_OBJECT_COMPILE_STATUS,
&is_compiled);
if (is_compiled != CGL_TRUE)
{
gchar error_buf[512];
cogl_shader_get_info_log (shader, 512, error_buf);
g_set_error (error, CLUTTER_SHADER_ERROR,
CLUTTER_SHADER_ERROR_COMPILE,
_("%s compilation failed: %s"),
shader_type == CLUTTER_VERTEX_SHADER ? _("Vertex shader")
: _("Fragment shader"),
error_buf);
return FALSE;
}
cogl_program_attach_shader (priv->program, shader);
return TRUE;
} }
static gboolean static gboolean
bind_glsl_shader (ClutterShader *self, bind_glsl_shader (ClutterShader *self,
GError **error) GError **error)
{ {
ClutterShaderPrivate *priv; ClutterShaderPrivate *priv = self->priv;
priv = self->priv; GError *bind_error = NULL;
gboolean res;
priv->program = cogl_create_program (); priv->program = cogl_create_program ();
if (priv->vertex_is_glsl && priv->vertex_source != COGL_INVALID_HANDLE) if (priv->vertex_is_glsl && priv->vertex_source != COGL_INVALID_HANDLE)
{ {
GLint compiled = CGL_FALSE; res = clutter_shader_glsl_bind (self,
CLUTTER_VERTEX_SHADER,
&bind_error);
priv->vertex_shader = cogl_create_shader (CGL_VERTEX_SHADER); if (!res)
cogl_shader_source (priv->vertex_shader, priv->vertex_source);
cogl_shader_compile (priv->vertex_shader);
cogl_shader_get_parameteriv (priv->vertex_shader,
CGL_OBJECT_COMPILE_STATUS,
&compiled);
if (compiled != CGL_TRUE)
{ {
gchar error_buf[512]; g_propagate_error (error, bind_error);
cogl_shader_get_info_log (priv->vertex_shader, 512, error_buf);
g_set_error (error, CLUTTER_SHADER_ERROR,
CLUTTER_SHADER_ERROR_COMPILE,
"Vertex shader compilation failed: %s",
error_buf);
return FALSE; return FALSE;
} }
else
cogl_program_attach_shader (priv->program, priv->vertex_shader);
} }
if (priv->fragment_is_glsl && priv->fragment_source != COGL_INVALID_HANDLE) if (priv->fragment_is_glsl && priv->fragment_source != COGL_INVALID_HANDLE)
{ {
GLint compiled = CGL_FALSE; res = clutter_shader_glsl_bind (self,
CLUTTER_FRAGMENT_SHADER,
&bind_error);
priv->fragment_shader = cogl_create_shader (CGL_FRAGMENT_SHADER); if (!res)
cogl_shader_source (priv->fragment_shader, priv->fragment_source);
cogl_shader_compile (priv->fragment_shader);
cogl_shader_get_parameteriv (priv->fragment_shader,
CGL_OBJECT_COMPILE_STATUS,
&compiled);
if (compiled != CGL_TRUE)
{ {
gchar error_buf[512]; g_propagate_error (error, bind_error);
cogl_shader_get_info_log (priv->fragment_shader, 512, error_buf);
g_set_error (error, CLUTTER_SHADER_ERROR,
CLUTTER_SHADER_ERROR_COMPILE,
"Fragment shader compilation failed: %s",
error_buf);
return FALSE; return FALSE;
} }
else
cogl_program_attach_shader (priv->program, priv->fragment_shader);
} }
cogl_program_link (priv->program); cogl_program_link (priv->program);
@ -652,23 +705,19 @@ clutter_shader_get_is_enabled (ClutterShader *shader)
* to a #ClutterShader. * to a #ClutterShader.
* *
* Since: 0.6 * Since: 0.6
*
* Deprecated: 1.0: Use clutter_shader_set_uniform() instead
*/ */
void void
clutter_shader_set_uniform_1f (ClutterShader *shader, clutter_shader_set_uniform_1f (ClutterShader *shader,
const gchar *name, const gchar *name,
gfloat value) gfloat value)
{ {
ClutterShaderPrivate *priv; GValue real_value = { 0, };
GLint location = 0;
GLfloat foo = value;
g_return_if_fail (CLUTTER_IS_SHADER (shader)); g_value_init (&real_value, G_TYPE_FLOAT);
g_return_if_fail (name != NULL); clutter_shader_set_uniform (shader, name, &real_value);
g_value_unset (&real_value);
priv = shader->priv;
location = cogl_program_get_uniform_location (priv->program, name);
cogl_program_uniform_1f (location, foo);
} }
/** /**
@ -757,7 +806,6 @@ _clutter_shader_release_all (void)
NULL); NULL);
} }
/** /**
* clutter_shader_get_fragment_source: * clutter_shader_get_fragment_source:
* @shader: a #ClutterShader * @shader: a #ClutterShader
@ -774,7 +822,8 @@ G_CONST_RETURN gchar *
clutter_shader_get_fragment_source (ClutterShader *shader) clutter_shader_get_fragment_source (ClutterShader *shader)
{ {
g_return_val_if_fail (CLUTTER_IS_SHADER (shader), NULL); g_return_val_if_fail (CLUTTER_IS_SHADER (shader), NULL);
return shader->priv->fragment_source;
return clutter_shader_get_source (shader, CLUTTER_FRAGMENT_SHADER);
} }
/** /**
@ -793,7 +842,8 @@ G_CONST_RETURN gchar *
clutter_shader_get_vertex_source (ClutterShader *shader) clutter_shader_get_vertex_source (ClutterShader *shader)
{ {
g_return_val_if_fail (CLUTTER_IS_SHADER (shader), NULL); g_return_val_if_fail (CLUTTER_IS_SHADER (shader), NULL);
return shader->priv->vertex_source;
return clutter_shader_get_source (shader, CLUTTER_VERTEX_SHADER);
} }
/** /**
@ -810,6 +860,7 @@ CoglHandle
clutter_shader_get_cogl_program (ClutterShader *shader) clutter_shader_get_cogl_program (ClutterShader *shader)
{ {
g_return_val_if_fail (CLUTTER_IS_SHADER (shader), NULL); g_return_val_if_fail (CLUTTER_IS_SHADER (shader), NULL);
return shader->priv->program; return shader->priv->program;
} }
@ -827,7 +878,8 @@ CoglHandle
clutter_shader_get_cogl_fragment_shader (ClutterShader *shader) clutter_shader_get_cogl_fragment_shader (ClutterShader *shader)
{ {
g_return_val_if_fail (CLUTTER_IS_SHADER (shader), NULL); g_return_val_if_fail (CLUTTER_IS_SHADER (shader), NULL);
return shader->priv->fragment_shader;
return clutter_shader_get_cogl_shader (shader, CLUTTER_FRAGMENT_SHADER);
} }
/** /**
@ -844,7 +896,8 @@ CoglHandle
clutter_shader_get_cogl_vertex_shader (ClutterShader *shader) clutter_shader_get_cogl_vertex_shader (ClutterShader *shader)
{ {
g_return_val_if_fail (CLUTTER_IS_SHADER (shader), NULL); g_return_val_if_fail (CLUTTER_IS_SHADER (shader), NULL);
return shader->priv->vertex_shader;
return clutter_shader_get_cogl_shader (shader, CLUTTER_VERTEX_SHADER);
} }
GQuark GQuark

View File

@ -1,3 +1,4 @@
# keep sorted alphabetically!
clutter/clutter-actor.c clutter/clutter-actor.c
clutter/clutter-behaviour.c clutter/clutter-behaviour.c
clutter/clutter-color.c clutter/clutter-color.c
@ -6,6 +7,7 @@ clutter/clutter-event.c
clutter/clutter-fixed.c clutter/clutter-fixed.c
clutter/clutter-fixed.h clutter/clutter-fixed.h
clutter/clutter-main.c clutter/clutter-main.c
clutter/clutter-shader.c
clutter/clutter-stage-window.c clutter/clutter-stage-window.c
clutter/clutter-stage.c clutter/clutter-stage.c
clutter/clutter-texture.c clutter/clutter-texture.c