[cairo-commit] 42 commits - boilerplate/cairo-boilerplate-cogl.c meson.build 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/Makefile.sources src/meson.build

GitLab Mirror gitlab-mirror at kemper.freedesktop.org
Sun Aug 30 09:31:31 UTC 2020


 boilerplate/cairo-boilerplate-cogl.c |  117 
 meson.build                          |    4 
 src/Makefile.sources                 |    8 
 src/cairo-cogl-context-private.h     |   52 
 src/cairo-cogl-context.c             |  822 ------
 src/cairo-cogl-gradient-private.h    |   11 
 src/cairo-cogl-gradient.c            |  130 -
 src/cairo-cogl-private.h             |   86 
 src/cairo-cogl-surface.c             | 4364 ++++++++++++++++++++++-------------
 src/cairo-cogl-utils-private.h       |   54 
 src/cairo-cogl-utils.c               |  126 -
 src/cairo-cogl.h                     |   27 
 src/cairo-debug.c                    |    4 
 src/meson.build                      |    2 
 14 files changed, 3053 insertions(+), 2754 deletions(-)

New commits:
commit 2f39018c2c6661de11d5e7c3f385e7f0b338f5b1
Author: George Matsumura <gmmatsumura01 at bvsd.org>
Date:   Tue Aug 25 03:26:27 2020 -0600

    meson: Remove unconditional disable of cogl backend build

diff --git a/meson.build b/meson.build
index dfd3fdaff..ae21ee446 100644
--- a/meson.build
+++ b/meson.build
@@ -518,10 +518,8 @@ if feature_conf.get('CAIRO_HAS_GL_SURFACE', 0) == 0 and feature_conf.get('CAIRO_
   endif
 endif
 
-# FIXME: Broken for me, depends on experimental API
-# https://gitlab.freedesktop.org/cairo/cairo/-/issues/410
 cogl_dep = dependency('cogl-2.0-experimental', required: get_option('cogl'))
-if cogl_dep.found() and false
+if cogl_dep.found()
   deps += [cogl_dep]
 
   feature_conf.set('CAIRO_HAS_COGL_SURFACE', 1)
commit e47d0de8d2f583c44e9f0aa57ff158e6307b7745
Author: George Matsumura <gmmatsumura01 at bvsd.org>
Date:   Sun Aug 23 21:23:01 2020 -0600

    cogl: Remove filling with cogl-path
    
    This removes code that uses the cogl-path library, which was not
    used except when manually modifying a preprocessor flag. It could
    not use path caching, was slightly broken, and all of its
    functionality was provided better by different code paths.
    
    Signed-off-by: George Matsumura <gmmatsumura01 at bvsd.org>

diff --git a/configure.ac b/configure.ac
index d5fb4ceda..8b2ab18ec 100644
--- a/configure.ac
+++ b/configure.ac
@@ -400,7 +400,7 @@ CAIRO_ENABLE_SURFACE_BACKEND(glesv3, OpenGLESv3, no, [
 
 dnl ===========================================================================
 CAIRO_ENABLE_SURFACE_BACKEND(cogl, Cogl, no, [
-  cogl_REQUIRES="cogl-2.0-experimental, cogl-path-2.0-experimental"
+  cogl_REQUIRES="cogl-2.0-experimental"
   PKG_CHECK_MODULES(cogl, $cogl_REQUIRES,, [use_cogl="no"])
 ])
 
diff --git a/src/Makefile.sources b/src/Makefile.sources
index 2b573c8e5..5e84cbfc6 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -467,8 +467,6 @@ 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-utils-private.h
+		     cairo-cogl-gradient-private.h
 cairo_cogl_sources = cairo-cogl-surface.c \
-		     cairo-cogl-gradient.c \
-		     cairo-cogl-utils.c
+		     cairo-cogl-gradient.c
diff --git a/src/cairo-cogl-surface.c b/src/cairo-cogl-surface.c
index fce03db5d..8fe8905e6 100644
--- a/src/cairo-cogl-surface.c
+++ b/src/cairo-cogl-surface.c
@@ -43,7 +43,6 @@
 #include "cairo-cogl-gradient-private.h"
 #include "cairo-arc-private.h"
 #include "cairo-traps-private.h"
-#include "cairo-cogl-utils-private.h"
 #include "cairo-surface-subsurface-inline.h"
 #include "cairo-surface-fallback-private.h"
 #include "cairo-surface-offset-private.h"
@@ -54,8 +53,6 @@
 #include <glib.h>
 
 #define CAIRO_COGL_DEBUG 0
-//#define FILL_WITH_COGL_PATH
-//#define USE_CAIRO_PATH_FLATTENER
 //#define DISABLE_BATCHING
 #define MAX_JOURNAL_SIZE 100
 
@@ -102,7 +99,6 @@ typedef struct _cairo_cogl_pipeline {
 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;
 
@@ -134,13 +130,6 @@ typedef struct _cairo_cogl_journal_prim_entry {
     cairo_matrix_t transform;
 } cairo_cogl_journal_prim_entry_t;
 
-typedef struct _cairo_cogl_journal_path_entry {
-    cairo_cogl_journal_entry_t base;
-    cairo_cogl_pipeline_t *pipeline;
-
-    CoglPath *path;
-} cairo_cogl_journal_path_entry_t;
-
 typedef struct _cairo_cogl_path_fill_meta {
     cairo_cache_entry_t	base;
     cairo_path_fixed_t path;
@@ -324,19 +313,6 @@ _cairo_cogl_journal_discard (cairo_cogl_surface_t *surface)
 	    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->pipeline);
-	    cogl_object_unref (path_entry->path);
-            if (path_entry->pipeline->has_src_tex_clip)
-                _cairo_path_fixed_fini (&path_entry->pipeline->src_tex_clip);
-            if (path_entry->pipeline->has_mask_tex_clip)
-                _cairo_path_fixed_fini (&path_entry->pipeline->mask_tex_clip);
-            g_free (path_entry->pipeline);
-	    entry_size = sizeof (cairo_cogl_journal_path_entry_t);
-	    break;
-	}
 	default:
 	    assert (0); /* not reached! */
 	    entry_size = 0; /* avoid compiler warning */
@@ -360,39 +336,6 @@ _cairo_cogl_journal_free (cairo_cogl_surface_t *surface)
     surface->journal = NULL;
 }
 
-#ifdef FILL_WITH_COGL_PATH
-static void
-_cairo_cogl_journal_log_path (cairo_cogl_surface_t  *surface,
-			      cairo_cogl_pipeline_t *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 = pipeline;
-    entry->path = cogl_object_ref (path);
-
-    g_queue_push_tail (surface->journal, entry);
-
-#ifdef DISABLE_BATCHING
-    _cairo_cogl_journal_flush (surface);
-#else
-    /* To avoid consuming too much memory, flush the journal if it gets
-     * to a certain size */
-    if (g_queue_get_length (surface->journal) > MAX_JOURNAL_SIZE)
-        _cairo_cogl_journal_flush (surface);
-#endif /* DISABLE_BATCHING */
-}
-#endif /* FILL_WITH_COGL_PATH */
-
 static void
 _cairo_cogl_journal_log_primitive (cairo_cogl_surface_t  *surface,
 				   cairo_cogl_pipeline_t *pipeline,
@@ -576,14 +519,14 @@ _cairo_cogl_traps_to_triangles_buffer (cairo_cogl_surface_t *surface,
 	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[0].x = _cairo_fixed_to_double (trap->left.p1.x);
+	p[0].y = _cairo_fixed_to_double (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[1].x = _cairo_fixed_to_double (trap->left.p2.x);
+	p[1].y = _cairo_fixed_to_double (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[2].x = _cairo_fixed_to_double (trap->right.p2.x);
+	p[2].y = _cairo_fixed_to_double (trap->right.p2.y);
 
 	p[3].x = p[0].x;
 	p[3].y = p[0].y;
@@ -591,8 +534,8 @@ _cairo_cogl_traps_to_triangles_buffer (cairo_cogl_surface_t *surface,
 	p[4].x = p[2].x;
 	p[4].y = p[2].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);
+	p[5].x = _cairo_fixed_to_double (trap->right.p1.x);
+	p[5].y = _cairo_fixed_to_double (trap->right.p1.y);
     }
 
     if (!one_shot)
@@ -1527,43 +1470,6 @@ _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface)
 	    cogl_framebuffer_pop_matrix (surface->framebuffer);
 	    break;
 	}
-	case CAIRO_COGL_JOURNAL_ENTRY_TYPE_PATH: {
-	    cairo_cogl_journal_path_entry_t *path_entry =
-		(cairo_cogl_journal_path_entry_t *)entry;
-            cairo_bool_t needs_vertex_render;
-            CoglPipeline *unbounded_pipeline;
-
-            _cairo_cogl_apply_tex_clips (surface,
-                                         &clip_stack_depth,
-                                         path_entry->pipeline);
-
-            /* Use this until cogl2_path_fill is updated to take
-             * framebuffer and pipeline arguments */
-            cogl_framebuffer_fill_path (surface->framebuffer,
-                                        path_entry->pipeline->pipeline,
-                                        path_entry->path);
-
-            _cairo_cogl_unbounded_render (surface,
-                                          &clip_stack_depth,
-                                          path_entry->pipeline,
-                                          &needs_vertex_render);
-            if (needs_vertex_render) {
-                unbounded_pipeline =
-                    _cairo_cogl_setup_unbounded_area_pipeline (surface,
-                                                               path_entry->pipeline->op);
-                cogl_framebuffer_fill_path (surface->framebuffer,
-                                            unbounded_pipeline,
-                                            path_entry->path);
-                cogl_object_unref (unbounded_pipeline);
-            }
-            _cairo_cogl_post_unbounded_render (surface,
-                                               &clip_stack_depth,
-                                               path_entry->pipeline);
-
-	    cogl_framebuffer_pop_matrix (surface->framebuffer);
-
-	    break;
-	}
 	default:
 	    assert (0); /* not reached! */
 	}
@@ -3689,7 +3595,6 @@ _cairo_cogl_surface_fill (void			    *abstract_surface,
     if (unlikely (status))
 	return status;
 
-#ifndef FILL_WITH_COGL_PATH
     status = _cairo_cogl_fill_to_primitive (surface, path, fill_rule,
                                             tolerance, TRUE, &prim,
                                             &transform);
@@ -3700,7 +3605,6 @@ _cairo_cogl_surface_fill (void			    *abstract_surface,
     } else if (unlikely (status)) {
         goto BAIL;
     }
-#endif /* !FILL_WITH_COGL_PATH */
 
     get_source_mask_operator_destination_pipelines (pipelines,
                                                     NULL,
@@ -3716,7 +3620,6 @@ _cairo_cogl_surface_fill (void			    *abstract_surface,
 
     _cairo_cogl_maybe_log_clip (surface, &extents);
 
-#ifndef FILL_WITH_COGL_PATH
     if (pipelines[0])
         _cairo_cogl_journal_log_primitive (surface,
                                            pipelines[0],
@@ -3727,20 +3630,6 @@ _cairo_cogl_surface_fill (void			    *abstract_surface,
                                            pipelines[1],
                                            prim,
                                            &transform);
-#else
-    CoglPath * cogl_path = _cairo_cogl_util_path_from_cairo (path, fill_rule, tolerance);
-
-    if (pipelines[0])
-        _cairo_cogl_journal_log_path (surface,
-                                      pipelines[0],
-                                      cogl_path);
-    if (pipelines[1])
-        _cairo_cogl_journal_log_path (surface,
-                                      pipelines[1],
-                                      cogl_path);
-    /* The journal will take a reference on the path */
-    cogl_object_unref (cogl_path);
-#endif
 
 BAIL:
     /* The journal will take a reference on the prim */
diff --git a/src/cairo-cogl-utils-private.h b/src/cairo-cogl-utils-private.h
deleted file mode 100644
index 3a11289bc..000000000
--- a/src/cairo-cogl-utils-private.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/* 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
deleted file mode 100644
index 6d2130125..000000000
--- a/src/cairo-cogl-utils.c
+++ /dev/null
@@ -1,126 +0,0 @@
-/* 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/meson.build b/src/meson.build
index 160e6657c..f65ab0e8d 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -217,7 +217,6 @@ cairo_feature_sources = {
   'cairo-cogl': [
     'cairo-cogl-surface.c',
     'cairo-cogl-gradient.c',
-    'cairo-cogl-utils.c',
   ],
   'cairo-directfb': [
     'cairo-directfb-surface.c',
commit ecbd7ed1740485ccb2be29326ffa6e5ace1df2a7
Author: George Matsumura <gmmatsumura01 at bvsd.org>
Date:   Wed Aug 5 23:10:42 2020 -0600

    cogl: Add new path cache
    
    This redesigns the path cache so that it does not mess with the
    context functions, thereby hopefully making it much more resilient
    to changes in the rest of cairo that change the way the default
    context works. It is also much simpler, and it is anticipated that
    it will be more maintainable. Performance in contrast to the old
    cache design speeds up most traces in cairo-perf-trace, and slows
    down only a lesser few by <20%.
    
    Signed-off-by: George Matsumura <gmmatsumura01 at bvsd.org>

diff --git a/boilerplate/cairo-boilerplate-cogl.c b/boilerplate/cairo-boilerplate-cogl.c
index c293b3932..2339dd883 100644
--- a/boilerplate/cairo-boilerplate-cogl.c
+++ b/boilerplate/cairo-boilerplate-cogl.c
@@ -119,12 +119,12 @@ _cairo_boilerplate_cogl_create_onscreen_color_surface (const char	       *name,
         height = 1;
 
     if (content & CAIRO_CONTENT_ALPHA) {
-		/* A hackish way to ensure that we get a framebuffer with
-		 * an alpha component */
-		CoglSwapChain *swap_chain;
-		CoglOnscreenTemplate *onscreen_template;
-		CoglRenderer *renderer;
-		CoglDisplay *display;
+	/* A hackish way to ensure that we get a framebuffer with
+	 * an alpha component */
+	CoglSwapChain *swap_chain;
+	CoglOnscreenTemplate *onscreen_template;
+	CoglRenderer *renderer;
+	CoglDisplay *display;
 
         swap_chain = cogl_swap_chain_new ();
         cogl_swap_chain_set_has_alpha (swap_chain, TRUE);
diff --git a/src/Makefile.sources b/src/Makefile.sources
index 096a3bcfa..2b573c8e5 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -468,9 +468,7 @@ 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/cairo-cogl-context-private.h b/src/cairo-cogl-context-private.h
deleted file mode 100644
index a1b3152b1..000000000
--- a/src/cairo-cogl-context-private.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/* 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;
-
-    int path_ctm_age;
-    cairo_path_fixed_t user_path;
-
-    cairo_bool_t path_is_rectangle;
-    double x, y, width, height;
-
-    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;
-} 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
deleted file mode 100644
index e0017dd9b..000000000
--- a/src/cairo-cogl-context.c
+++ /dev/null
@@ -1,1063 +0,0 @@
-/* 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 "cairoint.h"
-
-#include "cairo-cogl-context-private.h"
-#include "cairo-freed-pool-private.h"
-#include "cairo-arc-private.h"
-#include "cairo-path-fixed-private.h"
-#include "cairo-surface-subsurface-inline.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->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 further 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->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->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->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->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->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->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->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->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->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->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->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->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;
-    }
-
-    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_cogl_context_line_to (abstract_cr,
-                                              x_fixed,
-                                              y_fixed);
-	if (unlikely (status))
-	    return status;
-
-	status = _cairo_cogl_context_line_to (abstract_cr,
-                                              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;
-
-    /* These functions will be expressed in terms of the backend
-     * functions for line_to and curve_to, which we already add the
-     * appropriate segments to the user path in */
-    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);
-
-    /* any error will have already been set on cr */
-    return CAIRO_STATUS_SUCCESS;
-}
-
-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->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->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->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->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->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->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;
-
-    /* Do not take the single-rectangle shortcut if we already have a
-     * path, whether conventional or rectangle */
-    if (cr->user_path.buf.base.num_ops == 0 && !cr->path_is_rectangle) {
-	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;
-        cr->base.path->fill_is_empty = FALSE;
-	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);
-}
-
-static void
-_cairo_cogl_context_path_extents (void *abstract_cr,
-                                  double *x1,
-                                  double *y1,
-                                  double *x2,
-                                  double *y2)
-{
-    cairo_cogl_context_t *cr = abstract_cr;
-
-    if (cr->path_is_rectangle)
-        assert (_flush_cr_rectangle (cr) == CAIRO_STATUS_SUCCESS);
-
-    cr->backend_parent.path_extents (abstract_cr, x1, y1, x2, y2);
-}
-
-static cairo_bool_t
-_cairo_cogl_context_has_current_point (void *abstract_cr)
-{
-    cairo_cogl_context_t *cr = abstract_cr;
-
-    if (cr->path_is_rectangle)
-        assert (_flush_cr_rectangle (cr) == CAIRO_STATUS_SUCCESS);
-
-    return cr->backend_parent.has_current_point (abstract_cr);
-}
-
-static cairo_bool_t
-_cairo_cogl_context_get_current_point (void   *abstract_cr,
-                                       double *x,
-                                       double *y)
-{
-    cairo_cogl_context_t *cr = abstract_cr;
-
-    if (cr->path_is_rectangle)
-        assert (_flush_cr_rectangle (cr) == CAIRO_STATUS_SUCCESS);
-
-    return cr->backend_parent.get_current_point (abstract_cr, x, y);
-}
-
-static cairo_path_t *
-_cairo_cogl_context_copy_path (void *abstract_cr)
-{
-    cairo_cogl_context_t *cr = abstract_cr;
-
-    if (cr->path_is_rectangle)
-        assert (_flush_cr_rectangle (cr) == CAIRO_STATUS_SUCCESS);
-
-    return cr->backend_parent.copy_path (abstract_cr);
-}
-
-static cairo_path_t *
-_cairo_cogl_context_copy_path_flat (void *abstract_cr)
-{
-    cairo_cogl_context_t *cr = abstract_cr;
-
-    if (cr->path_is_rectangle)
-        assert (_flush_cr_rectangle (cr) == CAIRO_STATUS_SUCCESS);
-
-    return cr->backend_parent.copy_path_flat (abstract_cr);
-}
-
-static cairo_status_t
-_cairo_cogl_context_append_path (void               *abstract_cr,
-                                 const cairo_path_t *path)
-{
-    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;
-    }
-
-    return cr->backend_parent.append_path (abstract_cr, path);
-}
-
-/* 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)
-{
-    cairo_gstate_t *gstate = cr->base.gstate;
-
-    if (cr->path_ctm_age == 0) {
-	surface->user_path = &cr->user_path;
-        cairo_matrix_multiply (&surface->ctm,
-                               &gstate->ctm,
-                               &gstate->target->device_transform);
-        cairo_matrix_multiply (&surface->ctm_inverse,
-                               &gstate->target->device_transform_inverse,
-                               &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_cogl_surface_t *
-_cairo_cogl_get_cogl_surface (cairo_surface_t *target)
-{
-    /* Subsurfaces are always 1-depth */
-    if (_cairo_surface_is_subsurface (target))
-        target = _cairo_surface_subsurface_get_target (target);
-
-    if (target->type == CAIRO_SURFACE_TYPE_COGL)
-        return (cairo_cogl_surface_t *)target;
-    else
-        /* return NULL if the target is not a cogl surface */
-        return NULL;
-}
-
-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_get_cogl_surface (cr->base.gstate->target);
-
-    if (cr->path_is_rectangle) {
-        if (surface) {
-            cairo_matrix_t ctm;
-
-            status =
-                _cairo_surface_begin_modification (cr->base.gstate->target);
-            if (status)
-                return status;
-
-            ctm = cr->base.gstate->ctm;
-            if (_cairo_surface_is_subsurface (cr->base.gstate->target))
-                cairo_matrix_translate (&ctm,
-                                        ((cairo_surface_subsurface_t *)cr->base.gstate->target)->extents.x,
-                                        ((cairo_surface_subsurface_t *)cr->base.gstate->target)->extents.y);
-
-            status = _cairo_cogl_surface_fill_rectangle ((cairo_surface_t *)surface,
-                                                         cr->base.gstate->op,
-                                                         cr->base.gstate->source,
-                                                         cr->x,
-                                                         cr->y,
-                                                         cr->width,
-                                                         cr->height,
-                                                         &ctm,
-                                                         cr->base.gstate->clip);
-            if (status == CAIRO_STATUS_SUCCESS)
-                goto DONE;
-        }
-
-        status = _flush_cr_rectangle (cr);
-        if (unlikely (status))
-            return status;
-    }
-
-    if (surface)
-        _cairo_cogl_surface_set_side_band_state (surface, cr);
-
-    status = cr->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_get_cogl_surface (cr->base.gstate->target);
-
-    if (cr->path_is_rectangle) {
-        if (surface) {
-            cairo_matrix_t ctm;
-
-            status =
-                _cairo_surface_begin_modification (cr->base.gstate->target);
-            if (status)
-                return status;
-
-            ctm = cr->base.gstate->ctm;
-            if (_cairo_surface_is_subsurface (cr->base.gstate->target))
-                cairo_matrix_translate (&ctm,
-                                        ((cairo_surface_subsurface_t *)cr->base.gstate->target)->extents.x,
-                                        ((cairo_surface_subsurface_t *)cr->base.gstate->target)->extents.y);
-
-            status = _cairo_cogl_surface_fill_rectangle ((cairo_surface_t *)surface,
-                                                         cr->base.gstate->op,
-                                                         cr->base.gstate->source,
-                                                         cr->x,
-                                                         cr->y,
-                                                         cr->width,
-                                                         cr->height,
-                                                         &ctm,
-                                                         cr->base.gstate->clip);
-            if (status == CAIRO_STATUS_SUCCESS)
-                goto DONE;
-        }
-
-        status = _flush_cr_rectangle (cr);
-        if (unlikely (status))
-            return status;
-    }
-
-    if (surface)
-        _cairo_cogl_surface_set_side_band_state (surface, cr);
-
-    status = cr->backend_parent.fill_preserve (abstract_cr);
-    if (unlikely (status))
-	return status;
-
-DONE:
-    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_get_cogl_surface (cr->base.gstate->target);
-
-    /* This operator can't use an accelerated rectangle path yet */
-    if (cr->path_is_rectangle) {
-        status = _flush_cr_rectangle (cr);
-        if (unlikely (status))
-            return status;
-    }
-
-    if (surface)
-        _cairo_cogl_surface_set_side_band_state (surface, cr);
-
-    status = cr->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_get_cogl_surface (cr->base.gstate->target);
-
-    /* This operator can't use an accelerated rectangle path yet */
-    if (cr->path_is_rectangle) {
-        status = _flush_cr_rectangle (cr);
-        if (unlikely (status))
-            return status;
-    }
-
-    if (surface)
-        _cairo_cogl_surface_set_side_band_state (surface, cr);
-
-    status = cr->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;
-    cairo_cogl_surface_t *surface =
-        _cairo_cogl_get_cogl_surface (cr->base.gstate->target);
-
-    /* This operator can't use an accelerated rectangle path yet */
-    if (cr->path_is_rectangle) {
-        status = _flush_cr_rectangle (cr);
-        if (unlikely (status))
-            return status;
-    }
-
-    if (surface)
-        _cairo_cogl_surface_set_side_band_state (surface, cr);
-
-    status = cr->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 cairo_status_t
-_cairo_cogl_context_clip_preserve (void *abstract_cr)
-{
-    cairo_cogl_context_t *cr = abstract_cr;
-    cairo_status_t status;
-    cairo_cogl_surface_t *surface =
-        _cairo_cogl_get_cogl_surface (cr->base.gstate->target);
-
-    /* This operator can't use an accelerated rectangle path yet */
-    if (cr->path_is_rectangle) {
-        status = _flush_cr_rectangle (cr);
-        if (unlikely (status))
-            return status;
-    }
-
-    if (surface)
-        _cairo_cogl_surface_set_side_band_state (surface, cr);
-
-    status = cr->backend_parent.clip_preserve (abstract_cr);
-    if (unlikely (status))
-	return status;
-
-    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);
-}
-
-void
-_cairo_cogl_context_set_custom_vtable_funcs (cairo_backend_t *backend);
-
-/* 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_context_t *cr;
-    cairo_status_t status;
-
-    cr = _freed_pool_get (&context_pool);
-    if (unlikely (cr == NULL)) {
-	cr = _cairo_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);
-    }
-
-    memcpy (&cr->backend, cr->base.base.backend, sizeof (cairo_backend_t));
-    memcpy (&cr->backend_parent, cr->base.base.backend, sizeof (cairo_backend_t));
-
-    _cairo_cogl_context_set_custom_vtable_funcs (&cr->backend);
-
-    cr->base.base.backend = &cr->backend;
-
-    _cairo_path_fixed_init (&cr->user_path);
-    cr->path_is_rectangle = FALSE;
-    cr->path_ctm_age = 0;
-
-    return &cr->base.base;
-}
-
-void
-_cairo_cogl_context_set_custom_vtable_funcs (cairo_backend_t *backend)
-{
-    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;
-    backend->path_extents = _cairo_cogl_context_path_extents;
-    backend->has_current_point = _cairo_cogl_context_has_current_point;
-    backend->get_current_point = _cairo_cogl_context_get_current_point;
-    backend->copy_path = _cairo_cogl_context_copy_path;
-    backend->copy_path_flat = _cairo_cogl_context_copy_path_flat;
-    backend->append_path = _cairo_cogl_context_append_path;
-
-    /* Try to automatically catch if any new path APIs are added that
-     * mean we may need to overload more functions... */
-    assert (((char *)&backend->clip
-             - (char *)&backend->backend_to_user_distance)
-            == (sizeof (void *) * 21));
-
-    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;
-    backend->clip_preserve = _cairo_cogl_context_clip_preserve;
-}
diff --git a/src/cairo-cogl-private.h b/src/cairo-cogl-private.h
index 3f7b3693e..b0be2661e 100644
--- a/src/cairo-cogl-private.h
+++ b/src/cairo-cogl-private.h
@@ -37,6 +37,7 @@
 #include "cairo-backend-private.h"
 #include "cairo-default-context-private.h"
 #include "cairo-surface-private.h"
+#include "cairo-freelist-private.h"
 
 #include <cogl/cogl2-experimental.h>
 
@@ -93,10 +94,11 @@ typedef struct _cairo_cogl_device {
     /* 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_freelist_t path_fill_meta_freelist;
+    cairo_freelist_t path_stroke_meta_freelist;
 } cairo_cogl_device_t;
 
 typedef struct _cairo_cogl_clip_primitives {
@@ -159,15 +161,4 @@ _cairo_cogl_path_fixed_rectangle (cairo_path_fixed_t *path,
 				  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
index f1f091bad..fce03db5d 100644
--- a/src/cairo-cogl-surface.c
+++ b/src/cairo-cogl-surface.c
@@ -43,7 +43,6 @@
 #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-surface-subsurface-inline.h"
 #include "cairo-surface-fallback-private.h"
@@ -57,15 +56,8 @@
 #define CAIRO_COGL_DEBUG 0
 //#define FILL_WITH_COGL_PATH
 //#define USE_CAIRO_PATH_FLATTENER
-#define ENABLE_PATH_CACHE
 //#define DISABLE_BATCHING
 #define MAX_JOURNAL_SIZE 100
-#define ENABLE_RECTANGLES_FASTPATH
-//#define ENABLE_CLIP_CACHE // This hasn't been implemented yet
-
-#if defined (ENABLE_RECTANGLES_FASTPATH) || defined (ENABLE_PATH_CACHE)
-#define NEED_COGL_CONTEXT
-#endif
 
 #if CAIRO_COGL_DEBUG && __GNUC__
 #define UNSUPPORTED(reason) ({ \
@@ -139,7 +131,6 @@ typedef struct _cairo_cogl_journal_prim_entry {
     cairo_cogl_pipeline_t *pipeline;
 
     CoglPrimitive *primitive;
-    cairo_bool_t has_transform;
     cairo_matrix_t transform;
 } cairo_cogl_journal_prim_entry_t;
 
@@ -151,49 +142,25 @@ typedef struct _cairo_cogl_journal_path_entry {
 } 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 lengths of a unit rectangle
-     * transformed through the original ctm use to tessellate the geometry
-     * so we can check what the lengths are for any new ctm to know if
-     * this geometry is compatible.
-     */
-#endif
+    cairo_cache_entry_t	base;
+    cairo_path_fixed_t path;
+    cairo_fill_rule_t fill_rule;
+    double tolerance;
 
     CoglPrimitive *prim;
+
+    cairo_freelist_t *freelist;
 } 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_cache_entry_t	base;
+    cairo_path_fixed_t path;
     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 lengths of a unit rectangle
-     * transformed through the original ctm use to tessellate 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_freelist_t *freelist;
 } cairo_cogl_path_stroke_meta_t;
 
 static cairo_surface_t *
@@ -449,12 +416,7 @@ _cairo_cogl_journal_log_primitive (cairo_cogl_surface_t  *surface,
     if (primitive)
         cogl_object_ref (primitive);
 
-    if (transform) {
-	entry->transform = *transform;
-	entry->has_transform = TRUE;
-    } else {
-	entry->has_transform = FALSE;
-    }
+    entry->transform = *transform;
 
     g_queue_push_tail (surface->journal, entry);
 
@@ -623,11 +585,11 @@ _cairo_cogl_traps_to_triangles_buffer (cairo_cogl_surface_t *surface,
 	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[3].x = p[0].x;
+	p[3].y = p[0].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[4].x = p[2].x;
+	p[4].y = p[2].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);
@@ -699,6 +661,87 @@ _cairo_cogl_traps_to_composite_prim (cairo_cogl_surface_t *surface,
     return prim;
 }
 
+/* In order to facilitate path caching, we transform the input path
+ * into a form that will make all translations and rotations of a given
+ * path identical, thereby allowing them to be identified with
+ * conventional path hashing and equivalence functions. A
+ * transformation matrix is also output so that the path can be
+ * transformed back into its original form during rendering. */
+static cairo_int_status_t
+_cairo_cogl_get_untransformed_path (cairo_path_fixed_t       *copy,
+                                    const cairo_path_fixed_t *orig,
+                                    cairo_matrix_t           *transform_out)
+{
+    cairo_matrix_t transform;
+    cairo_int_status_t status;
+
+    if (orig->buf.base.num_points < 1)
+        return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+    status = _cairo_path_fixed_init_copy (copy, orig);
+    if (unlikely (status))
+        return status;
+
+    /* First the path is translated so that its first point lies on the
+     * origin. */
+    cairo_matrix_init_translate (&transform,
+                                 -_cairo_fixed_to_double(orig->buf.points[0].x),
+                                 -_cairo_fixed_to_double(orig->buf.points[0].y));
+
+    /* Then the path is rotated so that its second point lies on the
+     * x axis. */
+    if (orig->buf.base.num_points > 1) {
+        double x = _cairo_fixed_to_double(orig->buf.points[1].x) -
+                   _cairo_fixed_to_double(orig->buf.points[0].x);
+        double y = _cairo_fixed_to_double(orig->buf.points[1].y) -
+                   _cairo_fixed_to_double(orig->buf.points[0].y);
+        double hyp = sqrt (x * x + y * y);
+
+        transform.xx = x / hyp;
+        transform.yy = x / hyp;
+        transform.xy = -y / hyp;
+        transform.yx = y / hyp;
+    }
+
+    _cairo_path_fixed_transform (copy, &transform);
+
+    *transform_out = transform;
+    status = cairo_matrix_invert (transform_out);
+    if (unlikely (status)) {
+        _cairo_path_fixed_fini (copy);
+        return status;
+    }
+
+    return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static void
+_cairo_cogl_path_fill_meta_destroy (cairo_cogl_path_fill_meta_t *meta)
+{
+    _cairo_path_fixed_fini (&meta->path);
+    cogl_object_unref (meta->prim);
+
+    _cairo_freelist_free (meta->freelist, meta);
+}
+
+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;
+
+    if (meta0->fill_rule != meta1->fill_rule)
+        return FALSE;
+
+    if (meta0->tolerance != meta1->tolerance)
+        return FALSE;
+
+    if (!_cairo_path_fixed_equal (&meta0->path, &meta1->path))
+        return FALSE;
+
+    return TRUE;
+}
+
 static cairo_int_status_t
 _cairo_cogl_fill_to_primitive (cairo_cogl_surface_t	*surface,
 			       const cairo_path_fixed_t	*path,
@@ -706,13 +749,46 @@ _cairo_cogl_fill_to_primitive (cairo_cogl_surface_t	*surface,
 			       double			 tolerance,
 			       cairo_bool_t		 one_shot,
 			       CoglPrimitive	       **primitive,
-			       size_t			*size)
+                               cairo_matrix_t           *transform)
 {
     cairo_traps_t traps;
     cairo_int_status_t status;
+    cairo_cogl_path_fill_meta_t meta;
+    cairo_cogl_path_fill_meta_t *acquired_meta;
+    cairo_cogl_path_fill_meta_t *insert_meta = NULL;
+    cairo_cogl_device_t *dev = to_device (surface->base.device);
+    unsigned long hash;
+
+    *primitive = NULL;
+
+    status = _cairo_cogl_get_untransformed_path (&meta.path,
+                                                 path,
+                                                 transform);
+    if (unlikely (status))
+        return status;
+
+    hash = _cairo_path_fixed_hash (&meta.path);
+    hash = _cairo_hash_bytes (hash, &fill_rule, sizeof (fill_rule));
+    hash = _cairo_hash_bytes (hash, &tolerance, sizeof (tolerance));
+    meta.base.hash = hash;
+    meta.tolerance = tolerance;
+    meta.fill_rule = fill_rule;
+
+    acquired_meta = _cairo_cache_lookup (&dev->path_fill_prim_cache,
+                                         &meta.base);
+
+    if (acquired_meta) {
+        // g_print ("fill cache hit");
+        *primitive = cogl_object_ref (acquired_meta->prim);
+        _cairo_path_fixed_fini (&meta.path);
+        return CAIRO_STATUS_SUCCESS;
+    }
 
     _cairo_traps_init (&traps);
-    status = _cairo_path_fixed_fill_to_traps (path, fill_rule, tolerance, &traps);
+    status = _cairo_path_fixed_fill_to_traps (&meta.path,
+                                              fill_rule,
+                                              tolerance,
+                                              &traps);
     if (unlikely (status))
 	goto BAIL;
 
@@ -721,16 +797,249 @@ _cairo_cogl_fill_to_primitive (cairo_cogl_surface_t	*surface,
 	goto BAIL;
     }
 
-    *size = traps.num_traps * sizeof (CoglVertexP2) * 6;
+    *primitive = _cairo_cogl_traps_to_composite_prim (surface,
+                                                      &traps,
+                                                      one_shot);
+    if (unlikely (!*primitive)) {
+	status = CAIRO_INT_STATUS_NO_MEMORY;
+	goto BAIL;
+    }
+
+    insert_meta =
+        _cairo_freelist_alloc (&dev->path_fill_meta_freelist);
+    if (unlikely (!insert_meta)) {
+        status = CAIRO_INT_STATUS_NO_MEMORY;
+        goto BAIL;
+    }
+
+    insert_meta->base.hash = meta.base.hash;
+    insert_meta->base.size =
+        traps.num_traps * sizeof (CoglVertexP2) * 6;
+    insert_meta->tolerance = tolerance;
+    insert_meta->fill_rule = fill_rule;
+    insert_meta->prim = cogl_object_ref (*primitive);
+    insert_meta->freelist = &dev->path_fill_meta_freelist;
+
+    status = _cairo_path_fixed_init_copy (&insert_meta->path,
+                                          &meta.path);
+    if (unlikely (status))
+        goto BAIL;
+
+    if (unlikely (_cairo_cache_insert (&dev->path_fill_prim_cache,
+                                       &insert_meta->base)))
+    {
+        g_warning ("Fill primitive cache insertion unsuccessful");
+        goto BAIL;
+    }
+
+    _cairo_path_fixed_fini (&meta.path);
+    _cairo_traps_fini (&traps);
+
+    return status;
+
+BAIL:
+    if (*primitive) {
+        cogl_object_unref (*primitive);
+        *primitive = NULL;
+    }
+    if (insert_meta)
+        _cairo_cogl_path_fill_meta_destroy (insert_meta);
+    _cairo_path_fixed_fini (&meta.path);
+    _cairo_traps_fini (&traps);
+
+    return status;
+}
+
+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;
+
+    if (meta0->tolerance != meta1->tolerance)
+        return FALSE;
+
+    if (!_cairo_cogl_stroke_style_equal (&meta0->style, &meta1->style))
+        return FALSE;
+
+    if (!_cairo_path_fixed_equal (&meta0->path, &meta1->path))
+        return FALSE;
 
-    *primitive = _cairo_cogl_traps_to_composite_prim (surface, &traps, one_shot);
+    return TRUE;
+}
+
+static void
+_cairo_cogl_path_stroke_meta_destroy (cairo_cogl_path_stroke_meta_t *meta)
+{
+    _cairo_stroke_style_fini (&meta->style);
+    _cairo_path_fixed_fini (&meta->path);
+    cogl_object_unref (meta->prim);
+
+    _cairo_freelist_free (meta->freelist, meta);
+}
+
+static unsigned long
+_cairo_cogl_stroke_style_hash (unsigned long               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_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,
+				 double			     tolerance,
+				 cairo_bool_t		     one_shot,
+				 CoglPrimitive		   **primitive,
+                                 cairo_matrix_t             *transform)
+{
+    cairo_traps_t traps;
+    cairo_int_status_t status;
+    cairo_cogl_path_stroke_meta_t meta;
+    cairo_cogl_path_stroke_meta_t *acquired_meta;
+    cairo_cogl_path_stroke_meta_t *insert_meta = NULL;
+    cairo_matrix_t identity;
+    cairo_cogl_device_t *dev = to_device (surface->base.device);
+    unsigned long hash;
+
+    *primitive = NULL;
+
+    status = _cairo_cogl_get_untransformed_path (&meta.path,
+                                                 path,
+                                                 transform);
+    if (unlikely (status))
+        return status;
+
+    hash = _cairo_path_fixed_hash (&meta.path);
+    hash = _cairo_cogl_stroke_style_hash (hash, style);
+    hash = _cairo_hash_bytes (hash, &tolerance, sizeof (tolerance));
+    meta.base.hash = hash;
+    meta.tolerance = tolerance;
+
+    status = _cairo_stroke_style_init_copy (&meta.style, style);
+    if (unlikely (status)) {
+        _cairo_path_fixed_fini (&meta.path);
+        return status;
+    }
+
+    acquired_meta = _cairo_cache_lookup (&dev->path_stroke_prim_cache,
+                                         &meta.base);
+
+    if (acquired_meta) {
+        // g_print ("stroke cache hit");
+        *primitive = cogl_object_ref (acquired_meta->prim);
+        _cairo_path_fixed_fini (&meta.path);
+        return CAIRO_STATUS_SUCCESS;
+    }
+
+    _cairo_traps_init (&traps);
+
+    cairo_matrix_init_identity (&identity);
+    status = _cairo_path_fixed_stroke_polygon_to_traps (&meta.path,
+                                                        style,
+                                                        &identity,
+                                                        &identity,
+                                                        tolerance,
+                                                        &traps);
+    if (unlikely (status))
+	goto BAIL;
+
+    if (traps.num_traps == 0) {
+	status = CAIRO_INT_STATUS_NOTHING_TO_DO;
+	goto BAIL;
+    }
+
+    *primitive = _cairo_cogl_traps_to_composite_prim (surface,
+                                                      &traps,
+                                                      one_shot);
     if (unlikely (!*primitive)) {
 	status = CAIRO_INT_STATUS_NO_MEMORY;
 	goto BAIL;
     }
 
+    insert_meta =
+        _cairo_freelist_alloc (&dev->path_stroke_meta_freelist);
+    if (unlikely (!insert_meta)) {
+        status = CAIRO_INT_STATUS_NO_MEMORY;
+        goto BAIL;
+    }
+
+    insert_meta->base.hash = meta.base.hash;
+    insert_meta->base.size =
+        traps.num_traps * sizeof (CoglVertexP2) * 6;
+    insert_meta->tolerance = tolerance;
+    insert_meta->prim = cogl_object_ref (*primitive);
+    insert_meta->freelist = &dev->path_stroke_meta_freelist;
+
+    status = _cairo_stroke_style_init_copy (&insert_meta->style,
+                                            style);
+    if (unlikely (status)) {
+        _cairo_stroke_style_fini (&insert_meta->style);
+        free (insert_meta);
+        insert_meta = NULL;
+        goto BAIL;
+    }
+
+    status = _cairo_path_fixed_init_copy (&insert_meta->path,
+                                          &meta.path);
+    if (unlikely (status))
+        goto BAIL;
+
+    if (unlikely (_cairo_cache_insert (&dev->path_stroke_prim_cache,
+                                       &insert_meta->base)))
+    {
+        g_warning ("Stroke primitive cache insertion unsuccessful");
+        goto BAIL;
+    }
+
+    _cairo_path_fixed_fini (&meta.path);
+    _cairo_stroke_style_fini (&meta.style);
+    _cairo_traps_fini (&traps);
+
+    return status;
+
 BAIL:
+    if (*primitive) {
+        cogl_object_unref (*primitive);
+        *primitive = NULL;
+    }
+    if (insert_meta)
+        _cairo_cogl_path_stroke_meta_destroy (insert_meta);
+    _cairo_path_fixed_fini (&meta.path);
+    _cairo_stroke_style_fini (&meta.style);
     _cairo_traps_fini (&traps);
+
     return status;
 }
 
@@ -744,20 +1053,9 @@ _cairo_cogl_set_path_prim_clip (cairo_cogl_surface_t *surface,
     cairo_rectangle_int_t extents;
     cairo_int_status_t status;
     CoglPrimitive *prim;
-    size_t prim_size;
-
-    _cairo_path_fixed_approximate_clip_extents (path, &extents);
-
-    /* TODO - maintain a fifo of the last 10 used clips with cached
-     * primitives to see if we can avoid tessellating the path and
-     * uploading the vertices...
-     */
-#ifdef ENABLE_CLIP_CACHE
-    prim = NULL;
-    prim = find_clip_path_primitive (path);
-    if (prim)
-        // then bypass filling
-#endif
+    cairo_matrix_t transform;
+    CoglMatrix matrix;
+    double x1, y1, x2, y2;
 
     status = _cairo_cogl_fill_to_primitive (surface,
                                             path,
@@ -765,7 +1063,7 @@ _cairo_cogl_set_path_prim_clip (cairo_cogl_surface_t *surface,
                                             tolerance,
                                             FALSE,
                                             &prim,
-                                            &prim_size);
+                                            &transform);
     if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
         /* If the clip is of zero fill area, set all clipped */
         cogl_framebuffer_push_scissor_clip (surface->framebuffer,
@@ -773,16 +1071,50 @@ _cairo_cogl_set_path_prim_clip (cairo_cogl_surface_t *surface,
         (*clip_stack_depth)++;
         return;
     } else if (unlikely (status)) {
-        g_warning ("Failed to get primitive for clip path while flushing journal");
+        g_warning ("Failed to get primitive for clip path while "
+                   "flushing journal");
         goto BAIL;
     }
 
+    float transformfv[16] = {
+        transform.xx, transform.yx, 0, 0,
+        transform.xy, transform.yy, 0, 0,
+        0,            0,            1, 0,
+        transform.x0, transform.y0, 0, 1
+    };
+
+    cogl_matrix_init_from_array (&matrix, transformfv);
+
+    cogl_framebuffer_push_matrix (surface->framebuffer);
+    cogl_framebuffer_transform (surface->framebuffer, &matrix);
+
+    _cairo_path_fixed_approximate_clip_extents (path, &extents);
+
+    /* The extents need to be transformed by the inverse of the
+     * modelview matrix because they are in terms of un-transformed
+     * device coordinates and the bounds coordinates will be
+     * transformed by the modelview matrix */
+    status = cairo_matrix_invert (&transform);
+    if (unlikely (status)) {
+        g_warning ("Could not apply clip due to invalid matrix from "
+                   "path transformation");
+        goto BAIL;
+    }
+
+    x1 = extents.x;
+    y1 = extents.y;
+    x2 = extents.x + extents.width;
+    y2 = extents.y + extents.height;
+    _cairo_matrix_transform_bounding_box (&transform,
+                                          &x1, &y1, &x2, &y2,
+                                          NULL);
+
     cogl_framebuffer_push_primitive_clip (surface->framebuffer, prim,
-                                          extents.x, extents.y,
-                                          extents.x + extents.width,
-                                          extents.y + extents.height);
+                                          x1, y1, x2, y2);
     (*clip_stack_depth)++;
 
+    cogl_framebuffer_pop_matrix (surface->framebuffer);
+
 BAIL:
     if (prim)
         cogl_object_unref (prim);
@@ -1158,20 +1490,15 @@ _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface)
                                          prim_entry->pipeline);
 
 	    cogl_framebuffer_push_matrix (surface->framebuffer);
-	    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_framebuffer_transform (surface->framebuffer, &transform);
-	    } else {
-		cogl_matrix_init_identity (&transform);
-		cogl_framebuffer_set_modelview_matrix (surface->framebuffer, &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_framebuffer_transform (surface->framebuffer, &transform);
 
             /* If the primitive is NULL, it means we just draw the
              * unbounded rectangle */
@@ -2702,8 +3029,11 @@ static void
 set_layer_texture_with_attributes (CoglPipeline                    *pipeline,
 				   int                              layer_index,
 				   CoglTexture                     *texture,
-				   cairo_cogl_texture_attributes_t *attributes)
+				   cairo_cogl_texture_attributes_t *attributes,
+                                   cairo_matrix_t                  *path_transform)
 {
+    cairo_matrix_t m;
+
     cogl_pipeline_set_layer_texture (pipeline, layer_index, texture);
 
     cogl_pipeline_set_layer_filters (pipeline,
@@ -2711,13 +3041,20 @@ set_layer_texture_with_attributes (CoglPipeline                    *pipeline,
                                      attributes->filter,
                                      attributes->filter);
 
-    if (!_cairo_matrix_is_identity (&attributes->matrix)) {
-	cairo_matrix_t *m = &attributes->matrix;
+    /* We multiply in the path transform here so that we read texture
+     * values from coordinates that are consistent with the coordinates
+     * of the path after it is transformed by the modelview matrix */
+    if (path_transform)
+        cairo_matrix_multiply (&m, path_transform, &attributes->matrix);
+    else
+        m = attributes->matrix;
+
+    if (!_cairo_matrix_is_identity (&m)) {
 	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
+	    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);
@@ -2741,7 +3078,8 @@ get_source_mask_operator_destination_pipelines (cairo_cogl_pipeline_t       **pi
 					        const cairo_pattern_t        *source,
 					        cairo_operator_t              op,
 					        cairo_cogl_surface_t         *destination,
-					        cairo_composite_rectangles_t *extents)
+					        cairo_composite_rectangles_t *extents,
+                                                cairo_matrix_t               *path_transform)
 {
     cairo_cogl_template_type template_type;
     cairo_cogl_device_t *dev = to_device(destination->base.device);
@@ -2899,7 +3237,8 @@ get_source_mask_operator_destination_pipelines (cairo_cogl_pipeline_t       **pi
             set_layer_texture_with_attributes (pipelines[1]->pipeline,
                                                pipelines[1]->n_layers++,
                                                texture,
-                                               &attributes);
+                                               &attributes,
+                                               path_transform);
             cogl_object_unref (texture);
 
             if (pipelines[1]->src_tex_clip.buf.base.num_ops > 0)
@@ -2953,7 +3292,8 @@ get_source_mask_operator_destination_pipelines (cairo_cogl_pipeline_t       **pi
 	        set_layer_texture_with_attributes (pipelines[1]->pipeline,
                                                    pipelines[1]->n_layers++,
                                                    texture,
-                                                   &attributes);
+                                                   &attributes,
+                                                   path_transform);
             }
             if (pipelines[0]) {
                 if (mask_tex_clip.buf.base.num_ops > 0) {
@@ -2965,7 +3305,8 @@ get_source_mask_operator_destination_pipelines (cairo_cogl_pipeline_t       **pi
                 set_layer_texture_with_attributes (pipelines[0]->pipeline,
                                                    pipelines[0]->n_layers++,
                                                    texture,
-                                                   &attributes);
+                                                   &attributes,
+                                                   path_transform);
             }
 
             _cairo_path_fixed_fini (&mask_tex_clip);
@@ -3147,8 +3488,9 @@ _cairo_cogl_surface_paint (void                  *abstract_surface,
                                                     source,
                                                     op,
                                                     surface,
-                                                    &extents);
-    if (!pipelines[0] && !pipelines[1]) {
+                                                    &extents,
+                                                    NULL);
+    if (unlikely (pipelines[0] == NULL && pipelines[1] == NULL)) {
         status = CAIRO_INT_STATUS_UNSUPPORTED;
         goto BAIL;
     }
@@ -3210,8 +3552,9 @@ _cairo_cogl_surface_mask (void                  *abstract_surface,
                                                     source,
                                                     op,
                                                     surface,
-                                                    &extents);
-    if (!pipelines[0] && !pipelines[1]) {
+                                                    &extents,
+                                                    NULL);
+    if (unlikely (pipelines[0] == NULL && pipelines[1] == NULL)) {
 	status = CAIRO_INT_STATUS_UNSUPPORTED;
         goto BAIL;
     }
@@ -3242,230 +3585,6 @@ BAIL:
     return status;
 }
 
-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 (unlikely (!meta))
-	goto BAIL;
-    CAIRO_REFERENCE_COUNT_INIT (&meta->ref_count, 1);
-    meta->cache_entry.hash = hash;
-    meta->counter = 0;
-    meta_path = _cairo_malloc (sizeof (cairo_path_fixed_t));
-    if (unlikely (!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:
-    free (meta_path);
-    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,
-				 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_polygon_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, one_shot);
-    if (unlikely (!*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,
@@ -3482,14 +3601,8 @@ _cairo_cogl_surface_stroke (void                       *abstract_surface,
     cairo_composite_rectangles_t extents;
     cairo_cogl_pipeline_t *pipelines[2];
     cairo_int_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;
-    cairo_bool_t one_shot = TRUE;
+    cairo_matrix_t transform;
     CoglPrimitive *prim = NULL;
-    cairo_bool_t new_prim = FALSE;
 
     if (! is_operator_supported (op))
 	return CAIRO_INT_STATUS_UNSUPPORTED;
@@ -3503,44 +3616,15 @@ _cairo_cogl_surface_stroke (void                       *abstract_surface,
     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) {
-	size_t prim_size;
-	status = _cairo_cogl_stroke_to_primitive (surface, path, style,
-                                                  ctm, ctm_inverse, tolerance,
-                                                  one_shot, &prim,
-                                                  &prim_size);
-        if (status == CAIRO_INT_STATUS_NOTHING_TO_DO
-            && _cairo_operator_bounded_by_mask (op) == FALSE) {
-            /* Just render the unbounded rectangle */
-            prim = NULL;
-	} else if (unlikely (status)) {
-	    goto BAIL;
-        } else {
-            new_prim = TRUE;
-        }
-#if defined (ENABLE_PATH_CACHE)
-	if (meta && prim) {
-	    meta->prim = cogl_object_ref (prim);
-	    _cairo_cogl_path_stroke_meta_set_prim_size (surface, meta, prim_size);
-	}
-#endif
+    status = _cairo_cogl_stroke_to_primitive (surface, path, style,
+                                              tolerance, TRUE, &prim,
+                                              &transform);
+    if (status == CAIRO_INT_STATUS_NOTHING_TO_DO
+        && _cairo_operator_bounded_by_mask (op) == FALSE) {
+        /* Just render the unbounded rectangle */
+        prim = NULL;
+    } else if (unlikely (status)) {
+        goto BAIL;
     }
 
     get_source_mask_operator_destination_pipelines (pipelines,
@@ -3548,8 +3632,9 @@ _cairo_cogl_surface_stroke (void                       *abstract_surface,
                                                     source,
                                                     op,
                                                     surface,
-                                                    &extents);
-    if (!pipelines[0] && !pipelines[1]) {
+                                                    &extents,
+                                                    &transform);
+    if (unlikely (pipelines[0] == NULL && pipelines[1] == NULL)) {
         status = CAIRO_INT_STATUS_UNSUPPORTED;
         goto BAIL;
     }
@@ -3560,16 +3645,16 @@ _cairo_cogl_surface_stroke (void                       *abstract_surface,
         _cairo_cogl_journal_log_primitive (surface,
                                            pipelines[0],
                                            prim,
-                                           transform);
+                                           &transform);
     if (pipelines[1])
         _cairo_cogl_journal_log_primitive (surface,
                                            pipelines[1],
                                            prim,
-                                           transform);
+                                           &transform);
 
 BAIL:
     /* The journal will take a reference on the primitive... */
-    if (new_prim)
+    if (prim)
 	cogl_object_unref (prim);
 
     _cairo_composite_rectangles_fini (&extents);
@@ -3577,123 +3662,6 @@ BAIL:
     return status;
 }
 
-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 (unlikely (!meta))
-	goto BAIL;
-    meta->cache_entry.hash = hash;
-    meta->counter = 0;
-    CAIRO_REFERENCE_COUNT_INIT (&meta->ref_count, 1);
-    meta_path = _cairo_malloc (sizeof (cairo_path_fixed_t));
-    if (unlikely (!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:
-    free (meta_path);
-    free (meta);
-    return NULL;
-}
-
 static cairo_int_status_t
 _cairo_cogl_surface_fill (void			    *abstract_surface,
                           cairo_operator_t	     op,
@@ -3707,14 +3675,8 @@ _cairo_cogl_surface_fill (void			    *abstract_surface,
     cairo_cogl_surface_t *surface = abstract_surface;
     cairo_composite_rectangles_t extents;
     cairo_int_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;
+    cairo_matrix_t transform;
     CoglPrimitive *prim = NULL;
-    cairo_bool_t new_prim = FALSE;
     cairo_cogl_pipeline_t *pipelines[2];
 
     if (! is_operator_supported (op))
@@ -3728,42 +3690,16 @@ _cairo_cogl_surface_fill (void			    *abstract_surface,
 	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) {
-	size_t prim_size;
-	status = _cairo_cogl_fill_to_primitive (surface, path, fill_rule, tolerance,
-						one_shot, &prim, &prim_size);
-        if (status == CAIRO_INT_STATUS_NOTHING_TO_DO
-            && _cairo_operator_bounded_by_mask (op) == FALSE) {
-            /* Just render the unbounded rectangle */
-            prim = NULL;
-	} else if (unlikely (status)) {
-	    goto BAIL;
-        } else {
-            new_prim = TRUE;
-        }
-#ifdef ENABLE_PATH_CACHE
-	if (meta && prim) {
-	    meta->prim = cogl_object_ref (prim);
-	    _cairo_cogl_path_fill_meta_set_prim_size (surface, meta, prim_size);
-	}
-#endif /* ENABLE_PATH_CACHE */
+    status = _cairo_cogl_fill_to_primitive (surface, path, fill_rule,
+                                            tolerance, TRUE, &prim,
+                                            &transform);
+    if (status == CAIRO_INT_STATUS_NOTHING_TO_DO
+        && _cairo_operator_bounded_by_mask (op) == FALSE) {
+        /* Just render the unbounded rectangle */
+        prim = NULL;
+    } else if (unlikely (status)) {
+        goto BAIL;
     }
-
 #endif /* !FILL_WITH_COGL_PATH */
 
     get_source_mask_operator_destination_pipelines (pipelines,
@@ -3771,8 +3707,9 @@ _cairo_cogl_surface_fill (void			    *abstract_surface,
                                                     source,
                                                     op,
                                                     surface,
-                                                    &extents);
-    if (!pipelines[0] && !pipelines[1]) {
+                                                    &extents,
+                                                    &transform);
+    if (unlikely (pipelines[0] == NULL && pipelines[1] == NULL)) {
         status = CAIRO_INT_STATUS_UNSUPPORTED;
         goto BAIL;
     }
@@ -3784,12 +3721,12 @@ _cairo_cogl_surface_fill (void			    *abstract_surface,
         _cairo_cogl_journal_log_primitive (surface,
                                            pipelines[0],
                                            prim,
-                                           transform);
+                                           &transform);
     if (pipelines[1])
         _cairo_cogl_journal_log_primitive (surface,
                                            pipelines[1],
                                            prim,
-                                           transform);
+                                           &transform);
 #else
     CoglPath * cogl_path = _cairo_cogl_util_path_from_cairo (path, fill_rule, tolerance);
 
@@ -3806,88 +3743,14 @@ _cairo_cogl_surface_fill (void			    *abstract_surface,
 #endif
 
 BAIL:
-#ifndef FILL_WITH_COGL_PATH
     /* The journal will take a reference on the prim */
-    if (new_prim)
+    if (prim)
 	cogl_object_unref (prim);
-#endif
-
     _cairo_composite_rectangles_fini (&extents);
 
     return status;
 }
 
-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;
-    cairo_composite_rectangles_t extents;
-    cairo_int_status_t status;
-    cairo_cogl_pipeline_t *pipelines[2];
-
-    if (! is_operator_supported (op))
-	return CAIRO_INT_STATUS_UNSUPPORTED;
-
-    if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
-        /* These extents will be larger than necessary, but in the absence
-         * of a specialized function, they will do */
-        status =
-            _cairo_composite_rectangles_init_for_paint (&extents,
-                                                        &surface->base,
-                                                        op,
-                                                        source,
-                                                        clip);
-        if (unlikely (status))
-	    return status;
-
-	get_source_mask_operator_destination_pipelines (pipelines,
-                                                        NULL,
-                                                        source,
-                                                        op,
-                                                        surface,
-                                                        &extents);
-        if (!pipelines[0] && !pipelines[1]) {
-            status = CAIRO_INT_STATUS_UNSUPPORTED;
-            goto BAIL;
-        }
-
-	_cairo_cogl_log_clip (surface, clip);
-
-        if (pipelines[0])
-            _cairo_cogl_journal_log_rectangle (surface,
-                                               pipelines[0],
-                                               x, y, width, height,
-                                               ctm);
-        if (pipelines[1])
-            _cairo_cogl_journal_log_rectangle (surface,
-                                               pipelines[1],
-                                               x, y, width, height,
-                                               ctm);
-
-BAIL:
-        _cairo_composite_rectangles_fini (&extents);
-
-	return status;
-    } else {
-	return CAIRO_INT_STATUS_UNSUPPORTED;
-    }
-
-    /* TODO:
-     * We need to acquire 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.
-     */
-}
-
 /* Mostly taken from cairo_vg_surface.c */
 /* TODO: implement actual font support, with either cogl-pango's glyph
  * cache or our own */
@@ -3900,7 +3763,6 @@ _cairo_cogl_surface_show_glyphs (void                  *abstract_surface,
                                  cairo_scaled_font_t   *scaled_font,
                                  const cairo_clip_t    *clip)
 {
-    cairo_cogl_surface_t *surface = abstract_surface;
     cairo_status_t status = CAIRO_STATUS_SUCCESS;
     cairo_path_fixed_t path;
     int num_chunk_glyphs;
@@ -3925,15 +3787,6 @@ _cairo_cogl_surface_show_glyphs (void                  *abstract_surface,
         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 (&surface->ctm);
-        surface->ctm_inverse = surface->ctm;
-        surface->path_is_rectangle = FALSE;
-#endif
-
         status = _cairo_cogl_surface_fill (abstract_surface,
                                            op, source, &path,
                                            CAIRO_FILL_RULE_WINDING,
@@ -3957,11 +3810,7 @@ BAIL:
 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 */
@@ -4232,11 +4081,12 @@ _cairo_cogl_device_finish (void *device)
     /* XXX: Drop references to external resources */
 
     _cairo_cache_fini (&dev->linear_cache);
-    _cairo_cache_fini (&dev->path_fill_staging_cache);
     _cairo_cache_fini (&dev->path_fill_prim_cache);
-    _cairo_cache_fini (&dev->path_stroke_staging_cache);
     _cairo_cache_fini (&dev->path_stroke_prim_cache);
 
+    _cairo_freelist_fini (&dev->path_fill_meta_freelist);
+    _cairo_freelist_fini (&dev->path_stroke_meta_freelist);
+
     if (dev->buffer_stack && dev->buffer_stack_offset) {
         cogl_buffer_unmap (dev->buffer_stack);
         cogl_object_unref (dev->buffer_stack);
@@ -4308,18 +4158,6 @@ cairo_cogl_device_create (CoglContext *cogl_context)
         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,
@@ -4332,6 +4170,11 @@ cairo_cogl_device_create (CoglContext *cogl_context)
                                 (cairo_destroy_func_t) _cairo_cogl_path_stroke_meta_destroy,
                                 CAIRO_COGL_PATH_META_CACHE_SIZE);
 
+    _cairo_freelist_init (&dev->path_fill_meta_freelist,
+                          sizeof(cairo_cogl_path_fill_meta_t));
+    _cairo_freelist_init (&dev->path_stroke_meta_freelist,
+                          sizeof(cairo_cogl_path_stroke_meta_t));
+
     _cairo_device_init (&dev->base, &_cairo_cogl_device_backend);
     return &dev->base;
 }
diff --git a/src/cairo-debug.c b/src/cairo-debug.c
index 760f092e3..6acdea9dd 100644
--- a/src/cairo-debug.c
+++ b/src/cairo-debug.c
@@ -94,10 +94,6 @@ 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/meson.build b/src/meson.build
index 5a3fb92e5..160e6657c 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -217,7 +217,6 @@ cairo_feature_sources = {
   'cairo-cogl': [
     'cairo-cogl-surface.c',
     'cairo-cogl-gradient.c',
-    'cairo-cogl-context.c',
     'cairo-cogl-utils.c',
   ],
   'cairo-directfb': [
commit 939da43c44ec28b278db36ab744a1176bbdfa890
Author: George Matsumura <gmmatsumura01 at bvsd.org>
Date:   Wed Aug 19 15:50:56 2020 -0600

    cogl: Add minimal font support
    
    This adds very basic path-based font support to the cogl backend,
    improving cairo-perf-trace performance by 6x or more on many traces.
    This still is not the most desirable form of font support, which
    would resemble the texture-based glyph caching in the gl backend or
    use cogl-pango to automatically cache glyphs.
    
    Signed-off-by: George Matsumura <gmmatsumura01 at bvsd.org>

diff --git a/src/cairo-cogl-surface.c b/src/cairo-cogl-surface.c
index 162f909e1..f1f091bad 100644
--- a/src/cairo-cogl-surface.c
+++ b/src/cairo-cogl-surface.c
@@ -3888,6 +3888,72 @@ BAIL:
      */
 }
 
+/* Mostly taken from cairo_vg_surface.c */
+/* TODO: implement actual font support, with either cogl-pango's glyph
+ * cache or our own */
+static cairo_int_status_t
+_cairo_cogl_surface_show_glyphs (void                  *abstract_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)
+{
+    cairo_cogl_surface_t *surface = abstract_surface;
+    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    cairo_path_fixed_t path;
+    int num_chunk_glyphs;
+    int i;
+
+    if (num_glyphs <= 0)
+        return CAIRO_STATUS_SUCCESS;
+
+#define GLYPH_CHUNK_SIZE 100
+
+    /* Chunk glyphs in order to avoid large computation overheads
+     * during tessellation of long strings */
+    for (i = 0; i < num_glyphs; i += GLYPH_CHUNK_SIZE) {
+        num_chunk_glyphs = (num_glyphs - i) < GLYPH_CHUNK_SIZE ?
+                           (num_glyphs - i) : GLYPH_CHUNK_SIZE;
+
+        _cairo_path_fixed_init (&path);
+        status = _cairo_scaled_font_glyph_path (scaled_font,
+                                                &glyphs[i],
+                                                num_chunk_glyphs,
+                                                &path);
+        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 (&surface->ctm);
+        surface->ctm_inverse = surface->ctm;
+        surface->path_is_rectangle = FALSE;
+#endif
+
+        status = _cairo_cogl_surface_fill (abstract_surface,
+                                           op, source, &path,
+                                           CAIRO_FILL_RULE_WINDING,
+                                           CAIRO_GSTATE_TOLERANCE_DEFAULT,
+                                           CAIRO_ANTIALIAS_DEFAULT,
+                                           clip);
+
+        _cairo_path_fixed_fini (&path);
+    }
+
+#undef GLYPH_CHUNK_SIZE
+
+    return CAIRO_STATUS_SUCCESS;
+
+BAIL:
+    _cairo_path_fixed_fini (&path);
+
+    return status;
+}
+
 const cairo_surface_backend_t _cairo_cogl_surface_backend = {
     CAIRO_SURFACE_TYPE_COGL,
     _cairo_cogl_surface_finish,
@@ -3921,7 +3987,7 @@ const cairo_surface_backend_t _cairo_cogl_surface_backend = {
     _cairo_cogl_surface_stroke,
     _cairo_cogl_surface_fill,
     NULL, /* fill_stroke */
-    _cairo_surface_fallback_glyphs,
+    _cairo_cogl_surface_show_glyphs,
 };
 
 static cairo_surface_t *
commit 26c7103750b3578561140b522fcc7e77ccd1ab0b
Author: George Matsumura <gmmatsumura01 at bvsd.org>
Date:   Wed Aug 19 15:41:02 2020 -0600

    cogl: Fix reference counting bugs
    
    Signed-off-by: George Matsumura <gmmatsumura01 at bvsd.org>

diff --git a/boilerplate/cairo-boilerplate-cogl.c b/boilerplate/cairo-boilerplate-cogl.c
index 68ee5bd5d..c293b3932 100644
--- a/boilerplate/cairo-boilerplate-cogl.c
+++ b/boilerplate/cairo-boilerplate-cogl.c
@@ -78,6 +78,9 @@ _cairo_boilerplate_cogl_create_offscreen_color_surface (const char		*name,
 
     device = cairo_cogl_device_create (context);
 
+    /* The device will take a reference on the context */
+    cogl_object_unref (context);
+
     closure = _cairo_malloc (sizeof (cogl_closure_t));
     *abstract_closure = closure;
     closure->device = device;
@@ -124,19 +127,31 @@ _cairo_boilerplate_cogl_create_onscreen_color_surface (const char	       *name,
 		CoglDisplay *display;
 
         swap_chain = cogl_swap_chain_new ();
-		cogl_swap_chain_set_has_alpha (swap_chain, TRUE);
+        cogl_swap_chain_set_has_alpha (swap_chain, TRUE);
 
         onscreen_template = cogl_onscreen_template_new (swap_chain);
         renderer = cogl_renderer_new ();
         display = cogl_display_new (renderer, onscreen_template);
 
+        /* References will be taken on the swap chain, renderer, and
+         * onscreen template by the constructors */
+        cogl_object_unref (swap_chain);
+        cogl_object_unref (renderer);
+        cogl_object_unref (onscreen_template);
+
         context = cogl_context_new (display, NULL);
-	} else {
+
+        /* The context will take a reference on the display */
+        cogl_object_unref (display);
+    } else {
         context = cogl_context_new (NULL, NULL);
     }
 
     device = cairo_cogl_device_create (context);
 
+    /* The device will take a reference on the context */
+    cogl_object_unref (context);
+
     closure = _cairo_malloc (sizeof (cogl_closure_t));
     *abstract_closure = closure;
     closure->device = device;
diff --git a/src/cairo-cogl-gradient.c b/src/cairo-cogl-gradient.c
index 4798cdddb..cdb4cae82 100644
--- a/src/cairo-cogl-gradient.c
+++ b/src/cairo-cogl-gradient.c
@@ -644,6 +644,7 @@ _cairo_cogl_get_linear_gradient (cairo_cogl_device_t           *device,
     }
 
     cogl_object_unref (prim);
+    cogl_object_unref (pipeline);
 
     cogl_object_unref (offscreen);
     offscreen = NULL;
diff --git a/src/cairo-cogl-surface.c b/src/cairo-cogl-surface.c
index 62067b860..162f909e1 100644
--- a/src/cairo-cogl-surface.c
+++ b/src/cairo-cogl-surface.c
@@ -3148,8 +3148,10 @@ _cairo_cogl_surface_paint (void                  *abstract_surface,
                                                     op,
                                                     surface,
                                                     &extents);
-    if (!pipelines[0] && !pipelines[1])
-        return CAIRO_INT_STATUS_UNSUPPORTED;
+    if (!pipelines[0] && !pipelines[1]) {
+        status = CAIRO_INT_STATUS_UNSUPPORTED;
+        goto BAIL;
+    }
 
     _cairo_cogl_maybe_log_clip (surface, &extents);
 
@@ -3171,7 +3173,10 @@ _cairo_cogl_surface_paint (void                  *abstract_surface,
                                            extents.bounded.height,
                                            &identity);
 
-    return CAIRO_STATUS_SUCCESS;
+BAIL:
+    _cairo_composite_rectangles_fini (&extents);
+
+    return status;
 }
 
 static cairo_int_status_t
@@ -3206,8 +3211,10 @@ _cairo_cogl_surface_mask (void                  *abstract_surface,
                                                     op,
                                                     surface,
                                                     &extents);
-    if (!pipelines[0] && !pipelines[1])
-	return CAIRO_INT_STATUS_UNSUPPORTED;
+    if (!pipelines[0] && !pipelines[1]) {
+	status = CAIRO_INT_STATUS_UNSUPPORTED;
+        goto BAIL;
+    }
 
     _cairo_cogl_maybe_log_clip (surface, &extents);
 
@@ -3229,7 +3236,10 @@ _cairo_cogl_surface_mask (void                  *abstract_surface,
                                            extents.bounded.height,
                                            &identity);
 
-    return CAIRO_STATUS_SUCCESS;
+BAIL:
+    _cairo_composite_rectangles_fini (&extents);
+
+    return status;
 }
 
 static cairo_bool_t
@@ -3521,7 +3531,7 @@ _cairo_cogl_surface_stroke (void                       *abstract_surface,
             /* Just render the unbounded rectangle */
             prim = NULL;
 	} else if (unlikely (status)) {
-	    return status;
+	    goto BAIL;
         } else {
             new_prim = TRUE;
         }
@@ -3539,8 +3549,10 @@ _cairo_cogl_surface_stroke (void                       *abstract_surface,
                                                     op,
                                                     surface,
                                                     &extents);
-    if (!pipelines[0] && !pipelines[1])
-        return CAIRO_INT_STATUS_UNSUPPORTED;
+    if (!pipelines[0] && !pipelines[1]) {
+        status = CAIRO_INT_STATUS_UNSUPPORTED;
+        goto BAIL;
+    }
 
     _cairo_cogl_maybe_log_clip (surface, &extents);
 
@@ -3554,11 +3566,15 @@ _cairo_cogl_surface_stroke (void                       *abstract_surface,
                                            pipelines[1],
                                            prim,
                                            transform);
+
+BAIL:
     /* The journal will take a reference on the primitive... */
     if (new_prim)
 	cogl_object_unref (prim);
 
-    return CAIRO_STATUS_SUCCESS;
+    _cairo_composite_rectangles_fini (&extents);
+
+    return status;
 }
 
 static cairo_cogl_path_fill_meta_t *
@@ -3736,7 +3752,7 @@ _cairo_cogl_surface_fill (void			    *abstract_surface,
             /* Just render the unbounded rectangle */
             prim = NULL;
 	} else if (unlikely (status)) {
-	    return status;
+	    goto BAIL;
         } else {
             new_prim = TRUE;
         }
@@ -3756,8 +3772,10 @@ _cairo_cogl_surface_fill (void			    *abstract_surface,
                                                     op,
                                                     surface,
                                                     &extents);
-    if (!pipelines[0] && !pipelines[1])
-	return CAIRO_INT_STATUS_UNSUPPORTED;
+    if (!pipelines[0] && !pipelines[1]) {
+        status = CAIRO_INT_STATUS_UNSUPPORTED;
+        goto BAIL;
+    }
 
     _cairo_cogl_maybe_log_clip (surface, &extents);
 
@@ -3772,9 +3790,6 @@ _cairo_cogl_surface_fill (void			    *abstract_surface,
                                            pipelines[1],
                                            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);
 
@@ -3790,7 +3805,16 @@ _cairo_cogl_surface_fill (void			    *abstract_surface,
     cogl_object_unref (cogl_path);
 #endif
 
-    return CAIRO_STATUS_SUCCESS;
+BAIL:
+#ifndef FILL_WITH_COGL_PATH
+    /* The journal will take a reference on the prim */
+    if (new_prim)
+	cogl_object_unref (prim);
+#endif
+
+    _cairo_composite_rectangles_fini (&extents);
+
+    return status;
 }
 
 cairo_int_status_t
@@ -3830,8 +3854,10 @@ _cairo_cogl_surface_fill_rectangle (void		     *abstract_surface,
                                                         op,
                                                         surface,
                                                         &extents);
-        if (!pipelines[0] && !pipelines[1])
-            return CAIRO_INT_STATUS_UNSUPPORTED;
+        if (!pipelines[0] && !pipelines[1]) {
+            status = CAIRO_INT_STATUS_UNSUPPORTED;
+            goto BAIL;
+        }
 
 	_cairo_cogl_log_clip (surface, clip);
 
@@ -3846,7 +3872,10 @@ _cairo_cogl_surface_fill_rectangle (void		     *abstract_surface,
                                                x, y, width, height,
                                                ctm);
 
-	return CAIRO_INT_STATUS_SUCCESS;
+BAIL:
+        _cairo_composite_rectangles_fini (&extents);
+
+	return status;
     } else {
 	return CAIRO_INT_STATUS_UNSUPPORTED;
     }
@@ -4185,7 +4214,7 @@ cairo_cogl_device_create (CoglContext *cogl_context)
     cairo_cogl_device_t *dev = g_new (cairo_cogl_device_t, 1);
     cairo_status_t status;
 
-    dev->cogl_context = cogl_context;
+    dev->cogl_context = cogl_object_ref (cogl_context);
 
     dev->has_npots =
         cogl_has_features (cogl_context,
commit 20d475042cdd41603e0a0752779c6f74a7fae544
Author: George Matsumura <gmmatsumura01 at bvsd.org>
Date:   Wed Jul 29 14:07:41 2020 -0600

    cogl: Increase reading performance of RGB-only surfaces
    
    This makes it so that RGB24 surfaces are represented in such a way
    that does not require expensive format conversions during pixel
    packing and unpacking.
    
    Signed-off-by: George Matsumura <gmmatsumura01 at bvsd.org>

diff --git a/src/cairo-cogl-surface.c b/src/cairo-cogl-surface.c
index 8e145cf66..62067b860 100644
--- a/src/cairo-cogl-surface.c
+++ b/src/cairo-cogl-surface.c
@@ -1287,18 +1287,14 @@ _cairo_cogl_surface_finish (void *abstract_surface)
 static CoglTextureComponents
 get_components_from_cairo_content (cairo_content_t content)
 {
-    switch (content)
-    {
-    case CAIRO_CONTENT_ALPHA:
-       return COGL_TEXTURE_COMPONENTS_A;
-    case CAIRO_CONTENT_COLOR:
-       return COGL_TEXTURE_COMPONENTS_RGB;
-    case CAIRO_CONTENT_COLOR_ALPHA:
-       return COGL_TEXTURE_COMPONENTS_RGBA;
-    default:
-        g_warning ("Unrecognized content type");
-        assert (0);
-    }
+    /* We use RGBA for color-only surfaces due to the fact that cogl
+     * does not provide a padded XRGB format, thereby making us use
+     * RGBA formats to represent e.g. CAIRO_FORMAT_RGB24 and doing very
+     * expensive format conversions on the cpu when images are read
+     * back */
+    return (content & CAIRO_CONTENT_COLOR) ?
+           COGL_TEXTURE_COMPONENTS_RGBA :
+           COGL_TEXTURE_COMPONENTS_A;
 }
 
 static CoglPixelFormat
@@ -1426,7 +1422,6 @@ _cairo_cogl_surface_create_similar (void            *abstract_surface,
     cairo_cogl_surface_t *reference_surface = abstract_surface;
     cairo_cogl_surface_t *surface;
     CoglTexture *texture;
-    CoglTextureComponents components;
     cairo_status_t status;
     cairo_cogl_device_t *dev =
         to_device(reference_surface->base.device);
@@ -1445,8 +1440,8 @@ _cairo_cogl_surface_create_similar (void            *abstract_surface,
     if (!texture)
         return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
-    components = get_components_from_cairo_content (content);
-    cogl_texture_set_components (texture, components);
+    cogl_texture_set_components (texture,
+        get_components_from_cairo_content (content));
 
     surface = (cairo_cogl_surface_t *)
 	_cairo_cogl_surface_create_full (dev, content, NULL, texture);
@@ -1720,7 +1715,6 @@ _cairo_cogl_scale_texture (CoglContext           *context,
     CoglPipeline *copying_pipeline = NULL;
     CoglFramebuffer *fb = NULL;
     CoglError *error = NULL;
-    CoglTextureComponents components;
     unsigned int tex_width = new_width;
     unsigned int tex_height = new_height;
 
@@ -1737,8 +1731,6 @@ _cairo_cogl_scale_texture (CoglContext           *context,
         tex_height *= 2;
     }
 
-    components = cogl_texture_get_components (texture_in);
-
     texture_out =
         cogl_texture_2d_new_with_size (context, tex_width, tex_height);
     if (unlikely (!texture_out)) {
@@ -1746,7 +1738,8 @@ _cairo_cogl_scale_texture (CoglContext           *context,
         goto BAIL;
     }
 
-    cogl_texture_set_components (texture_out, components);
+    cogl_texture_set_components (texture_out,
+        cogl_texture_get_components (texture_in));
 
     fb = cogl_offscreen_new_with_texture (texture_out);
     if (unlikely (!cogl_framebuffer_allocate (fb, &error))) {
@@ -4025,7 +4018,6 @@ cairo_cogl_offscreen_surface_create (cairo_device_t *abstract_device,
 {
     CoglFramebuffer *fb;
     CoglTexture *tex;
-    CoglTextureComponents components;
     CoglError *error = NULL;
     cairo_surface_t *surface;
     cairo_cogl_device_t *dev = (cairo_cogl_device_t *)abstract_device;
@@ -4042,10 +4034,10 @@ cairo_cogl_offscreen_surface_create (cairo_device_t *abstract_device,
         tex_height = (int)pow (2, ceil (log2 (tex_height)));
     }
 
-    components = get_components_from_cairo_content (content);
     tex = cogl_texture_2d_new_with_size (dev->cogl_context,
                                          tex_width, tex_height);
-    cogl_texture_set_components (tex, components);
+    cogl_texture_set_components (tex,
+        get_components_from_cairo_content (content));
     fb = cogl_offscreen_new_with_texture (tex);
 
     if (unlikely (!cogl_framebuffer_allocate (fb, &error))) {
commit 4068fe0026c32bd04d2405956347544842fc11a8
Author: George Matsumura <gmmatsumura01 at bvsd.org>
Date:   Wed Jul 29 04:53:07 2020 -0600

    cogl: Support mirroring of gradients if no hardware support exists
    
    This mirrors a linear gradient by dobling its size and reflecting it
    in the case that hardware does not support mirrored repeating.
    
    Signed-off-by: George Matsumura <gmmatsumura01 at bvsd.org>

diff --git a/src/cairo-cogl-gradient-private.h b/src/cairo-cogl-gradient-private.h
index cc9771179..9367044f3 100644
--- a/src/cairo-cogl-gradient-private.h
+++ b/src/cairo-cogl-gradient-private.h
@@ -71,6 +71,7 @@ _cairo_cogl_get_linear_gradient (cairo_cogl_device_t           *context,
 				 cairo_extend_t                 extend_mode,
 				 int                            n_stops,
 				 const cairo_gradient_stop_t   *stops,
+				 const cairo_bool_t             need_mirrored_gradient,
 				 cairo_cogl_linear_gradient_t **gradient_out);
 
 cairo_cogl_linear_texture_entry_t *
diff --git a/src/cairo-cogl-gradient.c b/src/cairo-cogl-gradient.c
index 9d190612c..4798cdddb 100644
--- a/src/cairo-cogl-gradient.c
+++ b/src/cairo-cogl-gradient.c
@@ -356,6 +356,7 @@ _cairo_cogl_get_linear_gradient (cairo_cogl_device_t           *device,
 				 cairo_extend_t                 extend_mode,
 				 int                            n_stops,
 				 const cairo_gradient_stop_t   *stops,
+				 const cairo_bool_t             need_mirrored_gradient,
 				 cairo_cogl_linear_gradient_t **gradient_out)
 {
     unsigned long hash;
@@ -367,15 +368,15 @@ _cairo_cogl_get_linear_gradient (cairo_cogl_device_t           *device,
     int n;
     cairo_cogl_gradient_compatibility_t compatibilities;
     int width;
+    int tex_width;
     int left_padding = 0;
     cairo_color_stop_t left_padding_color;
     int right_padding = 0;
     cairo_color_stop_t right_padding_color;
     CoglTextureComponents components;
     CoglTexture2D *tex;
-    GError *error = NULL;
     int un_padded_width;
-    CoglHandle offscreen;
+    CoglFramebuffer *offscreen = NULL;
     cairo_int_status_t status;
     int n_quads;
     int n_vertices;
@@ -417,7 +418,7 @@ _cairo_cogl_get_linear_gradient (cairo_cogl_device_t           *device,
     }
 
     entry = _cairo_malloc (sizeof (cairo_cogl_linear_texture_entry_t));
-    if (!entry) {
+    if (unlikely (!entry)) {
 	status = CAIRO_INT_STATUS_NO_MEMORY;
 	goto BAIL;
     }
@@ -453,7 +454,7 @@ _cairo_cogl_get_linear_gradient (cairo_cogl_device_t           *device,
      * 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
+     * Another 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.
      */
@@ -556,16 +557,23 @@ _cairo_cogl_get_linear_gradient (cairo_cogl_device_t           *device,
 
     width = _cairo_cogl_util_next_p2 (width);
     width = MIN (4096, width); /* lets not go too stupidly big! */
+
+    if (!device->has_npots)
+        width = pow (2, ceil (log2 (width)));
+
+    if (need_mirrored_gradient)
+        tex_width = width * 2;
+    else
+        tex_width = width;
+
     components = _cairo_cogl_linear_gradient_components_for_stops (extend_mode, n_stops, stops);
 
     do {
-	tex = cogl_texture_2d_new_with_size (device->cogl_context, width, 1);
+	tex = cogl_texture_2d_new_with_size (device->cogl_context,
+	                                     tex_width, 1);
+    } while (tex == NULL && width >> 1 && tex_width >> 1);
 
-	if (!tex)
-	    g_error_free (error);
-    } while (tex == NULL && width >> 1);
-
-    if (!tex) {
+    if (unlikely (!tex)) {
 	status = CAIRO_INT_STATUS_NO_MEMORY;
 	goto BAIL;
     }
@@ -585,14 +593,21 @@ _cairo_cogl_get_linear_gradient (cairo_cogl_device_t           *device,
 	entry->translate_x += (entry->scale_x / (float)un_padded_width) * (float)left_padding;
 
     offscreen = cogl_offscreen_new_with_texture (tex);
-    cogl_framebuffer_orthographic (offscreen, 0, 0, width, 1, -1, 100);
+    cogl_framebuffer_orthographic (offscreen, 0, 0,
+                                              tex_width, 1,
+                                              -1, 100);
     cogl_framebuffer_clear4f (offscreen,
                               COGL_BUFFER_BIT_COLOR,
-			                  0, 0, 0, 0);
+                              0, 0, 0, 0);
 
     n_quads = n_stops - 1 + !!left_padding + !!right_padding;
     n_vertices = 6 * n_quads;
-    vertices = alloca (sizeof (CoglVertexP2C4) * n_vertices);
+    vertices = _cairo_malloc_ab (n_vertices, sizeof (CoglVertexP2C4));
+    if (unlikely (!vertices)) {
+        status = CAIRO_INT_STATUS_NO_MEMORY;
+        goto BAIL;
+    }
+
     p = vertices;
     if (left_padding)
 	emit_stop (&p, 0, left_padding, &left_padding_color, &left_padding_color);
@@ -609,11 +624,29 @@ _cairo_cogl_get_linear_gradient (cairo_cogl_device_t           *device,
                                     COGL_VERTICES_MODE_TRIANGLES,
                                     n_vertices,
                                     vertices);
+    free (vertices);
     pipeline = cogl_pipeline_new (device->cogl_context);
     cogl_primitive_draw (prim, offscreen, pipeline);
+
+    if (need_mirrored_gradient) {
+        /* In order to use a reflected gradient on hardware that
+         * doesn't have a mirrored repeating texture wrap mode, we
+         * render two reflected images to a double-length linear
+         * texture and reflect that */
+        CoglMatrix transform;
+
+        cogl_matrix_init_identity (&transform);
+        cogl_matrix_translate (&transform, tex_width, 0.0f, 0.0f);
+        cogl_matrix_scale (&transform, -1.0f, 1.0f, 1.0f);
+
+        cogl_framebuffer_transform (offscreen, &transform);
+        cogl_primitive_draw (prim, offscreen, pipeline);
+    }
+
     cogl_object_unref (prim);
 
     cogl_object_unref (offscreen);
+    offscreen = NULL;
 
     gradient->textures = g_list_prepend (gradient->textures, entry);
     gradient->cache_entry.size = _cairo_cogl_linear_gradient_size (gradient);
@@ -638,5 +671,7 @@ BAIL:
     free (entry);
     if (gradient)
 	_cairo_cogl_linear_gradient_destroy (gradient);
+    if (offscreen)
+        cogl_object_unref (offscreen);
     return status;
 }
diff --git a/src/cairo-cogl-surface.c b/src/cairo-cogl-surface.c
index b53580536..8e145cf66 100644
--- a/src/cairo-cogl-surface.c
+++ b/src/cairo-cogl-surface.c
@@ -82,9 +82,9 @@ 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;
 
+    CoglPipelineFilter      filter;
     CoglPipelineWrapMode    s_wrap;
     CoglPipelineWrapMode    t_wrap;
 } cairo_cogl_texture_attributes_t;
@@ -249,7 +249,9 @@ _cairo_cogl_surface_ensure_framebuffer (cairo_cogl_surface_t *surface)
 
     surface->framebuffer =
         cogl_offscreen_new_with_texture (surface->texture);
-    if (!cogl_framebuffer_allocate (surface->framebuffer, &error)) {
+    if (unlikely (!cogl_framebuffer_allocate (surface->framebuffer,
+                                              &error)))
+    {
         g_warning ("Could not create framebuffer for surface: %s",
                    error->message);
         cogl_error_free (error);
@@ -266,60 +268,6 @@ _cairo_cogl_surface_ensure_framebuffer (cairo_cogl_surface_t *surface)
     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;
-    cairo_cogl_device_t *dev =
-        to_device(reference_surface->base.device);
-    int tex_width = width;
-    int tex_height = height;
-
-    /* In the case of lack of NPOT texture support, we allocate texture
-     * with dimensions of the next power of two */
-    if (!dev->has_npots) {
-        tex_width = pow (2, ceil (log2 (tex_width)));
-        tex_height = pow (2, ceil (log2 (tex_height)));
-    }
-
-    texture = cogl_texture_2d_new_with_size (dev->cogl_context,
-                                             tex_width, tex_height);
-    if (!texture)
-        return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
-
-    cogl_texture_set_components (texture,
-                                 (content & CAIRO_CONTENT_COLOR) ?
-                                 COGL_TEXTURE_COMPONENTS_RGBA :
-                                 COGL_TEXTURE_COMPONENTS_A);
-
-    surface = (cairo_cogl_surface_t *)
-	_cairo_cogl_surface_create_full (dev, content, NULL, texture);
-    if (unlikely (surface->base.status))
-	return &surface->base;
-
-    /* The surface will take a reference on the texture */
-    cogl_object_unref (texture);
-
-    /* If we passed a texture with larger dimensions, we need to set
-     * the surface dimensions */
-    surface->width = width;
-    surface->height = height;
-
-    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)
@@ -776,7 +724,7 @@ _cairo_cogl_fill_to_primitive (cairo_cogl_surface_t	*surface,
     *size = traps.num_traps * sizeof (CoglVertexP2) * 6;
 
     *primitive = _cairo_cogl_traps_to_composite_prim (surface, &traps, one_shot);
-    if (!*primitive) {
+    if (unlikely (!*primitive)) {
 	status = CAIRO_INT_STATUS_NO_MEMORY;
 	goto BAIL;
     }
@@ -879,7 +827,7 @@ _cairo_cogl_apply_tex_clips (cairo_cogl_surface_t  *surface,
         cogl_status = cogl_pipeline_set_depth_state (pipeline->pipeline,
                                                      &depth_state,
                                                      NULL);
-        if (cogl_status != TRUE)
+        if (unlikely (cogl_status != TRUE))
             g_warning ("Error setting depth state for unbounded render");
 
         /* Clear the depth buffer to 1.0. The color values are unused
@@ -942,7 +890,7 @@ _cairo_cogl_setup_unbounded_area_pipeline (cairo_cogl_surface_t *surface,
     cogl_status = cogl_pipeline_set_depth_state (unbounded_pipeline,
                                                  &depth_state,
                                                  NULL);
-    if (cogl_status != TRUE)
+    if (unlikely (cogl_status != TRUE))
         g_warning ("Error setting depth state for unbounded render");
 
     return unbounded_pipeline;
@@ -1022,7 +970,7 @@ _cairo_cogl_post_unbounded_render (cairo_cogl_surface_t  *surface,
         cogl_status = cogl_pipeline_set_depth_state (pipeline->pipeline,
                                                      &depth_state,
                                                      NULL);
-        if (cogl_status != TRUE)
+        if (unlikely (cogl_status != TRUE))
             g_warning ("Error setting depth state after unbounded render");
     }
 
@@ -1070,7 +1018,7 @@ _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface)
 	dev->buffer_stack = NULL;
     }
 
-    if (_cairo_cogl_surface_ensure_framebuffer (surface)) {
+    if (unlikely (_cairo_cogl_surface_ensure_framebuffer (surface))) {
         g_warning ("Could not get framebuffer for flushing journal");
         assert (0);
     }
@@ -1184,7 +1132,7 @@ _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface)
                     _cairo_cogl_setup_unbounded_area_pipeline (surface,
                                                                rect_entry->pipeline->op);
                 cogl_framebuffer_draw_multitextured_rectangle (surface->framebuffer,
-                                                               rect_entry->pipeline->pipeline,
+                                                               unbounded_pipeline,
                                                                x1, y1,
                                                                x2, y2,
 						               tex_coords,
@@ -1236,7 +1184,7 @@ _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface)
                                           &clip_stack_depth,
                                           prim_entry->pipeline,
                                           &needs_vertex_render);
-            if (needs_vertex_render) {
+            if (needs_vertex_render && prim_entry->primitive) {
                 unbounded_pipeline =
                     _cairo_cogl_setup_unbounded_area_pipeline (surface,
                                                                prim_entry->pipeline->op);
@@ -1363,7 +1311,6 @@ get_default_cogl_format_from_components (CoglTextureComponents components)
     case COGL_TEXTURE_COMPONENTS_RG:
         return COGL_PIXEL_FORMAT_RG_88;
     case COGL_TEXTURE_COMPONENTS_RGB:
-        return COGL_PIXEL_FORMAT_RGB_888;
     case COGL_TEXTURE_COMPONENTS_RGBA:
 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
 	return COGL_PIXEL_FORMAT_BGRA_8888_PRE;
@@ -1470,6 +1417,59 @@ get_cogl_format_from_cairo_format (cairo_format_t cairo_format)
     return 0;
 }
 
+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;
+    CoglTextureComponents components;
+    cairo_status_t status;
+    cairo_cogl_device_t *dev =
+        to_device(reference_surface->base.device);
+    int tex_width = width;
+    int tex_height = height;
+
+    /* In the case of lack of NPOT texture support, we allocate texture
+     * with dimensions of the next power of two */
+    if (!dev->has_npots) {
+        tex_width = pow (2, ceil (log2 (tex_width)));
+        tex_height = pow (2, ceil (log2 (tex_height)));
+    }
+
+    texture = cogl_texture_2d_new_with_size (dev->cogl_context,
+                                             tex_width, tex_height);
+    if (!texture)
+        return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+    components = get_components_from_cairo_content (content);
+    cogl_texture_set_components (texture, components);
+
+    surface = (cairo_cogl_surface_t *)
+	_cairo_cogl_surface_create_full (dev, content, NULL, texture);
+    if (unlikely (surface->base.status))
+	return &surface->base;
+
+    /* The surface will take a reference on the texture */
+    cogl_object_unref (texture);
+
+    /* If we passed a texture with larger dimensions, we need to set
+     * the surface dimensions */
+    surface->width = width;
+    surface->height = height;
+
+    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_status_t
 _cairo_cogl_surface_read_rect_to_image_surface (cairo_cogl_surface_t   *surface,
 						cairo_rectangle_int_t  *interest,
@@ -1671,6 +1671,27 @@ get_cogl_wrap_mode_for_extend (cairo_extend_t       extend_mode,
     return COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE;
 }
 
+static CoglPipelineFilter
+get_cogl_filter_for_filter (cairo_filter_t filter)
+{
+    switch (filter)
+    {
+    case CAIRO_FILTER_FAST:
+    case CAIRO_FILTER_NEAREST:
+        return COGL_PIPELINE_FILTER_NEAREST;
+
+    case CAIRO_FILTER_GOOD:
+    case CAIRO_FILTER_BEST:
+    case CAIRO_FILTER_BILINEAR:
+        return COGL_PIPELINE_FILTER_LINEAR;
+
+    case CAIRO_FILTER_GAUSSIAN:
+    default:
+        g_warning("Invalid pattern filter");
+        return COGL_PIPELINE_FILTER_NEAREST;
+    }
+}
+
 static void
 _cairo_cogl_matrix_all_scale (cairo_matrix_t *matrix,
                               double          xscale,
@@ -1720,7 +1741,7 @@ _cairo_cogl_scale_texture (CoglContext           *context,
 
     texture_out =
         cogl_texture_2d_new_with_size (context, tex_width, tex_height);
-    if (!texture_out) {
+    if (unlikely (!texture_out)) {
         g_warning ("Failed to get texture for scaling");
         goto BAIL;
     }
@@ -1728,7 +1749,7 @@ _cairo_cogl_scale_texture (CoglContext           *context,
     cogl_texture_set_components (texture_out, components);
 
     fb = cogl_offscreen_new_with_texture (texture_out);
-    if (!cogl_framebuffer_allocate (fb, &error)) {
+    if (unlikely (!cogl_framebuffer_allocate (fb, &error))) {
         g_warning ("Could not get framebuffer for texture scaling: %s",
                    error->message);
         cogl_error_free (error);
@@ -1741,6 +1762,9 @@ _cairo_cogl_scale_texture (CoglContext           *context,
 
     copying_pipeline = cogl_pipeline_new (context);
     cogl_pipeline_set_layer_texture (copying_pipeline, 0, texture_in);
+    cogl_pipeline_set_layer_filters (copying_pipeline, 0,
+                                     COGL_PIPELINE_FILTER_NEAREST,
+                                     COGL_PIPELINE_FILTER_NEAREST);
 
     if (do_mirror_texture) {
         /* Draw four rectangles to the new texture with the appropriate
@@ -1973,17 +1997,17 @@ _cairo_cogl_acquire_recording_surface_texture (cairo_cogl_surface_t        *refe
                                          reference_surface->base.content,
                                          NULL,
                                          texture);
-    if (_cairo_cogl_surface_ensure_framebuffer ((cairo_cogl_surface_t *)clone))
+    if (unlikely (_cairo_cogl_surface_ensure_framebuffer ((cairo_cogl_surface_t *)clone)))
     {
         g_warning ("Could not get framebuffer for replaying recording "
                    "surface");
         goto BAIL;
     }
 
-    if (_cairo_recording_surface_replay_with_clip (surface,
-                                                   &transform,
-                                                   clone,
-                                                   NULL))
+    if (unlikely (_cairo_recording_surface_replay_with_clip (surface,
+                                                             &transform,
+                                                             clone,
+                                                             NULL)))
     {
         g_warning ("Could not replay recording surface");
         goto BAIL;
@@ -2092,12 +2116,12 @@ _cairo_cogl_acquire_generic_surface_texture (cairo_cogl_surface_t *reference_sur
          * that all of the pixels are read into the texture, but
          * upside-down. We then invert the matrix so the texture is
          * read from the bottom up instead of from the top down. */
-         stride = -image->stride;
-         data = image->data - stride * (image->height - 1);
+        stride = -image->stride;
+        data = image->data - stride * (image->height - 1);
 
-         out_matrix->yx *= -1.0;
-         out_matrix->yy *= -1.0;
-         out_matrix->y0 += image->height;
+        out_matrix->yx *= -1.0;
+        out_matrix->yy *= -1.0;
+        out_matrix->y0 += image->height;
     } else {
         stride = image->stride;
         data = image->data;
@@ -2123,15 +2147,15 @@ _cairo_cogl_acquire_generic_surface_texture (cairo_cogl_surface_t *reference_sur
     cogl_texture_set_components (texture,
         get_components_from_cairo_format (image->format));
 
-    if (!cogl_texture_allocate (texture, &error)) {
+    if (unlikely (!cogl_texture_allocate (texture, &error))) {
         g_warning ("Failed to allocate texture: %s", error->message);
         cogl_error_free (error);
         goto BAIL;
     }
 
     if (need_mirrored_texture) {
-        int new_width = image->width * 2;
-        int new_height = image->height * 2;
+        int new_width = image->width;
+        int new_height = image->height;
 
         /* If the device does not support npot textures, scale to the
          * next power of two as well */
@@ -2269,16 +2293,16 @@ _cairo_cogl_acquire_pattern_texture (const cairo_pattern_t           *pattern,
 {
     CoglTexture *texture = NULL;
     cairo_cogl_device_t *dev = to_device (destination->base.device);
+    cairo_bool_t is_mirrored_texture;
+    cairo_bool_t need_mirrored_texture =
+        (pattern->extend == CAIRO_EXTEND_REFLECT &&
+         !dev->has_mirrored_repeat);
 
     switch ((int)pattern->type)
     {
     case CAIRO_PATTERN_TYPE_SURFACE: {
         cairo_cogl_surface_t *clone;
         cairo_surface_t *surface = ((cairo_surface_pattern_t *)pattern)->surface;
-        cairo_bool_t is_mirrored_texture;
-        cairo_bool_t need_mirrored_texture =
-            (pattern->extend == CAIRO_EXTEND_REFLECT &&
-             !dev->has_mirrored_repeat);
 
         clone = (cairo_cogl_surface_t *)
             _cairo_surface_has_snapshot (surface,
@@ -2286,24 +2310,16 @@ _cairo_cogl_acquire_pattern_texture (const cairo_pattern_t           *pattern,
         if (clone && clone->texture)
             if ((!need_mirrored_texture) || clone->is_mirrored_snapshot)
             {
-                double xscale, yscale;
-
                 texture = cogl_object_ref (clone->texture);
                 attributes->matrix = pattern->matrix;
+                is_mirrored_texture = clone->is_mirrored_snapshot;
 
-                /* Get matrix for normalizing the texture coordinates */
-                if (clone->is_mirrored_snapshot) {
-                    xscale = 0.5 / clone->width;
-                    yscale = 0.5 / clone->height;
-                    is_mirrored_texture = TRUE;
-                } else {
-                    xscale = 1.0 / clone->width;
-                    yscale = 1.0 / clone->height;
-                    is_mirrored_texture = FALSE;
-                }
+                /* Convert from un-normalized source coordinates in
+                 * backend coordinates to normalized texture
+                 * coordinates. */
                 _cairo_cogl_matrix_all_scale (&attributes->matrix,
-                                              xscale,
-                                              yscale);
+                                              1.0 / clone->width,
+                                              1.0 / clone->height);
             };
 
         if (!texture) {
@@ -2342,11 +2358,12 @@ _cairo_cogl_acquire_pattern_texture (const cairo_pattern_t           *pattern,
                                                              need_mirrored_texture,
                                                              &is_mirrored_texture);
 
-        if (!texture)
+        if (unlikely (!texture))
             return NULL;
 
 	attributes->extend = pattern->extend;
-	attributes->filter = CAIRO_FILTER_BILINEAR;
+	attributes->filter =
+            get_cogl_filter_for_filter (pattern->filter);
 	attributes->has_component_alpha = pattern->has_component_alpha;
 
 	attributes->s_wrap =
@@ -2373,10 +2390,6 @@ _cairo_cogl_acquire_pattern_texture (const cairo_pattern_t           *pattern,
     case CAIRO_PATTERN_TYPE_RASTER_SOURCE: {
 	cairo_surface_t *surface;
         cairo_matrix_t new_pattern_matrix;
-        cairo_bool_t is_mirrored_texture;
-        cairo_bool_t need_mirrored_texture =
-            (pattern->extend == CAIRO_EXTEND_REFLECT &&
-             !dev->has_mirrored_repeat);
 
 	surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
 					      extents->width, extents->height);
@@ -2398,11 +2411,11 @@ _cairo_cogl_acquire_pattern_texture (const cairo_pattern_t           *pattern,
                                                          &attributes->matrix,
                                                          need_mirrored_texture,
                                                          &is_mirrored_texture);
-	if (!texture)
+	if (unlikely (!texture))
 	    goto BAIL;
 
 	attributes->extend = pattern->extend;
-	attributes->filter = CAIRO_FILTER_NEAREST;
+	attributes->filter = COGL_PIPELINE_FILTER_NEAREST;
 	attributes->has_component_alpha = pattern->has_component_alpha;
 
 	/* any pattern extend modes have already been dealt with... */
@@ -2433,11 +2446,13 @@ BAIL:
 	cairo_cogl_linear_gradient_t *gradient;
 	cairo_cogl_linear_texture_entry_t *linear_texture;
 	cairo_int_status_t status;
+        double dist, scale;
 
 	status = _cairo_cogl_get_linear_gradient (to_device(destination->base.device),
 						  pattern->extend,
 						  linear_pattern->base.n_stops,
 						  linear_pattern->base.stops,
+                                                  need_mirrored_texture,
 						  &gradient);
 	if (unlikely (status))
 	    return NULL;
@@ -2445,7 +2460,8 @@ BAIL:
 	linear_texture = _cairo_cogl_linear_gradient_texture_for_extend (gradient, pattern->extend);
 
 	attributes->extend = pattern->extend;
-	attributes->filter = CAIRO_FILTER_BILINEAR;
+	attributes->filter =
+            get_cogl_filter_for_filter (pattern->filter);
 	attributes->has_component_alpha = pattern->has_component_alpha;
 	attributes->s_wrap =
             get_cogl_wrap_mode_for_extend (pattern->extend, dev);
@@ -2464,18 +2480,14 @@ BAIL:
 				-linear_pattern->pd1.y);
 
 	/* Convert from un-normalized source coordinates in backend
-	 * coordinates to normalized texture coordinates. Since
-         * cairo_matrix_scale does not scale the x0 and y0 components,
-         * which is required for translations in normalized
-         * coordinates, use a custom solution here. */
-	double dist = sqrtf (a*a + b*b);
-	double scale = 1.0 / dist;
-        attributes->matrix.xx *= scale;
-        attributes->matrix.yx *= scale;
-        attributes->matrix.xy *= scale;
-        attributes->matrix.yy *= scale;
-        attributes->matrix.x0 *= scale;
-        attributes->matrix.y0 *= scale;
+	 * coordinates to normalized texture coordinates. */
+	dist = sqrtf (a*a + b*b);
+        if (need_mirrored_texture)
+            scale = 0.5 / dist;
+        else
+	    scale = 1.0 / dist;
+        _cairo_cogl_matrix_all_scale (&attributes->matrix,
+                                      scale, scale);
 
 	return cogl_object_ref (linear_texture->texture);
     }
@@ -2489,7 +2501,10 @@ static cairo_bool_t
 set_blend (CoglPipeline *pipeline, const char *blend_string)
 {
     CoglError *error = NULL;
-    if (!cogl_pipeline_set_blend (pipeline, blend_string, &error)) {
+    if (unlikely (!cogl_pipeline_set_blend (pipeline,
+                                            blend_string,
+                                            &error)))
+    {
 	g_warning ("Unsupported blend string with current gpu/driver: %s", blend_string);
 	cogl_error_free (error);
 	return FALSE;
@@ -2698,6 +2713,11 @@ set_layer_texture_with_attributes (CoglPipeline                    *pipeline,
 {
     cogl_pipeline_set_layer_texture (pipeline, layer_index, texture);
 
+    cogl_pipeline_set_layer_filters (pipeline,
+                                     layer_index,
+                                     attributes->filter,
+                                     attributes->filter);
+
     if (!_cairo_matrix_is_identity (&attributes->matrix)) {
 	cairo_matrix_t *m = &attributes->matrix;
 	float texture_matrixfv[16] = {
@@ -2807,11 +2827,10 @@ get_source_mask_operator_destination_pipelines (cairo_cogl_pipeline_t       **pi
     }
 
     /* pipelines[0] is for pre-rendering the mask alpha in the case
-     * that it cannot be represented by the source color alpha value.
-     * For more details, go to the description in
+     * that it cannot be represented through the source color alpha
+     * value. For more details, go to the description in
      * _cairo_cogl_setup_op_state */
-    if (op == CAIRO_OPERATOR_CLEAR ||
-        (op == CAIRO_OPERATOR_SOURCE && mask)) {
+    if (op == CAIRO_OPERATOR_CLEAR || op == CAIRO_OPERATOR_SOURCE) {
         cairo_cogl_template_type prerender_type;
 
         pipelines[0] = g_new (cairo_cogl_pipeline_t, 1);
@@ -2934,8 +2953,8 @@ get_source_mask_operator_destination_pipelines (cairo_cogl_pipeline_t       **pi
             if (pipelines[1]) {
                 if (mask_tex_clip.buf.base.num_ops > 0) {
                     pipelines[1]->has_mask_tex_clip = TRUE;
-                    if (_cairo_path_fixed_init_copy (&pipelines[1]->mask_tex_clip,
-                                                     &mask_tex_clip))
+                    if (unlikely (_cairo_path_fixed_init_copy (&pipelines[1]->mask_tex_clip,
+                                                               &mask_tex_clip)))
                         goto BAIL;
                 }
 	        set_layer_texture_with_attributes (pipelines[1]->pipeline,
@@ -2946,8 +2965,8 @@ get_source_mask_operator_destination_pipelines (cairo_cogl_pipeline_t       **pi
             if (pipelines[0]) {
                 if (mask_tex_clip.buf.base.num_ops > 0) {
                     pipelines[0]->has_mask_tex_clip = TRUE;
-                    if (_cairo_path_fixed_init_copy (&pipelines[0]->mask_tex_clip,
-                                                     &mask_tex_clip))
+                    if (unlikely (_cairo_path_fixed_init_copy (&pipelines[0]->mask_tex_clip,
+                                                               &mask_tex_clip)))
                         goto BAIL;
                 }
                 set_layer_texture_with_attributes (pipelines[0]->pipeline,
@@ -3371,13 +3390,13 @@ _cairo_cogl_get_path_stroke_meta (cairo_cogl_surface_t       *surface,
 	return meta;
 
     meta = calloc (1, sizeof (cairo_cogl_path_stroke_meta_t));
-    if (!meta)
+    if (unlikely (!meta))
 	goto BAIL;
     CAIRO_REFERENCE_COUNT_INIT (&meta->ref_count, 1);
     meta->cache_entry.hash = hash;
     meta->counter = 0;
     meta_path = _cairo_malloc (sizeof (cairo_path_fixed_t));
-    if (!meta_path)
+    if (unlikely (!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! */
@@ -3434,7 +3453,7 @@ _cairo_cogl_stroke_to_primitive (cairo_cogl_surface_t	    *surface,
 
     //g_print ("new stroke prim\n");
     *primitive = _cairo_cogl_traps_to_composite_prim (surface, &traps, one_shot);
-    if (!*primitive) {
+    if (unlikely (!*primitive)) {
 	status = CAIRO_INT_STATUS_NO_MEMORY;
 	goto BAIL;
     }
@@ -3634,13 +3653,13 @@ _cairo_cogl_get_path_fill_meta (cairo_cogl_surface_t *surface)
 	return meta;
 
     meta = calloc (1, sizeof (cairo_cogl_path_fill_meta_t));
-    if (!meta)
+    if (unlikely (!meta))
 	goto BAIL;
     meta->cache_entry.hash = hash;
     meta->counter = 0;
     CAIRO_REFERENCE_COUNT_INIT (&meta->ref_count, 1);
     meta_path = _cairo_malloc (sizeof (cairo_path_fixed_t));
-    if (!meta_path)
+    if (unlikely (!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! */
@@ -3980,7 +3999,7 @@ cairo_cogl_onscreen_surface_create (cairo_device_t *abstract_device,
 
     fb = cogl_onscreen_new (dev->cogl_context, width, height);
 
-    if (!cogl_framebuffer_allocate (fb, &error)) {
+    if (unlikely (!cogl_framebuffer_allocate (fb, &error))) {
         g_warning ("Could not allocate framebuffer for onscreen "
                    "surface: %s", error->message);
         cogl_error_free (error);
@@ -4029,7 +4048,7 @@ cairo_cogl_offscreen_surface_create (cairo_device_t *abstract_device,
     cogl_texture_set_components (tex, components);
     fb = cogl_offscreen_new_with_texture (tex);
 
-    if (!cogl_framebuffer_allocate (fb, &error)) {
+    if (unlikely (!cogl_framebuffer_allocate (fb, &error))) {
         g_warning ("Could not allocate framebuffer for offscreen "
                    "surface: %s", error->message);
         cogl_error_free (error);
commit 9b07a5379bac68565188ac9ea05970c1c4ae31cd
Author: George Matsumura <gmmatsumura01 at bvsd.org>
Date:   Tue Jul 28 02:39:31 2020 -0600

    cogl: Limit size of journal
    
    This makes sure the journal is flushed after it reaches a certain
    size, so that very large amounts of memory are not consumed if an
    explicit flush is not triggered.
    
    Signed-off-by: George Matsumura <gmmatsumura01 at bvsd.org>

diff --git a/src/cairo-cogl-surface.c b/src/cairo-cogl-surface.c
index 46e2e3921..b53580536 100644
--- a/src/cairo-cogl-surface.c
+++ b/src/cairo-cogl-surface.c
@@ -59,11 +59,11 @@
 //#define USE_CAIRO_PATH_FLATTENER
 #define ENABLE_PATH_CACHE
 //#define DISABLE_BATCHING
-#define USE_COGL_RECTANGLE_API
+#define MAX_JOURNAL_SIZE 100
 #define ENABLE_RECTANGLES_FASTPATH
 //#define ENABLE_CLIP_CACHE // This hasn't been implemented yet
 
-#if defined (USE_COGL_RECTANGLE_API) || defined (ENABLE_PATH_CACHE)
+#if defined (ENABLE_RECTANGLES_FASTPATH) || defined (ENABLE_PATH_CACHE)
 #define NEED_COGL_CONTEXT
 #endif
 
@@ -469,7 +469,12 @@ _cairo_cogl_journal_log_path (cairo_cogl_surface_t  *surface,
 
 #ifdef DISABLE_BATCHING
     _cairo_cogl_journal_flush (surface);
-#endif
+#else
+    /* To avoid consuming too much memory, flush the journal if it gets
+     * to a certain size */
+    if (g_queue_get_length (surface->journal) > MAX_JOURNAL_SIZE)
+        _cairo_cogl_journal_flush (surface);
+#endif /* DISABLE_BATCHING */
 }
 #endif /* FILL_WITH_COGL_PATH */
 
@@ -507,6 +512,11 @@ _cairo_cogl_journal_log_primitive (cairo_cogl_surface_t  *surface,
 
 #ifdef DISABLE_BATCHING
     _cairo_cogl_journal_flush (surface);
+#else
+    /* To avoid consuming too much memory, flush the journal if it gets
+     * to a certain size */
+    if (g_queue_get_length (surface->journal) > MAX_JOURNAL_SIZE)
+        _cairo_cogl_journal_flush (surface);
 #endif
 }
 
@@ -542,6 +552,11 @@ _cairo_cogl_journal_log_rectangle (cairo_cogl_surface_t  *surface,
 
 #ifdef DISABLE_BATCHING
     _cairo_cogl_journal_flush (surface);
+#else
+    /* To avoid consuming too much memory, flush the journal if it gets
+     * to a certain size */
+    if (g_queue_get_length (surface->journal) > MAX_JOURNAL_SIZE)
+        _cairo_cogl_journal_flush (surface);
 #endif
 }
 
@@ -1870,6 +1885,10 @@ _cairo_cogl_acquire_cogl_surface_texture (cairo_cogl_surface_t        *reference
     }
     _cairo_surface_attach_snapshot (surface, clone, NULL);
 
+    /* Attaching the snapshot will take a reference on the clone surface... */
+    cairo_surface_destroy (clone);
+    clone = NULL;
+
     /* Convert from un-normalized source coordinates in backend
      * coordinates to normalized texture coordinates. */
     if (*is_mirrored_texture) {
@@ -2791,7 +2810,8 @@ get_source_mask_operator_destination_pipelines (cairo_cogl_pipeline_t       **pi
      * that it cannot be represented by the source color alpha value.
      * For more details, go to the description in
      * _cairo_cogl_setup_op_state */
-    if (op == CAIRO_OPERATOR_CLEAR || op == CAIRO_OPERATOR_SOURCE) {
+    if (op == CAIRO_OPERATOR_CLEAR ||
+        (op == CAIRO_OPERATOR_SOURCE && mask)) {
         cairo_cogl_template_type prerender_type;
 
         pipelines[0] = g_new (cairo_cogl_pipeline_t, 1);
@@ -3949,6 +3969,7 @@ cairo_cogl_onscreen_surface_create (cairo_device_t *abstract_device,
     CoglFramebuffer *fb;
     CoglTextureComponents components;
     CoglError *error = NULL;
+    cairo_surface_t *surface;
     cairo_cogl_device_t *dev = (cairo_cogl_device_t *)abstract_device;
 
     if (abstract_device->backend->type != CAIRO_DEVICE_TYPE_COGL)
@@ -3967,9 +3988,14 @@ cairo_cogl_onscreen_surface_create (cairo_device_t *abstract_device,
     }
     cogl_framebuffer_orthographic (fb, 0, 0, width, height, -1, 100);
 
-    return cairo_cogl_surface_create_for_fb (abstract_device,
-                                             fb,
-                                             content);
+    surface = cairo_cogl_surface_create_for_fb (abstract_device,
+                                                fb,
+                                                content);
+
+    /* The surface will take a reference on the framebuffer */
+    cogl_object_unref (fb);
+
+    return surface;
 }
 slim_hidden_def (cairo_cogl_onscreen_surface_create);
 
@@ -4020,6 +4046,9 @@ cairo_cogl_offscreen_surface_create (cairo_device_t *abstract_device,
                                                 fb,
                                                 content);
 
+    /* The surface will take a reference on the framebuffer */
+    cogl_object_unref (fb);
+
     ((cairo_cogl_surface_t *)surface)->width = width;
     ((cairo_cogl_surface_t *)surface)->height = height;
 
commit 08d6c7de2db2c467da6e210f0b0f3565ba6f50be
Author: George Matsumura <gmmatsumura01 at bvsd.org>
Date:   Sun Jul 26 10:55:20 2020 -0600

    cogl: Support source surfaces without an alpha component
    
    This ensures that if the source surface does not have an alpha
    component (such as those with format RGB24), it is not blended
    as if it does.
    
    Signed-off-by: George Matsumura <gmmatsumura01 at bvsd.org>

diff --git a/src/cairo-cogl-private.h b/src/cairo-cogl-private.h
index 2d662ad2c..3f7b3693e 100644
--- a/src/cairo-cogl-private.h
+++ b/src/cairo-cogl-private.h
@@ -43,16 +43,22 @@
 typedef enum _cairo_cogl_template_type {
 	/* solid source */
     CAIRO_COGL_TEMPLATE_TYPE_SOLID,
-    /* texture source */
-    CAIRO_COGL_TEMPLATE_TYPE_TEXTURE,
     /* solid source with solid mask */
     CAIRO_COGL_TEMPLATE_TYPE_SOLID_MASK_SOLID,
     /* solid source with texture mask */
     CAIRO_COGL_TEMPLATE_TYPE_TEXTURE_MASK_SOLID,
+    /* texture source */
+    CAIRO_COGL_TEMPLATE_TYPE_TEXTURE,
     /* texture source with solid mask */
     CAIRO_COGL_TEMPLATE_TYPE_SOLID_MASK_TEXTURE,
     /* texture source with texture mask */
     CAIRO_COGL_TEMPLATE_TYPE_TEXTURE_MASK_TEXTURE,
+    /* texture source with source alpha ignored */
+    CAIRO_COGL_TEMPLATE_TYPE_TEXTURE_IGNORE_ALPHA,
+    /* texture source with solid mask with source alpha ignored */
+    CAIRO_COGL_TEMPLATE_TYPE_SOLID_MASK_TEXTURE_IGNORE_ALPHA,
+    /* texture source with texture mask with source alpha ignored */
+    CAIRO_COGL_TEMPLATE_TYPE_TEXTURE_MASK_TEXTURE_IGNORE_ALPHA,
     CAIRO_COGL_TEMPLATE_TYPE_COUNT
 } cairo_cogl_template_type;
 
@@ -101,8 +107,6 @@ typedef struct _cairo_cogl_clip_primitives {
 typedef struct _cairo_cogl_surface {
     cairo_surface_t base;
 
-    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
diff --git a/src/cairo-cogl-surface.c b/src/cairo-cogl-surface.c
index f87115a61..46e2e3921 100644
--- a/src/cairo-cogl-surface.c
+++ b/src/cairo-cogl-surface.c
@@ -198,7 +198,7 @@ typedef struct _cairo_cogl_path_stroke_meta {
 
 static cairo_surface_t *
 _cairo_cogl_surface_create_full (cairo_cogl_device_t *dev,
-				 cairo_bool_t         ignore_alpha,
+				 cairo_content_t      content,
 				 CoglFramebuffer     *framebuffer,
 				 CoglTexture         *texture);
 
@@ -242,7 +242,7 @@ _sanitize_trap (cairo_trapezoid_t *t)
 static cairo_status_t
 _cairo_cogl_surface_ensure_framebuffer (cairo_cogl_surface_t *surface)
 {
-    CoglError *error;
+    CoglError *error = NULL;
 
     if (surface->framebuffer)
 	return CAIRO_STATUS_SUCCESS;
@@ -299,10 +299,7 @@ _cairo_cogl_surface_create_similar (void            *abstract_surface,
                                  COGL_TEXTURE_COMPONENTS_A);
 
     surface = (cairo_cogl_surface_t *)
-	_cairo_cogl_surface_create_full (dev,
-					 (content & CAIRO_CONTENT_ALPHA) == 0,
-					 NULL,
-					 texture);
+	_cairo_cogl_surface_create_full (dev, content, NULL, texture);
     if (unlikely (surface->base.status))
 	return &surface->base;
 
@@ -1365,38 +1362,31 @@ get_default_cogl_format_from_components (CoglTextureComponents components)
     }
 }
 
-#if 0
 static CoglTextureComponents
-get_components_from_cogl_format (CoglPixelFormat format)
+get_components_from_cairo_format (cairo_format_t format)
 {
-    switch ((int)format)
+    switch (format)
     {
-    case COGL_PIXEL_FORMAT_BGRA_8888_PRE:
-    case COGL_PIXEL_FORMAT_ABGR_8888_PRE:
-    case COGL_PIXEL_FORMAT_RGBA_8888_PRE:
-        return COGL_TEXTURE_COMPONENTS_RGBA;
+    case CAIRO_FORMAT_A1:
+    case CAIRO_FORMAT_A8:
+        return COGL_TEXTURE_COMPONENTS_A;
 
-    case COGL_PIXEL_FORMAT_RGB_565:
-    case COGL_PIXEL_FORMAT_RGB_888:
+    case CAIRO_FORMAT_RGB16_565:
+    case CAIRO_FORMAT_RGB24:
+    case CAIRO_FORMAT_RGB30:
+    case CAIRO_FORMAT_RGB96F:
         return COGL_TEXTURE_COMPONENTS_RGB;
 
-    case COGL_PIXEL_FORMAT_A_8:
-        return COGL_TEXTURE_COMPONENTS_A;
-    case COGL_PIXEL_FORMAT_RG_88:
-        return COGL_TEXTURE_COMPONENTS_RG;
-    case COGL_PIXEL_FORMAT_DEPTH_32:
-        return COGL_TEXTURE_COMPONENTS_DEPTH;
+    case CAIRO_FORMAT_ARGB32:
+    case CAIRO_FORMAT_RGBA128F:
+        return COGL_TEXTURE_COMPONENTS_RGBA;
+
+    case CAIRO_FORMAT_INVALID:
     default:
-    g_warning("bad format: %x a? %d, bgr? %d, pre %d, format: %d",
-              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;
+        g_warning("Cairo format unrepresentable by cogl");
+        return 0;
     }
 }
-#endif
 
 static CoglPixelFormat
 get_cogl_format_from_cairo_format (cairo_format_t cairo_format);
@@ -1486,11 +1476,8 @@ _cairo_cogl_surface_read_rect_to_image_surface (cairo_cogl_surface_t   *surface,
                 cogl_texture_get_components (surface->texture) );
         cairo_format = get_cairo_format_from_cogl_format (cogl_format);
     } else {
-        /* Cogl doesn't give internal formats of framebuffers anymore,
-         * nor does it provide a way to find which color components are
-         * present, making it so that we may lose data if we don't get
-         * all 4 possible components */
-        cairo_format = CAIRO_FORMAT_ARGB32;
+        cairo_format =
+            _cairo_format_from_content (surface->base.content);
         cogl_format = get_cogl_format_from_cairo_format (cairo_format);
     }
 
@@ -1661,7 +1648,7 @@ get_cogl_wrap_mode_for_extend (cairo_extend_t       extend_mode,
         if (!dev->has_mirrored_repeat)
             /* If the hardware cannot support mirrored repeating, we
              * emulate it elsewhere */
-            return COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE;
+            return COGL_PIPELINE_WRAP_MODE_REPEAT;
         else
 	    return COGL_PIPELINE_WRAP_MODE_MIRRORED_REPEAT;
     }
@@ -1696,7 +1683,8 @@ _cairo_cogl_scale_texture (CoglContext           *context,
     CoglTexture *texture_out = NULL;
     CoglPipeline *copying_pipeline = NULL;
     CoglFramebuffer *fb = NULL;
-    CoglError *error;
+    CoglError *error = NULL;
+    CoglTextureComponents components;
     unsigned int tex_width = new_width;
     unsigned int tex_height = new_height;
 
@@ -1713,13 +1701,17 @@ _cairo_cogl_scale_texture (CoglContext           *context,
         tex_height *= 2;
     }
 
+    components = cogl_texture_get_components (texture_in);
+
     texture_out =
         cogl_texture_2d_new_with_size (context, tex_width, tex_height);
     if (!texture_out) {
-        g_warning ("Failed to allocate texture");
+        g_warning ("Failed to get texture for scaling");
         goto BAIL;
     }
 
+    cogl_texture_set_components (texture_out, components);
+
     fb = cogl_offscreen_new_with_texture (texture_out);
     if (!cogl_framebuffer_allocate (fb, &error)) {
         g_warning ("Could not get framebuffer for texture scaling: %s",
@@ -1809,6 +1801,13 @@ _cairo_cogl_acquire_cogl_surface_texture (cairo_cogl_surface_t        *reference
     int new_width = surface_extents->width;
     int new_height = surface_extents->height;
 
+    if (surface_extents->x < 0 || surface_extents->y < 0 ||
+        (surface_extents->x + surface_extents->width) >
+            cogl_surface->width ||
+        (surface_extents->y + surface_extents->height) >
+            cogl_surface->height)
+        return NULL;
+
     *out_matrix = *pattern_matrix;
     *is_mirrored_texture = FALSE;
 
@@ -1862,7 +1861,7 @@ _cairo_cogl_acquire_cogl_surface_texture (cairo_cogl_surface_t        *reference
 
     clone =
         _cairo_cogl_surface_create_full (dev,
-                                         reference_surface->ignore_alpha,
+                                         reference_surface->base.content,
                                          NULL,
                                          texture);
     if (unlikely (clone->status)) {
@@ -1940,15 +1939,19 @@ _cairo_cogl_acquire_recording_surface_texture (cairo_cogl_surface_t        *refe
                                              tex_width,
                                              tex_height);
     if (unlikely (!texture)) {
-        g_warning ("Failed to allocate texture");
+        g_warning ("Failed to create texture for replaying recording "
+                   "surface");
         goto BAIL;
     }
 
+    cogl_texture_set_components (texture,
+        get_components_from_cairo_content (surface->content));
+
     /* Do not attach this as a snapshot, as it only represents part of
      * the surface */
     clone =
         _cairo_cogl_surface_create_full (dev,
-                                         reference_surface->ignore_alpha,
+                                         reference_surface->base.content,
                                          NULL,
                                          texture);
     if (_cairo_cogl_surface_ensure_framebuffer ((cairo_cogl_surface_t *)clone))
@@ -2021,6 +2024,8 @@ _cairo_cogl_acquire_generic_surface_texture (cairo_cogl_surface_t *reference_sur
     cairo_image_surface_t *acquired_image = NULL;
     void *image_extra;
     cairo_image_surface_t *image_clone = NULL;
+    CoglBitmap *bitmap;
+    CoglError *error = NULL;
     cairo_surface_t *clone = NULL;
     CoglPixelFormat format;
     cairo_cogl_device_t *dev =
@@ -2079,26 +2084,29 @@ _cairo_cogl_acquire_generic_surface_texture (cairo_cogl_surface_t *reference_sur
         data = image->data;
     }
 
+    bitmap = cogl_bitmap_new_for_data (dev->cogl_context,
+                                       image->width,
+                                       image->height,
+                                       format, /* incoming */
+                                       stride,
+                                       data);
+
     if (!dev->has_npots)
         texture =
-            cogl_texture_2d_sliced_new_from_data (dev->cogl_context,
-                                                  image->width,
-                                                  image->height,
-                                                  COGL_TEXTURE_MAX_WASTE,
-                                                  format, /* incoming */
-                                                  stride,
-                                                  data,
-                                                  NULL);
+            cogl_texture_2d_sliced_new_from_bitmap (bitmap,
+                                                    COGL_TEXTURE_MAX_WASTE);
     else
-        texture = cogl_texture_2d_new_from_data (dev->cogl_context,
-                                                 image->width,
-                                                 image->height,
-                                                 format, /* incoming */
-                                                 stride,
-                                                 data,
-                                                 NULL);
-    if (!texture) {
-        g_warning ("Failed to allocate texture");
+        texture = cogl_texture_2d_new_from_bitmap (bitmap);
+
+    /* The texture will have taken a reference on the bitmap */
+    cogl_object_unref (bitmap);
+
+    cogl_texture_set_components (texture,
+        get_components_from_cairo_format (image->format));
+
+    if (!cogl_texture_allocate (texture, &error)) {
+        g_warning ("Failed to allocate texture: %s", error->message);
+        cogl_error_free (error);
         goto BAIL;
     }
 
@@ -2143,7 +2151,7 @@ _cairo_cogl_acquire_generic_surface_texture (cairo_cogl_surface_t *reference_sur
 
     clone =
         _cairo_cogl_surface_create_full (dev,
-                                         reference_surface->ignore_alpha,
+                                         reference_surface->base.content,
                                          NULL,
                                          texture);
     if (unlikely (clone->status)) {
@@ -2303,17 +2311,18 @@ _cairo_cogl_acquire_pattern_texture (const cairo_pattern_t           *pattern,
                                                               &attributes->matrix,
                                                               need_mirrored_texture,
                                                               &is_mirrored_texture);
-            } else {
-                texture =
-                    _cairo_cogl_acquire_generic_surface_texture (destination,
-                                                                 surface,
-                                                                 &pattern->matrix,
-                                                                 &attributes->matrix,
-                                                                 need_mirrored_texture,
-                                                                 &is_mirrored_texture);
             }
         }
 
+        if (!texture)
+            texture =
+                _cairo_cogl_acquire_generic_surface_texture (destination,
+                                                             surface,
+                                                             &pattern->matrix,
+                                                             &attributes->matrix,
+                                                             need_mirrored_texture,
+                                                             &is_mirrored_texture);
+
         if (!texture)
             return NULL;
 
@@ -2460,10 +2469,10 @@ BAIL:
 static cairo_bool_t
 set_blend (CoglPipeline *pipeline, const char *blend_string)
 {
-    GError *error = NULL;
+    CoglError *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);
+	cogl_error_free (error);
 	return FALSE;
     }
     return TRUE;
@@ -2603,6 +2612,57 @@ create_template_for_op_type (cairo_cogl_device_t      *dev,
                                          "RGBA = MODULATE (PREVIOUS, TEXTURE[A])",
                                          NULL);
         break;
+    case CAIRO_COGL_TEMPLATE_TYPE_TEXTURE_IGNORE_ALPHA:
+        pipeline =
+            cogl_pipeline_copy (dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID]);
+        cogl_pipeline_set_layer_null_texture (pipeline, 0,
+                                              COGL_TEXTURE_TYPE_2D);
+        /* We do not set the combine color when we use this template
+         * pipeline, so the source texture alpha will be replaces by
+         * ones */
+        cogl_pipeline_set_layer_combine_constant (pipeline, 0, &color);
+        cogl_pipeline_set_layer_combine (pipeline, 0,
+                                         "RGB = REPLACE (TEXTURE)"
+                                         "A = REPLACE (CONSTANT)",
+                                         NULL);
+        break;
+    case CAIRO_COGL_TEMPLATE_TYPE_SOLID_MASK_TEXTURE_IGNORE_ALPHA:
+        pipeline =
+            cogl_pipeline_copy (dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID]);
+        cogl_pipeline_set_layer_null_texture (pipeline, 0,
+                                              COGL_TEXTURE_TYPE_2D);
+        /* We do not set the combine color when we use this template
+         * pipeline, so the source texture alpha will be replaces by
+         * ones */
+        cogl_pipeline_set_layer_combine_constant (pipeline, 0, &color);
+        cogl_pipeline_set_layer_combine (pipeline, 0,
+                                         "RGB = REPLACE (TEXTURE)"
+                                         "A = REPLACE (CONSTANT)",
+                                         NULL);
+        cogl_pipeline_set_layer_combine_constant (pipeline, 1, &color);
+        cogl_pipeline_set_layer_combine (pipeline, 1,
+                                         "RGBA = MODULATE (PREVIOUS, CONSTANT[A])",
+                                         NULL);
+        break;
+    case CAIRO_COGL_TEMPLATE_TYPE_TEXTURE_MASK_TEXTURE_IGNORE_ALPHA:
+        pipeline =
+            cogl_pipeline_copy (dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID]);
+        cogl_pipeline_set_layer_null_texture (pipeline, 0,
+                                              COGL_TEXTURE_TYPE_2D);
+        /* We do not set the combine color when we use this template
+         * pipeline, so the source texture alpha will be replaces by
+         * ones */
+        cogl_pipeline_set_layer_combine_constant (pipeline, 0, &color);
+        cogl_pipeline_set_layer_combine (pipeline, 0,
+                                         "RGB = REPLACE (TEXTURE)"
+                                         "A = REPLACE (CONSTANT)",
+                                         NULL);
+        cogl_pipeline_set_layer_null_texture (pipeline, 1,
+                                              COGL_TEXTURE_TYPE_2D);
+        cogl_pipeline_set_layer_combine (pipeline, 1,
+                                         "RGBA = MODULATE (PREVIOUS, TEXTURE[A])",
+                                         NULL);
+        break;
     default:
         g_warning ("Invalid cogl pipeline template type");
         return;
@@ -2661,7 +2721,11 @@ get_source_mask_operator_destination_pipelines (cairo_cogl_pipeline_t       **pi
     {
     case CAIRO_PATTERN_TYPE_SOLID:
         if (mask) {
-            if (mask->type == CAIRO_PATTERN_TYPE_SOLID)
+            /* If the mask surface has no alpha content, we use a mask
+             * of solid ones */
+            if ((mask->type == CAIRO_PATTERN_TYPE_SOLID) ||
+                (mask->type == CAIRO_PATTERN_TYPE_SURFACE &&
+                 ((cairo_surface_pattern_t *)mask)->surface->content == CAIRO_CONTENT_COLOR))
                 template_type =
                     CAIRO_COGL_TEMPLATE_TYPE_SOLID_MASK_SOLID;
             else
@@ -2672,12 +2736,43 @@ get_source_mask_operator_destination_pipelines (cairo_cogl_pipeline_t       **pi
         }
         break;
     case CAIRO_PATTERN_TYPE_SURFACE:
+        /* If the source does not have alpha content, we have to use
+         * a specialized set of texture combining functions in order to
+         * ensure that if we have a CAIRO_FORMAT_RGB24 source, we are
+         * ignoring the alpha and replacing it with ones. Otherwise, we
+         * use the template types for any other type of non-solid
+         * source. */
+        if (((cairo_surface_pattern_t *)source)->surface->content ==
+             CAIRO_CONTENT_COLOR)
+        {
+            if (mask) {
+                /* If the mask surface has no alpha content, we use a
+                 * mask of solid ones */
+                if ((mask->type == CAIRO_PATTERN_TYPE_SOLID) ||
+                    (mask->type == CAIRO_PATTERN_TYPE_SURFACE &&
+                     ((cairo_surface_pattern_t *)mask)->surface->content == CAIRO_CONTENT_COLOR))
+                    template_type =
+                        CAIRO_COGL_TEMPLATE_TYPE_SOLID_MASK_TEXTURE_IGNORE_ALPHA;
+                else
+                    template_type =
+                        CAIRO_COGL_TEMPLATE_TYPE_TEXTURE_MASK_TEXTURE_IGNORE_ALPHA;
+            } else {
+                template_type =
+                    CAIRO_COGL_TEMPLATE_TYPE_TEXTURE_IGNORE_ALPHA;
+            }
+            break;
+        }
+        // else fall through
     case CAIRO_PATTERN_TYPE_LINEAR:
     case CAIRO_PATTERN_TYPE_RADIAL:
     case CAIRO_PATTERN_TYPE_MESH:
     case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
         if (mask) {
-            if (mask->type == CAIRO_PATTERN_TYPE_SOLID)
+            /* If the mask surface has no alpha content, we use a mask
+             * of solid ones */
+            if ((mask->type == CAIRO_PATTERN_TYPE_SOLID) ||
+                (mask->type == CAIRO_PATTERN_TYPE_SURFACE &&
+                 ((cairo_surface_pattern_t *)mask)->surface->content == CAIRO_CONTENT_COLOR))
                 template_type =
                     CAIRO_COGL_TEMPLATE_TYPE_SOLID_MASK_TEXTURE;
             else
@@ -2798,7 +2893,12 @@ get_source_mask_operator_destination_pipelines (cairo_cogl_pipeline_t       **pi
             if (pipelines[0])
                 cogl_pipeline_set_color (pipelines[0]->pipeline,
                                          &color);
-	} else {
+        /* If the only component present in our mask is a color
+         * component, skip setting the layer texture, as we already
+         * set a solid of uniform ones on it during the template
+         * creation process */
+	} else if (!(mask->type == CAIRO_PATTERN_TYPE_SURFACE &&
+                    ((cairo_surface_pattern_t *)mask)->surface->content == CAIRO_CONTENT_COLOR)) {
 	    cairo_cogl_texture_attributes_t attributes;
             cairo_path_fixed_t mask_tex_clip;
 
@@ -3765,7 +3865,7 @@ const cairo_surface_backend_t _cairo_cogl_surface_backend = {
 
 static cairo_surface_t *
 _cairo_cogl_surface_create_full (cairo_cogl_device_t *dev,
-				 cairo_bool_t         ignore_alpha,
+				 cairo_content_t      content,
 				 CoglFramebuffer     *framebuffer,
 				 CoglTexture         *texture)
 {
@@ -3780,8 +3880,6 @@ _cairo_cogl_surface_create_full (cairo_cogl_device_t *dev,
     if (unlikely (surface == NULL))
         return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
-    surface->ignore_alpha = ignore_alpha;
-
     surface->is_mirrored_snapshot = FALSE;
 
     surface->framebuffer = framebuffer;
@@ -3814,7 +3912,7 @@ _cairo_cogl_surface_create_full (cairo_cogl_device_t *dev,
     _cairo_surface_init (&surface->base,
                          &_cairo_cogl_surface_backend,
                          &dev->base,
-                         CAIRO_CONTENT_COLOR_ALPHA,
+                         content,
 			 FALSE); /* is_vector */
 
     return &surface->base;
@@ -3822,7 +3920,8 @@ _cairo_cogl_surface_create_full (cairo_cogl_device_t *dev,
 
 cairo_surface_t *
 cairo_cogl_surface_create_for_fb (cairo_device_t  *abstract_device,
-                                  CoglFramebuffer *framebuffer)
+                                  CoglFramebuffer *framebuffer,
+                                  cairo_content_t  content)
 {
     cairo_cogl_device_t *dev = (cairo_cogl_device_t *)abstract_device;
 
@@ -3836,7 +3935,7 @@ cairo_cogl_surface_create_for_fb (cairo_device_t  *abstract_device,
 	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
 
     return _cairo_cogl_surface_create_full (dev,
-                                            FALSE,
+                                            content,
                                             framebuffer,
                                             NULL);
 }
@@ -3849,7 +3948,7 @@ cairo_cogl_onscreen_surface_create (cairo_device_t *abstract_device,
 {
     CoglFramebuffer *fb;
     CoglTextureComponents components;
-    CoglError *error;
+    CoglError *error = NULL;
     cairo_cogl_device_t *dev = (cairo_cogl_device_t *)abstract_device;
 
     if (abstract_device->backend->type != CAIRO_DEVICE_TYPE_COGL)
@@ -3868,7 +3967,9 @@ cairo_cogl_onscreen_surface_create (cairo_device_t *abstract_device,
     }
     cogl_framebuffer_orthographic (fb, 0, 0, width, height, -1, 100);
 
-    return cairo_cogl_surface_create_for_fb (abstract_device, fb);
+    return cairo_cogl_surface_create_for_fb (abstract_device,
+                                             fb,
+                                             content);
 }
 slim_hidden_def (cairo_cogl_onscreen_surface_create);
 
@@ -3880,7 +3981,7 @@ cairo_cogl_offscreen_surface_create (cairo_device_t *abstract_device,
     CoglFramebuffer *fb;
     CoglTexture *tex;
     CoglTextureComponents components;
-    CoglError *error;
+    CoglError *error = NULL;
     cairo_surface_t *surface;
     cairo_cogl_device_t *dev = (cairo_cogl_device_t *)abstract_device;
     int tex_width = width;
@@ -3915,7 +4016,9 @@ cairo_cogl_offscreen_surface_create (cairo_device_t *abstract_device,
     /* The framebuffer will take a reference on the texture */
     cogl_object_unref (tex);
 
-    surface = cairo_cogl_surface_create_for_fb (abstract_device, fb);
+    surface = cairo_cogl_surface_create_for_fb (abstract_device,
+                                                fb,
+                                                content);
 
     ((cairo_cogl_surface_t *)surface)->width = width;
     ((cairo_cogl_surface_t *)surface)->height = height;
diff --git a/src/cairo-cogl.h b/src/cairo-cogl.h
index cb691b39d..b7a5b8e5c 100644
--- a/src/cairo-cogl.h
+++ b/src/cairo-cogl.h
@@ -59,7 +59,8 @@ cairo_cogl_offscreen_surface_create (cairo_device_t *device,
 
 cairo_public cairo_surface_t *
 cairo_cogl_surface_create_for_fb (cairo_device_t  *device,
-                                  CoglFramebuffer *framebuffer);
+                                  CoglFramebuffer *framebuffer,
+                                  cairo_content_t  content);
 
 cairo_public CoglFramebuffer *
 cairo_cogl_surface_get_framebuffer (cairo_surface_t *surface);
commit bb84bb650ddc1724bc3abb0ee8da8fa9ae34c9a4
Author: George Matsumura <gmmatsumura01 at bvsd.org>
Date:   Sat Jul 25 02:14:13 2020 -0600

    cogl: Ensure onscreen framebuffers have an alpha component if required
    
    Prior to this change, cogl often queried GLX so that it returned
    a framebuffer that could not support an alpha component.
    
    Signed-off-by: George Matsumura <gmmatsumura01 at bvsd.org>

diff --git a/boilerplate/cairo-boilerplate-cogl.c b/boilerplate/cairo-boilerplate-cogl.c
index ae42578fd..68ee5bd5d 100644
--- a/boilerplate/cairo-boilerplate-cogl.c
+++ b/boilerplate/cairo-boilerplate-cogl.c
@@ -115,7 +115,25 @@ _cairo_boilerplate_cogl_create_onscreen_color_surface (const char	       *name,
     if (height < 1)
         height = 1;
 
-    context = cogl_context_new (NULL, NULL);
+    if (content & CAIRO_CONTENT_ALPHA) {
+		/* A hackish way to ensure that we get a framebuffer with
+		 * an alpha component */
+		CoglSwapChain *swap_chain;
+		CoglOnscreenTemplate *onscreen_template;
+		CoglRenderer *renderer;
+		CoglDisplay *display;
+
+        swap_chain = cogl_swap_chain_new ();
+		cogl_swap_chain_set_has_alpha (swap_chain, TRUE);
+
+        onscreen_template = cogl_onscreen_template_new (swap_chain);
+        renderer = cogl_renderer_new ();
+        display = cogl_display_new (renderer, onscreen_template);
+
+        context = cogl_context_new (display, NULL);
+	} else {
+        context = cogl_context_new (NULL, NULL);
+    }
 
     device = cairo_cogl_device_create (context);
 
commit 02371a714e3fe7500b7cfca5d9ba824aeb3f4695
Author: George Matsumura <gmmatsumura01 at bvsd.org>
Date:   Fri Jul 24 21:27:13 2020 -0600

    cogl: Improve support for hardware without mirrored repeating
    
    Signed-off-by: George Matsumura <gmmatsumura01 at bvsd.org>

diff --git a/src/cairo-cogl-context.c b/src/cairo-cogl-context.c
index a84a9f7d7..e0017dd9b 100644
--- a/src/cairo-cogl-context.c
+++ b/src/cairo-cogl-context.c
@@ -703,11 +703,16 @@ static void
 _cairo_cogl_surface_set_side_band_state (cairo_cogl_surface_t *surface,
 					 cairo_cogl_context_t *cr)
 {
+    cairo_gstate_t *gstate = cr->base.gstate;
 
     if (cr->path_ctm_age == 0) {
 	surface->user_path = &cr->user_path;
-	surface->ctm = &cr->base.gstate->ctm;
-	surface->ctm_inverse = &cr->base.gstate->ctm_inverse;
+        cairo_matrix_multiply (&surface->ctm,
+                               &gstate->ctm,
+                               &gstate->target->device_transform);
+        cairo_matrix_multiply (&surface->ctm_inverse,
+                               &gstate->target->device_transform_inverse,
+                               &gstate->ctm_inverse);
 	surface->path_is_rectangle = cr->path_is_rectangle;
 	if (surface->path_is_rectangle) {
 	    surface->path_rectangle_x = cr->x;
@@ -1002,6 +1007,7 @@ _cairo_cogl_context_create (void *target)
 
     _cairo_path_fixed_init (&cr->user_path);
     cr->path_is_rectangle = FALSE;
+    cr->path_ctm_age = 0;
 
     return &cr->base.base;
 }
diff --git a/src/cairo-cogl-private.h b/src/cairo-cogl-private.h
index edd7b269c..2d662ad2c 100644
--- a/src/cairo-cogl-private.h
+++ b/src/cairo-cogl-private.h
@@ -61,7 +61,8 @@ typedef struct _cairo_cogl_device {
 
     CoglContext *cogl_context;
 
-    cairo_bool_t has_npots, has_mirrored_repeat;
+    cairo_bool_t has_npots;
+    cairo_bool_t has_mirrored_repeat;
 
     CoglAttributeBuffer *buffer_stack;
     size_t buffer_stack_size;
@@ -116,6 +117,10 @@ typedef struct _cairo_cogl_surface {
     int width;
     int height;
 
+    /* Is this a snapshot used for mirrored repeating on hardware which
+     * doesn't have it, consisting of four reflected images? */
+    cairo_bool_t is_mirrored_snapshot;
+
     GQueue *journal;
 
     cairo_clip_t *last_clip;
@@ -134,8 +139,8 @@ typedef struct _cairo_cogl_surface {
      * side band data on the surface...
      */
     cairo_path_fixed_t *user_path;
-    cairo_matrix_t *ctm;
-    cairo_matrix_t *ctm_inverse;
+    cairo_matrix_t ctm;
+    cairo_matrix_t ctm_inverse;
     cairo_bool_t path_is_rectangle;
     double path_rectangle_x;
     double path_rectangle_y;
diff --git a/src/cairo-cogl-surface.c b/src/cairo-cogl-surface.c
index e5382d694..f87115a61 100644
--- a/src/cairo-cogl-surface.c
+++ b/src/cairo-cogl-surface.c
@@ -35,7 +35,6 @@
 #include "cairo-path-fixed-private.h"
 #include "cairo-recording-surface-private.h"
 #include "cairo-recording-surface-inline.h"
-#include "cairo-surface-clipper-private.h"
 #include "cairo-fixed-private.h"
 #include "cairo-device-private.h"
 #include "cairo-composite-rectangles-private.h"
@@ -46,7 +45,6 @@
 #include "cairo-traps-private.h"
 #include "cairo-cogl-context-private.h"
 #include "cairo-cogl-utils-private.h"
-#include "cairo-box-inline.h"
 #include "cairo-surface-subsurface-inline.h"
 #include "cairo-surface-fallback-private.h"
 #include "cairo-surface-offset-private.h"
@@ -54,7 +52,6 @@
 #include "cairo-cogl.h"
 
 #include <cogl/cogl2-experimental.h>
-#include <cogl/deprecated/cogl-texture-deprecated.h>
 #include <glib.h>
 
 #define CAIRO_COGL_DEBUG 0
@@ -245,7 +242,7 @@ _sanitize_trap (cairo_trapezoid_t *t)
 static cairo_status_t
 _cairo_cogl_surface_ensure_framebuffer (cairo_cogl_surface_t *surface)
 {
-    GError *error = NULL;
+    CoglError *error;
 
     if (surface->framebuffer)
 	return CAIRO_STATUS_SUCCESS;
@@ -253,14 +250,17 @@ _cairo_cogl_surface_ensure_framebuffer (cairo_cogl_surface_t *surface)
     surface->framebuffer =
         cogl_offscreen_new_with_texture (surface->texture);
     if (!cogl_framebuffer_allocate (surface->framebuffer, &error)) {
-	g_error_free (error);
+        g_warning ("Could not create framebuffer for surface: %s",
+                   error->message);
+        cogl_error_free (error);
 	cogl_object_unref (surface->framebuffer);
 	surface->framebuffer = NULL;
 	return CAIRO_STATUS_NO_MEMORY;
     }
 
-    cogl_framebuffer_orthographic (surface-> framebuffer, 0, 0,
-                                   surface->width, surface->height,
+    cogl_framebuffer_orthographic (surface->framebuffer, 0, 0,
+                                   cogl_texture_get_width (surface->texture),
+                                   cogl_texture_get_height (surface->texture),
                                    -1, 100);
 
     return CAIRO_STATUS_SUCCESS;
@@ -278,9 +278,18 @@ _cairo_cogl_surface_create_similar (void            *abstract_surface,
     cairo_status_t status;
     cairo_cogl_device_t *dev =
         to_device(reference_surface->base.device);
+    int tex_width = width;
+    int tex_height = height;
+
+    /* In the case of lack of NPOT texture support, we allocate texture
+     * with dimensions of the next power of two */
+    if (!dev->has_npots) {
+        tex_width = pow (2, ceil (log2 (tex_width)));
+        tex_height = pow (2, ceil (log2 (tex_height)));
+    }
 
     texture = cogl_texture_2d_new_with_size (dev->cogl_context,
-                                             width, height);
+                                             tex_width, tex_height);
     if (!texture)
         return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
@@ -297,6 +306,14 @@ _cairo_cogl_surface_create_similar (void            *abstract_surface,
     if (unlikely (surface->base.status))
 	return &surface->base;
 
+    /* The surface will take a reference on the texture */
+    cogl_object_unref (texture);
+
+    /* If we passed a texture with larger dimensions, we need to set
+     * the surface dimensions */
+    surface->width = width;
+    surface->height = height;
+
     status = _cairo_cogl_surface_ensure_framebuffer (surface);
     if (unlikely (status)) {
 	cairo_surface_destroy (&surface->base);
@@ -1348,6 +1365,7 @@ get_default_cogl_format_from_components (CoglTextureComponents components)
     }
 }
 
+#if 0
 static CoglTextureComponents
 get_components_from_cogl_format (CoglPixelFormat format)
 {
@@ -1378,6 +1396,7 @@ get_components_from_cogl_format (CoglPixelFormat format)
     return CAIRO_FORMAT_INVALID;
     }
 }
+#endif
 
 static CoglPixelFormat
 get_cogl_format_from_cairo_format (cairo_format_t cairo_format);
@@ -1515,8 +1534,14 @@ _cairo_cogl_surface_acquire_source_image (void		         *abstract_surface,
                 get_cogl_format_from_cairo_format (cairo_format);
         }
 
+        /* We use the actual texture dimensions here instead, because
+         * if we have a larger texture than the surface dimensions for
+         * devices not supporting NPOT textures, the surface dimensions
+         * will not be able to fit the data */
         cairo_image_surface_t *image = (cairo_image_surface_t *)
-	        cairo_image_surface_create (cairo_format, surface->width, surface->height);
+	        cairo_image_surface_create (cairo_format,
+	                                    cogl_texture_get_width (surface->texture),
+	                                    cogl_texture_get_height (surface->texture));
         if (image->base.status)
             return image->base.status;
 
@@ -1525,6 +1550,13 @@ _cairo_cogl_surface_acquire_source_image (void		         *abstract_surface,
                                0,
                                image->data);
 
+        /* If the texture dimensions were different than the surface
+         * dimensions, this will set them to the correct values.
+         * Because the stride stays the same, it will still function
+         * correctly */
+        image->width = surface->width;
+        image->height = surface->height;
+
 	image->base.is_clear = FALSE;
 	*image_out = image;
     } else {
@@ -1614,7 +1646,8 @@ _cairo_cogl_path_fixed_rectangle (cairo_path_fixed_t *path,
 
 
 static CoglPipelineWrapMode
-get_cogl_wrap_mode_for_extend (cairo_extend_t extend_mode)
+get_cogl_wrap_mode_for_extend (cairo_extend_t       extend_mode,
+                               cairo_cogl_device_t *dev)
 {
     switch (extend_mode)
     {
@@ -1625,314 +1658,555 @@ get_cogl_wrap_mode_for_extend (cairo_extend_t extend_mode)
     case CAIRO_EXTEND_REPEAT:
 	return COGL_PIPELINE_WRAP_MODE_REPEAT;
     case CAIRO_EXTEND_REFLECT:
-        /* TODO: Detect hardware where MIRRORED_REPEAT is not available
-         * and implement fallback */
-	return COGL_PIPELINE_WRAP_MODE_MIRRORED_REPEAT;
+        if (!dev->has_mirrored_repeat)
+            /* If the hardware cannot support mirrored repeating, we
+             * emulate it elsewhere */
+            return COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE;
+        else
+	    return COGL_PIPELINE_WRAP_MODE_MIRRORED_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;
-    CoglOffscreen *offscreen = NULL;
-    CoglTexture2D *pot = NULL;
-    CoglPipeline *pipeline;
-    GError *error;
+static void
+_cairo_cogl_matrix_all_scale (cairo_matrix_t *matrix,
+                              double          xscale,
+                              double          yscale)
+{
+    /* Since cairo_matrix_scale does not scale the x0 and y0 components,
+     * which is required for scaling translations to normalized
+     * coordinates, use a custom solution here. */
+    matrix->xx *= xscale;
+    matrix->yx *= yscale;
+    matrix->xy *= xscale;
+    matrix->yy *= yscale;
+    matrix->x0 *= xscale;
+    matrix->y0 *= yscale;
+}
 
-    pot_width = _cairo_cogl_util_next_p2 (width);
-    pot_height = _cairo_cogl_util_next_p2 (height);
+static CoglTexture *
+_cairo_cogl_scale_texture (CoglContext           *context,
+                           CoglTexture           *texture_in,
+                           unsigned int           new_width,
+                           unsigned int           new_height,
+                           cairo_bool_t           do_mirror_texture,
+                           cairo_bool_t           always_new_texture)
+{
+    CoglTexture *texture_out = NULL;
+    CoglPipeline *copying_pipeline = NULL;
+    CoglFramebuffer *fb = NULL;
+    CoglError *error;
+    unsigned int tex_width = new_width;
+    unsigned int tex_height = new_height;
+
+    /* If the texture is already in the desired dimensions and we are
+     * not mirroring it, copying it, or reading from different extents,
+     * return it unmodified */
+    if (!do_mirror_texture && !always_new_texture &&
+        new_width == cogl_texture_get_width (texture_in) &&
+        new_height == cogl_texture_get_height (texture_in))
+        return texture_in;
+
+    if (do_mirror_texture) {
+        tex_width *= 2;
+        tex_height *= 2;
+    }
 
-    if (pot_width == width && pot_height == height)
-	return CAIRO_INT_STATUS_SUCCESS;
+    texture_out =
+        cogl_texture_2d_new_with_size (context, tex_width, tex_height);
+    if (!texture_out) {
+        g_warning ("Failed to allocate texture");
+        goto BAIL;
+    }
 
-    for (;;) {
-	pot = cogl_texture_2d_new_with_size (context,
-					     pot_width,
-					     pot_height);
-	if (pot)
-	    break;
+    fb = cogl_offscreen_new_with_texture (texture_out);
+    if (!cogl_framebuffer_allocate (fb, &error)) {
+        g_warning ("Could not get framebuffer for texture scaling: %s",
+                   error->message);
+        cogl_error_free (error);
+        goto BAIL;
+    }
+
+    cogl_framebuffer_orthographic (fb, 0, 0,
+                                       tex_width, tex_height,
+                                       -1, 100);
+
+    copying_pipeline = cogl_pipeline_new (context);
+    cogl_pipeline_set_layer_texture (copying_pipeline, 0, texture_in);
+
+    if (do_mirror_texture) {
+        /* Draw four rectangles to the new texture with the appropriate
+         * reflection on each one */
+
+        const float rect_coordinates[32] = {
+            /* Rectangle 1 */
+            0, 0, 0.5 * tex_width, 0.5 * tex_height,
+            0, 0, 1, 1,
+
+            /* Rectangle 2 */
+            tex_width, 0, 0.5 * tex_width, 0.5 * tex_height,
+            0, 0, 1, 1,
+
+            /* Rectangle 3 */
+            0, tex_height, 0.5 * tex_width, 0.5 * tex_height,
+            0, 0, 1, 1,
+
+            /* Rectangle 4 */
+            tex_width, tex_height, 0.5 * tex_width, 0.5 * tex_height,
+            0, 0, 1, 1
+        };
+
+        cogl_framebuffer_draw_textured_rectangles (fb,
+                                                   copying_pipeline,
+                                                   rect_coordinates,
+                                                   4);
+    } else {
+        cogl_framebuffer_draw_textured_rectangle (fb,
+                                                  copying_pipeline,
+                                                  0, 0,
+                                                  tex_width,
+                                                  tex_height,
+                                                  0, 0, 1, 1);
+    }
 
-	if (pot_width > pot_height)
-	    pot_width >>= 1;
-	else
-	    pot_height >>= 1;
+    cogl_object_unref (fb);
+    cogl_object_unref (copying_pipeline);
+    cogl_object_unref (texture_in);
 
-	if (!pot_width || !pot_height)
-	    break;
+    return texture_out;
+
+BAIL:
+    if (texture_out)
+        cogl_object_unref (texture_out);
+    if (fb)
+        cogl_object_unref (fb);
+    if (copying_pipeline)
+        cogl_object_unref (copying_pipeline);
+
+    return NULL;
+}
+
+/* NB: a reference for the texture is transferred to the caller which
+ * should be unrefed */
+static CoglTexture *
+_cairo_cogl_acquire_cogl_surface_texture (cairo_cogl_surface_t        *reference_surface,
+                                          cairo_surface_t             *surface,
+                                          const cairo_rectangle_int_t *surface_extents,
+                                          const cairo_matrix_t        *pattern_matrix,
+                                          cairo_matrix_t              *out_matrix,
+                                          const cairo_bool_t           need_mirrored_texture,
+                                          cairo_bool_t                *is_mirrored_texture)
+{
+    CoglTexture *texture;
+    cairo_surface_t *clone = NULL;
+    cairo_cogl_surface_t *cogl_surface =
+        (cairo_cogl_surface_t *)surface;
+    cairo_bool_t do_mirror_texture;
+    cairo_cogl_device_t *dev =
+        to_device (reference_surface->base.device);
+    double xscale, yscale;
+    int new_width = surface_extents->width;
+    int new_height = surface_extents->height;
+
+    *out_matrix = *pattern_matrix;
+    *is_mirrored_texture = FALSE;
+
+    if (unlikely (_cairo_surface_flush (surface, 0))) {
+        g_warning ("Error flushing source surface while getting "
+                   "pattern texture");
+        goto BAIL;
     }
 
-    *pot_texture = pot;
+    *is_mirrored_texture =
+        need_mirrored_texture || cogl_surface->is_mirrored_snapshot;
+    do_mirror_texture =
+        need_mirrored_texture && !cogl_surface->is_mirrored_snapshot;
 
-    if (!pot)
-	return CAIRO_INT_STATUS_NO_MEMORY;
+    /* There seems to be a bug in which cogl isn't flushing its own
+     * internal journal when reading from dependent sub-textures.
+     * If this is ever fixed, the following block of code can be
+     * removed. */
+    {
+        _cairo_cogl_surface_ensure_framebuffer (cogl_surface);
+        cogl_framebuffer_finish (cogl_surface->framebuffer);
+    }
+    /* We copy the surface to a new texture, thereby making a
+     * snapshot of it, as its contents may change between the time
+     * we log the pipeline and when we flush the journal. The sub
+     * texture itself cannot be used while drawing primitives, so we do
+     * a copy to a 2d texture. */
+    texture = cogl_sub_texture_new (dev->cogl_context,
+                                    cogl_surface->texture,
+                                    surface_extents->x,
+                                    surface_extents->y,
+                                    surface_extents->width,
+                                    surface_extents->height);
+    if (unlikely (!texture))
+        goto BAIL;
 
-    cogl_texture_set_components (pot,
-                                 cogl_texture_get_components(texture));
+    /* If we do not support NPOT dimensions, scale the new texture to
+     * the next power of two while copying */
+    if (!dev->has_npots) {
+        new_width = (int)pow (2, ceil (log2 (new_width)));
+        new_height = (int)pow (2, ceil (log2 (new_height)));
+    }
+    texture = _cairo_cogl_scale_texture (dev->cogl_context,
+                                         texture,
+                                         new_width,
+                                         new_height,
+                                         do_mirror_texture,
+                                         TRUE);
+    if (unlikely (!texture))
+        goto BAIL;
 
-    /* Use the GPU to do a bilinear filtered scale from npot to pot... */
-    offscreen = cogl_offscreen_new_with_texture (pot);
-    error = NULL;
-    if (!cogl_framebuffer_allocate (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;
+    clone =
+        _cairo_cogl_surface_create_full (dev,
+                                         reference_surface->ignore_alpha,
+                                         NULL,
+                                         texture);
+    if (unlikely (clone->status)) {
+        g_warning ("Could not get clone surface for texture");
+        goto BAIL;
+    }
+    _cairo_surface_attach_snapshot (surface, clone, NULL);
+
+    /* Convert from un-normalized source coordinates in backend
+     * coordinates to normalized texture coordinates. */
+    if (*is_mirrored_texture) {
+        xscale = 0.5 / surface_extents->width;
+        yscale = 0.5 / surface_extents->height;
+    } else {
+        xscale = 1.0 / surface_extents->width;
+        yscale = 1.0 / surface_extents->height;
     }
+    _cairo_cogl_matrix_all_scale (out_matrix, xscale, yscale);
+
+    return texture;
 
-    pipeline = cogl_pipeline_new (context);
-    cogl_pipeline_set_layer_texture (pipeline, 1, texture);
-    cogl_framebuffer_draw_textured_rectangle (offscreen, pipeline,
-                                              -1, 1, 1, -1,
-                                              0, 0, 1, 1);
+BAIL:
+    if (texture)
+        cogl_object_unref (texture);
+    if (clone)
+        cairo_surface_destroy (clone);
 
-    cogl_object_unref (offscreen);
+    return NULL;
 }
-#endif
 
-/* NB: a reference for the texture is transferred to the caller which should
- * be unrefed */
+/* 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             *surface,
-                                     const cairo_rectangle_int_t *extents,
-                                     const cairo_matrix_t        *pattern_matrix,
-                                     cairo_bool_t                *has_pre_transform,
-                                     cairo_bool_t                *vertical_invert)
+_cairo_cogl_acquire_recording_surface_texture (cairo_cogl_surface_t        *reference_surface,
+                                               cairo_surface_t             *surface,
+                                               const cairo_rectangle_int_t *extents,
+                                               const cairo_matrix_t        *pattern_matrix,
+                                               cairo_matrix_t              *out_matrix,
+                                               const cairo_bool_t           need_mirrored_texture,
+                                               cairo_bool_t                *is_mirrored_texture)
 {
-    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, *unwrapped;
-    cairo_rectangle_int_t surface_extents;
-    cairo_matrix_t transform;
-    CoglPipeline *copying_pipeline = NULL;
+    CoglTexture *texture = NULL;
+    cairo_surface_t *clone = NULL;
     cairo_cogl_device_t *dev =
-        to_device(reference_surface->base.device);
+        to_device (reference_surface->base.device);
+    cairo_matrix_t transform;
+    int tex_height, tex_width;
+    double xscale, yscale;
 
-    *has_pre_transform = FALSE;
-    *vertical_invert = FALSE;
+    *is_mirrored_texture = FALSE;
 
-    clone = _cairo_surface_has_snapshot (surface, &_cairo_cogl_surface_backend);
-    if (clone)
-        if (((cairo_cogl_surface_t *)clone)->texture)
-            return cogl_object_ref (((cairo_cogl_surface_t *)clone)->texture);
-
-    /* Unwrap things like subsurfaces, but get the original extents */
-    unwrapped = _cairo_surface_get_source (surface, &surface_extents);
-
-    if (_cairo_surface_is_recording (unwrapped)) {
-        /* We pre-transform the recording surface here and make the
-         * target surface the size of the extents in order to reduce
-         * texture size. When we return to the acquire_pattern_texture
-         * function, it will know to adjust the texture matrix
-         * accordingly. */
-        *has_pre_transform = TRUE;
-
-        texture = cogl_texture_2d_new_with_size (dev->cogl_context,
-                                                 extents->width,
-                                                 extents->height);
-        if (!texture) {
-	    g_warning ("Failed to allocate texture: %s", error->message);
-	    g_error_free (error);
-	    goto BAIL;
-        }
+    /* We will pre-transform all of the drawing by the pattern matrix
+     * and confine it to the required extents, so no later transform
+     * will be required */
+    cairo_matrix_init_translate (out_matrix, -extents->x, -extents->y);
 
-        clone =
-            _cairo_cogl_surface_create_full (dev,
-                                             reference_surface->ignore_alpha,
-                                             NULL,
-                                             texture);
-
-        cairo_matrix_init_translate (&transform,
-                                     extents->x,
-                                     extents->y);
-        cairo_matrix_multiply (&transform, &transform, pattern_matrix);
-
-        if (_cairo_recording_surface_replay_with_clip (unwrapped,
-                                                       &transform,
-                                                       clone,
-                                                       NULL))
-        {
-            g_warning ("could not replay recording surface");
-            texture = NULL;
-            goto BAIL;
-        }
+    cairo_matrix_init_translate (&transform, extents->x, extents->y);
+    cairo_matrix_multiply (&transform, &transform, pattern_matrix);
 
-        cairo_surface_destroy (clone);
-        return texture;
+    if (!dev->has_npots) {
+        /* Record to a texture sized to the next power of two */
+        tex_width = (int)pow (2, ceil (log2 (extents->width)));
+        tex_height = (int)pow (2, ceil (log2 (extents->height)));
+
+        /* And scale accordingly */
+        cairo_matrix_scale (&transform,
+                            (double)extents->width / (double)tex_width,
+                            (double)extents->height / (double)tex_height);
+    } else {
+        tex_width = extents->width;
+        tex_height = extents->height;
+    }
+
+    texture = cogl_texture_2d_new_with_size (dev->cogl_context,
+                                             tex_width,
+                                             tex_height);
+    if (unlikely (!texture)) {
+        g_warning ("Failed to allocate texture");
+        goto BAIL;
     }
 
-    if (to_device(unwrapped->device) == dev &&
-        ((cairo_cogl_surface_t *)unwrapped)->texture)
+    /* Do not attach this as a snapshot, as it only represents part of
+     * the surface */
+    clone =
+        _cairo_cogl_surface_create_full (dev,
+                                         reference_surface->ignore_alpha,
+                                         NULL,
+                                         texture);
+    if (_cairo_cogl_surface_ensure_framebuffer ((cairo_cogl_surface_t *)clone))
     {
-        cairo_cogl_surface_t *cogl_surface =
-            (cairo_cogl_surface_t *)unwrapped;
+        g_warning ("Could not get framebuffer for replaying recording "
+                   "surface");
+        goto BAIL;
+    }
 
-        if (unlikely (_cairo_surface_flush (unwrapped, 0))) {
-            g_warning ("Error flushing source surface while getting "
-                       "pattern texture");
+    if (_cairo_recording_surface_replay_with_clip (surface,
+                                                   &transform,
+                                                   clone,
+                                                   NULL))
+    {
+        g_warning ("Could not replay recording surface");
+        goto BAIL;
+    }
+    _cairo_cogl_journal_flush ((cairo_cogl_surface_t *)clone);
+    cairo_surface_destroy (clone);
+
+    if (need_mirrored_texture) {
+        /* Scale to the same image extents, but mirror the texture,
+         * thereby making it larger */
+        texture = _cairo_cogl_scale_texture (dev->cogl_context,
+                                             texture,
+                                             tex_width,
+                                             tex_height,
+                                             TRUE,
+                                             FALSE);
+        if (unlikely (!texture))
             goto BAIL;
-        }
 
-        /* We copy the surface to a new texture, thereby making a
-         * snapshot of it, as its contents may change between the time
-         * we log the pipeline and when we flush the journal */
-        texture =
-            cogl_texture_2d_new_with_size (dev->cogl_context,
-                                           surface_extents.width,
-                                           surface_extents.height);
-        if (!texture) {
-            g_warning ("Failed to allocate texture: %s",
-                           error->message);
-            g_error_free (error);
-            goto BAIL;
-        }
+        *is_mirrored_texture = TRUE;
+    }
 
-        clone =
-            _cairo_cogl_surface_create_full (dev,
-                                             reference_surface->ignore_alpha,
-                                             NULL,
-                                             texture);
+    /* Convert from un-normalized source coordinates in backend
+     * coordinates to normalized texture coordinates. */
+    if (*is_mirrored_texture) {
+        xscale = 0.5 / extents->width;
+        yscale = 0.5 / extents->height;
+    } else {
+        xscale = 1.0 / extents->width;
+        yscale = 1.0 / extents->height;
+    }
+    _cairo_cogl_matrix_all_scale (out_matrix, xscale, yscale);
 
-        if (_cairo_cogl_surface_ensure_framebuffer ((cairo_cogl_surface_t *)clone)) {
-            g_warning ("Could not get framebuffer for surface clone");
-            goto BAIL;
-        }
+    return texture;
 
-        /* If cogl ever makes its internal _cogl_blit API public
-         * we could use that and make this much simpler */
-        copying_pipeline = cogl_pipeline_new (dev->cogl_context);
-        cogl_pipeline_set_layer_texture (copying_pipeline,
-                                         0,
-                                         cogl_surface->texture);
+BAIL:
+    if (clone)
+        cairo_surface_destroy (clone);
+    if (texture)
+        cogl_object_unref (texture);
 
-        /* Factors for normalizing the texture coordinates */
-        double xscale = 1.0 / cogl_texture_get_width (cogl_surface->texture);
-        double yscale = 1.0 / cogl_texture_get_height (cogl_surface->texture);
+    return NULL;
+}
 
-        cogl_framebuffer_draw_textured_rectangle (((cairo_cogl_surface_t *)clone)->framebuffer,
-                                                  copying_pipeline,
-                                                  0,
-                                                  0,
-                                                  surface_extents.width,
-                                                  surface_extents.height,
-                                                  xscale * surface_extents.x,
-                                                  yscale * surface_extents.y,
-                                                  xscale * (surface_extents.x + surface_extents.width),
-                                                  yscale * (surface_extents.y + surface_extents.height));
-    } else {
-        ptrdiff_t stride;
-        unsigned char *data;
+/* NB: a reference for the texture is transferred to the caller which
+ * should be unrefed */
+static CoglTexture *
+_cairo_cogl_acquire_generic_surface_texture (cairo_cogl_surface_t *reference_surface,
+                                             cairo_surface_t      *surface,
+                                             const cairo_matrix_t *pattern_matrix,
+                                             cairo_matrix_t       *out_matrix,
+                                             const cairo_bool_t    need_mirrored_texture,
+                                             cairo_bool_t         *is_mirrored_texture)
+{
+    CoglTexture *texture = NULL;
+    cairo_image_surface_t *image;
+    cairo_image_surface_t *acquired_image = NULL;
+    void *image_extra;
+    cairo_image_surface_t *image_clone = NULL;
+    cairo_surface_t *clone = NULL;
+    CoglPixelFormat format;
+    cairo_cogl_device_t *dev =
+        to_device (reference_surface->base.device);
+    ptrdiff_t stride;
+    unsigned char *data;
+    double xscale, yscale;
 
-        // g_warning ("Uploading image surface to texture");
+    *out_matrix = *pattern_matrix;
+    *is_mirrored_texture = FALSE;
 
-        if (_cairo_surface_is_image (surface)) {
-            image = (cairo_image_surface_t *)surface;
-        } else {
-            cairo_status_t status =
-                _cairo_surface_acquire_source_image (surface,
-                                                     &acquired_image,
-                                                     &image_extra);
-            if (unlikely (status)) {
-                g_warning ("acquire_source_image failed: %s [%d]",
-                           cairo_status_to_string (status), status);
-                return NULL;
-            }
-            image = acquired_image;
+    if (_cairo_surface_is_image (surface)) {
+        image = (cairo_image_surface_t *)surface;
+    } else {
+        cairo_status_t status =
+            _cairo_surface_acquire_source_image (surface,
+                                                 &acquired_image,
+                                                 &image_extra);
+        if (unlikely (status)) {
+            g_warning ("acquire_source_image failed: %s [%d]",
+                        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->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);
+        format =
+            get_cogl_format_from_cairo_format (image_clone->format);
+        assert (format);
 
-            image = image_clone;
-        }
+        image = image_clone;
+    }
 
-        if (image->stride < 0) {
-            /* If the stride is negative, this modifies the data pointer so
-             * that all of the pixels are read into the texture, but
-             * upside-down. We then set vertical_invert so that
-             * acquire_pattern_texture will adjust the texture sampling
-             * matrix to correct this. */
-            stride = image->stride * -1;
-            data = image->data - stride * (image->height - 1);
-            *vertical_invert = TRUE;
-        } else {
-            stride = image->stride;
-            data = image->data;
-        }
+    if (image->stride < 0) {
+        /* If the stride is negative, this modifies the data pointer so
+         * that all of the pixels are read into the texture, but
+         * upside-down. We then invert the matrix so the texture is
+         * read from the bottom up instead of from the top down. */
+         stride = -image->stride;
+         data = image->data - stride * (image->height - 1);
+
+         out_matrix->yx *= -1.0;
+         out_matrix->yy *= -1.0;
+         out_matrix->y0 += image->height;
+    } else {
+        stride = image->stride;
+        data = image->data;
+    }
 
+    if (!dev->has_npots)
+        texture =
+            cogl_texture_2d_sliced_new_from_data (dev->cogl_context,
+                                                  image->width,
+                                                  image->height,
+                                                  COGL_TEXTURE_MAX_WASTE,
+                                                  format, /* incoming */
+                                                  stride,
+                                                  data,
+                                                  NULL);
+    else
         texture = cogl_texture_2d_new_from_data (dev->cogl_context,
                                                  image->width,
                                                  image->height,
                                                  format, /* incoming */
                                                  stride,
                                                  data,
-                                                 &error);
-        if (!texture) {
-            g_warning ("Failed to allocate texture: %s",
-                       error->message);
-            g_error_free (error);
-            goto BAIL;
+                                                 NULL);
+    if (!texture) {
+        g_warning ("Failed to allocate texture");
+        goto BAIL;
+    }
+
+    if (need_mirrored_texture) {
+        int new_width = image->width * 2;
+        int new_height = image->height * 2;
+
+        /* If the device does not support npot textures, scale to the
+         * next power of two as well */
+        if (!dev->has_npots) {
+            new_width = (int)pow (2, ceil (log2 (new_width)));
+            new_height = (int)pow (2, ceil (log2 (new_height)));
         }
 
-        clone =
-            _cairo_cogl_surface_create_full (dev,
-                                             reference_surface->ignore_alpha,
-                                             NULL,
-                                             texture);
+        texture = _cairo_cogl_scale_texture (dev->cogl_context,
+                                             texture,
+                                             new_width,
+                                             new_height,
+                                             TRUE,
+                                             FALSE);
+        if (unlikely (!texture))
+            goto BAIL;
+
+        *is_mirrored_texture = TRUE;
+    } else if (!dev->has_npots) {
+        /* We need to scale the texture up if the hardware does not
+         * support npots */
+
+        /* Get dimensions for the next power of two */
+        int new_width = (int)pow (2, ceil (log2 (image->width)));
+        int new_height = (int)pow (2, ceil (log2 (image->height)));
+
+        texture = _cairo_cogl_scale_texture (dev->cogl_context,
+                                             texture,
+                                             new_width,
+                                             new_height,
+                                             FALSE,
+                                             FALSE);
+        if (unlikely (!texture))
+            goto BAIL;
+    }
+
+    clone =
+        _cairo_cogl_surface_create_full (dev,
+                                         reference_surface->ignore_alpha,
+                                         NULL,
+                                         texture);
+    if (unlikely (clone->status)) {
+        g_warning ("Unable to create clone surface for texture");
+        goto BAIL;
     }
 
+    if (*is_mirrored_texture)
+        ((cairo_cogl_surface_t *)clone)->is_mirrored_snapshot = TRUE;
+
     if (_cairo_surface_is_subsurface (surface))
         _cairo_surface_subsurface_set_snapshot (surface, clone);
     else
         _cairo_surface_attach_snapshot (surface, clone, NULL);
 
+    /* Attaching the snapshot will take a reference on the clone surface... */
+    cairo_surface_destroy (clone);
+    clone = NULL;
+
+    /* Convert from un-normalized source coordinates in backend
+     * coordinates to normalized texture coordinates. */
+    if (*is_mirrored_texture) {
+        xscale = 0.5 / image->width;
+        yscale = 0.5 / image->height;
+    } else {
+        xscale = 1.0 / image->width;
+        yscale = 1.0 / image->height;
+    }
+    _cairo_cogl_matrix_all_scale (out_matrix, xscale, yscale);
+
+    /* Release intermediate surface representations */
+    if (image_clone) {
+	cairo_surface_destroy (&image_clone->base);
+        image_clone = NULL;
+    }
+    if (acquired_image) {
+	_cairo_surface_release_source_image (surface,
+                                             acquired_image,
+                                             image_extra);
+        acquired_image = NULL;
+    }
+
+    return texture;
+
 BAIL:
     if (clone)
-        /* Attaching the snapshot will take a reference on the clone surface... */
         cairo_surface_destroy (clone);
-    if (copying_pipeline)
-        cogl_object_unref (copying_pipeline);
     if (image_clone)
 	cairo_surface_destroy (&image_clone->base);
     if (acquired_image)
-	_cairo_surface_release_source_image (surface, acquired_image, image_extra);
+	_cairo_surface_release_source_image (surface,
+                                             acquired_image,
+                                             image_extra);
+    if (texture)
+        cogl_object_unref (texture);
 
-    return texture;
+    return NULL;
 }
 
 static cairo_status_t
 _cairo_cogl_create_tex_clip (cairo_path_fixed_t *tex_clip,
-                             cairo_matrix_t      inverse)
+                             cairo_matrix_t      inverse,
+                             cairo_bool_t        is_mirrored_texture)
 {
     cairo_status_t status;
 
@@ -1940,9 +2214,15 @@ _cairo_cogl_create_tex_clip (cairo_path_fixed_t *tex_clip,
     if (unlikely (status))
         return status;
 
-    status = _cairo_cogl_path_fixed_rectangle (tex_clip, 0, 0,
-                                               CAIRO_FIXED_ONE,
-                                               CAIRO_FIXED_ONE);
+    if (is_mirrored_texture)
+        status =
+            _cairo_cogl_path_fixed_rectangle (tex_clip, 0, 0,
+                                              _cairo_fixed_from_double (0.5),
+                                              _cairo_fixed_from_double (0.5));
+    else
+        status = _cairo_cogl_path_fixed_rectangle (tex_clip, 0, 0,
+                                                   CAIRO_FIXED_ONE,
+                                                   CAIRO_FIXED_ONE);
     if (unlikely (status))
 	return status;
 
@@ -1961,66 +2241,88 @@ _cairo_cogl_acquire_pattern_texture (const cairo_pattern_t           *pattern,
                                      cairo_path_fixed_t              *tex_clip)
 {
     CoglTexture *texture = NULL;
-    cairo_bool_t has_pre_transform;
-    cairo_bool_t vertical_invert;
+    cairo_cogl_device_t *dev = to_device (destination->base.device);
 
     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,
-                                                 extents,
-                                                 &pattern->matrix,
-                                                 &has_pre_transform,
-                                                 &vertical_invert);
-	if (!texture)
-	    return NULL;
-
-#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
-
-        if (has_pre_transform)
-            cairo_matrix_init_translate (&attributes->matrix,
-                                         -extents->x,
-                                         -extents->y);
-        else
-	    attributes->matrix = pattern->matrix;
+        cairo_cogl_surface_t *clone;
+        cairo_surface_t *surface = ((cairo_surface_pattern_t *)pattern)->surface;
+        cairo_bool_t is_mirrored_texture;
+        cairo_bool_t need_mirrored_texture =
+            (pattern->extend == CAIRO_EXTEND_REFLECT &&
+             !dev->has_mirrored_repeat);
+
+        clone = (cairo_cogl_surface_t *)
+            _cairo_surface_has_snapshot (surface,
+                                         &_cairo_cogl_surface_backend);
+        if (clone && clone->texture)
+            if ((!need_mirrored_texture) || clone->is_mirrored_snapshot)
+            {
+                double xscale, yscale;
+
+                texture = cogl_object_ref (clone->texture);
+                attributes->matrix = pattern->matrix;
+
+                /* Get matrix for normalizing the texture coordinates */
+                if (clone->is_mirrored_snapshot) {
+                    xscale = 0.5 / clone->width;
+                    yscale = 0.5 / clone->height;
+                    is_mirrored_texture = TRUE;
+                } else {
+                    xscale = 1.0 / clone->width;
+                    yscale = 1.0 / clone->height;
+                    is_mirrored_texture = FALSE;
+                }
+                _cairo_cogl_matrix_all_scale (&attributes->matrix,
+                                              xscale,
+                                              yscale);
+            };
 
-	/* Convert from un-normalized source coordinates in backend
-	 * coordinates to normalized texture coordinates. Since
-         * cairo_matrix_scale does not scale the x0 and y0 components,
-         * which is required for translations in normalized
-         * coordinates, use a custom solution here. */
-        double xscale = 1.0 / cogl_texture_get_width (texture);
-        double yscale = 1.0 / cogl_texture_get_height (texture);
-        attributes->matrix.xx *= xscale;
-        attributes->matrix.yx *= yscale;
-        attributes->matrix.xy *= xscale;
-        attributes->matrix.yy *= yscale;
-        attributes->matrix.x0 *= xscale;
-        attributes->matrix.y0 *= yscale;
-
-        if (vertical_invert) {
-            /* Convert the normalized texture matrix so that we read
-             * the texture from the bottom up instead of from the top
-             * down */
-            attributes->matrix.yx *= -1.0;
-            attributes->matrix.yy *= -1.0;
-            attributes->matrix.y0 += 1.0;
+        if (!texture) {
+            cairo_rectangle_int_t surface_extents;
+            cairo_surface_t *unwrapped =
+                _cairo_surface_get_source (surface, &surface_extents);
+
+            if (_cairo_surface_is_recording (surface)) {
+                texture =
+                    _cairo_cogl_acquire_recording_surface_texture (destination,
+                                                                   surface,
+                                                                   extents,
+                                                                   &pattern->matrix,
+                                                                   &attributes->matrix,
+                                                                   need_mirrored_texture,
+                                                                   &is_mirrored_texture);
+            } else if (surface->type == CAIRO_SURFACE_TYPE_COGL &&
+                       ((cairo_cogl_surface_t *)unwrapped)->texture) {
+                texture =
+                    _cairo_cogl_acquire_cogl_surface_texture (destination,
+                                                              unwrapped,
+                                                              &surface_extents,
+                                                              &pattern->matrix,
+                                                              &attributes->matrix,
+                                                              need_mirrored_texture,
+                                                              &is_mirrored_texture);
+            } else {
+                texture =
+                    _cairo_cogl_acquire_generic_surface_texture (destination,
+                                                                 surface,
+                                                                 &pattern->matrix,
+                                                                 &attributes->matrix,
+                                                                 need_mirrored_texture,
+                                                                 &is_mirrored_texture);
+            }
         }
 
+        if (!texture)
+            return NULL;
+
 	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->s_wrap =
+            get_cogl_wrap_mode_for_extend (pattern->extend, dev);
 	attributes->t_wrap = attributes->s_wrap;
 
         /* In order to support CAIRO_EXTEND_NONE, we use the same wrap
@@ -2029,7 +2331,9 @@ _cairo_cogl_acquire_pattern_texture (const cairo_pattern_t           *pattern,
          * the texture boundaries. */
         if (pattern->extend == CAIRO_EXTEND_NONE && tex_clip)
             if (_cairo_cogl_create_tex_clip (tex_clip,
-                                             attributes->matrix)) {
+                                             attributes->matrix,
+                                             is_mirrored_texture))
+            {
                 cogl_object_unref (texture);
                 return NULL;
             }
@@ -2040,6 +2344,11 @@ _cairo_cogl_acquire_pattern_texture (const cairo_pattern_t           *pattern,
     case CAIRO_PATTERN_TYPE_MESH:
     case CAIRO_PATTERN_TYPE_RASTER_SOURCE: {
 	cairo_surface_t *surface;
+        cairo_matrix_t new_pattern_matrix;
+        cairo_bool_t is_mirrored_texture;
+        cairo_bool_t need_mirrored_texture =
+            (pattern->extend == CAIRO_EXTEND_REFLECT &&
+             !dev->has_mirrored_repeat);
 
 	surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
 					      extents->width, extents->height);
@@ -2051,43 +2360,19 @@ _cairo_cogl_acquire_pattern_texture (const cairo_pattern_t           *pattern,
 	    return NULL;
 	}
 
+        cairo_matrix_init_translate (&new_pattern_matrix,
+                                     -extents->x, -extents->y);
+
 	texture =
-            _cairo_cogl_acquire_surface_texture (destination,
-                                                 surface,
-                                                 NULL, // As long as the surface is an image,
-                                                 NULL, // acquire_surface_texture shouldn't access these values
-                                                 &has_pre_transform,
-                                                 &vertical_invert);
+            _cairo_cogl_acquire_generic_surface_texture (destination,
+                                                         surface,
+                                                         &new_pattern_matrix,
+                                                         &attributes->matrix,
+                                                         need_mirrored_texture,
+                                                         &is_mirrored_texture);
 	if (!texture)
 	    goto BAIL;
 
-        cairo_matrix_init_translate (&attributes->matrix,
-                                     -extents->x,
-                                     -extents->y);
-
-	/* Convert from un-normalized source coordinates in backend
-	 * coordinates to normalized texture coordinates. Since
-         * cairo_matrix_scale does not scale the x0 and y0 components,
-         * which is required for translations in normalized
-         * coordinates, use a custom solution here. */
-        double xscale = 1.0 / cogl_texture_get_width (texture);
-        double yscale = 1.0 / cogl_texture_get_height (texture);
-        attributes->matrix.xx *= xscale;
-        attributes->matrix.yx *= yscale;
-        attributes->matrix.xy *= xscale;
-        attributes->matrix.yy *= yscale;
-        attributes->matrix.x0 *= xscale;
-        attributes->matrix.y0 *= yscale;
-
-        if (vertical_invert) {
-            /* Convert the normalized texture matrix so that we read
-             * the texture from the bottom up instead of from the top
-             * down */
-            attributes->matrix.yx *= -1.0;
-            attributes->matrix.yy *= -1.0;
-            attributes->matrix.y0 += 1.0;
-        }
-
 	attributes->extend = pattern->extend;
 	attributes->filter = CAIRO_FILTER_NEAREST;
 	attributes->has_component_alpha = pattern->has_component_alpha;
@@ -2102,7 +2387,9 @@ _cairo_cogl_acquire_pattern_texture (const cairo_pattern_t           *pattern,
          * the texture boundaries. */
         if (pattern->extend == CAIRO_EXTEND_NONE && tex_clip)
             if (_cairo_cogl_create_tex_clip (tex_clip,
-                                             attributes->matrix)) {
+                                             attributes->matrix,
+                                             is_mirrored_texture))
+            {
                 cogl_object_unref (texture);
                 cairo_surface_destroy (surface);
                 return NULL;
@@ -2132,8 +2419,9 @@ BAIL:
 	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;
+	attributes->s_wrap =
+            get_cogl_wrap_mode_for_extend (pattern->extend, dev);
+	attributes->t_wrap = attributes->s_wrap;
 
         attributes->matrix = pattern->matrix;
 
@@ -2977,7 +3265,7 @@ _cairo_cogl_get_path_stroke_meta (cairo_cogl_surface_t       *surface,
     if (unlikely (status))
 	goto BAIL;
     meta->user_path = meta_path;
-    meta->ctm_inverse = *surface->ctm_inverse;
+    meta->ctm_inverse = surface->ctm_inverse;
 
     status = _cairo_stroke_style_init_copy (&meta->style, style);
     if (unlikely (status)) {
@@ -3080,7 +3368,9 @@ _cairo_cogl_surface_stroke (void                       *abstract_surface,
     if (meta) {
 	prim = meta->prim;
 	if (prim) {
-	    cairo_matrix_multiply (&transform_matrix, &meta->ctm_inverse, surface->ctm);
+	    cairo_matrix_multiply (&transform_matrix,
+                                   &meta->ctm_inverse,
+                                   &surface->ctm);
 	    transform = &transform_matrix;
 	} else if (meta->counter++ > 10) {
 	    one_shot = FALSE;
@@ -3238,7 +3528,7 @@ _cairo_cogl_get_path_fill_meta (cairo_cogl_surface_t *surface)
     if (unlikely (status))
 	goto BAIL;
     meta->user_path = meta_path;
-    meta->ctm_inverse = *surface->ctm_inverse;
+    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
@@ -3295,7 +3585,9 @@ _cairo_cogl_surface_fill (void			    *abstract_surface,
     if (meta) {
 	prim = meta->prim;
 	if (prim) {
-	    cairo_matrix_multiply (&transform_matrix, &meta->ctm_inverse, surface->ctm);
+	    cairo_matrix_multiply (&transform_matrix,
+                                   &meta->ctm_inverse,
+                                   &surface->ctm);
 	    transform = &transform_matrix;
 	} else if (meta->counter++ > 10) {
 	    one_shot = FALSE;
@@ -3490,10 +3782,12 @@ _cairo_cogl_surface_create_full (cairo_cogl_device_t *dev,
 
     surface->ignore_alpha = ignore_alpha;
 
+    surface->is_mirrored_snapshot = FALSE;
+
     surface->framebuffer = framebuffer;
     if (framebuffer) {
-	surface->width = cogl_framebuffer_get_width (framebuffer);
-	surface->height = cogl_framebuffer_get_height (framebuffer);
+        surface->width = cogl_framebuffer_get_width (framebuffer);
+        surface->height = cogl_framebuffer_get_height (framebuffer);
 	cogl_object_ref (framebuffer);
     }
 
@@ -3502,9 +3796,9 @@ _cairo_cogl_surface_create_full (cairo_cogl_device_t *dev,
     surface->texture = texture;
     if (texture) {
 	if (!framebuffer) {
-	    surface->width = cogl_texture_get_width (texture);
-	    surface->height = cogl_texture_get_height (texture);
-	}
+            surface->width = cogl_texture_get_width (texture);
+            surface->height = cogl_texture_get_height (texture);
+        }
 	cogl_object_ref (texture);
     }
 
@@ -3541,7 +3835,10 @@ cairo_cogl_surface_create_for_fb (cairo_device_t  *abstract_device,
     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);
+    return _cairo_cogl_surface_create_full (dev,
+                                            FALSE,
+                                            framebuffer,
+                                            NULL);
 }
 slim_hidden_def (cairo_cogl_surface_create_for_fb);
 
@@ -3552,6 +3849,7 @@ cairo_cogl_onscreen_surface_create (cairo_device_t *abstract_device,
 {
     CoglFramebuffer *fb;
     CoglTextureComponents components;
+    CoglError *error;
     cairo_cogl_device_t *dev = (cairo_cogl_device_t *)abstract_device;
 
     if (abstract_device->backend->type != CAIRO_DEVICE_TYPE_COGL)
@@ -3562,12 +3860,13 @@ cairo_cogl_onscreen_surface_create (cairo_device_t *abstract_device,
 
     fb = cogl_onscreen_new (dev->cogl_context, width, height);
 
-    if (!cogl_framebuffer_allocate (fb, NULL))
+    if (!cogl_framebuffer_allocate (fb, &error)) {
+        g_warning ("Could not allocate framebuffer for onscreen "
+                   "surface: %s", error->message);
+        cogl_error_free (error);
         return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_ERROR);
-    cogl_framebuffer_orthographic (fb, 0, 0,
-                                   cogl_framebuffer_get_width (fb),
-                                   cogl_framebuffer_get_height (fb),
-                                   -1, 100);
+    }
+    cogl_framebuffer_orthographic (fb, 0, 0, width, height, -1, 100);
 
     return cairo_cogl_surface_create_for_fb (abstract_device, fb);
 }
@@ -3581,25 +3880,47 @@ cairo_cogl_offscreen_surface_create (cairo_device_t *abstract_device,
     CoglFramebuffer *fb;
     CoglTexture *tex;
     CoglTextureComponents components;
+    CoglError *error;
+    cairo_surface_t *surface;
     cairo_cogl_device_t *dev = (cairo_cogl_device_t *)abstract_device;
+    int tex_width = width;
+    int tex_height = height;
 
     if (abstract_device->backend->type != CAIRO_DEVICE_TYPE_COGL)
 	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
 
+    /* If we cannot use an NPOT texture, allocate the texture in power
+     * of two dimensions instead */
+    if (!dev->has_npots) {
+        tex_width = (int)pow (2, ceil (log2 (tex_width)));
+        tex_height = (int)pow (2, ceil (log2 (tex_height)));
+    }
+
     components = get_components_from_cairo_content (content);
     tex = cogl_texture_2d_new_with_size (dev->cogl_context,
-                                         width, height);
+                                         tex_width, tex_height);
     cogl_texture_set_components (tex, components);
     fb = cogl_offscreen_new_with_texture (tex);
 
-    if (!cogl_framebuffer_allocate (fb, NULL))
+    if (!cogl_framebuffer_allocate (fb, &error)) {
+        g_warning ("Could not allocate framebuffer for offscreen "
+                   "surface: %s", error->message);
+        cogl_error_free (error);
         return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_ERROR);
+    }
     cogl_framebuffer_orthographic (fb, 0, 0,
-                                   cogl_framebuffer_get_width (fb),
-                                   cogl_framebuffer_get_height (fb),
+                                   tex_width, tex_height,
                                    -1, 100);
 
-    return cairo_cogl_surface_create_for_fb (abstract_device, fb);
+    /* The framebuffer will take a reference on the texture */
+    cogl_object_unref (tex);
+
+    surface = cairo_cogl_surface_create_for_fb (abstract_device, fb);
+
+    ((cairo_cogl_surface_t *)surface)->width = width;
+    ((cairo_cogl_surface_t *)surface)->height = height;
+
+    return surface;
 }
 slim_hidden_def (cairo_cogl_offscreen_surface_create);
 
diff --git a/src/cairo-cogl.h b/src/cairo-cogl.h
index 69ee690db..cb691b39d 100644
--- a/src/cairo-cogl.h
+++ b/src/cairo-cogl.h
@@ -64,6 +64,9 @@ cairo_cogl_surface_create_for_fb (cairo_device_t  *device,
 cairo_public CoglFramebuffer *
 cairo_cogl_surface_get_framebuffer (cairo_surface_t *surface);
 
+/* If NPOT textures are not supported, the contents of interests may
+ * only be in the lowest-coordinate corner of the texture obtained from
+ * this function */
 cairo_public CoglTexture *
 cairo_cogl_surface_get_texture (cairo_surface_t *surface);
 
commit 916408481f3123bc6fc0800c90d910716046ec30
Author: George Matsumura <gmmatsumura01 at bvsd.org>
Date:   Sun Jul 19 15:22:24 2020 -0600

    cogl: Move framebuffer creation functionality out of boilerplate
    
    In order to be more consistent with backends such as gl, support for
    creating a surface from content, width, and height parameters was
    moved into the backend itself. The option to pass cairo-cogl a
    framebuffer to create a texture from still exists, just now it is
    not the only option.
    
    Signed-off-by: George Matsumura <gmmatsumura01 at bvsd.org>

diff --git a/boilerplate/cairo-boilerplate-cogl.c b/boilerplate/cairo-boilerplate-cogl.c
index 364654feb..ae42578fd 100644
--- a/boilerplate/cairo-boilerplate-cogl.c
+++ b/boilerplate/cairo-boilerplate-cogl.c
@@ -31,27 +31,23 @@
  */
 
 #include "cairo-boilerplate-private.h"
+#include "cairo-malloc-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);
 
@@ -68,9 +64,8 @@ _cairo_boilerplate_cogl_create_offscreen_color_surface (const char		*name,
 							cairo_boilerplate_mode_t mode,
 							void		       **abstract_closure)
 {
+    CoglContext *context;
     cairo_device_t *device;
-    CoglTexture *tex;
-    CoglFramebuffer *fb;
     cogl_closure_t *closure;
     cairo_status_t status;
 
@@ -79,25 +74,17 @@ _cairo_boilerplate_cogl_create_offscreen_color_surface (const char		*name,
     if (height < 1)
         height = 1;
 
-    if (!context)
-	context = cogl_context_new (NULL, NULL);
+    context = cogl_context_new (NULL, NULL);
 
     device = cairo_cogl_device_create (context);
-    tex = cogl_texture_2d_new_with_size (context, width, height);
-    cogl_texture_set_components (tex, COGL_TEXTURE_COMPONENTS_RGBA);
-    fb = cogl_offscreen_new_with_texture (tex);
-
-    cogl_framebuffer_allocate (fb, NULL);
-    cogl_framebuffer_orthographic (fb, 0, 0,
-                                   cogl_framebuffer_get_width (fb),
-                                   cogl_framebuffer_get_height (fb),
-                                   -1, 100);
 
-    closure = malloc (sizeof (cogl_closure_t));
+    closure = _cairo_malloc (sizeof (cogl_closure_t));
     *abstract_closure = closure;
     closure->device = device;
-    closure->fb = fb;
-    closure->surface = cairo_cogl_surface_create (device, fb);
+    closure->surface = cairo_cogl_offscreen_surface_create (device,
+                                                            content,
+                                                            width,
+                                                            height);
 
     status = cairo_surface_set_user_data (closure->surface,
 					  &cogl_closure_key, closure, NULL);
@@ -118,8 +105,8 @@ _cairo_boilerplate_cogl_create_onscreen_color_surface (const char	       *name,
 						       cairo_boilerplate_mode_t mode,
 						       void		      **abstract_closure)
 {
+    CoglContext *context;
     cairo_device_t *device;
-    CoglFramebuffer *fb;
     cogl_closure_t *closure;
     cairo_status_t status;
 
@@ -128,24 +115,17 @@ _cairo_boilerplate_cogl_create_onscreen_color_surface (const char	       *name,
     if (height < 1)
         height = 1;
 
-    if (!context)
-	context = cogl_context_new (NULL, NULL);
+    context = cogl_context_new (NULL, NULL);
 
     device = cairo_cogl_device_create (context);
-    fb = cogl_onscreen_new (context, width, height);
-
-    cogl_onscreen_show (fb);
-
-    cogl_framebuffer_orthographic (fb, 0, 0,
-                                   cogl_framebuffer_get_width (fb),
-                                   cogl_framebuffer_get_height (fb),
-                                   -1, 100);
 
-    closure = malloc (sizeof (cogl_closure_t));
+    closure = _cairo_malloc (sizeof (cogl_closure_t));
     *abstract_closure = closure;
     closure->device = device;
-    closure->fb = fb;
-    closure->surface = cairo_cogl_surface_create (device, fb);
+    closure->surface = cairo_cogl_onscreen_surface_create (device,
+                                                           content,
+                                                           width,
+                                                           height);
 
     status = cairo_surface_set_user_data (closure->surface,
 					  &cogl_closure_key, closure, NULL);
@@ -158,22 +138,15 @@ _cairo_boilerplate_cogl_create_onscreen_color_surface (const char	       *name,
 
 static cairo_status_t
 _cairo_boilerplate_cogl_finish (cairo_surface_t *surface)
-{	
-    cogl_closure_t *closure = cairo_surface_get_user_data (surface, &cogl_closure_key);
-
-    cairo_cogl_surface_end_frame (surface);
-
-    if (cogl_is_onscreen (closure->fb))
-        cogl_onscreen_swap_buffers (closure->fb);
-
-    return CAIRO_STATUS_SUCCESS;
+{
+    return cairo_cogl_surface_end_frame (surface);
 }
 
 static void
 _cairo_boilerplate_cogl_synchronize (void *abstract_closure)
 {
     cogl_closure_t *closure = abstract_closure;
-    cogl_framebuffer_finish (closure->fb);
+    cairo_cogl_surface_synchronize (closure->surface);
 }
 
 static const cairo_boilerplate_target_t targets[] = {
diff --git a/src/cairo-cogl-surface.c b/src/cairo-cogl-surface.c
index 49f12f700..e5382d694 100644
--- a/src/cairo-cogl-surface.c
+++ b/src/cairo-cogl-surface.c
@@ -211,10 +211,13 @@ _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_create_for_fb);
+slim_hidden_proto (cairo_cogl_onscreen_surface_create);
+slim_hidden_proto (cairo_cogl_offscreen_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);
+slim_hidden_proto (cairo_cogl_surface_synchronize);
 
 static cairo_cogl_device_t *
 to_device (cairo_device_t *device)
@@ -273,10 +276,11 @@ _cairo_cogl_surface_create_similar (void            *abstract_surface,
     cairo_cogl_surface_t *surface;
     CoglTexture *texture;
     cairo_status_t status;
+    cairo_cogl_device_t *dev =
+        to_device(reference_surface->base.device);
 
-    texture =
-        cogl_texture_2d_new_with_size (to_device(reference_surface->base.device)->cogl_context,
-                                       width, height);
+    texture = cogl_texture_2d_new_with_size (dev->cogl_context,
+                                             width, height);
     if (!texture)
         return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
@@ -286,7 +290,7 @@ _cairo_cogl_surface_create_similar (void            *abstract_surface,
                                  COGL_TEXTURE_COMPONENTS_A);
 
     surface = (cairo_cogl_surface_t *)
-	_cairo_cogl_surface_create_full (to_device(reference_surface->base.device),
+	_cairo_cogl_surface_create_full (dev,
 					 (content & CAIRO_CONTENT_ALPHA) == 0,
 					 NULL,
 					 texture);
@@ -346,20 +350,12 @@ static void
 _cairo_cogl_journal_discard (cairo_cogl_surface_t *surface)
 {
     GList *l;
-    cairo_cogl_device_t *dev;
 
     if (!surface->journal) {
 	assert (surface->last_clip == NULL);
 	return;
     }
 
-    dev = to_device(surface->base.device);
-    if (dev->buffer_stack && dev->buffer_stack_offset) {
-	cogl_buffer_unmap (dev->buffer_stack);
-	cogl_object_unref (dev->buffer_stack);
-	dev->buffer_stack = NULL;
-    }
-
     for (l = surface->journal->head; l; l = l->next) {
 	cairo_cogl_journal_entry_t *entry = l->data;
 	gsize entry_size;
@@ -604,18 +600,19 @@ _cairo_cogl_traps_to_triangles_buffer (cairo_cogl_surface_t *surface,
     int n_traps = traps->num_traps;
     int i;
     CoglVertexP2 *triangles;
+    cairo_cogl_device_t *dev = to_device(surface->base.device);
 
     if (one_shot) {
 	buffer =
-            _cairo_cogl_device_allocate_buffer_space (to_device(surface->base.device),
-                                                       n_traps * sizeof (CoglVertexP2) * 6,
-                                                       offset,
-                                                       (void **)&triangles);
+            _cairo_cogl_device_allocate_buffer_space (dev,
+                                                      n_traps * sizeof (CoglVertexP2) * 6,
+                                                      offset,
+                                                      (void **)&triangles);
 	if (!buffer)
 	    return NULL;
     } else {
 	buffer =
-            cogl_attribute_buffer_new (to_device(surface->base.device)->cogl_context,
+            cogl_attribute_buffer_new (dev->cogl_context,
                                        n_traps * sizeof (CoglVertexP2) * 6,
                                        NULL);
 	if (!buffer)
@@ -1310,6 +1307,23 @@ _cairo_cogl_surface_finish (void *abstract_surface)
     return CAIRO_STATUS_SUCCESS;
 }
 
+static CoglTextureComponents
+get_components_from_cairo_content (cairo_content_t content)
+{
+    switch (content)
+    {
+    case CAIRO_CONTENT_ALPHA:
+       return COGL_TEXTURE_COMPONENTS_A;
+    case CAIRO_CONTENT_COLOR:
+       return COGL_TEXTURE_COMPONENTS_RGB;
+    case CAIRO_CONTENT_COLOR_ALPHA:
+       return COGL_TEXTURE_COMPONENTS_RGBA;
+    default:
+        g_warning ("Unrecognized content type");
+        assert (0);
+    }
+}
+
 static CoglPixelFormat
 get_default_cogl_format_from_components (CoglTextureComponents components)
 {
@@ -1732,10 +1746,9 @@ _cairo_cogl_acquire_surface_texture (cairo_cogl_surface_t        *reference_surf
          * accordingly. */
         *has_pre_transform = TRUE;
 
-        texture =
-            cogl_texture_2d_new_with_size (to_device(reference_surface->base.device)->cogl_context,
-                                           extents->width,
-                                           extents->height);
+        texture = cogl_texture_2d_new_with_size (dev->cogl_context,
+                                                 extents->width,
+                                                 extents->height);
         if (!texture) {
 	    g_warning ("Failed to allocate texture: %s", error->message);
 	    g_error_free (error);
@@ -1743,7 +1756,7 @@ _cairo_cogl_acquire_surface_texture (cairo_cogl_surface_t        *reference_surf
         }
 
         clone =
-            _cairo_cogl_surface_create_full (to_device(reference_surface->base.device),
+            _cairo_cogl_surface_create_full (dev,
                                              reference_surface->ignore_alpha,
                                              NULL,
                                              texture);
@@ -3514,8 +3527,8 @@ _cairo_cogl_surface_create_full (cairo_cogl_device_t *dev,
 }
 
 cairo_surface_t *
-cairo_cogl_surface_create (cairo_device_t  *abstract_device,
-			   CoglFramebuffer *framebuffer)
+cairo_cogl_surface_create_for_fb (cairo_device_t  *abstract_device,
+                                  CoglFramebuffer *framebuffer)
 {
     cairo_cogl_device_t *dev = (cairo_cogl_device_t *)abstract_device;
 
@@ -3530,7 +3543,65 @@ cairo_cogl_surface_create (cairo_device_t  *abstract_device,
 
     return _cairo_cogl_surface_create_full (dev, FALSE, framebuffer, NULL);
 }
-slim_hidden_def (cairo_cogl_surface_create);
+slim_hidden_def (cairo_cogl_surface_create_for_fb);
+
+cairo_surface_t *
+cairo_cogl_onscreen_surface_create (cairo_device_t *abstract_device,
+                                    cairo_content_t content,
+                                    int width, int height)
+{
+    CoglFramebuffer *fb;
+    CoglTextureComponents components;
+    cairo_cogl_device_t *dev = (cairo_cogl_device_t *)abstract_device;
+
+    if (abstract_device->backend->type != CAIRO_DEVICE_TYPE_COGL)
+	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
+
+    /* We don't yet have a way to set the components of a framebuffer */
+    components = get_components_from_cairo_content (content);
+
+    fb = cogl_onscreen_new (dev->cogl_context, width, height);
+
+    if (!cogl_framebuffer_allocate (fb, NULL))
+        return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_ERROR);
+    cogl_framebuffer_orthographic (fb, 0, 0,
+                                   cogl_framebuffer_get_width (fb),
+                                   cogl_framebuffer_get_height (fb),
+                                   -1, 100);
+
+    return cairo_cogl_surface_create_for_fb (abstract_device, fb);
+}
+slim_hidden_def (cairo_cogl_onscreen_surface_create);
+
+cairo_surface_t *
+cairo_cogl_offscreen_surface_create (cairo_device_t *abstract_device,
+                                     cairo_content_t content,
+                                     int width, int height)
+{
+    CoglFramebuffer *fb;
+    CoglTexture *tex;
+    CoglTextureComponents components;
+    cairo_cogl_device_t *dev = (cairo_cogl_device_t *)abstract_device;
+
+    if (abstract_device->backend->type != CAIRO_DEVICE_TYPE_COGL)
+	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH));
+
+    components = get_components_from_cairo_content (content);
+    tex = cogl_texture_2d_new_with_size (dev->cogl_context,
+                                         width, height);
+    cogl_texture_set_components (tex, components);
+    fb = cogl_offscreen_new_with_texture (tex);
+
+    if (!cogl_framebuffer_allocate (fb, NULL))
+        return _cairo_surface_create_in_error (CAIRO_STATUS_DEVICE_ERROR);
+    cogl_framebuffer_orthographic (fb, 0, 0,
+                                   cogl_framebuffer_get_width (fb),
+                                   cogl_framebuffer_get_height (fb),
+                                   -1, 100);
+
+    return cairo_cogl_surface_create_for_fb (abstract_device, fb);
+}
+slim_hidden_def (cairo_cogl_offscreen_surface_create);
 
 CoglFramebuffer *
 cairo_cogl_surface_get_framebuffer (cairo_surface_t *abstract_surface)
@@ -3568,6 +3639,7 @@ static cairo_status_t
 _cairo_cogl_device_flush (void *device)
 {
     cairo_status_t status;
+    cairo_cogl_device_t *dev = device;
 
     status = cairo_device_acquire (device);
     if (unlikely (status))
@@ -3576,6 +3648,12 @@ _cairo_cogl_device_flush (void *device)
     /* XXX: we don't need to flush Cogl here, we just need to flush
      * any batching we do of compositing primitives. */
 
+    if (dev->buffer_stack && dev->buffer_stack_offset) {
+        cogl_buffer_unmap (dev->buffer_stack);
+        cogl_object_unref (dev->buffer_stack);
+        dev->buffer_stack = NULL;
+    }
+
     cairo_device_release (device);
 
     return CAIRO_STATUS_SUCCESS;
@@ -3585,6 +3663,8 @@ static void
 _cairo_cogl_device_finish (void *device)
 {
     cairo_status_t status;
+    cairo_cogl_device_t *dev = device;
+    int i, j;
 
     status = cairo_device_acquire (device);
     if (unlikely (status))
@@ -3592,6 +3672,27 @@ _cairo_cogl_device_finish (void *device)
 
     /* XXX: Drop references to external resources */
 
+    _cairo_cache_fini (&dev->linear_cache);
+    _cairo_cache_fini (&dev->path_fill_staging_cache);
+    _cairo_cache_fini (&dev->path_fill_prim_cache);
+    _cairo_cache_fini (&dev->path_stroke_staging_cache);
+    _cairo_cache_fini (&dev->path_stroke_prim_cache);
+
+    if (dev->buffer_stack && dev->buffer_stack_offset) {
+        cogl_buffer_unmap (dev->buffer_stack);
+        cogl_object_unref (dev->buffer_stack);
+        dev->buffer_stack = NULL;
+    }
+
+    for (i = 0; i < CAIRO_OPERATOR_SATURATE; i++)
+        for (j = 0; j < CAIRO_COGL_TEMPLATE_TYPE_COUNT; j++)
+            if (dev->template_pipelines[i][j] != NULL) {
+                cogl_object_unref (dev->template_pipelines[i][j]);
+                dev->template_pipelines[i][j] = NULL;
+            }
+
+    cogl_object_unref (dev->cogl_context);
+
     cairo_device_release (device);
 }
 
@@ -3600,8 +3701,6 @@ _cairo_cogl_device_destroy (void *device)
 {
     cairo_cogl_device_t *dev = device;
 
-    /* FIXME: Free stuff! */
-
     g_free (dev);
 }
 
@@ -3679,13 +3778,38 @@ cairo_cogl_device_create (CoglContext *cogl_context)
 }
 slim_hidden_def (cairo_cogl_device_create);
 
-void
+cairo_status_t
 cairo_cogl_surface_end_frame (cairo_surface_t *abstract_surface)
 {
     cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)abstract_surface;
+
+    if (abstract_surface->backend != &_cairo_cogl_surface_backend)
+        return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
+
     cairo_surface_flush (abstract_surface);
 
+    if (surface->framebuffer)
+        if (cogl_is_onscreen (surface->framebuffer))
+            cogl_onscreen_swap_buffers (surface->framebuffer);
+
     //g_print ("n_clip_updates_per_frame = %d\n", surface->n_clip_updates_per_frame);
     surface->n_clip_updates_per_frame = 0;
+
+    return CAIRO_STATUS_SUCCESS;
 }
 slim_hidden_def (cairo_cogl_surface_end_frame);
+
+cairo_status_t
+cairo_cogl_surface_synchronize (cairo_surface_t *abstract_surface)
+{
+    cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)abstract_surface;
+
+    if (abstract_surface->backend != &_cairo_cogl_surface_backend)
+        return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
+
+    if (surface->framebuffer)
+        cogl_framebuffer_finish (surface->framebuffer);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+slim_hidden_def (cairo_cogl_surface_synchronize);
diff --git a/src/cairo-cogl.h b/src/cairo-cogl.h
index e525df955..69ee690db 100644
--- a/src/cairo-cogl.h
+++ b/src/cairo-cogl.h
@@ -48,8 +48,18 @@ 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_cogl_onscreen_surface_create (cairo_device_t *device,
+                                    cairo_content_t content,
+                                    int width, int height);
+
+cairo_public cairo_surface_t *
+cairo_cogl_offscreen_surface_create (cairo_device_t *device,
+                                     cairo_content_t content,
+                                     int width, int height);
+
+cairo_public cairo_surface_t *
+cairo_cogl_surface_create_for_fb (cairo_device_t  *device,
+                                  CoglFramebuffer *framebuffer);
 
 cairo_public CoglFramebuffer *
 cairo_cogl_surface_get_framebuffer (cairo_surface_t *surface);
@@ -57,9 +67,12 @@ 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_public cairo_status_t
 cairo_cogl_surface_end_frame (cairo_surface_t *surface);
 
+cairo_public cairo_status_t
+cairo_cogl_surface_synchronize (cairo_surface_t *surface);
+
 CAIRO_END_DECLS
 
 #else  /* CAIRO_HAS_COGL_SURFACE*/
commit 9248ba1419b4164207afb982b4d3f9e43ddffe42
Author: George Matsumura <gmmatsumura01 at bvsd.org>
Date:   Sat Jul 18 19:03:28 2020 -0600

    cogl: Properly support unbounded operators
    
    This makes it so that operators requiring transparent values to be
    used outside of the bounds of the source and/or mask textures will
    trigger a second rendering of transparent pixels.
    
    Signed-off-by: George Matsumura <gmmatsumura01 at bvsd.org>

diff --git a/src/cairo-cogl-context.c b/src/cairo-cogl-context.c
index 56dbcf31b..a84a9f7d7 100644
--- a/src/cairo-cogl-context.c
+++ b/src/cairo-cogl-context.c
@@ -72,7 +72,7 @@ _cairo_cogl_context_reset_static_data (void)
 
 static cairo_status_t
 _cairo_cogl_context_rectangle_real (cairo_cogl_context_t *cr,
-				    double x, double y,
+				    double x,     double y,
 				    double width, double height)
 {
     cairo_status_t status;
@@ -174,7 +174,8 @@ _cairo_cogl_context_rotate (void *abstract_cr, double theta)
 }
 
 static cairo_status_t
-_cairo_cogl_context_transform (void *abstract_cr, const cairo_matrix_t *matrix)
+_cairo_cogl_context_transform (void                 *abstract_cr,
+                               const cairo_matrix_t *matrix)
 {
     cairo_cogl_context_t *cr = abstract_cr;
 
@@ -189,7 +190,8 @@ _cairo_cogl_context_transform (void *abstract_cr, const cairo_matrix_t *matrix)
 }
 
 static cairo_status_t
-_cairo_cogl_context_set_matrix (void *abstract_cr, const cairo_matrix_t *matrix)
+_cairo_cogl_context_set_matrix (void                 *abstract_cr,
+                                const cairo_matrix_t *matrix)
 {
     cairo_cogl_context_t *cr = abstract_cr;
 
@@ -313,9 +315,9 @@ _cairo_cogl_context_line_to (void *abstract_cr, double x, double y)
 
 static cairo_status_t
 _cairo_cogl_context_curve_to (void *abstract_cr,
-			       double x1, double y1,
-			       double x2, double y2,
-			       double x3, double y3)
+                              double x1, double y1,
+                              double x2, double y2,
+                              double x3, double y3)
 {
     cairo_cogl_context_t *cr = abstract_cr;
     cairo_status_t status;
@@ -352,10 +354,13 @@ _cairo_cogl_context_curve_to (void *abstract_cr,
 }
 
 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_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;
@@ -471,9 +476,9 @@ _cairo_cogl_context_rel_line_to (void *abstract_cr, double dx, double dy)
 
 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)
+                                  double dx1, double dy1,
+                                  double dx2, double dy2,
+                                  double dx3, double dy3)
 {
     cairo_cogl_context_t *cr = abstract_cr;
     cairo_status_t status;
@@ -577,8 +582,8 @@ _cairo_cogl_context_close_path (void *abstract_cr)
 
 static cairo_status_t
 _cairo_cogl_context_rectangle (void *abstract_cr,
-			       double x, double y,
-			       double width, double height)
+                               double x,     double y,
+                               double width, double height)
 {
     cairo_cogl_context_t *cr = abstract_cr;
 
@@ -636,7 +641,7 @@ _cairo_cogl_context_has_current_point (void *abstract_cr)
 }
 
 static cairo_bool_t
-_cairo_cogl_context_get_current_point (void *abstract_cr,
+_cairo_cogl_context_get_current_point (void   *abstract_cr,
                                        double *x,
                                        double *y)
 {
@@ -671,7 +676,7 @@ _cairo_cogl_context_copy_path_flat (void *abstract_cr)
 }
 
 static cairo_status_t
-_cairo_cogl_context_append_path (void *abstract_cr,
+_cairo_cogl_context_append_path (void               *abstract_cr,
                                  const cairo_path_t *path)
 {
     cairo_cogl_context_t *cr = abstract_cr;
@@ -719,18 +724,15 @@ _cairo_cogl_surface_set_side_band_state (cairo_cogl_surface_t *surface,
 static cairo_cogl_surface_t *
 _cairo_cogl_get_cogl_surface (cairo_surface_t *target)
 {
-    /* Collapse all nested subsurfaces. If another method of target
-     * surface redirection is added to cairo, we can add a test here. */
-    while (1) {
-        if (_cairo_surface_is_subsurface (target)) {
-            target = _cairo_surface_subsurface_get_target (target);
-        } else if (target->type == CAIRO_SURFACE_TYPE_COGL) {
-            return (cairo_cogl_surface_t *)target;
-        } else {
-            /* return NULL if the target is not a cogl surface */
-            return NULL;
-        }
-    }
+    /* Subsurfaces are always 1-depth */
+    if (_cairo_surface_is_subsurface (target))
+        target = _cairo_surface_subsurface_get_target (target);
+
+    if (target->type == CAIRO_SURFACE_TYPE_COGL)
+        return (cairo_cogl_surface_t *)target;
+    else
+        /* return NULL if the target is not a cogl surface */
+        return NULL;
 }
 
 static cairo_status_t
@@ -743,6 +745,19 @@ _cairo_cogl_context_fill (void *abstract_cr)
 
     if (cr->path_is_rectangle) {
         if (surface) {
+            cairo_matrix_t ctm;
+
+            status =
+                _cairo_surface_begin_modification (cr->base.gstate->target);
+            if (status)
+                return status;
+
+            ctm = cr->base.gstate->ctm;
+            if (_cairo_surface_is_subsurface (cr->base.gstate->target))
+                cairo_matrix_translate (&ctm,
+                                        ((cairo_surface_subsurface_t *)cr->base.gstate->target)->extents.x,
+                                        ((cairo_surface_subsurface_t *)cr->base.gstate->target)->extents.y);
+
             status = _cairo_cogl_surface_fill_rectangle ((cairo_surface_t *)surface,
                                                          cr->base.gstate->op,
                                                          cr->base.gstate->source,
@@ -750,7 +765,7 @@ _cairo_cogl_context_fill (void *abstract_cr)
                                                          cr->y,
                                                          cr->width,
                                                          cr->height,
-                                                         &cr->base.gstate->ctm,
+                                                         &ctm,
                                                          cr->base.gstate->clip);
             if (status == CAIRO_STATUS_SUCCESS)
                 goto DONE;
@@ -786,6 +801,19 @@ _cairo_cogl_context_fill_preserve (void *abstract_cr)
 
     if (cr->path_is_rectangle) {
         if (surface) {
+            cairo_matrix_t ctm;
+
+            status =
+                _cairo_surface_begin_modification (cr->base.gstate->target);
+            if (status)
+                return status;
+
+            ctm = cr->base.gstate->ctm;
+            if (_cairo_surface_is_subsurface (cr->base.gstate->target))
+                cairo_matrix_translate (&ctm,
+                                        ((cairo_surface_subsurface_t *)cr->base.gstate->target)->extents.x,
+                                        ((cairo_surface_subsurface_t *)cr->base.gstate->target)->extents.y);
+
             status = _cairo_cogl_surface_fill_rectangle ((cairo_surface_t *)surface,
                                                          cr->base.gstate->op,
                                                          cr->base.gstate->source,
@@ -793,7 +821,7 @@ _cairo_cogl_context_fill_preserve (void *abstract_cr)
                                                          cr->y,
                                                          cr->width,
                                                          cr->height,
-                                                         &cr->base.gstate->ctm,
+                                                         &ctm,
                                                          cr->base.gstate->clip);
             if (status == CAIRO_STATUS_SUCCESS)
                 goto DONE;
diff --git a/src/cairo-cogl-gradient-private.h b/src/cairo-cogl-gradient-private.h
index fa684d2c6..cc9771179 100644
--- a/src/cairo-cogl-gradient-private.h
+++ b/src/cairo-cogl-gradient-private.h
@@ -67,15 +67,15 @@ typedef struct _cairo_cogl_linear_gradient {
 } 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_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_extend_t                extend_mode);
 
 cairo_cogl_linear_gradient_t *
 _cairo_cogl_linear_gradient_reference (cairo_cogl_linear_gradient_t *gradient);
diff --git a/src/cairo-cogl-gradient.c b/src/cairo-cogl-gradient.c
index e401aeea6..9d190612c 100644
--- a/src/cairo-cogl-gradient.c
+++ b/src/cairo-cogl-gradient.c
@@ -48,7 +48,7 @@ _cairo_cogl_linear_gradient_hash (unsigned int                  n_stops,
 }
 
 static cairo_cogl_linear_gradient_t *
-_cairo_cogl_linear_gradient_lookup (cairo_cogl_device_t         *ctx,
+_cairo_cogl_linear_gradient_lookup (cairo_cogl_device_t          *ctx,
 				    unsigned long                 hash,
 				    unsigned int                  n_stops,
 				    const cairo_gradient_stop_t  *stops)
@@ -116,7 +116,8 @@ _cairo_cogl_util_next_p2 (int a)
 }
 
 static float
-get_max_color_component_range (const cairo_color_stop_t *color0, const cairo_color_stop_t *color1)
+get_max_color_component_range (const cairo_color_stop_t *color0,
+                               const cairo_color_stop_t *color1)
 {
     float range;
     float max = 0;
@@ -198,7 +199,7 @@ _cairo_cogl_linear_gradient_width_for_stops (cairo_extend_t		  extend,
 /* Aim to create gradient textures without an alpha component so we can avoid
  * needing to use blending... */
 static CoglTextureComponents
-_cairo_cogl_linear_gradient_components_for_stops (cairo_extend_t		   extend,
+_cairo_cogl_linear_gradient_components_for_stops (cairo_extend_t               extend,
 					          unsigned int                 n_stops,
 					          const cairo_gradient_stop_t *stops)
 {
@@ -238,7 +239,7 @@ _cairo_cogl_compatibility_from_extend_mode (cairo_extend_t extend_mode)
 
 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_extend_t                extend_mode)
 {
     GList *l;
     cairo_cogl_gradient_compatibility_t compatibility =
@@ -351,10 +352,10 @@ dump_gradient_to_png (CoglTexture *texture)
 #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_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;
@@ -411,8 +412,9 @@ _cairo_cogl_get_linear_gradient (cairo_cogl_device_t *device,
 	gradient->n_stops = n_stops;
 	gradient->stops = gradient->stops_embedded;
 	memcpy (gradient->stops_embedded, stops, sizeof (cairo_gradient_stop_t) * n_stops);
-    } else
+    } else {
 	_cairo_cogl_linear_gradient_reference (gradient);
+    }
 
     entry = _cairo_malloc (sizeof (cairo_cogl_linear_texture_entry_t));
     if (!entry) {
diff --git a/src/cairo-cogl-private.h b/src/cairo-cogl-private.h
index 5f3754558..edd7b269c 100644
--- a/src/cairo-cogl-private.h
+++ b/src/cairo-cogl-private.h
@@ -61,7 +61,7 @@ typedef struct _cairo_cogl_device {
 
     CoglContext *cogl_context;
 
-    CoglTexture *dummy_texture;
+    cairo_bool_t has_npots, has_mirrored_repeat;
 
     CoglAttributeBuffer *buffer_stack;
     size_t buffer_stack_size;
@@ -83,8 +83,6 @@ typedef struct _cairo_cogl_device {
      */
     CoglPipeline *template_pipelines[CAIRO_OPERATOR_ADD + 1][CAIRO_COGL_TEMPLATE_TYPE_COUNT];
 
-    CoglMatrix identity;
-
     /* Caches 1d linear gradient textures */
     cairo_cache_t linear_cache;
 
diff --git a/src/cairo-cogl-surface.c b/src/cairo-cogl-surface.c
index 8207532c4..49f12f700 100644
--- a/src/cairo-cogl-surface.c
+++ b/src/cairo-cogl-surface.c
@@ -92,6 +92,24 @@ typedef struct _cairo_cogl_texture_attributes {
     CoglPipelineWrapMode    t_wrap;
 } cairo_cogl_texture_attributes_t;
 
+typedef struct _cairo_cogl_pipeline {
+    int n_layers;
+    cairo_operator_t op;
+
+    /* These are not always the same as the operator's, as sometimes
+     * this is a pre-render for a different operator */
+    cairo_bool_t src_bounded;
+    cairo_bool_t mask_bounded;
+
+    cairo_bool_t has_src_tex_clip;
+    cairo_path_fixed_t src_tex_clip;
+    cairo_bool_t has_mask_tex_clip;
+    cairo_path_fixed_t mask_tex_clip;
+    cairo_rectangle_int_t unbounded_extents;
+
+    CoglPipeline *pipeline;
+} cairo_cogl_pipeline_t;
+
 typedef enum _cairo_cogl_journal_entry_type {
     CAIRO_COGL_JOURNAL_ENTRY_TYPE_RECTANGLE,
     CAIRO_COGL_JOURNAL_ENTRY_TYPE_PRIMITIVE,
@@ -110,39 +128,29 @@ typedef struct _cairo_cogl_journal_clip_entry {
 
 typedef struct _cairo_cogl_journal_rect_entry {
     cairo_cogl_journal_entry_t base;
-    CoglPipeline *pipeline;
+    cairo_cogl_pipeline_t *pipeline;
+
     float x;
     float y;
     float width;
     float height;
-    int n_layers;
     cairo_matrix_t ctm;
-    cairo_path_fixed_t *src_tex_clip;
-    cairo_path_fixed_t *mask_tex_clip;
-    cairo_operator_t op;
-    cairo_rectangle_int_t unbounded_extents;
 } cairo_cogl_journal_rect_entry_t;
 
 typedef struct _cairo_cogl_journal_prim_entry {
     cairo_cogl_journal_entry_t base;
-    CoglPipeline *pipeline;
+    cairo_cogl_pipeline_t *pipeline;
+
     CoglPrimitive *primitive;
-    gboolean has_transform;
+    cairo_bool_t has_transform;
     cairo_matrix_t transform;
-    cairo_path_fixed_t *src_tex_clip;
-    cairo_path_fixed_t *mask_tex_clip;
-    cairo_operator_t op;
-    cairo_rectangle_int_t unbounded_extents;
 } cairo_cogl_journal_prim_entry_t;
 
 typedef struct _cairo_cogl_journal_path_entry {
     cairo_cogl_journal_entry_t base;
-    CoglPipeline *pipeline;
+    cairo_cogl_pipeline_t *pipeline;
+
     CoglPath *path;
-    cairo_path_fixed_t *src_tex_clip;
-    cairo_path_fixed_t *mask_tex_clip;
-    cairo_operator_t op;
-    cairo_rectangle_int_t unbounded_extents;
 } cairo_cogl_journal_path_entry_t;
 
 typedef struct _cairo_cogl_path_fill_meta {
@@ -193,19 +201,9 @@ typedef struct _cairo_cogl_path_stroke_meta {
 
 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);
+				 cairo_bool_t         ignore_alpha,
+				 CoglFramebuffer     *framebuffer,
+				 CoglTexture         *texture);
 
 static void
 _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface);
@@ -305,7 +303,7 @@ _cairo_cogl_surface_create_similar (void            *abstract_surface,
 }
 
 static cairo_bool_t
-_cairo_cogl_surface_get_extents (void *abstract_surface,
+_cairo_cogl_surface_get_extents (void                  *abstract_surface,
                                  cairo_rectangle_int_t *extents)
 {
     cairo_cogl_surface_t *surface = abstract_surface;
@@ -378,36 +376,39 @@ _cairo_cogl_journal_discard (cairo_cogl_surface_t *surface)
 	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);
-            if (rect_entry->src_tex_clip)
-                _cairo_path_fixed_destroy (rect_entry->src_tex_clip);
-            if (rect_entry->mask_tex_clip)
-                _cairo_path_fixed_destroy (rect_entry->mask_tex_clip);
+	    cogl_object_unref (rect_entry->pipeline->pipeline);
+            if (rect_entry->pipeline->has_src_tex_clip)
+                _cairo_path_fixed_fini (&rect_entry->pipeline->src_tex_clip);
+            if (rect_entry->pipeline->has_mask_tex_clip)
+                _cairo_path_fixed_fini (&rect_entry->pipeline->mask_tex_clip);
+            g_free (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->pipeline->pipeline);
             if (prim_entry->primitive)
 	        cogl_object_unref (prim_entry->primitive);
-            if (prim_entry->src_tex_clip)
-                _cairo_path_fixed_destroy (prim_entry->src_tex_clip);
-            if (prim_entry->mask_tex_clip)
-                _cairo_path_fixed_destroy (prim_entry->mask_tex_clip);
+            if (prim_entry->pipeline->has_src_tex_clip)
+                _cairo_path_fixed_fini (&prim_entry->pipeline->src_tex_clip);
+            if (prim_entry->pipeline->has_mask_tex_clip)
+                _cairo_path_fixed_fini (&prim_entry->pipeline->mask_tex_clip);
+            g_free (prim_entry->pipeline);
 	    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->pipeline->pipeline);
 	    cogl_object_unref (path_entry->path);
-            if (path_entry->src_tex_clip)
-                _cairo_path_fixed_destroy (path_entry->src_tex_clip);
-            if (path_entry->mask_tex_clip)
-                _cairo_path_fixed_destroy (path_entry->mask_tex_clip);
+            if (path_entry->pipeline->has_src_tex_clip)
+                _cairo_path_fixed_fini (&path_entry->pipeline->src_tex_clip);
+            if (path_entry->pipeline->has_mask_tex_clip)
+                _cairo_path_fixed_fini (&path_entry->pipeline->mask_tex_clip);
+            g_free (path_entry->pipeline);
 	    entry_size = sizeof (cairo_cogl_journal_path_entry_t);
 	    break;
 	}
@@ -436,13 +437,9 @@ _cairo_cogl_journal_free (cairo_cogl_surface_t *surface)
 
 #ifdef FILL_WITH_COGL_PATH
 static void
-_cairo_cogl_journal_log_path (cairo_cogl_surface_t *surface,
-			      CoglPipeline *pipeline,
-			      CoglPath *path,
-                              cairo_path_fixed_t *src_tex_clip,
-                              cairo_path_fixed_t *mask_tex_clip,
-                              cairo_operator_t op
-                              cairo_rectangle_int_t unbounded_extents)
+_cairo_cogl_journal_log_path (cairo_cogl_surface_t  *surface,
+			      cairo_cogl_pipeline_t *pipeline,
+			      CoglPath              *path)
 {
     cairo_cogl_journal_path_entry_t *entry;
 
@@ -455,12 +452,8 @@ _cairo_cogl_journal_log_path (cairo_cogl_surface_t *surface,
     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->src_tex_clip = src_tex_clip;
-    entry->mask_tex_clip = mask_tex_clip;
-    entry->op = op;
+    entry->pipeline = pipeline;
     entry->path = cogl_object_ref (path);
-    entry->unbounded_extents = unbounded_extents;
 
     g_queue_push_tail (surface->journal, entry);
 
@@ -471,14 +464,10 @@ _cairo_cogl_journal_log_path (cairo_cogl_surface_t *surface,
 #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_path_fixed_t *src_tex_clip,
-                                   cairo_path_fixed_t *mask_tex_clip,
-                                   cairo_operator_t op,
-                                   cairo_rectangle_int_t unbounded_extents)
+_cairo_cogl_journal_log_primitive (cairo_cogl_surface_t  *surface,
+				   cairo_cogl_pipeline_t *pipeline,
+				   CoglPrimitive         *primitive,
+				   cairo_matrix_t        *transform)
 {
     cairo_cogl_journal_prim_entry_t *entry;
 
@@ -491,21 +480,18 @@ _cairo_cogl_journal_log_primitive (cairo_cogl_surface_t *surface,
     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);
-    entry->src_tex_clip = src_tex_clip;
-    entry->mask_tex_clip = mask_tex_clip;
-    entry->op = op;
-    entry->unbounded_extents = unbounded_extents;
+    entry->pipeline = pipeline;
+
+    entry->primitive = primitive;
+    if (primitive)
+        cogl_object_ref (primitive);
 
     if (transform) {
 	entry->transform = *transform;
 	entry->has_transform = TRUE;
-    } else
+    } else {
 	entry->has_transform = FALSE;
-
-    entry->primitive = primitive;
-    if (primitive)
-        cogl_object_ref (primitive);
+    }
 
     g_queue_push_tail (surface->journal, entry);
 
@@ -515,18 +501,13 @@ _cairo_cogl_journal_log_primitive (cairo_cogl_surface_t *surface,
 }
 
 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_path_fixed_t *src_tex_clip,
-                                   cairo_path_fixed_t *mask_tex_clip,
-                                   cairo_operator_t op,
-                                   cairo_rectangle_int_t unbounded_extents)
+_cairo_cogl_journal_log_rectangle (cairo_cogl_surface_t  *surface,
+				   cairo_cogl_pipeline_t *pipeline,
+				   float                  x,
+				   float                  y,
+				   float                  width,
+				   float                  height,
+				   cairo_matrix_t        *ctm)
 {
     cairo_cogl_journal_rect_entry_t *entry;
 
@@ -539,19 +520,13 @@ _cairo_cogl_journal_log_rectangle (cairo_cogl_surface_t *surface,
     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->pipeline = pipeline;
 
     entry->x = x;
     entry->y = y;
     entry->width = width;
     entry->height = height;
     entry->ctm = *ctm;
-    entry->src_tex_clip = src_tex_clip;
-    entry->mask_tex_clip = mask_tex_clip;
-    entry->op = op;
-    entry->unbounded_extents = unbounded_extents;
-
-    entry->n_layers = n_layers;
 
     g_queue_push_tail (surface->journal, entry);
 
@@ -562,7 +537,7 @@ _cairo_cogl_journal_log_rectangle (cairo_cogl_surface_t *surface,
 
 static void
 _cairo_cogl_journal_log_clip (cairo_cogl_surface_t *surface,
-			      const cairo_clip_t *clip)
+			      const cairo_clip_t   *clip)
 {
     cairo_cogl_journal_clip_entry_t *entry;
 
@@ -581,9 +556,9 @@ _cairo_cogl_journal_log_clip (cairo_cogl_surface_t *surface,
 
 static CoglAttributeBuffer *
 _cairo_cogl_device_allocate_buffer_space (cairo_cogl_device_t *dev,
-                                          size_t size,
-                                          size_t *offset,
-                                          void **pointer)
+                                          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 throw away our stack
@@ -621,9 +596,9 @@ _cairo_cogl_device_allocate_buffer_space (cairo_cogl_device_t *dev,
 
 static CoglAttributeBuffer *
 _cairo_cogl_traps_to_triangles_buffer (cairo_cogl_surface_t *surface,
-				       cairo_traps_t *traps,
-				       size_t *offset,
-				       gboolean one_shot)
+				       cairo_traps_t        *traps,
+				       size_t               *offset,
+				       cairo_bool_t          one_shot)
 {
     CoglAttributeBuffer *buffer;
     int n_traps = traps->num_traps;
@@ -690,79 +665,19 @@ _cairo_cogl_traps_to_triangles_buffer (cairo_cogl_surface_t *surface,
     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),
-						    offset,
-						    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)
+				     cairo_traps_t        *traps,
+				     cairo_bool_t          one_shot)
 {
     int n_traps = traps->num_traps;
+    size_t offset;
+    CoglAttributeBuffer *buffer;
+    CoglPrimitive *prim;
+    CoglAttribute *attributes[3];
+    const char *attrib_names[3] = {"cogl_position_in",
+                                   "cogl_tex_coord0_in",
+                                   "cogl_tex_coord1_in"};
     int i;
 
     /* XXX: Ideally we would skip tessellating to traps entirely since
@@ -775,12 +690,39 @@ _cairo_cogl_traps_to_composite_prim (cairo_cogl_surface_t *surface,
     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);
+    buffer = _cairo_cogl_traps_to_triangles_buffer (surface,
+                                                    traps,
+                                                    &offset,
+                                                    one_shot);
+
+    /* We create the largest used number of texture coordinate
+     * attributes here to ensure that when cached, they can be reused
+     * with any pipeline that we may associate with a given path. If
+     * the corresponding layer for a texture coordinates attribute
+     * doesn't exist, cogl simply ignores it. Since all of the
+     * attributes are aliasing the same attribute buffer, the overhead
+     * of specifying them is minimal. */
+    for (i = 0; i < 3; i++) {
+        attributes[i] = cogl_attribute_new (buffer,
+                                            attrib_names[i],
+                                            sizeof (CoglVertexP2),
+                                            offset,
+                                            2,
+                                            COGL_ATTRIBUTE_TYPE_FLOAT);
     }
+
+    /* The attributes will have taken references on the buffer */
+    cogl_object_unref (buffer);
+
+    prim =
+        cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_TRIANGLES,
+			                    n_traps * 6, attributes, 3);
+
+    /* The primitive will now keep the attributes alive... */
+    for (i = 0; i < 3; i++)
+        cogl_object_unref (attributes[i]);
+
+    return prim;
 }
 
 static cairo_int_status_t
@@ -788,7 +730,6 @@ _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)
@@ -808,7 +749,7 @@ _cairo_cogl_fill_to_primitive (cairo_cogl_surface_t	*surface,
 
     *size = traps.num_traps * sizeof (CoglVertexP2) * 6;
 
-    *primitive = _cairo_cogl_traps_to_composite_prim (surface, &traps, n_layers, one_shot);
+    *primitive = _cairo_cogl_traps_to_composite_prim (surface, &traps, one_shot);
     if (!*primitive) {
 	status = CAIRO_INT_STATUS_NO_MEMORY;
 	goto BAIL;
@@ -821,10 +762,10 @@ BAIL:
 
 static void
 _cairo_cogl_set_path_prim_clip (cairo_cogl_surface_t *surface,
-                                cairo_path_fixed_t *path,
-                                int *clip_stack_depth,
-                                cairo_fill_rule_t fill_rule,
-                                double tolerance)
+                                cairo_path_fixed_t   *path,
+                                int                  *clip_stack_depth,
+                                cairo_fill_rule_t     fill_rule,
+                                double                tolerance)
 {
     cairo_rectangle_int_t extents;
     cairo_int_status_t status;
@@ -848,7 +789,6 @@ _cairo_cogl_set_path_prim_clip (cairo_cogl_surface_t *surface,
                                             path,
                                             fill_rule,
                                             tolerance,
-                                            0,
                                             FALSE,
                                             &prim,
                                             &prim_size);
@@ -897,24 +837,20 @@ BAIL:
  * instead of just clipping it in, we may be able to use a more
  * efficient method using the stencil buffer. */
 static void
-_cairo_cogl_apply_tex_clips (cairo_cogl_surface_t *surface,
-                             int *clip_stack_depth,
-                             CoglPipeline *pipeline,
-                             cairo_path_fixed_t *src_tex_clip,
-                             cairo_path_fixed_t *mask_tex_clip,
-                             cairo_operator_t op)
+_cairo_cogl_apply_tex_clips (cairo_cogl_surface_t  *surface,
+                             int                   *clip_stack_depth,
+                             cairo_cogl_pipeline_t *pipeline)
 {
     CoglDepthState depth_state;
     CoglBool cogl_status;
-    uint32_t op_bounded = _cairo_operator_bounded_by_either (op);
 
     /* Enable the depth test if it will be needed */
-    if ((_cairo_operator_bounded_by_mask(op) == FALSE && mask_tex_clip)
-        || (_cairo_operator_bounded_by_source(op) == FALSE && src_tex_clip))
+    if ((!pipeline->mask_bounded && pipeline->has_mask_tex_clip) ||
+        (!pipeline->src_bounded && pipeline->has_src_tex_clip))
     {
         cogl_depth_state_init (&depth_state);
         cogl_depth_state_set_test_enabled (&depth_state, TRUE);
-        cogl_status = cogl_pipeline_set_depth_state (pipeline,
+        cogl_status = cogl_pipeline_set_depth_state (pipeline->pipeline,
                                                      &depth_state,
                                                      NULL);
         if (cogl_status != TRUE)
@@ -927,51 +863,43 @@ _cairo_cogl_apply_tex_clips (cairo_cogl_surface_t *surface,
                                   0.0, 0.0, 0.0, 0.0);
     }
 
-    switch (op_bounded) {
-    case CAIRO_OPERATOR_BOUND_BY_MASK | CAIRO_OPERATOR_BOUND_BY_SOURCE:
-    case CAIRO_OPERATOR_BOUND_BY_SOURCE:
-    case 0:
-        if (src_tex_clip)
+    if (pipeline->mask_bounded && !pipeline->src_bounded) {
+        /* Push mask clip first so later we can pop the source clip
+         * and still be bound by the mask clip */
+        if (pipeline->has_mask_tex_clip)
             _cairo_cogl_set_path_prim_clip (surface,
-                                            src_tex_clip,
+                                            &pipeline->mask_tex_clip,
                                             clip_stack_depth,
                                             CAIRO_FILL_RULE_WINDING,
                                             0.0);
-        if (mask_tex_clip)
+        if (pipeline->has_src_tex_clip)
             _cairo_cogl_set_path_prim_clip (surface,
-                                            mask_tex_clip,
+                                            &pipeline->src_tex_clip,
                                             clip_stack_depth,
                                             CAIRO_FILL_RULE_WINDING,
                                             0.0);
-        break;
-    case CAIRO_OPERATOR_BOUND_BY_MASK:
-        /* Push mask clip first so later we can pop the source clip 
-         * and still be bound by the mask clip */
-        if (mask_tex_clip)
+    } else {
+        if (pipeline->has_src_tex_clip)
             _cairo_cogl_set_path_prim_clip (surface,
-                                            mask_tex_clip,
+                                            &pipeline->src_tex_clip,
                                             clip_stack_depth,
                                             CAIRO_FILL_RULE_WINDING,
                                             0.0);
-        if (src_tex_clip)
+        if (pipeline->has_mask_tex_clip)
             _cairo_cogl_set_path_prim_clip (surface,
-                                            src_tex_clip,
+                                            &pipeline->mask_tex_clip,
                                             clip_stack_depth,
                                             CAIRO_FILL_RULE_WINDING,
                                             0.0);
-        break;
-    default:
-        assert (0); // not reached
     }
 }
 
 /* Get the pipeline for the second pass */
 static CoglPipeline *
 _cairo_cogl_setup_unbounded_area_pipeline (cairo_cogl_surface_t *surface,
-                                           cairo_operator_t op)
+                                           cairo_operator_t      op)
 {
     CoglPipeline *unbounded_pipeline;
-    CoglColor zero_color;
     CoglDepthState depth_state;
     CoglBool cogl_status;
     cairo_cogl_device_t *dev = to_device(surface->base.device);
@@ -980,8 +908,7 @@ _cairo_cogl_setup_unbounded_area_pipeline (cairo_cogl_surface_t *surface,
      * corresponding solid template pipeline always exists */
     unbounded_pipeline =
         cogl_pipeline_copy (dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID]);
-    cogl_color_init_from_4f (&zero_color, 0.0, 0.0, 0.0, 0.0);
-    cogl_pipeline_set_color (unbounded_pipeline, &zero_color);
+    cogl_pipeline_set_color4f (unbounded_pipeline, 0.0, 0.0, 0.0, 0.0);
 
     /* Enable depth test on second-pass pipeline */
     cogl_depth_state_init (&depth_state);
@@ -996,17 +923,11 @@ _cairo_cogl_setup_unbounded_area_pipeline (cairo_cogl_surface_t *surface,
 }
 
 static void
-_cairo_cogl_unbounded_render (cairo_cogl_surface_t *surface,
-                              int *clip_stack_depth,
-                              CoglPipeline *pipeline,
-                              cairo_path_fixed_t *src_tex_clip,
-                              cairo_path_fixed_t *mask_tex_clip,
-                              cairo_operator_t op,
-                              cairo_rectangle_int_t unbounded_extents,
-                              cairo_bool_t *needs_vertex_render)
+_cairo_cogl_unbounded_render (cairo_cogl_surface_t  *surface,
+                              int                   *clip_stack_depth,
+                              cairo_cogl_pipeline_t *pipeline,
+                              cairo_bool_t          *needs_vertex_render)
 {
-    uint32_t op_bounded = _cairo_operator_bounded_by_either (op);
-
     /* We will need a second rendering of the original vertices if we
      * still need to be bounded by the mask but had a source tex clip */
     *needs_vertex_render = FALSE;
@@ -1014,75 +935,65 @@ _cairo_cogl_unbounded_render (cairo_cogl_surface_t *surface,
     /* Pop all unbounded tex clips. Do not pop clips the operator is
      * bounded by, so that we can still be bounded by them during the
      * second pass (vertex render or extents render). */
-    switch (op_bounded) {
-    case CAIRO_OPERATOR_BOUND_BY_MASK | CAIRO_OPERATOR_BOUND_BY_SOURCE:
+    if (pipeline->mask_bounded && pipeline->src_bounded) {
         /* We don't need a second pass if it will just be in the same
          * region as the first */
-        break;
-    case CAIRO_OPERATOR_BOUND_BY_SOURCE:
-        if (mask_tex_clip) {
+    } else if (pipeline->src_bounded) {
+        if (pipeline->has_mask_tex_clip) {
             cogl_framebuffer_pop_clip (surface->framebuffer);
             (*clip_stack_depth)--;
         }
-        break;
-    case 0:
-        if (src_tex_clip) {
+    } else if (pipeline->mask_bounded) {
+        if (pipeline->has_src_tex_clip) {
             cogl_framebuffer_pop_clip (surface->framebuffer);
             (*clip_stack_depth)--;
+            *needs_vertex_render = TRUE;
         }
-        if (mask_tex_clip) {
+    } else {
+        if (pipeline->has_src_tex_clip) {
             cogl_framebuffer_pop_clip (surface->framebuffer);
             (*clip_stack_depth)--;
         }
-        break;
-    case CAIRO_OPERATOR_BOUND_BY_MASK:
-        if (src_tex_clip) {
+        if (pipeline->has_mask_tex_clip) {
             cogl_framebuffer_pop_clip (surface->framebuffer);
             (*clip_stack_depth)--;
-            *needs_vertex_render = TRUE;
         }
-        break;
-    default:
-        assert (0); // not reached
     }
 
     /* If an operator is unbounded by the mask, we need to render the
      * second transparent pass within the full unbounded extents */
-    if (!(op_bounded & CAIRO_OPERATOR_BOUND_BY_MASK)) {
+    if (!pipeline->mask_bounded) {
         CoglPipeline *unbounded_pipeline;
 
         /* Draw a transparent rectangle to cover the entire extents */
         unbounded_pipeline =
-            _cairo_cogl_setup_unbounded_area_pipeline (surface, op);
+            _cairo_cogl_setup_unbounded_area_pipeline (surface,
+                                                       pipeline->op);
         cogl_framebuffer_draw_rectangle (surface->framebuffer,
                                          unbounded_pipeline,
-                                         unbounded_extents.x,
-                                         unbounded_extents.y,
-                                         unbounded_extents.x + unbounded_extents.width,
-                                         unbounded_extents.y + unbounded_extents.height);
+                                         pipeline->unbounded_extents.x,
+                                         pipeline->unbounded_extents.y,
+                                         pipeline->unbounded_extents.x + pipeline->unbounded_extents.width,
+                                         pipeline->unbounded_extents.y + pipeline->unbounded_extents.height);
         cogl_object_unref (unbounded_pipeline);
     }
 }
 
 static void
-_cairo_cogl_post_unbounded_render (cairo_cogl_surface_t *surface,
-                                   int *clip_stack_depth,
-                                   CoglPipeline *pipeline,
-                                   cairo_path_fixed_t *src_tex_clip,
-                                   cairo_path_fixed_t *mask_tex_clip,
-                                   cairo_operator_t op)
+_cairo_cogl_post_unbounded_render (cairo_cogl_surface_t  *surface,
+                                   int                   *clip_stack_depth,
+                                   cairo_cogl_pipeline_t *pipeline)
 {
     CoglDepthState depth_state;
     CoglBool cogl_status;
-    uint32_t op_bounded = _cairo_operator_bounded_by_either (op);
 
     /* Disable the depth test */
-    if ((_cairo_operator_bounded_by_mask(op) == FALSE && mask_tex_clip)
-        || (_cairo_operator_bounded_by_source(op) == FALSE && src_tex_clip))
+    if ((!pipeline->mask_bounded && pipeline->has_mask_tex_clip) ||
+        (!pipeline->src_bounded && pipeline->has_src_tex_clip))
     {
         cogl_depth_state_init (&depth_state);
         cogl_depth_state_set_test_enabled (&depth_state, FALSE);
-        cogl_status = cogl_pipeline_set_depth_state (pipeline,
+        cogl_status = cogl_pipeline_set_depth_state (pipeline->pipeline,
                                                      &depth_state,
                                                      NULL);
         if (cogl_status != TRUE)
@@ -1090,33 +1001,28 @@ _cairo_cogl_post_unbounded_render (cairo_cogl_surface_t *surface,
     }
 
     /* Pop all bounded tex clips (those that were not popped before) */
-    switch (op_bounded) {
-    case CAIRO_OPERATOR_BOUND_BY_MASK | CAIRO_OPERATOR_BOUND_BY_SOURCE:
-        if (src_tex_clip) {
+    if (pipeline->src_bounded && pipeline->mask_bounded) {
+        if (pipeline->has_src_tex_clip) {
             cogl_framebuffer_pop_clip (surface->framebuffer);
             (*clip_stack_depth)--;
         }
-        if (mask_tex_clip) {
+        if (pipeline->has_mask_tex_clip) {
             cogl_framebuffer_pop_clip (surface->framebuffer);
             (*clip_stack_depth)--;
         }
-        break;
-    case CAIRO_OPERATOR_BOUND_BY_SOURCE:
-        if (src_tex_clip) {
+    } else if (pipeline->src_bounded) {
+        if (pipeline->has_src_tex_clip) {
             cogl_framebuffer_pop_clip (surface->framebuffer);
             (*clip_stack_depth)--;
         }
-        break;
-    case 0:
-        break;
-    case CAIRO_OPERATOR_BOUND_BY_MASK:
-        if (mask_tex_clip) {
+    } else if (pipeline->mask_bounded) {
+        if (pipeline->has_mask_tex_clip) {
             cogl_framebuffer_pop_clip (surface->framebuffer);
             (*clip_stack_depth)--;
         }
-        break;
-    default:
-        assert (0); // not reached
+    } else {
+        /* We have already popped all of the clips in the
+         * unbounded_render function */
     }
 }
 
@@ -1222,56 +1128,46 @@ _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface)
 
             _cairo_cogl_apply_tex_clips (surface,
                                          &clip_stack_depth,
-                                         rect_entry->pipeline,
-                                         rect_entry->src_tex_clip,
-                                         rect_entry->mask_tex_clip,
-                                         rect_entry->op);
+                                         rect_entry->pipeline);
 
-	    if (rect_entry->n_layers) {
-		g_assert (rect_entry->n_layers <= 2);
+	    if (rect_entry->pipeline->n_layers) {
+		g_assert (rect_entry->pipeline->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)
+		if (rect_entry->pipeline->n_layers > 1)
 		    memcpy (&tex_coords[4], tex_coords, sizeof (float) * 4);
 	    }
 
 	    cogl_framebuffer_push_matrix (surface->framebuffer);
 	    cogl_framebuffer_transform (surface->framebuffer, &transform);
 	    cogl_framebuffer_draw_multitextured_rectangle (surface->framebuffer,
-                                                           rect_entry->pipeline,
+                                                           rect_entry->pipeline->pipeline,
                                                            x1, y1,
                                                            x2, y2,
 						           tex_coords,
-                                                           4 * rect_entry->n_layers);
+                                                           4 * rect_entry->pipeline->n_layers);
 
             _cairo_cogl_unbounded_render (surface,
                                           &clip_stack_depth,
                                           rect_entry->pipeline,
-                                          rect_entry->src_tex_clip,
-                                          rect_entry->mask_tex_clip,
-                                          rect_entry->op,
-                                          rect_entry->unbounded_extents,
                                           &needs_vertex_render);
             if (needs_vertex_render) {
                 unbounded_pipeline =
                     _cairo_cogl_setup_unbounded_area_pipeline (surface,
-                                                               rect_entry->op);
+                                                               rect_entry->pipeline->op);
                 cogl_framebuffer_draw_multitextured_rectangle (surface->framebuffer,
-                                                               rect_entry->pipeline,
+                                                               rect_entry->pipeline->pipeline,
                                                                x1, y1,
                                                                x2, y2,
 						               tex_coords,
-                                                               4 * rect_entry->n_layers);
+                                                               4 * rect_entry->pipeline->n_layers);
                 cogl_object_unref (unbounded_pipeline);
             }
             _cairo_cogl_post_unbounded_render (surface,
                                                &clip_stack_depth,
-                                               rect_entry->pipeline,
-                                               rect_entry->src_tex_clip,
-                                               rect_entry->mask_tex_clip,
-                                               rect_entry->op);
+                                               rect_entry->pipeline);
 
 	    cogl_framebuffer_pop_matrix (surface->framebuffer);
 	    break;
@@ -1285,10 +1181,7 @@ _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface)
 
             _cairo_cogl_apply_tex_clips (surface,
                                          &clip_stack_depth,
-                                         prim_entry->pipeline,
-                                         prim_entry->src_tex_clip,
-                                         prim_entry->mask_tex_clip,
-                                         prim_entry->op);
+                                         prim_entry->pipeline);
 
 	    cogl_framebuffer_push_matrix (surface->framebuffer);
 	    if (prim_entry->has_transform) {
@@ -1311,20 +1204,16 @@ _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface)
             if (prim_entry->primitive)
 	        cogl_primitive_draw (prim_entry->primitive,
                                      surface->framebuffer,
-                                     prim_entry->pipeline);
+                                     prim_entry->pipeline->pipeline);
 
             _cairo_cogl_unbounded_render (surface,
                                           &clip_stack_depth,
                                           prim_entry->pipeline,
-                                          prim_entry->src_tex_clip,
-                                          prim_entry->mask_tex_clip,
-                                          prim_entry->op,
-                                          prim_entry->unbounded_extents,
                                           &needs_vertex_render);
             if (needs_vertex_render) {
                 unbounded_pipeline =
                     _cairo_cogl_setup_unbounded_area_pipeline (surface,
-                                                               prim_entry->op);
+                                                               prim_entry->pipeline->op);
                 cogl_primitive_draw (prim_entry->primitive,
                                      surface->framebuffer,
                                      unbounded_pipeline);
@@ -1332,10 +1221,7 @@ _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface)
             }
             _cairo_cogl_post_unbounded_render (surface,
                                                &clip_stack_depth,
-                                               prim_entry->pipeline,
-                                               prim_entry->src_tex_clip,
-                                               prim_entry->mask_tex_clip,
-                                               prim_entry->op);
+                                               prim_entry->pipeline);
 
 	    cogl_framebuffer_pop_matrix (surface->framebuffer);
 	    break;
@@ -1348,29 +1234,22 @@ _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface)
 
             _cairo_cogl_apply_tex_clips (surface,
                                          &clip_stack_depth,
-                                         path_entry->pipeline,
-                                         path_entry->src_tex_clip,
-                                         path_entry->mask_tex_clip,
-                                         path_entry->op);
+                                         path_entry->pipeline);
 
             /* Use this until cogl2_path_fill is updated to take
              * framebuffer and pipeline arguments */
             cogl_framebuffer_fill_path (surface->framebuffer,
-                                        path_entry->pipeline,
+                                        path_entry->pipeline->pipeline,
                                         path_entry->path);
 
             _cairo_cogl_unbounded_render (surface,
                                           &clip_stack_depth,
                                           path_entry->pipeline,
-                                          path_entry->src_tex_clip,
-                                          path_entry->mask_tex_clip,
-                                          path_entry->op,
-                                          path_entry->unbounded_extents,
                                           &needs_vertex_render);
             if (needs_vertex_render) {
                 unbounded_pipeline =
                     _cairo_cogl_setup_unbounded_area_pipeline (surface,
-                                                               path_entry->op);
+                                                               path_entry->pipeline->op);
                 cogl_framebuffer_fill_path (surface->framebuffer,
                                             unbounded_pipeline,
                                             path_entry->path);
@@ -1378,10 +1257,7 @@ _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface)
             }
             _cairo_cogl_post_unbounded_render (surface,
                                                &clip_stack_depth,
-                                               path_entry->pipeline,
-                                               path_entry->src_tex_clip,
-                                               path_entry->mask_tex_clip,
-                                               path_entry->op);
+                                               path_entry->pipeline);
 
 	    cogl_framebuffer_pop_matrix (surface->framebuffer);
 
@@ -1401,7 +1277,7 @@ _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface)
 }
 
 static cairo_status_t
-_cairo_cogl_surface_flush (void *abstract_surface,
+_cairo_cogl_surface_flush (void    *abstract_surface,
 			   unsigned flags)
 {
     cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)abstract_surface;
@@ -1566,8 +1442,6 @@ _cairo_cogl_surface_read_rect_to_image_surface (cairo_cogl_surface_t   *surface,
     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;
@@ -1611,6 +1485,9 @@ _cairo_cogl_surface_acquire_source_image (void		         *abstract_surface,
     cairo_cogl_surface_t *surface = abstract_surface;
     cairo_status_t status;
 
+    if (unlikely (_cairo_surface_flush (abstract_surface, 0)))
+        g_warning ("Error flushing journal while acquiring image");
+
     if (surface->texture) {
         CoglTextureComponents components =
             cogl_texture_get_components(surface->texture);
@@ -1661,7 +1538,7 @@ _cairo_cogl_surface_release_source_image (void			*abstract_surface,
 
 static cairo_status_t
 _cairo_cogl_surface_clear (cairo_cogl_surface_t *surface,
-			   const cairo_color_t *color)
+			   const cairo_color_t  *color)
 {
     /* Anything batched in the journal up until now is redundant... */
     _cairo_cogl_journal_discard (surface);
@@ -1714,9 +1591,14 @@ _cairo_cogl_path_fixed_rectangle (cairo_path_fixed_t *path,
     if (unlikely (status))
 	return status;
 
-    return _cairo_path_fixed_close_path (path);
+    status = _cairo_path_fixed_close_path (path);
+    if (unlikely (status))
+	return status;
+
+    return CAIRO_STATUS_SUCCESS;
 }
 
+
 static CoglPipelineWrapMode
 get_cogl_wrap_mode_for_extend (cairo_extend_t extend_mode)
 {
@@ -1742,8 +1624,8 @@ get_cogl_wrap_mode_for_extend (cairo_extend_t extend_mode)
  * 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,
+_cairo_cogl_get_pot_texture (CoglContext  *context,
+			     CoglTexture  *texture,
 			     CoglTexture **pot_texture)
 {
     int width = cogl_texture_get_width (texture);
@@ -1824,36 +1706,25 @@ _cairo_cogl_acquire_surface_texture (cairo_cogl_surface_t        *reference_surf
     cairo_image_surface_t *image_clone = NULL;
     CoglTexture2D *texture;
     GError *error = NULL;
-    cairo_surface_t *clone;
+    cairo_surface_t *clone, *unwrapped;
+    cairo_rectangle_int_t surface_extents;
     cairo_matrix_t transform;
-    ptrdiff_t stride;
-    unsigned char *data;
+    CoglPipeline *copying_pipeline = NULL;
+    cairo_cogl_device_t *dev =
+        to_device(reference_surface->base.device);
 
     *has_pre_transform = FALSE;
     *vertical_invert = FALSE;
 
-    if (surface->device == reference_surface->base.device) {
-        _cairo_cogl_surface_flush ((cairo_cogl_surface_t *)surface, 0);
-        if (((cairo_cogl_surface_t *)surface)->texture)
-            return cogl_object_ref (((cairo_cogl_surface_t *)surface)->texture);
-    }
-
-    if (surface->type == CAIRO_SURFACE_TYPE_COGL) {
-	if (_cairo_surface_is_subsurface (surface)) {
-            surface = _cairo_surface_subsurface_get_target (surface);
-            if (surface->device == reference_surface->base.device)
-                if (((cairo_cogl_surface_t *)surface)->texture)
-                    return cogl_object_ref (((cairo_cogl_surface_t *)surface)->texture);
-	}
-    }
-
     clone = _cairo_surface_has_snapshot (surface, &_cairo_cogl_surface_backend);
-    if (clone) {
+    if (clone)
         if (((cairo_cogl_surface_t *)clone)->texture)
             return cogl_object_ref (((cairo_cogl_surface_t *)clone)->texture);
-    }
 
-    if (_cairo_surface_is_recording (surface)) {
+    /* Unwrap things like subsurfaces, but get the original extents */
+    unwrapped = _cairo_surface_get_source (surface, &surface_extents);
+
+    if (_cairo_surface_is_recording (unwrapped)) {
         /* We pre-transform the recording surface here and make the
          * target surface the size of the extents in order to reduce
          * texture size. When we return to the acquire_pattern_texture
@@ -1882,7 +1753,7 @@ _cairo_cogl_acquire_surface_texture (cairo_cogl_surface_t        *reference_surf
                                      extents->y);
         cairo_matrix_multiply (&transform, &transform, pattern_matrix);
 
-        if (_cairo_recording_surface_replay_with_clip (surface,
+        if (_cairo_recording_surface_replay_with_clip (unwrapped,
                                                        &transform,
                                                        clone,
                                                        NULL))
@@ -1896,74 +1767,148 @@ _cairo_cogl_acquire_surface_texture (cairo_cogl_surface_t        *reference_surf
         return texture;
     }
 
-    // g_warning ("Uploading image surface to texture");
+    if (to_device(unwrapped->device) == dev &&
+        ((cairo_cogl_surface_t *)unwrapped)->texture)
+    {
+        cairo_cogl_surface_t *cogl_surface =
+            (cairo_cogl_surface_t *)unwrapped;
 
-    if (_cairo_surface_is_image (surface)) {
-	image = (cairo_image_surface_t *)surface;
-    } else {
-        cairo_status_t status =
-            _cairo_surface_acquire_source_image (surface,
-                                                 &acquired_image,
-                                                 &image_extra);
-	if (unlikely (status)) {
-	    g_warning ("acquire_source_image failed: %s [%d]",
-		       cairo_status_to_string (status), status);
-	    return NULL;
-	}
-	image = acquired_image;
-    }
+        if (unlikely (_cairo_surface_flush (unwrapped, 0))) {
+            g_warning ("Error flushing source surface while getting "
+                       "pattern texture");
+            goto BAIL;
+        }
 
-    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;
-	}
+        /* We copy the surface to a new texture, thereby making a
+         * snapshot of it, as its contents may change between the time
+         * we log the pipeline and when we flush the journal */
+        texture =
+            cogl_texture_2d_new_with_size (dev->cogl_context,
+                                           surface_extents.width,
+                                           surface_extents.height);
+        if (!texture) {
+            g_warning ("Failed to allocate texture: %s",
+                           error->message);
+            g_error_free (error);
+            goto BAIL;
+        }
 
-	format = get_cogl_format_from_cairo_format (image_clone->format);
-	assert (format);
-    }
+        clone =
+            _cairo_cogl_surface_create_full (dev,
+                                             reference_surface->ignore_alpha,
+                                             NULL,
+                                             texture);
 
-    if (image->stride < 0) {
-        /* If the stride is negative, this modifies the data pointer so
-         * that all of the pixels are read into the texture, but
-         * upside-down. We then set invert_vertical so that
-         * acquire_pattern_texture will adjust the texture sampling
-         * matrix to correct this. */
-        stride = image->stride * -1;
-        data = image->data - stride * (image->height - 1);
-        *vertical_invert = TRUE;
+        if (_cairo_cogl_surface_ensure_framebuffer ((cairo_cogl_surface_t *)clone)) {
+            g_warning ("Could not get framebuffer for surface clone");
+            goto BAIL;
+        }
+
+        /* If cogl ever makes its internal _cogl_blit API public
+         * we could use that and make this much simpler */
+        copying_pipeline = cogl_pipeline_new (dev->cogl_context);
+        cogl_pipeline_set_layer_texture (copying_pipeline,
+                                         0,
+                                         cogl_surface->texture);
+
+        /* Factors for normalizing the texture coordinates */
+        double xscale = 1.0 / cogl_texture_get_width (cogl_surface->texture);
+        double yscale = 1.0 / cogl_texture_get_height (cogl_surface->texture);
+
+        cogl_framebuffer_draw_textured_rectangle (((cairo_cogl_surface_t *)clone)->framebuffer,
+                                                  copying_pipeline,
+                                                  0,
+                                                  0,
+                                                  surface_extents.width,
+                                                  surface_extents.height,
+                                                  xscale * surface_extents.x,
+                                                  yscale * surface_extents.y,
+                                                  xscale * (surface_extents.x + surface_extents.width),
+                                                  yscale * (surface_extents.y + surface_extents.height));
     } else {
-        stride = image->stride;
-        data = image->data;
-    }
-
-    texture = cogl_texture_2d_new_from_data (to_device(reference_surface->base.device)->cogl_context,
-					     image->width,
-					     image->height,
-					     format, /* incoming */
-					     stride,
-					     data,
-					     &error);
-    if (!texture) {
-	g_warning ("Failed to allocate texture: %s", error->message);
-	g_error_free (error);
-	goto BAIL;
-    }
+        ptrdiff_t stride;
+        unsigned char *data;
+
+        // g_warning ("Uploading image surface to texture");
+
+        if (_cairo_surface_is_image (surface)) {
+            image = (cairo_image_surface_t *)surface;
+        } else {
+            cairo_status_t status =
+                _cairo_surface_acquire_source_image (surface,
+                                                     &acquired_image,
+                                                     &image_extra);
+            if (unlikely (status)) {
+                g_warning ("acquire_source_image failed: %s [%d]",
+                           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);
+
+            image = image_clone;
+        }
 
-    clone = _cairo_cogl_surface_create_full (to_device(reference_surface->base.device),
-					     reference_surface->ignore_alpha,
-					     NULL, texture);
+        if (image->stride < 0) {
+            /* If the stride is negative, this modifies the data pointer so
+             * that all of the pixels are read into the texture, but
+             * upside-down. We then set vertical_invert so that
+             * acquire_pattern_texture will adjust the texture sampling
+             * matrix to correct this. */
+            stride = image->stride * -1;
+            data = image->data - stride * (image->height - 1);
+            *vertical_invert = TRUE;
+        } else {
+            stride = image->stride;
+            data = image->data;
+        }
+
+        texture = cogl_texture_2d_new_from_data (dev->cogl_context,
+                                                 image->width,
+                                                 image->height,
+                                                 format, /* incoming */
+                                                 stride,
+                                                 data,
+                                                 &error);
+        if (!texture) {
+            g_warning ("Failed to allocate texture: %s",
+                       error->message);
+            g_error_free (error);
+            goto BAIL;
+        }
 
-    _cairo_surface_attach_snapshot (surface, clone, NULL);
+        clone =
+            _cairo_cogl_surface_create_full (dev,
+                                             reference_surface->ignore_alpha,
+                                             NULL,
+                                             texture);
+    }
 
-    /* Attaching the snapshot will take a reference on the clone surface... */
-    cairo_surface_destroy (clone);
+    if (_cairo_surface_is_subsurface (surface))
+        _cairo_surface_subsurface_set_snapshot (surface, clone);
+    else
+        _cairo_surface_attach_snapshot (surface, clone, NULL);
 
 BAIL:
+    if (clone)
+        /* Attaching the snapshot will take a reference on the clone surface... */
+        cairo_surface_destroy (clone);
+    if (copying_pipeline)
+        cogl_object_unref (copying_pipeline);
     if (image_clone)
 	cairo_surface_destroy (&image_clone->base);
     if (acquired_image)
@@ -1973,8 +1918,8 @@ BAIL:
 }
 
 static cairo_status_t
-_cairo_cogl_create_tex_clip (cairo_path_fixed_t **tex_clip,
-                             cairo_matrix_t inverse)
+_cairo_cogl_create_tex_clip (cairo_path_fixed_t *tex_clip,
+                             cairo_matrix_t      inverse)
 {
     cairo_status_t status;
 
@@ -1982,14 +1927,13 @@ _cairo_cogl_create_tex_clip (cairo_path_fixed_t **tex_clip,
     if (unlikely (status))
         return status;
 
-    *tex_clip = _cairo_path_fixed_create ();
-    status = _cairo_cogl_path_fixed_rectangle (*tex_clip, 0, 0,
+    status = _cairo_cogl_path_fixed_rectangle (tex_clip, 0, 0,
                                                CAIRO_FIXED_ONE,
                                                CAIRO_FIXED_ONE);
     if (unlikely (status))
-        return status;
+	return status;
 
-    _cairo_path_fixed_transform (*tex_clip, &inverse);
+    _cairo_path_fixed_transform (tex_clip, &inverse);
 
     return CAIRO_STATUS_SUCCESS;
 }
@@ -1997,11 +1941,11 @@ _cairo_cogl_create_tex_clip (cairo_path_fixed_t **tex_clip,
 /* 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,
+_cairo_cogl_acquire_pattern_texture (const cairo_pattern_t           *pattern,
+				     cairo_cogl_surface_t            *destination,
+				     const cairo_rectangle_int_t     *extents,
 				     cairo_cogl_texture_attributes_t *attributes,
-                                     cairo_path_fixed_t **tex_clip)
+                                     cairo_path_fixed_t              *tex_clip)
 {
     CoglTexture *texture = NULL;
     cairo_bool_t has_pre_transform;
@@ -2225,18 +2169,13 @@ set_blend (CoglPipeline *pipeline, const char *blend_string)
 }
 
 static cairo_bool_t
-_cairo_cogl_setup_op_state (CoglPipeline *pipeline, cairo_operator_t op)
+_cairo_cogl_setup_op_state (CoglPipeline    *pipeline,
+                            cairo_operator_t op)
 {
     cairo_bool_t status = FALSE;
 
     switch ((int)op)
     {
-    /* Use OVER for SOURCE so we can use a bounding mask. We will
-     * replace the source alpha with ones or the mask alpha when we do
-     * the rest of the pipeline setup. Note that this doesn't actually
-     * give the right result alpha value, which would be directly
-     * copied in a true SOURCE operation. */
-    case CAIRO_OPERATOR_SOURCE:
     case CAIRO_OPERATOR_OVER:
 	status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR, DST_COLOR * (1 - SRC_COLOR[A]))");
 	break;
@@ -2258,8 +2197,6 @@ _cairo_cogl_setup_op_state (CoglPipeline *pipeline, cairo_operator_t op)
     case CAIRO_OPERATOR_DEST_IN:
 	status = set_blend (pipeline, "RGBA = ADD (0, DST_COLOR * SRC_COLOR[A])");
 	break;
-    /* Use DEST_OUT for CLEAR so we can use a bounding mask */
-    case CAIRO_OPERATOR_CLEAR:
     case CAIRO_OPERATOR_DEST_OUT:
         status = set_blend (pipeline, "RGBA = ADD (0, DST_COLOR * (1 - SRC_COLOR[A]))");
         break;
@@ -2269,17 +2206,33 @@ _cairo_cogl_setup_op_state (CoglPipeline *pipeline, cairo_operator_t op)
     case CAIRO_OPERATOR_XOR:
         status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR * (1 - DST_COLOR[A]), DST_COLOR * (1 - SRC_COLOR[A]))");
         break;
+    /* In order to handle SOURCE with a mask, we use two passes. The
+     * first consists of a CAIRO_OPERATOR_DEST_OUT with the source alpha
+     * replaced by the mask alpha in order to multiply all the
+     * destination values by one minus the mask alpha. The second pass
+     * (this one) then adds the source values, which have already been
+     * premultiplied by the mask alpha. */
+    case CAIRO_OPERATOR_SOURCE:
     case CAIRO_OPERATOR_ADD:
 	status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR, DST_COLOR)");
 	break;
+    case CAIRO_OPERATOR_CLEAR:
+        /* Runtime check */
+        /* CAIRO_OPERATOR_CLEAR is not supposed to use its own pipeline
+         * type. Use CAIRO_OPERATOR_DEST_OUT with the mask alpha as
+         * source alpha instead. */
+        assert (0);
+    default:
+        g_warning ("Unsupported blend operator");
+        assert (0);
     }
 
     return status;
 }
 
 static void
-create_template_for_op_type (cairo_cogl_device_t *dev,
-                              cairo_operator_t op,
+create_template_for_op_type (cairo_cogl_device_t      *dev,
+                              cairo_operator_t         op,
                               cairo_cogl_template_type type)
 {
     CoglPipeline *pipeline;
@@ -2298,19 +2251,6 @@ create_template_for_op_type (cairo_cogl_device_t *dev,
             return;
         }
 
-        if (op == CAIRO_OPERATOR_CLEAR || op == CAIRO_OPERATOR_SOURCE) {
-            /* This will make it so that the DEST_OUT or OVER blending
-             * functions receives a source alpha indicating a mask of
-             * uniform ones */
-            cogl_pipeline_set_layer_combine_constant (base,
-                                                      0,
-                                                      &color);
-            cogl_pipeline_set_layer_combine (base, 0,
-                                             "RGB = REPLACE (PRIMARY)"
-                                             "A = REPLACE (CONSTANT)",
-                                             NULL);
-        }
-
         dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID] = base;
     }
 
@@ -2322,49 +2262,30 @@ create_template_for_op_type (cairo_cogl_device_t *dev,
         pipeline =
             cogl_pipeline_copy (dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID]);
         cogl_pipeline_set_layer_combine_constant (pipeline, 0, &color);
-        if (op == CAIRO_OPERATOR_CLEAR || op == CAIRO_OPERATOR_SOURCE)
-            cogl_pipeline_set_layer_combine (pipeline, 0,
-                                             "RGB = MODULATE (PRIMARY, CONSTANT[A])"
-                                             "A = REPLACE (CONSTANT)",
-                                             NULL);
-        else
-            cogl_pipeline_set_layer_combine (pipeline, 0,
-                                             "RGBA = MODULATE (PRIMARY, CONSTANT[A])",
-                                             NULL);
+        cogl_pipeline_set_layer_combine (pipeline, 0,
+                                         "RGBA = MODULATE (PRIMARY, CONSTANT[A])",
+                                         NULL);
         break;
     case CAIRO_COGL_TEMPLATE_TYPE_TEXTURE_MASK_SOLID:
         pipeline =
             cogl_pipeline_copy (dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID]);
-        cogl_pipeline_set_layer_texture (pipeline, 0, dev->dummy_texture);
-        if (op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_SOURCE)
-            cogl_pipeline_set_layer_combine (pipeline, 0,
-                                             "RGB = MODULATE (PRIMARY, TEXTURE[A])"
-                                             "A = REPLACE (TEXTURE)",
-                                             NULL);
-        else
-            cogl_pipeline_set_layer_combine (pipeline, 0,
-                                             "RGBA = MODULATE (PRIMARY, TEXTURE[A])",
-                                             NULL);
+        cogl_pipeline_set_layer_null_texture (pipeline, 0,
+                                              COGL_TEXTURE_TYPE_2D);
+        cogl_pipeline_set_layer_combine (pipeline, 0,
+                                         "RGBA = MODULATE (PRIMARY, TEXTURE[A])",
+                                         NULL);
         break;
     case CAIRO_COGL_TEMPLATE_TYPE_TEXTURE:
         pipeline =
             cogl_pipeline_copy (dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID]);
-        cogl_pipeline_set_layer_texture (pipeline, 0, dev->dummy_texture);
-        if (op == CAIRO_OPERATOR_CLEAR || op == CAIRO_OPERATOR_SOURCE)
-            cogl_pipeline_set_layer_combine (pipeline, 0,
-                                             "RGB = REPLACE (TEXTURE)"
-                                             "A = REPLACE (CONSTANT)",
-                                             NULL);
+        cogl_pipeline_set_layer_null_texture (pipeline, 0,
+                                              COGL_TEXTURE_TYPE_2D);
         break;
     case CAIRO_COGL_TEMPLATE_TYPE_SOLID_MASK_TEXTURE:
         pipeline =
             cogl_pipeline_copy (dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID]);
-        cogl_pipeline_set_layer_texture (pipeline, 0, dev->dummy_texture);
-        if (op == CAIRO_OPERATOR_CLEAR || op == CAIRO_OPERATOR_SOURCE)
-            cogl_pipeline_set_layer_combine (pipeline, 0,
-                                             "RGB = REPLACE (TEXTURE)"
-                                             "A = REPLACE (CONSTANT)",
-                                             NULL);
+        cogl_pipeline_set_layer_null_texture (pipeline, 0,
+                                              COGL_TEXTURE_TYPE_2D);
         cogl_pipeline_set_layer_combine_constant (pipeline, 1, &color);
         cogl_pipeline_set_layer_combine (pipeline, 1,
                                          "RGBA = MODULATE (PREVIOUS, CONSTANT[A])",
@@ -2373,13 +2294,10 @@ create_template_for_op_type (cairo_cogl_device_t *dev,
     case CAIRO_COGL_TEMPLATE_TYPE_TEXTURE_MASK_TEXTURE:
         pipeline =
             cogl_pipeline_copy (dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID]);
-        cogl_pipeline_set_layer_texture (pipeline, 0, dev->dummy_texture);
-        if (op == CAIRO_OPERATOR_CLEAR || op == CAIRO_OPERATOR_SOURCE)
-            cogl_pipeline_set_layer_combine (pipeline, 0,
-                                             "RGB = REPLACE (TEXTURE)"
-                                             "A = REPLACE (CONSTANT)",
-                                             NULL);
-        cogl_pipeline_set_layer_texture (pipeline, 1, dev->dummy_texture);
+        cogl_pipeline_set_layer_null_texture (pipeline, 0,
+                                              COGL_TEXTURE_TYPE_2D);
+        cogl_pipeline_set_layer_null_texture (pipeline, 1,
+                                              COGL_TEXTURE_TYPE_2D);
         cogl_pipeline_set_layer_combine (pipeline, 1,
                                          "RGBA = MODULATE (PREVIOUS, TEXTURE[A])",
                                          NULL);
@@ -2393,9 +2311,9 @@ create_template_for_op_type (cairo_cogl_device_t *dev,
 }
 
 static void
-set_layer_texture_with_attributes (CoglPipeline *pipeline,
-				   int layer_index,
-				   CoglTexture *texture,
+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);
@@ -2416,28 +2334,27 @@ set_layer_texture_with_attributes (CoglPipeline *pipeline,
     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
+    } 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_path_fixed_t **src_tex_clip,
-                                               cairo_path_fixed_t **mask_tex_clip)
+/* This takes an argument of a pointer to an array of two pointers to
+ * cairo_cogl_pipeline_ts. On failure, both pointers will be set to
+ * NULL */
+static void
+get_source_mask_operator_destination_pipelines (cairo_cogl_pipeline_t       **pipelines,
+                                                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;
     cairo_cogl_device_t *dev = to_device(destination->base.device);
-    int layer_counter = 0;
 
-    if (src_tex_clip)
-        *src_tex_clip = NULL;
-    if (mask_tex_clip)
-        *mask_tex_clip = NULL;
+    pipelines[0] = NULL;
+    pipelines[1] = NULL;
 
     switch ((int)source->type)
     {
@@ -2471,37 +2388,97 @@ get_source_mask_operator_destination_pipeline (const cairo_pattern_t *mask,
         break;
     default:
 	g_warning ("Unsupported source type");
-	return NULL;
+	return;
     }
 
-    /* Lazily create pipeline templates */
-    if (unlikely (dev->template_pipelines[op][template_type] == NULL))
-        create_template_for_op_type (dev, op, template_type);
+    /* pipelines[0] is for pre-rendering the mask alpha in the case
+     * that it cannot be represented by the source color alpha value.
+     * For more details, go to the description in
+     * _cairo_cogl_setup_op_state */
+    if (op == CAIRO_OPERATOR_CLEAR || op == CAIRO_OPERATOR_SOURCE) {
+        cairo_cogl_template_type prerender_type;
 
-    pipeline =
-        cogl_pipeline_copy (dev->template_pipelines[op][template_type]);
+        pipelines[0] = g_new (cairo_cogl_pipeline_t, 1);
 
-    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,
-						 &attributes,
-                                                 src_tex_clip);
-	if (!texture)
-	    goto BAIL;
-	set_layer_texture_with_attributes (pipeline,
-                                           layer_counter++,
-                                           texture,
-                                           &attributes);
-	cogl_object_unref (texture);
+        if (mask && mask->type != CAIRO_PATTERN_TYPE_SOLID)
+            prerender_type = CAIRO_COGL_TEMPLATE_TYPE_SOLID;
+        else
+            prerender_type = CAIRO_COGL_TEMPLATE_TYPE_TEXTURE;
+
+        /* Lazily create pipeline templates */
+        if (unlikely (dev->template_pipelines[CAIRO_OPERATOR_DEST_OUT][prerender_type] == NULL))
+            create_template_for_op_type (dev,
+                                         CAIRO_OPERATOR_DEST_OUT,
+                                         prerender_type);
+
+        pipelines[0]->pipeline =
+            cogl_pipeline_copy (dev->template_pipelines[CAIRO_OPERATOR_DEST_OUT][prerender_type]);
+
+        pipelines[0]->mask_bounded =
+            _cairo_operator_bounded_by_mask (op);
+        pipelines[0]->src_bounded =
+            _cairo_operator_bounded_by_source (op);
+        pipelines[0]->op = CAIRO_OPERATOR_DEST_OUT;
+        pipelines[0]->n_layers = 0;
+        pipelines[0]->has_src_tex_clip = FALSE;
+        pipelines[0]->has_mask_tex_clip = FALSE;
+        pipelines[0]->unbounded_extents = extents->unbounded;
+    }
+
+    /* pipelines[1] is for normal rendering, modulating the mask with
+     * the source. Most operators will only need this pipeline. */
+    if (op != CAIRO_OPERATOR_CLEAR) {
+        pipelines[1] = g_new (cairo_cogl_pipeline_t, 1);
+
+        /* Lazily create pipeline templates */
+        if (unlikely (dev->template_pipelines[op][template_type] == NULL))
+            create_template_for_op_type (dev, op, template_type);
+
+        pipelines[1]->pipeline =
+            cogl_pipeline_copy (dev->template_pipelines[op][template_type]);
+
+        pipelines[1]->mask_bounded =
+            _cairo_operator_bounded_by_mask (op);
+        pipelines[1]->src_bounded =
+            _cairo_operator_bounded_by_source (op);
+        pipelines[1]->op = op;
+        pipelines[1]->n_layers = 0;
+        pipelines[1]->has_src_tex_clip = FALSE;
+        pipelines[1]->has_mask_tex_clip = FALSE;
+        pipelines[1]->unbounded_extents = extents->unbounded;
+    }
+
+    if (pipelines[1]) {
+        if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
+            cairo_solid_pattern_t *solid_pattern = (cairo_solid_pattern_t *)source;
+            cogl_pipeline_set_color4f (pipelines[1]->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;
+
+            _cairo_path_fixed_init (&pipelines[1]->src_tex_clip);
+
+	    CoglTexture *texture =
+	        _cairo_cogl_acquire_pattern_texture (source, destination,
+                                                     &extents->bounded,
+                                                     &attributes,
+                                                     &pipelines[1]->src_tex_clip);
+            if (unlikely (!texture))
+                goto BAIL;
+            set_layer_texture_with_attributes (pipelines[1]->pipeline,
+                                               pipelines[1]->n_layers++,
+                                               texture,
+                                               &attributes);
+            cogl_object_unref (texture);
+
+            if (pipelines[1]->src_tex_clip.buf.base.num_ops > 0)
+                pipelines[1]->has_src_tex_clip = TRUE;
+            else
+                _cairo_path_fixed_fini (&pipelines[1]->src_tex_clip);
+        }
     }
 
     if (mask) {
@@ -2513,40 +2490,86 @@ get_source_mask_operator_destination_pipeline (const cairo_pattern_t *mask,
 				     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,
-                                                      layer_counter++,
-                                                      &color);
+            if (pipelines[1])
+	        cogl_pipeline_set_layer_combine_constant (pipelines[1]->pipeline,
+                                                          pipelines[1]->n_layers++,
+                                                          &color);
+            if (pipelines[0])
+                cogl_pipeline_set_color (pipelines[0]->pipeline,
+                                         &color);
 	} else {
 	    cairo_cogl_texture_attributes_t attributes;
+            cairo_path_fixed_t mask_tex_clip;
+
+            _cairo_path_fixed_init (&mask_tex_clip);
+
 	    CoglTexture *texture =
 		_cairo_cogl_acquire_pattern_texture (mask, destination,
 						     &extents->bounded,
 						     &attributes,
-                                                     mask_tex_clip);
-	    if (!texture)
+                                                     &mask_tex_clip);
+	    if (unlikely (!texture))
 		goto BAIL;
-	    set_layer_texture_with_attributes (pipeline,
-                                               layer_counter++,
-                                               texture,
-                                               &attributes);
+            if (pipelines[1]) {
+                if (mask_tex_clip.buf.base.num_ops > 0) {
+                    pipelines[1]->has_mask_tex_clip = TRUE;
+                    if (_cairo_path_fixed_init_copy (&pipelines[1]->mask_tex_clip,
+                                                     &mask_tex_clip))
+                        goto BAIL;
+                }
+	        set_layer_texture_with_attributes (pipelines[1]->pipeline,
+                                                   pipelines[1]->n_layers++,
+                                                   texture,
+                                                   &attributes);
+            }
+            if (pipelines[0]) {
+                if (mask_tex_clip.buf.base.num_ops > 0) {
+                    pipelines[0]->has_mask_tex_clip = TRUE;
+                    if (_cairo_path_fixed_init_copy (&pipelines[0]->mask_tex_clip,
+                                                     &mask_tex_clip))
+                        goto BAIL;
+                }
+                set_layer_texture_with_attributes (pipelines[0]->pipeline,
+                                                   pipelines[0]->n_layers++,
+                                                   texture,
+                                                   &attributes);
+            }
+
+            _cairo_path_fixed_fini (&mask_tex_clip);
 	    cogl_object_unref (texture);
 	}
     }
 
-    return pipeline;
+    return;
 
 BAIL:
-    cogl_object_unref (pipeline);
-    return NULL;
+    if (pipelines[0]) {
+        cogl_object_unref (pipelines[0]->pipeline);
+        if (pipelines[0]->has_src_tex_clip)
+            _cairo_path_fixed_fini (&pipelines[0]->src_tex_clip);
+        if (pipelines[0]->has_mask_tex_clip)
+            _cairo_path_fixed_fini (&pipelines[0]->mask_tex_clip);
+        g_free (pipelines[0]);
+        pipelines[0] = NULL;
+    }
+    if (pipelines[1]) {
+        cogl_object_unref (pipelines[1]->pipeline);
+        if (pipelines[1]->has_src_tex_clip)
+            _cairo_path_fixed_fini (&pipelines[1]->src_tex_clip);
+        if (pipelines[1]->has_mask_tex_clip)
+            _cairo_path_fixed_fini (&pipelines[1]->mask_tex_clip);
+        g_free (pipelines[1]);
+        pipelines[1] = NULL;
+    }
 }
 
 #if 0
 CoglPrimitive *
 _cairo_cogl_rectangle_new_p2t2t2 (CoglContext *cogl_context,
-                                  float x,
-                                  float y,
-                                  float width,
-                                  float height)
+                                  float        x,
+                                  float        y,
+                                  float        width,
+                                  float        height)
 {
     CoglVertexP2 vertices[] = {
 	{x, y}, {x, y + height}, {x + width, y + height},
@@ -2591,7 +2614,7 @@ _cairo_cogl_rectangle_new_p2t2t2 (CoglContext *cogl_context,
 
 static void
 _cairo_cogl_log_clip (cairo_cogl_surface_t *surface,
-		      const cairo_clip_t *clip)
+		      const cairo_clip_t   *clip)
 {
     if (!_cairo_clip_equal (clip, surface->last_clip)) {
 	_cairo_cogl_journal_log_clip (surface, clip);
@@ -2601,7 +2624,7 @@ _cairo_cogl_log_clip (cairo_cogl_surface_t *surface,
 }
 
 static void
-_cairo_cogl_maybe_log_clip (cairo_cogl_surface_t *surface,
+_cairo_cogl_maybe_log_clip (cairo_cogl_surface_t         *surface,
 			    cairo_composite_rectangles_t *composite)
 {
     cairo_clip_t *clip = composite->clip;
@@ -2643,25 +2666,6 @@ is_operator_supported (cairo_operator_t op)
     }
 }
 
-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_RASTER_SOURCE:
-    case CAIRO_PATTERN_TYPE_SURFACE:
-	return 1;
-    default:
-	g_warning ("Unsupported source type");
-	return 0;
-    }
-}
-
 static cairo_int_status_t
 _cairo_cogl_surface_paint (void                  *abstract_surface,
                            cairo_operator_t       op,
@@ -2671,9 +2675,8 @@ _cairo_cogl_surface_paint (void                  *abstract_surface,
     cairo_cogl_surface_t *surface;
     cairo_int_status_t status;
     cairo_matrix_t identity;
-    CoglPipeline *pipeline;
+    cairo_cogl_pipeline_t *pipelines[2];
     cairo_composite_rectangles_t extents;
-    cairo_path_fixed_t *src_tex_clip;
 
     if (clip == NULL) {
         status = _cairo_cogl_surface_ensure_framebuffer (abstract_surface);
@@ -2706,54 +2709,50 @@ _cairo_cogl_surface_paint (void                  *abstract_surface,
     if (unlikely (status))
 	return status;
 
-    pipeline =
-        get_source_mask_operator_destination_pipeline (NULL,
-                                                       source,
-                                                       op,
-                                                       surface,
-                                                       &extents,
-                                                       &src_tex_clip,
-                                                       NULL);
-    if (!pipeline) {
-	return CAIRO_INT_STATUS_UNSUPPORTED;
-        if (src_tex_clip)
-            _cairo_path_fixed_destroy (src_tex_clip);
-    }
+    get_source_mask_operator_destination_pipelines (pipelines,
+                                                    NULL,
+                                                    source,
+                                                    op,
+                                                    surface,
+                                                    &extents);
+    if (!pipelines[0] && !pipelines[1])
+        return CAIRO_INT_STATUS_UNSUPPORTED;
 
     _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,
-				       _cairo_cogl_source_n_layers (source),
-				       &identity,
-                                       src_tex_clip,
-                                       NULL,
-                                       op,
-                                       extents.unbounded);
-
-    /* The journal will take a reference on the pipeline... */
-    cogl_object_unref (pipeline);
+    if (pipelines[0])
+        _cairo_cogl_journal_log_rectangle (surface,
+                                           pipelines[0],
+                                           extents.bounded.x,
+                                           extents.bounded.y,
+                                           extents.bounded.width,
+                                           extents.bounded.height,
+                                           &identity);
+    if (pipelines[1])
+        _cairo_cogl_journal_log_rectangle (surface,
+                                           pipelines[1],
+                                           extents.bounded.x,
+                                           extents.bounded.y,
+                                           extents.bounded.width,
+                                           extents.bounded.height,
+                                           &identity);
 
     return CAIRO_STATUS_SUCCESS;
 }
 
 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_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_int_status_t status;
-    CoglPipeline *pipeline;
+    cairo_cogl_pipeline_t *pipelines[2];
     cairo_matrix_t identity;
-    cairo_path_fixed_t *src_tex_clip, *mask_tex_clip;
 
     /* XXX: Use this to smoke test the acquire_source/dest_image fallback
      * paths... */
@@ -2768,39 +2767,34 @@ _cairo_cogl_surface_mask (void                    *abstract_surface,
     if (unlikely (status))
 	return status;
 
-    pipeline =
-        get_source_mask_operator_destination_pipeline (mask,
-                                                       source,
-                                                       op,
-                                                       surface,
-                                                       &extents,
-                                                       &src_tex_clip,
-                                                       &mask_tex_clip);
-    if (!pipeline) {
-        if (src_tex_clip)
-            _cairo_path_fixed_destroy (src_tex_clip);
-        if (mask_tex_clip)
-            _cairo_path_fixed_destroy (mask_tex_clip);
+    get_source_mask_operator_destination_pipelines (pipelines,
+                                                    mask,
+                                                    source,
+                                                    op,
+                                                    surface,
+                                                    &extents);
+    if (!pipelines[0] && !pipelines[1])
 	return CAIRO_INT_STATUS_UNSUPPORTED;
-    }
 
     _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,
-				       _cairo_cogl_source_n_layers (source) + 1,
-				       &identity,
-                                       src_tex_clip,
-                                       mask_tex_clip,
-                                       op,
-                                       extents.unbounded);
-
-    /* The journal will take a reference on the pipeline... */
-    cogl_object_unref (pipeline);
+    if (pipelines[0])
+        _cairo_cogl_journal_log_rectangle (surface,
+                                           pipelines[0],
+                                           extents.bounded.x,
+                                           extents.bounded.y,
+                                           extents.bounded.width,
+                                           extents.bounded.height,
+                                           &identity);
+    if (pipelines[1])
+        _cairo_cogl_journal_log_rectangle (surface,
+                                           pipelines[1],
+                                           extents.bounded.x,
+                                           extents.bounded.y,
+                                           extents.bounded.width,
+                                           extents.bounded.height,
+                                           &identity);
 
     return CAIRO_STATUS_SUCCESS;
 }
@@ -2835,7 +2829,8 @@ _cairo_cogl_stroke_style_equal (const cairo_stroke_style_t *a,
 }
 
 static cairo_bool_t
-_cairo_cogl_path_stroke_meta_equal (const void *key_a, const void *key_b)
+_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;
@@ -2895,9 +2890,9 @@ _cairo_cogl_path_stroke_meta_lookup (cairo_cogl_device_t	*ctx,
 }
 
 static void
-_cairo_cogl_path_stroke_meta_set_prim_size (cairo_cogl_surface_t *surface,
+_cairo_cogl_path_stroke_meta_set_prim_size (cairo_cogl_surface_t          *surface,
 					    cairo_cogl_path_stroke_meta_t *meta,
-					    size_t size)
+					    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.
@@ -2917,7 +2912,7 @@ _cairo_cogl_path_stroke_meta_set_prim_size (cairo_cogl_surface_t *surface,
 }
 
 static unsigned int
-_cairo_cogl_stroke_style_hash (unsigned int hash,
+_cairo_cogl_stroke_style_hash (unsigned int                hash,
 			       const cairo_stroke_style_t *style)
 {
     unsigned int i;
@@ -2933,9 +2928,9 @@ _cairo_cogl_stroke_style_hash (unsigned int hash,
 }
 
 static cairo_cogl_path_stroke_meta_t *
-_cairo_cogl_get_path_stroke_meta (cairo_cogl_surface_t *surface,
+_cairo_cogl_get_path_stroke_meta (cairo_cogl_surface_t       *surface,
 				  const cairo_stroke_style_t *style,
-				  double tolerance)
+				  double                      tolerance)
 {
     unsigned long hash;
     cairo_cogl_path_stroke_meta_t *meta = NULL;
@@ -2993,7 +2988,6 @@ _cairo_cogl_stroke_to_primitive (cairo_cogl_surface_t	    *surface,
 				 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)
@@ -3018,7 +3012,7 @@ _cairo_cogl_stroke_to_primitive (cairo_cogl_surface_t	    *surface,
     *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);
+    *primitive = _cairo_cogl_traps_to_composite_prim (surface, &traps, one_shot);
     if (!*primitive) {
 	status = CAIRO_INT_STATUS_NO_MEMORY;
 	goto BAIL;
@@ -3043,17 +3037,16 @@ _cairo_cogl_surface_stroke (void                       *abstract_surface,
 {
     cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)abstract_surface;
     cairo_composite_rectangles_t extents;
-    CoglPipeline *pipeline;
+    cairo_cogl_pipeline_t *pipelines[2];
     cairo_int_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;
+    cairo_bool_t one_shot = TRUE;
     CoglPrimitive *prim = NULL;
     cairo_bool_t new_prim = FALSE;
-    cairo_path_fixed_t *src_tex_clip;
 
     if (! is_operator_supported (op))
 	return CAIRO_INT_STATUS_UNSUPPORTED;
@@ -3076,26 +3069,27 @@ _cairo_cogl_surface_stroke (void                       *abstract_surface,
 	if (prim) {
 	    cairo_matrix_multiply (&transform_matrix, &meta->ctm_inverse, surface->ctm);
 	    transform = &transform_matrix;
-	} else if (meta->counter++ > 10)
+	} 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;
+	size_t prim_size;
 	status = _cairo_cogl_stroke_to_primitive (surface, path, style,
-						  ctm, ctm_inverse, tolerance,
-						  n_layers, one_shot,
-						  &prim, &prim_size);
+                                                  ctm, ctm_inverse, tolerance,
+                                                  one_shot, &prim,
+                                                  &prim_size);
         if (status == CAIRO_INT_STATUS_NOTHING_TO_DO
             && _cairo_operator_bounded_by_mask (op) == FALSE) {
             /* Just render the unbounded rectangle */
             prim = NULL;
-	} else if (unlikely (status))
+	} else if (unlikely (status)) {
 	    return status;
-        else
+        } else {
             new_prim = TRUE;
+        }
 #if defined (ENABLE_PATH_CACHE)
 	if (meta && prim) {
 	    meta->prim = cogl_object_ref (prim);
@@ -3104,32 +3098,28 @@ _cairo_cogl_surface_stroke (void                       *abstract_surface,
 #endif
     }
 
-    pipeline =
-        get_source_mask_operator_destination_pipeline (NULL,
-                                                       source,
-                                                       op,
-                                                       surface,
-                                                       &extents,
-                                                       &src_tex_clip,
-                                                       NULL);
-    if (!pipeline) {
-        if (src_tex_clip)
-            _cairo_path_fixed_destroy (src_tex_clip);
+    get_source_mask_operator_destination_pipelines (pipelines,
+                                                    NULL,
+                                                    source,
+                                                    op,
+                                                    surface,
+                                                    &extents);
+    if (!pipelines[0] && !pipelines[1])
         return CAIRO_INT_STATUS_UNSUPPORTED;
-    }
 
     _cairo_cogl_maybe_log_clip (surface, &extents);
 
-    _cairo_cogl_journal_log_primitive (surface,
-                                       pipeline,
-                                       prim,
-                                       transform,
-                                       src_tex_clip,
-                                       NULL,
-                                       op,
-                                       extents.unbounded);
-    /* The journal will take a reference on the pipeline and primitive... */
-    cogl_object_unref (pipeline);
+    if (pipelines[0])
+        _cairo_cogl_journal_log_primitive (surface,
+                                           pipelines[0],
+                                           prim,
+                                           transform);
+    if (pipelines[1])
+        _cairo_cogl_journal_log_primitive (surface,
+                                           pipelines[1],
+                                           prim,
+                                           transform);
+    /* The journal will take a reference on the primitive... */
     if (new_prim)
 	cogl_object_unref (prim);
 
@@ -3181,9 +3171,9 @@ _cairo_cogl_path_fill_meta_lookup (cairo_cogl_device_t	*ctx,
 }
 
 static void
-_cairo_cogl_path_fill_meta_set_prim_size (cairo_cogl_surface_t *surface,
+_cairo_cogl_path_fill_meta_set_prim_size (cairo_cogl_surface_t        *surface,
 					  cairo_cogl_path_fill_meta_t *meta,
-					  size_t size)
+					  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.
@@ -3274,8 +3264,7 @@ _cairo_cogl_surface_fill (void			    *abstract_surface,
     cairo_bool_t one_shot = TRUE;
     CoglPrimitive *prim = NULL;
     cairo_bool_t new_prim = FALSE;
-    CoglPipeline *pipeline;
-    cairo_path_fixed_t *src_tex_clip;
+    cairo_cogl_pipeline_t *pipelines[2];
 
     if (! is_operator_supported (op))
 	return CAIRO_INT_STATUS_UNSUPPORTED;
@@ -3295,25 +3284,25 @@ _cairo_cogl_surface_fill (void			    *abstract_surface,
 	if (prim) {
 	    cairo_matrix_multiply (&transform_matrix, &meta->ctm_inverse, surface->ctm);
 	    transform = &transform_matrix;
-	} else if (meta->counter++ > 10)
+	} 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);
+						one_shot, &prim, &prim_size);
         if (status == CAIRO_INT_STATUS_NOTHING_TO_DO
             && _cairo_operator_bounded_by_mask (op) == FALSE) {
             /* Just render the unbounded rectangle */
             prim = NULL;
-	} else if (unlikely (status))
+	} else if (unlikely (status)) {
 	    return status;
-        else
+        } else {
             new_prim = TRUE;
+        }
 #ifdef ENABLE_PATH_CACHE
 	if (meta && prim) {
 	    meta->prim = cogl_object_ref (prim);
@@ -3324,49 +3313,46 @@ _cairo_cogl_surface_fill (void			    *abstract_surface,
 
 #endif /* !FILL_WITH_COGL_PATH */
 
-    pipeline =
-        get_source_mask_operator_destination_pipeline (NULL,
-                                                       source,
-                                                       op,
-                                                       surface,
-                                                       &extents,
-                                                       &src_tex_clip,
-                                                       NULL);
-    if (!pipeline) {
-        if (src_tex_clip)
-            _cairo_path_fixed_destroy (src_tex_clip);
+    get_source_mask_operator_destination_pipelines (pipelines,
+                                                    NULL,
+                                                    source,
+                                                    op,
+                                                    surface,
+                                                    &extents);
+    if (!pipelines[0] && !pipelines[1])
 	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,
-                                       src_tex_clip,
-                                       NULL,
-                                       op,
-                                       extents.unbounded);
+    if (pipelines[0])
+        _cairo_cogl_journal_log_primitive (surface,
+                                           pipelines[0],
+                                           prim,
+                                           transform);
+    if (pipelines[1])
+        _cairo_cogl_journal_log_primitive (surface,
+                                           pipelines[1],
+                                           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,
-                                  src_tex_clip,
-                                  NULL,
-                                  op,
-                                  extents.unbounded);
+
+    if (pipelines[0])
+        _cairo_cogl_journal_log_path (surface,
+                                      pipelines[0],
+                                      cogl_path);
+    if (pipelines[1])
+        _cairo_cogl_journal_log_path (surface,
+                                      pipelines[1],
+                                      cogl_path);
+    /* The journal will take a reference on the path */
     cogl_object_unref (cogl_path);
 #endif
 
-    /* The journal will take a reference on the pipeline... */
-    cogl_object_unref (pipeline);
-
     return CAIRO_STATUS_SUCCESS;
 }
 
@@ -3384,7 +3370,7 @@ _cairo_cogl_surface_fill_rectangle (void		     *abstract_surface,
     cairo_cogl_surface_t *surface = abstract_surface;
     cairo_composite_rectangles_t extents;
     cairo_int_status_t status;
-    CoglPipeline *pipeline;
+    cairo_cogl_pipeline_t *pipelines[2];
 
     if (! is_operator_supported (op))
 	return CAIRO_INT_STATUS_UNSUPPORTED;
@@ -3401,31 +3387,32 @@ _cairo_cogl_surface_fill_rectangle (void		     *abstract_surface,
         if (unlikely (status))
 	    return status;
 
-	pipeline =
-            get_source_mask_operator_destination_pipeline (NULL,
-                                                           source,
-                                                           op,
-                                                           surface,
-                                                           NULL,
-                                                           NULL,
-                                                           NULL);
-	if (!pipeline)
-	    return CAIRO_INT_STATUS_UNSUPPORTED;
+	get_source_mask_operator_destination_pipelines (pipelines,
+                                                        NULL,
+                                                        source,
+                                                        op,
+                                                        surface,
+                                                        &extents);
+        if (!pipelines[0] && !pipelines[1])
+            return CAIRO_INT_STATUS_UNSUPPORTED;
 
 	_cairo_cogl_log_clip (surface, clip);
 
-	_cairo_cogl_journal_log_rectangle (surface,
-					   pipeline,
-					   x, y, width, height,
-					   0,
-					   ctm,
-                                           NULL,
-                                           NULL,
-                                           op,
-                                           extents.unbounded);
+        if (pipelines[0])
+            _cairo_cogl_journal_log_rectangle (surface,
+                                               pipelines[0],
+                                               x, y, width, height,
+                                               ctm);
+        if (pipelines[1])
+            _cairo_cogl_journal_log_rectangle (surface,
+                                               pipelines[1],
+                                               x, y, width, height,
+                                               ctm);
+
 	return CAIRO_INT_STATUS_SUCCESS;
-    } else
+    } else {
 	return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
 
     /* TODO:
      * We need to acquire the textures here, look at the corresponding
@@ -3473,9 +3460,9 @@ const cairo_surface_backend_t _cairo_cogl_surface_backend = {
 
 static cairo_surface_t *
 _cairo_cogl_surface_create_full (cairo_cogl_device_t *dev,
-				 cairo_bool_t ignore_alpha,
-				 CoglFramebuffer *framebuffer,
-				 CoglTexture *texture)
+				 cairo_bool_t         ignore_alpha,
+				 CoglFramebuffer     *framebuffer,
+				 CoglTexture         *texture)
 {
     cairo_cogl_surface_t *surface;
     cairo_status_t status;
@@ -3632,15 +3619,20 @@ static const cairo_device_backend_t _cairo_cogl_device_backend = {
 cairo_device_t *
 cairo_cogl_device_create (CoglContext *cogl_context)
 {
-    cairo_cogl_device_t *dev = g_new0 (cairo_cogl_device_t, 1);
+    cairo_cogl_device_t *dev = g_new (cairo_cogl_device_t, 1);
     cairo_status_t status;
 
     dev->cogl_context = cogl_context;
 
-    dev->dummy_texture = cogl_texture_2d_new_with_size (cogl_context,
-                                                        1, 1);
-    if (!dev->dummy_texture)
-	goto ERROR;
+    dev->has_npots =
+        cogl_has_features (cogl_context,
+                           COGL_FEATURE_ID_TEXTURE_NPOT_BASIC,
+                           COGL_FEATURE_ID_TEXTURE_NPOT_REPEAT,
+                           0);
+
+    dev->has_mirrored_repeat =
+        cogl_has_feature (cogl_context,
+                          COGL_FEATURE_ID_MIRRORED_REPEAT);
 
     dev->buffer_stack = NULL;
     dev->buffer_stack_size = 4096;
@@ -3653,8 +3645,10 @@ cairo_cogl_device_create (CoglContext *cogl_context)
                                 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);
+    if (unlikely (status)) {
+        g_free (dev);
+        return _cairo_device_create_in_error (status);
+    }
 
     status = _cairo_cache_init (&dev->path_fill_staging_cache,
                                 _cairo_cogl_path_fill_meta_equal,
@@ -3682,10 +3676,6 @@ cairo_cogl_device_create (CoglContext *cogl_context)
 
     _cairo_device_init (&dev->base, &_cairo_cogl_device_backend);
     return &dev->base;
-
-ERROR:
-    g_free (dev);
-    return _cairo_device_create_in_error (CAIRO_STATUS_DEVICE_ERROR);
 }
 slim_hidden_def (cairo_cogl_device_create);
 
diff --git a/src/cairo-cogl-utils-private.h b/src/cairo-cogl-utils-private.h
index ee77f3034..3a11289bc 100644
--- a/src/cairo-cogl-utils-private.h
+++ b/src/cairo-cogl-utils-private.h
@@ -37,8 +37,8 @@
 
 CoglPath *
 _cairo_cogl_util_path_from_cairo (const cairo_path_fixed_t *path,
-				  cairo_fill_rule_t fill_rule,
-				  float tolerance);
+				  cairo_fill_rule_t         fill_rule,
+				  float                     tolerance);
 
 int
 _cairo_cogl_util_next_p2 (int a);
diff --git a/src/cairo-cogl-utils.c b/src/cairo-cogl-utils.c
index 4f02aaa52..6d2130125 100644
--- a/src/cairo-cogl-utils.c
+++ b/src/cairo-cogl-utils.c
@@ -80,8 +80,8 @@ _cogl_close_path (void *closure)
 
 CoglPath *
 _cairo_cogl_util_path_from_cairo (const cairo_path_fixed_t *path,
-				  cairo_fill_rule_t fill_rule,
-				  float tolerance)
+				  cairo_fill_rule_t         fill_rule,
+				  float                     tolerance)
 {
     CoglPath *cogl_path = cogl_path_new ();
     cairo_status_t status;
diff --git a/src/cairo-cogl.h b/src/cairo-cogl.h
index a7e5d4690..e525df955 100644
--- a/src/cairo-cogl.h
+++ b/src/cairo-cogl.h
@@ -48,7 +48,7 @@ cairo_public cairo_device_t *
 cairo_cogl_device_create (CoglContext *context);
 
 cairo_public cairo_surface_t *
-cairo_cogl_surface_create (cairo_device_t *device,
+cairo_cogl_surface_create (cairo_device_t  *device,
 			   CoglFramebuffer *framebuffer);
 
 cairo_public CoglFramebuffer *
commit b258f0203c4365a67e6cc88763608737badb4580
Author: George Matsumura <gmmatsumura01 at bvsd.org>
Date:   Wed Jul 15 12:34:12 2020 -0600

    cogl: Handle negative stride images correctly
    
    This adds correct support for negative stride images by uploading
    them to a texture in their upside-down orientation and then
    vertically inverting the matrix with which they are read, which
    is required because OpenGL (and therefore cogl) cannot pack pixels
    with a negative stride.
    
    Signed-off-by: George Matsumura <gmmatsumura01 at bvsd.org>

diff --git a/src/cairo-cogl-surface.c b/src/cairo-cogl-surface.c
index 9388189e2..8207532c4 100644
--- a/src/cairo-cogl-surface.c
+++ b/src/cairo-cogl-surface.c
@@ -1814,7 +1814,8 @@ _cairo_cogl_acquire_surface_texture (cairo_cogl_surface_t        *reference_surf
                                      cairo_surface_t             *surface,
                                      const cairo_rectangle_int_t *extents,
                                      const cairo_matrix_t        *pattern_matrix,
-                                     cairo_bool_t                *has_pre_transform)
+                                     cairo_bool_t                *has_pre_transform,
+                                     cairo_bool_t                *vertical_invert)
 {
     cairo_image_surface_t *image;
     cairo_image_surface_t *acquired_image = NULL;
@@ -1825,8 +1826,11 @@ _cairo_cogl_acquire_surface_texture (cairo_cogl_surface_t        *reference_surf
     GError *error = NULL;
     cairo_surface_t *clone;
     cairo_matrix_t transform;
+    ptrdiff_t stride;
+    unsigned char *data;
 
     *has_pre_transform = FALSE;
+    *vertical_invert = FALSE;
 
     if (surface->device == reference_surface->base.device) {
         _cairo_cogl_surface_flush ((cairo_cogl_surface_t *)surface, 0);
@@ -1923,12 +1927,26 @@ _cairo_cogl_acquire_surface_texture (cairo_cogl_surface_t        *reference_surf
 	assert (format);
     }
 
+    if (image->stride < 0) {
+        /* If the stride is negative, this modifies the data pointer so
+         * that all of the pixels are read into the texture, but
+         * upside-down. We then set invert_vertical so that
+         * acquire_pattern_texture will adjust the texture sampling
+         * matrix to correct this. */
+        stride = image->stride * -1;
+        data = image->data - stride * (image->height - 1);
+        *vertical_invert = TRUE;
+    } else {
+        stride = image->stride;
+        data = image->data;
+    }
+
     texture = cogl_texture_2d_new_from_data (to_device(reference_surface->base.device)->cogl_context,
 					     image->width,
 					     image->height,
 					     format, /* incoming */
-					     image->stride,
-					     image->data,
+					     stride,
+					     data,
 					     &error);
     if (!texture) {
 	g_warning ("Failed to allocate texture: %s", error->message);
@@ -1987,6 +2005,7 @@ _cairo_cogl_acquire_pattern_texture (const cairo_pattern_t *pattern,
 {
     CoglTexture *texture = NULL;
     cairo_bool_t has_pre_transform;
+    cairo_bool_t vertical_invert;
 
     switch ((int)pattern->type)
     {
@@ -1997,7 +2016,8 @@ _cairo_cogl_acquire_pattern_texture (const cairo_pattern_t *pattern,
                                                  surface,
                                                  extents,
                                                  &pattern->matrix,
-                                                 &has_pre_transform);
+                                                 &has_pre_transform,
+                                                 &vertical_invert);
 	if (!texture)
 	    return NULL;
 
@@ -2030,6 +2050,15 @@ _cairo_cogl_acquire_pattern_texture (const cairo_pattern_t *pattern,
         attributes->matrix.x0 *= xscale;
         attributes->matrix.y0 *= yscale;
 
+        if (vertical_invert) {
+            /* Convert the normalized texture matrix so that we read
+             * the texture from the bottom up instead of from the top
+             * down */
+            attributes->matrix.yx *= -1.0;
+            attributes->matrix.yy *= -1.0;
+            attributes->matrix.y0 += 1.0;
+        }
+
 	attributes->extend = pattern->extend;
 	attributes->filter = CAIRO_FILTER_BILINEAR;
 	attributes->has_component_alpha = pattern->has_component_alpha;
@@ -2070,7 +2099,8 @@ _cairo_cogl_acquire_pattern_texture (const cairo_pattern_t *pattern,
                                                  surface,
                                                  NULL, // As long as the surface is an image,
                                                  NULL, // acquire_surface_texture shouldn't access these values
-                                                 &has_pre_transform);
+                                                 &has_pre_transform,
+                                                 &vertical_invert);
 	if (!texture)
 	    goto BAIL;
 
@@ -2092,6 +2122,15 @@ _cairo_cogl_acquire_pattern_texture (const cairo_pattern_t *pattern,
         attributes->matrix.x0 *= xscale;
         attributes->matrix.y0 *= yscale;
 
+        if (vertical_invert) {
+            /* Convert the normalized texture matrix so that we read
+             * the texture from the bottom up instead of from the top
+             * down */
+            attributes->matrix.yx *= -1.0;
+            attributes->matrix.yy *= -1.0;
+            attributes->matrix.y0 += 1.0;
+        }
+
 	attributes->extend = pattern->extend;
 	attributes->filter = CAIRO_FILTER_NEAREST;
 	attributes->has_component_alpha = pattern->has_component_alpha;
commit 20cc7ad16ea2fe95499813b93732b7d7bdefa4dc
Author: George Matsumura <gmmatsumura01 at bvsd.org>
Date:   Wed Jul 15 07:51:31 2020 -0600

    cogl: Avoid duplicate representations of the path
    
    Signed-off-by: George Matsumura <gmmatsumura01 at bvsd.org>

diff --git a/src/cairo-cogl-context.c b/src/cairo-cogl-context.c
index 2b8fbc831..56dbcf31b 100644
--- a/src/cairo-cogl-context.c
+++ b/src/cairo-cogl-context.c
@@ -617,18 +617,9 @@ _cairo_cogl_context_path_extents (void *abstract_cr,
                                   double *y2)
 {
     cairo_cogl_context_t *cr = abstract_cr;
-    cairo_status_t status;
 
-    if (cr->path_is_rectangle) {
-        /* We do not flush the rectangle, as we are not modifying the
-         * path */
-        status = _cairo_cogl_context_rectangle_real (cr,
-                                                     cr->x,
-                                                     cr->y,
-                                                     cr->width,
-                                                     cr->height);
-        assert (status == CAIRO_STATUS_SUCCESS);
-    }
+    if (cr->path_is_rectangle)
+        assert (_flush_cr_rectangle (cr) == CAIRO_STATUS_SUCCESS);
 
     cr->backend_parent.path_extents (abstract_cr, x1, y1, x2, y2);
 }
@@ -637,18 +628,9 @@ static cairo_bool_t
 _cairo_cogl_context_has_current_point (void *abstract_cr)
 {
     cairo_cogl_context_t *cr = abstract_cr;
-    cairo_status_t status;
 
-    if (cr->path_is_rectangle) {
-        /* We do not flush the rectangle, as we are not modifying the
-         * path */
-        status = _cairo_cogl_context_rectangle_real (cr,
-                                                     cr->x,
-                                                     cr->y,
-                                                     cr->width,
-                                                     cr->height);
-        assert (status == CAIRO_STATUS_SUCCESS);
-    }
+    if (cr->path_is_rectangle)
+        assert (_flush_cr_rectangle (cr) == CAIRO_STATUS_SUCCESS);
 
     return cr->backend_parent.has_current_point (abstract_cr);
 }
@@ -659,18 +641,9 @@ _cairo_cogl_context_get_current_point (void *abstract_cr,
                                        double *y)
 {
     cairo_cogl_context_t *cr = abstract_cr;
-    cairo_status_t status;
 
-    if (cr->path_is_rectangle) {
-        /* We do not flush the rectangle, as we are not modifying the
-         * path */
-        status = _cairo_cogl_context_rectangle_real (cr,
-                                                     cr->x,
-                                                     cr->y,
-                                                     cr->width,
-                                                     cr->height);
-        assert (status == CAIRO_STATUS_SUCCESS);
-    }
+    if (cr->path_is_rectangle)
+        assert (_flush_cr_rectangle (cr) == CAIRO_STATUS_SUCCESS);
 
     return cr->backend_parent.get_current_point (abstract_cr, x, y);
 }
@@ -679,18 +652,9 @@ static cairo_path_t *
 _cairo_cogl_context_copy_path (void *abstract_cr)
 {
     cairo_cogl_context_t *cr = abstract_cr;
-    cairo_status_t status;
 
-    if (cr->path_is_rectangle) {
-        /* We do not flush the rectangle, as we are not modifying the
-         * path */
-        status = _cairo_cogl_context_rectangle_real (cr,
-                                                     cr->x,
-                                                     cr->y,
-                                                     cr->width,
-                                                     cr->height);
-        assert (status == CAIRO_STATUS_SUCCESS);
-    }
+    if (cr->path_is_rectangle)
+        assert (_flush_cr_rectangle (cr) == CAIRO_STATUS_SUCCESS);
 
     return cr->backend_parent.copy_path (abstract_cr);
 }
@@ -699,18 +663,9 @@ static cairo_path_t *
 _cairo_cogl_context_copy_path_flat (void *abstract_cr)
 {
     cairo_cogl_context_t *cr = abstract_cr;
-    cairo_status_t status;
 
-    if (cr->path_is_rectangle) {
-        /* We do not flush the rectangle, as we are not modifying the
-         * path */
-        status = _cairo_cogl_context_rectangle_real (cr,
-                                                     cr->x,
-                                                     cr->y,
-                                                     cr->width,
-                                                     cr->height);
-        assert (status == CAIRO_STATUS_SUCCESS);
-    }
+    if (cr->path_is_rectangle)
+        assert (_flush_cr_rectangle (cr) == CAIRO_STATUS_SUCCESS);
 
     return cr->backend_parent.copy_path_flat (abstract_cr);
 }
diff --git a/src/cairo-cogl-surface.c b/src/cairo-cogl-surface.c
index 09c6f2d00..9388189e2 100644
--- a/src/cairo-cogl-surface.c
+++ b/src/cairo-cogl-surface.c
@@ -64,8 +64,7 @@
 //#define DISABLE_BATCHING
 #define USE_COGL_RECTANGLE_API
 #define ENABLE_RECTANGLES_FASTPATH
-/* This hasn't been implemented yet */
-//#define ENABLE_CLIP_CACHE
+//#define ENABLE_CLIP_CACHE // This hasn't been implemented yet
 
 #if defined (USE_COGL_RECTANGLE_API) || defined (ENABLE_PATH_CACHE)
 #define NEED_COGL_CONTEXT
@@ -118,6 +117,10 @@ typedef struct _cairo_cogl_journal_rect_entry {
     float height;
     int n_layers;
     cairo_matrix_t ctm;
+    cairo_path_fixed_t *src_tex_clip;
+    cairo_path_fixed_t *mask_tex_clip;
+    cairo_operator_t op;
+    cairo_rectangle_int_t unbounded_extents;
 } cairo_cogl_journal_rect_entry_t;
 
 typedef struct _cairo_cogl_journal_prim_entry {
@@ -126,12 +129,20 @@ typedef struct _cairo_cogl_journal_prim_entry {
     CoglPrimitive *primitive;
     gboolean has_transform;
     cairo_matrix_t transform;
+    cairo_path_fixed_t *src_tex_clip;
+    cairo_path_fixed_t *mask_tex_clip;
+    cairo_operator_t op;
+    cairo_rectangle_int_t unbounded_extents;
 } cairo_cogl_journal_prim_entry_t;
 
 typedef struct _cairo_cogl_journal_path_entry {
     cairo_cogl_journal_entry_t base;
     CoglPipeline *pipeline;
     CoglPath *path;
+    cairo_path_fixed_t *src_tex_clip;
+    cairo_path_fixed_t *mask_tex_clip;
+    cairo_operator_t op;
+    cairo_rectangle_int_t unbounded_extents;
 } cairo_cogl_journal_path_entry_t;
 
 typedef struct _cairo_cogl_path_fill_meta {
@@ -334,24 +345,91 @@ _cairo_path_fixed_add_box (cairo_path_fixed_t *path,
 }
 
 static void
-_cairo_cogl_journal_free (cairo_cogl_surface_t *surface)
+_cairo_cogl_journal_discard (cairo_cogl_surface_t *surface)
 {
     GList *l;
+    cairo_cogl_device_t *dev;
+
+    if (!surface->journal) {
+	assert (surface->last_clip == NULL);
+	return;
+    }
+
+    dev = to_device(surface->base.device);
+    if (dev->buffer_stack && dev->buffer_stack_offset) {
+	cogl_buffer_unmap (dev->buffer_stack);
+	cogl_object_unref (dev->buffer_stack);
+	dev->buffer_stack = NULL;
+    }
 
     for (l = surface->journal->head; l; l = l->next) {
 	cairo_cogl_journal_entry_t *entry = l->data;
+	gsize entry_size;
 
-	if (entry->type == CAIRO_COGL_JOURNAL_ENTRY_TYPE_PRIMITIVE) {
+	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);
+            if (rect_entry->src_tex_clip)
+                _cairo_path_fixed_destroy (rect_entry->src_tex_clip);
+            if (rect_entry->mask_tex_clip)
+                _cairo_path_fixed_destroy (rect_entry->mask_tex_clip);
+	    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->primitive);
-	} else if (entry->type == CAIRO_COGL_JOURNAL_ENTRY_TYPE_PATH) {
+	    cogl_object_unref (prim_entry->pipeline);
+            if (prim_entry->primitive)
+	        cogl_object_unref (prim_entry->primitive);
+            if (prim_entry->src_tex_clip)
+                _cairo_path_fixed_destroy (prim_entry->src_tex_clip);
+            if (prim_entry->mask_tex_clip)
+                _cairo_path_fixed_destroy (prim_entry->mask_tex_clip);
+	    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);
+            if (path_entry->src_tex_clip)
+                _cairo_path_fixed_destroy (path_entry->src_tex_clip);
+            if (path_entry->mask_tex_clip)
+                _cairo_path_fixed_destroy (path_entry->mask_tex_clip);
+	    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 void
+_cairo_cogl_journal_free (cairo_cogl_surface_t *surface)
+{
+    _cairo_cogl_journal_discard (surface);
     g_queue_free (surface->journal);
     surface->journal = NULL;
 }
@@ -360,7 +438,11 @@ _cairo_cogl_journal_free (cairo_cogl_surface_t *surface)
 static void
 _cairo_cogl_journal_log_path (cairo_cogl_surface_t *surface,
 			      CoglPipeline *pipeline,
-			      CoglPath *path)
+			      CoglPath *path,
+                              cairo_path_fixed_t *src_tex_clip,
+                              cairo_path_fixed_t *mask_tex_clip,
+                              cairo_operator_t op
+                              cairo_rectangle_int_t unbounded_extents)
 {
     cairo_cogl_journal_path_entry_t *entry;
 
@@ -374,7 +456,11 @@ _cairo_cogl_journal_log_path (cairo_cogl_surface_t *surface,
     entry->base.type = CAIRO_COGL_JOURNAL_ENTRY_TYPE_PATH;
 
     entry->pipeline = cogl_object_ref (pipeline);
+    entry->src_tex_clip = src_tex_clip;
+    entry->mask_tex_clip = mask_tex_clip;
+    entry->op = op;
     entry->path = cogl_object_ref (path);
+    entry->unbounded_extents = unbounded_extents;
 
     g_queue_push_tail (surface->journal, entry);
 
@@ -388,7 +474,11 @@ static void
 _cairo_cogl_journal_log_primitive (cairo_cogl_surface_t *surface,
 				   CoglPipeline *pipeline,
 				   CoglPrimitive *primitive,
-				   cairo_matrix_t *transform)
+				   cairo_matrix_t *transform,
+                                   cairo_path_fixed_t *src_tex_clip,
+                                   cairo_path_fixed_t *mask_tex_clip,
+                                   cairo_operator_t op,
+                                   cairo_rectangle_int_t unbounded_extents)
 {
     cairo_cogl_journal_prim_entry_t *entry;
 
@@ -402,6 +492,10 @@ _cairo_cogl_journal_log_primitive (cairo_cogl_surface_t *surface,
     entry->base.type = CAIRO_COGL_JOURNAL_ENTRY_TYPE_PRIMITIVE;
 
     entry->pipeline = cogl_object_ref (pipeline);
+    entry->src_tex_clip = src_tex_clip;
+    entry->mask_tex_clip = mask_tex_clip;
+    entry->op = op;
+    entry->unbounded_extents = unbounded_extents;
 
     if (transform) {
 	entry->transform = *transform;
@@ -409,7 +503,9 @@ _cairo_cogl_journal_log_primitive (cairo_cogl_surface_t *surface,
     } else
 	entry->has_transform = FALSE;
 
-    entry->primitive = cogl_object_ref (primitive);
+    entry->primitive = primitive;
+    if (primitive)
+        cogl_object_ref (primitive);
 
     g_queue_push_tail (surface->journal, entry);
 
@@ -426,7 +522,11 @@ _cairo_cogl_journal_log_rectangle (cairo_cogl_surface_t *surface,
 				   float width,
 				   float height,
 				   int n_layers,
-				   cairo_matrix_t *ctm)
+				   cairo_matrix_t *ctm,
+                                   cairo_path_fixed_t *src_tex_clip,
+                                   cairo_path_fixed_t *mask_tex_clip,
+                                   cairo_operator_t op,
+                                   cairo_rectangle_int_t unbounded_extents)
 {
     cairo_cogl_journal_rect_entry_t *entry;
 
@@ -446,6 +546,10 @@ _cairo_cogl_journal_log_rectangle (cairo_cogl_surface_t *surface,
     entry->width = width;
     entry->height = height;
     entry->ctm = *ctm;
+    entry->src_tex_clip = src_tex_clip;
+    entry->mask_tex_clip = mask_tex_clip;
+    entry->op = op;
+    entry->unbounded_extents = unbounded_extents;
 
     entry->n_layers = n_layers;
 
@@ -475,75 +579,6 @@ _cairo_cogl_journal_log_clip (cairo_cogl_surface_t *surface,
     g_queue_push_tail (surface->journal, entry);
 }
 
-static void
-_cairo_cogl_journal_discard (cairo_cogl_surface_t *surface)
-{
-    GList *l;
-    cairo_cogl_device_t *dev;
-
-    if (!surface->journal) {
-	assert (surface->last_clip == NULL);
-	return;
-    }
-
-    dev = to_device(surface->base.device);
-    if (dev->buffer_stack && dev->buffer_stack_offset) {
-	cogl_buffer_unmap (dev->buffer_stack);
-	cogl_object_unref (dev->buffer_stack);
-	dev->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_device_allocate_buffer_space (cairo_cogl_device_t *dev,
                                           size_t size,
@@ -788,14 +823,11 @@ static void
 _cairo_cogl_set_path_prim_clip (cairo_cogl_surface_t *surface,
                                 cairo_path_fixed_t *path,
                                 int *clip_stack_depth,
-#ifdef ENABLE_CLIP_CACHE
-                                cairo_bool_t *checked_for_primitives,
-                                cairo_cogl_clip_primitives_t *clip_primitives;
-#endif
                                 cairo_fill_rule_t fill_rule,
                                 double tolerance)
 {
     cairo_rectangle_int_t extents;
+    cairo_int_status_t status;
     CoglPrimitive *prim;
     size_t prim_size;
 
@@ -806,35 +838,286 @@ _cairo_cogl_set_path_prim_clip (cairo_cogl_surface_t *surface,
      * uploading the vertices...
      */
 #ifdef ENABLE_CLIP_CACHE
-    if (!*checked_for_primitives) {
-        clip_primitives = find_clip_primitives (clip);
-        checked_for_primitives = TRUE;
-    }
-    if (clip_primitives)
-        prim = clip_primitives->primitives[i];
+    prim = NULL;
+    prim = find_clip_path_primitive (path);
+    if (prim)
+        // then bypass filling
 #endif
 
-    if (unlikely (_cairo_cogl_fill_to_primitive (surface,
-                                                 path,
-                                                 fill_rule,
-                                                 tolerance,
-                                                 0,
-                                                 FALSE,
-                                                 &prim,
-                                                 &prim_size)))
-    {
+    status = _cairo_cogl_fill_to_primitive (surface,
+                                            path,
+                                            fill_rule,
+                                            tolerance,
+                                            0,
+                                            FALSE,
+                                            &prim,
+                                            &prim_size);
+    if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
+        /* If the clip is of zero fill area, set all clipped */
+        cogl_framebuffer_push_scissor_clip (surface->framebuffer,
+                                            0, 0, 0, 0);
+        (*clip_stack_depth)++;
+        return;
+    } else if (unlikely (status)) {
         g_warning ("Failed to get primitive for clip path while flushing journal");
         goto BAIL;
     }
 
-    (*clip_stack_depth)++;
     cogl_framebuffer_push_primitive_clip (surface->framebuffer, prim,
                                           extents.x, extents.y,
                                           extents.x + extents.width,
                                           extents.y + extents.height);
+    (*clip_stack_depth)++;
 
 BAIL:
-    cogl_object_unref (prim);
+    if (prim)
+        cogl_object_unref (prim);
+}
+
+/* This is the way in which we handle CAIRO_EXTEND_NONE set on the
+ * source or mask pattern surfaces, as well as unbounded operators.
+ * First, we limit the rendering area to the region which will not be
+ * sampled from beyond the source or mask textures with additional clip
+ * paths, which were created when we obtained the original pipeline.
+ * The region will also be limited by the drawing area due to the fact
+ * we are drawing with the original primitive's vertices.
+ *
+ * In order to handle unbounded operators, we do a second rendering pass
+ * for places outside of such region. We limit the rending to outside
+ * this region by using a depth buffer to preserve all places where
+ * rendering took place during the first pass. For this region, we also
+ * have to remove the CAIRO_EXTEND_NONE clips if the operator is not
+ * bound by their respective contents. Because OpenGL sets all vertex
+ * z-values to 0.0 if none are supplied in the attributes data (we only
+ * supply x and y values), it will update the region in the buffer to a
+ * value over the default clearing value of 1.0. Given that the default
+ * test function is GL_LESS, we don't have to set z attributes on the
+ * vertices of the second rendering pass either, as 0.0 will never be
+ * less than 0.0. If cogl ever adds a method to clip out a primitive
+ * instead of just clipping it in, we may be able to use a more
+ * efficient method using the stencil buffer. */
+static void
+_cairo_cogl_apply_tex_clips (cairo_cogl_surface_t *surface,
+                             int *clip_stack_depth,
+                             CoglPipeline *pipeline,
+                             cairo_path_fixed_t *src_tex_clip,
+                             cairo_path_fixed_t *mask_tex_clip,
+                             cairo_operator_t op)
+{
+    CoglDepthState depth_state;
+    CoglBool cogl_status;
+    uint32_t op_bounded = _cairo_operator_bounded_by_either (op);
+
+    /* Enable the depth test if it will be needed */
+    if ((_cairo_operator_bounded_by_mask(op) == FALSE && mask_tex_clip)
+        || (_cairo_operator_bounded_by_source(op) == FALSE && src_tex_clip))
+    {
+        cogl_depth_state_init (&depth_state);
+        cogl_depth_state_set_test_enabled (&depth_state, TRUE);
+        cogl_status = cogl_pipeline_set_depth_state (pipeline,
+                                                     &depth_state,
+                                                     NULL);
+        if (cogl_status != TRUE)
+            g_warning ("Error setting depth state for unbounded render");
+
+        /* Clear the depth buffer to 1.0. The color values are unused
+         * placeholders. */
+        cogl_framebuffer_clear4f (surface->framebuffer,
+                                  COGL_BUFFER_BIT_DEPTH,
+                                  0.0, 0.0, 0.0, 0.0);
+    }
+
+    switch (op_bounded) {
+    case CAIRO_OPERATOR_BOUND_BY_MASK | CAIRO_OPERATOR_BOUND_BY_SOURCE:
+    case CAIRO_OPERATOR_BOUND_BY_SOURCE:
+    case 0:
+        if (src_tex_clip)
+            _cairo_cogl_set_path_prim_clip (surface,
+                                            src_tex_clip,
+                                            clip_stack_depth,
+                                            CAIRO_FILL_RULE_WINDING,
+                                            0.0);
+        if (mask_tex_clip)
+            _cairo_cogl_set_path_prim_clip (surface,
+                                            mask_tex_clip,
+                                            clip_stack_depth,
+                                            CAIRO_FILL_RULE_WINDING,
+                                            0.0);
+        break;
+    case CAIRO_OPERATOR_BOUND_BY_MASK:
+        /* Push mask clip first so later we can pop the source clip 
+         * and still be bound by the mask clip */
+        if (mask_tex_clip)
+            _cairo_cogl_set_path_prim_clip (surface,
+                                            mask_tex_clip,
+                                            clip_stack_depth,
+                                            CAIRO_FILL_RULE_WINDING,
+                                            0.0);
+        if (src_tex_clip)
+            _cairo_cogl_set_path_prim_clip (surface,
+                                            src_tex_clip,
+                                            clip_stack_depth,
+                                            CAIRO_FILL_RULE_WINDING,
+                                            0.0);
+        break;
+    default:
+        assert (0); // not reached
+    }
+}
+
+/* Get the pipeline for the second pass */
+static CoglPipeline *
+_cairo_cogl_setup_unbounded_area_pipeline (cairo_cogl_surface_t *surface,
+                                           cairo_operator_t op)
+{
+    CoglPipeline *unbounded_pipeline;
+    CoglColor zero_color;
+    CoglDepthState depth_state;
+    CoglBool cogl_status;
+    cairo_cogl_device_t *dev = to_device(surface->base.device);
+
+    /* If a template pipeline exists for any given operator, the
+     * corresponding solid template pipeline always exists */
+    unbounded_pipeline =
+        cogl_pipeline_copy (dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID]);
+    cogl_color_init_from_4f (&zero_color, 0.0, 0.0, 0.0, 0.0);
+    cogl_pipeline_set_color (unbounded_pipeline, &zero_color);
+
+    /* Enable depth test on second-pass pipeline */
+    cogl_depth_state_init (&depth_state);
+    cogl_depth_state_set_test_enabled (&depth_state, TRUE);
+    cogl_status = cogl_pipeline_set_depth_state (unbounded_pipeline,
+                                                 &depth_state,
+                                                 NULL);
+    if (cogl_status != TRUE)
+        g_warning ("Error setting depth state for unbounded render");
+
+    return unbounded_pipeline;
+}
+
+static void
+_cairo_cogl_unbounded_render (cairo_cogl_surface_t *surface,
+                              int *clip_stack_depth,
+                              CoglPipeline *pipeline,
+                              cairo_path_fixed_t *src_tex_clip,
+                              cairo_path_fixed_t *mask_tex_clip,
+                              cairo_operator_t op,
+                              cairo_rectangle_int_t unbounded_extents,
+                              cairo_bool_t *needs_vertex_render)
+{
+    uint32_t op_bounded = _cairo_operator_bounded_by_either (op);
+
+    /* We will need a second rendering of the original vertices if we
+     * still need to be bounded by the mask but had a source tex clip */
+    *needs_vertex_render = FALSE;
+
+    /* Pop all unbounded tex clips. Do not pop clips the operator is
+     * bounded by, so that we can still be bounded by them during the
+     * second pass (vertex render or extents render). */
+    switch (op_bounded) {
+    case CAIRO_OPERATOR_BOUND_BY_MASK | CAIRO_OPERATOR_BOUND_BY_SOURCE:
+        /* We don't need a second pass if it will just be in the same
+         * region as the first */
+        break;
+    case CAIRO_OPERATOR_BOUND_BY_SOURCE:
+        if (mask_tex_clip) {
+            cogl_framebuffer_pop_clip (surface->framebuffer);
+            (*clip_stack_depth)--;
+        }
+        break;
+    case 0:
+        if (src_tex_clip) {
+            cogl_framebuffer_pop_clip (surface->framebuffer);
+            (*clip_stack_depth)--;
+        }
+        if (mask_tex_clip) {
+            cogl_framebuffer_pop_clip (surface->framebuffer);
+            (*clip_stack_depth)--;
+        }
+        break;
+    case CAIRO_OPERATOR_BOUND_BY_MASK:
+        if (src_tex_clip) {
+            cogl_framebuffer_pop_clip (surface->framebuffer);
+            (*clip_stack_depth)--;
+            *needs_vertex_render = TRUE;
+        }
+        break;
+    default:
+        assert (0); // not reached
+    }
+
+    /* If an operator is unbounded by the mask, we need to render the
+     * second transparent pass within the full unbounded extents */
+    if (!(op_bounded & CAIRO_OPERATOR_BOUND_BY_MASK)) {
+        CoglPipeline *unbounded_pipeline;
+
+        /* Draw a transparent rectangle to cover the entire extents */
+        unbounded_pipeline =
+            _cairo_cogl_setup_unbounded_area_pipeline (surface, op);
+        cogl_framebuffer_draw_rectangle (surface->framebuffer,
+                                         unbounded_pipeline,
+                                         unbounded_extents.x,
+                                         unbounded_extents.y,
+                                         unbounded_extents.x + unbounded_extents.width,
+                                         unbounded_extents.y + unbounded_extents.height);
+        cogl_object_unref (unbounded_pipeline);
+    }
+}
+
+static void
+_cairo_cogl_post_unbounded_render (cairo_cogl_surface_t *surface,
+                                   int *clip_stack_depth,
+                                   CoglPipeline *pipeline,
+                                   cairo_path_fixed_t *src_tex_clip,
+                                   cairo_path_fixed_t *mask_tex_clip,
+                                   cairo_operator_t op)
+{
+    CoglDepthState depth_state;
+    CoglBool cogl_status;
+    uint32_t op_bounded = _cairo_operator_bounded_by_either (op);
+
+    /* Disable the depth test */
+    if ((_cairo_operator_bounded_by_mask(op) == FALSE && mask_tex_clip)
+        || (_cairo_operator_bounded_by_source(op) == FALSE && src_tex_clip))
+    {
+        cogl_depth_state_init (&depth_state);
+        cogl_depth_state_set_test_enabled (&depth_state, FALSE);
+        cogl_status = cogl_pipeline_set_depth_state (pipeline,
+                                                     &depth_state,
+                                                     NULL);
+        if (cogl_status != TRUE)
+            g_warning ("Error setting depth state after unbounded render");
+    }
+
+    /* Pop all bounded tex clips (those that were not popped before) */
+    switch (op_bounded) {
+    case CAIRO_OPERATOR_BOUND_BY_MASK | CAIRO_OPERATOR_BOUND_BY_SOURCE:
+        if (src_tex_clip) {
+            cogl_framebuffer_pop_clip (surface->framebuffer);
+            (*clip_stack_depth)--;
+        }
+        if (mask_tex_clip) {
+            cogl_framebuffer_pop_clip (surface->framebuffer);
+            (*clip_stack_depth)--;
+        }
+        break;
+    case CAIRO_OPERATOR_BOUND_BY_SOURCE:
+        if (src_tex_clip) {
+            cogl_framebuffer_pop_clip (surface->framebuffer);
+            (*clip_stack_depth)--;
+        }
+        break;
+    case 0:
+        break;
+    case CAIRO_OPERATOR_BOUND_BY_MASK:
+        if (mask_tex_clip) {
+            cogl_framebuffer_pop_clip (surface->framebuffer);
+            (*clip_stack_depth)--;
+        }
+        break;
+    default:
+        assert (0); // not reached
+    }
 }
 
 static void
@@ -871,10 +1154,6 @@ _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface)
 	    cairo_cogl_journal_clip_entry_t *clip_entry =
 		(cairo_cogl_journal_clip_entry_t *)entry;
 	    cairo_clip_path_t *path;
-#ifdef ENABLE_CLIP_CACHE
-	    cairo_bool_t checked_for_primitives = FALSE;
-	    cairo_cogl_clip_primitives_t *clip_primitives;
-#endif
 
 	    for (i = 0; i < clip_stack_depth; i++)
 		cogl_framebuffer_pop_clip (surface->framebuffer);
@@ -890,10 +1169,6 @@ _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface)
                 _cairo_cogl_set_path_prim_clip (surface,
                                                 &path->path,
                                                 &clip_stack_depth,
-#ifdef ENABLE_CLIP_CACHE
-                                                &checked_for_primitives,
-                                                clip_primitives,
-#endif
                                                 path->fill_rule,
                                                 path->tolerance);
             }
@@ -915,12 +1190,8 @@ _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface)
                 _cairo_cogl_set_path_prim_clip (surface,
                                                 &boxes_path,
                                                 &clip_stack_depth,
-#ifdef ENABLE_CLIP_CACHE
-                                                &checked_for_primitives,
-                                                clip_primitives,
-#endif
                                                 CAIRO_FILL_RULE_WINDING,
-                                                0.);
+                                                0.0);
 
                 _cairo_path_fixed_fini (&boxes_path);
 	    }
@@ -944,9 +1215,18 @@ _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface)
 		ctm->x0, ctm->y0, 0, 1
 	    };
 	    CoglMatrix transform;
+            cairo_bool_t needs_vertex_render;
+            CoglPipeline *unbounded_pipeline;
 
 	    cogl_matrix_init_from_array (&transform, ctmfv);
 
+            _cairo_cogl_apply_tex_clips (surface,
+                                         &clip_stack_depth,
+                                         rect_entry->pipeline,
+                                         rect_entry->src_tex_clip,
+                                         rect_entry->mask_tex_clip,
+                                         rect_entry->op);
+
 	    if (rect_entry->n_layers) {
 		g_assert (rect_entry->n_layers <= 2);
 		tex_coords[0] = x1;
@@ -965,6 +1245,34 @@ _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface)
                                                            x2, y2,
 						           tex_coords,
                                                            4 * rect_entry->n_layers);
+
+            _cairo_cogl_unbounded_render (surface,
+                                          &clip_stack_depth,
+                                          rect_entry->pipeline,
+                                          rect_entry->src_tex_clip,
+                                          rect_entry->mask_tex_clip,
+                                          rect_entry->op,
+                                          rect_entry->unbounded_extents,
+                                          &needs_vertex_render);
+            if (needs_vertex_render) {
+                unbounded_pipeline =
+                    _cairo_cogl_setup_unbounded_area_pipeline (surface,
+                                                               rect_entry->op);
+                cogl_framebuffer_draw_multitextured_rectangle (surface->framebuffer,
+                                                               rect_entry->pipeline,
+                                                               x1, y1,
+                                                               x2, y2,
+						               tex_coords,
+                                                               4 * rect_entry->n_layers);
+                cogl_object_unref (unbounded_pipeline);
+            }
+            _cairo_cogl_post_unbounded_render (surface,
+                                               &clip_stack_depth,
+                                               rect_entry->pipeline,
+                                               rect_entry->src_tex_clip,
+                                               rect_entry->mask_tex_clip,
+                                               rect_entry->op);
+
 	    cogl_framebuffer_pop_matrix (surface->framebuffer);
 	    break;
 	}
@@ -972,6 +1280,15 @@ _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface)
 	    cairo_cogl_journal_prim_entry_t *prim_entry =
 		(cairo_cogl_journal_prim_entry_t *)entry;
 	    CoglMatrix transform;
+            cairo_bool_t needs_vertex_render;
+            CoglPipeline *unbounded_pipeline;
+
+            _cairo_cogl_apply_tex_clips (surface,
+                                         &clip_stack_depth,
+                                         prim_entry->pipeline,
+                                         prim_entry->src_tex_clip,
+                                         prim_entry->mask_tex_clip,
+                                         prim_entry->op);
 
 	    cogl_framebuffer_push_matrix (surface->framebuffer);
 	    if (prim_entry->has_transform) {
@@ -989,21 +1306,85 @@ _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface)
 		cogl_framebuffer_set_modelview_matrix (surface->framebuffer, &transform);
 	    }
 
-	    cogl_primitive_draw (prim_entry->primitive,
-                                 surface->framebuffer,
-                                 prim_entry->pipeline);
+            /* If the primitive is NULL, it means we just draw the
+             * unbounded rectangle */
+            if (prim_entry->primitive)
+	        cogl_primitive_draw (prim_entry->primitive,
+                                     surface->framebuffer,
+                                     prim_entry->pipeline);
+
+            _cairo_cogl_unbounded_render (surface,
+                                          &clip_stack_depth,
+                                          prim_entry->pipeline,
+                                          prim_entry->src_tex_clip,
+                                          prim_entry->mask_tex_clip,
+                                          prim_entry->op,
+                                          prim_entry->unbounded_extents,
+                                          &needs_vertex_render);
+            if (needs_vertex_render) {
+                unbounded_pipeline =
+                    _cairo_cogl_setup_unbounded_area_pipeline (surface,
+                                                               prim_entry->op);
+                cogl_primitive_draw (prim_entry->primitive,
+                                     surface->framebuffer,
+                                     unbounded_pipeline);
+                cogl_object_unref (unbounded_pipeline);
+            }
+            _cairo_cogl_post_unbounded_render (surface,
+                                               &clip_stack_depth,
+                                               prim_entry->pipeline,
+                                               prim_entry->src_tex_clip,
+                                               prim_entry->mask_tex_clip,
+                                               prim_entry->op);
+
 	    cogl_framebuffer_pop_matrix (surface->framebuffer);
 	    break;
 	}
 	case CAIRO_COGL_JOURNAL_ENTRY_TYPE_PATH: {
 	    cairo_cogl_journal_path_entry_t *path_entry =
 		(cairo_cogl_journal_path_entry_t *)entry;
+            cairo_bool_t needs_vertex_render;
+            CoglPipeline *unbounded_pipeline;
+
+            _cairo_cogl_apply_tex_clips (surface,
+                                         &clip_stack_depth,
+                                         path_entry->pipeline,
+                                         path_entry->src_tex_clip,
+                                         path_entry->mask_tex_clip,
+                                         path_entry->op);
 
             /* Use this until cogl2_path_fill is updated to take
              * framebuffer and pipeline arguments */
             cogl_framebuffer_fill_path (surface->framebuffer,
                                         path_entry->pipeline,
                                         path_entry->path);
+
+            _cairo_cogl_unbounded_render (surface,
+                                          &clip_stack_depth,
+                                          path_entry->pipeline,
+                                          path_entry->src_tex_clip,
+                                          path_entry->mask_tex_clip,
+                                          path_entry->op,
+                                          path_entry->unbounded_extents,
+                                          &needs_vertex_render);
+            if (needs_vertex_render) {
+                unbounded_pipeline =
+                    _cairo_cogl_setup_unbounded_area_pipeline (surface,
+                                                               path_entry->op);
+                cogl_framebuffer_fill_path (surface->framebuffer,
+                                            unbounded_pipeline,
+                                            path_entry->path);
+                cogl_object_unref (unbounded_pipeline);
+            }
+            _cairo_cogl_post_unbounded_render (surface,
+                                               &clip_stack_depth,
+                                               path_entry->pipeline,
+                                               path_entry->src_tex_clip,
+                                               path_entry->mask_tex_clip,
+                                               path_entry->op);
+
+	    cogl_framebuffer_pop_matrix (surface->framebuffer);
+
 	    break;
 	}
 	default:
@@ -1333,11 +1714,7 @@ _cairo_cogl_path_fixed_rectangle (cairo_path_fixed_t *path,
     if (unlikely (status))
 	return status;
 
-    status = _cairo_path_fixed_close_path (path);
-    if (unlikely (status))
-	return status;
-
-    return CAIRO_STATUS_SUCCESS;
+    return _cairo_path_fixed_close_path (path);
 }
 
 static CoglPipelineWrapMode
@@ -1578,32 +1955,24 @@ BAIL:
 }
 
 static cairo_status_t
-_cairo_cogl_create_tex_clip (cairo_clip_t **tex_clip,
+_cairo_cogl_create_tex_clip (cairo_path_fixed_t **tex_clip,
                              cairo_matrix_t inverse)
 {
-    cairo_path_fixed_t path;
     cairo_status_t status;
 
-    _cairo_path_fixed_init (&path);
     status = cairo_matrix_invert (&inverse);
     if (unlikely (status))
         return status;
 
-    status = _cairo_cogl_path_fixed_rectangle (&path, 0, 0,
+    *tex_clip = _cairo_path_fixed_create ();
+    status = _cairo_cogl_path_fixed_rectangle (*tex_clip, 0, 0,
                                                CAIRO_FIXED_ONE,
                                                CAIRO_FIXED_ONE);
     if (unlikely (status))
         return status;
 
-    _cairo_path_fixed_transform (&path, &inverse);
+    _cairo_path_fixed_transform (*tex_clip, &inverse);
 
-    *tex_clip = _cairo_clip_intersect_path (NULL,
-                                            &path,
-                                            CAIRO_FILL_RULE_WINDING,
-                                            1,
-                                            CAIRO_ANTIALIAS_DEFAULT);
-
-    _cairo_path_fixed_fini (&path);
     return CAIRO_STATUS_SUCCESS;
 }
 
@@ -1614,7 +1983,7 @@ _cairo_cogl_acquire_pattern_texture (const cairo_pattern_t *pattern,
 				     cairo_cogl_surface_t *destination,
 				     const cairo_rectangle_int_t *extents,
 				     cairo_cogl_texture_attributes_t *attributes,
-                                     cairo_clip_t **tex_clip)
+                                     cairo_path_fixed_t **tex_clip)
 {
     CoglTexture *texture = NULL;
     cairo_bool_t has_pre_transform;
@@ -1827,7 +2196,7 @@ _cairo_cogl_setup_op_state (CoglPipeline *pipeline, cairo_operator_t op)
      * replace the source alpha with ones or the mask alpha when we do
      * the rest of the pipeline setup. Note that this doesn't actually
      * give the right result alpha value, which would be directly
-     * copied in a true SORUCE operation. */
+     * copied in a true SOURCE operation. */
     case CAIRO_OPERATOR_SOURCE:
     case CAIRO_OPERATOR_OVER:
 	status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR, DST_COLOR * (1 - SRC_COLOR[A]))");
@@ -2018,15 +2387,18 @@ get_source_mask_operator_destination_pipeline (const cairo_pattern_t *mask,
 					       cairo_operator_t op,
 					       cairo_cogl_surface_t *destination,
 					       cairo_composite_rectangles_t *extents,
-                                               cairo_clip_t **tex_clip)
+                                               cairo_path_fixed_t **src_tex_clip,
+                                               cairo_path_fixed_t **mask_tex_clip)
 {
     cairo_cogl_template_type template_type;
     CoglPipeline *pipeline;
     cairo_cogl_device_t *dev = to_device(destination->base.device);
     int layer_counter = 0;
 
-    if (tex_clip)
-        *tex_clip = NULL;
+    if (src_tex_clip)
+        *src_tex_clip = NULL;
+    if (mask_tex_clip)
+        *mask_tex_clip = NULL;
 
     switch ((int)source->type)
     {
@@ -2083,7 +2455,7 @@ get_source_mask_operator_destination_pipeline (const cairo_pattern_t *mask,
 	    _cairo_cogl_acquire_pattern_texture (source, destination,
 						 &extents->bounded,
 						 &attributes,
-                                                 tex_clip);
+                                                 src_tex_clip);
 	if (!texture)
 	    goto BAIL;
 	set_layer_texture_with_attributes (pipeline,
@@ -2107,15 +2479,11 @@ get_source_mask_operator_destination_pipeline (const cairo_pattern_t *mask,
                                                       &color);
 	} else {
 	    cairo_cogl_texture_attributes_t attributes;
-            /* We don't properly support CAIRO_EXTEND_NONE on mask
-             * textures. However, if this ends up being an issue, we
-             * can pass get_source_mask_operator_destination_pipeline
-             * two clip pointers. */
 	    CoglTexture *texture =
 		_cairo_cogl_acquire_pattern_texture (mask, destination,
 						     &extents->bounded,
 						     &attributes,
-                                                     NULL);
+                                                     mask_tex_clip);
 	    if (!texture)
 		goto BAIL;
 	    set_layer_texture_with_attributes (pipeline,
@@ -2262,16 +2630,16 @@ _cairo_cogl_surface_paint (void                  *abstract_surface,
                            const cairo_clip_t    *clip)
 {
     cairo_cogl_surface_t *surface;
-    cairo_status_t status;
+    cairo_int_status_t status;
     cairo_matrix_t identity;
     CoglPipeline *pipeline;
     cairo_composite_rectangles_t extents;
-    cairo_clip_t *tex_clip;
+    cairo_path_fixed_t *src_tex_clip;
 
     if (clip == NULL) {
         status = _cairo_cogl_surface_ensure_framebuffer (abstract_surface);
         if (unlikely (status))
-            goto BAIL;
+            return status;
 
 	if (op == CAIRO_OPERATOR_CLEAR)
             return _cairo_cogl_surface_clear (abstract_surface, CAIRO_COLOR_TRANSPARENT);
@@ -2305,22 +2673,12 @@ _cairo_cogl_surface_paint (void                  *abstract_surface,
                                                        op,
                                                        surface,
                                                        &extents,
-                                                       &tex_clip);
+                                                       &src_tex_clip,
+                                                       NULL);
     if (!pipeline) {
-	status = CAIRO_INT_STATUS_UNSUPPORTED;
-	goto BAIL;
-    }
-
-    if (tex_clip) {
-        tex_clip = _cairo_clip_intersect_clip (tex_clip, clip);
-        status =
-            _cairo_composite_rectangles_init_for_paint (&extents,
-                                                        &surface->base,
-                                                        op,
-                                                        source,
-                                                        clip);
-        if (unlikely (status))
-            goto BAIL;
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+        if (src_tex_clip)
+            _cairo_path_fixed_destroy (src_tex_clip);
     }
 
     _cairo_cogl_maybe_log_clip (surface, &extents);
@@ -2332,16 +2690,16 @@ _cairo_cogl_surface_paint (void                  *abstract_surface,
 				       extents.bounded.width,
 				       extents.bounded.height,
 				       _cairo_cogl_source_n_layers (source),
-				       &identity);
+				       &identity,
+                                       src_tex_clip,
+                                       NULL,
+                                       op,
+                                       extents.unbounded);
 
-    /* The journal will take a reference on the pipeline and clip_path... */
+    /* The journal will take a reference on the pipeline... */
     cogl_object_unref (pipeline);
 
-BAIL:
-    if (tex_clip)
-        _cairo_clip_destroy (tex_clip);
-
-    return status;
+    return CAIRO_STATUS_SUCCESS;
 }
 
 static cairo_int_status_t
@@ -2353,10 +2711,10 @@ _cairo_cogl_surface_mask (void                    *abstract_surface,
 {
     cairo_cogl_surface_t *surface = abstract_surface;
     cairo_composite_rectangles_t extents;
-    cairo_status_t status;
+    cairo_int_status_t status;
     CoglPipeline *pipeline;
     cairo_matrix_t identity;
-    cairo_clip_t *tex_clip;
+    cairo_path_fixed_t *src_tex_clip, *mask_tex_clip;
 
     /* XXX: Use this to smoke test the acquire_source/dest_image fallback
      * paths... */
@@ -2377,23 +2735,14 @@ _cairo_cogl_surface_mask (void                    *abstract_surface,
                                                        op,
                                                        surface,
                                                        &extents,
-                                                       &tex_clip);
+                                                       &src_tex_clip,
+                                                       &mask_tex_clip);
     if (!pipeline) {
-	status = CAIRO_INT_STATUS_UNSUPPORTED;
-	goto BAIL;
-    }
-
-    if (tex_clip) {
-        tex_clip = _cairo_clip_intersect_clip (tex_clip, clip);
-        status =
-            _cairo_composite_rectangles_init_for_mask (&extents,
-                                                       &surface->base,
-                                                       op,
-                                                       source,
-                                                       mask,
-                                                       clip);
-        if (unlikely (status))
-            goto BAIL;
+        if (src_tex_clip)
+            _cairo_path_fixed_destroy (src_tex_clip);
+        if (mask_tex_clip)
+            _cairo_path_fixed_destroy (mask_tex_clip);
+	return CAIRO_INT_STATUS_UNSUPPORTED;
     }
 
     _cairo_cogl_maybe_log_clip (surface, &extents);
@@ -2404,17 +2753,17 @@ _cairo_cogl_surface_mask (void                    *abstract_surface,
 				       extents.bounded.y,
 				       extents.bounded.width,
 				       extents.bounded.height,
-				       2,
-				       &identity);
+				       _cairo_cogl_source_n_layers (source) + 1,
+				       &identity,
+                                       src_tex_clip,
+                                       mask_tex_clip,
+                                       op,
+                                       extents.unbounded);
 
-    /* The journal will take a reference on the pipeline and clip_path... */
+    /* The journal will take a reference on the pipeline... */
     cogl_object_unref (pipeline);
 
-BAIL:
-    if (tex_clip)
-        _cairo_clip_destroy (tex_clip);
-
-    return status;
+    return CAIRO_STATUS_SUCCESS;
 }
 
 static cairo_bool_t
@@ -2656,7 +3005,7 @@ _cairo_cogl_surface_stroke (void                       *abstract_surface,
     cairo_cogl_surface_t *surface = (cairo_cogl_surface_t *)abstract_surface;
     cairo_composite_rectangles_t extents;
     CoglPipeline *pipeline;
-    cairo_status_t status;
+    cairo_int_status_t status;
 #ifdef ENABLE_PATH_CACHE
     cairo_cogl_path_stroke_meta_t *meta = NULL;
     cairo_matrix_t transform_matrix;
@@ -2665,7 +3014,7 @@ _cairo_cogl_surface_stroke (void                       *abstract_surface,
     gboolean one_shot = TRUE;
     CoglPrimitive *prim = NULL;
     cairo_bool_t new_prim = FALSE;
-    cairo_clip_t *tex_clip;
+    cairo_path_fixed_t *src_tex_clip;
 
     if (! is_operator_supported (op))
 	return CAIRO_INT_STATUS_UNSUPPORTED;
@@ -2700,11 +3049,16 @@ _cairo_cogl_surface_stroke (void                       *abstract_surface,
 						  ctm, ctm_inverse, tolerance,
 						  n_layers, one_shot,
 						  &prim, &prim_size);
-	if (unlikely (status))
+        if (status == CAIRO_INT_STATUS_NOTHING_TO_DO
+            && _cairo_operator_bounded_by_mask (op) == FALSE) {
+            /* Just render the unbounded rectangle */
+            prim = NULL;
+	} else if (unlikely (status))
 	    return status;
-	new_prim = TRUE;
+        else
+            new_prim = TRUE;
 #if defined (ENABLE_PATH_CACHE)
-	if (meta) {
+	if (meta && prim) {
 	    meta->prim = cogl_object_ref (prim);
 	    _cairo_cogl_path_stroke_meta_set_prim_size (surface, meta, prim_size);
 	}
@@ -2717,31 +3071,30 @@ _cairo_cogl_surface_stroke (void                       *abstract_surface,
                                                        op,
                                                        surface,
                                                        &extents,
-                                                       &tex_clip);
+                                                       &src_tex_clip,
+                                                       NULL);
     if (!pipeline) {
-	status = CAIRO_INT_STATUS_UNSUPPORTED;
-        goto BAIL;
+        if (src_tex_clip)
+            _cairo_path_fixed_destroy (src_tex_clip);
+        return CAIRO_INT_STATUS_UNSUPPORTED;
     }
 
-    if (tex_clip) {
-        tex_clip = _cairo_clip_intersect_clip (tex_clip, clip);
-        _cairo_cogl_log_clip (surface, tex_clip);
-    } else {
-        _cairo_cogl_maybe_log_clip (surface, &extents);
-    }
-
-    _cairo_cogl_journal_log_primitive (surface, pipeline, prim, transform);
+    _cairo_cogl_maybe_log_clip (surface, &extents);
 
+    _cairo_cogl_journal_log_primitive (surface,
+                                       pipeline,
+                                       prim,
+                                       transform,
+                                       src_tex_clip,
+                                       NULL,
+                                       op,
+                                       extents.unbounded);
     /* The journal will take a reference on the pipeline and primitive... */
     cogl_object_unref (pipeline);
     if (new_prim)
 	cogl_object_unref (prim);
 
-BAIL:
-    if (tex_clip)
-        _cairo_clip_destroy (tex_clip);
-
-    return status;
+    return CAIRO_STATUS_SUCCESS;
 }
 
 static cairo_cogl_path_fill_meta_t *
@@ -2873,7 +3226,7 @@ _cairo_cogl_surface_fill (void			    *abstract_surface,
 {
     cairo_cogl_surface_t *surface = abstract_surface;
     cairo_composite_rectangles_t extents;
-    cairo_status_t status;
+    cairo_int_status_t status;
 #ifdef ENABLE_PATH_CACHE
     cairo_cogl_path_fill_meta_t *meta = NULL;
     cairo_matrix_t transform_matrix;
@@ -2883,7 +3236,7 @@ _cairo_cogl_surface_fill (void			    *abstract_surface,
     CoglPrimitive *prim = NULL;
     cairo_bool_t new_prim = FALSE;
     CoglPipeline *pipeline;
-    cairo_clip_t *tex_clip;
+    cairo_path_fixed_t *src_tex_clip;
 
     if (! is_operator_supported (op))
 	return CAIRO_INT_STATUS_UNSUPPORTED;
@@ -2911,13 +3264,19 @@ _cairo_cogl_surface_fill (void			    *abstract_surface,
     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))
+        if (status == CAIRO_INT_STATUS_NOTHING_TO_DO
+            && _cairo_operator_bounded_by_mask (op) == FALSE) {
+            /* Just render the unbounded rectangle */
+            prim = NULL;
+	} else if (unlikely (status))
 	    return status;
-	new_prim = TRUE;
+        else
+            new_prim = TRUE;
 #ifdef ENABLE_PATH_CACHE
-	if (meta) {
+	if (meta && prim) {
 	    meta->prim = cogl_object_ref (prim);
 	    _cairo_cogl_path_fill_meta_set_prim_size (surface, meta, prim_size);
 	}
@@ -2932,38 +3291,44 @@ _cairo_cogl_surface_fill (void			    *abstract_surface,
                                                        op,
                                                        surface,
                                                        &extents,
-                                                       &tex_clip);
+                                                       &src_tex_clip,
+                                                       NULL);
     if (!pipeline) {
-	status = CAIRO_INT_STATUS_UNSUPPORTED;
-        goto BAIL;
+        if (src_tex_clip)
+            _cairo_path_fixed_destroy (src_tex_clip);
+	return CAIRO_INT_STATUS_UNSUPPORTED;
     }
 
-    if (tex_clip) {
-        tex_clip = _cairo_clip_intersect_clip (tex_clip, clip);
-        _cairo_cogl_log_clip (surface, tex_clip);
-    } else {
-        _cairo_cogl_maybe_log_clip (surface, &extents);
-    }
+    _cairo_cogl_maybe_log_clip (surface, &extents);
 
 #ifndef FILL_WITH_COGL_PATH
-    _cairo_cogl_journal_log_primitive (surface, pipeline, prim, transform);
+    _cairo_cogl_journal_log_primitive (surface,
+                                       pipeline,
+                                       prim,
+                                       transform,
+                                       src_tex_clip,
+                                       NULL,
+                                       op,
+                                       extents.unbounded);
     /* 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);
+    _cairo_cogl_journal_log_path (surface,
+                                  pipeline,
+                                  cogl_path,
+                                  src_tex_clip,
+                                  NULL,
+                                  op,
+                                  extents.unbounded);
     cogl_object_unref (cogl_path);
 #endif
 
     /* The journal will take a reference on the pipeline... */
     cogl_object_unref (pipeline);
 
-BAIL:
-    if (tex_clip)
-        _cairo_clip_destroy (tex_clip);
-
-    return status;
+    return CAIRO_STATUS_SUCCESS;
 }
 
 cairo_int_status_t
@@ -2978,28 +3343,32 @@ _cairo_cogl_surface_fill_rectangle (void		     *abstract_surface,
 				    const cairo_clip_t	     *clip)
 {
     cairo_cogl_surface_t *surface = abstract_surface;
+    cairo_composite_rectangles_t extents;
+    cairo_int_status_t status;
     CoglPipeline *pipeline;
 
     if (! is_operator_supported (op))
 	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) {
+        /* These extents will be larger than necessary, but in the absence
+         * of a specialized function, they will do */
+        status =
+            _cairo_composite_rectangles_init_for_paint (&extents,
+                                                        &surface->base,
+                                                        op,
+                                                        source,
+                                                        clip);
+        if (unlikely (status))
+	    return status;
+
 	pipeline =
             get_source_mask_operator_destination_pipeline (NULL,
                                                            source,
                                                            op,
                                                            surface,
                                                            NULL,
+                                                           NULL,
                                                            NULL);
 	if (!pipeline)
 	    return CAIRO_INT_STATUS_UNSUPPORTED;
@@ -3010,7 +3379,11 @@ _cairo_cogl_surface_fill_rectangle (void		     *abstract_surface,
 					   pipeline,
 					   x, y, width, height,
 					   0,
-					   ctm);
+					   ctm,
+                                           NULL,
+                                           NULL,
+                                           op,
+                                           extents.unbounded);
 	return CAIRO_INT_STATUS_SUCCESS;
     } else
 	return CAIRO_INT_STATUS_UNSUPPORTED;
@@ -3023,18 +3396,6 @@ _cairo_cogl_surface_fill_rectangle (void		     *abstract_surface,
      */
 }
 
-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,
@@ -3295,7 +3656,7 @@ 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);
+    //g_print ("n_clip_updates_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);
commit 9e473db58d22cc181a4a3388c72666ce28cdb8b8
Author: George Matsumura <gmmatsumura01 at bvsd.org>
Date:   Sat Jul 11 15:02:34 2020 -0600

    cogl: Flush the path before calling functions that require it
    
    Prior to this change, API functions like path_exents returned
    incorrect results if the cogl-specific representation of rectangular
    paths was being used.
    
    Signed-off-by: George Matsumura <gmmatsumura01 at bvsd.org>

diff --git a/src/cairo-cogl-context.c b/src/cairo-cogl-context.c
index bc1af4de6..2b8fbc831 100644
--- a/src/cairo-cogl-context.c
+++ b/src/cairo-cogl-context.c
@@ -351,7 +351,6 @@ _cairo_cogl_context_curve_to (void *abstract_cr,
 				       x3_fixed, y3_fixed);
 }
 
-#if 0
 static cairo_status_t
 _cairo_cogl_context_arc (void *abstract_cr,
 			  double xc, double yc, double radius,
@@ -367,10 +366,6 @@ _cairo_cogl_context_arc (void *abstract_cr,
 	    return status;
     }
 
-    status = cr->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;
 
@@ -380,11 +375,15 @@ _cairo_cogl_context_arc (void *abstract_cr,
 
 	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);
+	status = _cairo_cogl_context_line_to (abstract_cr,
+                                              x_fixed,
+                                              y_fixed);
 	if (unlikely (status))
 	    return status;
 
-	status = _cairo_path_fixed_line_to (&cr->user_path, x_fixed, y_fixed);
+	status = _cairo_cogl_context_line_to (abstract_cr,
+                                              x_fixed,
+                                              y_fixed);
 	if (unlikely (status))
 	    return status;
 
@@ -398,14 +397,27 @@ _cairo_cogl_context_arc (void *abstract_cr,
     if (unlikely (status))
 	return status;
 
+    /* These functions will be expressed in terms of the backend
+     * functions for line_to and curve_to, which we already add the
+     * appropriate segments to the user path in */
     if (forward)
-	_cairo_arc_path (&cr->base.base, xc, yc, radius, angle1, angle2);
+	_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 */
+	_cairo_arc_path_negative (&cr->base.base,
+                                  xc,
+                                  yc,
+                                  radius,
+                                  angle1,
+                                  angle2);
+
+    /* any error will have already been set on cr */
+    return CAIRO_STATUS_SUCCESS;
 }
-#endif
 
 static cairo_status_t
 _cairo_cogl_context_rel_move_to (void *abstract_cr, double dx, double dy)
@@ -583,6 +595,7 @@ _cairo_cogl_context_rectangle (void *abstract_cr,
 	cr->width = width;
 	cr->height = height;
 	cr->path_is_rectangle = TRUE;
+        cr->base.path->fill_is_empty = FALSE;
 	return CAIRO_STATUS_SUCCESS;
 #endif
     }
@@ -596,6 +609,128 @@ _cairo_cogl_context_rectangle (void *abstract_cr,
     return _cairo_cogl_context_rectangle_real (cr, x, y, width, height);
 }
 
+static void
+_cairo_cogl_context_path_extents (void *abstract_cr,
+                                  double *x1,
+                                  double *y1,
+                                  double *x2,
+                                  double *y2)
+{
+    cairo_cogl_context_t *cr = abstract_cr;
+    cairo_status_t status;
+
+    if (cr->path_is_rectangle) {
+        /* We do not flush the rectangle, as we are not modifying the
+         * path */
+        status = _cairo_cogl_context_rectangle_real (cr,
+                                                     cr->x,
+                                                     cr->y,
+                                                     cr->width,
+                                                     cr->height);
+        assert (status == CAIRO_STATUS_SUCCESS);
+    }
+
+    cr->backend_parent.path_extents (abstract_cr, x1, y1, x2, y2);
+}
+
+static cairo_bool_t
+_cairo_cogl_context_has_current_point (void *abstract_cr)
+{
+    cairo_cogl_context_t *cr = abstract_cr;
+    cairo_status_t status;
+
+    if (cr->path_is_rectangle) {
+        /* We do not flush the rectangle, as we are not modifying the
+         * path */
+        status = _cairo_cogl_context_rectangle_real (cr,
+                                                     cr->x,
+                                                     cr->y,
+                                                     cr->width,
+                                                     cr->height);
+        assert (status == CAIRO_STATUS_SUCCESS);
+    }
+
+    return cr->backend_parent.has_current_point (abstract_cr);
+}
+
+static cairo_bool_t
+_cairo_cogl_context_get_current_point (void *abstract_cr,
+                                       double *x,
+                                       double *y)
+{
+    cairo_cogl_context_t *cr = abstract_cr;
+    cairo_status_t status;
+
+    if (cr->path_is_rectangle) {
+        /* We do not flush the rectangle, as we are not modifying the
+         * path */
+        status = _cairo_cogl_context_rectangle_real (cr,
+                                                     cr->x,
+                                                     cr->y,
+                                                     cr->width,
+                                                     cr->height);
+        assert (status == CAIRO_STATUS_SUCCESS);
+    }
+
+    return cr->backend_parent.get_current_point (abstract_cr, x, y);
+}
+
+static cairo_path_t *
+_cairo_cogl_context_copy_path (void *abstract_cr)
+{
+    cairo_cogl_context_t *cr = abstract_cr;
+    cairo_status_t status;
+
+    if (cr->path_is_rectangle) {
+        /* We do not flush the rectangle, as we are not modifying the
+         * path */
+        status = _cairo_cogl_context_rectangle_real (cr,
+                                                     cr->x,
+                                                     cr->y,
+                                                     cr->width,
+                                                     cr->height);
+        assert (status == CAIRO_STATUS_SUCCESS);
+    }
+
+    return cr->backend_parent.copy_path (abstract_cr);
+}
+
+static cairo_path_t *
+_cairo_cogl_context_copy_path_flat (void *abstract_cr)
+{
+    cairo_cogl_context_t *cr = abstract_cr;
+    cairo_status_t status;
+
+    if (cr->path_is_rectangle) {
+        /* We do not flush the rectangle, as we are not modifying the
+         * path */
+        status = _cairo_cogl_context_rectangle_real (cr,
+                                                     cr->x,
+                                                     cr->y,
+                                                     cr->width,
+                                                     cr->height);
+        assert (status == CAIRO_STATUS_SUCCESS);
+    }
+
+    return cr->backend_parent.copy_path_flat (abstract_cr);
+}
+
+static cairo_status_t
+_cairo_cogl_context_append_path (void *abstract_cr,
+                                 const cairo_path_t *path)
+{
+    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;
+    }
+
+    return cr->backend_parent.append_path (abstract_cr, path);
+}
+
 /* 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
@@ -915,14 +1050,20 @@ _cairo_cogl_context_set_custom_vtable_funcs (cairo_backend_t *backend)
     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->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
+    backend->path_extents = _cairo_cogl_context_path_extents;
+    backend->has_current_point = _cairo_cogl_context_has_current_point;
+    backend->get_current_point = _cairo_cogl_context_get_current_point;
+    backend->copy_path = _cairo_cogl_context_copy_path;
+    backend->copy_path_flat = _cairo_cogl_context_copy_path_flat;
+    backend->append_path = _cairo_cogl_context_append_path;
+
+    /* Try to automatically catch if any new path APIs are added that
+     * mean we may need to overload more functions... */
+    assert (((char *)&backend->clip
              - (char *)&backend->backend_to_user_distance)
-            == (sizeof (void *) * 14));
+            == (sizeof (void *) * 21));
 
     backend->fill = _cairo_cogl_context_fill;
     backend->fill_preserve = _cairo_cogl_context_fill_preserve;
commit b7e1551814821a9288cde42a6e000fb1a887ee6f
Author: George Matsumura <gmmatsumura01 at bvsd.org>
Date:   Sat Jul 11 15:01:32 2020 -0600

    cogl: Fix rectangular filling fast path
    
    Prior to this, the rectangular filling fast path passed arguments to
    _cairo_cogl_journal_log_rectangle as if they represented the
    coordinates of two opposite points on the rectangle, when the arguments
    actually represent a rectangle with the x and y coordinates of one
    point and the width and height of the rectangle.
    
    Signed-off-by: George Matsumura <gmmatsumura01 at bvsd.org>

diff --git a/src/cairo-cogl-context.c b/src/cairo-cogl-context.c
index 70aa2a4d6..bc1af4de6 100644
--- a/src/cairo-cogl-context.c
+++ b/src/cairo-cogl-context.c
@@ -609,7 +609,7 @@ _cairo_cogl_surface_set_side_band_state (cairo_cogl_surface_t *surface,
 					 cairo_cogl_context_t *cr)
 {
 
-    if (cr->path_ctm_age <= 1) {
+    if (cr->path_ctm_age == 0) {
 	surface->user_path = &cr->user_path;
 	surface->ctm = &cr->base.gstate->ctm;
 	surface->ctm_inverse = &cr->base.gstate->ctm_inverse;
diff --git a/src/cairo-cogl-surface.c b/src/cairo-cogl-surface.c
index 2e95f3c76..09c6f2d00 100644
--- a/src/cairo-cogl-surface.c
+++ b/src/cairo-cogl-surface.c
@@ -1799,7 +1799,7 @@ BAIL:
 	return cogl_object_ref (linear_texture->texture);
     }
     default:
-	g_warning ("Un-supported source type");
+	g_warning ("Unsupported source type");
 	return NULL;
     }
 }
@@ -2059,7 +2059,7 @@ get_source_mask_operator_destination_pipeline (const cairo_pattern_t *mask,
         }
         break;
     default:
-	g_warning ("Un-supported source type");
+	g_warning ("Unsupported source type");
 	return NULL;
     }
 
@@ -2994,11 +2994,6 @@ _cairo_cogl_surface_fill_rectangle (void		     *abstract_surface,
 #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,
@@ -3013,7 +3008,7 @@ _cairo_cogl_surface_fill_rectangle (void		     *abstract_surface,
 
 	_cairo_cogl_journal_log_rectangle (surface,
 					   pipeline,
-					   x1, y1, x2, y2,
+					   x, y, width, height,
 					   0,
 					   ctm);
 	return CAIRO_INT_STATUS_SUCCESS;
commit 1bb048af24d68902981c2af0196fe4042b7e2a9f
Author: George Matsumura <gmmatsumura01 at bvsd.org>
Date:   Fri Jul 10 06:05:48 2020 -0600

    cogl: Support raster sources
    
    This adds supporte for raster sources using the same method that
    is used to support radial gradient and mesh sources.
    
    Signed-off-by: George Matsumura <gmmatsumura01 at bvsd.org>

diff --git a/src/cairo-cogl-surface.c b/src/cairo-cogl-surface.c
index a1cd0b723..2e95f3c76 100644
--- a/src/cairo-cogl-surface.c
+++ b/src/cairo-cogl-surface.c
@@ -1682,7 +1682,8 @@ _cairo_cogl_acquire_pattern_texture (const cairo_pattern_t *pattern,
 	return texture;
     }
     case CAIRO_PATTERN_TYPE_RADIAL:
-    case CAIRO_PATTERN_TYPE_MESH: {
+    case CAIRO_PATTERN_TYPE_MESH:
+    case CAIRO_PATTERN_TYPE_RASTER_SOURCE: {
 	cairo_surface_t *surface;
 
 	surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
@@ -2045,6 +2046,7 @@ get_source_mask_operator_destination_pipeline (const cairo_pattern_t *mask,
     case CAIRO_PATTERN_TYPE_LINEAR:
     case CAIRO_PATTERN_TYPE_RADIAL:
     case CAIRO_PATTERN_TYPE_MESH:
+    case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
         if (mask) {
             if (mask->type == CAIRO_PATTERN_TYPE_SOLID)
                 template_type =
@@ -2244,6 +2246,7 @@ _cairo_cogl_source_n_layers (const cairo_pattern_t *source)
     case CAIRO_PATTERN_TYPE_LINEAR:
     case CAIRO_PATTERN_TYPE_RADIAL:
     case CAIRO_PATTERN_TYPE_MESH:
+    case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
     case CAIRO_PATTERN_TYPE_SURFACE:
 	return 1;
     default:
commit f3d2623046b934eb2322af45feec3f6ff37e52d4
Author: George Matsumura <gmmatsumura01 at bvsd.org>
Date:   Fri Jul 10 03:26:03 2020 -0600

    cogl: Fix very small surfaces in boilerplate
    
    Prior to this change, the boilerplate code crashed when given surface
    dimensions less than 1. This fixes such behaviour by rounding the
    dimensions up to 1, which is also done by the boilerplate code for
    several other backends.
    
    Signed-off-by: George Matsumura <gmmatsumura01 at bvsd.org>

diff --git a/boilerplate/cairo-boilerplate-cogl.c b/boilerplate/cairo-boilerplate-cogl.c
index 037a34147..364654feb 100644
--- a/boilerplate/cairo-boilerplate-cogl.c
+++ b/boilerplate/cairo-boilerplate-cogl.c
@@ -74,6 +74,11 @@ _cairo_boilerplate_cogl_create_offscreen_color_surface (const char		*name,
     cogl_closure_t *closure;
     cairo_status_t status;
 
+    if (width < 1)
+        width = 1;
+    if (height < 1)
+        height = 1;
+
     if (!context)
 	context = cogl_context_new (NULL, NULL);
 
@@ -118,6 +123,11 @@ _cairo_boilerplate_cogl_create_onscreen_color_surface (const char	       *name,
     cogl_closure_t *closure;
     cairo_status_t status;
 
+    if (width < 1)
+        width = 1;
+    if (height < 1)
+        height = 1;
+
     if (!context)
 	context = cogl_context_new (NULL, NULL);
 
commit 8f0f371b873ed491ba9492fd0a6bad2695de24a6
Author: George Matsumura <gmmatsumura01 at bvsd.org>
Date:   Fri Jul 10 02:57:49 2020 -0600

    cogl: Use less memory during recording surface replaying
    
    Many of the tests in the test suite do many replays of different
    small parts of a single surface. When combined with the cogl backend's
    journaling mechanism, this consumes very large amounts of memory, as
    the entire surface is allocated for each replay. This changes this
    behaviour so that only the portion of the surface that will be used
    is allocated.
    
    Signed-off-by: George Matsumura <gmmatsumura01 at bvsd.org>

diff --git a/src/cairo-cogl-surface.c b/src/cairo-cogl-surface.c
index 77cb7ea4b..a1cd0b723 100644
--- a/src/cairo-cogl-surface.c
+++ b/src/cairo-cogl-surface.c
@@ -1433,8 +1433,11 @@ _cairo_cogl_get_pot_texture (CoglContext *context,
 /* 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	   *surface)
+_cairo_cogl_acquire_surface_texture (cairo_cogl_surface_t        *reference_surface,
+                                     cairo_surface_t             *surface,
+                                     const cairo_rectangle_int_t *extents,
+                                     const cairo_matrix_t        *pattern_matrix,
+                                     cairo_bool_t                *has_pre_transform)
 {
     cairo_image_surface_t *image;
     cairo_image_surface_t *acquired_image = NULL;
@@ -1444,6 +1447,9 @@ _cairo_cogl_acquire_surface_texture (cairo_cogl_surface_t  *reference_surface,
     CoglTexture2D *texture;
     GError *error = NULL;
     cairo_surface_t *clone;
+    cairo_matrix_t transform;
+
+    *has_pre_transform = FALSE;
 
     if (surface->device == reference_surface->base.device) {
         _cairo_cogl_surface_flush ((cairo_cogl_surface_t *)surface, 0);
@@ -1467,10 +1473,17 @@ _cairo_cogl_acquire_surface_texture (cairo_cogl_surface_t  *reference_surface,
     }
 
     if (_cairo_surface_is_recording (surface)) {
+        /* We pre-transform the recording surface here and make the
+         * target surface the size of the extents in order to reduce
+         * texture size. When we return to the acquire_pattern_texture
+         * function, it will know to adjust the texture matrix
+         * accordingly. */
+        *has_pre_transform = TRUE;
+
         texture =
             cogl_texture_2d_new_with_size (to_device(reference_surface->base.device)->cogl_context,
-                                           reference_surface->width,
-                                           reference_surface->height);
+                                           extents->width,
+                                           extents->height);
         if (!texture) {
 	    g_warning ("Failed to allocate texture: %s", error->message);
 	    g_error_free (error);
@@ -1483,7 +1496,16 @@ _cairo_cogl_acquire_surface_texture (cairo_cogl_surface_t  *reference_surface,
                                              NULL,
                                              texture);
 
-        if (_cairo_recording_surface_replay (surface, clone)) {
+        cairo_matrix_init_translate (&transform,
+                                     extents->x,
+                                     extents->y);
+        cairo_matrix_multiply (&transform, &transform, pattern_matrix);
+
+        if (_cairo_recording_surface_replay_with_clip (surface,
+                                                       &transform,
+                                                       clone,
+                                                       NULL))
+        {
             g_warning ("could not replay recording surface");
             texture = NULL;
             goto BAIL;
@@ -1595,12 +1617,18 @@ _cairo_cogl_acquire_pattern_texture (const cairo_pattern_t *pattern,
                                      cairo_clip_t **tex_clip)
 {
     CoglTexture *texture = NULL;
+    cairo_bool_t has_pre_transform;
 
     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);
+	texture =
+            _cairo_cogl_acquire_surface_texture (destination,
+                                                 surface,
+                                                 extents,
+                                                 &pattern->matrix,
+                                                 &has_pre_transform);
 	if (!texture)
 	    return NULL;
 
@@ -1612,7 +1640,12 @@ _cairo_cogl_acquire_pattern_texture (const cairo_pattern_t *pattern,
 	}
 #endif
 
-	attributes->matrix = pattern->matrix;
+        if (has_pre_transform)
+            cairo_matrix_init_translate (&attributes->matrix,
+                                         -extents->x,
+                                         -extents->y);
+        else
+	    attributes->matrix = pattern->matrix;
 
 	/* Convert from un-normalized source coordinates in backend
 	 * coordinates to normalized texture coordinates. Since
@@ -1651,7 +1684,6 @@ _cairo_cogl_acquire_pattern_texture (const cairo_pattern_t *pattern,
     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);
@@ -1663,13 +1695,18 @@ _cairo_cogl_acquire_pattern_texture (const cairo_pattern_t *pattern,
 	    return NULL;
 	}
 
-	texture = _cairo_cogl_acquire_surface_texture (destination, surface);
+	texture =
+            _cairo_cogl_acquire_surface_texture (destination,
+                                                 surface,
+                                                 NULL, // As long as the surface is an image,
+                                                 NULL, // acquire_surface_texture shouldn't access these values
+                                                 &has_pre_transform);
 	if (!texture)
 	    goto BAIL;
 
-	cairo_matrix_init_identity (&texture_matrix);
-
-	cairo_matrix_translate (&texture_matrix, -extents->x, -extents->y);
+        cairo_matrix_init_translate (&attributes->matrix,
+                                     -extents->x,
+                                     -extents->y);
 
 	/* Convert from un-normalized source coordinates in backend
 	 * coordinates to normalized texture coordinates. Since
@@ -1685,7 +1722,6 @@ _cairo_cogl_acquire_pattern_texture (const cairo_pattern_t *pattern,
         attributes->matrix.x0 *= xscale;
         attributes->matrix.y0 *= yscale;
 
-	attributes->matrix = texture_matrix;
 	attributes->extend = pattern->extend;
 	attributes->filter = CAIRO_FILTER_NEAREST;
 	attributes->has_component_alpha = pattern->has_component_alpha;
commit 641ea48b66c3ee96138366d64d50dd40c5d29dc1
Author: George Matsumura <gmmatsumura01 at bvsd.org>
Date:   Wed Jul 8 20:49:35 2020 -0600

    cogl: Support more unbounded operators
    
    Due to recent changes, all unbounded Porter-Duff compositing operators
    can be properly supported in addition to IN.
    
    Signed-off-by: George Matsumura <gmmatsumura01 at bvsd.org>

diff --git a/src/cairo-cogl-surface.c b/src/cairo-cogl-surface.c
index 8e6c6c295..77cb7ea4b 100644
--- a/src/cairo-cogl-surface.c
+++ b/src/cairo-cogl-surface.c
@@ -1352,6 +1352,8 @@ get_cogl_wrap_mode_for_extend (cairo_extend_t extend_mode)
     case CAIRO_EXTEND_REPEAT:
 	return COGL_PIPELINE_WRAP_MODE_REPEAT;
     case CAIRO_EXTEND_REFLECT:
+        /* TODO: Detect hardware where MIRRORED_REPEAT is not available
+         * and implement fallback */
 	return COGL_PIPELINE_WRAP_MODE_MIRRORED_REPEAT;
     }
     assert (0); /* not reached */
@@ -2629,15 +2631,6 @@ _cairo_cogl_surface_stroke (void                       *abstract_surface,
     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,
@@ -2856,15 +2849,6 @@ _cairo_cogl_surface_fill (void			    *abstract_surface,
     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,
@@ -2960,15 +2944,6 @@ _cairo_cogl_surface_fill_rectangle (void		     *abstract_surface,
     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,
commit e2e494b9ff1d3ba57d21f4cf23187b940cfa0968
Author: George Matsumura <gmmatsumura01 at bvsd.org>
Date:   Wed Jul 8 20:48:00 2020 -0600

    cogl: Fix rectangle filling conditions
    
    Prior to this change, the context functions were using the accelerated
    rectangle-filling path even if there was a preexisting rectangular path,
    producing incorrect results.
    
    Signed-off-by: George Matsumura <gmmatsumura01 at bvsd.org>

diff --git a/src/cairo-cogl-context.c b/src/cairo-cogl-context.c
index 93703c956..70aa2a4d6 100644
--- a/src/cairo-cogl-context.c
+++ b/src/cairo-cogl-context.c
@@ -570,7 +570,9 @@ _cairo_cogl_context_rectangle (void *abstract_cr,
 {
     cairo_cogl_context_t *cr = abstract_cr;
 
-    if (cr->user_path.buf.base.num_ops == 0) {
+    /* Do not take the single-rectangle shortcut if we already have a
+     * path, whether conventional or rectangle */
+    if (cr->user_path.buf.base.num_ops == 0 && !cr->path_is_rectangle) {
 	cr->path_ctm_age = 0;
 
 #if 1
commit bb02ea2b4f68b202682383f2517e33c0d66a9567
Author: George Matsumura <gmmatsumura01 at bvsd.org>
Date:   Wed Jul 8 06:58:53 2020 -0600

    cogl: Correct usage of clip boxes
    
    This changes the interpretation of multiple boxes in the clip from
    using the intersection of the multiple boxes to using the union of
    the multiple boxes, which corrects many results in the test suite.
    
    Signed-off-by: George Matsumura <gmmatsumura01 at bvsd.org>

diff --git a/src/cairo-cogl-surface.c b/src/cairo-cogl-surface.c
index 1b80d5be0..8e6c6c295 100644
--- a/src/cairo-cogl-surface.c
+++ b/src/cairo-cogl-surface.c
@@ -64,6 +64,8 @@
 //#define DISABLE_BATCHING
 #define USE_COGL_RECTANGLE_API
 #define ENABLE_RECTANGLES_FASTPATH
+/* This hasn't been implemented yet */
+//#define ENABLE_CLIP_CACHE
 
 #if defined (USE_COGL_RECTANGLE_API) || defined (ENABLE_PATH_CACHE)
 #define NEED_COGL_CONTEXT
@@ -305,6 +307,32 @@ _cairo_cogl_surface_get_extents (void *abstract_surface,
     return TRUE;
 }
 
+/* Taken from cairo-surface-clipper.c */
+static cairo_status_t
+_cairo_path_fixed_add_box (cairo_path_fixed_t *path,
+			   const cairo_box_t *box)
+{
+    cairo_status_t status;
+
+    status = _cairo_path_fixed_move_to (path, box->p1.x, box->p1.y);
+    if (unlikely (status))
+	return status;
+
+    status = _cairo_path_fixed_line_to (path, box->p2.x, box->p1.y);
+    if (unlikely (status))
+	return status;
+
+    status = _cairo_path_fixed_line_to (path, box->p2.x, box->p2.y);
+    if (unlikely (status))
+	return status;
+
+    status = _cairo_path_fixed_line_to (path, box->p1.x, box->p2.y);
+    if (unlikely (status))
+	return status;
+
+    return _cairo_path_fixed_close_path (path);
+}
+
 static void
 _cairo_cogl_journal_free (cairo_cogl_surface_t *surface)
 {
@@ -757,19 +785,56 @@ BAIL:
 }
 
 static void
-_cairo_cogl_clip_push_box (const cairo_box_t *box,
-                           CoglFramebuffer *framebuffer)
-{
-    if (_cairo_box_is_pixel_aligned (box)) {
-	cairo_rectangle_int_t rect;
-	_cairo_box_round_to_rectangle (box, &rect);
-	cogl_framebuffer_push_scissor_clip (framebuffer, rect.x, rect.y,
-					 rect.width, rect.height);
-    } else {
-	double x1, y1, x2, y2;
-	_cairo_box_to_doubles (box, &x1, &y1, &x2, &y2);
-	cogl_framebuffer_push_rectangle_clip (framebuffer, x1, y1, x2, y2);
+_cairo_cogl_set_path_prim_clip (cairo_cogl_surface_t *surface,
+                                cairo_path_fixed_t *path,
+                                int *clip_stack_depth,
+#ifdef ENABLE_CLIP_CACHE
+                                cairo_bool_t *checked_for_primitives,
+                                cairo_cogl_clip_primitives_t *clip_primitives;
+#endif
+                                cairo_fill_rule_t fill_rule,
+                                double tolerance)
+{
+    cairo_rectangle_int_t extents;
+    CoglPrimitive *prim;
+    size_t prim_size;
+
+    _cairo_path_fixed_approximate_clip_extents (path, &extents);
+
+    /* TODO - maintain a fifo of the last 10 used clips with cached
+     * primitives to see if we can avoid tessellating the path and
+     * uploading the vertices...
+     */
+#ifdef ENABLE_CLIP_CACHE
+    if (!*checked_for_primitives) {
+        clip_primitives = find_clip_primitives (clip);
+        checked_for_primitives = TRUE;
+    }
+    if (clip_primitives)
+        prim = clip_primitives->primitives[i];
+#endif
+
+    if (unlikely (_cairo_cogl_fill_to_primitive (surface,
+                                                 path,
+                                                 fill_rule,
+                                                 tolerance,
+                                                 0,
+                                                 FALSE,
+                                                 &prim,
+                                                 &prim_size)))
+    {
+        g_warning ("Failed to get primitive for clip path while flushing journal");
+        goto BAIL;
     }
+
+    (*clip_stack_depth)++;
+    cogl_framebuffer_push_primitive_clip (surface->framebuffer, prim,
+                                          extents.x, extents.y,
+                                          extents.x + extents.width,
+                                          extents.y + extents.height);
+
+BAIL:
+    cogl_object_unref (prim);
 }
 
 static void
@@ -791,7 +856,7 @@ _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface)
     }
 
     if (_cairo_cogl_surface_ensure_framebuffer (surface)) {
-        g_warning ("Could not get framebuffer for flushing journal\n");
+        g_warning ("Could not get framebuffer for flushing journal");
         assert (0);
     }
 
@@ -806,7 +871,7 @@ _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface)
 	    cairo_cogl_journal_clip_entry_t *clip_entry =
 		(cairo_cogl_journal_clip_entry_t *)entry;
 	    cairo_clip_path_t *path;
-#if 0
+#ifdef ENABLE_CLIP_CACHE
 	    cairo_bool_t checked_for_primitives = FALSE;
 	    cairo_cogl_clip_primitives_t *clip_primitives;
 #endif
@@ -818,50 +883,46 @@ _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface)
             if (clip_entry->clip == NULL)
                 continue; // there is no clip
 
-	    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);
+	    for (path = clip_entry->clip->path, i = 0;
+                 path;
+                 path = path->prev, i++)
+            {
+                _cairo_cogl_set_path_prim_clip (surface,
+                                                &path->path,
+                                                &clip_stack_depth,
+#ifdef ENABLE_CLIP_CACHE
+                                                &checked_for_primitives,
+                                                clip_primitives,
+#endif
+                                                path->fill_rule,
+                                                path->tolerance);
+            }
 
-		/* TODO - maintain a fifo of the last 10 used clips with cached
-		 * primitives to see if we can avoid tessellating 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];
+	    if (clip_entry->clip->num_boxes > 0) {
+                cairo_path_fixed_t boxes_path;
+
+                _cairo_path_fixed_init (&boxes_path);
+                for (int i = 0; i < clip_entry->clip->num_boxes; i++) {
+                    if (unlikely (_cairo_path_fixed_add_box (&boxes_path,
+                                                             &clip_entry->clip->boxes[i])))
+                    {
+                        g_warning ("Could not add all clip boxes while "
+                                   "flushing journal");
+                        break;
+                    }
+                }
+
+                _cairo_cogl_set_path_prim_clip (surface,
+                                                &boxes_path,
+                                                &clip_stack_depth,
+#ifdef ENABLE_CLIP_CACHE
+                                                &checked_for_primitives,
+                                                clip_primitives,
 #endif
-		status = _cairo_cogl_fill_to_primitive (surface,
-							&path->path,
-							path->fill_rule,
-							path->tolerance,
-							0,
-							FALSE,
-							&prim,
-							&prim_size);
-		if (unlikely (status)) {
-		    g_warning ("Failed to get primitive for clip path while flushing journal");
-		    continue;
-		}
-		clip_stack_depth++;
-		cogl_framebuffer_push_primitive_clip (surface->framebuffer, prim,
-                                                      extents.x, extents.y,
-                                                      extents.x + extents.width,
-                                                      extents.y + extents.height);
-		cogl_object_unref (prim);
-	    }
+                                                CAIRO_FILL_RULE_WINDING,
+                                                0.);
 
-	    for (i = 0; i < clip_entry->clip->num_boxes; i++) {
-		clip_stack_depth++;
-		_cairo_cogl_clip_push_box (&clip_entry->clip->boxes[i],
-                                           surface->framebuffer);
+                _cairo_path_fixed_fini (&boxes_path);
 	    }
 
 	    surface->n_clip_updates_per_frame++;
@@ -1037,7 +1098,7 @@ get_components_from_cogl_format (CoglPixelFormat format)
     case COGL_PIXEL_FORMAT_DEPTH_32:
         return COGL_TEXTURE_COMPONENTS_DEPTH;
     default:
-    g_warning("bad format: %x a? %d, bgr? %d, pre %d, format: %d\n",
+    g_warning("bad format: %x a? %d, bgr? %d, pre %d, format: %d",
               format,
               format & COGL_A_BIT,
               format & COGL_BGR_BIT,
@@ -1062,10 +1123,10 @@ get_cairo_format_from_cogl_format (CoglPixelFormat format)
     case COGL_PIXEL_FORMAT_RGB_565:
 	return CAIRO_FORMAT_RGB16_565;
     case COGL_PIXEL_FORMAT_RG_88:
-        g_warning ("cairo cannot handle red-green textures\n");
+        g_warning ("cairo cannot handle red-green textures");
         return CAIRO_FORMAT_INVALID;
     case COGL_PIXEL_FORMAT_DEPTH_32:
-        g_warning ("cairo cannot handle depth textures\n");
+        g_warning ("cairo cannot handle depth textures");
         return CAIRO_FORMAT_INVALID;
     
     case COGL_PIXEL_FORMAT_BGRA_8888_PRE:
@@ -1076,7 +1137,7 @@ get_cairo_format_from_cogl_format (CoglPixelFormat format)
 	return CAIRO_FORMAT_ARGB32;
 
     default:
-	g_warning("bad format: %x a? %d, bgr? %d, pre %d, format: %d\n",
+	g_warning("bad format: %x a? %d, bgr? %d, pre %d, format: %d",
 		  format,
 		  format & COGL_A_BIT,
 		  format & COGL_BGR_BIT,
@@ -1421,7 +1482,7 @@ _cairo_cogl_acquire_surface_texture (cairo_cogl_surface_t  *reference_surface,
                                              texture);
 
         if (_cairo_recording_surface_replay (surface, clone)) {
-            g_warning ("could not replay recording surface \n");
+            g_warning ("could not replay recording surface");
             texture = NULL;
             goto BAIL;
         }
@@ -1440,7 +1501,7 @@ _cairo_cogl_acquire_surface_texture (cairo_cogl_surface_t  *reference_surface,
                                                  &acquired_image,
                                                  &image_extra);
 	if (unlikely (status)) {
-	    g_warning ("acquire_source_image failed: %s [%d]\n",
+	    g_warning ("acquire_source_image failed: %s [%d]",
 		       cairo_status_to_string (status), status);
 	    return NULL;
 	}
@@ -1541,10 +1602,6 @@ _cairo_cogl_acquire_pattern_texture (const cairo_pattern_t *pattern,
 	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. */
@@ -1727,12 +1784,12 @@ _cairo_cogl_setup_op_state (CoglPipeline *pipeline, cairo_operator_t op)
 
     switch ((int)op)
     {
-    case CAIRO_OPERATOR_CLEAR:
-        status = set_blend (pipeline, "RGBA = ADD (0, 0)");
-        break;
+    /* Use OVER for SOURCE so we can use a bounding mask. We will
+     * replace the source alpha with ones or the mask alpha when we do
+     * the rest of the pipeline setup. Note that this doesn't actually
+     * give the right result alpha value, which would be directly
+     * copied in a true SORUCE operation. */
     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;
@@ -1754,6 +1811,8 @@ _cairo_cogl_setup_op_state (CoglPipeline *pipeline, cairo_operator_t op)
     case CAIRO_OPERATOR_DEST_IN:
 	status = set_blend (pipeline, "RGBA = ADD (0, DST_COLOR * SRC_COLOR[A])");
 	break;
+    /* Use DEST_OUT for CLEAR so we can use a bounding mask */
+    case CAIRO_OPERATOR_CLEAR:
     case CAIRO_OPERATOR_DEST_OUT:
         status = set_blend (pipeline, "RGBA = ADD (0, DST_COLOR * (1 - SRC_COLOR[A]))");
         break;
@@ -1772,13 +1831,18 @@ _cairo_cogl_setup_op_state (CoglPipeline *pipeline, cairo_operator_t op)
 }
 
 static void
-create_templates_for_op_type (cairo_cogl_device_t *dev,
+create_template_for_op_type (cairo_cogl_device_t *dev,
                               cairo_operator_t op,
                               cairo_cogl_template_type type)
 {
     CoglPipeline *pipeline;
     CoglColor color;
 
+    if (dev->template_pipelines[op][type])
+        return;
+
+    cogl_color_init_from_4f (&color, 1.0f, 1.0f, 1.0f, 1.0f);
+
     if (!dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID]) {
         CoglPipeline *base = cogl_pipeline_new (dev->cogl_context);
 
@@ -1787,6 +1851,19 @@ create_templates_for_op_type (cairo_cogl_device_t *dev,
             return;
         }
 
+        if (op == CAIRO_OPERATOR_CLEAR || op == CAIRO_OPERATOR_SOURCE) {
+            /* This will make it so that the DEST_OUT or OVER blending
+             * functions receives a source alpha indicating a mask of
+             * uniform ones */
+            cogl_pipeline_set_layer_combine_constant (base,
+                                                      0,
+                                                      &color);
+            cogl_pipeline_set_layer_combine (base, 0,
+                                             "RGB = REPLACE (PRIMARY)"
+                                             "A = REPLACE (CONSTANT)",
+                                             NULL);
+        }
+
         dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID] = base;
     }
 
@@ -1797,44 +1874,71 @@ create_templates_for_op_type (cairo_cogl_device_t *dev,
     case CAIRO_COGL_TEMPLATE_TYPE_SOLID_MASK_SOLID:
         pipeline =
             cogl_pipeline_copy (dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID]);
-        cogl_pipeline_set_layer_combine (pipeline, 0,
-                                         "RGBA = MODULATE (PRIMARY, CONSTANT[A])",
-                                         NULL);
         cogl_pipeline_set_layer_combine_constant (pipeline, 0, &color);
+        if (op == CAIRO_OPERATOR_CLEAR || op == CAIRO_OPERATOR_SOURCE)
+            cogl_pipeline_set_layer_combine (pipeline, 0,
+                                             "RGB = MODULATE (PRIMARY, CONSTANT[A])"
+                                             "A = REPLACE (CONSTANT)",
+                                             NULL);
+        else
+            cogl_pipeline_set_layer_combine (pipeline, 0,
+                                             "RGBA = MODULATE (PRIMARY, CONSTANT[A])",
+                                             NULL);
         break;
     case CAIRO_COGL_TEMPLATE_TYPE_TEXTURE_MASK_SOLID:
         pipeline =
             cogl_pipeline_copy (dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID]);
-        cogl_pipeline_set_layer_combine (pipeline, 0,
-                                         "RGBA = MODULATE (PRIMARY, TEXTURE[A])",
-                                         NULL);
         cogl_pipeline_set_layer_texture (pipeline, 0, dev->dummy_texture);
+        if (op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_SOURCE)
+            cogl_pipeline_set_layer_combine (pipeline, 0,
+                                             "RGB = MODULATE (PRIMARY, TEXTURE[A])"
+                                             "A = REPLACE (TEXTURE)",
+                                             NULL);
+        else
+            cogl_pipeline_set_layer_combine (pipeline, 0,
+                                             "RGBA = MODULATE (PRIMARY, TEXTURE[A])",
+                                             NULL);
         break;
     case CAIRO_COGL_TEMPLATE_TYPE_TEXTURE:
         pipeline =
             cogl_pipeline_copy (dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID]);
         cogl_pipeline_set_layer_texture (pipeline, 0, dev->dummy_texture);
+        if (op == CAIRO_OPERATOR_CLEAR || op == CAIRO_OPERATOR_SOURCE)
+            cogl_pipeline_set_layer_combine (pipeline, 0,
+                                             "RGB = REPLACE (TEXTURE)"
+                                             "A = REPLACE (CONSTANT)",
+                                             NULL);
         break;
     case CAIRO_COGL_TEMPLATE_TYPE_SOLID_MASK_TEXTURE:
         pipeline =
             cogl_pipeline_copy (dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID]);
         cogl_pipeline_set_layer_texture (pipeline, 0, dev->dummy_texture);
+        if (op == CAIRO_OPERATOR_CLEAR || op == CAIRO_OPERATOR_SOURCE)
+            cogl_pipeline_set_layer_combine (pipeline, 0,
+                                             "RGB = REPLACE (TEXTURE)"
+                                             "A = REPLACE (CONSTANT)",
+                                             NULL);
+        cogl_pipeline_set_layer_combine_constant (pipeline, 1, &color);
         cogl_pipeline_set_layer_combine (pipeline, 1,
                                          "RGBA = MODULATE (PREVIOUS, CONSTANT[A])",
                                          NULL);
-        cogl_pipeline_set_layer_combine_constant (pipeline, 1, &color);
         break;
     case CAIRO_COGL_TEMPLATE_TYPE_TEXTURE_MASK_TEXTURE:
         pipeline =
             cogl_pipeline_copy (dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID]);
         cogl_pipeline_set_layer_texture (pipeline, 0, dev->dummy_texture);
+        if (op == CAIRO_OPERATOR_CLEAR || op == CAIRO_OPERATOR_SOURCE)
+            cogl_pipeline_set_layer_combine (pipeline, 0,
+                                             "RGB = REPLACE (TEXTURE)"
+                                             "A = REPLACE (CONSTANT)",
+                                             NULL);
+        cogl_pipeline_set_layer_texture (pipeline, 1, dev->dummy_texture);
         cogl_pipeline_set_layer_combine (pipeline, 1,
                                          "RGBA = MODULATE (PREVIOUS, TEXTURE[A])",
                                          NULL);
-        cogl_pipeline_set_layer_texture (pipeline, 1, dev->dummy_texture);
         break;
     default:
-        g_warning ("Invalid cogl pipeline template type\n");
+        g_warning ("Invalid cogl pipeline template type");
         return;
     }
 
@@ -1921,7 +2025,7 @@ get_source_mask_operator_destination_pipeline (const cairo_pattern_t *mask,
 
     /* Lazily create pipeline templates */
     if (unlikely (dev->template_pipelines[op][template_type] == NULL))
-        create_templates_for_op_type (dev, op, template_type);
+        create_template_for_op_type (dev, op, template_type);
 
     pipeline =
         cogl_pipeline_copy (dev->template_pipelines[op][template_type]);
@@ -2087,7 +2191,7 @@ is_operator_supported (cairo_operator_t op)
 	return TRUE;
 
     default:
-        g_warning("cairo-cogl: Blend operator not supported\n");
+        g_warning("cairo-cogl: Blend operator not supported");
 	return FALSE;
     }
 }
@@ -2907,11 +3011,6 @@ _cairo_cogl_surface_fill_rectangle (void		     *abstract_surface,
      * 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.
      */
 }
 
commit 7319feb642ecdf21315c8e4a6e211ab27b7dc833
Author: George Matsumura <gmmatsumura01 at bvsd.org>
Date:   Sun Jul 5 17:34:54 2020 -0600

    cogl: Support CAIRO_EXTEND_NONE correctly
    
    Prior to this change, CAIRO_EXTEND_NONE was handled the same way as
    CAIRO_EXTEND_PAD, which produced incorrect results in many tests.
    This fixes such behaviour by created a per-source and per-mask clip
    that is applied so that areas outside the bounds of the source or
    mask textures are not sampled during drawing.
    
    Signed-off-by: George Matsumura <gmmatsumura01 at bvsd.org>

diff --git a/src/cairo-cogl-surface.c b/src/cairo-cogl-surface.c
index 0b2706ea5..1b80d5be0 100644
--- a/src/cairo-cogl-surface.c
+++ b/src/cairo-cogl-surface.c
@@ -1279,72 +1279,6 @@ _cairo_cogl_path_fixed_rectangle (cairo_path_fixed_t *path,
     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) {
-        status = _cairo_cogl_surface_ensure_framebuffer (abstract_surface);
-        if (unlikely (status))
-            goto BAIL;
-
-	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,
-                                          _cairo_fixed_from_int (surface->width),
-                                          _cairo_fixed_from_int (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)
 {
@@ -1558,14 +1492,44 @@ BAIL:
     return texture;
 }
 
+static cairo_status_t
+_cairo_cogl_create_tex_clip (cairo_clip_t **tex_clip,
+                             cairo_matrix_t inverse)
+{
+    cairo_path_fixed_t path;
+    cairo_status_t status;
+
+    _cairo_path_fixed_init (&path);
+    status = cairo_matrix_invert (&inverse);
+    if (unlikely (status))
+        return status;
+
+    status = _cairo_cogl_path_fixed_rectangle (&path, 0, 0,
+                                               CAIRO_FIXED_ONE,
+                                               CAIRO_FIXED_ONE);
+    if (unlikely (status))
+        return status;
+
+    _cairo_path_fixed_transform (&path, &inverse);
+
+    *tex_clip = _cairo_clip_intersect_path (NULL,
+                                            &path,
+                                            CAIRO_FILL_RULE_WINDING,
+                                            1,
+                                            CAIRO_ANTIALIAS_DEFAULT);
+
+    _cairo_path_fixed_fini (&path);
+    return CAIRO_STATUS_SUCCESS;
+}
+
 /* 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)
+				     cairo_cogl_texture_attributes_t *attributes,
+                                     cairo_clip_t **tex_clip)
 {
     CoglTexture *texture = NULL;
 
@@ -1612,6 +1576,17 @@ _cairo_cogl_acquire_pattern_texture (const cairo_pattern_t *pattern,
 	attributes->s_wrap = get_cogl_wrap_mode_for_extend (pattern->extend);
 	attributes->t_wrap = attributes->s_wrap;
 
+        /* In order to support CAIRO_EXTEND_NONE, we use the same wrap
+         * mode as CAIRO_EXTEND_PAD, but pass a clip to the drawing
+         * function to make sure that we never sample anything beyond
+         * the texture boundaries. */
+        if (pattern->extend == CAIRO_EXTEND_NONE && tex_clip)
+            if (_cairo_cogl_create_tex_clip (tex_clip,
+                                             attributes->matrix)) {
+                cogl_object_unref (texture);
+                return NULL;
+            }
+
 	return texture;
     }
     case CAIRO_PATTERN_TYPE_RADIAL:
@@ -1660,6 +1635,18 @@ _cairo_cogl_acquire_pattern_texture (const cairo_pattern_t *pattern,
 	attributes->s_wrap = COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE;
 	attributes->t_wrap = attributes->s_wrap;
 
+        /* In order to support CAIRO_EXTEND_NONE, we use the same wrap
+         * mode as CAIRO_EXTEND_PAD, but pass a clip to the drawing
+         * function to make sure that we never sample anything beyond
+         * the texture boundaries. */
+        if (pattern->extend == CAIRO_EXTEND_NONE && tex_clip)
+            if (_cairo_cogl_create_tex_clip (tex_clip,
+                                             attributes->matrix)) {
+                cogl_object_unref (texture);
+                cairo_surface_destroy (surface);
+                return NULL;
+            }
+
 BAIL:
 	cairo_surface_destroy (surface);
 
@@ -1887,13 +1874,17 @@ 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_composite_rectangles_t *extents,
+                                               cairo_clip_t **tex_clip)
 {
     cairo_cogl_template_type template_type;
     CoglPipeline *pipeline;
     cairo_cogl_device_t *dev = to_device(destination->base.device);
     int layer_counter = 0;
 
+    if (tex_clip)
+        *tex_clip = NULL;
+
     switch ((int)source->type)
     {
     case CAIRO_PATTERN_TYPE_SOLID:
@@ -1947,8 +1938,8 @@ get_source_mask_operator_destination_pipeline (const cairo_pattern_t *mask,
 	CoglTexture *texture =
 	    _cairo_cogl_acquire_pattern_texture (source, destination,
 						 &extents->bounded,
-						 &extents->source_sample_area,
-						 &attributes);
+						 &attributes,
+                                                 tex_clip);
 	if (!texture)
 	    goto BAIL;
 	set_layer_texture_with_attributes (pipeline,
@@ -1972,11 +1963,15 @@ get_source_mask_operator_destination_pipeline (const cairo_pattern_t *mask,
                                                       &color);
 	} else {
 	    cairo_cogl_texture_attributes_t attributes;
+            /* We don't properly support CAIRO_EXTEND_NONE on mask
+             * textures. However, if this ends up being an issue, we
+             * can pass get_source_mask_operator_destination_pipeline
+             * two clip pointers. */
 	    CoglTexture *texture =
 		_cairo_cogl_acquire_pattern_texture (mask, destination,
 						     &extents->bounded,
-						     &extents->mask_sample_area,
-						     &attributes);
+						     &attributes,
+                                                     NULL);
 	    if (!texture)
 		goto BAIL;
 	    set_layer_texture_with_attributes (pipeline,
@@ -2097,6 +2092,113 @@ is_operator_supported (cairo_operator_t op)
     }
 }
 
+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_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_status_t status;
+    cairo_matrix_t identity;
+    CoglPipeline *pipeline;
+    cairo_composite_rectangles_t extents;
+    cairo_clip_t *tex_clip;
+
+    if (clip == NULL) {
+        status = _cairo_cogl_surface_ensure_framebuffer (abstract_surface);
+        if (unlikely (status))
+            goto BAIL;
+
+	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);
+        }
+    }
+
+    /* fall back to handling the paint in terms of a rectangle... */
+
+    surface = (cairo_cogl_surface_t *)abstract_surface;
+
+    if (!is_operator_supported (op))
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    status =
+        _cairo_composite_rectangles_init_for_paint (&extents,
+                                                    &surface->base,
+                                                    op,
+                                                    source,
+                                                    clip);
+    if (unlikely (status))
+	return status;
+
+    pipeline =
+        get_source_mask_operator_destination_pipeline (NULL,
+                                                       source,
+                                                       op,
+                                                       surface,
+                                                       &extents,
+                                                       &tex_clip);
+    if (!pipeline) {
+	status = CAIRO_INT_STATUS_UNSUPPORTED;
+	goto BAIL;
+    }
+
+    if (tex_clip) {
+        tex_clip = _cairo_clip_intersect_clip (tex_clip, clip);
+        status =
+            _cairo_composite_rectangles_init_for_paint (&extents,
+                                                        &surface->base,
+                                                        op,
+                                                        source,
+                                                        clip);
+        if (unlikely (status))
+            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,
+				       _cairo_cogl_source_n_layers (source),
+				       &identity);
+
+    /* The journal will take a reference on the pipeline and clip_path... */
+    cogl_object_unref (pipeline);
+
+BAIL:
+    if (tex_clip)
+        _cairo_clip_destroy (tex_clip);
+
+    return status;
+}
+
 static cairo_int_status_t
 _cairo_cogl_surface_mask (void                    *abstract_surface,
                           cairo_operator_t         op,
@@ -2109,6 +2211,7 @@ _cairo_cogl_surface_mask (void                    *abstract_surface,
     cairo_status_t status;
     CoglPipeline *pipeline;
     cairo_matrix_t identity;
+    cairo_clip_t *tex_clip;
 
     /* XXX: Use this to smoke test the acquire_source/dest_image fallback
      * paths... */
@@ -2123,13 +2226,31 @@ _cairo_cogl_surface_mask (void                    *abstract_surface,
     if (unlikely (status))
 	return status;
 
-    pipeline = get_source_mask_operator_destination_pipeline (mask, source,
-							      op, surface, &extents);
-    if (!pipeline){
+    pipeline =
+        get_source_mask_operator_destination_pipeline (mask,
+                                                       source,
+                                                       op,
+                                                       surface,
+                                                       &extents,
+                                                       &tex_clip);
+    if (!pipeline) {
 	status = CAIRO_INT_STATUS_UNSUPPORTED;
 	goto BAIL;
     }
 
+    if (tex_clip) {
+        tex_clip = _cairo_clip_intersect_clip (tex_clip, clip);
+        status =
+            _cairo_composite_rectangles_init_for_mask (&extents,
+                                                       &surface->base,
+                                                       op,
+                                                       source,
+                                                       mask,
+                                                       clip);
+        if (unlikely (status))
+            goto BAIL;
+    }
+
     _cairo_cogl_maybe_log_clip (surface, &extents);
 
     cairo_matrix_init_identity (&identity);
@@ -2145,25 +2266,10 @@ _cairo_cogl_surface_mask (void                    *abstract_surface,
     cogl_object_unref (pipeline);
 
 BAIL:
-    return status;
-}
+    if (tex_clip)
+        _cairo_clip_destroy (tex_clip);
 
-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;
-    }
+    return status;
 }
 
 static cairo_bool_t
@@ -2414,6 +2520,7 @@ _cairo_cogl_surface_stroke (void                       *abstract_surface,
     gboolean one_shot = TRUE;
     CoglPrimitive *prim = NULL;
     cairo_bool_t new_prim = FALSE;
+    cairo_clip_t *tex_clip;
 
     if (! is_operator_supported (op))
 	return CAIRO_INT_STATUS_UNSUPPORTED;
@@ -2468,12 +2575,24 @@ _cairo_cogl_surface_stroke (void                       *abstract_surface,
 #endif
     }
 
-    pipeline = get_source_mask_operator_destination_pipeline (NULL, source,
-							      op, surface, &extents);
-    if (!pipeline)
-	return CAIRO_INT_STATUS_UNSUPPORTED;
+    pipeline =
+        get_source_mask_operator_destination_pipeline (NULL,
+                                                       source,
+                                                       op,
+                                                       surface,
+                                                       &extents,
+                                                       &tex_clip);
+    if (!pipeline) {
+	status = CAIRO_INT_STATUS_UNSUPPORTED;
+        goto BAIL;
+    }
 
-    _cairo_cogl_maybe_log_clip (surface, &extents);
+    if (tex_clip) {
+        tex_clip = _cairo_clip_intersect_clip (tex_clip, clip);
+        _cairo_cogl_log_clip (surface, tex_clip);
+    } else {
+        _cairo_cogl_maybe_log_clip (surface, &extents);
+    }
 
     _cairo_cogl_journal_log_primitive (surface, pipeline, prim, transform);
 
@@ -2482,7 +2601,11 @@ _cairo_cogl_surface_stroke (void                       *abstract_surface,
     if (new_prim)
 	cogl_object_unref (prim);
 
-    return CAIRO_INT_STATUS_SUCCESS;
+BAIL:
+    if (tex_clip)
+        _cairo_clip_destroy (tex_clip);
+
+    return status;
 }
 
 static cairo_cogl_path_fill_meta_t *
@@ -2624,6 +2747,7 @@ _cairo_cogl_surface_fill (void			    *abstract_surface,
     CoglPrimitive *prim = NULL;
     cairo_bool_t new_prim = FALSE;
     CoglPipeline *pipeline;
+    cairo_clip_t *tex_clip;
 
     if (! is_operator_supported (op))
 	return CAIRO_INT_STATUS_UNSUPPORTED;
@@ -2675,12 +2799,24 @@ _cairo_cogl_surface_fill (void			    *abstract_surface,
 
 #endif /* !FILL_WITH_COGL_PATH */
 
-    pipeline = get_source_mask_operator_destination_pipeline (NULL, source,
-							      op, surface, &extents);
-    if (!pipeline)
-	return CAIRO_INT_STATUS_UNSUPPORTED;
+    pipeline =
+        get_source_mask_operator_destination_pipeline (NULL,
+                                                       source,
+                                                       op,
+                                                       surface,
+                                                       &extents,
+                                                       &tex_clip);
+    if (!pipeline) {
+	status = CAIRO_INT_STATUS_UNSUPPORTED;
+        goto BAIL;
+    }
 
-    _cairo_cogl_maybe_log_clip (surface, &extents);
+    if (tex_clip) {
+        tex_clip = _cairo_clip_intersect_clip (tex_clip, clip);
+        _cairo_cogl_log_clip (surface, tex_clip);
+    } else {
+        _cairo_cogl_maybe_log_clip (surface, &extents);
+    }
 
 #ifndef FILL_WITH_COGL_PATH
     _cairo_cogl_journal_log_primitive (surface, pipeline, prim, transform);
@@ -2696,7 +2832,11 @@ _cairo_cogl_surface_fill (void			    *abstract_surface,
     /* The journal will take a reference on the pipeline... */
     cogl_object_unref (pipeline);
 
-    return CAIRO_INT_STATUS_SUCCESS;
+BAIL:
+    if (tex_clip)
+        _cairo_clip_destroy (tex_clip);
+
+    return status;
 }
 
 cairo_int_status_t
@@ -2741,8 +2881,13 @@ _cairo_cogl_surface_fill_rectangle (void		     *abstract_surface,
 	double x2 = x1 + width;
 	double y2 = y1 + height;
 
-	pipeline = get_source_mask_operator_destination_pipeline (NULL, source,
-								  op, surface, NULL);
+	pipeline =
+            get_source_mask_operator_destination_pipeline (NULL,
+                                                           source,
+                                                           op,
+                                                           surface,
+                                                           NULL,
+                                                           NULL);
 	if (!pipeline)
 	    return CAIRO_INT_STATUS_UNSUPPORTED;
 
commit 98eaa778b70715e57cd3ce927b71a9ffdcd0f39f
Author: George Matsumura <gmmatsumura01 at bvsd.org>
Date:   Sun Jul 5 00:04:43 2020 -0600

    cogl: Support combinations of mask and source patterns correctly
    
    Prior to this change, the set of template pipelines did not
    properly apply to the full combination of mask and source patterns,
    which could be either textures or solids.
    
    Signed-off-by: George Matsumura <gmmatsumura01 at bvsd.org>

diff --git a/src/cairo-cogl-private.h b/src/cairo-cogl-private.h
index b97a900c2..5f3754558 100644
--- a/src/cairo-cogl-private.h
+++ b/src/cairo-cogl-private.h
@@ -41,10 +41,18 @@
 #include <cogl/cogl2-experimental.h>
 
 typedef enum _cairo_cogl_template_type {
+	/* solid source */
     CAIRO_COGL_TEMPLATE_TYPE_SOLID,
+    /* texture source */
     CAIRO_COGL_TEMPLATE_TYPE_TEXTURE,
-    CAIRO_COGL_TEMPLATE_TYPE_MASK_SOLID,
-    CAIRO_COGL_TEMPLATE_TYPE_MASK_TEXTURE,
+    /* solid source with solid mask */
+    CAIRO_COGL_TEMPLATE_TYPE_SOLID_MASK_SOLID,
+    /* solid source with texture mask */
+    CAIRO_COGL_TEMPLATE_TYPE_TEXTURE_MASK_SOLID,
+    /* texture source with solid mask */
+    CAIRO_COGL_TEMPLATE_TYPE_SOLID_MASK_TEXTURE,
+    /* texture source with texture mask */
+    CAIRO_COGL_TEMPLATE_TYPE_TEXTURE_MASK_TEXTURE,
     CAIRO_COGL_TEMPLATE_TYPE_COUNT
 } cairo_cogl_template_type;
 
@@ -60,15 +68,18 @@ typedef struct _cairo_cogl_device {
     size_t buffer_stack_offset;
     guint8 *buffer_stack_pointer;
 
-    /* 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.
+    /* This is a limited set of templates because we don't support the
+     * full range of operators that cairo has. The CAIRO_OPERATOR_ADD
+     * is the operator enum with the highest value that we support so
+     * we cap the size of the array by that.
      *
-     * 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 a non-solid source. For each of those, we also have
+     * additional templates for when we have a solid mask or a
+     * non-solid mask, for a total of six templates per operator.
      *
-     * 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.
+     * The templates are set to null at device creation time and only
+     * actually created on their first use.
      */
     CoglPipeline *template_pipelines[CAIRO_OPERATOR_ADD + 1][CAIRO_COGL_TEMPLATE_TYPE_COUNT];
 
diff --git a/src/cairo-cogl-surface.c b/src/cairo-cogl-surface.c
index 8f0286ab3..0b2706ea5 100644
--- a/src/cairo-cogl-surface.c
+++ b/src/cairo-cogl-surface.c
@@ -1721,6 +1721,139 @@ BAIL:
     }
 }
 
+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_CLEAR:
+        status = set_blend (pipeline, "RGBA = ADD (0, 0)");
+        break;
+    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_OUT:
+        status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR * (1 - DST_COLOR[A]), 0)");
+        break;
+    case CAIRO_OPERATOR_ATOP:
+        status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR * DST_COLOR[A], DST_COLOR * (1 - SRC_COLOR[A]))");
+        break;
+    case CAIRO_OPERATOR_DEST:
+        status = set_blend (pipeline, "RGBA = ADD (0, DST_COLOR)");
+        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_DEST_OUT:
+        status = set_blend (pipeline, "RGBA = ADD (0, DST_COLOR * (1 - SRC_COLOR[A]))");
+        break;
+    case CAIRO_OPERATOR_DEST_ATOP:
+        status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR * (1 - DST_COLOR[A]), DST_COLOR * SRC_COLOR[A])");
+        break;
+    case CAIRO_OPERATOR_XOR:
+        status = set_blend (pipeline, "RGBA = ADD (SRC_COLOR * (1 - DST_COLOR[A]), DST_COLOR * (1 - 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_type (cairo_cogl_device_t *dev,
+                              cairo_operator_t op,
+                              cairo_cogl_template_type type)
+{
+    CoglPipeline *pipeline;
+    CoglColor color;
+
+    if (!dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID]) {
+        CoglPipeline *base = cogl_pipeline_new (dev->cogl_context);
+
+        if (!_cairo_cogl_setup_op_state (base, op)) {
+            cogl_object_unref (base);
+            return;
+        }
+
+        dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID] = base;
+    }
+
+    switch ((int)type)
+    {
+    case CAIRO_COGL_TEMPLATE_TYPE_SOLID:
+        return;
+    case CAIRO_COGL_TEMPLATE_TYPE_SOLID_MASK_SOLID:
+        pipeline =
+            cogl_pipeline_copy (dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID]);
+        cogl_pipeline_set_layer_combine (pipeline, 0,
+                                         "RGBA = MODULATE (PRIMARY, CONSTANT[A])",
+                                         NULL);
+        cogl_pipeline_set_layer_combine_constant (pipeline, 0, &color);
+        break;
+    case CAIRO_COGL_TEMPLATE_TYPE_TEXTURE_MASK_SOLID:
+        pipeline =
+            cogl_pipeline_copy (dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID]);
+        cogl_pipeline_set_layer_combine (pipeline, 0,
+                                         "RGBA = MODULATE (PRIMARY, TEXTURE[A])",
+                                         NULL);
+        cogl_pipeline_set_layer_texture (pipeline, 0, dev->dummy_texture);
+        break;
+    case CAIRO_COGL_TEMPLATE_TYPE_TEXTURE:
+        pipeline =
+            cogl_pipeline_copy (dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID]);
+        cogl_pipeline_set_layer_texture (pipeline, 0, dev->dummy_texture);
+        break;
+    case CAIRO_COGL_TEMPLATE_TYPE_SOLID_MASK_TEXTURE:
+        pipeline =
+            cogl_pipeline_copy (dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID]);
+        cogl_pipeline_set_layer_texture (pipeline, 0, dev->dummy_texture);
+        cogl_pipeline_set_layer_combine (pipeline, 1,
+                                         "RGBA = MODULATE (PREVIOUS, CONSTANT[A])",
+                                         NULL);
+        cogl_pipeline_set_layer_combine_constant (pipeline, 1, &color);
+        break;
+    case CAIRO_COGL_TEMPLATE_TYPE_TEXTURE_MASK_TEXTURE:
+        pipeline =
+            cogl_pipeline_copy (dev->template_pipelines[op][CAIRO_COGL_TEMPLATE_TYPE_SOLID]);
+        cogl_pipeline_set_layer_texture (pipeline, 0, dev->dummy_texture);
+        cogl_pipeline_set_layer_combine (pipeline, 1,
+                                         "RGBA = MODULATE (PREVIOUS, TEXTURE[A])",
+                                         NULL);
+        cogl_pipeline_set_layer_texture (pipeline, 1, dev->dummy_texture);
+        break;
+    default:
+        g_warning ("Invalid cogl pipeline template type\n");
+        return;
+    }
+
+    dev->template_pipelines[op][type] = pipeline;
+}
+
 static void
 set_layer_texture_with_attributes (CoglPipeline *pipeline,
 				   int layer_index,
@@ -1758,27 +1891,49 @@ get_source_mask_operator_destination_pipeline (const cairo_pattern_t *mask,
 {
     cairo_cogl_template_type template_type;
     CoglPipeline *pipeline;
+    cairo_cogl_device_t *dev = to_device(destination->base.device);
     int layer_counter = 0;
 
     switch ((int)source->type)
     {
     case CAIRO_PATTERN_TYPE_SOLID:
-	template_type = mask ?
-	    CAIRO_COGL_TEMPLATE_TYPE_MASK_SOLID : CAIRO_COGL_TEMPLATE_TYPE_SOLID;
-	break;
+        if (mask) {
+            if (mask->type == CAIRO_PATTERN_TYPE_SOLID)
+                template_type =
+                    CAIRO_COGL_TEMPLATE_TYPE_SOLID_MASK_SOLID;
+            else
+                template_type =
+                    CAIRO_COGL_TEMPLATE_TYPE_TEXTURE_MASK_SOLID;
+        } else {
+            template_type = 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;
+        if (mask) {
+            if (mask->type == CAIRO_PATTERN_TYPE_SOLID)
+                template_type =
+                    CAIRO_COGL_TEMPLATE_TYPE_SOLID_MASK_TEXTURE;
+            else
+                template_type =
+                    CAIRO_COGL_TEMPLATE_TYPE_TEXTURE_MASK_TEXTURE;
+        } else {
+            template_type = 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]);
+    /* Lazily create pipeline templates */
+    if (unlikely (dev->template_pipelines[op][template_type] == NULL))
+        create_templates_for_op_type (dev, op, template_type);
+
+    pipeline =
+        cogl_pipeline_copy (dev->template_pipelines[op][template_type]);
 
     if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
 	cairo_solid_pattern_t *solid_pattern = (cairo_solid_pattern_t *)source;
@@ -1921,11 +2076,18 @@ static cairo_bool_t
 is_operator_supported (cairo_operator_t op)
 {
     switch ((int)op) {
+    case CAIRO_OPERATOR_CLEAR:
     case CAIRO_OPERATOR_SOURCE:
     case CAIRO_OPERATOR_OVER:
     case CAIRO_OPERATOR_IN:
+    case CAIRO_OPERATOR_OUT:
+    case CAIRO_OPERATOR_ATOP:
+    case CAIRO_OPERATOR_DEST:
     case CAIRO_OPERATOR_DEST_OVER:
     case CAIRO_OPERATOR_DEST_IN:
+    case CAIRO_OPERATOR_DEST_OUT:
+    case CAIRO_OPERATOR_DEST_ATOP:
+    case CAIRO_OPERATOR_XOR:
     case CAIRO_OPERATOR_ADD:
 	return TRUE;
 
@@ -2814,82 +2976,6 @@ static const cairo_device_backend_t _cairo_cogl_device_backend = {
     _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 (dev->cogl_context);
-    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)
 {
@@ -2906,13 +2992,8 @@ cairo_cogl_device_create (CoglContext *cogl_context)
     dev->buffer_stack = NULL;
     dev->buffer_stack_size = 4096;
 
+    /* Set all template pipelines to NULL */
     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,
commit 97a98d9145f4dea411a2e42f827a4e50b02edcf1
Author: George Matsumura <gmmatsumura01 at bvsd.org>
Date:   Fri Jul 3 23:48:35 2020 -0600

    cogl: Fix aliased vertex buffer offset
    
    This fixes a bug in which the texture coordinate attributes were
    being read from the wrong location in the mapped buffer, due to a
    typo in the original code.
    
    Signed-off-by: George Matsumura <gmmatsumura01 at bvsd.org>

diff --git a/src/cairo-cogl-surface.c b/src/cairo-cogl-surface.c
index 9353630dd..8f0286ab3 100644
--- a/src/cairo-cogl-surface.c
+++ b/src/cairo-cogl-surface.c
@@ -675,7 +675,7 @@ _cairo_cogl_traps_to_composite_prim_p2t2 (cairo_cogl_surface_t *surface,
     CoglAttribute *tex_coords = cogl_attribute_new (buffer,
 						    "cogl_tex_coord0_in",
 						    sizeof (CoglVertexP2),
-						    0,
+						    offset,
 						    2,
 						    COGL_ATTRIBUTE_TYPE_FLOAT);
     CoglPrimitive *prim;
commit 3e810fe2d179f9f5aed4a551447ca312ee8029c7
Author: George Matsumura <gmmatsumura01 at bvsd.org>
Date:   Wed Jul 1 14:53:28 2020 -0600

    cogl: Fix handling of translated pattern matrices
    
    Due to how cairo matrix math works, the x0 and y0 elements of the
    texture matrix were not being normalized, leading to incorrect
    results in several tests.
    
    Signed-off-by: George Matsumura <gmmatsumura01 at bvsd.org>

diff --git a/src/cairo-cogl-surface.c b/src/cairo-cogl-surface.c
index 869671d3b..9353630dd 100644
--- a/src/cairo-cogl-surface.c
+++ b/src/cairo-cogl-surface.c
@@ -1589,15 +1589,21 @@ _cairo_cogl_acquire_pattern_texture (const cairo_pattern_t *pattern,
 	}
 #endif
 
-	cairo_matrix_init_identity (&attributes->matrix);
+	attributes->matrix = pattern->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 */
+	 * coordinates to normalized texture coordinates. Since
+         * cairo_matrix_scale does not scale the x0 and y0 components,
+         * which is required for translations in normalized
+         * coordinates, use a custom solution here. */
+        double xscale = 1.0 / cogl_texture_get_width (texture);
+        double yscale = 1.0 / cogl_texture_get_height (texture);
+        attributes->matrix.xx *= xscale;
+        attributes->matrix.yx *= yscale;
+        attributes->matrix.xy *= xscale;
+        attributes->matrix.yy *= yscale;
+        attributes->matrix.x0 *= xscale;
+        attributes->matrix.y0 *= yscale;
 
 	attributes->extend = pattern->extend;
 	attributes->filter = CAIRO_FILTER_BILINEAR;
@@ -1629,14 +1635,22 @@ _cairo_cogl_acquire_pattern_texture (const cairo_pattern_t *pattern,
 
 	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);
 
+	/* Convert from un-normalized source coordinates in backend
+	 * coordinates to normalized texture coordinates. Since
+         * cairo_matrix_scale does not scale the x0 and y0 components,
+         * which is required for translations in normalized
+         * coordinates, use a custom solution here. */
+        double xscale = 1.0 / cogl_texture_get_width (texture);
+        double yscale = 1.0 / cogl_texture_get_height (texture);
+        attributes->matrix.xx *= xscale;
+        attributes->matrix.yx *= yscale;
+        attributes->matrix.xy *= xscale;
+        attributes->matrix.yy *= yscale;
+        attributes->matrix.x0 *= xscale;
+        attributes->matrix.y0 *= yscale;
+
 	attributes->matrix = texture_matrix;
 	attributes->extend = pattern->extend;
 	attributes->filter = CAIRO_FILTER_NEAREST;
@@ -1656,10 +1670,6 @@ BAIL:
 	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,
@@ -1677,29 +1687,31 @@ BAIL:
 	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);
+        attributes->matrix = pattern->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);
+	double a = linear_pattern->pd2.x - linear_pattern->pd1.x;
+	double b = linear_pattern->pd2.y - linear_pattern->pd1.y;
+	double 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);
+	/* Convert from un-normalized source coordinates in backend
+	 * coordinates to normalized texture coordinates. Since
+         * cairo_matrix_scale does not scale the x0 and y0 components,
+         * which is required for translations in normalized
+         * coordinates, use a custom solution here. */
+	double dist = sqrtf (a*a + b*b);
+	double scale = 1.0 / dist;
+        attributes->matrix.xx *= scale;
+        attributes->matrix.yx *= scale;
+        attributes->matrix.xy *= scale;
+        attributes->matrix.yy *= scale;
+        attributes->matrix.x0 *= scale;
+        attributes->matrix.y0 *= scale;
 
 	return cogl_object_ref (linear_texture->texture);
     }
commit 1dce0112efb2ef007983c623348bce68c8914682
Author: George Matsumura <gmmatsumura01 at bvsd.org>
Date:   Tue Jun 30 19:39:40 2020 -0600

    cogl: Trailing whitespace fix
    
    Signed-off-by: George Matsumura <gmmatsumura01 at bvsd.org>

diff --git a/src/cairo-cogl-context.c b/src/cairo-cogl-context.c
index 4004c19f3..93703c956 100644
--- a/src/cairo-cogl-context.c
+++ b/src/cairo-cogl-context.c
@@ -646,7 +646,7 @@ _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 *surface =
         _cairo_cogl_get_cogl_surface (cr->base.gstate->target);
 
     if (cr->path_is_rectangle) {
@@ -689,7 +689,7 @@ _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 *surface =
         _cairo_cogl_get_cogl_surface (cr->base.gstate->target);
 
     if (cr->path_is_rectangle) {
@@ -728,7 +728,7 @@ _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 *surface =
         _cairo_cogl_get_cogl_surface (cr->base.gstate->target);
 
     /* This operator can't use an accelerated rectangle path yet */
@@ -757,7 +757,7 @@ _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 *surface =
         _cairo_cogl_get_cogl_surface (cr->base.gstate->target);
 
     /* This operator can't use an accelerated rectangle path yet */
@@ -782,7 +782,7 @@ _cairo_cogl_context_clip (void *abstract_cr)
 {
     cairo_cogl_context_t *cr = abstract_cr;
     cairo_status_t status;
-    cairo_cogl_surface_t *surface = 
+    cairo_cogl_surface_t *surface =
         _cairo_cogl_get_cogl_surface (cr->base.gstate->target);
 
     /* This operator can't use an accelerated rectangle path yet */
@@ -811,7 +811,7 @@ _cairo_cogl_context_clip_preserve (void *abstract_cr)
 {
     cairo_cogl_context_t *cr = abstract_cr;
     cairo_status_t status;
-    cairo_cogl_surface_t *surface = 
+    cairo_cogl_surface_t *surface =
         _cairo_cogl_get_cogl_surface (cr->base.gstate->target);
 
     /* This operator can't use an accelerated rectangle path yet */
commit fc8e68b37ae07dce6b5868ae18e02eff9b04382e
Author: George Matsumura <gmmatsumura01 at bvsd.org>
Date:   Tue Jun 30 19:32:22 2020 -0600

    cogl: Fix crash when specifying only mask surface
    
    Cogl enters into an infinite recursion if a texture is specified
    for layer 1 of a pipeline but no texture is specified for layer 0.
    This works around the bug by setting the mask texture on layer 0
    if there is no source texture.
    
    Signed-off-by: George Matsumura <gmmatsumura01 at bvsd.org>

diff --git a/src/cairo-cogl-surface.c b/src/cairo-cogl-surface.c
index 6e199141b..869671d3b 100644
--- a/src/cairo-cogl-surface.c
+++ b/src/cairo-cogl-surface.c
@@ -1746,6 +1746,7 @@ get_source_mask_operator_destination_pipeline (const cairo_pattern_t *mask,
 {
     cairo_cogl_template_type template_type;
     CoglPipeline *pipeline;
+    int layer_counter = 0;
 
     switch ((int)source->type)
     {
@@ -1783,7 +1784,10 @@ get_source_mask_operator_destination_pipeline (const cairo_pattern_t *mask,
 						 &attributes);
 	if (!texture)
 	    goto BAIL;
-	set_layer_texture_with_attributes (pipeline, 0, texture, &attributes);
+	set_layer_texture_with_attributes (pipeline,
+                                           layer_counter++,
+                                           texture,
+                                           &attributes);
 	cogl_object_unref (texture);
     }
 
@@ -1796,7 +1800,9 @@ get_source_mask_operator_destination_pipeline (const cairo_pattern_t *mask,
 				     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);
+	    cogl_pipeline_set_layer_combine_constant (pipeline,
+                                                      layer_counter++,
+                                                      &color);
 	} else {
 	    cairo_cogl_texture_attributes_t attributes;
 	    CoglTexture *texture =
@@ -1806,7 +1812,10 @@ get_source_mask_operator_destination_pipeline (const cairo_pattern_t *mask,
 						     &attributes);
 	    if (!texture)
 		goto BAIL;
-	    set_layer_texture_with_attributes (pipeline, 1, texture, &attributes);
+	    set_layer_texture_with_attributes (pipeline,
+                                               layer_counter++,
+                                               texture,
+                                               &attributes);
 	    cogl_object_unref (texture);
 	}
     }
commit 75d843208cf33b8ba0273745582bcaabf900ea24
Author: George Matsumura <gmmatsumura01 at bvsd.org>
Date:   Mon Jun 29 13:37:20 2020 -0600

    cogl: Move context, device, and surface members to most fitting places
    
    The buffer stack was moved from the surface to the device, as
    mapped buffers are shared across all surfaces using the device
    and more than one surface may be doing so. Stemming from this, if
    a surface tries to map a buffer without first unmapping one that
    has been mapped by another surface, it will trigger an error.
    
    The parent backend functions were moved from the device to the
    context, as it is possible that the context creation function could
    be passed a non-cogl device. Prior to this change, many of the
    subsurface tests segfaulted.
    
    Signed-off-by: George Matsumura <gmmatsumura01 at bvsd.org>

diff --git a/boilerplate/cairo-boilerplate-cogl.c b/boilerplate/cairo-boilerplate-cogl.c
index e11a8b8de..037a34147 100644
--- a/boilerplate/cairo-boilerplate-cogl.c
+++ b/boilerplate/cairo-boilerplate-cogl.c
@@ -70,7 +70,6 @@ _cairo_boilerplate_cogl_create_offscreen_color_surface (const char		*name,
 {
     cairo_device_t *device;
     CoglTexture *tex;
-    CoglHandle offscreen;
     CoglFramebuffer *fb;
     cogl_closure_t *closure;
     cairo_status_t status;
@@ -81,8 +80,7 @@ _cairo_boilerplate_cogl_create_offscreen_color_surface (const char		*name,
     device = cairo_cogl_device_create (context);
     tex = cogl_texture_2d_new_with_size (context, width, height);
     cogl_texture_set_components (tex, COGL_TEXTURE_COMPONENTS_RGBA);
-    offscreen = cogl_offscreen_new_with_texture (tex);
-    fb = COGL_FRAMEBUFFER (offscreen);
+    fb = cogl_offscreen_new_with_texture (tex);
 
     cogl_framebuffer_allocate (fb, NULL);
     cogl_framebuffer_orthographic (fb, 0, 0,
@@ -116,7 +114,6 @@ _cairo_boilerplate_cogl_create_onscreen_color_surface (const char	       *name,
 						       void		      **abstract_closure)
 {
     cairo_device_t *device;
-    CoglOnscreen *onscreen;
     CoglFramebuffer *fb;
     cogl_closure_t *closure;
     cairo_status_t status;
@@ -125,10 +122,9 @@ _cairo_boilerplate_cogl_create_onscreen_color_surface (const char	       *name,
 	context = cogl_context_new (NULL, NULL);
 
     device = cairo_cogl_device_create (context);
-    onscreen = cogl_onscreen_new (context, width, height);
-    fb = COGL_FRAMEBUFFER (onscreen);
+    fb = cogl_onscreen_new (context, width, height);
 
-    cogl_onscreen_show (onscreen);
+    cogl_onscreen_show (fb);
 
     cogl_framebuffer_orthographic (fb, 0, 0,
                                    cogl_framebuffer_get_width (fb),
diff --git a/src/cairo-cogl-context-private.h b/src/cairo-cogl-context-private.h
index 0a7185ef1..a1b3152b1 100644
--- a/src/cairo-cogl-context-private.h
+++ b/src/cairo-cogl-context-private.h
@@ -38,12 +38,18 @@
 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_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;
 } cairo_cogl_context_t;
 
 cairo_t *
diff --git a/src/cairo-cogl-context.c b/src/cairo-cogl-context.c
index 34aa32ed7..4004c19f3 100644
--- a/src/cairo-cogl-context.c
+++ b/src/cairo-cogl-context.c
@@ -58,6 +58,7 @@
 #include "cairo-freed-pool-private.h"
 #include "cairo-arc-private.h"
 #include "cairo-path-fixed-private.h"
+#include "cairo-surface-subsurface-inline.h"
 
 #include <glib.h>
 
@@ -75,7 +76,7 @@ _cairo_cogl_context_rectangle_real (cairo_cogl_context_t *cr,
 				    double width, double height)
 {
     cairo_status_t status;
-    status = cr->dev->backend_parent.rectangle (cr, x, y, width, height);
+    status = cr->backend_parent.rectangle (cr, x, y, width, height);
     if (unlikely (status))
 	return status;
 
@@ -124,7 +125,7 @@ _cairo_cogl_context_restore (void *abstract_cr)
     }
 
     cr->path_ctm_age++;
-    return cr->dev->backend_parent.restore (abstract_cr);
+    return cr->backend_parent.restore (abstract_cr);
 }
 
 static cairo_status_t
@@ -139,7 +140,7 @@ _cairo_cogl_context_translate (void *abstract_cr, double tx, double ty)
     }
 
     cr->path_ctm_age++;
-    return cr->dev->backend_parent.translate (abstract_cr, tx, ty);
+    return cr->backend_parent.translate (abstract_cr, tx, ty);
 }
 
 static cairo_status_t
@@ -154,7 +155,7 @@ _cairo_cogl_context_scale (void *abstract_cr, double sx, double sy)
     }
 
     cr->path_ctm_age++;
-    return cr->dev->backend_parent.scale (abstract_cr, sx, sy);
+    return cr->backend_parent.scale (abstract_cr, sx, sy);
 }
 
 static cairo_status_t
@@ -169,7 +170,7 @@ _cairo_cogl_context_rotate (void *abstract_cr, double theta)
     }
 
     cr->path_ctm_age++;
-    return cr->dev->backend_parent.rotate (abstract_cr, theta);
+    return cr->backend_parent.rotate (abstract_cr, theta);
 }
 
 static cairo_status_t
@@ -184,7 +185,7 @@ _cairo_cogl_context_transform (void *abstract_cr, const cairo_matrix_t *matrix)
     }
 
     cr->path_ctm_age++;
-    return cr->dev->backend_parent.transform (abstract_cr, matrix);
+    return cr->backend_parent.transform (abstract_cr, matrix);
 }
 
 static cairo_status_t
@@ -199,7 +200,7 @@ _cairo_cogl_context_set_matrix (void *abstract_cr, const cairo_matrix_t *matrix)
     }
 
     cr->path_ctm_age++;
-    return cr->dev->backend_parent.set_matrix (abstract_cr, matrix);
+    return cr->backend_parent.set_matrix (abstract_cr, matrix);
 }
 
 static cairo_status_t
@@ -214,7 +215,7 @@ _cairo_cogl_context_set_identity_matrix (void *abstract_cr)
     }
 
     cr->path_ctm_age++;
-    return cr->dev->backend_parent.set_identity_matrix (abstract_cr);
+    return cr->backend_parent.set_identity_matrix (abstract_cr);
 }
 
 static cairo_status_t
@@ -229,7 +230,7 @@ _cairo_cogl_context_new_path (void *abstract_cr)
 	    return status;
     }
 
-    status = cr->dev->backend_parent.new_path (abstract_cr);
+    status = cr->backend_parent.new_path (abstract_cr);
     if (unlikely (status))
 	return status;
 
@@ -252,7 +253,7 @@ _cairo_cogl_context_new_sub_path (void *abstract_cr)
 	    return status;
     }
 
-    status = cr->dev->backend_parent.new_sub_path (abstract_cr);
+    status = cr->backend_parent.new_sub_path (abstract_cr);
     if (unlikely (status))
 	return status;
 
@@ -274,7 +275,7 @@ _cairo_cogl_context_move_to (void *abstract_cr, double x, double y)
 	    return status;
     }
 
-    status = cr->dev->backend_parent.move_to (abstract_cr, x, y);
+    status = cr->backend_parent.move_to (abstract_cr, x, y);
     if (unlikely (status))
 	return status;
 
@@ -297,7 +298,7 @@ _cairo_cogl_context_line_to (void *abstract_cr, double x, double y)
 	    return status;
     }
 
-    status = cr->dev->backend_parent.line_to (abstract_cr, x, y);
+    status = cr->backend_parent.line_to (abstract_cr, x, y);
     if (unlikely (status))
 	return status;
 
@@ -328,7 +329,7 @@ _cairo_cogl_context_curve_to (void *abstract_cr,
 	    return status;
     }
 
-    status = cr->dev->backend_parent.curve_to (abstract_cr, x1, y1, x2, y2, x3, y3);
+    status = cr->backend_parent.curve_to (abstract_cr, x1, y1, x2, y2, x3, y3);
     if (unlikely (status))
 	return status;
 
@@ -366,7 +367,7 @@ _cairo_cogl_context_arc (void *abstract_cr,
 	    return status;
     }
 
-    status = cr->dev->backend_parent.arc (abstract_cr, xc, yc, radius, angle1, angle2, forward);
+    status = cr->backend_parent.arc (abstract_cr, xc, yc, radius, angle1, angle2, forward);
     if (unlikely (status))
 	return status;
 
@@ -419,7 +420,7 @@ _cairo_cogl_context_rel_move_to (void *abstract_cr, double dx, double dy)
 	    return status;
     }
 
-    status = cr->dev->backend_parent.rel_move_to (abstract_cr, dx, dy);
+    status = cr->backend_parent.rel_move_to (abstract_cr, dx, dy);
     if (unlikely (status))
 	return status;
 
@@ -442,7 +443,7 @@ _cairo_cogl_context_rel_line_to (void *abstract_cr, double dx, double dy)
 	    return status;
     }
 
-    status = cr->dev->backend_parent.rel_line_to (abstract_cr, dx, dy);
+    status = cr->backend_parent.rel_line_to (abstract_cr, dx, dy);
     if (unlikely (status))
 	return status;
 
@@ -474,7 +475,7 @@ _cairo_cogl_context_rel_curve_to (void *abstract_cr,
 	    return status;
     }
 
-    status = cr->dev->backend_parent.rel_curve_to (abstract_cr, dx1, dy1, dx2, dy2, dx3, dy3);
+    status = cr->backend_parent.rel_curve_to (abstract_cr, dx1, dy1, dx2, dy2, dx3, dy3);
     if (unlikely (status))
 	return status;
 
@@ -512,7 +513,7 @@ _cairo_cogl_context_arc_to (void *abstract_cr,
 	    return status;
     }
 
-    status = cr->dev->backend_parent.arc_to (abstract_cr, x1, y1, x2, y2, radius);
+    status = cr->backend_parent.arc_to (abstract_cr, x1, y1, x2, y2, radius);
     if (unlikely (status))
 	return status;
 #warning "FIXME"
@@ -533,7 +534,7 @@ _cairo_cogl_rel_arc_to (void *cr,
 	    return status;
     }
 
-    status = cr->dev->backend_parent.rel_arc_to (abstract_cr, dx1, dy2, dx2, dy2, radius);
+    status = cr->backend_parent.rel_arc_to (abstract_cr, dx1, dy2, dx2, dy2, radius);
     if (unlikely (status))
 	return status;
 #warning "FIXME"
@@ -552,7 +553,7 @@ _cairo_cogl_context_close_path (void *abstract_cr)
 	    return status;
     }
 
-    status = cr->dev->backend_parent.close_path (abstract_cr);
+    status = cr->backend_parent.close_path (abstract_cr);
     if (unlikely (status))
 	return status;
 
@@ -623,31 +624,55 @@ _cairo_cogl_surface_set_side_band_state (cairo_cogl_surface_t *surface,
     }
 }
 
+static cairo_cogl_surface_t *
+_cairo_cogl_get_cogl_surface (cairo_surface_t *target)
+{
+    /* Collapse all nested subsurfaces. If another method of target
+     * surface redirection is added to cairo, we can add a test here. */
+    while (1) {
+        if (_cairo_surface_is_subsurface (target)) {
+            target = _cairo_surface_subsurface_get_target (target);
+        } else if (target->type == CAIRO_SURFACE_TYPE_COGL) {
+            return (cairo_cogl_surface_t *)target;
+        } else {
+            /* return NULL if the target is not a cogl surface */
+            return NULL;
+        }
+    }
+}
+
 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;
+    cairo_cogl_surface_t *surface = 
+        _cairo_cogl_get_cogl_surface (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 (surface) {
+            status = _cairo_cogl_surface_fill_rectangle ((cairo_surface_t *)surface,
+                                                         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;
+        }
+
+        status = _flush_cr_rectangle (cr);
+        if (unlikely (status))
+            return status;
+    }
+
+    if (surface)
+        _cairo_cogl_surface_set_side_band_state (surface, cr);
+
+    status = cr->backend_parent.fill (abstract_cr);
     if (unlikely (status))
 	return status;
 
@@ -664,14 +689,37 @@ _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_t *surface = 
+        _cairo_cogl_get_cogl_surface (cr->base.gstate->target);
 
-    _cairo_cogl_surface_set_side_band_state (surface, cr);
+    if (cr->path_is_rectangle) {
+        if (surface) {
+            status = _cairo_cogl_surface_fill_rectangle ((cairo_surface_t *)surface,
+                                                         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;
+        }
 
-    status = cr->dev->backend_parent.fill_preserve (abstract_cr);
+        status = _flush_cr_rectangle (cr);
+        if (unlikely (status))
+            return status;
+    }
+
+    if (surface)
+        _cairo_cogl_surface_set_side_band_state (surface, cr);
+
+    status = cr->backend_parent.fill_preserve (abstract_cr);
     if (unlikely (status))
 	return status;
 
+DONE:
     return CAIRO_STATUS_SUCCESS;
 }
 
@@ -680,11 +728,20 @@ _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_t *surface = 
+        _cairo_cogl_get_cogl_surface (cr->base.gstate->target);
 
-    _cairo_cogl_surface_set_side_band_state (surface, cr);
+    /* This operator can't use an accelerated rectangle path yet */
+    if (cr->path_is_rectangle) {
+        status = _flush_cr_rectangle (cr);
+        if (unlikely (status))
+            return status;
+    }
 
-    status = cr->dev->backend_parent.stroke (abstract_cr);
+    if (surface)
+        _cairo_cogl_surface_set_side_band_state (surface, cr);
+
+    status = cr->backend_parent.stroke (abstract_cr);
     if (unlikely (status))
 	return status;
 
@@ -700,11 +757,20 @@ _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_t *surface = 
+        _cairo_cogl_get_cogl_surface (cr->base.gstate->target);
+
+    /* This operator can't use an accelerated rectangle path yet */
+    if (cr->path_is_rectangle) {
+        status = _flush_cr_rectangle (cr);
+        if (unlikely (status))
+            return status;
+    }
 
-    _cairo_cogl_surface_set_side_band_state (surface, cr);
+    if (surface)
+        _cairo_cogl_surface_set_side_band_state (surface, cr);
 
-    status = cr->dev->backend_parent.stroke_preserve (abstract_cr);
+    status = cr->backend_parent.stroke_preserve (abstract_cr);
     if (unlikely (status))
 	return status;
 
@@ -716,8 +782,20 @@ _cairo_cogl_context_clip (void *abstract_cr)
 {
     cairo_cogl_context_t *cr = abstract_cr;
     cairo_status_t status;
+    cairo_cogl_surface_t *surface = 
+        _cairo_cogl_get_cogl_surface (cr->base.gstate->target);
+
+    /* This operator can't use an accelerated rectangle path yet */
+    if (cr->path_is_rectangle) {
+        status = _flush_cr_rectangle (cr);
+        if (unlikely (status))
+            return status;
+    }
 
-    status = cr->dev->backend_parent.clip (abstract_cr);
+    if (surface)
+        _cairo_cogl_surface_set_side_band_state (surface, cr);
+
+    status = cr->backend_parent.clip (abstract_cr);
     if (unlikely (status))
 	return status;
 
@@ -728,75 +806,48 @@ _cairo_cogl_context_clip (void *abstract_cr)
     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);
-}
-
-static cairo_pattern_t *
-_cairo_cogl_context_pop_group (void *abstract_cr);
-
-void
-_cairo_cogl_context_set_custom_vtable_funcs (cairo_backend_t *backend);
-
-/* Pushed surfaces often take the form of image or recording surfaces,
- * which are unable to handle the rectangle fast path or tessellation
- * cacheing. Therfore, we need to restore the vtable functions from the
- * default context. */
 static cairo_status_t
-_cairo_cogl_context_push_group (void *abstract_cr, cairo_content_t content)
+_cairo_cogl_context_clip_preserve (void *abstract_cr)
 {
     cairo_cogl_context_t *cr = abstract_cr;
     cairo_status_t status;
+    cairo_cogl_surface_t *surface = 
+        _cairo_cogl_get_cogl_surface (cr->base.gstate->target);
 
-    /* Flush the values set in the side band to the normal path */
+    /* This operator can't use an accelerated rectangle path yet */
     if (cr->path_is_rectangle) {
         status = _flush_cr_rectangle (cr);
         if (unlikely (status))
             return status;
     }
 
-    /* Just assume the ctm will be modified */
-    cr->path_ctm_age++;
+    if (surface)
+        _cairo_cogl_surface_set_side_band_state (surface, cr);
 
-    status = cr->dev->backend_parent.push_group (abstract_cr, content);
+    status = cr->backend_parent.clip_preserve (abstract_cr);
     if (unlikely (status))
-        return status;
-
-    /* Restore all the vtable functions except the popping function */
-    memcpy (&cr->dev->backend, &cr->dev->backend_parent, sizeof (cairo_backend_t));
-    cr->dev->backend.pop_group = _cairo_cogl_context_pop_group;
+	return status;
 
     return CAIRO_STATUS_SUCCESS;
 }
 
-static cairo_pattern_t *
-_cairo_cogl_context_pop_group (void *abstract_cr)
+static void
+_cairo_cogl_context_destroy (void *abstract_cr)
 {
     cairo_cogl_context_t *cr = abstract_cr;
-    cairo_pattern_t *group_pattern;
 
-    /* The default popping function can still be found at
-     * backend_parent */
-    group_pattern = cr->dev->backend_parent.pop_group (abstract_cr);
+    _cairo_default_context_fini (&cr->base);
 
-    /* Skip restoring the vtable funcs if we are popping to a pushed
-     * surface */
-    if (cr->base.gstate->target->type == CAIRO_SURFACE_TYPE_COGL)
-        _cairo_cogl_context_set_custom_vtable_funcs (&cr->dev->backend);
+    _cairo_path_fixed_fini (&cr->user_path);
 
-    return group_pattern;
+    /* mark the context as invalid to protect against misuse */
+    cr->base.base.status = CAIRO_STATUS_NULL_POINTER;
+    _freed_pool_put (&context_pool, cr);
 }
 
+void
+_cairo_cogl_context_set_custom_vtable_funcs (cairo_backend_t *backend);
+
 /* 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
@@ -806,7 +857,6 @@ _cairo_cogl_context_pop_group (void *abstract_cr)
 cairo_t *
 _cairo_cogl_context_create (void *target)
 {
-    cairo_cogl_surface_t *surface = target;
     cairo_cogl_context_t *cr;
     cairo_status_t status;
 
@@ -823,18 +873,12 @@ _cairo_cogl_context_create (void *target)
 	return _cairo_create_in_error (status);
     }
 
-    cr->dev = (cairo_cogl_device_t *)surface->base.device;
+    memcpy (&cr->backend, cr->base.base.backend, sizeof (cairo_backend_t));
+    memcpy (&cr->backend_parent, cr->base.base.backend, sizeof (cairo_backend_t));
 
-    if (unlikely (cr->dev->backend_vtable_initialized == FALSE)) {
-	memcpy (&cr->dev->backend, cr->base.base.backend, sizeof (cairo_backend_t));
-	memcpy (&cr->dev->backend_parent, cr->base.base.backend, sizeof (cairo_backend_t));
+    _cairo_cogl_context_set_custom_vtable_funcs (&cr->backend);
 
-        _cairo_cogl_context_set_custom_vtable_funcs (&cr->dev->backend);
-
-	cr->dev->backend_vtable_initialized = TRUE;
-    }
-
-    cr->base.base.backend = &cr->dev->backend;
+    cr->base.base.backend = &cr->backend;
 
     _cairo_path_fixed_init (&cr->user_path);
     cr->path_is_rectangle = FALSE;
@@ -849,9 +893,6 @@ _cairo_cogl_context_set_custom_vtable_funcs (cairo_backend_t *backend)
 
     backend->restore = _cairo_cogl_context_restore;
 
-    backend->push_group = _cairo_cogl_context_push_group;
-    backend->pop_group = _cairo_cogl_context_pop_group;
-
     backend->translate = _cairo_cogl_context_translate;
     backend->scale = _cairo_cogl_context_scale;
     backend->rotate = _cairo_cogl_context_rotate;
@@ -886,4 +927,5 @@ _cairo_cogl_context_set_custom_vtable_funcs (cairo_backend_t *backend)
     backend->stroke = _cairo_cogl_context_stroke;
     backend->stroke_preserve = _cairo_cogl_context_stroke_preserve;
     backend->clip = _cairo_cogl_context_clip;
+    backend->clip_preserve = _cairo_cogl_context_clip_preserve;
 }
diff --git a/src/cairo-cogl-gradient.c b/src/cairo-cogl-gradient.c
index 94c9939c1..e401aeea6 100644
--- a/src/cairo-cogl-gradient.c
+++ b/src/cairo-cogl-gradient.c
@@ -37,7 +37,7 @@
 #include <cogl/cogl2-experimental.h>
 #include <glib.h>
 
-#define DUMP_GRADIENTS_TO_PNG
+//#define DUMP_GRADIENTS_TO_PNG
 
 static unsigned long
 _cairo_cogl_linear_gradient_hash (unsigned int                  n_stops,
@@ -568,9 +568,9 @@ _cairo_cogl_get_linear_gradient (cairo_cogl_device_t *device,
 	goto BAIL;
     }
 
-    cogl_texture_set_components (COGL_TEXTURE (tex), components);
+    cogl_texture_set_components (tex, components);
 
-    entry->texture = COGL_TEXTURE (tex);
+    entry->texture = tex;
     entry->compatibility = compatibilities;
 
     un_padded_width = width - left_padding - right_padding;
@@ -583,11 +583,10 @@ _cairo_cogl_get_linear_gradient (cairo_cogl_device_t *device,
 	entry->translate_x += (entry->scale_x / (float)un_padded_width) * (float)left_padding;
 
     offscreen = cogl_offscreen_new_with_texture (tex);
-    cogl_framebuffer_orthographic (COGL_FRAMEBUFFER (offscreen),
-                                   0, 0, width, 1, -1, 100);
-    cogl_framebuffer_clear4f (COGL_FRAMEBUFFER (offscreen),
-			      COGL_BUFFER_BIT_COLOR,
-			      0, 0, 0, 0);
+    cogl_framebuffer_orthographic (offscreen, 0, 0, width, 1, -1, 100);
+    cogl_framebuffer_clear4f (offscreen,
+                              COGL_BUFFER_BIT_COLOR,
+			                  0, 0, 0, 0);
 
     n_quads = n_stops - 1 + !!left_padding + !!right_padding;
     n_vertices = 6 * n_quads;
@@ -609,7 +608,7 @@ _cairo_cogl_get_linear_gradient (cairo_cogl_device_t *device,
                                     n_vertices,
                                     vertices);
     pipeline = cogl_pipeline_new (device->cogl_context);
-    cogl_primitive_draw (prim, COGL_FRAMEBUFFER (offscreen), pipeline);
+    cogl_primitive_draw (prim, offscreen, pipeline);
     cogl_object_unref (prim);
 
     cogl_object_unref (offscreen);
@@ -618,7 +617,7 @@ _cairo_cogl_get_linear_gradient (cairo_cogl_device_t *device,
     gradient->cache_entry.size = _cairo_cogl_linear_gradient_size (gradient);
 
 #ifdef DUMP_GRADIENTS_TO_PNG
-    dump_gradient_to_png (COGL_TEXTURE (tex));
+    dump_gradient_to_png (tex);
 #endif
 
 #warning "FIXME:"
diff --git a/src/cairo-cogl-private.h b/src/cairo-cogl-private.h
index c70bfe0c3..b97a900c2 100644
--- a/src/cairo-cogl-private.h
+++ b/src/cairo-cogl-private.h
@@ -51,18 +51,15 @@ typedef enum _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;
 
+    CoglAttributeBuffer *buffer_stack;
+    size_t buffer_stack_size;
+    size_t buffer_stack_offset;
+    guint8 *buffer_stack_pointer;
+
     /* 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.
@@ -112,11 +109,6 @@ typedef struct _cairo_cogl_surface {
 
     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
diff --git a/src/cairo-cogl-surface.c b/src/cairo-cogl-surface.c
index 6def833af..6e199141b 100644
--- a/src/cairo-cogl-surface.c
+++ b/src/cairo-cogl-surface.c
@@ -236,7 +236,8 @@ _cairo_cogl_surface_ensure_framebuffer (cairo_cogl_surface_t *surface)
     if (surface->framebuffer)
 	return CAIRO_STATUS_SUCCESS;
 
-    surface->framebuffer = COGL_FRAMEBUFFER (cogl_offscreen_new_with_texture (surface->texture));
+    surface->framebuffer =
+        cogl_offscreen_new_with_texture (surface->texture);
     if (!cogl_framebuffer_allocate (surface->framebuffer, &error)) {
 	g_error_free (error);
 	cogl_object_unref (surface->framebuffer);
@@ -261,12 +262,10 @@ _cairo_cogl_surface_create_similar (void            *abstract_surface,
     cairo_cogl_surface_t *surface;
     CoglTexture *texture;
     cairo_status_t status;
-    CoglContext *cogl_context;
 
-    cogl_context = cogl_framebuffer_get_context(reference_surface->framebuffer);
-
-    texture = cogl_texture_2d_new_with_size (cogl_context, width,
-                                             height);
+    texture =
+        cogl_texture_2d_new_with_size (to_device(reference_surface->base.device)->cogl_context,
+                                       width, height);
     if (!texture)
         return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
@@ -452,16 +451,18 @@ static void
 _cairo_cogl_journal_discard (cairo_cogl_surface_t *surface)
 {
     GList *l;
+    cairo_cogl_device_t *dev;
 
     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;
+    dev = to_device(surface->base.device);
+    if (dev->buffer_stack && dev->buffer_stack_offset) {
+	cogl_buffer_unmap (dev->buffer_stack);
+	cogl_object_unref (dev->buffer_stack);
+	dev->buffer_stack = NULL;
     }
 
     for (l = surface->journal->head; l; l = l->next) {
@@ -516,45 +517,42 @@ _cairo_cogl_journal_discard (cairo_cogl_surface_t *surface)
 }
 
 static CoglAttributeBuffer *
-_cairo_cogl_surface_allocate_buffer_space (cairo_cogl_surface_t *surface,
-					   size_t size,
-					   size_t *offset,
-					   void **pointer)
+_cairo_cogl_device_allocate_buffer_space (cairo_cogl_device_t *dev,
+                                          size_t size,
+                                          size_t *offset,
+                                          void **pointer)
 {
-    CoglContext *cogl_context =
-        cogl_framebuffer_get_context(surface->framebuffer);
-
     /* 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 throw 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 (dev->buffer_stack &&
+		  (dev->buffer_stack_size - dev->buffer_stack_offset) < size)) {
+	cogl_buffer_unmap (dev->buffer_stack);
+	cogl_object_unref (dev->buffer_stack);
+	dev->buffer_stack = NULL;
+	dev->buffer_stack_size *= 2;
     }
 
-    if (unlikely (surface->buffer_stack_size < size))
-	surface->buffer_stack_size = size * 2;
+    if (unlikely (dev->buffer_stack_size < size))
+	dev->buffer_stack_size = size * 2;
 
-    if (unlikely (surface->buffer_stack == NULL)) {
-	surface->buffer_stack =
-            cogl_attribute_buffer_new (cogl_context,
-                                       surface->buffer_stack_size,
+    if (unlikely (dev->buffer_stack == NULL)) {
+	dev->buffer_stack =
+            cogl_attribute_buffer_new (dev->cogl_context,
+                                       dev->buffer_stack_size,
                                        NULL);
-	surface->buffer_stack_pointer =
-	    cogl_buffer_map (COGL_BUFFER (surface->buffer_stack),
+	dev->buffer_stack_pointer =
+	    cogl_buffer_map (dev->buffer_stack,
 			     COGL_BUFFER_ACCESS_WRITE,
 			     COGL_BUFFER_MAP_HINT_DISCARD);
-	surface->buffer_stack_offset = 0;
+	dev->buffer_stack_offset = 0;
     }
 
-    *pointer = surface->buffer_stack_pointer + surface->buffer_stack_offset;
-    *offset = surface->buffer_stack_offset;
+    *pointer = dev->buffer_stack_pointer + dev->buffer_stack_offset;
+    *offset = dev->buffer_stack_offset;
 
-    surface->buffer_stack_offset += size;
-    return cogl_object_ref (surface->buffer_stack);
+    dev->buffer_stack_offset += size;
+    return cogl_object_ref (dev->buffer_stack);
 }
 
 
@@ -564,29 +562,27 @@ _cairo_cogl_traps_to_triangles_buffer (cairo_cogl_surface_t *surface,
 				       size_t *offset,
 				       gboolean one_shot)
 {
-    CoglContext *cogl_context =
-        cogl_framebuffer_get_context(surface->framebuffer);
-
     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);
+	buffer =
+            _cairo_cogl_device_allocate_buffer_space (to_device(surface->base.device),
+                                                       n_traps * sizeof (CoglVertexP2) * 6,
+                                                       offset,
+                                                       (void **)&triangles);
 	if (!buffer)
 	    return NULL;
     } else {
 	buffer =
-            cogl_attribute_buffer_new (cogl_context,
+            cogl_attribute_buffer_new (to_device(surface->base.device)->cogl_context,
                                        n_traps * sizeof (CoglVertexP2) * 6,
                                        NULL);
 	if (!buffer)
 	    return NULL;
-	triangles = cogl_buffer_map (COGL_BUFFER (buffer),
+	triangles = cogl_buffer_map (buffer,
 				     COGL_BUFFER_ACCESS_WRITE,
 				     COGL_BUFFER_MAP_HINT_DISCARD);
 	if (!triangles)
@@ -626,7 +622,7 @@ _cairo_cogl_traps_to_triangles_buffer (cairo_cogl_surface_t *surface,
     }
 
     if (!one_shot)
-	cogl_buffer_unmap (COGL_BUFFER (buffer));
+	cogl_buffer_unmap (buffer);
 
     return buffer;
 }
@@ -780,16 +776,23 @@ static void
 _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface)
 {
     GList *l;
+    cairo_cogl_device_t *dev;
     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;
+    dev = to_device(surface->base.device);
+    if (dev->buffer_stack && dev->buffer_stack_offset) {
+	cogl_buffer_unmap (dev->buffer_stack);
+	cogl_object_unref (dev->buffer_stack);
+	dev->buffer_stack = NULL;
+    }
+
+    if (_cairo_cogl_surface_ensure_framebuffer (surface)) {
+        g_warning ("Could not get framebuffer for flushing journal\n");
+        assert (0);
     }
 
     cogl_framebuffer_push_matrix (surface->framebuffer);
@@ -1373,8 +1376,9 @@ _cairo_cogl_get_pot_texture (CoglContext *context,
     int height = cogl_texture_get_height (texture);
     int pot_width;
     int pot_height;
-    CoglHandle offscreen = NULL;
+    CoglOffscreen *offscreen = NULL;
     CoglTexture2D *pot = NULL;
+    CoglPipeline *pipeline;
     GError *error;
 
     pot_width = _cairo_cogl_util_next_p2 (width);
@@ -1399,18 +1403,18 @@ _cairo_cogl_get_pot_texture (CoglContext *context,
 	    break;
     }
 
-    *pot_texture = COGL_TEXTURE (pot);
+    *pot_texture = pot;
 
     if (!pot)
 	return CAIRO_INT_STATUS_NO_MEMORY;
 
-    cogl_texture_set_components (COGL_TEXTURE (tex),
+    cogl_texture_set_components (pot,
                                  cogl_texture_get_components(texture));
 
     /* Use the GPU to do a bilinear filtered scale from npot to pot... */
-    offscreen = cogl_offscreen_new_with_texture (COGL_TEXTURE (pot));
+    offscreen = cogl_offscreen_new_with_texture (pot);
     error = NULL;
-    if (!cogl_framebuffer_allocate (COGL_FRAMEBUFFER (offscreen), &error)) {
+    if (!cogl_framebuffer_allocate (offscreen, &error)) {
 	/* NB: if we don't pass an error then Cogl is allowed to simply abort
 	 * automatically. */
 	g_error_free (error);
@@ -1419,10 +1423,11 @@ _cairo_cogl_get_pot_texture (CoglContext *context,
 	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 ();
+    pipeline = cogl_pipeline_new (context);
+    cogl_pipeline_set_layer_texture (pipeline, 1, texture);
+    cogl_framebuffer_draw_textured_rectangle (offscreen, pipeline,
+                                              -1, 1, 1, -1,
+                                              0, 0, 1, 1);
 
     cogl_object_unref (offscreen);
 }
@@ -1432,7 +1437,7 @@ _cairo_cogl_get_pot_texture (CoglContext *context,
  * be unrefed */
 static CoglTexture *
 _cairo_cogl_acquire_surface_texture (cairo_cogl_surface_t  *reference_surface,
-				     cairo_surface_t	   *abstract_surface)
+				     cairo_surface_t	   *surface)
 {
     cairo_image_surface_t *image;
     cairo_image_surface_t *acquired_image = NULL;
@@ -1443,27 +1448,25 @@ _cairo_cogl_acquire_surface_texture (cairo_cogl_surface_t  *reference_surface,
     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, 0);
-	return surface->texture ? cogl_object_ref (surface->texture) : NULL;
+    if (surface->device == reference_surface->base.device) {
+        _cairo_cogl_surface_flush ((cairo_cogl_surface_t *)surface, 0);
+        if (((cairo_cogl_surface_t *)surface)->texture)
+            return cogl_object_ref (((cairo_cogl_surface_t *)surface)->texture);
     }
 
-    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;
+    if (surface->type == CAIRO_SURFACE_TYPE_COGL) {
+	if (_cairo_surface_is_subsurface (surface)) {
+            surface = _cairo_surface_subsurface_get_target (surface);
+            if (surface->device == reference_surface->base.device)
+                if (((cairo_cogl_surface_t *)surface)->texture)
+                    return cogl_object_ref (((cairo_cogl_surface_t *)surface)->texture);
 	}
     }
 
-    clone = _cairo_surface_has_snapshot (abstract_surface, &_cairo_cogl_surface_backend);
+    clone = _cairo_surface_has_snapshot (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;
+        if (((cairo_cogl_surface_t *)clone)->texture)
+            return cogl_object_ref (((cairo_cogl_surface_t *)clone)->texture);
     }
 
     if (_cairo_surface_is_recording (surface)) {
@@ -1490,16 +1493,18 @@ _cairo_cogl_acquire_surface_texture (cairo_cogl_surface_t  *reference_surface,
         }
 
         cairo_surface_destroy (clone);
-        return ((cairo_cogl_surface_t *)clone)->texture;
+        return texture;
     }
 
     // g_warning ("Uploading image surface to texture");
 
-    if (_cairo_surface_is_image (abstract_surface)) {
-	image = (cairo_image_surface_t *)abstract_surface;
+    if (_cairo_surface_is_image (surface)) {
+	image = (cairo_image_surface_t *)surface;
     } else {
-	cairo_status_t status = _cairo_surface_acquire_source_image (abstract_surface,
-								     &acquired_image, &image_extra);
+        cairo_status_t status =
+            _cairo_surface_acquire_source_image (surface,
+                                                 &acquired_image,
+                                                 &image_extra);
 	if (unlikely (status)) {
 	    g_warning ("acquire_source_image failed: %s [%d]\n",
 		       cairo_status_to_string (status), status);
@@ -1537,9 +1542,9 @@ _cairo_cogl_acquire_surface_texture (cairo_cogl_surface_t  *reference_surface,
 
     clone = _cairo_cogl_surface_create_full (to_device(reference_surface->base.device),
 					     reference_surface->ignore_alpha,
-					     NULL, COGL_TEXTURE (texture));
+					     NULL, texture);
 
-    _cairo_surface_attach_snapshot (abstract_surface, clone, NULL);
+    _cairo_surface_attach_snapshot (surface, clone, NULL);
 
     /* Attaching the snapshot will take a reference on the clone surface... */
     cairo_surface_destroy (clone);
@@ -1548,9 +1553,9 @@ BAIL:
     if (image_clone)
 	cairo_surface_destroy (&image_clone->base);
     if (acquired_image)
-	_cairo_surface_release_source_image (abstract_surface, acquired_image, image_extra);
+	_cairo_surface_release_source_image (surface, acquired_image, image_extra);
 
-    return COGL_TEXTURE (texture);
+    return texture;
 }
 
 /* NB: a reference for the texture is transferred to the caller which should
@@ -1847,7 +1852,7 @@ _cairo_cogl_rectangle_new_p2t2t2 (CoglContext *cogl_context,
 						     COGL_ATTRIBUTE_TYPE_FLOAT);
     CoglPrimitive *prim;
 
-    cogl_buffer_set_data (COGL_BUFFER (buffer), 0, vertices, sizeof (vertices));
+    cogl_buffer_set_data (buffer, 0, vertices, sizeof (vertices));
 
     /* The attributes will now keep the buffer alive... */
     cogl_object_unref (buffer);
@@ -2669,13 +2674,13 @@ _cairo_cogl_surface_create_full (cairo_cogl_device_t *dev,
 
     surface->journal = NULL;
 
-    surface->buffer_stack = NULL;
-    surface->buffer_stack_size = 4096;
-
     surface->last_clip = NULL;
 
     surface->n_clip_updates_per_frame = 0;
 
+    surface->path_is_rectangle = FALSE;
+    surface->user_path = NULL;
+
     _cairo_surface_init (&surface->base,
                          &_cairo_cogl_surface_backend,
                          &dev->base,
@@ -2870,8 +2875,6 @@ 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_2d_new_with_size (cogl_context,
@@ -2879,6 +2882,9 @@ cairo_cogl_device_create (CoglContext *cogl_context)
     if (!dev->dummy_texture)
 	goto ERROR;
 
+    dev->buffer_stack = NULL;
+    dev->buffer_stack_size = 4096;
+
     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);
commit a3233bc5df49a8fee4ba07ad56b9cc67e8bfc744
Author: George Matsumura <gmmatsumura01 at bvsd.org>
Date:   Sun Jun 28 20:00:47 2020 -0600

    cogl: Handle recording surface sources properly
    
    Previously, if an unbounded recording surface was given as the
    source, the program triggered an assertion failure in the recording
    surface code, as acquire_source_image cannot be used on the surface
    in this case. This adds a new path to deal with such an unbounded
    source surface.
    
    Signed-off-by: George Matsumura <gmmatsumura01 at bvsd.org>

diff --git a/src/cairo-cogl-surface.c b/src/cairo-cogl-surface.c
index 591cefa46..6def833af 100644
--- a/src/cairo-cogl-surface.c
+++ b/src/cairo-cogl-surface.c
@@ -34,6 +34,7 @@
 #include "cairo-error-private.h"
 #include "cairo-path-fixed-private.h"
 #include "cairo-recording-surface-private.h"
+#include "cairo-recording-surface-inline.h"
 #include "cairo-surface-clipper-private.h"
 #include "cairo-fixed-private.h"
 #include "cairo-device-private.h"
@@ -1287,6 +1288,10 @@ _cairo_cogl_surface_paint (void                  *abstract_surface,
     cairo_matrix_t identity;
 
     if (clip == NULL) {
+        status = _cairo_cogl_surface_ensure_framebuffer (abstract_surface);
+        if (unlikely (status))
+            goto BAIL;
+
 	if (op == CAIRO_OPERATOR_CLEAR)
             return _cairo_cogl_surface_clear (abstract_surface, CAIRO_COLOR_TRANSPARENT);
 	else if (source->type == CAIRO_PATTERN_TYPE_SOLID &&
@@ -1349,8 +1354,7 @@ get_cogl_wrap_mode_for_extend (cairo_extend_t extend_mode)
     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;
+	return COGL_PIPELINE_WRAP_MODE_MIRRORED_REPEAT;
     }
     assert (0); /* not reached */
     return COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE;
@@ -1462,6 +1466,33 @@ _cairo_cogl_acquire_surface_texture (cairo_cogl_surface_t  *reference_surface,
 	return surface->texture ? cogl_object_ref (surface->texture) : NULL;
     }
 
+    if (_cairo_surface_is_recording (surface)) {
+        texture =
+            cogl_texture_2d_new_with_size (to_device(reference_surface->base.device)->cogl_context,
+                                           reference_surface->width,
+                                           reference_surface->height);
+        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,
+                                             texture);
+
+        if (_cairo_recording_surface_replay (surface, clone)) {
+            g_warning ("could not replay recording surface \n");
+            texture = NULL;
+            goto BAIL;
+        }
+
+        cairo_surface_destroy (clone);
+        return ((cairo_cogl_surface_t *)clone)->texture;
+    }
+
     // g_warning ("Uploading image surface to texture");
 
     if (_cairo_surface_is_image (abstract_surface)) {
commit 97c766f8fe6a547b2f1389c59e8a6260c1531539
Author: George Matsumura <gmmatsumura01 at bvsd.org>
Date:   Wed Jun 24 05:17:04 2020 -0600

    cogl: Convert int to fixed-point in rectangle painting fallback
    
    Previously, the rectangle fallback path in surface_paint was
    incorrectly passing and int to a function that required a
    fixed-point number.
    
    Signed-off-by: George Matsumura <gmmatsumura01 at bvsd.org>

diff --git a/src/cairo-cogl-surface.c b/src/cairo-cogl-surface.c
index e9a1b0f77..591cefa46 100644
--- a/src/cairo-cogl-surface.c
+++ b/src/cairo-cogl-surface.c
@@ -64,6 +64,10 @@
 #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); \
@@ -835,7 +839,7 @@ _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface)
 							path->fill_rule,
 							path->tolerance,
 							0,
-							TRUE,
+							FALSE,
 							&prim,
 							&prim_size);
 		if (unlikely (status)) {
@@ -1299,10 +1303,14 @@ _cairo_cogl_surface_paint (void                  *abstract_surface,
 
     _cairo_path_fixed_init (&path);
 
-    status = _cairo_cogl_path_fixed_rectangle (&path, 0, 0, surface->width, surface->height);
+    status =
+        _cairo_cogl_path_fixed_rectangle (&path, 0, 0,
+                                          _cairo_fixed_from_int (surface->width),
+                                          _cairo_fixed_from_int (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;
@@ -1314,6 +1322,7 @@ _cairo_cogl_surface_paint (void                  *abstract_surface,
     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,
@@ -2557,7 +2566,11 @@ _cairo_cogl_surface_show_glyphs (void			*surface,
 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 */
commit ceffa65fb0411b569690f56788285970ae275c25
Author: George Matsumura <gmmatsumura01 at bvsd.org>
Date:   Sun Jun 21 03:32:44 2020 -0600

    cogl: Account for new representations of framebuffer types
    
    With the new cogl API, casting the framebuffer into offscreen
    and onscreen types with the provided macros is a deprecated
    behavior.
    
    Signed-off-by: George Matsumura <gmmatsumura01 at bvsd.org>

diff --git a/boilerplate/cairo-boilerplate-cogl.c b/boilerplate/cairo-boilerplate-cogl.c
index 540df19d2..e11a8b8de 100644
--- a/boilerplate/cairo-boilerplate-cogl.c
+++ b/boilerplate/cairo-boilerplate-cogl.c
@@ -38,8 +38,6 @@
 typedef struct _cogl_closure {
     cairo_device_t *device;
     CoglFramebuffer *fb;
-    CoglOnscreen *onscreen;
-    CoglOffscreen *offscreen;
     cairo_surface_t *surface;
 } cogl_closure_t;
 
@@ -96,7 +94,6 @@ _cairo_boilerplate_cogl_create_offscreen_color_surface (const char		*name,
     *abstract_closure = closure;
     closure->device = device;
     closure->fb = fb;
-    closure->offscreen = offscreen;
     closure->surface = cairo_cogl_surface_create (device, fb);
 
     status = cairo_surface_set_user_data (closure->surface,
@@ -142,7 +139,6 @@ _cairo_boilerplate_cogl_create_onscreen_color_surface (const char	       *name,
     *abstract_closure = closure;
     closure->device = device;
     closure->fb = fb;
-    closure->onscreen = onscreen;
     closure->surface = cairo_cogl_surface_create (device, fb);
 
     status = cairo_surface_set_user_data (closure->surface,
@@ -155,35 +151,14 @@ _cairo_boilerplate_cogl_create_onscreen_color_surface (const char	       *name,
 }
 
 static cairo_status_t
-_cairo_boilerplate_cogl_finish_onscreen (cairo_surface_t *surface)
+_cairo_boilerplate_cogl_finish (cairo_surface_t *surface)
 {	
     cogl_closure_t *closure = cairo_surface_get_user_data (surface, &cogl_closure_key);
 
-    if (!closure->onscreen) {
-        fprintf(stderr, "Attempted to close an offscreen surface "
-                        "using onscreen closure function\n");
-        return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
-    }
-
     cairo_cogl_surface_end_frame (surface);
 
-    cogl_onscreen_swap_buffers (closure->onscreen);
-
-    return CAIRO_STATUS_SUCCESS;
-}
-
-static cairo_status_t
-_cairo_boilerplate_cogl_finish_offscreen (cairo_surface_t *surface)
-{
-    cogl_closure_t *closure = cairo_surface_get_user_data (surface, &cogl_closure_key);
-
-    if (!closure->offscreen) {
-        fprintf(stderr, "Attempted to close an onscreen surface "
-                        "using offscreen closure function\n");
-        return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
-    }
-
-    cairo_cogl_surface_end_frame (surface);
+    if (cogl_is_onscreen (closure->fb))
+        cogl_onscreen_swap_buffers (closure->fb);
 
     return CAIRO_STATUS_SUCCESS;
 }
@@ -203,7 +178,7 @@ static const cairo_boilerplate_target_t targets[] = {
 	_cairo_boilerplate_cogl_create_offscreen_color_surface,
 	cairo_surface_create_similar,
 	NULL,
-        _cairo_boilerplate_cogl_finish_offscreen,
+        _cairo_boilerplate_cogl_finish,
 	_cairo_boilerplate_get_image_surface,
 	cairo_surface_write_to_png,
 	_cairo_boilerplate_cogl_cleanup,
@@ -218,7 +193,7 @@ static const cairo_boilerplate_target_t targets[] = {
 	_cairo_boilerplate_cogl_create_onscreen_color_surface,
 	cairo_surface_create_similar,
 	NULL,
-	_cairo_boilerplate_cogl_finish_onscreen,
+	_cairo_boilerplate_cogl_finish,
 	_cairo_boilerplate_get_image_surface,
 	cairo_surface_write_to_png,
 	_cairo_boilerplate_cogl_cleanup,
diff --git a/src/cairo-cogl.h b/src/cairo-cogl.h
index f270d74d3..a7e5d4690 100644
--- a/src/cairo-cogl.h
+++ b/src/cairo-cogl.h
@@ -33,8 +33,8 @@
  *      Robert Bragg <robert at linux.intel.com>
  */
 
-#ifndef CAIRO_VG_H
-#define CAIRO_VG_H
+#ifndef CAIRO_COGL_H
+#define CAIRO_COGL_H
 
 #include "cairo.h"
 
commit 1ada7c9aa6adc70dc58b6678df5d95268f9ecc29
Author: George Matsumura <gmmatsumura01 at bvsd.org>
Date:   Sat Jun 20 19:10:29 2020 -0600

    cogl: Fix push_group and pop_group context functions
    
    Before this change, there was the possibility of context functions
    only meant for the cogl surface backend being used on a non-cogl
    surface.
    
    Signed-off-by: George Matsumura <gmmatsumura01 at bvsd.org>

diff --git a/src/cairo-cogl-context.c b/src/cairo-cogl-context.c
index e590d1eff..34aa32ed7 100644
--- a/src/cairo-cogl-context.c
+++ b/src/cairo-cogl-context.c
@@ -350,6 +350,7 @@ _cairo_cogl_context_curve_to (void *abstract_cr,
 				       x3_fixed, y3_fixed);
 }
 
+#if 0
 static cairo_status_t
 _cairo_cogl_context_arc (void *abstract_cr,
 			  double xc, double yc, double radius,
@@ -403,6 +404,7 @@ _cairo_cogl_context_arc (void *abstract_cr,
 
     return CAIRO_STATUS_SUCCESS; /* any error will have already been set on cr */
 }
+#endif
 
 static cairo_status_t
 _cairo_cogl_context_rel_move_to (void *abstract_cr, double dx, double dy)
@@ -740,6 +742,61 @@ _cairo_cogl_context_destroy (void *abstract_cr)
     _freed_pool_put (&context_pool, cr);
 }
 
+static cairo_pattern_t *
+_cairo_cogl_context_pop_group (void *abstract_cr);
+
+void
+_cairo_cogl_context_set_custom_vtable_funcs (cairo_backend_t *backend);
+
+/* Pushed surfaces often take the form of image or recording surfaces,
+ * which are unable to handle the rectangle fast path or tessellation
+ * cacheing. Therfore, we need to restore the vtable functions from the
+ * default context. */
+static cairo_status_t
+_cairo_cogl_context_push_group (void *abstract_cr, cairo_content_t content)
+{
+    cairo_cogl_context_t *cr = abstract_cr;
+    cairo_status_t status;
+
+    /* Flush the values set in the side band to the normal path */
+    if (cr->path_is_rectangle) {
+        status = _flush_cr_rectangle (cr);
+        if (unlikely (status))
+            return status;
+    }
+
+    /* Just assume the ctm will be modified */
+    cr->path_ctm_age++;
+
+    status = cr->dev->backend_parent.push_group (abstract_cr, content);
+    if (unlikely (status))
+        return status;
+
+    /* Restore all the vtable functions except the popping function */
+    memcpy (&cr->dev->backend, &cr->dev->backend_parent, sizeof (cairo_backend_t));
+    cr->dev->backend.pop_group = _cairo_cogl_context_pop_group;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_pattern_t *
+_cairo_cogl_context_pop_group (void *abstract_cr)
+{
+    cairo_cogl_context_t *cr = abstract_cr;
+    cairo_pattern_t *group_pattern;
+
+    /* The default popping function can still be found at
+     * backend_parent */
+    group_pattern = cr->dev->backend_parent.pop_group (abstract_cr);
+
+    /* Skip restoring the vtable funcs if we are popping to a pushed
+     * surface */
+    if (cr->base.gstate->target->type == CAIRO_SURFACE_TYPE_COGL)
+        _cairo_cogl_context_set_custom_vtable_funcs (&cr->dev->backend);
+
+    return group_pattern;
+}
+
 /* 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
@@ -769,46 +826,10 @@ _cairo_cogl_context_create (void *target)
     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, 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->backend_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;
+        _cairo_cogl_context_set_custom_vtable_funcs (&cr->dev->backend);
 
 	cr->dev->backend_vtable_initialized = TRUE;
     }
@@ -820,3 +841,49 @@ _cairo_cogl_context_create (void *target)
 
     return &cr->base.base;
 }
+
+void
+_cairo_cogl_context_set_custom_vtable_funcs (cairo_backend_t *backend)
+{
+    backend->destroy = _cairo_cogl_context_destroy;
+
+    backend->restore = _cairo_cogl_context_restore;
+
+    backend->push_group = _cairo_cogl_context_push_group;
+    backend->pop_group = _cairo_cogl_context_pop_group;
+
+    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->backend_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;
+}
diff --git a/src/cairo-cogl-surface.c b/src/cairo-cogl-surface.c
index ef470175a..e9a1b0f77 100644
--- a/src/cairo-cogl-surface.c
+++ b/src/cairo-cogl-surface.c
@@ -807,6 +807,9 @@ _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface)
 		cogl_framebuffer_pop_clip (surface->framebuffer);
 	    clip_stack_depth = 0;
 
+            if (clip_entry->clip == NULL)
+                continue; // there is no clip
+
 	    for (path = clip_entry->clip->path, i = 0; path; path = path->prev, i++) {
 		cairo_rectangle_int_t extents;
 		cairo_int_status_t status;
@@ -2620,8 +2623,6 @@ _cairo_cogl_surface_create_full (cairo_cogl_device_t *dev,
 	cogl_object_ref (texture);
     }
 
-    assert(surface->width && surface->height);
-
     surface->journal = NULL;
 
     surface->buffer_stack = NULL;
commit d1dec1b3842800b336f4bda27e7fc9943ed27c16
Author: George Matsumura <gmmatsumura01 at bvsd.org>
Date:   Thu Jun 18 23:22:56 2020 -0600

    cogl: Fix passing wrong type to _cairo_cogl_clip_push_box
    
    Signed-off-by: George Matsumura <gmmatsumura01 at bvsd.org>

diff --git a/src/cairo-cogl-surface.c b/src/cairo-cogl-surface.c
index 03fddbe10..ef470175a 100644
--- a/src/cairo-cogl-surface.c
+++ b/src/cairo-cogl-surface.c
@@ -850,7 +850,7 @@ _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface)
 	    for (i = 0; i < clip_entry->clip->num_boxes; i++) {
 		clip_stack_depth++;
 		_cairo_cogl_clip_push_box (&clip_entry->clip->boxes[i],
-                                           &surface->framebuffer);
+                                           surface->framebuffer);
 	    }
 
 	    surface->n_clip_updates_per_frame++;
@@ -1861,6 +1861,7 @@ is_operator_supported (cairo_operator_t op)
 	return TRUE;
 
     default:
+        g_warning("cairo-cogl: Blend operator not supported\n");
 	return FALSE;
     }
 }
commit bda0f90d6e1368e2414235ef84da2dc5390d89cd
Author: George Matsumura <gmmatsumura01 at bvsd.org>
Date:   Thu Jun 18 18:13:37 2020 -0600

    cogl: Correct behavior of boilerplate surface finishing functions
    
    Without this, a buffer-swapping function only usable on onscreen
    framebuffers would have been used on offscreen framebuffers.
    
    Signed-off-by: George Matsumura <gmmatsumura01 at bvsd.org>

diff --git a/boilerplate/cairo-boilerplate-cogl.c b/boilerplate/cairo-boilerplate-cogl.c
index 77bdb7972..540df19d2 100644
--- a/boilerplate/cairo-boilerplate-cogl.c
+++ b/boilerplate/cairo-boilerplate-cogl.c
@@ -159,11 +159,11 @@ _cairo_boilerplate_cogl_finish_onscreen (cairo_surface_t *surface)
 {	
     cogl_closure_t *closure = cairo_surface_get_user_data (surface, &cogl_closure_key);
 
-	if (!closure->onscreen) {
-            fprintf(stderr, "Attempted to close an offscreen surface "
-                "using onscreen closure function\n");
-            return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
-	}
+    if (!closure->onscreen) {
+        fprintf(stderr, "Attempted to close an offscreen surface "
+                        "using onscreen closure function\n");
+        return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
+    }
 
     cairo_cogl_surface_end_frame (surface);
 
@@ -172,6 +172,22 @@ _cairo_boilerplate_cogl_finish_onscreen (cairo_surface_t *surface)
     return CAIRO_STATUS_SUCCESS;
 }
 
+static cairo_status_t
+_cairo_boilerplate_cogl_finish_offscreen (cairo_surface_t *surface)
+{
+    cogl_closure_t *closure = cairo_surface_get_user_data (surface, &cogl_closure_key);
+
+    if (!closure->offscreen) {
+        fprintf(stderr, "Attempted to close an onscreen surface "
+                        "using offscreen closure function\n");
+        return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
+    }
+
+    cairo_cogl_surface_end_frame (surface);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
 static void
 _cairo_boilerplate_cogl_synchronize (void *abstract_closure)
 {
@@ -186,7 +202,8 @@ static const cairo_boilerplate_target_t targets[] = {
 	"cairo_cogl_device_create",
 	_cairo_boilerplate_cogl_create_offscreen_color_surface,
 	cairo_surface_create_similar,
-	NULL, NULL,
+	NULL,
+        _cairo_boilerplate_cogl_finish_offscreen,
 	_cairo_boilerplate_get_image_surface,
 	cairo_surface_write_to_png,
 	_cairo_boilerplate_cogl_cleanup,
diff --git a/src/cairo-cogl-surface.c b/src/cairo-cogl-surface.c
index b1d22d737..03fddbe10 100644
--- a/src/cairo-cogl-surface.c
+++ b/src/cairo-cogl-surface.c
@@ -993,7 +993,11 @@ get_default_cogl_format_from_components (CoglTextureComponents components)
     case COGL_TEXTURE_COMPONENTS_RGB:
         return COGL_PIXEL_FORMAT_RGB_888;
     case COGL_TEXTURE_COMPONENTS_RGBA:
-        return COGL_PIXEL_FORMAT_RGBA_8888_PRE;
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+	return COGL_PIXEL_FORMAT_BGRA_8888_PRE;
+#else
+	return COGL_PIXEL_FORMAT_ARGB_8888_PRE;
+#endif
     case COGL_TEXTURE_COMPONENTS_DEPTH:
         return COGL_PIXEL_FORMAT_DEPTH_32;
     default:
commit 78b00a32132a113cb8d72c4b5d2d4f9f935867d4
Author: George Matsumura <gmmatsumura01 at bvsd.org>
Date:   Thu Jun 18 00:21:36 2020 -0600

    cogl: Handle framebuffer formats better in case contents are unknown
    
    Signed-off-by: George Matsumura <gmmatsumura01 at bvsd.org>

diff --git a/src/cairo-cogl-surface.c b/src/cairo-cogl-surface.c
index 644f540f6..b1d22d737 100644
--- a/src/cairo-cogl-surface.c
+++ b/src/cairo-cogl-surface.c
@@ -1120,14 +1120,15 @@ _cairo_cogl_surface_read_rect_to_image_surface (cairo_cogl_surface_t   *surface,
         cogl_format =
             get_default_cogl_format_from_components (
                 cogl_texture_get_components (surface->texture) );
+        cairo_format = get_cairo_format_from_cogl_format (cogl_format);
     } else {
         /* Cogl doesn't give internal formats of framebuffers anymore,
          * nor does it provide a way to find which color components are
          * present, making it so that we may lose data if we don't get
          * all 4 possible components */
-        cogl_format = COGL_PIXEL_FORMAT_RGBA_8888_PRE;
+        cairo_format = CAIRO_FORMAT_ARGB32;
+        cogl_format = get_cogl_format_from_cairo_format (cairo_format);
     }
-    cairo_format = get_cairo_format_from_cogl_format (cogl_format);
 
     image = (cairo_image_surface_t *)
         cairo_image_surface_create (cairo_format,
@@ -1445,7 +1446,7 @@ _cairo_cogl_acquire_surface_texture (cairo_cogl_surface_t  *reference_surface,
 	return surface->texture ? cogl_object_ref (surface->texture) : NULL;
     }
 
-    g_warning ("Uploading image surface to texture");
+    // g_warning ("Uploading image surface to texture");
 
     if (_cairo_surface_is_image (abstract_surface)) {
 	image = (cairo_image_surface_t *)abstract_surface;
commit fd6de9820e50b21c0aca15e71b5bbadca9e26358
Author: George Matsumura <gmmatsumura01 at bvsd.org>
Date:   Wed Jun 17 13:28:47 2020 -0600

    cogl: Accommodate new context functions
    
    In order to satisfy the runtime check that accounts for new
    API functions that modify the transformation matrix, the new
    functions for translation from backend to user coordinates must
    be accounted for.
    
    Signed-off-by: George Matsumura <gmmatsumura01 at bvsd.org>

diff --git a/src/cairo-cogl-context.c b/src/cairo-cogl-context.c
index c32278057..e590d1eff 100644
--- a/src/cairo-cogl-context.c
+++ b/src/cairo-cogl-context.c
@@ -801,7 +801,7 @@ _cairo_cogl_context_create (void *target)
 
 	/* 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)
+	assert (((char *)&backend->path_extents - (char *)&backend->backend_to_user_distance)
 		== (sizeof (void *) * 14));
 
 	backend->fill = _cairo_cogl_context_fill;
commit 09092bf884bdb6ff25173ca91b2579620fd58b09
Author: George Matsumura <gmmatsumura01 at bvsd.org>
Date:   Wed Jun 17 04:20:10 2020 -0600

    cogl: Add dependency on cogl-path
    
    The path functions in the cogl API have now been split into a
    separate one which builds from the same codebase. In order to
    compile with this change, the new library has to be added as
    an explicit dependency.
    
    Signed-off-by: George Matsumura <gmmatsumura01 at bvsd.org>

diff --git a/configure.ac b/configure.ac
index 8b2ab18ec..d5fb4ceda 100644
--- a/configure.ac
+++ b/configure.ac
@@ -400,7 +400,7 @@ CAIRO_ENABLE_SURFACE_BACKEND(glesv3, OpenGLESv3, no, [
 
 dnl ===========================================================================
 CAIRO_ENABLE_SURFACE_BACKEND(cogl, Cogl, no, [
-  cogl_REQUIRES="cogl-2.0-experimental"
+  cogl_REQUIRES="cogl-2.0-experimental, cogl-path-2.0-experimental"
   PKG_CHECK_MODULES(cogl, $cogl_REQUIRES,, [use_cogl="no"])
 ])
 
commit 6250e1a8433cca797fb3ca106495f5d779882015
Author: George Matsumura <gmmatsumura01 at bvsd.org>
Date:   Wed Jun 17 03:47:02 2020 -0600

    cogl: Use new separate functions for offscreen and onscreen framebuffers
    
    The old boilerplate code no longer worked due to the deprecation of
    functions for swapping the buffers that it used.
    
    Signed-off-by: George Matsumura <gmmatsumura01 at bvsd.org>

diff --git a/boilerplate/cairo-boilerplate-cogl.c b/boilerplate/cairo-boilerplate-cogl.c
index e39ad333d..77bdb7972 100644
--- a/boilerplate/cairo-boilerplate-cogl.c
+++ b/boilerplate/cairo-boilerplate-cogl.c
@@ -38,6 +38,8 @@
 typedef struct _cogl_closure {
     cairo_device_t *device;
     CoglFramebuffer *fb;
+    CoglOnscreen *onscreen;
+    CoglOffscreen *offscreen;
     cairo_surface_t *surface;
 } cogl_closure_t;
 
@@ -79,23 +81,22 @@ _cairo_boilerplate_cogl_create_offscreen_color_surface (const char		*name,
 	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);
+    tex = cogl_texture_2d_new_with_size (context, width, height);
+    cogl_texture_set_components (tex, COGL_TEXTURE_COMPONENTS_RGBA);
+    offscreen = cogl_offscreen_new_with_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 ();
+    cogl_framebuffer_orthographic (fb, 0, 0,
+                                   cogl_framebuffer_get_width (fb),
+                                   cogl_framebuffer_get_height (fb),
+                                   -1, 100);
 
     closure = malloc (sizeof (cogl_closure_t));
     *abstract_closure = closure;
     closure->device = device;
     closure->fb = fb;
+    closure->offscreen = offscreen;
     closure->surface = cairo_cogl_surface_create (device, fb);
 
     status = cairo_surface_set_user_data (closure->surface,
@@ -132,16 +133,16 @@ _cairo_boilerplate_cogl_create_onscreen_color_surface (const char	       *name,
 
     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 ();
+    cogl_framebuffer_orthographic (fb, 0, 0,
+                                   cogl_framebuffer_get_width (fb),
+                                   cogl_framebuffer_get_height (fb),
+                                   -1, 100);
 
     closure = malloc (sizeof (cogl_closure_t));
     *abstract_closure = closure;
     closure->device = device;
     closure->fb = fb;
+    closure->onscreen = onscreen;
     closure->surface = cairo_cogl_surface_create (device, fb);
 
     status = cairo_surface_set_user_data (closure->surface,
@@ -155,12 +156,18 @@ _cairo_boilerplate_cogl_create_onscreen_color_surface (const char	       *name,
 
 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);
 
+	if (!closure->onscreen) {
+            fprintf(stderr, "Attempted to close an offscreen surface "
+                "using onscreen closure function\n");
+            return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
+	}
+
     cairo_cogl_surface_end_frame (surface);
 
-    cogl_framebuffer_swap_buffers (closure->fb);
+    cogl_onscreen_swap_buffers (closure->onscreen);
 
     return CAIRO_STATUS_SUCCESS;
 }
commit 7135ca2091b0c69b2f9aa2e9d5986d10a86d6103
Author: George Matsumura <gmmatsumura01 at bvsd.org>
Date:   Wed Jun 17 02:37:25 2020 -0600

    cogl: Futher changes to match new cogl-experimental public API
    
    These are more changes required to match the new backwards-incompatible
    API of cogl-experimental.
    
    Signed-off-by: George Matsumura <gmmatsumura01 at bvsd.org>

diff --git a/src/cairo-cogl-gradient.c b/src/cairo-cogl-gradient.c
index 0923e3b6d..94c9939c1 100644
--- a/src/cairo-cogl-gradient.c
+++ b/src/cairo-cogl-gradient.c
@@ -197,24 +197,24 @@ _cairo_cogl_linear_gradient_width_for_stops (cairo_extend_t		  extend,
 
 /* 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)
+static CoglTextureComponents
+_cairo_cogl_linear_gradient_components_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;
+	return COGL_TEXTURE_COMPONENTS_RGBA;
 
     for (n = 1; n < n_stops; n++) {
 	if (stops[n].color.alpha != 1.0)
-	    return COGL_PIXEL_FORMAT_BGRA_8888_PRE;
+	    return COGL_TEXTURE_COMPONENTS_RGBA;
     }
 
-    return COGL_PIXEL_FORMAT_BGR_888;
+    return COGL_TEXTURE_COMPONENTS_RGBA;
 }
 
 static cairo_cogl_gradient_compatibility_t
@@ -370,7 +370,7 @@ _cairo_cogl_get_linear_gradient (cairo_cogl_device_t *device,
     cairo_color_stop_t left_padding_color;
     int right_padding = 0;
     cairo_color_stop_t right_padding_color;
-    CoglPixelFormat format;
+    CoglTextureComponents components;
     CoglTexture2D *tex;
     GError *error = NULL;
     int un_padded_width;
@@ -383,6 +383,7 @@ _cairo_cogl_get_linear_gradient (cairo_cogl_device_t *device,
     CoglVertexP2C4 *vertices;
     CoglVertexP2C4 *p;
     CoglPrimitive *prim;
+    CoglPipeline *pipeline;
 
     hash = _cairo_cogl_linear_gradient_hash (n_stops, stops);
 
@@ -553,14 +554,11 @@ _cairo_cogl_get_linear_gradient (cairo_cogl_device_t *device,
 
     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);
+    components = _cairo_cogl_linear_gradient_components_for_stops (extend_mode, n_stops, stops);
 
     do {
-	tex = cogl_texture_2d_new_with_size (device->cogl_context,
-					     width,
-					     1,
-					     format,
-					     &error);
+	tex = cogl_texture_2d_new_with_size (device->cogl_context, width, 1);
+
 	if (!tex)
 	    g_error_free (error);
     } while (tex == NULL && width >> 1);
@@ -570,6 +568,8 @@ _cairo_cogl_get_linear_gradient (cairo_cogl_device_t *device,
 	goto BAIL;
     }
 
+    cogl_texture_set_components (COGL_TEXTURE (tex), components);
+
     entry->texture = COGL_TEXTURE (tex);
     entry->compatibility = compatibilities;
 
@@ -582,9 +582,9 @@ _cairo_cogl_get_linear_gradient (cairo_cogl_device_t *device,
     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);
+    offscreen = cogl_offscreen_new_with_texture (tex);
+    cogl_framebuffer_orthographic (COGL_FRAMEBUFFER (offscreen),
+                                   0, 0, width, 1, -1, 100);
     cogl_framebuffer_clear4f (COGL_FRAMEBUFFER (offscreen),
 			      COGL_BUFFER_BIT_COLOR,
 			      0, 0, 0, 0);
@@ -604,15 +604,14 @@ _cairo_cogl_get_linear_gradient (cairo_cogl_device_t *device,
     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);
+    prim = cogl_primitive_new_p2c4 (device->cogl_context,
+                                    COGL_VERTICES_MODE_TRIANGLES,
+                                    n_vertices,
+                                    vertices);
+    pipeline = cogl_pipeline_new (device->cogl_context);
+    cogl_primitive_draw (prim, COGL_FRAMEBUFFER (offscreen), pipeline);
     cogl_object_unref (prim);
 
-    cogl_pop_framebuffer ();
     cogl_object_unref (offscreen);
 
     gradient->textures = g_list_prepend (gradient->textures, entry);
diff --git a/src/cairo-cogl-private.h b/src/cairo-cogl-private.h
index 13fe5a8dc..c70bfe0c3 100644
--- a/src/cairo-cogl-private.h
+++ b/src/cairo-cogl-private.h
@@ -94,7 +94,6 @@ typedef struct _cairo_cogl_clip_primitives {
 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:
diff --git a/src/cairo-cogl-surface.c b/src/cairo-cogl-surface.c
index 5675ccab4..644f540f6 100644
--- a/src/cairo-cogl-surface.c
+++ b/src/cairo-cogl-surface.c
@@ -64,10 +64,6 @@
 #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); \
@@ -235,7 +231,7 @@ _cairo_cogl_surface_ensure_framebuffer (cairo_cogl_surface_t *surface)
     if (surface->framebuffer)
 	return CAIRO_STATUS_SUCCESS;
 
-    surface->framebuffer = COGL_FRAMEBUFFER (cogl_offscreen_new_to_texture (surface->texture));
+    surface->framebuffer = COGL_FRAMEBUFFER (cogl_offscreen_new_with_texture (surface->texture));
     if (!cogl_framebuffer_allocate (surface->framebuffer, &error)) {
 	g_error_free (error);
 	cogl_object_unref (surface->framebuffer);
@@ -243,11 +239,9 @@ _cairo_cogl_surface_ensure_framebuffer (cairo_cogl_surface_t *surface)
 	return CAIRO_STATUS_NO_MEMORY;
     }
 
-    cogl_push_framebuffer (surface->framebuffer);
-    cogl_ortho (0, surface->width,
-		surface->height, 0,
-		-1, 100);
-    cogl_pop_framebuffer ();
+    cogl_framebuffer_orthographic (surface-> framebuffer, 0, 0,
+                                   surface->width, surface->height,
+                                   -1, 100);
 
     return CAIRO_STATUS_SUCCESS;
 }
@@ -262,15 +256,20 @@ _cairo_cogl_surface_create_similar (void            *abstract_surface,
     cairo_cogl_surface_t *surface;
     CoglTexture *texture;
     cairo_status_t status;
+    CoglContext *cogl_context;
 
-    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);
+    cogl_context = cogl_framebuffer_get_context(reference_surface->framebuffer);
+
+    texture = cogl_texture_2d_new_with_size (cogl_context, width,
+                                             height);
     if (!texture)
         return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
+    cogl_texture_set_components (texture,
+                                 (content & CAIRO_CONTENT_COLOR) ?
+                                 COGL_TEXTURE_COMPONENTS_RGBA :
+                                 COGL_TEXTURE_COMPONENTS_A);
+
     surface = (cairo_cogl_surface_t *)
 	_cairo_cogl_surface_create_full (to_device(reference_surface->base.device),
 					 (content & CAIRO_CONTENT_ALPHA) == 0,
@@ -788,9 +787,7 @@ _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface)
 	surface->buffer_stack = NULL;
     }
 
-    cogl_set_framebuffer (surface->framebuffer);
-
-    cogl_push_matrix ();
+    cogl_framebuffer_push_matrix (surface->framebuffer);
 
     for (l = surface->journal->head; l; l = l->next) {
 	cairo_cogl_journal_entry_t *entry = l->data;
@@ -888,12 +885,15 @@ _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface)
 		    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 ();
+	    cogl_framebuffer_push_matrix (surface->framebuffer);
+	    cogl_framebuffer_transform (surface->framebuffer, &transform);
+	    cogl_framebuffer_draw_multitextured_rectangle (surface->framebuffer,
+                                                           rect_entry->pipeline,
+                                                           x1, y1,
+                                                           x2, y2,
+						           tex_coords,
+                                                           4 * rect_entry->n_layers);
+	    cogl_framebuffer_pop_matrix (surface->framebuffer);
 	    break;
 	}
 	case CAIRO_COGL_JOURNAL_ENTRY_TYPE_PRIMITIVE: {
@@ -901,7 +901,7 @@ _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface)
 		(cairo_cogl_journal_prim_entry_t *)entry;
 	    CoglMatrix transform;
 
-	    cogl_push_matrix ();
+	    cogl_framebuffer_push_matrix (surface->framebuffer);
 	    if (prim_entry->has_transform) {
 		cairo_matrix_t *ctm = &prim_entry->transform;
 		float ctmfv[16] = {
@@ -911,25 +911,27 @@ _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface)
 		    ctm->x0, ctm->y0, 0, 1
 		};
 		cogl_matrix_init_from_array (&transform, ctmfv);
-		cogl_transform (&transform);
+		cogl_framebuffer_transform (surface->framebuffer, &transform);
 	    } else {
 		cogl_matrix_init_identity (&transform);
-		cogl_set_modelview_matrix (&transform);
+		cogl_framebuffer_set_modelview_matrix (surface->framebuffer, &transform);
 	    }
 
-	    cogl_set_source (prim_entry->pipeline);
 	    cogl_primitive_draw (prim_entry->primitive,
                                  surface->framebuffer,
                                  prim_entry->pipeline);
-	    cogl_pop_matrix ();
+	    cogl_framebuffer_pop_matrix (surface->framebuffer);
 	    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);
+            /* Use this until cogl2_path_fill is updated to take
+             * framebuffer and pipeline arguments */
+            cogl_framebuffer_fill_path (surface->framebuffer,
+                                        path_entry->pipeline,
+                                        path_entry->path);
 	    break;
 	}
 	default:
@@ -937,7 +939,7 @@ _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface)
 	}
     }
 
-    cogl_pop_matrix ();
+    cogl_framebuffer_pop_matrix (surface->framebuffer);
 
     for (i = 0; i < clip_stack_depth; i++)
 	cogl_framebuffer_pop_clip (surface->framebuffer);
@@ -979,6 +981,57 @@ _cairo_cogl_surface_finish (void *abstract_surface)
     return CAIRO_STATUS_SUCCESS;
 }
 
+static CoglPixelFormat
+get_default_cogl_format_from_components (CoglTextureComponents components)
+{
+    switch (components)
+    {
+    case COGL_TEXTURE_COMPONENTS_A:
+        return COGL_PIXEL_FORMAT_A_8;
+    case COGL_TEXTURE_COMPONENTS_RG:
+        return COGL_PIXEL_FORMAT_RG_88;
+    case COGL_TEXTURE_COMPONENTS_RGB:
+        return COGL_PIXEL_FORMAT_RGB_888;
+    case COGL_TEXTURE_COMPONENTS_RGBA:
+        return COGL_PIXEL_FORMAT_RGBA_8888_PRE;
+    case COGL_TEXTURE_COMPONENTS_DEPTH:
+        return COGL_PIXEL_FORMAT_DEPTH_32;
+    default:
+        return 0;
+    }
+}
+
+static CoglTextureComponents
+get_components_from_cogl_format (CoglPixelFormat format)
+{
+    switch ((int)format)
+    {
+    case COGL_PIXEL_FORMAT_BGRA_8888_PRE:
+    case COGL_PIXEL_FORMAT_ABGR_8888_PRE:
+    case COGL_PIXEL_FORMAT_RGBA_8888_PRE:
+        return COGL_TEXTURE_COMPONENTS_RGBA;
+
+    case COGL_PIXEL_FORMAT_RGB_565:
+    case COGL_PIXEL_FORMAT_RGB_888:
+        return COGL_TEXTURE_COMPONENTS_RGB;
+
+    case COGL_PIXEL_FORMAT_A_8:
+        return COGL_TEXTURE_COMPONENTS_A;
+    case COGL_PIXEL_FORMAT_RG_88:
+        return COGL_TEXTURE_COMPONENTS_RG;
+    case COGL_PIXEL_FORMAT_DEPTH_32:
+        return COGL_TEXTURE_COMPONENTS_DEPTH;
+    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);
 
@@ -993,7 +1046,13 @@ get_cairo_format_from_cogl_format (CoglPixelFormat format)
 	return CAIRO_FORMAT_A8;
     case COGL_PIXEL_FORMAT_RGB_565:
 	return CAIRO_FORMAT_RGB16_565;
-
+    case COGL_PIXEL_FORMAT_RG_88:
+        g_warning ("cairo cannot handle red-green textures\n");
+        return CAIRO_FORMAT_INVALID;
+    case COGL_PIXEL_FORMAT_DEPTH_32:
+        g_warning ("cairo cannot handle depth textures\n");
+        return CAIRO_FORMAT_INVALID;
+    
     case COGL_PIXEL_FORMAT_BGRA_8888_PRE:
     case COGL_PIXEL_FORMAT_ARGB_8888_PRE:
     case COGL_PIXEL_FORMAT_RGBA_8888_PRE:
@@ -1031,6 +1090,8 @@ get_cogl_format_from_cairo_format (cairo_format_t cairo_format)
     case CAIRO_FORMAT_INVALID:
     case CAIRO_FORMAT_A1:
     case CAIRO_FORMAT_RGB30:
+    case CAIRO_FORMAT_RGB96F:
+    case CAIRO_FORMAT_RGBA128F:
 	return 0;
     }
 
@@ -1054,26 +1115,30 @@ _cairo_cogl_surface_read_rect_to_image_surface (cairo_cogl_surface_t   *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);
+    if (surface->texture)
+    {
+        cogl_format =
+            get_default_cogl_format_from_components (
+                cogl_texture_get_components (surface->texture) );
     } else {
-	cogl_format = cogl_framebuffer_get_color_format (surface->framebuffer);
+        /* Cogl doesn't give internal formats of framebuffers anymore,
+         * nor does it provide a way to find which color components are
+         * present, making it so that we may lose data if we don't get
+         * all 4 possible components */
+        cogl_format = COGL_PIXEL_FORMAT_RGBA_8888_PRE;
     }
+    cairo_format = get_cairo_format_from_cogl_format (cogl_format);
 
     image = (cairo_image_surface_t *)
-	cairo_image_surface_create (cairo_format, surface->width, surface->height);
+        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 ();
+        return image->base.status;
+
+    cogl_framebuffer_read_pixels (surface->framebuffer, 0, 0,
+                                  surface->width, surface->height,
+                                  cogl_format, image->data);
 
     *image_out = image;
 
@@ -1089,16 +1154,27 @@ _cairo_cogl_surface_acquire_source_image (void		         *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;
+        CoglTextureComponents components =
+            cogl_texture_get_components(surface->texture);
+        CoglPixelFormat cogl_format =
+            get_default_cogl_format_from_components (components);
+        cairo_format_t cairo_format =
+            get_cairo_format_from_cogl_format (cogl_format);
+        if (cairo_format == CAIRO_FORMAT_INVALID) {
+            cairo_format = CAIRO_FORMAT_ARGB32;
+            cogl_format =
+                get_cogl_format_from_cairo_format (cairo_format);
+        }
 
-	cogl_texture_get_data (surface->texture,
-			       cogl_texture_get_format (surface->texture),
-			       0,
-			       image->data);
+        cairo_image_surface_t *image = (cairo_image_surface_t *)
+	        cairo_image_surface_create (cairo_format, surface->width, surface->height);
+        if (image->base.status)
+            return image->base.status;
+
+        cogl_texture_get_data (surface->texture,
+                               cogl_format,
+                               0,
+                               image->data);
 
 	image->base.is_clear = FALSE;
 	*image_out = image;
@@ -1219,7 +1295,6 @@ _cairo_cogl_surface_paint (void                  *abstract_surface,
     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;
@@ -1231,7 +1306,6 @@ _cairo_cogl_surface_paint (void                  *abstract_surface,
     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,
@@ -1289,16 +1363,11 @@ _cairo_cogl_get_pot_texture (CoglContext *context,
 	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);
+					     pot_height);
 	if (pot)
 	    break;
-	else
-	    g_error_free (error);
 
 	if (pot_width > pot_height)
 	    pot_width >>= 1;
@@ -1314,8 +1383,11 @@ _cairo_cogl_get_pot_texture (CoglContext *context,
     if (!pot)
 	return CAIRO_INT_STATUS_NO_MEMORY;
 
+    cogl_texture_set_components (COGL_TEXTURE (tex),
+                                 cogl_texture_get_components(texture));
+
     /* Use the GPU to do a bilinear filtered scale from npot to pot... */
-    offscreen = cogl_offscreen_new_to_texture (COGL_TEXTURE (pot));
+    offscreen = cogl_offscreen_new_with_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
@@ -1405,7 +1477,7 @@ _cairo_cogl_acquire_surface_texture (cairo_cogl_surface_t  *reference_surface,
     texture = cogl_texture_2d_new_from_data (to_device(reference_surface->base.device)->cogl_context,
 					     image->width,
 					     image->height,
-					     format, /* both incoming and desired */
+					     format, /* incoming */
 					     image->stride,
 					     image->data,
 					     &error);
@@ -1695,14 +1767,12 @@ BAIL:
 
 #if 0
 CoglPrimitive *
-_cairo_cogl_rectangle_new_p2t2t2 (float x,
-				  float y,
-				  float width,
-				  float height)
+_cairo_cogl_rectangle_new_p2t2t2 (CoglContext *cogl_context,
+                                  float x,
+                                  float y,
+                                  float width,
+                                  float height)
 {
-    CoglContext *cogl_context =
-        cogl_framebuffer_get_context(surface->framebuffer);
-
     CoglVertexP2 vertices[] = {
 	{x, y}, {x, y + height}, {x + width, y + height},
 	{x, y}, {x + width, y + height}, {x + width, y}
@@ -2478,11 +2548,7 @@ _cairo_cogl_surface_show_glyphs (void			*surface,
 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 */
@@ -2507,7 +2573,7 @@ const cairo_surface_backend_t _cairo_cogl_surface_backend = {
     _cairo_cogl_surface_mask,
     _cairo_cogl_surface_stroke,
     _cairo_cogl_surface_fill,
-    NULL, /* fill_stroke*/
+    NULL, /* fill_stroke */
     _cairo_surface_fallback_glyphs,
 };
 
@@ -2534,7 +2600,6 @@ _cairo_cogl_surface_create_full (cairo_cogl_device_t *dev,
     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);
     }
 
@@ -2545,7 +2610,6 @@ _cairo_cogl_surface_create_full (cairo_cogl_device_t *dev,
 	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);
     }
@@ -2759,9 +2823,8 @@ cairo_cogl_device_create (CoglContext *cogl_context)
 
     dev->cogl_context = cogl_context;
 
-    dev->dummy_texture = cogl_texture_new_with_size (1, 1,
-						     COGL_TEXTURE_NO_SLICING,
-						     COGL_PIXEL_FORMAT_ANY);
+    dev->dummy_texture = cogl_texture_2d_new_with_size (cogl_context,
+                                                        1, 1);
     if (!dev->dummy_texture)
 	goto ERROR;
 
commit 418ad9db62356aa9dbf2e77ea7d5c3be88447f3b
Author: George Matsumura <gmmatsumura01 at bvsd.org>
Date:   Mon Jun 15 07:44:20 2020 +0000

    cogl: Update to match changed cogl-experimental public API
    
    This is the first part of updates to match the backwards-incompatible
    changes that have been made in the cogl-experimental public API
    since this backend last worked.
    
    Signed-off-by: George Matsumura <gmmatsumura01 at bvsd.org>

diff --git a/src/cairo-cogl-surface.c b/src/cairo-cogl-surface.c
index 0c32940e8..5675ccab4 100644
--- a/src/cairo-cogl-surface.c
+++ b/src/cairo-cogl-surface.c
@@ -53,6 +53,7 @@
 #include "cairo-cogl.h"
 
 #include <cogl/cogl2-experimental.h>
+#include <cogl/deprecated/cogl-texture-deprecated.h>
 #include <glib.h>
 
 #define CAIRO_COGL_DEBUG 0
@@ -516,6 +517,9 @@ _cairo_cogl_surface_allocate_buffer_space (cairo_cogl_surface_t *surface,
 					   size_t *offset,
 					   void **pointer)
 {
+    CoglContext *cogl_context =
+        cogl_framebuffer_get_context(surface->framebuffer);
+
     /* 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 throw away our stack
      * buffer each time we flush. */
@@ -531,7 +535,10 @@ _cairo_cogl_surface_allocate_buffer_space (cairo_cogl_surface_t *surface,
 	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 =
+            cogl_attribute_buffer_new (cogl_context,
+                                       surface->buffer_stack_size,
+                                       NULL);
 	surface->buffer_stack_pointer =
 	    cogl_buffer_map (COGL_BUFFER (surface->buffer_stack),
 			     COGL_BUFFER_ACCESS_WRITE,
@@ -553,6 +560,9 @@ _cairo_cogl_traps_to_triangles_buffer (cairo_cogl_surface_t *surface,
 				       size_t *offset,
 				       gboolean one_shot)
 {
+    CoglContext *cogl_context =
+        cogl_framebuffer_get_context(surface->framebuffer);
+
     CoglAttributeBuffer *buffer;
     int n_traps = traps->num_traps;
     int i;
@@ -566,7 +576,10 @@ _cairo_cogl_traps_to_triangles_buffer (cairo_cogl_surface_t *surface,
 	if (!buffer)
 	    return NULL;
     } else {
-	buffer = cogl_attribute_buffer_new (n_traps * sizeof (CoglVertexP2) * 6, NULL);
+	buffer =
+            cogl_attribute_buffer_new (cogl_context,
+                                       n_traps * sizeof (CoglVertexP2) * 6,
+                                       NULL);
 	if (!buffer)
 	    return NULL;
 	triangles = cogl_buffer_map (COGL_BUFFER (buffer),
@@ -744,17 +757,18 @@ BAIL:
 }
 
 static void
-_cairo_cogl_clip_push_box (const cairo_box_t *box)
+_cairo_cogl_clip_push_box (const cairo_box_t *box,
+                           CoglFramebuffer *framebuffer)
 {
     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,
+	cogl_framebuffer_push_scissor_clip (framebuffer, 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);
+	cogl_framebuffer_push_rectangle_clip (framebuffer, x1, y1, x2, y2);
     }
 }
 
@@ -793,7 +807,7 @@ _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface)
 #endif
 
 	    for (i = 0; i < clip_stack_depth; i++)
-		cogl_clip_pop ();
+		cogl_framebuffer_pop_clip (surface->framebuffer);
 	    clip_stack_depth = 0;
 
 	    for (path = clip_entry->clip->path, i = 0; path; path = path->prev, i++) {
@@ -829,16 +843,17 @@ _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface)
 		    continue;
 		}
 		clip_stack_depth++;
-		cogl_clip_push_primitive (prim,
-					  extents.x, extents.y,
-					  extents.x + extents.width,
-					  extents.y + extents.height);
+		cogl_framebuffer_push_primitive_clip (surface->framebuffer, 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]);
+		_cairo_cogl_clip_push_box (&clip_entry->clip->boxes[i],
+                                           &surface->framebuffer);
 	    }
 
 	    surface->n_clip_updates_per_frame++;
@@ -903,7 +918,9 @@ _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface)
 	    }
 
 	    cogl_set_source (prim_entry->pipeline);
-	    cogl_primitive_draw (prim_entry->primitive);
+	    cogl_primitive_draw (prim_entry->primitive,
+                                 surface->framebuffer,
+                                 prim_entry->pipeline);
 	    cogl_pop_matrix ();
 	    break;
 	}
@@ -923,7 +940,7 @@ _cairo_cogl_journal_flush (cairo_cogl_surface_t *surface)
     cogl_pop_matrix ();
 
     for (i = 0; i < clip_stack_depth; i++)
-	cogl_clip_pop ();
+	cogl_framebuffer_pop_clip (surface->framebuffer);
 
     _cairo_cogl_journal_discard (surface);
 }
@@ -1388,8 +1405,7 @@ _cairo_cogl_acquire_surface_texture (cairo_cogl_surface_t  *reference_surface,
     texture = cogl_texture_2d_new_from_data (to_device(reference_surface->base.device)->cogl_context,
 					     image->width,
 					     image->height,
-					     format, /* incoming */
-					     format, /* desired */
+					     format, /* both incoming and desired */
 					     image->stride,
 					     image->data,
 					     &error);
@@ -1684,11 +1700,15 @@ _cairo_cogl_rectangle_new_p2t2t2 (float x,
 				  float width,
 				  float height)
 {
+    CoglContext *cogl_context =
+        cogl_framebuffer_get_context(surface->framebuffer);
+
     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));
+    CoglAttributeBuffer *buffer = cogl_attribute_buffer_new (cogl_context,
+                                                             sizeof (vertices));
     CoglAttribute *pos = cogl_attribute_new (buffer,
 					     "cogl_position_in",
 					     sizeof (CoglVertexP2),
@@ -2698,7 +2718,7 @@ _cairo_cogl_setup_op_state (CoglPipeline *pipeline, cairo_operator_t op)
 static void
 create_templates_for_op (cairo_cogl_device_t *dev, cairo_operator_t op)
 {
-    CoglPipeline *base = cogl_pipeline_new ();
+    CoglPipeline *base = cogl_pipeline_new (dev->cogl_context);
     CoglPipeline *pipeline;
     CoglColor color;
 


More information about the cairo-commit mailing list