/* * Clutter COGL * * A basic GL/GLES Abstraction/Utility Layer * * Authored By Matthew Allum <mallum@openedhand.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. */ #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" /* Expecting EXT functions not to be defined - redirect to pointers in context */ #define glGenRenderbuffersEXT ctx->pf_glGenRenderbuffersEXT #define glDeleteRenderbuffersEXT ctx->pf_glDeleteRenderbuffersEXT #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 #ifndef GL_READ_FRAMEBUFFER_EXT #define GL_READ_FRAMEBUFFER_EXT 0x8CA8 #endif #ifndef GL_DRAW_FRAMEBUFFER_EXT #define GL_DRAW_FRAMEBUFFER_EXT 0x8CA9 #endif 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); 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( glGenRenderbuffersEXT (1, &gl_stencil_handle) ); GE( glBindRenderbufferEXT (GL_RENDERBUFFER_EXT, gl_stencil_handle) ); GE( glRenderbufferStorageEXT (GL_RENDERBUFFER_EXT, GL_STENCIL_INDEX8_EXT, cogl_texture_get_width (texhandle), cogl_texture_get_height (texhandle)) ); GE( glBindRenderbufferEXT (GL_RENDERBUFFER_EXT, 0) ); /* Generate framebuffer */ glGenFramebuffersEXT (1, &fbo_gl_handle); GE( glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, fbo_gl_handle) ); GE( glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, tex->gl_target, tex_gl_handle, 0) ); GE( glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, gl_stencil_handle) ); /* Make sure it's complete */ status = glCheckFramebufferStatusEXT (GL_FRAMEBUFFER_EXT); if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { /* Stencil renderbuffers aren't always supported. Try again without the stencil buffer */ GE( glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, 0) ); GE( glDeleteRenderbuffersEXT (1, &gl_stencil_handle) ); gl_stencil_handle = 0; status = glCheckFramebufferStatusEXT (GL_FRAMEBUFFER_EXT); if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { /* Still failing, so give up */ GE( glDeleteFramebuffersEXT (1, &fbo_gl_handle) ); GE( glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0) ); return COGL_INVALID_HANDLE; } } GE( glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 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_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( glDeleteRenderbuffersEXT (1, &fbo->gl_stencil_handle) ); GE( glDeleteFramebuffersEXT (1, &fbo->gl_handle) ); g_free (fbo); } 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) { CoglFbo *src_fbo; CoglFbo *dst_fbo; _COGL_GET_CONTEXT (ctx, NO_RETVAL); if (!cogl_features_available (COGL_FEATURE_OFFSCREEN_BLIT)) return; /* Make sure these are valid fbo handles */ if (!cogl_is_offscreen (src_buffer)) return; if (!cogl_is_offscreen (dst_buffer)) return; src_fbo = _cogl_offscreen_pointer_from_handle (src_buffer); dst_fbo = _cogl_offscreen_pointer_from_handle (dst_buffer); /* Copy (and scale) a region from one to another framebuffer */ GE( glBindFramebufferEXT (GL_READ_FRAMEBUFFER_EXT, src_fbo->gl_handle) ); GE( glBindFramebufferEXT (GL_DRAW_FRAMEBUFFER_EXT, dst_fbo->gl_handle) ); GE( glBlitFramebufferEXT (src_x, src_y, src_x + src_w, src_y + src_h, dst_x, dst_y, dst_x + dst_w, dst_y + dst_h, GL_COLOR_BUFFER_BIT, GL_LINEAR) ); } void cogl_offscreen_blit (CoglHandle src_buffer, CoglHandle dst_buffer) { CoglFbo *src_fbo; CoglFbo *dst_fbo; _COGL_GET_CONTEXT (ctx, NO_RETVAL); if (!cogl_features_available (COGL_FEATURE_OFFSCREEN_BLIT)) return; /* Make sure these are valid fbo handles */ if (!cogl_is_offscreen (src_buffer)) return; if (!cogl_is_offscreen (dst_buffer)) return; src_fbo = _cogl_offscreen_pointer_from_handle (src_buffer); dst_fbo = _cogl_offscreen_pointer_from_handle (dst_buffer); /* Copy (and scale) whole image from one to another framebuffer */ GE( glBindFramebufferEXT (GL_READ_FRAMEBUFFER_EXT, src_fbo->gl_handle) ); GE( glBindFramebufferEXT (GL_DRAW_FRAMEBUFFER_EXT, dst_fbo->gl_handle) ); GE( glBlitFramebufferEXT (0, 0, src_fbo->width, src_fbo->height, 0, 0, dst_fbo->width, dst_fbo->height, GL_COLOR_BUFFER_BIT, GL_LINEAR) ); } void cogl_draw_buffer (CoglBufferTarget target, CoglHandle offscreen) { CoglFbo *fbo = NULL; _COGL_GET_CONTEXT (ctx, NO_RETVAL); if (target == COGL_OFFSCREEN_BUFFER) { /* 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( glPushAttrib (GL_VIEWPORT_BIT) ); GE( glMatrixMode (GL_PROJECTION) ); GE( glPushMatrix () ); GE( glLoadIdentity () ); GE( glMatrixMode (GL_MODELVIEW) ); GE( glPushMatrix () ); GE( glLoadIdentity () ); } else { /* Override viewport and matrix setup if redirecting from another offscreen buffer */ GE( glMatrixMode (GL_PROJECTION) ); GE( glLoadIdentity () ); GE( glMatrixMode (GL_MODELVIEW) ); GE( glLoadIdentity () ); } /* Setup new viewport and matrices */ GE( glViewport (0, 0, fbo->width, fbo->height) ); GE( glTranslatef (-1.0f, -1.0f, 0.0f) ); GE( glScalef (2.0f / fbo->width, 2.0f / fbo->height, 1.0f) ); /* Bind offscreen framebuffer object */ GE( glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 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 */ GE( glPushAttrib (GL_SCISSOR_BIT) ); GE( glScissor (0,0,0,0) ); GE( glEnable (GL_SCISSOR_TEST) ); GE( glClear (GL_COLOR_BUFFER_BIT) ); GE( glPopAttrib () ); } 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( glPopAttrib () ); GE( glMatrixMode (GL_PROJECTION) ); GE( glPopMatrix () ); GE( glMatrixMode (GL_MODELVIEW) ); GE( glPopMatrix () ); } /* Bind window framebuffer object */ GE( glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0) ); if (target == COGL_WINDOW_BUFFER) { /* Draw to RGB channels */ GE( glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE) ); } 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; }