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.
This commit is contained in:
Øyvind Kolås 2007-12-03 16:29:18 +00:00
parent ace69e71b3
commit e92b864dff
16 changed files with 1274 additions and 5 deletions

View File

@ -1,3 +1,30 @@
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

View 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 \

View 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);
}

View 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);

View 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);

View 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 ())

433
clutter/clutter-shader.c Normal file
View File

@ -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;
}

80
clutter/clutter-shader.h Normal file
View File

@ -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 */

View 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__ */

View 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

View 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);
}

View 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

View 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)
{
}

View 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)

View 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

246
tests/test-shader.c Normal file
View File

@ -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;
}