15 #include "clockObject.h" 16 #include "config_util.h" 17 #include "configVariableEnum.h" 18 #include "string_utils.h" 21 void (*ClockObject::_start_clock_wait)() = ClockObject::dummy_clock_wait;
22 void (*ClockObject::_start_clock_busy_wait)() = ClockObject::dummy_clock_wait;
23 void (*ClockObject::_stop_clock_wait)() = ClockObject::dummy_clock_wait;
34 ClockObject() : _ticks(get_class_type()) {
42 _start_short_time = _true_clock->get_short_time();
43 _start_long_time = _true_clock->get_long_time();
44 _actual_frame_time = 0.0;
48 PRC_DESC(
"Sets a limit on the value returned by ClockObject::get_dt(). If " 49 "this value is less than zero, no limit is imposed; " 50 "otherwise, this is the maximum value that will ever " 51 "be returned by get_dt(), regardless of how much time " 52 "has actually elapsed between frames. See ClockObject::set_dt()."));
54 (
"clock-frame-rate", 1.0,
55 PRC_DESC(
"In non-real-time clock mode, sets the number of frames per " 56 "second that we should appear to be running. In forced " 57 "mode or limited mode, sets our target frame rate. In " 58 "normal mode, this has no effect. See ClockObject::set_frame_rate()."));
60 (
"clock-degrade-factor", 1.0,
61 PRC_DESC(
"In degrade clock mode, returns the ratio by which the " 62 "performance is degraded. A value of 2.0 causes the " 63 "clock to be slowed down by a factor of two (reducing " 64 "performance to 1/2 what would be otherwise). See ClockObject::set_degrade_factor()."));
66 (
"average-frame-rate-interval", 1.0,
67 PRC_DESC(
"See ClockObject::set_average_frame_rate_interval()."));
70 _user_frame_rate = clock_frame_rate;
71 _degrade_factor = clock_degrade_factor;
72 _average_frame_rate_interval = average_frame_rate_interval;
84 _true_clock(copy._true_clock),
86 _start_short_time(copy._start_short_time),
87 _start_long_time(copy._start_long_time),
88 _actual_frame_time(copy._actual_frame_time),
89 _max_dt(copy._max_dt),
90 _user_frame_rate(copy._user_frame_rate),
91 _degrade_factor(copy._degrade_factor),
92 _error_count(copy._error_count),
93 _average_frame_rate_interval(copy._average_frame_rate_interval),
153 CDWriter cdata(_cycler, current_thread);
160 case M_non_real_time:
162 cdata->_reported_frame_time_epoch = cdata->_reported_frame_time -
163 cdata->_frame_count / _user_frame_rate;
164 cdata->_dt = 1.0 / _user_frame_rate;
187 if (util_cat.is_debug() &&
this == _global_clock) {
189 <<
"Adjusting global clock's real time by " << time -
get_real_time()
192 #endif // NOTIFY_DEBUG 193 _start_short_time = _true_clock->get_short_time() - time;
194 _start_long_time = _true_clock->get_long_time() - time;
209 if (
this == _global_clock && _mode != M_slave) {
211 <<
"Adjusting global clock's frame time by " << time -
get_frame_time()
214 #endif // NOTIFY_DEBUG 215 CDWriter cdata(_cycler, current_thread);
216 _actual_frame_time = time;
217 cdata->_reported_frame_time = time;
220 cdata->_reported_frame_time_epoch = cdata->_reported_frame_time -
221 cdata->_frame_count / _user_frame_rate;
235 if (
this == _global_clock && _mode != M_slave) {
237 <<
"Adjusting global clock's frame count by " 240 #endif // NOTIFY_DEBUG 241 CDWriter cdata(_cycler, current_thread);
242 cdata->_frame_count = frame_count;
245 cdata->_reported_frame_time_epoch = cdata->_reported_frame_time -
246 cdata->_frame_count / _user_frame_rate;
262 if (_mode == M_slave) {
290 nassertv(frame_rate != 0.0);
295 CDWriter cdata(_cycler, current_thread);
296 _user_frame_rate = frame_rate;
299 case M_non_real_time:
301 cdata->_reported_frame_time_epoch = cdata->_reported_frame_time -
302 cdata->_frame_count / _user_frame_rate;
303 cdata->_dt = 1.0 / _user_frame_rate;
322 if (_ticks.size() <= 1) {
325 return _ticks.size() / (cdata->_reported_frame_time - _ticks.front());
338 double max_duration = 0.0;
339 double cur_duration = 0.0;
341 for (i = 0; i < _ticks.size() - 1; i++) {
342 cur_duration = _ticks[i + 1] - _ticks[i];
343 if (cur_duration > max_duration) {
344 max_duration = cur_duration;
369 if (_ticks.size() <= 1) {
372 double mean = (_ticks.back() - _ticks.front()) / (_ticks.size() - 1);
374 double sum_squares = 0.0;
375 for (i = 0; i < _ticks.size() - 1; ++i) {
376 double delta = _ticks[i + 1] - _ticks[i];
377 double diff = (delta - mean);
378 sum_squares += (diff * diff);
380 double deviation_2 = sum_squares / (_ticks.size() - 1);
381 return sqrt(deviation_2);
398 CDWriter cdata(_cycler, current_thread);
399 double old_reported_time = cdata->_reported_frame_time;
401 if (_mode != M_slave) {
402 double old_time = _actual_frame_time;
409 old_time = min(old_time, _actual_frame_time);
411 ++cdata->_frame_count;
416 cdata->_dt = _actual_frame_time - old_time;
417 cdata->_reported_frame_time = _actual_frame_time;
420 case M_non_real_time:
423 cdata->_reported_frame_time = cdata->_reported_frame_time_epoch +
424 cdata->_frame_count / _user_frame_rate;
430 double wait_until_time = old_time + 1.0 / _user_frame_rate;
431 wait_until(wait_until_time);
432 cdata->_dt = _actual_frame_time - old_time;
433 cdata->_reported_frame_time = max(_actual_frame_time, wait_until_time);
439 double dt = _actual_frame_time - old_time;
440 double target_dt = 1.0 / _user_frame_rate;
441 if (dt < target_dt) {
444 target_dt = target_dt / floor(target_dt / dt);
448 target_dt = target_dt * ceil(dt / target_dt);
450 double wait_until_time = old_time + target_dt;
451 wait_until(wait_until_time);
452 cdata->_dt = target_dt;
453 cdata->_reported_frame_time = wait_until_time;
457 case M_integer_limited:
459 double dt = _actual_frame_time - old_time;
460 double target_dt = 1.0 / _user_frame_rate;
461 if (dt < target_dt) {
468 target_dt = target_dt * ceil(dt / target_dt);
470 double wait_until_time = old_time + target_dt;
471 wait_until(wait_until_time);
472 cdata->_dt = target_dt;
473 cdata->_reported_frame_time = wait_until_time;
481 wait_until(old_time + 1.0 / _user_frame_rate);
482 cdata->_reported_frame_time = cdata->_reported_frame_time_epoch +
483 cdata->_frame_count / _user_frame_rate;
489 cdata->_dt = (_actual_frame_time - old_time) * _degrade_factor;
491 if (_degrade_factor < 1.0) {
494 cdata->_reported_frame_time += cdata->_dt;
499 wait_until(old_time + cdata->_dt);
500 cdata->_reported_frame_time = _actual_frame_time;
511 if (_average_frame_rate_interval > 0.0) {
512 _ticks.push_back(old_reported_time);
513 while (_ticks.size() > 2 &&
514 cdata->_reported_frame_time - _ticks.front() > _average_frame_rate_interval) {
536 if (_mode == M_normal) {
537 CDWriter cdata(_cycler, current_thread);
550 wait_until(
double want_time) {
551 if (want_time <= _actual_frame_time) {
556 (*_start_clock_wait)();
559 double wait_interval = (want_time - _actual_frame_time) - sleep_precision;
561 if (wait_interval > 0.0) {
566 (*_start_clock_busy_wait)();
570 while (_actual_frame_time < want_time) {
575 (*_stop_clock_wait)();
586 make_global_clock() {
590 (
"clock-mode", ClockObject::M_normal,
591 PRC_DESC(
"Specifies the mode of the global clock. The default mode, normal, " 592 "is a real-time clock; other modes allow non-real-time special " 593 "effects like simulated reduced frame rate. See " 594 "ClockObject::set_mode()."));
597 _global_clock->
set_mode(clock_mode);
598 _global_clock->
ref();
620 _reported_frame_time = 0.0;
621 _reported_frame_time_epoch = 0.0;
632 return new CData(*
this);
640 operator << (ostream &out, ClockObject::Mode mode) {
642 case ClockObject::M_normal:
643 return out <<
"normal";
645 case ClockObject::M_non_real_time:
646 return out <<
"non-real-time";
648 case ClockObject::M_limited:
649 return out <<
"limited";
651 case ClockObject::M_integer:
652 return out <<
"integer";
654 case ClockObject::M_integer_limited:
655 return out <<
"integer_limited";
657 case ClockObject::M_forced:
658 return out <<
"forced";
660 case ClockObject::M_degrade:
661 return out <<
"degrade";
663 case ClockObject::M_slave:
664 return out <<
"slave";
667 return out <<
"**invalid ClockObject::Mode(" << (int)mode <<
")**";
675 operator >> (istream &in, ClockObject::Mode &mode) {
679 if (cmp_nocase_uh(word,
"normal") == 0) {
680 mode = ClockObject::M_normal;
681 }
else if (cmp_nocase_uh(word,
"non-real-time") == 0) {
682 mode = ClockObject::M_non_real_time;
683 }
else if (cmp_nocase_uh(word,
"limited") == 0) {
684 mode = ClockObject::M_limited;
685 }
else if (cmp_nocase_uh(word,
"integer") == 0) {
686 mode = ClockObject::M_integer;
687 }
else if (cmp_nocase_uh(word,
"integer_limited") == 0) {
688 mode = ClockObject::M_integer_limited;
689 }
else if (cmp_nocase_uh(word,
"forced") == 0) {
690 mode = ClockObject::M_forced;
691 }
else if (cmp_nocase_uh(word,
"degrade") == 0) {
692 mode = ClockObject::M_degrade;
693 }
else if (cmp_nocase_uh(word,
"slave") == 0) {
694 mode = ClockObject::M_slave;
697 <<
"Invalid ClockObject::Mode: " << word <<
"\n";
698 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.
void set_dt(double dt)
In non-real-time mode, sets the number of seconds that should appear to elapse between frames...
double get_max_frame_duration(Thread *current_thread=Thread::get_current_thread()) const
Returns the maximum frame duration over the last get_average_frame_rate_interval() seconds...
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...
int get_pipeline_stage() const
Returns the Pipeline stage number associated with this thread.
void set_frame_time(double time, Thread *current_thread=Thread::get_current_thread())
Changes the time as reported for the current frame to the indicated time.
A single page of data maintained by a PipelineCycler.
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...
double get_frame_time(Thread *current_thread=Thread::get_current_thread()) const
Returns the time in seconds as of the last time tick() was called (typically, this will be as of the ...
void sync_frame_time(Thread *current_thread=Thread::get_current_thread())
Resets the frame time to the current real time.
static Thread * 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.
void set_real_time(double time)
Resets the clock to the indicated time.
int get_frame_count(Thread *current_thread=Thread::get_current_thread()) const
Returns the number of times tick() has been called since the ClockObject was created, or since it was last reset.
double get_average_frame_rate(Thread *current_thread=Thread::get_current_thread()) const
Returns the average frame rate in number of frames per second over the last get_average_frame_rate_in...
A ClockObject keeps track of elapsed real time and discrete time.
double get_real_time() const
Returns the actual number of seconds elapsed since the ClockObject was created, or since it was last ...
This class specializes ConfigVariable as an enumerated type.
This template class calls PipelineCycler::write() in the constructor and PipelineCycler::release_writ...
static void sleep(double seconds)
Suspends the current thread for at least the indicated amount of time.
void ref() const
Explicitly increments the reference count.
void set_frame_count(int frame_count, Thread *current_thread=Thread::get_current_thread())
Resets the number of frames counted to the indicated number.
void set_mode(Mode mode)
Changes the mode of the clock.
This class is similar to CycleDataReader, except it allows reading from a particular stage of the pip...
A thread; that is, a lightweight process.
int get_error_count() const
Returns the number of clock errors that have been detected.
TypeHandle is the identifier used to differentiate C++ class types.