1 // Copyright 2021 The Chromium OS Authors. All rights reserved.
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 use crate::checked_arithmetic;
11 use crate::rutabaga_gralloc::gralloc::{ImageAllocationInfo, ImageMemoryRequirements};
12 use crate::rutabaga_utils::*;
13
14 #[cfg(feature = "vulkano")]
15 use vulkano::format::Format as VulkanFormat;
16
17 #[cfg(feature = "vulkano")]
18 use vulkano::image::ImageAspect as VulkanImageAspect;
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::Unsupported),
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::R8Unorm),
148 DRM_FORMAT_RGB565 => Ok(VulkanFormat::R5G6B5UnormPack16),
149 DRM_FORMAT_BGR888 => Ok(VulkanFormat::R8G8B8Unorm),
150 DRM_FORMAT_ABGR2101010 | DRM_FORMAT_XBGR2101010 => {
151 Ok(VulkanFormat::A2R10G10B10UnormPack32)
152 }
153 DRM_FORMAT_ABGR8888 | DRM_FORMAT_XBGR8888 => Ok(VulkanFormat::R8G8B8A8Unorm),
154 DRM_FORMAT_ARGB2101010 | DRM_FORMAT_XRGB2101010 => {
155 Ok(VulkanFormat::A2B10G10R10UnormPack32)
156 }
157 DRM_FORMAT_ARGB8888 | DRM_FORMAT_XRGB8888 => Ok(VulkanFormat::B8G8R8A8Unorm),
158 DRM_FORMAT_ABGR16161616F => Ok(VulkanFormat::R16G16B16A16Sfloat),
159 DRM_FORMAT_NV12 => Ok(VulkanFormat::G8B8R8_2PLANE420Unorm),
160 DRM_FORMAT_YVU420 => Ok(VulkanFormat::G8B8R8_3PLANE420Unorm),
161 _ => Err(RutabagaError::Unsupported),
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 {
180 color: true,
181 ..VulkanImageAspect::none()
182 }),
183 DRM_FORMAT_NV12 => match plane {
184 0 => Ok(VulkanImageAspect {
185 plane0: true,
186 ..VulkanImageAspect::none()
187 }),
188 1 => Ok(VulkanImageAspect {
189 plane1: true,
190 ..VulkanImageAspect::none()
191 }),
192 _ => Err(RutabagaError::Unsupported),
193 },
194 DRM_FORMAT_YVU420 => match plane {
195 0 => Ok(VulkanImageAspect {
196 plane0: true,
197 ..VulkanImageAspect::none()
198 }),
199 1 => Ok(VulkanImageAspect {
200 plane1: true,
201 ..VulkanImageAspect::none()
202 }),
203 2 => Ok(VulkanImageAspect {
204 plane2: true,
205 ..VulkanImageAspect::none()
206 }),
207 _ => Err(RutabagaError::Unsupported),
208 },
209 _ => Err(RutabagaError::Unsupported),
210 }
211 }
212 }
213
214 impl From<u32> for DrmFormat {
from(u: u32) -> DrmFormat215 fn from(u: u32) -> DrmFormat {
216 DrmFormat(u)
217 }
218 }
219
220 impl From<DrmFormat> for u32 {
from(f: DrmFormat) -> u32221 fn from(f: DrmFormat) -> u32 {
222 f.0
223 }
224 }
225
226 impl fmt::Debug for DrmFormat {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result227 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
228 let b = self.to_bytes();
229 if b.iter().all(u8::is_ascii_graphic) {
230 write!(
231 f,
232 "fourcc({}{}{}{})",
233 b[0] as char, b[1] as char, b[2] as char, b[3] as char
234 )
235 } else {
236 write!(
237 f,
238 "fourcc(0x{:02x}{:02x}{:02x}{:02x})",
239 b[0], b[1], b[2], b[3]
240 )
241 }
242 }
243 }
244
stride_from_layout(layout: &PlanarLayout, width: u32, plane: usize) -> RutabagaResult<u32>245 fn stride_from_layout(layout: &PlanarLayout, width: u32, plane: usize) -> RutabagaResult<u32> {
246 let bytes_per_pixel = layout.bytes_per_pixel[plane];
247 let horizontal_subsampling = layout.horizontal_subsampling[plane];
248 let subsampled_width = checked_arithmetic!(width / horizontal_subsampling)?;
249 let stride = checked_arithmetic!(bytes_per_pixel * subsampled_width)?;
250 Ok(stride)
251 }
252
canonical_image_requirements( info: ImageAllocationInfo, ) -> RutabagaResult<ImageMemoryRequirements>253 pub fn canonical_image_requirements(
254 info: ImageAllocationInfo,
255 ) -> RutabagaResult<ImageMemoryRequirements> {
256 let mut image_requirements: ImageMemoryRequirements = Default::default();
257 let mut size: u32 = 0;
258 let layout = info.drm_format.planar_layout()?;
259 for plane in 0..layout.num_planes {
260 let plane_stride = stride_from_layout(&layout, info.width, plane)?;
261 image_requirements.strides[plane] = plane_stride;
262 if plane > 0 {
263 image_requirements.offsets[plane] = size as u32;
264 }
265
266 let height = info.height;
267 let vertical_subsampling = layout.vertical_subsampling[plane];
268 let subsampled_height = checked_arithmetic!(height / vertical_subsampling)?;
269 let plane_size = checked_arithmetic!(subsampled_height * plane_stride)?;
270 size = checked_arithmetic!(size + plane_size)?;
271 }
272
273 image_requirements.info = info;
274 image_requirements.size = size as u64;
275 Ok(image_requirements)
276 }
277
278 #[cfg(test)]
279 mod tests {
280 use super::*;
281 use crate::rutabaga_gralloc::RutabagaGrallocFlags;
282 use std::fmt::Write;
283
284 #[test]
format_debug()285 fn format_debug() {
286 let f = DrmFormat::new(b'X', b'R', b'2', b'4');
287 let mut buf = String::new();
288 write!(&mut buf, "{:?}", f).unwrap();
289 assert_eq!(buf, "fourcc(XR24)");
290
291 let f = DrmFormat::new(0, 1, 2, 16);
292 let mut buf = String::new();
293 write!(&mut buf, "{:?}", f).unwrap();
294 assert_eq!(buf, "fourcc(0x00010210)");
295 }
296
297 #[test]
canonical_formats()298 fn canonical_formats() {
299 let mut info = ImageAllocationInfo {
300 width: 10,
301 height: 10,
302 drm_format: DrmFormat::new(b'R', b'8', b' ', b' '),
303 flags: RutabagaGrallocFlags::empty(),
304 };
305
306 let r8_reqs = canonical_image_requirements(info).unwrap();
307
308 assert_eq!(r8_reqs.info.width, 10);
309 assert_eq!(r8_reqs.info.height, 10);
310 assert_eq!(r8_reqs.strides[0], 10);
311 assert_eq!(r8_reqs.strides[1], 0);
312 assert_eq!(r8_reqs.strides[2], 0);
313
314 assert_eq!(r8_reqs.offsets[0], 0);
315 assert_eq!(r8_reqs.offsets[1], 0);
316 assert_eq!(r8_reqs.offsets[2], 0);
317
318 assert_eq!(r8_reqs.size, 100);
319
320 info.drm_format = DrmFormat::new(b'X', b'R', b'2', b'4');
321 let xr24_reqs = canonical_image_requirements(info).unwrap();
322
323 assert_eq!(xr24_reqs.info.width, 10);
324 assert_eq!(xr24_reqs.info.height, 10);
325 assert_eq!(xr24_reqs.strides[0], 40);
326 assert_eq!(xr24_reqs.strides[1], 0);
327 assert_eq!(xr24_reqs.strides[2], 0);
328
329 assert_eq!(xr24_reqs.offsets[0], 0);
330 assert_eq!(xr24_reqs.offsets[1], 0);
331 assert_eq!(xr24_reqs.offsets[2], 0);
332
333 assert_eq!(xr24_reqs.size, 400);
334 }
335
336 #[test]
canonical_planar_formats()337 fn canonical_planar_formats() {
338 let mut info = ImageAllocationInfo {
339 width: 10,
340 height: 10,
341 drm_format: DrmFormat::new(b'N', b'V', b'1', b'2'),
342 flags: RutabagaGrallocFlags::empty(),
343 };
344
345 let nv12_reqs = canonical_image_requirements(info).unwrap();
346
347 assert_eq!(nv12_reqs.info.width, 10);
348 assert_eq!(nv12_reqs.info.height, 10);
349 assert_eq!(nv12_reqs.strides[0], 10);
350 assert_eq!(nv12_reqs.strides[1], 10);
351 assert_eq!(nv12_reqs.strides[2], 0);
352
353 assert_eq!(nv12_reqs.offsets[0], 0);
354 assert_eq!(nv12_reqs.offsets[1], 100);
355 assert_eq!(nv12_reqs.offsets[2], 0);
356
357 assert_eq!(nv12_reqs.size, 150);
358
359 info.drm_format = DrmFormat::new(b'Y', b'V', b'1', b'2');
360 let yv12_reqs = canonical_image_requirements(info).unwrap();
361
362 assert_eq!(yv12_reqs.info.width, 10);
363 assert_eq!(yv12_reqs.info.height, 10);
364 assert_eq!(yv12_reqs.strides[0], 10);
365 assert_eq!(yv12_reqs.strides[1], 5);
366 assert_eq!(yv12_reqs.strides[2], 5);
367
368 assert_eq!(yv12_reqs.offsets[0], 0);
369 assert_eq!(yv12_reqs.offsets[1], 100);
370 assert_eq!(yv12_reqs.offsets[2], 125);
371
372 assert_eq!(yv12_reqs.size, 150);
373 }
374 }
375