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 //! vulkano_gralloc: Implements swapchain allocation and memory mapping 6 //! using Vulkano. 7 //! 8 //! External code found at https://github.com/vulkano-rs/vulkano. 9 10 #![cfg(feature = "vulkano")] 11 12 mod sys; 13 14 use std::collections::HashMap as Map; 15 use std::convert::TryInto; 16 use std::sync::Arc; 17 18 use log::warn; 19 use vulkano::device::physical::PhysicalDeviceType; 20 use vulkano::device::Device; 21 use vulkano::device::DeviceCreateInfo; 22 use vulkano::device::DeviceCreationError; 23 use vulkano::device::QueueCreateInfo; 24 use vulkano::device::QueueFlags; 25 use vulkano::image; 26 use vulkano::image::ImageDimensions; 27 use vulkano::image::ImageError; 28 use vulkano::image::ImageUsage; 29 use vulkano::image::SampleCount; 30 use vulkano::instance::Instance; 31 use vulkano::instance::InstanceCreateInfo; 32 use vulkano::instance::InstanceCreationError; 33 use vulkano::instance::InstanceExtensions; 34 use vulkano::instance::Version; 35 use vulkano::memory::DedicatedAllocation; 36 use vulkano::memory::DeviceMemory; 37 use vulkano::memory::DeviceMemoryError; 38 use vulkano::memory::ExternalMemoryHandleType; 39 use vulkano::memory::ExternalMemoryHandleTypes; 40 use vulkano::memory::MappedDeviceMemory; 41 use vulkano::memory::MemoryAllocateInfo; 42 use vulkano::memory::MemoryMapError; 43 use vulkano::memory::MemoryPropertyFlags; 44 use vulkano::memory::MemoryRequirements; 45 use vulkano::memory::MemoryType; 46 use vulkano::sync::Sharing; 47 use vulkano::LoadingError; 48 use vulkano::VulkanError; 49 use vulkano::VulkanLibrary; 50 51 use crate::rutabaga_gralloc::gralloc::Gralloc; 52 use crate::rutabaga_gralloc::gralloc::ImageAllocationInfo; 53 use crate::rutabaga_gralloc::gralloc::ImageMemoryRequirements; 54 use crate::rutabaga_os::MappedRegion; 55 use crate::rutabaga_utils::*; 56 57 /// A convenience enum for allocation 58 #[derive(Debug, Copy, Clone, PartialEq, Eq)] 59 pub enum AllocFromRequirementsFilter { 60 Preferred, 61 Allowed, 62 Forbidden, 63 } 64 65 /// A gralloc implementation capable of allocation `VkDeviceMemory`. 66 pub struct VulkanoGralloc { 67 devices: Map<PhysicalDeviceType, Arc<Device>>, 68 device_by_id: Map<DeviceId, Arc<Device>>, 69 has_integrated_gpu: bool, 70 } 71 72 struct VulkanoMapping { 73 mapped_memory: MappedDeviceMemory, 74 size: usize, 75 } 76 77 impl VulkanoMapping { new(mapped_memory: MappedDeviceMemory, size: usize) -> VulkanoMapping78 pub fn new(mapped_memory: MappedDeviceMemory, size: usize) -> VulkanoMapping { 79 VulkanoMapping { 80 mapped_memory, 81 size, 82 } 83 } 84 } 85 86 unsafe impl MappedRegion for VulkanoMapping { 87 /// Used for passing this region for hypervisor memory mappings. We trust crosvm to use this 88 /// safely. as_ptr(&self) -> *mut u889 fn as_ptr(&self) -> *mut u8 { 90 unsafe { 91 // Will not panic since the requested range of the device memory was verified on 92 // creation 93 let x = self.mapped_memory.write(0..self.size as u64).unwrap(); 94 x.as_mut_ptr() 95 } 96 } 97 98 /// Returns the size of the memory region in bytes. size(&self) -> usize99 fn size(&self) -> usize { 100 self.size 101 } 102 103 /// Returns rutabaga mapping representation of the region as_rutabaga_mapping(&self) -> RutabagaMapping104 fn as_rutabaga_mapping(&self) -> RutabagaMapping { 105 let ptr: u64 = unsafe { 106 // Will not panic since the requested range of the device memory was verified on 107 // creation 108 let x = self.mapped_memory.write(0..self.size as u64).unwrap(); 109 x.as_mut_ptr() as u64 110 }; 111 112 RutabagaMapping { 113 ptr, 114 size: self.size as u64, 115 } 116 } 117 } 118 119 trait DeviceExt { 120 /// Get a unique identifier for the device. get_id(&self) -> DeviceId121 fn get_id(&self) -> DeviceId; 122 } 123 124 impl DeviceExt for Device { get_id(&self) -> DeviceId125 fn get_id(&self) -> DeviceId { 126 let properties = self.physical_device().properties(); 127 DeviceId { 128 device_uuid: properties.device_uuid.expect("Vulkan should support uuid"), 129 driver_uuid: properties.driver_uuid.expect("Vulkan should support uuid"), 130 } 131 } 132 } 133 134 impl VulkanoGralloc { 135 /// Returns a new `VulkanGralloc' instance upon success. init() -> RutabagaResult<Box<dyn Gralloc>>136 pub fn init() -> RutabagaResult<Box<dyn Gralloc>> { 137 // Initialization copied from triangle.rs in Vulkano. Look there for a more detailed 138 // explanation of VK initialization. 139 let library = VulkanLibrary::new()?; 140 141 let instance_extensions = InstanceExtensions { 142 khr_external_memory_capabilities: true, 143 khr_get_physical_device_properties2: true, 144 ..InstanceExtensions::empty() 145 }; 146 let instance = Instance::new( 147 library, 148 InstanceCreateInfo { 149 enabled_extensions: instance_extensions, 150 max_api_version: Some(Version::V1_1), 151 ..Default::default() 152 }, 153 )?; 154 155 let mut devices: Map<PhysicalDeviceType, Arc<Device>> = Default::default(); 156 let mut device_by_id: Map<DeviceId, Arc<Device>> = Default::default(); 157 let mut has_integrated_gpu = false; 158 159 for physical in instance.enumerate_physical_devices()? { 160 let queue_family_index = match physical.queue_family_properties().iter().position(|q| { 161 // We take the first queue family that supports graphics. 162 q.queue_flags.intersects(QueueFlags::GRAPHICS) 163 }) { 164 Some(family) => family, 165 None => { 166 warn!( 167 "Skipping Vulkano for {} due to no graphics queue", 168 physical.properties().device_name 169 ); 170 continue; 171 } 172 }; 173 174 let supported_extensions = physical.supported_extensions(); 175 176 let desired_extensions = Self::get_desired_device_extensions(); 177 178 let intersection = supported_extensions.intersection(&desired_extensions); 179 180 if let Ok((device, mut _queues)) = Device::new( 181 physical.clone(), 182 DeviceCreateInfo { 183 enabled_extensions: intersection, 184 queue_create_infos: vec![QueueCreateInfo { 185 queue_family_index: queue_family_index as u32, 186 ..Default::default() 187 }], 188 ..Default::default() 189 }, 190 ) { 191 let device_type = device.physical_device().properties().device_type; 192 if device_type == PhysicalDeviceType::IntegratedGpu { 193 has_integrated_gpu = true 194 } 195 196 // If we have two devices of the same type (two integrated GPUs), the old value is 197 // dropped. Vulkano is verbose enough such that a keener selection algorithm may 198 // be used, but the need for such complexity does not seem to exist now. 199 devices.insert(device_type, device.clone()); 200 device_by_id.insert(device.get_id(), device); 201 }; 202 } 203 204 if devices.is_empty() { 205 return Err(RutabagaError::SpecViolation( 206 "no matching VK devices available", 207 )); 208 } 209 210 Ok(Box::new(VulkanoGralloc { 211 devices, 212 device_by_id, 213 has_integrated_gpu, 214 })) 215 } 216 217 // This function is used safely in this module because gralloc does not: 218 // 219 // (1) bind images to any memory. 220 // (2) transition the layout of images. 221 // (3) transfer ownership of images between queues. 222 // 223 // In addition, we trust Vulkano to validate image parameters are within the Vulkan spec. 224 // TODO(tutankhamen): Do we still need a separate MemoryRequirements? create_image( &mut self, info: ImageAllocationInfo, ) -> RutabagaResult<(Arc<image::sys::RawImage>, MemoryRequirements)>225 unsafe fn create_image( 226 &mut self, 227 info: ImageAllocationInfo, 228 ) -> RutabagaResult<(Arc<image::sys::RawImage>, MemoryRequirements)> { 229 let device = if self.has_integrated_gpu { 230 self.devices 231 .get(&PhysicalDeviceType::IntegratedGpu) 232 .ok_or(RutabagaError::InvalidGrallocGpuType)? 233 } else { 234 self.devices 235 .get(&PhysicalDeviceType::DiscreteGpu) 236 .ok_or(RutabagaError::InvalidGrallocGpuType)? 237 }; 238 239 let usage = match info.flags.uses_rendering() { 240 true => ImageUsage::COLOR_ATTACHMENT, 241 false => ImageUsage::SAMPLED, 242 }; 243 244 // Reasonable bounds on image width. 245 if info.width == 0 || info.width > 4096 { 246 return Err(RutabagaError::InvalidGrallocDimensions); 247 } 248 249 // Reasonable bounds on image height. 250 if info.height == 0 || info.height > 4096 { 251 return Err(RutabagaError::InvalidGrallocDimensions); 252 } 253 254 let vulkan_format = info.drm_format.vulkan_format()?; 255 let raw_image = Arc::new(image::sys::RawImage::new( 256 device.clone(), 257 image::sys::ImageCreateInfo { 258 dimensions: ImageDimensions::Dim2d { 259 width: info.width, 260 height: info.height, 261 array_layers: 1, 262 }, 263 format: Some(vulkan_format), 264 samples: SampleCount::Sample1, 265 usage, 266 mip_levels: 1, 267 sharing: Sharing::Exclusive, 268 ..Default::default() 269 }, 270 )?); 271 272 // Won't panic since this not a swapchain image. 273 let memory_requirements = raw_image.memory_requirements()[0]; 274 Ok((raw_image, memory_requirements)) 275 } 276 } 277 278 impl Gralloc for VulkanoGralloc { supports_external_gpu_memory(&self) -> bool279 fn supports_external_gpu_memory(&self) -> bool { 280 for device in self.devices.values() { 281 if !device.enabled_extensions().khr_external_memory { 282 return false; 283 } 284 } 285 286 true 287 } 288 supports_dmabuf(&self) -> bool289 fn supports_dmabuf(&self) -> bool { 290 for device in self.devices.values() { 291 if !device.enabled_extensions().ext_external_memory_dma_buf { 292 return false; 293 } 294 } 295 296 true 297 } 298 get_image_memory_requirements( &mut self, info: ImageAllocationInfo, ) -> RutabagaResult<ImageMemoryRequirements>299 fn get_image_memory_requirements( 300 &mut self, 301 info: ImageAllocationInfo, 302 ) -> RutabagaResult<ImageMemoryRequirements> { 303 let mut reqs: ImageMemoryRequirements = Default::default(); 304 305 let (raw_image, memory_requirements) = unsafe { self.create_image(info)? }; 306 307 let device_type = if self.has_integrated_gpu { 308 &PhysicalDeviceType::IntegratedGpu 309 } else { 310 &PhysicalDeviceType::DiscreteGpu 311 }; 312 313 let device = self 314 .devices 315 .get(device_type) 316 .ok_or(RutabagaError::InvalidGrallocGpuType)?; 317 318 let planar_layout = info.drm_format.planar_layout()?; 319 320 // Safe because we created the image with the linear bit set and verified the format is 321 // not a depth or stencil format. We are also using the correct image aspect. Vulkano 322 // will panic if we are not. 323 for plane in 0..planar_layout.num_planes { 324 let aspect = info.drm_format.vulkan_image_aspect(plane)?; 325 let layout = raw_image.subresource_layout(aspect, 0, 0)?; 326 reqs.strides[plane] = layout.row_pitch as u32; 327 reqs.offsets[plane] = layout.offset as u32; 328 } 329 330 let need_visible = info.flags.host_visible(); 331 let want_cached = info.flags.host_cached(); 332 333 let (memory_type_index, memory_type) = { 334 let filter = |current_type: &MemoryType| { 335 if need_visible 336 && !current_type 337 .property_flags 338 .intersects(MemoryPropertyFlags::HOST_VISIBLE) 339 { 340 return AllocFromRequirementsFilter::Forbidden; 341 } 342 343 if !need_visible 344 && current_type 345 .property_flags 346 .intersects(MemoryPropertyFlags::DEVICE_LOCAL) 347 { 348 return AllocFromRequirementsFilter::Preferred; 349 } 350 351 if need_visible 352 && want_cached 353 && current_type 354 .property_flags 355 .intersects(MemoryPropertyFlags::HOST_CACHED) 356 { 357 return AllocFromRequirementsFilter::Preferred; 358 } 359 360 if need_visible 361 && !want_cached 362 && current_type 363 .property_flags 364 .intersects(MemoryPropertyFlags::HOST_COHERENT) 365 && !current_type 366 .property_flags 367 .intersects(MemoryPropertyFlags::HOST_CACHED) 368 { 369 return AllocFromRequirementsFilter::Preferred; 370 } 371 372 AllocFromRequirementsFilter::Allowed 373 }; 374 375 let first_loop = device 376 .physical_device() 377 .memory_properties() 378 .memory_types 379 .iter() 380 .enumerate() 381 .map(|(i, t)| (i, t, AllocFromRequirementsFilter::Preferred)); 382 let second_loop = device 383 .physical_device() 384 .memory_properties() 385 .memory_types 386 .iter() 387 .enumerate() 388 .map(|(i, t)| (i, t, AllocFromRequirementsFilter::Allowed)); 389 let found_type = first_loop 390 .chain(second_loop) 391 .filter(|&(i, _, _)| (memory_requirements.memory_type_bits & (1 << i)) != 0) 392 .find(|&(_, t, rq)| filter(t) == rq) 393 .ok_or(RutabagaError::SpecViolation( 394 "unable to find required memory type", 395 ))?; 396 (found_type.0, found_type.1) 397 }; 398 399 reqs.info = info; 400 reqs.size = memory_requirements.layout.size() as u64; 401 402 if memory_type 403 .property_flags 404 .intersects(MemoryPropertyFlags::HOST_VISIBLE) 405 { 406 if memory_type 407 .property_flags 408 .intersects(MemoryPropertyFlags::HOST_CACHED) 409 { 410 reqs.map_info = RUTABAGA_MAP_CACHE_CACHED; 411 } else if memory_type 412 .property_flags 413 .intersects(MemoryPropertyFlags::HOST_COHERENT) 414 { 415 reqs.map_info = RUTABAGA_MAP_CACHE_WC; 416 } 417 } 418 419 reqs.vulkan_info = Some(VulkanInfo { 420 memory_idx: memory_type_index as u32, 421 device_id: device.get_id(), 422 }); 423 424 Ok(reqs) 425 } 426 allocate_memory(&mut self, reqs: ImageMemoryRequirements) -> RutabagaResult<RutabagaHandle>427 fn allocate_memory(&mut self, reqs: ImageMemoryRequirements) -> RutabagaResult<RutabagaHandle> { 428 let (raw_image, memory_requirements) = unsafe { self.create_image(reqs.info)? }; 429 430 let vulkan_info = reqs.vulkan_info.ok_or(RutabagaError::InvalidVulkanInfo)?; 431 432 let device = if self.has_integrated_gpu { 433 self.devices 434 .get(&PhysicalDeviceType::IntegratedGpu) 435 .ok_or(RutabagaError::InvalidGrallocGpuType)? 436 } else { 437 self.devices 438 .get(&PhysicalDeviceType::DiscreteGpu) 439 .ok_or(RutabagaError::InvalidGrallocGpuType)? 440 }; 441 442 if vulkan_info.memory_idx as usize 443 >= device 444 .physical_device() 445 .memory_properties() 446 .memory_types 447 .len() 448 { 449 return Err(RutabagaError::InvalidVulkanInfo); 450 } 451 452 let (export_handle_type, export_handle_types, rutabaga_type) = 453 match device.enabled_extensions().ext_external_memory_dma_buf { 454 true => ( 455 ExternalMemoryHandleType::DmaBuf, 456 ExternalMemoryHandleTypes::DMA_BUF, 457 RUTABAGA_MEM_HANDLE_TYPE_DMABUF, 458 ), 459 false => ( 460 ExternalMemoryHandleType::OpaqueFd, 461 ExternalMemoryHandleTypes::OPAQUE_FD, 462 RUTABAGA_MEM_HANDLE_TYPE_OPAQUE_FD, 463 ), 464 }; 465 466 let dedicated_allocation = match device.enabled_extensions().khr_dedicated_allocation { 467 true => { 468 if memory_requirements.prefers_dedicated_allocation { 469 Some(DedicatedAllocation::Image(&raw_image)) 470 } else { 471 None 472 } 473 } 474 false => None, 475 }; 476 477 let device_memory = DeviceMemory::allocate( 478 device.clone(), 479 MemoryAllocateInfo { 480 allocation_size: reqs.size, 481 memory_type_index: vulkan_info.memory_idx, 482 dedicated_allocation, 483 export_handle_types, 484 ..Default::default() 485 }, 486 )?; 487 488 let descriptor = device_memory.export_fd(export_handle_type)?.into(); 489 490 Ok(RutabagaHandle { 491 os_handle: descriptor, 492 handle_type: rutabaga_type, 493 }) 494 } 495 496 /// Implementations must map the memory associated with the `resource_id` upon success. import_and_map( &mut self, handle: RutabagaHandle, vulkan_info: VulkanInfo, size: u64, ) -> RutabagaResult<Box<dyn MappedRegion>>497 fn import_and_map( 498 &mut self, 499 handle: RutabagaHandle, 500 vulkan_info: VulkanInfo, 501 size: u64, 502 ) -> RutabagaResult<Box<dyn MappedRegion>> { 503 let device = self 504 .device_by_id 505 .get(&vulkan_info.device_id) 506 .ok_or(RutabagaError::InvalidVulkanInfo)?; 507 508 let device_memory = unsafe { 509 VulkanoGralloc::import_memory( 510 device.clone(), 511 MemoryAllocateInfo { 512 allocation_size: size, 513 memory_type_index: vulkan_info.memory_idx, 514 ..Default::default() 515 }, 516 handle, 517 )? 518 }; 519 520 let mapped_memory = MappedDeviceMemory::new(device_memory, 0..size)?; 521 522 Ok(Box::new(VulkanoMapping::new( 523 mapped_memory, 524 size.try_into()?, 525 ))) 526 } 527 } 528 529 // Vulkano should really define an universal type that wraps all these errors, say 530 // "VulkanoError(e)". 531 impl From<InstanceCreationError> for RutabagaError { from(e: InstanceCreationError) -> RutabagaError532 fn from(e: InstanceCreationError) -> RutabagaError { 533 RutabagaError::VkInstanceCreationError(e) 534 } 535 } 536 537 impl From<ImageError> for RutabagaError { from(e: ImageError) -> RutabagaError538 fn from(e: ImageError) -> RutabagaError { 539 RutabagaError::VkImageCreationError(e) 540 } 541 } 542 543 impl From<DeviceCreationError> for RutabagaError { from(e: DeviceCreationError) -> RutabagaError544 fn from(e: DeviceCreationError) -> RutabagaError { 545 RutabagaError::VkDeviceCreationError(e) 546 } 547 } 548 549 impl From<DeviceMemoryError> for RutabagaError { from(e: DeviceMemoryError) -> RutabagaError550 fn from(e: DeviceMemoryError) -> RutabagaError { 551 RutabagaError::VkDeviceMemoryError(e) 552 } 553 } 554 555 impl From<MemoryMapError> for RutabagaError { from(e: MemoryMapError) -> RutabagaError556 fn from(e: MemoryMapError) -> RutabagaError { 557 RutabagaError::VkMemoryMapError(e) 558 } 559 } 560 561 impl From<LoadingError> for RutabagaError { from(e: LoadingError) -> RutabagaError562 fn from(e: LoadingError) -> RutabagaError { 563 RutabagaError::VkLoadingError(e) 564 } 565 } 566 567 impl From<VulkanError> for RutabagaError { from(e: VulkanError) -> RutabagaError568 fn from(e: VulkanError) -> RutabagaError { 569 RutabagaError::VkError(e) 570 } 571 } 572