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