[Cogl] [PATCH/RFC] Adds initial GLES2 integration support

Robert Bragg robert at sixbynine.org
Thu Mar 15 13:34:17 PDT 2012


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

I'd really like to see Cogl support good GLES2 integration not least
so that existing GLES2 code can more easily be re-used with Cogl based
applications but also I think it will enable people to start toying
with Cogl in their GLES2 based projects without having to commit 100%
to Cogl and also gives us some interesting opportunities to improve
the experience of using GLES2 on its own.

On the last point this is really about the difference between
providing a library and providing an api specification (which is
technically what GLES2 itself is). We can provide a GLES2 library
implemented in terms of the per-vendor GLES2 drivers with these
advantages:

» We can unify redundancy between vendor extensions. For example
  there is overlap between the ANGLE_framebuffer_blit extension, the
  EGL_NOK_swap_region2 and EGL_NV_post_sub_buffer extensions or also
  overlap between APPLE_framebuffer_multisample,
  ANGLE_framebuffer_multisample, EXT_multisampled_render_to_texture
  and IMG_multisampled_render_to_texture.
  
» We can support GLES2 implemented in terms of other graphics apis,
  such as OpenGL or D3D - hopefully leveraging work from the Angle and
  Mesa projects.  When you consider GLES2 in terms of GL that also
  means there is a further explosion of extensions that have
  redundancy. All in all being able to write OpenGL[ES] code that is
  portable *and* optimized to use all the available vendor extensions
  for a given platform is a lot of work which we can help with.

» We have the opportunity to make consistent certain differences
  between vendor drivers due to differing interpretations of the GLES2
  spec.

» Similarly we have an opportunity to work around bugs in vendor
  drivers. The drivers released in the wild running on consumer
  devices can't always be upgraded frequently to address bugs ranging
  from non-compliance issues to bugs that cause crashes or GPU
  artefacts. A project like Cogl though can provide an
  application library that can be upgraded with applications
  providing up-to-date workarounds for known bugs where possible.

» Debugging facilities available to Cogl can be made available to
  GLES2 applications too. So for example the wireframe mode could
  be used with GLES2 applications and also our debug-console plans
  will also be able to work with GLES2 applications.

» Since we are aiming to add threaded rendering to Cogl to avoid
  blocking applications when the OpenGL driver blocks (For example
  some embedded hardware can require the driver to block while
  the hardware does partial scene rendering if certain fixed-size
  hardware buffers become full) and these benefits can be made
  available to GLES2 api users too.

» Features such as cross-process remoting for the Cogl and GLES2 api
  (similar to what Google's Chrome browser does to put all GL
  rendering into a single process) could be added and it doesn't
  matter what vendor driver you have you can gain capabilities that
  individual vendors aren't likely to implement since they don't
  directly affect how you access the hardware itself.

» GLES2 context virtualization can be supported to avoid the very
  high costs of allocating and switching between the GLES2 contexts
  as provided by some vendor drivers. In particular there are several
  embedded gpus with extremely slow context switching taking in
  the order of milliseconds to switch which can be a problem for
  something like a browser wanting to give each WebGL app a separate
  context.

So given that background I've been been chipping away at another
iteration of adding GLES2 api support to Cogl based on Tomeu's patches
which he wrote to enable WebGL support to webkit-clutter.

I decided not to iterate directly on top of that original work and
instead started afresh because we realized that we should aim for a
slightly different api style than we were looking at before. I was
able to cherry pick several bits from Tomeu's branch though.

Instead of associating a GLES2 context with a framebuffer as was done
before using cogl_framebuffer_push_gles2_context() we now associate
read and write framebuffers with a CoglGLES2Context and push/pop a
gles2 context to a CoglContext using:

 cogl_push_gles2_context (cogl_context,
                          gles2_context,
                          read_framebuffer,
                          write_framebuffer,
                          &error);

The main reason for this api difference is that we are trying to
remove the idea of the "current framebuffer" from Cogl (so we will
eventually remove cogl_push/pop_framebuffer()) and the previous api
design wouldn't have allowed us to do that because it relied on
cogl_push_framebuffer() to bind any gles2 context that was pushed to
it.

