23TrueClock *TrueClock::_global_ptr =
nullptr;
25#if defined(WIN32_VC) || defined(WIN64_VC)
30#ifndef WIN32_LEAN_AND_MEAN
31#define WIN32_LEAN_AND_MEAN 1
35static const double _0001 = 1.0 / 1000.0;
36static const double _00000001 = 1.0 / 10000000.0;
43static const double paranoid_clock_interval = 3.0;
48static const double paranoid_clock_jump_error = 2.0;
53static const double paranoid_clock_jump_error_max_delta = 1.0;
58static const double paranoid_clock_report_scale_factor = 0.1;
63static const double paranoid_clock_chase_threshold = 0.5;
68static const double paranoid_clock_chase_factor = 0.1;
75 int tc = GetTickCount();
76 return (
double)(tc - _init_tc) * _0001;
97 QueryPerformanceCounter((LARGE_INTEGER *)&count);
99 time = (double)(count - _init_count) * _recip_frequency;
106 int tc = GetTickCount();
107 time = (double)(tc - _init_tc) * _0001;
116typedef BOOL (WINAPI * PFNSETPROCESSAFFINITYMASK)(HANDLE, DWORD_PTR);
117typedef BOOL (WINAPI * PFNGETPROCESSAFFINITYMASK)(HANDLE, DWORD_PTR*, DWORD_PTR*);
120set_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;
227correct_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) {
246 if (clock_cat.is_debug()) {
248 <<
"Clock error detected; elapsed time " << time_delta
249 <<
"s on high-resolution counter, and " << tod_delta
250 <<
"s on time-of-day clock.\n";
257 double time_adjust = 0.0;
258 double tod_adjust = 0.0;
260 if (time_delta < 0.0 && tod < 0.0) {
262 time_adjust = -time_delta;
263 tod_adjust = -tod_delta;
265 }
else if (time_delta < 0.0 || (tod_delta >= 0.0 && tod_delta < time_delta)) {
267 double new_tod_delta = min(tod_delta, paranoid_clock_jump_error);
268 time_adjust = new_tod_delta - time_delta;
269 tod_adjust = new_tod_delta - tod_delta;
273 double new_time_delta = min(time_delta, paranoid_clock_jump_error);
274 time_adjust = new_time_delta - time_delta;
275 tod_adjust = new_time_delta - tod_delta;
278 _time_offset += time_adjust;
279 time_delta += time_adjust;
280 _tod_offset += tod_adjust;
281 tod_delta += tod_adjust;
286 Timestamps::iterator ti;
287 for (ti = _timestamps.begin(); ti != _timestamps.end(); ++ti) {
288 (*ti)._time -= time_adjust / _time_scale;
289 (*ti)._tod -= tod_adjust;
294 _timestamps.push_back(Timestamp(time, tod));
306 double corrected_time = time * _time_scale + _time_offset;
307 double corrected_tod = tod + _tod_offset;
308 if (corrected_time - corrected_tod > paranoid_clock_jump_error_max_delta &&
309 _time_scale > 0.00001) {
311 <<
"Force-adjusting time_scale to catch up to errors.\n";
312 set_time_scale(time, _time_scale * 0.5);
315 }
else if (tod_delta < 0.0) {
330 Timestamp oldest = _timestamps.front();
331 double time_age = (time - oldest._time);
332 double tod_age = (tod - oldest._tod);
334 double keep_interval = paranoid_clock_interval;
336 if (tod_age > keep_interval / 2.0 && time_age > 0.0) {
339 double new_time_scale = tod_age / time_age;
344 set_time_scale(time, new_time_scale);
348 double ratio = _time_scale / _last_reported_time_scale;
349 if (fabs(ratio - 1.0) > paranoid_clock_report_scale_factor) {
350 _time_scale_changed =
true;
351 _last_reported_time_scale = _time_scale;
354 _report_time_scale_time = tod + _tod_offset + keep_interval;
355 if (clock_cat.is_debug()) {
357 <<
"Will report time scale, now " << 100.0 / _time_scale
358 <<
"%, tod_age = " << tod_age <<
", time_age = " << time_age
359 <<
", ratio = " << ratio <<
"\n";
365 if (tod_age > keep_interval) {
366 while (!_timestamps.empty() &&
367 tod - _timestamps.front()._tod > keep_interval) {
368 _timestamps.pop_front();
373 _timestamps.push_back(Timestamp(time, tod));
376 double corrected_time = time * _time_scale + _time_offset;
377 double corrected_tod = tod + _tod_offset;
379 if (_time_scale_changed && corrected_tod >= _report_time_scale_time) {
380 double percent = 100.0 / _time_scale;
382 percent = floor(percent / 20.0 + 0.5) * 20.0;
384 <<
"Clock appears to be running at " << percent <<
"% real time.\n";
385 _last_reported_time_scale = _time_scale;
386 _time_scale_changed =
false;
402 switch (_chase_clock) {
404 if (corrected_time < corrected_tod) {
406 _chase_clock = CC_keep_even;
407 if (clock_cat.is_debug()) {
409 <<
"Clock back down to real time.\n";
417 double fixup = 1.0 - (1.0 / (corrected_time - corrected_tod));
418 double correction = time_delta * max(fixup, paranoid_clock_chase_factor);
419 _time_offset -= correction;
420 corrected_time -= correction;
425 if ((corrected_tod - corrected_time) > paranoid_clock_chase_threshold) {
427 _chase_clock = CC_speed_up;
429 if (clock_cat.is_debug()) {
431 <<
"Clock is behind by " << (corrected_tod - corrected_time)
432 <<
"s; speeding up to correct.\n";
434 }
else if ((corrected_time - corrected_tod) > paranoid_clock_chase_threshold) {
436 _chase_clock = CC_slow_down;
438 if (clock_cat.is_debug()) {
440 <<
"Clock is ahead by " << (corrected_time - corrected_tod)
441 <<
"s; slowing down to correct.\n";
447 if (corrected_time > corrected_tod) {
449 _chase_clock = CC_keep_even;
450 if (clock_cat.is_debug()) {
452 <<
"Clock back up to real time.\n";
460 double fixup = 1.0 - (1.0 / (corrected_tod - corrected_time));
461 double correction = time_delta * max(fixup, paranoid_clock_chase_factor);
462 _time_offset += correction;
463 corrected_time += correction;
468 if (clock_cat.is_spam()) {
470 <<
"time " << time <<
" tod " << corrected_tod
471 <<
" corrected time " << corrected_time <<
"\n";
474 return corrected_time;
482set_time_scale(
double time,
double new_time_scale) {
483 nassertv(new_time_scale > 0.0);
484 _time_offset = time * _time_scale + _time_offset - (time * new_time_scale);
485 _time_scale = new_time_scale;
495static long _init_sec;
506#ifdef GETTIMEOFDAY_ONE_PARAM
507 result = gettimeofday(&tv);
509 result = gettimeofday(&tv,
nullptr);
520 return (
double)(tv.tv_sec - _init_sec) + (
double)tv.tv_usec / 1000000.0;
527get_short_raw_time() {
532#ifdef GETTIMEOFDAY_ONE_PARAM
533 result = gettimeofday(&tv);
535 result = gettimeofday(&tv,
nullptr);
546 return (
double)(tv.tv_sec - _init_sec) + (
double)tv.tv_usec / 1000000.0;
553set_cpu_affinity(uint32_t mask)
const {
566#ifdef GETTIMEOFDAY_ONE_PARAM
567 result = gettimeofday(&tv);
569 result = gettimeofday(&tv,
nullptr);
573 perror(
"gettimeofday");
576 _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.