1use std::io::{Seek, SeekFrom};
16use std::os::fd::{AsRawFd, OwnedFd};
17use std::sync::Arc;
18
19use crate::{
20 Color, Display, DrmPrimeSurfaceDescriptor, ExternalBufferDescriptor, MemoryType, Surface, UsageHint,
21 VADRMPRIMESurfaceDescriptor, VA_FOURCC_ARGB, VA_FOURCC_BGRA, VA_FOURCC_BGRX, VA_FOURCC_I420, VA_FOURCC_NV12,
22 VA_FOURCC_P010, VA_FOURCC_RGBA, VA_FOURCC_RGBX, VA_FOURCC_XRGB, VA_FOURCC_YUY2, VA_FOURCC_YV12, VA_RT_FORMAT_RGB32,
23 VA_RT_FORMAT_YUV420, VA_RT_FORMAT_YUV420_10, VA_RT_FORMAT_YUV422,
24};
25
26pub mod drm {
31 const fn fourcc(code: &[u8; 4]) -> u32 {
32 u32::from_le_bytes(*code)
33 }
34
35 pub const NV12: u32 = fourcc(b"NV12");
37 pub const P010: u32 = fourcc(b"P010");
39 pub const YUV420: u32 = fourcc(b"YU12");
41 pub const YVU420: u32 = fourcc(b"YV12");
43 pub const YUYV: u32 = fourcc(b"YUYV");
45 pub const XRGB8888: u32 = fourcc(b"XR24");
47 pub const ARGB8888: u32 = fourcc(b"AR24");
49 pub const XBGR8888: u32 = fourcc(b"XB24");
51 pub const ABGR8888: u32 = fourcc(b"AB24");
53 pub const BGRX8888: u32 = fourcc(b"BX24");
55 pub const BGRA8888: u32 = fourcc(b"BA24");
57}
58
59pub fn va_fourcc(drm_format: u32) -> Option<u32> {
63 Some(match drm_format {
64 drm::NV12 => VA_FOURCC_NV12,
65 drm::P010 => VA_FOURCC_P010,
66 drm::YUV420 => VA_FOURCC_I420,
67 drm::YVU420 => VA_FOURCC_YV12,
68 drm::YUYV => VA_FOURCC_YUY2,
69 drm::XRGB8888 => VA_FOURCC_BGRX,
70 drm::ARGB8888 => VA_FOURCC_BGRA,
71 drm::XBGR8888 => VA_FOURCC_RGBX,
72 drm::ABGR8888 => VA_FOURCC_RGBA,
73 drm::BGRX8888 => VA_FOURCC_XRGB,
74 drm::BGRA8888 => VA_FOURCC_ARGB,
75 _ => return None,
76 })
77}
78
79pub fn rt_format(fourcc: u32) -> Option<u32> {
85 match fourcc {
86 VA_FOURCC_NV12 | VA_FOURCC_I420 | VA_FOURCC_YV12 => Some(VA_RT_FORMAT_YUV420),
87 VA_FOURCC_P010 => Some(VA_RT_FORMAT_YUV420_10),
88 VA_FOURCC_YUY2 => Some(VA_RT_FORMAT_YUV422),
89 VA_FOURCC_BGRA | VA_FOURCC_BGRX | VA_FOURCC_RGBA | VA_FOURCC_RGBX | VA_FOURCC_ARGB | VA_FOURCC_XRGB => {
90 Some(VA_RT_FORMAT_RGB32)
91 }
92 _ => None,
93 }
94}
95
96#[derive(Clone, Copy, Debug, PartialEq, Eq)]
98pub struct Plane {
99 pub offset: u32,
101 pub pitch: u32,
103}
104
105#[derive(Debug)]
117pub struct DmaBuf {
118 pub drm_format: u32,
120 pub modifier: u64,
122 pub width: u32,
127 pub height: u32,
129 pub planes: Vec<Plane>,
131 pub fd: OwnedFd,
133 pub color: Option<Color>,
139}
140
141impl DmaBuf {
142 pub fn from_prime(mut exported: DrmPrimeSurfaceDescriptor) -> anyhow::Result<Self> {
152 if exported.objects.len() != 1 || exported.layers.len() != 1 {
153 anyhow::bail!(
154 "expected one object and one layer, the export has {} and {}",
155 exported.objects.len(),
156 exported.layers.len()
157 );
158 }
159 let object = exported.objects.remove(0);
160 let layer = &exported.layers[0];
161 Ok(Self {
162 drm_format: layer.drm_format,
163 modifier: object.drm_format_modifier,
164 width: exported.width,
165 height: exported.height,
166 planes: (0..(layer.num_planes as usize).min(4))
167 .map(|i| Plane {
168 offset: layer.offset[i],
169 pitch: layer.pitch[i],
170 })
171 .collect(),
172 fd: object.fd,
173 color: None,
174 })
175 }
176
177 pub fn fourcc(&self) -> Option<u32> {
179 va_fourcc(self.drm_format)
180 }
181
182 pub fn import(self, display: &Arc<Display>, usage_hint: Option<UsageHint>) -> anyhow::Result<Surface<DmaBuf>> {
195 let fourcc = self
196 .fourcc()
197 .ok_or_else(|| anyhow::anyhow!("no VA-API format for DRM format {:#010x}", self.drm_format))?;
198 let rt_format =
199 rt_format(fourcc).ok_or_else(|| anyhow::anyhow!("no VA-API render target format for {fourcc:#010x}"))?;
200 if self.planes.is_empty() || self.planes.len() > 4 {
201 anyhow::bail!("a DMA-BUF import takes one to four planes, got {}", self.planes.len());
202 }
203 let (width, height) = (self.width, self.height);
204
205 display
206 .create_surfaces(rt_format, Some(fourcc), width, height, usage_hint, vec![self])
207 .map_err(|e| anyhow::anyhow!("import a {width}x{height} DMA-BUF as a surface: {e:?}"))?
208 .pop()
209 .ok_or_else(|| anyhow::anyhow!("vaCreateSurfaces returned no surface"))
210 }
211}
212
213impl ExternalBufferDescriptor for DmaBuf {
214 const MEMORY_TYPE: MemoryType = MemoryType::DrmPrime2;
215 type DescriptorAttribute = VADRMPRIMESurfaceDescriptor;
216
217 fn va_surface_attribute(&mut self) -> Self::DescriptorAttribute {
218 let mut descriptor = VADRMPRIMESurfaceDescriptor {
221 fourcc: self.fourcc().unwrap_or(0),
222 width: self.width,
223 height: self.height,
224 num_objects: 1,
225 num_layers: 1,
226 ..Default::default()
227 };
228
229 descriptor.objects[0].fd = self.fd.as_raw_fd();
230 descriptor.objects[0].size = buffer_size(&self.fd);
231 descriptor.objects[0].drm_format_modifier = self.modifier;
232
233 let layer = &mut descriptor.layers[0];
234 layer.drm_format = self.drm_format;
235 layer.num_planes = self.planes.len().min(4) as u32;
236 for (index, plane) in self.planes.iter().take(4).enumerate() {
237 layer.object_index[index] = 0;
238 layer.offset[index] = plane.offset;
239 layer.pitch[index] = plane.pitch;
240 }
241
242 descriptor
243 }
244}
245
246fn buffer_size(fd: &OwnedFd) -> u32 {
256 let Ok(clone) = fd.try_clone() else {
257 return 0;
258 };
259 let mut file = std::fs::File::from(clone);
260 let size = file
261 .seek(SeekFrom::End(0))
262 .ok()
263 .and_then(|size| u32::try_from(size).ok());
264 let _ = file.seek(SeekFrom::Start(0));
265 size.unwrap_or(0)
266}
267
268#[cfg(test)]
269mod tests {
270 use super::*;
271
272 #[test]
273 fn every_mapped_format_has_a_render_target() {
274 for format in [
275 drm::NV12,
276 drm::P010,
277 drm::YUV420,
278 drm::YVU420,
279 drm::YUYV,
280 drm::XRGB8888,
281 drm::ARGB8888,
282 drm::XBGR8888,
283 drm::ABGR8888,
284 drm::BGRX8888,
285 drm::BGRA8888,
286 ] {
287 let fourcc = va_fourcc(format).expect("mapped");
288 assert!(rt_format(fourcc).is_some(), "{format:#010x} has no render target");
289 }
290 }
291
292 #[test]
293 fn packed_rgb_maps_to_the_byte_order_name() {
294 assert_eq!(va_fourcc(drm::XRGB8888), Some(VA_FOURCC_BGRX));
297 assert_eq!(va_fourcc(drm::ABGR8888), Some(VA_FOURCC_RGBA));
298 assert_eq!(va_fourcc(drm::YUV420), Some(VA_FOURCC_I420));
299 assert_eq!(va_fourcc(u32::from_le_bytes(*b"ZZZZ")), None);
300 }
301}