Tomeu I'd like to get your feedback if possible regarding the changed
api since I'm hoping that your clutter-webkit work can be easily
updated to work with this patch.

The patch includes an example under examples/cogl-gles2-context.c

Ok on to the patch itself...

kind regards,
- Robert

P.S if you pass --scissored to git am it should use the marker below
to strip this section of the email before applying the patch.

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

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 avoid the
cost of GLES2 context switches which are *very* expensive with some
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.
---
 cogl/Makefile.am                      |   13 ++
 cogl/cogl-context-private.h           |    7 +
 cogl/cogl-context.c                   |   10 +-
 cogl/cogl-context.h                   |    4 +
 cogl/cogl-framebuffer-private.h       |   30 +++-
 cogl/cogl-framebuffer.c               |  366 +++++++++++++++++++++------------
 cogl/cogl-gles2-context-private.h     |   61 ++++++
 cogl/cogl-gles2-context.c             |  337 ++++++++++++++++++++++++++++++
 cogl/cogl-gles2-context.h             |  265 ++++++++++++++++++++++++
 cogl/cogl.h                           |    3 +-
 cogl/winsys/cogl-winsys-egl-android.c |    8 +-
 cogl/winsys/cogl-winsys-egl-gdl.c     |    8 +-
 cogl/winsys/cogl-winsys-egl-kms.c     |   21 +-
 cogl/winsys/cogl-winsys-egl-null.c    |    8 +-
 cogl/winsys/cogl-winsys-egl-private.h |   12 +-
 cogl/winsys/cogl-winsys-egl-wayland.c |   10 +-
 cogl/winsys/cogl-winsys-egl-x11.c     |    8 +-
 cogl/winsys/cogl-winsys-egl.c         |  189 ++++++++++++++++--
 cogl/winsys/cogl-winsys-private.h     |   18 ++
 examples/Makefile.am                  |    5 +-
 examples/cogl-gles2-context.c         |  131 ++++++++++++
 examples/cogl-info.c                  |    6 +
 22 files changed, 1336 insertions(+), 184 deletions(-)
 create mode 100644 cogl/cogl-gles2-context-private.h
 create mode 100644 cogl/cogl-gles2-context.c
 create mode 100644 cogl/cogl-gles2-context.h
 create mode 100644 examples/cogl-gles2-context.c

diff --git a/cogl/Makefile.am b/cogl/Makefile.am
index 853e561..d19d558 100644
--- a/cogl/Makefile.am
+++ b/cogl/Makefile.am
@@ -89,6 +89,7 @@ cogl_experimental_h = \
 	$(srcdir)/cogl-pipeline-state.h 	\
 	$(srcdir)/cogl-pipeline-layer-state.h 	\
 	$(srcdir)/cogl-snippet.h		\
+	$(srcdir)/cogl-gles2-context.h		\
 	$(srcdir)/cogl2-path.h 			\
 	$(srcdir)/cogl-index-buffer.h 		\
 	$(srcdir)/cogl-attribute-buffer.h 	\
@@ -115,6 +116,14 @@ cogl_experimental_h = \
 	$(srcdir)/cogl2-compatibility.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 =
 
@@ -350,6 +359,7 @@ cogl_sources_c = \
 	$(srcdir)/gl-prototypes/cogl-in-gles2-core-functions.h	\
 	$(srcdir)/gl-prototypes/cogl-fixed-functions.h	\
 	$(srcdir)/gl-prototypes/cogl-glsl-functions.h	\
+	$(srcdir)/cogl-gles2-context.c			\
 	$(NULL)
 
 if USE_GLIB
@@ -477,6 +487,9 @@ coglincludedir = $(includedir)/cogl/cogl
 coglinclude_HEADERS = $(cogl_headers) $(cogl_experimental_h)
 nodist_coglinclude_HEADERS = cogl-defines.h cogl-enum-types.h
 
