[gtk/matthiasc/glshader-node: 11/18] snapshot: Drop n_children from push_gl_shader and use custom pop




commit 48cb5ca3db0a86fada9ac200786c583a65da20a0
Author: Alexander Larsson <alexl redhat com>
Date:   Mon Sep 28 12:59:27 2020 +0200

    snapshot: Drop n_children from push_gl_shader and use custom pop
    
    We now always assume you pass the right amount of children for the
    shader, and each such child is followed by
    gtk_snapshot_gl_shader_pop_texture() and then a final
    gtk_snapshot_pop().
    
    This means we handle the 0 child case ok, and that we can validate
    that the number of pops of various types are correct.

 demos/gtk-demo/gskshaderpaintable.c |   7 +-
 demos/gtk-demo/gtkshaderbin.c       |   4 +-
 demos/gtk-demo/gtkshaderstack.c     |   8 +-
 gtk/gtksnapshot.c                   | 174 ++++++++++++++++++++++++++----------
 gtk/gtksnapshot.h                   |   6 +-
 5 files changed, 136 insertions(+), 63 deletions(-)
---
diff --git a/demos/gtk-demo/gskshaderpaintable.c b/demos/gtk-demo/gskshaderpaintable.c
index c441714113..a6a6832365 100644
--- a/demos/gtk-demo/gskshaderpaintable.c
+++ b/demos/gtk-demo/gskshaderpaintable.c
@@ -76,12 +76,7 @@ gsk_shader_paintable_paintable_snapshot (GdkPaintable *paintable,
 {
   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_push_gl_shader (snapshot, self->shader, &GRAPHENE_RECT_INIT(0, 0, width, height), g_bytes_ref 
(self->uniform_data));
   gtk_snapshot_pop (snapshot);
 }
 
diff --git a/demos/gtk-demo/gtkshaderbin.c b/demos/gtk-demo/gtkshaderbin.c
index c728458090..14f5651ac6 100644
--- a/demos/gtk-demo/gtkshaderbin.c
+++ b/demos/gtk-demo/gtkshaderbin.c
@@ -202,9 +202,9 @@ gtk_shader_bin_snapshot (GtkWidget   *widget,
                                        &GRAPHENE_RECT_INIT(0, 0, width, height),
                                        gsk_gl_shader_format_args (self->active_shader->shader,
                                                                   "u_time", &self->time,
-                                                                  NULL),
-                                       1);
+                                                                  NULL));
           gtk_widget_snapshot_child (widget, self->child, snapshot);
+          gtk_snapshot_gl_shader_pop_texture (snapshot);
           gtk_snapshot_pop (snapshot);
 
           return;
diff --git a/demos/gtk-demo/gtkshaderstack.c b/demos/gtk-demo/gtkshaderstack.c
index 32dbc1e658..c84aa232f0 100644
--- a/demos/gtk-demo/gtkshaderstack.c
+++ b/demos/gtk-demo/gtkshaderstack.c
@@ -246,13 +246,13 @@ gtk_shader_stack_snapshot (GtkWidget   *widget,
                                        &GRAPHENE_RECT_INIT(0, 0, width, height),
                                        gsk_gl_shader_format_args (self->shader,
                                                                   "progress", &progress,
-                                                                  NULL),
-                                       2);
+                                                                  NULL));
 
           gtk_widget_snapshot_child (widget, current, snapshot);
-          gtk_snapshot_pop (snapshot); /* current child */
+          gtk_snapshot_gl_shader_pop_texture (snapshot); /* current child */
           gtk_widget_snapshot_child (widget, next, snapshot);
