18b96c84bf
When try_creating_fbo fails it returns 0 to report the error and if it succeeds it returns ‘flags’. However cogl_offscreen_new_to_texture also passes in 0 for the flags as the last fallback to create the fbo with nothing but the color buffer. In that case it will return 0 regardless of whether it succeeded so the last fallback will always be considered a failure. To fix this it now just returns a gboolean to indicate whether it succeeded and the flags used for each attempt is assigned when passing the argument rather than from the return value of the function. Also if the only configuration that succeeded was with flags==0 then it would always try all combinations because last_working_flags would also be zero. To avoid this it now uses a separate gboolean to mark whether we found a successful set of flags. http://bugzilla.openedhand.com/show_bug.cgi?id=1873
703 lines
20 KiB
C
703 lines
20 KiB
C
/*
|
|
* Cogl
|
|
*
|
|
* An object oriented GL/GLES Abstraction/Utility Layer
|
|
*
|
|
* Copyright (C) 2007,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-context.h"
|
|
#include "cogl-handle.h"
|
|
#include "cogl-util.h"
|
|
#include "cogl-texture-private.h"
|
|
#include "cogl-framebuffer-private.h"
|
|
#include "cogl-clip-stack.h"
|
|
|
|
#ifdef HAVE_COGL_GLES2
|
|
|
|
#include "../gles/cogl-gles2-wrapper.h"
|
|
|
|
#else
|
|
|
|
#define glGenRenderbuffers ctx->drv.pf_glGenRenderbuffers
|
|
#define glDeleteRenderbuffers ctx->drv.pf_glDeleteRenderbuffers
|
|
#define glBindRenderbuffer ctx->drv.pf_glBindRenderbuffer
|
|
#define glRenderbufferStorage ctx->drv.pf_glRenderbufferStorage
|
|
#define glGenFramebuffers ctx->drv.pf_glGenFramebuffers
|
|
#define glBindFramebuffer ctx->drv.pf_glBindFramebuffer
|
|
#define glFramebufferTexture2D ctx->drv.pf_glFramebufferTexture2D
|
|
#define glFramebufferRenderbuffer ctx->drv.pf_glFramebufferRenderbuffer
|
|
#define glCheckFramebufferStatus ctx->drv.pf_glCheckFramebufferStatus
|
|
#define glDeleteFramebuffers ctx->drv.pf_glDeleteFramebuffers
|
|
|
|
#endif
|
|
|
|
#ifndef GL_FRAMEBUFFER
|
|
#define GL_FRAMEBUFFER 0x8D40
|
|
#endif
|
|
#ifndef GL_RENDERBUFFER
|
|
#define GL_RENDERBUFFER 0x8D41
|
|
#endif
|
|
#ifndef GL_STENCIL_ATTACHMENT
|
|
#define GL_STENCIL_ATTACHMENT 0x8D00
|
|
#endif
|
|
#ifndef GL_COLOR_ATTACHMENT0
|
|
#define GL_COLOR_ATTACHMENT0 0x8CE0
|
|
#endif
|
|
#ifndef GL_FRAMEBUFFER_COMPLETE
|
|
#define GL_FRAMEBUFFER_COMPLETE 0x8CD5
|
|
#endif
|
|
#ifndef GL_STENCIL_INDEX8
|
|
#define GL_STENCIL_INDEX8 0x8D48
|
|
#endif
|
|
#ifndef GL_DEPTH_STENCIL
|
|
#define GL_DEPTH_STENCIL 0x84F9
|
|
#endif
|
|
#ifndef GL_DEPTH_ATTACHMENT
|
|
#define GL_DEPTH_ATTACHMENT 0x8D00
|
|
#endif
|
|
#ifndef GL_DEPTH_COMPONENT16
|
|
#define GL_DEPTH_COMPONENT16 0x81A5
|
|
#endif
|
|
|
|
typedef enum {
|
|
_TRY_DEPTH_STENCIL = 1L<<0,
|
|
_TRY_DEPTH = 1L<<1,
|
|
_TRY_STENCIL = 1L<<2
|
|
} TryFBOFlags;
|
|
|
|
static void _cogl_framebuffer_free (CoglFramebuffer *framebuffer);
|
|
static void _cogl_onscreen_free (CoglOnscreen *onscreen);
|
|
static void _cogl_offscreen_free (CoglOffscreen *offscreen);
|
|
|
|
COGL_HANDLE_DEFINE (Onscreen, onscreen);
|
|
COGL_HANDLE_DEFINE (Offscreen, offscreen);
|
|
|
|
/* XXX:
|
|
* The CoglHandle macros don't support any form of inheritance, so for
|
|
* now we implement the CoglHandle support for the CoglFramebuffer
|
|
* abstract class manually.
|
|
*/
|
|
|
|
gboolean
|
|
cogl_is_framebuffer (CoglHandle handle)
|
|
{
|
|
CoglHandleObject *obj = (CoglHandleObject *)handle;
|
|
|
|
if (handle == COGL_INVALID_HANDLE)
|
|
return FALSE;
|
|
|
|
return obj->klass->type == _cogl_handle_onscreen_get_type ()
|
|
|| obj->klass->type == _cogl_handle_offscreen_get_type ();
|
|
}
|
|
|
|
static void
|
|
_cogl_framebuffer_init (CoglFramebuffer *framebuffer,
|
|
CoglFramebufferType type,
|
|
int width,
|
|
int height)
|
|
{
|
|
framebuffer->type = type;
|
|
framebuffer->width = width;
|
|
framebuffer->height = height;
|
|
framebuffer->viewport_x = 0;
|
|
framebuffer->viewport_y = 0;
|
|
framebuffer->viewport_width = width;
|
|
framebuffer->viewport_height = height;
|
|
|
|
framebuffer->modelview_stack = _cogl_matrix_stack_new ();
|
|
framebuffer->projection_stack = _cogl_matrix_stack_new ();
|
|
|
|
/* Initialise the clip stack */
|
|
_cogl_clip_stack_state_init (&framebuffer->clip_state);
|
|
}
|
|
|
|
void
|
|
_cogl_framebuffer_free (CoglFramebuffer *framebuffer)
|
|
{
|
|
_cogl_clip_stack_state_destroy (&framebuffer->clip_state);
|
|
|
|
_cogl_matrix_stack_destroy (framebuffer->modelview_stack);
|
|
framebuffer->modelview_stack = NULL;
|
|
|
|
_cogl_matrix_stack_destroy (framebuffer->projection_stack);
|
|
framebuffer->projection_stack = NULL;
|
|
}
|
|
|
|
int
|
|
_cogl_framebuffer_get_width (CoglHandle handle)
|
|
{
|
|
CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (handle);
|
|
return framebuffer->width;
|
|
}
|
|
|
|
int
|
|
_cogl_framebuffer_get_height (CoglHandle handle)
|
|
{
|
|
CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (handle);
|
|
return framebuffer->height;
|
|
}
|
|
|
|
CoglClipStackState *
|
|
_cogl_framebuffer_get_clip_state (CoglHandle handle)
|
|
{
|
|
CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (handle);
|
|
|
|
return &framebuffer->clip_state;
|
|
}
|
|
|
|
void
|
|
_cogl_framebuffer_set_viewport (CoglHandle handle,
|
|
int x,
|
|
int y,
|
|
int width,
|
|
int height)
|
|
{
|
|
CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (handle);
|
|
|
|
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
|
|
|
if (framebuffer->viewport_x == x &&
|
|
framebuffer->viewport_y == y &&
|
|
framebuffer->viewport_width == width &&
|
|
framebuffer->viewport_height == height)
|
|
return;
|
|
|
|
_cogl_journal_flush ();
|
|
|
|
framebuffer->viewport_x = x;
|
|
framebuffer->viewport_y = y;
|
|
framebuffer->viewport_width = width;
|
|
framebuffer->viewport_height = height;
|
|
|
|
if (_cogl_get_framebuffer () == framebuffer)
|
|
ctx->dirty_gl_viewport = TRUE;
|
|
}
|
|
|
|
int
|
|
_cogl_framebuffer_get_viewport_x (CoglHandle handle)
|
|
{
|
|
CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (handle);
|
|
return framebuffer->viewport_x;
|
|
}
|
|
|
|
int
|
|
_cogl_framebuffer_get_viewport_y (CoglHandle handle)
|
|
{
|
|
CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (handle);
|
|
return framebuffer->viewport_y;
|
|
}
|
|
|
|
int
|
|
_cogl_framebuffer_get_viewport_width (CoglHandle handle)
|
|
{
|
|
CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (handle);
|
|
return framebuffer->viewport_width;
|
|
}
|
|
|
|
int
|
|
_cogl_framebuffer_get_viewport_height (CoglHandle handle)
|
|
{
|
|
CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (handle);
|
|
return framebuffer->viewport_height;
|
|
}
|
|
|
|
void
|
|
_cogl_framebuffer_get_viewport4fv (CoglHandle handle, int *viewport)
|
|
{
|
|
CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (handle);
|
|
viewport[0] = framebuffer->viewport_x;
|
|
viewport[1] = framebuffer->viewport_y;
|
|
viewport[2] = framebuffer->viewport_width;
|
|
viewport[3] = framebuffer->viewport_height;
|
|
}
|
|
|
|
CoglMatrixStack *
|
|
_cogl_framebuffer_get_modelview_stack (CoglHandle handle)
|
|
{
|
|
CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (handle);
|
|
return framebuffer->modelview_stack;
|
|
}
|
|
|
|
CoglMatrixStack *
|
|
_cogl_framebuffer_get_projection_stack (CoglHandle handle)
|
|
{
|
|
CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (handle);
|
|
return framebuffer->projection_stack;
|
|
}
|
|
|
|
static gboolean
|
|
try_creating_fbo (CoglOffscreen *offscreen,
|
|
TryFBOFlags flags,
|
|
CoglHandle texture)
|
|
{
|
|
GLuint gl_depth_stencil_handle;
|
|
GLuint gl_depth_handle;
|
|
GLuint gl_stencil_handle;
|
|
GLuint tex_gl_handle;
|
|
GLenum tex_gl_target;
|
|
GLuint fbo_gl_handle;
|
|
GLenum status;
|
|
|
|
_COGL_GET_CONTEXT (ctx, FALSE);
|
|
|
|
if (!cogl_texture_get_gl_texture (texture, &tex_gl_handle, &tex_gl_target))
|
|
return FALSE;
|
|
|
|
if (tex_gl_target != GL_TEXTURE_2D
|
|
#ifdef HAVE_COGL_GL
|
|
&& tex_gl_target != GL_TEXTURE_RECTANGLE_ARB
|
|
#endif
|
|
)
|
|
return FALSE;
|
|
|
|
/* We are about to generate and bind a new fbo, so when next flushing the
|
|
* journal, we will need to rebind the current framebuffer... */
|
|
ctx->dirty_bound_framebuffer = 1;
|
|
|
|
/* Generate framebuffer */
|
|
glGenFramebuffers (1, &fbo_gl_handle);
|
|
GE (glBindFramebuffer (GL_FRAMEBUFFER, fbo_gl_handle));
|
|
offscreen->fbo_handle = fbo_gl_handle;
|
|
|
|
GE (glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
|
tex_gl_target, tex_gl_handle, 0));
|
|
|
|
if (flags & _TRY_DEPTH_STENCIL)
|
|
{
|
|
/* Create a renderbuffer for depth and stenciling */
|
|
GE (glGenRenderbuffers (1, &gl_depth_stencil_handle));
|
|
GE (glBindRenderbuffer (GL_RENDERBUFFER, gl_depth_stencil_handle));
|
|
GE (glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_STENCIL,
|
|
cogl_texture_get_width (texture),
|
|
cogl_texture_get_height (texture)));
|
|
GE (glBindRenderbuffer (GL_RENDERBUFFER, 0));
|
|
GE (glFramebufferRenderbuffer (GL_FRAMEBUFFER,
|
|
GL_STENCIL_ATTACHMENT,
|
|
GL_RENDERBUFFER, gl_depth_stencil_handle));
|
|
GE (glFramebufferRenderbuffer (GL_FRAMEBUFFER,
|
|
GL_DEPTH_ATTACHMENT,
|
|
GL_RENDERBUFFER, gl_depth_stencil_handle));
|
|
offscreen->renderbuffers =
|
|
g_slist_prepend (offscreen->renderbuffers,
|
|
GUINT_TO_POINTER (gl_depth_stencil_handle));
|
|
}
|
|
|
|
if (flags & _TRY_DEPTH)
|
|
{
|
|
GE (glGenRenderbuffers (1, &gl_depth_handle));
|
|
GE (glBindRenderbuffer (GL_RENDERBUFFER, gl_depth_handle));
|
|
/* For now we just ask for GL_DEPTH_COMPONENT16 since this is all that's
|
|
* available under GLES */
|
|
GE (glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT16,
|
|
cogl_texture_get_width (texture),
|
|
cogl_texture_get_height (texture)));
|
|
GE (glBindRenderbuffer (GL_RENDERBUFFER, 0));
|
|
GE (glFramebufferRenderbuffer (GL_FRAMEBUFFER,
|
|
GL_DEPTH_ATTACHMENT,
|
|
GL_RENDERBUFFER, gl_depth_handle));
|
|
offscreen->renderbuffers =
|
|
g_slist_prepend (offscreen->renderbuffers,
|
|
GUINT_TO_POINTER (gl_depth_handle));
|
|
}
|
|
|
|
if (flags & _TRY_STENCIL)
|
|
{
|
|
GE (glGenRenderbuffers (1, &gl_stencil_handle));
|
|
GE (glBindRenderbuffer (GL_RENDERBUFFER, gl_stencil_handle));
|
|
GE (glRenderbufferStorage (GL_RENDERBUFFER, GL_STENCIL_INDEX8,
|
|
cogl_texture_get_width (texture),
|
|
cogl_texture_get_height (texture)));
|
|
GE (glBindRenderbuffer (GL_RENDERBUFFER, 0));
|
|
GE (glFramebufferRenderbuffer (GL_FRAMEBUFFER,
|
|
GL_STENCIL_ATTACHMENT,
|
|
GL_RENDERBUFFER, gl_stencil_handle));
|
|
offscreen->renderbuffers =
|
|
g_slist_prepend (offscreen->renderbuffers,
|
|
GUINT_TO_POINTER (gl_stencil_handle));
|
|
}
|
|
|
|
/* Make sure it's complete */
|
|
status = glCheckFramebufferStatus (GL_FRAMEBUFFER);
|
|
|
|
if (status != GL_FRAMEBUFFER_COMPLETE)
|
|
{
|
|
GSList *l;
|
|
|
|
GE (glDeleteFramebuffers (1, &fbo_gl_handle));
|
|
|
|
for (l = offscreen->renderbuffers; l; l = l->next)
|
|
{
|
|
GLuint renderbuffer = GPOINTER_TO_UINT (l->data);
|
|
GE (glDeleteRenderbuffers (1, &renderbuffer));
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
CoglHandle
|
|
cogl_offscreen_new_to_texture (CoglHandle texhandle)
|
|
{
|
|
CoglOffscreen *offscreen;
|
|
static TryFBOFlags flags;
|
|
static gboolean have_working_flags = FALSE;
|
|
|
|
_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 */
|
|
if (cogl_texture_is_sliced (texhandle))
|
|
return COGL_INVALID_HANDLE;
|
|
|
|
/* XXX: The framebuffer_object spec isn't clear in defining whether attaching
|
|
* a texture as a renderbuffer with mipmap filtering enabled while the
|
|
* mipmaps have not been uploaded should result in an incomplete framebuffer
|
|
* object. (different drivers make different decisions)
|
|
*
|
|
* To avoid an error with drivers that do consider this a problem we
|
|
* explicitly set non mipmapped filters here. These will later be reset when
|
|
* the texture is actually used for rendering according to the filters set on
|
|
* the corresponding CoglMaterial.
|
|
*/
|
|
_cogl_texture_set_filters (texhandle, GL_NEAREST, GL_NEAREST);
|
|
|
|
offscreen = g_new0 (CoglOffscreen, 1);
|
|
offscreen->texture = cogl_handle_ref (texhandle);
|
|
|
|
if ((have_working_flags &&
|
|
try_creating_fbo (offscreen, flags, texhandle)) ||
|
|
try_creating_fbo (offscreen, flags = _TRY_DEPTH_STENCIL, texhandle) ||
|
|
try_creating_fbo (offscreen, flags = _TRY_DEPTH | _TRY_STENCIL,
|
|
texhandle) ||
|
|
try_creating_fbo (offscreen, flags = _TRY_STENCIL, texhandle) ||
|
|
try_creating_fbo (offscreen, flags = _TRY_DEPTH, texhandle) ||
|
|
try_creating_fbo (offscreen, flags = 0, texhandle))
|
|
{
|
|
/* Record that the last set of flags succeeded so that we can
|
|
try that set first next time */
|
|
have_working_flags = TRUE;
|
|
|
|
_cogl_framebuffer_init (COGL_FRAMEBUFFER (offscreen),
|
|
COGL_FRAMEBUFFER_TYPE_OFFSCREEN,
|
|
cogl_texture_get_width (texhandle),
|
|
cogl_texture_get_height (texhandle));
|
|
|
|
return _cogl_offscreen_handle_new (offscreen);
|
|
}
|
|
else
|
|
{
|
|
g_free (offscreen);
|
|
/* XXX: This API should probably have been defined to take a GError */
|
|
g_warning ("%s: Failed to create an OpenGL framebuffer", G_STRLOC);
|
|
return COGL_INVALID_HANDLE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
_cogl_offscreen_free (CoglOffscreen *offscreen)
|
|
{
|
|
GSList *l;
|
|
|
|
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
|
|
|
/* Chain up to parent */
|
|
_cogl_framebuffer_free (COGL_FRAMEBUFFER (offscreen));
|
|
|
|
for (l = offscreen->renderbuffers; l; l = l->next)
|
|
{
|
|
GLuint renderbuffer = GPOINTER_TO_UINT (l->data);
|
|
GE (glDeleteRenderbuffers (1, &renderbuffer));
|
|
}
|
|
g_slist_free (offscreen->renderbuffers);
|
|
|
|
GE (glDeleteFramebuffers (1, &offscreen->fbo_handle));
|
|
|
|
if (offscreen->texture != COGL_INVALID_HANDLE)
|
|
cogl_handle_unref (offscreen->texture);
|
|
|
|
g_free (offscreen);
|
|
}
|
|
|
|
CoglHandle
|
|
_cogl_onscreen_new (void)
|
|
{
|
|
CoglOnscreen *onscreen;
|
|
|
|
/* XXX: Until we have full winsys support in Cogl then we can't fully
|
|
* implement CoglOnscreen framebuffers, since we can't, e.g. keep track of
|
|
* the window size. */
|
|
|
|
onscreen = g_new0 (CoglOnscreen, 1);
|
|
_cogl_framebuffer_init (COGL_FRAMEBUFFER (onscreen),
|
|
COGL_FRAMEBUFFER_TYPE_ONSCREEN,
|
|
0xdeadbeef, /* width */
|
|
0xdeadbeef); /* height */
|
|
|
|
return _cogl_onscreen_handle_new (onscreen);
|
|
}
|
|
|
|
static void
|
|
_cogl_onscreen_free (CoglOnscreen *onscreen)
|
|
{
|
|
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
|
|
|
/* Chain up to parent */
|
|
_cogl_framebuffer_free (COGL_FRAMEBUFFER (onscreen));
|
|
|
|
g_free (onscreen);
|
|
}
|
|
|
|
void
|
|
_cogl_onscreen_clutter_backend_set_size (int width, int height)
|
|
{
|
|
CoglFramebuffer *framebuffer;
|
|
|
|
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
|
|
|
framebuffer = COGL_FRAMEBUFFER (ctx->window_buffer);
|
|
|
|
if (framebuffer->width == width && framebuffer->height == height)
|
|
return;
|
|
|
|
framebuffer->width = width;
|
|
framebuffer->height = height;
|
|
|
|
/* We'll need to recalculate the GL viewport state derived
|
|
* from the Cogl viewport */
|
|
ctx->dirty_gl_viewport = 1;
|
|
}
|
|
|
|
GSList *
|
|
_cogl_create_framebuffer_stack (void)
|
|
{
|
|
GSList *stack = NULL;
|
|
|
|
return g_slist_prepend (stack, COGL_INVALID_HANDLE);
|
|
}
|
|
|
|
void
|
|
_cogl_free_framebuffer_stack (GSList *stack)
|
|
{
|
|
GSList *l;
|
|
|
|
for (l = stack; l != NULL; l = l->next)
|
|
{
|
|
CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (l->data);
|
|
if (framebuffer->type == COGL_FRAMEBUFFER_TYPE_OFFSCREEN)
|
|
_cogl_offscreen_free (COGL_OFFSCREEN (framebuffer));
|
|
else
|
|
_cogl_onscreen_free (COGL_ONSCREEN (framebuffer));
|
|
}
|
|
g_slist_free (stack);
|
|
}
|
|
|
|
/* Set the current framebuffer without checking if it's already the
|
|
* current framebuffer. This is used by cogl_pop_framebuffer while
|
|
* the top of the stack is currently not up to date. */
|
|
static void
|
|
_cogl_set_framebuffer_real (CoglFramebuffer *framebuffer)
|
|
{
|
|
CoglHandle *entry;
|
|
|
|
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
|
|
|
cogl_flush ();
|
|
|
|
entry = &ctx->framebuffer_stack->data;
|
|
|
|
ctx->dirty_bound_framebuffer = 1;
|
|
ctx->dirty_gl_viewport = 1;
|
|
|
|
if (framebuffer != COGL_INVALID_HANDLE)
|
|
cogl_handle_ref (framebuffer);
|
|
if (*entry != COGL_INVALID_HANDLE)
|
|
cogl_handle_unref (*entry);
|
|
|
|
*entry = framebuffer;
|
|
|
|
/* We've effectively just switched the current modelview and
|
|
* projection matrix stacks and clip state so we need to dirty
|
|
* them to ensure they get flushed for the next batch of geometry
|
|
* we flush */
|
|
_cogl_matrix_stack_dirty (framebuffer->modelview_stack);
|
|
_cogl_matrix_stack_dirty (framebuffer->projection_stack);
|
|
_cogl_clip_stack_state_dirty (&framebuffer->clip_state);
|
|
}
|
|
|
|
void
|
|
cogl_set_framebuffer (CoglHandle handle)
|
|
{
|
|
g_return_if_fail (cogl_is_framebuffer (handle));
|
|
|
|
if (_cogl_get_framebuffer () != handle)
|
|
_cogl_set_framebuffer_real (COGL_FRAMEBUFFER (handle));
|
|
}
|
|
|
|
/* XXX: deprecated API */
|
|
void
|
|
cogl_set_draw_buffer (CoglBufferTarget target, CoglHandle handle)
|
|
{
|
|
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
|
|
|
if (target == COGL_WINDOW_BUFFER)
|
|
handle = ctx->window_buffer;
|
|
|
|
cogl_set_framebuffer (handle);
|
|
}
|
|
|
|
CoglHandle
|
|
_cogl_get_framebuffer (void)
|
|
{
|
|
_COGL_GET_CONTEXT (ctx, NULL);
|
|
|
|
g_assert (ctx->framebuffer_stack);
|
|
|
|
return (CoglHandle)ctx->framebuffer_stack->data;
|
|
}
|
|
|
|
void
|
|
cogl_push_framebuffer (CoglHandle buffer)
|
|
{
|
|
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
|
|
|
g_return_if_fail (cogl_is_framebuffer (buffer));
|
|
g_assert (ctx->framebuffer_stack);
|
|
|
|
cogl_flush ();
|
|
|
|
ctx->framebuffer_stack =
|
|
g_slist_prepend (ctx->framebuffer_stack, COGL_INVALID_HANDLE);
|
|
|
|
cogl_set_framebuffer (buffer);
|
|
}
|
|
|
|
/* XXX: deprecated API */
|
|
void
|
|
cogl_push_draw_buffer (void)
|
|
{
|
|
cogl_push_framebuffer (_cogl_get_framebuffer ());
|
|
}
|
|
|
|
void
|
|
cogl_pop_framebuffer (void)
|
|
{
|
|
CoglHandle to_pop;
|
|
CoglHandle to_restore;
|
|
|
|
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
|
|
|
g_assert (ctx->framebuffer_stack != NULL);
|
|
g_assert (ctx->framebuffer_stack->next != NULL);
|
|
|
|
to_pop = ctx->framebuffer_stack->data;
|
|
to_restore = ctx->framebuffer_stack->next->data;
|
|
|
|
cogl_flush ();
|
|
|
|
cogl_handle_unref (to_pop);
|
|
ctx->framebuffer_stack =
|
|
g_slist_remove_link (ctx->framebuffer_stack,
|
|
ctx->framebuffer_stack);
|
|
|
|
/* If the framebuffer has changed as a result of popping the top
|
|
* then re-assert the current buffer so as to dirty state as
|
|
* necessary. */
|
|
if (to_pop != to_restore)
|
|
_cogl_set_framebuffer_real (to_restore);
|
|
}
|
|
|
|
/* XXX: deprecated API */
|
|
void
|
|
cogl_pop_draw_buffer (void)
|
|
{
|
|
cogl_pop_framebuffer ();
|
|
}
|
|
|
|
void
|
|
_cogl_framebuffer_flush_state (CoglHandle handle,
|
|
CoglFramebufferFlushFlags flags)
|
|
{
|
|
CoglFramebuffer *framebuffer;
|
|
|
|
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
|
|
|
framebuffer = COGL_FRAMEBUFFER (handle);
|
|
|
|
if (cogl_features_available (COGL_FEATURE_OFFSCREEN) &&
|
|
ctx->dirty_bound_framebuffer)
|
|
{
|
|
if (framebuffer->type == COGL_FRAMEBUFFER_TYPE_OFFSCREEN)
|
|
{
|
|
GE (glBindFramebuffer (GL_FRAMEBUFFER,
|
|
COGL_OFFSCREEN (framebuffer)->fbo_handle));
|
|
}
|
|
else
|
|
GE (glBindFramebuffer (GL_FRAMEBUFFER, 0));
|
|
ctx->dirty_bound_framebuffer = FALSE;
|
|
}
|
|
|
|
if (ctx->dirty_gl_viewport)
|
|
{
|
|
int gl_viewport_y;
|
|
|
|
/* Convert the Cogl viewport y offset to an OpenGL viewport y offset
|
|
* NB: OpenGL defines its window and viewport origins to be bottom
|
|
* left, while Cogl defines them to be top left.
|
|
* NB: We render upside down to offscreen framebuffers so we don't
|
|
* need to convert the y offset in this case. */
|
|
if (cogl_is_offscreen (framebuffer))
|
|
gl_viewport_y = framebuffer->viewport_y;
|
|
else
|
|
gl_viewport_y = framebuffer->height -
|
|
(framebuffer->viewport_y + framebuffer->viewport_height);
|
|
|
|
GE (glViewport (framebuffer->viewport_x,
|
|
gl_viewport_y,
|
|
framebuffer->viewport_width,
|
|
framebuffer->viewport_height));
|
|
ctx->dirty_gl_viewport = FALSE;
|
|
}
|
|
|
|
/* XXX: Flushing clip state may trash the modelview and projection
|
|
* matrices so we must do it before flushing the matrices...
|
|
*/
|
|
_cogl_flush_clip_state (&framebuffer->clip_state);
|
|
|
|
if (!(flags & COGL_FRAMEBUFFER_FLUSH_SKIP_MODELVIEW))
|
|
_cogl_matrix_stack_flush_to_gl (framebuffer->modelview_stack,
|
|
COGL_MATRIX_MODELVIEW);
|
|
|
|
_cogl_matrix_stack_flush_to_gl (framebuffer->projection_stack,
|
|
COGL_MATRIX_PROJECTION);
|
|
}
|
|
|