Add game-frame-synced flip toggle + multi-series colored perf graphs

Frame sync: new "Sync flip to game frames" toggle in the Video mirror panel
(Hooked source only -- WGC frames are delivered by the compositor at monitor
refresh and don't carry the game's true present cadence, so it's disabled there).
When on, the main loop waits for the hook's next published frame (its generation
bump) before rendering and presents with sync interval 0, so the tool flips in
lockstep with the game instead of vsync. The wait pumps messages to stay
responsive and times out after 200 ms so a paused/stalled game can't hang the
overlay. timeBeginPeriod(1) keeps the wait's Sleep(1) granular; links winmm.
render_frame() gained a sync_interval parameter (default 1 = vsync).

Perf graphs: the old graphs drew the tool's frametime and FPS as single same-color
lines. Replaced with a custom multi-series plotter (ImDrawList polylines) that
overlays Tool (blue), Game present (green), and Hook publish (orange) -- or Tool +
WGC capture in WGC mode -- in distinct colors with a colored legend, for both an
FPS (0-144) and a frametime (0-33 ms) view. Game/hook rates come from an EdgeRate
tracker that measures the instantaneous rate the moment each counter advances, so
the lines have real per-frame resolution rather than 0.5 s stair-steps.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-21 11:55:10 +02:00
parent 22b0dff918
commit feb8fc5dae
6 changed files with 299 additions and 35 deletions

View File

@@ -163,7 +163,7 @@ bool D3D11Window::pump_messages()
return true;
}
void D3D11Window::render_frame(const RenderCallback& render)
void D3D11Window::render_frame(const RenderCallback& render, UINT sync_interval)
{
const float clear[4] = {0.06f, 0.06f, 0.08f, 1.0f};
context_->OMSetRenderTargets(1, rtv_.GetAddressOf(), nullptr);
@@ -174,8 +174,9 @@ void D3D11Window::render_frame(const RenderCallback& render)
render();
}
// vsync on: matches the captured stream cadence and avoids a busy spin.
swap_chain_->Present(1, 0);
// sync_interval 1 (default) vsyncs to the monitor; 0 presents immediately so the
// caller can pace the flip itself (frame-sync to the game's published frames).
swap_chain_->Present(sync_interval, 0);
}
LRESULT CALLBACK D3D11Window::wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)