[gtk+/wip/ebassi/gsk-1] Snapshot/WIP



commit e847e4853d3db8409f17455a802d58776c6fa248
Author: Emmanuele Bassi <ebassi gnome org>
Date:   Tue Apr 12 09:00:09 2016 +0100

    Snapshot/WIP

 gsk/Makefile.am                                |   39 ++-
 gsk/gskglrenderer.c                            |  744 +++++++++++++++++++++++-
 gsk/gskprivate.c                               |   16 +
 gsk/gskprivate.h                               |   12 +
 gsk/gskrenderer.c                              |   59 ++-
 gsk/gskrendernode.c                            |   55 ++
 gsk/gskrendernode.h                            |    2 +
 gsk/gskrendernodeprivate.h                     |    3 +-
 gsk/resources/glsl/base-renderer-fragment.glsl |   12 +
 gsk/resources/glsl/base-renderer-vertex.glsl   |   15 +
 tests/testgskrenderer.c                        |   43 +-
 11 files changed, 943 insertions(+), 57 deletions(-)
---
diff --git a/gsk/Makefile.am b/gsk/Makefile.am
index f89155d..4a8ec18 100644
--- a/gsk/Makefile.am
+++ b/gsk/Makefile.am
@@ -38,12 +38,16 @@ gsk_private_source_h = \
        gskdebugprivate.h \
        gskglrendererprivate.h \
        gskrendererprivate.h \
-       gskrendernodeprivate.h
-gsk_private_source_c =
+       gskrendernodeprivate.h \
+       gskprivate.h
+gsk_private_source_c = \
+       gskprivate.c
 gsk_built_source_h = \
-       gskenumtypes.h
+       gskenumtypes.h \
+       gskresources.h
 gsk_built_source_c = \
-       gskenumtypes.c
+       gskenumtypes.c \
+       gskresources.c
 gsk_source_c = \
        gskcairorenderer.c \
        gskdebug.c \
@@ -59,7 +63,7 @@ all_sources = \
        $(gsk_private_source_c) \
        $(gsk_source_c)
 
-BUILT_SOURCES += $(gsk_built_source_h) $(gsk_built_source_c)
+BUILT_SOURCES += $(gsk_built_source_h) $(gsk_built_source_c) gsk.resources.xml
 
 gskenumtypes.h: $(gsk_public_source_h) gskenumtypes.h.template
        $(AM_V_GEN) $(GLIB_MKENUMS) --template $(filter %.template,$^) $(filter-out %.template,$^) > \
@@ -74,6 +78,31 @@ gskenumtypes.c: $(gsk_public_source_h) gskenumtypes.c.template
 EXTRA_DIST += gskenumtypes.h.template gskenumtypes.c.template
 DISTCLEANFILES += gskenumtypes.h gskenumtypes.c
 
+resource_files = $(shell $(GLIB_COMPILE_RESOURCES) --sourcedir=$(srcdir) --generate-dependencies 
$(builddir)/gsk.resources.xml)
+
+gsk.resources.xml: Makefile.am
+       $(AM_V_GEN) echo "<?xml version='1.0' encoding='UTF-8'?>" > $@; \
+       echo "<gresources>" >> $@; \
+       echo "  <gresource prefix='/org/gtk/libgsk'>" >> $@; \
+       for f in $(top_srcdir)/gsk/resources/glsl/*; do \
+         n=`basename $$f`; \
+         echo "    <file alias='glsl/$$n'>resources/glsl/$$n</file>" >> $@; \
+       done; \
+       echo "  </gresource>" >> $@; \
+       echo "</gresources>" >> $@
+
+gskresources.h: gsk.resources.xml
+       $(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) $< \
+               --target=$@ --sourcedir=$(srcdir) --c-name _gsk --generate-header --manual-register
+
+gskresources.c: gsk.resources.xml $(resource_files)
+       $(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) $< \
+               --target=$@ --sourcedir=$(srcdir) --c-name _gsk --generate-source --manual-register
+
+EXTRA_DIST += $(resource_files)
+CLEANFILES += gsk.resources.xml
+DISTCLEANFILES += gskresources.h gskresources.c
+
 libgsk_3_la_SOURCES = $(all_sources)
 nodist_libgsk_3_la_SOURCES = $(gsk_built_source_h) $(gsk_built_source_c)
 libgsk_3_la_CFLAGS = $(AM_CFLAGS) $(GDK_HIDDEN_VISIBILITY_CFLAGS)
diff --git a/gsk/gskglrenderer.c b/gsk/gskglrenderer.c
index 0870a22..9b97038 100644
--- a/gsk/gskglrenderer.c
+++ b/gsk/gskglrenderer.c
@@ -2,18 +2,37 @@
 
 #include "gskglrendererprivate.h"
 
+#include "gskdebugprivate.h"
+#include "gskenums.h"
 #include "gskrendererprivate.h"
 #include "gskrendernodeprivate.h"
 #include "gskrendernodeiter.h"
 
+#include "gskprivate.h"
+
 #include <epoxy/gl.h>
 
 typedef struct {
-  GskRenderNode *node;
-  guint texture_id;
-  graphene_rect_t frame;
-  graphene_matrix_t modelview;
+  graphene_point3d_t min;
+  graphene_point3d_t max;
+
+  graphene_size_t size;
+
+  graphene_matrix_t mvp;
+
+  gboolean opaque : 1;
   float opacity;
+  float z;
+
+  guint vao_id;
+  guint texture_id;
+  guint program_id;
+  guint mvp_location;
+  guint map_location;
+  guint uv_location;
+  guint position_location;
+  guint alpha_location;
+  guint buffer_id;
 } RenderItem;
 
 struct _GskGLRenderer
@@ -30,6 +49,13 @@ struct _GskGLRenderer
   guint depth_stencil_buffer;
   guint texture_id;
 
+  guint program_id;
+  guint mvp_location;
+  guint map_location;
+  guint uv_location;
+  guint position_location;
+  guint alpha_location;
+
   guint vao_id;
 
   GArray *opaque_render_items;
@@ -54,8 +80,6 @@ gsk_gl_renderer_dispose (GObject *gobject)
   GskGLRenderer *self = GSK_GL_RENDERER (gobject);
 
   g_clear_object (&self->context);
-  g_clear_pointer (&self->opaque_render_items, g_array_unref);
-  g_clear_pointer (&self->transparent_render_items, g_array_unref);
 
   G_OBJECT_CLASS (gsk_gl_renderer_parent_class)->dispose (gobject);
 }
@@ -63,6 +87,11 @@ gsk_gl_renderer_dispose (GObject *gobject)
 static void
 gsk_gl_renderer_create_buffers (GskGLRenderer *self)
 {
+  if (self->has_buffers)
+    return;
+
+  GSK_NOTE (OPENGL, g_print ("Creating buffers\n"));
+
   glGenFramebuffersEXT (1, &self->frame_buffer);
 
   if (self->has_alpha)
@@ -102,15 +131,95 @@ gsk_gl_renderer_create_buffers (GskGLRenderer *self)
         }
     }
 
+  /* We only have one VAO at the moment */
+  glGenVertexArrays (1, &self->vao_id);
+
   self->has_buffers = TRUE;
 }
 
 static void
+gsk_gl_renderer_allocate_buffers (GskGLRenderer *self,
+                                  int            width,
+                                  int            height)
+{
+  if (self->context == NULL)
+    return;
+
+  if (self->texture_id != 0)
+    {
+      glBindTexture (GL_TEXTURE_2D, self->texture_id);
+      glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+      glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+      glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+      glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+      glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
+    }
+
+  if (self->render_buffer != 0)
+    {
+      glBindRenderbuffer (GL_RENDERBUFFER, self->render_buffer);
+      glRenderbufferStorage (GL_RENDERBUFFER, GL_RGB8, width, height);
+    }
+
+  if (self->has_depth_buffer || self->has_stencil_buffer)
+    {
+      glBindRenderbuffer (GL_RENDERBUFFER, self->depth_stencil_buffer);
+
+      if (self->has_stencil_buffer)
+        glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
+      else
+        glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height);
+    }
+}
+
+static void
+gsk_gl_renderer_attach_buffers (GskGLRenderer *self)
+{
+  gsk_gl_renderer_create_buffers (self);
+
+  GSK_NOTE (OPENGL, g_print ("Attaching buffers\n"));
+
+  glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, self->frame_buffer);
+
+  if (self->texture_id != 0)
+    {
+      glFramebufferTexture2D (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
+                              GL_TEXTURE_2D, self->texture_id, 0);
+    }
+  else if (self->render_buffer != 0)
+    {
+      glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
+                                    GL_RENDERBUFFER_EXT, self->render_buffer);
+    }
+
+  if (self->depth_stencil_buffer != 0)
+    {
+      if (self->has_depth_buffer)
+        glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
+                                      GL_RENDERBUFFER_EXT, self->depth_stencil_buffer);
+      if (self->has_stencil_buffer)
+        glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT,
+                                      GL_RENDERBUFFER_EXT, self->depth_stencil_buffer);
+    }
+
+  if (self->vao_id != 0)
+    glBindVertexArray (self->vao_id);
+}
+
+static void
 gsk_gl_renderer_destroy_buffers (GskGLRenderer *self)
 {
   if (!self->has_buffers)
     return;
 
+  GSK_NOTE (OPENGL, g_print ("Destroying buffers\n"));
+
+  if (self->vao_id != 0)
+    {
+      glDeleteVertexArrays (1, &self->vao_id);
+      self->vao_id = 0;
+    }
+
   if (self->depth_stencil_buffer != 0)
     {
       glDeleteRenderbuffersEXT (1, &self->depth_stencil_buffer);
@@ -139,14 +248,151 @@ gsk_gl_renderer_destroy_buffers (GskGLRenderer *self)
   self->has_buffers = FALSE;
 }
 
+static guint
+create_shader (int         type,
+               const char *code)
+{
+  guint shader;
+  int status;
+
+  shader = glCreateShader (type);
+  glShaderSource (shader, 1, &code, NULL);
+  glCompileShader (shader);
+
+  glGetShaderiv (shader, GL_COMPILE_STATUS, &status);
+  if (status == GL_FALSE)
+    {
+      int log_len;
+      char *buffer;
+
+      glGetShaderiv (shader, GL_INFO_LOG_LENGTH, &log_len);
+
+      buffer = g_malloc0 (log_len + 1);
+      glGetShaderInfoLog (shader, log_len, NULL, buffer);
+
+      g_critical ("Compile failure in %s shader:\n%s",
+                  type == GL_VERTEX_SHADER ? "vertex" : "fragment",
+                  buffer);
+      g_free (buffer);
+
+      glDeleteShader (shader);
+
+      return 0;
+    }
+
+  return shader;
+}
+
+static void
+gsk_gl_renderer_create_program (GskGLRenderer *self)
+{
+  guint vertex_shader = 0, fragment_shader = 0;
+  GBytes *source;
+  int status;
+
+  GSK_NOTE (OPENGL, g_print ("Compiling vertex shader\n"));
+  source = g_resources_lookup_data ("/org/gtk/libgsk/glsl/base-renderer-vertex.glsl", 0, NULL);
+  vertex_shader = create_shader (GL_VERTEX_SHADER, g_bytes_get_data (source, NULL));
+  g_bytes_unref (source);
+  if (vertex_shader == 0)
+    goto out;
+
+  GSK_NOTE (OPENGL, g_print ("Compiling fragment shader\n"));
+  source = g_resources_lookup_data ("/org/gtk/libgsk/glsl/base-renderer-fragment.glsl", 0, NULL);
+  fragment_shader = create_shader (GL_FRAGMENT_SHADER, g_bytes_get_data (source, NULL));
+  g_bytes_unref (source);
+  if (fragment_shader == 0)
+    goto out;
+
+  self->program_id = glCreateProgram ();
+  glAttachShader (self->program_id, vertex_shader);
+  glAttachShader (self->program_id, fragment_shader);
+  glLinkProgram (self->program_id);
+
+  glGetProgramiv (self->program_id, GL_LINK_STATUS, &status);
+  if (status == GL_FALSE)
+    {
+      char *buffer = NULL;
+      int log_len = 0;
+
+      glGetProgramiv (self->program_id, GL_INFO_LOG_LENGTH, &log_len);
+
+      buffer = g_malloc0 (log_len + 1);
+      glGetProgramInfoLog (self->program_id, log_len, NULL, buffer);
+
+      g_critical ("Linking failure in shader:\n%s", buffer);
+      g_free (buffer);
+
+      glDeleteProgram (self->program_id);
+      self->program_id = 0;
+
+      goto out;
+    }
+
+  /* Find the location of each uniform and attribute we use in our
+   * shaders
+   */
+  self->mvp_location = glGetUniformLocation (self->program_id, "mvp");
+  self->map_location = glGetUniformLocation (self->program_id, "map");
+  self->alpha_location = glGetUniformLocation (self->program_id, "alpha");
+  self->position_location = glGetAttribLocation (self->program_id, "position");
+  self->uv_location = glGetAttribLocation (self->program_id, "uv");
+
+  GSK_NOTE (OPENGL, g_print ("Program [%d] { mvp:%d, map:%d, position:%d, uv:%d, alpha:%d }\n",
+                             self->program_id,
+                             self->mvp_location,
+                             self->map_location,
+                             self->position_location,
+                             self->uv_location,
+                             self->alpha_location));
+
+  /* We can detach and destroy the shaders from the linked program */
+  glDetachShader (self->program_id, vertex_shader);
+  glDetachShader (self->program_id, fragment_shader);
+
+out:
+  if (vertex_shader != 0)
+    glDeleteShader (vertex_shader);
+  if (fragment_shader != 0)
+    glDeleteShader (fragment_shader);
+}
+
+static void
+gsk_gl_renderer_destroy_program (GskGLRenderer *self)
+{
+  if (self->program_id != 0)
+    {
+      glDeleteProgram (self->program_id);
+      self->program_id = 0;
+    }
+}
+
 static gboolean
 gsk_gl_renderer_realize (GskRenderer *renderer)
 {
   GskGLRenderer *self = GSK_GL_RENDERER (renderer);
   GError *error = NULL;
 
+  /* If we didn't get a GdkGLContext before realization, try creating
+   * one now, for our exclusive use.
+   */
   if (self->context == NULL)
-    return FALSE;
+    {
+      GdkWindow *window = gsk_renderer_get_window (renderer);
+
+      if (window == NULL)
+        return FALSE;
+
+      self->context = gdk_window_create_gl_context (window, &error);
+      if (error != NULL)
+        {
+          g_critical ("Unable to create GL context for renderer: %s",
+                      error->message);
+          g_error_free (error);
+
+          return FALSE;
+        }
+    }
 
   gdk_gl_context_realize (self->context, &error);
   if (error != NULL)
@@ -156,8 +402,12 @@ gsk_gl_renderer_realize (GskRenderer *renderer)
       return FALSE;
     }
 
-  gdk_gl_context_make_current  (self->context);
+  gdk_gl_context_make_current (self->context);
+
+  GSK_NOTE (OPENGL, g_print ("Creating buffers and programs\n"));
+
   gsk_gl_renderer_create_buffers (self);
+  gsk_gl_renderer_create_program (self);
 
   return TRUE;
 }
