diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp index ddbc490e9a..93576e554f 100644 --- a/Source/Core/Core/Core.cpp +++ b/Source/Core/Core/Core.cpp @@ -613,30 +613,6 @@ bool PauseAndLock(bool doLock, bool unpauseOnUnlock) // This should only be called from VI void VideoThrottle() { - u32 TargetVPS = (SConfig::GetInstance().m_Framelimit > 2) ? - (SConfig::GetInstance().m_Framelimit - 1) * 5 : VideoInterface::TargetRefreshRate; - - if (Host_GetKeyState('\t')) - isTabPressed = true; - else - isTabPressed = false; - - // Disable the frame-limiter when the throttle (Tab) key is held down. Audio throttle: m_Framelimit = 2 - if (SConfig::GetInstance().m_Framelimit && SConfig::GetInstance().m_Framelimit != 2 && !Host_GetKeyState('\t')) - { - u32 frametime = ((SConfig::GetInstance().b_UseFPS)? Common::AtomicLoad(DrawnFrame) : DrawnVideo) * 1000 / TargetVPS; - - u32 timeDifference = (u32)Timer.GetTimeDifference(); - if (timeDifference < frametime) - { - Common::SleepCurrentThread(frametime - timeDifference - 1); - } - - while ((u32)Timer.GetTimeDifference() < frametime) - Common::YieldCPU(); - //Common::SleepCurrentThread(1); - } - // Update info per second u32 ElapseTime = (u32)Timer.GetTimeDifference(); if ((ElapseTime >= 1000 && DrawnVideo > 0) || g_requestRefreshInfo) diff --git a/Source/Core/Core/HW/SystemTimers.cpp b/Source/Core/Core/HW/SystemTimers.cpp index 976e2397cf..12c2897936 100644 --- a/Source/Core/Core/HW/SystemTimers.cpp +++ b/Source/Core/Core/HW/SystemTimers.cpp @@ -75,6 +75,7 @@ IPC_HLE_PERIOD: For the Wiimote this is the call schedule: #include "Timer.h" #include "VideoBackendBase.h" #include "CommandProcessor.h" +#include "Host.h" namespace SystemTimers @@ -115,6 +116,7 @@ int et_AudioDMA; int et_DSP; int et_IPC_HLE; int et_PatchEngine; // PatchEngine updates every 1/60th of a second by default +int et_Throttle; // These are badly educated guesses // Feel free to experiment. Set these in Init below. @@ -229,6 +231,29 @@ void PatchEngineCallback(u64 userdata, int cyclesLate) CoreTiming::ScheduleEvent(VideoInterface::GetTicksPerFrame() - cyclesLate, et_PatchEngine); } +void ThrottleCallback(u64 last_time, int cyclesLate) +{ + u32 time = Common::Timer::GetTimeMs(); + + int diff = (u32)last_time - time; + bool frame_limiter = SConfig::GetInstance().m_Framelimit && SConfig::GetInstance().m_Framelimit != 2 && !Host_GetKeyState('\t'); + u32 next_event = GetTicksPerSecond()/1000; + if (SConfig::GetInstance().m_Framelimit > 2) + { + next_event = next_event * (SConfig::GetInstance().m_Framelimit - 1) * 5 / VideoInterface::TargetRefreshRate; + } + + const int max_fallback = 40; // 40 ms for one frame on 25 fps games + if (frame_limiter && abs(diff) > max_fallback) + { + WARN_LOG(COMMON, "system too %s, %d ms skipped", diff<0 ? "slow" : "fast", abs(diff) - max_fallback); + last_time = time - max_fallback; + } + else if (frame_limiter && diff > 0) + Common::SleepCurrentThread(diff); + CoreTiming::ScheduleEvent(next_event - cyclesLate, et_Throttle, last_time + 1); +} + // split from Init to break a circular dependency between VideoInterface::Init and SystemTimers::Init void PreInit() { @@ -274,11 +299,13 @@ void Init() et_AudioDMA = CoreTiming::RegisterEvent("AudioDMACallback", AudioDMACallback); et_IPC_HLE = CoreTiming::RegisterEvent("IPC_HLE_UpdateCallback", IPC_HLE_UpdateCallback); et_PatchEngine = CoreTiming::RegisterEvent("PatchEngine", PatchEngineCallback); + et_Throttle = CoreTiming::RegisterEvent("Throttle", ThrottleCallback); CoreTiming::ScheduleEvent(VideoInterface::GetTicksPerLine(), et_VI); CoreTiming::ScheduleEvent(0, et_DSP); CoreTiming::ScheduleEvent(VideoInterface::GetTicksPerFrame(), et_SI); CoreTiming::ScheduleEvent(AUDIO_DMA_PERIOD, et_AudioDMA); + CoreTiming::ScheduleEvent(0, et_Throttle, Common::Timer::GetTimeMs()); if (SConfig::GetInstance().m_LocalCoreStartupParameter.bSyncGPU) CoreTiming::ScheduleEvent(CP_PERIOD, et_CP);