|
|
@@ -35,7 +35,10 @@
|
|
|
#include "datagramIterator.h"
|
|
|
#include "indent.h"
|
|
|
#include "compareTo.h"
|
|
|
-
|
|
|
+#include "reMutexHolder.h"
|
|
|
+#include "thread.h"
|
|
|
+
|
|
|
+ReMutex *RenderState::_states_lock = NULL;
|
|
|
RenderState::States *RenderState::_states = NULL;
|
|
|
CPT(RenderState) RenderState::_empty_state;
|
|
|
UpdateSeq RenderState::_last_cycle_detect;
|
|
|
@@ -59,12 +62,7 @@ TypeHandle RenderState::_type_handle;
|
|
|
RenderState::
|
|
|
RenderState() {
|
|
|
if (_states == (States *)NULL) {
|
|
|
- // Make sure the global _states map is allocated. This only has
|
|
|
- // to be done once. We could make this map static, but then we
|
|
|
- // run into problems if anyone creates a RenderState object at
|
|
|
- // static init time; it also seems to cause problems when the
|
|
|
- // Panda shared library is unloaded at application exit time.
|
|
|
- _states = new States;
|
|
|
+ init_states();
|
|
|
}
|
|
|
_saved_entry = _states->end();
|
|
|
_flags = 0;
|
|
|
@@ -103,12 +101,13 @@ RenderState::
|
|
|
nassertv(!is_destructing());
|
|
|
set_destructing();
|
|
|
|
|
|
+ ReMutexHolder holder(*_states_lock);
|
|
|
if (_saved_entry != _states->end()) {
|
|
|
nassertv(_states->find(this) == _saved_entry);
|
|
|
_states->erase(_saved_entry);
|
|
|
_saved_entry = _states->end();
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
remove_cache_pointers();
|
|
|
|
|
|
// If this was true at the beginning of the destructor, but is no
|
|
|
@@ -319,6 +318,8 @@ compose(const RenderState *other) const {
|
|
|
return this;
|
|
|
}
|
|
|
|
|
|
+ ReMutexHolder holder(*_states_lock);
|
|
|
+
|
|
|
// Is this composition already cached?
|
|
|
CompositionCache::const_iterator ci = _composition_cache.find(other);
|
|
|
if (ci != _composition_cache.end()) {
|
|
|
@@ -397,6 +398,8 @@ invert_compose(const RenderState *other) const {
|
|
|
return make_empty();
|
|
|
}
|
|
|
|
|
|
+ ReMutexHolder holder(*_states_lock);
|
|
|
+
|
|
|
// Is this composition already cached?
|
|
|
CompositionCache::const_iterator ci = _invert_composition_cache.find(other);
|
|
|
if (ci != _invert_composition_cache.end()) {
|
|
|
@@ -590,6 +593,8 @@ get_override(TypeHandle type) const {
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
int RenderState::
|
|
|
unref() const {
|
|
|
+ ReMutexHolder holder(*_states_lock);
|
|
|
+
|
|
|
if (get_cache_ref_count() > 0 &&
|
|
|
get_ref_count() == get_cache_ref_count() + 1) {
|
|
|
// If we are about to remove the one reference that is not in the
|
|
|
@@ -598,11 +603,6 @@ unref() const {
|
|
|
// it exists.
|
|
|
|
|
|
if (auto_break_cycles) {
|
|
|
- // There might be a tiny race condition if multiple different
|
|
|
- // threads perform cycle detects on related nodes at the same
|
|
|
- // time. But the cost of failing the race condition is low--we
|
|
|
- // end up with a tiny leak that may eventually be discovered, big
|
|
|
- // deal.
|
|
|
++_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
|
|
|
@@ -611,6 +611,7 @@ unref() const {
|
|
|
pgraph_cat.debug()
|
|
|
<< "Breaking cycle involving " << (*this) << "\n";
|
|
|
}
|
|
|
+
|
|
|
((RenderState *)this)->remove_cache_pointers();
|
|
|
}
|
|
|
}
|
|
|
@@ -684,6 +685,7 @@ get_num_states() {
|
|
|
if (_states == (States *)NULL) {
|
|
|
return 0;
|
|
|
}
|
|
|
+ ReMutexHolder holder(*_states_lock);
|
|
|
return _states->size();
|
|
|
}
|
|
|
|
|
|
@@ -710,6 +712,7 @@ get_num_unused_states() {
|
|
|
if (_states == (States *)NULL) {
|
|
|
return 0;
|
|
|
}
|
|
|
+ ReMutexHolder holder(*_states_lock);
|
|
|
|
|
|
// First, we need to count the number of times each RenderState
|
|
|
// object is recorded in the cache.
|
|
|
@@ -802,6 +805,7 @@ clear_cache() {
|
|
|
if (_states == (States *)NULL) {
|
|
|
return 0;
|
|
|
}
|
|
|
+ ReMutexHolder holder(*_states_lock);
|
|
|
|
|
|
PStatTimer timer(_cache_update_pcollector);
|
|
|
int orig_size = _states->size();
|
|
|
@@ -878,6 +882,11 @@ clear_cache() {
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
void RenderState::
|
|
|
list_cycles(ostream &out) {
|
|
|
+ if (_states == (States *)NULL) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ ReMutexHolder holder(*_states_lock);
|
|
|
+
|
|
|
typedef pset<const RenderState *> VisitedStates;
|
|
|
VisitedStates visited;
|
|
|
CompositionCycleDesc cycle_desc;
|
|
|
@@ -928,6 +937,12 @@ list_cycles(ostream &out) {
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
void RenderState::
|
|
|
list_states(ostream &out) {
|
|
|
+ if (_states == (States *)NULL) {
|
|
|
+ out << "0 states:\n";
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ ReMutexHolder holder(*_states_lock);
|
|
|
+
|
|
|
out << _states->size() << " states:\n";
|
|
|
States::const_iterator si;
|
|
|
for (si = _states->begin(); si != _states->end(); ++si) {
|
|
|
@@ -948,6 +963,11 @@ list_states(ostream &out) {
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
bool RenderState::
|
|
|
validate_states() {
|
|
|
+ if (_states == (States *)NULL) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ ReMutexHolder holder(*_states_lock);
|
|
|
if (_states->empty()) {
|
|
|
return true;
|
|
|
}
|
|
|
@@ -1021,7 +1041,7 @@ bin_removed(int bin_index) {
|
|
|
// Do something here.
|
|
|
nassertv(false);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
// Function: RenderState::return_new
|
|
|
// Access: Private, Static
|
|
|
@@ -1038,16 +1058,18 @@ CPT(RenderState) RenderState::
|
|
|
return_new(RenderState *state) {
|
|
|
nassertr(state != (RenderState *)NULL, state);
|
|
|
|
|
|
- // This should be a newly allocated pointer, not one that was used
|
|
|
- // for anything else.
|
|
|
- nassertr(state->_saved_entry == _states->end(), state);
|
|
|
-
|
|
|
#ifndef NDEBUG
|
|
|
if (paranoid_const) {
|
|
|
nassertr(validate_states(), state);
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
+ ReMutexHolder holder(*_states_lock);
|
|
|
+
|
|
|
+ // This should be a newly allocated pointer, not one that was used
|
|
|
+ // for anything else.
|
|
|
+ nassertr(state->_saved_entry == _states->end(), state);
|
|
|
+
|
|
|
// Save the state in a local PointerTo so that it will be freed at
|
|
|
// the end of this function if no one else uses it.
|
|
|
CPT(RenderState) pt_state = state;
|
|
|
@@ -1273,12 +1295,13 @@ r_detect_cycles(const RenderState *start_state,
|
|
|
// particular RenderState. The pointers to this
|
|
|
// object may be scattered around in the various
|
|
|
// CompositionCaches from other RenderState objects.
|
|
|
+//
|
|
|
+// You must already be holding _states_lock before you
|
|
|
+// call this method.
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
void RenderState::
|
|
|
remove_cache_pointers() {
|
|
|
- // Now make sure we clean up all other floating pointers to the
|
|
|
- // RenderState. These may be scattered around in the various
|
|
|
- // CompositionCaches from other RenderState objects.
|
|
|
+ nassertv(_states_lock->debug_is_locked());
|
|
|
|
|
|
// Fortunately, since we added CompositionCache records in pairs, we
|
|
|
// know exactly the set of RenderState objects that have us in their
|
|
|
@@ -1633,6 +1656,29 @@ update_pstats(int old_referenced_bits, int new_referenced_bits) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: RenderState::init_states
|
|
|
+// Access: Public, Static
|
|
|
+// Description: Make sure the global _states map is allocated. This
|
|
|
+// only has to be done once. We could make this map
|
|
|
+// static, but then we run into problems if anyone
|
|
|
+// creates a RenderState object at static init time;
|
|
|
+// it also seems to cause problems when the Panda shared
|
|
|
+// library is unloaded at application exit time.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+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, presumably when there is still only
|
|
|
+ // one thread in the world.
|
|
|
+ _states_lock = new ReMutex;
|
|
|
+ nassertv(Thread::get_current_thread() == Thread::get_main_thread());
|
|
|
+}
|
|
|
+
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
// Function: RenderState::register_with_read_factory
|