textview: cache paragraph render nodes
authorChristian Hergert <chergert@redhat.com>
Tue, 8 Oct 2019 18:35:49 +0000 (11:35 -0700)
committerChristian Hergert <chergert@redhat.com>
Tue, 8 Oct 2019 18:44:27 +0000 (11:44 -0700)
We can avoid recreating a number of text nodes from render_para() on
sub-sequent runs if we cache the rendernode instead of just the
PangoLayout.

When used with GtkSourceMap, this can yield a ~7 FPS improvement during
smooth scrolling at the cost of some more memory.

gtk/gtktextlayout.c
gtk/gtktextlayoutprivate.h
gtk/gtktextlinedisplaycache.c

index f51dd42fcb1adac43f4e3ce2aed91c36cd17399e..d7c270a9dc93679642dc94d9ea1a90a3075316cd 100644 (file)
@@ -2643,6 +2643,7 @@ gtk_text_line_display_finalize (GtkTextLineDisplay *display)
 
   g_clear_object (&display->layout);
   g_clear_pointer (&display->cursors, g_array_unref);
+  g_clear_pointer (&display->node, gsk_render_node_unref);
 }
 
 GtkTextLineDisplay *
@@ -3862,7 +3863,6 @@ render_para (GskPangoRenderer   *crenderer,
   int screen_width;
   GdkRGBA *selection = NULL;
   gboolean first = TRUE;
-  graphene_point_t point = { 0, offset_y };
 
   g_return_if_fail (GTK_IS_TEXT_VIEW (crenderer->widget));
 
@@ -3880,8 +3880,12 @@ render_para (GskPangoRenderer   *crenderer,
       gtk_style_context_restore (context);
     }
 
-  gtk_snapshot_save (crenderer->snapshot);
-  gtk_snapshot_translate (crenderer->snapshot, &point);
+  if (offset_y)
+    {
+      gtk_snapshot_save (crenderer->snapshot);
+      gtk_snapshot_translate (crenderer->snapshot,
+                              &GRAPHENE_POINT_INIT (0, offset_y));
+    }
 
   do
     {
@@ -4054,7 +4058,8 @@ render_para (GskPangoRenderer   *crenderer,
     }
   while (pango_layout_iter_next_line (iter));
 
-  gtk_snapshot_restore (crenderer->snapshot);
+  if (offset_y)
+    gtk_snapshot_restore (crenderer->snapshot);
 
   gdk_rgba_free (selection);
   pango_layout_iter_free (iter);
@@ -4146,9 +4151,27 @@ gtk_text_layout_snapshot (GtkTextLayout      *layout,
                 }
             }
 
-          render_para (crenderer, offset_y, line_display,
-                       selection_start_index, selection_end_index,
-                       cursor_alpha);
+          if (line_display->node == NULL)
+            {
+              GtkSnapshot *sub = gtk_snapshot_new ();
+
+              crenderer->snapshot = sub;
+              render_para (crenderer, 0, line_display,
+                           selection_start_index, selection_end_index,
+                           cursor_alpha);
+              crenderer->snapshot = snapshot;
+
+              line_display->node = gtk_snapshot_free_to_node (sub);
+            }
+
+          if (line_display->node != NULL)
+            {
+              gtk_snapshot_save (crenderer->snapshot);
+              gtk_snapshot_translate (crenderer->snapshot,
+                                      &GRAPHENE_POINT_INIT (0, offset_y));
+              gtk_snapshot_append_node (crenderer->snapshot, line_display->node);
+              gtk_snapshot_restore (crenderer->snapshot);
+            }
 
           /* We paint the cursors last, because they overlap another chunk
            * and need to appear on top.
index 99d4f812e85379bc15b7f11ae5446119fbb8503d..f408d199dea8c3b156b51ff70582a45056f402ce 100644 (file)
@@ -216,6 +216,9 @@ struct _GtkTextAttrAppearance
 struct _GtkTextLineDisplay
 {
   PangoLayout *layout;
+
+  GskRenderNode *node;
+
   GArray *cursors;      /* indexes of cursors in the PangoLayout */
 
   /* GSequenceIter backpointer for use within cache */
index 2fe174c0f6d43c7ea7434014272a797a1142f90a..c15d8907d6cc6ff51f013c0605662dbc1324e4db 100644 (file)
@@ -234,6 +234,7 @@ gtk_text_line_display_cache_invalidate_display (GtkTextLineDisplayCache *cache,
   if (cursors_only)
     {
       g_clear_pointer (&display->cursors, g_array_unref);
+      g_clear_pointer (&display->node, gsk_render_node_unref);
       display->cursors_invalid = TRUE;
       display->has_block_cursor = FALSE;
     }