[gtk/render-node-types: 70/75] Turn GskRenderNode into a derivable type



commit d701a892815453dd38e19062761443eeda54a7c5
Author: Emmanuele Bassi <ebassi gnome org>
Date:   Tue Apr 7 23:33:54 2020 +0100

    Turn GskRenderNode into a derivable type
    
    Language bindings—especially ones based on introspection—cannot deal
    with custom type hiearchies. Luckily for us, GType has a derivable type
    with low overhead: GTypeInstance.
    
    By turning GskRenderNode into a GTypeInstance, and creating derived
    types for each class of node, we can provide an introspectable API to
    our non-C API consumers, with no functional change to the C API itself.

 gsk/broadway/gskbroadwayrenderer.c |    2 +-
 gsk/gl/gskglnodesample.c           |    2 +-
 gsk/gl/gskglrenderer.c             |   47 +-
 gsk/gskenums.h                     |    4 +-
 gsk/gskrendernode.c                |  316 ++++++-
 gsk/gskrendernode.h                |   55 +-
 gsk/gskrendernodeimpl.c            | 1720 +++++++++++++++++++++++-------------
 gsk/gskrendernodeparser.c          |   10 +-
 gsk/gskrendernodeprivate.h         |   74 +-
 gsk/vulkan/gskvulkanrenderpass.c   |   12 +-
 gtk/gtkmain.c                      |    2 +
 gtk/inspector/recorder.c           |    4 +-
 12 files changed, 1574 insertions(+), 674 deletions(-)
---
diff --git a/gsk/broadway/gskbroadwayrenderer.c b/gsk/broadway/gskbroadwayrenderer.c
index cb46f92bc2..89def7e807 100644
--- a/gsk/broadway/gskbroadwayrenderer.c
+++ b/gsk/broadway/gskbroadwayrenderer.c
@@ -595,7 +595,7 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer,
           n = gsk_linear_gradient_node_get_n_color_stops (node);
           add_uint32 (nodes, n);
           for (i = 0; i < n; i++)
-            add_color_stop (nodes, &gsk_linear_gradient_node_peek_color_stops (node)[i]);
+            add_color_stop (nodes, &gsk_linear_gradient_node_peek_color_stops (node, NULL)[i]);
         }
       return;
 
diff --git a/gsk/gl/gskglnodesample.c b/gsk/gl/gskglnodesample.c
index 1ee64c7790..cf1afa5a68 100644
--- a/gsk/gl/gskglnodesample.c
+++ b/gsk/gl/gskglnodesample.c
@@ -25,7 +25,7 @@ node_sample_add (NodeSample    *self,
   g_assert (node_type <= N_NODE_TYPES);
 
   if (self->nodes[node_type].class_name == NULL)
-    self->nodes[node_type].class_name = node->node_class->type_name;
+    self->nodes[node_type].class_name = g_type_name_from_instance ((GTypeInstance *) node); 
 
   self->nodes[node_type].count ++;
   self->count ++;
diff --git a/gsk/gl/gskglrenderer.c b/gsk/gl/gskglrenderer.c
index f98527fb98..a706f0c18e 100644
--- a/gsk/gl/gskglrenderer.c
+++ b/gsk/gl/gskglrenderer.c
@@ -150,7 +150,7 @@ print_render_node_tree (GskRenderNode *root, int level)
         break;
 
       default:
-        g_print ("%*s %s\n", level * INDENT, " ", root->node_class->type_name);
+        g_print ("%*s %s\n", level * INDENT, " ", g_type_name_from_instance ((GTypeInstance *) root));
     }
 
 #undef INDENT
@@ -623,7 +623,9 @@ render_fallback_node (GskGLRenderer   *self,
                                            surface,
                                            GL_NEAREST, GL_NEAREST);
   gdk_gl_context_label_object_printf  (self->gl_context, GL_TEXTURE, texture_id,
-                                       "Fallback %s %d", node->node_class->type_name, texture_id);
+                                       "Fallback %s %d",
+                                       g_type_name_from_instance ((GTypeInstance *) node),
+                                       texture_id);
 
   cairo_surface_destroy (surface);
   cairo_surface_destroy (rendered_surface);
