Browse Source

Add pstats timing of wrt calls

David Rose 24 years ago
parent
commit
866507bb17

+ 1 - 1
panda/src/graph/Sources.pp

@@ -4,7 +4,7 @@
 #begin lib_target
   #define TARGET graph
   #define LOCAL_LIBS \
-    putil mathutil
+    pstatclient putil mathutil
 
   #define SOURCES \  
     allAttributesWrapper.I allAttributesWrapper.T  \

+ 4 - 1
panda/src/graph/wrt.I

@@ -392,7 +392,6 @@ cached_wrt_base(const Node *from,
 		InputIterator2 to_arcs_begin, InputIterator2 to_arcs_end,
 		TransitionWrapper &result,
 		TypeHandle graph_type) {
-
   UpdateSeq now = last_graph_update(graph_type);
 
   TransitionWrapper net_from_trans = TransitionWrapper::init_from(result);
@@ -506,6 +505,8 @@ wrt_base(const Node *from,
 	 InputIterator2 to_arcs_begin, InputIterator2 to_arcs_end,
 	 TransitionWrapper &result,
 	 TypeHandle graph_type) {
+  PStatTimer timer(_wrt_pcollector);
+
 #ifndef NDEBUG
   if (!cache_wrt) {
     uncached_wrt_base(from, from_arcs_begin, from_arcs_end,
@@ -874,6 +875,8 @@ template<class TransitionWrapper>
 INLINE_GRAPH Node *
 wrt_subtree(NodeRelation *arc, Node *to, UpdateSeq as_of, UpdateSeq now,
 	    TransitionWrapper &result, TypeHandle graph_type) {
+  PStatTimer timer(_wrt_subtree_pcollector);
+
 #ifndef NDEBUG
   if (!cache_wrt) {
     // If we aren't caching wrt, compute it explicitly.

+ 4 - 3
panda/src/graph/wrt.cxx

@@ -5,6 +5,7 @@
 
 #include "wrt.h"
 
-// There's no real need to have this file, but it's sometimes a little
-// cleaner to guarantee that every .h file gets scanned when this
-// directory is built.
+#ifndef CPPPARSER
+PStatCollector _wrt_pcollector("WRT");
+PStatCollector _wrt_subtree_pcollector("WRT:subtree");
+#endif

+ 5 - 0
panda/src/graph/wrt.h

@@ -13,6 +13,8 @@
 #include "config_graph.h"
 
 #include <typedObject.h>
+#include <pStatCollector.h>
+#include <pStatTimer.h>
 
 class Node;
 
@@ -127,6 +129,9 @@ INLINE_GRAPH Node *
 wrt_subtree(NodeRelation *arc, Node *to, UpdateSeq as_of, UpdateSeq now,
 	    TransitionWrapper &result, TypeHandle graph_type);
 
+// This is used to count the amount of time spend in calls to wrt().
+extern EXPCL_PANDA PStatCollector _wrt_pcollector;
+extern EXPCL_PANDA PStatCollector _wrt_subtree_pcollector;
 
 #ifndef DONT_INLINE_GRAPH
 #include "wrt.I"

+ 1 - 1
panda/src/mathutil/boundingHexahedron.cxx

@@ -304,7 +304,7 @@ set_planes() {
   // transforming it with a -1 matrix.  We do this by ensuring that
   // the centroid is in front of all of the planes (actually, we only
   // need to test the first plane).
-  if (_planes[0].dist_to_plane(_centroid) >= 0) {
+  if (_planes[0].dist_to_plane(_centroid) > 0) {
     // Oops!  We're flipped!  Rebuild the planes in the opposite
     // direction.
     _planes[0] = Planef(_points[0], _points[2], _points[3]);

+ 12 - 0
panda/src/net/connectionWriter.cxx

@@ -111,6 +111,7 @@ send(const Datagram &datagram, const PT(Connection) &connection) {
   }
 }
 
+
 ////////////////////////////////////////////////////////////////////
 //     Function: ConnectionWriter::send
 //       Access: Public
@@ -158,6 +159,17 @@ send(const Datagram &datagram, const PT(Connection) &connection,
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: ConnectionWriter::is_valid_for_udp
+//       Access: Public
+//  Description: Returns true if the datagram is small enough to be
+//               sent over a UDP packet, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool ConnectionWriter::
+is_valid_for_udp(const Datagram &datagram) const {
+  return (int)datagram.get_length() <= maximum_udp_datagram;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: ConnectionWriter::get_manager
 //       Access: Public

+ 2 - 0
panda/src/net/connectionWriter.h

@@ -41,6 +41,8 @@ PUBLISHED:
 	    const PT(Connection) &connection,
 	    const NetAddress &address);
 
+  bool is_valid_for_udp(const Datagram &datagram) const;
+
   ConnectionManager *get_manager() const;
   bool is_immediate() const;
   int get_num_threads() const;

+ 4 - 1
panda/src/pstatclient/Sources.pp

@@ -9,7 +9,9 @@
 
   #define SOURCES \
     config_pstats.cxx config_pstats.h pStatClient.I pStatClient.cxx \
-    pStatClient.h pStatClientControlMessage.cxx \
+    pStatClient.h \
+    pStatClientVersion.I pStatClientVersion.cxx pStatClientVersion.h \
+    pStatClientControlMessage.cxx \
     pStatClientControlMessage.h \
     pStatCollector.I pStatCollector.h \
     pStatCollectorDef.cxx \
@@ -22,6 +24,7 @@
 
   #define INSTALL_HEADERS \
     config_pstats.h pStatClient.I pStatClient.h \
+    pStatClientVersion.I pStatClientVersion.h \
     pStatClientControlMessage.h pStatCollector.I pStatCollector.h \
     pStatCollectorDef.h pStatFrameData.I pStatFrameData.h \
     pStatProperties.h \

+ 1 - 1
panda/src/pstatclient/config_pstats.cxx

@@ -7,7 +7,7 @@
 
 #include <dconfig.h>
 
-Configure(config_pstats);
+ConfigureDef(config_pstats);
 NotifyCategoryDef(pstats, "");
 
 ConfigureFn(config_pstats) {

+ 2 - 0
panda/src/pstatclient/config_pstats.h

@@ -9,9 +9,11 @@
 #include <pandabase.h>
 
 #include <notifyCategoryProxy.h>
+#include <dconfig.h>
 
 // Configure variables for pstats package.
 
+ConfigureDecl(config_pstats, EXPCL_PANDA, EXPTP_PANDA);
 NotifyCategoryDecl(pstats, EXPCL_PANDA, EXPTP_PANDA);
 
 extern EXPCL_PANDA string get_pstats_name();

+ 51 - 21
panda/src/pstatclient/pStatClient.cxx

@@ -325,7 +325,7 @@ make_thread(const string &name) {
   Thread thread;
   thread._name = name;
   thread._is_active = false;
-  thread._last_packet = 0.0;
+  thread._next_packet = 0.0;
   thread._frame_number = 0;
 
   _threads.push_back(thread);
@@ -435,7 +435,7 @@ ns_disconnect() {
   for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
     (*ti)._frame_number = 0;
     (*ti)._is_active = false;
-    (*ti)._last_packet = 0.0;
+    (*ti)._next_packet = 0.0;
     (*ti)._frame_data.clear();
   }
   
@@ -472,7 +472,8 @@ start(int collector_index, int thread_index, float as_of) {
   nassertv(collector_index >= 0 && collector_index < (int)_collectors.size());
   nassertv(thread_index >= 0 && thread_index < (int)_threads.size());
 
-  if (_threads[thread_index]._is_active) {
+  if (_collectors[collector_index]._def->_is_active &&
+      _threads[thread_index]._is_active) {
     if (_collectors[collector_index]._per_thread[thread_index]._nested_count == 0) {
       // This collector wasn't already started in this thread; record
       // a new data point.
@@ -494,7 +495,8 @@ stop(int collector_index, int thread_index, float as_of) {
   nassertv(collector_index >= 0 && collector_index < (int)_collectors.size());
   nassertv(thread_index >= 0 && thread_index < (int)_threads.size());
 
-  if (_threads[thread_index]._is_active) {
+  if (_collectors[collector_index]._def->_is_active &&
+      _threads[thread_index]._is_active) {
     if (_collectors[collector_index]._per_thread[thread_index]._nested_count == 0) {
       pstats_cat.warning()
 	<< "Collector " << get_collector_fullname(collector_index)
@@ -525,8 +527,11 @@ stop(int collector_index, int thread_index, float as_of) {
 ////////////////////////////////////////////////////////////////////
 void PStatClient::
 clear_level(int collector_index, int thread_index) {
-  _collectors[collector_index]._per_thread[thread_index]._has_level = false;
-  _collectors[collector_index]._per_thread[thread_index]._level = 0.0;
+  if (_collectors[collector_index]._def->_is_active &&
+      _threads[thread_index]._is_active) {
+    _collectors[collector_index]._per_thread[thread_index]._has_level = false;
+    _collectors[collector_index]._per_thread[thread_index]._level = 0.0;
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -540,9 +545,12 @@ clear_level(int collector_index, int thread_index) {
 ////////////////////////////////////////////////////////////////////
 void PStatClient::
 set_level(int collector_index, int thread_index, float level) {
-  level *= _collectors[collector_index]._def->_factor;
-  _collectors[collector_index]._per_thread[thread_index]._has_level = true;
-  _collectors[collector_index]._per_thread[thread_index]._level = level;
+  if (_collectors[collector_index]._def->_is_active &&
+      _threads[thread_index]._is_active) {
+    level *= _collectors[collector_index]._def->_factor;
+    _collectors[collector_index]._per_thread[thread_index]._has_level = true;
+    _collectors[collector_index]._per_thread[thread_index]._level = level;
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -558,9 +566,12 @@ set_level(int collector_index, int thread_index, float level) {
 ////////////////////////////////////////////////////////////////////
 void PStatClient::
 add_level(int collector_index, int thread_index, float increment) {
-  increment *= _collectors[collector_index]._def->_factor;
-  _collectors[collector_index]._per_thread[thread_index]._has_level = true;
-  _collectors[collector_index]._per_thread[thread_index]._level += increment;
+  if (_collectors[collector_index]._def->_is_active &&
+      _threads[thread_index]._is_active) {
+    increment *= _collectors[collector_index]._def->_factor;
+    _collectors[collector_index]._per_thread[thread_index]._has_level = true;
+    _collectors[collector_index]._per_thread[thread_index]._level += increment;
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -626,23 +637,42 @@ void PStatClient::
 transmit_frame_data(int thread_index) {
   nassertv(thread_index >= 0 && thread_index < (int)_threads.size());
   if (_is_connected && _threads[thread_index]._is_active) {
-    
+
     // We don't want to send too many packets in a hurry and flood the
-    // server.  In fact, we don't want to send more than
-    // _max_rate packets per second, per thread.
-    float min_packet_delay = 1.0 / _max_rate;
+    // server.  Check that enough time has elapsed for us to send a
+    // new packet.  If not, we'll drop this packet into the void and
+    // send a new one next time around.
     float now = _clock.get_real_time();
-
-    if (now - _threads[thread_index]._last_packet > min_packet_delay) {
-      nassertv(_got_udp_port);
+    if (now >= _threads[thread_index]._next_packet) {
+      // We don't want to send more than _max_rate UDP-size packets
+      // per second, per thread.
+      float packet_delay = 1.0 / _max_rate;
       
       // Send new data.
       NetDatagram datagram;
+      // We always start with a zero byte, to differentiate it from a
+      // control message.
+      datagram.add_uint8(0);
+
       datagram.add_uint16(thread_index);
       datagram.add_uint32(_threads[thread_index]._frame_number);
       _threads[thread_index]._frame_data.write_datagram(datagram);
-      _writer.send(datagram, _udp_connection, _server);
-      _threads[thread_index]._last_packet = now;
+
+      if (_writer.is_valid_for_udp(datagram)) {
+        nassertv(_got_udp_port);
+        _writer.send(datagram, _udp_connection, _server);
+
+      } else {
+        _writer.send(datagram, _tcp_connection);
+        // If our packets are so large that we must ship them via TCP,
+        // then artificially slow down the packet rate even further.
+        int packet_ratio = 
+          (datagram.get_length() + maximum_udp_datagram - 1) /
+          maximum_udp_datagram;
+        packet_delay *= (float)packet_ratio;
+      }
+
+      _threads[thread_index]._next_packet = now + packet_delay;
     }
   }
 }

+ 1 - 1
panda/src/pstatclient/pStatClient.h

@@ -127,7 +127,7 @@ private:
     PStatFrameData _frame_data;
     bool _is_active;
     int _frame_number;
-    float _last_packet;
+    float _next_packet;
   };
   typedef vector<Thread> Threads;
   Threads _threads;

+ 7 - 2
panda/src/pstatclient/pStatClientControlMessage.cxx

@@ -5,6 +5,7 @@
 
 #include "config_pstats.h"
 #include "pStatClientControlMessage.h"
+#include "pStatClientVersion.h"
 
 #include <datagram.h>
 #include <datagramIterator.h>
@@ -68,7 +69,7 @@ encode(Datagram &datagram) const {
 //               Returns true on success, false on error.
 ////////////////////////////////////////////////////////////////////
 bool PStatClientControlMessage::
-decode(const Datagram &datagram) {
+decode(const Datagram &datagram, PStatClientVersion *version) {
   DatagramIterator source(datagram);
   _type = (Type)source.get_uint8();
   
@@ -91,7 +92,7 @@ decode(const Datagram &datagram) {
       _collectors.clear();
       for (int i = 0; i < num; i++) {
 	PStatCollectorDef *def = new PStatCollectorDef;
-	def->read_datagram(source);
+	def->read_datagram(source, version);
 	_collectors.push_back(def);
       }
     }
@@ -108,6 +109,10 @@ decode(const Datagram &datagram) {
     }
     break;
 
+  case T_datagram:
+    // Not, strictly speaking, a control message.
+    return false;
+
   default:
     pstats_cat.error()
       << "Read invalid PStatClientControlMessage type: " << (int)_type << "\n";

+ 4 - 2
panda/src/pstatclient/pStatClientControlMessage.h

@@ -13,6 +13,7 @@
 #include <vector>
 
 class Datagram;
+class PStatClientVersion;
 
 ////////////////////////////////////////////////////////////////////
 // 	 Class : PStatClientControlMessage
@@ -25,13 +26,14 @@ public:
   PStatClientControlMessage();
 
   void encode(Datagram &datagram) const;
-  bool decode(const Datagram &datagram);
+  bool decode(const Datagram &datagram, PStatClientVersion *version);
 
   enum Type {
-    T_invalid,
+    T_datagram = 0,
     T_hello,
     T_define_collectors,
     T_define_threads,
+    T_invalid
   };
 
   Type _type;

+ 49 - 0
panda/src/pstatclient/pStatClientVersion.I

@@ -0,0 +1,49 @@
+// Filename: pStatClientVersion.I
+// Created by:  drose (21May01)
+// 
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClientVersion::get_major_version
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE int PStatClientVersion::
+get_major_version() const {
+  return _major_version;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClientVersion::get_minor_version
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE int PStatClientVersion::
+get_minor_version() const {
+  return _minor_version;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClientVersion::set_version
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void PStatClientVersion::
+set_version(int major, int minor) {
+  _major_version = major;
+  _minor_version = minor;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClientVersion::is_at_least
+//       Access: Public
+//  Description: Returns true if the client version is at least the
+//               indicated major/minor version number, false
+//               otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool PStatClientVersion::
+is_at_least(int major, int minor) const {
+  return (_major_version > major ||
+          (_major_version == major && _minor_version >= minor));
+}

+ 19 - 0
panda/src/pstatclient/pStatClientVersion.cxx

@@ -0,0 +1,19 @@
+// Filename: pStatClientVersion.cxx
+// Created by:  drose (21May01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "pStatClientVersion.h"
+#include "pStatProperties.h"
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClientVersion::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+PStatClientVersion::
+PStatClientVersion() {
+  _major_version = get_current_pstat_major_version();
+  _minor_version = get_current_pstat_minor_version();
+}

+ 39 - 0
panda/src/pstatclient/pStatClientVersion.h

@@ -0,0 +1,39 @@
+// Filename: pStatClientVersion.h
+// Created by:  drose (21May01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef PSTATCLIENTVERSION_H
+#define PSTATCLIENTVERSION_H
+
+#include <pandabase.h>
+
+#include <referenceCount.h>
+#include <pointerTo.h>
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : PStatClientVersion
+// Description : Records the version number of a particular client.
+//               Normally this will be the same as
+//               get_current_pstat_major/minor_version().
+////////////////////////////////////////////////////////////////////
+class PStatClientVersion : public ReferenceCount {
+public:
+  PStatClientVersion();
+
+  INLINE int get_major_version() const;
+  INLINE int get_minor_version() const;
+
+  INLINE void set_version(int major, int minor);
+
+  INLINE bool is_at_least(int major, int minor) const;
+
+private:
+  int _major_version;
+  int _minor_version;
+};
+
+#include "pStatClientVersion.I"
+
+#endif
+

+ 4 - 1
panda/src/pstatclient/pStatCollectorDef.cxx

@@ -22,6 +22,7 @@ PStatCollectorDef() {
   _sort = -1;
   _suggested_scale = 0.0;
   _factor = 1.0;
+  _is_active = true;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -39,6 +40,7 @@ PStatCollectorDef(int index, const string &name) :
   _sort = -1;
   _suggested_scale = 0.0;
   _factor = 1.0;
+  _is_active = true;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -55,6 +57,7 @@ set_parent(const PStatCollectorDef &parent) {
   _level_units = parent._level_units;
   _suggested_scale = parent._suggested_scale;
   _factor = parent._factor;
+  _is_active = parent._is_active;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -81,7 +84,7 @@ write_datagram(Datagram &destination) const {
 //  Description: Extracts the collectorDef definition from the datagram.
 ////////////////////////////////////////////////////////////////////
 void PStatCollectorDef::
-read_datagram(DatagramIterator &source) {
+read_datagram(DatagramIterator &source, PStatClientVersion *) {
   _index = source.get_int16();
   _name = source.get_string();
   _parent_index = source.get_int16();

+ 3 - 1
panda/src/pstatclient/pStatCollectorDef.h

@@ -13,6 +13,7 @@
 class Datagram;
 class DatagramIterator;
 class PStatClient;
+class PStatClientVersion;
 
 ////////////////////////////////////////////////////////////////////
 // 	 Class : PStatCollectorDef
@@ -26,7 +27,7 @@ public:
   void set_parent(const PStatCollectorDef &parent);
 
   void write_datagram(Datagram &destination) const;
-  void read_datagram(DatagramIterator &source);
+  void read_datagram(DatagramIterator &source, PStatClientVersion *version);
 
   int _index;
   string _name;
@@ -36,6 +37,7 @@ public:
   string _level_units;
   float _suggested_scale;
   float _factor;
+  bool _is_active;
 };
 
 #endif

+ 2 - 1
panda/src/pstatclient/pStatFrameData.cxx

@@ -4,6 +4,7 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "pStatFrameData.h"
+#include "pStatClientVersion.h"
 
 #include <datagram.h>
 #include <datagramIterator.h>
@@ -35,7 +36,7 @@ write_datagram(Datagram &destination) const {
 //  Description: Extracts the FrameData definition from the datagram.
 ////////////////////////////////////////////////////////////////////
 void PStatFrameData::
-read_datagram(DatagramIterator &source) {
+read_datagram(DatagramIterator &source, PStatClientVersion *) {
   clear();
 
   int i;

+ 2 - 1
panda/src/pstatclient/pStatFrameData.h

@@ -14,6 +14,7 @@
 
 class Datagram;
 class DatagramIterator;
+class PStatClientVersion;
 
 ////////////////////////////////////////////////////////////////////
 // 	 Class : PStatFrameData
@@ -47,7 +48,7 @@ public:
   INLINE float get_level(int n) const;
 
   void write_datagram(Datagram &destination) const;
-  void read_datagram(DatagramIterator &source);
+  void read_datagram(DatagramIterator &source, PStatClientVersion *version);
 
 private:  
   class DataPoint {

+ 132 - 44
panda/src/pstatclient/pStatProperties.cxx

@@ -6,6 +6,14 @@
 #include "pStatProperties.h"
 #include "pStatCollectorDef.h"
 #include "pStatClient.h"
+#include "config_pstats.h"
+
+#include <ctype.h>
+
+static const int current_pstat_major_version = 2;
+static const int current_pstat_minor_version = 1;
+// Initialized at 2.0 on 5/18/01, when version numbers were first added.
+// Incremented to 2.1 on 5/21/01 to add support for TCP frame data.
 
 ////////////////////////////////////////////////////////////////////
 //     Function: get_current_pstat_major_version
@@ -19,7 +27,7 @@
 ////////////////////////////////////////////////////////////////////
 int
 get_current_pstat_major_version() {
-  return 2;
+  return current_pstat_major_version;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -34,7 +42,7 @@ get_current_pstat_major_version() {
 ////////////////////////////////////////////////////////////////////
 int
 get_current_pstat_minor_version() {
-  return 0;
+  return current_pstat_minor_version;
 }
 
 
@@ -67,12 +75,14 @@ struct ColorDef {
 };
 
 struct TimeCollectorProperties {
+  bool is_active;
   const char *name;
   ColorDef color;
   float suggested_scale;
 };
 
 struct LevelCollectorProperties {
+  bool is_active;
   const char *name;
   ColorDef color;
   const char *units;
@@ -81,57 +91,50 @@ struct LevelCollectorProperties {
 };
 
 static TimeCollectorProperties time_properties[] = {
-  { "App",                              { 0.0, 1.0, 1.0 },  1.0 / 30.0 },
-  { "App:Animation",                    { 1.0, 0.0, 1.0 } },
-  { "App:Collisions",                   { 1.0, 0.5, 0.0 } },
-  { "App:Data graph",                   { 0.5, 0.8, 0.4 } },
-  { "App:Show code",                    { 0.8, 0.2, 1.0 } },
-  { "Cull",                             { 0.0, 1.0, 0.0 },  1.0 / 30.0 },
-  { "Draw",                             { 1.0, 0.0, 0.0 },  1.0 / 30.0 },
-  { "Draw:Swap buffers",                { 0.5, 1.0, 0.8 } },
-  { "Draw:Clear",                       { 0.5, 0.7, 0.7 } },
-  { "Draw:Show fps",                    { 0.5, 0.8, 1.0 } },
-  { "Draw:Make current",                { 1.0, 0.6, 0.3 } },
-  { NULL }
+  { 1, "App",                              { 0.0, 1.0, 1.0 },  1.0 / 30.0 },
+  { 1, "App:Animation",                    { 1.0, 0.0, 1.0 } },
+  { 1, "App:Collisions",                   { 1.0, 0.5, 0.0 } },
+  { 1, "App:Data graph",                   { 0.5, 0.8, 0.4 } },
+  { 1, "App:Show code",                    { 0.8, 0.2, 1.0 } },
+  { 1, "Cull",                             { 0.0, 1.0, 0.0 },  1.0 / 30.0 },
+  { 1, "Draw",                             { 1.0, 0.0, 0.0 },  1.0 / 30.0 },
+  { 1, "Draw:Swap buffers",                { 0.5, 1.0, 0.8 } },
+  { 1, "Draw:Clear",                       { 0.5, 0.7, 0.7 } },
+  { 1, "Draw:Show fps",                    { 0.5, 0.8, 1.0 } },
+  { 1, "Draw:Make current",                { 1.0, 0.6, 0.3 } },
+  { 0, "WRT",                              { 0.0, 0.0, 1.0 } },
+  { 0, "WRT:Subtree",                      { 0.3, 1.0, 0.3 } },
+  { 0, NULL }
 };
 
 static LevelCollectorProperties level_properties[] = {
-  { "Texture usage",                    { 1.0, 0.0, 0.0 },  "MB", 12, 1048576 },
-  { "Texture usage:Active",             { 1.0, 1.0, 0.0 } },
-  { "Texture memory",                   { 0.0, 0.0, 1.0 },  "MB", 12, 1048576 },
-  { "Texture memory:In use",            { 0.0, 1.0, 1.0 } },
-  { "Vertices",                         { 0.5, 0.2, 0.0 },  "K", 10, 1000 },
-  { "Vertices:Other",                   { 0.2, 0.2, 0.2 } },
-  { "Vertices:Triangles",               { 0.8, 0.8, 0.8 } },
-  { "Vertices:Triangle fans",           { 0.8, 0.5, 0.2 } },
-  { "Vertices:Triangle strips",         { 0.2, 0.5, 0.8 } },
-  { "Nodes",                            { 0.4, 0.2, 0.8 },  "", 500.0 },
-  { "Nodes:GeomNodes",                  { 0.8, 0.2, 0.0 } },
-  { "State changes",                    { 1.0, 0.5, 0.2 },  "", 500.0 },
-  { "State changes:Transforms",         { 0.2, 0.2, 0.8 }, },
-  { "State changes:Textures",           { 0.8, 0.2, 0.2 }, },
-  { NULL }
+  { 1, "Texture usage",                    { 1.0, 0.0, 0.0 },  "MB", 12, 1048576 },
+  { 1, "Texture usage:Active",             { 1.0, 1.0, 0.0 } },
+  { 1, "Texture memory",                   { 0.0, 0.0, 1.0 },  "MB", 12, 1048576 },
+  { 1, "Texture memory:In use",            { 0.0, 1.0, 1.0 } },
+  { 1, "Vertices",                         { 0.5, 0.2, 0.0 },  "K", 10, 1000 },
+  { 1, "Vertices:Other",                   { 0.2, 0.2, 0.2 } },
+  { 1, "Vertices:Triangles",               { 0.8, 0.8, 0.8 } },
+  { 1, "Vertices:Triangle fans",           { 0.8, 0.5, 0.2 } },
+  { 1, "Vertices:Triangle strips",         { 0.2, 0.5, 0.8 } },
+  { 1, "Nodes",                            { 0.4, 0.2, 0.8 },  "", 500.0 },
+  { 1, "Nodes:GeomNodes",                  { 0.8, 0.2, 0.0 } },
+  { 1, "State changes",                    { 1.0, 0.5, 0.2 },  "", 500.0 },
+  { 1, "State changes:Transforms",         { 0.2, 0.2, 0.8 }, },
+  { 1, "State changes:Textures",           { 0.8, 0.2, 0.2 }, },
+  { 0, NULL }
 };
 
     
 ////////////////////////////////////////////////////////////////////
-//     Function: initialize_collector_def
-//  Description: This is the only accessor function into this table.
-//               The PStatCollectorDef constructor calls it when a new
-//               PStatCollectorDef is created.  It should look up in
-//               the table and find a matching definition for this def
-//               by name; if one is found, the properties are applied.
+//     Function: initialize_collector_def_from_table
+//  Description: Looks up the collector in the compiled-in table
+//               defined above, and sets its properties appropriately
+//               if it is found.
 ////////////////////////////////////////////////////////////////////
-void
-initialize_collector_def(PStatClient *client, PStatCollectorDef *def) {
+static void
+initialize_collector_def_from_table(const string &fullname, PStatCollectorDef *def) {
   int i;
-  string fullname;
-
-  if (def->_index == 0) {
-    fullname = def->_name;
-  } else {
-    fullname = client->get_collector_fullname(def->_index);
-  }
 
   for (i = 0;
        time_properties[i].name != (const char *)NULL; 
@@ -139,6 +142,7 @@ initialize_collector_def(PStatClient *client, PStatCollectorDef *def) {
     const TimeCollectorProperties &tp = time_properties[i];
     if (fullname == tp.name) {
       def->_sort = i;
+      def->_is_active = tp.is_active;
       def->_suggested_color.set(tp.color.r, tp.color.g, tp.color.b);
       if (tp.suggested_scale != 0.0) {
         def->_suggested_scale = tp.suggested_scale;
@@ -153,6 +157,7 @@ initialize_collector_def(PStatClient *client, PStatCollectorDef *def) {
     const LevelCollectorProperties &lp = level_properties[i];
     if (fullname == lp.name) {
       def->_sort = i;
+      def->_is_active = lp.is_active;
       def->_suggested_color.set(lp.color.r, lp.color.g, lp.color.b);
       if (lp.suggested_scale != 0.0) {
         def->_suggested_scale = lp.suggested_scale;
@@ -168,4 +173,87 @@ initialize_collector_def(PStatClient *client, PStatCollectorDef *def) {
   }
 }
 
+    
+////////////////////////////////////////////////////////////////////
+//     Function: initialize_collector_def
+//  Description: This is the only accessor function into this table.
+//               The PStatCollectorDef constructor calls it when a new
+//               PStatCollectorDef is created.  It should look up in
+//               the table and find a matching definition for this def
+//               by name; if one is found, the properties are applied.
+////////////////////////////////////////////////////////////////////
+void
+initialize_collector_def(PStatClient *client, PStatCollectorDef *def) {
+  string fullname;
+
+  if (def->_index == 0) {
+    fullname = def->_name;
+  } else {
+    fullname = client->get_collector_fullname(def->_index);
+  }
+
+  // First, check the compiled-in defaults.
+  initialize_collector_def_from_table(fullname, def);
+
+  // Then, look to Config for more advice.  To do this, we first
+  // change the name to something more like a Config variable name.
+  // We replace colons and spaces with hyphens, eliminate other
+  // punctuation, and make all letters lowercase.
+
+  string config_name;
+  string::const_iterator ni;
+  for (ni = fullname.begin(); ni != fullname.end(); ++ni) {
+    switch (*ni) {
+    case ':':
+    case ' ':
+    case '\n':
+    case '\r':
+    case '\t':
+      config_name += '-';
+      break;
+
+    default:
+      if (isalnum(*ni)) {
+        config_name += tolower(*ni);
+      }
+    }
+  }
+
+  def->_is_active =
+    config_pstats.GetBool("pstats-active-" + config_name, def->_is_active);
+  def->_sort =
+    config_pstats.GetInt("pstats-sort-" + config_name, def->_sort);
+  def->_suggested_scale =
+    config_pstats.GetFloat("pstats-scale-" + config_name, def->_suggested_scale);
+  def->_level_units =
+    config_pstats.GetString("pstats-units-" + config_name, def->_level_units);
+  def->_level_units =
+    config_pstats.GetString("pstats-units-" + config_name, def->_level_units);
+  if (!config_pstats.GetString("pstats-factor-" + config_name, "").empty()) {
+    def->_factor =
+      1.0/config_pstats.GetFloat("pstats-factor-" + config_name, 1.0);
+  }
+
+  // Get and decode the color string.  We allow any three
+  // floating-point numbers, with any kind of non-digit characters
+  // between them.
+  string color_str = 
+    config_pstats.GetString("pstats-color-" + config_name, "");
+  if (!color_str.empty()) {
+    const char *cstr = color_str.c_str();
+    const char *p = cstr;
+
+    int i = 0;
+    while (i < 3 && *p != '\0') {
+      while (*p != '\0' && !(isdigit(*p) || *p == '.')) {
+        p++;
+      }
+      char *q;
+      def->_suggested_color[i] = strtod(p, &q);
+      p = q;
+      i++;
+    }
+  }
+}
+
 #endif // DO_PSTATS

+ 2 - 1
pandatool/src/pstatserver/pStatClientData.h

@@ -10,6 +10,7 @@
 
 #include "pStatThreadData.h"
 
+#include <pStatClientVersion.h>
 #include <referenceCount.h>
 #include <pointerTo.h>
 
@@ -23,7 +24,7 @@ class PStatReader;
 //               with any one particular frame or thread: the list of
 //               collectors and threads, for instance.
 ////////////////////////////////////////////////////////////////////
-class PStatClientData : public ReferenceCount {
+class PStatClientData : public PStatClientVersion {
 public:
   PStatClientData(PStatReader *reader);
   ~PStatClientData();

+ 27 - 8
pandatool/src/pstatserver/pStatReader.cxx

@@ -158,8 +158,12 @@ receive_datagram(const NetDatagram &datagram) {
 
   if (connection == _tcp_connection) {
     PStatClientControlMessage message;
-    if (message.decode(datagram)) {
+    if (message.decode(datagram, _client_data)) {
       handle_client_control_message(message);
+
+    } else if (message._type == PStatClientControlMessage::T_datagram) {
+      handle_client_udp_data(datagram);
+      
     } else {
       nout << "Got unexpected message from client.\n";
     }
@@ -183,15 +187,16 @@ handle_client_control_message(const PStatClientControlMessage &message) {
   switch (message._type) {
   case PStatClientControlMessage::T_hello:
     {
-      int current_major_version = get_current_pstat_major_version();
-      int current_minor_version = get_current_pstat_minor_version();
+      _client_data->set_version(message._major_version, message._minor_version);
+      int server_major_version = get_current_pstat_major_version();
+      int server_minor_version = get_current_pstat_minor_version();
 
-      if (message._major_version != current_major_version ||
-          (message._major_version == current_major_version &&
-           message._minor_version > current_minor_version)) {
+      if (message._major_version != server_major_version ||
+          (message._major_version == server_major_version &&
+           message._minor_version > server_minor_version)) {
         _monitor->bad_version(message._client_hostname, message._client_progname,
                               message._major_version, message._minor_version,
-                              current_major_version, current_minor_version);
+                              server_major_version, server_minor_version);
         _monitor->close();
       } else {
         _monitor->hello_from(message._client_hostname, message._client_progname);
@@ -233,12 +238,26 @@ handle_client_control_message(const PStatClientControlMessage &message) {
 ////////////////////////////////////////////////////////////////////
 void PStatReader::
 handle_client_udp_data(const Datagram &datagram) {
+  if (!_monitor->is_client_known()) {
+    // If we haven't heard a "hello" from the client yet, we don't
+    // know what version data it will be sending us, so we can't
+    // decode the data.  Chances are good we can't display it sensibly
+    // yet anyway.  Ignore frame data until we get that hello.
+    return;
+  }
+
   DatagramIterator source(datagram);
 
+  if (_client_data->is_at_least(2, 1)) {
+    // Throw away the zero byte at the beginning.
+    int initial_byte = source.get_uint8();
+    nassertv(initial_byte == 0);
+  }
+
   int thread_index = source.get_uint16();
   int frame_number = source.get_uint32();
   PStatFrameData *frame_data = new PStatFrameData;
-  frame_data->read_datagram(source);
+  frame_data->read_datagram(source, _client_data);
 
   // Check to see if any new collectors have level data.
   int num_levels = frame_data->get_num_levels();