[gtk/matthiasc/glshader-node: 1/9] Add GskGLShaderNode
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/matthiasc/glshader-node: 1/9] Add GskGLShaderNode
- Date: Wed, 23 Sep 2020 19:02:08 +0000 (UTC)
commit 111dfdf3f53ad6cbdb89a5a1b4165555e36bf590
Author: Alexander Larsson <alexl redhat com>
Date: Fri Sep 18 17:46:57 2020 +0200
Add GskGLShaderNode
This is a rendernode that is supposed to run a GLSL fragment
shader with a set of inputs and produce outputs.
The inputs are:
* A 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(-)
---
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 <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * 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:
+ *
+ * |[<!-- language="plain" -->
+ * 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:
+ *
+ * |[<!-- language="plain" -->
+ * 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:
+ *
+ * |[<!-- language="plain" -->
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GSK_GL_SHADER_H__
+#define __GSK_GL_SHADER_H__
+
+#if !defined (__GSK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gsk/gsk.h> can be included directly."
+#endif
+
+#include <stdarg.h>
+
+#include <gsk/gsktypes.h>
+#include <gsk/gskenums.h>
+
+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 <gsk/gskglshader.h>
+
+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 <gsk/gskroundedrect.h>
#include <gsk/gsktypes.h>
+#include <gsk/gskglshader.h>
#include <gtk/css/gtkcss.h>
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);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]