wayland: implement commit-timing-v1
This commit is contained in:
parent
d45aaffdb3
commit
fac5445f2e
19 changed files with 434 additions and 15 deletions
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue