patch 9.2.0010: Using Wayland compositor is still slow

Commit: 
https://github.com/vim/vim/commit/ac9426bf549a5dea853ae02fa6c5dba0ef3beb95
Author: Christoffer Aasted <[email protected]>
Date:   Sun Feb 15 17:21:03 2026 +0000

    patch 9.2.0010: Using Wayland compositor is still slow
    
    Problem:  Using the Wayland backend in GTK, rendering remains slow due
              to per-line redraws, unnecessary Cairo push/pop groups, and
              scroll operations that allocate new surfaces repeatedly.
    Solution: Improve rendering performance (Christoffer Aasted).
    
    This commit does the following:
    - Add gui.is_wayland to detect Wayland backend
    - Avoid blocking the input loop
    - Skip early redraws; let the compositor handle full-screen redraws
    - Use CAIRO_OPERATOR_SOURCE to overwrite instead of blend
    - Reuse scroll source region for destination scroll region
    - Optimize fast scroll-up
    - Remove cairo_push_group/pop_group and cairo_clip in scroll path
      to reduce allocations (~50MB saved on 4K fractional scale)
    
    Since Wayland redraws the entire screen between updates (unlike X11),
    further performance gains are possible by batching drawing in other code
    paths, e.g.:
    - message.c: batch lines to avoid scroll
    - term.c: batch terminal redraws
    
    These could be refactored later to deferred redraw with Wayland.
    
    closes: #19062
    
    Signed-off-by: Christoffer Aasted <[email protected]>
    Signed-off-by: Christian Brabandt <[email protected]>

diff --git a/src/gui.c b/src/gui.c
index 8673ec8d8..9eef90f92 100644
--- a/src/gui.c
+++ b/src/gui.c
@@ -211,7 +211,6 @@ gui_attempt_start(void)
        if (gui_get_x11_windis(&x11_window, &x11_display) == OK)
            set_vim_var_nr(VV_WINDOWID, (long)x11_window);
 # endif
-
        // Display error messages in a dialog now.
        display_errors();
     }
@@ -484,6 +483,9 @@ gui_init_check(void)
     result = OK;
 #else
 # ifdef FEAT_GUI_GTK
+#  ifdef GDK_WINDOWING_WAYLAND
+    gui.is_wayland = FALSE;
+#  endif
     /*
      * Note: Don't call gtk_init_check() before fork, it will be called after
      * the fork. When calling it before fork, it make vim hang for a while.
diff --git a/src/gui.h b/src/gui.h
index da06e2ce0..2069e7486 100644
--- a/src/gui.h
+++ b/src/gui.h
@@ -389,6 +389,9 @@ typedef struct Gui
     char_u     *browse_fname;      // file name from filedlg
 
     guint32    event_time;
+# ifdef GDK_WINDOWING_WAYLAND
+    _Bool      is_wayland;           // active gdk backend in gtk is wayland
+# endif
 #endif // FEAT_GUI_GTK
 
 #if defined(FEAT_GUI_GTK) || defined(FEAT_GUI_MSWIN)
diff --git a/src/gui_gtk_x11.c b/src/gui_gtk_x11.c
index 015a2c7f6..edf6b705d 100644
--- a/src/gui_gtk_x11.c
+++ b/src/gui_gtk_x11.c
@@ -59,6 +59,9 @@ extern void bonobo_dock_item_set_behavior(BonoboDockItem 
*dock_item, BonoboDockI
 #if defined(FEAT_GUI_GTK)
 # if GTK_CHECK_VERSION(3,0,0)
 #  include <gdk/gdkkeysyms-compat.h>
+#  ifdef GDK_WINDOWING_WAYLAND
+#   include <gdk/gdkwayland.h>
+#  endif
 #  include <gtk/gtkx.h>
 # else
 #  include <gdk/gdkkeysyms.h>
@@ -2039,11 +2042,6 @@ scroll_event(GtkWidget *widget,
 # if !GTK_CHECK_VERSION(3,22,0)
     static guint32 last_smooth_event_time;
 # endif
-# define DT_X11     1
-# define DT_WAYLAND 2
-    static int display_type;
-    if (!display_type)
-       display_type = gui_mch_get_display() ? DT_X11 : DT_WAYLAND;
 #endif
 
     if (gtk_socket_id != 0 && !gtk_widget_has_focus(widget))
@@ -2101,7 +2099,7 @@ scroll_event(GtkWidget *widget,
 #if GTK_CHECK_VERSION(3,4,0)
     // on x11, despite not requested, when we copy into primary clipboard,
     // we'll get smooth events. Unsmooth ones will also come along.
-    if (event->direction == GDK_SCROLL_SMOOTH && display_type == DT_WAYLAND)
+    if (event->direction == GDK_SCROLL_SMOOTH && gui.is_wayland)
     {
        while (acc_x >= 1.0)
        { // right
@@ -2128,12 +2126,10 @@ scroll_event(GtkWidget *widget,
                    FALSE, vim_modifiers);
        }
     }
-    else if (event->direction == GDK_SCROLL_SMOOTH && display_type == DT_X11)
+    else if (event->direction == GDK_SCROLL_SMOOTH && X_DISPLAY)
        // for X11 we deal with unsmooth events, and so ignore the smooth ones
        ;
     else
-# undef DT_X11
-# undef DT_WAYLAND
 #endif
        gui_send_mouse_event(button, (int)event->x, (int)event->y,
                FALSE, vim_modifiers);
@@ -4004,6 +4000,12 @@ gui_mch_init(void)
     gui.surface = NULL;
 #endif
 
+#ifdef GDK_WINDOWING_WAYLAND
+    GdkDisplay *d = gdk_display_get_default();
+    if (GDK_IS_WAYLAND_DISPLAY(d))
+       gui.is_wayland = TRUE;
+#endif
+
     // Determine which events we will filter.
     gint event_mask =
        GDK_EXPOSURE_MASK |
@@ -6619,6 +6621,11 @@ gui_mch_draw_part_cursor(int w, int h, guicolor_T color)
     void
 gui_mch_update(void)
 {
+#ifdef GDK_WINDOWING_WAYLAND
+    // avoid early redraws; compositor does redraw
+    if (gui.is_wayland)
+       return;
+#endif
     int cnt = 0;       // prevent endless loop
     while (g_main_context_pending(NULL) && !vim_is_input_buf_full()
                                                                && ++cnt < 100)
@@ -6718,7 +6725,14 @@ gui_mch_wait_for_chars(long wtime)
         * situations, sort of race condition).
         */
        if (!input_available())
-           g_main_context_iteration(NULL, TRUE);
+       {
+#ifdef GDK_WINDOWING_WAYLAND
+           if (gui.is_wayland)
+               g_main_context_iteration(NULL, FALSE);
+           else
+#endif
+               g_main_context_iteration(NULL, TRUE);
+       }
 
        // Got char, return immediately
        if (input_available())
@@ -6909,13 +6923,69 @@ gui_gtk_surface_copy_rect(int dest_x, int dest_y,
 {
     cairo_t * const cr = cairo_create(gui.surface);
 
-    cairo_rectangle(cr, dest_x, dest_y, width, height);
-    cairo_clip(cr);
-    cairo_push_group(cr);
-    cairo_set_source_surface(cr, gui.surface, dest_x - src_x, dest_y - src_y);
-    cairo_paint(cr);
-    cairo_pop_group_to_source(cr);
-    cairo_paint(cr);
+# ifdef GDK_WINDOWING_WAYLAND
+    /*
+       Following optimizations are temporary until all callers are refactored
+       to wayland deferred redraw; .. then it could be removed.
+    */
+    static cairo_surface_t *scroll_scratch = NULL;
+    static int scratch_w = 0;
+    static int scratch_h = 0;
+    int last_row = Rows - 1;
+    int last_row_y = last_row * gui.char_height;
+    _Bool last_row_overlap = (dest_y + height) > last_row_y;
+    if (gui.is_wayland && ( !(State & MODE_CMDLINE) || !last_row_overlap) )
+    {
+       /*
+          scrolling up
+          */
+       if (dest_y < src_y)
+       {
+           cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+           cairo_set_source_surface(cr, gui.surface,
+                   src_x - dest_x,
+                   dest_y - src_y);
+           cairo_rectangle(cr, dest_x, dest_y, width, height);
+           cairo_clip(cr);
+           cairo_paint(cr);;
+       }
+       else
+       {
+           //  reusing surface when scrolling, only realloc if larger
+           if (scroll_scratch == NULL || width > scratch_w || height > 
scratch_h)
+           {
+               cairo_surface_destroy(scroll_scratch); // safe even if NULL
+               scroll_scratch = cairo_surface_create_similar(gui.surface,
+                       cairo_surface_get_content(gui.surface), width, height);
+               scratch_w = width;
+               scratch_h = height;
+           }
+
+           // capture scroll source region
+           cairo_t *tcr = cairo_create(scroll_scratch);
+           cairo_set_source_surface(tcr, gui.surface, -src_x, -src_y);
+           cairo_paint(tcr);
+           cairo_destroy(tcr);
+
+           // reuse scroll source region
+           cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+           cairo_rectangle(cr, dest_x, dest_y, width, height);
+           cairo_clip(cr);
+           cairo_set_source_surface(cr, scroll_scratch, dest_x, dest_y);
+           cairo_paint(cr);
+       }
+    }
+    else
+# endif
+    {
+       cairo_rectangle(cr, dest_x, dest_y, width, height);
+       cairo_clip(cr);
+       cairo_push_group(cr);
+       cairo_set_source_surface(cr, gui.surface, dest_x - src_x, dest_y - 
src_y);
+       cairo_paint(cr);
+       cairo_pop_group_to_source(cr);
+       cairo_paint(cr);
+    }
 
     cairo_destroy(cr);
 }
diff --git a/src/version.c b/src/version.c
index 1c7ecd3b1..ad4b50cf7 100644
--- a/src/version.c
+++ b/src/version.c
@@ -734,6 +734,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    10,
 /**/
     9,
 /**/

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion visit 
https://groups.google.com/d/msgid/vim_dev/E1vrgB5-00DnmL-RY%40256bit.org.

Raspunde prin e-mail lui