• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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