[gtk/matthiasc/gltransition-demo: 8/10] gtk-demo: Add GskGLShaderNode demo




commit 91f4c4a967378529022c92b3f3b7a9c58a7d464f
Author: Alexander Larsson <alexl redhat com>
Date:   Mon Sep 21 21:05:04 2020 +0200

    gtk-demo: Add GskGLShaderNode demo

 demos/gtk-demo/demo.gresource.xml |   6 ++
 demos/gtk-demo/fire.glsl          |  72 +++++++++++++
 demos/gtk-demo/glshader.c         |  85 +++++++++++++++
 demos/gtk-demo/gtkshaderbin.c     | 220 ++++++++++++++++++++++++++++++++++++++
 demos/gtk-demo/gtkshaderbin.h     |  22 ++++
 demos/gtk-demo/meson.build        |   2 +
 6 files changed, 407 insertions(+)
---
diff --git a/demos/gtk-demo/demo.gresource.xml b/demos/gtk-demo/demo.gresource.xml
index 39d6ad2db8..5953feaaef 100644
--- a/demos/gtk-demo/demo.gresource.xml
+++ b/demos/gtk-demo/demo.gresource.xml
@@ -133,6 +133,11 @@
     <file>cogs.glsl</file>
     <file>glowingstars.glsl</file>
   </gresource>
+  <gresource prefix="/glshader">
+    <file>fire.glsl</file>
+    <file>gtkshaderbin.h</file>
+    <file>gtkshaderbin.c</file>
+  </gresource>
   <gresource prefix="/iconscroll">
     <file>iconscroll.ui</file>
   </gresource>
@@ -247,6 +252,7 @@
     <file>gears.c</file>
     <file>gestures.c</file>
     <file>glarea.c</file>
+    <file>glshader.c</file>
     <file>headerbar.c</file>
     <file>hypertext.c</file>
     <file>iconscroll.c</file>
