From f585cbc32fb4b2c4c841278b44c90d447cc85ad0 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Wed, 8 Nov 2023 15:51:12 -0800 Subject: [PATCH] Inital support for allocating dmabufs --- Cargo.lock | 1 + Cargo.toml | 1 + src/wayland/buffer.rs | 130 ++++++++++++++++++++++++++++++++++++------ 3 files changed, 114 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bc97f33..10965bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -734,6 +734,7 @@ dependencies = [ "futures-channel", "gbm", "libcosmic", + "memmap2 0.9.0", "tokio", "wayland-protocols 0.31.0", "zbus", diff --git a/Cargo.toml b/Cargo.toml index 765f9f5..498088d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ env_logger = "0.10.0" futures-channel = "0.3.25" gbm = "0.13.0" libcosmic = { git = "https://github.com/pop-os/libcosmic", default-features = false, features = ["tokio", "wayland"] } +memmap2 = "0.9.0" tokio = "1.23.0" wayland-protocols = "0.31.0" zbus = { version = "3.7.0", default-features = false, features = ["tokio"] } diff --git a/src/wayland/buffer.rs b/src/wayland/buffer.rs index d4bf592..32a19e9 100644 --- a/src/wayland/buffer.rs +++ b/src/wayland/buffer.rs @@ -8,31 +8,30 @@ use cctk::{ }, }; use cosmic::iced::widget::image; +use std::os::fd::{AsFd, OwnedFd}; +use wayland_protocols::wp::linux_dmabuf::zv1::client::zwp_linux_buffer_params_v1; use super::AppData; +enum BufferBacking { + Shm(RawPool), + Dmabuf(OwnedFd), +} + pub struct Buffer { - pub pool: RawPool, + backing: BufferBacking, pub buffer: wl_buffer::WlBuffer, pub buffer_info: BufferInfo, } impl AppData { - pub fn create_buffer(&self, buffer_infos: &[BufferInfo]) -> Buffer { - // XXX Handle other formats? - let format = wl_shm::Format::Abgr8888.into(); - - let buffer_info = buffer_infos - .iter() - .find(|x| x.type_ == WEnum::Value(BufferType::WlShm) && x.format == format) - .unwrap(); - - // Assume format is already known to be valid + fn create_shm_backing(&self, buffer_info: &BufferInfo) -> (BufferBacking, wl_buffer::WlBuffer) { let mut pool = RawPool::new( (buffer_info.stride * buffer_info.height) as usize, &self.shm_state, ) .unwrap(); + let format = wl_shm::Format::try_from(buffer_info.format).unwrap(); let buffer = pool.create_buffer( 0, @@ -43,8 +42,85 @@ impl AppData { (), &self.qh, ); + + (BufferBacking::Shm(pool), buffer) + } + + #[allow(dead_code)] + fn create_gbm_backing( + &self, + buffer_info: &BufferInfo, + needs_linear: bool, + ) -> Option<(BufferBacking, wl_buffer::WlBuffer)> { + // TODO Handle errors in some way + let gbm = self.gbm.as_ref()?; + let feedback = self.dmabuf_feedback.as_ref()?; + let formats = feedback.format_table(); + let format_info = feedback + .tranches() + .iter() + .flat_map(|x| &x.formats) + .filter_map(|x| formats.get(*x as usize)) + .find(|x| { + x.format == buffer_info.format + && (!needs_linear || x.modifier == u64::from(gbm::Modifier::Linear)) + })?; + let format = gbm::Format::try_from(buffer_info.format).ok()?; + let modifier = gbm::Modifier::try_from(format_info.modifier).ok()?; + let bo = gbm + .create_buffer_object_with_modifiers::<()>( + buffer_info.width, + buffer_info.height, + format, + [modifier].into_iter(), + ) + .ok()?; + + let fd = bo.fd().ok()?; + let stride = bo.stride().ok()?; + let params = self.dmabuf_state.create_params(&self.qh).ok()?; + params.add(fd.as_fd(), 0, 0, stride, modifier.into()); + let buffer = params + .create_immed( + buffer_info.width as i32, + buffer_info.height as i32, + buffer_info.format, + zwp_linux_buffer_params_v1::Flags::empty(), + &self.qh, + ) + .0; + + Some((BufferBacking::Dmabuf(fd), buffer)) + } + + pub fn create_buffer(&self, buffer_infos: &[BufferInfo]) -> Buffer { + // XXX Handle other formats? + let format = wl_shm::Format::Abgr8888.into(); + + /* + if let Some(buffer_info) = buffer_infos + .iter() + .find(|x| x.type_ == WEnum::Value(BufferType::Dmabuf) && x.format == format) + { + if let Some((backing, buffer)) = self.create_gbm_backing(buffer_info, true) { + return Buffer { + backing, + buffer, + buffer_info: buffer_info.clone(), + }; + } + } + */ + + // Fallback to shm buffer + // Assume format is already known to be valid + let buffer_info = buffer_infos + .iter() + .find(|x| x.type_ == WEnum::Value(BufferType::WlShm) && x.format == format) + .unwrap(); + let (backing, buffer) = self.create_shm_backing(buffer_info); Buffer { - pool, + backing, buffer, buffer_info: buffer_info.clone(), } @@ -53,14 +129,32 @@ impl AppData { impl Buffer { // Buffer must be released by server for safety + // XXX is this at all a performance issue? #[allow(clippy::wrong_self_convention)] pub unsafe fn to_image(&mut self) -> image::Handle { - // XXX is this at all a performance issue? - image::Handle::from_pixels( - self.buffer_info.width, - self.buffer_info.height, - self.pool.mmap().to_vec(), - ) + let pixels = match &mut self.backing { + BufferBacking::Shm(pool) => pool.mmap().to_vec(), + // NOTE: Only will work with linear modifier + BufferBacking::Dmabuf(fd) => { + // XXX Error handling? + let mmap = memmap2::Mmap::map(&*fd).unwrap(); + if self.buffer_info.stride == self.buffer_info.width * 4 { + mmap.to_vec() + } else { + let width = self.buffer_info.width as usize; + let height = self.buffer_info.height as usize; + let stride = self.buffer_info.stride as usize; + let output_stride = width * 4; + let mut pixels = vec![0; height * output_stride]; + for y in 0..height { + pixels[y * output_stride..y * output_stride + output_stride] + .copy_from_slice(&mmap[y * stride..y * stride + output_stride]); + } + pixels + } + } + }; + image::Handle::from_pixels(self.buffer_info.width, self.buffer_info.height, pixels) } }