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;
_framesToSkip = 0;
_lastSetFrameSkip = 0.0;
_unskipStep = 0;
_dynamicBiasStep = 0;
_prevExecBehavior = ExecutionBehavior_Pause;
_isGdbStubStarted = false;
@ -643,6 +646,11 @@ bool ClientExecutionControl::GetEnableSpeedLimiter()
return enable;
}
bool ClientExecutionControl::GetEnableSpeedLimiterApplied()
{
return this->_settingsApplied.enableExecutionSpeedLimiter;
}
void ClientExecutionControl::SetEnableSpeedLimiter(bool enable)
{
pthread_mutex_lock(&this->_mutexSettingsPendingOnExecutionLoopStart);
@ -661,6 +669,11 @@ double ClientExecutionControl::GetExecutionSpeed()
return speedScalar;
}
double ClientExecutionControl::GetExecutionSpeedApplied()
{
return this->_settingsApplied.executionSpeed;
}
void ClientExecutionControl::SetExecutionSpeed(double speedScalar)
{
pthread_mutex_lock(&this->_mutexSettingsPendingOnExecutionLoopStart);
@ -1242,68 +1255,70 @@ double ClientExecutionControl::GetFrameTime()
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[10] = {0.75, 0.70, 0.65, 0.60, 0.50, 0.40, 0.30, 0.20, 0.10, 0.00};
static size_t skipStep = 0;
static size_t unskipStep = 0;
static uint64_t lastSetFrameSkip = 0;
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 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};
// Calculate the time remaining.
const double elapsed = this->GetCurrentAbsoluteTime() - startAbsoluteTime;
uint64_t framesToSkip = 0;
uint64_t framesToSkipInt = 0;
if (elapsed > frameAbsoluteTime)
{
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 (skipStep < 9)
if (this->_dynamicBiasStep > 0)
{
skipStep++;
this->_dynamicBiasStep--;
}
}
else
else if (frameSkipDiff > 0.0)
{
framesToSkip += (uint64_t)((double)(lastSetFrameSkip - framesToSkip) * skipCurve[skipStep]);
if (skipStep > 0)
if (this->_dynamicBiasStep < 14)
{
skipStep--;
this->_dynamicBiasStep++;
}
}
this->_unskipStep = 0;
this->_lastSetFrameSkip = framesToSkipReal;
}
else
{
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
{
framesToSkip = (uint64_t)((double)lastSetFrameSkip * unskipCurve[unskipStep]);
if (unskipStep < 9)
const double framesToSkipReal = this->_lastSetFrameSkip * unskipCurve[this->_unskipStep];
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.
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)framesToSkip;
return (uint8_t)framesToSkipInt;
}
double ClientExecutionControl::GetCurrentAbsoluteTime()

View File

@ -40,11 +40,14 @@
#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 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.
#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 EXECUTION_WAIT_BIAS_MIN 0.70
#define EXECUTION_WAIT_BIAS_MAX 1.10
class ClientAVCaptureObject;
enum ExecutionBehavior
@ -215,6 +218,9 @@ protected:
double _frameTime;
uint8_t _framesToSkip;
double _lastSetFrameSkip;
size_t _unskipStep;
size_t _dynamicBiasStep;
ExecutionBehavior _prevExecBehavior;
bool _isGdbStubStarted;
@ -312,9 +318,11 @@ public:
void SetEnableCheats(bool enable);
bool GetEnableSpeedLimiter();
bool GetEnableSpeedLimiterApplied();
void SetEnableSpeedLimiter(bool enable);
double GetExecutionSpeed();
double GetExecutionSpeedApplied();
void SetExecutionSpeed(double speedScalar);
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.
const double standardNDSFrameTime = execControl->CalculateFrameAbsoluteTime(1.0);
double lastSelectedExecSpeedSelected = 1.0;
double executionSpeedAverage = 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 lastBehavior = ExecutionBehavior_Pause;
uint64_t frameJumpTarget = 0;
[[[cdsCore cdsGPU] sharedData] semaphoreFramebufferCreate];
@ -1085,6 +1092,21 @@ static void* RunCoreThread(void *arg)
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();
frameJumpTarget = execControl->GetFrameJumpTargetApplied();
@ -1144,7 +1166,39 @@ static void* RunCoreThread(void *arg)
{
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;
@ -1200,7 +1254,8 @@ static void* RunCoreThread(void *arg)
}
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;
@ -1249,9 +1304,14 @@ static void* RunCoreThread(void *arg)
else
{
// 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);
executionSpeedAverage += currentExecutionSpeed;
executionSpeedAverageFramesCollected += 1.0;
lastBehavior = behavior;
lastExecutionSpeedLimitEnable = execControl->GetEnableSpeedLimiterApplied();
} while(true);