Support overclocking and underclocking the Gamecube CPU

Won't work with all games, but provides a nice way to spend extra CPU to make
a variable framerate game faster (e.g. Spyro or The Last Story), or to make
a game use less CPU at the cost of a lower framerate (e.g. Rogue Leader).
This commit is contained in:
Fiora
2014-12-31 10:24:14 -08:00
parent e32d63c43d
commit c2c950108d
12 changed files with 111 additions and 10 deletions

View File

@ -48,6 +48,7 @@ static Common::FifoQueue<BaseEvent, false> tsQueue;
// event pools
static Event *eventPool = nullptr;
float lastOCFactor;
int slicelength;
static int maxSliceLength = MAX_SLICE_LENGTH;
@ -82,6 +83,23 @@ static void FreeEvent(Event* ev)
static void EmptyTimedCallback(u64 userdata, int cyclesLate) {}
// Changing the CPU speed in Dolphin isn't actually done by changing the physical clock rate,
// but by changing the amount of work done in a particular amount of time. This tends to be more
// compatible because it stops the games from actually knowing directly that the clock rate has
// changed, and ensures that anything based on waiting a specific number of cycles still works.
//
// Technically it might be more accurate to call this changing the IPC instead of the CPU speed,
// but the effect is largely the same.
static int DowncountToCycles(int downcount)
{
return (int)(downcount / lastOCFactor);
}
static int CyclesToDowncount(int cycles)
{
return (int)(cycles * lastOCFactor);
}
int RegisterEvent(const std::string& name, TimedCallback callback)
{
EventType type;
@ -115,7 +133,8 @@ void UnregisterAllEvents()
void Init()
{
PowerPC::ppcState.downcount = maxSliceLength;
lastOCFactor = SConfig::GetInstance().m_OCEnable ? SConfig::GetInstance().m_OCFactor : 1.0f;
PowerPC::ppcState.downcount = CyclesToDowncount(maxSliceLength);
slicelength = maxSliceLength;
globalTimer = 0;
idledCycles = 0;
@ -182,6 +201,7 @@ void DoState(PointerWrap &p)
p.Do(fakeDecStartTicks);
p.Do(fakeTBStartValue);
p.Do(fakeTBStartTicks);
p.Do(lastOCFactor);
p.DoMarker("CoreTimingData");
MoveEvents();
@ -338,10 +358,10 @@ void SetMaximumSlice(int maximumSliceLength)
void ForceExceptionCheck(int cycles)
{
if (PowerPC::ppcState.downcount > cycles)
if (DowncountToCycles(PowerPC::ppcState.downcount) > cycles)
{
slicelength -= (PowerPC::ppcState.downcount - cycles); // Account for cycles already executed by adjusting the slicelength
PowerPC::ppcState.downcount = cycles;
slicelength -= (DowncountToCycles(PowerPC::ppcState.downcount) - cycles); // Account for cycles already executed by adjusting the slicelength
PowerPC::ppcState.downcount = CyclesToDowncount(cycles);
}
}
@ -392,9 +412,10 @@ void Advance()
{
MoveEvents();
int cyclesExecuted = slicelength - PowerPC::ppcState.downcount;
int cyclesExecuted = slicelength - DowncountToCycles(PowerPC::ppcState.downcount);
globalTimer += cyclesExecuted;
PowerPC::ppcState.downcount = slicelength;
lastOCFactor = SConfig::GetInstance().m_OCEnable ? SConfig::GetInstance().m_OCFactor : 1.0f;
PowerPC::ppcState.downcount = CyclesToDowncount(slicelength);
while (first)
{
@ -416,14 +437,14 @@ void Advance()
if (!first)
{
WARN_LOG(POWERPC, "WARNING - no events in queue. Setting downcount to 10000");
PowerPC::ppcState.downcount += 10000;
PowerPC::ppcState.downcount += CyclesToDowncount(10000);
}
else
{
slicelength = (int)(first->time - globalTimer);
if (slicelength > maxSliceLength)
slicelength = maxSliceLength;
PowerPC::ppcState.downcount = slicelength;
PowerPC::ppcState.downcount = CyclesToDowncount(slicelength);
}
if (advanceCallback)
@ -456,7 +477,7 @@ void Idle()
}
}
idledCycles += PowerPC::ppcState.downcount;
idledCycles += DowncountToCycles(PowerPC::ppcState.downcount);
PowerPC::ppcState.downcount = 0;
Advance();