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 //! gralloc: Cross-platform, Rust-based, Vulkan centric GPU allocation and
6 //! mapping.
7 
8 use std::collections::BTreeMap as Map;
9 
10 use base::{round_up_to_page_size, MappedRegion};
11 
12 use crate::rutabaga_gralloc::formats::*;
13 use crate::rutabaga_gralloc::system_gralloc::SystemGralloc;
14 use crate::rutabaga_utils::*;
15 
16 #[cfg(feature = "minigbm")]
17 use crate::rutabaga_gralloc::minigbm::MinigbmDevice;
18 
19 #[cfg(feature = "vulkano")]
20 use crate::rutabaga_gralloc::vulkano_gralloc::VulkanoGralloc;
21 
22 /*
23  * Rutabaga gralloc flags are copied from minigbm, but redundant legacy flags are left out.
24  * For example, USE_WRITE / USE_CURSOR_64X64 / USE_CURSOR don't add much value.
25  */
26 const RUTABAGA_GRALLOC_USE_SCANOUT: u32 = 1 << 0;
27 const RUTABAGA_GRALLOC_USE_RENDERING: u32 = 1 << 2;
28 const RUTABAGA_GRALLOC_USE_LINEAR: u32 = 1 << 4;
29 const RUTABAGA_GRALLOC_USE_TEXTURING: u32 = 1 << 5;
30 const RUTABAGA_GRALLOC_USE_CAMERA_WRITE: u32 = 1 << 6;
31 const RUTABAGA_GRALLOC_USE_CAMERA_READ: u32 = 1 << 7;
32 #[allow(dead_code)]
33 const RUTABAGA_GRALLOC_USE_PROTECTED: u32 = 1 << 8;
34 
35 /* SW_{WRITE,READ}_RARELY omitted since not even Android uses this much. */
36 const RUTABAGA_GRALLOC_USE_SW_READ_OFTEN: u32 = 1 << 9;
37 const RUTABAGA_GRALLOC_USE_SW_WRITE_OFTEN: u32 = 1 << 11;
38 
39 #[allow(dead_code)]
40 const RUTABAGA_GRALLOC_VIDEO_DECODER: u32 = 1 << 13;
41 #[allow(dead_code)]
42 const RUTABAGA_GRALLOC_VIDEO_ENCODER: u32 = 1 << 14;
43 
44 /// Usage flags for constructing a buffer object.
45 #[derive(Copy, Clone, Eq, PartialEq, Default)]
46 pub struct RutabagaGrallocFlags(pub u32);
47 
48 impl RutabagaGrallocFlags {
49     /// Returns empty set of flags.
50     #[inline(always)]
empty() -> RutabagaGrallocFlags51     pub fn empty() -> RutabagaGrallocFlags {
52         RutabagaGrallocFlags(0)
53     }
54 
55     /// Returns the given set of raw `RUTABAGA_GRALLOC` flags wrapped in a RutabagaGrallocFlags
56     /// struct.
57     #[inline(always)]
new(raw: u32) -> RutabagaGrallocFlags58     pub fn new(raw: u32) -> RutabagaGrallocFlags {
59         RutabagaGrallocFlags(raw)
60     }
61 
62     /// Sets the scanout flag's presence.
63     #[inline(always)]
use_scanout(self, e: bool) -> RutabagaGrallocFlags64     pub fn use_scanout(self, e: bool) -> RutabagaGrallocFlags {
65         if e {
66             RutabagaGrallocFlags(self.0 | RUTABAGA_GRALLOC_USE_SCANOUT)
67         } else {
68             RutabagaGrallocFlags(self.0 & !RUTABAGA_GRALLOC_USE_SCANOUT)
69         }
70     }
71 
72     /// Sets the rendering flag's presence.
73     #[inline(always)]
use_rendering(self, e: bool) -> RutabagaGrallocFlags74     pub fn use_rendering(self, e: bool) -> RutabagaGrallocFlags {
75         if e {
76             RutabagaGrallocFlags(self.0 | RUTABAGA_GRALLOC_USE_RENDERING)
77         } else {
78             RutabagaGrallocFlags(self.0 & !RUTABAGA_GRALLOC_USE_RENDERING)
79         }
80     }
81 
82     /// Sets the linear flag's presence.
83     #[inline(always)]
use_linear(self, e: bool) -> RutabagaGrallocFlags84     pub fn use_linear(self, e: bool) -> RutabagaGrallocFlags {
85         if e {
86             RutabagaGrallocFlags(self.0 | RUTABAGA_GRALLOC_USE_LINEAR)
87         } else {
88             RutabagaGrallocFlags(self.0 & !RUTABAGA_GRALLOC_USE_LINEAR)
89         }
90     }
91 
92     /// Sets the SW write flag's presence.
93     #[inline(always)]
use_sw_write(self, e: bool) -> RutabagaGrallocFlags94     pub fn use_sw_write(self, e: bool) -> RutabagaGrallocFlags {
95         if e {
96             RutabagaGrallocFlags(self.0 | RUTABAGA_GRALLOC_USE_SW_WRITE_OFTEN)
97         } else {
98             RutabagaGrallocFlags(self.0 & !RUTABAGA_GRALLOC_USE_SW_WRITE_OFTEN)
99         }
100     }
101 
102     /// Sets the SW read flag's presence.
103     #[inline(always)]
use_sw_read(self, e: bool) -> RutabagaGrallocFlags104     pub fn use_sw_read(self, e: bool) -> RutabagaGrallocFlags {
105         if e {
106             RutabagaGrallocFlags(self.0 | RUTABAGA_GRALLOC_USE_SW_READ_OFTEN)
107         } else {
108             RutabagaGrallocFlags(self.0 & !RUTABAGA_GRALLOC_USE_SW_READ_OFTEN)
109         }
110     }
111 
112     /// Returns true if the texturing flag is set.
113     #[inline(always)]
uses_texturing(self) -> bool114     pub fn uses_texturing(self) -> bool {
115         self.0 & RUTABAGA_GRALLOC_USE_TEXTURING != 0
116     }
117 
118     /// Returns true if the rendering flag is set.
119     #[inline(always)]
uses_rendering(self) -> bool120     pub fn uses_rendering(self) -> bool {
121         self.0 & RUTABAGA_GRALLOC_USE_RENDERING != 0
122     }
123 
124     /// Returns true if the memory will accessed by the CPU or an IP block that prefers host
125     /// visible allocations (i.e, camera).
126     #[inline(always)]
host_visible(self) -> bool127     pub fn host_visible(self) -> bool {
128         self.0 & RUTABAGA_GRALLOC_USE_SW_READ_OFTEN != 0
129             || self.0 & RUTABAGA_GRALLOC_USE_SW_WRITE_OFTEN != 0
130             || self.0 & RUTABAGA_GRALLOC_USE_CAMERA_WRITE != 0
131             || self.0 & RUTABAGA_GRALLOC_USE_CAMERA_READ != 0
132     }
133 
134     /// Returns true if the memory will read by the CPU or an IP block that prefers cached
135     /// allocations (i.e, camera).
136     #[inline(always)]
host_cached(self) -> bool137     pub fn host_cached(self) -> bool {
138         self.0 & RUTABAGA_GRALLOC_USE_CAMERA_READ != 0
139             || self.0 & RUTABAGA_GRALLOC_USE_SW_READ_OFTEN != 0
140     }
141 }
142 
143 /// Information required to allocate a swapchain image.
144 #[derive(Copy, Clone, Default)]
145 pub struct ImageAllocationInfo {
146     pub width: u32,
147     pub height: u32,
148     pub drm_format: DrmFormat,
149     pub flags: RutabagaGrallocFlags,
150 }
151 
152 /// The memory requirements, compression and layout of a swapchain image.
153 #[derive(Copy, Clone, Default)]
154 pub struct ImageMemoryRequirements {
155     pub info: ImageAllocationInfo,
156     pub map_info: u32,
157     pub strides: [u32; 4],
158     pub offsets: [u32; 4],
159     pub modifier: u64,
160     pub size: u64,
161     pub vulkan_info: Option<VulkanInfo>,
162 }
163 
164 /// Trait that needs to be implemented to service graphics memory requests.  Two step allocation
165 /// process:
166 ///
167 ///   (1) Get memory requirements for a given allocation request.
168 ///   (2) Allocate using those requirements.
169 pub trait Gralloc {
170     /// This function must return true if the implementation can:
171     ///
172     ///   (1) allocate GPU memory and
173     ///   (2) {export to}/{import from} into a OS-specific RutabagaHandle.
supports_external_gpu_memory(&self) -> bool174     fn supports_external_gpu_memory(&self) -> bool;
175 
176     /// This function must return true the implementation can {export to}/{import from} a Linux
177     /// dma-buf.  This often used for sharing with the scanout engine or multimedia subsystems.
supports_dmabuf(&self) -> bool178     fn supports_dmabuf(&self) -> bool;
179 
180     /// Implementations must return the resource layout, compression, and caching properties of
181     /// an allocation request.
get_image_memory_requirements( &mut self, info: ImageAllocationInfo, ) -> RutabagaResult<ImageMemoryRequirements>182     fn get_image_memory_requirements(
183         &mut self,
184         info: ImageAllocationInfo,
185     ) -> RutabagaResult<ImageMemoryRequirements>;
186 
187     /// Implementations must allocate memory given the requirements and return a RutabagaHandle
188     /// upon success.
allocate_memory(&mut self, reqs: ImageMemoryRequirements) -> RutabagaResult<RutabagaHandle>189     fn allocate_memory(&mut self, reqs: ImageMemoryRequirements) -> RutabagaResult<RutabagaHandle>;
190 
191     /// Implementations must import the given `handle` and return a mapping, suitable for use with
192     /// KVM and other hypervisors.  This is optional and only works with the Vulkano backend.
import_and_map( &mut self, _handle: RutabagaHandle, _vulkan_info: VulkanInfo, _size: u64, ) -> RutabagaResult<Box<dyn MappedRegion>>193     fn import_and_map(
194         &mut self,
195         _handle: RutabagaHandle,
196         _vulkan_info: VulkanInfo,
197         _size: u64,
198     ) -> RutabagaResult<Box<dyn MappedRegion>> {
199         Err(RutabagaError::Unsupported)
200     }
201 }
202 
203 /// Enumeration of possible allocation backends.
204 #[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord)]
205 pub enum GrallocBackend {
206     #[allow(dead_code)]
207     Vulkano,
208     #[allow(dead_code)]
209     Minigbm,
210     System,
211 }
212 
213 /// A container for a variety of allocation backends.
214 pub struct RutabagaGralloc {
215     grallocs: Map<GrallocBackend, Box<dyn Gralloc>>,
216 }
217 
218 impl RutabagaGralloc {
219     /// Returns a new RutabagaGralloc instance upon success.  All allocation backends that have
220     /// been built are initialized.  The default system allocator is always initialized.
new() -> RutabagaResult<RutabagaGralloc>221     pub fn new() -> RutabagaResult<RutabagaGralloc> {
222         let mut grallocs: Map<GrallocBackend, Box<dyn Gralloc>> = Default::default();
223 
224         let system = SystemGralloc::init()?;
225         grallocs.insert(GrallocBackend::System, system);
226 
227         #[cfg(feature = "minigbm")]
228         {
229             // crosvm integration tests build with the "wl-dmabuf" feature, which translates in
230             // rutabaga to the "minigbm" feature.  These tests run on hosts where a rendernode is
231             // not present, and minigbm can not be initialized.
232             //
233             // Thus, to keep kokoro happy, allow minigbm initialization to fail silently for now.
234             if let Ok(gbm_device) = MinigbmDevice::init() {
235                 grallocs.insert(GrallocBackend::Minigbm, gbm_device);
236             }
237         }
238 
239         #[cfg(feature = "vulkano")]
240         {
241             let vulkano = VulkanoGralloc::init()?;
242             grallocs.insert(GrallocBackend::Vulkano, vulkano);
243         }
244 
245         Ok(RutabagaGralloc { grallocs })
246     }
247 
248     /// Returns true if one of the allocation backends supports GPU external memory.
supports_external_gpu_memory(&self) -> bool249     pub fn supports_external_gpu_memory(&self) -> bool {
250         for gralloc in self.grallocs.values() {
251             if gralloc.supports_external_gpu_memory() {
252                 return true;
253             }
254         }
255 
256         false
257     }
258 
259     /// Returns true if one of the allocation backends supports dma_buf.
supports_dmabuf(&self) -> bool260     pub fn supports_dmabuf(&self) -> bool {
261         for gralloc in self.grallocs.values() {
262             if gralloc.supports_dmabuf() {
263                 return true;
264             }
265         }
266 
267         false
268     }
269 
270     /// Returns the best allocation backend to service a particular request.
determine_optimal_backend(&self, _info: ImageAllocationInfo) -> GrallocBackend271     fn determine_optimal_backend(&self, _info: ImageAllocationInfo) -> GrallocBackend {
272         // This function could be more sophisticated and consider the allocation info.  For example,
273         // nobody has ever tried Mali allocated memory + a mediatek/rockchip display and as such it
274         // probably doesn't work.  In addition, YUV calculations in minigbm have yet to make it
275         // towards the Vulkan api.  This function allows for a variety of quirks, but for now just
276         // choose the most shiny backend that the user has built.  The rationale is "why would you
277         // build it if you don't want to use it".
278         let mut _backend = GrallocBackend::System;
279 
280         #[cfg(feature = "minigbm")]
281         {
282             // See note on "wl-dmabuf" and Kokoro in Gralloc::new().
283             if self.grallocs.contains_key(&GrallocBackend::Minigbm) {
284                 _backend = GrallocBackend::Minigbm;
285             }
286         }
287 
288         #[cfg(feature = "vulkano")]
289         {
290             _backend = GrallocBackend::Vulkano;
291         }
292 
293         _backend
294     }
295 
296     /// Returns a image memory requirements for the given `info` upon success.
get_image_memory_requirements( &mut self, info: ImageAllocationInfo, ) -> RutabagaResult<ImageMemoryRequirements>297     pub fn get_image_memory_requirements(
298         &mut self,
299         info: ImageAllocationInfo,
300     ) -> RutabagaResult<ImageMemoryRequirements> {
301         let backend = self.determine_optimal_backend(info);
302 
303         let gralloc = self
304             .grallocs
305             .get_mut(&backend)
306             .ok_or(RutabagaError::Unsupported)?;
307 
308         let mut reqs = gralloc.get_image_memory_requirements(info)?;
309         reqs.size = round_up_to_page_size(reqs.size as usize) as u64;
310         Ok(reqs)
311     }
312 
313     /// Allocates memory given the particular `reqs` upon success.
allocate_memory( &mut self, reqs: ImageMemoryRequirements, ) -> RutabagaResult<RutabagaHandle>314     pub fn allocate_memory(
315         &mut self,
316         reqs: ImageMemoryRequirements,
317     ) -> RutabagaResult<RutabagaHandle> {
318         let backend = self.determine_optimal_backend(reqs.info);
319 
320         let gralloc = self
321             .grallocs
322             .get_mut(&backend)
323             .ok_or(RutabagaError::Unsupported)?;
324 
325         gralloc.allocate_memory(reqs)
326     }
327 
328     /// Imports the `handle` using the given `vulkan_info`.  Returns a mapping using Vulkano upon
329     /// success.  Should not be used with minigbm or system gralloc backends.
import_and_map( &mut self, handle: RutabagaHandle, vulkan_info: VulkanInfo, size: u64, ) -> RutabagaResult<Box<dyn MappedRegion>>330     pub fn import_and_map(
331         &mut self,
332         handle: RutabagaHandle,
333         vulkan_info: VulkanInfo,
334         size: u64,
335     ) -> RutabagaResult<Box<dyn MappedRegion>> {
336         let gralloc = self
337             .grallocs
338             .get_mut(&GrallocBackend::Vulkano)
339             .ok_or(RutabagaError::Unsupported)?;
340 
341         gralloc.import_and_map(handle, vulkan_info, size)
342     }
343 }
344 
345 #[cfg(test)]
346 mod tests {
347     use super::*;
348 
349     #[test]
create_render_target()350     fn create_render_target() {
351         let gralloc_result = RutabagaGralloc::new();
352         if gralloc_result.is_err() {
353             return;
354         }
355 
356         let mut gralloc = gralloc_result.unwrap();
357 
358         let info = ImageAllocationInfo {
359             width: 512,
360             height: 1024,
361             drm_format: DrmFormat::new(b'X', b'R', b'2', b'4'),
362             flags: RutabagaGrallocFlags::empty().use_scanout(true),
363         };
364 
365         let reqs = gralloc.get_image_memory_requirements(info).unwrap();
366         let min_reqs = canonical_image_requirements(info).unwrap();
367 
368         assert_eq!(reqs.strides[0] >= min_reqs.strides[0], true);
369         assert_eq!(reqs.size >= min_reqs.size, true);
370 
371         let _handle = gralloc.allocate_memory(reqs).unwrap();
372 
373         // Reallocate with same requirements
374         let _handle2 = gralloc.allocate_memory(reqs).unwrap();
375     }
376 
377     #[test]
create_video_buffer()378     fn create_video_buffer() {
379         let gralloc_result = RutabagaGralloc::new();
380         if gralloc_result.is_err() {
381             return;
382         }
383 
384         let mut gralloc = gralloc_result.unwrap();
385 
386         let info = ImageAllocationInfo {
387             width: 512,
388             height: 1024,
389             drm_format: DrmFormat::new(b'N', b'V', b'1', b'2'),
390             flags: RutabagaGrallocFlags::empty().use_linear(true),
391         };
392 
393         let reqs = gralloc.get_image_memory_requirements(info).unwrap();
394         let min_reqs = canonical_image_requirements(info).unwrap();
395 
396         assert_eq!(reqs.strides[0] >= min_reqs.strides[0], true);
397         assert_eq!(reqs.strides[1] >= min_reqs.strides[1], true);
398         assert_eq!(reqs.strides[2], 0);
399         assert_eq!(reqs.strides[3], 0);
400 
401         assert_eq!(reqs.offsets[0] >= min_reqs.offsets[0], true);
402         assert_eq!(reqs.offsets[1] >= min_reqs.offsets[1], true);
403         assert_eq!(reqs.offsets[2], 0);
404         assert_eq!(reqs.offsets[3], 0);
405 
406         assert_eq!(reqs.size >= min_reqs.size, true);
407 
408         let _handle = gralloc.allocate_memory(reqs).unwrap();
409 
410         // Reallocate with same requirements
411         let _handle2 = gralloc.allocate_memory(reqs).unwrap();
412     }
413 
414     #[test]
export_and_map()415     fn export_and_map() {
416         let gralloc_result = RutabagaGralloc::new();
417         if gralloc_result.is_err() {
418             return;
419         }
420 
421         let mut gralloc = gralloc_result.unwrap();
422 
423         let info = ImageAllocationInfo {
424             width: 512,
425             height: 1024,
426             drm_format: DrmFormat::new(b'X', b'R', b'2', b'4'),
427             flags: RutabagaGrallocFlags::empty()
428                 .use_linear(true)
429                 .use_sw_write(true)
430                 .use_sw_read(true),
431         };
432 
433         let mut reqs = gralloc.get_image_memory_requirements(info).unwrap();
434 
435         // Anything else can use the mmap(..) system call.
436         if reqs.vulkan_info.is_none() {
437             return;
438         }
439 
440         let handle = gralloc.allocate_memory(reqs).unwrap();
441         let vulkan_info = reqs.vulkan_info.take().unwrap();
442 
443         let mapping = gralloc
444             .import_and_map(handle, vulkan_info, reqs.size)
445             .unwrap();
446 
447         let addr = mapping.as_ptr();
448         let size = mapping.size();
449 
450         assert_eq!(size as u64, reqs.size);
451         assert_ne!(addr as *const u8, std::ptr::null());
452     }
453 }
454