[gtk: 10/15] GtkSnapshot: Add gtk_snapshot_push_glshader()




commit 950cc41e15cc8b92c75f49e5e31fbff5e6d6efa2
Author: Alexander Larsson <alexl redhat com>
Date:   Fri Sep 18 17:56:02 2020 +0200

    GtkSnapshot: Add gtk_snapshot_push_glshader()

 docs/reference/gtk/gtk4-sections.txt |   1 +
 gtk/gtksnapshot.c                    | 199 +++++++++++++++++++++++++++++++++++
 gtk/gtksnapshot.h                    |   8 +-
 3 files changed, 207 insertions(+), 1 deletion(-)
---
diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt
index b0bbd9cb1d..352c9d26e1 100644
--- a/docs/reference/gtk/gtk4-sections.txt
+++ b/docs/reference/gtk/gtk4-sections.txt
@@ -4299,6 +4299,7 @@ gtk_snapshot_push_blend
 gtk_snapshot_push_blur
 gtk_snapshot_push_shadow
 gtk_snapshot_push_debug
+gtk_snapshot_push_gl_shader
 gtk_snapshot_pop
 gtk_snapshot_save
 gtk_snapshot_restore
diff --git a/gtk/gtksnapshot.c b/gtk/gtksnapshot.c
index 89e4ea9d39..ab19aa8ac1 100644
--- a/gtk/gtksnapshot.c
+++ b/gtk/gtksnapshot.c
@@ -91,6 +91,18 @@ struct _GtkSnapshotState {
     struct {
       graphene_rect_t bounds;
     } clip;
+    struct {
+      GskGLShader *shader;
+      GBytes *args;
+      graphene_rect_t bounds;
+      GskRenderNode **nodes;
+      GskRenderNode *internal_nodes[4];
+    } glshader;
+    struct {
+      graphene_rect_t bounds;
+      int node_idx;
+      int n_children;
+    } glshader_texture;
     struct {
       GskRoundedRect bounds;
     } rounded_clip;
@@ -230,6 +242,18 @@ gtk_snapshot_get_previous_state (const GtkSnapshot *snapshot)
   return gtk_snapshot_states_get (&snapshot->state_stack, size - 2);
 }
 
