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