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;
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is a convenience class to specialize ConfigVariable as a boolean type.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
An interface to whatever real-time clock we might have available in the current environment.