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