1
0
Fork 0
forked from wry/wry

wayland: implement commit-timing-v1

This commit is contained in:
Julian Orth 2024-09-26 16:18:17 +02:00
parent d45aaffdb3
commit fac5445f2e
19 changed files with 434 additions and 15 deletions

View file

@ -7,7 +7,10 @@ use {
wl_buffer::WlBufferStorage,
wl_surface::{PendingState, WlSurface, WlSurfaceError},
},
io_uring::{IoUring, IoUringError, PendingPoll, PollCallback},
io_uring::{
IoUring, IoUringError, PendingPoll, PendingTimeout, PollCallback, TimeoutCallback,
},
tree::BeforeLatchResult,
utils::{
clonecell::CloneCell,
copyhashmap::CopyHashMap,
@ -28,7 +31,7 @@ use {
std::{
cell::{Cell, RefCell},
mem,
ops::DerefMut,
ops::{Deref, DerefMut},
rc::{Rc, Weak},
slice,
},
@ -50,6 +53,11 @@ pub struct CommitTimelines {
_flush_requests_future: SpawnedFuture<()>,
}
struct CommitTimeWaiter {
node: NodeRef<Entry>,
present: u64,
}
pub struct CommitTimeline {
shared: Rc<CommitTimelines>,
own_timeline: Rc<Inner>,
@ -57,6 +65,7 @@ pub struct CommitTimeline {
effective_timeline_id: Cell<CommitTimelineId>,
fifo_barrier_set: Cell<bool>,
fifo_waiter: Cell<Option<NodeRef<Entry>>>,
commit_time_waiter: RefCell<Option<CommitTimeWaiter>>,
}
struct Inner {
@ -98,6 +107,8 @@ pub enum CommitTimelineError {
RegisterImplicitPoll(#[source] IoUringError),
#[error("Could not wait for a dmabuf to become idle")]
PollDmabuf(#[source] OsError),
#[error("Could not wait for the commit timeout")]
CommitTimeout(#[source] OsError),
}
impl CommitTimelines {
@ -136,6 +147,7 @@ impl CommitTimelines {
effective_timeline_id: Cell::new(id),
fifo_barrier_set: Cell::new(false),
fifo_waiter: Default::default(),
commit_time_waiter: Default::default(),
}
}
@ -168,6 +180,7 @@ impl CommitTimeline {
match reason {
ClearReason::BreakLoops => {
self.fifo_waiter.take();
self.commit_time_waiter.take();
break_loops(&self.own_timeline.entries)
}
ClearReason::Destroy => {
@ -191,13 +204,18 @@ impl CommitTimeline {
acquire_points: Default::default(),
shm_uploads: 0,
implicit_dmabufs: Default::default(),
commit_time: Default::default(),
};
collector.collect(pending);
let points = collector.acquire_points;
let pending_uploads = collector.shm_uploads;
let implicit_dmabufs = collector.implicit_dmabufs;
let has_dependencies =
points.is_not_empty() || pending_uploads > 0 || implicit_dmabufs.is_not_empty();
let commit_time = collector.commit_time;
let has_commit_time = commit_time > 0;
let has_dependencies = points.is_not_empty()
|| pending_uploads > 0
|| implicit_dmabufs.is_not_empty()
|| has_commit_time;
let must_be_queued = has_dependencies
|| self.own_timeline.entries.is_not_empty()
|| (pending.fifo_barrier_wait && self.fifo_barrier_set.get());
@ -227,6 +245,7 @@ impl CommitTimeline {
num_pending_polls: NumCell::new(implicit_dmabufs.len()),
pending_polls: Cell::new(Default::default()),
fifo_state: Cell::new(commit_fifo_state),
commit_times: RefCell::new(CommitTimesState::Ready),
}),
);
let mut needs_flush = commit_fifo_state == CommitFifoState::Queued;
@ -263,6 +282,13 @@ impl CommitTimeline {
}
commit.pending_polls.set(pending_polls);
}
if has_commit_time {
*commit.commit_times.borrow_mut() = CommitTimesState::Queued {
rc: noderef.clone(),
time: commit_time,
};
needs_flush = true;
}
}
if needs_flush && noderef.prev().is_none() {
flush_from(noderef.clone()).map_err(CommitTimelineError::DelayedCommit)?;
@ -284,6 +310,30 @@ impl CommitTimeline {
pub fn has_fifo_barrier(&self) -> bool {
self.fifo_barrier_set.get()
}
pub fn before_latch(&self, surface: &WlSurface, present: u64) -> BeforeLatchResult {
let waiter = &mut *self.commit_time_waiter.borrow_mut();
if let Some(w) = waiter {
if w.present <= present {
let EntryKind::Commit(c) = &w.node.kind else {
unreachable!();
};
*c.commit_times.borrow_mut() = CommitTimesState::Ready;
self.shared
.flush_requests
.flush_waiters
.push(w.node.clone());
*waiter = None;
surface.before_latch_listener.detach();
BeforeLatchResult::Yield
} else {
BeforeLatchResult::None
}
} else {
surface.before_latch_listener.detach();
BeforeLatchResult::None
}
}
}
impl SyncObjWaiter for NodeRef<Entry> {
@ -343,6 +393,25 @@ impl PollCallback for NodeRef<Entry> {
}
}
impl TimeoutCallback for NodeRef<Entry> {
fn completed(self: Rc<Self>, res: Result<(), OsError>, _data: u64) {
let EntryKind::Commit(commit) = &self.kind else {
unreachable!();
};
commit.surface.commit_timeline.commit_time_waiter.take();
commit.surface.before_latch_listener.detach();
if let Err(e) = res {
commit
.surface
.client
.error(CommitTimelineError::CommitTimeout(e));
return;
}
*commit.commit_times.borrow_mut() = CommitTimesState::Ready;
flush_commit(&self, commit);
}
}
struct Entry {
link: Cell<Option<LinkedNode<Entry>>>,
shared: Rc<CommitTimelines>,
@ -362,6 +431,12 @@ enum ShmUploadState {
Scheduled(#[expect(dead_code)] SmallVec<[PendingShmTransfer; 1]>),
}
enum CommitTimesState {
Ready,
Queued { rc: Rc<NodeRef<Entry>>, time: u64 },
Registered { _pending: PendingTimeout },
}
struct Commit {
surface: Rc<WlSurface>,
pending: RefCell<Box<PendingState>>,
@ -372,6 +447,7 @@ struct Commit {
num_pending_polls: NumCell<usize>,
pending_polls: Cell<SmallVec<[PendingPoll; 1]>>,
fifo_state: Cell<CommitFifoState>,
commit_times: RefCell<CommitTimesState>,
}
fn flush_from(mut point: NodeRef<Entry>) -> Result<(), WlSurfaceError> {
@ -421,6 +497,19 @@ impl NodeRef<Entry> {
CommitFifoState::Mailbox => {}
}
}
let commit_times = &mut *c.commit_times.borrow_mut();
match commit_times {
CommitTimesState::Ready => {}
CommitTimesState::Queued { rc, time } => {
*commit_times = register_commit_time(tl, rc, c, *time)?;
if let CommitTimesState::Registered { .. } = commit_times {
has_unmet_dependencies = true;
}
}
CommitTimesState::Registered { .. } => {
has_unmet_dependencies = true;
}
}
if has_unmet_dependencies {
return Ok(false);
}
@ -455,6 +544,36 @@ fn check_shm_uploads(c: &Commit) -> Result<(), WlSurfaceError> {
Ok(())
}
fn register_commit_time(
tl: &CommitTimeline,
rc: &Rc<NodeRef<Entry>>,
c: &Commit,
time: u64,
) -> Result<CommitTimesState, WlSurfaceError> {
let output = c.surface.output.get();
let render_margin = output.render_margin_ns.get();
let flip_margin = output.flip_margin_ns.get().unwrap_or_default();
let refresh = output.global.refresh_nsec.get();
let present_margin = render_margin.saturating_add(flip_margin).min(refresh);
let timeout = time.saturating_sub(present_margin);
if timeout <= c.surface.client.state.now_nsec() {
return Ok(CommitTimesState::Ready);
}
let pending = tl
.shared
.ring
.timeout_external(timeout, rc.clone(), 0)
.map_err(WlSurfaceError::RegisterCommitTimeout)?;
*tl.commit_time_waiter.borrow_mut() = Some(CommitTimeWaiter {
node: rc.deref().clone(),
present: time,
});
c.surface
.before_latch_listener
.attach(&output.before_latch_event);
Ok(CommitTimesState::Registered { _pending: pending })
}
fn schedule_async_uploads(
node_ref: &Rc<NodeRef<Entry>>,
surface: &WlSurface,
@ -554,6 +673,7 @@ struct CommitDataCollector {
acquire_points: SmallVec<[Point; 1]>,
shm_uploads: usize,
implicit_dmabufs: SmallVec<[Rc<OwnedFd>; 1]>,
commit_time: u64,
}
impl CommitDataCollector {
@ -573,6 +693,9 @@ impl CommitDataCollector {
if let Some(point) = pending.acquire_point.take() {
self.acquire_points.push(point);
}
if let Some(commit_time) = pending.commit_time.take() {
self.commit_time = self.commit_time.max(commit_time);
}
for ss in pending.subsurfaces.values_mut() {
if let Some(state) = &mut ss.pending.state {
self.collect(state);