mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-04 02:42:49 -04:00
703 lines
26 KiB
C++
703 lines
26 KiB
C++
// Filename: clockObject.cxx
|
|
// Created by: drose (17Feb00)
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// PANDA 3D SOFTWARE
|
|
// Copyright (c) Carnegie Mellon University. All rights reserved.
|
|
//
|
|
// All use of this software is subject to the terms of the revised BSD
|
|
// license. You should have received a copy of this license along
|
|
// with this source code in a file named "LICENSE."
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
#include "clockObject.h"
|
|
#include "config_util.h"
|
|
#include "configVariableEnum.h"
|
|
#include "string_utils.h"
|
|
#include "thread.h"
|
|
|
|
void (*ClockObject::_start_clock_wait)() = ClockObject::dummy_clock_wait;
|
|
void (*ClockObject::_start_clock_busy_wait)() = ClockObject::dummy_clock_wait;
|
|
void (*ClockObject::_stop_clock_wait)() = ClockObject::dummy_clock_wait;
|
|
|
|
ClockObject *ClockObject::_global_clock;
|
|
TypeHandle ClockObject::_type_handle;
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: ClockObject::Constructor
|
|
// Access: Published
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
ClockObject::
|
|
ClockObject() : _ticks(get_class_type()) {
|
|
_true_clock = TrueClock::get_global_ptr();
|
|
|
|
// Each clock except for the application global clock is created in
|
|
// M_normal mode. The application global clock is later reset to
|
|
// respect clock_mode, which comes from the Config.prc file.
|
|
_mode = M_normal;
|
|
|
|
_start_short_time = _true_clock->get_short_time();
|
|
_start_long_time = _true_clock->get_long_time();
|
|
_actual_frame_time = 0.0;
|
|
|
|
ConfigVariableDouble max_dt
|
|
("max-dt", -1.0,
|
|
PRC_DESC("Sets a limit on the value returned by ClockObject::get_dt(). If "
|
|
"this value is less than zero, no limit is imposed; "
|
|
"otherwise, this is the maximum value that will ever "
|
|
"be returned by get_dt(), regardless of how much time "
|
|
"has actually elapsed between frames. See ClockObject::set_dt()."));
|
|
ConfigVariableDouble clock_frame_rate
|
|
("clock-frame-rate", 1.0,
|
|
PRC_DESC("In non-real-time clock mode, sets the number of frames per "
|
|
"second that we should appear to be running. In forced "
|
|
"mode or limited mode, sets our target frame rate. In "
|
|
"normal mode, this has no effect. See ClockObject::set_frame_rate()."));
|
|
ConfigVariableDouble clock_degrade_factor
|
|
("clock-degrade-factor", 1.0,
|
|
PRC_DESC("In degrade clock mode, returns the ratio by which the "
|
|
"performance is degraded. A value of 2.0 causes the "
|
|
"clock to be slowed down by a factor of two (reducing "
|
|
"performance to 1/2 what would be otherwise). See ClockObject::set_degrade_factor()."));
|
|
ConfigVariableDouble average_frame_rate_interval
|
|
("average-frame-rate-interval", 1.0,
|
|
PRC_DESC("See ClockObject::set_average_frame_rate_interval()."));
|
|
|
|
_max_dt = max_dt;
|
|
_user_frame_rate = clock_frame_rate;
|
|
_degrade_factor = clock_degrade_factor;
|
|
_average_frame_rate_interval = average_frame_rate_interval;
|
|
|
|
_error_count = _true_clock->get_error_count();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: ClockObject::Copy Constructor
|
|
// Access: Published
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
ClockObject::
|
|
ClockObject(const ClockObject ©) :
|
|
_true_clock(copy._true_clock),
|
|
_mode(copy._mode),
|
|
_start_short_time(copy._start_short_time),
|
|
_start_long_time(copy._start_long_time),
|
|
_actual_frame_time(copy._actual_frame_time),
|
|
_max_dt(copy._max_dt),
|
|
_user_frame_rate(copy._user_frame_rate),
|
|
_degrade_factor(copy._degrade_factor),
|
|
_error_count(copy._error_count),
|
|
_average_frame_rate_interval(copy._average_frame_rate_interval),
|
|
_ticks(copy._ticks),
|
|
_cycler(copy._cycler)
|
|
{
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: ClockObject::set_mode
|
|
// Access: Published
|
|
// Description: Changes the mode of the clock. Normally, the clock
|
|
// is in mode M_normal. In this mode, each call to
|
|
// tick() will set the value returned by
|
|
// get_frame_time() to the current real time; thus, the
|
|
// clock simply reports time advancing.
|
|
//
|
|
// Other possible modes:
|
|
//
|
|
// M_non_real_time - the clock ignores real time
|
|
// completely; at each call to tick(), it pretends that
|
|
// exactly dt seconds have elapsed since the last call
|
|
// to tick(). You may set the value of dt with
|
|
// set_dt() or set_frame_rate().
|
|
//
|
|
// M_limited - the clock will run as fast as it can, as
|
|
// in M_normal, but will not run faster than the rate
|
|
// specified by set_frame_rate(). If the application
|
|
// would run faster than this rate, the clock will slow
|
|
// down the application.
|
|
//
|
|
// M_integer - the clock will run as fast as it can, but
|
|
// the rate will be constrained to be an integer
|
|
// multiple or divisor of the rate specified by
|
|
// set_frame_rate(). The clock will slow down the
|
|
// application a bit to guarantee this.
|
|
//
|
|
// M_integer_limited - a combination of M_limited and
|
|
// M_integer; the clock will not run faster than
|
|
// set_frame_rate(), and if it runs slower, it will run
|
|
// at a integer divisor of that rate.
|
|
//
|
|
// M_forced - the clock forces the application to run at
|
|
// the rate specified by set_frame_rate(). If the
|
|
// application would run faster than this rate, the
|
|
// clock will slow down the application; if the
|
|
// application would run slower than this rate, the
|
|
// clock slows down time so that the application
|
|
// believes it is running at the given rate.
|
|
//
|
|
// M_degrade - the clock runs at real time, but the
|
|
// application is slowed down by a set factor of its
|
|
// frame rate, specified by set_degrade_factor().
|
|
//
|
|
// M_slave - the clock does not advance, but relies on
|
|
// the user to call set_frame_time() and/or
|
|
// set_frame_count() each frame.
|
|
////////////////////////////////////////////////////////////////////
|
|
void ClockObject::
|
|
set_mode(ClockObject::Mode mode) {
|
|
Thread *current_thread = Thread::get_current_thread();
|
|
nassertv(current_thread->get_pipeline_stage() == 0);
|
|
CDWriter cdata(_cycler, current_thread);
|
|
|
|
_mode = mode;
|
|
|
|
// In case we have set the clock to one of the modes that uses
|
|
// _reported_frame_time_epoch, recompute the epoch.
|
|
switch (_mode) {
|
|
case M_non_real_time:
|
|
case M_forced:
|
|
cdata->_reported_frame_time_epoch = cdata->_reported_frame_time -
|
|
cdata->_frame_count / _user_frame_rate;
|
|
cdata->_dt = 1.0 / _user_frame_rate;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: ClockObject::set_real_time
|
|
// Access: Published
|
|
// Description: Resets the clock to the indicated time. This
|
|
// changes only the real time of the clock as reported
|
|
// by get_real_time(), but does not immediately change
|
|
// the time reported by get_frame_time()--that will
|
|
// change after the next call to tick(). Also see
|
|
// reset(), set_frame_time(), and set_frame_count().
|
|
////////////////////////////////////////////////////////////////////
|
|
void ClockObject::
|
|
set_real_time(double time) {
|
|
#ifdef NOTIFY_DEBUG
|
|
// This is only a debug message, since it happens during normal
|
|
// development, particularly at startup, or whenever you break into
|
|
// the task loop.
|
|
if (util_cat.is_debug() && this == _global_clock) {
|
|
util_cat.debug()
|
|
<< "Adjusting global clock's real time by " << time - get_real_time()
|
|
<< " seconds.\n";
|
|
}
|
|
#endif // NOTIFY_DEBUG
|
|
_start_short_time = _true_clock->get_short_time() - time;
|
|
_start_long_time = _true_clock->get_long_time() - time;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: ClockObject::set_frame_time
|
|
// Access: Published
|
|
// Description: Changes the time as reported for the current frame to
|
|
// the indicated time. Normally, the way to adjust the
|
|
// frame time is via tick(); this function is provided
|
|
// only for occasional special adjustments.
|
|
////////////////////////////////////////////////////////////////////
|
|
void ClockObject::
|
|
set_frame_time(double time, Thread *current_thread) {
|
|
nassertv(current_thread->get_pipeline_stage() == 0);
|
|
#ifdef NOTIFY_DEBUG
|
|
if (this == _global_clock && _mode != M_slave) {
|
|
util_cat.warning()
|
|
<< "Adjusting global clock's frame time by " << time - get_frame_time()
|
|
<< " seconds.\n";
|
|
}
|
|
#endif // NOTIFY_DEBUG
|
|
CDWriter cdata(_cycler, current_thread);
|
|
_actual_frame_time = time;
|
|
cdata->_reported_frame_time = time;
|
|
|
|
// Recompute the epoch in case we are in a mode that relies on this.
|
|
cdata->_reported_frame_time_epoch = cdata->_reported_frame_time -
|
|
cdata->_frame_count / _user_frame_rate;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: ClockObject::set_frame_count
|
|
// Access: Published
|
|
// Description: Resets the number of frames counted to the indicated
|
|
// number. Also see reset(), set_real_time(), and
|
|
// set_frame_time().
|
|
////////////////////////////////////////////////////////////////////
|
|
void ClockObject::
|
|
set_frame_count(int frame_count, Thread *current_thread) {
|
|
nassertv(current_thread->get_pipeline_stage() == 0);
|
|
#ifdef NOTIFY_DEBUG
|
|
if (this == _global_clock && _mode != M_slave) {
|
|
util_cat.warning()
|
|
<< "Adjusting global clock's frame count by "
|
|
<< frame_count - get_frame_count() << " frames.\n";
|
|
}
|
|
#endif // NOTIFY_DEBUG
|
|
CDWriter cdata(_cycler, current_thread);
|
|
cdata->_frame_count = frame_count;
|
|
|
|
// Recompute the epoch in case we are in a mode that relies on this.
|
|
cdata->_reported_frame_time_epoch = cdata->_reported_frame_time -
|
|
cdata->_frame_count / _user_frame_rate;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: ClockObject::set_dt
|
|
// Access: Published
|
|
// Description: In non-real-time mode, sets the number of seconds
|
|
// that should appear to elapse between frames. In
|
|
// forced mode or limited mode, sets our target dt. In
|
|
// normal mode, this has no effect.
|
|
//
|
|
// Also see set_frame_rate(), which is a different way
|
|
// to specify the same quantity.
|
|
////////////////////////////////////////////////////////////////////
|
|
void ClockObject::
|
|
set_dt(double dt) {
|
|
if (_mode == M_slave) {
|
|
// In M_slave mode, we can set any dt we like.
|
|
CDWriter cdata(_cycler, Thread::get_current_thread());
|
|
cdata->_dt = dt;
|
|
if (dt != 0.0) {
|
|
set_frame_rate(1.0 / dt);
|
|
}
|
|
|
|
} else {
|
|
// In any other mode, we can only set non-zero dt.
|
|
nassertv(dt != 0.0);
|
|
set_frame_rate(1.0 / dt);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: ClockObject::set_frame_rate
|
|
// Access: Published
|
|
// Description: In non-real-time mode, sets the number of frames per
|
|
// second that we should appear to be running. In forced
|
|
// mode or limited mode, sets our target frame rate. In
|
|
// normal mode, this has no effect.
|
|
//
|
|
// Also see set_dt(), which is a different way to
|
|
// specify the same quantity.
|
|
////////////////////////////////////////////////////////////////////
|
|
void ClockObject::
|
|
set_frame_rate(double frame_rate) {
|
|
nassertv(frame_rate != 0.0);
|
|
|
|
Thread *current_thread = Thread::get_current_thread();
|
|
nassertv(current_thread->get_pipeline_stage() == 0);
|
|
|
|
CDWriter cdata(_cycler, current_thread);
|
|
_user_frame_rate = frame_rate;
|
|
|
|
switch (_mode) {
|
|
case M_non_real_time:
|
|
case M_forced:
|
|
cdata->_reported_frame_time_epoch = cdata->_reported_frame_time -
|
|
cdata->_frame_count / _user_frame_rate;
|
|
cdata->_dt = 1.0 / _user_frame_rate;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: ClockObject::get_average_frame_rate
|
|
// Access: Published
|
|
// Description: Returns the average frame rate in number of frames
|
|
// per second over the last
|
|
// get_average_frame_rate_interval() seconds. This
|
|
// measures the virtual frame rate if the clock is in
|
|
// M_non_real_time mode.
|
|
////////////////////////////////////////////////////////////////////
|
|
double ClockObject::
|
|
get_average_frame_rate(Thread *current_thread) const {
|
|
CDStageReader cdata(_cycler, 0, current_thread);
|
|
if (_ticks.size() <= 1) {
|
|
return 0.0;
|
|
} else {
|
|
return _ticks.size() / (cdata->_reported_frame_time - _ticks.front());
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: ClockObject::get_max_frame_duration
|
|
// Access: Published
|
|
// Description: Returns the maximum frame duration over the last
|
|
// get_average_frame_rate_interval() seconds.
|
|
////////////////////////////////////////////////////////////////////
|
|
double ClockObject::
|
|
get_max_frame_duration(Thread *current_thread) const {
|
|
CDStageReader cdata(_cycler, 0, current_thread);
|
|
double max_duration = 0.0;
|
|
double cur_duration = 0.0;
|
|
size_t i;
|
|
for (i = 0; i < _ticks.size() - 1; i++) {
|
|
cur_duration = _ticks[i + 1] - _ticks[i];
|
|
if (cur_duration > max_duration) {
|
|
max_duration = cur_duration;
|
|
}
|
|
}
|
|
return max_duration;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: ClockObject::calc_frame_time_deviation
|
|
// Access: Published
|
|
// Description: Returns the standard deviation of the frame times of
|
|
// the frames rendered over the past
|
|
// get_average_frame_rate_interval() seconds. This
|
|
// number gives an estimate of the chugginess of the
|
|
// frame rate; if it is large, there is a large
|
|
// variation in the frame rate; if is small, all of the
|
|
// frames are consistent in length.
|
|
//
|
|
// A large value might also represent just a recent
|
|
// change in frame rate, for instance, because the
|
|
// camera has just rotated from looking at a simple
|
|
// scene to looking at a more complex scene.
|
|
////////////////////////////////////////////////////////////////////
|
|
double ClockObject::
|
|
calc_frame_rate_deviation(Thread *current_thread) const {
|
|
CDStageReader cdata(_cycler, 0, current_thread);
|
|
if (_ticks.size() <= 1) {
|
|
return 0.0;
|
|
} else {
|
|
double mean = (_ticks.back() - _ticks.front()) / (_ticks.size() - 1);
|
|
size_t i;
|
|
double sum_squares = 0.0;
|
|
for (i = 0; i < _ticks.size() - 1; ++i) {
|
|
double delta = _ticks[i + 1] - _ticks[i];
|
|
double diff = (delta - mean);
|
|
sum_squares += (diff * diff);
|
|
}
|
|
double deviation_2 = sum_squares / (_ticks.size() - 1);
|
|
return sqrt(deviation_2);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: ClockObject::tick
|
|
// Access: Published
|
|
// Description: Instructs the clock that a new frame has just begun.
|
|
// In normal, real-time mode, get_frame_time() will
|
|
// henceforth report the time as of this instant as the
|
|
// current start-of-frame time. In non-real-time mode,
|
|
// get_frame_time() will be incremented by the value of
|
|
// dt.
|
|
////////////////////////////////////////////////////////////////////
|
|
void ClockObject::
|
|
tick(Thread *current_thread) {
|
|
nassertv(current_thread->get_pipeline_stage() == 0);
|
|
CDWriter cdata(_cycler, current_thread);
|
|
double old_reported_time = cdata->_reported_frame_time;
|
|
|
|
if (_mode != M_slave) {
|
|
double old_time = _actual_frame_time;
|
|
_actual_frame_time = get_real_time();
|
|
|
|
// In case someone munged the clock last frame and sent us
|
|
// backward in time, clamp the previous time to the current time
|
|
// to make sure we don't report anything strange (or wait
|
|
// interminably).
|
|
old_time = min(old_time, _actual_frame_time);
|
|
|
|
++cdata->_frame_count;
|
|
|
|
switch (_mode) {
|
|
case M_normal:
|
|
// Time runs as it will; we simply report time elapsing.
|
|
cdata->_dt = _actual_frame_time - old_time;
|
|
cdata->_reported_frame_time = _actual_frame_time;
|
|
break;
|
|
|
|
case M_non_real_time:
|
|
// Ignore real time. We always report the same interval having
|
|
// elapsed each frame.
|
|
cdata->_reported_frame_time = cdata->_reported_frame_time_epoch +
|
|
cdata->_frame_count / _user_frame_rate;
|
|
break;
|
|
|
|
case M_limited:
|
|
// If we are running faster than the desired interval, slow down.
|
|
{
|
|
double wait_until_time = old_time + 1.0 / _user_frame_rate;
|
|
wait_until(wait_until_time);
|
|
cdata->_dt = _actual_frame_time - old_time;
|
|
cdata->_reported_frame_time = max(_actual_frame_time, wait_until_time);
|
|
}
|
|
break;
|
|
|
|
case M_integer:
|
|
{
|
|
double dt = _actual_frame_time - old_time;
|
|
double target_dt = 1.0 / _user_frame_rate;
|
|
if (dt < target_dt) {
|
|
// We're running faster than the desired interval, so slow
|
|
// down to the next integer multiple of the frame rate.
|
|
target_dt = target_dt / floor(target_dt / dt);
|
|
} else {
|
|
// We're running slower than the desired interval, so slow
|
|
// down to the next integer divisor of the frame rate.
|
|
target_dt = target_dt * ceil(dt / target_dt);
|
|
}
|
|
double wait_until_time = old_time + target_dt;
|
|
wait_until(wait_until_time);
|
|
cdata->_dt = target_dt;
|
|
cdata->_reported_frame_time = wait_until_time;
|
|
}
|
|
break;
|
|
|
|
case M_integer_limited:
|
|
{
|
|
double dt = _actual_frame_time - old_time;
|
|
double target_dt = 1.0 / _user_frame_rate;
|
|
if (dt < target_dt) {
|
|
// We're running faster than the desired interval, so slow
|
|
// down to the target frame rate.
|
|
|
|
} else {
|
|
// We're running slower than the desired interval, so slow
|
|
// down to the next integer divisor of the frame rate.
|
|
target_dt = target_dt * ceil(dt / target_dt);
|
|
}
|
|
double wait_until_time = old_time + target_dt;
|
|
wait_until(wait_until_time);
|
|
cdata->_dt = target_dt;
|
|
cdata->_reported_frame_time = wait_until_time;
|
|
}
|
|
break;
|
|
|
|
case M_forced:
|
|
// If we are running faster than the desired interval, slow down.
|
|
// If we are running slower than the desired interval, ignore that
|
|
// and pretend we're running at the specified rate.
|
|
wait_until(old_time + 1.0 / _user_frame_rate);
|
|
cdata->_reported_frame_time = cdata->_reported_frame_time_epoch +
|
|
cdata->_frame_count / _user_frame_rate;
|
|
break;
|
|
|
|
case M_degrade:
|
|
// Each frame, wait a certain fraction of the previous frame's
|
|
// time to degrade performance uniformly.
|
|
cdata->_dt = (_actual_frame_time - old_time) * _degrade_factor;
|
|
|
|
if (_degrade_factor < 1.0) {
|
|
// If the degrade_factor is less than one, we want to simulate a
|
|
// higher frame rate by incrementing the clock more slowly.
|
|
cdata->_reported_frame_time += cdata->_dt;
|
|
|
|
} else {
|
|
// Otherwise, we simulate a lower frame rate by waiting until
|
|
// the appropriate time has elapsed.
|
|
wait_until(old_time + cdata->_dt);
|
|
cdata->_reported_frame_time = _actual_frame_time;
|
|
}
|
|
|
|
break;
|
|
|
|
case M_slave:
|
|
// Handled above.
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (_average_frame_rate_interval > 0.0) {
|
|
_ticks.push_back(old_reported_time);
|
|
while (_ticks.size() > 2 &&
|
|
cdata->_reported_frame_time - _ticks.front() > _average_frame_rate_interval) {
|
|
_ticks.pop_front();
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: ClockObject::sync_frame_time
|
|
// Access: Published
|
|
// Description: Resets the frame time to the current real time. This
|
|
// is similar to tick(), except that it does not advance
|
|
// the frame counter and does not affect dt. This is
|
|
// intended to be used in the middle of a particularly
|
|
// long frame to compensate for the time that has
|
|
// already elapsed.
|
|
//
|
|
// In non-real-time mode, this function has no effect
|
|
// (because in this mode all frames take the same length
|
|
// of time).
|
|
////////////////////////////////////////////////////////////////////
|
|
void ClockObject::
|
|
sync_frame_time(Thread *current_thread) {
|
|
if (_mode == M_normal) {
|
|
CDWriter cdata(_cycler, current_thread);
|
|
cdata->_reported_frame_time = get_real_time();
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: ClockObject::wait_until
|
|
// Access: Private
|
|
// Description: Waits at the end of a frame until the indicated time
|
|
// has arrived. This is used to implement M_forced and
|
|
// M_degrade.
|
|
////////////////////////////////////////////////////////////////////
|
|
void ClockObject::
|
|
wait_until(double want_time) {
|
|
if (want_time <= _actual_frame_time) {
|
|
return;
|
|
}
|
|
|
|
#ifdef DO_PSTATS
|
|
(*_start_clock_wait)();
|
|
#endif
|
|
|
|
double wait_interval = (want_time - _actual_frame_time) - sleep_precision;
|
|
|
|
if (wait_interval > 0.0) {
|
|
Thread::sleep(wait_interval);
|
|
}
|
|
|
|
#ifdef DO_PSTATS
|
|
(*_start_clock_busy_wait)();
|
|
#endif
|
|
|
|
// Now busy-wait until the actual time elapses.
|
|
while (_actual_frame_time < want_time) {
|
|
_actual_frame_time = get_real_time();
|
|
}
|
|
|
|
#ifdef DO_PSTATS
|
|
(*_stop_clock_wait)();
|
|
#endif
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: ClockObject::make_global_clock
|
|
// Access: Private, Static
|
|
// Description: Called once per application to create the global
|
|
// clock object.
|
|
////////////////////////////////////////////////////////////////////
|
|
void ClockObject::
|
|
make_global_clock() {
|
|
nassertv(_global_clock == (ClockObject *)NULL);
|
|
|
|
ConfigVariableEnum<ClockObject::Mode> clock_mode
|
|
("clock-mode", ClockObject::M_normal,
|
|
PRC_DESC("Specifies the mode of the global clock. The default mode, normal, "
|
|
"is a real-time clock; other modes allow non-real-time special "
|
|
"effects like simulated reduced frame rate. See "
|
|
"ClockObject::set_mode()."));
|
|
|
|
_global_clock = new ClockObject;
|
|
_global_clock->set_mode(clock_mode);
|
|
_global_clock->ref();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: ClockObject::dummy_clock_wait
|
|
// Access: Private, Static
|
|
// Description: This no-op function is assigned as the initial
|
|
// pointer for _start_clock_wait and _stop_clock_wait,
|
|
// until the PStatClient comes along and replaces it.
|
|
////////////////////////////////////////////////////////////////////
|
|
void ClockObject::
|
|
dummy_clock_wait() {
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: ClockObject::CData::Constructor
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
ClockObject::CData::
|
|
CData() {
|
|
_frame_count = 0;
|
|
_reported_frame_time = 0.0;
|
|
_reported_frame_time_epoch = 0.0;
|
|
_dt = 0.0;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: ClockObject::CData::make_copy
|
|
// Access: Public, Virtual
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
CycleData *ClockObject::CData::
|
|
make_copy() const {
|
|
return new CData(*this);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: ClockObject::Mode ostream operator
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
ostream &
|
|
operator << (ostream &out, ClockObject::Mode mode) {
|
|
switch (mode) {
|
|
case ClockObject::M_normal:
|
|
return out << "normal";
|
|
|
|
case ClockObject::M_non_real_time:
|
|
return out << "non-real-time";
|
|
|
|
case ClockObject::M_limited:
|
|
return out << "limited";
|
|
|
|
case ClockObject::M_integer:
|
|
return out << "integer";
|
|
|
|
case ClockObject::M_integer_limited:
|
|
return out << "integer_limited";
|
|
|
|
case ClockObject::M_forced:
|
|
return out << "forced";
|
|
|
|
case ClockObject::M_degrade:
|
|
return out << "degrade";
|
|
|
|
case ClockObject::M_slave:
|
|
return out << "slave";
|
|
};
|
|
|
|
return out << "**invalid ClockObject::Mode(" << (int)mode << ")**";
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: ClockObject::Mode istream operator
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
istream &
|
|
operator >> (istream &in, ClockObject::Mode &mode) {
|
|
string word;
|
|
in >> word;
|
|
|
|
if (cmp_nocase_uh(word, "normal") == 0) {
|
|
mode = ClockObject::M_normal;
|
|
} else if (cmp_nocase_uh(word, "non-real-time") == 0) {
|
|
mode = ClockObject::M_non_real_time;
|
|
} else if (cmp_nocase_uh(word, "limited") == 0) {
|
|
mode = ClockObject::M_limited;
|
|
} else if (cmp_nocase_uh(word, "integer") == 0) {
|
|
mode = ClockObject::M_integer;
|
|
} else if (cmp_nocase_uh(word, "integer_limited") == 0) {
|
|
mode = ClockObject::M_integer_limited;
|
|
} else if (cmp_nocase_uh(word, "forced") == 0) {
|
|
mode = ClockObject::M_forced;
|
|
} else if (cmp_nocase_uh(word, "degrade") == 0) {
|
|
mode = ClockObject::M_degrade;
|
|
} else if (cmp_nocase_uh(word, "slave") == 0) {
|
|
mode = ClockObject::M_slave;
|
|
} else {
|
|
util_cat->error()
|
|
<< "Invalid ClockObject::Mode: " << word << "\n";
|
|
mode = ClockObject::M_normal;
|
|
}
|
|
|
|
return in;
|
|
}
|