[gtk/wip/chergert/gskhint] gsk: add GskHintNode




commit 990102f26962e0c5570eb9c3aaabdb7a3ce8ba44
Author: Christian Hergert <chergert redhat com>
Date:   Mon Mar 21 12:30:16 2022 -0700

    gsk: add GskHintNode
    
    The GskHintNode is meant to set flags for the renderer which can be used
    to optimize batch submission. Currently, two flags are supported. One will
    disable caching of textures from the hint-node to all descendants. The
    second will request that the child node be cached as an offscreen texture
    and used to blit on subsequent frames.

 gsk/broadway/gskbroadwayrenderer.c |  10 +++
 gsk/gl/gskglrenderjob.c            |  59 +++++++++++++++-
 gsk/gskenums.h                     |  25 ++++++-
 gsk/gskrendernode.h                |  12 ++++
 gsk/gskrendernodeimpl.c            | 138 +++++++++++++++++++++++++++++++++++++
 gsk/gskrendernodeparser.c          |  88 ++++++++++++++++++++++-
 gsk/gskrendernodeprivate.h         |   2 +-
 gtk/inspector/recorder.c           |  19 +++++
 8 files changed, 347 insertions(+), 6 deletions(-)
---
diff --git a/gsk/broadway/gskbroadwayrenderer.c b/gsk/broadway/gskbroadwayrenderer.c
index 5ac62e7ee0..833bfd1337 100644
--- a/gsk/broadway/gskbroadwayrenderer.c
+++ b/gsk/broadway/gskbroadwayrenderer.c
@@ -306,6 +306,11 @@ collect_reused_child_nodes (GskRenderer *renderer,
                            gsk_debug_node_get_child (node));
       break;
 
+    case GSK_HINT_NODE:
+      collect_reused_node (renderer,
+                           gsk_hint_node_get_child (node));
+      break;
+
       /* Generic nodes */
 
     case GSK_CONTAINER_NODE:
@@ -796,6 +801,11 @@ gsk_broadway_renderer_add_node (GskRenderer *renderer,
         }
       return;
 
+    case GSK_HINT_NODE:
+      gsk_broadway_renderer_add_node (renderer,
+                                      gsk_hint_node_get_child (node), offset_x, offset_y, clip_bounds);
+      return;
+
       /* Generic nodes */
 
     case GSK_CONTAINER_NODE:
diff --git a/gsk/gl/gskglrenderjob.c b/gsk/gl/gskglrenderjob.c
index 30c6cd7a41..bca221d962 100644
--- a/gsk/gl/gskglrenderjob.c
+++ b/gsk/gl/gskglrenderjob.c
@@ -173,6 +173,11 @@ struct _GskGLRenderJob
    * looking at the format of the framebuffer we are rendering on.
    */
   int target_format;
+
+  /* If caching render nodes is inhibited from a hint node through
+   * all of the descendants.
+   */
+  int inhibit_cache_count;
 };
 
 typedef struct _GskGLRenderOffscreen
@@ -2903,6 +2908,9 @@ is_non_branching (const GskRenderNode *node)
     case GSK_DEBUG_NODE:
       return is_non_branching (gsk_debug_node_get_child (node));
 
+    case GSK_HINT_NODE:
+      return is_non_branching (gsk_hint_node_get_child (node));
+
     case GSK_CONTAINER_NODE:
       return gsk_container_node_get_n_children (node) == 1 &&
              is_non_branching (gsk_container_node_get_child (node, 0));
@@ -3646,6 +3654,51 @@ gsk_gl_render_job_visit_repeat_node (GskGLRenderJob      *job,
   gsk_gl_render_job_end_draw (job);
 }
 
+static void
+gsk_gl_render_job_visit_hint_node (GskGLRenderJob      *job,
+                                   const GskRenderNode *node)
+{
+  const GskRenderNode *child = gsk_hint_node_get_child (node);
+  GskRenderHints hints = gsk_hint_node_get_hints (node);
+  gboolean never_cache = !!(hints & GSK_RENDER_HINTS_NEVER_CACHE);
+  gboolean force_cache = !!(hints & GSK_RENDER_HINTS_FORCE_CACHE);
+
+  job->inhibit_cache_count += never_cache;
+
+  if (force_cache && job->inhibit_cache_count == 0)
+    {
+      GskGLRenderOffscreen offscreen = {0};
+
+      offscreen.bounds = &child->bounds;
+      offscreen.reset_clip = TRUE;
+      offscreen.force_offscreen = TRUE;
+
+      /* gsk_gl_render_job_visit_node_with_offscreen() will cache the texture
+       * for @child and if already cached, re-use it without further rendering.
+       */
+      if (!gsk_gl_render_job_visit_node_with_offscreen (job, child, &offscreen))
+        g_assert_not_reached ();
+
+      g_assert (offscreen.texture_id);
+      g_assert (offscreen.was_offscreen == TRUE);
+
+      gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, blit));
+      gsk_gl_program_set_uniform_texture (job->current_program,
+                                          UNIFORM_SHARED_SOURCE, 0,
+                                          GL_TEXTURE_2D,
+                                          GL_TEXTURE0,
+                                          offscreen.texture_id);
+      gsk_gl_render_job_draw_offscreen (job, &node->bounds, &offscreen);
+      gsk_gl_render_job_end_draw (job);
+    }
+  else
+    {
+      gsk_gl_render_job_visit_node (job, child);
+    }
+
+  job->inhibit_cache_count -= never_cache;
+}
+
 static void
 gsk_gl_render_job_visit_node (GskGLRenderJob      *job,
                               const GskRenderNode *node)
