diff --git a/src/backend/allocator/dmabuf.rs b/src/backend/allocator/dmabuf.rs index d20f6f5..49b452b 100644 --- a/src/backend/allocator/dmabuf.rs +++ b/src/backend/allocator/dmabuf.rs @@ -10,21 +10,67 @@ //! This can be especially useful in resources where other parts of the stack should decide upon //! the lifetime of the buffer. E.g. when you are only caching associated resources for a dmabuf. -use super::{Buffer, Format, Modifier}; -use std::os::unix::io::RawFd; +use super::{Buffer, Format, Fourcc, Modifier}; +use std::os::unix::io::{IntoRawFd, RawFd}; use std::sync::{Arc, Weak}; +use std::hash::{Hash, Hasher}; -const MAX_PLANES: usize = 4; +/// Maximum amount of planes this implementation supports +pub const MAX_PLANES: usize = 4; #[derive(Debug)] pub(crate) struct DmabufInternal { - pub num_planes: usize, - pub offsets: [u32; MAX_PLANES], - pub strides: [u32; MAX_PLANES], - pub fds: [RawFd; MAX_PLANES], - pub width: u32, - pub height: u32, - pub format: Format, + /// The submitted planes + pub planes: Vec, + /// The width of this buffer + pub width: i32, + /// The height of this buffer + pub height: i32, + /// The format in use + pub format: Fourcc, + /// The flags applied to it + /// + /// This is a bitflag, to be compared with the `Flags` enum re-exported by this module. + pub flags: DmabufFlags, +} + +#[derive(Debug)] +pub(crate) struct Plane { + pub fd: Option, + /// The plane index + pub plane_idx: u32, + /// Offset from the start of the Fd + pub offset: u32, + /// Stride for this plane + pub stride: u32, + /// Modifier for this plane + pub modifier: Modifier, +} + +impl IntoRawFd for Plane { + fn into_raw_fd(mut self) -> RawFd { + self.fd.take().unwrap() + } +} + +impl Drop for Plane { + fn drop(&mut self) { + if let Some(fd) = self.fd.take() { + let _ = nix::unistd::close(fd); + } + } +} + +bitflags! { + /// Possible flags for a DMA buffer + pub struct DmabufFlags: u32 { + /// The buffer content is Y-inverted + const Y_INVERT = 1; + /// The buffer content is interlaced + const INTERLACED = 2; + /// The buffer content if interlaced is bottom-field first + const BOTTOM_FIRST = 4; + } } #[derive(Debug, Clone)] @@ -42,109 +88,138 @@ impl PartialEq for Dmabuf { } impl Eq for Dmabuf {} -impl PartialEq for Dmabuf { - fn eq(&self, other: &WeakDmabuf) -> bool { - if let Some(dmabuf) = other.upgrade() { - return Arc::ptr_eq(&self.0, &dmabuf.0); - } - false - } -} - impl PartialEq for WeakDmabuf { fn eq(&self, other: &Self) -> bool { - if let Some(dmabuf) = self.upgrade() { - return &dmabuf == other; - } - false + Weak::ptr_eq(&self.0, &other.0) + } +} +impl Eq for WeakDmabuf {} + +impl Hash for Dmabuf { + fn hash(&self, state: &mut H) { + Arc::as_ptr(&self.0).hash(state) + } +} +impl Hash for WeakDmabuf { + fn hash(&self, state: &mut H) { + self.0.as_ptr().hash(state) } } impl Buffer for Dmabuf { fn width(&self) -> u32 { - self.0.width + self.0.width as u32 } fn height(&self) -> u32 { - self.0.height + self.0.height as u32 } fn format(&self) -> Format { - self.0.format + Format { + code: self.0.format, + modifier: self.0.planes[0].modifier, + } + } +} + +/// Builder for Dmabufs +pub struct DmabufBuilder { + internal: DmabufInternal, +} + +impl DmabufBuilder { + /// Add a plane to the construted Dmabuf + /// + /// *Note*: Each Dmabuf needs atleast one plane. + /// MAX_PLANES notes the maximum amount of planes any format may use with this implementation. + pub fn add_plane(&mut self, fd: RawFd, idx: u32, offset: u32, stride: u32, modifier: Modifier) -> bool { + if self.internal.planes.len() == MAX_PLANES { + return false; + } + self.internal.planes.push(Plane { + fd: Some(fd), + plane_idx: idx, + offset, + stride, + modifier, + }); + + true + } + + /// Build a `Dmabuf` out of the provided parameters and planes + /// + /// Returns `None` if the builder has no planes attached. + pub fn build(mut self) -> Option { + if self.internal.planes.len() == 0 { + return None; + } + + self.internal.planes.sort_by_key(|plane| plane.plane_idx); + Some(Dmabuf(Arc::new(self.internal))) } } impl Dmabuf { + /// Create a new Dmabuf by intializing with values from an existing buffer + /// // Note: the `src` Buffer is only used a reference for size and format. // The contents are determined by the provided file descriptors, which // do not need to refer to the same buffer `src` does. - pub(crate) fn new( - src: &impl Buffer, - planes: usize, - offsets: &[u32], - strides: &[u32], - fds: &[RawFd], - ) -> Option { - if offsets.len() < planes - || strides.len() < planes - || fds.len() < planes - || planes == 0 - || planes > MAX_PLANES - { - return None; + pub fn new_from_buffer(src: &impl Buffer, flags: DmabufFlags) -> DmabufBuilder { + DmabufBuilder { + internal: DmabufInternal { + planes: Vec::with_capacity(MAX_PLANES), + width: src.width() as i32, + height: src.height() as i32, + format: src.format().code, + flags, + }, } - - let end = [0u32, 0, 0]; - let end_fds = [0i32, 0, 0]; - let mut offsets = offsets.iter().take(planes).chain(end.iter()); - let mut strides = strides.iter().take(planes).chain(end.iter()); - let mut fds = fds.iter().take(planes).chain(end_fds.iter()); - - Some(Dmabuf(Arc::new(DmabufInternal { - num_planes: planes, - offsets: [ - *offsets.next().unwrap(), - *offsets.next().unwrap(), - *offsets.next().unwrap(), - *offsets.next().unwrap(), - ], - strides: [ - *strides.next().unwrap(), - *strides.next().unwrap(), - *strides.next().unwrap(), - *strides.next().unwrap(), - ], - fds: [ - *fds.next().unwrap(), - *fds.next().unwrap(), - *fds.next().unwrap(), - *fds.next().unwrap(), - ], - - width: src.width(), - height: src.height(), - format: src.format(), - }))) } - /// Return raw handles of the planes of this buffer - pub fn handles(&self) -> &[RawFd] { - self.0.fds.split_at(self.0.num_planes).0 + /// Create a new Dmabuf + pub fn new(width: u32, height: u32, format: Fourcc, flags: DmabufFlags) -> DmabufBuilder { + DmabufBuilder { + internal: DmabufInternal { + planes: Vec::with_capacity(MAX_PLANES), + width: width as i32, + height: height as i32, + format, + flags, + }, + } } - /// Return offsets for the planes of this buffer - pub fn offsets(&self) -> &[u32] { - self.0.offsets.split_at(self.0.num_planes).0 + /// The amount of planes this Dmabuf has + pub fn num_planes(&self) -> usize { + self.0.planes.len() } - /// Return strides for the planes of this buffer - pub fn strides(&self) -> &[u32] { - self.0.strides.split_at(self.0.num_planes).0 + /// Returns raw handles of the planes of this buffer + pub fn handles<'a>(&'a self) -> impl Iterator + 'a { + self.0.planes.iter().map(|p| *p.fd.as_ref().unwrap()) } - /// Check if this buffer format has any vendor-specific modifiers set or is implicit/linear + /// Returns offsets for the planes of this buffer + pub fn offsets<'a>(&'a self) -> impl Iterator + 'a { + self.0.planes.iter().map(|p| p.offset) + } + + /// Returns strides for the planes of this buffer + pub fn strides<'a>(&'a self) -> impl Iterator + 'a { + self.0.planes.iter().map(|p| p.stride) + } + + /// Returns if this buffer format has any vendor-specific modifiers set or is implicit/linear pub fn has_modifier(&self) -> bool { - self.0.format.modifier != Modifier::Invalid && self.0.format.modifier != Modifier::Linear + self.0.planes[0].modifier != Modifier::Invalid && self.0.planes[0].modifier != Modifier::Linear + } + + /// Returns if the buffer is stored inverted on the y-axis + pub fn y_inverted(&self) -> bool { + self.0.flags.contains(DmabufFlags::Y_INVERT) } /// Create a weak reference to this dmabuf @@ -162,16 +237,6 @@ impl WeakDmabuf { } } -impl Drop for DmabufInternal { - fn drop(&mut self) { - for fd in self.fds.iter() { - if *fd != 0 { - let _ = nix::unistd::close(*fd); - } - } - } -} - /// Buffer that can be exported as Dmabufs pub trait AsDmabuf { /// Error type returned, if exporting fails diff --git a/src/backend/allocator/gbm.rs b/src/backend/allocator/gbm.rs index bc8dcbc..28d307d 100644 --- a/src/backend/allocator/gbm.rs +++ b/src/backend/allocator/gbm.rs @@ -5,7 +5,7 @@ //! conversions to and from [dmabufs](super::dmabuf). use super::{ - dmabuf::{AsDmabuf, Dmabuf}, + dmabuf::{AsDmabuf, Dmabuf, DmabufFlags, MAX_PLANES}, Allocator, Buffer, Format, Fourcc, Modifier, }; pub use gbm::{BufferObject as GbmBuffer, BufferObjectFlags as GbmBufferFlags, Device as GbmDevice}; @@ -95,20 +95,21 @@ impl AsDmabuf for GbmBuffer { return Err(GbmConvertError::UnsupportedBuffer); //TODO } - let fds = [self.fd()?, 0, 0, 0]; - //if fds.iter().any(|fd| fd == 0) { - if fds[0] < 0 { + if self.fd()? == 0 { return Err(GbmConvertError::InvalidFD); } - let offsets = (0i32..planes) - .map(|i| self.offset(i)) - .collect::, gbm::DeviceDestroyedError>>()?; - let strides = (0i32..planes) - .map(|i| self.stride_for_plane(i)) - .collect::, gbm::DeviceDestroyedError>>()?; - - Ok(Dmabuf::new(self, planes as usize, &offsets, &strides, &fds).unwrap()) + let mut builder = Dmabuf::new_from_buffer(self, DmabufFlags::empty()); + for idx in 0..planes { + builder.add_plane( + self.fd()?, + idx as u32, + self.offset(idx)?, + self.stride_for_plane(idx)?, + self.modifier()?, + ); + } + Ok(builder.build().unwrap()) } } @@ -119,27 +120,39 @@ impl Dmabuf { gbm: &GbmDevice, usage: GbmBufferFlags, ) -> std::io::Result> { - let buf = &*self.0; - if self.has_modifier() || buf.num_planes > 1 || buf.offsets[0] != 0 { + let mut handles = [0; MAX_PLANES]; + for (i, handle) in self.handles().take(MAX_PLANES).enumerate() { + handles[i] = handle; + } + let mut strides = [0i32; MAX_PLANES]; + for (i, stride) in self.strides().take(MAX_PLANES).enumerate() { + strides[i] = stride as i32; + } + let mut offsets = [0i32; MAX_PLANES]; + for (i, offset) in self.offsets().take(MAX_PLANES).enumerate() { + offsets[i] = offset as i32; + } + + if self.has_modifier() || self.num_planes() > 1 || self.offsets().next().unwrap() != 0 { gbm.import_buffer_object_from_dma_buf_with_modifiers( - buf.num_planes as u32, - buf.fds, - buf.width, - buf.height, - buf.format.code, + self.num_planes() as u32, + handles, + self.width(), + self.height(), + self.format().code, usage, - unsafe { std::mem::transmute::<[u32; 4], [i32; 4]>(buf.strides) }, - unsafe { std::mem::transmute::<[u32; 4], [i32; 4]>(buf.offsets) }, - buf.format.modifier, + strides, + offsets, + self.format().modifier, ) } else { gbm.import_buffer_object_from_dma_buf( - buf.fds[0], - buf.width, - buf.height, - buf.strides[0], - buf.format.code, - if buf.format.modifier == Modifier::Linear { + handles[0], + self.width(), + self.height(), + strides[0] as u32, + self.format().code, + if self.format().modifier == Modifier::Linear { usage | GbmBufferFlags::LINEAR } else { usage diff --git a/src/backend/egl/display.rs b/src/backend/egl/display.rs index 4821f5f..d2cde13 100644 --- a/src/backend/egl/display.rs +++ b/src/backend/egl/display.rs @@ -19,10 +19,10 @@ use crate::backend::egl::{ ffi, ffi::egl::types::EGLImage, native::EGLNativeDisplay, - wrap_egl_call, EGLError, Error, Format, + wrap_egl_call, EGLError, Error, }; -#[cfg(feature = "wayland_frontend")] -use crate::backend::egl::{BufferAccessError, EGLBuffer}; +#[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))] +use crate::backend::egl::{BufferAccessError, EGLBuffer, Format}; /// Wrapper around [`ffi::EGLDisplay`](ffi::egl::types::EGLDisplay) to ensure display is only destroyed /// once all resources bound to it have been dropped. @@ -473,18 +473,17 @@ impl EGLDisplay { for (i, ((fd, offset), stride)) in dmabuf .handles() - .iter() .zip(dmabuf.offsets()) .zip(dmabuf.strides()) .enumerate() { out.extend(&[ names[i][0] as i32, - *fd, + fd, names[i][1] as i32, - *offset as i32, + offset as i32, names[i][2] as i32, - *stride as i32, + stride as i32, ]); if dmabuf.has_modifier() { out.extend(&[ @@ -510,7 +509,6 @@ impl EGLDisplay { if image == ffi::egl::NO_IMAGE_KHR { Err(Error::EGLImageCreationFailed) } else { - // TODO check for external Ok(image) } } diff --git a/src/backend/renderer/gles2/mod.rs b/src/backend/renderer/gles2/mod.rs index 183915e..2ace12f 100644 --- a/src/backend/renderer/gles2/mod.rs +++ b/src/backend/renderer/gles2/mod.rs @@ -20,11 +20,11 @@ mod version; use super::{Bind, Frame, Renderer, Texture, Transform, Unbind}; use crate::backend::allocator::{ dmabuf::{Dmabuf, WeakDmabuf}, - Format, + Buffer, Format, }; use crate::backend::egl::{ ffi::egl::{self as ffi_egl, types::EGLImage}, - EGLContext, EGLSurface, Format as EGLFormat, MakeCurrentError, + EGLContext, EGLSurface, MakeCurrentError, }; use crate::backend::SwapBuffersError; @@ -34,11 +34,11 @@ use crate::{ wayland::compositor::SurfaceAttributes, }; #[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))] -use crate::backend::egl::display::EGLBufferReader; +use crate::backend::egl::{display::EGLBufferReader, Format as EGLFormat}; #[cfg(feature = "wayland_frontend")] use wayland_server::protocol::{wl_buffer, wl_shm}; #[cfg(feature = "wayland_frontend")] -use super::ImportShm; +use super::{ImportShm, ImportDma}; #[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))] use super::ImportEgl; @@ -76,8 +76,6 @@ struct Gles2TextureInternal { y_inverted: bool, width: u32, height: u32, - #[cfg(feature = "wayland_frontend")] - buffer: Option, egl_images: Option>, destruction_callback_sender: Sender, } @@ -155,7 +153,7 @@ pub struct Gles2Renderer { extensions: Vec, programs: [Gles2Program; shaders::FRAGMENT_COUNT], #[cfg(feature = "wayland_frontend")] - dmabuf_cache: HashMap, + dmabuf_cache: HashMap, egl: EGLContext, gl: ffi::Gles2, destruction_callback: Receiver, @@ -483,7 +481,7 @@ impl Gles2Renderer { self.make_current()?; #[cfg(feature = "wayland_frontend")] self.dmabuf_cache - .retain(|entry, _tex| entry.buffer.as_ref().is_alive()); + .retain(|entry, _tex| entry.upgrade().is_some()); for resource in self.destruction_callback.try_iter() { match resource { CleanupResource::Texture(texture) => unsafe { @@ -550,7 +548,6 @@ impl ImportShm for Gles2Renderer { y_inverted: false, width: width as u32, height: height as u32, - buffer: Some(buffer.clone()), egl_images: None, destruction_callback_sender: self.destruction_callback_sender.clone(), }); @@ -657,7 +654,6 @@ impl ImportEgl for Gles2Renderer { y_inverted: egl.y_inverted, width: egl.width, height: egl.height, - buffer: Some(buffer.clone()), egl_images: Some(egl.into_images()), destruction_callback_sender: self.destruction_callback_sender.clone(), })); @@ -666,16 +662,56 @@ impl ImportEgl for Gles2Renderer { } } +#[cfg(feature = "wayland_frontend")] +impl ImportDma for Gles2Renderer { + fn import_dmabuf( + &mut self, + buffer: &Dmabuf, + ) -> Result { + if !self.extensions.iter().any(|ext| ext == "GL_OES_EGL_image") { + return Err(Gles2Error::GLExtensionNotSupported(&["GL_OES_EGL_image"])); + } + + self.existing_dmabuf_texture(&buffer)?.map(Ok).unwrap_or_else(|| { + let is_external = !self.egl.dmabuf_render_formats().contains(&buffer.format()); + + self.make_current()?; + let image = self.egl.display.create_image_from_dmabuf(&buffer) + .map_err(Gles2Error::BindBufferEGLError)?; + + let tex = self.import_egl_image(image, is_external, None)?; + let texture = Gles2Texture(Rc::new(Gles2TextureInternal { + texture: tex, + texture_kind: if is_external { 2 } else { 0 }, + is_external, + y_inverted: buffer.y_inverted(), + width: buffer.width(), + height: buffer.height(), + egl_images: Some(vec![image]), + destruction_callback_sender: self.destruction_callback_sender.clone(), + })); + self.egl.unbind()?; + self.dmabuf_cache.insert(buffer.weak(), texture.clone()); + Ok(texture) + }) + } + + #[cfg(feature = "wayland_frontend")] + fn dmabuf_formats<'a>(&'a self) -> Box + 'a> { + Box::new(self.egl.dmabuf_texture_formats().iter()) + } +} + #[cfg(feature = "wayland_frontend")] impl Gles2Renderer { fn existing_dmabuf_texture( &self, - buffer: &wl_buffer::WlBuffer, + buffer: &Dmabuf, ) -> Result, Gles2Error> { let existing_texture = self .dmabuf_cache .iter() - .find(|(old_buffer, _)| &old_buffer.buffer == buffer) + .find(|(weak, _)| weak.upgrade().map(|entry| &entry == buffer).unwrap_or(false)) .map(|(_, tex)| tex.clone()); if let Some(texture) = existing_texture { @@ -928,7 +964,6 @@ impl Renderer for Gles2Renderer { y_inverted: false, width: image.width(), height: image.height(), - buffer: None, egl_images: None, destruction_callback_sender: self.destruction_callback_sender.clone(), })); diff --git a/src/backend/renderer/mod.rs b/src/backend/renderer/mod.rs index 25f467e..0249968 100644 --- a/src/backend/renderer/mod.rs +++ b/src/backend/renderer/mod.rs @@ -18,6 +18,8 @@ use wayland_server::protocol::{wl_buffer, wl_shm}; #[cfg(feature = "renderer_gl")] pub mod gles2; +#[cfg(feature = "wayland_frontend")] +use crate::backend::allocator::{dmabuf::Dmabuf, Format}; #[cfg(all(feature = "wayland_frontend", feature = "backend_egl", feature = "use_system_lib"))] use crate::backend::egl::display::EGLBufferReader; @@ -289,6 +291,54 @@ pub trait ImportEgl: Renderer { ) -> Result<::TextureId, ::Error>; } +#[cfg(feature = "wayland_frontend")] +/// Trait for Renderers supporting importing dmabuf-based buffers. +pub trait ImportDma: Renderer { + /// Returns supported formats for dmabufs. + fn dmabuf_formats<'a>(&'a self) -> Box + 'a> { + Box::new([].iter()) + } + + /// Import a given dmabuf-based buffer into the renderer (see [`buffer_type`]). + /// + /// Returns a texture_id, which can be used with [`Frame::render_texture`] (or [`Frame::render_texture_at`]) + /// or implementation-specific functions. + /// + /// If not otherwise defined by the implementation, this texture id is only valid for the renderer, that created it. + /// + /// This operation needs no bound or default rendering target. + /// + /// The implementation defines, if the id keeps being valid, if the buffer is released, + /// to avoid relying on implementation details, keep the buffer alive, until you destroyed this texture again. + fn import_dma_buffer( + &mut self, + buffer: &wl_buffer::WlBuffer, + ) -> Result<::TextureId, ::Error> { + let dmabuf = buffer + .as_ref() + .user_data() + .get::() + .expect("import_dma_buffer without checking buffer type?"); + self.import_dmabuf(dmabuf) + } + + /// Import a given raw dmabuf into the renderer. + /// + /// Returns a texture_id, which can be used with [`Frame::render_texture`] (or [`Frame::render_texture_at`]) + /// or implementation-specific functions. + /// + /// If not otherwise defined by the implementation, this texture id is only valid for the renderer, that created it. + /// + /// This operation needs no bound or default rendering target. + /// + /// The implementation defines, if the id keeps being valid, if the buffer is released, + /// to avoid relying on implementation details, keep the buffer alive, until you destroyed this texture again. + fn import_dmabuf( + &mut self, + dmabuf: &Dmabuf, + ) -> Result<::TextureId, ::Error>; +} + // TODO: Replace this with a trait_alias, once that is stabilized. // pub type ImportAll = Renderer + ImportShm + ImportEgl; @@ -352,7 +402,14 @@ pub fn buffer_type( buffer: &wl_buffer::WlBuffer, egl_buffer_reader: Option<&EGLBufferReader>, ) -> Option { - if egl_buffer_reader + if buffer + .as_ref() + .user_data() + .get::() + .is_some() + { + Some(BufferType::Dma) + } else if egl_buffer_reader .as_ref() .and_then(|x| x.egl_buffer_dimensions(&buffer)) .is_some() @@ -371,9 +428,14 @@ pub fn buffer_type( /// Returns `None` if the type is not recognized by smithay or otherwise not supported. #[cfg(all(feature = "wayland_frontend", not(all(feature = "backend_egl", feature = "use_system_lib"))))] pub fn buffer_type(buffer: &wl_buffer::WlBuffer) -> Option { - use crate::backend::allocator::Buffer; - - if crate::wayland::shm::with_buffer_contents(&buffer, |_, _| ()).is_ok() + if buffer + .as_ref() + .user_data() + .get::() + .is_some() + { + Some(BufferType::Dma) + } else if crate::wayland::shm::with_buffer_contents(&buffer, |_, _| ()).is_ok() { Some(BufferType::Shm) } else { @@ -389,7 +451,11 @@ pub fn buffer_dimensions( buffer: &wl_buffer::WlBuffer, egl_buffer_reader: Option<&EGLBufferReader>, ) -> Option<(i32, i32)> { - if let Some((w, h)) = egl_buffer_reader + use crate::backend::allocator::Buffer; + + if let Some(buf) = buffer.as_ref().user_data().get::() { + Some((buf.width() as i32, buf.height() as i32)) + } else if let Some((w, h)) = egl_buffer_reader .as_ref() .and_then(|x| x.egl_buffer_dimensions(&buffer)) { @@ -410,7 +476,9 @@ pub fn buffer_dimensions( pub fn buffer_dimensions(buffer: &wl_buffer::WlBuffer) -> Option<(i32, i32)> { use crate::backend::allocator::Buffer; - if let Ok((w, h)) = + if let Some(buf) = buffer.as_ref().user_data().get::() { + Some((buf.width() as i32, buf.height() as i32)) + } else if let Ok((w, h)) = crate::wayland::shm::with_buffer_contents(&buffer, |_, data| (data.width, data.height)) { Some((w, h)) diff --git a/src/backend/winit.rs b/src/backend/winit.rs index dc957bd..36fe6e8 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -16,7 +16,6 @@ use crate::backend::{ }; use std::{cell::RefCell, rc::Rc, time::Instant}; use wayland_egl as wegl; -use wayland_server::Display; use winit::{ dpi::{LogicalPosition, LogicalSize, PhysicalSize}, event::{ @@ -29,6 +28,8 @@ use winit::{ window::{Window as WinitWindow, WindowBuilder}, }; +#[cfg(feature = "use_system_lib")] +use wayland_server::Display; #[cfg(feature = "use_system_lib")] use crate::backend::egl::display::EGLBufferReader; diff --git a/src/wayland/dmabuf/mod.rs b/src/wayland/dmabuf/mod.rs index f734ed6..eb6bfa0 100644 --- a/src/wayland/dmabuf/mod.rs +++ b/src/wayland/dmabuf/mod.rs @@ -4,52 +4,25 @@ //! contents as dmabuf file descriptors. These handlers automate the aggregation of the metadata associated //! with a dma buffer, and do some basic checking of the sanity of what the client sends. //! -//! This module is only available if the `backend_drm` cargo feature is enabled. -//! //! ## How to use //! //! To setup the dmabuf global, you will need to provide 2 things: //! //! - a list of the dmabuf formats you wish to support -//! - an implementation of the `DmabufHandler` trait +//! - a closure to test if a dmabuf buffer can be imported by your renderer //! -//! The list of supported format is just a `Vec`, where you will enter all the (format, modifier) -//! couples you support. -//! -//! The implementation of the `DmabufHandler` trait will be called whenever a client has finished setting up -//! a dma buffer. You will be handled the full details of the client's submission as a `BufferInfo` struct, -//! and you need to validate it and maybe import it into your renderer. The `BufferData` associated type -//! allows you to store any metadata or handle to the resource you need into the created `wl_buffer`, -//! user data, to then retrieve it when it is attached to a surface to re-identify the dmabuf. +//! The list of supported format is just a `Vec`, where you will enter all the (code, modifier) +//! couples you support. You can typically receive a list of supported formats for one renderer by calling +//! [`crate::backend::renderer::Renderer::dmabuf_formats`]. //! //! ``` //! # extern crate wayland_server; //! # extern crate smithay; -//! use smithay::wayland::dmabuf::{DmabufHandler, BufferInfo, init_dmabuf_global}; -//! -//! struct MyDmabufHandler; -//! -//! struct MyBufferData { -//! /* ... */ -//! } -//! -//! impl Drop for MyBufferData { -//! fn drop(&mut self) { -//! // This is called when all handles to this buffer have been dropped, -//! // both client-side and server side. -//! // You can now free the associated resources in your renderer. -//! } -//! } -//! -//! impl DmabufHandler for MyDmabufHandler { -//! type BufferData = MyBufferData; -//! fn validate_dmabuf(&mut self, info: BufferInfo) -> Result { -//! /* validate the dmabuf and import it into your renderer state */ -//! Ok(MyBufferData { /* ... */ }) -//! } -//! } -//! -//! // Once this is defined, you can in your setup initialize the dmabuf global: +//! use smithay::{ +//! backend::allocator::dmabuf::Dmabuf, +//! reexports::{wayland_server::protocol::wl_buffer::WlBuffer}, +//! wayland::dmabuf::init_dmabuf_global, +//! }; //! //! # let mut display = wayland_server::Display::new(); //! // define your supported formats @@ -59,12 +32,21 @@ //! let dmabuf_global = init_dmabuf_global( //! &mut display, //! formats, -//! MyDmabufHandler, +//! |buffer, _| { +//! /* validate the dmabuf and import it into your renderer state */ +//! let dmabuf = buffer.as_ref().user_data().get::().expect("dmabuf global sets this for us"); +//! true +//! }, //! None // we don't provide a logger in this example //! ); //! ``` -use std::{cell::RefCell, os::unix::io::RawFd, rc::Rc}; +use std::{ + cell::RefCell, + convert::TryFrom, + os::unix::io::{IntoRawFd, RawFd}, + rc::Rc, +}; pub use wayland_protocols::unstable::linux_dmabuf::v1::server::zwp_linux_buffer_params_v1::Flags; use wayland_protocols::unstable::linux_dmabuf::v1::server::{ @@ -73,124 +55,34 @@ use wayland_protocols::unstable::linux_dmabuf::v1::server::{ }, zwp_linux_dmabuf_v1, }; -use wayland_server::{protocol::wl_buffer, Display, Filter, Global, Main}; +use wayland_server::{protocol::wl_buffer, DispatchData, Display, Filter, Global, Main}; -use crate::backend::allocator::{Fourcc, Modifier}; - -/// Representation of a Dmabuf format, as advertized to the client -#[derive(Debug)] -pub struct Format { - /// The format identifier. - pub format: Fourcc, - /// The supported dmabuf layout modifier. - /// - /// This is an opaque token. Drivers use this token to express tiling, compression, etc. driver-specific - /// modifications to the base format defined by the DRM fourcc code. - pub modifier: Modifier, - /// Number of planes used by this format - pub plane_count: u32, -} - -/// A plane send by the client -#[derive(Debug)] -pub struct Plane { - /// The file descriptor - pub fd: RawFd, - /// The plane index - pub plane_idx: u32, - /// Offset from the start of the Fd - pub offset: u32, - /// Stride for this plane - pub stride: u32, - /// Modifier for this plane - pub modifier: u64, -} - -bitflags! { - /// Possible flags for a DMA buffer - pub struct BufferFlags: u32 { - /// The buffer content is Y-inverted - const Y_INVERT = 1; - /// The buffer content is interlaced - const INTERLACED = 2; - /// The buffer content if interlaced is bottom-field first - const BOTTOM_FIRST = 4; - } -} - -/// The complete information provided by the client to create a dmabuf buffer -#[derive(Debug)] -pub struct BufferInfo { - /// The submitted planes - pub planes: Vec, - /// The width of this buffer - pub width: i32, - /// The height of this buffer - pub height: i32, - /// The format in use - pub format: u32, - /// The flags applied to it - /// - /// This is a bitflag, to be compared with the `Flags` enum reexported by this module. - pub flags: BufferFlags, -} +use crate::backend::allocator::{ + dmabuf::{Dmabuf, DmabufFlags, Plane}, + Format, Fourcc, Modifier, +}; /// Handler trait for dmabuf validation /// -/// You need to provide an implementation of this trait that will validate the parameters provided by the -/// client and import it as a dmabuf. -pub trait DmabufHandler { - /// The data of a successfully imported dmabuf. - /// - /// This will be stored as the `user_data` of the `WlBuffer` associated with this dmabuf. If it has a - /// destructor, it will be run when the client has destroyed the buffer and your compositor has dropped - /// all of its `WlBuffer` handles to it. - type BufferData: 'static; - /// Validate a dmabuf - /// - /// From the information provided by the client, you need to validate and/or import the buffer. - /// - /// You can then store any information your compositor will need to handle it later, when the client has - /// submitted the buffer by returning `Ok(BufferData)` where `BufferData` is the associated type of this, - /// trait, a type of your choosing. - /// - /// If the buffer could not be imported, whatever the reason, return `Err(())`. - fn validate_dmabuf(&mut self, info: BufferInfo) -> Result; - /// Create a buffer from validated buffer data. - /// - /// This method is pre-implemented for you by storing the provided `BufferData` as the `user_data` of the - /// provided `WlBuffer`. By default it assumes that your `BufferData` is not threadsafe. - /// - /// You can override it if you need your `BufferData` to be threadsafe, or which to register a destructor - /// for the `WlBuffer` for example. - fn create_buffer( - &mut self, - data: Self::BufferData, - buffer: Main, - ) -> wl_buffer::WlBuffer { - buffer.quick_assign(|_, _, _| {}); - buffer.as_ref().user_data().set(|| data); - (*buffer).clone() - } -} +/// You need to provide an implementation of this trait + /// Initialize a dmabuf global. /// -/// You need to provide a vector of the supported formats, as well as an implementation fo the `DmabufHandler` -/// trait, which will receive the buffer creation requests from the clients. -pub fn init_dmabuf_global( +/// You need to provide a vector of the supported formats, as well as a closure, +/// that will validate the parameters provided by the client and tests the import as a dmabuf. +pub fn init_dmabuf_global( display: &mut Display, formats: Vec, - handler: H, + handler: F, logger: L, ) -> Global where L: Into>, - H: DmabufHandler + 'static, + F: for<'a> FnMut(&Dmabuf, DispatchData<'a>) -> bool + 'static, { let log = crate::slog_or_fallback(logger).new(o!("smithay_module" => "dmabuf_handler")); - let max_planes = formats.iter().map(|f| f.plane_count).max().unwrap_or(0); let formats = Rc::<[Format]>::from(formats); let handler = Rc::new(RefCell::new(handler)); @@ -211,13 +103,13 @@ where if let zwp_linux_dmabuf_v1::Request::CreateParams { params_id } = req { let mut handler = ParamsHandler { pending_planes: Vec::new(), - max_planes, + max_planes: 4, used: false, formats: dma_formats.clone(), handler: dma_handler.clone(), log: dma_log.clone(), }; - params_id.quick_assign(move |params, req, _| match req { + params_id.quick_assign(move |params, req, ddata| match req { ParamsRequest::Add { fd, plane_idx, @@ -238,14 +130,14 @@ where height, format, flags, - } => handler.create(&*params, width, height, format, flags), + } => handler.create(&*params, width, height, format, flags, ddata), ParamsRequest::CreateImmed { buffer_id, width, height, format, flags, - } => handler.create_immed(&*params, buffer_id, width, height, format, flags), + } => handler.create_immed(&*params, buffer_id, width, height, format, flags, ddata), _ => {} }); } @@ -253,10 +145,10 @@ where // send the supported formats for f in &*formats { - dmabuf.format(f.format as u32); + dmabuf.format(f.code as u32); if version >= 3 { dmabuf.modifier( - f.format as u32, + f.code as u32, (Into::::into(f.modifier) >> 32) as u32, Into::::into(f.modifier) as u32, ); @@ -267,7 +159,7 @@ where ) } -struct ParamsHandler { +struct ParamsHandler FnMut(&Dmabuf, DispatchData<'a>) -> bool + 'static> { pending_planes: Vec, max_planes: u32, used: bool, @@ -276,7 +168,10 @@ struct ParamsHandler { log: ::slog::Logger, } -impl ParamsHandler { +impl ParamsHandler +where + H: for<'a> FnMut(&Dmabuf, DispatchData<'a>) -> bool + 'static +{ fn add( &mut self, params: &BufferParams, @@ -314,15 +209,15 @@ impl ParamsHandler { } // all checks passed, store the plane self.pending_planes.push(Plane { - fd, + fd: Some(fd), plane_idx, offset, stride, - modifier, + modifier: Modifier::from(modifier), }); } - fn create(&mut self, params: &BufferParams, width: i32, height: i32, format: u32, flags: u32) { + fn create<'a>(&mut self, params: &BufferParams, width: i32, height: i32, format: u32, flags: u32, ddata: DispatchData<'a>) { // Cannot reuse a params: if self.used { params.as_ref().post_error( @@ -332,6 +227,18 @@ impl ParamsHandler { return; } self.used = true; + + let format = match Fourcc::try_from(format) { + Ok(format) => format, + Err(_) => { + params.as_ref().post_error( + ParamError::InvalidFormat as u32, + format!("Format {:x} is not supported", format), + ); + return; + } + }; + if !buffer_basic_checks( &self.formats, &self.pending_planes, @@ -343,23 +250,46 @@ impl ParamsHandler { trace!(self.log, "Killing client providing bogus dmabuf buffer params."); return; } - let info = BufferInfo { - planes: ::std::mem::replace(&mut self.pending_planes, Vec::new()), - width, - height, + + let mut buf = Dmabuf::new( + width as u32, + height as u32, format, - flags: BufferFlags::from_bits_truncate(flags), + DmabufFlags::from_bits_truncate(flags), + ); + let planes = ::std::mem::replace(&mut self.pending_planes, Vec::new()); + for (i, plane) in planes.into_iter().enumerate() { + let offset = plane.offset; + let stride = plane.stride; + let modi = plane.modifier; + buf.add_plane(plane.into_raw_fd(), i as u32, offset, stride, modi); + } + let dmabuf = match buf.build() { + Some(buf) => buf, + None => { + params.as_ref().post_error( + ParamError::Incomplete as u32, + format!("Provided buffer is incomplete, it has zero planes"), + ); + return; + } }; + let mut handler = self.handler.borrow_mut(); - if let Ok(data) = handler.validate_dmabuf(info) { + if handler(&dmabuf, ddata) { if let Some(buffer) = params .as_ref() .client() .and_then(|c| c.create_resource::(1)) { - let buffer = handler.create_buffer(data, buffer); - trace!(self.log, "Creating a new validated dma wl_buffer."); - params.created(&buffer); + buffer.as_ref().user_data().set_threadsafe(|| dmabuf); + buffer.quick_assign(|_, _, _| {}); + + trace!(self.log, "Created a new validated dma wl_buffer."); + params.created(&buffer); + } else { + trace!(self.log, "Failed to create a wl_buffer"); + params.failed(); } } else { trace!(self.log, "Refusing creation of an invalid dma wl_buffer."); @@ -367,14 +297,15 @@ impl ParamsHandler { } } - fn create_immed( + fn create_immed<'a>( &mut self, params: &BufferParams, - buffer_id: Main, + buffer: Main, width: i32, height: i32, format: u32, flags: u32, + ddata: DispatchData<'a>, ) { // Cannot reuse a params: if self.used { @@ -385,6 +316,18 @@ impl ParamsHandler { return; } self.used = true; + + let format = match Fourcc::try_from(format) { + Ok(format) => format, + Err(_) => { + params.as_ref().post_error( + ParamError::InvalidFormat as u32, + format!("Format {:x} is not supported", format), + ); + return; + } + }; + if !buffer_basic_checks( &self.formats, &self.pending_planes, @@ -396,17 +339,36 @@ impl ParamsHandler { trace!(self.log, "Killing client providing bogus dmabuf buffer params."); return; } - let info = BufferInfo { - planes: ::std::mem::replace(&mut self.pending_planes, Vec::new()), - width, - height, + + let mut buf = Dmabuf::new( + width as u32, + height as u32, format, - flags: BufferFlags::from_bits_truncate(flags), + DmabufFlags::from_bits_truncate(flags), + ); + let planes = ::std::mem::replace(&mut self.pending_planes, Vec::new()); + for (i, plane) in planes.into_iter().enumerate() { + let offset = plane.offset; + let stride = plane.stride; + let modi = plane.modifier; + buf.add_plane(plane.into_raw_fd(), i as u32, offset, stride, modi); + } + let dmabuf = match buf.build() { + Some(buf) => buf, + None => { + params.as_ref().post_error( + ParamError::Incomplete as u32, + format!("Provided buffer is incomplete, it has zero planes"), + ); + return; + } }; + let mut handler = self.handler.borrow_mut(); - if let Ok(data) = handler.validate_dmabuf(info) { - trace!(self.log, "Creating a new validated immediate dma wl_buffer."); - handler.create_buffer(data, buffer_id); + if handler(&dmabuf, ddata) { + buffer.as_ref().user_data().set_threadsafe(|| dmabuf); + buffer.quick_assign(|_, _, _| {}); + trace!(self.log, "Created a new validated dma wl_buffer."); } else { trace!( self.log, @@ -417,6 +379,8 @@ impl ParamsHandler { "create_immed resulted in an invalid buffer.".into(), ); } + + } } @@ -424,34 +388,22 @@ fn buffer_basic_checks( formats: &[Format], pending_planes: &[Plane], params: &BufferParams, - format: u32, + format: Fourcc, width: i32, height: i32, ) -> bool { // protocol_checks: // This must be a known format - let format = match formats.iter().find(|f| f.format as u32 == format) { + let _format = match formats.iter().find(|f| f.code == format) { Some(f) => f, None => { params.as_ref().post_error( ParamError::InvalidFormat as u32, - format!("Format {:x} is not supported.", format), + format!("Format {:?}/{:x} is not supported.", format, format as u32), ); return false; } }; - // The number of planes set must match what the format expects - let max_plane_set = pending_planes.iter().map(|d| d.plane_idx + 1).max().unwrap_or(0); - if max_plane_set != format.plane_count || pending_planes.len() < format.plane_count as usize { - params.as_ref().post_error( - ParamError::Incomplete as u32, - format!( - "Format {:?} requires {} planes but got {}.", - format.format, format.plane_count, max_plane_set - ), - ); - return false; - } // Width and height must be positivie if width < 1 || height < 1 { params.as_ref().post_error( @@ -477,9 +429,9 @@ fn buffer_basic_checks( } Some(e) => e, }; - if let Ok(size) = ::nix::unistd::lseek(plane.fd, 0, ::nix::unistd::Whence::SeekEnd) { + if let Ok(size) = ::nix::unistd::lseek(plane.fd.unwrap(), 0, ::nix::unistd::Whence::SeekEnd) { // reset the seek point - let _ = ::nix::unistd::lseek(plane.fd, 0, ::nix::unistd::Whence::SeekSet); + let _ = ::nix::unistd::lseek(plane.fd.unwrap(), 0, ::nix::unistd::Whence::SeekSet); if plane.offset as i64 > size { params.as_ref().post_error( ParamError::OutOfBounds as u32, diff --git a/src/wayland/mod.rs b/src/wayland/mod.rs index 876bedd..9c481ac 100644 --- a/src/wayland/mod.rs +++ b/src/wayland/mod.rs @@ -16,7 +16,6 @@ use std::sync::atomic::{AtomicUsize, Ordering}; pub mod compositor; pub mod data_device; -#[cfg(feature = "backend_drm")] pub mod dmabuf; pub mod explicit_synchronization; pub mod output;