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