[gtk+/wip/ebassi/gsk-renderer: 42/79] gsk: Rework GskRenderer and GskRenderNode semantics



commit 541d99f2721891169075f6271f0a5bc7ed17ea7d
Author: Emmanuele Bassi <ebassi gnome org>
Date:   Wed Jun 22 17:30:36 2016 +0100

    gsk: Rework GskRenderer and GskRenderNode semantics
    
    This commit changes the way GskRenderer and GskRenderNode interact and
    are meant to be used.
    
    GskRenderNode should represent a transient tree of rendering nodes,
    which are submitted to the GskRenderer at render time; this allows the
    renderer to take ownership of the render tree. Once the toolkit and
    application code have finished assembling it, the render tree ownership
    is transferred to the renderer.

 gsk/gskcairorenderer.c     |   51 ++---
 gsk/gskglrenderer.c        |  292 +++++------------------
 gsk/gskrenderer.c          |  575 +++++++++++---------------------------------
 gsk/gskrenderer.h          |   31 +--
 gsk/gskrendererprivate.h   |   23 +--
 gsk/gskrendernode.c        |  338 ++++++++-------------------
 gsk/gskrendernode.h        |    9 +-
 gsk/gskrendernodeprivate.h |   46 +----
 tests/Makefile.am          |    4 -
 tests/testgskrenderer.c    |  229 ------------------
 10 files changed, 338 insertions(+), 1260 deletions(-)
---
diff --git a/gsk/gskcairorenderer.c b/gsk/gskcairorenderer.c
index 62b7a47..0d3591c 100644
--- a/gsk/gskcairorenderer.c
+++ b/gsk/gskcairorenderer.c
@@ -123,21 +123,28 @@ gsk_cairo_renderer_render_node (GskCairoRenderer *self,
 }
 
 static void
-gsk_cairo_renderer_resize_viewport (GskRenderer           *renderer,
-                                    const graphene_rect_t *viewport)
+gsk_cairo_renderer_render (GskRenderer *renderer,
+                           GskRenderNode *root,
+                           GdkDrawingContext *context)
 {
   GskCairoRenderer *self = GSK_CAIRO_RENDERER (renderer);
+  cairo_t *cr = gdk_drawing_context_get_cairo_context (context); 
 
-  self->viewport = *viewport;
-}
+  gsk_renderer_get_viewport (renderer, &self->viewport);
 
-static void
-gsk_cairo_renderer_render (GskRenderer *renderer)
-{
-  GskCairoRenderer *self = GSK_CAIRO_RENDERER (renderer);
-  cairo_surface_t *target = gsk_renderer_get_surface (renderer);
-  GskRenderNode *root = gsk_renderer_get_root_node (renderer);
-  cairo_t *cr = cairo_create (target);
+  if (gsk_renderer_get_auto_clear (renderer))
+    {
+      cairo_save (cr);
+      cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+
+      if (gsk_renderer_get_use_alpha (renderer))
+        cairo_set_source_rgba (cr, 0, 0, 0, 0);
+      else
+        cairo_set_source_rgb (cr, 0, 0, 0);
+
+      cairo_paint (cr);
+      cairo_restore (cr);
+    }
 
   if (GSK_RENDER_MODE_CHECK (GEOMETRY))
     {
@@ -154,26 +161,6 @@ gsk_cairo_renderer_render (GskRenderer *renderer)
     }
 
   gsk_cairo_renderer_render_node (self, root, cr);
-
-  cairo_destroy (cr);
-}
-
-static void
-gsk_cairo_renderer_clear (GskRenderer *renderer)
-{
-  cairo_surface_t *surface = gsk_renderer_get_surface (renderer);
-  cairo_t *cr = cairo_create (surface);
-
-  cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
-
-  if (gsk_renderer_get_use_alpha (renderer))
-    cairo_set_source_rgba (cr, 0, 0, 0, 0);
-  else
-    cairo_set_source_rgb (cr, 0, 0, 0);
-
-  cairo_paint (cr);
-
-  cairo_destroy (cr);
 }
 
 static void
@@ -183,8 +170,6 @@ gsk_cairo_renderer_class_init (GskCairoRendererClass *klass)
 
   renderer_class->realize = gsk_cairo_renderer_realize;
   renderer_class->unrealize = gsk_cairo_renderer_unrealize;
-  renderer_class->resize_viewport = gsk_cairo_renderer_resize_viewport;
-  renderer_class->clear = gsk_cairo_renderer_clear;
   renderer_class->render = gsk_cairo_renderer_render;
 }
 
diff --git a/gsk/gskglrenderer.c b/gsk/gskglrenderer.c
index 40febca..a2f9a2f 100644
--- a/gsk/gskglrenderer.c
+++ b/gsk/gskglrenderer.c
@@ -432,16 +432,9 @@ gsk_gl_renderer_realize (GskRenderer *renderer)
   gdk_gl_context_make_current (self->context);
 
   GSK_NOTE (OPENGL, g_print ("Creating buffers and programs\n"));
-
   gsk_gl_renderer_create_buffers (self);
   gsk_gl_renderer_create_program (self);
 
-  self->opaque_render_items = g_array_sized_new (FALSE, FALSE, sizeof (RenderItem), 16);
-  g_array_set_clear_func (self->opaque_render_items, render_item_clear);
-
-  self->transparent_render_items = g_array_sized_new (FALSE, FALSE, sizeof (RenderItem), 16);
-  g_array_set_clear_func (self->opaque_render_items, render_item_clear);
-
   return TRUE;
 }
 
@@ -466,18 +459,22 @@ gsk_gl_renderer_unrealize (GskRenderer *renderer)
 }
 
 static void
-gsk_gl_renderer_resize_viewport (GskRenderer           *renderer,
+gsk_gl_renderer_resize_viewport (GskGLRenderer         *self,
                                  const graphene_rect_t *viewport)
 {
+  GSK_NOTE (OPENGL, g_print ("glViewport(0, 0, %g, %g)\n",
+                             viewport->size.width,
+                             viewport->size.height));
+
+  glViewport (viewport->origin.x, viewport->origin.y,
+              viewport->size.width, viewport->size.height);
 }
 
 static void
-gsk_gl_renderer_update (GskRenderer             *renderer,
-                        const graphene_matrix_t *modelview,
-                        const graphene_matrix_t *projection)
+gsk_gl_renderer_update_frustum (GskGLRenderer           *self,
+                                const graphene_matrix_t *modelview,
+                                const graphene_matrix_t *projection)
 {
-  GskGLRenderer *self = GSK_GL_RENDERER (renderer);
-
   GSK_NOTE (OPENGL, g_print ("Updating the modelview/projection\n"));
 
   graphene_matrix_multiply (modelview, projection, &self->mvp);
@@ -662,6 +659,7 @@ get_gl_scaling_filters (GskRenderer *renderer,
     }
 }
 
+#if 0
 static gboolean
 check_in_frustum (const graphene_frustum_t *frustum,
                   RenderItem               *item)
@@ -673,6 +671,7 @@ check_in_frustum (const graphene_frustum_t *frustum,
 
   return graphene_frustum_intersects_box (frustum, &aabb);
 }
+#endif
 
 static float
 project_item (const graphene_matrix_t *projection,
@@ -748,19 +747,6 @@ gsk_gl_renderer_add_render_item (GskGLRenderer *self,
   gsk_renderer_get_projection (GSK_RENDERER (self), &projection);
   item.z = project_item (&projection, &mv);
 
-  /* Discard the item if it's outside of the frustum as determined by the
-   * viewport and the projection matrix
-   */
-#if 0
-  if (!check_in_frustum (&self->frustum, &item))
-    {
-      GSK_NOTE (OPENGL, g_print ("Node <%s>[%p] culled by frustum\n",
-                                 node->name != NULL ? node->name : "unnamed",
-                                 node));
-      return;
-    }
-#endif
-
   /* TODO: This should really be an asset atlas, to avoid uploading a ton
    * of textures. Ideally we could use a single Cairo surface to get around
    * the GL texture limits and reorder the texture data on the CPU side and
@@ -787,7 +773,7 @@ gsk_gl_renderer_add_render_item (GskGLRenderer *self,
   if (gsk_render_node_is_opaque (node) && gsk_render_node_get_opacity (node) == 1.f)
     g_array_append_val (self->opaque_render_items, item);
   else
-    g_array_append_val (self->transparent_render_items, item);
+    g_array_prepend_val (self->transparent_render_items, item);
 
 recurse_children:
   gsk_render_node_iter_init (&iter, node);
@@ -795,198 +781,72 @@ recurse_children:
     gsk_gl_renderer_add_render_item (self, child);
 }
 
-static int
-opaque_item_cmp (gconstpointer _a,
-                 gconstpointer _b)
-{
-  const RenderItem *a = _a;
-  const RenderItem *b = _b;
-
-  if (a->z != b->z)
-    {
-      if (a->z > b->z)
-        return 1;
-
-      return -1;
-    }
-
-  if (a != b)
-    {
-      if ((gsize) a > (gsize) b)
-        return 1;
-
-      return -1;
-    }
-
-  return 0;
-}
-
-static int
-transparent_item_cmp (gconstpointer _a,
-                      gconstpointer _b)
-{
-  const RenderItem *a = _a;
-  const RenderItem *b = _b;
-
-  if (a->z != b->z)
-    {
-     if (a->z < b->z)
-       return 1;
-
-     return -1;
-    }
-
-  if (a != b)
-    {
-      if ((gsize) a < (gsize) b)
-        return 1;
-
-      return -1;
-    }
-
-  return 0;
-}
-
-static void
-gsk_gl_renderer_validate_tree (GskRenderer   *renderer,
+static gboolean
+gsk_gl_renderer_validate_tree (GskGLRenderer *self,
                                GskRenderNode *root)
 {
-  GskGLRenderer *self = GSK_GL_RENDERER (renderer);
-  gboolean clear_items = FALSE;
-  int i;
+  int n_children;
 
   if (self->context == NULL)
-    return;
-
-  gdk_gl_context_make_current (self->context);
+    return FALSE;
 
-  if (self->opaque_render_items->len > 0 || self->transparent_render_items->len > 0)
-    {
-      /* If we only changed the opacity and transformations then there is no
-       * reason to clear the render items
-       */
-      for (i = 0; i < self->opaque_render_items->len; i++)
-        {
-          RenderItem *item = &g_array_index (self->opaque_render_items, RenderItem, i);
-          GskRenderNodeChanges changes = gsk_render_node_get_last_state (item->node);
-
-          if (changes == 0)
-            continue;
-
-          if ((changes & GSK_RENDER_NODE_CHANGES_UPDATE_OPACITY) != 0)
-            {
-              item->opaque = gsk_render_node_is_opaque (item->node);
-              item->opacity = gsk_render_node_get_opacity (item->node);
-              changes &= ~GSK_RENDER_NODE_CHANGES_UPDATE_OPACITY;
-            }
-
-          if (changes & GSK_RENDER_NODE_CHANGES_UPDATE_TRANSFORM)
-            {
-              gsk_render_node_get_world_matrix (item->node, &item->mvp);
-              changes &= ~ GSK_RENDER_NODE_CHANGES_UPDATE_TRANSFORM;
-            }
-
-          if (changes != 0)
-            {
-              clear_items = TRUE;
-              break;
-            }
-        }
-
-      for (i = 0; i < self->transparent_render_items->len; i++)
-        {
-          RenderItem *item = &g_array_index (self->transparent_render_items, RenderItem, i);
-          GskRenderNodeChanges changes = gsk_render_node_get_last_state (item->node);
-
-          if (changes == 0)
-            continue;
-
-          if ((changes & GSK_RENDER_NODE_CHANGES_UPDATE_OPACITY) != 0)
-            {
-              item->opaque = gsk_render_node_is_opaque (item->node);
-              item->opacity = gsk_render_node_get_opacity (item->node);
-              changes &= ~GSK_RENDER_NODE_CHANGES_UPDATE_OPACITY;
-            }
-
-          if (changes & GSK_RENDER_NODE_CHANGES_UPDATE_TRANSFORM)
-            {
-              gsk_render_node_get_world_matrix (item->node, &item->mvp);
-              changes &= ~ GSK_RENDER_NODE_CHANGES_UPDATE_TRANSFORM;
-            }
-
-          if (changes != 0)
-            {
-              clear_items = TRUE;
-              break;
-            }
-        }
-    }
-  else
-    clear_items = TRUE;
+  n_children = gsk_render_node_get_n_children (root);
+  if (n_children == 0)
+    return FALSE;
 
-  if (!clear_items)
-    {
-      GSK_NOTE (OPENGL, g_print ("Tree is still valid\n"));
-      goto out;
-    }
+  gdk_gl_context_make_current (self->context);
 
-  for (i = 0; i < self->opaque_render_items->len; i++)
-    render_item_clear (&g_array_index (self->opaque_render_items, RenderItem, i));
-  for (i = 0; i < self->transparent_render_items->len; i++)
-    render_item_clear (&g_array_index (self->transparent_render_items, RenderItem, i));
+  self->opaque_render_items = g_array_sized_new (FALSE, FALSE, sizeof (RenderItem), n_children);
+  self->transparent_render_items = g_array_sized_new (FALSE, FALSE, sizeof (RenderItem), n_children);
 
-  g_array_set_size (self->opaque_render_items, 0);
-  g_array_set_size (self->transparent_render_items, 0);
+  g_array_set_clear_func (self->opaque_render_items, render_item_clear);
+  g_array_set_clear_func (self->transparent_render_items, render_item_clear);
 
   GSK_NOTE (OPENGL, g_print ("RenderNode -> RenderItem\n"));
-  gsk_gl_renderer_add_render_item (self, gsk_renderer_get_root_node (renderer));
-
-  GSK_NOTE (OPENGL, g_print ("Sorting render nodes\n"));
-  g_array_sort (self->opaque_render_items, opaque_item_cmp);
-  g_array_sort (self->transparent_render_items, transparent_item_cmp);
+  gsk_gl_renderer_add_render_item (self, root);
 
-out:
   GSK_NOTE (OPENGL, g_print ("Total render items: %d (opaque:%d, transparent:%d)\n",
                              self->opaque_render_items->len + self->transparent_render_items->len,
                              self->opaque_render_items->len,
                              self->transparent_render_items->len));
+
+  return TRUE;
 }
 
 static void
-gsk_gl_renderer_clear_tree (GskRenderer *renderer,
-                            GskRenderNode *root_node)
+gsk_gl_renderer_clear_tree (GskGLRenderer *self)
 {
-  GskGLRenderer *self = GSK_GL_RENDERER (renderer);
-
   if (self->context == NULL)
     return;
 
-  gdk_gl_context_make_current (self->context);
-
   g_clear_pointer (&self->opaque_render_items, g_array_unref);
   g_clear_pointer (&self->transparent_render_items, g_array_unref);
-
-  if (gsk_renderer_is_realized (renderer))
-    {
-      self->opaque_render_items = g_array_sized_new (FALSE, FALSE, sizeof (RenderItem), 16);
-      g_array_set_clear_func (self->opaque_render_items, render_item_clear);
-
-      self->transparent_render_items = g_array_sized_new (FALSE, FALSE, sizeof (RenderItem), 16);
-      g_array_set_clear_func (self->opaque_render_items, render_item_clear);
-    }
 }
 
 static void
-gsk_gl_renderer_clear (GskRenderer *renderer)
+gsk_gl_renderer_clear (GskGLRenderer *self)
 {
+  int clear_bits = GL_COLOR_BUFFER_BIT;
+
+  if (self->has_depth_buffer)
+    clear_bits |= GL_DEPTH_BUFFER_BIT;
+  if (self->has_stencil_buffer)
+    clear_bits |= GL_STENCIL_BUFFER_BIT;
+
+  GSK_NOTE (OPENGL, g_print ("Clearing viewport\n"));
+  glClearColor (0, 0, 0, 0);
+  glClear (clear_bits);
 }
 
 static void
-gsk_gl_renderer_render (GskRenderer *renderer)
+gsk_gl_renderer_render (GskRenderer *renderer,
+                        GskRenderNode *root,
+                        GdkDrawingContext *context)
 {
   GskGLRenderer *self = GSK_GL_RENDERER (renderer);
+  graphene_matrix_t modelview, projection;
   graphene_rect_t viewport;
-  int scale, status, clear_bits;
+  int status;
   guint i;
 
   if (self->context == NULL)
@@ -1008,22 +868,16 @@ gsk_gl_renderer_render (GskRenderer *renderer)
   /* Ensure that the viewport is up to date */
   status = glCheckFramebufferStatusEXT (GL_FRAMEBUFFER_EXT);
   if (status == GL_FRAMEBUFFER_COMPLETE_EXT)
-    {
-      GSK_NOTE (OPENGL, g_print ("glViewport(0, 0, %g, %g)\n",
-                                 viewport.size.width,
-                                 viewport.size.height));
-      glViewport (0, 0, viewport.size.width, viewport.size.height);
-    }
+    gsk_gl_renderer_resize_viewport (self, &viewport);
 
-  clear_bits = GL_COLOR_BUFFER_BIT;
-  if (self->has_depth_buffer)
-    clear_bits |= GL_DEPTH_BUFFER_BIT;
-  if (self->has_stencil_buffer)
-    clear_bits |= GL_STENCIL_BUFFER_BIT;
+  gsk_gl_renderer_clear (self);
 
-  GSK_NOTE (OPENGL, g_print ("Clearing viewport\n"));
-  glClearColor (0, 0, 0, 0);
-  glClear (clear_bits);
+  gsk_renderer_get_modelview (renderer, &modelview);
+  gsk_renderer_get_projection (renderer, &projection);
+  gsk_gl_renderer_update_frustum (self, &modelview, &projection);
+
+  if (!gsk_gl_renderer_validate_tree (self, root))
+    goto out;
 
   /* Opaque pass: front-to-back */
   GSK_NOTE (OPENGL, g_print ("Rendering %u opaque items\n", self->opaque_render_items->len));
@@ -1050,15 +904,18 @@ gsk_gl_renderer_render (GskRenderer *renderer)
   /* Draw the output of the GL rendering to the window */
   GSK_NOTE (OPENGL, g_print ("Drawing GL content on Cairo surface using a %s\n",
                              self->texture_id != 0 ? "texture" : "renderbuffer"));
-  scale = 1;
-  gdk_cairo_draw_from_gl (gsk_renderer_get_draw_context (renderer),
-                          gsk_renderer_get_window (renderer),
+
+out:
+  gdk_cairo_draw_from_gl (gdk_drawing_context_get_cairo_context (context),
+                          gdk_drawing_context_get_window (context),
                           self->texture_id != 0 ? self->texture_id : self->render_buffer,
                           self->texture_id != 0 ? GL_TEXTURE : GL_RENDERBUFFER,
-                          scale,
+                          gsk_renderer_get_scale_factor (renderer),
                           0, 0, viewport.size.width, viewport.size.height);
 
   gdk_gl_context_make_current (self->context);
+
+  gsk_gl_renderer_clear_tree (self);
 }
 
 static void
@@ -1071,11 +928,6 @@ gsk_gl_renderer_class_init (GskGLRendererClass *klass)
 
   renderer_class->realize = gsk_gl_renderer_realize;
   renderer_class->unrealize = gsk_gl_renderer_unrealize;
-  renderer_class->resize_viewport = gsk_gl_renderer_resize_viewport;
-  renderer_class->update = gsk_gl_renderer_update;
-  renderer_class->clear = gsk_gl_renderer_clear;
-  renderer_class->validate_tree = gsk_gl_renderer_validate_tree;
-  renderer_class->clear_tree = gsk_gl_renderer_clear_tree;
   renderer_class->render = gsk_gl_renderer_render;
 }
 
@@ -1089,27 +941,3 @@ gsk_gl_renderer_init (GskGLRenderer *self)
   self->has_depth_buffer = TRUE;
   self->has_stencil_buffer = TRUE;
 }
-
-void
-gsk_gl_renderer_set_context (GskGLRenderer *renderer,
-                             GdkGLContext  *context)
-{
-  g_return_if_fail (GSK_IS_GL_RENDERER (renderer));
-  g_return_if_fail (context == NULL || GDK_IS_GL_CONTEXT (context));
-
-  if (gsk_renderer_is_realized (GSK_RENDERER (renderer)))
-    return;
-
-  if (gdk_gl_context_get_display (context) != gsk_renderer_get_display (GSK_RENDERER (renderer)))
-    return;
-
-  g_set_object (&renderer->context, context);
-}
-
-GdkGLContext *
-gsk_gl_renderer_get_context (GskGLRenderer *renderer)
-{
-  g_return_val_if_fail (GSK_IS_GL_RENDERER (renderer), NULL);
-
-  return renderer->context;
-}
diff --git a/gsk/gskrenderer.c b/gsk/gskrenderer.c
index 3a84fc9..72717bb 100644
--- a/gsk/gskrenderer.c
+++ b/gsk/gskrenderer.c
@@ -29,7 +29,6 @@
 #include "gskrendererprivate.h"
 
 #include "gskdebugprivate.h"
