From the 2026-07-03 comprehensive review.
The show thread grows/mutates m_composeTargets at runtime while the editor thread indexes it — no lock, no reserve. Show-thread writes: createComposeTarget does emplace_back (D3D12Renderer.cpp:3525 — realloc moves every element); resizeComposeTarget recreates sub-resources + rewrites srvHandles (:3646+). Reached from CompositorSystem::update (show thread) whenever a screen is added or a layer gains its first effect mid-show. Editor-thread reads: getComposeTargetTextureID (:3790), isComposeTargetReady (:3836), getComposeTargetStableSrvSlot (:3807) from StageWindow.cpp:149-155 and ProjectorCalibrationWindow.cpp:440,680.
Triple-buffering (ADR-0014 §2) guards RT contents, not vector structure. The comment at D3D12Renderer.hpp:718-719 ("targets only added at startup before the show thread runs") is false.
Failure: add a screen / toggle a first effect during a live show with Stage or Calibration window open → torn D3D12_GPU_DESCRIPTOR_HANDLE → ImGui binds a garbage descriptor → GPU page fault / DXGI_ERROR_DEVICE_REMOVED.
Fix: reserve(MAX_COMPOSE_TARGETS) at init + atomic ready gate; the resize resource-swap path additionally needs a mutex or generation gate (reserve alone kills the realloc UAF but not the resize race).
From the 2026-07-03 comprehensive review.
The show thread grows/mutates
m_composeTargetsat runtime while the editor thread indexes it — no lock, no reserve. Show-thread writes:createComposeTargetdoesemplace_back(D3D12Renderer.cpp:3525 — realloc moves every element);resizeComposeTargetrecreates sub-resources + rewrites srvHandles (:3646+). Reached from CompositorSystem::update (show thread) whenever a screen is added or a layer gains its first effect mid-show. Editor-thread reads: getComposeTargetTextureID (:3790), isComposeTargetReady (:3836), getComposeTargetStableSrvSlot (:3807) from StageWindow.cpp:149-155 and ProjectorCalibrationWindow.cpp:440,680.Triple-buffering (ADR-0014 §2) guards RT contents, not vector structure. The comment at D3D12Renderer.hpp:718-719 ("targets only added at startup before the show thread runs") is false.
Failure: add a screen / toggle a first effect during a live show with Stage or Calibration window open → torn
D3D12_GPU_DESCRIPTOR_HANDLE→ ImGui binds a garbage descriptor → GPU page fault / DXGI_ERROR_DEVICE_REMOVED.Fix:
reserve(MAX_COMPOSE_TARGETS)at init + atomic ready gate; the resize resource-swap path additionally needs a mutex or generation gate (reserve alone kills the realloc UAF but not the resize race).