Read/write the cross-process diagnostic counters atomically

present_calls, frames_dropped (VideoShare) and frames_rendered (AudioStreamInfo)
were plain `+= 1` / stores in the DLL, read by the host cross-process. On an x86
DLL a 64-bit store is two halves, so the x64 host could read a torn value during
a carry. Benign (display-only), but a real data race.

Use std::atomic_ref at the access sites rather than changing the field types:
the structs stay plain POD so the layout/offset asserts are unchanged and
AudioStreamInfo stays trivially copyable (it's published/read as a whole struct).
The DLL writers (note_present / note_video_dropped / note_audio_frames) and the
host readers (IpcServer::video_share / hook_status) now use relaxed atomic_ref;
hook_status reloads frames_rendered atomically after the wholesale struct copy.
The dev-tool readers (vk_validate, audio_probe) keep plain reads -- diagnostics of
diagnostics, and same-bitness in practice.

Validated by present_hook_test (present_calls via atomic_ref) and audio_hook_test
(frames_rendered) -- also confirms no atomic_ref alignment fault.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-24 01:27:18 +02:00
parent af129f8cfa
commit 66dd003c4c
4 changed files with 31 additions and 13 deletions

View File

@@ -113,10 +113,6 @@ default** and covers anything the hooked path doesn't.
From an in-depth review pass. Each item is fixed test-first (a failing test, then the fix) and lands
as its own commit; "verify" items are confirmed real before any change, and dropped if not.
Correctness (verify, then fix if real):
- **Non-atomic cross-process diagnostic counters** — `present_calls`, `frames_dropped`,
`frames_rendered` are plain `+=`/stores read by the host. Make them atomic.
Performance (failing perf-regression test first):
- **D3D9 capture readback off the present thread** — move the swizzle/flip to an off-thread reaper
(the inline-readback pattern that caused the Vulkan 144→3 FPS stall), add a present-thread overhead