1
0
Fork 0
forked from wry/wry

wayland: implement linux-drm-syncobj-v1

This commit is contained in:
Julian Orth 2024-03-21 20:54:21 +01:00
parent 816315170f
commit aaf73d6fdc
29 changed files with 1507 additions and 35 deletions

View file

@ -1,4 +1,6 @@
pub mod sync_obj;
mod sys;
pub mod wait_for_sync_obj;
use {
crate::{
@ -52,7 +54,7 @@ pub use sys::{
#[derive(Debug, Error)]
pub enum DrmError {
#[error("Could not reopen a node")]
ReopenNode(#[source] crate::utils::oserror::OsError),
ReopenNode(#[source] OsError),
#[error("Could not retrieve the render node name")]
RenderNodeName(#[source] OsError),
#[error("Could not retrieve the device node name")]
@ -113,6 +115,28 @@ pub enum DrmError {
Version(#[source] OsError),
#[error("Format of IN_FORMATS property is invalid")]
InFormats,
#[error("Could not import a sync obj")]
ImportSyncObj(#[source] OsError),
#[error("Could not create a sync obj")]
CreateSyncObj(#[source] OsError),
#[error("Could not export a sync obj")]
ExportSyncObj(#[source] OsError),
#[error("Could not register an eventfd with a sync obj")]
RegisterEventfd(#[source] OsError),
#[error("Could not create an eventfd")]
EventFd(#[source] OsError),
#[error("Could not read from an eventfd")]
ReadEventFd(#[source] IoUringError),
#[error("No sync obj context available")]
NoSyncObjContextAvailable,
#[error("Could not signal the sync obj")]
SignalSyncObj(#[source] OsError),
#[error("Could not transfer a sync obj point")]
TransferPoint(#[source] OsError),
#[error("Could not merge two sync files")]
Merge(#[source] OsError),
#[error("Could not import a sync file into a sync obj")]
ImportSyncFile(#[source] OsError),
}
fn render_node_name(fd: c::c_int) -> Result<Ustring, DrmError> {

238
src/video/drm/sync_obj.rs Normal file
View file

@ -0,0 +1,238 @@
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<OwnedFd>,
importers: LinkedList<Rc<Handles>>,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct SyncObjPoint(pub u64);
impl SyncObj {
pub fn new(fd: &Rc<OwnedFd>) -> 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<OwnedFd>,
handles: CopyHashMap<SyncObjId, SyncObjHandle>,
links: CopyHashMap<SyncObjId, LinkedNode<Rc<Handles>>>,
}
pub struct SyncObjCtx {
inner: Rc<Handles>,
dummy: CloneCell<Option<Rc<SyncObj>>>,
}
impl SyncObjCtx {
pub fn new(drm: &Rc<OwnedFd>) -> 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<SyncObjHandle, DrmError> {
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<SyncObj, DrmError> {
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<Item = &'a SyncFile>,
{
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<Rc<SyncObj>, 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));
}
}

View file

@ -1156,3 +1156,201 @@ pub struct drm_format_modifier {
}
unsafe impl Pod for drm_format_modifier {}
// pub const DRM_SYNCOBJ_CREATE_SIGNALED: u32 = 1 << 0;
#[repr(C)]
struct drm_syncobj_create {
handle: u32,
flags: u32,
}
const DRM_IOCTL_SYNCOBJ_CREATE: u64 = drm_iowr::<drm_syncobj_create>(0xBF);
pub fn sync_obj_create(drm: c::c_int, flags: u32) -> Result<u32, OsError> {
let mut res = drm_syncobj_create { handle: 0, flags };
unsafe {
ioctl(drm, DRM_IOCTL_SYNCOBJ_CREATE, &mut res)?;
}
Ok(res.handle)
}
#[repr(C)]
struct drm_syncobj_destroy {
handle: u32,
pad: u32,
}
const DRM_IOCTL_SYNCOBJ_DESTROY: u64 = drm_iowr::<drm_syncobj_destroy>(0xC0);
pub fn sync_obj_destroy(drm: c::c_int, handle: u32) -> Result<(), OsError> {
let mut res = drm_syncobj_destroy { handle, pad: 0 };
unsafe {
ioctl(drm, DRM_IOCTL_SYNCOBJ_DESTROY, &mut res)?;
}
Ok(())
}
pub const DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE: u32 = 1 << 0;
// pub const DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE: u32 = 1 << 0;
#[repr(C)]
struct drm_syncobj_handle {
handle: u32,
flags: u32,
fd: i32,
pad: u32,
}
const DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD: u64 = drm_iowr::<drm_syncobj_handle>(0xC1);
const DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE: u64 = drm_iowr::<drm_syncobj_handle>(0xC2);
pub fn sync_obj_handle_to_fd(drm: c::c_int, handle: u32, flags: u32) -> Result<OwnedFd, OsError> {
let mut res = drm_syncobj_handle {
handle,
flags,
fd: 0,
pad: 0,
};
unsafe {
ioctl(drm, DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD, &mut res)?;
}
Ok(OwnedFd::new(res.fd))
}
pub fn sync_obj_fd_to_handle(
drm: c::c_int,
fd: c::c_int,
flags: u32,
handle: u32,
) -> Result<u32, OsError> {
let mut res = drm_syncobj_handle {
handle,
flags,
fd,
pad: 0,
};
unsafe {
ioctl(drm, DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE, &mut res)?;
}
Ok(res.handle)
}
// pub const DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL: u32 = 1 << 0;
// pub const DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT: u32 = 1 << 1;
pub const DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE: u32 = 1 << 2;
#[repr(C)]
struct drm_syncobj_eventfd {
handle: u32,
flags: u32,
point: u64,
fd: i32,
pad: u32,
}
const DRM_IOCTL_SYNCOBJ_EVENTFD: u64 = drm_iowr::<drm_syncobj_eventfd>(0xCF);
pub fn sync_obj_eventfd(
drm: c::c_int,
eventfd: c::c_int,
handle: u32,
point: u64,
flags: u32,
) -> Result<(), OsError> {
let mut res = drm_syncobj_eventfd {
handle,
flags,
point,
fd: eventfd,
pad: 0,
};
unsafe {
ioctl(drm, DRM_IOCTL_SYNCOBJ_EVENTFD, &mut res)?;
}
Ok(())
}
#[repr(C)]
struct drm_syncobj_timeline_array {
handles: u64,
points: u64,
count_handles: u32,
flags: u32,
}
const DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL: u64 = drm_iowr::<drm_syncobj_timeline_array>(0xCD);
pub fn sync_obj_signal(drm: c::c_int, handle: u32, point: u64) -> Result<(), OsError> {
let mut res = drm_syncobj_timeline_array {
handles: &handle as *const u32 as u64,
points: &point as *const u64 as u64,
count_handles: 1,
flags: 0,
};
unsafe {
ioctl(drm, DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL, &mut res)?;
}
Ok(())
}
#[repr(C)]
struct drm_syncobj_transfer {
src_handle: u32,
dst_handle: u32,
src_point: u64,
dst_point: u64,
flags: u32,
pad: u32,
}
const DRM_IOCTL_SYNCOBJ_TRANSFER: u64 = drm_iowr::<drm_syncobj_transfer>(0xCC);
pub fn sync_obj_transfer(
drm: c::c_int,
src_handle: u32,
src_point: u64,
dst_handle: u32,
dst_point: u64,
flags: u32,
) -> Result<(), OsError> {
let mut res = drm_syncobj_transfer {
src_handle,
dst_handle,
src_point,
dst_point,
flags,
pad: 0,
};
unsafe {
ioctl(drm, DRM_IOCTL_SYNCOBJ_TRANSFER, &mut res)?;
}
Ok(())
}
#[repr(C)]
struct sync_merge_data {
name: [u8; 32],
fd2: i32,
fence: i32,
flags: u32,
pad: u32,
}
const SYNC_IOC_MAGIC: u64 = b'>' as _;
const SYNC_IOC_MERGE: u64 = uapi::_IOWR::<sync_merge_data>(SYNC_IOC_MAGIC, 3);
pub fn sync_ioc_merge(left: c::c_int, right: c::c_int) -> Result<OwnedFd, OsError> {
let mut res = sync_merge_data {
name: [0; 32],
fd2: right,
fence: 0,
flags: 0,
pad: 0,
};
unsafe {
ioctl(left, SYNC_IOC_MERGE, &mut res)?;
}
Ok(OwnedFd::new(res.fence))
}

View file

@ -0,0 +1,199 @@
use {
crate::{
async_engine::{AsyncEngine, SpawnedFuture},
io_uring::IoUring,
utils::{
asyncevent::AsyncEvent, buf::Buf, clonecell::CloneCell, copyhashmap::CopyHashMap,
numcell::NumCell, oserror::OsError, stack::Stack,
},
video::drm::{
sync_obj::{SyncObj, SyncObjCtx, SyncObjPoint},
DrmError,
},
},
std::{cell::Cell, rc::Rc},
uapi::{c, OwnedFd},
};
pub struct WaitForSyncObj {
inner: Rc<Inner>,
eng: Rc<AsyncEngine>,
}
pub trait SyncObjWaiter {
fn done(self: Rc<Self>, result: Result<(), DrmError>);
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
struct JobId(u64);
pub struct WaitForSyncObjHandle {
inner: Rc<Inner>,
id: JobId,
}
struct Inner {
ctx: CloneCell<Option<Rc<SyncObjCtx>>>,
next_id: NumCell<u64>,
ring: Rc<IoUring>,
busy: CopyHashMap<JobId, BusyWaiter>,
idle: Stack<Waiter>,
}
struct BusyWaiter {
waiter: Waiter,
job: Job,
sow: Rc<dyn SyncObjWaiter>,
}
struct Waiter {
_task: SpawnedFuture<()>,
inner: Rc<WaiterInner>,
}
#[derive(Clone)]
struct Job {
id: JobId,
sync_obj: Rc<SyncObj>,
point: SyncObjPoint,
signaled: bool,
}
struct WaiterInner {
inner: Rc<Inner>,
eventfd: Rc<OwnedFd>,
next: Cell<Option<Job>>,
trigger: AsyncEvent,
}
impl Drop for WaitForSyncObjHandle {
fn drop(&mut self) {
let _ = self.inner.busy.remove(&self.id);
}
}
impl WaitForSyncObj {
pub fn new(ring: &Rc<IoUring>, eng: &Rc<AsyncEngine>) -> Self {
Self {
inner: Rc::new(Inner {
ctx: Default::default(),
next_id: Default::default(),
ring: ring.clone(),
busy: Default::default(),
idle: Default::default(),
}),
eng: eng.clone(),
}
}
pub fn set_ctx(&self, ctx: Option<Rc<SyncObjCtx>>) {
self.inner.ctx.set(ctx);
let busy_waiters: Vec<_> = self.inner.busy.lock().drain().map(|(_, w)| w).collect();
for waiter in busy_waiters {
let res = self.submit_job(
waiter.job.id,
&waiter.job.sync_obj,
waiter.job.point,
waiter.job.signaled,
waiter.sow.clone(),
);
if res.is_err() {
waiter.sow.done(res);
}
}
}
pub fn wait(
&self,
sync_obj: &Rc<SyncObj>,
point: SyncObjPoint,
signaled: bool,
sow: Rc<dyn SyncObjWaiter>,
) -> Result<WaitForSyncObjHandle, DrmError> {
let job_id = JobId(self.inner.next_id.fetch_add(1));
self.submit_job(job_id, sync_obj, point, signaled, sow)?;
Ok(WaitForSyncObjHandle {
inner: self.inner.clone(),
id: job_id,
})
}
fn submit_job(
&self,
job_id: JobId,
sync_obj: &Rc<SyncObj>,
point: SyncObjPoint,
signaled: bool,
sow: Rc<dyn SyncObjWaiter>,
) -> Result<(), DrmError> {
let waiter = match self.inner.idle.pop() {
Some(w) => w,
None => {
let eventfd = uapi::eventfd(0, c::EFD_CLOEXEC)
.map_err(OsError::from)
.map_err(DrmError::EventFd)?;
let waiter = Rc::new(WaiterInner {
inner: self.inner.clone(),
eventfd: Rc::new(eventfd),
next: Cell::new(None),
trigger: Default::default(),
});
Waiter {
_task: self.eng.spawn(waiter.clone().run()),
inner: waiter,
}
}
};
let job = Job {
id: job_id,
sync_obj: sync_obj.clone(),
point,
signaled,
};
let waiter = BusyWaiter {
waiter,
job: job.clone(),
sow: sow.clone(),
};
waiter.waiter.inner.next.set(Some(job));
waiter.waiter.inner.trigger.trigger();
self.inner.busy.set(job_id, waiter);
Ok(())
}
}
impl Drop for WaitForSyncObj {
fn drop(&mut self) {
self.inner.busy.clear();
self.inner.idle.take();
}
}
impl WaiterInner {
async fn run(self: Rc<Self>) {
let mut buf = Buf::new(8);
loop {
self.trigger.triggered().await;
let job = self.next.take().unwrap();
let res = self.wait(&mut buf, &job).await;
if let Some(waiter) = self.inner.busy.remove(&job.id) {
waiter.sow.done(res);
self.inner.idle.push(waiter.waiter);
}
}
}
async fn wait(&self, buf: &mut Buf, job: &Job) -> Result<(), DrmError> {
let ctx = match self.inner.ctx.get() {
None => return Err(DrmError::NoSyncObjContextAvailable),
Some(c) => c,
};
ctx.wait_for_point(&self.eventfd, &job.sync_obj, job.point, job.signaled)?;
self.inner
.ring
.read(&self.eventfd, buf.clone())
.await
.map(drop)
.map_err(DrmError::ReadEventFd)
}
}