[cairo-commit] 5 commits - boilerplate/cairo-boilerplate-cogl.c boilerplate/Makefile.sources boilerplate/Makefile.win32.features build/configure.ac.features build/Makefile.win32.features build/Makefile.win32.features-h configure.ac perf/cairo-perf-chart.c src/cairo-clip.c src/cairo-clip-private.h src/cairo-cogl-context.c src/cairo-cogl-context-private.h src/cairo-cogl-gradient.c src/cairo-cogl-gradient-private.h src/cairo-cogl.h src/cairo-cogl-private.h src/cairo-cogl-surface.c src/cairo-cogl-utils.c src/cairo-cogl-utils-private.h src/cairo-debug.c src/cairo-default-context.c src/cairo-default-context-private.h src/cairo-gl-msaa-compositor.c src/cairo.h src/cairo-image-source.c src/cairo-image-surface-private.h src/cairoint.h src/cairo-traps.c src/Makefile.sources src/Makefile.win32.features

Chris Wilson ickle at kemper.freedesktop.org
Sat Oct 15 01:06:35 PDT 2011


 boilerplate/Makefile.sources         |    1 
 boilerplate/Makefile.win32.features  |   12 
 boilerplate/cairo-boilerplate-cogl.c |  208 ++
 build/Makefile.win32.features        |    1 
 build/Makefile.win32.features-h      |    3 
 build/configure.ac.features          |    1 
 configure.ac                         |    6 
 perf/cairo-perf-chart.c              |   61 
 src/Makefile.sources                 |   10 
 src/Makefile.win32.features          |   16 
 src/cairo-clip-private.h             |    3 
 src/cairo-clip.c                     |    2 
 src/cairo-cogl-context-private.h     |   52 
 src/cairo-cogl-context.c             |  820 ++++++++++
 src/cairo-cogl-gradient-private.h    |   89 +
 src/cairo-cogl-gradient.c            |  643 ++++++++
 src/cairo-cogl-private.h             |  164 ++
 src/cairo-cogl-surface.c             | 2802 +++++++++++++++++++++++++++++++++++
 src/cairo-cogl-utils-private.h       |   54 
 src/cairo-cogl-utils.c               |  126 +
 src/cairo-cogl.h                     |   69 
 src/cairo-debug.c                    |    4 
 src/cairo-default-context-private.h  |    6 
 src/cairo-default-context.c          |   36 
 src/cairo-gl-msaa-compositor.c       |   20 
 src/cairo-image-source.c             |    4 
 src/cairo-image-surface-private.h    |   15 
 src/cairo-traps.c                    |   35 
 src/cairo.h                          |    5 
 src/cairoint.h                       |    8 
 30 files changed, 5233 insertions(+), 43 deletions(-)

New commits:
commit 3813066f134d6dde7ff75f3909575beeddc685ed
Author: Martin Robinson <mrobinson at igalia.com>
Date:   Sat Oct 15 09:05:19 2011 +0100

    gl/msaa: Fix glScissor bounds
    
    When scissoring the compositing extents for windows, flip
    the y coordinate as windows use a projection matrix that
    does the same.

diff --git a/src/cairo-gl-msaa-compositor.c b/src/cairo-gl-msaa-compositor.c
index 7a0e828..2210da6 100644
--- a/src/cairo-gl-msaa-compositor.c
+++ b/src/cairo-gl-msaa-compositor.c
@@ -247,6 +247,16 @@ _stroke_shaper_add_quad (void			*closure,
 						      quad);
 }
 
