rutabaga_gfx/rutabaga_gralloc/
formats.rs

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
8use 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::*;
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
25pub const DRM_FORMAT_R8: [u8; 4] = [b'R', b'8', b' ', b' '];
26
27pub const DRM_FORMAT_RGB565: [u8; 4] = [b'R', b'G', b'1', b'6'];
28pub const DRM_FORMAT_BGR888: [u8; 4] = [b'B', b'G', b'2', b'4'];
29
30pub const DRM_FORMAT_XRGB8888: [u8; 4] = [b'X', b'R', b'2', b'4'];
31pub const DRM_FORMAT_XBGR8888: [u8; 4] = [b'X', b'B', b'2', b'4'];
32
33pub const DRM_FORMAT_ARGB8888: [u8; 4] = [b'A', b'R', b'2', b'4'];
34pub const DRM_FORMAT_ABGR8888: [u8; 4] = [b'A', b'B', b'2', b'4'];
35
36pub const DRM_FORMAT_XRGB2101010: [u8; 4] = [b'X', b'R', b'3', b'0'];
37pub const DRM_FORMAT_XBGR2101010: [u8; 4] = [b'X', b'B', b'3', b'0'];
38pub const DRM_FORMAT_ARGB2101010: [u8; 4] = [b'A', b'R', b'3', b'0'];
39pub const DRM_FORMAT_ABGR2101010: [u8; 4] = [b'A', b'B', b'3', b'0'];
40
41pub const DRM_FORMAT_ABGR16161616F: [u8; 4] = [b'A', b'B', b'4', b'H'];
42
43pub const DRM_FORMAT_NV12: [u8; 4] = [b'N', b'V', b'1', b'2'];
44pub 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)]
48pub struct DrmFormat(pub u32);
49
50/// Planar properties associated with each `DrmFormat`.  Copied from helpers.c in minigbm.
51#[derive(Copy, Clone)]
52pub 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
59static 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
66static 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
73static 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
80static 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
87static 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
94static 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
101static 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
108impl DrmFormat {
109    /// Constructs a format identifer using a fourcc byte sequence.
110    #[inline(always)]
111    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)]
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.
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.
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.
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
196impl From<u32> for DrmFormat {
197    fn from(u: u32) -> DrmFormat {
198        DrmFormat(u)
199    }
200}
201
202impl From<DrmFormat> for u32 {
203    fn from(f: DrmFormat) -> u32 {
204        f.0
205    }
206}
207
208impl fmt::Debug for DrmFormat {
209    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
227fn 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
235pub 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)]
261mod tests {
262    use std::fmt::Write;
263
264    use super::*;
265    use crate::rutabaga_gralloc::RutabagaGrallocFlags;
266
267    #[test]
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]
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]
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}