Changed Qt GUI frame throttling to use new time stamp record for computing timing. This fixes an issue where frame timing starts to lose precision when application time gets to float large values by performing calcultions using integer math

This commit is contained in:
harry 2023-05-21 16:44:20 -04:00
parent 6fb899e9a1
commit 1a3170fd68
3 changed files with 212 additions and 58 deletions

View File

@ -37,7 +37,8 @@ static const double Fastest = 32; // 32x speed (around 1920 fps on NTSC)
static const double Normal = 1.0; // 1x speed (around 60 fps on NTSC)
static uint32 frameLateCounter = 0;
static double Lasttime=0, Nexttime=0, Latetime=0;
static FCEU::timeStampRecord Lasttime, Nexttime, Latetime;
static FCEU::timeStampRecord DesiredFrameTime, HalfFrameTime, QuarterFrameTime, DoubleFrameTime;
static double desired_frametime = (1.0 / 60.099823);
static double desired_frameRate = (60.099823);
static double baseframeRate = (60.099823);
@ -119,9 +120,9 @@ static void setTimer( double hz )
//printf("Timer Set: %li ns\n", ispec.it_value.tv_nsec );
Lasttime = getHighPrecTimeStamp();
Nexttime = Lasttime + desired_frametime;
Latetime = Nexttime + (desired_frametime*0.50);
Lasttime.readNew();
Nexttime = Lasttime + DesiredFrameTime;
Latetime = Nexttime + HalfFrameTime;
}
#endif
@ -270,10 +271,17 @@ RefreshThrottleFPS(void)
if ( T < 0 ) T = 1;
DesiredFrameTime.fromSeconds( desired_frametime );
HalfFrameTime = DesiredFrameTime / 2;
QuarterFrameTime = DesiredFrameTime / 4;
DoubleFrameTime = DesiredFrameTime * 2;
//printf("FrameTime: %f %f %f %f \n", DesiredFrameTime.toSeconds(),
// HalfFrameTime.toSeconds(), QuarterFrameTime.toSeconds(), DoubleFrameTime.toSeconds() );
//printf("FrameTime: %llu %llu %f %lf \n", fps, fps >> 24, hz, desired_frametime );
Lasttime=0;
Nexttime=0;
Lasttime.zero();
Nexttime.zero();
InFrame=0;
#ifdef __linux__
@ -297,18 +305,17 @@ double getFrameRateAdjustmentRatio(void)
return frmRateAdjRatio;
}
int highPrecSleep( double timeSeconds )
static int highPrecSleep( FCEU::timeStampRecord &ts )
{
int ret = 0;
#if defined(__linux__) || defined(__APPLE__) || defined(__unix__)
struct timespec req, rem;
req.tv_sec = (long)timeSeconds;
req.tv_nsec = (long)((timeSeconds - (double)req.tv_sec) * 1e9);
req = ts.toTimeSpec();
ret = nanosleep( &req, &rem );
#else
SDL_Delay( (long)(timeSeconds * 1e3) );
SDL_Delay( ts.toMilliSeconds() );
#endif
return ret;
}
@ -323,39 +330,37 @@ SpeedThrottle(void)
{
return 0; /* Done waiting */
}
double time_left;
double cur_time, idleStart;
double frame_time = desired_frametime;
double halfFrame = 0.500 * frame_time;
double quarterFrame = 0.250 * frame_time;
FCEU::timeStampRecord cur_time, idleStart, time_left;
idleStart = cur_time = getHighPrecTimeStamp();
cur_time.readNew();
idleStart = cur_time;
if (Lasttime < 1.0)
if (Lasttime.isZero())
{
//printf("Lasttime Reset\n");
Lasttime = cur_time;
Latetime = Lasttime + 2.0*frame_time;
Latetime = Lasttime + DoubleFrameTime;
}
if (!InFrame)
{
InFrame = 1;
Nexttime = Lasttime + frame_time;
Latetime = Nexttime + halfFrame;
Nexttime = Lasttime + DesiredFrameTime;
Latetime = Nexttime + HalfFrameTime;
}
if (cur_time >= Nexttime)
{
time_left = 0;
time_left.zero();
}
else
{
time_left = Nexttime - cur_time;
}
if (time_left > 50)
if (time_left.toMilliSeconds() > 50)
{
time_left = 50;
time_left.fromMilliSeconds(50);
/* In order to keep input responsive, don't wait too long at once */
/* 50 ms wait gives us a 20 Hz responsetime which is nice. */
}
@ -381,7 +386,7 @@ SpeedThrottle(void)
}
}
}
else if ( time_left > 0 )
else if ( !time_left.isZero() )
{
highPrecSleep( time_left );
}
@ -394,7 +399,7 @@ SpeedThrottle(void)
}
}
#else
if ( time_left > 0 )
if ( !time_left.isZero() )
{
highPrecSleep( time_left );
}
@ -408,14 +413,15 @@ SpeedThrottle(void)
}
#endif
cur_time = getHighPrecTimeStamp();
cur_time.readNew();
if ( cur_time >= (Nexttime - quarterFrame) )
if ( cur_time >= (Nexttime - QuarterFrameTime) )
{
if ( keepFrameTimeStats )
{
FCEU::timeStampRecord diffTime = (cur_time - Lasttime);
frameDeltaCur = (cur_time - Lasttime);
frameDeltaCur = diffTime.toSeconds();
if ( frameDeltaCur < frameDeltaMin )
{
@ -426,7 +432,9 @@ SpeedThrottle(void)
frameDeltaMax = frameDeltaCur;
}
frameIdleCur = (cur_time - idleStart);
diffTime = (cur_time - idleStart);
frameIdleCur = diffTime.toSeconds();
if ( frameIdleCur < frameIdleMin )
{
@ -440,14 +448,14 @@ SpeedThrottle(void)
//printf("Frame Sleep Time: %f Target Error: %f us\n", time_left * 1e6, (cur_time - Nexttime) * 1e6 );
}
Lasttime = Nexttime;
Nexttime = Lasttime + frame_time;
Latetime = Nexttime + halfFrame;
Nexttime = Lasttime + DesiredFrameTime;
Latetime = Nexttime + HalfFrameTime;
if ( cur_time >= Nexttime )
{
Lasttime = cur_time;
Nexttime = Lasttime + frame_time;
Latetime = Nexttime + halfFrame;
Nexttime = Lasttime + DesiredFrameTime;
Latetime = Nexttime + HalfFrameTime;
}
return 0; /* Done waiting */
}

View File

@ -29,7 +29,7 @@ static uint64_t rdtsc()
namespace FCEU
{
uint64_t timeStampRecord::tscFreq = 0;
uint64_t timeStampRecord::_tscFreq = 0;
#if defined(WIN32)
uint64_t timeStampRecord::qpcFreq = 0;
#endif
@ -63,16 +63,15 @@ static timeStampModule module;
bool timeStampModuleInitialized(void)
{
bool initialized = false;
#if defined(WIN32)
initialized = timeStampRecord::qpcFreq != 0;
bool initialized = timeStampRecord::qpcFreq != 0;
#else
initialized = true;
bool initialized = true;
#endif
return initialized;
}
void timeStampModuleCalibrate(int numSamples)
void timeStampRecord::tscCalibrate(int numSamples)
{
timeStampRecord t1, t2, td;
uint64_t td_sum = 0;
@ -102,10 +101,10 @@ void timeStampModuleCalibrate(int numSamples)
td_avg = static_cast<double>(td_sum);
timeStampRecord::tscFreq = static_cast<uint64_t>( td_avg / td.toSeconds() );
timeStampRecord::_tscFreq = static_cast<uint64_t>( td_avg / td.toSeconds() );
printf("%i Calibration: %f sec TSC:%llu TSC Freq: %f MHz\n", i, td.toSeconds(),
static_cast<unsigned long long>(td.tsc), static_cast<double>(timeStampRecord::tscFreq) * 1.0e-6 );
static_cast<unsigned long long>(td.tsc), static_cast<double>(timeStampRecord::_tscFreq) * 1.0e-6 );
}
}

