diff --git a/panda/src/pgraph/transformState.cxx b/panda/src/pgraph/transformState.cxx index 3b81f50c84..0833f3e847 100644 --- a/panda/src/pgraph/transformState.cxx +++ b/panda/src/pgraph/transformState.cxx @@ -768,6 +768,66 @@ unref() const { return false; } +//////////////////////////////////////////////////////////////////// +// Function: TransformState::validate_composition_cache +// Access: Published +// Description: Returns true if the composition cache and invert +// composition cache for this particular TransformState +// are self-consistent and valid, false otherwise. +//////////////////////////////////////////////////////////////////// +bool TransformState:: +validate_composition_cache() const { + LightReMutexHolder holder(*_states_lock); + + int size = _composition_cache.get_size(); + for (int i = 0; i < size; ++i) { + if (!_composition_cache.has_element(i)) { + continue; + } + const TransformState *source = _composition_cache.get_key(i); + if (source != (TransformState *)NULL) { + // Check that the source also has a pointer back to this one. We + // always add entries to the composition cache in pairs. + int ri = source->_composition_cache.find(this); + if (ri == -1) { + // Failure! There is no back-pointer. + pgraph_cat.error() + << "TransformState::composition cache is inconsistent!\n"; + pgraph_cat.error(false) + << *this << " compose " << *source << "\n"; + pgraph_cat.error(false) + << "but no reverse\n"; + return false; + } + } + } + + size = _invert_composition_cache.get_size(); + for (int i = 0; i < size; ++i) { + if (!_invert_composition_cache.has_element(i)) { + continue; + } + const TransformState *source = _invert_composition_cache.get_key(i); + if (source != (TransformState *)NULL) { + // Check that the source also has a pointer back to this one. We + // always add entries to the composition cache in pairs. + int ri = source->_invert_composition_cache.find(this); + if (ri == -1) { + // Failure! There is no back-pointer. + pgraph_cat.error() + << "TransformState::invert composition cache is inconsistent!\n"; + pgraph_cat.error(false) + << *this << " invert compose " << *source << "\n"; + pgraph_cat.error(false) + << "but no reverse\n"; + return false; + } + } + } + + return true; +} + #ifdef HAVE_PYTHON //////////////////////////////////////////////////////////////////// // Function: TransformState::get_composition_cache @@ -787,43 +847,47 @@ PyObject *TransformState:: get_composition_cache() const { IMPORT_THIS struct Dtool_PyTypedObject Dtool_TransformState; LightReMutexHolder holder(*_states_lock); - size_t cache_size = _composition_cache.get_size(); - PyObject *list = PyList_New(cache_size); - for (size_t i = 0; i < cache_size; ++i) { + size_t num_states = _composition_cache.get_num_entries(); + PyObject *list = PyList_New(num_states); + size_t i = 0; + + int size = _composition_cache.get_size(); + for (int si = 0; si < size; ++si) { + if (!_composition_cache.has_element(si)) { + continue; + } + PyObject *tuple = PyTuple_New(2); PyObject *a, *b; - if (!_composition_cache.has_element(i)) { + + const TransformState *source = _composition_cache.get_key(si); + if (source == (TransformState *)NULL) { a = Py_None; Py_INCREF(a); + } else { + source->ref(); + a = DTool_CreatePyInstanceTyped((void *)source, Dtool_TransformState, + true, true, source->get_type_index()); + } + const TransformState *result = _composition_cache.get_data(si)._result; + if (result == (TransformState *)NULL) { b = Py_None; Py_INCREF(b); } else { - const TransformState *source = _composition_cache.get_key(i); - if (source == (TransformState *)NULL) { - a = Py_None; - Py_INCREF(a); - } else { - source->ref(); - a = DTool_CreatePyInstanceTyped((void *)source, Dtool_TransformState, - true, true, source->get_type_index()); - } - const TransformState *result = _composition_cache.get_data(i)._result; - if (result == (TransformState *)NULL) { - b = Py_None; - Py_INCREF(b); - } else { - result->ref(); - b = DTool_CreatePyInstanceTyped((void *)result, Dtool_TransformState, - true, true, result->get_type_index()); - } + result->ref(); + b = DTool_CreatePyInstanceTyped((void *)result, Dtool_TransformState, + true, true, result->get_type_index()); } + PyTuple_SET_ITEM(tuple, 0, a); PyTuple_SET_ITEM(tuple, 1, b); + nassertr(i < num_states, list); PyList_SET_ITEM(list, i, tuple); + ++i; } - + nassertr(i == num_states, list); return list; } #endif // HAVE_PYTHON @@ -847,43 +911,47 @@ PyObject *TransformState:: get_invert_composition_cache() const { IMPORT_THIS struct Dtool_PyTypedObject Dtool_TransformState; LightReMutexHolder holder(*_states_lock); - size_t cache_size = _invert_composition_cache.get_size(); - PyObject *list = PyList_New(cache_size); - for (size_t i = 0; i < cache_size; ++i) { + size_t num_states = _invert_composition_cache.get_num_entries(); + PyObject *list = PyList_New(num_states); + size_t i = 0; + + int size = _invert_composition_cache.get_size(); + for (int si = 0; si < size; ++si) { + if (!_invert_composition_cache.has_element(si)) { + continue; + } + PyObject *tuple = PyTuple_New(2); PyObject *a, *b; - if (!_invert_composition_cache.has_element(i)) { + + const TransformState *source = _invert_composition_cache.get_key(si); + if (source == (TransformState *)NULL) { a = Py_None; Py_INCREF(a); + } else { + source->ref(); + a = DTool_CreatePyInstanceTyped((void *)source, Dtool_TransformState, + true, true, source->get_type_index()); + } + const TransformState *result = _invert_composition_cache.get_data(si)._result; + if (result == (TransformState *)NULL) { b = Py_None; Py_INCREF(b); } else { - const TransformState *source = _invert_composition_cache.get_key(i); - if (source == (TransformState *)NULL) { - a = Py_None; - Py_INCREF(a); - } else { - source->ref(); - a = DTool_CreatePyInstanceTyped((void *)source, Dtool_TransformState, - true, true, source->get_type_index()); - } - const TransformState *result = _invert_composition_cache.get_data(i)._result; - if (result == (TransformState *)NULL) { - b = Py_None; - Py_INCREF(b); - } else { - result->ref(); - b = DTool_CreatePyInstanceTyped((void *)result, Dtool_TransformState, - true, true, result->get_type_index()); - } + result->ref(); + b = DTool_CreatePyInstanceTyped((void *)result, Dtool_TransformState, + true, true, result->get_type_index()); } + PyTuple_SET_ITEM(tuple, 0, a); PyTuple_SET_ITEM(tuple, 1, b); + nassertr(i < num_states, list); PyList_SET_ITEM(list, i, tuple); + ++i; } - + nassertr(i == num_states, list); return list; } #endif // HAVE_PYTHON @@ -1441,6 +1509,9 @@ validate_states() { while (snext < size) { 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); int c = ssi->compare_to(*ssnext); int ci = ssnext->compare_to(*ssi); @@ -1506,6 +1577,42 @@ get_states() { } #endif // HAVE_PYTHON +#ifdef HAVE_PYTHON +//////////////////////////////////////////////////////////////////// +// Function: TransformState::get_unused_states +// Access: Published, Static +// Description: Returns a list of all of the "unused" TransformState +// objects in the state cache. See +// get_num_unused_states(). +//////////////////////////////////////////////////////////////////// +PyObject *TransformState:: +get_unused_states() { + IMPORT_THIS struct Dtool_PyTypedObject Dtool_TransformState; + if (_states == (States *)NULL) { + return PyList_New(0); + } + LightReMutexHolder holder(*_states_lock); + + PyObject *list = PyList_New(0); + int size = _states->get_size(); + for (int si = 0; si < size; ++si) { + if (!_states->has_element(si)) { + continue; + } + const TransformState *state = _states->get_key(si); + if (state->get_cache_ref_count() == state->get_ref_count()) { + state->ref(); + PyObject *a = + DTool_CreatePyInstanceTyped((void *)state, Dtool_TransformState, + true, true, state->get_type_index()); + PyList_Append(list, a); + Py_DECREF(a); + } + } + return list; +} +#endif // HAVE_PYTHON + //////////////////////////////////////////////////////////////////// // Function: TransformState::init_states // Access: Public, Static @@ -2322,8 +2429,7 @@ do_calc_hash() { // Only bother to put the rest of the stuff in the hash if the // transform is not invalid or empty. - if ((_flags & (F_components_given | F_hpr_given | F_quat_given)) == - (F_components_given | F_hpr_given | F_quat_given)) { + if ((_flags & F_components_given) != 0) { // If the transform was specified componentwise, hash it // componentwise. _hash = _pos.add_hash(_hash); diff --git a/panda/src/pgraph/transformState.h b/panda/src/pgraph/transformState.h index 0d42bf74e2..8ec06ce141 100644 --- a/panda/src/pgraph/transformState.h +++ b/panda/src/pgraph/transformState.h @@ -189,6 +189,7 @@ PUBLISHED: INLINE int get_invert_composition_cache_size() const; INLINE const TransformState *get_invert_composition_cache_source(int n) const; INLINE const TransformState *get_invert_composition_cache_result(int n) const; + bool validate_composition_cache() const; #ifdef HAVE_PYTHON PyObject *get_composition_cache() const; PyObject *get_invert_composition_cache() const; @@ -207,6 +208,7 @@ PUBLISHED: static bool validate_states(); #ifdef HAVE_PYTHON static PyObject *get_states(); + static PyObject *get_unused_states(); #endif // HAVE_PYTHON