[gtk: 9/15] Add GskGLShaderNode and GskGLShader




commit 7ea755e206c36960db05f0e0660c6f5f20b692c6
Author: Alexander Larsson <alexl redhat com>
Date:   Fri Sep 18 17:46:57 2020 +0200

    Add GskGLShaderNode and GskGLShader
    
    A GskGLShader is an abstraction of a GLSL fragment shader that
    can produce pixel values given inputs:
     * N (currently max 4) textures
     * Current arguments for the shader uniform
    Uniform types are: float,(u)int,bool,vec234)
    There is also a builder for the uniform arguments which are
    passed around as immutable GBytes in the built form.
    
    A GskGLShaderNode is a render node that renders a GskGLShader inside a
    specified rectangular bounds. It renders its child nodes as textures
    and passes those as texture arguments to the shader. You also pass it
    a uniform arguments object.

 docs/reference/gsk/gsk4-docs.xml     |    1 +
 docs/reference/gsk/gsk4-sections.txt |   51 ++
 docs/reference/gsk/gsk4.types        |    1 +
 gsk/gsk.h                            |    1 +
 gsk/gskenums.h                       |   32 +-
 gsk/gskglshader.c                    | 1394 ++++++++++++++++++++++++++++++++++
 gsk/gskglshader.h                    |  162 ++++
 gsk/gskglshaderprivate.h             |   19 +
 gsk/gskrendernode.h                  |   21 +
 gsk/gskrendernodeimpl.c              |  220 ++++++
 gsk/gskrendernodeparser.c            |  336 +++++++-
 gsk/gskrendernodeprivate.h           |    2 +-
 gsk/meson.build                      |    2 +
 gtk/inspector/recorder.c             |  136 ++++
 14 files changed, 2375 insertions(+), 3 deletions(-)
---
diff --git a/docs/reference/gsk/gsk4-docs.xml b/docs/reference/gsk/gsk4-docs.xml
index ea2952f62c..2bfecbf290 100644
--- a/docs/reference/gsk/gsk4-docs.xml
+++ b/docs/reference/gsk/gsk4-docs.xml
@@ -20,6 +20,7 @@
     <xi:include href="xml/GskRenderNode.xml" />
     <xi:include href="xml/GskRoundedRect.xml" />
     <xi:include href="xml/GskTransform.xml" />
+    <xi:include href="xml/GskGLShader.xml" />
   </reference>
 
   <index id="api-index-full">
diff --git a/docs/reference/gsk/gsk4-sections.txt b/docs/reference/gsk/gsk4-sections.txt
index 3d0e9f0ccf..b0bf7b506b 100644
--- a/docs/reference/gsk/gsk4-sections.txt
+++ b/docs/reference/gsk/gsk4-sections.txt
@@ -48,6 +48,7 @@ GskShadowNode
 GskTextNode
 GskTextureNode
 GskTransformNode
+GskGLShaderNode
 gsk_render_node_ref
 gsk_render_node_unref
 GskRenderNodeType
@@ -152,6 +153,11 @@ gsk_blur_node_get_radius
 gsk_debug_node_new
 gsk_debug_node_get_child
 gsk_debug_node_get_message
+gsk_gl_shader_node_new
+gsk_gl_shader_node_get_n_children
+gsk_gl_shader_node_get_child
+gsk_gl_shader_node_get_args
+gsk_gl_shader_node_get_shader
 <SUBSECTION Standard>
 GSK_IS_RENDER_NODE
 GSK_RENDER_NODE
@@ -177,6 +183,7 @@ GSK_TYPE_SHADOW_NODE
 GSK_TYPE_TEXT_NODE
 GSK_TYPE_TEXTURE_NODE
 GSK_TYPE_TRANSFORM_NODE
+GSK_TYPE_GLSHADER_NODE
 GskRenderNodeClass
 gsk_blend_node_get_type
 gsk_blur_node_get_type
@@ -202,6 +209,7 @@ gsk_shadow_node_get_type
 gsk_text_node_get_type
 gsk_texture_node_get_type
 gsk_transform_node_get_type
+gsk_gl_shader_node_get_type
 GSK_TYPE_BLEND_MODE
 <SUBSECTION Standard>
 gsk_serialization_error_quark
@@ -266,3 +274,46 @@ gsk_transform_get_type
 gsk_transform_new
 </SECTION>
 
+<SECTION>
+<FILE>GskGLShader</FILE>
+GskGLShader
+gsk_gl_shader_new_from_bytes
+gsk_gl_shader_new_from_resource
+gsk_gl_shader_compile
+gsk_gl_shader_get_source
+gsk_gl_shader_get_n_textures
+gsk_gl_shader_get_n_uniforms
+gsk_gl_shader_get_uniform_name
+gsk_gl_shader_find_uniform_by_name
+gsk_gl_shader_get_uniform_type
+gsk_gl_shader_get_uniform_offset
+gsk_gl_shader_get_args_size
+
+<SUBSECTION Uniform Data>
+gsk_gl_shader_get_arg_float
+gsk_gl_shader_get_arg_int
+gsk_gl_shader_get_arg_uint
+gsk_gl_shader_get_arg_bool
+gsk_gl_shader_get_arg_vec2
+gsk_gl_shader_get_arg_vec3
+gsk_gl_shader_get_arg_vec4
+gsk_gl_shader_format_args_va
+gsk_gl_shader_format_args
+
+<SUBSECTION Shader Args Builder>
+GskShaderArgsBuilder
+
+gsk_shader_args_builder_new
+gsk_shader_args_builder_to_args
+gsk_shader_args_builder_free_to_args
+gsk_shader_args_builder_unref
+gsk_shader_args_builder_ref
+
+gsk_shader_args_builder_set_float
+gsk_shader_args_builder_set_int
+gsk_shader_args_builder_set_uint
+gsk_shader_args_builder_set_bool
+gsk_shader_args_builder_set_vec2
+gsk_shader_args_builder_set_vec3
+gsk_shader_args_builder_set_vec4
+</SECTION>
diff --git a/docs/reference/gsk/gsk4.types b/docs/reference/gsk/gsk4.types
index 71a774ca97..94eeabf270 100644
--- a/docs/reference/gsk/gsk4.types
+++ b/docs/reference/gsk/gsk4.types
@@ -1,2 +1,3 @@
 gsk_render_node_get_type
 gsk_renderer_get_type
+gsk_gl_shader_get_type
diff --git a/gsk/gsk.h b/gsk/gsk.h
index 523b24c81a..73cb1daa57 100644
--- a/gsk/gsk.h
+++ b/gsk/gsk.h
@@ -25,6 +25,7 @@
 #include <gsk/gskrendernode.h>
 #include <gsk/gskroundedrect.h>
 #include <gsk/gsktransform.h>
+#include <gsk/gskglshader.h>
 
 #include <gsk/gskcairorenderer.h>
 
diff --git a/gsk/gskenums.h b/gsk/gskenums.h
index 24aafec502..0cd0c284fc 100644
--- a/gsk/gskenums.h
+++ b/gsk/gskenums.h
@@ -48,6 +48,7 @@
  * @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
+ * @GSK_GL_SHADER_NODE: A node that uses OpenGL fragment shaders to render
  
  * The type of a node determines what the node is rendering.
  */
@@ -75,7 +76,8 @@ typedef enum {
   GSK_CROSS_FADE_NODE,
   GSK_TEXT_NODE,
   GSK_BLUR_NODE,
-  GSK_DEBUG_NODE
+  GSK_DEBUG_NODE,
+  GSK_GL_SHADER_NODE
 } GskRenderNodeType;
 
 /**
@@ -218,4 +220,32 @@ typedef enum
   GSK_TRANSFORM_CATEGORY_IDENTITY
 } GskTransformCategory;
 
+/**
+ * GskGLUniformType:
+ * @GSK_GL_UNIFORM_TYPE_NONE: No type, used for uninitialized or unspecified values.
+ * @GSK_GL_UNIFORM_TYPE_FLOAT: A float uniform
+ * @GSK_GL_UNIFORM_TYPE_INT: A GLSL int / gint32 uniform
+ * @GSK_GL_UNIFORM_TYPE_UINT: A GLSL uint / guint32 uniform
+ * @GSK_GL_UNIFORM_TYPE_BOOL: A GLSL bool / gboolean uniform
+ * @GSK_GL_UNIFORM_TYPE_VEC2: A GLSL vec2 / graphene_vec2_t uniform
+ * @GSK_GL_UNIFORM_TYPE_VEC3: A GLSL vec3 / graphene_vec3_t uniform
+ * @GSK_GL_UNIFORM_TYPE_VEC4: A GLSL vec4 / graphene_vec4_t uniform
+ *
+ * This defines the types of the uniforms that #GskGLShaders
+ * declare. It defines both what the type is called in the GLSL shader
+ * code, and what the corresponding C type is on the Gtk side.
+ */
+typedef enum
+{
+  GSK_GL_UNIFORM_TYPE_NONE,
+  GSK_GL_UNIFORM_TYPE_FLOAT,
+  GSK_GL_UNIFORM_TYPE_INT,
+  GSK_GL_UNIFORM_TYPE_UINT,
+  GSK_GL_UNIFORM_TYPE_BOOL,
+  GSK_GL_UNIFORM_TYPE_VEC2,
+  GSK_GL_UNIFORM_TYPE_VEC3,
+  GSK_GL_UNIFORM_TYPE_VEC4,
+} GskGLUniformType;
+
+
 #endif /* __GSK_TYPES_H__ */