View File

@ -9,11 +9,10 @@
namespace FCEU
{
struct timeStampRecord
class timeStampRecord
{
public:
#if defined(__linux__) || defined(__APPLE__) || defined(__unix__)
struct timespec ts;
uint64_t tsc;
timeStampRecord(void)
{
@ -37,7 +36,7 @@ namespace FCEU
if (ts.tv_nsec >= 1000000000)
{
ts.tv_nsec -= 1000000000;
ts.tv_sec++;
ts.tv_sec++;
}
tsc += op.tsc;
return *this;
@ -53,7 +52,7 @@ namespace FCEU
if (res.ts.tv_nsec >= 1000000000)
{
res.ts.tv_nsec -= 1000000000;
res.ts.tv_sec++;
res.ts.tv_sec++;
}
res.tsc = tsc + op.tsc;
return res;
@ -76,30 +75,84 @@ namespace FCEU
return res;
}
timeStampRecord operator * (const unsigned int multiplier)
{
timeStampRecord res;
res.ts.tv_sec = ts.tv_sec * multiplier;
res.ts.tv_nsec = ts.tv_nsec * multiplier;
if (res.ts.tv_nsec >= 1000000000)
{
res.ts.tv_nsec -= 1000000000;
res.ts.tv_sec++;
}
res.tsc = tsc * multiplier;
return res;
}
timeStampRecord operator / (const unsigned int divisor)
{
timeStampRecord res;
res.ts.tv_sec = ts.tv_sec / divisor;
res.ts.tv_nsec = ts.tv_nsec / divisor;
res.tsc = tsc / divisor;
return res;
}
bool operator > (const timeStampRecord& op)
{
bool res = false;
bool res;
if (ts.tv_sec == op.ts.tv_sec)
{
res = (ts.tv_nsec > op.ts.tv_nsec);
}
else if (ts.tv_sec > op.ts.tv_sec)
else
{
res = true;
res = (ts.tv_sec > op.ts.tv_sec);
}
return res;
}
bool operator >= (const timeStampRecord& op)
{
bool res;
if (ts.tv_sec == op.ts.tv_sec)
{
res = (ts.tv_nsec >= op.ts.tv_nsec);
}
else
{
res = (ts.tv_sec >= op.ts.tv_sec);
}
return res;
}
bool operator < (const timeStampRecord& op)
{
bool res = false;
bool res;
if (ts.tv_sec == op.ts.tv_sec)
{
res = (ts.tv_nsec < op.ts.tv_nsec);
}
else if (ts.tv_sec < op.ts.tv_sec)
else
{
res = true;
res = (ts.tv_sec < op.ts.tv_sec);
}
return res;
}
bool operator <= (const timeStampRecord& op)
{
bool res;
if (ts.tv_sec == op.ts.tv_sec)
{
res = (ts.tv_nsec <= op.ts.tv_nsec);
}
else
{
res = (ts.tv_sec <= op.ts.tv_sec);
}
return res;
}
@ -111,6 +164,11 @@ namespace FCEU
tsc = 0;
}
bool isZero(void)
{
return (ts.tv_sec == 0) && (ts.tv_nsec == 0);
}
void fromSeconds(unsigned int sec)
{
ts.tv_sec = sec;
@ -118,12 +176,33 @@ namespace FCEU
tsc = 0;
}
void fromSeconds(double sec)
{
double ns;
ts.tv_sec = static_cast<time_t>(sec);
ns = (sec - static_cast<double>(ts.tv_sec)) * 1.0e9;
ts.tv_nsec = static_cast<long>(ns);
tsc = 0;
}
double toSeconds(void)
{
double sec = static_cast<double>(ts.tv_sec) + ( static_cast<double>(ts.tv_nsec) * 1.0e-9 );
return sec;
}
void fromMilliSeconds(uint64_t ms)
{
ts.tv_sec = ms / 1000;
ts.tv_nsec = (ms * 1000000) - (ts.tv_sec * 1000000000);
}
uint64_t toMilliSeconds(void)
{
uint64_t ms = (ts.tv_sec * 1000) + (ts.tv_nsec / 1000000 );
return ms;
}
uint64_t toCounts(void)
{
return (ts.tv_sec * 1000000000) + ts.tv_nsec;
@ -133,9 +212,12 @@ namespace FCEU
{
return 1000000000;
}
struct timespec toTimeSpec(void)
{
return ts;
}
#else // WIN32
uint64_t ts;
uint64_t tsc;
timeStampRecord(void)
{
@ -176,15 +258,43 @@ namespace FCEU
return res;
}
timeStampRecord operator * (const unsigned int multiplier)
{
timeStampRecord res;
res.ts = ts * multiplier;
res.tsc = tsc * multiplier;
return res;
}
timeStampRecord operator / (const unsigned int divisor)
{
timeStampRecord res;
res.ts = ts / divisor;
res.tsc = tsc / divisor;
return res;
}
bool operator > (const timeStampRecord& op)
{
return ts > op.ts;
}
bool operator >= (const timeStampRecord& op)
{
return ts >= op.ts;
}
bool operator < (const timeStampRecord& op)
{
return ts < op.ts;
}
bool operator <= (const timeStampRecord& op)
{
return ts <= op.ts;
}
void zero(void)
{
@ -192,18 +302,41 @@ namespace FCEU
tsc = 0;
}
bool isZero(void)
{
return (ts == 0);
}
void fromSeconds(unsigned int sec)
{
ts = sec * qpcFreq;
tsc = 0;
}
void fromSeconds(double sec)
{
ts = static_cast<uint64_t>(sec * static_cast<double>(qpcFreq));
tsc = 0;
}
double toSeconds(void)
{
double sec = static_cast<double>(ts) / static_cast<double>(qpcFreq);
return sec;
}
void fromMilliSeconds(uint64_t ms)
{
ts = (ms * qpcFreq) / 1000;
}
uint64_t toMilliSeconds(void)
{
uint64_t ms = (ts * 1000) / qpcFreq;
return ms;
}
uint64_t toCounts(void)
{
return ts;
@ -213,19 +346,33 @@ namespace FCEU
{
return qpcFreq;
}
static uint64_t qpcFreq;
#endif
static uint64_t tscFreq;
uint64_t getTSC(void){ return tsc; };
static uint64_t tscFreq(void)
{
return _tscFreq;
}
static bool tscValid(void){ return tscFreq != 0; };
// Call this function to calibrate the estimated TSC frequency
static void tscCalibrate(int numSamples = 0);
void readNew(void);
private:
#if defined(__linux__) || defined(__APPLE__) || defined(__unix__)
struct timespec ts;
#else // Win32
uint64_t ts;
static uint64_t qpcFreq;
#endif
uint64_t tsc;
static uint64_t _tscFreq;
};
bool timeStampModuleInitialized(void);
// Call this function to calibrate the estimated TSC frequency
void timeStampModuleCalibrate(int numSamples = 1);
} // namespace FCEU