-#include "gskcairorendererprivate.h"
 #include "gskglrendererprivate.h"
 #include "gskrendernodeprivate.h"
 
@@ -50,9 +49,6 @@ typedef struct
 {
   GObject parent_instance;
 
-  GdkDisplay *display;
-  GdkWindow *window;
-
   graphene_rect_t viewport;
   graphene_matrix_t modelview;
   graphene_matrix_t projection;
@@ -60,16 +56,14 @@ typedef struct
   GskScalingFilter min_filter;
   GskScalingFilter mag_filter;
 
+  GdkWindow *window;
+  GdkDrawingContext *drawing_context;
   GskRenderNode *root_node;
+  GdkDisplay *display;
 
-  cairo_surface_t *surface;
-  cairo_t *draw_context;
+  int scale_factor;
 
   gboolean is_realized : 1;
-  gboolean needs_viewport_resize : 1;
-  gboolean needs_modelview_update : 1;
-  gboolean needs_projection_update : 1;
-  gboolean needs_tree_validation : 1;
   gboolean auto_clear : 1;
   gboolean use_alpha : 1;
 } GskRendererPrivate;
@@ -83,11 +77,12 @@ enum {
   PROP_MINIFICATION_FILTER,
   PROP_MAGNIFICATION_FILTER,
   PROP_AUTO_CLEAR,
+  PROP_USE_ALPHA,
+  PROP_SCALE_FACTOR,
+  PROP_WINDOW,
   PROP_ROOT_NODE,
   PROP_DISPLAY,
-  PROP_WINDOW,
-  PROP_SURFACE,
-  PROP_USE_ALPHA,
+  PROP_DRAWING_CONTEXT,
 
   N_PROPS
 };
@@ -111,37 +106,14 @@ gsk_renderer_real_unrealize (GskRenderer *self)
 }
 
 static void
-gsk_renderer_real_render (GskRenderer *self)
+gsk_renderer_real_render (GskRenderer *self,
+                          GskRenderNode *root,
+                          GdkDrawingContext *context)
 {
   GSK_RENDERER_WARN_NOT_IMPLEMENTED_METHOD (self, render);
 }
 
 static void
-gsk_renderer_real_resize_viewport (GskRenderer *self,
-                                   const graphene_rect_t *viewport)
-{
-}
-
-static void
-gsk_renderer_real_update (GskRenderer *self,
-                          const graphene_matrix_t *mv,
-                          const graphene_matrix_t *proj)
-{
-}
-
-static void
-gsk_renderer_real_validate_tree (GskRenderer *self,
-                                 GskRenderNode *root)
-{
-}
-
-static void
-gsk_renderer_real_clear_tree (GskRenderer *self,
-                              GskRenderNode *old_root)
-{
-}
-
-static void
 gsk_renderer_dispose (GObject *gobject)
 {
   GskRenderer *self = GSK_RENDERER (gobject);
@@ -149,11 +121,7 @@ gsk_renderer_dispose (GObject *gobject)
 
   gsk_renderer_unrealize (self);
 
-  g_clear_pointer (&priv->surface, cairo_surface_destroy);
-  g_clear_pointer (&priv->draw_context, cairo_destroy);
-
   g_clear_object (&priv->window);
-  g_clear_object (&priv->root_node);
   g_clear_object (&priv->display);
 
   G_OBJECT_CLASS (gsk_renderer_parent_class)->dispose (gobject);
@@ -194,12 +162,12 @@ gsk_renderer_set_property (GObject      *gobject,
       gsk_renderer_set_auto_clear (self, g_value_get_boolean (value));
       break;
 
-    case PROP_ROOT_NODE:
-      gsk_renderer_set_root_node (self, g_value_get_object (value));
+    case PROP_USE_ALPHA:
+      gsk_renderer_set_use_alpha (self, g_value_get_boolean (value));
       break;
 
-    case PROP_SURFACE:
-      gsk_renderer_set_surface (self, g_value_get_boxed (value));
+    case PROP_SCALE_FACTOR:
+      gsk_renderer_set_scale_factor (self, g_value_get_int (value));
       break;
 
     case PROP_WINDOW:
@@ -207,12 +175,9 @@ gsk_renderer_set_property (GObject      *gobject,
       break;
 
     case PROP_DISPLAY:
+      /* Construct-only */
       priv->display = g_value_dup_object (value);
       break;
-
-    case PROP_USE_ALPHA:
-      gsk_renderer_set_use_alpha (self, g_value_get_boolean (value));
-      break;
     }
 }
 
@@ -251,24 +216,28 @@ gsk_renderer_get_property (GObject    *gobject,
       g_value_set_boolean (value, priv->auto_clear);
       break;
 
-    case PROP_ROOT_NODE:
-      g_value_set_object (value, priv->root_node);
-      break;
-
-    case PROP_SURFACE:
-      g_value_set_boxed (value, priv->surface);
+    case PROP_USE_ALPHA:
+      g_value_set_boolean (value, priv->use_alpha);
       break;
 
-    case PROP_DISPLAY:
-      g_value_set_object (value, priv->display);
+    case PROP_SCALE_FACTOR:
+      g_value_set_int (value, priv->scale_factor);
       break;
 
     case PROP_WINDOW:
       g_value_set_object (value, priv->window);
       break;
 
-    case PROP_USE_ALPHA:
-      g_value_set_boolean (value, priv->use_alpha);
+    case PROP_ROOT_NODE:
+      g_value_set_object (value, priv->root_node);
+      break;
+
+    case PROP_DRAWING_CONTEXT:
+      g_value_set_object (value, priv->drawing_context);
+      break;
+
+    case PROP_DISPLAY:
+      g_value_set_object (value, priv->display);
       break;
     }
 }
@@ -297,10 +266,6 @@ gsk_renderer_class_init (GskRendererClass *klass)
 
   klass->realize = gsk_renderer_real_realize;
   klass->unrealize = gsk_renderer_real_unrealize;
-  klass->resize_viewport = gsk_renderer_real_resize_viewport;
-  klass->update = gsk_renderer_real_update;
-  klass->validate_tree = gsk_renderer_real_validate_tree;
-  klass->clear_tree = gsk_renderer_real_clear_tree;
   klass->render = gsk_renderer_real_render;
 
   gobject_class->constructed = gsk_renderer_constructed;
@@ -448,27 +413,8 @@ gsk_renderer_class_init (GskRendererClass *klass)
                          "Root Node",
                          "The root render node to render",
                          GSK_TYPE_RENDER_NODE,
-                         G_PARAM_READWRITE |
-                         G_PARAM_STATIC_STRINGS |
-                         G_PARAM_EXPLICIT_NOTIFY);
-
-  /**
-   * GskRenderer:surface:
-   *
-   * The target rendering surface.
-   *
-   * See also: #GskRenderer:window.
-   *
-   * Since: 3.22
-   */
-  gsk_renderer_properties[PROP_SURFACE] =
-    g_param_spec_boxed ("surface",
-                       "Surface",
-                       "The Cairo surface used to render to",
-                       CAIRO_GOBJECT_TYPE_SURFACE,
-                       G_PARAM_READWRITE |
-                       G_PARAM_STATIC_STRINGS |
-                       G_PARAM_EXPLICIT_NOTIFY);
+                         G_PARAM_READABLE |
+                         G_PARAM_STATIC_STRINGS);
 
   /**
    * GskRenderer:display:
@@ -486,22 +432,46 @@ gsk_renderer_class_init (GskRendererClass *klass)
                         G_PARAM_CONSTRUCT_ONLY |
                         G_PARAM_STATIC_STRINGS);
 
-  /**
-   * GskRenderer:window:
-   *
-   * The #GdkWindow used to create a target surface, if #GskRenderer:surface
-   * is not explicitly set.
-   *
-   * Since: 3.22
-   */
   gsk_renderer_properties[PROP_WINDOW] =
     g_param_spec_object ("window",
                          "Window",
-                         "The GdkWindow associated to the renderer",
+                         "The window associated to the renderer",
                          GDK_TYPE_WINDOW,
                          G_PARAM_READWRITE |
-                         G_PARAM_STATIC_STRINGS |
-                         G_PARAM_EXPLICIT_NOTIFY);
+                         G_PARAM_CONSTRUCT_ONLY |
+                         G_PARAM_STATIC_STRINGS);
+
+  /**
+   * GskRenderer:scale-factor:
+   *
+   * The scale factor used when rendering.
+   *
+   * Since: 3.22
+   */
+  gsk_renderer_properties[PROP_SCALE_FACTOR] =
+    g_param_spec_int ("scale-factor",
+                      "Scale Factor",
+                      "The scaling factor of the renderer",
+                      1, G_MAXINT,
+                      1,
+                      G_PARAM_READWRITE |
+                      G_PARAM_STATIC_STRINGS |
+                      G_PARAM_EXPLICIT_NOTIFY);
+
+  /**
+   * GskRenderer:drawing-context:
+   *
+   * The drawing context used when rendering.
+   *
+   * Since: 3.22
+   */
+  gsk_renderer_properties[PROP_DRAWING_CONTEXT] =
+    g_param_spec_object ("drawing-context",
+                         "Drawing Context",
+                         "The drawing context used by the renderer",
+                         GDK_TYPE_DRAWING_CONTEXT,
+                         G_PARAM_READABLE |
+                         G_PARAM_STATIC_STRINGS);
 
   /**
    * GskRenderer:use-alpha:
@@ -531,6 +501,7 @@ gsk_renderer_init (GskRenderer *self)
   graphene_matrix_init_identity (&priv->projection);
 
   priv->auto_clear = TRUE;
+  priv->scale_factor = 1;
 
   priv->min_filter = GSK_SCALING_FILTER_LINEAR;
   priv->mag_filter = GSK_SCALING_FILTER_LINEAR;
@@ -565,9 +536,6 @@ gsk_renderer_set_viewport (GskRenderer           *renderer,
     return;
 
   graphene_rect_init_from_rect (&priv->viewport, viewport);
-  priv->needs_viewport_resize = TRUE;
-  priv->needs_modelview_update = TRUE;
-  priv->needs_projection_update = TRUE;
 
   g_object_notify_by_pspec (G_OBJECT (renderer), gsk_renderer_properties[PROP_VIEWPORT]);
 }
@@ -618,8 +586,6 @@ gsk_renderer_set_modelview (GskRenderer             *renderer,
   else
     graphene_matrix_init_from_matrix (&priv->modelview, modelview);
 
-  priv->needs_modelview_update = TRUE;
-
   g_object_notify_by_pspec (G_OBJECT (renderer), gsk_renderer_properties[PROP_MODELVIEW]);
 }
 
@@ -666,8 +632,6 @@ gsk_renderer_set_projection (GskRenderer             *renderer,
   else
     graphene_matrix_init_from_matrix (&priv->projection, projection);
 
-  priv->needs_projection_update = TRUE;
-
   g_object_notify_by_pspec (G_OBJECT (renderer), gsk_renderer_properties[PROP_PROJECTION]);
 }
 
@@ -692,67 +656,6 @@ gsk_renderer_get_projection (GskRenderer       *renderer,
   graphene_matrix_init_from_matrix (projection, &priv->projection);
 }
 
-static void
-gsk_renderer_invalidate_tree (GskRenderNode *node,
-                              gpointer       data)
-{
-  GskRenderer *self = data;
-  GskRendererPrivate *priv = gsk_renderer_get_instance_private (self);
-
-  GSK_NOTE (RENDERER, g_print ("Invalidating tree.\n"));
-
-  /* Since the scene graph has changed in some way, we need to re-validate it. */
-  priv->needs_tree_validation = TRUE;
-}
-
-/**
- * gsk_renderer_set_root_node:
- * @renderer: a #GskRenderer
- * @root: (nullable): a #GskRenderNode
- *
- * Sets the root node of the scene graph to be rendered.
- *
- * The #GskRenderer will acquire a reference on @root.
- *
- * Since: 3.22
- */
-void
-gsk_renderer_set_root_node (GskRenderer   *renderer,
-                            GskRenderNode *root)
-{
-  GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
-  GskRenderNode *old_root;
-
-  g_return_if_fail (GSK_IS_RENDERER (renderer));
-  g_return_if_fail (GSK_IS_RENDER_NODE (root));
-
-  old_root = priv->root_node != NULL ? g_object_ref (priv->root_node) : NULL;
-
-  if (g_set_object (&priv->root_node, root))
-    {
-      /* We need to unset the invalidate function on the old instance */
-      if (old_root != NULL)
-        {
-          gsk_render_node_set_invalidate_func (old_root, NULL, NULL, NULL);
-          gsk_renderer_clear_tree (renderer, old_root);
-          g_object_unref (old_root);
-        }
-
-      if (priv->root_node != NULL)
-        gsk_render_node_set_invalidate_func (priv->root_node,
-                                             gsk_renderer_invalidate_tree,
-                                             renderer,
-                                             NULL);
-
-      /* If we don't have a root node, there's really no point in validating a
-       * tree that it's not going to be drawn
-       */
-      priv->needs_tree_validation = priv->root_node != NULL;
-
-      g_object_notify_by_pspec (G_OBJECT (renderer), gsk_renderer_properties[PROP_ROOT_NODE]);
-    }
-}
-
 /**
  * gsk_renderer_set_scaling_filters:
  * @renderer: a #GskRenderer
@@ -819,143 +722,73 @@ gsk_renderer_get_scaling_filters (GskRenderer      *renderer,
     *mag_filter = priv->mag_filter;
 }
 
-/**
- * gsk_renderer_set_surface:
- * @renderer: a #GskRenderer
- * @surface: (nullable): a Cairo surface
- *
- * Sets the #cairo_surface_t used as the target rendering surface.
- *
- * This function will acquire a reference to @surface.
- *
- * See also: gsk_renderer_set_window()
- *
- * Since: 3.22
- */
 void
-gsk_renderer_set_surface (GskRenderer     *renderer,
-                          cairo_surface_t *surface)
+gsk_renderer_set_scale_factor (GskRenderer *renderer,
+                               int          scale_factor)
 {
   GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
 
   g_return_if_fail (GSK_IS_RENDERER (renderer));
 
-  if (priv->surface == surface)
-    return;
-
-  g_clear_pointer (&priv->surface, cairo_surface_destroy);
-
-  if (surface != NULL)
-    priv->surface = cairo_surface_reference (surface);
+  if (priv->scale_factor != scale_factor)
+    {
+      priv->scale_factor = scale_factor;
 
-  g_object_notify_by_pspec (G_OBJECT (renderer), gsk_renderer_properties[PROP_SURFACE]);
+      g_object_notify_by_pspec (G_OBJECT (renderer), gsk_renderer_properties[PROP_SCALE_FACTOR]);
+    }
 }
 
-/**
- * gsk_renderer_get_surface:
- * @renderer: a #GskRenderer
- *
- * Retrieve the target rendering surface used by @renderer.
- *
- * If you did not use gsk_renderer_set_surface(), a compatible surface
- * will be created by using the #GdkWindow passed to gsk_renderer_set_window().
- *
- * Returns: (transfer none) (nullable): a Cairo surface
- *
- * Since: 3.22
- */
-cairo_surface_t *
-gsk_renderer_get_surface (GskRenderer *renderer)
+int
+gsk_renderer_get_scale_factor (GskRenderer *renderer)
 {
   GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
 
-  g_return_val_if_fail (GSK_IS_RENDERER (renderer), NULL);
-
-  if (priv->surface != NULL)
-    return priv->surface;
+  g_return_val_if_fail (GSK_IS_RENDERER (renderer), 1);
 
-  if (priv->window != NULL)
-    {
-      int scale = gdk_window_get_scale_factor (priv->window);
-      int width = gdk_window_get_width (priv->window);
-      int height = gdk_window_get_height (priv->window);
-      cairo_content_t content;
-
-      if (priv->use_alpha)
-        content = CAIRO_CONTENT_COLOR_ALPHA;
-      else
-        content = CAIRO_CONTENT_COLOR;
-
-      GSK_NOTE (RENDERER, g_print ("Creating surface from window [%p] (w:%d, h:%d, s:%d, a:%s)\n",
-                                   priv->window,
-                                   width, height, scale,
-                                   priv->use_alpha ? "y" : "n"));
-
-      priv->surface = gdk_window_create_similar_surface (priv->window,
-                                                         content,
-                                                         width, height);
-    }
-
-  return priv->surface;
+  return priv->scale_factor;
 }
 
 void
-gsk_renderer_set_draw_context (GskRenderer *renderer,
-                               cairo_t     *cr)
+gsk_renderer_set_window (GskRenderer *renderer,
+                         GdkWindow   *window)
 {
   GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
 
   g_return_if_fail (GSK_IS_RENDERER (renderer));
+  g_return_if_fail (!priv->is_realized);
+  g_return_if_fail (window == NULL || GDK_IS_WINDOW (window));
 
-  if (priv->draw_context == cr)
-    return;
-
-  g_clear_pointer (&priv->draw_context, cairo_destroy);
-  priv->draw_context = cr != NULL ? cairo_reference (cr) : NULL;
-}
-
-cairo_t *
-gsk_renderer_get_draw_context (GskRenderer *renderer)
-{
-  GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
-
-  g_return_val_if_fail (GSK_IS_RENDERER (renderer), NULL);
-
-  if (priv->draw_context != NULL)
-    return priv->draw_context;
-
-  return cairo_create (gsk_renderer_get_surface (renderer));
+  if (g_set_object (&priv->window, window))
+    g_object_notify_by_pspec (G_OBJECT (renderer), gsk_renderer_properties[PROP_WINDOW]);
 }
 
 /**
- * gsk_renderer_get_display:
+ * gsk_renderer_get_window:
  * @renderer: a #GskRenderer
  *
- * Retrieves the #GdkDisplay used when creating the #GskRenderer.
+ * Retrieves the #GdkWindow set using gsk_renderer_set_window().
  *
- * Returns: (transfer none): a #GdkDisplay
+ * Returns: (transfer none) (nullable): a #GdkWindow
  *
  * Since: 3.22
  */
-GdkDisplay *
-gsk_renderer_get_display (GskRenderer *renderer)
+GdkWindow *
+gsk_renderer_get_window (GskRenderer *renderer)
 {
   GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
 
   g_return_val_if_fail (GSK_IS_RENDERER (renderer), NULL);
 
-  return priv->display;
+  return priv->window;
 }
 
-/**
+/*< private >
  * gsk_renderer_get_root_node:
  * @renderer: a #GskRenderer
  *
- * Retrieves the root node of the scene graph.
+ * Retrieves the #GskRenderNode used by @renderer.
  *
  * Returns: (transfer none) (nullable): a #GskRenderNode
- *
- * Since: 3.22
  */
 GskRenderNode *
 gsk_renderer_get_root_node (GskRenderer *renderer)
@@ -968,68 +801,61 @@ gsk_renderer_get_root_node (GskRenderer *renderer)
 }
 
 /*< private >
- * gsk_renderer_is_realized:
+ * gsk_renderer_get_drawing_context:
  * @renderer: a #GskRenderer
  *
- * Checks whether the @renderer is realized or not.
+ * Retrieves the #GdkDrawingContext used by @renderer.
  *
- * Returns: %TRUE if the #GskRenderer was realized, and %FALSE otherwise
- *
- * Since: 3.22
+ * Returns: (transfer none) (nullable): a #GdkDrawingContext
  */
-gboolean
-gsk_renderer_is_realized (GskRenderer *renderer)
+GdkDrawingContext *
+gsk_renderer_get_drawing_context (GskRenderer *renderer)
 {
   GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
 
-  g_return_val_if_fail (GSK_IS_RENDERER (renderer), FALSE);
+  g_return_val_if_fail (GSK_IS_RENDERER (renderer), NULL);
 
-  return priv->is_realized;
+  return priv->drawing_context;
 }
 
 /**
- * gsk_renderer_set_window:
+ * gsk_renderer_get_display:
  * @renderer: a #GskRenderer
- * @window: (nullable): a #GdkWindow
  *
- * Sets the #GdkWindow used to create the target rendering surface.
+ * Retrieves the #GdkDisplay used when creating the #GskRenderer.
  *
- * See also: gsk_renderer_set_surface()
+ * Returns: (transfer none): a #GdkDisplay
  *
  * Since: 3.22
  */
-void
-gsk_renderer_set_window (GskRenderer *renderer,
-                         GdkWindow   *window)
+GdkDisplay *
+gsk_renderer_get_display (GskRenderer *renderer)
 {
   GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
 
-  g_return_if_fail (GSK_IS_RENDERER (renderer));
-  g_return_if_fail (window == NULL || GDK_IS_WINDOW (window));
-  g_return_if_fail (!priv->is_realized);
+  g_return_val_if_fail (GSK_IS_RENDERER (renderer), NULL);
 
-  if (g_set_object (&priv->window, window))
-    g_object_notify_by_pspec (G_OBJECT (renderer), gsk_renderer_properties[PROP_WINDOW]);
+  return priv->display;
 }
 
-/**
- * gsk_renderer_get_window:
+/*< private >
+ * gsk_renderer_is_realized:
  * @renderer: a #GskRenderer
  *
- * Retrieves the #GdkWindow set with gsk_renderer_set_window().
+ * Checks whether the @renderer is realized or not.
  *
- * Returns: (transfer none) (nullable): a #GdkWindow
+ * Returns: %TRUE if the #GskRenderer was realized, and %FALSE otherwise
  *
  * Since: 3.22
  */
-GdkWindow *
-gsk_renderer_get_window (GskRenderer *renderer)
+gboolean
+gsk_renderer_is_realized (GskRenderer *renderer)
 {
   GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
 
-  g_return_val_if_fail (GSK_IS_RENDERER (renderer), NULL);
+  g_return_val_if_fail (GSK_IS_RENDERER (renderer), FALSE);
 
-  return priv->window;
+  return priv->is_realized;
 }
 
 /**
@@ -1051,12 +877,6 @@ gsk_renderer_realize (GskRenderer *renderer)
   if (priv->is_realized)
     return TRUE;
 
-  if (priv->window == NULL && priv->surface == NULL)
-    {
-      g_critical ("No rendering surface has been set.");
-      return FALSE;
-    }
-
   priv->is_realized = GSK_RENDERER_GET_CLASS (renderer)->realize (renderer);
 
   return priv->is_realized;
@@ -1085,156 +905,41 @@ gsk_renderer_unrealize (GskRenderer *renderer)
   priv->is_realized = FALSE;
 }
 
-/*< private >
- * gsk_renderer_maybe_resize_viewport:
- * @renderer: a #GskRenderer
- *
- * Optionally resize the viewport of @renderer.
- *
- * This function should be called by gsk_renderer_render().
- *
- * This function may call @GskRendererClass.resize_viewport().
- */
-void
-gsk_renderer_maybe_resize_viewport (GskRenderer *renderer)
-{
-  GskRendererClass *renderer_class = GSK_RENDERER_GET_CLASS (renderer);
-  GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
-
-  if (priv->needs_viewport_resize)
-    {
-      renderer_class->resize_viewport (renderer, &priv->viewport);
-      priv->needs_viewport_resize = FALSE;
-
-      GSK_NOTE (RENDERER, g_print ("Viewport size: %g x %g\n",
-                                   priv->viewport.size.width,
-                                   priv->viewport.size.height));
-
-      /* If the target surface has been created from a window, we need
-       * to clear it, so that it gets recreated with the right size
-       */
-      if (priv->window != NULL && priv->surface != NULL)
-        g_clear_pointer (&priv->surface, cairo_surface_destroy);
-    }
-}
-
-/*< private >
- * gsk_renderer_maybe_update:
- * @renderer: a #GskRenderer
- *
- * Optionally recomputes the modelview-projection matrix used by
- * the @renderer.
- *
- * This function should be called by gsk_renderer_render().
- *
- * This function may call @GskRendererClass.update().
- */
-void
-gsk_renderer_maybe_update (GskRenderer *renderer)
-{
-  GskRendererClass *renderer_class = GSK_RENDERER_GET_CLASS (renderer);
-  GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
-
-  if (priv->needs_modelview_update || priv->needs_projection_update)
-    {
-      renderer_class->update (renderer, &priv->modelview, &priv->projection);
-      priv->needs_modelview_update = FALSE;
-      priv->needs_projection_update = FALSE;
-    }
-}
-
-void
-gsk_renderer_clear_tree (GskRenderer   *renderer,
-                         GskRenderNode *old_root)
-{
-  GSK_RENDERER_GET_CLASS (renderer)->clear_tree (renderer, old_root);
-}
-
-/*< private >
- * gsk_renderer_maybe_validate_tree:
- * @renderer: a #GskRenderer
- *
- * Optionally validates the #GskRenderNode scene graph, and uses it
- * to generate more efficient intermediate representations depending
- * on the type of @renderer.
- *
- * This function should be called by gsk_renderer_render().
- *
- * This function may call @GskRendererClas.validate_tree().
- */
-void
-gsk_renderer_maybe_validate_tree (GskRenderer *renderer)
-{
-  GskRendererClass *renderer_class = GSK_RENDERER_GET_CLASS (renderer);
-  GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
-
-  if (priv->root_node == NULL)
-    return;
-
-  /* Ensure that the render nodes are valid; this will change the
-   * needs_tree_validation flag on the renderer, if needed
-   */
-  gsk_render_node_validate (priv->root_node);
-
-  if (priv->needs_tree_validation)
-    {
-      /* Ensure that the Renderer can update itself */
-      renderer_class->validate_tree (renderer, priv->root_node);
-      priv->needs_tree_validation = FALSE;
-    }
-}
-
-/*< private >
- * gsk_renderer_maybe_clear:
- * @renderer: a #GskRenderer
- *
- * Optionally calls @GskRendererClass.clear(), depending on the value
- * of #GskRenderer:auto-clear.
- *
- * This function should be called by gsk_renderer_render().
- */
-void
-gsk_renderer_maybe_clear (GskRenderer *renderer)
-{
-  GskRendererClass *renderer_class = GSK_RENDERER_GET_CLASS (renderer);
-  GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
-
-  if (priv->auto_clear)
-    renderer_class->clear (renderer);
-}
-
 /**
  * gsk_renderer_render:
- * @renderer: a#GskRenderer
+ * @renderer: a #GskRenderer
+ * @root: a #GskRenderNode
+ * @context: a #GdkDrawingContext
+ *
+ * Renders the scene graph, described by a tree of #GskRenderNode instances,
+ * using the given #GdkDrawingContext.
  *
- * Renders the scene graph associated to @renderer, using the
- * given target surface.
+ * The @renderer will acquire a reference on the #GskRenderNode tree while
+ * the rendering is in progress, and will make the tree immutable.
  *
  * Since: 3.22
  */
 void
-gsk_renderer_render (GskRenderer *renderer)
+gsk_renderer_render (GskRenderer       *renderer,
+                     GskRenderNode     *root,
+                     GdkDrawingContext *context)
 {
   GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
 
   g_return_if_fail (GSK_IS_RENDERER (renderer));
   g_return_if_fail (priv->is_realized);
-  g_return_if_fail (priv->root_node != NULL);
-
-  /* We need to update the viewport and the modelview, to allow renderers
-   * to update their clip region and/or frustum; this allows them to cull
-   * render nodes in the tree validation phase
-   */
-  gsk_renderer_maybe_resize_viewport (renderer);
+  g_return_if_fail (GSK_IS_RENDER_NODE (root));
+  g_return_if_fail (GDK_IS_DRAWING_CONTEXT (context));
 
-  gsk_renderer_maybe_update (renderer);
+  g_set_object (&priv->root_node, root);
+  g_set_object (&priv->drawing_context, context);
 
-  gsk_renderer_maybe_validate_tree (renderer);
+  gsk_render_node_make_immutable (root);
 
-  /* Clear the output surface */
-  gsk_renderer_maybe_clear (renderer);
+  GSK_RENDERER_GET_CLASS (renderer)->render (renderer, root, context);
 
-  GSK_RENDERER_GET_CLASS (renderer)->render (renderer);
+  g_clear_object (&priv->root_node);
+  g_clear_object (&priv->drawing_context);
 }
 
 /**
@@ -1345,7 +1050,7 @@ gsk_renderer_get_use_alpha (GskRenderer *renderer)
  *
  * Creates an appropriate #GskRenderer instance for the given @display.
  *
- * Returns: (transfer full): a #GskRenderer
+ * Returns: (transfer full) (nullable): a #GskRenderer
  *
  * Since: 3.22
  */
@@ -1364,10 +1069,7 @@ gsk_renderer_get_for_display (GdkDisplay *display)
     }
 
   if (use_software[0] != '0')
-    {
-      renderer_type = GSK_TYPE_CAIRO_RENDERER;
-      goto out;
-    }
+    return NULL;
 
 #ifdef GDK_WINDOWING_X11
   if (GDK_IS_X11_DISPLAY (display))
@@ -1377,9 +1079,9 @@ gsk_renderer_get_for_display (GdkDisplay *display)
 #ifdef GDK_WINDOWING_WAYLAND
   if (GDK_IS_WAYLAND_DISPLAY (display))
     renderer_type = GSK_TYPE_GL_RENDERER;
-  else
 #endif
-    renderer_type = GSK_TYPE_CAIRO_RENDERER;
+  else
+    return NULL;
 
   GSK_NOTE (RENDERER, g_print ("Creating renderer of type '%s' for display '%s'\n",
                                g_type_name (renderer_type),
@@ -1387,6 +1089,5 @@ gsk_renderer_get_for_display (GdkDisplay *display)
 
   g_assert (renderer_type != G_TYPE_INVALID);
 
-out:
   return g_object_new (renderer_type, "display", display, NULL);
 }
diff --git a/gsk/gskrenderer.h b/gsk/gskrenderer.h
index a18ab9b..ec1d3aa 100644
--- a/gsk/gskrenderer.h
+++ b/gsk/gskrenderer.h
@@ -69,44 +69,37 @@ void                    gsk_renderer_get_scaling_filters        (GskRenderer
                                                                  GskScalingFilter        *min_filter,
                                                                  GskScalingFilter        *mag_filter);
 GDK_AVAILABLE_IN_3_22
+void                    gsk_renderer_set_scale_factor           (GskRenderer             *renderer,
+                                                                 int                      scale_factor);
+GDK_AVAILABLE_IN_3_22
+int                     gsk_renderer_get_scale_factor           (GskRenderer             *renderer);
+GDK_AVAILABLE_IN_3_22
 void                    gsk_renderer_set_auto_clear             (GskRenderer             *renderer,
                                                                  gboolean                 clear);
 GDK_AVAILABLE_IN_3_22
 gboolean                gsk_renderer_get_auto_clear             (GskRenderer             *renderer);
 GDK_AVAILABLE_IN_3_22
-void                    gsk_renderer_set_root_node              (GskRenderer             *renderer,
-                                                                 GskRenderNode           *root);
-GDK_AVAILABLE_IN_3_22
-GskRenderNode *         gsk_renderer_get_root_node              (GskRenderer             *renderer);
-GDK_AVAILABLE_IN_3_22
-void                    gsk_renderer_set_surface                (GskRenderer             *renderer,
-                                                                 cairo_surface_t         *surface);
+void                    gsk_renderer_set_use_alpha              (GskRenderer             *renderer,
+                                                                 gboolean                 use_alpha);
 GDK_AVAILABLE_IN_3_22
-cairo_surface_t *       gsk_renderer_get_surface                (GskRenderer             *renderer);
+gboolean                gsk_renderer_get_use_alpha              (GskRenderer             *renderer);
 GDK_AVAILABLE_IN_3_22
 void                    gsk_renderer_set_window                 (GskRenderer             *renderer,
                                                                  GdkWindow               *window);
 GDK_AVAILABLE_IN_3_22
 GdkWindow *             gsk_renderer_get_window                 (GskRenderer             *renderer);
-GDK_AVAILABLE_IN_3_22
-void                    gsk_renderer_set_draw_context           (GskRenderer             *renderer,
-                                                                 cairo_t                 *cr);
-GDK_AVAILABLE_IN_3_22
-cairo_t *               gsk_renderer_get_draw_context           (GskRenderer             *renderer);
+
 GDK_AVAILABLE_IN_3_22
 GdkDisplay *            gsk_renderer_get_display                (GskRenderer             *renderer);
-GDK_AVAILABLE_IN_3_22
-void                    gsk_renderer_set_use_alpha              (GskRenderer             *renderer,
-                                                                 gboolean                 use_alpha);
-GDK_AVAILABLE_IN_3_22
-gboolean                gsk_renderer_get_use_alpha              (GskRenderer             *renderer);
 
 GDK_AVAILABLE_IN_3_22
 gboolean                gsk_renderer_realize                    (GskRenderer             *renderer);
 GDK_AVAILABLE_IN_3_22
 void                    gsk_renderer_unrealize                  (GskRenderer             *renderer);
 GDK_AVAILABLE_IN_3_22
-void                    gsk_renderer_render                     (GskRenderer             *renderer);
+void                    gsk_renderer_render                     (GskRenderer             *renderer,
+                                                                 GskRenderNode           *root,
+                                                                 GdkDrawingContext       *context);
 
 G_END_DECLS
 
diff --git a/gsk/gskrendererprivate.h b/gsk/gskrendererprivate.h
index 2237c86..8103719 100644
--- a/gsk/gskrendererprivate.h
+++ b/gsk/gskrendererprivate.h
@@ -39,28 +39,15 @@ struct _GskRendererClass
   gboolean (* realize) (GskRenderer *renderer);
   void (* unrealize) (GskRenderer *renderer);
 
-  void (* resize_viewport) (GskRenderer *renderer,
-                            const graphene_rect_t *viewport);
-  void (* update) (GskRenderer *renderer,
-                   const graphene_matrix_t *modelview,
-                   const graphene_matrix_t *projection);
-  void (* validate_tree) (GskRenderer *renderer,
-                          GskRenderNode *root);
-  void (* clear_tree) (GskRenderer *renderer,
-                       GskRenderNode *old_root);
-  void (* clear) (GskRenderer *renderer);
-  void (* render) (GskRenderer *renderer);
+  void (* render) (GskRenderer *renderer,
+                   GskRenderNode *root,
+                   GdkDrawingContext *context);
 };
 
 gboolean gsk_renderer_is_realized (GskRenderer *renderer);
 
-void gsk_renderer_clear_tree (GskRenderer *renderer,
-                              GskRenderNode *old_root);
-
-void gsk_renderer_maybe_resize_viewport (GskRenderer *renderer);
-void gsk_renderer_maybe_update (GskRenderer *renderer);
-void gsk_renderer_maybe_validate_tree (GskRenderer *renderer);
-void gsk_renderer_maybe_clear (GskRenderer *renderer);
+GskRenderNode *         gsk_renderer_get_root_node              (GskRenderer *renderer);
+GdkDrawingContext *     gsk_renderer_get_drawing_context        (GskRenderer *renderer);
 
 G_END_DECLS
 
diff --git a/gsk/gskrendernode.c b/gsk/gskrendernode.c
index 092b96a..95feb24 100644
--- a/gsk/gskrendernode.c
+++ b/gsk/gskrendernode.c
@@ -41,8 +41,6 @@ gsk_render_node_dispose (GObject *gobject)
   GskRenderNode *self = GSK_RENDER_NODE (gobject);
   GskRenderNodeIter iter;
 
-  gsk_render_node_set_invalidate_func (self, NULL, NULL, NULL);
-
   gsk_render_node_iter_init (&iter, self);
   while (gsk_render_node_iter_next (&iter, NULL))
     gsk_render_node_iter_remove (&iter);
@@ -51,18 +49,11 @@ gsk_render_node_dispose (GObject *gobject)
 }
 
 static void
-gsk_render_node_real_resize (GskRenderNode *node)
-{
-}
-
-static void
 gsk_render_node_class_init (GskRenderNodeClass *klass)
 {
   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
   gobject_class->dispose = gsk_render_node_dispose;
-
-  klass->resize = gsk_render_node_real_resize;
 }
 
 static void
@@ -74,6 +65,8 @@ gsk_render_node_init (GskRenderNode *self)
   graphene_matrix_init_identity (&self->child_transform);
 
   self->opacity = 1.0;
+
+  self->is_mutable = TRUE;
 }
 
 /**
@@ -204,6 +197,13 @@ gsk_render_node_insert_child_internal (GskRenderNode   *node,
       return;
     }
 
+  if (!node->is_mutable)
+    {
+      g_critical ("The render node of type '%s' is immutable.",
+                  G_OBJECT_TYPE_NAME (node));
+      return;
+    }
+
   insert_func (node, child, insert_func_data);
 
   g_object_ref (child);
@@ -216,21 +216,6 @@ gsk_render_node_insert_child_internal (GskRenderNode   *node,
   node->age += 1;
   node->needs_world_matrix_update = TRUE;
 
-  /* Transfer invalidated children to the current top-level */
-  if (child->invalidated_descendants != NULL)
-    {
-      if (node->parent == NULL)
-        node->invalidated_descendants = child->invalidated_descendants;
-      else
-        {
-          GskRenderNode *tmp = gsk_render_node_get_toplevel (node);
-
-          tmp->invalidated_descendants = child->invalidated_descendants;
-        }
-
-      child->invalidated_descendants = NULL;
-    }
-
   if (child->prev_sibling == NULL)
     node->first_child = child;
   if (child->next_sibling == NULL)
@@ -297,6 +282,62 @@ insert_child_at_pos (GskRenderNode *node,
 }
 
 /**
+ * gsk_render_node_append_child:
+ * @node: a #GskRenderNode
+ * @child: a #GskRenderNode
+ *
+ * Appends @child to the list of children of @node.
+ *
+ * This function acquires a reference on @child.
+ *
+ * Returns: (transfer none): the #GskRenderNode
+ *
+ * Since: 3.22
+ */
+GskRenderNode *
+gsk_render_node_append_child (GskRenderNode *node,
+                              GskRenderNode *child)
+{
+  g_return_val_if_fail (GSK_IS_RENDER_NODE (node), NULL);
+  g_return_val_if_fail (GSK_IS_RENDER_NODE (child), node);
+  g_return_val_if_fail (node->is_mutable, node);
+
+  gsk_render_node_insert_child_internal (node, child,
+                                        insert_child_at_pos,
+                                        GINT_TO_POINTER (node->n_children));
+
+  return node;
+}
+
+/**
+ * gsk_render_node_prepend_child:
+ * @node: a #GskRenderNode
+ * @child: a #GskRenderNode
+ *
+ * Prepends @child to the list of children of @node.
+ *
+ * This function acquires a reference on @child.
+ *
+ * Returns: (transfer none): the #GskRenderNode
+ *
+ * Since: 3.22
+ */
+GskRenderNode *
+gsk_render_node_prepend_child (GskRenderNode *node,
+                               GskRenderNode *child)
+{
+  g_return_val_if_fail (GSK_IS_RENDER_NODE (node), NULL);
+  g_return_val_if_fail (GSK_IS_RENDER_NODE (child), node);
+  g_return_val_if_fail (node->is_mutable, node);
+
+  gsk_render_node_insert_child_internal (node, child,
+                                         insert_child_at_pos,
+                                         GINT_TO_POINTER (0));
+
+  return node;
+}
+
+/**
  * gsk_render_node_insert_child_at_pos:
  * @node: a #GskRenderNode
  * @child: a #GskRenderNode
@@ -322,6 +363,7 @@ gsk_render_node_insert_child_at_pos (GskRenderNode *node,
 {
   g_return_val_if_fail (GSK_IS_RENDER_NODE (node), NULL);
   g_return_val_if_fail (GSK_IS_RENDER_NODE (child), node);
+  g_return_val_if_fail (node->is_mutable, node);
 
   gsk_render_node_insert_child_internal (node, child,
                                         insert_child_at_pos,
@@ -382,6 +424,7 @@ gsk_render_node_insert_child_before (GskRenderNode *node,
   g_return_val_if_fail (GSK_IS_RENDER_NODE (node), NULL);
   g_return_val_if_fail (GSK_IS_RENDER_NODE (child), node);
   g_return_val_if_fail (sibling == NULL || GSK_IS_RENDER_NODE (sibling), node);
+  g_return_val_if_fail (node->is_mutable, node);
 
   gsk_render_node_insert_child_internal (node, child, insert_child_before, sibling);
 
@@ -440,6 +483,7 @@ gsk_render_node_insert_child_after (GskRenderNode *node,
   g_return_val_if_fail (GSK_IS_RENDER_NODE (node), NULL);
   g_return_val_if_fail (GSK_IS_RENDER_NODE (child), node);
   g_return_val_if_fail (sibling == NULL || GSK_IS_RENDER_NODE (sibling), node);
+  g_return_val_if_fail (node->is_mutable, node);
 
   if (sibling != NULL)
     g_return_val_if_fail (sibling->parent == node, node);
@@ -500,6 +544,8 @@ gsk_render_node_replace_child (GskRenderNode *node,
   g_return_val_if_fail (new_child->parent == NULL, node);
   g_return_val_if_fail (old_child->parent == node, node);
 
+  g_return_val_if_fail (node->is_mutable, node);
+
   clos.prev_sibling = old_child->prev_sibling;
   clos.next_sibling = old_child->next_sibling;
   gsk_render_node_remove_child (node, old_child);
@@ -529,6 +575,7 @@ gsk_render_node_remove_child (GskRenderNode *node,
 
   g_return_val_if_fail (GSK_IS_RENDER_NODE (node), NULL);
   g_return_val_if_fail (GSK_IS_RENDER_NODE (child), node);
+  g_return_val_if_fail (node->is_mutable, node);
 
   if (child->parent != node)
     {
@@ -582,6 +629,7 @@ gsk_render_node_remove_all_children (GskRenderNode *node)
   GskRenderNodeIter iter;
 
   g_return_val_if_fail (GSK_IS_RENDER_NODE (node), NULL);
+  g_return_val_if_fail (node->is_mutable, node);
 
   if (node->n_children == 0)
     return node;
@@ -631,13 +679,12 @@ gsk_render_node_set_bounds (GskRenderNode         *node,
                             const graphene_rect_t *bounds)
 {
   g_return_if_fail (GSK_IS_RENDER_NODE (node));
+  g_return_if_fail (node->is_mutable);
 
   if (bounds == NULL)
     graphene_rect_init_from_rect (&node->bounds, graphene_rect_zero ());
   else
     graphene_rect_init_from_rect (&node->bounds, bounds);
-
-  gsk_render_node_queue_invalidate (node, GSK_RENDER_NODE_CHANGES_UPDATE_BOUNDS);
 }
 
 /**
@@ -673,6 +720,7 @@ gsk_render_node_set_transform (GskRenderNode           *node,
                                const graphene_matrix_t *transform)
 {
   g_return_if_fail (GSK_IS_RENDER_NODE (node));
+  g_return_if_fail (node->is_mutable);
 
   if (transform == NULL)
     graphene_matrix_init_identity (&node->transform);
@@ -680,7 +728,6 @@ gsk_render_node_set_transform (GskRenderNode           *node,
     graphene_matrix_init_from_matrix (&node->transform, transform);
 
   node->transform_set = !graphene_matrix_is_identity (&node->transform);
-  gsk_render_node_queue_invalidate (node, GSK_RENDER_NODE_CHANGES_UPDATE_TRANSFORM);
 }
 
 /**
@@ -697,10 +744,8 @@ void
 gsk_render_node_set_child_transform (GskRenderNode           *node,
                                      const graphene_matrix_t *transform)
 {
-  GskRenderNodeIter iter;
-  GskRenderNode *child;
-
   g_return_if_fail (GSK_IS_RENDER_NODE (node));
+  g_return_if_fail (node->is_mutable);
 
   if (transform == NULL)
     graphene_matrix_init_identity (&node->child_transform);
@@ -708,11 +753,6 @@ gsk_render_node_set_child_transform (GskRenderNode           *node,
     graphene_matrix_init_from_matrix (&node->child_transform, transform);
 
   node->child_transform_set = !graphene_matrix_is_identity (&node->child_transform);
-
-  /* We need to invalidate the world matrix for our children */
-  gsk_render_node_iter_init (&iter, node);
-  while (gsk_render_node_iter_next (&iter, &child))
-    gsk_render_node_queue_invalidate (child, GSK_RENDER_NODE_CHANGES_UPDATE_TRANSFORM);
 }
 
 /**
@@ -730,10 +770,9 @@ gsk_render_node_set_opacity (GskRenderNode *node,
                              double         opacity)
 {
   g_return_if_fail (GSK_IS_RENDER_NODE (node));
+  g_return_if_fail (node->is_mutable);
 
   node->opacity = CLAMP (opacity, 0.0, 1.0);
-
-  gsk_render_node_queue_invalidate (node, GSK_RENDER_NODE_CHANGES_UPDATE_OPACITY);
 }
 
 /**
@@ -770,10 +809,9 @@ gsk_render_node_set_hidden (GskRenderNode *node,
                             gboolean       hidden)
 {
   g_return_if_fail (GSK_IS_RENDER_NODE (node));
+  g_return_if_fail (node->is_mutable);
 
   node->hidden = !!hidden;
-
-  gsk_render_node_queue_invalidate (node, GSK_RENDER_NODE_CHANGES_UPDATE_VISIBILITY);
 }
 
 /**
@@ -813,10 +851,9 @@ gsk_render_node_set_opaque (GskRenderNode *node,
                             gboolean       opaque)
 {
   g_return_if_fail (GSK_IS_RENDER_NODE (node));
+  g_return_if_fail (node->is_mutable);
 
   node->opaque = !!opaque;
-
-  gsk_render_node_queue_invalidate (node, GSK_RENDER_NODE_CHANGES_UPDATE_OPACITY);
 }
 
 /**
@@ -865,31 +902,6 @@ gsk_render_node_contains (GskRenderNode *node,
   return FALSE;
 }
 
-/**
- * gsk_render_node_set_surface:
- * @node: a #GskRenderNode
- * @surface: (nullable): a Cairo surface
- *
- * Sets the contents of the #GskRenderNode.
- *
- * The @node will acquire a reference on the given @surface.
- *
- * Since: 3.22
- */
-void
-gsk_render_node_set_surface (GskRenderNode   *node,
-                             cairo_surface_t *surface)
-{
-  g_return_if_fail (GSK_IS_RENDER_NODE (node));
-
-  g_clear_pointer (&node->surface, cairo_surface_destroy);
-
-  if (surface != NULL)
-    node->surface = cairo_surface_reference (surface);
-
-  gsk_render_node_queue_invalidate (node, GSK_RENDER_NODE_CHANGES_UPDATE_SURFACE);
-}
-
 /*< private >
  * gsk_render_node_get_toplevel:
  * @node: a #GskRenderNode
@@ -1014,168 +1026,6 @@ gsk_render_node_get_world_matrix (GskRenderNode     *node,
   *mv = node->world_matrix;
 }
 
-void
-gsk_render_node_set_invalidate_func (GskRenderNode               *node,
-                                     GskRenderNodeInvalidateFunc  invalidate_func,
-                                     gpointer                     func_data,
-                                     GDestroyNotify               destroy_func_data)
-{
-  if (node->parent != NULL)
-    {
-      g_critical ("Render node of type '%s' is not a root node. Only root "
-                  "nodes can have an invalidation function.",
-                  G_OBJECT_TYPE_NAME (node));
-      return;
-    }
-
-  if (node->invalidate_func != NULL)
-    {
-      if (node->destroy_func_data != NULL)
-        node->destroy_func_data (node->func_data);
-    }
-
-  node->invalidate_func = invalidate_func;
-  node->func_data = func_data;
-  node->destroy_func_data = destroy_func_data;
-}
-
-GskRenderNodeChanges
-gsk_render_node_get_current_state (GskRenderNode *node)
-{
-  GskRenderNodeChanges res = 0;
-
-  if (node->needs_resize)
-    res |= GSK_RENDER_NODE_CHANGES_UPDATE_BOUNDS;
-  if (node->needs_world_matrix_update)
-    res |= GSK_RENDER_NODE_CHANGES_UPDATE_TRANSFORM;
-  if (node->needs_content_update)
-    res |= GSK_RENDER_NODE_CHANGES_UPDATE_SURFACE;
-  if (node->needs_opacity_update)
-    res |= GSK_RENDER_NODE_CHANGES_UPDATE_OPACITY;
-  if (node->needs_visibility_update)
-    res |= GSK_RENDER_NODE_CHANGES_UPDATE_VISIBILITY;
-
-  return res;
-}
-
-GskRenderNodeChanges
-gsk_render_node_get_last_state (GskRenderNode *node)
-{
-  return node->last_state_change;
-}
-
-void
-gsk_render_node_queue_invalidate (GskRenderNode        *node,
-                                  GskRenderNodeChanges  changes)
-{
-  GskRenderNodeChanges cur_invalidated_bits = 0;
-  GskRenderNode *root;
-  int i;
-
-  cur_invalidated_bits = gsk_render_node_get_current_state (node);
-  if ((cur_invalidated_bits & changes) != 0)
-    return;
-
-  node->needs_resize = (changes & GSK_RENDER_NODE_CHANGES_UPDATE_BOUNDS) != 0;
-  node->needs_world_matrix_update = (changes & GSK_RENDER_NODE_CHANGES_UPDATE_TRANSFORM) != 0;
-  node->needs_content_update = (changes & GSK_RENDER_NODE_CHANGES_UPDATE_SURFACE) != 0;
-  node->needs_opacity_update = (changes & GSK_RENDER_NODE_CHANGES_UPDATE_OPACITY) != 0;
-  node->needs_visibility_update = (changes & GSK_RENDER_NODE_CHANGES_UPDATE_VISIBILITY) != 0;
-
-  if (node->parent == NULL)
-    {
-      GSK_NOTE (RENDER_NODE, g_print ("Invalid node [%p] is top-level\n", node));
-      return;
-    }
-
-  root = gsk_render_node_get_toplevel (node);
-
-  if (root->invalidated_descendants == NULL)
-    root->invalidated_descendants = g_ptr_array_new ();
-
-  for (i = 0; i < root->invalidated_descendants->len; i++)
-    {
-      if (node == g_ptr_array_index (root->invalidated_descendants, i))
-        {
-          GSK_NOTE (RENDER_NODE, g_print ("Node [%p] already invalidated; skipping...\n", node));
-          return;
-        }
-    }
-
-  GSK_NOTE (RENDER_NODE, g_print ("Adding node [%p] to list of invalid descendants of [%p]\n", node, root));
-  g_ptr_array_add (root->invalidated_descendants, node);
-}
-
-void
-gsk_render_node_validate (GskRenderNode *node)
-{
-  GPtrArray *invalidated_descendants;
-  gboolean call_invalidate_func;
-  int i;
-
-  node->last_state_change = gsk_render_node_get_current_state (node);
-
-  /* We call the invalidation function if our state changed, or if
-   * the descendants state has changed
-   */
-  call_invalidate_func = node->last_state_change != 0 ||
-                         node->invalidated_descendants != NULL;
-
-  gsk_render_node_maybe_resize (node);
-  gsk_render_node_update_world_matrix (node, FALSE);
-  node->needs_content_update = FALSE;
-  node->needs_visibility_update = FALSE;
-  node->needs_opacity_update = FALSE;
-
-  /* Steal the array of invalidated descendants, so that changes caused by
-   * the validation will not cause recursions
-   */
-  invalidated_descendants = node->invalidated_descendants;
-  node->invalidated_descendants = NULL;
-
-  if (invalidated_descendants != NULL)
-    {
-      for (i = 0; i < invalidated_descendants->len; i++)
-        {
-          GskRenderNode *child = g_ptr_array_index (invalidated_descendants, i);
-
-          child->last_state_change = 0;
-
-          GSK_NOTE (RENDER_NODE, g_print ("Validating descendant node [%p] (resize:%s, transform:%s)\n",
-                                          child,
-                                          child->needs_resize ? "yes" : "no",
-                                          child->needs_world_matrix_update ? "yes" : "no"));
-
-          child->last_state_change = gsk_render_node_get_current_state (child);
-
-          gsk_render_node_maybe_resize (child);
-          gsk_render_node_update_world_matrix (child, FALSE);
-
-          child->needs_content_update = FALSE;
-          child->needs_visibility_update = FALSE;
-          child->needs_opacity_update = FALSE;
-        }
-    }
-
-  g_clear_pointer (&invalidated_descendants, g_ptr_array_unref);
-
-  if (call_invalidate_func && node->invalidate_func != NULL)
-    node->invalidate_func (node, node->func_data);
-}
-
-void
-gsk_render_node_maybe_resize (GskRenderNode *node)
-{
-  g_return_if_fail (GSK_IS_RENDER_NODE (node));
-
-  if (!node->needs_resize)
-    return;
-
-  GSK_RENDER_NODE_GET_CLASS (node)->resize (node);
-
-  node->needs_resize = FALSE;
-}
-
 /**
  * gsk_render_node_set_name:
  * @node: a #GskRenderNode
@@ -1197,23 +1047,12 @@ gsk_render_node_set_name (GskRenderNode *node,
   node->name = g_strdup (name);
 }
 
-static cairo_user_data_key_t render_node_context_key;
-
-static void
-surface_invalidate (void *data)
-{
-  GskRenderNode *node = data;
-
-  gsk_render_node_queue_invalidate (node, GSK_RENDER_NODE_CHANGES_UPDATE_SURFACE);
-}
-
 /**
  * gsk_render_node_get_draw_context:
  * @node: a #GskRenderNode
  *
  * Creates a Cairo context for drawing using the surface associated
- * to the render node. If no surface has been attached to the render
- * node, a new surface will be created as a side effect.
+ * to the render node.
  *
  * Returns: (transfer full): a Cairo context used for drawing; use
  *   cairo_destroy() when done drawing
@@ -1226,6 +1065,7 @@ gsk_render_node_get_draw_context (GskRenderNode *node)
   cairo_t *res;
 
   g_return_val_if_fail (GSK_IS_RENDER_NODE (node), NULL);
+  g_return_val_if_fail (node->is_mutable, NULL);
 
   if (node->surface == NULL)
     node->surface = cairo_image_surface_create (node->opaque ? CAIRO_FORMAT_RGB24
@@ -1240,7 +1080,21 @@ gsk_render_node_get_draw_context (GskRenderNode *node)
                    node->bounds.size.width, node->bounds.size.height);
   cairo_clip (res);
 
-  cairo_set_user_data (res, &render_node_context_key, node, surface_invalidate);
-
   return res;
 }
+
+void
+gsk_render_node_make_immutable (GskRenderNode *node)
+{
+  GskRenderNodeIter iter;
+  GskRenderNode *child;
+
+  if (!node->is_mutable)
+    return;
+
+  node->is_mutable = FALSE;
+
+  gsk_render_node_iter_init (&iter, node);
+  while (gsk_render_node_iter_next (&iter, &child))
+    gsk_render_node_make_immutable (child);
+}
diff --git a/gsk/gskrendernode.h b/gsk/gskrendernode.h
index f729c27..18e56f7 100644
--- a/gsk/gskrendernode.h
+++ b/gsk/gskrendernode.h
@@ -53,6 +53,12 @@ GDK_AVAILABLE_IN_3_22
 GskRenderNode *         gsk_render_node_get_previous_sibling    (GskRenderNode *node);
 
 GDK_AVAILABLE_IN_3_22
+GskRenderNode *         gsk_render_node_append_child            (GskRenderNode *node,
+                                                                 GskRenderNode *child);
+GDK_AVAILABLE_IN_3_22
+GskRenderNode *         gsk_render_node_prepend_child           (GskRenderNode *node,
+                                                                 GskRenderNode *child);
+GDK_AVAILABLE_IN_3_22
 GskRenderNode *         gsk_render_node_insert_child_at_pos     (GskRenderNode *node,
                                                                  GskRenderNode *child,
                                                                  int            index_);
@@ -103,9 +109,6 @@ void                    gsk_render_node_set_opaque              (GskRenderNode *
 GDK_AVAILABLE_IN_3_22
 gboolean                gsk_render_node_is_opaque               (GskRenderNode *node);
 GDK_AVAILABLE_IN_3_22
-void                    gsk_render_node_set_surface             (GskRenderNode   *node,
-                                                                 cairo_surface_t *surface);
-GDK_AVAILABLE_IN_3_22
 cairo_t *               gsk_render_node_get_draw_context        (GskRenderNode   *node);
 
 GDK_AVAILABLE_IN_3_22
diff --git a/gsk/gskrendernodeprivate.h b/gsk/gskrendernodeprivate.h
index 72373d8..9e9188f 100644
--- a/gsk/gskrendernodeprivate.h
+++ b/gsk/gskrendernodeprivate.h
@@ -10,18 +10,6 @@ G_BEGIN_DECLS
 #define GSK_IS_RENDER_NODE_CLASS(klass)         (G_TYPE_CHECK_CLASS_TYPE ((klass), GSK_TYPE_RENDER_NODE))
 #define GSK_RENDER_NODE_GET_CLASS(obj)          (G_TYPE_INSTANCE_GET_CLASS ((obj), GSK_TYPE_RENDER_NODE, 
GskRenderNodeClass))
 
-typedef enum {
-  GSK_RENDER_NODE_CHANGES_UPDATE_BOUNDS = 1 << 0,
-  GSK_RENDER_NODE_CHANGES_UPDATE_TRANSFORM = 1 << 1,
-  GSK_RENDER_NODE_CHANGES_UPDATE_SURFACE = 1 << 2,
-  GSK_RENDER_NODE_CHANGES_UPDATE_OPACITY = 1 << 3,
-  GSK_RENDER_NODE_CHANGES_UPDATE_VISIBILITY = 1 << 4,
-  GSK_RENDER_NODE_CHANEGS_UPDATE_HIERARCHY = 1 << 5
-} GskRenderNodeChanges;
-
-typedef void (* GskRenderNodeInvalidateFunc) (GskRenderNode *node,
-                                              gpointer       data);
-
 struct _GskRenderNode
 {
   GObject parent_instance;
@@ -59,35 +47,22 @@ struct _GskRenderNode
   /* Transformations applied to the children of the node */
   graphene_matrix_t child_transform;
 
-  /* Invalidation function for root node */
-  GskRenderNodeInvalidateFunc invalidate_func;
-  gpointer func_data;
-  GDestroyNotify destroy_func_data;
-
-  /* Descendants that need to be validated; only for root node */
-  GPtrArray *invalidated_descendants;
-
-  GskRenderNodeChanges last_state_change;
-
   /* Bit fields; leave at the end */
+  gboolean is_mutable : 1;
   gboolean hidden : 1;
   gboolean opaque : 1;
   gboolean transform_set : 1;
   gboolean child_transform_set : 1;
-  gboolean needs_resize : 1;
   gboolean needs_world_matrix_update : 1;
-  gboolean needs_content_update : 1;
-  gboolean needs_opacity_update : 1;
-  gboolean needs_visibility_update : 1;
 };
 
 struct _GskRenderNodeClass
 {
   GObjectClass parent_class;
-
-  void (* resize) (GskRenderNode *node);
 };
 
+void gsk_render_node_make_immutable (GskRenderNode *node);
+
 void gsk_render_node_get_bounds (GskRenderNode   *node,
                                  graphene_rect_t *frame);
 void gsk_render_node_get_transform (GskRenderNode     *node,
@@ -104,21 +79,6 @@ void gsk_render_node_update_world_matrix (GskRenderNode *node,
 void gsk_render_node_get_world_matrix (GskRenderNode     *node,
                                        graphene_matrix_t *mv);
 
-void gsk_render_node_queue_invalidate (GskRenderNode        *node,
-                                       GskRenderNodeChanges  changes);
-
-void gsk_render_node_set_invalidate_func (GskRenderNode *root,
-                                          GskRenderNodeInvalidateFunc validate_func,
-                                          gpointer data,
-                                          GDestroyNotify notify);
-
-void gsk_render_node_validate (GskRenderNode *node);
-
-void gsk_render_node_maybe_resize (GskRenderNode *node);
-
-GskRenderNodeChanges gsk_render_node_get_current_state (GskRenderNode *node);
-GskRenderNodeChanges gsk_render_node_get_last_state (GskRenderNode *node);
-
 G_END_DECLS
 
 #endif /* __GSK_RENDER_NODE_PRIVATE_H__ */
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 9d451e2..913c67c 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -176,7 +176,6 @@ noinst_PROGRAMS =  $(TEST_PROGS)    \
        listmodel                       \
        testpopup                       \
        testpopupat                     \
-       testgskrenderer                 \
        $(NULL)
 
 if USE_X11
@@ -305,7 +304,6 @@ testtitlebar_DEPENDENCIES = $(TEST_DEPS)
 testwindowsize_DEPENDENCIES = $(TEST_DEPS)
 listmodel_DEPENDENCIES = $(TEST_DEPS)
 foreigndrawing_DEPENDENCIES = $(TEST_DEPS)
-testgskrenderer_DEPENDENCIES = $(TEST_DEPS)
 
 animated_resizing_SOURCES =    \
        animated-resizing.c     \
@@ -546,8 +544,6 @@ listmodel_SOURCES = listmodel.c
 
 foreigndrawing_SOURCES = foreigndrawing.c
 
-testgskrenderer_SOURCES = testgskrenderer.c
-
 EXTRA_DIST +=                  \
        gradient1.png           \
        testgtk.1               \


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