add cogl_push_draw_buffer() and cogl_pop_draw_buffer()

These are necessary if nesting redirections to an fbo,
otherwise there's no way to know how to restore
previous state.

glPushAttrib(GL_COLOR_BUFFER_BIT) would save draw buffer
state, but also saves a lot of other stuff, and
cogl_draw_buffer() relies on knowing about all draw
buffer state changes. So we have to implement a
draw buffer stack ourselves.

Signed-off-by: Robert Bragg <robert@linux.intel.com>
This commit is contained in:
Havoc Pennington 2009-02-19 22:37:08 -05:00 committed by Robert Bragg
parent d5fc61102f
commit e47b198225
7 changed files with 184 additions and 10 deletions

View File

@ -128,6 +128,20 @@ void cogl_offscreen_blit_region (CoglHandle src_buffer,
void cogl_draw_buffer (CoglBufferTarget target,
CoglHandle offscreen);
/**
* cogl_push_draw_buffer:
*
* Save cogl_draw_buffer() state.
*/
void cogl_push_draw_buffer (void);
/**
* cogl_pop_draw_buffer:
*
* Restore cogl_draw_buffer() state.
*/
void cogl_pop_draw_buffer (void);
G_END_DECLS
#endif /* __COGL_OFFSCREEN_H__ */

View File

@ -42,6 +42,7 @@ cogl_create_context ()
{
GLubyte default_texture_data[] = { 0xff, 0xff, 0xff, 0x0 };
gulong enable_flags = 0;
CoglDrawBufferState *draw_buffer;
if (_context != NULL)
return FALSE;
@ -78,7 +79,11 @@ cogl_create_context ()
sizeof (CoglLayerInfo));
_context->n_texcoord_arrays_enabled = 0;
_context->draw_buffer = COGL_WINDOW_BUFFER;
draw_buffer = g_slice_new0 (CoglDrawBufferState);
draw_buffer->target = COGL_WINDOW_BUFFER;
draw_buffer->offscreen = COGL_INVALID_HANDLE;
_context->draw_buffer_stack =
g_slist_prepend (NULL, draw_buffer);
_context->path_nodes = g_array_new (FALSE, FALSE, sizeof (CoglPathNode));
_context->last_path = 0;

View File

@ -36,6 +36,12 @@ typedef struct
GLubyte c[4];
} CoglTextureGLVertex;
typedef struct
{
CoglBufferTarget target;
CoglHandle offscreen;
} CoglDrawBufferState;
typedef struct
{
/* Features cache */
@ -82,7 +88,7 @@ typedef struct
guint n_texcoord_arrays_enabled;
/* Framebuffer objects */
CoglBufferTarget draw_buffer;
GSList *draw_buffer_stack;
/* Clip stack */
CoglClipStackState clip;

View File

@ -242,9 +242,13 @@ void
cogl_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)
{
/* Make sure it is a valid fbo handle */
@ -254,7 +258,7 @@ cogl_draw_buffer (CoglBufferTarget target, CoglHandle offscreen)
fbo = _cogl_offscreen_pointer_from_handle (offscreen);
/* Check current draw buffer target */
if (ctx->draw_buffer != COGL_OFFSCREEN_BUFFER)
if (draw_buffer->target != COGL_OFFSCREEN_BUFFER)
{
/* Push the viewport and matrix setup if redirecting
from a non-screen buffer */
@ -303,7 +307,7 @@ cogl_draw_buffer (CoglBufferTarget target, CoglHandle offscreen)
(target & COGL_MASK_BUFFER))
{
/* Check current draw buffer target */
if (ctx->draw_buffer == COGL_OFFSCREEN_BUFFER)
if (draw_buffer->target == COGL_OFFSCREEN_BUFFER)
{
/* Pop viewport and matrices if redirecting back
from an offscreen buffer */
@ -338,5 +342,70 @@ cogl_draw_buffer (CoglBufferTarget target, CoglHandle offscreen)
}
/* Store new target */
ctx->draw_buffer = 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_draw_buffer() only works if
* to_pop is still on top of the stack, because
* cogl_draw_buffer() needs to know the previous
* state.
*/
cogl_draw_buffer (to_restore->target, to_restore->offscreen);
/* cogl_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);
}

View File

@ -44,6 +44,7 @@ cogl_create_context ()
{
GLubyte default_texture_data[] = { 0xff, 0xff, 0xff, 0x0 };
gulong enable_flags = 0;
CoglDrawBufferState *draw_buffer;
if (_context != NULL)
return FALSE;
@ -81,7 +82,11 @@ cogl_create_context ()
sizeof (CoglLayerInfo));
_context->n_texcoord_arrays_enabled = 0;
_context->draw_buffer = COGL_WINDOW_BUFFER;
draw_buffer = g_slice_new0 (CoglDrawBufferState);
draw_buffer->target = COGL_WINDOW_BUFFER;
draw_buffer->offscreen = COGL_INVALID_HANDLE;
_context->draw_buffer_stack =
g_slist_prepend (NULL, draw_buffer);
_context->path_nodes = g_array_new (FALSE, FALSE, sizeof (CoglPathNode));
_context->last_path = 0;

View File

@ -38,6 +38,12 @@ typedef struct
GLubyte c[4];
} CoglTextureGLVertex;
typedef struct
{
CoglBufferTarget target;
CoglHandle offscreen;
} CoglDrawBufferState;
typedef struct
{
/* Features cache */
@ -84,7 +90,7 @@ typedef struct
guint n_texcoord_arrays_enabled;
/* Framebuffer objects */
CoglBufferTarget draw_buffer;
GSList *draw_buffer_stack;
/* Clip stack */
CoglClipStackState clip;

View File

@ -180,9 +180,13 @@ void
cogl_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;
@ -195,7 +199,7 @@ cogl_draw_buffer (CoglBufferTarget target, CoglHandle offscreen)
fbo = _cogl_offscreen_pointer_from_handle (offscreen);
/* Check current draw buffer target */
if (ctx->draw_buffer != COGL_OFFSCREEN_BUFFER)
if (draw_buffer->target != COGL_OFFSCREEN_BUFFER)
{
/* Push the viewport and matrix setup if redirecting
from a non-screen buffer */
@ -249,7 +253,7 @@ cogl_draw_buffer (CoglBufferTarget target, CoglHandle offscreen)
(target & COGL_MASK_BUFFER))
{
/* Check current draw buffer target */
if (ctx->draw_buffer == COGL_OFFSCREEN_BUFFER)
if (draw_buffer->target == COGL_OFFSCREEN_BUFFER)
{
/* Pop viewport and matrices if redirecting back
from an offscreen buffer */
@ -285,7 +289,72 @@ cogl_draw_buffer (CoglBufferTarget target, CoglHandle offscreen)
}
/* Store new target */
ctx->draw_buffer = 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_draw_buffer() only works if
* to_pop is still on top of the stack, because
* cogl_draw_buffer() needs to know the previous
* state.
*/
cogl_draw_buffer (to_restore->target, to_restore->offscreen);
/* cogl_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 */