/* * Cogl * * An object oriented GL/GLES Abstraction/Utility Layer * * Copyright (C) 2008,2009 Intel Corporation. * * 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "cogl.h" #include "cogl-internal.h" #include "cogl-util.h" #include "cogl-texture-private.h" #include "cogl-fbo.h" #include "cogl-context.h" #include "cogl-handle.h" #include "cogl-gles2-wrapper.h" #ifdef HAVE_COGL_GLES2 static void _cogl_offscreen_free (CoglFbo *fbo); COGL_HANDLE_DEFINE (Fbo, offscreen); 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); 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->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; return _cogl_offscreen_handle_new (fbo); } static void _cogl_offscreen_free (CoglFbo *fbo) { _COGL_GET_CONTEXT (ctx, NO_RETVAL); /* 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); } void cogl_set_draw_buffer (CoglBufferTarget target, CoglHandle offscreen) { CoglFbo *fbo = NULL; CoglDrawBufferState *draw_buffer; _COGL_GET_CONTEXT (ctx, NO_RETVAL); g_assert (ctx->draw_buffer_stack != NULL); draw_buffer = ctx->draw_buffer_stack->data; 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 (draw_buffer->target != COGL_OFFSCREEN_BUFFER) { /* Push the viewport and matrix setup if redirecting from a non-screen buffer */ GE( glGetIntegerv (GL_VIEWPORT, ctx->viewport_store) ); _cogl_set_current_matrix (COGL_MATRIX_PROJECTION); _cogl_current_matrix_push (); _cogl_current_matrix_identity (); _cogl_set_current_matrix (COGL_MATRIX_MODELVIEW); _cogl_current_matrix_push (); _cogl_current_matrix_identity (); } else { /* Override viewport and matrix setup if redirecting from another offscreen buffer */ _cogl_set_current_matrix (COGL_MATRIX_PROJECTION); _cogl_current_matrix_identity (); _cogl_set_current_matrix (COGL_MATRIX_MODELVIEW); _cogl_current_matrix_identity (); } /* Setup new viewport and matrices */ GE( glViewport (0, 0, fbo->width, fbo->height) ); _cogl_current_matrix_translate (-1.0f, -1.0f, 0.0f); _cogl_current_matrix_scale (2.0f / fbo->width, 2.0f / fbo->height, 1.0f); /* 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) { /* Check current draw buffer target */ if (draw_buffer->target == 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]) ); _cogl_set_current_matrix (COGL_MATRIX_PROJECTION); _cogl_current_matrix_pop (); _cogl_set_current_matrix (COGL_MATRIX_MODELVIEW); _cogl_current_matrix_pop (); } /* Bind window framebuffer object */ GE( glBindFramebuffer (GL_FRAMEBUFFER, 0) ); } /* Store new target */ draw_buffer->target = target; if (draw_buffer->offscreen != offscreen) { if (draw_buffer->offscreen != COGL_INVALID_HANDLE) cogl_handle_unref (draw_buffer->offscreen); if (offscreen != COGL_INVALID_HANDLE) cogl_handle_ref (offscreen); draw_buffer->offscreen = offscreen; } } void cogl_push_draw_buffer(void) { CoglDrawBufferState *old; CoglDrawBufferState *draw_buffer; _COGL_GET_CONTEXT (ctx, NO_RETVAL); g_assert (ctx->draw_buffer_stack != NULL); old = ctx->draw_buffer_stack->data; draw_buffer = g_slice_new0 (CoglDrawBufferState); *draw_buffer = *old; ctx->draw_buffer_stack = g_slist_prepend (ctx->draw_buffer_stack, draw_buffer); } void cogl_pop_draw_buffer(void) { CoglDrawBufferState *to_pop; CoglDrawBufferState *to_restore; _COGL_GET_CONTEXT (ctx, NO_RETVAL); g_assert (ctx->draw_buffer_stack != NULL); if (ctx->draw_buffer_stack->next == NULL) { g_warning ("1 more cogl_pop_draw_buffer() than cogl_push_draw_buffer()"); return; } to_pop = ctx->draw_buffer_stack->data; to_restore = ctx->draw_buffer_stack->next->data; /* the logic in cogl_set_draw_buffer() only works if * to_pop is still on top of the stack, because * cogl_set_draw_buffer() needs to know the previous * state. */ cogl_set_draw_buffer (to_restore->target, to_restore->offscreen); /* cogl_set_draw_buffer() should have set top of stack * to to_restore */ g_assert (to_restore->target == to_pop->target); g_assert (to_restore->offscreen == to_pop->offscreen); g_assert (ctx->draw_buffer_stack->data == to_pop); ctx->draw_buffer_stack = g_slist_remove_link (ctx->draw_buffer_stack, ctx->draw_buffer_stack); g_slice_free (CoglDrawBufferState, to_pop); } #else /* HAVE_COGL_GLES2 */ /* No support on regular OpenGL 1.1 */ gboolean cogl_is_offscreen (CoglHandle handle) { return FALSE; } CoglHandle cogl_offscreen_new_to_texture (CoglHandle texhandle) { return COGL_INVALID_HANDLE; } CoglHandle cogl_offscreen_ref (CoglHandle handle) { return COGL_INVALID_HANDLE; } void cogl_offscreen_unref (CoglHandle handle) { } void cogl_set_draw_buffer (CoglBufferTarget target, CoglHandle offscreen) { } #endif /* HAVE_COGL_GLES2 */