Browse Source

add ClockObject::M_forced and M_degrade

David Rose 22 years ago
parent
commit
1033c1d52e

+ 34 - 8
panda/src/express/clockObject.I

@@ -163,16 +163,12 @@ get_dt() const {
 //     Function: ClockObject::set_dt
 //     Function: ClockObject::set_dt
 //       Access: Published
 //       Access: Published
 //  Description: In non-real-time mode, sets the number of seconds
 //  Description: In non-real-time mode, sets the number of seconds
-//               that should appear to elapse between frames.
+//               that should appear to elapse between frames.  In
+//               forced mode, sets our target dt.  In normal mode,
+//               this has no effect.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void ClockObject::
 INLINE void ClockObject::
 set_dt(double dt) {
 set_dt(double dt) {
-  if (_mode != M_non_real_time) {
-    express_cat.error()
-      << "ClockObject cannot set dt in real-time mode." << endl;
-    return;
-  }
-
   _dt = dt;
   _dt = dt;
 }
 }
 
 
@@ -209,6 +205,36 @@ set_max_dt(double max_dt) {
   _max_dt = max_dt;
   _max_dt = max_dt;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: ClockObject::get_degrade_factor
+//       Access: Published
+//  Description: In degrade 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).
+//
+//               This has no effect if mode is not M_degrade.
+////////////////////////////////////////////////////////////////////
+INLINE double ClockObject::
+get_degrade_factor() const {
+  return _degrade_factor;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClockObject::set_degrade_factor
+//       Access: Published
+//  Description: In degrade mode, sets 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).
+//
+//               This has no effect if mode is not M_degrade.
+////////////////////////////////////////////////////////////////////
+INLINE void ClockObject::
+set_degrade_factor(double degrade_factor) {
+  _degrade_factor = degrade_factor;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ClockObject::set_average_frame_rate_interval
 //     Function: ClockObject::set_average_frame_rate_interval
 //       Access: Published
 //       Access: Published
@@ -270,7 +296,7 @@ get_average_frame_rate() const {
 INLINE ClockObject *ClockObject::
 INLINE ClockObject *ClockObject::
 get_global_clock() {
 get_global_clock() {
   if (_global_clock == (ClockObject *)NULL) {
   if (_global_clock == (ClockObject *)NULL) {
-    _global_clock = new ClockObject;
+    make_global_clock();
   }
   }
   return _global_clock;
   return _global_clock;
 }
 }

+ 93 - 3
panda/src/express/clockObject.cxx

@@ -20,8 +20,15 @@
 #include "clockObject.h"
 #include "clockObject.h"
 #include "config_express.h"
 #include "config_express.h"
 
 
-ClockObject *ClockObject::_global_clock = (ClockObject *)NULL;
+#if defined(WIN32)
+  #define WINDOWS_LEAN_AND_MEAN
+  #include <wtypes.h>  // For select()
+  #undef WINDOWS_LEAN_AND_MEAN  
+#else
+  #include <sys/time.h>  // For select()
+#endif
 
 
+ClockObject *ClockObject::_global_clock = (ClockObject *)NULL;
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ClockObject::Constructor
 //     Function: ClockObject::Constructor
@@ -31,14 +38,20 @@ ClockObject *ClockObject::_global_clock = (ClockObject *)NULL;
 ClockObject::
 ClockObject::
 ClockObject() {
 ClockObject() {
   _true_clock = TrueClock::get_ptr();
   _true_clock = TrueClock::get_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 Configrc file.
   _mode = M_normal;
   _mode = M_normal;
+
   _start_short_time = _true_clock->get_short_time();
   _start_short_time = _true_clock->get_short_time();
   _start_long_time = _true_clock->get_long_time();
   _start_long_time = _true_clock->get_long_time();
   _frame_count = 0;
   _frame_count = 0;
   _actual_frame_time = 0.0;
   _actual_frame_time = 0.0;
   _reported_frame_time = 0.0;
   _reported_frame_time = 0.0;
-  _dt = 0.0;
-  _max_dt = -1.0;
+  _dt = 1.0 / clock_frame_rate;
+  _max_dt = max_dt;
+  _degrade_factor = clock_degrade_factor;
   _average_frame_rate_interval = average_frame_rate_interval;
   _average_frame_rate_interval = average_frame_rate_interval;
 }
 }
 
 
@@ -124,6 +137,7 @@ tick() {
 
 
   switch (_mode) {
   switch (_mode) {
   case M_normal:
   case M_normal:
+    // Time runs as it will; we simply report time elapsing.
     _dt = _actual_frame_time - old_time;
     _dt = _actual_frame_time - old_time;
     if (_max_dt > 0.0) {
     if (_max_dt > 0.0) {
       _dt = min(_max_dt, _dt);
       _dt = min(_max_dt, _dt);
@@ -132,8 +146,41 @@ tick() {
     break;
     break;
 
 
   case M_non_real_time:
   case M_non_real_time:
+    // Ignore real time.  We always report the same interval having
+    // elapsed each frame.
     _reported_frame_time += _dt;
     _reported_frame_time += _dt;
     break;
     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 + _dt);
+    _reported_frame_time += _dt;
+    break;
+
+  case M_degrade:
+    // Each frame, wait a certain fraction of the previous frame's
+    // time to degrade performance uniformly.
+    _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.
+      _reported_frame_time += _dt;
+
+    } else {
+      // Otherwise, we simulate a lower frame rate by waiting until
+      // the appropriate time has elapsed.
+      wait_until(old_time + _dt);
+      _reported_frame_time = _actual_frame_time;
+    }
+
+    if (_max_dt > 0.0) {
+      _dt = min(_max_dt, _dt);
+    }
+    break;
+
   }
   }
 
 
   _frame_count++;
   _frame_count++;
@@ -168,6 +215,49 @@ sync_frame_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) {
+  double wait_interval = (want_time - _actual_frame_time) - sleep_precision;
+    
+  if (wait_interval > 0.0) {
+    struct timeval tv;
+    tv.tv_sec = (int)(wait_interval);
+    tv.tv_usec = (int)((wait_interval - tv.tv_sec) * 1000000.0);
+    select(0, NULL, NULL, NULL, &tv);
+  }
+  
+  // Now busy-wait until the actual time elapses.
+  while (_actual_frame_time < want_time) {
+    _actual_frame_time = get_real_time();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     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);
+
+  // Make sure we have run init_libexpress() by this time.  This
+  // function is responsible for initializing the clock_mode Configrc
+  // variable.
+  init_libexpress();
+
+  _global_clock = new ClockObject;
+  _global_clock->set_mode(clock_mode);
+}
+  
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: get_time_of_day
 //     Function: get_time_of_day
 //  Description:
 //  Description:

+ 9 - 1
panda/src/express/clockObject.h

@@ -22,7 +22,6 @@
 #include "pandabase.h"
 #include "pandabase.h"
 
 
 #include "trueClock.h"
 #include "trueClock.h"
-#include "config_express.h"
 #include "pdeque.h"
 #include "pdeque.h"
 
 
 class EXPCL_PANDAEXPRESS TimeVal {
 class EXPCL_PANDAEXPRESS TimeVal {
@@ -69,6 +68,8 @@ PUBLISHED:
   enum Mode {
   enum Mode {
     M_normal,
     M_normal,
     M_non_real_time,
     M_non_real_time,
+    M_forced,
+    M_degrade,
   };
   };
 
 
   ClockObject();
   ClockObject();
@@ -95,6 +96,9 @@ PUBLISHED:
   INLINE double get_max_dt() const;
   INLINE double get_max_dt() const;
   INLINE void set_max_dt(double max_dt);
   INLINE void set_max_dt(double max_dt);
 
 
+  INLINE double get_degrade_factor() const;
+  INLINE void set_degrade_factor(double degrade_factor);
+
   INLINE void set_average_frame_rate_interval(double time);
   INLINE void set_average_frame_rate_interval(double time);
   INLINE double get_average_frame_rate_interval() const;
   INLINE double get_average_frame_rate_interval() const;
   INLINE double get_average_frame_rate() const;
   INLINE double get_average_frame_rate() const;
@@ -105,6 +109,9 @@ PUBLISHED:
   INLINE static ClockObject *get_global_clock();
   INLINE static ClockObject *get_global_clock();
 
 
 private:
 private:
+  void wait_until(double want_time);
+  static void make_global_clock();
+
   TrueClock *_true_clock;
   TrueClock *_true_clock;
   Mode _mode;
   Mode _mode;
   double _start_short_time;
   double _start_short_time;
@@ -114,6 +121,7 @@ private:
   double _reported_frame_time;
   double _reported_frame_time;
   double _dt;
   double _dt;
   double _max_dt;
   double _max_dt;
+  double _degrade_factor;
 
 
   // For tracking the average frame rate over a certain interval of
   // For tracking the average frame rate over a certain interval of
   // time.
   // time.

+ 63 - 34
panda/src/express/config_express.cxx

@@ -43,6 +43,55 @@ ConfigureFn(config_express) {
   init_libexpress();
   init_libexpress();
 }
 }
 
 
+const int patchfile_window_size =
+        config_express.GetInt("patchfile-window-size", 16);
+
+const int patchfile_increment_size =
+        config_express.GetInt("patchfile-increment-size", 8);
+
+const int patchfile_buffer_size =
+        config_express.GetInt("patchfile-buffer-size", 4096);
+
+const int patchfile_zone_size =
+        config_express.GetInt("patchfile-zone-size", 10000);
+
+// Set this true to keep around the temporary files from downloading,
+// decompressing, and patching, or false (the default) to delete
+// these.  Mainly useful for debugging when the process goes wrong.
+const bool keep_temporary_files =
+config_express.GetBool("keep-temporary-files", false);
+
+const double average_frame_rate_interval = 
+config_express.GetDouble("average-frame-rate-interval", 1.0);
+
+ClockObject::Mode clock_mode = ClockObject::M_normal;
+const double clock_frame_rate = 
+config_express.GetDouble("clock-frame-rate", 1.0);
+const double clock_degrade_factor = 
+config_express.GetDouble("clock-degrade-factor", 1.0);
+const double max_dt = 
+config_express.GetDouble("max-dt", -1.0);
+
+// This is the accuracy within which we can expect select() to return
+// precisely.  That is, if we use select() to request a timeout of 1.0
+// seconds, we can expect to actually sleep for somewhere between 1.0
+// and 1.0 + sleep-precision seconds.
+const double sleep_precision =
+config_express.GetDouble("sleep-precision", 0.01);
+
+// Set this true to use the VirtualFileSystem mechanism for loading
+// models, etc.  Since the VirtualFileSystem maps to the same as the
+// actual file system by default, there is probably no reason to set
+// this false, except for testing or if you mistrust the new code.
+const bool use_vfs = config_express.GetBool("use-vfs", true);
+
+// Set this true to enable accumulation of several small consecutive
+// TCP datagrams into one large datagram before sending it, to reduce
+// overhead from the TCP/IP protocol.  See
+// Connection::set_collect_tcp() or SocketStream::set_collect_tcp().
+const bool collect_tcp = config_express.GetBool("collect-tcp", false);
+const double collect_tcp_interval = config_express.GetDouble("collect-tcp-interval", 0.2);
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: init_libexpress
 //     Function: init_libexpress
 //  Description: Initializes the library.  This must be called at
 //  Description: Initializes the library.  This must be called at
@@ -85,6 +134,20 @@ init_libexpress() {
     express_cat.error()
     express_cat.error()
       << "Invalid text-encoding: " << text_encoding << "\n";
       << "Invalid text-encoding: " << text_encoding << "\n";
   }
   }
+
+  string clock_mode_str = config_express.GetString("clock-mode", "normal");
+  if (clock_mode_str == "normal") {
+    clock_mode = ClockObject::M_normal;
+  } else if (clock_mode_str == "non-real-time") {
+    clock_mode = ClockObject::M_non_real_time;
+  } else if (clock_mode_str == "forced") {
+    clock_mode = ClockObject::M_forced;
+  } else if (clock_mode_str == "degrade") {
+    clock_mode = ClockObject::M_degrade;
+  } else {
+    express_cat.error()
+      << "Invalid clock-mode: " << clock_mode_str << "\n";
+  }
 }
 }
 
 
 
 
@@ -170,40 +233,6 @@ get_verify_dcast() {
   return verify_dcast;
   return verify_dcast;
 }
 }
 
 
-const int patchfile_window_size =
-        config_express.GetInt("patchfile-window-size", 16);
-
-const int patchfile_increment_size =
-        config_express.GetInt("patchfile-increment-size", 8);
-
-const int patchfile_buffer_size =
-        config_express.GetInt("patchfile-buffer-size", 4096);
-
-const int patchfile_zone_size =
-        config_express.GetInt("patchfile-zone-size", 10000);
-
-// Set this true to keep around the temporary files from downloading,
-// decompressing, and patching, or false (the default) to delete
-// these.  Mainly useful for debugging when the process goes wrong.
-const bool keep_temporary_files =
-config_express.GetBool("keep-temporary-files", false);
-
-extern const double average_frame_rate_interval = 
-config_express.GetDouble("average-frame-rate-interval", 1.0);
-
-// Set this true to use the VirtualFileSystem mechanism for loading
-// models, etc.  Since the VirtualFileSystem maps to the same as the
-// actual file system by default, there is probably no reason to set
-// this false, except for testing or if you mistrust the new code.
-const bool use_vfs = config_express.GetBool("use-vfs", true);
-
-// Set this true to enable accumulation of several small consecutive
-// TCP datagrams into one large datagram before sending it, to reduce
-// overhead from the TCP/IP protocol.  See
-// Connection::set_collect_tcp() or SocketStream::set_collect_tcp().
-const bool collect_tcp = config_express.GetBool("collect-tcp", false);
-const double collect_tcp_interval = config_express.GetDouble("collect-tcp-interval", 0.2);
-
 // Returns the configure object for accessing config variables from a
 // Returns the configure object for accessing config variables from a
 // scripting language.
 // scripting language.
 ConfigExpress &
 ConfigExpress &

+ 7 - 0
panda/src/express/config_express.h

@@ -21,6 +21,7 @@
 
 
 #include "pandabase.h"
 #include "pandabase.h"
 #include "notifyCategoryProxy.h"
 #include "notifyCategoryProxy.h"
+#include "clockObject.h"
 #include "dconfig.h"
 #include "dconfig.h"
 
 
 ConfigureDecl(config_express, EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS);
 ConfigureDecl(config_express, EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS);
@@ -49,6 +50,12 @@ extern const int patchfile_zone_size;
 extern const bool keep_temporary_files;
 extern const bool keep_temporary_files;
 extern const double average_frame_rate_interval;
 extern const double average_frame_rate_interval;
 
 
+extern ClockObject::Mode clock_mode;
+extern const double clock_frame_rate;
+extern const double clock_degrade_factor;
+extern const double max_dt;
+extern const double sleep_precision;
+
 extern EXPCL_PANDAEXPRESS const bool use_vfs;
 extern EXPCL_PANDAEXPRESS const bool use_vfs;
 
 
 extern EXPCL_PANDAEXPRESS const bool collect_tcp;
 extern EXPCL_PANDAEXPRESS const bool collect_tcp;

+ 1 - 1
panda/src/grutil/config_grutil.cxx

@@ -28,7 +28,7 @@ ConfigureFn(config_grutil) {
   init_libgrutil();
   init_libgrutil();
 }
 }
 
 
-const double frame_rate_meter_update_interval = config_grutil.GetDouble("frame-rate-meter-update-interval", 1.0);
+const double frame_rate_meter_update_interval = config_grutil.GetDouble("frame-rate-meter-update-interval", 1.5);
 const string frame_rate_meter_text_pattern = config_grutil.GetString("frame-rate-meter-text-pattern", "%0.1f fps");
 const string frame_rate_meter_text_pattern = config_grutil.GetString("frame-rate-meter-text-pattern", "%0.1f fps");
 const int frame_rate_meter_layer_sort = config_grutil.GetInt("frame-rate-meter-layer-sort", 1000);
 const int frame_rate_meter_layer_sort = config_grutil.GetInt("frame-rate-meter-layer-sort", 1000);
 const float frame_rate_meter_scale = config_grutil.GetFloat("frame-rate-meter-scale", 0.05f);
 const float frame_rate_meter_scale = config_grutil.GetFloat("frame-rate-meter-scale", 0.05f);

+ 3 - 0
panda/src/pstatclient/pStatClient.cxx

@@ -72,6 +72,9 @@ PStatClient() :
   _collectors_reported = 0;
   _collectors_reported = 0;
   _threads_reported = 0;
   _threads_reported = 0;
 
 
+  // Make sure our clock is in "normal" mode.
+  _clock.set_mode(ClockObject::M_normal);
+
   // We always have a collector at index 0 named "Frame".  This tracks
   // We always have a collector at index 0 named "Frame".  This tracks
   // the total frame time and is the root of all other collectors.  We
   // the total frame time and is the root of all other collectors.  We
   // have to make this one by hand since it's the root.
   // have to make this one by hand since it's the root.