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:
parent
6fb899e9a1
commit
1a3170fd68
|
@ -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 */
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue