Cocoa Port: Rework the auto-frameskip algorithm yet again. It should be more accurate this time.
This commit is contained in:
parent
6fa6467c5c
commit
5c85bcd732
|
@ -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()
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue