1use std::fmt;
9
10#[cfg(feature = "vulkano")]
11use vulkano::format::Format as VulkanFormat;
12#[cfg(feature = "vulkano")]
13use vulkano::image::ImageAspect as VulkanImageAspect;
14
15use crate::checked_arithmetic;
16use crate::rutabaga_gralloc::gralloc::ImageAllocationInfo;
17use crate::rutabaga_gralloc::gralloc::ImageMemoryRequirements;
18use crate::rutabaga_utils::RutabagaError;
19use crate::rutabaga_utils::RutabagaResult;
20
21pub const DRM_FORMAT_R8: [u8; 4] = [b'R', b'8', b' ', b' '];
27
28pub const DRM_FORMAT_RGB565: [u8; 4] = [b'R', b'G', b'1', b'6'];
29pub const DRM_FORMAT_BGR888: [u8; 4] = [b'B', b'G', b'2', b'4'];
30
31pub const DRM_FORMAT_XRGB8888: [u8; 4] = [b'X', b'R', b'2', b'4'];
32pub const DRM_FORMAT_XBGR8888: [u8; 4] = [b'X', b'B', b'2', b'4'];
33
34pub const DRM_FORMAT_ARGB8888: [u8; 4] = [b'A', b'R', b'2', b'4'];
35pub const DRM_FORMAT_ABGR8888: [u8; 4] = [b'A', b'B', b'2', b'4'];
36
37pub const DRM_FORMAT_XRGB2101010: [u8; 4] = [b'X', b'R', b'3', b'0'];
38pub const DRM_FORMAT_XBGR2101010: [u8; 4] = [b'X', b'B', b'3', b'0'];
39pub const DRM_FORMAT_ARGB2101010: [u8; 4] = [b'A', b'R', b'3', b'0'];
40pub const DRM_FORMAT_ABGR2101010: [u8; 4] = [b'A', b'B', b'3', b'0'];
41
42pub const DRM_FORMAT_ABGR16161616F: [u8; 4] = [b'A', b'B', b'4', b'H'];
43
44pub const DRM_FORMAT_NV12: [u8; 4] = [b'N', b'V', b'1', b'2'];
45pub const DRM_FORMAT_YVU420: [u8; 4] = [b'Y', b'V', b'1', b'2'];
46
47#[derive(Copy, Clone, Eq, PartialEq, Default)]
49pub struct DrmFormat(pub u32);
50
51#[derive(Copy, Clone)]
53pub struct PlanarLayout {
54 pub num_planes: usize,
55 horizontal_subsampling: [u32; 3],
56 vertical_subsampling: [u32; 3],
57 bytes_per_pixel: [u32; 3],
58}
59
60static PACKED_1BPP: PlanarLayout = PlanarLayout {
61 num_planes: 1,
62 horizontal_subsampling: [1, 0, 0],
63 vertical_subsampling: [1, 0, 0],
64 bytes_per_pixel: [1, 0, 0],
65};
66
67static PACKED_2BPP: PlanarLayout = PlanarLayout {
68 num_planes: 1,
69 horizontal_subsampling: [1, 0, 0],
70 vertical_subsampling: [1, 0, 0],
71 bytes_per_pixel: [2, 0, 0],
72};
73
74static PACKED_3BPP: PlanarLayout = PlanarLayout {
75 num_planes: 1,
76 horizontal_subsampling: [1, 0, 0],
77 vertical_subsampling: [1, 0, 0],
78 bytes_per_pixel: [3, 0, 0],
79};
80
81static PACKED_4BPP: PlanarLayout = PlanarLayout {
82 num_planes: 1,
83 horizontal_subsampling: [1, 0, 0],
84 vertical_subsampling: [1, 0, 0],
85 bytes_per_pixel: [4, 0, 0],
86};
87
88static PACKED_8BPP: PlanarLayout = PlanarLayout {
89 num_planes: 1,
90 horizontal_subsampling: [1, 0, 0],
91 vertical_subsampling: [1, 0, 0],
92 bytes_per_pixel: [8, 0, 0],
93};
94
95static BIPLANAR_YUV420: PlanarLayout = PlanarLayout {
96 num_planes: 2,
97 horizontal_subsampling: [1, 2, 0],
98 vertical_subsampling: [1, 2, 0],
99 bytes_per_pixel: [1, 2, 0],
100};
101
102static TRIPLANAR_YUV420: PlanarLayout = PlanarLayout {
103 num_planes: 3,
104 horizontal_subsampling: [1, 2, 2],
105 vertical_subsampling: [1, 2, 2],
106 bytes_per_pixel: [1, 1, 1],
107};
108
109impl DrmFormat {
110 #[inline(always)]
112 pub fn new(a: u8, b: u8, c: u8, d: u8) -> DrmFormat {
113 DrmFormat(a as u32 | (b as u32) << 8 | (c as u32) << 16 | (d as u32) << 24)
114 }
115
116 #[inline(always)]
118 pub fn to_bytes(&self) -> [u8; 4] {
119 let f = self.0;
120 [f as u8, (f >> 8) as u8, (f >> 16) as u8, (f >> 24) as u8]
121 }
122
123 pub fn planar_layout(&self) -> RutabagaResult<PlanarLayout> {
125 match self.to_bytes() {
126 DRM_FORMAT_R8 => Ok(PACKED_1BPP),
127 DRM_FORMAT_RGB565 => Ok(PACKED_2BPP),
128 DRM_FORMAT_BGR888 => Ok(PACKED_3BPP),
129 DRM_FORMAT_ABGR2101010
130 | DRM_FORMAT_ABGR8888
131 | DRM_FORMAT_XBGR2101010
132 | DRM_FORMAT_XBGR8888
133 | DRM_FORMAT_ARGB2101010
134 | DRM_FORMAT_ARGB8888
135 | DRM_FORMAT_XRGB2101010
136 | DRM_FORMAT_XRGB8888 => Ok(PACKED_4BPP),
137 DRM_FORMAT_ABGR16161616F => Ok(PACKED_8BPP),
138 DRM_FORMAT_NV12 => Ok(BIPLANAR_YUV420),
139 DRM_FORMAT_YVU420 => Ok(TRIPLANAR_YUV420),
140 _ => Err(RutabagaError::InvalidGrallocDrmFormat),
141 }
142 }
143
144 #[cfg(feature = "vulkano")]
145 pub fn vulkan_format(&self) -> RutabagaResult<VulkanFormat> {
147 match self.to_bytes() {
148 DRM_FORMAT_R8 => Ok(VulkanFormat::R8_UNORM),
149 DRM_FORMAT_RGB565 => Ok(VulkanFormat::R5G6B5_UNORM_PACK16),
150 DRM_FORMAT_BGR888 => Ok(VulkanFormat::R8G8B8_UNORM),
151 DRM_FORMAT_ABGR2101010 | DRM_FORMAT_XBGR2101010 => {
152 Ok(VulkanFormat::A2R10G10B10_UNORM_PACK32)
153 }
154 DRM_FORMAT_ABGR8888 | DRM_FORMAT_XBGR8888 => Ok(VulkanFormat::R8G8B8A8_UNORM),
155 DRM_FORMAT_ARGB2101010 | DRM_FORMAT_XRGB2101010 => {
156 Ok(VulkanFormat::A2B10G10R10_UNORM_PACK32)
157 }
158 DRM_FORMAT_ARGB8888 | DRM_FORMAT_XRGB8888 => Ok(VulkanFormat::B8G8R8A8_UNORM),
159 DRM_FORMAT_ABGR16161616F => Ok(VulkanFormat::R16G16B16A16_SFLOAT),
160 DRM_FORMAT_NV12 => Ok(VulkanFormat::G8_B8R8_2PLANE_420_UNORM),
161 DRM_FORMAT_YVU420 => Ok(VulkanFormat::G8_B8_R8_3PLANE_420_UNORM),
162 _ => Err(RutabagaError::InvalidGrallocDrmFormat),
163 }
164 }
165
166 #[cfg(feature = "vulkano")]
167 pub fn vulkan_image_aspect(&self, plane: usize) -> RutabagaResult<VulkanImageAspect> {
169 match self.to_bytes() {
170 DRM_FORMAT_R8
171 | DRM_FORMAT_RGB565
172 | DRM_FORMAT_BGR888
173 | DRM_FORMAT_ABGR2101010
174 | DRM_FORMAT_ABGR8888
175 | DRM_FORMAT_XBGR2101010
176 | DRM_FORMAT_XBGR8888
177 | DRM_FORMAT_ARGB2101010
178 | DRM_FORMAT_ARGB8888
179 | DRM_FORMAT_XRGB2101010
180 | DRM_FORMAT_XRGB8888 => Ok(VulkanImageAspect::Color),
181 DRM_FORMAT_NV12 => match plane {
182 0 => Ok(VulkanImageAspect::Plane0),
183 1 => Ok(VulkanImageAspect::Plane1),
184 _ => Err(RutabagaError::InvalidGrallocNumberOfPlanes.into()),
185 },
186 DRM_FORMAT_YVU420 => match plane {
187 0 => Ok(VulkanImageAspect::Plane0),
188 1 => Ok(VulkanImageAspect::Plane1),
189 2 => Ok(VulkanImageAspect::Plane2),
190 _ => Err(RutabagaError::InvalidGrallocNumberOfPlanes.into()),
191 },
192 _ => Err(RutabagaError::InvalidGrallocDrmFormat),
193 }
194 }
195}
196
197impl From<u32> for DrmFormat {
198 fn from(u: u32) -> DrmFormat {
199 DrmFormat(u)
200 }
201}
202
203impl From<DrmFormat> for u32 {
204 fn from(f: DrmFormat) -> u32 {
205 f.0
206 }
207}
208
209impl fmt::Debug for DrmFormat {
210 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
211 let b = self.to_bytes();
212 if b.iter().all(u8::is_ascii_graphic) {
213 write!(
214 f,
215 "fourcc({}{}{}{})",
216 b[0] as char, b[1] as char, b[2] as char, b[3] as char
217 )
218 } else {
219 write!(
220 f,
221 "fourcc(0x{:02x}{:02x}{:02x}{:02x})",
222 b[0], b[1], b[2], b[3]
223 )
224 }
225 }
226}
227
228fn stride_from_layout(layout: &PlanarLayout, width: u32, plane: usize) -> RutabagaResult<u32> {
229 let bytes_per_pixel = layout.bytes_per_pixel[plane];
230 let horizontal_subsampling = layout.horizontal_subsampling[plane];
231 let subsampled_width = checked_arithmetic!(width / horizontal_subsampling)?;
232 let stride = checked_arithmetic!(bytes_per_pixel * subsampled_width)?;
233 Ok(stride)
234}
235
236pub fn canonical_image_requirements(
237 info: ImageAllocationInfo,
238) -> RutabagaResult<ImageMemoryRequirements> {
239 let mut image_requirements: ImageMemoryRequirements = Default::default();
240 let mut size: u32 = 0;
241 let layout = info.drm_format.planar_layout()?;
242 for plane in 0..layout.num_planes {
243 let plane_stride = stride_from_layout(&layout, info.width, plane)?;
244 image_requirements.strides[plane] = plane_stride;
245 if plane > 0 {
246 image_requirements.offsets[plane] = size;
247 }
248
249 let height = info.height;
250 let vertical_subsampling = layout.vertical_subsampling[plane];
251 let subsampled_height = checked_arithmetic!(height / vertical_subsampling)?;
252 let plane_size = checked_arithmetic!(subsampled_height * plane_stride)?;
253 size = checked_arithmetic!(size + plane_size)?;
254 }
255
256 image_requirements.info = info;
257 image_requirements.size = size as u64;
258 Ok(image_requirements)
259}
260
261#[cfg(test)]
262mod tests {
263 use std::fmt::Write;
264
265 use super::*;
266 use crate::rutabaga_gralloc::RutabagaGrallocFlags;
267
268 #[test]
269 fn format_debug() {
270 let f = DrmFormat::new(b'X', b'R', b'2', b'4');
271 let mut buf = String::new();
272 write!(&mut buf, "{:?}", f).unwrap();
273 assert_eq!(buf, "fourcc(XR24)");
274
275 let f = DrmFormat::new(0, 1, 2, 16);
276 let mut buf = String::new();
277 write!(&mut buf, "{:?}", f).unwrap();
278 assert_eq!(buf, "fourcc(0x00010210)");
279 }
280
281 #[test]
282 fn canonical_formats() {
283 let mut info = ImageAllocationInfo {
284 width: 10,
285 height: 10,
286 drm_format: DrmFormat::new(b'R', b'8', b' ', b' '),
287 flags: RutabagaGrallocFlags::empty(),
288 };
289
290 let r8_reqs = canonical_image_requirements(info).unwrap();
291
292 assert_eq!(r8_reqs.info.width, 10);
293 assert_eq!(r8_reqs.info.height, 10);
294 assert_eq!(r8_reqs.strides[0], 10);
295 assert_eq!(r8_reqs.strides[1], 0);
296 assert_eq!(r8_reqs.strides[2], 0);
297
298 assert_eq!(r8_reqs.offsets[0], 0);
299 assert_eq!(r8_reqs.offsets[1], 0);
300 assert_eq!(r8_reqs.offsets[2], 0);
301
302 assert_eq!(r8_reqs.size, 100);
303
304 info.drm_format = DrmFormat::new(b'X', b'R', b'2', b'4');
305 let xr24_reqs = canonical_image_requirements(info).unwrap();
306
307 assert_eq!(xr24_reqs.info.width, 10);
308 assert_eq!(xr24_reqs.info.height, 10);
309 assert_eq!(xr24_reqs.strides[0], 40);
310 assert_eq!(xr24_reqs.strides[1], 0);
311 assert_eq!(xr24_reqs.strides[2], 0);
312
313 assert_eq!(xr24_reqs.offsets[0], 0);
314 assert_eq!(xr24_reqs.offsets[1], 0);
315 assert_eq!(xr24_reqs.offsets[2], 0);
316
317 assert_eq!(xr24_reqs.size, 400);
318 }
319
320 #[test]
321 fn canonical_planar_formats() {
322 let mut info = ImageAllocationInfo {
323 width: 10,
324 height: 10,
325 drm_format: DrmFormat::new(b'N', b'V', b'1', b'2'),
326 flags: RutabagaGrallocFlags::empty(),
327 };
328
329 let nv12_reqs = canonical_image_requirements(info).unwrap();
330
331 assert_eq!(nv12_reqs.info.width, 10);
332 assert_eq!(nv12_reqs.info.height, 10);
333 assert_eq!(nv12_reqs.strides[0], 10);
334 assert_eq!(nv12_reqs.strides[1], 10);
335 assert_eq!(nv12_reqs.strides[2], 0);
336
337 assert_eq!(nv12_reqs.offsets[0], 0);
338 assert_eq!(nv12_reqs.offsets[1], 100);
339 assert_eq!(nv12_reqs.offsets[2], 0);
340
341 assert_eq!(nv12_reqs.size, 150);
342
343 info.drm_format = DrmFormat::new(b'Y', b'V', b'1', b'2');
344 let yv12_reqs = canonical_image_requirements(info).unwrap();
345
346 assert_eq!(yv12_reqs.info.width, 10);
347 assert_eq!(yv12_reqs.info.height, 10);
348 assert_eq!(yv12_reqs.strides[0], 10);
349 assert_eq!(yv12_reqs.strides[1], 5);
350 assert_eq!(yv12_reqs.strides[2], 5);
351
352 assert_eq!(yv12_reqs.offsets[0], 0);
353 assert_eq!(yv12_reqs.offsets[1], 100);
354 assert_eq!(yv12_reqs.offsets[2], 125);
355
356 assert_eq!(yv12_reqs.size, 150);
357 }
358}