gpui: Throttle interactive resize events on Wayland (#34760)

Wayland compositors can potentially generate thousands of resize
requests when drag-resizing a window, which Zed is unable to keep up
with. This commit changes the behavior to only resize once per vblank
cycle, which helps significantly when resizing the window with a high
poll-rate mouse (1000Hz is common these days)

Here is an example of the behavior pre and post this commit, with some
print-debugging added for tracking resize calls. I have a wireless mouse
with a 2000Hz polling rate and a 165Hz display:

Before: 


https://github.com/user-attachments/assets/4c657f90-5fd2-4809-97ef-363fd48e81b8

After: 


https://github.com/user-attachments/assets/4a0f5fbd-c3c4-40a1-9f71-3b4358c827cf






Closes #20660

Release Notes:

- Improved: Make resizing smoother on Wayland/Linux
This commit is contained in:
Kamal Ahmad 2025-07-21 19:48:01 +05:00 committed by GitHub
parent cc56196152
commit c251f2a2d4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -76,6 +76,7 @@ struct InProgressConfigure {
size: Option<Size<Pixels>>,
fullscreen: bool,
maximized: bool,
resizing: bool,
tiling: Tiling,
}
@ -107,6 +108,7 @@ pub struct WaylandWindowState {
active: bool,
hovered: bool,
in_progress_configure: Option<InProgressConfigure>,
resize_throttle: bool,
in_progress_window_controls: Option<WindowControls>,
window_controls: WindowControls,
inset: Option<Pixels>,
@ -176,6 +178,7 @@ impl WaylandWindowState {
tiling: Tiling::default(),
window_bounds: options.bounds,
in_progress_configure: None,
resize_throttle: false,
client,
appearance,
handle,
@ -335,6 +338,7 @@ impl WaylandWindowStatePtr {
pub fn frame(&self) {
let mut state = self.state.borrow_mut();
state.surface.frame(&state.globals.qh, state.surface.id());
state.resize_throttle = false;
drop(state);
let mut cb = self.callbacks.borrow_mut();
@ -366,6 +370,12 @@ impl WaylandWindowStatePtr {
state.fullscreen = configure.fullscreen;
state.maximized = configure.maximized;
state.tiling = configure.tiling;
// Limit interactive resizes to once per vblank
if configure.resizing && state.resize_throttle {
return;
} else if configure.resizing {
state.resize_throttle = true;
}
if !configure.fullscreen && !configure.maximized {
configure.size = if got_unmaximized {
Some(state.window_bounds.size)
@ -472,6 +482,7 @@ impl WaylandWindowStatePtr {
let mut tiling = Tiling::default();
let mut fullscreen = false;
let mut maximized = false;
let mut resizing = false;
for state in states {
match state {
@ -481,6 +492,7 @@ impl WaylandWindowStatePtr {
xdg_toplevel::State::Fullscreen => {
fullscreen = true;
}
xdg_toplevel::State::Resizing => resizing = true,
xdg_toplevel::State::TiledTop => {
tiling.top = true;
}
@ -508,6 +520,7 @@ impl WaylandWindowStatePtr {
size,
fullscreen,
maximized,
resizing,
tiling,
});