diff --git a/gsk/gskglshader.c b/gsk/gskglshader.c
new file mode 100644
index 0000000000..7ad7773496
--- /dev/null
+++ b/gsk/gskglshader.c
@@ -0,0 +1,1394 @@
+/* GSK - The GTK Scene Kit
+ *
+ * Copyright 2020, Red Hat Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * SECTION:GskGLShader
+ * @Title: GskGLShader
+ * @Short_description: Fragment shaders for GSK
+ *
+ * A #GskGLShader is a snippet of GLSL that is meant to run in the
+ * fragment shader of the rendering pipeline. A fragment shader
+ * gets the coordinates being rendered as input and produces the
+ * pixel values for that particular pixel. Additionally, the
+ * shader can declare a set of other input arguments, called
+ * uniforms (as they are uniform over all the calls to your shader in
+ * each instance of use). A shader can also receive up to 4
+ * textures that it can use as input when producing the pixel data.
+ *
+  #GskGLShader is usually used with gtk_snapshot_push_gl_shader()
+* to produce a #GskGLShaderNode in the rendering hierarchy, and then
+* its input textures are constructed by rendering the child nodes to
+* textures before rendering the shader node itself. (You can pass
+* texture nodes as children if you want to directly use a texture
+* as input).
+ *
+ * The actual shader code is GLSL code that gets combined with
+ * some other code into the fragment shader. Since the exact
+ * capabilities of the GPU driver differs between different OpenGL
+ * drivers and hardware, GTK adds some defines that you can use
+ * to ensure your GLSL code runs on as many drivers as it can.
+ *
+ * If the OpenGL driver is GLES, then the shader language version
+ * is set to 100, and GSK_GLES will be defined in the shader.
+ *
+ * Otherwise, if the OpenGL driver does not support the 3.2 core profile,
+ * then the shader will run with language version 110 for GL2 and 130 for GL3,
+ * and GSK_LEGACY will be defined in the shader.
+ *
+ * If the OpenGL driver supports the 3.2 code profile, it will be used,
+ * the shader language version is set to 150, and GSK_GL3 will be defined
+ * in the shader.
+ *
+ * The main function the shader must implement is:
+ *
+ * |[<!-- language="plain" -->
+ *  void mainImage(out vec4 fragColor,
+ *                 in vec2 fragCoord,
+ *                 in vec2 resolution,
+ *                 in vec2 uv)
+ * ]|
+ * 
+ * Where the input @fragCoord is the coordinate of the pixel we're
+ * currently rendering, relative to the boundary rectangle that was
+ * specified in the #GskGLShaderNode, and @resolution is the width and
+ * height of that rectangle. This is in the typical GTK coordinate
+ * system with the origin in the top left. @uv contains the u and v
+ * coordinates that can be used to index a texture at the
+ * corresponding point. These coordinates are in the [0..1]x[0..1]
+ * region, with 0, 0 being in the lower left cornder (which is typical
+ * for OpenGL).
+ *
+ * The output @fragColor should be a RGBA color (with
+ * premultiplied alpha) that will be used as the output for the
+ * specified pixel location. Note that this output will be
+ * automatically clipped to the clip region of the glshader node.
+ *
+ * In addition to the function arguments the shader can define
+ * up to 4 uniforms for textures which must be called u_textureN
+ * (i.e. u_texture1 to u_texture4) as well as any custom uniforms
+ * you want of types int, uint, bool, float, vec2, vec3 or vec4.
+ *
+ * All textures sources contain premultiplied alpha colors, but if some
+ * there are outer sources of colors there is a gsk_premultiply() helper
+ * to compute premultiplication when needed.
+ *
+ * Note that GTK parses the uniform declarations, so each uniform has to
+ * be on a line by itself with no other code, like so:
+ *
+ * |[<!-- language="plain" -->
+ * uniform float u_time;
+ * uniform vec3 u_color;
+ * uniform sampler2D u_texture1;
+ * uniform sampler2D u_texture2;
+ * ]|
+ *
+ * GTK uses the the "gsk" namespace in the symbols it uses in the
+ * shader, so your code should not use any symbols with the prefix gsk
+ * or GSK. There are some helper functions declared that you can use:
+ *
+ * |[<!-- language="plain" -->
+ * vec4 GskTexture(sampler2D sampler, vec2 texCoords);
+ * ]|
+ *
+ * This samples a texture (e.g. u_texture1) at the specified
+ * coordinates, and containes some helper ifdefs to ensure that
+ * it works on all OpenGL versions.
+ *
+ * # An example shader
+ *
+ * |[<!-- language="plain" -->
+ * uniform float position;
+ * uniform sampler2D u_texture1;
+ * uniform sampler2D u_texture2;
+ *
+ * void mainImage(out vec4 fragColor,
+ *                in vec2 fragCoord,
+ *                in vec2 resolution,
+ *                in vec2 uv) {
+ *   vec4 source1 = GskTexture(u_texture1, uv);
+ *   vec4 source2 = GskTexture(u_texture2, uv);
+ *
+ *   fragColor = position * source1 + (1.0 - position) * source2;
+ * }
+ * ]|
+ */
+
+#include "config.h"
+#include "gskglshader.h"
+#include "gskglshaderprivate.h"
+#include "gskdebugprivate.h"
+#include "gl/gskglrendererprivate.h"
+
+static GskGLUniformType
+uniform_type_from_glsl (const char *str)
+{
+  if (strcmp (str, "int") == 0)
+    return GSK_GL_UNIFORM_TYPE_INT;
+  if (strcmp (str, "uint") == 0)
+    return GSK_GL_UNIFORM_TYPE_UINT;
+  if (strcmp (str, "bool") == 0)
+    return GSK_GL_UNIFORM_TYPE_BOOL;
+  if (strcmp (str, "float") == 0)
+    return GSK_GL_UNIFORM_TYPE_FLOAT;
+  if (strcmp (str, "vec2") == 0)
+    return GSK_GL_UNIFORM_TYPE_VEC2;
+  if (strcmp (str, "vec3") == 0)
+    return GSK_GL_UNIFORM_TYPE_VEC3;
+  if (strcmp (str, "vec4") == 0)
+    return GSK_GL_UNIFORM_TYPE_VEC4;
+
+  return  GSK_GL_UNIFORM_TYPE_NONE;
+}
+
+static const char *
+uniform_type_name (GskGLUniformType type)
+{
+  switch (type)
+    {
+    case GSK_GL_UNIFORM_TYPE_FLOAT:
+      return "float";
+
+    case GSK_GL_UNIFORM_TYPE_INT:
+      return "int";
+
+    case GSK_GL_UNIFORM_TYPE_UINT:
+      return "uint";
+
+    case GSK_GL_UNIFORM_TYPE_BOOL:
+      return "bool";
+
+    case GSK_GL_UNIFORM_TYPE_VEC2:
+      return "vec2";
+
+    case GSK_GL_UNIFORM_TYPE_VEC3:
+      return "vec3";
+
+    case GSK_GL_UNIFORM_TYPE_VEC4:
+      return "vec4";
+
+    case GSK_GL_UNIFORM_TYPE_NONE:
+    default:
+      g_assert_not_reached ();
+      return NULL;
+    }
+}
+
+static int
+uniform_type_size (GskGLUniformType type)
+{
+  switch (type)
+    {
+    case GSK_GL_UNIFORM_TYPE_FLOAT:
+      return sizeof (float);
+
+    case GSK_GL_UNIFORM_TYPE_INT:
+      return sizeof (gint32);
+
+    case GSK_GL_UNIFORM_TYPE_UINT:
+    case GSK_GL_UNIFORM_TYPE_BOOL:
+      return sizeof (guint32);
+
+    case GSK_GL_UNIFORM_TYPE_VEC2:
+      return sizeof (float) * 2;
+
+    case GSK_GL_UNIFORM_TYPE_VEC3:
+      return sizeof (float) * 3;
+
+    case GSK_GL_UNIFORM_TYPE_VEC4:
+      return sizeof (float) * 4;
+
+    case GSK_GL_UNIFORM_TYPE_NONE:
+    default:
+      g_assert_not_reached ();
+      return 0;
+    }
+}
+
+struct _GskGLShader
+{
+  GObject parent_instance;
+  GBytes *source;
+  char *resource;
+  int n_textures;
+  int uniforms_size;
+  GArray *uniforms;
+};
+
+G_DEFINE_TYPE (GskGLShader, gsk_gl_shader, G_TYPE_OBJECT)
+
+enum {
+  GLSHADER_PROP_0,
+  GLSHADER_PROP_SOURCE,
+  GLSHADER_PROP_RESOURCE,
+  GLSHADER_N_PROPS
+};
+static GParamSpec *gsk_gl_shader_properties[GLSHADER_N_PROPS];
+
+static void
+gsk_gl_shader_get_property (GObject    *object,
+                            guint       prop_id,
+                            GValue     *value,
+                            GParamSpec *pspec)
+{
+  GskGLShader *shader = GSK_GL_SHADER (object);
+
+  switch (prop_id)
+    {
+    case GLSHADER_PROP_SOURCE:
+      g_value_set_boxed (value, shader->source);
+      break;
+
+    case GLSHADER_PROP_RESOURCE:
+      g_value_set_string (value, shader->resource);
+      break;
+
+    default:
+      g_assert_not_reached ();
+    }
+}
+
+static void
+gsk_gl_shader_set_property (GObject      *object,
+                            guint         prop_id,
+                            const GValue *value,
+                            GParamSpec   *pspec)
+{
+  GskGLShader *shader = GSK_GL_SHADER (object);
+
+  switch (prop_id)
+    {
+    case GLSHADER_PROP_SOURCE:
+      g_clear_pointer (&shader->source, g_bytes_unref);
+      shader->source = g_value_dup_boxed (value);
+      break;
+
+    case GLSHADER_PROP_RESOURCE:
+      {
+        GError *error = NULL;
+        GBytes *source;
+        const char *resource;
+
+        resource = g_value_get_string (value);
+        if (resource == NULL)
+          break;
+
+        source = g_resources_lookup_data (resource, 0, &error);
+        if (source)
+          {
+            g_clear_pointer (&shader->source, g_bytes_unref);
+            shader->source = source;
+            shader->resource = g_strdup (resource);
+          }
+        else
+          {
+            g_critical ("Unable to load resource %s for glshader: %s", resource, error->message);
+            g_error_free (error);
+            if (shader->source == NULL)
+              shader->source = g_bytes_new_static ("", 1);
+          }
+      }
+      break;
+
+    default:
+      g_assert_not_reached ();
+    }
+}
+
+static void
+gsk_gl_shader_finalize (GObject *object)
+{
+  GskGLShader *shader = GSK_GL_SHADER (object);
+
+  g_bytes_unref (shader->source);
+  g_free (shader->resource);
+  for (int i = 0; i < shader->uniforms->len; i ++)
+    g_free (g_array_index (shader->uniforms, GskGLUniform, i).name);
+  g_array_free (shader->uniforms, TRUE);
+
+  G_OBJECT_CLASS (gsk_gl_shader_parent_class)->finalize (object);
+}
+
+static GRegex *uniform_regexp = NULL; /* Initialized in class_init */
+
+
+static void
+gsk_gl_shader_add_uniform (GskGLShader     *shader,
+                           const char      *name,
+                           GskGLUniformType type)
+{
+  GskGLUniform uniform = {
+    g_strdup (name),
+    type,
+    shader->uniforms_size
+  };
+
+  shader->uniforms_size += uniform_type_size (type);
+
+  g_array_append_val (shader->uniforms, uniform);
+}
+
+static void
+gsk_gl_shader_constructed (GObject *object)
+{
+  GskGLShader *shader = GSK_GL_SHADER (object);
+  gsize string_len;
+  const char *string = g_bytes_get_data (shader->source, &string_len);
+  GMatchInfo *match_info;
+  int max_texture_seen = 0;
+
+  g_regex_match_full (uniform_regexp,
+                      string, string_len, 0, 0,
+                      &match_info, NULL);
+  while (g_match_info_matches (match_info))
+    {
+      char *type = g_match_info_fetch (match_info, 1);
+      char *name = g_match_info_fetch (match_info, 2);
+
+      if (strcmp (type, "sampler2D") == 0)
+        {
+          /* Textures are special cased */
+
+          if (g_str_has_prefix (name, "u_texture") &&
+              strlen (name) == strlen ("u_texture")+1)
+            {
+              char digit = name[strlen("u_texture")];
+              if (digit >= '1' && digit <= '9')
+                max_texture_seen = MAX(max_texture_seen, digit - '0');
+            }
+          else
+            g_debug ("Unhandled shader texture uniform '%s', use uniforms of name 'u_texture[1..9]'", name);
+        }
+      else
+        {
+          GskGLUniformType utype = uniform_type_from_glsl (type);
+          g_assert (utype != GSK_GL_UNIFORM_TYPE_NONE); // Shouldn't have matched the regexp
+          gsk_gl_shader_add_uniform (shader, name, utype);
+        }
+
+      g_free (type);
+      g_free (name);
+
+      g_match_info_next (match_info, NULL);
+    }
+
+  g_match_info_free (match_info);
+
+  shader->n_textures = max_texture_seen;
+
+  if (GSK_DEBUG_CHECK(SHADERS))
+    {
+      GString *s;
+
+      s = g_string_new ("");
+      for (int i = 0; i < shader->uniforms->len; i++)
+        {
+          GskGLUniform *u = &g_array_index (shader->uniforms, GskGLUniform, i);
+          if (i > 0)
+            g_string_append (s, ", ");
+          g_string_append_printf (s, "%s %s", uniform_type_name (u->type), u->name);
+        }
+      g_message ("Shader constructed: %d textures, %d uniforms (%s)",
+                 shader->n_textures, shader->uniforms->len,
+                 s->str);
+      g_string_free (s, TRUE);
+    }
+}
+
+#define SPACE_RE "[ \\t]+" // Don't use \s, we don't want to match newlines
+#define OPT_SPACE_RE "[ \\t]*"
+#define UNIFORM_TYPE_RE "(int|uint|bool|float|vec2|vec3|vec4|sampler2D)"
+#define UNIFORM_NAME_RE "([\\w]+)"
+#define OPT_INIT_VALUE_RE "[-\\w(),. ]+" // This is a bit simple, but will match most initializers
+#define OPT_COMMENT_RE "(//.*)?"
+#define OPT_INITIALIZER_RE  "(" OPT_SPACE_RE "=" OPT_SPACE_RE  OPT_INIT_VALUE_RE ")?"
+#define UNIFORM_MATCHER_RE "^uniform" SPACE_RE UNIFORM_TYPE_RE SPACE_RE UNIFORM_NAME_RE OPT_INITIALIZER_RE 
OPT_SPACE_RE ";" OPT_SPACE_RE OPT_COMMENT_RE "$"
+
+static void
+gsk_gl_shader_class_init (GskGLShaderClass *klass)
+{
+   GObjectClass *object_class = G_OBJECT_CLASS (klass);
+   uniform_regexp = g_regex_new (UNIFORM_MATCHER_RE,
+                                 G_REGEX_MULTILINE | G_REGEX_RAW | G_REGEX_OPTIMIZE,
+                                 0, NULL);
+
+  object_class->get_property = gsk_gl_shader_get_property;
+  object_class->set_property = gsk_gl_shader_set_property;
+  object_class->finalize = gsk_gl_shader_finalize;
+  object_class->constructed = gsk_gl_shader_constructed;
+
+  /**
+   * GskGLShader:sourcecode:
+   *
+   * The source code for the shader, as a #GBytes.
+   */
+  gsk_gl_shader_properties[GLSHADER_PROP_SOURCE] =
+    g_param_spec_boxed ("source",
+                        "Source",
+                        "The sourcecode for the shader",
+                        G_TYPE_BYTES,
+                        G_PARAM_READWRITE |
+                        G_PARAM_CONSTRUCT_ONLY |
+                        G_PARAM_STATIC_STRINGS);
+
+  /**
+   * GskGLShader:resource:
+   *
+   * resource containing the source code for the shader.
+   */
+  gsk_gl_shader_properties[GLSHADER_PROP_RESOURCE] =
+    g_param_spec_string ("resource",
+                         "Resources",
+                         "Resource path to the source code",
+                         NULL,
+                         G_PARAM_READWRITE |
+                         G_PARAM_CONSTRUCT_ONLY |
+                         G_PARAM_STATIC_STRINGS);
+
+  g_object_class_install_properties (object_class, GLSHADER_N_PROPS, gsk_gl_shader_properties);
+}
+
+static void
+gsk_gl_shader_init (GskGLShader *shader)
+{
+  shader->uniforms = g_array_new (FALSE, FALSE, sizeof (GskGLUniform));
+}
+
+/**
+ * gsk_gl_shader_new_from_bytes:
+ * @sourcecode: The sourcecode for the shader, as a #GBytes
+ *
+ * Creates a #GskGLShader that will render pixels using the specified code.
+ *
+ * Returns: (transfer full): A new #GskGLShader
+ */
+GskGLShader *
+gsk_gl_shader_new_from_bytes (GBytes *sourcecode)
+{
+  g_return_val_if_fail (sourcecode != NULL, NULL);
+
+  return g_object_new (GSK_TYPE_GL_SHADER,
+                       "source", sourcecode,
+                       NULL);
+}
+
+/**
+ * gsk_gl_shader_new_from_resource:
+ * @resource_path: valid path to a resource that contains the sourcecode for the shader
+ *
+ * Creates a #GskGLShader that will render pixels using the specified code.
+ *
+ * Returns: (transfer full): A new #GskGLShader
+ */
+GskGLShader *
+gsk_gl_shader_new_from_resource (const char      *resource_path)
+{
+  g_return_val_if_fail (resource_path != NULL, NULL);
+
+  return g_object_new (GSK_TYPE_GL_SHADER,
+                       "resource", resource_path,
+                       NULL);
+}
+
+/**
+ * gsk_gl_shader_compile:
+ * @shader: A #GskGLShader
+ * @renderer: A #GskRenderer
+ * @error: Location to store error int
+ *
+ * Tries to compile the @shader for the given @renderer, and reports
+ * %FALSE with an error if there is a problem. You should use this
+ * before relying on the shader for rendering and use a fallback with
+ * a simpler shader or without shaders if it fails.
+ *
+ * Note that this will modify the rendering state (for example
+ * change the current GL context) and requires the renderer to be
+ * set up. This means that the widget has to be realized. Commonly you
+ * want to call this from the realize signal of a widget, or during
+ * widget snapshot.
+ *
+ * Returns: %TRUE on success, %FALSE if an error occurred
+ */
+gboolean
+gsk_gl_shader_compile (GskGLShader      *shader,
+                       GskRenderer      *renderer,
+                       GError          **error)
+{
+  g_return_val_if_fail (GSK_IS_GL_SHADER (shader), FALSE);
+
+  if (GSK_IS_GL_RENDERER (renderer))
+    return gsk_gl_render_try_compile_gl_shader (GSK_GL_RENDERER (renderer),
+                                                shader, error);
+
+  g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+               "The renderer does not support gl shaders");
+  return FALSE;
+}
+
+
+/**
+ * gsk_gl_shader_get_source:
+ * @shader: A #GskGLShader
+ *
+ * Get the source code being used to render this shader.
+ *
+ * Returns: (transfer none): The source code for the shader
+ */
+GBytes *
+gsk_gl_shader_get_source (GskGLShader *shader)
+{
+  g_return_val_if_fail (GSK_IS_GL_SHADER (shader), NULL);
+
+  return shader->source;
+}
+
+/**
+ * gsk_gl_shader_get_resource:
+ * @shader: A #GskGLShader
+ *
+ * Get the resource path for the sourcecode being used to render this shader.
+ *
+ * Returns: (transfer none): The resource path for the shader, or %NULL if none.
+ */
+const char *
+gsk_gl_shader_get_resource (GskGLShader *shader)
+{
+  g_return_val_if_fail (GSK_IS_GL_SHADER (shader), NULL);
+
+  return shader->resource;
+}
+
+/**
+ * gsk_gl_shader_get_n_textures:
+ * @shader: A #GskGLShader
+ *
+ * Returns the number of textures that the shader requires. This can be used
+ * to check that the a passed shader works in your usecase. This
+ * is determined by looking at the highest u_textureN value that the
+ * shader defines.
+ *
+ * Returns: The nr of texture input this shader requires.
+ */
+int
+gsk_gl_shader_get_n_textures (GskGLShader  *shader)
+{
+  g_return_val_if_fail (GSK_IS_GL_SHADER (shader), 0);
+
+  return shader->n_textures;
+}
+
+/**
+ * gsk_gl_shader_get_n_uniforms:
+ * @shader: A #GskGLShader
+ *
+ * Get the number of declared uniforms for this shader.
+ *
+ * Returns: The nr of declared uniforms
+ */
+int
+gsk_gl_shader_get_n_uniforms (GskGLShader     *shader)
+{
+  g_return_val_if_fail (GSK_IS_GL_SHADER (shader), 0);
+
+  return shader->uniforms->len;
+}
+
+/**
+ * gsk_gl_shader_get_uniform_name:
+ * @shader: A #GskGLShader
+ * @idx: A zero-based index of the uniforms
+ *
+ * Get the name of a declared uniforms for this shader at index @indx.
+ *
+ * Returns: (transfer none): The name of the declared uniform
+ */
+const char *
+gsk_gl_shader_get_uniform_name (GskGLShader     *shader,
+                                int              idx)
+{
+  g_return_val_if_fail (GSK_IS_GL_SHADER (shader), NULL);
+
+  return g_array_index (shader->uniforms, GskGLUniform, idx).name;
+}
+
+/**
+ * gsk_gl_shader_find_uniform_by_name:
+ * @shader: A #GskGLShader
+ * @name: A uniform name
+ *
+ * Looks for a uniform by the name @name, and returns the index
+ * of the unifor, or -1 if it was not found.
+ *
+ * Returns: The index of the uniform, or -1
+ */
+int
+gsk_gl_shader_find_uniform_by_name (GskGLShader      *shader,
+                                    const char       *name)
+{
+  g_return_val_if_fail (GSK_IS_GL_SHADER (shader), -1);
+
+  for (int i = 0; i < shader->uniforms->len; i++)
+    {
+      const GskGLUniform *u = &g_array_index (shader->uniforms, GskGLUniform, i);
+      if (strcmp (u->name, name) == 0)
+        return i;
+    }
+
+  return -1;
+}
+
+/**
+ * gsk_gl_shader_get_uniform_type:
+ * @shader: A #GskGLShader
+ * @idx: A zero-based index of the uniforms
+ *
+ * Get the type of a declared uniforms for this shader at index @indx.
+ *
+ * Returns: The type of the declared uniform
+ */
+GskGLUniformType
+gsk_gl_shader_get_uniform_type (GskGLShader     *shader,
+                                int              idx)
+{
+  g_return_val_if_fail (GSK_IS_GL_SHADER (shader), 0);
+
+  return g_array_index (shader->uniforms, GskGLUniform, idx).type;
+}
+
+/**
+ * gsk_gl_shader_get_uniform_offset:
+ * @shader: A #GskGLShader
+ * @idx: A zero-based index of the uniforms
+ *
+ * Get the offset into the data block where data for this uniforms is stored.
+ *
+ * Returns: The data offset
+ */
+int
+gsk_gl_shader_get_uniform_offset (GskGLShader     *shader,
+                                  int              idx)
+{
+  g_return_val_if_fail (GSK_IS_GL_SHADER (shader), 0);
+
+  return g_array_index (shader->uniforms, GskGLUniform, idx).offset;
+}
+
+const GskGLUniform *
+gsk_gl_shader_get_uniforms (GskGLShader *shader,
+                            int         *n_uniforms)
+{
+  *n_uniforms = shader->uniforms->len;
+  return &g_array_index (shader->uniforms, GskGLUniform, 0);
+}
+
+/**
+ * gsk_gl_shader_get_args_size:
+ * @shader: A #GskGLShader
+ *
+ * Get the size of the data block used to specify arguments for this shader.
+ *
+ * Returns: The size of the data block
+ */
+gsize
+gsk_gl_shader_get_args_size (GskGLShader *shader)
+{
+  g_return_val_if_fail (GSK_IS_GL_SHADER (shader), 0);
+
+  return shader->uniforms_size;
+}
+
+static const GskGLUniform *
+gsk_gl_shader_find_uniform (GskGLShader *shader,
+                            const char *name)
+{
+  for (int i = 0; i < shader->uniforms->len; i++)
+    {
+      const GskGLUniform *u = &g_array_index (shader->uniforms, GskGLUniform, i);
+      if (strcmp (u->name, name) == 0)
+        return u;
+    }
+
+  return NULL;
+}
+
+/**
+ * gsk_gl_shader_get_arg_float:
+ * @shader: A #GskGLShader
+ * @args: The uniform arguments
+ * @idx: The index of the uniform
+ *
+ * Gets the value of the uniform @idx in the @args block.
+ * The uniform must be of float type.
+ *
+ * Returns: The value
+ */
+float
+gsk_gl_shader_get_arg_float (GskGLShader *shader,
+                             GBytes       *args,
+                             int          idx)
+{
+  const GskGLUniform *u;
+  const guchar *args_src;
+  gsize size;
+  const guchar *data = g_bytes_get_data (args, &size);
+
+  g_return_val_if_fail (GSK_IS_GL_SHADER (shader), 0);
+
+  g_assert (size == shader->uniforms_size);
+  g_assert (idx < shader->uniforms->len);
+  u = &g_array_index (shader->uniforms, GskGLUniform, idx);
+  g_assert (u->type == GSK_GL_UNIFORM_TYPE_FLOAT);
+
+  args_src = data + u->offset;
+  return *(float *)args_src;
+}
+
+/**
+ * gsk_gl_shader_get_arg_int:
+ * @shader: A #GskGLShader
+ * @args: The uniform arguments
+ * @idx: The index of the uniform
+ *
+ * Gets the value of the uniform @idx in the @args block.
+ * The uniform must be of int type.
+ *
+ * Returns: The value
+ */
+gint32
+gsk_gl_shader_get_arg_int (GskGLShader *shader,
+                           GBytes       *args,
+                           int          idx)
+{
+  const GskGLUniform *u;
+  const guchar *args_src;
+  gsize size;
+  const guchar *data = g_bytes_get_data (args, &size);
+
+  g_return_val_if_fail (GSK_IS_GL_SHADER (shader), 0);
+
+  g_assert (size == shader->uniforms_size);
+  g_assert (idx < shader->uniforms->len);
+  u = &g_array_index (shader->uniforms, GskGLUniform, idx);
+  g_assert (u->type == GSK_GL_UNIFORM_TYPE_INT);
+
+  args_src = data + u->offset;
+  return *(gint32 *)args_src;
+}
+
+/**
+ * gsk_gl_shader_get_arg_uint:
+ * @shader: A #GskGLShader
+ * @args: The uniform arguments
+ * @idx: The index of the uniform
+ *
+ * Gets the value of the uniform @idx in the @args block.
+ * The uniform must be of uint type.
+ *
+ * Returns: The value
+ */
+guint32
+gsk_gl_shader_get_arg_uint (GskGLShader *shader,
+                            GBytes       *args,
+                            int          idx)
+{
+  const GskGLUniform *u;
+  const guchar *args_src;
+  gsize size;
+  const guchar *data = g_bytes_get_data (args, &size);
+
+  g_return_val_if_fail (GSK_IS_GL_SHADER (shader), 0);
+
+  g_assert (size == shader->uniforms_size);
+  g_assert (idx < shader->uniforms->len);
+  u = &g_array_index (shader->uniforms, GskGLUniform, idx);
+  g_assert (u->type == GSK_GL_UNIFORM_TYPE_UINT);
+
+  args_src = data + u->offset;
+  return *(guint32 *)args_src;
+}
+
+/**
+ * gsk_gl_shader_get_arg_bool:
+ * @shader: A #GskGLShader
+ * @args: The uniform arguments
+ * @idx: The index of the uniform
+ *
+ * Gets the value of the uniform @idx in the @args block.
+ * The uniform must be of bool type.
+ *
+ * Returns: The value
+ */
+gboolean
+gsk_gl_shader_get_arg_bool (GskGLShader *shader,
+                            GBytes       *args,
+                            int          idx)
+{
+  const GskGLUniform *u;
+  const guchar *args_src;
+  gsize size;
+  const guchar *data = g_bytes_get_data (args, &size);
+
+  g_return_val_if_fail (GSK_IS_GL_SHADER (shader), 0);
+
+  g_assert (size == shader->uniforms_size);
+  g_assert (idx < shader->uniforms->len);
+  u = &g_array_index (shader->uniforms, GskGLUniform, idx);
+  g_assert (u->type == GSK_GL_UNIFORM_TYPE_BOOL);
+
+  args_src = data + u->offset;
+  return *(guint32 *)args_src;
+}
+
+/**
+ * gsk_gl_shader_get_arg_vec2:
+ * @shader: A #GskGLShader
+ * @args: The uniform arguments
+ * @idx: The index of the uniform
+ * @out_value: Location to store set the uniform value too
+ *
+ * Gets the value of the uniform @idx in the @args block.
+ * The uniform must be of vec2 type.
+ */
+void
+gsk_gl_shader_get_arg_vec2 (GskGLShader           *shader,
+                            GBytes                *args,
+                            int                    idx,
+                            graphene_vec2_t       *out_value)
+{
+  const GskGLUniform *u;
+  const guchar *args_src;
+  gsize size;
+  const guchar *data = g_bytes_get_data (args, &size);
+
+  g_return_if_fail (GSK_IS_GL_SHADER (shader));
+
+  g_assert (size == shader->uniforms_size);
+  g_assert (idx < shader->uniforms->len);
+  u = &g_array_index (shader->uniforms, GskGLUniform, idx);
+  g_assert (u->type == GSK_GL_UNIFORM_TYPE_VEC2);
+
+  args_src = data + u->offset;
+  graphene_vec2_init_from_float (out_value, (float *)args_src);
+}
+
+/**
+ * gsk_gl_shader_get_arg_vec3:
+ * @shader: A #GskGLShader
+ * @args: The uniform arguments
+ * @idx: The index of the uniform
+ * @out_value: Location to store set the uniform value too
+ *
+ * Gets the value of the uniform @idx in the @args block.
+ * The uniform must be of vec3 type.
+ */
+void
+gsk_gl_shader_get_arg_vec3 (GskGLShader           *shader,
+                            GBytes                *args,
+                            int                    idx,
+                            graphene_vec3_t       *out_value)
+{
+  const GskGLUniform *u;
+  const guchar *args_src;
+  gsize size;
+  const guchar *data = g_bytes_get_data (args, &size);
+
+  g_return_if_fail (GSK_IS_GL_SHADER (shader));
+
+  g_assert (size == shader->uniforms_size);
+  g_assert (idx < shader->uniforms->len);
+  u = &g_array_index (shader->uniforms, GskGLUniform, idx);
+  g_assert (u->type == GSK_GL_UNIFORM_TYPE_VEC3);
+
+  args_src = data + u->offset;
+  graphene_vec3_init_from_float (out_value, (float *)args_src);
+}
+
+/**
+ * gsk_gl_shader_get_arg_vec4:
+ * @shader: A #GskGLShader
+ * @args: The uniform arguments
+ * @idx: The index of the uniform
+ * @out_value: Location to store set the uniform value too
+ *
+ * Gets the value of the uniform @idx in the @args block.
+ * The uniform must be of vec4 type.
+ */
+void
+gsk_gl_shader_get_arg_vec4 (GskGLShader           *shader,
+                            GBytes                *args,
+                            int                    idx,
+                            graphene_vec4_t       *out_value)
+{
+  const GskGLUniform *u;
+  const guchar *args_src;
+  gsize size;
+  const guchar *data = g_bytes_get_data (args, &size);
+
+  g_return_if_fail (GSK_IS_GL_SHADER (shader));
+
+  g_assert (size == shader->uniforms_size);
+  g_assert (idx < shader->uniforms->len);
+  u = &g_array_index (shader->uniforms, GskGLUniform, idx);
+  g_assert (u->type == GSK_GL_UNIFORM_TYPE_VEC4);
+
+  args_src = data + u->offset;
+  graphene_vec4_init_from_float (out_value, (float *)args_src);
+}
+
+/**
+ * gsk_gl_shader_format_args_va:
+ * @shader: A #GskGLShader
+ * @uniforms: Name-Value pairs for the uniforms of @shader, ending with a
+ *     %NULL name, all values are passed by reference.
+ *
+ * Formats the uniform data as needed for feeding the named uniforms
+ * values into the shader.  The argument list is a list of pairs of
+ * names, and values for the types that match the declared uniforms
+ * (i.e. double/int/guint/gboolean for primitive values and
+ * `graphene_vecN_t *` for vecN uniforms).
+ *
+ * It is an error to pass a uniform name that is not declared by the shader.
+ *
+ * Any uniforms of the shader that are not included in the argument list
+ * are zero-initialized.
+ *
+ * Returns: (transfer full): A newly allocated block of data which can be
+ *     passed to gsk_gl_shader_node_new().
+ */
+GBytes *
+gsk_gl_shader_format_args_va (GskGLShader *shader,
+                              va_list uniforms)
+{
+  guchar *args = g_malloc0 (shader->uniforms_size);
+  const char *name;
+
+  g_return_val_if_fail (GSK_IS_GL_SHADER (shader), NULL);
+
+  while ((name = va_arg (uniforms, const char *)) != NULL)
+    {
+      const GskGLUniform *u;
+      guchar *args_dest;
+
+      u = gsk_gl_shader_find_uniform (shader, name);
+      if (u == NULL)
+        {
+          g_warning ("No uniform named `%s` in shader", name);
+          break;
+        }
+
+      args_dest = args + u->offset;
+
+      /* We use pointers-to-value so that all values are the same
+         size, otherwise we couldn't handle the missing uniform case above */
+
+      switch (u->type)
+        {
+        case GSK_GL_UNIFORM_TYPE_FLOAT:
+          *(float *)args_dest = (float)va_arg (uniforms, double); /* floats are promoted to double in 
varargs */
+          break;
+
+        case GSK_GL_UNIFORM_TYPE_INT:
+          *(gint32 *)args_dest = (gint32)va_arg (uniforms, int);
+          break;
+
+        case GSK_GL_UNIFORM_TYPE_UINT:
+          *(guint32 *)args_dest = (guint32)va_arg (uniforms, guint);
+          break;
+
+        case GSK_GL_UNIFORM_TYPE_BOOL:
+          *(guint32 *)args_dest = (gboolean)va_arg (uniforms, gboolean);
+          break;
+
+        case GSK_GL_UNIFORM_TYPE_VEC2:
+          graphene_vec2_to_float (va_arg (uniforms, const graphene_vec2_t *),
+                                  (float *)args_dest);
+          break;
+
+        case GSK_GL_UNIFORM_TYPE_VEC3:
+          graphene_vec3_to_float (va_arg (uniforms, const graphene_vec3_t *),
+                                  (float *)args_dest);
+          break;
+
+        case GSK_GL_UNIFORM_TYPE_VEC4:
+          graphene_vec4_to_float (va_arg (uniforms, const graphene_vec4_t *),
+                                  (float *)args_dest);
+          break;
+
+        case GSK_GL_UNIFORM_TYPE_NONE:
+        default:
+          g_assert_not_reached ();
+        }
+    }
+
+  return g_bytes_new_take (args, shader->uniforms_size);
+}
+
+/**
+ * gsk_gl_shader_format_args:
+ * @shader: A #GskGLShader
+ * @...: Name-Value pairs for the uniforms of @shader, ending with a %NULL
+ *     name, all values are passed by reference.
+ *
+ * Formats the uniform data as needed for feeding the named uniforms
+ * values into the shader. The argument list is a list of pairs of
+ * names, and values for the types that match the declared uniforms
+ * (i.e. double/int/guint/gboolean for primitive values and
+ * `graphene_vecN_t *` for vecN uniforms).
+ *
+ * Any uniforms of the shader that are not included in the argument list
+ * are zero-initialized.
+ *
+ * Returns: (transfer full): A newly allocated block of data which can be
+ *     passed to gsk_gl_shader_node_new().
+ */
+GBytes *
+gsk_gl_shader_format_args (GskGLShader *shader,
+                           ...)
+{
+  GBytes *bytes;
+  va_list args;
+
+  va_start (args, shader);
+  bytes = gsk_gl_shader_format_args_va (shader, args);
+  va_end (args);
+
+  return bytes;
+}
+
+struct _GskShaderArgsBuilder {
+  guint ref_count;
+  GskGLShader *shader;
+  guchar *data;
+};
+
+G_DEFINE_BOXED_TYPE (GskShaderArgsBuilder, gsk_shader_args_builder,
+                     gsk_shader_args_builder_ref,
+                     gsk_shader_args_builder_unref);
+
+
+/**
+ * gsk_gl_shader_build_args:
+ * @shader: A #GskGLShader
+ * @initial_values: (nullable): optional #Bytes with initial values
+ *
+ * Allocates a builder that can be used to construct a new uniform data
+ * chunk.
+ *
+ * Returns: (transfer full): The newly allocated builder, free with
+ *     gsk_shader_args_builder_free()
+ */
+GskShaderArgsBuilder *
+gsk_shader_args_builder_new (GskGLShader *shader,
+                             GBytes      *initial_values)
+{
+  GskShaderArgsBuilder *builder = g_new0 (GskShaderArgsBuilder, 1);
+  builder->ref_count = 1;
+  builder->shader = g_object_ref (shader);
+  builder->data = g_malloc0 (shader->uniforms_size);
+
+  if (initial_values)
+    {
+      gsize size;
+      const guchar *data = g_bytes_get_data (initial_values, &size);
+
+      g_assert (size == shader->uniforms_size);
+      memcpy (builder->data, data, size);
+    }
+
+  return builder;
+}
+
+/**
+ * gsk_shader_args_builder_to_args:
+ * @builder: A #GskShaderArgsBuilder
+ *
+ * Creates a new #GBytes args from the current state of the
+ * given @builder. Any uniforms of the shader that have not
+ * been explicitly set on the @builder are zero-initialized.
+ *
+ * The given #GskShaderArgsBuilder is reset once this function returns;
+ * you cannot call this function multiple times on the same @builder instance.
+ *
+ * This function is intended primarily for bindings. C code should use
+ * gsk_shader_args_builder_free_to_args().
+ *
+ *
+ * Returns: (transfer full): The newly allocated builder, free with
+ *     gsk_shader_args_builder_free()
+ */
+GBytes *
+gsk_shader_args_builder_to_args (GskShaderArgsBuilder *builder)
+{
+  return g_bytes_new_take (g_steal_pointer (&builder->data),
+                           builder->shader->uniforms_size);
+}
+
+/**
+ * gdk_content_formats_builder_free_to_args: (skip)
+ * @builder: a #GdkContentFormatsBuilder
+ *
+ * Creates a new #GBytes args from the current state of the
+ * given @builder, and frees the @builder instance. Any uniforms
+ * of the shader that have not been explicitly set on the @builder
+ * are zero-initialized.
+ *
+ * Returns: (transfer full): the newly created #GBytes
+ *   with all the args added to @builder
+ */
+GBytes *
+gsk_shader_args_builder_free_to_args (GskShaderArgsBuilder *builder)
+{
+  GBytes *res;
+
+  g_return_val_if_fail (builder != NULL, NULL);
+
+  res = gsk_shader_args_builder_to_args (builder);
+
+  gsk_shader_args_builder_unref (builder);
+
+  return res;
+}
+
+
+/**
+ * gsk_shader_args_builder_unref:
+ * @builder: A #GskShaderArgsBuilder
+ *
+ * Decreases the reference count of a #GskShaderArgBuilder by one.
+ * If the resulting reference count is zero, frees the builder.
+ */
+void
+gsk_shader_args_builder_unref (GskShaderArgsBuilder *builder)
+
+{
+  g_return_if_fail (builder != NULL);
+  g_return_if_fail (builder->ref_count > 0);
+
+  builder->ref_count--;
+  if (builder->ref_count > 0)
+    return;
+
+  g_object_unref (builder->shader);
+  g_free (builder->data);
+  g_free (builder);
+}
+
+/**
+ * gsk_shader_args_builder_ref:
+ * @builder: A #GskShaderArgsBuilder
+ *
+ * Increases the reference count of a #GskShaderArgsBuilder by one.
+ *
+ * Returns: the passed in #GskShaderArgsBuilder.
+ */
+GskShaderArgsBuilder *
+gsk_shader_args_builder_ref (GskShaderArgsBuilder *builder)
+{
+  g_return_val_if_fail (builder != NULL, NULL);
+
+  builder->ref_count++;
+  return builder;
+}
+
+/**
+ * gsk_shader_args_builder_set_float:
+ * @builder: A #GskShaderArgsBuilder
+ * @idx: The index of the uniform
+ * @value: The value to set the uniform too
+ *
+ * Sets the value of the uniform @idx.
+ * The uniform must be of float type.
+ */
+void
+gsk_shader_args_builder_set_float (GskShaderArgsBuilder *builder,
+                                    int                    idx,
+                                    float                  value)
+{
+  GskGLShader *shader = builder->shader;
+  const GskGLUniform *u;
+  guchar *args_dest;
+
+  g_assert (builder->data != NULL);
+  g_assert (idx < shader->uniforms->len);
+  u = &g_array_index (shader->uniforms, GskGLUniform, idx);
+  g_assert (u->type == GSK_GL_UNIFORM_TYPE_FLOAT);
+
+  args_dest = builder->data + u->offset;
+  *(float *)args_dest = value;
+}
+
+/**
+ * gsk_shader_args_builder_set_int:
+ * @builder: A #GskShaderArgsBuilder
+ * @idx: The index of the uniform
+ * @value: The value to set the uniform too
+ *
+ * Sets the value of the uniform @idx.
+ * The uniform must be of int type.
+ */
+void
+gsk_shader_args_builder_set_int (GskShaderArgsBuilder *builder,
+                                 int                    idx,
+                                 gint32                 value)
+{
+  GskGLShader *shader = builder->shader;
+  const GskGLUniform *u;
+  guchar *args_dest;
+
+  g_assert (builder->data != NULL);
+  g_assert (idx < shader->uniforms->len);
+  u = &g_array_index (shader->uniforms, GskGLUniform, idx);
+  g_assert (u->type == GSK_GL_UNIFORM_TYPE_INT);
+
+  args_dest = builder->data + u->offset;
+  *(gint32 *)args_dest = value;
+}
+
+/**
+ * gsk_shader_args_builder_set_uint:
+ * @builder: A #GskShaderArgsBuilder
+ * @idx: The index of the uniform
+ * @value: The value to set the uniform too
+ *
+ * Sets the value of the uniform @idx.
+ * The uniform must be of uint type.
+ */
+void
+gsk_shader_args_builder_set_uint (GskShaderArgsBuilder *builder,
+                                  int                    idx,
+                                  guint32                value)
+{
+  GskGLShader *shader = builder->shader;
+  const GskGLUniform *u;
+  guchar *args_dest;
+
+  g_assert (builder->data != NULL);
+  g_assert (idx < shader->uniforms->len);
+  u = &g_array_index (shader->uniforms, GskGLUniform, idx);
+  g_assert (u->type == GSK_GL_UNIFORM_TYPE_UINT);
+
+  args_dest = builder->data + u->offset;
+  *(guint32 *)args_dest = value;
+}
+
+/**
+ * gsk_shader_args_builder_set_bool:
+ * @builder: A #GskShaderArgsBuilder
+ * @idx: The index of the uniform
+ * @value: The value to set the uniform too
+ *
+ * Sets the value of the uniform @idx.
+ * The uniform must be of bool type.
+ */
+void
+gsk_shader_args_builder_set_bool (GskShaderArgsBuilder *builder,
+                                  int                    idx,
+                                  gboolean               value)
+{
+  GskGLShader *shader = builder->shader;
+  const GskGLUniform *u;
+  guchar *args_dest;
+
+  g_assert (builder->data != NULL);
+  g_assert (idx < shader->uniforms->len);
+  u = &g_array_index (shader->uniforms, GskGLUniform, idx);
+  g_assert (u->type == GSK_GL_UNIFORM_TYPE_BOOL);
+
+  args_dest = builder->data + u->offset;
+  *(guint32 *)args_dest = !!value;
+}
+
+/**
+ * gsk_shader_args_builder_set_vec2:
+ * @builder: A #GskShaderArgsBuilder
+ * @idx: The index of the uniform
+ * @value: The value to set the uniform too
+ *
+ * Sets the value of the uniform @idx.
+ * The uniform must be of vec2 type.
+ */
+void
+gsk_shader_args_builder_set_vec2 (GskShaderArgsBuilder *builder,
+                                  int                    idx,
+                                  const graphene_vec2_t *value)
+{
+  GskGLShader *shader = builder->shader;
+  const GskGLUniform *u;
+  guchar *args_dest;
+
+  g_assert (builder->data != NULL);
+  g_assert (idx < shader->uniforms->len);
+  u = &g_array_index (shader->uniforms, GskGLUniform, idx);
+  g_assert (u->type == GSK_GL_UNIFORM_TYPE_VEC2);
+
+  args_dest = builder->data + u->offset;
+  graphene_vec2_to_float (value, (float *)args_dest);
+}
+
+/**
+ * gsk_shader_args_builder_set_vec3:
+ * @builder: A #GskShaderArgsBuilder
+ * @idx: The index of the uniform
+ * @value: The value to set the uniform too
+ *
+ * Sets the value of the uniform @idx.
+ * The uniform must be of vec3 type.
+ */
+void
+gsk_shader_args_builder_set_vec3 (GskShaderArgsBuilder *builder,
+                                  int                    idx,
+                                  const graphene_vec3_t *value)
+{
+  GskGLShader *shader = builder->shader;
+  const GskGLUniform *u;
+  guchar *args_dest;
+
+  g_assert (builder->data != NULL);
+  g_assert (idx < shader->uniforms->len);
+  u = &g_array_index (shader->uniforms, GskGLUniform, idx);
+  g_assert (u->type == GSK_GL_UNIFORM_TYPE_VEC3);
+
+  args_dest = builder->data + u->offset;
+  graphene_vec3_to_float (value, (float *)args_dest);
+}
+
+/**
+ * gsk_shader_args_builder_set_vec4:
+ * @builder: A #GskShaderArgsBuilder
+ * @idx: The index of the uniform
+ * @value: The value to set the uniform too
+ *
+ * Sets the value of the uniform @idx.
+ * The uniform must be of vec4 type.
+ */
+void
+gsk_shader_args_builder_set_vec4 (GskShaderArgsBuilder *builder,
+                                  int                    idx,
+                                  const graphene_vec4_t *value)
+{
+  GskGLShader *shader = builder->shader;
+  const GskGLUniform *u;
+  guchar *args_dest;
+
+  g_assert (builder->data != NULL);
+  g_assert (idx < shader->uniforms->len);
+  u = &g_array_index (shader->uniforms, GskGLUniform, idx);
+  g_assert (u->type == GSK_GL_UNIFORM_TYPE_VEC4);
+
+  args_dest = builder->data + u->offset;
+  graphene_vec4_to_float (value, (float *)args_dest);
+}
diff --git a/gsk/gskglshader.h b/gsk/gskglshader.h
new file mode 100644
index 0000000000..5a35394095
--- /dev/null
+++ b/gsk/gskglshader.h
@@ -0,0 +1,162 @@
+/* GSK - The GTK Scene Kit
+ *
+ * Copyright 2020  Red Hat Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GSK_GL_SHADER_H__
+#define __GSK_GL_SHADER_H__
+
+#if !defined (__GSK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gsk/gsk.h> can be included directly."
+#endif
+
+#include <stdarg.h>
+
+#include <gsk/gsktypes.h>
+#include <gsk/gskenums.h>
+
+G_BEGIN_DECLS
+
+#define GSK_TYPE_SHADER_ARGS_BUILDER    (gsk_shader_args_builder_get_type ())
+
+typedef struct _GskShaderArgsBuilder GskShaderArgsBuilder;
+
+#define GSK_TYPE_GL_SHADER (gsk_gl_shader_get_type ())
+
+GDK_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (GskGLShader, gsk_gl_shader, GSK, GL_SHADER, GObject)
+
+GDK_AVAILABLE_IN_ALL
+GskGLShader *    gsk_gl_shader_new_from_bytes          (GBytes           *sourcecode);
+GDK_AVAILABLE_IN_ALL
+GskGLShader *    gsk_gl_shader_new_from_resource       (const char       *resource_path);
+GDK_AVAILABLE_IN_ALL
+gboolean         gsk_gl_shader_compile                 (GskGLShader      *shader,
+                                                        GskRenderer      *renderer,
+                                                        GError          **error);
+GDK_AVAILABLE_IN_ALL
+GBytes *         gsk_gl_shader_get_source              (GskGLShader      *shader);
+GDK_AVAILABLE_IN_ALL
+const char *     gsk_gl_shader_get_resource            (GskGLShader      *shader);
+GDK_AVAILABLE_IN_ALL
+int              gsk_gl_shader_get_n_textures          (GskGLShader      *shader);
+GDK_AVAILABLE_IN_ALL
+int              gsk_gl_shader_get_n_uniforms          (GskGLShader      *shader);
+GDK_AVAILABLE_IN_ALL
+const char *     gsk_gl_shader_get_uniform_name        (GskGLShader      *shader,
+                                                       int               idx);
+GDK_AVAILABLE_IN_ALL
+int              gsk_gl_shader_find_uniform_by_name    (GskGLShader      *shader,
+                                                       const char       *name);
+GDK_AVAILABLE_IN_ALL
+GskGLUniformType gsk_gl_shader_get_uniform_type        (GskGLShader      *shader,
+                                                       int               idx);
+GDK_AVAILABLE_IN_ALL
+int              gsk_gl_shader_get_uniform_offset      (GskGLShader      *shader,
+                                                       int               idx);
+GDK_AVAILABLE_IN_ALL
+gsize            gsk_gl_shader_get_args_size       (GskGLShader      *shader);
+
+
+/* Helpers for managing shader args */
+
+GDK_AVAILABLE_IN_ALL
+GBytes * gsk_gl_shader_format_args_va (GskGLShader     *shader,
+                                       va_list          uniforms);
+GDK_AVAILABLE_IN_ALL
+GBytes * gsk_gl_shader_format_args    (GskGLShader     *shader,
+                                       ...) G_GNUC_NULL_TERMINATED;
+
+GDK_AVAILABLE_IN_ALL
+float    gsk_gl_shader_get_arg_float (GskGLShader     *shader,
+                                      GBytes          *args,
+                                      int              idx);
+GDK_AVAILABLE_IN_ALL
+gint32   gsk_gl_shader_get_arg_int   (GskGLShader     *shader,
+                                      GBytes          *args,
+                                      int              idx);
+GDK_AVAILABLE_IN_ALL
+guint32  gsk_gl_shader_get_arg_uint  (GskGLShader     *shader,
+                                      GBytes          *args,
+                                      int              idx);
+GDK_AVAILABLE_IN_ALL
+gboolean gsk_gl_shader_get_arg_bool  (GskGLShader     *shader,
+                                      GBytes          *args,
+                                      int              idx);
+GDK_AVAILABLE_IN_ALL
+void     gsk_gl_shader_get_arg_vec2  (GskGLShader     *shader,
+                                      GBytes          *args,
+                                      int              idx,
+                                      graphene_vec2_t *out_value);
+GDK_AVAILABLE_IN_ALL
+void     gsk_gl_shader_get_arg_vec3  (GskGLShader     *shader,
+                                      GBytes          *args,
+                                      int              idx,
+                                      graphene_vec3_t *out_value);
+GDK_AVAILABLE_IN_ALL
+void     gsk_gl_shader_get_arg_vec4  (GskGLShader     *shader,
+                                      GBytes          *args,
+                                      int              idx,
+                                      graphene_vec4_t *out_value);
+
+GDK_AVAILABLE_IN_ALL
+GType   gsk_shader_args_builder_get_type  (void) G_GNUC_CONST;
+
+GDK_AVAILABLE_IN_ALL
+GskShaderArgsBuilder *gsk_shader_args_builder_new           (GskGLShader *shader,
+                                                             GBytes      *initial_values);
+GDK_AVAILABLE_IN_ALL
+GBytes *               gsk_shader_args_builder_to_args      (GskShaderArgsBuilder *builder);
+GDK_AVAILABLE_IN_ALL
+GBytes *               gsk_shader_args_builder_free_to_args (GskShaderArgsBuilder *builder);
+GDK_AVAILABLE_IN_ALL
+GskShaderArgsBuilder  *gsk_shader_args_builder_ref          (GskShaderArgsBuilder *builder);
+GDK_AVAILABLE_IN_ALL
+void                   gsk_shader_args_builder_unref        (GskShaderArgsBuilder *builder);
+
+GDK_AVAILABLE_IN_ALL
+void    gsk_shader_args_builder_set_float (GskShaderArgsBuilder *builder,
+                                           int                    idx,
+                                           float                  value);
+GDK_AVAILABLE_IN_ALL
+void    gsk_shader_args_builder_set_int   (GskShaderArgsBuilder *builder,
+                                           int                    idx,
+                                           gint32                 value);
+GDK_AVAILABLE_IN_ALL
+void    gsk_shader_args_builder_set_uint  (GskShaderArgsBuilder *builder,
+                                           int                    idx,
+                                           guint32                value);
+GDK_AVAILABLE_IN_ALL
+void    gsk_shader_args_builder_set_bool  (GskShaderArgsBuilder *builder,
+                                           int                    idx,
+                                           gboolean               value);
+GDK_AVAILABLE_IN_ALL
+void    gsk_shader_args_builder_set_vec2  (GskShaderArgsBuilder *builder,
+                                           int                    idx,
+                                           const graphene_vec2_t *value);
+GDK_AVAILABLE_IN_ALL
+void    gsk_shader_args_builder_set_vec3  (GskShaderArgsBuilder *builder,
+                                           int                    idx,
+                                           const graphene_vec3_t *value);
+GDK_AVAILABLE_IN_ALL
+void    gsk_shader_args_builder_set_vec4  (GskShaderArgsBuilder *builder,
+                                           int                    idx,
+                                           const graphene_vec4_t *value);
+
+
+G_END_DECLS
+
+#endif /* __GSK_GL_SHADER_H__ */
diff --git a/gsk/gskglshaderprivate.h b/gsk/gskglshaderprivate.h
new file mode 100644
index 0000000000..3dc82c35dc
--- /dev/null
+++ b/gsk/gskglshaderprivate.h
@@ -0,0 +1,19 @@
+#ifndef __GSK_GLSHADER_PRIVATE_H__
+#define __GSK_GLSHADER_PRIVATE_H__
+
+#include <gsk/gskglshader.h>
+
+G_BEGIN_DECLS
+
+typedef struct {
+  char *name;
+  GskGLUniformType type;
+  gsize offset;
+} GskGLUniform;
+
+const GskGLUniform *gsk_gl_shader_get_uniforms (GskGLShader *shader,
+                                                int         *n_uniforms);
+
+G_END_DECLS
+
+#endif /* __GSK_GLSHADER_PRIVATE_H__ */
diff --git a/gsk/gskrendernode.h b/gsk/gskrendernode.h
index 424039753a..dd655d3b35 100644
--- a/gsk/gskrendernode.h
+++ b/gsk/gskrendernode.h
@@ -25,6 +25,7 @@
 
 #include <gsk/gskroundedrect.h>
 #include <gsk/gsktypes.h>
+#include <gsk/gskglshader.h>
 #include <gtk/css/gtkcss.h>
 
 G_BEGIN_DECLS
@@ -122,6 +123,7 @@ GskRenderNode *         gsk_render_node_deserialize             (GBytes
 #define GSK_TYPE_CROSS_FADE_NODE                (gsk_cross_fade_node_get_type())
 #define GSK_TYPE_TEXT_NODE                      (gsk_text_node_get_type())
 #define GSK_TYPE_BLUR_NODE                      (gsk_blur_node_get_type())
+#define GSK_TYPE_GL_SHADER_NODE                 (gsk_gl_shader_node_get_type())
 
 typedef struct _GskDebugNode                    GskDebugNode;
 typedef struct _GskColorNode                    GskColorNode;
@@ -146,6 +148,7 @@ typedef struct _GskBlendNode                    GskBlendNode;
 typedef struct _GskCrossFadeNode                GskCrossFadeNode;
 typedef struct _GskTextNode                     GskTextNode;
 typedef struct _GskBlurNode                     GskBlurNode;
+typedef struct _GskGLShaderNode                 GskGLShaderNode;
 
 GDK_AVAILABLE_IN_ALL
 GType                   gsk_debug_node_get_type                 (void) G_GNUC_CONST;
@@ -451,6 +454,24 @@ GskRenderNode *         gsk_blur_node_get_child                 (GskRenderNode
 GDK_AVAILABLE_IN_ALL
 float                   gsk_blur_node_get_radius                (GskRenderNode            *node);
 
+GDK_AVAILABLE_IN_ALL
+GType                   gsk_gl_shader_node_get_type              (void) G_GNUC_CONST;
+GDK_AVAILABLE_IN_ALL
+GskRenderNode *         gsk_gl_shader_node_new                  (GskGLShader              *shader,
+                                                                 const graphene_rect_t    *bounds,
+                                                                 GBytes                   *args,
+                                                                 GskRenderNode           **children,
+                                                                 guint                     n_children);
+GDK_AVAILABLE_IN_ALL
+guint                   gsk_gl_shader_node_get_n_children       (GskRenderNode            *node);
+GDK_AVAILABLE_IN_ALL
+GskRenderNode *         gsk_gl_shader_node_get_child            (GskRenderNode            *node,
+                                                                 guint                     idx);
+GDK_AVAILABLE_IN_ALL
+GBytes *                gsk_gl_shader_node_get_args             (GskRenderNode            *node);
+GDK_AVAILABLE_IN_ALL
+GskGLShader *           gsk_gl_shader_node_get_shader           (GskRenderNode            *node);
+
 G_END_DECLS
 
 #endif /* __GSK_RENDER_NODE_H__ */
diff --git a/gsk/gskrendernodeimpl.c b/gsk/gskrendernodeimpl.c
index 76d2d774bb..1a54bd972c 100644
--- a/gsk/gskrendernodeimpl.c
+++ b/gsk/gskrendernodeimpl.c
@@ -4470,6 +4470,209 @@ gsk_debug_node_get_message (GskRenderNode *node)
   return self->message;
 }
 
+/*** GSK_GL_SHADER_NODE ***/
+
+struct _GskGLShaderNode
+{
+  GskRenderNode render_node;
+
+  GskGLShader *shader;
+  GBytes *args;
+  GskRenderNode **children;
+  guint n_children;
+};
+
+static void
+gsk_gl_shader_node_finalize (GskRenderNode *node)
+{
+  GskGLShaderNode *self = (GskGLShaderNode *) node;
+  GskRenderNodeClass *parent_class = g_type_class_peek (g_type_parent (GSK_TYPE_GL_SHADER_NODE));
+
+  for (guint i = 0; i < self->n_children; i++)
+    gsk_render_node_unref (self->children[i]);
+  g_free (self->children);
+
+  g_bytes_unref (self->args);
+
+  g_object_unref (self->shader);
+
+  parent_class->finalize (node);
+}
+
+static void
+gsk_gl_shader_node_draw (GskRenderNode *node,
+                         cairo_t       *cr)
+{
+  cairo_set_source_rgb (cr, 255 / 255., 105 / 255., 180 / 255.);
+  gsk_cairo_rectangle (cr, &node->bounds);
+  cairo_fill (cr);
+}
+
+static void
+gsk_gl_shader_node_diff (GskRenderNode  *node1,
+                         GskRenderNode  *node2,
+                         cairo_region_t *region)
+{
+  GskGLShaderNode *self1 = (GskGLShaderNode *) node1;
+  GskGLShaderNode *self2 = (GskGLShaderNode *) node2;
+
+  if (graphene_rect_equal (&node1->bounds, &node2->bounds) &&
+      self1->shader == self2->shader &&
+      g_bytes_compare (self1->args, self2->args) == 0 &&
+      self1->n_children == self2->n_children)
+    {
+      cairo_region_t *child_region = cairo_region_create();
+      for (guint i = 0; i < self1->n_children; i++)
+        gsk_render_node_diff (self1->children[i], self2->children[i], child_region);
+      if (!cairo_region_is_empty (child_region))
+        gsk_render_node_diff_impossible (node1, node2, region);
+      cairo_region_destroy (child_region);
+    }
+  else
+    {
+      gsk_render_node_diff_impossible (node1, node2, region);
+    }
+}
+
+/**
+ * gsk_gl_shader_node_new:
+ * @shader: the #GskGLShader
+ * @bounds: the rectangle to render the shader into
+ * @args: Arguments for the uniforms
+ * @children: (array length=n_children): array of child nodes, these will
+ *     be rendered to textures and used as input.
+ * @n_children: Length of @children (currenly the GL backend supports
+ *     up to 4 children)
+ *
+ * Creates a #GskRenderNode that will render the given @shader into the
+ * area given by @bounds. The @args is a block of data to use for uniform
+ * input, as per types and offsets defined by the @shader. Normally this
+ * is generated by gsk_gl_shader_format_args() or #GskGLShaderArgBuilder.
+ *
+ * See #GskGLShader for details about how the shader should be written.
+ *
+ * All the children will be rendered into textures (if they aren't already
+ * #GskTextureNodes, which will be used directly). These textures will be
+ * sent as input to the shader.
+ *
+ * If the renderer doesn't support GL shaders, or if there is any problem
+ * when compiling the shader, then the node will draw pink. You should use
+ * gsk_gl_shader_compile() to ensure the @shader will work for the
+ * renderer before using it.
+ *
+ * Returns: (transfer full) (type GskGLShaderNode): A new #GskRenderNode
+ */
+GskRenderNode *
+gsk_gl_shader_node_new (GskGLShader           *shader,
+                        const graphene_rect_t *bounds,
+                        GBytes                *args,
+                        GskRenderNode        **children,
+                        guint                  n_children)
+{
+  GskGLShaderNode *self;
+  GskRenderNode *node;
+
+  g_return_val_if_fail (GSK_IS_GL_SHADER (shader), NULL);
+  g_return_val_if_fail (bounds != NULL, NULL);
+  g_return_val_if_fail ((args == NULL && gsk_gl_shader_get_n_uniforms (shader) == 0) ||
+                        (args != NULL && g_bytes_get_size (args) == gsk_gl_shader_get_args_size (shader)), 
NULL);
+  g_return_val_if_fail ((children == NULL && n_children == 0) ||
+                        (children != NULL && n_children == gsk_gl_shader_get_n_textures (shader)), NULL);
+
+  self = gsk_render_node_alloc (GSK_GL_SHADER_NODE);
+  node = (GskRenderNode *) self;
+
+  graphene_rect_init_from_rect (&node->bounds, bounds);
+  self->shader = g_object_ref (shader);
+
+  self->args = g_bytes_ref (args);
+
+  self->n_children = n_children;
+  if (n_children > 0)
+    {
+      self->children = g_malloc_n (n_children, sizeof (GskRenderNode *));
+      for (guint i = 0; i < n_children; i++)
+        self->children[i] = gsk_render_node_ref (children[i]);
+    }
+
+  return node;
+}
+
+/**
+ * gsk_gl_shader_node_get_n_children:
+ * @node: (type GskGLShaderNode): a #GskRenderNode for a gl shader
+ *
+ * Returns the number of children
+ *
+ * Returns: The number of children
+ */
+guint
+gsk_gl_shader_node_get_n_children (GskRenderNode *node)
+{
+  GskGLShaderNode *self = (GskGLShaderNode *) node;
+
+  g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_GL_SHADER_NODE), 0);
+
+  return self->n_children;
+}
+
+/**
+ * gsk_gl_shader_node_get_child:
+ * @node: (type GskGLShaderNode): a #GskRenderNode for a gl shader
+ * @idx: the position of the child to get
+ *
+ * Gets one of the children.
+ *
+ * Returns: (transfer none): the @idx'th child of @node
+ */
+GskRenderNode *
+gsk_gl_shader_node_get_child (GskRenderNode *node,
+                              guint          idx)
+{
+  GskGLShaderNode *self = (GskGLShaderNode *) node;
+
+  g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_GL_SHADER_NODE), NULL);
+  g_return_val_if_fail (idx < self->n_children, NULL);
+
+  return self->children[idx];
+}
+
+/**
+ * gsk_gl_shader_node_get_shader:
+ * @node: (type GskGLShaderNode): a #GskRenderNode for a gl shader
+ *
+ * Gets shader code for the node.
+ *
+ * Returns: (transfer none): the #GskGLShader shader
+ */
+GskGLShader *
+gsk_gl_shader_node_get_shader (GskRenderNode *node)
+{
+  GskGLShaderNode *self = (GskGLShaderNode *) node;
+
+  g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_GL_SHADER_NODE), 0);
+
+  return self->shader;
+}
+
+/**
+ * gsk_gl_shader_node_get_args:
+ * @node: (type GskGLShaderNode): a #GskRenderNode for a gl shader
+ *
+ * Gets args for the node.
+ *
+ * Returns: (transfer none): A #GBytes with the uniform arguments
+ */
+GBytes *
+gsk_gl_shader_node_get_args (GskRenderNode *node)
+{
+  GskGLShaderNode *self = (GskGLShaderNode *) node;
+
+  g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_GL_SHADER_NODE), NULL);
+
+  return self->args;
+}
+
 GType gsk_render_node_types[GSK_RENDER_NODE_TYPE_N_TYPES];
 
 #ifndef I_
@@ -4506,6 +4709,7 @@ GSK_DEFINE_RENDER_NODE_TYPE (gsk_blend_node, GSK_BLEND_NODE)
 GSK_DEFINE_RENDER_NODE_TYPE (gsk_cross_fade_node, GSK_CROSS_FADE_NODE)
 GSK_DEFINE_RENDER_NODE_TYPE (gsk_text_node, GSK_TEXT_NODE)
 GSK_DEFINE_RENDER_NODE_TYPE (gsk_blur_node, GSK_BLUR_NODE)
+GSK_DEFINE_RENDER_NODE_TYPE (gsk_gl_shader_node, GSK_GL_SHADER_NODE)
 GSK_DEFINE_RENDER_NODE_TYPE (gsk_debug_node, GSK_DEBUG_NODE)
 
 static void
@@ -4863,6 +5067,22 @@ gsk_render_node_init_types_once (void)
     gsk_render_node_types[GSK_BLUR_NODE] = node_type;
   }
 
+  {
+    const GskRenderNodeTypeInfo node_info =
+    {
+      GSK_GL_SHADER_NODE,
+      sizeof (GskGLShaderNode),
+      NULL,
+      gsk_gl_shader_node_finalize,
+      gsk_gl_shader_node_draw,
+      NULL,
+      gsk_gl_shader_node_diff,
+    };
+
+    GType node_type = gsk_render_node_type_register_static (I_("GskGLShaderNode"), &node_info);
+    gsk_render_node_types[GSK_GL_SHADER_NODE] = node_type;
+  }
+
   {
     const GskRenderNodeTypeInfo node_info =
     {
diff --git a/gsk/gskrendernodeparser.c b/gsk/gskrendernodeparser.c
index 870ce8818b..667a68f8a9 100644
--- a/gsk/gskrendernodeparser.c
+++ b/gsk/gskrendernodeparser.c
@@ -824,9 +824,9 @@ clear_node (gpointer inout_node)
 static GskRenderNode *
 parse_container_node (GtkCssParser *parser)
 {
-  GskRenderNode *node;
   GPtrArray *nodes;
   const GtkCssToken *token;
+  GskRenderNode *node;
 
   nodes = g_ptr_array_new_with_free_func ((GDestroyNotify) gsk_render_node_unref);
 
@@ -1089,6 +1089,209 @@ parse_inset_shadow_node (GtkCssParser *parser)
   return gsk_inset_shadow_node_new (&outline, &color, dx, dy, spread, blur);
 }
 
+typedef union {
+    gint32 i;
+    double v[4];
+} UniformValue;
+
+typedef struct {
+  GskGLShader *shader;
+  GskShaderArgsBuilder *args;
+} ShaderInfo;
+
+static void
+clear_shader_info (gpointer data)
+{
+  ShaderInfo *info = data;
+  g_clear_object (&info->shader);
+  g_clear_pointer (&info->args, gsk_shader_args_builder_unref);
+}
+
+static gboolean
+parse_shader (GtkCssParser *parser,
+              gpointer      out_shader_info)
+{
+  ShaderInfo *shader_info = out_shader_info;
+  char *sourcecode = NULL;
+  GBytes *bytes;
+  GskGLShader *shader;
+
+  if (!parse_string (parser, &sourcecode))
+    return FALSE;
+
+  bytes = g_bytes_new_take (sourcecode, strlen (sourcecode));
+  shader = gsk_gl_shader_new_from_bytes (bytes);
+  g_bytes_unref (bytes);
+
+  shader_info->shader = shader;
+
+  return TRUE;
+}
+
+static gboolean
+parse_uniform_value (GtkCssParser *parser,
+                     int           idx,
+                     ShaderInfo   *shader_info)
+{
+  switch (gsk_gl_shader_get_uniform_type (shader_info->shader, idx))
+    {
+    case GSK_GL_UNIFORM_TYPE_FLOAT:
+      {
+        double f;
+        if (!gtk_css_parser_consume_number (parser, &f))
+          return FALSE;
+        gsk_shader_args_builder_set_float (shader_info->args, idx, f);
+      }
+      break;
+
+    case GSK_GL_UNIFORM_TYPE_INT:
+      {
+        int i;
+        if (!gtk_css_parser_consume_integer (parser, &i))
+          return FALSE;
+        gsk_shader_args_builder_set_int (shader_info->args, idx, i);
+      }
+      break;
+
+    case GSK_GL_UNIFORM_TYPE_UINT:
+      {
+        int i;
+        if (!gtk_css_parser_consume_integer (parser, &i) || i < 0)
+          return FALSE;
+        gsk_shader_args_builder_set_uint (shader_info->args, idx, i);
+      }
+      break;
+
+    case GSK_GL_UNIFORM_TYPE_BOOL:
+      {
+        int i;
+        if (!gtk_css_parser_consume_integer (parser, &i) || (i != 0 && i != 1))
+          return FALSE;
+        gsk_shader_args_builder_set_bool (shader_info->args, idx, i);
+      }
+      break;
+
+    case GSK_GL_UNIFORM_TYPE_VEC2:
+      {
+        double f0, f1;
+        graphene_vec2_t v;
+        if (!gtk_css_parser_consume_number (parser, &f0) ||
+            !gtk_css_parser_consume_number (parser, &f1))
+          return FALSE;
+        graphene_vec2_init (&v, f0, f1);
+        gsk_shader_args_builder_set_vec2 (shader_info->args, idx, &v);
+      }
+      break;
+
+    case GSK_GL_UNIFORM_TYPE_VEC3:
+      {
+        double f0, f1, f2;
+        graphene_vec3_t v;
+        if (!gtk_css_parser_consume_number (parser, &f0) ||
+            !gtk_css_parser_consume_number (parser, &f1) ||
+            !gtk_css_parser_consume_number (parser, &f2))
+          return FALSE;
+        graphene_vec3_init (&v, f0, f1, f2);
+        gsk_shader_args_builder_set_vec3 (shader_info->args, idx, &v);
+      }
+      break;
+
+    case GSK_GL_UNIFORM_TYPE_VEC4:
+      {
+        double f0, f1, f2, f3;
+        graphene_vec4_t v;
+        if (!gtk_css_parser_consume_number (parser, &f0) ||
+            !gtk_css_parser_consume_number (parser, &f1) ||
+            !gtk_css_parser_consume_number (parser, &f2) ||
+            !gtk_css_parser_consume_number (parser, &f3))
+          return FALSE;
+        graphene_vec4_init (&v, f0, f1, f2, f3);
+        gsk_shader_args_builder_set_vec4 (shader_info->args, idx, &v);
+      }
+      break;
+
+    case GSK_GL_UNIFORM_TYPE_NONE:
+    default:
+      g_assert_not_reached ();
+      break;
+    }
+
+  if (idx < gsk_gl_shader_get_n_uniforms (shader_info->shader))
+    {
+      if (!gtk_css_parser_try_token (parser, GTK_CSS_TOKEN_COMMA))
+        return FALSE;
+    }
+
+  return TRUE;
+}
+
+static gboolean
+parse_shader_args (GtkCssParser *parser, gpointer data)
+{
+  ShaderInfo *shader_info = data;
+  int n_uniforms;
+  int i;
+
+  shader_info->args = gsk_shader_args_builder_new (shader_info->shader, NULL);
+  n_uniforms = gsk_gl_shader_get_n_uniforms (shader_info->shader);
+
+  for (i = 0; i < n_uniforms; i++)
+    {
+      if (!parse_uniform_value (parser, i, shader_info))
+        return FALSE;
+    }
+
+  return TRUE;
+}
+
+static GskRenderNode *
+parse_glshader_node (GtkCssParser *parser)
+{
+  graphene_rect_t bounds = GRAPHENE_RECT_INIT (0, 0, 50, 50);
+  GskRenderNode *child[4] = { NULL, };
+  ShaderInfo shader_info = {
+    NULL,
+    NULL,
+  };
+  const Declaration declarations[] = {
+    { "bounds", parse_rect, NULL, &bounds },
+    { "sourcecode", parse_shader, NULL, &shader_info },
+    { "args", parse_shader_args, clear_shader_info, &shader_info },
+    { "child1", parse_node, clear_node, &child[0] },
+    { "child2", parse_node, clear_node, &child[1] },
+    { "child3", parse_node, clear_node, &child[2] },
+    { "child4", parse_node, clear_node, &child[3] },
+  };
+  GskGLShader *shader;
+  GskRenderNode *node;
+  GBytes *args = NULL;
+  int len, i;
+
+  parse_declarations (parser, declarations, G_N_ELEMENTS(declarations));
+
+  for (len = 0; len < 4; len++)
+    {
+      if (child[len] == NULL)
+        break;
+    }
+
+  shader = shader_info.shader;
+  args = gsk_shader_args_builder_free_to_args (shader_info.args);
+
+  node = gsk_gl_shader_node_new (shader, &bounds, args, child, len);
+
+  g_bytes_unref (args);
+  g_object_unref (shader);
+
+  for (i = 0; i < 4; i++)
+    {
+      if (child[i])
+        gsk_render_node_unref (child[i]);
+    }
+
+  return node;
+}
+
 static GskRenderNode *
 parse_border_node (GtkCssParser *parser)
 {
@@ -1603,6 +1806,7 @@ parse_node (GtkCssParser *parser,
     { "text", parse_text_node },
     { "texture", parse_texture_node },
     { "transform", parse_transform_node },
+    { "glshader", parse_glshader_node },
   };
   GskRenderNode **node_p = out_node;
   guint i;
@@ -1914,6 +2118,18 @@ append_point_param (Printer                *p,
   g_string_append_c (p->str, '\n');
 }
 
+static void
+append_string_param (Printer    *p,
+                     const char *param_name,
+                     const char *value)
+{
+  _indent (p);
+  g_string_append_printf (p->str, "%s: ", param_name);
+  _gtk_css_print_string (p->str, value);
+  g_string_append_c (p->str, ';');
+  g_string_append_c (p->str, '\n');
+}
+
 static void
 append_vec4_param (Printer               *p,
                    const char            *param_name,
@@ -2441,6 +2657,124 @@ render_node_print (Printer       *p,
       }
       break;
 
+    case GSK_GL_SHADER_NODE:
+      {
+        GskGLShader *shader = gsk_gl_shader_node_get_shader (node);
+        GBytes *args = gsk_gl_shader_node_get_args (node);
+
+        start_node (p, "glshader");
+
+        append_rect_param (p, "bounds", &node->bounds);
+
+        GBytes *bytes = gsk_gl_shader_get_source (shader);
+        /* Ensure we are zero-terminated */
+        char *sourcecode = g_strndup (g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes));
+        append_string_param (p, "sourcecode", sourcecode);
+        g_free (sourcecode);
+
+        if (gsk_gl_shader_get_n_uniforms (shader) > 0)
+          {
+            GString *data = g_string_new ("");
+
+            for (guint i = 0; i < gsk_gl_shader_get_n_uniforms (shader); i++)
+              {
+                if (i > 0)
+                  g_string_append (data, ", ");
+
+                switch (gsk_gl_shader_get_uniform_type (shader, i))
+                  {
+                  case GSK_GL_UNIFORM_TYPE_NONE:
+                  default:
+                    g_assert_not_reached ();
+                    break;
+
+                  case GSK_GL_UNIFORM_TYPE_FLOAT:
+                    {
+                      float value = gsk_gl_shader_get_arg_float (shader, args, i);
+                      string_append_double (data, value);
+                    }
+                    break;
+
+                  case GSK_GL_UNIFORM_TYPE_INT:
+                    {
+                      gint32 value = gsk_gl_shader_get_arg_int (shader, args, i);
+                      g_string_append_printf (data, "%d", value);
+                    }
+                    break;
+
+                  case GSK_GL_UNIFORM_TYPE_UINT:
+                    {
+                      guint32 value = gsk_gl_shader_get_arg_uint (shader, args, i);
+                      g_string_append_printf (data, "%u", value);
+                    }
+                    break;
+
+                  case GSK_GL_UNIFORM_TYPE_BOOL:
+                    {
+                      gboolean value = gsk_gl_shader_get_arg_bool (shader, args, i);
+                      g_string_append_printf (data, "%d", value);
+                    }
+                    break;
+
+                  case GSK_GL_UNIFORM_TYPE_VEC2:
+                    {
+                      graphene_vec2_t value;
+                      gsk_gl_shader_get_arg_vec2 (shader, args, i,
+                                                  &value);
+                      string_append_double (data, graphene_vec2_get_x (&value));
+                      g_string_append (data, " ");
+                      string_append_double (data, graphene_vec2_get_y (&value));
+                    }
+                    break;
+
+                  case GSK_GL_UNIFORM_TYPE_VEC3:
+                    {
+                      graphene_vec3_t value;
+                      gsk_gl_shader_get_arg_vec3 (shader, args, i,
+                                                  &value);
+                      string_append_double (data, graphene_vec3_get_x (&value));
+                      g_string_append (data, " ");
+                      string_append_double (data, graphene_vec3_get_y (&value));
+                      g_string_append (data, " ");
+                      string_append_double (data, graphene_vec3_get_z (&value));
+                    }
+                    break;
+
+                  case GSK_GL_UNIFORM_TYPE_VEC4:
+                    {
+                      graphene_vec4_t value;
+                      gsk_gl_shader_get_arg_vec4 (shader, args, i,
+                                                  &value);
+                      string_append_double (data, graphene_vec4_get_x (&value));
+                      g_string_append (data, " ");
+                      string_append_double (data, graphene_vec4_get_y (&value));
+                      g_string_append (data, " ");
+                      string_append_double (data, graphene_vec4_get_z (&value));
+                      g_string_append (data, " ");
+                      string_append_double (data, graphene_vec4_get_w (&value));
+                    }
+                    break;
+                  }
+              }
+            _indent (p);
+            g_string_append_printf (p->str, "args: %s;\n", data->str);
+            g_string_free (data, TRUE);
+          }
+
+        for (guint i = 0; i < gsk_gl_shader_node_get_n_children (node); i ++)
+          {
+            GskRenderNode *child = gsk_gl_shader_node_get_child (node, i);
+            char *name;
+
+            name = g_strdup_printf ("child%d", i + 1);
+            append_node_param (p, name, child);
+            g_free (name);
+          }
+
+        end_node (p);
+      }
+      break;
+
     case GSK_REPEAT_NODE:
       {
         GskRenderNode *child = gsk_repeat_node_get_child (node);
diff --git a/gsk/gskrendernodeprivate.h b/gsk/gskrendernodeprivate.h
index d75a2c68b1..7e11a14b8f 100644
--- a/gsk/gskrendernodeprivate.h
+++ b/gsk/gskrendernodeprivate.h
@@ -13,7 +13,7 @@ typedef struct _GskRenderNodeClass GskRenderNodeClass;
  * 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_RENDER_NODE_TYPE_N_TYPES    (GSK_GL_SHADER_NODE + 1)
 
 extern GType gsk_render_node_types[];
 
diff --git a/gsk/meson.build b/gsk/meson.build
index c3567d78ed..b52d379eb9 100644
--- a/gsk/meson.build
+++ b/gsk/meson.build
@@ -21,6 +21,7 @@ gsk_private_gl_shaders = [
 gsk_public_sources = files([
   'gskdiff.c',
   'gskcairorenderer.c',
+  'gskglshader.c',
   'gskrenderer.c',
   'gskrendernode.c',
   'gskrendernodeimpl.c',
@@ -52,6 +53,7 @@ gsk_private_sources = files([
 gsk_public_headers = files([
   'gskcairorenderer.h',
   'gskenums.h',
+  'gskglshader.h',
   'gskrenderer.h',
   'gskrendernode.h',
   'gskroundedrect.h',
diff --git a/gtk/inspector/recorder.c b/gtk/inspector/recorder.c
index 8b42de031e..135b0b2def 100644
--- a/gtk/inspector/recorder.c
+++ b/gtk/inspector/recorder.c
@@ -171,6 +171,25 @@ create_list_model_for_render_node (GskRenderNode *node)
       return create_render_node_list_model ((GskRenderNode *[2]) { gsk_cross_fade_node_get_start_child 
(node),
                                                                    gsk_cross_fade_node_get_end_child (node) 
}, 2);
 
+    case GSK_GL_SHADER_NODE:
+      {
+        GListStore *store = g_list_store_new (GDK_TYPE_PAINTABLE);
+
+        for (guint i = 0; i < gsk_gl_shader_node_get_n_children (node); i++)
+          {
+            GskRenderNode *child = gsk_gl_shader_node_get_child (node, i);
+            graphene_rect_t bounds;
+            GdkPaintable *paintable;
+
+            gsk_render_node_get_bounds (child, &bounds);
+            paintable = gtk_render_node_paintable_new (child, &bounds);
+            g_list_store_append (store, paintable);
+            g_object_unref (paintable);
+          }
+
+        return G_LIST_MODEL (store);
+      }
+
     case GSK_CONTAINER_NODE:
       {
         GListStore *store;
@@ -270,6 +289,8 @@ node_type_name (GskRenderNodeType type)
       return "Text";
     case GSK_BLUR_NODE:
       return "Blur";
+    case GSK_GL_SHADER_NODE:
+      return "GL Shader";
     }
 }
 
@@ -301,6 +322,7 @@ node_name (GskRenderNode *node)
     case GSK_CROSS_FADE_NODE:
     case GSK_TEXT_NODE:
     case GSK_BLUR_NODE:
+    case GSK_GL_SHADER_NODE:
       return g_strdup (node_type_name (gsk_render_node_get_node_type (node)));
 
     case GSK_DEBUG_NODE:
@@ -511,6 +533,34 @@ add_color_row (GtkListStore  *store,
   g_object_unref (texture);
 }
 
+static void
+add_int_row (GtkListStore  *store,
+             const char    *name,
+             int            value)
+{
+  char *text = g_strdup_printf ("%d", value);
+  add_text_row (store, name, text);
+  g_free (text);
+}
+
+static void
+add_uint_row (GtkListStore  *store,
+              const char    *name,
+              guint          value)
+{
+  char *text = g_strdup_printf ("%u", value);
+  add_text_row (store, name, text);
+  g_free (text);
+}
+
+static void
+add_boolean_row (GtkListStore  *store,
+                 const char    *name,
+                 gboolean       value)
+{
+  add_text_row (store, name, value ? "TRUE" : "FALSE");
+}
+
 static void
 add_float_row (GtkListStore  *store,
                const char    *name,
@@ -759,6 +809,92 @@ populate_render_node_properties (GtkListStore  *store,
       add_float_row (store, "Radius", gsk_blur_node_get_radius (node));
       break;
 
+    case GSK_GL_SHADER_NODE:
+      {
+        GskGLShader *shader = gsk_gl_shader_node_get_shader (node);
+        GBytes *args = gsk_gl_shader_node_get_args (node);
+        int i;
+
+        add_int_row (store, "Required textures", gsk_gl_shader_get_n_textures (shader));
+        for (i = 0; i < gsk_gl_shader_get_n_uniforms (shader); i++)
+          {
+            const char *name;
+            char *title;
+
+            name = gsk_gl_shader_get_uniform_name (shader, i);
+            title = g_strdup_printf ("Uniform %s", name);
+
+            switch (gsk_gl_shader_get_uniform_type (shader, i))
+              {
+              case GSK_GL_UNIFORM_TYPE_NONE:
+              default:
+                g_assert_not_reached ();
+                break;
+
+              case GSK_GL_UNIFORM_TYPE_FLOAT:
+                add_float_row (store, title,
+                               gsk_gl_shader_get_arg_float (shader, args, i));
+                break;
+
+              case GSK_GL_UNIFORM_TYPE_INT:
+                add_int_row (store, title,
+                             gsk_gl_shader_get_arg_int (shader, args, i));
+                break;
+
+              case GSK_GL_UNIFORM_TYPE_UINT:
+                add_uint_row (store, title,
+                              gsk_gl_shader_get_arg_uint (shader, args, i));
+                break;
+
+              case GSK_GL_UNIFORM_TYPE_BOOL:
+                add_boolean_row (store, title,
+                                 gsk_gl_shader_get_arg_bool (shader, args, i));
+                break;
+
+              case GSK_GL_UNIFORM_TYPE_VEC2:
+                {
+                  graphene_vec2_t v;
+                  gsk_gl_shader_get_arg_vec2 (shader, args, i, &v);
+                  float x = graphene_vec2_get_x (&v);
+                  float y = graphene_vec2_get_x (&v);
+                  char *s = g_strdup_printf ("%.2f %.2f", x, y);
+                  add_text_row (store, title, s);
+                  g_free (s);
+                }
+                break;
+
+              case GSK_GL_UNIFORM_TYPE_VEC3:
+                {
+                  graphene_vec3_t v;
+                  gsk_gl_shader_get_arg_vec3 (shader, args, i, &v);
+                  float x = graphene_vec3_get_x (&v);
+                  float y = graphene_vec3_get_y (&v);
+                  float z = graphene_vec3_get_z (&v);
+                  char *s = g_strdup_printf ("%.2f %.2f %.2f", x, y, z);
+                  add_text_row (store, title, s);
+                  g_free (s);
+                }
+                break;
+
+              case GSK_GL_UNIFORM_TYPE_VEC4:
+                {
+                  graphene_vec4_t v;
+                  gsk_gl_shader_get_arg_vec4 (shader, args, i, &v);
+                  float x = graphene_vec4_get_x (&v);
+                  float y = graphene_vec4_get_y (&v);
+                  float z = graphene_vec4_get_z (&v);
+                  float w = graphene_vec4_get_w (&v);
+                  char *s = g_strdup_printf ("%.2f %.2f %.2f %.2f", x, y, z, w);
+                  add_text_row (store, title, s);
+                  g_free (s);
+                }
+                break;
+              }
+            g_free (title);
+          }
+      }
+      break;
+
     case GSK_INSET_SHADOW_NODE:
       {
         const GdkRGBA *color = gsk_inset_shadow_node_peek_color (node);


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