[cogl/wip/rib/gles2-context: 2/5] Adds initial GLES2 integration support



commit 97a2495ab37934d480be2017b17d3f64e6483af1
Author: Robert Bragg <robert linux intel com>
Date:   Tue Mar 6 03:21:30 2012 +0000

    Adds initial GLES2 integration support
    
    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 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(-)
---
diff --git a/cogl/Makefile.am b/cogl/Makefile.am
index 55b93f8..667bb78 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 =
 
@@ -351,6 +360,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
@@ -483,6 +493,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 8b1ab2b..9864093 100644
--- a/cogl/cogl-context-private.h
+++ b/cogl/cogl-context-private.h
@@ -48,6 +48,7 @@
 #include "cogl-texture-3d.h"
 #include "cogl-texture-rectangle.h"
 #include "cogl-gl-header.h"
+#include "cogl-framebuffer-private.h"
 
 typedef struct
 {
@@ -171,6 +172,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 8ed3103..065cd4d 100644
--- a/cogl/cogl-context.c
+++ b/cogl/cogl-context.c
@@ -158,7 +158,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
@@ -297,6 +297,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;
@@ -470,6 +472,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 af95cac..2467593 100644
--- a/cogl/cogl-framebuffer.c
+++ b/cogl/cogl-framebuffer.c
@@ -110,13 +110,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 != COGL_INVALID_HANDLE)
     cogl_object_unref (offscreen->texture);
@@ -817,72 +818,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 */
@@ -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 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;
     }
@@ -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;
 }
 
+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
@@ -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,
+                         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
@@ -1330,8 +1429,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 collabora com>
+ *  Robert Bragg <robert 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 collabora com>
+ *  Robert Bragg <robert 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 collabora com>
+ *  Robert Bragg <robert 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 b27b759..ee1fd69 100644
--- a/cogl/cogl.h
+++ b/cogl/cogl.h
@@ -102,10 +102,11 @@
 #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 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."
   }
 };
 



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