smithay_client_toolkit/
dmabuf.rs

1use crate::{error::GlobalError, globals::GlobalData, registry::GlobalProxy};
2use memmap2::{Mmap, MmapOptions};
3use std::{fmt, mem, os::unix::io::BorrowedFd, slice, sync::Mutex};
4use wayland_client::{
5    globals::GlobalList,
6    protocol::{wl_buffer, wl_surface},
7    Connection, Dispatch, Proxy, QueueHandle, WEnum,
8};
9use wayland_protocols::wp::linux_dmabuf::zv1::client::{
10    zwp_linux_buffer_params_v1,
11    zwp_linux_dmabuf_feedback_v1::{self, TrancheFlags},
12    zwp_linux_dmabuf_v1,
13};
14
15// Workaround until `libc` updates to FreeBSD 12 ABI
16#[cfg(target_os = "freebsd")]
17type dev_t = u64;
18#[cfg(not(target_os = "freebsd"))]
19use libc::dev_t;
20
21/// A preference tranche of dmabuf formats
22#[derive(Clone, Debug)]
23pub struct DmabufFeedbackTranche {
24    /// `dev_t` value for preferred target device. May be scan-out or
25    /// renderer device.
26    pub device: dev_t,
27    /// Flags for tranche
28    pub flags: WEnum<TrancheFlags>,
29    /// Indices of formats in the format table
30    pub formats: Vec<u16>,
31}
32
33impl Default for DmabufFeedbackTranche {
34    fn default() -> DmabufFeedbackTranche {
35        DmabufFeedbackTranche {
36            device: 0,
37            flags: WEnum::Value(TrancheFlags::empty()),
38            formats: Vec::new(),
39        }
40    }
41}
42
43/// A single dmabuf format/modifier pair
44// Must have correct representation to be able to mmap format table
45#[repr(C)]
46#[derive(Copy, Clone)]
47pub struct DmabufFormat {
48    /// Fourcc format
49    pub format: u32,
50    _padding: u32,
51    /// Modifier, or `DRM_FORMAT_MOD_INVALID` for implict modifier
52    pub modifier: u64,
53}
54
55impl fmt::Debug for DmabufFormat {
56    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57        f.debug_struct("DmabufFormat")
58            .field("format", &self.format)
59            .field("modifier", &self.modifier)
60            .finish()
61    }
62}
63
64/// Description of supported and preferred dmabuf formats
65#[derive(Default)]
66pub struct DmabufFeedback {
67    format_table: Option<(Mmap, usize)>,
68    main_device: dev_t,
69    tranches: Vec<DmabufFeedbackTranche>,
70}
71
72impl fmt::Debug for DmabufFeedback {
73    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74        f.debug_struct("DmabufFeedback")
75            .field("format_table", &self.format_table())
76            .field("main_device", &self.main_device)
77            .field("tranches", &self.tranches)
78            .finish()
79    }
80}
81
82impl DmabufFeedback {
83    /// Format/modifier pairs
84    pub fn format_table(&self) -> &[DmabufFormat] {
85        self.format_table.as_ref().map_or(&[], |(mmap, len)| unsafe {
86            slice::from_raw_parts(mmap.as_ptr() as *const DmabufFormat, *len)
87        })
88    }
89
90    /// `dev_t` value for main device. Buffers must be importable from main device.
91    pub fn main_device(&self) -> dev_t {
92        self.main_device
93    }
94
95    /// Tranches in descending order of preference
96    pub fn tranches(&self) -> &[DmabufFeedbackTranche] {
97        &self.tranches
98    }
99}
100
101#[doc(hidden)]
102#[derive(Debug, Default)]
103pub struct DmabufFeedbackData {
104    pending: Mutex<DmabufFeedback>,
105    pending_tranche: Mutex<DmabufFeedbackTranche>,
106}
107
108#[doc(hidden)]
109#[derive(Debug)]
110pub struct DmaBufferData;
111
112/// A handler for [`zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1`]
113#[derive(Debug)]
114pub struct DmabufState {
115    zwp_linux_dmabuf: GlobalProxy<zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1>,
116    modifiers: Vec<DmabufFormat>,
117}
118
119impl DmabufState {
120    /// Bind `zwp_linux_dmabuf_v1` global version 3 or 4, if it exists.
121    ///
122    /// This does not fail if the global does not exist.
123    pub fn new<D>(globals: &GlobalList, qh: &QueueHandle<D>) -> Self
124    where
125        D: Dispatch<zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1, GlobalData> + 'static,
126    {
127        // Mesa (at least the latest version) also requires version 3 or 4
128        let zwp_linux_dmabuf = GlobalProxy::from(globals.bind(qh, 3..=5, GlobalData));
129        Self { zwp_linux_dmabuf, modifiers: Vec::new() }
130    }
131
132    /// Only populated in version `<4`
133    ///
134    /// On version `4`, use [`DmabufState::get_surface_feedback`].
135    pub fn modifiers(&self) -> &[DmabufFormat] {
136        &self.modifiers
137    }
138
139    /// Supported protocol version, if any
140    pub fn version(&self) -> Option<u32> {
141        Some(self.zwp_linux_dmabuf.get().ok()?.version())
142    }
143
144    /// Create a params object for constructing a buffer
145    ///
146    /// Errors if `zwp_linux_dmabuf_v1` does not exist or has unsupported
147    /// version. An application can then fallback to using `shm` buffers.
148    pub fn create_params<D>(&self, qh: &QueueHandle<D>) -> Result<DmabufParams, GlobalError>
149    where
150        D: Dispatch<zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1, GlobalData> + 'static,
151    {
152        let zwp_linux_dmabuf = self.zwp_linux_dmabuf.get()?;
153        let params = zwp_linux_dmabuf.create_params(qh, GlobalData);
154        Ok(DmabufParams { params })
155    }
156
157    /// Get default dmabuf feedback. Requires version `4`.
158    ///
159    /// On version `3`, use [`DmabufState::modifiers`].
160    pub fn get_default_feedback<D>(
161        &self,
162        qh: &QueueHandle<D>,
163    ) -> Result<zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1, GlobalError>
164    where
165        D: Dispatch<zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1, DmabufFeedbackData>
166            + 'static,
167    {
168        let zwp_linux_dmabuf = self.zwp_linux_dmabuf.with_min_version(4)?;
169        Ok(zwp_linux_dmabuf.get_default_feedback(qh, DmabufFeedbackData::default()))
170    }
171
172    /// Get default dmabuf feedback for given surface. Requires version `4`.
173    ///
174    /// On version `3`, use [`DmabufState::modifiers`].
175    pub fn get_surface_feedback<D>(
176        &self,
177        surface: &wl_surface::WlSurface,
178        qh: &QueueHandle<D>,
179    ) -> Result<zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1, GlobalError>
180    where
181        D: Dispatch<zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1, DmabufFeedbackData>
182            + 'static,
183    {
184        let zwp_linux_dmabuf = self.zwp_linux_dmabuf.with_min_version(4)?;
185        Ok(zwp_linux_dmabuf.get_surface_feedback(surface, qh, DmabufFeedbackData::default()))
186    }
187}
188
189pub trait DmabufHandler: Sized {
190    fn dmabuf_state(&mut self) -> &mut DmabufState;
191
192    /// Server has sent dmabuf feedback information. This may be received multiple
193    /// times by a `ZwpLinuxDmabufFeedbackV1` object.
194    fn dmabuf_feedback(
195        &mut self,
196        conn: &Connection,
197        qh: &QueueHandle<Self>,
198        proxy: &zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1,
199        feedback: DmabufFeedback,
200    );
201
202    /// `wl_buffer` associated with `params` has been created successfully.
203    fn created(
204        &mut self,
205        conn: &Connection,
206        qh: &QueueHandle<Self>,
207        params: &zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1,
208        buffer: wl_buffer::WlBuffer,
209    );
210
211    /// Failed to create `wl_buffer` for `params`.
212    fn failed(
213        &mut self,
214        conn: &Connection,
215        qh: &QueueHandle<Self>,
216        params: &zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1,
217    );
218
219    /// Compositor has released a `wl_buffer` created through [`DmabufParams`].
220    fn released(&mut self, conn: &Connection, qh: &QueueHandle<Self>, buffer: &wl_buffer::WlBuffer);
221}
222
223/// Builder for a dmabuf backed buffer
224#[derive(Debug)]
225pub struct DmabufParams {
226    params: zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1,
227}
228
229impl DmabufParams {
230    /// Add a plane
231    ///
232    /// In version `4`, it is a protocol error if `format`/`modifier` pair wasn't
233    /// advertised as supported.
234    ///
235    /// `modifier` should be the same for all planes. It is a protocol error in version `5` if
236    /// they differ.
237    pub fn add(&self, fd: BorrowedFd<'_>, plane_idx: u32, offset: u32, stride: u32, modifier: u64) {
238        let modifier_hi = (modifier >> 32) as u32;
239        let modifier_lo = (modifier & 0xffffffff) as u32;
240        self.params.add(fd, plane_idx, offset, stride, modifier_hi, modifier_lo);
241    }
242
243    /// Create buffer.
244    ///
245    /// [`DmabufHandler::created`] or [`DmabufHandler::failed`] will be invoked when the
246    /// operation succeeds or fails.
247    pub fn create(
248        self,
249        width: i32,
250        height: i32,
251        format: u32,
252        flags: zwp_linux_buffer_params_v1::Flags,
253    ) -> zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1 {
254        self.params.create(width, height, format, flags);
255        self.params
256    }
257
258    /// Create buffer immediately.
259    ///
260    /// On failure buffer is invalid, and server may raise protocol error or
261    /// send [`DmabufHandler::failed`].
262    pub fn create_immed<D>(
263        self,
264        width: i32,
265        height: i32,
266        format: u32,
267        flags: zwp_linux_buffer_params_v1::Flags,
268        qh: &QueueHandle<D>,
269    ) -> (wl_buffer::WlBuffer, zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1)
270    where
271        D: Dispatch<wl_buffer::WlBuffer, DmaBufferData> + 'static,
272    {
273        let buffer = self.params.create_immed(width, height, format, flags, qh, DmaBufferData);
274        (buffer, self.params)
275    }
276}
277
278impl<D> Dispatch<zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1, GlobalData, D> for DmabufState
279where
280    D: Dispatch<zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1, GlobalData> + DmabufHandler,
281{
282    fn event(
283        state: &mut D,
284        proxy: &zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1,
285        event: zwp_linux_dmabuf_v1::Event,
286        _: &GlobalData,
287        _: &Connection,
288        _: &QueueHandle<D>,
289    ) {
290        match event {
291            zwp_linux_dmabuf_v1::Event::Format { format: _ } => {
292                // Formats are duplicated by modifier events since version 3.
293                // Ignore this event, like Mesa does.
294            }
295            zwp_linux_dmabuf_v1::Event::Modifier { format, modifier_hi, modifier_lo } => {
296                if proxy.version() < 4 {
297                    let modifier = (u64::from(modifier_hi) << 32) | u64::from(modifier_lo);
298                    state.dmabuf_state().modifiers.push(DmabufFormat {
299                        format,
300                        _padding: 0,
301                        modifier,
302                    });
303                }
304            }
305            _ => unreachable!(),
306        }
307    }
308}
309
310impl<D> Dispatch<zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1, DmabufFeedbackData, D>
311    for DmabufState
312where
313    D: Dispatch<zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1, DmabufFeedbackData>
314        + DmabufHandler,
315{
316    fn event(
317        state: &mut D,
318        proxy: &zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1,
319        event: zwp_linux_dmabuf_feedback_v1::Event,
320        data: &DmabufFeedbackData,
321        conn: &Connection,
322        qh: &QueueHandle<D>,
323    ) {
324        match event {
325            zwp_linux_dmabuf_feedback_v1::Event::Done => {
326                let feedback = mem::take(&mut *data.pending.lock().unwrap());
327                state.dmabuf_feedback(conn, qh, proxy, feedback);
328            }
329            zwp_linux_dmabuf_feedback_v1::Event::FormatTable { fd, size } => {
330                let size = size as usize;
331                let mmap = unsafe {
332                    MmapOptions::new().map_copy_read_only(&fd).expect("Failed to map format table")
333                };
334                assert!(mmap.len() >= size);
335                let entry_size = mem::size_of::<DmabufFormat>();
336                assert!((size % entry_size) == 0);
337                let len = size / entry_size;
338                data.pending.lock().unwrap().format_table = Some((mmap, len));
339            }
340            zwp_linux_dmabuf_feedback_v1::Event::MainDevice { device } => {
341                let device = dev_t::from_ne_bytes(device.try_into().unwrap());
342                data.pending.lock().unwrap().main_device = device;
343            }
344            zwp_linux_dmabuf_feedback_v1::Event::TrancheDone => {
345                let tranche = mem::take(&mut *data.pending_tranche.lock().unwrap());
346                data.pending.lock().unwrap().tranches.push(tranche);
347            }
348            zwp_linux_dmabuf_feedback_v1::Event::TrancheTargetDevice { device } => {
349                let device = dev_t::from_ne_bytes(device.try_into().unwrap());
350                data.pending_tranche.lock().unwrap().device = device;
351            }
352            zwp_linux_dmabuf_feedback_v1::Event::TrancheFormats { indices } => {
353                assert!((indices.len() % 2) == 0);
354                let indices =
355                    indices.chunks(2).map(|i| u16::from_ne_bytes([i[0], i[1]])).collect::<Vec<_>>();
356                data.pending_tranche.lock().unwrap().formats = indices;
357            }
358            zwp_linux_dmabuf_feedback_v1::Event::TrancheFlags { flags } => {
359                data.pending_tranche.lock().unwrap().flags = flags;
360            }
361            _ => unreachable!(),
362        }
363    }
364}
365
366impl<D> Dispatch<zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1, GlobalData, D> for DmabufState
367where
368    D: Dispatch<zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1, GlobalData>
369        + Dispatch<wl_buffer::WlBuffer, DmaBufferData>
370        + DmabufHandler
371        + 'static,
372{
373    fn event(
374        state: &mut D,
375        proxy: &zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1,
376        event: zwp_linux_buffer_params_v1::Event,
377        _: &GlobalData,
378        conn: &Connection,
379        qh: &QueueHandle<D>,
380    ) {
381        match event {
382            zwp_linux_buffer_params_v1::Event::Created { buffer } => {
383                state.created(conn, qh, proxy, buffer);
384            }
385            zwp_linux_buffer_params_v1::Event::Failed => {
386                state.failed(conn, qh, proxy);
387            }
388            _ => unreachable!(),
389        }
390    }
391
392    wayland_client::event_created_child!(D, zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1, [
393        zwp_linux_buffer_params_v1::EVT_CREATED_OPCODE => (wl_buffer::WlBuffer, DmaBufferData)
394    ]);
395}
396
397impl<D> Dispatch<wl_buffer::WlBuffer, DmaBufferData, D> for DmabufState
398where
399    D: Dispatch<wl_buffer::WlBuffer, DmaBufferData> + DmabufHandler,
400{
401    fn event(
402        state: &mut D,
403        proxy: &wl_buffer::WlBuffer,
404        event: wl_buffer::Event,
405        _: &DmaBufferData,
406        conn: &Connection,
407        qh: &QueueHandle<D>,
408    ) {
409        match event {
410            wl_buffer::Event::Release => state.released(conn, qh, proxy),
411            _ => unreachable!(),
412        }
413    }
414}
415
416#[macro_export]
417macro_rules! delegate_dmabuf {
418    ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
419        $crate::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty:
420            [
421                $crate::reexports::protocols::wp::linux_dmabuf::zv1::client::zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1: $crate::globals::GlobalData
422            ] => $crate::dmabuf::DmabufState
423        );
424        $crate::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty:
425            [
426                $crate::reexports::protocols::wp::linux_dmabuf::zv1::client::zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1: $crate::globals::GlobalData
427            ] => $crate::dmabuf::DmabufState
428        );
429        $crate::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty:
430            [
431                $crate::reexports::protocols::wp::linux_dmabuf::zv1::client::zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1: $crate::dmabuf::DmabufFeedbackData
432            ] => $crate::dmabuf::DmabufState
433        );
434        $crate::reexports::client::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty:
435            [
436                $crate::reexports::client::protocol::wl_buffer::WlBuffer: $crate::dmabuf::DmaBufferData
437            ] => $crate::dmabuf::DmabufState
438        );
439    };
440}