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 104 trait DeviceExt { 105 /// Get a unique identifier for the device. get_id(&self) -> DeviceId106 fn get_id(&self) -> DeviceId; 107 } 108 109 impl DeviceExt for Device { get_id(&self) -> DeviceId110 fn get_id(&self) -> DeviceId { 111 let properties = self.physical_device().properties(); 112 DeviceId { 113 device_uuid: properties.device_uuid.expect("Vulkan should support uuid"), 114 driver_uuid: properties.driver_uuid.expect("Vulkan should support uuid"), 115 } 116 } 117 } 118 119 impl VulkanoGralloc { 120 /// Returns a new `VulkanGralloc' instance upon success. init() -> RutabagaResult<Box<dyn Gralloc>>121 pub fn init() -> RutabagaResult<Box<dyn Gralloc>> { 122 // Initialization copied from triangle.rs in Vulkano. Look there for a more detailed 123 // explanation of VK initialization. 124 let library = VulkanLibrary::new()?; 125 126 let instance_extensions = InstanceExtensions { 127 khr_external_memory_capabilities: true, 128 khr_get_physical_device_properties2: true, 129 ..InstanceExtensions::empty() 130 }; 131 let instance = Instance::new( 132 library, 133 InstanceCreateInfo { 134 enabled_extensions: instance_extensions, 135 max_api_version: Some(Version::V1_1), 136 ..Default::default() 137 }, 138 )?; 139 140 let mut devices: Map<PhysicalDeviceType, Arc<Device>> = Default::default(); 141 let mut device_by_id: Map<DeviceId, Arc<Device>> = Default::default(); 142 let mut has_integrated_gpu = false; 143 144 for physical in instance.enumerate_physical_devices()? { 145 let queue_family_index = match physical.queue_family_properties().iter().position(|q| { 146 // We take the first queue family that supports graphics. 147 q.queue_flags.intersects(QueueFlags::GRAPHICS) 148 }) { 149 Some(family) => family, 150 None => { 151 warn!( 152 "Skipping Vulkano for {} due to no graphics queue", 153 physical.properties().device_name 154 ); 155 continue; 156 } 157 }; 158 159 let supported_extensions = physical.supported_extensions(); 160 161 let desired_extensions = Self::get_desired_device_extensions(); 162 163 let intersection = supported_extensions.intersection(&desired_extensions); 164 165 if let Ok((device, mut _queues)) = Device::new( 166 physical.clone(), 167 DeviceCreateInfo { 168 enabled_extensions: intersection, 169 queue_create_infos: vec![QueueCreateInfo { 170 queue_family_index: queue_family_index as u32, 171 ..Default::default() 172 }], 173 ..Default::default() 174 }, 175 ) { 176 let device_type = device.physical_device().properties().device_type; 177 if device_type == PhysicalDeviceType::IntegratedGpu { 178 has_integrated_gpu = true 179 } 180 181 // If we have two devices of the same type (two integrated GPUs), the old value is 182 // dropped. Vulkano is verbose enough such that a keener selection algorithm may 183 // be used, but the need for such complexity does not seem to exist now. 184 devices.insert(device_type, device.clone()); 185 device_by_id.insert(device.get_id(), device); 186 }; 187 } 188 189 if devices.is_empty() { 190 return Err(RutabagaError::SpecViolation( 191 "no matching VK devices available", 192 )); 193 } 194 195 Ok(Box::new(VulkanoGralloc { 196 devices, 197 device_by_id, 198 has_integrated_gpu, 199 })) 200 } 201 202 // This function is used safely in this module because gralloc does not: 203 // 204 // (1) bind images to any memory. 205 // (2) transition the layout of images. 206 // (3) transfer ownership of images between queues. 207 // 208 // In addition, we trust Vulkano to validate image parameters are within the Vulkan spec. 209 // TODO(tutankhamen): Do we still need a separate MemoryRequirements? create_image( &mut self, info: ImageAllocationInfo, ) -> RutabagaResult<(Arc<image::sys::RawImage>, MemoryRequirements)>210 unsafe fn create_image( 211 &mut self, 212 info: ImageAllocationInfo, 213 ) -> RutabagaResult<(Arc<image::sys::RawImage>, MemoryRequirements)> { 214 let device = if self.has_integrated_gpu { 215 self.devices 216 .get(&PhysicalDeviceType::IntegratedGpu) 217 .ok_or(RutabagaError::InvalidGrallocGpuType)? 218 } else { 219 self.devices 220 .get(&PhysicalDeviceType::DiscreteGpu) 221 .ok_or(RutabagaError::InvalidGrallocGpuType)? 222 }; 223 224 let usage = match info.flags.uses_rendering() { 225 true => ImageUsage::COLOR_ATTACHMENT, 226 false => ImageUsage::SAMPLED, 227 }; 228 229 // Reasonable bounds on image width. 230 if info.width == 0 || info.width > 4096 { 231 return Err(RutabagaError::InvalidGrallocDimensions); 232 } 233 234 // Reasonable bounds on image height. 235 if info.height == 0 || info.height > 4096 { 236 return Err(RutabagaError::InvalidGrallocDimensions); 237 } 238 239 let vulkan_format = info.drm_format.vulkan_format()?; 240 let raw_image = Arc::new(image::sys::RawImage::new( 241 device.clone(), 242 image::sys::ImageCreateInfo { 243 dimensions: ImageDimensions::Dim2d { 244 width: info.width, 245 height: info.height, 246 array_layers: 1, 247 }, 248 format: Some(vulkan_format), 249 samples: SampleCount::Sample1, 250 usage, 251 mip_levels: 1, 252 sharing: Sharing::Exclusive, 253 ..Default::default() 254 }, 255 )?); 256 257 // Won't panic since this not a swapchain image. 258 let memory_requirements = raw_image.memory_requirements()[0]; 259 Ok((raw_image, memory_requirements)) 260 } 261 } 262 263 impl Gralloc for VulkanoGralloc { supports_external_gpu_memory(&self) -> bool264 fn supports_external_gpu_memory(&self) -> bool { 265 for device in self.devices.values() { 266 if !device.enabled_extensions().khr_external_memory { 267 return false; 268 } 269 } 270 271 true 272 } 273 supports_dmabuf(&self) -> bool274 fn supports_dmabuf(&self) -> bool { 275 for device in self.devices.values() { 276 if !device.enabled_extensions().ext_external_memory_dma_buf { 277 return false; 278 } 279 } 280 281 true 282 } 283 get_image_memory_requirements( &mut self, info: ImageAllocationInfo, ) -> RutabagaResult<ImageMemoryRequirements>284 fn get_image_memory_requirements( 285 &mut self, 286 info: ImageAllocationInfo, 287 ) -> RutabagaResult<ImageMemoryRequirements> { 288 let mut reqs: ImageMemoryRequirements = Default::default(); 289 290 let (raw_image, memory_requirements) = unsafe { self.create_image(info)? }; 291 292 let device_type = if self.has_integrated_gpu { 293 &PhysicalDeviceType::IntegratedGpu 294 } else { 295 &PhysicalDeviceType::DiscreteGpu 296 }; 297 298 let device = self 299 .devices 300 .get(device_type) 301 .ok_or(RutabagaError::InvalidGrallocGpuType)?; 302 303 let planar_layout = info.drm_format.planar_layout()?; 304 305 // Safe because we created the image with the linear bit set and verified the format is 306 // not a depth or stencil format. We are also using the correct image aspect. Vulkano 307 // will panic if we are not. 308 for plane in 0..planar_layout.num_planes { 309 let aspect = info.drm_format.vulkan_image_aspect(plane)?; 310 let layout = raw_image.subresource_layout(aspect, 0, 0)?; 311 reqs.strides[plane] = layout.row_pitch as u32; 312 reqs.offsets[plane] = layout.offset as u32; 313 } 314 315 let need_visible = info.flags.host_visible(); 316 let want_cached = info.flags.host_cached(); 317 318 let (memory_type_index, memory_type) = { 319 let filter = |current_type: &MemoryType| { 320 if need_visible 321 && !current_type 322 .property_flags 323 .intersects(MemoryPropertyFlags::HOST_VISIBLE) 324 { 325 return AllocFromRequirementsFilter::Forbidden; 326 } 327 328 if !need_visible 329 && current_type 330 .property_flags 331 .intersects(MemoryPropertyFlags::DEVICE_LOCAL) 332 { 333 return AllocFromRequirementsFilter::Preferred; 334 } 335 336 if need_visible 337 && want_cached 338 && current_type 339 .property_flags 340 .intersects(MemoryPropertyFlags::HOST_CACHED) 341 { 342 return AllocFromRequirementsFilter::Preferred; 343 } 344 345 if need_visible 346 && !want_cached 347 && current_type 348 .property_flags 349 .intersects(MemoryPropertyFlags::HOST_COHERENT) 350 && !current_type 351 .property_flags 352 .intersects(MemoryPropertyFlags::HOST_CACHED) 353 { 354 return AllocFromRequirementsFilter::Preferred; 355 } 356 357 AllocFromRequirementsFilter::Allowed 358 }; 359 360 let first_loop = device 361 .physical_device() 362 .memory_properties() 363 .memory_types 364 .iter() 365 .enumerate() 366 .map(|(i, t)| (i, t, AllocFromRequirementsFilter::Preferred)); 367 let second_loop = device 368 .physical_device() 369 .memory_properties() 370 .memory_types 371 .iter() 372 .enumerate() 373 .map(|(i, t)| (i, t, AllocFromRequirementsFilter::Allowed)); 374 let found_type = first_loop 375 .chain(second_loop) 376 .filter(|&(i, _, _)| (memory_requirements.memory_type_bits & (1 << i)) != 0) 377 .find(|&(_, t, rq)| filter(t) == rq) 378 .ok_or(RutabagaError::SpecViolation( 379 "unable to find required memory type", 380 ))?; 381 (found_type.0, found_type.1) 382 }; 383 384 reqs.info = info; 385 reqs.size = memory_requirements.layout.size() as u64; 386 387 if memory_type 388 .property_flags 389 .intersects(MemoryPropertyFlags::HOST_VISIBLE) 390 { 391 if memory_type 392 .property_flags 393 .intersects(MemoryPropertyFlags::HOST_CACHED) 394 { 395 reqs.map_info = RUTABAGA_MAP_CACHE_CACHED; 396 } else if memory_type 397 .property_flags 398 .intersects(MemoryPropertyFlags::HOST_COHERENT) 399 { 400 reqs.map_info = RUTABAGA_MAP_CACHE_WC; 401 } 402 } 403 404 reqs.vulkan_info = Some(VulkanInfo { 405 memory_idx: memory_type_index as u32, 406 device_id: device.get_id(), 407 }); 408 409 Ok(reqs) 410 } 411 allocate_memory(&mut self, reqs: ImageMemoryRequirements) -> RutabagaResult<RutabagaHandle>412 fn allocate_memory(&mut self, reqs: ImageMemoryRequirements) -> RutabagaResult<RutabagaHandle> { 413 let (raw_image, memory_requirements) = unsafe { self.create_image(reqs.info)? }; 414 415 let vulkan_info = reqs.vulkan_info.ok_or(RutabagaError::InvalidVulkanInfo)?; 416 417 let device = if self.has_integrated_gpu { 418 self.devices 419 .get(&PhysicalDeviceType::IntegratedGpu) 420 .ok_or(RutabagaError::InvalidGrallocGpuType)? 421 } else { 422 self.devices 423 .get(&PhysicalDeviceType::DiscreteGpu) 424 .ok_or(RutabagaError::InvalidGrallocGpuType)? 425 }; 426 427 if vulkan_info.memory_idx as usize 428 >= device 429 .physical_device() 430 .memory_properties() 431 .memory_types 432 .len() 433 { 434 return Err(RutabagaError::InvalidVulkanInfo); 435 } 436 437 let (export_handle_type, export_handle_types, rutabaga_type) = 438 match device.enabled_extensions().ext_external_memory_dma_buf { 439 true => ( 440 ExternalMemoryHandleType::DmaBuf, 441 ExternalMemoryHandleTypes::DMA_BUF, 442 RUTABAGA_MEM_HANDLE_TYPE_DMABUF, 443 ), 444 false => ( 445 ExternalMemoryHandleType::OpaqueFd, 446 ExternalMemoryHandleTypes::OPAQUE_FD, 447 RUTABAGA_MEM_HANDLE_TYPE_OPAQUE_FD, 448 ), 449 }; 450 451 let dedicated_allocation = match device.enabled_extensions().khr_dedicated_allocation { 452 true => { 453 if memory_requirements.prefers_dedicated_allocation { 454 Some(DedicatedAllocation::Image(&raw_image)) 455 } else { 456 None 457 } 458 } 459 false => None, 460 }; 461 462 let device_memory = DeviceMemory::allocate( 463 device.clone(), 464 MemoryAllocateInfo { 465 allocation_size: reqs.size, 466 memory_type_index: vulkan_info.memory_idx, 467 dedicated_allocation, 468 export_handle_types, 469 ..Default::default() 470 }, 471 )?; 472 473 let descriptor = device_memory.export_fd(export_handle_type)?.into(); 474 475 Ok(RutabagaHandle { 476 os_handle: descriptor, 477 handle_type: rutabaga_type, 478 }) 479 } 480 481 /// 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>>482 fn import_and_map( 483 &mut self, 484 handle: RutabagaHandle, 485 vulkan_info: VulkanInfo, 486 size: u64, 487 ) -> RutabagaResult<Box<dyn MappedRegion>> { 488 let device = self 489 .device_by_id 490 .get(&vulkan_info.device_id) 491 .ok_or(RutabagaError::InvalidVulkanInfo)?; 492 493 let device_memory = unsafe { 494 VulkanoGralloc::import_memory( 495 device.clone(), 496 MemoryAllocateInfo { 497 allocation_size: size, 498 memory_type_index: vulkan_info.memory_idx, 499 ..Default::default() 500 }, 501 handle, 502 )? 503 }; 504 505 let mapped_memory = MappedDeviceMemory::new(device_memory, 0..size)?; 506 507 Ok(Box::new(VulkanoMapping::new( 508 mapped_memory, 509 size.try_into()?, 510 ))) 511 } 512 } 513 514 // Vulkano should really define an universal type that wraps all these errors, say 515 // "VulkanoError(e)". 516 impl From<InstanceCreationError> for RutabagaError { from(e: InstanceCreationError) -> RutabagaError517 fn from(e: InstanceCreationError) -> RutabagaError { 518 RutabagaError::VkInstanceCreationError(e) 519 } 520 } 521 522 impl From<ImageError> for RutabagaError { from(e: ImageError) -> RutabagaError523 fn from(e: ImageError) -> RutabagaError { 524 RutabagaError::VkImageCreationError(e) 525 } 526 } 527 528 impl From<DeviceCreationError> for RutabagaError { from(e: DeviceCreationError) -> RutabagaError529 fn from(e: DeviceCreationError) -> RutabagaError { 530 RutabagaError::VkDeviceCreationError(e) 531 } 532 } 533 534 impl From<DeviceMemoryError> for RutabagaError { from(e: DeviceMemoryError) -> RutabagaError535 fn from(e: DeviceMemoryError) -> RutabagaError { 536 RutabagaError::VkDeviceMemoryError(e) 537 } 538 } 539 540 impl From<MemoryMapError> for RutabagaError { from(e: MemoryMapError) -> RutabagaError541 fn from(e: MemoryMapError) -> RutabagaError { 542 RutabagaError::VkMemoryMapError(e) 543 } 544 } 545 546 impl From<LoadingError> for RutabagaError { from(e: LoadingError) -> RutabagaError547 fn from(e: LoadingError) -> RutabagaError { 548 RutabagaError::VkLoadingError(e) 549 } 550 } 551 552 impl From<VulkanError> for RutabagaError { from(e: VulkanError) -> RutabagaError553 fn from(e: VulkanError) -> RutabagaError { 554 RutabagaError::VkError(e) 555 } 556 } 557