@@ -171,7 +421,12 @@ gsk_gl_renderer_unrealize (GskRenderer *renderer)
     return;
 
   gdk_gl_context_make_current (self->context);
+
+  g_clear_pointer (&self->opaque_render_items, g_array_unref);
+  g_clear_pointer (&self->transparent_render_items, g_array_unref);
+
   gsk_gl_renderer_destroy_buffers (self);
+  gsk_gl_renderer_destroy_program (self);
 
   if (self->context == gdk_gl_context_get_current ())
     gdk_gl_context_clear_current ();
@@ -182,13 +437,26 @@ gsk_gl_renderer_resize_viewport (GskRenderer           *renderer,
                                  const graphene_rect_t *viewport)
 {
   GskGLRenderer *self = GSK_GL_RENDERER (renderer);
+  int status;
 
   if (self->context == NULL)
     return;
 
   gdk_gl_context_make_current (self->context);
 
-  glViewport (0, 0, viewport->size.width, viewport->size.height);
+  gsk_gl_renderer_create_buffers (self);
+  gsk_gl_renderer_allocate_buffers (self, viewport->size.width, viewport->size.height);
+  gsk_gl_renderer_attach_buffers (self);
+
+  /* Ensure that the viewport is up to date */
+  status = glCheckFramebufferStatusEXT (GL_FRAMEBUFFER_EXT);
+  if (status == GL_FRAMEBUFFER_COMPLETE_EXT)
+    {
+      GSK_NOTE (OPENGL, g_print ("glViewport(0, 0, %g, %g)\n",
+                                 viewport->size.width,
+                                 viewport->size.height));
+      glViewport (0, 0, viewport->size.width, viewport->size.height);
+    }
 }
 
 static void
@@ -205,52 +473,421 @@ gsk_gl_renderer_update (GskRenderer             *renderer,
 static void
 render_item_clear (gpointer data_)
 {
+  RenderItem *item = data_;
+
+  glDeleteBuffers (1, &item->buffer_id);
+  item->buffer_id = 0;
+
+  glDeleteTextures (1, &item->texture_id);
+  item->texture_id = 0;
+
+  graphene_matrix_init_identity (&item->mvp);
+
+  item->opacity = 1;
+}
+
+#define N_VERTICES      6
+
+static void
+render_item (RenderItem *item)
+{
+  struct vertex_info {
+    float position[3];
+    float uv[2];
+  };
+  float mvp[16];
+
+  if (item->buffer_id == 0)
+    {
+      glGenBuffers (1, &item->buffer_id);
+      glBindBuffer (GL_ARRAY_BUFFER, item->buffer_id);
+
+      {
+        float u1 = 0;
+        float v1 = 0;
+        float u2 = 1;
+        float v2 = 1;
+
+        struct vertex_info vertex_data[] = {
+      {
+        {
+          (item->min.x * 2) / item->size.width - 1,
+          (item->min.y * 2) / item->size.height - 1,
+          0.f
+        },
+        { u1, v1 },
+      },
+      {
+        {
+          (item->min.x * 2) / item->size.width - 1,
+          (item->max.y * 2) / item->size.height - 1,
+          0.f
+        },
+        { u1, v2 },
+      },
+      {
+        {
+          (item->max.x * 2) / item->size.width - 1,
+          (item->min.y * 2) / item->size.height - 1,
+          0.f
+        },
+        { u2, v1 },
+      },
+
+      {
+        {
+          (item->max.x * 2) / item->size.width - 1,
+          (item->max.y * 2) / item->size.height - 1,
+          0.f
+        },
+        { u2, v2 },
+      },
+      {
+        {
+          (item->min.x * 2) / item->size.width - 1,
+          (item->max.y * 2) / item->size.height - 1,
+          0.f
+        },
+        { u1, v2 },
+      },
+      {
+        {
+          (item->max.x * 2) / item->size.width - 1,
+          (item->min.y * 2) / item->size.height - 1,
+          0.f
+        },
+        { u2, v1 },
+        },
+      };
+
+      GSK_NOTE (OPENGL, g_print ("Item [%p] quad {\n"
+                                 "  { x1:%.2f y1:%.2f z:%.2f } - { x2:%.2f y2:%.2f z:%.2f },\n"
+                                 "  { u1:%.2f, v1:%.2f, u2:%.2f, v2:%.2f },\n"
+                                 "}\n",
+                                 item,
+                                 item->min.x, item->min.y, item->min.z,
+                                 item->max.x, item->max.y, item->max.z,
+                                 u1, v1, u2, v2));
+
+      /* The data won't change */
+      glBufferData (GL_ARRAY_BUFFER, sizeof (vertex_data), vertex_data, GL_STATIC_DRAW);
+  
+      /* Set up the buffers with the computed position and texels */
+      glEnableVertexAttribArray (item->position_location);
+      glVertexAttribPointer (item->position_location, 3, GL_FLOAT, GL_FALSE,
+                             sizeof (struct vertex_info),
+                             (void *) G_STRUCT_OFFSET (struct vertex_info, position));
+      glEnableVertexAttribArray (item->uv_location);
+      glVertexAttribPointer (item->uv_location, 2, GL_FLOAT, GL_FALSE,
+                             sizeof (struct vertex_info),
+                             (void *) G_STRUCT_OFFSET (struct vertex_info, uv));
+      }
+    }
+  else
+    {
+      glBindBuffer (GL_ARRAY_BUFFER, item->buffer_id);
+      glEnableVertexAttribArray (item->position_location);
+      glEnableVertexAttribArray (item->uv_location);
+    }
+
+  glUseProgram (item->program_id);
+
+  /* Use texture unit 0 for the sampler */
+  glActiveTexture (GL_TEXTURE0);
+  glUniform1i (item->map_location, 0);
+
+  /* Pass the mvp to the vertex shader */
+  graphene_matrix_to_float (&item->mvp, mvp);
+  glUniformMatrix4fv (item->mvp_location, 1, GL_FALSE, &mvp[0]);
+
+  glUniform1f (item->alpha_location, item->opaque ? 1 : item->opacity);
+
+  GSK_NOTE (OPENGL, graphene_matrix_print (&item->mvp));
+
+  glDrawArrays (GL_TRIANGLES, 0, 6);
+
+  glDisableVertexAttribArray (item->position_location);
+  glDisableVertexAttribArray (item->uv_location);
+  glUseProgram (0);
+}
+
+static void
+surface_to_texture (cairo_surface_t *surface,
+                    graphene_rect_t *clip,
+                    int              min_filter,
+                    int              mag_filter,
+                    guint           *texture_out)
+{
+  cairo_surface_t *tmp;
+  guint texture_id;
+
+  cairo_surface_flush (surface);
+
+  tmp = cairo_surface_map_to_image (surface, &(cairo_rectangle_int_t) {
+        0, 0, clip->size.width, clip->size.height
+  });
+
+  glGenTextures (1, &texture_id);
+  glBindTexture (GL_TEXTURE_2D, texture_id);
+
+  GSK_NOTE (OPENGL, g_print ("Uploading px [%p] { w:%g, h:%g } to texid:%d\n",
+                             cairo_image_surface_get_data (tmp),
+                             clip->size.width,
+                             clip->size.height,
+                             texture_id));
+
+  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter);
+  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter);
+
+  glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
+  glPixelStorei (GL_UNPACK_ROW_LENGTH, cairo_image_surface_get_stride (surface) / 4);
+  glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA,
+                clip->size.width, clip->size.height,
+                0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
+                cairo_image_surface_get_data (tmp));
+  glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
+
+  GSK_NOTE (OPENGL, g_print ("New texture id %d from surface %p\n", texture_id, surface));
+
+  cairo_surface_unmap_image (surface, tmp);
+
+  *texture_out = texture_id;
+}
+
+static void
+get_gl_scaling_filters (GskRenderer *renderer,
+                        int         *min_filter_r,
+                        int         *mag_filter_r)
+{
+  GskScalingFilter min_filter, mag_filter;
+
+  gsk_renderer_get_scaling_filters (renderer, &min_filter, &mag_filter);
+
+  switch (min_filter)
+    {
+    case GSK_SCALING_FILTER_NEAREST:
+      *min_filter_r = GL_NEAREST;
+      break;
+
+    case GSK_SCALING_FILTER_LINEAR:
+      *min_filter_r = GL_LINEAR;
+      break;
+
+    case GSK_SCALING_FILTER_TRILINEAR:
+      *min_filter_r = GL_LINEAR_MIPMAP_LINEAR;
+      break;
+    }
+
+  switch (mag_filter)
+    {
+    case GSK_SCALING_FILTER_NEAREST:
+      *mag_filter_r = GL_NEAREST;
+      break;
+
+    /* There's no point in using anything above GL_LINEAR for
+     * magnification filters
+     */
+    case GSK_SCALING_FILTER_LINEAR:
+    case GSK_SCALING_FILTER_TRILINEAR:
+      *mag_filter_r = GL_LINEAR;
+      break;
+    }
+}
+
+static gboolean
+check_in_frustum (const graphene_frustum_t *frustum,
+                  const graphene_matrix_t  *mvp,
+                  const graphene_rect_t    *rect)
+{
+  graphene_point3d_t bmin, bmax;
+  graphene_box_t aabb;
+
+  graphene_point3d_init (&bmin, rect->origin.x, rect->origin.y, 0.f);
+  graphene_point3d_init (&bmax,
+                         rect->origin.x + rect->size.width,
+                         rect->origin.y + rect->size.height,
+                         0.f);
+  graphene_box_init (&aabb, &bmin, &bmax);
+
+  graphene_matrix_transform_box (mvp, &aabb, &aabb);
+
+  return graphene_frustum_intersects_box (frustum, &aabb);
+}
+
+static float
+project_item (const graphene_matrix_t *projection,
+              const graphene_matrix_t *modelview)
+{
+  graphene_vec4_t vec;
+
+  graphene_matrix_get_row (modelview, 3, &vec);
+  graphene_matrix_transform_vec4 (projection, &vec, &vec);
+
+  return graphene_vec4_get_z (&vec) / graphene_vec4_get_w (&vec);
 }
 
 static void
 gsk_gl_renderer_add_render_item (GskGLRenderer *self,
                                  GskRenderNode *node)
 {
+  int gl_min_filter, gl_mag_filter;
   cairo_surface_t *surface;
   GskRenderNodeIter iter;
+  graphene_matrix_t mv, projection;
+  graphene_rect_t bounds;
   GskRenderNode *child;
   RenderItem item;
 
   if (gsk_render_node_is_hidden (node))
     return;
 
-  item.node = g_object_ref (node);
-  gsk_render_node_get_bounds (node, &item.frame);
-  gsk_render_node_get_world_matrix (node, &item.modelview);
+  gsk_render_node_get_bounds (node, &bounds);
+
+  gsk_render_node_get_world_matrix (node, &mv);
+  graphene_matrix_multiply (&self->mvp, &mv, &item.mvp);
+
+  if (!check_in_frustum (&self->frustum, &item.mvp, &bounds))
+    return;
+
+  /* Each render item is an axis-aligned bounding box that we
+   * transform using the given transformation matrix
+   */
+  item.min.x = bounds.origin.x;
+  item.min.y = bounds.origin.y;
+  item.min.z = 0.f;
+
+  item.max.x = bounds.origin.x + bounds.size.width;
+  item.max.y = bounds.origin.y + bounds.size.height;
+  item.max.z = 0.f;
+
+  /* The texture size */
+  item.size = bounds.size;
+
+  item.opaque = gsk_render_node_get_opacity (node);
   item.opacity = gsk_render_node_get_opacity (node);
 
-#if 0
-  /* TODO: This should really be an asset atlas */
+  /* TODO: This should really be an asset atlas, to avoid uploading a ton
+   * of textures. Ideally we could use a single Cairo surface to get around
+   * the GL texture limits and reorder the texture data on the CPU side and
+   * do a single upload; alternatively, we could use a separate FBO and
+   * render each texture into it
+   */
+  get_gl_scaling_filters (GSK_RENDERER (self), &gl_min_filter, &gl_mag_filter);
   surface = gsk_render_node_get_surface (node);
-  if (surface != NULL)
-    gdk_cairo_surface_to_texture (surface, &item.texture_id);
-#endif
 
-  if (gsk_render_node_is_opaque (node))
+  /* If the node does not have any surface we skip drawing it, but we still
+   * recurse.
+   *
+   * XXX: This needs to be re-done if the opacity is != 0, in which case we
+   * need to composite the opacity level of the children
+   */
+  if (surface == NULL)
+    goto recurse_children;
+
+  surface_to_texture (surface, &bounds, gl_min_filter, gl_mag_filter, &item.texture_id);
+
+  item.program_id = self->program_id;
+  item.map_location = self->map_location;
+  item.mvp_location = self->mvp_location;
+  item.uv_location = self->uv_location;
+  item.position_location = self->position_location;
+  item.alpha_location = self->alpha_location;
+
+  item.vao_id = self->vao_id;
+  item.buffer_id = 0;
+
+  gsk_renderer_get_projection (GSK_RENDERER (self), &projection);
+  item.z = project_item (&projection, &mv);
+
+  if (gsk_render_node_is_opaque (node) && gsk_render_node_get_opacity (node) == 1.f)
     g_array_append_val (self->opaque_render_items, item);
   else
-    g_array_prepend_val (self->transparent_render_items, item);
+    g_array_append_val (self->transparent_render_items, item);
 
+recurse_children:
   gsk_render_node_iter_init (&iter, node);
   while (gsk_render_node_iter_next (&iter, &child))
     gsk_gl_renderer_add_render_item (self, child);
 }
 
