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