[cogl] Flush matrices in the progend and flip with a vector



commit f005f517fe03f28bb7fe09383490d069fc1400e9
Author: Neil Roberts <neil linux intel com>
Date:   Tue Nov 29 14:21:07 2011 +0000

    Flush matrices in the progend and flip with a vector
    
    Previously flushing the matrices was performed as part of the
    framebuffer state. When on GLES2 this matrix flushing is actually
    diverted so that it only keeps a reference to the intended matrix
    stack. This is necessary because on GLES2 there are no builtin
    uniforms so it can't actually flush the matrices until the program for
    the pipeline is generated. When the matrices are flushed it would
    store the age of modifications on the matrix stack so that it could
    detect when the matrix hasn't changed and avoid flushing it.
    
    This patch changes it so that the pipeline is responsible for flushing
    the matrices even when we are using the GL builtins. The same
    mechanism for detecting unmodified matrix stacks is used in all
    cases. There is a new CoglMatrixStackCache type which is used to store
    a reference to the intended matrix stack along with its last flushed
    age. There are now two of these attached to the CoglContext to track
    the flushed state for the global matrix builtins and also two for each
    glsl progend program state to track the flushed state for a
    program. The framebuffer matrix flush now just updates the intended
    matrix stacks without actually trying to flush.
    
    When a vertex snippet is attached to the pipeline, the GLSL vertend
    will now avoid using the projection matrix to flip the rendering. This
    is necessary because any vertex snippet may cause the projection
    matrix not to be used. Instead the flip is done as a forced final step
    by multiplying cogl_position_out by a vec4 uniform. This uniform is
    updated as part of the progend pre_paint depending on whether the
    framebuffer is offscreen or not.
    
    Reviewed-by: Robert Bragg <robert linux intel com>

 cogl/Makefile.am                           |    2 +
 cogl/cogl-clip-stack.c                     |   38 ++---
 cogl/cogl-context-private.h                |   26 ++-
 cogl/cogl-context.c                        |   40 +++-
 cogl/cogl-framebuffer.c                    |   26 +--
 cogl/cogl-journal.c                        |   12 +-
 cogl/cogl-matrix-stack.c                   |  235 ++++++++++++---------
 cogl/cogl-matrix-stack.h                   |   34 ++-
 cogl/cogl-pipeline-private.h               |   22 ++-
 cogl/cogl-pipeline-progend-fixed-private.h |   36 +++
 cogl/cogl-pipeline-progend-fixed.c         |   69 ++++++
 cogl/cogl-pipeline-progend-glsl.c          |  323 +++++++++++++---------------
 cogl/cogl-pipeline-vertend-fixed.c         |    5 +-
 cogl/cogl-pipeline-vertend-glsl.c          |   38 +++-
 cogl/cogl-pipeline.c                       |   14 +-
 15 files changed, 561 insertions(+), 359 deletions(-)
---
diff --git a/cogl/Makefile.am b/cogl/Makefile.am
index a49a65b..777f366 100644
--- a/cogl/Makefile.am
+++ b/cogl/Makefile.am
@@ -271,6 +271,8 @@ cogl_sources_c = \
 	$(srcdir)/cogl-pipeline-vertend-glsl-private.h	\
 	$(srcdir)/cogl-pipeline-vertend-fixed.c		\
 	$(srcdir)/cogl-pipeline-vertend-fixed-private.h	\
+	$(srcdir)/cogl-pipeline-progend-fixed.c		\
+	$(srcdir)/cogl-pipeline-progend-fixed-private.h	\
 	$(srcdir)/cogl-pipeline-progend-glsl.c		\
 	$(srcdir)/cogl-pipeline-progend-glsl-private.h	\
 	$(srcdir)/cogl-pipeline-snippet-private.h	\
