Repository navigation
Submit an open Vulkan upload batch before releasing deferred destroys - #177
Conversation
VK_Device_WaitUploadBatch only retires a batch that has already been submitted. A batch that is still open has recorded buffer-to-image copies into images the frame slot may be about to release, and the slot fence does not cover it because it is only submitted with the next frame. The texture-reduction reload during InitRenderSystem hits this on the first frame: the images it purges were uploaded into the open batch, the slot's fence starts signalled, and the deferred destroys run before the batch is submitted. MoltenVK encodes copies at submit time, so it dereferences the released image. On an M1 Max this crashed every Vulkan boot, with both the pinned MoltenVK 1.4.1 and 1.4.2. Submit and retire the open batch inside VK_Device_FlushDeferredDestroys when that slot has anything to release, so every caller keeps the rule. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017RU1i6R3gN4cdKRELGiCEc
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
Only macOS (M1 Max) has been tested so far. Windows and Linux runs are in progress, and results will be added here shortly. |
|
Windows and Linux results, with
What this shows:
|
themuffinator
left a comment
There was a problem hiding this comment.
Good find, and the analysis in #175 is unusually complete — the conditional breakpoint showing 24 deferred destroys against 253 recorded uploads in an open batch is what makes this unarguable.
The fix goes in the right place. VK_Device_WaitUploadBatch only retires a batch that reached uploadBatchInFlight, so an open batch was outside the invariant the comment at the frame-begin site already states ("deferred destroys must never run while a submitted upload batch could still reference their images"). Putting the extra submit inside VK_Device_FlushDeferredDestroys extends that to all three callers rather than patching each one.
I checked the things that could have made this worse:
- No recursion. Neither
VK_Device_FlushUploadBatchnorVK_Device_WaitUploadBatchreaches back intoVK_Device_FlushDeferredDestroys. - Shutdown is a genuine no-op, as you said:
uploadBatchOpenis cleared before theVK_Device_FlushDeferredDestroys( i )loop runs, so the new condition is false there. - Ordering still holds. Both frame-begin sites run before any command recording and before
vkAcquireNextImageKHR, so submitting the upload batch early cannot violate the "upload batch must precede any frame submission in queue order" rule at the submit site.
Windows coverage
The crash itself is MoltenVK-specific — desktop drivers resolve handles at record time, as you guessed — so what I could add is that nothing regresses here. Windows x64, Vulkan, RTX 4060 laptop, game/airdefense1:
main (71c3230c) |
with this PR | |
|---|---|---|
| boots to menu | yes | yes |
| map loads | yes | yes |
| Vulkan error/validation lines in the log | 0 | 0 |
| game populate phase | 4,229 ms | 3,976 ms |
The load figure was my main worry: the extra flush-and-wait lands on the path where a batch is open, which is exactly level loading, and it could have serialised the uploads. It did not — the difference is inside run-to-run noise and in the favourable direction.
macos_moltenvk_policy.py, renderer_vulkan_probe_safety.py, renderer_vulkan_gui_residency.py pass. Builds clean on MSVC.
Merging, and closing #175 with it. Do not worry about the missing release-completion.md entry — I will fold this into the next release notes.
Fixes #175
VK_Device_WaitUploadBatch()only retires an upload batch that has already been submitted. A batch that is still open can hold copies into images thatVK_Device_FlushDeferredDestroys( slot )is about to release, and the slot fence does not cover it.This change submits and retires the open batch inside
VK_Device_FlushDeferredDestroyswhenever the slot has anything to release, so the rule holds for all three callers:The extra wait only happens when a batch is open at frame start, which in practice means loading.
What was tested, and what wasn't
Apple M1 Max, macOS 27.0, windowed 2560x1440:
applyPerformancePreset ultra/minimumswitches at the menu. The menu text is intact after each switch, and there are no Vulkan errors in the log.+set si_gameType singleplayer +set com_skipLoadingContinue 1 +map game/airdefense1loads (1,337 images, about 20 s) and renders the opening cinematic. No Vulkan errors in the log.+set si_gameType DM +set ui_autoJoin 1 +spawnServer mp/q4dm1joins warmup in first person with the world, weapon, HUD and fonts all drawing. No Vulkan errors in the log.macos_moltenvk_policy.py,renderer_vulkan_probe_safety.pyandrenderer_vulkan_gui_residency.pypass.No
release-completion.mdentry is included, because 0.13.2 has shipped and the right section for this is unclear.🤖 Generated with Claude Code
https://claude.ai/code/session_017RU1i6R3gN4cdKRELGiCEc