+cogl_proto_includedir = $(includedir)/cogl/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 6d92faf..d7ecb10 100644
--- a/cogl/cogl-context-private.h
+++ b/cogl/cogl-context-private.h
@@ -46,6 +46,7 @@
 #include "cogl-texture-2d.h"
 #include "cogl-texture-3d.h"
 #include "cogl-texture-rectangle.h"
+#include "cogl-framebuffer-private.h"
 
 typedef struct
 {
@@ -168,6 +169,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 */
   CoglPath         *current_path;
   CoglPipeline     *stencil_pipeline;
diff --git a/cogl/cogl-context.c b/cogl/cogl-context.c
index 8b1ea2b..32a247d 100644
--- a/cogl/cogl-context.c
+++ b/cogl/cogl-context.c
@@ -156,7 +156,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
@@ -293,6 +293,8 @@ cogl_context_new (CoglDisplay *display,
   context->current_draw_buffer_state_flushed = 0;
   context->current_draw_buffer_changes = COGL_FRAMEBUFFER_STATE_ALL;
 
+  context->gles2_context_stack = g_queue_new ();
+
   context->journal_flush_attributes_array =
     g_array_new (TRUE, FALSE, sizeof (CoglAttribute *));
   context->journal_clip_bounds = NULL;
@@ -466,6 +468,12 @@ _cogl_context_free (CoglContext *context)
   if (context->blit_texture_pipeline)
     cogl_handle_unref (context->blit_texture_pipeline);
 
+  if (context->gles2_context_stack)
+    {
+      g_warn_if_fail (context->gles2_context_stack->length == 0);
+      g_queue_free (context->gles2_context_stack);
+    }
+
   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 e2120d7..51a435f 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 0081a2f..43bd47f 100644
--- a/cogl/cogl-framebuffer-private.h
+++ b/cogl/cogl-framebuffer-private.h
@@ -155,17 +155,33 @@ struct _CoglFramebuffer
   gboolean            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 */
@@ -389,4 +405,14 @@ _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);
+
 #endif /* __COGL_FRAMEBUFFER_PRIVATE_H */
diff --git a/cogl/cogl-framebuffer.c b/cogl/cogl-framebuffer.c
index 799f406..130a0c6 100644
--- a/cogl/cogl-framebuffer.c
+++ b/cogl/cogl-framebuffer.c
@@ -109,13 +109,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;
@@ -792,23 +785,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 != COGL_INVALID_HANDLE)
     cogl_object_unref (offscreen->texture);
@@ -816,72 +817,20 @@ _cogl_offscreen_free (CoglOffscreen *offscreen)
   g_free (offscreen);
 }
 
-static gboolean
-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)
-    {
-      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))
+  if (flags & (COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH_STENCIL |
+               COGL_OFFSCREEN_ALLOCATE_FLAG_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 */
@@ -904,13 +853,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
@@ -927,13 +878,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)
@@ -948,28 +900,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 gboolean
+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, &fbo_gl_handle));
+      GE (ctx, glDeleteFramebuffers (1, &gl_framebuffer->fbo_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;
     }
@@ -986,21 +1001,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;
 }
 
+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)
+{
+  return try_creating_fbo (ctx,
+                           texture,
+                           texture_level,
+                           texture_level_width,
+                           texture_level_height,
+                           config,
+                           flags,
+                           gl_framebuffer);
+}
+
 static gboolean
 _cogl_offscreen_allocate (CoglOffscreen *offscreen,
                           GError **error)
 {
   CoglFramebuffer *fb = COGL_FRAMEBUFFER (offscreen);
   CoglContext *ctx = fb->context;
-  static TryFBOFlags flags;
-  static gboolean have_working_flags = FALSE;
-  gboolean 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
@@ -1014,41 +1048,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,
+                         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;
 }
 
 gboolean
