[gtk/matthiasc/glshader-demo: 4/9] Add GskGLShaderNode
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/matthiasc/glshader-demo: 4/9] Add GskGLShaderNode
- Date: Tue, 22 Sep 2020 02:17:57 +0000 (UTC)
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]