diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/niri.rs | 8 | ||||
| -rw-r--r-- | src/pw_utils.rs | 543 |
2 files changed, 417 insertions, 134 deletions
diff --git a/src/niri.rs b/src/niri.rs index aaf2ef7f..7ee4d2f7 100644 --- a/src/niri.rs +++ b/src/niri.rs @@ -1369,8 +1369,16 @@ impl State { } }; + let render_formats = self + .backend + .with_primary_renderer(|renderer| { + renderer.egl_context().dmabuf_render_formats().clone() + }) + .unwrap_or_default(); + let res = pw.start_cast( gbm, + render_formats, session_id, target, size, diff --git a/src/pw_utils.rs b/src/pw_utils.rs index 8d7d71f1..281fb3a9 100644 --- a/src/pw_utils.rs +++ b/src/pw_utils.rs @@ -1,6 +1,7 @@ use std::cell::{Cell, RefCell}; use std::collections::HashMap; use std::io::Cursor; +use std::iter::zip; use std::mem; use std::os::fd::{AsFd, AsRawFd, BorrowedFd}; use std::rc::Rc; @@ -16,16 +17,19 @@ use pipewire::spa::param::format::{FormatProperties, MediaSubtype, MediaType}; use pipewire::spa::param::format_utils::parse_format; use pipewire::spa::param::video::{VideoFormat, VideoInfoRaw}; use pipewire::spa::param::ParamType; +use pipewire::spa::pod::deserialize::PodDeserializer; use pipewire::spa::pod::serialize::PodSerializer; -use pipewire::spa::pod::{self, ChoiceValue, Pod, Property, PropertyFlags}; +use pipewire::spa::pod::{self, ChoiceValue, Pod, PodPropFlags, Property, PropertyFlags}; use pipewire::spa::sys::*; use pipewire::spa::utils::{ Choice, ChoiceEnum, ChoiceFlags, Direction, Fraction, Rectangle, SpaTypes, }; +use pipewire::spa::{self}; use pipewire::stream::{Stream, StreamFlags, StreamListener, StreamState}; use smithay::backend::allocator::dmabuf::{AsDmabuf, Dmabuf}; +use smithay::backend::allocator::format::FormatSet; use smithay::backend::allocator::gbm::{GbmBuffer, GbmBufferFlags, GbmDevice}; -use smithay::backend::allocator::Fourcc; +use smithay::backend::allocator::{Format, Fourcc}; use smithay::backend::drm::DrmDeviceFd; use smithay::backend::renderer::element::RenderElement; use smithay::backend::renderer::gles::GlesRenderer; @@ -57,22 +61,32 @@ pub struct Cast { _listener: StreamListener<()>, pub is_active: Rc<Cell<bool>>, pub target: CastTarget, - pub size: Rc<Cell<CastSize>>, - pub refresh: u32, + formats: FormatSet, + state: Rc<RefCell<CastState>>, + refresh: Rc<Cell<u32>>, offer_alpha: bool, pub cursor_mode: CursorMode, pub last_frame_time: Duration, - pub min_time_between_frames: Rc<Cell<Duration>>, - pub dmabufs: Rc<RefCell<HashMap<i32, Dmabuf>>>, + min_time_between_frames: Rc<Cell<Duration>>, + dmabufs: Rc<RefCell<HashMap<i64, Dmabuf>>>, } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum CastSize { - InitialPending(Size<i32, Physical>), - Ready(Size<i32, Physical>), - ChangePending { - last_negotiated: Size<i32, Physical>, - pending: Size<i32, Physical>, +#[derive(Debug)] +pub enum CastState { + ResizePending { + pending_size: Size<u32, Physical>, + }, + ConfirmationPending { + size: Size<u32, Physical>, + alpha: bool, + modifier: Modifier, + plane_count: i32, + }, + Ready { + size: Size<u32, Physical>, + alpha: bool, + modifier: Modifier, + plane_count: i32, }, } @@ -89,17 +103,17 @@ pub enum CastTarget { } macro_rules! make_params { - ($params:ident, $size:expr, $refresh:expr, $alpha:expr) => { + ($params:ident, $formats:expr, $size:expr, $refresh:expr, $alpha:expr) => { let mut b1 = Vec::new(); let mut b2 = Vec::new(); - let o1 = make_video_params($size, $refresh, false); + let o1 = make_video_params($formats, $size, $refresh, false); let pod1 = make_pod(&mut b1, o1); let mut p1; let mut p2; $params = if $alpha { - let o2 = make_video_params($size, $refresh, true); + let o2 = make_video_params($formats, $size, $refresh, true); p2 = [pod1, make_pod(&mut b2, o2)]; &mut p2[..] } else { @@ -157,6 +171,7 @@ impl PipeWire { pub fn start_cast( &self, gbm: GbmDevice<DrmDeviceFd>, + formats: FormatSet, session_id: usize, target: CastTarget, size: Size<i32, Physical>, @@ -190,10 +205,10 @@ impl PipeWire { let is_active = Rc::new(Cell::new(false)); let min_time_between_frames = Rc::new(Cell::new(Duration::ZERO)); let dmabufs = Rc::new(RefCell::new(HashMap::new())); - let negotiated_alpha = Rc::new(Cell::new(false)); + let refresh = Rc::new(Cell::new(refresh)); - let pending_size = size; - let size = Rc::new(Cell::new(CastSize::InitialPending(size))); + let pending_size = Size::from((size.w as u32, size.h as u32)); + let state = Rc::new(RefCell::new(CastState::ResizePending { pending_size })); let listener = stream .add_local_listener_with_user_data(()) @@ -244,8 +259,11 @@ impl PipeWire { }) .param_changed({ let min_time_between_frames = min_time_between_frames.clone(); - let size = size.clone(); - let negotiated_alpha = negotiated_alpha.clone(); + let stop_cast = stop_cast.clone(); + let state = state.clone(); + let gbm = gbm.clone(); + let formats = formats.clone(); + let refresh = refresh.clone(); move |stream, (), id, pod| { let id = ParamType::from_raw(id); trace!(?id, "pw stream: param_changed"); @@ -270,22 +288,28 @@ impl PipeWire { let mut format = VideoInfoRaw::new(); format.parse(pod).unwrap(); - trace!("pw stream: got format = {format:?}"); + debug!("pw stream: got format = {format:?}"); - let expected_size = size.get().expected_format_size(); - let format_size = - Size::from((format.size().width as i32, format.size().height as i32)); + let format_size = Size::from((format.size().width, format.size().height)); - if format_size == expected_size { - size.set(CastSize::Ready(expected_size)); - } else { - size.set(CastSize::ChangePending { - last_negotiated: format_size, - pending: expected_size, - }); + let mut state = state.borrow_mut(); + if format_size != state.expected_format_size() { + if !matches!(&*state, CastState::ResizePending { .. }) { + warn!("pw stream: wrong size, but we're not resizing"); + stop_cast(); + return; + } + + debug!("pw stream: wrong size, waiting"); + return; } - negotiated_alpha.set(format.format() == VideoFormat::BGRA); + let format_has_alpha = format.format() == VideoFormat::BGRA; + let fourcc = if format_has_alpha { + Fourcc::Argb8888 + } else { + Fourcc::Xrgb8888 + }; let max_frame_rate = format.max_framerate(); // Subtract 0.5 ms to improve edge cases when equal to refresh rate. @@ -294,9 +318,161 @@ impl PipeWire { ) - Duration::from_micros(500); min_time_between_frames.set(min_frame_time); - const BPP: u32 = 4; - let stride = format.size().width * BPP; - let size = stride * format.size().height; + let object = pod.as_object().unwrap(); + let Some(prop_modifier) = + object.find_prop(spa::utils::Id(FormatProperties::VideoModifier.0)) + else { + warn!("pw stream: modifier prop missing"); + stop_cast(); + return; + }; + + if prop_modifier.flags().contains(PodPropFlags::DONT_FIXATE) { + debug!("pw stream: fixating the modifier"); + + let pod_modifier = prop_modifier.value(); + let Ok((_, modifiers)) = PodDeserializer::deserialize_from::<Choice<i64>>( + pod_modifier.as_bytes(), + ) else { + warn!("pw stream: wrong modifier property type"); + stop_cast(); + return; + }; + + let ChoiceEnum::Enum { alternatives, .. } = modifiers.1 else { + warn!("pw stream: wrong modifier choice type"); + stop_cast(); + return; + }; + + let (modifier, plane_count) = match find_preferred_modifier( + &gbm, + format_size, + fourcc, + alternatives, + ) { + Ok(x) => x, + Err(err) => { + warn!("pw stream: couldn't find preferred modifier: {err:?}"); + stop_cast(); + return; + } + }; + + debug!( + "pw stream: allocation successful \ + (modifier={modifier:?}, plane_count={plane_count}), \ + moving to confirmation pending" + ); + + *state = CastState::ConfirmationPending { + size: format_size, + alpha: format_has_alpha, + modifier, + plane_count: plane_count as i32, + }; + + let fixated_format = FormatSet::from_iter([Format { + code: fourcc, + modifier, + }]); + + let mut b1 = Vec::new(); + let mut b2 = Vec::new(); + + let o1 = make_video_params( + &fixated_format, + format_size, + refresh.get(), + format_has_alpha, + ); + let pod1 = make_pod(&mut b1, o1); + + let o2 = make_video_params( + &formats, + format_size, + refresh.get(), + format_has_alpha, + ); + let mut params = [pod1, make_pod(&mut b2, o2)]; + + if let Err(err) = stream.update_params(&mut params) { + warn!("error updating stream params: {err:?}"); + stop_cast(); + } + + return; + } + + // Verify that alpha and modifier didn't change. + let plane_count = match &*state { + CastState::ConfirmationPending { + size, + alpha, + modifier, + plane_count, + } + | CastState::Ready { + size, + alpha, + modifier, + plane_count, + } if *alpha == format_has_alpha + && *modifier == Modifier::from(format.modifier()) => + { + let size = *size; + let alpha = *alpha; + let modifier = *modifier; + let plane_count = *plane_count; + + debug!("pw stream: moving to ready state"); + + *state = CastState::Ready { + size, + alpha, + modifier, + plane_count, + }; + + plane_count + } + _ => { + // We're negotiating a single modifier, or alpha or modifier changed, + // so we need to do a test allocation. + let (modifier, plane_count) = match find_preferred_modifier( + &gbm, + format_size, + fourcc, + vec![format.modifier() as i64], + ) { + Ok(x) => x, + Err(err) => { + warn!("pw stream: test allocation failed: {err:?}"); + stop_cast(); + return; + } + }; + + debug!( + "pw stream: allocation successful \ + (modifier={modifier:?}, plane_count={plane_count}), \ + moving to ready" + ); + + *state = CastState::Ready { + size: format_size, + alpha: format_has_alpha, + modifier, + plane_count: plane_count as i32, + }; + + plane_count as i32 + } + }; + + // const BPP: u32 = 4; + // let stride = format.size().width * BPP; + // let size = stride * format.size().height; let o1 = pod::object!( SpaTypes::ObjectParamBuffers, @@ -312,10 +488,10 @@ impl PipeWire { } ))), ), - Property::new(SPA_PARAM_BUFFERS_blocks, pod::Value::Int(1)), - Property::new(SPA_PARAM_BUFFERS_size, pod::Value::Int(size as i32)), - Property::new(SPA_PARAM_BUFFERS_stride, pod::Value::Int(stride as i32)), - Property::new(SPA_PARAM_BUFFERS_align, pod::Value::Int(16)), + Property::new(SPA_PARAM_BUFFERS_blocks, pod::Value::Int(plane_count)), + // Property::new(SPA_PARAM_BUFFERS_size, pod::Value::Int(size as i32)), + // Property::new(SPA_PARAM_BUFFERS_stride, pod::Value::Int(stride as i32)), + // Property::new(SPA_PARAM_BUFFERS_align, pod::Value::Int(16)), Property::new( SPA_PARAM_BUFFERS_dataType, pod::Value::Choice(ChoiceValue::Int(Choice( @@ -345,25 +521,38 @@ impl PipeWire { let mut params = [ make_pod(&mut b1, o1), // make_pod(&mut b2, o2) ]; - stream.update_params(&mut params).unwrap(); + + if let Err(err) = stream.update_params(&mut params) { + warn!("error updating stream params: {err:?}"); + stop_cast(); + } } }) .add_buffer({ let dmabufs = dmabufs.clone(); let stop_cast = stop_cast.clone(); - let size = size.clone(); - let negotiated_alpha = negotiated_alpha.clone(); + let state = state.clone(); move |stream, (), buffer| { - let size = size.get().negotiated_size(); - let alpha = negotiated_alpha.get(); - trace!("pw stream: add_buffer, size={:?}, alpha={alpha}", size); - let size = size.expect("size must be negotiated to allocate buffers"); + let (size, alpha, modifier) = if let CastState::Ready { + size, + alpha, + modifier, + .. + } = &*state.borrow() + { + (*size, *alpha, *modifier) + } else { + trace!("pw stream: add buffer, but not ready yet"); + return; + }; + + trace!( + "pw stream: add_buffer, size={size:?}, alpha={alpha}, \ + modifier={modifier:?}" + ); unsafe { let spa_buffer = (*buffer).buffer; - let spa_data = (*spa_buffer).datas; - assert!((*spa_buffer).n_datas > 0); - assert!((*spa_data).type_ & (1 << DataType::DmaBuf.as_raw()) > 0); let fourcc = if alpha { Fourcc::Argb8888 @@ -371,36 +560,29 @@ impl PipeWire { Fourcc::Xrgb8888 }; - let bo = match gbm.create_buffer_object::<()>( - size.w as u32, - size.h as u32, - fourcc, - GbmBufferFlags::RENDERING, - ) { - Ok(bo) => bo, - Err(err) => { - warn!("error creating GBM buffer object: {err:?}"); - stop_cast(); - return; - } - }; - let buffer = GbmBuffer::from_bo(bo, true); - let dmabuf = match buffer.export() { + let dmabuf = match allocate_dmabuf(&gbm, size, fourcc, modifier) { Ok(dmabuf) => dmabuf, Err(err) => { - warn!("error exporting GBM buffer object as dmabuf: {err:?}"); + warn!("error allocating dmabuf: {err:?}"); stop_cast(); return; } }; - let fd = dmabuf.handles().next().unwrap().as_raw_fd(); + let plane_count = dmabuf.num_planes(); + assert_eq!((*spa_buffer).n_datas as usize, plane_count); + + for (i, fd) in dmabuf.handles().enumerate() { + let spa_data = (*spa_buffer).datas.add(i); + assert!((*spa_data).type_ & (1 << DataType::DmaBuf.as_raw()) > 0); - (*spa_data).type_ = DataType::DmaBuf.as_raw(); - (*spa_data).maxsize = dmabuf.strides().next().unwrap() * size.h as u32; - (*spa_data).fd = fd as i64; - (*spa_data).flags = SPA_DATA_FLAG_READWRITE; + (*spa_data).type_ = DataType::DmaBuf.as_raw(); + (*spa_data).maxsize = 1; + (*spa_data).fd = fd.as_raw_fd() as i64; + (*spa_data).flags = SPA_DATA_FLAG_READWRITE; + } + let fd = (*(*spa_buffer).datas).fd; assert!(dmabufs.borrow_mut().insert(fd, dmabuf).is_none()); } @@ -421,7 +603,7 @@ impl PipeWire { let spa_data = (*spa_buffer).datas; assert!((*spa_buffer).n_datas > 0); - let fd = (*spa_data).fd as i32; + let fd = (*spa_data).fd; dmabufs.borrow_mut().remove(&fd); } } @@ -429,10 +611,10 @@ impl PipeWire { .register() .unwrap(); - trace!("starting pw stream with size={pending_size:?}, refresh={refresh}"); + trace!("starting pw stream with size={pending_size:?}, refresh={refresh:?}"); let params; - make_params!(params, pending_size, refresh, alpha); + make_params!(params, &formats, pending_size, refresh.get(), alpha); stream .connect( Direction::Output, @@ -448,7 +630,8 @@ impl PipeWire { _listener: listener, is_active, target, - size, + formats, + state, refresh, offer_alpha: alpha, cursor_mode, @@ -462,12 +645,14 @@ impl PipeWire { impl Cast { pub fn ensure_size(&self, size: Size<i32, Physical>) -> anyhow::Result<CastSizeChange> { - let current_size = self.size.get(); - if current_size == CastSize::Ready(size) { + let new_size = Size::from((size.w as u32, size.h as u32)); + + let mut state = self.state.borrow_mut(); + if matches!(&*state, CastState::Ready { size, .. } if *size == new_size) { return Ok(CastSizeChange::Ready); } - if current_size.pending_size() == Some(size) { + if state.pending_size() == Some(new_size) { debug!("stream size still hasn't changed, skipping frame"); return Ok(CastSizeChange::Pending); } @@ -475,10 +660,18 @@ impl Cast { let _span = tracy_client::span!("Cast::ensure_size"); debug!("cast size changed, updating stream size"); - self.size.set(current_size.with_pending(size)); + *state = CastState::ResizePending { + pending_size: new_size, + }; let params; - make_params!(params, size, self.refresh, self.offer_alpha); + make_params!( + params, + &self.formats, + new_size, + self.refresh.get(), + self.offer_alpha + ); self.stream .update_params(params) .context("error updating stream params")?; @@ -487,17 +680,17 @@ impl Cast { } pub fn set_refresh(&mut self, refresh: u32) -> anyhow::Result<()> { - if self.refresh == refresh { + if self.refresh.get() == refresh { return Ok(()); } let _span = tracy_client::span!("Cast::set_refresh"); debug!("cast FPS changed, updating stream FPS"); - self.refresh = refresh; + self.refresh.set(refresh); - let size = self.size.get().expected_format_size(); + let size = self.state.borrow().expected_format_size(); let params; - make_params!(params, size, self.refresh, self.offer_alpha); + make_params!(params, &self.formats, size, refresh, self.offer_alpha); self.stream .update_params(params) .context("error updating stream params")?; @@ -552,77 +745,88 @@ impl Cast { } }; - let data = &mut buffer.datas_mut()[0]; - let fd = data.as_raw().fd as i32; - let dmabuf = self.dmabufs.borrow()[&fd].clone(); + let fd = buffer.datas_mut()[0].as_raw().fd; + let dmabuf = &self.dmabufs.borrow()[&fd]; - if let Err(err) = - render_to_dmabuf(renderer, dmabuf, size, scale, Transform::Normal, elements) - { + if let Err(err) = render_to_dmabuf( + renderer, + dmabuf.clone(), + size, + scale, + Transform::Normal, + elements, + ) { warn!("error rendering to dmabuf: {err:?}"); return false; } - let maxsize = data.as_raw().maxsize; - let chunk = data.chunk_mut(); - *chunk.size_mut() = maxsize; - *chunk.stride_mut() = maxsize as i32 / size.h; - - true - } -} + for (data, (stride, offset)) in + zip(buffer.datas_mut(), zip(dmabuf.strides(), dmabuf.offsets())) + { + let chunk = data.chunk_mut(); + *chunk.size_mut() = 1; + *chunk.stride_mut() = stride as i32; + *chunk.offset_mut() = offset; -impl CastSize { - fn pending_size(self) -> Option<Size<i32, Physical>> { - match self { - CastSize::InitialPending(pending) => Some(pending), - CastSize::Ready(_) => None, - CastSize::ChangePending { pending, .. } => Some(pending), + trace!( + "pw buffer: fd = {}, stride = {stride}, offset = {offset}", + data.as_raw().fd + ); } - } - fn negotiated_size(self) -> Option<Size<i32, Physical>> { - match self { - CastSize::InitialPending(_) => None, - CastSize::Ready(size) => Some(size), - CastSize::ChangePending { - last_negotiated, .. - } => Some(last_negotiated), - } + true } +} - fn expected_format_size(self) -> Size<i32, Physical> { +impl CastState { + fn pending_size(&self) -> Option<Size<u32, Physical>> { match self { - CastSize::InitialPending(pending) => pending, - CastSize::Ready(size) => size, - CastSize::ChangePending { pending, .. } => pending, + CastState::ResizePending { pending_size } => Some(*pending_size), + CastState::ConfirmationPending { size, .. } => Some(*size), + CastState::Ready { .. } => None, } } - fn with_pending(self, pending: Size<i32, Physical>) -> Self { + fn expected_format_size(&self) -> Size<u32, Physical> { match self { - CastSize::InitialPending(_) => CastSize::InitialPending(pending), - CastSize::Ready(size) => CastSize::ChangePending { - last_negotiated: size, - pending, - }, - CastSize::ChangePending { - last_negotiated, .. - } => CastSize::ChangePending { - last_negotiated, - pending, - }, + CastState::ResizePending { pending_size } => *pending_size, + CastState::ConfirmationPending { size, .. } => *size, + CastState::Ready { size, .. } => *size, } } } -fn make_video_params(size: Size<i32, Physical>, refresh: u32, alpha: bool) -> pod::Object { +fn make_video_params( + formats: &FormatSet, + size: Size<u32, Physical>, + refresh: u32, + alpha: bool, +) -> pod::Object { let format = if alpha { VideoFormat::BGRA } else { VideoFormat::BGRx }; + let fourcc = if alpha { + Fourcc::Argb8888 + } else { + Fourcc::Xrgb8888 + }; + + let formats: Vec<_> = formats + .iter() + .filter_map(|f| (f.code == fourcc).then_some(u64::from(f.modifier) as i64)) + .collect(); + + trace!("offering: {formats:?}"); + + let dont_fixate = if formats.len() > 1 { + PropertyFlags::DONT_FIXATE + } else { + PropertyFlags::empty() + }; + pod::object!( SpaTypes::ObjectParamFormat, ParamType::EnumFormat, @@ -631,15 +835,21 @@ fn make_video_params(size: Size<i32, Physical>, refresh: u32, alpha: bool) -> po pod::property!(FormatProperties::VideoFormat, Id, format), Property { key: FormatProperties::VideoModifier.as_raw(), - value: pod::Value::Long(u64::from(Modifier::Invalid) as i64), - flags: PropertyFlags::MANDATORY, + flags: PropertyFlags::MANDATORY | dont_fixate, + value: pod::Value::Choice(ChoiceValue::Long(Choice( + ChoiceFlags::empty(), + ChoiceEnum::Enum { + default: formats[0], + alternatives: formats, + } + ))) }, pod::property!( FormatProperties::VideoSize, Rectangle, Rectangle { - width: size.w as u32, - height: size.h as u32, + width: size.w, + height: size.h, } ), pod::property!( @@ -669,3 +879,68 @@ fn make_pod(buffer: &mut Vec<u8>, object: pod::Object) -> &Pod { PodSerializer::serialize(Cursor::new(&mut *buffer), &pod::Value::Object(object)).unwrap(); Pod::from_bytes(buffer).unwrap() } + +fn find_preferred_modifier( + gbm: &GbmDevice<DrmDeviceFd>, + size: Size<u32, Physical>, + fourcc: Fourcc, + modifiers: Vec<i64>, +) -> anyhow::Result<(Modifier, usize)> { + debug!("find_preferred_modifier: size={size:?}, fourcc={fourcc}, modifiers={modifiers:?}"); + + let (buffer, modifier) = allocate_buffer(gbm, size, fourcc, &modifiers)?; + + let dmabuf = buffer + .export() + .context("error exporting GBM buffer object as dmabuf")?; + let plane_count = dmabuf.num_planes(); + + // FIXME: Ideally this also needs to try binding the dmabuf for rendering. + + Ok((modifier, plane_count)) +} + +fn allocate_buffer( + gbm: &GbmDevice<DrmDeviceFd>, + size: Size<u32, Physical>, + fourcc: Fourcc, + modifiers: &[i64], +) -> anyhow::Result<(GbmBuffer, Modifier)> { + let (w, h) = (size.w, size.h); + let flags = GbmBufferFlags::RENDERING; + + if modifiers.len() == 1 && Modifier::from(modifiers[0] as u64) == Modifier::Invalid { + let bo = gbm + .create_buffer_object::<()>(w, h, fourcc, flags) + .context("error creating GBM buffer object")?; + + let buffer = GbmBuffer::from_bo(bo, true); + Ok((buffer, Modifier::Invalid)) + } else { + let modifiers = modifiers + .iter() + .map(|m| Modifier::from(*m as u64)) + .filter(|m| *m != Modifier::Invalid); + + let bo = gbm + .create_buffer_object_with_modifiers2::<()>(w, h, fourcc, modifiers, flags) + .context("error creating GBM buffer object")?; + + let modifier = bo.modifier().unwrap(); + let buffer = GbmBuffer::from_bo(bo, false); + Ok((buffer, modifier)) + } +} + +fn allocate_dmabuf( + gbm: &GbmDevice<DrmDeviceFd>, + size: Size<u32, Physical>, + fourcc: Fourcc, + modifier: Modifier, +) -> anyhow::Result<Dmabuf> { + let (buffer, _modifier) = allocate_buffer(gbm, size, fourcc, &[u64::from(modifier) as i64])?; + let dmabuf = buffer + .export() + .context("error exporting GBM buffer object as dmabuf")?; + Ok(dmabuf) +} |
