Skip to content

Commit 5818ce4

Browse files
fix: use vk::Fence::null on non-Windows swapchain (#9878)
1 parent 3965a26 commit 5818ce4

2 files changed

Lines changed: 41 additions & 27 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ Bottom level categories:
4242

4343
## Unreleased
4444

45+
### Bug Fixes
46+
47+
#### Vulkan
48+
49+
- Stop passing an un-waited fence to `vkAcquireNextImageKHR` on non-Windows platforms, which triggered `VUID-vkAcquireNextImageKHR-fence-10066` validation errors every frame since v30.0.0. By @ErichDonGubler in [#9855](https://github.com/gfx-rs/wgpu/issues/9855).
50+
4551
## v30.0.0 (2026-07-01)
4652

4753
### Major changes

wgpu-hal/src/vulkan/swapchain/native.rs

Lines changed: 35 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -257,12 +257,21 @@ impl Surface for NativeSurface {
257257
let images = unsafe { functor.get_swapchain_images(raw) }
258258
.map_err(crate::vulkan::map_host_device_oom_err)?;
259259

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)?
260+
// This fence is only used to throttle acquisition on Windows. It is very important to
261+
// avoid bad frame pacing when the Vulkan driver is using a DXGI swapchain. See
262+
// https://github.com/gfx-rs/wgpu/issues/8310 and
263+
// https://github.com/gfx-rs/wgpu/issues/8354 for more details.
264+
let fence = if cfg!(target_os = "windows") {
265+
let raw = unsafe {
266+
device
267+
.shared
268+
.raw
269+
.create_fence(&vk::FenceCreateInfo::default(), None)
270+
.map_err(crate::vulkan::map_host_device_oom_err)?
271+
};
272+
Some(raw)
273+
} else {
274+
None
266275
};
267276

268277
// NOTE: It's important that we define the same number of acquire/present semaphores
@@ -309,7 +318,7 @@ pub(crate) struct NativeSwapchain {
309318
device: Arc<DeviceShared>,
310319
images: Vec<vk::Image>,
311320
/// Fence used to wait on the acquired image.
312-
fence: vk::Fence,
321+
fence: Option<vk::Fence>,
313322
config: crate::SurfaceConfiguration,
314323

315324
/// Semaphores used between image acquisition and the first submission
@@ -382,7 +391,9 @@ impl Swapchain for NativeSwapchain {
382391
};
383392
};
384393

385-
unsafe { device.shared.raw.destroy_fence(self.fence, None) }
394+
if let Some(fence) = self.fence {
395+
unsafe { device.shared.raw.destroy_fence(fence, None) }
396+
}
386397

387398
// We cannot take this by value, as the function returns `self`.
388399
for semaphore in self.acquire_semaphores.drain(..) {
@@ -455,14 +466,16 @@ impl Swapchain for NativeSwapchain {
455466
return Err(crate::SurfaceError::Timeout);
456467
}
457468

469+
let acquire_fence = self.fence.unwrap_or_else(vk::Fence::null);
470+
458471
// will block if no image is available
459472
let (index, suboptimal) = match unsafe {
460473
profiling::scope!("vkAcquireNextImageKHR");
461474
self.functor.acquire_next_image(
462475
self.raw,
463476
timeout_ns,
464477
acquire_semaphore_guard.acquire,
465-
self.fence,
478+
acquire_fence,
466479
)
467480
} {
468481
// We treat `VK_SUBOPTIMAL_KHR` as `VK_SUCCESS` on Android.
@@ -485,24 +498,19 @@ impl Swapchain for NativeSwapchain {
485498
}
486499
};
487500

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)?;
501+
if let Some(fence) = self.fence {
502+
unsafe {
503+
// The `wait_all` argument must be `true` to avoid crash on some Android devices. See https://github.com/gfx-rs/wgpu/pull/8769
504+
self.device
505+
.raw
506+
.wait_for_fences(&[fence], true, timeout_ns)
507+
.map_err(map_host_device_oom_and_lost_err)?;
508+
509+
self.device
510+
.raw
511+
.reset_fences(&[fence])
512+
.map_err(map_host_device_oom_and_lost_err)?;
513+
}
506514
}
507515

508516
drop(acquire_semaphore_guard);

0 commit comments

Comments
 (0)