From: Christoffer Aasted Date: Mon, 29 Jun 2026 21:04:52 +0000 (+0000) Subject: patch 9.2.0751: GTK3 GUI is slow under Wayland X-Git-Tag: v9.2.0751^0 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1f2f568f9147ca68d8772869fd6b6f56901847b3;p=thirdparty%2Fvim.git patch 9.2.0751: GTK3 GUI is slow under Wayland Problem: GTK3 GUI redraws are slow under Wayland because each draw operation queues its own draw area. Solution: Defer redraws under Wayland: coalesce dirty rectangles and flush them once in gui_mch_flush(), route draw calls through a queue_draw_area() wrapper (Christoffer Aasted) closes: #20528 Signed-off-by: Christoffer Aasted Signed-off-by: Christian Brabandt --- diff --git a/runtime/doc/gui_x11.txt b/runtime/doc/gui_x11.txt index 8a084505e8..d55f35aba7 100644 --- a/runtime/doc/gui_x11.txt +++ b/runtime/doc/gui_x11.txt @@ -1,4 +1,4 @@ -*gui_x11.txt* For Vim version 9.2. Last change: 2026 Jun 24 +*gui_x11.txt* For Vim version 9.2. Last change: 2026 June 29 VIM REFERENCE MANUAL by Bram Moolenaar @@ -353,8 +353,12 @@ to the GTK documentation, however little there is, on how to do this. See https://www.manpagez.com/html/gtk2/gtk2-2.24.24/gtk2-Resource-Files.php for more information. *gtk3-slow* -If you are using GTK3 and Vim appears to be slow, try setting the environment -variable $GDK_RENDERING to "image". +If the GTK3 GUI is slow, try: > + + $ GDK_RENDERING=image gvim + +Small font, high resolution, many 'lines' (vertical screen) and UI scaling all +contribute to a slower redraw. *gtk4-slow* If the GTK4 GUI is slow, freezes, or does not accept keyboard input right diff --git a/src/gui_gtk_x11.c b/src/gui_gtk_x11.c index d28ccde863..d7ea36274f 100644 --- a/src/gui_gtk_x11.c +++ b/src/gui_gtk_x11.c @@ -3745,6 +3745,54 @@ gui_gtk_set_dnd_targets(void) GDK_ACTION_COPY | GDK_ACTION_MOVE); } +#ifdef GDK_WINDOWING_WAYLAND +static struct { + int left; + int top; + int right; + int bottom; + bool active; +} wl_dirty_rect = {0, 0, 0, 0, false}; + + static void +wl_queue_dirty_area(int x, int y, int width, int height) +{ + if (!wl_dirty_rect.active) + { + wl_dirty_rect.left = x; + wl_dirty_rect.top = y; + wl_dirty_rect.right = x + width; + wl_dirty_rect.bottom = y + height; + wl_dirty_rect.active = true; + } + else + { + // Expand to append further changes + if (x < wl_dirty_rect.left) wl_dirty_rect.left = x; + if (y < wl_dirty_rect.top) wl_dirty_rect.top = y; + if (x + width > wl_dirty_rect.right) wl_dirty_rect.right = x + width; + if (y + height > wl_dirty_rect.bottom) wl_dirty_rect.bottom = y + height; + } +} + + static void +wl_flush(void) +{ + if (!wl_dirty_rect.active) + return; + int draw_x = wl_dirty_rect.left; + int draw_y = wl_dirty_rect.top; + int draw_w = wl_dirty_rect.right - wl_dirty_rect.left; + int draw_h = wl_dirty_rect.bottom - wl_dirty_rect.top; + + if (draw_w > 0 && draw_h > 0) + { + gtk_widget_queue_draw_area(gui.drawarea, draw_x, draw_y, draw_w, draw_h); + } + wl_dirty_rect.active = false; +} +#endif + /* * Initialize the GUI. Create all the windows, set up all the callbacks etc. * Returns OK for success, FAIL when the GUI can't be started. @@ -4425,7 +4473,7 @@ form_configure_event(GtkWidget *widget UNUSED, // too small. Schedule a corrective resize (now that offsets are // known) so the window actually fits the geometry Vim has just set. if ((mch_csd_height > 0 || mch_csd_width > 0) && gtk_socket_id == 0) - g_idle_add(startup_resize_correction_cb, NULL); + g_idle_add_full(GTK_PRIORITY_RESIZE, startup_resize_correction_cb, NULL, NULL); } clear_resize_hists(); #endif @@ -6221,6 +6269,21 @@ gui_gtk_draw_string(int row, int col, char_u *s, int len, int flags) return len_sum; } + static void +queue_draw_area( + int x, + int y, + int width, + int height) +{ +#ifdef GDK_WINDOWING_WAYLAND + if (gui.is_wayland) + wl_queue_dirty_area(x, y, width, height); + else +#endif + gtk_widget_queue_draw_area(gui.drawarea, x, y, width, height); +} + int gui_gtk_draw_string_ext( int row, @@ -6473,7 +6536,7 @@ skipitall: #if GTK_CHECK_VERSION(3,0,0) cairo_destroy(cr); - gtk_widget_queue_draw_area(gui.drawarea, area.x, area.y, + queue_draw_area(area.x, area.y, area.width, area.height); #else gdk_gc_set_clip_rectangle(gui.text_gc, NULL); @@ -6617,7 +6680,7 @@ gui_mch_invert_rectangle(int r, int c, int nr, int nc) cairo_destroy(cr); - gtk_widget_queue_draw_area(gui.drawarea, rect.x, rect.y, + queue_draw_area(rect.x, rect.y, rect.width, rect.height); #else GdkGCValues values; @@ -6772,7 +6835,19 @@ gui_mch_update(void) int cnt = 0; // prevent endless loop while (g_main_context_pending(NULL) && !vim_is_input_buf_full() && ++cnt < 100) + { +#ifdef GDK_WINDOWING_WAYLAND + if (gui.is_wayland) + { + int prio = 0; + g_main_context_prepare(NULL, &prio); + // peek internal scheduling of redraw, honors 'lazyredraw' + if (prio == GDK_PRIORITY_REDRAW && redrawing()) + gui_may_flush(); // prepares redraw: g_main_context_iteration + } +#endif g_main_context_iteration(NULL, TRUE); + } } static timeout_cb_type @@ -6905,11 +6980,17 @@ theend: gui_mch_flush(void) { if (gui.mainwin != NULL && gtk_widget_get_realized(gui.mainwin)) + { +#ifdef GDK_WINDOWING_WAYLAND + if (gui.is_wayland) + return wl_flush(); +#endif #if GTK_CHECK_VERSION(2,4,0) gdk_display_flush(gtk_widget_get_display(gui.mainwin)); #else gdk_display_sync(gtk_widget_get_display(gui.mainwin)); #endif + } } /* @@ -6961,7 +7042,7 @@ gui_mch_clear_block(int row1arg, int col1arg, int row2arg, int col2arg) cairo_fill(cr); cairo_destroy(cr); - gtk_widget_queue_draw_area(gui.drawarea, + queue_draw_area( rect.x, rect.y, rect.width, rect.height); } #else // !GTK_CHECK_VERSION(3,0,0) @@ -6998,7 +7079,7 @@ gui_gtk_window_clear(GdkWindow *win) cairo_fill(cr); cairo_destroy(cr); - gtk_widget_queue_draw_area(gui.drawarea, + queue_draw_area( rect.x, rect.y, rect.width, rect.height); } #else @@ -7058,7 +7139,7 @@ gui_mch_draw_popup_image( cairo_popup_image_paint(wp, gui.surface, x, y, src_x, src_y, draw_w, draw_h); if (gui.drawarea != NULL) - gtk_widget_queue_draw_area(gui.drawarea, x, y, draw_w, draw_h); + queue_draw_area(x, y, draw_w, draw_h); # else cairo_popup_image_paint(wp, gui.drawarea->window, x, y, src_x, src_y, draw_w, draw_h); @@ -7114,49 +7195,35 @@ gui_gtk_surface_copy_rect(int dest_x, int dest_y, cairo_t * const cr = cairo_create(gui.surface); # 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; + static int scratch_w = 0, scratch_h = 0; + int last_row = Rows - 1, 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) + if (dest_y < src_y) // scroll up { cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); - cairo_set_source_surface(cr, gui.surface, - src_x - dest_x, + 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 + { // reuse 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; + 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); @@ -7176,7 +7243,6 @@ gui_gtk_surface_copy_rect(int dest_x, int dest_y, cairo_pop_group_to_source(cr); cairo_paint(cr); } - cairo_destroy(cr); } #endif @@ -7200,7 +7266,7 @@ gui_mch_delete_lines(int row, int num_lines) gui_clear_block( gui.scroll_region_bot - num_lines + 1, gui.scroll_region_left, gui.scroll_region_bot, gui.scroll_region_right); - gtk_widget_queue_draw_area(gui.drawarea, + queue_draw_area( FILL_X(gui.scroll_region_left), FILL_Y(row), gui.char_width * ncols + 1, gui.char_height * nrows); #else @@ -7246,7 +7312,7 @@ gui_mch_insert_lines(int row, int num_lines) gui_clear_block( row, gui.scroll_region_left, row + num_lines - 1, gui.scroll_region_right); - gtk_widget_queue_draw_area(gui.drawarea, + queue_draw_area( FILL_X(gui.scroll_region_left), FILL_Y(row), gui.char_width * ncols + 1, gui.char_height * nrows); #else @@ -7739,7 +7805,7 @@ gui_mch_drawsign(int row, int col, int typenr) cairo_surface_destroy(bg_surf); cairo_destroy(cr); - gtk_widget_queue_draw_area(gui.drawarea, + queue_draw_area( FILL_X(col), FILL_Y(col), width, height); } diff --git a/src/version.c b/src/version.c index 218fdc163a..d4c08b3eb9 100644 --- a/src/version.c +++ b/src/version.c @@ -759,6 +759,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 751, /**/ 750, /**/