From 13cc12d4e7e358ae1d610776da4573e979c9d619 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Fri, 18 Nov 2022 14:47:41 +0100 Subject: [PATCH] Ensure a consistent frame pacing in X11 windows The next frame's time stamp needs to be computed based on the last frame's, not based on the current time, unless the last frame took too long to process. --- src/x11/window.rs | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/x11/window.rs b/src/x11/window.rs index f528de9..9dbad5b 100644 --- a/src/x11/window.rs +++ b/src/x11/window.rs @@ -445,19 +445,21 @@ impl Window { xcb::ffi::xcb_get_file_descriptor(raw_conn) }; - let mut next_frame = Instant::now() + self.frame_interval; + let mut last_frame = Instant::now(); self.event_loop_running = true; while self.event_loop_running { - let now = Instant::now(); - let until_next_frame = if now > next_frame { + // We'll try to keep a consistent frame pace. If the last frame couldn't be processed in + // the expected frame time, this will throttle down to prevent multiple frames from + // being queued up. The conditional here is needed because event handling and frame + // drawing is interleaved. The `poll()` function below will wait until the next frame + // can be drawn, or until the window receives an event. We thus need to manually check + // if it's already time to draw a new frame. + let next_frame = last_frame + self.frame_interval; + if Instant::now() >= next_frame { handler.on_frame(&mut crate::Window::new(self)); - - next_frame = Instant::now() + self.frame_interval; - self.frame_interval - } else { - next_frame - now - }; + last_frame = Instant::max(next_frame, Instant::now() - self.frame_interval); + } let mut fds = [PollFd::new(xcb_fd, PollFlags::POLLIN)]; @@ -466,7 +468,8 @@ impl Window { self.drain_xcb_events(handler); // FIXME: handle errors - poll(&mut fds, until_next_frame.subsec_millis() as i32).unwrap(); + poll(&mut fds, next_frame.duration_since(Instant::now()).subsec_millis() as i32) + .unwrap(); if let Some(revents) = fds[0].revents() { if revents.contains(PollFlags::POLLERR) {