diff --git a/src/ifs/jay_compositor.rs b/src/ifs/jay_compositor.rs index be54b9c8..443a944d 100644 --- a/src/ifs/jay_compositor.rs +++ b/src/ifs/jay_compositor.rs @@ -24,6 +24,7 @@ use { jay_tree_query::JayTreeQuery, jay_workspace_watcher::JayWorkspaceWatcher, jay_xwayland::JayXwayland, + wl_surface::jay_sync_file_surface::JaySyncFileSurface, }, leaks::Tracker, object::{Object, Version}, @@ -76,7 +77,7 @@ global_base!(JayCompositorGlobal, JayCompositor, JayCompositorError); impl Global for JayCompositorGlobal { fn version(&self) -> u32 { - 25 + 26 } fn required_caps(&self) -> ClientCaps { @@ -520,6 +521,18 @@ impl JayCompositorRequestHandler for JayCompositor { } Ok(()) } + + fn get_sync_file_surface( + &self, + req: GetSyncFileSurface, + _slf: &Rc, + ) -> Result<(), Self::Error> { + let surface = self.client.lookup(req.surface)?; + let obj = Rc::new(JaySyncFileSurface::new(req.id, self.version, &surface)); + track!(self.client, obj); + self.client.add_client_obj(&obj)?; + Ok(()) + } } object_base! { diff --git a/src/ifs/wl_surface.rs b/src/ifs/wl_surface.rs index 4d59924e..c9d6051e 100644 --- a/src/ifs/wl_surface.rs +++ b/src/ifs/wl_surface.rs @@ -2,6 +2,8 @@ pub mod commit_timeline; pub mod cursor; pub mod dnd_icon; pub mod ext_session_lock_surface_v1; +pub mod jay_sync_file_release; +pub mod jay_sync_file_surface; pub mod tray; pub mod wl_subsurface; pub mod wp_alpha_modifier_surface_v1; @@ -31,7 +33,7 @@ use { fixed::Fixed, gfx_api::{ AlphaMode, AsyncShmGfxTexture, BufferResv, BufferResvUser, FdSync, GfxError, - GfxStagingBuffer, ReleaseSync, SampleRect, + GfxStagingBuffer, ReleaseSync, SampleRect, SyncFile, }, ifs::{ color_management::wp_color_management_surface_feedback_v1::WpColorManagementSurfaceFeedbackV1, @@ -52,6 +54,7 @@ use { commit_timeline::{ClearReason, CommitTimeline, CommitTimelineError}, cursor::CursorSurface, dnd_icon::DndIcon, + jay_sync_file_release::SyncFileRelease, tray::TrayItemId, wl_subsurface::{PendingSubsurfaceData, SubsurfaceId, WlSubsurface}, wp_alpha_modifier_surface_v1::WpAlphaModifierSurfaceV1, @@ -90,7 +93,7 @@ use { }, video::{ dmabuf::DMA_BUF_SYNC_READ, - drm::syncobj::{Syncobj, SyncobjPoint}, + drm::syncobj::{Syncobj, SyncobjPoint, merge_sync_files}, }, wire::{ WlOutputId, WlSurfaceId, WpColorManagementSurfaceFeedbackV1Id, ZwpIdleInhibitorV1Id, @@ -219,11 +222,20 @@ pub struct SurfaceBuffer { pub release_sync: ReleaseSync, release: Option, _surface_release: SmallVec<[SurfaceRelease; 1]>, + sync_file_release: Option, } impl Drop for SurfaceBuffer { fn drop(&mut self) { let syncs = self.syncs.take(); + if let Some(release) = &mut self.sync_file_release { + let sync_file = merge_sync_files(syncs.iter().flat_map(|f| f.1.get_sync_file())) + .unwrap_or_else(|e| { + log::error!("Could not merge sync files: {}", ErrorFmt(e)); + None + }); + release.done(sync_file.as_ref()); + } if let Some(release) = &mut self.release { release.signal(Some(&syncs)); return; @@ -456,8 +468,10 @@ struct PendingState { subsurfaces: AHashMap, acquire_point: Option<(Rc, SyncobjPoint)>, release_point: Option, + sync_file_acquire: Option>, + sync_file_release: Option, alpha_multiplier: Option>, - explicit_sync: bool, + syncobj_sync: bool, fifo_barrier_set: bool, fifo_barrier_wait: bool, commit_time: Option, @@ -485,8 +499,9 @@ impl PendingState { self.buffer = Some(buffer); self.acquire_point = next.acquire_point.take(); self.release_point = next.release_point.take(); - self.explicit_sync = mem::take(&mut next.explicit_sync); + self.syncobj_sync = mem::take(&mut next.syncobj_sync); self.surface_release = mem::take(&mut next.surface_release); + self.sync_file_release = next.sync_file_release.take(); } macro_rules! opt { ($name:ident) => { @@ -510,6 +525,7 @@ impl PendingState { opt!(color_description); opt!(serial); opt!(alpha_mode); + opt!(sync_file_acquire); { let (dx1, dy1) = self.offset; let (dx2, dy2) = mem::take(&mut next.offset); @@ -1094,16 +1110,20 @@ impl WlSurfaceRequestHandler for WlSurface { let pending = &mut *self.pending.borrow_mut(); if let Some(Some(buffer)) = &mut pending.buffer && pending.release_point.is_none() + && pending.sync_file_release.is_none() { buffer.send_release = true; } if let Some(release) = &mut pending.release_point { release.committed = true; } - self.verify_explicit_sync(pending)?; + self.verify_syncobj_sync(pending)?; if pending.surface_release.is_not_empty() && not_matches!(pending.buffer, Some(Some(_))) { return Err(WlSurfaceError::SurfaceReleaseWithoutAttach); } + if pending.sync_file_release.is_some() && not_matches!(pending.buffer, Some(Some(_))) { + return Err(WlSurfaceError::SyncFileReleaseWithoutAttach); + } if ext.commit_requested(pending) == CommitAction::ContinueCommit { self.commit_timeline.commit(slf, pending)?; } @@ -1227,16 +1247,17 @@ impl WlSurface { self.reset_shm_textures(); } buffer.buf.update_texture_or_log(self, false); - let release_sync = match pending.explicit_sync { - false => ReleaseSync::Implicit, - true => ReleaseSync::Explicit, - }; + let mut release_sync = ReleaseSync::Implicit; + if pending.syncobj_sync || pending.sync_file_release.is_some() { + release_sync = ReleaseSync::Explicit; + } let surface_buffer = SurfaceBuffer { buffer, syncs: Default::default(), release_sync, release: pending.release_point.take(), _surface_release: mem::take(&mut pending.surface_release), + sync_file_release: pending.sync_file_release.take(), }; self.buffer.set(Some(Rc::new(surface_buffer))); } else { @@ -1542,9 +1563,9 @@ impl WlSurface { } } - fn verify_explicit_sync(&self, pending: &mut PendingState) -> Result<(), WlSurfaceError> { - pending.explicit_sync = self.syncobj_surface.is_some(); - if !pending.explicit_sync { + fn verify_syncobj_sync(&self, pending: &mut PendingState) -> Result<(), WlSurfaceError> { + pending.syncobj_sync = self.syncobj_surface.is_some(); + if !pending.syncobj_sync { return Ok(()); } let have_new_buffer = match &pending.buffer { @@ -2171,6 +2192,8 @@ pub enum WlSurfaceError { 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, } efrom!(WlSurfaceError, ClientError); efrom!(WlSurfaceError, XdgSurfaceError); diff --git a/src/ifs/wl_surface/commit_timeline.rs b/src/ifs/wl_surface/commit_timeline.rs index e5f18262..80ac2b4f 100644 --- a/src/ifs/wl_surface/commit_timeline.rs +++ b/src/ifs/wl_surface/commit_timeline.rs @@ -203,18 +203,18 @@ impl CommitTimeline { let mut collector = CommitDataCollector { acquire_points: Default::default(), shm_uploads: 0, - implicit_dmabufs: Default::default(), + acquire_files: 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 acquire_files = collector.acquire_files; 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() + || acquire_files.is_not_empty() || has_commit_time; let must_be_queued = has_dependencies || self.own_timeline.entries.is_not_empty() @@ -242,7 +242,7 @@ 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()), + num_pending_polls: NumCell::new(acquire_files.len()), pending_polls: Cell::new(Default::default()), fifo_state: Cell::new(commit_fifo_state), commit_times: RefCell::new(CommitTimesState::Ready), @@ -270,9 +270,9 @@ impl CommitTimeline { *commit.shm_upload.borrow_mut() = ShmUploadState::Todo(noderef.clone()); needs_flush = true; } - if implicit_dmabufs.is_not_empty() { + if acquire_files.is_not_empty() { let mut pending_polls = SmallVec::new(); - for fd in implicit_dmabufs { + for fd in acquire_files { let handle = self .shared .ring @@ -692,7 +692,7 @@ type Point = (Rc, SyncobjPoint); struct CommitDataCollector { acquire_points: SmallVec<[Point; 1]>, shm_uploads: usize, - implicit_dmabufs: SmallVec<[Rc; 1]>, + acquire_files: SmallVec<[Rc; 1]>, commit_time: u64, } @@ -703,14 +703,18 @@ impl CommitDataCollector { if buffer.is_shm() { self.shm_uploads += 1; } - if !pending.explicit_sync + if !pending.syncobj_sync + && pending.sync_file_acquire.is_none() && let Some(dmabuf) = &buffer.client_dmabuf { for plane in &dmabuf.planes { - self.implicit_dmabufs.push(plane.fd.clone()); + self.acquire_files.push(plane.fd.clone()); } } } + if let Some(Some(sf)) = pending.sync_file_acquire.take() { + self.acquire_files.push(sf.0); + } if let Some(point) = pending.acquire_point.take() { self.acquire_points.push(point); } diff --git a/src/ifs/wl_surface/jay_sync_file_release.rs b/src/ifs/wl_surface/jay_sync_file_release.rs new file mode 100644 index 00000000..fef6e101 --- /dev/null +++ b/src/ifs/wl_surface/jay_sync_file_release.rs @@ -0,0 +1,92 @@ +use { + crate::{ + client::{Client, ClientError}, + gfx_api::SyncFile, + leaks::Tracker, + object::{Object, Version}, + wire::{JaySyncFileReleaseId, jay_sync_file_release::*}, + }, + std::{cell::Cell, rc::Rc}, + thiserror::Error, +}; + +pub struct SyncFileRelease { + pub release: Option>, +} + +impl SyncFileRelease { + pub fn done(&mut self, sync_file: Option<&SyncFile>) { + if let Some(release) = self.release.take() { + release.done(sync_file); + } + } +} + +impl Drop for SyncFileRelease { + fn drop(&mut self) { + self.done(None); + } +} + +pub struct JaySyncFileRelease { + pub id: JaySyncFileReleaseId, + pub client: Rc, + pub tracker: Tracker, + pub version: Version, + pub destroyed: Cell, +} + +impl JaySyncFileRelease { + pub fn new(client: &Rc, id: JaySyncFileReleaseId, version: Version) -> Self { + Self { + id, + client: client.clone(), + tracker: Default::default(), + version, + destroyed: Cell::new(false), + } + } + + pub fn done(&self, sync_file: Option<&SyncFile>) { + if self.destroyed.get() { + return; + } + match sync_file { + None => { + self.client.event(ReleaseImmediate { self_id: self.id }); + } + Some(fd) => { + self.client.event(ReleaseAsync { + self_id: self.id, + sync_file: fd.0.clone(), + }); + } + } + } +} + +impl JaySyncFileReleaseRequestHandler for JaySyncFileRelease { + type Error = JaySyncFileReleaseError; + + fn destroy(&self, _req: Destroy, _slf: &Rc) -> Result<(), Self::Error> { + self.destroyed.set(true); + self.client.remove_obj(self)?; + Ok(()) + } +} + +object_base! { + self = JaySyncFileRelease; + version = self.version; +} + +impl Object for JaySyncFileRelease {} + +simple_add_obj!(JaySyncFileRelease); + +#[derive(Debug, Error)] +pub enum JaySyncFileReleaseError { + #[error(transparent)] + ClientError(Box), +} +efrom!(JaySyncFileReleaseError, ClientError); diff --git a/src/ifs/wl_surface/jay_sync_file_surface.rs b/src/ifs/wl_surface/jay_sync_file_surface.rs new file mode 100644 index 00000000..7c369bb4 --- /dev/null +++ b/src/ifs/wl_surface/jay_sync_file_surface.rs @@ -0,0 +1,92 @@ +use { + crate::{ + client::{Client, ClientError}, + gfx_api::SyncFile, + ifs::wl_surface::{ + WlSurface, + jay_sync_file_release::{JaySyncFileRelease, SyncFileRelease}, + }, + leaks::Tracker, + object::{Object, Version}, + wire::{JaySyncFileSurfaceId, jay_sync_file_surface::*}, + }, + std::rc::Rc, + thiserror::Error, +}; + +pub struct JaySyncFileSurface { + pub id: JaySyncFileSurfaceId, + pub client: Rc, + pub surface: Rc, + pub tracker: Tracker, + pub version: Version, +} + +impl JaySyncFileSurface { + pub fn new(id: JaySyncFileSurfaceId, version: Version, surface: &Rc) -> Self { + Self { + id, + client: surface.client.clone(), + surface: surface.clone(), + tracker: Default::default(), + version, + } + } +} + +impl JaySyncFileSurfaceRequestHandler for JaySyncFileSurface { + type Error = JaySyncFileSurfaceError; + + fn destroy(&self, _req: Destroy, _slf: &Rc) -> Result<(), Self::Error> { + self.client.remove_obj(self)?; + Ok(()) + } + + fn set_acquire_immediate( + &self, + _req: SetAcquireImmediate, + _slf: &Rc, + ) -> Result<(), Self::Error> { + self.surface.pending.borrow_mut().sync_file_acquire = Some(None); + Ok(()) + } + + fn set_acquire_async(&self, req: SetAcquireAsync, _slf: &Rc) -> Result<(), Self::Error> { + self.surface.pending.borrow_mut().sync_file_acquire = Some(Some(SyncFile(req.sync_file))); + Ok(()) + } + + fn get_release(&self, req: GetRelease, _slf: &Rc) -> Result<(), Self::Error> { + let obj = Rc::new(JaySyncFileRelease::new( + &self.client, + req.release, + self.version, + )); + track!(self.client, obj); + self.client.add_client_obj(&obj)?; + let pending = &mut *self.surface.pending.borrow_mut(); + if pending.sync_file_release.is_some() { + return Err(JaySyncFileSurfaceError::HasRelease); + } + pending.sync_file_release = Some(SyncFileRelease { release: Some(obj) }); + Ok(()) + } +} + +object_base! { + self = JaySyncFileSurface; + version = self.version; +} + +impl Object for JaySyncFileSurface {} + +simple_add_obj!(JaySyncFileSurface); + +#[derive(Debug, Error)] +pub enum JaySyncFileSurfaceError { + #[error(transparent)] + ClientError(Box), + #[error("The content update already has a release object")] + HasRelease, +} +efrom!(JaySyncFileSurfaceError, ClientError); diff --git a/src/video/drm/syncobj.rs b/src/video/drm/syncobj.rs index 160528fa..23529748 100644 --- a/src/video/drm/syncobj.rs +++ b/src/video/drm/syncobj.rs @@ -352,7 +352,7 @@ fn destroy(drm: &OwnedFd, handle: SyncobjHandle) { } } -fn merge_sync_files<'a, I>(sync_files: I) -> Result, DrmError> +pub fn merge_sync_files<'a, I>(sync_files: I) -> Result, DrmError> where I: IntoIterator, { diff --git a/src/wl_usr/usr_ifs.rs b/src/wl_usr/usr_ifs.rs index c2272c7c..82e1ab77 100644 --- a/src/wl_usr/usr_ifs.rs +++ b/src/wl_usr/usr_ifs.rs @@ -7,6 +7,8 @@ pub mod usr_jay_render_ctx; pub mod usr_jay_screencast; pub mod usr_jay_select_toplevel; pub mod usr_jay_select_workspace; +pub mod usr_jay_sync_file_release; +pub mod usr_jay_sync_file_surface; pub mod usr_jay_toplevel; pub mod usr_jay_workspace; pub mod usr_jay_workspace_watcher; diff --git a/src/wl_usr/usr_ifs/usr_jay_compositor.rs b/src/wl_usr/usr_ifs/usr_jay_compositor.rs index 186e8e4c..8ad821ec 100644 --- a/src/wl_usr/usr_ifs/usr_jay_compositor.rs +++ b/src/wl_usr/usr_ifs/usr_jay_compositor.rs @@ -12,8 +12,9 @@ use { usr_jay_screencast::UsrJayScreencast, usr_jay_select_toplevel::UsrJaySelectToplevel, usr_jay_select_workspace::UsrJaySelectWorkspace, + usr_jay_sync_file_surface::UsrJaySyncFileSurface, usr_jay_workspace_watcher::UsrJayWorkspaceWatcher, usr_wl_output::UsrWlOutput, - usr_wl_seat::UsrWlSeat, + usr_wl_seat::UsrWlSeat, usr_wl_surface::UsrWlSurface, }, usr_object::UsrObject, }, @@ -187,6 +188,22 @@ impl UsrJayCompositor { self.con.add_object(obj.clone()); obj } + + #[expect(dead_code)] + pub fn get_sync_file_surface(&self, surface: &UsrWlSurface) -> Rc { + let obj = Rc::new(UsrJaySyncFileSurface { + id: self.con.id(), + con: self.con.clone(), + version: self.version, + }); + self.con.request(GetSyncFileSurface { + self_id: self.id, + id: obj.id, + surface: surface.id, + }); + self.con.add_object(obj.clone()); + obj + } } impl JayCompositorEventHandler for UsrJayCompositor { diff --git a/src/wl_usr/usr_ifs/usr_jay_sync_file_release.rs b/src/wl_usr/usr_ifs/usr_jay_sync_file_release.rs new file mode 100644 index 00000000..7564fa15 --- /dev/null +++ b/src/wl_usr/usr_ifs/usr_jay_sync_file_release.rs @@ -0,0 +1,60 @@ +use { + crate::{ + gfx_api::SyncFile, + object::Version, + utils::clonecell::CloneCell, + wire::{JaySyncFileReleaseId, jay_sync_file_release::*}, + wl_usr::{UsrCon, usr_object::UsrObject}, + }, + std::{convert::Infallible, rc::Rc}, + uapi::OwnedFd, +}; + +pub struct UsrJaySyncFileRelease { + pub id: JaySyncFileReleaseId, + pub con: Rc, + pub owner: CloneCell>>, + pub version: Version, +} + +pub trait UsrJaySyncFileReleaseOwner { + fn release(&self, sync_file: Option); +} + +impl UsrJaySyncFileRelease { + fn release(&self, sf: Option>) { + if let Some(owner) = self.owner.get() { + owner.release(sf.map(SyncFile)); + } + self.con.remove_obj(self); + } +} + +impl JaySyncFileReleaseEventHandler for UsrJaySyncFileRelease { + type Error = Infallible; + + fn release_immediate(&self, _ev: ReleaseImmediate, _slf: &Rc) -> Result<(), Self::Error> { + self.release(None); + Ok(()) + } + + fn release_async(&self, ev: ReleaseAsync, _slf: &Rc) -> Result<(), Self::Error> { + self.release(Some(ev.sync_file)); + Ok(()) + } +} + +usr_object_base! { + self = UsrJaySyncFileRelease = JaySyncFileRelease; + version = self.version; +} + +impl UsrObject for UsrJaySyncFileRelease { + fn destroy(&self) { + self.con.request(Destroy { self_id: self.id }); + } + + fn break_loops(&self) { + self.owner.take(); + } +} diff --git a/src/wl_usr/usr_ifs/usr_jay_sync_file_surface.rs b/src/wl_usr/usr_ifs/usr_jay_sync_file_surface.rs new file mode 100644 index 00000000..533cfdf2 --- /dev/null +++ b/src/wl_usr/usr_ifs/usr_jay_sync_file_surface.rs @@ -0,0 +1,66 @@ +use { + crate::{ + gfx_api::SyncFile, + object::Version, + wire::{JaySyncFileSurfaceId, jay_sync_file_surface::*}, + wl_usr::{ + UsrCon, usr_ifs::usr_jay_sync_file_release::UsrJaySyncFileRelease, + usr_object::UsrObject, + }, + }, + std::{convert::Infallible, rc::Rc}, +}; + +pub struct UsrJaySyncFileSurface { + pub id: JaySyncFileSurfaceId, + pub con: Rc, + pub version: Version, +} + +impl UsrJaySyncFileSurface { + #[expect(dead_code)] + pub fn set_acquire(&self, sf: Option<&SyncFile>) { + match sf { + None => { + self.con.request(SetAcquireImmediate { self_id: self.id }); + } + Some(sf) => { + self.con.request(SetAcquireAsync { + self_id: self.id, + sync_file: sf.0.clone(), + }); + } + } + } + + #[expect(dead_code)] + pub fn get_release(&self) -> Rc { + let obj = Rc::new(UsrJaySyncFileRelease { + id: self.con.id(), + con: self.con.clone(), + owner: Default::default(), + version: self.version, + }); + self.con.request(GetRelease { + self_id: self.id, + release: obj.id, + }); + self.con.add_object(obj.clone()); + obj + } +} + +impl JaySyncFileSurfaceEventHandler for UsrJaySyncFileSurface { + type Error = Infallible; +} + +usr_object_base! { + self = UsrJaySyncFileSurface = JaySyncFileSurface; + version = self.version; +} + +impl UsrObject for UsrJaySyncFileSurface { + fn destroy(&self) { + self.con.request(Destroy { self_id: self.id }); + } +} diff --git a/wire/jay_compositor.txt b/wire/jay_compositor.txt index 2f5b74b8..23b200d0 100644 --- a/wire/jay_compositor.txt +++ b/wire/jay_compositor.txt @@ -126,6 +126,11 @@ request get_tagged_acceptor (since = 25) { tag: str, } +request get_sync_file_surface (since = 26) { + id: id(jay_sync_file_surface), + surface: id(wl_surface), +} + # events event client_id { diff --git a/wire/jay_sync_file_release.txt b/wire/jay_sync_file_release.txt new file mode 100644 index 00000000..2f1981ea --- /dev/null +++ b/wire/jay_sync_file_release.txt @@ -0,0 +1,11 @@ +request destroy { + +} + +event release_immediate { + +} + +event release_async { + sync_file: fd, +} diff --git a/wire/jay_sync_file_surface.txt b/wire/jay_sync_file_surface.txt new file mode 100644 index 00000000..bd8cf9a9 --- /dev/null +++ b/wire/jay_sync_file_surface.txt @@ -0,0 +1,15 @@ +request destroy { + +} + +request set_acquire_immediate { + +} + +request set_acquire_async { + sync_file: fd, +} + +request get_release { + release: id(jay_sync_file_release) (new), +}