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