[Cogl] [PATCH v3] Adds initial GLES2 integration support

Robert Bragg robert at sixbynine.org
Wed May 16 05:54:42 PDT 2012


From: Robert Bragg <robert at linux.intel.com>

Ok, here's another update with the following changes:

* Uses g_queue_init() instead of g_queue_new()
* Uses an embedded list for the gles2-offscreens
* Fixes how we free gles2-offscreens to ensure they are removed from
  the foreign_offscreens list.
* Fixes building without EGL
* Wraps glCopyTex{Sub}Image2D
* Documents that textures shared from gles2 to Cogl or vica versa
  should be considered read only for now.

kind regards,
- Robert

-- >8 --

This makes it possible to integrate existing GLES2 code with
applications using Cogl as the rendering api.

Currently all GLES2 usage is handled with separate GLES2 contexts to
ensure that GLES2 api usage doesn't interfere with Cogl's own use of
OpenGL[ES]. The api has been designed though so we can provide tighter
integration later.

The api would allow us to support GLES2 virtualized on top of an
OpenGL/GLX driver as well as GLES2 virtualized on the core rendering api
of Cogl itself. Virtualizing the GLES2 support on Cogl will allow us to
take advantage of Cogl debugging facilities as well as let us optimize
the cost of allocating multiple GLES2 contexts and switching between
them which can both be very expensive with many drivers.

As as a side effect of this patch Cogl can also now be used as a
portable window system binding API for GLES2 as an alternative to EGL.

Parts of this patch are based on work done by Tomeu Vizoso
<tomeu.vizoso at collabora.com> who did the first iteration of adding GLES2
API support to Cogl so that WebGL support could be added to
webkit-clutter.

This patch adds a very minimal cogl-gles2-context example that shows how
to create a gles2 context, clear the screen to a random color and also
draw a triangle with the cogl api.
---
 cogl/Makefile.am                      |   14 +
 cogl/cogl-context-private.h           |    7 +
 cogl/cogl-context.c                   |    6 +-
 cogl/cogl-context.h                   |    4 +
 cogl/cogl-framebuffer-private.h       |   33 ++-
 cogl/cogl-framebuffer.c               |  380 +++++++++++++++---------
 cogl/cogl-gles2-context-private.h     |   68 ++++
 cogl/cogl-gles2-context.c             |  542 +++++++++++++++++++++++++++++++++
 cogl/cogl-gles2-types.h               |  474 ++++++++++++++++++++++++++++
 cogl/cogl-gles2.h                     |  368 ++++++++++++++++++++++
 cogl/cogl.h                           |    2 +-
 cogl/winsys/cogl-winsys-egl-android.c |    8 +-
 cogl/winsys/cogl-winsys-egl-gdl.c     |    8 +-
 cogl/winsys/cogl-winsys-egl-kms.c     |    8 +-
 cogl/winsys/cogl-winsys-egl-null.c    |    8 +-
 cogl/winsys/cogl-winsys-egl-private.h |   13 +-
 cogl/winsys/cogl-winsys-egl-wayland.c |   10 +-
 cogl/winsys/cogl-winsys-egl-x11.c     |    8 +-
 cogl/winsys/cogl-winsys-egl.c         |  225 +++++++++++++--
 cogl/winsys/cogl-winsys-private.h     |   18 ++
 examples/Makefile.am                  |    5 +-
 examples/cogl-gles2-context.c         |  135 ++++++++
 examples/cogl-info.c                  |    6 +
 23 files changed, 2159 insertions(+), 191 deletions(-)
 create mode 100644 cogl/cogl-gles2-context-private.h
 create mode 100644 cogl/cogl-gles2-context.c
 create mode 100644 cogl/cogl-gles2-types.h
 create mode 100644 cogl/cogl-gles2.h
 create mode 100644 examples/cogl-gles2-context.c

diff --git a/cogl/Makefile.am b/cogl/Makefile.am
index c363f9b..3c5500c 100644
--- a/cogl/Makefile.am
+++ b/cogl/Makefile.am
@@ -74,6 +74,8 @@ cogl_experimental_h = \
 	$(srcdir)/cogl-pipeline-layer-state.h 	\
 	$(srcdir)/cogl-snippet.h		\
 	$(srcdir)/cogl-path.h 			\
+	$(srcdir)/cogl-gles2.h			\
+	$(srcdir)/cogl-gles2-types.h		\
 	$(srcdir)/cogl-index-buffer.h 		\
 	$(srcdir)/cogl-attribute-buffer.h 	\
 	$(srcdir)/cogl-indices.h 		\
@@ -99,6 +101,14 @@ cogl_experimental_h = \
 	$(srcdir)/cogl-version.h		\
 	$(NULL)
 
+cogl_gl_prototypes_h = \
+	$(srcdir)/gl-prototypes/cogl-gles2-functions.h		\
+	$(srcdir)/gl-prototypes/cogl-core-functions.h		\
+	$(srcdir)/gl-prototypes/cogl-in-gles-core-functions.h	\
+	$(srcdir)/gl-prototypes/cogl-in-gles2-core-functions.h	\
+	$(srcdir)/gl-prototypes/cogl-glsl-functions.h		\
+	$(NULL)
+
 # driver sources
 cogl_driver_sources =
 
@@ -333,6 +343,7 @@ cogl_sources_c = \
 	$(srcdir)/cogl-memory-stack.c			\
 	$(srcdir)/cogl-magazine-private.h		\
 	$(srcdir)/cogl-magazine.c			\
+	$(srcdir)/cogl-gles2-context.c			\
 	$(NULL)
 
 if USE_GLIB
@@ -477,6 +488,9 @@ if USE_GLIB
 nodist_coglinclude_HEADERS += cogl-enum-types.h
 endif
 
+cogl_proto_includedir = $(includedir)/cogl2/cogl/gl-prototypes
+cogl_proto_include_HEADERS = $(cogl_gl_prototypes_h)
+
 dist-hook: ../build/win32/vs9/cogl.vcproj ../build/win32/vs10/cogl.vcxproj ../build/win32/vs10/cogl.vcxproj.filters ../build/win32/gen-enums.bat
 
 # I know those filters below don't look nice, but this is to ensure the right files are in the Project files only *once*
diff --git a/cogl/cogl-context-private.h b/cogl/cogl-context-private.h
index a1776dc..48c170e 100644
--- a/cogl/cogl-context-private.h
+++ b/cogl/cogl-context-private.h
@@ -50,6 +50,7 @@
 #include "cogl-sampler-cache-private.h"
 #include "cogl-gpu-info-private.h"
 #include "cogl-gl-header.h"
