2008-04-25 13:37:36 +00:00
|
|
|
/*
|
2009-04-27 14:48:12 +00:00
|
|
|
* Cogl
|
2008-04-25 13:37:36 +00:00
|
|
|
*
|
2009-04-27 14:48:12 +00:00
|
|
|
* An object oriented GL/GLES Abstraction/Utility Layer
|
2008-04-25 13:37:36 +00:00
|
|
|
*
|
2009-04-27 14:48:12 +00:00
|
|
|
* Copyright (C) 2008,2009 Intel Corporation.
|
2008-04-25 13:37:36 +00:00
|
|
|
*
|
|
|
|
* 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"
|
2008-10-30 17:25:00 +00:00
|
|
|
#include "cogl-texture-private.h"
|
2008-04-25 13:37:36 +00:00
|
|
|
#include "cogl-fbo.h"
|
|
|
|
#include "cogl-context.h"
|
2008-10-29 14:52:48 +00:00
|
|
|
#include "cogl-handle.h"
|
|
|
|
#include "cogl-gles2-wrapper.h"
|
2008-04-25 13:37:36 +00:00
|
|
|
|
2008-10-29 14:52:48 +00:00
|
|
|
#ifdef HAVE_COGL_GLES2
|
|
|
|
|
|
|
|
static void _cogl_offscreen_free (CoglFbo *fbo);
|
|
|
|
|
2009-04-01 16:16:44 +00:00
|
|
|
COGL_HANDLE_DEFINE (Fbo, offscreen);
|
2008-10-29 14:52:48 +00:00
|
|
|
|
|
|
|
CoglHandle
|
|
|
|
cogl_offscreen_new_to_texture (CoglHandle texhandle)
|
2008-04-25 13:37:36 +00:00
|
|
|
{
|
2008-10-29 14:52:48 +00:00
|
|
|
CoglFbo *fbo;
|
2009-09-16 10:56:17 +00:00
|
|
|
int width;
|
|
|
|
int height;
|
2008-10-29 14:52:48 +00:00
|
|
|
GLuint tex_gl_handle;
|
2009-09-16 10:56:17 +00:00
|
|
|
GLenum tex_gl_target;
|
2008-10-29 14:52:48 +00:00
|
|
|
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;
|
|
|
|
|
|
|
|
/* The texture must not be sliced */
|
2009-09-16 10:56:17 +00:00
|
|
|
if (cogl_texture_is_sliced (texhandle))
|
2008-10-29 14:52:48 +00:00
|
|
|
return COGL_INVALID_HANDLE;
|
|
|
|
|
2009-09-16 10:56:17 +00:00
|
|
|
/* Pick the single texture slice width, height and GL id */
|
|
|
|
|
|
|
|
width = cogl_texture_get_width (texhandle);
|
|
|
|
height = cogl_texture_get_height (texhandle);
|
|
|
|
|
|
|
|
if (!cogl_texture_get_gl_texture (texhandle, &tex_gl_handle, &tex_gl_target))
|
2008-10-29 14:52:48 +00:00
|
|
|
return COGL_INVALID_HANDLE;
|
|
|
|
|
2009-09-16 10:56:17 +00:00
|
|
|
if (tex_gl_target != GL_TEXTURE_2D)
|
|
|
|
return COGL_INVALID_HANDLE;
|
2008-10-29 14:52:48 +00:00
|
|
|
|
|
|
|
/* 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,
|
2009-09-16 10:56:17 +00:00
|
|
|
tex_gl_target, tex_gl_handle, 0) );
|
2008-10-29 14:52:48 +00:00
|
|
|
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));
|
2009-09-16 10:56:17 +00:00
|
|
|
fbo->width = width;
|
|
|
|
fbo->height = height;
|
2008-10-29 14:52:48 +00:00
|
|
|
fbo->gl_handle = fbo_gl_handle;
|
|
|
|
fbo->gl_stencil_handle = gl_stencil_handle;
|
|
|
|
|
|
|
|
return _cogl_offscreen_handle_new (fbo);
|
2008-04-25 13:37:36 +00:00
|
|
|
}
|
2008-10-29 14:52:48 +00:00
|
|
|
|
2008-04-25 13:37:36 +00:00
|
|
|
static void
|
2008-10-29 14:52:48 +00:00
|
|
|
_cogl_offscreen_free (CoglFbo *fbo)
|
2008-04-25 13:37:36 +00:00
|
|
|
{
|
|
|
|
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
2008-10-29 14:52:48 +00:00
|
|
|
|
|
|
|
/* 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);
|
2008-04-25 13:37:36 +00:00
|
|
|
}
|
|
|
|
|
2008-10-29 14:52:48 +00:00
|
|
|
void
|
2009-04-30 21:19:43 +00:00
|
|
|
cogl_set_draw_buffer (CoglBufferTarget target, CoglHandle offscreen)
|
2008-10-29 14:52:48 +00:00
|
|
|
{
|
|
|
|
CoglFbo *fbo = NULL;
|
2009-02-20 03:37:08 +00:00
|
|
|
CoglDrawBufferState *draw_buffer;
|
2008-10-29 14:52:48 +00:00
|
|
|
|
|
|
|
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
|
|
|
|
2009-02-20 03:37:08 +00:00
|
|
|
g_assert (ctx->draw_buffer_stack != NULL);
|
|
|
|
draw_buffer = ctx->draw_buffer_stack->data;
|
|
|
|
|
2008-10-29 14:52:48 +00:00
|
|
|
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 */
|
2009-02-20 03:37:08 +00:00
|
|
|
if (draw_buffer->target != COGL_OFFSCREEN_BUFFER)
|
2008-10-29 14:52:48 +00:00
|
|
|
{
|
|
|
|
/* Push the viewport and matrix setup if redirecting
|
|
|
|
from a non-screen buffer */
|
2009-07-28 00:34:33 +00:00
|
|
|
GE( glGetIntegerv (GL_VIEWPORT, ctx->drv.viewport_store) );
|
2008-10-29 14:52:48 +00:00
|
|
|
|
2009-10-13 22:09:42 +00:00
|
|
|
_cogl_matrix_stack_push (ctx->projection_stack);
|
|
|
|
_cogl_matrix_stack_load_identity (ctx->projection_stack);
|
2008-10-29 14:52:48 +00:00
|
|
|
|
2009-10-13 22:09:42 +00:00
|
|
|
_cogl_matrix_stack_push (ctx->modelview_stack);
|
|
|
|
_cogl_matrix_stack_load_identity (ctx->modelview_stack);
|
2008-10-29 14:52:48 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Override viewport and matrix setup if redirecting
|
|
|
|
from another offscreen buffer */
|
2009-10-13 22:09:42 +00:00
|
|
|
_cogl_matrix_stack_load_identity (ctx->projection_stack);
|
2008-10-29 14:52:48 +00:00
|
|
|
|
2009-10-13 22:09:42 +00:00
|
|
|
_cogl_matrix_stack_load_identity (ctx->modelview_stack);
|
2008-10-29 14:52:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Setup new viewport and matrices */
|
|
|
|
GE( glViewport (0, 0, fbo->width, fbo->height) );
|
2009-10-13 22:09:42 +00:00
|
|
|
_cogl_matrix_stack_translate (ctx->modelview_stack, -1.0f, -1.0f, 0.0f);
|
|
|
|
_cogl_matrix_stack_scale (ctx->modelview_stack,
|
|
|
|
2.0f / fbo->width, 2.0f / fbo->height, 1.0f);
|
2008-10-29 14:52:48 +00:00
|
|
|
|
|
|
|
/* 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]);
|
|
|
|
|
|
|
|
}
|
2009-04-30 21:19:43 +00:00
|
|
|
else if (target & COGL_WINDOW_BUFFER)
|
2008-10-29 14:52:48 +00:00
|
|
|
{
|
|
|
|
/* Check current draw buffer target */
|
2009-02-20 03:37:08 +00:00
|
|
|
if (draw_buffer->target == COGL_OFFSCREEN_BUFFER)
|
2008-10-29 14:52:48 +00:00
|
|
|
{
|
|
|
|
/* Pop viewport and matrices if redirecting back
|
|
|
|
from an offscreen buffer */
|
2009-07-28 00:34:33 +00:00
|
|
|
GE( glViewport (ctx->drv.viewport_store[0],
|
|
|
|
ctx->drv.viewport_store[1],
|
|
|
|
ctx->drv.viewport_store[2],
|
|
|
|
ctx->drv.viewport_store[3]) );
|
2008-10-29 14:52:48 +00:00
|
|
|
|
2009-10-13 22:09:42 +00:00
|
|
|
_cogl_matrix_stack_pop (ctx->projection_stack);
|
2008-10-29 14:52:48 +00:00
|
|
|
|
2009-10-13 22:09:42 +00:00
|
|
|
_cogl_matrix_stack_pop (ctx->modelview_stack);
|
2008-10-29 14:52:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Bind window framebuffer object */
|
|
|
|
GE( glBindFramebuffer (GL_FRAMEBUFFER, 0) );
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Store new target */
|
2009-02-20 03:37:08 +00:00
|
|
|
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;
|
|
|
|
|
2009-04-30 21:19:43 +00:00
|
|
|
/* the logic in cogl_set_draw_buffer() only works if
|
2009-02-20 03:37:08 +00:00
|
|
|
* to_pop is still on top of the stack, because
|
2009-04-30 21:19:43 +00:00
|
|
|
* cogl_set_draw_buffer() needs to know the previous
|
2009-02-20 03:37:08 +00:00
|
|
|
* state.
|
|
|
|
*/
|
2009-04-30 21:19:43 +00:00
|
|
|
cogl_set_draw_buffer (to_restore->target, to_restore->offscreen);
|
2009-02-20 03:37:08 +00:00
|
|
|
|
2009-04-30 21:19:43 +00:00
|
|
|
/* cogl_set_draw_buffer() should have set top of stack
|
2009-02-20 03:37:08 +00:00
|
|
|
* 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);
|
2008-10-29 14:52:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#else /* HAVE_COGL_GLES2 */
|
|
|
|
|
|
|
|
/* No support on regular OpenGL 1.1 */
|
2008-04-25 13:37:36 +00:00
|
|
|
|
|
|
|
gboolean
|
2008-10-29 14:52:48 +00:00
|
|
|
cogl_is_offscreen (CoglHandle handle)
|
|
|
|
{
|
|
|
|
return FALSE;
|
2008-04-25 13:37:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2009-04-30 21:19:43 +00:00
|
|
|
cogl_set_draw_buffer (CoglBufferTarget target, CoglHandle offscreen)
|
2008-04-25 13:37:36 +00:00
|
|
|
{
|
|
|
|
}
|
2008-10-29 14:52:48 +00:00
|
|
|
|
|
|
|
#endif /* HAVE_COGL_GLES2 */
|