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;
|
_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()
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue