summaryrefslogtreecommitdiff
path: root/gsk/gskrendernodeparser.c
diff options
context:
space:
mode:
authorAlexander Larsson <alexl@redhat.com>2020-09-18 17:46:57 +0200
committerAlexander Larsson <alexl@redhat.com>2020-09-29 09:51:16 +0200
commit7ea755e206c36960db05f0e0660c6f5f20b692c6 (patch)
tree56a83de7a5cd4f40d8f4ab539f4b3211015a4f12 /gsk/gskrendernodeparser.c
parent6e9b58b6f081f3cf1cba697adada8cd3bc2438d5 (diff)
downloadgtk+-7ea755e206c36960db05f0e0660c6f5f20b692c6.tar.gz
Add GskGLShaderNode and GskGLShader
A GskGLShader is an abstraction of a GLSL fragment shader that can produce pixel values given inputs: * N (currently max 4) textures * Current arguments for the shader uniform Uniform types are: float,(u)int,bool,vec234) There is also a builder for the uniform arguments which are passed around as immutable GBytes in the built form. A GskGLShaderNode is a render node that renders a GskGLShader inside a specified rectangular bounds. It renders its child nodes as textures and passes those as texture arguments to the shader. You also pass it a uniform arguments object.
Diffstat (limited to 'gsk/gskrendernodeparser.c')
-rw-r--r--gsk/gskrendernodeparser.c336
1 files changed, 335 insertions, 1 deletions
diff --git a/gsk/gskrendernodeparser.c b/gsk/gskrendernodeparser.c
index 870ce8818b..667a68f8a9 100644
--- a/gsk/gskrendernodeparser.c
+++ b/gsk/gskrendernodeparser.c
@@ -824,9 +824,9 @@ clear_node (gpointer inout_node)
static GskRenderNode *
parse_container_node (GtkCssParser *parser)
{
- GskRenderNode *node;
GPtrArray *nodes;
const GtkCssToken *token;
+ GskRenderNode *node;
nodes = g_ptr_array_new_with_free_func ((GDestroyNotify) gsk_render_node_unref);
@@ -1089,6 +1089,209 @@ parse_inset_shadow_node (GtkCssParser *parser)
return gsk_inset_shadow_node_new (&outline, &color, dx, dy, spread, blur);
}
+typedef union {
+ gint32 i;
+ double v[4];
+} UniformValue;
+
+typedef struct {
+ GskGLShader *shader;
+ GskShaderArgsBuilder *args;
+} ShaderInfo;
+
+static void
+clear_shader_info (gpointer data)
+{
+ ShaderInfo *info = data;
+ g_clear_object (&info->shader);
+ g_clear_pointer (&info->args, gsk_shader_args_builder_unref);
+}
+
+static gboolean
+parse_shader (GtkCssParser *parser,
+ gpointer out_shader_info)
+{
+ ShaderInfo *shader_info = out_shader_info;
+ char *sourcecode = NULL;
+ GBytes *bytes;
+ GskGLShader *shader;
+
+ if (!parse_string (parser, &sourcecode))
+ return FALSE;
+
+ bytes = g_bytes_new_take (sourcecode, strlen (sourcecode));
+ shader = gsk_gl_shader_new_from_bytes (bytes);
+ g_bytes_unref (bytes);
+
+ shader_info->shader = shader;
+
+ return TRUE;
+}
+
+static gboolean
+parse_uniform_value (GtkCssParser *parser,
+ int idx,
+ ShaderInfo *shader_info)
+{
+ switch (gsk_gl_shader_get_uniform_type (shader_info->shader, idx))
+ {
+ case GSK_GL_UNIFORM_TYPE_FLOAT:
+ {
+ double f;
+ if (!gtk_css_parser_consume_number (parser, &f))
+ return FALSE;
+ gsk_shader_args_builder_set_float (shader_info->args, idx, f);
+ }
+ break;
+
+ case GSK_GL_UNIFORM_TYPE_INT:
+ {
+ int i;
+ if (!gtk_css_parser_consume_integer (parser, &i))
+ return FALSE;
+ gsk_shader_args_builder_set_int (shader_info->args, idx, i);
+ }
+ break;
+
+ case GSK_GL_UNIFORM_TYPE_UINT:
+ {
+ int i;
+ if (!gtk_css_parser_consume_integer (parser, &i) || i < 0)
+ return FALSE;
+ gsk_shader_args_builder_set_uint (shader_info->args, idx, i);
+ }
+ break;
+
+ case GSK_GL_UNIFORM_TYPE_BOOL:
+ {
+ int i;
+ if (!gtk_css_parser_consume_integer (parser, &i) || (i != 0 && i != 1))
+ return FALSE;
+ gsk_shader_args_builder_set_bool (shader_info->args, idx, i);
+ }
+ break;
+
+ case GSK_GL_UNIFORM_TYPE_VEC2:
+ {
+ double f0, f1;
+ graphene_vec2_t v;
+ if (!gtk_css_parser_consume_number (parser, &f0) ||
+ !gtk_css_parser_consume_number (parser, &f1))
+ return FALSE;
+ graphene_vec2_init (&v, f0, f1);
+ gsk_shader_args_builder_set_vec2 (shader_info->args, idx, &v);
+ }
+ break;
+
+ case GSK_GL_UNIFORM_TYPE_VEC3:
+ {
+ double f0, f1, f2;
+ graphene_vec3_t v;
+ if (!gtk_css_parser_consume_number (parser, &f0) ||
+ !gtk_css_parser_consume_number (parser, &f1) ||
+ !gtk_css_parser_consume_number (parser, &f2))
+ return FALSE;
+ graphene_vec3_init (&v, f0, f1, f2);
+ gsk_shader_args_builder_set_vec3 (shader_info->args, idx, &v);
+ }
+ break;
+
+ case GSK_GL_UNIFORM_TYPE_VEC4:
+ {
+ double f0, f1, f2, f3;
+ graphene_vec4_t v;
+ if (!gtk_css_parser_consume_number (parser, &f0) ||
+ !gtk_css_parser_consume_number (parser, &f1) ||
+ !gtk_css_parser_consume_number (parser, &f2) ||
+ !gtk_css_parser_consume_number (parser, &f3))
+ return FALSE;
+ graphene_vec4_init (&v, f0, f1, f2, f3);
+ gsk_shader_args_builder_set_vec4 (shader_info->args, idx, &v);
+ }
+ break;
+
+ case GSK_GL_UNIFORM_TYPE_NONE:
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ if (idx < gsk_gl_shader_get_n_uniforms (shader_info->shader))
+ {
+ if (!gtk_css_parser_try_token (parser, GTK_CSS_TOKEN_COMMA))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+parse_shader_args (GtkCssParser *parser, gpointer data)
+{
+ ShaderInfo *shader_info = data;
+ int n_uniforms;
+ int i;
+
+ shader_info->args = gsk_shader_args_builder_new (shader_info->shader, NULL);
+ n_uniforms = gsk_gl_shader_get_n_uniforms (shader_info->shader);
+
+ for (i = 0; i < n_uniforms; i++)
+ {
+ if (!parse_uniform_value (parser, i, shader_info))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static GskRenderNode *
+parse_glshader_node (GtkCssParser *parser)
+{
+ graphene_rect_t bounds = GRAPHENE_RECT_INIT (0, 0, 50, 50);
+ GskRenderNode *child[4] = { NULL, };
+ ShaderInfo shader_info = {
+ NULL,
+ NULL,
+ };
+ const Declaration declarations[] = {
+ { "bounds", parse_rect, NULL, &bounds },
+ { "sourcecode", parse_shader, NULL, &shader_info },
+ { "args", parse_shader_args, clear_shader_info, &shader_info },
+ { "child1", parse_node, clear_node, &child[0] },
+ { "child2", parse_node, clear_node, &child[1] },
+ { "child3", parse_node, clear_node, &child[2] },
+ { "child4", parse_node, clear_node, &child[3] },
+ };
+ GskGLShader *shader;
+ GskRenderNode *node;
+ GBytes *args = NULL;
+ int len, i;
+
+ parse_declarations (parser, declarations, G_N_ELEMENTS(declarations));
+
+ for (len = 0; len < 4; len++)
+ {
+ if (child[len] == NULL)
+ break;
+ }
+
+ shader = shader_info.shader;
+ args = gsk_shader_args_builder_free_to_args (shader_info.args);
+
+ node = gsk_gl_shader_node_new (shader, &bounds, args, child, len);
+
+ g_bytes_unref (args);
+ g_object_unref (shader);
+
+ for (i = 0; i < 4; i++)
+ {
+ if (child[i])
+ gsk_render_node_unref (child[i]);
+ }
+
+ return node;
+}
+
static GskRenderNode *
parse_border_node (GtkCssParser *parser)
{
@@ -1603,6 +1806,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;
@@ -1915,6 +2119,18 @@ append_point_param (Printer *p,
}
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);
+ _gtk_css_print_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,
const graphene_vec4_t *value)
@@ -2441,6 +2657,124 @@ render_node_print (Printer *p,
}
break;
+ case GSK_GL_SHADER_NODE:
+ {
+ GskGLShader *shader = gsk_gl_shader_node_get_shader (node);
+ GBytes *args = gsk_gl_shader_node_get_args (node);
+
+ start_node (p, "glshader");
+
+ append_rect_param (p, "bounds", &node->bounds);
+
+ GBytes *bytes = gsk_gl_shader_get_source (shader);
+ /* Ensure we are zero-terminated */
+ char *sourcecode = g_strndup (g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes));
+ append_string_param (p, "sourcecode", sourcecode);
+ g_free (sourcecode);
+
+ if (gsk_gl_shader_get_n_uniforms (shader) > 0)
+ {
+ GString *data = g_string_new ("");
+
+ for (guint i = 0; i < gsk_gl_shader_get_n_uniforms (shader); i++)
+ {
+ if (i > 0)
+ g_string_append (data, ", ");
+
+ switch (gsk_gl_shader_get_uniform_type (shader, i))
+ {
+ case GSK_GL_UNIFORM_TYPE_NONE:
+ default:
+ g_assert_not_reached ();
+ break;
+
+ case GSK_GL_UNIFORM_TYPE_FLOAT:
+ {
+ float value = gsk_gl_shader_get_arg_float (shader, args, i);
+ string_append_double (data, value);
+ }
+ break;
+
+ case GSK_GL_UNIFORM_TYPE_INT:
+ {
+ gint32 value = gsk_gl_shader_get_arg_int (shader, args, i);
+ g_string_append_printf (data, "%d", value);
+ }
+ break;
+
+ case GSK_GL_UNIFORM_TYPE_UINT:
+ {
+ guint32 value = gsk_gl_shader_get_arg_uint (shader, args, i);
+ g_string_append_printf (data, "%u", value);
+ }
+ break;
+
+ case GSK_GL_UNIFORM_TYPE_BOOL:
+ {
+ gboolean value = gsk_gl_shader_get_arg_bool (shader, args, i);
+ g_string_append_printf (data, "%d", value);
+ }
+ break;
+
+ case GSK_GL_UNIFORM_TYPE_VEC2:
+ {
+ graphene_vec2_t value;
+ gsk_gl_shader_get_arg_vec2 (shader, args, i,
+ &value);
+ string_append_double (data, graphene_vec2_get_x (&value));
+ g_string_append (data, " ");
+ string_append_double (data, graphene_vec2_get_y (&value));
+ }
+ break;
+
+ case GSK_GL_UNIFORM_TYPE_VEC3:
+ {
+ graphene_vec3_t value;
+ gsk_gl_shader_get_arg_vec3 (shader, args, i,
+ &value);
+ string_append_double (data, graphene_vec3_get_x (&value));
+ g_string_append (data, " ");
+ string_append_double (data, graphene_vec3_get_y (&value));
+ g_string_append (data, " ");
+ string_append_double (data, graphene_vec3_get_z (&value));
+ }
+ break;
+
+ case GSK_GL_UNIFORM_TYPE_VEC4:
+ {
+ graphene_vec4_t value;
+ gsk_gl_shader_get_arg_vec4 (shader, args, i,
+ &value);
+ string_append_double (data, graphene_vec4_get_x (&value));
+ g_string_append (data, " ");
+ string_append_double (data, graphene_vec4_get_y (&value));
+ g_string_append (data, " ");
+ string_append_double (data, graphene_vec4_get_z (&value));
+ g_string_append (data, " ");
+ string_append_double (data, graphene_vec4_get_w (&value));
+ }
+ break;
+ }
+ }
+ _indent (p);
+ g_string_append_printf (p->str, "args: %s;\n", data->str);
+ g_string_free (data, TRUE);
+ }
+
+ for (guint i = 0; i < gsk_gl_shader_node_get_n_children (node); i ++)
+ {
+ GskRenderNode *child = gsk_gl_shader_node_get_child (node, i);
+ char *name;
+
+ name = g_strdup_printf ("child%d", i + 1);
+ append_node_param (p, name, child);
+ g_free (name);
+ }
+
+ end_node (p);
+ }
+ break;
+
case GSK_REPEAT_NODE:
{
GskRenderNode *child = gsk_repeat_node_get_child (node);