diff --git a/panda/src/display/graphicsEngine.cxx b/panda/src/display/graphicsEngine.cxx index 2cbdaceada..cfe8ca55cd 100644 --- a/panda/src/display/graphicsEngine.cxx +++ b/panda/src/display/graphicsEngine.cxx @@ -60,7 +60,7 @@ PStatCollector GraphicsEngine::_transform_states_unused_pcollector("TransformSta PStatCollector GraphicsEngine::_render_states_pcollector("RenderStates"); PStatCollector GraphicsEngine::_render_states_unused_pcollector("RenderStates:Unused"); PStatCollector GraphicsEngine::_cyclers_pcollector("PipelineCyclers"); -PStatCollector GraphicsEngine::_dirty_cyclers_pcollector("PipelineCyclers:Dirty"); +PStatCollector GraphicsEngine::_dirty_cyclers_pcollector("Dirty PipelineCyclers"); //////////////////////////////////////////////////////////////////// // Function: GraphicsEngine::Constructor @@ -540,10 +540,18 @@ render_frame() { } } -#if defined(DO_PIPELINING) && defined(HAVE_THREADS) +#if defined(THREADED_PIPELINE) && defined(DO_PSTATS) _cyclers_pcollector.set_level(_pipeline->get_num_cyclers()); _dirty_cyclers_pcollector.set_level(_pipeline->get_num_dirty_cyclers()); -#endif // DO_PIPELINING && HAVE_THREADS + +#ifdef DEBUG_THREADS + if (PStatClient::is_connected()) { + _pipeline->iterate_all_cycler_types(pstats_count_cycler_type, this); + _pipeline->iterate_dirty_cycler_types(pstats_count_dirty_cycler_type, this); + } +#endif // DEBUG_THREADS + +#endif // THREADED_PIPELINE && DO_PSTATS // Now cycle the pipeline and officially begin the next frame. { @@ -1458,6 +1466,42 @@ terminate_threads() { _threads.clear(); } +//////////////////////////////////////////////////////////////////// +// Function: GraphicsEngine::pstats_count_cycler_type +// Access: Private, Static +// Description: A callback function for +// Pipeline::iterate_all_cycler_types() to report the +// cycler types to PStats. +//////////////////////////////////////////////////////////////////// +void GraphicsEngine:: +pstats_count_cycler_type(TypeHandle type, int count, void *data) { + GraphicsEngine *self = (GraphicsEngine *)data; + CyclerTypeCounters::iterator ci = self->_all_cycler_types.find(type); + if (ci == self->_all_cycler_types.end()) { + PStatCollector collector(_cyclers_pcollector, type.get_name()); + ci = self->_all_cycler_types.insert(CyclerTypeCounters::value_type(type, collector)).first; + } + (*ci).second.set_level(count); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsEngine::pstats_count_dirty_cycler_type +// Access: Private, Static +// Description: A callback function for +// Pipeline::iterate_dirty_cycler_types() to report the +// cycler types to PStats. +//////////////////////////////////////////////////////////////////// +void GraphicsEngine:: +pstats_count_dirty_cycler_type(TypeHandle type, int count, void *data) { + GraphicsEngine *self = (GraphicsEngine *)data; + CyclerTypeCounters::iterator ci = self->_dirty_cycler_types.find(type); + if (ci == self->_dirty_cycler_types.end()) { + PStatCollector collector(_dirty_cyclers_pcollector, type.get_name()); + ci = self->_dirty_cycler_types.insert(CyclerTypeCounters::value_type(type, collector)).first; + } + (*ci).second.set_level(count); +} + //////////////////////////////////////////////////////////////////// // Function: GraphicsEngine::get_invert_polygon_state // Access: Protected, Static @@ -1505,11 +1549,12 @@ get_window_renderer(const string &name, int pipeline_stage) { PT(RenderThread) thread = new RenderThread(name, this); thread->set_min_pipeline_stage(pipeline_stage); - _pipeline->set_min_stages(pipeline_stage + 1); thread->start(TP_normal, true, true); _threads[name] = thread; + nassertr(pipeline_stage < _pipeline->get_num_stages(), thread.p()); + return thread.p(); } diff --git a/panda/src/display/graphicsEngine.h b/panda/src/display/graphicsEngine.h index 0f827e8ab8..b5a8724844 100644 --- a/panda/src/display/graphicsEngine.h +++ b/panda/src/display/graphicsEngine.h @@ -171,6 +171,14 @@ private: void do_resort_windows(); void terminate_threads(); +#ifdef DO_PSTATS + typedef map CyclerTypeCounters; + CyclerTypeCounters _all_cycler_types; + CyclerTypeCounters _dirty_cycler_types; + static void pstats_count_cycler_type(TypeHandle type, int count, void *data); + static void pstats_count_dirty_cycler_type(TypeHandle type, int count, void *data); +#endif // DO_PSTATS + static const RenderState *get_invert_polygon_state(); // The WindowRenderer class records the stages of the pipeline that diff --git a/panda/src/pstatclient/pStatProperties.cxx b/panda/src/pstatclient/pStatProperties.cxx index 40b613509b..baff29e87a 100644 --- a/panda/src/pstatclient/pStatProperties.cxx +++ b/panda/src/pstatclient/pStatProperties.cxx @@ -211,8 +211,8 @@ static LevelCollectorProperties level_properties[] = { { 1, "RenderStates:On nodes", { 0.2, 0.8, 1.0 } }, { 1, "RenderStates:Cached", { 1.0, 0.0, 0.2 } }, { 1, "RenderStates:Unused", { 0.2, 0.2, 0.2 } }, - { 1, "PipelineCyclers", { 0.5, 0.5, 1.0 }, "", 5000 }, - { 1, "PipelineCyclers:Dirty", { 0.2, 0.2, 0.2 } }, + { 1, "PipelineCyclers", { 0.5, 0.5, 1.0 }, "", 50000 }, + { 1, "Dirty PipelineCyclers", { 0.2, 0.2, 0.2 }, "", 5000 }, { 0, NULL } }; diff --git a/panda/src/putil/pipeline.I b/panda/src/putil/pipeline.I index ef1e59a11b..1bff17f440 100644 --- a/panda/src/putil/pipeline.I +++ b/panda/src/putil/pipeline.I @@ -30,17 +30,6 @@ get_render_pipeline() { return _render_pipeline; } -//////////////////////////////////////////////////////////////////// -// Function: Pipeline::set_min_stages -// Access: Public -// Description: Ensures that at least the indicated number of stages -// are in the pipeline. -//////////////////////////////////////////////////////////////////// -INLINE void Pipeline:: -set_min_stages(int min_stages) { - set_num_stages(max(min_stages, get_num_stages())); -} - //////////////////////////////////////////////////////////////////// // Function: Pipeline::get_num_stages // Access: Public @@ -62,7 +51,7 @@ get_num_stages() const { INLINE int Pipeline:: get_num_cyclers() const { ReMutexHolder holder(_lock); - return _cyclers.size(); + return _num_cyclers; } #endif // THREADED_PIPELINE diff --git a/panda/src/putil/pipeline.cxx b/panda/src/putil/pipeline.cxx index 8616d6dce9..bb7930cbac 100644 --- a/panda/src/putil/pipeline.cxx +++ b/panda/src/putil/pipeline.cxx @@ -19,6 +19,7 @@ #include "pipeline.h" #include "pipelineCyclerTrueImpl.h" #include "reMutexHolder.h" +#include "configVariableInt.h" Pipeline *Pipeline::_render_pipeline = (Pipeline *)NULL; @@ -28,12 +29,12 @@ Pipeline *Pipeline::_render_pipeline = (Pipeline *)NULL; // Description: //////////////////////////////////////////////////////////////////// Pipeline:: -Pipeline(const string &name) : - Namable(name) +Pipeline(const string &name, int num_stages) : + Namable(name), + _num_stages(num_stages) { - _num_stages = 1; - #ifdef THREADED_PIPELINE + _num_cyclers = 0; _cycling = false; #endif // THREADED_PIPELINE } @@ -46,7 +47,7 @@ Pipeline(const string &name) : Pipeline:: ~Pipeline() { #ifdef THREADED_PIPELINE - nassertv(_cyclers.empty()); + nassertv(_num_cyclers == 0); nassertv(!_cycling); #endif // THREADED_PIPELINE } @@ -91,6 +92,10 @@ cycle() { // set for next time. bool inserted = next_dirty_cyclers.insert(cycler).second; nassertv(inserted); + } else { +#ifdef DEBUG_THREADS + inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1); +#endif } } break; @@ -104,6 +109,10 @@ cycle() { if (cycler->_dirty) { bool inserted = next_dirty_cyclers.insert(cycler).second; nassertv(inserted); + } else { +#ifdef DEBUG_THREADS + inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1); +#endif } } break; @@ -117,6 +126,10 @@ cycle() { if (cycler->_dirty) { bool inserted = next_dirty_cyclers.insert(cycler).second; nassertv(inserted); + } else { +#ifdef DEBUG_THREADS + inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1); +#endif } } break; @@ -135,44 +148,6 @@ cycle() { #endif // THREADED_PIPELINE } -//////////////////////////////////////////////////////////////////// -// Function: Pipeline::set_num_stages -// Access: Public -// Description: Specifies the number of stages required for the -// pipeline. -//////////////////////////////////////////////////////////////////// -void Pipeline:: -set_num_stages(int num_stages) { - nassertv(num_stages >= 1); -#ifdef THREADED_PIPELINE - ReMutexHolder holder(_lock); - if (num_stages != _num_stages) { - - // We need to lock every PipelineCycler object in the world before - // we can adjust the number of stages. - Cyclers::iterator ci; - for (ci = _cyclers.begin(); ci != _cyclers.end(); ++ci) { - (*ci)->_lock.lock(); - } - - _num_stages = num_stages; - - for (ci = _cyclers.begin(); ci != _cyclers.end(); ++ci) { - (*ci)->set_num_stages(num_stages); - } - - // Now release them all. - for (ci = _cyclers.begin(); ci != _cyclers.end(); ++ci) { - (*ci)->_lock.release(); - } - } - -#else // THREADED_PIPELINE - _num_stages = num_stages; -#endif // THREADED_PIPELINE -} - - #ifdef THREADED_PIPELINE //////////////////////////////////////////////////////////////////// // Function: Pipeline::add_cycler @@ -186,8 +161,11 @@ add_cycler(PipelineCyclerTrueImpl *cycler) { ReMutexHolder holder(_lock); nassertv(!cycler->_dirty); nassertv(!_cycling); - bool inserted = _cyclers.insert(cycler).second; - nassertv(inserted); + ++_num_cyclers; + +#ifdef DEBUG_THREADS + inc_cycler_type(_all_cycler_types, cycler->get_parent_type(), 1); +#endif } #endif // THREADED_PIPELINE @@ -212,6 +190,10 @@ add_dirty_cycler(PipelineCyclerTrueImpl *cycler) { bool inserted = _dirty_cyclers.insert(cycler).second; nassertv(inserted); + +#ifdef DEBUG_THREADS + inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), 1); +#endif } #endif // THREADED_PIPELINE @@ -229,20 +211,66 @@ remove_cycler(PipelineCyclerTrueImpl *cycler) { ReMutexHolder holder(_lock); nassertv(!_cycling); - - Cyclers::iterator ci = _cyclers.find(cycler); - nassertv(ci != _cyclers.end()); - _cyclers.erase(ci); + + --_num_cyclers; + +#ifdef DEBUG_THREADS + inc_cycler_type(_all_cycler_types, cycler->get_parent_type(), -1); +#endif if (cycler->_dirty) { cycler->_dirty = false; Cyclers::iterator ci = _dirty_cyclers.find(cycler); nassertv(ci != _dirty_cyclers.end()); _dirty_cyclers.erase(ci); +#ifdef DEBUG_THREADS + inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1); +#endif } } #endif // THREADED_PIPELINE +#ifdef DEBUG_THREADS +//////////////////////////////////////////////////////////////////// +// Function: Pipeline::iterate_all_cycler_types +// Access: Public +// Description: Walks through the list of all the different +// PipelineCycler types in the universe. For each one, +// calls the indicated callback function with the +// TypeHandle of the respective type (actually, the +// result of cycler::get_parent_type()) and the count of +// pipeline cyclers of that type. Mainly used for +// PStats reporting. +//////////////////////////////////////////////////////////////////// +void Pipeline:: +iterate_all_cycler_types(CallbackFunc *func, void *data) const { + ReMutexHolder holder(_lock); + TypeCount::const_iterator ci; + for (ci = _all_cycler_types.begin(); ci != _all_cycler_types.end(); ++ci) { + func((*ci).first, (*ci).second, data); + } +} +#endif // DEBUG_THREADS + +#ifdef DEBUG_THREADS +//////////////////////////////////////////////////////////////////// +// Function: Pipeline::iterate_dirty_cycler_types +// Access: Public +// Description: Walks through the list of all the different +// PipelineCycler types, for only the dirty +// PipelineCyclers. See also +// iterate_all_cycler_types(). +//////////////////////////////////////////////////////////////////// +void Pipeline:: +iterate_dirty_cycler_types(CallbackFunc *func, void *data) const { + ReMutexHolder holder(_lock); + TypeCount::const_iterator ci; + for (ci = _dirty_cycler_types.begin(); ci != _dirty_cycler_types.end(); ++ci) { + func((*ci).first, (*ci).second, data); + } +} +#endif // DEBUG_THREADS + //////////////////////////////////////////////////////////////////// // Function: Pipeline::make_render_pipeline // Access: Private, Static @@ -250,6 +278,38 @@ remove_cycler(PipelineCyclerTrueImpl *cycler) { //////////////////////////////////////////////////////////////////// void Pipeline:: make_render_pipeline() { + ConfigVariableInt pipeline_stages + ("pipeline-stages", 1, + PRC_DESC("The number of stages in the render pipeline. This is only " + "meaningful if threaded pipelining is compiled into Panda, in " + "which case you should set this to 1, 2, or 3, according to " + "your application's threading model. You may set it larger " + "than your application requires, but this will incur additional " + "overhead.")); + nassertv(_render_pipeline == (Pipeline *)NULL); - _render_pipeline = new Pipeline("render"); + _render_pipeline = new Pipeline("render", pipeline_stages); } + +#ifdef DEBUG_THREADS +//////////////////////////////////////////////////////////////////// +// Function: Pipeline::inc_cycler_type +// Access: Private, Static +// Description: Increments (or decrements, according to added) the +// value for TypeHandle in the indicated TypeCount map. +// This is used in DEBUG_THREADS mode to track the types +// of PipelineCyclers that are coming and going, mainly +// for PStats reporting. +// +// It is assumed the lock is held during this call. +//////////////////////////////////////////////////////////////////// +void Pipeline:: +inc_cycler_type(TypeCount &count, TypeHandle type, int addend) { + TypeCount::iterator ci = count.find(type); + if (ci == count.end()) { + ci = count.insert(TypeCount::value_type(type, 0)).first; + } + (*ci).second += addend; + nassertv((*ci).second >= 0); +} +#endif // DEBUG_THREADS diff --git a/panda/src/putil/pipeline.h b/panda/src/putil/pipeline.h index 6e9fe3bde4..2271c1b0a8 100644 --- a/panda/src/putil/pipeline.h +++ b/panda/src/putil/pipeline.h @@ -42,15 +42,13 @@ struct PipelineCyclerTrueImpl; //////////////////////////////////////////////////////////////////// class EXPCL_PANDA Pipeline : public Namable { public: - Pipeline(const string &name); + Pipeline(const string &name, int num_stages); ~Pipeline(); INLINE static Pipeline *get_render_pipeline(); void cycle(); - void set_num_stages(int num_stages); - INLINE void set_min_stages(int min_stages); INLINE int get_num_stages() const; #ifdef THREADED_PIPELINE @@ -60,6 +58,13 @@ public: INLINE int get_num_cyclers() const; INLINE int get_num_dirty_cyclers() const; + +#ifdef DEBUG_THREADS + typedef void CallbackFunc(TypeHandle type, int count, void *data); + void iterate_all_cycler_types(CallbackFunc *func, void *data) const; + void iterate_dirty_cycler_types(CallbackFunc *func, void *data) const; +#endif // DEBUG_THREADS + #endif // THREADED_PIPELINE private: @@ -69,10 +74,17 @@ private: static Pipeline *_render_pipeline; #ifdef THREADED_PIPELINE + int _num_cyclers; typedef pset Cyclers; - Cyclers _cyclers; Cyclers _dirty_cyclers; +#ifdef DEBUG_THREADS + typedef pmap TypeCount; + TypeCount _all_cycler_types, _dirty_cycler_types; + + static void inc_cycler_type(TypeCount &count, TypeHandle type, int addend); +#endif // DEBUG_THREADS + // This is true only during cycle(). bool _cycling; diff --git a/panda/src/putil/pipelineCyclerDummyImpl.I b/panda/src/putil/pipelineCyclerDummyImpl.I index dc3c60ad55..b1c83ad6a2 100644 --- a/panda/src/putil/pipelineCyclerDummyImpl.I +++ b/panda/src/putil/pipelineCyclerDummyImpl.I @@ -368,6 +368,17 @@ release_write_stage(int n, CycleData *pointer) { _write_count--; } +//////////////////////////////////////////////////////////////////// +// Function: PipelineCyclerDummyImpl::get_parent_type +// Access: Public +// Description: Returns the type of object that owns this cycler, as +// reported by CycleData::get_parent_type(). +//////////////////////////////////////////////////////////////////// +INLINE TypeHandle PipelineCyclerDummyImpl:: +get_parent_type() const { + return _data->get_parent_type(); +} + //////////////////////////////////////////////////////////////////// // Function: PipelineCyclerDummyImpl::cheat // Access: Public diff --git a/panda/src/putil/pipelineCyclerDummyImpl.h b/panda/src/putil/pipelineCyclerDummyImpl.h index 275dbbeba8..33e5206048 100644 --- a/panda/src/putil/pipelineCyclerDummyImpl.h +++ b/panda/src/putil/pipelineCyclerDummyImpl.h @@ -73,6 +73,8 @@ public: INLINE CycleData *elevate_read_stage(int n, const CycleData *pointer); INLINE void release_write_stage(int n, CycleData *pointer); + INLINE TypeHandle get_parent_type() const; + INLINE CycleData *cheat() const; INLINE int get_read_count() const; INLINE int get_write_count() const; diff --git a/panda/src/putil/pipelineCyclerTrivialImpl.I b/panda/src/putil/pipelineCyclerTrivialImpl.I index 56fb317fe8..19cc5483d5 100644 --- a/panda/src/putil/pipelineCyclerTrivialImpl.I +++ b/panda/src/putil/pipelineCyclerTrivialImpl.I @@ -339,6 +339,17 @@ INLINE void PipelineCyclerTrivialImpl:: release_write_stage(int, CycleData *) { } +//////////////////////////////////////////////////////////////////// +// Function: PipelineCyclerTrivialImpl::get_parent_type +// Access: Public +// Description: Returns the type of object that owns this cycler, as +// reported by CycleData::get_parent_type(). +//////////////////////////////////////////////////////////////////// +INLINE TypeHandle PipelineCyclerTrivialImpl:: +get_parent_type() const { + return cheat()->get_parent_type(); +} + //////////////////////////////////////////////////////////////////// // Function: PipelineCyclerTrivialImpl::cheat // Access: Public diff --git a/panda/src/putil/pipelineCyclerTrivialImpl.h b/panda/src/putil/pipelineCyclerTrivialImpl.h index cb37ca7fbc..a9f436f6b2 100644 --- a/panda/src/putil/pipelineCyclerTrivialImpl.h +++ b/panda/src/putil/pipelineCyclerTrivialImpl.h @@ -76,6 +76,8 @@ public: INLINE CycleData *elevate_read_stage(int n, const CycleData *pointer); INLINE void release_write_stage(int n, CycleData *pointer); + INLINE TypeHandle get_parent_type() const; + INLINE CycleData *cheat() const; INLINE int get_read_count() const; INLINE int get_write_count() const; diff --git a/panda/src/putil/pipelineCyclerTrueImpl.I b/panda/src/putil/pipelineCyclerTrueImpl.I index 21649e52d4..8f7d7dcf8d 100644 --- a/panda/src/putil/pipelineCyclerTrueImpl.I +++ b/panda/src/putil/pipelineCyclerTrueImpl.I @@ -266,6 +266,17 @@ release_write_stage(int n, CycleData *pointer) { _lock.release(); } +//////////////////////////////////////////////////////////////////// +// Function: PipelineCyclerTrueImpl::get_parent_type +// Access: Public +// Description: Returns the type of object that owns this cycler, as +// reported by CycleData::get_parent_type(). +//////////////////////////////////////////////////////////////////// +INLINE TypeHandle PipelineCyclerTrueImpl:: +get_parent_type() const { + return _data[0]->get_parent_type(); +} + //////////////////////////////////////////////////////////////////// // Function: PipelineCyclerTrueImpl::cheat // Access: Public diff --git a/panda/src/putil/pipelineCyclerTrueImpl.cxx b/panda/src/putil/pipelineCyclerTrueImpl.cxx index 65512d2ed3..0068b4d7d3 100644 --- a/panda/src/putil/pipelineCyclerTrueImpl.cxx +++ b/panda/src/putil/pipelineCyclerTrueImpl.cxx @@ -37,13 +37,14 @@ PipelineCyclerTrueImpl(CycleData *initial_data, Pipeline *pipeline) : if (_pipeline == (Pipeline *)NULL) { _pipeline = Pipeline::get_render_pipeline(); } - _pipeline->add_cycler(this); _num_stages = _pipeline->get_num_stages(); _data = new PT(CycleData)[_num_stages]; for (int i = 0; i < _num_stages; ++i) { _data[i] = initial_data; } + + _pipeline->add_cycler(this); } //////////////////////////////////////////////////////////////////// @@ -58,23 +59,19 @@ PipelineCyclerTrueImpl(const PipelineCyclerTrueImpl ©) : _lock(this) { ReMutexHolder holder(_lock); - _pipeline->add_cycler(this); - if (copy._dirty) { - _pipeline->add_dirty_cycler(this); - } - ReMutexHolder holder2(copy._lock); + _num_stages = _pipeline->get_num_stages(); nassertv(_num_stages == copy._num_stages); _data = new PT(CycleData)[_num_stages]; - + // It's no longer critically important that we preserve pointerwise // equivalence between different stages in the copy, but it doesn't // cost much and might be a little more efficient, so we do it // anyway. typedef pmap Pointers; Pointers pointers; - + for (int i = 0; i < _num_stages; ++i) { PT(CycleData) &new_pt = pointers[copy._data[i]]; if (new_pt == NULL) { @@ -82,6 +79,11 @@ PipelineCyclerTrueImpl(const PipelineCyclerTrueImpl ©) : } _data[i] = new_pt; } + + _pipeline->add_cycler(this); + if (copy._dirty) { + _pipeline->add_dirty_cycler(this); + } } //////////////////////////////////////////////////////////////////// @@ -93,8 +95,7 @@ void PipelineCyclerTrueImpl:: operator = (const PipelineCyclerTrueImpl ©) { ReMutexHolder holder1(_lock); ReMutexHolder holder2(copy._lock); - - nassertv(_num_stages == copy._num_stages); + nassertv(get_parent_type() == copy.get_parent_type()); typedef pmap Pointers; Pointers pointers; @@ -120,11 +121,12 @@ operator = (const PipelineCyclerTrueImpl ©) { PipelineCyclerTrueImpl:: ~PipelineCyclerTrueImpl() { ReMutexHolder holder(_lock); + + _pipeline->remove_cycler(this); + delete[] _data; _data = NULL; _num_stages = 0; - - _pipeline->remove_cycler(this); } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/putil/pipelineCyclerTrueImpl.h b/panda/src/putil/pipelineCyclerTrueImpl.h index 1fd4122fb3..11911ea538 100644 --- a/panda/src/putil/pipelineCyclerTrueImpl.h +++ b/panda/src/putil/pipelineCyclerTrueImpl.h @@ -74,6 +74,8 @@ public: INLINE CycleData *elevate_read_stage(int n, const CycleData *pointer); INLINE void release_write_stage(int n, CycleData *pointer); + INLINE TypeHandle get_parent_type() const; + INLINE CycleData *cheat() const; INLINE int get_read_count() const; INLINE int get_write_count() const;