00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "clockObject.h"
00016 #include "config_util.h"
00017 #include "configVariableEnum.h"
00018 #include "string_utils.h"
00019 #include "thread.h"
00020
00021 void (*ClockObject::_start_clock_wait)() = ClockObject::dummy_clock_wait;
00022 void (*ClockObject::_start_clock_busy_wait)() = ClockObject::dummy_clock_wait;
00023 void (*ClockObject::_stop_clock_wait)() = ClockObject::dummy_clock_wait;
00024
00025 PT(ClockObject) ClockObject::_global_clock;
00026 TypeHandle ClockObject::_type_handle;
00027
00028
00029
00030
00031
00032
00033 ClockObject::
00034 ClockObject() : _ticks(get_class_type()) {
00035 _true_clock = TrueClock::get_global_ptr();
00036
00037
00038
00039
00040 _mode = M_normal;
00041
00042 _start_short_time = _true_clock->get_short_time();
00043 _start_long_time = _true_clock->get_long_time();
00044 _actual_frame_time = 0.0;
00045
00046 ConfigVariableDouble max_dt
00047 ("max-dt", -1.0,
00048 PRC_DESC("Sets a limit on the value returned by ClockObject::get_dt(). If "
00049 "this value is less than zero, no limit is imposed; "
00050 "otherwise, this is the maximum value that will ever "
00051 "be returned by get_dt(), regardless of how much time "
00052 "has actually elapsed between frames. See ClockObject::set_dt()."));
00053 ConfigVariableDouble clock_frame_rate
00054 ("clock-frame-rate", 1.0,
00055 PRC_DESC("In non-real-time clock mode, sets the number of frames per "
00056 "second that we should appear to be running. In forced "
00057 "mode or limited mode, sets our target frame rate. In "
00058 "normal mode, this has no effect. See ClockObject::set_frame_rate()."));
00059 ConfigVariableDouble clock_degrade_factor
00060 ("clock-degrade-factor", 1.0,
00061 PRC_DESC("In degrade clock mode, returns the ratio by which the "
00062 "performance is degraded. A value of 2.0 causes the "
00063 "clock to be slowed down by a factor of two (reducing "
00064 "performance to 1/2 what would be otherwise). See ClockObject::set_degrade_factor()."));
00065 ConfigVariableDouble average_frame_rate_interval
00066 ("average-frame-rate-interval", 1.0,
00067 PRC_DESC("See ClockObject::set_average_frame_rate_interval()."));
00068
00069 _max_dt = max_dt;
00070 _user_frame_rate = clock_frame_rate;
00071 _degrade_factor = clock_degrade_factor;
00072 _average_frame_rate_interval = average_frame_rate_interval;
00073
00074 _error_count = _true_clock->get_error_count();
00075 }
00076
00077
00078
00079
00080
00081
00082 ClockObject::
00083 ClockObject(const ClockObject ©) :
00084 _true_clock(copy._true_clock),
00085 _mode(copy._mode),
00086 _start_short_time(copy._start_short_time),
00087 _start_long_time(copy._start_long_time),
00088 _actual_frame_time(copy._actual_frame_time),
00089 _max_dt(copy._max_dt),
00090 _user_frame_rate(copy._user_frame_rate),
00091 _degrade_factor(copy._degrade_factor),
00092 _error_count(copy._error_count),
00093 _average_frame_rate_interval(copy._average_frame_rate_interval),
00094 _ticks(copy._ticks),
00095 _cycler(copy._cycler)
00096 {
00097 }
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149 void ClockObject::
00150 set_mode(ClockObject::Mode mode) {
00151 Thread *current_thread = Thread::get_current_thread();
00152 nassertv(current_thread->get_pipeline_stage() == 0);
00153 CDWriter cdata(_cycler, current_thread);
00154
00155 _mode = mode;
00156
00157
00158
00159 switch (_mode) {
00160 case M_non_real_time:
00161 case M_forced:
00162 cdata->_reported_frame_time_epoch = cdata->_reported_frame_time -
00163 cdata->_frame_count / _user_frame_rate;
00164 cdata->_dt = 1.0 / _user_frame_rate;
00165
00166 default:
00167 break;
00168 }
00169 }
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181 void ClockObject::
00182 set_real_time(double time) {
00183 #ifdef NOTIFY_DEBUG
00184
00185
00186
00187 if (util_cat.is_debug() && this == _global_clock) {
00188 util_cat.debug()
00189 << "Adjusting global clock's real time by " << time - get_real_time()
00190 << " seconds.\n";
00191 }
00192 #endif // NOTIFY_DEBUG
00193 _start_short_time = _true_clock->get_short_time() - time;
00194 _start_long_time = _true_clock->get_long_time() - time;
00195 }
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205 void ClockObject::
00206 set_frame_time(double time, Thread *current_thread) {
00207 nassertv(current_thread->get_pipeline_stage() == 0);
00208 #ifdef NOTIFY_DEBUG
00209 if (this == _global_clock && _mode != M_slave) {
00210 util_cat.warning()
00211 << "Adjusting global clock's frame time by " << time - get_frame_time()
00212 << " seconds.\n";
00213 }
00214 #endif // NOTIFY_DEBUG
00215 CDWriter cdata(_cycler, current_thread);
00216 _actual_frame_time = time;
00217 cdata->_reported_frame_time = time;
00218
00219
00220 cdata->_reported_frame_time_epoch = cdata->_reported_frame_time -
00221 cdata->_frame_count / _user_frame_rate;
00222 }
00223
00224
00225
00226
00227
00228
00229
00230
00231 void ClockObject::
00232 set_frame_count(int frame_count, Thread *current_thread) {
00233 nassertv(current_thread->get_pipeline_stage() == 0);
00234 #ifdef NOTIFY_DEBUG
00235 if (this == _global_clock && _mode != M_slave) {
00236 util_cat.warning()
00237 << "Adjusting global clock's frame count by "
00238 << frame_count - get_frame_count() << " frames.\n";
00239 }
00240 #endif // NOTIFY_DEBUG
00241 CDWriter cdata(_cycler, current_thread);
00242 cdata->_frame_count = frame_count;
00243
00244
00245 cdata->_reported_frame_time_epoch = cdata->_reported_frame_time -
00246 cdata->_frame_count / _user_frame_rate;
00247 }
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260 void ClockObject::
00261 set_dt(double dt) {
00262 if (_mode == M_slave) {
00263
00264 CDWriter cdata(_cycler, Thread::get_current_thread());
00265 cdata->_dt = dt;
00266 if (dt != 0.0) {
00267 set_frame_rate(1.0 / dt);
00268 }
00269
00270 } else {
00271
00272 nassertv(dt != 0.0);
00273 set_frame_rate(1.0 / dt);
00274 }
00275 }
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288 void ClockObject::
00289 set_frame_rate(double frame_rate) {
00290 nassertv(frame_rate != 0.0);
00291
00292 Thread *current_thread = Thread::get_current_thread();
00293 nassertv(current_thread->get_pipeline_stage() == 0);
00294
00295 CDWriter cdata(_cycler, current_thread);
00296 _user_frame_rate = frame_rate;
00297
00298 switch (_mode) {
00299 case M_non_real_time:
00300 case M_forced:
00301 cdata->_reported_frame_time_epoch = cdata->_reported_frame_time -
00302 cdata->_frame_count / _user_frame_rate;
00303 cdata->_dt = 1.0 / _user_frame_rate;
00304
00305 default:
00306 break;
00307 }
00308 }
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319 double ClockObject::
00320 get_average_frame_rate(Thread *current_thread) const {
00321 CDStageReader cdata(_cycler, 0, current_thread);
00322 if (_ticks.size() <= 1) {
00323 return 0.0;
00324 } else {
00325 return _ticks.size() / (cdata->_reported_frame_time - _ticks.front());
00326 }
00327 }
00328
00329
00330
00331
00332
00333
00334
00335 double ClockObject::
00336 get_max_frame_duration(Thread *current_thread) const {
00337 CDStageReader cdata(_cycler, 0, current_thread);
00338 double max_duration = 0.0;
00339 double cur_duration = 0.0;
00340 size_t i;
00341 for (i = 0; i < _ticks.size() - 1; i++) {
00342 cur_duration = _ticks[i + 1] - _ticks[i];
00343 if (cur_duration > max_duration) {
00344 max_duration = cur_duration;
00345 }
00346 }
00347 return max_duration;
00348 }
00349
00350
00351
00352
00353
00354
00355
00356
00357
00358
00359
00360
00361
00362
00363
00364
00365
00366 double ClockObject::
00367 calc_frame_rate_deviation(Thread *current_thread) const {
00368 CDStageReader cdata(_cycler, 0, current_thread);
00369 if (_ticks.size() <= 1) {
00370 return 0.0;
00371 } else {
00372 double mean = (_ticks.back() - _ticks.front()) / (_ticks.size() - 1);
00373 size_t i;
00374 double sum_squares = 0.0;
00375 for (i = 0; i < _ticks.size() - 1; ++i) {
00376 double delta = _ticks[i + 1] - _ticks[i];
00377 double diff = (delta - mean);
00378 sum_squares += (diff * diff);
00379 }
00380 double deviation_2 = sum_squares / (_ticks.size() - 1);
00381 return sqrt(deviation_2);
00382 }
00383 }
00384
00385
00386
00387
00388
00389
00390
00391
00392
00393
00394
00395 void ClockObject::
00396 tick(Thread *current_thread) {
00397 nassertv(current_thread->get_pipeline_stage() == 0);
00398 CDWriter cdata(_cycler, current_thread);
00399 double old_reported_time = cdata->_reported_frame_time;
00400
00401 if (_mode != M_slave) {
00402 double old_time = _actual_frame_time;
00403 _actual_frame_time = get_real_time();
00404
00405
00406
00407
00408
00409 old_time = min(old_time, _actual_frame_time);
00410
00411 ++cdata->_frame_count;
00412
00413 switch (_mode) {
00414 case M_normal:
00415
00416 cdata->_dt = _actual_frame_time - old_time;
00417 cdata->_reported_frame_time = _actual_frame_time;
00418 break;
00419
00420 case M_non_real_time:
00421
00422
00423 cdata->_reported_frame_time = cdata->_reported_frame_time_epoch +
00424 cdata->_frame_count / _user_frame_rate;
00425 break;
00426
00427 case M_limited:
00428
00429 {
00430 double wait_until_time = old_time + 1.0 / _user_frame_rate;
00431 wait_until(wait_until_time);
00432 cdata->_dt = _actual_frame_time - old_time;
00433 cdata->_reported_frame_time = max(_actual_frame_time, wait_until_time);
00434 }
00435 break;
00436
00437 case M_integer:
00438 {
00439 double dt = _actual_frame_time - old_time;
00440 double target_dt = 1.0 / _user_frame_rate;
00441 if (dt < target_dt) {
00442
00443
00444 target_dt = target_dt / floor(target_dt / dt);
00445 } else {
00446
00447
00448 target_dt = target_dt * ceil(dt / target_dt);
00449 }
00450 double wait_until_time = old_time + target_dt;
00451 wait_until(wait_until_time);
00452 cdata->_dt = target_dt;
00453 cdata->_reported_frame_time = wait_until_time;
00454 }
00455 break;
00456
00457 case M_integer_limited:
00458 {
00459 double dt = _actual_frame_time - old_time;
00460 double target_dt = 1.0 / _user_frame_rate;
00461 if (dt < target_dt) {
00462
00463
00464
00465 } else {
00466
00467
00468 target_dt = target_dt * ceil(dt / target_dt);
00469 }
00470 double wait_until_time = old_time + target_dt;
00471 wait_until(wait_until_time);
00472 cdata->_dt = target_dt;
00473 cdata->_reported_frame_time = wait_until_time;
00474 }
00475 break;
00476
00477 case M_forced:
00478
00479
00480
00481 wait_until(old_time + 1.0 / _user_frame_rate);
00482 cdata->_reported_frame_time = cdata->_reported_frame_time_epoch +
00483 cdata->_frame_count / _user_frame_rate;
00484 break;
00485
00486 case M_degrade:
00487
00488
00489 cdata->_dt = (_actual_frame_time - old_time) * _degrade_factor;
00490
00491 if (_degrade_factor < 1.0) {
00492
00493
00494 cdata->_reported_frame_time += cdata->_dt;
00495
00496 } else {
00497
00498
00499 wait_until(old_time + cdata->_dt);
00500 cdata->_reported_frame_time = _actual_frame_time;
00501 }
00502
00503 break;
00504
00505 case M_slave:
00506
00507 break;
00508 }
00509 }
00510
00511 if (_average_frame_rate_interval > 0.0) {
00512 _ticks.push_back(old_reported_time);
00513 while (_ticks.size() > 2 &&
00514 cdata->_reported_frame_time - _ticks.front() > _average_frame_rate_interval) {
00515 _ticks.pop_front();
00516 }
00517 }
00518 }
00519
00520
00521
00522
00523
00524
00525
00526
00527
00528
00529
00530
00531
00532
00533
00534 void ClockObject::
00535 sync_frame_time(Thread *current_thread) {
00536 if (_mode == M_normal) {
00537 CDWriter cdata(_cycler, current_thread);
00538 cdata->_reported_frame_time = get_real_time();
00539 }
00540 }
00541
00542
00543
00544
00545
00546
00547
00548
00549 void ClockObject::
00550 wait_until(double want_time) {
00551 if (want_time <= _actual_frame_time) {
00552 return;
00553 }
00554
00555 #ifdef DO_PSTATS
00556 (*_start_clock_wait)();
00557 #endif
00558
00559 double wait_interval = (want_time - _actual_frame_time) - sleep_precision;
00560
00561 if (wait_interval > 0.0) {
00562 Thread::sleep(wait_interval);
00563 }
00564
00565 #ifdef DO_PSTATS
00566 (*_start_clock_busy_wait)();
00567 #endif
00568
00569
00570 while (_actual_frame_time < want_time) {
00571 _actual_frame_time = get_real_time();
00572 }
00573
00574 #ifdef DO_PSTATS
00575 (*_stop_clock_wait)();
00576 #endif
00577 }
00578
00579
00580
00581
00582
00583
00584
00585 void ClockObject::
00586 make_global_clock() {
00587 nassertv(_global_clock == (ClockObject *)NULL);
00588
00589 ConfigVariableEnum<ClockObject::Mode> clock_mode
00590 ("clock-mode", ClockObject::M_normal,
00591 PRC_DESC("Specifies the mode of the global clock. The default mode, normal, "
00592 "is a real-time clock; other modes allow non-real-time special "
00593 "effects like simulated reduced frame rate. See "
00594 "ClockObject::set_mode()."));
00595
00596 _global_clock = new ClockObject;
00597 _global_clock->set_mode(clock_mode);
00598 }
00599
00600
00601
00602
00603
00604
00605
00606
00607 void ClockObject::
00608 dummy_clock_wait() {
00609 }
00610
00611
00612
00613
00614
00615
00616 ClockObject::CData::
00617 CData() {
00618 _frame_count = 0;
00619 _reported_frame_time = 0.0;
00620 _reported_frame_time_epoch = 0.0;
00621 _dt = 0.0;
00622 }
00623
00624
00625
00626
00627
00628
00629 CycleData *ClockObject::CData::
00630 make_copy() const {
00631 return new CData(*this);
00632 }
00633
00634
00635
00636
00637
00638 ostream &
00639 operator << (ostream &out, ClockObject::Mode mode) {
00640 switch (mode) {
00641 case ClockObject::M_normal:
00642 return out << "normal";
00643
00644 case ClockObject::M_non_real_time:
00645 return out << "non-real-time";
00646
00647 case ClockObject::M_limited:
00648 return out << "limited";
00649
00650 case ClockObject::M_integer:
00651 return out << "integer";
00652
00653 case ClockObject::M_integer_limited:
00654 return out << "integer_limited";
00655
00656 case ClockObject::M_forced:
00657 return out << "forced";
00658
00659 case ClockObject::M_degrade:
00660 return out << "degrade";
00661
00662 case ClockObject::M_slave:
00663 return out << "slave";
00664 };
00665
00666 return out << "**invalid ClockObject::Mode(" << (int)mode << ")**";
00667 }
00668
00669
00670
00671
00672
00673 istream &
00674 operator >> (istream &in, ClockObject::Mode &mode) {
00675 string word;
00676 in >> word;
00677
00678 if (cmp_nocase_uh(word, "normal") == 0) {
00679 mode = ClockObject::M_normal;
00680 } else if (cmp_nocase_uh(word, "non-real-time") == 0) {
00681 mode = ClockObject::M_non_real_time;
00682 } else if (cmp_nocase_uh(word, "limited") == 0) {
00683 mode = ClockObject::M_limited;
00684 } else if (cmp_nocase_uh(word, "integer") == 0) {
00685 mode = ClockObject::M_integer;
00686 } else if (cmp_nocase_uh(word, "integer_limited") == 0) {
00687 mode = ClockObject::M_integer_limited;
00688 } else if (cmp_nocase_uh(word, "forced") == 0) {
00689 mode = ClockObject::M_forced;
00690 } else if (cmp_nocase_uh(word, "degrade") == 0) {
00691 mode = ClockObject::M_degrade;
00692 } else if (cmp_nocase_uh(word, "slave") == 0) {
00693 mode = ClockObject::M_slave;
00694 } else {
00695 util_cat->error()
00696 << "Invalid ClockObject::Mode: " << word << "\n";
00697 mode = ClockObject::M_normal;
00698 }
00699
00700 return in;
00701 }