+static int
+opaque_item_cmp (gconstpointer _a,
+                 gconstpointer _b)
+{
+  const RenderItem *a = _a;
+  const RenderItem *b = _b;
+
+  if (a->z != b->z)
+    {
+      if (a->z > b->z)
+        return -1;
+
+      return 1;
+    }
+
+  if (a != b)
+    {
+      if ((gsize) a > (gsize) b)
+        return -1;
+
+      return 1;
+    }
+
+  return 0;
+}
+
+static int
+transparent_item_cmp (gconstpointer _a,
+                      gconstpointer _b)
+{
+  const RenderItem *a = _a;
+  const RenderItem *b = _b;
+
+  if (a->z != b->z)
+    {
+     if (a->z < b->z)
+       return 1;
+
+     return -1;
+    }
+
+  if (a != b)
+    {
+      if ((gsize) a < (gsize) b)
+        return 1;
+
+      return -1;
+    }
+
+  return 0;
+}
+
 static void
 gsk_gl_renderer_validate_tree (GskRenderer   *renderer,
                                GskRenderNode *root)
 {
   GskGLRenderer *self = GSK_GL_RENDERER (renderer);
 
+  if (self->context == NULL)
+    return;
+
+  gdk_gl_context_make_current (self->context);
+
   g_array_set_size (self->opaque_render_items, 0);
   g_array_set_size (self->transparent_render_items, 0);
 
+  GSK_NOTE (OPENGL, g_print ("RenderNode -> RenderItem\n"));
   gsk_gl_renderer_add_render_item (self, gsk_renderer_get_root_node (renderer));
+
+  //g_array_sort (self->opaque_render_items, opaque_item_cmp);
+  //g_array_sort (self->transparent_render_items, transparent_item_cmp);
+
+  GSK_NOTE (OPENGL, g_print ("Total render items: %d (opaque:%d, transparent:%d)\n",
+                             self->opaque_render_items->len + self->transparent_render_items->len,
+                             self->opaque_render_items->len,
+                             self->transparent_render_items->len));
 }
 
 static void
