xref: /aosp_15_r20/external/crosvm/gpu_display/src/vulkan/post_worker.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 use std::any::Any;
6 use std::borrow::Borrow;
7 use std::collections::BTreeSet;
8 use std::marker::PhantomData;
9 use std::rc::Rc;
10 use std::sync::Arc;
11 use std::time::Duration;
12 
13 use anyhow::bail;
14 use anyhow::ensure;
15 use anyhow::format_err;
16 use anyhow::Context;
17 use anyhow::Result;
18 use ash::vk::UUID_SIZE;
19 use ash::vk::{self};
20 use base::error;
21 use base::info;
22 use euclid::size2;
23 use euclid::Size2D;
24 use rand::rngs::ThreadRng;
25 use rand::seq::IteratorRandom;
26 use smallvec::SmallVec;
27 use vulkano::command_buffer::pool::standard::StandardCommandPoolAlloc;
28 use vulkano::command_buffer::pool::CommandPool;
29 use vulkano::command_buffer::pool::CommandPoolAlloc;
30 use vulkano::command_buffer::pool::CommandPoolBuilderAlloc;
31 use vulkano::command_buffer::pool::StandardCommandPool;
32 use vulkano::command_buffer::submit::SubmitPresentBuilder;
33 use vulkano::command_buffer::submit::SubmitPresentError;
34 use vulkano::command_buffer::sys::CommandBufferBeginInfo;
35 use vulkano::command_buffer::sys::UnsafeCommandBufferBuilder;
36 use vulkano::command_buffer::BlitImageInfo;
37 use vulkano::command_buffer::CommandBufferLevel;
38 use vulkano::command_buffer::CommandBufferUsage;
39 use vulkano::device::physical::PhysicalDevice;
40 use vulkano::device::Device;
41 use vulkano::device::DeviceCreateInfo;
42 use vulkano::device::DeviceExtensions;
43 use vulkano::device::Features;
44 use vulkano::device::Queue;
45 use vulkano::device::QueueCreateInfo;
46 use vulkano::format::Format;
47 use vulkano::image::ImageAccess;
48 use vulkano::image::ImageAspects;
49 use vulkano::image::ImageLayout;
50 use vulkano::image::ImageSubresourceRange;
51 use vulkano::image::ImageUsage;
52 use vulkano::image::{self};
53 use vulkano::instance::Instance;
54 use vulkano::instance::InstanceCreateInfo;
55 use vulkano::instance::InstanceExtensions;
56 use vulkano::swapchain::acquire_next_image_raw;
57 use vulkano::swapchain::AcquireError;
58 use vulkano::swapchain::AcquiredImage;
59 use vulkano::swapchain::CompositeAlpha;
60 use vulkano::swapchain::PresentInfo;
61 use vulkano::swapchain::PresentMode;
62 use vulkano::swapchain::SwapchainCreateInfo;
63 use vulkano::swapchain::{self};
64 use vulkano::sync::AccessFlags;
65 use vulkano::sync::DependencyInfo;
66 use vulkano::sync::ExternalFenceHandleTypes;
67 use vulkano::sync::Fence;
68 use vulkano::sync::FenceCreateInfo;
69 use vulkano::sync::ImageMemoryBarrier;
70 use vulkano::sync::PipelineStages;
71 use vulkano::sync::QueueFamilyTransfer;
72 use vulkano::sync::Semaphore;
73 use vulkano::sync::SemaphoreCreateInfo;
74 use vulkano::sync::Sharing;
75 use vulkano::SynchronizedVulkanObject;
76 use vulkano::Version;
77 use vulkano::VulkanLibrary;
78 use vulkano::VulkanObject;
79 
80 use super::sys::Window;
81 use super::ExternalImage;
82 use crate::vulkan::external_image::AcquireImageMemoryBarrier;
83 use crate::vulkan::external_image::ReleaseImageMemoryBarrier;
84 
85 type VulkanoWindow = Arc<dyn Any + Send + Sync>;
86 type Surface = swapchain::Surface<VulkanoWindow>;
87 type Swapchain = swapchain::Swapchain<VulkanoWindow>;
88 type SwapchainImage = image::swapchain::SwapchainImage<VulkanoWindow>;
89 
create_swapchain_create_info<T>( physical_device: &PhysicalDevice, surface: &Surface, image_size: &Size2D<u32, T>, ) -> Result<SwapchainCreateInfo>90 fn create_swapchain_create_info<T>(
91     physical_device: &PhysicalDevice,
92     surface: &Surface,
93     image_size: &Size2D<u32, T>,
94 ) -> Result<SwapchainCreateInfo> {
95     let surface_capabilities = physical_device
96         .surface_capabilities(surface, Default::default())
97         .context("query surface cpabilities")?;
98     ensure!(
99         surface_capabilities.supported_usage_flags.transfer_dst,
100         "The swapchain image must support USAGE_TRANSFER_DST. Supported usages: {:?}",
101         surface_capabilities,
102     );
103     if let Some([width, height]) = surface_capabilities.current_extent {
104         ensure!(
105             *image_size == size2(width, height),
106             "The passed in size {}x{} doesn't match the current surface extent {}x{}.",
107             image_size.width,
108             image_size.height,
109             width,
110             height
111         );
112     }
113     ensure!(
114         image_size.width <= surface_capabilities.max_image_extent[0]
115             && image_size.width >= surface_capabilities.min_image_extent[0],
116         "The passed in width {} must be within the range of [{}, {}].",
117         image_size.width,
118         surface_capabilities.min_image_extent[0],
119         surface_capabilities.max_image_extent[0]
120     );
121     ensure!(
122         image_size.height <= surface_capabilities.max_image_extent[1]
123             && image_size.height >= surface_capabilities.min_image_extent[1],
124         "The passed in width {} must be within the range of [{}, {}].",
125         image_size.height,
126         surface_capabilities.min_image_extent[1],
127         surface_capabilities.max_image_extent[1]
128     );
129     // Triple buffering if possible.
130     let min_image_count = 3.clamp(
131         surface_capabilities.min_image_count,
132         surface_capabilities.max_image_count.unwrap_or(u32::MAX),
133     );
134     let pre_transform = surface_capabilities.current_transform;
135     let available_format_and_color_space = physical_device
136         .surface_formats(surface, Default::default())
137         .context("query formats and color spaces supported by the surface")?;
138     let (image_format, image_color_space) = available_format_and_color_space
139         .iter()
140         .find(|(image_format, _)| matches!(image_format, Format::B8G8R8A8_UNORM))
141         .copied()
142         .ok_or_else(|| {
143             format_err!(
144                 concat!(
145                     "No supported formats and color spaces found. All supported formats and color ",
146                     "spaces are {:?}"
147                 ),
148                 available_format_and_color_space
149             )
150         })?;
151     let present_modes = physical_device
152         .surface_present_modes(surface)
153         .context("query the supported present mode")?
154         .collect::<Vec<_>>();
155     assert!(
156         present_modes
157             .iter()
158             .any(|mode| matches!(mode, PresentMode::Fifo)),
159         concat!(
160             "The Vulkan spec requires the support of FIFO present mode, but it is not supported. ",
161             "All supported present modes: {:?}."
162         ),
163         present_modes
164     );
165     let present_mode = PresentMode::Fifo;
166     Ok(SwapchainCreateInfo {
167         min_image_count,
168         image_format: Some(image_format),
169         image_color_space,
170         image_extent: [image_size.width, image_size.height],
171         image_array_layers: 1,
172         image_usage: ImageUsage {
173             transfer_dst: true,
174             ..ImageUsage::empty()
175         },
176         image_sharing: Sharing::Exclusive,
177         pre_transform,
178         composite_alpha: CompositeAlpha::Opaque,
179         present_mode,
180         clipped: true,
181         ..Default::default()
182     })
183 }
184 
185 #[derive(Clone)]
186 pub struct Timepoint {
187     pub semaphore: Arc<Semaphore>,
188     pub value: u64,
189 }
190 
191 /// PostResource contains the required structures and information for posting to an individual
192 /// swapchain image.
193 struct PostResource {
194     acquire_swapchain_image_semaphore: Semaphore,
195     command_buffer_alloc: StandardCommandPoolAlloc,
196     command_complete_fence: Fence,
197     command_complete_semaphore: Semaphore,
198 }
199 
200 impl PostResource {
new(device: &Arc<Device>, command_buffer_alloc: StandardCommandPoolAlloc) -> Result<Self>201     fn new(device: &Arc<Device>, command_buffer_alloc: StandardCommandPoolAlloc) -> Result<Self> {
202         let acquire_swapchain_image_semaphore =
203             Semaphore::new(Arc::clone(device), SemaphoreCreateInfo::default())
204                 .context("create semaphore for acquiring next swapchain image")?;
205         let command_complete_semaphore =
206             Semaphore::new(Arc::clone(device), SemaphoreCreateInfo::default())
207                 .context("create semaphore to be signaled after the command has completed")?;
208         let command_complete_fence = Fence::new(
209             Arc::clone(device),
210             FenceCreateInfo {
211                 signaled: true,
212                 export_handle_types: ExternalFenceHandleTypes::empty(),
213                 ..Default::default()
214             },
215         )
216         .context("create fence signaled when the command has completed")?;
217         Ok(Self {
218             acquire_swapchain_image_semaphore,
219             command_buffer_alloc,
220             command_complete_fence,
221             command_complete_semaphore,
222         })
223     }
224 
225     /// Submit a blit of post_image to swap_chain image.
226     ///
227     /// The image in `post_image` needs to be transferred from an ExternalImage before it can be
228     /// used as a blit source. It also may need to be transitioned to TransferSrcOptimal. This
229     /// function will transition it back to an ExternalImage as part of the same submission. The
230     /// image in `swapchain_image` will also be transferred to TransferDstOptimal for the blit and
231     /// then finally to PresentSrc after the blit.
record_and_submit_post_command( &mut self, device: &Device, ash_device: &ash::Device, post_image: ExternalImage, last_layout_transition: (ImageLayout, ImageLayout), swapchain_image: Arc<SwapchainImage>, graphics_queue: &Queue, present_queue: &Queue, post_image_acquire_timepoint: Option<&Timepoint>, post_image_release_timepoint: &Timepoint, ) -> ExternalImage232     fn record_and_submit_post_command(
233         &mut self,
234         device: &Device,
235         ash_device: &ash::Device,
236         post_image: ExternalImage,
237         last_layout_transition: (ImageLayout, ImageLayout),
238         swapchain_image: Arc<SwapchainImage>,
239         graphics_queue: &Queue,
240         present_queue: &Queue,
241         post_image_acquire_timepoint: Option<&Timepoint>,
242         post_image_release_timepoint: &Timepoint,
243     ) -> ExternalImage {
244         assert_eq!(
245             device.internal_object(),
246             ash_device.handle(),
247             "The vulkano device and the ash device must refer to the same VkDevice."
248         );
249         // SAFETY: Safe because self.command_buffer_alloc outlives command_buffer_builder.
250         let mut command_buffer_builder = unsafe {
251             UnsafeCommandBufferBuilder::new(
252                 self.command_buffer_alloc.inner(),
253                 CommandBufferBeginInfo {
254                     usage: CommandBufferUsage::OneTimeSubmit,
255                     inheritance_info: None,
256                     ..Default::default()
257                 },
258             )
259         }
260         .unwrap_or_else(|e| panic!("Failed to begin recording the command buffer: {:?}", e));
261         let post_image_old_layout = last_layout_transition.0;
262         // SAFETY: Safe because the image in image_access (which is eventually converted back to
263         // post_image where it still lives) outlives the command buffer.
264         let image_access = unsafe {
265             post_image.acquire(
266                 &mut command_buffer_builder,
267                 AcquireImageMemoryBarrier {
268                     source_stages: PipelineStages {
269                         transfer: true,
270                         ..PipelineStages::empty()
271                     },
272                     destination_stages: PipelineStages {
273                         transfer: true,
274                         ..PipelineStages::empty()
275                     },
276                     destination_access: AccessFlags {
277                         transfer_read: true,
278                         ..AccessFlags::empty()
279                     },
280                     destination_queue_family_index: graphics_queue.queue_family_index(),
281                     subresource_range: ImageSubresourceRange {
282                         aspects: ImageAspects {
283                             color: true,
284                             ..ImageAspects::empty()
285                         },
286                         mip_levels: 0..1,
287                         array_layers: 0..1,
288                     },
289                 },
290                 last_layout_transition,
291             )
292         };
293         let mut image_memory_barriers = SmallVec::from_vec(vec![ImageMemoryBarrier {
294             source_stages: PipelineStages {
295                 transfer: true,
296                 ..PipelineStages::empty()
297             },
298             source_access: AccessFlags::empty(),
299             destination_stages: PipelineStages {
300                 transfer: true,
301                 ..PipelineStages::empty()
302             },
303             destination_access: AccessFlags {
304                 transfer_write: true,
305                 ..AccessFlags::empty()
306             },
307             old_layout: ImageLayout::Undefined,
308             new_layout: ImageLayout::TransferDstOptimal,
309             // No need for ownership transfer, because we don't care about the content.
310             queue_family_transfer: None,
311             subresource_range: ImageSubresourceRange {
312                 aspects: ImageAspects {
313                     color: true,
314                     ..ImageAspects::empty()
315                 },
316                 mip_levels: 0..1,
317                 array_layers: 0..1,
318             },
319             ..ImageMemoryBarrier::image(Arc::clone(swapchain_image.inner().image))
320         }]);
321         if !matches!(
322             image_access.initial_layout_requirement(),
323             ImageLayout::TransferSrcOptimal
324         ) {
325             image_memory_barriers.push(ImageMemoryBarrier {
326                 source_stages: PipelineStages {
327                     transfer: true,
328                     ..PipelineStages::empty()
329                 },
330                 source_access: AccessFlags {
331                     transfer_read: true,
332                     ..AccessFlags::empty()
333                 },
334                 destination_stages: PipelineStages {
335                     transfer: true,
336                     ..PipelineStages::empty()
337                 },
338                 destination_access: AccessFlags {
339                     transfer_read: true,
340                     ..AccessFlags::empty()
341                 },
342                 old_layout: image_access.initial_layout_requirement(),
343                 new_layout: ImageLayout::TransferSrcOptimal,
344                 queue_family_transfer: None,
345                 subresource_range: ImageSubresourceRange {
346                     aspects: ImageAspects {
347                         color: true,
348                         ..ImageAspects::empty()
349                     },
350                     mip_levels: 0..1,
351                     array_layers: 0..1,
352                 },
353                 ..ImageMemoryBarrier::image(Arc::clone(image_access.inner().image))
354             });
355         }
356         // SAFETY: Safe because the image in image_access (which is eventually converted back to
357         // post_image where it still lives) outlives the command buffer.
358         unsafe {
359             command_buffer_builder.pipeline_barrier(&DependencyInfo {
360                 image_memory_barriers,
361                 ..Default::default()
362             });
363         }
364         let image_access = Arc::new(image_access);
365         // SAFETY: Safe because the image in image_access (which is eventually converted back to
366         // post_image where it still lives) outlives the command buffer, and because
367         // swapchain_image lives until the end of this function, thus outliving the command buffer.
368         unsafe {
369             command_buffer_builder.blit_image(&BlitImageInfo::images(
370                 Arc::clone(&image_access) as Arc<_>,
371                 Arc::clone(&swapchain_image) as Arc<_>,
372             ));
373         }
374         let queue_family_transfer =
375             if graphics_queue.queue_family_index() == present_queue.queue_family_index() {
376                 None
377             } else {
378                 Some(QueueFamilyTransfer {
379                     source_index: graphics_queue.queue_family_index(),
380                     destination_index: present_queue.queue_family_index(),
381                 })
382             };
383         let mut image_memory_barriers = SmallVec::from_vec(vec![ImageMemoryBarrier {
384             source_stages: PipelineStages {
385                 transfer: true,
386                 ..PipelineStages::empty()
387             },
388             source_access: AccessFlags {
389                 transfer_write: true,
390                 ..AccessFlags::empty()
391             },
392             destination_stages: PipelineStages::empty(),
393             destination_access: AccessFlags::empty(),
394             old_layout: ImageLayout::TransferDstOptimal,
395             new_layout: ImageLayout::PresentSrc,
396             queue_family_transfer,
397             subresource_range: ImageSubresourceRange {
398                 aspects: ImageAspects {
399                     color: true,
400                     ..ImageAspects::empty()
401                 },
402                 mip_levels: 0..1,
403                 array_layers: 0..1,
404             },
405             ..ImageMemoryBarrier::image(Arc::clone(swapchain_image.inner().image))
406         }]);
407         if !matches!(
408             image_access.final_layout_requirement(),
409             ImageLayout::TransferSrcOptimal
410         ) {
411             image_memory_barriers.push(ImageMemoryBarrier {
412                 source_stages: PipelineStages {
413                     transfer: true,
414                     ..PipelineStages::empty()
415                 },
416                 source_access: AccessFlags {
417                     transfer_read: true,
418                     ..AccessFlags::empty()
419                 },
420                 destination_stages: PipelineStages {
421                     transfer: true,
422                     ..PipelineStages::empty()
423                 },
424                 destination_access: AccessFlags {
425                     transfer_read: true,
426                     ..AccessFlags::empty()
427                 },
428                 old_layout: ImageLayout::TransferSrcOptimal,
429                 new_layout: image_access.final_layout_requirement(),
430                 queue_family_transfer: None,
431                 subresource_range: ImageSubresourceRange {
432                     aspects: ImageAspects {
433                         color: true,
434                         ..ImageAspects::empty()
435                     },
436                     mip_levels: 0..1,
437                     array_layers: 0..1,
438                 },
439                 ..ImageMemoryBarrier::image(Arc::clone(image_access.inner().image))
440             });
441         }
442         // SAFETY: Safe because the image in image_access (which is eventually converted back to
443         // post_image where it still lives) outlives the command buffer.
444         unsafe {
445             command_buffer_builder.pipeline_barrier(&DependencyInfo {
446                 image_memory_barriers,
447                 ..Default::default()
448             });
449         }
450         let image_access = Arc::try_unwrap(image_access).expect("should be the sole owner");
451         // SAFETY: Safe because the image in post_image outlives the command buffer.
452         let post_image = unsafe {
453             image_access.release(
454                 &mut command_buffer_builder,
455                 ReleaseImageMemoryBarrier {
456                     source_stages: PipelineStages {
457                         transfer: true,
458                         ..PipelineStages::empty()
459                     },
460                     source_access: AccessFlags {
461                         transfer_read: true,
462                         ..AccessFlags::empty()
463                     },
464                     destination_stages: PipelineStages {
465                         transfer: true,
466                         ..PipelineStages::empty()
467                     },
468                     new_layout: post_image_old_layout,
469                     source_queue_family_index: graphics_queue.queue_family_index(),
470                 },
471             )
472         };
473         let command_buffer = command_buffer_builder
474             .build()
475             .unwrap_or_else(|e| panic!("Failed to end the command buffer recording: {:#}", e));
476 
477         assert!(self
478             .command_complete_fence
479             .is_signaled()
480             .unwrap_or_else(|e| panic!("Failed to query the status of the fence: {:?}", e)));
481         self.command_complete_fence.reset().unwrap_or_else(|e| {
482             panic!("Failed to reset the fence for the post resources: {:?}", e)
483         });
484         {
485             // The first values are not used, because they are not binary semaphores.
486             let mut wait_semaphore_values = vec![0];
487             let signal_semaphore_values = [0, post_image_release_timepoint.value];
488             let mut wait_semaphores =
489                 vec![self.acquire_swapchain_image_semaphore.internal_object()];
490             let mut wait_dst_stage_mask = vec![vk::PipelineStageFlags::TRANSFER];
491             let command_buffers = [command_buffer.internal_object()];
492             let signal_semaphores = [
493                 self.command_complete_semaphore.internal_object(),
494                 post_image_release_timepoint.semaphore.internal_object(),
495             ];
496 
497             if let Some(post_image_acquire_timepoint) = post_image_acquire_timepoint {
498                 wait_semaphore_values.push(post_image_acquire_timepoint.value);
499                 wait_semaphores.push(post_image_acquire_timepoint.semaphore.internal_object());
500                 wait_dst_stage_mask.push(vk::PipelineStageFlags::TRANSFER);
501             }
502 
503             let mut timeline_semaphore_submit_info = vk::TimelineSemaphoreSubmitInfo::builder()
504                 .wait_semaphore_values(&wait_semaphore_values)
505                 .signal_semaphore_values(&signal_semaphore_values)
506                 .build();
507             let submit_info = vk::SubmitInfo::builder()
508                 .wait_semaphores(&wait_semaphores)
509                 .wait_dst_stage_mask(&wait_dst_stage_mask)
510                 .command_buffers(&command_buffers)
511                 .signal_semaphores(&signal_semaphores)
512                 .push_next(&mut timeline_semaphore_submit_info)
513                 .build();
514 
515             // SAFETY: Safe because the contents of submit_info and all the things it points to
516             // outlive this function call.
517             unsafe {
518                 ash_device.queue_submit(
519                     *graphics_queue.internal_object_guard(),
520                     &[submit_info],
521                     self.command_complete_fence.internal_object(),
522                 )
523             }
524             .unwrap_or_else(|e| panic!("Failed to submit the command: {:?}", e));
525         }
526         post_image
527     }
528 }
529 
530 /// PostWorker owns the vulkan surface and swapchain, and can post images to it.
531 pub struct PostWorker {
532     physical_device: Arc<PhysicalDevice>,
533     ash_device: Arc<ash::Device>,
534     device: Arc<Device>,
535     window: Arc<dyn Window>,
536     swapchain: Arc<Swapchain>,
537     swapchain_images: Vec<Arc<SwapchainImage>>,
538     graphics_queue: Arc<Queue>,
539     present_queue: Arc<Queue>,
540     post_resources: Vec<PostResource>,
541     _command_pool: Arc<StandardCommandPool>,
542     rng: ThreadRng,
543     // Mark Worker as !Sync and !Send
544     _marker: PhantomData<Rc<()>>,
545 }
546 
547 impl PostWorker {
548     /// Initialize the post worker which does the following:
549     ///   - Create the VkInstance
550     ///   - Create the VkDevice and VkQueue
551     ///   - Create the Swapchain
552     ///   - Create a PostResource for each swapchain image
new( vulkan_library: Arc<VulkanLibrary>, device_uuid: &[u8; UUID_SIZE], driver_uuid: &[u8; UUID_SIZE], window: Arc<dyn Window>, ) -> Result<Self>553     pub(crate) fn new(
554         vulkan_library: Arc<VulkanLibrary>,
555         device_uuid: &[u8; UUID_SIZE],
556         driver_uuid: &[u8; UUID_SIZE],
557         window: Arc<dyn Window>,
558     ) -> Result<Self> {
559         // Create the Vulkan instance.
560         let api_version = vulkan_library.api_version();
561         if api_version < Version::V1_1 {
562             bail!("Vulkan instance version too low: {:?}", api_version);
563         }
564         let instance = Instance::new(
565             vulkan_library,
566             InstanceCreateInfo {
567                 application_name: Some("vulkan_display_host".to_owned()),
568                 enabled_extensions: InstanceExtensions {
569                     khr_external_memory_capabilities: true,
570                     khr_get_physical_device_properties2: true,
571                     khr_surface: true,
572                     khr_win32_surface: true,
573                     ..InstanceExtensions::empty()
574                 },
575                 ..Default::default()
576             },
577         )
578         .context("create VkInstance")?;
579         assert!(instance.api_version() >= Version::V1_1);
580 
581         // Choose the Vulkan physical device.
582         let mut physical_devices = instance
583             .enumerate_physical_devices()
584             .context("enumerate physical devices")?;
585         let physical_device = physical_devices.find(|physical_device| {
586             let properties = physical_device.properties();
587             if let (Some(current_device_uuid), Some(current_driver_uuid)) = (
588                 properties.device_uuid.as_ref(),
589                 properties.driver_uuid.as_ref(),
590             ) {
591                 current_device_uuid == device_uuid && current_driver_uuid == driver_uuid
592             } else {
593                 false
594             }
595         });
596         let physical_device = if let Some(physical_device) = physical_device {
597             physical_device
598         } else {
599             bail!("Failed to find the target physical device.");
600         };
601         {
602             let properties = physical_device.properties();
603             info!(
604                 "The post worker chooses the device: name: {}, vendor_id: {}",
605                 properties.device_name, properties.vendor_id
606             );
607         }
608         let api_version = physical_device.api_version();
609         if api_version < Version::V1_1 {
610             bail!(
611                 "The physical device Vulkan version is too low: {:#}",
612                 api_version
613             );
614         }
615         ensure!(
616             physical_device.supported_features().timeline_semaphore,
617             "The physical device doesn't support timeline semaphore."
618         );
619 
620         let surface = Arc::clone(&window)
621             .create_vulkan_surface(Arc::clone(&instance))
622             .context("Failed to create the surface.")?;
623 
624         let queue_family_properties = physical_device.queue_family_properties();
625         let queue_family_indices = (0u32..queue_family_properties
626             .len()
627             .try_into()
628             .expect("queue family index too large"))
629             .collect::<Vec<_>>();
630         // Find the present queue.
631         let mut present_queue_family_index = None;
632         for queue_family_index in queue_family_indices.iter().copied() {
633             let supported = physical_device
634                 .surface_support(queue_family_index, surface.borrow())
635                 .with_context(|| {
636                     format!(
637                         "query if queue family index {} supports the present",
638                         queue_family_index
639                     )
640                 })?;
641             if supported {
642                 present_queue_family_index = Some(queue_family_index);
643                 break;
644             }
645         }
646         let present_queue_family_index = match present_queue_family_index {
647             Some(queue_index) => queue_index,
648             None => bail!("No queue supports presentation."),
649         };
650 
651         // Find the graphics queue.
652         let graphics_queue_family_index = if queue_family_properties
653             [usize::try_from(present_queue_family_index).expect("queue family index too large")]
654         .queue_flags
655         .graphics
656         {
657             Some(present_queue_family_index)
658         } else {
659             queue_family_indices
660                 .iter()
661                 .copied()
662                 .find(|queue_family_index| {
663                     queue_family_properties[usize::try_from(*queue_family_index)
664                         .expect("queue family index too large")]
665                     .queue_flags
666                     .graphics
667                 })
668         };
669         let graphics_queue_family_index = match graphics_queue_family_index {
670             Some(queue_index) => queue_index,
671             None => bail!("No queue supports graphics"),
672         };
673 
674         // Create VkDevice.
675         let queue_create_infos =
676             BTreeSet::from([present_queue_family_index, graphics_queue_family_index])
677                 .iter()
678                 .copied()
679                 .map(|queue_family_index| QueueCreateInfo {
680                     queue_family_index,
681                     ..Default::default()
682                 })
683                 .collect();
684         let (device, queues) = Device::new(
685             Arc::clone(&physical_device),
686             DeviceCreateInfo {
687                 enabled_extensions: DeviceExtensions {
688                     khr_external_fence: true,
689                     khr_external_fence_win32: true,
690                     khr_external_semaphore: true,
691                     khr_external_semaphore_win32: true,
692                     khr_external_memory: true,
693                     khr_external_memory_win32: true,
694                     khr_swapchain: true,
695                     khr_timeline_semaphore: true,
696                     ..DeviceExtensions::empty()
697                 },
698                 enabled_features: Features {
699                     timeline_semaphore: true,
700                     ..Features::empty()
701                 },
702                 queue_create_infos,
703                 ..Default::default()
704             },
705         )
706         .context("create VkDevice")?;
707 
708         // Create the swapchain.
709         let (swapchain, swapchain_images) = Swapchain::new(
710             Arc::clone(&device),
711             Arc::clone(&surface),
712             create_swapchain_create_info(
713                 physical_device.borrow(),
714                 surface.borrow(),
715                 &window
716                     .get_inner_size()
717                     .context("get the window size to create the swapchain")?,
718             )
719             .context("create the swapchain create info")?,
720         )
721         .context("create Vulkan swapchain")?;
722         let queues = queues.collect::<Vec<_>>();
723         let graphics_queue = queues
724             .iter()
725             .find(|queue| queue.queue_family_index() == graphics_queue_family_index)
726             .cloned()
727             .expect("Graphics queue not found.");
728         let present_queue = queues
729             .iter()
730             .find(|queue| queue.queue_family_index() == present_queue_family_index)
731             .cloned()
732             .expect("Present queue not found.");
733 
734         let ash_device =
735         // SAFETY: Safe because instance_fn comes from an instance we created and know is valid and
736         // we also created device which is valid.
737             unsafe { ash::Device::load(&instance.fns().v1_0, device.internal_object()) };
738 
739         // TODO (b/327677792): StandardCommandPool must be put inside an Arc, it's intended to work
740         // that way. We need move to a newer version of vulkano to fix this.
741         #[allow(clippy::arc_with_non_send_sync)]
742         let command_pool = Arc::new(
743             StandardCommandPool::new(Arc::clone(&device), graphics_queue_family_index)
744                 .context("create command pool")?,
745         );
746         let command_buffer_allocs = command_pool
747             .allocate(
748                 CommandBufferLevel::Primary,
749                 (swapchain_images.len() + 1)
750                     .try_into()
751                     .expect("too many swapchain images"),
752             )
753             .context("allocate command buffers")?
754             .map(CommandPoolBuilderAlloc::into_alloc);
755         let post_resources: Vec<PostResource> = command_buffer_allocs
756             .map(|command_buffer| {
757                 PostResource::new(&device, command_buffer)
758                     .context("create resources for posting one frame")
759             })
760             .collect::<Result<_>>()?;
761 
762         Ok(Self {
763             physical_device,
764             ash_device: Arc::new(ash_device),
765             device,
766             window,
767             swapchain,
768             swapchain_images,
769             graphics_queue,
770             present_queue,
771             post_resources,
772             _command_pool: command_pool,
773             rng: rand::thread_rng(),
774             _marker: Default::default(),
775         })
776     }
777 
drain_queues(&self) -> Result<()>778     fn drain_queues(&self) -> Result<()> {
779         self.graphics_queue
780             .wait()
781             .context("wait for the graphics queue to become idle")?;
782         self.present_queue
783             .wait()
784             .context("wait for the present queue to become idle")?;
785         Ok(())
786     }
787 
recreate_swapchain(&mut self) -> Result<()>788     pub fn recreate_swapchain(&mut self) -> Result<()> {
789         let swapchain_create_info = create_swapchain_create_info(
790             self.physical_device.borrow(),
791             self.swapchain.surface(),
792             &self
793                 .window
794                 .get_inner_size()
795                 .context("get the window size when recreating the swapchain")?,
796         )
797         .context("create swapchain create info")?;
798         self.drain_queues()
799             .context("wait for queues to become idel")?;
800         (self.swapchain, self.swapchain_images) = self
801             .swapchain
802             .recreate(swapchain_create_info)
803             .context("recreate swapchain")?;
804         Ok(())
805     }
806 
807     /// Acquire a swapchain image and call the supplied function with the swapchain's PostResource,
808     /// SwapchainImage, and the generic `userdata`. Userdata is likely to be the source image(s)
809     /// that are being presented to the swapchain image and the userdata is returned to the caller
810     /// for reuse. After `f` is called, this function will submit a present call for that swapchain
811     /// image.
812     ///
813     /// Note: It is the responsibility of `f` to make sure that the swapchain image has been
814     /// transferred to the PresentSrc layout.
with_swapchain_image<T>( &mut self, mut f: impl FnMut(&mut PostResource, Arc<SwapchainImage>, T) -> T, mut userdata: T, ) -> Result<T>815     fn with_swapchain_image<T>(
816         &mut self,
817         mut f: impl FnMut(&mut PostResource, Arc<SwapchainImage>, T) -> T,
818         mut userdata: T,
819     ) -> Result<T> {
820         let mut attempts = 0;
821         const MAX_ATTEMPTS: i32 = 5;
822         loop {
823             if attempts > 0 {
824                 info!("Recreate the swapchain: attempt {}", attempts);
825                 self.recreate_swapchain()
826                     .context("recreate the swapchain")?;
827             }
828             if attempts > MAX_ATTEMPTS {
829                 bail!(
830                     "The swapchain is always suboptimal or out of date with {} attempts.",
831                     attempts
832                 );
833             }
834             attempts += 1;
835 
836             let post_resource_index = (0..self.post_resources.len()).find(|i| {
837                 self.post_resources[*i]
838                     .command_complete_fence
839                     .is_signaled()
840                     .unwrap_or_else(|e| panic!("Failed to retrieve the fence status: {}", e))
841             });
842             let post_resource = match post_resource_index {
843                 Some(i) => &mut self.post_resources[i],
844                 None => {
845                     let post_resource = self
846                         .post_resources
847                         .iter_mut()
848                         .choose(&mut self.rng)
849                         .expect("post resources shouldn't be empty");
850                     post_resource
851                         .command_complete_fence
852                         .wait(Some(Duration::from_secs(5)))
853                         .unwrap_or_else(|e| {
854                             panic!("Failed to wait for one recource to be available: {:?}.", e)
855                         });
856                     post_resource
857                 }
858             };
859 
860             // SAFETY: Safe because self.swapchain and
861             // post_resource.acquire_swapchain_image_semaphore contain no pointers and outlive this
862             //  call.
863             let (index, present_wait_semaphore) = match unsafe {
864                 acquire_next_image_raw(
865                     self.swapchain.borrow(),
866                     None,
867                     Some(&post_resource.acquire_swapchain_image_semaphore),
868                     None,
869                 )
870             } {
871                 Ok(AcquiredImage {
872                     suboptimal: true, ..
873                 })
874                 | Err(AcquireError::OutOfDate) => continue,
875                 Ok(AcquiredImage {
876                     id: index,
877                     suboptimal: false,
878                 }) => {
879                     userdata = f(
880                         post_resource,
881                         Arc::clone(&self.swapchain_images[index]),
882                         userdata,
883                     );
884                     (index, &post_resource.command_complete_semaphore)
885                 }
886                 Err(err) => return Err(err).context("acquire next image"),
887             };
888             let present_info = PresentInfo {
889                 index,
890                 ..PresentInfo::swapchain(Arc::clone(&self.swapchain))
891             };
892             let mut submit_present_builder = SubmitPresentBuilder::new();
893             // SAFETY: Safe because before destroying the swapchain, we always wait for the queues
894             // to be idle. Hence the swapchain must outlive this presenting.
895             unsafe { submit_present_builder.add_swapchain(&present_info) };
896             // SAFETY: Safe because of the safety requirement of this function.
897             unsafe { submit_present_builder.add_wait_semaphore(present_wait_semaphore) }
898             match submit_present_builder.submit(self.present_queue.borrow()) {
899                 Ok(()) => return Ok(userdata),
900                 Err(SubmitPresentError::OutOfDate) => continue,
901                 Err(err) => return Err(err).context("submit the present request"),
902             }
903         }
904     }
905 
post( &mut self, image: ExternalImage, last_layout_transition: (ImageLayout, ImageLayout), acquire_timepoint: Option<Timepoint>, release_timepoint: Timepoint, ) -> ExternalImage906     pub fn post(
907         &mut self,
908         image: ExternalImage,
909         last_layout_transition: (ImageLayout, ImageLayout),
910         acquire_timepoint: Option<Timepoint>,
911         release_timepoint: Timepoint,
912     ) -> ExternalImage {
913         let device = Arc::clone(&self.device);
914         let ash_device = Arc::clone(&self.ash_device);
915         let graphics_queue = Arc::clone(&self.graphics_queue);
916         let present_queue = Arc::clone(&self.present_queue);
917         self.with_swapchain_image(
918             |post_resource, swapchain_image, image| {
919                 let out_image = post_resource.record_and_submit_post_command(
920                     device.borrow(),
921                     ash_device.borrow(),
922                     image,
923                     last_layout_transition,
924                     swapchain_image,
925                     graphics_queue.borrow(),
926                     present_queue.borrow(),
927                     acquire_timepoint.as_ref(),
928                     &release_timepoint,
929                 );
930                 out_image
931             },
932             image,
933         )
934         .unwrap_or_else(|err| panic!("Failed when posting a frame: {:?}", err))
935     }
936 
device(&self) -> Arc<Device>937     pub fn device(&self) -> Arc<Device> {
938         Arc::clone(&self.device)
939     }
940 }
941 
942 impl Drop for PostWorker {
drop(&mut self)943     fn drop(&mut self) {
944         if let Err(err) = self.drain_queues() {
945             error!(
946                 "Failed to wait for queues to become idle when destroying the post worker: {:?}",
947                 err
948             );
949         }
950     }
951 }
952