23 TrueClock *TrueClock::_global_ptr =
nullptr;
25 #if defined(WIN32_VC) || defined(WIN64_VC)
29 #include <sys/timeb.h>
30 #ifndef WIN32_LEAN_AND_MEAN
31 #define WIN32_LEAN_AND_MEAN 1
35 static const double _0001 = 1.0 / 1000.0;
36 static const double _00000001 = 1.0 / 10000000.0;
43 static const double paranoid_clock_interval = 3.0;
48 static const double paranoid_clock_jump_error = 2.0;
53 static const double paranoid_clock_jump_error_max_delta = 1.0;
58 static const double paranoid_clock_report_scale_factor = 0.1;
63 static const double paranoid_clock_chase_threshold = 0.5;
68 static const double paranoid_clock_chase_factor = 0.1;
75 int tc = GetTickCount();
76 return (
double)(tc - _init_tc) * _0001;
83 get_short_raw_time() {
97 QueryPerformanceCounter((LARGE_INTEGER *)&count);
99 time = (double)(count - _init_count) * _recip_frequency;
106 int tc = GetTickCount();
107 time = (double)(tc - _init_tc) * _0001;
116 typedef BOOL (WINAPI * PFNSETPROCESSAFFINITYMASK)(HANDLE, DWORD_PTR);
117 typedef BOOL (WINAPI * PFNGETPROCESSAFFINITYMASK)(HANDLE, DWORD_PTR*, DWORD_PTR*);
120 set_cpu_affinity(uint32_t mask)
const {
121 HMODULE hker = GetModuleHandle(
"kernel32");
123 PFNGETPROCESSAFFINITYMASK gp = (PFNGETPROCESSAFFINITYMASK)
124 GetProcAddress(hker,
"GetProcessAffinityMask");
125 PFNSETPROCESSAFFINITYMASK sp = (PFNSETPROCESSAFFINITYMASK)
126 GetProcAddress(hker,
"SetProcessAffinityMask");
127 if (gp != 0 && sp != 0) {
130 if (gp(GetCurrentProcess(), (PDWORD_PTR)&proc_mask, (PDWORD_PTR)&sys_mask)) {
132 proc_mask = mask & sys_mask;
134 return sp(GetCurrentProcess(), proc_mask) != 0;
148 _has_high_res =
false;
153 _time_scale_changed =
false;
154 _last_reported_time_scale = 1.0;
155 _report_time_scale_time = 0.0;
158 (
"lock-to-one-cpu",
false,
159 PRC_DESC(
"Set this to true if you want the entire process to use one "
160 "CPU, even on multi-core and multi-CPU workstations. This is "
161 "mainly a hack to solve a bug in which QueryPerformanceCounter "
162 "returns inconsistent results on multi-core machines. "));
164 if (lock_to_one_cpu) {
165 set_cpu_affinity(0x01);
168 if (get_use_high_res_clock()) {
169 int64_t int_frequency;
171 (QueryPerformanceFrequency((LARGE_INTEGER *)&int_frequency) != 0);
173 if (int_frequency <= 0) {
175 <<
"TrueClock::get_real_time() - frequency is negative!" << std::endl;
176 _has_high_res =
false;
179 _frequency = (double)int_frequency;
180 _recip_frequency = 1.0 / _frequency;
182 QueryPerformanceCounter((LARGE_INTEGER *)&_init_count);
190 _init_tc = GetTickCount();
194 GetSystemTimeAsFileTime((FILETIME *)&_init_tod);
196 _chase_clock = CC_keep_even;
200 _timestamps.push_back(Timestamp(0.0, 0.0));
202 if (!_has_high_res) {
204 <<
"No high resolution clock available." << std::endl;
227 correct_time(
double time) {
230 GetSystemTimeAsFileTime((FILETIME *)&int_tod);
231 double tod = (double)(int_tod - _init_tod) * _00000001;
233 nassertr(!_timestamps.empty(), time);
236 double time_delta = (time - _timestamps.back()._time) * _time_scale;
237 double tod_delta = (tod - _timestamps.back()._tod);
239 if (time_delta < -0.0001 ||
240 fabs(time_delta - tod_delta) > paranoid_clock_jump_error) {
247 <<
"Clock error detected; elapsed time " << time_delta
248 <<
"s on high-resolution counter, and " << tod_delta
249 <<
"s on time-of-day clock.\n";
255 double time_adjust = 0.0;
256 double tod_adjust = 0.0;
258 if (time_delta < 0.0 && tod < 0.0) {
260 time_adjust = -time_delta;
261 tod_adjust = -tod_delta;
263 }
else if (time_delta < 0.0 || (tod_delta >= 0.0 && tod_delta < time_delta)) {
265 double new_tod_delta = min(tod_delta, paranoid_clock_jump_error);
266 time_adjust = new_tod_delta - time_delta;
267 tod_adjust = new_tod_delta - tod_delta;
271 double new_time_delta = min(time_delta, paranoid_clock_jump_error);
272 time_adjust = new_time_delta - time_delta;
273 tod_adjust = new_time_delta - tod_delta;
276 _time_offset += time_adjust;
277 time_delta += time_adjust;
278 _tod_offset += tod_adjust;
279 tod_delta += tod_adjust;
284 Timestamps::iterator ti;
285 for (ti = _timestamps.begin(); ti != _timestamps.end(); ++ti) {
286 (*ti)._time -= time_adjust / _time_scale;
287 (*ti)._tod -= tod_adjust;
292 _timestamps.push_back(Timestamp(time, tod));
304 double corrected_time = time * _time_scale + _time_offset;
305 double corrected_tod = tod + _tod_offset;
306 if (corrected_time - corrected_tod > paranoid_clock_jump_error_max_delta &&
307 _time_scale > 0.00001) {
309 <<
"Force-adjusting time_scale to catch up to errors.\n";
310 set_time_scale(time, _time_scale * 0.5);
313 }
else if (tod_delta < 0.0) {
328 Timestamp oldest = _timestamps.front();
329 double time_age = (time - oldest._time);
330 double tod_age = (tod - oldest._tod);
332 double keep_interval = paranoid_clock_interval;
334 if (tod_age > keep_interval / 2.0 && time_age > 0.0) {
337 double new_time_scale = tod_age / time_age;
342 set_time_scale(time, new_time_scale);
346 double ratio = _time_scale / _last_reported_time_scale;
347 if (fabs(ratio - 1.0) > paranoid_clock_report_scale_factor) {
348 _time_scale_changed =
true;
349 _last_reported_time_scale = _time_scale;
352 _report_time_scale_time = tod + _tod_offset + keep_interval;
353 if (clock_cat.is_debug()) {
355 <<
"Will report time scale, now " << 100.0 / _time_scale
356 <<
"%, tod_age = " << tod_age <<
", time_age = " << time_age
357 <<
", ratio = " << ratio <<
"\n";
363 if (tod_age > keep_interval) {
364 while (!_timestamps.empty() &&
365 tod - _timestamps.front()._tod > keep_interval) {
366 _timestamps.pop_front();
371 _timestamps.push_back(Timestamp(time, tod));
374 double corrected_time = time * _time_scale + _time_offset;
375 double corrected_tod = tod + _tod_offset;
377 if (_time_scale_changed && corrected_tod >= _report_time_scale_time) {
378 double percent = 100.0 / _time_scale;
380 percent = floor(percent / 20.0 + 0.5) * 20.0;
382 <<
"Clock appears to be running at " << percent <<
"% real time.\n";
383 _last_reported_time_scale = _time_scale;
384 _time_scale_changed =
false;
400 switch (_chase_clock) {
402 if (corrected_time < corrected_tod) {
404 _chase_clock = CC_keep_even;
405 if (clock_cat.is_debug()) {
407 <<
"Clock back down to real time.\n";
415 double fixup = 1.0 - (1.0 / (corrected_time - corrected_tod));
416 double correction = time_delta * max(fixup, paranoid_clock_chase_factor);
417 _time_offset -= correction;
418 corrected_time -= correction;
423 if ((corrected_tod - corrected_time) > paranoid_clock_chase_threshold) {
425 _chase_clock = CC_speed_up;
427 if (clock_cat.is_debug()) {
429 <<
"Clock is behind by " << (corrected_tod - corrected_time)
430 <<
"s; speeding up to correct.\n";
432 }
else if ((corrected_time - corrected_tod) > paranoid_clock_chase_threshold) {
434 _chase_clock = CC_slow_down;
436 if (clock_cat.is_debug()) {
438 <<
"Clock is ahead by " << (corrected_time - corrected_tod)
439 <<
"s; slowing down to correct.\n";
445 if (corrected_time > corrected_tod) {
447 _chase_clock = CC_keep_even;
448 if (clock_cat.is_debug()) {
450 <<
"Clock back up to real time.\n";
458 double fixup = 1.0 - (1.0 / (corrected_tod - corrected_time));
459 double correction = time_delta * max(fixup, paranoid_clock_chase_factor);
460 _time_offset += correction;
461 corrected_time += correction;
466 if (clock_cat.is_spam()) {
468 <<
"time " << time <<
" tod " << corrected_tod
469 <<
" corrected time " << corrected_time <<
"\n";
472 return corrected_time;
480 set_time_scale(
double time,
double new_time_scale) {
481 nassertv(new_time_scale > 0.0);
482 _time_offset = time * _time_scale + _time_offset - (time * new_time_scale);
483 _time_scale = new_time_scale;
490 #include <sys/time.h>
493 static long _init_sec;
504 #ifdef GETTIMEOFDAY_ONE_PARAM
505 result = gettimeofday(&tv);
507 result = gettimeofday(&tv,
nullptr);
518 return (
double)(tv.tv_sec - _init_sec) + (
double)tv.tv_usec / 1000000.0;
525 get_short_raw_time() {
530 #ifdef GETTIMEOFDAY_ONE_PARAM
531 result = gettimeofday(&tv);
533 result = gettimeofday(&tv,
nullptr);
544 return (
double)(tv.tv_sec - _init_sec) + (
double)tv.tv_usec / 1000000.0;
551 set_cpu_affinity(uint32_t mask)
const {
564 #ifdef GETTIMEOFDAY_ONE_PARAM
565 result = gettimeofday(&tv);
567 result = gettimeofday(&tv,
nullptr);
571 perror(
"gettimeofday");
574 _init_sec = tv.tv_sec;
This is a convenience class to specialize ConfigVariable as a boolean type.
An interface to whatever real-time clock we might have available in the current environment.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.