mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-02 09:52:27 -04:00
SpeedGear defeating code
This commit is contained in:
parent
ddfd490c86
commit
9a58c40a83
@ -33,6 +33,7 @@
|
|||||||
#include "cullFaceAttrib.h"
|
#include "cullFaceAttrib.h"
|
||||||
#include "string_utils.h"
|
#include "string_utils.h"
|
||||||
#include "geomCacheManager.h"
|
#include "geomCacheManager.h"
|
||||||
|
#include "throw_event.h"
|
||||||
|
|
||||||
#if defined(WIN32)
|
#if defined(WIN32)
|
||||||
#define WINDOWS_LEAN_AND_MEAN
|
#define WINDOWS_LEAN_AND_MEAN
|
||||||
@ -499,7 +500,11 @@ render_frame() {
|
|||||||
|
|
||||||
// Now cycle the pipeline and officially begin the next frame.
|
// Now cycle the pipeline and officially begin the next frame.
|
||||||
_pipeline->cycle();
|
_pipeline->cycle();
|
||||||
ClockObject::get_global_clock()->tick();
|
ClockObject *global_clock = ClockObject::get_global_clock();
|
||||||
|
global_clock->tick();
|
||||||
|
if (global_clock->check_errors()) {
|
||||||
|
throw_event("clock_error");
|
||||||
|
}
|
||||||
PStatClient::main_tick();
|
PStatClient::main_tick();
|
||||||
|
|
||||||
// Reset our pcollectors that track data across the frame.
|
// Reset our pcollectors that track data across the frame.
|
||||||
|
@ -289,6 +289,23 @@ get_average_frame_rate() const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: ClockObject::check_errors
|
||||||
|
// Access: Published
|
||||||
|
// Description: Returns true if a clock error was detected since the
|
||||||
|
// last time check_errors() was called. A clock error
|
||||||
|
// means that something happened, an OS or BIOS bug, for
|
||||||
|
// instance, that makes the current value of the clock
|
||||||
|
// somewhat suspect, and an application may wish to
|
||||||
|
// resynchronize with any external clocks.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
INLINE bool ClockObject::
|
||||||
|
check_errors() {
|
||||||
|
int orig_error_count = _error_count;
|
||||||
|
_error_count = _true_clock->get_error_count();
|
||||||
|
return (_error_count != orig_error_count);
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: ClockObject::get_global_clock
|
// Function: ClockObject::get_global_clock
|
||||||
// Access: Published
|
// Access: Published
|
||||||
|
@ -54,6 +54,8 @@ ClockObject() {
|
|||||||
_max_dt = max_dt;
|
_max_dt = max_dt;
|
||||||
_degrade_factor = clock_degrade_factor;
|
_degrade_factor = clock_degrade_factor;
|
||||||
_average_frame_rate_interval = average_frame_rate_interval;
|
_average_frame_rate_interval = average_frame_rate_interval;
|
||||||
|
|
||||||
|
_error_count = _true_clock->get_error_count();
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
@ -260,14 +262,6 @@ make_global_clock() {
|
|||||||
_global_clock->set_mode(clock_mode);
|
_global_clock->set_mode(clock_mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
// Function: get_time_of_day
|
|
||||||
// Description:
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
void get_time_of_day(TimeVal &tv) {
|
|
||||||
get_true_time_of_day(tv.tv[0], tv.tv[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: ClockObject::Mode ostream operator
|
// Function: ClockObject::Mode ostream operator
|
||||||
// Description:
|
// Description:
|
||||||
|
@ -32,12 +32,6 @@ PUBLISHED:
|
|||||||
ulong tv[2];
|
ulong tv[2];
|
||||||
};
|
};
|
||||||
|
|
||||||
BEGIN_PUBLISH
|
|
||||||
|
|
||||||
EXPCL_PANDAEXPRESS void get_time_of_day(TimeVal &tv);
|
|
||||||
|
|
||||||
END_PUBLISH
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Class : ClockObject
|
// Class : ClockObject
|
||||||
// Description : A ClockObject keeps track of elapsed real time and
|
// Description : A ClockObject keeps track of elapsed real time and
|
||||||
@ -107,6 +101,8 @@ PUBLISHED:
|
|||||||
void tick();
|
void tick();
|
||||||
void sync_frame_time();
|
void sync_frame_time();
|
||||||
|
|
||||||
|
INLINE bool check_errors();
|
||||||
|
|
||||||
INLINE static ClockObject *get_global_clock();
|
INLINE static ClockObject *get_global_clock();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -123,6 +119,7 @@ private:
|
|||||||
double _dt;
|
double _dt;
|
||||||
double _max_dt;
|
double _max_dt;
|
||||||
double _degrade_factor;
|
double _degrade_factor;
|
||||||
|
int _error_count;
|
||||||
|
|
||||||
// For tracking the average frame rate over a certain interval of
|
// For tracking the average frame rate over a certain interval of
|
||||||
// time.
|
// time.
|
||||||
|
@ -30,6 +30,22 @@ get_ptr() {
|
|||||||
return _global_ptr;
|
return _global_ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: TrueClock::get_error_count
|
||||||
|
// Access: Public
|
||||||
|
// Description: Returns the number of clock errors that have
|
||||||
|
// been detected. Each time a clock error is detected,
|
||||||
|
// in which the value returned by either of the above
|
||||||
|
// methods is suspect, the value returned by this method
|
||||||
|
// will be incremented. Applications can monitor this
|
||||||
|
// value and react, for instance, by resynchronizing
|
||||||
|
// their clocks each time this value changes.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
INLINE int TrueClock::
|
||||||
|
get_error_count() const {
|
||||||
|
return _error_count;
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Function: TrueClock::Destructor
|
// Function: TrueClock::Destructor
|
||||||
// Access: Protected
|
// Access: Protected
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
TrueClock *TrueClock::_global_ptr = NULL;
|
TrueClock *TrueClock::_global_ptr = NULL;
|
||||||
|
|
||||||
#if defined(WIN32_VC)
|
#ifdef WIN32_VC
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
@ -34,96 +34,153 @@ TrueClock *TrueClock::_global_ptr = NULL;
|
|||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#include <sys/timeb.h>
|
#include <sys/timeb.h>
|
||||||
|
|
||||||
#define WINDOWS_LEAN_AND_MEAN
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#undef WINDOWS_LEAN_AND_MEAN
|
|
||||||
|
|
||||||
static BOOL _has_high_res;
|
static const double _0001 = 1.0 / 1000.0;
|
||||||
static PN_int64 _frequency;
|
static const double _00000001 = 1.0 / 10000000.0;
|
||||||
static PN_int64 _init_count;
|
|
||||||
static double _fFrequency,_recip_fFrequency;
|
|
||||||
static DWORD _init_tc;
|
|
||||||
static bool _paranoid_clock;
|
|
||||||
static const double _0001 = 1.0/1000.0;
|
|
||||||
|
|
||||||
void get_true_time_of_day(ulong &sec, ulong &usec) {
|
// This is the interval of time, in seconds, over which to measure the
|
||||||
struct timeb tb;
|
// high-precision clock rate vs. the time-of-day rate, when
|
||||||
ftime(&tb);
|
// paranoid-clock is in effect. Reducing it makes the clock respond
|
||||||
sec = tb.time;
|
// more quickly to changes in rate, but setting it too small may
|
||||||
usec = (ulong)(tb.millitm * 1000.0);
|
// introduce erratic behavior.
|
||||||
}
|
static const double paranoid_clock_interval = 1.0;
|
||||||
|
|
||||||
|
// It will be considered a clock jump error if either the
|
||||||
|
// high-precision clock or the time-of-day clock change by this number
|
||||||
|
// of seconds without the other jumping by a similar amount.
|
||||||
|
static const double paranoid_clock_jump_error = 2.0;
|
||||||
|
|
||||||
|
// If the we detect a clock jump error but the corrected clock skew is
|
||||||
|
// currently more than this amount, we hack the clock scale to try to
|
||||||
|
// compensate.
|
||||||
|
static const double paranoid_clock_jump_error_max_delta = 1.0;
|
||||||
|
|
||||||
|
// If the measured time_scale appears to change by more than this
|
||||||
|
// factor, it will be reported to the log. Changes to time_scale less
|
||||||
|
// than this factor are assumed to be within the margin of error.
|
||||||
|
static const double paranoid_clock_report_scale_factor = 0.1;
|
||||||
|
|
||||||
|
// If the high-precision clock, after applying time_scale correction,
|
||||||
|
// is still more than this number of seconds above or below the
|
||||||
|
// time-of-day clock, it will be sped up or slowed down slightly until
|
||||||
|
// it is back in sync.
|
||||||
|
static const double paranoid_clock_chase_threshold = 0.5;
|
||||||
|
|
||||||
|
// This is the minimum factor by which the high-precision clock will
|
||||||
|
// be sped up or slowed down when it gets out of sync by
|
||||||
|
// paranoid-clock-chase-threshold.
|
||||||
|
static const double paranoid_clock_chase_factor = 0.1;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: TrueClock::get_long_time, Win32 implementation
|
||||||
|
// Access: Published
|
||||||
|
// Description:
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
double TrueClock::
|
double TrueClock::
|
||||||
get_long_time() const {
|
get_long_time() {
|
||||||
DWORD tc = GetTickCount();
|
int tc = GetTickCount();
|
||||||
return (double)(tc - _init_tc) * _0001;
|
return (double)(tc - _init_tc) * _0001;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: TrueClock::get_short_time, Win32 implementation
|
||||||
|
// Access: Published
|
||||||
|
// Description:
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
double TrueClock::
|
double TrueClock::
|
||||||
get_short_time() const {
|
get_short_time() {
|
||||||
|
double time;
|
||||||
|
|
||||||
if (_has_high_res) {
|
if (_has_high_res) {
|
||||||
LARGE_INTEGER count;
|
// Use the high-resolution clock. This is of questionable value,
|
||||||
QueryPerformanceCounter(&count);
|
// since (a) on some OS's and hardware, the low 24 bits can
|
||||||
|
// occasionally roll over without setting the carry bit, causing
|
||||||
|
// the time to jump backwards, and (b) reportedly it can set the
|
||||||
|
// carry bit incorrectly sometimes, causing the time to jump
|
||||||
|
// forwards, and (c) even when it doesn't do that, it's not very
|
||||||
|
// accurate and seems to lose seconds of time per hour, and (d)
|
||||||
|
// someone could be running a program such as Speed Gear which
|
||||||
|
// munges this value anyway.
|
||||||
|
PN_int64 count;
|
||||||
|
QueryPerformanceCounter((LARGE_INTEGER *)&count);
|
||||||
|
|
||||||
double time = (double)(count.QuadPart - _init_count) * _recip_fFrequency;
|
time = (double)(count - _init_count) * _recip_frequency;
|
||||||
|
|
||||||
if (_paranoid_clock) {
|
|
||||||
// Now double-check the high-resolution clock against the system
|
|
||||||
// clock.
|
|
||||||
DWORD tc = GetTickCount();
|
|
||||||
double sys_time = (double)(tc - _init_tc) * _0001;
|
|
||||||
if (fabs(time - sys_time) > 0.5) {
|
|
||||||
// Too much variance!
|
|
||||||
express_cat.info()
|
|
||||||
<< "Clock error! High resolution clock reads " << time
|
|
||||||
<< " while system clock reads " << sys_time << ".\n";
|
|
||||||
_init_count =
|
|
||||||
(PN_int64)(count.QuadPart - sys_time * _fFrequency);
|
|
||||||
time = (double)(count.QuadPart - _init_count) * _recip_fFrequency;
|
|
||||||
express_cat.info()
|
|
||||||
<< "High resolution clock reset to " << time << ".\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return time;
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// No high-resolution clock; return the best information we have.
|
// No high-resolution clock; return the best information we have.
|
||||||
DWORD tc = GetTickCount();
|
// This doesn't suffer from the rollover problems that
|
||||||
return (double)(tc - _init_tc) * _0001;
|
// QueryPerformanceCounter does, but it's not very precise--only
|
||||||
|
// precise to 50ms on Win98, and 10ms on XP-based systems--and
|
||||||
|
// Speed Gear still munges it.
|
||||||
|
int tc = GetTickCount();
|
||||||
|
time = (double)(tc - _init_tc) * _0001;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_paranoid_clock) {
|
||||||
|
// Check for rollforwards, rollbacks, and compensate for Speed
|
||||||
|
// Gear type programs by verifying against the time of day clock.
|
||||||
|
time = correct_time(time);
|
||||||
|
}
|
||||||
|
|
||||||
|
return time;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: TrueClock::Constructor, Win32 implementation
|
||||||
|
// Access: Protected
|
||||||
|
// Description:
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
TrueClock::
|
TrueClock::
|
||||||
TrueClock() {
|
TrueClock() {
|
||||||
|
_error_count = 0;
|
||||||
_has_high_res = false;
|
_has_high_res = false;
|
||||||
|
|
||||||
|
_time_scale = 1.0;
|
||||||
|
_time_offset = 0.0;
|
||||||
|
_tod_offset = 0.0;
|
||||||
|
_time_scale_changed = false;
|
||||||
|
_last_reported_time_scale = 1.0;
|
||||||
|
_report_time_scale_time = 0.0;
|
||||||
|
|
||||||
if (get_use_high_res_clock()) {
|
if (get_use_high_res_clock()) {
|
||||||
_has_high_res = QueryPerformanceFrequency((LARGE_INTEGER *)&_frequency);
|
PN_int64 int_frequency;
|
||||||
_fFrequency = (double) _frequency;
|
_has_high_res =
|
||||||
_recip_fFrequency = 1.0/_fFrequency;
|
(QueryPerformanceFrequency((LARGE_INTEGER *)&int_frequency) != 0);
|
||||||
}
|
|
||||||
|
|
||||||
_paranoid_clock = get_paranoid_clock();
|
|
||||||
|
|
||||||
if (_has_high_res) {
|
if (_has_high_res) {
|
||||||
LARGE_INTEGER count;
|
if (int_frequency <= 0) {
|
||||||
QueryPerformanceCounter(&count);
|
|
||||||
_init_count = count.QuadPart;
|
|
||||||
|
|
||||||
if (_frequency <= 0) {
|
|
||||||
express_cat.error()
|
express_cat.error()
|
||||||
<< "TrueClock::get_real_time() - frequency is negative!" << endl;
|
<< "TrueClock::get_real_time() - frequency is negative!" << endl;
|
||||||
_has_high_res = false;
|
_has_high_res = false;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
_frequency = (double)int_frequency;
|
||||||
|
_recip_frequency = 1.0 / _frequency;
|
||||||
|
|
||||||
|
QueryPerformanceCounter((LARGE_INTEGER *)&_init_count);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also store the initial tick count. We'll need this for
|
// Also store the initial tick count. We'll need this for
|
||||||
// get_long_time(), as well as for get_short_time() if we're not
|
// get_long_time(), as well as for get_short_time() if we're not
|
||||||
// using the high resolution clock, or to cross-check the high
|
// using the high resolution clock.
|
||||||
// resolution clock if we are using it.
|
|
||||||
_init_tc = GetTickCount();
|
_init_tc = GetTickCount();
|
||||||
|
|
||||||
|
// And we will need the current time of day to cross-check either of
|
||||||
|
// the above clocks if paranoid-clock is enabled.
|
||||||
|
GetSystemTimeAsFileTime((FILETIME *)&_init_tod);
|
||||||
|
|
||||||
|
_paranoid_clock = get_paranoid_clock();
|
||||||
|
_chase_clock = CC_keep_even;
|
||||||
|
|
||||||
|
if (_paranoid_clock) {
|
||||||
|
// If we'll be cross-checking the clock, we'd better start out
|
||||||
|
// with at least one timestamp, so we'll know if the clock jumps
|
||||||
|
// just after startup.
|
||||||
|
_timestamps.push_back(Timestamp(0.0, 0.0));
|
||||||
|
}
|
||||||
|
|
||||||
if (!_has_high_res) {
|
if (!_has_high_res) {
|
||||||
express_cat.warning()
|
express_cat.warning()
|
||||||
<< "No high resolution clock available." << endl;
|
<< "No high resolution clock available." << endl;
|
||||||
@ -134,74 +191,287 @@ TrueClock() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#elif defined(PENV_PS2)
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: TrueClock::correct_time, Win32 implementation
|
||||||
|
// Access: Protected
|
||||||
|
// Description: Ensures that the reported timestamp from the
|
||||||
|
// high-precision (or even the low-precision) clock is
|
||||||
|
// valid by verifying against the time-of-day clock.
|
||||||
//
|
//
|
||||||
// The PS2 implementation.
|
// This attempts to detect sudden jumps in time that
|
||||||
|
// might be caused by a failure of the high-precision
|
||||||
|
// clock to roll over properly.
|
||||||
//
|
//
|
||||||
|
// It also corrects for long-term skew of the clock by
|
||||||
|
// measuring the timing discrepency against the wall
|
||||||
|
// clock and projecting that discrepency into the
|
||||||
|
// future. This also should defeat programs such as
|
||||||
|
// Speed Gear that work by munging the value returned by
|
||||||
|
// QueryPerformanceCounter() and GetTickCount(), but not
|
||||||
|
// the wall clock time.
|
||||||
|
//
|
||||||
|
// However, relying on wall clock time presents its own
|
||||||
|
// set of problems, since the time of day might be
|
||||||
|
// adjusted slightly forward or back from time to time
|
||||||
|
// in response to ntp messages, or it might even be
|
||||||
|
// suddenly reset at any time by the user. So we do the
|
||||||
|
// best we can.
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
|
double TrueClock::
|
||||||
|
correct_time(double time) {
|
||||||
|
// First, get the current time of day measurement.
|
||||||
|
PN_uint64 int_tod;
|
||||||
|
GetSystemTimeAsFileTime((FILETIME *)&int_tod);
|
||||||
|
double tod = (double)(int_tod - _init_tod) * _00000001;
|
||||||
|
|
||||||
|
nassertr(!_timestamps.empty(), time);
|
||||||
|
|
||||||
#include <eeregs.h>
|
// Make sure we didn't experience a sudden jump from the last
|
||||||
#include <eekernel.h>
|
// measurement.
|
||||||
|
double time_delta = (time - _timestamps.back()._time) * _time_scale;
|
||||||
|
double tod_delta = (tod - _timestamps.back()._tod);
|
||||||
|
|
||||||
static unsigned int _msec;
|
if (time_delta < 0.0 ||
|
||||||
static unsigned int _sec;
|
fabs(time_delta - tod_delta) > paranoid_clock_jump_error) {
|
||||||
|
// A step backward in the high-precision clock, or more than a
|
||||||
|
// small jump on only one of the clocks, is cause for alarm.
|
||||||
|
|
||||||
// PS2 timer interrupt, as the RTC routines don't exist unless you're
|
express_cat.warning()
|
||||||
// using the .irx iop compiler, which scares us. A lot.
|
<< "Clock error detected; elapsed time " << time_delta
|
||||||
static int
|
<< "s on high-resolution counter, and " << tod_delta
|
||||||
timer_handler(int) {
|
<< "s on time-of-day clock.\n";
|
||||||
_msec++;
|
++_error_count;
|
||||||
|
|
||||||
if (_msec >= 1000) {
|
// If both are negative, we call it 0. If one is negative, we
|
||||||
_msec = 0;
|
// trust the other one. If both are nonnegative, we trust the
|
||||||
_sec++;
|
// smaller of the two.
|
||||||
|
double time_adjust = 0.0;
|
||||||
|
double tod_adjust = 0.0;
|
||||||
|
|
||||||
|
if (time_delta < 0.0 && tod < 0.0) {
|
||||||
|
// Trust neither.
|
||||||
|
time_adjust = -time_delta;
|
||||||
|
tod_adjust = -tod_delta;
|
||||||
|
|
||||||
|
} else if (time_delta < 0.0 || (tod_delta >= 0.0 && tod_delta < time_delta)) {
|
||||||
|
// Trust tod.
|
||||||
|
time_adjust = (tod_delta - time_delta);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Trust time.
|
||||||
|
tod_adjust = (time_delta - tod_delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
_time_offset += time_adjust;
|
||||||
|
time_delta += time_adjust;
|
||||||
|
_tod_offset += tod_adjust;
|
||||||
|
tod_delta += tod_adjust;
|
||||||
|
|
||||||
|
// Apply the adjustments to the timestamp queue. We could just
|
||||||
|
// completely empty the timestamp queue, but that makes it hard to
|
||||||
|
// catch up if we are getting lots of these "momentary" errors in
|
||||||
|
// a row.
|
||||||
|
Timestamps::iterator ti;
|
||||||
|
for (ti = _timestamps.begin(); ti != _timestamps.end(); ++ti) {
|
||||||
|
(*ti)._time -= time_adjust;
|
||||||
|
(*ti)._tod -= tod_adjust;
|
||||||
|
}
|
||||||
|
|
||||||
|
// And now we can record this timestamp, which is now consistent
|
||||||
|
// with the previous timestamps in the queue.
|
||||||
|
_timestamps.push_back(Timestamp(time, tod));
|
||||||
|
|
||||||
|
// Detecting and filtering this kind of momentary error can help
|
||||||
|
// protect us from legitimate problems cause by OS or BIOS bugs
|
||||||
|
// (which might introduce errors into the high precision clock),
|
||||||
|
// or from sudden changes to the time-of-day by the user, but we
|
||||||
|
// have to be careful because if the user uses a Speed Gear-type
|
||||||
|
// program to speed up the clock by an extreme amount, it can look
|
||||||
|
// like a lot of such "momentary" errors in a row--and if we throw
|
||||||
|
// them all out, we won't compute _time_scale correctly. To avoid
|
||||||
|
// this, we hack _time_scale here if we seem to be getting out of
|
||||||
|
// sync.
|
||||||
|
double corrected_time = time * _time_scale + _time_offset;
|
||||||
|
double corrected_tod = tod + _tod_offset;
|
||||||
|
if (corrected_time - corrected_tod > paranoid_clock_jump_error_max_delta) {
|
||||||
|
express_cat.info()
|
||||||
|
<< "Force-adjusting time_scale to catch up to errors.\n";
|
||||||
|
set_time_scale(time, _time_scale * 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (tod_delta < 0.0) {
|
||||||
|
// A small backwards jump on the time-of-day clock is not a
|
||||||
|
// concern, since this is technically allowed with ntp enabled.
|
||||||
|
// We simply ignore the event.
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Ok, we don't think there was a sudden jump, so carry on.
|
||||||
|
|
||||||
|
// The timestamp queue here records the measured timestamps over
|
||||||
|
// the past _priority_interval seconds. Its main purpose is to
|
||||||
|
// keep a running observation of _time_scale, so we can detect
|
||||||
|
// runtime changes of the clock's scale, for instance if the user
|
||||||
|
// is using a program like Speed Gear and pulls the slider during
|
||||||
|
// runtime.
|
||||||
|
|
||||||
|
// Consider the oldest timestamp in our queue.
|
||||||
|
Timestamp oldest = _timestamps.front();
|
||||||
|
double time_age = (time - oldest._time);
|
||||||
|
double tod_age = (tod - oldest._tod);
|
||||||
|
|
||||||
|
double keep_interval = paranoid_clock_interval;
|
||||||
|
|
||||||
|
if (tod_age > 0.0 && time_age > 0.0) {
|
||||||
|
// Adjust the _time_scale value to match the ratio between the
|
||||||
|
// elapsed time on the high-resolution clock, and the
|
||||||
|
// time-of-day clock.
|
||||||
|
double new_time_scale = tod_age / time_age;
|
||||||
|
|
||||||
|
// When we adjust _time_scale, we have to be careful to adjust
|
||||||
|
// _time_offset at the same time, so we don't introduce a
|
||||||
|
// sudden jump in time.
|
||||||
|
set_time_scale(time, new_time_scale);
|
||||||
|
|
||||||
|
// Check to see if the time scale has changed significantly
|
||||||
|
// since we last reported it.
|
||||||
|
double ratio = _time_scale / _last_reported_time_scale;
|
||||||
|
if (fabs(ratio - 1.0) > paranoid_clock_report_scale_factor) {
|
||||||
|
_time_scale_changed = true;
|
||||||
|
_last_reported_time_scale = _time_scale;
|
||||||
|
// Actually report it a little bit later, to give the time
|
||||||
|
// scale a chance to settle down.
|
||||||
|
_report_time_scale_time = tod + _tod_offset + keep_interval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean out old entries in the timestamps queue.
|
||||||
|
if (tod_age > keep_interval) {
|
||||||
|
while (!_timestamps.empty() &&
|
||||||
|
tod - _timestamps.front()._tod > keep_interval) {
|
||||||
|
_timestamps.pop_front();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Record this timestamp.
|
||||||
|
_timestamps.push_back(Timestamp(time, tod));
|
||||||
|
}
|
||||||
|
|
||||||
|
double corrected_time = time * _time_scale + _time_offset;
|
||||||
|
double corrected_tod = tod + _tod_offset;
|
||||||
|
|
||||||
|
if (_time_scale_changed && corrected_tod >= _report_time_scale_time) {
|
||||||
|
express_cat.info()
|
||||||
|
<< "Clock appears to be running at " << 100.0 / _time_scale
|
||||||
|
<< "% real time.\n";
|
||||||
|
_last_reported_time_scale = _time_scale;
|
||||||
|
_time_scale_changed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// By the time we get here, we have a corrected_time and a
|
||||||
|
// corrected_tod value, both of which should be advancing at about
|
||||||
|
// the same rate. However, there might be accumulated skew between
|
||||||
|
// them, since there is some lag in the above algorithm that
|
||||||
|
// corrects the _time_scale, and clock skew can accumulate while the
|
||||||
|
// algorithm is catching up.
|
||||||
|
|
||||||
|
// Therefore, we have one more line of defense: we check at this
|
||||||
|
// point for skew, and correct for it by slowing the clock down or
|
||||||
|
// speeding it up a bit as needed, until we even out the clocks
|
||||||
|
// again. Rather than adjusting the clock speed with _time_scale
|
||||||
|
// here, we simply slide _time_offset forward and back as
|
||||||
|
// needed--that way we don't interfere with the above algorithm,
|
||||||
|
// which is trying to compute _time_scale accurately.
|
||||||
|
|
||||||
|
switch (_chase_clock) {
|
||||||
|
case CC_slow_down:
|
||||||
|
if (corrected_time < corrected_tod) {
|
||||||
|
// We caught up.
|
||||||
|
_chase_clock = CC_keep_even;
|
||||||
|
if (express_cat.is_debug()) {
|
||||||
|
express_cat.debug()
|
||||||
|
<< "Clock back down to real time.\n";
|
||||||
|
// Let's report the clock error now, so an app can resync now
|
||||||
|
// that we're at a good time.
|
||||||
|
++_error_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Slow down the clock by sliding the offset a bit backward.
|
||||||
|
double fixup = 1.0 - (1.0 / (corrected_time - corrected_tod));
|
||||||
|
double correction = time_delta * max(fixup, paranoid_clock_chase_factor);
|
||||||
|
_time_offset -= correction;
|
||||||
|
corrected_time -= correction;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CC_keep_even:
|
||||||
|
if ((corrected_tod - corrected_time) > paranoid_clock_chase_threshold) {
|
||||||
|
// Oops, we're dropping behind; need to speed up.
|
||||||
|
_chase_clock = CC_speed_up;
|
||||||
|
|
||||||
|
if (express_cat.is_debug()) {
|
||||||
|
express_cat.debug()
|
||||||
|
<< "Clock is behind by " << (corrected_tod - corrected_time)
|
||||||
|
<< "s; speeding up to correct.\n";
|
||||||
|
}
|
||||||
|
} else if ((corrected_time - corrected_tod) > paranoid_clock_chase_threshold) {
|
||||||
|
// Oops, we're going too fast; need to slow down.
|
||||||
|
_chase_clock = CC_slow_down;
|
||||||
|
|
||||||
|
if (express_cat.is_debug()) {
|
||||||
|
express_cat.debug()
|
||||||
|
<< "Clock is ahead by " << (corrected_time - corrected_tod)
|
||||||
|
<< "s; slowing down to correct.\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CC_speed_up:
|
||||||
|
if (corrected_time > corrected_tod) {
|
||||||
|
// We caught up.
|
||||||
|
_chase_clock = CC_keep_even;
|
||||||
|
if (express_cat.is_debug()) {
|
||||||
|
express_cat.debug()
|
||||||
|
<< "Clock back up to real time.\n";
|
||||||
|
// Let's report the clock error now, so an app can resync now
|
||||||
|
// that we're at a good time.
|
||||||
|
++_error_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Speed up the clock by sliding the offset a bit forward.
|
||||||
|
double fixup = 1.0 - (1.0 / (corrected_tod - corrected_time));
|
||||||
|
double correction = time_delta * max(fixup, paranoid_clock_chase_factor);
|
||||||
|
_time_offset += correction;
|
||||||
|
corrected_time += correction;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (express_cat.is_spam()) {
|
||||||
|
express_cat.spam()
|
||||||
|
<< "time " << time << " tod " << corrected_tod
|
||||||
|
<< " corrected time " << corrected_time << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return corrected_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
void get_true_time_of_day(ulong &sec, ulong &msec) {
|
////////////////////////////////////////////////////////////////////
|
||||||
cerr << "get_true_time_of_day() not implemented!" << endl;
|
// Function: TrueClock::set_time_scale, Win32 implementation
|
||||||
|
// Access: Protected
|
||||||
|
// Description: Changes the _time_scale value, recomputing
|
||||||
|
// _time_offset at the same time so we don't introduce a
|
||||||
|
// sudden jump in time.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
void TrueClock::
|
||||||
|
set_time_scale(double time, double new_time_scale) {
|
||||||
|
nassertv(new_time_scale > 0.0);
|
||||||
|
_time_offset = time * _time_scale + _time_offset - (time * new_time_scale);
|
||||||
|
_time_scale = new_time_scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
double TrueClock::
|
#else // !WIN32_VC
|
||||||
get_long_time() const {
|
|
||||||
return (double) _sec + ((double) _msec / 1000.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
double TrueClock::
|
|
||||||
get_short_time() const {
|
|
||||||
return (double) _sec + ((double) _msec / 1000.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
TrueClock::
|
|
||||||
TrueClock() {
|
|
||||||
_init_sec = 0;
|
|
||||||
_msec = 0;
|
|
||||||
_sec = 0;
|
|
||||||
|
|
||||||
tT_MODE timer_mode;
|
|
||||||
*(unsigned int *) &timer_mode = 0;
|
|
||||||
|
|
||||||
timer_mode.cxxLKS = 1;
|
|
||||||
timer_mode.ZRET = 1;
|
|
||||||
timer_mode.cxxUE = 1;
|
|
||||||
timer_mode.cxxMPE = 1;
|
|
||||||
timer_mode.EQUF = 1;
|
|
||||||
|
|
||||||
*T0_COMP = 9375;
|
|
||||||
*T0_MODE = *(unsigned int *) &timer_mode;
|
|
||||||
|
|
||||||
EnableIntc(INTC_TIM0);
|
|
||||||
AddIntcHandler(INTC_TIM0, timer_handler, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#elif !defined(WIN32_VC)
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
@ -214,28 +484,13 @@ TrueClock() {
|
|||||||
|
|
||||||
static long _init_sec;
|
static long _init_sec;
|
||||||
|
|
||||||
void get_true_time_of_day(ulong &sec, ulong &msec) {
|
////////////////////////////////////////////////////////////////////
|
||||||
struct timeval tv;
|
// Function: TrueClock::get_long_time, Posix implementation
|
||||||
int result;
|
// Access: Published
|
||||||
|
// Description:
|
||||||
#ifdef GETTIMEOFDAY_ONE_PARAM
|
////////////////////////////////////////////////////////////////////
|
||||||
result = gettimeofday(&tv);
|
|
||||||
#else
|
|
||||||
result = gettimeofday(&tv, (struct timezone *)NULL);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (result < 0) {
|
|
||||||
sec = 0;
|
|
||||||
msec = 0;
|
|
||||||
// Error in gettimeofday().
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
sec = tv.tv_sec;
|
|
||||||
msec = tv.tv_usec;
|
|
||||||
}
|
|
||||||
|
|
||||||
double TrueClock::
|
double TrueClock::
|
||||||
get_long_time() const {
|
get_long_time() {
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
|
|
||||||
int result;
|
int result;
|
||||||
@ -258,8 +513,13 @@ get_long_time() const {
|
|||||||
return (double)(tv.tv_sec - _init_sec) + (double)tv.tv_usec / 1000000.0;
|
return (double)(tv.tv_sec - _init_sec) + (double)tv.tv_usec / 1000000.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: TrueClock::get_short_time, Posix implementation
|
||||||
|
// Access: Published
|
||||||
|
// Description:
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
double TrueClock::
|
double TrueClock::
|
||||||
get_short_time() const {
|
get_short_time() {
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
|
|
||||||
int result;
|
int result;
|
||||||
@ -282,8 +542,14 @@ get_short_time() const {
|
|||||||
return (double)(tv.tv_sec - _init_sec) + (double)tv.tv_usec / 1000000.0;
|
return (double)(tv.tv_sec - _init_sec) + (double)tv.tv_usec / 1000000.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: TrueClock::Constructor, Posix implementation
|
||||||
|
// Access: Protected
|
||||||
|
// Description:
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
TrueClock::
|
TrueClock::
|
||||||
TrueClock() {
|
TrueClock() {
|
||||||
|
_error_count = 0;
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
|
|
||||||
int result;
|
int result;
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#include "pandabase.h"
|
#include "pandabase.h"
|
||||||
#include "typedef.h"
|
#include "typedef.h"
|
||||||
|
#include "pdeque.h"
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Class : TrueClock
|
// Class : TrueClock
|
||||||
@ -44,22 +45,63 @@ public:
|
|||||||
// long interval. It may not be very precise for measuring short
|
// long interval. It may not be very precise for measuring short
|
||||||
// intervals, but it should not drift substantially over the long
|
// intervals, but it should not drift substantially over the long
|
||||||
// haul.
|
// haul.
|
||||||
double get_long_time() const;
|
double get_long_time();
|
||||||
|
|
||||||
// get_short_time() returns the most precise timer we have over a
|
// get_short_time() returns the most precise timer we have over a
|
||||||
// short interval. It may tend to drift over the long haul, but it
|
// short interval. It may tend to drift over the long haul, but it
|
||||||
// should have lots of digits to measure short intervals very
|
// should have lots of digits to measure short intervals very
|
||||||
// precisely.
|
// precisely.
|
||||||
double get_short_time() const;
|
double get_short_time();
|
||||||
|
|
||||||
|
INLINE int get_error_count() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
TrueClock();
|
TrueClock();
|
||||||
INLINE ~TrueClock();
|
INLINE ~TrueClock();
|
||||||
|
|
||||||
static TrueClock *_global_ptr;
|
int _error_count;
|
||||||
};
|
|
||||||
|
|
||||||
void get_true_time_of_day(ulong &sec, ulong &usec);
|
static TrueClock *_global_ptr;
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
double correct_time(double time);
|
||||||
|
void set_time_scale(double time, double new_time_scale);
|
||||||
|
|
||||||
|
bool _has_high_res;
|
||||||
|
PN_int64 _init_count;
|
||||||
|
double _frequency, _recip_frequency;
|
||||||
|
int _init_tc;
|
||||||
|
PN_uint64 _init_tod;
|
||||||
|
|
||||||
|
bool _paranoid_clock;
|
||||||
|
|
||||||
|
// The rest of the data structures in this block are strictly for
|
||||||
|
// implementing paranoid_clock: they are designed to allow us to
|
||||||
|
// cross-check the high-resolution clock against the time-of-day
|
||||||
|
// clock, and smoothly correct for deviations.
|
||||||
|
class Timestamp {
|
||||||
|
public:
|
||||||
|
Timestamp(double time, double tod) : _time(time), _tod(tod) { }
|
||||||
|
double _time;
|
||||||
|
double _tod;
|
||||||
|
};
|
||||||
|
typedef pdeque<Timestamp> Timestamps;
|
||||||
|
Timestamps _timestamps;
|
||||||
|
double _time_scale;
|
||||||
|
double _time_offset;
|
||||||
|
double _tod_offset;
|
||||||
|
int _num_jump_errors;
|
||||||
|
bool _time_scale_changed;
|
||||||
|
double _last_reported_time_scale;
|
||||||
|
double _report_time_scale_time;
|
||||||
|
enum ChaseClock {
|
||||||
|
CC_slow_down,
|
||||||
|
CC_keep_even,
|
||||||
|
CC_speed_up,
|
||||||
|
};
|
||||||
|
ChaseClock _chase_clock;
|
||||||
|
#endif // WIN32
|
||||||
|
};
|
||||||
|
|
||||||
#include "trueClock.I"
|
#include "trueClock.I"
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user