From 111dfdf3f53ad6cbdb89a5a1b4165555e36bf590 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Fri, 18 Sep 2020 17:46:57 +0200 Subject: [PATCH] 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 GskGLShader object with the source and uniforms definitions computed from the source. * A the data for the uniforms, formated according to the GskGLShader * a list of render nodes that are rendered to textures 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 | 32 +- gsk/gskglshader.c | 1134 ++++++++++++++++++++++++++++++++++++ gsk/gskglshader.h | 153 +++++ gsk/gskglshaderprivate.h | 19 + gsk/gskrendernode.h | 25 + gsk/gskrendernodeimpl.c | 240 ++++++++ gsk/gskrendernodeparser.c | 408 ++++++++++++- gsk/gskrendernodeprivate.h | 2 +- gsk/meson.build | 2 + gtk/inspector/recorder.c | 136 +++++ 10 files changed, 2148 insertions(+), 3 deletions(-) create mode 100644 gsk/gskglshader.c create mode 100644 gsk/gskglshader.h create mode 100644 gsk/gskglshaderprivate.h diff --git a/gsk/gskenums.h b/gsk/gskenums.h index 24aafec502..3db02599db 100644 --- a/gsk/gskenums.h +++ b/gsk/gskenums.h @@ -48,6 +48,7 @@ * @GSK_TEXT_NODE: A node containing a glyph string * @GSK_BLUR_NODE: A node that applies a blur * @GSK_DEBUG_NODE: Debug information that does not affect the rendering + * @GSK_GL_SHADER_NODE: A node that uses OpenGL fragment shaders to render * The type of a node determines what the node is rendering. */ @@ -75,7 +76,8 @@ typedef enum { GSK_CROSS_FADE_NODE, GSK_TEXT_NODE, GSK_BLUR_NODE, - GSK_DEBUG_NODE + GSK_DEBUG_NODE, + GSK_GL_SHADER_NODE } GskRenderNodeType; /** @@ -218,4 +220,32 @@ typedef enum GSK_TRANSFORM_CATEGORY_IDENTITY } GskTransformCategory; +/** + * GskGLUniformType: + * @GSK_GLUNIFORM_TYPE_NONE: No type, used for uninitialized or unspecified values. + * @GSK_GLUNIFORM_TYPE_FLOAT: A float uniform + * @GSK_GLUNIFORM_TYPE_INT: A GLSL int / gint32 uniform + * @GSK_GLUNIFORM_TYPE_UINT: A GLSL uint / guint32 uniform + * @GSK_GLUNIFORM_TYPE_BOOL: A GLSL bool / gboolean uniform + * @GSK_GLUNIFORM_TYPE_VEC2: A GLSL vec2 / graphene_vec2_t uniform + * @GSK_GLUNIFORM_TYPE_VEC3: A GLSL vec3 / graphene_vec3_t uniform + * @GSK_GLUNIFORM_TYPE_VEC4: A GLSL vec4 / graphene_vec4_t uniform + * + * This defines the types of the uniforms that #GskGLShaders + * declare. It defines both what the type is called in the GLSL shader + * code, and what the corresponding C type is on the Gtk side. + */ +typedef enum +{ + GSK_GLUNIFORM_TYPE_NONE, + GSK_GLUNIFORM_TYPE_FLOAT, + GSK_GLUNIFORM_TYPE_INT, + GSK_GLUNIFORM_TYPE_UINT, + GSK_GLUNIFORM_TYPE_BOOL, + GSK_GLUNIFORM_TYPE_VEC2, + GSK_GLUNIFORM_TYPE_VEC3, + GSK_GLUNIFORM_TYPE_VEC4, +} GskGLUniformType; + + #endif /* __GSK_TYPES_H__ */ diff --git a/gsk/gskglshader.c b/gsk/gskglshader.c new file mode 100644 index 0000000000..cf392160ae --- /dev/null +++ b/gsk/gskglshader.c @@ -0,0 +1,1134 @@ +/* GSK - The GTK Scene Kit + * + * Copyright 2020, Red Hat Inc + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +/** + * SECTION:GskGLShader + * @Title: GskGLShader + * @Short_description: A description of GskGLShader + * + * A #GskGLShader is a snippet of GLSL that is meant to run in the + * fragment shader of the rendering pipeline. A fragment shader it + * gets the coordinates being rendered as input and produces the + * pixel values for that particular pixel. Additionally the + * shader can declare a set of other input arguments, called + * uniforms (as they are uniform over all the calls to your shader in + * each instance of use). A shader can also receive up to 4 + * textures that it can use as input when producing the pixel data. + * + * The typical way a #GskGLShader is used is as an argument to a + * #GskGLShaderNode in the rendering hierarchy, and then the textures + * it gets as input are constructed by rendering the child nodes to + * textures before rendering the shader node itself. (Although you can + * also pass texture nodes as children if you want to directly use a + * texture as input). Note that the #GskGLShaderNode API is a bit + * lowlevel, and highlevel code normally uses + * gtk_snapshot_push_glshader() to produce the nodes. + * + * The actual shader code is GLSL code that gets combined with + * some other code into the fragment shader. Since the exact + * capabilities of the GPU driver differs between different OpenGL + * drivers and hardware, GTK adds some defines that you can use + * to ensure your GLSL code runs on as many drivers as it can. + * + * If the OpenGL driver is GLES, then the shader language version + * is set to 100, and GSK_GLES will be defined in the shader. + * + * Otherwise, if the OpenGL driver does not support the 3.2 core profile, + * then the shader will run with language version 110 for GL2 and 130 for GL3, + * and GSK_LEGACY will be defined in the shader. + * + * If the OpenGL driver supports the 3.2 code profile, it will be used, + * the shader language version is set to 150, and GSK_GL3 will be defined + * in the shader. + * + * The main function the shader should implement is: + * + * |[ + * void mainImage(out vec4 fragColor, + * in vec2 fragCoord, + * in vec2 resolution, + * in vec2 uv) + * ]| + * + * Where the input @fragCoord is the coordinate of the pixel we're + * currently rendering, relative to the boundary rectangle that was + * specified in the #GskGLShaderNode, and @resolution is the width and + * height of that rectangle. This is in the typical GTK coordinate + * system with the origin in the top left. @uv contains the u and v + * coordinates that can be used to index a texture at the + * corresponding point. These coordinates are in the [0..1]x[0..1] + * region, with 0, 0 being in the lower left cornder (which is typical + * for OpenGL). + * + * The output @fragColor should be a RGBA color (with + * non-premultiplied alpha) that will be used as the output for the + * specified pixel location. Note that this output will be + * automatically clipped to the clip region of the glshader node. + * + * In addition to the function arguments the shader can define + * up to 4 uniforms for tetxures which must be called u_textureN + * (i.e. u_texture1 to u_texture4) as well as any custom uniforms + * you want of types int, uint, bool, float, vec2, vec3 or vec4. + * + * Note that GTK parses the uniform declarations, so each uniform has to + * be on a line by itself with no other code, like so: + * + * |[ + * uniform float u_time; + * uniform vec3 u_color; + * uniform sampler2D u_texture1; + * uniform sampler2D u_texture2; + * ]| + * + * GTK uses the the "gsk" namespace in the symbols it uses in the + * shader, so your code should not use any symbols with the prefix gsk + * or GSK. There are some helper functions declared that you can use: + * + * |[ + * vec4 GskTexture(sampler2D sampler, vec2 texCoords); + * ]| + * + * This samples a texture (e.g. u_texture1) at the specified + * coordinates, and containes some helper ifdefs to ensure that + * it works on all OpenGL versions. + */ + +#include "config.h" +#include "gskglshader.h" +#include "gskglshaderprivate.h" + +static GskGLUniformType +uniform_type_from_glsl (const char *str) +{ + if (strcmp (str, "int") == 0) + return GSK_GLUNIFORM_TYPE_INT; + if (strcmp (str, "uint") == 0) + return GSK_GLUNIFORM_TYPE_UINT; + if (strcmp (str, "bool") == 0) + return GSK_GLUNIFORM_TYPE_BOOL; + if (strcmp (str, "float") == 0) + return GSK_GLUNIFORM_TYPE_FLOAT; + if (strcmp (str, "vec2") == 0) + return GSK_GLUNIFORM_TYPE_VEC2; + if (strcmp (str, "vec3") == 0) + return GSK_GLUNIFORM_TYPE_VEC3; + if (strcmp (str, "vec4") == 0) + return GSK_GLUNIFORM_TYPE_VEC4; + + return GSK_GLUNIFORM_TYPE_NONE; +} + +static int +uniform_type_size (GskGLUniformType type) +{ + switch (type) + { + case GSK_GLUNIFORM_TYPE_FLOAT: + return sizeof (float); + + case GSK_GLUNIFORM_TYPE_INT: + return sizeof (gint32); + + case GSK_GLUNIFORM_TYPE_UINT: + case GSK_GLUNIFORM_TYPE_BOOL: + return sizeof (guint32); + + case GSK_GLUNIFORM_TYPE_VEC2: + return sizeof (float) * 2; + + case GSK_GLUNIFORM_TYPE_VEC3: + return sizeof (float) * 3; + + case GSK_GLUNIFORM_TYPE_VEC4: + return sizeof (float) * 4; + + case GSK_GLUNIFORM_TYPE_NONE: + default: + g_assert_not_reached (); + return 0; + } +} + +struct _GskGLShader +{ + GObject parent_instance; + GBytes *bytes; + char *resource; + int n_required_textures; + int uniforms_size; + GArray *uniforms; +}; + +G_DEFINE_TYPE (GskGLShader, gsk_gl_shader, G_TYPE_OBJECT) + +enum { + GLSHADER_PROP_0, + GLSHADER_PROP_BYTES, + GLSHADER_PROP_RESOURCE, + GLSHADER_N_PROPS +}; +static GParamSpec *gsk_gl_shader_properties[GLSHADER_N_PROPS]; + +static void +gsk_gl_shader_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GskGLShader *shader = GSK_GL_SHADER (object); + + switch (prop_id) + { + case GLSHADER_PROP_BYTES: + g_value_set_boxed (value, shader->bytes); + break; + + case GLSHADER_PROP_RESOURCE: + g_value_set_string (value, shader->resource); + break; + + default: + g_assert_not_reached (); + } +} + +static void +gsk_gl_shader_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GskGLShader *shader = GSK_GL_SHADER (object); + + switch (prop_id) + { + case GLSHADER_PROP_BYTES: + g_clear_pointer (&shader->bytes, g_bytes_unref); + shader->bytes = g_value_dup_boxed (value); + break; + + case GLSHADER_PROP_RESOURCE: + { + GError *error = NULL; + GBytes *bytes; + const char *resource; + + resource = g_value_get_string (value); + if (resource == NULL) + break; + + bytes = g_resources_lookup_data (resource, 0, &error); + if (bytes) + { + g_clear_pointer (&shader->bytes, g_bytes_unref); + shader->bytes = bytes; + shader->resource = g_strdup (resource); + } + else + { + g_critical ("Unable to load resource %s for glshader: %s", resource, error->message); + g_error_free (error); + if (shader->bytes == NULL) + shader->bytes = g_bytes_new_static ("", 1); + } + } + break; + + default: + g_assert_not_reached (); + } +} + +static void +gsk_gl_shader_finalize (GObject *object) +{ + GskGLShader *shader = GSK_GL_SHADER (object); + + g_bytes_unref (shader->bytes); + g_free (shader->resource); + for (int i = 0; i < shader->uniforms->len; i ++) + g_free (g_array_index (shader->uniforms, GskGLUniform, i).name); + g_array_free (shader->uniforms, TRUE); + + G_OBJECT_CLASS (gsk_gl_shader_parent_class)->finalize (object); +} + +static GRegex *uniform_regexp = NULL; /* Initialized in class_init */ + + +static void +gsk_gl_shader_add_uniform (GskGLShader *shader, + const char *name, + GskGLUniformType type) +{ + GskGLUniform uniform = { + g_strdup (name), + type, + shader->uniforms_size + }; + + shader->uniforms_size += uniform_type_size (type); + + g_array_append_val (shader->uniforms, uniform); +} + +static void +gsk_gl_shader_constructed (GObject *object) +{ + GskGLShader *shader = GSK_GL_SHADER (object); + gsize string_len; + const char *string = g_bytes_get_data (shader->bytes, &string_len); + GMatchInfo *match_info; + int max_texture_seen = 0; + + g_regex_match_full (uniform_regexp, + string, string_len, 0, 0, + &match_info, NULL); + while (g_match_info_matches (match_info)) + { + char *type = g_match_info_fetch (match_info, 1); + char *name = g_match_info_fetch (match_info, 2); + + if (strcmp (type, "sampler2D") == 0) + { + /* Textures are special cased */ + + if (g_str_has_prefix (name, "u_texture") && + strlen (name) == strlen ("u_texture")+1) + { + char digit = name[strlen("u_texture")]; + if (digit >= '1' && digit <= '9') + max_texture_seen = MAX(max_texture_seen, digit - '0'); + } + else + g_debug ("Unhandled shader texture uniform '%s', use uniforms of name 'u_texture[1..9]'", name); + } + else + { + GskGLUniformType utype = uniform_type_from_glsl (type); + g_assert (utype != GSK_GLUNIFORM_TYPE_NONE); // Shouldn't have matched the regexp + gsk_gl_shader_add_uniform (shader, name, utype); + } + + g_free (type); + g_free (name); + + g_match_info_next (match_info, NULL); + } + + g_match_info_free (match_info); + + shader->n_required_textures = max_texture_seen; +} + +#define SPACE_RE "[ \\t]+" // Don't use \s, we don't want to match newlines +#define OPT_SPACE_RE "[ \\t]*" +#define UNIFORM_TYPE_RE "(int|uint|bool|float|vec2|vec3|vec4|sampler2D)" +#define UNIFORM_NAME_RE "([\\w]+)" +#define OPT_INIT_VALUE_RE "[\\w(),. ]+" // This is a bit simple, but will match most initializers +#define OPT_INITIALIZER_RE "(" OPT_SPACE_RE "=" OPT_SPACE_RE OPT_INIT_VALUE_RE ")?" +#define UNIFORM_MATCHER_RE "^uniform" SPACE_RE UNIFORM_TYPE_RE SPACE_RE UNIFORM_NAME_RE OPT_INITIALIZER_RE OPT_SPACE_RE ";" OPT_SPACE_RE "$" + +static void +gsk_gl_shader_class_init (GskGLShaderClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + uniform_regexp = g_regex_new (UNIFORM_MATCHER_RE, + G_REGEX_MULTILINE | G_REGEX_RAW | G_REGEX_OPTIMIZE, + 0, NULL); + + object_class->get_property = gsk_gl_shader_get_property; + object_class->set_property = gsk_gl_shader_set_property; + object_class->finalize = gsk_gl_shader_finalize; + object_class->constructed = gsk_gl_shader_constructed; + + /** + * GskGLShader:sourcecode: + * + * The source code for the shader, as a #GBytes. + */ + gsk_gl_shader_properties[GLSHADER_PROP_BYTES] = + g_param_spec_boxed ("bytes", + "Bytes", + "The sourcecode code for the shader", + G_TYPE_BYTES, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + /** + * GskGLShader:resource: + * + * resource containing the source code for the shader. + */ + gsk_gl_shader_properties[GLSHADER_PROP_RESOURCE] = + g_param_spec_string ("resource", + "Resources", + "Resource path to the source code", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, GLSHADER_N_PROPS, gsk_gl_shader_properties); +} + +static void +gsk_gl_shader_init (GskGLShader *shader) +{ + shader->uniforms = g_array_new (FALSE, FALSE, sizeof (GskGLUniform)); +} + +/** + * gsk_gl_shader_new_from_bytes: + * @sourcecode: The sourcecode for the shader, as a #GBytes + * + * Creates a #GskGLShader that will render pixels using the specified code. + * + * Returns: (transfer full): A new #GskGLShader + */ +GskGLShader * +gsk_gl_shader_new_from_bytes (GBytes *sourcecode) +{ + g_return_val_if_fail (sourcecode != NULL, NULL); + + return g_object_new (GSK_TYPE_GLSHADER, + "bytes", sourcecode, + NULL); +} + +/** + * gsk_gl_shader_new_from_resource: + * @resource_path: valid path to a resource that contains the sourcecode for the shader + * + * Creates a #GskGLShader that will render pixels using the specified code. + * + * Returns: (transfer full): A new #GskGLShader + */ +GskGLShader * +gsk_gl_shader_new_from_resource (const char *resource_path) +{ + g_return_val_if_fail (resource_path != NULL, NULL); + + return g_object_new (GSK_TYPE_GLSHADER, + "resource", resource_path, + NULL); +} + +/** + * gsk_gl_shader_get_sourcecode: + * @shader: A #GskGLShader + * + * Get the source code being used to render this shader. + * + * Returns: (transfer none): The source code for the shader + */ +GBytes * +gsk_gl_shader_get_bytes (GskGLShader *shader) +{ + return shader->bytes; +} + +/** + * gsk_gl_shader_get_n_required_textures: + * @shader: A #GskGLShader + * + * Returns the number of textures that the shader requires. This can be used + * to check that the a passed shader works in your usecase. This + * is determined by looking at the highest u_textureN value that the + * shader defines. + * + * Returns: The nr of texture input this shader requires. + */ +int +gsk_gl_shader_get_n_required_textures (GskGLShader *shader) +{ + return shader->n_required_textures; +} + +/** + * gsk_gl_shader_get_n_uniforms: + * @shader: A #GskGLShader + * + * Get the number of declared uniforms for this shader. + * + * Returns: The nr of declared uniforms + */ +int +gsk_gl_shader_get_n_uniforms (GskGLShader *shader) +{ + return shader->uniforms->len; +} + +/** + * gsk_gl_shader_get_uniform_name: + * @shader: A #GskGLShader + * @idx: A zero-based index of the uniforms + * + * Get the name of a declared uniforms for this shader at index @indx. + * + * Returns: (transfer none): The name of the declared uniform + */ +const char * +gsk_gl_shader_get_uniform_name (GskGLShader *shader, + int idx) +{ + return g_array_index (shader->uniforms, GskGLUniform, idx).name; +} + +/** + * gsk_gl_shader_find_uniform_by_name: + * @shader: A #GskGLShader + * @name: A uniform name + * + * Looks for a uniform by the name @name, and returns the index + * of the unifor, or -1 if it was not found. + * + * Returns: The index of the uniform, or -1 + */ +int +gsk_gl_shader_find_uniform_by_name (GskGLShader *shader, + const char *name) +{ + for (int i = 0; i < shader->uniforms->len; i++) + { + const GskGLUniform *u = &g_array_index (shader->uniforms, GskGLUniform, i); + if (strcmp (u->name, name) == 0) + return i; + } + + return -1; +} + +/** + * gsk_gl_shader_get_uniform_type: + * @shader: A #GskGLShader + * @idx: A zero-based index of the uniforms + * + * Get the type of a declared uniforms for this shader at index @indx. + * + * Returns: The type of the declared uniform + */ +GskGLUniformType +gsk_gl_shader_get_uniform_type (GskGLShader *shader, + int idx) +{ + return g_array_index (shader->uniforms, GskGLUniform, idx).type; +} + +/** + * gsk_gl_shader_get_uniform_offset: + * @shader: A #GskGLShader + * @idx: A zero-based index of the uniforms + * + * Get the offset into the data block where data for this uniforms is stored. + * + * Returns: The data offset + */ +int +gsk_gl_shader_get_uniform_offset (GskGLShader *shader, + int idx) +{ + return g_array_index (shader->uniforms, GskGLUniform, idx).offset; +} + +const GskGLUniform * +gsk_gl_shader_get_uniforms (GskGLShader *shader, + int *n_uniforms) +{ + *n_uniforms = shader->uniforms->len; + return &g_array_index (shader->uniforms, GskGLUniform, 0); +} + +/** + * gsk_gl_shader_get_uniforms_size: + * @shader: A #GskGLShader + * + * Get the size of the data block used to specify uniform data for this shader. + * + * Returns: The size of the data block + */ +int +gsk_gl_shader_get_uniforms_size (GskGLShader *shader) +{ + return shader->uniforms_size; +} + +static const GskGLUniform * +gsk_gl_shader_find_uniform (GskGLShader *shader, + const char *name) +{ + for (int i = 0; i < shader->uniforms->len; i++) + { + const GskGLUniform *u = &g_array_index (shader->uniforms, GskGLUniform, i); + if (strcmp (u->name, name) == 0) + return u; + } + + return NULL; +} + +/** + * gsk_gl_shader_get_uniform_data_float: + * @shader: A #GskGLShader + * @uniform_data: The uniform data block + * @idx: The index of the uniform + * + * Gets the value of the uniform @idx in the @uniform_data block. + * The uniform must be of float type. + * + * Returns: The value + */ +float +gsk_gl_shader_get_uniform_data_float (GskGLShader *shader, + GBytes *uniform_data, + int idx) +{ + const GskGLUniform *u; + const guchar *args_src; + gsize size; + const guchar *data = g_bytes_get_data (uniform_data, &size); + + g_assert (size == shader->uniforms_size); + g_assert (idx < shader->uniforms->len); + u = &g_array_index (shader->uniforms, GskGLUniform, idx); + g_assert (u->type == GSK_GLUNIFORM_TYPE_FLOAT); + + args_src = data + u->offset; + return *(float *)args_src; +} + +/** + * gsk_gl_shader_get_uniform_data_int: + * @shader: A #GskGLShader + * @uniform_data: The uniform data block + * @idx: The index of the uniform + * + * Gets the value of the uniform @idx in the @uniform_data block. + * The uniform must be of int type. + * + * Returns: The value + */ +gint32 +gsk_gl_shader_get_uniform_data_int (GskGLShader *shader, + GBytes *uniform_data, + int idx) +{ + const GskGLUniform *u; + const guchar *args_src; + gsize size; + const guchar *data = g_bytes_get_data (uniform_data, &size); + + g_assert (size == shader->uniforms_size); + g_assert (idx < shader->uniforms->len); + u = &g_array_index (shader->uniforms, GskGLUniform, idx); + g_assert (u->type == GSK_GLUNIFORM_TYPE_INT); + + args_src = data + u->offset; + return *(gint32 *)args_src; +} + +/** + * gsk_gl_shader_get_uniform_data_uint: + * @shader: A #GskGLShader + * @uniform_data: The uniform data block + * @idx: The index of the uniform + * + * Gets the value of the uniform @idx in the @uniform_data block. + * The uniform must be of uint type. + * + * Returns: The value + */ +guint32 +gsk_gl_shader_get_uniform_data_uint (GskGLShader *shader, + GBytes *uniform_data, + int idx) +{ + const GskGLUniform *u; + const guchar *args_src; + gsize size; + const guchar *data = g_bytes_get_data (uniform_data, &size); + + g_assert (size == shader->uniforms_size); + g_assert (idx < shader->uniforms->len); + u = &g_array_index (shader->uniforms, GskGLUniform, idx); + g_assert (u->type == GSK_GLUNIFORM_TYPE_UINT); + + args_src = data + u->offset; + return *(guint32 *)args_src; +} + +/** + * gsk_gl_shader_get_uniform_data_bool: + * @shader: A #GskGLShader + * @uniform_data: The uniform data block + * @idx: The index of the uniform + * + * Gets the value of the uniform @idx in the @uniform_data block. + * The uniform must be of bool type. + * + * Returns: The value + */ +gboolean +gsk_gl_shader_get_uniform_data_bool (GskGLShader *shader, + GBytes *uniform_data, + int idx) +{ + const GskGLUniform *u; + const guchar *args_src; + gsize size; + const guchar *data = g_bytes_get_data (uniform_data, &size); + + g_assert (size == shader->uniforms_size); + g_assert (idx < shader->uniforms->len); + u = &g_array_index (shader->uniforms, GskGLUniform, idx); + g_assert (u->type == GSK_GLUNIFORM_TYPE_BOOL); + + args_src = data + u->offset; + return *(guint32 *)args_src; +} + +/** + * gsk_gl_shader_get_uniform_data_vec2: + * @shader: A #GskGLShader + * @uniform_data: The uniform data block + * @idx: The index of the uniform + * @out_value: Location to store set the uniform value too + * + * Gets the value of the uniform @idx in the @uniform_data block. + * The uniform must be of vec2 type. + */ +void +gsk_gl_shader_get_uniform_data_vec2 (GskGLShader *shader, + GBytes *uniform_data, + int idx, + graphene_vec2_t *out_value) +{ + const GskGLUniform *u; + const guchar *args_src; + gsize size; + const guchar *data = g_bytes_get_data (uniform_data, &size); + + g_assert (size == shader->uniforms_size); + g_assert (idx < shader->uniforms->len); + u = &g_array_index (shader->uniforms, GskGLUniform, idx); + g_assert (u->type == GSK_GLUNIFORM_TYPE_FLOAT); + + args_src = data + u->offset; + graphene_vec2_init_from_float (out_value, (float *)args_src); +} + +/** + * gsk_gl_shader_get_uniform_data_vec3: + * @shader: A #GskGLShader + * @uniform_data: The uniform data block + * @idx: The index of the uniform + * @out_value: Location to store set the uniform value too + * + * Gets the value of the uniform @idx in the @uniform_data block. + * The uniform must be of vec3 type. + */ +void +gsk_gl_shader_get_uniform_data_vec3 (GskGLShader *shader, + GBytes *uniform_data, + int idx, + graphene_vec3_t *out_value) +{ + const GskGLUniform *u; + const guchar *args_src; + gsize size; + const guchar *data = g_bytes_get_data (uniform_data, &size); + + g_assert (size == shader->uniforms_size); + g_assert (idx < shader->uniforms->len); + u = &g_array_index (shader->uniforms, GskGLUniform, idx); + g_assert (u->type == GSK_GLUNIFORM_TYPE_FLOAT); + + args_src = data + u->offset; + graphene_vec3_init_from_float (out_value, (float *)args_src); +} + +/** + * gsk_gl_shader_get_uniform_data_vec4: + * @shader: A #GskGLShader + * @uniform_data: The uniform data block + * @idx: The index of the uniform + * @out_value: Location to store set the uniform value too + * + * Gets the value of the uniform @idx in the @uniform_data block. + * The uniform must be of vec4 type. + */ +void +gsk_gl_shader_get_uniform_data_vec4 (GskGLShader *shader, + GBytes *uniform_data, + int idx, + graphene_vec4_t *out_value) +{ + const GskGLUniform *u; + const guchar *args_src; + gsize size; + const guchar *data = g_bytes_get_data (uniform_data, &size); + + g_assert (size == shader->uniforms_size); + g_assert (idx < shader->uniforms->len); + u = &g_array_index (shader->uniforms, GskGLUniform, idx); + g_assert (u->type == GSK_GLUNIFORM_TYPE_FLOAT); + + args_src = data + u->offset; + graphene_vec4_init_from_float (out_value, (float *)args_src); +} + +/** + * gsk_gl_shader_format_uniform_data_va: + * @shader: A #GskGLShader + * @uniforms: Name-Value pairs for the uniforms of @shader, ending with a %NULL name, all values are passed by reference. + * + * Formats the uniform data as needed for feeding the named uniforms values into the shader. + * The argument list is a list of pairs of names, and pointers to data of the types + * that match the declared uniforms (i.e. `float *` for float uniforms and `graphene_vec4_t *` f + * or vec3 uniforms). + * + * Returns: (transfer full): A newly allocated block of data which can be passed to gsk_gl_shader_node_new(). + */ +GBytes * +gsk_gl_shader_format_uniform_data_va (GskGLShader *shader, + va_list uniforms) +{ + guchar *args = g_malloc0 (shader->uniforms_size); + const char *name; + + while ((name = va_arg (uniforms, const char *)) != NULL) + { + const GskGLUniform *u; + gpointer value = va_arg (uniforms, gpointer); + guchar *args_dest; + + u = gsk_gl_shader_find_uniform (shader, name); + if (u == NULL) + { + /* This isn't really an error, because we can easily imaging + a shader interface that have input which isn't needed for + a particular shader */ + g_debug ("No uniform named `%s` in shader", name); + continue; + } + + args_dest = args + u->offset; + + /* We use pointers-to-value so that all values are the same + size, otherwise we couldn't handle the missing uniform case above */ + + switch (u->type) + { + case GSK_GLUNIFORM_TYPE_FLOAT: + *(float *)args_dest = *(float *)value; + break; + + case GSK_GLUNIFORM_TYPE_INT: + *(gint32 *)args_dest = *(gint32 *)value; + break; + + case GSK_GLUNIFORM_TYPE_UINT: + *(guint32 *)args_dest = *(guint32 *)value; + break; + + case GSK_GLUNIFORM_TYPE_BOOL: + *(guint32 *)args_dest = *(gboolean *)value; + break; + + case GSK_GLUNIFORM_TYPE_VEC2: + graphene_vec2_to_float ((const graphene_vec2_t *)value, + (float *)args_dest); + break; + + case GSK_GLUNIFORM_TYPE_VEC3: + graphene_vec3_to_float ((const graphene_vec3_t *)value, + (float *)args_dest); + break; + + case GSK_GLUNIFORM_TYPE_VEC4: + graphene_vec4_to_float ((const graphene_vec4_t *)value, + (float *)args_dest); + break; + + case GSK_GLUNIFORM_TYPE_NONE: + default: + g_assert_not_reached (); + } + } + + return g_bytes_new_take (args, shader->uniforms_size); +} + +struct _GskUniformDataBuilder { + GskGLShader *shader; + guchar *data; +}; + +G_DEFINE_BOXED_TYPE (GskUniformDataBuilder, gsk_uniform_data_builder, + gsk_uniform_data_builder_copy, + gsk_uniform_data_builder_free); + + +/** + * gsk_gl_shader_build_uniform_data: + * @shader: A #GskGLShader + * + * Allocates a builder that can be used to construct a new uniform data + * chunk. + * + * Returns: (transfer full): The newly allocated builder, free with gsk_uniform_data_builder_free() + */ +GskUniformDataBuilder * +gsk_gl_shader_build_uniform_data (GskGLShader *shader) +{ + GskUniformDataBuilder *builder = g_new0 (GskUniformDataBuilder, 1); + builder->shader = g_object_ref (shader); + builder->data = g_malloc0 (shader->uniforms_size); + + return builder; +} + +/** + * gsk_uniform_data_builder_finish: + * @builder: A #GskUniformDataBuilder + * + * Finishes building the uniform data and returns it as a GBytes. Once this + * is called the builder can not be used anymore. + * + * Returns: (transfer full): The newly allocated builder, free with gsk_uniform_data_builder_free() + */ +GBytes * +gsk_uniform_data_builder_finish (GskUniformDataBuilder *builder) +{ + return g_bytes_new_take (g_steal_pointer (&builder->data), + builder->shader->uniforms_size); +} + +/** + * gsk_uniform_data_builder_free: + * @builder: A #GskUniformDataBuilder + * + * Frees the builder. + */ +void +gsk_uniform_data_builder_free (GskUniformDataBuilder *builder) +{ + g_object_unref (builder->shader); + g_free (builder->data); + g_free (builder); +} + +/** + * gsk_uniform_data_builder_copy: + * @builder: A #GskUniformDataBuilder + * + * Makes a copy of the builder. + * + * Returns: (transfer full): A copy of the builder, free with gsk_uniform_data_builder_free(). + */ +GskUniformDataBuilder * +gsk_uniform_data_builder_copy (GskUniformDataBuilder *builder) +{ + GskUniformDataBuilder *new = g_new0 (GskUniformDataBuilder, 1); + new->data = g_memdup (builder->data, builder->shader->uniforms_size); + new->shader = g_object_ref (builder->shader); + return new; +} + +/** + * gsk_uniform_data_builder_set_float: + * @builder: A #GskUniformDataBuilder + * @idx: The index of the uniform + * @value: The value to set the uniform too + * + * Sets the value of the uniform @idx. + * The uniform must be of float type. + */ +void +gsk_uniform_data_builder_set_float (GskUniformDataBuilder *builder, + int idx, + float value) +{ + GskGLShader *shader = builder->shader; + const GskGLUniform *u; + guchar *args_dest; + + g_assert (idx < shader->uniforms->len); + u = &g_array_index (shader->uniforms, GskGLUniform, idx); + g_assert (u->type == GSK_GLUNIFORM_TYPE_FLOAT); + + args_dest = builder->data + u->offset; + *(float *)args_dest = value; +} + +/** + * gsk_uniform_data_builder_set_int: + * @builder: A #GskUniformDataBuilder + * @idx: The index of the uniform + * @value: The value to set the uniform too + * + * Sets the value of the uniform @idx. + * The uniform must be of int type. + */ +void +gsk_uniform_data_builder_set_int (GskUniformDataBuilder *builder, + int idx, + gint32 value) +{ + GskGLShader *shader = builder->shader; + const GskGLUniform *u; + guchar *args_dest; + + g_assert (idx < shader->uniforms->len); + u = &g_array_index (shader->uniforms, GskGLUniform, idx); + g_assert (u->type == GSK_GLUNIFORM_TYPE_INT); + + args_dest = builder->data + u->offset; + *(gint32 *)args_dest = value; +} + +/** + * gsk_uniform_data_builder_set_uint: + * @builder: A #GskUniformDataBuilder + * @idx: The index of the uniform + * @value: The value to set the uniform too + * + * Sets the value of the uniform @idx. + * The uniform must be of uint type. + */ +void +gsk_uniform_data_builder_set_uint (GskUniformDataBuilder *builder, + int idx, + guint32 value) +{ + GskGLShader *shader = builder->shader; + const GskGLUniform *u; + guchar *args_dest; + + g_assert (idx < shader->uniforms->len); + u = &g_array_index (shader->uniforms, GskGLUniform, idx); + g_assert (u->type == GSK_GLUNIFORM_TYPE_UINT); + + args_dest = builder->data + u->offset; + *(guint32 *)args_dest = value; +} + +/** + * gsk_uniform_data_builder_set_bool: + * @builder: A #GskUniformDataBuilder + * @idx: The index of the uniform + * @value: The value to set the uniform too + * + * Sets the value of the uniform @idx. + * The uniform must be of bool type. + */ +void +gsk_uniform_data_builder_set_bool (GskUniformDataBuilder *builder, + int idx, + gboolean value) +{ + GskGLShader *shader = builder->shader; + const GskGLUniform *u; + guchar *args_dest; + + g_assert (idx < shader->uniforms->len); + u = &g_array_index (shader->uniforms, GskGLUniform, idx); + g_assert (u->type == GSK_GLUNIFORM_TYPE_BOOL); + + args_dest = builder->data + u->offset; + *(guint32 *)args_dest = !!value; +} + +/** + * gsk_uniform_data_builder_set_vec2: + * @builder: A #GskUniformDataBuilder + * @idx: The index of the uniform + * @value: The value to set the uniform too + * + * Sets the value of the uniform @idx. + * The uniform must be of vec2 type. + */ +void +gsk_uniform_data_builder_set_vec2 (GskUniformDataBuilder *builder, + int idx, + const graphene_vec2_t *value) +{ + GskGLShader *shader = builder->shader; + const GskGLUniform *u; + guchar *args_dest; + + g_assert (idx < shader->uniforms->len); + u = &g_array_index (shader->uniforms, GskGLUniform, idx); + g_assert (u->type == GSK_GLUNIFORM_TYPE_FLOAT); + + args_dest = builder->data + u->offset; + graphene_vec2_to_float (value, (float *)args_dest); +} + +/** + * gsk_uniform_data_builder_set_vec3: + * @builder: A #GskUniformDataBuilder + * @idx: The index of the uniform + * @value: The value to set the uniform too + * + * Sets the value of the uniform @idx. + * The uniform must be of vec3 type. + */ +void +gsk_uniform_data_builder_set_vec3 (GskUniformDataBuilder *builder, + int idx, + const graphene_vec3_t *value) +{ + GskGLShader *shader = builder->shader; + const GskGLUniform *u; + guchar *args_dest; + + g_assert (idx < shader->uniforms->len); + u = &g_array_index (shader->uniforms, GskGLUniform, idx); + g_assert (u->type == GSK_GLUNIFORM_TYPE_FLOAT); + + args_dest = builder->data + u->offset; + graphene_vec3_to_float (value, (float *)args_dest); +} + +/** + * gsk_uniform_data_builder_set_vec4: + * @builder: A #GskUniformDataBuilder + * @idx: The index of the uniform + * @value: The value to set the uniform too + * + * Sets the value of the uniform @idx. + * The uniform must be of vec4 type. + */ +void +gsk_uniform_data_builder_set_vec4 (GskUniformDataBuilder *builder, + int idx, + const graphene_vec4_t *value) +{ + GskGLShader *shader = builder->shader; + const GskGLUniform *u; + guchar *args_dest; + + g_assert (idx < shader->uniforms->len); + u = &g_array_index (shader->uniforms, GskGLUniform, idx); + g_assert (u->type == GSK_GLUNIFORM_TYPE_FLOAT); + + args_dest = builder->data + u->offset; + graphene_vec4_to_float (value, (float *)args_dest); +} diff --git a/gsk/gskglshader.h b/gsk/gskglshader.h new file mode 100644 index 0000000000..26f21ee694 --- /dev/null +++ b/gsk/gskglshader.h @@ -0,0 +1,153 @@ +/* GSK - The GTK Scene Kit + * + * Copyright 2020 Red Hat Inc + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#ifndef __GSK_GL_SHADER_H__ +#define __GSK_GL_SHADER_H__ + +#if !defined (__GSK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +#include +#include + +G_BEGIN_DECLS + +#define GSK_TYPE_GLSHADER (gsk_gl_shader_get_type ()) + +GDK_AVAILABLE_IN_ALL +G_DECLARE_FINAL_TYPE (GskGLShader, gsk_gl_shader, GSK, GL_SHADER, GObject) + +GDK_AVAILABLE_IN_ALL +GskGLShader * gsk_gl_shader_new_from_bytes (GBytes *sourcecode); +GDK_AVAILABLE_IN_ALL +GskGLShader * gsk_gl_shader_new_from_resource (const char *resource_path); +GDK_AVAILABLE_IN_ALL +GBytes * gsk_gl_shader_get_bytes (GskGLShader *shader); +GDK_AVAILABLE_IN_ALL +int gsk_gl_shader_get_n_required_textures (GskGLShader *shader); +GDK_AVAILABLE_IN_ALL +int gsk_gl_shader_get_n_uniforms (GskGLShader *shader); +GDK_AVAILABLE_IN_ALL +const char * gsk_gl_shader_get_uniform_name (GskGLShader *shader, + int idx); +GDK_AVAILABLE_IN_ALL +int gsk_gl_shader_find_uniform_by_name (GskGLShader *shader, + const char *name); +GDK_AVAILABLE_IN_ALL +GskGLUniformType gsk_gl_shader_get_uniform_type (GskGLShader *shader, + int idx); +GDK_AVAILABLE_IN_ALL +int gsk_gl_shader_get_uniform_offset (GskGLShader *shader, + int idx); +GDK_AVAILABLE_IN_ALL +int gsk_gl_shader_get_uniforms_size (GskGLShader *shader); + + +/* Helpers for managing uniform data */ + +GDK_AVAILABLE_IN_ALL +float gsk_gl_shader_get_uniform_data_float (GskGLShader *shader, + GBytes *uniform_data, + int idx); +GDK_AVAILABLE_IN_ALL +gint32 gsk_gl_shader_get_uniform_data_int (GskGLShader *shader, + GBytes *uniform_data, + int idx); +GDK_AVAILABLE_IN_ALL +guint32 gsk_gl_shader_get_uniform_data_uint (GskGLShader *shader, + GBytes *uniform_data, + int idx); +GDK_AVAILABLE_IN_ALL +gboolean gsk_gl_shader_get_uniform_data_bool (GskGLShader *shader, + GBytes *uniform_data, + int idx); +GDK_AVAILABLE_IN_ALL +void gsk_gl_shader_get_uniform_data_vec2 (GskGLShader *shader, + GBytes *uniform_data, + int idx, + graphene_vec2_t *out_value); +GDK_AVAILABLE_IN_ALL +void gsk_gl_shader_get_uniform_data_vec3 (GskGLShader *shader, + GBytes *uniform_data, + int idx, + graphene_vec3_t *out_value); +GDK_AVAILABLE_IN_ALL +void gsk_gl_shader_get_uniform_data_vec4 (GskGLShader *shader, + GBytes *uniform_data, + int idx, + graphene_vec4_t *out_value); +GDK_AVAILABLE_IN_ALL +GBytes * gsk_gl_shader_format_uniform_data_va (GskGLShader *shader, + va_list uniforms); + +typedef struct _GskUniformDataBuilder GskUniformDataBuilder; + +GDK_AVAILABLE_IN_ALL +GskUniformDataBuilder *gsk_gl_shader_build_uniform_data (GskGLShader *shader); + + +#define GSK_TYPE_UNIFORM_DATA_BUILDER (gsk_uniform_data_builder_get_type ()) + +GDK_AVAILABLE_IN_ALL +GType gsk_uniform_data_builder_get_type (void) G_GNUC_CONST; + +GDK_AVAILABLE_IN_ALL +GBytes * gsk_uniform_data_builder_finish (GskUniformDataBuilder *builder); +GDK_AVAILABLE_IN_ALL +void gsk_uniform_data_builder_free (GskUniformDataBuilder *builder); +GDK_AVAILABLE_IN_ALL +GskUniformDataBuilder *gsk_uniform_data_builder_copy (GskUniformDataBuilder *builder); + +GDK_AVAILABLE_IN_ALL +void gsk_uniform_data_builder_set_float (GskUniformDataBuilder *builder, + int idx, + float value); +GDK_AVAILABLE_IN_ALL +void gsk_uniform_data_builder_set_int (GskUniformDataBuilder *builder, + int idx, + gint32 value); +GDK_AVAILABLE_IN_ALL +void gsk_uniform_data_builder_set_uint (GskUniformDataBuilder *builder, + int idx, + guint32 value); +GDK_AVAILABLE_IN_ALL +void gsk_uniform_data_builder_set_bool (GskUniformDataBuilder *builder, + int idx, + gboolean value); +GDK_AVAILABLE_IN_ALL +void gsk_uniform_data_builder_set_vec2 (GskUniformDataBuilder *builder, + int idx, + const graphene_vec2_t *value); +GDK_AVAILABLE_IN_ALL +void gsk_uniform_data_builder_set_vec3 (GskUniformDataBuilder *builder, + int idx, + const graphene_vec3_t *value); +GDK_AVAILABLE_IN_ALL +void gsk_uniform_data_builder_set_vec4 (GskUniformDataBuilder *builder, + int idx, + const graphene_vec4_t *value); + + + + +G_END_DECLS + +#endif /* __GSK_GL_SHADER_H__ */ diff --git a/gsk/gskglshaderprivate.h b/gsk/gskglshaderprivate.h new file mode 100644 index 0000000000..3dc82c35dc --- /dev/null +++ b/gsk/gskglshaderprivate.h @@ -0,0 +1,19 @@ +#ifndef __GSK_GLSHADER_PRIVATE_H__ +#define __GSK_GLSHADER_PRIVATE_H__ + +#include + +G_BEGIN_DECLS + +typedef struct { + char *name; + GskGLUniformType type; + gsize offset; +} GskGLUniform; + +const GskGLUniform *gsk_gl_shader_get_uniforms (GskGLShader *shader, + int *n_uniforms); + +G_END_DECLS + +#endif /* __GSK_GLSHADER_PRIVATE_H__ */ diff --git a/gsk/gskrendernode.h b/gsk/gskrendernode.h index 424039753a..f554ef56a5 100644 --- a/gsk/gskrendernode.h +++ b/gsk/gskrendernode.h @@ -25,6 +25,7 @@ #include #include +#include #include G_BEGIN_DECLS @@ -122,6 +123,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_gl_shader_node_get_type()) typedef struct _GskDebugNode GskDebugNode; typedef struct _GskColorNode GskColorNode; @@ -146,6 +148,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 +454,28 @@ 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_gl_shader_node_get_type (void) G_GNUC_CONST; +GDK_AVAILABLE_IN_ALL +GskRenderNode * gsk_gl_shader_node_new (GskGLShader *shader, + const graphene_rect_t *bounds, + GBytes *uniform_data, + GskRenderNode *fallback, + GskRenderNode **children, + int n_children); +GDK_AVAILABLE_IN_ALL +GskRenderNode * gsk_gl_shader_node_get_fallback_child (GskRenderNode *node); +GDK_AVAILABLE_IN_ALL +guint gsk_gl_shader_node_get_n_children (GskRenderNode *node); +GDK_AVAILABLE_IN_ALL +GskRenderNode * gsk_gl_shader_node_get_child (GskRenderNode *node, + int idx); +GDK_AVAILABLE_IN_ALL +GBytes * gsk_gl_shader_node_get_uniform_data (GskRenderNode *node); +GDK_AVAILABLE_IN_ALL +GskGLShader * gsk_gl_shader_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..8b0ba45350 100644 --- a/gsk/gskrendernodeimpl.c +++ b/gsk/gskrendernodeimpl.c @@ -4470,6 +4470,229 @@ gsk_debug_node_get_message (GskRenderNode *node) return self->message; } +/*** GSK_GL_SHADER_NODE ***/ + +struct _GskGLShaderNode +{ + GskRenderNode render_node; + + GskGLShader *shader; + GBytes *uniform_data; + GskRenderNode *fallback; + GskRenderNode **children; + guint n_children; +}; + +static void +gsk_gl_shader_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_bytes_unref (self->uniform_data); + + g_object_unref (self->shader); + + parent_class->finalize (node); +} + +static void +gsk_gl_shader_node_draw (GskRenderNode *node, + cairo_t *cr) +{ + GskGLShaderNode *self = (GskGLShaderNode *) node; + + gsk_render_node_draw (self->fallback, cr); +} + +static void +gsk_gl_shader_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 && + g_bytes_compare (self1->uniform_data, self2->uniform_data) == 0 && + 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_gl_shader_node_new: + * @shader: the #GskGLShader + * @bounds: the rectangle to render the shader into + * @uniform_data: Data for the uniforms + * @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. + * The @uniform_data is a block of data to use for uniform input, as per types and offsets + * defined by the @shader. Normally this is generated by gsk_gl_shader_format_uniform_data_va(). + * + * See #GskGLShader for details about how the shader should be written. + * + * All the children will be rendered into textures, if they aren't already #GskTextureNode:s + * then they will be used directly. These textures will be sent as input to the shader. + * + * If the backend doesn't support GL shaders, or if there is any problem when compiling + * the shader, then the fallback shader node will be used instead. + * + * Returns: (transfer full) (type GskGLShaderNode): A new #GskRenderNode + */ +GskRenderNode * +gsk_gl_shader_node_new (GskGLShader *shader, + const graphene_rect_t *bounds, + GBytes *uniform_data, + GskRenderNode *fallback, + GskRenderNode **children, + int n_children) +{ + GskGLShaderNode *self; + GskRenderNode *node; + int uniforms_size; + + g_return_val_if_fail (bounds != NULL, NULL); + + self = gsk_render_node_alloc (GSK_GL_SHADER_NODE); + node = (GskRenderNode *) self; + + graphene_rect_init_from_rect (&node->bounds, bounds); + self->shader = g_object_ref (shader); + + uniforms_size = gsk_gl_shader_get_uniforms_size (shader); + g_assert (g_bytes_get_size (uniform_data) == uniforms_size); + + self->uniform_data = g_bytes_ref (uniform_data); + 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_gl_shader_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_gl_shader_node_get_fallback_child (GskRenderNode *node) +{ + GskGLShaderNode *self = (GskGLShaderNode *) node; + + g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_GL_SHADER_NODE), NULL); + + return self->fallback; +} + +/** + * gsk_gl_shader_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_gl_shader_node_get_n_children (GskRenderNode *node) +{ + GskGLShaderNode *self = (GskGLShaderNode *) node; + + g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_GL_SHADER_NODE), 0); + + return self->n_children; +} + +/** + * gsk_gl_shader_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_gl_shader_node_get_child (GskRenderNode *node, + int idx) +{ + GskGLShaderNode *self = (GskGLShaderNode *) node; + + g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_GL_SHADER_NODE), NULL); + g_return_val_if_fail (idx < self->n_children, NULL); + + return self->children[idx]; +} + +/** + * gsk_gl_shader_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_gl_shader_node_get_shader (GskRenderNode *node) +{ + GskGLShaderNode *self = (GskGLShaderNode *) node; + + g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_GL_SHADER_NODE), 0); + + return self->shader; +} + +/** + * gsk_gl_shader_node_get_uniform_data: + * @node: (type GskGLShaderNode): a #GskRenderNode for a gl shader + * + * Gets args for the node. + * + * Returns: (transfer none): A #GBytes with the uniform data + */ +GBytes * +gsk_gl_shader_node_get_uniform_data (GskRenderNode *node) +{ + GskGLShaderNode *self = (GskGLShaderNode *) node; + + g_return_val_if_fail (GSK_IS_RENDER_NODE_TYPE (node, GSK_GL_SHADER_NODE), NULL); + + return self->uniform_data; +} + GType gsk_render_node_types[GSK_RENDER_NODE_TYPE_N_TYPES]; #ifndef I_ @@ -4506,6 +4729,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_gl_shader_node, GSK_GL_SHADER_NODE) GSK_DEFINE_RENDER_NODE_TYPE (gsk_debug_node, GSK_DEBUG_NODE) static void @@ -4863,6 +5087,22 @@ gsk_render_node_init_types_once (void) gsk_render_node_types[GSK_BLUR_NODE] = node_type; } + { + const GskRenderNodeTypeInfo node_info = + { + GSK_GL_SHADER_NODE, + sizeof (GskGLShaderNode), + NULL, + gsk_gl_shader_node_finalize, + gsk_gl_shader_node_draw, + NULL, + gsk_gl_shader_node_diff, + }; + + GType node_type = gsk_render_node_type_register_static (I_("GskGLShaderNode"), &node_info); + gsk_render_node_types[GSK_GL_SHADER_NODE] = node_type; + } + { const GskRenderNodeTypeInfo node_info = { diff --git a/gsk/gskrendernodeparser.c b/gsk/gskrendernodeparser.c index 870ce8818b..ca447e4a8d 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,235 @@ 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; + GArray *uniform_values; +} ShaderInfo; + +static void +clear_shader_info (gpointer data) +{ + ShaderInfo *info = data; + g_array_set_size (info->uniform_values, 0); +} + +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, + GskGLUniformType uniform_type, + UniformValue *value) +{ + switch (uniform_type) + { + case GSK_GLUNIFORM_TYPE_FLOAT: + if (!gtk_css_parser_consume_number (parser, &value->v[0])) + return FALSE; + break; + + case GSK_GLUNIFORM_TYPE_INT: + if (!gtk_css_parser_consume_integer (parser, &value->i)) + return FALSE; + break; + + case GSK_GLUNIFORM_TYPE_UINT: + if (!gtk_css_parser_consume_integer (parser, &value->i) || + value->i < 0) + return FALSE; + break; + + case GSK_GLUNIFORM_TYPE_BOOL: + if (!gtk_css_parser_consume_integer (parser, &value->i) || + (value->i != 0 && value->i != 1)) + return FALSE; + break; + + case GSK_GLUNIFORM_TYPE_VEC2: + if (!gtk_css_parser_consume_number (parser, &value->v[0]) || + !gtk_css_parser_consume_number (parser, &value->v[1])) + return FALSE; + break; + + case GSK_GLUNIFORM_TYPE_VEC3: + if (!gtk_css_parser_consume_number (parser, &value->v[0]) || + !gtk_css_parser_consume_number (parser, &value->v[1]) || + !gtk_css_parser_consume_number (parser, &value->v[2])) + return FALSE; + break; + + case GSK_GLUNIFORM_TYPE_VEC4: + if (!gtk_css_parser_consume_number (parser, &value->v[0]) || + !gtk_css_parser_consume_number (parser, &value->v[1]) || + !gtk_css_parser_consume_number (parser, &value->v[2]) || + !gtk_css_parser_consume_number (parser, &value->v[3])) + return FALSE; + break; + + case GSK_GLUNIFORM_TYPE_NONE: + default: + g_assert_not_reached (); + break; + } + + gtk_css_parser_try_token (parser, GTK_CSS_TOKEN_COMMA); + + return TRUE; +} + +static gboolean +parse_uniform_data (GtkCssParser *parser, gpointer data) +{ + ShaderInfo *shader_info = data; + int n_uniforms = gsk_gl_shader_get_n_uniforms (shader_info->shader); + int i; + + g_array_set_size (shader_info->uniform_values, n_uniforms); + + for (i = 0; i < n_uniforms; i++) + { + GskGLUniformType uniform_type = gsk_gl_shader_get_uniform_type (shader_info->shader, i); + UniformValue *uniform_value = &g_array_index (shader_info->uniform_values, UniformValue, i); + + if (!parse_uniform_value (parser, uniform_type, uniform_value)) + return FALSE; + } + + return TRUE; +} + +static GskRenderNode * +parse_glshader_node (GtkCssParser *parser) +{ + graphene_rect_t bounds = GRAPHENE_RECT_INIT (0, 0, 50, 50); + GskRenderNode *fallback = NULL; + GskRenderNode *child[4] = { NULL, }; + ShaderInfo shader_info = { + NULL, + g_array_new (FALSE, FALSE, sizeof (UniformValue)) + }; + const Declaration declarations[] = { + { "bounds", parse_rect, NULL, &bounds }, + { "sourcecode", parse_shader, NULL, &shader_info }, + { "uniform-data", parse_uniform_data, clear_shader_info, &shader_info }, + { "fallback", parse_node, clear_node, &fallback }, + { "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; + GskUniformDataBuilder *builder; + GBytes *uniform_data = 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; + + builder = gsk_gl_shader_build_uniform_data (shader); + for (i = 0; i < shader_info.uniform_values->len; i++) + { + GskGLUniformType uniform_type = gsk_gl_shader_get_uniform_type (shader, i); + UniformValue *value = &g_array_index (shader_info.uniform_values, UniformValue, i); + + switch (uniform_type) + { + case GSK_GLUNIFORM_TYPE_FLOAT: + gsk_uniform_data_builder_set_float (builder, i, value->v[0]); + break; + + case GSK_GLUNIFORM_TYPE_INT: + gsk_uniform_data_builder_set_int (builder, i, value->i); + break; + + case GSK_GLUNIFORM_TYPE_UINT: + gsk_uniform_data_builder_set_uint (builder, i, value->i); + break; + + case GSK_GLUNIFORM_TYPE_BOOL: + gsk_uniform_data_builder_set_bool (builder, i, value->i); + break; + + case GSK_GLUNIFORM_TYPE_VEC2: + { + graphene_vec2_t v; + graphene_vec2_init (&v, value->v[0], value->v[1]); + gsk_uniform_data_builder_set_vec2 (builder, i, &v); + } + break; + + case GSK_GLUNIFORM_TYPE_VEC3: + { + graphene_vec3_t v; + graphene_vec3_init (&v, value->v[0], value->v[1], value->v[2]); + gsk_uniform_data_builder_set_vec3 (builder, i, &v); + } + break; + + case GSK_GLUNIFORM_TYPE_VEC4: + { + graphene_vec4_t v; + graphene_vec4_init (&v, value->v[0], value->v[1], value->v[2], value->v[3]); + gsk_uniform_data_builder_set_vec4 (builder, i, &v); + } + break; + + case GSK_GLUNIFORM_TYPE_NONE: + default: + g_assert_not_reached (); + } + } + + uniform_data = gsk_uniform_data_builder_finish (builder); + gsk_uniform_data_builder_free (builder); + + node = gsk_gl_shader_node_new (shader, &bounds, uniform_data, + fallback, child, len); + + g_array_unref (shader_info.uniform_values); + g_bytes_unref (uniform_data); + 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 +1832,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 +2067,51 @@ append_point (GString *str, string_append_double (str, p->y); } +static void +append_string (GString *str, + const char *string) +{ + gsize len; + + g_return_if_fail (str != NULL); + g_return_if_fail (string != NULL); + + g_string_append_c (str, '"'); + + do { + len = strcspn (string, "\\\"\n\r\f"); + g_string_append_len (str, string, len); + string += len; + switch (*string) + { + case '\0': + goto out; + case '\n': + g_string_append (str, "\\A "); + break; + case '\r': + g_string_append (str, "\\D "); + break; + case '\f': + g_string_append (str, "\\C "); + break; + case '\"': + g_string_append (str, "\\\""); + break; + case '\\': + g_string_append (str, "\\\\"); + break; + default: + g_assert_not_reached (); + break; + } + string++; + } while (*string); + +out: + g_string_append_c (str, '"'); +} + static void append_vec4 (GString *str, const graphene_vec4_t *v) @@ -1914,6 +2189,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 +2728,125 @@ render_node_print (Printer *p, } break; + case GSK_GL_SHADER_NODE: + { + GskGLShader *shader = gsk_gl_shader_node_get_shader (node); + GBytes *uniform_data = gsk_gl_shader_node_get_uniform_data (node); + + start_node (p, "glshader"); + + append_rect_param (p, "bounds", &node->bounds); + + GBytes *bytes = gsk_gl_shader_get_bytes (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_GLUNIFORM_TYPE_NONE: + default: + g_assert_not_reached (); + break; + + case GSK_GLUNIFORM_TYPE_FLOAT: + { + float value = gsk_gl_shader_get_uniform_data_float (shader, uniform_data, i); + string_append_double (data, value); + } + break; + + case GSK_GLUNIFORM_TYPE_INT: + { + gint32 value = gsk_gl_shader_get_uniform_data_int (shader, uniform_data, i); + g_string_append_printf (data, "%d", value); + } + break; + + case GSK_GLUNIFORM_TYPE_UINT: + { + guint32 value = gsk_gl_shader_get_uniform_data_uint (shader, uniform_data, i); + g_string_append_printf (data, "%u", value); + } + break; + + case GSK_GLUNIFORM_TYPE_BOOL: + { + gboolean value = gsk_gl_shader_get_uniform_data_bool (shader, uniform_data, i); + g_string_append_printf (data, "%d", value); + } + break; + + case GSK_GLUNIFORM_TYPE_VEC2: + { + graphene_vec2_t value; + gsk_gl_shader_get_uniform_data_vec2 (shader, uniform_data, 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_GLUNIFORM_TYPE_VEC3: + { + graphene_vec3_t value; + gsk_gl_shader_get_uniform_data_vec3 (shader, uniform_data, 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_GLUNIFORM_TYPE_VEC4: + { + graphene_vec4_t value; + gsk_gl_shader_get_uniform_data_vec4 (shader, uniform_data, 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, "uniform-data: %s;\n", data->str); + g_string_free (data, TRUE); + } + + append_node_param (p, "fallback", gsk_gl_shader_node_get_fallback_child (node)); + 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); diff --git a/gsk/gskrendernodeprivate.h b/gsk/gskrendernodeprivate.h index d75a2c68b1..7e11a14b8f 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_GL_SHADER_NODE + 1) extern GType gsk_render_node_types[]; diff --git a/gsk/meson.build b/gsk/meson.build index c3567d78ed..b52d379eb9 100644 --- a/gsk/meson.build +++ b/gsk/meson.build @@ -21,6 +21,7 @@ gsk_private_gl_shaders = [ gsk_public_sources = files([ 'gskdiff.c', 'gskcairorenderer.c', + 'gskglshader.c', 'gskrenderer.c', 'gskrendernode.c', 'gskrendernodeimpl.c', @@ -52,6 +53,7 @@ gsk_private_sources = files([ gsk_public_headers = files([ 'gskcairorenderer.h', 'gskenums.h', + 'gskglshader.h', 'gskrenderer.h', 'gskrendernode.h', 'gskroundedrect.h', diff --git a/gtk/inspector/recorder.c b/gtk/inspector/recorder.c index 8b42de031e..59747d625a 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_GL_SHADER_NODE: + { + GListStore *store = G_LIST_STORE (create_render_node_list_model ((GskRenderNode *[1]) { gsk_gl_shader_node_get_fallback_child (node) }, 1)); + + for (guint i = 0; i < gsk_gl_shader_node_get_n_children (node); i++) + { + GskRenderNode *child = gsk_gl_shader_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_GL_SHADER_NODE: + return "GL Shader"; } } @@ -301,6 +322,7 @@ node_name (GskRenderNode *node) case GSK_CROSS_FADE_NODE: case GSK_TEXT_NODE: case GSK_BLUR_NODE: + case GSK_GL_SHADER_NODE: return g_strdup (node_type_name (gsk_render_node_get_node_type (node))); case GSK_DEBUG_NODE: @@ -511,6 +533,34 @@ add_color_row (GtkListStore *store, g_object_unref (texture); } +static void +add_int_row (GtkListStore *store, + const char *name, + int value) +{ + char *text = g_strdup_printf ("%d", value); + add_text_row (store, name, text); + g_free (text); +} + +static void +add_uint_row (GtkListStore *store, + const char *name, + guint value) +{ + char *text = g_strdup_printf ("%u", value); + add_text_row (store, name, text); + g_free (text); +} + +static void +add_boolean_row (GtkListStore *store, + const char *name, + gboolean value) +{ + add_text_row (store, name, value ? "TRUE" : "FALSE"); +} + static void add_float_row (GtkListStore *store, const char *name, @@ -759,6 +809,92 @@ populate_render_node_properties (GtkListStore *store, add_float_row (store, "Radius", gsk_blur_node_get_radius (node)); break; + case GSK_GL_SHADER_NODE: + { + GskGLShader *shader = gsk_gl_shader_node_get_shader (node); + GBytes *uniform_data = gsk_gl_shader_node_get_uniform_data (node); + int i; + + add_int_row (store, "Required textures", gsk_gl_shader_get_n_required_textures (shader)); + for (i = 0; i < gsk_gl_shader_get_n_uniforms (shader); i++) + { + const char *name; + char *title; + + name = gsk_gl_shader_get_uniform_name (shader, i); + title = g_strdup_printf ("Uniform %s", name); + + switch (gsk_gl_shader_get_uniform_type (shader, i)) + { + case GSK_GLUNIFORM_TYPE_NONE: + default: + g_assert_not_reached (); + break; + + case GSK_GLUNIFORM_TYPE_FLOAT: + add_float_row (store, title, + gsk_gl_shader_get_uniform_data_float (shader, uniform_data, i)); + break; + + case GSK_GLUNIFORM_TYPE_INT: + add_int_row (store, title, + gsk_gl_shader_get_uniform_data_int (shader, uniform_data, i)); + break; + + case GSK_GLUNIFORM_TYPE_UINT: + add_uint_row (store, title, + gsk_gl_shader_get_uniform_data_uint (shader, uniform_data, i)); + break; + + case GSK_GLUNIFORM_TYPE_BOOL: + add_boolean_row (store, title, + gsk_gl_shader_get_uniform_data_bool (shader, uniform_data, i)); + break; + + case GSK_GLUNIFORM_TYPE_VEC2: + { + graphene_vec2_t v; + gsk_gl_shader_get_uniform_data_vec2 (shader, uniform_data, i, &v); + float x = graphene_vec2_get_x (&v); + float y = graphene_vec2_get_x (&v); + char *s = g_strdup_printf ("%.2f %.2f", x, y); + add_text_row (store, title, s); + g_free (s); + } + break; + + case GSK_GLUNIFORM_TYPE_VEC3: + { + graphene_vec3_t v; + gsk_gl_shader_get_uniform_data_vec3 (shader, uniform_data, i, &v); + float x = graphene_vec3_get_x (&v); + float y = graphene_vec3_get_y (&v); + float z = graphene_vec3_get_z (&v); + char *s = g_strdup_printf ("%.2f %.2f %.2f", x, y, z); + add_text_row (store, title, s); + g_free (s); + } + break; + + case GSK_GLUNIFORM_TYPE_VEC4: + { + graphene_vec4_t v; + gsk_gl_shader_get_uniform_data_vec4 (shader, uniform_data, i, &v); + float x = graphene_vec4_get_x (&v); + float y = graphene_vec4_get_y (&v); + float z = graphene_vec4_get_z (&v); + float w = graphene_vec4_get_w (&v); + char *s = g_strdup_printf ("%.2f %.2f %.2f %.2f", x, y, z, w); + add_text_row (store, title, s); + g_free (s); + } + break; + } + g_free (title); + } + } + break; + case GSK_INSET_SHADOW_NODE: { const GdkRGBA *color = gsk_inset_shadow_node_peek_color (node);