+#include "cogl-framebuffer-private.h"
 
 typedef struct
 {
@@ -172,6 +173,12 @@ struct _CoglContext
   CoglFramebuffer  *current_draw_buffer;
   CoglFramebuffer  *current_read_buffer;
 
+  gboolean have_last_offscreen_allocate_flags;
+  CoglOffscreenAllocateFlags last_offscreen_allocate_flags;
+
+  CoglGLES2Context *current_gles2_context;
+  GQueue gles2_context_stack;
+
   /* Primitives */
   CoglPipeline     *stencil_pipeline;
 
diff --git a/cogl/cogl-context.c b/cogl/cogl-context.c
index 73871d4..a971bc9 100644
--- a/cogl/cogl-context.c
+++ b/cogl/cogl-context.c
@@ -159,7 +159,7 @@ cogl_context_new (CoglDisplay *display,
 #endif
 
   /* Allocate context memory */
-  context = g_malloc (sizeof (CoglContext));
+  context = g_malloc0 (sizeof (CoglContext));
 
   /* Convert the context into an object immediately in case any of the
      code below wants to verify that the context pointer is a valid
@@ -303,6 +303,8 @@ cogl_context_new (CoglDisplay *display,
   context->current_draw_buffer_state_flushed = 0;
   context->current_draw_buffer_changes = COGL_FRAMEBUFFER_STATE_ALL;
 
+  g_queue_init (&context->gles2_context_stack);
+
   context->journal_flush_attributes_array =
     g_array_new (TRUE, FALSE, sizeof (CoglAttribute *));
   context->journal_clip_bounds = NULL;
@@ -466,6 +468,8 @@ _cogl_context_free (CoglContext *context)
   if (context->blit_texture_pipeline)
     cogl_object_unref (context->blit_texture_pipeline);
 
+  g_warn_if_fail (context->gles2_context_stack.length == 0);
+
   if (context->journal_flush_attributes_array)
     g_array_free (context->journal_flush_attributes_array, TRUE);
   if (context->journal_clip_bounds)
diff --git a/cogl/cogl-context.h b/cogl/cogl-context.h
index 21ea636..757b1a8 100644
--- a/cogl/cogl-context.h
+++ b/cogl/cogl-context.h
@@ -203,6 +203,9 @@ cogl_is_context (void *object);
  * @COGL_FEATURE_ID_SWAP_BUFFERS_EVENT:
  *     Available if the window system supports reporting an event
  *     for swap buffer completions.
+ * @COGL_FEATURE_ID_GLES2_CONTEXT: Whether creating new GLES2 contexts is
+ *    suported.
+ *
  *
  * All the capabilities that can vary between different GPUs supported
  * by Cogl. Applications that depend on any of these features should explicitly
@@ -230,6 +233,7 @@ typedef enum _CoglFeatureID
   COGL_FEATURE_ID_MAP_BUFFER_FOR_WRITE,
   COGL_FEATURE_ID_MIRRORED_REPEAT,
   COGL_FEATURE_ID_SWAP_BUFFERS_EVENT,
+  COGL_FEATURE_ID_GLES2_CONTEXT,
 
   /*< private > */
   _COGL_N_FEATURE_IDS
diff --git a/cogl/cogl-framebuffer-private.h b/cogl/cogl-framebuffer-private.h
index 4c95f73..9922a1a 100644
--- a/cogl/cogl-framebuffer-private.h
+++ b/cogl/cogl-framebuffer-private.h
@@ -166,17 +166,33 @@ struct _CoglFramebuffer
   CoglBool            clear_clip_dirty;
 };
 
+typedef enum {
+  COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH_STENCIL    = 1L<<0,
+  COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH24_STENCIL8 = 1L<<1,
+  COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH            = 1L<<2,
+  COGL_OFFSCREEN_ALLOCATE_FLAG_STENCIL          = 1L<<3
+} CoglOffscreenAllocateFlags;
+
+typedef struct _CoglGLFramebuffer
+{
+  GLuint fbo_handle;
+  GList *renderbuffers;
+  int samples_per_pixel;
+} CoglGLFramebuffer;
+
 struct _CoglOffscreen
 {
   CoglFramebuffer  _parent;
-  GLuint          fbo_handle;
-  GSList          *renderbuffers;
+
+  CoglGLFramebuffer gl_framebuffer;
 
   CoglTexture    *texture;
   int             texture_level;
   int             texture_level_width;
   int             texture_level_height;
 
+  CoglOffscreenAllocateFlags allocation_flags;
+
   /* FIXME: _cogl_offscreen_new_to_texture_full should be made to use
    * fb->config to configure if we want a depth or stencil buffer so
    * we can get rid of these flags */
@@ -400,4 +416,17 @@ _cogl_framebuffer_draw_indexed_attributes (CoglFramebuffer *framebuffer,
                                            int n_attributes,
                                            CoglDrawFlags flags);
 
+gboolean
+_cogl_framebuffer_try_creating_gl_fbo (CoglContext *ctx,
+                                       CoglTexture *texture,
+                                       int texture_level,
+                                       int texture_level_width,
+                                       int texture_level_height,
+                                       CoglFramebufferConfig *config,
+                                       CoglOffscreenAllocateFlags flags,
+                                       CoglGLFramebuffer *gl_framebuffer);
+
+void
+_cogl_gl_framebuffer_bind (CoglFramebuffer *framebuffer, GLenum target);
+
 #endif /* __COGL_FRAMEBUFFER_PRIVATE_H */
diff --git a/cogl/cogl-framebuffer.c b/cogl/cogl-framebuffer.c
index 45af11b..63d8c4b 100644
--- a/cogl/cogl-framebuffer.c
+++ b/cogl/cogl-framebuffer.c
@@ -111,13 +111,6 @@
 #endif
 
 
-typedef enum {
-  _TRY_DEPTH_STENCIL    = 1L<<0,
-  _TRY_DEPTH24_STENCIL8 = 1L<<1,
-  _TRY_DEPTH            = 1L<<2,
-  _TRY_STENCIL          = 1L<<3
-} TryFBOFlags;
-
 typedef struct _CoglFramebufferStackEntry
 {
   CoglFramebuffer *draw_buffer;
@@ -793,23 +786,31 @@ cogl_offscreen_new_to_texture (CoglTexture *texture)
 }
 
 static void
+delete_renderbuffers (CoglContext *ctx, GList *renderbuffers)
+{
+  GList *l;
+
+  for (l = renderbuffers; l; l = l->next)
+    {
+      GLuint renderbuffer = GPOINTER_TO_UINT (l->data);
+      GE (ctx, glDeleteRenderbuffers (1, &renderbuffer));
+    }
+
+  g_list_free (renderbuffers);
+}
+
+static void
 _cogl_offscreen_free (CoglOffscreen *offscreen)
 {
   CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (offscreen);
   CoglContext *ctx = framebuffer->context;
-  GSList *l;
 
   /* Chain up to parent */
   _cogl_framebuffer_free (framebuffer);
 
-  for (l = offscreen->renderbuffers; l; l = l->next)
-    {
-      GLuint renderbuffer = GPOINTER_TO_UINT (l->data);
-      GE (ctx, glDeleteRenderbuffers (1, &renderbuffer));
-    }
-  g_slist_free (offscreen->renderbuffers);
+  delete_renderbuffers (ctx, offscreen->gl_framebuffer.renderbuffers);
 
-  GE (ctx, glDeleteFramebuffers (1, &offscreen->fbo_handle));
+  GE (ctx, glDeleteFramebuffers (1, &offscreen->gl_framebuffer.fbo_handle));
 
   if (offscreen->texture != NULL)
     cogl_object_unref (offscreen->texture);
@@ -817,72 +818,20 @@ _cogl_offscreen_free (CoglOffscreen *offscreen)
   g_free (offscreen);
 }
 
-static CoglBool
-try_creating_fbo (CoglOffscreen *offscreen,
-                  TryFBOFlags flags)
+static GList *
+try_creating_renderbuffers (CoglContext *ctx,
+                            int width,
+                            int height,
+                            CoglOffscreenAllocateFlags flags,
+                            int n_samples)
 {
-  CoglFramebuffer *fb = COGL_FRAMEBUFFER (offscreen);
-  CoglContext *ctx = fb->context;
+  GList *renderbuffers = NULL;
   GLuint gl_depth_stencil_handle;
-  GLuint gl_depth_handle;
-  GLuint gl_stencil_handle;
-  GLuint tex_gl_handle;
-  GLenum tex_gl_target;
-  GLuint fbo_gl_handle;
-  GLenum status;
-  int n_samples;
-  int height;
-  int width;
 
-  if (!cogl_texture_get_gl_texture (offscreen->texture,
-                                    &tex_gl_handle, &tex_gl_target))
-    return FALSE;
-
-  if (tex_gl_target != GL_TEXTURE_2D
-#ifdef HAVE_COGL_GL
-      && tex_gl_target != GL_TEXTURE_RECTANGLE_ARB
-#endif
-      )
-    return FALSE;
-
-  if (fb->config.samples_per_pixel)
-    {
-      if (!ctx->glFramebufferTexture2DMultisampleIMG)
-        return FALSE;
-      n_samples = fb->config.samples_per_pixel;
-    }
-  else
-    n_samples = 0;
-
-  width = offscreen->texture_level_width;
-  height = offscreen->texture_level_height;
-
-  /* We are about to generate and bind a new fbo, so we pretend to
-   * change framebuffer state so that the old framebuffer will be
-   * rebound again before drawing. */
-  ctx->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_BIND;
-
-  /* Generate framebuffer */
-  ctx->glGenFramebuffers (1, &fbo_gl_handle);
-  GE (ctx, glBindFramebuffer (GL_FRAMEBUFFER, fbo_gl_handle));
-  offscreen->fbo_handle = fbo_gl_handle;
-
-  if (n_samples)
+  if (flags & (COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH_STENCIL |
+               COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH24_STENCIL8))
     {
-      GE (ctx, glFramebufferTexture2DMultisampleIMG (GL_FRAMEBUFFER,
-                                                     GL_COLOR_ATTACHMENT0,
-                                                     tex_gl_target, tex_gl_handle,
-                                                     n_samples,
-                                                     offscreen->texture_level));
-    }
-  else
-    GE (ctx, glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
-                                     tex_gl_target, tex_gl_handle,
-                                     offscreen->texture_level));
-
-  if (flags & (_TRY_DEPTH_STENCIL | _TRY_DEPTH24_STENCIL8))
-    {
-      GLenum format = ((flags & _TRY_DEPTH_STENCIL) ?
+      GLenum format = ((flags & COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH_STENCIL) ?
                        GL_DEPTH_STENCIL : GL_DEPTH24_STENCIL8);
 
       /* Create a renderbuffer for depth and stenciling */
@@ -905,13 +854,15 @@ try_creating_fbo (CoglOffscreen *offscreen,
                                           GL_DEPTH_ATTACHMENT,
                                           GL_RENDERBUFFER,
                                           gl_depth_stencil_handle));
-      offscreen->renderbuffers =
-        g_slist_prepend (offscreen->renderbuffers,
-                         GUINT_TO_POINTER (gl_depth_stencil_handle));
+      renderbuffers =
+        g_list_prepend (renderbuffers,
+                        GUINT_TO_POINTER (gl_depth_stencil_handle));
     }
 
-  if (flags & _TRY_DEPTH)
+  if (flags & COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH)
     {
+      GLuint gl_depth_handle;
+
       GE (ctx, glGenRenderbuffers (1, &gl_depth_handle));
       GE (ctx, glBindRenderbuffer (GL_RENDERBUFFER, gl_depth_handle));
       /* For now we just ask for GL_DEPTH_COMPONENT16 since this is all that's
@@ -928,13 +879,14 @@ try_creating_fbo (CoglOffscreen *offscreen,
       GE (ctx, glFramebufferRenderbuffer (GL_FRAMEBUFFER,
                                           GL_DEPTH_ATTACHMENT,
                                           GL_RENDERBUFFER, gl_depth_handle));
-      offscreen->renderbuffers =
-        g_slist_prepend (offscreen->renderbuffers,
-                         GUINT_TO_POINTER (gl_depth_handle));
+      renderbuffers =
+        g_list_prepend (renderbuffers, GUINT_TO_POINTER (gl_depth_handle));
     }
 
-  if (flags & _TRY_STENCIL)
+  if (flags & COGL_OFFSCREEN_ALLOCATE_FLAG_STENCIL)
     {
+      GLuint gl_stencil_handle;
+
       GE (ctx, glGenRenderbuffers (1, &gl_stencil_handle));
       GE (ctx, glBindRenderbuffer (GL_RENDERBUFFER, gl_stencil_handle));
       if (n_samples)
@@ -949,28 +901,91 @@ try_creating_fbo (CoglOffscreen *offscreen,
       GE (ctx, glFramebufferRenderbuffer (GL_FRAMEBUFFER,
                                           GL_STENCIL_ATTACHMENT,
                                           GL_RENDERBUFFER, gl_stencil_handle));
-      offscreen->renderbuffers =
-        g_slist_prepend (offscreen->renderbuffers,
-                         GUINT_TO_POINTER (gl_stencil_handle));
+      renderbuffers =
+        g_list_prepend (renderbuffers, GUINT_TO_POINTER (gl_stencil_handle));
+    }
+
+  return renderbuffers;
+}
+
+/*
+ * NB: This function may be called with a standalone GLES2 context
+ * bound so we can create a shadow framebuffer that wraps the same
+ * CoglTexture as the given CoglOffscreen. This function shouldn't
+ * modify anything in
+ */
+static CoglBool
+try_creating_fbo (CoglContext *ctx,
+                  CoglTexture *texture,
+                  int texture_level,
+                  int texture_level_width,
+                  int texture_level_height,
+                  CoglFramebufferConfig *config,
+                  CoglOffscreenAllocateFlags flags,
+                  CoglGLFramebuffer *gl_framebuffer)
+{
+  GLuint tex_gl_handle;
+  GLenum tex_gl_target;
+  GLenum status;
+  int n_samples;
+
+  if (!cogl_texture_get_gl_texture (texture, &tex_gl_handle, &tex_gl_target))
+    return FALSE;
+
+  if (tex_gl_target != GL_TEXTURE_2D
+#ifdef HAVE_COGL_GL
+      && tex_gl_target != GL_TEXTURE_RECTANGLE_ARB
+#endif
+      )
+    return FALSE;
+
+  if (config->samples_per_pixel)
+    {
+      if (!ctx->glFramebufferTexture2DMultisampleIMG)
+        return FALSE;
+      n_samples = config->samples_per_pixel;
     }
+  else
+    n_samples = 0;
+
+  /* We are about to generate and bind a new fbo, so we pretend to
+   * change framebuffer state so that the old framebuffer will be
+   * rebound again before drawing. */
+  ctx->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_BIND;
+
+  /* Generate framebuffer */
+  ctx->glGenFramebuffers (1, &gl_framebuffer->fbo_handle);
+  GE (ctx, glBindFramebuffer (GL_FRAMEBUFFER, gl_framebuffer->fbo_handle));
+
+  if (n_samples)
+    {
+      GE (ctx, glFramebufferTexture2DMultisampleIMG (GL_FRAMEBUFFER,
+                                                     GL_COLOR_ATTACHMENT0,
+                                                     tex_gl_target, tex_gl_handle,
+                                                     n_samples,
+                                                     texture_level));
+    }
+  else
+    GE (ctx, glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+                                     tex_gl_target, tex_gl_handle,
+                                     texture_level));
+
+  gl_framebuffer->renderbuffers =
+    try_creating_renderbuffers (ctx,
+                                texture_level_width,
+                                texture_level_height,
+                                flags,
+                                n_samples);
 
   /* Make sure it's complete */
   status = ctx->glCheckFramebufferStatus (GL_FRAMEBUFFER);
 
   if (status != GL_FRAMEBUFFER_COMPLETE)
     {
-      GSList *l;
+      GE (ctx, glDeleteFramebuffers (1, &gl_framebuffer->fbo_handle));
 
-      GE (ctx, glDeleteFramebuffers (1, &fbo_gl_handle));
-
-      for (l = offscreen->renderbuffers; l; l = l->next)
-        {
-          GLuint renderbuffer = GPOINTER_TO_UINT (l->data);
-          GE (ctx, glDeleteRenderbuffers (1, &renderbuffer));
-        }
-
-      g_slist_free (offscreen->renderbuffers);
-      offscreen->renderbuffers = NULL;
+      delete_renderbuffers (ctx, gl_framebuffer->renderbuffers);
+      gl_framebuffer->renderbuffers = NULL;
 
       return FALSE;
     }
@@ -987,21 +1002,40 @@ try_creating_fbo (CoglOffscreen *offscreen,
                                                       attachment,
                                                       pname,
                                                       &texture_samples) );
-      fb->samples_per_pixel = texture_samples;
+      gl_framebuffer->samples_per_pixel = texture_samples;
     }
 
   return TRUE;
 }
 
+CoglBool
+_cogl_framebuffer_try_creating_gl_fbo (CoglContext *ctx,
+                                       CoglTexture *texture,
+                                       int texture_level,
+                                       int texture_level_width,
+                                       int texture_level_height,
+                                       CoglFramebufferConfig *config,
+                                       CoglOffscreenAllocateFlags flags,
+                                       CoglGLFramebuffer *gl_framebuffer)
+{
+  return try_creating_fbo (ctx,
+                           texture,
+                           texture_level,
+                           texture_level_width,
+                           texture_level_height,
+                           config,
+                           flags,
+                           gl_framebuffer);
+}
+
 static CoglBool
 _cogl_offscreen_allocate (CoglOffscreen *offscreen,
                           GError **error)
 {
   CoglFramebuffer *fb = COGL_FRAMEBUFFER (offscreen);
   CoglContext *ctx = fb->context;
-  static TryFBOFlags flags;
-  static CoglBool have_working_flags = FALSE;
-  CoglBool fbo_created;
+  CoglOffscreenAllocateFlags flags;
+  CoglGLFramebuffer *gl_framebuffer = &offscreen->gl_framebuffer;
 
   /* XXX: The framebuffer_object spec isn't clear in defining whether attaching
    * a texture as a renderbuffer with mipmap filtering enabled while the
@@ -1015,41 +1049,106 @@ _cogl_offscreen_allocate (CoglOffscreen *offscreen,
    */
   _cogl_texture_set_filters (offscreen->texture, GL_NEAREST, GL_NEAREST);
 
-  if ((offscreen->create_flags & COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL))
-    fbo_created = try_creating_fbo (offscreen, 0);
-  else
+  if (((offscreen->create_flags & COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL) &&
+       try_creating_fbo (ctx,
+                         offscreen->texture,
+                         offscreen->texture_level,
+                         offscreen->texture_level_width,
+                         offscreen->texture_level_height,
+                         &fb->config,
+                         flags = 0,
+                         gl_framebuffer)) ||
+
+      (ctx->have_last_offscreen_allocate_flags &&
+       try_creating_fbo (ctx,
+                         offscreen->texture,
+                         offscreen->texture_level,
+                         offscreen->texture_level_width,
+                         offscreen->texture_level_height,
+                         &fb->config,
+                         flags = ctx->last_offscreen_allocate_flags,
+                         gl_framebuffer)) ||
+
+      ((ctx->private_feature_flags &
+        COGL_PRIVATE_FEATURE_EXT_PACKED_DEPTH_STENCIL) &&
+       try_creating_fbo (ctx,
+                         offscreen->texture,
+                         offscreen->texture_level,
+                         offscreen->texture_level_width,
+                         offscreen->texture_level_height,
+                         &fb->config,
+                         flags = COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH_STENCIL,
+                         gl_framebuffer)) ||
+
+      ((ctx->private_feature_flags &
+        COGL_PRIVATE_FEATURE_OES_PACKED_DEPTH_STENCIL) &&
+       try_creating_fbo (ctx,
+                         offscreen->texture,
+                         offscreen->texture_level,
+                         offscreen->texture_level_width,
+                         offscreen->texture_level_height,
+                         &fb->config,
+                         flags = COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH24_STENCIL8,
+                         gl_framebuffer)) ||
+
+      try_creating_fbo (ctx,
+                        offscreen->texture,
+                        offscreen->texture_level,
+                        offscreen->texture_level_width,
+                        offscreen->texture_level_height,
+                        &fb->config,
+                        flags = COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH |
+                        COGL_OFFSCREEN_ALLOCATE_FLAG_STENCIL,
+                        gl_framebuffer) ||
+
+      try_creating_fbo (ctx,
+                        offscreen->texture,
+                        offscreen->texture_level,
+                        offscreen->texture_level_width,
+                        offscreen->texture_level_height,
+                        &fb->config,
+                        flags = COGL_OFFSCREEN_ALLOCATE_FLAG_STENCIL,
+                        gl_framebuffer) ||
+
+      try_creating_fbo (ctx,
+                        offscreen->texture,
+                        offscreen->texture_level,
+                        offscreen->texture_level_width,
+                        offscreen->texture_level_height,
+                        &fb->config,
+                        flags = COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH,
+                        gl_framebuffer) ||
+
+      try_creating_fbo (ctx,
+                        offscreen->texture,
+                        offscreen->texture_level,
+                        offscreen->texture_level_width,
+                        offscreen->texture_level_height,
+                        &fb->config,
+                        flags = 0,
+                        gl_framebuffer))
     {
-      if ((have_working_flags &&
-           try_creating_fbo (offscreen, flags)) ||
-          ((ctx->private_feature_flags &
-            COGL_PRIVATE_FEATURE_EXT_PACKED_DEPTH_STENCIL) &&
-           try_creating_fbo (offscreen, flags = _TRY_DEPTH_STENCIL)) ||
-          ((ctx->private_feature_flags &
-            COGL_PRIVATE_FEATURE_OES_PACKED_DEPTH_STENCIL) &&
-           try_creating_fbo (offscreen, flags = _TRY_DEPTH24_STENCIL8)) ||
-          try_creating_fbo (offscreen, flags = _TRY_DEPTH | _TRY_STENCIL) ||
-          try_creating_fbo (offscreen, flags = _TRY_STENCIL) ||
-          try_creating_fbo (offscreen, flags = _TRY_DEPTH) ||
-          try_creating_fbo (offscreen, flags = 0))
-        {
-          /* Record that the last set of flags succeeded so that we can
-             try that set first next time */
-          have_working_flags = TRUE;
-          fbo_created = TRUE;
-        }
-      else
-        fbo_created = FALSE;
-    }
+      fb->samples_per_pixel = gl_framebuffer->samples_per_pixel;
+
+      /* Record that the last set of flags succeeded so that we can
+         try that set first next time */
+      ctx->last_offscreen_allocate_flags = flags;
+      ctx->have_last_offscreen_allocate_flags = TRUE;
 
-  if (!fbo_created)
+      /* Save the flags we managed so successfully allocate the
+       * renderbuffers with in case we need to make renderbuffers for a
+       * GLES2 context later */
+      offscreen->allocation_flags = flags;
+
+      return TRUE;
+    }
+  else
     {
       g_set_error (error, COGL_FRAMEBUFFER_ERROR,
                    COGL_FRAMEBUFFER_ERROR_ALLOCATE,
                    "Failed to create an OpenGL framebuffer object");
       return FALSE;
     }
