diff --git a/ChangeLog b/ChangeLog index af8201b68..c3ecfca67 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,22 @@ +2008-10-29 Neil Roberts + + Bug 1074 - FBOs on GLES + + * clutter/cogl/gles/cogl-fbo.c: Copy the code from gl/cogl-fbo but + use the API calls directly instead of loading an extension because + it is part of the core in GLES 2. + + * clutter/cogl/gles/cogl.c (_cogl_features_init): Report + COGL_FEATURE_OFFSCREEN. + + * clutter/cogl/gles/cogl-fbo.h (CoglFbo): Add gl_stencil_handle. + + * clutter/cogl/gles/cogl-context.h (CoglContext): Add + viewport_store + + * tests/test-fbo.c (make_shader): Conditionally use the GLES 2 + names of the shader variables + 2008-10-29 Neil Roberts Bug 1206 - Picking disabled dithering diff --git a/clutter/cogl/gles/cogl-context.h b/clutter/cogl/gles/cogl-context.h index 6efa790b2..cbeb0fda9 100644 --- a/clutter/cogl/gles/cogl-context.h +++ b/clutter/cogl/gles/cogl-context.h @@ -79,6 +79,10 @@ typedef struct #ifdef HAVE_COGL_GLES2 CoglGles2Wrapper gles2; + + /* Viewport store for FBOs. Needed because glPushAttrib() isn't + supported */ + GLint viewport_store[4]; #endif } CoglContext; diff --git a/clutter/cogl/gles/cogl-fbo.c b/clutter/cogl/gles/cogl-fbo.c index 022a61c73..cd4cff705 100644 --- a/clutter/cogl/gles/cogl-fbo.c +++ b/clutter/cogl/gles/cogl-fbo.c @@ -5,7 +5,7 @@ * * Authored By Matthew Allum * - * Copyright (C) 2007 OpenedHand + * Copyright (C) 2008 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -33,78 +33,278 @@ #include "cogl-texture.h" #include "cogl-fbo.h" #include "cogl-context.h" +#include "cogl-handle.h" +#include "cogl-gles2-wrapper.h" -/* Expecting EXT functions not to be defined - redirect to pointers in context */ -#define glGenRenderbuffersEXT ctx->pf_glGenRenderbuffersEXT -#define glBindRenderbufferEXT ctx->pf_glBindRenderbufferEXT -#define glRenderbufferStorageEXT ctx->pf_glRenderbufferStorageEXT -#define glGenFramebuffersEXT ctx->pf_glGenFramebuffersEXT -#define glBindFramebufferEXT ctx->pf_glBindFramebufferEXT -#define glFramebufferTexture2DEXT ctx->pf_glFramebufferTexture2DEXT -#define glFramebufferRenderbufferEXT ctx->pf_glFramebufferRenderbufferEXT -#define glCheckFramebufferStatusEXT ctx->pf_glCheckFramebufferStatusEXT -#define glDeleteFramebuffersEXT ctx->pf_glDeleteFramebuffersEXT -#define glBlitFramebufferEXT ctx->pf_glBlitFramebufferEXT -#define glRenderbufferStorageMultisampleEXT ctx->pf_glRenderbufferStorageMultisampleEXT +#ifdef HAVE_COGL_GLES2 -static gint -_cogl_fbo_handle_find (CoglHandle handle) -{ - gint i; - - _COGL_GET_CONTEXT (ctx, -1); - - if (ctx->fbo_handles == NULL) - return -1; - - for (i=0; i < ctx->fbo_handles->len; ++i) - if (g_array_index (ctx->fbo_handles, CoglHandle, i) == handle) - return i; - - return -1; -} -/* -static CoglHandle -_cogl_fbo_handle_new (CoglFbo *fbo) +static void _cogl_offscreen_free (CoglFbo *fbo); + +COGL_HANDLE_DEFINE (Fbo, offscreen, fbo_handles); + +CoglHandle +cogl_offscreen_new_to_texture (CoglHandle texhandle) { + CoglTexture *tex; + CoglFbo *fbo; + CoglTexSliceSpan *x_span; + CoglTexSliceSpan *y_span; + GLuint tex_gl_handle; + GLuint fbo_gl_handle; + GLuint gl_stencil_handle; + GLenum status; + _COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE); - - CoglHandle handle = (CoglHandle)fbo; - - if (ctx->fbo_handles == NULL) - ctx->fbo_handles = g_array_new (FALSE, FALSE, sizeof (CoglHandle)); - - g_array_append_val (ctx->fbo_handles, handle); - - return handle; + + if (!cogl_features_available (COGL_FEATURE_OFFSCREEN)) + return COGL_INVALID_HANDLE; + + /* Make texhandle is a valid texture object */ + if (!cogl_is_texture (texhandle)) + return COGL_INVALID_HANDLE; + + tex = _cogl_texture_pointer_from_handle (texhandle); + + /* The texture must not be sliced */ + if (tex->slice_gl_handles == NULL) + return COGL_INVALID_HANDLE; + + if (tex->slice_gl_handles->len != 1) + return COGL_INVALID_HANDLE; + + /* Pick the single texture slice width, height and GL id */ + x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, 0); + y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, 0); + tex_gl_handle = g_array_index (tex->slice_gl_handles, GLuint, 0); + + /* Create a renderbuffer for stenciling */ + GE( glGenRenderbuffers (1, &gl_stencil_handle) ); + GE( glBindRenderbuffer (GL_RENDERBUFFER, gl_stencil_handle) ); + GE( glRenderbufferStorage (GL_RENDERBUFFER, GL_STENCIL_INDEX8, + cogl_texture_get_width (texhandle), + cogl_texture_get_height (texhandle)) ); + GE( glBindRenderbuffer (GL_RENDERBUFFER, 0) ); + + /* Generate framebuffer */ + glGenFramebuffers (1, &fbo_gl_handle); + GE( glBindFramebuffer (GL_FRAMEBUFFER, fbo_gl_handle) ); + GE( glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + tex->gl_target, tex_gl_handle, 0) ); + GE( glFramebufferRenderbuffer (GL_FRAMEBUFFER, + GL_STENCIL_ATTACHMENT, + GL_RENDERBUFFER, gl_stencil_handle) ); + + /* Make sure it's complete */ + status = glCheckFramebufferStatus (GL_FRAMEBUFFER); + + if (status != GL_FRAMEBUFFER_COMPLETE) + { + /* Stencil renderbuffers aren't always supported. Try again + without the stencil buffer */ + GE( glFramebufferRenderbuffer (GL_FRAMEBUFFER, + GL_STENCIL_ATTACHMENT, + GL_RENDERBUFFER, + 0) ); + GE( glDeleteRenderbuffers (1, &gl_stencil_handle) ); + gl_stencil_handle = 0; + + status = glCheckFramebufferStatus (GL_FRAMEBUFFER); + + if (status != GL_FRAMEBUFFER_COMPLETE) + { + /* Still failing, so give up */ + GE( glDeleteFramebuffers (1, &fbo_gl_handle) ); + GE( glBindFramebuffer (GL_FRAMEBUFFER, 0) ); + return COGL_INVALID_HANDLE; + } + } + + GE( glBindFramebuffer (GL_FRAMEBUFFER, 0) ); + + /* Allocate and init a CoglFbo object (store non-wasted size + for subsequent blits and viewport setup) */ + fbo = (CoglFbo*) g_malloc (sizeof (CoglFbo)); + fbo->ref_count = 1; + fbo->width = x_span->size - x_span->waste; + fbo->height = y_span->size - y_span->waste; + fbo->gl_handle = fbo_gl_handle; + fbo->gl_stencil_handle = gl_stencil_handle; + + COGL_HANDLE_DEBUG_NEW (offscreen, fbo); + + return _cogl_offscreen_handle_new (fbo); +} + +CoglHandle +cogl_offscreen_new_multisample () +{ + if (!cogl_features_available (COGL_FEATURE_OFFSCREEN_MULTISAMPLE)) + return COGL_INVALID_HANDLE; + + return COGL_INVALID_HANDLE; } static void -_cogl_fbo_handle_release (CoglHandle handle) +_cogl_offscreen_free (CoglFbo *fbo) { _COGL_GET_CONTEXT (ctx, NO_RETVAL); - - gint i; - - if ( (i = _cogl_fbo_handle_find (handle)) == -1) - return; - - g_array_remove_index_fast (ctx->fbo_handles, i); + + /* Frees FBO resources but its handle is not + released! Do that separately before this! */ + if (fbo->gl_stencil_handle) + GE( glDeleteRenderbuffers (1, &fbo->gl_stencil_handle) ); + GE( glDeleteFramebuffers (1, &fbo->gl_handle) ); + g_free (fbo); } -static CoglFbo* -_cogl_fbo_pointer_from_handle (CoglHandle handle) +void +cogl_offscreen_blit_region (CoglHandle src_buffer, + CoglHandle dst_buffer, + int src_x, + int src_y, + int src_w, + int src_h, + int dst_x, + int dst_y, + int dst_w, + int dst_h) { - return (CoglFbo*) handle; -}*/ + /* Not supported on GLES */ + return; +} + +void +cogl_offscreen_blit (CoglHandle src_buffer, + CoglHandle dst_buffer) +{ + /* Not supported on GLES */ + return; +} + +void +cogl_draw_buffer (CoglBufferTarget target, CoglHandle offscreen) +{ + CoglFbo *fbo = NULL; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (target == COGL_OFFSCREEN_BUFFER) + { + GLboolean scissor_enabled; + GLint scissor_box[4]; + + /* Make sure it is a valid fbo handle */ + if (!cogl_is_offscreen (offscreen)) + return; + + fbo = _cogl_offscreen_pointer_from_handle (offscreen); + + /* Check current draw buffer target */ + if (ctx->draw_buffer != COGL_OFFSCREEN_BUFFER) + { + /* Push the viewport and matrix setup if redirecting + from a non-screen buffer */ + GE( glGetIntegerv (GL_VIEWPORT, ctx->viewport_store) ); + + GE( cogl_wrap_glMatrixMode (GL_PROJECTION) ); + GE( cogl_wrap_glPushMatrix () ); + GE( cogl_wrap_glLoadIdentity () ); + + GE( cogl_wrap_glMatrixMode (GL_MODELVIEW) ); + GE( cogl_wrap_glPushMatrix () ); + GE( cogl_wrap_glLoadIdentity () ); + } + else + { + /* Override viewport and matrix setup if redirecting + from another offscreen buffer */ + GE( cogl_wrap_glMatrixMode (GL_PROJECTION) ); + GE( cogl_wrap_glLoadIdentity () ); + + GE( cogl_wrap_glMatrixMode (GL_MODELVIEW) ); + GE( cogl_wrap_glLoadIdentity () ); + } + + /* Setup new viewport and matrices */ + GE( glViewport (0, 0, fbo->width, fbo->height) ); + GE( cogl_wrap_glTranslatex (-CFX_ONE, -CFX_ONE, 0) ); + GE( cogl_wrap_glScalex (CFX_QDIV (CLUTTER_INT_TO_FIXED (2), + CLUTTER_INT_TO_FIXED (fbo->width)), + CFX_QDIV (CLUTTER_INT_TO_FIXED (2), + CLUTTER_INT_TO_FIXED (fbo->height)), + CFX_ONE) ); + + /* Bind offscreen framebuffer object */ + GE( glBindFramebuffer (GL_FRAMEBUFFER, fbo->gl_handle) ); + GE( glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE) ); + + /* Some implementation require a clear before drawing + to an fbo. Luckily it is affected by scissor test. */ + /* FIXME: test where exactly this is needed end whether + a glClear with 0 argument is enough */ + + scissor_enabled = glIsEnabled (GL_SCISSOR_TEST); + GE( glGetIntegerv (GL_SCISSOR_BOX, scissor_box) ); + GE( glScissor (0, 0, 0, 0) ); + GE( glEnable (GL_SCISSOR_TEST) ); + GE( glClear (GL_COLOR_BUFFER_BIT) ); + if (!scissor_enabled) + glDisable (GL_SCISSOR_TEST); + glScissor (scissor_box[0], scissor_box[1], + scissor_box[2], scissor_box[3]); + + } + else if ((target & COGL_WINDOW_BUFFER) || + (target & COGL_MASK_BUFFER)) + { + /* Check current draw buffer target */ + if (ctx->draw_buffer == COGL_OFFSCREEN_BUFFER) + { + /* Pop viewport and matrices if redirecting back + from an offscreen buffer */ + GE( glViewport (ctx->viewport_store[0], ctx->viewport_store[1], + ctx->viewport_store[2], ctx->viewport_store[3]) ); + + GE( cogl_wrap_glMatrixMode (GL_PROJECTION) ); + GE( cogl_wrap_glPopMatrix () ); + + GE( cogl_wrap_glMatrixMode (GL_MODELVIEW) ); + GE( cogl_wrap_glPopMatrix () ); + } + + /* Bind window framebuffer object */ + GE( glBindFramebuffer (GL_FRAMEBUFFER, 0) ); + + + if (target == COGL_WINDOW_BUFFER) + { + /* Draw to RGB channels */ + GE( glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE) ); + } + else if (target == COGL_MASK_BUFFER) + { + /* Draw only to ALPHA channel */ + GE( glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE) ); + } + else + { + /* Draw to all channels */ + GE( glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE) ); + } + } + + /* Store new target */ + ctx->draw_buffer = target; +} + +#else /* HAVE_COGL_GLES2 */ + +/* No support on regular OpenGL 1.1 */ gboolean -cogl_is_offscreen_buffer (CoglHandle handle) -{ - if (handle == COGL_INVALID_HANDLE) - return FALSE; - - return _cogl_fbo_handle_find (handle) >= 0; +cogl_is_offscreen (CoglHandle handle) +{ + return FALSE; } CoglHandle @@ -153,38 +353,6 @@ cogl_offscreen_blit (CoglHandle src_buffer, void cogl_draw_buffer (CoglBufferTarget target, CoglHandle offscreen) { - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - - switch (target) - { - case COGL_OFFSCREEN_BUFFER: - - /* Not supported */ - return; - - case COGL_WINDOW_BUFFER: - - /* Draw to RGB channels */ - GE( glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE) ); - - break; - - case COGL_MASK_BUFFER: - - /* Draw only to ALPHA channel */ - GE( glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE) ); - - break; - case COGL_WINDOW_BUFFER & COGL_MASK_BUFFER: - - /* Draw to all channels */ - GE( glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE) ); - - break; - default: - break; - } - - /* Store new target */ - ctx->draw_buffer = target; } + +#endif /* HAVE_COGL_GLES2 */ diff --git a/clutter/cogl/gles/cogl-fbo.h b/clutter/cogl/gles/cogl-fbo.h index f2dd5298f..f0efef79f 100644 --- a/clutter/cogl/gles/cogl-fbo.h +++ b/clutter/cogl/gles/cogl-fbo.h @@ -32,6 +32,7 @@ typedef struct int width; int height; GLuint gl_handle; + GLuint gl_stencil_handle; } CoglFbo; diff --git a/clutter/cogl/gles/cogl.c b/clutter/cogl/gles/cogl.c index 82cbeed59..0bf4d54f6 100644 --- a/clutter/cogl/gles/cogl.c +++ b/clutter/cogl/gles/cogl.c @@ -830,7 +830,7 @@ _cogl_features_init () flags |= COGL_FEATURE_FOUR_CLIP_PLANES; #ifdef HAVE_COGL_GLES2 - flags |= COGL_FEATURE_SHADERS_GLSL; + flags |= COGL_FEATURE_SHADERS_GLSL | COGL_FEATURE_OFFSCREEN; #endif ctx->feature_flags = flags; diff --git a/tests/test-fbo.c b/tests/test-fbo.c index 5b32428fc..eb6097a59 100644 --- a/tests/test-fbo.c +++ b/tests/test-fbo.c @@ -1,5 +1,26 @@ +#include "../config.h" + /*#define TEST_GROUP */ +/* These variables are used instead of the standard GLSL variables on + GLES 2 */ +#ifdef HAVE_COGL_GLES2 + +#define GLES2_VARS \ + "precision mediump float;\n" \ + "varying vec2 tex_coord;\n" \ + "varying vec4 frag_color;\n" +#define TEX_COORD "tex_coord" +#define COLOR_VAR "frag_color" + +#else /* HAVE_COGL_GLES2 */ + +#define GLES2_VARS "" +#define TEX_COORD "gl_TexCoord[0]" +#define COLOR_VAR "gl_Color" + +#endif /* HAVE_COGL_GLES2 */ + #include #include @@ -34,17 +55,19 @@ ClutterShader* make_shader(void) { ClutterShader *shader; + GError *error = NULL; shader = clutter_shader_new (); clutter_shader_set_fragment_source (shader, + GLES2_VARS "uniform float radius ;" "uniform sampler2D rectTexture;" "uniform float x_step, y_step;" "" "void main()" "{" - " vec4 color = texture2D(rectTexture, gl_TexCoord[0].st);" + " vec4 color = texture2D(rectTexture, " TEX_COORD ".st);" " float u;" " float v;" " int count = 1;" @@ -52,19 +75,25 @@ make_shader(void) " for (v=-radius;vmessage); + g_error_free (error); + } + return shader; }