use dual linked lists instead of a linked list and a pset

This commit is contained in:
David Rose 2011-09-07 06:22:04 +00:00
parent 48df7995e5
commit ad0a1ab64f
5 changed files with 148 additions and 42 deletions

View File

@ -74,7 +74,7 @@ get_num_cyclers() const {
INLINE int Pipeline:: INLINE int Pipeline::
get_num_dirty_cyclers() const { get_num_dirty_cyclers() const {
ReMutexHolder holder(_lock); ReMutexHolder holder(_lock);
return _dirty_cyclers.size(); return _num_dirty_cyclers;
} }
#endif // THREADED_PIPELINE #endif // THREADED_PIPELINE

View File

@ -33,12 +33,27 @@ Pipeline(const string &name, int num_stages) :
#endif #endif
{ {
#ifdef THREADED_PIPELINE #ifdef THREADED_PIPELINE
// Set up the linked list of cyclers to be a circular list that
// begins with this object.
_prev = this;
_next = this;
// We maintain all of the cyclers in the world on one of two linked
// lists. Cyclers that are "clean", which is to say, they have the
// same value across all pipeline stages, are stored on the _clean
// list. Cyclers that are "dirty", which have different values
// across some pipeline stages, are stored instead on the _dirty
// list. Cyclers can move themselves from clean to dirty by calling
// add_dirty_cycler(), and cyclers get moved from dirty to clean
// during cycle().
// To visit each cycler once requires traversing both lists.
_clean.make_head();
_dirty.make_head();
// We also store the total count of all cyclers, clean and dirty, in
// _num_cyclers; and the count of only dirty cyclers in
// _num_dirty_cyclers.
_num_cyclers = 0; _num_cyclers = 0;
_num_dirty_cyclers = 0;
// This flag is true only during the call to cycle().
_cycling = false; _cycling = false;
#endif // THREADED_PIPELINE #endif // THREADED_PIPELINE
@ -55,9 +70,9 @@ Pipeline::
~Pipeline() { ~Pipeline() {
#ifdef THREADED_PIPELINE #ifdef THREADED_PIPELINE
nassertv(_num_cyclers == 0); nassertv(_num_cyclers == 0);
nassertv(_prev == this && _next == this); nassertv(_num_dirty_cyclers == 0);
_prev = NULL; _clean.clear_head();
_next = NULL; _dirty.clear_head();
nassertv(!_cycling); nassertv(!_cycling);
#endif // THREADED_PIPELINE #endif // THREADED_PIPELINE
} }
@ -76,25 +91,29 @@ cycle() {
} }
pvector< PT(CycleData) > saved_cdatas; pvector< PT(CycleData) > saved_cdatas;
saved_cdatas.reserve(_dirty_cyclers.size()); saved_cdatas.reserve(_num_dirty_cyclers);
{ {
ReMutexHolder holder(_lock); ReMutexHolder holder(_lock);
if (_num_stages == 1) { if (_num_stages == 1) {
// No need to cycle if there's only one stage. // No need to cycle if there's only one stage.
nassertv(_dirty_cyclers.empty()); nassertv(_dirty._next == &_dirty);
return; return;
} }
nassertv(!_cycling); nassertv(!_cycling);
_cycling = true; _cycling = true;
Cyclers next_dirty_cyclers; // Move the dirty list to prev_dirty, for processing.
PipelineCyclerLinks prev_dirty;
prev_dirty.make_head();
prev_dirty.take_list(_dirty);
_num_dirty_cyclers = 0;
Cyclers::iterator ci;
switch (_num_stages) { switch (_num_stages) {
case 2: case 2:
for (ci = _dirty_cyclers.begin(); ci != _dirty_cyclers.end(); ++ci) { while (prev_dirty._next != &prev_dirty) {
PipelineCyclerTrueImpl *cycler = (*ci); PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)prev_dirty._next;
cycler->remove_from_list();
ReMutexHolder holder2(cycler->_lock); ReMutexHolder holder2(cycler->_lock);
// We save the result of cycle(), so that we can defer the // We save the result of cycle(), so that we can defer the
@ -103,11 +122,13 @@ cycle() {
saved_cdatas.push_back(cycler->cycle_2()); saved_cdatas.push_back(cycler->cycle_2());
if (cycler->_dirty) { if (cycler->_dirty) {
// The cycler is still dirty after cycling. Preserve it in the // The cycler is still dirty after cycling. Keep it on the
// set for next time. // dirty list for next time.
bool inserted = next_dirty_cyclers.insert(cycler).second; cycler->insert_before(&_dirty);
nassertv(inserted); ++_num_dirty_cyclers;
} else { } else {
// The cycler is now clean. Add it back to the clean list.
cycler->insert_before(&_clean);
#ifdef DEBUG_THREADS #ifdef DEBUG_THREADS
inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1); inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1);
#endif #endif
@ -116,15 +137,18 @@ cycle() {
break; break;
case 3: case 3:
for (ci = _dirty_cyclers.begin(); ci != _dirty_cyclers.end(); ++ci) { while (prev_dirty._next != &prev_dirty) {
PipelineCyclerTrueImpl *cycler = (*ci); PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)prev_dirty._next;
cycler->remove_from_list();
ReMutexHolder holder2(cycler->_lock); ReMutexHolder holder2(cycler->_lock);
saved_cdatas.push_back(cycler->cycle_3()); saved_cdatas.push_back(cycler->cycle_3());
if (cycler->_dirty) { if (cycler->_dirty) {
bool inserted = next_dirty_cyclers.insert(cycler).second; cycler->insert_before(&_dirty);
nassertv(inserted); ++_num_dirty_cyclers;
} else { } else {
cycler->insert_before(&_clean);
#ifdef DEBUG_THREADS #ifdef DEBUG_THREADS
inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1); inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1);
#endif #endif
@ -133,15 +157,18 @@ cycle() {
break; break;
default: default:
for (ci = _dirty_cyclers.begin(); ci != _dirty_cyclers.end(); ++ci) { while (prev_dirty._next != &prev_dirty) {
PipelineCyclerTrueImpl *cycler = (*ci); PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)prev_dirty._next;
cycler->remove_from_list();
ReMutexHolder holder2(cycler->_lock); ReMutexHolder holder2(cycler->_lock);
saved_cdatas.push_back(cycler->cycle()); saved_cdatas.push_back(cycler->cycle());
if (cycler->_dirty) { if (cycler->_dirty) {
bool inserted = next_dirty_cyclers.insert(cycler).second; cycler->insert_before(&_dirty);
nassertv(inserted); ++_num_dirty_cyclers;
} else { } else {
cycler->insert_before(&_clean);
#ifdef DEBUG_THREADS #ifdef DEBUG_THREADS
inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1); inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1);
#endif #endif
@ -150,15 +177,15 @@ cycle() {
break; break;
} }
// Finally, we're ready for the next frame. // Now we're ready for the next frame.
_dirty_cyclers.swap(next_dirty_cyclers); prev_dirty.clear_head();
_cycling = false; _cycling = false;
} }
// 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 cause 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 list.
saved_cdatas.clear(); saved_cdatas.clear();
if (pipeline_cat.is_debug()) { if (pipeline_cat.is_debug()) {
@ -185,21 +212,34 @@ set_num_stages(int num_stages) {
// We need to lock every PipelineCycler object attached to this // We need to lock every PipelineCycler object attached to this
// pipeline before we can adjust the number of stages. // pipeline before we can adjust the number of stages.
PipelineCyclerLinks *links; PipelineCyclerLinks *links;
for (links = this->_next; links != this; links = links->_next) { for (links = _clean._next; links != &_clean; links = links->_next) {
PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)links;
cycler->_lock.acquire();
}
for (links = _dirty._next; links != &_dirty; links = links->_next) {
PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)links; PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)links;
cycler->_lock.acquire(); cycler->_lock.acquire();
} }
_num_stages = num_stages; _num_stages = num_stages;
for (links = this->_next; links != this; links = links->_next) { for (links = _clean._next; links != &_clean; links = links->_next) {
PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)links;
cycler->set_num_stages(num_stages);
}
for (links = _dirty._next; links != &_dirty; links = links->_next) {
PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)links; PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)links;
cycler->set_num_stages(num_stages); cycler->set_num_stages(num_stages);
} }
// Now release them all. // Now release them all.
int count = 0; int count = 0;
for (links = this->_next; links != this; links = links->_next) { for (links = _clean._next; links != &_clean; links = links->_next) {
PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)links;
cycler->_lock.release();
++count;
}
for (links = _dirty._next; links != &_dirty; links = links->_next) {
PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)links; PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)links;
cycler->_lock.release(); cycler->_lock.release();
++count; ++count;
@ -230,8 +270,9 @@ add_cycler(PipelineCyclerTrueImpl *cycler) {
ReMutexHolder holder(_lock); ReMutexHolder holder(_lock);
nassertv(!cycler->_dirty); nassertv(!cycler->_dirty);
nassertv(!_cycling); nassertv(!_cycling);
cycler->insert_before(&_clean);
++_num_cyclers; ++_num_cyclers;
cycler->insert_before(this);
#ifdef DEBUG_THREADS #ifdef DEBUG_THREADS
inc_cycler_type(_all_cycler_types, cycler->get_parent_type(), 1); inc_cycler_type(_all_cycler_types, cycler->get_parent_type(), 1);
@ -257,10 +298,12 @@ add_dirty_cycler(PipelineCyclerTrueImpl *cycler) {
nassertv(_num_stages != 1); nassertv(_num_stages != 1);
nassertv(!_cycling); nassertv(!_cycling);
nassertv(!cycler->_dirty); nassertv(!cycler->_dirty);
cycler->_dirty = true;
bool inserted = _dirty_cyclers.insert(cycler).second; // Remove it from the "clean" list and add it to the "dirty" list.
nassertv(inserted); cycler->remove_from_list();
cycler->insert_before(&_dirty);
cycler->_dirty = true;
++_num_dirty_cyclers;
#ifdef DEBUG_THREADS #ifdef DEBUG_THREADS
inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), 1); inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), 1);
@ -292,9 +335,7 @@ remove_cycler(PipelineCyclerTrueImpl *cycler) {
if (cycler->_dirty) { if (cycler->_dirty) {
cycler->_dirty = false; cycler->_dirty = false;
Cyclers::iterator ci = _dirty_cyclers.find(cycler); --_num_dirty_cyclers;
nassertv(ci != _dirty_cyclers.end());
_dirty_cyclers.erase(ci);
#ifdef DEBUG_THREADS #ifdef DEBUG_THREADS
inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1); inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1);
#endif #endif

View File

@ -38,7 +38,7 @@ struct PipelineCyclerTrueImpl;
// pipeline. Other specialty pipelines may be created // pipeline. Other specialty pipelines may be created
// as needed. // as needed.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
class EXPCL_PANDA_PIPELINE Pipeline : public PipelineCyclerLinks, public Namable { class EXPCL_PANDA_PIPELINE Pipeline : public Namable {
public: public:
Pipeline(const string &name, int num_stages); Pipeline(const string &name, int num_stages);
~Pipeline(); ~Pipeline();
@ -74,9 +74,11 @@ private:
static Pipeline *_render_pipeline; static Pipeline *_render_pipeline;
#ifdef THREADED_PIPELINE #ifdef THREADED_PIPELINE
PipelineCyclerLinks _clean;
PipelineCyclerLinks _dirty;
int _num_cyclers; int _num_cyclers;
typedef pset<PipelineCyclerTrueImpl *> Cyclers; int _num_dirty_cyclers;
Cyclers _dirty_cyclers;
#ifdef DEBUG_THREADS #ifdef DEBUG_THREADS
typedef pmap<TypeHandle, int> TypeCount; typedef pmap<TypeHandle, int> TypeCount;

View File

@ -40,6 +40,38 @@ INLINE PipelineCyclerLinks::
} }
#endif // THREADED_PIPELINE #endif // THREADED_PIPELINE
#ifdef THREADED_PIPELINE
////////////////////////////////////////////////////////////////////
// Function: PipelineCyclerLinks::make_head
// Access: Protected
// Description: When called on an empty object, sets it up to be the
// head of a linked list.
////////////////////////////////////////////////////////////////////
INLINE void PipelineCyclerLinks::
make_head() {
nassertv(_next == NULL && _prev == NULL);
_next = this;
_prev = this;
}
#endif // THREADED_PIPELINE
#ifdef THREADED_PIPELINE
////////////////////////////////////////////////////////////////////
// Function: PipelineCyclerLinks::clear_head
// Access: Protected
// Description: When called on the head of an empty linked list,
// resets it to an empty object, for safe destruction.
////////////////////////////////////////////////////////////////////
INLINE void PipelineCyclerLinks::
clear_head() {
nassertv(_next == this && _prev == this);
#ifndef NDEBUG
_next = NULL;
_prev = NULL;
#endif
}
#endif // THREADED_PIPELINE
#ifdef THREADED_PIPELINE #ifdef THREADED_PIPELINE
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: PipelineCyclerLinks::remove_from_list // Function: PipelineCyclerLinks::remove_from_list
@ -77,3 +109,29 @@ insert_before(PipelineCyclerLinks *node) {
node->_prev = this; node->_prev = this;
} }
#endif // THREADED_PIPELINE #endif // THREADED_PIPELINE
#ifdef THREADED_PIPELINE
////////////////////////////////////////////////////////////////////
// Function: PipelineCyclerLinks::take_list
// Access: Protected
// Description: When called on the head of an empty list, takes all
// of the leemnts from the indicated list and moves them
// to this list.
////////////////////////////////////////////////////////////////////
INLINE void PipelineCyclerLinks::
take_list(PipelineCyclerLinks &other) {
nassertv(_next == this && _prev == this);
if (other._next == &other && other._prev == &other) {
// The other list is empty; this is a no-op.
return;
}
other._next->_prev = this;
other._prev->_next = this;
_next = other._next;
_prev = other._prev;
other._next = &other;
other._prev = &other;
}
#endif // THREADED_PIPELINE

View File

@ -38,9 +38,14 @@ protected:
INLINE PipelineCyclerLinks(); INLINE PipelineCyclerLinks();
INLINE ~PipelineCyclerLinks(); INLINE ~PipelineCyclerLinks();
INLINE void make_head();
INLINE void clear_head();
INLINE void remove_from_list(); INLINE void remove_from_list();
INLINE void insert_before(PipelineCyclerLinks *node); INLINE void insert_before(PipelineCyclerLinks *node);
INLINE void take_list(PipelineCyclerLinks &other);
PipelineCyclerLinks *_prev, *_next; PipelineCyclerLinks *_prev, *_next;
#endif #endif
friend class Pipeline; friend class Pipeline;