Browse Source

pgraph: take advantage of constant initialization for states maps

SimpleHashMap has a constexpr default constructor, so there is no static init ordering issue that requires us to allocate the states maps on the heap and then to leak them.
rdb 7 years ago
parent
commit
193ae8a3a6

+ 2 - 2
panda/src/display/graphicsStateGuardian.cxx

@@ -301,9 +301,9 @@ GraphicsStateGuardian::
   // Note that if uniquify-states is false, we can't iterate over all the
   // states, and some GSGs will linger.  Let's hope this isn't a problem.
   LightReMutexHolder holder(*RenderState::_states_lock);
-  size_t size = RenderState::_states->get_num_entries();
+  size_t size = RenderState::_states.get_num_entries();
   for (size_t si = 0; si < size; ++si) {
-    const RenderState *state = RenderState::_states->get_key(si);
+    const RenderState *state = RenderState::_states.get_key(si);
     state->_mungers.remove(_id);
     state->_munged_states.remove(_id);
   }

+ 26 - 32
panda/src/pgraph/renderAttrib.cxx

@@ -21,7 +21,7 @@
 using std::ostream;
 
 LightReMutex *RenderAttrib::_attribs_lock = nullptr;
-RenderAttrib::Attribs *RenderAttrib::_attribs = nullptr;
+RenderAttrib::Attribs RenderAttrib::_attribs;
 TypeHandle RenderAttrib::_type_handle;
 
 size_t RenderAttrib::_garbage_index = 0;
@@ -33,7 +33,7 @@ PStatCollector RenderAttrib::_garbage_collect_pcollector("*:State Cache:Garbage
  */
 RenderAttrib::
 RenderAttrib() {
-  if (_attribs == nullptr) {
+  if (_attribs_lock == nullptr) {
     init_attribs();
   }
   _saved_entry = -1;
@@ -156,11 +156,7 @@ write(ostream &out, int indent_level) const {
 int RenderAttrib::
 get_num_attribs() {
   LightReMutexHolder holder(*_attribs_lock);
-
-  if (_attribs == nullptr) {
-    return 0;
-  }
-  return _attribs->get_num_entries();
+  return _attribs.get_num_entries();
 }
 
 /**
@@ -172,10 +168,10 @@ void RenderAttrib::
 list_attribs(ostream &out) {
   LightReMutexHolder holder(*_attribs_lock);
 
-  size_t size = _attribs->get_num_entries();
+  size_t size = _attribs.get_num_entries();
   out << size << " attribs:\n";
   for (size_t si = 0; si < size; ++si) {
-    const RenderAttrib *attrib = _attribs->get_key(si);
+    const RenderAttrib *attrib = _attribs.get_key(si);
     attrib->write(out, 2);
   }
 }
@@ -186,16 +182,16 @@ list_attribs(ostream &out) {
  */
 int RenderAttrib::
 garbage_collect() {
-  if (_attribs == nullptr || !garbage_collect_states) {
+  if (!garbage_collect_states) {
     return 0;
   }
   LightReMutexHolder holder(*_attribs_lock);
 
   PStatTimer timer(_garbage_collect_pcollector);
-  size_t orig_size = _attribs->get_num_entries();
+  size_t orig_size = _attribs.get_num_entries();
 
 #ifdef _DEBUG
-  nassertr(_attribs->validate(), 0);
+  nassertr(_attribs.validate(), 0);
 #endif
 
   // How many elements to process this pass?
@@ -214,7 +210,7 @@ garbage_collect() {
   size_t stop_at_element = (si + num_this_pass) % size;
 
   do {
-    RenderAttrib *attrib = (RenderAttrib *)_attribs->get_key(si);
+    RenderAttrib *attrib = (RenderAttrib *)_attribs.get_key(si);
     if (attrib->get_ref_count() == 1) {
       // This attrib has recently been unreffed to 1 (the one we added when
       // we stored it in the cache).  Now it's time to delete it.  This is
@@ -238,15 +234,15 @@ garbage_collect() {
   } while (si != stop_at_element);
   _garbage_index = si;
 
-  nassertr(_attribs->get_num_entries() == size, 0);
+  nassertr(_attribs.get_num_entries() == size, 0);
 
 #ifdef _DEBUG
-  nassertr(_attribs->validate(), 0);
+  nassertr(_attribs.validate(), 0);
 #endif
 
   // If we just cleaned up a lot of attribs, see if we can reduce the table in
   // size.  This will help reduce iteration overhead in the future.
-  _attribs->consider_shrink_table();
+  _attribs.consider_shrink_table();
 
   return (int)orig_size - (int)size;
 }
@@ -259,17 +255,17 @@ garbage_collect() {
 bool RenderAttrib::
 validate_attribs() {
   LightReMutexHolder holder(*_attribs_lock);
-  if (_attribs->is_empty()) {
+  if (_attribs.is_empty()) {
     return true;
   }
 
-  if (!_attribs->validate()) {
+  if (!_attribs.validate()) {
     pgraph_cat.error()
       << "RenderAttrib::_attribs cache is invalid!\n";
 
-    size_t size = _attribs->get_num_entries();
+    size_t size = _attribs.get_num_entries();
     for (size_t si = 0; si < size; ++si) {
-      const RenderAttrib *attrib = _attribs->get_key(si);
+      const RenderAttrib *attrib = _attribs.get_key(si);
       //cerr << si << ": " << attrib << "\n";
       attrib->write(std::cerr, 2);
     }
@@ -277,16 +273,16 @@ validate_attribs() {
     return false;
   }
 
-  size_t size = _attribs->get_num_entries();
+  size_t size = _attribs.get_num_entries();
   size_t si = 0;
   nassertr(si < size, false);
-  nassertr(_attribs->get_key(si)->get_ref_count() >= 0, false);
+  nassertr(_attribs.get_key(si)->get_ref_count() >= 0, false);
   size_t snext = si;
   ++snext;
   while (snext < size) {
-    nassertr(_attribs->get_key(snext)->get_ref_count() >= 0, false);
-    const RenderAttrib *ssi = _attribs->get_key(si);
-    const RenderAttrib *ssnext = _attribs->get_key(snext);
+    nassertr(_attribs.get_key(snext)->get_ref_count() >= 0, false);
+    const RenderAttrib *ssi = _attribs.get_key(si);
+    const RenderAttrib *ssnext = _attribs.get_key(snext);
     int c = ssi->compare_to(*ssnext);
     int ci = ssnext->compare_to(*ssi);
     if ((ci < 0) != (c > 0) ||
@@ -356,19 +352,19 @@ return_unique(RenderAttrib *attrib) {
   LightReMutexHolder holder(*_attribs_lock);
 
   if (attrib->_saved_entry != -1) {
-    // This attrib is already in the cache.  nassertr(_attribs->find(attrib)
+    // This attrib is already in the cache.  nassertr(_attribs.find(attrib)
     // == attrib->_saved_entry, attrib);
     return attrib;
   }
 
-  int si = _attribs->find(attrib);
+  int si = _attribs.find(attrib);
   if (si != -1) {
     // There's an equivalent attrib already in the set.  Return it.  If this
     // is a newly created RenderAttrib, though, be sure to delete it.
     if (attrib->get_ref_count() == 0) {
       delete attrib;
     }
-    return _attribs->get_key(si);
+    return _attribs.get_key(si);
   }
 
   // Not already in the set; add it.
@@ -378,7 +374,7 @@ return_unique(RenderAttrib *attrib) {
     // deleted while it's in it.
     attrib->ref();
   }
-  si = _attribs->store(attrib, nullptr);
+  si = _attribs.store(attrib, nullptr);
 
   // Save the index and return the input attrib.
   attrib->_saved_entry = si;
@@ -495,7 +491,7 @@ release_new() {
 
   if (_saved_entry != -1) {
     _saved_entry = -1;
-    nassertv_always(_attribs->remove(this));
+    nassertv_always(_attribs.remove(this));
   }
 }
 
@@ -508,8 +504,6 @@ release_new() {
  */
 void RenderAttrib::
 init_attribs() {
-  _attribs = new Attribs;
-
   // TODO: we should have a global Panda mutex to allow us to safely create
   // _attribs_lock without a startup race condition.  For the meantime, this
   // is OK because we guarantee that this method is called at static init

+ 1 - 1
panda/src/pgraph/renderAttrib.h

@@ -186,7 +186,7 @@ private:
   // This mutex protects _attribs.
   static LightReMutex *_attribs_lock;
   typedef SimpleHashMap<const RenderAttrib *, std::nullptr_t, indirect_compare_to_hash<const RenderAttrib *> > Attribs;
-  static Attribs *_attribs;
+  static Attribs _attribs;
 
   int _saved_entry;
   size_t _hash;

+ 2 - 2
panda/src/pgraph/renderAttribRegistry.cxx

@@ -91,9 +91,9 @@ register_slot(TypeHandle type_handle, int sort, RenderAttrib *default_attrib) {
 
     if (default_attrib->_saved_entry == -1) {
       // If this attribute was already registered, something odd is going on.
-      nassertr(RenderAttrib::_attribs->find(default_attrib) == -1, 0);
+      nassertr(RenderAttrib::_attribs.find(default_attrib) == -1, 0);
       default_attrib->_saved_entry =
-        RenderAttrib::_attribs->store(default_attrib, nullptr);
+        RenderAttrib::_attribs.store(default_attrib, nullptr);
     }
 
     // It effectively lives forever.  Might as well make it official.

+ 34 - 56
panda/src/pgraph/renderState.cxx

@@ -39,7 +39,7 @@
 using std::ostream;
 
 LightReMutex *RenderState::_states_lock = nullptr;
-RenderState::States *RenderState::_states = nullptr;
+RenderState::States RenderState::_states;
 const RenderState *RenderState::_empty_state = nullptr;
 UpdateSeq RenderState::_last_cycle_detect;
 size_t RenderState::_garbage_index = 0;
@@ -68,7 +68,7 @@ RenderState() :
   _flags(0),
   _lock("RenderState")
 {
-  if (_states == nullptr) {
+  if (_states_lock == nullptr) {
     init_states();
   }
   _saved_entry = -1;
@@ -716,11 +716,8 @@ get_max_priority() {
  */
 int RenderState::
 get_num_states() {
-  if (_states == nullptr) {
-    return 0;
-  }
   LightReMutexHolder holder(*_states_lock);
-  return _states->get_num_entries();
+  return _states.get_num_entries();
 }
 
 /**
@@ -738,9 +735,6 @@ get_num_states() {
  */
 int RenderState::
 get_num_unused_states() {
-  if (_states == nullptr) {
-    return 0;
-  }
   LightReMutexHolder holder(*_states_lock);
 
   // First, we need to count the number of times each RenderState object is
@@ -748,9 +742,9 @@ get_num_unused_states() {
   typedef pmap<const RenderState *, int> StateCount;
   StateCount state_count;
 
-  size_t size = _states->get_num_entries();
+  size_t size = _states.get_num_entries();
   for (size_t si = 0; si < size; ++si) {
-    const RenderState *state = _states->get_key(si);
+    const RenderState *state = _states.get_key(si);
 
     size_t i;
     size_t cache_size = state->_composition_cache.get_num_entries();
@@ -821,13 +815,10 @@ get_num_unused_states() {
  */
 int RenderState::
 clear_cache() {
-  if (_states == nullptr) {
-    return 0;
-  }
   LightReMutexHolder holder(*_states_lock);
 
   PStatTimer timer(_cache_update_pcollector);
-  int orig_size = _states->get_num_entries();
+  int orig_size = _states.get_num_entries();
 
   // First, we need to copy the entire set of states to a temporary vector,
   // reference-counting each object.  That way we can walk through the copy,
@@ -838,9 +829,9 @@ clear_cache() {
     TempStates temp_states;
     temp_states.reserve(orig_size);
 
-    size_t size = _states->get_num_entries();
+    size_t size = _states.get_num_entries();
     for (size_t si = 0; si < size; ++si) {
-      const RenderState *state = _states->get_key(si);
+      const RenderState *state = _states.get_key(si);
       temp_states.push_back(state);
     }
 
@@ -879,7 +870,7 @@ clear_cache() {
     // the various objects' caches will go away.
   }
 
-  int new_size = _states->get_num_entries();
+  int new_size = _states.get_num_entries();
   return orig_size - new_size;
 }
 
@@ -895,14 +886,14 @@ int RenderState::
 garbage_collect() {
   int num_attribs = RenderAttrib::garbage_collect();
 
-  if (_states == nullptr || !garbage_collect_states) {
+  if (!garbage_collect_states) {
     return num_attribs;
   }
 
   LightReMutexHolder holder(*_states_lock);
 
   PStatTimer timer(_garbage_collect_pcollector);
-  size_t orig_size = _states->get_num_entries();
+  size_t orig_size = _states.get_num_entries();
 
   // How many elements to process this pass?
   size_t size = orig_size;
@@ -922,7 +913,7 @@ garbage_collect() {
   size_t stop_at_element = (si + num_this_pass) % size;
 
   do {
-    RenderState *state = (RenderState *)_states->get_key(si);
+    RenderState *state = (RenderState *)_states.get_key(si);
     if (break_and_uniquify) {
       if (state->get_cache_ref_count() > 0 &&
           state->get_ref_count() == state->get_cache_ref_count()) {
@@ -959,15 +950,15 @@ garbage_collect() {
   } while (si != stop_at_element);
   _garbage_index = si;
 
-  nassertr(_states->get_num_entries() == size, 0);
+  nassertr(_states.get_num_entries() == size, 0);
 
 #ifdef _DEBUG
-  nassertr(_states->validate(), 0);
+  nassertr(_states.validate(), 0);
 #endif
 
   // If we just cleaned up a lot of states, see if we can reduce the table in
   // size.  This will help reduce iteration overhead in the future.
-  _states->consider_shrink_table();
+  _states.consider_shrink_table();
 
   return (int)orig_size - (int)size + num_attribs;
 }
@@ -980,9 +971,9 @@ void RenderState::
 clear_munger_cache() {
   LightReMutexHolder holder(*_states_lock);
 
-  size_t size = _states->get_num_entries();
+  size_t size = _states.get_num_entries();
   for (size_t si = 0; si < size; ++si) {
-    RenderState *state = (RenderState *)(_states->get_key(si));
+    RenderState *state = (RenderState *)(_states.get_key(si));
     state->_mungers.clear();
     state->_munged_states.clear();
     state->_last_mi = -1;
@@ -1004,18 +995,15 @@ clear_munger_cache() {
  */
 void RenderState::
 list_cycles(ostream &out) {
-  if (_states == nullptr) {
-    return;
-  }
   LightReMutexHolder holder(*_states_lock);
 
   typedef pset<const RenderState *> VisitedStates;
   VisitedStates visited;
   CompositionCycleDesc cycle_desc;
 
-  size_t size = _states->get_num_entries();
+  size_t size = _states.get_num_entries();
   for (size_t si = 0; si < size; ++si) {
-    const RenderState *state = _states->get_key(si);
+    const RenderState *state = _states.get_key(si);
 
     bool inserted = visited.insert(state).second;
     if (inserted) {
@@ -1081,16 +1069,12 @@ list_cycles(ostream &out) {
  */
 void RenderState::
 list_states(ostream &out) {
-  if (_states == nullptr) {
-    out << "0 states:\n";
-    return;
-  }
   LightReMutexHolder holder(*_states_lock);
 
-  size_t size = _states->get_num_entries();
+  size_t size = _states.get_num_entries();
   out << size << " states:\n";
   for (size_t si = 0; si < size; ++si) {
-    const RenderState *state = _states->get_key(si);
+    const RenderState *state = _states.get_key(si);
     state->write(out, 2);
   }
 }
@@ -1103,33 +1087,29 @@ list_states(ostream &out) {
  */
 bool RenderState::
 validate_states() {
-  if (_states == nullptr) {
-    return true;
-  }
-
   PStatTimer timer(_state_validate_pcollector);
 
   LightReMutexHolder holder(*_states_lock);
-  if (_states->is_empty()) {
+  if (_states.is_empty()) {
     return true;
   }
 
-  if (!_states->validate()) {
+  if (!_states.validate()) {
     pgraph_cat.error()
       << "RenderState::_states cache is invalid!\n";
     return false;
   }
 
-  size_t size = _states->get_num_entries();
+  size_t size = _states.get_num_entries();
   size_t si = 0;
   nassertr(si < size, false);
-  nassertr(_states->get_key(si)->get_ref_count() >= 0, false);
+  nassertr(_states.get_key(si)->get_ref_count() >= 0, false);
   size_t snext = si;
   ++snext;
   while (snext < size) {
-    nassertr(_states->get_key(snext)->get_ref_count() >= 0, false);
-    const RenderState *ssi = _states->get_key(si);
-    const RenderState *ssnext = _states->get_key(snext);
+    nassertr(_states.get_key(snext)->get_ref_count() >= 0, false);
+    const RenderState *ssi = _states.get_key(si);
+    const RenderState *ssnext = _states.get_key(snext);
     int c = ssi->compare_to(*ssnext);
     int ci = ssnext->compare_to(*ssi);
     if ((ci < 0) != (c > 0) ||
@@ -1299,7 +1279,7 @@ return_unique(RenderState *state) {
   LightReMutexHolder holder(*_states_lock);
 
   if (state->_saved_entry != -1) {
-    // This state is already in the cache.  nassertr(_states->find(state) ==
+    // This state is already in the cache.  nassertr(_states.find(state) ==
     // state->_saved_entry, pt_state);
     return state;
   }
@@ -1318,7 +1298,7 @@ return_unique(RenderState *state) {
     }
   }
 
-  int si = _states->find(state);
+  int si = _states.find(state);
   if (si != -1) {
     // There's an equivalent state already in the set.  Return it.  The state
     // that was passed may be newly created and therefore may not be
@@ -1326,7 +1306,7 @@ return_unique(RenderState *state) {
     if (state->get_ref_count() == 0) {
       delete state;
     }
-    return _states->get_key(si);
+    return _states.get_key(si);
   }
 
   // Not already in the set; add it.
@@ -1336,7 +1316,7 @@ return_unique(RenderState *state) {
     // deleted while it's in it.
     state->cache_ref();
   }
-  si = _states->store(state, nullptr);
+  si = _states.store(state, nullptr);
 
   // Save the index and return the input state.
   state->_saved_entry = si;
@@ -1612,7 +1592,7 @@ release_new() {
 
   if (_saved_entry != -1) {
     _saved_entry = -1;
-    nassertv_always(_states->remove(this));
+    nassertv_always(_states.remove(this));
   }
 }
 
@@ -1849,8 +1829,6 @@ update_pstats(int old_referenced_bits, int new_referenced_bits) {
  */
 void RenderState::
 init_states() {
-  _states = new States;
-
   // TODO: we should have a global Panda mutex to allow us to safely create
   // _states_lock without a startup race condition.  For the meantime, this is
   // OK because we guarantee that this method is called at static init time,
@@ -1863,7 +1841,7 @@ init_states() {
   // is declared globally, and lives forever.
   RenderState *state = new RenderState;
   state->local_object();
-  state->_saved_entry = _states->store(state, nullptr);
+  state->_saved_entry = _states.store(state, nullptr);
   _empty_state = state;
 }
 

+ 1 - 1
panda/src/pgraph/renderState.h

@@ -232,7 +232,7 @@ private:
   // _invert_composition_cache.
   static LightReMutex *_states_lock;
   typedef SimpleHashMap<const RenderState *, std::nullptr_t, indirect_compare_to_hash<const RenderState *> > States;
-  static States *_states;
+  static States _states;
   static const RenderState *_empty_state;
 
   // This iterator records the entry corresponding to this RenderState object

+ 3 - 8
panda/src/pgraph/renderState_ext.cxx

@@ -118,18 +118,15 @@ get_invert_composition_cache() const {
 PyObject *Extension<RenderState>::
 get_states() {
   extern struct Dtool_PyTypedObject Dtool_RenderState;
-  if (RenderState::_states == nullptr) {
-    return PyList_New(0);
-  }
   LightReMutexHolder holder(*RenderState::_states_lock);
 
-  size_t num_states = RenderState::_states->get_num_entries();
+  size_t num_states = RenderState::_states.get_num_entries();
   PyObject *list = PyList_New(num_states);
   size_t i = 0;
 
-  size_t size = RenderState::_states->get_num_entries();
+  size_t size = RenderState::_states.get_num_entries();
   for (size_t si = 0; si < size; ++si) {
-    const RenderState *state = RenderState::_states->get_key(si);
+    const RenderState *state = RenderState::_states.get_key(si);
     state->ref();
     PyObject *a =
       DTool_CreatePyInstanceTyped((void *)state, Dtool_RenderState,
@@ -142,6 +139,4 @@ get_states() {
   return list;
 }
 
-
-
 #endif  // HAVE_PYTHON

+ 31 - 53
panda/src/pgraph/transformState.cxx

@@ -27,7 +27,7 @@
 using std::ostream;
 
 LightReMutex *TransformState::_states_lock = nullptr;
-TransformState::States *TransformState::_states = nullptr;
+TransformState::States TransformState::_states;
 CPT(TransformState) TransformState::_identity_state;
 CPT(TransformState) TransformState::_invalid_state;
 UpdateSeq TransformState::_last_cycle_detect;
@@ -57,7 +57,7 @@ TypeHandle TransformState::_type_handle;
  */
 TransformState::
 TransformState() : _lock("TransformState") {
-  if (_states == nullptr) {
+  if (_states_lock == nullptr) {
     init_states();
   }
   _saved_entry = -1;
@@ -988,11 +988,8 @@ write_composition_cache(ostream &out, int indent_level) const {
  */
 int TransformState::
 get_num_states() {
-  if (_states == nullptr) {
-    return 0;
-  }
   LightReMutexHolder holder(*_states_lock);
-  return _states->get_num_entries();
+  return _states.get_num_entries();
 }
 
 /**
@@ -1010,9 +1007,6 @@ get_num_states() {
  */
 int TransformState::
 get_num_unused_states() {
-  if (_states == nullptr) {
-    return 0;
-  }
   LightReMutexHolder holder(*_states_lock);
 
   // First, we need to count the number of times each TransformState object is
@@ -1021,9 +1015,9 @@ get_num_unused_states() {
   typedef pmap<const TransformState *, int> StateCount;
   StateCount state_count;
 
-  size_t size = _states->get_num_entries();
+  size_t size = _states.get_num_entries();
   for (size_t si = 0; si < size; ++si) {
-    const TransformState *state = _states->get_key(si);
+    const TransformState *state = _states.get_key(si);
 
     size_t i;
     size_t cache_size = state->_composition_cache.get_num_entries();
@@ -1095,13 +1089,10 @@ get_num_unused_states() {
  */
 int TransformState::
 clear_cache() {
-  if (_states == nullptr) {
-    return 0;
-  }
   LightReMutexHolder holder(*_states_lock);
 
   PStatTimer timer(_cache_update_pcollector);
-  int orig_size = _states->get_num_entries();
+  int orig_size = _states.get_num_entries();
 
   // First, we need to copy the entire set of states to a temporary vector,
   // reference-counting each object.  That way we can walk through the copy,
@@ -1112,9 +1103,9 @@ clear_cache() {
     TempStates temp_states;
     temp_states.reserve(orig_size);
 
-    size_t size = _states->get_num_entries();
+    size_t size = _states.get_num_entries();
     for (size_t si = 0; si < size; ++si) {
-      const TransformState *state = _states->get_key(si);
+      const TransformState *state = _states.get_key(si);
       temp_states.push_back(state);
     }
 
@@ -1153,7 +1144,7 @@ clear_cache() {
     // the various objects' caches will go away.
   }
 
-  int new_size = _states->get_num_entries();
+  int new_size = _states.get_num_entries();
   return orig_size - new_size;
 }
 
@@ -1165,14 +1156,14 @@ clear_cache() {
  */
 int TransformState::
 garbage_collect() {
-  if (_states == nullptr || !garbage_collect_states) {
+  if (!garbage_collect_states) {
     return 0;
   }
 
   LightReMutexHolder holder(*_states_lock);
 
   PStatTimer timer(_garbage_collect_pcollector);
-  size_t orig_size = _states->get_num_entries();
+  size_t orig_size = _states.get_num_entries();
 
   // How many elements to process this pass?
   size_t size = orig_size;
@@ -1192,7 +1183,7 @@ garbage_collect() {
   size_t stop_at_element = (si + num_this_pass) % size;
 
   do {
-    TransformState *state = (TransformState *)_states->get_key(si);
+    TransformState *state = (TransformState *)_states.get_key(si);
     if (break_and_uniquify) {
       if (state->get_cache_ref_count() > 0 &&
           state->get_ref_count() == state->get_cache_ref_count()) {
@@ -1229,15 +1220,15 @@ garbage_collect() {
   } while (si != stop_at_element);
   _garbage_index = si;
 
-  nassertr(_states->get_num_entries() == size, 0);
+  nassertr(_states.get_num_entries() == size, 0);
 
 #ifdef _DEBUG
-  nassertr(_states->validate(), 0);
+  nassertr(_states.validate(), 0);
 #endif
 
   // If we just cleaned up a lot of states, see if we can reduce the table in
   // size.  This will help reduce iteration overhead in the future.
-  _states->consider_shrink_table();
+  _states.consider_shrink_table();
 
   return (int)orig_size - (int)size;
 }
@@ -1257,18 +1248,15 @@ garbage_collect() {
  */
 void TransformState::
 list_cycles(ostream &out) {
-  if (_states == nullptr) {
-    return;
-  }
   LightReMutexHolder holder(*_states_lock);
 
   typedef pset<const TransformState *> VisitedStates;
   VisitedStates visited;
   CompositionCycleDesc cycle_desc;
 
-  size_t size = _states->get_num_entries();
+  size_t size = _states.get_num_entries();
   for (size_t si = 0; si < size; ++si) {
-    const TransformState *state = _states->get_key(si);
+    const TransformState *state = _states.get_key(si);
 
     bool inserted = visited.insert(state).second;
     if (inserted) {
@@ -1334,16 +1322,12 @@ list_cycles(ostream &out) {
  */
 void TransformState::
 list_states(ostream &out) {
-  if (_states == nullptr) {
-    out << "0 states:\n";
-    return;
-  }
   LightReMutexHolder holder(*_states_lock);
 
-  size_t size = _states->get_num_entries();
+  size_t size = _states.get_num_entries();
   out << size << " states:\n";
   for (size_t si = 0; si < size; ++si) {
-    const TransformState *state = _states->get_key(si);
+    const TransformState *state = _states.get_key(si);
     state->write(out, 2);
   }
 }
@@ -1356,36 +1340,32 @@ list_states(ostream &out) {
  */
 bool TransformState::
 validate_states() {
-  if (_states == nullptr) {
-    return true;
-  }
-
   PStatTimer timer(_transform_validate_pcollector);
 
   LightReMutexHolder holder(*_states_lock);
-  if (_states->is_empty()) {
+  if (_states.is_empty()) {
     return true;
   }
 
-  if (!_states->validate()) {
+  if (!_states.validate()) {
     pgraph_cat.error()
       << "TransformState::_states cache is invalid!\n";
     return false;
   }
 
-  size_t size = _states->get_num_entries();
+  size_t size = _states.get_num_entries();
   size_t si = 0;
   nassertr(si < size, false);
-  nassertr(_states->get_key(si)->get_ref_count() >= 0, false);
+  nassertr(_states.get_key(si)->get_ref_count() >= 0, false);
   size_t snext = si;
   ++snext;
   while (snext < size) {
-    nassertr(_states->get_key(snext)->get_ref_count() >= 0, false);
-    const TransformState *ssi = _states->get_key(si);
+    nassertr(_states.get_key(snext)->get_ref_count() >= 0, false);
+    const TransformState *ssi = _states.get_key(si);
     if (!ssi->validate_composition_cache()) {
       return false;
     }
-    const TransformState *ssnext = _states->get_key(snext);
+    const TransformState *ssnext = _states.get_key(snext);
     bool c = (*ssi) == (*ssnext);
     bool ci = (*ssnext) == (*ssi);
     if (c != ci) {
@@ -1415,8 +1395,6 @@ validate_states() {
  */
 void TransformState::
 init_states() {
-  _states = new States;
-
   ConfigVariableBool uniquify_matrix
   ("uniquify-matrix", true,
    PRC_DESC("Set this true to look up arbitrary 4x4 transform matrices in "
@@ -1483,7 +1461,7 @@ return_unique(TransformState *state) {
   LightReMutexHolder holder(*_states_lock);
 
   if (state->_saved_entry != -1) {
-    // This state is already in the cache.  nassertr(_states->find(state) ==
+    // This state is already in the cache.  nassertr(_states.find(state) ==
     // state->_saved_entry, state);
     return state;
   }
@@ -1492,10 +1470,10 @@ return_unique(TransformState *state) {
   // of this function if no one else uses it.
   CPT(TransformState) pt_state = state;
 
-  int si = _states->find(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);
+    return _states.get_key(si);
   }
 
   // Not already in the set; add it.
@@ -1505,7 +1483,7 @@ return_unique(TransformState *state) {
     // deleted while it's in it.
     state->cache_ref();
   }
-  si = _states->store(state, nullptr);
+  si = _states.store(state, nullptr);
 
   // Save the index and return the input state.
   state->_saved_entry = si;
@@ -1893,7 +1871,7 @@ release_new() {
 
   if (_saved_entry != -1) {
     _saved_entry = -1;
-    nassertv_always(_states->remove(this));
+    nassertv_always(_states.remove(this));
   }
 }
 

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

@@ -253,7 +253,7 @@ private:
   // _invert_composition_cache.
   static LightReMutex *_states_lock;
   typedef SimpleHashMap<const TransformState *, std::nullptr_t, indirect_equals_hash<const TransformState *> > States;
-  static States *_states;
+  static States _states;
   static CPT(TransformState) _identity_state;
   static CPT(TransformState) _invalid_state;
 

+ 5 - 11
panda/src/pgraph/transformState_ext.cxx

@@ -132,18 +132,15 @@ get_invert_composition_cache() const {
 PyObject *Extension<TransformState>::
 get_states() {
   extern struct Dtool_PyTypedObject Dtool_TransformState;
-  if (TransformState::_states == nullptr) {
-    return PyList_New(0);
-  }
   LightReMutexHolder holder(*TransformState::_states_lock);
 
-  size_t num_states = TransformState::_states->get_num_entries();
+  size_t num_states = TransformState::_states.get_num_entries();
   PyObject *list = PyList_New(num_states);
   size_t i = 0;
 
-  size_t size = TransformState::_states->get_num_entries();
+  size_t size = TransformState::_states.get_num_entries();
   for (size_t si = 0; si < size; ++si) {
-    const TransformState *state = TransformState::_states->get_key(si);
+    const TransformState *state = TransformState::_states.get_key(si);
     state->ref();
     PyObject *a =
       DTool_CreatePyInstanceTyped((void *)state, Dtool_TransformState,
@@ -163,15 +160,12 @@ get_states() {
 PyObject *Extension<TransformState>::
 get_unused_states() {
   extern struct Dtool_PyTypedObject Dtool_TransformState;
-  if (TransformState::_states == nullptr) {
-    return PyList_New(0);
-  }
   LightReMutexHolder holder(*TransformState::_states_lock);
 
   PyObject *list = PyList_New(0);
-  size_t size = TransformState::_states->get_num_entries();
+  size_t size = TransformState::_states.get_num_entries();
   for (size_t si = 0; si < size; ++si) {
-    const TransformState *state = TransformState::_states->get_key(si);
+    const TransformState *state = TransformState::_states.get_key(si);
     if (state->get_cache_ref_count() == state->get_ref_count()) {
       state->ref();
       PyObject *a =

+ 4 - 4
panda/src/pgraphnodes/shaderGenerator.cxx

@@ -580,9 +580,9 @@ rehash_generated_shaders() {
 
   // With uniquify-states turned on, we can actually go through all the states
   // and check whether their generated shader is still OK.
-  size_t size = RenderState::_states->get_num_entries();
+  size_t size = RenderState::_states.get_num_entries();
   for (size_t si = 0; si < size; ++si) {
-    const RenderState *state = RenderState::_states->get_key(si);
+    const RenderState *state = RenderState::_states.get_key(si);
 
     if (state->_generated_shader != nullptr) {
       ShaderKey key;
@@ -619,9 +619,9 @@ void ShaderGenerator::
 clear_generated_shaders() {
   LightReMutexHolder holder(*RenderState::_states_lock);
 
-  size_t size = RenderState::_states->get_num_entries();
+  size_t size = RenderState::_states.get_num_entries();
   for (size_t si = 0; si < size; ++si) {
-    const RenderState *state = RenderState::_states->get_key(si);
+    const RenderState *state = RenderState::_states.get_key(si);
     state->_generated_shader.clear();
   }