-
-  return TRUE;
 }
 
 CoglBool
@@ -1309,14 +1408,17 @@ cogl_pop_draw_buffer (void)
   cogl_pop_framebuffer ();
 }
 
-static void
-bind_gl_framebuffer (CoglContext *ctx,
-                     GLenum target,
-                     CoglFramebuffer *framebuffer)
+void
+_cogl_gl_framebuffer_bind (CoglFramebuffer *framebuffer, GLenum target)
 {
+  CoglContext *ctx = framebuffer->context;
+
   if (framebuffer->type == COGL_FRAMEBUFFER_TYPE_OFFSCREEN)
-    GE (ctx, glBindFramebuffer (target,
-                           COGL_OFFSCREEN (framebuffer)->fbo_handle));
+    {
+      CoglOffscreen *offscreen = COGL_OFFSCREEN (framebuffer);
+      GE (ctx, glBindFramebuffer (target,
+                                  offscreen->gl_framebuffer.fbo_handle));
+    }
   else
     {
       const CoglWinsysVtable *winsys =
@@ -1665,7 +1767,7 @@ _cogl_framebuffer_flush_state (CoglFramebuffer *draw_buffer,
   if (differences & COGL_FRAMEBUFFER_STATE_BIND)
     {
       if (draw_buffer == read_buffer)
-        bind_gl_framebuffer (ctx, GL_FRAMEBUFFER, draw_buffer);
+        _cogl_gl_framebuffer_bind (draw_buffer, GL_FRAMEBUFFER);
       else
         {
           /* NB: Currently we only take advantage of binding separate
@@ -1676,8 +1778,8 @@ _cogl_framebuffer_flush_state (CoglFramebuffer *draw_buffer,
           _COGL_RETURN_IF_FAIL (draw_buffer->type == COGL_FRAMEBUFFER_TYPE_OFFSCREEN);
           _COGL_RETURN_IF_FAIL (read_buffer->type == COGL_FRAMEBUFFER_TYPE_OFFSCREEN);
 
-          bind_gl_framebuffer (ctx, GL_DRAW_FRAMEBUFFER, draw_buffer);
-          bind_gl_framebuffer (ctx, GL_READ_FRAMEBUFFER, read_buffer);
+          _cogl_gl_framebuffer_bind (draw_buffer, GL_DRAW_FRAMEBUFFER);
+          _cogl_gl_framebuffer_bind (read_buffer, GL_READ_FRAMEBUFFER);
         }
 
       differences &= ~COGL_FRAMEBUFFER_STATE_BIND;
diff --git a/cogl/cogl-gles2-context-private.h b/cogl/cogl-gles2-context-private.h
new file mode 100644
index 0000000..4512de4
--- /dev/null
+++ b/cogl/cogl-gles2-context-private.h
@@ -0,0 +1,68 @@
+/*
+ * Cogl
+ *
+ * An object oriented GL/GLES Abstraction/Utility Layer
+ *
+ * Copyright (C) 2011 Collabora Ltd.
+ * Copyright (C) 2012 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:
+ *  Tomeu Vizoso <tomeu.vizoso at collabora.com>
+ *  Robert Bragg <robert at linux.intel.com>
+ *
+ */
+
+#ifndef __COGL_GLES2_CONTEXT_PRIVATE_H
+#define __COGL_GLES2_CONTEXT_PRIVATE_H
+
+#include <glib.h>
+
+#include "cogl-object-private.h"
+#include "cogl-framebuffer-private.h"
+
+typedef struct _CoglGLES2Offscreen CoglGLES2Offscreen;
+
+COGL_LIST_HEAD (CoglGLES2OffscreenList, CoglGLES2Offscreen);
+
+struct _CoglGLES2Offscreen
+{
+  COGL_LIST_ENTRY (CoglGLES2Offscreen) list_node;
+  CoglOffscreen *original_offscreen;
+  CoglGLFramebuffer gl_framebuffer;
+};
+
+struct _CoglGLES2Context
+{
+  CoglObject _parent;
+
+  CoglContext *context;
+
+  CoglFramebuffer *read_buffer;
+  CoglGLES2Offscreen *gles2_read_buffer;
+  CoglFramebuffer *write_buffer;
+  CoglGLES2Offscreen *gles2_write_buffer;
+
+  GLuint current_fbo_handle;
+
+  CoglGLES2OffscreenList foreign_offscreens;
+
+  CoglGLES2Vtable *vtable;
+
+  void *winsys;
+};
+
+#endif /* __COGL_GLES2_CONTEXT_PRIVATE_H */
diff --git a/cogl/cogl-gles2-context.c b/cogl/cogl-gles2-context.c
new file mode 100644
index 0000000..7fbf2ff
--- /dev/null
+++ b/cogl/cogl-gles2-context.c
@@ -0,0 +1,542 @@
+/*
+ * Cogl
+ *
+ * An object oriented GL/GLES Abstraction/Utility Layer
+ *
+ * Copyright (C) 2011 Collabora Ltd.
+ * Copyright (C) 2012 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:
+ *  Tomeu Vizoso <tomeu.vizoso at collabora.com>
+ *  Robert Bragg <robert at linux.intel.com>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "cogl-gles2.h"
+#include "cogl-gles2-context-private.h"
+
+#include "cogl-context-private.h"
+#include "cogl-display-private.h"
+#include "cogl-framebuffer-private.h"
+#include "cogl-onscreen-template-private.h"
+#include "cogl-renderer-private.h"
+#include "cogl-swap-chain-private.h"
+#include "cogl-texture-2d-private.h"
+
+static void _cogl_gles2_context_free (CoglGLES2Context *gles2_context);
+
+COGL_OBJECT_DEFINE (GLES2Context, gles2_context);
+
+static CoglGLES2Context *current_gles2_context;
+
+static CoglUserDataKey offscreen_wrapper_key;
+
+enum {
+  RESTORE_FB_NONE,
+  RESTORE_FB_FROM_OFFSCREEN,
+  RESTORE_FB_FROM_ONSCREEN,
+};
+
+GQuark
+_cogl_gles2_context_error_quark (void)
+{
+  return g_quark_from_static_string ("cogl-gles2-context-error-quark");
+}
+
+/* We wrap glBindFramebuffer so that when framebuffer 0 is bound
+ * we can instead bind the write_framebuffer passed to
+ * cogl_push_gles2_context().
+ */
+static void
+gl_bind_framebuffer_wrapper (GLenum target, GLuint framebuffer)
+{
+  CoglGLES2Context *gles2_ctx = current_gles2_context;
+
+  gles2_ctx->current_fbo_handle = framebuffer;
+
+  if (framebuffer == 0 && cogl_is_offscreen (gles2_ctx->write_buffer))
+    {
+      CoglGLES2Offscreen *write = gles2_ctx->gles2_write_buffer;
+      framebuffer = write->gl_framebuffer.fbo_handle;
+    }
+
+  gles2_ctx->context->glBindFramebuffer (target, framebuffer);
+}
+
+static int
+transient_bind_read_buffer (CoglGLES2Context *gles2_ctx)
+{
+  if (gles2_ctx->current_fbo_handle == 0)
+    {
+      if (cogl_is_offscreen (gles2_ctx->read_buffer))
+        {
+          CoglGLES2Offscreen *read = gles2_ctx->gles2_read_buffer;
+          GLuint read_fbo_handle = read->gl_framebuffer.fbo_handle;
+
+          gles2_ctx->context->glBindFramebuffer (GL_FRAMEBUFFER,
+                                                 read_fbo_handle);
+
+          return RESTORE_FB_FROM_OFFSCREEN;
+        }
+      else
+        {
+          _cogl_gl_framebuffer_bind (gles2_ctx->read_buffer,
+                                     0 /* target ignored */);
+
+          return RESTORE_FB_FROM_ONSCREEN;
+        }
+    }
+  else
+    return RESTORE_FB_NONE;
+}
+
+static void
+restore_write_buffer (CoglGLES2Context *gles2_ctx,
+                      int restore_mode)
+{
+  switch (restore_mode)
+    {
+    case RESTORE_FB_FROM_OFFSCREEN:
+
+      gl_bind_framebuffer_wrapper (GL_FRAMEBUFFER, 0);
+
+      break;
+    case RESTORE_FB_FROM_ONSCREEN:
+
+      /* Note: we can't restore the original write buffer using
+       * _cogl_gl_framebuffer_bind() if it's an offscreen
+       * framebuffer because _cogl_gl_framebuffer_bind() doesn't
+       * know about the fbo handle owned by the gles2 context.
+       */
+      if (cogl_is_offscreen (gles2_ctx->write_buffer))
+        gl_bind_framebuffer_wrapper (GL_FRAMEBUFFER, 0);
+      else
+        _cogl_gl_framebuffer_bind (gles2_ctx->write_buffer, GL_FRAMEBUFFER);
+
+      break;
+    case RESTORE_FB_NONE:
+      break;
+    }
+}
+
+/* We wrap glReadPixels so when framebuffer 0 is bound then we can
+ * read from the read_framebuffer passed to cogl_push_gles2_context().
+ */
+static void
+gl_read_pixels_wrapper (GLint x,
+                        GLint y,
+                        GLsizei width,
+                        GLsizei height,
+                        GLenum format,
+                        GLenum type,
+                        GLvoid *pixels)
+{
+  CoglGLES2Context *gles2_ctx = current_gles2_context;
+  int restore_mode = transient_bind_read_buffer (gles2_ctx);
+
+  gles2_ctx->context->glReadPixels (x, y, width, height, format, type, pixels);
+
+  restore_write_buffer (gles2_ctx, restore_mode);
+}
+
+static void
+gl_copy_tex_image_2d_wrapper (GLenum target,
+                              GLint level,
+                              GLenum internalformat,
+                              GLint x,
+                              GLint y,
+                              GLsizei width,
+                              GLsizei height,
+                              GLint border)
+{
+  CoglGLES2Context *gles2_ctx = current_gles2_context;
+  int restore_mode = transient_bind_read_buffer (gles2_ctx);
+
+  gles2_ctx->context->glCopyTexImage2D (target, level, internalformat,
+                                        x, y, width, height, border);
+
+  restore_write_buffer (gles2_ctx, restore_mode);
+}
+
+static void
+gl_copy_tex_sub_image_2d_wrapper (GLenum target,
+                                  GLint level,
+                                  GLint xoffset,
+                                  GLint yoffset,
+                                  GLint x,
+                                  GLint y,
+                                  GLsizei width,
+                                  GLsizei height)
+{
+  CoglGLES2Context *gles2_ctx = current_gles2_context;
+  int restore_mode = transient_bind_read_buffer (gles2_ctx);
+
+  gles2_ctx->context->glCopyTexImage2D (target, level,
+                                        xoffset, yoffset,
+                                        x, y, width, height);
+
+  restore_write_buffer (gles2_ctx, restore_mode);
+}
+
+static void
+_cogl_gles2_offscreen_free (CoglGLES2Offscreen *gles2_offscreen)
+{
+  COGL_LIST_REMOVE (gles2_offscreen, list_node);
+  g_slice_free (CoglGLES2Offscreen, gles2_offscreen);
+}
+
+static void
+_cogl_gles2_context_free (CoglGLES2Context *gles2_context)
+{
+  CoglContext *ctx = gles2_context->context;
+  const CoglWinsysVtable *winsys;
+
+  winsys = ctx->display->renderer->winsys_vtable;
+  winsys->destroy_gles2_context (gles2_context);
+
+  while (gles2_context->foreign_offscreens.lh_first)
+    {
+      CoglGLES2Offscreen *gles2_offscreen =
+        gles2_context->foreign_offscreens.lh_first;
+
+      /* Note: this will also indirectly free the gles2_offscreen by
+       * calling the destroy notify for the _user_data */
+      cogl_object_set_user_data (COGL_OBJECT (gles2_offscreen->original_offscreen),
+                                 &offscreen_wrapper_key,
+                                 NULL,
+                                 NULL);
+    }
+
+  cogl_object_unref (gles2_context->context);
+
+  g_free (gles2_context->vtable);
+
+  g_free (gles2_context);
+}
+
+CoglGLES2Context *
+cogl_gles2_context_new (CoglContext *ctx, GError **error)
+{
+  CoglGLES2Context *gles2_ctx;
+  const CoglWinsysVtable *winsys;
+
+  if (!cogl_has_feature (ctx, COGL_FEATURE_ID_GLES2_CONTEXT))
+    {
+      g_set_error (error, COGL_GLES2_CONTEXT_ERROR,
+                   COGL_GLES2_CONTEXT_ERROR_UNSUPPORTED,
+                   "Backend doesn't support creating GLES2 contexts");
+
+      return NULL;
+    }
+
+  gles2_ctx = g_malloc0 (sizeof (CoglGLES2Context));
+
+  cogl_object_ref (ctx);
+  gles2_ctx->context = ctx;
+
+  COGL_LIST_INIT (&gles2_ctx->foreign_offscreens);
+
+  winsys = ctx->display->renderer->winsys_vtable;
+  gles2_ctx->winsys = winsys->context_create_gles2_context (ctx, error);
+  if (gles2_ctx->winsys == NULL)
+    {
+      cogl_object_unref (gles2_ctx->context);
+      g_free (gles2_ctx);
+      return NULL;
+    }
+
+  gles2_ctx->vtable = g_malloc0 (sizeof (CoglGLES2Vtable));
+#define COGL_EXT_BEGIN(name, \
+                       min_gl_major, min_gl_minor, \
+                       gles_availability, \
+                       extension_suffixes, extension_names)
+
+#define COGL_EXT_FUNCTION(ret, name, args) \
+  gles2_ctx->vtable->name = ctx->name;
+
+#define COGL_EXT_END()
+
+#include "gl-prototypes/cogl-gles2-functions.h"
+
+#undef COGL_EXT_BEGIN
+#undef COGL_EXT_FUNCTION
+#undef COGL_EXT_END
+
+  gles2_ctx->vtable->glBindFramebuffer = gl_bind_framebuffer_wrapper;
+  gles2_ctx->vtable->glReadPixels = gl_read_pixels_wrapper;
+  gles2_ctx->vtable->glCopyTexImage2D = gl_copy_tex_image_2d_wrapper;
+  gles2_ctx->vtable->glCopyTexSubImage2D = gl_copy_tex_sub_image_2d_wrapper;
+
+  return _cogl_gles2_context_object_new (gles2_ctx);
+}
+
+const CoglGLES2Vtable *
+cogl_gles2_context_get_vtable (CoglGLES2Context *gles2_ctx)
+{
+  return gles2_ctx->vtable;
+}
+
+/* When drawing to a CoglFramebuffer from a separate context we have
+ * to be able to allocate ancillary buffers for that context...
+ */
+static CoglGLES2Offscreen *
+_cogl_gles2_offscreen_allocate (CoglOffscreen *offscreen,
+                                CoglGLES2Context *gles2_context,
+                                GError **error)
+{
+  CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (offscreen);
+  const CoglWinsysVtable *winsys;
+  GError *internal_error = NULL;
+  CoglGLES2Offscreen *gles2_offscreen;
+
+  if (!framebuffer->allocated &&
+      !cogl_framebuffer_allocate (framebuffer, error))
+    {
+      return NULL;
+    }
+
+  for (gles2_offscreen = gles2_context->foreign_offscreens.lh_first;
+       gles2_offscreen;
+       gles2_offscreen = gles2_offscreen->list_node.le_next)
+    {
+      if (gles2_offscreen->original_offscreen == offscreen)
+        return gles2_offscreen;
+    }
+
+  winsys = _cogl_framebuffer_get_winsys (framebuffer);
+  winsys->save_context (framebuffer->context);
+  if (!winsys->set_gles2_context (gles2_context, &internal_error))
+    {
+      winsys->restore_context (framebuffer->context);
+
+      g_error_free (internal_error);
+      g_set_error (error, COGL_FRAMEBUFFER_ERROR,
+                   COGL_FRAMEBUFFER_ERROR_ALLOCATE,
+                   "Failed to bind gles2 context to create framebuffer");
+      return NULL;
+    }
+
+  gles2_offscreen = g_slice_new0 (CoglGLES2Offscreen);
+  if (!_cogl_framebuffer_try_creating_gl_fbo (gles2_context->context,
+                                              offscreen->texture,
+                                              offscreen->texture_level,
+                                              offscreen->texture_level_width,
+                                              offscreen->texture_level_height,
+                                              &COGL_FRAMEBUFFER (offscreen)->config,
+                                              offscreen->allocation_flags,
+                                              &gles2_offscreen->gl_framebuffer))
+    {
+      winsys->restore_context (framebuffer->context);
+
+      g_slice_free (CoglGLES2Offscreen, gles2_offscreen);
+
+      g_set_error (error, COGL_FRAMEBUFFER_ERROR,
+                   COGL_FRAMEBUFFER_ERROR_ALLOCATE,
+                   "Failed to create an OpenGL framebuffer object");
+      return NULL;
+    }
+
+  winsys->restore_context (framebuffer->context);
+
+  gles2_offscreen->original_offscreen = offscreen;
+
+  COGL_LIST_INSERT_HEAD (&gles2_context->foreign_offscreens,
+                         gles2_offscreen,
+                         list_node);
+
+  /* So we avoid building up an ever growing collection of ancillary
+   * buffers for wrapped framebuffers, we make sure that the wrappers
+   * get freed when the original offscreen framebuffer is freed. */
+  cogl_object_set_user_data (COGL_OBJECT (framebuffer),
+                             &offscreen_wrapper_key,
+                             gles2_offscreen,
+                             (CoglUserDataDestroyCallback)
+                                _cogl_gles2_offscreen_free);
+
+  return gles2_offscreen;
+}
+
+CoglBool
+cogl_push_gles2_context (CoglContext *ctx,
+                         CoglGLES2Context *gles2_ctx,
+                         CoglFramebuffer *read_buffer,
+                         CoglFramebuffer *write_buffer,
+                         GError **error)
+{
+  const CoglWinsysVtable *winsys = ctx->display->renderer->winsys_vtable;
+  GError *internal_error = NULL;
+
+  _COGL_RETURN_VAL_IF_FAIL (gles2_ctx != NULL, FALSE);
+
+  /* The read/write buffers are properties of the gles2 context and we
+   * don't currently track the read/write buffers as part of the stack
+   * entries so we explicitly don't allow the same context to be
+   * pushed multiple times. */
+  if (g_queue_find (&ctx->gles2_context_stack, gles2_ctx))
+    {
+      g_critical ("Pushing the same GLES2 context multiple times isn't "
+                  "supported");
+      return FALSE;
+    }
+
+  if (ctx->gles2_context_stack.length == 0)
+    {
+      _cogl_journal_flush (read_buffer->journal);
+      if (write_buffer != read_buffer)
+        _cogl_journal_flush (write_buffer->journal);
+      winsys->save_context (ctx);
+    }
+  else
+    gles2_ctx->vtable->glFlush ();
+
+  if (gles2_ctx->read_buffer != read_buffer)
+    {
+      if (cogl_is_offscreen (read_buffer))
+        {
+          gles2_ctx->gles2_read_buffer =
+            _cogl_gles2_offscreen_allocate (COGL_OFFSCREEN (read_buffer),
+                                            gles2_ctx,
+                                            error);
+          /* XXX: what consistency guarantees should this api have?
+           *
+           * It should be safe to return at this point but we provide
+           * no guarantee to the caller whether their given buffers
+           * may be referenced and old buffers unreferenced even
+           * if the _push fails. */
+          if (!gles2_ctx->gles2_read_buffer)
+            return FALSE;
+        }
+      else
+        gles2_ctx->gles2_read_buffer = NULL;
+      if (gles2_ctx->read_buffer)
+        cogl_object_unref (gles2_ctx->read_buffer);
+      gles2_ctx->read_buffer = cogl_object_ref (read_buffer);
+    }
+
+  if (gles2_ctx->write_buffer != write_buffer)
+    {
+      if (cogl_is_offscreen (write_buffer))
+        {
+          gles2_ctx->gles2_write_buffer =
+            _cogl_gles2_offscreen_allocate (COGL_OFFSCREEN (write_buffer),
+                                            gles2_ctx,
+                                            error);
+          /* XXX: what consistency guarantees should this api have?
+           *
+           * It should be safe to return at this point but we provide
+           * no guarantee to the caller whether their given buffers
+           * may be referenced and old buffers unreferenced even
+           * if the _push fails. */
+          if (!gles2_ctx->gles2_write_buffer)
+            return FALSE;
+        }
+      else
+        gles2_ctx->gles2_write_buffer = NULL;
+      if (gles2_ctx->write_buffer)
+        cogl_object_unref (gles2_ctx->write_buffer);
+      gles2_ctx->write_buffer = cogl_object_ref (write_buffer);
+    }
+
+  if (!winsys->set_gles2_context (gles2_ctx, &internal_error))
+    {
+      winsys->restore_context (ctx);
+
+      g_error_free (internal_error);
+      g_set_error (error, COGL_GLES2_CONTEXT_ERROR,
+                   COGL_GLES2_CONTEXT_ERROR_DRIVER,
+                   "Driver failed to make GLES2 context current");
+      return FALSE;
+    }
+
+  g_queue_push_tail (&ctx->gles2_context_stack, gles2_ctx);
+
+  /* The last time this context was pushed may have been with a
+   * different offscreen draw framebuffer and so if GL framebuffer 0
+   * is bound for this GLES2 context we may need to bind a new,
+   * corresponding, window system framebuffer... */
+  if (gles2_ctx->current_fbo_handle == 0)
+    {
+      if (cogl_is_offscreen (gles2_ctx->write_buffer))
+        {
+          CoglGLES2Offscreen *write = gles2_ctx->gles2_write_buffer;
+          GLuint handle = write->gl_framebuffer.fbo_handle;
+          gles2_ctx->context->glBindFramebuffer (GL_FRAMEBUFFER, handle);
+        }
+    }
+
+  current_gles2_context = gles2_ctx;
+  return TRUE;
+}
+
+CoglGLES2Vtable *
+cogl_gles2_get_current_vtable (void)
+{
+  return current_gles2_context ? current_gles2_context->vtable : NULL;
+}
+
+void
+cogl_pop_gles2_context (CoglContext *ctx)
+{
+  CoglGLES2Context *gles2_ctx;
+  const CoglWinsysVtable *winsys = ctx->display->renderer->winsys_vtable;
+
+  _COGL_RETURN_IF_FAIL (ctx->gles2_context_stack->length > 0);
+
+  g_queue_pop_tail (&ctx->gles2_context_stack);
+
+  gles2_ctx = g_queue_peek_tail (&ctx->gles2_context_stack);
+
+  if (gles2_ctx)
+    {
+      winsys->set_gles2_context (gles2_ctx, NULL);
+      current_gles2_context = gles2_ctx;
+    }
+  else
+    {
+      winsys->restore_context (ctx);
+      current_gles2_context = NULL;
+    }
+}
+
+CoglTexture2D *
+cogl_gles2_texture_2d_new_from_handle (CoglContext *ctx,
+                                       CoglGLES2Context *gles2_ctx,
+                                       unsigned int handle,
+                                       int width,
+                                       int height,
+                                       CoglPixelFormat internal_format,
+                                       GError **error)
+{
+  return cogl_texture_2d_new_from_foreign (ctx,
+                                           handle,
+                                           width,
+                                           height,
+                                           internal_format,
+                                           error);
+}
+
+CoglBool
+cogl_gles2_texture_get_handle (CoglTexture *texture,
+                               unsigned int *handle,
+                               unsigned int *target)
+{
+  return cogl_texture_get_gl_texture (texture, handle, target);
+}
diff --git a/cogl/cogl-gles2-types.h b/cogl/cogl-gles2-types.h
new file mode 100644
index 0000000..9f6d097
--- /dev/null
+++ b/cogl/cogl-gles2-types.h
@@ -0,0 +1,474 @@
+#ifndef __COGL_GLES2_TYPES_H_
+#define __COGL_GLES2_TYPES_H_
+
+/* $Revision: 16803 $ on $Date:: 2012-02-02 09:49:18 -0800 #$ */
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * This document is licensed under the SGI Free Software B License Version
+ * 2.0. For details, see http://oss.sgi.com/projects/FreeB/ .
+ */
+
+/*-------------------------------------------------------------------------
+ * Data type definitions
+ *-----------------------------------------------------------------------*/
+
+typedef void             GLvoid;
+typedef char             GLchar;
+typedef unsigned int     GLenum;
+typedef unsigned char    GLboolean;
+typedef unsigned int     GLbitfield;
+typedef int8_t           GLbyte;
+typedef short            GLshort;
+typedef int              GLint;
+typedef int              GLsizei;
+typedef uint8_t          GLubyte;
+typedef unsigned short   GLushort;
+typedef unsigned int     GLuint;
+typedef float            GLfloat;
+typedef float            GLclampf;
+typedef int32_t          GLfixed;
+
+/* GL types for handling large vertex buffer objects */
+typedef intptr_t         GLintptr;
+typedef long             GLsizeiptr;
+
+/* OpenGL ES core versions */
+#define GL_ES_VERSION_2_0                 1
+
+/* ClearBufferMask */
+#define GL_DEPTH_BUFFER_BIT               0x00000100
+#define GL_STENCIL_BUFFER_BIT             0x00000400
+#define GL_COLOR_BUFFER_BIT               0x00004000
+
+/* Boolean */
+#define GL_FALSE                          0
+#define GL_TRUE                           1
+
+/* BeginMode */
+#define GL_POINTS                         0x0000
+#define GL_LINES                          0x0001
+#define GL_LINE_LOOP                      0x0002
+#define GL_LINE_STRIP                     0x0003
+#define GL_TRIANGLES                      0x0004
+#define GL_TRIANGLE_STRIP                 0x0005
+#define GL_TRIANGLE_FAN                   0x0006
+
+/* AlphaFunction (not supported in ES20) */
+/*      GL_NEVER */
+/*      GL_LESS */
+/*      GL_EQUAL */
+/*      GL_LEQUAL */
+/*      GL_GREATER */
+/*      GL_NOTEQUAL */
+/*      GL_GEQUAL */
+/*      GL_ALWAYS */
+
+/* BlendingFactorDest */
+#define GL_ZERO                           0
+#define GL_ONE                            1
+#define GL_SRC_COLOR                      0x0300
+#define GL_ONE_MINUS_SRC_COLOR            0x0301
+#define GL_SRC_ALPHA                      0x0302
+#define GL_ONE_MINUS_SRC_ALPHA            0x0303
+#define GL_DST_ALPHA                      0x0304
+#define GL_ONE_MINUS_DST_ALPHA            0x0305
+
+/* BlendingFactorSrc */
+/*      GL_ZERO */
+/*      GL_ONE */
+#define GL_DST_COLOR                      0x0306
+#define GL_ONE_MINUS_DST_COLOR            0x0307
+#define GL_SRC_ALPHA_SATURATE             0x0308
+/*      GL_SRC_ALPHA */
+/*      GL_ONE_MINUS_SRC_ALPHA */
+/*      GL_DST_ALPHA */
+/*      GL_ONE_MINUS_DST_ALPHA */
+
+/* BlendEquationSeparate */
+#define GL_FUNC_ADD                       0x8006
+#define GL_BLEND_EQUATION                 0x8009
+#define GL_BLEND_EQUATION_RGB             0x8009    /* same as BLEND_EQUATION */
+#define GL_BLEND_EQUATION_ALPHA           0x883D
+
+/* BlendSubtract */
+#define GL_FUNC_SUBTRACT                  0x800A
+#define GL_FUNC_REVERSE_SUBTRACT          0x800B
+
+/* Separate Blend Functions */
+#define GL_BLEND_DST_RGB                  0x80C8
+#define GL_BLEND_SRC_RGB                  0x80C9
+#define GL_BLEND_DST_ALPHA                0x80CA
+#define GL_BLEND_SRC_ALPHA                0x80CB
+#define GL_CONSTANT_COLOR                 0x8001
+#define GL_ONE_MINUS_CONSTANT_COLOR       0x8002
+#define GL_CONSTANT_ALPHA                 0x8003
+#define GL_ONE_MINUS_CONSTANT_ALPHA       0x8004
+#define GL_BLEND_COLOR                    0x8005
+
+/* Buffer Objects */
+#define GL_ARRAY_BUFFER                   0x8892
+#define GL_ELEMENT_ARRAY_BUFFER           0x8893
+#define GL_ARRAY_BUFFER_BINDING           0x8894
+#define GL_ELEMENT_ARRAY_BUFFER_BINDING   0x8895
+
+#define GL_STREAM_DRAW                    0x88E0
+#define GL_STATIC_DRAW                    0x88E4
+#define GL_DYNAMIC_DRAW                   0x88E8
+
+#define GL_BUFFER_SIZE                    0x8764
+#define GL_BUFFER_USAGE                   0x8765
+
+#define GL_CURRENT_VERTEX_ATTRIB          0x8626
+
+/* CullFaceMode */
+#define GL_FRONT                          0x0404
+#define GL_BACK                           0x0405
+#define GL_FRONT_AND_BACK                 0x0408
+
+/* DepthFunction */
+/*      GL_NEVER */
+/*      GL_LESS */
+/*      GL_EQUAL */
+/*      GL_LEQUAL */
+/*      GL_GREATER */
+/*      GL_NOTEQUAL */
+/*      GL_GEQUAL */
+/*      GL_ALWAYS */
+
+/* EnableCap */
+#define GL_TEXTURE_2D                     0x0DE1
+#define GL_CULL_FACE                      0x0B44
+#define GL_BLEND                          0x0BE2
+#define GL_DITHER                         0x0BD0
+#define GL_STENCIL_TEST                   0x0B90
+#define GL_DEPTH_TEST                     0x0B71
+#define GL_SCISSOR_TEST                   0x0C11
+#define GL_POLYGON_OFFSET_FILL            0x8037
+#define GL_SAMPLE_ALPHA_TO_COVERAGE       0x809E
+#define GL_SAMPLE_COVERAGE                0x80A0
+
+/* ErrorCode */
+#define GL_NO_ERROR                       0
+#define GL_INVALID_ENUM                   0x0500
+#define GL_INVALID_VALUE                  0x0501
+#define GL_INVALID_OPERATION              0x0502
+#define GL_OUT_OF_MEMORY                  0x0505
+
+/* FrontFaceDirection */
+#define GL_CW                             0x0900
+#define GL_CCW                            0x0901
+
+/* GetPName */
+#define GL_LINE_WIDTH                     0x0B21
+#define GL_ALIASED_POINT_SIZE_RANGE       0x846D
+#define GL_ALIASED_LINE_WIDTH_RANGE       0x846E
+#define GL_CULL_FACE_MODE                 0x0B45
+#define GL_FRONT_FACE                     0x0B46
+#define GL_DEPTH_RANGE                    0x0B70
+#define GL_DEPTH_WRITEMASK                0x0B72
+#define GL_DEPTH_CLEAR_VALUE              0x0B73
+#define GL_DEPTH_FUNC                     0x0B74
+#define GL_STENCIL_CLEAR_VALUE            0x0B91
+#define GL_STENCIL_FUNC                   0x0B92
+#define GL_STENCIL_FAIL                   0x0B94
+#define GL_STENCIL_PASS_DEPTH_FAIL        0x0B95
+#define GL_STENCIL_PASS_DEPTH_PASS        0x0B96
+#define GL_STENCIL_REF                    0x0B97
+#define GL_STENCIL_VALUE_MASK             0x0B93
+#define GL_STENCIL_WRITEMASK              0x0B98
+#define GL_STENCIL_BACK_FUNC              0x8800
+#define GL_STENCIL_BACK_FAIL              0x8801
+#define GL_STENCIL_BACK_PASS_DEPTH_FAIL   0x8802
+#define GL_STENCIL_BACK_PASS_DEPTH_PASS   0x8803
+#define GL_STENCIL_BACK_REF               0x8CA3
+#define GL_STENCIL_BACK_VALUE_MASK        0x8CA4
+#define GL_STENCIL_BACK_WRITEMASK         0x8CA5
+#define GL_VIEWPORT                       0x0BA2
+#define GL_SCISSOR_BOX                    0x0C10
+/*      GL_SCISSOR_TEST */
+#define GL_COLOR_CLEAR_VALUE              0x0C22
+#define GL_COLOR_WRITEMASK                0x0C23
+#define GL_UNPACK_ALIGNMENT               0x0CF5
+#define GL_PACK_ALIGNMENT                 0x0D05
+#define GL_MAX_TEXTURE_SIZE               0x0D33
+#define GL_MAX_VIEWPORT_DIMS              0x0D3A
+#define GL_SUBPIXEL_BITS                  0x0D50
+#define GL_RED_BITS                       0x0D52
+#define GL_GREEN_BITS                     0x0D53
+#define GL_BLUE_BITS                      0x0D54
+#define GL_ALPHA_BITS                     0x0D55
+#define GL_DEPTH_BITS                     0x0D56
+#define GL_STENCIL_BITS                   0x0D57
+#define GL_POLYGON_OFFSET_UNITS           0x2A00
+/*      GL_POLYGON_OFFSET_FILL */
+#define GL_POLYGON_OFFSET_FACTOR          0x8038
+#define GL_TEXTURE_BINDING_2D             0x8069
+#define GL_SAMPLE_BUFFERS                 0x80A8
+#define GL_SAMPLES                        0x80A9
+#define GL_SAMPLE_COVERAGE_VALUE          0x80AA
+#define GL_SAMPLE_COVERAGE_INVERT         0x80AB
+
+/* GetTextureParameter */
+/*      GL_TEXTURE_MAG_FILTER */
+/*      GL_TEXTURE_MIN_FILTER */
+/*      GL_TEXTURE_WRAP_S */
+/*      GL_TEXTURE_WRAP_T */
+
+#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2
+#define GL_COMPRESSED_TEXTURE_FORMATS     0x86A3
+
+/* HintMode */
+#define GL_DONT_CARE                      0x1100
+#define GL_FASTEST                        0x1101
+#define GL_NICEST                         0x1102
+
+/* HintTarget */
+#define GL_GENERATE_MIPMAP_HINT            0x8192
+
+/* DataType */
+#define GL_BYTE                           0x1400
+#define GL_UNSIGNED_BYTE                  0x1401
+#define GL_SHORT                          0x1402
+#define GL_UNSIGNED_SHORT                 0x1403
+#define GL_INT                            0x1404
+#define GL_UNSIGNED_INT                   0x1405
+#define GL_FLOAT                          0x1406
+#define GL_FIXED                          0x140C
+
+/* PixelFormat */
+#define GL_DEPTH_COMPONENT                0x1902
+#define GL_ALPHA                          0x1906
+#define GL_RGB                            0x1907
+#define GL_RGBA                           0x1908
+#define GL_LUMINANCE                      0x1909
+#define GL_LUMINANCE_ALPHA                0x190A
+
+/* PixelType */
+/*      GL_UNSIGNED_BYTE */
+#define GL_UNSIGNED_SHORT_4_4_4_4         0x8033
+#define GL_UNSIGNED_SHORT_5_5_5_1         0x8034
+#define GL_UNSIGNED_SHORT_5_6_5           0x8363
+
+/* Shaders */
+#define GL_FRAGMENT_SHADER                  0x8B30
+#define GL_VERTEX_SHADER                    0x8B31
+#define GL_MAX_VERTEX_ATTRIBS               0x8869
+#define GL_MAX_VERTEX_UNIFORM_VECTORS       0x8DFB
+#define GL_MAX_VARYING_VECTORS              0x8DFC
+#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D
+#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS   0x8B4C
+#define GL_MAX_TEXTURE_IMAGE_UNITS          0x8872
+#define GL_MAX_FRAGMENT_UNIFORM_VECTORS     0x8DFD
+#define GL_SHADER_TYPE                      0x8B4F
+#define GL_DELETE_STATUS                    0x8B80
+#define GL_LINK_STATUS                      0x8B82
+#define GL_VALIDATE_STATUS                  0x8B83
+#define GL_ATTACHED_SHADERS                 0x8B85
+#define GL_ACTIVE_UNIFORMS                  0x8B86
+#define GL_ACTIVE_UNIFORM_MAX_LENGTH        0x8B87
+#define GL_ACTIVE_ATTRIBUTES                0x8B89
+#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH      0x8B8A
+#define GL_SHADING_LANGUAGE_VERSION         0x8B8C
+#define GL_CURRENT_PROGRAM                  0x8B8D
+
+/* StencilFunction */
+#define GL_NEVER                          0x0200
+#define GL_LESS                           0x0201
+#define GL_EQUAL                          0x0202
+#define GL_LEQUAL                         0x0203
+#define GL_GREATER                        0x0204
+#define GL_NOTEQUAL                       0x0205
+#define GL_GEQUAL                         0x0206
+#define GL_ALWAYS                         0x0207
+
+/* StencilOp */
+/*      GL_ZERO */
+#define GL_KEEP                           0x1E00
+#define GL_REPLACE                        0x1E01
+#define GL_INCR                           0x1E02
+#define GL_DECR                           0x1E03
+#define GL_INVERT                         0x150A
+#define GL_INCR_WRAP                      0x8507
+#define GL_DECR_WRAP                      0x8508
+
+/* StringName */
+#define GL_VENDOR                         0x1F00
+#define GL_RENDERER                       0x1F01
+#define GL_VERSION                        0x1F02
+#define GL_EXTENSIONS                     0x1F03
+
+/* TextureMagFilter */
+#define GL_NEAREST                        0x2600
+#define GL_LINEAR                         0x2601
+
+/* TextureMinFilter */
+/*      GL_NEAREST */
+/*      GL_LINEAR */
+#define GL_NEAREST_MIPMAP_NEAREST         0x2700
+#define GL_LINEAR_MIPMAP_NEAREST          0x2701
+#define GL_NEAREST_MIPMAP_LINEAR          0x2702
+#define GL_LINEAR_MIPMAP_LINEAR           0x2703
+
+/* TextureParameterName */
+#define GL_TEXTURE_MAG_FILTER             0x2800
+#define GL_TEXTURE_MIN_FILTER             0x2801
+#define GL_TEXTURE_WRAP_S                 0x2802
+#define GL_TEXTURE_WRAP_T                 0x2803
+
+/* TextureTarget */
+/*      GL_TEXTURE_2D */
+#define GL_TEXTURE                        0x1702
+
+#define GL_TEXTURE_CUBE_MAP               0x8513
+#define GL_TEXTURE_BINDING_CUBE_MAP       0x8514
+#define GL_TEXTURE_CUBE_MAP_POSITIVE_X    0x8515
+#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X    0x8516
+#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y    0x8517
+#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y    0x8518
+#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z    0x8519
+#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z    0x851A
+#define GL_MAX_CUBE_MAP_TEXTURE_SIZE      0x851C
+
+/* TextureUnit */
+#define GL_TEXTURE0                       0x84C0
+#define GL_TEXTURE1                       0x84C1
+#define GL_TEXTURE2                       0x84C2
+#define GL_TEXTURE3                       0x84C3
+#define GL_TEXTURE4                       0x84C4
+#define GL_TEXTURE5                       0x84C5
+#define GL_TEXTURE6                       0x84C6
+#define GL_TEXTURE7                       0x84C7
+#define GL_TEXTURE8                       0x84C8
+#define GL_TEXTURE9                       0x84C9
+#define GL_TEXTURE10                      0x84CA
+#define GL_TEXTURE11                      0x84CB
+#define GL_TEXTURE12                      0x84CC
+#define GL_TEXTURE13                      0x84CD
+#define GL_TEXTURE14                      0x84CE
+#define GL_TEXTURE15                      0x84CF
+#define GL_TEXTURE16                      0x84D0
+#define GL_TEXTURE17                      0x84D1
+#define GL_TEXTURE18                      0x84D2
+#define GL_TEXTURE19                      0x84D3
+#define GL_TEXTURE20                      0x84D4
+#define GL_TEXTURE21                      0x84D5
+#define GL_TEXTURE22                      0x84D6
+#define GL_TEXTURE23                      0x84D7
+#define GL_TEXTURE24                      0x84D8
+#define GL_TEXTURE25                      0x84D9
+#define GL_TEXTURE26                      0x84DA
+#define GL_TEXTURE27                      0x84DB
+#define GL_TEXTURE28                      0x84DC
+#define GL_TEXTURE29                      0x84DD
+#define GL_TEXTURE30                      0x84DE
+#define GL_TEXTURE31                      0x84DF
+#define GL_ACTIVE_TEXTURE                 0x84E0
+
+/* TextureWrapMode */
+#define GL_REPEAT                         0x2901
+#define GL_CLAMP_TO_EDGE                  0x812F
+#define GL_MIRRORED_REPEAT                0x8370
+
+/* Uniform Types */
+#define GL_FLOAT_VEC2                     0x8B50
+#define GL_FLOAT_VEC3                     0x8B51
+#define GL_FLOAT_VEC4                     0x8B52
+#define GL_INT_VEC2                       0x8B53
+#define GL_INT_VEC3                       0x8B54
+#define GL_INT_VEC4                       0x8B55
+#define GL_BOOL                           0x8B56
+#define GL_BOOL_VEC2                      0x8B57
+#define GL_BOOL_VEC3                      0x8B58
+#define GL_BOOL_VEC4                      0x8B59
+#define GL_FLOAT_MAT2                     0x8B5A
+#define GL_FLOAT_MAT3                     0x8B5B
+#define GL_FLOAT_MAT4                     0x8B5C
+#define GL_SAMPLER_2D                     0x8B5E
+#define GL_SAMPLER_CUBE                   0x8B60
+
+/* Vertex Arrays */
+#define GL_VERTEX_ATTRIB_ARRAY_ENABLED        0x8622
+#define GL_VERTEX_ATTRIB_ARRAY_SIZE           0x8623
+#define GL_VERTEX_ATTRIB_ARRAY_STRIDE         0x8624
+#define GL_VERTEX_ATTRIB_ARRAY_TYPE           0x8625
+#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED     0x886A
+#define GL_VERTEX_ATTRIB_ARRAY_POINTER        0x8645
+#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F
+
+/* Read Format */
+#define GL_IMPLEMENTATION_COLOR_READ_TYPE   0x8B9A
+#define GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B
+
+/* Shader Source */
+#define GL_COMPILE_STATUS                 0x8B81
+#define GL_INFO_LOG_LENGTH                0x8B84
+#define GL_SHADER_SOURCE_LENGTH           0x8B88
+#define GL_SHADER_COMPILER                0x8DFA
+
+/* Shader Binary */
+#define GL_SHADER_BINARY_FORMATS          0x8DF8
+#define GL_NUM_SHADER_BINARY_FORMATS      0x8DF9
+
+/* Shader Precision-Specified Types */
+#define GL_LOW_FLOAT                      0x8DF0
+#define GL_MEDIUM_FLOAT                   0x8DF1
+#define GL_HIGH_FLOAT                     0x8DF2
+#define GL_LOW_INT                        0x8DF3
+#define GL_MEDIUM_INT                     0x8DF4
+#define GL_HIGH_INT                       0x8DF5
+
+/* Framebuffer Object. */
+#define GL_FRAMEBUFFER                    0x8D40
+#define GL_RENDERBUFFER                   0x8D41
+
+#define GL_RGBA4                          0x8056
+#define GL_RGB5_A1                        0x8057
+#define GL_RGB565                         0x8D62
+#define GL_DEPTH_COMPONENT16              0x81A5
+#define GL_STENCIL_INDEX8                 0x8D48
+
+#define GL_RENDERBUFFER_WIDTH             0x8D42
+#define GL_RENDERBUFFER_HEIGHT            0x8D43
+#define GL_RENDERBUFFER_INTERNAL_FORMAT   0x8D44
+#define GL_RENDERBUFFER_RED_SIZE          0x8D50
+#define GL_RENDERBUFFER_GREEN_SIZE        0x8D51
+#define GL_RENDERBUFFER_BLUE_SIZE         0x8D52
+#define GL_RENDERBUFFER_ALPHA_SIZE        0x8D53
+#define GL_RENDERBUFFER_DEPTH_SIZE        0x8D54
+#define GL_RENDERBUFFER_STENCIL_SIZE      0x8D55
+
+#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE           0x8CD0
+#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME           0x8CD1
+#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL         0x8CD2
+#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3
+
+#define GL_COLOR_ATTACHMENT0              0x8CE0
+#define GL_DEPTH_ATTACHMENT               0x8D00
+#define GL_STENCIL_ATTACHMENT             0x8D20
+
+#define GL_NONE                           0
+
+#define GL_FRAMEBUFFER_COMPLETE                      0x8CD5
+#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT         0x8CD6
+#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7
+#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS         0x8CD9
+#define GL_FRAMEBUFFER_UNSUPPORTED                   0x8CDD
+
+#define GL_FRAMEBUFFER_BINDING            0x8CA6
+#define GL_RENDERBUFFER_BINDING           0x8CA7
+#define GL_MAX_RENDERBUFFER_SIZE          0x84E8
+
+#define GL_INVALID_FRAMEBUFFER_OPERATION  0x0506
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __COGL_GLES2_TYPES_H_ */
diff --git a/cogl/cogl-gles2.h b/cogl/cogl-gles2.h
new file mode 100644
index 0000000..56ff64d
--- /dev/null
+++ b/cogl/cogl-gles2.h
@@ -0,0 +1,368 @@
+/*
+ * Cogl
+ *
+ * An object oriented GL/GLES Abstraction/Utility Layer
+ *
+ * Copyright (C) 2011 Collabora Ltd.
+ * Copyright (C) 2012 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:
+ *  Tomeu Vizoso <tomeu.vizoso at collabora.com>
+ *  Robert Bragg <robert at linux.intel.com>
+ *
+ */
+
+#ifndef __COGL_GLES2_H__
+#define __COGL_GLES2_H__
+
+#define __COGL_H_INSIDE__
+#include <cogl/cogl-defines.h>
+#include <cogl/cogl-context.h>
+#include <cogl/cogl-framebuffer.h>
+#include <cogl/cogl-texture.h>
+#include <cogl/cogl-texture-2d.h>
+
+/* CoglGLES2Vtable depends on GLES 2.0 typedefs being available but we
+ * want to be careful that the public api doesn't expose arbitrary
+ * system GL headers as part of the Cogl API so although when building
+ * internally we consistently refer to the system headers to avoid
+ * conflicts we only expose the minimal set of GLES 2.0 types and enums
+ * publicly.
+ */
+#ifdef CLUTTER_COMPILATION
+#include "cogl-gl-header.h"
+#else
+#include <cogl/cogl-gles2-types.h>
+#endif
+
+G_BEGIN_DECLS
+
+/**
+ * SECTION:cogl-gles2
+ * @short_description: A portable api to access OpenGLES 2.0
+ *
+ * Cogl provides portable access to the OpenGLES api through a single
+ * library that is able to smooth over inconsistencies between the
+ * different vendor drivers for OpenGLES in a single place.
+ *
+ * The api is designed to allow Cogl to transparently implement the
+ * api on top of other drivers, such as OpenGL, D3D or on Cogl's own
+ * drawing api so even if your platform doesn't come with an
+ * OpenGLES 2.0 api Cogl may still be able to expose the api to your
+ * application.
+ *
+ * Since Cogl is a library and not an api specification it is possible
+ * to add OpenGLES 2.0 api features to Cogl which can immidiately
+ * benefit developers regardless of what platform they are running on.
+ *
+ * With this api it's possible to re-use existing OpenGLES 2.0 code
+ * within applications that are rendering with the Cogl API and also
+ * it's possible for applications that render using OpenGLES 2.0 to
+ * incorporate content rendered with Cogl.
+ *
+ * Applications can check for OpenGLES 2.0 api support by checking for
+ * %COGL_FEATURE_ID_GLES2_CONTEXT support with cogl_has_feature().
+ *
+ * Since: 1.12
+ * Stability: unstable
+ */
+
+/**
+ * CoglGLES2Context:
+ *
+ * Represents an OpenGLES 2.0 api context used as a sandbox for
+ * OpenGLES 2.0 state. This is comparable to an EGLContext for those
+ * who have used OpenGLES 2.0 with EGL before.
+ *
+ * Since: 1.12
+ * Stability: unstable
+ */
+typedef struct _CoglGLES2Context CoglGLES2Context;
+
+/**
+ * CoglGLES2Vtable:
+ *
+ * Provides function pointers for the full OpenGLES 2.0 api. The
+ * api must be accessed this way and not by directly calling
+ * symbols of any system OpenGLES 2.0 api.
+ *
+ * Since: 1.12
+ * Stability: unstable
+ */
+typedef struct _CoglGLES2Vtable CoglGLES2Vtable;
+
+struct _CoglGLES2Vtable
+{
+#define COGL_EXT_BEGIN(name, \
+                       min_gl_major, min_gl_minor, \
+                       gles_availability, \
+                       extension_suffixes, extension_names)
+
+#define COGL_EXT_FUNCTION(ret, name, args) \
+  ret (* name) args;
+
+#define COGL_EXT_END()
+
+#include <cogl/gl-prototypes/cogl-gles2-functions.h>
+
+#undef COGL_EXT_BEGIN
+#undef COGL_EXT_FUNCTION
+#undef COGL_EXT_END
+};
+
+GQuark
+_cogl_gles2_context_error_quark (void);
+
+/**
+ * COGL_GLES2_CONTEXT_ERROR:
+ *
+ * An error domain for runtime exceptions relating to the
+ * cogl_gles2_context api.
+ *
+ * Since: 2.0
+ * Stability: unstable
+ */
+#define COGL_GLES2_CONTEXT_ERROR (_cogl_gles2_context_error_quark ())
+
+/**
+ * CoglGLES2ContextError:
+ * @COGL_GLES2_CONTEXT_ERROR_UNSUPPORTED: Creating GLES2 contexts
+ *    isn't supported. Applications should use cogl_has_feature() to
+ *    check for the %COGL_FEATURE_ID_GLES2_CONTEXT.
+ * @COGL_GLES2_CONTEXT_ERROR_DRIVER: An underlying driver error
+ *    occured.
+ *
+ * Error codes that relate to the cogl_gles2_context api.
+ */
+typedef enum { /*< prefix=COGL_GLES2_CONTEXT_ERROR >*/
+  COGL_GLES2_CONTEXT_ERROR_UNSUPPORTED,
+  COGL_GLES2_CONTEXT_ERROR_DRIVER
+} CoglGLES2ContextError;
+
+/**
+ * cogl_gles2_context_new:
+ * @ctx: A #CoglContext
+ * @error: A pointer to a #GError for returning exceptions
+ *
+ * Allocates a new OpenGLES 2.0 context that can be used to render to
+ * #CoglOffscreen framebuffers (Rendering to #CoglOnscreen
+ * framebuffers is not currently supported).
+ *
+ * To actually access the OpenGLES 2.0 api itself you need to use
+ * cogl_gles2_context_get_vtable(). You should not try to directly link
+ * to and use the symbols provided by the a system OpenGLES 2.0
+ * driver.
+ *
+ * Once you have allocated an OpenGLES 2.0 context you can make it
+ * current using cogl_push_gles2_context(). For those familiar with
+ * using the EGL api, this serves a similar purpose to eglMakeCurrent.
+ *
+ * <note>Before using this api applications can check for OpenGLES 2.0
+ * api support by checking for %COGL_FEATURE_ID_GLES2_CONTEXT support
+ * with cogl_has_feature(). This function will return %FALSE and
+ * return an %COGL_GLES2_CONTEXT_ERROR_UNSUPPORTED error if the
+ * feature isn't available.</note>
+ *
+ * Since: 2.0
+ * Return value: A newly allocated #CoglGLES2Context or %NULL if there
+ *               was an error and @error will be updated in that case.
+ * Stability: unstable
+ */
+CoglGLES2Context *
+cogl_gles2_context_new (CoglContext *ctx, GError **error);
+
+/**
+ * cogl_gles2_context_get_vtable:
+ * @gles2_ctx: A #CoglGLES2Context allocated with
+ *             cogl_gles2_context_new()
+ *
+ * Queries the OpenGLES 2.0 api function pointers that should be
+ * used for rendering with the given @gles2_ctx.
+ *
+ * <note>You should not try to directly link to and use the symbols
+ * provided by any system OpenGLES 2.0 driver.</note>
+ *
+ * Since: 2.0
+ * Return value: A pointer to a #CoglGLES2Vtable providing pointers
+ *               to functions for the full OpenGLES 2.0 api.
+ * Stability: unstable
+ */
+const CoglGLES2Vtable *
+cogl_gles2_context_get_vtable (CoglGLES2Context *gles2_ctx);
+
+/**
+ * cogl_push_gles2_context:
+ * @ctx: A #CoglContext
+ * @gles2_ctx: A #CoglGLES2Context allocated with
+ *             cogl_gles2_context_new()
+ * @read_buffer: A #CoglFramebuffer to access to read operations
+ *               such as glReadPixels. (must be a #CoglOffscreen
+ *               framebuffer currently)
+ * @write_buffer: A #CoglFramebuffer to access for drawing operations
+ *                such as glDrawArrays. (must be a #CoglOffscreen
+ *               framebuffer currently)
+ * @error: A pointer to a #GError for returning exceptions
+ *
+ * Pushes the given @gles2_ctx onto a stack associated with @ctx so
+ * that the OpenGLES 2.0 api can be used instead of the Cogl
+ * rendering apis to read and write to the specified framebuffers.
+ *
+ * Usage of the api available through a #CoglGLES2Vtable is only
+ * allowed between cogl_push_gles2_context() and
+ * cogl_pop_gles2_context() calls.
+ *
+ * If there is a runtime problem with switching over to the given
+ * @gles2_ctx then this function will return %FALSE and return
+ * an error through @error.
+ *
+ * Since: 2.0
+ * Return value: %TRUE if operation was successfull or %FALSE
+ *               otherwise and @error will be updated.
+ * Stability: unstable
+ */
+CoglBool
+cogl_push_gles2_context (CoglContext *ctx,
+                         CoglGLES2Context *gles2_ctx,
+                         CoglFramebuffer *read_buffer,
+                         CoglFramebuffer *write_buffer,
+                         GError **error);
+
+/**
+ * cogl_pop_gles2_context:
+ * @ctx: A #CoglContext
+ *
+ * Restores the previously active #CoglGLES2Context if there
+ * were nested calls to cogl_push_gles2_context() or otherwise
+ * restores the ability to render with the Cogl api instead
+ * of OpenGLES 2.0.
+ *
+ * The behaviour is undefined if calls to cogl_pop_gles2_context()
+ * are not balenced with the number of corresponding calls to
+ * cogl_push_gles2_context().
+ *
+ * Since: 2.0
+ * Stability: unstable
+ */
+void
+cogl_pop_gles2_context (CoglContext *ctx);
+
+/**
+ * cogl_gles2_get_current_vtable:
+ *
+ * Returns the OpenGL ES 2.0 api vtable for the currently pushed
+ * #CoglGLES2Context (last pushed with cogl_push_gles2_context()) or
+ * %NULL if no #CoglGLES2Context has been pushed.
+ *
+ * Return value: The #CoglGLES2Vtable for the currently pushed
+ *               #CoglGLES2Context or %NULL if none has been pushed.
+ * Since: 2.0
+ * Stability: unstable
+ */
+CoglGLES2Vtable *
+cogl_gles2_get_current_vtable (void);
+
+/**
+ * cogl_gles2_texture_2d_new_from_handle:
+ * @ctx: A #CoglContext
+ * @gles2_ctx: A #CoglGLES2Context allocated with
+ *             cogl_gles2_context_new()
+ * @handle: An OpenGL ES 2.0 texture handle created with
+ *          glGenTextures()
+ *
+ * Creates a #CoglTexture2D from an OpenGL ES 2.0 texture handle that
+ * was created within the given @gles2_ctx via glGenTextures(). The
+ * texture needs to have been associated with the GL_TEXTURE_2D target.
+ *
+ * <note>This interface is only intended for sharing textures to read
+ * from.  The behaviour is undefined if the texture is modified using
+ * the Cogl api.</note>
+ *
+ * <note>Applications should only pass this function handles that were
+ * created via a #CoglGLES2Vtable or via libcogl-gles2 and not pass
+ * handles created directly using the system's native libGLESv2
+ * api.</note>
+ *
+ * Since: 2.0
+ * Stability: unstable
+ */
+CoglTexture2D *
+cogl_gles2_texture_2d_new_from_handle (CoglContext *ctx,
+                                       CoglGLES2Context *gles2_ctx,
+                                       unsigned int handle,
+                                       int width,
+                                       int height,
+                                       CoglPixelFormat internal_format,
+                                       GError **error);
+
+/**
+ * cogl_gles2_texture_get_handle:
+ * @handle: A return location for an OpenGL ES 2.0 texture handle
+ * @target: A return location for an OpenGL ES 2.0 texture target
+ *
+ * Gets an OpenGL ES 2.0 texture handle for a #CoglTexture that can
+ * then be referenced by a #CoglGLES2Context. As well as returning
+ * a texture handle the texture's target (such as GL_TEXTURE_2D) is
+ * also returned.
+ *
+ * If the #CoglTexture can not be shared with a #CoglGLES2Context then
+ * this function will return %FALSE.
+ *
+ * This api does not affect the lifetime of the CoglTexture and you
+ * must take care not to reference the returned handle after the
+ * original texture has been freed.
+ *
+ * <note>This interface is only intended for sharing textures to read
+ * from.  The behaviour is undefined if the texture is modified by a
+ * GLES2 context.</note>
+ *
+ * <note>This function will only return %TRUE for low-level
+ * #CoglTexture<!-- -->s such as #CoglTexture2D or #CoglTexture3D but
+ * not for high level meta textures such as
+ * #CoglTexture2DSliced</note>
+ *
+ * <note>The handle returned should not be passed directly to a system
+ * OpenGL ES 2.0 library, the handle is only intended to be used via
+ * a #CoglGLES2Vtable or via libcogl-gles2.</note>
+ *
+ * Return value: %TRUE if a handle and target could be returned
+ *               otherwise %FALSE is returned.
+ * Since: 2.0
+ * Stability: unstable
+ */
+CoglBool
+cogl_gles2_texture_get_handle (CoglTexture *texture,
+                               unsigned int *handle,
+                               unsigned int *target);
+
+/**
+ * cogl_is_gles2_context:
+ * @object: A #CoglObject pointer
+ *
+ * Gets whether the given object references a #CoglGLES2Context.
+ *
+ * Return value: %TRUE if the object references a #CoglGLES2Context
+ *   and %FALSE otherwise.
+ * Since: 2.0
+ * Stability: unstable
+ */
+CoglBool
+cogl_is_gles2_context (void *object);
+
+G_END_DECLS
+
+#endif /* __COGL_GLES2_H__ */
+
diff --git a/cogl/cogl.h b/cogl/cogl.h
index 7ce7e32..f2c915b 100644
--- a/cogl/cogl.h
+++ b/cogl/cogl.h
@@ -102,7 +102,7 @@
 #include <cogl/cogl-kms-renderer.h>
 #include <cogl/cogl-kms-display.h>
 #endif
-#if COGL_HAS_WIN32_SUPPORT
+#ifdef COGL_HAS_WIN32_SUPPORT
 #include <cogl/cogl-win32-renderer.h>
 #endif
 #ifdef COGL_HAS_GLIB_SUPPORT
diff --git a/cogl/winsys/cogl-winsys-egl-android.c b/cogl/winsys/cogl-winsys-egl-android.c
index dd96ccf..269d65d 100644
--- a/cogl/winsys/cogl-winsys-egl-android.c
+++ b/cogl/winsys/cogl-winsys-egl-android.c
@@ -132,10 +132,10 @@ _cogl_winsys_egl_context_created (CoglDisplay *display,
       goto fail;
     }
 
-  if (!eglMakeCurrent (egl_renderer->edpy,
-                       egl_display->egl_surface,
-                       egl_display->egl_surface,
-                       egl_display->egl_context))
+  if (!_cogl_winsys_egl_make_current (display,
+                                      egl_display->egl_surface,
+                                      egl_display->egl_surface,
+                                      egl_display->egl_context))
     {
       error_message = "Unable to eglMakeCurrent with egl surface";
       goto fail;
diff --git a/cogl/winsys/cogl-winsys-egl-gdl.c b/cogl/winsys/cogl-winsys-egl-gdl.c
index fbaad74..8d57904 100644
--- a/cogl/winsys/cogl-winsys-egl-gdl.c
+++ b/cogl/winsys/cogl-winsys-egl-gdl.c
@@ -140,10 +140,10 @@ _cogl_winsys_egl_context_created (CoglDisplay *display,
       goto fail;
     }
 
-  if (!eglMakeCurrent (egl_renderer->edpy,
-                       egl_display->egl_surface,
-                       egl_display->egl_surface,
-                       egl_display->egl_context))
+  if (!_cogl_winsys_egl_make_current (display,
+                                      egl_display->egl_surface,
+                                      egl_display->egl_surface,
+                                      egl_display->egl_context))
     {
       error_message = "Unable to eglMakeCurrent with egl surface";
       goto fail;
diff --git a/cogl/winsys/cogl-winsys-egl-kms.c b/cogl/winsys/cogl-winsys-egl-kms.c
index c2ad357..8ff03c4 100644
--- a/cogl/winsys/cogl-winsys-egl-kms.c
+++ b/cogl/winsys/cogl-winsys-egl-kms.c
@@ -541,10 +541,10 @@ _cogl_winsys_egl_context_created (CoglDisplay *display,
   CoglRendererEGL *egl_renderer = renderer->winsys;
   CoglDisplayEGL *egl_display = display->winsys;
 
-  if (!eglMakeCurrent (egl_renderer->edpy,
-                       EGL_NO_SURFACE,
-                       EGL_NO_SURFACE,
-                       egl_display->egl_context))
+  if (!_cogl_winsys_egl_make_current (display,
+                                      EGL_NO_SURFACE,
+                                      EGL_NO_SURFACE,
+                                      egl_display->egl_context))
     {
       g_set_error (error, COGL_WINSYS_ERROR,
                    COGL_WINSYS_ERROR_CREATE_CONTEXT,
diff --git a/cogl/winsys/cogl-winsys-egl-null.c b/cogl/winsys/cogl-winsys-egl-null.c
index 20f209f..65da170 100644
--- a/cogl/winsys/cogl-winsys-egl-null.c
+++ b/cogl/winsys/cogl-winsys-egl-null.c
@@ -98,10 +98,10 @@ _cogl_winsys_egl_context_created (CoglDisplay *display,
       goto fail;
     }
 
-  if (!eglMakeCurrent (egl_renderer->edpy,
-                       egl_display->egl_surface,
-                       egl_display->egl_surface,
-                       egl_display->egl_context))
+  if (!_cogl_winsys_egl_make_current (display,
+                                      egl_display->egl_surface,
+                                      egl_display->egl_surface,
+                                      egl_display->egl_context))
     {
       error_message = "Unable to eglMakeCurrent with egl surface";
       goto fail;
diff --git a/cogl/winsys/cogl-winsys-egl-private.h b/cogl/winsys/cogl-winsys-egl-private.h
index 000fe51..27274bb 100644
--- a/cogl/winsys/cogl-winsys-egl-private.h
+++ b/cogl/winsys/cogl-winsys-egl-private.h
@@ -113,13 +113,18 @@ typedef struct _CoglDisplayEGL
   CoglBool found_egl_config;
   CoglBool stencil_disabled;
 
+  EGLSurface current_read_surface;
+  EGLSurface current_draw_surface;
+  EGLContext current_context;
+
   /* Platform specific display data */
   void *platform;
 } CoglDisplayEGL;
 
 typedef struct _CoglContextEGL
 {
-  EGLSurface current_surface;
+  EGLSurface saved_draw_surface;
+  EGLSurface saved_read_surface;
 } CoglContextEGL;
 
 typedef struct _CoglOnscreenEGL
@@ -133,6 +138,12 @@ typedef struct _CoglOnscreenEGL
 const CoglWinsysVtable *
 _cogl_winsys_egl_get_vtable (void);
 
+EGLBoolean
+_cogl_winsys_egl_make_current (CoglDisplay *display,
+                               EGLSurface draw,
+                               EGLSurface read,
+                               EGLContext context);
+
 #ifdef EGL_KHR_image_base
 EGLImageKHR
 _cogl_egl_create_image (CoglContext *ctx,
diff --git a/cogl/winsys/cogl-winsys-egl-wayland.c b/cogl/winsys/cogl-winsys-egl-wayland.c
index cabe632..c1dec24 100644
--- a/cogl/winsys/cogl-winsys-egl-wayland.c
+++ b/cogl/winsys/cogl-winsys-egl-wayland.c
@@ -231,14 +231,14 @@ _cogl_winsys_egl_context_created (CoglDisplay *display,
                             NULL);
   if (egl_display->dummy_surface == EGL_NO_SURFACE)
     {
-      error_message= "Unable to eglMakeCurrent with dummy surface";
+      error_message= "Unable to create dummy window surface";
       goto fail;
     }
 
-  if (!eglMakeCurrent (egl_renderer->edpy,
-                       egl_display->dummy_surface,
-                       egl_display->dummy_surface,
-                       egl_display->egl_context))
+  if (!_cogl_winsys_egl_make_current (display,
+                                      egl_display->dummy_surface,
+                                      egl_display->dummy_surface,
+                                      egl_display->egl_context))
     {
       error_message = "Unable to eglMakeCurrent with dummy surface";
       goto fail;
diff --git a/cogl/winsys/cogl-winsys-egl-x11.c b/cogl/winsys/cogl-winsys-egl-x11.c
index 8b4551c..94a9410 100644
--- a/cogl/winsys/cogl-winsys-egl-x11.c
+++ b/cogl/winsys/cogl-winsys-egl-x11.c
@@ -505,10 +505,10 @@ _cogl_winsys_egl_context_created (CoglDisplay *display,
       goto fail;
     }
 
-  if (!eglMakeCurrent (egl_renderer->edpy,
-                       egl_display->dummy_surface,
-                       egl_display->dummy_surface,
-                       egl_display->egl_context))
+  if (!_cogl_winsys_egl_make_current (display,
+                                      egl_display->dummy_surface,
+                                      egl_display->dummy_surface,
+                                      egl_display->egl_context))
     {
       error_message = "Unable to eglMakeCurrent with dummy surface";
       goto fail;
diff --git a/cogl/winsys/cogl-winsys-egl.c b/cogl/winsys/cogl-winsys-egl.c
index e0e8bb5..b2413ad 100644
--- a/cogl/winsys/cogl-winsys-egl.c
+++ b/cogl/winsys/cogl-winsys-egl.c
@@ -38,6 +38,7 @@
 #include "cogl-swap-chain-private.h"
 #include "cogl-renderer-private.h"
 #include "cogl-onscreen-template-private.h"
+#include "cogl-gles2-context-private.h"
 
 #include "cogl-private.h"
 
@@ -82,6 +83,39 @@ static const CoglFeatureData winsys_feature_data[] =
 #include "cogl-winsys-egl-feature-functions.h"
   };
 
+static const char *
+get_error_string (void)
+{
+  switch (eglGetError()){
+  case EGL_BAD_DISPLAY:
+    return "Invalid display";
+  case EGL_NOT_INITIALIZED:
+    return "Display not initialized";
+  case EGL_BAD_ALLOC:
+    return "Not enough resources to allocate context";
+  case EGL_BAD_ATTRIBUTE:
+    return "Invalid attribute";
+  case EGL_BAD_CONFIG:
+    return "Invalid config";
+  case EGL_BAD_CONTEXT:
+    return "Invalid context";
+  case EGL_BAD_CURRENT_SURFACE:
+     return "Invalid current surface";
+  case EGL_BAD_MATCH:
+     return "Bad match";
+  case EGL_BAD_NATIVE_PIXMAP:
+     return "Invalid native pixmap";
+  case EGL_BAD_NATIVE_WINDOW:
+     return "Invalid native window";
+  case EGL_BAD_PARAMETER:
+     return "Invalid parameter";
+  case EGL_BAD_SURFACE:
+     return "Invalid surface";
+  default:
+    g_assert_not_reached ();
+  }
+}
+
 static CoglFuncPtr
 _cogl_winsys_renderer_get_proc_address (CoglRenderer *renderer,
                                         const char *name)
@@ -289,6 +323,33 @@ fail:
   return FALSE;
 }
 
+EGLBoolean
+_cogl_winsys_egl_make_current (CoglDisplay *display,
+                               EGLSurface draw,
+                               EGLSurface read,
+                               EGLContext context)
+{
+  CoglDisplayEGL *egl_display = display->winsys;
+  CoglRendererEGL *egl_renderer = display->renderer->winsys;
+  EGLBoolean ret;
+
+  if (egl_display->current_draw_surface == draw &&
+      egl_display->current_read_surface == read &&
+      egl_display->current_context == context)
+  return EGL_TRUE;
+
+  ret = eglMakeCurrent (egl_renderer->edpy,
+                        draw,
+                        read,
+                        context);
+
+  egl_display->current_draw_surface = draw;
+  egl_display->current_read_surface = read;
+  egl_display->current_context = context;
+
+  return ret;
+}
+
 static void
 cleanup_context (CoglDisplay *display)
 {
@@ -298,8 +359,9 @@ cleanup_context (CoglDisplay *display)
 
   if (egl_display->egl_context != EGL_NO_CONTEXT)
     {
-      eglMakeCurrent (egl_renderer->edpy, EGL_NO_SURFACE, EGL_NO_SURFACE,
-                      EGL_NO_CONTEXT);
+      _cogl_winsys_egl_make_current (display,
+                                     EGL_NO_SURFACE, EGL_NO_SURFACE,
+                                     EGL_NO_CONTEXT);
       eglDestroyContext (egl_renderer->edpy, egl_display->egl_context);
       egl_display->egl_context = EGL_NO_CONTEXT;
     }
@@ -416,6 +478,13 @@ _cogl_winsys_context_init (CoglContext *context, GError **error)
                       COGL_WINSYS_FEATURE_SWAP_REGION_THROTTLE, TRUE);
     }
 
+  /* NB: We currently only support creating standalone GLES2 contexts
+   * for offscreen rendering and so we need a dummy (non-visible)
+   * surface to be able to bind those contexts */
+  if (egl_display->dummy_surface != EGL_NO_SURFACE)
+    COGL_FLAGS_SET (context->features,
+                    COGL_FEATURE_ID_GLES2_CONTEXT, TRUE);
+
   if (egl_renderer->platform_vtable->context_init &&
       !egl_renderer->platform_vtable->context_init (context, error))
     return FALSE;
@@ -435,6 +504,54 @@ _cogl_winsys_context_deinit (CoglContext *context)
   g_free (context->winsys);
 }
 
+typedef struct _CoglGLES2ContextEGL
+{
+  EGLContext egl_context;
+  EGLSurface dummy_surface;
+} CoglGLES2ContextEGL;
+
+static void *
+_cogl_winsys_context_create_gles2_context (CoglContext *ctx, GError **error)
+{
+  CoglRendererEGL *egl_renderer = ctx->display->renderer->winsys;
+  CoglDisplayEGL *egl_display = ctx->display->winsys;
+  EGLint attribs[3];
+  EGLContext egl_context;
+
+  attribs[0] = EGL_CONTEXT_CLIENT_VERSION;
+  attribs[1] = 2;
+  attribs[2] = EGL_NONE;
+
+  egl_context = eglCreateContext (egl_renderer->edpy,
+                                  egl_display->egl_config,
+                                  egl_display->egl_context,
+                                  attribs);
+  if (egl_context == EGL_NO_CONTEXT)
+    {
+      g_set_error (error, COGL_WINSYS_ERROR,
+                   COGL_WINSYS_ERROR_CREATE_GLES2_CONTEXT,
+                   "%s", get_error_string ());
+      return NULL;
+    }
+
+  return (void *)egl_context;
+}
+
+static void
+_cogl_winsys_destroy_gles2_context (CoglGLES2Context *gles2_ctx)
+{
+  CoglContext *context = gles2_ctx->context;
+  CoglDisplay *display = context->display;
+  CoglDisplayEGL *egl_display = display->winsys;
+  CoglRenderer *renderer = display->renderer;
+  CoglRendererEGL *egl_renderer = renderer->winsys;
+  EGLContext egl_context = gles2_ctx->winsys;
+
+  _COGL_RETURN_IF_FAIL (egl_display->current_context != egl_context);
+
+  eglDestroyContext (egl_renderer->edpy, egl_context);
+}
+
 static CoglBool
 _cogl_winsys_onscreen_init (CoglOnscreen *onscreen,
                             GError **error)
@@ -524,30 +641,36 @@ _cogl_winsys_onscreen_deinit (CoglOnscreen *onscreen)
   onscreen->winsys = NULL;
 }
 
-static void
-_cogl_winsys_onscreen_bind (CoglOnscreen *onscreen)
+static CoglBool
+bind_onscreen (CoglOnscreen *onscreen)
 {
   CoglFramebuffer *fb = COGL_FRAMEBUFFER (onscreen);
   CoglContext *context = fb->context;
   CoglDisplayEGL *egl_display = context->display->winsys;
-  CoglRenderer *renderer = context->display->renderer;
-  CoglRendererEGL *egl_renderer = renderer->winsys;
   CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
-  CoglContextEGL *egl_context = context->winsys;
 
-  if (egl_context->current_surface == egl_onscreen->egl_surface)
-    return;
+  CoglBool status = _cogl_winsys_egl_make_current (context->display,
+                                                   egl_onscreen->egl_surface,
+                                                   egl_onscreen->egl_surface,
+                                                   egl_display->egl_context);
+  if (status)
+    {
+      CoglRenderer *renderer = context->display->renderer;
+      CoglRendererEGL *egl_renderer = renderer->winsys;
+
+      if (fb->config.swap_throttled)
+        eglSwapInterval (egl_renderer->edpy, 1);
+      else
+        eglSwapInterval (egl_renderer->edpy, 0);
+    }
 
-  eglMakeCurrent (egl_renderer->edpy,
-                  egl_onscreen->egl_surface,
-                  egl_onscreen->egl_surface,
-                  egl_display->egl_context);
-  egl_context->current_surface = egl_onscreen->egl_surface;
+  return status;
+}
 
-  if (fb->config.swap_throttled)
-    eglSwapInterval (egl_renderer->edpy, 1);
-  else
-    eglSwapInterval (egl_renderer->edpy, 0);
+static void
+_cogl_winsys_onscreen_bind (CoglOnscreen *onscreen)
+{
+  bind_onscreen (onscreen);
 }
 
 static void
@@ -613,13 +736,13 @@ static void
 _cogl_winsys_onscreen_update_swap_throttled (CoglOnscreen *onscreen)
 {
   CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context;
-  CoglContextEGL *egl_context = context->winsys;
+  CoglDisplayEGL *egl_display = context->display->winsys;
   CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
 
-  if (egl_context->current_surface != egl_onscreen->egl_surface)
+  if (egl_display->current_draw_surface != egl_onscreen->egl_surface)
     return;
 
-  egl_context->current_surface = EGL_NO_SURFACE;
+  egl_display->current_draw_surface = EGL_NO_SURFACE;
 
   _cogl_winsys_onscreen_bind (onscreen);
 }
@@ -632,6 +755,58 @@ _cogl_winsys_context_egl_get_egl_display (CoglContext *context)
   return egl_renderer->edpy;
 }
 
+static void
+_cogl_winsys_save_context (CoglContext *ctx)
+{
+  CoglContextEGL *egl_context = ctx->winsys;
+  CoglDisplayEGL *egl_display = ctx->display->winsys;
+
+  egl_context->saved_draw_surface = egl_display->current_draw_surface;
+  egl_context->saved_read_surface = egl_display->current_read_surface;
+}
+
+static CoglBool
+_cogl_winsys_set_gles2_context (CoglGLES2Context *gles2_ctx, GError **error)
+{
+  CoglContext *ctx = gles2_ctx->context;
+  CoglDisplayEGL *egl_display = ctx->display->winsys;
+  CoglBool status;
+
+  if (gles2_ctx->write_buffer &&
+      cogl_is_onscreen (gles2_ctx->write_buffer))
+    {
+      status = bind_onscreen (COGL_ONSCREEN (gles2_ctx->write_buffer));
+    }
+  else
+    status = _cogl_winsys_egl_make_current (ctx->display,
+                                            egl_display->dummy_surface,
+                                            egl_display->dummy_surface,
+                                            gles2_ctx->winsys);
+
+  if (!status)
+    {
+      g_set_error (error,
+                   COGL_WINSYS_ERROR,
+                   COGL_WINSYS_ERROR_MAKE_CURRENT,
+                   "Failed to make gles2 context current");
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static void
+_cogl_winsys_restore_context (CoglContext *ctx)
+{
+  CoglContextEGL *egl_context = ctx->winsys;
+  CoglDisplayEGL *egl_display = ctx->display->winsys;
+
+  _cogl_winsys_egl_make_current (ctx->display,
+                                 egl_context->saved_draw_surface,
+                                 egl_context->saved_read_surface,
+                                 egl_display->egl_context);
+}
+
 static CoglWinsysVtable _cogl_winsys_vtable =
   {
     .constraints = COGL_RENDERER_CONSTRAINT_USES_EGL,
@@ -648,6 +823,9 @@ static CoglWinsysVtable _cogl_winsys_vtable =
     .context_deinit = _cogl_winsys_context_deinit,
     .context_egl_get_egl_display =
       _cogl_winsys_context_egl_get_egl_display,
+    .context_create_gles2_context =
+      _cogl_winsys_context_create_gles2_context,
+    .destroy_gles2_context = _cogl_winsys_destroy_gles2_context,
     .onscreen_init = _cogl_winsys_onscreen_init,
     .onscreen_deinit = _cogl_winsys_onscreen_deinit,
     .onscreen_bind = _cogl_winsys_onscreen_bind,
@@ -655,6 +833,11 @@ static CoglWinsysVtable _cogl_winsys_vtable =
     .onscreen_swap_region = _cogl_winsys_onscreen_swap_region,
     .onscreen_update_swap_throttled =
       _cogl_winsys_onscreen_update_swap_throttled,
+
+    /* CoglGLES2Context related methods */
+    .save_context = _cogl_winsys_save_context,
+    .set_gles2_context = _cogl_winsys_set_gles2_context,
+    .restore_context = _cogl_winsys_restore_context,
   };
 
 /* XXX: we use a function because no doubt someone will complain
diff --git a/cogl/winsys/cogl-winsys-private.h b/cogl/winsys/cogl-winsys-private.h
index 5332723..cd9ca2e 100644
--- a/cogl/winsys/cogl-winsys-private.h
+++ b/cogl/winsys/cogl-winsys-private.h
@@ -26,6 +26,7 @@
 
 #include "cogl-renderer.h"
 #include "cogl-onscreen.h"
+#include "cogl-gles2.h"
 
 #ifdef COGL_HAS_XLIB_SUPPORT
 #include "cogl-texture-pixmap-x11-private.h"
@@ -47,6 +48,8 @@ typedef enum { /*< prefix=COGL_WINSYS_ERROR >*/
   COGL_WINSYS_ERROR_INIT,
   COGL_WINSYS_ERROR_CREATE_CONTEXT,
   COGL_WINSYS_ERROR_CREATE_ONSCREEN,
+  COGL_WINSYS_ERROR_MAKE_CURRENT,
+  COGL_WINSYS_ERROR_CREATE_GLES2_CONTEXT,
 } CoglWinsysError;
 
 typedef enum
@@ -87,6 +90,9 @@ typedef struct _CoglWinsysVtable
   void
   (*context_deinit) (CoglContext *context);
 
+  void *
+  (*context_create_gles2_context) (CoglContext *ctx, GError **error);
+
   CoglBool
   (*onscreen_init) (CoglOnscreen *onscreen, GError **error);
 
@@ -158,6 +164,18 @@ typedef struct _CoglWinsysVtable
   (*texture_pixmap_x11_get_texture) (CoglTexturePixmapX11 *tex_pixmap);
 #endif
 
+  void
+  (*save_context) (CoglContext *ctx);
+
+  CoglBool
+  (*set_gles2_context) (CoglGLES2Context *gles2_ctx, GError **error);
+
+  void
+  (*restore_context) (CoglContext *ctx);
+
+  void
+  (*destroy_gles2_context) (CoglGLES2Context *gles2_ctx);
+
 } CoglWinsysVtable;
 
 CoglBool
diff --git a/examples/Makefile.am b/examples/Makefile.am
index fec0a65..1dab73c 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -23,7 +23,7 @@ cogl_info_SOURCES = cogl-info.c
 cogl_info_LDADD = $(common_ldadd)
 
 if USE_GLIB
-programs += cogl-hello cogl-msaa
+programs += cogl-hello cogl-msaa  cogl-gles2-context
 examples_datadir = $(pkgdatadir)/examples-data
 examples_data_DATA =
 
@@ -60,6 +60,9 @@ cogl_sdl_hello_SOURCES = cogl-sdl-hello.c
 cogl_sdl_hello_LDADD = $(common_ldadd)
 endif
 
+cogl_gles2_context_SOURCES = cogl-gles2-context.c
+cogl_gles2_context_LDADD = $(common_ldadd)
+
 endif #USE_GLIB
 
 if INSTALL_EXAMPLES
diff --git a/examples/cogl-gles2-context.c b/examples/cogl-gles2-context.c
new file mode 100644
index 0000000..c25b73d
--- /dev/null
+++ b/examples/cogl-gles2-context.c
@@ -0,0 +1,135 @@
+#include <cogl/cogl.h>
+#include <cogl/cogl-gles2.h>
+#include <glib.h>
+#include <stdio.h>
+
+#define OFFSCREEN_WIDTH 100
+#define OFFSCREEN_HEIGHT 100
+
+typedef struct _Data
+{
+    CoglContext *ctx;
+    CoglFramebuffer *fb;
+    CoglPrimitive *triangle;
+    CoglPipeline *pipeline;
+
+    CoglTexture *offscreen_texture;
+    CoglOffscreen *offscreen;
+    CoglGLES2Context *gles2_ctx;
+    const CoglGLES2Vtable *gles2_vtable;
+} Data;
+
+static gboolean
+paint_cb (void *user_data)
+{
+    Data *data = user_data;
+    GError *error = NULL;
+    const CoglGLES2Vtable *gles2 = data->gles2_vtable;
+
+    /* Draw scene with GLES2 */
+    if (!cogl_push_gles2_context (data->ctx,
+                                  data->gles2_ctx,
+                                  data->fb,
+                                  data->fb,
+                                  &error))
+    {
+        g_error ("Failed to push gles2 context: %s\n", error->message);
+    }
+
+    /* Clear offscreen framebuffer with a random color */
+    gles2->glClearColor (g_random_double (),
+                         g_random_double (),
+                         g_random_double (),
+                         1.0f);
+    gles2->glClear (GL_COLOR_BUFFER_BIT);
+
+    cogl_pop_gles2_context (data->ctx);
+
+    /* Draw scene with Cogl */
+    cogl_framebuffer_draw_primitive (data->fb, data->pipeline, data->triangle);
+
+    cogl_onscreen_swap_buffers (COGL_ONSCREEN (data->fb));
+
+    /* If the driver can deliver swap complete events then we can remove
+     * the idle paint callback until we next get a swap complete event
+     * otherwise we keep the idle paint callback installed and simply
+     * paint as fast as the driver will allow... */
+    if (cogl_has_feature (data->ctx, COGL_FEATURE_ID_SWAP_BUFFERS_EVENT))
+      return FALSE; /* remove the callback */
+    else
+      return TRUE;
+}
+
+static void
+swap_complete_cb (CoglFramebuffer *framebuffer, void *user_data)
+{
+    g_idle_add (paint_cb, user_data);
+}
+
+int
+main (int argc, char **argv)
+{
+    Data data;
+    CoglOnscreen *onscreen;
+    GError *error = NULL;
+    CoglVertexP2C4 triangle_vertices[] = {
+        {0, 0.7, 0xff, 0x00, 0x00, 0x80},
+        {-0.7, -0.7, 0x00, 0xff, 0x00, 0xff},
+        {0.7, -0.7, 0x00, 0x00, 0xff, 0xff}
+    };
+    GSource *cogl_source;
+    GMainLoop *loop;
+
+    data.ctx = cogl_context_new (NULL, NULL);
+
+    onscreen = cogl_onscreen_new (data.ctx, 640, 480);
+    cogl_onscreen_show (onscreen);
+    data.fb = COGL_FRAMEBUFFER (onscreen);
+
+    /* Prepare onscreen primitive */
+    data.triangle = cogl_primitive_new_p2c4 (data.ctx,
+                                             COGL_VERTICES_MODE_TRIANGLES,
+                                             3, triangle_vertices);
+    data.pipeline = cogl_pipeline_new (data.ctx);
+
+    data.offscreen_texture =
+      cogl_texture_new_with_size (OFFSCREEN_WIDTH,
+                                  OFFSCREEN_HEIGHT,
+                                  COGL_TEXTURE_NO_SLICING,
+                                  COGL_PIXEL_FORMAT_ANY);
+    data.offscreen = cogl_offscreen_new_to_texture (data.offscreen_texture);
+
+    data.gles2_ctx = cogl_gles2_context_new (data.ctx, &error);
+    if (!data.gles2_ctx) {
+        g_error ("Failed to create GLES2 context: %s\n", error->message);
+    }
+
+    data.gles2_vtable = cogl_gles2_context_get_vtable (data.gles2_ctx);
+
+    /* Draw scene with GLES2 */
+    if (!cogl_push_gles2_context (data.ctx,
+                                  data.gles2_ctx,
+                                  data.fb,
+                                  data.fb,
+                                  &error))
+    {
+        g_error ("Failed to push gles2 context: %s\n", error->message);
+    }
+
+    cogl_pop_gles2_context (data.ctx);
+
+    cogl_source = cogl_glib_source_new (data.ctx, G_PRIORITY_DEFAULT);
+
+    g_source_attach (cogl_source, NULL);
+
+    if (cogl_has_feature (data.ctx, COGL_FEATURE_ID_SWAP_BUFFERS_EVENT))
+      cogl_onscreen_add_swap_buffers_callback (COGL_ONSCREEN (data.fb),
+                                               swap_complete_cb, &data);
+
+    g_idle_add (paint_cb, &data);
+
+    loop = g_main_loop_new (NULL, TRUE);
+    g_main_loop_run (loop);
+
+    return 0;
+}
diff --git a/examples/cogl-info.c b/examples/cogl-info.c
index 988e991..b7dab00 100644
--- a/examples/cogl-info.c
+++ b/examples/cogl-info.c
@@ -103,6 +103,12 @@ struct {
     COGL_FEATURE_ID_MIRRORED_REPEAT,
     "Mirrored repeat wrap modes",
     "Mirrored repeat wrap modes"
+  },
+  {
+    COGL_FEATURE_ID_GLES2_CONTEXT,
+    "GLES2 API integration supported",
+    "Support for creating a GLES2 context for using the GLES2 API in a "
+      "way that's integrated with Cogl."
   }
 };
 
-- 
1.7.7.6



More information about the Cogl mailing list