@@ -643,7 +645,7 @@ render_text_node (GskGLRenderer   *self,
                   gboolean         force_color)
 {
   const PangoFont *font = gsk_text_node_peek_font (node);
-  const PangoGlyphInfo *glyphs = gsk_text_node_peek_glyphs (node);
+  const PangoGlyphInfo *glyphs = gsk_text_node_peek_glyphs (node, NULL);
   const float text_scale = ops_get_scale (builder);
   const graphene_point_t *offset = gsk_text_node_get_offset (node);
   const guint num_glyphs = gsk_text_node_get_num_glyphs (node);
@@ -1106,7 +1108,7 @@ render_linear_gradient_node (GskGLRenderer   *self,
                              RenderOpBuilder *builder)
 {
   const int n_color_stops = MIN (8, gsk_linear_gradient_node_get_n_color_stops (node));
-  const GskColorStop *stops = gsk_linear_gradient_node_peek_color_stops (node);
+  const GskColorStop *stops = gsk_linear_gradient_node_peek_color_stops (node, NULL);
   const graphene_point_t *start = gsk_linear_gradient_node_peek_start (node);
   const graphene_point_t *end = gsk_linear_gradient_node_peek_end (node);
   OpLinearGradient *op;
@@ -3159,9 +3161,13 @@ add_offscreen_ops (GskGLRenderer         *self,
 
   gsk_gl_driver_create_render_target (self->gl_driver, width, height, &texture_id, &render_target);
   gdk_gl_context_label_object_printf (self->gl_context, GL_TEXTURE, texture_id,
-                                      "Offscreen<%s> %d", child_node->node_class->type_name, texture_id);
-  gdk_gl_context_label_object_printf  (self->gl_context, GL_FRAMEBUFFER, render_target,
-                                       "Offscreen<%s> FB %d", child_node->node_class->type_name, 
render_target);
+                                      "Offscreen<%s> %d",
+                                      g_type_name_from_instance ((GTypeInstance *) child_node),
+                                      texture_id);
+  gdk_gl_context_label_object_printf (self->gl_context, GL_FRAMEBUFFER, render_target,
+                                      "Offscreen<%s> FB %d",
+                                      g_type_name_from_instance ((GTypeInstance *) child_node),
+                                      render_target);
 
   graphene_matrix_init_ortho (&item_proj,
                               bounds->origin.x * scale,
@@ -3209,10 +3215,11 @@ add_offscreen_ops (GskGLRenderer         *self,
   if (G_UNLIKELY (flags & DUMP_FRAMEBUFFER))
     {
       static int k;
-      ops_dump_framebuffer (builder, g_strdup_printf ("%s_%p_%d.png",
-                                                      child_node->node_class->type_name,
-                                                      child_node,
-                                                      k ++),
+      ops_dump_framebuffer (builder,
+                            g_strdup_printf ("%s_%p_%d.png",
+                                             g_type_name_from_instance ((GTypeInstance *) child_node),
+                                             child_node,
+                                             k ++),
                             width, height);
     }
 #endif
@@ -3583,7 +3590,9 @@ gsk_gl_renderer_render_texture (GskRenderer           *renderer,
 
   gdk_gl_context_make_current (self->gl_context);
   gdk_gl_context_push_debug_group_printf (self->gl_context,
-                                          "Render %s<%p> to texture", root->node_class->type_name, root);
+                                          "Render %s<%p> to texture",
+                                          g_type_name_from_instance ((GTypeInstance *) root),
+                                          root);
 
   width = ceilf (viewport->size.width);
   height = ceilf (viewport->size.height);
@@ -3595,8 +3604,11 @@ gsk_gl_renderer_render_texture (GskRenderer           *renderer,
   glGenTextures (1, &texture_id);
   glBindTexture (GL_TEXTURE_2D, texture_id);
 
-  gdk_gl_context_label_object_printf  (self->gl_context, GL_TEXTURE, texture_id,
-                                       "Texture %s<%p> %d", root->node_class->type_name, root, texture_id);
+  gdk_gl_context_label_object_printf (self->gl_context, GL_TEXTURE, texture_id,
+                                      "Texture %s<%p> %d",
+                                      g_type_name_from_instance ((GTypeInstance *) root),
+                                      root,
+                                      texture_id);
 
   if (gdk_gl_context_get_use_es (self->gl_context))
     glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
@@ -3605,8 +3617,11 @@ gsk_gl_renderer_render_texture (GskRenderer           *renderer,
 
   glGenFramebuffers (1, &fbo_id);
   glBindFramebuffer (GL_FRAMEBUFFER, fbo_id);
-  gdk_gl_context_label_object_printf  (self->gl_context, GL_FRAMEBUFFER, fbo_id,
-                                       "FB %s<%p> %d", root->node_class->type_name, root, fbo_id);
+  gdk_gl_context_label_object_printf (self->gl_context, GL_FRAMEBUFFER, fbo_id,
+                                      "FB %s<%p> %d",
+                                      g_type_name_from_instance ((GTypeInstance *) root),
+                                      root,
+                                      fbo_id);
   glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture_id, 0);
   g_assert_cmphex (glCheckFramebufferStatus (GL_FRAMEBUFFER), ==, GL_FRAMEBUFFER_COMPLETE);
 
diff --git a/gsk/gskenums.h b/gsk/gskenums.h
index 1851918bd1..d5fcb8ec55 100644
--- a/gsk/gskenums.h
+++ b/gsk/gskenums.h
@@ -46,9 +46,9 @@
  * @GSK_TEXT_NODE: A node containing a glyph string
  * @GSK_BLUR_NODE: A node that applies a blur
  * @GSK_DEBUG_NODE: Debug information that does not affect the rendering
- *
+ 
  * The type of a node determines what the node is rendering.
- **/
+ */
 typedef enum {
   GSK_NOT_A_RENDER_NODE = 0,
   GSK_CONTAINER_NODE,
diff --git a/gsk/gskrendernode.c b/gsk/gskrendernode.c
index fb80a7c3a0..2a614fd9ca 100644
--- a/gsk/gskrendernode.c
+++ b/gsk/gskrendernode.c
@@ -50,47 +50,297 @@
 
 #include <gobject/gvaluecollector.h>
 
+G_DEFINE_QUARK (gsk-serialization-error-quark, gsk_serialization_error)
+
+#define GSK_RENDER_NODE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GSK_TYPE_RENDER_NODE, 
GskRenderNodeClass))
+
 /**
  * GskRenderNode: (ref-func gsk_render_node_ref) (unref-func gsk_render_node_unref)
  *
- * The `GskRenderNode` structure contains only private data.
+ * A node in the render tree.
  */
 
-G_DEFINE_BOXED_TYPE (GskRenderNode, gsk_render_node,
-                     gsk_render_node_ref,
-                     gsk_render_node_unref)
+static void
+value_render_node_init (GValue *value)
+{
+  value->data[0].v_pointer = NULL;
+}
+ 
+static void
+value_render_node_free_value (GValue *value)
+{
+  if (value->data[0].v_pointer != NULL)
+    gsk_render_node_unref (value->data[0].v_pointer);
+}
+ 
+static void
+value_render_node_copy_value (const GValue *src,
+                              GValue       *dst)
+{
+  if (src->data[0].v_pointer != NULL)
+    dst->data[0].v_pointer = gsk_render_node_ref (src->data[0].v_pointer);
+  else
+    dst->data[0].v_pointer = NULL;
+}
+ 
+static gpointer
+value_render_node_peek_pointer (const GValue *value)
+{
+  return value->data[0].v_pointer;
+}
 
-G_DEFINE_QUARK (gsk-serialization-error-quark, gsk_serialization_error)
+static char *
+value_render_node_collect_value (GValue      *value,
+                                 guint        n_collect_values,
+                                 GTypeCValue *collect_values,
+                                 guint        collect_flags)
+{
+  GskRenderNode *node = collect_values[0].v_pointer;
+
+  if (node == NULL)
+    {
+      value->data[0].v_pointer = NULL;
+      return NULL;
+    }
+
+  if (node->parent_instance.g_class == NULL)
+    return g_strconcat ("invalid unclassed GskRenderNode pointer for "
+                        "value type '",
+                        G_VALUE_TYPE_NAME (value),
+                        "'",
+                        NULL);
+
+  value->data[0].v_pointer = gsk_render_node_ref (node);
+
+  return NULL;
+}
+
+static gchar *
+value_render_node_lcopy_value (const GValue *value,
+                               guint         n_collect_values,
+                               GTypeCValue  *collect_values,
+                               guint         collect_flags)
+{
+  GskRenderNode **node_p = collect_values[0].v_pointer;
+
+  if (node_p == NULL)
+    return g_strconcat ("value location for '",
+                        G_VALUE_TYPE_NAME (value),
+                        "' passed as NULL",
+                        NULL);
+
+  if (value->data[0].v_pointer == NULL)
+    *node_p = NULL;
+  else if (collect_flags & G_VALUE_NOCOPY_CONTENTS)
+    *node_p = value->data[0].v_pointer;
+  else
+    *node_p = gsk_render_node_ref (value->data[0].v_pointer);
+
+  return NULL;
+}
 
 static void
 gsk_render_node_finalize (GskRenderNode *self)
 {
-  self->node_class->finalize (self);
+  g_type_free_instance ((GTypeInstance *) self);
+}
 
-  g_free (self);
+static void
+gsk_render_node_real_draw (GskRenderNode *node,
+                           cairo_t       *cr)
+{
 }
 
-/*< private >
- * gsk_render_node_new:
- * @node_class: class structure for this node
- *
- * Returns: (transfer full): the newly created #GskRenderNode
- */
-GskRenderNode *
-gsk_render_node_new (const GskRenderNodeClass *node_class, gsize extra_size)
+static gboolean
+gsk_render_node_real_can_diff (const GskRenderNode *node1,
+                               const GskRenderNode *node2)
+{
+  return FALSE;
+}
+
+static void
+gsk_render_node_real_diff (GskRenderNode  *node1,
+                           GskRenderNode  *node2,
+                           cairo_region_t *region)
+{
+}
+
+static void
+gsk_render_node_class_init (GskRenderNodeClass *klass)
+{
+  klass->node_type = GSK_NOT_A_RENDER_NODE;
+  klass->finalize = gsk_render_node_finalize;
+  klass->draw = gsk_render_node_real_draw;
+  klass->can_diff = gsk_render_node_real_can_diff;
+  klass->diff = gsk_render_node_real_diff;
+}
+
+static void
+gsk_render_node_init (GskRenderNode *self)
+{
+  g_atomic_ref_count_init (&self->ref_count);
+}
+
+GType
+gsk_render_node_get_type (void)
+{
+  static volatile gsize render_node_type__volatile;
+
+  if (g_once_init_enter (&render_node_type__volatile))
+    {
+      static const GTypeFundamentalInfo finfo = {
+        (G_TYPE_FLAG_CLASSED |
+         G_TYPE_FLAG_INSTANTIATABLE |
+         G_TYPE_FLAG_DERIVABLE |
+         G_TYPE_FLAG_DEEP_DERIVABLE),
+      };
+
+      static const GTypeValueTable value_table = {
+        value_render_node_init,
+        value_render_node_free_value,
+        value_render_node_copy_value,
+        value_render_node_peek_pointer,
+        "p",
+        value_render_node_collect_value,
+        "p",
+        value_render_node_lcopy_value,
+      };
+
+      const GTypeInfo node_info = {
+        /* Class */
+        sizeof (GskRenderNodeClass),
+        (GBaseInitFunc) NULL,
+        (GBaseFinalizeFunc) NULL,
+        (GClassInitFunc) gsk_render_node_class_init,
+        (GClassFinalizeFunc) NULL,
+        NULL,
+
+        /* Instance */
+        sizeof (GskRenderNode),
+        0,
+        (GInstanceInitFunc) gsk_render_node_init,
+
+        /* GValue */
+        &value_table,
+      };
+
+      GType render_node_type =
+        g_type_register_fundamental (g_type_fundamental_next (),
+                                     g_intern_static_string ("GskRenderNode"),
+                                     &node_info, &finfo,
+                                     G_TYPE_FLAG_ABSTRACT);
+
+      g_once_init_leave (&render_node_type__volatile, render_node_type);
+    }
+
+  return render_node_type__volatile;
+}
+
+typedef struct
+{
+  GskRenderNodeType node_type;
+
+  void     (* finalize) (GskRenderNode        *node);
+  void     (* draw)     (GskRenderNode        *node,
+                         cairo_t              *cr);
+  gboolean (* can_diff) (const GskRenderNode  *node1,
+                         const GskRenderNode  *node2);
+  void     (* diff)     (GskRenderNode        *node1,
+                         GskRenderNode        *node2,
+                         cairo_region_t       *region);
+} RenderNodeClassData;
+
+static void
+gsk_render_node_generic_class_init (gpointer g_class,
+                                    gpointer class_data)
 {
-  GskRenderNode *self;
+  GskRenderNodeClass *node_class = g_class;
+  RenderNodeClassData *node_data = class_data;
+
+  /* Mandatory */
+  node_class->node_type = node_data->node_type;
 
-  g_return_val_if_fail (node_class != NULL, NULL);
-  g_return_val_if_fail (node_class->node_type != GSK_NOT_A_RENDER_NODE, NULL);
+  /* Optional */
+  if (node_data->finalize != NULL)
+    node_class->finalize = node_data->finalize;
+  if (node_data->can_diff != NULL)
+    node_class->can_diff = node_data->can_diff;
 
-  self = g_malloc0 (node_class->struct_size + extra_size);
+  /* Mandatory */
+  node_class->draw = node_data->draw;
+  node_class->diff = node_data->diff;
 
-  self->node_class = node_class;
+  g_free (node_data);
+}
 
-  self->ref_count = 1;
+static gboolean
+gsk_render_node_can_diff_true (const GskRenderNode *node1,
+                               const GskRenderNode *node2)
+{
+  return TRUE;
+}
 
-  return self;
+/*< private >
+ * gsk_render_node_type_register_static:
+ * @node_name: the name of the node
+ * @node_info: type information of the node
+ *
+ * Registers a new #GskRenderNode type for the given @node_name using
+ * the type information in @node_info.
+ *
+ * Returns: the newly registered GType
+ */
+GType
+gsk_render_node_type_register_static (const char                  *node_name,
+                                      const GskRenderNodeTypeInfo *node_info)
+{
+  GTypeInfo info;
+
+  info.class_size = sizeof (GskRenderNodeClass);
+  info.base_init = NULL;
+  info.base_finalize = NULL;
+  info.class_init = gsk_render_node_generic_class_init;
+  info.class_finalize = NULL;
+
+  /* Avoid having a class_init() and a class struct for every GskRenderNode,
+   * by passing the various virtual functions and class data when initializing
+   * the base class
+   */
+  info.class_data = g_new (RenderNodeClassData, 1);
+  ((RenderNodeClassData *) info.class_data)->node_type = node_info->node_type;
+  ((RenderNodeClassData *) info.class_data)->finalize = node_info->finalize;
+  ((RenderNodeClassData *) info.class_data)->draw = node_info->draw;
+  ((RenderNodeClassData *) info.class_data)->can_diff = node_info->can_diff != NULL
+                                                      ? node_info->can_diff
+                                                      : gsk_render_node_can_diff_true;
+  ((RenderNodeClassData *) info.class_data)->diff = node_info->diff != NULL
+                                                  ? node_info->diff
+                                                  : gsk_render_node_diff_impossible;
+
+  info.instance_size = node_info->instance_size;
+  info.n_preallocs = 0;
+  info.instance_init = (GInstanceInitFunc) node_info->instance_init;
+  info.value_table = NULL;
+
+  return g_type_register_static (GSK_TYPE_RENDER_NODE, node_name, &info, 0);
+}
+
+/*< private >
+ * gsk_render_node_alloc:
+ * @node_type: the #GskRenderNodeType to instantiate
+ *
+ * Instantiates a new #GskRenderNode for the given @node_type.
+ *
+ * Returns: (transfer full) (type GskRenderNode): the newly created #GskRenderNode
+ */
+gpointer
+gsk_render_node_alloc (GskRenderNodeType node_type)
+{
+  g_return_val_if_fail (node_type > GSK_NOT_A_RENDER_NODE, NULL);
+  g_return_val_if_fail (node_type < GSK_RENDER_NODE_TYPE_N_TYPES, NULL);
+
+  g_assert (gsk_render_node_types[node_type] != G_TYPE_INVALID);
+  return g_type_create_instance (gsk_render_node_types[node_type]);
 }
 
 /**
@@ -106,7 +356,7 @@ gsk_render_node_ref (GskRenderNode *node)
 {
   g_return_val_if_fail (GSK_IS_RENDER_NODE (node), NULL);
 
-  g_atomic_int_inc (&node->ref_count);
+  g_atomic_ref_count_inc (&node->ref_count);
 
   return node;
 }
@@ -125,10 +375,11 @@ gsk_render_node_unref (GskRenderNode *node)
 {
   g_return_if_fail (GSK_IS_RENDER_NODE (node));
 
-  if (g_atomic_int_dec_and_test (&node->ref_count))
-    gsk_render_node_finalize (node);
+  if (g_atomic_ref_count_dec (&node->ref_count))
+    GSK_RENDER_NODE_GET_CLASS (node)->finalize (node);
 }
 
+
 /**
  * gsk_render_node_get_node_type:
  * @node: a #GskRenderNode
@@ -142,14 +393,14 @@ gsk_render_node_get_node_type (GskRenderNode *node)
 {
   g_return_val_if_fail (GSK_IS_RENDER_NODE (node), GSK_NOT_A_RENDER_NODE);
 
-  return node->node_class->node_type;
+  return GSK_RENDER_NODE_GET_CLASS (node)->node_type;
 }
 
 G_GNUC_PURE static inline
 GskRenderNodeType
 _gsk_render_node_get_node_type (const GskRenderNode *node)
 {
-  return node->node_class->node_type;
+  return GSK_RENDER_NODE_GET_CLASS (node)->node_type;
 }
 
 /**
@@ -195,9 +446,10 @@ gsk_render_node_draw (GskRenderNode *node,
   cairo_save (cr);
 
   GSK_NOTE (CAIRO, g_message ("Rendering node %s[%p]",
-                            node->node_class->type_name, node));
+                              g_type_name_from_instance ((GTypeInstance *) node),
+                              node));
 
-  node->node_class->draw (node, cr);
+  GSK_RENDER_NODE_GET_CLASS (node)->draw (node, cr);
 
 #ifdef G_ENABLE_DEBUG
   if (GSK_DEBUG_CHECK (GEOMETRY))
@@ -216,7 +468,7 @@ gsk_render_node_draw (GskRenderNode *node,
   if (cairo_status (cr))
     {
       g_warning ("drawing failure for render node %s: %s",
-                 node->node_class->type_name,
+                 g_type_name_from_instance ((GTypeInstance *) node),
                  cairo_status_to_string (cairo_status (cr)));
     }
 }
@@ -245,7 +497,7 @@ gsk_render_node_can_diff (const GskRenderNode *node1,
   if (_gsk_render_node_get_node_type (node1) != _gsk_render_node_get_node_type (node2))
     return FALSE;
 
-  return node1->node_class->can_diff (node1, node2);
+  return GSK_RENDER_NODE_GET_CLASS (node1)->can_diff (node1, node2);
 }
 
 static void
@@ -299,7 +551,7 @@ gsk_render_node_diff (GskRenderNode  *node1,
   if (_gsk_render_node_get_node_type (node1) != _gsk_render_node_get_node_type (node2))
     return gsk_render_node_diff_impossible (node1, node2, region);
 
-  return node1->node_class->diff (node1, node2, region);
+  return GSK_RENDER_NODE_GET_CLASS (node1)->diff (node1, node2, region);
 }
 
 /**
diff --git a/gsk/gskrendernode.h b/gsk/gskrendernode.h
index 17a0549d36..7cc7b90b62 100644
--- a/gsk/gskrendernode.h
+++ b/gsk/gskrendernode.h
@@ -99,6 +99,53 @@ GskRenderNode *         gsk_render_node_deserialize             (GBytes
                                                                  GskParseErrorFunc  error_func,
                                                                  gpointer           user_data);
 
+#define GSK_TYPE_DEBUG_NODE                     (gsk_render_node_types[GSK_DEBUG_NODE])
+#define GSK_TYPE_COLOR_NODE                     (gsk_render_node_types[GSK_COLOR_NODE])
+#define GSK_TYPE_TEXTURE_NODE                   (gsk_render_node_types[GSK_TEXTURE_NODE])
+#define GSK_TYPE_LINEAR_GRADIENT_NODE           (gsk_render_node_types[GSK_LINEAR_GRADIENT_NODE])
+#define GSK_TYPE_REPEATING_LINEAR_GRADIENT_NODE (gsk_render_node_types[GSK_REPEATING_LINEAR_GRADIENT_NODE])
+#define GSK_TYPE_BORDER_NODE                    (gsk_render_node_types[GSK_BORDER_NODE])
+#define GSK_TYPE_INSET_SHADOW_NODE              (gsk_render_node_types[GSK_INSET_SHADOW_NODE])
+#define GSK_TYPE_OUTSET_SHADOW_NODE             (gsk_render_node_types[GSK_OUTSET_SHADOW_NODE])
+#define GSK_TYPE_CAIRO_NODE                     (gsk_render_node_types[GSK_CAIRO_NODE])
+#define GSK_TYPE_CONTAINER_NODE                 (gsk_render_node_types[GSK_CONTAINER_NODE])
+#define GSK_TYPE_TRANSFORM_NODE                 (gsk_render_node_types[GSK_TRANSFORM_NODE])
+#define GSK_TYPE_OPACITY_NODE                   (gsk_render_node_types[GSK_OPACITY_NODE])
+#define GSK_TYPE_COLOR_MATRIX_NODE              (gsk_render_node_types[GSK_COLOR_MATRIX_NODE])
+#define GSK_TYPE_REPEAT_NODE                    (gsk_render_node_types[GSK_REPEAT_NODE])
+#define GSK_TYPE_CLIP_NODE                      (gsk_render_node_types[GSK_CLIP_NODE])
+#define GSK_TYPE_ROUNDED_CLIP_NODE              (gsk_render_node_types[GSK_ROUNDED_CLIP_NODE])
+#define GSK_TYPE_SHADOW_NODE                    (gsk_render_node_types[GSK_SHADOW_NODE])
+#define GSK_TYPE_BLEND_NODE                     (gsk_render_node_types[GSK_BLEND_NODE])
+#define GSK_TYPE_CROSS_FADE_NODE                (gsk_render_node_types[GSK_CROSS_FADE_NODE])
+#define GSK_TYPE_TEXT_NODE                      (gsk_render_node_types[GSK_TEXT_NODE])
+#define GSK_TYPE_BLUR_NODE                      (gsk_render_node_types[GSK_BLUR_NODE])
+
+/*< private >*/
+GDK_EXTERN_VAR GType gsk_render_node_types[];
+
+typedef struct _GskDebugNode                    GskDebugNode;
+typedef struct _GskColorNode                    GskColorNode;
+typedef struct _GskTextureNode                  GskTextureNode;
+typedef struct _GskLinearGradientNode           GskLinearGradientNode;
+typedef struct _GskRepeatingLinearGradientNode  GskRepeatingLinearGradientNode;
+typedef struct _GskBorderNode                   GskBorderNode;
+typedef struct _GskInsetShadowNode              GskInsetShadowNode;
+typedef struct _GskOutsetShadowNode             GskOutsetShadowNode;
+typedef struct _GskCairoNode                    GskCairoNode;
+typedef struct _GskContainerNode                GskContainerNode;
+typedef struct _GskTransformNode                GskTransformNode;
+typedef struct _GskOpacityNode                  GskOpacityNode;
+typedef struct _GskColorMatrixNode              GskColorMatrixNode;
+typedef struct _GskRepeatNode                   GskRepeatNode;
+typedef struct _GskClipNode                     GskClipNode;
+typedef struct _GskRoundedClipNode              GskRoundedClipNode;
+typedef struct _GskShadowNode                   GskShadowNode;
+typedef struct _GskBlendNode                    GskBlendNode;
+typedef struct _GskCrossFadeNode                GskCrossFadeNode;
+typedef struct _GskTextNode                     GskTextNode;
+typedef struct _GskBlurNode                     GskBlurNode;
+
 GDK_AVAILABLE_IN_ALL
 GskRenderNode *         gsk_debug_node_new                      (GskRenderNode            *child,
                                                                  char                     *message);
@@ -132,7 +179,8 @@ const graphene_point_t * gsk_linear_gradient_node_peek_end          (GskRenderNo
 GDK_AVAILABLE_IN_ALL
 gsize                    gsk_linear_gradient_node_get_n_color_stops (GskRenderNode            *node);
 GDK_AVAILABLE_IN_ALL
-const GskColorStop *     gsk_linear_gradient_node_peek_color_stops  (GskRenderNode            *node);
+const GskColorStop *     gsk_linear_gradient_node_peek_color_stops  (GskRenderNode            *node,
+                                                                     gsize                    *n_stops);
 
 GDK_AVAILABLE_IN_ALL
 GskRenderNode *         gsk_repeating_linear_gradient_node_new      (const graphene_rect_t    *bounds,
@@ -152,7 +200,6 @@ const float *           gsk_border_node_peek_widths             (GskRenderNode
 GDK_AVAILABLE_IN_ALL
 const GdkRGBA *         gsk_border_node_peek_colors             (GskRenderNode            *node);
 
-
 GDK_AVAILABLE_IN_ALL
 GskRenderNode *         gsk_inset_shadow_node_new               (const GskRoundedRect     *outline,
                                                                  const GdkRGBA            *color,
@@ -254,7 +301,6 @@ GskRenderNode *         gsk_clip_node_get_child                 (GskRenderNode
 GDK_AVAILABLE_IN_ALL
 const graphene_rect_t * gsk_clip_node_peek_clip                 (GskRenderNode            *node);
 
-
 GDK_AVAILABLE_IN_ALL
 GskRenderNode *         gsk_rounded_clip_node_new               (GskRenderNode            *child,
                                                                  const GskRoundedRect     *clip);
@@ -308,7 +354,8 @@ gboolean                gsk_text_node_has_color_glyphs          (GskRenderNode
 GDK_AVAILABLE_IN_ALL
 guint                   gsk_text_node_get_num_glyphs            (GskRenderNode            *node);
 GDK_AVAILABLE_IN_ALL
-const PangoGlyphInfo   *gsk_text_node_peek_glyphs               (GskRenderNode            *node);
+const PangoGlyphInfo   *gsk_text_node_peek_glyphs               (GskRenderNode            *node,
+                                                                 guint                    *n_glyphs);
 GDK_AVAILABLE_IN_ALL
 const GdkRGBA *         gsk_text_node_peek_color                (GskRenderNode            *node);
 GDK_AVAILABLE_IN_ALL
diff --git a/gsk/gskrendernodeimpl.c b/gsk/gskrendernodeimpl.c
index aad1fa59ef..48219c5758 100644
--- a/gsk/gskrendernodeimpl.c
+++ b/gsk/gskrendernodeimpl.c
@@ -30,6 +30,8 @@
 #include "gdk/gdktextureprivate.h"
 #include <cairo-ft.h>
 
+GType gsk_render_node_types[GSK_RENDER_NODE_TYPE_N_TYPES];
+
 static inline void
 gsk_cairo_rectangle (cairo_t               *cr,
                      const graphene_rect_t *rect)
@@ -49,17 +51,8 @@ rectangle_init_from_graphene (cairo_rectangle_int_t *cairo,
   cairo->height = ceilf (graphene->origin.y + graphene->size.height) - cairo->y;
 }
 
-static gboolean
-gsk_render_node_can_diff_true (const GskRenderNode *node1,
-                               const GskRenderNode *node2)
-{
-  return TRUE;
-}
-
 /*** GSK_COLOR_NODE ***/
 
-typedef struct _GskColorNode GskColorNode;
-
 struct _GskColorNode
 {
   GskRenderNode render_node;
@@ -67,11 +60,6 @@ struct _GskColorNode
   GdkRGBA color;
 };
 
-static void
-gsk_color_node_finalize (GskRenderNode *node)
-{
-}
-
 static void
 gsk_color_node_draw (GskRenderNode *node,
                      cairo_t       *cr)
@@ -99,55 +87,55 @@ gsk_color_node_diff (GskRenderNode  *node1,
   gsk_render_node_diff_impossible (node1, node2, region);
 }
 
-static const GskRenderNodeClass GSK_COLOR_NODE_CLASS = {
-  GSK_COLOR_NODE,
-  sizeof (GskColorNode),
-  "GskColorNode",
-  gsk_color_node_finalize,
-  gsk_color_node_draw,
-  gsk_render_node_can_diff_true,
-  gsk_color_node_diff,
-};
-
+/**
+ * gsk_color_node_peek_color:
+ * @node: (type GskColorNode): a #GskColorNode
+ *
+ * Retrieves the color of the given @node.
+ *
+ * Returns: (transfer none): the color of the node
+ */
 const GdkRGBA *
 gsk_color_node_peek_color (GskRenderNode *node)
 {
   GskColorNode *self = (GskColorNode *) node;
 
+  g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_COLOR_NODE), NULL);
+
   return &self->color;
 }
 
 /**
- * gsk_color_node_new:
+ * gsk_color_node_new: 
  * @rgba: a #GdkRGBA specifying a color
  * @bounds: the rectangle to render the color into
  *
  * Creates a #GskRenderNode that will render the color specified by @rgba into
  * the area given by @bounds.
  *
- * Returns: A new #GskRenderNode
+ * Returns: (transfer full): A new #GskRenderNode
  */
 GskRenderNode *
 gsk_color_node_new (const GdkRGBA         *rgba,
                     const graphene_rect_t *bounds)
 {
   GskColorNode *self;
+  GskRenderNode *node;
 
   g_return_val_if_fail (rgba != NULL, NULL);
   g_return_val_if_fail (bounds != NULL, NULL);
 
-  self = (GskColorNode *) gsk_render_node_new (&GSK_COLOR_NODE_CLASS, 0);
+  self = gsk_render_node_alloc (GSK_COLOR_NODE);
+  node = (GskRenderNode *) self;
 
   self->color = *rgba;
-  graphene_rect_init_from_rect (&self->render_node.bounds, bounds);
+  graphene_rect_init_from_rect (&node->bounds, bounds);
 
-  return &self->render_node;
+  return node;
 }
 
 /*** GSK_LINEAR_GRADIENT_NODE ***/
 
-typedef struct _GskLinearGradientNode GskLinearGradientNode;
-
 struct _GskLinearGradientNode
 {
   GskRenderNode render_node;
@@ -156,12 +144,18 @@ struct _GskLinearGradientNode
   graphene_point_t end;
 
   gsize n_stops;
-  GskColorStop stops[];
+  GskColorStop *stops;
 };
 
 static void
 gsk_linear_gradient_node_finalize (GskRenderNode *node)
 {
+  GskLinearGradientNode *self = (GskLinearGradientNode *) node;
+  GskRenderNodeClass *parent_class = g_type_class_peek (g_type_parent (GSK_TYPE_LINEAR_GRADIENT_NODE));
+
+  g_free (self->stops);
+
+  parent_class->finalize (node);
 }
 
 static void
@@ -228,26 +222,6 @@ gsk_linear_gradient_node_diff (GskRenderNode  *node1,
   gsk_render_node_diff_impossible (node1, node2, region);
 }
 
-static const GskRenderNodeClass GSK_LINEAR_GRADIENT_NODE_CLASS = {
-  GSK_LINEAR_GRADIENT_NODE,
-  sizeof (GskLinearGradientNode),
-  "GskLinearGradientNode",
-  gsk_linear_gradient_node_finalize,
-  gsk_linear_gradient_node_draw,
-  gsk_render_node_can_diff_true,
-  gsk_linear_gradient_node_diff,
-};
-
-static const GskRenderNodeClass GSK_REPEATING_LINEAR_GRADIENT_NODE_CLASS = {
-  GSK_REPEATING_LINEAR_GRADIENT_NODE,
-  sizeof (GskLinearGradientNode),
-  "GskRepeatingLinearGradientNode",
-  gsk_linear_gradient_node_finalize,
-  gsk_linear_gradient_node_draw,
-  gsk_render_node_can_diff_true,
-  gsk_linear_gradient_node_diff,
-};
-
 /**
  * gsk_linear_gradient_node_new:
  * @bounds: the rectangle to render the linear gradient into
@@ -259,7 +233,7 @@ static const GskRenderNodeClass GSK_REPEATING_LINEAR_GRADIENT_NODE_CLASS = {
  * Creates a #GskRenderNode that will create a linear gradient from the given
  * points and color stops, and render that into the area given by @bounds.
  *
- * Returns: A new #GskRenderNode
+ * Returns: (transfer full): A new #GskRenderNode
  */
 GskRenderNode *
 gsk_linear_gradient_node_new (const graphene_rect_t  *bounds,
@@ -269,6 +243,7 @@ gsk_linear_gradient_node_new (const graphene_rect_t  *bounds,
                               gsize                   n_color_stops)
 {
   GskLinearGradientNode *self;
+  GskRenderNode *node;
   gsize i;
 
   g_return_val_if_fail (bounds != NULL, NULL);
@@ -278,19 +253,21 @@ gsk_linear_gradient_node_new (const graphene_rect_t  *bounds,
   g_return_val_if_fail (n_color_stops >= 2, NULL);
   g_return_val_if_fail (color_stops[0].offset >= 0, NULL);
   for (i = 1; i < n_color_stops; i++)
-    g_return_val_if_fail (color_stops[i].offset >= color_stops[i-1].offset, NULL);
+    g_return_val_if_fail (color_stops[i].offset >= color_stops[i - 1].offset, NULL);
   g_return_val_if_fail (color_stops[n_color_stops - 1].offset <= 1, NULL);
 
-  self = (GskLinearGradientNode *) gsk_render_node_new (&GSK_LINEAR_GRADIENT_NODE_CLASS, sizeof 
(GskColorStop) * n_color_stops);
+  self = gsk_render_node_alloc (GSK_LINEAR_GRADIENT_NODE);
+  node = (GskRenderNode *) self;
 
-  graphene_rect_init_from_rect (&self->render_node.bounds, bounds);
+  graphene_rect_init_from_rect (&node->bounds, bounds);
   graphene_point_init_from_point (&self->start, start);
   graphene_point_init_from_point (&self->end, end);
 
-  memcpy (&self->stops, color_stops, sizeof (GskColorStop) * n_color_stops);
   self->n_stops = n_color_stops;
+  self->stops = g_malloc_n (n_color_stops, sizeof (GskColorStop));
+  memcpy (self->stops, color_stops, n_color_stops * sizeof (GskColorStop));
 
-  return &self->render_node;
+  return node;
 }
 
 /**
@@ -305,7 +282,7 @@ gsk_linear_gradient_node_new (const graphene_rect_t  *bounds,
  * from the given points and color stops, and render that into the area
  * given by @bounds.
  *
- * Returns: A new #GskRenderNode
+ * Returns: (transfer full): A new #GskRenderNode
  */
 GskRenderNode *
 gsk_repeating_linear_gradient_node_new (const graphene_rect_t  *bounds,
@@ -315,6 +292,7 @@ gsk_repeating_linear_gradient_node_new (const graphene_rect_t  *bounds,
                                         gsize                   n_color_stops)
 {
   GskLinearGradientNode *self;
+  GskRenderNode *node;
   gsize i;
 
   g_return_val_if_fail (bounds != NULL, NULL);
@@ -324,21 +302,31 @@ gsk_repeating_linear_gradient_node_new (const graphene_rect_t  *bounds,
   g_return_val_if_fail (n_color_stops >= 2, NULL);
   g_return_val_if_fail (color_stops[0].offset >= 0, NULL);
   for (i = 1; i < n_color_stops; i++)
-    g_return_val_if_fail (color_stops[i].offset >= color_stops[i-1].offset, NULL);
+    g_return_val_if_fail (color_stops[i].offset >= color_stops[i - 1].offset, NULL);
   g_return_val_if_fail (color_stops[n_color_stops - 1].offset <= 1, NULL);
 
-  self = (GskLinearGradientNode *) gsk_render_node_new (&GSK_REPEATING_LINEAR_GRADIENT_NODE_CLASS, sizeof 
(GskColorStop) * n_color_stops);
+  self = gsk_render_node_alloc (GSK_REPEATING_LINEAR_GRADIENT_NODE);
+  node = (GskRenderNode *) self;
 
-  graphene_rect_init_from_rect (&self->render_node.bounds, bounds);
+  graphene_rect_init_from_rect (&node->bounds, bounds);
   graphene_point_init_from_point (&self->start, start);
   graphene_point_init_from_point (&self->end, end);
 
-  memcpy (&self->stops, color_stops, sizeof (GskColorStop) * n_color_stops);
+  self->stops = g_malloc_n (n_color_stops, sizeof (GskColorStop));
+  memcpy (self->stops, color_stops, n_color_stops * sizeof (GskColorStop));
   self->n_stops = n_color_stops;
 
-  return &self->render_node;
+  return node;
 }
 
+/**
+ * gsk_linear_gradient_node_peek_start:
+ * @node: (type GskLinearGradientNode): a #GskRenderNode for a linear gradient
+ *
+ * Retrieves the initial point of the linear gradient.
+ *
+ * Returns: (transfer none): the initial point
+ */
 const graphene_point_t *
 gsk_linear_gradient_node_peek_start (GskRenderNode *node)
 {
@@ -347,6 +335,14 @@ gsk_linear_gradient_node_peek_start (GskRenderNode *node)
   return &self->start;
 }
 
+/**
+ * gsk_linear_gradient_node_peek_end:
+ * @node: (type GskLinearGradientNode): a #GskRenderNode for a linear gradient
+ *
+ * Retrieves the final point of the linear gradient.
+ *
+ * Returns: (transfer none): the final point
+ */
 const graphene_point_t *
 gsk_linear_gradient_node_peek_end (GskRenderNode *node)
 {
@@ -355,6 +351,14 @@ gsk_linear_gradient_node_peek_end (GskRenderNode *node)
   return &self->end;
 }
 
+/**
+ * gsk_linear_gradient_node_get_n_color_stops:
+ * @node: (type GskLinearGradientNode): a #GskRenderNode for a linear gradient
+ *
+ * Retrieves the number of color stops in the gradient.
+ *
+ * Returns: the number of color stops
+ */
 gsize
 gsk_linear_gradient_node_get_n_color_stops (GskRenderNode *node)
 {
@@ -363,18 +367,29 @@ gsk_linear_gradient_node_get_n_color_stops (GskRenderNode *node)
   return self->n_stops;
 }
 
+/**
+ * gsk_linear_gradient_node_peek_color_stops:
+ * @node: (type GskLinearGradientNode): a #GskRenderNode for a linear gradient
+ * @n_stops: (out) (optional): the number of color stops in the returned array
+ *
+ * Retrievs the color stops in the gradient.
+ *
+ * Returns: (array length=n_stops): the color stops in the gradient
+ */
 const GskColorStop *
-gsk_linear_gradient_node_peek_color_stops (GskRenderNode *node)
+gsk_linear_gradient_node_peek_color_stops (GskRenderNode *node,
+                                           gsize         *n_stops)
 {
   GskLinearGradientNode *self = (GskLinearGradientNode *) node;
 
+  if (n_stops != NULL)
+    *n_stops = self->n_stops;
+
   return self->stops;
 }
 
 /*** GSK_BORDER_NODE ***/
 
-typedef struct _GskBorderNode GskBorderNode;
-
 struct _GskBorderNode
 {
   GskRenderNode render_node;
@@ -384,11 +399,6 @@ struct _GskBorderNode
   GdkRGBA border_color[4];
 };
 
-static void
-gsk_border_node_finalize (GskRenderNode *node)
-{
-}
-
 static void
 gsk_border_node_mesh_add_patch (cairo_pattern_t *pattern,
                                 const GdkRGBA   *color,
@@ -547,16 +557,6 @@ gsk_border_node_diff (GskRenderNode  *node1,
   gsk_render_node_diff_impossible (node1, node2, region);
 }
 
-static const GskRenderNodeClass GSK_BORDER_NODE_CLASS = {
-  GSK_BORDER_NODE,
-  sizeof (GskBorderNode),
-  "GskBorderNode",
-  gsk_border_node_finalize,
-  gsk_border_node_draw,
-  gsk_render_node_can_diff_true,
-  gsk_border_node_diff,
-};
-
 const GskRoundedRect *
 gsk_border_node_peek_outline (GskRenderNode *node)
 {
@@ -593,7 +593,7 @@ gsk_border_node_peek_colors (GskRenderNode *node)
  * given @outline. The 4 sides of the border can have different widths and
  * colors.
  *
- * Returns: A new #GskRenderNode
+ * Returns: (transfer full): A new #GskRenderNode
  */
 GskRenderNode *
 gsk_border_node_new (const GskRoundedRect     *outline,
@@ -601,26 +601,26 @@ gsk_border_node_new (const GskRoundedRect     *outline,
                      const GdkRGBA             border_color[4])
 {
   GskBorderNode *self;
+  GskRenderNode *node;
 
   g_return_val_if_fail (outline != NULL, NULL);
   g_return_val_if_fail (border_width != NULL, NULL);
   g_return_val_if_fail (border_color != NULL, NULL);
 
-  self = (GskBorderNode *) gsk_render_node_new (&GSK_BORDER_NODE_CLASS, 0);
+  self = gsk_render_node_alloc (GSK_BORDER_NODE);
+  node = (GskRenderNode *) self;
 
   gsk_rounded_rect_init_copy (&self->outline, outline);
   memcpy (self->border_width, border_width, sizeof (self->border_width));
   memcpy (self->border_color, border_color, sizeof (self->border_color));
 
-  graphene_rect_init_from_rect (&self->render_node.bounds, &self->outline.bounds);
+  graphene_rect_init_from_rect (&node->bounds, &self->outline.bounds);
 
-  return &self->render_node;
+  return node;
 }
 
 /*** GSK_TEXTURE_NODE ***/
 
-typedef struct _GskTextureNode GskTextureNode;
-
 struct _GskTextureNode
 {
   GskRenderNode render_node;
@@ -632,8 +632,11 @@ static void
 gsk_texture_node_finalize (GskRenderNode *node)
 {
   GskTextureNode *self = (GskTextureNode *) node;
+  GskRenderNodeClass *parent_class = g_type_class_peek (g_type_parent (GSK_TYPE_TEXTURE_NODE));
+
+  g_clear_object (&self->texture);
 
-  g_object_unref (self->texture);
+  parent_class->finalize (node);
 }
 
 static void
@@ -680,19 +683,11 @@ gsk_texture_node_diff (GskRenderNode  *node1,
   gsk_render_node_diff_impossible (node1, node2, region);
 }
 
-static const GskRenderNodeClass GSK_TEXTURE_NODE_CLASS = {
-  GSK_TEXTURE_NODE,
-  sizeof (GskTextureNode),
-  "GskTextureNode",
-  gsk_texture_node_finalize,
-  gsk_texture_node_draw,
-  gsk_render_node_can_diff_true,
-  gsk_texture_node_diff,
-};
-
 /**
  * gsk_texture_node_get_texture:
- * @node: a #GskRenderNode
+ * @node: (type GskTextureNode): a #GskRenderNode of type %GSK_TEXTURE_NODE
+ *
+ * Retrieves the #GdkTexture used when creating this #GskRenderNode.
  *
  * Returns: (transfer none): the #GdkTexture
  */
@@ -714,29 +709,29 @@ gsk_texture_node_get_texture (GskRenderNode *node)
  * Creates a #GskRenderNode that will render the given
  * @texture into the area given by @bounds.
  *
- * Returns: A new #GskRenderNode
+ * Returns: (transfer full): A new #GskRenderNode
  */
 GskRenderNode *
 gsk_texture_node_new (GdkTexture            *texture,
                       const graphene_rect_t *bounds)
 {
   GskTextureNode *self;
+  GskRenderNode *node;
 
   g_return_val_if_fail (GDK_IS_TEXTURE (texture), NULL);
   g_return_val_if_fail (bounds != NULL, NULL);
 
-  self = (GskTextureNode *) gsk_render_node_new (&GSK_TEXTURE_NODE_CLASS, 0);
+  self = gsk_render_node_alloc (GSK_TEXTURE_NODE);
+  node = (GskRenderNode *) self;
 
   self->texture = g_object_ref (texture);
-  graphene_rect_init_from_rect (&self->render_node.bounds, bounds);
+  graphene_rect_init_from_rect (&node->bounds, bounds);
 
-  return &self->render_node;
+  return node;
 }
 
 /*** GSK_INSET_SHADOW_NODE ***/
 
-typedef struct _GskInsetShadowNode GskInsetShadowNode;
-
 struct _GskInsetShadowNode
 {
   GskRenderNode render_node;
@@ -749,11 +744,6 @@ struct _GskInsetShadowNode
   float blur_radius;
 };
 
-static void
-gsk_inset_shadow_node_finalize (GskRenderNode *node)
-{
-}
-
 static gboolean
 has_empty_clip (cairo_t *cr)
 {
@@ -1148,18 +1138,8 @@ gsk_inset_shadow_node_diff (GskRenderNode  *node1,
   gsk_render_node_diff_impossible (node1, node2, region);
 }
 
-static const GskRenderNodeClass GSK_INSET_SHADOW_NODE_CLASS = {
-  GSK_INSET_SHADOW_NODE,
-  sizeof (GskInsetShadowNode),
-  "GskInsetShadowNode",
-  gsk_inset_shadow_node_finalize,
-  gsk_inset_shadow_node_draw,
-  gsk_render_node_can_diff_true,
-  gsk_inset_shadow_node_diff,
-};
-
 /**
- * gsk_inset_shadow_node_new:
+ * gsk_inset_shadow_node_new: 
  * @outline: outline of the region containing the shadow
  * @color: color of the shadow
  * @dx: horizontal offset of shadow
@@ -1170,7 +1150,7 @@ static const GskRenderNodeClass GSK_INSET_SHADOW_NODE_CLASS = {
  * Creates a #GskRenderNode that will render an inset shadow
  * into the box given by @outline.
  *
- * Returns: A new #GskRenderNode
+ * Returns: (transfer full): A new #GskRenderNode
  */
 GskRenderNode *
 gsk_inset_shadow_node_new (const GskRoundedRect *outline,
@@ -1181,11 +1161,13 @@ gsk_inset_shadow_node_new (const GskRoundedRect *outline,
                            float                 blur_radius)
 {
   GskInsetShadowNode *self;
+  GskRenderNode *node;
 
   g_return_val_if_fail (outline != NULL, NULL);
   g_return_val_if_fail (color != NULL, NULL);
 
-  self = (GskInsetShadowNode *) gsk_render_node_new (&GSK_INSET_SHADOW_NODE_CLASS, 0);
+  self = gsk_render_node_alloc (GSK_INSET_SHADOW_NODE);
+  node = (GskRenderNode *) self;
 
   gsk_rounded_rect_init_copy (&self->outline, outline);
   self->color = *color;
@@ -1194,11 +1176,19 @@ gsk_inset_shadow_node_new (const GskRoundedRect *outline,
   self->spread = spread;
   self->blur_radius = blur_radius;
 
-  graphene_rect_init_from_rect (&self->render_node.bounds, &self->outline.bounds);
+  graphene_rect_init_from_rect (&node->bounds, &self->outline.bounds);
 
-  return &self->render_node;
+  return node;
 }
 
+/**
+ * gsk_inset_shadow_node_peek_outline:
+ * @node: (type GskInsetShadowNode): a #GskRenderNode for an inset shadow
+ *
+ * Retrieves the outline rectangle of the inset shadow.
+ *
+ * Returns: (transfer none): a rounded rectangle
+ */
 const GskRoundedRect *
 gsk_inset_shadow_node_peek_outline (GskRenderNode *node)
 {
@@ -1209,6 +1199,14 @@ gsk_inset_shadow_node_peek_outline (GskRenderNode *node)
   return &self->outline;
 }
 
+/**
+ * gsk_inset_shadow_node_peek_color:
+ * @node: (type GskInsetShadowNode): a #GskRenderNode for an inset shadow
+ *
+ * Retrieves the color of the inset shadow.
+ *
+ * Returns: (transfer none): the color of the shadow
+ */
 const GdkRGBA *
 gsk_inset_shadow_node_peek_color (GskRenderNode *node)
 {
@@ -1219,6 +1217,14 @@ gsk_inset_shadow_node_peek_color (GskRenderNode *node)
   return &self->color;
 }
 
+/**
+ * gsk_inset_shadow_node_get_dx:
+ * @node: (type GskInsetShadowNode): a #GskRenderNode for an inset shadow
+ *
+ * Retrieves the horizontal offset of the inset shadow.
+ *
+ * Returns: an offset, in pixels
+ */
 float
 gsk_inset_shadow_node_get_dx (GskRenderNode *node)
 {
@@ -1229,6 +1235,14 @@ gsk_inset_shadow_node_get_dx (GskRenderNode *node)
   return self->dx;
 }
 
+/**
+ * gsk_inset_shadow_node_get_dy:
+ * @node: (type GskInsetShadowNode): a #GskRenderNode for an inset shadow
+ *
+ * Retrieves the vertical offset of the inset shadow.
+ *
+ * Returns: an offset, in pixels
+ */
 float
 gsk_inset_shadow_node_get_dy (GskRenderNode *node)
 {
@@ -1239,6 +1253,14 @@ gsk_inset_shadow_node_get_dy (GskRenderNode *node)
   return self->dy;
 }
 
+/**
+ * gsk_inset_shadow_node_get_spread:
+ * @node: (type GskInsetShadowNode): a #GskRenderNode for an inset shadow
+ *
+ * Retrieves how much the shadow spreads inwards.
+ *
+ * Returns: the size of the shadow, in pixels
+ */
 float
 gsk_inset_shadow_node_get_spread (GskRenderNode *node)
 {
@@ -1249,6 +1271,14 @@ gsk_inset_shadow_node_get_spread (GskRenderNode *node)
   return self->spread;
 }
 
+/**
+ * gsk_inset_shadow_node_get_blur_radius:
+ * @node: (type GskInsetShadowNode): a #GskRenderNode for an inset shadow
+ *
+ * Retrieves the blur radius to apply to the shadow.
+ *
+ * Returns: the blur radius, in pixels
+ */
 float
 gsk_inset_shadow_node_get_blur_radius (GskRenderNode *node)
 {
@@ -1261,8 +1291,6 @@ gsk_inset_shadow_node_get_blur_radius (GskRenderNode *node)
 
 /*** GSK_OUTSET_SHADOW_NODE ***/
 
-typedef struct _GskOutsetShadowNode GskOutsetShadowNode;
-
 struct _GskOutsetShadowNode
 {
   GskRenderNode render_node;
@@ -1275,11 +1303,6 @@ struct _GskOutsetShadowNode
   float blur_radius;
 };
 
-static void
-gsk_outset_shadow_node_finalize (GskRenderNode *node)
-{
-}
-
 static void
 gsk_outset_shadow_get_extents (GskOutsetShadowNode *self,
                                float               *top,
@@ -1417,18 +1440,8 @@ gsk_outset_shadow_node_diff (GskRenderNode  *node1,
   gsk_render_node_diff_impossible (node1, node2, region);
 }
 
-static const GskRenderNodeClass GSK_OUTSET_SHADOW_NODE_CLASS = {
-  GSK_OUTSET_SHADOW_NODE,
-  sizeof (GskOutsetShadowNode),
-  "GskOutsetShadowNode",
-  gsk_outset_shadow_node_finalize,
-  gsk_outset_shadow_node_draw,
-  gsk_render_node_can_diff_true,
-  gsk_outset_shadow_node_diff,
-};
-
 /**
- * gsk_outset_shadow_node_new:
+ * gsk_outset_shadow_node_new: 
  * @outline: outline of the region surrounded by shadow
  * @color: color of the shadow
  * @dx: horizontal offset of shadow
@@ -1439,7 +1452,7 @@ static const GskRenderNodeClass GSK_OUTSET_SHADOW_NODE_CLASS = {
  * Creates a #GskRenderNode that will render an outset shadow
  * around the box given by @outline.
  *
- * Returns: A new #GskRenderNode
+ * Returns: (transfer full): A new #GskRenderNode
  */
 GskRenderNode *
 gsk_outset_shadow_node_new (const GskRoundedRect *outline,
@@ -1450,12 +1463,14 @@ gsk_outset_shadow_node_new (const GskRoundedRect *outline,
                             float                 blur_radius)
 {
   GskOutsetShadowNode *self;
+  GskRenderNode *node;
   float top, right, bottom, left;
 
   g_return_val_if_fail (outline != NULL, NULL);
   g_return_val_if_fail (color != NULL, NULL);
 
-  self = (GskOutsetShadowNode *) gsk_render_node_new (&GSK_OUTSET_SHADOW_NODE_CLASS, 0);
+  self = gsk_render_node_alloc (GSK_OUTSET_SHADOW_NODE);
+  node = (GskRenderNode *) self;
 
   gsk_rounded_rect_init_copy (&self->outline, outline);
   self->color = *color;
@@ -1466,16 +1481,23 @@ gsk_outset_shadow_node_new (const GskRoundedRect *outline,
 
   gsk_outset_shadow_get_extents (self, &top, &right, &bottom, &left);
 
-  graphene_rect_init_from_rect (&self->render_node.bounds, &self->outline.bounds);
-
-  self->render_node.bounds.origin.x -= left;
-  self->render_node.bounds.origin.y -= top;
-  self->render_node.bounds.size.width += left + right;
-  self->render_node.bounds.size.height += top + bottom;
+  graphene_rect_init_from_rect (&node->bounds, &self->outline.bounds);
+  node->bounds.origin.x -= left;
+  node->bounds.origin.y -= top;
+  node->bounds.size.width += left + right;
+  node->bounds.size.height += top + bottom;
 
-  return &self->render_node;
+  return node;
 }
 
+/**
+ * gsk_outset_shadow_node_peek_outline:
+ * @node: (type GskOutsetShadowNode): a #GskRenderNode for an outset shadow
+ *
+ * Retrieves the outline rectangle of the outset shadow.
+ *
+ * Returns: (transfer none): a rounded rectangle
+ */
 const GskRoundedRect *
 gsk_outset_shadow_node_peek_outline (GskRenderNode *node)
 {
@@ -1486,6 +1508,14 @@ gsk_outset_shadow_node_peek_outline (GskRenderNode *node)
   return &self->outline;
 }
 
+/**
+ * gsk_outset_shadow_node_peek_color:
+ * @node: (type GskOutsetShadowNode): a #GskRenderNode for an outset shadow
+ *
+ * Retrieves the color of the outset shadow.
+ *
+ * Returns: (transfer none): a color
+ */
 const GdkRGBA *
 gsk_outset_shadow_node_peek_color (GskRenderNode *node)
 {
@@ -1496,6 +1526,14 @@ gsk_outset_shadow_node_peek_color (GskRenderNode *node)
   return &self->color;
 }
 
+/**
+ * gsk_outset_shadow_node_get_dx:
+ * @node: (type GskOutsetShadowNode): a #GskRenderNode for an outset shadow
+ *
+ * Retrieves the horizontal offset of the outset shadow.
+ *
+ * Returns: an offset, in pixels
+ */
 float
 gsk_outset_shadow_node_get_dx (GskRenderNode *node)
 {
@@ -1506,6 +1544,14 @@ gsk_outset_shadow_node_get_dx (GskRenderNode *node)
   return self->dx;
 }
 
+/**
+ * gsk_outset_shadow_node_get_dy:
+ * @node: (type GskOutsetShadowNode): a #GskRenderNode for an outset shadow
+ *
+ * Retrieves the vertical offset of the outset shadow.
+ *
+ * Returns: an offset, in pixels
+ */
 float
 gsk_outset_shadow_node_get_dy (GskRenderNode *node)
 {
@@ -1516,6 +1562,14 @@ gsk_outset_shadow_node_get_dy (GskRenderNode *node)
   return self->dy;
 }
 
+/**
+ * gsk_outset_shadow_node_get_spread:
+ * @node: (type GskOutsetShadowNode): a #GskRenderNode for an outset shadow
+ *
+ * Retrieves how much the shadow spreads outwards.
+ *
+ * Returns: the size of the shadow, in pixels
+ */
 float
 gsk_outset_shadow_node_get_spread (GskRenderNode *node)
 {
@@ -1526,6 +1580,14 @@ gsk_outset_shadow_node_get_spread (GskRenderNode *node)
   return self->spread;
 }
 
+/**
+ * gsk_outset_shadow_node_get_blur_radius:
+ * @node: (type GskOutsetShadowNode): a #GskRenderNode for an outset shadow
+ *
+ * Retrieves the blur radius of the shadow.
+ *
+ * Returns: the blur radius, in pixels
+ */
 float
 gsk_outset_shadow_node_get_blur_radius (GskRenderNode *node)
 {
@@ -1538,8 +1600,6 @@ gsk_outset_shadow_node_get_blur_radius (GskRenderNode *node)
 
 /*** GSK_CAIRO_NODE ***/
 
-typedef struct _GskCairoNode GskCairoNode;
-
 struct _GskCairoNode
 {
   GskRenderNode render_node;
@@ -1569,16 +1629,14 @@ gsk_cairo_node_draw (GskRenderNode *node,
   cairo_paint (cr);
 }
 
-static const GskRenderNodeClass GSK_CAIRO_NODE_CLASS = {
-  GSK_CAIRO_NODE,
-  sizeof (GskCairoNode),
-  "GskCairoNode",
-  gsk_cairo_node_finalize,
-  gsk_cairo_node_draw,
-  gsk_render_node_can_diff_true,
-  gsk_render_node_diff_impossible,
-};
-
+/**
+ * gsk_cairo_node_peek_surface:
+ * @node: (type GskCairoNode): a #GskRenderNode for a Cairo surface
+ *
+ * Retrieves the Cairo surface used by the render node.
+ *
+ * Returns: (transfer none): a Cairo surface
+ */
 cairo_surface_t *
 gsk_cairo_node_peek_surface (GskRenderNode *node)
 {
@@ -1590,35 +1648,38 @@ gsk_cairo_node_peek_surface (GskRenderNode *node)
 }
 
 /**
- * gsk_cairo_node_new:
+ * gsk_cairo_node_new: 
  * @bounds: the rectangle to render to
  *
  * Creates a #GskRenderNode that will render a cairo surface
  * into the area given by @bounds. You can draw to the cairo
  * surface using gsk_cairo_node_get_draw_context()
  *
- * Returns: A new #GskRenderNode
+ * Returns: (transfer full): A new #GskRenderNode
  */
 GskRenderNode *
 gsk_cairo_node_new (const graphene_rect_t *bounds)
 {
   GskCairoNode *self;
+  GskRenderNode *node;
 
   g_return_val_if_fail (bounds != NULL, NULL);
 
-  self = (GskCairoNode *) gsk_render_node_new (&GSK_CAIRO_NODE_CLASS, 0);
+  self = gsk_render_node_alloc (GSK_CAIRO_NODE);
+  node = (GskRenderNode *) self;
 
-  graphene_rect_init_from_rect (&self->render_node.bounds, bounds);
+  graphene_rect_init_from_rect (&node->bounds, bounds);
 
-  return &self->render_node;
+  return node;
 }
 
 /**
  * gsk_cairo_node_get_draw_context:
- * @node: a cairo #GskRenderNode
+ * @node: (type GskCairoNode): a #GskRenderNode for a Cairo surface
  *
  * Creates a Cairo context for drawing using the surface associated
  * to the render node.
+ *
  * If no surface exists yet, a surface will be created optimized for
  * rendering to @renderer.
  *
@@ -1667,24 +1728,26 @@ gsk_cairo_node_get_draw_context (GskRenderNode *node)
 
 /**** GSK_CONTAINER_NODE ***/
 
-typedef struct _GskContainerNode GskContainerNode;
-
 struct _GskContainerNode
 {
   GskRenderNode render_node;
 
   guint n_children;
-  GskRenderNode *children[];
+  GskRenderNode **children;
 };
 
 static void
 gsk_container_node_finalize (GskRenderNode *node)
 {
   GskContainerNode *container = (GskContainerNode *) node;
-  guint i;
+  GskRenderNodeClass *parent_class = g_type_class_peek (g_type_parent (GSK_TYPE_CONTAINER_NODE));
 
-  for (i = 0; i < container->n_children; i++)
+  for (guint i = 0; i < container->n_children; i++)
     gsk_render_node_unref (container->children[i]);
+
+  g_free (container->children);
+
+  parent_class->finalize (node);
 }
 
 static void
@@ -1700,13 +1763,6 @@ gsk_container_node_draw (GskRenderNode *node,
     }
 }
 
-static gboolean
-gsk_container_node_can_diff (const GskRenderNode *node1,
-                             const GskRenderNode *node2)
-{
-  return TRUE;
-}
-
 static void
 gsk_render_node_add_to_region (GskRenderNode  *node,
                                cairo_region_t *region)
@@ -1771,16 +1827,6 @@ gsk_container_node_diff (GskRenderNode  *node1,
   gsk_render_node_diff_impossible (node1, node2, region);
 }
 
-static const GskRenderNodeClass GSK_CONTAINER_NODE_CLASS = {
-  GSK_CONTAINER_NODE,
-  sizeof (GskContainerNode),
-  "GskContainerNode",
-  gsk_container_node_finalize,
-  gsk_container_node_draw,
-  gsk_container_node_can_diff,
-  gsk_container_node_diff,
-};
-
 /**
  * gsk_container_node_new:
  * @children: (array length=n_children) (transfer none): The children of the node
@@ -1795,38 +1841,42 @@ GskRenderNode *
 gsk_container_node_new (GskRenderNode **children,
                         guint           n_children)
 {
-  GskContainerNode *container;
-  guint i;
+  GskContainerNode *self;
+  GskRenderNode *node;
 
-  container = (GskContainerNode *) gsk_render_node_new (&GSK_CONTAINER_NODE_CLASS, sizeof (GskRenderNode *) 
* n_children);
+  self = gsk_render_node_alloc (GSK_CONTAINER_NODE);
+  node = (GskRenderNode *) self;
 
-  container->n_children = n_children;
+  self->n_children = n_children;
 
   if (n_children == 0)
     {
-      graphene_rect_init_from_rect (&container->render_node.bounds, graphene_rect_zero());
+      graphene_rect_init_from_rect (&node->bounds, graphene_rect_zero ());
     }
   else
     {
       graphene_rect_t bounds;
 
-      container->children[0] = gsk_render_node_ref (children[0]);
-      graphene_rect_init_from_rect (&bounds, &container->children[0]->bounds);
-      for (i = 1; i < n_children; i++)
+      self->children = g_malloc_n (n_children, sizeof (GskRenderNode *));
+
+      self->children[0] = gsk_render_node_ref (children[0]);
+      graphene_rect_init_from_rect (&bounds, &(children[0]->bounds));
+
+      for (guint i = 1; i < n_children; i++)
         {
-          container->children[i] = gsk_render_node_ref (children[i]);
-          graphene_rect_union (&bounds, &children[i]->bounds, &bounds);
+          self->children[i] = gsk_render_node_ref (children[i]);
+          graphene_rect_union (&bounds, &(children[i]->bounds), &bounds);
         }
 
-      graphene_rect_init_from_rect (&container->render_node.bounds, &bounds);
+      graphene_rect_init_from_rect (&node->bounds, &bounds);
     }
 
-  return &container->render_node;
+  return node;
 }
 
 /**
  * gsk_container_node_get_n_children:
- * @node: a container #GskRenderNode
+ * @node: (type GskContainerNode): a container #GskRenderNode
  *
  * Retrieves the number of direct children of @node.
  *
@@ -1835,38 +1885,36 @@ gsk_container_node_new (GskRenderNode **children,
 guint
 gsk_container_node_get_n_children (GskRenderNode *node)
 {
-  GskContainerNode *container = (GskContainerNode *) node;
+  GskContainerNode *self = (GskContainerNode *) node;
 
   g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_CONTAINER_NODE), 0);
 
-  return container->n_children;
+  return self->n_children;
 }
 
 /**
  * gsk_container_node_get_child:
- * @node: a container #GskRenderNode
+ * @node: (type GskContainerNode): a container #GskRenderNode
  * @idx: the position of the child to get
  *
  * Gets one of the children of @container.
  *
- * Returns: the @idx'th child of @container
+ * Returns: (transfer none): the @idx'th child of @container
  */
 GskRenderNode *
 gsk_container_node_get_child (GskRenderNode *node,
                               guint          idx)
 {
-  GskContainerNode *container = (GskContainerNode *) node;
+  GskContainerNode *self = (GskContainerNode *) node;
 
   g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_CONTAINER_NODE), NULL);
-  g_return_val_if_fail (idx < container->n_children, 0);
+  g_return_val_if_fail (idx < self->n_children, 0);
 
-  return container->children[idx];
+  return self->children[idx];
 }
 
 /*** GSK_TRANSFORM_NODE ***/
 
-typedef struct _GskTransformNode GskTransformNode;
-
 struct _GskTransformNode
 {
   GskRenderNode render_node;
@@ -1879,9 +1927,12 @@ static void
 gsk_transform_node_finalize (GskRenderNode *node)
 {
   GskTransformNode *self = (GskTransformNode *) node;
+  GskRenderNodeClass *parent_class = g_type_class_peek (g_type_parent (GSK_TYPE_TRANSFORM_NODE));
 
   gsk_render_node_unref (self->child);
   gsk_transform_unref (self->transform);
+
+  parent_class->finalize (node);
 }
 
 static void
@@ -1986,55 +2037,47 @@ gsk_transform_node_diff (GskRenderNode  *node1,
     }
 }
 
-static const GskRenderNodeClass GSK_TRANSFORM_NODE_CLASS = {
-  GSK_TRANSFORM_NODE,
-  sizeof (GskTransformNode),
-  "GskTransformNode",
-  gsk_transform_node_finalize,
-  gsk_transform_node_draw,
-  gsk_transform_node_can_diff,
-  gsk_transform_node_diff,
-};
-
 /**
- * gsk_transform_node_new:
+ * gsk_transform_node_new: 
  * @child: The node to transform
  * @transform: (transfer none): The transform to apply
  *
  * Creates a #GskRenderNode that will transform the given @child
  * with the given @transform.
  *
- * Returns: A new #GskRenderNode
+ * Returns: (transfer full): A new #GskRenderNode
  */
 GskRenderNode *
 gsk_transform_node_new (GskRenderNode *child,
                         GskTransform  *transform)
 {
   GskTransformNode *self;
+  GskRenderNode *node;
 
   g_return_val_if_fail (GSK_IS_RENDER_NODE (child), NULL);
   g_return_val_if_fail (transform != NULL, NULL);
 
-  self = (GskTransformNode *) gsk_render_node_new (&GSK_TRANSFORM_NODE_CLASS, 0);
+  self = gsk_render_node_alloc (GSK_TRANSFORM_NODE);
+  node = (GskRenderNode *) self;
 
   self->child = gsk_render_node_ref (child);
   self->transform = gsk_transform_ref (transform);
 
   gsk_transform_transform_bounds (self->transform,
                                   &child->bounds,
-                                  &self->render_node.bounds);
+                                  &node->bounds);
 
-  return &self->render_node;
+  return node;
 }
 
 /**
  * gsk_transform_node_get_child:
- * @node: a transform @GskRenderNode
+ * @node: (type GskTransformNode): a #GskRenderNode for a transform
  *
  * Gets the child node that is getting transformed by the given @node.
  *
  * Returns: (transfer none): The child that is getting transformed
- **/
+ */
 GskRenderNode *
 gsk_transform_node_get_child (GskRenderNode *node)
 {
@@ -2045,6 +2088,14 @@ gsk_transform_node_get_child (GskRenderNode *node)
   return self->child;
 }
 
+/**
+ * gsk_transform_node_get_transform:
+ * @node: (type GskTransformNode): a #GskRenderNode for a transform
+ *
+ * Retrieves the #GskTransform used by the @node.
+ *
+ * Returns: (transfer none): a #GskTransform
+ */
 GskTransform *
 gsk_transform_node_get_transform (GskRenderNode *node)
 {
@@ -2055,258 +2106,130 @@ gsk_transform_node_get_transform (GskRenderNode *node)
   return self->transform;
 }
 
-/*** GSK_DEBUG_NODE ***/
-
-typedef struct _GskDebugNode GskDebugNode;
+/*** GSK_OPACITY_NODE ***/
 
-struct _GskDebugNode
+struct _GskOpacityNode
 {
   GskRenderNode render_node;
 
   GskRenderNode *child;
-  char *message;
+  float opacity;
 };
 
 static void
-gsk_debug_node_finalize (GskRenderNode *node)
+gsk_opacity_node_finalize (GskRenderNode *node)
 {
-  GskDebugNode *self = (GskDebugNode *) node;
+  GskOpacityNode *self = (GskOpacityNode *) node;
+  GskRenderNodeClass *parent_class = g_type_class_peek (g_type_parent (GSK_TYPE_OPACITY_NODE));
 
   gsk_render_node_unref (self->child);
-  g_free (self->message);
+
+  parent_class->finalize (node);
 }
 
 static void
-gsk_debug_node_draw (GskRenderNode *node,
-                      cairo_t       *cr)
+gsk_opacity_node_draw (GskRenderNode *node,
+                       cairo_t       *cr)
 {
-  GskDebugNode *self = (GskDebugNode *) node;
+  GskOpacityNode *self = (GskOpacityNode *) node;
+
+  cairo_save (cr);
+
+  /* clip so the push_group() creates a smaller surface */
+  gsk_cairo_rectangle (cr, &node->bounds);
+  cairo_clip (cr);
+
+  cairo_push_group (cr);
 
   gsk_render_node_draw (self->child, cr);
-}
 
-static gboolean
-gsk_debug_node_can_diff (const GskRenderNode *node1,
-                         const GskRenderNode *node2)
-{
-  GskDebugNode *self1 = (GskDebugNode *) node1;
-  GskDebugNode *self2 = (GskDebugNode *) node2;
+  cairo_pop_group_to_source (cr);
+  cairo_paint_with_alpha (cr, self->opacity);
 
-  return gsk_render_node_can_diff (self1->child, self2->child);
+  cairo_restore (cr);
 }
 
 static void
-gsk_debug_node_diff (GskRenderNode  *node1,
-                     GskRenderNode  *node2,
-                     cairo_region_t *region)
+gsk_opacity_node_diff (GskRenderNode  *node1,
+                       GskRenderNode  *node2,
+                       cairo_region_t *region)
 {
-  GskDebugNode *self1 = (GskDebugNode *) node1;
-  GskDebugNode *self2 = (GskDebugNode *) node2;
+  GskOpacityNode *self1 = (GskOpacityNode *) node1;
+  GskOpacityNode *self2 = (GskOpacityNode *) node2;
 
-  gsk_render_node_diff (self1->child, self2->child, region);
+  if (self1->opacity == self2->opacity)
+    gsk_render_node_diff (self1->child, self2->child, region);
+  else
+    gsk_render_node_diff_impossible (node1, node2, region);
 }
 
-static const GskRenderNodeClass GSK_DEBUG_NODE_CLASS = {
-  GSK_DEBUG_NODE,
-  sizeof (GskDebugNode),
-  "GskDebugNode",
-  gsk_debug_node_finalize,
-  gsk_debug_node_draw,
-  gsk_debug_node_can_diff,
-  gsk_debug_node_diff,
-};
-
 /**
- * gsk_debug_node_new:
- * @child: The child to add debug info for
- * @message: (transfer full): The debug message 
- *
- * Creates a #GskRenderNode that will add debug information about
- * the given @child.
+ * gsk_opacity_node_new: 
+ * @child: The node to draw
+ * @opacity: The opacity to apply
  *
- * Adding this node has no visual effect.
+ * Creates a #GskRenderNode that will drawn the @child with reduced
+ * @opacity.
  *
- * Returns: A new #GskRenderNode
+ * Returns: (transfer full): A new #GskRenderNode
  */
 GskRenderNode *
-gsk_debug_node_new (GskRenderNode *child,
-                    char          *message)
+gsk_opacity_node_new (GskRenderNode *child,
+                      float          opacity)
 {
-  GskDebugNode *self;
+  GskOpacityNode *self;
+  GskRenderNode *node;
 
   g_return_val_if_fail (GSK_IS_RENDER_NODE (child), NULL);
 
-  self = (GskDebugNode *) gsk_render_node_new (&GSK_DEBUG_NODE_CLASS, 0);
+  self = gsk_render_node_alloc (GSK_OPACITY_NODE);
+  node = (GskRenderNode *) self;
 
   self->child = gsk_render_node_ref (child);
-  self->message = message;
+  self->opacity = CLAMP (opacity, 0.0, 1.0);
 
-  graphene_rect_init_from_rect (&self->render_node.bounds, &child->bounds);
+  graphene_rect_init_from_rect (&node->bounds, &child->bounds);
 
-  return &self->render_node;
+  return node;
 }
 
 /**
- * gsk_debug_node_get_child:
- * @node: a debug @GskRenderNode
+ * gsk_opacity_node_get_child:
+ * @node: (type GskOpacityNode): a #GskRenderNode for an opacity
  *
- * Gets the child node that is getting debug by the given @node.
+ * Gets the child node that is getting opacityed by the given @node.
  *
- * Returns: (transfer none): The child that is getting debug
- **/
+ * Returns: (transfer none): The child that is getting opacityed
+ */
 GskRenderNode *
-gsk_debug_node_get_child (GskRenderNode *node)
+gsk_opacity_node_get_child (GskRenderNode *node)
 {
-  GskDebugNode *self = (GskDebugNode *) node;
+  GskOpacityNode *self = (GskOpacityNode *) node;
 
-  g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_DEBUG_NODE), NULL);
+  g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_OPACITY_NODE), NULL);
 
   return self->child;
 }
 
 /**
- * gsk_debug_node_get_message:
- * @node: a debug #GskRenderNode
+ * gsk_opacity_node_get_opacity:
+ * @node: (type GskOpacityNode): a #GskRenderNode for an opacity
  *
- * Gets the debug message that was set on this node
+ * Gets the transparency factor for an opacity node.
  *
- * Returns: (transfer none): The debug message
- **/
-const char *
-gsk_debug_node_get_message (GskRenderNode *node)
+ * Returns: the opacity factor
+ */
+float
+gsk_opacity_node_get_opacity (GskRenderNode *node)
 {
-  GskDebugNode *self = (GskDebugNode *) node;
+  GskOpacityNode *self = (GskOpacityNode *) node;
 
-  g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_DEBUG_NODE), "You run broken code!");
+  g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_OPACITY_NODE), 1.0);
 
-  return self->message;
+  return self->opacity;
 }
 
-/*** GSK_OPACITY_NODE ***/
-
-typedef struct _GskOpacityNode GskOpacityNode;
-
-struct _GskOpacityNode
-{
-  GskRenderNode render_node;
-
-  GskRenderNode *child;
-  float opacity;
-};
-
-static void
-gsk_opacity_node_finalize (GskRenderNode *node)
-{
-  GskOpacityNode *self = (GskOpacityNode *) node;
-
-  gsk_render_node_unref (self->child);
-}
-
-static void
-gsk_opacity_node_draw (GskRenderNode *node,
-                       cairo_t       *cr)
-{
-  GskOpacityNode *self = (GskOpacityNode *) node;
-
-  cairo_save (cr);
-
-  /* clip so the push_group() creates a smaller surface */
-  gsk_cairo_rectangle (cr, &node->bounds);
-  cairo_clip (cr);
-
-  cairo_push_group (cr);
-
-  gsk_render_node_draw (self->child, cr);
-
-  cairo_pop_group_to_source (cr);
-  cairo_paint_with_alpha (cr, self->opacity);
-
-  cairo_restore (cr);
-}
-
-static void
-gsk_opacity_node_diff (GskRenderNode  *node1,
-                       GskRenderNode  *node2,
-                       cairo_region_t *region)
-{
-  GskOpacityNode *self1 = (GskOpacityNode *) node1;
-  GskOpacityNode *self2 = (GskOpacityNode *) node2;
-
-  if (self1->opacity == self2->opacity)
-    gsk_render_node_diff (self1->child, self2->child, region);
-  else
-    gsk_render_node_diff_impossible (node1, node2, region);
-}
-
-static const GskRenderNodeClass GSK_OPACITY_NODE_CLASS = {
-  GSK_OPACITY_NODE,
-  sizeof (GskOpacityNode),
-  "GskOpacityNode",
-  gsk_opacity_node_finalize,
-  gsk_opacity_node_draw,
-  gsk_render_node_can_diff_true,
-  gsk_opacity_node_diff,
-};
-
-/**
- * gsk_opacity_node_new:
- * @child: The node to draw
- * @opacity: The opacity to apply
- *
- * Creates a #GskRenderNode that will drawn the @child with reduced
- * @opacity.
- *
- * Returns: A new #GskRenderNode
- */
-GskRenderNode *
-gsk_opacity_node_new (GskRenderNode *child,
-                      float          opacity)
-{
-  GskOpacityNode *self;
-
-  g_return_val_if_fail (GSK_IS_RENDER_NODE (child), NULL);
-
-  self = (GskOpacityNode *) gsk_render_node_new (&GSK_OPACITY_NODE_CLASS, 0);
-
-  self->child = gsk_render_node_ref (child);
-  self->opacity = CLAMP (opacity, 0.0, 1.0);
-
-  graphene_rect_init_from_rect (&self->render_node.bounds, &child->bounds);
-
-  return &self->render_node;
-}
-
-/**
- * gsk_opacity_node_get_child:
- * @node: an opacity @GskRenderNode
- *
- * Gets the child node that is getting opacityed by the given @node.
- *
- * Returns: (transfer none): The child that is getting opacityed
- **/
-GskRenderNode *
-gsk_opacity_node_get_child (GskRenderNode *node)
-{
-  GskOpacityNode *self = (GskOpacityNode *) node;
-
-  g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_OPACITY_NODE), NULL);
-
-  return self->child;
-}
-
-float
-gsk_opacity_node_get_opacity (GskRenderNode *node)
-{
-  GskOpacityNode *self = (GskOpacityNode *) node;
-
-  g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_OPACITY_NODE), 1.0);
-
-  return self->opacity;
-}
-
-/*** GSK_COLOR_MATRIX_NODE ***/
-
-typedef struct _GskColorMatrixNode GskColorMatrixNode;
+/*** GSK_COLOR_MATRIX_NODE ***/
 
 struct _GskColorMatrixNode
 {
@@ -2321,8 +2244,11 @@ static void
 gsk_color_matrix_node_finalize (GskRenderNode *node)
 {
   GskColorMatrixNode *self = (GskColorMatrixNode *) node;
+  GskRenderNodeClass *parent_class = g_type_class_peek (g_type_parent (GSK_TYPE_COLOR_MATRIX_NODE));
 
   gsk_render_node_unref (self->child);
+
+  parent_class->finalize (node);
 }
 
 static void
@@ -2407,18 +2333,8 @@ gsk_color_matrix_node_draw (GskRenderNode *node,
   cairo_pattern_destroy (pattern);
 }
 
-static const GskRenderNodeClass GSK_COLOR_MATRIX_NODE_CLASS = {
-  GSK_COLOR_MATRIX_NODE,
-  sizeof (GskColorMatrixNode),
-  "GskColorMatrixNode",
-  gsk_color_matrix_node_finalize,
-  gsk_color_matrix_node_draw,
-  gsk_render_node_can_diff_true,
-  gsk_render_node_diff_impossible,
-};
-
 /**
- * gsk_color_matrix_node_new:
+ * gsk_color_matrix_node_new: 
  * @child: The node to draw
  * @color_matrix: The matrix to apply
  * @color_offset: Values to add to the color
@@ -2427,10 +2343,14 @@ static const GskRenderNodeClass GSK_COLOR_MATRIX_NODE_CLASS = {
  * @color_matrix.
  *
  * In particular, the node will transform the operation
+ *
+ * |[<!-- language="plain" -->
  *   pixel = color_matrix * pixel + color_offset
+ * ]|
+ *
  * for every pixel.
  *
- * Returns: A new #GskRenderNode
+ * Returns: (transfer full): A new #GskRenderNode
  */
 GskRenderNode *
 gsk_color_matrix_node_new (GskRenderNode           *child,
@@ -2438,23 +2358,25 @@ gsk_color_matrix_node_new (GskRenderNode           *child,
                            const graphene_vec4_t   *color_offset)
 {
   GskColorMatrixNode *self;
+  GskRenderNode *node;
 
   g_return_val_if_fail (GSK_IS_RENDER_NODE (child), NULL);
 
-  self = (GskColorMatrixNode *) gsk_render_node_new (&GSK_COLOR_MATRIX_NODE_CLASS, 0);
+  self = gsk_render_node_alloc (GSK_COLOR_MATRIX_NODE);
+  node = (GskRenderNode *) self;
 
   self->child = gsk_render_node_ref (child);
   graphene_matrix_init_from_matrix (&self->color_matrix, color_matrix);
   graphene_vec4_init_from_vec4 (&self->color_offset, color_offset);
 
-  graphene_rect_init_from_rect (&self->render_node.bounds, &child->bounds);
+  graphene_rect_init_from_rect (&node->bounds, &child->bounds);
 
-  return &self->render_node;
+  return node;
 }
 
 /**
  * gsk_color_matrix_node_get_child:
- * @node: a color matrix @GskRenderNode
+ * @node: (type GskColorMatrixNode): a color matrix #GskRenderNode
  *
  * Gets the child node that is getting its colors modified by the given @node.
  *
@@ -2470,6 +2392,14 @@ gsk_color_matrix_node_get_child (GskRenderNode *node)
   return self->child;
 }
 
+/**
+ * gsk_color_matrix_node_peek_color_matrix:
+ * @node: (type GskColorMatrixNode): a color matrix #GskRenderNode
+ *
+ * Retrieves the color matrix used by the @node.
+ *
+ * Returns: a 4x4 color matrix
+ */
 const graphene_matrix_t *
 gsk_color_matrix_node_peek_color_matrix (GskRenderNode *node)
 {
@@ -2480,6 +2410,14 @@ gsk_color_matrix_node_peek_color_matrix (GskRenderNode *node)
   return &self->color_matrix;
 }
 
+/**
+ * gsk_color_matrix_node_peek_color_offset:
+ * @node: (type GskColorMatrixNode): a color matrix #GskRenderNode
+ *
+ * Retrieves the color offset used by the @node.
+ *
+ * Returns: a color vector
+ */
 const graphene_vec4_t *
 gsk_color_matrix_node_peek_color_offset (GskRenderNode *node)
 {
@@ -2492,8 +2430,6 @@ gsk_color_matrix_node_peek_color_offset (GskRenderNode *node)
 
 /*** GSK_REPEAT_NODE ***/
 
-typedef struct _GskRepeatNode GskRepeatNode;
-
 struct _GskRepeatNode
 {
   GskRenderNode render_node;
@@ -2506,8 +2442,11 @@ static void
 gsk_repeat_node_finalize (GskRenderNode *node)
 {
   GskRepeatNode *self = (GskRepeatNode *) node;
+  GskRenderNodeClass *parent_class = g_type_class_peek (g_type_parent (GSK_TYPE_REPEAT_NODE));
 
   gsk_render_node_unref (self->child);
+
+  parent_class->finalize (node);
 }
 
 static void
@@ -2547,18 +2486,8 @@ gsk_repeat_node_draw (GskRenderNode *node,
   cairo_fill (cr);
 }
 
-static const GskRenderNodeClass GSK_REPEAT_NODE_CLASS = {
-  GSK_REPEAT_NODE,
-  sizeof (GskRepeatNode),
-  "GskRepeatNode",
-  gsk_repeat_node_finalize,
-  gsk_repeat_node_draw,
-  gsk_render_node_can_diff_true,
-  gsk_render_node_diff_impossible,
-};
-
 /**
- * gsk_repeat_node_new:
+ * gsk_repeat_node_new: 
  * @bounds: The bounds of the area to be painted
  * @child: The child to repeat
  * @child_bounds: (allow-none): The area of the child to repeat or %NULL to
@@ -2567,7 +2496,7 @@ static const GskRenderNodeClass GSK_REPEAT_NODE_CLASS = {
  * Creates a #GskRenderNode that will repeat the drawing of @child across
  * the given @bounds.
  *
- * Returns: A new #GskRenderNode
+ * Returns: (transfer full): A new #GskRenderNode
  */
 GskRenderNode *
 gsk_repeat_node_new (const graphene_rect_t *bounds,
@@ -2575,22 +2504,34 @@ gsk_repeat_node_new (const graphene_rect_t *bounds,
                      const graphene_rect_t *child_bounds)
 {
   GskRepeatNode *self;
+  GskRenderNode *node;
 
   g_return_val_if_fail (bounds != NULL, NULL);
   g_return_val_if_fail (GSK_IS_RENDER_NODE (child), NULL);
 
-  self = (GskRepeatNode *) gsk_render_node_new (&GSK_REPEAT_NODE_CLASS, 0);
+  self = gsk_render_node_alloc (GSK_REPEAT_NODE);
+  node = (GskRenderNode *) self;
+
+  graphene_rect_init_from_rect (&node->bounds, bounds);
 
-  graphene_rect_init_from_rect (&self->render_node.bounds, bounds);
   self->child = gsk_render_node_ref (child);
+
   if (child_bounds)
     graphene_rect_init_from_rect (&self->child_bounds, child_bounds);
   else
     graphene_rect_init_from_rect (&self->child_bounds, &child->bounds);
 
-  return &self->render_node;
+  return node;
 }
 
+/**
+ * gsk_repeat_node_get_child:
+ * @node: (type GskRepeatNode): a repeat #GskRenderNode
+ *
+ * Retrieves the child of @node.
+ *
+ * Returns: (transfer none): a #GskRenderNode
+ */
 GskRenderNode *
 gsk_repeat_node_get_child (GskRenderNode *node)
 {
@@ -2601,6 +2542,14 @@ gsk_repeat_node_get_child (GskRenderNode *node)
   return self->child;
 }
 
+/**
+ * gsk_repeat_node_peek_child_bounds:
+ * @node: (type GskRepeatNode): a repeat #GskRenderNode
+ *
+ * Retrieves the bounding rectangle of the child of @node.
+ *
+ * Returns: (transfer none): a bounding rectangle
+ */
 const graphene_rect_t *
 gsk_repeat_node_peek_child_bounds (GskRenderNode *node)
 {
@@ -2613,8 +2562,6 @@ gsk_repeat_node_peek_child_bounds (GskRenderNode *node)
 
 /*** GSK_CLIP_NODE ***/
 
-typedef struct _GskClipNode GskClipNode;
-
 struct _GskClipNode
 {
   GskRenderNode render_node;
@@ -2627,8 +2574,11 @@ static void
 gsk_clip_node_finalize (GskRenderNode *node)
 {
   GskClipNode *self = (GskClipNode *) node;
+  GskRenderNodeClass *parent_class = g_type_class_peek (g_type_parent (GSK_TYPE_CLIP_NODE));
 
   gsk_render_node_unref (self->child);
+
+  parent_class->finalize (node);
 }
 
 static void
@@ -2673,48 +2623,40 @@ gsk_clip_node_diff (GskRenderNode  *node1,
     }
 }
 
-static const GskRenderNodeClass GSK_CLIP_NODE_CLASS = {
-  GSK_CLIP_NODE,
-  sizeof (GskClipNode),
-  "GskClipNode",
-  gsk_clip_node_finalize,
-  gsk_clip_node_draw,
-  gsk_render_node_can_diff_true,
-  gsk_clip_node_diff,
-};
-
 /**
- * gsk_clip_node_new:
+ * gsk_clip_node_new: 
  * @child: The node to draw
  * @clip: The clip to apply
  *
  * Creates a #GskRenderNode that will clip the @child to the area
  * given by @clip.
  *
- * Returns: A new #GskRenderNode
+ * Returns: (transfer full): A new #GskRenderNode
  */
 GskRenderNode *
 gsk_clip_node_new (GskRenderNode         *child,
                    const graphene_rect_t *clip)
 {
   GskClipNode *self;
+  GskRenderNode *node;
 
   g_return_val_if_fail (GSK_IS_RENDER_NODE (child), NULL);
   g_return_val_if_fail (clip != NULL, NULL);
 
-  self = (GskClipNode *) gsk_render_node_new (&GSK_CLIP_NODE_CLASS, 0);
+  self = gsk_render_node_alloc (GSK_CLIP_NODE);
+  node = (GskRenderNode *) self;
 
   self->child = gsk_render_node_ref (child);
   graphene_rect_normalize_r (clip, &self->clip);
 
-  graphene_rect_intersection (&self->clip, &child->bounds, &self->render_node.bounds);
+  graphene_rect_intersection (&self->clip, &child->bounds, &node->bounds);
 
-  return &self->render_node;
+  return node;
 }
 
 /**
  * gsk_clip_node_get_child:
- * @node: a clip @GskRenderNode
+ * @node: (type GskClipNode): a clip @GskRenderNode
  *
  * Gets the child node that is getting clipped by the given @node.
  *
@@ -2730,6 +2672,14 @@ gsk_clip_node_get_child (GskRenderNode *node)
   return self->child;
 }
 
+/**
+ * gsk_clip_node_peek_clip:
+ * @node: (type GskClipNode): a #GskClipNode
+ *
+ * Retrieves the clip rectangle for @node.
+ *
+ * Returns: a clip rectangle
+ */
 const graphene_rect_t *
 gsk_clip_node_peek_clip (GskRenderNode *node)
 {
@@ -2742,8 +2692,6 @@ gsk_clip_node_peek_clip (GskRenderNode *node)
 
 /*** GSK_ROUNDED_CLIP_NODE ***/
 
-typedef struct _GskRoundedClipNode GskRoundedClipNode;
-
 struct _GskRoundedClipNode
 {
   GskRenderNode render_node;
@@ -2756,8 +2704,11 @@ static void
 gsk_rounded_clip_node_finalize (GskRenderNode *node)
 {
   GskRoundedClipNode *self = (GskRoundedClipNode *) node;
+  GskRenderNodeClass *parent_class = g_type_class_peek (g_type_parent (GSK_TYPE_ROUNDED_CLIP_NODE));
 
   gsk_render_node_unref (self->child);
+
+  parent_class->finalize (node);
 }
 
 static void
@@ -2802,48 +2753,40 @@ gsk_rounded_clip_node_diff (GskRenderNode  *node1,
     }
 }
 
-static const GskRenderNodeClass GSK_ROUNDED_CLIP_NODE_CLASS = {
-  GSK_ROUNDED_CLIP_NODE,
-  sizeof (GskRoundedClipNode),
-  "GskRoundedClipNode",
-  gsk_rounded_clip_node_finalize,
-  gsk_rounded_clip_node_draw,
-  gsk_render_node_can_diff_true,
-  gsk_rounded_clip_node_diff,
-};
-
 /**
- * gsk_rounded_clip_node_new:
+ * gsk_rounded_clip_node_new: 
  * @child: The node to draw
  * @clip: The clip to apply
  *
  * Creates a #GskRenderNode that will clip the @child to the area
  * given by @clip.
  *
- * Returns: A new #GskRenderNode
+ * Returns: (transfer none): A new #GskRenderNode
  */
 GskRenderNode *
 gsk_rounded_clip_node_new (GskRenderNode         *child,
                            const GskRoundedRect  *clip)
 {
   GskRoundedClipNode *self;
+  GskRenderNode *node;
 
   g_return_val_if_fail (GSK_IS_RENDER_NODE (child), NULL);
   g_return_val_if_fail (clip != NULL, NULL);
 
-  self = (GskRoundedClipNode *) gsk_render_node_new (&GSK_ROUNDED_CLIP_NODE_CLASS, 0);
+  self = gsk_render_node_alloc (GSK_ROUNDED_CLIP_NODE);
+  node = (GskRenderNode *) self;
 
   self->child = gsk_render_node_ref (child);
   gsk_rounded_rect_init_copy (&self->clip, clip);
 
-  graphene_rect_intersection (&self->clip.bounds, &child->bounds, &self->render_node.bounds);
+  graphene_rect_intersection (&self->clip.bounds, &child->bounds, &node->bounds);
 
-  return &self->render_node;
+  return node;
 }
 
 /**
  * gsk_rounded_clip_node_get_child:
- * @node: a clip @GskRenderNode
+ * @node: (type GskRoundedClipNode): a rounded clip #GskRenderNode
  *
  * Gets the child node that is getting clipped by the given @node.
  *
@@ -2859,6 +2802,14 @@ gsk_rounded_clip_node_get_child (GskRenderNode *node)
   return self->child;
 }
 
+/**
+ * gsk_rounded_clip_node_peek_clip:
+ * @node: (type GskRoundedClipNode): a rounded clip #GskRenderNode
+ *
+ * Retrievs the rounded rectangle used to clip the contents of the @node.
+ *
+ * Returns: (transfer none): a rounded rectangle
+ */
 const GskRoundedRect *
 gsk_rounded_clip_node_peek_clip (GskRenderNode *node)
 {
@@ -2871,8 +2822,6 @@ gsk_rounded_clip_node_peek_clip (GskRenderNode *node)
 
 /*** GSK_SHADOW_NODE ***/
 
-typedef struct _GskShadowNode GskShadowNode;
-
 struct _GskShadowNode
 {
   GskRenderNode render_node;
@@ -2880,15 +2829,19 @@ struct _GskShadowNode
   GskRenderNode *child;
 
   gsize n_shadows;
-  GskShadow shadows[];
+  GskShadow *shadows;
 };
 
 static void
 gsk_shadow_node_finalize (GskRenderNode *node)
 {
   GskShadowNode *self = (GskShadowNode *) node;
+  GskRenderNodeClass *parent_class = g_type_class_peek (g_type_parent (GSK_TYPE_SHADOW_NODE));
 
   gsk_render_node_unref (self->child);
+  g_free (self->shadows);
+
+  parent_class->finalize (node);
 }
 
 static void
@@ -3013,18 +2966,8 @@ gsk_shadow_node_get_bounds (GskShadowNode *self,
   bounds->size.height += top + bottom;
 }
 
-static const GskRenderNodeClass GSK_SHADOW_NODE_CLASS = {
-  GSK_SHADOW_NODE,
-  sizeof (GskShadowNode),
-  "GskShadowNode",
-  gsk_shadow_node_finalize,
-  gsk_shadow_node_draw,
-  gsk_render_node_can_diff_true,
-  gsk_shadow_node_diff,
-};
-
 /**
- * gsk_shadow_node_new:
+ * gsk_shadow_node_new: 
  * @child: The node to draw
  * @shadows: (array length=n_shadows): The shadows to apply
  * @n_shadows: number of entries in the @shadows array
@@ -3032,30 +2975,41 @@ static const GskRenderNodeClass GSK_SHADOW_NODE_CLASS = {
  * Creates a #GskRenderNode that will draw a @child with the given
  * @shadows below it.
  *
- * Returns: A new #GskRenderNode
+ * Returns: (transfer full): A new #GskRenderNode
  */
 GskRenderNode *
-gsk_shadow_node_new (GskRenderNode         *child,
-                     const GskShadow       *shadows,
-                     gsize                  n_shadows)
+gsk_shadow_node_new (GskRenderNode   *child,
+                     const GskShadow *shadows,
+                     gsize            n_shadows)
 {
   GskShadowNode *self;
+  GskRenderNode *node;
 
   g_return_val_if_fail (GSK_IS_RENDER_NODE (child), NULL);
   g_return_val_if_fail (shadows != NULL, NULL);
   g_return_val_if_fail (n_shadows > 0, NULL);
 
-  self = (GskShadowNode *) gsk_render_node_new (&GSK_SHADOW_NODE_CLASS, n_shadows * sizeof (GskShadow));
+  self = gsk_render_node_alloc (GSK_SHADOW_NODE);
+  node = (GskRenderNode *) self;
 
   self->child = gsk_render_node_ref (child);
-  memcpy (&self->shadows, shadows, n_shadows * sizeof (GskShadow));
   self->n_shadows = n_shadows;
+  self->shadows = g_malloc_n (n_shadows, sizeof (GskShadow));
+  memcpy (self->shadows, shadows, n_shadows * sizeof (GskShadow));
 
-  gsk_shadow_node_get_bounds (self, &self->render_node.bounds);
+  gsk_shadow_node_get_bounds (self, &node->bounds);
 
-  return &self->render_node;
+  return node;
 }
 
+/**
+ * gsk_shadow_node_get_child:
+ * @node: (type GskShadowNode): a shadow #GskRenderNode
+ *
+ * Retrieves the child #GskRenderNode of the shadow @node.
+ *
+ * Returns: (transfer none): the child render node
+ */
 GskRenderNode *
 gsk_shadow_node_get_child (GskRenderNode *node)
 {
@@ -3066,6 +3020,14 @@ gsk_shadow_node_get_child (GskRenderNode *node)
   return self->child;
 }
 
+/**
+ * gsk_shadow_node_peek_shadow:
+ * @node: (type GskShadowNode): a shadow #GskRenderNode
+ *
+ * Retrieves the shadow data at the given index @i.
+ *
+ * Returns: (transfer none): the shadow data
+ */
 const GskShadow *
 gsk_shadow_node_peek_shadow (GskRenderNode *node,
                              gsize          i)
@@ -3078,6 +3040,14 @@ gsk_shadow_node_peek_shadow (GskRenderNode *node,
   return &self->shadows[i];
 }
 
+/**
+ * gsk_shadow_node_get_n_shadows:
+ * @node: (type GskShadowNode): a shadow #GskRenderNode
+ *
+ * Retrieves the number of shadows in the @node.
+ *
+ * Returns: the number of shadows.
+ */
 gsize
 gsk_shadow_node_get_n_shadows (GskRenderNode *node)
 {
@@ -3090,8 +3060,6 @@ gsk_shadow_node_get_n_shadows (GskRenderNode *node)
 
 /*** GSK_BLEND_NODE ***/
 
-typedef struct _GskBlendNode GskBlendNode;
-
 struct _GskBlendNode
 {
   GskRenderNode render_node;
@@ -3147,9 +3115,12 @@ static void
 gsk_blend_node_finalize (GskRenderNode *node)
 {
   GskBlendNode *self = (GskBlendNode *) node;
+  GskRenderNodeClass *parent_class = g_type_class_peek (g_type_parent (GSK_TYPE_BLEND_NODE));
 
   gsk_render_node_unref (self->bottom);
   gsk_render_node_unref (self->top);
+
+  parent_class->finalize (node);
 }
 
 static void
@@ -3191,18 +3162,8 @@ gsk_blend_node_diff (GskRenderNode  *node1,
     }
 }
 
-static const GskRenderNodeClass GSK_BLEND_NODE_CLASS = {
-  GSK_BLEND_NODE,
-  sizeof (GskBlendNode),
-  "GskBlendNode",
-  gsk_blend_node_finalize,
-  gsk_blend_node_draw,
-  gsk_render_node_can_diff_true,
-  gsk_blend_node_diff,
-};
-
 /**
- * gsk_blend_node_new:
+ * gsk_blend_node_new: 
  * @bottom: The bottom node to be drawn
  * @top: The node to be blended onto the @bottom node
  * @blend_mode: The blend mode to use
@@ -3210,7 +3171,7 @@ static const GskRenderNodeClass GSK_BLEND_NODE_CLASS = {
  * Creates a #GskRenderNode that will use @blend_mode to blend the @top
  * node onto the @bottom node.
  *
- * Returns: A new #GskRenderNode
+ * Returns: (transfer full): A new #GskRenderNode
  */
 GskRenderNode *
 gsk_blend_node_new (GskRenderNode *bottom,
@@ -3218,21 +3179,31 @@ gsk_blend_node_new (GskRenderNode *bottom,
                     GskBlendMode   blend_mode)
 {
   GskBlendNode *self;
+  GskRenderNode *node;
 
   g_return_val_if_fail (GSK_IS_RENDER_NODE (bottom), NULL);
   g_return_val_if_fail (GSK_IS_RENDER_NODE (top), NULL);
 
-  self = (GskBlendNode *) gsk_render_node_new (&GSK_BLEND_NODE_CLASS, 0);
+  self = gsk_render_node_alloc (GSK_BLEND_NODE);
+  node = (GskRenderNode *) self;
 
   self->bottom = gsk_render_node_ref (bottom);
   self->top = gsk_render_node_ref (top);
   self->blend_mode = blend_mode;
 
-  graphene_rect_union (&bottom->bounds, &top->bounds, &self->render_node.bounds);
+  graphene_rect_union (&bottom->bounds, &top->bounds, &node->bounds);
 
-  return &self->render_node;
+  return node;
 }
 
+/**
+ * gsk_blend_node_get_bottom_child:
+ * @node: (type GskBlendNode): a blending #GskRenderNode
+ *
+ * Retrieves the bottom #GskRenderNode child of the @node.
+ *
+ * Returns: (transfer none): the bottom child node
+ */
 GskRenderNode *
 gsk_blend_node_get_bottom_child (GskRenderNode *node)
 {
@@ -3243,6 +3214,14 @@ gsk_blend_node_get_bottom_child (GskRenderNode *node)
   return self->bottom;
 }
 
+/**
+ * gsk_blend_node_get_top_child:
+ * @node: (type GskBlendNode): a blending #GskRenderNode
+ *
+ * Retrieves the top #GskRenderNode child of the @node.
+ *
+ * Returns: (transfer none): the top child node
+ */
 GskRenderNode *
 gsk_blend_node_get_top_child (GskRenderNode *node)
 {
@@ -3253,6 +3232,14 @@ gsk_blend_node_get_top_child (GskRenderNode *node)
   return self->top;
 }
 
+/**
+ * gsk_blend_node_get_blend_mode:
+ * @node: (type GskBlendNode): a blending #GskRenderNode
+ *
+ * Retrieves the blend mode used by @node.
+ *
+ * Returns: the blend mode
+ */
 GskBlendMode
 gsk_blend_node_get_blend_mode (GskRenderNode *node)
 {
@@ -3265,8 +3252,6 @@ gsk_blend_node_get_blend_mode (GskRenderNode *node)
 
 /*** GSK_CROSS_FADE_NODE ***/
 
-typedef struct _GskCrossFadeNode GskCrossFadeNode;
-
 struct _GskCrossFadeNode
 {
   GskRenderNode render_node;
@@ -3280,9 +3265,12 @@ static void
 gsk_cross_fade_node_finalize (GskRenderNode *node)
 {
   GskCrossFadeNode *self = (GskCrossFadeNode *) node;
+  GskRenderNodeClass *parent_class = g_type_class_peek (g_type_parent (GSK_TYPE_CROSS_FADE_NODE));
 
   gsk_render_node_unref (self->start);
   gsk_render_node_unref (self->end);
+
+  parent_class->finalize (node);
 }
 
 static void
@@ -3323,18 +3311,8 @@ gsk_cross_fade_node_diff (GskRenderNode  *node1,
   gsk_render_node_diff_impossible (node1, node2, region);
 }
 
-static const GskRenderNodeClass GSK_CROSS_FADE_NODE_CLASS = {
-  GSK_CROSS_FADE_NODE,
-  sizeof (GskCrossFadeNode),
-  "GskCrossFadeNode",
-  gsk_cross_fade_node_finalize,
-  gsk_cross_fade_node_draw,
-  gsk_render_node_can_diff_true,
-  gsk_cross_fade_node_diff,
-};
-
 /**
- * gsk_cross_fade_node_new:
+ * gsk_cross_fade_node_new: 
  * @start: The start node to be drawn
  * @end: The node to be cross_fadeed onto the @start node
  * @progress: How far the fade has progressed from start to end. The value will
@@ -3342,7 +3320,7 @@ static const GskRenderNodeClass GSK_CROSS_FADE_NODE_CLASS = {
  *
  * Creates a #GskRenderNode that will do a cross-fade between @start and @end.
  *
- * Returns: A new #GskRenderNode
+ * Returns: (transfer full): A new #GskRenderNode
  */
 GskRenderNode *
 gsk_cross_fade_node_new (GskRenderNode *start,
@@ -3350,21 +3328,31 @@ gsk_cross_fade_node_new (GskRenderNode *start,
                          float          progress)
 {
   GskCrossFadeNode *self;
+  GskRenderNode *node;
 
   g_return_val_if_fail (GSK_IS_RENDER_NODE (start), NULL);
   g_return_val_if_fail (GSK_IS_RENDER_NODE (end), NULL);
 
-  self = (GskCrossFadeNode *) gsk_render_node_new (&GSK_CROSS_FADE_NODE_CLASS, 0);
+  self = gsk_render_node_alloc (GSK_CROSS_FADE_NODE);
+  node = (GskRenderNode *) self;
 
   self->start = gsk_render_node_ref (start);
   self->end = gsk_render_node_ref (end);
   self->progress = CLAMP (progress, 0.0, 1.0);
 
-  graphene_rect_union (&start->bounds, &end->bounds, &self->render_node.bounds);
+  graphene_rect_union (&start->bounds, &end->bounds, &node->bounds);
 
-  return &self->render_node;
+  return node;
 }
 
+/**
+ * gsk_cross_fade_node_get_start_child:
+ * @node: (type GskCrossFadeNode): a cross-fading #GskRenderNode
+ *
+ * Retrieves the child #GskRenderNode at the beginning of the cross-fade.
+ *
+ * Returns: (transfer none): a #GskRenderNode
+ */
 GskRenderNode *
 gsk_cross_fade_node_get_start_child (GskRenderNode *node)
 {
@@ -3375,6 +3363,14 @@ gsk_cross_fade_node_get_start_child (GskRenderNode *node)
   return self->start;
 }
 
+/**
+ * gsk_cross_fade_node_get_end_child:
+ * @node: (type GskCrossFadeNode): a cross-fading #GskRenderNode
+ *
+ * Retrieves the child #GskRenderNode at the end of the cross-fade.
+ *
+ * Returns: (transfer none): a #GskRenderNode
+ */
 GskRenderNode *
 gsk_cross_fade_node_get_end_child (GskRenderNode *node)
 {
@@ -3385,6 +3381,14 @@ gsk_cross_fade_node_get_end_child (GskRenderNode *node)
   return self->end;
 }
 
+/**
+ * gsk_cross_fade_node_get_progress:
+ * @node: (type GskCrossFadeNode): a cross-fading #GskRenderNode
+ *
+ * Retrieves the progress value of the cross fade.
+ *
+ * Returns: the progress value, between 0 and 1
+ */
 float
 gsk_cross_fade_node_get_progress (GskRenderNode *node)
 {
@@ -3397,8 +3401,6 @@ gsk_cross_fade_node_get_progress (GskRenderNode *node)
 
 /*** GSK_TEXT_NODE ***/
 
-typedef struct _GskTextNode GskTextNode;
-
 struct _GskTextNode
 {
   GskRenderNode render_node;
@@ -3410,15 +3412,19 @@ struct _GskTextNode
   graphene_point_t offset;
 
   guint num_glyphs;
-  PangoGlyphInfo glyphs[];
+  PangoGlyphInfo *glyphs;
 };
 
 static void
 gsk_text_node_finalize (GskRenderNode *node)
 {
   GskTextNode *self = (GskTextNode *) node;
+  GskRenderNodeClass *parent_class = g_type_class_peek (g_type_parent (GSK_TYPE_TEXT_NODE));
 
   g_object_unref (self->font);
+  g_free (self->glyphs);
+
+  parent_class->finalize (node);
 }
 
 static void
@@ -3478,16 +3484,6 @@ gsk_text_node_diff (GskRenderNode  *node1,
   gsk_render_node_diff_impossible (node1, node2, region);
 }
 
-static const GskRenderNodeClass GSK_TEXT_NODE_CLASS = {
-  GSK_TEXT_NODE,
-  sizeof (GskTextNode),
-  "GskTextNode",
-  gsk_text_node_finalize,
-  gsk_text_node_draw,
-  gsk_render_node_can_diff_true,
-  gsk_text_node_diff,
-};
-
 static gboolean
 font_has_color_glyphs (const PangoFont *font)
 {
@@ -3506,7 +3502,7 @@ font_has_color_glyphs (const PangoFont *font)
 }
 
 /**
- * gsk_text_node_new:
+ * gsk_text_node_new: 
  * @font: the #PangoFont containing the glyphs
  * @glyphs: the #PangoGlyphString to render
  * @color: the foreground color to render with
@@ -3516,7 +3512,7 @@ font_has_color_glyphs (const PangoFont *font)
  * Note that @color may not be used if the font contains
  * color glyphs.
  *
- * Returns: (nullable): a new text node, or %NULL
+ * Returns: (nullable) (transfer full): a new text node, or %NULL
  */
 GskRenderNode *
 gsk_text_node_new (PangoFont              *font,
@@ -3525,6 +3521,7 @@ gsk_text_node_new (PangoFont              *font,
                    const graphene_point_t *offset)
 {
   GskTextNode *self;
+  GskRenderNode *node;
   PangoRectangle ink_rect;
 
   pango_glyph_string_extents (glyphs, font, &ink_rect, NULL);
@@ -3534,24 +3531,34 @@ gsk_text_node_new (PangoFont              *font,
   if (ink_rect.width == 0 || ink_rect.height == 0)
     return NULL;
 
-  self = (GskTextNode *) gsk_render_node_new (&GSK_TEXT_NODE_CLASS, sizeof (PangoGlyphInfo) * 
glyphs->num_glyphs);
+  self = gsk_render_node_alloc (GSK_TEXT_NODE);
+  node = (GskRenderNode *) self;
 
   self->font = g_object_ref (font);
   self->has_color_glyphs = font_has_color_glyphs (font);
   self->color = *color;
   self->offset = *offset;
   self->num_glyphs = glyphs->num_glyphs;
-  memcpy (self->glyphs, glyphs->glyphs, sizeof (PangoGlyphInfo) * glyphs->num_glyphs);
+  self->glyphs = g_malloc_n (glyphs->num_glyphs, sizeof (PangoGlyphInfo));
+  memcpy (self->glyphs, glyphs->glyphs, glyphs->num_glyphs * sizeof (PangoGlyphInfo));
 
-  graphene_rect_init (&self->render_node.bounds,
+  graphene_rect_init (&node->bounds,
                       offset->x + ink_rect.x - 1,
                       offset->y + ink_rect.y - 1,
                       ink_rect.width + 2,
                       ink_rect.height + 2);
 
-  return &self->render_node;
+  return node;
 }
 
+/**
+ * gsk_text_node_peek_color:
+ * @node: (type GskTextNode): a text #GskRenderNode
+ *
+ * Retrieves the color used by the text @node.
+ *
+ * Returns: (transfer none): the text color
+ */
 const GdkRGBA *
 gsk_text_node_peek_color (GskRenderNode *node)
 {
@@ -3566,9 +3573,9 @@ gsk_text_node_peek_color (GskRenderNode *node)
  * gsk_text_node_peek_font:
  * @node: The #GskRenderNode
  *
- * Returns the font used by the text node.
+ * Returns the font used by the text @node.
  *
- * Returns: (transfer none): The used #PangoFont.
+ * Returns: (transfer none): the font
  */
 PangoFont *
 gsk_text_node_peek_font (GskRenderNode *node)
@@ -3580,6 +3587,14 @@ gsk_text_node_peek_font (GskRenderNode *node)
   return self->font;
 }
 
+/**
+ * gsk_text_node_has_color_glyphs:
+ * @node: (type GskTextNode): a text #GskRenderNode
+ *
+ * Checks whether the text @node has color glyphs.
+ *
+ * Returns: %TRUE if the text node has color glyphs
+ */
 gboolean
 gsk_text_node_has_color_glyphs (GskRenderNode *node)
 {
@@ -3600,16 +3615,37 @@ gsk_text_node_get_num_glyphs (GskRenderNode *node)
   return self->num_glyphs;
 }
 
+/**
+ * gsk_text_node_peek_glyphs:
+ * @node: (type GskTextNode): a text #GskRenderNode
+ * @n_glyphs: (out) (optional): the number of glyphs returned
+ *
+ * Retrieves the glyph information in the @node.
+ *
+ * Returns: (transfer none) (array length=n_glyphs): the glyph information
+ */
 const PangoGlyphInfo *
-gsk_text_node_peek_glyphs (GskRenderNode *node)
+gsk_text_node_peek_glyphs (GskRenderNode *node,
+                           guint         *n_glyphs)
 {
   GskTextNode *self = (GskTextNode *) node;
 
   g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_TEXT_NODE), NULL);
 
+  if (n_glyphs != NULL)
+    *n_glyphs = self->num_glyphs;
+
   return self->glyphs;
 }
 
+/**
+ * gsk_text_node_get_offset:
+ * @node: (type GskTextNode): a text #GskRenderNode
+ *
+ * Retrieves the offset applied to the text.
+ *
+ * Returns: (transfer none): a point with the horizontal and vertical offsets
+ */
 const graphene_point_t *
 gsk_text_node_get_offset (GskRenderNode *node)
 {
@@ -3622,8 +3658,6 @@ gsk_text_node_get_offset (GskRenderNode *node)
 
 /*** GSK_BLUR_NODE ***/
 
-typedef struct _GskBlurNode GskBlurNode;
-
 struct _GskBlurNode
 {
   GskRenderNode render_node;
@@ -3636,8 +3670,11 @@ static void
 gsk_blur_node_finalize (GskRenderNode *node)
 {
   GskBlurNode *self = (GskBlurNode *) node;
+  GskRenderNodeClass *parent_class = g_type_class_peek (g_type_parent (GSK_TYPE_BLUR_NODE));
 
   gsk_render_node_unref (self->child);
+
+  parent_class->finalize (node);
 }
 
 static void
@@ -3860,44 +3897,49 @@ gsk_blur_node_diff (GskRenderNode  *node1,
     }
 }
 
-static const GskRenderNodeClass GSK_BLUR_NODE_CLASS = {
-  GSK_BLUR_NODE,
-  sizeof (GskBlurNode),
-  "GskBlurNode",
-  gsk_blur_node_finalize,
-  gsk_blur_node_draw,
-  gsk_render_node_can_diff_true,
-  gsk_blur_node_diff,
-};
-
 /**
- * gsk_blur_node_new:
+ * gsk_blur_node_new: 
  * @child: the child node to blur
  * @radius: the blur radius
  *
  * Creates a render node that blurs the child.
+ *
+ * Returns: (transfer full): a new #GskRenderNode
  */
 GskRenderNode *
 gsk_blur_node_new (GskRenderNode *child,
                    float          radius)
 {
   GskBlurNode *self;
-  float clip_radius = gsk_cairo_blur_compute_pixels (radius);
+  GskRenderNode *node;
+  float clip_radius;
 
   g_return_val_if_fail (GSK_IS_RENDER_NODE (child), NULL);
 
-  self = (GskBlurNode *) gsk_render_node_new (&GSK_BLUR_NODE_CLASS, 0);
+  self = gsk_render_node_alloc (GSK_BLUR_NODE);
+  node = (GskRenderNode *) self;
 
   self->child = gsk_render_node_ref (child);
   self->radius = radius;
 
-  graphene_rect_init_from_rect (&self->render_node.bounds, &child->bounds);
+  clip_radius = gsk_cairo_blur_compute_pixels (radius);
+
+  graphene_rect_init_from_rect (&node->bounds, &child->bounds);
   graphene_rect_inset (&self->render_node.bounds,
-                       - clip_radius, - clip_radius);
+                       - clip_radius,
+                       - clip_radius);
 
-  return &self->render_node;
+  return node;
 }
 
+/**
+ * gsk_blur_node_get_child:
+ * @node: (type GskBlurNode): a blur #GskRenderNode
+ *
+ * Retrieves the child #GskRenderNode of the blur @node.
+ *
+ * Returns: (transfer none): the blurred child node
+ */
 GskRenderNode *
 gsk_blur_node_get_child (GskRenderNode *node)
 {
@@ -3908,6 +3950,14 @@ gsk_blur_node_get_child (GskRenderNode *node)
   return self->child;
 }
 
+/**
+ * gsk_blur_node_get_radius:
+ * @node: (type GskBlurNode): a blur #GskRenderNode
+ *
+ * Retrieves the blur radius of the @node.
+ *
+ * Returns: the blur radius
+ */
 float
 gsk_blur_node_get_radius (GskRenderNode *node)
 {
@@ -3917,3 +3967,493 @@ gsk_blur_node_get_radius (GskRenderNode *node)
 
   return self->radius;
 }
+
+/*** GSK_DEBUG_NODE ***/
+
+struct _GskDebugNode
+{
+  GskRenderNode render_node;
+
+  GskRenderNode *child;
+  char *message;
+};
+
+static void
+gsk_debug_node_finalize (GskRenderNode *node)
+{
+  GskDebugNode *self = (GskDebugNode *) node;
+  GskRenderNodeClass *parent_class = g_type_class_peek (g_type_parent (GSK_TYPE_DEBUG_NODE));
+
+  gsk_render_node_unref (self->child);
+  g_free (self->message);
+
+  parent_class->finalize (node);
+}
+
+static void
+gsk_debug_node_draw (GskRenderNode *node,
+                      cairo_t       *cr)
+{
+  GskDebugNode *self = (GskDebugNode *) node;
+
+  gsk_render_node_draw (self->child, cr);
+}
+
+static gboolean
+gsk_debug_node_can_diff (const GskRenderNode *node1,
+                         const GskRenderNode *node2)
+{
+  GskDebugNode *self1 = (GskDebugNode *) node1;
+  GskDebugNode *self2 = (GskDebugNode *) node2;
+
+  return gsk_render_node_can_diff (self1->child, self2->child);
+}
+
+static void
+gsk_debug_node_diff (GskRenderNode  *node1,
+                     GskRenderNode  *node2,
+                     cairo_region_t *region)
+{
+  GskDebugNode *self1 = (GskDebugNode *) node1;
+  GskDebugNode *self2 = (GskDebugNode *) node2;
+
+  gsk_render_node_diff (self1->child, self2->child, region);
+}
+
+/**
+ * gsk_debug_node_new: 
+ * @child: The child to add debug info for
+ * @message: (transfer full): The debug message 
+ *
+ * Creates a #GskRenderNode that will add debug information about
+ * the given @child.
+ *
+ * Adding this node has no visual effect.
+ *
+ * Returns: (transfer full): A new #GskRenderNode
+ */
+GskRenderNode *
+gsk_debug_node_new (GskRenderNode *child,
+                    char          *message)
+{
+  GskDebugNode *self;
+  GskRenderNode *node;
+
+  g_return_val_if_fail (GSK_IS_RENDER_NODE (child), NULL);
+
+  self = gsk_render_node_alloc (GSK_DEBUG_NODE);
+  node = (GskRenderNode *) self;
+
+  self->child = gsk_render_node_ref (child);
+  self->message = message;
+
+  graphene_rect_init_from_rect (&node->bounds, &child->bounds);
+
+  return node;
+}
+
+/**
+ * gsk_debug_node_get_child:
+ * @node: (type GskDebugNode): a debug #GskRenderNode
+ *
+ * Gets the child node that is getting drawn by the given @node.
+ *
+ * Returns: (transfer none): the child #GskRenderNode
+ **/
+GskRenderNode *
+gsk_debug_node_get_child (GskRenderNode *node)
+{
+  GskDebugNode *self = (GskDebugNode *) node;
+
+  g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_DEBUG_NODE), NULL);
+
+  return self->child;
+}
+
+/**
+ * gsk_debug_node_get_message:
+ * @node: (type GskDebugNode): a debug #GskRenderNode
+ *
+ * Gets the debug message that was set on this node
+ *
+ * Returns: (transfer none): The debug message
+ **/
+const char *
+gsk_debug_node_get_message (GskRenderNode *node)
+{
+  GskDebugNode *self = (GskDebugNode *) node;
+
+  g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_DEBUG_NODE), "You run broken code!");
+
+  return self->message;
+}
+
+#ifndef I_
+# define I_(str) g_intern_static_string ((str))
+#endif
+
+/*< private >
+ * gsk_render_node_init_types:
+ *
+ * Initialize all the #GskRenderNode types provided by GSK.
+ */
+void
+gsk_render_node_init_types (void)
+{
+  {
+    const GskRenderNodeTypeInfo node_info =
+    {
+      GSK_CONTAINER_NODE,
+      sizeof (GskContainerNode),
+      NULL,
+      gsk_container_node_finalize,
+      gsk_container_node_draw,
+      NULL,
+      gsk_container_node_diff,
+    };
+
+    GType node_type = gsk_render_node_type_register_static (I_("GskContainerNode"), &node_info);
+    gsk_render_node_types[GSK_CONTAINER_NODE] = node_type;
+    g_assert (GSK_TYPE_CONTAINER_NODE == node_type);
+  }
+
+  {
+    const GskRenderNodeTypeInfo node_info =
+    {
+      GSK_CAIRO_NODE,
+      sizeof (GskCairoNode),
+      NULL,
+      gsk_cairo_node_finalize,
+      gsk_cairo_node_draw,
+      NULL,
+      NULL,
+    };
+
+    GType node_type = gsk_render_node_type_register_static (I_("GskCairoNode"), &node_info);
+    gsk_render_node_types[GSK_CAIRO_NODE] = node_type;
+    g_assert (GSK_TYPE_CAIRO_NODE == node_type);
+  }
+
+  {
+    const GskRenderNodeTypeInfo node_info =
+    {
+      GSK_COLOR_NODE,
+      sizeof (GskColorNode),
+      NULL,
+      NULL,
+      gsk_color_node_draw,
+      NULL,
+      gsk_color_node_diff,
+    };
+
+    GType node_type = gsk_render_node_type_register_static (I_("GskColorNode"), &node_info);
+    gsk_render_node_types[GSK_COLOR_NODE] = node_type;
+    g_assert (GSK_TYPE_COLOR_NODE == node_type);
+  }
+
+  {
+    const GskRenderNodeTypeInfo node_info =
+    {
+      GSK_LINEAR_GRADIENT_NODE,
+      sizeof (GskLinearGradientNode),
+      NULL,
+      gsk_linear_gradient_node_finalize,
+      gsk_linear_gradient_node_draw,
+      NULL,
+      gsk_linear_gradient_node_diff,
+    };
+
+    GType node_type = gsk_render_node_type_register_static (I_("GskLinearGradientNode"), &node_info);
+    gsk_render_node_types[GSK_LINEAR_GRADIENT_NODE] = node_type;
+    g_assert (GSK_TYPE_LINEAR_GRADIENT_NODE == node_type);
+  }
+
+  {
+    const GskRenderNodeTypeInfo node_info =
+    {
+      GSK_REPEATING_LINEAR_GRADIENT_NODE,
+      sizeof (GskLinearGradientNode),
+      NULL,
+      gsk_linear_gradient_node_finalize,
+      gsk_linear_gradient_node_draw,
+      NULL,
+      gsk_linear_gradient_node_diff,
+    };
+
+    GType node_type = gsk_render_node_type_register_static (I_("GskRepeatingLinearGradientNode"), 
&node_info);
+    gsk_render_node_types[GSK_REPEATING_LINEAR_GRADIENT_NODE] = node_type;
+    g_assert (GSK_TYPE_REPEATING_LINEAR_GRADIENT_NODE == node_type);
+  }
+
+  {
+    const GskRenderNodeTypeInfo node_info =
+    {
+      GSK_BORDER_NODE,
+      sizeof (GskBorderNode),
+      NULL,
+      NULL,
+      gsk_border_node_draw,
+      NULL,
+      gsk_border_node_diff,
+    };
+
+    GType node_type = gsk_render_node_type_register_static (I_("GskBorderNode"), &node_info);
+    gsk_render_node_types[GSK_BORDER_NODE] = node_type;
+    g_assert (GSK_TYPE_BORDER_NODE == node_type);
+  }
+
+  {
+    const GskRenderNodeTypeInfo node_info =
+    {
+      GSK_TEXTURE_NODE,
+      sizeof (GskTextureNode),
+      NULL,
+      gsk_texture_node_finalize,
+      gsk_texture_node_draw,
+      NULL,
+      gsk_texture_node_diff,
+    };
+
+    GType node_type = gsk_render_node_type_register_static (I_("GskTextureNode"), &node_info);
+    gsk_render_node_types[GSK_TEXTURE_NODE] = node_type;
+    g_assert (GSK_TYPE_TEXTURE_NODE == node_type);
+  }
+
+  {
+    const GskRenderNodeTypeInfo node_info =
+    {
+      GSK_INSET_SHADOW_NODE,
+      sizeof (GskInsetShadowNode),
+      NULL,
+      NULL,
+      gsk_inset_shadow_node_draw,
+      NULL,
+      gsk_inset_shadow_node_diff,
+    };
+
+    GType node_type = gsk_render_node_type_register_static (I_("GskInsetShadowNode"), &node_info);
+    gsk_render_node_types[GSK_INSET_SHADOW_NODE] = node_type;
+    g_assert (GSK_TYPE_INSET_SHADOW_NODE == node_type);
+  }
+
+  {
+    const GskRenderNodeTypeInfo node_info =
+    {
+      GSK_OUTSET_SHADOW_NODE,
+      sizeof (GskOutsetShadowNode),
+      NULL,
+      NULL,
+      gsk_outset_shadow_node_draw,
+      NULL,
+      gsk_outset_shadow_node_diff,
+    };
+
+    GType node_type = gsk_render_node_type_register_static (I_("GskOutsetShadowNode"), &node_info);
+    gsk_render_node_types[GSK_OUTSET_SHADOW_NODE] = node_type;
+    g_assert (GSK_TYPE_OUTSET_SHADOW_NODE == node_type);
+  }
+
+  {
+    const GskRenderNodeTypeInfo node_info =
+    {
+      GSK_TRANSFORM_NODE,
+      sizeof (GskTransformNode),
+      NULL,
+      gsk_transform_node_finalize,
+      gsk_transform_node_draw,
+      gsk_transform_node_can_diff,
+      gsk_transform_node_diff,
+    };
+
+    GType node_type = gsk_render_node_type_register_static (I_("GskTransformNode"), &node_info);
+    gsk_render_node_types[GSK_TRANSFORM_NODE] = node_type;
+    g_assert (GSK_TYPE_TRANSFORM_NODE == node_type);
+  }
+
+  {
+    const GskRenderNodeTypeInfo node_info =
+    {
+      GSK_OPACITY_NODE,
+      sizeof (GskOpacityNode),
+      NULL,
+      gsk_opacity_node_finalize,
+      gsk_opacity_node_draw,
+      NULL,
+      gsk_opacity_node_diff,
+    };
+
+    GType node_type = gsk_render_node_type_register_static (I_("GskOpacityNode"), &node_info);
+    gsk_render_node_types[GSK_OPACITY_NODE] = node_type;
+    g_assert (GSK_TYPE_OPACITY_NODE == node_type);
+  }
+
+  {
+    const GskRenderNodeTypeInfo node_info =
+    {
+      GSK_COLOR_MATRIX_NODE,
+      sizeof (GskColorMatrixNode),
+      NULL,
+      gsk_color_matrix_node_finalize,
+      gsk_color_matrix_node_draw,
+      NULL,
+      NULL,
+    };
+
+    GType node_type = gsk_render_node_type_register_static (I_("GskColorMatrixNode"), &node_info);
+    gsk_render_node_types[GSK_COLOR_MATRIX_NODE] = node_type;
+    g_assert (GSK_TYPE_COLOR_MATRIX_NODE == node_type);
+  }
+
+  {
+    const GskRenderNodeTypeInfo node_info =
+    {
+      GSK_REPEAT_NODE,
+      sizeof (GskRepeatNode),
+      NULL,
+      gsk_repeat_node_finalize,
+      gsk_repeat_node_draw,
+      NULL,
+      NULL,
+    };
+
+    GType node_type = gsk_render_node_type_register_static (I_("GskRepeatNode"), &node_info);
+    gsk_render_node_types[GSK_REPEAT_NODE] = node_type;
+    g_assert (GSK_TYPE_REPEAT_NODE == node_type);
+  }
+
+  {
+    const GskRenderNodeTypeInfo node_info =
+    {
+      GSK_CLIP_NODE,
+      sizeof (GskClipNode),
+      NULL,
+      gsk_clip_node_finalize,
+      gsk_clip_node_draw,
+      NULL,
+      gsk_clip_node_diff,
+    };
+
+    GType node_type = gsk_render_node_type_register_static (I_("GskClipNode"), &node_info);
+    gsk_render_node_types[GSK_CLIP_NODE] = node_type;
+    g_assert (GSK_TYPE_CLIP_NODE == node_type);
+  }
+
+  {
+    const GskRenderNodeTypeInfo node_info =
+    {
+      GSK_ROUNDED_CLIP_NODE,
+      sizeof (GskRoundedClipNode),
+      NULL,
+      gsk_rounded_clip_node_finalize,
+      gsk_rounded_clip_node_draw,
+      NULL,
+      gsk_rounded_clip_node_diff,
+    };
+
+    GType node_type = gsk_render_node_type_register_static (I_("GskRoundedClipNode"), &node_info);
+    gsk_render_node_types[GSK_ROUNDED_CLIP_NODE] = node_type;
+    g_assert (GSK_TYPE_ROUNDED_CLIP_NODE == node_type);
+  }
+
+  {
+    const GskRenderNodeTypeInfo node_info =
+    {
+      GSK_SHADOW_NODE,
+      sizeof (GskShadowNode),
+      NULL,
+      gsk_shadow_node_finalize,
+      gsk_shadow_node_draw,
+      NULL,
+      gsk_shadow_node_diff,
+    };
+
+    GType node_type = gsk_render_node_type_register_static (I_("GskShadowNode"), &node_info);
+    gsk_render_node_types[GSK_SHADOW_NODE] = node_type;
+    g_assert (GSK_TYPE_SHADOW_NODE == node_type);
+  }
+
+  {
+    const GskRenderNodeTypeInfo node_info =
+    {
+      GSK_BLEND_NODE,
+      sizeof (GskBlendNode),
+      NULL,
+      gsk_blend_node_finalize,
+      gsk_blend_node_draw,
+      NULL,
+      gsk_blend_node_diff,
+    };
+
+    GType node_type = gsk_render_node_type_register_static (I_("GskBlendNode"), &node_info);
+    gsk_render_node_types[GSK_BLEND_NODE] = node_type;
+    g_assert (GSK_TYPE_BLEND_NODE == node_type);
+  }
+
+  {
+    const GskRenderNodeTypeInfo node_info =
+    {
+      GSK_CROSS_FADE_NODE,
+      sizeof (GskCrossFadeNode),
+      NULL,
+      gsk_cross_fade_node_finalize,
+      gsk_cross_fade_node_draw,
+      NULL,
+      gsk_cross_fade_node_diff,
+    };
+
+    GType node_type = gsk_render_node_type_register_static (I_("GskCrossFadeNode"), &node_info);
+    gsk_render_node_types[GSK_CROSS_FADE_NODE] = node_type;
+    g_assert (GSK_TYPE_CROSS_FADE_NODE == node_type);
+  }
+
+  {
+    const GskRenderNodeTypeInfo node_info =
+    {
+      GSK_TEXT_NODE,
+      sizeof (GskTextNode),
+      NULL,
+      gsk_text_node_finalize,
+      gsk_text_node_draw,
+      NULL,
+      gsk_text_node_diff,
+    };
+
+    GType node_type = gsk_render_node_type_register_static (I_("GskTextNode"), &node_info);
+    gsk_render_node_types[GSK_TEXT_NODE] = node_type;
+    g_assert (GSK_TYPE_TEXT_NODE == node_type);
+  }
+
+  {
+    const GskRenderNodeTypeInfo node_info =
+    {
+      GSK_BLUR_NODE,
+      sizeof (GskBlurNode),
+      NULL,
+      gsk_blur_node_finalize,
+      gsk_blur_node_draw,
+      NULL,
+      gsk_blur_node_diff,
+    };
+
+    GType node_type = gsk_render_node_type_register_static (I_("GskBlurNode"), &node_info);
+    gsk_render_node_types[GSK_BLUR_NODE] = node_type;
+    g_assert (GSK_TYPE_BLUR_NODE == node_type);
+  }
+
+  {
+    const GskRenderNodeTypeInfo node_info =
+    {
+      GSK_DEBUG_NODE,
+      sizeof (GskDebugNode),
+      NULL,
+      gsk_debug_node_finalize,
+      gsk_debug_node_draw,
+      gsk_debug_node_can_diff,
+      gsk_debug_node_diff,
+    };
+
+    GType node_type = gsk_render_node_type_register_static (I_("GskDebugNode"), &node_info);
+    gsk_render_node_types[GSK_DEBUG_NODE] = node_type;
+    g_assert (GSK_TYPE_DEBUG_NODE == node_type);
+  }
+}
diff --git a/gsk/gskrendernodeparser.c b/gsk/gskrendernodeparser.c
index 02959bce93..fcacb8b888 100644
--- a/gsk/gskrendernodeparser.c
+++ b/gsk/gskrendernodeparser.c
@@ -1973,9 +1973,9 @@ render_node_print (Printer       *p,
     case GSK_REPEATING_LINEAR_GRADIENT_NODE:
     case GSK_LINEAR_GRADIENT_NODE:
       {
-        const guint n_stops = gsk_linear_gradient_node_get_n_color_stops (node);
-        const GskColorStop *stops = gsk_linear_gradient_node_peek_color_stops (node);
-        int i;
+        const gsize n_stops = gsk_linear_gradient_node_get_n_color_stops (node);
+        const GskColorStop *stops = gsk_linear_gradient_node_peek_color_stops (node, NULL);
+        gsize i;
 
         if (gsk_render_node_get_node_type (node) == GSK_REPEATING_LINEAR_GRADIENT_NODE)
           start_node (p, "repeating-linear-gradient");
@@ -2229,7 +2229,7 @@ render_node_print (Printer       *p,
     case GSK_TEXT_NODE:
       {
         const guint n_glyphs = gsk_text_node_get_num_glyphs (node);
-        const PangoGlyphInfo *glyphs = gsk_text_node_peek_glyphs (node);
+        const PangoGlyphInfo *glyphs = gsk_text_node_peek_glyphs (node, NULL);
         const graphene_point_t *offset = gsk_text_node_get_offset (node);
         const GdkRGBA *color = gsk_text_node_peek_color (node);
         PangoFont *font = gsk_text_node_peek_font (node);
@@ -2449,7 +2449,7 @@ render_node_print (Printer       *p,
       break;
 
     default:
-      g_error ("Unhandled node: %s", node->node_class->type_name);
+      g_error ("Unhandled node: %s", g_type_name_from_instance ((GTypeInstance *) node));
       break;
     }
 }
diff --git a/gsk/gskrendernodeprivate.h b/gsk/gskrendernodeprivate.h
index 83d01740a5..2ca03419e5 100644
--- a/gsk/gskrendernodeprivate.h
+++ b/gsk/gskrendernodeprivate.h
@@ -8,22 +8,30 @@ G_BEGIN_DECLS
 
 typedef struct _GskRenderNodeClass GskRenderNodeClass;
 
-#define GSK_IS_RENDER_NODE_TYPE(node,type) (GSK_IS_RENDER_NODE (node) && (node)->node_class->node_type == 
(type))
+/* Keep this in sync with the GskRenderNodeType enumeration.
+ *
+ * We don't add an "n-types" value to avoid having to handle
+ * it in every single switch.
+ */
+#define GSK_RENDER_NODE_TYPE_N_TYPES    (GSK_DEBUG_NODE + 1)
+
+#define GSK_IS_RENDER_NODE_TYPE(node,type) \
+  (G_TYPE_INSTANCE_GET_CLASS ((node), GSK_TYPE_RENDER_NODE, GskRenderNodeClass)->node_type == (type))
 
 struct _GskRenderNode
 {
-  const GskRenderNodeClass *node_class;
+  GTypeInstance parent_instance;
 
-  volatile int ref_count;
+  gatomicrefcount ref_count;
 
   graphene_rect_t bounds;
 };
 
 struct _GskRenderNodeClass
 {
+  GTypeClass parent_class;
+
   GskRenderNodeType node_type;
-  gsize struct_size;
-  const char *type_name;
 
   void            (* finalize)    (GskRenderNode  *node);
   void            (* draw)        (GskRenderNode  *node,
@@ -35,18 +43,54 @@ struct _GskRenderNodeClass
                                    cairo_region_t *region);
 };
 
-GskRenderNode * gsk_render_node_new              (const GskRenderNodeClass  *node_class,
-                                                  gsize                      extra_size);
+/*< private >
+ * GskRenderNodeTypeInfo:
+ * @node_type: the render node type in the #GskRenderNodeType enumeration
+ * @instance_size: the size of the render node instance
+ * @instance_init: (nullable): the instance initialization function
+ * @finalize: (nullable): the instance finalization function; must chain up to the
+ *   implementation of the parent class
+ * @draw: the function called by gsk_render_node_draw()
+ * @can_diff: (nullable): the function called by gsk_render_node_can_diff(); if
+ *   unset, gsk_render_node_can_diff_true() will be used
+ * @diff: (nullable): the function called by gsk_render_node_diff(); if unset,
+ *   gsk_render_node_diff_impossible() will be used
+ *
+ * A struction that contains the type information for a #GskRenderNode subclass,
+ * to be used by gsk_render_node_type_register_static().
+ */
+typedef struct
+{
+  GskRenderNodeType node_type;
+
+  gsize instance_size;
+
+  void            (* instance_init) (GskRenderNode        *node);
+  void            (* finalize)      (GskRenderNode        *node);
+  void            (* draw)          (GskRenderNode        *node,
+                                     cairo_t              *cr);
+  gboolean        (* can_diff)      (const GskRenderNode  *node1,
+                                     const GskRenderNode  *node2);
+  void            (* diff)          (GskRenderNode        *node1,
+                                     GskRenderNode        *node2,
+                                     cairo_region_t       *region);
+} GskRenderNodeTypeInfo;
+
+void            gsk_render_node_init_types              (void);
+
+GType           gsk_render_node_type_register_static    (const char                  *node_name,
+                                                         const GskRenderNodeTypeInfo *node_info);
 
-gboolean        gsk_render_node_can_diff         (const GskRenderNode       *node1,
-                                                  const GskRenderNode       *node2) G_GNUC_PURE;
-void            gsk_render_node_diff             (GskRenderNode             *node1,
-                                                  GskRenderNode             *node2,
-                                                  cairo_region_t            *region);
-void            gsk_render_node_diff_impossible  (GskRenderNode             *node1,
-                                                  GskRenderNode             *node2,
-                                                  cairo_region_t            *region);
+gpointer        gsk_render_node_alloc                   (GskRenderNodeType            node_type);
 
+gboolean        gsk_render_node_can_diff                (const GskRenderNode         *node1,
+                                                         const GskRenderNode         *node2) G_GNUC_PURE;
+void            gsk_render_node_diff                    (GskRenderNode               *node1,
+                                                         GskRenderNode               *node2,
+                                                         cairo_region_t              *region);
+void            gsk_render_node_diff_impossible         (GskRenderNode               *node1,
+                                                         GskRenderNode               *node2,
+                                                         cairo_region_t              *region);
 
 G_END_DECLS
 
diff --git a/gsk/vulkan/gskvulkanrenderpass.c b/gsk/vulkan/gskvulkanrenderpass.c
index fb19d8d04d..c622a3b14b 100644
--- a/gsk/vulkan/gskvulkanrenderpass.c
+++ b/gsk/vulkan/gskvulkanrenderpass.c
@@ -258,7 +258,7 @@ gsk_vulkan_render_pass_add_node (GskVulkanRenderPass           *self,
       return;
     case GSK_SHADOW_NODE:
     default:
-      FALLBACK ("Unsupported node '%s'", node->node_class->type_name);
+      FALLBACK ("Unsupported node '%s'", g_type_name_from_instance ((GTypeInstance *) node));
 
     case GSK_REPEAT_NODE:
       if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds))
@@ -346,7 +346,7 @@ gsk_vulkan_render_pass_add_node (GskVulkanRenderPass           *self,
     case GSK_TEXT_NODE:
       {
         const PangoFont *font = gsk_text_node_peek_font (node);
-        const PangoGlyphInfo *glyphs = gsk_text_node_peek_glyphs (node);
+        const PangoGlyphInfo *glyphs = gsk_text_node_peek_glyphs (node, NULL);
         guint num_glyphs = gsk_text_node_get_num_glyphs (node);
         gboolean has_color_glyphs = gsk_text_node_has_color_glyphs (node);
         int i;
@@ -817,7 +817,7 @@ gsk_vulkan_render_pass_upload_fallback (GskVulkanRenderPass  *self,
             g_message ("Upload op=%s, node %s[%p], bounds %gx%g",
                      op->type == GSK_VULKAN_OP_FALLBACK_CLIP ? "fallback-clip" :
                      (op->type == GSK_VULKAN_OP_FALLBACK_ROUNDED_CLIP ? "fallback-rounded-clip" : 
"fallback"),
-                     node->node_class->type_name, node,
+                     g_type_name_from_instance ((GTypeInstance *) node), node,
                      ceil (node->bounds.size.width),
                      ceil (node->bounds.size.height)));
 #ifdef G_ENABLE_DEBUG
@@ -1195,7 +1195,7 @@ gsk_vulkan_render_pass_collect_vertex_data (GskVulkanRenderPass *self,
                                                           &op->text.node->bounds,
                                                           (PangoFont *)gsk_text_node_peek_font 
(op->text.node),
                                                           gsk_text_node_get_num_glyphs (op->text.node),
-                                                          gsk_text_node_peek_glyphs (op->text.node),
+                                                          gsk_text_node_peek_glyphs (op->text.node, NULL),
                                                           gsk_text_node_peek_color (op->text.node),
                                                           gsk_text_node_get_offset (op->text.node),
                                                           op->text.start_glyph,
@@ -1214,7 +1214,7 @@ gsk_vulkan_render_pass_collect_vertex_data (GskVulkanRenderPass *self,
                                                                 &op->text.node->bounds,
                                                                 (PangoFont *)gsk_text_node_peek_font 
(op->text.node),
                                                                 gsk_text_node_get_num_glyphs (op->text.node),
-                                                                gsk_text_node_peek_glyphs (op->text.node),
+                                                                gsk_text_node_peek_glyphs (op->text.node, 
NULL),
                                                                 gsk_text_node_get_offset (op->text.node),
                                                                 op->text.start_glyph,
                                                                 op->text.num_glyphs,
@@ -1244,7 +1244,7 @@ gsk_vulkan_render_pass_collect_vertex_data (GskVulkanRenderPass *self,
                                                                      gsk_linear_gradient_node_peek_end 
(op->render.node),
                                                                      gsk_render_node_get_node_type 
(op->render.node) == GSK_REPEATING_LINEAR_GRADIENT_NODE,
                                                                      
gsk_linear_gradient_node_get_n_color_stops (op->render.node),
-                                                                     
gsk_linear_gradient_node_peek_color_stops (op->render.node));
+                                                                     
gsk_linear_gradient_node_peek_color_stops (op->render.node, NULL));
             n_bytes += op->render.vertex_count;
           }
           break;
diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c
index d100e578dd..986e7ae8a0 100644
--- a/gtk/gtkmain.c
+++ b/gtk/gtkmain.c
@@ -95,6 +95,7 @@
 #include "gdk/gdk.h"
 #include "gdk/gdk-private.h"
 #include "gsk/gskprivate.h"
+#include "gsk/gskrendernodeprivate.h"
 
 #include <locale.h>
 
@@ -660,6 +661,7 @@ do_post_parse_initialization (void)
   gtk_widget_set_default_direction (gtk_get_locale_direction ());
 
   gsk_ensure_resources ();
+  gsk_render_node_init_types ();
   _gtk_ensure_resources ();
 
   gtk_initialized = TRUE;
diff --git a/gtk/inspector/recorder.c b/gtk/inspector/recorder.c
index 876ec709e5..b4ed3d965e 100644
--- a/gtk/inspector/recorder.c
+++ b/gtk/inspector/recorder.c
@@ -601,7 +601,7 @@ populate_render_node_properties (GtkListStore  *store,
         const graphene_point_t *start = gsk_linear_gradient_node_peek_start (node);
         const graphene_point_t *end = gsk_linear_gradient_node_peek_end (node);
         const gsize n_stops = gsk_linear_gradient_node_get_n_color_stops (node);
-        const GskColorStop *stops = gsk_linear_gradient_node_peek_color_stops (node);
+        const GskColorStop *stops = gsk_linear_gradient_node_peek_color_stops (node, NULL);
         int i;
         GString *s;
         GdkTexture *texture;
@@ -633,7 +633,7 @@ populate_render_node_properties (GtkListStore  *store,
     case GSK_TEXT_NODE:
       {
         const PangoFont *font = gsk_text_node_peek_font (node);
-        const PangoGlyphInfo *glyphs = gsk_text_node_peek_glyphs (node);
+        const PangoGlyphInfo *glyphs = gsk_text_node_peek_glyphs (node, NULL);
         const GdkRGBA *color = gsk_text_node_peek_color (node);
         guint num_glyphs = gsk_text_node_get_num_glyphs (node);
         const graphene_point_t *offset = gsk_text_node_get_offset (node);


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