diff --git a/host/src/capture_panel.cpp b/host/src/capture_panel.cpp index 2615ac0..62f73bb 100644 --- a/host/src/capture_panel.cpp +++ b/host/src/capture_panel.cpp @@ -57,6 +57,15 @@ void plot_multiseries(const char* id, const GraphSeries* series, int n_series, f } } // namespace +bool CapturePanel::frame_sync_active() const +{ + // Only sync to the hook's frames while the target is actually alive and producing + // them. A terminated or hung game stops bumping the generation, so syncing would make + // every iteration wait out the timeout in wait_for_hooked_frame -- fall back to vsync. + const bool hook_alive = injection_ != nullptr && injection_->target_state() == TargetState::Alive; + return frame_sync_ && enabled_ && source_ == Source_Hooked && hook_alive; +} + bool CapturePanel::init(ID3D11Device* device) { device_ = device; diff --git a/host/src/capture_panel.hpp b/host/src/capture_panel.hpp index d0f37f5..03c4591 100644 --- a/host/src/capture_panel.hpp +++ b/host/src/capture_panel.hpp @@ -58,13 +58,13 @@ public: return source_ == Source_Hooked; } - // True when the operator asked to pace the tool's flip to the game's published - // frames (only meaningful with the Hooked source while mirroring). The main loop - // reads this to wait for a new hook frame and present without vsync. - [[nodiscard]] bool frame_sync_active() const - { - return frame_sync_ && enabled_ && source_ == Source_Hooked; - } + // True when the operator asked to pace the tool's flip to the game's published frames + // (only meaningful with the Hooked source while mirroring) AND the target is alive and + // still producing them. The main loop reads this to wait for a new hook frame and + // present without vsync; gating on liveness stops a terminated/hung game from making + // every frame wait out the sync timeout (~5 FPS sluggish overlay). Defined in the .cpp + // since it needs the InjectionPanel definition for target liveness. + [[nodiscard]] bool frame_sync_active() const; private: enum Source : int