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}