use { crate::{ gfx_api::SyncFile, utils::{ clonecell::{CloneCell, UnsafeCellCloneSafe}, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, linkedlist::{LinkedList, LinkedNode}, oserror::OsError, }, video::drm::{ sys::{ sync_ioc_merge, sync_obj_create, sync_obj_destroy, sync_obj_eventfd, sync_obj_fd_to_handle, sync_obj_handle_to_fd, sync_obj_signal, sync_obj_transfer, DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE, DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE, }, DrmError, }, }, std::{ rc::Rc, sync::atomic::{AtomicU64, Ordering::Relaxed}, }, uapi::{c, OwnedFd}, }; static SYNCOBJ_ID: AtomicU64 = AtomicU64::new(0); #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] pub struct SyncObjId(u64); #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] struct SyncObjHandle(u32); unsafe impl UnsafeCellCloneSafe for SyncObjHandle {} pub struct SyncObj { id: SyncObjId, fd: Rc, importers: LinkedList>, } #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)] pub struct SyncObjPoint(pub u64); impl SyncObj { pub fn new(fd: &Rc) -> Self { Self { id: SyncObjId(SYNCOBJ_ID.fetch_add(1, Relaxed)), fd: fd.clone(), importers: Default::default(), } } } impl Drop for SyncObj { fn drop(&mut self) { let mut links = vec![]; for importer in self.importers.iter() { if let Some(handle) = importer.handles.remove(&self.id) { destroy(&importer.drm, handle); } if let Some(link) = importer.links.remove(&self.id) { links.push(link); } } } } struct Handles { drm: Rc, handles: CopyHashMap, links: CopyHashMap>>, } pub struct SyncObjCtx { inner: Rc, dummy: CloneCell>>, } impl SyncObjCtx { pub fn new(drm: &Rc) -> Self { Self { inner: Rc::new(Handles { drm: drm.clone(), handles: Default::default(), links: Default::default(), }), dummy: Default::default(), } } fn get_handle(&self, sync_obj: &SyncObj) -> Result { 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) .map_err(DrmError::ImportSyncObj)?; let handle = SyncObjHandle(handle); let link = sync_obj.importers.add_last(self.inner.clone()); self.inner.handles.set(sync_obj.id, handle); self.inner.links.set(sync_obj.id, link); Ok(handle) } pub fn create_sync_obj(&self) -> Result { let handle = sync_obj_create(self.inner.drm.raw(), 0).map_err(DrmError::CreateSyncObj)?; let handle = SyncObjHandle(handle); let fd = sync_obj_handle_to_fd(self.inner.drm.raw(), handle.0, 0); if fd.is_err() { destroy(&self.inner.drm, handle); } let fd = fd.map_err(DrmError::ExportSyncObj).map(Rc::new)?; let sync_obj = SyncObj::new(&fd); let link = sync_obj.importers.add_last(self.inner.clone()); self.inner.handles.set(sync_obj.id, handle); self.inner.links.set(sync_obj.id, link); Ok(sync_obj) } pub fn wait_for_point( &self, eventfd: &OwnedFd, sync_obj: &SyncObj, point: SyncObjPoint, signaled: bool, ) -> Result<(), DrmError> { let handle = self.get_handle(sync_obj)?; let flags = match signaled { true => 0, false => DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE, }; sync_obj_eventfd( self.inner.drm.raw(), eventfd.raw(), handle.0, point.0, flags, ) .map_err(DrmError::RegisterEventfd) } pub fn supports_async_wait(&self) -> bool { self.supports_async_wait_().is_ok() } fn supports_async_wait_(&self) -> Result<(), DrmError> { let sync_obj = self.create_sync_obj()?; let eventfd = uapi::eventfd(0, c::EFD_CLOEXEC) .map_err(OsError::from) .map_err(DrmError::EventFd)?; self.wait_for_point(&eventfd, &sync_obj, SyncObjPoint(1), true)?; Ok(()) } 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) } pub fn import_sync_files<'a, I>( &self, sync_obj: &SyncObj, point: SyncObjPoint, sync_files: I, ) -> Result<(), DrmError> where I: IntoIterator, { let mut sync_files = sync_files.into_iter(); let Some(first) = sync_files.next() 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 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)?; self.transfer(&dummy, SyncObjPoint(0), sync_obj, point) } fn transfer( &self, src_sync_obj: &SyncObj, src_point: SyncObjPoint, dst_sync_obj: &SyncObj, dst_point: SyncObjPoint, ) -> Result<(), DrmError> { let src_handle = self.get_handle(src_sync_obj)?; let dst_handle = self.get_handle(dst_sync_obj)?; sync_obj_transfer( self.inner.drm.raw(), src_handle.0, src_point.0, dst_handle.0, dst_point.0, 0, ) .map_err(DrmError::TransferPoint) } fn get_dummy(&self) -> Result, DrmError> { match self.dummy.get() { Some(d) => Ok(d), None => { let d = Rc::new(self.create_sync_obj()?); self.dummy.set(Some(d.clone())); Ok(d) } } } } impl Drop for SyncObjCtx { fn drop(&mut self) { self.inner.links.clear(); let mut map = self.inner.handles.lock(); for (_, handle) in map.drain() { destroy(&self.inner.drm, handle); } } } fn destroy(drm: &OwnedFd, handle: SyncObjHandle) { if let Err(e) = sync_obj_destroy(drm.raw(), handle.0) { log::error!("Could not destroy sync obj: {}", ErrorFmt(e)); } }