From 8e581ea6bbda961f8b85f6a7cc40b0d2d231e5f8 Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Sun, 23 Mar 2025 16:46:21 -0500 Subject: [PATCH] CoreTiming: Limit throttling after Immediate XFB for better input latency. --- Source/Core/Core/CoreTiming.cpp | 26 ++++++++++++++++++++++++-- Source/Core/Core/CoreTiming.h | 4 ++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/Source/Core/Core/CoreTiming.cpp b/Source/Core/Core/CoreTiming.cpp index 6d1ab99262..9c1954eeff 100644 --- a/Source/Core/Core/CoreTiming.cpp +++ b/Source/Core/Core/CoreTiming.cpp @@ -28,6 +28,7 @@ #include "VideoCommon/PerformanceMetrics.h" #include "VideoCommon/VideoBackendBase.h" #include "VideoCommon/VideoConfig.h" +#include "VideoCommon/VideoEvents.h" namespace CoreTiming { @@ -104,6 +105,13 @@ void CoreTimingManager::Init() m_last_oc_factor = m_config_oc_factor; m_globals.last_OC_factor_inverted = m_config_oc_inv_factor; + + m_throttled_since_presentation = false; + m_frame_hook = AfterPresentEvent::Register( + [this](const PresentInfo&) { + m_throttled_since_presentation.store(false, std::memory_order_relaxed); + }, + "CoreTiming AfterPresentEvent"); } void CoreTimingManager::Shutdown() @@ -113,6 +121,7 @@ void CoreTimingManager::Shutdown() ClearPendingEvents(); UnregisterAllEvents(); CPUThreadConfigCallback::RemoveConfigChangedCallback(m_registered_config_callback_id); + m_frame_hook.reset(); } void CoreTimingManager::RefreshConfig() @@ -403,6 +412,21 @@ void CoreTimingManager::SleepUntil(TimePoint time_point) void CoreTimingManager::Throttle(const s64 target_cycle) { + const TimePoint time = Clock::now(); + + const bool already_throttled = + m_throttled_since_presentation.exchange(true, std::memory_order_relaxed); + + // When Immediate XFB is enabled, try to Throttle just once since each presentation. + // This lowers latency by speeding through to the next presentation after grabbing input. + // Make sure we don't get too far ahead of proper timing though, + // otherwise the emulator unreasonably speeds through loading screens that don't have XFB copies. + const bool skip_throttle = already_throttled && g_ActiveConfig.bImmediateXFB && + ((GetTargetHostTime(target_cycle) - time) < (m_max_fallback / 2)); + + if (skip_throttle) + return; + if (IsSpeedUnlimited()) { ResetThrottle(target_cycle); @@ -422,8 +446,6 @@ void CoreTimingManager::Throttle(const s64 target_cycle) TimePoint target_time = CalculateTargetHostTimeInternal(target_cycle); - const TimePoint time = Clock::now(); - const TimePoint min_target = time - m_max_fallback; if (target_time < min_target) { diff --git a/Source/Core/Core/CoreTiming.h b/Source/Core/Core/CoreTiming.h index d6f652c35a..395eb71f3a 100644 --- a/Source/Core/Core/CoreTiming.h +++ b/Source/Core/Core/CoreTiming.h @@ -23,6 +23,7 @@ #include #include "Common/CommonTypes.h" +#include "Common/HookableEvent.h" #include "Common/SPSCQueue.h" #include "Common/Timer.h" #include "Core/CPUThreadConfigCallback.h" @@ -223,6 +224,9 @@ private: std::atomic_bool m_use_precision_timer = false; Common::PrecisionTimer m_precision_cpu_timer; Common::PrecisionTimer m_precision_gpu_timer; + + Common::EventHook m_frame_hook; + std::atomic m_throttled_since_presentation = false; }; } // namespace CoreTiming