From c75b55eb983772624d20716e828b68c2bb765190 Mon Sep 17 00:00:00 2001 From: David Rose Date: Fri, 3 Feb 2006 18:12:08 +0000 Subject: [PATCH] better pipelining --- panda/src/display/graphicsEngine.cxx | 148 +++++++++++++-------- panda/src/display/graphicsEngine.h | 1 + panda/src/express/thread.I | 12 ++ panda/src/express/thread.h | 1 + panda/src/gobj/boundedObject.h | 3 + panda/src/gobj/geom.h | 3 + panda/src/gobj/geomPrimitive.h | 3 + panda/src/gobj/geomVertexArrayData.h | 3 + panda/src/gobj/geomVertexData.I | 12 ++ panda/src/gobj/geomVertexData.cxx | 62 +++++---- panda/src/gobj/geomVertexData.h | 12 +- panda/src/gobj/sliderTable.h | 3 + panda/src/gobj/transformBlendTable.h | 3 + panda/src/gobj/transformTable.h | 3 + panda/src/gobj/userVertexSlider.h | 3 + panda/src/gobj/userVertexTransform.h | 3 + panda/src/gobj/vertexSlider.h | 3 + panda/src/gobj/vertexTransform.h | 3 + panda/src/parametrics/ropeNode.h | 3 + panda/src/parametrics/sheetNode.h | 3 + panda/src/pgraph/directionalLight.h | 3 + panda/src/pgraph/geomNode.h | 3 + panda/src/pgraph/light.h | 3 + panda/src/pgraph/lodNode.h | 3 + panda/src/pgraph/nodePathComponent.h | 3 + panda/src/pgraph/pandaNode.h | 3 + panda/src/pgraph/planeNode.h | 3 + panda/src/pgraph/pointLight.h | 3 + panda/src/pgraph/spotlight.h | 3 + panda/src/pgraph/switchNode.h | 3 + panda/src/putil/animInterface.h | 3 + panda/src/putil/cycleData.cxx | 22 +++ panda/src/putil/cycleData.h | 11 +- panda/src/putil/pipeline.I | 11 ++ panda/src/putil/pipeline.cxx | 28 +++- panda/src/putil/pipeline.h | 4 + panda/src/putil/pipelineCyclerTrueImpl.I | 62 ++++++--- panda/src/putil/pipelineCyclerTrueImpl.cxx | 88 +++++++++++- panda/src/putil/pipelineCyclerTrueImpl.h | 5 +- 39 files changed, 425 insertions(+), 126 deletions(-) diff --git a/panda/src/display/graphicsEngine.cxx b/panda/src/display/graphicsEngine.cxx index 12ad6bdb1a..be1cdc870a 100644 --- a/panda/src/display/graphicsEngine.cxx +++ b/panda/src/display/graphicsEngine.cxx @@ -46,6 +46,7 @@ #endif PStatCollector GraphicsEngine::_wait_pcollector("Wait"); +PStatCollector GraphicsEngine::_cycle_pcollector("App:Cycle"); PStatCollector GraphicsEngine::_app_pcollector("App"); PStatCollector GraphicsEngine::_yield_pcollector("App:Yield"); PStatCollector GraphicsEngine::_cull_pcollector("Cull"); @@ -487,12 +488,35 @@ render_frame() { GraphicsOutput *win = (*wi); if (win->get_delete_flag()) { do_remove_window(win); - + } else { new_windows.push_back(win); + + // Let's calculate each scene's bounding volume here in App, + // before we cycle the pipeline. The cull traversal will + // calculate it anyway, but if we calculate it in App first + // before it gets calculated in the Cull thread, it will be more + // likely to stick for subsequent frames, so we won't have to + // recompute it each frame. + int num_drs = win->get_num_active_display_regions(); + for (int i = 0; i < num_drs; ++i) { + DisplayRegion *dr = win->get_active_display_region(i); + if (dr != (DisplayRegion *)NULL) { + NodePath camera_np = dr->get_camera(); + if (!camera_np.is_empty()) { + Camera *camera = DCAST(Camera, camera_np.node()); + NodePath scene = camera->get_scene(); + if (scene.is_empty()) { + scene = camera_np.get_top(); + } + if (!scene.is_empty()) { + scene.get_bounds(); + } + } + } + } } } - new_windows.swap(_windows); // Now it's time to do any drawing from the main frame--after all of // the App code has executed, but before we begin the next frame. @@ -500,13 +524,13 @@ render_frame() { // Grab each thread's mutex again after all windows have flipped, // and wait for the thread to finish. - Threads::const_iterator ti; - for (ti = _threads.begin(); ti != _threads.end(); ++ti) { - RenderThread *thread = (*ti).second; - thread->_cv_mutex.lock(); - - if (thread->_thread_state != TS_wait) { - PStatTimer timer(_wait_pcollector); + { + PStatTimer timer(_wait_pcollector); + Threads::const_iterator ti; + for (ti = _threads.begin(); ti != _threads.end(); ++ti) { + RenderThread *thread = (*ti).second; + thread->_cv_mutex.lock(); + while (thread->_thread_state != TS_wait) { thread->_cv_done.wait(); } @@ -514,7 +538,10 @@ render_frame() { } // Now cycle the pipeline and officially begin the next frame. - _pipeline->cycle(); + { + PStatTimer timer(_cycle_pcollector); + _pipeline->cycle(); + } ClockObject *global_clock = ClockObject::get_global_clock(); global_clock->tick(); if (global_clock->check_errors()) { @@ -539,6 +566,7 @@ render_frame() { } // Now signal all of our threads to begin their next frame. + Threads::const_iterator ti; for (ti = _threads.begin(); ti != _threads.end(); ++ti) { RenderThread *thread = (*ti).second; if (thread->_thread_state == TS_wait) { @@ -589,39 +617,36 @@ open_windows() { _app.do_windows(this); - Threads::const_iterator ti; - for (ti = _threads.begin(); ti != _threads.end(); ++ti) { - RenderThread *thread = (*ti).second; - thread->_cv_mutex.lock(); - - if (thread->_thread_state != TS_wait) { - PStatTimer timer(_wait_pcollector); + { + PStatTimer timer(_wait_pcollector); + Threads::const_iterator ti; + for (ti = _threads.begin(); ti != _threads.end(); ++ti) { + RenderThread *thread = (*ti).second; + thread->_cv_mutex.lock(); + while (thread->_thread_state != TS_wait) { thread->_cv_done.wait(); } + + thread->_thread_state = TS_do_windows; + thread->_cv_start.signal(); + thread->_cv_mutex.release(); } - thread->_thread_state = TS_do_windows; - thread->_cv_start.signal(); - thread->_cv_mutex.release(); - } - - // We do it twice, to allow both cull and draw to process the - // window. - for (ti = _threads.begin(); ti != _threads.end(); ++ti) { - RenderThread *thread = (*ti).second; - thread->_cv_mutex.lock(); - - if (thread->_thread_state != TS_wait) { - PStatTimer timer(_wait_pcollector); + // We do it twice, to allow both cull and draw to process the + // window. + for (ti = _threads.begin(); ti != _threads.end(); ++ti) { + RenderThread *thread = (*ti).second; + thread->_cv_mutex.lock(); + while (thread->_thread_state != TS_wait) { thread->_cv_done.wait(); } + + thread->_thread_state = TS_do_windows; + thread->_cv_start.signal(); + thread->_cv_mutex.release(); } - - thread->_thread_state = TS_do_windows; - thread->_cv_start.signal(); - thread->_cv_mutex.release(); } } @@ -1008,13 +1033,13 @@ do_flip_frame() { // First, wait for all the threads to finish their current frame, if // necessary. Grabbing the mutex (and waiting for TS_wait) should // achieve that. - Threads::const_iterator ti; - for (ti = _threads.begin(); ti != _threads.end(); ++ti) { - RenderThread *thread = (*ti).second; - thread->_cv_mutex.lock(); - - if (thread->_thread_state != TS_wait) { - PStatTimer timer(_wait_pcollector); + { + PStatTimer timer(_wait_pcollector); + Threads::const_iterator ti; + for (ti = _threads.begin(); ti != _threads.end(); ++ti) { + RenderThread *thread = (*ti).second; + thread->_cv_mutex.lock(); + while (thread->_thread_state != TS_wait) { thread->_cv_done.wait(); } @@ -1023,12 +1048,16 @@ do_flip_frame() { // Now signal all of our threads to flip the windows. _app.do_flip(this); - for (ti = _threads.begin(); ti != _threads.end(); ++ti) { - RenderThread *thread = (*ti).second; - nassertv(thread->_thread_state == TS_wait); - thread->_thread_state = TS_do_flip; - thread->_cv_start.signal(); - thread->_cv_mutex.release(); + + { + Threads::const_iterator ti; + for (ti = _threads.begin(); ti != _threads.end(); ++ti) { + RenderThread *thread = (*ti).second; + nassertv(thread->_thread_state == TS_wait); + thread->_thread_state = TS_do_flip; + thread->_cv_start.signal(); + thread->_cv_mutex.release(); + } } _flip_state = FS_flip; @@ -1156,6 +1185,9 @@ do_cull(CullHandler *cull_handler, SceneSetup *scene_setup, trav.set_view_frustum(local_frustum); } } + + static PStatCollector traverse("Cull:Traverse"); + PStatTimer timer2(traverse); trav.traverse(scene_setup->get_scene_root(), get_portal_cull()); } @@ -1326,6 +1358,10 @@ do_resort_windows() { void GraphicsEngine:: terminate_threads() { MutexHolder holder(_lock); + + // We spend almost our entire time in this method just waiting for + // threads. Time it appropriately. + PStatTimer timer(_wait_pcollector); // First, wait for all the threads to finish their current frame. // Grabbing the mutex should achieve that. @@ -1334,26 +1370,26 @@ terminate_threads() { RenderThread *thread = (*ti).second; thread->_cv_mutex.lock(); } - + // Now tell them to release their windows' graphics contexts. for (ti = _threads.begin(); ti != _threads.end(); ++ti) { RenderThread *thread = (*ti).second; - - if (thread->_thread_state != TS_wait) { - PStatTimer timer(_wait_pcollector); - while (thread->_thread_state != TS_wait) { - thread->_cv_done.wait(); - } + + while (thread->_thread_state != TS_wait) { + thread->_cv_done.wait(); } thread->_thread_state = TS_do_release; thread->_cv_start.signal(); thread->_cv_mutex.release(); } - + // Grab the mutex again to wait for the above to complete. for (ti = _threads.begin(); ti != _threads.end(); ++ti) { RenderThread *thread = (*ti).second; thread->_cv_mutex.lock(); + while (thread->_thread_state != TS_wait) { + thread->_cv_done.wait(); + } } // Now tell them to close their windows and terminate. @@ -1416,7 +1452,7 @@ get_window_renderer(const string &name) { PT(RenderThread) thread = new RenderThread(name, this); thread->set_pipeline_stage(1); - Pipeline::get_render_pipeline()->set_num_stages(2); + _pipeline->set_min_stages(2); thread->start(TP_normal, true, true); _threads[name] = thread; diff --git a/panda/src/display/graphicsEngine.h b/panda/src/display/graphicsEngine.h index b06f32e1c7..a499821ff4 100644 --- a/panda/src/display/graphicsEngine.h +++ b/panda/src/display/graphicsEngine.h @@ -246,6 +246,7 @@ private: Mutex _lock; static PStatCollector _wait_pcollector; + static PStatCollector _cycle_pcollector; static PStatCollector _app_pcollector; static PStatCollector _yield_pcollector; static PStatCollector _cull_pcollector; diff --git a/panda/src/express/thread.I b/panda/src/express/thread.I index 6d5601db91..e7f6f726e1 100644 --- a/panda/src/express/thread.I +++ b/panda/src/express/thread.I @@ -150,6 +150,18 @@ set_pipeline_stage(int pipeline_stage) { _pipeline_stage = pipeline_stage; } +//////////////////////////////////////////////////////////////////// +// Function: Thread::set_min_pipeline_stage +// Access: Published +// Description: Sets this thread's pipeline stage number to at least +// the indicated value, unless it is already larger. +// See set_pipeline_stage(). +//////////////////////////////////////////////////////////////////// +INLINE void Thread:: +set_min_pipeline_stage(int min_pipeline_stage) { + _pipeline_stage = max(_pipeline_stage, min_pipeline_stage); +} + //////////////////////////////////////////////////////////////////// // Function: Thread::get_main_thread // Access: Published, Static diff --git a/panda/src/express/thread.h b/panda/src/express/thread.h index 416fd658da..01e0695822 100644 --- a/panda/src/express/thread.h +++ b/panda/src/express/thread.h @@ -63,6 +63,7 @@ PUBLISHED: INLINE int get_pipeline_stage() const; INLINE void set_pipeline_stage(int pipeline_stage); + INLINE void set_min_pipeline_stage(int min_pipeline_stage); INLINE static Thread *get_main_thread(); INLINE static Thread *get_external_thread(); diff --git a/panda/src/gobj/boundedObject.h b/panda/src/gobj/boundedObject.h index 9cd6122249..bb6a3849fb 100644 --- a/panda/src/gobj/boundedObject.h +++ b/panda/src/gobj/boundedObject.h @@ -82,6 +82,9 @@ private: INLINE void operator = (const CData ©); virtual CycleData *make_copy() const; + virtual TypeHandle get_parent_type() const { + return BoundedObject::get_class_type(); + } int _flags; BoundingVolumeType _bound_type; diff --git a/panda/src/gobj/geom.h b/panda/src/gobj/geom.h index 60d333e7be..fe8819668a 100644 --- a/panda/src/gobj/geom.h +++ b/panda/src/gobj/geom.h @@ -177,6 +177,9 @@ private: virtual void write_datagram(BamWriter *manager, Datagram &dg) const; virtual int complete_pointers(TypedWritable **plist, BamReader *manager); virtual void fillin(DatagramIterator &scan, BamReader *manager); + virtual TypeHandle get_parent_type() const { + return Geom::get_class_type(); + } PT(GeomVertexData) _data; Primitives _primitives; diff --git a/panda/src/gobj/geomPrimitive.h b/panda/src/gobj/geomPrimitive.h index f122eff191..b31f5511c0 100644 --- a/panda/src/gobj/geomPrimitive.h +++ b/panda/src/gobj/geomPrimitive.h @@ -209,6 +209,9 @@ private: virtual void write_datagram(BamWriter *manager, Datagram &dg) const; virtual int complete_pointers(TypedWritable **plist, BamReader *manager); virtual void fillin(DatagramIterator &scan, BamReader *manager); + virtual TypeHandle get_parent_type() const { + return GeomPrimitive::get_class_type(); + } ShadeModel _shade_model; int _first_vertex; diff --git a/panda/src/gobj/geomVertexArrayData.h b/panda/src/gobj/geomVertexArrayData.h index 5c37174099..0b555452cd 100644 --- a/panda/src/gobj/geomVertexArrayData.h +++ b/panda/src/gobj/geomVertexArrayData.h @@ -123,6 +123,9 @@ private: void *extra_data) const; virtual void fillin(DatagramIterator &scan, BamReader *manager, void *extra_data); + virtual TypeHandle get_parent_type() const { + return GeomVertexArrayData::get_class_type(); + } UsageHint _usage_hint; PTA_uchar _data; diff --git a/panda/src/gobj/geomVertexData.I b/panda/src/gobj/geomVertexData.I index 9f6b40a221..b870cb7ddb 100644 --- a/panda/src/gobj/geomVertexData.I +++ b/panda/src/gobj/geomVertexData.I @@ -234,6 +234,18 @@ get_modified() const { return cdata->_modified; } +//////////////////////////////////////////////////////////////////// +// Function: GeomVertexData::clear_cache +// Access: Published +// Description: Removes all of the previously-cached results of +// convert_to(). +//////////////////////////////////////////////////////////////////// +INLINE void GeomVertexData:: +clear_cache() { + CDWriter cdata(_cycler); + do_clear_cache(cdata); +} + //////////////////////////////////////////////////////////////////// // Function: GeomVertexData::has_vertex // Access: Public diff --git a/panda/src/gobj/geomVertexData.cxx b/panda/src/gobj/geomVertexData.cxx index c3b55483b3..6b99f3e476 100644 --- a/panda/src/gobj/geomVertexData.cxx +++ b/panda/src/gobj/geomVertexData.cxx @@ -152,7 +152,7 @@ operator = (const GeomVertexData ©) { _morphs_pcollector = copy._morphs_pcollector; CDWriter cdata(_cycler); - clear_cache(); + do_clear_cache(cdata); cdata->_modified = Geom::get_next_modified(); cdata->_animated_vertices_modified = UpdateSeq(); } @@ -218,7 +218,7 @@ set_usage_hint(GeomVertexData::UsageHint usage_hint) { } (*ai)->set_usage_hint(usage_hint); } - clear_cache(); + do_clear_cache(cdata); cdata->_modified = Geom::get_next_modified(); cdata->_animated_vertices_modified = UpdateSeq(); } @@ -265,7 +265,7 @@ clear_rows() { } (*ai)->clear_rows(); } - clear_cache(); + do_clear_cache(cdata); cdata->_modified = Geom::get_next_modified(); cdata->_animated_vertices.clear(); } @@ -291,7 +291,7 @@ modify_array(int i) { if (cdata->_arrays[i]->get_ref_count() > 1) { cdata->_arrays[i] = new GeomVertexArrayData(*cdata->_arrays[i]); } - clear_cache(); + do_clear_cache(cdata); cdata->_modified = Geom::get_next_modified(); cdata->_animated_vertices_modified = UpdateSeq(); @@ -311,7 +311,7 @@ set_array(int i, const GeomVertexArrayData *array) { CDWriter cdata(_cycler); nassertv(i >= 0 && i < (int)cdata->_arrays.size()); cdata->_arrays[i] = (GeomVertexArrayData *)array; - clear_cache(); + do_clear_cache(cdata); cdata->_modified = Geom::get_next_modified(); cdata->_animated_vertices_modified = UpdateSeq(); } @@ -331,7 +331,7 @@ set_transform_table(const TransformTable *table) { CDWriter cdata(_cycler); cdata->_transform_table = (TransformTable *)table; - clear_cache(); + do_clear_cache(cdata); cdata->_modified = Geom::get_next_modified(); cdata->_animated_vertices_modified = UpdateSeq(); } @@ -354,7 +354,7 @@ modify_transform_blend_table() { if (cdata->_transform_blend_table->get_ref_count() > 1) { cdata->_transform_blend_table = new TransformBlendTable(*cdata->_transform_blend_table); } - clear_cache(); + do_clear_cache(cdata); cdata->_modified = Geom::get_next_modified(); cdata->_animated_vertices_modified = UpdateSeq(); @@ -374,7 +374,7 @@ void GeomVertexData:: set_transform_blend_table(const TransformBlendTable *table) { CDWriter cdata(_cycler); cdata->_transform_blend_table = (TransformBlendTable *)table; - clear_cache(); + do_clear_cache(cdata); cdata->_modified = Geom::get_next_modified(); cdata->_animated_vertices_modified = UpdateSeq(); } @@ -396,7 +396,7 @@ set_slider_table(const SliderTable *table) { CDWriter cdata(_cycler); cdata->_slider_table = (SliderTable *)table; - clear_cache(); + do_clear_cache(cdata); cdata->_modified = Geom::get_next_modified(); cdata->_animated_vertices_modified = UpdateSeq(); } @@ -1069,26 +1069,6 @@ write(ostream &out, int indent_level) const { } } -//////////////////////////////////////////////////////////////////// -// Function: GeomVertexData::clear_cache -// Access: Published -// Description: Removes all of the previously-cached results of -// convert_to(). -//////////////////////////////////////////////////////////////////// -void GeomVertexData:: -clear_cache() { - // Probably we shouldn't do anything at all here unless we are - // running in pipeline stage 0. - CData *cdata = CDWriter(_cycler); - for (Cache::iterator ci = cdata->_cache.begin(); - ci != cdata->_cache.end(); - ++ci) { - CacheEntry *entry = (*ci); - entry->erase(); - } - cdata->_cache.clear(); -} - //////////////////////////////////////////////////////////////////// // Function: GeomVertexData::get_array_info // Access: Public @@ -1263,7 +1243,7 @@ uint8_rgba_to_packed_argb(unsigned char *to, int to_stride, // Description: The private implementation of set_num_rows(). //////////////////////////////////////////////////////////////////// bool GeomVertexData:: -do_set_num_rows(int n, GeomVertexData::CDWriter &cdata) { +do_set_num_rows(int n, GeomVertexData::CData *cdata) { nassertr(_format->get_num_arrays() == (int)cdata->_arrays.size(), false); bool any_changed = false; @@ -1324,7 +1304,7 @@ do_set_num_rows(int n, GeomVertexData::CDWriter &cdata) { } if (any_changed) { - clear_cache(); + do_clear_cache(cdata); cdata->_modified = Geom::get_next_modified(); cdata->_animated_vertices.clear(); } @@ -1340,7 +1320,7 @@ do_set_num_rows(int n, GeomVertexData::CDWriter &cdata) { // existing animated_vertices object. //////////////////////////////////////////////////////////////////// void GeomVertexData:: -update_animated_vertices(GeomVertexData::CDWriter &cdata) { +update_animated_vertices(GeomVertexData::CData *cdata) { int num_rows = get_num_rows(); if (gobj_cat.is_debug()) { @@ -1474,6 +1454,24 @@ update_animated_vertices(GeomVertexData::CDWriter &cdata) { } } +//////////////////////////////////////////////////////////////////// +// Function: GeomVertexData::do_clear_cache +// Access: Private +// Description: The private implementation of clear_cache(). +//////////////////////////////////////////////////////////////////// +void GeomVertexData:: +do_clear_cache(GeomVertexData::CData *cdata) { + // Probably we shouldn't do anything at all here unless we are + // running in pipeline stage 0. + for (Cache::iterator ci = cdata->_cache.begin(); + ci != cdata->_cache.end(); + ++ci) { + CacheEntry *entry = (*ci); + entry->erase(); + } + cdata->_cache.clear(); +} + //////////////////////////////////////////////////////////////////// // Function: GeomVertexData::register_with_read_factory // Access: Public, Static diff --git a/panda/src/gobj/geomVertexData.h b/panda/src/gobj/geomVertexData.h index 209df10d17..f245420815 100644 --- a/panda/src/gobj/geomVertexData.h +++ b/panda/src/gobj/geomVertexData.h @@ -142,7 +142,7 @@ PUBLISHED: void output(ostream &out) const; void write(ostream &out, int indent_level = 0) const; - void clear_cache(); + INLINE void clear_cache(); public: bool get_array_info(const InternalName *name, @@ -191,7 +191,7 @@ private: INLINE static int add_transform(TransformTable *table, const VertexTransform *transform, TransformMap &already_added); - + private: string _name; CPT(GeomVertexFormat) _format; @@ -224,6 +224,9 @@ private: virtual void write_datagram(BamWriter *manager, Datagram &dg) const; virtual int complete_pointers(TypedWritable **plist, BamReader *manager); virtual void fillin(DatagramIterator &scan, BamReader *manager); + virtual TypeHandle get_parent_type() const { + return GeomVertexData::get_class_type(); + } UsageHint _usage_hint; Arrays _arrays; @@ -241,8 +244,9 @@ private: typedef CycleDataWriter CDWriter; private: - bool do_set_num_rows(int n, CDWriter &cdata); - void update_animated_vertices(CDWriter &cdata); + bool do_set_num_rows(int n, CData *cdata); + void update_animated_vertices(CData *cdata); + void do_clear_cache(CData *cdata); static PStatCollector _convert_pcollector; static PStatCollector _scale_color_pcollector; diff --git a/panda/src/gobj/sliderTable.h b/panda/src/gobj/sliderTable.h index a234b33556..5742bf009d 100644 --- a/panda/src/gobj/sliderTable.h +++ b/panda/src/gobj/sliderTable.h @@ -89,6 +89,9 @@ private: virtual CycleData *make_copy() const; virtual void write_datagram(BamWriter *manager, Datagram &dg) const; virtual void fillin(DatagramIterator &scan, BamReader *manager); + virtual TypeHandle get_parent_type() const { + return SliderTable::get_class_type(); + } UpdateSeq _modified; }; diff --git a/panda/src/gobj/transformBlendTable.h b/panda/src/gobj/transformBlendTable.h index 4913c4a29d..0c0869de14 100644 --- a/panda/src/gobj/transformBlendTable.h +++ b/panda/src/gobj/transformBlendTable.h @@ -99,6 +99,9 @@ private: virtual CycleData *make_copy() const; virtual void write_datagram(BamWriter *manager, Datagram &dg) const; virtual void fillin(DatagramIterator &scan, BamReader *manager); + virtual TypeHandle get_parent_type() const { + return TransformBlendTable::get_class_type(); + } UpdateSeq _modified; UpdateSeq _global_modified; diff --git a/panda/src/gobj/transformTable.h b/panda/src/gobj/transformTable.h index 37d987d786..ead3cd7088 100644 --- a/panda/src/gobj/transformTable.h +++ b/panda/src/gobj/transformTable.h @@ -81,6 +81,9 @@ private: virtual CycleData *make_copy() const; virtual void write_datagram(BamWriter *manager, Datagram &dg) const; virtual void fillin(DatagramIterator &scan, BamReader *manager); + virtual TypeHandle get_parent_type() const { + return TransformTable::get_class_type(); + } UpdateSeq _modified; }; diff --git a/panda/src/gobj/userVertexSlider.h b/panda/src/gobj/userVertexSlider.h index 4b44114e6d..4786336c0b 100644 --- a/panda/src/gobj/userVertexSlider.h +++ b/panda/src/gobj/userVertexSlider.h @@ -50,6 +50,9 @@ private: virtual CycleData *make_copy() const; virtual void write_datagram(BamWriter *manager, Datagram &dg) const; virtual void fillin(DatagramIterator &scan, BamReader *manager); + virtual TypeHandle get_parent_type() const { + return UserVertexSlider::get_class_type(); + } float _slider; }; diff --git a/panda/src/gobj/userVertexTransform.h b/panda/src/gobj/userVertexTransform.h index deee1dce38..bbc94e9590 100644 --- a/panda/src/gobj/userVertexTransform.h +++ b/panda/src/gobj/userVertexTransform.h @@ -55,6 +55,9 @@ private: virtual CycleData *make_copy() const; virtual void write_datagram(BamWriter *manager, Datagram &dg) const; virtual void fillin(DatagramIterator &scan, BamReader *manager); + virtual TypeHandle get_parent_type() const { + return UserVertexTransform::get_class_type(); + } LMatrix4f _matrix; }; diff --git a/panda/src/gobj/vertexSlider.h b/panda/src/gobj/vertexSlider.h index 14e85a74c8..10a76806a2 100644 --- a/panda/src/gobj/vertexSlider.h +++ b/panda/src/gobj/vertexSlider.h @@ -74,6 +74,9 @@ private: virtual void write_datagram(BamWriter *manager, Datagram &dg) const; virtual int complete_pointers(TypedWritable **plist, BamReader *manager); virtual void fillin(DatagramIterator &scan, BamReader *manager); + virtual TypeHandle get_parent_type() const { + return VertexSlider::get_class_type(); + } UpdateSeq _modified; }; diff --git a/panda/src/gobj/vertexTransform.h b/panda/src/gobj/vertexTransform.h index bbf6c45dc8..6207845116 100644 --- a/panda/src/gobj/vertexTransform.h +++ b/panda/src/gobj/vertexTransform.h @@ -73,6 +73,9 @@ private: virtual void write_datagram(BamWriter *manager, Datagram &dg) const; virtual int complete_pointers(TypedWritable **plist, BamReader *manager); virtual void fillin(DatagramIterator &scan, BamReader *manager); + virtual TypeHandle get_parent_type() const { + return VertexTransform::get_class_type(); + } UpdateSeq _modified; }; diff --git a/panda/src/parametrics/ropeNode.h b/panda/src/parametrics/ropeNode.h index 4d0a8ed753..6649a01d3a 100644 --- a/panda/src/parametrics/ropeNode.h +++ b/panda/src/parametrics/ropeNode.h @@ -197,6 +197,9 @@ private: virtual CycleData *make_copy() const; virtual void write_datagram(BamWriter *manager, Datagram &dg) const; virtual void fillin(DatagramIterator &scan, BamReader *manager); + virtual TypeHandle get_parent_type() const { + return RopeNode::get_class_type(); + } PT(NurbsCurveEvaluator) _curve; RenderMode _render_mode; diff --git a/panda/src/parametrics/sheetNode.h b/panda/src/parametrics/sheetNode.h index 9d9e207a67..3b81c353b3 100644 --- a/panda/src/parametrics/sheetNode.h +++ b/panda/src/parametrics/sheetNode.h @@ -84,6 +84,9 @@ private: virtual CycleData *make_copy() const; virtual void write_datagram(BamWriter *manager, Datagram &dg) const; virtual void fillin(DatagramIterator &scan, BamReader *manager); + virtual TypeHandle get_parent_type() const { + return SheetNode::get_class_type(); + } PT(NurbsSurfaceEvaluator) _surface; bool _use_vertex_color; diff --git a/panda/src/pgraph/directionalLight.h b/panda/src/pgraph/directionalLight.h index ad5ac27763..be652ec044 100644 --- a/panda/src/pgraph/directionalLight.h +++ b/panda/src/pgraph/directionalLight.h @@ -69,6 +69,9 @@ private: virtual CycleData *make_copy() const; virtual void write_datagram(BamWriter *manager, Datagram &dg) const; virtual void fillin(DatagramIterator &scan, BamReader *manager); + virtual TypeHandle get_parent_type() const { + return DirectionalLight::get_class_type(); + } Colorf _specular_color; LPoint3f _point; diff --git a/panda/src/pgraph/geomNode.h b/panda/src/pgraph/geomNode.h index 843618560d..deff7c9aa7 100644 --- a/panda/src/pgraph/geomNode.h +++ b/panda/src/pgraph/geomNode.h @@ -111,6 +111,9 @@ private: virtual void write_datagram(BamWriter *manager, Datagram &dg) const; virtual int complete_pointers(TypedWritable **plist, BamReader *manager); virtual void fillin(DatagramIterator &scan, BamReader *manager); + virtual TypeHandle get_parent_type() const { + return GeomNode::get_class_type(); + } Geoms _geoms; }; diff --git a/panda/src/pgraph/light.h b/panda/src/pgraph/light.h index 294deb3984..23b3939ffb 100644 --- a/panda/src/pgraph/light.h +++ b/panda/src/pgraph/light.h @@ -106,6 +106,9 @@ private: virtual CycleData *make_copy() const; virtual void write_datagram(BamWriter *manager, Datagram &dg) const; virtual void fillin(DatagramIterator &scan, BamReader *manager); + virtual TypeHandle get_parent_type() const { + return Light::get_class_type(); + } Colorf _color; diff --git a/panda/src/pgraph/lodNode.h b/panda/src/pgraph/lodNode.h index 8a01bf5992..e4fa1917b8 100644 --- a/panda/src/pgraph/lodNode.h +++ b/panda/src/pgraph/lodNode.h @@ -104,6 +104,9 @@ protected: virtual void write_datagram(BamWriter *manager, Datagram &dg) const; virtual void fillin(DatagramIterator &scan, BamReader *manager); + virtual TypeHandle get_parent_type() const { + return LODNode::get_class_type(); + } LPoint3f _center; SwitchVector _switch_vector; diff --git a/panda/src/pgraph/nodePathComponent.h b/panda/src/pgraph/nodePathComponent.h index fcb4813bf2..3c44673608 100644 --- a/panda/src/pgraph/nodePathComponent.h +++ b/panda/src/pgraph/nodePathComponent.h @@ -82,6 +82,9 @@ private: INLINE CData(); CData(const CData ©); virtual CycleData *make_copy() const; + virtual TypeHandle get_parent_type() const { + return NodePathComponent::get_class_type(); + } PT(NodePathComponent) _next; int _length; diff --git a/panda/src/pgraph/pandaNode.h b/panda/src/pgraph/pandaNode.h index 13ca2e51a5..88b0c1f57d 100644 --- a/panda/src/pgraph/pandaNode.h +++ b/panda/src/pgraph/pandaNode.h @@ -322,6 +322,9 @@ private: virtual void write_datagram(BamWriter *manager, Datagram &dg) const; virtual int complete_pointers(TypedWritable **plist, BamReader *manager); virtual void fillin(DatagramIterator &scan, BamReader *manager); + virtual TypeHandle get_parent_type() const { + return PandaNode::get_class_type(); + } #ifdef HAVE_PYTHON void inc_py_refs(); diff --git a/panda/src/pgraph/planeNode.h b/panda/src/pgraph/planeNode.h index a13f0df91a..4d72097252 100644 --- a/panda/src/pgraph/planeNode.h +++ b/panda/src/pgraph/planeNode.h @@ -70,6 +70,9 @@ private: virtual CycleData *make_copy() const; virtual void write_datagram(BamWriter *manager, Datagram &dg) const; virtual void fillin(DatagramIterator &scan, BamReader *manager); + virtual TypeHandle get_parent_type() const { + return PlaneNode::get_class_type(); + } Planef _plane; }; diff --git a/panda/src/pgraph/pointLight.h b/panda/src/pgraph/pointLight.h index 1fa21fa952..a270a37c23 100644 --- a/panda/src/pgraph/pointLight.h +++ b/panda/src/pgraph/pointLight.h @@ -69,6 +69,9 @@ private: virtual CycleData *make_copy() const; virtual void write_datagram(BamWriter *manager, Datagram &dg) const; virtual void fillin(DatagramIterator &scan, BamReader *manager); + virtual TypeHandle get_parent_type() const { + return PointLight::get_class_type(); + } Colorf _specular_color; LVecBase3f _attenuation; diff --git a/panda/src/pgraph/spotlight.h b/panda/src/pgraph/spotlight.h index 518afd902c..53d4586d8c 100644 --- a/panda/src/pgraph/spotlight.h +++ b/panda/src/pgraph/spotlight.h @@ -89,6 +89,9 @@ private: virtual CycleData *make_copy() const; virtual void write_datagram(BamWriter *manager, Datagram &dg) const; virtual void fillin(DatagramIterator &scan, BamReader *manager); + virtual TypeHandle get_parent_type() const { + return Spotlight::get_class_type(); + } float _exponent; Colorf _specular_color; diff --git a/panda/src/pgraph/switchNode.h b/panda/src/pgraph/switchNode.h index ce2c4b37e6..27b1ea7fa1 100644 --- a/panda/src/pgraph/switchNode.h +++ b/panda/src/pgraph/switchNode.h @@ -54,6 +54,9 @@ private: virtual CycleData *make_copy() const; virtual void write_datagram(BamWriter *manager, Datagram &dg) const; virtual void fillin(DatagramIterator &scan, BamReader *manager); + virtual TypeHandle get_parent_type() const { + return SwitchNode::get_class_type(); + } int _visible_child; }; diff --git a/panda/src/putil/animInterface.h b/panda/src/putil/animInterface.h index c4debed60c..88659dfe0a 100644 --- a/panda/src/putil/animInterface.h +++ b/panda/src/putil/animInterface.h @@ -95,6 +95,9 @@ private: virtual CycleData *make_copy() const; virtual void write_datagram(BamWriter *manager, Datagram &dg) const; virtual void fillin(DatagramIterator &scan, BamReader *manager); + virtual TypeHandle get_parent_type() const { + return AnimInterface::get_class_type(); + } void play(double from, double to); void loop(bool restart, double from, double to); diff --git a/panda/src/putil/cycleData.cxx b/panda/src/putil/cycleData.cxx index 7b3f5f2737..5eda716b0d 100644 --- a/panda/src/putil/cycleData.cxx +++ b/panda/src/putil/cycleData.cxx @@ -85,3 +85,25 @@ void CycleData:: fillin(DatagramIterator &, BamReader *, void *) { } +//////////////////////////////////////////////////////////////////// +// Function: CycleData::get_parent_type +// Access: Public, Virtual +// Description: Returns the type of the container that owns the +// CycleData. This is useful mainly for debugging. +//////////////////////////////////////////////////////////////////// +TypeHandle CycleData:: +get_parent_type() const { + return TypeHandle::none(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CycleData::output +// Access: Public, Virtual +// Description: Formats the contents of the CycleData in some +// meaningful way for humans. This is useful mainly for +// debugging. +//////////////////////////////////////////////////////////////////// +void CycleData:: +output(ostream &out) const { + out << get_parent_type() << "::CData"; +} diff --git a/panda/src/putil/cycleData.h b/panda/src/putil/cycleData.h index 2e0b536c20..45d1cc814a 100644 --- a/panda/src/putil/cycleData.h +++ b/panda/src/putil/cycleData.h @@ -20,7 +20,7 @@ #define CYCLEDATA_H #include "pandabase.h" - +#include "typeHandle.h" #include "referenceCount.h" class BamWriter; @@ -64,8 +64,17 @@ public: virtual void fillin(DatagramIterator &scan, BamReader *manager); virtual void fillin(DatagramIterator &scan, BamReader *manager, void *extra_data); + + virtual TypeHandle get_parent_type() const; + virtual void output(ostream &out) const; }; +INLINE ostream & +operator << (ostream &out, const CycleData &cd) { + cd.output(out); + return out; +} + #include "cycleData.I" #endif diff --git a/panda/src/putil/pipeline.I b/panda/src/putil/pipeline.I index e60a56f89f..6c2729d6ca 100644 --- a/panda/src/putil/pipeline.I +++ b/panda/src/putil/pipeline.I @@ -29,3 +29,14 @@ 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())); +} diff --git a/panda/src/putil/pipeline.cxx b/panda/src/putil/pipeline.cxx index 59866d67a9..749da8863c 100644 --- a/panda/src/putil/pipeline.cxx +++ b/panda/src/putil/pipeline.cxx @@ -18,6 +18,7 @@ #include "pipeline.h" #include "pipelineCyclerTrueImpl.h" +#include "mutexHolder.h" Pipeline *Pipeline::_render_pipeline = (Pipeline *)NULL; @@ -53,6 +54,7 @@ Pipeline:: void Pipeline:: cycle() { #if defined(DO_PIPELINING) && defined(HAVE_THREADS) + MutexHolder holder(_lock); Cyclers::iterator ci; for (ci = _cyclers.begin(); ci != _cyclers.end(); ++ci) { (*ci)->cycle(); @@ -69,16 +71,32 @@ cycle() { void Pipeline:: set_num_stages(int num_stages) { nassertv(num_stages >= 1); - if (num_stages != _num_stages) { - _num_stages = num_stages; - #if defined(DO_PIPELINING) && defined(HAVE_THREADS) + MutexHolder 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); } -#endif // DO_PIPELINING && HAVE_THREADS + + // Now release them all. + for (ci = _cyclers.begin(); ci != _cyclers.end(); ++ci) { + (*ci)->_lock.release(); + } } + +#else // DO_PIPELINING && HAVE_THREADS + _num_stages = num_stages; +#endif // DO_PIPELINING && HAVE_THREADS } //////////////////////////////////////////////////////////////////// @@ -103,6 +121,7 @@ get_num_stages() const { //////////////////////////////////////////////////////////////////// void Pipeline:: add_cycler(PipelineCyclerTrueImpl *cycler) { + MutexHolder holder(_lock); bool inserted = _cyclers.insert(cycler).second; nassertv(inserted); } @@ -118,6 +137,7 @@ add_cycler(PipelineCyclerTrueImpl *cycler) { //////////////////////////////////////////////////////////////////// void Pipeline:: remove_cycler(PipelineCyclerTrueImpl *cycler) { + MutexHolder holder(_lock); Cyclers::iterator ci = _cyclers.find(cycler); nassertv(ci != _cyclers.end()); _cyclers.erase(ci); diff --git a/panda/src/putil/pipeline.h b/panda/src/putil/pipeline.h index b930b1145e..129e1f7f54 100644 --- a/panda/src/putil/pipeline.h +++ b/panda/src/putil/pipeline.h @@ -22,6 +22,7 @@ #include "pandabase.h" #include "namable.h" #include "pset.h" +#include "pmutex.h" class PipelineCyclerTrueImpl; @@ -48,6 +49,7 @@ public: void cycle(); void set_num_stages(int num_stages); + INLINE void set_min_stages(int min_stages); int get_num_stages() const; #if defined(DO_PIPELINING) && defined(HAVE_THREADS) @@ -64,6 +66,8 @@ private: #if defined(DO_PIPELINING) && defined(HAVE_THREADS) typedef pset Cyclers; Cyclers _cyclers; + + Mutex _lock; #endif // DO_PIPELINING && HAVE_THREADS }; diff --git a/panda/src/putil/pipelineCyclerTrueImpl.I b/panda/src/putil/pipelineCyclerTrueImpl.I index d91bca6227..c57b3ed356 100644 --- a/panda/src/putil/pipelineCyclerTrueImpl.I +++ b/panda/src/putil/pipelineCyclerTrueImpl.I @@ -32,6 +32,7 @@ INLINE const CycleData *PipelineCyclerTrueImpl:: read() const { int pipeline_stage = Thread::get_current_thread()->get_pipeline_stage(); nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL); + _lock.lock(); return _data[pipeline_stage]._cycle_data; } @@ -44,6 +45,12 @@ read() const { //////////////////////////////////////////////////////////////////// INLINE void PipelineCyclerTrueImpl:: increment_read(const CycleData *pointer) const { +#ifndef NDEBUG + int pipeline_stage = Thread::get_current_thread()->get_pipeline_stage(); + nassertv(pipeline_stage >= 0 && pipeline_stage < _num_stages); + nassertv(_data[pipeline_stage]._cycle_data == pointer); +#endif + _lock.lock(); } //////////////////////////////////////////////////////////////////// @@ -54,6 +61,12 @@ increment_read(const CycleData *pointer) const { //////////////////////////////////////////////////////////////////// INLINE void PipelineCyclerTrueImpl:: release_read(const CycleData *pointer) const { +#ifndef NDEBUG + int pipeline_stage = Thread::get_current_thread()->get_pipeline_stage(); + nassertv(pipeline_stage >= 0 && pipeline_stage < _num_stages); + nassertv(_data[pipeline_stage]._cycle_data == pointer); +#endif + _lock.release(); } //////////////////////////////////////////////////////////////////// @@ -75,8 +88,7 @@ release_read(const CycleData *pointer) const { INLINE CycleData *PipelineCyclerTrueImpl:: write() { int pipeline_stage = Thread::get_current_thread()->get_pipeline_stage(); - nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL); - return _data[pipeline_stage]._cycle_data; + return write_stage(pipeline_stage); } //////////////////////////////////////////////////////////////////// @@ -90,7 +102,9 @@ write() { //////////////////////////////////////////////////////////////////// INLINE CycleData *PipelineCyclerTrueImpl:: elevate_read(const CycleData *pointer) { - return (CycleData *)pointer; + CycleData *new_pointer = write(); + _lock.release(); + return new_pointer; } //////////////////////////////////////////////////////////////////// @@ -101,6 +115,12 @@ elevate_read(const CycleData *pointer) { //////////////////////////////////////////////////////////////////// INLINE void PipelineCyclerTrueImpl:: release_write(CycleData *pointer) { +#ifdef NDEBUG + int pipeline_stage = Thread::get_current_thread()->get_pipeline_stage(); + return release_write_stage(pipeline_stage); +#else + _lock.release(); +#endif } //////////////////////////////////////////////////////////////////// @@ -123,23 +143,11 @@ get_num_stages() { INLINE bool PipelineCyclerTrueImpl:: is_stage_unique(int n) const { nassertr(n >= 0 && n < _num_stages, false); - return true; -} - -//////////////////////////////////////////////////////////////////// -// Function: PipelineCyclerTrueImpl::write_stage -// Access: Public -// Description: Returns a pointer suitable for writing to the nth -// stage of the pipeline. This is for special -// applications that need to update the entire pipeline -// at once (for instance, to remove an invalid pointer). -// This pointer should later be released with -// release_write_stage(). -//////////////////////////////////////////////////////////////////// -INLINE CycleData *PipelineCyclerTrueImpl:: -write_stage(int n) { - nassertr(n >= 0 && n < _num_stages, (CycleData *)NULL); - return _data[n]._cycle_data; + if (n == 0) { + return true; + } else { + return _data[n]._cycle_data == _data[n - 1]._cycle_data; + } } //////////////////////////////////////////////////////////////////// @@ -150,7 +158,11 @@ write_stage(int n) { //////////////////////////////////////////////////////////////////// INLINE void PipelineCyclerTrueImpl:: release_write_stage(int n, CycleData *pointer) { +#ifndef NDEBUG nassertv(n >= 0 && n < _num_stages); + nassertv(_data[n]._cycle_data == pointer); +#endif + _lock.release(); } //////////////////////////////////////////////////////////////////// @@ -179,7 +191,10 @@ cheat() const { //////////////////////////////////////////////////////////////////// INLINE int PipelineCyclerTrueImpl:: get_read_count() const { - return 0; + int pipeline_stage = Thread::get_current_thread()->get_pipeline_stage(); + nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, 0); + _lock.lock(); + return _lock.get_lock_count(); } //////////////////////////////////////////////////////////////////// @@ -192,5 +207,8 @@ get_read_count() const { //////////////////////////////////////////////////////////////////// INLINE int PipelineCyclerTrueImpl:: get_write_count() const { - return 0; + int pipeline_stage = Thread::get_current_thread()->get_pipeline_stage(); + nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, 0); + _lock.lock(); + return _lock.get_lock_count(); } diff --git a/panda/src/putil/pipelineCyclerTrueImpl.cxx b/panda/src/putil/pipelineCyclerTrueImpl.cxx index 514dfb01d7..288a7beae8 100644 --- a/panda/src/putil/pipelineCyclerTrueImpl.cxx +++ b/panda/src/putil/pipelineCyclerTrueImpl.cxx @@ -20,7 +20,9 @@ #if defined(DO_PIPELINING) && defined(HAVE_THREADS) +#include "config_util.h" #include "pipeline.h" +#include "reMutexHolder.h" //////////////////////////////////////////////////////////////////// @@ -40,7 +42,7 @@ PipelineCyclerTrueImpl(CycleData *initial_data, Pipeline *pipeline) : _num_stages = _pipeline->get_num_stages(); _data = new StageData[_num_stages]; for (int i = 0; i < _num_stages; ++i) { - _data[i]._cycle_data = initial_data->make_copy(); + _data[i]._cycle_data = initial_data; } } @@ -55,6 +57,7 @@ PipelineCyclerTrueImpl(const PipelineCyclerTrueImpl ©) : { _pipeline->add_cycler(this); + ReMutexHolder holder(copy._lock); _num_stages = _pipeline->get_num_stages(); nassertv(_num_stages == copy._num_stages); _data = new StageData[_num_stages]; @@ -70,9 +73,12 @@ PipelineCyclerTrueImpl(const PipelineCyclerTrueImpl ©) : //////////////////////////////////////////////////////////////////// void PipelineCyclerTrueImpl:: operator = (const PipelineCyclerTrueImpl ©) { + ReMutexHolder holder1(_lock); + ReMutexHolder holder2(copy._lock); + nassertv(_num_stages == copy._num_stages); for (int i = 0; i < _num_stages; ++i) { - _data[i]._cycle_data = copy._data[i]._cycle_data->make_copy(); + _data[i]._cycle_data = copy._data[i]._cycle_data; } } @@ -83,11 +89,77 @@ operator = (const PipelineCyclerTrueImpl ©) { //////////////////////////////////////////////////////////////////// PipelineCyclerTrueImpl:: ~PipelineCyclerTrueImpl() { + ReMutexHolder holder(_lock); _pipeline->remove_cycler(this); delete[] _data; } +//////////////////////////////////////////////////////////////////// +// Function: PipelineCyclerTrueImpl::write_stage +// Access: Public +// Description: Returns a pointer suitable for writing to the nth +// stage of the pipeline. This is for special +// applications that need to update the entire pipeline +// at once (for instance, to remove an invalid pointer). +// This pointer should later be released with +// release_write_stage(). +//////////////////////////////////////////////////////////////////// +CycleData *PipelineCyclerTrueImpl:: +write_stage(int n) { + _lock.lock(); + +#ifndef NDEBUG + nassertd(n >= 0 && n < _num_stages) { + _lock.release(); + return NULL; + } +#endif // NDEBUG + + CycleData *old_data = _data[n]._cycle_data; + + if (old_data->get_ref_count() != 1) { + // Copy-on-write. + + // There's a special problem that happens when we write to a stage + // other than stage 0. If we do this, when the next frame cycles, + // the changes that we record to stage n will be lost when the + // data from stage (n - 1) is cycled into place. This can be + // wasteful, especially if we are updating a cached value (which + // is generally the case when we are writing to stages other than + // stage 0). + + // To minimize this, we make a special exception: whenever we + // write to stage n, if stage (n - 1) has the same pointer, we + // will write to stage (n - 1) at the same time, and so on all the + // way back to stage 0 or the last different stage. + + // On the other hand, if *all* of the instances of this pointer + // are found in stages k .. n, then we don't need to do anything + // at all. + int count = old_data->get_ref_count() - 1; + int k = n - 1; + while (k >= 0 && _data[k]._cycle_data == old_data) { + --k; + --count; + } + + if (count > 0) { + PT(CycleData) new_data = old_data->make_copy(); + + int k = n - 1; + while (k >= 0 && _data[k]._cycle_data == old_data) { + _data[k]._cycle_data = new_data; + --k; + } + + _data[n]._cycle_data = new_data; + } + } + + return _data[n]._cycle_data; +} + //////////////////////////////////////////////////////////////////// // Function: PipelineCyclerTrueImpl::cycle // Access: Private @@ -96,10 +168,11 @@ PipelineCyclerTrueImpl:: //////////////////////////////////////////////////////////////////// void PipelineCyclerTrueImpl:: cycle() { + ReMutexHolder holder(_lock); + for (int i = _num_stages - 1; i > 0; --i) { _data[i] = _data[i - 1]; } - _data[0]._cycle_data = _data[0]._cycle_data->make_copy(); } //////////////////////////////////////////////////////////////////// @@ -110,8 +183,11 @@ cycle() { //////////////////////////////////////////////////////////////////// void PipelineCyclerTrueImpl:: set_num_stages(int num_stages) { + nassertv(_lock.debug_is_locked()); + if (num_stages <= _num_stages) { - // Don't bother to reallocate the array. + // Don't bother to reallocate the array smaller; we just won't use + // the rest of the array. for (int i = _num_stages; i < num_stages; ++i) { _data[i]._cycle_data.clear(); } @@ -120,14 +196,14 @@ set_num_stages(int num_stages) { } else { - // Increase the array. + // To increase the array, we must reallocate it larger. StageData *new_data = new StageData[num_stages]; int i; for (i = 0; i < _num_stages; ++i) { new_data[i] = _data[i]; } for (i = _num_stages; i < num_stages; ++i) { - new_data[i]._cycle_data = _data[_num_stages - 1]._cycle_data->make_copy(); + new_data[i]._cycle_data = _data[_num_stages - 1]._cycle_data; } delete[] _data; diff --git a/panda/src/putil/pipelineCyclerTrueImpl.h b/panda/src/putil/pipelineCyclerTrueImpl.h index 24c1111986..69ccf26e2d 100644 --- a/panda/src/putil/pipelineCyclerTrueImpl.h +++ b/panda/src/putil/pipelineCyclerTrueImpl.h @@ -26,6 +26,7 @@ #include "cycleData.h" #include "pointerTo.h" #include "thread.h" +#include "reMutex.h" class Pipeline; @@ -61,7 +62,7 @@ public: INLINE int get_num_stages(); INLINE bool is_stage_unique(int n) const; - INLINE CycleData *write_stage(int n); + CycleData *write_stage(int n); INLINE void release_write_stage(int n, CycleData *pointer); INLINE CycleData *cheat() const; @@ -83,6 +84,8 @@ private: StageData *_data; int _num_stages; + ReMutex _lock; + friend class Pipeline; };