⚠️ VeridianOS Kernel Documentation - This is low-level kernel code. All functions are unsafe unless explicitly marked otherwise. no_std

veridian_kernel/desktop/wayland/
dmabuf.rs

1//! DMA-BUF Protocol (zwp_linux_dmabuf_v1)
2//!
3//! Provides zero-copy buffer sharing between GPU and compositor.
4//!
5//! On VeridianOS, DMA-BUF handles are virtio-gpu resource IDs rather than
6//! Linux file descriptors. The protocol framework remains compatible with
7//! the Wayland zwp_linux_dmabuf_v1 specification, but the underlying
8//! buffer import mechanism uses virtio-gpu resource handles instead of
9//! dmabuf fds. This allows GPU-rendered content to be composited without
10//! copying pixel data through the CPU.
11//!
12//! ## Buffer lifecycle
13//!
14//! 1. Client calls `create_params()` to get a params builder
15//! 2. Client calls `add_plane()` for each plane (most formats need 1)
16//! 3. Client calls `create_buffer()` to finalize and import the buffer
17//! 4. The compositor can then sample from the imported buffer directly
18
19#![allow(dead_code)]
20
21use alloc::{collections::BTreeMap, vec, vec::Vec};
22
23use crate::error::KernelError;
24
25// ---------------------------------------------------------------------------
26// Protocol constants
27// ---------------------------------------------------------------------------
28
29/// Wayland global interface name
30pub const ZWP_LINUX_DMABUF_V1: &str = "zwp_linux_dmabuf_v1";
31
32/// Protocol version
33pub const ZWP_LINUX_DMABUF_V1_VERSION: u32 = 4;
34
35// zwp_linux_dmabuf_v1 request opcodes
36/// destroy
37pub const ZWP_LINUX_DMABUF_V1_DESTROY: u16 = 0;
38/// create_params(id: new_id) -> zwp_linux_buffer_params_v1
39pub const ZWP_LINUX_DMABUF_V1_CREATE_PARAMS: u16 = 1;
40/// get_default_feedback(id: new_id) -- since version 4
41pub const ZWP_LINUX_DMABUF_V1_GET_DEFAULT_FEEDBACK: u16 = 2;
42/// get_surface_feedback(id: new_id, surface: object) -- since version 4
43pub const ZWP_LINUX_DMABUF_V1_GET_SURFACE_FEEDBACK: u16 = 3;
44
45// zwp_linux_dmabuf_v1 event opcodes
46/// format(format: uint) -- deprecated since version 4
47pub const ZWP_LINUX_DMABUF_V1_FORMAT: u16 = 0;
48/// modifier(format: uint, modifier_hi: uint, modifier_lo: uint) -- since
49/// version 3
50pub const ZWP_LINUX_DMABUF_V1_MODIFIER: u16 = 1;
51
52// zwp_linux_buffer_params_v1 request opcodes
53/// destroy
54pub const ZWP_LINUX_BUFFER_PARAMS_V1_DESTROY: u16 = 0;
55/// add(fd, plane_idx, offset, stride, modifier_hi, modifier_lo)
56pub const ZWP_LINUX_BUFFER_PARAMS_V1_ADD: u16 = 1;
57/// create(width, height, format, flags)
58pub const ZWP_LINUX_BUFFER_PARAMS_V1_CREATE: u16 = 2;
59/// create_immed(buffer_id, width, height, format, flags) -- since version 2
60pub const ZWP_LINUX_BUFFER_PARAMS_V1_CREATE_IMMED: u16 = 3;
61
62// zwp_linux_buffer_params_v1 event opcodes
63/// created(buffer: new_id)
64pub const ZWP_LINUX_BUFFER_PARAMS_V1_CREATED: u16 = 0;
65/// failed
66pub const ZWP_LINUX_BUFFER_PARAMS_V1_FAILED: u16 = 1;
67
68// Buffer params flags
69/// Bottom-first (y-inverted) buffer
70pub const ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT: u32 = 1;
71/// Buffer content is interlaced
72pub const ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_INTERLACED: u32 = 2;
73/// Buffer content has bottom field first
74pub const ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_BOTTOM_FIRST: u32 = 4;
75
76/// Maximum number of planes per buffer
77pub const MAX_PLANES: usize = 4;
78
79// ---------------------------------------------------------------------------
80// DRM fourcc format codes
81// ---------------------------------------------------------------------------
82
83/// Common DRM fourcc pixel format codes.
84///
85/// These are the standard format identifiers used by the Linux DRM
86/// subsystem and the Wayland DMA-BUF protocol.
87pub mod fourcc {
88    /// 32-bit ARGB (8:8:8:8) -- the most common compositing format
89    pub const DRM_FORMAT_ARGB8888: u32 = 0x34325241; // AR24
90    /// 32-bit XRGB (8:8:8:8) -- opaque RGB with unused alpha byte
91    pub const DRM_FORMAT_XRGB8888: u32 = 0x34325258; // XR24
92    /// 32-bit ABGR (8:8:8:8) -- ARGB with reversed channel order
93    pub const DRM_FORMAT_ABGR8888: u32 = 0x34324241; // AB24
94    /// 32-bit XBGR (8:8:8:8) -- XRGB with reversed channel order
95    pub const DRM_FORMAT_XBGR8888: u32 = 0x34324258; // XB24
96    /// 24-bit RGB (8:8:8) -- packed, no padding
97    pub const DRM_FORMAT_RGB888: u32 = 0x34324752; // RG24
98    /// 24-bit BGR (8:8:8) -- packed, no padding
99    pub const DRM_FORMAT_BGR888: u32 = 0x34324742; // BG24
100    /// NV12 semi-planar YUV 4:2:0 -- common video format
101    pub const DRM_FORMAT_NV12: u32 = 0x3231564E; // NV12
102    /// YUYV packed YUV 4:2:2 -- common camera/video format
103    pub const DRM_FORMAT_YUYV: u32 = 0x56595559; // YUYV
104    /// 16-bit RGB 5:6:5 -- legacy embedded/mobile format
105    pub const DRM_FORMAT_RGB565: u32 = 0x36314752; // RG16
106}
107
108// ---------------------------------------------------------------------------
109// DRM format modifier constants
110// ---------------------------------------------------------------------------
111
112/// DRM format modifiers describe GPU-specific memory tiling layouts.
113pub mod modifiers {
114    /// Invalid modifier (unspecified layout)
115    pub const DRM_FORMAT_MOD_INVALID: u64 = 0x00FF_FFFF_FFFF_FFFF;
116    /// Linear (row-major, no tiling) -- universally supported
117    pub const DRM_FORMAT_MOD_LINEAR: u64 = 0;
118    /// Intel X-tiling (legacy)
119    pub const I915_FORMAT_MOD_X_TILED: u64 = (1u64 << 56) | 1;
120    /// Intel Y-tiling
121    pub const I915_FORMAT_MOD_Y_TILED: u64 = (1u64 << 56) | 2;
122    /// Intel Tile4 (Xe/DG2+)
123    pub const I915_FORMAT_MOD_4_TILED: u64 = (1u64 << 56) | 9;
124}
125
126// ---------------------------------------------------------------------------
127// Types
128// ---------------------------------------------------------------------------
129
130/// DMA-BUF format descriptor (fourcc + modifier pair).
131///
132/// Each supported format is advertised as a (fourcc, modifier) combination.
133/// The modifier describes the GPU-specific memory layout (e.g., linear,
134/// tiled). A single fourcc may appear with multiple modifiers.
135#[derive(Debug, Clone, Copy, PartialEq, Eq)]
136pub struct DmaBufFormat {
137    /// DRM fourcc format code
138    pub fourcc: u32,
139    /// Upper 32 bits of the 64-bit modifier
140    pub modifier_hi: u32,
141    /// Lower 32 bits of the 64-bit modifier
142    pub modifier_lo: u32,
143}
144
145impl DmaBufFormat {
146    /// Create a new format descriptor.
147    pub fn new(fourcc: u32, modifier: u64) -> Self {
148        Self {
149            fourcc,
150            modifier_hi: (modifier >> 32) as u32,
151            modifier_lo: modifier as u32,
152        }
153    }
154
155    /// Reconstruct the full 64-bit modifier.
156    pub fn modifier(&self) -> u64 {
157        ((self.modifier_hi as u64) << 32) | (self.modifier_lo as u64)
158    }
159
160    /// Check whether this format uses the linear modifier.
161    pub fn is_linear(&self) -> bool {
162        self.modifier() == modifiers::DRM_FORMAT_MOD_LINEAR
163    }
164}
165
166/// DMA-BUF parameter builder (corresponds to zwp_linux_buffer_params_v1).
167///
168/// Accumulates plane descriptions before buffer creation.
169#[derive(Debug, Clone)]
170pub struct DmaBufParams {
171    /// Params object ID
172    pub id: u32,
173    /// Requested buffer width in pixels
174    pub width: u32,
175    /// Requested buffer height in pixels
176    pub height: u32,
177    /// DRM fourcc format code
178    pub format: u32,
179    /// Buffer creation flags (Y_INVERT, INTERLACED, BOTTOM_FIRST)
180    pub flags: u32,
181    /// Planes added so far (up to MAX_PLANES)
182    pub planes: Vec<DmaBufPlane>,
183}
184
185impl DmaBufParams {
186    /// Create empty params for accumulating planes.
187    pub fn new(id: u32) -> Self {
188        Self {
189            id,
190            width: 0,
191            height: 0,
192            format: 0,
193            flags: 0,
194            planes: Vec::new(),
195        }
196    }
197}
198
199/// Single plane of a DMA-BUF.
200///
201/// Multi-planar formats (e.g., NV12) have one plane per component.
202/// Most RGB formats use a single plane.
203#[derive(Debug, Clone, Copy)]
204pub struct DmaBufPlane {
205    /// Virtio-GPU resource ID (VeridianOS DMA-BUF handle).
206    ///
207    /// On Linux this would be a file descriptor; on VeridianOS we use
208    /// the virtio-gpu resource ID directly.
209    pub resource_id: u32,
210    /// Byte offset into the resource where this plane starts
211    pub offset: u32,
212    /// Byte stride (distance between rows) for this plane
213    pub stride: u32,
214    /// Upper 32 bits of the 64-bit format modifier
215    pub modifier_hi: u32,
216    /// Lower 32 bits of the 64-bit format modifier
217    pub modifier_lo: u32,
218}
219
220impl DmaBufPlane {
221    /// Reconstruct the full 64-bit modifier for this plane.
222    pub fn modifier(&self) -> u64 {
223        ((self.modifier_hi as u64) << 32) | (self.modifier_lo as u64)
224    }
225}
226
227/// Imported DMA-BUF buffer ready for compositor use.
228///
229/// Created from validated DmaBufParams. The compositor references this
230/// buffer by its `id` when compositing.
231pub struct DmaBufBuffer {
232    /// Buffer ID within the DMA-BUF manager
233    pub id: u32,
234    /// The validated params that created this buffer
235    pub params: DmaBufParams,
236    /// Associated wl_buffer object ID (for Wayland object mapping)
237    pub wl_buffer_id: u32,
238}
239
240// ---------------------------------------------------------------------------
241// DMA-BUF manager
242// ---------------------------------------------------------------------------
243
244/// DMA-BUF manager.
245///
246/// Tracks supported formats, in-progress params builders, and imported
247/// buffers. Provides the server-side implementation of zwp_linux_dmabuf_v1.
248pub struct DmaBufManager {
249    /// Supported format + modifier combinations advertised to clients
250    supported_formats: Vec<DmaBufFormat>,
251    /// In-progress params builders keyed by params object ID
252    params: BTreeMap<u32, DmaBufParams>,
253    /// Imported buffers keyed by buffer ID
254    buffers: BTreeMap<u32, DmaBufBuffer>,
255    /// Next params object ID
256    next_params_id: u32,
257    /// Next buffer ID
258    next_buffer_id: u32,
259}
260
261impl DmaBufManager {
262    /// Create a new DMA-BUF manager with default supported formats.
263    ///
264    /// By default, supports ARGB8888 and XRGB8888 with the LINEAR modifier.
265    /// These are universally supported by software renderers and virtio-gpu.
266    pub fn new() -> Self {
267        let supported_formats = vec![
268            DmaBufFormat::new(
269                fourcc::DRM_FORMAT_ARGB8888,
270                modifiers::DRM_FORMAT_MOD_LINEAR,
271            ),
272            DmaBufFormat::new(
273                fourcc::DRM_FORMAT_XRGB8888,
274                modifiers::DRM_FORMAT_MOD_LINEAR,
275            ),
276        ];
277
278        Self {
279            supported_formats,
280            params: BTreeMap::new(),
281            buffers: BTreeMap::new(),
282            next_params_id: 1,
283            next_buffer_id: 1,
284        }
285    }
286
287    /// Get the list of supported format + modifier combinations.
288    pub fn get_supported_formats(&self) -> &[DmaBufFormat] {
289        &self.supported_formats
290    }
291
292    /// Add a supported format + modifier combination.
293    pub fn add_supported_format(&mut self, format: DmaBufFormat) {
294        if !self.supported_formats.contains(&format) {
295            self.supported_formats.push(format);
296        }
297    }
298
299    /// Check whether a specific fourcc + modifier combination is supported.
300    pub fn is_format_supported(&self, fourcc: u32, modifier: u64) -> bool {
301        self.supported_formats
302            .iter()
303            .any(|f| f.fourcc == fourcc && f.modifier() == modifier)
304    }
305
306    /// Create a new params builder. Returns the params object ID.
307    ///
308    /// The client should subsequently call `add_plane()` for each buffer
309    /// plane, then `create_buffer()` to finalize.
310    pub fn create_params(&mut self) -> u32 {
311        let id = self.next_params_id;
312        self.next_params_id += 1;
313
314        self.params.insert(id, DmaBufParams::new(id));
315        id
316    }
317
318    /// Add a plane to an in-progress params builder.
319    pub fn add_plane(&mut self, params_id: u32, plane: DmaBufPlane) -> Result<(), KernelError> {
320        let params = self
321            .params
322            .get_mut(&params_id)
323            .ok_or(KernelError::NotFound {
324                resource: "dmabuf_params",
325                id: params_id as u64,
326            })?;
327
328        if params.planes.len() >= MAX_PLANES {
329            return Err(KernelError::InvalidArgument {
330                name: "plane_count",
331                value: "exceeds_max_planes",
332            });
333        }
334
335        params.planes.push(plane);
336        Ok(())
337    }
338
339    /// Finalize a params builder and create an imported DMA-BUF buffer.
340    ///
341    /// Validates that at least one plane was added and that the format is
342    /// supported. Returns the buffer ID on success.
343    pub fn create_buffer(
344        &mut self,
345        params_id: u32,
346        width: u32,
347        height: u32,
348        format: u32,
349        flags: u32,
350    ) -> Result<u32, KernelError> {
351        let mut params = self
352            .params
353            .remove(&params_id)
354            .ok_or(KernelError::NotFound {
355                resource: "dmabuf_params",
356                id: params_id as u64,
357            })?;
358
359        // Validate: at least one plane required
360        if params.planes.is_empty() {
361            return Err(KernelError::InvalidArgument {
362                name: "planes",
363                value: "no_planes_added",
364            });
365        }
366
367        // Validate: dimensions must be non-zero
368        if width == 0 || height == 0 {
369            return Err(KernelError::InvalidArgument {
370                name: "dimensions",
371                value: "zero_width_or_height",
372            });
373        }
374
375        // Validate: all planes must use the same modifier
376        let first_modifier = params.planes[0].modifier();
377        for plane in &params.planes[1..] {
378            if plane.modifier() != first_modifier {
379                return Err(KernelError::InvalidArgument {
380                    name: "modifier",
381                    value: "planes_have_different_modifiers",
382                });
383            }
384        }
385
386        // Validate: format + modifier combination must be supported
387        if !self.is_format_supported(format, first_modifier) {
388            return Err(KernelError::InvalidArgument {
389                name: "format",
390                value: "unsupported_format_modifier",
391            });
392        }
393
394        // Finalize the params
395        params.width = width;
396        params.height = height;
397        params.format = format;
398        params.flags = flags;
399
400        // Create the imported buffer
401        let buffer_id = self.next_buffer_id;
402        self.next_buffer_id += 1;
403
404        let buffer = DmaBufBuffer {
405            id: buffer_id,
406            params,
407            wl_buffer_id: 0, // Set when bound to a wl_buffer object
408        };
409
410        self.buffers.insert(buffer_id, buffer);
411        Ok(buffer_id)
412    }
413
414    /// Destroy an imported buffer.
415    pub fn destroy_buffer(&mut self, buffer_id: u32) -> Result<(), KernelError> {
416        self.buffers
417            .remove(&buffer_id)
418            .ok_or(KernelError::NotFound {
419                resource: "dmabuf_buffer",
420                id: buffer_id as u64,
421            })?;
422        Ok(())
423    }
424
425    /// Convenience wrapper: import a single-plane buffer from a virtio-gpu
426    /// resource with the LINEAR modifier.
427    ///
428    /// This is the common case for software-rendered content shared via
429    /// virtio-gpu. The stride is computed as `width * bpp`.
430    pub fn import_from_virtio_gpu(
431        &mut self,
432        resource_id: u32,
433        width: u32,
434        height: u32,
435        format: u32,
436    ) -> Result<u32, KernelError> {
437        let bpp: u32 = match format {
438            fourcc::DRM_FORMAT_ARGB8888
439            | fourcc::DRM_FORMAT_XRGB8888
440            | fourcc::DRM_FORMAT_ABGR8888
441            | fourcc::DRM_FORMAT_XBGR8888 => 4,
442            fourcc::DRM_FORMAT_RGB888 | fourcc::DRM_FORMAT_BGR888 => 3,
443            fourcc::DRM_FORMAT_RGB565 => 2,
444            _ => {
445                return Err(KernelError::InvalidArgument {
446                    name: "format",
447                    value: "unknown_format_bpp",
448                });
449            }
450        };
451
452        let stride = width * bpp;
453        let params_id = self.create_params();
454
455        let plane = DmaBufPlane {
456            resource_id,
457            offset: 0,
458            stride,
459            modifier_hi: 0,
460            modifier_lo: 0, // LINEAR
461        };
462
463        self.add_plane(params_id, plane)?;
464        self.create_buffer(params_id, width, height, format, 0)
465    }
466
467    /// Get a reference to an imported buffer.
468    pub fn get_buffer(&self, buffer_id: u32) -> Option<&DmaBufBuffer> {
469        self.buffers.get(&buffer_id)
470    }
471
472    /// Get a mutable reference to an imported buffer.
473    pub fn get_buffer_mut(&mut self, buffer_id: u32) -> Option<&mut DmaBufBuffer> {
474        self.buffers.get_mut(&buffer_id)
475    }
476
477    /// Cancel an in-progress params builder without creating a buffer.
478    pub fn destroy_params(&mut self, params_id: u32) -> Result<(), KernelError> {
479        self.params
480            .remove(&params_id)
481            .ok_or(KernelError::NotFound {
482                resource: "dmabuf_params",
483                id: params_id as u64,
484            })?;
485        Ok(())
486    }
487
488    /// Number of imported buffers.
489    pub fn buffer_count(&self) -> usize {
490        self.buffers.len()
491    }
492
493    /// Number of in-progress params builders.
494    pub fn params_count(&self) -> usize {
495        self.params.len()
496    }
497}
498
499impl Default for DmaBufManager {
500    fn default() -> Self {
501        Self::new()
502    }
503}
504
505// ---------------------------------------------------------------------------
506// Tests
507// ---------------------------------------------------------------------------
508
509#[cfg(test)]
510mod tests {
511    use super::*;
512
513    #[test]
514    fn test_default_formats() {
515        let mgr = DmaBufManager::new();
516        let formats = mgr.get_supported_formats();
517        assert_eq!(formats.len(), 2);
518        assert!(mgr.is_format_supported(
519            fourcc::DRM_FORMAT_ARGB8888,
520            modifiers::DRM_FORMAT_MOD_LINEAR
521        ));
522        assert!(mgr.is_format_supported(
523            fourcc::DRM_FORMAT_XRGB8888,
524            modifiers::DRM_FORMAT_MOD_LINEAR
525        ));
526        assert!(!mgr.is_format_supported(fourcc::DRM_FORMAT_NV12, modifiers::DRM_FORMAT_MOD_LINEAR));
527    }
528
529    #[test]
530    fn test_create_buffer_single_plane() {
531        let mut mgr = DmaBufManager::new();
532        let params_id = mgr.create_params();
533
534        let plane = DmaBufPlane {
535            resource_id: 42,
536            offset: 0,
537            stride: 1280 * 4,
538            modifier_hi: 0,
539            modifier_lo: 0,
540        };
541
542        mgr.add_plane(params_id, plane).unwrap();
543        let buf_id = mgr
544            .create_buffer(params_id, 1280, 720, fourcc::DRM_FORMAT_ARGB8888, 0)
545            .unwrap();
546
547        let buf = mgr.get_buffer(buf_id).unwrap();
548        assert_eq!(buf.params.width, 1280);
549        assert_eq!(buf.params.height, 720);
550        assert_eq!(buf.params.planes.len(), 1);
551        assert_eq!(buf.params.planes[0].resource_id, 42);
552    }
553
554    #[test]
555    fn test_create_buffer_no_planes_fails() {
556        let mut mgr = DmaBufManager::new();
557        let params_id = mgr.create_params();
558
559        let result = mgr.create_buffer(params_id, 100, 100, fourcc::DRM_FORMAT_ARGB8888, 0);
560        assert!(result.is_err());
561    }
562
563    #[test]
564    fn test_create_buffer_zero_dimensions_fails() {
565        let mut mgr = DmaBufManager::new();
566        let params_id = mgr.create_params();
567
568        let plane = DmaBufPlane {
569            resource_id: 1,
570            offset: 0,
571            stride: 0,
572            modifier_hi: 0,
573            modifier_lo: 0,
574        };
575        mgr.add_plane(params_id, plane).unwrap();
576
577        let result = mgr.create_buffer(params_id, 0, 100, fourcc::DRM_FORMAT_ARGB8888, 0);
578        assert!(result.is_err());
579    }
580
581    #[test]
582    fn test_create_buffer_unsupported_format_fails() {
583        let mut mgr = DmaBufManager::new();
584        let params_id = mgr.create_params();
585
586        let plane = DmaBufPlane {
587            resource_id: 1,
588            offset: 0,
589            stride: 100,
590            modifier_hi: 0,
591            modifier_lo: 0,
592        };
593        mgr.add_plane(params_id, plane).unwrap();
594
595        // NV12 not in default supported formats
596        let result = mgr.create_buffer(params_id, 100, 100, fourcc::DRM_FORMAT_NV12, 0);
597        assert!(result.is_err());
598    }
599
600    #[test]
601    fn test_mismatched_modifiers_fails() {
602        let mut mgr = DmaBufManager::new();
603        let params_id = mgr.create_params();
604
605        let plane1 = DmaBufPlane {
606            resource_id: 1,
607            offset: 0,
608            stride: 100,
609            modifier_hi: 0,
610            modifier_lo: 0, // LINEAR
611        };
612        let plane2 = DmaBufPlane {
613            resource_id: 2,
614            offset: 0,
615            stride: 50,
616            modifier_hi: 1,
617            modifier_lo: 1, // different
618        };
619
620        mgr.add_plane(params_id, plane1).unwrap();
621        mgr.add_plane(params_id, plane2).unwrap();
622
623        let result = mgr.create_buffer(params_id, 100, 100, fourcc::DRM_FORMAT_ARGB8888, 0);
624        assert!(result.is_err());
625    }
626
627    #[test]
628    fn test_too_many_planes_fails() {
629        let mut mgr = DmaBufManager::new();
630        let params_id = mgr.create_params();
631
632        for i in 0..MAX_PLANES {
633            let plane = DmaBufPlane {
634                resource_id: i as u32,
635                offset: 0,
636                stride: 100,
637                modifier_hi: 0,
638                modifier_lo: 0,
639            };
640            mgr.add_plane(params_id, plane).unwrap();
641        }
642
643        // One more should fail
644        let extra_plane = DmaBufPlane {
645            resource_id: 99,
646            offset: 0,
647            stride: 100,
648            modifier_hi: 0,
649            modifier_lo: 0,
650        };
651        assert!(mgr.add_plane(params_id, extra_plane).is_err());
652    }
653
654    #[test]
655    fn test_destroy_buffer() {
656        let mut mgr = DmaBufManager::new();
657        let params_id = mgr.create_params();
658        let plane = DmaBufPlane {
659            resource_id: 1,
660            offset: 0,
661            stride: 400,
662            modifier_hi: 0,
663            modifier_lo: 0,
664        };
665        mgr.add_plane(params_id, plane).unwrap();
666        let buf_id = mgr
667            .create_buffer(params_id, 100, 100, fourcc::DRM_FORMAT_ARGB8888, 0)
668            .unwrap();
669
670        assert_eq!(mgr.buffer_count(), 1);
671        mgr.destroy_buffer(buf_id).unwrap();
672        assert_eq!(mgr.buffer_count(), 0);
673    }
674
675    #[test]
676    fn test_destroy_nonexistent_buffer_fails() {
677        let mut mgr = DmaBufManager::new();
678        assert!(mgr.destroy_buffer(999).is_err());
679    }
680
681    #[test]
682    fn test_import_from_virtio_gpu() {
683        let mut mgr = DmaBufManager::new();
684        let buf_id = mgr
685            .import_from_virtio_gpu(7, 640, 480, fourcc::DRM_FORMAT_ARGB8888)
686            .unwrap();
687
688        let buf = mgr.get_buffer(buf_id).unwrap();
689        assert_eq!(buf.params.width, 640);
690        assert_eq!(buf.params.height, 480);
691        assert_eq!(buf.params.format, fourcc::DRM_FORMAT_ARGB8888);
692        assert_eq!(buf.params.planes.len(), 1);
693        assert_eq!(buf.params.planes[0].resource_id, 7);
694        assert_eq!(buf.params.planes[0].stride, 640 * 4);
695    }
696
697    #[test]
698    fn test_import_unknown_format_fails() {
699        let mut mgr = DmaBufManager::new();
700        let result = mgr.import_from_virtio_gpu(1, 100, 100, 0xDEADBEEF);
701        assert!(result.is_err());
702    }
703
704    #[test]
705    fn test_destroy_params() {
706        let mut mgr = DmaBufManager::new();
707        let params_id = mgr.create_params();
708        assert_eq!(mgr.params_count(), 1);
709        mgr.destroy_params(params_id).unwrap();
710        assert_eq!(mgr.params_count(), 0);
711    }
712
713    #[test]
714    fn test_add_supported_format() {
715        let mut mgr = DmaBufManager::new();
716        assert_eq!(mgr.get_supported_formats().len(), 2);
717
718        mgr.add_supported_format(DmaBufFormat::new(
719            fourcc::DRM_FORMAT_NV12,
720            modifiers::DRM_FORMAT_MOD_LINEAR,
721        ));
722        assert_eq!(mgr.get_supported_formats().len(), 3);
723
724        // Adding duplicate should not increase count
725        mgr.add_supported_format(DmaBufFormat::new(
726            fourcc::DRM_FORMAT_NV12,
727            modifiers::DRM_FORMAT_MOD_LINEAR,
728        ));
729        assert_eq!(mgr.get_supported_formats().len(), 3);
730    }
731
732    #[test]
733    fn test_format_is_linear() {
734        let linear = DmaBufFormat::new(
735            fourcc::DRM_FORMAT_ARGB8888,
736            modifiers::DRM_FORMAT_MOD_LINEAR,
737        );
738        assert!(linear.is_linear());
739
740        let tiled = DmaBufFormat::new(
741            fourcc::DRM_FORMAT_ARGB8888,
742            modifiers::I915_FORMAT_MOD_X_TILED,
743        );
744        assert!(!tiled.is_linear());
745    }
746
747    #[test]
748    fn test_format_modifier_roundtrip() {
749        let f = DmaBufFormat::new(
750            fourcc::DRM_FORMAT_ARGB8888,
751            modifiers::I915_FORMAT_MOD_Y_TILED,
752        );
753        assert_eq!(f.modifier(), modifiers::I915_FORMAT_MOD_Y_TILED);
754    }
755
756    #[test]
757    fn test_plane_modifier_roundtrip() {
758        let p = DmaBufPlane {
759            resource_id: 0,
760            offset: 0,
761            stride: 0,
762            modifier_hi: 0x0001_0000,
763            modifier_lo: 0x0000_0002,
764        };
765        assert_eq!(p.modifier(), 0x0001_0000_0000_0002);
766    }
767}