Skip to main content

pixelflux/encoders/
dmabuf_identity.rs

1/*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
5 */
6
7//! What a dmabuf is, beyond the fd number that names it, for the hardware sessions that keep
8//! an import of each recurring capture buffer (NVENC's CUDA registrations, VA-API's surfaces).
9
10/// The stable identity of a dmabuf, so a cache keyed by the raw fd number cannot hand back a
11/// stale import after that fd was closed and recycled onto a different buffer.
12///
13/// The fd integer alone is not an identity: the host's slot renegotiation frees the backing buffer
14/// objects and the kernel reissues the same small fd numbers for the new ones. Since Linux 5.3 each
15/// dma-buf carries its own inode, so `(st_dev, st_ino)` distinguishes two buffers that reuse one fd
16/// number, and `size` reports the true allocation; the DRM format modifier and the geometry round
17/// out the identity for older kernels where the inode is shared. A cache hit requires every field to
18/// match, so a recycled fd whose buffer differs in any of them is re-imported instead of reused.
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub(crate) struct DmaBufIdentity {
21    dev: u64,
22    ino: u64,
23    size: i64,
24    modifier: u64,
25    width: u32,
26    height: u32,
27}
28
29impl DmaBufIdentity {
30    /// Read the identity of the buffer behind `fd`: `fstat` supplies the inode and allocation
31    /// size, and the caller supplies the modifier and geometry from the dmabuf descriptor. A failed
32    /// `fstat` leaves the inode/size zero, which still combines with the modifier and geometry.
33    pub(crate) fn probe(fd: i32, modifier: u64, width: u32, height: u32) -> Self {
34        let mut st: libc::stat = unsafe { std::mem::zeroed() };
35        let (dev, ino, size) = if unsafe { libc::fstat(fd, &mut st) } == 0 {
36            (st.st_dev as u64, st.st_ino as u64, st.st_size as i64)
37        } else {
38            (0, 0, 0)
39        };
40        Self {
41            dev,
42            ino,
43            size,
44            modifier,
45            width,
46            height,
47        }
48    }
49}
50
51#[cfg(test)]
52mod tests {
53    use super::*;
54
55    /// Two buffers that reuse one fd number but differ in any identity field are distinct, so a
56    /// cache keyed by fd cannot return a stale import: a new inode (the kernel reissues one per
57    /// dma-buf since 5.3), a new size, or new geometry each breaks the match.
58    #[test]
59    fn dmabuf_identity_distinguishes_recycled_fd() {
60        let base = DmaBufIdentity {
61            dev: 1,
62            ino: 10,
63            size: 100,
64            modifier: 0,
65            width: 1920,
66            height: 1080,
67        };
68        assert_eq!(base, base);
69        let mut new_ino = base;
70        new_ino.ino = 11;
71        assert_ne!(base, new_ino);
72        let mut new_size = base;
73        new_size.size = 200;
74        assert_ne!(base, new_size);
75        let mut new_mod = base;
76        new_mod.modifier = 1;
77        assert_ne!(base, new_mod);
78        let mut new_geom = base;
79        new_geom.width = 1280;
80        assert_ne!(base, new_geom);
81    }
82
83    /// `probe` reads a real fd deterministically and folds the modifier and geometry into the
84    /// identity: the same fd and parameters yield equal identities, and changing the modifier or the
85    /// geometry changes the identity even on the same fd.
86    #[test]
87    fn dmabuf_identity_probe_is_stable_and_parameterized() {
88        use std::os::fd::AsRawFd;
89        let f = std::fs::File::open("/dev/null").expect("open /dev/null");
90        let fd = f.as_raw_fd();
91        let a = DmaBufIdentity::probe(fd, 0x1234, 1920, 1080);
92        assert_eq!(a, DmaBufIdentity::probe(fd, 0x1234, 1920, 1080));
93        assert_ne!(a, DmaBufIdentity::probe(fd, 0x9999, 1920, 1080));
94        assert_ne!(a, DmaBufIdentity::probe(fd, 0x1234, 1280, 720));
95    }
96}