wgpu-hal v26.0.6 PR #8420 fence wait blows up acquire latency on AMD + Immediate present (bisect + reproducer)
Summary
PR #8420 (`[hal/vulkan] Wait for fence signalled in vkAcquireNextImage`, commit gfx-rs/wgpu@1258bc7a0, first shipped in wgpu-hal v26.0.6) was intended to fix #8310 (NVIDIA Fifo stuttering). On AMD + Vulkan + Windows + `PresentMode::Immediate` it causes a ~60× regression in `Surface::get_current_texture` mean latency (74 µs → 4833 µs), bursts to 20+ ms at p99, and drops mean frame rate from 78 → 56 fps on a workload that previously hit the 60 fps target.
This regression rides forward through every wgpu-hal release since 26.0.6 (27.0.4, 28.0.x, 29.0.x — the same PR was re-applied / cherry-picked forward via gfx-rs/wgpu@386d44376 and gfx-rs/wgpu@b3d94317d).
Reproducer
- GPU: AMD Radeon RX 6800 XT (RDNA 2)
- Driver: 32.0.21043.5001
- OS: Windows 11 Pro (build 26200)
- Backend: Vulkan
- Workload: voxel renderer, ~3,600 mesher compute dispatches + 1 render submit per frame, 4K@vd=640
- PresentMode: `Immediate` (resolved from `AutoNoVsync`; `Mailbox` not in caps on this hardware)
- SurfaceConfiguration: `desired_maximum_frame_latency` = 1, 2, or 3 — all equivalent
Bisect
`git bisect` v25.0.2 → v26.0.0 ruled out every source-line commit (all GOOD). Pivoted to bisect wgpu-hal patch tags:
| Test | `r_acquire` mean | Disposition |
|---|---|---|
| v25.0.2 baseline | 74 µs | GOOD |
| ece29b6e6 (OOM check on submit/poll, 2025-04-02) | 73 µs | GOOD |
| 6058dd429 (4/16) | 78 µs | GOOD |
| 15477b84a (4/22, pre-framebuffer cluster) | 73 µs | GOOD |
| cc406f919 (4/25, post-framebuffer cluster) | 85 µs | GOOD |
| 41616d9dd (SemaphoreList helper, 6/11) | 74 µs | GOOD |
| v26.0.0 tag (source-line) | 76 µs | GOOD — entire v25→v26 source line is clean |
| wgpu-hal-v26.0.5 (just before PR #8420) | 82 µs | GOOD |
| wgpu-hal-v26.0.6 (PR #8420 fence wait) | 4833 µs | BAD |
| wgpu-hal-v26.0.6 with PR #8420 reverted | 80 µs | GOOD (proves cause) |
`p50` r_acquire goes from 60 µs (v26.0.5) → 225 µs (v26.0.6); `p99` goes from 130 µs → 20,254 µs. The mean is dragged almost entirely by the `p99` spike pattern (every ~1 in 100 frames hits ~20 ms — suggestive of compositor / DWM interaction in the fence-wait code path on AMD Vulkan).
Test methodology: `[patch.crates-io]` in our app's `Cargo.toml` pointing wgpu-* / naga at a local clone of gfx-rs/wgpu; `git checkout` each candidate; rebuild + run a 30-second stationary bench in our renderer; collect frame-by-frame `r_acquire` timing (already instrumented for an unrelated investigation).
Existing mitigations don't help
- `desired_maximum_frame_latency` = 1, 2, 3 — all benched at 4.8 ms acquire mean
- `PresentMode::Immediate` vs `PresentMode::Fifo` vs `PresentMode::FifoRelaxed` — Immediate is slightly less bad (4.6 ms) but Fifo is even worse (22.5 ms); none are acceptable
- Per-frame `device.poll(PollType::Poll)` after `frame.present()` — no effect
- Splitting the per-frame submit so heavy compute submits before `Surface::get_current_texture()` and only render submits after — makes p50 acquire worse (60 µs → 5625 µs)
Suggested fix
Make the fence wait platform- or vendor-conditional. Two options:
- Vendor-gated: skip the wait on AMD adapters (check `adapter.info().vendor == 0x1002`). The NVIDIA Fifo stuttering #8310 reproducer is NVIDIA-specific by report; the AMD Immediate-mode regression doesn't.
- Present-mode-gated: only apply the fence wait when `present_mode` is one of `Fifo` / `FifoRelaxed`. The #8310 reporter only saw stuttering in Fifo modes; Immediate-mode applications don't benefit from the fence wait but pay the cost.
Either option restores baseline behaviour on this hardware while preserving the #8310 fix on the affected NVIDIA configurations.
Our workaround (until upstream lands a fix)
`[patch.crates-io]` in our `Cargo.toml` pointing wgpu-* / naga at a local fork that reverts PR #8420 on top of v27.0.4 (Alfao/wgpu#gaym-revert-pr-8420-v27). `git revert 386d44376` applies cleanly on v27.0.4 (3 files, ~40 lines).
For v29: the revert doesn't cherry-pick cleanly because the swap-chain code was refactored into `wgpu-hal/src/vulkan/swapchain/native.rs`; a 7-line surgical removal of the same fence-wait code does work and is at Alfao/wgpu#gaym-revert-pr-8420-v29 for reference.
Source: gfx-rs/wgpu