@@ -1329,8 +1428,11 @@ bind_gl_framebuffer (CoglContext *ctx,
                      CoglFramebuffer *framebuffer)
 {
   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 =
diff --git a/cogl/cogl-gles2-context-private.h b/cogl/cogl-gles2-context-private.h
new file mode 100644
index 0000000..f3f9b47
--- /dev/null
+++ b/cogl/cogl-gles2-context-private.h
@@ -0,0 +1,61 @@
+/*
+ * 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 _CoglGLES2Framebuffer
+{
+  CoglFramebuffer *original;
+  CoglGLFramebuffer gl_framebuffer;
+} CoglGLES2Framebuffer;
+
+struct _CoglGLES2Context
+{
+  CoglObject _parent;
+
+  CoglContext *context;
+
+  CoglGLES2Framebuffer *read_buffer;
+  CoglGLES2Framebuffer *write_buffer;
+
+  GLuint current_fbo_handle;
+
+  GList *foreign_framebuffers;
+
+  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..0209b71
--- /dev/null
+++ b/cogl/cogl-gles2-context.c
@@ -0,0 +1,337 @@
+/*
+ * 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-context.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"
+#include "winsys/cogl-winsys-egl-private.h"
+
+static void _cogl_gles2_context_free (CoglGLES2Context *gles2_context);
+
+COGL_OBJECT_DEFINE (GLES2Context, gles2_context);
+
+static CoglGLES2Context *current_gles2_context;
+
+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)
+    {
+      CoglGLES2Framebuffer *write = gles2_ctx->write_buffer;
+      framebuffer = write->gl_framebuffer.fbo_handle;
+    }
+
+  gles2_ctx->context->glBindFramebuffer (target, framebuffer);
+}
+
+/* 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;
+
+  if (gles2_ctx->current_fbo_handle == 0)
+    {
+      CoglGLES2Framebuffer *read = gles2_ctx->read_buffer;
+      CoglGLES2Framebuffer *write = gles2_ctx->write_buffer;
+
+      gles2_ctx->context->glBindFramebuffer (GL_FRAMEBUFFER,
+                                             read->gl_framebuffer.fbo_handle);
+      gles2_ctx->context->glReadPixels (x, y,
+                                        width, height,
+                                        format, type, pixels);
+      gles2_ctx->context->glBindFramebuffer (GL_FRAMEBUFFER,
+                                             write->gl_framebuffer.fbo_handle);
+    }
+  else
+    gles2_ctx->context->glReadPixels (x, y,
+                                      width, height, format, type, pixels);
+}
+
+static void
+_cogl_gles2_context_free (CoglGLES2Context *gles2_context)
+{
+  CoglContext *ctx = gles2_context->context;
+  const CoglWinsysVtable *winsys;
+  GList *l;
+
+  winsys = ctx->display->renderer->winsys_vtable;
+  winsys->destroy_gles2_context (gles2_context);
+
+  for (l = gles2_context->foreign_framebuffers; l; l = l->next)
+    {
+      CoglGLES2Framebuffer *gles2_framebuffer = l->data;
+
+      cogl_object_unref (gles2_framebuffer->original);
+      g_slice_free (CoglGLES2Framebuffer, gles2_framebuffer);
+    }
+  g_list_free (gles2_context->foreign_framebuffers);
+
+  g_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;
+
+  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;
+
+  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 separate context we have
+ * to be able to allocate ancillary buffers for that context...
+ */
+static CoglGLES2Framebuffer *
+_cogl_gles2_framebuffer_allocate (CoglFramebuffer *framebuffer,
+                                  CoglGLES2Context *gles2_context,
+                                  GError **error)
+{
+  CoglOffscreen *offscreen = COGL_OFFSCREEN (framebuffer);
+  const CoglWinsysVtable *winsys;
+  GList *l;
+  GError *internal_error = NULL;
+  CoglGLES2Framebuffer *gles2_framebuffer;
+
+  _COGL_RETURN_VAL_IF_FAIL (cogl_is_offscreen (framebuffer), FALSE);
+
+  if (!framebuffer->allocated &&
+      !cogl_framebuffer_allocate (framebuffer, error))
+    {
+      return NULL;
+    }
+
+  for (l = gles2_context->foreign_framebuffers; l; l = l->next)
+    {
+      CoglGLES2Framebuffer *gles2_framebuffer = l->data;
+      if (gles2_framebuffer->original == framebuffer)
+        return gles2_framebuffer;
+    }
+
+  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_framebuffer = g_slice_new0 (CoglGLES2Framebuffer);
+  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_framebuffer->gl_framebuffer))
+    {
+      winsys->restore_context (framebuffer->context);
+
+      g_slice_free (CoglGLES2Framebuffer, gles2_framebuffer);
+
+      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_framebuffer->original = cogl_object_ref (framebuffer);
+
+  gles2_context->foreign_framebuffers =
+    g_list_prepend (gles2_context->foreign_framebuffers,
+                    gles2_framebuffer);
+
+  return gles2_framebuffer;
+}
+
+gboolean
+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;
+
+  _COGL_RETURN_VAL_IF_FAIL (gles2_ctx != NULL, FALSE);
+  _COGL_RETURN_VAL_IF_FAIL (cogl_is_offscreen (read_buffer), FALSE);
+  _COGL_RETURN_VAL_IF_FAIL (cogl_is_offscreen (write_buffer), FALSE);
+
+  if (ctx->gles2_context_stack->length == 0)
+    {
+      _cogl_journal_flush (read_buffer->journal, read_buffer);
+      if (write_buffer != read_buffer)
+        _cogl_journal_flush (write_buffer->journal, write_buffer);
+      winsys->save_context (ctx);
+    }
+  else
+    gles2_ctx->vtable->glFlush ();
+
+  winsys->set_gles2_context (gles2_ctx, error);
+
+  g_queue_push_tail (ctx->gles2_context_stack, gles2_ctx);
+
+  gles2_ctx->read_buffer = _cogl_gles2_framebuffer_allocate (read_buffer,
+                                                             gles2_ctx,
+                                                             error);
+  if (!gles2_ctx->read_buffer)
+    {
+      winsys->restore_context (ctx);
+      return FALSE;
+    }
+
+  gles2_ctx->write_buffer = _cogl_gles2_framebuffer_allocate (write_buffer,
+                                                              gles2_ctx,
+                                                              error);
+  if (!gles2_ctx->write_buffer)
+    {
+      winsys->restore_context (ctx);
+      return FALSE;
+    }
+
+  current_gles2_context = gles2_ctx;
+  return TRUE;
+}
+
+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 = NULL;
+    }
+  else
+    {
+      winsys->restore_context (ctx);
+      current_gles2_context = gles2_ctx;
+    }
+}
diff --git a/cogl/cogl-gles2-context.h b/cogl/cogl-gles2-context.h
new file mode 100644
index 0000000..e8d051d
--- /dev/null
+++ b/cogl/cogl-gles2-context.h
@@ -0,0 +1,265 @@
+/*
+ * 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>
+ *
+ */
+
+#if !defined(__COGL_H_INSIDE__) && !defined(CLUTTER_COMPILATION)
+#error "Only <cogl/cogl.h> can be included directly."
+#endif
+
+#ifndef __COGL_GLES2_CONTEXT_H__
+#define __COGL_GLES2_CONTEXT_H__
+
+#include <cogl/cogl-defines.h>
+#include <cogl/cogl-context.h>
+#include <cogl/cogl-framebuffer.h>
+
+G_BEGIN_DECLS
+
+/**
+ * SECTION:cogl-gles2-context
+ * @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.10
+ * 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.10
+ * 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.10
+ * 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: 1.10
+ * 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.
+ *
+ * Error codes that relate to the cogl_gles2_context api.
+ */
+typedef enum { /*< prefix=COGL_GLES2_CONTEXT_ERROR >*/
+  COGL_GLES2_CONTEXT_ERROR_UNSUPPORTED,
+} 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: 1.10
+ * 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: 1.10
+ * 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: 1.10
+ * Return value: %TRUE if operation was successfull or %FALSE
+ *               otherwise and @error will be updated.
+ * Stability: unstable
+ */
+gboolean
+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: 1.10
+ * Stability: unstable
+ */
+void
+cogl_pop_gles2_context (CoglContext *ctx);
+
+/**
+ * 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: 1.10
+ * Stability: unstable
+ */
+gboolean
+cogl_is_gles2_context (void *object);
+
+G_END_DECLS
+
+#endif /* __COGL_GLES2_CONTEXT_H__ */
+
diff --git a/cogl/cogl.h b/cogl/cogl.h
index d2e0bc0..1e9dd49 100644
--- a/cogl/cogl.h
+++ b/cogl/cogl.h
@@ -102,13 +102,14 @@
 #include <cogl/cogl-framebuffer.h>
 #include <cogl/cogl-onscreen.h>
 #include <cogl/cogl-poll.h>
