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.
This commit is contained in:
Alexander Larsson
2020-09-18 17:46:57 +02:00
parent a70addb8a5
commit 61ae25d55e
6 changed files with 473 additions and 2 deletions

View File

@@ -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;
/**

View File

@@ -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__ */

View File

@@ -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 =
{

View File

@@ -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);

View File

@@ -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[];

View File

@@ -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);