wayland.dmabuf: module docs & some logging

This commit is contained in:
Victor Berger 2019-05-06 14:30:47 +02:00 committed by Victor Berger
parent e11c0278dd
commit c1f759aa00
1 changed files with 102 additions and 14 deletions

View File

@ -1,3 +1,70 @@
//! Linux DMABUF protocol
//!
//! This module provides helper to handle the linux-dmabuf protocol, which allows clients to submit their
//! 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.
//!
//! ## 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
//!
//! 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 sclient's submission as a `BufferInfo` struct,
//! and you need to validate it and maybe import it into your rendered. The `BufferData` associated type
//! allows you to store any metadata of 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.
//!
//! ```
//! # 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 resource 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:
//!
//! # fn main() {
//! # let mut event_loop = wayland_server::calloop::EventLoop::<()>::new().unwrap();
//! # let mut display = wayland_server::Display::new(event_loop.handle());
//! // define your supported formats
//! let formats = vec![
//! /* ... */
//! ];
//! let dmabuf_global = init_dmabuf_global(
//! &mut display,
//! formats,
//! MyDmabufHandler,
//! None // we don't provide a logger in this example
//! );
//! # }
//! ```
use std::{cell::RefCell, os::unix::io::RawFd, rc::Rc}; use std::{cell::RefCell, os::unix::io::RawFd, rc::Rc};
pub use wayland_protocols::unstable::linux_dmabuf::v1::server::zwp_linux_buffer_params_v1::Flags; pub use wayland_protocols::unstable::linux_dmabuf::v1::server::zwp_linux_buffer_params_v1::Flags;
@ -11,12 +78,16 @@ use wayland_server::{protocol::wl_buffer, Display, Global, NewResource};
/// Representation of a Dmabuf format, as advertized to the client /// Representation of a Dmabuf format, as advertized to the client
pub struct Format { pub struct Format {
/// The format identifier /// The format identifier.
///
/// It must be a `DRM_FORMAT` code, as defined by the libdrm's drm_fourcc.h. The Linux kernel's DRM
/// sub-system is the authoritative source on how the format codes should work.
pub format: u32, pub format: u32,
/// High part of the supported modifiers /// The supported dmabuf layout modifier.
pub modifier_hi: u32, ///
/// Low part of the supported modifiers /// This is an opaque token. Drivers use this token to express tiling, compression, etc. driver-specific
pub modifier_lo: u32, /// modifications to the base format defined by the DRM fourcc code.
pub modifier: u64,
/// Number of planes used by this format /// Number of planes used by this format
pub plane_count: u32, pub plane_count: u32,
} }
@ -31,10 +102,8 @@ pub struct Plane {
pub offset: u32, pub offset: u32,
/// Stride for this plane /// Stride for this plane
pub stride: u32, pub stride: u32,
/// High part of the modifiers for this plane /// Modifier for this plane
pub modifier_hi: u32, pub modifier: u64,
/// Low part of the modifiers for this plane
pub modifier_lo: u32,
} }
/// The complete information provided by the client to create a dmabuf buffer /// The complete information provided by the client to create a dmabuf buffer
@ -60,7 +129,9 @@ pub struct BufferInfo {
pub trait DmabufHandler { pub trait DmabufHandler {
/// The data of a successfully imported dmabuf. /// The data of a successfully imported dmabuf.
/// ///
/// This will be stored as the `user_data` of the `WlBuffer` associated with this 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; type BufferData: 'static;
/// Validate a dmabuf /// Validate a dmabuf
/// ///
@ -102,15 +173,22 @@ where
L: Into<Option<::slog::Logger>>, L: Into<Option<::slog::Logger>>,
H: DmabufHandler + 'static, H: DmabufHandler + 'static,
{ {
let log = crate::slog_or_stdlog(logger); let log = crate::slog_or_stdlog(logger).new(o!("smithay_module" => "dmabuf_handler"));
let max_planes = formats.iter().map(|f| f.plane_count).max().unwrap_or(0); let max_planes = formats.iter().map(|f| f.plane_count).max().unwrap_or(0);
let formats = Rc::new(formats); let formats = Rc::new(formats);
let handler = Rc::new(RefCell::new(handler)); let handler = Rc::new(RefCell::new(handler));
trace!(
log,
"Initializing DMABUF handler with {} supported formats",
formats.len()
);
display.create_global(3, move |new_dmabuf, version| { display.create_global(3, move |new_dmabuf, version| {
let dma_formats = formats.clone(); let dma_formats = formats.clone();
let dma_handler = handler.clone(); let dma_handler = handler.clone();
let dma_log = log.clone();
let dmabuf: zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1 = new_dmabuf.implement_closure( let dmabuf: zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1 = new_dmabuf.implement_closure(
move |req, _| match req { move |req, _| match req {
zwp_linux_dmabuf_v1::Request::CreateParams { params_id } => { zwp_linux_dmabuf_v1::Request::CreateParams { params_id } => {
@ -121,6 +199,7 @@ where
used: false, used: false,
formats: dma_formats.clone(), formats: dma_formats.clone(),
handler: dma_handler.clone(), handler: dma_handler.clone(),
log: dma_log.clone(),
}, },
None::<fn(_)>, None::<fn(_)>,
(), (),
@ -136,7 +215,7 @@ where
for f in &*formats { for f in &*formats {
dmabuf.format(f.format); dmabuf.format(f.format);
if version >= 3 { if version >= 3 {
dmabuf.modifier(f.format, f.modifier_hi, f.modifier_lo); dmabuf.modifier(f.format, (f.modifier >> 32) as u32, f.modifier as u32);
} }
} }
}) })
@ -148,6 +227,7 @@ struct ParamsHandler<H: DmabufHandler> {
used: bool, used: bool,
formats: Rc<Vec<Format>>, formats: Rc<Vec<Format>>,
handler: Rc<RefCell<H>>, handler: Rc<RefCell<H>>,
log: ::slog::Logger,
} }
impl<H: DmabufHandler> ParamRequestHandler for ParamsHandler<H> { impl<H: DmabufHandler> ParamRequestHandler for ParamsHandler<H> {
@ -193,8 +273,7 @@ impl<H: DmabufHandler> ParamRequestHandler for ParamsHandler<H> {
plane_idx, plane_idx,
offset, offset,
stride, stride,
modifier_hi, modifier: ((modifier_hi as u64) << 32) + (modifier_lo as u64),
modifier_lo,
}); });
} }
@ -216,6 +295,7 @@ impl<H: DmabufHandler> ParamRequestHandler for ParamsHandler<H> {
width, width,
height, height,
) { ) {
trace!(self.log, "Killing client providing bogus dmabuf buffer params.");
return; return;
} }
let info = BufferInfo { let info = BufferInfo {
@ -233,9 +313,11 @@ impl<H: DmabufHandler> ParamRequestHandler for ParamsHandler<H> {
.and_then(|c| c.create_resource::<wl_buffer::WlBuffer>(1)) .and_then(|c| c.create_resource::<wl_buffer::WlBuffer>(1))
{ {
let buffer = handler.create_buffer(data, buffer); let buffer = handler.create_buffer(data, buffer);
trace!(self.log, "Creating a new validated dma wl_buffer.");
params.created(&buffer); params.created(&buffer);
} }
} else { } else {
trace!(self.log, "Refusing creation of an invalid dma wl_buffer.");
params.failed(); params.failed();
} }
} }
@ -266,6 +348,7 @@ impl<H: DmabufHandler> ParamRequestHandler for ParamsHandler<H> {
width, width,
height, height,
) { ) {
trace!(self.log, "Killing client providing bogus dmabuf buffer params.");
return; return;
} }
let info = BufferInfo { let info = BufferInfo {
@ -277,8 +360,13 @@ impl<H: DmabufHandler> ParamRequestHandler for ParamsHandler<H> {
}; };
let mut handler = self.handler.borrow_mut(); let mut handler = self.handler.borrow_mut();
if let Ok(data) = handler.validate_dmabuf(info) { 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); handler.create_buffer(data, buffer_id);
} else { } else {
trace!(
self.log,
"Refusing creation of an invalid immediate dma wl_buffer, killing client."
);
params.as_ref().post_error( params.as_ref().post_error(
ParamError::InvalidWlBuffer as u32, ParamError::InvalidWlBuffer as u32,
"create_immed resulted in an invalid buffer.".into(), "create_immed resulted in an invalid buffer.".into(),