-          gtk_snapshot_pop (snapshot); /* next child */
+          gtk_snapshot_gl_shader_pop_texture (snapshot); /* next child */
+          gtk_snapshot_pop (snapshot);
         }
       else
         {
diff --git a/gtk/gtksnapshot.c b/gtk/gtksnapshot.c
index 1f5af89655..685af380b9 100644
--- a/gtk/gtksnapshot.c
+++ b/gtk/gtksnapshot.c
@@ -95,10 +95,14 @@ struct _GtkSnapshotState {
       GskGLShader *shader;
       GBytes *args;
       graphene_rect_t bounds;
-      int n_children;
-      int node_idx;
       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;
@@ -238,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)
 {
@@ -823,59 +839,88 @@ gtk_snapshot_push_clip (GtkSnapshot           *snapshot,
 static GskRenderNode *
 gtk_snapshot_collect_gl_shader (GtkSnapshot      *snapshot,
                                 GtkSnapshotState *state,
-                                GskRenderNode   **nodes,
-                                guint             n_nodes)
+                                GskRenderNode   **collected_nodes,
+                                guint             n_collected_nodes)
 {
-  GskRenderNode *shader_node = NULL, *child_node;
-  GdkRGBA transparent = { 0, 0, 0, 0 };
-
-  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.bounds);
-
-  state->data.glshader.nodes[state->data.glshader.node_idx] = child_node;
+  GskRenderNode *shader_node = NULL;
+  GskRenderNode **nodes;
+  int n_children;
 
-  if (state->data.glshader.node_idx != state->data.glshader.n_children - 1)
-    return NULL; /* Not last */
+  n_children = gsk_gl_shader_get_n_required_textures (state->data.glshader.shader);
+  shader_node = NULL;
 
-  /* This is the last pop */
+  if (n_collected_nodes != 0)
+    g_warning ("Unexpected children when poping gl shader.");
 
-  shader_node = NULL;
+  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,
-                                            &state->data.glshader.nodes[0],
-                                            state->data.glshader.n_children);
-    }
+    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  < state->data.glshader.n_children; i++)
-    gsk_render_node_unref (state->data.glshader.nodes[i]);
+
+  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.
- * @n_children: The number of extra nodes given as argument to the shader as textures.
  *
  * 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
- * input, as per types and offsets defined by the @shader. Normally this is
+ * 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.
@@ -885,10 +930,16 @@ gtk_snapshot_collect_gl_shader (GtkSnapshot      *snapshot,
  * gsk_gl_shader_try_compile_for() to ensure the @shader will work for the renderer
  * before using it.
  *
- * If @n_children > 0, then it is expected that you (after the fallback call
- * gtk_snapshot_pop() @n_children times. Each of these will generate a node that
- * is added as a child to the gl shader node, which in turn will render these to
- * textures and pass as arguments to the shader.
+ * If the shader requires textures (see gsk_gl_shader_get_n_required_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.
  */
@@ -896,14 +947,12 @@ void
 gtk_snapshot_push_gl_shader (GtkSnapshot           *snapshot,
                              GskGLShader           *shader,
                              const graphene_rect_t *bounds,
-                             GBytes                *take_args,
-                             int                    n_children)
+                             GBytes                *take_args)
 {
   GtkSnapshotState *state;
   float scale_x, scale_y, dx, dy;
-  GskRenderNode **nodes;
-  int node_idx;
   graphene_rect_t transformed_bounds;
+  int n_children = gsk_gl_shader_get_n_required_textures (shader);
 
   gtk_snapshot_ensure_affine (snapshot, &scale_x, &scale_y, &dx, &dy);
 
@@ -914,22 +963,19 @@ gtk_snapshot_push_gl_shader (GtkSnapshot           *snapshot,
   state->data.glshader.bounds = transformed_bounds;
   state->data.glshader.shader = g_object_ref (shader);
   state->data.glshader.args = take_args; /* Takes ownership */
-  state->data.glshader.n_children = n_children;
-  nodes = g_new0 (GskRenderNode *, n_children);
-  node_idx = n_children-1; /* We pop in reverse order */
-
-  state->data.glshader.node_idx = node_idx--;
-  state->data.glshader.nodes = nodes;
+  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-1; i++)
+  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);
-      state->data.glshader.node_idx = node_idx--;
-      state->data.glshader.n_children = n_children;
-      state->data.glshader.nodes = nodes;
-      state->data.glshader.bounds = transformed_bounds;
+                                       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;
     }
 }
 
@@ -1433,14 +1479,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 de8f554159..19e0045b17 100644
--- a/gtk/gtksnapshot.h
+++ b/gtk/gtksnapshot.h
@@ -102,11 +102,11 @@ GDK_AVAILABLE_IN_ALL
 void            gtk_snapshot_push_gl_shader             (GtkSnapshot            *snapshot,
                                                          GskGLShader            *shader,
                                                          const graphene_rect_t  *bounds,
-                                                         GBytes                 *take_args,
-                                                         int                     n_children);
+                                                         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]