diff --git a/demos/gtk-demo/fire.glsl b/demos/gtk-demo/fire.glsl
new file mode 100644
index 0000000000..58147cbbb2
--- /dev/null
+++ b/demos/gtk-demo/fire.glsl
@@ -0,0 +1,72 @@
+uniform float u_time;
+
+/* 2D -> [0..1] random number generator */
+float random(vec2 st) {
+  return fract(sin(dot(st.xy,
+                       vec2(12.9898,78.233))) *
+               43758.5453123);
+}
+
+/* Generate a smoothed 2d noise based on random() */
+float noise(vec2 v) {
+  /* Round point v to integer grid grid */
+  vec2 grid_point = floor(v);
+  /* Randomize in grid corners */
+  float corner1 = random(grid_point);
+  float corner2 = random(grid_point + vec2(1, 0));
+  float corner3 = random(grid_point + vec2(0, 1));
+  float corner4 = random(grid_point + vec2(1, 1));
+  /* Interpolate smoothly between grid points */
+  vec2 fraction = smoothstep(vec2(0.0), vec2(1.0), fract(v));
+  return mix(mix(corner1, corner2, fraction.x),
+             mix(corner3, corner4, fraction.x),
+             fraction.y);
+}
+
+/* fractal brownian motion noice, see https://www.iquilezles.org/www/articles/fbm/fbm.htm */
+float fbm(in vec2 x)
+{
+  const float octaveScale = 1.9;
+  const float G = 0.5;
+  float f = 1.0;
+  float a = 1.0;
+  float t = 0.0;
+  int numOctaves = 5;
+  for (int i = 0; i < numOctaves; i++) {
+    t += a*noise(f*x);
+    f *= octaveScale;
+    a *= G;
+  }
+
+  return t;
+}
+
+
+void mainImage(out vec4 fragColor, in vec2 fragCoord, in vec2 resolution, in vec2 uv)
+{
+  vec2 xy = fragCoord / resolution;
+
+  float zoom = 3.0 - sin(u_time*0.5)*0.3;
+
+  // Normalize coord to height of widget
+  vec2 p = (vec2 (-resolution.x/2 + fragCoord.x, resolution.y - fragCoord.y) / resolution.yy)* zoom;
+
+    // Use recursive incantations of fbm
+  float q1 = fbm(p - vec2(0.8, 0.3) * u_time);
+  float q2 = fbm(p - vec2(0.5, 1.3) * u_time);
+  float r = fbm(2.0*p + vec2(q1,q2) - vec2(0.0, 1.0)*u_time*10.0 *0.4);
+
+  // Compute intensity, mostly on the bottom
+  float w = 2 * r * p.y;
+
+  // Smooth out left/right side and fade in at start
+  w /= smoothstep(0.0,0.1, xy.x)* smoothstep(0.0,0.1, 1.0-xy.x) *  smoothstep(0.0,0.4, u_time);
+
+  // Compute colors
+  vec3 c = vec3(1.0,.2,.05);
+  vec3 color = 1.0 / (w*w/c + 1.0);
+
+  // Mix in widget
+  vec4 widget = texture(u_source,uv);
+  fragColor = mix(vec4(color,1), widget, 1.0-color.x);
+}
diff --git a/demos/gtk-demo/glshader.c b/demos/gtk-demo/glshader.c
new file mode 100644
index 0000000000..8bf8fccd65
--- /dev/null
+++ b/demos/gtk-demo/glshader.c
@@ -0,0 +1,85 @@
+/* OpenGL/GLShader
+ * #Keywords: OpenGL, shader
+ *
+ * Demonstrates using GskGLShaderNodes to integrate GLSL fragment shaders
+ * with the Gtk widget rendering.
+ */
+#include <math.h>
+#include <gtk/gtk.h>
+#include "gtkshaderbin.h"
+
+static GtkWidget *demo_window = NULL;
+
+static void
+close_window (GtkWidget *widget)
+{
+  /* Reset the state */
+  demo_window = NULL;
+}
+
+static GtkWidget *
+fire_bin_new (void)
+{
+  GtkWidget *bin = gtk_shader_bin_new ();
+  GBytes *shader_b;
+  GskGLShader *shader;
+
+  shader_b = g_resources_lookup_data ("/glshader/fire.glsl", 0, NULL);
+  shader = gsk_glshader_new ((const char *)g_bytes_get_data (shader_b, NULL));
+  gsk_glshader_add_uniform (shader, "u_time", GSK_GLUNIFORM_TYPE_FLOAT);
+  gtk_shader_bin_add_shader (GTK_SHADER_BIN (bin), shader, GTK_STATE_FLAG_PRELIGHT, GTK_STATE_FLAG_PRELIGHT);
+  g_bytes_unref (shader_b);
+  g_object_unref (shader);
+
+  return bin;
+}
+
+
+static GtkWidget *
+create_glshader_window (GtkWidget *do_widget)
+{
+  GtkWidget *window, *box, *button, *bin;
+
+  window = gtk_window_new ();
+  gtk_window_set_display (GTK_WINDOW (window),  gtk_widget_get_display (do_widget));
+  gtk_window_set_title (GTK_WINDOW (window), "glshader");
+  g_signal_connect (window, "destroy", G_CALLBACK (close_window), NULL);
+
+  box = gtk_box_new (GTK_ORIENTATION_VERTICAL, FALSE);
+  gtk_widget_set_margin_start (box, 12);
+  gtk_widget_set_margin_end (box, 12);
+  gtk_widget_set_margin_top (box, 12);
+  gtk_widget_set_margin_bottom (box, 12);
+  gtk_box_set_spacing (GTK_BOX (box), 6);
+  gtk_window_set_child (GTK_WINDOW (window), box);
+
+  bin = fire_bin_new ();
+  gtk_box_append (GTK_BOX (box), bin);
+
+  button = gtk_button_new_with_label ("Click me");
+  gtk_widget_set_receives_default (button, TRUE);
+  gtk_shader_bin_set_child (GTK_SHADER_BIN (bin), button);
+
+  bin = fire_bin_new ();
+  gtk_box_append (GTK_BOX (box), bin);
+
+  button = gtk_button_new_with_label ("Or me!");
+  gtk_widget_set_receives_default (button, TRUE);
+  gtk_shader_bin_set_child (GTK_SHADER_BIN (bin), button);
+
+  return window;
+}
+
+GtkWidget *
+do_glshader (GtkWidget *do_widget)
+{
+  if (!demo_window)
+    demo_window = create_glshader_window (do_widget);
+
+  if (!gtk_widget_get_visible (demo_window))
+    gtk_widget_show (demo_window);
+  else
+    gtk_window_destroy (GTK_WINDOW (demo_window));
+
+  return demo_window;
+}
diff --git a/demos/gtk-demo/gtkshaderbin.c b/demos/gtk-demo/gtkshaderbin.c
new file mode 100644
index 0000000000..abdf226942
--- /dev/null
+++ b/demos/gtk-demo/gtkshaderbin.c
@@ -0,0 +1,220 @@
+#include "gtkshaderbin.h"
+
+typedef struct {
+  GskGLShader *shader;
+  GtkStateFlags state;
+  GtkStateFlags state_mask;
+} ShaderInfo;
+
+struct _GtkShaderBin
+{
+  GtkWidget parent_instance;
+  GtkWidget *child;
+  GskGLShader *active_shader;
+  GPtrArray *shaders;
+  guint tick_id;
+  float time;
+  gint64 first_frame_time;
+};
+
+struct _GtkShaderBinClass
+{
+  GtkWidgetClass parent_class;
+};
+
+G_DEFINE_TYPE (GtkShaderBin, gtk_shader_bin, GTK_TYPE_WIDGET)
+
+static void
+shader_info_free (ShaderInfo *info)
+{
+  g_object_unref (info->shader);
+  g_free (info);
+}
+
+static void
+gtk_shader_bin_finalize (GObject *object)
+{
+  GtkShaderBin *self = GTK_SHADER_BIN (object);
+
+  g_ptr_array_free (self->shaders, TRUE);
+
+  G_OBJECT_CLASS (gtk_shader_bin_parent_class)->finalize (object);
+}
+
+static void
+gtk_shader_bin_dispose (GObject *object)
+{
+  GtkShaderBin *self = GTK_SHADER_BIN (object);
+
+  g_clear_pointer (&self->child, gtk_widget_unparent);
+
+  G_OBJECT_CLASS (gtk_shader_bin_parent_class)->dispose (object);
+}
+
+static gboolean
+gtk_shader_bin_tick (GtkWidget     *widget,
+                     GdkFrameClock *frame_clock,
+                     gpointer       unused)
+{
+  GtkShaderBin *self = GTK_SHADER_BIN (widget);
+  gint64 frame_time;
+
+  frame_time = gdk_frame_clock_get_frame_time (frame_clock);
+  if (self->first_frame_time == 0)
+    self->first_frame_time = frame_time;
+  self->time = (frame_time - self->first_frame_time) / (float)G_USEC_PER_SEC;
+
+  gtk_widget_queue_draw (widget);
+
+  return G_SOURCE_CONTINUE;
+}
+
+static void
+gtk_shader_bin_init (GtkShaderBin *self)
+{
+  self->shaders = g_ptr_array_new_with_free_func ((GDestroyNotify)shader_info_free);
+}
+
+void
+gtk_shader_bin_update_active_shader (GtkShaderBin *self)
+{
+  GtkStateFlags new_state = gtk_widget_get_state_flags (GTK_WIDGET (self));
+  GskGLShader *new_shader = NULL;
+
+  for (int i = 0; i < self->shaders->len; i++)
+    {
+      ShaderInfo *info = g_ptr_array_index (self->shaders, i);
+
+      if ((info->state_mask & new_state) == info->state)
+        {
+          new_shader = info->shader;
+          break;
+        }
+    }
+
+  if (self->active_shader == new_shader)
+    return;
+
+  self->active_shader = new_shader;
+  self->first_frame_time = 0;
+
+  if (self->active_shader)
+    {
+      if (self->tick_id == 0)
+        self->tick_id = gtk_widget_add_tick_callback (GTK_WIDGET (self),
+                                                      gtk_shader_bin_tick,
+                                                      NULL, NULL);
+    }
+  else
+    {
+      if (self->tick_id != 0)
+        {
+          gtk_widget_remove_tick_callback (GTK_WIDGET (self), self->tick_id);
+          self->tick_id = 0;
+        }
+    }
+
+  gtk_widget_queue_draw (GTK_WIDGET (self));
+}
+
+static void
+gtk_shader_bin_state_flags_changed (GtkWidget        *widget,
+                                    GtkStateFlags     previous_state_flags)
+{
+  GtkShaderBin *self = GTK_SHADER_BIN (widget);
+
+  gtk_shader_bin_update_active_shader (self);
+}
+
+void
+gtk_shader_bin_add_shader (GtkShaderBin *self,
+                           GskGLShader  *shader,
+                           GtkStateFlags state,
+                           GtkStateFlags state_mask)
+{
+  ShaderInfo *info = g_new0 (ShaderInfo, 1);
+  info->shader = g_object_ref (shader);
+  info->state = state;
+  info->state_mask = state_mask;
+
+  g_ptr_array_add (self->shaders, info);
+
+  gtk_shader_bin_update_active_shader (self);
+}
+
+void
+gtk_shader_bin_set_child (GtkShaderBin *self,
+                          GtkWidget    *child)
+{
+
+  if (self->child == child)
+    return;
+
+  g_clear_pointer (&self->child, gtk_widget_unparent);
+
+  if (child)
+    {
+      self->child = child;
+      gtk_widget_set_parent (child, GTK_WIDGET (self));
+    }
+}
+
+GtkWidget *
+gtk_shader_bin_get_child   (GtkShaderBin *self)
+{
+  return self->child;
+}
+
+static void
+gtk_shader_bin_snapshot (GtkWidget   *widget,
+                         GtkSnapshot *snapshot)
+{
+  GtkShaderBin *self = GTK_SHADER_BIN (widget);
+  int width, height;
+
+  width = gtk_widget_get_width (widget);
+  height = gtk_widget_get_height (widget);
+
+  if (self->active_shader)
+    {
+      gtk_snapshot_push_glshader (snapshot, self->active_shader,
+                                  &GRAPHENE_RECT_INIT(0, 0, width, height),
+                                  1,
+                                  "u_time", &self->time,
+                                  NULL
+                                  );
+      gtk_widget_snapshot_child (widget, self->child, snapshot);
+      gtk_snapshot_pop (snapshot); /* Fallback */
+      gtk_widget_snapshot_child (widget, self->child, snapshot);
+      gtk_snapshot_pop (snapshot); /* Shader node child 1 */
+    }
+  else
+    {
+      gtk_widget_snapshot_child (widget, self->child, snapshot);
+    }
+}
+
+static void
+gtk_shader_bin_class_init (GtkShaderBinClass *class)
+{
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+  object_class->finalize = gtk_shader_bin_finalize;
+  object_class->dispose = gtk_shader_bin_dispose;
+
+  gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
+
+  widget_class->snapshot = gtk_shader_bin_snapshot;
+  widget_class->state_flags_changed = gtk_shader_bin_state_flags_changed;
+}
+
+GtkWidget *
+gtk_shader_bin_new (void)
+{
+  GtkShaderBin *self;
+
+  self = g_object_new (GTK_TYPE_SHADER_BIN, NULL);
+
+  return GTK_WIDGET (self);
+}
diff --git a/demos/gtk-demo/gtkshaderbin.h b/demos/gtk-demo/gtkshaderbin.h
new file mode 100644
index 0000000000..eabac8f0d6
--- /dev/null
+++ b/demos/gtk-demo/gtkshaderbin.h
@@ -0,0 +1,22 @@
+#ifndef __GTK_SHADER_BIN_H__
+#define __GTK_SHADER_BIN_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_SHADER_BIN      (gtk_shader_bin_get_type ())
+G_DECLARE_FINAL_TYPE (GtkShaderBin, gtk_shader_bin, GTK, SHADER_BIN, GtkWidget)
+
+GtkWidget  *gtk_shader_bin_new        (void);
+void        gtk_shader_bin_add_shader (GtkShaderBin *self,
+                                       GskGLShader  *shader,
+                                       GtkStateFlags state,
+                                       GtkStateFlags state_mask);
+void       gtk_shader_bin_set_child   (GtkShaderBin *self,
+                                       GtkWidget    *child);
+GtkWidget *gtk_shader_bin_get_child   (GtkShaderBin *self);
+
+G_END_DECLS
+
+#endif /* __GTK_SHADER_BIN_H__ */
diff --git a/demos/gtk-demo/meson.build b/demos/gtk-demo/meson.build
index a32fee88e5..cbc52bcca6 100644
--- a/demos/gtk-demo/meson.build
+++ b/demos/gtk-demo/meson.build
@@ -32,6 +32,7 @@ demos = files([
   'gears.c',
   'gestures.c',
   'glarea.c',
+  'glshader.c',
   'headerbar.c',
   'hypertext.c',
   'iconscroll.c',
@@ -102,6 +103,7 @@ extra_demo_sources = files(['main.c',
                             'gtkfishbowl.c',
                             'fontplane.c',
                             'gtkgears.c',
+                            'gtkshaderbin.c',
                             'gtkshadertoy.c',
                             'puzzlepiece.c',
                             'bluroverlay.c',


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]