perf(gtk3-wayland): deferred dirty redraw by dezza · Pull Request #20528 · vim/vim · GitHub
Skip to content

perf(gtk3-wayland): deferred dirty redraw#20528

Open
dezza wants to merge 8 commits into
vim:masterfrom
dezza:perf-gui-wayland
Open

perf(gtk3-wayland): deferred dirty redraw#20528
dezza wants to merge 8 commits into
vim:masterfrom
dezza:perf-gui-wayland

Conversation

@dezza

@dezza dezza commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Implements a dirty rect box for gtk3 with wayland backend.

Instead of constantly pumping gtk3 loop between keypress, which almost always have a queued draw due to especially

  • gui_gtk_draw_string_ext
  • gui_mch_clear_block

.. or gui_mch_delete_lines, gui_mch_insert_lines when scrolling.

Instead we re-use gui_mch_update, which before pumped redraws during key-repeat like pgup for scrolling. This caused tearing, partial redraws.

In gui_mch_update call gui_mch_flush for wayland, where a wl_flush function flushes the dirty rect.

We gate that flush checking for a queued redraw in gdk/gtk3.

    void
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
	    if (prio == GDK_PRIORITY_REDRAW)
		gui_mch_flush(); // prepares redraw; g_main_context_iteration
	}
#endif
	g_main_context_iteration(NULL, TRUE);
    }
}

This prepares a redraw for g_main_context_iteration below.

The result is that scrolling is fast, mostly smooth and cpu usage lowered significantly; because key input, especially with continous key-repeat and scroll key such as pgup/c-b does not simultaneously pump excessive redraws from the micro-redraws queued by gui_gtk_draw_string_ext etc.

Inside gui_mch_wait_for_chars it triggers
g_main_context_iteration constantly both at idle or with key input. and finally reaches gui_mch_update, even if a key is held.

    int
gui_mch_wait_for_chars(long wtime)
{
 // ...
    do
    {
 // ...
	/*
	 * Loop in GTK+ processing  until a timeout or input occurs.
	 * Skip this if input is available anyway (can happen in rare
	 * situations, sort of race condition).
	 */
	if (!input_available())
	    g_main_context_iteration(NULL, TRUE);

	// Got char, return immediately
	if (input_available())
	{
	    retval = OK;
	    goto theend;
	}
    } while (wtime < 0 || !timed_out);

    /*
     * Flush all eventually pending (drawing) events.
     */
    gui_mch_update();

theend:
    if (timer != 0 && !timed_out)
	timeout_remove(timer);
#ifdef FEAT_JOB_CHANNEL
    if (channel_timer != 0)
	timeout_remove(channel_timer);
#endif

    return retval;
}
    void
gui_mch_update(void)
/*
 * Catch up with any queued X11 events.  This may put keyboard input into the
 * input buffer, call resize call-backs, trigger timers etc.  If there is
 * nothing in the X11 event queue (& no timers pending), then we return
 * immediately.
 */

^ This aligns with intended use of gui_mch_update on x11, but is mostly a symptom of wayland being asynchronous, whereas x11 and vim is largely stateful, imperative and immediate. x11 handles the continous drawing efficiently, but wayland redraws fully + gtk3 has fractional/hidpi perf issues itself.

The constant pump of the g_main loop has side-effects on wayland, as there is almost always a small redraw queued, or it will simply render too early (midscroll) and cause tearing, choking performance.

Therefore there aren't many good solutions to an architectural issue of both gtk3+vim. No better solutions other than reducing load (raise font size, lower res etc) or throttling (non-trivial and not proper) - while gtk4 still matures.

.. Thats why the:

if (prio == GDK_PRIORITY_REDRAW) gate - as it seems to work, is a very neat discovery, because even if if misses a single event, it comes around faster again than without. Fast enough, to not notice within a monitor 60hz refresh rate (higher refresh rate forget about it 😄 testing is welcome).

More importantly it is unbiased, no compromises, its not a "lazy redraw" as redraws seemingly work normal everywhere (but faster, non-tearing). Its not waiting for key release or frame skipping - every single scroll is still displayed. Looking really closely, it seems gtk3 is avoiding redraw storms this way, as it does skip some redraws, but only those skipped due to more queued redraws invalidating the previous one.

96e30bc

Closes: #18002

@dezza

dezza commented Jun 15, 2026

Copy link
Copy Markdown
Contributor Author

@dezza

dezza commented Jun 22, 2026

Copy link
Copy Markdown
Contributor Author

@chrisbra does this align with the intended use of lazyredraw ? 9d7b53f

If not, I will rethink it or remove it entirely.

Could be useful if someone has an extraordinarily large screen, or if for other reasons it became slow again.

The scroll and snap redraw feels right to me, a good compromise.

@chrisbra

Copy link
Copy Markdown
Member

@chrisbra does this align with the intended use of lazyredraw ? 9d7b53f

I don't think this fits. The use case of lazyredraw it to speed up non-typed editing (like macro recordings and mappings). It's not so much meant for interactive use cases.

@dezza

dezza commented Jun 22, 2026

Copy link
Copy Markdown
Contributor Author

@chrisbra does this align with the intended use of lazyredraw ? 9d7b53f

I don't think this fits. The use case of lazyredraw it to speed up non-typed editing (like macro recordings and mappings). It's not so much meant for interactive use cases.

Thanks! This was news to me too when I read the help - I know some have it permanently enabled, I'll just revert it. Still haven't found issues.

dezza added 6 commits June 24, 2026 00:45
Implements a dirty rect box for gtk3 with wayland backend.

Instead of constantly pumping gtk3 loop between keypress, which
*almost always* have a queued draw due to especially
* `gui_gtk_draw_string_ext`
* `gui_mch_clear_block`

.. or `gui_mch_delete_lines`, `gui_mch_insert_lines` when scrolling.

Instead we re-use `gui_mch_update`, which before pumped redraws during
key-repeat like `pgup` for scrolling, this caused tearing, partial
redraws.

In `gui_mch_update` we now explicitly call `gui_mch_flush` for wayland,
where a `wl_flush` function flushes the dirty rect.

We gate that flush checking for a queued redraw in gdk/gtk3.

This prepares a redraw for `g_main_context_iteration` below.

The result is, that scrolling is fast, mostly smooth and cpu usage
lowered significantly; because key input, especially with continous-
key repeat with a scroll key such as `pgup`/`c-b` does not-
simultaneously pump excessive redraws from the micro-redraws queued by
`gui_gtk_draw_string_ext` etc.

This aligns with intended use of `gui_mch_update` on x11, but is mostly
a symptom of wayland being asynchronous, whereas x11 and vim is largely
stateful, imperative and immediate.

Inside `gui_mch_wait_for_chars` it triggers
`g_main_context_iteration` constantly *both at idle or with key input.*
and finally reaches `gui_mch_update`, even if a key is held.
gdk_display_flush is noop on wayland anyways.
If `set lazyredraw` is on holding pgup to scroll will not redraw until
the key is released.
@dezza dezza force-pushed the perf-gui-wayland branch from 3ee1641 to b15f49c Compare June 23, 2026 22:45
@dezza dezza marked this pull request as ready for review June 25, 2026 14:42
@dezza

dezza commented Jun 25, 2026

Copy link
Copy Markdown
Contributor Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

gvim is incredibly slow for :messages :highlight

2 participants