mirror of https://github.com/PCSX2/pcsx2.git
Counters: Tighten tolerances on framelimiter (#3785)
Smooths out frame pace and keeps average framerate closer to target.
This commit is contained in:
parent
11bb7bdff9
commit
b62270ea2e
|
@ -103,7 +103,7 @@ static __fi void _rcntSet( int cntidx )
|
|||
if (c < nextCounter)
|
||||
{
|
||||
nextCounter = c;
|
||||
cpuSetNextEvent( nextsCounter, nextCounter ); //Need to update on counter resets/target changes
|
||||
cpuSetNextEvent( nextsCounter, nextCounter ); // Need to update on counter resets/target changes
|
||||
}
|
||||
|
||||
// Ignore target diff if target is currently disabled.
|
||||
|
@ -121,7 +121,7 @@ static __fi void _rcntSet( int cntidx )
|
|||
if (c < nextCounter)
|
||||
{
|
||||
nextCounter = c;
|
||||
cpuSetNextEvent( nextsCounter, nextCounter ); //Need to update on counter resets/target changes
|
||||
cpuSetNextEvent(nextsCounter, nextCounter); // Need to update on counter resets/target changes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -139,6 +139,7 @@ static __fi void cpuRcntSet()
|
|||
|
||||
// sanity check!
|
||||
if( nextCounter < 0 ) nextCounter = 0;
|
||||
cpuSetNextEvent(nextsCounter, nextCounter); // Need to update on counter resets/target changes
|
||||
}
|
||||
|
||||
void rcntInit()
|
||||
|
@ -394,58 +395,65 @@ void frameLimitReset()
|
|||
m_iStart = GetCPUTicks();
|
||||
}
|
||||
|
||||
// Convenience function to update UI thread and set patches.
|
||||
static __fi void frameLimitUpdateCore()
|
||||
{
|
||||
GetCoreThread().VsyncInThread();
|
||||
Cpu->CheckExecutionState();
|
||||
}
|
||||
|
||||
// Framelimiter - Measures the delta time between calls and stalls until a
|
||||
// certain amount of time passes if such time hasn't passed yet.
|
||||
// See the GS FrameSkip function for details on why this is here and not in the GS.
|
||||
static __fi void frameLimit()
|
||||
{
|
||||
// 999 means the user would rather just have framelimiting turned off...
|
||||
if( !EmuConfig.GS.FrameLimitEnable ) return;
|
||||
|
||||
u64 uExpectedEnd = m_iStart + m_iTicks;
|
||||
u64 iEnd = GetCPUTicks();
|
||||
s64 sDeltaTime = iEnd - uExpectedEnd;
|
||||
|
||||
// If the framerate drops too low, reset the expected value. This avoids
|
||||
// excessive amounts of "fast forward" syndrome which would occur if we
|
||||
// tried to catch up too much.
|
||||
|
||||
if( sDeltaTime > m_iTicks*8 )
|
||||
// Framelimiter off in settings? Framelimiter go brrr.
|
||||
if (!EmuConfig.GS.FrameLimitEnable)
|
||||
{
|
||||
m_iStart = iEnd - m_iTicks;
|
||||
frameLimitUpdateCore();
|
||||
return;
|
||||
}
|
||||
|
||||
// use the expected frame completion time as our starting point.
|
||||
// improves smoothness by making the framelimiter more adaptive to the
|
||||
// imperfect TIMESLICE() wait, and allows it to speed up a wee bit after
|
||||
// slow frames to "catch up."
|
||||
u64 uExpectedEnd = m_iStart + m_iTicks; // Compute when we would expect this frame to end, assuming everything goes perfectly perfect.
|
||||
u64 iEnd = GetCPUTicks(); // The current tick we actually stopped on.
|
||||
s64 sDeltaTime = iEnd - uExpectedEnd; // The diff between when we stopped and when we expected to.
|
||||
|
||||
// If frame ran too long...
|
||||
if (sDeltaTime >= m_iTicks)
|
||||
{
|
||||
// ... Fudge the next frame start over a bit. Prevents fast forward zoomies.
|
||||
m_iStart += (sDeltaTime / m_iTicks) * m_iTicks;
|
||||
frameLimitUpdateCore();
|
||||
return;
|
||||
}
|
||||
|
||||
// Conversion of delta from CPU ticks (microseconds) to milliseconds
|
||||
s32 msec = (int) ((sDeltaTime * -1000) / (s64) GetTickFrequency());
|
||||
|
||||
// If any integer value of milliseconds exists, sleep it off.
|
||||
// Prior comments suggested that 1-2 ms sleeps were inaccurate on some OSes;
|
||||
// further testing suggests instead that this was utter bullshit.
|
||||
if (msec > 1)
|
||||
{
|
||||
Threading::Sleep(msec - 1);
|
||||
}
|
||||
|
||||
// Conversion to milliseconds loses some precision; after sleeping off whole milliseconds,
|
||||
// spin the thread without sleeping until we finally reach our expected end time.
|
||||
while (GetCPUTicks() < uExpectedEnd)
|
||||
{
|
||||
// SKREEEEEEEE
|
||||
}
|
||||
|
||||
// Finally, set our next frame start to when this one ends
|
||||
m_iStart = uExpectedEnd;
|
||||
|
||||
// Shortcut for cases where no waiting is needed (they're running slow already,
|
||||
// so don't bog 'em down with extra math...)
|
||||
if( sDeltaTime >= 0 ) return;
|
||||
|
||||
// If we're way ahead then we can afford to sleep the thread a bit.
|
||||
// (note, on Windows sleep(1) thru sleep(2) tend to be the least accurate sleeps,
|
||||
// and longer sleeps tend to be pretty reliable, so that's why the convoluted if/
|
||||
// else below. The same generally isn't true for Linux, but no harm either way
|
||||
// really.)
|
||||
|
||||
s32 msec = (int)((sDeltaTime*-1000) / (s64)GetTickFrequency());
|
||||
if( msec > 4 ) Threading::Sleep( msec );
|
||||
else if( msec > 2 ) Threading::Sleep( 1 );
|
||||
|
||||
// Sleep is not picture-perfect accurate, but it's actually not necessary to
|
||||
// maintain a "perfect" lock to uExpectedEnd anyway. if we're a little ahead
|
||||
// starting this frame, it'll just sleep longer the next to make up for it. :)
|
||||
frameLimitUpdateCore();
|
||||
}
|
||||
|
||||
static __fi void VSyncStart(u32 sCycle)
|
||||
{
|
||||
GetCoreThread().VsyncInThread();
|
||||
Cpu->CheckExecutionState();
|
||||
frameLimit(); // limit FPS
|
||||
gsPostVsyncStart(); // MUST be after framelimit; doing so before causes funk with frame times!
|
||||
|
||||
if(EmuConfig.Trace.Enabled && EmuConfig.Trace.EE.m_EnableAll)
|
||||
SysTrace.EE.Counters.Write( " ================ EE COUNTER VSYNC START (frame: %d) ================", g_FrameCount );
|
||||
|
@ -460,7 +468,7 @@ static __fi void VSyncStart(u32 sCycle)
|
|||
|
||||
hwIntcIrq(INTC_VBLANK_S);
|
||||
psxVBlankStart();
|
||||
gsPostVsyncStart();
|
||||
|
||||
if (gates) rcntStartGate(true, sCycle); // Counters Start Gate code
|
||||
|
||||
// INTC - VB Blank Start Hack --
|
||||
|
@ -514,8 +522,6 @@ static __fi void VSyncEnd(u32 sCycle)
|
|||
if (!(g_FrameCount % 60))
|
||||
sioNextFrame();
|
||||
|
||||
frameLimit(); // limit FPS
|
||||
|
||||
// This doesn't seem to be needed here. Games only seem to break with regard to the
|
||||
// vsyncstart irq.
|
||||
//cpuRegs.eCycle[30] = 2;
|
||||
|
|
Loading…
Reference in New Issue