further bugfixes

This commit is contained in:
David Rose 2011-09-05 23:53:19 +00:00
parent 229cf67ae5
commit 3ed8632a68
22 changed files with 297 additions and 128 deletions

View File

@ -1640,4 +1640,5 @@ 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(); RenderState::garbage_collect();
return AsyncTask::DS_cont;
} }

View File

@ -96,8 +96,7 @@ get_vertex_data(Thread *current_thread) const {
INLINE bool Geom:: INLINE bool Geom::
is_empty() const { is_empty() const {
CDReader cdata(_cycler); CDReader cdata(_cycler);
return (cdata->_data.get_read_pointer()->get_num_rows() == 0 || return cdata->_primitives.empty();
cdata->_primitives.empty());
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////

View File

@ -1724,8 +1724,15 @@ check_usage_hint() const {
// already have modified the pointer on the object since we // already have modified the pointer on the object since we
// queried it. // queried it.
{ {
#ifdef DO_PIPELINING
unref_delete((CycleData *)_cdata);
#endif
Geom::CDWriter fresh_cdata(((Geom *)_object.p())->_cycler, Geom::CDWriter fresh_cdata(((Geom *)_object.p())->_cycler,
false, _current_thread); false, _current_thread);
((GeomPipelineReader *)this)->_cdata = fresh_cdata;
#ifdef DO_PIPELINING
_cdata->ref();
#endif
if (!fresh_cdata->_got_usage_hint) { if (!fresh_cdata->_got_usage_hint) {
// The cache is still stale. We have to do the work of // The cache is still stale. We have to do the work of
// freshening it. // freshening it.
@ -1733,14 +1740,9 @@ check_usage_hint() const {
nassertv(fresh_cdata->_got_usage_hint); nassertv(fresh_cdata->_got_usage_hint);
} }
// Save the new pointer, and then let the lock release itself. // When fresh_cdata goes out of scope, its write lock is
#ifdef DO_PIPELINING // released, and _cdata reverts to our usual convention of an
unref_delete((CycleData *)_cdata); // unlocked copy of the data.
#endif
((GeomPipelineReader *)this)->_cdata = fresh_cdata;
#ifdef DO_PIPELINING
_cdata->ref();
#endif
} }
} }

View File

@ -1983,8 +1983,16 @@ check_minmax() const {
// already have modified the pointer on the object since we // already have modified the pointer on the object since we
// queried it. // queried it.
{ {
#ifdef DO_PIPELINING
unref_delete((CycleData *)_cdata);
#endif
GeomPrimitive::CDWriter fresh_cdata(((GeomPrimitive *)_object.p())->_cycler, GeomPrimitive::CDWriter fresh_cdata(((GeomPrimitive *)_object.p())->_cycler,
false, _current_thread); false, _current_thread);
((GeomPrimitivePipelineReader *)this)->_cdata = fresh_cdata;
#ifdef DO_PIPELINING
_cdata->ref();
#endif
if (!fresh_cdata->_got_minmax) { if (!fresh_cdata->_got_minmax) {
// The cache is still stale. We have to do the work of // The cache is still stale. We have to do the work of
// freshening it. // freshening it.
@ -1992,14 +2000,9 @@ check_minmax() const {
nassertv(fresh_cdata->_got_minmax); nassertv(fresh_cdata->_got_minmax);
} }
// Save the new pointer, and then let the lock release itself. // When fresh_cdata goes out of scope, its write lock is
#ifdef DO_PIPELINING // released, and _cdata reverts to our usual convention of an
unref_delete((CycleData *)_cdata); // unlocked copy of the data.
#endif
((GeomPrimitivePipelineReader *)this)->_cdata = fresh_cdata;
#ifdef DO_PIPELINING
_cdata->ref();
#endif
} }
} }

View File

@ -1012,8 +1012,22 @@ reverse_normals() const {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
CPT(GeomVertexData) GeomVertexData:: CPT(GeomVertexData) GeomVertexData::
animate_vertices(bool force, Thread *current_thread) const { animate_vertices(bool force, Thread *current_thread) const {
CDLockedReader cdata(_cycler, current_thread); #ifdef DO_PIPELINING
{
// In the pipelining case, we take a simple short-route
// optimization: if the vdata isn't animated, we don't need to
// grab any mutex first.
CDReader cdata(_cycler, current_thread);
if (cdata->_format->get_animation().get_animation_type() != AT_panda) {
return this;
}
}
#endif // DO_PIPELINING
// Now that we've short-circuited the short route, we reasonably
// believe the vdata is animated. Grab the mutex and make sure it's
// still animated after we've acquired it.
CDLockedReader cdata(_cycler, current_thread);
if (cdata->_format->get_animation().get_animation_type() != AT_panda) { if (cdata->_format->get_animation().get_animation_type() != AT_panda) {
return this; return this;
} }

View File

@ -903,6 +903,7 @@ inc_add_pointer() {
if (_vertex_data != (GeomVertexData *)NULL) { if (_vertex_data != (GeomVertexData *)NULL) {
// If we have a whole GeomVertexData, we must set the length of // If we have a whole GeomVertexData, we must set the length of
// all its arrays at once. // all its arrays at once.
_handle = NULL;
GeomVertexDataPipelineWriter writer(_vertex_data, true, _current_thread); GeomVertexDataPipelineWriter writer(_vertex_data, true, _current_thread);
writer.check_array_writers(); writer.check_array_writers();
writer.set_num_rows(max(write_row + 1, writer.get_num_rows())); writer.set_num_rows(max(write_row + 1, writer.get_num_rows()));

View File

@ -264,6 +264,8 @@ garbage_collect() {
PStatTimer timer(_garbage_collect_pcollector); PStatTimer timer(_garbage_collect_pcollector);
int orig_size = _attribs->get_num_entries(); int orig_size = _attribs->get_num_entries();
nassertr(_attribs->validate(), 0);
// How many elements to process this pass? // How many elements to process this pass?
int size = _attribs->get_size(); int size = _attribs->get_size();
int num_this_pass = int(size * garbage_collect_states_rate); int num_this_pass = int(size * garbage_collect_states_rate);
@ -294,6 +296,7 @@ garbage_collect() {
si = (si + 1) % size; si = (si + 1) % size;
} while (si != stop_at_element); } while (si != stop_at_element);
_garbage_index = si; _garbage_index = si;
nassertr(_attribs->validate(), 0);
int new_size = _attribs->get_num_entries(); int new_size = _attribs->get_num_entries();
return orig_size - new_size; return orig_size - new_size;
@ -314,28 +317,41 @@ validate_attribs() {
return true; return true;
} }
if (!_attribs->validate()) {
pgraph_cat.error()
<< "RenderAttrib::_attribs cache is invalid!\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);
cerr << si << ": " << attrib << "\n";
attrib->get_hash();
attrib->write(cerr, 2);
}
return false;
}
int size = _attribs->get_size(); int size = _attribs->get_size();
int si = 0; int si = 0;
while (si < size && !_attribs->has_element(si)) { while (si < size && !_attribs->has_element(si)) {
++si; ++si;
} }
nassertr(si < size, false); nassertr(si < size, false);
nassertr(_attribs->get_key(si)->get_ref_count() > 0, false); nassertr(_attribs->get_key(si)->get_ref_count() >= 0, false);
int snext = si; int snext = si;
++snext;
while (snext < size && !_attribs->has_element(snext)) { while (snext < size && !_attribs->has_element(snext)) {
++snext; ++snext;
} }
while (snext < size) { while (snext < size) {
nassertr(_attribs->get_key(snext)->get_ref_count() >= 0, false);
const RenderAttrib *ssi = _attribs->get_key(si); const RenderAttrib *ssi = _attribs->get_key(si);
const RenderAttrib *ssnext = _attribs->get_key(snext); const RenderAttrib *ssnext = _attribs->get_key(snext);
int c = ssi->compare_to(*ssnext); int c = ssi->compare_to(*ssnext);
if (c >= 0) {
pgraph_cat.error()
<< "RenderAttribs out of order!\n";
ssi->write(pgraph_cat.error(false), 2);
ssnext->write(pgraph_cat.error(false), 2);
return false;
}
int ci = ssnext->compare_to(*ssi); int ci = ssnext->compare_to(*ssi);
if ((ci < 0) != (c > 0) || if ((ci < 0) != (c > 0) ||
(ci > 0) != (c < 0) || (ci > 0) != (c < 0) ||
@ -351,10 +367,10 @@ validate_attribs() {
return false; return false;
} }
si = snext; si = snext;
++snext;
while (snext < size && !_attribs->has_element(snext)) { while (snext < size && !_attribs->has_element(snext)) {
++snext; ++snext;
} }
nassertr(_attribs->get_key(si)->get_ref_count() > 0, false);
} }
return true; return true;

View File

@ -1193,6 +1193,7 @@ garbage_collect() {
si = (si + 1) % size; si = (si + 1) % size;
} while (si != stop_at_element); } while (si != stop_at_element);
_garbage_index = si; _garbage_index = si;
nassertr(_states->validate(), 0);
int new_size = _states->get_num_entries(); int new_size = _states->get_num_entries();
return orig_size - new_size + num_attribs; return orig_size - new_size + num_attribs;
@ -1363,28 +1364,29 @@ validate_states() {
return true; return true;
} }
if (!_states->validate()) {
pgraph_cat.error()
<< "RenderState::_states cache is invalid!\n";
return false;
}
int size = _states->get_size(); int size = _states->get_size();
int si = 0; int si = 0;
while (si < size && !_states->has_element(si)) { while (si < size && !_states->has_element(si)) {
++si; ++si;
} }
nassertr(si < size, false); nassertr(si < size, false);
nassertr(_states->get_key(si)->get_ref_count() > 0, false); nassertr(_states->get_key(si)->get_ref_count() >= 0, false);
int snext = si; int snext = si;
++snext;
while (snext < size && !_states->has_element(snext)) { while (snext < size && !_states->has_element(snext)) {
++snext; ++snext;
} }
while (snext < size) { while (snext < size) {
nassertr(_states->get_key(snext)->get_ref_count() >= 0, false);
const RenderState *ssi = _states->get_key(si); const RenderState *ssi = _states->get_key(si);
const RenderState *ssnext = _states->get_key(snext); const RenderState *ssnext = _states->get_key(snext);
int c = ssi->compare_to(*ssnext); int c = ssi->compare_to(*ssnext);
if (c >= 0) {
pgraph_cat.error()
<< "RenderStates out of order!\n";
ssi->write(pgraph_cat.error(false), 2);
ssnext->write(pgraph_cat.error(false), 2);
return false;
}
int ci = ssnext->compare_to(*ssi); int ci = ssnext->compare_to(*ssi);
if ((ci < 0) != (c > 0) || if ((ci < 0) != (c > 0) ||
(ci > 0) != (c < 0) || (ci > 0) != (c < 0) ||
@ -1400,10 +1402,10 @@ validate_states() {
return false; return false;
} }
si = snext; si = snext;
++snext;
while (snext < size && !_states->has_element(snext)) { while (snext < size && !_states->has_element(snext)) {
++snext; ++snext;
} }
nassertr(_states->get_key(si)->get_ref_count() > 0, false);
} }
return true; return true;

View File

@ -572,6 +572,8 @@ compare_to_impl(const RenderAttrib *other) const {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
size_t TextureAttrib:: size_t TextureAttrib::
get_hash_impl() const { get_hash_impl() const {
check_sorted();
size_t hash = 0; size_t hash = 0;
Stages::const_iterator si; Stages::const_iterator si;
for (si = _on_stages.begin(); si != _on_stages.end(); ++si) { for (si = _on_stages.begin(); si != _on_stages.end(); ++si) {

View File

@ -1271,6 +1271,7 @@ garbage_collect() {
si = (si + 1) % size; si = (si + 1) % size;
} while (si != stop_at_element); } while (si != stop_at_element);
_garbage_index = si; _garbage_index = si;
nassertr(_states->validate(), 0);
int new_size = _states->get_num_entries(); int new_size = _states->get_num_entries();
return orig_size - new_size; return orig_size - new_size;
@ -1419,28 +1420,29 @@ validate_states() {
return true; return true;
} }
if (!_states->validate()) {
pgraph_cat.error()
<< "TransformState::_states cache is invalid!\n";
return false;
}
int size = _states->get_size(); int size = _states->get_size();
int si = 0; int si = 0;
while (si < size && !_states->has_element(si)) { while (si < size && !_states->has_element(si)) {
++si; ++si;
} }
nassertr(si < size, false); nassertr(si < size, false);
nassertr(_states->get_key(si)->get_ref_count() > 0, false); nassertr(_states->get_key(si)->get_ref_count() >= 0, false);
int snext = si; int snext = si;
++snext;
while (snext < size && !_states->has_element(snext)) { while (snext < size && !_states->has_element(snext)) {
++snext; ++snext;
} }
while (snext < size) { while (snext < size) {
nassertr(_states->get_key(snext)->get_ref_count() >= 0, false);
const TransformState *ssi = _states->get_key(si); const TransformState *ssi = _states->get_key(si);
const TransformState *ssnext = _states->get_key(snext); const TransformState *ssnext = _states->get_key(snext);
int c = ssi->compare_to(*ssnext); int c = ssi->compare_to(*ssnext);
if (c >= 0) {
pgraph_cat.error()
<< "TransformStates out of order!\n";
ssi->write(pgraph_cat.error(false), 2);
ssnext->write(pgraph_cat.error(false), 2);
return false;
}
int ci = ssnext->compare_to(*ssi); int ci = ssnext->compare_to(*ssi);
if ((ci < 0) != (c > 0) || if ((ci < 0) != (c > 0) ||
(ci > 0) != (c < 0) || (ci > 0) != (c < 0) ||
@ -1456,10 +1458,10 @@ validate_states() {
return false; return false;
} }
si = snext; si = snext;
++snext;
while (snext < size && !_states->has_element(snext)) { while (snext < size && !_states->has_element(snext)) {
++snext; ++snext;
} }
nassertr(_states->get_key(si)->get_ref_count() > 0, false);
} }
return true; return true;
@ -2339,7 +2341,11 @@ do_calc_hash() {
// Otherwise, hash the matrix . . . // Otherwise, hash the matrix . . .
if (uniquify_matrix) { if (uniquify_matrix) {
// . . . but only if the user thinks that's worthwhile. // . . . but only if the user thinks that's worthwhile.
_hash = get_mat().add_hash(_hash); if ((_flags & F_mat_known) == 0) {
// Calculate the matrix without doubly-locking.
do_calc_mat();
}
_hash = _mat.add_hash(_hash);
} else { } else {
// Otherwise, hash the pointer only--any two different // Otherwise, hash the pointer only--any two different

View File

@ -41,7 +41,7 @@
// It exists as a syntactic convenience to access the // It exists as a syntactic convenience to access the
// data in the CycleData. It also allows the whole // data in the CycleData. It also allows the whole
// system to compile down to nothing if // system to compile down to nothing if
// SUPPORT_PIPELINING is not defined. // DO_PIPELINING is not defined.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
template<class CycleDataType> template<class CycleDataType>
class CycleDataLockedReader { class CycleDataLockedReader {

View File

@ -35,7 +35,7 @@
// It exists as a syntactic convenience to access the // It exists as a syntactic convenience to access the
// data in the CycleData. It also allows the whole // data in the CycleData. It also allows the whole
// system to compile down to nothing if // system to compile down to nothing if
// SUPPORT_PIPELINING is not defined. // DO_PIPELINING is not defined.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
template<class CycleDataType> template<class CycleDataType>
class CycleDataReader { class CycleDataReader {

View File

@ -32,7 +32,7 @@
// It exists as a syntactic convenience to access the // It exists as a syntactic convenience to access the
// data in the CycleData. It also allows the whole // data in the CycleData. It also allows the whole
// system to compile down to nothing if // system to compile down to nothing if
// SUPPORT_PIPELINING is not defined. // DO_PIPELINING is not defined.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
template<class CycleDataType> template<class CycleDataType>
class CycleDataWriter { class CycleDataWriter {

View File

@ -70,6 +70,11 @@ Pipeline::
void Pipeline:: void Pipeline::
cycle() { cycle() {
#ifdef THREADED_PIPELINE #ifdef THREADED_PIPELINE
if (pipeline_cat.is_debug()) {
pipeline_cat.debug()
<< "Beginning the pipeline cycle\n";
}
pvector< PT(CycleData) > saved_cdatas; pvector< PT(CycleData) > saved_cdatas;
saved_cdatas.reserve(_dirty_cyclers.size()); saved_cdatas.reserve(_dirty_cyclers.size());
{ {
@ -152,8 +157,14 @@ cycle() {
// And now it's safe to let the CycleData pointers in saved_cdatas // And now it's safe to let the CycleData pointers in saved_cdatas
// destruct, which may cause cascading deletes, and which will in // destruct, which may cause cascading deletes, and which will in
// turn case PipelineCyclers to remove themselves from (or add // turn cause PipelineCyclers to remove themselves from (or add
// themselves to) the _dirty_cyclers list. // themselves to) the _dirty_cyclers list.
saved_cdatas.clear();
if (pipeline_cat.is_debug()) {
pipeline_cat.debug()
<< "Finished the pipeline cycle\n";
}
#endif // THREADED_PIPELINE #endif // THREADED_PIPELINE
} }

View File

@ -40,11 +40,9 @@
// CycleDataWriter classes transparently handle this. // CycleDataWriter classes transparently handle this.
// //
// If pipelining support is not enabled at compile time // If pipelining support is not enabled at compile time
// (that is, SUPPORT_PIPELINING is not defined), this // (that is, DO_PIPELINING is not defined), this object
// object compiles to a minimum object that presents the // compiles to a minimum object that presents the same
// same interface but with minimal runtime overhead. // interface but with minimal runtime overhead.
// (Actually, this isn't true yet, but it will be one
// day.)
// //
// We define this as a struct instead of a class to // We define this as a struct instead of a class to
// guarantee byte placement within the object, so that // guarantee byte placement within the object, so that

View File

@ -72,7 +72,7 @@ read_unlocked(Thread *current_thread) const {
#ifdef _DEBUG #ifdef _DEBUG
nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL); nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL);
#endif #endif
return _data[pipeline_stage]; return _data[pipeline_stage]._cdata;
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -94,7 +94,7 @@ read(Thread *current_thread) const {
nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL); nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL);
#endif #endif
_lock.acquire(current_thread); _lock.acquire(current_thread);
return _data[pipeline_stage]; return _data[pipeline_stage]._cdata;
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -110,7 +110,7 @@ increment_read(const CycleData *pointer) const {
#ifdef _DEBUG #ifdef _DEBUG
int pipeline_stage = Thread::get_current_pipeline_stage(); int pipeline_stage = Thread::get_current_pipeline_stage();
nassertv(pipeline_stage >= 0 && pipeline_stage < _num_stages); nassertv(pipeline_stage >= 0 && pipeline_stage < _num_stages);
nassertv(_data[pipeline_stage] == pointer); nassertv(_data[pipeline_stage]._cdata == pointer);
#endif #endif
_lock.elevate_lock(); _lock.elevate_lock();
} }
@ -127,7 +127,7 @@ release_read(const CycleData *pointer) const {
#ifdef _DEBUG #ifdef _DEBUG
int pipeline_stage = Thread::get_current_pipeline_stage(); int pipeline_stage = Thread::get_current_pipeline_stage();
nassertv(pipeline_stage >= 0 && pipeline_stage < _num_stages); nassertv(pipeline_stage >= 0 && pipeline_stage < _num_stages);
nassertv(_data[pipeline_stage] == pointer); nassertv(_data[pipeline_stage]._cdata == pointer);
#endif #endif
_lock.release(); _lock.release();
} }
@ -205,7 +205,7 @@ elevate_read(const CycleData *pointer, Thread *current_thread) {
#ifdef _DEBUG #ifdef _DEBUG
int pipeline_stage = current_thread->get_pipeline_stage(); int pipeline_stage = current_thread->get_pipeline_stage();
nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL); nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL);
nassertr(_data[pipeline_stage] == pointer, NULL); nassertr(_data[pipeline_stage]._cdata == pointer, NULL);
#endif #endif
CycleData *new_pointer = write(current_thread); CycleData *new_pointer = write(current_thread);
_lock.release(); _lock.release();
@ -226,7 +226,7 @@ elevate_read_upstream(const CycleData *pointer, bool force_to_0, Thread *current
#ifdef _DEBUG #ifdef _DEBUG
int pipeline_stage = current_thread->get_pipeline_stage(); int pipeline_stage = current_thread->get_pipeline_stage();
nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL); nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL);
nassertr(_data[pipeline_stage] == pointer, NULL); nassertr(_data[pipeline_stage]._cdata == pointer, NULL);
#endif #endif
CycleData *new_pointer = write_upstream(force_to_0, current_thread); CycleData *new_pointer = write_upstream(force_to_0, current_thread);
_lock.release(); _lock.release();
@ -246,8 +246,9 @@ increment_write(CycleData *pointer) const {
#ifdef _DEBUG #ifdef _DEBUG
int pipeline_stage = Thread::get_current_pipeline_stage(); int pipeline_stage = Thread::get_current_pipeline_stage();
nassertv(pipeline_stage >= 0 && pipeline_stage < _num_stages); nassertv(pipeline_stage >= 0 && pipeline_stage < _num_stages);
nassertv(_data[pipeline_stage] == pointer); nassertv(_data[pipeline_stage]._cdata == pointer);
#endif #endif
++(_data[pipeline_stage]._writes_outstanding);
_lock.elevate_lock(); _lock.elevate_lock();
} }
@ -260,12 +261,8 @@ increment_write(CycleData *pointer) const {
INLINE void PipelineCyclerTrueImpl:: INLINE void PipelineCyclerTrueImpl::
release_write(CycleData *pointer) { release_write(CycleData *pointer) {
TAU_PROFILE("void PipelineCyclerTrueImpl::release_write(CycleData *)", " ", TAU_USER); TAU_PROFILE("void PipelineCyclerTrueImpl::release_write(CycleData *)", " ", TAU_USER);
#ifdef _DEBUG
int pipeline_stage = Thread::get_current_pipeline_stage(); int pipeline_stage = Thread::get_current_pipeline_stage();
return release_write_stage(pipeline_stage, pointer); return release_write_stage(pipeline_stage, pointer);
#else
_lock.release();
#endif
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -292,7 +289,7 @@ read_stage_unlocked(int pipeline_stage) const {
#ifdef _DEBUG #ifdef _DEBUG
nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL); nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL);
#endif #endif
return _data[pipeline_stage]; return _data[pipeline_stage]._cdata;
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -313,7 +310,7 @@ read_stage(int pipeline_stage, Thread *current_thread) const {
nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL); nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL);
#endif #endif
_lock.acquire(current_thread); _lock.acquire(current_thread);
return _data[pipeline_stage]; return _data[pipeline_stage]._cdata;
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -327,7 +324,7 @@ release_read_stage(int pipeline_stage, const CycleData *pointer) const {
TAU_PROFILE("void PipelineCyclerTrueImpl::release_read_stage(int, const CycleData *)", " ", TAU_USER); TAU_PROFILE("void PipelineCyclerTrueImpl::release_read_stage(int, const CycleData *)", " ", TAU_USER);
#ifdef _DEBUG #ifdef _DEBUG
nassertv(pipeline_stage >= 0 && pipeline_stage < _num_stages); nassertv(pipeline_stage >= 0 && pipeline_stage < _num_stages);
nassertv(_data[pipeline_stage] == pointer); nassertv(_data[pipeline_stage]._cdata == pointer);
#endif #endif
_lock.release(); _lock.release();
} }
@ -347,7 +344,7 @@ elevate_read_stage(int pipeline_stage, const CycleData *pointer,
TAU_PROFILE("CycleData *PipelineCyclerTrueImpl::elevate_read_stage(int, const CycleData *)", " ", TAU_USER); TAU_PROFILE("CycleData *PipelineCyclerTrueImpl::elevate_read_stage(int, const CycleData *)", " ", TAU_USER);
#ifdef _DEBUG #ifdef _DEBUG
nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL); nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL);
nassertr(_data[pipeline_stage] == pointer, NULL); nassertr(_data[pipeline_stage]._cdata == pointer, NULL);
#endif #endif
CycleData *new_pointer = write_stage(pipeline_stage, current_thread); CycleData *new_pointer = write_stage(pipeline_stage, current_thread);
_lock.release(); _lock.release();
@ -369,7 +366,7 @@ elevate_read_stage_upstream(int pipeline_stage, const CycleData *pointer,
TAU_PROFILE("CycleData *PipelineCyclerTrueImpl::elevate_read_stage(int, const CycleData *)", " ", TAU_USER); TAU_PROFILE("CycleData *PipelineCyclerTrueImpl::elevate_read_stage(int, const CycleData *)", " ", TAU_USER);
#ifdef _DEBUG #ifdef _DEBUG
nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL); nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL);
nassertr(_data[pipeline_stage] == pointer, NULL); nassertr(_data[pipeline_stage]._cdata == pointer, NULL);
#endif #endif
CycleData *new_pointer = CycleData *new_pointer =
write_stage_upstream(pipeline_stage, force_to_0, current_thread); write_stage_upstream(pipeline_stage, force_to_0, current_thread);
@ -388,8 +385,10 @@ release_write_stage(int pipeline_stage, CycleData *pointer) {
TAU_PROFILE("void PipelineCyclerTrueImpl::release_write_stage(int, const CycleData *)", " ", TAU_USER); TAU_PROFILE("void PipelineCyclerTrueImpl::release_write_stage(int, const CycleData *)", " ", TAU_USER);
#ifdef _DEBUG #ifdef _DEBUG
nassertv(pipeline_stage >= 0 && pipeline_stage < _num_stages); nassertv(pipeline_stage >= 0 && pipeline_stage < _num_stages);
nassertv(_data[pipeline_stage] == pointer); nassertv(_data[pipeline_stage]._cdata == pointer);
nassertv(_data[pipeline_stage]._writes_outstanding > 0);
#endif #endif
--(_data[pipeline_stage]._writes_outstanding);
_lock.release(); _lock.release();
} }
@ -401,7 +400,7 @@ release_write_stage(int pipeline_stage, CycleData *pointer) {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
INLINE TypeHandle PipelineCyclerTrueImpl:: INLINE TypeHandle PipelineCyclerTrueImpl::
get_parent_type() const { get_parent_type() const {
return _data[0]->get_parent_type(); return _data[0]._cdata->get_parent_type();
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -419,7 +418,7 @@ cheat() const {
TAU_PROFILE("CycleData *PipelineCyclerTrueImpl::cheat()", " ", TAU_USER); TAU_PROFILE("CycleData *PipelineCyclerTrueImpl::cheat()", " ", TAU_USER);
int pipeline_stage = Thread::get_current_pipeline_stage(); int pipeline_stage = Thread::get_current_pipeline_stage();
nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL); nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL);
return _data[pipeline_stage]; return _data[pipeline_stage]._cdata;
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -458,12 +457,13 @@ get_write_count() const {
INLINE PT(CycleData) PipelineCyclerTrueImpl:: INLINE PT(CycleData) PipelineCyclerTrueImpl::
cycle_2() { cycle_2() {
TAU_PROFILE("PT(CycleData) PipelineCyclerTrueImpl::cycle_2()", " ", TAU_USER); TAU_PROFILE("PT(CycleData) PipelineCyclerTrueImpl::cycle_2()", " ", TAU_USER);
PT(CycleData) last_val = _data[1].p(); PT(CycleData) last_val = _data[1]._cdata.p();
nassertr(_lock.debug_is_locked(), last_val); nassertr(_lock.debug_is_locked(), last_val);
nassertr(_dirty, last_val); nassertr(_dirty, last_val);
nassertr(_num_stages == 2, last_val); nassertr(_num_stages == 2, last_val);
_data[1] = _data[0]; nassertr(_data[1]._writes_outstanding == 0, last_val);
_data[1]._cdata = _data[0]._cdata;
// No longer dirty. // No longer dirty.
_dirty = false; _dirty = false;
@ -482,15 +482,17 @@ cycle_2() {
INLINE PT(CycleData) PipelineCyclerTrueImpl:: INLINE PT(CycleData) PipelineCyclerTrueImpl::
cycle_3() { cycle_3() {
TAU_PROFILE("PT(CycleData) PipelineCyclerTrueImpl::cycle_3()", " ", TAU_USER); TAU_PROFILE("PT(CycleData) PipelineCyclerTrueImpl::cycle_3()", " ", TAU_USER);
PT(CycleData) last_val = _data[2].p(); PT(CycleData) last_val = _data[2]._cdata.p();
nassertr(_lock.debug_is_locked(), last_val); nassertr(_lock.debug_is_locked(), last_val);
nassertr(_dirty, last_val); nassertr(_dirty, last_val);
nassertr(_num_stages == 3, last_val); nassertr(_num_stages == 3, last_val);
_data[2] = _data[1]; nassertr(_data[2]._writes_outstanding == 0, last_val);
_data[1] = _data[0]; nassertr(_data[1]._writes_outstanding == 0, last_val);
_data[2]._cdata = _data[1]._cdata;
_data[1]._cdata = _data[0]._cdata;
if (_data[2] == _data[1]) { if (_data[2]._cdata == _data[1]._cdata) {
// No longer dirty. // No longer dirty.
_dirty = false; _dirty = false;
} }
@ -509,3 +511,46 @@ CyclerMutex(PipelineCyclerTrueImpl *cycler) {
_cycler = cycler; _cycler = cycler;
#endif #endif
} }
////////////////////////////////////////////////////////////////////
// Function: PipelineCyclerTrueImpl::CyclerMutex::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE PipelineCyclerTrueImpl::CycleDataNode::
CycleDataNode() :
_writes_outstanding(0)
{
}
////////////////////////////////////////////////////////////////////
// Function: PipelineCyclerTrueImpl::CyclerMutex::Copy Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE PipelineCyclerTrueImpl::CycleDataNode::
CycleDataNode(const PipelineCyclerTrueImpl::CycleDataNode &copy) :
_cdata(copy._cdata),
_writes_outstanding(0)
{
}
////////////////////////////////////////////////////////////////////
// Function: PipelineCyclerTrueImpl::CyclerMutex::Destructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE PipelineCyclerTrueImpl::CycleDataNode::
~CycleDataNode() {
nassertv(_writes_outstanding == 0);
}
////////////////////////////////////////////////////////////////////
// Function: PipelineCyclerTrueImpl::CyclerMutex::Copy Assignment
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE void PipelineCyclerTrueImpl::CycleDataNode::
operator = (const PipelineCyclerTrueImpl::CycleDataNode &copy) {
_cdata = copy._cdata;
}

View File

@ -35,9 +35,9 @@ PipelineCyclerTrueImpl(CycleData *initial_data, Pipeline *pipeline) :
} }
_num_stages = _pipeline->get_num_stages(); _num_stages = _pipeline->get_num_stages();
_data = new NPT(CycleData)[_num_stages]; _data = new CycleDataNode[_num_stages];
for (int i = 0; i < _num_stages; ++i) { for (int i = 0; i < _num_stages; ++i) {
_data[i] = initial_data; _data[i]._cdata = initial_data;
} }
_pipeline->add_cycler(this); _pipeline->add_cycler(this);
@ -59,7 +59,7 @@ PipelineCyclerTrueImpl(const PipelineCyclerTrueImpl &copy) :
_num_stages = _pipeline->get_num_stages(); _num_stages = _pipeline->get_num_stages();
nassertv(_num_stages == copy._num_stages); nassertv(_num_stages == copy._num_stages);
_data = new NPT(CycleData)[_num_stages]; _data = new CycleDataNode[_num_stages];
// It's no longer critically important that we preserve pointerwise // It's no longer critically important that we preserve pointerwise
// equivalence between different stages in the copy, but it doesn't // equivalence between different stages in the copy, but it doesn't
@ -69,11 +69,11 @@ PipelineCyclerTrueImpl(const PipelineCyclerTrueImpl &copy) :
Pointers pointers; Pointers pointers;
for (int i = 0; i < _num_stages; ++i) { for (int i = 0; i < _num_stages; ++i) {
PT(CycleData) &new_pt = pointers[copy._data[i]]; PT(CycleData) &new_pt = pointers[copy._data[i]._cdata];
if (new_pt == NULL) { if (new_pt == NULL) {
new_pt = copy._data[i]->make_copy(); new_pt = copy._data[i]._cdata->make_copy();
} }
_data[i] = new_pt.p(); _data[i]._cdata = new_pt.p();
} }
_pipeline->add_cycler(this); _pipeline->add_cycler(this);
@ -97,11 +97,11 @@ operator = (const PipelineCyclerTrueImpl &copy) {
Pointers pointers; Pointers pointers;
for (int i = 0; i < _num_stages; ++i) { for (int i = 0; i < _num_stages; ++i) {
PT(CycleData) &new_pt = pointers[copy._data[i]]; PT(CycleData) &new_pt = pointers[copy._data[i]._cdata];
if (new_pt == NULL) { if (new_pt == NULL) {
new_pt = copy._data[i]->make_copy(); new_pt = copy._data[i]._cdata->make_copy();
} }
_data[i] = new_pt.p(); _data[i]._cdata = new_pt.p();
} }
if (copy._dirty && !_dirty) { if (copy._dirty && !_dirty) {
@ -146,15 +146,26 @@ write_stage(int pipeline_stage, Thread *current_thread) {
} }
#endif // NDEBUG #endif // NDEBUG
CycleData *old_data = _data[pipeline_stage]; CycleData *old_data = _data[pipeline_stage]._cdata;
// We only perform copy-on-write if this is the first CycleData
// requested for write mode from this thread. (We will never have
// outstanding writes for multiple threads, because we hold the
// CyclerMutex during the entire lifetime of write() .. release()).
if (_data[pipeline_stage]._writes_outstanding == 0) {
// Only the node reference count is considered an important count // Only the node reference count is considered an important count
// for copy-on-write purposes. A standard reference of other than 1 // for copy-on-write purposes. A standard reference of other than 1
// just means that some code (other that the PipelineCycler) has a // just means that some code (other that the PipelineCycler) has a
// pointer, which is safe to modify. // pointer, which is safe to modify.
if (old_data->get_node_ref_count() != 1) { if (old_data->get_node_ref_count() != 1) {
// Copy-on-write. // Copy-on-write.
_data[pipeline_stage] = old_data->make_copy(); _data[pipeline_stage]._cdata = old_data->make_copy();
if (pipeline_cat.is_debug()) {
pipeline_cat.debug()
<< "Copy-on-write a: " << old_data << " becomes "
<< _data[pipeline_stage]._cdata << "\n";
//nassertr(false, NULL);
}
// Now we have differences between some of the data pointers, so // Now we have differences between some of the data pointers, so
// we're "dirty". Mark it so. // we're "dirty". Mark it so.
@ -162,8 +173,10 @@ write_stage(int pipeline_stage, Thread *current_thread) {
_pipeline->add_dirty_cycler(this); _pipeline->add_dirty_cycler(this);
} }
} }
}
return _data[pipeline_stage]; ++(_data[pipeline_stage]._writes_outstanding);
return _data[pipeline_stage]._cdata;
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -184,30 +197,42 @@ write_stage_upstream(int pipeline_stage, bool force_to_0, Thread *current_thread
} }
#endif // NDEBUG #endif // NDEBUG
CycleData *old_data = _data[pipeline_stage]; CycleData *old_data = _data[pipeline_stage]._cdata;
if (old_data->get_ref_count() != 1 || force_to_0) { if (old_data->get_ref_count() != 1 || force_to_0) {
// Count the number of references before the current stage, and // Count the number of references before the current stage, and
// the number of references remaining other than those. // the number of references remaining other than those.
int external_count = old_data->get_ref_count() - 1; int external_count = old_data->get_ref_count() - 1;
int k = pipeline_stage - 1; int k = pipeline_stage - 1;
while (k >= 0 && _data[k] == old_data) { while (k >= 0 && _data[k]._cdata == old_data) {
--k; --k;
--external_count; --external_count;
} }
if (external_count > 0) { // We only perform copy-on-write if this is the first CycleData
// requested for write mode from this thread. (We will never have
// outstanding writes for multiple threads, because we hold the
// CyclerMutex during the entire lifetime of write()
// .. release()).
if (external_count > 0 && _data[pipeline_stage]._writes_outstanding == 0) {
// There are references other than the ones before this stage in // There are references other than the ones before this stage in
// the pipeline; perform a copy-on-write. // the pipeline; perform a copy-on-write.
PT(CycleData) new_data = old_data->make_copy(); PT(CycleData) new_data = old_data->make_copy();
if (pipeline_cat.is_debug()) {
pipeline_cat.debug()
<< "Copy-on-write b: " << old_data << " becomes "
<< new_data << "\n";
//nassertr(false, NULL);
}
k = pipeline_stage - 1; k = pipeline_stage - 1;
while (k >= 0 && (_data[k] == old_data || force_to_0)) { while (k >= 0 && (_data[k]._cdata == old_data || force_to_0)) {
_data[k] = new_data.p(); nassertr(_data[k]._writes_outstanding == 0, NULL);
_data[k]._cdata = new_data.p();
--k; --k;
} }
_data[pipeline_stage] = new_data; _data[pipeline_stage]._cdata = new_data;
if (k >= 0 || pipeline_stage + 1 < _num_stages) { if (k >= 0 || pipeline_stage + 1 < _num_stages) {
// Now we have differences between some of the data pointers, // Now we have differences between some of the data pointers,
@ -222,13 +247,15 @@ write_stage_upstream(int pipeline_stage, bool force_to_0, Thread *current_thread
// but the current pointer doesn't go all the way back. Make it // but the current pointer doesn't go all the way back. Make it
// do so. // do so.
while (k >= 0) { while (k >= 0) {
_data[k] = old_data; nassertr(_data[k]._writes_outstanding == 0, NULL);
_data[k]._cdata = old_data;
--k; --k;
} }
} }
} }
return _data[pipeline_stage]; ++(_data[pipeline_stage]._writes_outstanding);
return _data[pipeline_stage]._cdata;
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -251,17 +278,18 @@ write_stage_upstream(int pipeline_stage, bool force_to_0, Thread *current_thread
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
PT(CycleData) PipelineCyclerTrueImpl:: PT(CycleData) PipelineCyclerTrueImpl::
cycle() { cycle() {
PT(CycleData) last_val = _data[_num_stages - 1].p(); PT(CycleData) last_val = _data[_num_stages - 1]._cdata.p();
nassertr(_lock.debug_is_locked(), last_val); nassertr(_lock.debug_is_locked(), last_val);
nassertr(_dirty, last_val); nassertr(_dirty, last_val);
int i; int i;
for (i = _num_stages - 1; i > 0; --i) { for (i = _num_stages - 1; i > 0; --i) {
_data[i] = _data[i - 1]; nassertr(_data[i]._writes_outstanding == 0, last_val);
_data[i]._cdata = _data[i - 1]._cdata;
} }
for (i = 1; i < _num_stages; ++i) { for (i = 1; i < _num_stages; ++i) {
if (_data[i] != _data[i - 1]) { if (_data[i]._cdata != _data[i - 1]._cdata) {
// Still dirty. // Still dirty.
return last_val; return last_val;
} }
@ -286,7 +314,8 @@ set_num_stages(int num_stages) {
// Don't bother to reallocate the array smaller; we just won't use // Don't bother to reallocate the array smaller; we just won't use
// the rest of the array. // the rest of the array.
for (int i = _num_stages; i < num_stages; ++i) { for (int i = _num_stages; i < num_stages; ++i) {
_data[i].clear(); nassertv(_data[i]._writes_outstanding == 0);
_data[i]._cdata.clear();
} }
_num_stages = num_stages; _num_stages = num_stages;
@ -294,13 +323,14 @@ set_num_stages(int num_stages) {
} else { } else {
// To increase the array, we must reallocate it larger. // To increase the array, we must reallocate it larger.
NPT(CycleData) *new_data = new NPT(CycleData)[num_stages]; CycleDataNode *new_data = new CycleDataNode[num_stages];
int i; int i;
for (i = 0; i < _num_stages; ++i) { for (i = 0; i < _num_stages; ++i) {
new_data[i] = _data[i]; nassertv(_data[i]._writes_outstanding == 0);
new_data[i]._cdata = _data[i]._cdata;
} }
for (i = _num_stages; i < num_stages; ++i) { for (i = _num_stages; i < num_stages; ++i) {
new_data[i] = _data[_num_stages - 1]; new_data[i]._cdata = _data[_num_stages - 1]._cdata;
} }
delete[] _data; delete[] _data;

View File

@ -113,8 +113,19 @@ private:
private: private:
Pipeline *_pipeline; Pipeline *_pipeline;
// An array of NPT(CycleData) objects. // An array of PT(CycleData) objects representing the different
NPT(CycleData) *_data; // copies of the cycled data, one for each stage.
class CycleDataNode : public MemoryBase {
public:
INLINE CycleDataNode();
INLINE CycleDataNode(const CycleDataNode &copy);
INLINE ~CycleDataNode();
INLINE void operator = (const CycleDataNode &copy);
NPT(CycleData) _cdata;
int _writes_outstanding;
};
CycleDataNode *_data;
int _num_stages; int _num_stages;
bool _dirty; bool _dirty;

View File

@ -123,20 +123,32 @@ store(const Key &key, const Value &data) {
size_t index = get_hash(key); size_t index = get_hash(key);
store_new_element(index, key, data); store_new_element(index, key, data);
++_num_entries; ++_num_entries;
#ifdef _DEBUG
nassertr(validate(), index);
#endif
return index; return index;
} }
size_t index = get_hash(key); size_t index = get_hash(key);
if (!has_element(index)) { if (!has_element(index)) {
// This element is not already in the map; add it.
if (consider_expand_table()) { if (consider_expand_table()) {
return store(key, data); return store(key, data);
} }
store_new_element(index, key, data); store_new_element(index, key, data);
++_num_entries; ++_num_entries;
#ifdef _DEBUG
nassertr(validate(), index);
#endif
return index; return index;
} }
if (is_element(index, key)) { if (is_element(index, key)) {
// This element is already in the map; replace the data at that
// key.
_table[index]._data = data; _table[index]._data = data;
#ifdef _DEBUG
nassertr(validate(), index);
#endif
return index; return index;
} }
@ -151,10 +163,16 @@ store(const Key &key, const Value &data) {
} }
store_new_element(i, key, data); store_new_element(i, key, data);
++_num_entries; ++_num_entries;
#ifdef _DEBUG
nassertr(validate(), i);
#endif
return i; return i;
} }
if (is_element(i, key)) { if (is_element(i, key)) {
_table[i]._data = data; _table[i]._data = data;
#ifdef _DEBUG
nassertr(validate(), i);
#endif
return i; return i;
} }
i = (i + 1) & (_table_size - 1); i = (i + 1) & (_table_size - 1);
@ -363,6 +381,10 @@ remove_element(int n) {
// conflicts. // conflicts.
i = (i + 1) & (_table_size - 1); i = (i + 1) & (_table_size - 1);
} }
#ifdef _DEBUG
nassertv(validate());
#endif
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -605,13 +627,19 @@ expand_table() {
_deleted_chain = memory_hook->get_deleted_chain(alloc_size); _deleted_chain = memory_hook->get_deleted_chain(alloc_size);
_table = (TableEntry *)_deleted_chain->allocate(alloc_size, TypeHandle::none()); _table = (TableEntry *)_deleted_chain->allocate(alloc_size, TypeHandle::none());
memset(get_exists_array(), 0, _table_size); memset(get_exists_array(), 0, _table_size);
nassertv(_num_entries == 0);
// Now copy the entries from the old table into the new table. // Now copy the entries from the old table into the new table.
int num_added = 0;
for (size_t i = 0; i < old_map._table_size; ++i) { for (size_t i = 0; i < old_map._table_size; ++i) {
if (old_map.has_element(i)) { if (old_map.has_element(i)) {
store(old_map._table[i]._key, old_map._table[i]._data); store(old_map._table[i]._key, old_map._table[i]._data);
++num_added;
} }
} }
nassertv(old_map._num_entries == _num_entries); nassertv(validate());
nassertv(old_map.validate());
nassertv(_num_entries == old_map._num_entries);
} }

View File

@ -215,7 +215,7 @@ load_gridded_models(WindowFramework *window,
Loader loader; Loader loader;
LoaderOptions options; LoaderOptions options;
options.set_flags(options.get_flags() | LoaderOptions::LF_no_ram_cache); // options.set_flags(options.get_flags() | LoaderOptions::LF_no_ram_cache);
// First, load up each model from disk once, and store them all // First, load up each model from disk once, and store them all
// separate from the scene graph. Also count up the total number of // separate from the scene graph. Also count up the total number of