From 45576eeeab3adabd7cf77c51f3f406b7d0875efa Mon Sep 17 00:00:00 2001 From: Wez Furlong Date: Sat, 7 Aug 2021 09:30:36 -0700 Subject: [PATCH] wayland: make use of the frame callback This commit ties our invalidation requests together with the surface frame callback request so that we can throttle our frame rate if we're busy, but still remain largely idle if we're not changing any content. refs: https://github.com/wez/wezterm/issues/884 --- window/src/os/wayland/window.rs | 48 ++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/window/src/os/wayland/window.rs b/window/src/os/wayland/window.rs index 2388901e8..2a6bc63bc 100644 --- a/window/src/os/wayland/window.rs +++ b/window/src/os/wayland/window.rs @@ -123,6 +123,7 @@ pub struct WaylandWindowInner { pending_mouse: Arc>, pending_first_configure: Option>, frame_callback: Option>, + invalidated: bool, // wegl_surface is listed before gl_state because it // must be dropped before gl_state otherwise the underlying // libraries will segfault on shutdown @@ -308,6 +309,7 @@ impl WaylandWindow { copy_and_paste, events: WindowEventSender::new(event_handler), surface, + invalidated: false, window: Some(window), dimensions, window_state: WindowState::default(), @@ -648,26 +650,36 @@ impl WaylandWindowInner { Ok(gl_state) } + fn next_frame_is_ready(&mut self) { + self.frame_callback.take(); + if self.invalidated { + self.do_paint().ok(); + } + } + fn do_paint(&mut self) -> anyhow::Result<()> { + if self.frame_callback.is_some() { + // Painting now won't be productive, so skip it but + // remember that we need to be painted so that when + // the compositor is ready for us, we can paint then. + self.invalidated = true; + return Ok(()); + } + + self.invalidated = false; self.events.dispatch(WindowEvent::NeedRepaint); - // We could request a callback when we should render the next frame - // by doing this here, but unconditionally doing this will make us - // redraw at the display refresh rate which is potentially more - // often than we want. - if false { - let callback = self.surface.frame(); - let window_id = self.window_id; - callback.quick_assign(move |_source, _event, _data| { - WaylandConnection::with_window_inner(window_id, |inner| { - inner.invalidate(); - Ok(()) - }); + // Ask the compositor to wake us up when its time to paint + // the next frame + let window_id = self.window_id; + let callback = self.surface.frame(); + callback.quick_assign(move |_source, _event, _data| { + WaylandConnection::with_window_inner(window_id, |inner| { + inner.next_frame_is_ready(); + Ok(()) }); - self.frame_callback.replace(callback); - } else { - self.frame_callback.take(); - } + }); + self.frame_callback.replace(callback); Ok(()) } @@ -943,6 +955,10 @@ impl WaylandWindowInner { } fn invalidate(&mut self) { + if self.frame_callback.is_some() { + self.invalidated = true; + return; + } self.do_paint().unwrap(); }