Skip to main content

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 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 [`wgt::Features::VULKAN_GOOGLE_DISPLAY_TIMING`] is enabled, and
388    /// so the VK_GOOGLE_display_timing extension is present.
389    next_present_time: Option<vk::PresentTimeGOOGLE>,
390
391    /// A caller-provided `pNext` chain to attach to the [`vk::PresentInfoKHR`] of the next
392    /// call to [`present()`](crate::Queue::present()).
393    ///
394    /// Set only through
395    /// [`Surface::set_next_present_chain()`](crate::vulkan::Surface::set_next_present_chain).
396    next_present_chain: Option<PnextChain>,
397}
398
399impl Drop for NativeSwapchain {
400    fn drop(&mut self) {
401        unsafe {
402            self.functor.destroy_swapchain(self.raw, None);
403        }
404    }
405}
406
407/// Destroy the swapchain semaphores that are no longer referenced by a live
408/// surface texture. A still-referenced semaphore is skipped, as the texture
409/// may still submit work using it.
410///
411/// The vectors are drained.
412fn destroy_swapchain_semaphores(
413    acquire_semaphores: &mut Vec<Arc<Mutex<SwapchainAcquireSemaphore>>>,
414    present_semaphores: &mut Vec<Arc<Mutex<SwapchainPresentSemaphores>>>,
415    device: &crate::vulkan::Device,
416) {
417    for semaphore in acquire_semaphores.drain(..) {
418        if let Some(mutex_removed) = Arc::into_inner(semaphore) {
419            let semaphore_removed = mutex_removed.into_inner();
420            unsafe { semaphore_removed.destroy(&device.shared.raw) };
421        }
422    }
423
424    for semaphore in present_semaphores.drain(..) {
425        if let Some(mutex_removed) = Arc::into_inner(semaphore) {
426            let semaphore_removed = mutex_removed.into_inner();
427            unsafe { semaphore_removed.destroy(&device.shared.raw) };
428        }
429    }
430}
431
432impl Swapchain for NativeSwapchain {
433    unsafe fn release_resources(&mut self, device: &crate::vulkan::Device) {
434        profiling::scope!("Swapchain::release_resources");
435        {
436            profiling::scope!("vkDeviceWaitIdle");
437            // We need to also wait until all presentation work is done. Because there is no way to portably wait until
438            // the presentation work is done, we are forced to wait until the device is idle.
439            let _ = unsafe {
440                device
441                    .shared
442                    .raw
443                    .device_wait_idle()
444                    .map_err(map_host_device_oom_and_lost_err)
445            };
446        };
447
448        if let Some(fence) = self.fence {
449            unsafe { device.shared.raw.destroy_fence(fence, None) }
450        }
451
452        destroy_swapchain_semaphores(
453            &mut self.acquire_semaphores,
454            &mut self.present_semaphores,
455            device,
456        );
457    }
458
459    unsafe fn acquire(
460        &mut self,
461        timeout: Option<core::time::Duration>,
462        fence: &crate::vulkan::Fence,
463    ) -> Result<crate::AcquiredSurfaceTexture<crate::api::Vulkan>, crate::SurfaceError> {
464        let mut timeout_ns = match timeout {
465            Some(duration) => duration.as_nanos() as u64,
466            None => u64::MAX,
467        };
468
469        // AcquireNextImageKHR on Android (prior to Android 11) doesn't support timeouts
470        // and will also log verbose warnings if tying to use a timeout.
471        //
472        // Android 10 implementation for reference:
473        // https://android.googlesource.com/platform/frameworks/native/+/refs/tags/android-mainline-10.0.0_r13/vulkan/libvulkan/swapchain.cpp#1426
474        // Android 11 implementation for reference:
475        // https://android.googlesource.com/platform/frameworks/native/+/refs/tags/android-mainline-11.0.0_r45/vulkan/libvulkan/swapchain.cpp#1438
476        //
477        // Android 11 corresponds to an SDK_INT/ro.build.version.sdk of 30
478        if cfg!(target_os = "android") && self.device.instance.android_sdk_version < 30 {
479            timeout_ns = u64::MAX;
480        }
481
482        let acquire_semaphore_arc = self.get_acquire_semaphore();
483        // Nothing should be using this, so we don't block, but panic if we fail to lock.
484        let acquire_semaphore_guard = acquire_semaphore_arc
485            .try_lock()
486            .expect("Failed to lock a SwapchainSemaphores.");
487
488        // Wait for all commands writing to the previously acquired image to
489        // complete.
490        //
491        // Almost all the steps in the usual acquire-draw-present flow are
492        // asynchronous: they get something started on the presentation engine
493        // or the GPU, but on the CPU, control returns immediately. Without some
494        // sort of intervention, the CPU could crank out frames much faster than
495        // the presentation engine can display them.
496        //
497        // This is the intervention: if any submissions drew on this image, and
498        // thus waited for `locked_swapchain_semaphores.acquire`, wait for all
499        // of them to finish, thus ensuring that it's okay to pass `acquire` to
500        // `vkAcquireNextImageKHR` again.
501        let completed = self.device.wait_for_fence(
502            fence,
503            acquire_semaphore_guard.previously_used_submission_index,
504            timeout_ns,
505        )?;
506        if !completed {
507            return Err(crate::SurfaceError::Timeout);
508        }
509
510        let acquire_fence = self.fence.unwrap_or_else(vk::Fence::null);
511
512        // will block if no image is available
513        let (index, suboptimal) = match unsafe {
514            profiling::scope!("vkAcquireNextImageKHR");
515            self.functor.acquire_next_image(
516                self.raw,
517                timeout_ns,
518                acquire_semaphore_guard.acquire,
519                acquire_fence,
520            )
521        } {
522            // We treat `VK_SUBOPTIMAL_KHR` as `VK_SUCCESS` on Android.
523            // See the comment in `Queue::present`.
524            #[cfg(target_os = "android")]
525            Ok((index, _)) => (index, false),
526            #[cfg(not(target_os = "android"))]
527            Ok(pair) => pair,
528            Err(error) => {
529                return match error {
530                    vk::Result::TIMEOUT => Err(crate::SurfaceError::Timeout),
531                    vk::Result::NOT_READY | vk::Result::ERROR_OUT_OF_DATE_KHR => {
532                        Err(crate::SurfaceError::Outdated)
533                    }
534                    vk::Result::ERROR_SURFACE_LOST_KHR => Err(crate::SurfaceError::Lost),
535                    // We don't use VK_EXT_full_screen_exclusive
536                    // VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT
537                    other => Err(map_host_device_oom_and_lost_err(other).into()),
538                };
539            }
540        };
541
542        if let Some(fence) = self.fence {
543            unsafe {
544                // The `wait_all` argument must be `true` to avoid crash on some Android devices. See https://github.com/gfx-rs/wgpu/pull/8769
545                self.device
546                    .raw
547                    .wait_for_fences(&[fence], true, timeout_ns)
548                    .map_err(map_host_device_oom_and_lost_err)?;
549
550                self.device
551                    .raw
552                    .reset_fences(&[fence])
553                    .map_err(map_host_device_oom_and_lost_err)?;
554            }
555        }
556
557        drop(acquire_semaphore_guard);
558        // We only advance the surface semaphores if we successfully acquired an image, otherwise
559        // we should try to re-acquire using the same semaphores.
560        self.advance_acquire_semaphore();
561
562        let present_semaphore_arc = self.get_present_semaphores(index);
563
564        // special case for Intel Vulkan returning bizarre values (ugh)
565        if self.device.vendor_id == crate::auxil::db::intel::VENDOR && index > 0x100 {
566            return Err(crate::SurfaceError::Outdated);
567        }
568
569        let identity = self.device.texture_identity_factory.next();
570
571        let texture = crate::vulkan::SurfaceTexture {
572            index,
573            texture: crate::vulkan::Texture {
574                raw: self.images[index as usize],
575                drop_guard: None,
576                memory: crate::vulkan::TextureMemory::External,
577                format: self.config.format,
578                copy_size: crate::CopyExtent {
579                    width: self.config.extent.width,
580                    height: self.config.extent.height,
581                    depth: 1,
582                },
583                identity,
584            },
585            metadata: Box::new(NativeSurfaceTextureMetadata {
586                acquire_semaphores: acquire_semaphore_arc,
587                present_semaphores: present_semaphore_arc,
588            }),
589        };
590        Ok(crate::AcquiredSurfaceTexture {
591            texture,
592            suboptimal,
593        })
594    }
595
596    unsafe fn discard_texture(
597        &mut self,
598        _texture: crate::vulkan::SurfaceTexture,
599    ) -> Result<(), crate::SurfaceError> {
600        // TODO: Current implementation no-ops
601        Ok(())
602    }
603
604    unsafe fn present(
605        &mut self,
606        queue: &crate::vulkan::Queue,
607        texture: crate::vulkan::SurfaceTexture,
608    ) -> Result<(), crate::SurfaceError> {
609        let metadata = texture
610            .metadata
611            .as_any()
612            .downcast_ref::<NativeSurfaceTextureMetadata>()
613            .unwrap();
614        let mut acquire_semaphore = metadata.acquire_semaphores.lock();
615        let mut present_semaphores = metadata.present_semaphores.lock();
616
617        let wait_semaphores = present_semaphores.get_present_wait_semaphores();
618
619        // Reset the acquire and present semaphores internal state
620        // to be ready for the next frame.
621        //
622        // We do this before the actual call to present to ensure that
623        // even if this method errors and early outs, we have reset
624        // the state for next frame.
625        acquire_semaphore.end_semaphore_usage();
626        present_semaphores.end_semaphore_usage();
627
628        drop(acquire_semaphore);
629
630        let swapchains = [self.raw];
631        let image_indices = [texture.index];
632        let vk_info = vk::PresentInfoKHR::default()
633            .swapchains(&swapchains)
634            .image_indices(&image_indices)
635            .wait_semaphores(&wait_semaphores);
636
637        let mut display_timing;
638        let present_times;
639        let mut vk_info = if let Some(present_time) = self.next_present_time.take() {
640            debug_assert!(
641                self.device
642                    .features
643                    .contains(wgt::Features::VULKAN_GOOGLE_DISPLAY_TIMING),
644                "`next_present_time` should only be set if `VULKAN_GOOGLE_DISPLAY_TIMING` is enabled"
645            );
646            present_times = [present_time];
647            display_timing = vk::PresentTimesInfoGOOGLE::default().times(&present_times);
648            // SAFETY: We know that VK_GOOGLE_display_timing is present because of the safety contract on `next_present_time`.
649            vk_info.push_next(&mut display_timing)
650        } else {
651            vk_info
652        };
653
654        if let Some(chain) = self.next_present_chain.take() {
655            // SAFETY: The contract on `Surface::set_next_present_chain()` keeps the chain
656            // valid and unaliased until this present completes.
657            vk_info.p_next = unsafe { chain.splice_into(vk_info.p_next) };
658        }
659
660        let suboptimal = {
661            profiling::scope!("vkQueuePresentKHR");
662            unsafe { self.functor.queue_present(queue.raw, &vk_info) }.map_err(|error| {
663                match error {
664                    vk::Result::ERROR_OUT_OF_DATE_KHR => crate::SurfaceError::Outdated,
665                    vk::Result::ERROR_SURFACE_LOST_KHR => crate::SurfaceError::Lost,
666                    // We don't use VK_EXT_full_screen_exclusive
667                    // VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT
668                    _ => map_host_device_oom_and_lost_err(error).into(),
669                }
670            })?
671        };
672        if suboptimal {
673            // We treat `VK_SUBOPTIMAL_KHR` as `VK_SUCCESS` on Android.
674            // On Android 10+, libvulkan's `vkQueuePresentKHR` implementation returns `VK_SUBOPTIMAL_KHR` if not doing pre-rotation
675            // (i.e `VkSwapchainCreateInfoKHR::preTransform` not being equal to the current device orientation).
676            // 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`.
677            #[cfg(not(target_os = "android"))]
678            log::debug!("Suboptimal present of frame {}", texture.index);
679        }
680        Ok(())
681    }
682
683    fn as_any(&self) -> &dyn Any {
684        self
685    }
686
687    fn as_any_mut(&mut self) -> &mut dyn Any {
688        self
689    }
690}
691
692impl NativeSwapchain {
693    pub(crate) fn as_raw(&self) -> vk::SwapchainKHR {
694        self.raw
695    }
696
697    pub fn set_next_present_time(&mut self, present_timing: vk::PresentTimeGOOGLE) {
698        let features = wgt::Features::VULKAN_GOOGLE_DISPLAY_TIMING;
699        if self.device.features.contains(features) {
700            self.next_present_time = Some(present_timing);
701        } else {
702            // Ideally we'd use something like `device.required_features` here, but that's in `wgpu-core`, which we are a dependency of
703            panic!(
704                concat!(
705                    "Tried to set display timing properties ",
706                    "without the corresponding feature ({:?}) enabled."
707                ),
708                features
709            );
710        }
711    }
712
713    /// # Safety
714    ///
715    /// See [`Surface::set_next_present_chain()`](crate::vulkan::Surface::set_next_present_chain).
716    pub unsafe fn set_next_present_chain(&mut self, chain: *mut core::ffi::c_void) {
717        self.next_present_chain = Some(PnextChain::new(chain));
718    }
719
720    /// Mark the current frame finished, advancing to the next acquire semaphore.
721    fn advance_acquire_semaphore(&mut self) {
722        let semaphore_count = self.acquire_semaphores.len();
723        self.next_acquire_index = (self.next_acquire_index + 1) % semaphore_count;
724    }
725
726    /// Get the next acquire semaphore that should be used with this swapchain.
727    fn get_acquire_semaphore(&self) -> Arc<Mutex<SwapchainAcquireSemaphore>> {
728        self.acquire_semaphores[self.next_acquire_index].clone()
729    }
730
731    /// Get the set of present semaphores that should be used with the given image index.
732    fn get_present_semaphores(&self, index: u32) -> Arc<Mutex<SwapchainPresentSemaphores>> {
733        self.present_semaphores[index as usize].clone()
734    }
735}
736
737/// Semaphore used to acquire a swapchain image.
738#[derive(Debug)]
739struct SwapchainAcquireSemaphore {
740    /// A semaphore that is signaled when this image is safe for us to modify.
741    ///
742    /// When [`vkAcquireNextImageKHR`] returns the index of the next swapchain
743    /// image that we should use, that image may actually still be in use by the
744    /// presentation engine, and is not yet safe to modify. However, that
745    /// function does accept a semaphore that it will signal when the image is
746    /// indeed safe to begin messing with.
747    ///
748    /// This semaphore is:
749    ///
750    /// - waited for by the first queue submission to operate on this image
751    ///   since it was acquired, and
752    ///
753    /// - signaled by [`vkAcquireNextImageKHR`] when the acquired image is ready
754    ///   for us to use.
755    ///
756    /// [`vkAcquireNextImageKHR`]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#vkAcquireNextImageKHR
757    acquire: vk::Semaphore,
758
759    /// True if the next command submission operating on this image should wait
760    /// for [`acquire`].
761    ///
762    /// We must wait for `acquire` before drawing to this swapchain image, but
763    /// because `wgpu-hal` queue submissions are always strongly ordered, only
764    /// the first submission that works with a swapchain image actually needs to
765    /// wait. We set this flag when this image is acquired, and clear it the
766    /// first time it's passed to [`Queue::submit`] as a surface texture.
767    ///
768    /// Additionally, semaphores can only be waited on once, so we need to ensure
769    /// that we only actually pass this semaphore to the first submission that
770    /// uses that image.
771    ///
772    /// [`acquire`]: SwapchainAcquireSemaphore::acquire
773    /// [`Queue::submit`]: crate::Queue::submit
774    should_wait_for_acquire: bool,
775
776    /// The fence value of the last command submission that wrote to this image.
777    ///
778    /// The next time we try to acquire this image, we'll block until
779    /// this submission finishes, proving that [`acquire`] is ready to
780    /// pass to `vkAcquireNextImageKHR` again.
781    ///
782    /// [`acquire`]: SwapchainAcquireSemaphore::acquire
783    previously_used_submission_index: crate::FenceValue,
784}
785
786impl SwapchainAcquireSemaphore {
787    fn new(device: &DeviceShared, index: usize) -> Result<Self, crate::DeviceError> {
788        Ok(Self {
789            acquire: device
790                .new_binary_semaphore(&format!("SwapchainImageSemaphore: Index {index} acquire"))?,
791            should_wait_for_acquire: true,
792            previously_used_submission_index: 0,
793        })
794    }
795
796    /// Sets the fence value which the next acquire will wait for. This prevents
797    /// the semaphore from being used while the previous submission is still in flight.
798    fn set_used_fence_value(&mut self, value: crate::FenceValue) {
799        self.previously_used_submission_index = value;
800    }
801
802    /// Return the semaphore that commands drawing to this image should wait for, if any.
803    ///
804    /// This only returns `Some` once per acquisition; see
805    /// [`SwapchainAcquireSemaphore::should_wait_for_acquire`] for details.
806    fn get_acquire_wait_semaphore(&mut self) -> Option<vk::Semaphore> {
807        if self.should_wait_for_acquire {
808            self.should_wait_for_acquire = false;
809            Some(self.acquire)
810        } else {
811            None
812        }
813    }
814
815    /// Indicates the cpu-side usage of this semaphore has finished for the frame,
816    /// so reset internal state to be ready for the next frame.
817    fn end_semaphore_usage(&mut self) {
818        // Reset the acquire semaphore, so that the next time we acquire this
819        // image, we can wait for it again.
820        self.should_wait_for_acquire = true;
821    }
822
823    unsafe fn destroy(&self, device: &ash::Device) {
824        unsafe {
825            device.destroy_semaphore(self.acquire, None);
826        }
827    }
828}
829
830#[derive(Debug)]
831struct SwapchainPresentSemaphores {
832    /// A pool of semaphores for ordering presentation after drawing.
833    ///
834    /// The first [`present_index`] semaphores in this vector are:
835    ///
836    /// - all waited on by the call to [`vkQueuePresentKHR`] that presents this
837    ///   image, and
838    ///
839    /// - each signaled by some [`vkQueueSubmit`] queue submission that draws to
840    ///   this image, when the submission finishes execution.
841    ///
842    /// This vector accumulates one semaphore per submission that writes to this
843    /// image. This is awkward, but hard to avoid: [`vkQueuePresentKHR`]
844    /// requires a semaphore to order it with respect to drawing commands, and
845    /// we can't attach new completion semaphores to a command submission after
846    /// it's been submitted. This means that, at submission time, we must create
847    /// the semaphore we might need if the caller's next action is to enqueue a
848    /// presentation of this image.
849    ///
850    /// An alternative strategy would be for presentation to enqueue an empty
851    /// submit, ordered relative to other submits in the usual way, and
852    /// signaling a single presentation semaphore. But we suspect that submits
853    /// are usually expensive enough, and semaphores usually cheap enough, that
854    /// performance-sensitive users will avoid making many submits, so that the
855    /// cost of accumulated semaphores will usually be less than the cost of an
856    /// additional submit.
857    ///
858    /// Only the first [`present_index`] semaphores in the vector are actually
859    /// going to be signalled by submitted commands, and need to be waited for
860    /// by the next present call. Any semaphores beyond that index were created
861    /// for prior presents and are simply being retained for recycling.
862    ///
863    /// [`present_index`]: SwapchainPresentSemaphores::present_index
864    /// [`vkQueuePresentKHR`]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#vkQueuePresentKHR
865    /// [`vkQueueSubmit`]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#vkQueueSubmit
866    present: Vec<vk::Semaphore>,
867
868    /// The number of semaphores in [`present`] to be signalled for this submission.
869    ///
870    /// [`present`]: SwapchainPresentSemaphores::present
871    present_index: usize,
872
873    /// Which image this semaphore set is used for.
874    frame_index: usize,
875}
876
877impl SwapchainPresentSemaphores {
878    pub fn new(frame_index: usize) -> Self {
879        Self {
880            present: Vec::new(),
881            present_index: 0,
882            frame_index,
883        }
884    }
885
886    /// Return the semaphore that the next submission that writes to this image should
887    /// signal when it's done.
888    ///
889    /// See [`SwapchainPresentSemaphores::present`] for details.
890    fn get_submit_signal_semaphore(
891        &mut self,
892        device: &DeviceShared,
893    ) -> Result<vk::Semaphore, crate::DeviceError> {
894        // Try to recycle a semaphore we created for a previous presentation.
895        let sem = match self.present.get(self.present_index) {
896            Some(sem) => *sem,
897            None => {
898                let sem = device.new_binary_semaphore(&format!(
899                    "SwapchainImageSemaphore: Image {} present semaphore {}",
900                    self.frame_index, self.present_index
901                ))?;
902                self.present.push(sem);
903                sem
904            }
905        };
906
907        self.present_index += 1;
908
909        Ok(sem)
910    }
911
912    /// Indicates the cpu-side usage of this semaphore has finished for the frame,
913    /// so reset internal state to be ready for the next frame.
914    fn end_semaphore_usage(&mut self) {
915        // Reset the index to 0, so that the next time we get a semaphore, we
916        // start from the beginning of the list.
917        self.present_index = 0;
918    }
919
920    /// Return the semaphores that a presentation of this image should wait on.
921    ///
922    /// Return a slice of semaphores that the call to [`vkQueueSubmit`] that
923    /// ends this image's acquisition should wait for. See
924    /// [`SwapchainPresentSemaphores::present`] for details.
925    ///
926    /// Reset `self` to be ready for the next acquisition cycle.
927    ///
928    /// [`vkQueueSubmit`]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#vkQueueSubmit
929    fn get_present_wait_semaphores(&mut self) -> Vec<vk::Semaphore> {
930        self.present[0..self.present_index].to_vec()
931    }
932
933    unsafe fn destroy(&self, device: &ash::Device) {
934        unsafe {
935            for sem in &self.present {
936                device.destroy_semaphore(*sem, None);
937            }
938        }
939    }
940}
941
942#[derive(Debug)]
943struct NativeSurfaceTextureMetadata {
944    acquire_semaphores: Arc<Mutex<SwapchainAcquireSemaphore>>,
945    present_semaphores: Arc<Mutex<SwapchainPresentSemaphores>>,
946}
947
948impl SurfaceTextureMetadata for NativeSurfaceTextureMetadata {
949    fn get_semaphore_guard(&self) -> Box<dyn SwapchainSubmissionSemaphoreGuard + '_> {
950        Box::new(NativeSwapchainSubmissionSemaphoreGuard {
951            acquire_semaphore_guard: self
952                .acquire_semaphores
953                .try_lock()
954                .expect("Failed to lock surface acquire semaphore"),
955            present_semaphores_guard: self
956                .present_semaphores
957                .try_lock()
958                .expect("Failed to lock surface present semaphores"),
959        })
960    }
961
962    fn as_any(&self) -> &dyn Any {
963        self
964    }
965}
966
967struct NativeSwapchainSubmissionSemaphoreGuard<'a> {
968    acquire_semaphore_guard: MutexGuard<'a, SwapchainAcquireSemaphore>,
969    present_semaphores_guard: MutexGuard<'a, SwapchainPresentSemaphores>,
970}
971
972impl<'a> SwapchainSubmissionSemaphoreGuard for NativeSwapchainSubmissionSemaphoreGuard<'a> {
973    fn set_used_fence_value(&mut self, value: u64) {
974        self.acquire_semaphore_guard.set_used_fence_value(value);
975    }
976
977    fn get_acquire_wait_semaphore(&mut self) -> Option<SemaphoreType> {
978        self.acquire_semaphore_guard
979            .get_acquire_wait_semaphore()
980            .map(SemaphoreType::Binary)
981    }
982
983    fn get_submit_signal_semaphore(
984        &mut self,
985        device: &DeviceShared,
986    ) -> Result<SemaphoreType, crate::DeviceError> {
987        self.present_semaphores_guard
988            .get_submit_signal_semaphore(device)
989            .map(SemaphoreType::Binary)
990    }
991}