diff --git a/Source/Core/Core/CoreTiming.cpp b/Source/Core/Core/CoreTiming.cpp index 59d40727ae..7479d03922 100644 --- a/Source/Core/Core/CoreTiming.cpp +++ b/Source/Core/Core/CoreTiming.cpp @@ -50,7 +50,7 @@ static Common::FifoQueue tsQueue; // event pools static Event *eventPool = nullptr; -static float lastOCFactor; +float lastOCFactor; int slicelength; static int maxSliceLength = MAX_SLICE_LENGTH; @@ -58,6 +58,9 @@ static s64 idledCycles; static u32 fakeDecStartValue; static u64 fakeDecStartTicks; +// Are we in a function that has been called from Advance() +static bool GlobalTimerIsSane; + s64 globalTimer; u64 fakeTBStartValue; u64 fakeTBStartTicks; @@ -137,6 +140,7 @@ void Init() slicelength = maxSliceLength; globalTimer = 0; idledCycles = 0; + GlobalTimerIsSane = true; ev_lost = RegisterEvent("_lost_event", &EmptyTimedCallback); } @@ -209,9 +213,16 @@ void DoState(PointerWrap &p) p.DoMarker("CoreTimingEvents"); } +// This should only be called from the CPU thread, if you are calling it any other thread, you are doing something evil u64 GetTicks() { - return (u64)globalTimer; + u64 ticks = (u64)globalTimer; + if (!GlobalTimerIsSane) + { + int downcount = DowncountToCycles(PowerPC::ppcState.downcount); + ticks += slicelength - downcount; + } + return ticks; } u64 GetIdleTicks() @@ -303,10 +314,16 @@ void ScheduleEvent(int cyclesIntoFuture, int event_type, u64 userdata) { _assert_msg_(POWERPC, Core::IsCPUThread() || Core::GetState() == Core::CORE_PAUSE, "ScheduleEvent from wrong thread"); + Event *ne = GetNewEvent(); ne->userdata = userdata; ne->type = event_type; - ne->time = globalTimer + cyclesIntoFuture; + ne->time = GetTicks() + cyclesIntoFuture; + + // If this event needs to be scheduled before the next advance(), force one early + if (!GlobalTimerIsSane) + ForceExceptionCheck(cyclesIntoFuture); + AddEventToQueue(ne); } @@ -402,6 +419,8 @@ void Advance() lastOCFactor = SConfig::GetInstance().m_OCEnable ? SConfig::GetInstance().m_OCFactor : 1.0f; PowerPC::ppcState.downcount = CyclesToDowncount(slicelength); + GlobalTimerIsSane = true; + while (first && first->time <= globalTimer) { //LOG(POWERPC, "[Scheduler] %s (%lld, %lld) ", @@ -412,6 +431,8 @@ void Advance() FreeEvent(evt); } + GlobalTimerIsSane = false; + if (!first) { WARN_LOG(POWERPC, "WARNING - no events in queue. Setting downcount to 10000"); diff --git a/Source/Core/Core/CoreTiming.h b/Source/Core/Core/CoreTiming.h index c87cbd55f5..287ea38ccc 100644 --- a/Source/Core/Core/CoreTiming.h +++ b/Source/Core/Core/CoreTiming.h @@ -34,6 +34,7 @@ void Shutdown(); typedef void (*TimedCallback)(u64 userdata, int cyclesLate); +// This should only be called from the CPU thread, if you are calling it any other thread, you are doing something evil u64 GetTicks(); u64 GetIdleTicks(); @@ -79,5 +80,6 @@ void SetFakeTBStartTicks(u64 val); void ForceExceptionCheck(int cycles); extern int slicelength; +extern float lastOCFactor; } // end of namespace diff --git a/Source/Core/Core/PowerPC/Jit64/Jit_SystemRegisters.cpp b/Source/Core/Core/PowerPC/Jit64/Jit_SystemRegisters.cpp index 8d14610f4b..6669436885 100644 --- a/Source/Core/Core/PowerPC/Jit64/Jit_SystemRegisters.cpp +++ b/Source/Core/Core/PowerPC/Jit64/Jit_SystemRegisters.cpp @@ -283,7 +283,13 @@ void Jit64::mfspr(UGeckoInstruction inst) // An inline implementation of CoreTiming::GetFakeTimeBase, since in timer-heavy games the // cost of calling out to C for this is actually significant. - MOV(64, R(RAX), M(&CoreTiming::globalTimer)); + // Scale downcount by the CPU overclocking factor. + CVTSI2SS(XMM0, PPCSTATE(downcount)); + DIVSS(XMM0, M(&CoreTiming::lastOCFactor)); + CVTSS2SI(RDX, R(XMM0)); // RDX is downcount scaled by the overclocking factor + MOV(32, R(RAX), M(&CoreTiming::slicelength)); + SUB(64, R(RAX), R(RDX)); // cycles since the last CoreTiming::Advance() event is (slicelength - Scaled_downcount) + ADD(64, R(RAX), M(&CoreTiming::globalTimer)); SUB(64, R(RAX), M(&CoreTiming::fakeTBStartTicks)); // It might seem convenient to correct the timer for the block position here for even more accurate // timing, but as of currently, this can break games. If we end up reading a time *after* the time