dmabuf: Unify types of `wayland::dmabuf` and `allocator::dmabuf`

This commit is contained in:
Victor Brekenfeld 2021-06-06 15:24:48 +02:00 committed by Victor Berger
parent b6822becf6
commit a38592bc92
8 changed files with 468 additions and 337 deletions

View File

@ -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<Plane>,
/// 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<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: 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<WeakDmabuf> 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<H: Hasher>(&self, state: &mut H) {
Arc::as_ptr(&self.0).hash(state)
}
}
impl Hash for WeakDmabuf {
fn hash<H: Hasher>(&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<Dmabuf> {
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<Dmabuf> {
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<Item = RawFd> + '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<Item = u32> + '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<Item = u32> + '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

View File

@ -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<T> AsDmabuf for GbmBuffer<T> {
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::<Result<Vec<u32>, gbm::DeviceDestroyedError>>()?;
let strides = (0i32..planes)
.map(|i| self.stride_for_plane(i))
.collect::<Result<Vec<u32>, 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<A>,
usage: GbmBufferFlags,
) -> std::io::Result<GbmBuffer<T>> {
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

View File

@ -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)
}
}

View File

@ -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<wl_buffer::WlBuffer>,
egl_images: Option<Vec<EGLImage>>,
destruction_callback_sender: Sender<CleanupResource>,
}
@ -155,7 +153,7 @@ pub struct Gles2Renderer {
extensions: Vec<String>,
programs: [Gles2Program; shaders::FRAGMENT_COUNT],
#[cfg(feature = "wayland_frontend")]
dmabuf_cache: HashMap<BufferEntry, Gles2Texture>,
dmabuf_cache: HashMap<WeakDmabuf, Gles2Texture>,
egl: EGLContext,
gl: ffi::Gles2,
destruction_callback: Receiver<CleanupResource>,
@ -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<Gles2Texture, Gles2Error> {
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<dyn Iterator<Item = &'a Format> + '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<Option<Gles2Texture>, 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(),
}));

View File

@ -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<<Self as Renderer>::TextureId, <Self as Renderer>::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<dyn Iterator<Item = &'a Format> + '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<<Self as Renderer>::TextureId, <Self as Renderer>::Error> {
let dmabuf = buffer
.as_ref()
.user_data()
.get::<Dmabuf>()
.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<<Self as Renderer>::TextureId, <Self as Renderer>::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<BufferType> {
if egl_buffer_reader
if buffer
.as_ref()
.user_data()
.get::<Dmabuf>()
.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<BufferType> {
use crate::backend::allocator::Buffer;
if crate::wayland::shm::with_buffer_contents(&buffer, |_, _| ()).is_ok()
if buffer
.as_ref()
.user_data()
.get::<Dmabuf>()
.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::<Dmabuf>() {
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::<Dmabuf>() {
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))

View File

@ -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;

View File

@ -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<Format>`, 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<Format>`, 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<Self::BufferData, ()> {
//! /* 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::<Dmabuf>().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<Plane>,
/// 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<Self::BufferData, ()>;
/// 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>,
) -> 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<H, L>(
/// 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<F, L>(
display: &mut Display,
formats: Vec<Format>,
handler: H,
handler: F,
logger: L,
) -> Global<zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1>
where
L: Into<Option<::slog::Logger>>,
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::<u64>::into(f.modifier) >> 32) as u32,
Into::<u64>::into(f.modifier) as u32,
);
@ -267,7 +159,7 @@ where
)
}
struct ParamsHandler<H: DmabufHandler> {
struct ParamsHandler<H: for<'a> FnMut(&Dmabuf, DispatchData<'a>) -> bool + 'static> {
pending_planes: Vec<Plane>,
max_planes: u32,
used: bool,
@ -276,7 +168,10 @@ struct ParamsHandler<H: DmabufHandler> {
log: ::slog::Logger,
}
impl<H: DmabufHandler> ParamsHandler<H> {
impl<H> ParamsHandler<H>
where
H: for<'a> FnMut(&Dmabuf, DispatchData<'a>) -> bool + 'static
{
fn add(
&mut self,
params: &BufferParams,
@ -314,15 +209,15 @@ impl<H: DmabufHandler> ParamsHandler<H> {
}
// 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<H: DmabufHandler> ParamsHandler<H> {
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<H: DmabufHandler> ParamsHandler<H> {
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::<wl_buffer::WlBuffer>(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<H: DmabufHandler> ParamsHandler<H> {
}
}
fn create_immed(
fn create_immed<'a>(
&mut self,
params: &BufferParams,
buffer_id: Main<wl_buffer::WlBuffer>,
buffer: Main<wl_buffer::WlBuffer>,
width: i32,
height: i32,
format: u32,
flags: u32,
ddata: DispatchData<'a>,
) {
// Cannot reuse a params:
if self.used {
@ -385,6 +316,18 @@ impl<H: DmabufHandler> ParamsHandler<H> {
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<H: DmabufHandler> ParamsHandler<H> {
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<H: DmabufHandler> ParamsHandler<H> {
"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,

View File

@ -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;