diff --git a/cogl/cogl-clip-stack.c b/cogl/cogl-clip-stack.c
index 8600f30..5cc4981 100644
--- a/cogl/cogl-clip-stack.c
+++ b/cogl/cogl-clip-stack.c
@@ -109,7 +109,13 @@ set_clip_plane (CoglFramebuffer *framebuffer,
   _cogl_matrix_stack_translate (modelview_stack,
                                 -vertex_a[0], -vertex_a[1], -vertex_a[2]);
 
-  _cogl_matrix_stack_flush_to_gl (ctx, modelview_stack, COGL_MATRIX_MODELVIEW);
+  /* Clip planes can only be used when a fixed function backend is in
+     use so we know we can directly push this matrix to the builtin
+     state */
+  _cogl_matrix_stack_flush_to_gl_builtins (ctx,
+                                           modelview_stack,
+                                           COGL_MATRIX_MODELVIEW,
+                                           FALSE /* don't disable flip */);
 
   planef[0] = 0;
   planef[1] = -1.0;
@@ -218,12 +224,8 @@ add_stencil_clip_rectangle (CoglFramebuffer *framebuffer,
   /* This can be called from the journal code which doesn't flush
      the matrix stacks between calls so we need to ensure they're
      flushed now */
-  _cogl_matrix_stack_flush_to_gl (ctx,
-                                  modelview_stack,
-                                  COGL_MATRIX_MODELVIEW);
-  _cogl_matrix_stack_flush_to_gl (ctx,
-                                  projection_stack,
-                                  COGL_MATRIX_PROJECTION);
+  _cogl_context_set_current_projection (ctx, projection_stack);
+  _cogl_context_set_current_modelview (ctx, modelview_stack);
 
   if (first)
     {
@@ -258,12 +260,8 @@ add_stencil_clip_rectangle (CoglFramebuffer *framebuffer,
       _cogl_matrix_stack_push (modelview_stack);
       _cogl_matrix_stack_load_identity (modelview_stack);
 
-      _cogl_matrix_stack_flush_to_gl (ctx,
-                                      modelview_stack,
-                                      COGL_MATRIX_MODELVIEW);
-      _cogl_matrix_stack_flush_to_gl (ctx,
-                                      projection_stack,
-                                      COGL_MATRIX_PROJECTION);
+      _cogl_context_set_current_projection (ctx, projection_stack);
+      _cogl_context_set_current_modelview (ctx, modelview_stack);
 
       _cogl_rectangle_immediate (-1.0, -1.0, 1.0, 1.0);
 
@@ -301,12 +299,8 @@ add_stencil_clip_silhouette (CoglFramebuffer *framebuffer,
   /* This can be called from the clip stack code which doesn't flush
      the matrix stacks between calls so we need to ensure they're
      flushed now */
-  _cogl_matrix_stack_flush_to_gl (ctx,
-                                  modelview_stack,
-                                  COGL_MATRIX_MODELVIEW);
-  _cogl_matrix_stack_flush_to_gl (ctx,
-                                  projection_stack,
-                                  COGL_MATRIX_PROJECTION);
+  _cogl_context_set_current_projection (ctx, projection_stack);
+  _cogl_context_set_current_modelview (ctx, modelview_stack);
 
   /* Just setup a simple pipeline that doesn't use texturing... */
   _cogl_push_source (ctx->stencil_pipeline, FALSE);
@@ -366,15 +360,9 @@ add_stencil_clip_silhouette (CoglFramebuffer *framebuffer,
 
       _cogl_matrix_stack_push (projection_stack);
       _cogl_matrix_stack_load_identity (projection_stack);
-      _cogl_matrix_stack_flush_to_gl (ctx,
-                                      projection_stack,
-                                      COGL_MATRIX_PROJECTION);
 
       _cogl_matrix_stack_push (modelview_stack);
       _cogl_matrix_stack_load_identity (modelview_stack);
-      _cogl_matrix_stack_flush_to_gl (ctx,
-                                      modelview_stack,
-                                      COGL_MATRIX_MODELVIEW);
 
       _cogl_rectangle_immediate (-1.0, -1.0, 1.0, 1.0);
       _cogl_rectangle_immediate (-1.0, -1.0, 1.0, 1.0);
diff --git a/cogl/cogl-context-private.h b/cogl/cogl-context-private.h
index f8b9cf0..b67c7e7 100644
--- a/cogl/cogl-context-private.h
+++ b/cogl/cogl-context-private.h
@@ -94,16 +94,18 @@ struct _CoglContext
   CoglMatrix        identity_matrix;
   CoglMatrix        y_flip_matrix;
 
-  /* Client-side matrix stack or NULL if none */
+  /* Value that was last used when calling glMatrixMode to avoid
+     calling it multiple times */
   CoglMatrixMode    flushed_matrix_mode;
 
-  /* On GLES2 we need to track the matrices separately because the are
-     stored in GLSL uniforms rather than using the fixed function
-     API. We keep track of the matrix stack that Cogl is trying to
-     flush so we can flush it later after the program is generated. A
-     reference is taken on the stacks. */
-  CoglMatrixStack  *flushed_modelview_stack;
-  CoglMatrixStack  *flushed_projection_stack;
+  /* The matrix stack that should be used for the next render */
+  CoglMatrixStack  *current_projection_stack;
+  CoglMatrixStack  *current_modelview_stack;
+
+  /* The last matrix stack with age that was flushed to the GL matrix
+     builtins */
+  CoglMatrixStackCache builtin_flushed_projection;
+  CoglMatrixStackCache builtin_flushed_modelview;
 
   GArray           *texture_units;
   int               active_texture_unit;
@@ -320,4 +322,12 @@ if (ctxvar == NULL) return retval;
 
 #define NO_RETVAL
 
+void
+_cogl_context_set_current_projection (CoglContext *context,
+                                      CoglMatrixStack *stack);
+
+void
+_cogl_context_set_current_modelview (CoglContext *context,
+                                     CoglMatrixStack *stack);
+
 #endif /* __COGL_CONTEXT_PRIVATE_H */
diff --git a/cogl/cogl-context.c b/cogl/cogl-context.c
index 08b527e..21c10b7 100644
--- a/cogl/cogl-context.c
+++ b/cogl/cogl-context.c
@@ -371,10 +371,10 @@ cogl_context_new (CoglDisplay *display,
     GE (context, glEnable (GL_ALPHA_TEST));
 #endif
 
-#ifdef HAVE_COGL_GLES2
-  _context->flushed_modelview_stack = NULL;
-  _context->flushed_projection_stack = NULL;
-#endif
+  _context->current_modelview_stack = NULL;
+  _context->current_projection_stack = NULL;
+  _cogl_matrix_stack_init_cache (&_context->builtin_flushed_projection);
+  _cogl_matrix_stack_init_cache (&_context->builtin_flushed_modelview);
 
   /* Create default textures used for fall backs */
   context->default_gl_texture_2d_tex =
@@ -491,12 +491,12 @@ _cogl_context_free (CoglContext *context)
   g_slist_free (context->texture_types);
   g_slist_free (context->buffer_types);
 
-#ifdef HAVE_COGL_GLES2
-  if (_context->flushed_modelview_stack)
-    cogl_object_unref (_context->flushed_modelview_stack);
-  if (_context->flushed_projection_stack)
-    cogl_object_unref (_context->flushed_projection_stack);
-#endif
+  if (_context->current_modelview_stack)
+    cogl_object_unref (_context->current_modelview_stack);
+  if (_context->current_projection_stack)
+    cogl_object_unref (_context->current_projection_stack);
+  _cogl_matrix_stack_destroy_cache (&context->builtin_flushed_projection);
+  _cogl_matrix_stack_destroy_cache (&context->builtin_flushed_modelview);
 
   cogl_pipeline_cache_free (context->pipeline_cache);
 
@@ -569,3 +569,23 @@ _cogl_context_update_features (CoglContext *context,
 
   g_assert_not_reached ();
 }
+
+void
+_cogl_context_set_current_projection (CoglContext *context,
+                                      CoglMatrixStack *stack)
+{
+  cogl_object_ref (stack);
+  if (context->current_projection_stack)
+    cogl_object_unref (context->current_projection_stack);
+  context->current_projection_stack = stack;
+}
+
+void
+_cogl_context_set_current_modelview (CoglContext *context,
+                                     CoglMatrixStack *stack)
+{
+  cogl_object_ref (stack);
+  if (context->current_modelview_stack)
+    cogl_object_unref (context->current_modelview_stack);
+  context->current_modelview_stack = stack;
+}
diff --git a/cogl/cogl-framebuffer.c b/cogl/cogl-framebuffer.c
index eaa0be7..829b5c2 100644
--- a/cogl/cogl-framebuffer.c
+++ b/cogl/cogl-framebuffer.c
@@ -1382,20 +1382,20 @@ static unsigned long
 _cogl_framebuffer_compare_modelview_state (CoglFramebuffer *a,
                                            CoglFramebuffer *b)
 {
-  if (!_cogl_matrix_stack_equal (a->modelview_stack, b->modelview_stack))
-    return COGL_FRAMEBUFFER_STATE_MODELVIEW;
-  else
-    return 0;
+  /* We always want to flush the modelview state. All this does is set
+     the current modelview stack on the context to the framebuffer's
+     stack. */
+  return COGL_FRAMEBUFFER_STATE_MODELVIEW;
 }
 
 static unsigned long
 _cogl_framebuffer_compare_projection_state (CoglFramebuffer *a,
                                             CoglFramebuffer *b)
 {
-  if (!_cogl_matrix_stack_equal (a->projection_stack, b->projection_stack))
-    return COGL_FRAMEBUFFER_STATE_MODELVIEW;
-  else
-    return 0;
+  /* We always want to flush the projection state. All this does is
+     set the current projection stack on the context to the
+     framebuffer's stack. */
+  return COGL_FRAMEBUFFER_STATE_PROJECTION;
 }
 
 static unsigned long
@@ -1533,17 +1533,15 @@ _cogl_framebuffer_flush_dither_state (CoglFramebuffer *framebuffer)
 static void
 _cogl_framebuffer_flush_modelview_state (CoglFramebuffer *framebuffer)
 {
-  _cogl_matrix_stack_flush_to_gl (framebuffer->context,
-                                  framebuffer->modelview_stack,
-                                  COGL_MATRIX_MODELVIEW);
+  _cogl_context_set_current_modelview (framebuffer->context,
+                                       framebuffer->modelview_stack);
 }
 
 static void
 _cogl_framebuffer_flush_projection_state (CoglFramebuffer *framebuffer)
 {
-  _cogl_matrix_stack_flush_to_gl (framebuffer->context,
-                                  framebuffer->projection_stack,
-                                  COGL_MATRIX_PROJECTION);
+  _cogl_context_set_current_projection (framebuffer->context,
+                                        framebuffer->projection_stack);
 }
 
 static void
diff --git a/cogl/cogl-journal.c b/cogl/cogl-journal.c
index e04bf1c..63cf90f 100644
--- a/cogl/cogl-journal.c
+++ b/cogl/cogl-journal.c
@@ -292,9 +292,7 @@ _cogl_journal_flush_modelview_and_entries (CoglJournalEntry *batch_start,
     {
       _cogl_matrix_stack_set (state->modelview_stack,
                               &batch_start->model_view);
-      _cogl_matrix_stack_flush_to_gl (ctx,
-                                      state->modelview_stack,
-                                      COGL_MATRIX_MODELVIEW);
+      _cogl_context_set_current_modelview (ctx, state->modelview_stack);
     }
 
   attributes = (CoglAttribute **)state->attributes->data;
@@ -725,17 +723,13 @@ _cogl_journal_flush_clip_stacks_and_entries (CoglJournalEntry *batch_start,
   if (G_LIKELY (!(COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM))))
     {
       _cogl_matrix_stack_load_identity (state->modelview_stack);
-      _cogl_matrix_stack_flush_to_gl (ctx,
-                                      state->modelview_stack,
-                                      COGL_MATRIX_MODELVIEW);
+      _cogl_context_set_current_modelview (ctx, state->modelview_stack);
     }
 
   /* Setting up the clip state can sometimes also flush the projection
      matrix so we should flush it again. This will be a no-op if the
      clip code didn't modify the projection */
-  _cogl_matrix_stack_flush_to_gl (ctx,
-                                  state->projection_stack,
-                                  COGL_MATRIX_PROJECTION);
+  _cogl_context_set_current_projection (ctx, state->projection_stack);
 
   batch_and_call (batch_start,
                   batch_len,
diff --git a/cogl/cogl-matrix-stack.c b/cogl/cogl-matrix-stack.c
index a6e30dd..c97d3b9 100644
--- a/cogl/cogl-matrix-stack.c
+++ b/cogl/cogl-matrix-stack.c
@@ -362,124 +362,117 @@ _cogl_matrix_stack_set (CoglMatrixStack  *stack,
   stack->age++;
 }
 
-#if defined (HAVE_COGL_GL) || defined (HAVE_COGL_GLES)
-
 static void
-flush_to_fixed_api_gl (CoglContext *context,
-                       gboolean is_identity,
-                       const CoglMatrix *matrix,
-                       void *user_data)
+_cogl_matrix_stack_flush_matrix_to_gl_builtin (CoglContext *ctx,
+                                               gboolean is_identity,
+                                               CoglMatrix *matrix,
+                                               CoglMatrixMode mode)
 {
-  if (is_identity)
-    GE (context, glLoadIdentity ());
-  else
-    GE (context, glLoadMatrixf (cogl_matrix_get_array (matrix)) );
-}
-
-#endif
-
-void
-_cogl_prepare_matrix_stack_for_flush (CoglContext *context,
-                                      CoglMatrixStack *stack,
-                                      CoglMatrixMode mode,
-                                      CoglMatrixStackFlushFunc callback,
-                                      void *user_data)
-{
-  CoglMatrixState *state = _cogl_matrix_stack_top (stack);
-
-  /* Because Cogl defines texture coordinates to have a top left origin and
-   * because offscreen framebuffers may be used for rendering to textures we
-   * always render upside down to offscreen buffers.
-   */
-  if (mode == COGL_MATRIX_PROJECTION &&
-      cogl_is_offscreen (cogl_get_draw_framebuffer ()))
-    {
-      CoglMatrix flipped_projection;
-      CoglMatrix *projection =
-        state->is_identity ? &context->identity_matrix : &state->matrix;
+  g_assert (ctx->driver == COGL_DRIVER_GL ||
+            ctx->driver == COGL_DRIVER_GLES1);
 
-      cogl_matrix_multiply (&flipped_projection,
-                            &context->y_flip_matrix, projection);
-      callback (context, FALSE, &flipped_projection, user_data);
-    }
-  else
-    {
-      CoglMatrix *modelview =
-        state->is_identity ? &context->identity_matrix : &state->matrix;
-      callback (context, state->is_identity, modelview, user_data);
-    }
-}
-
-void
-_cogl_matrix_stack_flush_to_gl (CoglContext *context,
-                                CoglMatrixStack *stack,
-                                CoglMatrixMode mode)
-{
-  if (context->driver == COGL_DRIVER_GLES2)
+#if defined (HAVE_COGL_GL) || defined (HAVE_COGL_GLES)
+  if (ctx->flushed_matrix_mode != mode)
     {
-      /* Under GLES2 we need to flush the matrices differently because
-         they are stored in uniforms attached to the program instead of
-         the global GL context state. At this point we can't be sure that
-         the right program will be generated so instead we'll just store a
-         reference to the matrix stack that is intended to be flushed and
-         update the uniform once the program is ready. */
+      GLenum gl_mode = 0;
 
       switch (mode)
         {
         case COGL_MATRIX_MODELVIEW:
-          cogl_object_ref (stack);
-          if (context->flushed_modelview_stack)
-            cogl_object_unref (context->flushed_modelview_stack);
-          context->flushed_modelview_stack = stack;
+          gl_mode = GL_MODELVIEW;
           break;
 
         case COGL_MATRIX_PROJECTION:
-          cogl_object_ref (stack);
-          if (context->flushed_projection_stack)
-            cogl_object_unref (context->flushed_projection_stack);
-          context->flushed_projection_stack = stack;
+          gl_mode = GL_PROJECTION;
           break;
 
         case COGL_MATRIX_TEXTURE:
-          /* This shouldn't happen because the texture matrices are
-             handled by the GLSL pipeline backend */
-          g_assert_not_reached ();
+          gl_mode = GL_TEXTURE;
           break;
         }
+
+      GE (ctx, glMatrixMode (gl_mode));
+      ctx->flushed_matrix_mode = mode;
     }
 
-#if defined (HAVE_COGL_GL) || defined (HAVE_COGL_GLES)
+  if (is_identity)
+    GE (ctx, glLoadIdentity ());
   else
-    {
-      if (context->flushed_matrix_mode != mode)
-        {
-          GLenum gl_mode = 0;
-
-          switch (mode)
-            {
-            case COGL_MATRIX_MODELVIEW:
-              gl_mode = GL_MODELVIEW;
-              break;
-
-            case COGL_MATRIX_PROJECTION:
-              gl_mode = GL_PROJECTION;
-              break;
-
-            case COGL_MATRIX_TEXTURE:
-              gl_mode = GL_TEXTURE;
-              break;
-            }
+    GE (ctx, glLoadMatrixf (cogl_matrix_get_array (matrix)));
+#endif
+}
 
-          GE (context, glMatrixMode (gl_mode));
-          context->flushed_matrix_mode = mode;
-        }
+void
+_cogl_matrix_stack_flush_to_gl_builtins (CoglContext *ctx,
+                                         CoglMatrixStack *stack,
+                                         CoglMatrixMode mode,
+                                         gboolean disable_flip)
+{
+  g_assert (ctx->driver == COGL_DRIVER_GL ||
+            ctx->driver == COGL_DRIVER_GLES1);
 
-      _cogl_prepare_matrix_stack_for_flush (context,
-                                            stack,
-                                            mode,
-                                            flush_to_fixed_api_gl,
-                                            stack);
-    }
+#if defined (HAVE_COGL_GL) || defined (HAVE_COGL_GLES)
+  {
+    gboolean needs_flip;
+    CoglMatrixState *state;
+    CoglMatrixStackCache *cache;
+
+    state = _cogl_matrix_stack_top (stack);
+
+    if (mode == COGL_MATRIX_PROJECTION)
+      {
+        /* Because Cogl defines texture coordinates to have a top left
+         * origin and because offscreen framebuffers may be used for
+         * rendering to textures we always render upside down to
+         * offscreen buffers. Also for some backends we need to render
+         * onscreen buffers upside-down too.
+         */
+        if (disable_flip)
+          needs_flip = FALSE;
+        else
+          needs_flip = cogl_is_offscreen (cogl_get_draw_framebuffer ());
+
+        cache = &ctx->builtin_flushed_projection;
+      }
+    else
+      {
+        needs_flip = FALSE;
+
+        if (mode == COGL_MATRIX_MODELVIEW)
+          cache = &ctx->builtin_flushed_modelview;
+        else
+          cache = NULL;
+      }
+
+    /* We don't need to do anything if the state is the same */
+    if (!cache ||
+        _cogl_matrix_stack_check_and_update_cache (stack, cache, needs_flip))
+      {
+        gboolean is_identity = state->is_identity && !needs_flip;
+
+        if (needs_flip)
+          {
+            CoglMatrix flipped_matrix;
+
+            cogl_matrix_multiply (&flipped_matrix,
+                                  &ctx->y_flip_matrix,
+                                  state->is_identity ?
+                                  &ctx->identity_matrix :
+                                  &state->matrix);
+
+            _cogl_matrix_stack_flush_matrix_to_gl_builtin (ctx,
+                                                           /* not identity */
+                                                           FALSE,
+                                                           &flipped_matrix,
+                                                           mode);
+          }
+        else
+          _cogl_matrix_stack_flush_matrix_to_gl_builtin (ctx,
+                                                         is_identity,
+                                                         &state->matrix,
+                                                         mode);
+      }
+  }
 #endif
 }
 
@@ -510,3 +503,51 @@ _cogl_matrix_stack_equal (CoglMatrixStack *stack0,
   else
     return cogl_matrix_equal (&state0->matrix, &state1->matrix);
 }
+
+gboolean
+_cogl_matrix_stack_check_and_update_cache (CoglMatrixStack *stack,
+                                           CoglMatrixStackCache *cache,
+                                           gboolean flip)
+{
+  gboolean is_identity =
+    _cogl_matrix_stack_has_identity_flag (stack) && !flip;
+  gboolean is_dirty;
+
+  if (is_identity && cache->flushed_identity)
+    is_dirty = FALSE;
+  else if (cache->stack == NULL ||
+           cache->stack->age != cache->age ||
+           flip != cache->flipped)
+    is_dirty = TRUE;
+  else
+    is_dirty = (cache->stack != stack &&
+                !_cogl_matrix_stack_equal (cache->stack, stack));
+
+  /* We'll update the cache values even if the stack isn't dirty in
+     case the reason it wasn't dirty is because we compared the
+     matrices and found them to be the same. In that case updating the
+     cache values will avoid the comparison next time */
+  cache->age = stack->age;
+  cogl_object_ref (stack);
+  if (cache->stack)
+    cogl_object_unref (cache->stack);
+  cache->stack = stack;
+  cache->flushed_identity = is_identity;
+  cache->flipped = flip;
+
+  return is_dirty;
+}
+
+void
+_cogl_matrix_stack_init_cache (CoglMatrixStackCache *cache)
+{
+  cache->stack = NULL;
+  cache->flushed_identity = FALSE;
+}
+
+void
+_cogl_matrix_stack_destroy_cache (CoglMatrixStackCache *cache)
+{
+  if (cache->stack)
+    cogl_object_unref (cache->stack);
+}
diff --git a/cogl/cogl-matrix-stack.h b/cogl/cogl-matrix-stack.h
index e16125a..834e5ab 100644
--- a/cogl/cogl-matrix-stack.h
+++ b/cogl/cogl-matrix-stack.h
@@ -34,6 +34,14 @@
 
 typedef struct _CoglMatrixStack CoglMatrixStack;
 
+typedef struct
+{
+  CoglMatrixStack *stack;
+  unsigned int age;
+  gboolean flushed_identity;
+  gboolean flipped;
+} CoglMatrixStackCache;
+
 typedef enum {
   COGL_MATRIX_MODELVIEW,
   COGL_MATRIX_PROJECTION,
@@ -107,10 +115,12 @@ _cogl_matrix_stack_get (CoglMatrixStack *stack,
 void
 _cogl_matrix_stack_set (CoglMatrixStack *stack,
                         const CoglMatrix *matrix);
+
 void
-_cogl_matrix_stack_flush_to_gl (CoglContext *ctx,
-                                CoglMatrixStack *stack,
-                                CoglMatrixMode mode);
+_cogl_matrix_stack_flush_to_gl_builtins (CoglContext *ctx,
+                                         CoglMatrixStack *stack,
+                                         CoglMatrixMode mode,
+                                         gboolean disable_flip);
 
 unsigned int
 _cogl_matrix_stack_get_age (CoglMatrixStack *stack);
@@ -122,15 +132,19 @@ _cogl_matrix_stack_get_age (CoglMatrixStack *stack);
 gboolean
 _cogl_matrix_stack_has_identity_flag (CoglMatrixStack *stack);
 
-void
-_cogl_prepare_matrix_stack_for_flush (CoglContext *context,
-                                      CoglMatrixStack *stack,
-                                      CoglMatrixMode mode,
-                                      CoglMatrixStackFlushFunc callback,
-                                      void *user_data);
-
 gboolean
 _cogl_matrix_stack_equal (CoglMatrixStack *stack0,
                           CoglMatrixStack *stack1);
 
+void
+_cogl_matrix_stack_init_cache (CoglMatrixStackCache *cache);
+
+gboolean
+_cogl_matrix_stack_check_and_update_cache (CoglMatrixStack *stack,
+                                           CoglMatrixStackCache *cache,
+                                           gboolean flip);
+
+void
+_cogl_matrix_stack_destroy_cache (CoglMatrixStackCache *cache);
+
 #endif /* __COGL_MATRIX_STACK_H */
diff --git a/cogl/cogl-pipeline-private.h b/cogl/cogl-pipeline-private.h
index 8e7f321..f676b1a 100644
--- a/cogl/cogl-pipeline-private.h
+++ b/cogl/cogl-pipeline-private.h
@@ -137,11 +137,21 @@
 
 /* If we have either of the GLSL backends then we also need a GLSL
    progend to combine the shaders generated into a single
-   program. Currently there is only one progend but if we ever add
-   other languages they would likely need their own progend too. The
-   progends are different from the other backends because there can be
-   more than one in use for each pipeline. All of the progends are
-   invoked whenever a pipeline is flushed. */
+   program. Same goes for the fixed progends which are used to flush
+   the matrices */
+#ifdef COGL_PIPELINE_FRAGEND_FIXED
+
+#define COGL_PIPELINE_PROGEND_FIXED      0
+
+#ifdef COGL_PIPELINE_FRAGEND_GLSL
+#define COGL_PIPELINE_PROGEND_GLSL       1
+#define COGL_PIPELINE_N_PROGENDS         2
+#else
+#define COGL_PIPELINE_N_PROGENDS         1
+#endif
+
+#else /* COGL_PIPELINE_FRAGEND_FIXED */
+
 #ifdef COGL_PIPELINE_FRAGEND_GLSL
 #define COGL_PIPELINE_PROGEND_GLSL       0
 #define COGL_PIPELINE_N_PROGENDS         1
@@ -149,6 +159,8 @@
 #define COGL_PIPELINE_N_PROGENDS         0
 #endif
 
+#endif /* COGL_PIPELINE_FRAGEND_FIXED */
+
 /* XXX: should I rename these as
  * COGL_PIPELINE_STATE_INDEX_XYZ... ?
  */
diff --git a/cogl/cogl-pipeline-progend-fixed-private.h b/cogl/cogl-pipeline-progend-fixed-private.h
new file mode 100644
index 0000000..a256059
--- /dev/null
+++ b/cogl/cogl-pipeline-progend-fixed-private.h
@@ -0,0 +1,36 @@
+/*
+ * Cogl
+ *
+ * An object oriented GL/GLES Abstraction/Utility Layer
+ *
+ * Copyright (C) 2011 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, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ *
+ *
+ * Authors:
+ *   Neil Roberts <neil linux intel com>
+ */
+
+#ifndef __COGL_PIPELINE_PROGEND_FIXED_PRIVATE_H
+#define __COGL_PIPELINE_PROGEND_FIXED_PRIVATE_H
+
+#include "cogl-pipeline-private.h"
+
+extern const CoglPipelineProgend _cogl_pipeline_fixed_progend;
+
+#endif /* __COGL_PIPELINE_PROGEND_FIXED_PRIVATE_H */
+
diff --git a/cogl/cogl-pipeline-progend-fixed.c b/cogl/cogl-pipeline-progend-fixed.c
new file mode 100644
index 0000000..d715439
--- /dev/null
+++ b/cogl/cogl-pipeline-progend-fixed.c
@@ -0,0 +1,69 @@
+/*
+ * Cogl
+ *
+ * An object oriented GL/GLES Abstraction/Utility Layer
+ *
+ * Copyright (C) 2011 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, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ *
+ *
+ * Authors:
+ *   Neil Roberts <neil linux intel com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#include "cogl-pipeline-private.h"
+
+#ifdef COGL_PIPELINE_PROGEND_FIXED
+
+#include "cogl-context.h"
+#include "cogl-context-private.h"
+
+static void
+_cogl_pipeline_progend_fixed_pre_paint (CoglPipeline *pipeline)
+{
+  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+  if (pipeline->vertend != COGL_PIPELINE_VERTEND_FIXED)
+    return;
+
+  if (ctx->current_projection_stack)
+    _cogl_matrix_stack_flush_to_gl_builtins (ctx,
+                                             ctx->current_projection_stack,
+                                             COGL_MATRIX_PROJECTION,
+                                             FALSE /* enable flip */);
+  if (ctx->current_modelview_stack)
+    _cogl_matrix_stack_flush_to_gl_builtins (ctx,
+                                             ctx->current_modelview_stack,
+                                             COGL_MATRIX_MODELVIEW,
+                                             FALSE /* enable flip */);
+}
+
+const CoglPipelineProgend _cogl_pipeline_fixed_progend =
+  {
+    NULL, /* end */
+    NULL, /* pre_change_notify */
+    NULL, /* layer_pre_change_notify */
+    _cogl_pipeline_progend_fixed_pre_paint
+  };
+
+#endif /* COGL_PIPELINE_PROGEND_FIXED */
diff --git a/cogl/cogl-pipeline-progend-glsl.c b/cogl/cogl-pipeline-progend-glsl.c
index bc96cb8..561ea3e 100644
--- a/cogl/cogl-pipeline-progend-glsl.c
+++ b/cogl/cogl-pipeline-progend-glsl.c
@@ -48,6 +48,7 @@
 #include "cogl-pipeline-cache.h"
 #include "cogl-pipeline-state-private.h"
 #include "cogl-attribute-private.h"
+#include "cogl-framebuffer-private.h"
 
 #ifdef HAVE_COGL_GLES2
 
@@ -121,11 +122,8 @@ typedef struct
   GLint projection_uniform;
   GLint mvp_uniform;
 
-  CoglMatrixStack *flushed_modelview_stack;
-  unsigned int flushed_modelview_stack_age;
-  gboolean flushed_modelview_is_identity;
-  CoglMatrixStack *flushed_projection_stack;
-  unsigned int flushed_projection_stack_age;
+  CoglMatrixStackCache projection_cache;
+  CoglMatrixStackCache modelview_cache;
 #endif
 
   /* We need to track the last pipeline that the program was used with
@@ -140,6 +138,13 @@ typedef struct
   /* Array of attribute locations. */
   GArray *attribute_locations;
 
+  /* The 'flip' uniform is used to flip the geometry upside-down when
+     the framebuffer requires it only when there are vertex
+     snippets. Otherwise this is acheived using the projection
+     matrix */
+  GLint flip_uniform;
+  int flushed_flip_state;
+
   UnitState *unit_state;
 } CoglPipelineProgramState;
 
@@ -225,17 +230,10 @@ clear_attribute_cache (CoglPipelineProgramState *program_state)
 static void
 clear_flushed_matrix_stacks (CoglPipelineProgramState *program_state)
 {
-  if (program_state->flushed_modelview_stack)
-    {
-      cogl_object_unref (program_state->flushed_modelview_stack);
-      program_state->flushed_modelview_stack = NULL;
-    }
-  if (program_state->flushed_projection_stack)
-    {
-      cogl_object_unref (program_state->flushed_projection_stack);
-      program_state->flushed_projection_stack = NULL;
-    }
-  program_state->flushed_modelview_is_identity = FALSE;
+  _cogl_matrix_stack_destroy_cache (&program_state->projection_cache);
+  _cogl_matrix_stack_init_cache (&program_state->projection_cache);
+  _cogl_matrix_stack_destroy_cache (&program_state->modelview_cache);
+  _cogl_matrix_stack_init_cache (&program_state->modelview_cache);
 }
 
 #endif /* HAVE_COGL_GLES2 */
@@ -253,9 +251,8 @@ program_state_new (int n_layers)
   program_state->uniform_locations = NULL;
   program_state->attribute_locations = NULL;
 #ifdef HAVE_COGL_GLES2
-  program_state->flushed_modelview_stack = NULL;
-  program_state->flushed_modelview_is_identity = FALSE;
-  program_state->flushed_projection_stack = NULL;
+  _cogl_matrix_stack_init_cache (&program_state->modelview_cache);
+  _cogl_matrix_stack_init_cache (&program_state->projection_cache);
 #endif
 
   return program_state;
@@ -282,7 +279,10 @@ destroy_program_state (void *user_data,
 
 #ifdef HAVE_COGL_GLES2
       if (ctx->driver == COGL_DRIVER_GLES2)
-        clear_flushed_matrix_stacks (program_state);
+        {
+          _cogl_matrix_stack_destroy_cache (&program_state->projection_cache);
+          _cogl_matrix_stack_destroy_cache (&program_state->modelview_cache);
+        }
 #endif
 
       if (program_state->program)
@@ -793,6 +793,10 @@ _cogl_pipeline_progend_glsl_end (CoglPipeline *pipeline,
                                    get_uniform_cb,
                                    &state);
       clear_attribute_cache (program_state);
+
+      GE_RET (program_state->flip_uniform,
+              ctx, glGetUniformLocation (gl_program, "_cogl_flip_vector"));
+      program_state->flushed_flip_state = -1;
     }
 
   state.unit = 0;
@@ -923,174 +927,158 @@ _cogl_pipeline_progend_glsl_layer_pre_change_notify (
     }
 }
 
-#ifdef HAVE_COGL_GLES2
-
-static void
-flush_modelview_cb (CoglContext *ctx,
-                    gboolean is_identity,
-                    const CoglMatrix *matrix,
-                    void *user_data)
-{
-  CoglPipelineProgramState *program_state = user_data;
-
-  GE( ctx, glUniformMatrix4fv (program_state->modelview_uniform, 1, FALSE,
-                               cogl_matrix_get_array (matrix)) );
-}
-
-static void
-flush_projection_cb (CoglContext *ctx,
-                     gboolean is_identity,
-                     const CoglMatrix *matrix,
-                     void *user_data)
-{
-  CoglPipelineProgramState *program_state = user_data;
-
-  GE( ctx, glUniformMatrix4fv (program_state->projection_uniform, 1, FALSE,
-                               cogl_matrix_get_array (matrix)) );
-}
-
-typedef struct
-{
-  CoglPipelineProgramState *program_state;
-  const CoglMatrix *projection_matrix;
-} FlushCombinedData;
-
-static void
-flush_combined_step_two_cb (CoglContext *ctx,
-                            gboolean is_identity,
-                            const CoglMatrix *matrix,
-                            void *user_data)
-{
-  FlushCombinedData *data = user_data;
-  CoglMatrix mvp_matrix;
-
-  /* If the modelview is the identity then we can bypass the matrix
-     multiplication */
-  if (is_identity)
-    {
-      const float *array = cogl_matrix_get_array (data->projection_matrix);
-      GE( ctx, glUniformMatrix4fv (data->program_state->mvp_uniform,
-                                   1, FALSE, array ) );
-    }
-  else
-    {
-      cogl_matrix_multiply (&mvp_matrix,
-                            data->projection_matrix,
-                            matrix);
-
-      GE( ctx, glUniformMatrix4fv (data->program_state->mvp_uniform, 1, FALSE,
-                                   cogl_matrix_get_array (&mvp_matrix)) );
-    }
-}
-
-static void
-flush_combined_step_one_cb (CoglContext *ctx,
-                            gboolean is_identity,
-                            const CoglMatrix *matrix,
-                            void *user_data)
-{
-  FlushCombinedData data;
-
-  data.program_state = user_data;
-  data.projection_matrix = matrix;
-
-  _cogl_prepare_matrix_stack_for_flush (ctx,
-                                        ctx->flushed_modelview_stack,
-                                        COGL_MATRIX_MODELVIEW,
-                                        flush_combined_step_two_cb,
-                                        &data);
-}
-
 static void
 _cogl_pipeline_progend_glsl_pre_paint (CoglPipeline *pipeline)
 {
-  CoglPipelineProgramState *program_state = get_program_state (pipeline);
-  gboolean modelview_changed;
-  gboolean projection_changed;
+  gboolean needs_flip;
+  CoglMatrixStack *projection_stack;
+  CoglMatrixStack *modelview_stack;
+  CoglPipelineProgramState *program_state;
 
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
-  if (ctx->driver != COGL_DRIVER_GLES2)
+  if (pipeline->vertend != COGL_PIPELINE_VERTEND_GLSL)
     return;
 
-  /* We only need to update the matrices if we're using the the GLSL
-     vertend, but this is a requirement on GLES2 anyway */
-  _COGL_RETURN_IF_FAIL (pipeline->vertend == COGL_PIPELINE_VERTEND_GLSL);
-
   program_state = get_program_state (pipeline);
 
+  projection_stack = ctx->current_projection_stack;
+  modelview_stack = ctx->current_modelview_stack;
+
   /* An initial pipeline is flushed while creating the context. At
-     this point there are no matrices flushed so we can't do
+     this point there are no matrices selected so we can't do
      anything */
-  if (ctx->flushed_modelview_stack == NULL ||
-      ctx->flushed_projection_stack == NULL)
+  if (modelview_stack == NULL || projection_stack == NULL)
     return;
 
-  /* When flushing from the journal the modelview matrix is usually
-     the identity matrix so it makes sense to optimise this case by
-     specifically checking whether we already have the identity matrix
-     which will catch a lot of common cases of redundant flushing */
-  if (program_state->flushed_modelview_is_identity &&
-      _cogl_matrix_stack_has_identity_flag (ctx->flushed_modelview_stack))
-    modelview_changed = FALSE;
-  else
-    modelview_changed =
-      program_state->flushed_modelview_stack != ctx->flushed_modelview_stack ||
-      program_state->flushed_modelview_stack_age !=
-      _cogl_matrix_stack_get_age (program_state->flushed_modelview_stack);
+  needs_flip = cogl_is_offscreen (cogl_get_draw_framebuffer ());
+
+#ifdef HAVE_COGL_GLES2
+  if (ctx->driver == COGL_DRIVER_GLES2)
+    {
+      gboolean modelview_changed;
+      gboolean projection_changed;
+      gboolean need_modelview;
+      gboolean need_projection;
+      CoglMatrix modelview, projection;
+
+      projection_changed =
+        _cogl_matrix_stack_check_and_update_cache (projection_stack,
+                                                   &program_state->
+                                                   projection_cache,
+                                                   needs_flip &&
+                                                   program_state->
+                                                   flip_uniform == -1);
+
+      modelview_changed =
+        _cogl_matrix_stack_check_and_update_cache (modelview_stack,
+                                                   &program_state->
+                                                   modelview_cache,
+                                                   /* never flip modelview */
+                                                   FALSE);
+
+      if (modelview_changed || projection_changed)
+        {
+          if (program_state->mvp_uniform != -1)
+            need_modelview = need_projection = TRUE;
+          else
+            {
+              need_projection = (program_state->projection_uniform != -1 &&
+                                 projection_changed);
+              need_modelview = (program_state->modelview_uniform != -1 &&
+                                modelview_changed);
+            }
+
+          if (need_modelview)
+            _cogl_matrix_stack_get (modelview_stack, &modelview);
+          if (need_projection)
+            {
+              if (needs_flip && program_state->flip_uniform == -1)
+                {
+                  CoglMatrix tmp_matrix;
+                  _cogl_matrix_stack_get (projection_stack, &tmp_matrix);
+                  cogl_matrix_multiply (&projection,
+                                        &ctx->y_flip_matrix,
+                                        &tmp_matrix);
+                }
+              else
+                _cogl_matrix_stack_get (projection_stack, &projection);
+            }
 
-  projection_changed =
-    program_state->flushed_projection_stack != ctx->flushed_projection_stack ||
-    program_state->flushed_projection_stack_age !=
-    _cogl_matrix_stack_get_age (program_state->flushed_projection_stack);
+          if (projection_changed && program_state->projection_uniform != -1)
+            GE (ctx, glUniformMatrix4fv (program_state->projection_uniform,
+                                         1, /* count */
+                                         FALSE, /* transpose */
+                                         cogl_matrix_get_array (&projection)));
 
-  if (modelview_changed)
+          if (modelview_changed && program_state->modelview_uniform != -1)
+            GE (ctx, glUniformMatrix4fv (program_state->modelview_uniform,
+                                         1, /* count */
+                                         FALSE, /* transpose */
+                                         cogl_matrix_get_array (&modelview)));
+
+          if (program_state->mvp_uniform != -1)
+            {
+              /* The journal usually uses an identity matrix for the
+                 modelview so we can optimise this common case by
+                 avoiding the matrix multiplication */
+              if (_cogl_matrix_stack_has_identity_flag (modelview_stack))
+                {
+                  GE (ctx,
+                      glUniformMatrix4fv (program_state->mvp_uniform,
+                                          1, /* count */
+                                          FALSE, /* transpose */
+                                          cogl_matrix_get_array (&projection)));
+                }
+              else
+                {
+                  CoglMatrix combined;
+
+                  cogl_matrix_multiply (&combined,
+                                        &projection,
+                                        &modelview);
+                  GE (ctx,
+                      glUniformMatrix4fv (program_state->mvp_uniform,
+                                          1, /* count */
+                                          FALSE, /* transpose */
+                                          cogl_matrix_get_array (&combined)));
+                }
+            }
+        }
+    }
+  else
+#endif
     {
-      cogl_object_ref (ctx->flushed_modelview_stack);
-      if (program_state->flushed_modelview_stack)
-        cogl_object_unref (program_state->flushed_modelview_stack);
-      program_state->flushed_modelview_stack = ctx->flushed_modelview_stack;
-      program_state->flushed_modelview_stack_age =
-        _cogl_matrix_stack_get_age (ctx->flushed_modelview_stack);
-      program_state->flushed_modelview_is_identity =
-        _cogl_matrix_stack_has_identity_flag (ctx->flushed_modelview_stack);
-
-      if (program_state->modelview_uniform != -1)
-        _cogl_prepare_matrix_stack_for_flush (ctx,
-                                              program_state
-                                                ->flushed_modelview_stack,
-                                              COGL_MATRIX_MODELVIEW,
-                                              flush_modelview_cb,
-                                              program_state);
+      gboolean disable_flip;
+
+      /* If there are vertex snippets, then we'll disable flipping the
+         geometry via the matrix and use the flip vertex instead */
+      disable_flip = program_state->flip_uniform != -1;
+
+      _cogl_matrix_stack_flush_to_gl_builtins (ctx,
+                                               projection_stack,
+                                               COGL_MATRIX_PROJECTION,
+                                               disable_flip);
+      _cogl_matrix_stack_flush_to_gl_builtins (ctx,
+                                               modelview_stack,
+                                               COGL_MATRIX_MODELVIEW,
+                                               disable_flip);
     }
 
-  if (projection_changed)
+  if (program_state->flip_uniform != -1
+      && program_state->flushed_flip_state != needs_flip)
     {
-      cogl_object_ref (ctx->flushed_projection_stack);
-      if (program_state->flushed_projection_stack)
-        cogl_object_unref (program_state->flushed_projection_stack);
-      program_state->flushed_projection_stack = ctx->flushed_projection_stack;
-      program_state->flushed_projection_stack_age =
-        _cogl_matrix_stack_get_age (ctx->flushed_projection_stack);
-
-      if (program_state->projection_uniform != -1)
-        _cogl_prepare_matrix_stack_for_flush (ctx,
-                                              program_state
-                                                ->flushed_projection_stack,
-                                              COGL_MATRIX_PROJECTION,
-                                              flush_projection_cb,
-                                              program_state);
+      static const float do_flip[4] = { 1.0f, -1.0f, 1.0f, 1.0f };
+      static const float dont_flip[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
+      GE( ctx, glUniform4fv (program_state->flip_uniform,
+                             1, /* count */
+                             needs_flip ? do_flip : dont_flip) );
+      program_state->flushed_flip_state = needs_flip;
     }
-
-  if (program_state->mvp_uniform != -1 &&
-      (modelview_changed || projection_changed))
-    _cogl_prepare_matrix_stack_for_flush (ctx,
-                                          ctx->flushed_projection_stack,
-                                          COGL_MATRIX_PROJECTION,
-                                          flush_combined_step_one_cb,
-                                          program_state);
 }
 
+#ifdef HAVE_COGL_GLES2
+
 static void
 update_float_uniform (CoglPipeline *pipeline,
                       int uniform_location,
@@ -1112,12 +1100,7 @@ const CoglPipelineProgend _cogl_pipeline_glsl_progend =
     _cogl_pipeline_progend_glsl_end,
     _cogl_pipeline_progend_glsl_pre_change_notify,
     _cogl_pipeline_progend_glsl_layer_pre_change_notify,
-#ifdef HAVE_COGL_GLES2
     _cogl_pipeline_progend_glsl_pre_paint
-#else
-    NULL
-#endif
   };
 
 #endif /* COGL_PIPELINE_PROGEND_GLSL */
-
diff --git a/cogl/cogl-pipeline-vertend-fixed.c b/cogl/cogl-pipeline-vertend-fixed.c
index 3b19016..1d60c83 100644
--- a/cogl/cogl-pipeline-vertend-fixed.c
+++ b/cogl/cogl-pipeline-vertend-fixed.c
@@ -99,8 +99,9 @@ _cogl_pipeline_vertend_fixed_add_layer (CoglPipeline *pipeline,
 
       _cogl_set_active_texture_unit (unit_index);
 
-      _cogl_matrix_stack_flush_to_gl (ctx, unit->matrix_stack,
-                                      COGL_MATRIX_TEXTURE);
+      _cogl_matrix_stack_flush_to_gl_builtins (ctx, unit->matrix_stack,
+                                               COGL_MATRIX_TEXTURE,
+                                               FALSE /* enable flip */);
     }
 
   return TRUE;
diff --git a/cogl/cogl-pipeline-vertend-glsl.c b/cogl/cogl-pipeline-vertend-glsl.c
index bd99557..602cf02 100644
--- a/cogl/cogl-pipeline-vertend-glsl.c
+++ b/cogl/cogl-pipeline-vertend-glsl.c
@@ -43,6 +43,7 @@
 #include "cogl-handle.h"
 #include "cogl-program-private.h"
 #include "cogl-pipeline-vertend-glsl-private.h"
+#include "cogl-pipeline-state-private.h"
 
 const CoglPipelineVertend _cogl_pipeline_glsl_vertend;
 
@@ -328,9 +329,10 @@ _cogl_pipeline_vertend_glsl_add_layer (CoglPipeline *pipeline,
 
           _cogl_set_active_texture_unit (unit_index);
 
-          _cogl_matrix_stack_flush_to_gl (ctx,
-                                          unit->matrix_stack,
-                                          COGL_MATRIX_TEXTURE);
+          _cogl_matrix_stack_flush_to_gl_builtins (ctx,
+                                                   unit->matrix_stack,
+                                                   COGL_MATRIX_TEXTURE,
+                                                   FALSE /* do flip */);
         }
     }
 
@@ -410,6 +412,7 @@ _cogl_pipeline_vertend_glsl_end (CoglPipeline *pipeline,
       GLint compile_status;
       GLuint shader;
       CoglPipelineSnippetData snippet_data;
+      CoglPipelineSnippetList *vertex_snippets;
 
       COGL_STATIC_COUNTER (vertend_glsl_compile_counter,
                            "glsl vertex compile counter",
@@ -432,9 +435,11 @@ _cogl_pipeline_vertend_glsl_end (CoglPipeline *pipeline,
                        "  cogl_color_out = cogl_color_in;\n"
                        "}\n");
 
+      vertex_snippets = get_vertex_snippets (pipeline);
+
       /* Add hooks for the vertex transform part */
       memset (&snippet_data, 0, sizeof (snippet_data));
-      snippet_data.snippets = get_vertex_snippets (pipeline);
+      snippet_data.snippets = vertex_snippets;
       snippet_data.hook = COGL_SNIPPET_HOOK_VERTEX_TRANSFORM;
       snippet_data.chain_function = "cogl_real_vertex_transform";
       snippet_data.final_name = "cogl_vertex_transform";
@@ -444,14 +449,35 @@ _cogl_pipeline_vertend_glsl_end (CoglPipeline *pipeline,
 
       /* Add all of the hooks for vertex processing */
       memset (&snippet_data, 0, sizeof (snippet_data));
-      snippet_data.snippets = get_vertex_snippets (pipeline);
+      snippet_data.snippets = vertex_snippets;
       snippet_data.hook = COGL_SNIPPET_HOOK_VERTEX;
       snippet_data.chain_function = "cogl_generated_source";
-      snippet_data.final_name = "main";
+      snippet_data.final_name = "cogl_vertex_hook";
       snippet_data.function_prefix = "cogl_vertex_hook";
       snippet_data.source_buf = shader_state->source;
       _cogl_pipeline_snippet_generate_code (&snippet_data);
 
+      g_string_append (shader_state->source,
+                       "void\n"
+                       "main ()\n"
+                       "{\n"
+                       "  cogl_vertex_hook ();\n");
+
+      /* If there are any snippets then we can't rely on the
+         projection matrix to flip the rendering for offscreen buffers
+         so we'll need to flip it using an extra statement and a
+         uniform */
+      if (_cogl_pipeline_has_vertex_snippets (pipeline))
+        {
+          g_string_append (shader_state->header,
+                           "uniform vec4 _cogl_flip_vector;\n");
+          g_string_append (shader_state->source,
+                           "  cogl_position_out *= _cogl_flip_vector;\n");
+        }
+
+      g_string_append (shader_state->source,
+                       "}\n");
+
       GE_RET( shader, ctx, glCreateShader (GL_VERTEX_SHADER) );
 
       lengths[0] = shader_state->header->len;
diff --git a/cogl/cogl-pipeline.c b/cogl/cogl-pipeline.c
index 7277782..74ba160 100644
--- a/cogl/cogl-pipeline.c
+++ b/cogl/cogl-pipeline.c
@@ -71,9 +71,6 @@ _cogl_pipeline_progends[MAX (COGL_PIPELINE_N_PROGENDS, 1)];
 #ifdef COGL_PIPELINE_FRAGEND_FIXED
 #include "cogl-pipeline-fragend-fixed-private.h"
 #endif
-#ifdef COGL_PIPELINE_PROGEND_GLSL
-#include "cogl-pipeline-progend-glsl-private.h"
-#endif
 
 #ifdef COGL_PIPELINE_VERTEND_GLSL
 #include "cogl-pipeline-vertend-glsl-private.h"
@@ -82,6 +79,13 @@ _cogl_pipeline_progends[MAX (COGL_PIPELINE_N_PROGENDS, 1)];
 #include "cogl-pipeline-vertend-fixed-private.h"
 #endif
 
+#ifdef COGL_PIPELINE_PROGEND_FIXED
+#include "cogl-pipeline-progend-fixed-private.h"
+#endif
+#ifdef COGL_PIPELINE_PROGEND_GLSL
+#include "cogl-pipeline-progend-glsl-private.h"
+#endif
+
 COGL_OBJECT_DEFINE (Pipeline, pipeline);
 
 GQuark
@@ -128,6 +132,10 @@ _cogl_pipeline_init_default_pipeline (void)
   _cogl_pipeline_fragends[COGL_PIPELINE_FRAGEND_FIXED] =
     &_cogl_pipeline_fixed_fragend;
 #endif
+#ifdef COGL_PIPELINE_PROGEND_FIXED
+  _cogl_pipeline_progends[COGL_PIPELINE_PROGEND_FIXED] =
+    &_cogl_pipeline_fixed_progend;
+#endif
 #ifdef COGL_PIPELINE_PROGEND_GLSL
   _cogl_pipeline_progends[COGL_PIPELINE_PROGEND_GLSL] =
     &_cogl_pipeline_glsl_progend;



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]