Panda3D
clockObject.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file clockObject.cxx
10  * @author drose
11  * @date 2000-02-17
12  */
13 
14 #include "clockObject.h"
15 #include "config_putil.h"
16 #include "configVariableEnum.h"
17 #include "string_utils.h"
18 #include "thread.h"
19 
20 using std::istream;
21 using std::ostream;
22 using std::string;
23 
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;
27 
28 AtomicAdjust::Pointer ClockObject::_global_clock = nullptr;
29 TypeHandle ClockObject::_type_handle;
30 
31 /**
32  *
33  */
34 ClockObject::
35 ClockObject(Mode mode) : _ticks(get_class_type()), _mode(mode) {
36  _true_clock = TrueClock::get_global_ptr();
37 
38  _start_short_time = _true_clock->get_short_time();
39  _start_long_time = _true_clock->get_long_time();
40  _actual_frame_time = 0.0;
41 
43  ("max-dt", -1.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()."));
49  ConfigVariableDouble clock_frame_rate
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()."));
55  ConfigVariableDouble clock_degrade_factor
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()."));
61  ConfigVariableDouble average_frame_rate_interval
62  ("average-frame-rate-interval", 1.0,
63  PRC_DESC("See ClockObject::set_average_frame_rate_interval()."));
64 
65  _max_dt = max_dt;
66  _user_frame_rate = clock_frame_rate;
67  _degrade_factor = clock_degrade_factor;
68  _average_frame_rate_interval = average_frame_rate_interval;
69 
70  _error_count = _true_clock->get_error_count();
71 }
72 
73 /**
74  *
75  */
76 ClockObject::
77 ClockObject(const ClockObject &copy) :
78  _true_clock(copy._true_clock),
79  _mode(copy._mode),
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),
88  _ticks(copy._ticks),
89  _cycler(copy._cycler)
90 {
91 }
92 
93 /**
94  * Changes the mode of the clock. Normally, the clock is in mode M_normal.
95  * In this mode, each call to tick() will set the value returned by
96  * get_frame_time() to the current real time; thus, the clock simply reports
97  * time advancing.
98  *
99  * Other possible modes:
100  *
101  * M_non_real_time - the clock ignores real time completely; at each call to
102  * tick(), it pretends that exactly dt seconds have elapsed since the last
103  * call to tick(). You may set the value of dt with set_dt() or
104  * set_frame_rate().
105  *
106  * M_limited - the clock will run as fast as it can, as in M_normal, but will
107  * not run faster than the rate specified by set_frame_rate(). If the
108  * application would run faster than this rate, the clock will slow down the
109  * application.
110  *
111  * M_integer - the clock will run as fast as it can, but the rate will be
112  * constrained to be an integer multiple or divisor of the rate specified by
113  * set_frame_rate(). The clock will slow down the application a bit to
114  * guarantee this.
115  *
116  * M_integer_limited - a combination of M_limited and M_integer; the clock
117  * will not run faster than set_frame_rate(), and if it runs slower, it will
118  * run at a integer divisor of that rate.
119  *
120  * M_forced - the clock forces the application to run at the rate specified by
121  * set_frame_rate(). If the application would run faster than this rate, the
122  * clock will slow down the application; if the application would run slower
123  * than this rate, the clock slows down time so that the application believes
124  * it is running at the given rate.
125  *
126  * M_degrade - the clock runs at real time, but the application is slowed down
127  * by a set factor of its frame rate, specified by set_degrade_factor().
128  *
129  * M_slave - the clock does not advance, but relies on the user to call
130  * set_frame_time() and/or set_frame_count() each frame.
131  */
132 void ClockObject::
133 set_mode(ClockObject::Mode mode) {
134  Thread *current_thread = Thread::get_current_thread();
135  nassertv(current_thread->get_pipeline_stage() == 0);
136  CDWriter cdata(_cycler, current_thread);
137 
138  _mode = mode;
139 
140  // In case we have set the clock to one of the modes that uses
141  // _reported_frame_time_epoch, recompute the epoch.
142  switch (_mode) {
143  case M_non_real_time:
144  case M_forced:
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;
148 
149  default:
150  break;
151  }
152 }
153 
154 /**
155  * Resets the clock to the indicated time. This changes only the real time of
156  * the clock as reported by get_real_time(), but does not immediately change
157  * the time reported by get_frame_time()--that will change after the next call
158  * to tick(). Also see reset(), set_frame_time(), and set_frame_count().
159  */
160 void ClockObject::
161 set_real_time(double time) {
162 #ifdef NOTIFY_DEBUG
163  // This is only a debug message, since it happens during normal development,
164  // particularly at startup, or whenever you break into the task loop.
165  if (util_cat.is_debug() && this == _global_clock) {
166  util_cat.debug()
167  << "Adjusting global clock's real time by " << time - get_real_time()
168  << " seconds.\n";
169  }
170 #endif // NOTIFY_DEBUG
171  _start_short_time = _true_clock->get_short_time() - time;
172  _start_long_time = _true_clock->get_long_time() - time;
173 }
174 
175 /**
176  * Changes the time as reported for the current frame to the indicated time.
177  * Normally, the way to adjust the frame time is via tick(); this function is
178  * provided only for occasional special adjustments.
179  */
180 void ClockObject::
181 set_frame_time(double time, Thread *current_thread) {
182  nassertv(current_thread->get_pipeline_stage() == 0);
183 #ifdef NOTIFY_DEBUG
184  if (this == _global_clock && _mode != M_slave) {
185  util_cat.warning()
186  << "Adjusting global clock's frame time by " << time - get_frame_time()
187  << " seconds.\n";
188  }
189 #endif // NOTIFY_DEBUG
190  CDWriter cdata(_cycler, current_thread);
191  _actual_frame_time = time;
192  cdata->_reported_frame_time = time;
193 
194  // Recompute the epoch in case we are in a mode that relies on this.
195  cdata->_reported_frame_time_epoch = cdata->_reported_frame_time -
196  cdata->_frame_count / _user_frame_rate;
197 }
198 
199 /**
200  * Resets the number of frames counted to the indicated number. Also see
201  * reset(), set_real_time(), and set_frame_time().
202  */
203 void ClockObject::
204 set_frame_count(int frame_count, Thread *current_thread) {
205  nassertv(current_thread->get_pipeline_stage() == 0);
206 #ifdef NOTIFY_DEBUG
207  if (this == _global_clock && _mode != M_slave) {
208  util_cat.warning()
209  << "Adjusting global clock's frame count by "
210  << frame_count - get_frame_count() << " frames.\n";
211  }
212 #endif // NOTIFY_DEBUG
213  CDWriter cdata(_cycler, current_thread);
214  cdata->_frame_count = frame_count;
215 
216  // Recompute the epoch in case we are in a mode that relies on this.
217  cdata->_reported_frame_time_epoch = cdata->_reported_frame_time -
218  cdata->_frame_count / _user_frame_rate;
219 }
220 
221 /**
222  * In non-real-time mode, sets the number of seconds that should appear to
223  * elapse between frames. In forced mode or limited mode, sets our target dt.
224  * In normal mode, this has no effect.
225  *
226  * Also see set_frame_rate(), which is a different way to specify the same
227  * quantity.
228  */
229 void ClockObject::
230 set_dt(double dt) {
231  if (_mode == M_slave) {
232  // In M_slave mode, we can set any dt we like.
233  CDWriter cdata(_cycler, Thread::get_current_thread());
234  cdata->_dt = dt;
235  if (dt != 0.0) {
236  set_frame_rate(1.0 / dt);
237  }
238 
239  } else {
240  // In any other mode, we can only set non-zero dt.
241  nassertv(dt != 0.0);
242  set_frame_rate(1.0 / dt);
243  }
244 }
245 
246 /**
247  * In non-real-time mode, sets the number of frames per second that we should
248  * appear to be running. In forced mode or limited mode, sets our target
249  * frame rate. In normal mode, this has no effect.
250  *
251  * Also see set_dt(), which is a different way to specify the same quantity.
252  */
253 void ClockObject::
254 set_frame_rate(double frame_rate) {
255  nassertv(frame_rate != 0.0);
256 
257  Thread *current_thread = Thread::get_current_thread();
258  nassertv(current_thread->get_pipeline_stage() == 0);
259 
260  CDWriter cdata(_cycler, current_thread);
261  _user_frame_rate = frame_rate;
262 
263  switch (_mode) {
264  case M_non_real_time:
265  case M_forced:
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;
269 
270  default:
271  break;
272  }
273 }
274 
275 /**
276  * Returns the average frame rate in number of frames per second over the last
277  * get_average_frame_rate_interval() seconds. This measures the virtual frame
278  * rate if the clock is in M_non_real_time mode.
279  */
280 double ClockObject::
281 get_average_frame_rate(Thread *current_thread) const {
282  CDStageReader cdata(_cycler, 0, current_thread);
283  if (_ticks.size() <= 1) {
284  return 0.0;
285  } else {
286  return _ticks.size() / (cdata->_reported_frame_time - _ticks.front());
287  }
288 }
289 
290 /**
291  * Returns the maximum frame duration over the last
292  * get_average_frame_rate_interval() seconds.
293  */
294 double ClockObject::
295 get_max_frame_duration(Thread *current_thread) const {
296  CDStageReader cdata(_cycler, 0, current_thread);
297  double max_duration = 0.0;
298  double cur_duration = 0.0;
299  size_t i;
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;
304  }
305  }
306  return max_duration;
307 }
308 
309 /**
310  * Returns the standard deviation of the frame times of the frames rendered
311  * over the past get_average_frame_rate_interval() seconds. This number gives
312  * an estimate of the chugginess of the frame rate; if it is large, there is a
313  * large variation in the frame rate; if is small, all of the frames are
314  * consistent in length.
315  *
316  * A large value might also represent just a recent change in frame rate, for
317  * instance, because the camera has just rotated from looking at a simple
318  * scene to looking at a more complex scene.
319  */
320 double ClockObject::
321 calc_frame_rate_deviation(Thread *current_thread) const {
322  CDStageReader cdata(_cycler, 0, current_thread);
323  if (_ticks.size() <= 1) {
324  return 0.0;
325  } else {
326  double mean = (_ticks.back() - _ticks.front()) / (_ticks.size() - 1);
327  size_t i;
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);
333  }
334  double deviation_2 = sum_squares / (_ticks.size() - 1);
335  return sqrt(deviation_2);
336  }
337 }
338 
339 /**
340  * Instructs the clock that a new frame has just begun. In normal, real-time
341  * mode, get_frame_time() will henceforth report the time as of this instant
342  * as the current start-of-frame time. In non-real-time mode,
343  * get_frame_time() will be incremented by the value of dt.
344  */
345 void ClockObject::
346 tick(Thread *current_thread) {
347  nassertv(current_thread->get_pipeline_stage() == 0);
348  CDWriter cdata(_cycler, current_thread);
349  double old_reported_time = cdata->_reported_frame_time;
350 
351  if (_mode != M_slave) {
352  double old_time = _actual_frame_time;
353  _actual_frame_time = get_real_time();
354 
355  // In case someone munged the clock last frame and sent us backward in
356  // time, clamp the previous time to the current time to make sure we don't
357  // report anything strange (or wait interminably).
358  old_time = std::min(old_time, _actual_frame_time);
359 
360  ++cdata->_frame_count;
361 
362  switch (_mode) {
363  case M_normal:
364  // Time runs as it will; we simply report time elapsing.
365  cdata->_dt = _actual_frame_time - old_time;
366  cdata->_reported_frame_time = _actual_frame_time;
367  break;
368 
369  case M_non_real_time:
370  // Ignore real time. We always report the same interval having elapsed
371  // each frame.
372  cdata->_reported_frame_time = cdata->_reported_frame_time_epoch +
373  cdata->_frame_count / _user_frame_rate;
374  break;
375 
376  case M_limited:
377  // If we are running faster than the desired interval, slow down.
378  {
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);
383  }
384  break;
385 
386  case M_integer:
387  {
388  double dt = _actual_frame_time - old_time;
389  double target_dt = 1.0 / _user_frame_rate;
390  if (dt < target_dt) {
391  // We're running faster than the desired interval, so slow down to
392  // the next integer multiple of the frame rate.
393  target_dt = target_dt / floor(target_dt / dt);
394  } else {
395  // We're running slower than the desired interval, so slow down to
396  // the next integer divisor of the frame rate.
397  target_dt = target_dt * ceil(dt / target_dt);
398  }
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;
403  }
404  break;
405 
406  case M_integer_limited:
407  {
408  double dt = _actual_frame_time - old_time;
409  double target_dt = 1.0 / _user_frame_rate;
410  if (dt < target_dt) {
411  // We're running faster than the desired interval, so slow down to
412  // the target frame rate.
413 
414  } else {
415  // We're running slower than the desired interval, so slow down to
416  // the next integer divisor of the frame rate.
417  target_dt = target_dt * ceil(dt / target_dt);
418  }
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;
423  }
424  break;
425 
426  case M_forced:
427  // If we are running faster than the desired interval, slow down. If we
428  // are running slower than the desired interval, ignore that and pretend
429  // we're running at the specified rate.
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;
433  break;
434 
435  case M_degrade:
436  // Each frame, wait a certain fraction of the previous frame's time to
437  // degrade performance uniformly.
438  cdata->_dt = (_actual_frame_time - old_time) * _degrade_factor;
439 
440  if (_degrade_factor < 1.0) {
441  // If the degrade_factor is less than one, we want to simulate a
442  // higher frame rate by incrementing the clock more slowly.
443  cdata->_reported_frame_time += cdata->_dt;
444 
445  } else {
446  // Otherwise, we simulate a lower frame rate by waiting until the
447  // appropriate time has elapsed.
448  wait_until(old_time + cdata->_dt);
449  cdata->_reported_frame_time = _actual_frame_time;
450  }
451 
452  break;
453 
454  case M_slave:
455  // Handled above.
456  break;
457  }
458  }
459 
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) {
464  _ticks.pop_front();
465  }
466  }
467 }
468 
469 /**
470  * Resets the frame time to the current real time. This is similar to tick(),
471  * except that it does not advance the frame counter and does not affect dt.
472  * This is intended to be used in the middle of a particularly long frame to
473  * compensate for the time that has already elapsed.
474  *
475  * In non-real-time mode, this function has no effect (because in this mode
476  * all frames take the same length of time).
477  */
478 void ClockObject::
479 sync_frame_time(Thread *current_thread) {
480  if (_mode == M_normal) {
481  CDWriter cdata(_cycler, current_thread);
482  cdata->_reported_frame_time = get_real_time();
483  }
484 }
485 
486 /**
487  * Waits at the end of a frame until the indicated time has arrived. This is
488  * used to implement M_forced and M_degrade.
489  */
490 void ClockObject::
491 wait_until(double want_time) {
492  if (want_time <= _actual_frame_time) {
493  return;
494  }
495 
496 #ifdef DO_PSTATS
497  (*_start_clock_wait)();
498 #endif
499 
500  double wait_interval = (want_time - _actual_frame_time) - sleep_precision;
501 
502  if (wait_interval > 0.0) {
503  Thread::sleep(wait_interval);
504  }
505 
506 #ifdef DO_PSTATS
507  (*_start_clock_busy_wait)();
508 #endif
509 
510  // Now busy-wait until the actual time elapses.
511  while (_actual_frame_time < want_time) {
512  _actual_frame_time = get_real_time();
513  }
514 
515 #ifdef DO_PSTATS
516  (*_stop_clock_wait)();
517 #endif
518 }
519 
520 /**
521  * Called once per application to create the global clock object.
522  */
523 void ClockObject::
524 make_global_clock() {
525  nassertv(_global_clock == nullptr);
526 
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()."));
533 
534  ClockObject *clock = new ClockObject(clock_mode);
535  clock->local_object();
536 
537  if (AtomicAdjust::compare_and_exchange_ptr(_global_clock, nullptr, clock) != nullptr) {
538  // Another thread beat us to it.
539  delete clock;
540  }
541 }
542 
543 /**
544  * This no-op function is assigned as the initial pointer for
545  * _start_clock_wait and _stop_clock_wait, until the PStatClient comes along
546  * and replaces it.
547  */
548 void ClockObject::
549 dummy_clock_wait() {
550 }
551 
552 /**
553  *
554  */
555 ClockObject::CData::
556 CData() {
557  _frame_count = 0;
558  _reported_frame_time = 0.0;
559  _reported_frame_time_epoch = 0.0;
560  _dt = 0.0;
561 }
562 
563 /**
564  *
565  */
566 CycleData *ClockObject::CData::
567 make_copy() const {
568  return new CData(*this);
569 }
570 
571 /**
572  *
573  */
574 ostream &
575 operator << (ostream &out, ClockObject::Mode mode) {
576  switch (mode) {
577  case ClockObject::M_normal:
578  return out << "normal";
579 
580  case ClockObject::M_non_real_time:
581  return out << "non-real-time";
582 
583  case ClockObject::M_limited:
584  return out << "limited";
585 
586  case ClockObject::M_integer:
587  return out << "integer";
588 
589  case ClockObject::M_integer_limited:
590  return out << "integer_limited";
591 
592  case ClockObject::M_forced:
593  return out << "forced";
594 
595  case ClockObject::M_degrade:
596  return out << "degrade";
597 
598  case ClockObject::M_slave:
599  return out << "slave";
600  };
601 
602  return out << "**invalid ClockObject::Mode(" << (int)mode << ")**";
603 }
604 
605 /**
606  *
607  */
608 istream &
609 operator >> (istream &in, ClockObject::Mode &mode) {
610  string word;
611  in >> word;
612 
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;
629  } else {
630  util_cat->error()
631  << "Invalid ClockObject::Mode: " << word << "\n";
632  mode = ClockObject::M_normal;
633  }
634 
635  return in;
636 }
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.
Definition: trueClock.I:68
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.
Definition: clockObject.h:99
get_average_frame_rate
Returns the average frame rate in number of frames per second over the last get_average_frame_rate_in...
Definition: clockObject.h:118
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.
Definition: cycleData.h:47
set_real_time
Resets the clock to the indicated time.
Definition: clockObject.h:92
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.
Definition: thread.h:105
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.
Definition: thread.h:109
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 ...
Definition: clockObject.h:91
get_frame_count
Returns the number of times tick() has been called since the ClockObject was created,...
Definition: clockObject.h:94
A ClockObject keeps track of elapsed real time and discrete time.
Definition: clockObject.h:58
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 ...
Definition: clockObject.h:92
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.
Definition: clockObject.h:119
set_mode
Changes the mode of the clock.
Definition: clockObject.h:77
static void sleep(double seconds)
Suspends the current thread for at least the indicated amount of time.
Definition: thread.I:192
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.
Definition: clockObject.h:94
A thread; that is, a lightweight process.
Definition: thread.h:46
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
set_frame_time
Changes the time as reported for the current frame to the indicated time.
Definition: clockObject.h:91
static Pointer compare_and_exchange_ptr(Pointer &mem, Pointer old_value, Pointer new_value)
Atomic compare and exchange.