linux/x11: Reduce input latency and ensure rerender priority (#13355)
This change ensures that we always render a window according to its refresh rate, even if there are a lot of X11 events. We're working around some limitations of `calloop`. In the future, we think we should revisit how the event loop is implemented on X11, so that we can ensure proper prioritization of input events vs. rendering. Release Notes: - N/A Co-authored-by: Antonio <me@as-cii.com>
This commit is contained in:
parent
04a79780d8
commit
f69c8ca74e
2 changed files with 79 additions and 60 deletions
|
@ -14,6 +14,7 @@ use util::{maybe, ResultExt};
|
|||
use x11rb::{
|
||||
connection::Connection,
|
||||
protocol::{
|
||||
randr::{self, ConnectionExt as _},
|
||||
xinput::{self, ConnectionExt as _},
|
||||
xproto::{
|
||||
self, ClientMessageEvent, ConnectionExt as _, EventMask, TranslateCoordinatesReply,
|
||||
|
@ -31,6 +32,7 @@ use std::{
|
|||
ptr::NonNull,
|
||||
rc::Rc,
|
||||
sync::{self, Arc},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use super::{X11Display, XINPUT_MASTER_DEVICE};
|
||||
|
@ -159,6 +161,8 @@ pub struct Callbacks {
|
|||
|
||||
pub struct X11WindowState {
|
||||
pub destroyed: bool,
|
||||
pub last_render_at: Option<Instant>,
|
||||
pub refresh_rate: Duration,
|
||||
client: X11ClientStatePtr,
|
||||
executor: ForegroundExecutor,
|
||||
atoms: XcbAtoms,
|
||||
|
@ -389,6 +393,31 @@ impl X11WindowState {
|
|||
};
|
||||
xcb_connection.map_window(x_window).unwrap();
|
||||
|
||||
let screen_resources = xcb_connection
|
||||
.randr_get_screen_resources(x_window)
|
||||
.unwrap()
|
||||
.reply()
|
||||
.expect("Could not find available screens");
|
||||
|
||||
let mode = screen_resources
|
||||
.crtcs
|
||||
.iter()
|
||||
.find_map(|crtc| {
|
||||
let crtc_info = xcb_connection
|
||||
.randr_get_crtc_info(*crtc, x11rb::CURRENT_TIME)
|
||||
.ok()?
|
||||
.reply()
|
||||
.ok()?;
|
||||
|
||||
screen_resources
|
||||
.modes
|
||||
.iter()
|
||||
.find(|m| m.id == crtc_info.mode)
|
||||
})
|
||||
.expect("Unable to find screen refresh rate");
|
||||
|
||||
let refresh_rate = mode_refresh_rate(mode);
|
||||
|
||||
Ok(Self {
|
||||
client,
|
||||
executor,
|
||||
|
@ -405,6 +434,8 @@ impl X11WindowState {
|
|||
appearance,
|
||||
handle,
|
||||
destroyed: false,
|
||||
last_render_at: None,
|
||||
refresh_rate,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -574,6 +605,11 @@ impl X11WindowStatePtr {
|
|||
let mut cb = self.callbacks.borrow_mut();
|
||||
if let Some(ref mut fun) = cb.request_frame {
|
||||
fun();
|
||||
|
||||
self.state
|
||||
.borrow_mut()
|
||||
.last_render_at
|
||||
.replace(Instant::now());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1020,3 +1056,12 @@ impl PlatformWindow for X11Window {
|
|||
false
|
||||
}
|
||||
}
|
||||
|
||||
// Adatpted from:
|
||||
// https://docs.rs/winit/0.29.11/src/winit/platform_impl/linux/x11/monitor.rs.html#103-111
|
||||
pub fn mode_refresh_rate(mode: &randr::ModeInfo) -> Duration {
|
||||
let millihertz = mode.dot_clock as u64 * 1_000 / (mode.htotal as u64 * mode.vtotal as u64);
|
||||
let micros = 1_000_000_000 / millihertz;
|
||||
log::info!("Refreshing at {} micros", micros);
|
||||
Duration::from_micros(micros)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue