Skip to main content

smithay_client_toolkit/
dmabuf.rs

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