24 void (*ClockObject::_start_clock_wait)() = ClockObject::dummy_clock_wait;
25 void (*ClockObject::_start_clock_busy_wait)() = ClockObject::dummy_clock_wait;
26 void (*ClockObject::_stop_clock_wait)() = ClockObject::dummy_clock_wait;
28 AtomicAdjust::Pointer ClockObject::_global_clock =
nullptr;
35 ClockObject(Mode mode) : _ticks(get_class_type()), _mode(mode) {
38 _start_short_time = _true_clock->get_short_time();
39 _start_long_time = _true_clock->get_long_time();
40 _actual_frame_time = 0.0;
44 PRC_DESC(
"Sets a limit on the value returned by ClockObject::get_dt(). If "
45 "this value is less than zero, no limit is imposed; "
46 "otherwise, this is the maximum value that will ever "
47 "be returned by get_dt(), regardless of how much time "
48 "has actually elapsed between frames. See ClockObject::set_dt()."));
50 (
"clock-frame-rate", 1.0,
51 PRC_DESC(
"In non-real-time clock mode, sets the number of frames per "
52 "second that we should appear to be running. In forced "
53 "mode or limited mode, sets our target frame rate. In "
54 "normal mode, this has no effect. See ClockObject::set_frame_rate()."));
56 (
"clock-degrade-factor", 1.0,
57 PRC_DESC(
"In degrade clock mode, returns the ratio by which the "
58 "performance is degraded. A value of 2.0 causes the "
59 "clock to be slowed down by a factor of two (reducing "
60 "performance to 1/2 what would be otherwise). See ClockObject::set_degrade_factor()."));
62 (
"average-frame-rate-interval", 1.0,
63 PRC_DESC(
"See ClockObject::set_average_frame_rate_interval()."));
66 _user_frame_rate = clock_frame_rate;
67 _degrade_factor = clock_degrade_factor;
68 _average_frame_rate_interval = average_frame_rate_interval;
70 _error_count = _true_clock->get_error_count();
78 _true_clock(copy._true_clock),
80 _start_short_time(copy._start_short_time),
81 _start_long_time(copy._start_long_time),
82 _actual_frame_time(copy._actual_frame_time),
83 _max_dt(copy._max_dt),
84 _user_frame_rate(copy._user_frame_rate),
85 _degrade_factor(copy._degrade_factor),
86 _error_count(copy._error_count),
87 _average_frame_rate_interval(copy._average_frame_rate_interval),
136 CDWriter cdata(_cycler, current_thread);
143 case M_non_real_time:
145 cdata->_reported_frame_time_epoch = cdata->_reported_frame_time -
146 cdata->_frame_count / _user_frame_rate;
147 cdata->_dt = 1.0 / _user_frame_rate;
165 if (util_cat.is_debug() &&
this == _global_clock) {
167 <<
"Adjusting global clock's real time by " << time -
get_real_time()
170 #endif // NOTIFY_DEBUG
171 _start_short_time = _true_clock->get_short_time() - time;
172 _start_long_time = _true_clock->get_long_time() - time;
184 if (
this == _global_clock && _mode != M_slave) {
186 <<
"Adjusting global clock's frame time by " << time -
get_frame_time()
189 #endif // NOTIFY_DEBUG
190 CDWriter cdata(_cycler, current_thread);
191 _actual_frame_time = time;
192 cdata->_reported_frame_time = time;
195 cdata->_reported_frame_time_epoch = cdata->_reported_frame_time -
196 cdata->_frame_count / _user_frame_rate;
207 if (
this == _global_clock && _mode != M_slave) {
209 <<
"Adjusting global clock's frame count by "
212 #endif // NOTIFY_DEBUG
213 CDWriter cdata(_cycler, current_thread);
214 cdata->_frame_count = frame_count;
217 cdata->_reported_frame_time_epoch = cdata->_reported_frame_time -
218 cdata->_frame_count / _user_frame_rate;
231 if (_mode == M_slave) {
255 nassertv(frame_rate != 0.0);
260 CDWriter cdata(_cycler, current_thread);
261 _user_frame_rate = frame_rate;
264 case M_non_real_time:
266 cdata->_reported_frame_time_epoch = cdata->_reported_frame_time -
267 cdata->_frame_count / _user_frame_rate;
268 cdata->_dt = 1.0 / _user_frame_rate;
282 CDStageReader cdata(_cycler, 0, current_thread);
283 if (_ticks.size() <= 1) {
286 return _ticks.size() / (cdata->_reported_frame_time - _ticks.front());
296 CDStageReader cdata(_cycler, 0, current_thread);
297 double max_duration = 0.0;
298 double cur_duration = 0.0;
300 for (i = 0; i < _ticks.size() - 1; i++) {
301 cur_duration = _ticks[i + 1] - _ticks[i];
302 if (cur_duration > max_duration) {
303 max_duration = cur_duration;
323 if (_ticks.size() <= 1) {
326 double mean = (_ticks.back() - _ticks.front()) / (_ticks.size() - 1);
328 double sum_squares = 0.0;
329 for (i = 0; i < _ticks.size() - 1; ++i) {
330 double delta = _ticks[i + 1] - _ticks[i];
331 double diff = (delta - mean);
332 sum_squares += (diff * diff);
334 double deviation_2 = sum_squares / (_ticks.size() - 1);
335 return sqrt(deviation_2);
348 CDWriter cdata(_cycler, current_thread);
349 double old_reported_time = cdata->_reported_frame_time;
351 if (_mode != M_slave) {
352 double old_time = _actual_frame_time;
358 old_time = std::min(old_time, _actual_frame_time);
360 ++cdata->_frame_count;
365 cdata->_dt = _actual_frame_time - old_time;
366 cdata->_reported_frame_time = _actual_frame_time;
369 case M_non_real_time:
372 cdata->_reported_frame_time = cdata->_reported_frame_time_epoch +
373 cdata->_frame_count / _user_frame_rate;
379 double wait_until_time = old_time + 1.0 / _user_frame_rate;
380 wait_until(wait_until_time);
381 cdata->_dt = _actual_frame_time - old_time;
382 cdata->_reported_frame_time = std::max(_actual_frame_time, wait_until_time);
388 double dt = _actual_frame_time - old_time;
389 double target_dt = 1.0 / _user_frame_rate;
390 if (dt < target_dt) {
393 target_dt = target_dt / floor(target_dt / dt);
397 target_dt = target_dt * ceil(dt / target_dt);
399 double wait_until_time = old_time + target_dt;
400 wait_until(wait_until_time);
401 cdata->_dt = target_dt;
402 cdata->_reported_frame_time = wait_until_time;
406 case M_integer_limited:
408 double dt = _actual_frame_time - old_time;
409 double target_dt = 1.0 / _user_frame_rate;
410 if (dt < target_dt) {
417 target_dt = target_dt * ceil(dt / target_dt);
419 double wait_until_time = old_time + target_dt;
420 wait_until(wait_until_time);
421 cdata->_dt = target_dt;
422 cdata->_reported_frame_time = wait_until_time;
430 wait_until(old_time + 1.0 / _user_frame_rate);
431 cdata->_reported_frame_time = cdata->_reported_frame_time_epoch +
432 cdata->_frame_count / _user_frame_rate;
438 cdata->_dt = (_actual_frame_time - old_time) * _degrade_factor;
440 if (_degrade_factor < 1.0) {
443 cdata->_reported_frame_time += cdata->_dt;
448 wait_until(old_time + cdata->_dt);
449 cdata->_reported_frame_time = _actual_frame_time;
460 if (_average_frame_rate_interval > 0.0) {
461 _ticks.push_back(old_reported_time);
462 while (_ticks.size() > 2 &&
463 cdata->_reported_frame_time - _ticks.front() > _average_frame_rate_interval) {
480 if (_mode == M_normal) {
481 CDWriter cdata(_cycler, current_thread);
491 wait_until(
double want_time) {
492 if (want_time <= _actual_frame_time) {
497 (*_start_clock_wait)();
500 double wait_interval = (want_time - _actual_frame_time) - sleep_precision;
502 if (wait_interval > 0.0) {
507 (*_start_clock_busy_wait)();
511 while (_actual_frame_time < want_time) {
516 (*_stop_clock_wait)();
524 make_global_clock() {
525 nassertv(_global_clock ==
nullptr);
528 (
"clock-mode", ClockObject::M_normal,
529 PRC_DESC(
"Specifies the mode of the global clock. The default mode, normal, "
530 "is a real-time clock; other modes allow non-real-time special "
531 "effects like simulated reduced frame rate. See "
532 "ClockObject::set_mode()."));
558 _reported_frame_time = 0.0;
559 _reported_frame_time_epoch = 0.0;
568 return new CData(*
this);
575 operator << (ostream &out, ClockObject::Mode mode) {
577 case ClockObject::M_normal:
578 return out <<
"normal";
580 case ClockObject::M_non_real_time:
581 return out <<
"non-real-time";
583 case ClockObject::M_limited:
584 return out <<
"limited";
586 case ClockObject::M_integer:
587 return out <<
"integer";
589 case ClockObject::M_integer_limited:
590 return out <<
"integer_limited";
592 case ClockObject::M_forced:
593 return out <<
"forced";
595 case ClockObject::M_degrade:
596 return out <<
"degrade";
598 case ClockObject::M_slave:
599 return out <<
"slave";
602 return out <<
"**invalid ClockObject::Mode(" << (int)mode <<
")**";
609 operator >> (istream &in, ClockObject::Mode &mode) {
613 if (cmp_nocase_uh(word,
"normal") == 0) {
614 mode = ClockObject::M_normal;
615 }
else if (cmp_nocase_uh(word,
"non-real-time") == 0) {
616 mode = ClockObject::M_non_real_time;
617 }
else if (cmp_nocase_uh(word,
"limited") == 0) {
618 mode = ClockObject::M_limited;
619 }
else if (cmp_nocase_uh(word,
"integer") == 0) {
620 mode = ClockObject::M_integer;
621 }
else if (cmp_nocase_uh(word,
"integer_limited") == 0) {
622 mode = ClockObject::M_integer_limited;
623 }
else if (cmp_nocase_uh(word,
"forced") == 0) {
624 mode = ClockObject::M_forced;
625 }
else if (cmp_nocase_uh(word,
"degrade") == 0) {
626 mode = ClockObject::M_degrade;
627 }
else if (cmp_nocase_uh(word,
"slave") == 0) {
628 mode = ClockObject::M_slave;
631 <<
"Invalid ClockObject::Mode: " << word <<
"\n";
632 mode = ClockObject::M_normal;