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