diff --git a/src/client.rs b/src/client.rs index f13fff45..1e798af0 100644 --- a/src/client.rs +++ b/src/client.rs @@ -168,7 +168,10 @@ impl Clients { last_xwayland_serial: Cell::new(0), surfaces_by_xwayland_serial: Default::default(), activation_tokens: Default::default(), - commit_timelines: Rc::new(CommitTimelines::new(&global.wait_for_sync_obj)), + commit_timelines: Rc::new(CommitTimelines::new( + &global.wait_for_sync_obj, + &global.ring, + )), }); track!(data, data); let display = Rc::new(WlDisplay::new(&data)); diff --git a/src/ifs/wl_surface.rs b/src/ifs/wl_surface.rs index 63b9101b..80c396d4 100644 --- a/src/ifs/wl_surface.rs +++ b/src/ifs/wl_surface.rs @@ -23,8 +23,8 @@ use { drm_feedback::DrmFeedback, fixed::Fixed, gfx_api::{ - AcquireSync, AsyncShmGfxTexture, BufferResv, BufferResvUser, GfxError, ReleaseSync, - SampleRect, SyncFile, + AsyncShmGfxTexture, BufferResv, BufferResvUser, GfxError, ReleaseSync, SampleRect, + SyncFile, }, ifs::{ wl_buffer::WlBuffer, @@ -190,7 +190,6 @@ struct SurfaceBufferExplicitRelease { pub struct SurfaceBuffer { pub buffer: Rc, sync_files: SmallMap, - pub sync: AcquireSync, pub release_sync: ReleaseSync, release: Option, } @@ -1093,9 +1092,9 @@ impl WlSurface { self.reset_shm_textures(); } buffer.update_texture_or_log(self, false); - let (sync, release_sync) = match pending.explicit_sync { - false => (AcquireSync::Implicit, ReleaseSync::Implicit), - true => (AcquireSync::Unnecessary, ReleaseSync::Explicit), + let release_sync = match pending.explicit_sync { + false => ReleaseSync::Implicit, + true => ReleaseSync::Explicit, }; let release = pending .release_point @@ -1104,7 +1103,6 @@ impl WlSurface { let surface_buffer = SurfaceBuffer { buffer, sync_files: Default::default(), - sync, release_sync, release, }; diff --git a/src/ifs/wl_surface/commit_timeline.rs b/src/ifs/wl_surface/commit_timeline.rs index b71a59cf..da4a2c46 100644 --- a/src/ifs/wl_surface/commit_timeline.rs +++ b/src/ifs/wl_surface/commit_timeline.rs @@ -5,12 +5,14 @@ use { wl_buffer::WlBufferStorage, wl_surface::{PendingState, WlSurface, WlSurfaceError}, }, + io_uring::{IoUring, IoUringError, PendingPoll, PollCallback}, utils::{ clonecell::CloneCell, copyhashmap::CopyHashMap, hash_map_ext::HashMapExt, linkedlist::{LinkedList, LinkedNode, NodeRef}, numcell::NumCell, + oserror::OsError, }, video::drm::{ sync_obj::{SyncObj, SyncObjPoint}, @@ -28,6 +30,7 @@ use { slice, }, thiserror::Error, + uapi::{c::c_short, OwnedFd}, }; const MAX_TIMELINE_DEPTH: usize = 256; @@ -37,6 +40,7 @@ linear_ids!(CommitTimelineIds, CommitTimelineId, u64); pub struct CommitTimelines { next_id: CommitTimelineIds, wfs: Rc, + ring: Rc, depth: NumCell, gc: CopyHashMap>, } @@ -83,15 +87,20 @@ pub enum CommitTimelineError { Depth, #[error("Could not upload a shm texture")] ShmUpload(#[source] GfxError), + #[error("Could not register an implicit-sync wait")] + RegisterImplicitPoll(#[source] IoUringError), + #[error("Could not wait for a dmabuf to become idle")] + PollDmabuf(#[source] OsError), } impl CommitTimelines { - pub fn new(wfs: &Rc) -> Self { + pub fn new(wfs: &Rc, ring: &Rc) -> Self { Self { next_id: Default::default(), depth: NumCell::new(0), wfs: wfs.clone(), gc: Default::default(), + ring: ring.clone(), } } @@ -127,6 +136,7 @@ fn break_loops(list: &LinkedList) { if let EntryKind::Commit(c) = &entry.kind { c.wait_handles.take(); *c.shm_upload.borrow_mut() = ShmUploadState::None; + c.pending_polls.take(); } } } @@ -153,8 +163,15 @@ impl CommitTimeline { ) -> Result<(), CommitTimelineError> { let mut points = SmallVec::new(); let mut pending_uploads = 0; - collect_commit_data(pending, &mut points, &mut pending_uploads); - let has_dependencies = points.is_not_empty() || pending_uploads > 0; + let mut implicit_dmabufs = SmallVec::new(); + collect_commit_data( + pending, + &mut points, + &mut pending_uploads, + &mut implicit_dmabufs, + ); + let has_dependencies = + points.is_not_empty() || pending_uploads > 0 || implicit_dmabufs.is_not_empty(); if !has_dependencies && self.own_timeline.entries.is_empty() { return surface .apply_state(pending) @@ -174,6 +191,8 @@ impl CommitTimeline { wait_handles: Cell::new(Default::default()), pending_uploads: NumCell::new(pending_uploads), shm_upload: RefCell::new(ShmUploadState::None), + num_pending_polls: NumCell::new(implicit_dmabufs.len()), + pending_polls: Cell::new(Default::default()), }), ); let mut needs_flush = false; @@ -198,6 +217,18 @@ impl CommitTimeline { *commit.shm_upload.borrow_mut() = ShmUploadState::Todo(noderef.clone()); needs_flush = true; } + if implicit_dmabufs.is_not_empty() { + let mut pending_polls = SmallVec::new(); + for fd in implicit_dmabufs { + let handle = self + .shared + .ring + .readable_external(&fd, noderef.clone()) + .map_err(CommitTimelineError::RegisterImplicitPoll)?; + pending_polls.push(handle); + } + commit.pending_polls.set(pending_polls); + } } if needs_flush && noderef.prev().is_none() { flush_from(noderef.clone()).map_err(CommitTimelineError::DelayedCommit)?; @@ -246,6 +277,23 @@ impl AsyncShmGfxTextureCallback for NodeRef { } } +impl PollCallback for NodeRef { + fn completed(self: Rc, res: Result) { + let EntryKind::Commit(commit) = &self.kind else { + unreachable!(); + }; + if let Err(e) = res { + commit + .surface + .client + .error(CommitTimelineError::PollDmabuf(e)); + return; + } + commit.num_pending_polls.fetch_sub(1); + flush_commit(&self, commit); + } +} + struct Entry { link: Cell>>, shared: Rc, @@ -272,6 +320,8 @@ struct Commit { wait_handles: Cell>, pending_uploads: NumCell, shm_upload: RefCell, + num_pending_polls: NumCell, + pending_polls: Cell>, } fn flush_from(mut point: NodeRef) -> Result<(), WlSurfaceError> { @@ -304,6 +354,9 @@ impl NodeRef { has_unmet_dependencies = true; } } + if c.num_pending_polls.get() > 0 { + has_unmet_dependencies = true; + } if has_unmet_dependencies { return Ok(false); } @@ -417,18 +470,26 @@ fn collect_commit_data( pending: &mut PendingState, acquire_points: &mut SmallVec<[Point; 1]>, shm_uploads: &mut usize, + implicit_dmabufs: &mut SmallVec<[Rc; 1]>, ) { if let Some(Some(buffer)) = &pending.buffer { if buffer.is_shm() { *shm_uploads += 1; } + if !pending.explicit_sync { + if let Some(dmabuf) = &buffer.dmabuf { + for plane in &dmabuf.planes { + implicit_dmabufs.push(plane.fd.clone()); + } + } + } } if let Some(point) = pending.acquire_point.take() { acquire_points.push(point); } for ss in pending.subsurfaces.values_mut() { if let Some(state) = &mut ss.pending.state { - collect_commit_data(state, acquire_points, shm_uploads); + collect_commit_data(state, acquire_points, shm_uploads, implicit_dmabufs); } } } diff --git a/src/io_uring.rs b/src/io_uring.rs index 60c5291a..7a912ba1 100644 --- a/src/io_uring.rs +++ b/src/io_uring.rs @@ -1,11 +1,14 @@ -pub use ops::TaskResultExt; +pub use ops::{ + poll_external::{PendingPoll, PollCallback}, + TaskResultExt, +}; use { crate::{ async_engine::AsyncEngine, io_uring::{ ops::{ accept::AcceptTask, async_cancel::AsyncCancelTask, connect::ConnectTask, - poll::PollTask, read_write::ReadWriteTask, + poll::PollTask, poll_external::PollExternalTask, read_write::ReadWriteTask, read_write_no_cancel::ReadWriteNoCancelTask, recvmsg::RecvmsgTask, sendmsg::SendmsgTask, timeout::TimeoutTask, timeout_link::TimeoutLinkTask, }, @@ -209,6 +212,7 @@ impl IoUring { cached_read_writes_no_cancel: Default::default(), cached_cancels: Default::default(), cached_polls: Default::default(), + cached_polls_external: Default::default(), cached_sendmsg: Default::default(), cached_recvmsg: Default::default(), cached_timeouts: Default::default(), @@ -270,6 +274,7 @@ struct IoUringData { cached_read_writes_no_cancel: Stack>, cached_cancels: Stack>, cached_polls: Stack>, + cached_polls_external: Stack>, cached_sendmsg: Stack>, cached_recvmsg: Stack>, cached_timeouts: Stack>, diff --git a/src/io_uring/ops.rs b/src/io_uring/ops.rs index 15fc1235..e5f4bf0a 100644 --- a/src/io_uring/ops.rs +++ b/src/io_uring/ops.rs @@ -4,6 +4,7 @@ pub mod accept; pub mod async_cancel; pub mod connect; pub mod poll; +pub mod poll_external; pub mod read_write; pub mod read_write_no_cancel; pub mod recvmsg; diff --git a/src/io_uring/ops/poll_external.rs b/src/io_uring/ops/poll_external.rs new file mode 100644 index 00000000..d17440a0 --- /dev/null +++ b/src/io_uring/ops/poll_external.rs @@ -0,0 +1,116 @@ +use { + crate::{ + io_uring::{ + sys::{io_uring_sqe, IORING_OP_POLL_ADD}, + IoUring, IoUringData, IoUringError, IoUringTaskId, Task, + }, + utils::oserror::OsError, + }, + std::{cell::Cell, rc::Rc}, + uapi::{c, OwnedFd}, +}; + +pub trait PollCallback { + fn completed(self: Rc, res: Result); +} + +pub struct PendingPoll { + data: Rc, + shared: Rc, + id: IoUringTaskId, +} + +impl Drop for PendingPoll { + fn drop(&mut self) { + if self.shared.id.get() != self.id { + return; + } + self.shared.callback.take(); + self.data.cancel_task(self.id); + } +} + +impl IoUring { + pub fn poll_external( + &self, + fd: &Rc, + events: c::c_short, + callback: Rc, + ) -> Result { + self.ring.check_destroyed()?; + let mut pw = self.ring.cached_polls_external.pop().unwrap_or_default(); + pw.shared.id.set(self.ring.id_raw()); + pw.shared.callback.set(Some(callback)); + pw.fd = fd.raw() as _; + pw.events = events as _; + pw.data = Some(Data { _fd: fd.clone() }); + let pending = PendingPoll { + data: self.ring.clone(), + shared: pw.shared.clone(), + id: pw.shared.id.get(), + }; + self.ring.schedule(pw); + Ok(pending) + } + + pub fn readable_external( + &self, + fd: &Rc, + callback: Rc, + ) -> Result { + self.poll_external(fd, c::POLLIN, callback) + } + + #[expect(dead_code)] + pub fn writable_external( + &self, + fd: &Rc, + callback: Rc, + ) -> Result { + self.poll_external(fd, c::POLLOUT, callback) + } +} + +struct Data { + _fd: Rc, +} + +#[derive(Default)] +struct PollExternalTaskShared { + id: Cell, + callback: Cell>>, +} + +#[derive(Default)] +pub struct PollExternalTask { + shared: Rc, + events: u16, + fd: i32, + data: Option, +} + +unsafe impl Task for PollExternalTask { + fn id(&self) -> IoUringTaskId { + self.shared.id.get() + } + + fn complete(mut self: Box, ring: &IoUringData, res: i32) { + self.data.take(); + self.shared.id.set(Default::default()); + if let Some(cb) = self.shared.callback.take() { + let res = if res < 0 { + Err(OsError::from(-res as c::c_int)) + } else { + Ok(res as _) + }; + cb.completed(res) + } + ring.cached_polls_external.push(self); + } + + fn encode(&self, sqe: &mut io_uring_sqe) { + sqe.opcode = IORING_OP_POLL_ADD; + sqe.fd = self.fd; + sqe.u3.poll_events = self.events; + } +} diff --git a/src/renderer.rs b/src/renderer.rs index e1445d03..03c2b87b 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -472,7 +472,7 @@ impl Renderer<'_> { self.base.scale, bounds, Some(buffer.clone()), - buffer.sync.clone(), + AcquireSync::Unnecessary, buffer.release_sync, ); } else if let Some(color) = &buffer.buffer.color {