瀏覽代碼

Finishing migration of RenderState and RenderAttrib to SimpleHashMap and garbage_collect() models, working towards threaded pipelining

David Rose 14 年之前
父節點
當前提交
229cf67ae5
共有 67 個文件被更改,包括 1156 次插入243 次删除
  1. 3 5
      panda/src/egg2pg/eggRenderState.cxx
  2. 6 1
      panda/src/framework/pandaFramework.cxx
  3. 25 3
      panda/src/pgraph/alphaTestAttrib.cxx
  4. 1 0
      panda/src/pgraph/alphaTestAttrib.h
  5. 17 0
      panda/src/pgraph/antialiasAttrib.cxx
  6. 1 0
      panda/src/pgraph/antialiasAttrib.h
  7. 23 14
      panda/src/pgraph/audioVolumeAttrib.cxx
  8. 1 0
      panda/src/pgraph/audioVolumeAttrib.h
  9. 18 1
      panda/src/pgraph/auxBitplaneAttrib.cxx
  10. 1 0
      panda/src/pgraph/auxBitplaneAttrib.h
  11. 32 0
      panda/src/pgraph/clipPlaneAttrib.cxx
  12. 1 0
      panda/src/pgraph/clipPlaneAttrib.h
  13. 20 0
      panda/src/pgraph/colorAttrib.cxx
  14. 1 0
      panda/src/pgraph/colorAttrib.h
  15. 21 0
      panda/src/pgraph/colorBlendAttrib.cxx
  16. 1 0
      panda/src/pgraph/colorBlendAttrib.h
  17. 18 12
      panda/src/pgraph/colorScaleAttrib.cxx
  18. 1 0
      panda/src/pgraph/colorScaleAttrib.h
  19. 17 0
      panda/src/pgraph/colorWriteAttrib.cxx
  20. 1 0
      panda/src/pgraph/colorWriteAttrib.h
  21. 6 4
      panda/src/pgraph/config_pgraph.cxx
  22. 18 0
      panda/src/pgraph/cullBinAttrib.cxx
  23. 1 0
      panda/src/pgraph/cullBinAttrib.h
  24. 18 0
      panda/src/pgraph/cullFaceAttrib.cxx
  25. 1 0
      panda/src/pgraph/cullFaceAttrib.h
  26. 17 0
      panda/src/pgraph/depthOffsetAttrib.cxx
  27. 1 0
      panda/src/pgraph/depthOffsetAttrib.h
  28. 17 0
      panda/src/pgraph/depthTestAttrib.cxx
  29. 1 0
      panda/src/pgraph/depthTestAttrib.h
  30. 17 0
      panda/src/pgraph/depthWriteAttrib.cxx
  31. 1 0
      panda/src/pgraph/depthWriteAttrib.h
  32. 17 0
      panda/src/pgraph/fogAttrib.cxx
  33. 1 0
      panda/src/pgraph/fogAttrib.h
  34. 32 0
      panda/src/pgraph/lightAttrib.cxx
  35. 1 0
      panda/src/pgraph/lightAttrib.h
  36. 25 3
      panda/src/pgraph/lightRampAttrib.cxx
  37. 1 0
      panda/src/pgraph/lightRampAttrib.h
  38. 17 0
      panda/src/pgraph/materialAttrib.cxx
  39. 1 0
      panda/src/pgraph/materialAttrib.h
  40. 14 0
      panda/src/pgraph/renderAttrib.I
  41. 154 61
      panda/src/pgraph/renderAttrib.cxx
  42. 15 3
      panda/src/pgraph/renderAttrib.h
  43. 19 0
      panda/src/pgraph/renderModeAttrib.cxx
  44. 1 0
      panda/src/pgraph/renderModeAttrib.h
  45. 43 7
      panda/src/pgraph/renderState.I
  46. 261 119
      panda/src/pgraph/renderState.cxx
  47. 23 7
      panda/src/pgraph/renderState.h
  48. 17 0
      panda/src/pgraph/rescaleNormalAttrib.cxx
  49. 1 0
      panda/src/pgraph/rescaleNormalAttrib.h
  50. 17 0
      panda/src/pgraph/scissorAttrib.cxx
  51. 1 0
      panda/src/pgraph/scissorAttrib.h
  52. 17 0
      panda/src/pgraph/shadeModelAttrib.cxx
  53. 1 0
      panda/src/pgraph/shadeModelAttrib.h
  54. 34 0
      panda/src/pgraph/shaderAttrib.cxx
  55. 1 0
      panda/src/pgraph/shaderAttrib.h
  56. 19 0
      panda/src/pgraph/stencilAttrib.cxx
  57. 1 0
      panda/src/pgraph/stencilAttrib.h
  58. 28 0
      panda/src/pgraph/texGenAttrib.cxx
  59. 1 0
      panda/src/pgraph/texGenAttrib.h
  60. 25 0
      panda/src/pgraph/texMatrixAttrib.cxx
  61. 1 0
      panda/src/pgraph/texMatrixAttrib.h
  62. 38 0
      panda/src/pgraph/textureAttrib.cxx
  63. 1 0
      panda/src/pgraph/textureAttrib.h
  64. 3 2
      panda/src/pgraph/transformState.cxx
  65. 0 1
      panda/src/pgraph/transformState.h
  66. 17 0
      panda/src/pgraph/transparencyAttrib.cxx
  67. 1 0
      panda/src/pgraph/transparencyAttrib.h

+ 3 - 5
panda/src/egg2pg/eggRenderState.cxx

@@ -428,11 +428,9 @@ fill_state(EggPrimitive *egg_prim) {
 int EggRenderState::
 int EggRenderState::
 compare_to(const EggRenderState &other) const {
 compare_to(const EggRenderState &other) const {
   if (_state != other._state) {
   if (_state != other._state) {
-    if ((*_state) < (*other._state)) {
-      return -1;
-    }
-    if ((*other._state) < (*_state)) {
-      return 1;
+    int c = _state->compare_to(*other._state);
+    if (c != 0) {
+      return c;
     }
     }
   }
   }
   if (_hidden != other._hidden) {
   if (_hidden != other._hidden) {

+ 6 - 1
panda/src/framework/pandaFramework.cxx

@@ -29,6 +29,8 @@
 #include "throw_event.h"
 #include "throw_event.h"
 #include "executionEnvironment.h"
 #include "executionEnvironment.h"
 #include "sceneGraphAnalyzer.h"
 #include "sceneGraphAnalyzer.h"
+#include "transformState.h"
+#include "renderState.h"
 
 
 LoaderOptions PandaFramework::_loader_options;
 LoaderOptions PandaFramework::_loader_options;
 
 
@@ -136,6 +138,7 @@ open_framework(int &argc, char **&argv) {
 
 
   if (garbage_collect_states) {
   if (garbage_collect_states) {
     PT(GenericAsyncTask) task = new GenericAsyncTask("garbage_collect", task_garbage_collect, this);
     PT(GenericAsyncTask) task = new GenericAsyncTask("garbage_collect", task_garbage_collect, this);
+    task->set_sort(46);
     _task_mgr.add(task);
     _task_mgr.add(task);
   }
   }
 
 
@@ -1630,9 +1633,11 @@ task_clear_text(GenericAsyncTask *task, void *data) {
 //       Access: Public, Static
 //       Access: Public, Static
 //  Description: This task is created automatically if
 //  Description: This task is created automatically if
 //               garbage_collect_states is true.  It calls the needed
 //               garbage_collect_states is true.  It calls the needed
-//               TransformState::garbage_collect() method each frame.
+//               TransformState::garbage_collect() and
+//               RenderState::garbage_collect() methods each frame.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 AsyncTask::DoneStatus PandaFramework::
 AsyncTask::DoneStatus PandaFramework::
 task_garbage_collect(GenericAsyncTask *task, void *data) {
 task_garbage_collect(GenericAsyncTask *task, void *data) {
   TransformState::garbage_collect();
   TransformState::garbage_collect();
+  RenderState::garbage_collect();
 }
 }

+ 25 - 3
panda/src/pgraph/alphaTestAttrib.cxx

@@ -79,11 +79,33 @@ compare_to_impl(const RenderAttrib *other) const {
   const AlphaTestAttrib *ta;
   const AlphaTestAttrib *ta;
   DCAST_INTO_R(ta, other, 0);
   DCAST_INTO_R(ta, other, 0);
   int compare_result = ((int)_mode - (int)ta->_mode) ;
   int compare_result = ((int)_mode - (int)ta->_mode) ;
-  if (compare_result!=0) {
+  if (compare_result != 0) {
     return compare_result;
     return compare_result;
-  } else {
-    return (int) (255.0f*(_reference_alpha - ta->_reference_alpha));
   }
   }
+   
+  if (_reference_alpha != ta->_reference_alpha) {
+    return _reference_alpha < ta->_reference_alpha ? -1 : 1;
+  }
+
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AlphaTestAttrib::get_hash_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived RenderAttrib
+//               types to return a unique hash for these particular
+//               properties.  RenderAttribs that compare the same with
+//               compare_to_impl(), above, should return the same
+//               hash; RenderAttribs that compare differently should
+//               return a different hash.
+////////////////////////////////////////////////////////////////////
+size_t AlphaTestAttrib::
+get_hash_impl() const {
+  size_t hash = 0;
+  hash = int_hash::add_hash(hash, (int)_mode);
+  hash = float_hash().add_hash(hash, _reference_alpha);
+  return hash;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 1 - 0
panda/src/pgraph/alphaTestAttrib.h

@@ -43,6 +43,7 @@ public:
 
 
 protected:
 protected:
   virtual int compare_to_impl(const RenderAttrib *other) const;
   virtual int compare_to_impl(const RenderAttrib *other) const;
+  virtual size_t get_hash_impl() const;
 
 
 private:
 private:
   PandaCompareFunc _mode;
   PandaCompareFunc _mode;

+ 17 - 0
panda/src/pgraph/antialiasAttrib.cxx

@@ -153,6 +153,23 @@ compare_to_impl(const RenderAttrib *other) const {
   return 0;
   return 0;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: AntialiasAttrib::get_hash_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived RenderAttrib
+//               types to return a unique hash for these particular
+//               properties.  RenderAttribs that compare the same with
+//               compare_to_impl(), above, should return the same
+//               hash; RenderAttribs that compare differently should
+//               return a different hash.
+////////////////////////////////////////////////////////////////////
+size_t AntialiasAttrib::
+get_hash_impl() const {
+  size_t hash = 0;
+  hash = int_hash::add_hash(hash, (int)_mode);
+  return hash;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: AntialiasAttrib::compose_impl
 //     Function: AntialiasAttrib::compose_impl
 //       Access: Protected, Virtual
 //       Access: Protected, Virtual

+ 1 - 0
panda/src/pgraph/antialiasAttrib.h

@@ -59,6 +59,7 @@ public:
 
 
 protected:
 protected:
   virtual int compare_to_impl(const RenderAttrib *other) const;
   virtual int compare_to_impl(const RenderAttrib *other) const;
+  virtual size_t get_hash_impl() const;
   virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const;
   virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const;
 
 
 private:
 private:

+ 23 - 14
panda/src/pgraph/audioVolumeAttrib.cxx

@@ -150,24 +150,33 @@ compare_to_impl(const RenderAttrib *other) const {
   const AudioVolumeAttrib *ta;
   const AudioVolumeAttrib *ta;
   DCAST_INTO_R(ta, other, 0);
   DCAST_INTO_R(ta, other, 0);
 
 
-  if (is_off() != ta->is_off()) {
-    if (pgraph_cat.is_spam()) {
-      pgraph_cat.spam()
-        << "Comparing " << (int)is_off() << " to " << (int)ta->is_off() << " result = "
-        << (int)is_off() - (int)ta->is_off() << "\n";
-    }
-    
-    return (int)is_off() - (int)ta->is_off();
+  if (_off != ta->_off) {
+    return (int)_off - (int)ta->_off;
   }
   }
 
 
-  int result = int(_volume * 1000.0f) - int(ta->_volume * 1000.0f);
-  if (pgraph_cat.is_spam()) {
-    pgraph_cat.spam()
-      << "Comparing " << _volume << " to " << ta->_volume << " result = "
-      << result << "\n";
+  if (_volume != ta->_volume) {
+    return _volume < ta->_volume ? -1 : 1;
   }
   }
 
 
-  return result;
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioVolumeAttrib::get_hash_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived RenderAttrib
+//               types to return a unique hash for these particular
+//               properties.  RenderAttribs that compare the same with
+//               compare_to_impl(), above, should return the same
+//               hash; RenderAttribs that compare differently should
+//               return a different hash.
+////////////////////////////////////////////////////////////////////
+size_t AudioVolumeAttrib::
+get_hash_impl() const {
+  size_t hash = 0;
+  hash = int_hash::add_hash(hash, (int)_off);
+  hash = float_hash().add_hash(hash, _volume);
+  return hash;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 1 - 0
panda/src/pgraph/audioVolumeAttrib.h

@@ -48,6 +48,7 @@ public:
 
 
 protected:
 protected:
   virtual int compare_to_impl(const RenderAttrib *other) const;
   virtual int compare_to_impl(const RenderAttrib *other) const;
+  virtual size_t get_hash_impl() const;
   virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const;
   virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const;
   virtual CPT(RenderAttrib) invert_compose_impl(const RenderAttrib *other) const;
   virtual CPT(RenderAttrib) invert_compose_impl(const RenderAttrib *other) const;
 
 

+ 18 - 1
panda/src/pgraph/auxBitplaneAttrib.cxx

@@ -91,12 +91,29 @@ compare_to_impl(const RenderAttrib *other) const {
   const AuxBitplaneAttrib *ta;
   const AuxBitplaneAttrib *ta;
   DCAST_INTO_R(ta, other, 0);
   DCAST_INTO_R(ta, other, 0);
   int compare_result = _outputs - ta->_outputs;
   int compare_result = _outputs - ta->_outputs;
-  if (compare_result!=0) {
+  if (compare_result != 0) {
     return compare_result;
     return compare_result;
   }
   }
   return 0;
   return 0;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: AuxBitplaneAttrib::get_hash_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived RenderAttrib
+//               types to return a unique hash for these particular
+//               properties.  RenderAttribs that compare the same with
+//               compare_to_impl(), above, should return the same
+//               hash; RenderAttribs that compare differently should
+//               return a different hash.
+////////////////////////////////////////////////////////////////////
+size_t AuxBitplaneAttrib::
+get_hash_impl() const {
+  size_t hash = 0;
+  hash = int_hash::add_hash(hash, _outputs);
+  return hash;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: AuxBitplaneAttrib::register_with_read_factory
 //     Function: AuxBitplaneAttrib::register_with_read_factory
 //       Access: Public, Static
 //       Access: Public, Static

+ 1 - 0
panda/src/pgraph/auxBitplaneAttrib.h

@@ -76,6 +76,7 @@ public:
   
   
 protected:
 protected:
   virtual int compare_to_impl(const RenderAttrib *other) const;
   virtual int compare_to_impl(const RenderAttrib *other) const;
+  virtual size_t get_hash_impl() const;
   
   
 private:
 private:
   int _outputs;
   int _outputs;

+ 32 - 0
panda/src/pgraph/clipPlaneAttrib.cxx

@@ -719,6 +719,38 @@ compare_to_impl(const RenderAttrib *other) const {
   return 0;
   return 0;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: ClipPlaneAttrib::get_hash_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived RenderAttrib
+//               types to return a unique hash for these particular
+//               properties.  RenderAttribs that compare the same with
+//               compare_to_impl(), above, should return the same
+//               hash; RenderAttribs that compare differently should
+//               return a different hash.
+////////////////////////////////////////////////////////////////////
+size_t ClipPlaneAttrib::
+get_hash_impl() const {
+  size_t hash = 0;
+
+  Planes::const_iterator li;
+  for (li = _on_planes.begin(); li != _on_planes.end(); ++li) {
+    NodePath plane = (*li);
+    hash = int_hash::add_hash(hash, plane.get_key());
+  }
+
+  // This bool value goes here, between the two lists, to
+  // differentiate between the two.
+  hash = int_hash::add_hash(hash, (int)_off_all_planes);
+
+  for (li = _off_planes.begin(); li != _off_planes.end(); ++li) {
+    NodePath plane = (*li);
+    hash = int_hash::add_hash(hash, plane.get_key());
+  }
+
+  return hash;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ClipPlaneAttrib::compose_impl
 //     Function: ClipPlaneAttrib::compose_impl
 //       Access: Protected, Virtual
 //       Access: Protected, Virtual

+ 1 - 0
panda/src/pgraph/clipPlaneAttrib.h

@@ -100,6 +100,7 @@ public:
 
 
 protected:
 protected:
   virtual int compare_to_impl(const RenderAttrib *other) const;
   virtual int compare_to_impl(const RenderAttrib *other) const;
+  virtual size_t get_hash_impl() const;
   virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const;
   virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const;
   virtual CPT(RenderAttrib) invert_compose_impl(const RenderAttrib *other) const;
   virtual CPT(RenderAttrib) invert_compose_impl(const RenderAttrib *other) const;
 
 

+ 20 - 0
panda/src/pgraph/colorAttrib.cxx

@@ -133,6 +133,26 @@ compare_to_impl(const RenderAttrib *other) const {
   return 0;
   return 0;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: ColorAttrib::get_hash_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived RenderAttrib
+//               types to return a unique hash for these particular
+//               properties.  RenderAttribs that compare the same with
+//               compare_to_impl(), above, should return the same
+//               hash; RenderAttribs that compare differently should
+//               return a different hash.
+////////////////////////////////////////////////////////////////////
+size_t ColorAttrib::
+get_hash_impl() const {
+  size_t hash = 0;
+  hash = int_hash::add_hash(hash, (int)_type);
+  if (_type == T_flat) {
+    hash = _color.add_hash(hash);
+  }
+  return hash;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ColorAttrib::quantize_color
 //     Function: ColorAttrib::quantize_color
 //       Access: Private
 //       Access: Private

+ 1 - 0
panda/src/pgraph/colorAttrib.h

@@ -50,6 +50,7 @@ public:
 
 
 protected:
 protected:
   virtual int compare_to_impl(const RenderAttrib *other) const;
   virtual int compare_to_impl(const RenderAttrib *other) const;
+  virtual size_t get_hash_impl() const;
 
 
 private:
 private:
   void quantize_color();
   void quantize_color();

+ 21 - 0
panda/src/pgraph/colorBlendAttrib.cxx

@@ -131,6 +131,27 @@ compare_to_impl(const RenderAttrib *other) const {
   return _color.compare_to(ta->_color);
   return _color.compare_to(ta->_color);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: ColorBlendAttrib::get_hash_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived RenderAttrib
+//               types to return a unique hash for these particular
+//               properties.  RenderAttribs that compare the same with
+//               compare_to_impl(), above, should return the same
+//               hash; RenderAttribs that compare differently should
+//               return a different hash.
+////////////////////////////////////////////////////////////////////
+size_t ColorBlendAttrib::
+get_hash_impl() const {
+  size_t hash = 0;
+  hash = int_hash::add_hash(hash, (int)_mode);
+  hash = int_hash::add_hash(hash, (int)_a);
+  hash = int_hash::add_hash(hash, (int)_b);
+  hash = _color.add_hash(hash);
+
+  return hash;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ColorBlendAttrib::register_with_read_factory
 //     Function: ColorBlendAttrib::register_with_read_factory
 //       Access: Public, Static
 //       Access: Public, Static

+ 1 - 0
panda/src/pgraph/colorBlendAttrib.h

@@ -95,6 +95,7 @@ public:
 
 
 protected:
 protected:
   virtual int compare_to_impl(const RenderAttrib *other) const;
   virtual int compare_to_impl(const RenderAttrib *other) const;
+  virtual size_t get_hash_impl() const;
 
 
 private:
 private:
   Mode _mode;
   Mode _mode;

+ 18 - 12
panda/src/pgraph/colorScaleAttrib.cxx

@@ -190,24 +190,30 @@ compare_to_impl(const RenderAttrib *other) const {
   DCAST_INTO_R(ta, other, 0);
   DCAST_INTO_R(ta, other, 0);
 
 
   if (is_off() != ta->is_off()) {
   if (is_off() != ta->is_off()) {
-    if (pgraph_cat.is_spam()) {
-      pgraph_cat.spam()
-        << "Comparing " << (int)is_off() << " to " << (int)ta->is_off() << " result = "
-        << (int)is_off() - (int)ta->is_off() << "\n";
-    }
-    
     return (int)is_off() - (int)ta->is_off();
     return (int)is_off() - (int)ta->is_off();
   }
   }
 
 
-  if (pgraph_cat.is_spam()) {
-    pgraph_cat.spam()
-      << "Comparing " << _scale << " to " << ta->_scale << " result = "
-      << _scale.compare_to(ta->_scale) << "\n";
-  }
-
   return _scale.compare_to(ta->_scale);
   return _scale.compare_to(ta->_scale);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: ColorScaleAttrib::get_hash_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived RenderAttrib
+//               types to return a unique hash for these particular
+//               properties.  RenderAttribs that compare the same with
+//               compare_to_impl(), above, should return the same
+//               hash; RenderAttribs that compare differently should
+//               return a different hash.
+////////////////////////////////////////////////////////////////////
+size_t ColorScaleAttrib::
+get_hash_impl() const {
+  size_t hash = 0;
+  hash = int_hash::add_hash(hash, (int)is_off());
+  hash = _scale.add_hash(hash);
+  return hash;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ColorScaleAttrib::compose_impl
 //     Function: ColorScaleAttrib::compose_impl
 //       Access: Protected, Virtual
 //       Access: Protected, Virtual

+ 1 - 0
panda/src/pgraph/colorScaleAttrib.h

@@ -52,6 +52,7 @@ public:
 
 
 protected:
 protected:
   virtual int compare_to_impl(const RenderAttrib *other) const;
   virtual int compare_to_impl(const RenderAttrib *other) const;
+  virtual size_t get_hash_impl() const;
   virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const;
   virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const;
   virtual CPT(RenderAttrib) invert_compose_impl(const RenderAttrib *other) const;
   virtual CPT(RenderAttrib) invert_compose_impl(const RenderAttrib *other) const;
 
 

+ 17 - 0
panda/src/pgraph/colorWriteAttrib.cxx

@@ -94,6 +94,23 @@ compare_to_impl(const RenderAttrib *other) const {
   return (int)_channels - (int)ta->_channels;
   return (int)_channels - (int)ta->_channels;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: ColorWriteAttrib::get_hash_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived RenderAttrib
+//               types to return a unique hash for these particular
+//               properties.  RenderAttribs that compare the same with
+//               compare_to_impl(), above, should return the same
+//               hash; RenderAttribs that compare differently should
+//               return a different hash.
+////////////////////////////////////////////////////////////////////
+size_t ColorWriteAttrib::
+get_hash_impl() const {
+  size_t hash = 0;
+  hash = int_hash::add_hash(hash, (int)_channels);
+  return hash;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ColorWriteAttrib::register_with_read_factory
 //     Function: ColorWriteAttrib::register_with_read_factory
 //       Access: Public, Static
 //       Access: Public, Static

+ 1 - 0
panda/src/pgraph/colorWriteAttrib.h

@@ -56,6 +56,7 @@ public:
 
 
 protected:
 protected:
   virtual int compare_to_impl(const RenderAttrib *other) const;
   virtual int compare_to_impl(const RenderAttrib *other) const;
+  virtual size_t get_hash_impl() const;
 
 
 private:
 private:
   int _channels;
   int _channels;

+ 6 - 4
panda/src/pgraph/config_pgraph.cxx

@@ -188,13 +188,15 @@ ConfigVariableBool auto_break_cycles
  PRC_DESC("Set this true to automatically detect and break reference-count "
  PRC_DESC("Set this true to automatically detect and break reference-count "
           "cycles in the TransformState and RenderState caches.  When this "
           "cycles in the TransformState and RenderState caches.  When this "
           "is false, you must explicitly call TransformState.clear_cache() "
           "is false, you must explicitly call TransformState.clear_cache() "
-          "from time to time to prevent gradual memory bloat.  This has "
-          "no meaning when garbage-collect-states is true."));
+          "from time to time to prevent gradual memory bloat."));
 
 
 ConfigVariableBool garbage_collect_states
 ConfigVariableBool garbage_collect_states
 ("garbage-collect-states", false,
 ("garbage-collect-states", false,
- PRC_DESC("This temporary config variable is used for development only.  "
-          "Do not set!"));
+ PRC_DESC("Set this true to defer destruction of TransformState and "
+          "RenderState objects until the end of the frame (or whenever "
+          "TransformState::garbage_collect() and RenderState::garbage_collect() "
+          "are called).  This is a particularly useful thing to do when "
+          "using multiple threads, because it improves parallelization."));
 
 
 ConfigVariableDouble garbage_collect_states_rate
 ConfigVariableDouble garbage_collect_states_rate
 ("garbage-collect-states-rate", 0.25,
 ("garbage-collect-states-rate", 0.25,

+ 18 - 0
panda/src/pgraph/cullBinAttrib.cxx

@@ -92,6 +92,24 @@ compare_to_impl(const RenderAttrib *other) const {
   return strcmp(_bin_name.c_str(), ta->_bin_name.c_str());
   return strcmp(_bin_name.c_str(), ta->_bin_name.c_str());
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: CullBinAttrib::get_hash_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived RenderAttrib
+//               types to return a unique hash for these particular
+//               properties.  RenderAttribs that compare the same with
+//               compare_to_impl(), above, should return the same
+//               hash; RenderAttribs that compare differently should
+//               return a different hash.
+////////////////////////////////////////////////////////////////////
+size_t CullBinAttrib::
+get_hash_impl() const {
+  size_t hash = 0;
+  hash = int_hash::add_hash(hash, _draw_order);
+  hash = string_hash::add_hash(hash, _bin_name);
+  return hash;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CullBinAttrib::register_with_read_factory
 //     Function: CullBinAttrib::register_with_read_factory
 //       Access: Public, Static
 //       Access: Public, Static

+ 1 - 0
panda/src/pgraph/cullBinAttrib.h

@@ -43,6 +43,7 @@ public:
 
 
 protected:
 protected:
   virtual int compare_to_impl(const RenderAttrib *other) const;
   virtual int compare_to_impl(const RenderAttrib *other) const;
+  virtual size_t get_hash_impl() const;
 
 
 private:
 private:
   string _bin_name;
   string _bin_name;

+ 18 - 0
panda/src/pgraph/cullFaceAttrib.cxx

@@ -162,6 +162,24 @@ compare_to_impl(const RenderAttrib *other) const {
   return (int)_reverse - (int)ta->_reverse;
   return (int)_reverse - (int)ta->_reverse;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: CullFaceAttrib::get_hash_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived RenderAttrib
+//               types to return a unique hash for these particular
+//               properties.  RenderAttribs that compare the same with
+//               compare_to_impl(), above, should return the same
+//               hash; RenderAttribs that compare differently should
+//               return a different hash.
+////////////////////////////////////////////////////////////////////
+size_t CullFaceAttrib::
+get_hash_impl() const {
+  size_t hash = 0;
+  hash = int_hash::add_hash(hash, (int)_mode);
+  hash = int_hash::add_hash(hash, (int)_reverse);
+  return hash;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CullFaceAttrib::compose_impl
 //     Function: CullFaceAttrib::compose_impl
 //       Access: Protected, Virtual
 //       Access: Protected, Virtual

+ 1 - 0
panda/src/pgraph/cullFaceAttrib.h

@@ -52,6 +52,7 @@ public:
 
 
 protected:
 protected:
   virtual int compare_to_impl(const RenderAttrib *other) const;
   virtual int compare_to_impl(const RenderAttrib *other) const;
+  virtual size_t get_hash_impl() const;
   virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const;
   virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const;
   virtual CPT(RenderAttrib) invert_compose_impl(const RenderAttrib *other) const;
   virtual CPT(RenderAttrib) invert_compose_impl(const RenderAttrib *other) const;
 
 

+ 17 - 0
panda/src/pgraph/depthOffsetAttrib.cxx

@@ -80,6 +80,23 @@ compare_to_impl(const RenderAttrib *other) const {
   return _offset - ta->_offset;
   return _offset - ta->_offset;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: DepthOffsetAttrib::get_hash_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived RenderAttrib
+//               types to return a unique hash for these particular
+//               properties.  RenderAttribs that compare the same with
+//               compare_to_impl(), above, should return the same
+//               hash; RenderAttribs that compare differently should
+//               return a different hash.
+////////////////////////////////////////////////////////////////////
+size_t DepthOffsetAttrib::
+get_hash_impl() const {
+  size_t hash = 0;
+  hash = int_hash::add_hash(hash, _offset);
+  return hash;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: DepthOffsetAttrib::compose_impl
 //     Function: DepthOffsetAttrib::compose_impl
 //       Access: Protected, Virtual
 //       Access: Protected, Virtual

+ 1 - 0
panda/src/pgraph/depthOffsetAttrib.h

@@ -65,6 +65,7 @@ public:
 
 
 protected:
 protected:
   virtual int compare_to_impl(const RenderAttrib *other) const;
   virtual int compare_to_impl(const RenderAttrib *other) const;
+  virtual size_t get_hash_impl() const;
   virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const;
   virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const;
   virtual CPT(RenderAttrib) invert_compose_impl(const RenderAttrib *other) const;
   virtual CPT(RenderAttrib) invert_compose_impl(const RenderAttrib *other) const;
 
 

+ 17 - 0
panda/src/pgraph/depthTestAttrib.cxx

@@ -79,6 +79,23 @@ compare_to_impl(const RenderAttrib *other) const {
   return (int)_mode - (int)ta->_mode;
   return (int)_mode - (int)ta->_mode;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: DepthTestAttrib::get_hash_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived RenderAttrib
+//               types to return a unique hash for these particular
+//               properties.  RenderAttribs that compare the same with
+//               compare_to_impl(), above, should return the same
+//               hash; RenderAttribs that compare differently should
+//               return a different hash.
+////////////////////////////////////////////////////////////////////
+size_t DepthTestAttrib::
+get_hash_impl() const {
+  size_t hash = 0;
+  hash = int_hash::add_hash(hash, (int)_mode);
+  return hash;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: DepthTestAttrib::register_with_read_factory
 //     Function: DepthTestAttrib::register_with_read_factory
 //       Access: Public, Static
 //       Access: Public, Static

+ 1 - 0
panda/src/pgraph/depthTestAttrib.h

@@ -40,6 +40,7 @@ public:
 
 
 protected:
 protected:
   virtual int compare_to_impl(const RenderAttrib *other) const;
   virtual int compare_to_impl(const RenderAttrib *other) const;
+  virtual size_t get_hash_impl() const;
 
 
 private:
 private:
   PandaCompareFunc _mode;
   PandaCompareFunc _mode;

+ 17 - 0
panda/src/pgraph/depthWriteAttrib.cxx

@@ -86,6 +86,23 @@ compare_to_impl(const RenderAttrib *other) const {
   return (int)_mode - (int)ta->_mode;
   return (int)_mode - (int)ta->_mode;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: DepthWriteAttrib::get_hash_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived RenderAttrib
+//               types to return a unique hash for these particular
+//               properties.  RenderAttribs that compare the same with
+//               compare_to_impl(), above, should return the same
+//               hash; RenderAttribs that compare differently should
+//               return a different hash.
+////////////////////////////////////////////////////////////////////
+size_t DepthWriteAttrib::
+get_hash_impl() const {
+  size_t hash = 0;
+  hash = int_hash::add_hash(hash, (int)_mode);
+  return hash;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: DepthWriteAttrib::register_with_read_factory
 //     Function: DepthWriteAttrib::register_with_read_factory
 //       Access: Public, Static
 //       Access: Public, Static

+ 1 - 0
panda/src/pgraph/depthWriteAttrib.h

@@ -46,6 +46,7 @@ public:
 
 
 protected:
 protected:
   virtual int compare_to_impl(const RenderAttrib *other) const;
   virtual int compare_to_impl(const RenderAttrib *other) const;
+  virtual size_t get_hash_impl() const;
 
 
 private:
 private:
   Mode _mode;
   Mode _mode;

+ 17 - 0
panda/src/pgraph/fogAttrib.cxx

@@ -103,6 +103,23 @@ compare_to_impl(const RenderAttrib *other) const {
   return 0;
   return 0;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: FogAttrib::get_hash_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived RenderAttrib
+//               types to return a unique hash for these particular
+//               properties.  RenderAttribs that compare the same with
+//               compare_to_impl(), above, should return the same
+//               hash; RenderAttribs that compare differently should
+//               return a different hash.
+////////////////////////////////////////////////////////////////////
+size_t FogAttrib::
+get_hash_impl() const {
+  size_t hash = 0;
+  hash = pointer_hash::add_hash(hash, _fog);
+  return hash;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: FogAttrib::register_with_read_factory
 //     Function: FogAttrib::register_with_read_factory
 //       Access: Public, Static
 //       Access: Public, Static

+ 1 - 0
panda/src/pgraph/fogAttrib.h

@@ -41,6 +41,7 @@ public:
 
 
 protected:
 protected:
   virtual int compare_to_impl(const RenderAttrib *other) const;
   virtual int compare_to_impl(const RenderAttrib *other) const;
+  virtual size_t get_hash_impl() const;
 
 
 private:
 private:
   PT(Fog) _fog;
   PT(Fog) _fog;

+ 32 - 0
panda/src/pgraph/lightAttrib.cxx

@@ -751,6 +751,38 @@ compare_to_impl(const RenderAttrib *other) const {
   return 0;
   return 0;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: LightAttrib::get_hash_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived RenderAttrib
+//               types to return a unique hash for these particular
+//               properties.  RenderAttribs that compare the same with
+//               compare_to_impl(), above, should return the same
+//               hash; RenderAttribs that compare differently should
+//               return a different hash.
+////////////////////////////////////////////////////////////////////
+size_t LightAttrib::
+get_hash_impl() const {
+  size_t hash = 0;
+
+  Lights::const_iterator li;
+  for (li = _on_lights.begin(); li != _on_lights.end(); ++li) {
+    NodePath light = (*li);
+    hash = int_hash::add_hash(hash, light.get_key());
+  }
+
+  // This bool value goes here, between the two lists, to
+  // differentiate between the two.
+  hash = int_hash::add_hash(hash, (int)_off_all_lights);
+
+  for (li = _off_lights.begin(); li != _off_lights.end(); ++li) {
+    NodePath light = (*li);
+    hash = int_hash::add_hash(hash, light.get_key());
+  }
+
+  return hash;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: LightAttrib::compose_impl
 //     Function: LightAttrib::compose_impl
 //       Access: Protected, Virtual
 //       Access: Protected, Virtual

+ 1 - 0
panda/src/pgraph/lightAttrib.h

@@ -99,6 +99,7 @@ public:
 
 
 protected:
 protected:
   virtual int compare_to_impl(const RenderAttrib *other) const;
   virtual int compare_to_impl(const RenderAttrib *other) const;
+  virtual size_t get_hash_impl() const;
   virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const;
   virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const;
   virtual CPT(RenderAttrib) invert_compose_impl(const RenderAttrib *other) const;
   virtual CPT(RenderAttrib) invert_compose_impl(const RenderAttrib *other) const;
 
 

+ 25 - 3
panda/src/pgraph/lightRampAttrib.cxx

@@ -244,15 +244,15 @@ compare_to_impl(const RenderAttrib *other) const {
   const LightRampAttrib *ta;
   const LightRampAttrib *ta;
   DCAST_INTO_R(ta, other, 0);
   DCAST_INTO_R(ta, other, 0);
   int compare_result = ((int)_mode - (int)ta->_mode) ;
   int compare_result = ((int)_mode - (int)ta->_mode) ;
-  if (compare_result!=0) {
+  if (compare_result != 0) {
     return compare_result;
     return compare_result;
   }
   }
-  for (int i=0; i<2; i++) {
+  for (int i = 0; i < 2; i++) {
     if (_level[i] != ta->_level[i]) {
     if (_level[i] != ta->_level[i]) {
       return (_level[i] < ta->_level[i]) ? -1 : 1;
       return (_level[i] < ta->_level[i]) ? -1 : 1;
     }
     }
   }
   }
-  for (int i=0; i<2; i++) {
+  for (int i = 0; i < 2; i++) {
     if (_threshold[i] != ta->_threshold[i]) {
     if (_threshold[i] != ta->_threshold[i]) {
       return (_threshold[i] < ta->_threshold[i]) ? -1 : 1;
       return (_threshold[i] < ta->_threshold[i]) ? -1 : 1;
     }
     }
@@ -260,6 +260,28 @@ compare_to_impl(const RenderAttrib *other) const {
   return 0;
   return 0;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: LightRampAttrib::get_hash_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived RenderAttrib
+//               types to return a unique hash for these particular
+//               properties.  RenderAttribs that compare the same with
+//               compare_to_impl(), above, should return the same
+//               hash; RenderAttribs that compare differently should
+//               return a different hash.
+////////////////////////////////////////////////////////////////////
+size_t LightRampAttrib::
+get_hash_impl() const {
+  size_t hash = 0;
+  hash = int_hash::add_hash(hash, (int)_mode);
+  float_hash fh;
+  for (int i = 0; i < 2; i++) {
+    hash = fh.add_hash(hash, _level[i]);
+    hash = fh.add_hash(hash, _threshold[i]);
+  }
+  return hash;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: LightRampAttrib::register_with_read_factory
 //     Function: LightRampAttrib::register_with_read_factory
 //       Access: Public, Static
 //       Access: Public, Static

+ 1 - 0
panda/src/pgraph/lightRampAttrib.h

@@ -61,6 +61,7 @@ public:
   
   
 protected:
 protected:
   virtual int compare_to_impl(const RenderAttrib *other) const;
   virtual int compare_to_impl(const RenderAttrib *other) const;
+  virtual size_t get_hash_impl() const;
   
   
 private:
 private:
   LightRampMode _mode;
   LightRampMode _mode;

+ 17 - 0
panda/src/pgraph/materialAttrib.cxx

@@ -104,6 +104,23 @@ compare_to_impl(const RenderAttrib *other) const {
   return 0;
   return 0;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: MaterialAttrib::get_hash_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived RenderAttrib
+//               types to return a unique hash for these particular
+//               properties.  RenderAttribs that compare the same with
+//               compare_to_impl(), above, should return the same
+//               hash; RenderAttribs that compare differently should
+//               return a different hash.
+////////////////////////////////////////////////////////////////////
+size_t MaterialAttrib::
+get_hash_impl() const {
+  size_t hash = 0;
+  hash = pointer_hash::add_hash(hash, _material);
+  return hash;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: MaterialAttrib::register_with_read_factory
 //     Function: MaterialAttrib::register_with_read_factory
 //       Access: Public, Static
 //       Access: Public, Static

+ 1 - 0
panda/src/pgraph/materialAttrib.h

@@ -44,6 +44,7 @@ public:
 
 
 protected:
 protected:
   virtual int compare_to_impl(const RenderAttrib *other) const;
   virtual int compare_to_impl(const RenderAttrib *other) const;
+  virtual size_t get_hash_impl() const;
 
 
 private:
 private:
   PT(Material) _material;
   PT(Material) _material;

+ 14 - 0
panda/src/pgraph/renderAttrib.I

@@ -89,6 +89,20 @@ compare_to(const RenderAttrib &other) const {
   return compare_to_impl(&other);
   return compare_to_impl(&other);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: RenderAttrib::get_hash
+//       Access: Published
+//  Description: Returns a suitable hash value for phash_map.
+////////////////////////////////////////////////////////////////////
+INLINE size_t RenderAttrib::
+get_hash() const {
+  size_t hash = get_hash_impl();
+
+  // The type is also added to the hash.
+  hash = int_hash::add_hash(hash, get_type().get_index());
+  return hash;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: RenderAttrib::get_unique
 //     Function: RenderAttrib::get_unique
 //       Access: Published
 //       Access: Published

+ 154 - 61
panda/src/pgraph/renderAttrib.cxx

@@ -17,11 +17,16 @@
 #include "indent.h"
 #include "indent.h"
 #include "config_pgraph.h"
 #include "config_pgraph.h"
 #include "lightReMutexHolder.h"
 #include "lightReMutexHolder.h"
+#include "pStatTimer.h"
 
 
 LightReMutex *RenderAttrib::_attribs_lock = NULL;
 LightReMutex *RenderAttrib::_attribs_lock = NULL;
 RenderAttrib::Attribs *RenderAttrib::_attribs = NULL;
 RenderAttrib::Attribs *RenderAttrib::_attribs = NULL;
 TypeHandle RenderAttrib::_type_handle;
 TypeHandle RenderAttrib::_type_handle;
 
 
+int RenderAttrib::_garbage_index = 0;
+
+PStatCollector RenderAttrib::_garbage_collect_pcollector("*:State Cache:Garbage Collect");
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: RenderAttrib::Constructor
 //     Function: RenderAttrib::Constructor
 //       Access: Protected
 //       Access: Protected
@@ -32,7 +37,7 @@ RenderAttrib() {
   if (_attribs == (Attribs *)NULL) {
   if (_attribs == (Attribs *)NULL) {
     init_attribs();
     init_attribs();
   }
   }
-  _saved_entry = _attribs->end();
+  _saved_entry = -1;
 
 
   _always_reissue = false;
   _always_reissue = false;
 }
 }
@@ -68,7 +73,7 @@ RenderAttrib::
   LightReMutexHolder holder(*_attribs_lock);
   LightReMutexHolder holder(*_attribs_lock);
 
 
   // unref() should have cleared this.
   // unref() should have cleared this.
-  nassertv(_saved_entry == _attribs->end());
+  nassertv(_saved_entry == -1);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -145,11 +150,29 @@ cull_callback(CullTraverser *, const CullTraverserData &) const {
 bool RenderAttrib::
 bool RenderAttrib::
 unref() const {
 unref() const {
   if (!state_cache) {
   if (!state_cache) {
+    // If we're not using the cache anyway, just allow the pointer to
+    // unref normally.
     return ReferenceCount::unref();
     return ReferenceCount::unref();
   }
   }
+  
+  if (garbage_collect_states) {
+    // In the garbage collector case, we don't delete RenderStates
+    // immediately; instead, we allow them to remain in the cache with
+    // a ref count of 0, and we delete them later in
+    // garbage_collect().
+
+    ReferenceCount::unref();
+    // Return true so that it is never deleted here.
+    return true;
+  }
+
+  // Here is the normal refcounting case, with a normal cache, and
+  // without garbage collection in effect.
 
 
   // We always have to grab the lock, since we will definitely need to
   // We always have to grab the lock, since we will definitely need to
   // be holding it if we happen to drop the reference count to 0.
   // be holding it if we happen to drop the reference count to 0.
+  // Having to grab the lock at every call to unref() is a big
+  // limiting factor on parallelization.
   LightReMutexHolder holder(*_attribs_lock);
   LightReMutexHolder holder(*_attribs_lock);
 
 
   if (ReferenceCount::unref()) {
   if (ReferenceCount::unref()) {
@@ -199,7 +222,7 @@ get_num_attribs() {
   if (_attribs == (Attribs *)NULL) {
   if (_attribs == (Attribs *)NULL) {
     return 0;
     return 0;
   }
   }
-  return _attribs->size();
+  return _attribs->get_num_entries();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -213,14 +236,69 @@ void RenderAttrib::
 list_attribs(ostream &out) {
 list_attribs(ostream &out) {
   LightReMutexHolder holder(*_attribs_lock);
   LightReMutexHolder holder(*_attribs_lock);
 
 
-  out << _attribs->size() << " attribs:\n";
-  Attribs::const_iterator si;
-  for (si = _attribs->begin(); si != _attribs->end(); ++si) {
-    const RenderAttrib *attrib = (*si);
+  out << _attribs->get_num_entries() << " attribs:\n";
+  int size = _attribs->get_size();
+  for (int si = 0; si < size; ++si) {
+    if (!_attribs->has_element(si)) {
+      continue;
+    }
+    const RenderAttrib *attrib = _attribs->get_key(si);
     attrib->write(out, 2);
     attrib->write(out, 2);
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: RenderAttrib::garbage_collect
+//       Access: Published, Static
+//  Description: Performs a garbage-collection cycle.  This is called
+//               automatically from RenderState::garbage_collect();
+//               see that method for more information.
+////////////////////////////////////////////////////////////////////
+int RenderAttrib::
+garbage_collect() {
+  if (_attribs == (Attribs *)NULL) {
+    return 0;
+  }
+  LightReMutexHolder holder(*_attribs_lock);
+
+  PStatTimer timer(_garbage_collect_pcollector);
+  int orig_size = _attribs->get_num_entries();
+
+  // How many elements to process this pass?
+  int size = _attribs->get_size();
+  int num_this_pass = int(size * garbage_collect_states_rate);
+  if (num_this_pass <= 0) {
+    return 0;
+  }
+  num_this_pass = min(num_this_pass, size);
+  int stop_at_element = (_garbage_index + num_this_pass) % size;
+  
+  int num_elements = 0;
+  int si = _garbage_index;
+  do {
+    if (_attribs->has_element(si)) {
+      ++num_elements;
+      RenderAttrib *attrib = (RenderAttrib *)_attribs->get_key(si);
+      if (attrib->get_ref_count() == 0) {
+        // This attrib has recently been unreffed to 0, but it hasn't
+        // been deleted yet (because we have overloaded unref(),
+        // above, to always return true).  Now it's time to delete it.
+        // This is safe, because we're holding the _attribs_lock, so
+        // it's not possible for some other thread to find the attrib
+        // in the cache and ref it while we're doing this.
+        attrib->release_new();
+        delete attrib;
+      }
+    }      
+
+    si = (si + 1) % size;
+  } while (si != stop_at_element);
+  _garbage_index = si;
+
+  int new_size = _attribs->get_num_entries();
+  return orig_size - new_size;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: RenderAttrib::validate_attribs
 //     Function: RenderAttrib::validate_attribs
 //       Access: Published, Static
 //       Access: Published, Static
@@ -232,24 +310,51 @@ list_attribs(ostream &out) {
 bool RenderAttrib::
 bool RenderAttrib::
 validate_attribs() {
 validate_attribs() {
   LightReMutexHolder holder(*_attribs_lock);
   LightReMutexHolder holder(*_attribs_lock);
-
-  if (_attribs->empty()) {
+  if (_attribs->is_empty()) {
     return true;
     return true;
   }
   }
 
 
-  Attribs::const_iterator si = _attribs->begin();
-  Attribs::const_iterator snext = si;
-  ++snext;
-  while (snext != _attribs->end()) {
-    if ((*si)->compare_to(*(*snext)) >= 0) {
+  int size = _attribs->get_size();
+  int si = 0;
+  while (si < size && !_attribs->has_element(si)) {
+    ++si;
+  }
+  nassertr(si < size, false);
+  nassertr(_attribs->get_key(si)->get_ref_count() > 0, false);
+  int snext = si;
+  while (snext < size && !_attribs->has_element(snext)) {
+    ++snext;
+  }
+  while (snext < size) {
+    const RenderAttrib *ssi = _attribs->get_key(si);
+    const RenderAttrib *ssnext = _attribs->get_key(snext);
+    int c = ssi->compare_to(*ssnext);
+    if (c >= 0) {
       pgraph_cat.error()
       pgraph_cat.error()
         << "RenderAttribs out of order!\n";
         << "RenderAttribs out of order!\n";
-      (*si)->write(pgraph_cat.error(false), 2);
-      (*snext)->write(pgraph_cat.error(false), 2);
+      ssi->write(pgraph_cat.error(false), 2);
+      ssnext->write(pgraph_cat.error(false), 2);
+      return false;
+    }
+    int ci = ssnext->compare_to(*ssi);
+    if ((ci < 0) != (c > 0) ||
+        (ci > 0) != (c < 0) ||
+        (ci == 0) != (c == 0)) {
+      pgraph_cat.error()
+        << "RenderAttrib::compare_to() not defined properly!\n";
+      pgraph_cat.error(false)
+        << "(a, b): " << c << "\n";
+      pgraph_cat.error(false)
+        << "(b, a): " << ci << "\n";
+      ssi->write(pgraph_cat.error(false), 2);
+      ssnext->write(pgraph_cat.error(false), 2);
       return false;
       return false;
     }
     }
     si = snext;
     si = snext;
-    ++snext;
+    while (snext < size && !_attribs->has_element(snext)) {
+      ++snext;
+    }
+    nassertr(_attribs->get_key(si)->get_ref_count() > 0, false);
   }
   }
 
 
   return true;
   return true;
@@ -306,9 +411,9 @@ return_unique(RenderAttrib *attrib) {
 
 
   LightReMutexHolder holder(*_attribs_lock);
   LightReMutexHolder holder(*_attribs_lock);
 
 
-  if (attrib->_saved_entry != _attribs->end()) {
+  if (attrib->_saved_entry != -1) {
     // This attrib is already in the cache.
     // This attrib is already in the cache.
-    nassertr(_attribs->find(attrib) == attrib->_saved_entry, attrib);
+    //nassertr(_attribs->find(attrib) == attrib->_saved_entry, attrib);
     return attrib;
     return attrib;
   }
   }
 
 
@@ -316,18 +421,18 @@ return_unique(RenderAttrib *attrib) {
   // the end of this function if no one else uses it.
   // the end of this function if no one else uses it.
   CPT(RenderAttrib) pt_attrib = attrib;
   CPT(RenderAttrib) pt_attrib = attrib;
 
 
-  pair<Attribs::iterator, bool> result = _attribs->insert(attrib);
-  if (result.second) {
-    // The attribute was inserted; save the iterator and return the
-    // input attribute.
-    attrib->_saved_entry = result.first;
-
-    return pt_attrib;
+  int si = _attribs->find(attrib);
+  if (si != -1) {
+    // There's an equivalent attrib already in the set.  Return it.
+    return _attribs->get_key(si);
   }
   }
+  
+  // Not already in the set; add it.
+  si = _attribs->store(attrib, Empty());
 
 
-  // The attribute was not inserted; there must be an equivalent one
-  // already in the set.  Return that one.
-  return *(result.first);
+  // Save the index and return the input attrib.
+  attrib->_saved_entry = si;
+  return pt_attrib;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -350,6 +455,21 @@ compare_to_impl(const RenderAttrib *other) const {
   return 0;
   return 0;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: RenderAttrib::get_hash_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived RenderAttrib
+//               types to return a unique hash for these particular
+//               properties.  RenderAttribs that compare the same with
+//               compare_to_impl(), above, should return the same
+//               hash; RenderAttribs that compare differently should
+//               return a different hash.
+////////////////////////////////////////////////////////////////////
+size_t RenderAttrib::
+get_hash_impl() const {
+  return 0;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: RenderAttrib::compose_impl
 //     Function: RenderAttrib::compose_impl
 //       Access: Protected, Virtual
 //       Access: Protected, Virtual
@@ -446,38 +566,11 @@ void RenderAttrib::
 release_new() {
 release_new() {
   nassertv(_attribs_lock->debug_is_locked());
   nassertv(_attribs_lock->debug_is_locked());
 
 
-  if (_saved_entry != _attribs->end()) {
-
-#ifndef NDEBUG
-    nassertd(_attribs->find(this) == _saved_entry) {
-      cerr << "Tried to release " << *this << " (" << (void *)this << "), not found!\n";
-      validate_attribs();
-      Attribs::const_iterator si = _attribs->begin();
-      if (si == _attribs->end()) {
-        cerr << "  Attribs list is empty.\n";
-      } else {
-        cerr << "  Attribs list contains " << _attribs->size() << " entries.\n";
-        const RenderAttrib *attrib = (*si);
-        cerr << "    " << *attrib << " (" << (void *)attrib << ")\n";
-
-        Attribs::const_iterator sprev = si;
-        ++si;
-        while (si != _attribs->end()) {
-          const RenderAttrib *attrib = (*si);
-          cerr << "    " << *attrib << " (" << (void *)attrib << ")\n";
-          if (((*sprev)->compare_to(*attrib)) >= 0) {
-            cerr << "*** above out of order!\n";
-          }
-          sprev = si;
-          ++si;
-        }
-        cerr << "  Done.\n";
-      }
-    }
-#endif  // NDEBUG
-
-    _attribs->erase(_saved_entry);
-    _saved_entry = _attribs->end();
+  if (_saved_entry != -1) {
+    //nassertv(_attribs->find(this) == _saved_entry);
+    _saved_entry = _attribs->find(this);
+    _attribs->remove_element(_saved_entry);
+    _saved_entry = -1;
   }
   }
 }
 }
 
 

+ 15 - 3
panda/src/pgraph/renderAttrib.h

@@ -20,8 +20,9 @@
 #include "typedWritableReferenceCount.h"
 #include "typedWritableReferenceCount.h"
 #include "renderAttribRegistry.h"
 #include "renderAttribRegistry.h"
 #include "pointerTo.h"
 #include "pointerTo.h"
-#include "pset.h"
+#include "simpleHashMap.h"
 #include "lightReMutex.h"
 #include "lightReMutex.h"
+#include "pStatCollector.h"
 
 
 class AttribSlots;
 class AttribSlots;
 class GraphicsStateGuardianBase;
 class GraphicsStateGuardianBase;
@@ -78,6 +79,7 @@ public:
 
 
 PUBLISHED:
 PUBLISHED:
   INLINE int compare_to(const RenderAttrib &other) const;
   INLINE int compare_to(const RenderAttrib &other) const;
+  INLINE size_t get_hash() const;
   INLINE CPT(RenderAttrib) get_unique() const;
   INLINE CPT(RenderAttrib) get_unique() const;
 
 
   virtual bool unref() const;
   virtual bool unref() const;
@@ -87,6 +89,7 @@ PUBLISHED:
 
 
   static int get_num_attribs();
   static int get_num_attribs();
   static void list_attribs(ostream &out);
   static void list_attribs(ostream &out);
+  static int garbage_collect();
   static bool validate_attribs();
   static bool validate_attribs();
 
 
   virtual int get_slot() const=0;
   virtual int get_slot() const=0;
@@ -179,6 +182,7 @@ protected:
   static CPT(RenderAttrib) return_new(RenderAttrib *attrib);
   static CPT(RenderAttrib) return_new(RenderAttrib *attrib);
   static CPT(RenderAttrib) return_unique(RenderAttrib *attrib);
   static CPT(RenderAttrib) return_unique(RenderAttrib *attrib);
   virtual int compare_to_impl(const RenderAttrib *other) const;
   virtual int compare_to_impl(const RenderAttrib *other) const;
+  virtual size_t get_hash_impl() const;
   virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const;
   virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const;
   virtual CPT(RenderAttrib) invert_compose_impl(const RenderAttrib *other) const;
   virtual CPT(RenderAttrib) invert_compose_impl(const RenderAttrib *other) const;
   void output_comparefunc(ostream &out, PandaCompareFunc fn) const;
   void output_comparefunc(ostream &out, PandaCompareFunc fn) const;
@@ -199,10 +203,18 @@ public:
 private:
 private:
   // This mutex protects _attribs.
   // This mutex protects _attribs.
   static LightReMutex *_attribs_lock;
   static LightReMutex *_attribs_lock;
-  typedef pset<const RenderAttrib *, indirect_compare_to<const RenderAttrib *> > Attribs;
+  class Empty {
+  };
+  typedef SimpleHashMap<const RenderAttrib *, Empty, indirect_compare_to_hash<const RenderAttrib *> > Attribs;
   static Attribs *_attribs;
   static Attribs *_attribs;
 
 
-  Attribs::iterator _saved_entry;
+  int _saved_entry;
+
+  // This keeps track of our current position through the garbage
+  // collection cycle.
+  static int _garbage_index;
+
+  static PStatCollector _garbage_collect_pcollector;
 
 
 public:
 public:
   virtual void write_datagram(BamWriter *manager, Datagram &dg);
   virtual void write_datagram(BamWriter *manager, Datagram &dg);

+ 19 - 0
panda/src/pgraph/renderModeAttrib.cxx

@@ -133,6 +133,25 @@ compare_to_impl(const RenderAttrib *other) const {
   return 0;
   return 0;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: RenderModeAttrib::get_hash_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived RenderAttrib
+//               types to return a unique hash for these particular
+//               properties.  RenderAttribs that compare the same with
+//               compare_to_impl(), above, should return the same
+//               hash; RenderAttribs that compare differently should
+//               return a different hash.
+////////////////////////////////////////////////////////////////////
+size_t RenderModeAttrib::
+get_hash_impl() const {
+  size_t hash = 0;
+  hash = int_hash::add_hash(hash, (int)_mode);
+  hash = float_hash().add_hash(hash, _thickness);
+  hash = int_hash::add_hash(hash, (int)_perspective);
+  return hash;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: RenderModeAttrib::compose_impl
 //     Function: RenderModeAttrib::compose_impl
 //       Access: Protected, Virtual
 //       Access: Protected, Virtual

+ 1 - 0
panda/src/pgraph/renderModeAttrib.h

@@ -66,6 +66,7 @@ public:
 
 
 protected:
 protected:
   virtual int compare_to_impl(const RenderAttrib *other) const;
   virtual int compare_to_impl(const RenderAttrib *other) const;
+  virtual size_t get_hash_impl() const;
   virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const;
   virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const;
 
 
 private:
 private:

+ 43 - 7
panda/src/pgraph/renderState.I

@@ -13,6 +13,17 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: RenderState::get_hash
+//       Access: Published
+//  Description: Returns a suitable hash value for phash_map.
+////////////////////////////////////////////////////////////////////
+INLINE size_t RenderState::
+get_hash() const {
+  check_hash();
+  return _hash;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: RenderState::is_empty
 //     Function: RenderState::is_empty
 //       Access: Published
 //       Access: Published
@@ -568,15 +579,17 @@ flush_level() {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: RenderState::do_node_unref
+//     Function: RenderState::check_hash
 //       Access: Private
 //       Access: Private
-//  Description: Reimplements NodeReferenceCount::node_unref().  We do
-//               this because we have a non-virtual unref() method.
+//  Description: Ensures that we know the hash value.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE bool RenderState::
-do_node_unref() const {
-  node_unref_only();
-  return unref();
+INLINE void RenderState::
+check_hash() const {
+  // This pretends to be a const function, even though it's not,
+  // because it only updates a transparent cache value.
+  if ((_flags & F_hash_known) == 0) {
+    ((RenderState *)this)->calc_hash();
+  }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -592,6 +605,29 @@ do_cache_unref() const {
   return unref();
   return unref();
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: RenderState::do_node_unref
+//       Access: Private
+//  Description: Reimplements NodeReferenceCount::node_unref().  We do
+//               this because we have a non-virtual unref() method.
+////////////////////////////////////////////////////////////////////
+INLINE bool RenderState::
+do_node_unref() const {
+  node_unref_only();
+  return unref();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderState::calc_hash
+//       Access: Private
+//  Description: Computes the hash value.
+////////////////////////////////////////////////////////////////////
+INLINE void RenderState::
+calc_hash() {
+  LightMutexHolder holder(_lock);
+  do_calc_hash();
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: RenderState::CompositionCycleDescEntry::Constructor
 //     Function: RenderState::CompositionCycleDescEntry::Constructor
 //       Access: Public
 //       Access: Public

+ 261 - 119
panda/src/pgraph/renderState.cxx

@@ -43,12 +43,16 @@ RenderState::States *RenderState::_states = NULL;
 CPT(RenderState) RenderState::_empty_state;
 CPT(RenderState) RenderState::_empty_state;
 CPT(RenderState) RenderState::_full_default_state;
 CPT(RenderState) RenderState::_full_default_state;
 UpdateSeq RenderState::_last_cycle_detect;
 UpdateSeq RenderState::_last_cycle_detect;
+int RenderState::_garbage_index = 0;
 
 
 PStatCollector RenderState::_cache_update_pcollector("*:State Cache:Update");
 PStatCollector RenderState::_cache_update_pcollector("*:State Cache:Update");
+PStatCollector RenderState::_garbage_collect_pcollector("*:State Cache:Garbage Collect");
 PStatCollector RenderState::_state_compose_pcollector("*:State Cache:Compose State");
 PStatCollector RenderState::_state_compose_pcollector("*:State Cache:Compose State");
 PStatCollector RenderState::_state_invert_pcollector("*:State Cache:Invert State");
 PStatCollector RenderState::_state_invert_pcollector("*:State Cache:Invert State");
 PStatCollector RenderState::_node_counter("RenderStates:On nodes");
 PStatCollector RenderState::_node_counter("RenderStates:On nodes");
 PStatCollector RenderState::_cache_counter("RenderStates:Cached");
 PStatCollector RenderState::_cache_counter("RenderStates:Cached");
+PStatCollector RenderState::_state_break_cycles_pcollector("*:State Cache:Break Cycles");
+PStatCollector RenderState::_state_validate_pcollector("*:State Cache:Validate");
 
 
 CacheStats RenderState::_cache_stats;
 CacheStats RenderState::_cache_stats;
 
 
@@ -79,7 +83,7 @@ RenderState() :
   if (_states == (States *)NULL) {
   if (_states == (States *)NULL) {
     init_states();
     init_states();
   }
   }
-  _saved_entry = _states->end();
+  _saved_entry = -1;
   _last_mi = _mungers.end();
   _last_mi = _mungers.end();
   _cache_stats.add_num_states(1);
   _cache_stats.add_num_states(1);
   _read_overrides = NULL;
   _read_overrides = NULL;
@@ -106,7 +110,7 @@ RenderState(const RenderState &copy) :
     new(&_attributes[i]) Attribute(copy._attributes[i]);
     new(&_attributes[i]) Attribute(copy._attributes[i]);
   }
   }
 
 
-  _saved_entry = _states->end();
+  _saved_entry = -1;
   _last_mi = _mungers.end();
   _last_mi = _mungers.end();
   _cache_stats.add_num_states(1);
   _cache_stats.add_num_states(1);
   _read_overrides = NULL;
   _read_overrides = NULL;
@@ -138,7 +142,7 @@ RenderState::
   LightReMutexHolder holder(*_states_lock);
   LightReMutexHolder holder(*_states_lock);
 
 
   // unref() should have cleared these.
   // unref() should have cleared these.
-  nassertv(_saved_entry == _states->end());
+  nassertv(_saved_entry == -1);
   nassertv(_composition_cache.is_empty() && _invert_composition_cache.is_empty());
   nassertv(_composition_cache.is_empty() && _invert_composition_cache.is_empty());
 
 
   // If this was true at the beginning of the destructor, but is no
   // If this was true at the beginning of the destructor, but is no
@@ -156,7 +160,7 @@ RenderState::
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: RenderState::operator <
+//     Function: RenderState::compare_to
 //       Access: Published
 //       Access: Published
 //  Description: Provides an arbitrary ordering among all unique
 //  Description: Provides an arbitrary ordering among all unique
 //               RenderStates, so we can store the essentially
 //               RenderStates, so we can store the essentially
@@ -167,20 +171,20 @@ RenderState::
 //               guaranteed to share the same pointer; thus, a pointer
 //               guaranteed to share the same pointer; thus, a pointer
 //               comparison is always sufficient.
 //               comparison is always sufficient.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-bool RenderState::
-operator < (const RenderState &other) const {
+int RenderState::
+compare_to(const RenderState &other) const {
   SlotMask mask = _filled_slots | other._filled_slots;
   SlotMask mask = _filled_slots | other._filled_slots;
   int slot = mask.get_lowest_on_bit();
   int slot = mask.get_lowest_on_bit();
   while (slot >= 0) {
   while (slot >= 0) {
     int result = _attributes[slot].compare_to(other._attributes[slot]);
     int result = _attributes[slot].compare_to(other._attributes[slot]);
     if (result != 0) {
     if (result != 0) {
-      return result < 0;
+      return result;
     }
     }
     mask.clear_bit(slot);
     mask.clear_bit(slot);
     slot = mask.get_lowest_on_bit();
     slot = mask.get_lowest_on_bit();
   }
   }
 
 
-  return false;
+  return 0;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -191,7 +195,7 @@ operator < (const RenderState &other) const {
 //               performance, so that "heavier" RenderAttribs (as
 //               performance, so that "heavier" RenderAttribs (as
 //               defined by RenderAttribRegistry::get_slot_sort()) are
 //               defined by RenderAttribRegistry::get_slot_sort()) are
 //               more likely to be grouped together.  This is not
 //               more likely to be grouped together.  This is not
-//               related to the sorting order defined by operator <.
+//               related to the sorting order defined by compare_to.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 int RenderState::
 int RenderState::
 compare_sort(const RenderState &other) const {
 compare_sort(const RenderState &other) const {
@@ -216,30 +220,6 @@ compare_sort(const RenderState &other) const {
   return 0;
   return 0;
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: RenderState::get_hash
-//       Access: Published
-//  Description: Returns a suitable hash value for phash_map.
-////////////////////////////////////////////////////////////////////
-size_t RenderState::
-get_hash() const {
-  size_t hash = 0;
-
-  SlotMask mask = _filled_slots;
-  int slot = mask.get_lowest_on_bit();
-  while (slot >= 0) {
-    const Attribute &attrib = _attributes[slot];
-    nassertr(attrib._attrib != (RenderAttrib *)NULL, 0);
-    hash = pointer_hash::add_hash(hash, attrib._attrib);
-    hash = int_hash::add_hash(hash, attrib._override);
-
-    mask.clear_bit(slot);
-    slot = mask.get_lowest_on_bit();
-  }
-
-  return hash;
-}
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: RenderState::cull_callback
 //     Function: RenderState::cull_callback
 //       Access: Published
 //       Access: Published
@@ -703,11 +683,29 @@ adjust_all_priorities(int adjustment) const {
 bool RenderState::
 bool RenderState::
 unref() const {
 unref() const {
   if (!state_cache) {
   if (!state_cache) {
+    // If we're not using the cache anyway, just allow the pointer to
+    // unref normally.
     return ReferenceCount::unref();
     return ReferenceCount::unref();
   }
   }
+  
+  if (garbage_collect_states) {
+    // In the garbage collector case, we don't delete RenderStates
+    // immediately; instead, we allow them to remain in the cache with
+    // a ref count of 0, and we delete them later in
+    // garbage_collect().
+
+    ReferenceCount::unref();
+    // Return true so that it is never deleted here.
+    return true;
+  }
+
+  // Here is the normal refcounting case, with a normal cache, and
+  // without garbage collection in effect.
 
 
   // We always have to grab the lock, since we will definitely need to
   // We always have to grab the lock, since we will definitely need to
   // be holding it if we happen to drop the reference count to 0.
   // be holding it if we happen to drop the reference count to 0.
+  // Having to grab the lock at every call to unref() is a big
+  // limiting factor on parallelization.
   LightReMutexHolder holder(*_states_lock);
   LightReMutexHolder holder(*_states_lock);
 
 
   if (auto_break_cycles && uniquify_states) {
   if (auto_break_cycles && uniquify_states) {
@@ -717,28 +715,7 @@ unref() const {
       // cache, leaving only references in the cache, then we need to
       // cache, leaving only references in the cache, then we need to
       // check for a cycle involving this RenderState and break it if
       // check for a cycle involving this RenderState and break it if
       // it exists.
       // it exists.
-      
-      ++_last_cycle_detect;
-      if (r_detect_cycles(this, this, 1, _last_cycle_detect, NULL)) {
-        // Ok, we have a cycle.  This will be a leak unless we break the
-        // cycle by freeing the cache on this object.
-        if (pgraph_cat.is_debug()) {
-          pgraph_cat.debug()
-            << "Breaking cycle involving " << (*this) << "\n";
-        }
-        
-        ((RenderState *)this)->remove_cache_pointers();
-      } else {
-        ++_last_cycle_detect;
-        if (r_detect_reverse_cycles(this, this, 1, _last_cycle_detect, NULL)) {
-          if (pgraph_cat.is_debug()) {
-            pgraph_cat.debug()
-              << "Breaking cycle involving " << (*this) << "\n";
-          }
-          
-          ((RenderState *)this)->remove_cache_pointers();
-        }
-      }
+      ((RenderState *)this)->detect_and_break_cycles();
     }
     }
   }
   }
 
 
@@ -958,7 +935,7 @@ get_num_states() {
     return 0;
     return 0;
   }
   }
   LightReMutexHolder holder(*_states_lock);
   LightReMutexHolder holder(*_states_lock);
-  return _states->size();
+  return _states->get_num_entries();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -991,9 +968,12 @@ get_num_unused_states() {
   typedef pmap<const RenderState *, int> StateCount;
   typedef pmap<const RenderState *, int> StateCount;
   StateCount state_count;
   StateCount state_count;
 
 
-  States::iterator si;
-  for (si = _states->begin(); si != _states->end(); ++si) {
-    const RenderState *state = (*si);
+  int size = _states->get_size();
+  for (int si = 0; si < size; ++si) {
+    if (!_states->has_element(si)) {
+      continue;
+    }
+    const RenderState *state = _states->get_key(si);
 
 
     int i;
     int i;
     int cache_size = state->_composition_cache.get_size();
     int cache_size = state->_composition_cache.get_size();
@@ -1082,7 +1062,7 @@ clear_cache() {
   LightReMutexHolder holder(*_states_lock);
   LightReMutexHolder holder(*_states_lock);
 
 
   PStatTimer timer(_cache_update_pcollector);
   PStatTimer timer(_cache_update_pcollector);
-  int orig_size = _states->size();
+  int orig_size = _states->get_num_entries();
 
 
   // First, we need to copy the entire set of states to a temporary
   // First, we need to copy the entire set of states to a temporary
   // vector, reference-counting each object.  That way we can walk
   // vector, reference-counting each object.  That way we can walk
@@ -1093,8 +1073,14 @@ clear_cache() {
     TempStates temp_states;
     TempStates temp_states;
     temp_states.reserve(orig_size);
     temp_states.reserve(orig_size);
 
 
-    copy(_states->begin(), _states->end(),
-         back_inserter(temp_states));
+    int size = _states->get_size();
+    for (int si = 0; si < size; ++si) {
+      if (!_states->has_element(si)) {
+        continue;
+      }
+      const RenderState *state = _states->get_key(si);
+      temp_states.push_back(state);
+    }
 
 
     // Now it's safe to walk through the list, destroying the cache
     // Now it's safe to walk through the list, destroying the cache
     // within each object as we go.  Nothing will be destructed till
     // within each object as we go.  Nothing will be destructed till
@@ -1136,10 +1122,82 @@ clear_cache() {
     // held only within the various objects' caches will go away.
     // held only within the various objects' caches will go away.
   }
   }
 
 
-  int new_size = _states->size();
+  int new_size = _states->get_num_entries();
   return orig_size - new_size;
   return orig_size - new_size;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: RenderState::garbage_collect
+//       Access: Published, Static
+//  Description: Performs a garbage-collection cycle.  This must be
+//               called periodically if garbage-collect-states is true
+//               to ensure that RenderStates get cleaned up
+//               appropriately.  It does no harm to call it even if
+//               this variable is not true, but there is probably no
+//               advantage in that case.
+//
+//               This automatically calls
+//               RenderAttrib::garbage_collect() as well.
+////////////////////////////////////////////////////////////////////
+int RenderState::
+garbage_collect() {
+  int num_attribs = RenderAttrib::garbage_collect();
+
+  if (_states == (States *)NULL) {
+    return num_attribs;
+  }
+  LightReMutexHolder holder(*_states_lock);
+
+  PStatTimer timer(_garbage_collect_pcollector);
+  int orig_size = _states->get_num_entries();
+
+  // How many elements to process this pass?
+  int size = _states->get_size();
+  int num_this_pass = int(size * garbage_collect_states_rate);
+  if (num_this_pass <= 0) {
+    return num_attribs;
+  }
+  num_this_pass = min(num_this_pass, size);
+  int stop_at_element = (_garbage_index + num_this_pass) % size;
+  
+  int num_elements = 0;
+  int si = _garbage_index;
+  do {
+    if (_states->has_element(si)) {
+      ++num_elements;
+      RenderState *state = (RenderState *)_states->get_key(si);
+      if (auto_break_cycles && uniquify_states) {
+        if (state->get_cache_ref_count() > 0 &&
+            state->get_ref_count() == state->get_cache_ref_count()) {
+          // If we have removed all the references to this state not in
+          // the cache, leaving only references in the cache, then we
+          // need to check for a cycle involving this RenderState and
+          // break it if it exists.
+          state->detect_and_break_cycles();
+        }
+      }
+
+      if (state->get_ref_count() == 0) {
+        // This state has recently been unreffed to 0, but it hasn't
+        // been deleted yet (because we have overloaded unref(),
+        // above, to always return true).  Now it's time to delete it.
+        // This is safe, because we're holding the _states_lock, so
+        // it's not possible for some other thread to find the state
+        // in the cache and ref it while we're doing this.
+        state->release_new();
+        state->remove_cache_pointers();
+        delete state;
+      }
+    }      
+
+    si = (si + 1) % size;
+  } while (si != stop_at_element);
+  _garbage_index = si;
+
+  int new_size = _states->get_num_entries();
+  return orig_size - new_size + num_attribs;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: RenderState::clear_munger_cache
 //     Function: RenderState::clear_munger_cache
 //       Access: Published, Static
 //       Access: Published, Static
@@ -1151,14 +1209,12 @@ void RenderState::
 clear_munger_cache() {
 clear_munger_cache() {
   LightReMutexHolder holder(*_states_lock);
   LightReMutexHolder holder(*_states_lock);
 
 
-  // First, we need to count the number of times each RenderState
-  // object is recorded in the cache.
-  typedef pmap<const RenderState *, int> StateCount;
-  StateCount state_count;
-
-  States::iterator si;
-  for (si = _states->begin(); si != _states->end(); ++si) {
-    RenderState *state = (RenderState *)(*si);
+  int size = _states->get_size();
+  for (int si = 0; si < size; ++si) {
+    if (!_states->has_element(si)) {
+      continue;
+    }
+    RenderState *state = (RenderState *)(_states->get_key(si));
     state->_mungers.clear();
     state->_mungers.clear();
     state->_last_mi = state->_mungers.end();
     state->_last_mi = state->_mungers.end();
   }
   }
@@ -1193,9 +1249,12 @@ list_cycles(ostream &out) {
   VisitedStates visited;
   VisitedStates visited;
   CompositionCycleDesc cycle_desc;
   CompositionCycleDesc cycle_desc;
 
 
-  States::iterator si;
-  for (si = _states->begin(); si != _states->end(); ++si) {
-    const RenderState *state = (*si);
+  int size = _states->get_size();
+  for (int si = 0; si < size; ++si) {
+    if (!_states->has_element(si)) {
+      continue;
+    }
+    const RenderState *state = _states->get_key(si);
 
 
     bool inserted = visited.insert(state).second;
     bool inserted = visited.insert(state).second;
     if (inserted) {
     if (inserted) {
@@ -1269,10 +1328,14 @@ list_states(ostream &out) {
   }
   }
   LightReMutexHolder holder(*_states_lock);
   LightReMutexHolder holder(*_states_lock);
 
 
-  out << _states->size() << " states:\n";
-  States::const_iterator si;
-  for (si = _states->begin(); si != _states->end(); ++si) {
-    const RenderState *state = (*si);
+  out << _states->get_num_entries() << " states:\n";
+
+  int size = _states->get_size();
+  for (int si = 0; si < size; ++si) {
+    if (!_states->has_element(si)) {
+      continue;
+    }
+    const RenderState *state = _states->get_key(si);
     state->write(out, 2);
     state->write(out, 2);
   }
   }
 }
 }
@@ -1293,39 +1356,54 @@ validate_states() {
     return true;
     return true;
   }
   }
 
 
+  PStatTimer timer(_state_validate_pcollector);
+
   LightReMutexHolder holder(*_states_lock);
   LightReMutexHolder holder(*_states_lock);
-  if (_states->empty()) {
+  if (_states->is_empty()) {
     return true;
     return true;
   }
   }
 
 
-  States::const_iterator si = _states->begin();
-  States::const_iterator snext = si;
-  ++snext;
-  nassertr((*si)->get_ref_count() > 0, false);
-  nassertr((*si)->validate_filled_slots(), false);
-  while (snext != _states->end()) {
-    if (!(*(*si) < *(*snext))) {
+  int size = _states->get_size();
+  int si = 0;
+  while (si < size && !_states->has_element(si)) {
+    ++si;
+  }
+  nassertr(si < size, false);
+  nassertr(_states->get_key(si)->get_ref_count() > 0, false);
+  int snext = si;
+  while (snext < size && !_states->has_element(snext)) {
+    ++snext;
+  }
+  while (snext < size) {
+    const RenderState *ssi = _states->get_key(si);
+    const RenderState *ssnext = _states->get_key(snext);
+    int c = ssi->compare_to(*ssnext);
+    if (c >= 0) {
       pgraph_cat.error()
       pgraph_cat.error()
         << "RenderStates out of order!\n";
         << "RenderStates out of order!\n";
-      (*si)->write(pgraph_cat.error(false), 2);
-      (*snext)->write(pgraph_cat.error(false), 2);
+      ssi->write(pgraph_cat.error(false), 2);
+      ssnext->write(pgraph_cat.error(false), 2);
       return false;
       return false;
     }
     }
-    if ((*(*snext) < *(*si))) {
+    int ci = ssnext->compare_to(*ssi);
+    if ((ci < 0) != (c > 0) ||
+        (ci > 0) != (c < 0) ||
+        (ci == 0) != (c == 0)) {
       pgraph_cat.error()
       pgraph_cat.error()
-        << "RenderStates::operator < not defined properly!\n";
+        << "RenderState::compare_to() not defined properly!\n";
       pgraph_cat.error(false)
       pgraph_cat.error(false)
-        << "a < b: " << (*(*si) < *(*snext)) << "\n";
+        << "(a, b): " << c << "\n";
       pgraph_cat.error(false)
       pgraph_cat.error(false)
-        << "b < a: " << (*(*snext) < *(*si)) << "\n";
-      (*si)->write(pgraph_cat.error(false), 2);
-      (*snext)->write(pgraph_cat.error(false), 2);
+        << "(b, a): " << ci << "\n";
+      ssi->write(pgraph_cat.error(false), 2);
+      ssnext->write(pgraph_cat.error(false), 2);
       return false;
       return false;
     }
     }
     si = snext;
     si = snext;
-    ++snext;
-    nassertr((*si)->get_ref_count() > 0, false);
-    nassertr((*si)->validate_filled_slots(), false);
+    while (snext < size && !_states->has_element(snext)) {
+      ++snext;
+    }
+    nassertr(_states->get_key(si)->get_ref_count() > 0, false);
   }
   }
 
 
   return true;
   return true;
@@ -1395,6 +1473,30 @@ validate_filled_slots() const {
   return (mask == _filled_slots);
   return (mask == _filled_slots);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: RenderState::do_calc_hash
+//       Access: Private
+//  Description: Computes a suitable hash value for phash_map.
+////////////////////////////////////////////////////////////////////
+void RenderState::
+do_calc_hash() {
+  _hash = 0;
+
+  SlotMask mask = _filled_slots;
+  int slot = mask.get_lowest_on_bit();
+  while (slot >= 0) {
+    const Attribute &attrib = _attributes[slot];
+    nassertv(attrib._attrib != (RenderAttrib *)NULL);
+    _hash = pointer_hash::add_hash(_hash, attrib._attrib);
+    _hash = int_hash::add_hash(_hash, attrib._override);
+
+    mask.clear_bit(slot);
+    slot = mask.get_lowest_on_bit();
+  }
+
+  _flags |= F_hash_known;
+}
+
   
   
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: RenderState::return_new
 //     Function: RenderState::return_new
@@ -1472,9 +1574,9 @@ return_unique(RenderState *state) {
 
 
   LightReMutexHolder holder(*_states_lock);
   LightReMutexHolder holder(*_states_lock);
 
 
-  if (state->_saved_entry != _states->end()) {
+  if (state->_saved_entry != -1) {
     // This state is already in the cache.
     // This state is already in the cache.
-    nassertr(_states->find(state) == state->_saved_entry, state);
+    //nassertr(_states->find(state) == state->_saved_entry, state);
     return state;
     return state;
   }
   }
 
 
@@ -1496,18 +1598,18 @@ return_unique(RenderState *state) {
     }    
     }    
   }
   }
 
 
-  pair<States::iterator, bool> result = _states->insert(state);
-
-  if (result.second) {
-    // The state was inserted; save the iterator and return the
-    // input state.
-    state->_saved_entry = result.first;
-    return pt_state;
+  int si = _states->find(state);
+  if (si != -1) {
+    // There's an equivalent state already in the set.  Return it.
+    return _states->get_key(si);
   }
   }
   
   
-  // The state was not inserted; there must be an equivalent one
-  // already in the set.  Return that one.
-  return *(result.first);
+  // Not already in the set; add it.
+  si = _states->store(state, Empty());
+
+  // Save the index and return the input state.
+  state->_saved_entry = si;
+  return pt_state;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1621,6 +1723,40 @@ do_invert_compose(const RenderState *other) const {
   return return_new(new_state);
   return return_new(new_state);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: RenderState::detect_and_break_cycles
+//       Access: Private
+//  Description: Detects whether there is a cycle in the cache that
+//               begins with this state.  If any are detected, breaks
+//               them by removing this state from the cache.
+////////////////////////////////////////////////////////////////////
+void RenderState::
+detect_and_break_cycles() {
+  PStatTimer timer(_state_break_cycles_pcollector);
+      
+  ++_last_cycle_detect;
+  if (r_detect_cycles(this, this, 1, _last_cycle_detect, NULL)) {
+    // Ok, we have a cycle.  This will be a leak unless we break the
+    // cycle by freeing the cache on this object.
+    if (pgraph_cat.is_debug()) {
+      pgraph_cat.debug()
+        << "Breaking cycle involving " << (*this) << "\n";
+    }
+    
+    ((RenderState *)this)->remove_cache_pointers();
+  } else {
+    ++_last_cycle_detect;
+    if (r_detect_reverse_cycles(this, this, 1, _last_cycle_detect, NULL)) {
+      if (pgraph_cat.is_debug()) {
+        pgraph_cat.debug()
+          << "Breaking cycle involving " << (*this) << "\n";
+      }
+      
+      ((RenderState *)this)->remove_cache_pointers();
+    }
+  }
+}
+  
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: RenderState::r_detect_cycles
 //     Function: RenderState::r_detect_cycles
 //       Access: Private, Static
 //       Access: Private, Static
@@ -1783,10 +1919,11 @@ void RenderState::
 release_new() {
 release_new() {
   nassertv(_states_lock->debug_is_locked());
   nassertv(_states_lock->debug_is_locked());
 
 
-  if (_saved_entry != _states->end()) {
-    nassertv(_states->find(this) == _saved_entry);
-    _states->erase(_saved_entry);
-    _saved_entry = _states->end();
+  if (_saved_entry != -1) {
+    //nassertv(_states->find(this) == _saved_entry);
+    _saved_entry = _states->find(this);
+    _states->remove_element(_saved_entry);
+    _saved_entry = -1;
   }
   }
 }
 }
 
 
@@ -2058,18 +2195,23 @@ get_states() {
   }
   }
   LightReMutexHolder holder(*_states_lock);
   LightReMutexHolder holder(*_states_lock);
 
 
-  size_t num_states = _states->size();
+  size_t num_states = _states->get_num_entries();
   PyObject *list = PyList_New(num_states);
   PyObject *list = PyList_New(num_states);
-  States::const_iterator si;
-  size_t i;
-  for (si = _states->begin(), i = 0; si != _states->end(); ++si, ++i) {
-    nassertr(i < num_states, list);
-    const RenderState *state = (*si);
+  size_t i = 0;
+
+  int size = _states->get_size();
+  for (int si = 0; si < size; ++si) {
+    if (!_states->has_element(si)) {
+      continue;
+    }
+    const RenderState *state = _states->get_key(si);
     state->ref();
     state->ref();
     PyObject *a = 
     PyObject *a = 
       DTool_CreatePyInstanceTyped((void *)state, Dtool_RenderState, 
       DTool_CreatePyInstanceTyped((void *)state, Dtool_RenderState, 
                                   true, true, state->get_type_index());
                                   true, true, state->get_type_index());
+    nassertr(i < num_states, list);
     PyList_SET_ITEM(list, i, a);
     PyList_SET_ITEM(list, i, a);
+    ++i;
   }
   }
   nassertr(i == num_states, list);
   nassertr(i == num_states, list);
   return list;
   return list;

+ 23 - 7
panda/src/pgraph/renderState.h

@@ -65,9 +65,9 @@ public:
   typedef RenderAttribRegistry::SlotMask SlotMask;
   typedef RenderAttribRegistry::SlotMask SlotMask;
 
 
 PUBLISHED:
 PUBLISHED:
-  bool operator < (const RenderState &other) const;
+  int compare_to(const RenderState &other) const;
   int compare_sort(const RenderState &other) const;
   int compare_sort(const RenderState &other) const;
-  size_t get_hash() const;
+  INLINE size_t get_hash() const;
 
 
   INLINE bool is_empty() const;
   INLINE bool is_empty() const;
 
 
@@ -140,6 +140,7 @@ PUBLISHED:
   static int get_num_unused_states();
   static int get_num_unused_states();
   static int clear_cache();
   static int clear_cache();
   static void clear_munger_cache();
   static void clear_munger_cache();
+  static int garbage_collect();
   static void list_cycles(ostream &out);
   static void list_cycles(ostream &out);
   static void list_states(ostream &out);
   static void list_states(ostream &out);
   static bool validate_states();
   static bool validate_states();
@@ -160,9 +161,12 @@ public:
   INLINE static void flush_level();
   INLINE static void flush_level();
 
 
 private:
 private:
+  INLINE void check_hash() const;
   bool validate_filled_slots() const;
   bool validate_filled_slots() const;
   INLINE bool do_cache_unref() const;
   INLINE bool do_cache_unref() const;
   INLINE bool do_node_unref() const;
   INLINE bool do_node_unref() const;
+  INLINE void calc_hash();
+  void do_calc_hash();
 
 
   class CompositionCycleDescEntry {
   class CompositionCycleDescEntry {
   public:
   public:
@@ -180,6 +184,7 @@ private:
   static CPT(RenderState) return_unique(RenderState *state);
   static CPT(RenderState) return_unique(RenderState *state);
   CPT(RenderState) do_compose(const RenderState *other) const;
   CPT(RenderState) do_compose(const RenderState *other) const;
   CPT(RenderState) do_invert_compose(const RenderState *other) const;
   CPT(RenderState) do_invert_compose(const RenderState *other) const;
+  void detect_and_break_cycles();
   static bool r_detect_cycles(const RenderState *start_state,
   static bool r_detect_cycles(const RenderState *start_state,
                               const RenderState *current_state,
                               const RenderState *current_state,
                               int length, UpdateSeq this_seq,
                               int length, UpdateSeq this_seq,
@@ -216,15 +221,17 @@ private:
   // to the cache, which is encoded in _composition_cache and
   // to the cache, which is encoded in _composition_cache and
   // _invert_composition_cache.
   // _invert_composition_cache.
   static LightReMutex *_states_lock;
   static LightReMutex *_states_lock;
-  typedef phash_set<const RenderState *, indirect_less_hash<const RenderState *> > States;
+  class Empty {
+  };
+  typedef SimpleHashMap<const RenderState *, Empty, indirect_compare_to_hash<const RenderState *> > States;
   static States *_states;
   static States *_states;
   static CPT(RenderState) _empty_state;
   static CPT(RenderState) _empty_state;
   static CPT(RenderState) _full_default_state;
   static CPT(RenderState) _full_default_state;
 
 
-  // This iterator records the entry corresponding to this RenderState
-  // object in the above global set.  We keep the iterator around so
-  // we can remove it when the RenderState destructs.
-  States::iterator _saved_entry;
+  // This iterator records the entry corresponding to this
+  // RenderState object in the above global set.  We keep the index
+  // around so we can remove it when the RenderState destructs.
+  int _saved_entry;
 
 
   // This data structure manages the job of caching the composition of
   // This data structure manages the job of caching the composition of
   // two RenderStates.  It's complicated because we have to be sure to
   // two RenderStates.  It's complicated because we have to be sure to
@@ -263,9 +270,16 @@ private:
   UpdateSeq _cycle_detect;
   UpdateSeq _cycle_detect;
   static UpdateSeq _last_cycle_detect;
   static UpdateSeq _last_cycle_detect;
 
 
+  // This keeps track of our current position through the garbage
+  // collection cycle.
+  static int _garbage_index;
+
   static PStatCollector _cache_update_pcollector;
   static PStatCollector _cache_update_pcollector;
+  static PStatCollector _garbage_collect_pcollector;
   static PStatCollector _state_compose_pcollector;
   static PStatCollector _state_compose_pcollector;
   static PStatCollector _state_invert_pcollector;
   static PStatCollector _state_invert_pcollector;
+  static PStatCollector _state_break_cycles_pcollector;
+  static PStatCollector _state_validate_pcollector;
 
 
   static PStatCollector _node_counter;
   static PStatCollector _node_counter;
   static PStatCollector _cache_counter;
   static PStatCollector _cache_counter;
@@ -295,12 +309,14 @@ private:
   // be a CullBinAttrib in the state.
   // be a CullBinAttrib in the state.
   int _bin_index;
   int _bin_index;
   int _draw_order;
   int _draw_order;
+  size_t _hash;
 
 
   enum Flags {
   enum Flags {
     F_checked_bin_index     = 0x000001,
     F_checked_bin_index     = 0x000001,
     F_checked_cull_callback = 0x000002,
     F_checked_cull_callback = 0x000002,
     F_has_cull_callback     = 0x000004,
     F_has_cull_callback     = 0x000004,
     F_is_destructing        = 0x000008,
     F_is_destructing        = 0x000008,
+    F_hash_known            = 0x000010,
   };
   };
   unsigned int _flags;
   unsigned int _flags;
 
 

+ 17 - 0
panda/src/pgraph/rescaleNormalAttrib.cxx

@@ -96,6 +96,23 @@ compare_to_impl(const RenderAttrib *other) const {
   return (int)_mode - (int)ta->_mode;
   return (int)_mode - (int)ta->_mode;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: RescaleNormalAttrib::get_hash_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived RenderAttrib
+//               types to return a unique hash for these particular
+//               properties.  RenderAttribs that compare the same with
+//               compare_to_impl(), above, should return the same
+//               hash; RenderAttribs that compare differently should
+//               return a different hash.
+////////////////////////////////////////////////////////////////////
+size_t RescaleNormalAttrib::
+get_hash_impl() const {
+  size_t hash = 0;
+  hash = int_hash::add_hash(hash, (int)_mode);
+  return hash;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: RescaleNormalAttrib::register_with_read_factory
 //     Function: RescaleNormalAttrib::register_with_read_factory
 //       Access: Public, Static
 //       Access: Public, Static

+ 1 - 0
panda/src/pgraph/rescaleNormalAttrib.h

@@ -58,6 +58,7 @@ public:
 
 
 protected:
 protected:
   virtual int compare_to_impl(const RenderAttrib *other) const;
   virtual int compare_to_impl(const RenderAttrib *other) const;
+  virtual size_t get_hash_impl() const;
 
 
 private:
 private:
   Mode _mode;
   Mode _mode;

+ 17 - 0
panda/src/pgraph/scissorAttrib.cxx

@@ -115,6 +115,23 @@ compare_to_impl(const RenderAttrib *other) const {
   return _frame.compare_to(ta->_frame);
   return _frame.compare_to(ta->_frame);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: ScissorAttrib::get_hash_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived RenderAttrib
+//               types to return a unique hash for these particular
+//               properties.  RenderAttribs that compare the same with
+//               compare_to_impl(), above, should return the same
+//               hash; RenderAttribs that compare differently should
+//               return a different hash.
+////////////////////////////////////////////////////////////////////
+size_t ScissorAttrib::
+get_hash_impl() const {
+  size_t hash = 0;
+  hash = _frame.add_hash(hash);
+  return hash;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ScissorAttrib::compose_impl
 //     Function: ScissorAttrib::compose_impl
 //       Access: Protected, Virtual
 //       Access: Protected, Virtual

+ 1 - 0
panda/src/pgraph/scissorAttrib.h

@@ -55,6 +55,7 @@ public:
 
 
 protected:
 protected:
   virtual int compare_to_impl(const RenderAttrib *other) const;
   virtual int compare_to_impl(const RenderAttrib *other) const;
+  virtual size_t get_hash_impl() const;
   virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const;
   virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const;
 
 
 private:
 private:

+ 17 - 0
panda/src/pgraph/shadeModelAttrib.cxx

@@ -89,6 +89,23 @@ compare_to_impl(const RenderAttrib *other) const {
   return (int)_mode - (int)ta->_mode;
   return (int)_mode - (int)ta->_mode;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: ShadeModelAttrib::get_hash_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived RenderAttrib
+//               types to return a unique hash for these particular
+//               properties.  RenderAttribs that compare the same with
+//               compare_to_impl(), above, should return the same
+//               hash; RenderAttribs that compare differently should
+//               return a different hash.
+////////////////////////////////////////////////////////////////////
+size_t ShadeModelAttrib::
+get_hash_impl() const {
+  size_t hash = 0;
+  hash = int_hash::add_hash(hash, (int)_mode);
+  return hash;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ShadeModelAttrib::compose_impl
 //     Function: ShadeModelAttrib::compose_impl
 //       Access: Protected, Virtual
 //       Access: Protected, Virtual

+ 1 - 0
panda/src/pgraph/shadeModelAttrib.h

@@ -47,6 +47,7 @@ public:
 
 
 protected:
 protected:
   virtual int compare_to_impl(const RenderAttrib *other) const;
   virtual int compare_to_impl(const RenderAttrib *other) const;
+  virtual size_t get_hash_impl() const;
   virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const;
   virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const;
 
 
 private:
 private:

+ 34 - 0
panda/src/pgraph/shaderAttrib.cxx

@@ -799,6 +799,40 @@ compare_to_impl(const RenderAttrib *other) const {
   return 0;
   return 0;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: ShaderAttrib::get_hash_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived RenderAttrib
+//               types to return a unique hash for these particular
+//               properties.  RenderAttribs that compare the same with
+//               compare_to_impl(), above, should return the same
+//               hash; RenderAttribs that compare differently should
+//               return a different hash.
+////////////////////////////////////////////////////////////////////
+size_t ShaderAttrib::
+get_hash_impl() const {
+  size_t hash = 0;
+  hash = pointer_hash::add_hash(hash, _shader);
+  hash = int_hash::add_hash(hash, _shader_priority);
+  hash = int_hash::add_hash(hash, (int)_auto_shader);
+  hash = int_hash::add_hash(hash, (int)_has_shader);
+  hash = int_hash::add_hash(hash, _flags);
+  hash = int_hash::add_hash(hash, _has_flags);
+  hash = int_hash::add_hash(hash, _instance_count);
+  hash = int_hash::add_hash(hash, (int)_auto_normal_on);
+  hash = int_hash::add_hash(hash, (int)_auto_glow_on);
+  hash = int_hash::add_hash(hash, (int)_auto_gloss_on);
+  hash = int_hash::add_hash(hash, (int)_auto_shadow_on);
+
+  Inputs::const_iterator ii;
+  for (ii = _inputs.begin(); ii != _inputs.end(); ++ii) {
+    hash = pointer_hash::add_hash(hash, (*ii).first);
+    hash = pointer_hash::add_hash(hash, (*ii).second);
+  }
+
+  return hash;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderAttrib::compose_impl
 //     Function: ShaderAttrib::compose_impl
 //       Access: Public, Virtual
 //       Access: Public, Virtual

+ 1 - 0
panda/src/pgraph/shaderAttrib.h

@@ -134,6 +134,7 @@ public:
 
 
 protected:
 protected:
   virtual int compare_to_impl(const RenderAttrib *other) const;
   virtual int compare_to_impl(const RenderAttrib *other) const;
+  virtual size_t get_hash_impl() const;
   virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const;
   virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const;
   
   
 private:
 private:

+ 19 - 0
panda/src/pgraph/stencilAttrib.cxx

@@ -328,6 +328,25 @@ compare_to_impl(const RenderAttrib *other) const {
   return compare_result;
   return compare_result;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: StencilAttrib::get_hash_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived RenderAttrib
+//               types to return a unique hash for these particular
+//               properties.  RenderAttribs that compare the same with
+//               compare_to_impl(), above, should return the same
+//               hash; RenderAttribs that compare differently should
+//               return a different hash.
+////////////////////////////////////////////////////////////////////
+size_t StencilAttrib::
+get_hash_impl() const {
+  size_t hash = 0;
+  for (int index = 0; index < SRS_total; index++) {
+    hash = int_hash::add_hash(hash, (int)_stencil_render_states[index]);
+  }
+  return hash;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: StencilAttrib::register_with_read_factory
 //     Function: StencilAttrib::register_with_read_factory
 //       Access: Public, Static
 //       Access: Public, Static

+ 1 - 0
panda/src/pgraph/stencilAttrib.h

@@ -159,6 +159,7 @@ public:
 
 
 protected:
 protected:
   virtual int compare_to_impl(const RenderAttrib *other) const;
   virtual int compare_to_impl(const RenderAttrib *other) const;
+  virtual size_t get_hash_impl() const;
 
 
 private:
 private:
   unsigned int _stencil_render_states [SRS_total];
   unsigned int _stencil_render_states [SRS_total];

+ 28 - 0
panda/src/pgraph/texGenAttrib.cxx

@@ -411,6 +411,34 @@ compare_to_impl(const RenderAttrib *other) const {
   return 0;
   return 0;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: TexGenAttrib::get_hash_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived RenderAttrib
+//               types to return a unique hash for these particular
+//               properties.  RenderAttribs that compare the same with
+//               compare_to_impl(), above, should return the same
+//               hash; RenderAttribs that compare differently should
+//               return a different hash.
+////////////////////////////////////////////////////////////////////
+size_t TexGenAttrib::
+get_hash_impl() const {
+  size_t hash = 0;
+  Stages::const_iterator ri;
+  for (ri = _stages.begin(); ri != _stages.end(); ++ri) {
+    const TextureStage *stage = (*ri).first;
+    const ModeDef &mode_def = (*ri).second;
+
+    hash = pointer_hash::add_hash(hash, stage);
+    hash = int_hash::add_hash(hash, (int)mode_def._mode);
+    hash = string_hash::add_hash(hash, mode_def._source_name);
+    hash = int_hash::add_hash(hash, mode_def._light.get_key());
+    hash = mode_def._constant_value.add_hash(hash);
+  }
+
+  return hash;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: TexGenAttrib::compose_impl
 //     Function: TexGenAttrib::compose_impl
 //       Access: Protected, Virtual
 //       Access: Protected, Virtual

+ 1 - 0
panda/src/pgraph/texGenAttrib.h

@@ -76,6 +76,7 @@ public:
 
 
 protected:
 protected:
   virtual int compare_to_impl(const RenderAttrib *other) const;
   virtual int compare_to_impl(const RenderAttrib *other) const;
+  virtual size_t get_hash_impl() const;
   virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const;
   virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const;
   virtual CPT(RenderAttrib) invert_compose_impl(const RenderAttrib *other) const;
   virtual CPT(RenderAttrib) invert_compose_impl(const RenderAttrib *other) const;
 
 

+ 25 - 0
panda/src/pgraph/texMatrixAttrib.cxx

@@ -282,6 +282,31 @@ compare_to_impl(const RenderAttrib *other) const {
   return 0;
   return 0;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: TexMatrixAttrib::get_hash_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived RenderAttrib
+//               types to return a unique hash for these particular
+//               properties.  RenderAttribs that compare the same with
+//               compare_to_impl(), above, should return the same
+//               hash; RenderAttribs that compare differently should
+//               return a different hash.
+////////////////////////////////////////////////////////////////////
+size_t TexMatrixAttrib::
+get_hash_impl() const {
+  size_t hash = 0;
+  Stages::const_iterator si;
+  for (si = _stages.begin(); si != _stages.end(); ++si) {
+    const StageNode &sn = (*si);
+
+    hash = pointer_hash::add_hash(hash, sn._stage);
+    hash = pointer_hash::add_hash(hash, sn._transform);
+    hash = int_hash::add_hash(hash, sn._override);
+  }
+
+  return hash;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: TexMatrixAttrib::compose_impl
 //     Function: TexMatrixAttrib::compose_impl
 //       Access: Protected, Virtual
 //       Access: Protected, Virtual

+ 1 - 0
panda/src/pgraph/texMatrixAttrib.h

@@ -67,6 +67,7 @@ public:
 
 
 protected:
 protected:
   virtual int compare_to_impl(const RenderAttrib *other) const;
   virtual int compare_to_impl(const RenderAttrib *other) const;
+  virtual size_t get_hash_impl() const;
   virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const;
   virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const;
   virtual CPT(RenderAttrib) invert_compose_impl(const RenderAttrib *other) const;
   virtual CPT(RenderAttrib) invert_compose_impl(const RenderAttrib *other) const;
 
 

+ 38 - 0
panda/src/pgraph/textureAttrib.cxx

@@ -560,6 +560,44 @@ compare_to_impl(const RenderAttrib *other) const {
   return 0;
   return 0;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: TextureAttrib::get_hash_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived RenderAttrib
+//               types to return a unique hash for these particular
+//               properties.  RenderAttribs that compare the same with
+//               compare_to_impl(), above, should return the same
+//               hash; RenderAttribs that compare differently should
+//               return a different hash.
+////////////////////////////////////////////////////////////////////
+size_t TextureAttrib::
+get_hash_impl() const {
+  size_t hash = 0;
+  Stages::const_iterator si;
+  for (si = _on_stages.begin(); si != _on_stages.end(); ++si) {
+    const StageNode &sn = (*si);
+
+    hash = pointer_hash::add_hash(hash, sn._stage);
+    hash = pointer_hash::add_hash(hash, sn._texture);
+    hash = int_hash::add_hash(hash, sn._ff_tc_index);
+    hash = int_hash::add_hash(hash, (int)sn._implicit_sort);
+    hash = int_hash::add_hash(hash, sn._override);
+  }
+
+  // This bool value goes here, between the two lists, to
+  // differentiate between the two.
+  hash = int_hash::add_hash(hash, (int)_off_all_stages);
+
+  for (si = _off_stages.begin(); si != _off_stages.end(); ++si) {
+    const StageNode &sn = (*si);
+
+    hash = pointer_hash::add_hash(hash, sn._stage);
+    hash = int_hash::add_hash(hash, sn._override);
+  }
+
+  return hash;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: TextureAttrib::compose_impl
 //     Function: TextureAttrib::compose_impl
 //       Access: Protected, Virtual
 //       Access: Protected, Virtual

+ 1 - 0
panda/src/pgraph/textureAttrib.h

@@ -90,6 +90,7 @@ public:
 
 
 protected:
 protected:
   virtual int compare_to_impl(const RenderAttrib *other) const;
   virtual int compare_to_impl(const RenderAttrib *other) const;
+  virtual size_t get_hash_impl() const;
   virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const;
   virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const;
   virtual CPT(RenderAttrib) invert_compose_impl(const RenderAttrib *other) const;
   virtual CPT(RenderAttrib) invert_compose_impl(const RenderAttrib *other) const;
 
 

+ 3 - 2
panda/src/pgraph/transformState.cxx

@@ -1267,7 +1267,8 @@ garbage_collect() {
         delete state;
         delete state;
       }
       }
     }      
     }      
-    ++si;
+    
+    si = (si + 1) % size;
   } while (si != stop_at_element);
   } while (si != stop_at_element);
   _garbage_index = si;
   _garbage_index = si;
 
 
@@ -1579,7 +1580,7 @@ return_unique(TransformState *state) {
 
 
   if (state->_saved_entry != -1) {
   if (state->_saved_entry != -1) {
     // This state is already in the cache.
     // This state is already in the cache.
-    nassertr(_states->find(state) == state->_saved_entry, state);
+    //nassertr(_states->find(state) == state->_saved_entry, state);
     return state;
     return state;
   }
   }
 
 

+ 0 - 1
panda/src/pgraph/transformState.h

@@ -20,7 +20,6 @@
 #include "nodeCachedReferenceCount.h"
 #include "nodeCachedReferenceCount.h"
 #include "pointerTo.h"
 #include "pointerTo.h"
 #include "luse.h"
 #include "luse.h"
-#include "pset.h"
 #include "event.h"
 #include "event.h"
 #include "updateSeq.h"
 #include "updateSeq.h"
 #include "pStatCollector.h"
 #include "pStatCollector.h"

+ 17 - 0
panda/src/pgraph/transparencyAttrib.cxx

@@ -106,6 +106,23 @@ compare_to_impl(const RenderAttrib *other) const {
   return (int)_mode - (int)ta->_mode;
   return (int)_mode - (int)ta->_mode;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: TransparencyAttrib::get_hash_impl
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by derived RenderAttrib
+//               types to return a unique hash for these particular
+//               properties.  RenderAttribs that compare the same with
+//               compare_to_impl(), above, should return the same
+//               hash; RenderAttribs that compare differently should
+//               return a different hash.
+////////////////////////////////////////////////////////////////////
+size_t TransparencyAttrib::
+get_hash_impl() const {
+  size_t hash = 0;
+  hash = int_hash::add_hash(hash, (int)_mode);
+  return hash;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: TransparencyAttrib::register_with_read_factory
 //     Function: TransparencyAttrib::register_with_read_factory
 //       Access: Public, Static
 //       Access: Public, Static

+ 1 - 0
panda/src/pgraph/transparencyAttrib.h

@@ -62,6 +62,7 @@ public:
 
 
 protected:
 protected:
   virtual int compare_to_impl(const RenderAttrib *other) const;
   virtual int compare_to_impl(const RenderAttrib *other) const;
+  virtual size_t get_hash_impl() const;
 
 
 private:
 private:
   Mode _mode;
   Mode _mode;