diff --git a/src/video/drm/sync_obj.rs b/src/video/drm/sync_obj.rs index 7b04fd9f..a4223640 100644 --- a/src/video/drm/sync_obj.rs +++ b/src/video/drm/sync_obj.rs @@ -13,6 +13,7 @@ use { DrmError, sys::{ DRM_SYNCOBJ_CREATE_SIGNALED, DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE, + DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_TIMELINE, DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE, DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE, sync_ioc_merge, sync_obj_create, sync_obj_destroy, sync_obj_eventfd, sync_obj_fd_to_handle, sync_obj_handle_to_fd, @@ -21,6 +22,7 @@ use { }, }, std::{ + cell::OnceCell, rc::Rc, sync::atomic::{AtomicU64, Ordering::Relaxed}, }, @@ -84,6 +86,7 @@ struct Handles { pub struct SyncObjCtx { inner: Rc, dummy: CloneCell>>, + supports_timeline_import: OnceCell, } impl SyncObjCtx { @@ -95,6 +98,7 @@ impl SyncObjCtx { links: Default::default(), }), dummy: Default::default(), + supports_timeline_import: Default::default(), } } @@ -102,7 +106,7 @@ impl SyncObjCtx { if let Some(handle) = self.inner.handles.get(&sync_obj.id) { return Ok(handle); } - let handle = sync_obj_fd_to_handle(self.inner.drm.raw(), sync_obj.fd.raw(), 0, 0) + let handle = sync_obj_fd_to_handle(self.inner.drm.raw(), sync_obj.fd.raw(), 0, 0, 0) .map_err(DrmError::ImportSyncObj)?; let handle = SyncObjHandle(handle); let link = sync_obj.importers.add_last(self.inner.clone()); @@ -176,6 +180,40 @@ impl SyncObjCtx { Ok(()) } + fn supports_timeline_import(&self) -> bool { + *self + .supports_timeline_import + .get_or_init(|| match self.test_timeline_import() { + Ok(_) => { + log::info!("Kernel supports sync file timeline import"); + true + } + Err(e) => { + log::warn!( + "Kernel does not support sync file timeline import: {}", + ErrorFmt(e), + ); + false + } + }) + } + + fn test_timeline_import(&self) -> Result<(), DrmError> { + let sync_obj = self.create_sync_obj()?; + let sync_obj = self.get_handle(&sync_obj)?; + let sync_file = self.create_signaled_sync_file()?; + sync_obj_fd_to_handle( + self.inner.drm.raw(), + sync_file.raw(), + DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE + | DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_TIMELINE, + sync_obj.0, + 123, + ) + .map(drop) + .map_err(DrmError::ImportSyncFile) + } + pub fn signal(&self, sync_obj: &SyncObj, point: SyncObjPoint) -> Result<(), DrmError> { let handle = self.get_handle(sync_obj)?; sync_obj_signal(self.inner.drm.raw(), handle.0, point.0).map_err(DrmError::SignalSyncObj) @@ -190,24 +228,28 @@ impl SyncObjCtx { where I: IntoIterator, { - let mut sync_files = sync_files.into_iter(); - let Some(first) = sync_files.next() else { + let Some(fd) = merge_sync_files(sync_files)? else { return self.signal(sync_obj, point); }; - let mut stash; - let mut fd = &*first.0; - for next in sync_files { - stash = sync_ioc_merge(fd.raw(), next.raw()).map_err(DrmError::Merge)?; - fd = &stash; + let import = |flags: u32, handle: SyncObjHandle| { + sync_obj_fd_to_handle( + self.inner.drm.raw(), + fd.raw(), + DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE | flags, + handle.0, + point.0, + ) + .map(drop) + .map_err(DrmError::ImportSyncFile) + }; + if self.supports_timeline_import() { + return import( + DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_TIMELINE, + self.get_handle(sync_obj)?, + ); } let dummy = self.get_dummy()?; - sync_obj_fd_to_handle( - self.inner.drm.raw(), - fd.raw(), - DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE, - self.get_handle(&dummy)?.0, - ) - .map_err(DrmError::ImportSyncFile)?; + import(0, self.get_handle(&dummy)?)?; self.transfer(&dummy, SyncObjPoint(0), sync_obj, point) } @@ -258,3 +300,24 @@ fn destroy(drm: &OwnedFd, handle: SyncObjHandle) { log::error!("Could not destroy sync obj: {}", ErrorFmt(e)); } } + +fn merge_sync_files<'a, I>(sync_files: I) -> Result, DrmError> +where + I: IntoIterator, +{ + let mut sync_files = sync_files.into_iter(); + let Some(first) = sync_files.next() else { + return Ok(None); + }; + let Some(second) = sync_files.next() else { + return Ok(Some(first.clone())); + }; + let merge = |left: &OwnedFd, right: &OwnedFd| { + sync_ioc_merge(left.raw(), right.raw()).map_err(DrmError::Merge) + }; + let mut fd = merge(first, second)?; + for next in sync_files { + fd = merge(&fd, next)?; + } + Ok(Some(SyncFile(Rc::new(fd)))) +} diff --git a/src/video/drm/sys.rs b/src/video/drm/sys.rs index 8cd29a95..f39ca14a 100644 --- a/src/video/drm/sys.rs +++ b/src/video/drm/sys.rs @@ -1220,6 +1220,8 @@ pub fn sync_obj_destroy(drm: c::c_int, handle: u32) -> Result<(), OsError> { } pub const DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE: u32 = 1 << 0; +pub const DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_TIMELINE: u32 = 1 << 1; + pub const DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE: u32 = 1 << 0; #[repr(C)] @@ -1228,6 +1230,7 @@ struct drm_syncobj_handle { flags: u32, fd: i32, pad: u32, + point: u64, } const DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD: u64 = drm_iowr::(0xC1); @@ -1239,6 +1242,7 @@ pub fn sync_obj_handle_to_fd(drm: c::c_int, handle: u32, flags: u32) -> Result Result { let mut res = drm_syncobj_handle { handle, flags, fd, pad: 0, + point, }; unsafe { ioctl(drm, DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE, &mut res)?;