diff --git a/src/ifs/wl_surface.rs b/src/ifs/wl_surface.rs index 58cddbe3e..913a350f2 100644 --- a/src/ifs/wl_surface.rs +++ b/src/ifs/wl_surface.rs @@ -43,7 +43,6 @@ use crate::gfx_api::AsyncShmGfxTexture; use crate::gfx_api::BufferResv; use crate::gfx_api::BufferResvUser; use crate::gfx_api::FdSync; -use crate::gfx_api::GfxError; use crate::gfx_api::GfxStagingBuffer; use crate::gfx_api::ReleaseSync; use crate::gfx_api::SampleRect; @@ -75,7 +74,6 @@ use crate::ifs::wl_surface::commit_timeline::CommitTimelineError; use crate::ifs::wl_surface::cursor::CursorSurface; use crate::ifs::wl_surface::dnd_icon::DndIcon; use crate::ifs::wl_surface::jay_sync_file_release::SyncFileRelease; -use crate::ifs::wl_surface::prime::PrimeError; use crate::ifs::wl_surface::prime::PrimeSurfaceBuffer; use crate::ifs::wl_surface::prime::SurfacePrimeState; use crate::ifs::wl_surface::tray::TrayItemId; @@ -99,7 +97,6 @@ use crate::ifs::wl_surface::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1Error; use crate::ifs::wp_content_type_v1::ContentType; use crate::ifs::wp_presentation_feedback::PresentationFeedback; use crate::ifs::zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1; -use crate::io_uring::IoUringError; use crate::keyboard::KeyboardState; use crate::leaks::Tracker; use crate::object::BreakLoops; @@ -162,7 +159,6 @@ use crate::utils::obj_and_id::ObjAndId; use crate::utils::obj_and_id::ObjWithId; use crate::utils::reset::Reset; use crate::utils::smallmap::SmallMap; -use crate::video::dmabuf::ChainedCopyError; use crate::video::dmabuf::DMA_BUF_SYNC_READ; use crate::video::drm::syncobj::Syncobj; use crate::video::drm::syncobj::SyncobjPoint; @@ -2532,22 +2528,10 @@ pub enum WlSurfaceError { UnexpectedSyncPoints, #[error("The supplied region is invalid")] InvalidRect, - #[error("There is no render context")] - NoRenderContext, - #[error("Could not create a shm texture")] - CreateAsyncShmTexture(#[source] GfxError), - #[error("Could not prepare upload to a shm texture")] - PrepareAsyncUpload(#[source] GfxError), - #[error("Could not register a commit timeout")] - RegisterCommitTimeout(#[source] IoUringError), #[error("Content update contains release callbacks but no non-null buffer")] SurfaceReleaseWithoutAttach, #[error("Content update contains sync file release but no non-null buffer")] SyncFileReleaseWithoutAttach, - #[error("Could not prepare prime copy")] - PreparePrimeCopy(#[source] PrimeError), - #[error("Could not perform prime copy")] - PrimeCopy(#[source] ChainedCopyError), #[error("The surface already has an extension object")] HasExt, } diff --git a/src/ifs/wl_surface/commit_timeline.rs b/src/ifs/wl_surface/commit_timeline.rs index 05a07cbd1..79ebbde3f 100644 --- a/src/ifs/wl_surface/commit_timeline.rs +++ b/src/ifs/wl_surface/commit_timeline.rs @@ -11,6 +11,7 @@ use crate::ifs::wl_buffer::WlBufferStorage; use crate::ifs::wl_surface::PendingState; use crate::ifs::wl_surface::WlSurface; use crate::ifs::wl_surface::WlSurfaceError; +use crate::ifs::wl_surface::prime::PrimeError; use crate::ifs::wl_surface::prime::PrimeValidity; use crate::io_uring::IoUring; use crate::io_uring::IoUringError; @@ -28,6 +29,7 @@ use crate::utils::box_cache::BoxCache; use crate::utils::box_cache::BoxReset; use crate::utils::box_cache::BoxUninit; use crate::utils::box_cache::CachedBox; +use crate::utils::clonecell::CloneCell; use crate::utils::copyhashmap::CopyHashMap; use crate::utils::hash_map_ext::HashMapExt; use crate::utils::numcell::NumCell; @@ -43,6 +45,7 @@ use crate::video::drm::syncobj::SyncobjPoint; use isnt::std_1::primitive::IsntSliceExt; use isnt::std_1::vec::IsntVecExt; use jay_algorithms::oserror::OsError; +use jay_proc::jay_clone; use smallvec::SmallVec; use std::cell::Cell; use std::cell::LazyCell; @@ -94,6 +97,7 @@ pub struct CommitCache { struct Inner { id: CommitTimelineId, entries: SyncQueue>, + last_buffer_entry: CloneCell>>, } impl ObjWithId for Inner { @@ -110,11 +114,24 @@ fn add_entry(inner: &Rc, shared: &Rc, kind: EntryKind) - inner: inner.clone(), shared: shared.clone(), kind, + next_buffer_entry: Default::default(), + head_of_buffer_queue: Default::default(), }); inner.entries.push(entry.clone()); entry } +fn set_last_buffer_entry(inner: &Rc, entry: &Rc) { + entry.next_buffer_entry.set(BufferTimelineLink::Last); + let prev = inner.last_buffer_entry.set(Some(entry.clone())); + if let Some(prev) = prev { + prev.next_buffer_entry + .set(BufferTimelineLink::Next(entry.clone())); + } else { + entry.head_of_buffer_queue.set(true); + } +} + #[derive(Debug, Error)] pub enum CommitTimelineError { #[error(transparent)] @@ -137,6 +154,16 @@ pub enum CommitTimelineError { CommitTimeout(#[source] OsError), #[error("Could not perform prime copy")] PrimeCopy(#[source] ChainedCopyError), + #[error("Could not register a commit timeout")] + RegisterCommitTimeout(#[source] IoUringError), + #[error("Could not prepare prime copy")] + PreparePrimeCopy(#[source] PrimeError), + #[error("Could not prepare upload to a shm texture")] + PrepareAsyncUpload(#[source] GfxError), + #[error("Could not create a shm texture")] + CreateAsyncShmTexture(#[source] GfxError), + #[error("There is no render context")] + NoRenderContext, } impl CommitTimelines { @@ -167,6 +194,7 @@ impl CommitTimelines { let timeline = Rc::new(Inner { id, entries: Default::default(), + last_buffer_entry: Default::default(), }); CommitTimeline { shared: self.clone(), @@ -205,6 +233,7 @@ fn break_loops(inner: &Rc) { *c.commit_times.borrow_mut() = CommitTimesState::Ready; } } + inner.last_buffer_entry.take(); } impl CommitTimeline { @@ -264,10 +293,12 @@ impl CommitTimeline { acquire_files: Default::default(), commit_time: Default::default(), toplevel_restored: Default::default(), + has_attach: Default::default(), }; collector.collect(&mut pending); let points = collector.acquire_points; let pending_uploads = collector.shm_uploads; + let has_attach = collector.has_attach; let has_dmabuf = collector.has_dmabuf; let needs_prime_copies = collector.needs_prime_copies; let acquire_files = collector.acquire_files; @@ -326,10 +357,10 @@ impl CommitTimeline { })), ); let mut needs_flush = commit_fifo_state == CommitFifoState::Queued; + let EntryKind::Commit(commit) = &entry.kind else { + unreachable!(); + }; if has_dependencies { - let EntryKind::Commit(commit) = &entry.kind else { - unreachable!(); - }; if points.is_not_empty() { let mut wait_handles = SmallVec::new(); for (syncobj, point) in points { @@ -375,8 +406,12 @@ impl CommitTimeline { needs_flush = true; } } + if has_attach { + set_last_buffer_entry(&self.own_timeline, &entry); + schedule_buffer_ops(&entry, commit)?; + } if needs_flush && queue_was_empty { - flush_list(&self.own_timeline).map_err(CommitTimelineError::DelayedCommit)?; + flush_list(&self.own_timeline)?; } Ok(()) } @@ -439,26 +474,38 @@ impl CommitTimeline { } } +impl Commit { + fn error(&self, e: CommitTimelineError) { + self.surface.client.error(e); + } + + fn may_access_buffers(&self) -> bool { + self.syncobj.get() == 0 && self.num_pending_polls.get() == 0 + } +} + impl SyncobjWaiter for Entry { fn done(self: Rc, result: Result<(), SyncobjError>) { let EntryKind::Commit(commit) = &self.kind else { unreachable!(); }; if let Err(e) = result { - commit.surface.client.error(CommitTimelineError::Wait(e)); + commit.error(CommitTimelineError::Wait(e)); + return; + } + if commit.syncobj.sub_fetch(1) > 0 { + return; + } + if schedule_buffer_ops_fatal(&self, commit).is_err() { return; } - commit.syncobj.fetch_sub(1); flush_commit(&self.inner, commit); } } fn flush_commit(list: &Rc, commit: &Commit) { if let Err(e) = flush_list(list) { - commit - .surface - .client - .error(CommitTimelineError::DelayedCommit(e)); + commit.error(e); } } @@ -468,10 +515,7 @@ impl AsyncShmGfxTextureCallback for Entry { unreachable!(); }; if let Err(e) = res { - commit - .surface - .client - .error(CommitTimelineError::ShmUpload(e)); + commit.error(CommitTimelineError::ShmUpload(e)); return; } commit.pending_uploads.fetch_sub(1); @@ -485,10 +529,7 @@ impl ChainedCopyCallback for Entry { unreachable!(); }; if let Err(e) = res { - commit - .surface - .client - .error(CommitTimelineError::PrimeCopy(e)); + commit.error(CommitTimelineError::PrimeCopy(e)); return; } commit.pending_prime_copies.fetch_sub(1); @@ -502,13 +543,15 @@ impl PollCallback for Entry { unreachable!(); }; if let Err(e) = res { - commit - .surface - .client - .error(CommitTimelineError::PollDmabuf(e)); + commit.error(CommitTimelineError::PollDmabuf(e)); + return; + } + if commit.num_pending_polls.sub_fetch(1) > 0 { + return; + } + if schedule_buffer_ops_fatal(&self, commit).is_err() { return; } - commit.num_pending_polls.fetch_sub(1); flush_commit(&self.inner, commit); } } @@ -521,10 +564,7 @@ impl TimeoutCallback for Entry { 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)); + commit.error(CommitTimelineError::CommitTimeout(e)); return; } *commit.commit_times.borrow_mut() = CommitTimesState::Ready; @@ -532,10 +572,21 @@ impl TimeoutCallback for Entry { } } +#[jay_clone] +#[derive(Default)] +enum BufferTimelineLink { + #[default] + Unlinked, + Last, + Next(Rc), +} + struct Entry { inner: Rc, shared: Rc, kind: EntryKind, + next_buffer_entry: CloneCell, + head_of_buffer_queue: Cell, } enum EntryKind { @@ -579,10 +630,11 @@ struct Commit { unmap: Option>, } -fn flush_list(inner: &Rc) -> Result<(), WlSurfaceError> { +fn flush_list(inner: &Rc) -> Result<(), CommitTimelineError> { while let Some(el) = inner.entries.pop() { if el.maybe_apply()? { el.shared.depth.fetch_sub(1); + schedule_next_buffer_ops(inner, &el)?; } else { inner.entries.push_front(el); break; @@ -591,24 +643,56 @@ fn flush_list(inner: &Rc) -> Result<(), WlSurfaceError> { Ok(()) } +fn schedule_next_buffer_ops( + inner: &Rc, + prev: &Rc, +) -> Result<(), CommitTimelineError> { + match prev.next_buffer_entry.take() { + BufferTimelineLink::Unlinked => {} + BufferTimelineLink::Last => { + inner.last_buffer_entry.take(); + } + BufferTimelineLink::Next(next) => { + next.head_of_buffer_queue.set(true); + if let EntryKind::Commit(c) = &next.kind { + schedule_buffer_ops(&next, c)?; + } + } + } + Ok(()) +} + +fn schedule_buffer_ops_fatal(entry: &Rc, c: &Commit) -> Result<(), ()> { + schedule_buffer_ops(entry, c).map_err(|e| { + c.error(e); + }) +} + +fn schedule_buffer_ops(entry: &Rc, c: &Commit) -> Result<(), CommitTimelineError> { + if !c.may_access_buffers() || !entry.head_of_buffer_queue.get() { + return Ok(()); + } + if c.pending_uploads.get() > 0 { + check_shm_uploads(entry, c)?; + } + if c.has_dmabuf && c.pending_prime_copies.get() == 0 { + check_prime_copies(entry, c)?; + } + Ok(()) +} + impl Entry { - fn maybe_apply(self: &Rc) -> Result { + fn maybe_apply(self: &Rc) -> Result { match &self.kind { EntryKind::Commit(c) => { - let mut has_unmet_dependencies = false; - let may_access_buffer = c.syncobj.get() == 0 && c.num_pending_polls.get() == 0; - if may_access_buffer { - if c.pending_uploads.get() > 0 { - check_shm_uploads(self, c)?; - has_unmet_dependencies |= c.pending_uploads.get() > 0; - } - if c.has_dmabuf && c.pending_prime_copies.get() == 0 { - check_prime_copies(self, c)?; - } - has_unmet_dependencies |= c.pending_prime_copies.get() > 0; - } else { - has_unmet_dependencies = true; + let may_access_buffers = c.may_access_buffers(); + if may_access_buffers && c.has_dmabuf && c.pending_prime_copies.get() == 0 { + check_prime_copies(self, c)?; } + let mut has_unmet_dependencies = false; + has_unmet_dependencies |= !may_access_buffers; + has_unmet_dependencies |= c.pending_uploads.get() > 0; + has_unmet_dependencies |= c.pending_prime_copies.get() > 0; let tl = &c.surface.commit_timeline; if tl.fifo_barrier_set.get() { match c.fifo_state.get() { @@ -661,7 +745,9 @@ impl Entry { if has_unmet_dependencies { return Ok(false); } - c.surface.apply_state(c.pending.borrow_mut().deref_mut())?; + c.surface + .apply_state(c.pending.borrow_mut().deref_mut()) + .map_err(CommitTimelineError::DelayedCommit)?; Ok(true) } EntryKind::Wait(signaled) => Ok(signaled.get()), @@ -682,7 +768,7 @@ impl Entry { } } -fn check_shm_uploads(entry: &Rc, c: &Commit) -> Result<(), WlSurfaceError> { +fn check_shm_uploads(entry: &Rc, c: &Commit) -> Result<(), CommitTimelineError> { let state = &mut *c.shm_upload.borrow_mut(); if let ShmUploadState::Todo = state { let mut pending = SmallVec::new(); @@ -693,7 +779,7 @@ fn check_shm_uploads(entry: &Rc, c: &Commit) -> Result<(), WlSurfaceError Ok(()) } -fn check_prime_copies(entry: &Rc, c: &Commit) -> Result<(), WlSurfaceError> { +fn check_prime_copies(entry: &Rc, c: &Commit) -> Result<(), CommitTimelineError> { let validities = &mut *c.prime_validities.borrow_mut(); if let Some(validities) = validities && validities.iter().all(|v| v.valid()) @@ -721,7 +807,7 @@ fn register_commit_time( rc: &Rc, c: &Commit, time: u64, -) -> Result { +) -> Result { 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(); @@ -735,7 +821,7 @@ fn register_commit_time( .shared .ring .timeout_external(timeout, rc.clone(), 0) - .map_err(WlSurfaceError::RegisterCommitTimeout)?; + .map_err(CommitTimelineError::RegisterCommitTimeout)?; *tl.commit_time_waiter.borrow_mut() = Some(CommitTimeWaiter { node: rc.clone(), present: time, @@ -751,7 +837,7 @@ fn schedule_async_uploads( surface: &WlSurface, pending: &PendingState, uploads: &mut SmallVec<[PendingShmTransfer; 1]>, -) -> Result<(), WlSurfaceError> { +) -> Result<(), CommitTimelineError> { if let Some(pending) = schedule_async_upload(node_ref, surface, pending)? { uploads.push(pending); } @@ -767,7 +853,7 @@ fn schedule_async_upload( node_ref: &Rc, surface: &WlSurface, pending: &PendingState, -) -> Result, WlSurfaceError> { +) -> Result, CommitTimelineError> { let Some(Some(buf)) = &pending.buffer else { return Ok(None); }; @@ -785,7 +871,7 @@ fn schedule_async_upload( let ctx = state .render_ctx .get() - .ok_or(WlSurfaceError::NoRenderContext)?; + .ok_or(CommitTimelineError::NoRenderContext)?; if ctx.fast_ram_access() && buf .import_udmabuf_texture(&ctx, mem, *stride, dmabuf_buffer_params) @@ -828,7 +914,7 @@ fn schedule_async_upload( *stride, &state.cpu_worker, ) - .map_err(WlSurfaceError::CreateAsyncShmTexture)?; + .map_err(CommitTimelineError::CreateAsyncShmTexture)?; back.tex.set(Some(back_tex.clone())); back_tex } @@ -836,7 +922,7 @@ fn schedule_async_upload( if let Some(hb) = buf.get_gfx_buffer(&ctx, mem, dmabuf_buffer_params) { return back_tex .async_upload_from_buffer(&hb, node_ref.clone(), back.damage.get()) - .map_err(WlSurfaceError::PrepareAsyncUpload); + .map_err(CommitTimelineError::PrepareAsyncUpload); } let mut staging_opt = surface.shm_staging.get(); if let Some(staging) = &staging_opt @@ -851,7 +937,7 @@ fn schedule_async_upload( .state .render_ctx .get() - .ok_or(WlSurfaceError::NoRenderContext)? + .ok_or(CommitTimelineError::NoRenderContext)? .create_staging_buffer(back_tex.staging_size(), STAGING_UPLOAD); surface.shm_staging.set(Some(s.clone())); s @@ -859,7 +945,7 @@ fn schedule_async_upload( }; back_tex .async_upload(&staging, node_ref.clone(), mem.clone(), back.damage.get()) - .map_err(WlSurfaceError::PrepareAsyncUpload) + .map_err(CommitTimelineError::PrepareAsyncUpload) } fn schedule_prime_copies( @@ -868,7 +954,7 @@ fn schedule_prime_copies( pending: &mut PendingState, copies: &mut SmallVec<[PendingChainedCopy; 1]>, validities: &mut SmallVec<[PrimeValidity; 1]>, -) -> Result<(), WlSurfaceError> { +) -> Result<(), CommitTimelineError> { let state = &surface.state; let Some(ctx) = state.render_ctx.get() else { return Ok(()); @@ -893,7 +979,7 @@ fn schedule_prime_copies_( pending: &mut PendingState, copies: &mut SmallVec<[PendingChainedCopy; 1]>, validities: &mut SmallVec<[PrimeValidity; 1]>, -) -> Result<(), WlSurfaceError> { +) -> Result<(), CommitTimelineError> { let (copy, validity) = schedule_prime_copy(&ctx, render_device, entry, surface, pending)?; if let Some(copy) = copy { copies.push(copy); @@ -923,7 +1009,7 @@ fn schedule_prime_copy( entry: &Rc, surface: &WlSurface, pending: &mut PendingState, -) -> Result<(Option, Option), WlSurfaceError> { +) -> Result<(Option, Option), CommitTimelineError> { let Some(Some(buf)) = &pending.buffer else { return Ok((None, None)); }; @@ -943,7 +1029,7 @@ fn schedule_prime_copy( let allow_lazy = ctx.supports_wait_sync(); let copies = surface .prepare_prime_copies(ctx, render_dev, buf, storage, damage, allow_lazy) - .map_err(WlSurfaceError::PreparePrimeCopy)?; + .map_err(CommitTimelineError::PreparePrimeCopy)?; let Some((copies, psb)) = copies else { pending.prime_buffer = None; return Ok((None, Some(validity))); @@ -951,7 +1037,7 @@ fn schedule_prime_copy( let chain = surface .state .schedule_chained_copy(&copies, entry.clone(), psb.take_sync(), Some(psb.damage())) - .map_err(WlSurfaceError::PrimeCopy)?; + .map_err(CommitTimelineError::PrimeCopy)?; pending.prime_buffer = Some(psb); Ok((chain, Some(validity))) } @@ -969,6 +1055,7 @@ struct CommitDataCollector { acquire_files: SmallVec<[Rc; 1]>, commit_time: u64, toplevel_restored: Option>>, + has_attach: bool, } impl CommitDataCollector @@ -976,6 +1063,7 @@ where F: FnOnce() -> Option, { fn collect(&mut self, pending: &mut PendingState) { + self.has_attach |= pending.buffer.is_some(); if let Some(Some(buffer)) = &pending.buffer { let buffer = &buffer.buf; if buffer.is_shm() { @@ -1029,11 +1117,12 @@ fn set_effective_timeline( let prev = timeline.effective_timeline.set(effective.clone()); if prev.entries.is_not_empty() { let signaled = Rc::new(Cell::new(false)); - add_entry( + let entry = add_entry( effective, &timeline.shared, EntryKind::Wait(signaled.clone()), ); + set_last_buffer_entry(effective, &entry); add_entry( &prev, &timeline.shared,