diff --git a/cogl/cogl-attribute.c b/cogl/cogl-attribute.c
index 5cb898687..5872aa232 100644
--- a/cogl/cogl-attribute.c
+++ b/cogl/cogl-attribute.c
@@ -480,7 +480,7 @@ enable_gl_state (CoglDrawFlags flags,
                  CoglAttribute **attributes,
                  ValidateLayerState *state)
 {
-  CoglFramebuffer *framebuffer = _cogl_get_framebuffer ();
+  CoglFramebuffer *framebuffer = _cogl_get_draw_buffer ();
   int i;
 #ifdef MAY_HAVE_PROGRAMABLE_GL
   GLuint generic_index = 0;
@@ -1060,7 +1060,7 @@ flush_state (CoglDrawFlags flags,
 {
   if (!(flags & COGL_DRAW_SKIP_JOURNAL_FLUSH))
     {
-      CoglFramebuffer *framebuffer = _cogl_get_framebuffer ();
+      CoglFramebuffer *framebuffer = _cogl_get_draw_buffer ();
       _cogl_journal_flush (framebuffer->journal, framebuffer);
     }
 
@@ -1080,7 +1080,9 @@ flush_state (CoglDrawFlags flags,
    * stack can cause some drawing which would change the array
    * pointers. */
   if (!(flags & COGL_DRAW_SKIP_FRAMEBUFFER_FLUSH))
-    _cogl_framebuffer_flush_state (_cogl_get_framebuffer (), 0);
+    _cogl_framebuffer_flush_state (_cogl_get_draw_buffer (),
+                                   _cogl_get_read_buffer (),
+                                   0);
 }
 
 /* This can be called directly by the CoglJournal to draw attributes
diff --git a/cogl/cogl-clip-stack.c b/cogl/cogl-clip-stack.c
index ef0313be3..c8685d98b 100644
--- a/cogl/cogl-clip-stack.c
+++ b/cogl/cogl-clip-stack.c
@@ -77,7 +77,7 @@ set_clip_plane (GLint plane_num,
   GLdouble plane[4];
 #endif
   GLfloat angle;
-  CoglFramebuffer *framebuffer = _cogl_get_framebuffer ();
+  CoglFramebuffer *framebuffer = _cogl_get_draw_buffer ();
   CoglMatrixStack *modelview_stack =
     _cogl_framebuffer_get_modelview_stack (framebuffer);
   CoglMatrixStack *projection_stack =
@@ -131,7 +131,7 @@ set_clip_planes (float x_1,
 		 float x_2,
 		 float y_2)
 {
-  CoglFramebuffer *framebuffer = _cogl_get_framebuffer ();
+  CoglFramebuffer *framebuffer = _cogl_get_draw_buffer ();
   CoglMatrixStack *modelview_stack =
     _cogl_framebuffer_get_modelview_stack (framebuffer);
   CoglMatrix modelview_matrix;
@@ -192,7 +192,7 @@ add_stencil_clip_rectangle (float x_1,
                             float y_2,
                             gboolean first)
 {
-  CoglFramebuffer *framebuffer = _cogl_get_framebuffer ();
+  CoglFramebuffer *framebuffer = _cogl_get_draw_buffer ();
   CoglMatrixStack *modelview_stack =
     _cogl_framebuffer_get_modelview_stack (framebuffer);
   CoglMatrixStack *projection_stack =
@@ -586,7 +586,7 @@ _cogl_clip_stack_flush (CoglClipStack *stack)
   ctx->current_clip_stack = _cogl_clip_stack_ref (stack);
 
   modelview_stack =
-    _cogl_framebuffer_get_modelview_stack (_cogl_get_framebuffer ());
+    _cogl_framebuffer_get_modelview_stack (_cogl_get_draw_buffer ());
 
   has_clip_planes = cogl_features_available (COGL_FEATURE_FOUR_CLIP_PLANES);
 
@@ -617,7 +617,7 @@ _cogl_clip_stack_flush (CoglClipStack *stack)
     scissor_x0 = scissor_y0 = scissor_x1 = scissor_y1 = scissor_y_start = 0;
   else
     {
-      CoglFramebuffer *framebuffer = _cogl_get_framebuffer ();
+      CoglFramebuffer *framebuffer = _cogl_get_draw_buffer ();
 
       /* We store the entry coordinates in Cogl coordinate space
        * but OpenGL requires the window origin to be the bottom
diff --git a/cogl/cogl-clip-state.c b/cogl/cogl-clip-state.c
index 4e6adca8b..a4a3b1ab2 100644
--- a/cogl/cogl-clip-state.c
+++ b/cogl/cogl-clip-state.c
@@ -51,7 +51,7 @@ cogl_clip_push_window_rectangle (int x_offset,
 
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
-  framebuffer = _cogl_get_framebuffer ();
+  framebuffer = _cogl_get_draw_buffer ();
   clip_state = _cogl_framebuffer_get_clip_state (framebuffer);
 
   clip_state->stacks->data =
@@ -82,7 +82,7 @@ cogl_clip_push_rectangle (float x_1,
 
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
-  framebuffer = _cogl_get_framebuffer ();
+  framebuffer = _cogl_get_draw_buffer ();
   clip_state = _cogl_framebuffer_get_clip_state (framebuffer);
 
   cogl_get_modelview_matrix (&modelview_matrix);
@@ -115,7 +115,7 @@ cogl_clip_push_from_path_preserve (void)
 
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
-  framebuffer = _cogl_get_framebuffer ();
+  framebuffer = _cogl_get_draw_buffer ();
   clip_state = _cogl_framebuffer_get_clip_state (framebuffer);
 
   cogl_get_modelview_matrix (&modelview_matrix);
@@ -151,7 +151,7 @@ cogl_clip_pop (void)
 
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
-  framebuffer = _cogl_get_framebuffer ();
+  framebuffer = _cogl_get_draw_buffer ();
   clip_state = _cogl_framebuffer_get_clip_state (framebuffer);
 
   _cogl_clip_pop_real (clip_state);
@@ -169,7 +169,7 @@ _cogl_clip_state_flush (CoglClipState *clip_state)
 void
 cogl_clip_ensure (void)
 {
-  CoglFramebuffer *framebuffer = _cogl_get_framebuffer ();
+  CoglFramebuffer *framebuffer = _cogl_get_draw_buffer ();
   CoglClipState *clip_state;
 
   clip_state = _cogl_framebuffer_get_clip_state (framebuffer);
@@ -195,7 +195,7 @@ cogl_clip_stack_save (void)
 
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
-  framebuffer = _cogl_get_framebuffer ();
+  framebuffer = _cogl_get_draw_buffer ();
   clip_state = _cogl_framebuffer_get_clip_state (framebuffer);
 
   _cogl_clip_stack_save_real (clip_state);
@@ -225,7 +225,7 @@ cogl_clip_stack_restore (void)
 
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
-  framebuffer = _cogl_get_framebuffer ();
+  framebuffer = _cogl_get_draw_buffer ();
   clip_state = _cogl_framebuffer_get_clip_state (framebuffer);
 
   _cogl_clip_stack_restore_real (clip_state);
diff --git a/cogl/cogl-framebuffer-private.h b/cogl/cogl-framebuffer-private.h
index 6f69d0699..ce7a7d709 100644
--- a/cogl/cogl-framebuffer-private.h
+++ b/cogl/cogl-framebuffer-private.h
@@ -236,14 +236,18 @@ typedef enum _CoglFramebufferFlushFlags
 } CoglFramebufferFlushFlags;
 
 void
-_cogl_framebuffer_flush_state (CoglFramebuffer *framebuffer,
+_cogl_framebuffer_flush_state (CoglFramebuffer *draw_buffer,
+                               CoglFramebuffer *read_buffer,
                                CoglFramebufferFlushFlags flags);
 
 CoglHandle
 _cogl_onscreen_new (void);
 
 CoglFramebuffer *
-_cogl_get_framebuffer (void);
+_cogl_get_draw_buffer (void);
+
+CoglFramebuffer *
+_cogl_get_read_buffer (void);
 
 GSList *
 _cogl_create_framebuffer_stack (void);
@@ -269,5 +273,20 @@ _cogl_offscreen_new_to_texture_full (CoglHandle texhandle,
                                      CoglOffscreenFlags create_flags,
                                      unsigned int level);
 
+/*
+ * _cogl_push_framebuffers:
+ * @draw_buffer: A pointer to the buffer used for drawing
+ * @read_buffer: A pointer to the buffer used for reading back pixels
+ *
+ * Redirects drawing and reading to the specified framebuffers as in
+ * cogl_push_framebuffer() except that it allows the draw and read
+ * buffer to be different. The buffers are pushed as a pair so that
+ * they can later both be restored with a single call to
+ * cogl_pop_framebuffer().
+ */
+void
+_cogl_push_framebuffers (CoglFramebuffer *draw_buffer,
+                         CoglFramebuffer *read_buffer);
+
 #endif /* __COGL_FRAMEBUFFER_PRIVATE_H */
 
diff --git a/cogl/cogl-framebuffer.c b/cogl/cogl-framebuffer.c
index 23b851116..fd98bee3d 100644
--- a/cogl/cogl-framebuffer.c
+++ b/cogl/cogl-framebuffer.c
@@ -99,6 +99,12 @@
 #ifndef GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE
 #define GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE  0x8217
 #endif
+#ifndef GL_READ_FRAMEBUFFER
+#define GL_READ_FRAMEBUFFER               0x8CA8
+#endif
+#ifndef GL_DRAW_FRAMEBUFFER
+#define GL_DRAW_FRAMEBUFFER               0x8CA9
+#endif
 
 typedef enum {
   _TRY_DEPTH_STENCIL = 1L<<0,
@@ -106,6 +112,12 @@ typedef enum {
   _TRY_STENCIL       = 1L<<2
 } TryFBOFlags;
 
+typedef struct _CoglFramebufferStackEntry
+{
+  CoglFramebuffer *draw_buffer;
+  CoglFramebuffer *read_buffer;
+} CoglFramebufferStackEntry;
+
 static void _cogl_framebuffer_free (CoglFramebuffer *framebuffer);
 static void _cogl_onscreen_free (CoglOnscreen *onscreen);
 static void _cogl_offscreen_free (CoglOffscreen *offscreen);
@@ -364,7 +376,7 @@ _cogl_framebuffer_clear4f (CoglFramebuffer *framebuffer,
   /* NB: _cogl_framebuffer_flush_state may disrupt various state (such
    * as the pipeline state) when flushing the clip stack, so should
    * always be done first when preparing to draw. */
-  _cogl_framebuffer_flush_state (framebuffer, 0);
+  _cogl_framebuffer_flush_state (framebuffer, framebuffer, 0);
 
   _cogl_clear4f (buffers, red, green, blue, alpha);;
 
@@ -505,7 +517,7 @@ _cogl_framebuffer_set_viewport (CoglFramebuffer *framebuffer,
   framebuffer->viewport_width = width;
   framebuffer->viewport_height = height;
 
-  if (_cogl_get_framebuffer () == framebuffer)
+  if (_cogl_get_draw_buffer () == framebuffer)
     ctx->dirty_gl_viewport = TRUE;
 }
 
@@ -988,12 +1000,27 @@ _cogl_onscreen_clutter_backend_set_size (int width, int height)
   ctx->dirty_gl_viewport = 1;
 }
 
+static CoglFramebufferStackEntry *
+create_stack_entry (CoglFramebuffer *draw_buffer,
+                    CoglFramebuffer *read_buffer)
+{
+  CoglFramebufferStackEntry *entry = g_slice_new (CoglFramebufferStackEntry);
+
+  entry->draw_buffer = draw_buffer;
+  entry->read_buffer = read_buffer;
+
+  return entry;
+}
+
 GSList *
 _cogl_create_framebuffer_stack (void)
 {
+  CoglFramebufferStackEntry *entry;
   GSList *stack = NULL;
 
-  return g_slist_prepend (stack, COGL_INVALID_HANDLE);
+  entry = create_stack_entry (COGL_INVALID_HANDLE, COGL_INVALID_HANDLE);
+
+  return g_slist_prepend (stack, entry);
 }
 
 void
@@ -1003,11 +1030,19 @@ _cogl_free_framebuffer_stack (GSList *stack)
 
   for (l = stack; l != NULL; l = l->next)
     {
-      CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (l->data);
-      if (framebuffer->type == COGL_FRAMEBUFFER_TYPE_OFFSCREEN)
-        _cogl_offscreen_free (COGL_OFFSCREEN (framebuffer));
+      CoglFramebufferStackEntry *entry = l->data;
+
+      if (entry->draw_buffer->type == COGL_FRAMEBUFFER_TYPE_OFFSCREEN)
+        _cogl_offscreen_free (COGL_OFFSCREEN (entry->draw_buffer));
       else
-        _cogl_onscreen_free (COGL_ONSCREEN (framebuffer));
+        _cogl_onscreen_free (COGL_ONSCREEN (entry->draw_buffer));
+
+      if (entry->read_buffer->type == COGL_FRAMEBUFFER_TYPE_OFFSCREEN)
+        _cogl_offscreen_free (COGL_OFFSCREEN (entry->read_buffer));
+      else
+        _cogl_onscreen_free (COGL_ONSCREEN (entry->read_buffer));
+
+      g_slice_free (CoglFramebufferStackEntry, entry);
     }
   g_slist_free (stack);
 }
@@ -1016,42 +1051,55 @@ _cogl_free_framebuffer_stack (GSList *stack)
  * current framebuffer. This is used by cogl_pop_framebuffer while
  * the top of the stack is currently not up to date. */
 static void
-_cogl_set_framebuffer_real (CoglFramebuffer *framebuffer)
+_cogl_set_framebuffers_real (CoglFramebuffer *draw_buffer,
+                             CoglFramebuffer *read_buffer)
 {
-  CoglFramebuffer **entry;
+  CoglFramebufferStackEntry *entry;
 
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
-  entry = (CoglFramebuffer **)&ctx->framebuffer_stack->data;
+  entry = ctx->framebuffer_stack->data;
 
   ctx->dirty_bound_framebuffer = 1;
   ctx->dirty_gl_viewport = 1;
 
-  if (framebuffer)
-    cogl_object_ref (framebuffer);
-  if (*entry)
-    cogl_object_unref (*entry);
+  if (draw_buffer)
+    cogl_object_ref (draw_buffer);
+  if (entry->draw_buffer)
+    cogl_object_unref (entry->draw_buffer);
 
-  *entry = framebuffer;
+  if (read_buffer)
+    cogl_object_ref (read_buffer);
+  if (entry->read_buffer)
+    cogl_object_unref (entry->read_buffer);
+
+  entry->draw_buffer = draw_buffer;
+  entry->read_buffer = read_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 (framebuffer->modelview_stack);
-  _cogl_matrix_stack_dirty (framebuffer->projection_stack);
+  _cogl_matrix_stack_dirty (draw_buffer->modelview_stack);
+  _cogl_matrix_stack_dirty (draw_buffer->projection_stack);
   _cogl_clip_stack_dirty ();
 }
 
-void
-cogl_set_framebuffer (CoglFramebuffer *framebuffer)
+static void
+_cogl_set_framebuffers (CoglFramebuffer *draw_buffer,
+                        CoglFramebuffer *read_buffer)
 {
-  CoglFramebuffer *current;
+  CoglFramebuffer *current_draw_buffer;
+  CoglFramebuffer *current_read_buffer;
 
-  g_return_if_fail (_cogl_is_framebuffer (framebuffer));
+  g_return_if_fail (_cogl_is_framebuffer (draw_buffer));
+  g_return_if_fail (_cogl_is_framebuffer (read_buffer));
 
-  current = _cogl_get_framebuffer ();
-  if (current != framebuffer)
+  current_draw_buffer = _cogl_get_draw_buffer ();
+  current_read_buffer = _cogl_get_read_buffer ();
+
+  if (current_draw_buffer != draw_buffer ||
+      current_read_buffer != read_buffer)
     {
       /* XXX: eventually we want to remove this implicit journal flush
        * so we can log into the journal beyond framebuffer changes to
@@ -1059,12 +1107,20 @@ cogl_set_framebuffer (CoglFramebuffer *framebuffer)
        * mid-scene renders to textures. Current will be NULL when the
        * framebuffer stack is first created so we need to guard
        * against that here */
-      if (current)
-        _cogl_framebuffer_flush_journal (current);
-      _cogl_set_framebuffer_real (framebuffer);
+      if (current_draw_buffer)
+        _cogl_framebuffer_flush_journal (current_draw_buffer);
+      if (current_read_buffer)
+        _cogl_framebuffer_flush_journal (current_read_buffer);
+      _cogl_set_framebuffers_real (draw_buffer, read_buffer);
     }
 }
 
+void
+cogl_set_framebuffer (CoglFramebuffer *framebuffer)
+{
+  _cogl_set_framebuffers (framebuffer, framebuffer);
+}
+
 /* XXX: deprecated API */
 void
 cogl_set_draw_buffer (CoglBufferTarget target, CoglHandle handle)
@@ -1074,48 +1130,83 @@ cogl_set_draw_buffer (CoglBufferTarget target, CoglHandle handle)
   if (target == COGL_WINDOW_BUFFER)
     handle = ctx->window_buffer;
 
+  /* This is deprecated public API. The public API doesn't currently
+     really expose the concept of separate draw and read buffers so
+     for the time being this actually just sets both buffers */
   cogl_set_framebuffer (handle);
 }
 
 CoglFramebuffer *
-_cogl_get_framebuffer (void)
+_cogl_get_draw_buffer (void)
 {
+  CoglFramebufferStackEntry *entry;
+
   _COGL_GET_CONTEXT (ctx, NULL);
 
   g_assert (ctx->framebuffer_stack);
 
-  return ctx->framebuffer_stack->data;
+  entry = ctx->framebuffer_stack->data;
+
+  return entry->draw_buffer;
+}
+
+CoglFramebuffer *
+_cogl_get_read_buffer (void)
+{
+  CoglFramebufferStackEntry *entry;
+
+  _COGL_GET_CONTEXT (ctx, NULL);
+
+  g_assert (ctx->framebuffer_stack);
+
+  entry = ctx->framebuffer_stack->data;
+
+  return entry->read_buffer;
+}
+
+void
+_cogl_push_framebuffers (CoglFramebuffer *draw_buffer,
+                         CoglFramebuffer *read_buffer)
+{
+  CoglFramebuffer *old_draw_buffer, *old_read_buffer;
+
+  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+  g_return_if_fail (_cogl_is_framebuffer (draw_buffer));
+  g_return_if_fail (_cogl_is_framebuffer (read_buffer));
+  g_assert (ctx->framebuffer_stack);
+
+  /* Copy the top of the stack so that when we call cogl_set_framebuffer
+     it will still know what the old framebuffer was */
+  old_draw_buffer = cogl_object_ref (_cogl_get_draw_buffer ());
+  old_read_buffer = cogl_object_ref (_cogl_get_read_buffer ());
+  ctx->framebuffer_stack =
+    g_slist_prepend (ctx->framebuffer_stack,
+                     create_stack_entry (old_draw_buffer,
+                                         old_read_buffer));
+
+  _cogl_set_framebuffers (draw_buffer, read_buffer);
 }
 
 void
 cogl_push_framebuffer (CoglFramebuffer *buffer)
 {
-  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
-
-  g_return_if_fail (_cogl_is_framebuffer (buffer));
-  g_assert (ctx->framebuffer_stack);
-
-  /* Copy the top of the stack so that when we call cogl_set_framebuffer
-     it will still know what the old framebuffer was */
-  ctx->framebuffer_stack =
-    g_slist_prepend (ctx->framebuffer_stack,
-                     cogl_object_ref (_cogl_get_framebuffer ()));
-
-  cogl_set_framebuffer (buffer);
+  _cogl_push_framebuffers (buffer, buffer);
 }
 
 /* XXX: deprecated API */
 void
 cogl_push_draw_buffer (void)
 {
-  cogl_push_framebuffer (_cogl_get_framebuffer ());
+  cogl_push_framebuffer (_cogl_get_draw_buffer ());
 }
 
 void
 cogl_pop_framebuffer (void)
 {
-  CoglFramebuffer *to_pop;
-  CoglFramebuffer *to_restore;
+  CoglFramebufferStackEntry *to_pop;
+  CoglFramebufferStackEntry *to_restore;
+  gboolean changed = FALSE;
 
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
@@ -1125,16 +1216,23 @@ cogl_pop_framebuffer (void)
   to_pop = ctx->framebuffer_stack->data;
   to_restore = ctx->framebuffer_stack->next->data;
 
-  if (to_pop != to_restore)
+  if (to_pop->draw_buffer != to_restore->draw_buffer ||
+      to_pop->read_buffer != to_restore->read_buffer)
     {
       /* XXX: eventually we want to remove this implicit journal flush
        * so we can log into the journal beyond framebuffer changes to
        * support batching scenes that depend on the results of
        * mid-scene renders to textures. */
-      _cogl_framebuffer_flush_journal (to_pop);
+      _cogl_framebuffer_flush_journal (to_pop->draw_buffer);
+      _cogl_framebuffer_flush_journal (to_pop->read_buffer);
+
+      changed = TRUE;
     }
 
-  cogl_object_unref (to_pop);
+  cogl_object_unref (to_pop->draw_buffer);
+  cogl_object_unref (to_pop->read_buffer);
+  g_slice_free (CoglFramebufferStackEntry, to_pop);
+
   ctx->framebuffer_stack =
     g_slist_delete_link (ctx->framebuffer_stack,
                          ctx->framebuffer_stack);
@@ -1142,8 +1240,9 @@ cogl_pop_framebuffer (void)
   /* If the framebuffer has changed as a result of popping the top
    * then re-assert the current buffer so as to dirty state as
    * necessary. */
-  if (to_pop != to_restore)
-    _cogl_set_framebuffer_real (to_restore);
+  if (changed)
+    _cogl_set_framebuffers_real (to_restore->draw_buffer,
+                                 to_restore->read_buffer);
 }
 
 /* XXX: deprecated API */
@@ -1153,8 +1252,21 @@ cogl_pop_draw_buffer (void)
   cogl_pop_framebuffer ();
 }
 
+static void
+bind_gl_framebuffer (GLenum target, CoglFramebuffer *framebuffer)
+{
+  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+  if (framebuffer->type == COGL_FRAMEBUFFER_TYPE_OFFSCREEN)
+    GE (glBindFramebuffer (target,
+                           COGL_OFFSCREEN (framebuffer)->fbo_handle));
+  else
+    GE (glBindFramebuffer (target, 0));
+}
+
 void
-_cogl_framebuffer_flush_state (CoglFramebuffer *framebuffer,
+_cogl_framebuffer_flush_state (CoglFramebuffer *draw_buffer,
+                               CoglFramebuffer *read_buffer,
                                CoglFramebufferFlushFlags flags)
 {
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
@@ -1162,13 +1274,15 @@ _cogl_framebuffer_flush_state (CoglFramebuffer *framebuffer,
   if (cogl_features_available (COGL_FEATURE_OFFSCREEN) &&
       ctx->dirty_bound_framebuffer)
     {
-      if (framebuffer->type == COGL_FRAMEBUFFER_TYPE_OFFSCREEN)
-        {
-          GE (glBindFramebuffer (GL_FRAMEBUFFER,
-                                 COGL_OFFSCREEN (framebuffer)->fbo_handle));
-        }
+      if (!cogl_features_available (COGL_FEATURE_OFFSCREEN_BLIT) ||
+          draw_buffer == read_buffer)
+        bind_gl_framebuffer (GL_FRAMEBUFFER, draw_buffer);
       else
-        GE (glBindFramebuffer (GL_FRAMEBUFFER, 0));
+        {
+          bind_gl_framebuffer (GL_DRAW_FRAMEBUFFER, draw_buffer);
+          bind_gl_framebuffer (GL_READ_FRAMEBUFFER, read_buffer);
+        }
+
       ctx->dirty_bound_framebuffer = FALSE;
     }
 
@@ -1181,41 +1295,42 @@ _cogl_framebuffer_flush_state (CoglFramebuffer *framebuffer,
        * left, while Cogl defines them to be top left.
        * NB: We render upside down to offscreen framebuffers so we don't
        * need to convert the y offset in this case. */
-      if (cogl_is_offscreen (framebuffer))
-        gl_viewport_y = framebuffer->viewport_y;
+      if (cogl_is_offscreen (draw_buffer))
+        gl_viewport_y = draw_buffer->viewport_y;
       else
-        gl_viewport_y = framebuffer->height -
-          (framebuffer->viewport_y + framebuffer->viewport_height);
+        gl_viewport_y = draw_buffer->height -
+          (draw_buffer->viewport_y + draw_buffer->viewport_height);
 
       COGL_NOTE (OPENGL, "Calling glViewport(%d, %d, %d, %d)",
-                 framebuffer->viewport_x,
+                 draw_buffer->viewport_x,
                  gl_viewport_y,
-                 framebuffer->viewport_width,
-                 framebuffer->viewport_height);
+                 draw_buffer->viewport_width,
+                 draw_buffer->viewport_height);
 
-      GE (glViewport (framebuffer->viewport_x,
+      GE (glViewport (draw_buffer->viewport_x,
                       gl_viewport_y,
-                      framebuffer->viewport_width,
-                      framebuffer->viewport_height));
+                      draw_buffer->viewport_width,
+                      draw_buffer->viewport_height));
       ctx->dirty_gl_viewport = FALSE;
     }
 
   /* since we might have changed the framebuffer, we should initialize
    * the bits; this is a no-op if they have already been initialized
    */
-  _cogl_framebuffer_init_bits (framebuffer);
+  _cogl_framebuffer_init_bits (draw_buffer);
+  _cogl_framebuffer_init_bits (read_buffer);
 
   /* XXX: Flushing clip state may trash the modelview and projection
    * matrices so we must do it before flushing the matrices...
    */
   if (!(flags & COGL_FRAMEBUFFER_FLUSH_SKIP_CLIP_STATE))
-    _cogl_clip_state_flush (&framebuffer->clip_state);
+    _cogl_clip_state_flush (&draw_buffer->clip_state);
 
   if (!(flags & COGL_FRAMEBUFFER_FLUSH_SKIP_MODELVIEW))
-    _cogl_matrix_stack_flush_to_gl (framebuffer->modelview_stack,
+    _cogl_matrix_stack_flush_to_gl (draw_buffer->modelview_stack,
                                     COGL_MATRIX_MODELVIEW);
 
-  _cogl_matrix_stack_flush_to_gl (framebuffer->projection_stack,
+  _cogl_matrix_stack_flush_to_gl (draw_buffer->projection_stack,
                                   COGL_MATRIX_PROJECTION);
 }
 
diff --git a/cogl/cogl-journal.c b/cogl/cogl-journal.c
index 7ec25cdfc..cdfb3c7ef 100644
--- a/cogl/cogl-journal.c
+++ b/cogl/cogl-journal.c
@@ -1312,6 +1312,7 @@ _cogl_journal_flush (CoglJournal *journal,
   /* NB: the journal deals with flushing the modelview stack and clip
      state manually */
   _cogl_framebuffer_flush_state (framebuffer,
+                                 framebuffer,
                                  COGL_FRAMEBUFFER_FLUSH_SKIP_MODELVIEW |
                                  COGL_FRAMEBUFFER_FLUSH_SKIP_CLIP_STATE);
 
@@ -1518,7 +1519,7 @@ _cogl_journal_log_quad (CoglJournal  *journal,
 
   entry->pipeline = _cogl_pipeline_journal_ref (source);
 
-  clip_stack = _cogl_framebuffer_get_clip_stack (_cogl_get_framebuffer ());
+  clip_stack = _cogl_framebuffer_get_clip_stack (_cogl_get_draw_buffer ());
   entry->clip_stack = _cogl_clip_stack_ref (clip_stack);
 
   if (G_UNLIKELY (source != pipeline))
@@ -1528,7 +1529,7 @@ _cogl_journal_log_quad (CoglJournal  *journal,
 
   _cogl_pipeline_foreach_layer_internal (pipeline,
                                          add_framebuffer_deps_cb,
-                                         _cogl_get_framebuffer ());
+                                         _cogl_get_draw_buffer ());
 
   /* XXX: It doesn't feel very nice that in this case we just assume
    * that the journal is associated with the current framebuffer. I
@@ -1536,7 +1537,7 @@ _cogl_journal_log_quad (CoglJournal  *journal,
    * the reason we don't have that currently is that it would
    * introduce a circular reference. */
   if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_BATCHING)))
-    _cogl_framebuffer_flush_journal (_cogl_get_framebuffer ());
+    _cogl_framebuffer_flush_journal (_cogl_get_draw_buffer ());
 
   COGL_TIMER_STOP (_cogl_uprof_context, log_timer);
 }
@@ -1587,7 +1588,7 @@ entry_to_screen_polygon (const CoglJournalEntry *entry,
                                 4 /* n_points */);
 
   projection_stack =
-    _cogl_framebuffer_get_projection_stack (_cogl_get_framebuffer ());
+    _cogl_framebuffer_get_projection_stack (_cogl_get_draw_buffer ());
   _cogl_matrix_stack_get (projection_stack, &projection);
 
   cogl_matrix_project_points (&projection,
@@ -1599,7 +1600,7 @@ entry_to_screen_polygon (const CoglJournalEntry *entry,
                               poly, /* points_out */
                               4 /* n_points */);
 
-  _cogl_framebuffer_get_viewport4fv (_cogl_get_framebuffer (),
+  _cogl_framebuffer_get_viewport4fv (_cogl_get_draw_buffer (),
                                      viewport);
 
 /* Scale from OpenGL normalized device coordinates (ranging from -1 to 1)
diff --git a/cogl/cogl-matrix-stack.c b/cogl/cogl-matrix-stack.c
index d0d34ef97..fd6541248 100644
--- a/cogl/cogl-matrix-stack.c
+++ b/cogl/cogl-matrix-stack.c
@@ -434,7 +434,7 @@ _cogl_matrix_stack_prepare_for_flush (CoglMatrixStack *stack,
    * always render upside down to offscreen buffers.
    */
   if (mode == COGL_MATRIX_PROJECTION &&
-      cogl_is_offscreen (_cogl_get_framebuffer ()))
+      cogl_is_offscreen (_cogl_get_draw_buffer ()))
     {
       CoglMatrix flipped_projection;
       CoglMatrix *projection =
diff --git a/cogl/cogl-primitives.c b/cogl/cogl-primitives.c
index 8628ee434..6719fe6d8 100644
--- a/cogl/cogl-primitives.c
+++ b/cogl/cogl-primitives.c
@@ -78,7 +78,7 @@ log_quad_sub_textures_cb (CoglHandle texture_handle,
                           void *user_data)
 {
   TextureSlicedQuadState *state = user_data;
-  CoglFramebuffer *framebuffer = _cogl_get_framebuffer ();
+  CoglFramebuffer *framebuffer = _cogl_get_draw_buffer ();
   CoglHandle texture_override;
   float quad_coords[4];
 
@@ -542,7 +542,7 @@ _cogl_multitexture_quad_single_primitive (const float  *position,
   if (state.override_pipeline)
     pipeline = state.override_pipeline;
 
-  framebuffer = _cogl_get_framebuffer ();
+  framebuffer = _cogl_get_draw_buffer ();
   _cogl_journal_log_quad (framebuffer->journal,
                           position,
                           pipeline,
diff --git a/cogl/cogl-texture.c b/cogl/cogl-texture.c
index 41aa68b95..2b4c09762 100644
--- a/cogl/cogl-texture.c
+++ b/cogl/cogl-texture.c
@@ -1048,7 +1048,7 @@ _cogl_texture_draw_and_read (CoglHandle   handle,
 
   bpp = _cogl_get_format_bpp (COGL_PIXEL_FORMAT_RGBA_8888);
 
-  framebuffer = _cogl_get_framebuffer ();
+  framebuffer = _cogl_get_draw_buffer ();
   /* Viewport needs to have some size and be inside the window for this */
   _cogl_framebuffer_get_viewport4fv (framebuffer, viewport);
   if (viewport[0] <  0 || viewport[1] <  0 ||
diff --git a/cogl/cogl.c b/cogl/cogl.c
index 333ef9a64..515e4dd3c 100644
--- a/cogl/cogl.c
+++ b/cogl/cogl.c
@@ -151,7 +151,7 @@ cogl_check_extension (const char *name, const char *ext)
 void
 cogl_clear (const CoglColor *color, unsigned long buffers)
 {
-  _cogl_framebuffer_clear (_cogl_get_framebuffer (), buffers, color);
+  _cogl_framebuffer_clear (_cogl_get_draw_buffer (), buffers, color);
 }
 
 static gboolean
@@ -282,7 +282,7 @@ cogl_set_backface_culling_enabled (gboolean setting)
     return;
 
   /* Currently the journal can't track changes to backface culling state... */
-  _cogl_framebuffer_flush_journal (_cogl_get_framebuffer ());
+  _cogl_framebuffer_flush_journal (_cogl_get_draw_buffer ());
 
   ctx->enable_backface_culling = setting;
 }
@@ -311,7 +311,7 @@ _cogl_flush_face_winding (void)
    * all offscreen rendering is done upside down resulting in reversed winding
    * for all triangles.
    */
-  if (cogl_is_offscreen (_cogl_get_framebuffer ()))
+  if (cogl_is_offscreen (_cogl_get_draw_buffer ()))
     winding = COGL_FRONT_WINDING_CLOCKWISE;
   else
     winding = COGL_FRONT_WINDING_COUNTER_CLOCKWISE;
@@ -360,7 +360,7 @@ cogl_set_viewport (int x,
 
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
-  framebuffer = _cogl_get_framebuffer ();
+  framebuffer = _cogl_get_draw_buffer ();
 
   _cogl_framebuffer_set_viewport (framebuffer,
                                   x,
@@ -415,7 +415,7 @@ cogl_get_viewport (float v[4])
 
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
-  framebuffer = _cogl_get_framebuffer ();
+  framebuffer = _cogl_get_draw_buffer ();
   _cogl_framebuffer_get_viewport4fv (framebuffer, viewport);
 
   for (i = 0; i < 4; i++)
@@ -430,7 +430,7 @@ cogl_get_bitmasks (int *red,
 {
   CoglFramebuffer *framebuffer;
 
-  framebuffer = _cogl_get_framebuffer ();
+  framebuffer = _cogl_get_draw_buffer ();
 
   if (red)
     *red = _cogl_framebuffer_get_red_bits (framebuffer);
@@ -497,7 +497,7 @@ _cogl_read_pixels_with_rowstride (int x,
                                   guint8 *pixels,
                                   int rowstride)
 {
-  CoglFramebuffer *framebuffer = _cogl_get_framebuffer ();
+  CoglFramebuffer *framebuffer = _cogl_get_read_buffer ();
   int              framebuffer_height;
   int              bpp;
   CoglBitmap      *bmp;
@@ -537,7 +537,9 @@ _cogl_read_pixels_with_rowstride (int x,
    */
   cogl_flush ();
 
-  _cogl_framebuffer_flush_state (framebuffer, 0);
+  _cogl_framebuffer_flush_state (_cogl_get_draw_buffer (),
+                                 framebuffer,
+                                 0);
 
   framebuffer_height = _cogl_framebuffer_get_height (framebuffer);
 
@@ -706,7 +708,9 @@ cogl_begin_gl (void)
    * NB: _cogl_framebuffer_flush_state may disrupt various state (such
    * as the pipeline state) when flushing the clip stack, so should
    * always be done first when preparing to draw. */
-  _cogl_framebuffer_flush_state (_cogl_get_framebuffer (), 0);
+  _cogl_framebuffer_flush_state (_cogl_get_draw_buffer (),
+                                 _cogl_get_read_buffer (),
+                                 0);
 
   /* Setup the state for the current pipeline */
 
@@ -762,7 +766,7 @@ void
 cogl_push_matrix (void)
 {
   CoglMatrixStack *modelview_stack =
-    _cogl_framebuffer_get_modelview_stack (_cogl_get_framebuffer ());
+    _cogl_framebuffer_get_modelview_stack (_cogl_get_draw_buffer ());
   _cogl_matrix_stack_push (modelview_stack);
 }
 
@@ -770,7 +774,7 @@ void
 cogl_pop_matrix (void)
 {
   CoglMatrixStack *modelview_stack =
-    _cogl_framebuffer_get_modelview_stack (_cogl_get_framebuffer ());
+    _cogl_framebuffer_get_modelview_stack (_cogl_get_draw_buffer ());
   _cogl_matrix_stack_pop (modelview_stack);
 }
 
@@ -778,7 +782,7 @@ void
 cogl_scale (float x, float y, float z)
 {
   CoglMatrixStack *modelview_stack =
-    _cogl_framebuffer_get_modelview_stack (_cogl_get_framebuffer ());
+    _cogl_framebuffer_get_modelview_stack (_cogl_get_draw_buffer ());
   _cogl_matrix_stack_scale (modelview_stack, x, y, z);
 }
 
@@ -786,7 +790,7 @@ void
 cogl_translate (float x, float y, float z)
 {
   CoglMatrixStack *modelview_stack =
-    _cogl_framebuffer_get_modelview_stack (_cogl_get_framebuffer ());
+    _cogl_framebuffer_get_modelview_stack (_cogl_get_draw_buffer ());
   _cogl_matrix_stack_translate (modelview_stack, x, y, z);
 }
 
@@ -794,7 +798,7 @@ void
 cogl_rotate (float angle, float x, float y, float z)
 {
   CoglMatrixStack *modelview_stack =
-    _cogl_framebuffer_get_modelview_stack (_cogl_get_framebuffer ());
+    _cogl_framebuffer_get_modelview_stack (_cogl_get_draw_buffer ());
   _cogl_matrix_stack_rotate (modelview_stack, angle, x, y, z);
 }
 
@@ -802,7 +806,7 @@ void
 cogl_transform (const CoglMatrix *matrix)
 {
   CoglMatrixStack *modelview_stack =
-    _cogl_framebuffer_get_modelview_stack (_cogl_get_framebuffer ());
+    _cogl_framebuffer_get_modelview_stack (_cogl_get_draw_buffer ());
   _cogl_matrix_stack_multiply (modelview_stack, matrix);
 }
 
@@ -831,7 +835,7 @@ cogl_frustum (float        left,
 	      float        z_far)
 {
   CoglMatrixStack *projection_stack =
-    _cogl_framebuffer_get_projection_stack (_cogl_get_framebuffer ());
+    _cogl_framebuffer_get_projection_stack (_cogl_get_draw_buffer ());
 
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
@@ -856,7 +860,7 @@ cogl_ortho (float left,
 {
   CoglMatrix ortho;
   CoglMatrixStack *projection_stack =
-    _cogl_framebuffer_get_projection_stack (_cogl_get_framebuffer ());
+    _cogl_framebuffer_get_projection_stack (_cogl_get_draw_buffer ());
 
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
@@ -869,7 +873,7 @@ void
 cogl_get_modelview_matrix (CoglMatrix *matrix)
 {
   CoglMatrixStack *modelview_stack =
-    _cogl_framebuffer_get_modelview_stack (_cogl_get_framebuffer ());
+    _cogl_framebuffer_get_modelview_stack (_cogl_get_draw_buffer ());
   _cogl_matrix_stack_get (modelview_stack, matrix);
   _COGL_MATRIX_DEBUG_PRINT (matrix);
 }
@@ -878,7 +882,7 @@ void
 cogl_set_modelview_matrix (CoglMatrix *matrix)
 {
   CoglMatrixStack *modelview_stack =
-    _cogl_framebuffer_get_modelview_stack (_cogl_get_framebuffer ());
+    _cogl_framebuffer_get_modelview_stack (_cogl_get_draw_buffer ());
   _cogl_matrix_stack_set (modelview_stack, matrix);
   _COGL_MATRIX_DEBUG_PRINT (matrix);
 }
@@ -887,7 +891,7 @@ void
 cogl_get_projection_matrix (CoglMatrix *matrix)
 {
   CoglMatrixStack *projection_stack =
-    _cogl_framebuffer_get_projection_stack (_cogl_get_framebuffer ());
+    _cogl_framebuffer_get_projection_stack (_cogl_get_draw_buffer ());
   _cogl_matrix_stack_get (projection_stack, matrix);
   _COGL_MATRIX_DEBUG_PRINT (matrix);
 }
@@ -896,7 +900,7 @@ void
 cogl_set_projection_matrix (CoglMatrix *matrix)
 {
   CoglMatrixStack *projection_stack =
-    _cogl_framebuffer_get_projection_stack (_cogl_get_framebuffer ());
+    _cogl_framebuffer_get_projection_stack (_cogl_get_draw_buffer ());
   _cogl_matrix_stack_set (projection_stack, matrix);
 
   /* FIXME: Update the inverse projection matrix!! Presumably use
@@ -909,7 +913,7 @@ _cogl_get_clip_state (void)
 {
   CoglFramebuffer *framebuffer;
 
-  framebuffer = _cogl_get_framebuffer ();
+  framebuffer = _cogl_get_draw_buffer ();
   return _cogl_framebuffer_get_clip_state (framebuffer);
 }
 
@@ -1119,5 +1123,5 @@ _cogl_swap_buffers_notify (void)
 {
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
-  _cogl_framebuffer_swap_notify (_cogl_get_framebuffer ());
+  _cogl_framebuffer_swap_notify (_cogl_get_draw_buffer ());
 }
diff --git a/cogl/cogl2-path.c b/cogl/cogl2-path.c
index d812290d4..f1f0e4532 100644
--- a/cogl/cogl2-path.c
+++ b/cogl/cogl2-path.c
@@ -355,7 +355,7 @@ _cogl_add_path_to_stencil_buffer (CoglPath *path,
                                   gboolean need_clear)
 {
   CoglPathData *data = path->data;
-  CoglFramebuffer *framebuffer = _cogl_get_framebuffer ();
+  CoglFramebuffer *framebuffer = _cogl_get_draw_buffer ();
   CoglMatrixStack *modelview_stack =
     _cogl_framebuffer_get_modelview_stack (framebuffer);
   CoglMatrixStack *projection_stack =
@@ -466,14 +466,16 @@ cogl2_path_fill (CoglPath *path)
   if (path->data->path_nodes->len == 0)
     return;
 
-  framebuffer = _cogl_get_framebuffer ();
+  framebuffer = _cogl_get_draw_buffer ();
 
   _cogl_framebuffer_flush_journal (framebuffer);
 
   /* NB: _cogl_framebuffer_flush_state may disrupt various state (such
    * as the pipeline state) when flushing the clip stack, so should
    * always be done first when preparing to draw. */
-  _cogl_framebuffer_flush_state (framebuffer, 0);
+  _cogl_framebuffer_flush_state (framebuffer,
+                                 _cogl_get_read_buffer (),
+                                 0);
 
   _cogl_path_fill_nodes (path);
 }