aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/niri.rs8
-rw-r--r--src/pw_utils.rs543
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)
+}