summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Clasen <mclasen@redhat.com>2020-09-25 17:16:19 -0400
committerMatthias Clasen <mclasen@redhat.com>2020-09-25 17:21:40 -0400
commitbdcc29ebef3b106fa242d7fdec74451c8138a0a0 (patch)
tree66ab73f0d957388cc95a7163d81f7f37b85e1638
parent810e9c238eb6bbd6f24bf1f4c2468930fbb77bbe (diff)
downloadgtk+-bdcc29ebef3b106fa242d7fdec74451c8138a0a0.tar.gz
gtk-demo: Add a shader paintable
This is a GdkPaintable implementation wrapped around a GskGLShader. It can optionally be hooked up to a frame clock to update a time uniform. The code is set up for this to live as public api in GSK, if we find it useful enough.
-rw-r--r--demos/gtk-demo/fishbowl.c1
-rw-r--r--demos/gtk-demo/gltransition.c1
-rw-r--r--demos/gtk-demo/gskshaderpaintable.c349
-rw-r--r--demos/gtk-demo/gskshaderpaintable.h53
-rw-r--r--demos/gtk-demo/meson.build1
5 files changed, 405 insertions, 0 deletions
diff --git a/demos/gtk-demo/fishbowl.c b/demos/gtk-demo/fishbowl.c
index 40c8203726..38f7302795 100644
--- a/demos/gtk-demo/fishbowl.c
+++ b/demos/gtk-demo/fishbowl.c
@@ -9,6 +9,7 @@
#include "gtkfishbowl.h"
#include "gtkgears.h"
+#include "gskshaderpaintable.h"
const char *const css =
".blurred-button {"
diff --git a/demos/gtk-demo/gltransition.c b/demos/gtk-demo/gltransition.c
index 444d906d52..c0e49cc552 100644
--- a/demos/gtk-demo/gltransition.c
+++ b/demos/gtk-demo/gltransition.c
@@ -13,6 +13,7 @@
#include "gtkshaderstack.h"
#include "gtkshaderbin.h"
#include "gtkshadertoy.h"
+#include "gskshaderpaintable.h"
static GtkWidget *demo_window = NULL;
diff --git a/demos/gtk-demo/gskshaderpaintable.c b/demos/gtk-demo/gskshaderpaintable.c
new file mode 100644
index 0000000000..c441714113
--- /dev/null
+++ b/demos/gtk-demo/gskshaderpaintable.c
@@ -0,0 +1,349 @@
+/*
+ * 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.1 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/>.
+ *
+ * Authors: Matthias Clasen <mclasen@redhat.com>
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+#include "gskshaderpaintable.h"
+
+/**
+ * SECTION:gskshaderpaintable
+ * @Short_description: Drawing with shaders
+ * @Title: GskShaderPaintable
+ * @see_also: #GdkPaintable
+ *
+ * GskShaderPaintable is an implementation of the #GdkPaintable interface
+ * that uses a #GskGLShader to create pixels.
+ *
+ * You can set the uniform data that the shader needs for rendering
+ * using gsk_shader_paintable_set_uniform_data(). This function can
+ * be called repeatedly to change the uniform data for the next
+ * snapshot.
+ *
+ * Commonly, time is passed to shaders as a float uniform containing
+ * the elapsed time in seconds. The convenience API
+ * gsk_shader_paintable_update_time() can be called from a #GtkTickCallback
+ * to update the time based on the frame time of the frame clock.
+ */
+
+
+struct _GskShaderPaintable
+{
+ GObject parent_instance;
+
+ GskGLShader *shader;
+ GBytes *uniform_data;
+
+ gint64 start_time;
+};
+
+struct _GskShaderPaintableClass
+{
+ GObjectClass parent_class;
+};
+
+enum {
+ PROP_0,
+ PROP_SHADER,
+ PROP_UNIFORM_DATA,
+
+ N_PROPS,
+};
+
+static GParamSpec *properties[N_PROPS] = { NULL, };
+
+static void
+gsk_shader_paintable_paintable_snapshot (GdkPaintable *paintable,
+ GdkSnapshot *snapshot,
+ double width,
+ double height)
+{
+ GskShaderPaintable *self = GSK_SHADER_PAINTABLE (paintable);
+
+ /* FIXME: We have to add a pointless extra child here to
+ * keep GtkSnapshot from blowing up. We really want n_children = 0,
+ * but then we would pop() 0 times, and... no pop, no node.
+ */
+ gtk_snapshot_push_gl_shader (snapshot, self->shader, &GRAPHENE_RECT_INIT(0, 0, width, height), g_bytes_ref (self->uniform_data), 1);
+ gtk_snapshot_append_color (snapshot, &(GdkRGBA){1.0, 0.5, 0.6, 1.0}, &GRAPHENE_RECT_INIT(0, 0, width, height));
+ gtk_snapshot_pop (snapshot);
+}
+
+static void
+gsk_shader_paintable_paintable_init (GdkPaintableInterface *iface)
+{
+ iface->snapshot = gsk_shader_paintable_paintable_snapshot;
+}
+
+G_DEFINE_TYPE_EXTENDED (GskShaderPaintable, gsk_shader_paintable, G_TYPE_OBJECT, 0,
+ G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,
+ gsk_shader_paintable_paintable_init))
+
+static void
+gsk_shader_paintable_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+
+{
+ GskShaderPaintable *self = GSK_SHADER_PAINTABLE (object);
+
+ switch (prop_id)
+ {
+ case PROP_SHADER:
+ gsk_shader_paintable_set_shader (self, g_value_get_object (value));
+ break;
+
+ case PROP_UNIFORM_DATA:
+ gsk_shader_paintable_set_uniform_data (self, g_value_get_boxed (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gsk_shader_paintable_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GskShaderPaintable *self = GSK_SHADER_PAINTABLE (object);
+
+ switch (prop_id)
+ {
+ case PROP_SHADER:
+ g_value_set_object (value, self->shader);
+ break;
+
+ case PROP_UNIFORM_DATA:
+ g_value_set_boxed (value, self->uniform_data);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gsk_shader_paintable_finalize (GObject *object)
+{
+ GskShaderPaintable *self = GSK_SHADER_PAINTABLE (object);
+
+ g_clear_pointer (&self->uniform_data, g_bytes_unref);
+ g_clear_object (&self->shader);
+
+ G_OBJECT_CLASS (gsk_shader_paintable_parent_class)->finalize (object);
+}
+
+static void
+gsk_shader_paintable_class_init (GskShaderPaintableClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->get_property = gsk_shader_paintable_get_property;
+ gobject_class->set_property = gsk_shader_paintable_set_property;
+ gobject_class->finalize = gsk_shader_paintable_finalize;
+
+ properties[PROP_SHADER] =
+ g_param_spec_object ("shader", "Shader", "The shader",
+ GSK_TYPE_GL_SHADER,
+ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_UNIFORM_DATA] =
+ g_param_spec_boxed ("uniform-data", "Uniform data", "The uniform data",
+ G_TYPE_BYTES,
+ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (gobject_class, N_PROPS, properties);
+}
+
+static void
+gsk_shader_paintable_init (GskShaderPaintable *self)
+{
+}
+
+/**
+ * gsk_shader_paintable_new:
+ * @shader: (transfer full) (nullable): the shader to use
+ * @data: (transfer full) (nullable): uniform data
+ *
+ * Creates a paintable that uses the @shader to create
+ * pixels. The shader must not require input textures.
+ * If @data is %NULL, all uniform values are set to zero.
+ *
+ * Returns: (transfer full): a new #GskShaderPaintable
+ */
+GdkPaintable *
+gsk_shader_paintable_new (GskGLShader *shader,
+ GBytes *data)
+{
+ GdkPaintable *ret;
+
+ g_return_val_if_fail (shader == NULL || GSK_IS_GL_SHADER (shader), NULL);
+
+ if (shader && !data)
+ {
+ int size = gsk_gl_shader_get_args_size (shader);
+ data = g_bytes_new_take (g_new0 (guchar, size), size);
+ }
+
+ ret = g_object_new (GSK_TYPE_SHADER_PAINTABLE,
+ "shader", shader,
+ "uniform-data", data,
+ NULL);
+
+ g_clear_object (&shader);
+ g_clear_pointer (&data, g_bytes_unref);
+
+ return ret;
+}
+
+/**
+ * gsk_shader_paintable_set_shader:
+ * @self: a #GskShaderPaintable
+ * @shader: the #GskGLShader to use
+ *
+ * Sets the shader that the paintable will use
+ * to create pixels. The shader must not require
+ * input textures.
+ */
+void
+gsk_shader_paintable_set_shader (GskShaderPaintable *self,
+ GskGLShader *shader)
+{
+ g_return_if_fail (GSK_IS_SHADER_PAINTABLE (self));
+ g_return_if_fail (shader == NULL || GSK_IS_GL_SHADER (shader));
+ g_return_if_fail (shader == NULL || gsk_gl_shader_get_n_required_textures (shader) == 0);
+
+ if (!g_set_object (&self->shader, shader))
+ return;
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SHADER]);
+ gdk_paintable_invalidate_contents (GDK_PAINTABLE (self));
+
+ g_clear_pointer (&self->uniform_data, g_bytes_unref);
+}
+
+/**
+ * gsk_shader_paintable_get_shader:
+ * @self: a #GskShaderPaintable
+ *
+ * Returns the shader that the paintable is using.
+ *
+ * Returns: (transfer none): the #GskGLShader that is used
+ */
+GskGLShader *
+gsk_shader_paintable_get_shader (GskShaderPaintable *self)
+{
+ g_return_val_if_fail (GSK_IS_SHADER_PAINTABLE (self), NULL);
+
+ return self->shader;
+}
+
+/**
+ * gsk_shader_paintable_set_uniform_data:
+ * @self: a #GskShaderPaintable
+ * @data: Data block with uniform data for the shader
+ *
+ * Sets the uniform data that will be passed to the
+ * shader when rendering. The @data will typically
+ * be produced by a #GskUniformDataBuilder.
+ *
+ * Note that the @data should be considered immutable
+ * after it has been passed to this function.
+ */
+void
+gsk_shader_paintable_set_uniform_data (GskShaderPaintable *self,
+ GBytes *data)
+{
+ g_return_if_fail (GSK_IS_SHADER_PAINTABLE (self));
+ g_return_if_fail (data == NULL || g_bytes_get_size (data) == gsk_gl_shader_get_args_size (self->shader));
+
+ g_clear_pointer (&self->uniform_data, g_bytes_unref);
+ if (data)
+ self->uniform_data = g_bytes_ref (data);
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_UNIFORM_DATA]);
+ gdk_paintable_invalidate_contents (GDK_PAINTABLE (self));
+}
+
+/**
+ * gsk_shader_paintable_get_uniform_data:
+ * @self: a #GskShaderPaintable
+ *
+ * Returns the uniform data set with
+ * gsk_shader_paintable_get_uniform_data().
+ *
+ * Returns: (transfer none): the uniform data
+ */
+GBytes *
+gsk_shader_paintable_get_uniform_data (GskShaderPaintable *self)
+{
+ g_return_val_if_fail (GSK_IS_SHADER_PAINTABLE (self), NULL);
+
+ return self->uniform_data;
+}
+
+/**
+ * gsk_shader_paintable_update_time:
+ * @self: a #GskShaderPaintable
+ * @time_idx: the index of the uniform for time in seconds as float
+ * @frame_time: the current frame time, as returned by #GdkFrameClock
+ *
+ * This function is a convenience wrapper for
+ * gsk_shader_paintable_set_uniform_data() that leaves all
+ * uniform values unchanged, except for the uniform with
+ * index @time_idx, which will be set to the elapsed time
+ * in seconds, since the first call to this function.
+ *
+ * This function is usually called from a #GtkTickCallback.
+ */
+void
+gsk_shader_paintable_update_time (GskShaderPaintable *self,
+ int time_idx,
+ gint64 frame_time)
+{
+ int size;
+ int offset;
+ guchar *data;
+ float time;
+ GBytes *uniform_data;
+
+ size = gsk_gl_shader_get_args_size (self->shader);
+ offset = gsk_gl_shader_get_uniform_offset (self->shader, time_idx);
+
+ data = g_new0 (guchar, size);
+ memcpy (data, g_bytes_get_data (self->uniform_data, NULL), size);
+
+ if (self->start_time == 0)
+ self->start_time = frame_time;
+
+ time = (frame_time - self->start_time) / (float)G_TIME_SPAN_SECOND;
+ *(float*)(data + offset) = time;
+
+ uniform_data = g_bytes_new_take (data, size);
+
+ gsk_shader_paintable_set_uniform_data (self, uniform_data);
+
+ g_bytes_unref (uniform_data);
+}
diff --git a/demos/gtk-demo/gskshaderpaintable.h b/demos/gtk-demo/gskshaderpaintable.h
new file mode 100644
index 0000000000..f9c38fdfbb
--- /dev/null
+++ b/demos/gtk-demo/gskshaderpaintable.h
@@ -0,0 +1,53 @@
+/*
+ * 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.1 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/>.
+ *
+ * Authors: Matthias Clasen <mclasen@redhat.com>
+ */
+
+#ifndef __GSK_SHADER_PAINTABLE_H__
+#define __GSK_SHADER_PAINTABLE_H__
+
+#include <gdk/gdk.h>
+#include <gsk/gsk.h>
+
+G_BEGIN_DECLS
+
+#define GSK_TYPE_SHADER_PAINTABLE (gsk_shader_paintable_get_type ())
+
+GDK_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (GskShaderPaintable, gsk_shader_paintable, GSK, SHADER_PAINTABLE, GObject)
+
+GDK_AVAILABLE_IN_ALL
+GdkPaintable * gsk_shader_paintable_new (GskGLShader *shader,
+ GBytes *data);
+
+GDK_AVAILABLE_IN_ALL
+GskGLShader * gsk_shader_paintable_get_shader (GskShaderPaintable *self);
+GDK_AVAILABLE_IN_ALL
+void gsk_shader_paintable_set_shader (GskShaderPaintable *self,
+ GskGLShader *shader);
+GDK_AVAILABLE_IN_ALL
+GBytes * gsk_shader_paintable_get_uniform_data (GskShaderPaintable *self);
+GDK_AVAILABLE_IN_ALL
+void gsk_shader_paintable_set_uniform_data (GskShaderPaintable *self,
+ GBytes *data);
+GDK_AVAILABLE_IN_ALL
+void gsk_shader_paintable_update_time (GskShaderPaintable *self,
+ int time_idx,
+ gint64 frame_time);
+G_END_DECLS
+
+#endif /* __GSK_SHADER_PAINTABLE_H__ */
diff --git a/demos/gtk-demo/meson.build b/demos/gtk-demo/meson.build
index 444458d182..ecb08ebaac 100644
--- a/demos/gtk-demo/meson.build
+++ b/demos/gtk-demo/meson.build
@@ -106,6 +106,7 @@ extra_demo_sources = files(['main.c',
'gtkshaderbin.c',
'gtkshadertoy.c',
'gtkshaderstack.c',
+ 'gskshaderpaintable.c',
'puzzlepiece.c',
'bluroverlay.c',
'demoimage.c',