diff options
author | Matthias Clasen <mclasen@redhat.com> | 2020-09-25 17:16:19 -0400 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2020-09-25 17:21:40 -0400 |
commit | bdcc29ebef3b106fa242d7fdec74451c8138a0a0 (patch) | |
tree | 66ab73f0d957388cc95a7163d81f7f37b85e1638 | |
parent | 810e9c238eb6bbd6f24bf1f4c2468930fbb77bbe (diff) | |
download | gtk+-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.c | 1 | ||||
-rw-r--r-- | demos/gtk-demo/gltransition.c | 1 | ||||
-rw-r--r-- | demos/gtk-demo/gskshaderpaintable.c | 349 | ||||
-rw-r--r-- | demos/gtk-demo/gskshaderpaintable.h | 53 | ||||
-rw-r--r-- | demos/gtk-demo/meson.build | 1 |
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', |