+#include <cogl/cogl-gles2-context.h>
 #if defined (COGL_HAS_EGL_PLATFORM_KMS_SUPPORT)
 #include <cogl/cogl-kms-renderer.h>
 #endif
 #if defined (COGL_HAS_EGL_PLATFORM_WAYLAND_SUPPORT)
 #include <cogl/cogl-wayland-renderer.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 2efa6b2..c9f7a09 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 1f7a2c3..ef7a72a 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 ecddb43..fcba974 100644
--- a/cogl/winsys/cogl-winsys-egl-kms.c
+++ b/cogl/winsys/cogl-winsys-egl-kms.c
@@ -278,10 +278,10 @@ _cogl_winsys_egl_try_create_context (CoglDisplay *display,
       return FALSE;
     }
 
-  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,
@@ -511,14 +511,13 @@ static void
 _cogl_winsys_onscreen_bind (CoglOnscreen *onscreen)
 {
   CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context;
-  CoglDisplayEGL *egl_display = context->display->winsys;
-  CoglRenderer *renderer = context->display->renderer;
-  CoglRendererEGL *egl_renderer = renderer->winsys;
+  CoglDisplay *display = context->display;
+  CoglDisplayEGL *egl_display = display->winsys;
 
-  eglMakeCurrent (egl_renderer->edpy,
-                  EGL_NO_SURFACE,
-                  EGL_NO_SURFACE,
-                  egl_display->egl_context);
+  _cogl_winsys_egl_make_current (display,
+                                 EGL_NO_SURFACE,
+                                 EGL_NO_SURFACE,
+                                 egl_display->egl_context);
 }
 
 static void
