1 // Copyright 2023 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 #![cfg_attr(unix, allow(dead_code))] 6 7 use std::cell::RefCell; 8 use std::collections::HashMap; 9 use std::sync::mpsc::channel; 10 use std::sync::mpsc::Receiver; 11 use std::sync::mpsc::RecvTimeoutError; 12 use std::sync::mpsc::Sender; 13 use std::sync::Arc; 14 use std::time::Duration; 15 16 use anyhow::anyhow; 17 use anyhow::ensure; 18 use anyhow::format_err; 19 use anyhow::Context; 20 use anyhow::Result; 21 use ash::vk::UUID_SIZE; 22 use ash::vk::{self}; 23 use base::error; 24 use base::warn; 25 use base::AsRawDescriptor; 26 use euclid::Box2D; 27 use euclid::Size2D; 28 use euclid::UnknownUnit; 29 use smallvec::SmallVec; 30 31 mod external_image; 32 mod post_worker; 33 mod sys; 34 35 pub use external_image::AcquireImageMemoryBarrier; 36 pub use external_image::ExternalImage; 37 pub use external_image::ExternalImageAccess; 38 pub use external_image::ReleaseImageMemoryBarrier; 39 use post_worker::PostWorker; 40 use post_worker::Timepoint; 41 use sync::create_promise_and_waitable; 42 use sync::Promise; 43 use sync::Waitable; 44 use vulkano::device::Device; 45 use vulkano::image::sys::UnsafeImageCreateInfo; 46 use vulkano::image::ImageCreateFlags; 47 use vulkano::image::ImageDimensions; 48 use vulkano::image::ImageLayout; 49 use vulkano::image::ImageUsage; 50 use vulkano::memory::ExternalMemoryHandleTypes; 51 use vulkano::memory::MemoryAllocateInfo; 52 use vulkano::sync::ExternalSemaphoreHandleTypes; 53 use vulkano::sync::Semaphore; 54 use vulkano::sync::SemaphoreCreateInfo; 55 use vulkano::sync::Sharing; 56 use vulkano::VulkanLibrary; 57 use vulkano::VulkanObject; 58 59 use self::sys::platform::create_post_image_external_memory_handle_types; 60 use self::sys::platform::create_post_image_memory_import_info; 61 use self::sys::platform::import_semaphore_from_descriptor; 62 use self::sys::platform::NativeWindowType; 63 use self::sys::ApplicationState; 64 use self::sys::ApplicationStateBuilder; 65 pub(crate) use self::sys::PlatformWindowEventLoop; 66 use self::sys::Window; 67 use self::sys::WindowEvent; 68 use self::sys::WindowEventLoop; 69 use crate::SemaphoreTimepoint; 70 use crate::VulkanDisplayImageImportMetadata; 71 72 /// Vulkan Safety Notes: 73 /// Most vulkan APIs are unsafe, but even the wrapper APIs like ash and vulkano will mark their 74 /// APIs as unsafe when they cannot ensure that they are 100% obeying the vulkan spec. For the 75 /// purposes of VulkanDisplay, however, we do not consider disobeying the vulkan spec to be unsafe 76 /// in terms of memory safety. Safety comments in these cases will say: 77 /// "Safe irrespective of vulkan spec conformance" 78 /// 79 /// If the function is unsafe for any other reason we will still note why it's safe. 80 81 pub type SemaphoreId = u32; 82 pub type ImageId = u32; 83 84 pub enum UserEvent { 85 GetVulkanDevice(Sender<Arc<Device>>), 86 PostCommand { 87 image: ExternalImage, 88 last_layout_transition: (ImageLayout, ImageLayout), 89 acquire_timepoint: Option<Timepoint>, 90 release_timepoint: Timepoint, 91 image_return: Sender<ExternalImage>, 92 promise: Promise, 93 }, 94 } 95 96 pub struct VulkanState { 97 // Post worker submits renders and posts to vulkan. It needs to be in a RefCell because 98 // process_event cannot take a mutable reference to ApplicationState. 99 post_worker: RefCell<PostWorker>, 100 } 101 102 impl ApplicationState for VulkanState { 103 type UserEvent = UserEvent; 104 105 /// Process events coming from the Window. process_event(&self, event: WindowEvent<Self::UserEvent>)106 fn process_event(&self, event: WindowEvent<Self::UserEvent>) { 107 match event { 108 WindowEvent::User(UserEvent::GetVulkanDevice(sender)) => { 109 sender 110 .send(self.post_worker.borrow().device()) 111 .expect("Should send VkDevice back to the caller successfully."); 112 } 113 WindowEvent::User(UserEvent::PostCommand { 114 image, 115 last_layout_transition, 116 acquire_timepoint, 117 release_timepoint, 118 image_return, 119 promise, 120 }) => { 121 // If this post triggers a resize event then the recursive wndproc call will 122 // call into this function again and trigger another borrow which will panic. 123 // TODO (b/314379499): figure out a way to avoid this 124 let image = self.post_worker.borrow_mut().post( 125 image, 126 last_layout_transition, 127 acquire_timepoint, 128 release_timepoint, 129 ); 130 promise.signal(); 131 image_return 132 .send(image) 133 .expect("Should send ExternalImage back to the caller successfully."); 134 } 135 WindowEvent::Resized => { 136 // If this resize event triggers another resize event then this will fail. 137 // TODO (b/314379499): figure out a way to avoid this 138 if let Err(err) = self.post_worker.borrow_mut().recreate_swapchain() { 139 panic!( 140 concat!( 141 "Failed to recreate the swapchain when handling the Resized window ", 142 "event: {:?}." 143 ), 144 err 145 ); 146 } 147 } 148 } 149 } 150 } 151 152 struct VulkanStateBuilder { 153 vulkan_library: Arc<VulkanLibrary>, 154 device_uuid: [u8; vk::UUID_SIZE], 155 driver_uuid: [u8; vk::UUID_SIZE], 156 } 157 158 impl ApplicationStateBuilder for VulkanStateBuilder { 159 type Target = VulkanState; 160 build<T: Window>(self, window: Arc<T>) -> Result<VulkanState>161 fn build<T: Window>(self, window: Arc<T>) -> Result<VulkanState> { 162 let post_worker = PostWorker::new( 163 self.vulkan_library, 164 &self.device_uuid, 165 &self.driver_uuid, 166 Arc::clone(&window) as _, 167 ) 168 .context("creating the post worker")?; 169 Ok(VulkanState { 170 post_worker: RefCell::new(post_worker), 171 }) 172 } 173 } 174 175 pub struct VulkanDisplayImpl<T: WindowEventLoop<VulkanState>> { 176 ash_device: ash::Device, 177 device: Arc<Device>, 178 window_event_loop: T, 179 imported_semaphores: HashMap<SemaphoreId, Arc<Semaphore>>, 180 imported_images: HashMap<ImageId, ExternalImage>, 181 used_image_receivers: HashMap<ImageId, Receiver<ExternalImage>>, 182 } 183 184 impl<T: WindowEventLoop<VulkanState>> VulkanDisplayImpl<T> { 185 /// # Safety 186 /// The parent window must outlive the lifetime of this object. 187 #[deny(unsafe_op_in_unsafe_fn)] new( vulkan_library: Arc<VulkanLibrary>, parent: NativeWindowType, initial_window_size: &Size2D<i32, UnknownUnit>, device_uuid: [u8; UUID_SIZE], driver_uuid: [u8; UUID_SIZE], ) -> Result<Self>188 pub unsafe fn new( 189 vulkan_library: Arc<VulkanLibrary>, 190 parent: NativeWindowType, 191 initial_window_size: &Size2D<i32, UnknownUnit>, 192 device_uuid: [u8; UUID_SIZE], 193 driver_uuid: [u8; UUID_SIZE], 194 ) -> Result<Self> { 195 let vulkan_state_builder = VulkanStateBuilder { 196 vulkan_library, 197 device_uuid, 198 driver_uuid, 199 }; 200 // SAFETY: Safe because it is guaranteed by the safety requirement of this function that 201 // the parent window outlives the event loop object. 202 let window_event_loop = unsafe { 203 T::create(parent, initial_window_size, vulkan_state_builder) 204 .context("create window and event loop")? 205 }; 206 let (vk_device_tx, vk_device_rx) = channel(); 207 window_event_loop 208 .send_event(UserEvent::GetVulkanDevice(vk_device_tx)) 209 .context("retrieve VkDevice from the window event loop")?; 210 let device = loop { 211 const TIMEOUT: Duration = Duration::from_secs(60); 212 match vk_device_rx.recv_timeout(TIMEOUT) { 213 Ok(value) => break value, 214 215 Err(RecvTimeoutError::Timeout) => { 216 warn!( 217 "Didn't receive the VkDevice from the event loop for {:?}. Retry.", 218 TIMEOUT 219 ); 220 continue; 221 } 222 Err(e) => { 223 return Err(format_err!( 224 "Failed to receive VkDevice from the event loop: {:?}.", 225 e 226 )); 227 } 228 } 229 }; 230 let ash_device = 231 // SAFETY: Safe because we trust the vulkan device we get from the window event loop and 232 // the instance_fn comes from an instance we know is valid because the device was created 233 // with it. 234 unsafe { ash::Device::load(&device.instance().fns().v1_0, device.internal_object()) }; 235 Ok(Self { 236 ash_device, 237 device, 238 window_event_loop, 239 imported_semaphores: Default::default(), 240 imported_images: Default::default(), 241 used_image_receivers: Default::default(), 242 }) 243 } 244 move_window(&self, pos: &Box2D<i32, UnknownUnit>) -> Result<()>245 pub fn move_window(&self, pos: &Box2D<i32, UnknownUnit>) -> Result<()> { 246 self.window_event_loop.move_window(pos) 247 } 248 import_semaphore( &mut self, semaphore_id: SemaphoreId, descriptor: &dyn AsRawDescriptor, ) -> Result<()>249 pub fn import_semaphore( 250 &mut self, 251 semaphore_id: SemaphoreId, 252 descriptor: &dyn AsRawDescriptor, 253 ) -> Result<()> { 254 let mut type_create_info = vk::SemaphoreTypeCreateInfo::builder() 255 .semaphore_type(vk::SemaphoreType::TIMELINE) 256 .initial_value(0) 257 .build(); 258 let create_info = vk::SemaphoreCreateInfo::builder() 259 .push_next(&mut type_create_info) 260 .build(); 261 // SAFETY: Safe because create_info and it's fields are local to this function and outlive 262 // this function call. 263 let semaphore = unsafe { self.ash_device.create_semaphore(&create_info, None) } 264 .context("create timeline semaphore")?; 265 266 let res = import_semaphore_from_descriptor(&self.device, semaphore, descriptor); 267 ensure!( 268 res == vk::Result::SUCCESS, 269 "Failed to import the external handle to the semaphore: {}.", 270 res 271 ); 272 273 // SAFETY: Safe irrespective of vulkan spec conformance 274 let res = unsafe { 275 Semaphore::from_handle( 276 Arc::clone(&self.device), 277 semaphore, 278 SemaphoreCreateInfo { 279 // Note that as of vulkano version 0.34.1, this 280 // export_handle_types field is only used to validate which 281 // export APIs can be used in the future. We do not export 282 // this semaphore so we do not need to specify any export 283 // handle types. 284 export_handle_types: ExternalSemaphoreHandleTypes::empty(), 285 ..Default::default() 286 }, 287 ) 288 }; 289 290 if self 291 .imported_semaphores 292 .insert(semaphore_id, Arc::new(res)) 293 .is_some() 294 { 295 warn!("Reused semaphore_id {}", semaphore_id); 296 } 297 298 Ok(()) 299 } 300 import_image( &mut self, image_id: ImageId, descriptor: &dyn AsRawDescriptor, metadata: VulkanDisplayImageImportMetadata, ) -> Result<()>301 pub fn import_image( 302 &mut self, 303 image_id: ImageId, 304 descriptor: &dyn AsRawDescriptor, 305 metadata: VulkanDisplayImageImportMetadata, 306 ) -> Result<()> { 307 let image_create_flags = metadata.flags; 308 let ImageCreateFlags { 309 sparse_binding, 310 sparse_residency, 311 sparse_aliased, 312 mutable_format, 313 cube_compatible, 314 array_2d_compatible, 315 block_texel_view_compatible, 316 _ne: _, 317 } = vk::ImageCreateFlags::from_raw(image_create_flags) 318 .try_into() 319 .map_err(|_| { 320 format_err!( 321 "Failed to convert flags {} to an image create flags.", 322 image_create_flags 323 ) 324 })?; 325 assert!( 326 !(sparse_binding || sparse_residency || sparse_aliased), 327 "unsupported image create flags {:#x}", 328 image_create_flags 329 ); 330 let image_type = vk::ImageType::from_raw(metadata.image_type); 331 let image_extent = metadata.extent; 332 let image_dimensions = match image_type { 333 vk::ImageType::TYPE_2D => ImageDimensions::Dim2d { 334 width: image_extent.width, 335 height: image_extent.height, 336 array_layers: metadata.array_layers, 337 }, 338 _ => unimplemented!(), 339 }; 340 let format = { 341 let format = metadata.format; 342 vk::Format::from_raw(format) 343 .try_into() 344 .map_err(|_| format_err!("Failed to convert {:#x} to format.", format))? 345 }; 346 let image_samples = { 347 let samples = metadata.samples; 348 vk::SampleCountFlags::from_raw(samples) 349 .try_into() 350 .map_err(|_| { 351 format_err!("Failed to convert {:#x} to sample count flag.", samples) 352 })? 353 }; 354 let image_tiling = { 355 let tiling = metadata.tiling; 356 vk::ImageTiling::from_raw(tiling) 357 .try_into() 358 .map_err(|_| format_err!("Failed to convert {:#x} to image tiling enum.", tiling))? 359 }; 360 let image_usage = { 361 let usage = metadata.usage; 362 vk::ImageUsageFlags::from_raw(usage) 363 .try_into() 364 .map_err(|_| format_err!("Failed to convert {:#x} to image usage.", usage))? 365 }; 366 let image_sharing = { 367 let sharing_mode = metadata.sharing_mode; 368 match vk::SharingMode::from_raw(sharing_mode) { 369 vk::SharingMode::EXCLUSIVE => Sharing::Exclusive, 370 vk::SharingMode::CONCURRENT => { 371 let mut queue_family_indices = SmallVec::new(); 372 queue_family_indices.copy_from_slice(&metadata.queue_family_indices); 373 Sharing::Concurrent(queue_family_indices) 374 } 375 _ => return Err(format_err!("Invalid sharing mode {:#x}.", sharing_mode)), 376 } 377 }; 378 let image_initial_layout = { 379 let initial_layout = metadata.initial_layout; 380 vk::ImageLayout::from_raw(initial_layout) 381 .try_into() 382 .map_err(|_| { 383 format_err!( 384 "Failed to convert the initial layout {:#x} to an image layout.", 385 initial_layout 386 ) 387 })? 388 }; 389 390 let image = ExternalImage::import( 391 &self.device, 392 UnsafeImageCreateInfo { 393 dimensions: image_dimensions, 394 format: Some(format), 395 mip_levels: metadata.mip_levels, 396 samples: image_samples, 397 tiling: image_tiling, 398 usage: image_usage, 399 stencil_usage: ImageUsage::empty(), 400 sharing: image_sharing, 401 initial_layout: image_initial_layout, 402 external_memory_handle_types: create_post_image_external_memory_handle_types(), 403 mutable_format, 404 cube_compatible, 405 array_2d_compatible, 406 block_texel_view_compatible, 407 ..Default::default() 408 }, 409 MemoryAllocateInfo { 410 allocation_size: metadata.allocation_size, 411 memory_type_index: metadata.memory_type_index, 412 export_handle_types: ExternalMemoryHandleTypes::empty(), 413 ..Default::default() 414 }, 415 create_post_image_memory_import_info(descriptor), 416 metadata.dedicated_allocation, 417 0, 418 ) 419 .context("import the composition result image")?; 420 421 if self.imported_images.insert(image_id, image).is_some() { 422 warn!("Reused image_id {}", image_id); 423 } 424 Ok(()) 425 } 426 delete_imported_image_or_semaphore(&mut self, import_id: u32)427 pub fn delete_imported_image_or_semaphore(&mut self, import_id: u32) { 428 // Import ids are shared between images and semaphores, so first try to remove from 429 // self.imported_sempahores, and if that returns none then try to remove from images. 430 if self.imported_semaphores.remove(&import_id).is_none() { 431 if let Some(receiver) = self.used_image_receivers.remove(&import_id) { 432 if let Err(e) = receiver.recv() { 433 error!("Failed to receive used image from post worker: {}", e); 434 } 435 } else if self.imported_images.remove(&import_id).is_none() { 436 error!("Import id {} has not been imported", import_id); 437 } 438 } 439 } 440 post( &mut self, image_id: ImageId, last_layout_transition: (i32, i32), acquire_semaphore: Option<SemaphoreTimepoint>, release_semaphore: SemaphoreTimepoint, ) -> Result<Waitable>441 pub fn post( 442 &mut self, 443 image_id: ImageId, 444 last_layout_transition: (i32, i32), 445 acquire_semaphore: Option<SemaphoreTimepoint>, 446 release_semaphore: SemaphoreTimepoint, 447 ) -> Result<Waitable> { 448 let image = if let Some(receiver) = self.used_image_receivers.remove(&image_id) { 449 receiver 450 .recv() 451 .context("failed to receive used image from post worker")? 452 } else { 453 self.imported_images 454 .remove(&image_id) 455 .ok_or(anyhow!("Image id {} has not been imported", image_id))? 456 }; 457 458 let acquire_timepoint = 459 if let Some(SemaphoreTimepoint { import_id, value }) = acquire_semaphore { 460 let semaphore = self 461 .imported_semaphores 462 .get(&import_id) 463 .ok_or(anyhow!("Semaphore id {} has not been imported", import_id))?; 464 Some(Timepoint { 465 semaphore: semaphore.clone(), 466 value, 467 }) 468 } else { 469 None 470 }; 471 472 let release_timepoint = { 473 let semaphore = self 474 .imported_semaphores 475 .get(&release_semaphore.import_id) 476 .ok_or(anyhow!( 477 "Semaphore id {} has not been imported", 478 release_semaphore.import_id 479 ))?; 480 Timepoint { 481 semaphore: semaphore.clone(), 482 value: release_semaphore.value, 483 } 484 }; 485 486 let last_layout_transition: (ImageLayout, ImageLayout) = ( 487 ash::vk::ImageLayout::from_raw(last_layout_transition.0) 488 .try_into() 489 .map_err(|_| { 490 anyhow!( 491 "Failed to convert {:#x} to a valid image layout.", 492 last_layout_transition.0 493 ) 494 })?, 495 ash::vk::ImageLayout::from_raw(last_layout_transition.1) 496 .try_into() 497 .map_err(|_| { 498 anyhow!( 499 "Failed to convert {:#x} to a valid image layout.", 500 last_layout_transition.1 501 ) 502 })?, 503 ); 504 505 let (promise, waitable) = create_promise_and_waitable(); 506 507 let (image_return_tx, image_return_rx) = channel(); 508 self.used_image_receivers.insert(image_id, image_return_rx); 509 510 self.window_event_loop 511 .send_event(UserEvent::PostCommand { 512 image, 513 last_layout_transition, 514 acquire_timepoint, 515 release_timepoint, 516 image_return: image_return_tx, 517 promise, 518 }) 519 .context("send user defined message to the window event loop")?; 520 Ok(waitable) 521 } 522 } 523 524 pub(crate) type VulkanDisplay = VulkanDisplayImpl<PlatformWindowEventLoop<VulkanState>>; 525