+/* n == 0 => current, n == 1, previous, etc */
+static GtkSnapshotState *
+gtk_snapshot_get_nth_previous_state (const GtkSnapshot *snapshot,
+                                     int n)
+{
+  gsize size = gtk_snapshot_states_get_size (&snapshot->state_stack);
+
+  g_assert (size > n);
+
+  return gtk_snapshot_states_get (&snapshot->state_stack, size - (1 + n));
+}
+
 static void
 gtk_snapshot_state_clear (GtkSnapshotState *state)
 {
@@ -825,6 +849,149 @@ gtk_snapshot_push_clip (GtkSnapshot           *snapshot,
   gtk_graphene_rect_scale_affine (bounds, scale_x, scale_y, dx, dy, &state->data.clip.bounds);
 }
 
+static GskRenderNode *
+gtk_snapshot_collect_gl_shader (GtkSnapshot      *snapshot,
+                                GtkSnapshotState *state,
+                                GskRenderNode   **collected_nodes,
+                                guint             n_collected_nodes)
+{
+  GskRenderNode *shader_node = NULL;
+  GskRenderNode **nodes;
+  int n_children;
+
+  n_children = gsk_gl_shader_get_n_textures (state->data.glshader.shader);
+  shader_node = NULL;
+
+  if (n_collected_nodes != 0)
+    g_warning ("Unexpected children when poping gl shader.");
+
+  if (state->data.glshader.nodes)
+    nodes = state->data.glshader.nodes;
+  else
+    nodes = &state->data.glshader.internal_nodes[0];
+
+  if (state->data.glshader.bounds.size.width != 0 &&
+      state->data.glshader.bounds.size.height != 0)
+    shader_node = gsk_gl_shader_node_new (state->data.glshader.shader,
+                                          &state->data.glshader.bounds,
+                                          state->data.glshader.args,
+                                          nodes, n_children);
+
+  g_object_unref (state->data.glshader.shader);
+  g_bytes_unref (state->data.glshader.args);
+
+  for (guint i = 0; i  < n_children; i++)
+    gsk_render_node_unref (nodes[i]);
+
+  g_free (state->data.glshader.nodes);
+
+  return shader_node;
+}
+
+static GskRenderNode *
+gtk_snapshot_collect_gl_shader_texture (GtkSnapshot      *snapshot,
+                                        GtkSnapshotState *state,
+                                        GskRenderNode   **nodes,
+                                        guint             n_nodes)
+{
+  GskRenderNode *child_node;
+  GdkRGBA transparent = { 0, 0, 0, 0 };
+  int n_children, node_idx;
+  GtkSnapshotState *glshader_state;
+  GskRenderNode **out_nodes;
+
+  child_node = gtk_snapshot_collect_default (snapshot, state, nodes, n_nodes);
+
+  if (child_node == NULL)
+    child_node = gsk_color_node_new (&transparent, &state->data.glshader_texture.bounds);
+
+  n_children = state->data.glshader_texture.n_children;
+  node_idx = state->data.glshader_texture.node_idx;
+
+  glshader_state = gtk_snapshot_get_nth_previous_state (snapshot, n_children - node_idx);
+  g_assert (glshader_state->collect_func == gtk_snapshot_collect_gl_shader);
+
+  if (glshader_state->data.glshader.nodes)
+    out_nodes = glshader_state->data.glshader.nodes;
+  else
+    out_nodes = &glshader_state->data.glshader.internal_nodes[0];
+
+  out_nodes[node_idx] = child_node;
+
+  return NULL;
+}
+
+/**
+ * gtk_snapshot_push_gl_shader:
+ * @snapshot: a #GtkSnapshot
+ * @shader: The code to run
+ * @bounds: the rectangle to render into
+ * @take_args: (transfer full): Data block with arguments for the shader.
+ *
+ * Push a #GskGLShaderNode with a specific #GskGLShader and a set of uniform values
+ * to use while rendering. Additionally this takes a list of @n_children other nodes
+ * which will be passed to the #GskGLShaderNode.
+ *
+ * The @take_args argument is a block of data to use for uniform
+ * arguments, as per types and offsets defined by the @shader. Normally this is
+ * generated by gsk_gl_shader_format_args() or #GskGLShaderArgBuilder.
+ * The snapshotter takes ownership of @take_args, so the caller should not free it
+ * after this.
+ *
+ * If the renderer doesn't support GL shaders, or if there is any problem when
+ * compiling the shader, then the node will draw pink. You should use
+ * gsk_gl_shader_compile() to ensure the @shader will work for the renderer
+ * before using it.
+ *
+ * If the shader requires textures (see gsk_gl_shader_get_n_textures()), then it is
+ * expected that you call gtk_snapshot_gl_shader_pop_texture() the number of times that are
+ * required. Each of these calls will generate a node that is added as a child to the gl shader
+ * node, which in turn will render these offscreen and pass as a texture to the shader.
+ *
+ * Once all textures (if any) are pop:ed, you must call the regular gtk_snapshot_pop().
+ *
+ * If you want to use pre-existing textures as input to the shader rather than
+ * rendering new ones, use gtk_snapshot_append_texture() to push a texture node. These
+ * will be used directly rather than being re-rendered.
+ *
+ * For details on how to write shaders, see #GskGLShader.
+ */
+void
+gtk_snapshot_push_gl_shader (GtkSnapshot           *snapshot,
+                             GskGLShader           *shader,
+                             const graphene_rect_t *bounds,
+                             GBytes                *take_args)
+{
+  GtkSnapshotState *state;
+  float scale_x, scale_y, dx, dy;
+  graphene_rect_t transformed_bounds;
+  int n_children = gsk_gl_shader_get_n_textures (shader);
+
+  gtk_snapshot_ensure_affine (snapshot, &scale_x, &scale_y, &dx, &dy);
+
+  state = gtk_snapshot_push_state (snapshot,
+                                   gtk_snapshot_get_current_state (snapshot)->transform,
+                                   gtk_snapshot_collect_gl_shader);
+  gtk_graphene_rect_scale_affine (bounds, scale_x, scale_y, dx, dy, &transformed_bounds);
+  state->data.glshader.bounds = transformed_bounds;
+  state->data.glshader.shader = g_object_ref (shader);
+  state->data.glshader.args = take_args; /* Takes ownership */
+  if (n_children <= G_N_ELEMENTS (state->data.glshader.internal_nodes))
+    state->data.glshader.nodes = NULL;
+  else
+    state->data.glshader.nodes = g_new (GskRenderNode *, n_children);
+
+  for (int i = 0; i  < n_children; i++)
+    {
+      state = gtk_snapshot_push_state (snapshot,
+                                       gtk_snapshot_get_current_state (snapshot)->transform,
+                                       gtk_snapshot_collect_gl_shader_texture);
+      state->data.glshader_texture.bounds = transformed_bounds;
+      state->data.glshader_texture.node_idx = n_children - 1 - i;/* We pop in reverse order */
+      state->data.glshader_texture.n_children = n_children;
+    }
+}
+
 static GskRenderNode *
 gtk_snapshot_collect_rounded_clip (GtkSnapshot      *snapshot,
                                    GtkSnapshotState *state,
@@ -1325,14 +1492,46 @@ gtk_snapshot_to_paintable (GtkSnapshot           *snapshot,
 void
 gtk_snapshot_pop (GtkSnapshot *snapshot)
 {
+  GtkSnapshotState *state = gtk_snapshot_get_current_state (snapshot);
   GskRenderNode *node;
 
+  if (state->collect_func == gtk_snapshot_collect_gl_shader_texture)
+    g_warning ("Not enough calls to gtk_snapshot_gl_shader_pop_texture().");
+
   node = gtk_snapshot_pop_internal (snapshot);
 
   if (node)
     gtk_snapshot_append_node_internal (snapshot, node);
 }
 
+/**
+ * gtk_snapshot_gl_shader_pop_texture:
+ * @snapshot: a #GtkSnapshot
+ *
+ * Removes the top element from the stack of render nodes and
+ * adds it to the nearest GskGLShaderNode below it. This must be called the
+ * same number of times as the number of textures is needed for the
+ * shader in gtk_snapshot_push_gl_shader().
+ */
+void
+gtk_snapshot_gl_shader_pop_texture (GtkSnapshot *snapshot)
+{
+  GtkSnapshotState *state = gtk_snapshot_get_current_state (snapshot);
+  GskRenderNode *node;
+
+  if (state->collect_func != gtk_snapshot_collect_gl_shader_texture)
+    {
+      g_warning ("Too many calls to gtk_snapshot_gl_shader_pop_texture().");
+      return;
+    }
+
+  g_assert (state->collect_func == gtk_snapshot_collect_gl_shader_texture);
+
+  node = gtk_snapshot_pop_internal (snapshot);
+  g_assert (node == NULL);
+}
+
+
 /**
  * gtk_snapshot_save:
  * @snapshot: a #GtkSnapshot
diff --git a/gtk/gtksnapshot.h b/gtk/gtksnapshot.h
index e3c0e17e65..19e0045b17 100644
--- a/gtk/gtksnapshot.h
+++ b/gtk/gtksnapshot.h
@@ -99,8 +99,14 @@ GDK_AVAILABLE_IN_ALL
 void            gtk_snapshot_push_cross_fade            (GtkSnapshot            *snapshot,
                                                          double                  progress);
 GDK_AVAILABLE_IN_ALL
+void            gtk_snapshot_push_gl_shader             (GtkSnapshot            *snapshot,
+                                                         GskGLShader            *shader,
+                                                         const graphene_rect_t  *bounds,
+                                                         GBytes                 *take_args);
+GDK_AVAILABLE_IN_ALL
+void           gtk_snapshot_gl_shader_pop_texture       (GtkSnapshot            *snapshot);
+GDK_AVAILABLE_IN_ALL
 void            gtk_snapshot_pop                        (GtkSnapshot            *snapshot);
-
 GDK_AVAILABLE_IN_ALL
 void            gtk_snapshot_save                       (GtkSnapshot            *snapshot);
 GDK_AVAILABLE_IN_ALL


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