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).into(); 318 assert!( 319 !(sparse_binding || sparse_residency || sparse_aliased), 320 "unsupported image create flags {:#x}", 321 image_create_flags 322 ); 323 let image_type = vk::ImageType::from_raw(metadata.image_type); 324 let image_extent = metadata.extent; 325 let image_dimensions = match image_type { 326 vk::ImageType::TYPE_2D => ImageDimensions::Dim2d { 327 width: image_extent.width, 328 height: image_extent.height, 329 array_layers: metadata.array_layers, 330 }, 331 _ => unimplemented!(), 332 }; 333 let format = { 334 let format = metadata.format; 335 vk::Format::from_raw(format) 336 .try_into() 337 .map_err(|_| format_err!("Failed to convert {:#x} to format.", format))? 338 }; 339 let image_samples = { 340 let samples = metadata.samples; 341 vk::SampleCountFlags::from_raw(samples) 342 .try_into() 343 .map_err(|_| { 344 format_err!("Failed to convert {:#x} to sample count flag.", samples) 345 })? 346 }; 347 let image_tiling = { 348 let tiling = metadata.tiling; 349 vk::ImageTiling::from_raw(tiling) 350 .try_into() 351 .map_err(|_| format_err!("Failed to convert {:#x} to image tiling enum.", tiling))? 352 }; 353 let image_usage = { 354 let usage = metadata.usage; 355 vk::ImageUsageFlags::from_raw(usage).into() 356 }; 357 let image_sharing = { 358 let sharing_mode = metadata.sharing_mode; 359 match vk::SharingMode::from_raw(sharing_mode) { 360 vk::SharingMode::EXCLUSIVE => Sharing::Exclusive, 361 vk::SharingMode::CONCURRENT => { 362 let mut queue_family_indices = SmallVec::new(); 363 queue_family_indices.copy_from_slice(&metadata.queue_family_indices); 364 Sharing::Concurrent(queue_family_indices) 365 } 366 _ => return Err(format_err!("Invalid sharing mode {:#x}.", sharing_mode)), 367 } 368 }; 369 let image_initial_layout = { 370 let initial_layout = metadata.initial_layout; 371 vk::ImageLayout::from_raw(initial_layout) 372 .try_into() 373 .map_err(|_| { 374 format_err!( 375 "Failed to convert the initial layout {:#x} to an image layout.", 376 initial_layout 377 ) 378 })? 379 }; 380 381 let image = ExternalImage::import( 382 &self.device, 383 UnsafeImageCreateInfo { 384 dimensions: image_dimensions, 385 format: Some(format), 386 mip_levels: metadata.mip_levels, 387 samples: image_samples, 388 tiling: image_tiling, 389 usage: image_usage, 390 stencil_usage: ImageUsage::empty(), 391 sharing: image_sharing, 392 initial_layout: image_initial_layout, 393 external_memory_handle_types: create_post_image_external_memory_handle_types(), 394 mutable_format, 395 cube_compatible, 396 array_2d_compatible, 397 block_texel_view_compatible, 398 ..Default::default() 399 }, 400 MemoryAllocateInfo { 401 allocation_size: metadata.allocation_size, 402 memory_type_index: metadata.memory_type_index, 403 export_handle_types: ExternalMemoryHandleTypes::empty(), 404 ..Default::default() 405 }, 406 create_post_image_memory_import_info(descriptor), 407 metadata.dedicated_allocation, 408 0, 409 ) 410 .context("import the composition result image")?; 411 412 if self.imported_images.insert(image_id, image).is_some() { 413 warn!("Reused image_id {}", image_id); 414 } 415 Ok(()) 416 } 417 delete_imported_image_or_semaphore(&mut self, import_id: u32)418 pub fn delete_imported_image_or_semaphore(&mut self, import_id: u32) { 419 // Import ids are shared between images and semaphores, so first try to remove from 420 // self.imported_sempahores, and if that returns none then try to remove from images. 421 if self.imported_semaphores.remove(&import_id).is_none() { 422 if let Some(receiver) = self.used_image_receivers.remove(&import_id) { 423 if let Err(e) = receiver.recv() { 424 error!("Failed to receive used image from post worker: {}", e); 425 } 426 } else if self.imported_images.remove(&import_id).is_none() { 427 error!("Import id {} has not been imported", import_id); 428 } 429 } 430 } 431 post( &mut self, image_id: ImageId, last_layout_transition: (i32, i32), acquire_semaphore: Option<SemaphoreTimepoint>, release_semaphore: SemaphoreTimepoint, ) -> Result<Waitable>432 pub fn post( 433 &mut self, 434 image_id: ImageId, 435 last_layout_transition: (i32, i32), 436 acquire_semaphore: Option<SemaphoreTimepoint>, 437 release_semaphore: SemaphoreTimepoint, 438 ) -> Result<Waitable> { 439 let image = if let Some(receiver) = self.used_image_receivers.remove(&image_id) { 440 receiver 441 .recv() 442 .context("failed to receive used image from post worker")? 443 } else { 444 self.imported_images 445 .remove(&image_id) 446 .ok_or(anyhow!("Image id {} has not been imported", image_id))? 447 }; 448 449 let acquire_timepoint = 450 if let Some(SemaphoreTimepoint { import_id, value }) = acquire_semaphore { 451 let semaphore = self 452 .imported_semaphores 453 .get(&import_id) 454 .ok_or(anyhow!("Semaphore id {} has not been imported", import_id))?; 455 Some(Timepoint { 456 semaphore: semaphore.clone(), 457 value, 458 }) 459 } else { 460 None 461 }; 462 463 let release_timepoint = { 464 let semaphore = self 465 .imported_semaphores 466 .get(&release_semaphore.import_id) 467 .ok_or(anyhow!( 468 "Semaphore id {} has not been imported", 469 release_semaphore.import_id 470 ))?; 471 Timepoint { 472 semaphore: semaphore.clone(), 473 value: release_semaphore.value, 474 } 475 }; 476 477 let last_layout_transition: (ImageLayout, ImageLayout) = ( 478 ash::vk::ImageLayout::from_raw(last_layout_transition.0) 479 .try_into() 480 .map_err(|_| { 481 anyhow!( 482 "Failed to convert {:#x} to a valid image layout.", 483 last_layout_transition.0 484 ) 485 })?, 486 ash::vk::ImageLayout::from_raw(last_layout_transition.1) 487 .try_into() 488 .map_err(|_| { 489 anyhow!( 490 "Failed to convert {:#x} to a valid image layout.", 491 last_layout_transition.1 492 ) 493 })?, 494 ); 495 496 let (promise, waitable) = create_promise_and_waitable(); 497 498 let (image_return_tx, image_return_rx) = channel(); 499 self.used_image_receivers.insert(image_id, image_return_rx); 500 501 self.window_event_loop 502 .send_event(UserEvent::PostCommand { 503 image, 504 last_layout_transition, 505 acquire_timepoint, 506 release_timepoint, 507 image_return: image_return_tx, 508 promise, 509 }) 510 .context("send user defined message to the window event loop")?; 511 Ok(waitable) 512 } 513 } 514 515 pub(crate) type VulkanDisplay = VulkanDisplayImpl<PlatformWindowEventLoop<VulkanState>>; 516