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) {
233 CDWriter cdata(_cycler, Thread::get_current_thread());
255 nassertv(frame_rate != 0.0);
257 Thread *current_thread = Thread::get_current_thread();
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;
void tick(Thread *current_thread=Thread::get_current_thread())
Instructs the clock that a new frame has just begun.
static TrueClock * get_global_ptr()
Returns a pointer to the one TrueClock object in the world.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
set_dt
In non-real-time mode, sets the number of seconds that should appear to elapse between frames.
get_average_frame_rate
Returns the average frame rate in number of frames per second over the last get_average_frame_rate_in...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
double calc_frame_rate_deviation(Thread *current_thread=Thread::get_current_thread()) const
Returns the standard deviation of the frame times of the frames rendered over the past get_average_fr...
A single page of data maintained by a PipelineCycler.
set_real_time
Resets the clock to the indicated time.
void set_frame_rate(double frame_rate)
In non-real-time mode, sets the number of frames per second that we should appear to be running.
void sync_frame_time(Thread *current_thread=Thread::get_current_thread())
Resets the frame time to the current real time.
get_pipeline_stage
Returns the Pipeline stage number associated with this thread.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_current_thread
Returns a pointer to the currently-executing Thread object.
This is a convenience class to specialize ConfigVariable as a floating- point type.
get_frame_time
Returns the time in seconds as of the last time tick() was called (typically, this will be as of the ...
get_frame_count
Returns the number of times tick() has been called since the ClockObject was created,...
A ClockObject keeps track of elapsed real time and discrete time.
This class specializes ConfigVariable as an enumerated type.
get_real_time
Returns the actual number of seconds elapsed since the ClockObject was created, or since it was last ...
This template class calls PipelineCycler::write() in the constructor and PipelineCycler::release_writ...
get_max_frame_duration
Returns the maximum frame duration over the last get_average_frame_rate_interval() seconds.
set_mode
Changes the mode of the clock.
static void sleep(double seconds)
Suspends the current thread for at least the indicated amount of time.
void local_object()
This function should be called, once, immediately after creating a new instance of some ReferenceCoun...
This class is similar to CycleDataReader, except it allows reading from a particular stage of the pip...
set_frame_count
Resets the number of frames counted to the indicated number.
A thread; that is, a lightweight process.
TypeHandle is the identifier used to differentiate C++ class types.
set_frame_time
Changes the time as reported for the current frame to the indicated time.
static Pointer compare_and_exchange_ptr(Pointer &mem, Pointer old_value, Pointer new_value)
Atomic compare and exchange.