From 0599d12ec94344174c947310bddb5658f1b9db58 Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Sun, 1 Nov 2009 14:36:05 +0000 Subject: [PATCH 01/39] texture: Don't compare fbo_source with COGL_INVALID_HANDLE fbo_source is a ClutterActor, it should be compared to NULL instead of COGL_INVALID_HANDLE. --- clutter/clutter-texture.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clutter/clutter-texture.c b/clutter/clutter-texture.c index 363c99179..4d2996208 100644 --- a/clutter/clutter-texture.c +++ b/clutter/clutter-texture.c @@ -251,7 +251,7 @@ clutter_texture_unrealize (ClutterActor *actor) CLUTTER_MARK(); - if (priv->fbo_source != COGL_INVALID_HANDLE) + if (priv->fbo_source != NULL) { /* Free up our fbo handle and texture resources, realize will recreate */ cogl_handle_unref (priv->fbo_handle); From 613977f7108efb8897bffb2e47bfcf29bce29ddc Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Tue, 15 Sep 2009 22:15:03 +0100 Subject: [PATCH 02/39] [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. --- clutter/cogl/cogl/driver/gl/cogl-fbo.c | 10 ---------- clutter/cogl/cogl/driver/gles/cogl-fbo.c | 19 ------------------- 2 files changed, 29 deletions(-) diff --git a/clutter/cogl/cogl/driver/gl/cogl-fbo.c b/clutter/cogl/cogl/driver/gl/cogl-fbo.c index 1bd88aeea..99315e6b0 100644 --- a/clutter/cogl/cogl/driver/gl/cogl-fbo.c +++ b/clutter/cogl/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/clutter/cogl/cogl/driver/gles/cogl-fbo.c b/clutter/cogl/cogl/driver/gles/cogl-fbo.c index 0b1006970..34f6a5946 100644 --- a/clutter/cogl/cogl/driver/gles/cogl-fbo.c +++ b/clutter/cogl/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 ad98e96d977e43a882b8a2f680e59ed281c90baf Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Tue, 15 Sep 2009 22:19:01 +0100 Subject: [PATCH 03/39] [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. --- clutter/cogl/cogl/driver/gles/cogl-fbo.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/clutter/cogl/cogl/driver/gles/cogl-fbo.c b/clutter/cogl/cogl/driver/gles/cogl-fbo.c index 34f6a5946..41b5c6b0c 100644 --- a/clutter/cogl/cogl/driver/gles/cogl-fbo.c +++ b/clutter/cogl/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 f8f8873714043efbcbf92718493a94ac9ddf303f Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Tue, 20 Oct 2009 16:55:10 +0100 Subject: [PATCH 04/39] [stage-x11] Ensure viewport is initialized before first stage paint This ensures that glViewport is called before the first stage paint. Previously _clutter_stage_maybe_setup_viewport (which is done before we start painting) was bailing out without calling cogl_setup_viewport because the CLUTTER_STAGE_IN_RESIZE flag may be set if the stage was resized before the first paint. (NB: The CLUTTER_STAGE_IN_RESIZE flag isn't removed until we get an explicit event back from the X server since the window manager may choose to deny/alter the resize.) We now special case the first resize - where the viewport hasn't previously been initialized and use the requested geometry to initialize the glViewport without waiting for a reply from the server. --- clutter/x11/clutter-stage-x11.c | 18 ++++++++++++++++++ clutter/x11/clutter-stage-x11.h | 7 ++++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/clutter/x11/clutter-stage-x11.c b/clutter/x11/clutter-stage-x11.c index 074595f9f..678bbde2f 100644 --- a/clutter/x11/clutter-stage-x11.c +++ b/clutter/x11/clutter-stage-x11.c @@ -210,6 +210,7 @@ clutter_stage_x11_resize (ClutterStageWindow *stage_window, ClutterBackend *backend = clutter_get_default_backend (); ClutterBackendX11 *backend_x11; ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window); + ClutterStage *stage = stage_x11->wrapper; gboolean resize; resize = clutter_stage_get_user_resizable (stage_x11->wrapper); @@ -250,6 +251,22 @@ clutter_stage_x11_resize (ClutterStageWindow *stage_window, stage_x11->xwin, stage_x11->xwin_width, stage_x11->xwin_height); + + /* If the viewport hasn't previously been initialized then even + * though we can't guarantee that the server will honour our request + * we need to ensure a valid viewport is set before our first paint. + */ + if (G_UNLIKELY (!stage_x11->viewport_initialized)) + { + ClutterPerspective perspective; + clutter_stage_get_perspective (stage, &perspective); + _cogl_setup_viewport (stage_x11->xwin_width, + stage_x11->xwin_height, + perspective.fovy, + perspective.aspect, + perspective.z_near, + perspective.z_far); + } } if (!resize) @@ -654,6 +671,7 @@ clutter_stage_x11_init (ClutterStageX11 *stage) stage->is_foreign_xwin = FALSE; stage->fullscreen_on_map = FALSE; stage->is_cursor_visible = TRUE; + stage->viewport_initialized = FALSE; stage->title = NULL; diff --git a/clutter/x11/clutter-stage-x11.h b/clutter/x11/clutter-stage-x11.h index 0929094bc..71b66e348 100644 --- a/clutter/x11/clutter-stage-x11.h +++ b/clutter/x11/clutter-stage-x11.h @@ -50,9 +50,10 @@ struct _ClutterStageX11 { ClutterGroup parent_instance; - guint is_foreign_xwin : 1; - guint fullscreen_on_map : 1; - guint is_cursor_visible : 1; + guint is_foreign_xwin : 1; + guint fullscreen_on_map : 1; + guint is_cursor_visible : 1; + guint viewport_initialized : 1; Window xwin; gint xwin_width; From f7d64e5abd5717bb522d28b7565ecccae63271fc Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Fri, 25 Sep 2009 14:34:34 +0100 Subject: [PATCH 05/39] [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 --- clutter/clutter-main.c | 5 +- clutter/cogl/cogl/Makefile.am | 2 + clutter/cogl/cogl/cogl-clip-stack.c | 211 +++++-- clutter/cogl/cogl/cogl-clip-stack.h | 9 +- clutter/cogl/cogl/cogl-context.c | 34 +- clutter/cogl/cogl/cogl-context.h | 20 +- clutter/cogl/cogl/cogl-draw-buffer-private.h | 126 ++++ clutter/cogl/cogl/cogl-draw-buffer.c | 555 ++++++++++++++++++ clutter/cogl/cogl/cogl-journal.c | 33 +- clutter/cogl/cogl/cogl-primitives.c | 19 +- clutter/cogl/cogl/cogl-texture.c | 19 +- clutter/cogl/cogl/cogl-vertex-buffer.c | 14 +- clutter/cogl/cogl/cogl.c | 222 ++++--- clutter/cogl/cogl/cogl.h.in | 1 + clutter/cogl/cogl/driver/gl/Makefile.am | 2 - .../cogl/cogl/driver/gl/cogl-context-driver.c | 19 +- .../cogl/cogl/driver/gl/cogl-context-driver.h | 23 +- clutter/cogl/cogl/driver/gl/cogl-defines.h.in | 28 +- clutter/cogl/cogl/driver/gl/cogl-fbo.c | 315 ---------- clutter/cogl/cogl/driver/gl/cogl-fbo.h | 39 -- clutter/cogl/cogl/driver/gl/cogl-primitives.c | 99 +++- .../cogl/cogl/driver/gl/cogl-texture-driver.c | 2 +- clutter/cogl/cogl/driver/gl/cogl.c | 110 ++-- clutter/cogl/cogl/driver/gles/Makefile.am | 2 - .../cogl/driver/gles/cogl-context-driver.c | 10 + .../cogl/driver/gles/cogl-context-driver.h | 17 +- .../cogl/cogl/driver/gles/cogl-defines.h.in | 70 +++ clutter/cogl/cogl/driver/gles/cogl-fbo.c | 328 ----------- clutter/cogl/cogl/driver/gles/cogl-fbo.h | 39 -- .../cogl/cogl/driver/gles/cogl-primitives.c | 121 +++- clutter/cogl/cogl/driver/gles/cogl.c | 75 +++ 31 files changed, 1476 insertions(+), 1093 deletions(-) create mode 100644 clutter/cogl/cogl/cogl-draw-buffer-private.h create mode 100644 clutter/cogl/cogl/cogl-draw-buffer.c delete mode 100644 clutter/cogl/cogl/driver/gl/cogl-fbo.c delete mode 100644 clutter/cogl/cogl/driver/gl/cogl-fbo.h delete mode 100644 clutter/cogl/cogl/driver/gles/cogl-fbo.c delete mode 100644 clutter/cogl/cogl/driver/gles/cogl-fbo.h diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index f62d6bd89..8559da550 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -1338,6 +1338,8 @@ clutter_init_real (GError **error) /* Now we can safely assume we have a valid GL context and can * start issueing cogl commands */ + /* - will call to backend and cogl */ + _clutter_feature_init (); /* * Resolution requires display to be open, so can only be queried after @@ -1374,9 +1376,6 @@ clutter_init_real (GError **error) /* Initiate event collection */ _clutter_backend_init_events (ctx->backend); - /* finally features - will call to backend and cogl */ - _clutter_feature_init (); - clutter_is_initialized = TRUE; ctx->is_initialized = TRUE; diff --git a/clutter/cogl/cogl/Makefile.am b/clutter/cogl/cogl/Makefile.am index 81c5186d2..8988f8d35 100644 --- a/clutter/cogl/cogl/Makefile.am +++ b/clutter/cogl/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/clutter/cogl/cogl/cogl-clip-stack.c b/clutter/cogl/cogl/cogl-clip-stack.c index a0dc3bc49..8cf59d197 100644 --- a/clutter/cogl/cogl/cogl-clip-stack.c +++ b/clutter/cogl/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/clutter/cogl/cogl/cogl-clip-stack.h b/clutter/cogl/cogl/cogl-clip-stack.h index 324097b17..0ca041ec4 100644 --- a/clutter/cogl/cogl/cogl-clip-stack.h +++ b/clutter/cogl/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/clutter/cogl/cogl/cogl-context.c b/clutter/cogl/cogl/cogl-context.c index 6cbc7b7c2..8005aaf23 100644 --- a/clutter/cogl/cogl/cogl-context.c +++ b/clutter/cogl/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/clutter/cogl/cogl/cogl-context.h b/clutter/cogl/cogl/cogl-context.h index cf8e3ad84..67e33cc68 100644 --- a/clutter/cogl/cogl/cogl-context.h +++ b/clutter/cogl/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/clutter/cogl/cogl/cogl-draw-buffer-private.h b/clutter/cogl/cogl/cogl-draw-buffer-private.h new file mode 100644 index 000000000..d5bdf0b11 --- /dev/null +++ b/clutter/cogl/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/clutter/cogl/cogl/cogl-draw-buffer.c b/clutter/cogl/cogl/cogl-draw-buffer.c new file mode 100644 index 000000000..41f0a5f91 --- /dev/null +++ b/clutter/cogl/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/clutter/cogl/cogl/cogl-journal.c b/clutter/cogl/cogl/cogl-journal.c index aa0d5892e..a50bf8e42 100644 --- a/clutter/cogl/cogl/cogl-journal.c +++ b/clutter/cogl/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/clutter/cogl/cogl/cogl-primitives.c b/clutter/cogl/cogl/cogl-primitives.c index 016833ad3..45ec90ce6 100644 --- a/clutter/cogl/cogl/cogl-primitives.c +++ b/clutter/cogl/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/clutter/cogl/cogl/cogl-texture.c b/clutter/cogl/cogl/cogl-texture.c index fa33f047d..763edbc2e 100644 --- a/clutter/cogl/cogl/cogl-texture.c +++ b/clutter/cogl/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/clutter/cogl/cogl/cogl-vertex-buffer.c b/clutter/cogl/cogl/cogl-vertex-buffer.c index c1aa172f4..357d1e6e5 100644 --- a/clutter/cogl/cogl/cogl-vertex-buffer.c +++ b/clutter/cogl/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/clutter/cogl/cogl/cogl.c b/clutter/cogl/cogl/cogl.c index 4fb1c7e67..8fdb36ff8 100644 --- a/clutter/cogl/cogl/cogl.c +++ b/clutter/cogl/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/clutter/cogl/cogl/cogl.h.in b/clutter/cogl/cogl/cogl.h.in index 524aec71d..23e417a29 100644 --- a/clutter/cogl/cogl/cogl.h.in +++ b/clutter/cogl/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/clutter/cogl/cogl/driver/gl/Makefile.am b/clutter/cogl/cogl/driver/gl/Makefile.am index 81a21c90e..afe961780 100644 --- a/clutter/cogl/cogl/driver/gl/Makefile.am +++ b/clutter/cogl/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/clutter/cogl/cogl/driver/gl/cogl-context-driver.c b/clutter/cogl/cogl/driver/gl/cogl-context-driver.c index dcf65ef26..c27721fd0 100644 --- a/clutter/cogl/cogl/driver/gl/cogl-context-driver.c +++ b/clutter/cogl/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/clutter/cogl/cogl/driver/gl/cogl-context-driver.h b/clutter/cogl/cogl/driver/gl/cogl-context-driver.h index ac5e61621..ff266d663 100644 --- a/clutter/cogl/cogl/driver/gl/cogl-context-driver.h +++ b/clutter/cogl/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/clutter/cogl/cogl/driver/gl/cogl-defines.h.in b/clutter/cogl/cogl/driver/gl/cogl-defines.h.in index 1c79bedfa..9d04be9af 100644 --- a/clutter/cogl/cogl/driver/gl/cogl-defines.h.in +++ b/clutter/cogl/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/clutter/cogl/cogl/driver/gl/cogl-fbo.c b/clutter/cogl/cogl/driver/gl/cogl-fbo.c deleted file mode 100644 index 99315e6b0..000000000 --- a/clutter/cogl/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/clutter/cogl/cogl/driver/gl/cogl-fbo.h b/clutter/cogl/cogl/driver/gl/cogl-fbo.h deleted file mode 100644 index 8499f8fb0..000000000 --- a/clutter/cogl/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/clutter/cogl/cogl/driver/gl/cogl-primitives.c b/clutter/cogl/cogl/driver/gl/cogl-primitives.c index 3c1c9e656..d2bc19335 100644 --- a/clutter/cogl/cogl/driver/gl/cogl-primitives.c +++ b/clutter/cogl/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/clutter/cogl/cogl/driver/gl/cogl-texture-driver.c b/clutter/cogl/cogl/driver/gl/cogl-texture-driver.c index e4c868dbd..742fab807 100644 --- a/clutter/cogl/cogl/driver/gl/cogl-texture-driver.c +++ b/clutter/cogl/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/clutter/cogl/cogl/driver/gl/cogl.c b/clutter/cogl/cogl/driver/gl/cogl.c index 04d3b750a..e81ec377c 100644 --- a/clutter/cogl/cogl/driver/gl/cogl.c +++ b/clutter/cogl/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/clutter/cogl/cogl/driver/gles/Makefile.am b/clutter/cogl/cogl/driver/gles/Makefile.am index 0a44fb465..182b29b84 100644 --- a/clutter/cogl/cogl/driver/gles/Makefile.am +++ b/clutter/cogl/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/clutter/cogl/cogl/driver/gles/cogl-context-driver.c b/clutter/cogl/cogl/driver/gles/cogl-context-driver.c index 6660db1f3..ae2a06477 100644 --- a/clutter/cogl/cogl/driver/gles/cogl-context-driver.c +++ b/clutter/cogl/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/clutter/cogl/cogl/driver/gles/cogl-context-driver.h b/clutter/cogl/cogl/driver/gles/cogl-context-driver.h index 9b5996e74..de24eb775 100644 --- a/clutter/cogl/cogl/driver/gles/cogl-context-driver.h +++ b/clutter/cogl/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/clutter/cogl/cogl/driver/gles/cogl-defines.h.in b/clutter/cogl/cogl/driver/gles/cogl-defines.h.in index 97045df94..8f0cff1da 100644 --- a/clutter/cogl/cogl/driver/gles/cogl-defines.h.in +++ b/clutter/cogl/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/clutter/cogl/cogl/driver/gles/cogl-fbo.c b/clutter/cogl/cogl/driver/gles/cogl-fbo.c deleted file mode 100644 index 41b5c6b0c..000000000 --- a/clutter/cogl/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/clutter/cogl/cogl/driver/gles/cogl-fbo.h b/clutter/cogl/cogl/driver/gles/cogl-fbo.h deleted file mode 100644 index 8499f8fb0..000000000 --- a/clutter/cogl/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/clutter/cogl/cogl/driver/gles/cogl-primitives.c b/clutter/cogl/cogl/driver/gles/cogl-primitives.c index f31bd2143..09d637e7d 100644 --- a/clutter/cogl/cogl/driver/gles/cogl-primitives.c +++ b/clutter/cogl/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/clutter/cogl/cogl/driver/gles/cogl.c b/clutter/cogl/cogl/driver/gles/cogl.c index aa7da6ef2..565ff0fa7 100644 --- a/clutter/cogl/cogl/driver/gles/cogl.c +++ b/clutter/cogl/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 419db4dcfbef74475e155f8dd060bc1c526d5643 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Sat, 17 Oct 2009 00:31:26 +0100 Subject: [PATCH 06/39] [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) --- clutter/cogl/cogl/cogl-clip-stack.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/clutter/cogl/cogl/cogl-clip-stack.c b/clutter/cogl/cogl/cogl-clip-stack.c index 8cf59d197..e9a7571cc 100644 --- a/clutter/cogl/cogl/cogl-clip-stack.c +++ b/clutter/cogl/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 8b4c496f2a0291eb202d775e210b5901b6b73940 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Thu, 22 Oct 2009 12:35:33 +0100 Subject: [PATCH 07/39] [cogl] Ensure features are initialized first in cogl_create_context Previously some context initializing was being done without valid feature flags. --- clutter/cogl/cogl/cogl-context.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/clutter/cogl/cogl/cogl-context.c b/clutter/cogl/cogl/cogl-context.c index 8005aaf23..0f309bd06 100644 --- a/clutter/cogl/cogl/cogl-context.c +++ b/clutter/cogl/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 e3391b0173479b9ba3b01321ab606d83999bee1a Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Thu, 22 Oct 2009 16:13:01 +0100 Subject: [PATCH 08/39] [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. --- clutter/cogl/cogl/cogl-context.c | 4 +++ clutter/cogl/cogl/cogl-context.h | 4 +++ clutter/cogl/cogl/cogl-draw-buffer-private.h | 3 ++ clutter/cogl/cogl/cogl-matrix-stack.c | 36 ++++++++++++++------ 4 files changed, 37 insertions(+), 10 deletions(-) diff --git a/clutter/cogl/cogl/cogl-context.c b/clutter/cogl/cogl/cogl-context.c index 0f309bd06..ccd71c7d0 100644 --- a/clutter/cogl/cogl/cogl-context.c +++ b/clutter/cogl/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/clutter/cogl/cogl/cogl-context.h b/clutter/cogl/cogl/cogl-context.h index 67e33cc68..1d3f9ecf3 100644 --- a/clutter/cogl/cogl/cogl-context.h +++ b/clutter/cogl/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/clutter/cogl/cogl/cogl-draw-buffer-private.h b/clutter/cogl/cogl/cogl-draw-buffer-private.h index d5bdf0b11..5ad4fc896 100644 --- a/clutter/cogl/cogl/cogl-draw-buffer-private.h +++ b/clutter/cogl/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/clutter/cogl/cogl/cogl-matrix-stack.c b/clutter/cogl/cogl/cogl-matrix-stack.c index b1f21c946..4e2269dbc 100644 --- a/clutter/cogl/cogl/cogl-matrix-stack.c +++ b/clutter/cogl/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 12af2751392ea53959fa41dd7cb19e07358655f2 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Wed, 21 Oct 2009 23:24:49 +0100 Subject: [PATCH 09/39] [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. --- clutter/clutter-backend.c | 19 ++++++++++++++- clutter/clutter-stage.c | 8 +++++++ clutter/cogl/cogl/cogl-context.c | 2 +- clutter/cogl/cogl/cogl-context.h | 2 +- clutter/cogl/cogl/cogl-draw-buffer.c | 36 ++++++++++++++++++++++++---- clutter/cogl/cogl/cogl.h.in | 3 +++ 6 files changed, 62 insertions(+), 8 deletions(-) diff --git a/clutter/clutter-backend.c b/clutter/clutter-backend.c index b0a7c279e..ca4fd3c26 100644 --- a/clutter/clutter-backend.c +++ b/clutter/clutter-backend.c @@ -47,6 +47,8 @@ #include "clutter-marshal.h" #include "clutter-private.h" +#include + G_DEFINE_ABSTRACT_TYPE (ClutterBackend, clutter_backend, G_TYPE_OBJECT); #define DEFAULT_FONT_NAME "Sans 10" @@ -355,7 +357,22 @@ _clutter_backend_ensure_context (ClutterBackend *backend, klass = CLUTTER_BACKEND_GET_CLASS (backend); if (G_LIKELY (klass->ensure_context)) klass->ensure_context (backend, new_stage); - + + /* XXX: Until Cogl becomes fully responsible for backend windows + * Clutter need to manually keep it informed of the current window size + * + * NB: This must be done after we ensure_context above because Cogl + * always assumes there is a current GL context. + */ + if (new_stage) + { + float width, height; + + clutter_actor_get_size (CLUTTER_ACTOR (stage), &width, &height); + + _cogl_onscreen_clutter_backend_set_size (width, height); + } + /* FIXME: With a NULL stage and thus no active context it may make more * sense to clean the context but then re call with the default stage * so at least there is some kind of context in place (as to avoid diff --git a/clutter/clutter-stage.c b/clutter/clutter-stage.c index 9fe5cd347..be2941217 100644 --- a/clutter/clutter-stage.c +++ b/clutter/clutter-stage.c @@ -192,6 +192,10 @@ clutter_stage_allocate (ClutterActor *self, { ClutterActorClass *klass; + /* XXX: Until Cogl becomes fully responsible for backend windows Clutter + * need to manually keep it informed of the current window size */ + _cogl_onscreen_clutter_backend_set_size (width, height); + CLUTTER_NOTE (LAYOUT, "Following allocation to %dx%d (origin %s)", width, height, @@ -210,6 +214,10 @@ clutter_stage_allocate (ClutterActor *self, _clutter_stage_window_get_geometry (priv->impl, &geom); + /* XXX: Until Cogl becomes fully responsible for backend windows Clutter + * need to manually keep it informed of the current window size */ + _cogl_onscreen_clutter_backend_set_size (geom.width, geom.height); + override.x1 = 0; override.y1 = 0; override.x2 = geom.width; diff --git a/clutter/cogl/cogl/cogl-context.c b/clutter/cogl/cogl/cogl-context.c index ccd71c7d0..d50ec6cf9 100644 --- a/clutter/cogl/cogl/cogl-context.c +++ b/clutter/cogl/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/clutter/cogl/cogl/cogl-context.h b/clutter/cogl/cogl/cogl-context.h index 1d3f9ecf3..aa2045456 100644 --- a/clutter/cogl/cogl/cogl-context.h +++ b/clutter/cogl/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/clutter/cogl/cogl/cogl-draw-buffer.c b/clutter/cogl/cogl/cogl-draw-buffer.c index 41f0a5f91..76077627a 100644 --- a/clutter/cogl/cogl/cogl-draw-buffer.c +++ b/clutter/cogl/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/clutter/cogl/cogl/cogl.h.in b/clutter/cogl/cogl/cogl.h.in index 23e417a29..9ce95ba35 100644 --- a/clutter/cogl/cogl/cogl.h.in +++ b/clutter/cogl/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 e1630be35a912125b1f493c7d1007b65eaecd048 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Wed, 21 Oct 2009 23:20:44 +0100 Subject: [PATCH 10/39] [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. --- clutter/cogl/cogl/cogl-draw-buffer-private.h | 4 ++++ clutter/cogl/cogl/cogl-draw-buffer.c | 14 ++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/clutter/cogl/cogl/cogl-draw-buffer-private.h b/clutter/cogl/cogl/cogl-draw-buffer-private.h index 5ad4fc896..3490cd26c 100644 --- a/clutter/cogl/cogl/cogl-draw-buffer-private.h +++ b/clutter/cogl/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/clutter/cogl/cogl/cogl-draw-buffer.c b/clutter/cogl/cogl/cogl-draw-buffer.c index 76077627a..83ad3e313 100644 --- a/clutter/cogl/cogl/cogl-draw-buffer.c +++ b/clutter/cogl/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 1e2d88e7891a02b26d0143e28788072e8731be6b Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Wed, 21 Oct 2009 23:24:28 +0100 Subject: [PATCH 11/39] [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. --- clutter/cogl/cogl/cogl.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/clutter/cogl/cogl/cogl.c b/clutter/cogl/cogl/cogl.c index 8fdb36ff8..e2fd88d88 100644 --- a/clutter/cogl/cogl/cogl.c +++ b/clutter/cogl/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 8c3a132ecb3ce805c45dad70c05417713ed9d05d Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Tue, 3 Nov 2009 13:26:58 +0000 Subject: [PATCH 12/39] [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. --- README | 6 ++++++ clutter/clutter-texture.c | 2 +- clutter/cogl/cogl/cogl.c | 12 ++++++------ clutter/cogl/cogl/cogl.h.in | 22 +++++++++++++++++++++- 4 files changed, 34 insertions(+), 8 deletions(-) diff --git a/README b/README index 668296055..464ebe9ee 100644 --- a/README +++ b/README @@ -212,6 +212,12 @@ Release Notes for Clutter 1.2 * Clutter now depends on the system copy of JSON-GLib, and will fall back to the internal copy only if JSON-GLib is not installed. +Cogl API changes for Clutter 1.2 +-------------------------------- + +* cogl_viewport is now deprecated in favour of cogl_set_viewport which + accepts a viewport offset. + Release Notes for Clutter 1.0 ------------------------------- diff --git a/clutter/clutter-texture.c b/clutter/clutter-texture.c index 4d2996208..aab1435f2 100644 --- a/clutter/clutter-texture.c +++ b/clutter/clutter-texture.c @@ -571,7 +571,7 @@ clutter_texture_paint (ClutterActor *self) clutter_texture_set_fbo_projection (self); /* Reset the viewport to the size of the FBO */ - cogl_viewport (priv->image_width, priv->image_height); + cogl_set_viewport (0, 0, priv->image_width, priv->image_height); /* Reapply the source's parent transformations */ if ((source_parent = clutter_actor_get_parent (priv->fbo_source))) diff --git a/clutter/cogl/cogl/cogl.c b/clutter/cogl/cogl/cogl.c index e2fd88d88..3e1f69c12 100644 --- a/clutter/cogl/cogl/cogl.c +++ b/clutter/cogl/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/clutter/cogl/cogl/cogl.h.in b/clutter/cogl/cogl/cogl.h.in index 9ce95ba35..03ab052d9 100644 --- a/clutter/cogl/cogl/cogl.h.in +++ b/clutter/cogl/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 21322848e0094a025dc0b5461eff30e0d66542da Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Wed, 21 Oct 2009 23:22:45 +0100 Subject: [PATCH 13/39] [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. --- clutter/cogl/cogl/cogl-clip-stack.c | 30 +++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/clutter/cogl/cogl/cogl-clip-stack.c b/clutter/cogl/cogl/cogl-clip-stack.c index e9a7571cc..cac156fa6 100644 --- a/clutter/cogl/cogl/cogl-clip-stack.c +++ b/clutter/cogl/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 426197f51da9c4351b36812276f0caee06965cb1 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Thu, 22 Oct 2009 16:55:07 +0100 Subject: [PATCH 14/39] [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. --- clutter/cogl/cogl/cogl.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/clutter/cogl/cogl/cogl.c b/clutter/cogl/cogl/cogl.c index 3e1f69c12..d9effc441 100644 --- a/clutter/cogl/cogl/cogl.c +++ b/clutter/cogl/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 b47404c91d6fac264840a0dafb5dd2dd5567c6a9 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Mon, 2 Nov 2009 01:12:10 +0000 Subject: [PATCH 15/39] [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. --- clutter/cogl/cogl/cogl.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/clutter/cogl/cogl/cogl.c b/clutter/cogl/cogl/cogl.c index d9effc441..c34205290 100644 --- a/clutter/cogl/cogl/cogl.c +++ b/clutter/cogl/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 ed431a3c65507b3e3da4760eae6e2b22be1f27f5 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Mon, 2 Nov 2009 01:10:02 +0000 Subject: [PATCH 16/39] [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. --- clutter/cogl/cogl/cogl.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/clutter/cogl/cogl/cogl.c b/clutter/cogl/cogl/cogl.c index c34205290..1b2e9c46a 100644 --- a/clutter/cogl/cogl/cogl.c +++ b/clutter/cogl/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 a222ee22f9dd35c5e0671914206759699e63750d Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Mon, 2 Nov 2009 01:11:21 +0000 Subject: [PATCH 17/39] [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. --- clutter/cogl/cogl/cogl.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clutter/cogl/cogl/cogl.c b/clutter/cogl/cogl/cogl.c index 1b2e9c46a..21e5572e8 100644 --- a/clutter/cogl/cogl/cogl.c +++ b/clutter/cogl/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 40b8399d49f6b0383b9eebcdf1ca501b5edb4f39 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Tue, 3 Nov 2009 12:54:45 +0000 Subject: [PATCH 18/39] [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. --- clutter/cogl/cogl/cogl.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/clutter/cogl/cogl/cogl.c b/clutter/cogl/cogl/cogl.c index 21e5572e8..b8b51b375 100644 --- a/clutter/cogl/cogl/cogl.c +++ b/clutter/cogl/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 b2ebb7db485d0a58bcb642fe9e4584c85ac9a503 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Fri, 30 Oct 2009 23:54:13 +0000 Subject: [PATCH 19/39] [main] Use cogl_read_pixels not glReadPixels in clutter-main.c The debugging function read_pixels_to_file() and _clutter_do_pick were both directly calling glReadPixels, but we don't wan't Clutter making direct OpenGL calls and Cogl provides a suitable alternative. It also means read_pixels_to_file() doesn't need to manually flip the data read due to differences in Clutter/Cogl coordinate systems. --- clutter/clutter-main.c | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 8559da550..b6bb287e0 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -425,10 +425,12 @@ read_pixels_to_file (char *filename_stem, GLubyte *data; GdkPixbuf *pixbuf; static int read_count = 0; - GdkPixbuf *flipped; data = g_malloc (4 * width * height); - glReadPixels (0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data); + cogl_read_pixels (x, y, width, height, + COGL_READ_PIXELS_COLOR_BUFFER, + COGL_PIXEL_FORMAT_RGBA_8888, + data); pixbuf = gdk_pixbuf_new_from_data (data, GDK_COLORSPACE_RGB, TRUE, /* has alpha */ @@ -439,24 +441,18 @@ read_pixels_to_file (char *filename_stem, pixbuf_free, /* callback to free data */ NULL); /* callback data */ if (pixbuf) - { - flipped = gdk_pixbuf_flip (pixbuf, FALSE); - g_object_unref (pixbuf); - } - - if (flipped) { char *filename = g_strdup_printf ("%s-%05d.png", filename_stem, read_count); GError *error = NULL; - if (!gdk_pixbuf_save (flipped, filename, "png", &error, NULL)) + if (!gdk_pixbuf_save (pixbuf, filename, "png", &error, NULL)) { g_warning ("Failed to save pick buffer to file %s: %s", filename, error->message); g_error_free (error); } g_free (filename); - g_object_unref (flipped); + g_object_unref (pixbuf); read_count++; } #else @@ -478,7 +474,6 @@ _clutter_do_pick (ClutterStage *stage, { ClutterMainContext *context; guchar pixel[4] = { 0xff, 0xff, 0xff, 0xff }; - GLint viewport[4]; CoglColor white; guint32 id; GLboolean dither_was_on; @@ -517,17 +512,21 @@ _clutter_do_pick (ClutterStage *stage, if (G_LIKELY (!(clutter_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS))) cogl_clip_pop (); - /* Calls should work under both GL and GLES, note GLES needs RGBA */ - glGetIntegerv(GL_VIEWPORT, viewport); - /* Make sure Cogl flushes any batched geometry to the GPU driver */ cogl_flush (); /* Read the color of the screen co-ords pixel */ - glReadPixels (x, viewport[3] - y -1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel); + cogl_read_pixels (x, y, 1, 1, + COGL_READ_PIXELS_COLOR_BUFFER, + COGL_PIXEL_FORMAT_RGBA_8888, + pixel); if (G_UNLIKELY (clutter_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS)) - read_pixels_to_file ("pick-buffer", 0, 0, viewport[2], viewport[3]); + { + read_pixels_to_file ("pick-buffer", 0, 0, + clutter_actor_get_width (CLUTTER_ACTOR (stage)), + clutter_actor_get_height (CLUTTER_ACTOR (stage))); + } /* Restore whether GL_DITHER was enabled */ if (dither_was_on) From 764cca75b4ee6bb269097445cb5220f0e044a57c Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Thu, 22 Oct 2009 19:01:52 +0100 Subject: [PATCH 20/39] [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. --- clutter/cogl/cogl/cogl-context.c | 2 ++ clutter/cogl/cogl/cogl-context.h | 1 + clutter/cogl/cogl/cogl-internal.h | 9 +++++++ clutter/cogl/cogl/cogl-journal.c | 1 + clutter/cogl/cogl/cogl-primitives.c | 1 + clutter/cogl/cogl/cogl-vertex-buffer.c | 1 + clutter/cogl/cogl/cogl.c | 33 ++++++++++++++++++++++++++ 7 files changed, 48 insertions(+) diff --git a/clutter/cogl/cogl/cogl-context.c b/clutter/cogl/cogl/cogl-context.c index d50ec6cf9..34c65d08c 100644 --- a/clutter/cogl/cogl/cogl-context.c +++ b/clutter/cogl/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/clutter/cogl/cogl/cogl-context.h b/clutter/cogl/cogl/cogl-context.h index aa2045456..9d2aa077a 100644 --- a/clutter/cogl/cogl/cogl-context.h +++ b/clutter/cogl/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/clutter/cogl/cogl/cogl-internal.h b/clutter/cogl/cogl/cogl-internal.h index 1f84e0d05..a920a9785 100644 --- a/clutter/cogl/cogl/cogl-internal.h +++ b/clutter/cogl/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/clutter/cogl/cogl/cogl-journal.c b/clutter/cogl/cogl/cogl-journal.c index a50bf8e42..bfa92c6be 100644 --- a/clutter/cogl/cogl/cogl-journal.c +++ b/clutter/cogl/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/clutter/cogl/cogl/cogl-primitives.c b/clutter/cogl/cogl/cogl-primitives.c index 45ec90ce6..af32264d7 100644 --- a/clutter/cogl/cogl/cogl-primitives.c +++ b/clutter/cogl/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/clutter/cogl/cogl/cogl-vertex-buffer.c b/clutter/cogl/cogl/cogl-vertex-buffer.c index 357d1e6e5..2ded6eeb4 100644 --- a/clutter/cogl/cogl/cogl-vertex-buffer.c +++ b/clutter/cogl/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/clutter/cogl/cogl/cogl.c b/clutter/cogl/cogl/cogl.c index b8b51b375..18a51fa90 100644 --- a/clutter/cogl/cogl/cogl.c +++ b/clutter/cogl/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 d38d888f786f3be03778bcc10f20389e6c7ff1ff Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Thu, 22 Oct 2009 19:09:20 +0100 Subject: [PATCH 21/39] [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. --- clutter/cogl/cogl/cogl.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/clutter/cogl/cogl/cogl.c b/clutter/cogl/cogl/cogl.c index 18a51fa90..b1cef8b5b 100644 --- a/clutter/cogl/cogl/cogl.c +++ b/clutter/cogl/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 eb438dd499c002a7d770d5e3d0c1fb1c84da933e Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Mon, 26 Oct 2009 08:23:21 +0000 Subject: [PATCH 22/39] [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) --- clutter/cogl/cogl/Makefile.am | 2 + clutter/cogl/cogl/cogl-matrix-mesa.c | 1698 ++++++++++++++++++++++++++ clutter/cogl/cogl/cogl-matrix-mesa.h | 226 ++++ clutter/cogl/cogl/cogl-matrix.c | 39 +- clutter/cogl/cogl/cogl-matrix.h | 6 +- 5 files changed, 1967 insertions(+), 4 deletions(-) create mode 100644 clutter/cogl/cogl/cogl-matrix-mesa.c create mode 100644 clutter/cogl/cogl/cogl-matrix-mesa.h diff --git a/clutter/cogl/cogl/Makefile.am b/clutter/cogl/cogl/Makefile.am index 8988f8d35..4e0c2a1e7 100644 --- a/clutter/cogl/cogl/Makefile.am +++ b/clutter/cogl/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/clutter/cogl/cogl/cogl-matrix-mesa.c b/clutter/cogl/cogl/cogl-matrix-mesa.c new file mode 100644 index 000000000..0057180bd --- /dev/null +++ b/clutter/cogl/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/clutter/cogl/cogl/cogl-matrix-mesa.h b/clutter/cogl/cogl/cogl-matrix-mesa.h new file mode 100644 index 000000000..1babce7f4 --- /dev/null +++ b/clutter/cogl/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/clutter/cogl/cogl/cogl-matrix.c b/clutter/cogl/cogl/cogl-matrix.c index 4cc635985..5cb121b53 100644 --- a/clutter/cogl/cogl/cogl-matrix.c +++ b/clutter/cogl/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/clutter/cogl/cogl/cogl-matrix.h b/clutter/cogl/cogl/cogl-matrix.h index e2c07f5c8..8f0f73de5 100644 --- a/clutter/cogl/cogl/cogl-matrix.h +++ b/clutter/cogl/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 8051596e96a296cf30887f6e522a288895b628f8 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Mon, 26 Oct 2009 11:01:33 +0000 Subject: [PATCH 23/39] [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. --- clutter/cogl/cogl/cogl-debug.c | 3 +- clutter/cogl/cogl/cogl-debug.h | 3 +- clutter/cogl/cogl/cogl-matrix-private.h | 47 +++++++++++++++++++++++++ clutter/cogl/cogl/cogl-matrix.c | 20 +++++++++++ clutter/cogl/cogl/cogl.c | 5 +++ 5 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 clutter/cogl/cogl/cogl-matrix-private.h diff --git a/clutter/cogl/cogl/cogl-debug.c b/clutter/cogl/cogl/cogl-debug.c index 6ca99c5b3..efc0146a3 100644 --- a/clutter/cogl/cogl/cogl-debug.c +++ b/clutter/cogl/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/clutter/cogl/cogl/cogl-debug.h b/clutter/cogl/cogl/cogl-debug.h index a1e5a358c..86d3b57db 100644 --- a/clutter/cogl/cogl/cogl-debug.h +++ b/clutter/cogl/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/clutter/cogl/cogl/cogl-matrix-private.h b/clutter/cogl/cogl/cogl-matrix-private.h new file mode 100644 index 000000000..4323815e9 --- /dev/null +++ b/clutter/cogl/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/clutter/cogl/cogl/cogl-matrix.c b/clutter/cogl/cogl/cogl-matrix.c index 5cb121b53..4a4fccf31 100644 --- a/clutter/cogl/cogl/cogl-matrix.c +++ b/clutter/cogl/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/clutter/cogl/cogl/cogl.c b/clutter/cogl/cogl/cogl.c index b1cef8b5b..7f556155a 100644 --- a/clutter/cogl/cogl/cogl.c +++ b/clutter/cogl/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 0369a1b84d725c6dbd90f9a9dd1753bea1f9c3e1 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Mon, 26 Oct 2009 17:51:34 +0000 Subject: [PATCH 24/39] [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. --- clutter/cogl/cogl/cogl-context.h | 3 -- clutter/cogl/cogl/cogl-matrix-stack.c | 11 +++++++ clutter/cogl/cogl/cogl-matrix-stack.h | 3 ++ clutter/cogl/cogl/cogl-matrix.c | 33 ++++++++++++------- clutter/cogl/cogl/cogl-matrix.h | 21 ++++++++++++ clutter/cogl/cogl/cogl.c | 47 +++++---------------------- 6 files changed, 65 insertions(+), 53 deletions(-) diff --git a/clutter/cogl/cogl/cogl-context.h b/clutter/cogl/cogl/cogl-context.h index 9d2aa077a..d10664f30 100644 --- a/clutter/cogl/cogl/cogl-context.h +++ b/clutter/cogl/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/clutter/cogl/cogl/cogl-matrix-stack.c b/clutter/cogl/cogl/cogl-matrix-stack.c index 4e2269dbc..c6236efd3 100644 --- a/clutter/cogl/cogl/cogl-matrix-stack.c +++ b/clutter/cogl/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/clutter/cogl/cogl/cogl-matrix-stack.h b/clutter/cogl/cogl/cogl-matrix-stack.h index 1762eda95..648eace84 100644 --- a/clutter/cogl/cogl/cogl-matrix-stack.h +++ b/clutter/cogl/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/clutter/cogl/cogl/cogl-matrix.c b/clutter/cogl/cogl/cogl-matrix.c index 4a4fccf31..5eb0f9782 100644 --- a/clutter/cogl/cogl/cogl-matrix.c +++ b/clutter/cogl/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/clutter/cogl/cogl/cogl-matrix.h b/clutter/cogl/cogl/cogl-matrix.h index 8f0f73de5..19e3e133b 100644 --- a/clutter/cogl/cogl/cogl-matrix.h +++ b/clutter/cogl/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/clutter/cogl/cogl/cogl.c b/clutter/cogl/cogl/cogl.c index 7f556155a..7631eee5b 100644 --- a/clutter/cogl/cogl/cogl.c +++ b/clutter/cogl/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 149e3e168a14049bb496a6438b711caa1fbe8129 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Fri, 30 Oct 2009 23:57:56 +0000 Subject: [PATCH 25/39] [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. --- clutter/cogl/cogl/cogl-clip-stack.c | 32 +++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/clutter/cogl/cogl/cogl-clip-stack.c b/clutter/cogl/cogl/cogl-clip-stack.c index cac156fa6..93900338e 100644 --- a/clutter/cogl/cogl/cogl-clip-stack.c +++ b/clutter/cogl/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 90dbae5aa9a31688f2dd73b75df67607795fa60e Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Sat, 31 Oct 2009 00:00:33 +0000 Subject: [PATCH 26/39] [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. --- clutter/cogl/cogl/cogl-draw-buffer.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/clutter/cogl/cogl/cogl-draw-buffer.c b/clutter/cogl/cogl/cogl-draw-buffer.c index 83ad3e313..2a7b75784 100644 --- a/clutter/cogl/cogl/cogl-draw-buffer.c +++ b/clutter/cogl/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 c7d229585f2e8dec30162cbad955230c25e16718 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Fri, 23 Oct 2009 00:16:30 +0100 Subject: [PATCH 27/39] [texture] Avoid redundant use of cogl_clip_stack_save when drawing offscreen Since cogl draw buffers now own their clip state the stage's clip state will automatically be saved and restored via the cogl_set_draw_buffer API. --- clutter/clutter-texture.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/clutter/clutter-texture.c b/clutter/clutter-texture.c index aab1435f2..abc713d4c 100644 --- a/clutter/clutter-texture.c +++ b/clutter/clutter-texture.c @@ -586,15 +586,9 @@ clutter_texture_paint (ClutterActor *self) COGL_BUFFER_BIT_DEPTH); cogl_disable_fog (); - /* Clear the clipping stack so that if the FBO actor is being - clipped then it won't affect drawing the source */ - cogl_clip_stack_save (); - /* Render out actor scene to fbo */ clutter_actor_paint (priv->fbo_source); - cogl_clip_stack_restore (); - /* Restore drawing to the frame buffer */ cogl_set_draw_buffer (COGL_WINDOW_BUFFER, COGL_INVALID_HANDLE); From 2762e6d9b90731fd05b46aac407cebd3ba372027 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Tue, 13 Oct 2009 22:22:39 +0100 Subject: [PATCH 28/39] [texture] push/pop draw buffer when painting actors to a texture When updating the FBO for a source actor (to support clutter_texture_new_from_actor()) we used to simply set an offscreen draw buffer to be current, paint the source actor and then explicitly set the window to be current again. This precluded chaining texture_new_from_actor though because updating another FBO associated with a source actor would end up restoring the window as the current buffer instead of the previous offscreen buffer. Now that we use Cogl's draw buffer stack; chaining clutter_texture_new_from_actor() should be possible. --- clutter/clutter-texture.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/clutter/clutter-texture.c b/clutter/clutter-texture.c index abc713d4c..246458de2 100644 --- a/clutter/clutter-texture.c +++ b/clutter/clutter-texture.c @@ -548,6 +548,7 @@ clutter_texture_paint (ClutterActor *self) clutter_shader_set_is_enabled (shader, FALSE); /* Redirect drawing to the fbo */ + cogl_push_draw_buffer (); cogl_set_draw_buffer (COGL_OFFSCREEN_BUFFER, priv->fbo_handle); if ((stage = clutter_actor_get_stage (self))) @@ -589,8 +590,8 @@ clutter_texture_paint (ClutterActor *self) /* Render out actor scene to fbo */ clutter_actor_paint (priv->fbo_source); - /* Restore drawing to the frame buffer */ - cogl_set_draw_buffer (COGL_WINDOW_BUFFER, COGL_INVALID_HANDLE); + /* Restore drawing to the previous draw buffer */ + cogl_pop_draw_buffer (); /* Restore the perspective matrix using cogl_perspective so that the inverse matrix will be right */ From 94a6028358caed32dba7ba37db9784c95d4690f2 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Thu, 24 Sep 2009 14:34:09 +0100 Subject: [PATCH 29/39] [texture] split out fbo update code from cluter_texture_paint To help keep clutter_texture_paint maintainable this splits out a big chunk of standalone code that's responsible for updating the fbo when clutter_texture_new_from_actor has been used. --- clutter/clutter-texture.c | 156 ++++++++++++++++++++------------------ 1 file changed, 81 insertions(+), 75 deletions(-) diff --git a/clutter/clutter-texture.c b/clutter/clutter-texture.c index 246458de2..857d16694 100644 --- a/clutter/clutter-texture.c +++ b/clutter/clutter-texture.c @@ -511,13 +511,92 @@ clutter_texture_set_fbo_projection (ClutterActor *self) perspective.z_far); } +static void +update_fbo (ClutterActor *self) +{ + ClutterTexture *texture = CLUTTER_TEXTURE (self); + ClutterTexturePrivate *priv = texture->priv; + ClutterMainContext *context; + ClutterShader *shader = NULL; + ClutterActor *stage = NULL; + ClutterPerspective perspective; + CoglColor transparent_col; + + context = _clutter_context_get_default (); + + if (context->shaders) + shader = clutter_actor_get_shader (context->shaders->data); + + /* Temporarily turn of the shader on the top of the context's + * shader stack, to restore the GL pipeline to it's natural state. + */ + if (shader) + clutter_shader_set_is_enabled (shader, FALSE); + + /* Redirect drawing to the fbo */ + cogl_push_draw_buffer (); + cogl_set_draw_buffer (COGL_OFFSCREEN_BUFFER, priv->fbo_handle); + + if ((stage = clutter_actor_get_stage (self))) + { + gfloat stage_width, stage_height; + ClutterActor *source_parent; + + clutter_stage_get_perspective (CLUTTER_STAGE (stage), &perspective); + clutter_actor_get_size (stage, &stage_width, &stage_height); + + /* Use below to set the modelview matrix as if the viewport + was still the same size as the stage */ + _cogl_setup_viewport (stage_width, stage_height, + perspective.fovy, + perspective.aspect, + perspective.z_near, + perspective.z_far); + + /* Use a projection matrix that makes the actor appear as it + would if it was rendered at its normal screen location */ + clutter_texture_set_fbo_projection (self); + + /* Reset the viewport to the size of the FBO */ + cogl_set_viewport (0, 0, priv->image_width, priv->image_height); + + /* Reapply the source's parent transformations */ + if ((source_parent = clutter_actor_get_parent (priv->fbo_source))) + _clutter_actor_apply_modelview_transform_recursive (source_parent, + NULL); + } + + /* cogl_clear is called to clear the buffers */ + cogl_color_set_from_4ub (&transparent_col, 0, 0, 0, 0); + cogl_clear (&transparent_col, + COGL_BUFFER_BIT_COLOR | + COGL_BUFFER_BIT_DEPTH); + cogl_disable_fog (); + + /* Render the actor to the fbo */ + clutter_actor_paint (priv->fbo_source); + + /* Restore drawing to the previous draw buffer */ + cogl_pop_draw_buffer (); + + /* Restore the perspective matrix using cogl_perspective so that + the inverse matrix will be right */ + cogl_perspective (perspective.fovy, + perspective.aspect, + perspective.z_near, + perspective.z_far); + + /* If there is a shader on top of the shader stack, turn it back on. */ + if (shader) + clutter_shader_set_is_enabled (shader, TRUE); +} + static void clutter_texture_paint (ClutterActor *self) { ClutterTexture *texture = CLUTTER_TEXTURE (self); ClutterTexturePrivate *priv = texture->priv; ClutterActorBox box = { 0, }; - CoglColor transparent_col; gfloat t_w, t_h; guint8 paint_opacity = clutter_actor_get_paint_opacity (self); @@ -530,80 +609,7 @@ clutter_texture_paint (ClutterActor *self) } if (priv->fbo_handle != COGL_INVALID_HANDLE) - { - ClutterMainContext *context; - ClutterShader *shader = NULL; - ClutterActor *stage = NULL; - ClutterPerspective perspective; - - context = _clutter_context_get_default (); - - if (context->shaders) - shader = clutter_actor_get_shader (context->shaders->data); - - /* Temporarily turn of the shader on the top of the context's - * shader stack, to restore the GL pipeline to it's natural state. - */ - if (shader) - clutter_shader_set_is_enabled (shader, FALSE); - - /* Redirect drawing to the fbo */ - cogl_push_draw_buffer (); - cogl_set_draw_buffer (COGL_OFFSCREEN_BUFFER, priv->fbo_handle); - - if ((stage = clutter_actor_get_stage (self))) - { - gfloat stage_width, stage_height; - ClutterActor *source_parent; - - clutter_stage_get_perspective (CLUTTER_STAGE (stage), &perspective); - clutter_actor_get_size (stage, &stage_width, &stage_height); - - /* Use below to set the modelview matrix as if the viewport - was still the same size as the stage */ - _cogl_setup_viewport (stage_width, stage_height, - perspective.fovy, - perspective.aspect, - perspective.z_near, - perspective.z_far); - - /* Use a projection matrix that makes the actor appear as it - would if it was rendered at its normal screen location */ - clutter_texture_set_fbo_projection (self); - - /* Reset the viewport to the size of the FBO */ - cogl_set_viewport (0, 0, priv->image_width, priv->image_height); - - /* Reapply the source's parent transformations */ - if ((source_parent = clutter_actor_get_parent (priv->fbo_source))) - _clutter_actor_apply_modelview_transform_recursive (source_parent, - NULL); - } - - /* cogl_clear is called to clear the buffers */ - cogl_color_set_from_4ub (&transparent_col, 0, 0, 0, 0); - cogl_clear (&transparent_col, - COGL_BUFFER_BIT_COLOR | - COGL_BUFFER_BIT_DEPTH); - cogl_disable_fog (); - - /* Render out actor scene to fbo */ - clutter_actor_paint (priv->fbo_source); - - /* Restore drawing to the previous draw buffer */ - cogl_pop_draw_buffer (); - - /* Restore the perspective matrix using cogl_perspective so that - the inverse matrix will be right */ - cogl_perspective (perspective.fovy, - perspective.aspect, - perspective.z_near, - perspective.z_far); - - /* If there is a shader on top of the shader stack, turn it back on. */ - if (shader) - clutter_shader_set_is_enabled (shader, TRUE); - } + update_fbo (self); CLUTTER_NOTE (PAINT, "painting texture '%s'", From 309f852efbde6a3e025225d01293509c28989d0c Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Mon, 2 Nov 2009 01:14:50 +0000 Subject: [PATCH 30/39] [texture] switch to a new design for handling offscreen rendering For supporting clutter_texture_new_from_actor(): when updating a ClutterTexture's fbo we previously set up an offset frustum in the perspective matrix before rendering source actors to an offscreen draw buffer so as to give a perspective as if it were being drawn at its original stage location. Now that Cogl supports offset viewports there is a simpler way... When we come to render the source actor to our offscreen draw buffer we now copy the projection matrix from the stage; we create a viewport that's also the same size as the stage (though larger than the offscreen draw buffer) and as before we apply the modelview transformations of the source actors ancestry before painting it. The only trick we need now is to offset the viewport according to the transformed (to screen space) allocation of the source actor (something we required previously too). We negatively offset the stage sized viewport such that the smaller offscreen draw buffer is positioned to sit underneath the source actor in stage coordinates. --- clutter/clutter-texture.c | 100 +++++++++++++------------------------- 1 file changed, 35 insertions(+), 65 deletions(-) diff --git a/clutter/clutter-texture.c b/clutter/clutter-texture.c index 857d16694..6d240a05f 100644 --- a/clutter/clutter-texture.c +++ b/clutter/clutter-texture.c @@ -449,66 +449,32 @@ clutter_texture_allocate (ClutterActor *self, } static void -clutter_texture_set_fbo_projection (ClutterActor *self) +set_viewport_with_buffer_under_fbo_source (ClutterActor *fbo_source, + int viewport_width, + int viewport_height) { - ClutterTexturePrivate *priv = CLUTTER_TEXTURE (self)->priv; ClutterVertex verts[4]; - gfloat viewport[4]; - gfloat x_min, x_max, y_min, y_max; - gfloat tx_min, tx_max, ty_min, ty_max; - gfloat tan_angle, near_size; - ClutterPerspective perspective; - ClutterStage *stage; + float x_min = G_MAXFLOAT, y_min = G_MAXFLOAT; int i; - /* Get the bounding rectangle of the source as drawn in screen - coordinates */ - clutter_actor_get_abs_allocation_vertices (priv->fbo_source, verts); + /* Get the actors allocation transformed into screen coordinates. + * + * XXX: Note: this may not be a bounding box for the actor, since an + * actor with depth may escape the box due to its perspective + * projection. */ + clutter_actor_get_abs_allocation_vertices (fbo_source, verts); - x_min = x_max = verts[0].x; - y_min = y_max = verts[0].y; - - for (i = 1; i < G_N_ELEMENTS (verts); ++i) + for (i = 0; i < G_N_ELEMENTS (verts); ++i) { if (verts[i].x < x_min) x_min = verts[i].x; - - if (verts[i].x > x_max) - x_max = verts[i].x; - if (verts[i].y < y_min) y_min = verts[i].y; - - if (verts[i].y > y_max) - y_max = verts[i].y; } - stage = CLUTTER_STAGE (clutter_actor_get_stage (self)); - clutter_stage_get_perspective (stage, &perspective); - - /* Convert the coordinates back to [-1,1] range */ - cogl_get_viewport (viewport); - - tx_min = (x_min / viewport[2]) - * 2 - 1.0; - tx_max = (x_max / viewport[2]) - * 2 - 1.0; - ty_min = (y_min / viewport[3]) - * 2 - 1.0; - ty_max = (y_max / viewport[3]) - * 2 - 1.0; - - /* Set up a projection matrix so that the actor will be projected as - if it was drawn at its original location */ - tan_angle = tanf ((perspective.fovy / 2) * (G_PI / 180.0)); - near_size = perspective.z_near * tan_angle; - - cogl_frustum ((tx_min * near_size), - (tx_max * near_size), - (-ty_min * near_size), - (-ty_max * near_size), - perspective.z_near, - perspective.z_far); + /* translate the viewport so that the source actor lands on the + * sub-region backed by the offscreen draw buffer... */ + cogl_set_viewport (-x_min, -y_min, viewport_width, viewport_height); } static void @@ -527,8 +493,8 @@ update_fbo (ClutterActor *self) if (context->shaders) shader = clutter_actor_get_shader (context->shaders->data); - /* Temporarily turn of the shader on the top of the context's - * shader stack, to restore the GL pipeline to it's natural state. + /* Temporarily turn off the shader on the top of the context's shader stack, + * to restore the GL pipeline to it's natural state. */ if (shader) clutter_shader_set_is_enabled (shader, FALSE); @@ -542,23 +508,34 @@ update_fbo (ClutterActor *self) gfloat stage_width, stage_height; ClutterActor *source_parent; + /* We copy the projection and modelview matrices from the stage to + * the offscreen draw buffer and create a viewport larger than the + * offscreen draw buffer - the same size as the stage. + * + * The fbo source actor gets rendered into this stage size viewport at the + * same position it normally would after applying all it's usual parent + * transforms and it's own scale and rotate transforms etc. + * + * The viewport is offset such that the offscreen buffer will be positioned + * under the actor. + */ + clutter_stage_get_perspective (CLUTTER_STAGE (stage), &perspective); clutter_actor_get_size (stage, &stage_width, &stage_height); - /* Use below to set the modelview matrix as if the viewport - was still the same size as the stage */ + /* Set the projection matrix modelview matrix and viewport size as + * they are for the stage... */ _cogl_setup_viewport (stage_width, stage_height, perspective.fovy, perspective.aspect, perspective.z_near, perspective.z_far); - /* Use a projection matrix that makes the actor appear as it - would if it was rendered at its normal screen location */ - clutter_texture_set_fbo_projection (self); - - /* Reset the viewport to the size of the FBO */ - cogl_set_viewport (0, 0, priv->image_width, priv->image_height); + /* Negatively offset the viewport so that the offscreen draw buffer is + * position underneath the fbo_source actor... */ + set_viewport_with_buffer_under_fbo_source (priv->fbo_source, + stage_width, + stage_height); /* Reapply the source's parent transformations */ if ((source_parent = clutter_actor_get_parent (priv->fbo_source))) @@ -579,13 +556,6 @@ update_fbo (ClutterActor *self) /* Restore drawing to the previous draw buffer */ cogl_pop_draw_buffer (); - /* Restore the perspective matrix using cogl_perspective so that - the inverse matrix will be right */ - cogl_perspective (perspective.fovy, - perspective.aspect, - perspective.z_near, - perspective.z_far); - /* If there is a shader on top of the shader stack, turn it back on. */ if (shader) clutter_shader_set_is_enabled (shader, TRUE); From ae57c30e937d90eb1e541edd36ae9f81aedc2854 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Sun, 1 Nov 2009 23:56:03 +0000 Subject: [PATCH 31/39] [texture] fix rounding when calculating update_fbo viewport offset When rendering to an fbo for supporting clutter_texture_new_from_actor we render to an fbo with the same size as the source actor, but with a viewport the same size as the stage. We offset the viewport so when we render the source actor in its normal transformed stage position it lands on the fbo. Previously we were rounding the transformed position given as a float by truncating the fraction (just using a C cast) but that resulted in an incorrect pixel offset when rendering offscreen depending on the source position. We now simply + 0.5 before casting (or -0.5 for negative numbers) --- clutter/clutter-texture.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/clutter/clutter-texture.c b/clutter/clutter-texture.c index 6d240a05f..abe0f8485 100644 --- a/clutter/clutter-texture.c +++ b/clutter/clutter-texture.c @@ -455,6 +455,7 @@ set_viewport_with_buffer_under_fbo_source (ClutterActor *fbo_source, { ClutterVertex verts[4]; float x_min = G_MAXFLOAT, y_min = G_MAXFLOAT; + int x_offset, y_offset; int i; /* Get the actors allocation transformed into screen coordinates. @@ -472,9 +473,19 @@ set_viewport_with_buffer_under_fbo_source (ClutterActor *fbo_source, y_min = verts[i].y; } + /* XXX: It's not good enough to round by simply truncating the fraction here + * via a cast, as it results in offscreen rendering being offset by 1 pixel + * in many cases... */ +#define ROUND(x) ((x) >= 0 ? (long)((x) + 0.5) : (long)((x) - 0.5)) + + x_offset = ROUND (-x_min); + y_offset = ROUND (-y_min); + +#undef ROUND + /* translate the viewport so that the source actor lands on the * sub-region backed by the offscreen draw buffer... */ - cogl_set_viewport (-x_min, -y_min, viewport_width, viewport_height); + cogl_set_viewport (x_offset, y_offset, viewport_width, viewport_height); } static void @@ -543,6 +554,7 @@ update_fbo (ClutterActor *self) NULL); } + /* cogl_clear is called to clear the buffers */ cogl_color_set_from_4ub (&transparent_col, 0, 0, 0, 0); cogl_clear (&transparent_col, From fec13f6202067f0700514db6676d3984b78a26df Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Sat, 31 Oct 2009 00:01:44 +0000 Subject: [PATCH 32/39] [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. --- clutter/cogl/cogl/cogl-texture-2d-sliced.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clutter/cogl/cogl/cogl-texture-2d-sliced.c b/clutter/cogl/cogl/cogl-texture-2d-sliced.c index 9b70860e8..52eec49fe 100644 --- a/clutter/cogl/cogl/cogl-texture-2d-sliced.c +++ b/clutter/cogl/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); From b41a81fb0868e51bf03d25446f8cc9748fcd6bc7 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Thu, 17 Sep 2009 14:25:29 +0100 Subject: [PATCH 33/39] Add a conformance test for clutter_texture_new_from_actor This contains four tests :- - A regular onscreen source with a clone next to it - An offscreen source with a clone. This is currently commented out because it no longer works. - An onscreen source with a rectangular clip and a clone. - An onscreen source with a clip from a path and a clone. The sources are all a 2x2 grid of colors. Each clone is tested that it either contains the color that should be at that grid position or that the stage color is showing through if the source is clipped. --- tests/conform/Makefile.am | 1 + tests/conform/test-conform-main.c | 1 + tests/conform/test-texture-fbo.c | 255 ++++++++++++++++++++++++++++++ 3 files changed, 257 insertions(+) create mode 100644 tests/conform/test-texture-fbo.c diff --git a/tests/conform/Makefile.am b/tests/conform/Makefile.am index 7a5b6f971..442800d63 100644 --- a/tests/conform/Makefile.am +++ b/tests/conform/Makefile.am @@ -35,6 +35,7 @@ test_conformance_SOURCES = \ test-materials.c \ test-group.c \ test-actor-size.c \ + test-texture-fbo.c \ $(NULL) # For convenience, this provides a way to easily run individual unit tests: diff --git a/tests/conform/test-conform-main.c b/tests/conform/test-conform-main.c index 2b2624434..3379fbabf 100644 --- a/tests/conform/test-conform-main.c +++ b/tests/conform/test-conform-main.c @@ -147,6 +147,7 @@ main (int argc, char **argv) TEST_CONFORM_SIMPLE ("/texture", test_backface_culling); TEST_CONFORM_SIMPLE ("/texture", test_npot_texture); TEST_CONFORM_SIMPLE ("/texture", test_premult); + TEST_CONFORM_SIMPLE ("/texture", test_texture_fbo); TEST_CONFORM_SIMPLE ("/path", test_path); diff --git a/tests/conform/test-texture-fbo.c b/tests/conform/test-texture-fbo.c new file mode 100644 index 000000000..b11bde080 --- /dev/null +++ b/tests/conform/test-texture-fbo.c @@ -0,0 +1,255 @@ + +#include +#include + +#include "test-conform-common.h" + +#define SOURCE_SIZE 32 +#define SOURCE_DIVISIONS_X 2 +#define SOURCE_DIVISIONS_Y 2 +#define DIVISION_WIDTH (SOURCE_SIZE / SOURCE_DIVISIONS_X) +#define DIVISION_HEIGHT (SOURCE_SIZE / SOURCE_DIVISIONS_Y) + +static const ClutterColor +corner_colors[SOURCE_DIVISIONS_X * SOURCE_DIVISIONS_Y] = + { + { 0xff, 0x00, 0x00, 0xff }, + { 0x00, 0xff, 0x00, 0xff }, + { 0x00, 0x00, 0xff, 0xff }, + { 0xff, 0x00, 0xff, 0xff } + }; + +static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff }; + +typedef struct _TestState +{ + ClutterActor *stage; + guint frame; +} TestState; + +static ClutterActor * +create_source (void) +{ + int x, y; + ClutterActor *group = clutter_group_new (); + + /* Create a group with a different coloured rectangle at each + corner */ + for (y = 0; y < SOURCE_DIVISIONS_Y; y++) + for (x = 0; x < SOURCE_DIVISIONS_X; x++) + { + ClutterActor *rect = clutter_rectangle_new (); + clutter_actor_set_size (rect, DIVISION_WIDTH, DIVISION_HEIGHT); + clutter_actor_set_position (rect, + DIVISION_WIDTH * x, + DIVISION_HEIGHT * y); + clutter_rectangle_set_color (CLUTTER_RECTANGLE (rect), + corner_colors + + (y * SOURCE_DIVISIONS_X + x)); + clutter_container_add (CLUTTER_CONTAINER (group), rect, NULL); + } + + return group; +} + +static void +pre_paint_clip_cb (void) +{ + /* Generate a clip path that clips out the top left division */ + cogl_path_move_to (DIVISION_WIDTH, 0); + cogl_path_line_to (SOURCE_SIZE, 0); + cogl_path_line_to (SOURCE_SIZE, SOURCE_SIZE); + cogl_path_line_to (0, SOURCE_SIZE); + cogl_path_line_to (0, DIVISION_HEIGHT); + cogl_path_line_to (DIVISION_WIDTH, DIVISION_HEIGHT); + cogl_path_close (); + cogl_clip_push_from_path (); +} + +static void +post_paint_clip_cb (void) +{ + cogl_clip_pop (); +} + +static gboolean +validate_part (TestState *state, + int xpos, int ypos, + int clip_flags) +{ + int x, y; + gboolean pass = TRUE; + + /* Check whether the center of each division is the right color */ + for (y = 0; y < SOURCE_DIVISIONS_Y; y++) + for (x = 0; x < SOURCE_DIVISIONS_X; x++) + { + guchar *pixels; + const ClutterColor *correct_color; + + /* Read the center pixels of this division */ + pixels = clutter_stage_read_pixels (CLUTTER_STAGE (state->stage), + x * DIVISION_WIDTH + + DIVISION_WIDTH / 2 + xpos, + y * DIVISION_HEIGHT + + DIVISION_HEIGHT / 2 + ypos, + 1, 1); + + /* If this division is clipped then it should be the stage + color */ + if ((clip_flags & (1 << ((y * SOURCE_DIVISIONS_X) + x)))) + correct_color = &stage_color; + else + /* Otherwise it should be the color for this division */ + correct_color = corner_colors + (y * SOURCE_DIVISIONS_X) + x; + + if (pixels == NULL || + pixels[0] != correct_color->red || + pixels[1] != correct_color->green || + pixels[2] != correct_color->blue) + pass = FALSE; + + g_free (pixels); + } + + return pass; +} + +static void +validate_result (TestState *state) +{ + int ypos = 0; + + if (g_test_verbose ()) + g_print ("Testing onscreen clone...\n"); + g_assert (validate_part (state, SOURCE_SIZE, ypos * SOURCE_SIZE, 0)); + ypos++; + +#if 0 /* this doesn't work */ + if (g_test_verbose ()) + g_print ("Testing offscreen clone...\n"); + g_assert (validate_part (state, SOURCE_SIZE, ypos * SOURCE_SIZE, 0)); +#endif + ypos++; + + if (g_test_verbose ()) + g_print ("Testing onscreen clone with rectangular clip...\n"); + g_assert (validate_part (state, SOURCE_SIZE, ypos * SOURCE_SIZE, ~1)); + ypos++; + + if (g_test_verbose ()) + g_print ("Testing onscreen clone with path clip...\n"); + g_assert (validate_part (state, SOURCE_SIZE, ypos * SOURCE_SIZE, 1)); + ypos++; + + /* Comment this out if you want visual feedback of what this test + * paints. + */ + clutter_main_quit (); +} + +static void +on_paint (ClutterActor *actor, TestState *state) +{ + int frame_num; + + /* XXX: Experiments have shown that for some buggy drivers, when using + * glReadPixels there is some kind of race, so we delay our test for a + * few frames and a few seconds: + */ + /* Need to increment frame first because clutter_stage_read_pixels + fires a redraw */ + frame_num = state->frame++; + if (frame_num == 2) + validate_result (state); + else if (frame_num < 2) + g_usleep (G_USEC_PER_SEC); +} + +static gboolean +queue_redraw (gpointer stage) +{ + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); + + return TRUE; +} + +void +test_texture_fbo (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + TestState state; + guint idle_source; + ClutterActor *actor; + int ypos = 0; + + state.frame = 0; + + state.stage = clutter_stage_get_default (); + + clutter_stage_set_color (CLUTTER_STAGE (state.stage), &stage_color); + + /* Onscreen source with clone next to it */ + actor = create_source (); + clutter_container_add (CLUTTER_CONTAINER (state.stage), actor, NULL); + clutter_actor_set_position (actor, 0, ypos * SOURCE_SIZE); + actor = clutter_texture_new_from_actor (actor); + clutter_actor_set_position (actor, SOURCE_SIZE, ypos * SOURCE_SIZE); + clutter_container_add (CLUTTER_CONTAINER (state.stage), actor, NULL); + ypos++; + + /* Offscreen source with clone */ +#if 0 /* this doesn't work */ + actor = create_source (); + actor = clutter_texture_new_from_actor (actor); + clutter_actor_set_position (actor, SOURCE_SIZE, ypos * SOURCE_SIZE); + clutter_container_add (CLUTTER_CONTAINER (state.stage), actor, NULL); +#endif + ypos++; + + /* Source clipped to the top left division */ + actor = create_source (); + clutter_container_add (CLUTTER_CONTAINER (state.stage), actor, NULL); + clutter_actor_set_position (actor, 0, ypos * SOURCE_SIZE); + clutter_actor_set_clip (actor, 0, 0, DIVISION_WIDTH, DIVISION_HEIGHT); + actor = clutter_texture_new_from_actor (actor); + clutter_actor_set_position (actor, SOURCE_SIZE, ypos * SOURCE_SIZE); + clutter_container_add (CLUTTER_CONTAINER (state.stage), actor, NULL); + ypos++; + + /* Source clipped to everything but top left division using a + path */ + actor = create_source (); + clutter_container_add (CLUTTER_CONTAINER (state.stage), actor, NULL); + clutter_actor_set_position (actor, 0, ypos * SOURCE_SIZE); + g_signal_connect (actor, "paint", + G_CALLBACK (pre_paint_clip_cb), NULL); + g_signal_connect_after (actor, "paint", + G_CALLBACK (post_paint_clip_cb), NULL); + actor = clutter_texture_new_from_actor (actor); + clutter_actor_set_position (actor, SOURCE_SIZE, ypos * SOURCE_SIZE); + clutter_container_add (CLUTTER_CONTAINER (state.stage), actor, NULL); + ypos++; + + /* We force continuous redrawing of the stage, since we need to skip + * the first few frames, and we wont be doing anything else that + * will trigger redrawing. */ + idle_source = g_idle_add (queue_redraw, state.stage); + + g_signal_connect_after (state.stage, "paint", G_CALLBACK (on_paint), &state); + + clutter_actor_show_all (state.stage); + + clutter_main (); + + g_source_remove (idle_source); + + /* Remove all of the actors from the stage */ + clutter_container_foreach (CLUTTER_CONTAINER (state.stage), + (ClutterCallback) clutter_actor_destroy, + NULL); + + if (g_test_verbose ()) + g_print ("OK\n"); +} + From bc24190b9ac32405d4be24d84968357921c7f7e2 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Mon, 2 Nov 2009 01:23:25 +0000 Subject: [PATCH 34/39] [test-texture-fbo] comment the colors defined in corner_colors It helps to be able to quickly glance at the definition to see which quadrant of the test actor should be which color, so when debugging a problem and looking at the visual output you can easily verify if it's being flipped upside down/left to right. --- tests/conform/test-texture-fbo.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/conform/test-texture-fbo.c b/tests/conform/test-texture-fbo.c index b11bde080..958fc2932 100644 --- a/tests/conform/test-texture-fbo.c +++ b/tests/conform/test-texture-fbo.c @@ -13,10 +13,10 @@ static const ClutterColor corner_colors[SOURCE_DIVISIONS_X * SOURCE_DIVISIONS_Y] = { - { 0xff, 0x00, 0x00, 0xff }, - { 0x00, 0xff, 0x00, 0xff }, - { 0x00, 0x00, 0xff, 0xff }, - { 0xff, 0x00, 0xff, 0xff } + { 0xff, 0x00, 0x00, 0xff }, /* red top left */ + { 0x00, 0xff, 0x00, 0xff }, /* green top right */ + { 0x00, 0x00, 0xff, 0xff }, /* blue bottom left */ + { 0xff, 0x00, 0xff, 0xff } /* purple bottom right */ }; static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff }; From 369e68630daf7f833da986a19ad17043b43d61e3 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Wed, 21 Oct 2009 16:06:43 +0100 Subject: [PATCH 35/39] [tests] test-cogl-viewport tests semantics of over/under size viewports Unlike OpenGL Cogl puts the origin of windows/viewports at the top left instead of bottom left. This test verifies that we correctly translate Cogl viewports to OpenGL viewports for the awkward cases where the given viewport has an offset and/or the viewport has a different size to the current draw buffer. --- tests/conform/Makefile.am | 1 + tests/conform/test-cogl-viewport.c | 417 +++++++++++++++++++++++++++++ tests/conform/test-conform-main.c | 2 + 3 files changed, 420 insertions(+) create mode 100644 tests/conform/test-cogl-viewport.c diff --git a/tests/conform/Makefile.am b/tests/conform/Makefile.am index 442800d63..177c0654b 100644 --- a/tests/conform/Makefile.am +++ b/tests/conform/Makefile.am @@ -36,6 +36,7 @@ test_conformance_SOURCES = \ test-group.c \ test-actor-size.c \ test-texture-fbo.c \ + test-cogl-viewport.c \ $(NULL) # For convenience, this provides a way to easily run individual unit tests: diff --git a/tests/conform/test-cogl-viewport.c b/tests/conform/test-cogl-viewport.c new file mode 100644 index 000000000..1188b3bd2 --- /dev/null +++ b/tests/conform/test-cogl-viewport.c @@ -0,0 +1,417 @@ + +#include +#include + +#include "test-conform-common.h" + +#define RED 0 +#define GREEN 1 +#define BLUE 2 +#define ALPHA 3 + +#define DRAW_BUFFER_WIDTH 640 +#define DRAW_BUFFER_HEIGHT 480 + +static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff }; + +static void +assert_region_color (int x, + int y, + int width, + int height, + guint8 red, + guint8 green, + guint8 blue, + guint8 alpha) +{ + guint8 *data = g_malloc0 (width * height * 4); + cogl_read_pixels (x, y, width, height, + COGL_READ_PIXELS_COLOR_BUFFER, + COGL_PIXEL_FORMAT_RGBA_8888, + data); + for (y = 0; y < height; y++) + for (x = 0; x < width; x++) + { + guint8 *pixel = &data[y*width*4 + x*4]; +#if 1 + g_assert (pixel[RED] == red && + pixel[GREEN] == green && + pixel[BLUE] == blue && + pixel[ALPHA] == alpha); +#endif + } + g_free (data); +} + +static void +assert_rectangle_color_and_black_border (int x, + int y, + int width, + int height, + guint8 red, + guint8 green, + guint8 blue) +{ + /* check the rectangle itself... */ + assert_region_color (x, y, width, height, red, green, blue, 0xff); + /* black to left of the rectangle */ + assert_region_color (x-10, y-10, 10, height+20, 0x00, 0x00, 0x00, 0xff); + /* black to right of the rectangle */ + assert_region_color (x+width, y-10, 10, height+20, 0x00, 0x00, 0x00, 0xff); + /* black above the rectangle */ + assert_region_color (x-10, y-10, width+20, 10, 0x00, 0x00, 0x00, 0xff); + /* and black below the rectangle */ + assert_region_color (x-10, y+height, width+20, 10, 0x00, 0x00, 0x00, 0xff); +} + + +static void +on_paint (ClutterActor *actor, void *state) +{ + float saved_viewport[4]; + CoglMatrix saved_projection; + CoglMatrix projection; + CoglMatrix modelview; + guchar *data; + CoglHandle tex; + CoglHandle offscreen; + CoglColor black; + float x0; + float y0; + float width; + float height; + + /* for clearing the offscreen draw buffer to black... */ + cogl_color_set_from_4ub (&black, 0x00, 0x00, 0x00, 0xff); + + cogl_get_viewport (saved_viewport); + cogl_get_projection_matrix (&saved_projection); + cogl_push_matrix (); + + cogl_matrix_init_identity (&projection); + cogl_matrix_init_identity (&modelview); + + cogl_set_projection_matrix (&projection); + cogl_set_modelview_matrix (&modelview); + + /* - Create a 100x200 viewport (i.e. smaller than the onscreen draw buffer) + * and position it a (20, 10) inside the draw buffer. + * - Fill the whole viewport with a purple rectangle + * - Verify that the draw buffer is black with a 100x200 purple rectangle at + * (20, 10) + */ + cogl_set_viewport (20, /* x */ + 10, /* y */ + 100, /* width */ + 200); /* height */ + /* clear everything... */ + cogl_clear (&black, COGL_BUFFER_BIT_COLOR); + /* fill the viewport with purple.. */ + cogl_set_source_color4ub (0xff, 0x00, 0xff, 0xff); + cogl_rectangle (-1, 1, 1, -1); + assert_rectangle_color_and_black_border (20, 10, 100, 200, + 0xff, 0x00, 0xff); + + + /* - Create a viewport twice the size of the onscreen draw buffer with + * a negative offset positioning it at (-20, -10) relative to the + * buffer itself. + * - Draw a 100x200 green rectangle at (40, 20) within the viewport (which + * is (20, 10) within the draw buffer) + * - Verify that the draw buffer is black with a 100x200 green rectangle at + * (20, 10) + */ + cogl_set_viewport (-20, /* x */ + -10, /* y */ + DRAW_BUFFER_WIDTH * 2, /* width */ + DRAW_BUFFER_HEIGHT * 2); /* height */ + /* clear everything... */ + cogl_clear (&black, COGL_BUFFER_BIT_COLOR); + /* draw a 100x200 green rectangle offset into the viewport such that its + * top left corner should be found at (20, 10) in the offscreen buffer */ + /* (offset 40 pixels right from the left of the viewport) */ + x0 = -1.0f + (1.0f / DRAW_BUFFER_WIDTH) * 40.f; + /* (offset 20 pixels down from the top of the viewport) */ + y0 = 1.0f - (1.0f / DRAW_BUFFER_HEIGHT) * 20.0f; + width = (1.0f / DRAW_BUFFER_WIDTH) * 100; + height = (1.0f / DRAW_BUFFER_HEIGHT) * 200; + cogl_set_source_color4ub (0x00, 0xff, 0x00, 0xff); + cogl_rectangle (x0, y0, x0 + width, y0 - height); + assert_rectangle_color_and_black_border (20, 10, 100, 200, + 0x00, 0xff, 0x00); + + + /* - Create a 200x400 viewport and position it a (20, 10) inside the draw + * buffer. + * - Push a 100x200 window space clip rectangle at (20, 10) + * - Fill the whole viewport with a blue rectangle + * - Verify that the draw buffer is black with a 100x200 blue rectangle at + * (20, 10) + */ + cogl_set_viewport (20, /* x */ + 10, /* y */ + 200, /* width */ + 400); /* height */ + /* clear everything... */ + cogl_clear (&black, COGL_BUFFER_BIT_COLOR); + cogl_clip_push_window_rect (20, 10, 100, 200); + /* fill the viewport with blue.. */ + cogl_set_source_color4ub (0x00, 0x00, 0xff, 0xff); + cogl_rectangle (-1, 1, 1, -1); + cogl_clip_pop (); + assert_rectangle_color_and_black_border (20, 10, 100, 200, + 0x00, 0x00, 0xff); + + + /* - Create a 200x400 viewport and position it a (20, 10) inside the draw + * buffer. + * - Push a 100x200 model space clip rectangle at (20, 10) in the viewport + * (i.e. (40, 20) inside the draw buffer) + * - Fill the whole viewport with a green rectangle + * - Verify that the draw buffer is black with a 100x200 green rectangle at + * (40, 20) + */ + cogl_set_viewport (20, /* x */ + 10, /* y */ + 200, /* width */ + 400); /* height */ + /* clear everything... */ + cogl_clear (&black, COGL_BUFFER_BIT_COLOR); + /* figure out where to position our clip rectangle in model space + * coordinates... */ + /* (offset 40 pixels right from the left of the viewport) */ + x0 = -1.0f + (2.0f / 200) * 20.f; + /* (offset 20 pixels down from the top of the viewport) */ + y0 = 1.0f - (2.0f / 400) * 10.0f; + width = (2.0f / 200) * 100; + height = (2.0f / 400) * 200; + /* add the clip rectangle... */ + cogl_push_matrix (); + cogl_translate (x0 + (width/2.0), y0 - (height/2.0), 0); + /* XXX: Rotate just enough to stop Cogl from converting our model space + * rectangle into a window space rectangle.. */ + cogl_rotate (0.1, 0, 0, 1); + cogl_clip_push (-(width/2.0), -(height/2.0), width, height); + cogl_pop_matrix (); + /* fill the viewport with green.. */ + cogl_set_source_color4ub (0x00, 0xff, 0x00, 0xff); + cogl_rectangle (-1, 1, 1, -1); + cogl_clip_pop (); + assert_rectangle_color_and_black_border (40, 20, 100, 200, + 0x00, 0xff, 0x00); + + + /* Set the viewport to something specific so we can verify that it gets + * restored after we are done testing with an offscreen draw buffer... */ + cogl_set_viewport (20, 10, 100, 200); + + /* + * Next test offscreen drawing... + */ + cogl_push_draw_buffer (); + + data = g_malloc (DRAW_BUFFER_WIDTH * 4 * DRAW_BUFFER_HEIGHT); + tex = cogl_texture_new_from_data (DRAW_BUFFER_WIDTH, DRAW_BUFFER_HEIGHT, + COGL_TEXTURE_NO_SLICING, + COGL_PIXEL_FORMAT_RGBA_8888, /* data fmt */ + COGL_PIXEL_FORMAT_ANY, /* internal fmt */ + DRAW_BUFFER_WIDTH * 4, /* rowstride */ + data); + g_free (data); + offscreen = cogl_offscreen_new_to_texture (tex); + + cogl_set_draw_buffer (0 /* unused */, offscreen); + + + /* - Create a 100x200 viewport (i.e. smaller than the offscreen draw buffer) + * and position it a (20, 10) inside the draw buffer. + * - Fill the whole viewport with a blue rectangle + * - Verify that the draw buffer is black with a 100x200 blue rectangle at + * (20, 10) + */ + cogl_set_viewport (20, /* x */ + 10, /* y */ + 100, /* width */ + 200); /* height */ + /* clear everything... */ + cogl_clear (&black, COGL_BUFFER_BIT_COLOR); + /* fill the viewport with blue.. */ + cogl_set_source_color4ub (0x00, 0x00, 0xff, 0xff); + cogl_rectangle (-1, 1, 1, -1); + assert_rectangle_color_and_black_border (20, 10, 100, 200, + 0x00, 0x00, 0xff); + + + /* - Create a viewport twice the size of the offscreen draw buffer with + * a negative offset positioning it at (-20, -10) relative to the + * buffer itself. + * - Draw a 100x200 red rectangle at (40, 20) within the viewport (which + * is (20, 10) within the draw buffer) + * - Verify that the draw buffer is black with a 100x200 red rectangle at + * (20, 10) + */ + cogl_set_viewport (-20, /* x */ + -10, /* y */ + DRAW_BUFFER_WIDTH * 2, /* width */ + DRAW_BUFFER_HEIGHT * 2); /* height */ + /* clear everything... */ + cogl_clear (&black, COGL_BUFFER_BIT_COLOR); + /* draw a 100x200 red rectangle offset into the viewport such that its + * top left corner should be found at (20, 10) in the offscreen buffer */ + /* (offset 40 pixels right from the left of the viewport) */ + x0 = -1.0f + (1.0f / DRAW_BUFFER_WIDTH) * 40.f; + /* (offset 20 pixels down from the top of the viewport) */ + y0 = 1.0f - (1.0f / DRAW_BUFFER_HEIGHT) * 20.0f; + width = (1.0f / DRAW_BUFFER_WIDTH) * 100; + height = (1.0f / DRAW_BUFFER_HEIGHT) * 200; + cogl_set_source_color4ub (0xff, 0x00, 0x00, 0xff); + cogl_rectangle (x0, y0, x0 + width, y0 - height); + assert_rectangle_color_and_black_border (20, 10, 100, 200, + 0xff, 0x00, 0x00); + + + /* - Create a 200x400 viewport and position it a (20, 10) inside the draw + * buffer. + * - Push a 100x200 window space clip rectangle at (20, 10) + * - Fill the whole viewport with a blue rectangle + * - Verify that the draw buffer is black with a 100x200 blue rectangle at + * (20, 10) + */ + cogl_set_viewport (20, /* x */ + 10, /* y */ + 200, /* width */ + 400); /* height */ + /* clear everything... */ + cogl_clear (&black, COGL_BUFFER_BIT_COLOR); + cogl_clip_push_window_rect (20, 10, 100, 200); + /* fill the viewport with blue.. */ + cogl_set_source_color4ub (0x00, 0x00, 0xff, 0xff); + cogl_rectangle (-1, 1, 1, -1); + cogl_clip_pop (); + assert_rectangle_color_and_black_border (20, 10, 100, 200, + 0x00, 0x00, 0xff); + + + /* - Create a 200x400 viewport and position it a (20, 10) inside the draw + * buffer. + * - Push a 100x200 model space clip rectangle at (20, 10) in the viewport + * (i.e. (40, 20) inside the draw buffer) + * - Fill the whole viewport with a green rectangle + * - Verify that the draw buffer is black with a 100x200 green rectangle at + * (40, 20) + */ + cogl_set_viewport (20, /* x */ + 10, /* y */ + 200, /* width */ + 400); /* height */ + /* clear everything... */ + cogl_clear (&black, COGL_BUFFER_BIT_COLOR); + /* figure out where to position our clip rectangle in model space + * coordinates... */ + /* (offset 40 pixels right from the left of the viewport) */ + x0 = -1.0f + (2.0f / 200) * 20.f; + /* (offset 20 pixels down from the top of the viewport) */ + y0 = 1.0f - (2.0f / 400) * 10.0f; + width = (2.0f / 200) * 100; + height = (2.0f / 400) * 200; + /* add the clip rectangle... */ + cogl_push_matrix (); + cogl_translate (x0 + (width/2.0), y0 - (height/2.0), 0); + /* XXX: Rotate just enough to stop Cogl from converting our model space + * rectangle into a window space rectangle.. */ + cogl_rotate (0.1, 0, 0, 1); + cogl_clip_push (-(width/2.0), -(height/2.0), width, height); + cogl_pop_matrix (); + /* fill the viewport with green.. */ + cogl_set_source_color4ub (0x00, 0xff, 0x00, 0xff); + cogl_rectangle (-1, 1, 1, -1); + cogl_clip_pop (); + assert_rectangle_color_and_black_border (40, 20, 100, 200, + 0x00, 0xff, 0x00); + + + /* Set the viewport to something obscure to verify that it gets + * replace when we switch back to the onscreen draw buffer... */ + cogl_set_viewport (0, 0, 10, 10); + + cogl_pop_draw_buffer (); + cogl_handle_unref (offscreen); + + /* + * Verify that the previous onscreen draw buffer's viewport was restored + * by drawing a white rectangle across the whole viewport. This should + * draw a 100x200 rectangle at (20,10) relative to the onscreen draw + * buffer... + */ + cogl_clear (&black, COGL_BUFFER_BIT_COLOR); + cogl_set_source_color4ub (0xff, 0xff, 0xff, 0xff); + cogl_rectangle (-1, 1, 1, -1); + assert_rectangle_color_and_black_border (20, 10, 100, 200, + 0xff, 0xff, 0xff); + + + /* Uncomment to display the last contents of the offscreen draw buffer */ +#if 1 + cogl_matrix_init_identity (&projection); + cogl_matrix_init_identity (&modelview); + cogl_set_viewport (0, 0, DRAW_BUFFER_WIDTH, DRAW_BUFFER_HEIGHT); + cogl_set_projection_matrix (&projection); + cogl_set_modelview_matrix (&modelview); + cogl_set_source_texture (tex); + cogl_rectangle (-1, 1, 1, -1); +#endif + + cogl_handle_unref (tex); + + /* Finally restore the stage's original state... */ + cogl_pop_matrix (); + cogl_set_projection_matrix (&saved_projection); + cogl_set_viewport (saved_viewport[0], saved_viewport[1], + saved_viewport[2], saved_viewport[3]); + + + /* Comment this out if you want visual feedback of what this test + * paints. + */ + clutter_main_quit (); +} + +static gboolean +queue_redraw (gpointer stage) +{ + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); + + return TRUE; +} + +void +test_cogl_viewport (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + guint idle_source; + ClutterActor *stage; + + stage = clutter_stage_get_default (); + clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); + clutter_actor_set_size (stage, DRAW_BUFFER_WIDTH, DRAW_BUFFER_HEIGHT); + + /* We force continuous redrawing of the stage, since we need to skip + * the first few frames, and we wont be doing anything else that + * will trigger redrawing. */ + idle_source = g_idle_add (queue_redraw, stage); + g_signal_connect_after (stage, "paint", G_CALLBACK (on_paint), NULL); + + clutter_actor_show (stage); + clutter_main (); + + g_source_remove (idle_source); + + /* Remove all of the actors from the stage */ + clutter_container_foreach (CLUTTER_CONTAINER (stage), + (ClutterCallback) clutter_actor_destroy, + NULL); + + if (g_test_verbose ()) + g_print ("OK\n"); +} + diff --git a/tests/conform/test-conform-main.c b/tests/conform/test-conform-main.c index 3379fbabf..7a96c5376 100644 --- a/tests/conform/test-conform-main.c +++ b/tests/conform/test-conform-main.c @@ -175,5 +175,7 @@ main (int argc, char **argv) TEST_CONFORM_SIMPLE ("/sizing", test_fixed_size); TEST_CONFORM_SIMPLE ("/sizing", test_preferred_size); + TEST_CONFORM_SIMPLE ("/cogl", test_cogl_viewport); + return g_test_run (); } From 2ff7f2d287f8dc80f0e6e02bdc388d4fee99d878 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Thu, 22 Oct 2009 12:36:17 +0100 Subject: [PATCH 36/39] [tests] Adds test-cogl-offscreen to validate offscreen draw buffer This adds a basic test to check that rendering a few colored rectangles offscreen works and that the modelview gets restored when switching back to the previous buffer. --- tests/conform/Makefile.am | 1 + tests/conform/test-cogl-offscreen.c | 169 ++++++++++++++++++++++++++++ tests/conform/test-conform-main.c | 1 + 3 files changed, 171 insertions(+) create mode 100644 tests/conform/test-cogl-offscreen.c diff --git a/tests/conform/Makefile.am b/tests/conform/Makefile.am index 177c0654b..1f3329f67 100644 --- a/tests/conform/Makefile.am +++ b/tests/conform/Makefile.am @@ -37,6 +37,7 @@ test_conformance_SOURCES = \ test-actor-size.c \ test-texture-fbo.c \ test-cogl-viewport.c \ + test-cogl-offscreen.c \ $(NULL) # For convenience, this provides a way to easily run individual unit tests: diff --git a/tests/conform/test-cogl-offscreen.c b/tests/conform/test-cogl-offscreen.c new file mode 100644 index 000000000..f6f4a972a --- /dev/null +++ b/tests/conform/test-cogl-offscreen.c @@ -0,0 +1,169 @@ + +#include +#include + +#include "test-conform-common.h" + +#define RED 0 +#define GREEN 1 +#define BLUE 2 + +#define DRAW_BUFFER_WIDTH 640 +#define DRAW_BUFFER_HEIGHT 480 + +static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff }; + + +static void +on_paint (ClutterActor *actor, void *state) +{ + float saved_viewport[4]; + CoglMatrix saved_projection; + CoglMatrix projection; + CoglMatrix modelview; + guchar *data; + CoglHandle tex; + CoglHandle offscreen; + guint8 pixel[4]; + + /* Save the Clutter viewport/matrices and load identity matrices */ + + cogl_get_viewport (saved_viewport); + cogl_get_projection_matrix (&saved_projection); + cogl_push_matrix (); + + cogl_matrix_init_identity (&projection); + cogl_matrix_init_identity (&modelview); + + cogl_set_projection_matrix (&projection); + cogl_set_modelview_matrix (&modelview); + + data = g_malloc (DRAW_BUFFER_WIDTH * 4 * DRAW_BUFFER_HEIGHT); + tex = cogl_texture_new_from_data (DRAW_BUFFER_WIDTH, DRAW_BUFFER_HEIGHT, + COGL_TEXTURE_NO_SLICING, + COGL_PIXEL_FORMAT_RGBA_8888, /* data fmt */ + COGL_PIXEL_FORMAT_ANY, /* internal fmt */ + DRAW_BUFFER_WIDTH * 4, /* rowstride */ + data); + g_free (data); + offscreen = cogl_offscreen_new_to_texture (tex); + + /* Set a scale and translate transform on the window draw buffer before + * switching to the offscreen draw buffer so we can verify it gets restored + * when we switch back + * + * The test is going to draw a grid of 4 colors to a texture which we + * subsequently draw to the window with a fullscreen rectangle. This + * transform will flip the texture left to right, scale it to a quater of the + * window size and slide it to the top right of the window. + */ + cogl_translate (0.5, 0.5, 0); + cogl_scale (-0.5, 0.5, 1); + + cogl_push_draw_buffer (); + cogl_set_draw_buffer (0 /* unused */, offscreen); + + /* Cogl should release the last reference when we call cogl_pop_draw_buffer() + */ + cogl_handle_unref (offscreen); + + /* Setup something other than the identity matrix for the modelview so we can + * verify it gets restored when we call cogl_pop_draw_buffer () */ + cogl_scale (2, 2, 1); + + /* red, top left */ + cogl_set_source_color4ub (0xff, 0x00, 0x00, 0xff); + cogl_rectangle (-0.5, 0.5, 0, 0); + /* green, top right */ + cogl_set_source_color4ub (0x00, 0xff, 0x00, 0xff); + cogl_rectangle (0, 0.5, 0.5, 0); + /* blue, bottom left */ + cogl_set_source_color4ub (0x00, 0x00, 0xff, 0xff); + cogl_rectangle (-0.5, 0, 0, -0.5); + /* white, bottom right */ + cogl_set_source_color4ub (0xff, 0xff, 0xff, 0xff); + cogl_rectangle (0, 0, 0.5, -0.5); + + cogl_pop_draw_buffer (); + + cogl_set_source_texture (tex); + cogl_rectangle (-1, 1, 1, -1); + + cogl_handle_unref (tex); + + /* NB: The texture is drawn flipped horizontally and scaled to fit in the + * top right corner of the window. */ + + /* red, top right */ + cogl_read_pixels (DRAW_BUFFER_WIDTH - 1, 0, 1, 1, + COGL_READ_PIXELS_COLOR_BUFFER, + COGL_PIXEL_FORMAT_RGBA_8888, + pixel); + g_assert (pixel[RED] == 0xff && pixel[GREEN] == 0x00 && pixel[BLUE] == 0x00); + + /* green, top left */ + cogl_read_pixels ((DRAW_BUFFER_WIDTH/2), 0, 1, 1, + COGL_READ_PIXELS_COLOR_BUFFER, + COGL_PIXEL_FORMAT_RGBA_8888, + pixel); + g_assert (pixel[RED] == 0x00 && pixel[GREEN] == 0xff && pixel[BLUE] == 0x00); + + /* blue, bottom right */ + cogl_read_pixels (DRAW_BUFFER_WIDTH - 1, (DRAW_BUFFER_HEIGHT/2) - 1, 1, 1, + COGL_READ_PIXELS_COLOR_BUFFER, + COGL_PIXEL_FORMAT_RGBA_8888, + pixel); + g_assert (pixel[RED] == 0x00 && pixel[GREEN] == 0x00 && pixel[BLUE] == 0xff); + + /* white, bottom left */ + cogl_read_pixels ((DRAW_BUFFER_WIDTH/2), (DRAW_BUFFER_HEIGHT/2) - 1, 1, 1, + COGL_READ_PIXELS_COLOR_BUFFER, + COGL_PIXEL_FORMAT_RGBA_8888, + pixel); + g_assert (pixel[RED] == 0xff && pixel[GREEN] == 0xff && pixel[BLUE] == 0xff); + + /* Comment this out if you want visual feedback of what this test + * paints. + */ + clutter_main_quit (); +} + +static gboolean +queue_redraw (gpointer stage) +{ + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); + + return TRUE; +} + +void +test_cogl_offscreen (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + guint idle_source; + ClutterActor *stage; + + stage = clutter_stage_get_default (); + clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); + clutter_actor_set_size (stage, DRAW_BUFFER_WIDTH, DRAW_BUFFER_HEIGHT); + + /* We force continuous redrawing of the stage, since we need to skip + * the first few frames, and we wont be doing anything else that + * will trigger redrawing. */ + idle_source = g_idle_add (queue_redraw, stage); + g_signal_connect_after (stage, "paint", G_CALLBACK (on_paint), NULL); + + clutter_actor_show (stage); + clutter_main (); + + g_source_remove (idle_source); + + /* Remove all of the actors from the stage */ + clutter_container_foreach (CLUTTER_CONTAINER (stage), + (ClutterCallback) clutter_actor_destroy, + NULL); + + if (g_test_verbose ()) + g_print ("OK\n"); +} + diff --git a/tests/conform/test-conform-main.c b/tests/conform/test-conform-main.c index 7a96c5376..78188949c 100644 --- a/tests/conform/test-conform-main.c +++ b/tests/conform/test-conform-main.c @@ -176,6 +176,7 @@ main (int argc, char **argv) TEST_CONFORM_SIMPLE ("/sizing", test_preferred_size); TEST_CONFORM_SIMPLE ("/cogl", test_cogl_viewport); + TEST_CONFORM_SIMPLE ("/cogl", test_cogl_offscreen); return g_test_run (); } From 60b642f5d6aada0d237cfb7d81f6b78299c29e73 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Thu, 22 Oct 2009 16:56:01 +0100 Subject: [PATCH 37/39] [tests] Adds test-cogl-readpixels.c for very basic cogl_read_pixels testing Mostly this was written to verify that we don't flip the data read back from an offscreen draw buffer. (since all offscreen rendering is done upside down) --- tests/conform/Makefile.am | 1 + tests/conform/test-cogl-readpixels.c | 153 +++++++++++++++++++++++++++ tests/conform/test-conform-main.c | 1 + 3 files changed, 155 insertions(+) create mode 100644 tests/conform/test-cogl-readpixels.c diff --git a/tests/conform/Makefile.am b/tests/conform/Makefile.am index 1f3329f67..a2cb54d27 100644 --- a/tests/conform/Makefile.am +++ b/tests/conform/Makefile.am @@ -38,6 +38,7 @@ test_conformance_SOURCES = \ test-texture-fbo.c \ test-cogl-viewport.c \ test-cogl-offscreen.c \ + test-cogl-readpixels.c \ $(NULL) # For convenience, this provides a way to easily run individual unit tests: diff --git a/tests/conform/test-cogl-readpixels.c b/tests/conform/test-cogl-readpixels.c new file mode 100644 index 000000000..563efdf60 --- /dev/null +++ b/tests/conform/test-cogl-readpixels.c @@ -0,0 +1,153 @@ + +#include +#include + +#include "test-conform-common.h" + +#define RED 0 +#define GREEN 1 +#define BLUE 2 + +#define DRAW_BUFFER_WIDTH 640 +#define DRAW_BUFFER_HEIGHT 480 + +static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff }; + + +static void +on_paint (ClutterActor *actor, void *state) +{ + float saved_viewport[4]; + CoglMatrix saved_projection; + CoglMatrix projection; + CoglMatrix modelview; + guchar *data; + CoglHandle tex; + CoglHandle offscreen; + guint32 *pixels; + + /* Save the Clutter viewport/matrices and load identity matrices */ + + cogl_get_viewport (saved_viewport); + cogl_get_projection_matrix (&saved_projection); + cogl_push_matrix (); + + cogl_matrix_init_identity (&projection); + cogl_matrix_init_identity (&modelview); + + cogl_set_projection_matrix (&projection); + cogl_set_modelview_matrix (&modelview); + + /* All offscreen rendering is done upside down so the first thing we + * verify is reading back grid of colors from a CoglOffscreen draw buffer + */ + + data = g_malloc (DRAW_BUFFER_WIDTH * 4 * DRAW_BUFFER_HEIGHT); + tex = cogl_texture_new_from_data (DRAW_BUFFER_WIDTH, DRAW_BUFFER_HEIGHT, + COGL_TEXTURE_NO_SLICING, + COGL_PIXEL_FORMAT_RGBA_8888, /* data fmt */ + COGL_PIXEL_FORMAT_ANY, /* internal fmt */ + DRAW_BUFFER_WIDTH * 4, /* rowstride */ + data); + g_free (data); + offscreen = cogl_offscreen_new_to_texture (tex); + + cogl_push_draw_buffer (); + cogl_set_draw_buffer (0 /* unused */, offscreen); + + /* red, top left */ + cogl_set_source_color4ub (0xff, 0x00, 0x00, 0xff); + cogl_rectangle (-1, 1, 0, 0); + /* green, top right */ + cogl_set_source_color4ub (0x00, 0xff, 0x00, 0xff); + cogl_rectangle (0, 1, 1, 0); + /* blue, bottom left */ + cogl_set_source_color4ub (0x00, 0x00, 0xff, 0xff); + cogl_rectangle (-1, 0, 0, -1); + /* white, bottom right */ + cogl_set_source_color4ub (0xff, 0xff, 0xff, 0xff); + cogl_rectangle (0, 0, 1, -1); + + pixels = g_malloc0 (DRAW_BUFFER_WIDTH * 4 * DRAW_BUFFER_HEIGHT); + cogl_read_pixels (0, 0, DRAW_BUFFER_WIDTH, DRAW_BUFFER_HEIGHT, + COGL_READ_PIXELS_COLOR_BUFFER, + COGL_PIXEL_FORMAT_RGBA_8888, + (guchar *)pixels); + + g_assert (pixels[0] == 0xff0000ff); + g_assert (pixels[DRAW_BUFFER_WIDTH - 1] == 0xff00ff00); + g_assert (pixels[(DRAW_BUFFER_HEIGHT - 1) * DRAW_BUFFER_WIDTH] == 0xffff0000); + g_assert (pixels[(DRAW_BUFFER_HEIGHT - 1) * DRAW_BUFFER_WIDTH + + DRAW_BUFFER_WIDTH - 1] == 0xffffffff); + g_free (pixels); + + cogl_pop_draw_buffer (); + cogl_handle_unref (offscreen); + + /* Now verify reading back from an onscreen draw buffer... + */ + + cogl_set_source_texture (tex); + cogl_rectangle (-1, 1, 1, -1); + + pixels = g_malloc0 (DRAW_BUFFER_WIDTH * 4 * DRAW_BUFFER_HEIGHT); + cogl_read_pixels (0, 0, DRAW_BUFFER_WIDTH, DRAW_BUFFER_HEIGHT, + COGL_READ_PIXELS_COLOR_BUFFER, + COGL_PIXEL_FORMAT_RGBA_8888, + (guchar *)pixels); + + g_assert (pixels[0] == 0xff0000ff); + g_assert (pixels[DRAW_BUFFER_WIDTH - 1] == 0xff00ff00); + g_assert (pixels[(DRAW_BUFFER_HEIGHT - 1) * DRAW_BUFFER_WIDTH] == 0xffff0000); + g_assert (pixels[(DRAW_BUFFER_HEIGHT - 1) * DRAW_BUFFER_WIDTH + + DRAW_BUFFER_WIDTH - 1] == 0xffffffff); + g_free (pixels); + + cogl_handle_unref (tex); + + + /* Comment this out if you want visual feedback of what this test + * paints. + */ + clutter_main_quit (); +} + +static gboolean +queue_redraw (gpointer stage) +{ + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); + + return TRUE; +} + +void +test_cogl_readpixels (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + guint idle_source; + ClutterActor *stage; + + stage = clutter_stage_get_default (); + clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); + clutter_actor_set_size (stage, DRAW_BUFFER_WIDTH, DRAW_BUFFER_HEIGHT); + + /* We force continuous redrawing of the stage, since we need to skip + * the first few frames, and we wont be doing anything else that + * will trigger redrawing. */ + idle_source = g_idle_add (queue_redraw, stage); + g_signal_connect_after (stage, "paint", G_CALLBACK (on_paint), NULL); + + clutter_actor_show (stage); + clutter_main (); + + g_source_remove (idle_source); + + /* Remove all of the actors from the stage */ + clutter_container_foreach (CLUTTER_CONTAINER (stage), + (ClutterCallback) clutter_actor_destroy, + NULL); + + if (g_test_verbose ()) + g_print ("OK\n"); +} + diff --git a/tests/conform/test-conform-main.c b/tests/conform/test-conform-main.c index 78188949c..88b7abfc5 100644 --- a/tests/conform/test-conform-main.c +++ b/tests/conform/test-conform-main.c @@ -177,6 +177,7 @@ main (int argc, char **argv) TEST_CONFORM_SIMPLE ("/cogl", test_cogl_viewport); TEST_CONFORM_SIMPLE ("/cogl", test_cogl_offscreen); + TEST_CONFORM_SIMPLE ("/cogl", test_cogl_readpixels); return g_test_run (); } From ffb592de5908d844b527cb4fe75b7195eb0dc55d Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Thu, 22 Oct 2009 19:01:27 +0100 Subject: [PATCH 38/39] [tests] test-backface-culling: test culling with offscreen rendering Since offscreen rendering is internally forced to be upside down Cogl needs to reverse the glFrontFace winding order so as not to interfere with the use of cogl_set_backface_culling_enabled() This ensures we test that mechanism. --- tests/conform/test-backface-culling.c | 174 +++++++++++++++++--------- 1 file changed, 112 insertions(+), 62 deletions(-) diff --git a/tests/conform/test-backface-culling.c b/tests/conform/test-backface-culling.c index 56cb8c45a..af45d3c9c 100644 --- a/tests/conform/test-backface-culling.c +++ b/tests/conform/test-backface-culling.c @@ -5,8 +5,6 @@ #include "test-conform-common.h" -static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff }; - #ifdef CLUTTER_COGL_HAS_GL /* Size the texture so that it is just off a power of two to enourage @@ -31,24 +29,31 @@ static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff }; typedef struct _TestState { - guint frame; CoglHandle texture; + CoglHandle offscreen; + CoglHandle offscreen_tex; } TestState; +#include + static gboolean validate_part (int xnum, int ynum, gboolean shown) { guchar *pixels, *p; - ClutterActor *stage = clutter_stage_get_default (); gboolean ret = TRUE; + pixels = g_malloc0 ((TEXTURE_RENDER_SIZE - TEST_INSET * 2) + * (TEXTURE_RENDER_SIZE - TEST_INSET * 2) * 4); + /* Read the appropriate part but skip out a few pixels around the edges */ - pixels = clutter_stage_read_pixels (CLUTTER_STAGE (stage), - xnum * TEXTURE_RENDER_SIZE + TEST_INSET, - ynum * TEXTURE_RENDER_SIZE + TEST_INSET, - TEXTURE_RENDER_SIZE - TEST_INSET * 2, - TEXTURE_RENDER_SIZE - TEST_INSET * 2); + cogl_read_pixels (xnum * TEXTURE_RENDER_SIZE + TEST_INSET, + ynum * TEXTURE_RENDER_SIZE + TEST_INSET, + TEXTURE_RENDER_SIZE - TEST_INSET * 2, + TEXTURE_RENDER_SIZE - TEST_INSET * 2, + COGL_READ_PIXELS_COLOR_BUFFER, + COGL_PIXEL_FORMAT_RGBA_8888, + pixels); /* Make sure every pixels is the appropriate color */ for (p = pixels; @@ -70,47 +75,9 @@ validate_part (int xnum, int ynum, gboolean shown) } static void -validate_result (TestState *state) -{ - /* Front-facing texture */ - g_assert (validate_part (0, 0, TRUE)); - /* Front-facing texture with flipped tex coords */ - g_assert (validate_part (1, 0, TRUE)); - /* Back-facing texture */ - g_assert (validate_part (2, 0, FALSE)); - /* Front-facing texture polygon */ - g_assert (validate_part (3, 0, TRUE)); - /* Back-facing texture polygon */ - g_assert (validate_part (4, 0, FALSE)); - /* Regular rectangle */ - g_assert (validate_part (5, 0, TRUE)); - - /* Backface culling disabled - everything should be shown */ - - /* Front-facing texture */ - g_assert (validate_part (0, 1, TRUE)); - /* Front-facing texture with flipped tex coords */ - g_assert (validate_part (1, 1, TRUE)); - /* Back-facing texture */ - g_assert (validate_part (2, 1, TRUE)); - /* Front-facing texture polygon */ - g_assert (validate_part (3, 1, TRUE)); - /* Back-facing texture polygon */ - g_assert (validate_part (4, 1, TRUE)); - /* Regular rectangle */ - g_assert (validate_part (5, 1, TRUE)); - - /* Comment this out if you want visual feedback of what this test - * paints. - */ - clutter_main_quit (); -} - -static void -on_paint (ClutterActor *actor, TestState *state) +do_test_backface_culling (TestState *state) { int i; - int frame_num; CoglHandle material = cogl_material_new (); cogl_material_set_layer_filters (material, 0, @@ -197,21 +164,91 @@ on_paint (ClutterActor *actor, TestState *state) cogl_set_backface_culling_enabled (FALSE); } - cogl_pop_matrix (); - cogl_handle_unref (material); - /* XXX: Experiments have shown that for some buggy drivers, when using - * glReadPixels there is some kind of race, so we delay our test for a - * few frames and a few seconds: + cogl_pop_matrix (); + + /* Front-facing texture */ + g_assert (validate_part (0, 0, TRUE)); + /* Front-facing texture with flipped tex coords */ + g_assert (validate_part (1, 0, TRUE)); + /* Back-facing texture */ + g_assert (validate_part (2, 0, FALSE)); + /* Front-facing texture polygon */ + g_assert (validate_part (3, 0, TRUE)); + /* Back-facing texture polygon */ + g_assert (validate_part (4, 0, FALSE)); + /* Regular rectangle */ + g_assert (validate_part (5, 0, TRUE)); + + /* Backface culling disabled - everything should be shown */ + + /* Front-facing texture */ + g_assert (validate_part (0, 1, TRUE)); + /* Front-facing texture with flipped tex coords */ + g_assert (validate_part (1, 1, TRUE)); + /* Back-facing texture */ + g_assert (validate_part (2, 1, TRUE)); + /* Front-facing texture polygon */ + g_assert (validate_part (3, 1, TRUE)); + /* Back-facing texture polygon */ + g_assert (validate_part (4, 1, TRUE)); + /* Regular rectangle */ + g_assert (validate_part (5, 1, TRUE)); + +} + +static void +on_paint (ClutterActor *actor, TestState *state) +{ + CoglColor clr; + float stage_viewport[4]; + CoglMatrix stage_projection; + CoglMatrix stage_modelview; + + cogl_color_set_from_4ub (&clr, 0x00, 0x00, 0x00, 0xff); + + do_test_backface_culling (state); + + /* Since we are going to repeat the test rendering offscreen we clear the + * stage, just to minimize the chance of a some other bug causing us + * mistakenly reading back the results from the stage and giving a false + * posistive. */ + cogl_clear (&clr, COGL_BUFFER_BIT_COLOR|COGL_BUFFER_BIT_STENCIL); + + /* + * Now repeat the test but rendered to an offscreen draw buffer... */ - /* Need to increment frame first because clutter_stage_read_pixels - fires a redraw */ - frame_num = state->frame++; - if (frame_num == 2) - validate_result (state); - else if (frame_num < 2) - g_usleep (G_USEC_PER_SEC); + + cogl_get_viewport (stage_viewport); + cogl_get_projection_matrix (&stage_projection); + cogl_get_modelview_matrix (&stage_modelview); + + cogl_push_draw_buffer (); + cogl_set_draw_buffer (0 /* unused */, state->offscreen); + + cogl_clear (&clr, COGL_BUFFER_BIT_COLOR|COGL_BUFFER_BIT_STENCIL); + + cogl_set_viewport (stage_viewport[0], + stage_viewport[1], + stage_viewport[2], + stage_viewport[3]); + cogl_set_projection_matrix (&stage_projection); + cogl_set_modelview_matrix (&stage_modelview); + + do_test_backface_culling (state); + + cogl_pop_draw_buffer (); + + /* Incase we want feedback of what was drawn offscreen we draw it + * to the stage... */ + cogl_set_source_texture (state->offscreen_tex); + cogl_rectangle (0, 0, stage_viewport[2], stage_viewport[3]); + + /* Comment this out if you want visual feedback of what this test + * paints. + */ + clutter_main_quit (); } static gboolean @@ -256,15 +293,26 @@ test_backface_culling (TestConformSimpleFixture *fixture, gconstpointer data) { TestState state; + CoglHandle tex; ClutterActor *stage; + float stage_width; + float stage_height; + const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff }; ClutterActor *group; guint idle_source; - state.frame = 0; + stage = clutter_stage_get_default (); + clutter_actor_get_size (stage, &stage_width, &stage_height); + + state.offscreen = COGL_INVALID_HANDLE; state.texture = make_texture (); - stage = clutter_stage_get_default (); + tex = cogl_texture_new_with_size (stage_width, stage_height, + COGL_TEXTURE_NO_SLICING, + COGL_PIXEL_FORMAT_ANY); /* internal fmt */ + state.offscreen = cogl_offscreen_new_to_texture (tex); + state.offscreen_tex = tex; clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); @@ -284,6 +332,8 @@ test_backface_culling (TestConformSimpleFixture *fixture, g_source_remove (idle_source); + cogl_handle_unref (state.offscreen); + cogl_handle_unref (state.offscreen_tex); cogl_handle_unref (state.texture); if (g_test_verbose ()) From 4258214e509edbc4ff82de418ffc9662088fa835 Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Wed, 7 Oct 2009 10:40:15 +0100 Subject: [PATCH 39/39] [test-fbo] greatly simplify the test This test tried to do too much, and I can't remember the last time I saw this test work. It no longer tries to create a texture from an offscreen actor and it no longer tries to use shaders. It does though show that chaining of clutter_texture_new_from_actor now works, and that animating the source actor is reflected in textures created from it. When run you should see three actors: - on the left is the pristine source actor rotating around the y-axis - in the middle is the first texture created from the source actor - and on the right a texture created from the middle actor Note: the somewhat strange bobbing of the middle and right textures is actually correct given how it was decided long ago to map the transformed (to screen space) allocation of the source actor to the texture. When the hand spins around the perspective projection of the top of the hand results in the origin of the texture bobbing up to a higher stage position, but the position of the textures is fixed. This design also means we end up reallocating our offscreen draw buffer every frame that the actors transformed size changes, which isn't ideal. --- tests/interactive/test-fbo.c | 194 +++++++---------------------------- 1 file changed, 35 insertions(+), 159 deletions(-) diff --git a/tests/interactive/test-fbo.c b/tests/interactive/test-fbo.c index 3d82b9b44..fdf8b83a9 100644 --- a/tests/interactive/test-fbo.c +++ b/tests/interactive/test-fbo.c @@ -1,25 +1,4 @@ -/*#define TEST_GROUP */ - -/* These variables are used instead of the standard GLSL variables on - GLES 2 */ -#ifdef HAVE_COGL_GLES2 - -#define GLES2_VARS \ - "precision mediump float;\n" \ - "varying vec2 tex_coord;\n" \ - "varying vec4 frag_color;\n" -#define TEX_COORD "tex_coord" -#define COLOR_VAR "frag_color" - -#else /* HAVE_COGL_GLES2 */ - -#define GLES2_VARS "" -#define TEX_COORD "gl_TexCoord[0]" -#define COLOR_VAR "gl_Color" - -#endif /* HAVE_COGL_GLES2 */ - #include #include @@ -27,13 +6,16 @@ #include #include -ClutterActor* -make_source(void) -{ - ClutterActor *source, *actor; - GError *error = NULL; +#define STAGE_WIDTH 800 +#define STAGE_HEIGHT 600 - ClutterColor yellow = {0xff, 0xff, 0x00, 0xff}; +ClutterActor * +make_source (void) +{ + ClutterActor *source, *actor; + GError *error = NULL; + + ClutterColor yellow = {0xff, 0xff, 0x00, 0xff}; source = clutter_group_new(); actor = clutter_texture_new_from_file ("redhand.png", &error); @@ -51,65 +33,17 @@ make_source(void) return source; } -ClutterShader* -make_shader(void) -{ - ClutterShader *shader; - GError *error = NULL; - - shader = clutter_shader_new (); - clutter_shader_set_fragment_source (shader, - - GLES2_VARS - "uniform float radius ;" - "uniform sampler2D rectTexture;" - "uniform float x_step, y_step;" - "" - "void main()" - "{" - " vec4 color = texture2D(rectTexture, " TEX_COORD ".st);" - " float u;" - " float v;" - " int count = 1;" - " for (u=-radius;umessage); - g_error_free (error); - } - - return shader; -} - -G_MODULE_EXPORT gint -test_fbo_main (gint argc, gchar *argv[]) +G_MODULE_EXPORT int +test_fbo_main (int argc, char *argv[]) { ClutterColor blue = {0x33, 0x44, 0x55, 0xff}; ClutterActor *fbo; - ClutterActor *onscreen_source, *offscreen_source, *trans_source; - ClutterActor *foo_source; + ClutterActor *onscreen_source; ClutterActor *stage; - ClutterActor *clone; - ClutterShader *shader; - gint padx, pady; - gint fbo_width, fbo_height; + ClutterAnimation *animation; + int x_pos = 200; + int y_pos = 100; clutter_init (&argc, &argv); @@ -117,6 +51,7 @@ test_fbo_main (gint argc, gchar *argv[]) g_error("This test requires CLUTTER_FEATURE_OFFSCREEN"); stage = clutter_stage_get_default (); + clutter_actor_set_size (stage, STAGE_WIDTH, STAGE_HEIGHT); clutter_stage_set_color (CLUTTER_STAGE (stage), &blue); /* Create the first source */ @@ -124,93 +59,34 @@ test_fbo_main (gint argc, gchar *argv[]) clutter_actor_show_all (onscreen_source); clutter_group_add (stage, onscreen_source); - /* Basic sizing for alignment */ - fbo_width = clutter_actor_get_width (onscreen_source); - fbo_height = clutter_actor_get_height (onscreen_source); - padx = fbo_width + 10; - pady = fbo_height + 10; - clutter_actor_set_size (stage, padx*4, pady*2); + y_pos = (STAGE_HEIGHT/2.0) - + (clutter_actor_get_height (onscreen_source)/2.0); + clutter_actor_set_position (onscreen_source, x_pos, y_pos); + x_pos += clutter_actor_get_width (onscreen_source); - /* Second hand from fbo onscreen */ + animation = clutter_actor_animate (onscreen_source, + CLUTTER_LINEAR, + 5000, /* 1 second duration */ + "rotation-angle-y", 360.0f, + NULL); + clutter_animation_set_loop (animation, TRUE); + + /* Second hand = actor from onscreen_source */ if ((fbo = clutter_texture_new_from_actor (onscreen_source)) == NULL) g_error("onscreen fbo creation failed"); - clutter_actor_set_position (fbo, padx, 0); + clutter_actor_set_position (fbo, x_pos, y_pos); + x_pos += clutter_actor_get_width (fbo); clutter_group_add (stage, fbo); - /* apply a shader to it */ - shader = make_shader(); - clutter_actor_set_shader (fbo, shader); - clutter_actor_set_shader_param_float (fbo, "radius", 2.0); - clutter_actor_set_shader_param_float (fbo, "x_step", - 1.0f / clutter_util_next_p2 (fbo_width)); - clutter_actor_set_shader_param_float (fbo, "y_step", - 1.0f / clutter_util_next_p2 (fbo_height)); + /* Third hand = actor from Second hand */ + if ((fbo = clutter_texture_new_from_actor (fbo)) == NULL) + g_error("fbo from fbo creation failed"); - /* Third from cloning the fbo texture */ - clone = clutter_clone_new (fbo); - clutter_container_add_actor (CLUTTER_CONTAINER (stage), clone); - clutter_actor_set_position (clone, padx*2, 0); - - - /* Forth - an offscreen source */ - offscreen_source = make_source(); - clutter_actor_show_all (offscreen_source); /* need to show() offscreen */ - if ((fbo = clutter_texture_new_from_actor (offscreen_source)) == NULL) - g_error("offscreen fbo creation failed"); - - clutter_actor_set_position (fbo, padx*3, 0); + clutter_actor_set_position (fbo, x_pos, y_pos); + x_pos += clutter_actor_get_width (fbo); clutter_group_add (stage, fbo); - - /* 5th transformed */ - trans_source = make_source(); - clutter_actor_show_all (trans_source); /* need to show() offscreen */ - - clutter_actor_set_scale (trans_source, 2.5, 2.5); - - if ((fbo = clutter_texture_new_from_actor (trans_source)) == NULL) - g_error("transformed fbo creation failed"); - - clutter_actor_set_position (fbo, 0, pady); - clutter_group_add (stage, fbo); - - - /* 6th resized bigger, but after fbo creation */ - trans_source = make_source(); - clutter_actor_show_all (trans_source); /* need to show() offscreen */ - - if ((fbo = clutter_texture_new_from_actor (trans_source)) == NULL) - g_error("transformed fbo creation failed"); - - /* rotate after */ - clutter_actor_move_anchor_point_from_gravity (trans_source, - CLUTTER_GRAVITY_CENTER); - clutter_actor_set_rotation (trans_source, CLUTTER_Z_AXIS, 90.0, 0, 0, 0); - - clutter_actor_set_position (fbo, padx, pady); - clutter_group_add (stage, fbo); - - - /* non visual breaks */ - foo_source = make_source(); - g_object_ref_sink (foo_source); - - clutter_actor_show_all (foo_source); - if ((fbo = clutter_texture_new_from_actor (foo_source)) == NULL) - g_error("foo fbo creation failed"); - - g_object_unref (foo_source); /* fbo should keep it around */ - - clutter_actor_set_position (fbo, padx*3, pady); - clutter_group_add (stage, fbo); - - /* TODO: - * Check realize/unrealize - * get_pixbuf() - * set_rgba on fbo texture. - */ - clutter_actor_show_all (stage); clutter_main ();