[gtk/matthiasc/gltransition-demo: 5/10] Add GskGLShaderNode




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

    Add GskGLShaderNode
    
    This is a rendernode that is supposed to run a GLSL fragment
    shader with a set of inputs and produce outputs.
    The inputs are:
     * A GskGLShader object with the source and definitions of the uniforms
     * A the data for the unifors, formated according to the GskGLShader
     * a list of render nodes that are rendered to textures
    
    Additionally there is a fallback node which is used in case
    OpenGL is not supported or there is some kind of failure
    with the shader code.

 gsk/gskenums.h             |  28 +++-
 gsk/gskglshader.c          | 356 +++++++++++++++++++++++++++++++++++++++++++++
 gsk/gskglshader.h          |  70 +++++++++
 gsk/gskrendernode.h        |  25 ++++
 gsk/gskrendernodeimpl.c    | 246 +++++++++++++++++++++++++++++++
 gsk/gskrendernodeparser.c  |  53 +++++++
 gsk/gskrendernodeprivate.h |   2 +-
 gsk/meson.build            |   2 +
 gtk/inspector/recorder.c   |  25 ++++
 9 files changed, 805 insertions(+), 2 deletions(-)
---
diff --git a/gsk/gskenums.h b/gsk/gskenums.h
index 24aafec502..4c0cc72113 100644
--- a/gsk/gskenums.h
+++ b/gsk/gskenums.h
@@ -75,7 +75,8 @@ typedef enum {
   GSK_CROSS_FADE_NODE,
   GSK_TEXT_NODE,
   GSK_BLUR_NODE,
-  GSK_DEBUG_NODE
+  GSK_DEBUG_NODE,
+  GSK_GLSHADER_NODE
 } GskRenderNodeType;
 
 /**
@@ -218,4 +219,29 @@ typedef enum
   GSK_TRANSFORM_CATEGORY_IDENTITY
 } GskTransformCategory;
 
+/**
+ * GskGLUniformType:
+ * @GSK_GLUNIFORM_TYPE_NONE: No type, used for uninitialized or unspecified values.
+ * @GSK_GLUNIFORM_TYPE_FLOAT: A float uniform
+ * @GSK_GLUNIFORM_TYPE_INT: A GLSL int / C gint32 uniform
+ * @GSK_GLUNIFORM_TYPE_UINT: A GLSL uint / C gunt32 uniform
+ * @GSK_GLUNIFORM_TYPE_BOOL: A GLSL bool / C gboolean uniform
+ * @GSK_GLUNIFORM_TYPE_VEC2: A GLSL vec2 / C graphene_vec2_t uniform
+ * @GSK_GLUNIFORM_TYPE_VEC3: A GLSL vec3 / C graphene_vec3_t uniform
+ * @GSK_GLUNIFORM_TYPE_VEC4: A GLSL vec4/ C graphene_vec4_t uniform
+ *
+ */
+typedef enum
+{
+  GSK_GLUNIFORM_TYPE_NONE,
+  GSK_GLUNIFORM_TYPE_FLOAT,
+  GSK_GLUNIFORM_TYPE_INT,
+  GSK_GLUNIFORM_TYPE_UINT,
+  GSK_GLUNIFORM_TYPE_BOOL,
+  GSK_GLUNIFORM_TYPE_VEC2,
+  GSK_GLUNIFORM_TYPE_VEC3,
+  GSK_GLUNIFORM_TYPE_VEC4,
+} GskGLUniformType;
+
+
 #endif /* __GSK_TYPES_H__ */
diff --git a/gsk/gskglshader.c b/gsk/gskglshader.c
new file mode 100644
index 0000000000..d009eedc20
--- /dev/null
+++ b/gsk/gskglshader.c
@@ -0,0 +1,356 @@
+/* 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: A description of GskGLShader
+ *
+ */
+
+#include "config.h"
+#include "gskglshader.h"
+#include "gskglshaderprivate.h"
+
+struct _GskGLShader
+{
+  GObject parent_instance;
+  char *sourcecode;
+  int min_sources;
+  int uniforms_size;
+  GArray *uniforms;
+};
+
+G_DEFINE_TYPE (GskGLShader, gsk_glshader, G_TYPE_OBJECT)
+
+enum {
+  GLSHADER_PROP_0,
+  GLSHADER_PROP_SOURCECODE,
+  GLSHADER_PROP_MIN_SOURCES,
+  GLSHADER_N_PROPS
+};
+static GParamSpec *gsk_glshader_properties[GLSHADER_N_PROPS];
+
+static void
+gsk_glshader_get_property (GObject    *object,
+                           guint       prop_id,
+                           GValue     *value,
+                           GParamSpec *pspec)
+{
+  GskGLShader *shader = GSK_GLSHADER (object);
+
+  switch (prop_id)
+    {
+    case GLSHADER_PROP_SOURCECODE:
+      g_value_set_string (value, shader->sourcecode);
+      break;
+
+    case GLSHADER_PROP_MIN_SOURCES:
+      g_value_set_int (value, shader->min_sources);
+      break;
+
+    default:
+      g_assert_not_reached ();
+    }
+}
+
+static void
+gsk_glshader_set_property (GObject      *object,
+                           guint         prop_id,
+                           const GValue *value,
+                           GParamSpec   *pspec)
+{
+  GskGLShader *shader = GSK_GLSHADER (object);
+
+  switch (prop_id)
+    {
+    case GLSHADER_PROP_SOURCECODE:
+      g_free (shader->sourcecode);
+      shader->sourcecode = g_value_dup_string (value);
+      break;
+
+    case GLSHADER_PROP_MIN_SOURCES:
+      gsk_glshader_set_min_sources (shader, g_value_get_int (value));
+      break;
+
+    default:
+      g_assert_not_reached ();
+    }
+}
+
+static void
+gsk_glshader_finalize (GObject *object)
+{
+  GskGLShader *shader = GSK_GLSHADER (object);
+
+  g_free (shader->sourcecode);
+  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_glshader_parent_class)->finalize (object);
+}
+
+static void
+gsk_glshader_class_init (GskGLShaderClass *klass)
+{
+   GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->get_property = gsk_glshader_get_property;
+  object_class->set_property = gsk_glshader_set_property;
+  object_class->finalize = gsk_glshader_finalize;
+
+  /**
+   * GskGLShader:sourcecode:
+   *
+   * The sourcecode code for the shader.
+   */
+  gsk_glshader_properties[GLSHADER_PROP_SOURCECODE] =
+    g_param_spec_string ("sourcecode",
+                         "Sourcecode",
+                         "The sourcecode code for the shader",
+                         NULL,
+                         G_PARAM_READWRITE |
+                         G_PARAM_CONSTRUCT_ONLY |
+                         G_PARAM_STATIC_STRINGS);
+
+  /**
+   * GskGLShader:min-sources:
+   *
+   * The sourcecode code for the shader.
+   */
+  gsk_glshader_properties[GLSHADER_PROP_MIN_SOURCES] =
+    g_param_spec_int ("min-sources",
+                      "Min sources",
+                      "Minimum required source textures for the shader",
+                      0, 4, 0,
+                      G_PARAM_READWRITE |
+                      G_PARAM_STATIC_STRINGS);
+
+  g_object_class_install_properties (object_class, GLSHADER_N_PROPS, gsk_glshader_properties);
+}
+
+static void
+gsk_glshader_init (GskGLShader *shader)
+{
+  shader->uniforms = g_array_new (FALSE, FALSE, sizeof (GskGLUniform));
+}
+
+GskGLShader *
+gsk_glshader_new (const char *sourcecode)
+{
+   GskGLShader *shader = g_object_new (GSK_TYPE_GLSHADER,
+                                       "sourcecode", sourcecode,
+                                       NULL);
+   return shader;
+}
+
+const char *
+gsk_glshader_peek_sourcecode (GskGLShader *shader)
+{
+  return shader->sourcecode;
+}
+
+int
+gsk_glshader_get_min_sources (GskGLShader  *shader)
+{
+  return shader->min_sources;
+}
+
+void
+gsk_glshader_set_min_sources (GskGLShader  *shader,
+                              int           min_sources)
+{
+  shader->min_sources = min_sources;
+}
+
+static int
+uniform_type_size (GskGLUniformType type)
+{
+  switch (type)
+    {
+    case GSK_GLUNIFORM_TYPE_FLOAT:
+      return sizeof (float);
+
+    case GSK_GLUNIFORM_TYPE_INT:
+      return sizeof (gint32);
+
+    case GSK_GLUNIFORM_TYPE_UINT:
+    case GSK_GLUNIFORM_TYPE_BOOL:
+      return sizeof (guint32);
+
+    case GSK_GLUNIFORM_TYPE_VEC2:
+      return sizeof (float) * 2;
+
+    case GSK_GLUNIFORM_TYPE_VEC3:
+      return sizeof (float) * 3;
+
+    case GSK_GLUNIFORM_TYPE_VEC4:
+      return sizeof (float) * 4;
+
+    case GSK_GLUNIFORM_TYPE_NONE:
+    default:
+      g_assert_not_reached ();
+      return 0;
+
+    }
+}
+
+int
+gsk_glshader_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);
+
+  return uniform.offset;
+}
+
+int
+gsk_glshader_get_n_uniforms (GskGLShader     *shader)
+{
+  return shader->uniforms->len;
+}
+
+const char *
+gsk_glshader_peek_uniform_name (GskGLShader     *shader,
+                                int              idx)
+{
+  return g_array_index (shader->uniforms, GskGLUniform, idx).name;
+}
+
+GskGLUniformType
+gsk_glshader_get_uniform_type (GskGLShader     *shader,
+                                  int              idx)
+{
+  return g_array_index (shader->uniforms, GskGLUniform, idx).type;
+}
+
+int
+gsk_glshader_get_uniform_offset (GskGLShader     *shader,
+                                 int              idx)
+{
+  return g_array_index (shader->uniforms, GskGLUniform, idx).offset;
+}
+
+const GskGLUniform *
+gsk_glshader_peek_uniforms (GskGLShader *shader,
+                            int         *n_uniforms)
+{
+  *n_uniforms = shader->uniforms->len;
+  return &g_array_index (shader->uniforms, GskGLUniform, 0);
+}
+
+int
+gsk_glshader_get_uniforms_size (GskGLShader *shader)
+{
+  return shader->uniforms_size;
+}
+
+static const GskGLUniform *
+gsk_glshader_find_uniform (GskGLShader *shader,
+                           const char *name)
+{
+  int i;
+  for (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;
+}
+
+guchar *
+gsk_glshader_format_uniform_data_va (GskGLShader *shader,
+                                     va_list uniforms)
+{
+  guchar *args = g_malloc0 (shader->uniforms_size);
+  const char *name;
+
+  while ((name = va_arg (uniforms, const char *)) != NULL)
+    {
+      const GskGLUniform *u;
+      gpointer value = va_arg (uniforms, gpointer);
+      guchar *args_dest;
+
+      u = gsk_glshader_find_uniform (shader, name);
+      if (u == NULL)
+        {
+          /* This isn't really an error, because we can easily imaging
+             a shader interface that have input which isn't needed for
+             a particular shader */
+          g_debug ("No uniform named `%s` in shader", name);
+          continue;
+        }
+
+      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_GLUNIFORM_TYPE_FLOAT:
+          *(float *)args_dest = *(float *)value;
+          break;
+
+        case GSK_GLUNIFORM_TYPE_INT:
+          *(gint32 *)args_dest = *(gint32 *)value;
+          break;
+
+        case GSK_GLUNIFORM_TYPE_UINT:
+          *(guint32 *)args_dest = *(guint32 *)value;
+          break;
+
+        case GSK_GLUNIFORM_TYPE_BOOL:
+          *(guint32 *)args_dest = *(gboolean *)value;
+          break;
+
+        case GSK_GLUNIFORM_TYPE_VEC2:
+          graphene_vec2_to_float ((const graphene_vec2_t *)value,
+                                  (float *)args_dest);
+          break;
+
+        case GSK_GLUNIFORM_TYPE_VEC3:
+          graphene_vec3_to_float ((const graphene_vec3_t *)value,
+                                  (float *)args_dest);
+          break;
+
+        case GSK_GLUNIFORM_TYPE_VEC4:
+          graphene_vec4_to_float ((const graphene_vec4_t *)value,
+                                  (float *)args_dest);
+          break;
+
+        case GSK_GLUNIFORM_TYPE_NONE:
+        default:
+          g_assert_not_reached ();
+        }
+    }
+
+  return args;
+}
diff --git a/gsk/gskglshader.h b/gsk/gskglshader.h
new file mode 100644
index 0000000000..75ed3635f6
--- /dev/null
+++ b/gsk/gskglshader.h
@@ -0,0 +1,70 @@
+/* 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_GLSHADER_H__
+#define __GSK_GLSHADER_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_GLSHADER (gsk_glshader_get_type ())
+
+GDK_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (GskGLShader, gsk_glshader, GSK, GLSHADER, GObject)
+
+GDK_AVAILABLE_IN_ALL
+GskGLShader *    gsk_glshader_new                (const char       *sourcecode);
+GDK_AVAILABLE_IN_ALL
+const char *     gsk_glshader_peek_sourcecode    (GskGLShader      *shader);
+GDK_AVAILABLE_IN_ALL
+int              gsk_glshader_get_min_sources    (GskGLShader      *shader);
+GDK_AVAILABLE_IN_ALL
+void             gsk_glshader_set_min_sources    (GskGLShader      *shader,
+                                                  int               min_sources);
+GDK_AVAILABLE_IN_ALL
+int              gsk_glshader_add_uniform        (GskGLShader      *shader,
+                                                  const char       *name,
+                                                  GskGLUniformType  type);
+GDK_AVAILABLE_IN_ALL
+int              gsk_glshader_get_n_uniforms     (GskGLShader      *shader);
+GDK_AVAILABLE_IN_ALL
+const char *     gsk_glshader_peek_uniform_name  (GskGLShader      *shader,
+                                                  int               idx);
+GDK_AVAILABLE_IN_ALL
+GskGLUniformType gsk_glshader_get_uniform_type   (GskGLShader      *shader,
+                                                  int               idx);
+GDK_AVAILABLE_IN_ALL
+int              gsk_glshader_get_uniform_offset (GskGLShader      *shader,
+                                                  int               idx);
+GDK_AVAILABLE_IN_ALL
+int              gsk_glshader_get_uniforms_size  (GskGLShader      *shader);
+GDK_AVAILABLE_IN_ALL
+guchar *         gsk_glshader_format_uniform_data_va (GskGLShader  *shader,
+                                                      va_list       uniforms);
+
+G_END_DECLS
+
+#endif /* __GSK_GLSHADER_H__ */
diff --git a/gsk/gskrendernode.h b/gsk/gskrendernode.h
index 424039753a..a24070ab8e 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_GLSHADER_NODE                  (gsk_glshader_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,28 @@ 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_glshader_node_get_type              (void) G_GNUC_CONST;
+GDK_AVAILABLE_IN_ALL
+GskRenderNode *         gsk_glshader_node_new                   (GskGLShader              *shader,
+                                                                 const graphene_rect_t    *bounds,
+                                                                 guchar                   *uniform_data,
+                                                                 GskRenderNode            *fallback,
+                                                                 GskRenderNode           **children,
+                                                                 int                       n_children);
+GDK_AVAILABLE_IN_ALL
+GskRenderNode *         gsk_glshader_node_get_fallback_child    (GskRenderNode            *node);
+GDK_AVAILABLE_IN_ALL
+guint                   gsk_glshader_node_get_n_children        (GskRenderNode            *node);
+GDK_AVAILABLE_IN_ALL
+GskRenderNode *         gsk_glshader_node_get_child             (GskRenderNode            *node,
+                                                                 int                       idx);
+GDK_AVAILABLE_IN_ALL
+const guchar *          gsk_glshader_node_peek_uniform_data     (GskRenderNode            *node);
+GDK_AVAILABLE_IN_ALL
+GskGLShader *           gsk_glshader_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..1ad4e8bb98 100644
--- a/gsk/gskrendernodeimpl.c
+++ b/gsk/gskrendernodeimpl.c
@@ -4470,6 +4470,235 @@ gsk_debug_node_get_message (GskRenderNode *node)
   return self->message;
 }
 
+/*** GSK_GLSHADER_NODE ***/
+
+struct _GskGLShaderNode
+{
+  GskRenderNode render_node;
+
+  GskGLShader *shader;
+  union {
+    guchar inlined[8]; /* Most shaders have few args, so avoid extra alloc */
+    guchar *external;
+  } args;
+  GskRenderNode *fallback;
+  GskRenderNode **children;
+  guint n_children;
+};
+
+static void
+gsk_glshader_node_finalize (GskRenderNode *node)
+{
+  GskGLShaderNode *self = (GskGLShaderNode *) node;
+  GskRenderNodeClass *parent_class = g_type_class_peek (g_type_parent (GSK_TYPE_GLSHADER_NODE));
+  int uniforms_size;
+
+  for (guint i = 0; i < self->n_children; i++)
+    gsk_render_node_unref (self->children[i]);
+  g_free (self->children);
+
+  gsk_render_node_unref (self->fallback);
+
+  uniforms_size = gsk_glshader_get_uniforms_size (self->shader);
+  if (uniforms_size > sizeof (self->args.inlined))
+    g_free (self->args.external);
+
+  g_object_unref (self->shader);
+
+  parent_class->finalize (node);
+}
+
+static void
+gsk_glshader_node_draw (GskRenderNode *node,
+                        cairo_t       *cr)
+{
+  GskGLShaderNode *self = (GskGLShaderNode *) node;
+
+  gsk_render_node_draw (self->fallback, cr);
+}
+
+static void
+gsk_glshader_node_diff (GskRenderNode  *node1,
+                        GskRenderNode  *node2,
+                        cairo_region_t *region)
+{
+  GskGLShaderNode *self1 = (GskGLShaderNode *) node1;
+  GskGLShaderNode *self2 = (GskGLShaderNode *) node2;
+  int uniforms_size = gsk_glshader_get_uniforms_size (self1->shader);
+
+  if (graphene_rect_equal (&node1->bounds, &node2->bounds) &&
+      self1->shader == self2->shader &&
+      ((uniforms_size <= sizeof (self1->args.inlined)) ?
+       memcmp (&self1->args.inlined[0], &self2->args.inlined[0], uniforms_size) == 0:
+       memcmp (self1->args.external, self2->args.external, uniforms_size) == 0) &&
+      self1->n_children == self2->n_children)
+    {
+      gsk_render_node_diff (self1->fallback, self2->fallback, region);
+
+      for (guint i = 0; i < self1->n_children; i++)
+        {
+          if (self1->children[i] != self2->children[i])
+            {
+              gsk_render_node_diff_impossible (node1, node2, region);
+              break;
+            }
+        }
+    }
+  else
+    {
+      gsk_render_node_diff_impossible (node1, node2, region);
+    }
+}
+
+/**
+ * gsk_glshader_node_new:
+ * @shader: the shader glsl code
+ * @bounds: the rectangle to render the glshader into
+ * @uniform_data: Data for the uniforms
+ * @fallback: Render node to use if OpenGL is not supported
+ * @children: List of child nodes, these will be rendered to textures and used as input.
+ * @n_children: Length of @children (currenly the GL backend only supports max 4 children)
+ *
+ *
+ * Creates a #GskRenderNode that will render the given @gl_program into the area given by @bounds.
+ *
+ * Returns: (transfer full) (type GskGLShaderNode): A new #GskRenderNode
+ */
+GskRenderNode *
+gsk_glshader_node_new (GskGLShader           *shader,
+                       const graphene_rect_t *bounds,
+                       guchar                *uniform_data,
+                       GskRenderNode         *fallback,
+                       GskRenderNode        **children,
+                       int                    n_children)
+{
+  GskGLShaderNode *self;
+  GskRenderNode *node;
+  int uniforms_size;
+
+  g_return_val_if_fail (bounds != NULL, NULL);
+
+  self = gsk_render_node_alloc (GSK_GLSHADER_NODE);
+  node = (GskRenderNode *) self;
+
+  graphene_rect_init_from_rect (&node->bounds, bounds);
+  self->shader = g_object_ref (shader);
+
+  uniforms_size = gsk_glshader_get_uniforms_size (shader);
+  if (uniforms_size <= sizeof (self->args.inlined))
+    memcpy (self->args.inlined, uniform_data, uniforms_size);
+  else
+    self->args.external = g_memdup (uniform_data, uniforms_size);
+
+  self->fallback = gsk_render_node_ref (fallback);
+
+  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_glshader_node_get_fallback_child:
+ * @node: (type GskGLShaderNode): a #GskRenderNode for a gl shader
+ *
+ * Gets the fallback child node
+ *
+ * Returns: (transfer none): The fallback node
+ */
+GskRenderNode *
+gsk_glshader_node_get_fallback_child (GskRenderNode *node)
+{
+  GskGLShaderNode *self = (GskGLShaderNode *) node;
+
+  g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_GLSHADER_NODE), NULL);
+
+  return self->fallback;
+}
+
+/**
+ * gsk_glshader_node_get_n_children:
+ * @node: (type GskGLShaderNode): a #GskRenderNode for a gl shader
+ *
+ * Returns the number of (non-fallback) children
+ *
+ * Returns: The number of children
+ */
+guint
+gsk_glshader_node_get_n_children (GskRenderNode *node)
+{
+  GskGLShaderNode *self = (GskGLShaderNode *) node;
+
+  g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_GLSHADER_NODE), 0);
+
+  return self->n_children;
+}
+
+/**
+ * gsk_glshader_node_get_child:
+ * @node: (type GskGLShaderNode): a #GskRenderNode for a gl shader
+ * @idx: the position of the child to get
+ *
+ * Gets one of the (non-fallback) children.
+ *
+ * Returns: (transfer none): the @idx'th child of @node
+ */
+GskRenderNode *
+gsk_glshader_node_get_child (GskRenderNode *node,
+                             int            idx)
+{
+  GskGLShaderNode *self = (GskGLShaderNode *) node;
+
+  g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_GLSHADER_NODE), NULL);
+  g_return_val_if_fail (idx < self->n_children, NULL);
+
+  return self->children[idx];
+}
+
+/**
+ * gsk_glshader_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_glshader_node_get_shader (GskRenderNode *node)
+{
+  GskGLShaderNode *self = (GskGLShaderNode *) node;
+
+  g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_GLSHADER_NODE), 0);
+
+  return self->shader;
+}
+
+/**
+ * gsk_glshader_node_get_uniform_data:
+ * @node: (type GskGLShaderNode): a #GskRenderNode for a gl shader
+ *
+ * Gets args for the node.
+ */
+const guchar *
+gsk_glshader_node_peek_uniform_data (GskRenderNode *node)
+{
+  GskGLShaderNode *self = (GskGLShaderNode *) node;
+  int uniforms_size;
+
+  g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_GLSHADER_NODE), NULL);
+
+  uniforms_size = gsk_glshader_get_uniforms_size (self->shader);
+  if (uniforms_size > sizeof (self->args.inlined))
+    return self->args.external;
+  else
+    return &self->args.inlined[0];
+}
+
 GType gsk_render_node_types[GSK_RENDER_NODE_TYPE_N_TYPES];
 
 #ifndef I_
@@ -4506,6 +4735,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_glshader_node, GSK_GLSHADER_NODE)
 GSK_DEFINE_RENDER_NODE_TYPE (gsk_debug_node, GSK_DEBUG_NODE)
 
 static void
@@ -4863,6 +5093,22 @@ gsk_render_node_init_types_once (void)
     gsk_render_node_types[GSK_BLUR_NODE] = node_type;
   }
 
+  {
+    const GskRenderNodeTypeInfo node_info =
+    {
+      GSK_GLSHADER_NODE,
+      sizeof (GskGLShaderNode),
+      NULL,
+      gsk_glshader_node_finalize,
+      gsk_glshader_node_draw,
+      NULL,
+      gsk_glshader_node_diff,
+    };
+
+    GType node_type = gsk_render_node_type_register_static (I_("GskGLShaderNode"), &node_info);
+    gsk_render_node_types[GSK_GLSHADER_NODE] = node_type;
+  }
+
   {
     const GskRenderNodeTypeInfo node_info =
     {
diff --git a/gsk/gskrendernodeparser.c b/gsk/gskrendernodeparser.c
index 870ce8818b..398f7dc315 100644
--- a/gsk/gskrendernodeparser.c
+++ b/gsk/gskrendernodeparser.c
@@ -1089,6 +1089,15 @@ parse_inset_shadow_node (GtkCssParser *parser)
   return gsk_inset_shadow_node_new (&outline, &color, dx, dy, spread, blur);
 }
 
+
+static GskRenderNode *
+parse_glshader_node (GtkCssParser *parser)
+{
+  /* TODO */
+  gtk_css_parser_error_syntax (parser, "glshader node parsing not implemented yet");
+  return NULL;
+}
+
 static GskRenderNode *
 parse_border_node (GtkCssParser *parser)
 {
@@ -1603,6 +1612,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;
@@ -1837,6 +1847,19 @@ append_point (GString                *str,
   string_append_double (str, p->y);
 }
 
+static void
+append_string (GString    *str,
+               const char *value)
+{
+  char *escaped = g_strescape (value, NULL);
+
+  g_string_append_c (str, '"');
+  g_string_append (str, escaped);
+  g_string_append_c (str, '"');
+
+  g_free (escaped);
+}
+
 static void
 append_vec4 (GString               *str,
              const graphene_vec4_t *v)
@@ -1914,6 +1937,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);
+  append_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 +2476,24 @@ render_node_print (Printer       *p,
       }
       break;
 
+    case GSK_GLSHADER_NODE:
+      {
+        start_node (p, "glshader");
+
+        GskGLShader *shader = gsk_glshader_node_get_shader (node);
+        append_string_param (p, "shader", gsk_glshader_peek_sourcecode (shader));
+        append_node_param (p, "fallback", gsk_glshader_node_get_fallback_child (node));
+        for (guint i = 0; i < gsk_glshader_node_get_n_children (node); i ++)
+          {
+            GskRenderNode *child = gsk_glshader_node_get_child (node, i);
+            _indent (p);
+            render_node_print (p, child);
+          }
+
+        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..13961acc82 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_GLSHADER_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..0f229d17ad 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_GLSHADER_NODE:
+      {
+        GListStore *store = G_LIST_STORE (create_render_node_list_model ((GskRenderNode *[1]) { 
gsk_glshader_node_get_fallback_child (node) }, 1));
+
+        for (guint i = 0; i < gsk_glshader_node_get_n_children (node); i++)
+          {
+            GskRenderNode *child = gsk_glshader_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_GLSHADER_NODE:
+      return "GLShader";
     }
 }
 
@@ -301,6 +322,7 @@ node_name (GskRenderNode *node)
     case GSK_CROSS_FADE_NODE:
     case GSK_TEXT_NODE:
     case GSK_BLUR_NODE:
+    case GSK_GLSHADER_NODE:
       return g_strdup (node_type_name (gsk_render_node_get_node_type (node)));
 
     case GSK_DEBUG_NODE:
@@ -759,6 +781,9 @@ populate_render_node_properties (GtkListStore  *store,
       add_float_row (store, "Radius", gsk_blur_node_get_radius (node));
       break;
 
+    case GSK_GLSHADER_NODE:
+      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]