From 8366a906e2d46bd76eae2897d24d495259622841 Mon Sep 17 00:00:00 2001 From: Matthew Allum Date: Fri, 1 Feb 2008 15:29:00 +0000 Subject: [PATCH] 2008-02-01 Matthew Allum * clutter/clutter-feature.h: * clutter/clutter-texture.c: * clutter/clutter-texture.h: * clutter/cogl/cogl.h: * clutter/cogl/gl/cogl.c: * clutter/cogl/gles/cogl.c: * tests/Makefile.am: * tests/test.fbo.c: Add initial support for FBO's in Clutter (OpenGL only so far). See new clutter_texture_new_from_actor() Initial implementation, needs work. * clutter/x11/clutter-stage-x11.c: (clutter_stage_x11_set_cursor_visible): Fall back to again not relying on xfixes to hide cursor. *sigh* * clutter/clutter-deprecated.h: Add clutter_group_find_child_by_id --- ChangeLog | 21 ++++++ clutter/clutter-deprecated.h | 2 + clutter/clutter-feature.h | 4 +- clutter/clutter-texture.c | 106 ++++++++++++++++++++++++++++ clutter/clutter-texture.h | 2 +- clutter/cogl/cogl.h | 17 +++++ clutter/cogl/gl/cogl.c | 153 +++++++++++++++++++++++++++++++++++++++- clutter/cogl/gles/cogl.c | 30 ++++++++ clutter/x11/clutter-stage-x11.c | 6 +- tests/Makefile.am | 3 +- tests/test-fbo.c | 100 ++++++++++++++++++++++++++ 11 files changed, 438 insertions(+), 6 deletions(-) create mode 100644 tests/test-fbo.c diff --git a/ChangeLog b/ChangeLog index 4a04302..c0905d3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,24 @@ +2008-02-01 Matthew Allum + + * clutter/clutter-feature.h: + * clutter/clutter-texture.c: + * clutter/clutter-texture.h: + * clutter/cogl/cogl.h: + * clutter/cogl/gl/cogl.c: + * clutter/cogl/gles/cogl.c: + * tests/Makefile.am: + * tests/test.fbo.c: + Add initial support for FBO's in Clutter (OpenGL only so far). + See new clutter_texture_new_from_actor() + Initial implementation, needs work. + + * clutter/x11/clutter-stage-x11.c: + (clutter_stage_x11_set_cursor_visible): + Fall back to again not relying on xfixes to hide cursor. *sigh* + + * clutter/clutter-deprecated.h: + Add clutter_group_find_child_by_id + 2008-02-01 Øyvind KolÃ¥s * clutter/cogl/gles/cogl.c: (cogl_rectangle_internal): increase size diff --git a/clutter/clutter-deprecated.h b/clutter/clutter-deprecated.h index e79ffc8..1c2b529 100644 --- a/clutter/clutter-deprecated.h +++ b/clutter/clutter-deprecated.h @@ -11,6 +11,8 @@ * new functionality should giver errors containing _DEPRECATED_BY_. */ +#define clutter_group_find_child_by_id clutter_group_find_child_by_id_REPLACED_BY_clutter_container_find_child_by_name + #define clutter_behaviour_ellipse_set_angle_begin clutter_behaviour_ellipse_set_angle_begin_REPLACED_BY_clutter_behaviour_set_angle_start #define clutter_behaviour_ellipse_set_angle_beginx clutter_behaviour_ellipse_set_angle_beginx_REPLACED_BY_clutter_behaviour_set_angle_startx #define clutter_behaviour_ellipse_get_angle_begin clutter_behaviour_ellipse_get_angle_begin_REPLACED_BY_clutter_behaviour_get_angle_start diff --git a/clutter/clutter-feature.h b/clutter/clutter-feature.h index 0fc013b..ec771ba 100644 --- a/clutter/clutter-feature.h +++ b/clutter/clutter-feature.h @@ -47,6 +47,7 @@ G_BEGIN_DECLS * @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_SHADERS_GLSL: Set if the backend supports GLSL shaders. + * @CLUTTER_FEATURE_OFFSCREEN: Set if the backend supports offscreen rendering. * * Runtime flags indicating specific features available via Clutter window * sysytem and graphics backend. @@ -62,7 +63,8 @@ typedef enum CLUTTER_FEATURE_STAGE_STATIC = (1 << 5), CLUTTER_FEATURE_STAGE_USER_RESIZE = (1 << 6), CLUTTER_FEATURE_STAGE_CURSOR = (1 << 7), - CLUTTER_FEATURE_SHADERS_GLSL = (1 << 8) + CLUTTER_FEATURE_SHADERS_GLSL = (1 << 8), + CLUTTER_FEATURE_OFFSCREEN = (1 << 9) } ClutterFeatureFlags; gboolean clutter_feature_available (ClutterFeatureFlags feature); diff --git a/clutter/clutter-texture.c b/clutter/clutter-texture.c index 8366c53..0df4041 100644 --- a/clutter/clutter-texture.c +++ b/clutter/clutter-texture.c @@ -96,6 +96,9 @@ struct _ClutterTexturePrivate gint n_x_tiles; gint n_y_tiles; COGLuint *tiles; + + ClutterActor *fbo_source; + COGLuint fbo_handle; }; enum @@ -668,6 +671,9 @@ clutter_texture_realize (ClutterActor *actor) texture = CLUTTER_TEXTURE(actor); priv = texture->priv; + if (priv->fbo_handle) + return; /* handle better */ + CLUTTER_MARK(); if (priv->local_pixbuf != NULL) @@ -751,6 +757,19 @@ clutter_texture_paint (ClutterActor *self) return; } + if (priv->fbo_handle) + { + cogl_offscreen_redirect_start (priv->fbo_handle, + priv->width, priv->height); + clutter_actor_paint (priv->fbo_source); + cogl_offscreen_redirect_end (priv->fbo_handle, + CLUTTER_STAGE_WIDTH(), + CLUTTER_STAGE_HEIGHT()); + + glBindTexture(CGL_TEXTURE_RECTANGLE_ARB, priv->tiles[0]); + } + + CLUTTER_NOTE (PAINT, "painting texture '%s'", clutter_actor_get_name (self) ? clutter_actor_get_name (self) @@ -2083,3 +2102,90 @@ clutter_texture_set_area_from_rgb_data (ClutterTexture *texture, return TRUE; } + +/** + * clutter_texture_new_from_actor: + * @actor: A #ClutterActor + * + * Creates a new #ClutterTexture object with its source a prexisting + * actor (and associated children). + * + * Note this function is intented as a utility call for uniformly applying + * shaders to groups and other potentail visual effects. It requires the + * #CLUTTER_FEATURE_TEXTURE_RECTANGLE & #CLUTTER_FEATURE_OFFSCREEN features + * are supported by both the current backend and target system + * + * Return value: A newly created #ClutterTexture object or NULL on fail. + **/ +ClutterActor * +clutter_texture_new_from_actor (ClutterActor *actor) +{ + ClutterTexture *texture; + ClutterTexturePrivate *priv; + guint w, h; + + + /* TODO (before 0.6 release): + * + * - Figure out getting source actor size correctly. + * - Figure out refing/reparenting source actor. + * - Handle source actor resizing. + * - Handle failure better. + * - Handle cleanup on destruction. + * - Beef up test-fbo. + * - Have the source actor as a prop? + */ + + if (clutter_feature_available (CLUTTER_FEATURE_TEXTURE_RECTANGLE) == FALSE) + return NULL; + + if (clutter_feature_available (CLUTTER_FEATURE_OFFSCREEN) == FALSE) + return NULL; + + texture = g_object_new (CLUTTER_TYPE_TEXTURE, NULL); + + priv = texture->priv; + + priv->fbo_source = actor; + + if (!CLUTTER_ACTOR_IS_REALIZED (priv->fbo_source)) + clutter_actor_realize (priv->fbo_source); + + + /* FIXME: just ref ? */ + clutter_actor_set_parent (actor, CLUTTER_ACTOR(texture)); + + /* FIXME abs size */ + /*clutter_actor_get_abs_size (priv->fbo_source, &w, &h);*/ + clutter_actor_get_size (actor, &w, &h); + + /* FIXME: Check we can actually create a texture this large */ + priv->width = w; + priv->height = h; + + priv->target_type = CGL_TEXTURE_RECTANGLE_ARB; + priv->pixel_format = CGL_RGBA; + priv->pixel_type = PIXEL_TYPE; + priv->is_tiled = 0; + + priv->tiles = g_new (COGLuint, 1); + + /* FIXME: needs a cogl wrapper */ + glGenTextures (1, priv->tiles); + + cogl_texture_bind (priv->target_type, priv->tiles[0]); + + cogl_texture_image_2d (priv->target_type, + CGL_RGBA, + w, + h, + priv->pixel_format, + priv->pixel_type, + NULL); + + priv->fbo_handle = cogl_offscreen_create (priv->tiles[0]); + + clutter_actor_set_size (actor, w, h); + + return CLUTTER_ACTOR(texture); +} diff --git a/clutter/clutter-texture.h b/clutter/clutter-texture.h index 9ce9a07..8aeb749 100644 --- a/clutter/clutter-texture.h +++ b/clutter/clutter-texture.h @@ -119,7 +119,7 @@ gboolean clutter_texture_set_from_rgb_data (ClutterTexture *texture, gint bpp, ClutterTextureFlags flags, GError **error); - +ClutterActor *clutter_texture_new_from_actor (ClutterActor *actor); gboolean clutter_texture_set_area_from_rgb_data (ClutterTexture *texture, const guchar *data, gboolean has_alpha, diff --git a/clutter/cogl/cogl.h b/clutter/cogl/cogl.h index ae3d2a0..c037172 100644 --- a/clutter/cogl/cogl.h +++ b/clutter/cogl/cogl.h @@ -264,6 +264,23 @@ void cogl_program_uniform_1f (COGLint uniform_no, gfloat value); +/* Offscreen - FBO support */ + +COGLuint +cogl_offscreen_create (COGLuint target_texture); + +void +cogl_offscreen_destroy (COGLuint offscreen_handle); + +void +cogl_offscreen_redirect_start (COGLuint offscreen_handle, + gint width, + gint height); + +void +cogl_offscreen_redirect_end (COGLuint offscreen_handle, + gint width, + gint height); G_END_DECLS diff --git a/clutter/cogl/gl/cogl.c b/clutter/cogl/gl/cogl.c index a09914c..25f1f1d 100644 --- a/clutter/cogl/gl/cogl.c +++ b/clutter/cogl/gl/cogl.c @@ -41,6 +41,21 @@ typedef CoglFuncPtr (*GLXGetProcAddressProc) (const guint8 *procName); static gulong __enable_flags = 0; +/* FBO Procs */ +typedef void (*GenFramebuffers) (GLsizei n, GLuint *ids); +typedef void (*BindFramebuffer) (GLenum target, GLuint framebuffer); +typedef void (*FramebufferTexture2D) (GLenum target, GLenum attachment, + GLenum textarget, GLuint texture, + GLint level); +typedef GLenum (*CheckFramebufferStatus)(GLenum target); +typedef void (*DeleteFramebuffers) (GLsizei n, const GLuint *framebuffers); + +static GenFramebuffers _gen_framebuffers = NULL; +static BindFramebuffer _bind_framebuffer = NULL; +static FramebufferTexture2D _framebuffer_texture_2d = NULL; +static CheckFramebufferStatus _check_framebuffer_status = NULL; +static DeleteFramebuffers _delete_framebuffers = NULL; + #if COGL_DEBUG struct token_string { @@ -189,7 +204,6 @@ cogl_paint_init (const ClutterColor *color) glDepthFunc (GL_LEQUAL); cogl_enable (CGL_ENABLE_BLEND); - glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); } @@ -295,6 +309,7 @@ cogl_enable (gulong flags) } #endif +#if 0 if (flags & CGL_ENABLE_ALPHA_TEST) { if (!(__enable_flags & CGL_ENABLE_ALPHA_TEST)) @@ -308,6 +323,7 @@ cogl_enable (gulong flags) glDisable (GL_ALPHA_TEST); __enable_flags &= ~CGL_ENABLE_ALPHA_TEST; } +#endif } void @@ -648,6 +664,35 @@ cogl_get_features () flags |= CLUTTER_FEATURE_SHADERS_GLSL; } + if (cogl_check_extension ("GL_EXT_framebuffer_object", gl_extensions) || + cogl_check_extension ("GL_ARB_framebuffer_object", gl_extensions)) + { + _gen_framebuffers = + (GenFramebuffers) cogl_get_proc_address ("glGenFramebuffersEXT"); + + _bind_framebuffer = + (BindFramebuffer) cogl_get_proc_address ("glBindFramebufferEXT"); + + _framebuffer_texture_2d = + (FramebufferTexture2D) + cogl_get_proc_address ("glFramebufferTexture2DEXT"); + + _check_framebuffer_status = + (CheckFramebufferStatus) + cogl_get_proc_address ("glCheckFramebufferStatusEXT"); + + _delete_framebuffers = + (DeleteFramebuffers) + cogl_get_proc_address ("glDeleteFramebuffersEXT"); + + if (_gen_framebuffers + && _bind_framebuffer + && _framebuffer_texture_2d + && _check_framebuffer_status + && _delete_framebuffers) + flags |= CLUTTER_FEATURE_OFFSCREEN; + } + return flags; } @@ -774,6 +819,112 @@ cogl_fog_set (const ClutterColor *fog_color, glFogf (GL_FOG_END, CLUTTER_FIXED_TO_FLOAT (stop)); } +/* FBOs - offscreen */ + +COGLuint +cogl_offscreen_create (COGLuint target_texture) +{ +#ifdef GL_FRAMEBUFFER_EXT + COGLuint handle; + GLenum status; + + if (_gen_framebuffers == NULL + || _bind_framebuffer == NULL + || _framebuffer_texture_2d == NULL + || _check_framebuffer_status == NULL) + { + /* tmp warning - need error reporting */ + g_warning("Missing GL_FRAMEBUFFER_EXT API\n"); + return 0; + } + + _gen_framebuffers (1, &handle); + _bind_framebuffer (GL_FRAMEBUFFER_EXT, handle); + + _framebuffer_texture_2d (GL_FRAMEBUFFER_EXT, + GL_COLOR_ATTACHMENT0_EXT, + GL_TEXTURE_RECTANGLE_ARB, + target_texture, + 0); + + status = _check_framebuffer_status (GL_FRAMEBUFFER_EXT); + + if (status != GL_FRAMEBUFFER_COMPLETE_EXT) + { + _delete_framebuffers (1, &handle); + return 0; + } + + _bind_framebuffer (GL_FRAMEBUFFER_EXT, 0); + + return handle; +#else + /* tmp warning - need error reporting */ + g_warning("No GL_FRAMEBUFFER_EXT\n"); + return 0; +#endif +} + +void +cogl_offscreen_destroy (COGLuint offscreen_handle) +{ + if (_delete_framebuffers) + _delete_framebuffers (1, &offscreen_handle); +} + +void +cogl_offscreen_redirect_start (COGLuint offscreen_handle, + gint width, + gint height) +{ + /* FIXME: silly we need to pass width / height to init viewport */ +#ifdef GL_FRAMEBUFFER_EXT + + if (_bind_framebuffer == NULL) + return; + + _bind_framebuffer (GL_FRAMEBUFFER_EXT, offscreen_handle); + + glViewport (0, 0, width, height); + + glMatrixMode (GL_PROJECTION); + glPushMatrix(); + glLoadIdentity (); + + glMatrixMode (GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity (); + + glTranslatef (-1.0f, -1.0f, 0.0f); + glScalef (2.0f / (float)width, 2.0f / (float)height, 1.0f); +#endif +} + +void +cogl_offscreen_redirect_end (COGLuint offscreen_handle, + gint width, + gint height) +{ + /* FIXME: silly we need to pass width / height to reset to */ + if (_bind_framebuffer == NULL) + return; + +#ifdef GL_FRAMEBUFFER_EXT + glViewport (0, 0, width, height); + + glMatrixMode (GL_PROJECTION); + glPopMatrix(); + + glMatrixMode (GL_MODELVIEW); + glPopMatrix(); + + _bind_framebuffer (GL_FRAMEBUFFER_EXT, 0); +#endif +} + + +/* Shader Magic follows */ + #ifdef __GNUC__ #define PROC(rettype, retval, procname, args...) \ diff --git a/clutter/cogl/gles/cogl.c b/clutter/cogl/gles/cogl.c index b1a15da..f972dc9 100644 --- a/clutter/cogl/gles/cogl.c +++ b/clutter/cogl/gles/cogl.c @@ -645,6 +645,36 @@ cogl_fog_set (const ClutterColor *fog_color, glFogx (GL_FOG_END, (GLfixed) z_far); } +/* Offscreen - TODO: possible support from FBO's */ + +COGLuint +cogl_offscreen_create (COGLuint target_texture) +{ + return 0; +} + +void +cogl_offscreen_destroy (COGLuint offscreen_handle) +{ +} + +void +cogl_offscreen_redirect_start (COGLuint offscreen_handle, + gint width, + gint height) +{ +} + +void +cogl_offscreen_redirect_end (COGLuint offscreen_handle, + gint width, + gint height) +{ +} + + +/* Shaders, no support on regular OpenGL 1.1 */ + COGLhandle cogl_create_program (void) { diff --git a/clutter/x11/clutter-stage-x11.c b/clutter/x11/clutter-stage-x11.c index 8017aff..7e7fcbc 100644 --- a/clutter/x11/clutter-stage-x11.c +++ b/clutter/x11/clutter-stage-x11.c @@ -308,7 +308,7 @@ clutter_stage_x11_set_cursor_visible (ClutterStage *stage, if (show_cursor) { -#if HAVE_XFIXES +#if 0 /* HAVE_XFIXES - seems buggy/unreliable */ XFixesShowCursor (stage_x11->xdpy, stage_x11->xwin); #else XUndefineCursor (stage_x11->xdpy, stage_x11->xwin); @@ -316,7 +316,9 @@ clutter_stage_x11_set_cursor_visible (ClutterStage *stage, } else { -#if HAVE_XFIXES +#if 0 /* HAVE_XFIXES - seems buggy/unreliable, check cursor in firefox + * loading page after hiding. + */ XFixesHideCursor (stage_x11->xdpy, stage_x11->xwin); #else XColor col; diff --git a/tests/Makefile.am b/tests/Makefile.am index 17a06b0..e6f8698 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -3,7 +3,7 @@ noinst_PROGRAMS = test-textures test-events test-offscreen test-scale \ test-perspective test-rotate test-depth \ test-threads test-timeline test-score test-script \ test-model test-grab test-effects test-fullscreen \ - test-shader test-unproject test-viewport + test-shader test-unproject test-viewport test-fbo INCLUDES = -I$(top_srcdir)/ LDADD = $(top_builddir)/clutter/libclutter-@CLUTTER_FLAVOUR@-@CLUTTER_MAJORMINOR@.la @@ -33,5 +33,6 @@ test_model_SOURCES = test-model.c test_effects_SOURCES = test-effects.c test_fullscreen_SOURCES = test-fullscreen.c test_viewport_SOURCES = test-viewport.c +test_fbo_SOURCES = test-fbo.c EXTRA_DIST = redhand.png test-script.json diff --git a/tests/test-fbo.c b/tests/test-fbo.c new file mode 100644 index 0000000..0bacced --- /dev/null +++ b/tests/test-fbo.c @@ -0,0 +1,100 @@ +/*#define TEST_GROUP */ + +#include + +#include +#include +#include + + +gint +main (gint argc, + gchar *argv[]) +{ + ClutterColor color={0x33, 0x44, 0x55, 0xff}; + ClutterActor *fbo; + ClutterActor *actor; + ClutterActor *actor2; + ClutterActor *group; + ClutterShader *shader; + ClutterActor *stage; + ClutterActor *rectangle; + ClutterActor *clone; + GdkPixbuf *pixbuf; + GError *error = NULL; + + clutter_init (&argc, &argv); + + stage = clutter_stage_get_default (); + clutter_stage_set_color (CLUTTER_STAGE (stage), &color); + + pixbuf = gdk_pixbuf_new_from_file ("redhand.png", &error); + if (!pixbuf) + g_error("pixbuf load failed: %s", error ? error->message : "Unknown"); + + /* actor = clutter_texture_new_from_pixbuf (pixbuf);*/ + + group = clutter_group_new (); + { + ClutterColor nothing = {0, 0,0,0}; + rectangle = clutter_rectangle_new_with_color (¬hing); + clutter_actor_set_size (rectangle, 800, 270); + } + + actor2 = clutter_texture_new_from_pixbuf (pixbuf); + clutter_container_add_actor (CLUTTER_CONTAINER (group), actor2); + { + ClutterColor yellow = {0xff, 0xff, 0x00, 0xff}; + actor = clutter_label_new_with_text ("Sans 50px", "Hello hadyness"); + clutter_label_set_color (CLUTTER_LABEL (actor), &yellow); + } + + clutter_container_add_actor (CLUTTER_CONTAINER (group), actor); + clutter_container_add_actor (CLUTTER_CONTAINER (group), rectangle); + clutter_actor_set_position (actor, 0, 15); + + clutter_actor_show_all (group); + + fbo = clutter_texture_new_from_actor (group); + + clutter_container_add_actor (CLUTTER_CONTAINER (stage), fbo); + clutter_actor_set_position (fbo, 20, 120); + clutter_actor_set_position (actor2, 130, 20); + + shader = clutter_shader_new (); + clutter_shader_set_fragment_source (shader, + + "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