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