From cc0af441f2fe3a731a5338d6fc2c585f4bc017f4 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Tue, 15 Sep 2009 22:15:03 +0100 Subject: [PATCH 01/24] [cogl-fbo] Remove poorly documented workaround for unknown driver/hardware The comment just said: "Some implementation require a clear before drawing to an fbo. Luckily it is affected by scissor test." and did a scissored clear, which is clearly a driver bug workaround, but for what driver? The fact that it was copied into the gles backend (or vica versa is also suspicious since it seems unlikely that the workaround is necessary for both backends.) We can easily restore the workaround with a better comment if this problem really still exists on current drivers, but for now I'd rather minimize hand-wavey workaround code that can't be tested. --- cogl/driver/gl/cogl-fbo.c | 10 ---------- cogl/driver/gles/cogl-fbo.c | 19 ------------------- 2 files changed, 29 deletions(-) diff --git a/cogl/driver/gl/cogl-fbo.c b/cogl/driver/gl/cogl-fbo.c index 1bd88aeea..99315e6b0 100644 --- a/cogl/driver/gl/cogl-fbo.c +++ b/cogl/driver/gl/cogl-fbo.c @@ -225,16 +225,6 @@ cogl_set_draw_buffer (CoglBufferTarget target, CoglHandle offscreen) /* 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) { diff --git a/cogl/driver/gles/cogl-fbo.c b/cogl/driver/gles/cogl-fbo.c index 0b1006970..34f6a5946 100644 --- a/cogl/driver/gles/cogl-fbo.c +++ b/cogl/driver/gles/cogl-fbo.c @@ -157,9 +157,6 @@ cogl_set_draw_buffer (CoglBufferTarget target, CoglHandle offscreen) 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; @@ -197,22 +194,6 @@ cogl_set_draw_buffer (CoglBufferTarget target, CoglHandle offscreen) /* 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]); - } else if (target & COGL_WINDOW_BUFFER) { From 6b2ff320cab3404a532e194cae853ee9823568f7 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Tue, 15 Sep 2009 22:19:01 +0100 Subject: [PATCH 02/24] [cogl-fbo] Bring the gles code more in line with gl code Over time the two cogl-fbo.c files have needlessly diverged as bug fixes or cleanups went into one version but not the other. This tries to bring them back in line with each other. It should actually be simple enough to move cogl-fbo.c to be a common file, and simply not build it for GLES 1.1, so maybe I'll follow up with such a patch soon. --- cogl/driver/gles/cogl-fbo.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cogl/driver/gles/cogl-fbo.c b/cogl/driver/gles/cogl-fbo.c index 34f6a5946..41b5c6b0c 100644 --- a/cogl/driver/gles/cogl-fbo.c +++ b/cogl/driver/gles/cogl-fbo.c @@ -152,6 +152,8 @@ cogl_set_draw_buffer (CoglBufferTarget target, CoglHandle offscreen) _COGL_GET_CONTEXT (ctx, NO_RETVAL); + _cogl_journal_flush (); + g_assert (ctx->draw_buffer_stack != NULL); draw_buffer = ctx->draw_buffer_stack->data; @@ -186,10 +188,16 @@ cogl_set_draw_buffer (CoglBufferTarget target, CoglHandle offscreen) } /* Setup new viewport and matrices */ +<<<<<<< HEAD:clutter/cogl/cogl/driver/gles/cogl-fbo.c GE( glViewport (0, 0, fbo->width, fbo->height) ); _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); +======= + cogl_viewport (fbo->width, fbo->height); + _cogl_current_matrix_translate (-1.0f, -1.0f, 0.0f); + _cogl_current_matrix_scale (2.0f / fbo->width, 2.0f / fbo->height, 1.0f); +>>>>>>> c3e471c... [cogl-fbo] Bring the gles code more in line with gl code:clutter/cogl/cogl/driver/gles/cogl-fbo.c /* Bind offscreen framebuffer object */ GE( glBindFramebuffer (GL_FRAMEBUFFER, fbo->gl_handle) ); From bb3a008318c97f573d98b09d7e8fe911548947fa Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Fri, 25 Sep 2009 14:34:34 +0100 Subject: [PATCH 03/24] [draw-buffers] First pass at overhauling Cogl's framebuffer management Cogl's support for offscreen rendering was originally written just to support the clutter_texture_new_from_actor API and due to lack of documentation and several confusing - non orthogonal - side effects of using the API it wasn't really possible to use directly. This commit does a number of things: - It removes {gl,gles}/cogl-fbo.{c,h} and adds shared cogl-draw-buffer.{c,h} files instead which should be easier to maintain. - internally CoglFbo objects are now called CoglDrawBuffers. A CoglDrawBuffer is an abstract base class that is inherited from to implement CoglOnscreen and CoglOffscreen draw buffers. CoglOffscreen draw buffers will initially be used to support the cogl_offscreen_new_to_texture API, and CoglOnscreen draw buffers will start to be used internally to represent windows as we aim to migrate some of Clutter's backend code to Cogl. - It makes draw buffer objects the owners of the following state: - viewport - projection matrix stack - modelview matrix stack - clip state (This means when you switch between draw buffers you will automatically be switching to their associated viewport, matrix and clip state) Aside from hopefully making cogl_offscreen_new_to_texture be more useful short term by having simpler and well defined semantics for cogl_set_draw_buffer, as mentioned above this is the first step for a couple of other things: - Its a step toward moving ownership for windows down from Clutter backends into Cogl, by (internally at least) introducing the CoglOnscreen draw buffer. Note: the plan is that cogl_set_draw_buffer will accept on or offscreen draw buffer handles, and the "target" argument will become redundant since we will instead query the type of the given draw buffer handle. - Because we have a common type for on and offscreen framebuffers we can provide a unified API for framebuffer management. Things like: - blitting between buffers - managing ancillary buffers (e.g. attaching depth and stencil buffers) - size requisition - clearing --- cogl/Makefile.am | 2 + cogl/cogl-clip-stack.c | 211 +++++++--- cogl/cogl-clip-stack.h | 9 +- cogl/cogl-context.c | 34 +- cogl/cogl-context.h | 20 +- cogl/cogl-draw-buffer-private.h | 126 ++++++ cogl/cogl-draw-buffer.c | 555 +++++++++++++++++++++++++ cogl/cogl-journal.c | 33 +- cogl/cogl-primitives.c | 19 +- cogl/cogl-texture.c | 19 +- cogl/cogl-vertex-buffer.c | 14 +- cogl/cogl.c | 222 ++++++---- cogl/cogl.h.in | 1 + cogl/driver/gl/Makefile.am | 2 - cogl/driver/gl/cogl-context-driver.c | 19 +- cogl/driver/gl/cogl-context-driver.h | 23 +- cogl/driver/gl/cogl-defines.h.in | 28 +- cogl/driver/gl/cogl-fbo.c | 315 -------------- cogl/driver/gl/cogl-fbo.h | 39 -- cogl/driver/gl/cogl-primitives.c | 99 +++-- cogl/driver/gl/cogl-texture-driver.c | 2 +- cogl/driver/gl/cogl.c | 110 ++--- cogl/driver/gles/Makefile.am | 2 - cogl/driver/gles/cogl-context-driver.c | 10 + cogl/driver/gles/cogl-context-driver.h | 17 +- cogl/driver/gles/cogl-defines.h.in | 70 ++++ cogl/driver/gles/cogl-fbo.c | 328 --------------- cogl/driver/gles/cogl-fbo.h | 39 -- cogl/driver/gles/cogl-primitives.c | 121 ++++-- cogl/driver/gles/cogl.c | 75 ++++ 30 files changed, 1474 insertions(+), 1090 deletions(-) create mode 100644 cogl/cogl-draw-buffer-private.h create mode 100644 cogl/cogl-draw-buffer.c delete mode 100644 cogl/driver/gl/cogl-fbo.c delete mode 100644 cogl/driver/gl/cogl-fbo.h delete mode 100644 cogl/driver/gles/cogl-fbo.c delete mode 100644 cogl/driver/gles/cogl-fbo.h diff --git a/cogl/Makefile.am b/cogl/Makefile.am index 81c5186d2..8988f8d35 100644 --- a/cogl/Makefile.am +++ b/cogl/Makefile.am @@ -120,6 +120,8 @@ libclutter_cogl_la_SOURCES = \ $(srcdir)/cogl-spans.c \ $(srcdir)/cogl-journal-private.h \ $(srcdir)/cogl-journal.c \ + $(srcdir)/cogl-draw-buffer-private.h \ + $(srcdir)/cogl-draw-buffer.c \ $(BUILT_SOURCES) \ $(NULL) diff --git a/cogl/cogl-clip-stack.c b/cogl/cogl-clip-stack.c index a0dc3bc49..8cf59d197 100644 --- a/cogl/cogl-clip-stack.c +++ b/cogl/cogl-clip-stack.c @@ -34,6 +34,7 @@ #include "cogl-primitives.h" #include "cogl-context.h" #include "cogl-internal.h" +#include "cogl-draw-buffer-private.h" /* These are defined in the particular backend (float in GL vs fixed in GL ES) */ @@ -119,15 +120,24 @@ cogl_clip_push_window_rect (float x_offset, float width, float height) { - CoglClipStackEntryWindowRect *entry; + CoglHandle draw_buffer; + CoglClipStackState *clip_state; CoglClipStack *stack; - float v[4]; + CoglClipStackEntryWindowRect *entry; + float viewport_height; _COGL_GET_CONTEXT (ctx, NO_RETVAL); - stack = (CoglClipStack *) ctx->clip.stacks->data; + /* We don't log clip stack changes in the journal so we must flush + * it before making modifications */ + _cogl_journal_flush (); - cogl_get_viewport (v); + draw_buffer = _cogl_get_draw_buffer (); + clip_state = _cogl_draw_buffer_get_clip_state (draw_buffer); + + stack = clip_state->stacks->data; + + viewport_height = _cogl_draw_buffer_get_viewport_height (draw_buffer); entry = g_slice_new (CoglClipStackEntryWindowRect); @@ -135,14 +145,14 @@ cogl_clip_push_window_rect (float x_offset, * with (0,0) at bottom left. */ entry->type = COGL_CLIP_STACK_WINDOW_RECT; entry->x0 = x_offset; - entry->y0 = v[3] - y_offset - height; + entry->y0 = viewport_height - y_offset - height; entry->x1 = x_offset + width; - entry->y1 = v[3] - y_offset; + entry->y1 = viewport_height - y_offset; /* Store it in the stack */ stack->stack_top = g_list_prepend (stack->stack_top, entry); - ctx->clip.stack_dirty = TRUE; + clip_state->stack_dirty = TRUE; } /* Scale from OpenGL <-1,1> coordinates system to window coordinates @@ -214,17 +224,26 @@ cogl_clip_push (float x_offset, float width, float height) { - CoglClipStackEntryRect *entry; + CoglHandle draw_buffer; + CoglClipStackState *clip_state; CoglClipStack *stack; + CoglClipStackEntryRect *entry; _COGL_GET_CONTEXT (ctx, NO_RETVAL); + /* We don't log clip stack changes in the journal so we must flush + * it before making modifications */ + _cogl_journal_flush (); + /* Try and catch window space rectangles so we can redirect to * cogl_clip_push_window_rect which will use scissoring. */ if (try_pushing_rect_as_window_rect (x_offset, y_offset, width, height)) return; - stack = (CoglClipStack *) ctx->clip.stacks->data; + draw_buffer = _cogl_get_draw_buffer (); + clip_state = _cogl_draw_buffer_get_clip_state (draw_buffer); + + stack = clip_state->stacks->data; entry = g_slice_new (CoglClipStackEntryRect); @@ -240,18 +259,27 @@ cogl_clip_push (float x_offset, /* Store it in the stack */ stack->stack_top = g_list_prepend (stack->stack_top, entry); - ctx->clip.stack_dirty = TRUE; + clip_state->stack_dirty = TRUE; } void cogl_clip_push_from_path_preserve (void) { - CoglClipStackEntryPath *entry; + CoglHandle draw_buffer; + CoglClipStackState *clip_state; CoglClipStack *stack; + CoglClipStackEntryPath *entry; _COGL_GET_CONTEXT (ctx, NO_RETVAL); - stack = (CoglClipStack *) ctx->clip.stacks->data; + /* We don't log clip stack changes in the journal so we must flush + * it before making modifications */ + _cogl_journal_flush (); + + draw_buffer = _cogl_get_draw_buffer (); + clip_state = _cogl_draw_buffer_get_clip_state (draw_buffer); + + stack = clip_state->stacks->data; entry = g_malloc (sizeof (CoglClipStackEntryPath) + sizeof (CoglPathNode) * (ctx->path_nodes->len - 1)); @@ -268,7 +296,7 @@ cogl_clip_push_from_path_preserve (void) /* Store it in the stack */ stack->stack_top = g_list_prepend (stack->stack_top, entry); - ctx->clip.stack_dirty = TRUE; + clip_state->stack_dirty = TRUE; } void @@ -279,16 +307,18 @@ cogl_clip_push_from_path (void) cogl_path_new (); } -void -cogl_clip_pop (void) +static void +_cogl_clip_pop_real (CoglClipStackState *clip_state) { - gpointer entry; CoglClipStack *stack; + gpointer entry; CoglClipStackEntryType type; - _COGL_GET_CONTEXT (ctx, NO_RETVAL); + /* We don't log clip stack changes in the journal so we must flush + * it before making modifications */ + _cogl_journal_flush (); - stack = (CoglClipStack *) ctx->clip.stacks->data; + stack = clip_state->stacks->data; g_return_if_fail (stack->stack_top != NULL); @@ -306,35 +336,57 @@ cogl_clip_pop (void) stack->stack_top = g_list_delete_link (stack->stack_top, stack->stack_top); - ctx->clip.stack_dirty = TRUE; + clip_state->stack_dirty = TRUE; } void -_cogl_clip_stack_rebuild (void) +cogl_clip_pop (void) { - int has_clip_planes = cogl_features_available (COGL_FEATURE_FOUR_CLIP_PLANES); + CoglHandle draw_buffer; + CoglClipStackState *clip_state; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + draw_buffer = _cogl_get_draw_buffer (); + clip_state = _cogl_draw_buffer_get_clip_state (draw_buffer); + + _cogl_clip_pop_real (clip_state); +} + +void +_cogl_flush_clip_state (CoglClipStackState *clip_state) +{ + CoglClipStack *stack; + int has_clip_planes; gboolean using_clip_planes = FALSE; gboolean using_stencil_buffer = FALSE; GList *node; - CoglClipStack *stack; gint scissor_x0 = 0; gint scissor_y0 = 0; gint scissor_x1 = G_MAXINT; gint scissor_y1 = G_MAXINT; - CoglMatrixStack *modelview_stack; + CoglMatrixStack *modelview_stack = + _cogl_draw_buffer_get_modelview_stack (_cogl_get_draw_buffer ()); - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - - modelview_stack = ctx->modelview_stack; + if (!clip_state->stack_dirty) + return; /* The current primitive journal does not support tracking changes to the * clip stack... */ _cogl_journal_flush (); - stack = (CoglClipStack *) ctx->clip.stacks->data; + /* XXX: the handling of clipping is quite complex. It may involve use of + * the Cogl Journal or other Cogl APIs which may end up recursively + * wanting to ensure the clip state is flushed. We need to ensure we + * don't recurse infinitely... + */ + clip_state->stack_dirty = FALSE; - ctx->clip.stack_dirty = FALSE; - ctx->clip.stencil_used = FALSE; + has_clip_planes = cogl_features_available (COGL_FEATURE_FOUR_CLIP_PLANES); + + stack = clip_state->stacks->data; + + clip_state->stencil_used = FALSE; _cogl_disable_clip_planes (); _cogl_disable_stencil_buffer (); @@ -433,74 +485,111 @@ _cogl_clip_stack_rebuild (void) scissor_y1 - scissor_y0)); } - ctx->clip.stencil_used = using_stencil_buffer; + clip_state->stencil_used = using_stencil_buffer; } +/* XXX: This should never have been made public API! */ void cogl_clip_ensure (void) { - _COGL_GET_CONTEXT (ctx, NO_RETVAL); + CoglClipStackState *clip_state; - if (ctx->clip.stack_dirty) - _cogl_clip_stack_rebuild (); + clip_state = _cogl_draw_buffer_get_clip_state (_cogl_get_draw_buffer ()); + _cogl_flush_clip_state (clip_state); +} + +static void +_cogl_clip_stack_save_real (CoglClipStackState *clip_state) +{ + CoglClipStack *stack; + + /* We don't log clip stack changes in the journal so we must flush + * it before making modifications */ + _cogl_journal_flush (); + + stack = g_slice_new (CoglClipStack); + stack->stack_top = NULL; + + clip_state->stacks = g_slist_prepend (clip_state->stacks, stack); + clip_state->stack_dirty = TRUE; } void cogl_clip_stack_save (void) { - CoglClipStack *stack; + CoglHandle draw_buffer; + CoglClipStackState *clip_state; _COGL_GET_CONTEXT (ctx, NO_RETVAL); - stack = g_slice_new (CoglClipStack); - stack->stack_top = NULL; + draw_buffer = _cogl_get_draw_buffer (); + clip_state = _cogl_draw_buffer_get_clip_state (draw_buffer); - ctx->clip.stacks = g_slist_prepend (ctx->clip.stacks, stack); + _cogl_clip_stack_save_real (clip_state); +} - ctx->clip.stack_dirty = TRUE; +static void +_cogl_clip_stack_restore_real (CoglClipStackState *clip_state) +{ + CoglClipStack *stack; + + g_return_if_fail (clip_state->stacks != NULL); + + /* We don't log clip stack changes in the journal so we must flush + * it before making modifications */ + _cogl_journal_flush (); + + stack = clip_state->stacks->data; + + /* Empty the current stack */ + while (stack->stack_top) + _cogl_clip_pop_real (clip_state); + + /* Revert to an old stack */ + g_slice_free (CoglClipStack, stack); + clip_state->stacks = g_slist_delete_link (clip_state->stacks, + clip_state->stacks); + + clip_state->stack_dirty = TRUE; } void cogl_clip_stack_restore (void) { - CoglClipStack *stack; + CoglHandle draw_buffer; + CoglClipStackState *clip_state; _COGL_GET_CONTEXT (ctx, NO_RETVAL); - g_return_if_fail (ctx->clip.stacks != NULL); + draw_buffer = _cogl_get_draw_buffer (); + clip_state = _cogl_draw_buffer_get_clip_state (draw_buffer); - stack = (CoglClipStack *) ctx->clip.stacks->data; - - /* Empty the current stack */ - while (stack->stack_top) - cogl_clip_pop (); - - /* Revert to an old stack */ - g_slice_free (CoglClipStack, stack); - ctx->clip.stacks = g_slist_delete_link (ctx->clip.stacks, - ctx->clip.stacks); - - ctx->clip.stack_dirty = TRUE; + _cogl_clip_stack_restore_real (clip_state); } void -_cogl_clip_stack_state_init (void) +_cogl_clip_stack_state_init (CoglClipStackState *clip_state) { _COGL_GET_CONTEXT (ctx, NO_RETVAL); - ctx->clip.stacks = NULL; - ctx->clip.stack_dirty = TRUE; + clip_state->stacks = NULL; + clip_state->stack_dirty = TRUE; /* Add an intial stack */ - cogl_clip_stack_save (); + _cogl_clip_stack_save_real (clip_state); } void -_cogl_clip_stack_state_destroy (void) +_cogl_clip_stack_state_destroy (CoglClipStackState *clip_state) { - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - /* Destroy all of the stacks */ - while (ctx->clip.stacks) - cogl_clip_stack_restore (); + while (clip_state->stacks) + _cogl_clip_stack_restore_real (clip_state); } + +void +_cogl_clip_stack_state_dirty (CoglClipStackState *clip_state) +{ + clip_state->stack_dirty = TRUE; +} + diff --git a/cogl/cogl-clip-stack.h b/cogl/cogl-clip-stack.h index 324097b17..0ca041ec4 100644 --- a/cogl/cogl-clip-stack.h +++ b/cogl/cogl-clip-stack.h @@ -35,9 +35,10 @@ struct _CoglClipStackState gboolean stencil_used; }; -void _cogl_clip_stack_state_init (void); -void _cogl_clip_stack_state_destroy (void); -void _cogl_clip_stack_rebuild (void); -void _cogl_clip_stack_merge (void); +void _cogl_clip_stack_state_init (CoglClipStackState *state); +void _cogl_clip_stack_state_destroy (CoglClipStackState *state); +void _cogl_clip_stack_state_dirty (CoglClipStackState *state); + +void _cogl_flush_clip_state (CoglClipStackState *clip_state); #endif /* __COGL_CLIP_STACK_H */ diff --git a/cogl/cogl-context.c b/cogl/cogl-context.c index 6cbc7b7c2..8005aaf23 100644 --- a/cogl/cogl-context.c +++ b/cogl/cogl-context.c @@ -32,6 +32,7 @@ #include "cogl-journal-private.h" #include "cogl-texture-private.h" #include "cogl-material-private.h" +#include "cogl-draw-buffer-private.h" #include @@ -46,7 +47,7 @@ cogl_create_context (void) { GLubyte default_texture_data[] = { 0xff, 0xff, 0xff, 0x0 }; gulong enable_flags = 0; - CoglDrawBufferState *draw_buffer; + CoglHandle window_buffer; if (_context != NULL) return FALSE; @@ -66,8 +67,6 @@ cogl_create_context (void) _context->indirect = gl_is_indirect; _context->flushed_matrix_mode = COGL_MATRIX_MODELVIEW; - _context->modelview_stack = _cogl_matrix_stack_new (); - _context->projection_stack = _cogl_matrix_stack_new (); _context->texture_units = NULL; _context->default_material = cogl_material_new (); @@ -87,11 +86,14 @@ cogl_create_context (void) sizeof (CoglLayerInfo)); _context->n_texcoord_arrays_enabled = 0; - 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->draw_buffer_stack = _cogl_create_draw_buffer_stack (); + window_buffer = _cogl_onscreen_new (); + /* XXX: When setting up the window buffer, cogl_set_draw_buffer + * assumes that the handle can be found in ctx->window_buffer */ + _context->window_buffer = window_buffer; + cogl_set_draw_buffer (COGL_WINDOW_BUFFER, 0/* ignored */); + _context->dirty_bound_framebuffer = TRUE; + _context->dirty_viewport = TRUE; _context->path_nodes = g_array_new (FALSE, FALSE, sizeof (CoglPathNode)); _context->last_path = 0; @@ -99,20 +101,16 @@ cogl_create_context (void) _context->in_begin_gl_block = FALSE; - _context->viewport_width = 0; - _context->viewport_height = 0; - _context->quad_indices_byte = COGL_INVALID_HANDLE; _context->quad_indices_short = COGL_INVALID_HANDLE; _context->quad_indices_short_len = 0; _context->texture_download_material = COGL_INVALID_HANDLE; - /* Initialise the clip stack */ - _cogl_clip_stack_state_init (); - /* Initialise the driver specific state */ + /* TODO: combine these two into one function */ _cogl_create_context_driver (_context); + _cogl_features_init (); /* Create default textures used for fall backs */ _context->default_gl_texture_2d_tex = @@ -146,16 +144,14 @@ cogl_create_context (void) void _cogl_destroy_context () { + if (_context == NULL) return; - _cogl_clip_stack_state_destroy (); - - _cogl_matrix_stack_destroy (_context->modelview_stack); - _cogl_matrix_stack_destroy (_context->projection_stack); - _cogl_destroy_texture_units (); + _cogl_free_draw_buffer_stack (_context->draw_buffer_stack); + if (_context->path_nodes) g_array_free (_context->path_nodes, TRUE); diff --git a/cogl/cogl-context.h b/cogl/cogl-context.h index cf8e3ad84..67e33cc68 100644 --- a/cogl/cogl-context.h +++ b/cogl/cogl-context.h @@ -38,12 +38,6 @@ typedef struct GLubyte c[4]; } CoglTextureGLVertex; -typedef struct -{ - CoglBufferTarget target; - CoglHandle offscreen; -} CoglDrawBufferState; - typedef struct { /* Features cache */ @@ -60,9 +54,6 @@ typedef struct /* Client-side matrix stack or NULL if none */ CoglMatrixMode flushed_matrix_mode; - CoglMatrixStack *projection_stack; - CoglMatrixStack *modelview_stack; - GList *texture_units; /* Cache of inverse projection matrix */ @@ -91,11 +82,11 @@ typedef struct GArray *current_layers; guint n_texcoord_arrays_enabled; - /* Framebuffer objects */ + /* Draw Buffers */ GSList *draw_buffer_stack; - - /* Clip stack */ - CoglClipStackState clip; + CoglHandle window_buffer; + gboolean dirty_bound_framebuffer; + gboolean dirty_viewport; /* Primitives */ floatVec2 path_start; @@ -114,9 +105,6 @@ typedef struct gboolean in_begin_gl_block; - guint viewport_width; - guint viewport_height; - CoglHandle texture_download_material; CoglContextDriver drv; diff --git a/cogl/cogl-draw-buffer-private.h b/cogl/cogl-draw-buffer-private.h new file mode 100644 index 000000000..d5bdf0b11 --- /dev/null +++ b/cogl/cogl-draw-buffer-private.h @@ -0,0 +1,126 @@ +/* + * 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. + */ + +#ifndef __COGL_DRAW_BUFFER_PRIVATE_H +#define __COGL_DRAW_BUFFER_PRIVATE_H + +#include "cogl-handle.h" +#include "cogl-matrix-stack.h" +#include "cogl-clip-stack.h" + +typedef enum _CoglDrawBufferType { + COGL_DRAW_BUFFER_TYPE_ONSCREEN, + COGL_DRAW_BUFFER_TYPE_OFFSCREEN +} CoglDrawBufferType; + +typedef struct +{ + CoglHandleObject _parent; + CoglDrawBufferType type; + int width; + int height; + + CoglMatrixStack *modelview_stack; + CoglMatrixStack *projection_stack; + int viewport_x; + int viewport_y; + int viewport_width; + int viewport_height; + + CoglClipStackState clip_state; +} CoglDrawBuffer; + +#define COGL_DRAW_BUFFER(X) ((CoglDrawBuffer *)(X)) + +typedef struct _CoglDrawBufferStackEntry +{ + CoglBufferTarget target; + CoglHandle draw_buffer; +} CoglDrawBufferStackEntry; + +typedef struct _CoglOffscreen +{ + CoglDrawBuffer _parent; + GLuint fbo_handle; + GLuint gl_stencil_handle; +} CoglOffscreen; + +#define COGL_OFFSCREEN(X) ((CoglOffscreen *)(X)) + +typedef struct _CoglOnscreen +{ + CoglDrawBuffer _parent; +} CoglOnscreen; + +#define COGL_ONSCREEN(X) ((CoglOnscreen *)(X)) + +void +_cogl_draw_buffer_state_init (void); +CoglClipStackState * +_cogl_draw_buffer_get_clip_state (CoglHandle handle); +void +_cogl_draw_buffer_set_viewport (CoglHandle handle, + int x, + int y, + int width, + int height); +int +_cogl_draw_buffer_get_viewport_x (CoglHandle handle); +int +_cogl_draw_buffer_get_viewport_y (CoglHandle handle); +int +_cogl_draw_buffer_get_viewport_width (CoglHandle handle); +int +_cogl_draw_buffer_get_viewport_height (CoglHandle handle); +void +_cogl_draw_buffer_get_viewport4fv (CoglHandle handle, int *viewport); +CoglMatrixStack * +_cogl_draw_buffer_get_modelview_stack (CoglHandle handle); +CoglMatrixStack * +_cogl_draw_buffer_get_projection_stack (CoglHandle handle); + +typedef enum _CoglDrawBufferFlushFlags +{ + /* XXX: When using this, that imples you are going to manually load the + * modelview matrix (via glLoadMatrix). _cogl_matrix_stack_flush_to_gl wont + * be called for draw_buffer->modelview_stack, and the modelview_stack will + * also be marked as dirty. */ + COGL_DRAW_BUFFER_FLUSH_SKIP_MODELVIEW = 1L<<0, +} CoglDrawBufferFlushFlags; + +void +_cogl_draw_buffer_flush_state (CoglHandle handle, + CoglDrawBufferFlushFlags flags); + +CoglHandle +_cogl_onscreen_new (void); + +CoglHandle +_cogl_get_draw_buffer (void); +GSList * +_cogl_create_draw_buffer_stack (void); +void +_cogl_free_draw_buffer_stack (GSList *stack); + +#endif /* __COGL_DRAW_BUFFER_PRIVATE_H */ + diff --git a/cogl/cogl-draw-buffer.c b/cogl/cogl-draw-buffer.c new file mode 100644 index 000000000..41f0a5f91 --- /dev/null +++ b/cogl/cogl-draw-buffer.c @@ -0,0 +1,555 @@ +/* + * 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-draw-buffer-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 + +static void _cogl_draw_buffer_free (CoglDrawBuffer *draw_buffer); +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 CoglDrawBuffer + * abstract class manually. + */ + +gboolean +cogl_is_draw_buffer (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_draw_buffer_init (CoglDrawBuffer *draw_buffer, + CoglDrawBufferType type, + int width, + int height) +{ + draw_buffer->type = type; + draw_buffer->width = width; + draw_buffer->height = height; + draw_buffer->viewport_x = 0; + draw_buffer->viewport_y = 0; + draw_buffer->viewport_width = width; + draw_buffer->viewport_height = height; + + draw_buffer->modelview_stack = _cogl_matrix_stack_new (); + draw_buffer->projection_stack = _cogl_matrix_stack_new (); + + /* Initialise the clip stack */ + _cogl_clip_stack_state_init (&draw_buffer->clip_state); +} + +void +_cogl_draw_buffer_free (CoglDrawBuffer *draw_buffer) +{ + _cogl_clip_stack_state_destroy (&draw_buffer->clip_state); + + _cogl_matrix_stack_destroy (draw_buffer->modelview_stack); + draw_buffer->modelview_stack = NULL; + + _cogl_matrix_stack_destroy (draw_buffer->projection_stack); + draw_buffer->projection_stack = NULL; +} + +CoglClipStackState * +_cogl_draw_buffer_get_clip_state (CoglHandle handle) +{ + CoglDrawBuffer *draw_buffer = COGL_DRAW_BUFFER (handle); + + return &draw_buffer->clip_state; +} + +void +_cogl_draw_buffer_set_viewport (CoglHandle handle, + int x, + int y, + int width, + int height) +{ + CoglDrawBuffer *draw_buffer = COGL_DRAW_BUFFER (handle); + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (draw_buffer->viewport_x == x && + draw_buffer->viewport_y == y && + draw_buffer->viewport_width == width && + draw_buffer->viewport_height == height) + return; + + _cogl_journal_flush (); + + draw_buffer->viewport_x = x; + draw_buffer->viewport_y = y; + draw_buffer->viewport_width = width; + draw_buffer->viewport_height = height; + + if (_cogl_get_draw_buffer () == draw_buffer) + ctx->dirty_viewport = TRUE; +} + +int +_cogl_draw_buffer_get_viewport_x (CoglHandle handle) +{ + CoglDrawBuffer *draw_buffer = COGL_DRAW_BUFFER (handle); + return draw_buffer->viewport_x; +} + +int +_cogl_draw_buffer_get_viewport_y (CoglHandle handle) +{ + CoglDrawBuffer *draw_buffer = COGL_DRAW_BUFFER (handle); + return draw_buffer->viewport_y; +} + +int +_cogl_draw_buffer_get_viewport_width (CoglHandle handle) +{ + CoglDrawBuffer *draw_buffer = COGL_DRAW_BUFFER (handle); + return draw_buffer->viewport_width; +} + +int +_cogl_draw_buffer_get_viewport_height (CoglHandle handle) +{ + CoglDrawBuffer *draw_buffer = COGL_DRAW_BUFFER (handle); + return draw_buffer->viewport_height; +} + +void +_cogl_draw_buffer_get_viewport4fv (CoglHandle handle, int *viewport) +{ + CoglDrawBuffer *draw_buffer = COGL_DRAW_BUFFER (handle); + viewport[0] = draw_buffer->viewport_x; + viewport[1] = draw_buffer->viewport_y; + viewport[2] = draw_buffer->viewport_width; + viewport[3] = draw_buffer->viewport_height; +} + +CoglMatrixStack * +_cogl_draw_buffer_get_modelview_stack (CoglHandle handle) +{ + CoglDrawBuffer *draw_buffer = COGL_DRAW_BUFFER (handle); + return draw_buffer->modelview_stack; +} + +CoglMatrixStack * +_cogl_draw_buffer_get_projection_stack (CoglHandle handle) +{ + CoglDrawBuffer *draw_buffer = COGL_DRAW_BUFFER (handle); + return draw_buffer->projection_stack; +} + +CoglHandle +cogl_offscreen_new_to_texture (CoglHandle texhandle) +{ + CoglOffscreen *offscreen; + int width; + int height; + GLuint tex_gl_handle; + GLenum tex_gl_target; + 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 */ + if (cogl_texture_is_sliced (texhandle)) + return COGL_INVALID_HANDLE; + + /* 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)) + return COGL_INVALID_HANDLE; + + if (tex_gl_target != GL_TEXTURE_2D) + return COGL_INVALID_HANDLE; + + /* 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)); + + /* We are about to generate and bind a new fbo, so when next flushing the + * journal, we will need to rebind the current draw buffer... */ + ctx->dirty_bound_framebuffer = 1; + + /* Generate framebuffer */ + glGenFramebuffers (1, &fbo_gl_handle); + GE (glBindFramebuffer (GL_FRAMEBUFFER, fbo_gl_handle)); + GE (glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + tex_gl_target, tex_gl_handle, 0)); + GE (glFramebufferRenderbuffer (GL_FRAMEBUFFER, + GL_STENCIL_ATTACHMENT, + GL_RENDERBUFFER, gl_stencil_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); + + /* 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; + } + } + + offscreen = g_new0 (CoglOffscreen, 1); + + _cogl_draw_buffer_init (COGL_DRAW_BUFFER (offscreen), + COGL_DRAW_BUFFER_TYPE_OFFSCREEN, + width, + height); + + offscreen->fbo_handle = fbo_gl_handle; + offscreen->gl_stencil_handle = gl_stencil_handle; + + /* XXX: Can we get a away with removing this? It wasn't documented, and most + * users of the API are hopefully setting up the modelview from scratch + * anyway */ +#if 0 + cogl_matrix_translate (&draw_buffer->modelview, -1.0f, -1.0f, 0.0f); + cogl_matrix_scale (&draw_buffer->modelview, + 2.0f / draw_buffer->width, 2.0f / draw_buffer->height, 1.0f); +#endif + + return _cogl_offscreen_handle_new (offscreen); +} + +static void +_cogl_offscreen_free (CoglOffscreen *offscreen) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* Chain up to parent */ + _cogl_draw_buffer_free (COGL_DRAW_BUFFER (offscreen)); + + if (offscreen->gl_stencil_handle) + GE (glDeleteRenderbuffers (1, &offscreen->gl_stencil_handle)); + GE (glDeleteFramebuffers (1, &offscreen->fbo_handle)); + 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 draw buffers, since we can't, e.g. keep track of + * the window size. */ + + onscreen = g_new0 (CoglOnscreen, 1); + _cogl_draw_buffer_init (COGL_DRAW_BUFFER (onscreen), + COGL_DRAW_BUFFER_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_draw_buffer_free (COGL_DRAW_BUFFER (onscreen)); + + g_free (onscreen); +} + +GSList * +_cogl_create_draw_buffer_stack (void) +{ + GSList *stack = NULL; + CoglDrawBufferStackEntry *entry; + + entry = g_slice_new0 (CoglDrawBufferStackEntry); + entry->target = COGL_WINDOW_BUFFER; + entry->draw_buffer = COGL_INVALID_HANDLE; + + return g_slist_prepend (stack, entry); +} + +void +_cogl_free_draw_buffer_stack (GSList *stack) +{ + GSList *l; + + for (l = stack; l != NULL; l = l->next) + { + CoglDrawBufferStackEntry *entry = l->data; + CoglDrawBuffer *draw_buffer = COGL_DRAW_BUFFER (entry->draw_buffer); + if (draw_buffer->type == COGL_DRAW_BUFFER_TYPE_OFFSCREEN) + _cogl_offscreen_free (COGL_OFFSCREEN (draw_buffer)); + else + _cogl_onscreen_free (COGL_ONSCREEN (draw_buffer)); + } + g_slist_free (stack); +} + +/* XXX: The target argument is redundant; when we break API, we should + * remove it! */ +void +cogl_set_draw_buffer (CoglBufferTarget target, CoglHandle handle) +{ + CoglDrawBuffer *draw_buffer = NULL; + CoglDrawBufferStackEntry *entry; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + _cogl_journal_flush (); + + g_assert (ctx->draw_buffer_stack != NULL); + entry = ctx->draw_buffer_stack->data; + + if (target == COGL_WINDOW_BUFFER) + handle = ctx->window_buffer; + else if (!cogl_is_draw_buffer (handle)) + return; + + draw_buffer = COGL_DRAW_BUFFER (handle); + + if (entry->draw_buffer != draw_buffer) + { + entry->target = target; + + ctx->dirty_bound_framebuffer = 1; + ctx->dirty_viewport = 1; + + if (draw_buffer != COGL_INVALID_HANDLE) + cogl_handle_ref (draw_buffer); + if (entry->draw_buffer != COGL_INVALID_HANDLE) + cogl_handle_unref (entry->draw_buffer); + entry->draw_buffer = draw_buffer; + + /* 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 (draw_buffer->modelview_stack); + _cogl_matrix_stack_dirty (draw_buffer->projection_stack); + _cogl_clip_stack_state_dirty (&draw_buffer->clip_state); + } +} + +CoglHandle +_cogl_get_draw_buffer (void) +{ + CoglDrawBufferStackEntry *entry; + + _COGL_GET_CONTEXT (ctx, NULL); + + g_assert (ctx->draw_buffer_stack); + entry = ctx->draw_buffer_stack->data; + + return entry->draw_buffer; +} + +void +cogl_push_draw_buffer (void) +{ + CoglDrawBufferStackEntry *old; + CoglDrawBufferStackEntry *entry; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + entry = g_slice_new0 (CoglDrawBufferStackEntry); + + g_assert (ctx->draw_buffer_stack); + + old = ctx->draw_buffer_stack->data; + *entry = *old; + + cogl_handle_ref (entry->draw_buffer); + + ctx->draw_buffer_stack = + g_slist_prepend (ctx->draw_buffer_stack, entry); +} + +void +cogl_pop_draw_buffer (void) +{ + CoglDrawBufferStackEntry *to_pop; + CoglDrawBufferStackEntry *to_restore; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + g_assert (ctx->draw_buffer_stack != NULL); + + to_pop = ctx->draw_buffer_stack->data; + to_restore = ctx->draw_buffer_stack->next->data; + + cogl_set_draw_buffer (to_restore->target, to_restore->draw_buffer); + + cogl_handle_unref (to_pop->draw_buffer); + ctx->draw_buffer_stack = + g_slist_remove_link (ctx->draw_buffer_stack, + ctx->draw_buffer_stack); + g_slice_free (CoglDrawBufferStackEntry, to_pop); +} + +void +_cogl_draw_buffer_flush_state (CoglHandle handle, + CoglDrawBufferFlushFlags flags) +{ + CoglDrawBuffer *draw_buffer; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + draw_buffer = COGL_DRAW_BUFFER (handle); + + if (cogl_features_available (COGL_FEATURE_OFFSCREEN) && + ctx->dirty_bound_framebuffer) + { + if (draw_buffer->type == COGL_DRAW_BUFFER_TYPE_OFFSCREEN) + { + GE (glBindFramebuffer (GL_FRAMEBUFFER, + COGL_OFFSCREEN (draw_buffer)->fbo_handle)); + } + else + GE (glBindFramebuffer (GL_FRAMEBUFFER, 0)); + ctx->dirty_bound_framebuffer = FALSE; + } + + if (ctx->dirty_viewport) + { + GE (glViewport (draw_buffer->viewport_x, + draw_buffer->viewport_y, + draw_buffer->viewport_width, + draw_buffer->viewport_height)); + ctx->dirty_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 (&draw_buffer->clip_state); + + if (!(flags & COGL_DRAW_BUFFER_FLUSH_SKIP_MODELVIEW)) + _cogl_matrix_stack_flush_to_gl (draw_buffer->modelview_stack, + COGL_MATRIX_MODELVIEW); + + _cogl_matrix_stack_flush_to_gl (draw_buffer->projection_stack, + COGL_MATRIX_PROJECTION); +} + diff --git a/cogl/cogl-journal.c b/cogl/cogl-journal.c index aa0d5892e..a50bf8e42 100644 --- a/cogl/cogl-journal.c +++ b/cogl/cogl-journal.c @@ -32,6 +32,7 @@ #include "cogl-texture-private.h" #include "cogl-material-private.h" #include "cogl-vertex-buffer-private.h" +#include "cogl-draw-buffer-private.h" #include #include @@ -531,6 +532,8 @@ _cogl_journal_flush (void) GLuint journal_vbo; gboolean vbo_fallback = (cogl_get_features () & COGL_FEATURE_VBOS) ? FALSE : TRUE; + CoglHandle draw_buffer; + CoglMatrixStack *modelview_stack; _COGL_GET_CONTEXT (ctx, NO_RETVAL); @@ -547,20 +550,19 @@ _cogl_journal_flush (void) else state.vbo_offset = (char *)ctx->logged_vertices->data; - _cogl_matrix_stack_flush_to_gl (ctx->projection_stack, - COGL_MATRIX_PROJECTION); + draw_buffer = _cogl_get_draw_buffer (); + modelview_stack = _cogl_draw_buffer_get_modelview_stack (draw_buffer); + state.modelview_stack = modelview_stack; - state.modelview_stack = ctx->modelview_stack; - _cogl_matrix_stack_push (ctx->modelview_stack); + _cogl_matrix_stack_push (modelview_stack); /* If we have transformed all our quads at log time then we ensure no * further model transform is applied by loading the identity matrix * here... */ if (G_LIKELY (!(cogl_debug_flags & COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM))) { - _cogl_matrix_stack_load_identity (ctx->modelview_stack); - _cogl_matrix_stack_flush_to_gl (ctx->modelview_stack, - COGL_MATRIX_MODELVIEW); + _cogl_matrix_stack_load_identity (modelview_stack); + _cogl_matrix_stack_flush_to_gl (modelview_stack, COGL_MATRIX_MODELVIEW); } /* batch_and_call() batches a list of journal entries according to some @@ -591,7 +593,7 @@ _cogl_journal_flush (void) _cogl_journal_flush_vbo_offsets_and_entries, /* callback */ &state); /* data */ - _cogl_matrix_stack_pop (ctx->modelview_stack); + _cogl_matrix_stack_pop (modelview_stack); for (i = 0; i < ctx->journal->len; i++) { @@ -607,6 +609,18 @@ _cogl_journal_flush (void) g_array_set_size (ctx->logged_vertices, 0); } +static void +_cogl_journal_init (void) +{ + /* Here we flush anything that we know must remain constant until the + * next the the journal is flushed. Note: This lets up flush things + * that themselves depend on the journal, such as clip state. */ + + /* NB: the journal deals with flushing the modelview stack manually */ + _cogl_draw_buffer_flush_state (_cogl_get_draw_buffer (), + COGL_DRAW_BUFFER_FLUSH_SKIP_MODELVIEW); +} + void _cogl_journal_log_quad (float x_1, float y_1, @@ -632,6 +646,9 @@ _cogl_journal_log_quad (float x_1, _COGL_GET_CONTEXT (ctx, NO_RETVAL); + if (ctx->logged_vertices->len == 0) + _cogl_journal_init (); + /* The vertex data is logged into a separate array in a layout that can be * directly passed to OpenGL */ diff --git a/cogl/cogl-primitives.c b/cogl/cogl-primitives.c index 016833ad3..45ec90ce6 100644 --- a/cogl/cogl-primitives.c +++ b/cogl/cogl-primitives.c @@ -32,6 +32,7 @@ #include "cogl-texture-private.h" #include "cogl-material-private.h" #include "cogl-vertex-buffer-private.h" +#include "cogl-draw-buffer-private.h" #include #include @@ -396,8 +397,6 @@ _cogl_rectangles_with_multitexture_coords ( _COGL_GET_CONTEXT (ctx, NO_RETVAL); - cogl_clip_ensure (); - material = ctx->source_material; layers = cogl_material_get_layers (material); @@ -706,7 +705,6 @@ draw_polygon_sub_texture_cb (CoglHandle tex_handle, options.layer0_override_texture = gl_handle; _cogl_material_flush_gl_state (ctx->source_material, &options); - _cogl_flush_matrix_stacks (); GE (glDrawArrays (GL_TRIANGLE_FAN, 0, state->n_vertices)); } @@ -840,7 +838,6 @@ _cogl_multitexture_polygon_single_primitive (CoglTextureVertex *vertices, options.flags |= COGL_MATERIAL_FLUSH_SKIP_GL_COLOR; options.fallback_layers = fallback_layers; _cogl_material_flush_gl_state (ctx->source_material, &options); - _cogl_flush_matrix_stacks (); GE (glDrawArrays (GL_TRIANGLE_FAN, 0, n_vertices)); } @@ -866,7 +863,11 @@ cogl_polygon (CoglTextureVertex *vertices, _COGL_GET_CONTEXT (ctx, NO_RETVAL); _cogl_journal_flush (); - cogl_clip_ensure (); + + /* NB: _cogl_draw_buffer_flush_state may disrupt various state (such + * as the material state) when flushing the clip stack, so should + * always be done first when preparing to draw. */ + _cogl_draw_buffer_flush_state (_cogl_get_draw_buffer (), 0); material = ctx->source_material; layers = cogl_material_get_layers (ctx->source_material); @@ -1030,9 +1031,6 @@ cogl_path_fill_preserve (void) { _COGL_GET_CONTEXT (ctx, NO_RETVAL); - _cogl_journal_flush (); - cogl_clip_ensure (); - if (ctx->path_nodes->len == 0) return; @@ -1055,10 +1053,7 @@ cogl_path_stroke_preserve (void) if (ctx->path_nodes->len == 0) return; - _cogl_journal_flush (); - cogl_clip_ensure (); - - _cogl_path_stroke_nodes(); + _cogl_path_stroke_nodes (); } void diff --git a/cogl/cogl-texture.c b/cogl/cogl-texture.c index fa33f047d..763edbc2e 100644 --- a/cogl/cogl-texture.c +++ b/cogl/cogl-texture.c @@ -42,6 +42,7 @@ #include "cogl-context.h" #include "cogl-handle.h" #include "cogl-primitives.h" +#include "cogl-draw-buffer-private.h" #include #include @@ -667,7 +668,8 @@ _cogl_texture_draw_and_read (CoglTexture *tex, GLuint target_gl_type) { gint bpp; - GLint viewport[4]; + CoglHandle draw_buffer; + int viewport[4]; CoglBitmap alpha_bmp; CoglHandle prev_source; CoglMatrixStack *projection_stack; @@ -677,8 +679,9 @@ _cogl_texture_draw_and_read (CoglTexture *tex, bpp = _cogl_get_format_bpp (COGL_PIXEL_FORMAT_RGBA_8888); + draw_buffer = _cogl_get_draw_buffer (); /* Viewport needs to have some size and be inside the window for this */ - GE( glGetIntegerv (GL_VIEWPORT, viewport)); + _cogl_draw_buffer_get_viewport4fv (draw_buffer, viewport); if (viewport[0] < 0 || viewport[1] < 0 || viewport[2] <= 0 || viewport[3] <= 0) return FALSE; @@ -688,16 +691,18 @@ _cogl_texture_draw_and_read (CoglTexture *tex, * works) */ - _cogl_matrix_stack_push (ctx->projection_stack); - _cogl_matrix_stack_load_identity (ctx->projection_stack); - _cogl_matrix_stack_ortho (ctx->projection_stack, + projection_stack = _cogl_draw_buffer_get_projection_stack (draw_buffer); + _cogl_matrix_stack_push (projection_stack); + _cogl_matrix_stack_load_identity (projection_stack); + _cogl_matrix_stack_ortho (projection_stack, 0, (float)(viewport[2]), 0, (float)(viewport[3]), (float)(0), (float)(100)); - _cogl_matrix_stack_push (ctx->modelview_stack); - _cogl_matrix_stack_load_identity (ctx->modelview_stack); + modelview_stack = _cogl_draw_buffer_get_modelview_stack (draw_buffer); + _cogl_matrix_stack_push (modelview_stack); + _cogl_matrix_stack_load_identity (modelview_stack); /* Direct copy operation */ diff --git a/cogl/cogl-vertex-buffer.c b/cogl/cogl-vertex-buffer.c index c1aa172f4..357d1e6e5 100644 --- a/cogl/cogl-vertex-buffer.c +++ b/cogl/cogl-vertex-buffer.c @@ -138,6 +138,7 @@ #include "cogl-texture-private.h" #include "cogl-material-private.h" #include "cogl-primitives.h" +#include "cogl-draw-buffer-private.h" #define PAD_FOR_ALIGNMENT(VAR, TYPE_SIZE) \ (VAR = TYPE_SIZE + ((VAR - 1) & ~(TYPE_SIZE - 1))) @@ -1664,6 +1665,11 @@ enable_state_for_drawing_buffer (CoglVertexBuffer *buffer) } ctx->n_texcoord_arrays_enabled = max_texcoord_attrib_unit + 1; + /* NB: _cogl_draw_buffer_flush_state may disrupt various state (such + * as the material state) when flushing the clip stack, so should + * always be done first when preparing to draw. */ + _cogl_draw_buffer_flush_state (_cogl_get_draw_buffer (), 0); + options.flags = COGL_MATERIAL_FLUSH_FALLBACK_MASK | COGL_MATERIAL_FLUSH_DISABLE_MASK; @@ -1753,15 +1759,11 @@ cogl_vertex_buffer_draw (CoglHandle handle, return; _cogl_journal_flush (); - cogl_clip_ensure (); buffer = _cogl_vertex_buffer_pointer_from_handle (handle); - cogl_clip_ensure (); - _cogl_flush_matrix_stacks (); enable_state_for_drawing_buffer (buffer); - /* FIXME: flush cogl cache */ GE (glDrawArrays (mode, first, count)); disable_state_for_drawing_buffer (buffer); @@ -1885,7 +1887,6 @@ cogl_vertex_buffer_draw_elements (CoglHandle handle, return; _cogl_journal_flush (); - cogl_clip_ensure (); buffer = _cogl_vertex_buffer_pointer_from_handle (handle); @@ -1894,8 +1895,6 @@ cogl_vertex_buffer_draw_elements (CoglHandle handle, indices = _cogl_vertex_buffer_indices_pointer_from_handle (indices_handle); - cogl_clip_ensure (); - _cogl_flush_matrix_stacks (); enable_state_for_drawing_buffer (buffer); byte_offset = indices_offset * get_indices_type_size (indices->type); @@ -1905,7 +1904,6 @@ cogl_vertex_buffer_draw_elements (CoglHandle handle, GE (glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, GPOINTER_TO_UINT (indices->vbo_name))); - /* FIXME: flush cogl cache */ GE (glDrawRangeElements (mode, min_index, max_index, count, indices->type, (void *)byte_offset)); diff --git a/cogl/cogl.c b/cogl/cogl.c index 4fb1c7e67..8fdb36ff8 100644 --- a/cogl/cogl.c +++ b/cogl/cogl.c @@ -38,6 +38,7 @@ #include "cogl-context.h" #include "cogl-material-private.h" #include "cogl-winsys.h" +#include "cogl-draw-buffer-private.h" #if defined (HAVE_COGL_GLES2) || defined (HAVE_COGL_GLES) #include "cogl-gles2-wrapper.h" @@ -116,7 +117,12 @@ cogl_clear (const CoglColor *color, gulong buffers) COGL_NOTE (DRAW, "Clear begin"); - cogl_clip_ensure (); + _cogl_journal_flush (); + + /* NB: _cogl_draw_buffer_flush_state may disrupt various state (such + * as the material state) when flushing the clip stack, so should + * always be done first when preparing to draw. */ + _cogl_draw_buffer_flush_state (_cogl_get_draw_buffer (), 0); if (buffers & COGL_BUFFER_BIT_COLOR) { @@ -327,6 +333,10 @@ set_clip_plane (GLint plane_num, #endif GLfloat angle; CoglMatrix inverse_projection; + CoglHandle draw_buffer = _cogl_get_draw_buffer (); + CoglMatrixStack *modelview_stack = + _cogl_draw_buffer_get_modelview_stack (draw_buffer); + _COGL_GET_CONTEXT (ctx, NO_RETVAL); /* Calculate the angle between the axes and the line crossing the @@ -334,24 +344,25 @@ set_clip_plane (GLint plane_num, angle = atan2f (vertex_b[1] - vertex_a[1], vertex_b[0] - vertex_a[0]) * (180.0/G_PI); - _cogl_matrix_stack_push (ctx->modelview_stack); + _cogl_matrix_stack_push (modelview_stack); + /* Load the identity matrix and multiply by the reverse of the projection matrix so we can specify the plane in screen coordinates */ - _cogl_matrix_stack_load_identity (ctx->modelview_stack); + _cogl_matrix_stack_load_identity (modelview_stack); cogl_matrix_init_from_array (&inverse_projection, ctx->inverse_projection); - _cogl_matrix_stack_multiply (ctx->modelview_stack, &inverse_projection); + _cogl_matrix_stack_multiply (modelview_stack, &inverse_projection); /* Rotate about point a */ - _cogl_matrix_stack_translate (ctx->modelview_stack, + _cogl_matrix_stack_translate (modelview_stack, vertex_a[0], vertex_a[1], vertex_a[2]); /* Rotate the plane by the calculated angle so that it will connect the two points */ - _cogl_matrix_stack_rotate (ctx->modelview_stack, angle, 0.0f, 0.0f, 1.0f); - _cogl_matrix_stack_translate (ctx->modelview_stack, + _cogl_matrix_stack_rotate (modelview_stack, angle, 0.0f, 0.0f, 1.0f); + _cogl_matrix_stack_translate (modelview_stack, -vertex_a[0], -vertex_a[1], -vertex_a[2]); - _cogl_flush_matrix_stacks (); + _cogl_matrix_stack_flush_to_gl (modelview_stack, COGL_MATRIX_MODELVIEW); plane[0] = 0; plane[1] = -1.0; @@ -363,7 +374,7 @@ set_clip_plane (GLint plane_num, GE( glClipPlane (plane_num, plane) ); #endif - _cogl_matrix_stack_pop (ctx->modelview_stack); + _cogl_matrix_stack_pop (modelview_stack); } void @@ -372,7 +383,12 @@ _cogl_set_clip_planes (float x_offset, float width, float height) { + CoglHandle draw_buffer = _cogl_get_draw_buffer (); + CoglMatrixStack *modelview_stack = + _cogl_draw_buffer_get_modelview_stack (draw_buffer); CoglMatrix modelview_matrix; + CoglMatrixStack *projection_stack = + _cogl_draw_buffer_get_projection_stack (draw_buffer); CoglMatrix projection_matrix; float vertex_tl[4] = { x_offset, y_offset, 0, 1.0 }; @@ -381,10 +397,8 @@ _cogl_set_clip_planes (float x_offset, float vertex_br[4] = { x_offset + width, y_offset + height, 0, 1.0 }; - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - - _cogl_matrix_stack_get (ctx->projection_stack, &projection_matrix); - _cogl_matrix_stack_get (ctx->modelview_stack, &modelview_matrix); + _cogl_matrix_stack_get (projection_stack, &projection_matrix); + _cogl_matrix_stack_get (modelview_stack, &modelview_matrix); project_vertex (&modelview_matrix, &projection_matrix, vertex_tl); project_vertex (&modelview_matrix, &projection_matrix, vertex_tr); @@ -422,6 +436,7 @@ _cogl_add_stencil_clip (float x_offset, gboolean first) { CoglHandle current_source; + CoglHandle draw_buffer = _cogl_get_draw_buffer (); _COGL_GET_CONTEXT (ctx, NO_RETVAL); @@ -429,6 +444,8 @@ _cogl_add_stencil_clip (float x_offset, * batched geometry before we start... */ _cogl_journal_flush (); + _cogl_draw_buffer_flush_state (draw_buffer, 0); + /* temporarily swap in our special stenciling material */ current_source = cogl_handle_ref (ctx->source_material); cogl_set_source (ctx->stencil_material); @@ -450,6 +467,11 @@ _cogl_add_stencil_clip (float x_offset, } else { + CoglMatrixStack *modelview_stack = + _cogl_draw_buffer_get_modelview_stack (draw_buffer); + CoglMatrixStack *projection_stack = + _cogl_draw_buffer_get_projection_stack (draw_buffer); + /* Add one to every pixel of the stencil buffer in the rectangle */ GE( glStencilFunc (GL_NEVER, 0x1, 0x3) ); @@ -466,16 +488,16 @@ _cogl_add_stencil_clip (float x_offset, rectangle are set will be valid */ GE( glStencilOp (GL_DECR, GL_DECR, GL_DECR) ); - _cogl_matrix_stack_push (ctx->projection_stack); - _cogl_matrix_stack_load_identity (ctx->projection_stack); + _cogl_matrix_stack_push (projection_stack); + _cogl_matrix_stack_load_identity (projection_stack); - _cogl_matrix_stack_push (ctx->modelview_stack); - _cogl_matrix_stack_load_identity (ctx->modelview_stack); + _cogl_matrix_stack_push (modelview_stack); + _cogl_matrix_stack_load_identity (modelview_stack); cogl_rectangle (-1.0, -1.0, 1.0, 1.0); - _cogl_matrix_stack_pop (ctx->modelview_stack); - _cogl_matrix_stack_pop (ctx->projection_stack); + _cogl_matrix_stack_pop (modelview_stack); + _cogl_matrix_stack_pop (projection_stack); } /* make sure our rectangles hit the stencil buffer before we restore @@ -515,19 +537,32 @@ _cogl_disable_clip_planes (void) GE( glDisable (GL_CLIP_PLANE0) ); } -/* XXX: This should be deprecated and Cogl should be left to manage - * the glViewport automatically when switching draw buffers. */ +void +_cogl_set_viewport (int x, + int y, + int width, + int height) +{ + CoglHandle draw_buffer; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + draw_buffer = _cogl_get_draw_buffer (); + + _cogl_draw_buffer_set_viewport (draw_buffer, + x, + y, + width, + height); +} + +/* XXX: This should be deprecated, and we should expose a way to also + * specify an x and y viewport offset */ void cogl_viewport (guint width, guint height) { - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - - COGL_NOTE (MISC, "glViewport(0, 0, %u, %u)", width, height); - GE( glViewport (0, 0, width, height) ); - - ctx->viewport_width = width; - ctx->viewport_height = height; + _cogl_set_viewport (0, 0, width, height); } void @@ -540,6 +575,7 @@ _cogl_setup_viewport (guint width, { float z_camera; CoglMatrix projection_matrix; + CoglMatrixStack *modelview_stack; _COGL_GET_CONTEXT (ctx, NO_RETVAL); @@ -593,11 +629,13 @@ _cogl_setup_viewport (guint width, cogl_get_projection_matrix (&projection_matrix); z_camera = 0.5 * projection_matrix.xx; - _cogl_matrix_stack_load_identity (ctx->modelview_stack); - _cogl_matrix_stack_translate (ctx->modelview_stack, -0.5f, -0.5f, -z_camera); - _cogl_matrix_stack_scale (ctx->modelview_stack, + modelview_stack = + _cogl_draw_buffer_get_modelview_stack (_cogl_get_draw_buffer ()); + _cogl_matrix_stack_load_identity (modelview_stack); + _cogl_matrix_stack_translate (modelview_stack, -0.5f, -0.5f, -z_camera); + _cogl_matrix_stack_scale (modelview_stack, 1.0f / width, -1.0f / height, 1.0f / width); - _cogl_matrix_stack_translate (ctx->modelview_stack, + _cogl_matrix_stack_translate (modelview_stack, 0.0f, -1.0 * height, 0.0f); } @@ -606,9 +644,6 @@ cogl_get_features (void) { _COGL_GET_CONTEXT (ctx, 0); - if (!ctx->features_cached) - _cogl_features_init (); - if (cogl_debug_flags & COGL_DEBUG_DISABLE_VBOS) ctx->feature_flags &= ~COGL_FEATURE_VBOS; @@ -626,18 +661,24 @@ cogl_features_available (CoglFeatureFlags features) return (ctx->feature_flags & features) == features; } -/* XXX: This function should be deprecated, and replaced with a - * cogl_draw_buffer_get_size() API instead. We don't support offset - * viewports, and you can't have floating point viewport sizes. */ +/* XXX: This function should either be replaced with one returning + * integers, or removed/deprecated and make the + * _cogl_draw_buffer_get_viewport* functions public. + */ void cogl_get_viewport (float v[4]) { + CoglHandle draw_buffer; + int viewport[4]; + int i; + _COGL_GET_CONTEXT (ctx, NO_RETVAL); - v[0] = 0; - v[1] = 0; - v[2] = ctx->viewport_width; - v[3] = ctx->viewport_height; + draw_buffer = _cogl_get_draw_buffer (); + _cogl_draw_buffer_get_viewport4fv (draw_buffer, viewport); + + for (i = 0; i < 4; i++) + v[i] = viewport[i]; } void @@ -733,7 +774,7 @@ cogl_disable_fog (void) void cogl_flush_gl_state (int flags) { - _cogl_flush_matrix_stacks (); + _cogl_draw_buffer_flush_state (_cogl_get_draw_buffer (), 0); } #endif @@ -752,18 +793,20 @@ cogl_read_pixels (int x, CoglPixelFormat format, guint8 *pixels) { - GLint viewport[4]; - GLint viewport_height; - int rowstride = width * 4; - guint8 *temprow; + int viewport_height; + int rowstride = width * 4; + guint8 *temprow; + CoglHandle draw_buffer; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); g_return_if_fail (format == COGL_PIXEL_FORMAT_RGBA_8888); g_return_if_fail (source == COGL_READ_PIXELS_COLOR_BUFFER); temprow = g_alloca (rowstride * sizeof (guint8)); - glGetIntegerv (GL_VIEWPORT, viewport); - viewport_height = viewport[3]; + draw_buffer = _cogl_get_draw_buffer (); + viewport_height = _cogl_draw_buffer_get_viewport_height (draw_buffer); /* The y co-ordinate should be given in OpenGL's coordinate system so 0 is the bottom row */ @@ -825,12 +868,13 @@ cogl_begin_gl (void) /* Flush all batched primitives */ cogl_flush (); - /* Flush our clipping state to GL */ - cogl_clip_ensure (); - - /* Flush any client side matrix state */ - _cogl_flush_matrix_stacks (); - + /* Flush framebuffer state, including clip state, modelview and + * projection matrix state + * + * NB: _cogl_draw_buffer_flush_state may disrupt various state (such + * as the material state) when flushing the clip stack, so should + * always be done first when preparing to draw. */ + _cogl_draw_buffer_flush_state (_cogl_get_draw_buffer (), 0); /* Setup the state for the current material */ @@ -940,36 +984,41 @@ _cogl_destroy_texture_units (void) void cogl_push_matrix (void) { - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - _cogl_matrix_stack_push (ctx->modelview_stack); + CoglMatrixStack *modelview_stack = + _cogl_draw_buffer_get_modelview_stack (_cogl_get_draw_buffer ()); + _cogl_matrix_stack_push (modelview_stack); } void cogl_pop_matrix (void) { - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - _cogl_matrix_stack_pop (ctx->modelview_stack); + CoglMatrixStack *modelview_stack = + _cogl_draw_buffer_get_modelview_stack (_cogl_get_draw_buffer ()); + _cogl_matrix_stack_pop (modelview_stack); } void cogl_scale (float x, float y, float z) { - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - _cogl_matrix_stack_scale (ctx->modelview_stack, x, y, z); + CoglMatrixStack *modelview_stack = + _cogl_draw_buffer_get_modelview_stack (_cogl_get_draw_buffer ()); + _cogl_matrix_stack_scale (modelview_stack, x, y, z); } void cogl_translate (float x, float y, float z) { - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - _cogl_matrix_stack_translate (ctx->modelview_stack, x, y, z); + CoglMatrixStack *modelview_stack = + _cogl_draw_buffer_get_modelview_stack (_cogl_get_draw_buffer ()); + _cogl_matrix_stack_translate (modelview_stack, x, y, z); } void cogl_rotate (float angle, float x, float y, float z) { - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - _cogl_matrix_stack_rotate (ctx->modelview_stack, angle, x, y, z); + CoglMatrixStack *modelview_stack = + _cogl_draw_buffer_get_modelview_stack (_cogl_get_draw_buffer ()); + _cogl_matrix_stack_rotate (modelview_stack, angle, x, y, z); } void @@ -997,12 +1046,14 @@ cogl_frustum (float left, float z_far) { float c, d; + CoglMatrixStack *projection_stack = + _cogl_draw_buffer_get_projection_stack (_cogl_get_draw_buffer ()); _COGL_GET_CONTEXT (ctx, NO_RETVAL); - _cogl_matrix_stack_load_identity (ctx->projection_stack); + _cogl_matrix_stack_load_identity (projection_stack); - _cogl_matrix_stack_frustum (ctx->projection_stack, + _cogl_matrix_stack_frustum (projection_stack, left, right, bottom, @@ -1036,12 +1087,14 @@ cogl_ortho (float left, float z_far) { CoglMatrix ortho; + CoglMatrixStack *projection_stack = + _cogl_draw_buffer_get_projection_stack (_cogl_get_draw_buffer ()); _COGL_GET_CONTEXT (ctx, NO_RETVAL); cogl_matrix_init_identity (&ortho); cogl_matrix_ortho (&ortho, left, right, bottom, top, z_near, z_far); - _cogl_matrix_stack_set (ctx->projection_stack, &ortho); + _cogl_matrix_stack_set (projection_stack, &ortho); /* Calculate and store the inverse of the matrix */ memset (ctx->inverse_projection, 0, sizeof (float) * 16); @@ -1060,41 +1113,44 @@ cogl_ortho (float left, void cogl_get_modelview_matrix (CoglMatrix *matrix) { - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - _cogl_matrix_stack_get (ctx->modelview_stack, matrix); + CoglMatrixStack *modelview_stack = + _cogl_draw_buffer_get_modelview_stack (_cogl_get_draw_buffer ()); + _cogl_matrix_stack_get (modelview_stack, matrix); } void cogl_set_modelview_matrix (CoglMatrix *matrix) { - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - _cogl_matrix_stack_set (ctx->modelview_stack, matrix); + CoglMatrixStack *modelview_stack = + _cogl_draw_buffer_get_modelview_stack (_cogl_get_draw_buffer ()); + _cogl_matrix_stack_set (modelview_stack, matrix); } void cogl_get_projection_matrix (CoglMatrix *matrix) { - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - _cogl_matrix_stack_get (ctx->projection_stack, matrix); + CoglMatrixStack *projection_stack = + _cogl_draw_buffer_get_projection_stack (_cogl_get_draw_buffer ()); + _cogl_matrix_stack_get (projection_stack, matrix); } void cogl_set_projection_matrix (CoglMatrix *matrix) { - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - _cogl_matrix_stack_set (ctx->projection_stack, matrix); + CoglMatrixStack *projection_stack = + _cogl_draw_buffer_get_projection_stack (_cogl_get_draw_buffer ()); + _cogl_matrix_stack_set (projection_stack, matrix); /* FIXME: Update the inverse projection matrix!! Presumably use * of clip planes must currently be broken if this API is used. */ } -void -_cogl_flush_matrix_stacks (void) +CoglClipStackState * +_cogl_get_clip_state (void) { - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - _cogl_matrix_stack_flush_to_gl (ctx->projection_stack, - COGL_MATRIX_PROJECTION); - _cogl_matrix_stack_flush_to_gl (ctx->modelview_stack, - COGL_MATRIX_MODELVIEW); + CoglHandle draw_buffer; + + draw_buffer = _cogl_get_draw_buffer (); + return _cogl_draw_buffer_get_clip_state (draw_buffer); } diff --git a/cogl/cogl.h.in b/cogl/cogl.h.in index 524aec71d..23e417a29 100644 --- a/cogl/cogl.h.in +++ b/cogl/cogl.h.in @@ -867,6 +867,7 @@ void cogl_flush_gl_state (int flags); /* private */ void _cogl_set_indirect_context (gboolean indirect); +void _cogl_set_viewport (int x, int y, int width, int height); G_END_DECLS diff --git a/cogl/driver/gl/Makefile.am b/cogl/driver/gl/Makefile.am index 81a21c90e..afe961780 100644 --- a/cogl/driver/gl/Makefile.am +++ b/cogl/driver/gl/Makefile.am @@ -25,8 +25,6 @@ libclutter_cogl_driver_la_SOURCES = \ cogl.c \ cogl-primitives.c \ cogl-texture-driver.c \ - cogl-fbo.h \ - cogl-fbo.c \ cogl-shader-private.h \ cogl-shader.c \ cogl-program.h \ diff --git a/cogl/driver/gl/cogl-context-driver.c b/cogl/driver/gl/cogl-context-driver.c index dcf65ef26..c27721fd0 100644 --- a/cogl/driver/gl/cogl-context-driver.c +++ b/cogl/driver/gl/cogl-context-driver.c @@ -30,15 +30,16 @@ void _cogl_create_context_driver (CoglContext *_context) { - _context->drv.pf_glGenRenderbuffersEXT = NULL; - _context->drv.pf_glBindRenderbufferEXT = NULL; - _context->drv.pf_glRenderbufferStorageEXT = NULL; - _context->drv.pf_glGenFramebuffersEXT = NULL; - _context->drv.pf_glBindFramebufferEXT = NULL; - _context->drv.pf_glFramebufferTexture2DEXT = NULL; - _context->drv.pf_glFramebufferRenderbufferEXT = NULL; - _context->drv.pf_glCheckFramebufferStatusEXT = NULL; - _context->drv.pf_glDeleteFramebuffersEXT = NULL; + _context->drv.pf_glGenRenderbuffers = NULL; + _context->drv.pf_glBindRenderbuffer = NULL; + _context->drv.pf_glRenderbufferStorage = NULL; + _context->drv.pf_glGenFramebuffers = NULL; + _context->drv.pf_glBindFramebuffer = NULL; + _context->drv.pf_glFramebufferTexture2D = NULL; + _context->drv.pf_glFramebufferRenderbuffer = NULL; + _context->drv.pf_glCheckFramebufferStatus = NULL; + _context->drv.pf_glDeleteFramebuffers = NULL; + _context->drv.pf_glBlitFramebufferEXT = NULL; _context->drv.pf_glRenderbufferStorageMultisampleEXT = NULL; diff --git a/cogl/driver/gl/cogl-context-driver.h b/cogl/driver/gl/cogl-context-driver.h index ac5e61621..ff266d663 100644 --- a/cogl/driver/gl/cogl-context-driver.h +++ b/cogl/driver/gl/cogl-context-driver.h @@ -29,19 +29,20 @@ typedef struct _CoglContextDriver { /* Relying on glext.h to define these */ - COGL_PFNGLGENRENDERBUFFERSEXTPROC pf_glGenRenderbuffersEXT; - COGL_PFNGLDELETERENDERBUFFERSEXTPROC pf_glDeleteRenderbuffersEXT; - COGL_PFNGLBINDRENDERBUFFEREXTPROC pf_glBindRenderbufferEXT; - COGL_PFNGLRENDERBUFFERSTORAGEEXTPROC pf_glRenderbufferStorageEXT; - COGL_PFNGLGENFRAMEBUFFERSEXTPROC pf_glGenFramebuffersEXT; - COGL_PFNGLBINDFRAMEBUFFEREXTPROC pf_glBindFramebufferEXT; - COGL_PFNGLFRAMEBUFFERTEXTURE2DEXTPROC pf_glFramebufferTexture2DEXT; - COGL_PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC pf_glFramebufferRenderbufferEXT; - COGL_PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC pf_glCheckFramebufferStatusEXT; - COGL_PFNGLDELETEFRAMEBUFFERSEXTPROC pf_glDeleteFramebuffersEXT; + COGL_PFNGLGENRENDERBUFFERSPROC pf_glGenRenderbuffers; + COGL_PFNGLDELETERENDERBUFFERSPROC pf_glDeleteRenderbuffers; + COGL_PFNGLBINDRENDERBUFFERPROC pf_glBindRenderbuffer; + COGL_PFNGLRENDERBUFFERSTORAGEPROC pf_glRenderbufferStorage; + COGL_PFNGLGENFRAMEBUFFERSPROC pf_glGenFramebuffers; + COGL_PFNGLBINDFRAMEBUFFERPROC pf_glBindFramebuffer; + COGL_PFNGLFRAMEBUFFERTEXTURE2DPROC pf_glFramebufferTexture2D; + COGL_PFNGLFRAMEBUFFERRENDERBUFFERPROC pf_glFramebufferRenderbuffer; + COGL_PFNGLCHECKFRAMEBUFFERSTATUSPROC pf_glCheckFramebufferStatus; + COGL_PFNGLDELETEFRAMEBUFFERSPROC pf_glDeleteFramebuffers; + COGL_PFNGLGENERATEMIPMAPPROC pf_glGenerateMipmap; + COGL_PFNGLBLITFRAMEBUFFEREXTPROC pf_glBlitFramebufferEXT; COGL_PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC pf_glRenderbufferStorageMultisampleEXT; - COGL_PFNGLGENERATEMIPMAPEXTPROC pf_glGenerateMipmapEXT; COGL_PFNGLCREATEPROGRAMOBJECTARBPROC pf_glCreateProgramObjectARB; COGL_PFNGLCREATESHADEROBJECTARBPROC pf_glCreateShaderObjectARB; diff --git a/cogl/driver/gl/cogl-defines.h.in b/cogl/driver/gl/cogl-defines.h.in index 1c79bedfa..9d04be9af 100644 --- a/cogl/driver/gl/cogl-defines.h.in +++ b/cogl/driver/gl/cogl-defines.h.in @@ -698,34 +698,34 @@ G_BEGIN_DECLS #endif typedef void - (APIENTRYP COGL_PFNGLGENRENDERBUFFERSEXTPROC) + (APIENTRYP COGL_PFNGLGENRENDERBUFFERSPROC) (GLsizei n, GLuint *renderbuffers); typedef void - (APIENTRYP COGL_PFNGLBINDRENDERBUFFEREXTPROC) + (APIENTRYP COGL_PFNGLBINDRENDERBUFFERPROC) (GLenum target, GLuint renderbuffer); typedef void - (APIENTRYP COGL_PFNGLRENDERBUFFERSTORAGEEXTPROC) + (APIENTRYP COGL_PFNGLRENDERBUFFERSTORAGEPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); typedef void - (APIENTRYP COGL_PFNGLGENFRAMEBUFFERSEXTPROC) + (APIENTRYP COGL_PFNGLGENFRAMEBUFFERSPROC) (GLsizei n, GLuint *framebuffers); typedef void - (APIENTRYP COGL_PFNGLBINDFRAMEBUFFEREXTPROC) + (APIENTRYP COGL_PFNGLBINDFRAMEBUFFERPROC) (GLenum target, GLuint framebuffer); typedef void - (APIENTRYP COGL_PFNGLFRAMEBUFFERTEXTURE2DEXTPROC) + (APIENTRYP COGL_PFNGLFRAMEBUFFERTEXTURE2DPROC) (GLenum target, GLenum attachment, GLenum textarget, @@ -733,26 +733,30 @@ typedef void GLint level); typedef void - (APIENTRYP COGL_PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC) + (APIENTRYP COGL_PFNGLFRAMEBUFFERRENDERBUFFERPROC) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); typedef GLenum - (APIENTRYP COGL_PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC) + (APIENTRYP COGL_PFNGLCHECKFRAMEBUFFERSTATUSPROC) (GLenum target); typedef void - (APIENTRYP COGL_PFNGLDELETEFRAMEBUFFERSEXTPROC) + (APIENTRYP COGL_PFNGLDELETEFRAMEBUFFERSPROC) (GLsizei n, const GLuint *framebuffers); typedef void - (APIENTRYP COGL_PFNGLDELETERENDERBUFFERSEXTPROC) + (APIENTRYP COGL_PFNGLDELETERENDERBUFFERSPROC) (GLsizei n, const GLuint *renderbuffers); +typedef void + (APIENTRYP COGL_PFNGLGENERATEMIPMAPPROC) + (GLenum target); + typedef void (APIENTRYP COGL_PFNGLBLITFRAMEBUFFEREXTPROC) (GLint srcX0, @@ -774,10 +778,6 @@ typedef void GLsizei width, GLsizei height); -typedef void - (APIENTRYP COGL_PFNGLGENERATEMIPMAPEXTPROC) - (GLenum target); - typedef GLhandleARB (APIENTRYP COGL_PFNGLCREATEPROGRAMOBJECTARBPROC) (void); diff --git a/cogl/driver/gl/cogl-fbo.c b/cogl/driver/gl/cogl-fbo.c deleted file mode 100644 index 99315e6b0..000000000 --- a/cogl/driver/gl/cogl-fbo.c +++ /dev/null @@ -1,315 +0,0 @@ -/* - * 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-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->drv.pf_glGenRenderbuffersEXT -#define glDeleteRenderbuffersEXT ctx->drv.pf_glDeleteRenderbuffersEXT -#define glBindRenderbufferEXT ctx->drv.pf_glBindRenderbufferEXT -#define glRenderbufferStorageEXT ctx->drv.pf_glRenderbufferStorageEXT -#define glGenFramebuffersEXT ctx->drv.pf_glGenFramebuffersEXT -#define glBindFramebufferEXT ctx->drv.pf_glBindFramebufferEXT -#define glFramebufferTexture2DEXT ctx->drv.pf_glFramebufferTexture2DEXT -#define glFramebufferRenderbufferEXT ctx->drv.pf_glFramebufferRenderbufferEXT -#define glCheckFramebufferStatusEXT ctx->drv.pf_glCheckFramebufferStatusEXT -#define glDeleteFramebuffersEXT ctx->drv.pf_glDeleteFramebuffersEXT -#define glBlitFramebufferEXT ctx->drv.pf_glBlitFramebufferEXT -#define glRenderbufferStorageMultisampleEXT ctx->drv.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); - -CoglHandle -cogl_offscreen_new_to_texture (CoglHandle texhandle) -{ - CoglFbo *fbo; - int width; - int height; - GLuint tex_gl_handle; - GLenum tex_gl_target; - 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 */ - if (cogl_texture_is_sliced (texhandle)) - return COGL_INVALID_HANDLE; - - /* 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)) - return COGL_INVALID_HANDLE; - - if (tex_gl_target != GL_TEXTURE_2D) - return COGL_INVALID_HANDLE; - - /* 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) ); - - /* 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); - - /* 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->width = width; - fbo->height = height; - fbo->gl_handle = fbo_gl_handle; - fbo->gl_stencil_handle = gl_stencil_handle; - - return _cogl_offscreen_handle_new (fbo); -} - -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_set_draw_buffer (CoglBufferTarget target, CoglHandle offscreen) -{ - CoglFbo *fbo = NULL; - CoglDrawBufferState *draw_buffer; - - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - - _cogl_journal_flush (); - - 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 */ - if (!cogl_is_offscreen (offscreen)) - return; - - fbo = _cogl_offscreen_pointer_from_handle (offscreen); - - /* Check current draw buffer target */ - if (draw_buffer->target != COGL_OFFSCREEN_BUFFER) - { - /* Push the viewport and matrix setup if redirecting - from a non-screen buffer */ - GE( glPushAttrib (GL_VIEWPORT_BIT) ); - - _cogl_matrix_stack_push (ctx->projection_stack); - _cogl_matrix_stack_load_identity (ctx->projection_stack); - - _cogl_matrix_stack_push (ctx->modelview_stack); - _cogl_matrix_stack_load_identity (ctx->modelview_stack); - } - else - { - /* Override viewport and matrix setup if redirecting - from another offscreen buffer */ - _cogl_matrix_stack_load_identity (ctx->projection_stack); - - _cogl_matrix_stack_load_identity (ctx->modelview_stack); - } - - /* Setup new viewport and matrices */ - cogl_viewport (fbo->width, fbo->height); - _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); - - /* Bind offscreen framebuffer object */ - GE( glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, fbo->gl_handle) ); - GE( glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE) ); - } - else if (target & COGL_WINDOW_BUFFER) - { - /* Check current draw buffer target */ - if (draw_buffer->target == COGL_OFFSCREEN_BUFFER) - { - /* Pop viewport and matrices if redirecting back - from an offscreen buffer */ - GE( glPopAttrib () ); - - _cogl_matrix_stack_pop (ctx->projection_stack); - - _cogl_matrix_stack_pop (ctx->modelview_stack); - } - - /* Bind window framebuffer object */ - GE( glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0) ); - } - - /* Store new 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_set_draw_buffer() only works if - * to_pop is still on top of the stack, because - * cogl_set_draw_buffer() needs to know the previous - * state. - */ - cogl_set_draw_buffer (to_restore->target, to_restore->offscreen); - - /* cogl_set_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/cogl/driver/gl/cogl-fbo.h b/cogl/driver/gl/cogl-fbo.h deleted file mode 100644 index 8499f8fb0..000000000 --- a/cogl/driver/gl/cogl-fbo.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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. - */ - -#ifndef __COGL_FBO_H -#define __COGL_FBO_H - -#include "cogl-handle.h" - -typedef struct -{ - CoglHandleObject _parent; - int width; - int height; - GLuint gl_handle; - GLuint gl_stencil_handle; - -} CoglFbo; - -#endif /* __COGL_FBO_H */ diff --git a/cogl/driver/gl/cogl-primitives.c b/cogl/driver/gl/cogl-primitives.c index 3c1c9e656..d2bc19335 100644 --- a/cogl/driver/gl/cogl-primitives.c +++ b/cogl/driver/gl/cogl-primitives.c @@ -30,6 +30,8 @@ #include "cogl-context.h" #include "cogl-clip-stack.h" #include "cogl-material-private.h" +#include "cogl-clip-stack.h" +#include "cogl-draw-buffer-private.h" #include #include @@ -74,7 +76,7 @@ _cogl_path_add_node (gboolean new_sub_path, } void -_cogl_path_stroke_nodes () +_cogl_path_stroke_nodes (void) { guint path_start = 0; gulong enable_flags = COGL_ENABLE_VERTEX_ARRAY; @@ -82,6 +84,13 @@ _cogl_path_stroke_nodes () _COGL_GET_CONTEXT (ctx, NO_RETVAL); + _cogl_journal_flush (); + + /* NB: _cogl_draw_buffer_flush_state may disrupt various state (such + * as the material state) when flushing the clip stack, so should + * always be done first when preparing to draw. */ + _cogl_draw_buffer_flush_state (_cogl_get_draw_buffer (), 0); + enable_flags |= _cogl_material_get_cogl_enable_flags (ctx->source_material); cogl_enable (enable_flags); @@ -90,7 +99,6 @@ _cogl_path_stroke_nodes () options.disable_layers = (guint32)~0; _cogl_material_flush_gl_state (ctx->source_material, &options); - _cogl_flush_matrix_stacks (); while (path_start < ctx->path_nodes->len) { @@ -127,20 +135,33 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min, CoglPathNode *path, gboolean merge) { - guint path_start = 0; - guint sub_path_num = 0; - float bounds_x; - float bounds_y; - float bounds_w; - float bounds_h; - gulong enable_flags = COGL_ENABLE_VERTEX_ARRAY; - CoglHandle prev_source; - int i; + guint path_start = 0; + guint sub_path_num = 0; + float bounds_x; + float bounds_y; + float bounds_w; + float bounds_h; + gulong enable_flags = COGL_ENABLE_VERTEX_ARRAY; + CoglHandle prev_source; + int i; + CoglHandle draw_buffer = _cogl_get_draw_buffer (); + CoglMatrixStack *modelview_stack = + _cogl_draw_buffer_get_modelview_stack (draw_buffer); + CoglMatrixStack *projection_stack = + _cogl_draw_buffer_get_projection_stack (draw_buffer); + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + /* We don't track changes to the stencil buffer in the journal + * so we need to flush any batched geometry first */ _cogl_journal_flush (); + /* NB: _cogl_draw_buffer_flush_state may disrupt various state (such + * as the material state) when flushing the clip stack, so should + * always be done first when preparing to draw. */ + _cogl_draw_buffer_flush_state (draw_buffer, 0); + /* Just setup a simple material that doesn't use texturing... */ prev_source = cogl_handle_ref (ctx->source_material); cogl_set_source (ctx->stencil_material); @@ -161,7 +182,7 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min, } else { - GE( glClear (GL_STENCIL_BUFFER_BIT) ); + cogl_clear (NULL, COGL_BUFFER_BIT_STENCIL); GE( glStencilMask (1) ); GE( glStencilFunc (GL_LEQUAL, 0x1, 0x3) ); } @@ -179,8 +200,6 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min, } ctx->n_texcoord_arrays_enabled = 0; - _cogl_flush_matrix_stacks (); - while (path_start < path_size) { GE( glVertexPointer (2, GL_FLOAT, sizeof (CoglPathNode), @@ -194,8 +213,17 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min, significant bit */ GE( glStencilMask (merge ? 6 : 3) ); GE( glStencilOp (GL_ZERO, GL_REPLACE, GL_REPLACE) ); - glRectf (bounds_x, bounds_y, - bounds_x + bounds_w, bounds_y + bounds_h); + cogl_rectangle (bounds_x, bounds_y, + bounds_x + bounds_w, bounds_y + bounds_h); + /* Make sure the rectangle hits the stencil buffer before + * directly changing other GL state. */ + _cogl_journal_flush (); + /* NB: The journal flushing may trash the modelview state and + * enable flags */ + _cogl_matrix_stack_flush_to_gl (modelview_stack, + COGL_MATRIX_MODELVIEW); + cogl_enable (enable_flags); + GE( glStencilOp (GL_INVERT, GL_INVERT, GL_INVERT) ); } @@ -216,19 +244,24 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min, /* Decrement all of the bits twice so that only pixels where the value is 3 will remain */ - _cogl_matrix_stack_push (ctx->projection_stack); - _cogl_matrix_stack_load_identity (ctx->projection_stack); + _cogl_matrix_stack_push (projection_stack); + _cogl_matrix_stack_load_identity (projection_stack); + _cogl_matrix_stack_flush_to_gl (projection_stack, + COGL_MATRIX_PROJECTION); - _cogl_matrix_stack_push (ctx->modelview_stack); - _cogl_matrix_stack_load_identity (ctx->modelview_stack); + _cogl_matrix_stack_push (modelview_stack); + _cogl_matrix_stack_load_identity (modelview_stack); + _cogl_matrix_stack_flush_to_gl (modelview_stack, + COGL_MATRIX_MODELVIEW); - _cogl_flush_matrix_stacks (); + cogl_rectangle (-1.0, -1.0, 1.0, 1.0); + cogl_rectangle (-1.0, -1.0, 1.0, 1.0); + /* Make sure these rectangles hit the stencil buffer before we + * restore the stencil op/func. */ + _cogl_journal_flush (); - glRectf (-1.0, -1.0, 1.0, 1.0); - glRectf (-1.0, -1.0, 1.0, 1.0); - - _cogl_matrix_stack_pop (ctx->modelview_stack); - _cogl_matrix_stack_pop (ctx->projection_stack); + _cogl_matrix_stack_pop (modelview_stack); + _cogl_matrix_stack_pop (projection_stack); } GE( glStencilMask (~(GLuint) 0) ); @@ -244,8 +277,10 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min, } void -_cogl_path_fill_nodes () +_cogl_path_fill_nodes (void) { + CoglHandle draw_buffer; + CoglClipStackState *clip_state; float bounds_x; float bounds_y; float bounds_w; @@ -253,6 +288,11 @@ _cogl_path_fill_nodes () _COGL_GET_CONTEXT (ctx, NO_RETVAL); + _cogl_journal_flush (); + + draw_buffer = _cogl_get_draw_buffer (); + clip_state = _cogl_draw_buffer_get_clip_state (draw_buffer); + _cogl_path_get_bounds (ctx->path_nodes_min, ctx->path_nodes_max, &bounds_x, &bounds_y, &bounds_w, &bounds_h); @@ -261,12 +301,13 @@ _cogl_path_fill_nodes () ctx->path_nodes->len, &g_array_index (ctx->path_nodes, CoglPathNode, 0), - ctx->clip.stencil_used); + clip_state->stencil_used); cogl_rectangle (bounds_x, bounds_y, bounds_x + bounds_w, bounds_y + bounds_h); /* The stencil buffer now contains garbage so the clip area needs to be rebuilt */ - ctx->clip.stack_dirty = TRUE; + _cogl_clip_stack_state_dirty (clip_state); } + diff --git a/cogl/driver/gl/cogl-texture-driver.c b/cogl/driver/gl/cogl-texture-driver.c index e4c868dbd..742fab807 100644 --- a/cogl/driver/gl/cogl-texture-driver.c +++ b/cogl/driver/gl/cogl-texture-driver.c @@ -45,7 +45,7 @@ #include #include -#define glGenerateMipmap ctx->drv.pf_glGenerateMipmapEXT +#define glGenerateMipmap ctx->drv.pf_glGenerateMipmap void _cogl_texture_driver_bind (GLenum gl_target, diff --git a/cogl/driver/gl/cogl.c b/cogl/driver/gl/cogl.c index 04d3b750a..e81ec377c 100644 --- a/cogl/driver/gl/cogl.c +++ b/cogl/driver/gl/cogl.c @@ -32,6 +32,12 @@ #include "cogl-internal.h" #include "cogl-context.h" +typedef struct _CoglGLSymbolTableEntry +{ + const char *name; + void *ptr; +} CoglGLSymbolTableEntry; + gboolean cogl_check_extension (const gchar *name, const gchar *ext) { @@ -57,6 +63,26 @@ cogl_check_extension (const gchar *name, const gchar *ext) return FALSE; } +gboolean +_cogl_resolve_gl_symbols (CoglGLSymbolTableEntry *symbol_table, + const char *suffix) +{ + int i; + gboolean status = TRUE; + for (i = 0; symbol_table[i].name; i++) + { + char *full_name = g_strdup_printf ("%s%s", symbol_table[i].name, suffix); + *((CoglFuncPtr *)symbol_table[i].ptr) = cogl_get_proc_address (full_name); + g_free (full_name); + if (!*((CoglFuncPtr *)symbol_table[i].ptr)) + { + status = FALSE; + break; + } + } + return status; +} + #ifdef HAVE_CLUTTER_OSX static gboolean really_enable_npot (void) @@ -90,6 +116,9 @@ _cogl_features_init (void) const gchar *gl_extensions; GLint max_clip_planes = 0; GLint num_stencil_bits = 0; + gboolean fbo_ARB = FALSE; + gboolean fbo_EXT = FALSE; + const char *suffix; _COGL_GET_CONTEXT (ctx, NO_RETVAL); @@ -284,64 +313,35 @@ _cogl_features_init (void) flags |= COGL_FEATURE_SHADERS_GLSL; } - if (cogl_check_extension ("GL_EXT_framebuffer_object", gl_extensions) || - cogl_check_extension ("GL_ARB_framebuffer_object", gl_extensions)) + fbo_ARB = cogl_check_extension ("GL_ARB_framebuffer_object", gl_extensions); + if (fbo_ARB) + suffix = ""; + else { - ctx->drv.pf_glGenRenderbuffersEXT = - (COGL_PFNGLGENRENDERBUFFERSEXTPROC) - cogl_get_proc_address ("glGenRenderbuffersEXT"); + fbo_EXT = cogl_check_extension ("GL_EXT_framebuffer_object", gl_extensions); + if (fbo_EXT) + suffix = "EXT"; + } - ctx->drv.pf_glDeleteRenderbuffersEXT = - (COGL_PFNGLDELETERENDERBUFFERSEXTPROC) - cogl_get_proc_address ("glDeleteRenderbuffersEXT"); + if (fbo_ARB || fbo_EXT) + { + CoglGLSymbolTableEntry symbol_table[] = { + {"glGenRenderbuffers", &ctx->drv.pf_glGenRenderbuffers}, + {"glDeleteRenderbuffers", &ctx->drv.pf_glDeleteRenderbuffers}, + {"glBindRenderbuffer", &ctx->drv.pf_glBindRenderbuffer}, + {"glRenderbufferStorage", &ctx->drv.pf_glRenderbufferStorage}, + {"glGenFramebuffers", &ctx->drv.pf_glGenFramebuffers}, + {"glBindFramebuffer", &ctx->drv.pf_glBindFramebuffer}, + {"glFramebufferTexture2D", &ctx->drv.pf_glFramebufferTexture2D}, + {"glFramebufferRenderbuffer", &ctx->drv.pf_glFramebufferRenderbuffer}, + {"glCheckFramebufferStatus", &ctx->drv.pf_glCheckFramebufferStatus}, + {"glDeleteFramebuffers", &ctx->drv.pf_glDeleteFramebuffers}, + {"glGenerateMipmap", &ctx->drv.pf_glGenerateMipmap}, + {NULL, NULL} + }; - ctx->drv.pf_glBindRenderbufferEXT = - (COGL_PFNGLBINDRENDERBUFFEREXTPROC) - cogl_get_proc_address ("glBindRenderbufferEXT"); - - ctx->drv.pf_glRenderbufferStorageEXT = - (COGL_PFNGLRENDERBUFFERSTORAGEEXTPROC) - cogl_get_proc_address ("glRenderbufferStorageEXT"); - - ctx->drv.pf_glGenFramebuffersEXT = - (COGL_PFNGLGENFRAMEBUFFERSEXTPROC) - cogl_get_proc_address ("glGenFramebuffersEXT"); - - ctx->drv.pf_glBindFramebufferEXT = - (COGL_PFNGLBINDFRAMEBUFFEREXTPROC) - cogl_get_proc_address ("glBindFramebufferEXT"); - - ctx->drv.pf_glFramebufferTexture2DEXT = - (COGL_PFNGLFRAMEBUFFERTEXTURE2DEXTPROC) - cogl_get_proc_address ("glFramebufferTexture2DEXT"); - - ctx->drv.pf_glFramebufferRenderbufferEXT = - (COGL_PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC) - cogl_get_proc_address ("glFramebufferRenderbufferEXT"); - - ctx->drv.pf_glCheckFramebufferStatusEXT = - (COGL_PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC) - cogl_get_proc_address ("glCheckFramebufferStatusEXT"); - - ctx->drv.pf_glDeleteFramebuffersEXT = - (COGL_PFNGLDELETEFRAMEBUFFERSEXTPROC) - cogl_get_proc_address ("glDeleteFramebuffersEXT"); - - ctx->drv.pf_glGenerateMipmapEXT = - (COGL_PFNGLGENERATEMIPMAPEXTPROC) - cogl_get_proc_address ("glGenerateMipmapEXT"); - - if (ctx->drv.pf_glGenRenderbuffersEXT && - ctx->drv.pf_glBindRenderbufferEXT && - ctx->drv.pf_glRenderbufferStorageEXT && - ctx->drv.pf_glGenFramebuffersEXT && - ctx->drv.pf_glBindFramebufferEXT && - ctx->drv.pf_glFramebufferTexture2DEXT && - ctx->drv.pf_glFramebufferRenderbufferEXT && - ctx->drv.pf_glCheckFramebufferStatusEXT && - ctx->drv.pf_glDeleteFramebuffersEXT && - ctx->drv.pf_glGenerateMipmapEXT) - flags |= COGL_FEATURE_OFFSCREEN; + if (_cogl_resolve_gl_symbols (symbol_table, suffix)) + flags |= COGL_FEATURE_OFFSCREEN; } if (cogl_check_extension ("GL_EXT_framebuffer_blit", gl_extensions)) diff --git a/cogl/driver/gles/Makefile.am b/cogl/driver/gles/Makefile.am index 0a44fb465..182b29b84 100644 --- a/cogl/driver/gles/Makefile.am +++ b/cogl/driver/gles/Makefile.am @@ -22,11 +22,9 @@ libclutter_cogl_driver_la_CPPFLAGS = \ $(CLUTTER_DEBUG_CFLAGS) \ $(MAINTAINER_CFLAGS) libclutter_cogl_driver_la_SOURCES = \ - cogl-fbo.h \ cogl.c \ cogl-primitives.c \ cogl-texture-driver.c \ - cogl-fbo.c \ cogl-context-driver.c \ cogl-context-driver.h \ cogl-gles2-wrapper.h \ diff --git a/cogl/driver/gles/cogl-context-driver.c b/cogl/driver/gles/cogl-context-driver.c index 6660db1f3..ae2a06477 100644 --- a/cogl/driver/gles/cogl-context-driver.c +++ b/cogl/driver/gles/cogl-context-driver.c @@ -31,6 +31,16 @@ void _cogl_create_context_driver (CoglContext *context) { + context->drv.pf_glGenRenderbuffers = NULL; + context->drv.pf_glBindRenderbuffer = NULL; + context->drv.pf_glRenderbufferStorage = NULL; + context->drv.pf_glGenFramebuffers = NULL; + context->drv.pf_glBindFramebuffer = NULL; + context->drv.pf_glFramebufferTexture2D = NULL; + context->drv.pf_glFramebufferRenderbuffer = NULL; + context->drv.pf_glCheckFramebufferStatus = NULL; + context->drv.pf_glDeleteFramebuffers = NULL; + /* Init the GLES2 wrapper */ #ifdef HAVE_COGL_GLES2 cogl_gles2_wrapper_init (&context->drv.gles2); diff --git a/cogl/driver/gles/cogl-context-driver.h b/cogl/driver/gles/cogl-context-driver.h index 9b5996e74..de24eb775 100644 --- a/cogl/driver/gles/cogl-context-driver.h +++ b/cogl/driver/gles/cogl-context-driver.h @@ -24,17 +24,26 @@ #ifndef __COGL_CONTEXT_DRIVER_H #define __COGL_CONTEXT_DRIVER_H +#include "cogl.h" #include "cogl-gles2-wrapper.h" typedef struct _CoglContextDriver { + COGL_PFNGLGENRENDERBUFFERSPROC pf_glGenRenderbuffers; + COGL_PFNGLDELETERENDERBUFFERSPROC pf_glDeleteRenderbuffers; + COGL_PFNGLBINDRENDERBUFFERPROC pf_glBindRenderbuffer; + COGL_PFNGLRENDERBUFFERSTORAGEPROC pf_glRenderbufferStorage; + COGL_PFNGLGENFRAMEBUFFERSPROC pf_glGenFramebuffers; + COGL_PFNGLBINDFRAMEBUFFERPROC pf_glBindFramebuffer; + COGL_PFNGLFRAMEBUFFERTEXTURE2DPROC pf_glFramebufferTexture2D; + COGL_PFNGLFRAMEBUFFERRENDERBUFFERPROC pf_glFramebufferRenderbuffer; + COGL_PFNGLCHECKFRAMEBUFFERSTATUSPROC pf_glCheckFramebufferStatus; + COGL_PFNGLDELETEFRAMEBUFFERSPROC pf_glDeleteFramebuffers; + COGL_PFNGLGENERATEMIPMAPPROC pf_glGenerateMipmap; + #ifdef HAVE_COGL_GLES2 CoglGles2Wrapper gles2; - - /* Viewport store for FBOs. Needed because glPushAttrib() isn't - supported */ - GLint viewport_store[4]; #endif } CoglContextDriver; diff --git a/cogl/driver/gles/cogl-defines.h.in b/cogl/driver/gles/cogl-defines.h.in index 97045df94..8f0cff1da 100644 --- a/cogl/driver/gles/cogl-defines.h.in +++ b/cogl/driver/gles/cogl-defines.h.in @@ -633,6 +633,76 @@ G_BEGIN_DECLS #define CGL_SHININESS 0x1601 #endif +/* Extension function prototypes */ + +#ifndef APIENTRY +#define APIENTRY +#endif + +#ifndef APIENTRYP +#define APIENTRYP APIENTRY * +#endif + +typedef void + (APIENTRYP COGL_PFNGLGENRENDERBUFFERSPROC) + (GLsizei n, + GLuint *renderbuffers); + +typedef void + (APIENTRYP COGL_PFNGLBINDRENDERBUFFERPROC) + (GLenum target, + GLuint renderbuffer); + +typedef void + (APIENTRYP COGL_PFNGLRENDERBUFFERSTORAGEPROC) + (GLenum target, + GLenum internalformat, + GLsizei width, + GLsizei height); + +typedef void + (APIENTRYP COGL_PFNGLGENFRAMEBUFFERSPROC) + (GLsizei n, + GLuint *framebuffers); + +typedef void + (APIENTRYP COGL_PFNGLBINDFRAMEBUFFERPROC) + (GLenum target, + GLuint framebuffer); + +typedef void + (APIENTRYP COGL_PFNGLFRAMEBUFFERTEXTURE2DPROC) + (GLenum target, + GLenum attachment, + GLenum textarget, + GLuint texture, + GLint level); + +typedef void + (APIENTRYP COGL_PFNGLFRAMEBUFFERRENDERBUFFERPROC) + (GLenum target, + GLenum attachment, + GLenum renderbuffertarget, + GLuint renderbuffer); + +typedef GLenum + (APIENTRYP COGL_PFNGLCHECKFRAMEBUFFERSTATUSPROC) + (GLenum target); + +typedef void + (APIENTRYP COGL_PFNGLDELETEFRAMEBUFFERSPROC) + (GLsizei n, + const GLuint *framebuffers); + +typedef void + (APIENTRYP COGL_PFNGLDELETERENDERBUFFERSPROC) + (GLsizei n, + const GLuint *renderbuffers); + +typedef void + (APIENTRYP COGL_PFNGLGENERATEMIPMAPPROC) + (GLenum target); + G_END_DECLS #endif diff --git a/cogl/driver/gles/cogl-fbo.c b/cogl/driver/gles/cogl-fbo.c deleted file mode 100644 index 41b5c6b0c..000000000 --- a/cogl/driver/gles/cogl-fbo.c +++ /dev/null @@ -1,328 +0,0 @@ -/* - * Cogl - * - * An object oriented GL/GLES Abstraction/Utility Layer - * - * Copyright (C) 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-util.h" -#include "cogl-texture-private.h" -#include "cogl-fbo.h" -#include "cogl-context.h" -#include "cogl-handle.h" -#include "cogl-gles2-wrapper.h" - -#ifdef HAVE_COGL_GLES2 - -static void _cogl_offscreen_free (CoglFbo *fbo); - -COGL_HANDLE_DEFINE (Fbo, offscreen); - -CoglHandle -cogl_offscreen_new_to_texture (CoglHandle texhandle) -{ - CoglFbo *fbo; - int width; - int height; - GLuint tex_gl_handle; - GLenum tex_gl_target; - 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 */ - if (cogl_texture_is_sliced (texhandle)) - return COGL_INVALID_HANDLE; - - /* 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)) - return COGL_INVALID_HANDLE; - - if (tex_gl_target != GL_TEXTURE_2D) - return COGL_INVALID_HANDLE; - - /* 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, - tex_gl_target, tex_gl_handle, 0) ); - 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)); - fbo->width = width; - fbo->height = height; - fbo->gl_handle = fbo_gl_handle; - fbo->gl_stencil_handle = gl_stencil_handle; - - return _cogl_offscreen_handle_new (fbo); -} - -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( glDeleteRenderbuffers (1, &fbo->gl_stencil_handle) ); - GE( glDeleteFramebuffers (1, &fbo->gl_handle) ); - g_free (fbo); -} - -void -cogl_set_draw_buffer (CoglBufferTarget target, CoglHandle offscreen) -{ - CoglFbo *fbo = NULL; - CoglDrawBufferState *draw_buffer; - - _COGL_GET_CONTEXT (ctx, NO_RETVAL); - - _cogl_journal_flush (); - - 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 */ - if (!cogl_is_offscreen (offscreen)) - return; - - fbo = _cogl_offscreen_pointer_from_handle (offscreen); - - /* Check current draw buffer target */ - if (draw_buffer->target != COGL_OFFSCREEN_BUFFER) - { - /* Push the viewport and matrix setup if redirecting - from a non-screen buffer */ - GE( glGetIntegerv (GL_VIEWPORT, ctx->drv.viewport_store) ); - - _cogl_matrix_stack_push (ctx->projection_stack); - _cogl_matrix_stack_load_identity (ctx->projection_stack); - - _cogl_matrix_stack_push (ctx->modelview_stack); - _cogl_matrix_stack_load_identity (ctx->modelview_stack); - } - else - { - /* Override viewport and matrix setup if redirecting - from another offscreen buffer */ - _cogl_matrix_stack_load_identity (ctx->projection_stack); - - _cogl_matrix_stack_load_identity (ctx->modelview_stack); - } - - /* Setup new viewport and matrices */ -<<<<<<< HEAD:clutter/cogl/cogl/driver/gles/cogl-fbo.c - GE( glViewport (0, 0, fbo->width, fbo->height) ); - _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); -======= - cogl_viewport (fbo->width, fbo->height); - _cogl_current_matrix_translate (-1.0f, -1.0f, 0.0f); - _cogl_current_matrix_scale (2.0f / fbo->width, 2.0f / fbo->height, 1.0f); ->>>>>>> c3e471c... [cogl-fbo] Bring the gles code more in line with gl code:clutter/cogl/cogl/driver/gles/cogl-fbo.c - - /* Bind offscreen framebuffer object */ - GE( glBindFramebuffer (GL_FRAMEBUFFER, fbo->gl_handle) ); - GE( glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE) ); - } - else if (target & COGL_WINDOW_BUFFER) - { - /* Check current draw buffer target */ - if (draw_buffer->target == COGL_OFFSCREEN_BUFFER) - { - /* Pop viewport and matrices if redirecting back - from an offscreen buffer */ - GE( glViewport (ctx->drv.viewport_store[0], - ctx->drv.viewport_store[1], - ctx->drv.viewport_store[2], - ctx->drv.viewport_store[3]) ); - - _cogl_matrix_stack_pop (ctx->projection_stack); - - _cogl_matrix_stack_pop (ctx->modelview_stack); - } - - /* Bind window framebuffer object */ - GE( glBindFramebuffer (GL_FRAMEBUFFER, 0) ); - } - - /* Store new 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_set_draw_buffer() only works if - * to_pop is still on top of the stack, because - * cogl_set_draw_buffer() needs to know the previous - * state. - */ - cogl_set_draw_buffer (to_restore->target, to_restore->offscreen); - - /* cogl_set_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 */ - -/* No support on regular OpenGL 1.1 */ - -gboolean -cogl_is_offscreen (CoglHandle handle) -{ - return FALSE; -} - -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 -cogl_set_draw_buffer (CoglBufferTarget target, CoglHandle offscreen) -{ -} - -#endif /* HAVE_COGL_GLES2 */ diff --git a/cogl/driver/gles/cogl-fbo.h b/cogl/driver/gles/cogl-fbo.h deleted file mode 100644 index 8499f8fb0..000000000 --- a/cogl/driver/gles/cogl-fbo.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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. - */ - -#ifndef __COGL_FBO_H -#define __COGL_FBO_H - -#include "cogl-handle.h" - -typedef struct -{ - CoglHandleObject _parent; - int width; - int height; - GLuint gl_handle; - GLuint gl_stencil_handle; - -} CoglFbo; - -#endif /* __COGL_FBO_H */ diff --git a/cogl/driver/gles/cogl-primitives.c b/cogl/driver/gles/cogl-primitives.c index f31bd2143..09d637e7d 100644 --- a/cogl/driver/gles/cogl-primitives.c +++ b/cogl/driver/gles/cogl-primitives.c @@ -30,6 +30,9 @@ #include "cogl-context.h" #include "cogl-clip-stack.h" #include "cogl-material-private.h" +#include "cogl-clip-stack.h" +#include "cogl-draw-buffer-private.h" +#include "cogl-clip-stack.h" #include #include @@ -72,7 +75,7 @@ _cogl_path_add_node (gboolean new_sub_path, } void -_cogl_path_stroke_nodes () +_cogl_path_stroke_nodes (void) { guint path_start = 0; gulong enable_flags = COGL_ENABLE_VERTEX_ARRAY; @@ -80,13 +83,21 @@ _cogl_path_stroke_nodes () _COGL_GET_CONTEXT (ctx, NO_RETVAL); + _cogl_journal_flush (); + + /* NB: _cogl_draw_buffer_flush_state may disrupt various state (such + * as the material state) when flushing the clip stack, so should + * always be done first when preparing to draw. */ + _cogl_draw_buffer_flush_state (_cogl_get_draw_buffer (), 0); + enable_flags |= _cogl_material_get_cogl_enable_flags (ctx->source_material); cogl_enable (enable_flags); options.flags = COGL_MATERIAL_FLUSH_DISABLE_MASK; + /* disable all texture layers */ options.disable_layers = (guint32)~0; - _cogl_material_flush_gl_state (ctx->source_material,&options); - _cogl_flush_matrix_stacks(); + + _cogl_material_flush_gl_state (ctx->source_material, &options); while (path_start < ctx->path_nodes->len) { @@ -129,18 +140,38 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min, CoglPathNode *path, gboolean merge) { - guint path_start = 0; - guint sub_path_num = 0; - float bounds_x; - float bounds_y; - float bounds_w; - float bounds_h; - gulong enable_flags = COGL_ENABLE_VERTEX_ARRAY; + guint path_start = 0; + guint sub_path_num = 0; + float bounds_x; + float bounds_y; + float bounds_w; + float bounds_h; + gulong enable_flags = COGL_ENABLE_VERTEX_ARRAY; + CoglHandle prev_source; + int i; + CoglHandle draw_buffer = _cogl_get_draw_buffer (); + CoglMatrixStack *modelview_stack = + _cogl_draw_buffer_get_modelview_stack (draw_buffer); + CoglMatrixStack *projection_stack = + _cogl_draw_buffer_get_projection_stack (draw_buffer); + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + /* We don't track changes to the stencil buffer in the journal + * so we need to flush any batched geometry first */ + _cogl_journal_flush (); + + /* NB: _cogl_draw_buffer_flush_state may disrupt various state (such + * as the material state) when flushing the clip stack, so should + * always be done first when preparing to draw. */ + _cogl_draw_buffer_flush_state (draw_buffer, 0); + /* Just setup a simple material that doesn't use texturing... */ - _cogl_material_flush_gl_state (ctx->stencil_material, NULL); + prev_source = cogl_handle_ref (ctx->source_material); + cogl_set_source (ctx->stencil_material); + + _cogl_material_flush_gl_state (ctx->source_material, NULL); enable_flags |= _cogl_material_get_cogl_enable_flags (ctx->source_material); @@ -156,7 +187,7 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min, } else { - GE( glClear (GL_STENCIL_BUFFER_BIT) ); + cogl_clear (NULL, COGL_BUFFER_BIT_STENCIL); GE( glStencilMask (1) ); GE( glStencilFunc (GL_LEQUAL, 0x1, 0x3) ); } @@ -167,7 +198,13 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min, GE( glColorMask (FALSE, FALSE, FALSE, FALSE) ); GE( glDepthMask (FALSE) ); - _cogl_matrix_stack_flush_to_gl (ctx->modelview_stack, COGL_MATRIX_MODELVIEW); + for (i = 0; i < ctx->n_texcoord_arrays_enabled; i++) + { + GE (glClientActiveTexture (GL_TEXTURE0 + i)); + GE (glDisableClientState (GL_TEXTURE_COORD_ARRAY)); + } + ctx->n_texcoord_arrays_enabled = 0; + while (path_start < path_size) { GE( glVertexPointer (2, GL_FLOAT, sizeof (CoglPathNode), @@ -183,6 +220,14 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min, GE( glStencilOp (GL_ZERO, GL_REPLACE, GL_REPLACE) ); cogl_rectangle (bounds_x, bounds_y, bounds_x + bounds_w, bounds_y + bounds_h); + /* Make sure the rectangle hits the stencil buffer before + * directly changing other GL state. */ + _cogl_journal_flush (); + /* NB: The journal flushing may trash the modelview state and + * enable flags */ + _cogl_matrix_stack_flush_to_gl (modelview_stack, + COGL_MATRIX_MODELVIEW); + cogl_enable (enable_flags); GE( glStencilOp (GL_INVERT, GL_INVERT, GL_INVERT) ); } @@ -204,17 +249,24 @@ _cogl_add_path_to_stencil_buffer (floatVec2 nodes_min, /* Decrement all of the bits twice so that only pixels where the value is 3 will remain */ - _cogl_matrix_stack_push (ctx->projection_stack); - _cogl_matrix_stack_load_identity (ctx->projection_stack); + _cogl_matrix_stack_push (projection_stack); + _cogl_matrix_stack_load_identity (projection_stack); + _cogl_matrix_stack_flush_to_gl (projection_stack, + COGL_MATRIX_PROJECTION); - _cogl_matrix_stack_push (ctx->modelview_stack); - _cogl_matrix_stack_load_identity (ctx->modelview_stack); + _cogl_matrix_stack_push (modelview_stack); + _cogl_matrix_stack_load_identity (modelview_stack); + _cogl_matrix_stack_flush_to_gl (modelview_stack, + COGL_MATRIX_MODELVIEW); cogl_rectangle (-1.0, -1.0, 1.0, 1.0); cogl_rectangle (-1.0, -1.0, 1.0, 1.0); + /* Make sure these rectangles hit the stencil buffer before we + * restore the stencil op/func. */ + _cogl_journal_flush (); - _cogl_matrix_stack_pop (ctx->modelview_stack); - _cogl_matrix_stack_pop (ctx->projection_stack); + _cogl_matrix_stack_pop (modelview_stack); + _cogl_matrix_stack_pop (projection_stack); } GE( glStencilMask (~(GLuint) 0) ); @@ -250,6 +302,21 @@ _cogl_path_fill_nodes_scanlines (CoglPathNode *path, _COGL_GET_CONTEXT (ctx, NO_RETVAL); + /* We are going to use GL to draw directly so make sure any + * previously batched geometry gets to GL before we start... + */ + _cogl_journal_flush (); + + /* NB: _cogl_draw_buffer_flush_state may disrupt various state (such + * as the material state) when flushing the clip stack, so should + * always be done first when preparing to draw. */ + _cogl_draw_buffer_flush_state (_cogl_get_draw_buffer (), 0); + + _cogl_material_flush_gl_state (ctx->source_material, NULL); + + cogl_enable (COGL_ENABLE_VERTEX_ARRAY + | (ctx->color_alpha < 255 ? COGL_ENABLE_BLEND : 0)); + /* clear scanline intersection lists */ for (i=0; i < bounds_h; i++) scanlines[i]=NULL; @@ -386,8 +453,6 @@ _cogl_path_fill_nodes_scanlines (CoglPathNode *path, } /* render triangles */ - cogl_enable (COGL_ENABLE_VERTEX_ARRAY - | (ctx->color_alpha < 255 ? COGL_ENABLE_BLEND : 0)); GE ( glVertexPointer (2, GL_FLOAT, 0, coords ) ); GE ( glDrawArrays (GL_TRIANGLES, 0, spans * 2 * 3)); g_free (coords); @@ -395,7 +460,7 @@ _cogl_path_fill_nodes_scanlines (CoglPathNode *path, } void -_cogl_path_fill_nodes () +_cogl_path_fill_nodes (void) { float bounds_x; float bounds_y; @@ -409,19 +474,27 @@ _cogl_path_fill_nodes () if (cogl_features_available (COGL_FEATURE_STENCIL_BUFFER)) { + CoglHandle draw_buffer; + CoglClipStackState *clip_state; + + _cogl_journal_flush (); + + draw_buffer = _cogl_get_draw_buffer (); + clip_state = _cogl_draw_buffer_get_clip_state (draw_buffer); + _cogl_add_path_to_stencil_buffer (ctx->path_nodes_min, ctx->path_nodes_max, ctx->path_nodes->len, &g_array_index (ctx->path_nodes, CoglPathNode, 0), - ctx->clip.stencil_used); + clip_state->stencil_used); cogl_rectangle (bounds_x, bounds_y, bounds_x + bounds_w, bounds_y + bounds_h); /* The stencil buffer now contains garbage so the clip area needs to be rebuilt */ - ctx->clip.stack_dirty = TRUE; + _cogl_clip_stack_state_dirty (clip_state); } else { diff --git a/cogl/driver/gles/cogl.c b/cogl/driver/gles/cogl.c index aa7da6ef2..565ff0fa7 100644 --- a/cogl/driver/gles/cogl.c +++ b/cogl/driver/gles/cogl.c @@ -31,6 +31,56 @@ #include "cogl-internal.h" #include "cogl-context.h" +typedef struct _CoglGLSymbolTableEntry +{ + const char *name; + void *ptr; +} CoglGLSymbolTableEntry; + +gboolean +cogl_check_extension (const gchar *name, const gchar *ext) +{ + gchar *end; + gint name_len, n; + + if (name == NULL || ext == NULL) + return FALSE; + + end = (gchar*)(ext + strlen(ext)); + + name_len = strlen(name); + + while (ext < end) + { + n = strcspn(ext, " "); + + if ((name_len == n) && (!strncmp(name, ext, n))) + return TRUE; + ext += (n + 1); + } + + return FALSE; +} + +gboolean +_cogl_resolve_gl_symbols (CoglGLSymbolTableEntry *symbol_table, + const char *suffix) +{ + int i; + gboolean status = TRUE; + for (i = 0; symbol_table[i].name; i++) + { + char *full_name = g_strdup_printf ("%s%s", symbol_table[i].name, suffix); + *((CoglFuncPtr *)symbol_table[i].ptr) = cogl_get_proc_address (full_name); + g_free (full_name); + if (!*((CoglFuncPtr *)symbol_table[i].ptr)) + { + status = FALSE; + break; + } + } + return status; +} void @@ -39,9 +89,34 @@ _cogl_features_init (void) CoglFeatureFlags flags = 0; int max_clip_planes = 0; GLint num_stencil_bits = 0; + const char *gl_extensions; _COGL_GET_CONTEXT (ctx, NO_RETVAL); + gl_extensions = (const char*) glGetString (GL_EXTENSIONS); + + if (cogl_check_extension ("GL_OES_framebuffer_object", gl_extensions)) + { + g_assert (0); + CoglGLSymbolTableEntry symbol_table[] = { + {"glGenRenderbuffers", &ctx->drv.pf_glGenRenderbuffers}, + {"glDeleteRenderbuffers", &ctx->drv.pf_glDeleteRenderbuffers}, + {"glBindRenderbuffer", &ctx->drv.pf_glBindRenderbuffer}, + {"glRenderbufferStorage", &ctx->drv.pf_glRenderbufferStorage}, + {"glGenFramebuffers", &ctx->drv.pf_glGenFramebuffers}, + {"glBindFramebuffer", &ctx->drv.pf_glBindFramebuffer}, + {"glFramebufferTexture2D", &ctx->drv.pf_glFramebufferTexture2D}, + {"glFramebufferRenderbuffer", &ctx->drv.pf_glFramebufferRenderbuffer}, + {"glCheckFramebufferStatus", &ctx->drv.pf_glCheckFramebufferStatus}, + {"glDeleteFramebuffers", &ctx->drv.pf_glDeleteFramebuffers}, + {"glGenerateMipmap", &ctx->drv.pf_glGenerateMipmap}, + {NULL, NULL} + }; + + if (_cogl_resolve_gl_symbols (symbol_table, "OES")) + flags |= COGL_FEATURE_OFFSCREEN; + } + GE( glGetIntegerv (GL_STENCIL_BITS, &num_stencil_bits) ); /* We need at least three stencil bits to combine clips */ if (num_stencil_bits > 2) From 49db9f4f775c2308c31bc3bacf64cd9263f70b55 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Sat, 17 Oct 2009 00:31:26 +0100 Subject: [PATCH 04/24] [clip-stack] Handle flipped rectangles in try_pushing_rect_as_window_rect() We were ignoring the possibility that the current modelview matrix may flip the incoming rectangle in which case we didn't calculate a valid scissor rectangle for clipping. This fixes: http://bugzilla.o-hand.com/show_bug.cgi?id=1809 (Clipping doesn't work within an FBO) --- cogl/cogl-clip-stack.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/cogl/cogl-clip-stack.c b/cogl/cogl-clip-stack.c index 8cf59d197..e9a7571cc 100644 --- a/cogl/cogl-clip-stack.c +++ b/cogl/cogl-clip-stack.c @@ -203,6 +203,15 @@ try_pushing_rect_as_window_rect (float x_offset, cogl_get_modelview_matrix (&matrix); + /* If the modelview meets these constraints then a transformed rectangle + * should still be a rectangle when it reaches screen coordinates. + * + * FIXME: we are are making certain assumptions about the projection + * matrix a.t.m and should really be looking at the combined modelview + * and projection matrix. + * FIXME: we don't consider rotations that are a multiple of 90 degrees + * which could be quite common. + */ if (matrix.xy != 0 || matrix.xz != 0 || matrix.yx != 0 || matrix.yz != 0 || matrix.zx != 0 || matrix.zy != 0) @@ -214,6 +223,15 @@ try_pushing_rect_as_window_rect (float x_offset, transform_point (&matrix, &matrix_p, v, &_x0, &_y0); transform_point (&matrix, &matrix_p, v, &_x1, &_y1); + /* Consider that the modelview matrix may flip the rectangle + * along the x or y axis... */ +#define SWAP(A,B) do { float tmp = B; B = A; A = tmp; } while (0) + if (_x0 > _x1) + SWAP (_x0, _x1); + if (_y0 > _y1) + SWAP (_y0, _y1); +#undef SWAP + cogl_clip_push_window_rect (_x0, _y0, _x1 - _x0, _y1 - _y0); return TRUE; } From 4bc947cfcc73f7a44d8e1c715471dc7805def9aa Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Thu, 22 Oct 2009 12:35:33 +0100 Subject: [PATCH 05/24] [cogl] Ensure features are initialized first in cogl_create_context Previously some context initializing was being done without valid feature flags. --- cogl/cogl-context.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cogl/cogl-context.c b/cogl/cogl-context.c index 8005aaf23..0f309bd06 100644 --- a/cogl/cogl-context.c +++ b/cogl/cogl-context.c @@ -59,6 +59,11 @@ cogl_create_context (void) _context->feature_flags = 0; _context->features_cached = FALSE; + /* Initialise the driver specific state */ + /* TODO: combine these two into one function */ + _cogl_create_context_driver (_context); + _cogl_features_init (); + _context->enable_flags = 0; _context->color_alpha = 0; @@ -107,11 +112,6 @@ cogl_create_context (void) _context->texture_download_material = COGL_INVALID_HANDLE; - /* Initialise the driver specific state */ - /* TODO: combine these two into one function */ - _cogl_create_context_driver (_context); - _cogl_features_init (); - /* Create default textures used for fall backs */ _context->default_gl_texture_2d_tex = cogl_texture_new_from_data (1, /* width */ From 2d610c230035d39cb6ebfa0bdd9af35f69bda347 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Thu, 22 Oct 2009 16:13:01 +0100 Subject: [PATCH 06/24] [cogl] Make sure we draw upside down to offscreen draw buffers First a few notes about Cogl coordinate systems: - Cogl defines the window origin, viewport origin and texture coordinates origin to be top left unlike OpenGL which defines them as bottom left. - Cogl defines the modelview and projection identity matrices in exactly the same way as OpenGL. - I.e. we believe that for 2D centric constructs: windows/framebuffers, viewports and textures developers are more used to dealing with a top left origin, but when modeling objects in 3D; an origin at the center with y going up is quite natural. The way Cogl handles textures is by uploading data upside down in OpenGL terms so that bottom left becomes top left. (Note: This also has the benefit that we don't need to flip the data we get from image decoding libraries since they typically also consider top left to be the image origin.) The viewport and window coords are mostly handled with various y = height - y tweaks before we pass y coordinates to OpenGL. Generally speaking though the handling of coordinate spaces in Cogl is a bit fragile. I guess partly because none of it was design to be, it just evolved from how Clutter defines its coordinates without much consideration or testing. I hope to improve this over a number of commits; starting here. This commit deals with the fact that offscreen draw buffers may be bound to textures but we don't "upload" the texture data upside down, and so if you texture from an offscreen draw buffer you need to manually flip the texture coordinates to get it the right way around. We now force offscreen rendering to be flipped upside down by tweaking the projection matrix right before we submit it to OpenGL to scale y by -1. The tweak is entirely hidden from the user such that if you call cogl_get_projection you will not see this scale. --- cogl/cogl-context.c | 4 ++++ cogl/cogl-context.h | 4 ++++ cogl/cogl-draw-buffer-private.h | 3 +++ cogl/cogl-matrix-stack.c | 36 ++++++++++++++++++++++++--------- 4 files changed, 37 insertions(+), 10 deletions(-) diff --git a/cogl/cogl-context.c b/cogl/cogl-context.c index 0f309bd06..ccd71c7d0 100644 --- a/cogl/cogl-context.c +++ b/cogl/cogl-context.c @@ -71,6 +71,10 @@ cogl_create_context (void) _context->indirect = gl_is_indirect; + cogl_matrix_init_identity (&_context->identity_matrix); + cogl_matrix_init_identity (&_context->y_flip_matrix); + cogl_matrix_scale (&_context->y_flip_matrix, 1, -1, 1); + _context->flushed_matrix_mode = COGL_MATRIX_MODELVIEW; _context->texture_units = NULL; diff --git a/cogl/cogl-context.h b/cogl/cogl-context.h index 67e33cc68..1d3f9ecf3 100644 --- a/cogl/cogl-context.h +++ b/cogl/cogl-context.h @@ -52,6 +52,10 @@ typedef struct gboolean indirect; + /* A few handy matrix constants */ + CoglMatrix identity_matrix; + CoglMatrix y_flip_matrix; + /* Client-side matrix stack or NULL if none */ CoglMatrixMode flushed_matrix_mode; GList *texture_units; diff --git a/cogl/cogl-draw-buffer-private.h b/cogl/cogl-draw-buffer-private.h index d5bdf0b11..5ad4fc896 100644 --- a/cogl/cogl-draw-buffer-private.h +++ b/cogl/cogl-draw-buffer-private.h @@ -115,6 +115,9 @@ _cogl_draw_buffer_flush_state (CoglHandle handle, CoglHandle _cogl_onscreen_new (void); +gboolean +cogl_is_offscreen (CoglHandle handle); + CoglHandle _cogl_get_draw_buffer (void); GSList * diff --git a/cogl/cogl-matrix-stack.c b/cogl/cogl-matrix-stack.c index b1f21c946..4e2269dbc 100644 --- a/cogl/cogl-matrix-stack.c +++ b/cogl/cogl-matrix-stack.c @@ -22,6 +22,7 @@ * * Authors: * Havoc Pennington for litl + * Robert Bragg */ #ifdef HAVE_CONFIG_H @@ -32,6 +33,7 @@ #include "cogl-context.h" #include "cogl-internal.h" #include "cogl-matrix-stack.h" +#include "cogl-draw-buffer-private.h" typedef struct { CoglMatrix matrix; @@ -412,21 +414,35 @@ _cogl_matrix_stack_flush_to_gl (CoglMatrixStack *stack, ctx->flushed_matrix_mode = mode; } - /* In theory it might help the GL implementation if we used our - * local analysis of the matrix and called Translate/Scale rather - * than LoadMatrix to send a 2D matrix + /* Because Cogl defines texture coordinates to have a top left origin and + * because offscreen draw buffers may be used for rendering to textures we + * always render upside down to offscreen buffers. */ - - if (state->is_identity) + if (mode == COGL_MATRIX_PROJECTION && + cogl_is_offscreen (_cogl_get_draw_buffer ())) { - if (!stack->flushed_identity) - GE (glLoadIdentity ()); - stack->flushed_identity = TRUE; + CoglMatrix flipped_projection; + CoglMatrix *projection = + state->is_identity ? &ctx->identity_matrix : &state->matrix; + + cogl_matrix_multiply (&flipped_projection, + &ctx->y_flip_matrix, projection); + GE (glLoadMatrixf (cogl_matrix_get_array (&flipped_projection))); + stack->flushed_identity = FALSE; } else { - GE (glLoadMatrixf (cogl_matrix_get_array (&state->matrix))); - stack->flushed_identity = FALSE; + if (state->is_identity) + { + if (!stack->flushed_identity) + GE (glLoadIdentity ()); + stack->flushed_identity = TRUE; + } + else + { + GE (glLoadMatrixf (cogl_matrix_get_array (&state->matrix))); + stack->flushed_identity = FALSE; + } } stack->flushed_state = state; } From b4770c1e9b27f7816572342b3899b117e5a91adb Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Wed, 21 Oct 2009 23:24:49 +0100 Subject: [PATCH 07/24] [cogl] Make sure Cogl always knows the current window geometry Because Cogl defines the origin of viewport and window coordinates to be top-left it always needs to know the size of the current window so that Cogl window/viewport coordinates can be transformed into OpenGL coordinates. This also fixes cogl_read_pixels to use the current draw buffer height instead of the viewport height to determine the OpenGL y coordinate to use for glReadPixels. --- cogl/cogl-context.c | 2 +- cogl/cogl-context.h | 2 +- cogl/cogl-draw-buffer.c | 36 +++++++++++++++++++++++++++++++----- cogl/cogl.h.in | 3 +++ 4 files changed, 36 insertions(+), 7 deletions(-) diff --git a/cogl/cogl-context.c b/cogl/cogl-context.c index ccd71c7d0..d50ec6cf9 100644 --- a/cogl/cogl-context.c +++ b/cogl/cogl-context.c @@ -102,7 +102,7 @@ cogl_create_context (void) _context->window_buffer = window_buffer; cogl_set_draw_buffer (COGL_WINDOW_BUFFER, 0/* ignored */); _context->dirty_bound_framebuffer = TRUE; - _context->dirty_viewport = TRUE; + _context->dirty_gl_viewport = TRUE; _context->path_nodes = g_array_new (FALSE, FALSE, sizeof (CoglPathNode)); _context->last_path = 0; diff --git a/cogl/cogl-context.h b/cogl/cogl-context.h index 1d3f9ecf3..aa2045456 100644 --- a/cogl/cogl-context.h +++ b/cogl/cogl-context.h @@ -90,7 +90,7 @@ typedef struct GSList *draw_buffer_stack; CoglHandle window_buffer; gboolean dirty_bound_framebuffer; - gboolean dirty_viewport; + gboolean dirty_gl_viewport; /* Primitives */ floatVec2 path_start; diff --git a/cogl/cogl-draw-buffer.c b/cogl/cogl-draw-buffer.c index 41f0a5f91..76077627a 100644 --- a/cogl/cogl-draw-buffer.c +++ b/cogl/cogl-draw-buffer.c @@ -163,7 +163,7 @@ _cogl_draw_buffer_set_viewport (CoglHandle handle, draw_buffer->viewport_height = height; if (_cogl_get_draw_buffer () == draw_buffer) - ctx->dirty_viewport = TRUE; + ctx->dirty_gl_viewport = TRUE; } int @@ -377,6 +377,26 @@ _cogl_onscreen_free (CoglOnscreen *onscreen) g_free (onscreen); } +void +_cogl_onscreen_clutter_backend_set_size (int width, int height) +{ + CoglDrawBuffer *draw_buffer; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + draw_buffer = COGL_DRAW_BUFFER (ctx->window_buffer); + + if (draw_buffer->width == width && draw_buffer->height == height) + return; + + draw_buffer->width = width; + draw_buffer->height = height; + + /* We'll need to recalculate the GL viewport state derived + * from the Cogl viewport */ + ctx->dirty_gl_viewport = 1; +} + GSList * _cogl_create_draw_buffer_stack (void) { @@ -434,7 +454,7 @@ cogl_set_draw_buffer (CoglBufferTarget target, CoglHandle handle) entry->target = target; ctx->dirty_bound_framebuffer = 1; - ctx->dirty_viewport = 1; + ctx->dirty_gl_viewport = 1; if (draw_buffer != COGL_INVALID_HANDLE) cogl_handle_ref (draw_buffer); @@ -531,13 +551,19 @@ _cogl_draw_buffer_flush_state (CoglHandle handle, ctx->dirty_bound_framebuffer = FALSE; } - if (ctx->dirty_viewport) + if (ctx->dirty_gl_viewport) { + /* 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.) */ + int gl_viewport_y = draw_buffer->height - + (draw_buffer->viewport_y + draw_buffer->viewport_height); + GE (glViewport (draw_buffer->viewport_x, - draw_buffer->viewport_y, + gl_viewport_y, draw_buffer->viewport_width, draw_buffer->viewport_height)); - ctx->dirty_viewport = FALSE; + ctx->dirty_gl_viewport = FALSE; } /* XXX: Flushing clip state may trash the modelview and projection diff --git a/cogl/cogl.h.in b/cogl/cogl.h.in index 23e417a29..9ce95ba35 100644 --- a/cogl/cogl.h.in +++ b/cogl/cogl.h.in @@ -869,6 +869,9 @@ void cogl_flush_gl_state (int flags); void _cogl_set_indirect_context (gboolean indirect); void _cogl_set_viewport (int x, int y, int width, int height); +void +_cogl_onscreen_clutter_backend_set_size (int width, int height); + G_END_DECLS #undef __COGL_H_INSIDE__ From e4fe65552195ffd0e91c9a357731554b177acb03 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Wed, 21 Oct 2009 23:20:44 +0100 Subject: [PATCH 08/24] [draw-buffer] Adds cogl_draw_buffer_get_{width,height} API Simply adds missing API to query the width and height of any Cogl draw buffer. --- cogl/cogl-draw-buffer-private.h | 4 ++++ cogl/cogl-draw-buffer.c | 14 ++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/cogl/cogl-draw-buffer-private.h b/cogl/cogl-draw-buffer-private.h index 5ad4fc896..3490cd26c 100644 --- a/cogl/cogl-draw-buffer-private.h +++ b/cogl/cogl-draw-buffer-private.h @@ -76,6 +76,10 @@ typedef struct _CoglOnscreen void _cogl_draw_buffer_state_init (void); +int +_cogl_draw_buffer_get_width (CoglHandle handle); +int +_cogl_draw_buffer_get_height (CoglHandle handle); CoglClipStackState * _cogl_draw_buffer_get_clip_state (CoglHandle handle); void diff --git a/cogl/cogl-draw-buffer.c b/cogl/cogl-draw-buffer.c index 76077627a..83ad3e313 100644 --- a/cogl/cogl-draw-buffer.c +++ b/cogl/cogl-draw-buffer.c @@ -130,6 +130,20 @@ _cogl_draw_buffer_free (CoglDrawBuffer *draw_buffer) draw_buffer->projection_stack = NULL; } +int +_cogl_draw_buffer_get_width (CoglHandle handle) +{ + CoglDrawBuffer *draw_buffer = COGL_DRAW_BUFFER (handle); + return draw_buffer->width; +} + +int +_cogl_draw_buffer_get_height (CoglHandle handle) +{ + CoglDrawBuffer *draw_buffer = COGL_DRAW_BUFFER (handle); + return draw_buffer->height; +} + CoglClipStackState * _cogl_draw_buffer_get_clip_state (CoglHandle handle) { From c2a982cf9c7ccd2c2b110c464d86881b5b1689d1 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Wed, 21 Oct 2009 23:24:28 +0100 Subject: [PATCH 09/24] [cogl_read_pixels] use buffer not viewport height to calculate y offset glReadPixel takes window coordinates not viewport coordinates so we shouldn't have been assuming that the viewport height == window height. --- cogl/cogl.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/cogl/cogl.c b/cogl/cogl.c index 8fdb36ff8..e2fd88d88 100644 --- a/cogl/cogl.c +++ b/cogl/cogl.c @@ -793,10 +793,9 @@ cogl_read_pixels (int x, CoglPixelFormat format, guint8 *pixels) { - int viewport_height; + int draw_buffer_height; int rowstride = width * 4; guint8 *temprow; - CoglHandle draw_buffer; _COGL_GET_CONTEXT (ctx, NO_RETVAL); @@ -805,12 +804,11 @@ cogl_read_pixels (int x, temprow = g_alloca (rowstride * sizeof (guint8)); - draw_buffer = _cogl_get_draw_buffer (); - viewport_height = _cogl_draw_buffer_get_viewport_height (draw_buffer); + draw_buffer_height = _cogl_draw_buffer_get_height (_cogl_get_draw_buffer ()); /* The y co-ordinate should be given in OpenGL's coordinate system so 0 is the bottom row */ - y = viewport_height - y - height; + y = draw_buffer_height - y - height; /* Setup the pixel store parameters that may have been changed by Cogl */ From 78ee3f71b2daad28fd686b5dfa9f056b65bd4238 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Tue, 3 Nov 2009 13:26:58 +0000 Subject: [PATCH 10/24] [cogl] deprecate cogl_viewport() in favour of cogl_set_viewport() cogl_viewport only accepted a viewport width and height, but there are times when it's also desireable to have a viewport offset so that a scene can be translated after projection but before hitting the framebuffer. --- cogl/cogl.c | 12 ++++++------ cogl/cogl.h.in | 22 +++++++++++++++++++++- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/cogl/cogl.c b/cogl/cogl.c index e2fd88d88..3e1f69c12 100644 --- a/cogl/cogl.c +++ b/cogl/cogl.c @@ -538,10 +538,10 @@ _cogl_disable_clip_planes (void) } void -_cogl_set_viewport (int x, - int y, - int width, - int height) +cogl_set_viewport (int x, + int y, + int width, + int height) { CoglHandle draw_buffer; @@ -562,7 +562,7 @@ void cogl_viewport (guint width, guint height) { - _cogl_set_viewport (0, 0, width, height); + cogl_set_viewport (0, 0, width, height); } void @@ -579,7 +579,7 @@ _cogl_setup_viewport (guint width, _COGL_GET_CONTEXT (ctx, NO_RETVAL); - cogl_viewport (width, height); + cogl_set_viewport (0, 0, width, height); /* For Ortho projection. * _cogl_matrix_stack_ortho (projection_stack, 0, width, 0, height, -1, 1); diff --git a/cogl/cogl.h.in b/cogl/cogl.h.in index 9ce95ba35..03ab052d9 100644 --- a/cogl/cogl.h.in +++ b/cogl/cogl.h.in @@ -219,6 +219,8 @@ void _cogl_setup_viewport (guint width, float z_near, float z_far); +#ifndef COGL_DISABLE_DEPRECATED + /** * cogl_viewport: * @width: Width of the viewport @@ -229,7 +231,25 @@ void _cogl_setup_viewport (guint width, * Since: 0.8.2 */ void cogl_viewport (guint width, - guint height); + guint height) G_GNUC_DEPRECATED; + +#endif + +/** + * cogl_set_viewport: + * @x: viewport X offset + * @x: viewport Y offset + * @width: Width of the viewport + * @height: Height of the viewport + * + * Replace the current viewport with the given values. + * + * Since: 1.2 + */ +void cogl_set_viewport (int x, + int y, + int width, + int height); /** * cogl_push_matrix: From 0f86470eefcf77794180e638d5f098b01bbfb020 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Wed, 21 Oct 2009 23:22:45 +0100 Subject: [PATCH 11/24] [clip-stack] tidy up transform_point() code I was originally expecting the code not to handle offset viewports or viewports with a different size to the framebuffer, but it turns out the code worked fine. In the process though I think I made the code slightly more readable. --- cogl/cogl-clip-stack.c | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/cogl/cogl-clip-stack.c b/cogl/cogl-clip-stack.c index e9a7571cc..cac156fa6 100644 --- a/cogl/cogl-clip-stack.c +++ b/cogl/cogl-clip-stack.c @@ -155,13 +155,18 @@ cogl_clip_push_window_rect (float x_offset, clip_state->stack_dirty = TRUE; } -/* Scale from OpenGL <-1,1> coordinates system to window coordinates - * <0,window-size> with (0,0) being top left. */ -#define VIEWPORT_SCALE_X(x, w, width, origin) \ - ((((((x) / (w)) + 1.0) / 2) * (width)) + (origin)) -#define VIEWPORT_SCALE_Y(y, w, height, origin) \ - ((height) - (((((y) / (w)) + 1.0) / 2) * (height)) + (origin)) +/* Scale from OpenGL normalized device coordinates (ranging from -1 to 1) + * to Cogl window/draw-buffer coordinates (ranging from 0 to buffer-size) with + * (0,0) being top left. */ +#define VIEWPORT_TRANSFORM_X(x, vp_origin_x, vp_width) \ + ( ( ((x) + 1.0) * ((vp_width) / 2.0) ) + (vp_origin_x) ) +/* Note: for Y we first flip all coordinates around the X axis while in + * normalized device coodinates */ +#define VIEWPORT_TRANSFORM_Y(y, vp_origin_y, vp_height) \ + ( ( ((-(y)) + 1.0) * ((vp_height) / 2.0) ) + (vp_origin_y) ) +/* Transform a homogeneous vertex position from model space to Cogl + * window coordinates (with 0,0 being top left) */ static void transform_point (CoglMatrix *matrix_mv, CoglMatrix *matrix_p, @@ -172,14 +177,19 @@ transform_point (CoglMatrix *matrix_mv, float z = 0; float w = 1; - /* Apply the model view matrix */ + /* Apply the modelview matrix transform */ cogl_matrix_transform_point (matrix_mv, x, y, &z, &w); - /* Apply the projection matrix */ + /* Apply the projection matrix transform */ cogl_matrix_transform_point (matrix_p, x, y, &z, &w); + + /* Perform perspective division */ + *x /= w; + *y /= w; + /* Apply viewport transform */ - *x = VIEWPORT_SCALE_X (*x, w, viewport[2], viewport[0]); - *y = VIEWPORT_SCALE_Y (*y, w, viewport[3], viewport[1]); + *x = VIEWPORT_TRANSFORM_X (*x, viewport[0], viewport[2]); + *y = VIEWPORT_TRANSFORM_Y (*y, viewport[1], viewport[3]); } #undef VIEWPORT_SCALE_X From 800d18bc196e97d7fb606efe2adc69424effccb4 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Thu, 22 Oct 2009 16:55:07 +0100 Subject: [PATCH 12/24] [read-pixels] don't flip data when reading from offscreen draw buffers Since we do all offscreen rendering upside down (so that we can have the origin for texture coordinates be the top left of textures for the cases where offscreen draw buffers are bound to textures) we don't need to flip data read back from an offscreen framebuffer before we we return it to the user. --- cogl/cogl.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/cogl/cogl.c b/cogl/cogl.c index 3e1f69c12..d9effc441 100644 --- a/cogl/cogl.c +++ b/cogl/cogl.c @@ -793,6 +793,7 @@ cogl_read_pixels (int x, CoglPixelFormat format, guint8 *pixels) { + CoglHandle draw_buffer; int draw_buffer_height; int rowstride = width * 4; guint8 *temprow; @@ -804,7 +805,8 @@ cogl_read_pixels (int x, temprow = g_alloca (rowstride * sizeof (guint8)); - draw_buffer_height = _cogl_draw_buffer_get_height (_cogl_get_draw_buffer ()); + draw_buffer = _cogl_get_draw_buffer (); + draw_buffer_height = _cogl_draw_buffer_get_height (draw_buffer); /* The y co-ordinate should be given in OpenGL's coordinate system so 0 is the bottom row */ @@ -825,6 +827,11 @@ cogl_read_pixels (int x, glReadPixels (x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + /* NB: All offscreen rendering is done upside down so there is no need + * to flip in this case... */ + if (cogl_is_offscreen (draw_buffer)) + return; + /* TODO: consider using the GL_MESA_pack_invert extension in the future * to avoid this flip... */ From b8fe310b6d3b13d00d8380c28f24c1ecf59c8616 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Mon, 2 Nov 2009 01:12:10 +0000 Subject: [PATCH 13/24] [cogl_read_pixels] fixes for calculating the y offset when rendering offscreen Since offscreen rendering is forced to be upside down we don't need to do any conversion of the users coordinates to go from Cogl window coordinates to OpenGL window coordinates. --- cogl/cogl.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/cogl/cogl.c b/cogl/cogl.c index d9effc441..c34205290 100644 --- a/cogl/cogl.c +++ b/cogl/cogl.c @@ -809,8 +809,13 @@ cogl_read_pixels (int x, draw_buffer_height = _cogl_draw_buffer_get_height (draw_buffer); /* The y co-ordinate should be given in OpenGL's coordinate system - so 0 is the bottom row */ - y = draw_buffer_height - y - height; + * so 0 is the bottom row + * + * NB: all offscreen rendering is done upside down so no conversion + * is necissary in this case. + */ + if (!cogl_is_offscreen (draw_buffer)) + y = draw_buffer_height - y - height; /* Setup the pixel store parameters that may have been changed by Cogl */ From d1de286109a66797a0b3badd4fbb3064a5b1335f Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Mon, 2 Nov 2009 01:10:02 +0000 Subject: [PATCH 14/24] [cogl_read_pixels] ensure we flush the current draw buffer state before reading Make sure we call _cogl_draw_buffer_flush_state() before glReadPixels() to be sure we have bound the correct framebuffer. --- cogl/cogl.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cogl/cogl.c b/cogl/cogl.c index c34205290..1b2e9c46a 100644 --- a/cogl/cogl.c +++ b/cogl/cogl.c @@ -806,6 +806,9 @@ cogl_read_pixels (int x, temprow = g_alloca (rowstride * sizeof (guint8)); draw_buffer = _cogl_get_draw_buffer (); + + _cogl_draw_buffer_flush_state (draw_buffer, 0); + draw_buffer_height = _cogl_draw_buffer_get_height (draw_buffer); /* The y co-ordinate should be given in OpenGL's coordinate system From f59180d073bf1a4e975367860b1f0030dcef3476 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Mon, 2 Nov 2009 01:11:21 +0000 Subject: [PATCH 15/24] [cogl_read_pixels] call cogl_flush() before changing glPixelStore state We were previously calling cogl_flush() after setting up the glPixelStore state for calling glReadPixels, but flushing the journal could itself change the glPixelStore state. --- cogl/cogl.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cogl/cogl.c b/cogl/cogl.c index 1b2e9c46a..21e5572e8 100644 --- a/cogl/cogl.c +++ b/cogl/cogl.c @@ -820,6 +820,10 @@ cogl_read_pixels (int x, if (!cogl_is_offscreen (draw_buffer)) y = draw_buffer_height - y - height; + /* make sure any batched primitives get emitted to the GL driver before + * issuing our read pixels... */ + cogl_flush (); + /* Setup the pixel store parameters that may have been changed by Cogl */ glPixelStorei (GL_PACK_ALIGNMENT, 4); @@ -829,10 +833,6 @@ cogl_read_pixels (int x, glPixelStorei (GL_PACK_SKIP_ROWS, 0); #endif /* HAVE_COGL_GL */ - /* make sure any batched primitives get emitted to the GL driver before - * issuing our read pixels... */ - cogl_flush (); - glReadPixels (x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); /* NB: All offscreen rendering is done upside down so there is no need From 1acf5cc36f9ca8c92ea12f3f6d0e62c8e159804b Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Tue, 3 Nov 2009 12:54:45 +0000 Subject: [PATCH 16/24] [cogl_read_pixels] don't force a 4 byte pack alignment Technically this change shouldn't make a difference since we are calling glReadPixels with GL_RGBA GL_UNSIGNED_BYTE which is a 4 byte format and it should always result in the same value according to how OpenGL calculates the location of sequential rows. i.e. k = a/s * ceil(snl/a) where: a = alignment s = component size (1) n = number of components per pixel (4) l = number of pixels in a row gives: k = 4/1 * ceil(4l/4) and k = 1/1 * ceil(4l/1) which are equivalent I'm changing it because I've seen i915 driver code that bails out of hardware accelerated paths if the alignment isn't 1, and because conceptually we have no alignment constraints here so even if the current value has no effect, when we start reading back other formats it may upset things. --- cogl/cogl.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cogl/cogl.c b/cogl/cogl.c index 21e5572e8..b8b51b375 100644 --- a/cogl/cogl.c +++ b/cogl/cogl.c @@ -826,14 +826,14 @@ cogl_read_pixels (int x, /* Setup the pixel store parameters that may have been changed by Cogl */ - glPixelStorei (GL_PACK_ALIGNMENT, 4); + GE (glPixelStorei (GL_PACK_ALIGNMENT, 1)); #ifdef HAVE_COGL_GL - glPixelStorei (GL_PACK_ROW_LENGTH, 0); - glPixelStorei (GL_PACK_SKIP_PIXELS, 0); - glPixelStorei (GL_PACK_SKIP_ROWS, 0); + GE (glPixelStorei (GL_PACK_ROW_LENGTH, 0)); + GE (glPixelStorei (GL_PACK_SKIP_PIXELS, 0)); + GE (glPixelStorei (GL_PACK_SKIP_ROWS, 0)); #endif /* HAVE_COGL_GL */ - glReadPixels (x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + GE (glReadPixels (x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels)); /* NB: All offscreen rendering is done upside down so there is no need * to flip in this case... */ From 181bf920868847d523438e69cfb36c8975d5ccae Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Thu, 22 Oct 2009 19:01:52 +0100 Subject: [PATCH 17/24] [cogl] Use clockwise face winding for offscreen buffers with culling enabled Because Cogl defines the origin for texture as top left and offscreen draw buffers can be used to render to textures, we (internally) force all offscreen rendering to be upside down. (because OpenGL defines the origin to be bottom left) By forcing the users scene to be rendered upside down though we also reverse the winding order of all the drawn triangles which may interfere with the users use of backface culling. This patch ensures that we reverse the winding order for a front face (if culling is in use) while rendering offscreen so we don't conflict with the users back face culling. --- cogl/cogl-context.c | 2 ++ cogl/cogl-context.h | 1 + cogl/cogl-internal.h | 9 +++++++++ cogl/cogl-journal.c | 1 + cogl/cogl-primitives.c | 1 + cogl/cogl-vertex-buffer.c | 1 + cogl/cogl.c | 33 +++++++++++++++++++++++++++++++++ 7 files changed, 48 insertions(+) diff --git a/cogl/cogl-context.c b/cogl/cogl-context.c index d50ec6cf9..34c65d08c 100644 --- a/cogl/cogl-context.c +++ b/cogl/cogl-context.c @@ -68,6 +68,7 @@ cogl_create_context (void) _context->color_alpha = 0; _context->enable_backface_culling = FALSE; + _context->flushed_front_winding = COGL_FRONT_WINDING_COUNTER_CLOCKWISE; _context->indirect = gl_is_indirect; @@ -141,6 +142,7 @@ cogl_create_context (void) enable_flags = _cogl_material_get_cogl_enable_flags (_context->source_material); cogl_enable (enable_flags); + _cogl_flush_face_winding (); return TRUE; } diff --git a/cogl/cogl-context.h b/cogl/cogl-context.h index aa2045456..9d2aa077a 100644 --- a/cogl/cogl-context.h +++ b/cogl/cogl-context.h @@ -49,6 +49,7 @@ typedef struct guint8 color_alpha; gboolean enable_backface_culling; + CoglFrontWinding flushed_front_winding; gboolean indirect; diff --git a/cogl/cogl-internal.h b/cogl/cogl-internal.h index 1f84e0d05..a920a9785 100644 --- a/cogl/cogl-internal.h +++ b/cogl/cogl-internal.h @@ -34,6 +34,12 @@ typedef enum COGL_MATRIX_TEXTURE } CoglMatrixMode; +typedef enum +{ + COGL_FRONT_WINDING_CLOCKWISE, + COGL_FRONT_WINDING_COUNTER_CLOCKWISE +} CoglFrontWinding; + #ifdef HAVE_COGL_GLES2 typedef enum { COGL_BOXED_NONE, @@ -105,4 +111,7 @@ _cogl_destroy_texture_units (void); void _cogl_flush_matrix_stacks (void); +void +_cogl_flush_face_winding (void); + #endif /* __COGL_INTERNAL_H */ diff --git a/cogl/cogl-journal.c b/cogl/cogl-journal.c index a50bf8e42..bfa92c6be 100644 --- a/cogl/cogl-journal.c +++ b/cogl/cogl-journal.c @@ -302,6 +302,7 @@ _cogl_journal_flush_material_and_entries (CoglJournalEntry *batch_start, enable_flags |= COGL_ENABLE_VERTEX_ARRAY; enable_flags |= COGL_ENABLE_COLOR_ARRAY; cogl_enable (enable_flags); + _cogl_flush_face_winding (); /* If we haven't transformed the quads in software then we need to also break * up batches according to changes in the modelview matrix... */ diff --git a/cogl/cogl-primitives.c b/cogl/cogl-primitives.c index 45ec90ce6..af32264d7 100644 --- a/cogl/cogl-primitives.c +++ b/cogl/cogl-primitives.c @@ -979,6 +979,7 @@ cogl_polygon (CoglTextureVertex *vertices, } cogl_enable (enable_flags); + _cogl_flush_face_winding (); GE (glVertexPointer (3, GL_FLOAT, stride_bytes, v)); diff --git a/cogl/cogl-vertex-buffer.c b/cogl/cogl-vertex-buffer.c index 357d1e6e5..2ded6eeb4 100644 --- a/cogl/cogl-vertex-buffer.c +++ b/cogl/cogl-vertex-buffer.c @@ -1683,6 +1683,7 @@ enable_state_for_drawing_buffer (CoglVertexBuffer *buffer) enable_flags |= COGL_ENABLE_BACKFACE_CULLING; cogl_enable (enable_flags); + _cogl_flush_face_winding (); } static void diff --git a/cogl/cogl.c b/cogl/cogl.c index b8b51b375..18a51fa90 100644 --- a/cogl/cogl.c +++ b/cogl/cogl.c @@ -284,6 +284,38 @@ cogl_get_backface_culling_enabled (void) return ctx->enable_backface_culling; } +void +_cogl_flush_face_winding (void) +{ + CoglFrontWinding winding; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* The front face winding doesn't matter if we aren't performing any + * backface culling... */ + if (!ctx->enable_backface_culling) + return; + + /* NB: We use a clockwise face winding order when drawing offscreen because + * all offscreen rendering is done upside down resulting in reversed winding + * for all triangles. + */ + if (cogl_is_offscreen (_cogl_get_draw_buffer ())) + winding = COGL_FRONT_WINDING_CLOCKWISE; + else + winding = COGL_FRONT_WINDING_COUNTER_CLOCKWISE; + + if (winding != ctx->flushed_front_winding) + { + + if (winding == COGL_FRONT_WINDING_CLOCKWISE) + GE (glFrontFace (GL_CW)); + else + GE (glFrontFace (GL_CCW)); + ctx->flushed_front_winding = winding; + } +} + void cogl_set_source_color (const CoglColor *color) { @@ -909,6 +941,7 @@ cogl_begin_gl (void) enable_flags |= COGL_ENABLE_BACKFACE_CULLING; cogl_enable (enable_flags); + _cogl_flush_face_winding (); /* Disable all client texture coordinate arrays */ for (i = 0; i < ctx->n_texcoord_arrays_enabled; i++) From 8ca17f52627f6d6191973352ee5c2624bc0b93bf Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Thu, 22 Oct 2009 19:09:20 +0100 Subject: [PATCH 18/24] [cogl] avoid any state changes when cogl_set_backface_culling_enable is a nop This is a simple optimization to bail out of cogl_set_backface_culling_enable if it's not resulting in a change of state. --- cogl/cogl.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cogl/cogl.c b/cogl/cogl.c index 18a51fa90..b1cef8b5b 100644 --- a/cogl/cogl.c +++ b/cogl/cogl.c @@ -270,6 +270,9 @@ cogl_set_backface_culling_enabled (gboolean setting) { _COGL_GET_CONTEXT (ctx, NO_RETVAL); + if (ctx->enable_backface_culling == setting) + return; + /* Currently the journal can't track changes to backface culling state... */ _cogl_journal_flush (); From 0a1db7c4d878b35dc5b3986b6392643f067b6ffa Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Mon, 26 Oct 2009 08:23:21 +0000 Subject: [PATCH 19/24] [cogl-matrix] Import Mesa's matrix manipulation code This pulls in code from Mesa to improve our matrix manipulation support. It includes support for calculating the inverse of matrices based on top of a matrix categorizing system that allows optimizing certain matrix types. (the main thing we were after) but also adds some optimisations for rotations. Changes compared to the original code from Mesa: - Coding style is consistent with the rest of Cogl - Instead of allocating matrix->m and matrix->inv using malloc, our public CoglMatrix typedef is large enough to directly contain the matrix, its inverse, a type and a set of flags. - Instead of having a _math_matrix_analyse which updates the type, flags and inverse, we have _math_matrix_update_inverse which essentially does the same thing (internally making use of _math_matrix_update_type_and_flags()) but with additional guards in place to bail out when the inverse matrix is still valid. - When initializing a matrix with the identity matrix we don't immediately initialize the inverse matrix; rather we just set the dirty flag for the inverse (since it's likely the user won't request the inverse of the identity matrix) --- cogl/Makefile.am | 2 + cogl/cogl-matrix-mesa.c | 1698 +++++++++++++++++++++++++++++++++++++++ cogl/cogl-matrix-mesa.h | 226 ++++++ cogl/cogl-matrix.c | 39 +- cogl/cogl-matrix.h | 6 +- 5 files changed, 1967 insertions(+), 4 deletions(-) create mode 100644 cogl/cogl-matrix-mesa.c create mode 100644 cogl/cogl-matrix-mesa.h diff --git a/cogl/Makefile.am b/cogl/Makefile.am index 8988f8d35..4e0c2a1e7 100644 --- a/cogl/Makefile.am +++ b/cogl/Makefile.am @@ -122,6 +122,8 @@ libclutter_cogl_la_SOURCES = \ $(srcdir)/cogl-journal.c \ $(srcdir)/cogl-draw-buffer-private.h \ $(srcdir)/cogl-draw-buffer.c \ + $(srcdir)/cogl-matrix-mesa.h \ + $(srcdir)/cogl-matrix-mesa.c \ $(BUILT_SOURCES) \ $(NULL) diff --git a/cogl/cogl-matrix-mesa.c b/cogl/cogl-matrix-mesa.c new file mode 100644 index 000000000..0057180bd --- /dev/null +++ b/cogl/cogl-matrix-mesa.c @@ -0,0 +1,1698 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 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. + */ +/* + * Copyright (C) 1999-2005 Brian Paul All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +/** + * \file cogl-matrix-mesa.c + * Matrix operations. + * + * \note + * -# 4x4 transformation matrices are stored in memory in column major order. + * -# Points/vertices are to be thought of as column vectors. + * -# Transformation of a point p by a matrix M is: p' = M * p + */ + +/* + * Changes compared to the original code from Mesa: + * + * - instead of allocating matrix->m and matrix->inv using malloc, our + * public CoglMatrix typedef is large enough to directly contain the + * matrix, its inverse, a type and a set of flags. + * - instead of having a _math_matrix_analyse which updates the type, + * flags and inverse, we have _math_matrix_update_inverse which + * essentially does the same thing (internally making use of + * _math_matrix_update_type_and_flags()) but with additional guards in + * place to bail out when the inverse matrix is still valid. + * - when initializing a matrix with the identity matrix we don't + * immediately initialize the inverse matrix; rather we just set the + * dirty flag for the inverse (since it's likely the user won't request + * the inverse of the identity matrix) + */ + +#include "cogl-matrix-mesa.h" + +#include +#include + + +#define DEG2RAD (G_PI/180.0) + +/** Dot product of two 2-element vectors */ +#define DOT2(A,B) ( (A)[0]*(B)[0] + (A)[1]*(B)[1] ) + +/** Dot product of two 3-element vectors */ +#define DOT3(A,B) ( (A)[0]*(B)[0] + (A)[1]*(B)[1] + (A)[2]*(B)[2] ) + +#define CROSS3(N, U, V) \ +do { \ + (N)[0] = (U)[1]*(V)[2] - (U)[2]*(V)[1]; \ + (N)[1] = (U)[2]*(V)[0] - (U)[0]*(V)[2]; \ + (N)[2] = (U)[0]*(V)[1] - (U)[1]*(V)[0]; \ +} while (0) + +#define SUB_3V(DST, SRCA, SRCB) \ +do { \ + (DST)[0] = (SRCA)[0] - (SRCB)[0]; \ + (DST)[1] = (SRCA)[1] - (SRCB)[1]; \ + (DST)[2] = (SRCA)[2] - (SRCB)[2]; \ +} while (0) + +#define LEN_SQUARED_3FV( V ) ((V)[0]*(V)[0]+(V)[1]*(V)[1]+(V)[2]*(V)[2]) + +/** + * \defgroup MatFlags MAT_FLAG_XXX-flags + * + * Bitmasks to indicate different kinds of 4x4 matrices in CoglMatrix::flags + */ +/*@{*/ +#define MAT_FLAG_IDENTITY 0 /**< is an identity matrix flag. + * (Not actually used - the identity + * matrix is identified by the absense + * of all other flags.) + */ +#define MAT_FLAG_GENERAL 0x1 /**< is a general matrix flag */ +#define MAT_FLAG_ROTATION 0x2 /**< is a rotation matrix flag */ +#define MAT_FLAG_TRANSLATION 0x4 /**< is a translation matrix flag */ +#define MAT_FLAG_UNIFORM_SCALE 0x8 /**< is an uniform scaling matrix flag */ +#define MAT_FLAG_GENERAL_SCALE 0x10 /**< is a general scaling matrix flag */ +#define MAT_FLAG_GENERAL_3D 0x20 /**< general 3D matrix flag */ +#define MAT_FLAG_PERSPECTIVE 0x40 /**< is a perspective proj matrix flag */ +#define MAT_FLAG_SINGULAR 0x80 /**< is a singular matrix flag */ +#define MAT_DIRTY_TYPE 0x100 /**< matrix type is dirty */ +#define MAT_DIRTY_FLAGS 0x200 /**< matrix flags are dirty */ +#define MAT_DIRTY_INVERSE 0x400 /**< matrix inverse is dirty */ + +/** angle preserving matrix flags mask */ +#define MAT_FLAGS_ANGLE_PRESERVING (MAT_FLAG_ROTATION | \ + MAT_FLAG_TRANSLATION | \ + MAT_FLAG_UNIFORM_SCALE) + +/** geometry related matrix flags mask */ +#define MAT_FLAGS_GEOMETRY (MAT_FLAG_GENERAL | \ + MAT_FLAG_ROTATION | \ + MAT_FLAG_TRANSLATION | \ + MAT_FLAG_UNIFORM_SCALE | \ + MAT_FLAG_GENERAL_SCALE | \ + MAT_FLAG_GENERAL_3D | \ + MAT_FLAG_PERSPECTIVE | \ + MAT_FLAG_SINGULAR) + +/** length preserving matrix flags mask */ +#define MAT_FLAGS_LENGTH_PRESERVING (MAT_FLAG_ROTATION | \ + MAT_FLAG_TRANSLATION) + + +/** 3D (non-perspective) matrix flags mask */ +#define MAT_FLAGS_3D (MAT_FLAG_ROTATION | \ + MAT_FLAG_TRANSLATION | \ + MAT_FLAG_UNIFORM_SCALE | \ + MAT_FLAG_GENERAL_SCALE | \ + MAT_FLAG_GENERAL_3D) + +/** dirty matrix flags mask */ +#define MAT_DIRTY_ALL (MAT_DIRTY_TYPE | \ + MAT_DIRTY_FLAGS | \ + MAT_DIRTY_INVERSE) + +/*@}*/ + + +/** + * Test geometry related matrix flags. + * + * \param mat a pointer to a CoglMatrix structure. + * \param a flags mask. + * + * \returns non-zero if all geometry related matrix flags are contained within + * the mask, or zero otherwise. + */ +#define TEST_MAT_FLAGS(mat, a) \ + ((MAT_FLAGS_GEOMETRY & (~(a)) & ((mat)->flags) ) == 0) + + + +/** + * Names of the corresponding CoglMatrixType values. + */ +static const char *types[] = { + "COGL_MATRIX_TYPE_GENERAL", + "COGL_MATRIX_TYPE_IDENTITY", + "COGL_MATRIX_TYPE_3D_NO_ROT", + "COGL_MATRIX_TYPE_PERSPECTIVE", + "COGL_MATRIX_TYPE_2D", + "COGL_MATRIX_TYPE_2D_NO_ROT", + "COGL_MATRIX_TYPE_3D" +}; + + +/** + * Identity matrix. + */ +static float identity[16] = { + 1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0 +}; + + + +/**********************************************************************/ +/** \name Matrix multiplication */ +/*@{*/ + +#define A(row,col) a[(col<<2)+row] +#define B(row,col) b[(col<<2)+row] +#define R(row,col) result[(col<<2)+row] + +/** + * Perform a full 4x4 matrix multiplication. + * + * \param a matrix. + * \param b matrix. + * \param product will receive the product of \p a and \p b. + * + * \warning Is assumed that \p product != \p b. \p product == \p a is allowed. + * + * \note KW: 4*16 = 64 multiplications + * + * \author This \c matmul was contributed by Thomas Malik + */ +static void +matrix_multiply4x4 (float *result, const float *a, const float *b) +{ + int i; + for (i = 0; i < 4; i++) + { + const float ai0 = A(i,0), ai1=A(i,1), ai2=A(i,2), ai3=A(i,3); + R(i,0) = ai0 * B(0,0) + ai1 * B(1,0) + ai2 * B(2,0) + ai3 * B(3,0); + R(i,1) = ai0 * B(0,1) + ai1 * B(1,1) + ai2 * B(2,1) + ai3 * B(3,1); + R(i,2) = ai0 * B(0,2) + ai1 * B(1,2) + ai2 * B(2,2) + ai3 * B(3,2); + R(i,3) = ai0 * B(0,3) + ai1 * B(1,3) + ai2 * B(2,3) + ai3 * B(3,3); + } +} + +/** + * Multiply two matrices known to occupy only the top three rows, such + * as typical model matrices, and orthogonal matrices. + * + * \param a matrix. + * \param b matrix. + * \param product will receive the product of \p a and \p b. + */ +static void +matrix_multiply3x4 (float *result, const float *a, const float *b) +{ + int i; + for (i = 0; i < 3; i++) + { + const float ai0 = A(i,0), ai1 = A(i,1), ai2 = A(i,2), ai3 = A(i,3); + R(i,0) = ai0 * B(0,0) + ai1 * B(1,0) + ai2 * B(2,0); + R(i,1) = ai0 * B(0,1) + ai1 * B(1,1) + ai2 * B(2,1); + R(i,2) = ai0 * B(0,2) + ai1 * B(1,2) + ai2 * B(2,2); + R(i,3) = ai0 * B(0,3) + ai1 * B(1,3) + ai2 * B(2,3) + ai3; + } + R(3,0) = 0; + R(3,1) = 0; + R(3,2) = 0; + R(3,3) = 1; +} + +#undef A +#undef B +#undef R + +/** + * Multiply a matrix by an array of floats with known properties. + * + * \param mat pointer to a CoglMatrix structure containing the left multiplication + * matrix, and that will receive the product result. + * \param m right multiplication matrix array. + * \param flags flags of the matrix \p m. + * + * Joins both flags and marks the type and inverse as dirty. Calls + * matrix_multiply3x4() if both matrices are 3D, or matrix_multiply4x4() + * otherwise. + */ +static void +matrix_multiply_array_with_flags (CoglMatrix *result, + const float *array, + unsigned int flags) +{ + result->flags |= (flags | MAT_DIRTY_TYPE | MAT_DIRTY_INVERSE); + + if (TEST_MAT_FLAGS (result, MAT_FLAGS_3D)) + matrix_multiply3x4 ((float *)result, (float *)result, array); + else + matrix_multiply4x4 ((float *)result, (float *)result, array); +} + +/** + * Matrix multiplication. + * + * \param dest destination matrix. + * \param a left matrix. + * \param b right matrix. + * + * Joins both flags and marks the type and inverse as dirty. Calls + * matrix_multiply3x4() if both matrices are 3D, or matrix_multiply4x4() + * otherwise. + */ +void +_math_matrix_multiply (CoglMatrix *result, + const CoglMatrix *a, + const CoglMatrix *b) +{ + result->flags = (a->flags | + b->flags | + MAT_DIRTY_TYPE | + MAT_DIRTY_INVERSE); + + if (TEST_MAT_FLAGS(result, MAT_FLAGS_3D)) + matrix_multiply3x4 ((float *)result, (float *)a, (float *)b); + else + matrix_multiply4x4 ((float *)result, (float *)a, (float *)b); +} + +/** + * Matrix multiplication. + * + * \param dest left and destination matrix. + * \param m right matrix array. + * + * Marks the matrix flags with general flag, and type and inverse dirty flags. + * Calls matrix_multiply4x4() for the multiplication. + */ +void +_math_matrix_multiply_array (CoglMatrix *result, const float *array) +{ + result->flags |= (MAT_FLAG_GENERAL | + MAT_DIRTY_TYPE | + MAT_DIRTY_INVERSE | + MAT_DIRTY_FLAGS); + + matrix_multiply4x4 ((float *)result, (float *)result, (float *)array); +} + +/*@}*/ + + +/**********************************************************************/ +/** \name Matrix output */ +/*@{*/ + +/** + * Print a matrix array. + * + * \param m matrix array. + * + * Called by _math_matrix_print() to print a matrix or its inverse. + */ +static void +print_matrix_floats (const float m[16]) +{ + int i; + for (i = 0;i < 4; i++) + g_print ("\t%f %f %f %f\n", m[i], m[4+i], m[8+i], m[12+i] ); +} + +/** + * Dumps the contents of a CoglMatrix structure. + * + * \param m pointer to the CoglMatrix structure. + */ +void +_math_matrix_print (const CoglMatrix *matrix) +{ + g_print ("Matrix type: %s, flags: %x\n", + types[matrix->type], (int)matrix->flags); + print_matrix_floats ((float *)matrix); + g_print ("Inverse: \n"); + if (!(matrix->flags & MAT_DIRTY_INVERSE)) + { + float prod[16]; + print_matrix_floats (matrix->inv); + matrix_multiply4x4 (prod, (float *)matrix, matrix->inv); + g_print ("Mat * Inverse:\n"); + print_matrix_floats (prod); + } + else + g_print (" - not available\n"); +} + +/*@}*/ + + +/** + * References an element of 4x4 matrix. + * + * \param m matrix array. + * \param c column of the desired element. + * \param r row of the desired element. + * + * \return value of the desired element. + * + * Calculate the linear storage index of the element and references it. + */ +#define MAT(m,r,c) (m)[(c)*4+(r)] + + +/**********************************************************************/ +/** \name Matrix inversion */ +/*@{*/ + +/** + * Swaps the values of two floating pointer variables. + * + * Used by invert_matrix_general() to swap the row pointers. + */ +#define SWAP_ROWS(a, b) { float *_tmp = a; (a)=(b); (b)=_tmp; } + +/** + * Compute inverse of 4x4 transformation matrix. + * + * \param mat pointer to a CoglMatrix structure. The matrix inverse will be + * stored in the CoglMatrix::inv attribute. + * + * \return TRUE for success, FALSE for failure (\p singular matrix). + * + * \author + * Code contributed by Jacques Leroy jle@star.be + * + * Calculates the inverse matrix by performing the gaussian matrix reduction + * with partial pivoting followed by back/substitution with the loops manually + * unrolled. + */ +static gboolean +invert_matrix_general (CoglMatrix *matrix) +{ + const float *m = (float *)matrix; + float *out = matrix->inv; + float wtmp[4][8]; + float m0, m1, m2, m3, s; + float *r0, *r1, *r2, *r3; + + r0 = wtmp[0], r1 = wtmp[1], r2 = wtmp[2], r3 = wtmp[3]; + + r0[0] = MAT (m, 0, 0), r0[1] = MAT (m, 0, 1), + r0[2] = MAT (m, 0, 2), r0[3] = MAT (m, 0, 3), + r0[4] = 1.0, r0[5] = r0[6] = r0[7] = 0.0, + + r1[0] = MAT (m, 1, 0), r1[1] = MAT (m, 1, 1), + r1[2] = MAT (m, 1, 2), r1[3] = MAT (m, 1, 3), + r1[5] = 1.0, r1[4] = r1[6] = r1[7] = 0.0, + + r2[0] = MAT (m, 2, 0), r2[1] = MAT (m, 2, 1), + r2[2] = MAT (m, 2, 2), r2[3] = MAT (m, 2, 3), + r2[6] = 1.0, r2[4] = r2[5] = r2[7] = 0.0, + + r3[0] = MAT (m, 3, 0), r3[1] = MAT (m, 3, 1), + r3[2] = MAT (m, 3, 2), r3[3] = MAT (m, 3, 3), + r3[7] = 1.0, r3[4] = r3[5] = r3[6] = 0.0; + + /* choose pivot - or die */ + if (fabsf (r3[0]) > fabsf (r2[0])) + SWAP_ROWS (r3, r2); + if (fabsf (r2[0]) > fabsf (r1[0])) + SWAP_ROWS (r2, r1); + if (fabsf (r1[0]) > fabsf (r0[0])) + SWAP_ROWS (r1, r0); + if (0.0 == r0[0]) + return FALSE; + + /* eliminate first variable */ + m1 = r1[0]/r0[0]; m2 = r2[0]/r0[0]; m3 = r3[0]/r0[0]; + s = r0[1]; r1[1] -= m1 * s; r2[1] -= m2 * s; r3[1] -= m3 * s; + s = r0[2]; r1[2] -= m1 * s; r2[2] -= m2 * s; r3[2] -= m3 * s; + s = r0[3]; r1[3] -= m1 * s; r2[3] -= m2 * s; r3[3] -= m3 * s; + s = r0[4]; + if (s != 0.0) { r1[4] -= m1 * s; r2[4] -= m2 * s; r3[4] -= m3 * s; } + s = r0[5]; + if (s != 0.0) { r1[5] -= m1 * s; r2[5] -= m2 * s; r3[5] -= m3 * s; } + s = r0[6]; + if (s != 0.0) { r1[6] -= m1 * s; r2[6] -= m2 * s; r3[6] -= m3 * s; } + s = r0[7]; + if (s != 0.0) { r1[7] -= m1 * s; r2[7] -= m2 * s; r3[7] -= m3 * s; } + + /* choose pivot - or die */ + if (fabsf (r3[1]) > fabsf (r2[1])) + SWAP_ROWS (r3, r2); + if (fabsf (r2[1]) > fabsf (r1[1])) + SWAP_ROWS (r2, r1); + if (0.0 == r1[1]) + return FALSE; + + /* eliminate second variable */ + m2 = r2[1] / r1[1]; m3 = r3[1] / r1[1]; + r2[2] -= m2 * r1[2]; r3[2] -= m3 * r1[2]; + r2[3] -= m2 * r1[3]; r3[3] -= m3 * r1[3]; + s = r1[4]; if (0.0 != s) { r2[4] -= m2 * s; r3[4] -= m3 * s; } + s = r1[5]; if (0.0 != s) { r2[5] -= m2 * s; r3[5] -= m3 * s; } + s = r1[6]; if (0.0 != s) { r2[6] -= m2 * s; r3[6] -= m3 * s; } + s = r1[7]; if (0.0 != s) { r2[7] -= m2 * s; r3[7] -= m3 * s; } + + /* choose pivot - or die */ + if (fabsf (r3[2]) > fabsf (r2[2])) + SWAP_ROWS (r3, r2); + if (0.0 == r2[2]) + return FALSE; + + /* eliminate third variable */ + m3 = r3[2] / r2[2]; + r3[3] -= m3 * r2[3], r3[4] -= m3 * r2[4], + r3[5] -= m3 * r2[5], r3[6] -= m3 * r2[6], + r3[7] -= m3 * r2[7]; + + /* last check */ + if (0.0 == r3[3]) + return FALSE; + + s = 1.0f / r3[3]; /* now back substitute row 3 */ + r3[4] *= s; r3[5] *= s; r3[6] *= s; r3[7] *= s; + + m2 = r2[3]; /* now back substitute row 2 */ + s = 1.0f / r2[2]; + r2[4] = s * (r2[4] - r3[4] * m2), r2[5] = s * (r2[5] - r3[5] * m2), + r2[6] = s * (r2[6] - r3[6] * m2), r2[7] = s * (r2[7] - r3[7] * m2); + m1 = r1[3]; + r1[4] -= r3[4] * m1, r1[5] -= r3[5] * m1, + r1[6] -= r3[6] * m1, r1[7] -= r3[7] * m1; + m0 = r0[3]; + r0[4] -= r3[4] * m0, r0[5] -= r3[5] * m0, + r0[6] -= r3[6] * m0, r0[7] -= r3[7] * m0; + + m1 = r1[2]; /* now back substitute row 1 */ + s = 1.0f / r1[1]; + r1[4] = s * (r1[4] - r2[4] * m1), r1[5] = s * (r1[5] - r2[5] * m1), + r1[6] = s * (r1[6] - r2[6] * m1), r1[7] = s * (r1[7] - r2[7] * m1); + m0 = r0[2]; + r0[4] -= r2[4] * m0, r0[5] -= r2[5] * m0, + r0[6] -= r2[6] * m0, r0[7] -= r2[7] * m0; + + m0 = r0[1]; /* now back substitute row 0 */ + s = 1.0f / r0[0]; + r0[4] = s * (r0[4] - r1[4] * m0), r0[5] = s * (r0[5] - r1[5] * m0), + r0[6] = s * (r0[6] - r1[6] * m0), r0[7] = s * (r0[7] - r1[7] * m0); + + MAT (out, 0, 0) = r0[4]; MAT (out, 0, 1) = r0[5], + MAT (out, 0, 2) = r0[6]; MAT (out, 0, 3) = r0[7], + MAT (out, 1, 0) = r1[4]; MAT (out, 1, 1) = r1[5], + MAT (out, 1, 2) = r1[6]; MAT (out, 1, 3) = r1[7], + MAT (out, 2, 0) = r2[4]; MAT (out, 2, 1) = r2[5], + MAT (out, 2, 2) = r2[6]; MAT (out, 2, 3) = r2[7], + MAT (out, 3, 0) = r3[4]; MAT (out, 3, 1) = r3[5], + MAT (out, 3, 2) = r3[6]; MAT (out, 3, 3) = r3[7]; + + return TRUE; +} +#undef SWAP_ROWS + +/** + * Compute inverse of a general 3d transformation matrix. + * + * \param mat pointer to a CoglMatrix structure. The matrix inverse will be + * stored in the CoglMatrix::inv attribute. + * + * \return TRUE for success, FALSE for failure (\p singular matrix). + * + * \author Adapted from graphics gems II. + * + * Calculates the inverse of the upper left by first calculating its + * determinant and multiplying it to the symmetric adjust matrix of each + * element. Finally deals with the translation part by transforming the + * original translation vector using by the calculated submatrix inverse. + */ +static gboolean +invert_matrix_3d_general (CoglMatrix *matrix) +{ + const float *in = (float *)matrix; + float *out = matrix->inv; + float pos, neg, t; + float det; + + /* Calculate the determinant of upper left 3x3 submatrix and + * determine if the matrix is singular. + */ + pos = neg = 0.0; + t = MAT (in,0,0) * MAT (in,1,1) * MAT (in,2,2); + if (t >= 0.0) pos += t; else neg += t; + + t = MAT (in,1,0) * MAT (in,2,1) * MAT (in,0,2); + if (t >= 0.0) pos += t; else neg += t; + + t = MAT (in,2,0) * MAT (in,0,1) * MAT (in,1,2); + if (t >= 0.0) pos += t; else neg += t; + + t = -MAT (in,2,0) * MAT (in,1,1) * MAT (in,0,2); + if (t >= 0.0) pos += t; else neg += t; + + t = -MAT (in,1,0) * MAT (in,0,1) * MAT (in,2,2); + if (t >= 0.0) pos += t; else neg += t; + + t = -MAT (in,0,0) * MAT (in,2,1) * MAT (in,1,2); + if (t >= 0.0) pos += t; else neg += t; + + det = pos + neg; + + if (det*det < 1e-25) + return FALSE; + + det = 1.0f / det; + MAT (out,0,0) = + ( (MAT (in, 1, 1)*MAT (in, 2, 2) - MAT (in, 2, 1)*MAT (in, 1, 2) )*det); + MAT (out,0,1) = + (- (MAT (in, 0, 1)*MAT (in, 2, 2) - MAT (in, 2, 1)*MAT (in, 0, 2) )*det); + MAT (out,0,2) = + ( (MAT (in, 0, 1)*MAT (in, 1, 2) - MAT (in, 1, 1)*MAT (in, 0, 2) )*det); + MAT (out,1,0) = + (- (MAT (in,1,0)*MAT (in,2,2) - MAT (in,2,0)*MAT (in,1,2) )*det); + MAT (out,1,1) = + ( (MAT (in,0,0)*MAT (in,2,2) - MAT (in,2,0)*MAT (in,0,2) )*det); + MAT (out,1,2) = + (- (MAT (in,0,0)*MAT (in,1,2) - MAT (in,1,0)*MAT (in,0,2) )*det); + MAT (out,2,0) = + ( (MAT (in,1,0)*MAT (in,2,1) - MAT (in,2,0)*MAT (in,1,1) )*det); + MAT (out,2,1) = + (- (MAT (in,0,0)*MAT (in,2,1) - MAT (in,2,0)*MAT (in,0,1) )*det); + MAT (out,2,2) = + ( (MAT (in,0,0)*MAT (in,1,1) - MAT (in,1,0)*MAT (in,0,1) )*det); + + /* Do the translation part */ + MAT (out,0,3) = - (MAT (in, 0, 3) * MAT (out, 0, 0) + + MAT (in, 1, 3) * MAT (out, 0, 1) + + MAT (in, 2, 3) * MAT (out, 0, 2) ); + MAT (out,1,3) = - (MAT (in, 0, 3) * MAT (out, 1, 0) + + MAT (in, 1, 3) * MAT (out, 1, 1) + + MAT (in, 2, 3) * MAT (out, 1, 2) ); + MAT (out,2,3) = - (MAT (in, 0, 3) * MAT (out, 2 ,0) + + MAT (in, 1, 3) * MAT (out, 2, 1) + + MAT (in, 2, 3) * MAT (out, 2, 2) ); + + return TRUE; +} + +/** + * Compute inverse of a 3d transformation matrix. + * + * \param mat pointer to a CoglMatrix structure. The matrix inverse will be + * stored in the CoglMatrix::inv attribute. + * + * \return TRUE for success, FALSE for failure (\p singular matrix). + * + * If the matrix is not an angle preserving matrix then calls + * invert_matrix_3d_general for the actual calculation. Otherwise calculates + * the inverse matrix analyzing and inverting each of the scaling, rotation and + * translation parts. + */ +static gboolean +invert_matrix_3d (CoglMatrix *matrix) +{ + const float *in = (float *)matrix; + float *out = matrix->inv; + + if (!TEST_MAT_FLAGS(matrix, MAT_FLAGS_ANGLE_PRESERVING)) + return invert_matrix_3d_general (matrix); + + if (matrix->flags & MAT_FLAG_UNIFORM_SCALE) + { + float scale = (MAT (in, 0, 0) * MAT (in, 0, 0) + + MAT (in, 0, 1) * MAT (in, 0, 1) + + MAT (in, 0, 2) * MAT (in, 0, 2)); + + if (scale == 0.0) + return FALSE; + + scale = 1.0f / scale; + + /* Transpose and scale the 3 by 3 upper-left submatrix. */ + MAT (out, 0, 0) = scale * MAT (in, 0, 0); + MAT (out, 1, 0) = scale * MAT (in, 0, 1); + MAT (out, 2, 0) = scale * MAT (in, 0, 2); + MAT (out, 0, 1) = scale * MAT (in, 1, 0); + MAT (out, 1, 1) = scale * MAT (in, 1, 1); + MAT (out, 2, 1) = scale * MAT (in, 1, 2); + MAT (out, 0, 2) = scale * MAT (in, 2, 0); + MAT (out, 1, 2) = scale * MAT (in, 2, 1); + MAT (out, 2, 2) = scale * MAT (in, 2, 2); + } + else if (matrix->flags & MAT_FLAG_ROTATION) + { + /* Transpose the 3 by 3 upper-left submatrix. */ + MAT (out, 0, 0) = MAT (in, 0, 0); + MAT (out, 1, 0) = MAT (in, 0, 1); + MAT (out, 2, 0) = MAT (in, 0, 2); + MAT (out, 0, 1) = MAT (in, 1, 0); + MAT (out, 1, 1) = MAT (in, 1, 1); + MAT (out, 2, 1) = MAT (in, 1, 2); + MAT (out, 0, 2) = MAT (in, 2, 0); + MAT (out, 1, 2) = MAT (in, 2, 1); + MAT (out, 2, 2) = MAT (in, 2, 2); + } + else + { + /* pure translation */ + memcpy (out, identity, 16 * sizeof (float)); + MAT (out, 0, 3) = - MAT (in, 0, 3); + MAT (out, 1, 3) = - MAT (in, 1, 3); + MAT (out, 2, 3) = - MAT (in, 2, 3); + return TRUE; + } + + if (matrix->flags & MAT_FLAG_TRANSLATION) + { + /* Do the translation part */ + MAT (out,0,3) = - (MAT (in, 0, 3) * MAT (out, 0, 0) + + MAT (in, 1, 3) * MAT (out, 0, 1) + + MAT (in, 2, 3) * MAT (out, 0, 2) ); + MAT (out,1,3) = - (MAT (in, 0, 3) * MAT (out, 1, 0) + + MAT (in, 1, 3) * MAT (out, 1, 1) + + MAT (in, 2, 3) * MAT (out, 1, 2) ); + MAT (out,2,3) = - (MAT (in, 0, 3) * MAT (out, 2, 0) + + MAT (in, 1, 3) * MAT (out, 2, 1) + + MAT (in, 2, 3) * MAT (out, 2, 2) ); + } + else + MAT (out, 0, 3) = MAT (out, 1, 3) = MAT (out, 2, 3) = 0.0; + + return TRUE; +} + +/** + * Compute inverse of an identity transformation matrix. + * + * \param mat pointer to a CoglMatrix structure. The matrix inverse will be + * stored in the CoglMatrix::inv attribute. + * + * \return always TRUE. + * + * Simply copies identity into CoglMatrix::inv. + */ +static gboolean +invert_matrix_identity (CoglMatrix *matrix) +{ + memcpy (matrix->inv, identity, 16 * sizeof (float)); + return TRUE; +} + +/** + * Compute inverse of a no-rotation 3d transformation matrix. + * + * \param mat pointer to a CoglMatrix structure. The matrix inverse will be + * stored in the CoglMatrix::inv attribute. + * + * \return TRUE for success, FALSE for failure (\p singular matrix). + * + * Calculates the + */ +static gboolean +invert_matrix_3d_no_rotation (CoglMatrix *matrix) +{ + const float *in = (float *)matrix; + float *out = matrix->inv; + + if (MAT (in,0,0) == 0 || MAT (in,1,1) == 0 || MAT (in,2,2) == 0) + return FALSE; + + memcpy (out, identity, 16 * sizeof (float)); + MAT (out,0,0) = 1.0f / MAT (in,0,0); + MAT (out,1,1) = 1.0f / MAT (in,1,1); + MAT (out,2,2) = 1.0f / MAT (in,2,2); + + if (matrix->flags & MAT_FLAG_TRANSLATION) + { + MAT (out,0,3) = - (MAT (in,0,3) * MAT (out,0,0)); + MAT (out,1,3) = - (MAT (in,1,3) * MAT (out,1,1)); + MAT (out,2,3) = - (MAT (in,2,3) * MAT (out,2,2)); + } + + return TRUE; +} + +/** + * Compute inverse of a no-rotation 2d transformation matrix. + * + * \param mat pointer to a CoglMatrix structure. The matrix inverse will be + * stored in the CoglMatrix::inv attribute. + * + * \return TRUE for success, FALSE for failure (\p singular matrix). + * + * Calculates the inverse matrix by applying the inverse scaling and + * translation to the identity matrix. + */ +static gboolean +invert_matrix_2d_no_rotation (CoglMatrix *matrix) +{ + const float *in = (float *)matrix; + float *out = matrix->inv; + + if (MAT (in, 0, 0) == 0 || MAT (in, 1, 1) == 0) + return FALSE; + + memcpy (out, identity, 16 * sizeof (float)); + MAT (out, 0, 0) = 1.0f / MAT (in, 0, 0); + MAT (out, 1, 1) = 1.0f / MAT (in, 1, 1); + + if (matrix->flags & MAT_FLAG_TRANSLATION) + { + MAT (out, 0, 3) = - (MAT (in, 0, 3) * MAT (out, 0, 0)); + MAT (out, 1, 3) = - (MAT (in, 1, 3) * MAT (out, 1, 1)); + } + + return TRUE; +} + +#if 0 +/* broken */ +static gboolean +invert_matrix_perspective (CoglMatrix *matrix) +{ + const float *in = matrix; + float *out = matrix->inv; + + if (MAT (in,2,3) == 0) + return FALSE; + + memcpy( out, identity, 16 * sizeof(float) ); + + MAT (out, 0, 0) = 1.0f / MAT (in, 0, 0); + MAT (out, 1, 1) = 1.0f / MAT (in, 1, 1); + + MAT (out, 0, 3) = MAT (in, 0, 2); + MAT (out, 1, 3) = MAT (in, 1, 2); + + MAT (out,2,2) = 0; + MAT (out,2,3) = -1; + + MAT (out,3,2) = 1.0f / MAT (in,2,3); + MAT (out,3,3) = MAT (in,2,2) * MAT (out,3,2); + + return TRUE; +} +#endif + +/** + * Matrix inversion function pointer type. + */ +typedef gboolean (*inv_mat_func)(CoglMatrix *matrix); + +/** + * Table of the matrix inversion functions according to the matrix type. + */ +static inv_mat_func inv_mat_tab[7] = { + invert_matrix_general, + invert_matrix_identity, + invert_matrix_3d_no_rotation, +#if 0 + /* Don't use this function for now - it fails when the projection matrix + * is premultiplied by a translation (ala Chromium's tilesort SPU). + */ + invert_matrix_perspective, +#else + invert_matrix_general, +#endif + invert_matrix_3d, /* lazy! */ + invert_matrix_2d_no_rotation, + invert_matrix_3d +}; + +/** + * Compute inverse of a transformation matrix. + * + * \param mat pointer to a CoglMatrix structure. The matrix inverse will be + * stored in the CoglMatrix::inv attribute. + * + * \return TRUE for success, FALSE for failure (\p singular matrix). + * + * Calls the matrix inversion function in inv_mat_tab corresponding to the + * given matrix type. In case of failure, updates the MAT_FLAG_SINGULAR flag, + * and copies the identity matrix into CoglMatrix::inv. + */ +gboolean +_math_matrix_update_inverse (CoglMatrix *matrix) +{ + if (matrix->flags & MAT_DIRTY_FLAGS || + matrix->flags & MAT_DIRTY_INVERSE) + { + _math_matrix_update_type_and_flags (matrix); + + if (inv_mat_tab[matrix->type](matrix)) + matrix->flags &= ~MAT_FLAG_SINGULAR; + else + { + matrix->flags |= MAT_FLAG_SINGULAR; + memcpy (matrix->inv, identity, 16 * sizeof (float)); + } + + matrix->flags &= ~MAT_DIRTY_INVERSE; + } + + if (matrix->flags & MAT_FLAG_SINGULAR) + return FALSE; + else + return TRUE; +} + +/*@}*/ + + +/**********************************************************************/ +/** \name Matrix generation */ +/*@{*/ + +/** + * Generate a 4x4 transformation matrix from glRotate parameters, and + * post-multiply the input matrix by it. + * + * \author + * This function was contributed by Erich Boleyn (erich@uruk.org). + * Optimizations contributed by Rudolf Opalla (rudi@khm.de). + */ +void +_math_matrix_rotate (CoglMatrix *matrix, + float angle, + float x, + float y, + float z) +{ + float xx, yy, zz, xy, yz, zx, xs, ys, zs, one_c, s, c; + float m[16]; + gboolean optimized; + + s = sinf (angle * DEG2RAD); + c = cosf (angle * DEG2RAD); + + memcpy (m, identity, 16 * sizeof (float)); + optimized = FALSE; + +#define M(row,col) m[col*4+row] + + if (x == 0.0f) + { + if (y == 0.0f) + { + if (z != 0.0f) + { + optimized = TRUE; + /* rotate only around z-axis */ + M (0,0) = c; + M (1,1) = c; + if (z < 0.0f) + { + M (0,1) = s; + M (1,0) = -s; + } + else + { + M (0,1) = -s; + M (1,0) = s; + } + } + } + else if (z == 0.0f) + { + optimized = TRUE; + /* rotate only around y-axis */ + M (0,0) = c; + M (2,2) = c; + if (y < 0.0f) + { + M (0,2) = -s; + M (2,0) = s; + } + else + { + M (0,2) = s; + M (2,0) = -s; + } + } + } + else if (y == 0.0f) + { + if (z == 0.0f) + { + optimized = TRUE; + /* rotate only around x-axis */ + M (1,1) = c; + M (2,2) = c; + if (x < 0.0f) + { + M (1,2) = s; + M (2,1) = -s; + } + else + { + M (1,2) = -s; + M (2,1) = s; + } + } + } + + if (!optimized) + { + const float mag = sqrtf (x * x + y * y + z * z); + + if (mag <= 1.0e-4) + { + /* no rotation, leave mat as-is */ + return; + } + + x /= mag; + y /= mag; + z /= mag; + + + /* + * Arbitrary axis rotation matrix. + * + * This is composed of 5 matrices, Rz, Ry, T, Ry', Rz', multiplied + * like so: Rz * Ry * T * Ry' * Rz'. T is the final rotation + * (which is about the X-axis), and the two composite transforms + * Ry' * Rz' and Rz * Ry are (respectively) the rotations necessary + * from the arbitrary axis to the X-axis then back. They are + * all elementary rotations. + * + * Rz' is a rotation about the Z-axis, to bring the axis vector + * into the x-z plane. Then Ry' is applied, rotating about the + * Y-axis to bring the axis vector parallel with the X-axis. The + * rotation about the X-axis is then performed. Ry and Rz are + * simply the respective inverse transforms to bring the arbitrary + * axis back to it's original orientation. The first transforms + * Rz' and Ry' are considered inverses, since the data from the + * arbitrary axis gives you info on how to get to it, not how + * to get away from it, and an inverse must be applied. + * + * The basic calculation used is to recognize that the arbitrary + * axis vector (x, y, z), since it is of unit length, actually + * represents the sines and cosines of the angles to rotate the + * X-axis to the same orientation, with theta being the angle about + * Z and phi the angle about Y (in the order described above) + * as follows: + * + * cos ( theta ) = x / sqrt ( 1 - z^2 ) + * sin ( theta ) = y / sqrt ( 1 - z^2 ) + * + * cos ( phi ) = sqrt ( 1 - z^2 ) + * sin ( phi ) = z + * + * Note that cos ( phi ) can further be inserted to the above + * formulas: + * + * cos ( theta ) = x / cos ( phi ) + * sin ( theta ) = y / sin ( phi ) + * + * ...etc. Because of those relations and the standard trigonometric + * relations, it is pssible to reduce the transforms down to what + * is used below. It may be that any primary axis chosen will give the + * same results (modulo a sign convention) using thie method. + * + * Particularly nice is to notice that all divisions that might + * have caused trouble when parallel to certain planes or + * axis go away with care paid to reducing the expressions. + * After checking, it does perform correctly under all cases, since + * in all the cases of division where the denominator would have + * been zero, the numerator would have been zero as well, giving + * the expected result. + */ + + xx = x * x; + yy = y * y; + zz = z * z; + xy = x * y; + yz = y * z; + zx = z * x; + xs = x * s; + ys = y * s; + zs = z * s; + one_c = 1.0f - c; + + /* We already hold the identity-matrix so we can skip some statements */ + M (0,0) = (one_c * xx) + c; + M (0,1) = (one_c * xy) - zs; + M (0,2) = (one_c * zx) + ys; + /* M (0,3) = 0.0f; */ + + M (1,0) = (one_c * xy) + zs; + M (1,1) = (one_c * yy) + c; + M (1,2) = (one_c * yz) - xs; + /* M (1,3) = 0.0f; */ + + M (2,0) = (one_c * zx) - ys; + M (2,1) = (one_c * yz) + xs; + M (2,2) = (one_c * zz) + c; + /* M (2,3) = 0.0f; */ + + /* + M (3,0) = 0.0f; + M (3,1) = 0.0f; + M (3,2) = 0.0f; + M (3,3) = 1.0f; + */ + } +#undef M + + matrix_multiply_array_with_flags (matrix, m, MAT_FLAG_ROTATION); +} + +/** + * Apply a perspective projection matrix. + * + * \param mat matrix to apply the projection. + * \param left left clipping plane coordinate. + * \param right right clipping plane coordinate. + * \param bottom bottom clipping plane coordinate. + * \param top top clipping plane coordinate. + * \param nearval distance to the near clipping plane. + * \param farval distance to the far clipping plane. + * + * Creates the projection matrix and multiplies it with \p mat, marking the + * MAT_FLAG_PERSPECTIVE flag. + */ +void +_math_matrix_frustum (CoglMatrix *matrix, + float left, + float right, + float bottom, + float top, + float nearval, + float farval) +{ + float x, y, a, b, c, d; + float m[16]; + + x = (2.0f * nearval) / (right - left); + y = (2.0f * nearval) / (top - bottom); + a = (right + left) / (right - left); + b = (top + bottom) / (top - bottom); + c = -(farval + nearval) / ( farval - nearval); + d = -(2.0f * farval * nearval) / (farval - nearval); /* error? */ + +#define M(row,col) m[col*4+row] + M (0,0) = x; M (0,1) = 0.0f; M (0,2) = a; M (0,3) = 0.0f; + M (1,0) = 0.0f; M (1,1) = y; M (1,2) = b; M (1,3) = 0.0f; + M (2,0) = 0.0f; M (2,1) = 0.0f; M (2,2) = c; M (2,3) = d; + M (3,0) = 0.0f; M (3,1) = 0.0f; M (3,2) = -1.0f; M (3,3) = 0.0f; +#undef M + + matrix_multiply_array_with_flags (matrix, m, MAT_FLAG_PERSPECTIVE); +} + +/** + * Apply an orthographic projection matrix. + * + * \param mat matrix to apply the projection. + * \param left left clipping plane coordinate. + * \param right right clipping plane coordinate. + * \param bottom bottom clipping plane coordinate. + * \param top top clipping plane coordinate. + * \param nearval distance to the near clipping plane. + * \param farval distance to the far clipping plane. + * + * Creates the projection matrix and multiplies it with \p mat, marking the + * MAT_FLAG_GENERAL_SCALE and MAT_FLAG_TRANSLATION flags. + */ +void +_math_matrix_ortho (CoglMatrix *matrix, + float left, + float right, + float bottom, + float top, + float nearval, + float farval) +{ + float m[16]; + +#define M(row,col) m[col*4+row] + M (0,0) = 2.0f / (right-left); + M (0,1) = 0.0f; + M (0,2) = 0.0f; + M (0,3) = -(right+left) / (right-left); + + M (1,0) = 0.0f; + M (1,1) = 2.0f / (top-bottom); + M (1,2) = 0.0f; + M (1,3) = -(top+bottom) / (top-bottom); + + M (2,0) = 0.0f; + M (2,1) = 0.0f; + M (2,2) = -2.0f / (farval-nearval); + M (2,3) = -(farval+nearval) / (farval-nearval); + + M (3,0) = 0.0f; + M (3,1) = 0.0f; + M (3,2) = 0.0f; + M (3,3) = 1.0f; +#undef M + + matrix_multiply_array_with_flags (matrix, m, + (MAT_FLAG_GENERAL_SCALE | + MAT_FLAG_TRANSLATION)); +} + +/** + * Multiply a matrix with a general scaling matrix. + * + * \param mat matrix. + * \param x x axis scale factor. + * \param y y axis scale factor. + * \param z z axis scale factor. + * + * Multiplies in-place the elements of \p mat by the scale factors. Checks if + * the scales factors are roughly the same, marking the MAT_FLAG_UNIFORM_SCALE + * flag, or MAT_FLAG_GENERAL_SCALE. Marks the MAT_DIRTY_TYPE and + * MAT_DIRTY_INVERSE dirty flags. + */ +void +_math_matrix_scale (CoglMatrix *matrix, float x, float y, float z) +{ + float *m = (float *)matrix; + m[0] *= x; m[4] *= y; m[8] *= z; + m[1] *= x; m[5] *= y; m[9] *= z; + m[2] *= x; m[6] *= y; m[10] *= z; + m[3] *= x; m[7] *= y; m[11] *= z; + + if (fabsf (x - y) < 1e-8 && fabsf (x - z) < 1e-8) + matrix->flags |= MAT_FLAG_UNIFORM_SCALE; + else + matrix->flags |= MAT_FLAG_GENERAL_SCALE; + + matrix->flags |= (MAT_DIRTY_TYPE | MAT_DIRTY_INVERSE); +} + +/** + * Multiply a matrix with a translation matrix. + * + * \param mat matrix. + * \param x translation vector x coordinate. + * \param y translation vector y coordinate. + * \param z translation vector z coordinate. + * + * Adds the translation coordinates to the elements of \p mat in-place. Marks + * the MAT_FLAG_TRANSLATION flag, and the MAT_DIRTY_TYPE and MAT_DIRTY_INVERSE + * dirty flags. + */ +void +_math_matrix_translate (CoglMatrix *matrix, float x, float y, float z) +{ + float *m = (float *)matrix; + m[12] = m[0] * x + m[4] * y + m[8] * z + m[12]; + m[13] = m[1] * x + m[5] * y + m[9] * z + m[13]; + m[14] = m[2] * x + m[6] * y + m[10] * z + m[14]; + m[15] = m[3] * x + m[7] * y + m[11] * z + m[15]; + + matrix->flags |= (MAT_FLAG_TRANSLATION | + MAT_DIRTY_TYPE | + MAT_DIRTY_INVERSE); +} + + +/** + * Set matrix to do viewport and depthrange mapping. + * Transforms Normalized Device Coords to window/Z values. + */ +void +_math_matrix_viewport (CoglMatrix *matrix, int x, int y, int width, int height, + float zNear, float zFar, float depthMax) +{ + float *m = (float *)matrix; + m[MAT_SX] = (float)width / 2.0f; + m[MAT_TX] = m[MAT_SX] + x; + m[MAT_SY] = (float) height / 2.0f; + m[MAT_TY] = m[MAT_SY] + y; + m[MAT_SZ] = depthMax * ((zFar - zNear) / 2.0f); + m[MAT_TZ] = depthMax * ((zFar - zNear) / 2.0f + zNear); + matrix->flags = MAT_FLAG_GENERAL_SCALE | MAT_FLAG_TRANSLATION; + matrix->type = COGL_MATRIX_TYPE_3D_NO_ROT; +} + + +/** + * Set a matrix to the identity matrix. + * + * \param mat matrix. + * + * Copies ::identity into \p CoglMatrix::m, and into CoglMatrix::inv if + * not NULL. Sets the matrix type to identity, resets the flags. It + * doesn't initialize the inverse matrix, it just marks it dirty. + */ +void +_math_matrix_init_identity (CoglMatrix *matrix) +{ + memcpy (matrix, identity, 16 * sizeof (float)); + + matrix->type = COGL_MATRIX_TYPE_IDENTITY; + matrix->flags = MAT_DIRTY_INVERSE; +} + +/*@}*/ + + +/**********************************************************************/ +/** \name Matrix analysis */ +/*@{*/ + +#define ZERO(x) (1<flags &= ~MAT_FLAGS_GEOMETRY; + + /* Check for translation - no-one really cares + */ + if ((mask & MASK_NO_TRX) != MASK_NO_TRX) + matrix->flags |= MAT_FLAG_TRANSLATION; + + /* Do the real work + */ + if (mask == (unsigned int) MASK_IDENTITY) + matrix->type = COGL_MATRIX_TYPE_IDENTITY; + else if ((mask & MASK_2D_NO_ROT) == (unsigned int) MASK_2D_NO_ROT) + { + matrix->type = COGL_MATRIX_TYPE_2D_NO_ROT; + + if ((mask & MASK_NO_2D_SCALE) != MASK_NO_2D_SCALE) + matrix->flags |= MAT_FLAG_GENERAL_SCALE; + } + else if ((mask & MASK_2D) == (unsigned int) MASK_2D) + { + float mm = DOT2 (m, m); + float m4m4 = DOT2 (m+4,m+4); + float mm4 = DOT2 (m,m+4); + + matrix->type = COGL_MATRIX_TYPE_2D; + + /* Check for scale */ + if (SQ (mm-1) > SQ (1e-6) || + SQ (m4m4-1) > SQ (1e-6)) + matrix->flags |= MAT_FLAG_GENERAL_SCALE; + + /* Check for rotation */ + if (SQ (mm4) > SQ (1e-6)) + matrix->flags |= MAT_FLAG_GENERAL_3D; + else + matrix->flags |= MAT_FLAG_ROTATION; + + } + else if ((mask & MASK_3D_NO_ROT) == (unsigned int) MASK_3D_NO_ROT) + { + matrix->type = COGL_MATRIX_TYPE_3D_NO_ROT; + + /* Check for scale */ + if (SQ (m[0]-m[5]) < SQ (1e-6) && + SQ (m[0]-m[10]) < SQ (1e-6)) + { + if (SQ (m[0]-1.0) > SQ (1e-6)) + matrix->flags |= MAT_FLAG_UNIFORM_SCALE; + } + else + matrix->flags |= MAT_FLAG_GENERAL_SCALE; + } + else if ((mask & MASK_3D) == (unsigned int) MASK_3D) + { + float c1 = DOT3 (m,m); + float c2 = DOT3 (m+4,m+4); + float c3 = DOT3 (m+8,m+8); + float d1 = DOT3 (m, m+4); + float cp[3]; + + matrix->type = COGL_MATRIX_TYPE_3D; + + /* Check for scale */ + if (SQ (c1-c2) < SQ (1e-6) && SQ (c1-c3) < SQ (1e-6)) + { + if (SQ (c1-1.0) > SQ (1e-6)) + matrix->flags |= MAT_FLAG_UNIFORM_SCALE; + /* else no scale at all */ + } + else + matrix->flags |= MAT_FLAG_GENERAL_SCALE; + + /* Check for rotation */ + if (SQ (d1) < SQ (1e-6)) + { + CROSS3 ( cp, m, m+4); + SUB_3V ( cp, cp, (m+8)); + if (LEN_SQUARED_3FV(cp) < SQ(1e-6)) + matrix->flags |= MAT_FLAG_ROTATION; + else + matrix->flags |= MAT_FLAG_GENERAL_3D; + } + else + matrix->flags |= MAT_FLAG_GENERAL_3D; /* shear, etc */ + } + else if ((mask & MASK_PERSPECTIVE) == MASK_PERSPECTIVE && m[11]==-1.0f) + { + matrix->type = COGL_MATRIX_TYPE_PERSPECTIVE; + matrix->flags |= MAT_FLAG_GENERAL; + } + else + { + matrix->type = COGL_MATRIX_TYPE_GENERAL; + matrix->flags |= MAT_FLAG_GENERAL; + } +} + +/** + * Analyze a matrix given that its flags are accurate. + * + * This is the more common operation, hopefully. + */ +static void +analyse_from_flags (CoglMatrix *matrix) +{ + const float *m = (float *)matrix; + + if (TEST_MAT_FLAGS(matrix, 0)) + matrix->type = COGL_MATRIX_TYPE_IDENTITY; + else if (TEST_MAT_FLAGS(matrix, (MAT_FLAG_TRANSLATION | + MAT_FLAG_UNIFORM_SCALE | + MAT_FLAG_GENERAL_SCALE))) + { + if ( m[10] == 1.0f && m[14] == 0.0f ) + matrix->type = COGL_MATRIX_TYPE_2D_NO_ROT; + else + matrix->type = COGL_MATRIX_TYPE_3D_NO_ROT; + } + else if (TEST_MAT_FLAGS (matrix, MAT_FLAGS_3D)) + { + if ( m[ 8]==0.0f + && m[ 9]==0.0f + && m[2]==0.0f && m[6]==0.0f && m[10]==1.0f && m[14]==0.0f) + { + matrix->type = COGL_MATRIX_TYPE_2D; + } + else + matrix->type = COGL_MATRIX_TYPE_3D; + } + else if ( m[4]==0.0f && m[12]==0.0f + && m[1]==0.0f && m[13]==0.0f + && m[2]==0.0f && m[6]==0.0f + && m[3]==0.0f && m[7]==0.0f && m[11]==-1.0f && m[15]==0.0f) + { + matrix->type = COGL_MATRIX_TYPE_PERSPECTIVE; + } + else + matrix->type = COGL_MATRIX_TYPE_GENERAL; +} + +/** + * Analyze and update the type and flags of a matrix. + * + * \param mat matrix. + * + * If the matrix type is dirty then calls either analyse_from_scratch() or + * analyse_from_flags() to determine its type, according to whether the flags + * are dirty or not, respectively. If the matrix has an inverse and it's dirty + * then calls matrix_invert(). Finally clears the dirty flags. + */ +void +_math_matrix_update_type_and_flags (CoglMatrix *matrix) +{ + if (matrix->flags & MAT_DIRTY_TYPE) + { + if (matrix->flags & MAT_DIRTY_FLAGS) + analyse_from_scratch (matrix); + else + analyse_from_flags (matrix); + } + + matrix->flags &= ~(MAT_DIRTY_FLAGS | MAT_DIRTY_TYPE); +} + +/*@}*/ + + +/** + * Test if the given matrix preserves vector lengths. + */ +gboolean +_math_matrix_is_length_preserving (const CoglMatrix *m) +{ + return TEST_MAT_FLAGS (m, MAT_FLAGS_LENGTH_PRESERVING); +} + + +/** + * Test if the given matrix does any rotation. + * (or perhaps if the upper-left 3x3 is non-identity) + */ +gboolean +_math_matrix_has_rotation (const CoglMatrix *matrix) +{ + if (matrix->flags & (MAT_FLAG_GENERAL | + MAT_FLAG_ROTATION | + MAT_FLAG_GENERAL_3D | + MAT_FLAG_PERSPECTIVE)) + return TRUE; + else + return FALSE; +} + + +gboolean +_math_matrix_is_general_scale (const CoglMatrix *matrix) +{ + return (matrix->flags & MAT_FLAG_GENERAL_SCALE) ? TRUE : FALSE; +} + + +gboolean +_math_matrix_is_dirty (const CoglMatrix *matrix) +{ + return (matrix->flags & MAT_DIRTY_ALL) ? TRUE : FALSE; +} + + +/**********************************************************************/ +/** \name Matrix setup */ +/*@{*/ + +/** + * Loads a matrix array into CoglMatrix. + * + * \param m matrix array. + * \param mat matrix. + * + * Copies \p m into CoglMatrix::m and marks the MAT_FLAG_GENERAL and + * MAT_DIRTY_ALL + * flags. + */ +void +_math_matrix_init_from_array (CoglMatrix *matrix, const float *array) +{ + memcpy (matrix, array, 16 * sizeof (float)); + matrix->flags = (MAT_FLAG_GENERAL | MAT_DIRTY_ALL); +} + +/*@}*/ + + +/**********************************************************************/ +/** \name Matrix transpose */ +/*@{*/ + +/** + * Transpose a float matrix. + * + * \param to destination array. + * \param from source array. + */ +void +_math_transposef (float to[16], const float from[16]) +{ + to[0] = from[0]; + to[1] = from[4]; + to[2] = from[8]; + to[3] = from[12]; + to[4] = from[1]; + to[5] = from[5]; + to[6] = from[9]; + to[7] = from[13]; + to[8] = from[2]; + to[9] = from[6]; + to[10] = from[10]; + to[11] = from[14]; + to[12] = from[3]; + to[13] = from[7]; + to[14] = from[11]; + to[15] = from[15]; +} + +/** + * Transpose a double matrix. + * + * \param to destination array. + * \param from source array. + */ +void +_math_transposed (double to[16], const double from[16]) +{ + to[0] = from[0]; + to[1] = from[4]; + to[2] = from[8]; + to[3] = from[12]; + to[4] = from[1]; + to[5] = from[5]; + to[6] = from[9]; + to[7] = from[13]; + to[8] = from[2]; + to[9] = from[6]; + to[10] = from[10]; + to[11] = from[14]; + to[12] = from[3]; + to[13] = from[7]; + to[14] = from[11]; + to[15] = from[15]; +} + +/** + * Transpose a double matrix and convert to float. + * + * \param to destination array. + * \param from source array. + */ +void +_math_transposefd (float to[16], const double from[16]) +{ + to[0] = (float)from[0]; + to[1] = (float)from[4]; + to[2] = (float)from[8]; + to[3] = (float)from[12]; + to[4] = (float)from[1]; + to[5] = (float)from[5]; + to[6] = (float)from[9]; + to[7] = (float)from[13]; + to[8] = (float)from[2]; + to[9] = (float)from[6]; + to[10] = (float)from[10]; + to[11] = (float)from[14]; + to[12] = (float)from[3]; + to[13] = (float)from[7]; + to[14] = (float)from[11]; + to[15] = (float)from[15]; +} + +/*@}*/ + + +/** + * Transform a 4-element row vector (1x4 matrix) by a 4x4 matrix. This + * function is used for transforming clipping plane equations and spotlight + * directions. + * Mathematically, u = v * m. + * Input: v - input vector + * m - transformation matrix + * Output: u - transformed vector + */ +void +_mesa_transform_vector (float u[4], const float v[4], const float m[16]) +{ + const float v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3]; +#define M(row,col) m[row + col*4] + u[0] = v0 * M (0,0) + v1 * M (1,0) + v2 * M (2,0) + v3 * M (3,0); + u[1] = v0 * M (0,1) + v1 * M (1,1) + v2 * M (2,1) + v3 * M (3,1); + u[2] = v0 * M (0,2) + v1 * M (1,2) + v2 * M (2,2) + v3 * M (3,2); + u[3] = v0 * M (0,3) + v1 * M (1,3) + v2 * M (2,3) + v3 * M (3,3); +#undef M +} + diff --git a/cogl/cogl-matrix-mesa.h b/cogl/cogl-matrix-mesa.h new file mode 100644 index 000000000..1babce7f4 --- /dev/null +++ b/cogl/cogl-matrix-mesa.h @@ -0,0 +1,226 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 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. + */ +/* + * Copyright (C) 1999-2005 Brian Paul All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +/** + * \file math/m_matrix.h + * Defines basic structures for matrix-handling. + */ + +#ifndef _M_MATRIX_H +#define _M_MATRIX_H + +#include + +#include + +/** + * \name Symbolic names to some of the entries in the matrix + * + * These are handy for the viewport mapping, which is expressed as a matrix. + */ +/*@{*/ +#define MAT_SX 0 +#define MAT_SY 5 +#define MAT_SZ 10 +#define MAT_TX 12 +#define MAT_TY 13 +#define MAT_TZ 14 +/*@}*/ + + +/** + * Different kinds of 4x4 transformation matrices. + * We use these to select specific optimized vertex transformation routines. + */ +enum CoglMatrixType { + COGL_MATRIX_TYPE_GENERAL, /**< general 4x4 matrix */ + COGL_MATRIX_TYPE_IDENTITY, /**< identity matrix */ + COGL_MATRIX_TYPE_3D_NO_ROT, /**< orthogonal projection and others... */ + COGL_MATRIX_TYPE_PERSPECTIVE, /**< perspective projection matrix */ + COGL_MATRIX_TYPE_2D, /**< 2-D transformation */ + COGL_MATRIX_TYPE_2D_NO_ROT, /**< 2-D scale & translate only */ + COGL_MATRIX_TYPE_3D /**< 3-D transformation */ +} ; + + +#if 0 +/** + * Matrix type to represent 4x4 transformation matrices. + */ +typedef struct { + float *m; /**< 16 matrix elements (16-byte aligned) */ + float *inv; /**< optional 16-element inverse (16-byte aligned) */ + unsigned int flags; /**< possible values determined by (of \link + * MatFlags MAT_FLAG_* flags\endlink) + */ + enum CoglMatrixType type; +} CoglMatrix; +#endif + + +void +_math_matrix_multiply (CoglMatrix *result, + const CoglMatrix *a, + const CoglMatrix *b); + +void +_math_matrix_multiply_array (CoglMatrix *result, const float *b); + +void +_math_matrix_init_from_array (CoglMatrix *matrix, const float *array); + +void +_math_matrix_translate (CoglMatrix *matrix, float x, float y, float z); + +void +_math_matrix_rotate (CoglMatrix *matrix, float angle, + float x, float y, float z); + +void +_math_matrix_scale (CoglMatrix *matrix, float x, float y, float z); + +void +_math_matrix_ortho (CoglMatrix *matrix, + float left, float right, + float bottom, float top, + float nearval, float farval); + +void +_math_matrix_frustum (CoglMatrix *matrix, + float left, float right, + float bottom, float top, + float nearval, float farval); + +void +_math_matrix_viewport (CoglMatrix *matrix, + int x, int y, int width, int height, + float z_near, float z_far, float depth_max); + +void +_math_matrix_init_identity (CoglMatrix *matrix); + +gboolean +_math_matrix_update_inverse (CoglMatrix *matrix); + +void +_math_matrix_update_type_and_flags (CoglMatrix *matrix); + +void +_math_matrix_print (const CoglMatrix *matrix); + +gboolean +_math_matrix_is_length_preserving (const CoglMatrix *matrix); + +gboolean +_math_matrix_has_rotation (const CoglMatrix *matrix); + +gboolean +_math_matrix_is_general_scale (const CoglMatrix *matrix); + +gboolean +_math_matrix_is_dirty (const CoglMatrix *matrix); + + +/** + * \name Related functions that don't actually operate on CoglMatrix structs + */ +/*@{*/ + +void +_math_transposef ( float to[16], const float from[16]); + +void +_math_transposed (double to[16], const double from[16]); + +void +_math_transposefd (float to[16], const double from[16]); + + +/* + * Transform a point (column vector) by a matrix: Q = M * P + */ +#define TRANSFORM_POINT( Q, M, P ) \ + Q[0] = M[0] * P[0] + M[4] * P[1] + M[8] * P[2] + M[12] * P[3]; \ + Q[1] = M[1] * P[0] + M[5] * P[1] + M[9] * P[2] + M[13] * P[3]; \ + Q[2] = M[2] * P[0] + M[6] * P[1] + M[10] * P[2] + M[14] * P[3]; \ + Q[3] = M[3] * P[0] + M[7] * P[1] + M[11] * P[2] + M[15] * P[3]; + + +#define TRANSFORM_POINT3( Q, M, P ) \ + Q[0] = M[0] * P[0] + M[4] * P[1] + M[8] * P[2] + M[12]; \ + Q[1] = M[1] * P[0] + M[5] * P[1] + M[9] * P[2] + M[13]; \ + Q[2] = M[2] * P[0] + M[6] * P[1] + M[10] * P[2] + M[14]; \ + Q[3] = M[3] * P[0] + M[7] * P[1] + M[11] * P[2] + M[15]; + + +/* + * Transform a normal (row vector) by a matrix: [NX NY NZ] = N * MAT + */ +#define TRANSFORM_NORMAL( TO, N, MAT ) \ +do { \ + TO[0] = N[0] * MAT[0] + N[1] * MAT[1] + N[2] * MAT[2]; \ + TO[1] = N[0] * MAT[4] + N[1] * MAT[5] + N[2] * MAT[6]; \ + TO[2] = N[0] * MAT[8] + N[1] * MAT[9] + N[2] * MAT[10]; \ +} while (0) + + +/** + * Transform a direction by a matrix. + */ +#define TRANSFORM_DIRECTION( TO, DIR, MAT ) \ +do { \ + TO[0] = DIR[0] * MAT[0] + DIR[1] * MAT[4] + DIR[2] * MAT[8]; \ + TO[1] = DIR[0] * MAT[1] + DIR[1] * MAT[5] + DIR[2] * MAT[9]; \ + TO[2] = DIR[0] * MAT[2] + DIR[1] * MAT[6] + DIR[2] * MAT[10];\ +} while (0) + + +void +_mesa_transform_vector (float u[4], const float v[4], const float m[16]); + + +/*@}*/ + + +#endif diff --git a/cogl/cogl-matrix.c b/cogl/cogl-matrix.c index 4cc635985..5cb121b53 100644 --- a/cogl/cogl-matrix.c +++ b/cogl/cogl-matrix.c @@ -24,8 +24,13 @@ * Robert Bragg */ +#define USE_MESA_MATRIX_API + #include #include +#ifdef USE_MESA_MATRIX_API +#include +#endif #include #include @@ -34,10 +39,14 @@ void cogl_matrix_init_identity (CoglMatrix *matrix) { +#ifndef USE_MESA_MATRIX_API matrix->xx = 1; matrix->xy = 0; matrix->xz = 0; matrix->xw = 0; matrix->yx = 0; matrix->yy = 1; matrix->yz = 0; matrix->yw = 0; matrix->zx = 0; matrix->zy = 0; matrix->zz = 1; matrix->zw = 0; matrix->wx = 0; matrix->wy = 0; matrix->wz = 0; matrix->ww = 1; +#else + _math_matrix_init_identity (matrix); +#endif } void @@ -45,6 +54,7 @@ cogl_matrix_multiply (CoglMatrix *result, const CoglMatrix *a, const CoglMatrix *b) { +#ifndef USE_MESA_MATRIX_API CoglMatrix r; /* row 0 */ @@ -73,10 +83,13 @@ cogl_matrix_multiply (CoglMatrix *result, /* The idea was that having this unrolled; it might be easier for the * compiler to vectorize, but that's probably not true. Mesa does it - * using a single for (i=0; i<4; i++) approach, may that's better... + * using a single for (i=0; i<4; i++) approach, maybe that's better... */ *result = r; +#else + _math_matrix_multiply (result, a, b); +#endif } void @@ -86,6 +99,7 @@ cogl_matrix_rotate (CoglMatrix *matrix, float y, float z) { +#ifndef USE_MESA_MATRIX_API CoglMatrix rotation; CoglMatrix result; float c, s; @@ -116,6 +130,9 @@ cogl_matrix_rotate (CoglMatrix *matrix, cogl_matrix_multiply (&result, matrix, &rotation); *matrix = result; +#else + _math_matrix_rotate (matrix, angle, x, y, z); +#endif } void @@ -124,10 +141,14 @@ cogl_matrix_translate (CoglMatrix *matrix, float y, float z) { +#ifndef USE_MESA_MATRIX_API matrix->xw = matrix->xx * x + matrix->xy * y + matrix->xz * z + matrix->xw; matrix->yw = matrix->yx * x + matrix->yy * y + matrix->yz * z + matrix->yw; matrix->zw = matrix->zx * x + matrix->zy * y + matrix->zz * z + matrix->zw; matrix->ww = matrix->wx * x + matrix->wy * y + matrix->wz * z + matrix->ww; +#else + _math_matrix_translate (matrix, x, y, z); +#endif } void @@ -136,10 +157,14 @@ cogl_matrix_scale (CoglMatrix *matrix, float sy, float sz) { +#ifndef USE_MESA_MATRIX_API matrix->xx *= sx; matrix->xy *= sy; matrix->xz *= sz; matrix->yx *= sx; matrix->yy *= sy; matrix->yz *= sz; matrix->zx *= sx; matrix->zy *= sy; matrix->zz *= sz; matrix->wx *= sx; matrix->wy *= sy; matrix->wz *= sz; +#else + _math_matrix_scale (matrix, sx, sy, sz); +#endif } #if 0 @@ -163,6 +188,7 @@ cogl_matrix_frustum (CoglMatrix *matrix, float z_near, float z_far) { +#ifndef USE_MESA_MATRIX_API float x, y, a, b, c, d; CoglMatrix frustum; @@ -194,6 +220,9 @@ cogl_matrix_frustum (CoglMatrix *matrix, frustum.ww = 0.0f; cogl_matrix_multiply (matrix, matrix, &frustum); +#else + _math_matrix_frustum (matrix, left, right, bottom, top, z_near, z_far); +#endif } void @@ -223,6 +252,7 @@ cogl_matrix_ortho (CoglMatrix *matrix, float near_val, float far_val) { +#ifndef USE_MESA_MATRIX_API CoglMatrix ortho; /* column 0 */ @@ -250,12 +280,19 @@ cogl_matrix_ortho (CoglMatrix *matrix, ortho.ww = 1.0; cogl_matrix_multiply (matrix, matrix, &ortho); +#else + _math_matrix_ortho (matrix, left, right, bottom, top, near_val, far_val); +#endif } void cogl_matrix_init_from_array (CoglMatrix *matrix, const float *array) { +#ifndef USE_MESA_MATRIX_API memcpy (matrix, array, sizeof (float) * 16); +#else + _math_matrix_init_from_array (matrix, array); +#endif } const float * diff --git a/cogl/cogl-matrix.h b/cogl/cogl-matrix.h index e2c07f5c8..8f0f73de5 100644 --- a/cogl/cogl-matrix.h +++ b/cogl/cogl-matrix.h @@ -101,9 +101,9 @@ struct _CoglMatrix /* Note: we may want to extend this later with private flags * and a cache of the inverse transform matrix. */ - float _padding0[16]; - gulong _padding1; - gulong _padding2; + float inv[16]; + gulong type; + gulong flags; gulong _padding3; }; From 2126bf60fde7d0d1944fe217adb9eab17179493b Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Mon, 26 Oct 2009 11:01:33 +0000 Subject: [PATCH 20/24] [debug] Adds a COGL_DEBUG=matrices debug option This adds a COGL_DEBUG=matrices debug option that can be used to trace all matrix manipulation done using the Cogl API. This can be handy when you break something in such a way that a trace is still comparable with a previous working version since you can simply diff a log of the broken version vs the working version to home in on the bug. --- cogl/cogl-debug.c | 3 ++- cogl/cogl-debug.h | 3 ++- cogl/cogl-matrix-private.h | 47 ++++++++++++++++++++++++++++++++++++++ cogl/cogl-matrix.c | 20 ++++++++++++++++ cogl/cogl.c | 5 ++++ 5 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 cogl/cogl-matrix-private.h diff --git a/cogl/cogl-debug.c b/cogl/cogl-debug.c index 6ca99c5b3..efc0146a3 100644 --- a/cogl/cogl-debug.c +++ b/cogl/cogl-debug.c @@ -46,7 +46,8 @@ static const GDebugKey cogl_debug_keys[] = { { "disable-vbos", COGL_DEBUG_DISABLE_VBOS }, { "journal", COGL_DEBUG_JOURNAL }, { "batching", COGL_DEBUG_BATCHING }, - { "disable-software-transform", COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM } + { "disable-software-transform", COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM }, + { "matrices", COGL_DEBUG_MATRICES } }; static const gint n_cogl_debug_keys = G_N_ELEMENTS (cogl_debug_keys); diff --git a/cogl/cogl-debug.h b/cogl/cogl-debug.h index a1e5a358c..86d3b57db 100644 --- a/cogl/cogl-debug.h +++ b/cogl/cogl-debug.h @@ -44,7 +44,8 @@ typedef enum { COGL_DEBUG_DISABLE_VBOS = 1 << 12, COGL_DEBUG_JOURNAL = 1 << 13, COGL_DEBUG_BATCHING = 1 << 14, - COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM = 1 << 15 + COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM = 1 << 15, + COGL_DEBUG_MATRICES = 1 << 16 } CoglDebugFlags; #ifdef COGL_ENABLE_DEBUG diff --git a/cogl/cogl-matrix-private.h b/cogl/cogl-matrix-private.h new file mode 100644 index 000000000..4323815e9 --- /dev/null +++ b/cogl/cogl-matrix-private.h @@ -0,0 +1,47 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 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. + * + * Authors: + * Robert Bragg + */ + +#ifndef __COGL_MATRIX_PRIVATE_H +#define __COGL_MATRIX_PRIVATE_H + +#include + +G_BEGIN_DECLS + +#define _COGL_MATRIX_DEBUG_PRINT(MATRIX) \ + if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_MATRICES)) \ + { \ + g_print ("%s:\n", G_STRFUNC); \ + _cogl_matrix_print (MATRIX); \ + } + +void +_cogl_matrix_print (CoglMatrix *matrix); + +G_END_DECLS + +#endif /* __COGL_MATRIX_PRIVATE_H */ + diff --git a/cogl/cogl-matrix.c b/cogl/cogl-matrix.c index 5cb121b53..4a4fccf31 100644 --- a/cogl/cogl-matrix.c +++ b/cogl/cogl-matrix.c @@ -28,6 +28,7 @@ #include #include +#include #ifdef USE_MESA_MATRIX_API #include #endif @@ -36,6 +37,16 @@ #include #include +void +_cogl_matrix_print (CoglMatrix *matrix) +{ + float *m = (float *)matrix; + int y; + + for (y = 0; y < 4; y++) + g_print ("\t%6.4f %6.4f %6.4f %6.4f\n", m[y], m[4+y], m[8+y], m[12+y]); +} + void cogl_matrix_init_identity (CoglMatrix *matrix) { @@ -47,6 +58,7 @@ cogl_matrix_init_identity (CoglMatrix *matrix) #else _math_matrix_init_identity (matrix); #endif + _COGL_MATRIX_DEBUG_PRINT (matrix); } void @@ -90,6 +102,7 @@ cogl_matrix_multiply (CoglMatrix *result, #else _math_matrix_multiply (result, a, b); #endif + _COGL_MATRIX_DEBUG_PRINT (result); } void @@ -133,6 +146,7 @@ cogl_matrix_rotate (CoglMatrix *matrix, #else _math_matrix_rotate (matrix, angle, x, y, z); #endif + _COGL_MATRIX_DEBUG_PRINT (matrix); } void @@ -149,6 +163,7 @@ cogl_matrix_translate (CoglMatrix *matrix, #else _math_matrix_translate (matrix, x, y, z); #endif + _COGL_MATRIX_DEBUG_PRINT (matrix); } void @@ -165,6 +180,7 @@ cogl_matrix_scale (CoglMatrix *matrix, #else _math_matrix_scale (matrix, sx, sy, sz); #endif + _COGL_MATRIX_DEBUG_PRINT (matrix); } #if 0 @@ -223,6 +239,7 @@ cogl_matrix_frustum (CoglMatrix *matrix, #else _math_matrix_frustum (matrix, left, right, bottom, top, z_near, z_far); #endif + _COGL_MATRIX_DEBUG_PRINT (matrix); } void @@ -241,6 +258,7 @@ cogl_matrix_perspective (CoglMatrix *matrix, ymax, /* top */ z_near, z_far); + _COGL_MATRIX_DEBUG_PRINT (matrix); } void @@ -283,6 +301,7 @@ cogl_matrix_ortho (CoglMatrix *matrix, #else _math_matrix_ortho (matrix, left, right, bottom, top, near_val, far_val); #endif + _COGL_MATRIX_DEBUG_PRINT (matrix); } void @@ -293,6 +312,7 @@ cogl_matrix_init_from_array (CoglMatrix *matrix, const float *array) #else _math_matrix_init_from_array (matrix, array); #endif + _COGL_MATRIX_DEBUG_PRINT (matrix); } const float * diff --git a/cogl/cogl.c b/cogl/cogl.c index b1cef8b5b..7f556155a 100644 --- a/cogl/cogl.c +++ b/cogl/cogl.c @@ -39,6 +39,7 @@ #include "cogl-material-private.h" #include "cogl-winsys.h" #include "cogl-draw-buffer-private.h" +#include "cogl-matrix-private.h" #if defined (HAVE_COGL_GLES2) || defined (HAVE_COGL_GLES) #include "cogl-gles2-wrapper.h" @@ -1165,6 +1166,7 @@ cogl_get_modelview_matrix (CoglMatrix *matrix) CoglMatrixStack *modelview_stack = _cogl_draw_buffer_get_modelview_stack (_cogl_get_draw_buffer ()); _cogl_matrix_stack_get (modelview_stack, matrix); + _COGL_MATRIX_DEBUG_PRINT (matrix); } void @@ -1173,6 +1175,7 @@ cogl_set_modelview_matrix (CoglMatrix *matrix) CoglMatrixStack *modelview_stack = _cogl_draw_buffer_get_modelview_stack (_cogl_get_draw_buffer ()); _cogl_matrix_stack_set (modelview_stack, matrix); + _COGL_MATRIX_DEBUG_PRINT (matrix); } void @@ -1181,6 +1184,7 @@ cogl_get_projection_matrix (CoglMatrix *matrix) CoglMatrixStack *projection_stack = _cogl_draw_buffer_get_projection_stack (_cogl_get_draw_buffer ()); _cogl_matrix_stack_get (projection_stack, matrix); + _COGL_MATRIX_DEBUG_PRINT (matrix); } void @@ -1192,6 +1196,7 @@ cogl_set_projection_matrix (CoglMatrix *matrix) /* FIXME: Update the inverse projection matrix!! Presumably use * of clip planes must currently be broken if this API is used. */ + _COGL_MATRIX_DEBUG_PRINT (matrix); } CoglClipStackState * From 28c7e940bf322f96233534f6b769ccd1bcd3b14e Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Mon, 26 Oct 2009 17:51:34 +0000 Subject: [PATCH 21/24] [matrix] Adds cogl_matrix_get_inverse API This new API takes advantage of the recently imported Mesa code to support inverse matrix calculation. The matrix code keeps track (via internal flags) of the transformations a matrix represents so that it can select an optimized inversion function. Note: although other aspects of the Cogl matrix API have followed a similar style to Cairo's matrix API we haven't added a cogl_matrix_invert API because the inverse of a CoglMatrix is actually cached as part of the CoglMatrix structure meaning a destructive API like cogl_matrix_invert doesn't let users take advantage of this caching design. --- cogl/cogl-context.h | 3 --- cogl/cogl-matrix-stack.c | 11 ++++++++++ cogl/cogl-matrix-stack.h | 3 +++ cogl/cogl-matrix.c | 33 ++++++++++++++++++---------- cogl/cogl-matrix.h | 21 ++++++++++++++++++ cogl/cogl.c | 47 ++++++++-------------------------------- 6 files changed, 65 insertions(+), 53 deletions(-) diff --git a/cogl/cogl-context.h b/cogl/cogl-context.h index 9d2aa077a..d10664f30 100644 --- a/cogl/cogl-context.h +++ b/cogl/cogl-context.h @@ -61,9 +61,6 @@ typedef struct CoglMatrixMode flushed_matrix_mode; GList *texture_units; - /* Cache of inverse projection matrix */ - float inverse_projection[16]; - /* Materials */ CoglHandle default_material; CoglHandle source_material; diff --git a/cogl/cogl-matrix-stack.c b/cogl/cogl-matrix-stack.c index 4e2269dbc..c6236efd3 100644 --- a/cogl/cogl-matrix-stack.c +++ b/cogl/cogl-matrix-stack.c @@ -348,6 +348,17 @@ _cogl_matrix_stack_ortho (CoglMatrixStack *stack, state->is_identity = FALSE; } +gboolean +_cogl_matrix_stack_get_inverse (CoglMatrixStack *stack, + CoglMatrix *inverse) +{ + CoglMatrixState *state; + + state = _cogl_matrix_stack_top_mutable (stack, TRUE); + + return cogl_matrix_get_inverse (&state->matrix, inverse); +} + void _cogl_matrix_stack_get (CoglMatrixStack *stack, CoglMatrix *matrix) diff --git a/cogl/cogl-matrix-stack.h b/cogl/cogl-matrix-stack.h index 1762eda95..648eace84 100644 --- a/cogl/cogl-matrix-stack.h +++ b/cogl/cogl-matrix-stack.h @@ -71,6 +71,9 @@ void _cogl_matrix_stack_ortho (CoglMatrixStack *stack, float top, float z_near, float z_far); + +gboolean _cogl_matrix_stack_get_inverse (CoglMatrixStack *stack, + CoglMatrix *inverse); void _cogl_matrix_stack_get (CoglMatrixStack *stack, CoglMatrix *matrix); void _cogl_matrix_stack_set (CoglMatrixStack *stack, diff --git a/cogl/cogl-matrix.c b/cogl/cogl-matrix.c index 4a4fccf31..5eb0f9782 100644 --- a/cogl/cogl-matrix.c +++ b/cogl/cogl-matrix.c @@ -183,18 +183,6 @@ cogl_matrix_scale (CoglMatrix *matrix, _COGL_MATRIX_DEBUG_PRINT (matrix); } -#if 0 -gboolean -cogl_matrix_invert (CoglMatrix *matrix) -{ - /* TODO */ - /* Note: It might be nice to also use the flag based tricks that mesa does - * to alow it to track the type of transformations a matrix represents - * so it can use various assumptions to optimise the inversion. - */ -} -#endif - void cogl_matrix_frustum (CoglMatrix *matrix, float left, @@ -321,6 +309,27 @@ cogl_matrix_get_array (const CoglMatrix *matrix) return (float *)matrix; } +gboolean +cogl_matrix_get_inverse (const CoglMatrix *matrix, CoglMatrix *inverse) +{ +#ifndef USE_MESA_MATRIX_API +#warning "cogl_matrix_get_inverse not supported without Mesa matrix API" + cogl_matrix_init_identity (inverse); + return FALSE; +#else + if (_math_matrix_update_inverse ((CoglMatrix *)matrix)) + { + cogl_matrix_init_from_array (inverse, matrix->inv); + return TRUE; + } + else + { + cogl_matrix_init_identity (inverse); + return FALSE; + } +#endif +} + void cogl_matrix_transform_point (const CoglMatrix *matrix, float *x, diff --git a/cogl/cogl-matrix.h b/cogl/cogl-matrix.h index 8f0f73de5..19e3e133b 100644 --- a/cogl/cogl-matrix.h +++ b/cogl/cogl-matrix.h @@ -266,6 +266,27 @@ void cogl_matrix_init_from_array (CoglMatrix *matrix, const float *array); */ G_CONST_RETURN float *cogl_matrix_get_array (const CoglMatrix *matrix); +/** + * cogl_matrix_get_inverse: + * @matrix: A 4x4 transformation matrix + * @inverse: The destination for a 4x4 inverse transformation matrix + * + * This gets the inverse transform of a given matrix and uses it to initialize + * a new CoglMatrix. + * + * Note: that although the first parameter is annotated as const to indicate + * that the transform it represents isn't modified this function may + * technically save a copy of the inverse transform within the given CoglMatrix + * so that subsequent requests for the inverse transform may avoid costly + * inversion calculations. + * + * Returns TRUE if the inverse was successfully calculated or FALSE for + * degenerate transformations that can't be inverted (in this case the matrix + * will simply be initialized with the identity matrix) + */ +gboolean +cogl_matrix_get_inverse (const CoglMatrix *matrix, CoglMatrix *inverse); + /** * cogl_matrix_transform_point: * @matrix: A 4x4 transformation matrix diff --git a/cogl/cogl.c b/cogl/cogl.c index 7f556155a..7631eee5b 100644 --- a/cogl/cogl.c +++ b/cogl/cogl.c @@ -368,13 +368,17 @@ set_clip_plane (GLint plane_num, GLdouble plane[4]; #endif GLfloat angle; - CoglMatrix inverse_projection; CoglHandle draw_buffer = _cogl_get_draw_buffer (); CoglMatrixStack *modelview_stack = _cogl_draw_buffer_get_modelview_stack (draw_buffer); + CoglMatrixStack *projection_stack = + _cogl_draw_buffer_get_projection_stack (draw_buffer); + CoglMatrix inverse_projection; _COGL_GET_CONTEXT (ctx, NO_RETVAL); + _cogl_matrix_stack_get_inverse (projection_stack, &inverse_projection); + /* Calculate the angle between the axes and the line crossing the two points */ angle = atan2f (vertex_b[1] - vertex_a[1], @@ -382,13 +386,10 @@ set_clip_plane (GLint plane_num, _cogl_matrix_stack_push (modelview_stack); - /* Load the identity matrix and multiply by the reverse of the - projection matrix so we can specify the plane in screen - coordinates */ - _cogl_matrix_stack_load_identity (modelview_stack); - cogl_matrix_init_from_array (&inverse_projection, - ctx->inverse_projection); - _cogl_matrix_stack_multiply (modelview_stack, &inverse_projection); + /* Load the inverse of the projection matrix so we can specify the plane + * in screen coordinates */ + _cogl_matrix_stack_set (modelview_stack, &inverse_projection); + /* Rotate about point a */ _cogl_matrix_stack_translate (modelview_stack, vertex_a[0], vertex_a[1], vertex_a[2]); @@ -1095,7 +1096,6 @@ cogl_frustum (float left, float z_near, float z_far) { - float c, d; CoglMatrixStack *projection_stack = _cogl_draw_buffer_get_projection_stack (_cogl_get_draw_buffer ()); @@ -1110,22 +1110,6 @@ cogl_frustum (float left, top, z_near, z_far); - - /* Calculate and store the inverse of the matrix */ - memset (ctx->inverse_projection, 0, sizeof (float) * 16); - - c = - (z_far + z_near) / (z_far - z_near); - d = - (2 * (z_far * z_near)) / (z_far - z_near); - -#define M(row,col) ctx->inverse_projection[col*4+row] - M(0,0) = (right - left) / (2 * z_near); - M(0,3) = (right + left) / (2 * z_near); - M(1,1) = (top - bottom) / (2 * z_near); - M(1,3) = (top + bottom) / (2 * z_near); - M(2,3) = -1.0; - M(3,2) = 1.0 / d; - M(3,3) = c / d; -#undef M } void @@ -1145,19 +1129,6 @@ cogl_ortho (float left, cogl_matrix_init_identity (&ortho); cogl_matrix_ortho (&ortho, left, right, bottom, top, z_near, z_far); _cogl_matrix_stack_set (projection_stack, &ortho); - - /* Calculate and store the inverse of the matrix */ - memset (ctx->inverse_projection, 0, sizeof (float) * 16); - -#define M(row,col) ctx->inverse_projection[col*4+row] - M(0,0) = 1.0 / ortho.xx; - M(0,3) = -ortho.xw; - M(1,1) = 1.0 / ortho.yy; - M(1,3) = -ortho.yw; - M(2,2) = 1.0 / ortho.zz; - M(2,3) = -ortho.zw; - M(3,3) = 1.0; -#undef M } void From 57bd250a71742ca9ab704fb0a5466f4b210a6db8 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Fri, 30 Oct 2009 23:57:56 +0000 Subject: [PATCH 22/24] [cogl_clip_push_window_rect] fix Cogl -> GL coordinate conversion Firstly this now uses the draw buffer height not the viewport height when we need to perform a y = height - y conversion, since (as the name suggests) we are dealing with window coordinates not viewport coordinates. Secondly this skips any conversion when the current draw buffer is an offscreen draw buffer since offscreen rendering is always forced to be upside down and in this case Cogl window coordinates == GL window coordinates. --- cogl/cogl-clip-stack.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/cogl/cogl-clip-stack.c b/cogl/cogl-clip-stack.c index cac156fa6..93900338e 100644 --- a/cogl/cogl-clip-stack.c +++ b/cogl/cogl-clip-stack.c @@ -114,6 +114,13 @@ struct _CoglClipStackEntryPath CoglPathNode path[1]; }; +/* FIXME: deprecate and replace with: + * void + * cogl_clip_push_window_rectangle (int x_offset, + * int y_offset, + * int width, + * int height); + */ void cogl_clip_push_window_rect (float x_offset, float y_offset, @@ -123,8 +130,8 @@ cogl_clip_push_window_rect (float x_offset, CoglHandle draw_buffer; CoglClipStackState *clip_state; CoglClipStack *stack; + int draw_buffer_height; CoglClipStackEntryWindowRect *entry; - float viewport_height; _COGL_GET_CONTEXT (ctx, NO_RETVAL); @@ -137,17 +144,30 @@ cogl_clip_push_window_rect (float x_offset, stack = clip_state->stacks->data; - viewport_height = _cogl_draw_buffer_get_viewport_height (draw_buffer); + draw_buffer_height = _cogl_draw_buffer_get_height (draw_buffer); entry = g_slice_new (CoglClipStackEntryWindowRect); - /* We convert from coords with (0,0) at top left to coords - * with (0,0) at bottom left. */ + /* We store the entry coordinates in OpenGL window coordinate space and so + * because Cogl defines the window origin to be top left but OpenGL defines + * it as bottom left we may need to convert the incoming coordinates. + * + * NB: Cogl forces all offscreen rendering to be done upside down so in this + * case no conversion is needed. + */ entry->type = COGL_CLIP_STACK_WINDOW_RECT; entry->x0 = x_offset; - entry->y0 = viewport_height - y_offset - height; entry->x1 = x_offset + width; - entry->y1 = viewport_height - y_offset; + if (cogl_is_offscreen (draw_buffer)) + { + entry->y0 = y_offset; + entry->y1 = y_offset + height; + } + else + { + entry->y0 = draw_buffer_height - y_offset - height; + entry->y1 = draw_buffer_height - y_offset; + } /* Store it in the stack */ stack->stack_top = g_list_prepend (stack->stack_top, entry); From f910ce2ee55b45d069febab9f78abe45bdb84872 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Sat, 31 Oct 2009 00:00:33 +0000 Subject: [PATCH 23/24] [cogl-draw-buffer] fix Cogl -> GL viewport coord conversion Before we call glViewport we need to convert Cogl viewport coordinates (where the origin is defined to be top left) to OpenGL coordinates (where the origin is defined to be bottom left) We weren't considering that offscreen rendering is always upside down and in this case Cogl coordinates == OpenGL coordinates. --- cogl/cogl-draw-buffer.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/cogl/cogl-draw-buffer.c b/cogl/cogl-draw-buffer.c index 83ad3e313..2a7b75784 100644 --- a/cogl/cogl-draw-buffer.c +++ b/cogl/cogl-draw-buffer.c @@ -567,11 +567,18 @@ _cogl_draw_buffer_flush_state (CoglHandle handle, 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.) */ - int gl_viewport_y = draw_buffer->height - - (draw_buffer->viewport_y + draw_buffer->viewport_height); + * 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 draw buffers so we don't + * need to convert the y offset in this case. */ + if (cogl_is_offscreen (draw_buffer)) + gl_viewport_y = draw_buffer->viewport_y; + else + gl_viewport_y = draw_buffer->height - + (draw_buffer->viewport_y + draw_buffer->viewport_height); GE (glViewport (draw_buffer->viewport_x, gl_viewport_y, From 61654a9ab448d17a03d5b65cacf667c7364f29b9 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Sat, 31 Oct 2009 00:01:44 +0000 Subject: [PATCH 24/24] [cogl-texture-2d-sliced] allow COGL_FORMAT_ANY with _new_with_size() It's useful when initialzing offscreen draw buffers to be able to ask Cogl to create a texture of a given size and with the default internal pixel format. --- cogl/cogl-texture-2d-sliced.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cogl/cogl-texture-2d-sliced.c b/cogl/cogl-texture-2d-sliced.c index 9b70860e8..52eec49fe 100644 --- a/cogl/cogl-texture-2d-sliced.c +++ b/cogl/cogl-texture-2d-sliced.c @@ -907,7 +907,7 @@ _cogl_texture_2d_sliced_new_with_size (unsigned int width, /* Since no data, we need some internal format */ if (internal_format == COGL_PIXEL_FORMAT_ANY) - return COGL_INVALID_HANDLE; + internal_format = COGL_PIXEL_FORMAT_RGBA_8888_PRE; /* Rowstride from width */ bpp = _cogl_get_format_bpp (internal_format);