[gtksourceview/wip/improve-gutter-perfs: 9/10] Don't always invalidate the whole gutter



commit ee0504d0b64ff9732f3285c16655e44771b9ced8
Author: Sébastien Wilmet <swilmet gnome org>
Date:   Fri Sep 12 14:55:41 2014 +0200

    Don't always invalidate the whole gutter
    
    Instead, invalidate the whole lines gutter renderer only when needed.
    
    WARNING: during file loading the gutter is redrawn a lot. There is a cycle:
    redraw gutter -> invalidate gutter -> load chunk of text -> draw gutter ->
    invalidate gutter -> etc.
    Instead of taking the total line count of the buffer, a solution is to compare
    the last visible line number, but it's slower.
    
    This should prevent degenerate cases where the gutter window is being
    continuously invalidated during certain operations, for example search.
    
    This is needed to have decent search performances on Mac OS X (but this
    is not the root cause why the search was really slow on OS X, it is
    probably because drawing is much slower than on Linux).

 gtksourceview/gtksourcegutter.c              |   42 ++-------------------
 gtksourceview/gtksourcegutterrendererlines.c |   50 ++++++++++++++++++++++++++
 2 files changed, 54 insertions(+), 38 deletions(-)
---
diff --git a/gtksourceview/gtksourcegutter.c b/gtksourceview/gtksourcegutter.c
index 978a5e0..8b55d52 100644
--- a/gtksourceview/gtksourcegutter.c
+++ b/gtksourceview/gtksourcegutter.c
@@ -395,48 +395,13 @@ calculate_gutter_size (GtkSourceGutter  *gutter,
 }
 
 static void
-window_invalidate_handler (GdkWindow      *window,
-                          cairo_region_t *region)
-{
-       cairo_rectangle_int_t rect;
-
-       /* Always invalidate the whole window.
-        * When the text is modified in a GtkTextBuffer, GtkTextView tries to
-        * redraw the smallest required region. But the information displayed in
-        * the gutter may become invalid in a bigger region.
-        * See https://bugzilla.gnome.org/show_bug.cgi?id=732418 for an example
-        * where line numbers are not updated correctly when splitting a wrapped
-        * line.
-        * The performances should not be a big problem here. Correctness is
-        * more important than performances.
-        */
-
-       rect.x = 0;
-       rect.y = 0;
-       rect.width = gdk_window_get_width (window);
-       rect.height = gdk_window_get_height (window);
-
-       cairo_region_union_rectangle (region, &rect);
-}
-
-static void
 update_gutter_size (GtkSourceGutter *gutter)
 {
-       gint width;
-       GdkWindow *window;
-
-       width = calculate_gutter_size (gutter, NULL);
+       gint width = calculate_gutter_size (gutter, NULL);
 
        gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (gutter->priv->view),
                                              gutter->priv->window_type,
                                              width);
-
-       window = get_window (gutter);
-
-       if (window != NULL)
-       {
-               gdk_window_set_invalidate_handler (window, window_invalidate_handler);
-       }
 }
 
 static gboolean
@@ -1226,6 +1191,9 @@ on_view_draw (GtkSourceView   *view,
                gtk_text_iter_forward_line (&start);
        }
 
+       /* Allow to call queue_redraw() in ::end. */
+       gutter->priv->is_drawing = FALSE;
+
        for (item = gutter->priv->renderers; item != NULL; item = g_list_next (item))
        {
                Renderer *renderer = item->data;
@@ -1241,8 +1209,6 @@ on_view_draw (GtkSourceView   *view,
        g_array_free (heights, TRUE);
        g_array_free (sizes, TRUE);
 
-       gutter->priv->is_drawing = FALSE;
-
        gtk_style_context_restore (style_context);
 
        return FALSE;
diff --git a/gtksourceview/gtksourcegutterrendererlines.c b/gtksourceview/gtksourcegutterrendererlines.c
index ca65e07..5670dba 100644
--- a/gtksourceview/gtksourcegutterrendererlines.c
+++ b/gtksourceview/gtksourcegutterrendererlines.c
@@ -25,6 +25,7 @@
 struct _GtkSourceGutterRendererLinesPrivate
 {
        gint num_line_digits;
+       gint prev_line_count;
 };
 
 G_DEFINE_TYPE_WITH_PRIVATE (GtkSourceGutterRendererLines, gtk_source_gutter_renderer_lines, 
GTK_SOURCE_TYPE_GUTTER_RENDERER_TEXT)
@@ -117,6 +118,8 @@ gutter_renderer_change_buffer (GtkSourceGutterRenderer *renderer,
 
                recalculate_size (lines);
        }
+
+       lines->priv->prev_line_count = 0;
 }
 
 static void
@@ -189,6 +192,52 @@ gutter_renderer_query_data (GtkSourceGutterRenderer      *renderer,
 }
 
 static void
+gutter_renderer_end (GtkSourceGutterRenderer *renderer)
+{
+       GtkSourceGutterRendererLines *lines = GTK_SOURCE_GUTTER_RENDERER_LINES (renderer);
+       GtkTextBuffer *buffer = get_buffer (lines);
+
+       if (buffer != NULL)
+       {
+               gint line_count = gtk_text_buffer_get_line_count (buffer);
+
+               /* When the text is modified in a GtkTextBuffer, GtkTextView tries to
+                * redraw the smallest required region. But the information displayed in
+                * the gutter may become invalid in a bigger region.
+                * See https://bugzilla.gnome.org/show_bug.cgi?id=732418 for an example
+                * where line numbers are not updated correctly when splitting a wrapped
+                * line.
+                * The performances should not be a big problem here. Correctness is
+                * more important than performances. It just triggers a second
+                * draw.
+                * The queue_draw() is called in gutter_renderer_end(), because
+                * the first draw is anyway needed to avoid flickering (if the
+                * first draw is not done, there will be a white region in the
+                * gutter during one frame).
+                *
+                * FIXME A better solution would be to add a vfunc in the
+                * GutterRenderer so that the Gutter can ask each renderer for
+                * the invalidation region, before drawing. So that only one
+                * draw is needed, and the solution would be more generic (if
+                * other renderers also need a different invalidation region
+                * than the GtkTextView). But the GutterRendererClass doesn't
+                * have padding for future expansion, so it must wait for
+                * GtkSourceView 4.
+                */
+               if (lines->priv->prev_line_count != line_count)
+               {
+                       lines->priv->prev_line_count = line_count;
+                       gtk_source_gutter_renderer_queue_draw (renderer);
+               }
+       }
+
+       if (GTK_SOURCE_GUTTER_RENDERER_CLASS (gtk_source_gutter_renderer_lines_parent_class)->end != NULL)
+       {
+               GTK_SOURCE_GUTTER_RENDERER_CLASS (gtk_source_gutter_renderer_lines_parent_class)->end 
(renderer);
+       }
+}
+
+static void
 extend_selection_to_line (GtkSourceGutterRendererLines *renderer,
                           GtkTextIter                  *line_start)
 {
@@ -305,6 +354,7 @@ gtk_source_gutter_renderer_lines_class_init (GtkSourceGutterRendererLinesClass *
        GtkSourceGutterRendererClass *renderer_class = GTK_SOURCE_GUTTER_RENDERER_CLASS (klass);
 
        renderer_class->query_data = gutter_renderer_query_data;
+       renderer_class->end = gutter_renderer_end;
        renderer_class->query_activatable = gutter_renderer_query_activatable;
        renderer_class->activate = gutter_renderer_activate;
        renderer_class->change_buffer = gutter_renderer_change_buffer;


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