diff --git a/cogl/winsys/cogl-winsys-egl-null.c b/cogl/winsys/cogl-winsys-egl-null.c
index f0b9200..d19d06b 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 6658278..374c894 100644
--- a/cogl/winsys/cogl-winsys-egl-private.h
+++ b/cogl/winsys/cogl-winsys-egl-private.h
@@ -118,13 +118,17 @@ typedef struct _CoglDisplayEGL
   gboolean found_egl_config;
   gboolean stencil_disabled;
 
+  EGLSurface current_read_surface;
+  EGLSurface current_draw_surface;
+
   /* 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
@@ -138,6 +142,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 a9d2129..03e995b 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 55cbbc2..5fdf105 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 5d96ac4..13ea9e7 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)
@@ -294,6 +328,31 @@ 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)
+  return EGL_TRUE;
+
+  ret = eglMakeCurrent (egl_renderer->edpy,
+                        draw,
+                        read,
+                        context);
+
+  egl_display->current_draw_surface = draw;
+  egl_display->current_read_surface = read;
+
+  return ret;
+}
+
 static void
 cleanup_context (CoglDisplay *display)
 {
@@ -303,8 +362,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;
     }
@@ -421,6 +481,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;
@@ -440,6 +507,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;
+  CoglRenderer *renderer = display->renderer;
+  CoglRendererEGL *egl_renderer = renderer->winsys;
+  EGLContext egl_context = gles2_ctx->winsys;
+
+  _cogl_winsys_egl_make_current (display,
+                                 EGL_NO_SURFACE, EGL_NO_SURFACE,
+                                 EGL_NO_CONTEXT);
+  eglDestroyContext (egl_renderer->edpy, egl_context);
+}
+
 static gboolean
 _cogl_winsys_onscreen_init (CoglOnscreen *onscreen,
                             GError **error)
@@ -538,16 +653,11 @@ _cogl_winsys_onscreen_bind (CoglOnscreen *onscreen)
   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;
-
-  eglMakeCurrent (egl_renderer->edpy,
-                  egl_onscreen->egl_surface,
-                  egl_onscreen->egl_surface,
-                  egl_display->egl_context);
-  egl_context->current_surface = egl_onscreen->egl_surface;
+  _cogl_winsys_egl_make_current (context->display,
+                                 egl_onscreen->egl_surface,
+                                 egl_onscreen->egl_surface,
+                                 egl_display->egl_context);
 
   if (fb->config.swap_throttled)
     eglSwapInterval (egl_renderer->edpy, 1);
@@ -618,13 +728,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);
 }
@@ -637,6 +747,49 @@ _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 gboolean
+_cogl_winsys_set_gles2_context (CoglGLES2Context *gles2_ctx,
+                                GError **error)
+{
+  CoglContext *ctx = gles2_ctx->context;
+  CoglDisplayEGL *egl_display = ctx->display->winsys;
+
+  if (!_cogl_winsys_egl_make_current (ctx->display,
+                                      egl_display->dummy_surface,
+                                      egl_display->dummy_surface,
+                                      gles2_ctx->winsys))
+    {
+      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,
@@ -653,6 +806,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,
@@ -660,6 +816,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 0e6c78b..c493dc2 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-context.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);
+
   gboolean
   (*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);
+
+  gboolean
+  (*set_gles2_context) (CoglGLES2Context *gles2_ctx, GError **error);
+
+  void
+  (*restore_context) (CoglContext *ctx);
+
+  void
+  (*destroy_gles2_context) (CoglGLES2Context *gles2_ctx);
+
 } CoglWinsysVtable;
 
 gboolean
diff --git a/examples/Makefile.am b/examples/Makefile.am
index f96ae65..868e9bc 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -18,7 +18,7 @@ common_ldadd = \
 	$(COGL_DEP_LIBS) \
 	$(top_builddir)/cogl/libcogl.la
 
-programs = cogl-hello cogl-info cogl-msaa
+programs = cogl-hello cogl-info cogl-msaa cogl-gles2-context
 examples_datadir = $(pkgdatadir)/examples-data
 examples_data_DATA =
 
@@ -57,6 +57,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)
+
 if INSTALL_EXAMPLES
 bin_PROGRAMS = $(programs)
 else
diff --git a/examples/cogl-gles2-context.c b/examples/cogl-gles2-context.c
new file mode 100644
index 0000000..ee4c960
--- /dev/null
+++ b/examples/cogl-gles2-context.c
@@ -0,0 +1,131 @@
+#include <cogl/cogl.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;
+    CoglGLES2Vtable *gles2 = data->gles2_vtable;
+
+    /* Draw scene with Cogl */
+    cogl_framebuffer_clear4f (data->fb, COGL_BUFFER_BIT_COLOR, 0, 0, 0, 1);
+    cogl_framebuffer_draw_primitive (data->fb, data->pipeline, data->triangle);
+
+    /* Draw scene with GLES2 */
+    if (!cogl_push_gles2_context (data->ctx,
+                                  data->gles2_ctx,
+                                  COGL_FRAMEBUFFER (data->offscreen),
+                                  COGL_FRAMEBUFFER (data->offscreen),
+                                  &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);
+
+    /* XXX: we still need a "current framebuffer" to use the cogl_rectangle api.
+     * FIXME: Add cogl_framebuffer_draw_textured_rectangle API. */
+    cogl_push_framebuffer (data->fb);
+    cogl_set_source_texture (data->offscreen_texture);
+    cogl_rectangle_with_texture_coords (-0.5, -0.5, 0.5, 0.5,
+                                        0, 0, 1.0, 1.0);
+    cogl_pop_framebuffer ();
+
+    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);
+
+    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