wgpu_hal/vulkan/swapchain/native.rs
1//! Vulkan Surface and Swapchain implementation using native Vulkan surfaces.
2
3use alloc::{boxed::Box, sync::Arc, vec::Vec};
4use core::any::Any;
5
6use ash::{google, khr, vk};
7use wgpu_sync::{Mutex, MutexGuard};
8
9use crate::vulkan::{
10 conv, map_host_device_oom_and_lost_err,
11 semaphore_list::SemaphoreType,
12 swapchain::{
13 Surface, SurfaceTextureMetadata, Swapchain, SwapchainSubmissionSemaphoreGuard, WindowHandle,
14 },
15 DeviceShared, InstanceShared, PnextChain,
16};
17
18pub(crate) struct NativeSurface {
19 raw: vk::SurfaceKHR,
20 functor: khr::surface::Instance,
21 instance: Arc<InstanceShared>,
22 /// Built from the window's `HWND` (Windows only) to answer the display-HDR
23 /// query; `None` for non-Win32 surfaces.
24 #[cfg(windows)]
25 hdr_source: Option<crate::auxil::dxgi::hdr::DxgiHdrSource>,
26 /// A caller-provided `pNext` chain to attach to the [`vk::SwapchainCreateInfoKHR`]
27 /// of the next swapchain created for this surface.
28 ///
29 /// Set only through
30 /// [`Surface::set_next_swapchain_create_chain()`](crate::vulkan::Surface::set_next_swapchain_create_chain).
31 next_swapchain_create_chain: Mutex<Option<PnextChain>>,
32}
33
34impl NativeSurface {
35 pub fn from_vk_surface_khr(
36 instance: &crate::vulkan::Instance,
37 raw: vk::SurfaceKHR,
38 hwnd: Option<WindowHandle>,
39 ) -> Self {
40 #[cfg(not(windows))]
41 let _ = hwnd;
42 let functor = khr::surface::Instance::new(&instance.shared.entry, &instance.shared.raw);
43 Self {
44 raw,
45 functor,
46 instance: Arc::clone(&instance.shared),
47 #[cfg(windows)]
48 hdr_source: hwnd.map(|wh| crate::auxil::dxgi::hdr::DxgiHdrSource::new(wh.0)),
49 next_swapchain_create_chain: Mutex::new(None),
50 }
51 }
52
53 pub fn as_raw(&self) -> vk::SurfaceKHR {
54 self.raw
55 }
56
57 /// # Safety
58 ///
59 /// See [`Surface::set_next_swapchain_create_chain()`](crate::vulkan::Surface::set_next_swapchain_create_chain).
60 pub unsafe fn set_next_swapchain_create_chain(&self, chain: *mut core::ffi::c_void) {
61 *self.next_swapchain_create_chain.lock() = Some(PnextChain::new(chain));
62 }
63}
64
65impl Drop for NativeSurface {
66 fn drop(&mut self) {
67 unsafe {
68 self.functor.destroy_surface(self.raw, None);
69 }
70 }
71}
72
73impl Surface for NativeSurface {
74 fn surface_capabilities(
75 &self,
76 adapter: &crate::vulkan::Adapter,
77 ) -> Option<crate::SurfaceCapabilities> {
78 if !adapter.private_caps.can_present {
79 return None;
80 }
81 let queue_family_index = 0; //TODO
82 {
83 profiling::scope!("vkGetPhysicalDeviceSurfaceSupportKHR");
84 match unsafe {
85 self.functor.get_physical_device_surface_support(
86 adapter.raw,
87 queue_family_index,
88 self.raw,
89 )
90 } {
91 Ok(true) => (),
92 Ok(false) => return None,
93 Err(e) => {
94 log::error!("get_physical_device_surface_support: {e}");
95 return None;
96 }
97 }
98 }
99
100 let caps = {
101 profiling::scope!("vkGetPhysicalDeviceSurfaceCapabilitiesKHR");
102 match unsafe {
103 self.functor
104 .get_physical_device_surface_capabilities(adapter.raw, self.raw)
105 } {
106 Ok(caps) => caps,
107 Err(e) => {
108 log::error!("get_physical_device_surface_capabilities: {e}");
109 return None;
110 }
111 }
112 };
113
114 // If image count is 0, the support number of images is unlimited.
115 let max_image_count = if caps.max_image_count == 0 {
116 !0
117 } else {
118 caps.max_image_count
119 };
120
121 // `0xFFFFFFFF` indicates that the extent depends on the created swapchain.
122 let current_extent = if caps.current_extent.width != !0 && caps.current_extent.height != !0
123 {
124 Some(wgt::Extent3d {
125 width: caps.current_extent.width,
126 height: caps.current_extent.height,
127 depth_or_array_layers: 1,
128 })
129 } else {
130 None
131 };
132
133 let raw_present_modes = {
134 profiling::scope!("vkGetPhysicalDeviceSurfacePresentModesKHR");
135 match unsafe {
136 self.functor
137 .get_physical_device_surface_present_modes(adapter.raw, self.raw)
138 } {
139 Ok(present_modes) => present_modes,
140 Err(e) => {
141 log::error!("get_physical_device_surface_present_modes: {e}");
142 // Per definition of `SurfaceCapabilities`, there must be at least one present mode.
143 return None;
144 }
145 }
146 };
147
148 let raw_surface_formats = {
149 profiling::scope!("vkGetPhysicalDeviceSurfaceFormatsKHR");
150 match unsafe {
151 self.functor
152 .get_physical_device_surface_formats(adapter.raw, self.raw)
153 } {
154 Ok(formats) => formats,
155 Err(e) => {
156 log::error!("get_physical_device_surface_formats: {e}");
157 // Per definition of `SurfaceCapabilities`, there must be at least one present format.
158 return None;
159 }
160 }
161 };
162
163 // Group the driver's (format, color space) pairs into one entry per
164 // format, preserving the driver's format order.
165 let mut formats: Vec<wgt::SurfaceFormatCapabilities> = Vec::new();
166 for (format, color_space) in raw_surface_formats
167 .into_iter()
168 .filter_map(conv::map_vk_surface_formats)
169 {
170 let color_spaces = color_space.to_color_spaces().unwrap();
171 match formats.iter_mut().find(|fc| fc.format == format) {
172 Some(fc) => fc.color_spaces |= color_spaces,
173 None => formats.push(wgt::SurfaceFormatCapabilities {
174 format,
175 color_spaces,
176 }),
177 }
178 }
179 Some(crate::SurfaceCapabilities {
180 formats,
181 // TODO: Right now we're always truncating the swap chain
182 // (presumably - we're actually setting the min image count which isn't necessarily the swap chain size)
183 // Instead, we should use extensions when available to wait in present.
184 // See https://github.com/gfx-rs/wgpu/issues/2869
185 maximum_frame_latency: (caps.min_image_count - 1)..=(max_image_count - 1), // Note this can't underflow since both `min_image_count` is at least one and we already patched `max_image_count`.
186 current_extent,
187 usage: conv::map_vk_image_usage(caps.supported_usage_flags),
188 present_modes: raw_present_modes
189 .into_iter()
190 .flat_map(conv::map_vk_present_mode)
191 .collect(),
192 composite_alpha_modes: conv::map_vk_composite_alpha(caps.supported_composite_alpha),
193 })
194 }
195
196 unsafe fn create_swapchain(
197 &self,
198 device: &crate::vulkan::Device,
199 config: &crate::SurfaceConfiguration,
200 provided_old_swapchain: Option<Box<dyn Swapchain>>,
201 ) -> Result<Box<dyn Swapchain>, crate::SurfaceError> {
202 profiling::scope!("Device::create_swapchain");
203 let functor = khr::swapchain::Device::new(&self.instance.raw, &device.shared.raw);
204
205 let old_swapchain = provided_old_swapchain
206 .as_ref()
207 .map(|osc| osc.as_any().downcast_ref::<NativeSwapchain>().unwrap().raw)
208 .unwrap_or(vk::SwapchainKHR::null());
209
210 let color_space = conv::map_surface_color_space(config.color_space);
211
212 let original_format = device.shared.private_caps.map_texture_format(config.format);
213 let mut raw_flags = vk::SwapchainCreateFlagsKHR::empty();
214 let mut raw_view_formats: Vec<vk::Format> = vec![];
215 if !config.view_formats.is_empty() {
216 raw_flags |= vk::SwapchainCreateFlagsKHR::MUTABLE_FORMAT;
217 raw_view_formats = config
218 .view_formats
219 .iter()
220 .map(|f| device.shared.private_caps.map_texture_format(*f))
221 .collect();
222 raw_view_formats.push(original_format);
223 }
224
225 let mut info = vk::SwapchainCreateInfoKHR::default()
226 .flags(raw_flags)
227 .surface(self.raw)
228 .min_image_count(config.maximum_frame_latency + 1) // TODO: https://github.com/gfx-rs/wgpu/issues/2869
229 .image_format(original_format)
230 .image_color_space(color_space)
231 .image_extent(vk::Extent2D {
232 width: config.extent.width,
233 height: config.extent.height,
234 })
235 .image_array_layers(config.extent.depth_or_array_layers)
236 .image_usage(conv::map_texture_usage(config.usage))
237 .image_sharing_mode(vk::SharingMode::EXCLUSIVE)
238 .pre_transform(vk::SurfaceTransformFlagsKHR::IDENTITY)
239 .composite_alpha(conv::map_composite_alpha_mode(config.composite_alpha_mode))
240 .present_mode(conv::map_present_mode(config.present_mode))
241 .clipped(true)
242 .old_swapchain(old_swapchain);
243
244 let mut format_list_info = vk::ImageFormatListCreateInfo::default();
245 if !raw_view_formats.is_empty() {
246 format_list_info = format_list_info.view_formats(&raw_view_formats);
247 info = info.push_next(&mut format_list_info);
248 }
249
250 let create_chain = self.next_swapchain_create_chain.lock().take();
251 if let Some(chain) = create_chain {
252 // SAFETY: The contract on `Surface::set_next_swapchain_create_chain()` keeps
253 // the chain valid and unaliased until this swapchain creation returns.
254 info.p_next = unsafe { chain.splice_into(info.p_next) };
255 }
256
257 let result = {
258 profiling::scope!("vkCreateSwapchainKHR");
259 unsafe { functor.create_swapchain(&info, None) }
260 };
261
262 let raw = match result {
263 Ok(swapchain) => swapchain,
264 Err(error) => {
265 return Err(match error {
266 vk::Result::ERROR_SURFACE_LOST_KHR
267 | vk::Result::ERROR_INITIALIZATION_FAILED => crate::SurfaceError::Lost,
268 vk::Result::ERROR_NATIVE_WINDOW_IN_USE_KHR => {
269 crate::SurfaceError::Other("Native window is in use")
270 }
271 // We don't use VK_EXT_image_compression_control
272 // VK_ERROR_COMPRESSION_EXHAUSTED_EXT
273 other => map_host_device_oom_and_lost_err(other).into(),
274 });
275 }
276 };
277
278 let images = unsafe { functor.get_swapchain_images(raw) }
279 .map_err(crate::vulkan::map_host_device_oom_err)?;
280
281 // This fence is only used to throttle acquisition on Windows. It is very important to
282 // avoid bad frame pacing when the Vulkan driver is using a DXGI swapchain. See
283 // https://github.com/gfx-rs/wgpu/issues/8310 and
284 // https://github.com/gfx-rs/wgpu/issues/8354 for more details.
285 let fence = if cfg!(target_os = "windows") {
286 let raw = unsafe {
287 device
288 .shared
289 .raw
290 .create_fence(&vk::FenceCreateInfo::default(), None)
291 .map_err(crate::vulkan::map_host_device_oom_err)?
292 };
293 Some(raw)
294 } else {
295 None
296 };
297
298 // NOTE: It's important that we define the same number of acquire/present semaphores
299 // as we will need to index into them with the image index.
300 let acquire_semaphores = (0..images.len())
301 .map(|i| {
302 SwapchainAcquireSemaphore::new(&device.shared, i)
303 .map(Mutex::new)
304 .map(Arc::new)
305 })
306 .collect::<Result<Vec<_>, _>>()?;
307
308 let present_semaphores = (0..images.len())
309 .map(|i| Arc::new(Mutex::new(SwapchainPresentSemaphores::new(i))))
310 .collect::<Vec<_>>();
311
312 Ok(Box::new(NativeSwapchain {
313 raw,
314 functor,
315 device: Arc::clone(&device.shared),
316 images,
317 fence,
318 config: config.clone(),
319 acquire_semaphores,
320 next_acquire_index: 0,
321 present_semaphores,
322 next_present_time: None,
323 next_present_chain: None,
324 }))
325 }
326
327 #[cfg(windows)]
328 fn display_hdr_info(&self) -> Option<wgt::DisplayHdrInfo> {
329 self.hdr_source.as_ref()?.display_hdr_info()
330 }
331
332 fn as_any(&self) -> &dyn Any {
333 self
334 }
335}
336
337pub(crate) struct NativeSwapchain {
338 raw: vk::SwapchainKHR,
339 functor: khr::swapchain::Device,
340 device: Arc<DeviceShared>,
341 images: Vec<vk::Image>,
342 /// Fence used to wait on the acquired image.
343 fence: Option<vk::Fence>,
344 config: crate::SurfaceConfiguration,
345
346 /// Semaphores used between image acquisition and the first submission
347 /// that uses that image. This is indexed using [`next_acquire_index`].
348 ///
349 /// Because we need to provide this to [`vkAcquireNextImageKHR`], we haven't
350 /// received the swapchain image index for the frame yet, so we cannot use
351 /// that to index it.
352 ///
353 /// Before we pass this to [`vkAcquireNextImageKHR`], we ensure that we wait on
354 /// the submission indicated by [`previously_used_submission_index`]. This ensures
355 /// the semaphore is no longer in use before we use it.
356 ///
357 /// [`next_acquire_index`]: NativeSwapchain::next_acquire_index
358 /// [`vkAcquireNextImageKHR`]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#vkAcquireNextImageKHR
359 /// [`previously_used_submission_index`]: SwapchainAcquireSemaphore::previously_used_submission_index
360 acquire_semaphores: Vec<Arc<Mutex<SwapchainAcquireSemaphore>>>,
361 /// The index of the next acquire semaphore to use.
362 ///
363 /// This is incremented each time we acquire a new image, and wraps around
364 /// to 0 when it reaches the end of [`acquire_semaphores`].
365 ///
366 /// [`acquire_semaphores`]: NativeSwapchain::acquire_semaphores
367 next_acquire_index: usize,
368
369 /// Semaphore sets used between all submissions that write to an image and
370 /// the presentation of that image.
371 ///
372 /// This is indexed by the swapchain image index returned by
373 /// [`vkAcquireNextImageKHR`].
374 ///
375 /// We know it is safe to use these semaphores because use them
376 /// _after_ the acquire semaphore. Because the acquire semaphore
377 /// has been signaled, the previous presentation using that image
378 /// is known-finished, so this semaphore is no longer in use.
379 ///
380 /// [`vkAcquireNextImageKHR`]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#vkAcquireNextImageKHR
381 present_semaphores: Vec<Arc<Mutex<SwapchainPresentSemaphores>>>,
382
383 /// The present timing information which will be set in the next call to [`present()`](crate::Queue::present()).
384 ///
385 /// # Safety
386 ///
387 /// This must only be set if the VK_GOOGLE_display_timing extension is enabled.
388 next_present_time: Option<vk::PresentTimeGOOGLE>,
389
390 /// A caller-provided `pNext` chain to attach to the [`vk::PresentInfoKHR`] of the next
391 /// call to [`present()`](crate::Queue::present()).
392 ///
393 /// Set only through
394 /// [`Surface::set_next_present_chain()`](crate::vulkan::Surface::set_next_present_chain).
395 next_present_chain: Option<PnextChain>,
396}
397
398impl Drop for NativeSwapchain {
399 fn drop(&mut self) {
400 unsafe {
401 self.functor.destroy_swapchain(self.raw, None);
402 }
403 }
404}
405
406/// Destroy the swapchain semaphores that are no longer referenced by a live
407/// surface texture. A still-referenced semaphore is skipped, as the texture
408/// may still submit work using it.
409///
410/// The vectors are drained.
411fn destroy_swapchain_semaphores(
412 acquire_semaphores: &mut Vec<Arc<Mutex<SwapchainAcquireSemaphore>>>,
413 present_semaphores: &mut Vec<Arc<Mutex<SwapchainPresentSemaphores>>>,
414 device: &crate::vulkan::Device,
415) {
416 for semaphore in acquire_semaphores.drain(..) {
417 if let Some(mutex_removed) = Arc::into_inner(semaphore) {
418 let semaphore_removed = mutex_removed.into_inner();
419 unsafe { semaphore_removed.destroy(&device.shared.raw) };
420 }
421 }
422
423 for semaphore in present_semaphores.drain(..) {
424 if let Some(mutex_removed) = Arc::into_inner(semaphore) {
425 let semaphore_removed = mutex_removed.into_inner();
426 unsafe { semaphore_removed.destroy(&device.shared.raw) };
427 }
428 }
429}
430
431impl Swapchain for NativeSwapchain {
432 unsafe fn release_resources(&mut self, device: &crate::vulkan::Device) {
433 profiling::scope!("Swapchain::release_resources");
434 {
435 profiling::scope!("vkDeviceWaitIdle");
436 // We need to also wait until all presentation work is done. Because there is no way to portably wait until
437 // the presentation work is done, we are forced to wait until the device is idle.
438 let _ = unsafe {
439 device
440 .shared
441 .raw
442 .device_wait_idle()
443 .map_err(map_host_device_oom_and_lost_err)
444 };
445 };
446
447 if let Some(fence) = self.fence {
448 unsafe { device.shared.raw.destroy_fence(fence, None) }
449 }
450
451 destroy_swapchain_semaphores(
452 &mut self.acquire_semaphores,
453 &mut self.present_semaphores,
454 device,
455 );
456 }
457
458 unsafe fn acquire(
459 &mut self,
460 timeout: Option<core::time::Duration>,
461 fence: &crate::vulkan::Fence,
462 ) -> Result<crate::AcquiredSurfaceTexture<crate::api::Vulkan>, crate::SurfaceError> {
463 let mut timeout_ns = match timeout {
464 Some(duration) => duration.as_nanos() as u64,
465 None => u64::MAX,
466 };
467
468 // AcquireNextImageKHR on Android (prior to Android 11) doesn't support timeouts
469 // and will also log verbose warnings if tying to use a timeout.
470 //
471 // Android 10 implementation for reference:
472 // https://android.googlesource.com/platform/frameworks/native/+/refs/tags/android-mainline-10.0.0_r13/vulkan/libvulkan/swapchain.cpp#1426
473 // Android 11 implementation for reference:
474 // https://android.googlesource.com/platform/frameworks/native/+/refs/tags/android-mainline-11.0.0_r45/vulkan/libvulkan/swapchain.cpp#1438
475 //
476 // Android 11 corresponds to an SDK_INT/ro.build.version.sdk of 30
477 if cfg!(target_os = "android") && self.device.instance.android_sdk_version < 30 {
478 timeout_ns = u64::MAX;
479 }
480
481 let acquire_semaphore_arc = self.get_acquire_semaphore();
482 // Nothing should be using this, so we don't block, but panic if we fail to lock.
483 let acquire_semaphore_guard = acquire_semaphore_arc
484 .try_lock()
485 .expect("Failed to lock a SwapchainSemaphores.");
486
487 // Wait for all commands writing to the previously acquired image to
488 // complete.
489 //
490 // Almost all the steps in the usual acquire-draw-present flow are
491 // asynchronous: they get something started on the presentation engine
492 // or the GPU, but on the CPU, control returns immediately. Without some
493 // sort of intervention, the CPU could crank out frames much faster than
494 // the presentation engine can display them.
495 //
496 // This is the intervention: if any submissions drew on this image, and
497 // thus waited for `locked_swapchain_semaphores.acquire`, wait for all
498 // of them to finish, thus ensuring that it's okay to pass `acquire` to
499 // `vkAcquireNextImageKHR` again.
500 let completed = self.device.wait_for_fence(
501 fence,
502 acquire_semaphore_guard.previously_used_submission_index,
503 timeout_ns,
504 )?;
505 if !completed {
506 return Err(crate::SurfaceError::Timeout);
507 }
508
509 let acquire_fence = self.fence.unwrap_or_else(vk::Fence::null);
510
511 // will block if no image is available
512 let (index, suboptimal) = match unsafe {
513 profiling::scope!("vkAcquireNextImageKHR");
514 self.functor.acquire_next_image(
515 self.raw,
516 timeout_ns,
517 acquire_semaphore_guard.acquire,
518 acquire_fence,
519 )
520 } {
521 // We treat `VK_SUBOPTIMAL_KHR` as `VK_SUCCESS` on Android.
522 // See the comment in `Queue::present`.
523 #[cfg(target_os = "android")]
524 Ok((index, _)) => (index, false),
525 #[cfg(not(target_os = "android"))]
526 Ok(pair) => pair,
527 Err(error) => {
528 return match error {
529 vk::Result::TIMEOUT => Err(crate::SurfaceError::Timeout),
530 vk::Result::NOT_READY | vk::Result::ERROR_OUT_OF_DATE_KHR => {
531 Err(crate::SurfaceError::Outdated)
532 }
533 vk::Result::ERROR_SURFACE_LOST_KHR => Err(crate::SurfaceError::Lost),
534 // We don't use VK_EXT_full_screen_exclusive
535 // VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT
536 other => Err(map_host_device_oom_and_lost_err(other).into()),
537 };
538 }
539 };
540
541 if let Some(fence) = self.fence {
542 unsafe {
543 // The `wait_all` argument must be `true` to avoid crash on some Android devices. See https://github.com/gfx-rs/wgpu/pull/8769
544 self.device
545 .raw
546 .wait_for_fences(&[fence], true, timeout_ns)
547 .map_err(map_host_device_oom_and_lost_err)?;
548
549 self.device
550 .raw
551 .reset_fences(&[fence])
552 .map_err(map_host_device_oom_and_lost_err)?;
553 }
554 }
555
556 drop(acquire_semaphore_guard);
557 // We only advance the surface semaphores if we successfully acquired an image, otherwise
558 // we should try to re-acquire using the same semaphores.
559 self.advance_acquire_semaphore();
560
561 let present_semaphore_arc = self.get_present_semaphores(index);
562
563 // special case for Intel Vulkan returning bizarre values (ugh)
564 if self.device.vendor_id == crate::auxil::db::intel::VENDOR && index > 0x100 {
565 return Err(crate::SurfaceError::Outdated);
566 }
567
568 let identity = self.device.texture_identity_factory.next();
569
570 let texture = crate::vulkan::SurfaceTexture {
571 index,
572 texture: crate::vulkan::Texture {
573 raw: self.images[index as usize],
574 drop_guard: None,
575 memory: crate::vulkan::TextureMemory::External,
576 format: self.config.format,
577 copy_size: crate::CopyExtent {
578 width: self.config.extent.width,
579 height: self.config.extent.height,
580 depth: 1,
581 },
582 identity,
583 },
584 metadata: Box::new(NativeSurfaceTextureMetadata {
585 acquire_semaphores: acquire_semaphore_arc,
586 present_semaphores: present_semaphore_arc,
587 }),
588 };
589 Ok(crate::AcquiredSurfaceTexture {
590 texture,
591 suboptimal,
592 })
593 }
594
595 unsafe fn discard_texture(
596 &mut self,
597 _texture: crate::vulkan::SurfaceTexture,
598 ) -> Result<(), crate::SurfaceError> {
599 // TODO: Current implementation no-ops
600 Ok(())
601 }
602
603 unsafe fn present(
604 &mut self,
605 queue: &crate::vulkan::Queue,
606 texture: crate::vulkan::SurfaceTexture,
607 ) -> Result<(), crate::SurfaceError> {
608 let metadata = texture
609 .metadata
610 .as_any()
611 .downcast_ref::<NativeSurfaceTextureMetadata>()
612 .unwrap();
613 let mut acquire_semaphore = metadata.acquire_semaphores.lock();
614 let mut present_semaphores = metadata.present_semaphores.lock();
615
616 let wait_semaphores = present_semaphores.get_present_wait_semaphores();
617
618 // Reset the acquire and present semaphores internal state
619 // to be ready for the next frame.
620 //
621 // We do this before the actual call to present to ensure that
622 // even if this method errors and early outs, we have reset
623 // the state for next frame.
624 acquire_semaphore.end_semaphore_usage();
625 present_semaphores.end_semaphore_usage();
626
627 drop(acquire_semaphore);
628
629 let swapchains = [self.raw];
630 let image_indices = [texture.index];
631 let vk_info = vk::PresentInfoKHR::default()
632 .swapchains(&swapchains)
633 .image_indices(&image_indices)
634 .wait_semaphores(&wait_semaphores);
635
636 let mut display_timing;
637 let present_times;
638 let mut vk_info = if let Some(present_time) = self.next_present_time.take() {
639 debug_assert!(
640 self.device
641 .enabled_extensions
642 .contains(&google::display_timing::NAME),
643 "`next_present_time` should only be set if VK_GOOGLE_display_timing is enabled"
644 );
645 present_times = [present_time];
646 display_timing = vk::PresentTimesInfoGOOGLE::default().times(&present_times);
647 // SAFETY: We know that VK_GOOGLE_display_timing is present because of the safety contract on `next_present_time`.
648 vk_info.push_next(&mut display_timing)
649 } else {
650 vk_info
651 };
652
653 if let Some(chain) = self.next_present_chain.take() {
654 // SAFETY: The contract on `Surface::set_next_present_chain()` keeps the chain
655 // valid and unaliased until this present completes.
656 vk_info.p_next = unsafe { chain.splice_into(vk_info.p_next) };
657 }
658
659 let suboptimal = {
660 profiling::scope!("vkQueuePresentKHR");
661 unsafe { self.functor.queue_present(queue.raw, &vk_info) }.map_err(|error| {
662 match error {
663 vk::Result::ERROR_OUT_OF_DATE_KHR => crate::SurfaceError::Outdated,
664 vk::Result::ERROR_SURFACE_LOST_KHR => crate::SurfaceError::Lost,
665 // We don't use VK_EXT_full_screen_exclusive
666 // VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT
667 _ => map_host_device_oom_and_lost_err(error).into(),
668 }
669 })?
670 };
671 if suboptimal {
672 // We treat `VK_SUBOPTIMAL_KHR` as `VK_SUCCESS` on Android.
673 // On Android 10+, libvulkan's `vkQueuePresentKHR` implementation returns `VK_SUBOPTIMAL_KHR` if not doing pre-rotation
674 // (i.e `VkSwapchainCreateInfoKHR::preTransform` not being equal to the current device orientation).
675 // This is always the case when the device orientation is anything other than the identity one, as we unconditionally use `VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR`.
676 #[cfg(not(target_os = "android"))]
677 log::debug!("Suboptimal present of frame {}", texture.index);
678 }
679 Ok(())
680 }
681
682 fn as_any(&self) -> &dyn Any {
683 self
684 }
685
686 fn as_any_mut(&mut self) -> &mut dyn Any {
687 self
688 }
689}
690
691impl NativeSwapchain {
692 pub(crate) fn as_raw(&self) -> vk::SwapchainKHR {
693 self.raw
694 }
695
696 pub fn set_next_present_time(&mut self, present_timing: vk::PresentTimeGOOGLE) {
697 if self
698 .device
699 .enabled_extensions
700 .contains(&google::display_timing::NAME)
701 {
702 self.next_present_time = Some(present_timing);
703 } else {
704 panic!(
705 "Tried to set display timing properties without the VK_GOOGLE_display_timing extension enabled."
706 );
707 }
708 }
709
710 /// # Safety
711 ///
712 /// See [`Surface::set_next_present_chain()`](crate::vulkan::Surface::set_next_present_chain).
713 pub unsafe fn set_next_present_chain(&mut self, chain: *mut core::ffi::c_void) {
714 self.next_present_chain = Some(PnextChain::new(chain));
715 }
716
717 /// Mark the current frame finished, advancing to the next acquire semaphore.
718 fn advance_acquire_semaphore(&mut self) {
719 let semaphore_count = self.acquire_semaphores.len();
720 self.next_acquire_index = (self.next_acquire_index + 1) % semaphore_count;
721 }
722
723 /// Get the next acquire semaphore that should be used with this swapchain.
724 fn get_acquire_semaphore(&self) -> Arc<Mutex<SwapchainAcquireSemaphore>> {
725 self.acquire_semaphores[self.next_acquire_index].clone()
726 }
727
728 /// Get the set of present semaphores that should be used with the given image index.
729 fn get_present_semaphores(&self, index: u32) -> Arc<Mutex<SwapchainPresentSemaphores>> {
730 self.present_semaphores[index as usize].clone()
731 }
732}
733
734/// Semaphore used to acquire a swapchain image.
735#[derive(Debug)]
736struct SwapchainAcquireSemaphore {
737 /// A semaphore that is signaled when this image is safe for us to modify.
738 ///
739 /// When [`vkAcquireNextImageKHR`] returns the index of the next swapchain
740 /// image that we should use, that image may actually still be in use by the
741 /// presentation engine, and is not yet safe to modify. However, that
742 /// function does accept a semaphore that it will signal when the image is
743 /// indeed safe to begin messing with.
744 ///
745 /// This semaphore is:
746 ///
747 /// - waited for by the first queue submission to operate on this image
748 /// since it was acquired, and
749 ///
750 /// - signaled by [`vkAcquireNextImageKHR`] when the acquired image is ready
751 /// for us to use.
752 ///
753 /// [`vkAcquireNextImageKHR`]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#vkAcquireNextImageKHR
754 acquire: vk::Semaphore,
755
756 /// True if the next command submission operating on this image should wait
757 /// for [`acquire`].
758 ///
759 /// We must wait for `acquire` before drawing to this swapchain image, but
760 /// because `wgpu-hal` queue submissions are always strongly ordered, only
761 /// the first submission that works with a swapchain image actually needs to
762 /// wait. We set this flag when this image is acquired, and clear it the
763 /// first time it's passed to [`Queue::submit`] as a surface texture.
764 ///
765 /// Additionally, semaphores can only be waited on once, so we need to ensure
766 /// that we only actually pass this semaphore to the first submission that
767 /// uses that image.
768 ///
769 /// [`acquire`]: SwapchainAcquireSemaphore::acquire
770 /// [`Queue::submit`]: crate::Queue::submit
771 should_wait_for_acquire: bool,
772
773 /// The fence value of the last command submission that wrote to this image.
774 ///
775 /// The next time we try to acquire this image, we'll block until
776 /// this submission finishes, proving that [`acquire`] is ready to
777 /// pass to `vkAcquireNextImageKHR` again.
778 ///
779 /// [`acquire`]: SwapchainAcquireSemaphore::acquire
780 previously_used_submission_index: crate::FenceValue,
781}
782
783impl SwapchainAcquireSemaphore {
784 fn new(device: &DeviceShared, index: usize) -> Result<Self, crate::DeviceError> {
785 Ok(Self {
786 acquire: device
787 .new_binary_semaphore(&format!("SwapchainImageSemaphore: Index {index} acquire"))?,
788 should_wait_for_acquire: true,
789 previously_used_submission_index: 0,
790 })
791 }
792
793 /// Sets the fence value which the next acquire will wait for. This prevents
794 /// the semaphore from being used while the previous submission is still in flight.
795 fn set_used_fence_value(&mut self, value: crate::FenceValue) {
796 self.previously_used_submission_index = value;
797 }
798
799 /// Return the semaphore that commands drawing to this image should wait for, if any.
800 ///
801 /// This only returns `Some` once per acquisition; see
802 /// [`SwapchainAcquireSemaphore::should_wait_for_acquire`] for details.
803 fn get_acquire_wait_semaphore(&mut self) -> Option<vk::Semaphore> {
804 if self.should_wait_for_acquire {
805 self.should_wait_for_acquire = false;
806 Some(self.acquire)
807 } else {
808 None
809 }
810 }
811
812 /// Indicates the cpu-side usage of this semaphore has finished for the frame,
813 /// so reset internal state to be ready for the next frame.
814 fn end_semaphore_usage(&mut self) {
815 // Reset the acquire semaphore, so that the next time we acquire this
816 // image, we can wait for it again.
817 self.should_wait_for_acquire = true;
818 }
819
820 unsafe fn destroy(&self, device: &ash::Device) {
821 unsafe {
822 device.destroy_semaphore(self.acquire, None);
823 }
824 }
825}
826
827#[derive(Debug)]
828struct SwapchainPresentSemaphores {
829 /// A pool of semaphores for ordering presentation after drawing.
830 ///
831 /// The first [`present_index`] semaphores in this vector are:
832 ///
833 /// - all waited on by the call to [`vkQueuePresentKHR`] that presents this
834 /// image, and
835 ///
836 /// - each signaled by some [`vkQueueSubmit`] queue submission that draws to
837 /// this image, when the submission finishes execution.
838 ///
839 /// This vector accumulates one semaphore per submission that writes to this
840 /// image. This is awkward, but hard to avoid: [`vkQueuePresentKHR`]
841 /// requires a semaphore to order it with respect to drawing commands, and
842 /// we can't attach new completion semaphores to a command submission after
843 /// it's been submitted. This means that, at submission time, we must create
844 /// the semaphore we might need if the caller's next action is to enqueue a
845 /// presentation of this image.
846 ///
847 /// An alternative strategy would be for presentation to enqueue an empty
848 /// submit, ordered relative to other submits in the usual way, and
849 /// signaling a single presentation semaphore. But we suspect that submits
850 /// are usually expensive enough, and semaphores usually cheap enough, that
851 /// performance-sensitive users will avoid making many submits, so that the
852 /// cost of accumulated semaphores will usually be less than the cost of an
853 /// additional submit.
854 ///
855 /// Only the first [`present_index`] semaphores in the vector are actually
856 /// going to be signalled by submitted commands, and need to be waited for
857 /// by the next present call. Any semaphores beyond that index were created
858 /// for prior presents and are simply being retained for recycling.
859 ///
860 /// [`present_index`]: SwapchainPresentSemaphores::present_index
861 /// [`vkQueuePresentKHR`]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#vkQueuePresentKHR
862 /// [`vkQueueSubmit`]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#vkQueueSubmit
863 present: Vec<vk::Semaphore>,
864
865 /// The number of semaphores in [`present`] to be signalled for this submission.
866 ///
867 /// [`present`]: SwapchainPresentSemaphores::present
868 present_index: usize,
869
870 /// Which image this semaphore set is used for.
871 frame_index: usize,
872}
873
874impl SwapchainPresentSemaphores {
875 pub fn new(frame_index: usize) -> Self {
876 Self {
877 present: Vec::new(),
878 present_index: 0,
879 frame_index,
880 }
881 }
882
883 /// Return the semaphore that the next submission that writes to this image should
884 /// signal when it's done.
885 ///
886 /// See [`SwapchainPresentSemaphores::present`] for details.
887 fn get_submit_signal_semaphore(
888 &mut self,
889 device: &DeviceShared,
890 ) -> Result<vk::Semaphore, crate::DeviceError> {
891 // Try to recycle a semaphore we created for a previous presentation.
892 let sem = match self.present.get(self.present_index) {
893 Some(sem) => *sem,
894 None => {
895 let sem = device.new_binary_semaphore(&format!(
896 "SwapchainImageSemaphore: Image {} present semaphore {}",
897 self.frame_index, self.present_index
898 ))?;
899 self.present.push(sem);
900 sem
901 }
902 };
903
904 self.present_index += 1;
905
906 Ok(sem)
907 }
908
909 /// Indicates the cpu-side usage of this semaphore has finished for the frame,
910 /// so reset internal state to be ready for the next frame.
911 fn end_semaphore_usage(&mut self) {
912 // Reset the index to 0, so that the next time we get a semaphore, we
913 // start from the beginning of the list.
914 self.present_index = 0;
915 }
916
917 /// Return the semaphores that a presentation of this image should wait on.
918 ///
919 /// Return a slice of semaphores that the call to [`vkQueueSubmit`] that
920 /// ends this image's acquisition should wait for. See
921 /// [`SwapchainPresentSemaphores::present`] for details.
922 ///
923 /// Reset `self` to be ready for the next acquisition cycle.
924 ///
925 /// [`vkQueueSubmit`]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#vkQueueSubmit
926 fn get_present_wait_semaphores(&mut self) -> Vec<vk::Semaphore> {
927 self.present[0..self.present_index].to_vec()
928 }
929
930 unsafe fn destroy(&self, device: &ash::Device) {
931 unsafe {
932 for sem in &self.present {
933 device.destroy_semaphore(*sem, None);
934 }
935 }
936 }
937}
938
939#[derive(Debug)]
940struct NativeSurfaceTextureMetadata {
941 acquire_semaphores: Arc<Mutex<SwapchainAcquireSemaphore>>,
942 present_semaphores: Arc<Mutex<SwapchainPresentSemaphores>>,
943}
944
945impl SurfaceTextureMetadata for NativeSurfaceTextureMetadata {
946 fn get_semaphore_guard(&self) -> Box<dyn SwapchainSubmissionSemaphoreGuard + '_> {
947 Box::new(NativeSwapchainSubmissionSemaphoreGuard {
948 acquire_semaphore_guard: self
949 .acquire_semaphores
950 .try_lock()
951 .expect("Failed to lock surface acquire semaphore"),
952 present_semaphores_guard: self
953 .present_semaphores
954 .try_lock()
955 .expect("Failed to lock surface present semaphores"),
956 })
957 }
958
959 fn as_any(&self) -> &dyn Any {
960 self
961 }
962}
963
964struct NativeSwapchainSubmissionSemaphoreGuard<'a> {
965 acquire_semaphore_guard: MutexGuard<'a, SwapchainAcquireSemaphore>,
966 present_semaphores_guard: MutexGuard<'a, SwapchainPresentSemaphores>,
967}
968
969impl<'a> SwapchainSubmissionSemaphoreGuard for NativeSwapchainSubmissionSemaphoreGuard<'a> {
970 fn set_used_fence_value(&mut self, value: u64) {
971 self.acquire_semaphore_guard.set_used_fence_value(value);
972 }
973
974 fn get_acquire_wait_semaphore(&mut self) -> Option<SemaphoreType> {
975 self.acquire_semaphore_guard
976 .get_acquire_wait_semaphore()
977 .map(SemaphoreType::Binary)
978 }
979
980 fn get_submit_signal_semaphore(
981 &mut self,
982 device: &DeviceShared,
983 ) -> Result<SemaphoreType, crate::DeviceError> {
984 self.present_semaphores_guard
985 .get_submit_signal_semaphore(device)
986 .map(SemaphoreType::Binary)
987 }
988}