+static void
+_scissor_to_rectangle (cairo_gl_surface_t *surface,
+		       const cairo_rectangle_int_t *r)
+{
+    int y = _cairo_gl_y_flip (surface, r->y);
+
+    glScissor (r->x, y, r->width, r->height);
+    glEnable (GL_SCISSOR_TEST);
+}
+
 static cairo_int_status_t
 _cairo_gl_msaa_compositor_stroke (const cairo_compositor_t	*compositor,
 				  cairo_composite_rectangles_t	*composite,
@@ -289,10 +299,7 @@ _cairo_gl_msaa_compositor_stroke (const cairo_compositor_t	*compositor,
     if (unlikely (status))
 	goto finish;
 
-    glScissor (composite->unbounded.x, composite->unbounded.y,
-	       composite->unbounded.width, composite->unbounded.height);
-    glEnable (GL_SCISSOR_TEST);
-
+    _scissor_to_rectangle (dst, &composite->unbounded);
     if (! _cairo_composite_rectangles_can_reduce_clip (composite,
 						       composite->clip))
     {
@@ -372,10 +379,7 @@ _cairo_gl_msaa_compositor_fill (const cairo_compositor_t	*compositor,
     if (unlikely (status))
 	goto cleanup_setup;
 
-    glScissor (composite->unbounded.x, composite->unbounded.y,
-	       composite->unbounded.width, composite->unbounded.height);
-    glEnable (GL_SCISSOR_TEST);
-
+    _scissor_to_rectangle (dst, &composite->unbounded);
     if (! _cairo_composite_rectangles_can_reduce_clip (composite,
 						       composite->clip))
     {
commit cc3f06a58a91bf52ca4f958c96ee1e598cdaa62d
Merge: 571a27b... 10ebda8...
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Oct 15 08:59:35 2011 +0100

    Merge branch 'master' of git://cairographics.org/git/cairo

commit 571a27b4fcfe11fec2b33c31525c353eb30c0ad7
Author: Robert Bragg <robert at linux.intel.com>
Date:   Thu Jul 21 12:15:05 2011 +0100

    backends: Adds a new Cogl based backend
    
    This adds a new GPU accelerated backend for Cairo based on the Cogl 3D
    graphics API.
    
    This backend aims to support Cairo in a way that translates as naturally
    as possible to using a GPU, it does not strive to compete with the
    anti-aliasing quality of the image backend if it can't be done
    efficiently using the GPU - raw performance isn't the only metric of
    concern, so is power usage.
    
    As an overview of how the backend works:
    - fills are handled by tessellating paths into triangles
    - the backend has an extra fill_rectangle drawing operation so we have
      a fast-path for drawing rectangles which are so common.
    - strokes are also tessellated into triangles.
    - stroke and fill tessellations are cached to avoid the cpu overhead
      of tessellation and cost of upload given that its common for apps to
      re-draw the same path multiple times. The tessellations can survive
      translations and rotations increasing the probability that they can be
      re-used.
    - sources and masks are handled using multi-texturing.
    - clipping is handled with a scissor and the stencil buffer which
      we're careful to only update when they really change.
    - linear gradients are rendered to a 1d texture using a triangle
      strip + interpolating color attributes. All cairo extend modes
      are handled by corresponding texture sampler wrap modes without
      needing programmable fragment processing.
    - antialiasing should be handled using Cogl's multisampling API
    
    XXX: This is a work in progress!!
    TODO:
    - handle at least basic radial gradients (No need to handle full
      pdf semantics, since css, svg and canvas only allow radial gradients
      defined as one circle + a point that must lie within the first
      circle.) - currently we fall back to pixman for radial gradients.
    - support glyph rendering with a decent glyph cache design. The
      current plan is a per scaled-font growable cache texture + a
      scratch cache for one-shot/short-lived glyphs.
    - decide how to handle npot textures when lacking hardware support.
      Current plan is to add a transparent border to npot textures and use
      CLAMP_TO_EDGE for the default EXTEND_NONE semantics. For anything else
      we can allocate a shadow npot texture and scale the original to fit
      that so we can map extend modes to texture sampler modes.

diff --git a/boilerplate/Makefile.sources b/boilerplate/Makefile.sources
index 63ef34e..101e997 100644
--- a/boilerplate/Makefile.sources
+++ b/boilerplate/Makefile.sources
@@ -38,3 +38,4 @@ cairo_boilerplate_xcb_sources = cairo-boilerplate-xcb.c
 cairo_boilerplate_xlib_headers = cairo-boilerplate-xlib.h
 cairo_boilerplate_xlib_sources = cairo-boilerplate-xlib.c
 cairo_boilerplate_vg_sources = cairo-boilerplate-vg.c
+cairo_boilerplate_cogl_sources = cairo-boilerplate-cogl.c
diff --git a/boilerplate/Makefile.win32.features b/boilerplate/Makefile.win32.features
index 8b6a673..e60a95b 100644
--- a/boilerplate/Makefile.win32.features
+++ b/boilerplate/Makefile.win32.features
@@ -247,6 +247,18 @@ enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_glesv2_cxx_sources)
 enabled_cairo_boilerplate_sources += $(cairo_boilerplate_glesv2_sources)
 endif
 
+unsupported_cairo_boilerplate_headers += $(cairo_boilerplate_cogl_headers)
+all_cairo_boilerplate_headers += $(cairo_boilerplate_cogl_headers)
+all_cairo_boilerplate_private += $(cairo_boilerplate_cogl_private)
+all_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_cogl_cxx_sources)
+all_cairo_boilerplate_sources += $(cairo_boilerplate_cogl_sources)
+ifeq ($(CAIRO_HAS_COGL_SURFACE),1)
+enabled_cairo_boilerplate_headers += $(cairo_boilerplate_cogl_headers)
+enabled_cairo_boilerplate_private += $(cairo_boilerplate_cogl_private)
+enabled_cairo_boilerplate_cxx_sources += $(cairo_boilerplate_cogl_cxx_sources)
+enabled_cairo_boilerplate_sources += $(cairo_boilerplate_cogl_sources)
+endif
+
 unsupported_cairo_boilerplate_headers += $(cairo_boilerplate_directfb_headers)
 all_cairo_boilerplate_headers += $(cairo_boilerplate_directfb_headers)
 all_cairo_boilerplate_private += $(cairo_boilerplate_directfb_private)
diff --git a/boilerplate/cairo-boilerplate-cogl.c b/boilerplate/cairo-boilerplate-cogl.c
new file mode 100644
index 0000000..8dda317
--- /dev/null
+++ b/boilerplate/cairo-boilerplate-cogl.c
@@ -0,0 +1,208 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson.
+ */
+
+#include "cairo-boilerplate-private.h"
+
+#include <cairo-cogl.h>
+#include <cogl/cogl2-experimental.h>
+
+typedef struct _cogl_closure {
+    cairo_device_t *device;
+    CoglFramebuffer *fb;
+    cairo_surface_t *surface;
+} cogl_closure_t;
+
+static const cairo_user_data_key_t cogl_closure_key;
+
+static CoglContext *context = NULL;
+
+static void
+_cairo_boilerplate_cogl_cleanup (void *abstract_closure)
+{
+    cogl_closure_t *closure = abstract_closure;
+
+    cogl_object_unref (closure->fb);
+
+    cairo_device_finish (closure->device);
+    cairo_device_destroy (closure->device);
+
+    free (closure);
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_cogl_create_offscreen_color_surface (const char		*name,
+							cairo_content_t		 content,
+							double			 width,
+							double			 height,
+							double			 max_width,
+							double			 max_height,
+							cairo_boilerplate_mode_t mode,
+							int			 id,
+							void		       **abstract_closure)
+{
+    cairo_device_t *device;
+    CoglTexture *tex;
+    CoglHandle offscreen;
+    CoglFramebuffer *fb;
+    cogl_closure_t *closure;
+    cairo_status_t status;
+
+    if (!context)
+	context = cogl_context_new (NULL, NULL);
+
+    device = cairo_cogl_device_create (context);
+    tex = cogl_texture_new_with_size (width, height,
+				      COGL_TEXTURE_NO_SLICING,
+				      COGL_PIXEL_FORMAT_BGRA_8888_PRE);
+    offscreen = cogl_offscreen_new_to_texture (tex);
+    fb = COGL_FRAMEBUFFER (offscreen);
+
+    cogl_framebuffer_allocate (fb, NULL);
+    cogl_push_framebuffer (fb);
+    cogl_ortho (0, cogl_framebuffer_get_width (fb),
+                cogl_framebuffer_get_height (fb), 0,
+                -1, 100);
+    cogl_pop_framebuffer ();
+
+    closure = malloc (sizeof (cogl_closure_t));
+    *abstract_closure = closure;
+    closure->device = device;
+    closure->fb = fb;
+    closure->surface = cairo_cogl_surface_create (device, fb);
+
+    status = cairo_surface_set_user_data (closure->surface,
+					  &cogl_closure_key, closure, NULL);
+    if (status == CAIRO_STATUS_SUCCESS)
+	return closure->surface;
+
+    _cairo_boilerplate_cogl_cleanup (closure);
+    return cairo_boilerplate_surface_create_in_error (status);
+}
+
+static cairo_surface_t *
+_cairo_boilerplate_cogl_create_onscreen_color_surface (const char	       *name,
+						       cairo_content_t		content,
+						       double			width,
+						       double			height,
+						       double			max_width,
+						       double			max_height,
+						       cairo_boilerplate_mode_t mode,
+						       int			id,
+						       void		      **abstract_closure)
+{
+    cairo_device_t *device;
+    CoglOnscreen *onscreen;
+    CoglFramebuffer *fb;
+    cogl_closure_t *closure;
+    cairo_status_t status;
+
+    if (!context)
+	context = cogl_context_new (NULL, NULL);
+
+    device = cairo_cogl_device_create (context);
+    onscreen = cogl_onscreen_new (context, width, height);
+    fb = COGL_FRAMEBUFFER (onscreen);
+
+    cogl_onscreen_show (onscreen);
+
+    cogl_push_framebuffer (fb);
+    cogl_ortho (0, cogl_framebuffer_get_width (fb),
+                cogl_framebuffer_get_height (fb), 0,
+                -1, 100);
+    cogl_pop_framebuffer ();
+
+    closure = malloc (sizeof (cogl_closure_t));
+    *abstract_closure = closure;
+    closure->device = device;
+    closure->fb = fb;
+    closure->surface = cairo_cogl_surface_create (device, fb);
+
+    status = cairo_surface_set_user_data (closure->surface,
+					  &cogl_closure_key, closure, NULL);
+    if (status == CAIRO_STATUS_SUCCESS)
+	return closure->surface;
+
+    _cairo_boilerplate_cogl_cleanup (closure);
+    return cairo_boilerplate_surface_create_in_error (status);
+}
+
+static cairo_status_t
+_cairo_boilerplate_cogl_finish_onscreen (cairo_surface_t *surface)
+{
+    cogl_closure_t *closure = cairo_surface_get_user_data (surface, &cogl_closure_key);
+
+    cairo_cogl_surface_end_frame (surface);
+
+    cogl_framebuffer_swap_buffers (closure->fb);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_boilerplate_cogl_synchronize (void *abstract_closure)
+{
+    cogl_closure_t *closure = abstract_closure;
+    cogl_framebuffer_finish (closure->fb);
+}
+
+static const cairo_boilerplate_target_t targets[] = {
+    {
+	"cogl-offscreen-color", "cogl", NULL, NULL,
+	CAIRO_SURFACE_TYPE_COGL, CAIRO_CONTENT_COLOR_ALPHA, 1,
+	"cairo_cogl_device_create",
+	_cairo_boilerplate_cogl_create_offscreen_color_surface,
+	cairo_surface_create_similar,
+	NULL, NULL,
+	_cairo_boilerplate_get_image_surface,
+	cairo_surface_write_to_png,
+	_cairo_boilerplate_cogl_cleanup,
+	_cairo_boilerplate_cogl_synchronize,
+        NULL,
+	TRUE, FALSE, FALSE
+    },
+    {
+	"cogl-onscreen-color", "cogl", NULL, NULL,
+	CAIRO_SURFACE_TYPE_COGL, CAIRO_CONTENT_COLOR_ALPHA, 1,
+	"cairo_cogl_device_create",
+	_cairo_boilerplate_cogl_create_onscreen_color_surface,
+	cairo_surface_create_similar,
+	NULL,
+	_cairo_boilerplate_cogl_finish_onscreen,
+	_cairo_boilerplate_get_image_surface,
+	cairo_surface_write_to_png,
+	_cairo_boilerplate_cogl_cleanup,
+	_cairo_boilerplate_cogl_synchronize,
+        NULL,
+	TRUE, FALSE, FALSE
+    }
+};
+CAIRO_BOILERPLATE (cogl, targets)
diff --git a/build/Makefile.win32.features b/build/Makefile.win32.features
index 650b224..8cb155d 100644
--- a/build/Makefile.win32.features
+++ b/build/Makefile.win32.features
@@ -19,6 +19,7 @@ CAIRO_HAS_GALLIUM_SURFACE=0
 CAIRO_HAS_PNG_FUNCTIONS=1
 CAIRO_HAS_GL_SURFACE=0
 CAIRO_HAS_GLESV2_SURFACE=0
+CAIRO_HAS_COGL_SURFACE=0
 CAIRO_HAS_DIRECTFB_SURFACE=0
 CAIRO_HAS_VG_SURFACE=0
 CAIRO_HAS_EGL_FUNCTIONS=0
diff --git a/build/Makefile.win32.features-h b/build/Makefile.win32.features-h
index 84d65f4..13904cf 100644
--- a/build/Makefile.win32.features-h
+++ b/build/Makefile.win32.features-h
@@ -62,6 +62,9 @@ endif
 ifeq ($(CAIRO_HAS_GLESV2_SURFACE),1)
 	@echo "#define CAIRO_HAS_GLESV2_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
 endif
+ifeq ($(CAIRO_HAS_COGL_SURFACE),1)
+	@echo "#define CAIRO_HAS_COGL_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
+endif
 ifeq ($(CAIRO_HAS_DIRECTFB_SURFACE),1)
 	@echo "#define CAIRO_HAS_DIRECTFB_SURFACE 1" >> $(top_srcdir)/src/cairo-features.h
 endif
diff --git a/build/configure.ac.features b/build/configure.ac.features
index faa762c..e4a2aaf 100644
--- a/build/configure.ac.features
+++ b/build/configure.ac.features
@@ -388,6 +388,7 @@ AC_DEFUN([CAIRO_REPORT],
 	echo "  DirectFB:      $use_directfb"
 	echo "  OpenVG:        $use_vg"
 	echo "  DRM:           $use_drm"
+	echo "  Cogl:          $use_cogl"
 	echo ""
 	echo "The following font backends:"
 	echo "  User:          yes (always builtin)"
diff --git a/configure.ac b/configure.ac
index 2d3fd36..3a438ee 100644
--- a/configure.ac
+++ b/configure.ac
@@ -328,6 +328,12 @@ CAIRO_ENABLE_SURFACE_BACKEND(glesv2, OpenGLESv2, no, [
 ])
 
 dnl ===========================================================================
+CAIRO_ENABLE_SURFACE_BACKEND(cogl, Cogl, no, [
+  cogl_REQUIRES="cogl-2.0-experimental"
+  PKG_CHECK_MODULES(cogl, $cogl_REQUIRES,, [use_cogl="no"])
+])
+
+dnl ===========================================================================
 
 CAIRO_ENABLE_SURFACE_BACKEND(directfb, directfb, no, [
   directfb_REQUIRES=directfb
diff --git a/src/Makefile.sources b/src/Makefile.sources
index 2320f29..f4b55d9 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -427,3 +427,13 @@ cairo_xml_sources = cairo-xml-surface.c
 
 cairo_vg_headers = cairo-vg.h
 cairo_vg_sources = cairo-vg-surface.c
+
+cairo_cogl_headers = cairo-cogl.h
+cairo_cogl_private = cairo-cogl-private.h \
+		     cairo-cogl-gradient-private.h \
+		     cairo-cogl-context-private.h \
+		     cairo-cogl-utils-private.h
+cairo_cogl_sources = cairo-cogl-surface.c \
+		     cairo-cogl-gradient.c \
+		     cairo-cogl-context.c \
+		     cairo-cogl-utils.c
diff --git a/src/Makefile.win32.features b/src/Makefile.win32.features
index 4b90d64..2274f4a 100644
--- a/src/Makefile.win32.features
+++ b/src/Makefile.win32.features
@@ -325,6 +325,22 @@ ifeq ($(CAIRO_HAS_GLESV2_SURFACE),1)
 enabled_cairo_pkgconf += cairo-glesv2.pc
 endif
 
+unsupported_cairo_headers += $(cairo_cogl_headers)
+all_cairo_headers += $(cairo_cogl_headers)
+all_cairo_private += $(cairo_cogl_private)
+all_cairo_cxx_sources += $(cairo_cogl_cxx_sources)
+all_cairo_sources += $(cairo_cogl_sources)
+ifeq ($(CAIRO_HAS_COGL_SURFACE),1)
+enabled_cairo_headers += $(cairo_cogl_headers)
+enabled_cairo_private += $(cairo_cogl_private)
+enabled_cairo_cxx_sources += $(cairo_cogl_cxx_sources)
+enabled_cairo_sources += $(cairo_cogl_sources)
+endif
+all_cairo_pkgconf += cairo-cogl.pc
+ifeq ($(CAIRO_HAS_COGL_SURFACE),1)
+enabled_cairo_pkgconf += cairo-cogl.pc
+endif
+
 unsupported_cairo_headers += $(cairo_directfb_headers)
 all_cairo_headers += $(cairo_directfb_headers)
 all_cairo_private += $(cairo_directfb_private)
diff --git a/src/cairo-clip-private.h b/src/cairo-clip-private.h
index aaf16f3..2c2aa95 100644
--- a/src/cairo-clip-private.h
+++ b/src/cairo-clip-private.h
@@ -73,6 +73,9 @@ struct _cairo_clip {
 cairo_private cairo_clip_t *
 _cairo_clip_create (void);
 
+cairo_private cairo_clip_path_t *
+_cairo_clip_path_reference (cairo_clip_path_t *clip_path);
+
 cairo_private void
 _cairo_clip_path_destroy (cairo_clip_path_t *clip_path);
 
diff --git a/src/cairo-clip.c b/src/cairo-clip.c
index 11ba54d..10d3068 100644
--- a/src/cairo-clip.c
+++ b/src/cairo-clip.c
@@ -74,7 +74,7 @@ _cairo_clip_path_create (cairo_clip_t *clip)
     return clip_path;
 }
 
-static cairo_clip_path_t *
+cairo_clip_path_t *
 _cairo_clip_path_reference (cairo_clip_path_t *clip_path)
 {
     assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&clip_path->ref_count));
diff --git a/src/cairo-cogl-context-private.h b/src/cairo-cogl-context-private.h
new file mode 100644
index 0000000..0a7185e
--- /dev/null
+++ b/src/cairo-cogl-context-private.h
@@ -0,0 +1,52 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.og/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * Contributor(s):
+ *      Robert Bragg <robert at linux.intel.com>
+ */
+
+#ifndef CAIRO_COGL_CONTEXT_PRIVATE_H
+#define CAIRO_COGL_CONTEXT_PRIVATE_H
+
+#include "cairo-default-context-private.h"
+#include "cairo-cogl-private.h"
+
+typedef struct _cairo_cogl_context {
+    cairo_default_context_t base;
+
+    cairo_cogl_device_t *dev;
+    int path_ctm_age;
+    cairo_path_fixed_t user_path;
+
+    cairo_bool_t path_is_rectangle;
+    double x, y, width, height;
+} cairo_cogl_context_t;
+
+cairo_t *
+_cairo_cogl_context_create (void *target);
+
+#endif /* CAIRO_COGL_CONTEXT_PRIVATE_H */
diff --git a/src/cairo-cogl-context.c b/src/cairo-cogl-context.c
new file mode 100644
index 0000000..e203125
--- /dev/null
+++ b/src/cairo-cogl-context.c
@@ -0,0 +1,820 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.og/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * Contributor(s):
+ *      Robert Bragg <robert at linux.intel.com>
+ */
+
+/* so long as we can verify that the ctm doesn't change multiple times
+ * during the construction of a path we can build a shadow
+ * cairo_path_fixed_t in user coordinates that we can use to create a
+ * hash value for caching tessellations of that path.
+ *
+ * We need to hook into all the points where the ctm can be changed
+ * so we can bump a cr->path_ctm_age counter.
+ *
+ * We need to hook into all the points where the path can be modified
+ * so we can catch the start of a path and reset the cr->path_ctm_age
+ * counter at that point.
+ *
+ * When a draw operation is hit we can then check that the
+ * path_ctm_age == 0 and if so we create a hash of the path.
+ *
+ * We use this hash to lookup a cairo_cogl_path_meta_t struct which
+ * may contain tessellated triangles for the path or may just contain
+ * a count of how many times the path has been re-seen (we only cache
+ * tessellated triangles if there is evidence that the path is being
+ * used multiple times because there is a cost involved in allocating
+ * a separate buffer for the triangles).
+ */
+
+#include "cairo-cogl-context-private.h"
+#include "cairo-freed-pool-private.h"
+#include "cairo-arc-private.h"
+#include "cairo-path-fixed-private.h"
+
+#include <glib.h>
+
+static freed_pool_t context_pool;
+
+void
+_cairo_cogl_context_reset_static_data (void)
+{
+    _freed_pool_reset (&context_pool);
+}
+
+static cairo_status_t
+_cairo_cogl_context_rectangle_real (cairo_cogl_context_t *cr,
+				    double x, double y,
+				    double width, double height)
+{
+    cairo_status_t status;
+    status = cr->dev->backend_parent.rectangle (cr, x, y, width, height);
+    if (unlikely (status))
+	return status;
+
+    return _cairo_cogl_path_fixed_rectangle (&cr->user_path,
+					     _cairo_fixed_from_double (x),
+					     _cairo_fixed_from_double (y),
+					     _cairo_fixed_from_double (width),
+					     _cairo_fixed_from_double (height));
+}
+
+/* The idea here is that we have a simplified way of tracking rectangle paths
+ * because rectangles are perhaps the most common shape drawn with cairo.
+ *
+ * Basically we have a speculative store for a rectangle path that doesn't
+ * need to use the cairo_path_fixed_t api to describe a rectangle in terms of
+ * (move_to,rel_line_to,rel_line_to,_rel_line_to,close) because if you profile
+ * heavy rectangle drawing with Cairo that process can be overly expensive.
+ *
+ * If the user asks to add more than just a rectangle to their current path
+ * then we "flush" any speculative rectangle stored into the current path
+ * before continuing to append their operations.
+ *
+ * In addition to the speculative store cairo-cogl also has a fast-path
+ * fill_rectangle drawing operation that futher aims to minimize the cost
+ * of drawing rectangles.
+ */
+static cairo_status_t
+_flush_cr_rectangle (cairo_cogl_context_t *cr)
+{
+    if (!cr->path_is_rectangle)
+	return CAIRO_STATUS_SUCCESS;
+
+    cr->path_is_rectangle = FALSE;
+    return _cairo_cogl_context_rectangle_real (cr, cr->x, cr->y, cr->width, cr->height);
+}
+
+static cairo_status_t
+_cairo_cogl_context_restore (void *abstract_cr)
+{
+    cairo_cogl_context_t *cr = abstract_cr;
+
+    if (cr->path_is_rectangle) {
+	cairo_status_t status = _flush_cr_rectangle (cr);
+	if (unlikely (status))
+	    return status;
+    }
+
+    cr->path_ctm_age++;
+    return cr->dev->backend_parent.restore (abstract_cr);
+}
+
+static cairo_status_t
+_cairo_cogl_context_translate (void *abstract_cr, double tx, double ty)
+{
+    cairo_cogl_context_t *cr = abstract_cr;
+
+    if (cr->path_is_rectangle) {
+	cairo_status_t status = _flush_cr_rectangle (cr);
+	if (unlikely (status))
+	    return status;
+    }
+
+    cr->path_ctm_age++;
+    return cr->dev->backend_parent.translate (abstract_cr, tx, ty);
+}
+
+static cairo_status_t
+_cairo_cogl_context_scale (void *abstract_cr, double sx, double sy)
+{
+    cairo_cogl_context_t *cr = abstract_cr;
+
+    if (cr->path_is_rectangle) {
+	cairo_status_t status = _flush_cr_rectangle (cr);
+	if (unlikely (status))
+	    return status;
+    }
+
+    cr->path_ctm_age++;
+    return cr->dev->backend_parent.scale (abstract_cr, sx, sy);
+}
+
+static cairo_status_t
+_cairo_cogl_context_rotate (void *abstract_cr, double theta)
+{
+    cairo_cogl_context_t *cr = abstract_cr;
+
+    if (cr->path_is_rectangle) {
+	cairo_status_t status = _flush_cr_rectangle (cr);
+	if (unlikely (status))
+	    return status;
+    }
+
+    cr->path_ctm_age++;
+    return cr->dev->backend_parent.rotate (abstract_cr, theta);
+}
+
+static cairo_status_t
+_cairo_cogl_context_transform (void *abstract_cr, const cairo_matrix_t *matrix)
+{
+    cairo_cogl_context_t *cr = abstract_cr;
+
+    if (cr->path_is_rectangle) {
+	cairo_status_t status = _flush_cr_rectangle (cr);
+	if (unlikely (status))
+	    return status;
+    }
+
+    cr->path_ctm_age++;
+    return cr->dev->backend_parent.transform (abstract_cr, matrix);
+}
+
+static cairo_status_t
+_cairo_cogl_context_set_matrix (void *abstract_cr, const cairo_matrix_t *matrix)
+{
+    cairo_cogl_context_t *cr = abstract_cr;
+
+    if (cr->path_is_rectangle) {
+	cairo_status_t status = _flush_cr_rectangle (cr);
+	if (unlikely (status))
+	    return status;
+    }
+
+    cr->path_ctm_age++;
+    return cr->dev->backend_parent.set_matrix (abstract_cr, matrix);
+}
+
+static cairo_status_t
+_cairo_cogl_context_set_identity_matrix (void *abstract_cr)
+{
+    cairo_cogl_context_t *cr = abstract_cr;
+
+    if (cr->path_is_rectangle) {
+	cairo_status_t status = _flush_cr_rectangle (cr);
+	if (unlikely (status))
+	    return status;
+    }
+
+    cr->path_ctm_age++;
+    return cr->dev->backend_parent.set_identity_matrix (abstract_cr);
+}
+
+static cairo_status_t
+_cairo_cogl_context_new_path (void *abstract_cr)
+{
+    cairo_cogl_context_t *cr = abstract_cr;
+    cairo_status_t status;
+
+    if (cr->path_is_rectangle) {
+	status = _flush_cr_rectangle (cr);
+	if (unlikely (status))
+	    return status;
+    }
+
+    status = cr->dev->backend_parent.new_path (abstract_cr);
+    if (unlikely (status))
+	return status;
+
+    _cairo_path_fixed_fini (&cr->user_path);
+    _cairo_path_fixed_init (&cr->user_path);
+    cr->path_is_rectangle = FALSE;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_cogl_context_new_sub_path (void *abstract_cr)
+{
+    cairo_cogl_context_t *cr = abstract_cr;
+    cairo_status_t status;
+
+    if (cr->path_is_rectangle) {
+	status = _flush_cr_rectangle (cr);
+	if (unlikely (status))
+	    return status;
+    }
+
+    status = cr->dev->backend_parent.new_sub_path (abstract_cr);
+    if (unlikely (status))
+	return status;
+
+    _cairo_path_fixed_new_sub_path (&cr->user_path);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_cogl_context_move_to (void *abstract_cr, double x, double y)
+{
+    cairo_cogl_context_t *cr = abstract_cr;
+    cairo_status_t status;
+    cairo_fixed_t x_fixed, y_fixed;
+
+    if (cr->path_is_rectangle) {
+	status = _flush_cr_rectangle (cr);
+	if (unlikely (status))
+	    return status;
+    }
+
+    status = cr->dev->backend_parent.move_to (abstract_cr, x, y);
+    if (unlikely (status))
+	return status;
+
+    x_fixed = _cairo_fixed_from_double (x);
+    y_fixed = _cairo_fixed_from_double (y);
+
+    return _cairo_path_fixed_move_to (&cr->user_path, x_fixed, y_fixed);
+}
+
+static cairo_status_t
+_cairo_cogl_context_line_to (void *abstract_cr, double x, double y)
+{
+    cairo_cogl_context_t *cr = abstract_cr;
+    cairo_status_t status;
+    cairo_fixed_t x_fixed, y_fixed;
+
+    if (cr->path_is_rectangle) {
+	status = _flush_cr_rectangle (cr);
+	if (unlikely (status))
+	    return status;
+    }
+
+    status = cr->dev->backend_parent.line_to (abstract_cr, x, y);
+    if (unlikely (status))
+	return status;
+
+    x_fixed = _cairo_fixed_from_double (x);
+    y_fixed = _cairo_fixed_from_double (y);
+
+    if (cr->user_path.buf.base.num_ops == 0)
+	cr->path_ctm_age = 0;
+
+    return _cairo_path_fixed_line_to (&cr->user_path, x_fixed, y_fixed);
+}
+
+static cairo_status_t
+_cairo_cogl_context_curve_to (void *abstract_cr,
+			       double x1, double y1,
+			       double x2, double y2,
+			       double x3, double y3)
+{
+    cairo_cogl_context_t *cr = abstract_cr;
+    cairo_status_t status;
+    cairo_fixed_t x1_fixed, y1_fixed;
+    cairo_fixed_t x2_fixed, y2_fixed;
+    cairo_fixed_t x3_fixed, y3_fixed;
+
+    if (cr->path_is_rectangle) {
+	status = _flush_cr_rectangle (cr);
+	if (unlikely (status))
+	    return status;
+    }
+
+    status = cr->dev->backend_parent.curve_to (abstract_cr, x1, y1, x2, y2, x3, y3);
+    if (unlikely (status))
+	return status;
+
+    x1_fixed = _cairo_fixed_from_double (x1);
+    y1_fixed = _cairo_fixed_from_double (y1);
+
+    x2_fixed = _cairo_fixed_from_double (x2);
+    y2_fixed = _cairo_fixed_from_double (y2);
+
+    x3_fixed = _cairo_fixed_from_double (x3);
+    y3_fixed = _cairo_fixed_from_double (y3);
+
+    if (cr->user_path.buf.base.num_ops == 0)
+	cr->path_ctm_age = 0;
+
+    return _cairo_path_fixed_curve_to (&cr->user_path,
+				       x1_fixed, y1_fixed,
+				       x2_fixed, y2_fixed,
+				       x3_fixed, y3_fixed);
+}
+
+static cairo_status_t
+_cairo_cogl_context_arc (void *abstract_cr,
+			  double xc, double yc, double radius,
+			  double angle1, double angle2,
+			  cairo_bool_t forward)
+{
+    cairo_cogl_context_t *cr = abstract_cr;
+    cairo_status_t status;
+
+    if (cr->path_is_rectangle) {
+	status = _flush_cr_rectangle (cr);
+	if (unlikely (status))
+	    return status;
+    }
+
+    status = cr->dev->backend_parent.arc (abstract_cr, xc, yc, radius, angle1, angle2, forward);
+    if (unlikely (status))
+	return status;
+
+    if (cr->user_path.buf.base.num_ops == 0)
+	cr->path_ctm_age = 0;
+
+    /* Do nothing, successfully, if radius is <= 0 */
+    if (radius <= 0.0) {
+	cairo_fixed_t x_fixed, y_fixed;
+
+	x_fixed = _cairo_fixed_from_double (xc);
+	y_fixed = _cairo_fixed_from_double (yc);
+	status = _cairo_path_fixed_line_to (&cr->user_path, x_fixed, y_fixed);
+	if (unlikely (status))
+	    return status;
+
+	status = _cairo_path_fixed_line_to (&cr->user_path, x_fixed, y_fixed);
+	if (unlikely (status))
+	    return status;
+
+	return CAIRO_STATUS_SUCCESS;
+    }
+
+    status = _cairo_cogl_context_line_to (cr,
+					  xc + radius * cos (angle1),
+					  yc + radius * sin (angle1));
+
+    if (unlikely (status))
+	return status;
+
+    if (forward)
+	_cairo_arc_path (&cr->base.base, xc, yc, radius, angle1, angle2);
+    else
+	_cairo_arc_path_negative (&cr->base.base, xc, yc, radius, angle1, angle2);
+
+    return CAIRO_STATUS_SUCCESS; /* any error will have already been set on cr */
+}
+
+static cairo_status_t
+_cairo_cogl_context_rel_move_to (void *abstract_cr, double dx, double dy)
+{
+    cairo_cogl_context_t *cr = abstract_cr;
+    cairo_status_t status;
+    cairo_fixed_t dx_fixed, dy_fixed;
+
+    if (cr->path_is_rectangle) {
+	status = _flush_cr_rectangle (cr);
+	if (unlikely (status))
+	    return status;
+    }
+
+    status = cr->dev->backend_parent.rel_move_to (abstract_cr, dx, dy);
+    if (unlikely (status))
+	return status;
+
+    dx_fixed = _cairo_fixed_from_double (dx);
+    dy_fixed = _cairo_fixed_from_double (dy);
+
+    return _cairo_path_fixed_rel_move_to (&cr->user_path, dx_fixed, dy_fixed);
+}
+
+static cairo_status_t
+_cairo_cogl_context_rel_line_to (void *abstract_cr, double dx, double dy)
+{
+    cairo_cogl_context_t *cr = abstract_cr;
+    cairo_status_t status;
+    cairo_fixed_t dx_fixed, dy_fixed;
+
+    if (cr->path_is_rectangle) {
+	status = _flush_cr_rectangle (cr);
+	if (unlikely (status))
+	    return status;
+    }
+
+    status = cr->dev->backend_parent.rel_line_to (abstract_cr, dx, dy);
+    if (unlikely (status))
+	return status;
+
+    dx_fixed = _cairo_fixed_from_double (dx);
+    dy_fixed = _cairo_fixed_from_double (dy);
+
+    if (cr->user_path.buf.base.num_ops == 0)
+	cr->path_ctm_age = 0;
+
+    return _cairo_path_fixed_rel_line_to (&cr->user_path, dx_fixed, dy_fixed);
+}
+
+
+static cairo_status_t
+_cairo_cogl_context_rel_curve_to (void *abstract_cr,
+				   double dx1, double dy1,
+				   double dx2, double dy2,
+				   double dx3, double dy3)
+{
+    cairo_cogl_context_t *cr = abstract_cr;
+    cairo_status_t status;
+    cairo_fixed_t dx1_fixed, dy1_fixed;
+    cairo_fixed_t dx2_fixed, dy2_fixed;
+    cairo_fixed_t dx3_fixed, dy3_fixed;
+
+    if (cr->path_is_rectangle) {
+	status = _flush_cr_rectangle (cr);
+	if (unlikely (status))
+	    return status;
+    }
+
+    status = cr->dev->backend_parent.rel_curve_to (abstract_cr, dx1, dy1, dx2, dy2, dx3, dy3);
+    if (unlikely (status))
+	return status;
+
+    dx1_fixed = _cairo_fixed_from_double (dx1);
+    dy1_fixed = _cairo_fixed_from_double (dy1);
+
+    dx2_fixed = _cairo_fixed_from_double (dx2);
+    dy2_fixed = _cairo_fixed_from_double (dy2);
+
+    dx3_fixed = _cairo_fixed_from_double (dx3);
+    dy3_fixed = _cairo_fixed_from_double (dy3);
+
+    if (cr->user_path.buf.base.num_ops == 0)
+	cr->path_ctm_age = 0;
+
+    return _cairo_path_fixed_rel_curve_to (&cr->user_path,
+					   dx1_fixed, dy1_fixed,
+					   dx2_fixed, dy2_fixed,
+					   dx3_fixed, dy3_fixed);
+}
+
+#if 0
+static cairo_status_t
+_cairo_cogl_context_arc_to (void *abstract_cr,
+			    double x1, double y1,
+			    double x2, double y2,
+			    double radius)
+{
+    cairo_cogl_context_t *cr = abstract_cr;
+    cairo_status_t status;
+
+    if (cr->path_is_rectangle) {
+	status = _flush_cr_rectangle (cr);
+	if (unlikely (status))
+	    return status;
+    }
+
+    status = cr->dev->backend_parent.arc_to (abstract_cr, x1, y1, x2, y2, radius);
+    if (unlikely (status))
+	return status;
+#warning "FIXME"
+}
+
+static cairo_status_t
+_cairo_cogl_rel_arc_to (void *cr,
+			double dx1, double dy1,
+			double dx2, double dy2,
+			double radius)
+{
+    cairo_cogl_context_t *cr = abstract_cr;
+    cairo_status_t status;
+
+    if (cr->path_is_rectangle) {
+	status = _flush_cr_rectangle (cr);
+	if (unlikely (status))
+	    return status;
+    }
+
+    status = cr->dev->backend_parent.rel_arc_to (abstract_cr, dx1, dy2, dx2, dy2, radius);
+    if (unlikely (status))
+	return status;
+#warning "FIXME"
+}
+#endif
+
+static cairo_status_t
+_cairo_cogl_context_close_path (void *abstract_cr)
+{
+    cairo_cogl_context_t *cr = abstract_cr;
+    cairo_status_t status;
+
+    if (cr->path_is_rectangle) {
+	status = _flush_cr_rectangle (cr);
+	if (unlikely (status))
+	    return status;
+    }
+
+    status = cr->dev->backend_parent.close_path (abstract_cr);
+    if (unlikely (status))
+	return status;
+
+    if (cr->user_path.buf.base.num_ops == 0)
+	cr->path_ctm_age = 0;
+
+    return _cairo_path_fixed_close_path (&cr->user_path);
+}
+
+static cairo_status_t
+_cairo_cogl_context_rectangle (void *abstract_cr,
+			       double x, double y,
+			       double width, double height)
+{
+    cairo_cogl_context_t *cr = abstract_cr;
+
+    if (cr->user_path.buf.base.num_ops == 0) {
+	cr->path_ctm_age = 0;
+
+#if 1
+	/* XXX: Since drawing rectangles is so common we have a
+	 * fast-path for drawing a single rectangle. */
+	cr->x = x;
+	cr->y = y;
+	cr->width = width;
+	cr->height = height;
+	cr->path_is_rectangle = TRUE;
+	return CAIRO_STATUS_SUCCESS;
+#endif
+    }
+
+    if (cr->path_is_rectangle) {
+	cairo_status_t status = _flush_cr_rectangle (cr);
+	if (unlikely (status))
+	    return status;
+    }
+
+    return _cairo_cogl_context_rectangle_real (cr, x, y, width, height);
+}
+
+/* Since the surface backend drawing operator functions don't get
+ * passed the current cairo_t context we don't have a good way
+ * to get our user-coordinates path into our surface operator
+ * functions.
+ *
+ * For now we use this function to set side band data on the surface
+ * itself.
+ */
+static void
+_cairo_cogl_surface_set_side_band_state (cairo_cogl_surface_t *surface,
+					 cairo_cogl_context_t *cr)
+{
+
+    if (cr->path_ctm_age <= 1) {
+	surface->user_path = &cr->user_path;
+	surface->ctm = &cr->base.gstate->ctm;
+	surface->ctm_inverse = &cr->base.gstate->ctm_inverse;
+	surface->path_is_rectangle = cr->path_is_rectangle;
+	if (surface->path_is_rectangle) {
+	    surface->path_rectangle_x = cr->x;
+	    surface->path_rectangle_y = cr->y;
+	    surface->path_rectangle_width = cr->width;
+	    surface->path_rectangle_height = cr->height;
+	}
+    } else {
+	surface->user_path = NULL;
+	surface->path_is_rectangle = FALSE;
+    }
+}
+
+static cairo_status_t
+_cairo_cogl_context_fill (void *abstract_cr)
+{
+    cairo_cogl_context_t *cr = abstract_cr;
+    cairo_status_t status;
+    cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)cr->base.gstate->target;
+
+    if (cr->path_is_rectangle) {
+	status = _cairo_cogl_surface_fill_rectangle (cr->base.gstate->target,
+						     cr->base.gstate->op,
+						     cr->base.gstate->source,
+						     cr->x,
+						     cr->y,
+						     cr->width,
+						     cr->height,
+						     &cr->base.gstate->ctm,
+						     cr->base.gstate->clip);
+	if (status == CAIRO_STATUS_SUCCESS)
+	    goto DONE;
+	_flush_cr_rectangle (cr);
+    }
+
+    _cairo_cogl_surface_set_side_band_state (surface, cr);
+
+    status = cr->dev->backend_parent.fill (abstract_cr);
+    if (unlikely (status))
+	return status;
+
+DONE:
+    _cairo_path_fixed_fini (&cr->user_path);
+    _cairo_path_fixed_init (&cr->user_path);
+    cr->path_is_rectangle = FALSE;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_cogl_context_fill_preserve (void *abstract_cr)
+{
+    cairo_cogl_context_t *cr = abstract_cr;
+    cairo_status_t status;
+    cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)cr->base.gstate->target;
+
+    _cairo_cogl_surface_set_side_band_state (surface, cr);
+
+    status = cr->dev->backend_parent.fill_preserve (abstract_cr);
+    if (unlikely (status))
+	return status;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_cogl_context_stroke (void *abstract_cr)
+{
+    cairo_cogl_context_t *cr = abstract_cr;
+    cairo_status_t status;
+    cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)cr->base.gstate->target;
+
+    _cairo_cogl_surface_set_side_band_state (surface, cr);
+
+    status = cr->dev->backend_parent.stroke (abstract_cr);
+    if (unlikely (status))
+	return status;
+
+    _cairo_path_fixed_fini (&cr->user_path);
+    _cairo_path_fixed_init (&cr->user_path);
+    cr->path_is_rectangle = FALSE;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_cogl_context_stroke_preserve (void *abstract_cr)
+{
+    cairo_cogl_context_t *cr = abstract_cr;
+    cairo_status_t status;
+    cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)cr->base.gstate->target;
+
+    _cairo_cogl_surface_set_side_band_state (surface, cr);
+
+    status = cr->dev->backend_parent.stroke_preserve (abstract_cr);
+    if (unlikely (status))
+	return status;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_cogl_context_clip (void *abstract_cr)
+{
+    cairo_cogl_context_t *cr = abstract_cr;
+    cairo_status_t status;
+
+    status = cr->dev->backend_parent.clip (abstract_cr);
+    if (unlikely (status))
+	return status;
+
+    _cairo_path_fixed_fini (&cr->user_path);
+    _cairo_path_fixed_init (&cr->user_path);
+    cr->path_is_rectangle = FALSE;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_cogl_context_destroy (void *abstract_cr)
+{
+    cairo_cogl_context_t *cr = abstract_cr;
+
+    _cairo_default_context_fini (&cr->base);
+
+    _cairo_path_fixed_fini (&cr->user_path);
+
+    /* mark the context as invalid to protect against misuse */
+    cr->base.base.status = CAIRO_STATUS_NULL_POINTER;
+    _freed_pool_put (&context_pool, cr);
+}
+
+/* We want to hook into the frontend of the path construction APIs so
+ * we can build up a path description in user coordinates instead of
+ * backend coordinates so that we can recognize user coordinate
+ * rectangles and so we can hash a user path independent of its
+ * transform. (With some care to catch unusual cases where the ctm
+ * changes mid-path) */
+cairo_t *
+_cairo_cogl_context_create (void *target)
+{
+    cairo_cogl_surface_t *surface = target;
+    cairo_cogl_context_t *cr;
+    cairo_status_t status;
+
+    cr = _freed_pool_get (&context_pool);
+    if (unlikely (cr == NULL)) {
+	cr = malloc (sizeof (cairo_cogl_context_t));
+	if (unlikely (cr == NULL))
+	    return _cairo_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+    }
+
+    status = _cairo_default_context_init (&cr->base, target);
+    if (unlikely (status)) {
+	_freed_pool_put (&context_pool, cr);
+	return _cairo_create_in_error (status);
+    }
+
+    cr->dev = (cairo_cogl_device_t *)surface->base.device;
+
+    if (unlikely (cr->dev->backend_vtable_initialized == FALSE)) {
+	cairo_backend_t *backend = &cr->dev->backend;
+	memcpy (backend, cr->base.base.backend, sizeof (cairo_backend_t));
+	memcpy (&cr->dev->backend_parent, cr->base.base.backend, sizeof (cairo_backend_t));
+
+	backend->destroy = _cairo_cogl_context_destroy;
+
+	backend->restore = _cairo_cogl_context_restore;
+	backend->translate = _cairo_cogl_context_translate;
+	backend->scale = _cairo_cogl_context_scale;
+	backend->rotate = _cairo_cogl_context_rotate;
+	backend->transform = _cairo_cogl_context_transform;
+	backend->set_matrix = _cairo_cogl_context_set_matrix;
+	backend->set_identity_matrix = _cairo_cogl_context_set_identity_matrix;
+
+	backend->new_path = _cairo_cogl_context_new_path;
+	backend->new_sub_path = _cairo_cogl_context_new_sub_path;
+	backend->move_to = _cairo_cogl_context_move_to;
+	backend->rel_move_to = _cairo_cogl_context_rel_move_to;
+	backend->line_to = _cairo_cogl_context_line_to;
+	backend->rel_line_to = _cairo_cogl_context_rel_line_to;
+	backend->curve_to = _cairo_cogl_context_curve_to;
+	backend->rel_curve_to = _cairo_cogl_context_rel_curve_to;
+#if 0
+	backend->arc_to = _cairo_cogl_context_arc_to;
+	backend->rel_arc_to = _cairo_cogl_context_rel_arc_to;
+#endif
+	backend->close_path = _cairo_cogl_context_close_path;
+	//backend->arc = _cairo_cogl_context_arc;
+	backend->rectangle = _cairo_cogl_context_rectangle;
+
+	/* Try to automatically catch if any new path APIs are added that mean
+	 * we may need to overload more functions... */
+	assert (((char *)&backend->path_extents - (char *)&backend->device_to_user_distance)
+		== (sizeof (void *) * 14));
+
+	backend->fill = _cairo_cogl_context_fill;
+	backend->fill_preserve = _cairo_cogl_context_fill_preserve;
+	backend->stroke = _cairo_cogl_context_stroke;
+	backend->stroke_preserve = _cairo_cogl_context_stroke_preserve;
+	backend->clip = _cairo_cogl_context_clip;
+
+	cr->dev->backend_vtable_initialized = TRUE;
+    }
+
+    cr->base.base.backend = &cr->dev->backend;
+
+    _cairo_path_fixed_init (&cr->user_path);
+    cr->path_is_rectangle = FALSE;
+
+    return &cr->base.base;
+}
diff --git a/src/cairo-cogl-gradient-private.h b/src/cairo-cogl-gradient-private.h
new file mode 100644
index 0000000..9589b07
--- /dev/null
+++ b/src/cairo-cogl-gradient-private.h
@@ -0,0 +1,89 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.og/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * Contributor(s):
+ *      Robert Bragg <robert at linux.intel.com>
+ */
+
+#ifndef CAIRO_COGL_GRADIENT_PRIVATE_H
+#define CAIRO_COGL_GRADIENT_PRIVATE_H
+
+#include "cairoint.h"
+#include "cairo-pattern-private.h"
+
+#include <cogl/cogl2-experimental.h>
+
+#define CAIRO_COGL_LINEAR_GRADIENT_CACHE_SIZE (1024 * 1024)
+
+typedef enum _cairo_cogl_gradient_compatability {
+    CAIRO_COGL_GRADIENT_CAN_EXTEND_PAD	    = 1<<0,
+    CAIRO_COGL_GRADIENT_CAN_EXTEND_REPEAT   = 1<<1,
+    CAIRO_COGL_GRADIENT_CAN_EXTEND_REFLECT  = 1<<2,
+    CAIRO_COGL_GRADIENT_CAN_EXTEND_NONE	    = 1<<3
+} cairo_cogl_gradient_compatability_t;
+#define CAIRO_COGL_GRADIENT_CAN_EXTEND_ALL (CAIRO_COGL_GRADIENT_CAN_EXTEND_PAD |\
+					    CAIRO_COGL_GRADIENT_CAN_EXTEND_REPEAT|\
+					    CAIRO_COGL_GRADIENT_CAN_EXTEND_REFLECT|\
+					    CAIRO_COGL_GRADIENT_CAN_EXTEND_NONE)
+
+typedef struct _cairo_cogl_linear_texture_entry {
+    cairo_cogl_gradient_compatability_t compatability;
+    CoglTexture	*texture;
+    float translate_x;
+    float scale_x;
+} cairo_cogl_linear_texture_entry_t;
+
+typedef struct _cairo_cogl_linear_gradient {
+    cairo_cache_entry_t		 cache_entry;
+    cairo_reference_count_t	 ref_count;
+    GList			*textures;
+    int				 n_stops;
+    const cairo_gradient_stop_t	*stops;
+    cairo_gradient_stop_t	 stops_embedded[1];
+} cairo_cogl_linear_gradient_t;
+
+cairo_int_status_t
+_cairo_cogl_get_linear_gradient (cairo_cogl_device_t *context,
+				 cairo_extend_t extend_mode,
+				 int n_stops,
+				 const cairo_gradient_stop_t *stops,
+				 cairo_cogl_linear_gradient_t **gradient_out);
+
+cairo_cogl_linear_texture_entry_t *
+_cairo_cogl_linear_gradient_texture_for_extend (cairo_cogl_linear_gradient_t *gradient,
+						cairo_extend_t extend_mode);
+
+cairo_cogl_linear_gradient_t *
+_cairo_cogl_linear_gradient_reference (cairo_cogl_linear_gradient_t *gradient);
+
+void
+_cairo_cogl_linear_gradient_destroy (cairo_cogl_linear_gradient_t *gradient);
+
+cairo_bool_t
+_cairo_cogl_linear_gradient_equal (const void *key_a, const void *key_b);
+
+#endif /* CAIRO_COGL_GRADIENT_PRIVATE_H */
diff --git a/src/cairo-cogl-gradient.c b/src/cairo-cogl-gradient.c
new file mode 100644
index 0000000..2dc0707
--- /dev/null
+++ b/src/cairo-cogl-gradient.c
@@ -0,0 +1,643 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.og/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * Contributor(s):
+ *      Robert Bragg <robert at linux.intel.com>
+ */
+//#include "cairoint.h"
+
+#include "cairo-cogl-private.h"
+#include "cairo-cogl-gradient-private.h"
+#include "cairo-image-surface-private.h"
+
+#include <cogl/cogl2-experimental.h>
+#include <glib.h>
+
+#define DUMP_GRADIENTS_TO_PNG
+
+static unsigned long
+_cairo_cogl_linear_gradient_hash (unsigned int                  n_stops,
+				  const cairo_gradient_stop_t  *stops)
+{
+    return _cairo_hash_bytes (n_stops, stops,
+                              sizeof (cairo_gradient_stop_t) * n_stops);
+}
+
+static cairo_cogl_linear_gradient_t *
+_cairo_cogl_linear_gradient_lookup (cairo_cogl_device_t         *ctx,
+				    unsigned long                 hash,
+				    unsigned int                  n_stops,
+				    const cairo_gradient_stop_t  *stops)
+{
+    cairo_cogl_linear_gradient_t lookup;
+
+    lookup.cache_entry.hash = hash,
+    lookup.n_stops = n_stops;
+    lookup.stops = stops;
+
+    return _cairo_cache_lookup (&ctx->linear_cache, &lookup.cache_entry);
+}
+
+cairo_bool_t
+_cairo_cogl_linear_gradient_equal (const void *key_a, const void *key_b)
+{
+    const cairo_cogl_linear_gradient_t *a = key_a;
+    const cairo_cogl_linear_gradient_t *b = key_b;
+
+    if (a->n_stops != b->n_stops)
+        return FALSE;
+
+    return memcmp (a->stops, b->stops, a->n_stops * sizeof (cairo_gradient_stop_t)) == 0;
+}
+
+cairo_cogl_linear_gradient_t *
+_cairo_cogl_linear_gradient_reference (cairo_cogl_linear_gradient_t *gradient)
+{
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&gradient->ref_count));
+
+    _cairo_reference_count_inc (&gradient->ref_count);
+
+    return gradient;
+}
+
+void
+_cairo_cogl_linear_gradient_destroy (cairo_cogl_linear_gradient_t *gradient)
+{
+    GList *l;
+
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&gradient->ref_count));
+
+    if (! _cairo_reference_count_dec_and_test (&gradient->ref_count))
+	return;
+
+    for (l = gradient->textures; l; l = l->next) {
+	cairo_cogl_linear_texture_entry_t *entry = l->data;
+	cogl_object_unref (entry->texture);
+	free (entry);
+    }
+    g_list_free (gradient->textures);
+
+    free (gradient);
+}
+
+static int
+_cairo_cogl_util_next_p2 (int a)
+{
+  int rval = 1;
+
+  while (rval < a)
+    rval <<= 1;
+
+  return rval;
+}
+
+static float
+get_max_color_component_range (const cairo_color_stop_t *color0, const cairo_color_stop_t *color1)
+{
+    float range;
+    float max = 0;
+
+    range = fabs (color0->red - color1->red);
+    max = MAX (range, max);
+    range = fabs (color0->green - color1->green);
+    max = MAX (range, max);
+    range = fabs (color0->blue - color1->blue);
+    max = MAX (range, max);
+    range = fabs (color0->alpha - color1->alpha);
+    max = MAX (range, max);
+
+    return max;
+}
+
+static int
+_cairo_cogl_linear_gradient_width_for_stops (cairo_extend_t		  extend,
+					     unsigned int                 n_stops,
+					     const cairo_gradient_stop_t *stops)
+{
+    unsigned int n;
+    float max_texels_per_unit_offset = 0;
+    float total_offset_range;
+
+    /* Find the stop pair demanding the most precision because we are
+     * interpolating the largest color-component range.
+     *
+     * From that we can define the relative sizes of all the other
+     * stop pairs within our texture and thus the overall size.
+     *
+     * To determine the maximum number of texels for a given gap we
+     * look at the range of colors we are expected to interpolate (so
+     * long as the stop offsets are not degenerate) and we simply
+     * assume we want one texel for each unique color value possible
+     * for a one byte-per-component representation.
+     * XXX: maybe this is overkill and just allowing 128 levels
+     * instead of 256 would be enough and then we'd rely on the
+     * bilinear filtering to give the full range.
+     *
+     * XXX: potentially we could try and map offsets to pixels to come
+     * up with a more precise mapping, but we are aiming to cache
+     * the gradients so we can't make assumptions about how it will be
+     * scaled in the future.
+     */
+    for (n = 1; n < n_stops; n++) {
+	float color_range;
+	float offset_range;
+	float texels;
+	float texels_per_unit_offset;
+
+	/* note: degenerate stops don't need to be represented in the
+	 * texture but we want to be sure that solid gaps get at least
+	 * one texel and all other gaps get at least 2 texels.
+	 */
+
+	if (stops[n].offset == stops[n-1].offset)
+	    continue;
+
+	color_range = get_max_color_component_range (&stops[n].color, &stops[n-1].color);
+	if (color_range == 0)
+	    texels = 1;
+	else
+	    texels = MAX (2, 256.0f * color_range);
+
+	/* So how many texels would we need to map over the full [0,1]
+	 * gradient range so this gap would have enough texels? ... */
+	offset_range = stops[n].offset - stops[n - 1].offset;
+	texels_per_unit_offset = texels / offset_range;
+
+	if (texels_per_unit_offset > max_texels_per_unit_offset)
+	    max_texels_per_unit_offset = texels_per_unit_offset;
+    }
+
+    total_offset_range = fabs (stops[n_stops - 1].offset - stops[0].offset);
+    return max_texels_per_unit_offset * total_offset_range;
+}
+
+/* Aim to create gradient textures without an alpha component so we can avoid
+ * needing to use blending... */
+static CoglPixelFormat
+_cairo_cogl_linear_gradient_format_for_stops (cairo_extend_t		   extend,
+					      unsigned int                 n_stops,
+					      const cairo_gradient_stop_t *stops)
+{
+    unsigned int n;
+
+    /* We have to add extra transparent texels to the end of the gradient to
+     * handle CAIRO_EXTEND_NONE... */
+    if (extend == CAIRO_EXTEND_NONE)
+	return COGL_PIXEL_FORMAT_BGRA_8888_PRE;
+
+    for (n = 1; n < n_stops; n++) {
+	if (stops[n].color.alpha != 1.0)
+	    return COGL_PIXEL_FORMAT_BGRA_8888_PRE;
+    }
+
+    return COGL_PIXEL_FORMAT_BGR_888;
+}
+
+static cairo_cogl_gradient_compatability_t
+_cairo_cogl_compatability_from_extend_mode (cairo_extend_t extend_mode)
+{
+    switch (extend_mode)
+    {
+    case CAIRO_EXTEND_NONE:
+	return CAIRO_COGL_GRADIENT_CAN_EXTEND_NONE;
+    case CAIRO_EXTEND_PAD:
+	return CAIRO_COGL_GRADIENT_CAN_EXTEND_PAD;
+    case CAIRO_EXTEND_REPEAT:
+	return CAIRO_COGL_GRADIENT_CAN_EXTEND_REPEAT;
+    case CAIRO_EXTEND_REFLECT:
+	return CAIRO_COGL_GRADIENT_CAN_EXTEND_REFLECT;
+    }
+
+    assert (0); /* not reached */
+    return CAIRO_EXTEND_NONE;
+}
+
+cairo_cogl_linear_texture_entry_t *
+_cairo_cogl_linear_gradient_texture_for_extend (cairo_cogl_linear_gradient_t *gradient,
+						cairo_extend_t extend_mode)
+{
+    GList *l;
+    cairo_cogl_gradient_compatability_t compatability =
+	_cairo_cogl_compatability_from_extend_mode (extend_mode);
+    for (l = gradient->textures; l; l = l->next) {
+	cairo_cogl_linear_texture_entry_t *entry = l->data;
+	if (entry->compatability & compatability)
+	    return entry;
+    }
+    return NULL;
+}
+
+static void
+color_stop_lerp (const cairo_color_stop_t *c0,
+		 const cairo_color_stop_t *c1,
+		 float factor,
+		 cairo_color_stop_t *dest)
+{
+    /* NB: we always ignore the short members in this file so we don't need to
+     * worry about initializing them here. */
+    dest->red = c0->red * (1.0f-factor) + c1->red * factor;
+    dest->green = c0->green * (1.0f-factor) + c1->green * factor;
+    dest->blue = c0->blue * (1.0f-factor) + c1->blue * factor;
+    dest->alpha = c0->alpha * (1.0f-factor) + c1->alpha * factor;
+}
+
+static size_t
+_cairo_cogl_linear_gradient_size (cairo_cogl_linear_gradient_t *gradient)
+{
+    GList *l;
+    size_t size = 0;
+    for (l = gradient->textures; l; l = l->next) {
+	cairo_cogl_linear_texture_entry_t *entry = l->data;
+	size += cogl_texture_get_width (entry->texture) * 4;
+    }
+    return size;
+}
+
+static void
+emit_stop (CoglVertexP2C4 **position,
+	   float left,
+	   float right,
+	   const cairo_color_stop_t *left_color,
+	   const cairo_color_stop_t *right_color)
+{
+    CoglVertexP2C4 *p = *position;
+
+    guint8 lr = left_color->red * 255;
+    guint8 lg = left_color->green * 255;
+    guint8 lb = left_color->blue * 255;
+    guint8 la = left_color->alpha * 255;
+
+    guint8 rr = right_color->red * 255;
+    guint8 rg = right_color->green * 255;
+    guint8 rb = right_color->blue * 255;
+    guint8 ra = right_color->alpha * 255;
+
+    p[0].x = left;
+    p[0].y = 0;
+    p[0].r = lr; p[0].g = lg; p[0].b = lb; p[0].a = la;
+    p[1].x = left;
+    p[1].y = 1;
+    p[1].r = lr; p[1].g = lg; p[1].b = lb; p[1].a = la;
+    p[2].x = right;
+    p[2].y = 1;
+    p[2].r = rr; p[2].g = rg; p[2].b = rb; p[2].a = ra;
+
+    p[3].x = left;
+    p[3].y = 0;
+    p[3].r = lr; p[3].g = lg; p[3].b = lb; p[3].a = la;
+    p[4].x = right;
+    p[4].y = 1;
+    p[4].r = rr; p[4].g = rg; p[4].b = rb; p[4].a = ra;
+    p[5].x = right;
+    p[5].y = 0;
+    p[5].r = rr; p[5].g = rg; p[5].b = rb; p[5].a = ra;
+
+    *position = &p[6];
+}
+
+#ifdef DUMP_GRADIENTS_TO_PNG
+static void
+dump_gradient_to_png (CoglTexture *texture)
+{
+    cairo_image_surface_t *image = (cairo_image_surface_t *)
+	cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+				    cogl_texture_get_width (texture),
+				    cogl_texture_get_height (texture));
+    CoglPixelFormat format;
+    static int gradient_id = 0;
+    char *gradient_name;
+
+    if (image->base.status)
+	return;
+
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+    format = COGL_PIXEL_FORMAT_BGRA_8888_PRE;
+#else
+    format = COGL_PIXEL_FORMAT_ARGB_8888_PRE;
+#endif
+    cogl_texture_get_data (texture,
+			   format,
+			   0,
+			   image->data);
+    gradient_name = g_strdup_printf ("./gradient%d.png", gradient_id++);
+    g_print ("writing gradient: %s\n", gradient_name);
+    cairo_surface_write_to_png ((cairo_surface_t *)image, gradient_name);
+    g_free (gradient_name);
+}
+#endif
+
+cairo_int_status_t
+_cairo_cogl_get_linear_gradient (cairo_cogl_device_t *device,
+				 cairo_extend_t extend_mode,
+				 int n_stops,
+				 const cairo_gradient_stop_t *stops,
+				 cairo_cogl_linear_gradient_t **gradient_out)
+{
+    unsigned long hash;
+    cairo_cogl_linear_gradient_t *gradient;
+    cairo_cogl_linear_texture_entry_t *entry;
+    cairo_gradient_stop_t *internal_stops;
+    int stop_offset;
+    int n_internal_stops;
+    int n;
+    cairo_cogl_gradient_compatability_t compatabilities;
+    int width;
+    int left_padding = 0;
+    cairo_color_stop_t left_padding_color;
+    int right_padding = 0;
+    cairo_color_stop_t right_padding_color;
+    CoglPixelFormat format;
+    CoglTexture2D *tex;
+    GError *error = NULL;
+    int un_padded_width;
+    CoglHandle offscreen;
+    cairo_int_status_t status;
+    int n_quads;
+    int n_vertices;
+    float prev;
+    float right;
+    CoglVertexP2C4 *vertices;
+    CoglVertexP2C4 *p;
+    CoglPrimitive *prim;
+
+    hash = _cairo_cogl_linear_gradient_hash (n_stops, stops);
+
+    gradient = _cairo_cogl_linear_gradient_lookup (device, hash, n_stops, stops);
+    if (gradient) {
+	cairo_cogl_linear_texture_entry_t *entry =
+	    _cairo_cogl_linear_gradient_texture_for_extend (gradient, extend_mode);
+	if (entry) {
+	    *gradient_out = _cairo_cogl_linear_gradient_reference (gradient);
+	    return CAIRO_INT_STATUS_SUCCESS;
+	}
+    }
+
+    if (!gradient) {
+	gradient = malloc (sizeof (cairo_cogl_linear_gradient_t) +
+			   sizeof (cairo_gradient_stop_t) * (n_stops - 1));
+	if (!gradient)
+	    return CAIRO_INT_STATUS_NO_MEMORY;
+
+	CAIRO_REFERENCE_COUNT_INIT (&gradient->ref_count, 1);
+	/* NB: we update the cache_entry size at the end before
+	 * [re]adding it to the cache. */
+	gradient->cache_entry.hash = hash;
+	gradient->textures = NULL;
+	gradient->n_stops = n_stops;
+	gradient->stops = gradient->stops_embedded;
+	memcpy (gradient->stops_embedded, stops, sizeof (cairo_gradient_stop_t) * n_stops);
+    } else
+	_cairo_cogl_linear_gradient_reference (gradient);
+
+    entry = malloc (sizeof (cairo_cogl_linear_texture_entry_t));
+    if (!entry) {
+	status = CAIRO_INT_STATUS_NO_MEMORY;
+	goto BAIL;
+    }
+
+    compatabilities = _cairo_cogl_compatability_from_extend_mode (extend_mode);
+
+    n_internal_stops = n_stops;
+    stop_offset = 0;
+
+    /* We really need stops covering the full [0,1] range for repeat/reflect
+     * if we want to use sampler REPEAT/MIRROR wrap modes so we may need
+     * to add some extra stops... */
+    if (extend_mode == CAIRO_EXTEND_REPEAT || extend_mode == CAIRO_EXTEND_REFLECT)
+    {
+	/* If we don't need any extra stops then actually the texture
+	 * will be shareable for repeat and reflect... */
+	compatabilities = (CAIRO_COGL_GRADIENT_CAN_EXTEND_REPEAT |
+			   CAIRO_COGL_GRADIENT_CAN_EXTEND_REFLECT);
+
+	if (stops[0].offset != 0) {
+	    n_internal_stops++;
+	    stop_offset++;
+	}
+
+	if (stops[n_stops - 1].offset != 1)
+	    n_internal_stops++;
+    }
+
+    internal_stops = alloca (n_internal_stops * sizeof (cairo_gradient_stop_t));
+    memcpy (&internal_stops[stop_offset], stops, sizeof (cairo_gradient_stop_t) * n_stops);
+
+    /* cairo_color_stop_t values are all unpremultiplied but we need to
+     * interpolate premultiplied colors so we premultiply all the double
+     * components now. (skipping any extra stops added for repeat/reflect)
+     *
+     * Anothing thing to note is that by premultiplying the colors
+     * early we'll also reduce the range of colors to interpolate
+     * which can result in smaller gradient textures.
+     */
+    for (n = stop_offset; n < n_stops; n++) {
+	cairo_color_stop_t *color = &internal_stops[n].color;
+	color->red *= color->alpha;
+	color->green *= color->alpha;
+	color->blue *= color->alpha;
+    }
+
+    if (n_internal_stops != n_stops)
+    {
+	if (extend_mode == CAIRO_EXTEND_REPEAT) {
+	    compatabilities &= ~CAIRO_COGL_GRADIENT_CAN_EXTEND_REFLECT;
+	    if (stops[0].offset != 0) {
+		/* what's the wrap-around distance between the user's end-stops? */
+		double dx = (1.0 - stops[n_stops - 1].offset) + stops[0].offset;
+		internal_stops[0].offset = 0;
+		color_stop_lerp (&stops[0].color,
+				 &stops[n_stops - 1].color,
+				 stops[0].offset / dx,
+				 &internal_stops[0].color);
+	    }
+	    if (stops[n_stops - 1].offset != 1) {
+		internal_stops[n_internal_stops - 1].offset = 1;
+		internal_stops[n_internal_stops - 1].color = internal_stops[0].color;
+	    }
+	} else if (extend_mode == CAIRO_EXTEND_REFLECT) {
+	    compatabilities &= ~CAIRO_COGL_GRADIENT_CAN_EXTEND_REPEAT;
+	    if (stops[0].offset != 0) {
+		internal_stops[0].offset = 0;
+		internal_stops[0].color = stops[n_stops - 1].color;
+	    }
+	    if (stops[n_stops - 1].offset != 1) {
+		internal_stops[n_internal_stops - 1].offset = 1;
+		internal_stops[n_internal_stops - 1].color = stops[0].color;
+	    }
+	}
+    }
+
+    stops = internal_stops;
+    n_stops = n_internal_stops;
+
+    width = _cairo_cogl_linear_gradient_width_for_stops (extend_mode, n_stops, stops);
+
+    if (extend_mode == CAIRO_EXTEND_PAD) {
+
+	/* Here we need to guarantee that the edge texels of our
+	 * texture correspond to the desired padding color so we
+	 * can use CLAMP_TO_EDGE.
+	 *
+	 * For short stop-gaps and especially for degenerate stops
+	 * it's possible that without special consideration the
+	 * user's end stop colors would not be present in our final
+	 * texture.
+	 *
+	 * To handle this we forcibly add two extra padding texels
+	 * at the edges which extend beyond the [0,1] range of the
+	 * gradient itself and we will later report a translate and
+	 * scale transform to compensate for this.
+	 */
+
+	/* XXX: If we consider generating a mipmap for our 1d texture
+	 * at some point then we also need to consider how much
+	 * padding to add to be sure lower mipmap levels still have
+	 * the desired edge color (as opposed to a linear blend with
+	 * other colors of the gradient).
+	 */
+
+	left_padding = 1;
+	left_padding_color = stops[0].color;
+	right_padding = 1;
+	right_padding_color = stops[n_stops - 1].color;
+    } else if (extend_mode == CAIRO_EXTEND_NONE) {
+	/* We handle EXTEND_NONE by adding two extra, transparent, texels at
+	 * the ends of the texture and use CLAMP_TO_EDGE.
+	 *
+	 * We add a scale and translate transform so to account for our texels
+	 * extending beyond the [0,1] range. */
+
+	left_padding = 1;
+	left_padding_color.red = 0;
+	left_padding_color.green = 0;
+	left_padding_color.blue = 0;
+	left_padding_color.alpha = 0;
+	right_padding = 1;
+	right_padding_color = left_padding_color;
+    }
+
+    /* If we still have stops that don't cover the full [0,1] range
+     * then we need to define a texture-coordinate scale + translate
+     * transform to account for that... */
+    if (stops[n_stops - 1].offset - stops[0].offset < 1) {
+	float range = stops[n_stops - 1].offset - stops[0].offset;
+	entry->scale_x = 1.0 / range;
+	entry->translate_x = -(stops[0].offset * entry->scale_x);
+    }
+
+    width += left_padding + right_padding;
+
+    width = _cairo_cogl_util_next_p2 (width);
+    width = MIN (4096, width); /* lets not go too stupidly big! */
+    format = _cairo_cogl_linear_gradient_format_for_stops (extend_mode, n_stops, stops);
+
+    do {
+	tex = cogl_texture_2d_new_with_size (device->cogl_context,
+					     width,
+					     1,
+					     format,
+					     &error);
+	if (!tex)
+	    g_error_free (error);
+    } while (tex == NULL && width >> 1);
+
+    if (!tex) {
+	status = CAIRO_INT_STATUS_NO_MEMORY;
+	goto BAIL;
+    }
+
+    entry->texture = COGL_TEXTURE (tex);
+    entry->compatability = compatabilities;
+
+    un_padded_width = width - left_padding - right_padding;
+
+    /* XXX: only when we know the final texture width can we calculate the
+     * scale and translate factors needed to account for padding... */
+    if (un_padded_width != width)
+	entry->scale_x *= (float)un_padded_width / (float)width;
+    if (left_padding)
+	entry->translate_x += (entry->scale_x / (float)un_padded_width) * (float)left_padding;
+
+    offscreen = cogl_offscreen_new_to_texture (tex);
+    cogl_push_framebuffer (COGL_FRAMEBUFFER (offscreen));
+    cogl_ortho (0, width, 1, 0, -1, 100);
+    cogl_framebuffer_clear4f (COGL_FRAMEBUFFER (offscreen),
+			      COGL_BUFFER_BIT_COLOR,
+			      0, 0, 0, 0);
+
+    n_quads = n_stops - 1 + !!left_padding + !!right_padding;
+    n_vertices = 6 * n_quads;
+    vertices = alloca (sizeof (CoglVertexP2C4) * n_vertices);
+    p = vertices;
+    if (left_padding)
+	emit_stop (&p, 0, left_padding, &left_padding_color, &left_padding_color);
+    prev = (float)left_padding;
+    for (n = 1; n < n_stops; n++) {
+	right = (float)left_padding + (float)un_padded_width * stops[n].offset;
+	emit_stop (&p, prev, right, &stops[n-1].color, &stops[n].color);
+	prev = right;
+    }
+    if (right_padding)
+	emit_stop (&p, prev, width, &right_padding_color, &right_padding_color);
+
+    prim = cogl_primitive_new_p2c4 (COGL_VERTICES_MODE_TRIANGLES,
+				    n_vertices,
+				    vertices);
+    /* Just use this as the simplest way to setup a default pipeline... */
+    cogl_set_source_color4f (0, 0, 0, 0);
+    cogl_primitive_draw (prim);
+    cogl_object_unref (prim);
+
+    cogl_pop_framebuffer ();
+    cogl_object_unref (offscreen);
+
+    gradient->textures = g_list_prepend (gradient->textures, entry);
+    gradient->cache_entry.size = _cairo_cogl_linear_gradient_size (gradient);
+
+#ifdef DUMP_GRADIENTS_TO_PNG
+    dump_gradient_to_png (COGL_TEXTURE (tex));
+#endif
+
+#warning "FIXME:"
+    /* XXX: it seems the documentation of _cairo_cache_insert isn't true - it
+     * doesn't handle re-adding the same entry gracefully - the cache will
+     * just keep on growing and then it will start randomly evicting things
+     * pointlessly */
+    /* we ignore errors here and just return an uncached gradient */
+    if (likely (! _cairo_cache_insert (&device->linear_cache, &gradient->cache_entry)))
+        _cairo_cogl_linear_gradient_reference (gradient);
+
+    *gradient_out = gradient;
+    return CAIRO_INT_STATUS_SUCCESS;
+
+BAIL:
+    if (entry)
+	free (entry);
+    if (gradient)
+	_cairo_cogl_linear_gradient_destroy (gradient);
+    return status;
+}
diff --git a/src/cairo-cogl-private.h b/src/cairo-cogl-private.h
new file mode 100644
index 0000000..13fe5a8
--- /dev/null
+++ b/src/cairo-cogl-private.h
@@ -0,0 +1,164 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.og/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * Contributor(s):
+ *      Robert Bragg <robert at linux.intel.com>
+ */
+
+#ifndef CAIRO_COGL_PRIVATE_H
+#define CAIRO_COGL_PRIVATE_H
+
+#include "cairo-device-private.h"
+#include "cairo-cache-private.h"
+#include "cairo-backend-private.h"
+#include "cairo-default-context-private.h"
+#include "cairo-surface-private.h"
+
+#include <cogl/cogl2-experimental.h>
+
+typedef enum _cairo_cogl_template_type {
+    CAIRO_COGL_TEMPLATE_TYPE_SOLID,
+    CAIRO_COGL_TEMPLATE_TYPE_TEXTURE,
+    CAIRO_COGL_TEMPLATE_TYPE_MASK_SOLID,
+    CAIRO_COGL_TEMPLATE_TYPE_MASK_TEXTURE,
+    CAIRO_COGL_TEMPLATE_TYPE_COUNT
+} cairo_cogl_template_type;
+
+typedef struct _cairo_cogl_device {
+    cairo_device_t base;
+
+    cairo_bool_t backend_vtable_initialized;
+    cairo_backend_t backend;
+
+    /* We save a copy of all the original backend methods that we override so
+     * we can chain up...
+     */
+    cairo_backend_t backend_parent;
+
+    CoglContext *cogl_context;
+
+    CoglTexture *dummy_texture;
+
+    /* This is a sparsely filled set of templates because we don't support
+     * the full range of operators that cairo has. All entries corresponding
+     * to unsupported operators are NULL.
+     *
+     * The CAIRO_OPERATOR_ADD is the operator enum with the highest value that
+     * we support so we at least cap the size of the array by that.
+     *
+     * For each operator we have a template for when we have a solid source
+     * and another for each texture format that could be used as a source.
+     */
+    CoglPipeline *template_pipelines[CAIRO_OPERATOR_ADD + 1][CAIRO_COGL_TEMPLATE_TYPE_COUNT];
+
+    CoglMatrix identity;
+
+    /* Caches 1d linear gradient textures */
+    cairo_cache_t linear_cache;
+
+    cairo_cache_t path_fill_staging_cache;
+    cairo_cache_t path_fill_prim_cache;
+    cairo_cache_t path_stroke_staging_cache;
+    cairo_cache_t path_stroke_prim_cache;
+} cairo_cogl_device_t;
+
+typedef struct _cairo_cogl_clip_primitives {
+    cairo_t *clip;
+    CoglPrimitive **primitives;
+} cairo_cogl_clip_primitives_t;
+
+typedef struct _cairo_cogl_surface {
+    cairo_surface_t base;
+
+    CoglPixelFormat cogl_format;
+    cairo_bool_t ignore_alpha;
+
+    /* We currently have 3 basic kinds of Cogl surfaces:
+     * 1) A light surface simply wrapping a CoglTexture
+     * 2) A CoglOffscreen framebuffer that implicitly also wraps a CoglTexture
+     * 3) A CoglOnscreen framebuffer which could potentially be mapped to
+     *    a CoglTexture (e.g. via tfp on X11) but we don't currently do
+     *    that.
+     */
+
+    CoglTexture *texture;
+    CoglFramebuffer *framebuffer;
+
+    int width;
+    int height;
+
+    GQueue *journal;
+
+    CoglAttributeBuffer *buffer_stack;
+    size_t buffer_stack_size;
+    size_t buffer_stack_offset;
+    guint8 *buffer_stack_pointer;
+
+    cairo_clip_t *last_clip;
+
+    /* A small fifo of recently used cairo_clip_ts paired with CoglPrimitives
+     * that can be used to mask the stencil buffer. */
+    GList *clips_fifo;
+
+    int n_clip_updates_per_frame;
+
+    /* Since the surface backend drawing operator functions don't get
+     * passed the current cairo_t context we don't have a good way
+     * to get our user-coordinates path into our surface_fill function.
+     *
+     * For now we use our _cairo_cogl_context_fill() wrapper to set this
+     * side band data on the surface...
+     */
+    cairo_path_fixed_t *user_path;
+    cairo_matrix_t *ctm;
+    cairo_matrix_t *ctm_inverse;
+    cairo_bool_t path_is_rectangle;
+    double path_rectangle_x;
+    double path_rectangle_y;
+    double path_rectangle_width;
+    double path_rectangle_height;
+} cairo_cogl_surface_t;
+
+cairo_status_t
+_cairo_cogl_path_fixed_rectangle (cairo_path_fixed_t *path,
+				  cairo_fixed_t x,
+				  cairo_fixed_t y,
+				  cairo_fixed_t width,
+				  cairo_fixed_t height);
+
+cairo_int_status_t
+_cairo_cogl_surface_fill_rectangle (void		     *abstract_surface,
+				    cairo_operator_t	      op,
+				    const cairo_pattern_t    *source,
+				    double		      x,
+				    double		      y,
+				    double		      width,
+				    double		      height,
+				    cairo_matrix_t	     *ctm,
+				    const cairo_clip_t	     *clip);
+
+#endif /* CAIRO_COGL_PRIVATE_H */
diff --git a/src/cairo-cogl-surface.c b/src/cairo-cogl-surface.c
new file mode 100644
index 0000000..0d990b3
--- /dev/null
+++ b/src/cairo-cogl-surface.c
@@ -0,0 +1,2802 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.og/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * Contributor(s):
+ *      Robert Bragg <robert at linux.intel.com>
+ */
+#include "cairoint.h"
+
+#include "cairo-cache-private.h"
+#include "cairo-error-private.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-recording-surface-private.h"
+#include "cairo-surface-clipper-private.h"
+#include "cairo-fixed-private.h"
+#include "cairo-device-private.h"
+#include "cairo-composite-rectangles-private.h"
+#include "cairo-image-surface-private.h"
+#include "cairo-cogl-private.h"
+#include "cairo-cogl-gradient-private.h"
+#include "cairo-arc-private.h"
+#include "cairo-traps-private.h"
+#include "cairo-cogl-context-private.h"
+#include "cairo-cogl-utils-private.h"
+#include "cairo-box-private.h"
+#include "cairo-surface-subsurface-private.h"
+#include "cairo-surface-fallback-private.h"
+#include "cairo-surface-offset-private.h"
+
+#include "cairo-cogl.h"
+
+#include <cogl/cogl2-experimental.h>
+#include <glib.h>
+
+#define CAIRO_COGL_DEBUG 0
+//#define FILL_WITH_COGL_PATH
+//#define USE_CAIRO_PATH_FLATTENER
+#define ENABLE_PATH_CACHE
+//#define DISABLE_BATCHING
+#define USE_COGL_RECTANGLE_API
+#define ENABLE_RECTANGLES_FASTPATH
+
+#if defined (USE_COGL_RECTANGLE_API) || defined (ENABLE_PATH_CACHE)
+#define NEED_COGL_CONTEXT
+#endif
+
+#if CAIRO_COGL_DEBUG && __GNUC__
+#define UNSUPPORTED(reason) ({ \
+    g_warning ("cairo-cogl: hit unsupported operation: %s", reason); \
+    CAIRO_INT_STATUS_UNSUPPORTED; \
+})
+#else
+#define UNSUPPORTED(reason) CAIRO_INT_STATUS_UNSUPPORTED
+#endif
+
+#define CAIRO_COGL_PATH_META_CACHE_SIZE (1024 * 1024)
+
+typedef struct _cairo_cogl_texture_attributes {
+    /* nabbed from cairo_surface_attributes_t... */
+    cairo_matrix_t	    matrix;
+    cairo_extend_t	    extend;
+    cairo_filter_t	    filter;
+    cairo_bool_t	    has_component_alpha;
+
+    CoglPipelineWrapMode    s_wrap;
+    CoglPipelineWrapMode    t_wrap;
+} cairo_cogl_texture_attributes_t;
+
+typedef enum _cairo_cogl_journal_entry_type {
+    CAIRO_COGL_JOURNAL_ENTRY_TYPE_RECTANGLE,
+    CAIRO_COGL_JOURNAL_ENTRY_TYPE_PRIMITIVE,
+    CAIRO_COGL_JOURNAL_ENTRY_TYPE_PATH,
+    CAIRO_COGL_JOURNAL_ENTRY_TYPE_CLIP
+} cairo_cogl_journal_entry_type_t;
+
+typedef struct _cairo_cogl_journal_entry {
+    cairo_cogl_journal_entry_type_t type;
+} cairo_cogl_journal_entry_t;
+
+typedef struct _cairo_cogl_journal_clip_entry {
+    cairo_cogl_journal_entry_t base;
+    cairo_clip_t *clip;
+} cairo_cogl_journal_clip_entry_t;
+
+typedef struct _cairo_cogl_journal_rect_entry {
+    cairo_cogl_journal_entry_t base;
+    CoglPipeline *pipeline;
+    float x;
+    float y;
+    float width;
+    float height;
+    int n_layers;
+    cairo_matrix_t ctm;
+} cairo_cogl_journal_rect_entry_t;
+
+typedef struct _cairo_cogl_journal_prim_entry {
+    cairo_cogl_journal_entry_t base;
+    CoglPipeline *pipeline;
+    CoglPrimitive *primitive;
+    gboolean has_transform;
+    cairo_matrix_t transform;
+} cairo_cogl_journal_prim_entry_t;
+
+typedef struct _cairo_cogl_journal_path_entry {
+    cairo_cogl_journal_entry_t base;
+    CoglPipeline *pipeline;
+    CoglPath *path;
+} cairo_cogl_journal_path_entry_t;
+
+typedef struct _cairo_cogl_path_fill_meta {
+    cairo_cache_entry_t	cache_entry;
+    cairo_reference_count_t ref_count;
+    int counter;
+    cairo_path_fixed_t *user_path;
+    cairo_matrix_t ctm_inverse;
+
+    /* TODO */
+#if 0
+    /* A cached path tessellation should be re-usable with different rotations
+     * and translations but not for different scales.
+     *
+     * one idea is to track the diagonal lenghts of a unit rectangle
+     * transformed through the original ctm use to tesselate the geometry
+     * so we can check what the lengths are for any new ctm to know if
+     * this geometry is compatible.
+     */
+#endif
+
+    CoglPrimitive *prim;
+} cairo_cogl_path_fill_meta_t;
+
+typedef struct _cairo_cogl_path_stroke_meta {
+    cairo_cache_entry_t	cache_entry;
+    cairo_reference_count_t ref_count;
+    int counter;
+    cairo_path_fixed_t *user_path;
+    cairo_matrix_t ctm_inverse;
+    cairo_stroke_style_t style;
+    double tolerance;
+
+    /* TODO */
+#if 0
+    /* A cached path tessellation should be re-usable with different rotations
+     * and translations but not for different scales.
+     *
+     * one idea is to track the diagonal lenghts of a unit rectangle
+     * transformed through the original ctm use to tesselate the geometry
+     * so we can check what the lengths are for any new ctm to know if
+     * this geometry is compatible.
+     */
+#endif
+
+    CoglPrimitive *prim;
+} cairo_cogl_path_stroke_meta_t;
+
+static cairo_surface_t *
+_cairo_cogl_surface_create_full (cairo_cogl_device_t *dev,
+				 cairo_bool_t ignore_alpha,
+				 CoglFramebuffer *framebuffer,
+				 CoglTexture *texture);
+
+static cairo_int_status_t
+_cairo_cogl_surface_fill (void			    *abstract_surface,
+                          cairo_operator_t	     op,
+                          const cairo_pattern_t	    *source,
+                          const cairo_path_fixed_t  *path,
+                          cairo_fill_rule_t	     fill_rule,
+                          double		     tolerance,
+                          cairo_antialias_t	     antialias,
+                          const cairo_clip_t	    *clip);
+
+static void
+_cairo_cogl_journal_flush (cairo_cogl_surface_t *surface);
+
+cairo_private extern const cairo_surface_backend_t _cairo_cogl_surface_backend;
+
+slim_hidden_proto (cairo_cogl_device_create);
+slim_hidden_proto (cairo_cogl_surface_create);
+slim_hidden_proto (cairo_cogl_surface_get_framebuffer);
+slim_hidden_proto (cairo_cogl_surface_get_texture);
+slim_hidden_proto (cairo_cogl_surface_end_frame);
+
+static cairo_cogl_device_t *
+to_device (cairo_device_t *device)
+{
+    return (cairo_cogl_device_t *)device;
+}
+
+/* moves trap points such that they become the actual corners of the trapezoid */
+static void
+_sanitize_trap (cairo_trapezoid_t *t)
+{
+    cairo_trapezoid_t s = *t;
+
+#define FIX(lr, tb, p) \
+    if (t->lr.p.y != t->tb) { \
+        t->lr.p.x = s.lr.p2.x + _cairo_fixed_mul_div_floor (s.lr.p1.x - s.lr.p2.x, s.tb - s.lr.p2.y, s.lr.p1.y - s.lr.p2.y); \
+        t->lr.p.y = s.tb; \
+    }
+    FIX (left,  top,    p1);
+    FIX (left,  bottom, p2);
+    FIX (right, top,    p1);
+    FIX (right, bottom, p2);
+}
+
+static cairo_status_t
+_cairo_cogl_surface_ensure_framebuffer (cairo_cogl_surface_t *surface)
+{
+    GError *error = NULL;
+
+    if (surface->framebuffer)
+	return CAIRO_STATUS_SUCCESS;
+
+    surface->framebuffer = COGL_FRAMEBUFFER (cogl_offscreen_new_to_texture (surface->texture));
+    if (!cogl_framebuffer_allocate (surface->framebuffer, &error)) {
+	g_error_free (error);
+	cogl_object_unref (surface->framebuffer);
+	surface->framebuffer = NULL;
+	return CAIRO_STATUS_NO_MEMORY;
+    }
+
+    cogl_push_framebuffer (surface->framebuffer);
+    cogl_ortho (0, surface->width,
+		surface->height, 0,
+		-1, 100);
+    cogl_pop_framebuffer ();
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_surface_t *
+_cairo_cogl_surface_create_similar (void            *abstract_surface,
+				    cairo_content_t  content,
+				    int              width,
+				    int              height)
+{
+    cairo_cogl_surface_t *reference_surface = abstract_surface;
+    cairo_cogl_surface_t *surface;
+    CoglTexture *texture;
+    cairo_status_t status;
+
+    texture = cogl_texture_new_with_size (width, height,
+					  COGL_TEXTURE_NO_SLICING,
+					  (content & CAIRO_CONTENT_COLOR) ?
+					  COGL_PIXEL_FORMAT_BGRA_8888_PRE :
+					  COGL_PIXEL_FORMAT_A_8);
+    if (!texture)
+        return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    surface = (cairo_cogl_surface_t *)
+	_cairo_cogl_surface_create_full (to_device(reference_surface->base.device),
+					 (content & CAIRO_CONTENT_ALPHA) == 0,
+					 NULL,
+					 texture);
+    if (unlikely (surface->base.status))
+	return &surface->base;
+
+    status = _cairo_cogl_surface_ensure_framebuffer (surface);
+    if (unlikely (status)) {
+	cairo_surface_destroy (&surface->base);
+	return _cairo_surface_create_in_error (status);
+    }
+
+    return &surface->base;
+}
+
+static cairo_bool_t
+_cairo_cogl_surface_get_extents (void *abstract_surface,
+                                 cairo_rectangle_int_t *extents)
+{
+    cairo_cogl_surface_t *surface = abstract_surface;
+
+    extents->x = 0;
+    extents->y = 0;
+    extents->width  = surface->width;
+    extents->height = surface->height;
+
+    return TRUE;
+}
+
+static void
+_cairo_cogl_journal_free (cairo_cogl_surface_t *surface)
+{
+    GList *l;
+
+    for (l = surface->journal->head; l; l = l->next) {
+	cairo_cogl_journal_entry_t *entry = l->data;
+
+	if (entry->type == CAIRO_COGL_JOURNAL_ENTRY_TYPE_PRIMITIVE) {
+	    cairo_cogl_journal_prim_entry_t *prim_entry =
+		(cairo_cogl_journal_prim_entry_t *)entry;
+	    cogl_object_unref (prim_entry->primitive);
+	} else if (entry->type == CAIRO_COGL_JOURNAL_ENTRY_TYPE_PATH) {
+	    cairo_cogl_journal_path_entry_t *path_entry =
+		(cairo_cogl_journal_path_entry_t *)entry;
+	    cogl_object_unref (path_entry->path);
+	}
+    }
+
+    g_queue_free (surface->journal);
+    surface->journal = NULL;
+}
+
+#ifdef FILL_WITH_COGL_PATH
+static void
+_cairo_cogl_journal_log_path (cairo_cogl_surface_t *surface,
+			      CoglPipeline *pipeline,
+			      CoglPath *path)
+{
+    cairo_cogl_journal_path_entry_t *entry;
+
+    if (unlikely (surface->journal == NULL))
+	surface->journal = g_queue_new ();
+
+    /* FIXME: Instead of a GList here we should stack allocate the journal
+     * entries so it would be cheaper to allocate and they can all be freed in
+     * one go after flushing! */
+    entry = g_slice_new (cairo_cogl_journal_path_entry_t);
+    entry->base.type = CAIRO_COGL_JOURNAL_ENTRY_TYPE_PATH;
+
+    entry->pipeline = cogl_object_ref (pipeline);
+    entry->path = cogl_object_ref (path);
+
+    g_queue_push_tail (surface->journal, entry);
+
+#ifdef DISABLE_BATCHING
+    _cairo_cogl_journal_flush (surface);
+#endif
+}
+#endif /* FILL_WITH_COGL_PATH */
+
+static void
+_cairo_cogl_journal_log_primitive (cairo_cogl_surface_t *surface,
+				   CoglPipeline *pipeline,
+				   CoglPrimitive *primitive,
+				   cairo_matrix_t *transform)
+{
+    cairo_cogl_journal_prim_entry_t *entry;
+
+    if (unlikely (surface->journal == NULL))
+	surface->journal = g_queue_new ();
+
+    /* FIXME: Instead of a GList here we should stack allocate the journal
+     * entries so it would be cheaper to allocate and they can all be freed in
+     * one go after flushing! */
+    entry = g_slice_new (cairo_cogl_journal_prim_entry_t);
+    entry->base.type = CAIRO_COGL_JOURNAL_ENTRY_TYPE_PRIMITIVE;
+
+    entry->pipeline = cogl_object_ref (pipeline);
+
+    if (transform) {
+	entry->transform = *transform;
+	entry->has_transform = TRUE;
+    } else
+	entry->has_transform = FALSE;
+
+    entry->primitive = cogl_object_ref (primitive);
+
+    g_queue_push_tail (surface->journal, entry);
+
+#ifdef DISABLE_BATCHING
+    _cairo_cogl_journal_flush (surface);
+#endif
+}
+
+static void
+_cairo_cogl_journal_log_rectangle (cairo_cogl_surface_t *surface,
+				   CoglPipeline *pipeline,
+				   float x,
+				   float y,
+				   float width,
+				   float height,
+				   int n_layers,
+				   cairo_matrix_t *ctm)
+{
+    cairo_cogl_journal_rect_entry_t *entry;
+
+    if (unlikely (surface->journal == NULL))
+	surface->journal = g_queue_new ();
+
+    /* FIXME: Instead of a GList here we should stack allocate the journal
+     * entries so it would be cheaper to allocate and they can all be freed in
+     * one go after flushing! */
+    entry = g_slice_new (cairo_cogl_journal_rect_entry_t);
+    entry->base.type = CAIRO_COGL_JOURNAL_ENTRY_TYPE_RECTANGLE;
+
+    entry->pipeline = cogl_object_ref (pipeline);
+
+    entry->x = x;
+    entry->y = y;
+    entry->width = width;
+    entry->height = height;
+    entry->ctm = *ctm;
+
+    entry->n_layers = n_layers;
+
+    g_queue_push_tail (surface->journal, entry);
+
+#ifdef DISABLE_BATCHING
+    _cairo_cogl_journal_flush (surface);
+#endif
+}
+
+static void
+_cairo_cogl_journal_log_clip (cairo_cogl_surface_t *surface,
+			      const cairo_clip_t *clip)
+{
+    cairo_cogl_journal_clip_entry_t *entry;
+
+    if (unlikely (surface->journal == NULL))
+	surface->journal = g_queue_new ();
+
+    /* FIXME: Instead of a GList here we should stack allocate the journal
+     * entries so it would be cheaper to allocate and they can all be freed in
+     * one go after flushing! */
+    entry = g_slice_new (cairo_cogl_journal_clip_entry_t);
+    entry->base.type = CAIRO_COGL_JOURNAL_ENTRY_TYPE_CLIP;
+    entry->clip = _cairo_clip_copy (clip);
+
+    g_queue_push_tail (surface->journal, entry);
+}
+
+static void
+_cairo_cogl_journal_discard (cairo_cogl_surface_t *surface)
+{
+    GList *l;
+
+    if (!surface->journal) {
+	assert (surface->last_clip == NULL);
+	return;
+    }
+
+    if (surface->buffer_stack && surface->buffer_stack_offset) {
+	cogl_buffer_unmap (COGL_BUFFER (surface->buffer_stack));
+	cogl_object_unref (surface->buffer_stack);
+	surface->buffer_stack = NULL;
+    }
+
+    for (l = surface->journal->head; l; l = l->next) {
+	cairo_cogl_journal_entry_t *entry = l->data;
+	gsize entry_size;
+
+	switch (entry->type)
+	{
+	case CAIRO_COGL_JOURNAL_ENTRY_TYPE_CLIP: {
+	    cairo_cogl_journal_clip_entry_t *clip_entry =
+		(cairo_cogl_journal_clip_entry_t *)entry;
+	    _cairo_clip_destroy (clip_entry->clip);
+	    entry_size = sizeof (cairo_cogl_journal_clip_entry_t);
+	    break;
+	}
+	case CAIRO_COGL_JOURNAL_ENTRY_TYPE_RECTANGLE: {
+	    cairo_cogl_journal_rect_entry_t *rect_entry =
+		(cairo_cogl_journal_rect_entry_t *)entry;
+	    cogl_object_unref (rect_entry->pipeline);
+	    entry_size = sizeof (cairo_cogl_journal_rect_entry_t);
+	    break;
+	}
+	case CAIRO_COGL_JOURNAL_ENTRY_TYPE_PRIMITIVE: {
+	    cairo_cogl_journal_prim_entry_t *prim_entry =
+		(cairo_cogl_journal_prim_entry_t *)entry;
+	    cogl_object_unref (prim_entry->pipeline);
+	    cogl_object_unref (prim_entry->primitive);
+	    entry_size = sizeof (cairo_cogl_journal_prim_entry_t);
+	    break;
+	}
+	case CAIRO_COGL_JOURNAL_ENTRY_TYPE_PATH: {
+	    cairo_cogl_journal_path_entry_t *path_entry =
+		(cairo_cogl_journal_path_entry_t *)entry;
+	    cogl_object_unref (path_entry->pipeline);
+	    cogl_object_unref (path_entry->path);
+	    entry_size = sizeof (cairo_cogl_journal_path_entry_t);
+	    break;
+	}
+	default:
+	    assert (0); /* not reached! */
+	    entry_size = 0; /* avoid compiler warning */
+	}
+	g_slice_free1 (entry_size, entry);
+    }
+
+    g_queue_clear (surface->journal);
+
+    if (surface->last_clip) {
+	_cairo_clip_destroy (surface->last_clip);
+	surface->last_clip = NULL;
+    }
+}
+
+static CoglAttributeBuffer *
+_cairo_cogl_surface_allocate_buffer_space (cairo_cogl_surface_t *surface,
+					   size_t size,
+					   size_t *offset,
+					   void **pointer)
+{
+    /* XXX: In the Cogl journal we found it more efficient to have a pool of
+     * buffers that we re-cycle but for now we simply thow away our stack
+     * buffer each time we flush. */
+    if (unlikely (surface->buffer_stack &&
+		  (surface->buffer_stack_size - surface->buffer_stack_offset) < size)) {
+	cogl_buffer_unmap (COGL_BUFFER (surface->buffer_stack));
+	cogl_object_unref (surface->buffer_stack);
+	surface->buffer_stack = NULL;
+	surface->buffer_stack_size *= 2;
+    }
+
+    if (unlikely (surface->buffer_stack_size < size))
+	surface->buffer_stack_size = size * 2;
+
+    if (unlikely (surface->buffer_stack == NULL)) {
+	surface->buffer_stack = cogl_attribute_buffer_new (surface->buffer_stack_size, NULL);
+	surface->buffer_stack_pointer =
+	    cogl_buffer_map (COGL_BUFFER (surface->buffer_stack),
+			     COGL_BUFFER_ACCESS_WRITE,
+			     COGL_BUFFER_MAP_HINT_DISCARD);
+	surface->buffer_stack_offset = 0;
+    }
+
+    *pointer = surface->buffer_stack_pointer + surface->buffer_stack_offset;
+    *offset = surface->buffer_stack_offset;
+
+    surface->buffer_stack_offset += size;
+    return cogl_object_ref (surface->buffer_stack);
+}
+
+
+static CoglAttributeBuffer *
+_cairo_cogl_traps_to_triangles_buffer (cairo_cogl_surface_t *surface,
+				       cairo_traps_t *traps,
+				       size_t *offset,
+				       gboolean one_shot)
+{
+    CoglAttributeBuffer *buffer;
+    int n_traps = traps->num_traps;
+    int i;
+    CoglVertexP2 *triangles;
+
+    if (one_shot) {
+	buffer = _cairo_cogl_surface_allocate_buffer_space (surface,
+							    n_traps * sizeof (CoglVertexP2) * 6,
+							    offset,
+							    (void **)&triangles);
+	if (!buffer)
+	    return NULL;
+    } else {
+	buffer = cogl_attribute_buffer_new (n_traps * sizeof (CoglVertexP2) * 6, NULL);
+	if (!buffer)
+	    return NULL;
+	triangles = cogl_buffer_map (COGL_BUFFER (buffer),
+				     COGL_BUFFER_ACCESS_WRITE,
+				     COGL_BUFFER_MAP_HINT_DISCARD);
+	if (!triangles)
+	    return NULL;
+	*offset = 0;
+    }
+
+    /* XXX: This is can be very expensive. I'm not sure a.t.m if it's
+     * predominantly the bandwidth required or the cost of the fixed_to_float
+     * conversions but either way we should try using an index buffer to
+     * reduce the amount we upload by 1/3 (offset by allocating and uploading
+     * indices though) sadly though my experience with the intel mesa drivers
+     * is that slow paths can easily be hit when starting to use indices.
+     */
+    for (i = 0; i < n_traps; i++)
+    {
+	CoglVertexP2 *p = &triangles[i * 6];
+	cairo_trapezoid_t *trap = &traps->traps[i];
+
+	p[0].x = _cairo_cogl_util_fixed_to_float (trap->left.p1.x);
+	p[0].y = _cairo_cogl_util_fixed_to_float (trap->left.p1.y);
+
+	p[1].x = _cairo_cogl_util_fixed_to_float (trap->left.p2.x);
+	p[1].y = _cairo_cogl_util_fixed_to_float (trap->left.p2.y);
+
+	p[2].x = _cairo_cogl_util_fixed_to_float (trap->right.p2.x);
+	p[2].y = _cairo_cogl_util_fixed_to_float (trap->right.p2.y);
+
+	p[3].x = _cairo_cogl_util_fixed_to_float (trap->left.p1.x);
+	p[3].y = _cairo_cogl_util_fixed_to_float (trap->left.p1.y);
+
+	p[4].x = _cairo_cogl_util_fixed_to_float (trap->right.p2.x);
+	p[4].y = _cairo_cogl_util_fixed_to_float (trap->right.p2.y);
+
+	p[5].x = _cairo_cogl_util_fixed_to_float (trap->right.p1.x);
+	p[5].y = _cairo_cogl_util_fixed_to_float (trap->right.p1.y);
+    }
+
+    if (!one_shot)
+	cogl_buffer_unmap (COGL_BUFFER (buffer));
+
+    return buffer;
+}
+
+/* Used for solid fills, in this case we just need a mesh made of
+ * a single (2-component) position attribute. */
+static CoglPrimitive *
+_cairo_cogl_traps_to_composite_prim_p2 (cairo_cogl_surface_t *surface,
+					cairo_traps_t *traps,
+					gboolean one_shot)
+{
+    size_t offset;
+    CoglAttributeBuffer *buffer = _cairo_cogl_traps_to_triangles_buffer (surface, traps, &offset, one_shot);
+    CoglAttribute *pos = cogl_attribute_new (buffer,
+					     "cogl_position_in",
+					     sizeof (CoglVertexP2),
+					     offset,
+					     2,
+					     COGL_ATTRIBUTE_TYPE_FLOAT);
+    CoglPrimitive *prim;
+
+    /* The attribute will have taken a reference on the buffer */
+    cogl_object_unref (buffer);
+
+    prim = cogl_primitive_new (COGL_VERTICES_MODE_TRIANGLES,
+			       traps->num_traps * 6, pos, NULL);
+
+    /* The primitive will now keep the attribute alive... */
+    cogl_object_unref (pos);
+
+    return prim;
+}
+
+/* Used for surface fills, in this case we need a mesh made of a single
+ * (2-component) position attribute + we also alias the same attribute as
+ * (2-component) texture coordinates */
+static CoglPrimitive *
+_cairo_cogl_traps_to_composite_prim_p2t2 (cairo_cogl_surface_t *surface,
+					  cairo_traps_t *traps,
+					  gboolean one_shot)
+{
+    size_t offset;
+    CoglAttributeBuffer *buffer = _cairo_cogl_traps_to_triangles_buffer (surface, traps, &offset, one_shot);
+    CoglAttribute *pos = cogl_attribute_new (buffer,
+					     "cogl_position_in",
+					     sizeof (CoglVertexP2),
+					     offset,
+					     2,
+					     COGL_ATTRIBUTE_TYPE_FLOAT);
+    CoglAttribute *tex_coords = cogl_attribute_new (buffer,
+						    "cogl_tex_coord0_in",
+						    sizeof (CoglVertexP2),
+						    0,
+						    2,
+						    COGL_ATTRIBUTE_TYPE_FLOAT);
+    CoglPrimitive *prim;
+
+    /* The attributes will have taken references on the buffer */
+    cogl_object_unref (buffer);
+
+    prim = cogl_primitive_new (COGL_VERTICES_MODE_TRIANGLES,
+			       traps->num_traps * 6, pos, tex_coords, NULL);
+
+    /* The primitive will now keep the attributes alive... */
+    cogl_object_unref (pos);
+    cogl_object_unref (tex_coords);
+
+    return prim;
+}
+
+static CoglPrimitive *
+_cairo_cogl_traps_to_composite_prim (cairo_cogl_surface_t *surface,
+				     cairo_traps_t *traps,
+				     int n_layers,
+				     gboolean one_shot)
+{
+    int n_traps = traps->num_traps;
+    int i;
+
+    /* XXX: Ideally we would skip tessellating to traps entirely since
+     * given their representation, conversion to triangles is quite expensive.
+     *
+     * This simplifies the conversion to triangles by making the end points of
+     * the two side lines actually just correspond to the corners of the
+     * traps.
+     */
+    for (i = 0; i < n_traps; i++)
+	_sanitize_trap (&traps->traps[i]);
+
+    if (n_layers == 0)
+	return _cairo_cogl_traps_to_composite_prim_p2 (surface, traps, one_shot);
+    else {
+	assert (n_layers == 1);
+	return _cairo_cogl_traps_to_composite_prim_p2t2 (surface, traps, one_shot);
+    }
+}
+
+static cairo_int_status_t
+_cairo_cogl_fill_to_primitive (cairo_cogl_surface_t	*surface,
+			       const cairo_path_fixed_t	*path,
+			       cairo_fill_rule_t	 fill_rule,
+			       double			 tolerance,
+			       int			 n_layers,
+			       cairo_bool_t		 one_shot,
+			       CoglPrimitive	       **primitive,
+			       size_t			*size)
+{
+    cairo_traps_t traps;
+    cairo_int_status_t status;
+
+    _cairo_traps_init (&traps);
+    status = _cairo_path_fixed_fill_to_traps (path, fill_rule, tolerance, &traps);
+    if (unlikely (status))
+	goto BAIL;
+
+    if (traps.num_traps == 0) {
+	status = CAIRO_INT_STATUS_NOTHING_TO_DO;
+	goto BAIL;
+    }
+
+    *size = traps.num_traps * sizeof (CoglVertexP2) * 6;
+
+    *primitive = _cairo_cogl_traps_to_composite_prim (surface, &traps, n_layers, one_shot);
+    if (!*primitive) {
+	status = CAIRO_INT_STATUS_NO_MEMORY;
+	goto BAIL;
+    }
+
+BAIL:
+    _cairo_traps_fini (&traps);
+    return status;
+}
+
+static void
+_cairo_cogl_clip_push_box (const cairo_box_t *box)
+{
+    if (_cairo_box_is_pixel_aligned (box)) {
+	cairo_rectangle_int_t rect;
+	_cairo_box_round_to_rectangle (box, &rect);
+	cogl_clip_push_window_rectangle (rect.x, rect.y,
+					 rect.width, rect.height);
+    } else {
+	double x1, y1, x2, y2;
+	_cairo_box_to_doubles (box, &x1, &y1, &x2, &y2);
+	cogl_clip_push_rectangle (x1, y1, x2, y2);
+    }
+}
+
+static void
+_cairo_cogl_journal_flush (cairo_cogl_surface_t *surface)
+{
+    GList *l;
+    int clip_stack_depth = 0;
+    int i;
+
+    if (!surface->journal)
+	return;
+
+    if (surface->buffer_stack && surface->buffer_stack_offset) {
+	cogl_buffer_unmap (COGL_BUFFER (surface->buffer_stack));
+	cogl_object_unref (surface->buffer_stack);
+	surface->buffer_stack = NULL;
+    }
+
+    cogl_set_framebuffer (surface->framebuffer);
+
+    cogl_push_matrix ();
+
+    for (l = surface->journal->head; l; l = l->next) {
+	cairo_cogl_journal_entry_t *entry = l->data;
+
+	switch (entry->type)
+	{
+	case CAIRO_COGL_JOURNAL_ENTRY_TYPE_CLIP: {
+	    cairo_cogl_journal_clip_entry_t *clip_entry =
+		(cairo_cogl_journal_clip_entry_t *)entry;
+	    cairo_clip_path_t *path;
+#if 0
+	    cairo_bool_t checked_for_primitives = FALSE;
+	    cairo_cogl_clip_primitives_t *clip_primitives;
+#endif
+
+	    for (i = 0; i < clip_stack_depth; i++)
+		cogl_clip_pop ();
+	    clip_stack_depth = 0;
+
+	    for (path = clip_entry->clip->path, i = 0; path; path = path->prev, i++) {
+		cairo_rectangle_int_t extents;
+		cairo_int_status_t status;
+		CoglPrimitive *prim;
+		size_t prim_size;
+
+		_cairo_path_fixed_approximate_clip_extents (&path->path, &extents);
+
+		/* TODO - maintain a fifo of the last 10 used clips with cached
+		 * primitives to see if we can avoid tesselating the path and
+		 * uploading the vertices...
+		 */
+#if 0
+		if (!checked_for_primitives) {
+		    clip_primitives = find_clip_primitives (clip);
+		    checked_for_primitives = TRUE;
+		}
+		if (clip_primitives)
+		    prim = clip_primitives->primitives[i];
+#endif
+		status = _cairo_cogl_fill_to_primitive (surface,
+							&path->path,
+							path->fill_rule,
+							path->tolerance,
+							0,
+							TRUE,
+							&prim,
+							&prim_size);
+		if (unlikely (status)) {
+		    g_warning ("Failed to get primitive for clip path while flushing journal");
+		    continue;
+		}
+		clip_stack_depth++;
+		cogl_clip_push_primitive (prim,
+					  extents.x, extents.y,
+					  extents.x + extents.width,
+					  extents.y + extents.height);
+		cogl_object_unref (prim);
+	    }
+
+	    for (i = 0; i < clip_entry->clip->num_boxes; i++) {
+		clip_stack_depth++;
+		_cairo_cogl_clip_push_box (&clip_entry->clip->boxes[i]);
+	    }
+
+	    surface->n_clip_updates_per_frame++;
+	    break;
+	}
+	case CAIRO_COGL_JOURNAL_ENTRY_TYPE_RECTANGLE: {
+	    cairo_cogl_journal_rect_entry_t *rect_entry =
+		(cairo_cogl_journal_rect_entry_t *)entry;
+	    float tex_coords[8];
+	    float x1 = rect_entry->x;
+	    float y1 = rect_entry->y;
+	    float x2 = rect_entry->x + rect_entry->width;
+	    float y2 = rect_entry->y + rect_entry->height;
+	    cairo_matrix_t *ctm = &rect_entry->ctm;
+	    float ctmfv[16] = {
+		ctm->xx, ctm->yx, 0, 0,
+		ctm->xy, ctm->yy, 0, 0,
+		0,	     0,	      1, 0,
+		ctm->x0, ctm->y0, 0, 1
+	    };
+	    CoglMatrix transform;
+
+	    cogl_matrix_init_from_array (&transform, ctmfv);
+
+	    if (rect_entry->n_layers) {
+		g_assert (rect_entry->n_layers <= 2);
+		tex_coords[0] = x1;
+		tex_coords[1] = y1;
+		tex_coords[2] = x2;
+		tex_coords[3] = y2;
+		if (rect_entry->n_layers > 1)
+		    memcpy (&tex_coords[4], tex_coords, sizeof (float) * 4);
+	    }
+
+	    cogl_set_source (rect_entry->pipeline);
+	    cogl_push_matrix ();
+	    cogl_transform (&transform);
+	    cogl_rectangle_with_multitexture_coords (x1, y1, x2, y2,
+						     tex_coords, 4 * rect_entry->n_layers);
+	    cogl_pop_matrix ();
+	    break;
+	}
+	case CAIRO_COGL_JOURNAL_ENTRY_TYPE_PRIMITIVE: {
+	    cairo_cogl_journal_prim_entry_t *prim_entry =
+		(cairo_cogl_journal_prim_entry_t *)entry;
+	    CoglMatrix transform;
+
+	    cogl_push_matrix ();
+	    if (prim_entry->has_transform) {
+		cairo_matrix_t *ctm = &prim_entry->transform;
+		float ctmfv[16] = {
+		    ctm->xx, ctm->yx, 0, 0,
+		    ctm->xy, ctm->yy, 0, 0,
+		    0,	     0,	      1, 0,
+		    ctm->x0, ctm->y0, 0, 1
+		};
+		cogl_matrix_init_from_array (&transform, ctmfv);
+		cogl_transform (&transform);
+	    } else {
+		cogl_matrix_init_identity (&transform);
+		cogl_set_modelview_matrix (&transform);
+	    }
+
+	    cogl_set_source (prim_entry->pipeline);
+	    cogl_primitive_draw (prim_entry->primitive);
+	    cogl_pop_matrix ();
+	    break;
+	}
+	case CAIRO_COGL_JOURNAL_ENTRY_TYPE_PATH: {
+	    cairo_cogl_journal_path_entry_t *path_entry =
+		(cairo_cogl_journal_path_entry_t *)entry;
+
+	    cogl_set_source (path_entry->pipeline);
+	    cogl_path_fill (path_entry->path);
+	    break;
+	}
+	default:
+	    assert (0); /* not reached! */
+	}
+    }
+
+    cogl_pop_matrix ();
+
+    for (i = 0; i < clip_stack_depth; i++)
+	cogl_clip_pop ();
+
+    _cairo_cogl_journal_discard (surface);
+}
+
+static cairo_status_t
+_cairo_cogl_surface_flush (void *abstract_surface)
+{
+    cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)abstract_surface;
+
+    _cairo_cogl_journal_flush (surface);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_cogl_surface_finish (void *abstract_surface)
+{
+    cairo_cogl_surface_t *surface = abstract_surface;
+
+    if (surface->texture)
+	cogl_object_unref (surface->texture);
+
+    if (surface->framebuffer)
+	cogl_object_unref (surface->framebuffer);
+
+    if (surface->journal)
+	_cairo_cogl_journal_free (surface);
+
+    /*XXX wtf */
+    cairo_device_release (surface->base.device);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static CoglPixelFormat
+get_cogl_format_from_cairo_format (cairo_format_t cairo_format);
+
+/* XXX: We often use RGBA format for onscreen framebuffers so make sure
+ * to handle CAIRO_FORMAT_INVALID sensibly */
+static cairo_format_t
+get_cairo_format_from_cogl_format (CoglPixelFormat format)
+{
+    switch ((int)format)
+    {
+    case COGL_PIXEL_FORMAT_A_8:
+	return CAIRO_FORMAT_A8;
+    case COGL_PIXEL_FORMAT_RGB_565:
+	return CAIRO_FORMAT_RGB16_565;
+
+    case COGL_PIXEL_FORMAT_BGRA_8888_PRE:
+    case COGL_PIXEL_FORMAT_ARGB_8888_PRE:
+    case COGL_PIXEL_FORMAT_RGBA_8888_PRE:
+	/* Note: this is ambiguous since CAIRO_FORMAT_RGB24
+	 * would also map to the same CoglPixelFormat */
+	return CAIRO_FORMAT_ARGB32;
+
+    default:
+	g_warning("bad format: %x a? %d, bgr? %d, pre %d, format: %d\n",
+		  format,
+		  format & COGL_A_BIT,
+		  format & COGL_BGR_BIT,
+		  format & COGL_PREMULT_BIT,
+		  format & ~(COGL_A_BIT | COGL_BGR_BIT | COGL_PREMULT_BIT));
+	return CAIRO_FORMAT_INVALID;
+    }
+}
+
+static CoglPixelFormat
+get_cogl_format_from_cairo_format (cairo_format_t cairo_format)
+{
+    switch (cairo_format)
+    {
+    case CAIRO_FORMAT_ARGB32:
+    case CAIRO_FORMAT_RGB24:
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+	return COGL_PIXEL_FORMAT_BGRA_8888_PRE;
+#else
+	return COGL_PIXEL_FORMAT_ARGB_8888_PRE;
+#endif
+    case CAIRO_FORMAT_A8:
+	return COGL_PIXEL_FORMAT_A_8;
+    case CAIRO_FORMAT_RGB16_565:
+	return COGL_PIXEL_FORMAT_RGB_565;
+    case CAIRO_FORMAT_INVALID:
+    case CAIRO_FORMAT_A1:
+    case CAIRO_FORMAT_RGB30:
+	return 0;
+    }
+
+    g_warn_if_reached ();
+    return 0;
+}
+
+static cairo_status_t
+_cairo_cogl_surface_read_rect_to_image_surface (cairo_cogl_surface_t   *surface,
+						cairo_rectangle_int_t  *interest,
+						cairo_image_surface_t **image_out)
+{
+    cairo_image_surface_t *image;
+    cairo_status_t status;
+    cairo_format_t cairo_format;
+    CoglPixelFormat cogl_format;
+
+    /* TODO: Add cogl_texture_get_region() API so we don't have to ensure the
+     * surface is bound to an fbo to read back pixels */
+    status = _cairo_cogl_surface_ensure_framebuffer (surface);
+    if (unlikely (status))
+	return status;
+
+    cairo_format = get_cairo_format_from_cogl_format (surface->cogl_format);
+    if (cairo_format == CAIRO_FORMAT_INVALID) {
+	cairo_format = CAIRO_FORMAT_ARGB32;
+	cogl_format = get_cogl_format_from_cairo_format (cairo_format);
+    } else {
+	cogl_format = cogl_framebuffer_get_color_format (surface->framebuffer);
+    }
+
+    image = (cairo_image_surface_t *)
+	cairo_image_surface_create (cairo_format, surface->width, surface->height);
+    if (image->base.status)
+	return image->base.status;
+
+    /* TODO: Add cogl_framebuffer_read_pixels() API */
+    cogl_push_framebuffer (surface->framebuffer);
+    cogl_read_pixels (0, 0, surface->width, surface->height,
+		      COGL_READ_PIXELS_COLOR_BUFFER,
+		      cogl_format,
+		      image->data);
+    cogl_pop_framebuffer ();
+
+    *image_out = image;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_cogl_surface_acquire_source_image (void		         *abstract_surface,
+					  cairo_image_surface_t **image_out,
+					  void		        **image_extra)
+{
+    cairo_cogl_surface_t *surface = abstract_surface;
+    cairo_status_t status;
+
+    if (surface->texture) {
+	cairo_format_t format = get_cairo_format_from_cogl_format (surface->cogl_format);
+	cairo_image_surface_t *image = (cairo_image_surface_t *)
+	    cairo_image_surface_create (format, surface->width, surface->height);
+	if (image->base.status)
+	    return image->base.status;
+
+	cogl_texture_get_data (surface->texture,
+			       cogl_texture_get_format (surface->texture),
+			       0,
+			       image->data);
+
+	image->base.is_clear = FALSE;
+	*image_out = image;
+    } else {
+	cairo_rectangle_int_t extents = {
+	    0, 0, surface->width, surface->height
+	};
+	status = _cairo_cogl_surface_read_rect_to_image_surface (surface, &extents,
+								 image_out);
+	if (unlikely (status))
+	    return status;
+    }
+
+    *image_extra = NULL;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_cogl_surface_release_source_image (void			*abstract_surface,
+					  cairo_image_surface_t *image,
+					  void			*image_extra)
+{
+    cairo_surface_destroy (&image->base);
+}
+
+static cairo_status_t
+_cairo_cogl_surface_clear (cairo_cogl_surface_t *surface,
+			   const cairo_color_t *color)
+{
+    /* Anything batched in the journal up until now is redundant... */
+    _cairo_cogl_journal_discard (surface);
+
+    /* XXX: we currently implicitly clear the depth and stencil buffer here
+     * but since we use the framebuffer_discard extension when available I
+     * suppose this doesn't matter too much.
+     *
+     * The main concern is that we want to avoid re-loading an external z
+     * buffer at the start of each frame, but also many gpu architectures have
+     * optmizations for how they handle the depth/stencil buffers and can get
+     * upset if they aren't cleared together at the start of the frame.
+     *
+     * FIXME: we need a way to assert that the clip stack currently isn't
+     * using the stencil buffer before clearing it here!
+     */
+    cogl_framebuffer_clear4f (surface->framebuffer,
+			      COGL_BUFFER_BIT_COLOR |
+			      COGL_BUFFER_BIT_DEPTH |
+			      COGL_BUFFER_BIT_STENCIL,
+			      color->red * color->alpha,
+			      color->green * color->alpha,
+			      color->blue * color->alpha,
+			      color->alpha);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_status_t
+_cairo_cogl_path_fixed_rectangle (cairo_path_fixed_t *path,
+				  cairo_fixed_t x,
+				  cairo_fixed_t y,
+				  cairo_fixed_t width,
+				  cairo_fixed_t height)
+{
+    cairo_status_t status;
+
+    status = _cairo_path_fixed_move_to (path, x, y);
+    if (unlikely (status))
+	return status;
+
+    status = _cairo_path_fixed_rel_line_to (path, width, 0);
+    if (unlikely (status))
+	return status;
+
+    status = _cairo_path_fixed_rel_line_to (path, 0, height);
+    if (unlikely (status))
+	return status;
+
+    status = _cairo_path_fixed_rel_line_to (path, -width, 0);
+    if (unlikely (status))
+	return status;
+
+    status = _cairo_path_fixed_close_path (path);
+    if (unlikely (status))
+	return status;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+_cairo_cogl_surface_paint (void                  *abstract_surface,
+                           cairo_operator_t       op,
+                           const cairo_pattern_t *source,
+                           const cairo_clip_t    *clip)
+{
+    cairo_cogl_surface_t *surface;
+    cairo_path_fixed_t path;
+    cairo_status_t status;
+    cairo_matrix_t identity;
+
+    if (clip == NULL) {
+	if (op == CAIRO_OPERATOR_CLEAR)
+            return _cairo_cogl_surface_clear (abstract_surface, CAIRO_COLOR_TRANSPARENT);
+	else if (source->type == CAIRO_PATTERN_TYPE_SOLID &&
+                (op == CAIRO_OPERATOR_SOURCE ||
+                 (op == CAIRO_OPERATOR_OVER && (((cairo_surface_t *)abstract_surface)->is_clear || _cairo_pattern_is_opaque_solid (source))))) {
+            return _cairo_cogl_surface_clear (abstract_surface,
+					      &((cairo_solid_pattern_t *) source)->color);
+        }
+    }
+
+    /* fallback to handling the paint in terms of a fill... */
+
+    surface = abstract_surface;
+
+    _cairo_path_fixed_init (&path);
+
+    status = _cairo_cogl_path_fixed_rectangle (&path, 0, 0, surface->width, surface->height);
+    if (unlikely (status))
+	goto BAIL;
+
+#ifdef NEED_COGL_CONTEXT
+    /* XXX: in cairo-cogl-context.c we set some sideband data on the
+     * surface before issuing a fill so we need to do that here too... */
+    surface->user_path = &path;
+    cairo_matrix_init_identity (&identity);
+    surface->ctm = &identity;
+    surface->ctm_inverse = &identity;
+    surface->path_is_rectangle = TRUE;
+    surface->path_rectangle_x = 0;
+    surface->path_rectangle_y = 0;
+    surface->path_rectangle_width = surface->width;
+    surface->path_rectangle_height = surface->height;
+#endif
+
+    status = _cairo_cogl_surface_fill (abstract_surface,
+				       op,
+				       source,
+				       &path,
+				       CAIRO_FILL_RULE_WINDING,
+				       1,
+				       CAIRO_ANTIALIAS_DEFAULT,
+				       clip);
+BAIL:
+    _cairo_path_fixed_fini (&path);
+    return status;
+}
+
+static CoglPipelineWrapMode
+get_cogl_wrap_mode_for_extend (cairo_extend_t extend_mode)
+{
+    switch (extend_mode)
+    {
+    case CAIRO_EXTEND_NONE:
+	return COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE;
+    case CAIRO_EXTEND_PAD:
+	return COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE;
+    case CAIRO_EXTEND_REPEAT:
+	return COGL_PIPELINE_WRAP_MODE_REPEAT;
+    case CAIRO_EXTEND_REFLECT:
+	/* TODO: return COGL_PIPELINE_WRAP_MODE_MIRROR; */
+	return CAIRO_EXTEND_REPEAT;
+    }
+    assert (0); /* not reached */
+    return COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE;
+}
+
+#if 0
+/* Given an arbitrary texture, check if it's already a pot texture and simply
+ * return it back if so. If not create a new pot texture, scale the old to
+ * fill it, unref the old and return a pointer to the new pot texture. */
+static cairo_int_status_t
+_cairo_cogl_get_pot_texture (CoglContext *context,
+			     CoglTexture *texture,
+			     CoglTexture **pot_texture)
+{
+    int width = cogl_texture_get_width (texture);
+    int height = cogl_texture_get_height (texture);
+    int pot_width;
+    int pot_height;
+    CoglHandle offscreen = NULL;
+    CoglTexture2D *pot = NULL;
+    GError *error;
+
+    pot_width = _cairo_cogl_util_next_p2 (width);
+    pot_height = _cairo_cogl_util_next_p2 (height);
+
+    if (pot_width == width && pot_height == height)
+	return CAIRO_INT_STATUS_SUCCESS;
+
+    for (;;) {
+	error = NULL;
+	pot = cogl_texture_2d_new_with_size (context,
+					     pot_width,
+					     pot_height,
+					     cogl_texture_get_format (texture),
+					     &error);
+	if (pot)
+	    break;
+	else
+	    g_error_free (error);
+
+	if (pot_width > pot_height)
+	    pot_width >>= 1;
+	else
+	    pot_height >>= 1;
+
+	if (!pot_width || !pot_height)
+	    break;
+    }
+
+    *pot_texture = COGL_TEXTURE (pot);
+
+    if (!pot)
+	return CAIRO_INT_STATUS_NO_MEMORY;
+
+    /* Use the GPU to do a bilinear filtered scale from npot to pot... */
+    offscreen = cogl_offscreen_new_to_texture (COGL_TEXTURE (pot));
+    error = NULL;
+    if (!cogl_framebuffer_allocate (COGL_FRAMEBUFFER (offscreen), &error)) {
+	/* NB: if we don't pass an error then Cogl is allowed to simply abort
+	 * automatically. */
+	g_error_free (error);
+	cogl_object_unref (pot);
+	*pot_texture = NULL;
+	return CAIRO_INT_STATUS_NO_MEMORY;
+    }
+
+    cogl_push_framebuffer (COGL_FRAMEBUFFER (offscreen));
+    cogl_set_source_texture (texture);
+    cogl_rectangle (-1, 1, 1, -1);
+    cogl_pop_framebuffer ();
+
+    cogl_object_unref (offscreen);
+}
+#endif
+
+/* NB: a reference for the texture is transferred to the caller which should
+ * be unrefed */
+static CoglTexture *
+_cairo_cogl_acquire_surface_texture (cairo_cogl_surface_t  *reference_surface,
+				     cairo_surface_t	   *abstract_surface)
+{
+    cairo_image_surface_t *image;
+    cairo_image_surface_t *acquired_image = NULL;
+    void *image_extra;
+    CoglPixelFormat format;
+    cairo_image_surface_t *image_clone = NULL;
+    CoglTexture2D *texture;
+    GError *error = NULL;
+    cairo_surface_t *clone;
+
+    if (abstract_surface->device == reference_surface->base.device) {
+	cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)abstract_surface;
+	_cairo_cogl_surface_flush (surface);
+	return surface->texture ? cogl_object_ref (surface->texture) : NULL;
+    }
+
+    if (abstract_surface->type == CAIRO_SURFACE_TYPE_COGL) {
+	if (_cairo_surface_is_subsurface (abstract_surface)) {
+	    cairo_cogl_surface_t *surface;
+
+	    surface = (cairo_cogl_surface_t *)
+		_cairo_surface_subsurface_get_target (abstract_surface);
+	    if (surface->base.device == reference_surface->base.device)
+		return surface->texture ? cogl_object_ref (surface->texture) : NULL;
+	}
+    }
+
+    clone = _cairo_surface_has_snapshot (abstract_surface, &_cairo_cogl_surface_backend);
+    if (clone) {
+	cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)clone;
+	return surface->texture ? cogl_object_ref (surface->texture) : NULL;
+    }
+
+    g_warning ("Uploading image surface to texture");
+
+    if (_cairo_surface_is_image (abstract_surface)) {
+	image = (cairo_image_surface_t *)abstract_surface;
+    } else {
+	cairo_status_t status = _cairo_surface_acquire_source_image (abstract_surface,
+								     &acquired_image, &image_extra);
+	if (unlikely (status)) {
+	    g_warning ("acquire_source_image failed: %s [%d]\n",
+		       cairo_status_to_string (status), status);
+	    return NULL;
+	}
+	image = acquired_image;
+    }
+
+    format = get_cogl_format_from_cairo_format (image->format);
+    if (!format)
+    {
+	image_clone = _cairo_image_surface_coerce (image);
+	if (unlikely (image_clone->base.status)) {
+	    g_warning ("image_surface_coerce failed");
+	    texture = NULL;
+	    goto BAIL;
+	}
+
+	format = get_cogl_format_from_cairo_format (image_clone->format);
+	assert (format);
+    }
+
+    texture = cogl_texture_2d_new_from_data (to_device(reference_surface->base.device)->cogl_context,
+					     image->width,
+					     image->height,
+					     format, /* incoming */
+					     format, /* desired */
+					     image->stride,
+					     image->data,
+					     &error);
+    if (!texture) {
+	g_warning ("Failed to allocate texture: %s", error->message);
+	g_error_free (error);
+	goto BAIL;
+    }
+
+    clone = _cairo_cogl_surface_create_full (to_device(reference_surface->base.device),
+					     reference_surface->ignore_alpha,
+					     NULL, COGL_TEXTURE (texture));
+
+    _cairo_surface_attach_snapshot (abstract_surface, clone, NULL);
+
+    /* Attaching the snapshot will take a reference on the clone surface... */
+    cairo_surface_destroy (clone);
+
+BAIL:
+    if (image_clone)
+	cairo_surface_destroy (&image_clone->base);
+    if (acquired_image)
+	_cairo_surface_release_source_image (abstract_surface, acquired_image, image_extra);
+
+    return COGL_TEXTURE (texture);
+}
+
+/* NB: a reference for the texture is transferred to the caller which should
+ * be unrefed */
+static CoglTexture *
+_cairo_cogl_acquire_pattern_texture (const cairo_pattern_t *pattern,
+				     cairo_cogl_surface_t *destination,
+				     const cairo_rectangle_int_t *extents,
+				     const cairo_rectangle_int_t *sample,
+				     cairo_cogl_texture_attributes_t *attributes)
+{
+    CoglTexture *texture = NULL;
+
+    switch ((int)pattern->type)
+    {
+    case CAIRO_PATTERN_TYPE_SURFACE: {
+	cairo_surface_t *surface = ((cairo_surface_pattern_t *)pattern)->surface;
+	texture = _cairo_cogl_acquire_surface_texture (destination, surface);
+	if (!texture)
+	    return NULL;
+
+	/* XXX: determine if it would have no effect to change the
+	 * extend mode to EXTEND_PAD instead since we can simply map
+	 * EXTEND_PAD to CLAMP_TO_EDGE without needing fragment shader
+	 * tricks or extra border texels. */
+#if 0
+	/* TODO: We still need to consider HW such as SGX which doesn't have
+	 * full support for NPOT textures. */
+	if (pattern->extend == CAIRO_EXTEND_REPEAT || pattern->extend == CAIRO_EXTEND_REFLECT) {
+	    _cairo_cogl_get_pot_texture ();
+	}
+#endif
+
+	cairo_matrix_init_identity (&attributes->matrix);
+
+	/* Convert from un-normalized source coordinates in backend
+	 * coordinates to normalized texture coordinates */
+	cairo_matrix_scale (&attributes->matrix,
+			    1.0f / cogl_texture_get_width (texture),
+			    1.0f / cogl_texture_get_height (texture));
+
+	/* XXX: need to multiply in the pattern->matrix */
+
+	attributes->extend = pattern->extend;
+	attributes->filter = CAIRO_FILTER_BILINEAR;
+	attributes->has_component_alpha = pattern->has_component_alpha;
+
+	attributes->s_wrap = get_cogl_wrap_mode_for_extend (pattern->extend);
+	attributes->t_wrap = attributes->s_wrap;
+
+	return texture;
+    }
+    case CAIRO_PATTERN_TYPE_RADIAL:
+    case CAIRO_PATTERN_TYPE_MESH: {
+	cairo_surface_t *surface;
+	cairo_matrix_t texture_matrix;
+
+	surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+					      extents->width, extents->height);
+	if (_cairo_surface_offset_paint (surface,
+					 extents->x, extents->y,
+					 CAIRO_OPERATOR_SOURCE,
+					 pattern, NULL)) {
+	    cairo_surface_destroy (surface);
+	    return NULL;
+	}
+
+	texture = _cairo_cogl_acquire_surface_texture (destination, surface);
+	if (!texture)
+	    goto BAIL;
+
+	cairo_matrix_init_identity (&texture_matrix);
+
+	/* Convert from un-normalized source coordinates in backend
+	 * coordinates to normalized texture coordinates */
+	cairo_matrix_scale (&texture_matrix,
+			    1.0f / cogl_texture_get_width (texture),
+			    1.0f / cogl_texture_get_height (texture));
+
+	cairo_matrix_translate (&texture_matrix, -extents->x, -extents->y);
+
+	attributes->matrix = texture_matrix;
+	attributes->extend = pattern->extend;
+	attributes->filter = CAIRO_FILTER_NEAREST;
+	attributes->has_component_alpha = pattern->has_component_alpha;
+
+	/* any pattern extend modes have already been dealt with... */
+	attributes->s_wrap = COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE;
+	attributes->t_wrap = attributes->s_wrap;
+
+BAIL:
+	cairo_surface_destroy (surface);
+
+	return texture;
+    }
+    case CAIRO_PATTERN_TYPE_LINEAR: {
+	cairo_linear_pattern_t *linear_pattern = (cairo_linear_pattern_t *)pattern;
+	cairo_cogl_linear_gradient_t *gradient;
+	cairo_cogl_linear_texture_entry_t *linear_texture;
+	cairo_int_status_t status;
+	float a, b;
+	float dist;
+	float scale;
+	float angle;
+
+	status = _cairo_cogl_get_linear_gradient (to_device(destination->base.device),
+						  pattern->extend,
+						  linear_pattern->base.n_stops,
+						  linear_pattern->base.stops,
+						  &gradient);
+	if (unlikely (status))
+	    return NULL;
+
+	linear_texture = _cairo_cogl_linear_gradient_texture_for_extend (gradient, pattern->extend);
+
+	attributes->extend = pattern->extend;
+	attributes->filter = CAIRO_FILTER_BILINEAR;
+	attributes->has_component_alpha = pattern->has_component_alpha;
+	attributes->s_wrap = get_cogl_wrap_mode_for_extend (pattern->extend);
+	attributes->t_wrap = COGL_PIPELINE_WRAP_MODE_REPEAT;
+
+	cairo_matrix_init_identity (&attributes->matrix);
+
+	a = linear_pattern->pd2.x - linear_pattern->pd1.x;
+	b = linear_pattern->pd2.y - linear_pattern->pd1.y;
+	dist = sqrtf (a*a + b*b);
+	scale = 1.0f / dist;
+	angle = - atan2f (b, a);
+
+	cairo_matrix_rotate (&attributes->matrix, angle);
+	cairo_matrix_scale (&attributes->matrix, scale, scale);
+
+	cairo_matrix_translate (&attributes->matrix,
+				-linear_pattern->pd1.x,
+				-linear_pattern->pd1.y);
+
+	/* XXX: this caught me out: cairo doesn't follow the standard
+	 * maths convention for multiplying two matrices A x B - cairo
+	 * does B x A so the final matrix is as if A's transforms were
+	 * applied first.
+	 */
+	cairo_matrix_multiply (&attributes->matrix,
+			       &pattern->matrix,
+			       &attributes->matrix);
+
+	return cogl_object_ref (linear_texture->texture);
+    }
+    default:
+	g_warning ("Un-supported source type");
+	return NULL;
+    }
+}
+
+static void
+set_layer_texture_with_attributes (CoglPipeline *pipeline,
+				   int layer_index,
+				   CoglTexture *texture,
+				   cairo_cogl_texture_attributes_t *attributes)
+{
+    cogl_pipeline_set_layer_texture (pipeline, layer_index, texture);
+
+    if (!_cairo_matrix_is_identity (&attributes->matrix)) {
+	cairo_matrix_t *m = &attributes->matrix;
+	float texture_matrixfv[16] = {
+	    m->xx, m->yx, 0, 0,
+	    m->xy, m->yy, 0, 0,
+	    0, 0, 1, 0,
+	    m->x0, m->y0, 0, 1
+	};
+	CoglMatrix texture_matrix;
+	cogl_matrix_init_from_array (&texture_matrix, texture_matrixfv);
+	cogl_pipeline_set_layer_matrix (pipeline, layer_index, &texture_matrix);
+    }
+
+    if (attributes->s_wrap != attributes->t_wrap) {
+	cogl_pipeline_set_layer_wrap_mode_s (pipeline, layer_index, attributes->s_wrap);
+	cogl_pipeline_set_layer_wrap_mode_t (pipeline, layer_index, attributes->t_wrap);
+    } else
+	cogl_pipeline_set_layer_wrap_mode (pipeline, layer_index, attributes->s_wrap);
+}
+
+static CoglPipeline *
+get_source_mask_operator_destination_pipeline (const cairo_pattern_t *mask,
+					       const cairo_pattern_t *source,
+					       cairo_operator_t op,
+					       cairo_cogl_surface_t *destination,
+					       cairo_composite_rectangles_t *extents)
+{
+    cairo_cogl_template_type template_type;
+    CoglPipeline *pipeline;
+
+    switch ((int)source->type)
+    {
+    case CAIRO_PATTERN_TYPE_SOLID:
+	template_type = mask ?
+	    CAIRO_COGL_TEMPLATE_TYPE_MASK_SOLID : CAIRO_COGL_TEMPLATE_TYPE_SOLID;
+	break;
+    case CAIRO_PATTERN_TYPE_SURFACE:
+    case CAIRO_PATTERN_TYPE_LINEAR:
+    case CAIRO_PATTERN_TYPE_RADIAL:
+    case CAIRO_PATTERN_TYPE_MESH:
+	template_type = mask ?
+	    CAIRO_COGL_TEMPLATE_TYPE_MASK_TEXTURE : CAIRO_COGL_TEMPLATE_TYPE_TEXTURE;
+	break;
+    default:
+	g_warning ("Un-supported source type");
+	return NULL;
+    }
+
+    pipeline = cogl_pipeline_copy (to_device(destination->base.device)->template_pipelines[op][template_type]);
+
+    if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
+	cairo_solid_pattern_t *solid_pattern = (cairo_solid_pattern_t *)source;
+	cogl_pipeline_set_color4f (pipeline,
+				   solid_pattern->color.red * solid_pattern->color.alpha,
+				   solid_pattern->color.green * solid_pattern->color.alpha,
+				   solid_pattern->color.blue * solid_pattern->color.alpha,
+				   solid_pattern->color.alpha);
+    } else {
+	cairo_cogl_texture_attributes_t attributes;
+	CoglTexture *texture =
+	    _cairo_cogl_acquire_pattern_texture (source, destination,
+						 &extents->bounded,
+						 &extents->source_sample_area,
+						 &attributes);
+	if (!texture)
+	    goto BAIL;
+	set_layer_texture_with_attributes (pipeline, 0, texture, &attributes);
+	cogl_object_unref (texture);
+    }
+
+    if (mask) {
+	if (mask->type == CAIRO_PATTERN_TYPE_SOLID) {
+	    cairo_solid_pattern_t *solid_pattern = (cairo_solid_pattern_t *)mask;
+	    CoglColor color;
+	    cogl_color_init_from_4f (&color,
+				     solid_pattern->color.red * solid_pattern->color.alpha,
+				     solid_pattern->color.green * solid_pattern->color.alpha,
+				     solid_pattern->color.blue * solid_pattern->color.alpha,
+				     solid_pattern->color.alpha);
+	    cogl_pipeline_set_layer_combine_constant (pipeline, 1, &color);
+	} else {
+	    cairo_cogl_texture_attributes_t attributes;
+	    CoglTexture *texture =
+		_cairo_cogl_acquire_pattern_texture (mask, destination,
+						     &extents->bounded,
+						     &extents->mask_sample_area,
+						     &attributes);
+	    if (!texture)
+		goto BAIL;
+	    set_layer_texture_with_attributes (pipeline, 1, texture, &attributes);
+	    cogl_object_unref (texture);
+	}
+    }
+
+    return pipeline;
+
+BAIL:
+    cogl_object_unref (pipeline);
+    return NULL;
+}
+
+#if 0
+CoglPrimitive *
+_cairo_cogl_rectangle_new_p2t2t2 (float x,
+				  float y,
+				  float width,
+				  float height)
+{
+    CoglVertexP2 vertices[] = {
+	{x, y}, {x, y + height}, {x + width, y + height},
+	{x, y}, {x + width, y + height}, {x + width, y}
+    };
+    CoglAttributeBuffer *buffer = cogl_attribute_buffer_new (sizeof (vertices));
+    CoglAttribute *pos = cogl_attribute_new (buffer,
+					     "cogl_position_in",
+					     sizeof (CoglVertexP2),
+					     0,
+					     2,
+					     COGL_ATTRIBUTE_TYPE_FLOAT);
+    CoglAttribute *tex_coords0 = cogl_attribute_new (buffer,
+						     "cogl_tex_coord0_in",
+						     sizeof (CoglVertexP2),
+						     0,
+						     2,
+						     COGL_ATTRIBUTE_TYPE_FLOAT);
+    CoglAttribute *tex_coords0 = cogl_attribute_new (buffer,
+						     "cogl_tex_coord0_in",
+						     sizeof (CoglVertexP2),
+						     0,
+						     2,
+						     COGL_ATTRIBUTE_TYPE_FLOAT);
+    CoglPrimitive *prim;
+
+    cogl_buffer_set_data (COGL_BUFFER (buffer), 0, vertices, sizeof (vertices));
+
+    /* The attributes will now keep the buffer alive... */
+    cogl_object_unref (buffer);
+
+    prim = cogl_primitive_new (COGL_VERTICES_MODE_TRIANGLES,
+			       6, pos, tex_coords, NULL);
+
+    /* The primitive will now keep the attribute alive... */
+    cogl_object_unref (pos);
+
+    return prim;
+}
+#endif
+
+static void
+_cairo_cogl_log_clip (cairo_cogl_surface_t *surface,
+		      const cairo_clip_t *clip)
+{
+    if (!_cairo_clip_equal (clip, surface->last_clip)) {
+	_cairo_cogl_journal_log_clip (surface, clip);
+	_cairo_clip_destroy (surface->last_clip);
+	surface->last_clip = _cairo_clip_copy (clip);
+    }
+}
+
+static void
+_cairo_cogl_maybe_log_clip (cairo_cogl_surface_t *surface,
+			    cairo_composite_rectangles_t *composite)
+{
+    cairo_clip_t *clip = composite->clip;
+
+    if (_cairo_composite_rectangles_can_reduce_clip (composite, clip))
+	clip = NULL;
+
+    if (clip == NULL) {
+	if (_cairo_composite_rectangles_can_reduce_clip (composite,
+							 surface->last_clip))
+	    return;
+    }
+
+    _cairo_cogl_log_clip (surface, clip);
+}
+
+static cairo_bool_t
+is_operator_supported (cairo_operator_t op)
+{
+    switch ((int)op) {
+    case CAIRO_OPERATOR_SOURCE:
+    case CAIRO_OPERATOR_OVER:
+    case CAIRO_OPERATOR_IN:
+    case CAIRO_OPERATOR_DEST_OVER:
+    case CAIRO_OPERATOR_DEST_IN:
+    case CAIRO_OPERATOR_ADD:
+	return TRUE;
+
+    default:
+	return FALSE;
+    }
+}
+
+static cairo_int_status_t
+_cairo_cogl_surface_mask (void                    *abstract_surface,
+                          cairo_operator_t         op,
+                          const cairo_pattern_t   *source,
+                          const cairo_pattern_t   *mask,
+                          const cairo_clip_t      *clip)
+{
+    cairo_cogl_surface_t *surface = abstract_surface;
+    cairo_composite_rectangles_t extents;
+    cairo_status_t status;
+    CoglPipeline *pipeline;
+    cairo_matrix_t identity;
+
+    /* XXX: Use this to smoke test the acquire_source/dest_image fallback
+     * paths... */
+    //return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (!is_operator_supported (op))
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    status = _cairo_composite_rectangles_init_for_mask (&extents,
+							&surface->base,
+							op, source, mask, clip);
+    if (unlikely (status))
+	return status;
+
+    pipeline = get_source_mask_operator_destination_pipeline (mask, source,
+							      op, surface, &extents);
+    if (!pipeline){
+	status = CAIRO_INT_STATUS_UNSUPPORTED;
+	goto BAIL;
+    }
+
+    _cairo_cogl_maybe_log_clip (surface, &extents);
+
+    cairo_matrix_init_identity (&identity);
+    _cairo_cogl_journal_log_rectangle (surface, pipeline,
+				       extents.bounded.x,
+				       extents.bounded.y,
+				       extents.bounded.width,
+				       extents.bounded.height,
+				       2,
+				       &identity);
+
+    /* The journal will take a reference on the pipeline and clip_path... */
+    cogl_object_unref (pipeline);
+
+BAIL:
+    return status;
+}
+
+static int
+_cairo_cogl_source_n_layers (const cairo_pattern_t *source)
+{
+    switch ((int)source->type)
+    {
+    case CAIRO_PATTERN_TYPE_SOLID:
+	return 0;
+    case CAIRO_PATTERN_TYPE_LINEAR:
+    case CAIRO_PATTERN_TYPE_RADIAL:
+    case CAIRO_PATTERN_TYPE_MESH:
+    case CAIRO_PATTERN_TYPE_SURFACE:
+	return 1;
+    default:
+	g_warning ("Unsupported source type");
+	return 0;
+    }
+}
+
+static cairo_bool_t
+_cairo_cogl_path_fill_meta_equal (const void *key_a, const void *key_b)
+{
+    const cairo_cogl_path_fill_meta_t *meta0 = key_a;
+    const cairo_cogl_path_fill_meta_t *meta1 = key_b;
+
+    return _cairo_path_fixed_equal (meta0->user_path, meta1->user_path);
+}
+
+static cairo_bool_t
+_cairo_cogl_stroke_style_equal (const cairo_stroke_style_t *a,
+			        const cairo_stroke_style_t *b)
+{
+    if (a->line_width == b->line_width &&
+	a->line_cap == b->line_cap &&
+	a->line_join == b->line_join &&
+	a->miter_limit == b->miter_limit &&
+	a->num_dashes == b->num_dashes &&
+	a->dash_offset == b->dash_offset)
+    {
+	unsigned int i;
+	for (i = 0; i < a->num_dashes; i++) {
+	    if (a->dash[i] != b->dash[i])
+		return FALSE;
+	}
+    }
+    return TRUE;
+}
+
+static cairo_bool_t
+_cairo_cogl_path_stroke_meta_equal (const void *key_a, const void *key_b)
+{
+    const cairo_cogl_path_stroke_meta_t *meta0 = key_a;
+    const cairo_cogl_path_stroke_meta_t *meta1 = key_b;
+
+    return _cairo_cogl_stroke_style_equal (&meta0->style, &meta1->style) &&
+	_cairo_path_fixed_equal (meta0->user_path, meta1->user_path);
+}
+
+static cairo_cogl_path_stroke_meta_t *
+_cairo_cogl_path_stroke_meta_reference (cairo_cogl_path_stroke_meta_t *meta)
+{
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&meta->ref_count));
+
+    _cairo_reference_count_inc (&meta->ref_count);
+
+    return meta;
+}
+
+static void
+_cairo_cogl_path_stroke_meta_destroy (cairo_cogl_path_stroke_meta_t *meta)
+{
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&meta->ref_count));
+
+    if (! _cairo_reference_count_dec_and_test (&meta->ref_count))
+	return;
+
+    _cairo_path_fixed_fini (meta->user_path);
+    free (meta->user_path);
+
+    _cairo_stroke_style_fini (&meta->style);
+
+    if (meta->prim)
+	cogl_object_unref (meta->prim);
+
+    free (meta);
+}
+
+static cairo_cogl_path_stroke_meta_t *
+_cairo_cogl_path_stroke_meta_lookup (cairo_cogl_device_t	*ctx,
+				     unsigned long		 hash,
+				     cairo_path_fixed_t		*user_path,
+				     const cairo_stroke_style_t *style,
+				     double			 tolerance)
+{
+    cairo_cogl_path_stroke_meta_t *ret;
+    cairo_cogl_path_stroke_meta_t lookup;
+
+    lookup.cache_entry.hash = hash;
+    lookup.user_path = user_path;
+    lookup.style = *style;
+    lookup.tolerance = tolerance;
+
+    ret = _cairo_cache_lookup (&ctx->path_stroke_staging_cache, &lookup.cache_entry);
+    if (!ret)
+	ret = _cairo_cache_lookup (&ctx->path_stroke_prim_cache, &lookup.cache_entry);
+    return ret;
+}
+
+static void
+_cairo_cogl_path_stroke_meta_set_prim_size (cairo_cogl_surface_t *surface,
+					    cairo_cogl_path_stroke_meta_t *meta,
+					    size_t size)
+{
+    /* now that we know the meta structure is associated with a primitive
+     * we promote it from the staging cache into the primitive cache.
+     */
+
+    /* XXX: _cairo_cache borks if you try and remove an entry that's already
+     * been evicted so we explicitly look it up first... */
+    if (_cairo_cache_lookup (&to_device(surface->base.device)->path_stroke_staging_cache, &meta->cache_entry)) {
+	_cairo_cogl_path_stroke_meta_reference (meta);
+	_cairo_cache_remove (&to_device(surface->base.device)->path_stroke_staging_cache, &meta->cache_entry);
+    }
+
+    meta->cache_entry.size = size;
+    if (_cairo_cache_insert (&to_device(surface->base.device)->path_stroke_prim_cache, &meta->cache_entry) !=
+	CAIRO_STATUS_SUCCESS)
+	_cairo_cogl_path_stroke_meta_destroy (meta);
+}
+
+static unsigned int
+_cairo_cogl_stroke_style_hash (unsigned int hash,
+			       const cairo_stroke_style_t *style)
+{
+    unsigned int i;
+    hash = _cairo_hash_bytes (hash, &style->line_width, sizeof (style->line_width));
+    hash = _cairo_hash_bytes (hash, &style->line_cap, sizeof (style->line_cap));
+    hash = _cairo_hash_bytes (hash, &style->line_join, sizeof (style->line_join));
+    hash = _cairo_hash_bytes (hash, &style->miter_limit, sizeof (style->miter_limit));
+    hash = _cairo_hash_bytes (hash, &style->num_dashes, sizeof (style->num_dashes));
+    hash = _cairo_hash_bytes (hash, &style->dash_offset, sizeof (style->dash_offset));
+    for (i = 0; i < style->num_dashes; i++)
+	hash = _cairo_hash_bytes (hash, &style->dash[i], sizeof (double));
+    return hash;
+}
+
+static cairo_cogl_path_stroke_meta_t *
+_cairo_cogl_get_path_stroke_meta (cairo_cogl_surface_t *surface,
+				  const cairo_stroke_style_t *style,
+				  double tolerance)
+{
+    unsigned long hash;
+    cairo_cogl_path_stroke_meta_t *meta = NULL;
+    cairo_path_fixed_t *meta_path = NULL;
+    cairo_status_t status;
+
+    if (!surface->user_path)
+	return NULL;
+
+    hash = _cairo_path_fixed_hash (surface->user_path);
+    hash = _cairo_cogl_stroke_style_hash (hash, style);
+    hash = _cairo_hash_bytes (hash, &tolerance, sizeof (tolerance));
+
+    meta = _cairo_cogl_path_stroke_meta_lookup (to_device(surface->base.device), hash,
+						surface->user_path, style, tolerance);
+    if (meta)
+	return meta;
+
+    meta = calloc (1, sizeof (cairo_cogl_path_stroke_meta_t));
+    if (!meta)
+	goto BAIL;
+    CAIRO_REFERENCE_COUNT_INIT (&meta->ref_count, 1);
+    meta->cache_entry.hash = hash;
+    meta->counter = 0;
+    meta_path = malloc (sizeof (cairo_path_fixed_t));
+    if (!meta_path)
+	goto BAIL;
+    /* FIXME: we should add a ref-counted wrapper for our user_paths
+     * so we don't have to keep copying them here! */
+    status = _cairo_path_fixed_init_copy (meta_path, surface->user_path);
+    if (unlikely (status))
+	goto BAIL;
+    meta->user_path = meta_path;
+    meta->ctm_inverse = *surface->ctm_inverse;
+
+    status = _cairo_stroke_style_init_copy (&meta->style, style);
+    if (unlikely (status)) {
+	_cairo_path_fixed_fini (meta_path);
+	goto BAIL;
+    }
+    meta->tolerance = tolerance;
+
+    return meta;
+
+BAIL:
+    if (meta_path)
+	free (meta_path);
+    if (meta)
+	free (meta);
+    return NULL;
+}
+
+static cairo_int_status_t
+_cairo_cogl_stroke_to_primitive (cairo_cogl_surface_t	    *surface,
+				 const cairo_path_fixed_t   *path,
+				 const cairo_stroke_style_t *style,
+				 const cairo_matrix_t	    *ctm,
+				 const cairo_matrix_t	    *ctm_inverse,
+				 double			     tolerance,
+				 int			     n_layers,
+				 cairo_bool_t		     one_shot,
+				 CoglPrimitive		   **primitive,
+				 size_t			    *size)
+{
+    cairo_traps_t traps;
+    cairo_int_status_t status;
+
+    _cairo_traps_init (&traps);
+
+    status = _cairo_path_fixed_stroke_to_traps (path, style, ctm, ctm_inverse, tolerance,
+						&traps);
+    if (unlikely (status))
+	goto BAIL;
+
+    if (traps.num_traps == 0) {
+	status = CAIRO_INT_STATUS_NOTHING_TO_DO;
+	goto BAIL;
+    }
+
+    *size = traps.num_traps * sizeof (CoglVertexP2) * 6;
+
+    //g_print ("new stroke prim\n");
+    *primitive = _cairo_cogl_traps_to_composite_prim (surface, &traps, n_layers, one_shot);
+    if (!*primitive) {
+	status = CAIRO_INT_STATUS_NO_MEMORY;
+	goto BAIL;
+    }
+
+BAIL:
+    _cairo_traps_fini (&traps);
+    return status;
+}
+
+static cairo_int_status_t
+_cairo_cogl_surface_stroke (void                       *abstract_surface,
+			    cairo_operator_t            op,
+			    const cairo_pattern_t      *source,
+			    const cairo_path_fixed_t   *path,
+			    const cairo_stroke_style_t *style,
+			    const cairo_matrix_t       *ctm,
+			    const cairo_matrix_t       *ctm_inverse,
+			    double                      tolerance,
+			    cairo_antialias_t           antialias,
+			    const cairo_clip_t         *clip)
+{
+    cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)abstract_surface;
+    cairo_composite_rectangles_t extents;
+    CoglPipeline *pipeline;
+    cairo_status_t status;
+#ifdef ENABLE_PATH_CACHE
+    cairo_cogl_path_stroke_meta_t *meta = NULL;
+    cairo_matrix_t transform_matrix;
+#endif
+    cairo_matrix_t *transform = NULL;
+    gboolean one_shot = TRUE;
+    CoglPrimitive *prim = NULL;
+    cairo_bool_t new_prim = FALSE;
+
+    if (! is_operator_supported (op))
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* FIXME - support unbounded operators */
+    if (!_cairo_operator_bounded_by_mask (op)) {
+	/* Currently IN this is the only unbounded operator we aim to support
+	 * in cairo-cogl. */
+	assert (op == CAIRO_OPERATOR_IN);
+	g_warning ("FIXME: handle stroking with unbounded operators!");
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    status = _cairo_composite_rectangles_init_for_stroke (&extents,
+							  &surface->base,
+							  op, source, path,
+							  style,
+							  ctm,
+							  clip);
+    if (unlikely (status))
+	return status;
+
+#ifdef ENABLE_PATH_CACHE
+    /* FIXME: we are currently leaking the meta state if we don't reach
+     * the cache_insert at the end. */
+    meta = _cairo_cogl_get_path_stroke_meta (surface, style, tolerance);
+    if (meta) {
+	prim = meta->prim;
+	if (prim) {
+	    cairo_matrix_multiply (&transform_matrix, &meta->ctm_inverse, surface->ctm);
+	    transform = &transform_matrix;
+	} else if (meta->counter++ > 10)
+	    one_shot = FALSE;
+    }
+#endif
+
+    if (!prim) {
+	int n_layers = _cairo_cogl_source_n_layers (source);
+	size_t prim_size = 0;
+	status = _cairo_cogl_stroke_to_primitive (surface, path, style,
+						  ctm, ctm_inverse, tolerance,
+						  n_layers, one_shot,
+						  &prim, &prim_size);
+	if (unlikely (status))
+	    return status;
+	new_prim = TRUE;
+#if defined (ENABLE_PATH_CACHE)
+	if (meta) {
+	    meta->prim = cogl_object_ref (prim);
+	    _cairo_cogl_path_stroke_meta_set_prim_size (surface, meta, prim_size);
+	}
+#endif
+    }
+
+    pipeline = get_source_mask_operator_destination_pipeline (NULL, source,
+							      op, surface, &extents);
+    if (!pipeline)
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    _cairo_cogl_maybe_log_clip (surface, &extents);
+
+    _cairo_cogl_journal_log_primitive (surface, pipeline, prim, transform);
+
+    /* The journal will take a reference on the pipeline and primitive... */
+    cogl_object_unref (pipeline);
+    if (new_prim)
+	cogl_object_unref (prim);
+
+    return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_cogl_path_fill_meta_t *
+_cairo_cogl_path_fill_meta_reference (cairo_cogl_path_fill_meta_t *meta)
+{
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&meta->ref_count));
+
+    _cairo_reference_count_inc (&meta->ref_count);
+
+    return meta;
+}
+
+static void
+_cairo_cogl_path_fill_meta_destroy (cairo_cogl_path_fill_meta_t *meta)
+{
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&meta->ref_count));
+
+    if (! _cairo_reference_count_dec_and_test (&meta->ref_count))
+	return;
+
+    _cairo_path_fixed_fini (meta->user_path);
+    free (meta->user_path);
+
+    if (meta->prim)
+	cogl_object_unref (meta->prim);
+
+    free (meta);
+}
+
+static cairo_cogl_path_fill_meta_t *
+_cairo_cogl_path_fill_meta_lookup (cairo_cogl_device_t	*ctx,
+				   unsigned long	 hash,
+				   cairo_path_fixed_t	*user_path)
+{
+    cairo_cogl_path_fill_meta_t *ret;
+    cairo_cogl_path_fill_meta_t lookup;
+
+    lookup.cache_entry.hash = hash;
+    lookup.user_path = user_path;
+
+    ret = _cairo_cache_lookup (&ctx->path_fill_staging_cache, &lookup.cache_entry);
+    if (!ret)
+	ret = _cairo_cache_lookup (&ctx->path_fill_prim_cache, &lookup.cache_entry);
+    return ret;
+}
+
+static void
+_cairo_cogl_path_fill_meta_set_prim_size (cairo_cogl_surface_t *surface,
+					  cairo_cogl_path_fill_meta_t *meta,
+					  size_t size)
+{
+    /* now that we know the meta structure is associated with a primitive
+     * we promote it from the staging cache into the primitive cache.
+     */
+
+    /* XXX: _cairo_cache borks if you try and remove an entry that's already
+     * been evicted so we explicitly look it up first... */
+    if (_cairo_cache_lookup (&to_device(surface->base.device)->path_fill_staging_cache, &meta->cache_entry)) {
+	_cairo_cogl_path_fill_meta_reference (meta);
+	_cairo_cache_remove (&to_device(surface->base.device)->path_fill_staging_cache, &meta->cache_entry);
+    }
+
+    meta->cache_entry.size = size;
+    if (_cairo_cache_insert (&to_device(surface->base.device)->path_fill_prim_cache, &meta->cache_entry) !=
+	CAIRO_STATUS_SUCCESS)
+	_cairo_cogl_path_fill_meta_destroy (meta);
+}
+
+static cairo_cogl_path_fill_meta_t *
+_cairo_cogl_get_path_fill_meta (cairo_cogl_surface_t *surface)
+{
+    unsigned long hash;
+    cairo_cogl_path_fill_meta_t *meta = NULL;
+    cairo_path_fixed_t *meta_path = NULL;
+    cairo_status_t status;
+
+    if (!surface->user_path)
+	return NULL;
+
+    hash = _cairo_path_fixed_hash (surface->user_path);
+
+    meta = _cairo_cogl_path_fill_meta_lookup (to_device(surface->base.device),
+					      hash, surface->user_path);
+    if (meta)
+	return meta;
+
+    meta = calloc (1, sizeof (cairo_cogl_path_fill_meta_t));
+    if (!meta)
+	goto BAIL;
+    meta->cache_entry.hash = hash;
+    meta->counter = 0;
+    CAIRO_REFERENCE_COUNT_INIT (&meta->ref_count, 1);
+    meta_path = malloc (sizeof (cairo_path_fixed_t));
+    if (!meta_path)
+	goto BAIL;
+    /* FIXME: we should add a ref-counted wrapper for our user_paths
+     * so we don't have to keep copying them here! */
+    status = _cairo_path_fixed_init_copy (meta_path, surface->user_path);
+    if (unlikely (status))
+	goto BAIL;
+    meta->user_path = meta_path;
+    meta->ctm_inverse = *surface->ctm_inverse;
+
+    /* To start with - until we associate a CoglPrimitive with the meta
+     * structure - we keep the meta in a staging structure until we
+     * see whether it actually gets re-used. */
+    meta->cache_entry.size = 1;
+    if (_cairo_cache_insert (&to_device(surface->base.device)->path_fill_staging_cache, &meta->cache_entry) !=
+	CAIRO_STATUS_SUCCESS)
+	_cairo_cogl_path_fill_meta_destroy (meta);
+
+    return meta;
+
+BAIL:
+    if (meta_path)
+	free (meta_path);
+    if (meta)
+	free (meta);
+    return NULL;
+}
+
+static cairo_int_status_t
+_cairo_cogl_surface_fill (void			    *abstract_surface,
+                          cairo_operator_t	     op,
+                          const cairo_pattern_t	    *source,
+                          const cairo_path_fixed_t  *path,
+                          cairo_fill_rule_t	     fill_rule,
+                          double		     tolerance,
+                          cairo_antialias_t	     antialias,
+                          const cairo_clip_t	    *clip)
+{
+    cairo_cogl_surface_t *surface = abstract_surface;
+    cairo_composite_rectangles_t extents;
+    cairo_status_t status;
+#ifdef ENABLE_PATH_CACHE
+    cairo_cogl_path_fill_meta_t *meta = NULL;
+    cairo_matrix_t transform_matrix;
+#endif
+    cairo_matrix_t *transform = NULL;
+    cairo_bool_t one_shot = TRUE;
+    CoglPrimitive *prim = NULL;
+    cairo_bool_t new_prim = FALSE;
+    CoglPipeline *pipeline;
+
+    if (! is_operator_supported (op))
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* FIXME - support unbounded operators */
+    if (!_cairo_operator_bounded_by_mask (op)) {
+	/* Currently IN this is the only unbounded operator we aim to support
+	 * in cairo-cogl. */
+	assert (op == CAIRO_OPERATOR_IN);
+	g_warning ("FIXME: handle filling with unbounded operators!");
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    status = _cairo_composite_rectangles_init_for_fill (&extents,
+							&surface->base,
+							op, source, path,
+							clip);
+    if (unlikely (status))
+	return status;
+
+#ifndef FILL_WITH_COGL_PATH
+#ifdef ENABLE_PATH_CACHE
+    meta = _cairo_cogl_get_path_fill_meta (surface);
+    if (meta) {
+	prim = meta->prim;
+	if (prim) {
+	    cairo_matrix_multiply (&transform_matrix, &meta->ctm_inverse, surface->ctm);
+	    transform = &transform_matrix;
+	} else if (meta->counter++ > 10)
+	    one_shot = FALSE;
+    }
+#endif /* ENABLE_PATH_CACHE */
+
+    if (!prim) {
+	int n_layers = _cairo_cogl_source_n_layers (source);
+	size_t prim_size;
+	status = _cairo_cogl_fill_to_primitive (surface, path, fill_rule, tolerance,
+						one_shot, n_layers, &prim, &prim_size);
+	if (unlikely (status))
+	    return status;
+	new_prim = TRUE;
+#ifdef ENABLE_PATH_CACHE
+	if (meta) {
+	    meta->prim = cogl_object_ref (prim);
+	    _cairo_cogl_path_fill_meta_set_prim_size (surface, meta, prim_size);
+	}
+#endif /* ENABLE_PATH_CACHE */
+    }
+
+#endif /* !FILL_WITH_COGL_PATH */
+
+    pipeline = get_source_mask_operator_destination_pipeline (NULL, source,
+							      op, surface, &extents);
+    if (!pipeline)
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    _cairo_cogl_maybe_log_clip (surface, &extents);
+
+#ifndef FILL_WITH_COGL_PATH
+    _cairo_cogl_journal_log_primitive (surface, pipeline, prim, transform);
+    /* The journal will take a reference on the prim */
+    if (new_prim)
+	cogl_object_unref (prim);
+#else
+    CoglPath * cogl_path = _cairo_cogl_util_path_from_cairo (path, fill_rule, tolerance);
+    _cairo_cogl_journal_log_path (surface, pipeline, cogl_path);
+    cogl_object_unref (cogl_path);
+#endif
+
+    /* The journal will take a reference on the pipeline... */
+    cogl_object_unref (pipeline);
+
+    return CAIRO_INT_STATUS_SUCCESS;
+}
+
+cairo_int_status_t
+_cairo_cogl_surface_fill_rectangle (void		     *abstract_surface,
+				    cairo_operator_t	      op,
+				    const cairo_pattern_t    *source,
+				    double		      x,
+				    double		      y,
+				    double		      width,
+				    double		      height,
+				    cairo_matrix_t	     *ctm,
+				    const cairo_clip_t	     *clip)
+{
+    cairo_cogl_surface_t *surface = abstract_surface;
+    CoglPipeline *pipeline;
+
+    if (! is_operator_supported (op))
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* FIXME - support unbounded operators */
+    if (!_cairo_operator_bounded_by_mask (op)) {
+	/* Currently IN this is the only unbounded operator we aim to support
+	 * in cairo-cogl. */
+	assert (op == CAIRO_OPERATOR_IN);
+	g_warning ("FIXME: handle filling with unbounded operators!");
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    /* FIXME */
+#if 0
+    status = _cairo_composite_rectangles_init_for_fill_rectangle (&extents,
+								  &surface->base,
+								  op, source, path,
+								  clip);
+    if (unlikely (status))
+	return status;
+#endif
+
+    if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
+	double x1 = x;
+	double y1 = y;
+	double x2 = x1 + width;
+	double y2 = y1 + height;
+
+	pipeline = get_source_mask_operator_destination_pipeline (NULL, source,
+								  op, surface, NULL);
+	if (!pipeline)
+	    return CAIRO_INT_STATUS_UNSUPPORTED;
+
+	_cairo_cogl_log_clip (surface, clip);
+
+	_cairo_cogl_journal_log_rectangle (surface,
+					   pipeline,
+					   x1, y1, x2, y2,
+					   0,
+					   ctm);
+	return CAIRO_INT_STATUS_SUCCESS;
+    } else
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* TODO:
+     * We need to aquire the textures here, look at the corresponding
+     * attributes and see if this can be trivially handled by logging
+     * a textured rectangle only needing simple scaling or translation
+     * of texture coordinates.
+     *
+     * At this point we should also aim to remap the default
+     * EXTEND_NONE mode to EXTEND_PAD which is more efficient if we
+     * know it makes no difference either way since we can map that to
+     * CLAMP_TO_EDGE.
+     */
+}
+
+static cairo_int_status_t
+_cairo_cogl_surface_show_glyphs (void			*surface,
+                                 cairo_operator_t        op,
+                                 const cairo_pattern_t	*source,
+                                 cairo_glyph_t          *glyphs,
+                                 int                     num_glyphs,
+                                 cairo_scaled_font_t	*scaled_font,
+                                 const cairo_clip_t     *clip)
+{
+    return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+const cairo_surface_backend_t _cairo_cogl_surface_backend = {
+    CAIRO_SURFACE_TYPE_COGL,
+    _cairo_cogl_surface_finish,
+#ifdef NEED_COGL_CONTEXT
+    _cairo_cogl_context_create,
+#else
+    _cairo_default_context_create,
+#endif
+
+    _cairo_cogl_surface_create_similar,
+    NULL, /* create similar image */
+    NULL, /* map to image */
+    NULL, /* unmap image */
+
+    _cairo_cogl_surface_acquire_source_image,
+    _cairo_cogl_surface_release_source_image,
+    NULL, /* snapshot */
+
+    NULL, /* copy_page */
+    NULL, /* show_page */
+
+    _cairo_cogl_surface_get_extents,
+    NULL, /* get_font_options */
+
+    _cairo_cogl_surface_flush, /* flush */
+    NULL, /* mark_dirty_rectangle */
+
+    _cairo_cogl_surface_paint,
+    _cairo_cogl_surface_mask,
+    _cairo_cogl_surface_stroke,
+    _cairo_cogl_surface_fill,
+    NULL, /* fill_stroke*/
+    _cairo_surface_fallback_glyphs,
+};
+
+static cairo_surface_t *
+_cairo_cogl_surface_create_full (cairo_cogl_device_t *dev,
+				 cairo_bool_t ignore_alpha,
+				 CoglFramebuffer *framebuffer,
+				 CoglTexture *texture)
+{
+    cairo_cogl_surface_t *surface;
+    cairo_status_t status;
+
+    status = cairo_device_acquire (&dev->base);
+    if (unlikely (status))
+	return _cairo_surface_create_in_error (status);
+
+    surface = malloc (sizeof (cairo_cogl_surface_t));
+    if (unlikely (surface == NULL))
+        return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    surface->ignore_alpha = ignore_alpha;
+
+    surface->framebuffer = framebuffer;
+    if (framebuffer) {
+	surface->width = cogl_framebuffer_get_width (framebuffer);
+	surface->height = cogl_framebuffer_get_height (framebuffer);
+	surface->cogl_format = cogl_framebuffer_get_color_format (framebuffer);
+	cogl_object_ref (framebuffer);
+    }
+
+    /* FIXME: If texture == NULL and we are given an offscreen framebuffer
+     * then we want a way to poke inside the framebuffer to get a texture */
+    surface->texture = texture;
+    if (texture) {
+	if (!framebuffer) {
+	    surface->width = cogl_texture_get_width (texture);
+	    surface->height = cogl_texture_get_height (texture);
+	    surface->cogl_format = cogl_texture_get_format (texture);
+	}
+	cogl_object_ref (texture);
+    }
+
+    assert(surface->width && surface->height);
+
+    surface->journal = NULL;
+
+    surface->buffer_stack = NULL;
+    surface->buffer_stack_size = 4096;
+
+    surface->last_clip = NULL;
+
+    surface->n_clip_updates_per_frame = 0;
+
+    _cairo_surface_init (&surface->base,
+                         &_cairo_cogl_surface_backend,
+                         &dev->base,
+                         CAIRO_CONTENT_COLOR_ALPHA);
+
+    return &surface->base;
+}
+
+cairo_surface_t *
+cairo_cogl_surface_create (cairo_device_t  *abstract_device,
+			   CoglFramebuffer *framebuffer)
+{
+    cairo_cogl_device_t *dev = (cairo_cogl_device_t *)abstract_device;
+
+    if (abstract_device == NULL)
+	return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_ERROR);
+
+    if (abstract_device->status)
+	return _cairo_surface_create_in_error (abstract_device->status);
+
+    if (abstract_device->backend->type != CAIRO_DEVICE_TYPE_COGL)
+	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
+
+    return _cairo_cogl_surface_create_full (dev, FALSE, framebuffer, NULL);
+}
+slim_hidden_def (cairo_cogl_surface_create);
+
+CoglFramebuffer *
+cairo_cogl_surface_get_framebuffer (cairo_surface_t *abstract_surface)
+{
+    cairo_cogl_surface_t *surface;
+
+    if (abstract_surface->backend != &_cairo_cogl_surface_backend) {
+        _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+        return NULL;
+    }
+
+    surface = (cairo_cogl_surface_t *) abstract_surface;
+
+    return surface->framebuffer;
+}
+slim_hidden_def (cairo_cogl_surface_get_framebuffer);
+
+CoglTexture *
+cairo_cogl_surface_get_texture (cairo_surface_t *abstract_surface)
+{
+    cairo_cogl_surface_t *surface;
+
+    if (abstract_surface->backend != &_cairo_cogl_surface_backend) {
+        _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
+        return NULL;
+    }
+
+    surface = (cairo_cogl_surface_t *) abstract_surface;
+
+    return surface->texture;
+}
+slim_hidden_def (cairo_cogl_surface_get_texture);
+
+static cairo_status_t
+_cairo_cogl_device_flush (void *device)
+{
+    cairo_status_t status;
+
+    status = cairo_device_acquire (device);
+    if (unlikely (status))
+	return status;
+
+    /* XXX: we don't need to flush Cogl here, we just need to flush
+     * any batching we do of compositing primitives. */
+
+    cairo_device_release (device);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_cogl_device_finish (void *device)
+{
+    cairo_status_t status;
+
+    status = cairo_device_acquire (device);
+    if (unlikely (status))
+	return;
+
+    /* XXX: Drop references to external resources */
+
+    cairo_device_release (device);
+}
+
+static void
+_cairo_cogl_device_destroy (void *device)
+{
+    cairo_cogl_device_t *dev = device;
+
+    /* FIXME: Free stuff! */
+
+    g_free (dev);
+}
+
+static const cairo_device_backend_t _cairo_cogl_device_backend = {
+    CAIRO_DEVICE_TYPE_COGL,
+
+    NULL, /* lock */
+    NULL, /* unlock */
+
+    _cairo_cogl_device_flush,
+    _cairo_cogl_device_finish,
+    _cairo_cogl_device_destroy,
+};
+
+static cairo_bool_t
+set_blend (CoglPipeline *pipeline, const char *blend_string)
+{
+    GError *error = NULL;
+    if (!cogl_pipeline_set_blend (pipeline, blend_string, &error)) {
+	g_warning ("Unsupported blend string with current gpu/driver: %s", blend_string);
+	g_error_free (error);
+	return FALSE;
+    }
+    return TRUE;
+}
+
+static cairo_bool_t
+_cairo_cogl_setup_op_state (CoglPipeline *pipeline, cairo_operator_t op)
+{
+    cairo_bool_t status = FALSE;
+
+    switch ((int)op)
+    {
+    case CAIRO_OPERATOR_SOURCE:
+	status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR, 0)");
+	break;
+    case CAIRO_OPERATOR_OVER:
+	status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR, DST_COLOR * (1 - SRC_COLOR[A]))");
+	break;
+    case CAIRO_OPERATOR_IN:
+	status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR * DST_COLOR[A], 0)");
+	break;
+    case CAIRO_OPERATOR_DEST_OVER:
+	status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR * (1 - DST_COLOR[A]), DST_COLOR)");
+	break;
+    case CAIRO_OPERATOR_DEST_IN:
+	status = set_blend (pipeline, "RGBA = ADD (0, DST_COLOR * SRC_COLOR[A])");
+	break;
+    case CAIRO_OPERATOR_ADD:
+	status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR, DST_COLOR)");
+	break;
+    }
+
+    return status;
+}
+
+static void
+create_templates_for_op (cairo_cogl_device_t *dev, cairo_operator_t op)
+{
+    CoglPipeline *base = cogl_pipeline_new ();
+    CoglPipeline *pipeline;
+    CoglColor color;
+
+    if (!_cairo_cogl_setup_op_state (base, op)) {
+	cogl_object_unref (base);
+	return;
+    }
+
+    dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID] = base;
+
+    pipeline = cogl_pipeline_copy (base);
+    cogl_pipeline_set_layer_texture (pipeline, 0, dev->dummy_texture);
+    dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_TEXTURE] = pipeline;
+
+    pipeline = cogl_pipeline_copy (base);
+    cogl_pipeline_set_layer_combine (pipeline, 1,
+                                     "RGBA = MODULATE (PREVIOUS, CONSTANT[A])",
+                                     NULL);
+    cogl_pipeline_set_layer_combine_constant (pipeline, 1, &color);
+    cogl_pipeline_set_layer_texture (pipeline, 1, dev->dummy_texture);
+    dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_MASK_SOLID] = pipeline;
+
+    pipeline = cogl_pipeline_copy (base);
+    cogl_pipeline_set_layer_combine (pipeline, 1,
+                                     "RGBA = MODULATE (PREVIOUS, TEXTURE[A])",
+                                     NULL);
+    cogl_pipeline_set_layer_texture (pipeline, 1, dev->dummy_texture);
+    dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_MASK_TEXTURE] = pipeline;
+}
+
+cairo_device_t *
+cairo_cogl_device_create (CoglContext *cogl_context)
+{
+    cairo_cogl_device_t *dev = g_new0 (cairo_cogl_device_t, 1);
+    cairo_status_t status;
+
+    dev->backend_vtable_initialized = FALSE;
+
+    dev->cogl_context = cogl_context;
+
+    dev->dummy_texture = cogl_texture_new_with_size (1, 1,
+						     COGL_TEXTURE_NO_SLICING,
+						     COGL_PIXEL_FORMAT_ANY);
+    if (!dev->dummy_texture)
+	goto ERROR;
+
+    memset (dev->template_pipelines, 0, sizeof (dev->template_pipelines));
+    create_templates_for_op (dev, CAIRO_OPERATOR_SOURCE);
+    create_templates_for_op (dev, CAIRO_OPERATOR_OVER);
+    create_templates_for_op (dev, CAIRO_OPERATOR_IN);
+    create_templates_for_op (dev, CAIRO_OPERATOR_DEST_OVER);
+    create_templates_for_op (dev, CAIRO_OPERATOR_DEST_IN);
+    create_templates_for_op (dev, CAIRO_OPERATOR_ADD);
+
+    status = _cairo_cache_init (&dev->linear_cache,
+                                _cairo_cogl_linear_gradient_equal,
+                                NULL,
+                                (cairo_destroy_func_t) _cairo_cogl_linear_gradient_destroy,
+                                CAIRO_COGL_LINEAR_GRADIENT_CACHE_SIZE);
+    if (unlikely (status))
+	return _cairo_device_create_in_error(status);
+
+    status = _cairo_cache_init (&dev->path_fill_staging_cache,
+                                _cairo_cogl_path_fill_meta_equal,
+                                NULL,
+                                (cairo_destroy_func_t) _cairo_cogl_path_fill_meta_destroy,
+                                1000);
+
+    status = _cairo_cache_init (&dev->path_stroke_staging_cache,
+                                _cairo_cogl_path_stroke_meta_equal,
+                                NULL,
+                                (cairo_destroy_func_t) _cairo_cogl_path_stroke_meta_destroy,
+                                1000);
+
+    status = _cairo_cache_init (&dev->path_fill_prim_cache,
+                                _cairo_cogl_path_fill_meta_equal,
+                                NULL,
+                                (cairo_destroy_func_t) _cairo_cogl_path_fill_meta_destroy,
+                                CAIRO_COGL_PATH_META_CACHE_SIZE);
+
+    status = _cairo_cache_init (&dev->path_stroke_prim_cache,
+                                _cairo_cogl_path_stroke_meta_equal,
+                                NULL,
+                                (cairo_destroy_func_t) _cairo_cogl_path_stroke_meta_destroy,
+                                CAIRO_COGL_PATH_META_CACHE_SIZE);
+
+    _cairo_device_init (&dev->base, &_cairo_cogl_device_backend);
+    return &dev->base;
+
+ERROR:
+    g_free (dev);
+    return NULL;
+}
+slim_hidden_def (cairo_cogl_device_create);
+
+void
+cairo_cogl_surface_end_frame (cairo_surface_t *abstract_surface)
+{
+    cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)abstract_surface;
+    cairo_surface_flush (abstract_surface);
+
+    //g_print ("n_clip_update_per_frame = %d\n", surface->n_clip_updates_per_frame);
+    surface->n_clip_updates_per_frame = 0;
+}
+slim_hidden_def (cairo_cogl_surface_end_frame);
diff --git a/src/cairo-cogl-utils-private.h b/src/cairo-cogl-utils-private.h
new file mode 100644
index 0000000..ee77f30
--- /dev/null
+++ b/src/cairo-cogl-utils-private.h
@@ -0,0 +1,54 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.og/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * Contributor(s):
+ *      Robert Bragg <robert at linux.intel.com>
+ */
+
+#ifndef CAIRO_COGL_UTILS_PRIVATE_H
+#define CAIRO_COGL_UTILS_PRIVATE_H
+
+#include "cairo-path-fixed-private.h"
+#include <cogl/cogl2-experimental.h>
+
+CoglPath *
+_cairo_cogl_util_path_from_cairo (const cairo_path_fixed_t *path,
+				  cairo_fill_rule_t fill_rule,
+				  float tolerance);
+
+int
+_cairo_cogl_util_next_p2 (int a);
+
+#define CAIRO_FIXED_ONE_FLOAT ((float)(1 << CAIRO_FIXED_FRAC_BITS))
+
+static inline float
+_cairo_cogl_util_fixed_to_float (cairo_fixed_t f)
+{
+    return ((float) f) / CAIRO_FIXED_ONE_FLOAT;
+}
+
+#endif /* CAIRO_COGL_UTILS_PRIVATE_H */
diff --git a/src/cairo-cogl-utils.c b/src/cairo-cogl-utils.c
new file mode 100644
index 0000000..4f02aaa
--- /dev/null
+++ b/src/cairo-cogl-utils.c
@@ -0,0 +1,126 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.og/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * Contributor(s):
+ *      Robert Bragg <robert at linux.intel.com>
+ */
+
+#include "cairoint.h"
+#include "cairo-cogl-utils-private.h"
+
+#include <cogl/cogl.h>
+#include <glib.h>
+
+static cairo_status_t
+_cogl_move_to (void		   *closure,
+	       const cairo_point_t *point)
+{
+    cogl_path_move_to (closure,
+		       _cairo_cogl_util_fixed_to_float (point->x),
+		       _cairo_cogl_util_fixed_to_float (point->y));
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cogl_line_to (void		   *closure,
+	       const cairo_point_t *point)
+{
+    cogl_path_line_to (closure,
+		       _cairo_cogl_util_fixed_to_float (point->x),
+		       _cairo_cogl_util_fixed_to_float (point->y));
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cogl_curve_to (void                *closure,
+		const cairo_point_t *p0,
+		const cairo_point_t *p1,
+		const cairo_point_t *p2)
+{
+    cogl_path_curve_to (closure,
+			_cairo_cogl_util_fixed_to_float (p0->x),
+			_cairo_cogl_util_fixed_to_float (p0->y),
+			_cairo_cogl_util_fixed_to_float (p1->x),
+			_cairo_cogl_util_fixed_to_float (p1->y),
+			_cairo_cogl_util_fixed_to_float (p2->x),
+			_cairo_cogl_util_fixed_to_float (p2->y));
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cogl_close_path (void *closure)
+{
+    cogl_path_close (closure);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+CoglPath *
+_cairo_cogl_util_path_from_cairo (const cairo_path_fixed_t *path,
+				  cairo_fill_rule_t fill_rule,
+				  float tolerance)
+{
+    CoglPath *cogl_path = cogl_path_new ();
+    cairo_status_t status;
+
+    if (fill_rule == CAIRO_FILL_RULE_EVEN_ODD)
+	cogl_path_set_fill_rule (cogl_path, COGL_PATH_FILL_RULE_EVEN_ODD);
+    else
+	cogl_path_set_fill_rule (cogl_path, COGL_PATH_FILL_RULE_NON_ZERO);
+
+#ifdef USE_CAIRO_PATH_FLATTENER
+    /* XXX: rely on cairo to do path flattening, since it seems Cogl's
+     * curve_to flattening is much slower */
+    status = _cairo_path_fixed_interpret_flat (path,
+					       _cogl_move_to,
+					       _cogl_line_to,
+					       _cogl_close_path,
+					       cogl_path,
+					       tolerance);
+#else
+    status = _cairo_path_fixed_interpret (path,
+					  _cogl_move_to,
+					  _cogl_line_to,
+					  _cogl_curve_to,
+					  _cogl_close_path,
+					  cogl_path);
+#endif
+
+    assert (status == CAIRO_STATUS_SUCCESS);
+    return cogl_path;
+}
+
+int
+_cairo_cogl_util_next_p2 (int a)
+{
+  int rval = 1;
+
+  while (rval < a)
+    rval <<= 1;
+
+  return rval;
+}
+
diff --git a/src/cairo-cogl.h b/src/cairo-cogl.h
new file mode 100644
index 0000000..f270d74
--- /dev/null
+++ b/src/cairo-cogl.h
@@ -0,0 +1,69 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2011 Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Mozilla Corporation.
+ *
+ * Contributor(s):
+ *      Robert Bragg <robert at linux.intel.com>
+ */
+
+#ifndef CAIRO_VG_H
+#define CAIRO_VG_H
+
+#include "cairo.h"
+
+#if CAIRO_HAS_COGL_SURFACE
+
+#include <cogl/cogl2-experimental.h>
+
+CAIRO_BEGIN_DECLS
+
+cairo_public cairo_device_t *
+cairo_cogl_device_create (CoglContext *context);
+
+cairo_public cairo_surface_t *
+cairo_cogl_surface_create (cairo_device_t *device,
+			   CoglFramebuffer *framebuffer);
+
+cairo_public CoglFramebuffer *
+cairo_cogl_surface_get_framebuffer (cairo_surface_t *surface);
+
+cairo_public CoglTexture *
+cairo_cogl_surface_get_texture (cairo_surface_t *surface);
+
+cairo_public void
+cairo_cogl_surface_end_frame (cairo_surface_t *surface);
+
+CAIRO_END_DECLS
+
+#else  /* CAIRO_HAS_COGL_SURFACE*/
+# error Cairo was not compiled with support for the Cogl backend
+#endif /* CAIRO_HAS_COGL_SURFACE*/
+
+#endif /* CAIRO_COGL_H */
diff --git a/src/cairo-debug.c b/src/cairo-debug.c
index b4cde7d..ebae072 100644
--- a/src/cairo-debug.c
+++ b/src/cairo-debug.c
@@ -90,6 +90,10 @@ cairo_debug_reset_static_data (void)
 
     _cairo_default_context_reset_static_data ();
 
+#if CAIRO_HAS_COGL_SURFACE
+    _cairo_cogl_context_reset_static_data ();
+#endif
+
     CAIRO_MUTEX_FINALIZE ();
 }
 
diff --git a/src/cairo-default-context-private.h b/src/cairo-default-context-private.h
index 32fd12d..b8d1a9a 100644
--- a/src/cairo-default-context-private.h
+++ b/src/cairo-default-context-private.h
@@ -55,4 +55,10 @@ struct _cairo_default_context {
 cairo_private cairo_t *
 _cairo_default_context_create (void *target);
 
+cairo_status_t
+_cairo_default_context_init (cairo_default_context_t *cr, void *target);
+
+void
+_cairo_default_context_fini (cairo_default_context_t *cr);
+
 #endif /* CAIRO_DEFAULT_CONTEXT_PRIVATE_H */
diff --git a/src/cairo-default-context.c b/src/cairo-default-context.c
index 98cc668..b58a766 100644
--- a/src/cairo-default-context.c
+++ b/src/cairo-default-context.c
@@ -63,11 +63,9 @@ _cairo_default_context_reset_static_data (void)
     _freed_pool_reset (&context_pool);
 }
 
-static void
-_cairo_default_context_destroy (void *abstract_cr)
+void
+_cairo_default_context_fini (cairo_default_context_t *cr)
 {
-    cairo_default_context_t *cr = abstract_cr;
-
     while (cr->gstate != &cr->gstate_tail[0]) {
 	if (_cairo_gstate_restore (&cr->gstate, &cr->gstate_freelist))
 	    break;
@@ -84,6 +82,14 @@ _cairo_default_context_destroy (void *abstract_cr)
     _cairo_path_fixed_fini (cr->path);
 
     _cairo_fini (&cr->base);
+}
+
+static void
+_cairo_default_context_destroy (void *abstract_cr)
+{
+    cairo_default_context_t *cr = abstract_cr;
+
+    _cairo_default_context_fini (cr);
 
     /* mark the context as invalid to protect against misuse */
     cr->base.status = CAIRO_STATUS_NULL_POINTER;
@@ -1381,6 +1387,19 @@ static const cairo_backend_t _cairo_default_context_backend = {
     _cairo_default_context_show_page,
 };
 
+cairo_status_t
+_cairo_default_context_init (cairo_default_context_t *cr, void *target)
+{
+    _cairo_init (&cr->base, &_cairo_default_context_backend);
+    _cairo_path_fixed_init (cr->path);
+
+    cr->gstate = &cr->gstate_tail[0];
+    cr->gstate_freelist = &cr->gstate_tail[1];
+    cr->gstate_tail[1].next = NULL;
+
+    return _cairo_gstate_init (cr->gstate, target);
+}
+
 cairo_t *
 _cairo_default_context_create (void *target)
 {
@@ -1394,14 +1413,7 @@ _cairo_default_context_create (void *target)
 	    return _cairo_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
     }
 
-    _cairo_init (&cr->base, &_cairo_default_context_backend);
-    _cairo_path_fixed_init (cr->path);
-
-    cr->gstate = &cr->gstate_tail[0];
-    cr->gstate_freelist = &cr->gstate_tail[1];
-    cr->gstate_tail[1].next = NULL;
-
-    status = _cairo_gstate_init (cr->gstate, target);
+    status = _cairo_default_context_init (cr, target);
     if (unlikely (status)) {
 	_freed_pool_put (&context_pool, cr);
 	return _cairo_create_in_error (status);
diff --git a/src/cairo-image-source.c b/src/cairo-image-source.c
index 943790d..fde4475 100644
--- a/src/cairo-image-source.c
+++ b/src/cairo-image-source.c
@@ -962,7 +962,7 @@ _cairo_image_source_finish (void *abstract_surface)
     return CAIRO_STATUS_SUCCESS;
 }
 
-static const cairo_surface_backend_t cairo_image_source_backend = {
+const cairo_surface_backend_t _cairo_image_source_backend = {
     CAIRO_SURFACE_TYPE_IMAGE,
     _cairo_image_source_finish,
     NULL, /* read-only wrapper */
@@ -993,7 +993,7 @@ _cairo_image_source_create_for_pattern (cairo_surface_t *dst,
     }
 
     _cairo_surface_init (&source->base,
-			 &cairo_image_source_backend,
+			 &_cairo_image_source_backend,
 			 NULL, /* device */
 			 CAIRO_CONTENT_COLOR_ALPHA);
 
diff --git a/src/cairo-image-surface-private.h b/src/cairo-image-surface-private.h
index 227a447..2777e2f 100644
--- a/src/cairo-image-surface-private.h
+++ b/src/cairo-image-surface-private.h
@@ -74,6 +74,7 @@ typedef struct _cairo_image_source {
 } cairo_image_source_t;
 
 cairo_private extern const cairo_surface_backend_t _cairo_image_surface_backend;
+cairo_private extern const cairo_surface_backend_t _cairo_image_source_backend;
 
 cairo_private const cairo_compositor_t *
 _cairo_image_mask_compositor_get (void);
@@ -163,6 +164,20 @@ _cairo_surface_is_image (const cairo_surface_t *surface)
     return surface->backend == &_cairo_image_surface_backend;
 }
 
+/**
+ * _cairo_surface_is_image_source:
+ * @surface: a #cairo_surface_t
+ *
+ * Checks if a surface is an #cairo_image_source_t
+ *
+ * Return value: %TRUE if the surface is an image source
+ **/
+static inline cairo_bool_t
+_cairo_surface_is_image_source (const cairo_surface_t *surface)
+{
+    return surface->backend == &_cairo_image_source_backend;
+}
+
 CAIRO_END_DECLS
 
 #endif /* CAIRO_IMAGE_SURFACE_PRIVATE_H */
diff --git a/src/cairo.h b/src/cairo.h
index f5450fe..aa4eb2b 100644
--- a/src/cairo.h
+++ b/src/cairo.h
@@ -2068,6 +2068,7 @@ typedef enum _cairo_device_type {
     CAIRO_DEVICE_TYPE_XCB,
     CAIRO_DEVICE_TYPE_XLIB,
     CAIRO_DEVICE_TYPE_XML,
+    CAIRO_DEVICE_TYPE_COGL,
 
     CAIRO_DEVICE_TYPE_INVALID = -1
 } cairo_device_type_t;
@@ -2260,6 +2261,7 @@ cairo_surface_status (cairo_surface_t *surface);
  * @CAIRO_SURFACE_TYPE_SUBSURFACE: The surface is a subsurface created with
  *   cairo_surface_create_for_rectangle(), since 1.10
  * @CAIRO_SURFACE_TYPE_MIME: The surface is a callback (mime) surface, since 1.12
+ * @CAIRO_SURFACE_TYPE_COGL: This surface is of type Cogl
  *
  * #cairo_surface_type_t is used to describe the type of a given
  * surface. The surface types are also known as "backends" or "surface
@@ -2310,7 +2312,8 @@ typedef enum _cairo_surface_type {
     CAIRO_SURFACE_TYPE_XML,
     CAIRO_SURFACE_TYPE_SKIA,
     CAIRO_SURFACE_TYPE_SUBSURFACE,
-    CAIRO_SURFACE_TYPE_MIME
+    CAIRO_SURFACE_TYPE_MIME,
+    CAIRO_SURFACE_TYPE_COGL
 } cairo_surface_type_t;
 
 cairo_public cairo_surface_type_t
diff --git a/src/cairoint.h b/src/cairoint.h
index 4f27a1e..10bb2f7 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -401,6 +401,11 @@ _cairo_ft_font_reset_static_data (void);
 cairo_private void
 _cairo_win32_font_reset_static_data (void);
 
+#if CAIRO_HAS_COGL_SURFACE
+void
+_cairo_cogl_context_reset_static_data (void);
+#endif
+
 /* the font backend interface */
 
 struct _cairo_unscaled_font_backend {
commit 5783bbfc750c73a4b36ad30a054de59a7ee99f34
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sun Oct 9 19:53:56 2011 +0100

    debug-traps

diff --git a/src/cairo-traps.c b/src/cairo-traps.c
index a6e7f94..30e626f 100644
--- a/src/cairo-traps.c
+++ b/src/cairo-traps.c
@@ -715,3 +715,38 @@ _cairo_traps_path (const cairo_traps_t *traps,
 
     return CAIRO_STATUS_SUCCESS;
 }
+
+void
+_cairo_debug_print_traps (FILE *file, const cairo_traps_t *traps)
+{
+    cairo_box_t extents;
+    int n;
+
+#if 0
+    if (traps->has_limits) {
+	printf ("%s: limits=(%d, %d, %d, %d)\n",
+		filename,
+		traps->limits.p1.x, traps->limits.p1.y,
+		traps->limits.p2.x, traps->limits.p2.y);
+    }
+#endif
+
+    _cairo_traps_extents (traps, &extents);
+    fprintf (file, "extents=(%d, %d, %d, %d)\n",
+	     extents.p1.x, extents.p1.y,
+	     extents.p2.x, extents.p2.y);
+
+    for (n = 0; n < traps->num_traps; n++) {
+	fprintf (file, "%d %d L:(%d, %d), (%d, %d) R:(%d, %d), (%d, %d)\n",
+		 traps->traps[n].top,
+		 traps->traps[n].bottom,
+		 traps->traps[n].left.p1.x,
+		 traps->traps[n].left.p1.y,
+		 traps->traps[n].left.p2.x,
+		 traps->traps[n].left.p2.y,
+		 traps->traps[n].right.p1.x,
+		 traps->traps[n].right.p1.y,
+		 traps->traps[n].right.p2.x,
+		 traps->traps[n].right.p2.y);
+    }
+}
diff --git a/src/cairoint.h b/src/cairoint.h
index 28f3e9f..4f27a1e 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -1951,6 +1951,9 @@ cairo_private void
 _cairo_debug_print_polygon (FILE *stream, cairo_polygon_t *polygon);
 
 cairo_private void
+_cairo_debug_print_traps (FILE *file, const cairo_traps_t *traps);
+
+cairo_private void
 _cairo_debug_print_clip (FILE *stream, const cairo_clip_t *clip);
 
 #endif
commit 2cb4eb53feb9223db8db66c9f86d962cc7248f27
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sun Oct 9 19:53:16 2011 +0100

    perf

diff --git a/perf/cairo-perf-chart.c b/perf/cairo-perf-chart.c
index 70ff353..0ecf8b4 100644
--- a/perf/cairo-perf-chart.c
+++ b/perf/cairo-perf-chart.c
@@ -54,7 +54,7 @@ struct color {
 };
 
 #define FONT_SIZE 12
-#define PAD (FONT_SIZE/2+1)
+#define PAD (4)
 
 #define MAX(a,b) ((a) > (b) ? (a) : (b))
 
@@ -108,13 +108,13 @@ trim_outliers (double *values,
     qsort (values, num_values,
 	   sizeof (double), _double_cmp);
 
-    q1		= values[1*num_values / 4];
-    q3		= values[3*num_values / 4];
+    q1		= values[1*num_values / 6];
+    q3		= values[5*num_values / 6];
 
     iqr = q3 - q1;
 
-    outlier_min = q1 - 1.5 * iqr;
-    outlier_max = q3 + 1.5 * iqr;
+    outlier_min = q1 - 3 * iqr;
+    outlier_max = q3 + 3 * iqr;
 
     i = 0;
     while (i < num_values && values[i] < outlier_min)
@@ -140,6 +140,8 @@ find_ranges (struct chart *chart)
     double test_time;
     int seen_non_null;
     int num_tests = 0;
+    double slow_sum = 0, fast_sum = 0, sum;
+    int slow_count = 0, fast_count = 0;
     int i;
 
     num_values = 0;
@@ -201,17 +203,25 @@ find_ranges (struct chart *chart)
 		    test_time = report_time;
 
 		if (chart->relative) {
-		    double v = to_factor (test_time / report_time);
-		    if (num_values == size_values) {
-			size_values *= 2;
-			values = xrealloc (values,
-					   size_values * sizeof (double));
+		    if (test_time != report_time) {
+			double v = to_factor (test_time / report_time);
+			if (num_values == size_values) {
+			    size_values *= 2;
+			    values = xrealloc (values,
+					       size_values * sizeof (double));
+			}
+			values[num_values++] = v;
+			if (v < min)
+			    min = v;
+			if (v > max)
+			    max = v;
+			if (v > 0)
+			    fast_sum += v/100, fast_count++;
+			else
+			    slow_sum += v/100, slow_count++;
+			sum += v/100;
+			printf ("%s %d: %f\n", min_test->name, num_values, v);
 		    }
-		    values[num_values++] = v;
-		    if (v < min)
-			min = v;
-		    if (v > max)
-			max = v;
 		} else {
 		    if (report_time < min)
 			min = report_time;
@@ -230,6 +240,9 @@ find_ranges (struct chart *chart)
 
     free (values);
     free (tests);
+
+    printf ("%d: slow[%d] average: %f, fast[%d] average: %f, %f\n",
+	    num_values, slow_count, slow_sum / slow_count, fast_count, fast_sum / fast_count, sum / num_values);
 }
 
 #define SET_COLOR(C, R, G, B) (C)->red = (R), (C)->green = (G), (C)->blue = (B)
@@ -390,19 +403,29 @@ add_label (struct chart *c,
 
     cairo_save (c->cr);
     dx = c->width / (double) c->num_tests;
-    if (dx / 2 - PAD < 6)
+    if (dx / 2 - PAD < 4)
 	return;
     cairo_set_font_size (c->cr, dx / 2 - PAD);
     cairo_text_extents (c->cr, label, &extents);
 
+    cairo_set_source_rgb (c->cr, .5, .5, .5);
+
     x = (test + .5) * dx;
-    cairo_translate (c->cr, x, PAD / 2);
+    cairo_save (c->cr);
+    cairo_translate (c->cr, x, c->height - PAD / 2);
     cairo_rotate (c->cr, -M_PI/2);
+    cairo_move_to (c->cr, 0, -extents.y_bearing/2);
+    cairo_show_text (c->cr, label);
+    cairo_restore (c->cr);
 
-    cairo_set_source_rgb (c->cr, .5, .5, .5);
+    cairo_save (c->cr);
+    cairo_translate (c->cr, x, PAD / 2);
+    cairo_rotate (c->cr, -M_PI/2);
     cairo_move_to (c->cr, -extents.width, -extents.y_bearing/2);
     cairo_show_text (c->cr, label);
     cairo_restore (c->cr);
+
+    cairo_restore (c->cr);
 }
 
 static void
@@ -840,7 +863,7 @@ main (int	  argc,
 	    chart.names[chart.num_reports] = argv[i] + 7;
 	} else {
 	    cairo_perf_report_load (&chart.reports[chart.num_reports++],
-				    argv[i],
+				    argv[i], i,
 				    test_report_cmp_name);
 	}
     }


More information about the cairo-commit mailing list