Vulkan: editor freezes in vkWaitForFences on Windows + NVIDIA after a sub-window closes
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 35/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- cpp
Research direction
Start with the Vulkan swap chain teardown in drivers/vulkan/rendering_device_driver_vulkan.{h,cpp} and the linked workaround in dominicbytes/redot-engine#10, which changes only those files. Reproduce with the attached MRP on Windows with an NVIDIA GPU, then trace how screen_free() handles an in-flight present. Done means repeated window open and close cycles no longer stall in vkWaitForFences; the workaround reports zero stalls in the runs described.
Written by the indexing model from the issue text.
Description
Tested versions
- Reproducible in: v26.3.stable.official [85e520394] (the MRP below stalled on 1 of 3 runs).
- Reproducible in local builds of the same commit, used for the counts below (see Issue description).
- Not reproducible with D3D12, OpenGL (Compatibility), or Vulkan on an AMD GPU on the same machine.
- Upstream Godot
masterhas the same swap chain teardown code; not tested there.
System information
Windows 10 Pro 22H2 (build 19045), x86-64. Forward+ / Vulkan. NVIDIA driver 616.56; RTX 4080 SUPER (primary monitor) + RTX 5060 Ti (two side monitors), AMD integrated GPU (no monitor).
Issue description
The editor sometimes freezes for good right after a sub-window (popup, tooltip, dialog or plugin window) closes. It stops drawing and never recovers. The main thread is blocked forever in vkWaitForFences:
vkWaitForFences (NVIDIA driver)
RenderingDeviceDriverVulkan::fence_wait
RenderingDevice::_stall_for_frame
RenderingDevice::_begin_frame <- swap_buffers
or _flush_and_stall_for_all_frames <- screen_prepare_for_drawing (resize_required)
What happens. A local instrumented build logged every acquire, submit, present, fence wait and swap chain event. Every captured stall had the same shape:
- A sub-window and the main window are presented in the same frame.
- The sub-window closes.
RenderingDevice::screen_free()waits for the frame fences, then destroys the swap chain, followed by the surface and the window. This happens a few milliseconds after that swap chain's lastvkQueuePresentKHR. The fences only cover rendering, so that present is still in flight. - From then on the presentation engine never gives back the main window's images.
vkAcquireNextImageKHRreturnsVK_SUCCESS, but the image's semaphore (and a test acquire fence) is never signaled, still pending 35 s later. The next submission waits on that semaphore, so its fence never signals.
When it stalls, an empty fence-only submission on every other queue completes; only the main queue is blocked. The GPU is not hung.
Whose bug. Redot's call sequence is valid. The Khronos validation layer (1.4.363) reports nothing, in stalled runs too. A deliberate-error canary confirmed its messages reach the log. Only NVIDIA's Vulkan driver shows it, so this is a driver bug. It is much more likely when the window's monitor is driven by a different GPU than the one rendering, but it also happens on a single GPU's own monitor.
Workaround available. dominicbytes/redot-engine#10 (single commit, against 26.3-stable) changes only drivers/vulkan/rendering_device_driver_vulkan.{h,cpp}:
- On NVIDIA on Windows, it enables
VK_KHR_present_idandVK_KHR_present_waitwhen supported. - It numbers every present.
swap_chain_free()waits for the swap chain's last present withvkWaitForPresentKHRbefore freeing it, for at most 200 ms.
Only window close changes; resizing, other vendors and other platforms keep the current behavior. Each close waited a median of 10 ms and at most 67 ms, measured over 179 closes.
Stalled runs, each opening and closing a window 40 times. A run counts as stalled when output stops for 45 s. These used local builds of 26.3-stable plus the WorldScape shutdown fix and test-only off-desktop window placement, with and without the workaround:
| Setup | Without the workaround | With it |
|---|---|---|
| 5060 Ti renders, windows on the 4080's monitor (worst case) | 14/30 | 0/30 |
| 4080 renders, windows on a 5060 Ti monitor (default GPU) | 11/50 | 0/30 |
| 4080 renders, windows on its own monitor | 1/20 | 0/10 |
| 5060 Ti renders, windows on its own monitor | 3/35 | 0/10 |
| Validation layer on | 2/15, 0 messages | 0/6, 0 messages |
| D3D12 / OpenGL / Vulkan on the AMD GPU | 0/15 each | 0/10 each |
Tried before the swap chain teardown, and still stalled: vkQueueWaitIdle on the present queue, presenting each swap chain separately, and keeping the present semaphores alive. A fixed 100 ms delay stopped the stalls, which confirms the cause is the present still being in flight.
Steps to reproduce
- Download and extract the attached MRP. Its editor plugin waits until the editor has loaded, then creates, shows, hides and frees a plain 512x512
Window40 times, and quits. - Run the editor with the console executable:
redot --editor --audio-driver Dummy --path <mrp>. Add--gpu-index <n>to pick the rendering GPU. - A normal run prints
WINCYCLE_COUNT 0to39, thenWINCYCLE_DONE, and exits. In a stalled run the counts stop (in our runs always within the first 5 cycles), the editor stops redrawing, and the process never exits. - It is intermittent, so repeat the run a few times. It is most likely when the editor and the cycle window are on a monitor driven by a different NVIDIA GPU than the rendering one.
On the official build, the editor may crash at exit after WINCYCLE_DONE. That is the separate WorldScape shutdown crash (#1402), not this issue.
Minimal reproduction project (MRP)
- Dominant language
- C++
- Stars
- 6.1k
- Forks
- 337
- Avg merge
- 1d 51m
- Merged PRs (30d)
- 41
Getting set up
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from Redot-Engine/redot-engine
-
bug topic:worldscape usability
Difficulty 4/5 3-5 days Newbie friendliness 35/100
Redot-Engine/redot-engine#1556 · 3 comments ·
Maintainers usually reply within 1 day
-
bug topic:3d topic:editor usability
Difficulty 3/5 1-2 days Newbie friendliness 48/100
Redot-Engine/redot-engine#1554 ·
Maintainers usually reply within 1 day
-
bug topic:animation
Difficulty 3/5 1-2 days Newbie friendliness 54/100
Redot-Engine/redot-engine#1552 · 3 comments ·
Maintainers usually reply within 1 day
-
AnimationTree does not respect AnimationPlayer's speed_scale or the AnimationClip's update modeOpenbug topic:animation
Difficulty 4/5 3-5 days Newbie friendliness 45/100
Redot-Engine/redot-engine#1551 ·
Maintainers usually reply within 1 day
-
bug needs testing topic:core
Difficulty 4/5 3-5 days Newbie friendliness 50/100
Redot-Engine/redot-engine#1549 · 1 reaction ·
Maintainers usually reply within 1 day
All issues in Redot-Engine/redot-engine
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
foxglove/mcap#1907 · 1 comment ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 66/100
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
telegramdesktop/tdesktop#31505 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Maintainers usually reply within 1 day
-
ll-builder shell completions omit existing lockfile commands and --lockedPossibly taken @project-nju-learner claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 90/100
OpenAtom-Linyaps/linyaps#2291 ·
Maintainers usually reply within 1 day