From cb344560dcf363fdd4ac830db6f59426ceae8c3b Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Thu, 19 Feb 2009 22:37:08 -0500 Subject: [PATCH] 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 --- cogl-offscreen.h | 14 +++++++++ gl/cogl-context.c | 7 ++++- gl/cogl-context.h | 8 ++++- gl/cogl-fbo.c | 75 +++++++++++++++++++++++++++++++++++++++++++-- gles/cogl-context.c | 7 ++++- gles/cogl-context.h | 8 ++++- gles/cogl-fbo.c | 75 +++++++++++++++++++++++++++++++++++++++++++-- 7 files changed, 184 insertions(+), 10 deletions(-) diff --git a/cogl-offscreen.h b/cogl-offscreen.h index 17d2e54d6..6be6cf0f2 100644 --- a/cogl-offscreen.h +++ b/cogl-offscreen.h @@ -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__ */ diff --git a/gl/cogl-context.c b/gl/cogl-context.c index 7f757f45e..986da8aa8 100644 --- a/gl/cogl-context.c +++ b/gl/cogl-context.c @@ -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; diff --git a/gl/cogl-context.h b/gl/cogl-context.h index 602240793..2660ed812 100644 --- a/gl/cogl-context.h +++ b/gl/cogl-context.h @@ -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; diff --git a/gl/cogl-fbo.c b/gl/cogl-fbo.c index ce703d9a6..a5f6e22fd 100644 --- a/gl/cogl-fbo.c +++ b/gl/cogl-fbo.c @@ -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); } diff --git a/gles/cogl-context.c b/gles/cogl-context.c index c5bc2e3c6..d70d59d35 100644 --- a/gles/cogl-context.c +++ b/gles/cogl-context.c @@ -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; diff --git a/gles/cogl-context.h b/gles/cogl-context.h index 994b100ff..3542e038d 100644 --- a/gles/cogl-context.h +++ b/gles/cogl-context.h @@ -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; diff --git a/gles/cogl-fbo.c b/gles/cogl-fbo.c index 6f2aef569..2b8aa46fa 100644 --- a/gles/cogl-fbo.c +++ b/gles/cogl-fbo.c @@ -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 */