Merge pull request #201 from mahkoh/jorth/page-flip-delay
metal: delay rendering until shortly before page flip
This commit is contained in:
commit
98f5e14ed0
9 changed files with 113 additions and 22 deletions
|
|
@ -1,6 +1,10 @@
|
|||
# Unreleased
|
||||
|
||||
- Add remaining layer-shell features.
|
||||
- Add JAY_MAX_RENDER_TIME_NSEC environment variable.
|
||||
This can be used to delay rendering until shortly before a page flip, reducing input
|
||||
delay.
|
||||
This is an unstable feature that might change in the future.
|
||||
|
||||
# 1.2.0 (2024-05-05)
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ use {
|
|||
renderer::RenderResult,
|
||||
state::State,
|
||||
theme::Color,
|
||||
time::now_nsec,
|
||||
tree::OutputNode,
|
||||
udev::UdevDevice,
|
||||
utils::{
|
||||
|
|
@ -45,6 +46,7 @@ use {
|
|||
indexmap::{indexset, IndexSet},
|
||||
isnt::std_1::collections::IsntHashMap2Ext,
|
||||
jay_config::video::GfxApi,
|
||||
once_cell::sync::Lazy,
|
||||
std::{
|
||||
any::Any,
|
||||
cell::{Cell, RefCell},
|
||||
|
|
@ -416,6 +418,7 @@ pub struct MetalConnector {
|
|||
pub can_present: Cell<bool>,
|
||||
pub has_damage: Cell<bool>,
|
||||
pub cursor_changed: Cell<bool>,
|
||||
pub next_flip_nsec: Cell<u64>,
|
||||
|
||||
pub display: RefCell<ConnectorDisplayData>,
|
||||
|
||||
|
|
@ -578,6 +581,20 @@ impl MetalConnector {
|
|||
async fn present_loop(self: Rc<Self>) {
|
||||
loop {
|
||||
self.present_trigger.triggered().await;
|
||||
static DELTA: Lazy<Option<u64>> = Lazy::new(|| {
|
||||
if let Ok(max_render_time) = std::env::var("JAY_MAX_RENDER_TIME_NSEC") {
|
||||
if let Ok(max_render_time) = max_render_time.parse() {
|
||||
return Some(max_render_time);
|
||||
}
|
||||
}
|
||||
None
|
||||
});
|
||||
if let Some(delta) = *DELTA {
|
||||
let next_present = self.next_flip_nsec.get().saturating_sub(delta);
|
||||
if now_nsec() < next_present {
|
||||
self.state.ring.timeout(next_present).await.unwrap();
|
||||
}
|
||||
}
|
||||
match self.present(true) {
|
||||
Ok(_) => self.state.set_backend_idle(false),
|
||||
Err(e) => {
|
||||
|
|
@ -1397,6 +1414,7 @@ fn create_connector(
|
|||
active_framebuffer: Default::default(),
|
||||
next_framebuffer: Default::default(),
|
||||
direct_scanout_active: Cell::new(false),
|
||||
next_flip_nsec: Cell::new(0),
|
||||
});
|
||||
let futures = ConnectorFutures {
|
||||
present: backend
|
||||
|
|
@ -2161,6 +2179,9 @@ impl MetalBackend {
|
|||
connector.schedule_present();
|
||||
}
|
||||
let dd = connector.display.borrow_mut();
|
||||
connector
|
||||
.next_flip_nsec
|
||||
.set(tv_sec as u64 * 1_000_000_000 + tv_usec as u64 * 1000 + dd.refresh as u64);
|
||||
{
|
||||
let global = self.state.root.outputs.get(&connector.connector_id);
|
||||
let mut rr = connector.render_result.borrow_mut();
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use {
|
|||
ops::{
|
||||
accept::AcceptTask, async_cancel::AsyncCancelTask, connect::ConnectTask,
|
||||
poll::PollTask, read_write::ReadWriteTask, recvmsg::RecvmsgTask,
|
||||
sendmsg::SendmsgTask, timeout::TimeoutTask,
|
||||
sendmsg::SendmsgTask, timeout::TimeoutTask, timeout_link::TimeoutLinkTask,
|
||||
},
|
||||
pending_result::PendingResults,
|
||||
sys::{
|
||||
|
|
@ -211,6 +211,7 @@ impl IoUring {
|
|||
cached_sendmsg: Default::default(),
|
||||
cached_recvmsg: Default::default(),
|
||||
cached_timeouts: Default::default(),
|
||||
cached_timeout_links: Default::default(),
|
||||
cached_cmsg_bufs: Default::default(),
|
||||
cached_connects: Default::default(),
|
||||
cached_accepts: Default::default(),
|
||||
|
|
@ -266,6 +267,7 @@ struct IoUringData {
|
|||
cached_sendmsg: Stack<Box<SendmsgTask>>,
|
||||
cached_recvmsg: Stack<Box<RecvmsgTask>>,
|
||||
cached_timeouts: Stack<Box<TimeoutTask>>,
|
||||
cached_timeout_links: Stack<Box<TimeoutLinkTask>>,
|
||||
cached_cmsg_bufs: Stack<Buf>,
|
||||
cached_connects: Stack<Box<ConnectTask>>,
|
||||
cached_accepts: Stack<Box<AcceptTask>>,
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ pub mod read_write;
|
|||
pub mod recvmsg;
|
||||
pub mod sendmsg;
|
||||
pub mod timeout;
|
||||
pub mod timeout_link;
|
||||
|
||||
pub type TaskResult<T> = Result<Result<T, OsError>, IoUringError>;
|
||||
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ impl IoUring {
|
|||
});
|
||||
self.ring.schedule(pw);
|
||||
if let Some(time) = timeout {
|
||||
self.schedule_timeout(time);
|
||||
self.schedule_timeout_link(time);
|
||||
}
|
||||
}
|
||||
Ok(pr.await.map(|v| v as usize)).merge()
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ impl IoUring {
|
|||
st.has_timeout = timeout.is_some();
|
||||
self.ring.schedule(st);
|
||||
if let Some(timeout) = timeout {
|
||||
self.schedule_timeout(timeout);
|
||||
self.schedule_timeout_link(timeout);
|
||||
}
|
||||
}
|
||||
Ok(pr.await? as _)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
use {
|
||||
crate::{
|
||||
io_uring::{
|
||||
sys::{io_uring_sqe, IORING_OP_LINK_TIMEOUT, IORING_TIMEOUT_ABS},
|
||||
IoUring, IoUringData, Task,
|
||||
},
|
||||
time::Time,
|
||||
crate::io_uring::{
|
||||
pending_result::PendingResult,
|
||||
sys::{io_uring_sqe, IORING_OP_TIMEOUT, IORING_TIMEOUT_ABS},
|
||||
IoUring, IoUringData, IoUringError, Task,
|
||||
},
|
||||
uapi::c,
|
||||
};
|
||||
|
|
@ -12,27 +10,35 @@ use {
|
|||
#[allow(non_camel_case_types)]
|
||||
#[repr(C)]
|
||||
#[derive(Default)]
|
||||
struct timespec64 {
|
||||
tv_sec: i64,
|
||||
tv_nsec: c::c_long,
|
||||
pub(super) struct timespec64 {
|
||||
pub tv_sec: i64,
|
||||
pub tv_nsec: c::c_long,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TimeoutTask {
|
||||
id: u64,
|
||||
timespec: timespec64,
|
||||
pr: Option<PendingResult>,
|
||||
}
|
||||
|
||||
impl IoUring {
|
||||
pub(super) fn schedule_timeout(&self, timeout: Time) {
|
||||
let id = self.ring.id_raw();
|
||||
pub async fn timeout(&self, timeout_nsec: u64) -> Result<(), IoUringError> {
|
||||
self.ring.check_destroyed()?;
|
||||
let id = self.ring.id();
|
||||
let pr = self.ring.pending_results.acquire();
|
||||
{
|
||||
let mut to = self.ring.cached_timeouts.pop().unwrap_or_default();
|
||||
to.id = id;
|
||||
to.timespec.tv_sec = timeout.0.tv_sec as _;
|
||||
to.timespec.tv_nsec = timeout.0.tv_nsec as _;
|
||||
self.ring.schedule(to);
|
||||
let mut pw = self.ring.cached_timeouts.pop().unwrap_or_default();
|
||||
pw.id = id.id;
|
||||
pw.timespec = timespec64 {
|
||||
tv_sec: (timeout_nsec / 1_000_000_000) as _,
|
||||
tv_nsec: (timeout_nsec % 1_000_000_000) as _,
|
||||
};
|
||||
pw.pr = Some(pr.clone());
|
||||
self.ring.schedule(pw);
|
||||
}
|
||||
let _ = pr.await;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -41,14 +47,18 @@ unsafe impl Task for TimeoutTask {
|
|||
self.id
|
||||
}
|
||||
|
||||
fn complete(self: Box<Self>, ring: &IoUringData, _res: i32) {
|
||||
fn complete(mut self: Box<Self>, ring: &IoUringData, res: i32) {
|
||||
if let Some(pr) = self.pr.take() {
|
||||
pr.complete(res);
|
||||
}
|
||||
ring.cached_timeouts.push(self);
|
||||
}
|
||||
|
||||
fn encode(&self, sqe: &mut io_uring_sqe) {
|
||||
sqe.opcode = IORING_OP_LINK_TIMEOUT;
|
||||
sqe.opcode = IORING_OP_TIMEOUT;
|
||||
sqe.u2.addr = &self.timespec as *const _ as _;
|
||||
sqe.len = 1;
|
||||
sqe.u3.timeout_flags = IORING_TIMEOUT_ABS;
|
||||
sqe.u1.off = 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
44
src/io_uring/ops/timeout_link.rs
Normal file
44
src/io_uring/ops/timeout_link.rs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
use crate::{
|
||||
io_uring::{
|
||||
ops::timeout::timespec64,
|
||||
sys::{io_uring_sqe, IORING_OP_LINK_TIMEOUT, IORING_TIMEOUT_ABS},
|
||||
IoUring, IoUringData, Task,
|
||||
},
|
||||
time::Time,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TimeoutLinkTask {
|
||||
id: u64,
|
||||
timespec: timespec64,
|
||||
}
|
||||
|
||||
impl IoUring {
|
||||
pub(super) fn schedule_timeout_link(&self, timeout: Time) {
|
||||
let id = self.ring.id_raw();
|
||||
{
|
||||
let mut to = self.ring.cached_timeout_links.pop().unwrap_or_default();
|
||||
to.id = id;
|
||||
to.timespec.tv_sec = timeout.0.tv_sec as _;
|
||||
to.timespec.tv_nsec = timeout.0.tv_nsec as _;
|
||||
self.ring.schedule(to);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Task for TimeoutLinkTask {
|
||||
fn id(&self) -> u64 {
|
||||
self.id
|
||||
}
|
||||
|
||||
fn complete(self: Box<Self>, ring: &IoUringData, _res: i32) {
|
||||
ring.cached_timeout_links.push(self);
|
||||
}
|
||||
|
||||
fn encode(&self, sqe: &mut io_uring_sqe) {
|
||||
sqe.opcode = IORING_OP_LINK_TIMEOUT;
|
||||
sqe.u2.addr = &self.timespec as *const _ as _;
|
||||
sqe.len = 1;
|
||||
sqe.u3.timeout_flags = IORING_TIMEOUT_ABS;
|
||||
}
|
||||
}
|
||||
11
src/time.rs
11
src/time.rs
|
|
@ -62,7 +62,12 @@ impl Time {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn nsec(self) -> u64 {
|
||||
let sec = self.0.tv_sec as u64 * 1_000_000_000;
|
||||
let nsec = self.0.tv_nsec as u64;
|
||||
sec + nsec
|
||||
}
|
||||
|
||||
pub fn usec(self) -> u64 {
|
||||
let sec = self.0.tv_sec as u64 * 1_000_000;
|
||||
let nsec = self.0.tv_nsec as u64 / 1_000;
|
||||
|
|
@ -119,6 +124,10 @@ impl Add<Duration> for Time {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn now_nsec() -> u64 {
|
||||
Time::now_unchecked().nsec()
|
||||
}
|
||||
|
||||
pub fn now_usec() -> u64 {
|
||||
Time::now_unchecked().usec()
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue