From: Christoffer Aasted Date: Sun, 15 Feb 2026 17:21:03 +0000 (+0000) Subject: patch 9.2.0010: Using Wayland compositor is still slow X-Git-Tag: v9.2.0010^0 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ac9426bf549a5dea853ae02fa6c5dba0ef3beb95;p=thirdparty%2Fvim.git 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 Signed-off-by: Christian Brabandt --- diff --git a/src/gui.c b/src/gui.c index 8673ec8d84..9eef90f92f 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 da06e2ce09..2069e74865 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 015a2c7f65..edf6b705d5 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 +# ifdef GDK_WINDOWING_WAYLAND +# include +# endif # include # else # include @@ -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 1c7ecd3b17..ad4b50cf77 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, /**/