@@ -258,6 +895,9 @@ gsk_gl_renderer_clear (GskRenderer *renderer)
 {
   GskGLRenderer *self = GSK_GL_RENDERER (renderer);
 
+  if (self->context == NULL)
+    return;
+
   gdk_gl_context_make_current (self->context);
 
   glClearColor (0, 0, 0, 0);
@@ -268,21 +908,80 @@ static void
 gsk_gl_renderer_render (GskRenderer *renderer)
 {
   GskGLRenderer *self = GSK_GL_RENDERER (renderer);
+  graphene_rect_t viewport;
+  int scale, status, clear_bits;
+  cairo_t *cr;
   guint i;
 
+  if (self->context == NULL)
+    return;
+
   gdk_gl_context_make_current (self->context);
 
+  gsk_renderer_get_viewport (renderer, &viewport);
+
+  gsk_gl_renderer_create_buffers (self);
+  gsk_gl_renderer_allocate_buffers (self, viewport.size.width, viewport.size.height);
+  gsk_gl_renderer_attach_buffers (self);
+
+  if (self->has_depth_buffer)
+    glEnable (GL_DEPTH_TEST);
+  else
+    glDisable (GL_DEPTH_TEST);
+
+  /* Ensure that the viewport is up to date */
+  status = glCheckFramebufferStatusEXT (GL_FRAMEBUFFER_EXT);
+  if (status == GL_FRAMEBUFFER_COMPLETE_EXT)
+    {
+      GSK_NOTE (OPENGL, g_print ("glViewport(0, 0, %g, %g)\n",
+                                 viewport.size.width,
+                                 viewport.size.height));
+      glViewport (0, 0, viewport.size.width, viewport.size.height);
+    }
+
+  clear_bits = GL_COLOR_BUFFER_BIT;
+  if (self->has_depth_buffer)
+    clear_bits |= GL_DEPTH_BUFFER_BIT;
+  if (self->has_stencil_buffer)
+    clear_bits |= GL_STENCIL_BUFFER_BIT;
+
+  GSK_NOTE (OPENGL, g_print ("Clearing viewport\n"));
+  glClearColor (0, 0, 0, 0);
+  glClear (clear_bits);
+
+  glBindVertexArray (self->vao_id);
+
   /* Opaque pass: front-to-back */
   for (i = 0; i < self->opaque_render_items->len; i++)
     {
       RenderItem *item = &g_array_index (self->opaque_render_items, RenderItem, i);
+
+      render_item (item);
     }
 
+  glEnable (GL_BLEND);
+
   /* Transparent pass: back-to-front */
   for (i = 0; i < self->transparent_render_items->len; i++)
     {
       RenderItem *item = &g_array_index (self->transparent_render_items, RenderItem, i);
+
+      render_item (item);
     }
+
+  glDisable (GL_BLEND);
+
+  /* Draw the output of the GL rendering to the window */
+  scale = 1;
+  cr = cairo_create (gsk_renderer_get_surface (renderer));
+  gdk_cairo_draw_from_gl (cr, gsk_renderer_get_window (renderer),
+                          self->texture_id != 0 ? self->texture_id : self->render_buffer,
+                          self->texture_id != 0 ? GL_TEXTURE : GL_RENDERBUFFER,
+                          scale,
+                          0, 0, viewport.size.width, viewport.size.height);
+  cairo_destroy (cr);
+
+  gdk_gl_context_make_current (self->context);
 }
 
 static void
@@ -305,11 +1004,18 @@ gsk_gl_renderer_class_init (GskGLRendererClass *klass)
 static void
 gsk_gl_renderer_init (GskGLRenderer *self)
 {
+  gsk_ensure_resources ();
+
+  graphene_matrix_init_identity (&self->mvp);
+
   self->opaque_render_items = g_array_sized_new (FALSE, FALSE, sizeof (RenderItem), 16);
   g_array_set_clear_func (self->opaque_render_items, render_item_clear);
 
   self->transparent_render_items = g_array_sized_new (FALSE, FALSE, sizeof (RenderItem), 16);
   g_array_set_clear_func (self->opaque_render_items, render_item_clear);
+
+  self->has_depth_buffer = TRUE;
+  self->has_stencil_buffer = TRUE;
 }
 
 void
diff --git a/gsk/gskprivate.c b/gsk/gskprivate.c
new file mode 100644
index 0000000..9e82502
--- /dev/null
+++ b/gsk/gskprivate.c
@@ -0,0 +1,16 @@
+#include "gskresources.h"
+
+static gpointer
+register_resources (gpointer data)
+{
+  _gsk_register_resource ();
+  return NULL;
+}
+
+void
+gsk_ensure_resources (void)
+{
+  static GOnce register_resources_once = G_ONCE_INIT;
+
+  g_once (&register_resources_once, register_resources, NULL);
+}
diff --git a/gsk/gskprivate.h b/gsk/gskprivate.h
new file mode 100644
index 0000000..84539c1
--- /dev/null
+++ b/gsk/gskprivate.h
@@ -0,0 +1,12 @@
+#ifndef __GSK_PRIVATE_H__
+#define __GSK_PRIVATE_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+void gsk_ensure_resources (void);
+
+G_END_DECLS
+
+#endif /* __GSK_PRIVATE_H__ */
diff --git a/gsk/gskrenderer.c b/gsk/gskrenderer.c
index 8307707..cec24ff 100644
--- a/gsk/gskrenderer.c
+++ b/gsk/gskrenderer.c
@@ -39,6 +39,13 @@
 #include <cairo-gobject.h>
 #include <gdk/gdk.h>
 
+#ifdef GDK_WINDOWING_X11
+#include <gdk/x11/gdkx.h>
+#endif
+#ifdef GDK_WINDOWING_WAYLAND
+#include <gdk/wayland/gdkwayland.h>
+#endif
+
 typedef struct
 {
   GObject parent_instance;
@@ -858,22 +865,21 @@ gsk_renderer_get_surface (GskRenderer *renderer)
       int scale = gdk_window_get_scale_factor (priv->window);
       int width = gdk_window_get_width (priv->window);
       int height = gdk_window_get_height (priv->window);
-      cairo_format_t format;
+      cairo_content_t content;
 
       if (priv->use_alpha)
-        format = CAIRO_FORMAT_ARGB32;
+        content = CAIRO_CONTENT_COLOR_ALPHA;
       else
-        format = CAIRO_FORMAT_RGB24;
+        content = CAIRO_CONTENT_COLOR;
 
       GSK_NOTE (RENDERER, g_print ("Creating surface from window [%p] (w:%d, h:%d, s:%d, a:%s)\n",
                                    priv->window,
                                    width, height, scale,
                                    priv->use_alpha ? "y" : "n"));
 
-      priv->surface = gdk_window_create_similar_image_surface (priv->window,
-                                                               format,
-                                                               width, height,
-                                                               scale);
+      priv->surface = gdk_window_create_similar_surface (priv->window,
+                                                         content,
+                                                         width, height);
     }
 
   return priv->surface;
@@ -1165,12 +1171,17 @@ gsk_renderer_render (GskRenderer *renderer)
   g_return_if_fail (priv->is_realized);
   g_return_if_fail (priv->root_node != NULL);
 
+  /* We need to update the viewport and the modelview, to allow renderers
+   * to update their clip region and/or frustum; this allows them to cull
+   * render nodes in the tree validation phase
+   */
   gsk_renderer_maybe_resize_viewport (renderer);
 
   gsk_renderer_maybe_update (renderer);
 
   gsk_renderer_maybe_validate_tree (renderer);
 
+  /* Clear the output surface */
   gsk_renderer_maybe_clear (renderer);
 
   GSK_RENDERER_GET_CLASS (renderer)->render (renderer);
@@ -1291,5 +1302,37 @@ gsk_renderer_get_use_alpha (GskRenderer *renderer)
 GskRenderer *
 gsk_renderer_get_for_display (GdkDisplay *display)
 {
-  return g_object_new (GSK_TYPE_CAIRO_RENDERER, "display", display, NULL);
+  static const char *use_software;
+
+  GType renderer_type = G_TYPE_INVALID;
+
+  if (use_software == NULL)
+    {
+      use_software = g_getenv ("GSK_USE_SOFTWARE");
+      if (use_software == NULL)
+        use_software = "0";
+    }
+
+  if (use_software[0] != '0')
+    {
+      renderer_type = GSK_TYPE_CAIRO_RENDERER;
+      goto out;
+    }
+
+#ifdef GDK_WINDOWING_X11
+  if (GDK_IS_X11_DISPLAY (display))
+    renderer_type = GSK_TYPE_GL_RENDERER; 
+  else
+#endif
+#ifdef GDK_WINDOWING_WAYLAND
+  if (GDK_IS_WAYLAND_DISPLAY (display))
+    renderer_type = GSK_TYPE_GL_RENDERER;
+  else
+#endif
+    renderer_type = GSK_TYPE_CAIRO_RENDERER;
+
+  g_assert (renderer_type != G_TYPE_INVALID);
+
+out:
+  return g_object_new (renderer_type, "display", display, NULL);
 }
diff --git a/gsk/gskrendernode.c b/gsk/gskrendernode.c
index 34cf61c..7bc0422 100644
--- a/gsk/gskrendernode.c
+++ b/gsk/gskrendernode.c
@@ -880,6 +880,8 @@ gsk_render_node_set_surface (GskRenderNode   *node,
 
   if (surface != NULL)
     node->surface = cairo_surface_reference (surface);
+
+  gsk_render_node_queue_invalidate (node, GSK_RENDER_NODE_CHANGES_UPDATE_SURFACE);
 }
 
 /*< private >
@@ -1129,6 +1131,17 @@ gsk_render_node_resize (GskRenderNode *node)
   node->needs_resize = FALSE;
 }
 
+/**
+ * gsk_render_node_set_name:
+ * @node: a #GskRenderNode
+ * @name: (nullable): a name for the node
+ *
+ * Sets the name of the node.
+ *
+ * A name is generally useful for debugging purposes.
+ *
+ * Since: 3.22
+ */
 void
 gsk_render_node_set_name (GskRenderNode *node,
                           const char    *name)
@@ -1138,3 +1151,45 @@ gsk_render_node_set_name (GskRenderNode *node,
   g_free (node->name);
   node->name = g_strdup (name);
 }
+
+static cairo_user_data_key_t render_node_context_key;
+
+static void
+surface_invalidate (void *data)
+{
+  GskRenderNode *node = data;
+
+  gsk_render_node_queue_invalidate (node, GSK_RENDER_NODE_CHANGES_UPDATE_SURFACE);
+}
+
+/**
+ * gsk_render_node_get_draw_context:
+ * @node: a #GskRenderNode
+ *
+ * Creates a Cairo context for drawing using the surface associated
+ * to the render node. If no surface has been attached to the render
+ * node, a new surface will be created as a side effect.
+ *
+ * Returns: (transfer full): a Cairo context used for drawing; use
+ *   cairo_destroy() when done drawing
+ *
+ * Since: 3.22
+ */
+cairo_t *
+gsk_render_node_get_draw_context (GskRenderNode *node)
+{
+  cairo_t *res;
+
+  g_return_val_if_fail (GSK_IS_RENDER_NODE (node), NULL);
+
+  if (node->surface == NULL)
+    node->surface = cairo_image_surface_create (node->opaque ? CAIRO_FORMAT_RGB24
+                                                             : CAIRO_FORMAT_ARGB32,
+                                                node->bounds.size.width,
+                                                node->bounds.size.height);
+
+  res = cairo_create (node->surface);
+  cairo_set_user_data (res, &render_node_context_key, node, surface_invalidate);
+
+  return res;
+}
diff --git a/gsk/gskrendernode.h b/gsk/gskrendernode.h
index ffa1236..f729c27 100644
--- a/gsk/gskrendernode.h
+++ b/gsk/gskrendernode.h
@@ -105,6 +105,8 @@ gboolean                gsk_render_node_is_opaque               (GskRenderNode *
 GDK_AVAILABLE_IN_3_22
 void                    gsk_render_node_set_surface             (GskRenderNode   *node,
                                                                  cairo_surface_t *surface);
+GDK_AVAILABLE_IN_3_22
+cairo_t *               gsk_render_node_get_draw_context        (GskRenderNode   *node);
 
 GDK_AVAILABLE_IN_3_22
 void                    gsk_render_node_set_name                (GskRenderNode *node,
diff --git a/gsk/gskrendernodeprivate.h b/gsk/gskrendernodeprivate.h
index 64ded07..82e3dad 100644
--- a/gsk/gskrendernodeprivate.h
+++ b/gsk/gskrendernodeprivate.h
@@ -12,7 +12,8 @@ G_BEGIN_DECLS
 
 typedef enum {
   GSK_RENDER_NODE_CHANGES_UPDATE_BOUNDS = 1 << 0,
-  GSK_RENDER_NODE_CHANGES_UPDATE_TRANSFORM = 1 << 1
+  GSK_RENDER_NODE_CHANGES_UPDATE_TRANSFORM = 1 << 1,
+  GSK_RENDER_NODE_CHANGES_UPDATE_SURFACE = 1 << 2
 } GskRenderNodeChanges;
 
 typedef void (* GskRenderNodeInvalidateFunc) (GskRenderNode *node,
diff --git a/gsk/resources/glsl/base-renderer-fragment.glsl b/gsk/resources/glsl/base-renderer-fragment.glsl
new file mode 100644
index 0000000..51814f8
--- /dev/null
+++ b/gsk/resources/glsl/base-renderer-fragment.glsl
@@ -0,0 +1,12 @@
+#version 150
+
+uniform sampler2D map;
+uniform float alpha;
+
+smooth in vec2 vUv;
+
+out vec4 outputColor;
+
+void main() {
+  outputColor = texture2D(map, vUv) * vec4(1, 1, 1, alpha);
+}
diff --git a/gsk/resources/glsl/base-renderer-vertex.glsl b/gsk/resources/glsl/base-renderer-vertex.glsl
new file mode 100644
index 0000000..24d28de
--- /dev/null
+++ b/gsk/resources/glsl/base-renderer-vertex.glsl
@@ -0,0 +1,15 @@
+#version 150
+
+in vec3 position;
+in vec2 uv;
+
+uniform sampler2D map;
+uniform mat4 mvp;
+
+smooth out vec2 vUv;
+
+void main() {
+  gl_Position = mvp * vec4(position, 1.0);
+
+  vUv = uv;
+}
diff --git a/tests/testgskrenderer.c b/tests/testgskrenderer.c
index 08fabca..6d6bb15 100644
--- a/tests/testgskrenderer.c
+++ b/tests/testgskrenderer.c
@@ -9,22 +9,12 @@
 #define PADDING         10.f
 #define ROOT_SIZE       BOX_SIZE * 2 + PADDING * 2
 
-static cairo_surface_t *
-create_color_surface (GdkRGBA *color, int w, int h)
+static void
+create_color_surface (cairo_t *cr, GdkRGBA *color, int w, int h)
 {
-  cairo_surface_t *res;
-  cairo_t *cr;
-
-  res = cairo_image_surface_create (CAIRO_FORMAT_RGB24, w, h);
-  cr = cairo_create (res);
-
   cairo_set_source_rgba (cr, color->red, color->green, color->blue, color->alpha);
   cairo_rectangle (cr, 0, 0, w, h);
   cairo_fill (cr);
-
-  cairo_destroy (cr);
-
-  return res;
 }
 
 static GskRenderer *
@@ -48,49 +38,52 @@ static void
 create_scene (GskRenderer *renderer)
 {
   GskRenderNode *root, *node;
-  cairo_surface_t *surface;
   graphene_matrix_t ctm;
+  cairo_t *cr;
 
   root = gsk_render_node_new ();
+  gsk_render_node_set_name (root, "Root node");
   gsk_render_node_set_bounds (root, &(graphene_rect_t) {
                                 .origin.x = 0.f,
                                 .origin.y = 0.f,
                                 .size.width = ROOT_SIZE,
                                 .size.height = ROOT_SIZE
                               });
-  surface = create_color_surface (&(GdkRGBA) { .red = 1, .green = 0, .blue = 0, .alpha = 1 }, ROOT_SIZE, 
ROOT_SIZE);
-  gsk_render_node_set_surface (root, surface);
-  cairo_surface_destroy (surface);
+  cr = gsk_render_node_get_draw_context (root);
+  create_color_surface (cr, &(GdkRGBA) { .red = 1, .green = 0, .blue = 0, .alpha = 1 }, ROOT_SIZE, 
ROOT_SIZE);
+  cairo_destroy (cr);
   gsk_renderer_set_root_node (renderer, root);
   g_object_set_data (G_OBJECT (renderer), "-gsk-renderer-root-node", root);
 
   g_object_unref (root);
 
   node = gsk_render_node_new ();
+  gsk_render_node_set_name (node, "Green node");
   gsk_render_node_set_bounds (node, &(graphene_rect_t) {
                                 .origin.x = 0.f,
                                 .origin.y = 0.f,
                                 .size.width = BOX_SIZE,
                                 .size.height = BOX_SIZE
                               });
-  surface = create_color_surface (&(GdkRGBA) { .red = 0, .green = 1, .blue = 0, .alpha = 1 }, BOX_SIZE, 
BOX_SIZE);
-  gsk_render_node_set_surface (node, surface);
-  cairo_surface_destroy (surface);
+  cr = gsk_render_node_get_draw_context (node);
+  create_color_surface (cr, &(GdkRGBA) { .red = 0, .green = 1, .blue = 0, .alpha = 1 }, BOX_SIZE, BOX_SIZE);
+  cairo_destroy (cr);
   graphene_matrix_init_translate (&ctm, &(graphene_point3d_t) { .x = PADDING, .y = PADDING, .z = 0.f });
   gsk_render_node_set_transform (node, &ctm);
   gsk_render_node_insert_child_at_pos (root, node, 0);
   g_object_unref (node);
 
   node = gsk_render_node_new ();
+  gsk_render_node_set_name (node, "Blue node");
   gsk_render_node_set_bounds (node, &(graphene_rect_t) {
                                 .origin.x = 0.f,
                                 .origin.y = 0.f,
                                 .size.width = BOX_SIZE,
                                 .size.height = BOX_SIZE
                               });
-  surface = create_color_surface (&(GdkRGBA) { .red = 0, .green = 0, .blue = 1, .alpha = 1 }, BOX_SIZE, 
BOX_SIZE);
-  gsk_render_node_set_surface (node, surface);
-  cairo_surface_destroy (surface);
+  cr = gsk_render_node_get_draw_context (node);
+  create_color_surface (cr, &(GdkRGBA) { .red = 0, .green = 0, .blue = 1, .alpha = 1 }, BOX_SIZE, BOX_SIZE);
+  cairo_destroy (cr);
   graphene_matrix_init_translate (&ctm, &(graphene_point3d_t) { .x = BOX_SIZE + PADDING, .y = BOX_SIZE + 
PADDING, .z = 0.f });
   gsk_render_node_set_transform (node, &ctm);
   gsk_render_node_insert_child_at_pos (root, node, 1);
@@ -125,8 +118,8 @@ size_allocate (GtkWidget *widget, GtkAllocation *allocation)
   gsk_renderer_set_viewport (renderer, &(graphene_rect_t) {
                                .origin.x = 0,
                                .origin.y = 0,
-                               .size.width = allocation->width - allocation->x,
-                               .size.height = allocation->height - allocation->y
+                               .size.width = allocation->width,
+                               .size.height = allocation->height
                              });
 
   root = g_object_get_data (G_OBJECT (renderer), "-gsk-renderer-root-node");
@@ -172,6 +165,8 @@ main (int argc, char *argv[])
   area = gtk_drawing_area_new ();
   gtk_widget_set_hexpand (area, TRUE);
   gtk_widget_set_vexpand (area, TRUE);
+  gtk_widget_set_has_window (GTK_WIDGET (area), FALSE);
+  gtk_widget_set_app_paintable (GTK_WIDGET (area), TRUE);
   gtk_container_add (GTK_CONTAINER (window), area);
 
   g_signal_connect (area, "realize", G_CALLBACK (realize), NULL);



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