diff --git a/src/clientmem.rs b/src/clientmem.rs index 69f9c530..c01b26f2 100644 --- a/src/clientmem.rs +++ b/src/clientmem.rs @@ -54,8 +54,9 @@ impl ClientMem { read_only: bool, client: Option<&Client>, cpu: Option<&Rc>, + is_udmabuf: bool, ) -> Result { - Self::new2(fd, len, read_only, client, cpu, c::MAP_SHARED) + Self::new2(fd, len, read_only, client, cpu, c::MAP_SHARED, is_udmabuf) } pub fn new_private( @@ -65,7 +66,7 @@ impl ClientMem { client: Option<&Client>, cpu: Option<&Rc>, ) -> Result { - Self::new2(fd, len, read_only, client, cpu, c::MAP_PRIVATE) + Self::new2(fd, len, read_only, client, cpu, c::MAP_PRIVATE, false) } fn new2( @@ -75,10 +76,12 @@ impl ClientMem { client: Option<&Client>, cpu: Option<&Rc>, flags: c::c_int, + is_udmabuf: bool, ) -> Result { - let mut sigbus_impossible = false; + let mut sigbus_impossible = is_udmabuf; let mut real_size = None; - if let Ok(seals) = uapi::fcntl_get_seals(fd.raw()) + if !sigbus_impossible + && let Ok(seals) = uapi::fcntl_get_seals(fd.raw()) && seals & c::F_SEAL_SHRINK != 0 && let Ok(stat) = uapi::fstat(fd.raw()) { diff --git a/src/ifs/wl_buffer.rs b/src/ifs/wl_buffer.rs index 89cb5c4f..cbb0a2cc 100644 --- a/src/ifs/wl_buffer.rs +++ b/src/ifs/wl_buffer.rs @@ -108,6 +108,7 @@ impl WlBuffer { stride: i32, format: &'static Format, mem: &Rc, + udmabuf: Option<(&Rc, usize)>, ) -> Result { let bytes = stride as u64 * height as u64; let required = bytes + offset as u64; @@ -119,6 +120,26 @@ impl WlBuffer { if (stride as u64) < min_row_size { return Err(WlBufferError::StrideTooSmall); } + let dmabuf_buffer_params = match udmabuf { + None => DmabufBufferParams { + size: bytes as usize, + udmabuf: None, + udmabuf_offset: 0, + udmabuf_size: 0, + udmabuf_impossible: !mem.pool().is_sealed_memfd(), + host_buffer: None, + host_buffer_impossible: !mem.pool().is_sealed_memfd(), + }, + Some((udmabuf, size)) => DmabufBufferParams { + size, + udmabuf: Some(udmabuf.clone()), + udmabuf_offset: offset, + udmabuf_size: size, + udmabuf_impossible: false, + host_buffer: None, + host_buffer_impossible: false, + }, + }; Ok(Self { id, destroyed: Cell::new(false), @@ -128,15 +149,7 @@ impl WlBuffer { dmabuf: None, render_ctx_version: Cell::new(client.state.render_ctx_version.get()), storage: RefCell::new(Some(WlBufferStorage::Shm { - dmabuf_buffer_params: DmabufBufferParams { - size: bytes as usize, - udmabuf: None, - udmabuf_offset: 0, - udmabuf_size: 0, - udmabuf_impossible: !mem.pool().is_sealed_memfd(), - host_buffer: None, - host_buffer_impossible: !mem.pool().is_sealed_memfd(), - }, + dmabuf_buffer_params, mem, stride, })), @@ -191,9 +204,9 @@ impl WlBuffer { }; let had_texture = match s { WlBufferStorage::Shm { - mem, dmabuf_buffer_params: DmabufBufferParams { + udmabuf_impossible, host_buffer, host_buffer_impossible, .. @@ -201,7 +214,7 @@ impl WlBuffer { .. } => { host_buffer.take(); - *host_buffer_impossible = !mem.pool().is_sealed_memfd(); + *host_buffer_impossible = *udmabuf_impossible; return match surface { Some(s) => { s.shm_staging.take(); diff --git a/src/ifs/wl_shm_pool.rs b/src/ifs/wl_shm_pool.rs index f6d12b34..8b343f3c 100644 --- a/src/ifs/wl_shm_pool.rs +++ b/src/ifs/wl_shm_pool.rs @@ -40,6 +40,7 @@ impl WlShmPool { false, Some(client), Some(&client.state.cpu_worker), + false, )?)), fd, tracker: Default::default(), @@ -68,6 +69,7 @@ impl WlShmPoolRequestHandler for WlShmPool { req.stride, format, &self.mem.get(), + None, )?); track!(self.client, buffer); self.client.add_client_obj(&buffer)?; @@ -92,6 +94,7 @@ impl WlShmPoolRequestHandler for WlShmPool { false, Some(&self.client), Some(&self.client.state.cpu_worker), + false, )?)); Ok(()) } diff --git a/src/ifs/zwp_linux_buffer_params_v1.rs b/src/ifs/zwp_linux_buffer_params_v1.rs index e91794d3..801d258e 100644 --- a/src/ifs/zwp_linux_buffer_params_v1.rs +++ b/src/ifs/zwp_linux_buffer_params_v1.rs @@ -1,8 +1,12 @@ use { crate::{ client::ClientError, + clientmem::{ClientMem, ClientMemError}, gfx_api::GfxError, - ifs::{wl_buffer::WlBuffer, zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1}, + ifs::{ + wl_buffer::{WlBuffer, WlBufferError}, + zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1, + }, leaks::Tracker, object::Object, utils::{errorfmt::ErrorFmt, hash_map_ext::HashMapExt}, @@ -102,25 +106,52 @@ impl ZwpLinuxBufferParamsV1 { fd: p.fd, }); } - let img = ctx.dmabuf_img(&dmabuf)?; - let (is_client_id, buffer_id) = match buffer_id { - Some(i) => (true, i), - None => (false, self.parent.client.new_id()?), + let get_id = || match buffer_id { + None => self.parent.client.new_id(), + Some(i) => Ok(i), + }; + let buffer = if format.supports_shm + && let Some(size) = dmabuf.udmabuf_size() + { + let p = &dmabuf.planes[0]; + let client_mem = ClientMem::new( + &p.fd, + size, + true, + Some(&self.parent.client), + Some(&self.parent.client.state.cpu_worker), + true, + ) + .map(Rc::new) + .map_err(ZwpLinuxBufferParamsV1Error::CreateClientMem)?; + Rc::new(WlBuffer::new_shm( + get_id()?, + &self.parent.client, + p.offset as usize, + dmabuf.width, + dmabuf.height, + p.stride as _, + format.format, + &client_mem, + Some((&p.fd, size)), + )?) + } else { + let img = ctx.dmabuf_img(&dmabuf)?; + Rc::new(WlBuffer::new_dmabuf( + get_id()?, + &self.parent.client, + format.format, + dmabuf, + &img, + )) }; - let buffer = Rc::new(WlBuffer::new_dmabuf( - buffer_id, - &self.parent.client, - format.format, - dmabuf, - &img, - )); track!(self.parent.client, buffer); - if is_client_id { + if buffer_id.is_some() { self.parent.client.add_client_obj(&buffer)?; } else { self.parent.client.add_server_obj(&buffer); } - Ok(buffer_id) + Ok(buffer.id) } } @@ -218,5 +249,10 @@ pub enum ZwpLinuxBufferParamsV1Error { MissingPlane(usize), #[error("Could not import the buffer")] ImportError(#[from] GfxError), + #[error("Could not create ClientMem")] + CreateClientMem(#[source] ClientMemError), + #[error(transparent)] + WlBufferError(Box), } efrom!(ZwpLinuxBufferParamsV1Error, ClientError); +efrom!(ZwpLinuxBufferParamsV1Error, WlBufferError); diff --git a/src/video/dmabuf.rs b/src/video/dmabuf.rs index f16bba9d..2991d5d8 100644 --- a/src/video/dmabuf.rs +++ b/src/video/dmabuf.rs @@ -2,11 +2,15 @@ use { crate::{ format::Format, utils::{compat::IoctlNumber, oserror::OsError}, - video::Modifier, + video::{LINEAR_MODIFIER, Modifier}, }, arrayvec::ArrayVec, - std::rc::Rc, - uapi::{_IOW, _IOWR, OwnedFd, c::ioctl}, + std::{rc::Rc, sync::OnceLock}, + uapi::{ + _IOW, _IOWR, OwnedFd, + c::{self, dev_t, ioctl}, + format_ustr, + }, }; #[derive(Clone, Debug)] @@ -53,6 +57,46 @@ impl DmaBuf { false } + pub fn udmabuf_size(&self) -> Option { + if self.planes.len() != 1 { + return None; + } + if self.modifier != LINEAR_MODIFIER { + return None; + } + let stat = match uapi::fstat(self.planes[0].fd.raw()) { + Ok(s) => s, + _ => return None, + }; + static DMABUF_DEV: OnceLock = OnceLock::new(); + match DMABUF_DEV.get() { + Some(d) => { + if stat.st_dev != *d { + return None; + } + } + None => { + if dma_buf_export_sync_file(&self.planes[0].fd, DMA_BUF_SYNC_READ).is_err() { + return None; + } + let _ = DMABUF_DEV.set(stat.st_dev); + } + } + let path = format_ustr!("/sys/kernel/dmabuf/buffers/{}/exporter_name", stat.st_ino); + let Ok(file) = uapi::open(path, c::O_RDONLY, 0) else { + return None; + }; + const MARKER: &[u8] = b"udmabuf\n"; + let mut buf = [0u8; MARKER.len()]; + if uapi::read(file.raw(), &mut buf).is_err() { + return None; + } + if buf != MARKER { + return None; + } + Some(stat.st_size as usize) + } + pub fn import_sync_file(&self, flags: u32, sync_file: &OwnedFd) -> Result<(), OsError> { for plane in &self.planes { dma_buf_import_sync_file(&plane.fd, flags, sync_file)?;