Adjust cycle counts so they are accurate to the jit block level

Previously GlobalTimer was only updated at the end of each slice
when CoreTiming::Advance() was called, so it could be upto 20,000
cycles off.

This was causing huge problems with games which made heavy use of
the time base register, such as OoT (virtual console) and Pokemon
puzzle.

I've also made it so event scheduling will be accurate to the jit
block level, instead of accurate to the slice.
This commit is contained in:
Scott Mansell 2016-02-07 05:59:45 +13:00
parent 3ff56aa192
commit 2ebbfd6f85
3 changed files with 33 additions and 4 deletions

View file

@ -50,7 +50,7 @@ static Common::FifoQueue<BaseEvent, false> 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");

View file

@ -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

View file

@ -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