Cocoa Port: Rework the auto-frameskip algorithm yet again. It should be more accurate this time.

This commit is contained in:
rogerman 2018-08-22 01:25:52 -07:00
parent 6fa6467c5c
commit 5c85bcd732
3 changed files with 118 additions and 33 deletions

View File

@ -55,6 +55,9 @@ ClientExecutionControl::ClientExecutionControl()
_frameTime = 0.0; _frameTime = 0.0;
_framesToSkip = 0; _framesToSkip = 0;
_lastSetFrameSkip = 0.0;
_unskipStep = 0;
_dynamicBiasStep = 0;
_prevExecBehavior = ExecutionBehavior_Pause; _prevExecBehavior = ExecutionBehavior_Pause;
_isGdbStubStarted = false; _isGdbStubStarted = false;
@ -643,6 +646,11 @@ bool ClientExecutionControl::GetEnableSpeedLimiter()
return enable; return enable;
} }
bool ClientExecutionControl::GetEnableSpeedLimiterApplied()
{
return this->_settingsApplied.enableExecutionSpeedLimiter;
}
void ClientExecutionControl::SetEnableSpeedLimiter(bool enable) void ClientExecutionControl::SetEnableSpeedLimiter(bool enable)
{ {
pthread_mutex_lock(&this->_mutexSettingsPendingOnExecutionLoopStart); pthread_mutex_lock(&this->_mutexSettingsPendingOnExecutionLoopStart);
@ -661,6 +669,11 @@ double ClientExecutionControl::GetExecutionSpeed()
return speedScalar; return speedScalar;
} }
double ClientExecutionControl::GetExecutionSpeedApplied()
{
return this->_settingsApplied.executionSpeed;
}
void ClientExecutionControl::SetExecutionSpeed(double speedScalar) void ClientExecutionControl::SetExecutionSpeed(double speedScalar)
{ {
pthread_mutex_lock(&this->_mutexSettingsPendingOnExecutionLoopStart); pthread_mutex_lock(&this->_mutexSettingsPendingOnExecutionLoopStart);
@ -1242,68 +1255,70 @@ double ClientExecutionControl::GetFrameTime()
uint8_t ClientExecutionControl::CalculateFrameSkip(double startAbsoluteTime, double frameAbsoluteTime) uint8_t ClientExecutionControl::CalculateFrameSkip(double startAbsoluteTime, double frameAbsoluteTime)
{ {
static const double skipCurve[10] = {0.60, 0.58, 0.55, 0.51, 0.46, 0.40, 0.30, 0.20, 0.10, 0.00}; static const double unskipCurve[21] = {0.98, 0.95, 0.91, 0.86, 0.80, 0.73, 0.65, 0.56, 0.46, 0.35, 0.23, 0.20, 0.17, 0.14, 0.11, 0.08, 0.06, 0.04, 0.02, 0.01, 0.00};
static const double unskipCurve[10] = {0.75, 0.70, 0.65, 0.60, 0.50, 0.40, 0.30, 0.20, 0.10, 0.00}; static const double dynamicBiasCurve[15] = {0.0, 0.2, 0.6, 1.2, 2.0, 3.0, 4.2, 5.6, 7.2, 9.0, 11.0, 13.2, 15.6, 18.2, 20.0};
static size_t skipStep = 0;
static size_t unskipStep = 0;
static uint64_t lastSetFrameSkip = 0;
// Calculate the time remaining. // Calculate the time remaining.
const double elapsed = this->GetCurrentAbsoluteTime() - startAbsoluteTime; const double elapsed = this->GetCurrentAbsoluteTime() - startAbsoluteTime;
uint64_t framesToSkip = 0; uint64_t framesToSkipInt = 0;
if (elapsed > frameAbsoluteTime) if (elapsed > frameAbsoluteTime)
{ {
if (frameAbsoluteTime > 0) if (frameAbsoluteTime > 0)
{ {
framesToSkip = (uint64_t)( (((elapsed - frameAbsoluteTime) * FRAME_SKIP_AGGRESSIVENESS) / frameAbsoluteTime) + FRAME_SKIP_BIAS ); const double framesToSkipReal = ((elapsed * FRAME_SKIP_AGGRESSIVENESS) / frameAbsoluteTime) + dynamicBiasCurve[this->_dynamicBiasStep] + FRAME_SKIP_BIAS;
framesToSkipInt = (uint64_t)(framesToSkipReal + 0.5);
if (framesToSkip > lastSetFrameSkip) const double frameSkipDiff = framesToSkipReal - this->_lastSetFrameSkip;
if (this->_unskipStep > 0)
{ {
framesToSkip -= (uint64_t)((double)(framesToSkip - lastSetFrameSkip) * skipCurve[skipStep]); if (this->_dynamicBiasStep > 0)
if (skipStep < 9)
{ {
skipStep++; this->_dynamicBiasStep--;
} }
} }
else else if (frameSkipDiff > 0.0)
{ {
framesToSkip += (uint64_t)((double)(lastSetFrameSkip - framesToSkip) * skipCurve[skipStep]); if (this->_dynamicBiasStep < 14)
if (skipStep > 0)
{ {
skipStep--; this->_dynamicBiasStep++;
} }
} }
this->_unskipStep = 0;
this->_lastSetFrameSkip = framesToSkipReal;
} }
else else
{ {
static const double frameRate100x = (double)FRAME_SKIP_AGGRESSIVENESS / CalculateFrameAbsoluteTime(1.0/100.0); static const double frameRate100x = (double)FRAME_SKIP_AGGRESSIVENESS / CalculateFrameAbsoluteTime(1.0/100.0);
framesToSkip = (uint64_t)(elapsed * frameRate100x); framesToSkipInt = (uint64_t)(elapsed * frameRate100x);
} }
unskipStep = 0;
} }
else else
{ {
framesToSkip = (uint64_t)((double)lastSetFrameSkip * unskipCurve[unskipStep]); const double framesToSkipReal = this->_lastSetFrameSkip * unskipCurve[this->_unskipStep];
if (unskipStep < 9) framesToSkipInt = (uint64_t)(framesToSkipReal + 0.5);
if (this->_unskipStep < 20)
{ {
unskipStep++; this->_unskipStep++;
} }
skipStep = 0; if (framesToSkipInt == 0)
{
this->_lastSetFrameSkip = 0.0;
this->_unskipStep = 20;
}
} }
// Bound the frame skip. // Bound the frame skip.
static const uint64_t kMaxFrameSkip = (uint64_t)MAX_FRAME_SKIP; static const uint64_t kMaxFrameSkip = (uint64_t)MAX_FRAME_SKIP;
if (framesToSkip > kMaxFrameSkip) if (framesToSkipInt > kMaxFrameSkip)
{ {
framesToSkip = kMaxFrameSkip; framesToSkipInt = kMaxFrameSkip;
} }
lastSetFrameSkip = framesToSkip; return (uint8_t)framesToSkipInt;
return (uint8_t)framesToSkip;
} }
double ClientExecutionControl::GetCurrentAbsoluteTime() double ClientExecutionControl::GetCurrentAbsoluteTime()

View File

@ -40,11 +40,14 @@
#define DS_FRAMES_PER_SECOND 59.8261 // Number of DS frames per second. #define DS_FRAMES_PER_SECOND 59.8261 // Number of DS frames per second.
#define DS_SECONDS_PER_FRAME (1.0 / DS_FRAMES_PER_SECOND) // The length of time in seconds that, ideally, a frame should be processed within. #define DS_SECONDS_PER_FRAME (1.0 / DS_FRAMES_PER_SECOND) // The length of time in seconds that, ideally, a frame should be processed within.
#define FRAME_SKIP_AGGRESSIVENESS 9.0 // Must be a value between 0.0 (inclusive) and positive infinity. #define FRAME_SKIP_AGGRESSIVENESS 1.0 // Must be a value between 0.0 (inclusive) and positive infinity.
// This value acts as a scalar multiple of the frame skip. // This value acts as a scalar multiple of the frame skip.
#define FRAME_SKIP_BIAS 0.1 // May be any real number. This value acts as a vector addition to the frame skip. #define FRAME_SKIP_BIAS 0.9 // May be any real number. This value acts as a vector addition to the frame skip.
#define MAX_FRAME_SKIP (DS_FRAMES_PER_SECOND / 2.98) #define MAX_FRAME_SKIP (DS_FRAMES_PER_SECOND / 2.98)
#define EXECUTION_WAIT_BIAS_MIN 0.70
#define EXECUTION_WAIT_BIAS_MAX 1.10
class ClientAVCaptureObject; class ClientAVCaptureObject;
enum ExecutionBehavior enum ExecutionBehavior
@ -215,6 +218,9 @@ protected:
double _frameTime; double _frameTime;
uint8_t _framesToSkip; uint8_t _framesToSkip;
double _lastSetFrameSkip;
size_t _unskipStep;
size_t _dynamicBiasStep;
ExecutionBehavior _prevExecBehavior; ExecutionBehavior _prevExecBehavior;
bool _isGdbStubStarted; bool _isGdbStubStarted;
@ -312,9 +318,11 @@ public:
void SetEnableCheats(bool enable); void SetEnableCheats(bool enable);
bool GetEnableSpeedLimiter(); bool GetEnableSpeedLimiter();
bool GetEnableSpeedLimiterApplied();
void SetEnableSpeedLimiter(bool enable); void SetEnableSpeedLimiter(bool enable);
double GetExecutionSpeed(); double GetExecutionSpeed();
double GetExecutionSpeedApplied();
void SetExecutionSpeed(double speedScalar); void SetExecutionSpeed(double speedScalar);
bool GetEnableFrameSkip(); bool GetEnableFrameSkip();

View File

@ -1060,10 +1060,17 @@ static void* RunCoreThread(void *arg)
double frameTime = 0.0; // The amount of time that is expected for the frame to run. double frameTime = 0.0; // The amount of time that is expected for the frame to run.
const double standardNDSFrameTime = execControl->CalculateFrameAbsoluteTime(1.0); const double standardNDSFrameTime = execControl->CalculateFrameAbsoluteTime(1.0);
double lastSelectedExecSpeedSelected = 1.0;
double executionSpeedAverage = 0.0; double executionSpeedAverage = 0.0;
double executionSpeedAverageFramesCollected = 0.0; double executionSpeedAverageFramesCollected = 0.0;
double executionWaitBias = 1.0;
double lastExecutionWaitBias = 1.0;
double lastExecutionSpeedDifference = 0.0;
bool needRestoreExecutionWaitBias = false;
bool lastExecutionSpeedLimitEnable = execControl->GetEnableSpeedLimiter();
ExecutionBehavior behavior = ExecutionBehavior_Pause; ExecutionBehavior behavior = ExecutionBehavior_Pause;
ExecutionBehavior lastBehavior = ExecutionBehavior_Pause;
uint64_t frameJumpTarget = 0; uint64_t frameJumpTarget = 0;
[[[cdsCore cdsGPU] sharedData] semaphoreFramebufferCreate]; [[[cdsCore cdsGPU] sharedData] semaphoreFramebufferCreate];
@ -1085,6 +1092,21 @@ static void* RunCoreThread(void *arg)
behavior = execControl->GetExecutionBehaviorApplied(); behavior = execControl->GetExecutionBehaviorApplied();
} }
if ( (lastBehavior == ExecutionBehavior_Run) && (behavior != ExecutionBehavior_Run) )
{
lastExecutionWaitBias = executionWaitBias;
needRestoreExecutionWaitBias = true;
}
if ( (behavior == ExecutionBehavior_Run) && lastExecutionSpeedLimitEnable && !execControl->GetEnableSpeedLimiter() )
{
lastExecutionWaitBias = executionWaitBias;
}
else if ( (behavior == ExecutionBehavior_Run) && !lastExecutionSpeedLimitEnable && execControl->GetEnableSpeedLimiter() )
{
needRestoreExecutionWaitBias = true;
}
frameTime = execControl->GetFrameTime(); frameTime = execControl->GetFrameTime();
frameJumpTarget = execControl->GetFrameJumpTargetApplied(); frameJumpTarget = execControl->GetFrameJumpTargetApplied();
@ -1144,7 +1166,39 @@ static void* RunCoreThread(void *arg)
{ {
if (executionSpeedAverageFramesCollected > 0.0001) if (executionSpeedAverageFramesCollected > 0.0001)
{ {
execControl->SetFrameInfoExecutionSpeed((executionSpeedAverage / executionSpeedAverageFramesCollected) * 100.0); const double execSpeedSelected = execControl->GetExecutionSpeedApplied();
const double execSpeedCurrent = (executionSpeedAverage / executionSpeedAverageFramesCollected);
const double execSpeedDifference = execSpeedSelected - execSpeedCurrent;
execControl->SetFrameInfoExecutionSpeed(execSpeedCurrent * 100.0);
if ( (behavior == ExecutionBehavior_Run) && needRestoreExecutionWaitBias )
{
executionWaitBias = lastExecutionWaitBias;
needRestoreExecutionWaitBias = false;
}
else
{
if (lastSelectedExecSpeedSelected == execSpeedSelected)
{
executionWaitBias -= execSpeedDifference;
lastExecutionSpeedDifference = execSpeedDifference;
if (executionWaitBias < EXECUTION_WAIT_BIAS_MIN)
{
executionWaitBias = EXECUTION_WAIT_BIAS_MIN;
}
else if (executionWaitBias > EXECUTION_WAIT_BIAS_MAX)
{
executionWaitBias = EXECUTION_WAIT_BIAS_MAX;
}
}
else
{
executionWaitBias = 1.0;
lastSelectedExecSpeedSelected = execSpeedSelected;
}
}
} }
executionSpeedAverage = 0.0; executionSpeedAverage = 0.0;
@ -1200,7 +1254,8 @@ static void* RunCoreThread(void *arg)
} }
else else
{ {
execControl->SetFramesToSkip( execControl->CalculateFrameSkip(startTime, frameTime) ); const double frameTimeBias = (lastExecutionSpeedDifference > 0.0) ? 1.0 - lastExecutionSpeedDifference : 1.0;
execControl->SetFramesToSkip( execControl->CalculateFrameSkip(startTime, frameTime * frameTimeBias) );
} }
} }
break; break;
@ -1249,9 +1304,14 @@ static void* RunCoreThread(void *arg)
else else
{ {
// If there is any time left in the loop, go ahead and pad it. // If there is any time left in the loop, go ahead and pad it.
if ( (execControl->GetCurrentAbsoluteTime() - startTime) < frameTime ) const double biasedFrameTime = frameTime * executionWaitBias;
if (biasedFrameTime > 0.0)
{ {
execControl->WaitUntilAbsoluteTime(startTime + frameTime); if ( (execControl->GetCurrentAbsoluteTime() - startTime) < frameTime )
{
execControl->WaitUntilAbsoluteTime(startTime + biasedFrameTime);
}
} }
} }
@ -1259,6 +1319,8 @@ static void* RunCoreThread(void *arg)
const double currentExecutionSpeed = standardNDSFrameTime / (endTime - startTime); const double currentExecutionSpeed = standardNDSFrameTime / (endTime - startTime);
executionSpeedAverage += currentExecutionSpeed; executionSpeedAverage += currentExecutionSpeed;
executionSpeedAverageFramesCollected += 1.0; executionSpeedAverageFramesCollected += 1.0;
lastBehavior = behavior;
lastExecutionSpeedLimitEnable = execControl->GetEnableSpeedLimiterApplied();
} while(true); } while(true);