Synchronize and bound the Vulkan swapchain tracking (g_swaps)

g_swaps (vk_hook.cpp and the Vulkan layer) is pushed from the create-swapchain
detour and iterated by the present detour, which can run on different game
threads (Vulkan external sync is per-object, not global), and cleared on removal
from another thread -- all with no mutex. A push_back realloc could dangle the
SwapInfo* a concurrent find_swap/present is using. It was also never pruned, so a
game that recreates its swapchain each resize grew it without bound and could
match a recycled handle's stale images.

Add g_swaps_mutex around every access; the present detour now copies the matched
swapchain's fields out under the lock and captures without holding it (no GPU
submit under the lock, no dangling pointer). Create de-dups by handle and an LRU
cap (8) bounds growth -- the active swapchain is the newest, so it's never
evicted. Deliberately NOT hooking vkDestroySwapchainKHR: forwarding a destroy
incorrectly could break the game, and the de-dup + cap already bound growth and
defeat handle recycling.

Verified real by inspection (a concurrent-create+present Vulkan race isn't
deterministically reproducible in a test); validated by the full mock_game_test
Vulkan paths (capture + layer + too-late) staying green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-24 01:22:09 +02:00
parent bdb700ec56
commit 47462287fc
3 changed files with 98 additions and 15 deletions

View File

@@ -114,9 +114,6 @@ From an in-depth review pass. Each item is fixed test-first (a failing test, the
as its own commit; "verify" items are confirmed real before any change, and dropped if not.
Correctness (verify, then fix if real):
- **`vk_hook` / `vk_layer` `g_swaps`** — no synchronization on push/iterate and never pruned on
swapchain destroy (unbounded growth + stale-handle match). Add a guard + a `vkDestroySwapchainKHR`
prune.
- **`mkb_hook` raw-input slot reuse** — a 64-slot ring can overwrite an event before the game reads
the `WM_INPUT`. Verify; fix if real.
- **Non-atomic cross-process diagnostic counters** — `present_calls`, `frames_dropped`,