@@ -3757,6 +3810,10 @@ gsk_gl_render_job_visit_node (GskGLRenderJob      *job,
       gsk_gl_render_job_visit_node (job, gsk_debug_node_get_child (node));
     break;
 
+    case GSK_HINT_NODE:
+      gsk_gl_render_job_visit_hint_node (job, node);
+    break;
+
     case GSK_GL_SHADER_NODE:
       gsk_gl_render_job_visit_gl_shader_node (job, node);
     break;
@@ -4000,7 +4057,7 @@ gsk_gl_render_job_visit_node_with_offscreen (GskGLRenderJob       *job,
 
   init_full_texture_region (offscreen);
 
-  if (!offscreen->do_not_cache)
+  if (!offscreen->do_not_cache && job->inhibit_cache_count == 0)
     gsk_gl_driver_cache_texture (job->driver, &key, offscreen->texture_id);
 
   return TRUE;
diff --git a/gsk/gskenums.h b/gsk/gskenums.h
index 0dfeb0a0ea..5199666518 100644
--- a/gsk/gskenums.h
+++ b/gsk/gskenums.h
@@ -50,7 +50,8 @@
  * @GSK_BLUR_NODE: A node that applies a blur
  * @GSK_DEBUG_NODE: Debug information that does not affect the rendering
  * @GSK_GL_SHADER_NODE: A node that uses OpenGL fragment shaders to render
- 
+ * @GSK_HINT_NODE: A node that provides hints to the renderer
+
  * The type of a node determines what the node is rendering.
  */
 typedef enum {
@@ -79,7 +80,8 @@ typedef enum {
   GSK_TEXT_NODE,
   GSK_BLUR_NODE,
   GSK_DEBUG_NODE,
-  GSK_GL_SHADER_NODE
+  GSK_GL_SHADER_NODE,
+  GSK_HINT_NODE
 } GskRenderNodeType;
 
 /**
@@ -251,5 +253,24 @@ typedef enum
   GSK_GL_UNIFORM_TYPE_VEC4,
 } GskGLUniformType;
 
+/**
+ * GskRenderHints:
+ * @GSK_RENDER_HINTS_NEVER_CACHE: Hints to the renderer that it should never
+ *   cache the contents of this or any descendant node.
+ * @GSK_RENDER_HINTS_FORCE_CACHE: Hints to the renderer that it should try
+ *   to cache the conents of the node as it is likely to be reused in future
+ *   render requests.
+ *
+ * This defines the hints that may be provided to renderers to instruct
+ * preferred behavior.
+ *
+ * Since: 4.8
+ */
+typedef enum
+{
+  GSK_RENDER_HINTS_NEVER_CACHE = 1 << 0,
+  GSK_RENDER_HINTS_FORCE_CACHE = 1 << 1,
+} GskRenderHints;
+
 
 #endif /* __GSK_TYPES_H__ */
diff --git a/gsk/gskrendernode.h b/gsk/gskrendernode.h
index ba05d606f9..cb374e7bb7 100644
--- a/gsk/gskrendernode.h
+++ b/gsk/gskrendernode.h
@@ -164,6 +164,7 @@ GskRenderNode *         gsk_render_node_deserialize             (GBytes
 #define GSK_TYPE_TEXT_NODE                      (gsk_text_node_get_type())
 #define GSK_TYPE_BLUR_NODE                      (gsk_blur_node_get_type())
 #define GSK_TYPE_GL_SHADER_NODE                 (gsk_gl_shader_node_get_type())
+#define GSK_TYPE_HINT_NODE                      (gsk_hint_node_get_type())
 
 typedef struct _GskDebugNode                    GskDebugNode;
 typedef struct _GskColorNode                    GskColorNode;
@@ -190,6 +191,7 @@ typedef struct _GskCrossFadeNode                GskCrossFadeNode;
 typedef struct _GskTextNode                     GskTextNode;
 typedef struct _GskBlurNode                     GskBlurNode;
 typedef struct _GskGLShaderNode                 GskGLShaderNode;
+typedef struct _GskHintNode                     GskHintNode;
 
 GDK_AVAILABLE_IN_ALL
 GType                   gsk_debug_node_get_type                 (void) G_GNUC_CONST;
@@ -534,6 +536,16 @@ GBytes *                gsk_gl_shader_node_get_args             (const GskRender
 GDK_AVAILABLE_IN_ALL
 GskGLShader *           gsk_gl_shader_node_get_shader           (const GskRenderNode      *node) G_GNUC_PURE;
 
+GDK_AVAILABLE_IN_4_8
+GType                   gsk_hint_node_get_type                  (void) G_GNUC_CONST;
+GDK_AVAILABLE_IN_4_8
+GskRenderNode *         gsk_hint_node_new                       (GskRenderNode            *child,
+                                                                 GskRenderHints            hints);
+GDK_AVAILABLE_IN_4_8
+GskRenderNode *         gsk_hint_node_get_child                 (const GskRenderNode      *node) G_GNUC_PURE;
+GDK_AVAILABLE_IN_4_8
+GskRenderHints          gsk_hint_node_get_hints                 (const GskRenderNode      *node) G_GNUC_PURE;
+
 /**
  * GSK_VALUE_HOLDS_RENDER_NODE:
  * @value: a `GValue`
diff --git a/gsk/gskrendernodeimpl.c b/gsk/gskrendernodeimpl.c
index 384de83bad..09fde39057 100644
--- a/gsk/gskrendernodeimpl.c
+++ b/gsk/gskrendernodeimpl.c
@@ -5056,6 +5056,127 @@ gsk_debug_node_get_message (const GskRenderNode *node)
   return self->message;
 }
 
+/*** GSK_HINT_NODE ***/
+
+/**
+ * GskHintNode:
+ *
+ * A render node that provides hints to the renderer about how a child node
+ * should be processed.
+ */
+struct _GskHintNode
+{
+  GskRenderNode render_node;
+
+  GskRenderNode *child;
+  GskRenderHints hints;
+};
+
+static void
+gsk_hint_node_finalize (GskRenderNode *node)
+{
+  GskHintNode *self = (GskHintNode *) node;
+  GskRenderNodeClass *parent_class = g_type_class_peek (g_type_parent (GSK_TYPE_HINT_NODE));
+
+  gsk_render_node_unref (self->child);
+
+  parent_class->finalize (node);
+}
+
+static void
+gsk_hint_node_draw (GskRenderNode *node,
+                    cairo_t       *cr)
+{
+  GskHintNode *self = (GskHintNode *) node;
+
+  gsk_render_node_draw (self->child, cr);
+}
+
+static gboolean
+gsk_hint_node_can_diff (const GskRenderNode *node1,
+                        const GskRenderNode *node2)
+{
+  GskHintNode *self1 = (GskHintNode *) node1;
+  GskHintNode *self2 = (GskHintNode *) node2;
+
+  return gsk_render_node_can_diff (self1->child, self2->child);
+}
+
+static void
+gsk_hint_node_diff (GskRenderNode  *node1,
+                    GskRenderNode  *node2,
+                    cairo_region_t *region)
+{
+  GskHintNode *self1 = (GskHintNode *) node1;
+  GskHintNode *self2 = (GskHintNode *) node2;
+
+  gsk_render_node_diff (self1->child, self2->child, region);
+}
+
+/**
+ * gsk_hint_node_new:
+ * @child: The child to add hint info for
+ * @hints: The hints for the child
+ *
+ * Creates a `GskRenderNode` that will add hints about how the child
+ * should be processed by the renderer.
+ *
+ * Returns: (transfer full) (type GskHintNode): A new `GskRenderNode`
+ */
+GskRenderNode *
+gsk_hint_node_new (GskRenderNode  *child,
+                   GskRenderHints  hints)
+{
+  GskHintNode *self;
+  GskRenderNode *node;
+
+  g_return_val_if_fail (GSK_IS_RENDER_NODE (child), NULL);
+
+  self = gsk_render_node_alloc (GSK_HINT_NODE);
+  node = (GskRenderNode *) self;
+
+  self->child = gsk_render_node_ref (child);
+  self->hints = hints;
+
+  graphene_rect_init_from_rect (&node->bounds, &child->bounds);
+
+  node->prefers_high_depth = gsk_render_node_prefers_high_depth (child);
+
+  return node;
+}
+
+/**
+ * gsk_hint_node_get_child:
+ * @node: (type GskHintNode): a hint `GskRenderNode`
+ *
+ * Gets the child node that is getting drawn by the given @node.
+ *
+ * Returns: (transfer none): the child `GskRenderNode`
+ **/
+GskRenderNode *
+gsk_hint_node_get_child (const GskRenderNode *node)
+{
+  const GskHintNode *self = (const GskHintNode *) node;
+
+  return self->child;
+}
+
+/**
+ * gsk_hint_node_get_hints:
+ * @node: (type GskHintNode): a hint `GskRenderNode`
+ *
+ * Gets the hints applied to this node
+ *
+ * Returns: The hints for the node
+ **/
+GskRenderHints
+gsk_hint_node_get_hints (const GskRenderNode *node)
+{
+  const GskHintNode *self = (const GskHintNode *) node;
+
+  return self->hints;
+}
+
 /*** GSK_GL_SHADER_NODE ***/
 
 /**
@@ -5298,6 +5419,7 @@ GSK_DEFINE_RENDER_NODE_TYPE (gsk_cross_fade_node, GSK_CROSS_FADE_NODE)
 GSK_DEFINE_RENDER_NODE_TYPE (gsk_text_node, GSK_TEXT_NODE)
 GSK_DEFINE_RENDER_NODE_TYPE (gsk_blur_node, GSK_BLUR_NODE)
 GSK_DEFINE_RENDER_NODE_TYPE (gsk_gl_shader_node, GSK_GL_SHADER_NODE)
+GSK_DEFINE_RENDER_NODE_TYPE (gsk_hint_node, GSK_HINT_NODE)
 GSK_DEFINE_RENDER_NODE_TYPE (gsk_debug_node, GSK_DEBUG_NODE)
 
 static void
@@ -5687,6 +5809,22 @@ gsk_render_node_init_types_once (void)
     gsk_render_node_types[GSK_GL_SHADER_NODE] = node_type;
   }
 
+  {
+    const GskRenderNodeTypeInfo node_info =
+    {
+      GSK_HINT_NODE,
+      sizeof (GskHintNode),
+      NULL,
+      gsk_hint_node_finalize,
+      gsk_hint_node_draw,
+      gsk_hint_node_can_diff,
+      gsk_hint_node_diff,
+    };
+
+    GType node_type = gsk_render_node_type_register_static (I_("GskHintNode"), &node_info);
+    gsk_render_node_types[GSK_HINT_NODE] = node_type;
+  }
+
   {
     const GskRenderNodeTypeInfo node_info =
     {
diff --git a/gsk/gskrendernodeparser.c b/gsk/gskrendernodeparser.c
index 11d5e61d70..e4a979cebc 100644
--- a/gsk/gskrendernodeparser.c
+++ b/gsk/gskrendernodeparser.c
@@ -416,6 +416,29 @@ clear_string (gpointer inout_string)
   g_clear_pointer ((char **) inout_string, g_free);
 }
 
+static gboolean
+parse_bool (GtkCssParser *parser,
+            gpointer      out_bool)
+{
+  if (gtk_css_parser_try_ident (parser, "true") ||
+      gtk_css_parser_try_ident (parser, "True") ||
+      gtk_css_parser_try_ident (parser, "TRUE"))
+    {
+      *(gboolean *) out_bool = TRUE;
+      return TRUE;
+    }
+
+  if (gtk_css_parser_try_ident (parser, "false") ||
+      gtk_css_parser_try_ident (parser, "False") ||
+      gtk_css_parser_try_ident (parser, "FALSE"))
+    {
+      *(gboolean *) out_bool = FALSE;
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
 static gboolean
 parse_stops (GtkCssParser *parser,
              gpointer      out_stops)
@@ -1102,7 +1125,7 @@ parse_conic_gradient_node (GtkCssParser *parser)
       g_array_append_val (stops, to);
     }
 
-  result = gsk_conic_gradient_node_new (&bounds, &center, rotation, 
+  result = gsk_conic_gradient_node_new (&bounds, &center, rotation,
                                         (GskColorStop *) stops->data, stops->len);
 
   g_array_free (stops, TRUE);
@@ -1388,7 +1411,7 @@ parse_cairo_node (GtkCssParser *parser)
   parse_declarations (parser, declarations, G_N_ELEMENTS(declarations));
 
   node = gsk_cairo_node_new (&bounds);
-  
+
   if (surface != NULL)
     {
       cairo_t *cr = gsk_cairo_node_get_draw_context (node);
@@ -1816,6 +1839,37 @@ parse_debug_node (GtkCssParser *parser)
   return result;
 }
 
+static GskRenderNode *
+parse_hint_node (GtkCssParser *parser)
+{
+  gboolean force_cache = FALSE;
+  gboolean never_cache = FALSE;
+  GskRenderHints hints = 0;
+  GskRenderNode *child = NULL;
+  const Declaration declarations[] = {
+    { "never-cache", parse_bool, NULL, &never_cache },
+    { "force-cache", parse_bool, NULL, &force_cache },
+    { "child", parse_node, clear_node, &child },
+  };
+  GskRenderNode *result;
+
+  parse_declarations (parser, declarations, G_N_ELEMENTS(declarations));
+  if (child == NULL)
+    child = create_default_render_node ();
+
+  if (never_cache)
+    hints |= GSK_RENDER_HINTS_NEVER_CACHE;
+
+  if (force_cache)
+    hints |= GSK_RENDER_HINTS_FORCE_CACHE;
+
+  result = gsk_hint_node_new (child, hints);
+
+  gsk_render_node_unref (child);
+
+  return result;
+}
+
 static gboolean
 parse_node (GtkCssParser *parser,
             gpointer      out_node)
@@ -1849,6 +1903,7 @@ parse_node (GtkCssParser *parser,
     { "texture", parse_texture_node },
     { "transform", parse_transform_node },
     { "glshader", parse_glshader_node },
+    { "hint", parse_hint_node },
   };
   GskRenderNode **node_p = out_node;
   guint i;
@@ -2158,6 +2213,17 @@ append_point_param (Printer                *p,
   g_string_append_c (p->str, '\n');
 }
 
+static void
+append_bool_param (Printer    *p,
+                   const char *param_name,
+                   gboolean    value)
+{
+  _indent (p);
+  g_string_append_printf (p->str, "%s: %s", param_name, value ? "true" : "false");
+  g_string_append_c (p->str, ';');
+  g_string_append_c (p->str, '\n');
+}
+
 static void
 append_string_param (Printer    *p,
                      const char *param_name,
@@ -2765,6 +2831,24 @@ render_node_print (Printer       *p,
       }
       break;
 
+    case GSK_HINT_NODE:
+      {
+        GskRenderHints hints = gsk_hint_node_get_hints (node);
+
+        start_node (p, "hint");
+
+        if (hints & GSK_RENDER_HINTS_NEVER_CACHE)
+          append_bool_param (p, "never-cache", TRUE);
+
+        if (hints & GSK_RENDER_HINTS_FORCE_CACHE)
+          append_bool_param (p, "force-cache", TRUE);
+
+        append_node_param (p, "child", gsk_hint_node_get_child (node));
+
+        end_node (p);
+      }
+      break;
+
     case GSK_BLUR_NODE:
       {
         start_node (p, "blur");
diff --git a/gsk/gskrendernodeprivate.h b/gsk/gskrendernodeprivate.h
index cdb75afd2f..ed0557bfb8 100644
--- a/gsk/gskrendernodeprivate.h
+++ b/gsk/gskrendernodeprivate.h
@@ -13,7 +13,7 @@ typedef struct _GskRenderNodeClass GskRenderNodeClass;
  * We don't add an "n-types" value to avoid having to handle
  * it in every single switch.
  */
-#define GSK_RENDER_NODE_TYPE_N_TYPES    (GSK_GL_SHADER_NODE + 1)
+#define GSK_RENDER_NODE_TYPE_N_TYPES    (GSK_HINT_NODE + 1)
 
 extern GType gsk_render_node_types[];
 
diff --git a/gtk/inspector/recorder.c b/gtk/inspector/recorder.c
index 48ff6105b7..090c23b1af 100644
--- a/gtk/inspector/recorder.c
+++ b/gtk/inspector/recorder.c
@@ -232,6 +232,9 @@ create_list_model_for_render_node (GskRenderNode *node)
 
     case GSK_DEBUG_NODE:
       return create_render_node_list_model ((GskRenderNode *[1]) { gsk_debug_node_get_child (node) }, 1);
+
+    case GSK_HINT_NODE:
+      return create_render_node_list_model ((GskRenderNode *[1]) { gsk_hint_node_get_child (node) }, 1);
     }
 }
 
@@ -310,6 +313,8 @@ node_type_name (GskRenderNodeType type)
       return "Blur";
     case GSK_GL_SHADER_NODE:
       return "GL Shader";
+    case GSK_HINT_NODE:
+      return "Hint";
     }
 }
 
@@ -343,6 +348,7 @@ node_name (GskRenderNode *node)
     case GSK_TEXT_NODE:
     case GSK_BLUR_NODE:
     case GSK_GL_SHADER_NODE:
+    case GSK_HINT_NODE:
       return g_strdup (node_type_name (gsk_render_node_get_node_type (node)));
 
     case GSK_DEBUG_NODE:
@@ -1216,6 +1222,19 @@ populate_render_node_properties (GtkListStore  *store,
       add_text_row (store, "Message", gsk_debug_node_get_message (node));
       break;
 
+    case GSK_HINT_NODE:
+      {
+        GskRenderHints hints = gsk_hint_node_get_hints (node);
+
+        if (hints & GSK_RENDER_HINTS_NEVER_CACHE)
+          add_text_row (store, "Never Cache", "True");
+
+        if (hints & GSK_RENDER_HINTS_FORCE_CACHE)
+          add_text_row (store, "Force Cache", "True");
+
+        break;
+      }
+
     case GSK_SHADOW_NODE:
       {
         int i;


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