[gtk/matthiasc/glshader-demo: 4/9] Add GskGLShaderNode




commit 61ae25d55e535fd52a3f92d3878abb6258ce8802
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 vec4 args that can be used however the shader wants
     * a list of render nodes that are rendered to textures
     * A glsl string as a GRefString, which will be compiled
    
    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             |   3 +-
 gsk/gskrendernode.h        |  36 +++++
 gsk/gskrendernodeimpl.c    | 334 +++++++++++++++++++++++++++++++++++++++++++++
 gsk/gskrendernodeparser.c  |  56 ++++++++
 gsk/gskrendernodeprivate.h |   2 +-
 gtk/inspector/recorder.c   |  44 ++++++
 6 files changed, 473 insertions(+), 2 deletions(-)
---
diff --git a/gsk/gskenums.h b/gsk/gskenums.h
index 24aafec502..4e7e557537 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;
 
 /**
diff --git a/gsk/gskrendernode.h b/gsk/gskrendernode.h
index 424039753a..549c7e59cb 100644
--- a/gsk/gskrendernode.h
+++ b/gsk/gskrendernode.h
@@ -53,6 +53,11 @@ struct _GskShadow
   float radius;
 };
 
+#define GSK_TYPE_GLSHADER (gsk_glshader_get_type ())
+
+GDK_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (GskGLShader, gsk_glshader, GSK, GLSHADER, GObject)
+
 /**
  * GskParseErrorFunc:
  * @section: the #GtkCssSection where the error occurred
@@ -72,6 +77,12 @@ GType                   gsk_render_node_get_type                (void) G_GNUC_CO
 GDK_AVAILABLE_IN_ALL
 GQuark                  gsk_serialization_error_quark           (void);
 
+
+GDK_AVAILABLE_IN_ALL
+GskGLShader *           gsk_glshader_new                        (const char   *source);
+GDK_AVAILABLE_IN_ALL
+const char *            gsk_glshader_peek_source                (GskGLShader  *shader);
+
 GDK_AVAILABLE_IN_ALL
 GskRenderNode *         gsk_render_node_ref                     (GskRenderNode *node);
 GDK_AVAILABLE_IN_ALL
@@ -122,6 +133,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 +158,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 +464,29 @@ 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_vec4_t    *args,
+                                                                 const graphene_rect_t    *bounds,
+                                                                 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
+void                    gsk_glshader_node_get_args              (GskRenderNode            *node,
+                                                                 graphene_vec4_t          *out_args);
+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..6a493609b4 100644
--- a/gsk/gskrendernodeimpl.c
+++ b/gsk/gskrendernodeimpl.c
@@ -4470,6 +4470,323 @@ gsk_debug_node_get_message (GskRenderNode *node)
   return self->message;
 }
 
+/*** GSK_GLSHADER_NODE ***/
+struct _GskGLShader
+{
+  GObject parent_instance;
+  char *source;
+};
+
+G_DEFINE_TYPE (GskGLShader, gsk_glshader, G_TYPE_OBJECT)
+
+enum {
+  GLSHADER_PROP_0,
+  GLSHADER_PROP_SOURCE,
+  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_SOURCE:
+      g_value_set_string (value, shader->source);
+      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_SOURCE:
+      g_free (shader->source);
+      shader->source = g_value_dup_string (value);
+      break;
+
+    default:
+      g_assert_not_reached ();
+    }
+}
+
+static void
+gsk_glshader_finalize (GObject *object)
+{
+  GskGLShader *shader = GSK_GLSHADER (object);
+
+  g_free (shader->source);
+
+  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:source:
+   *
+   * The source code for the shader.
+   */
+  gsk_glshader_properties[GLSHADER_PROP_SOURCE] =
+    g_param_spec_string ("source",
+                         "Source",
+                         "The source code for the shader",
+                         NULL,
+                         G_PARAM_READWRITE |
+                         G_PARAM_CONSTRUCT_ONLY |
+                         G_PARAM_STATIC_STRINGS);
+
+  g_object_class_install_properties (object_class, GLSHADER_N_PROPS, gsk_glshader_properties);
+}
+
+static void
+gsk_glshader_init (GskGLShader *shader)
+{
+}
+
+GskGLShader *
+gsk_glshader_new (const char *source)
+{
+   GskGLShader *shader = g_object_new (GSK_TYPE_GLSHADER,
+                                       "source", source,
+                                       NULL);
+   return shader;
+}
+
+const char *
+gsk_glshader_peek_source (GskGLShader *shader)
+{
+  return shader->source;
+}
+
+struct _GskGLShaderNode
+{
+  GskRenderNode render_node;
+
+  GskGLShader *shader;
+  graphene_vec4_t args;
+  GskRenderNode *fallback;
+  guint n_children;
+  GskRenderNode **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));
+
+  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);
+
+  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;
+
+  if (graphene_rect_equal (&node1->bounds, &node2->bounds) &&
+      self1->shader == self2->shader &&
+      graphene_vec4_equal (&self1->args, &self2->args) &&
+      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
+ * @args: a vec4 argument to the shader
+ * @bounds: the rectangle to render the glshader into
+ * @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_vec4_t *args,
+                       const graphene_rect_t *bounds,
+                       GskRenderNode         *fallback,
+                       GskRenderNode        **children,
+                       int                    n_children)
+{
+  GskGLShaderNode *self;
+  GskRenderNode *node;
+
+  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);
+  self->args = *args;
+
+  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_args:
+ * @node: (type GskGLShaderNode): a #GskRenderNode for a gl shader
+ * @out_args: Location to write results to.
+ *
+ * Gets args for the node.
+ */
+void
+gsk_glshader_node_get_args (GskRenderNode   *node,
+                            graphene_vec4_t *out_args)
+{
+  GskGLShaderNode *self = (GskGLShaderNode *) node;
+
+  g_return_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_GLSHADER_NODE));
+
+  *out_args = self->args;
+}
+
 GType gsk_render_node_types[GSK_RENDER_NODE_TYPE_N_TYPES];
 
 #ifndef I_
@@ -4506,6 +4823,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 +5181,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..88e5f9d8b9 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,27 @@ 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_source (shader));
+        graphene_vec4_t args;
+        gsk_glshader_node_get_args (node, &args);
+        append_vec4_param (p, "args", &args);
+        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/gtk/inspector/recorder.c b/gtk/inspector/recorder.c
index 8b42de031e..1266076bd0 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:
@@ -521,6 +543,20 @@ add_float_row (GtkListStore  *store,
   g_free (text);
 }
 
+static void
+add_vec4_row (GtkListStore    *store,
+              const char      *name,
+              graphene_vec4_t *value)
+{
+  char *text = g_strdup_printf ("[%.2f, %.2f, %.2f, %.2f]",
+                                graphene_vec4_get_x (value),
+                                graphene_vec4_get_y (value),
+                                graphene_vec4_get_z (value),
+                                graphene_vec4_get_w (value));
+  add_text_row (store, name, text);
+  g_free (text);
+}
+
 static void
 populate_render_node_properties (GtkListStore  *store,
                                  GskRenderNode *node)
@@ -759,6 +795,14 @@ populate_render_node_properties (GtkListStore  *store,
       add_float_row (store, "Radius", gsk_blur_node_get_radius (node));
       break;
 
+    case GSK_GLSHADER_NODE:
+      {
+        graphene_vec4_t args;
+        gsk_glshader_node_get_args (node, &args);
+        add_vec4_row (store, "Args", &args);
+      }
+      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]