Skip to content

Submit an open Vulkan upload batch before releasing deferred destroys - #177

Merged
themuffinator merged 1 commit into
themuffinator:mainfrom
jm2:fix/vk-open-upload-batch-destroy
Sep 22, 2026
Merged

themuffinator merged 1 commit into
themuffinator:mainfrom
jm2:fix/vk-open-upload-batch-destroy

Conversation

@jm2

@jm2 jm2 commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

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 that VK_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_FlushDeferredDestroys whenever the slot has anything to release, so the rule holds for all three callers:

  • the two frame-begin sites
  • shutdown, where the batch has already been abandoned, so the new check does nothing

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:

  • Before: SIGSEGV on the first frame of every Vulkan boot, with both MoltenVK 1.4.1 (pinned) and 1.4.2. The trace is in the issue.
  • After: boots to the menu with both MoltenVK versions.
  • After: four applyPerformancePreset ultra/minimum switches at the menu. The menu text is intact after each switch, and there are no Vulkan errors in the log.
  • After, SP: +set si_gameType singleplayer +set com_skipLoadingContinue 1 +map game/airdefense1 loads (1,337 images, about 20 s) and renders the opening cinematic. No Vulkan errors in the log.
  • After, MP: +set si_gameType DM +set ui_autoJoin 1 +spawnServer mp/q4dm1 joins 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.py and renderer_vulkan_gui_residency.py pass.
  • Not tested on Windows or Linux. The change only adds work when a batch is open while a slot has deferred destroys.

No release-completion.md entry 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

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
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@jm2

jm2 commented Sep 22, 2026

Copy link
Copy Markdown
Contributor Author

Only macOS (M1 Max) has been tested so far. Windows and Linux runs are in progress, and results will be added here shortly.

@jm2

jm2 commented Sep 22, 2026

Copy link
Copy Markdown
Contributor Author

Windows and Linux results, with r_vkValidation 1. Commits: base 71c3230c, this PR b935746f.

Windows 11, RX 6400, Adrenalin 25.10.30.02, layer 1.4.357 Fedora 44 (Wayland), RX 6400, RADV / Mesa 26.2.2, layer 1.4.341
boot, preset cycle ×4, SP airdefense1, MP q4dm1 clean exit and correct rendering on both builds clean exit and correct rendering on both builds
Vulkan validation: lines 0 on base, 0 with this PR 0 on base, 0 with this PR
new warnings/errors vs base none none
Vulkan contract tests pass pass

What this shows:

  • No regressions on desktop drivers. Output, warnings and rendering match base in every run.

  • The old code does not crash or trip validation on desktop drivers. On Windows, a control run with the layer writing its own log (Core Checks and Object Lifetime enabled) reported nothing either. The macOS crash in [macOS Vulkan]: crash on boot, MoltenVK copy into an image released while its upload batch was still open #175 has not been reproduced off MoltenVK.

  • The guarded state does occur on Linux. A gdb breakpoint inside the new branch hit once each in boot, preset cycle and SP (not in MP). Each hit was at the first GUI frame from VK_GuiExecutor_BeginFrame, with slot 0, 5 pending image destroys and 163 recorded regions in the open batch. Base runs the same path without the check.

  • On macOS, the destroyed images are confirmed to be copy targets in the open batch. Every recorded upload's target image was logged, along with the deferred-destroy list, at the first-frame hit:

    • All 253 upload records made before the hit are exactly the 253 still pending in the open batch, so no earlier batch had been submitted.
    • All 24 images queued for destruction are targets of copies in that batch.

    The release therefore frees images that the batch copies into when it is submitted. The Linux hits were not checked the same way. The validation layer's silence on desktop drivers is still unexplained.

@themuffinator themuffinator left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_FlushUploadBatch nor VK_Device_WaitUploadBatch reaches back into VK_Device_FlushDeferredDestroys.
  • Shutdown is a genuine no-op, as you said: uploadBatchOpen is cleared before the VK_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.

@themuffinator
themuffinator merged commit e5eefda into themuffinator:main Sep 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[macOS Vulkan]: crash on boot, MoltenVK copy into an image released while its upload batch was still open

2 participants