xref: /aosp_15_r20/external/crosvm/gpu_display/src/vulkan.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
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