Add allocator module
- Add module that deals with different kinds of buffers (memory and external), their allocation and usage for rendering. Also try to properly support modifiers this time. - Describe gbm functionality as an allocator (instead of a rendering device/surface). - Also create a quick-and-dirty dumb buffer allocator for tesing / simpler tasks. - Add a (current untested) wrapper for dmabufs and some code for converting from gbm for now. - (also untested) Swapchain helper to manage front/backbuffers for surfaceless rendering.
This commit is contained in:
parent
8fc23c767b
commit
c388a502c4
|
@ -0,0 +1,119 @@
|
|||
use super::{Buffer, Format, Modifier};
|
||||
use std::sync::{Arc, Weak};
|
||||
use std::os::unix::io::RawFd;
|
||||
|
||||
const MAX_PLANES: usize = 4;
|
||||
|
||||
struct DmabufInternal {
|
||||
src: Box<dyn Buffer + 'static>,
|
||||
|
||||
num_planes: usize,
|
||||
offsets: [u32; MAX_PLANES],
|
||||
strides: [u32; MAX_PLANES],
|
||||
fds: [RawFd; MAX_PLANES],
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Dmabuf(Arc<DmabufInternal>);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct WeakDmabuf(Weak<DmabufInternal>);
|
||||
|
||||
impl PartialEq for Dmabuf {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
Arc::ptr_eq(&self.0, &other.0)
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
impl Buffer for Dmabuf {
|
||||
fn width(&self) -> u32 {
|
||||
self.0.src.width()
|
||||
}
|
||||
|
||||
fn height(&self) -> u32 {
|
||||
self.0.src.height()
|
||||
}
|
||||
|
||||
fn format(&self) -> Format {
|
||||
self.0.src.format()
|
||||
}
|
||||
}
|
||||
|
||||
impl Dmabuf {
|
||||
pub fn new(
|
||||
src: impl Buffer + 'static,
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 {
|
||||
src: Box::new(src),
|
||||
|
||||
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()],
|
||||
})))
|
||||
}
|
||||
|
||||
pub fn handles(&self) -> &[RawFd] {
|
||||
self.0.fds.split_at(self.0.num_planes).0
|
||||
}
|
||||
|
||||
pub fn offsets(&self) -> &[u32] {
|
||||
self.0.offsets.split_at(self.0.num_planes).0
|
||||
}
|
||||
|
||||
pub fn strides(&self) -> &[u32] {
|
||||
self.0.strides.split_at(self.0.num_planes).0
|
||||
}
|
||||
|
||||
pub fn has_modifier(&self) -> bool {
|
||||
self.0.src.format().modifier != Modifier::Invalid &&
|
||||
self.0.src.format().modifier != Modifier::Linear
|
||||
}
|
||||
|
||||
pub fn weak(&self) -> WeakDmabuf {
|
||||
WeakDmabuf(Arc::downgrade(&self.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl WeakDmabuf {
|
||||
pub fn upgrade(&self) -> Option<Dmabuf> {
|
||||
self.0.upgrade().map(|internal| Dmabuf(internal))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
use std::os::unix::io::AsRawFd;
|
||||
use std::sync::Arc;
|
||||
|
||||
use drm::buffer::Buffer as DrmBuffer;
|
||||
use drm::control::{Device as ControlDevice, dumbbuffer::DumbBuffer as Handle};
|
||||
|
||||
use super::{Allocator, Buffer, Format};
|
||||
use crate::backend::drm::device::{DrmDevice, DrmDeviceInternal, FdWrapper};
|
||||
|
||||
pub struct DumbBuffer<A: AsRawFd + 'static> {
|
||||
fd: Arc<FdWrapper<A>>,
|
||||
handle: Handle,
|
||||
format: Format,
|
||||
}
|
||||
|
||||
impl<A: AsRawFd + 'static> Allocator<DumbBuffer<A>> for DrmDevice<A> {
|
||||
type Error = drm::SystemError;
|
||||
|
||||
fn create_buffer(&mut self, width: u32, height: u32, format: Format) -> Result<DumbBuffer<A>, Self::Error> {
|
||||
let handle = self.create_dumb_buffer((width, height), format.code, 32/* TODO */)?;
|
||||
|
||||
Ok(DumbBuffer {
|
||||
fd: match &*self.internal {
|
||||
DrmDeviceInternal::Atomic(dev) => dev.fd.clone(),
|
||||
DrmDeviceInternal::Legacy(dev) => dev.fd.clone(),
|
||||
},
|
||||
handle,
|
||||
format,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: AsRawFd + 'static> Buffer for DumbBuffer<A> {
|
||||
fn width(&self) -> u32 {
|
||||
self.handle.size().0
|
||||
}
|
||||
|
||||
fn height(&self) -> u32 {
|
||||
self.handle.size().1
|
||||
}
|
||||
|
||||
fn format(&self) -> Format {
|
||||
self.format
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: AsRawFd + 'static> Drop for DumbBuffer<A> {
|
||||
fn drop(&mut self) {
|
||||
let _ = self.fd.destroy_dumb_buffer(self.handle);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
use std::os::unix::io::AsRawFd;
|
||||
use gbm::{BufferObject as GbmBuffer, Device as GbmDevice, BufferObjectFlags};
|
||||
use super::{Allocator, Buffer, Format, Fourcc, Modifier, dmabuf::Dmabuf};
|
||||
|
||||
impl<A: AsRawFd + 'static, T> Allocator<GbmBuffer<T>> for GbmDevice<A> {
|
||||
type Error = std::io::Error;
|
||||
|
||||
fn create_buffer(&mut self, width: u32, height: u32, format: Format) -> std::io::Result<GbmBuffer<T>> {
|
||||
if format.modifier == Modifier::Invalid || format.modifier == Modifier::Linear {
|
||||
let mut usage = BufferObjectFlags::SCANOUT | BufferObjectFlags::RENDERING;
|
||||
if format.modifier == Modifier::Linear {
|
||||
usage |= BufferObjectFlags::LINEAR;
|
||||
}
|
||||
self.create_buffer_object(width, height, format.code, usage)
|
||||
} else {
|
||||
self.create_buffer_object_with_modifiers(width, height, format.code, Some(format.modifier).into_iter())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Buffer for GbmBuffer<T> {
|
||||
fn width(&self) -> u32 {
|
||||
self.width().unwrap_or(0)
|
||||
}
|
||||
|
||||
fn height(&self) -> u32 {
|
||||
self.height().unwrap_or(0)
|
||||
}
|
||||
|
||||
fn format(&self) -> Format {
|
||||
Format {
|
||||
code: self.format().unwrap_or(Fourcc::Argb8888), // we got to return something, but this should never happen anyway
|
||||
modifier: self.modifier().unwrap_or(Modifier::Invalid),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum GbmConvertError {
|
||||
#[error("The gbm device was destroyed")]
|
||||
DeviceDestroyed(#[from] gbm::DeviceDestroyedError),
|
||||
#[error("Buffer consists out of multiple file descriptors, which is currently unsupported")]
|
||||
UnsupportedBuffer,
|
||||
#[error("Buffer returned invalid file descriptor")]
|
||||
InvalidFD,
|
||||
}
|
||||
|
||||
impl<T> std::convert::TryFrom<GbmBuffer<T>> for Dmabuf {
|
||||
type Error = GbmConvertError;
|
||||
|
||||
fn try_from(buf: GbmBuffer<T>) -> Result<Self, GbmConvertError> {
|
||||
let planes = buf.plane_count()? as i32;
|
||||
|
||||
//TODO switch to gbm_bo_get_plane_fd when it lands
|
||||
let mut iter = (0i32..planes).map(|i| buf.handle_for_plane(i));
|
||||
let first = iter.next().expect("Encountered a buffer with zero planes");
|
||||
if iter.try_fold(first, |first, next| {
|
||||
if let (Ok(next), Ok(first)) = (next, first) {
|
||||
if unsafe { next.u64_ == first.u64_ } {
|
||||
return Some(Ok(first));
|
||||
}
|
||||
}
|
||||
None
|
||||
}).is_none() {
|
||||
// GBM is lacking a function to get a FD for a given plane. Instead,
|
||||
// check all planes have the same handle. We can't use
|
||||
// drmPrimeHandleToFD because that messes up handle ref'counting in
|
||||
// the user-space driver.
|
||||
return Err(GbmConvertError::UnsupportedBuffer); //TODO
|
||||
}
|
||||
|
||||
let fds = [buf.fd()?, 0, 0, 0];
|
||||
//if fds.iter().any(|fd| fd == 0) {
|
||||
if fds[0] < 0 {
|
||||
return Err(GbmConvertError::InvalidFD);
|
||||
}
|
||||
|
||||
let offsets = (0i32..planes).map(|i| buf.offset(i)).collect::<Result<Vec<u32>, gbm::DeviceDestroyedError>>()?;
|
||||
let strides = (0i32..planes).map(|i| buf.stride_for_plane(i)).collect::<Result<Vec<u32>, gbm::DeviceDestroyedError>>()?;
|
||||
|
||||
Ok(Dmabuf::new(buf, planes as usize, &offsets, &strides, &fds).unwrap())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
#[cfg(feature = "backend_gbm")]
|
||||
pub mod gbm;
|
||||
#[cfg(feature = "backend_drm")]
|
||||
pub mod dumb;
|
||||
pub mod dmabuf;
|
||||
|
||||
mod swapchain;
|
||||
pub use swapchain::{Slot, Swapchain};
|
||||
|
||||
pub use drm_fourcc::{DrmFormat as Format, DrmFourcc as Fourcc, DrmModifier as Modifier, DrmVendor as Vendor, UnrecognizedFourcc, UnrecognizedVendor};
|
||||
|
||||
pub trait Buffer {
|
||||
fn width(&self) -> u32;
|
||||
fn height(&self) -> u32;
|
||||
fn size(&self) -> (u32, u32) { (self.width(), self.height()) }
|
||||
fn format(&self) -> Format;
|
||||
}
|
||||
|
||||
pub trait Allocator<B: Buffer> {
|
||||
type Error: std::error::Error;
|
||||
|
||||
fn create_buffer(&mut self, width: u32, height: u32, format: Format) -> Result<B, Self::Error>;
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
use std::sync::{Arc, atomic::{AtomicBool, Ordering}};
|
||||
use std::ops::Deref;
|
||||
|
||||
use crate::backend::allocator::{Allocator, Buffer, Format};
|
||||
|
||||
pub const SLOT_CAP: usize = 3;
|
||||
|
||||
pub struct Swapchain<A: Allocator<B>, B: Buffer> {
|
||||
allocator: A,
|
||||
|
||||
width: u32,
|
||||
height: u32,
|
||||
format: Format,
|
||||
|
||||
slots: [Slot<B>; SLOT_CAP],
|
||||
}
|
||||
|
||||
pub struct Slot<B: Buffer> {
|
||||
buffer: Arc<Option<B>>,
|
||||
acquired: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
impl<B: Buffer> Default for Slot<B> {
|
||||
fn default() -> Self {
|
||||
Slot {
|
||||
buffer: Arc::new(None),
|
||||
acquired: Arc::new(AtomicBool::new(false)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Buffer> Clone for Slot<B> {
|
||||
fn clone(&self) -> Self {
|
||||
Slot {
|
||||
buffer: self.buffer.clone(),
|
||||
acquired: self.acquired.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Buffer> Deref for Slot<B> {
|
||||
type Target = B;
|
||||
fn deref(&self) -> &B {
|
||||
Option::as_ref(&*self.buffer).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Buffer> Drop for Slot<B> {
|
||||
fn drop(&mut self) {
|
||||
self.acquired.store(false, Ordering::AcqRel);
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Allocator<B>, B: Buffer> Swapchain<A, B> {
|
||||
pub fn new(allocator: A, width: u32, height: u32, format: Format) -> Swapchain<A, B> {
|
||||
Swapchain {
|
||||
allocator,
|
||||
width,
|
||||
height,
|
||||
format,
|
||||
slots: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn acquire(&mut self) -> Result<Option<Slot<B>>, A::Error> {
|
||||
if let Some(free_slot) = self.slots.iter_mut().filter(|s| !s.acquired.load(Ordering::SeqCst)).next() {
|
||||
if free_slot.buffer.is_none() {
|
||||
free_slot.buffer = Arc::new(Some(self.allocator.create_buffer(self.width, self.height, self.format)?));
|
||||
}
|
||||
assert!(!free_slot.buffer.is_some());
|
||||
|
||||
if !free_slot.acquired.swap(true, Ordering::AcqRel) {
|
||||
return Ok(Some(free_slot.clone()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// no free slots
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, width: u32, height: u32) {
|
||||
if self.width == width && self.height == height {
|
||||
return;
|
||||
}
|
||||
|
||||
self.width = width;
|
||||
self.height = height;
|
||||
|
||||
for mut slot in &mut self.slots {
|
||||
let _ = std::mem::replace(&mut slot, &mut Slot::default());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -75,16 +75,18 @@ use wayland_protocols::unstable::linux_dmabuf::v1::server::{
|
|||
};
|
||||
use wayland_server::{protocol::wl_buffer, 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: ::drm::buffer::format::PixelFormat,
|
||||
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: u64,
|
||||
pub modifier: Modifier,
|
||||
/// Number of planes used by this format
|
||||
pub plane_count: u32,
|
||||
}
|
||||
|
@ -251,9 +253,9 @@ where
|
|||
|
||||
// send the supported formats
|
||||
for f in &*formats {
|
||||
dmabuf.format(f.format.as_raw());
|
||||
dmabuf.format(f.format as u32);
|
||||
if version >= 3 {
|
||||
dmabuf.modifier(f.format.as_raw(), (f.modifier >> 32) as u32, f.modifier as u32);
|
||||
dmabuf.modifier(f.format as u32, (Into::<u64>::into(f.modifier) >> 32) as u32, Into::<u64>::into(f.modifier) as u32);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -424,7 +426,7 @@ fn buffer_basic_checks(
|
|||
) -> bool {
|
||||
// protocol_checks:
|
||||
// This must be a known format
|
||||
let format = match formats.iter().find(|f| f.format.as_raw() == format) {
|
||||
let format = match formats.iter().find(|f| f.format as u32 == format) {
|
||||
Some(f) => f,
|
||||
None => {
|
||||
params.as_ref().post_error(
|
||||
|
|
Loading…
Reference in New Issue