diff --git a/panda/src/display/graphicsStateGuardian.I b/panda/src/display/graphicsStateGuardian.I index 134c086677..de95fe96c7 100644 --- a/panda/src/display/graphicsStateGuardian.I +++ b/panda/src/display/graphicsStateGuardian.I @@ -62,6 +62,28 @@ release_all_geoms() { return _prepared_objects->release_all_geoms(); } +//////////////////////////////////////////////////////////////////// +// Function: GraphicsStateGuardian::release_all_vertex_buffers +// Access: Public +// Description: Frees the resources for all vertex buffers associated +// with this GSG. +//////////////////////////////////////////////////////////////////// +INLINE int GraphicsStateGuardian:: +release_all_vertex_buffers() { + return _prepared_objects->release_all_vertex_buffers(); +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsStateGuardian::release_all_index_buffers +// Access: Public +// Description: Frees the resources for all index buffers associated +// with this GSG. +//////////////////////////////////////////////////////////////////// +INLINE int GraphicsStateGuardian:: +release_all_index_buffers() { + return _prepared_objects->release_all_index_buffers(); +} + //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::set_active // Access: Published diff --git a/panda/src/display/graphicsStateGuardian.cxx b/panda/src/display/graphicsStateGuardian.cxx index c2b4d40a4e..4c2091e792 100644 --- a/panda/src/display/graphicsStateGuardian.cxx +++ b/panda/src/display/graphicsStateGuardian.cxx @@ -1511,6 +1511,8 @@ close_gsg() { if (_prepared_objects->get_ref_count() == 1) { release_all_textures(); release_all_geoms(); + release_all_vertex_buffers(); + release_all_index_buffers(); } } diff --git a/panda/src/display/graphicsStateGuardian.h b/panda/src/display/graphicsStateGuardian.h index 8ca0327162..143c0688d1 100644 --- a/panda/src/display/graphicsStateGuardian.h +++ b/panda/src/display/graphicsStateGuardian.h @@ -74,6 +74,8 @@ public: PUBLISHED: INLINE int release_all_textures(); INLINE int release_all_geoms(); + INLINE int release_all_vertex_buffers(); + INLINE int release_all_index_buffers(); INLINE void set_active(bool active); INLINE bool is_active() const; diff --git a/panda/src/display/graphicsThreadingModel.cxx b/panda/src/display/graphicsThreadingModel.cxx index 4d1136aa66..6b05b12579 100644 --- a/panda/src/display/graphicsThreadingModel.cxx +++ b/panda/src/display/graphicsThreadingModel.cxx @@ -59,7 +59,7 @@ GraphicsThreadingModel(const string &model) { size_t slash = model.find('/', start); if (slash == string::npos) { - _cull_name = model; + _cull_name = model.substr(start); } else { _cull_name = model.substr(start, slash - start); _draw_name = model.substr(slash + 1); diff --git a/panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx b/panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx index e903d2c091..3e65b405c3 100644 --- a/panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx +++ b/panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx @@ -3197,44 +3197,51 @@ release_texture(TextureContext *tc) { VertexBufferContext *DXGraphicsStateGuardian8:: prepare_vertex_buffer(qpGeomVertexArrayData *data) { DXVertexBufferContext8 *dvbc = new DXVertexBufferContext8(data); - - if (vertex_buffers && data->get_usage_hint() != qpGeom::UH_client) { - dvbc->create_vbuffer(*_pScrn); - - if (dxgsg8_cat.is_debug()) { - dxgsg8_cat.debug() - << "creating vertex buffer " << dvbc->_vbuffer << ": " - << data->get_num_rows() << " vertices " - << *data->get_array_format() << "\n"; - } - } - return dvbc; } //////////////////////////////////////////////////////////////////// // Function: DXGraphicsStateGuardian8::apply_vertex_buffer // Access: Public -// Description: Makes the data the currently available data for -// rendering. +// Description: Updates the vertex buffer with the current data, and +// makes it the current vertex buffer for rendering. //////////////////////////////////////////////////////////////////// void DXGraphicsStateGuardian8:: apply_vertex_buffer(VertexBufferContext *vbc) { DXVertexBufferContext8 *dvbc = DCAST(DXVertexBufferContext8, vbc); - if (dvbc->_vbuffer != NULL) { + if (dvbc->_vbuffer == NULL) { + // Attempt to create a new vertex buffer. + if (vertex_buffers && + dvbc->get_data()->get_usage_hint() != qpGeom::UH_client) { + dvbc->create_vbuffer(*_pScrn); + } + + if (dvbc->_vbuffer != NULL) { + dvbc->upload_data(); + + add_to_vertex_buffer_record(dvbc); + add_to_total_buffer_record(dvbc); + dvbc->mark_loaded(); + + _pD3DDevice->SetStreamSource + (0, dvbc->_vbuffer, dvbc->get_data()->get_array_format()->get_stride()); + _vbuffer_active = true; + } else { + _vbuffer_active = false; + } + + } else { add_to_vertex_buffer_record(dvbc); if (dvbc->was_modified()) { if (dvbc->changed_size()) { - // Here we have to destroy the old vertex buffer and create a - // new one. + // We have to destroy the old vertex buffer and create a new + // one. dvbc->create_vbuffer(*_pScrn); - - } else { - // Here we just copy the new data to the vertex buffer. - dvbc->upload_data(); } + + dvbc->upload_data(); add_to_total_buffer_record(dvbc); dvbc->mark_loaded(); @@ -3243,9 +3250,6 @@ apply_vertex_buffer(VertexBufferContext *vbc) { _pD3DDevice->SetStreamSource (0, dvbc->_vbuffer, dvbc->get_data()->get_array_format()->get_stride()); _vbuffer_active = true; - - } else { - _vbuffer_active = false; } set_vertex_format(dvbc->_fvf); @@ -3281,43 +3285,48 @@ release_vertex_buffer(VertexBufferContext *vbc) { IndexBufferContext *DXGraphicsStateGuardian8:: prepare_index_buffer(qpGeomPrimitive *data) { DXIndexBufferContext8 *dibc = new DXIndexBufferContext8(data); - - dibc->create_ibuffer(*_pScrn); - - if (dxgsg8_cat.is_debug()) { - dxgsg8_cat.debug() - << "creating index buffer " << dibc->_ibuffer << ": " - << data->get_num_vertices() << " indices (" - << data->get_vertices()->get_array_format()->get_column(0)->get_numeric_type() - << ")\n"; - } - return dibc; } //////////////////////////////////////////////////////////////////// // Function: DXGraphicsStateGuardian8::apply_index_buffer // Access: Public -// Description: Makes the data the currently available data for -// rendering. +// Description: Updates the index buffer with the current data, and +// makes it the current index buffer for rendering. //////////////////////////////////////////////////////////////////// void DXGraphicsStateGuardian8:: apply_index_buffer(IndexBufferContext *ibc) { DXIndexBufferContext8 *dibc = DCAST(DXIndexBufferContext8, ibc); - if (dibc->_ibuffer != NULL) { + if (dibc->_ibuffer == NULL) { + // Attempt to create a new index buffer. + dibc->create_ibuffer(*_pScrn); + + if (dibc->_ibuffer != NULL) { + dibc->upload_data(); + add_to_index_buffer_record(dibc); + add_to_total_buffer_record(dibc); + dibc->mark_loaded(); + + _pD3DDevice->SetIndices(dibc->_ibuffer, 0); + _ibuffer_active = true; + } else { + + _pD3DDevice->SetIndices(NULL, 0); + _ibuffer_active = false; + } + + } else { add_to_index_buffer_record(dibc); if (dibc->was_modified()) { if (dibc->changed_size()) { - // Here we have to destroy the old index buffer and create a - // new one. + // We have to destroy the old index buffer and create a new + // one. dibc->create_ibuffer(*_pScrn); - - } else { - // Here we just copy the new data to the index buffer. - dibc->upload_data(); } + + dibc->upload_data(); add_to_total_buffer_record(dibc); dibc->mark_loaded(); @@ -3325,10 +3334,6 @@ apply_index_buffer(IndexBufferContext *ibc) { _pD3DDevice->SetIndices(dibc->_ibuffer, 0); _ibuffer_active = true; - - } else { - _pD3DDevice->SetIndices(NULL, 0); - _ibuffer_active = false; } } diff --git a/panda/src/dxgsg8/dxIndexBufferContext8.cxx b/panda/src/dxgsg8/dxIndexBufferContext8.cxx index 7d1dc0b1a9..d7f6d14e1b 100644 --- a/panda/src/dxgsg8/dxIndexBufferContext8.cxx +++ b/panda/src/dxgsg8/dxIndexBufferContext8.cxx @@ -58,7 +58,8 @@ DXIndexBufferContext8:: //////////////////////////////////////////////////////////////////// // Function: DXIndexBufferContext8::create_ibuffer // Access: Public -// Description: Creates a new index buffer and uploads data to it. +// Description: Creates a new index buffer (but does not upload data +// to it). //////////////////////////////////////////////////////////////////// void DXIndexBufferContext8:: create_ibuffer(DXScreenData &scrn) { @@ -77,9 +78,14 @@ create_ibuffer(DXScreenData &scrn) { dxgsg8_cat.warning() << "CreateIndexBuffer failed" << D3DERRORSTRING(hr); _ibuffer = NULL; - } else { - upload_data(); + if (dxgsg8_cat.is_debug()) { + dxgsg8_cat.debug() + << "creating index buffer " << _ibuffer << ": " + << get_data()->get_num_vertices() << " indices (" + << get_data()->get_vertices()->get_array_format()->get_column(0)->get_numeric_type() + << ")\n"; + } } } diff --git a/panda/src/dxgsg8/dxVertexBufferContext8.cxx b/panda/src/dxgsg8/dxVertexBufferContext8.cxx index 5d0d312607..07c0c619e5 100644 --- a/panda/src/dxgsg8/dxVertexBufferContext8.cxx +++ b/panda/src/dxgsg8/dxVertexBufferContext8.cxx @@ -153,7 +153,8 @@ DXVertexBufferContext8:: //////////////////////////////////////////////////////////////////// // Function: DXVertexBufferContext8::create_vbuffer // Access: Public -// Description: Creates a new vertex buffer and uploads data to it. +// Description: Creates a new vertex buffer (but does not upload data +// to it). //////////////////////////////////////////////////////////////////// void DXVertexBufferContext8:: create_vbuffer(DXScreenData &scrn) { @@ -169,9 +170,13 @@ create_vbuffer(DXScreenData &scrn) { dxgsg8_cat.warning() << "CreateVertexBuffer failed" << D3DERRORSTRING(hr); _vbuffer = NULL; - } else { - upload_data(); + if (dxgsg8_cat.is_debug()) { + dxgsg8_cat.debug() + << "created vertex buffer " << _vbuffer << ": " + << get_data()->get_num_rows() << " vertices " + << *get_data()->get_array_format() << "\n"; + } } } diff --git a/panda/src/express/referenceCount.I b/panda/src/express/referenceCount.I index 0de88fbd05..54637f92b2 100644 --- a/panda/src/express/referenceCount.I +++ b/panda/src/express/referenceCount.I @@ -87,7 +87,7 @@ operator = (const ReferenceCount &) { // instance of a class that derives from ReferenceCount. Or maybe // your headers are out of sync, and you need to make clean in // direct or some higher tree. - nassertv(_ref_count != -100); + nassertv(_ref_count != deleted_ref_count); } //////////////////////////////////////////////////////////////////// @@ -112,7 +112,13 @@ INLINE ReferenceCount:: // automatic (local variable) instance of a class that derives from // ReferenceCount. Or maybe your headers are out of sync, and you // need to make clean in direct or some higher tree. - nassertv(_ref_count != -100); + nassertv(_ref_count != deleted_ref_count); + + // If this assertion fails, we're trying to delete a static object + // that still has an outstanding reference count. You should make + // sure that all references to your static objects are gone by the + // time the object itself destructs. + nassertv(_ref_count <= local_ref_count); // If this assertion fails, the reference counts are all screwed // up altogether. Maybe some errant code stomped all over memory @@ -129,7 +135,7 @@ INLINE ReferenceCount:: // constructor for a ReferenceCount object, and then bitwise // copied a dynamically allocated value--reference count and // all--onto a locally allocated one. - nassertv(_ref_count == 0); + nassertv(_ref_count == 0 || _ref_count == local_ref_count); // Tell our weak reference holders that we're going away now. if (_weak_list != (WeakReferenceList *)NULL) { @@ -138,10 +144,10 @@ INLINE ReferenceCount:: } #ifndef NDEBUG - // Ok, all clear to delete. Now set the reference count to -100, - // so we'll have a better chance of noticing if we happen to have - // a stray pointer to it still out there. - _ref_count = -100; + // Ok, all clear to delete. Now set the reference count to + // deleted_ref_count, so we'll have a better chance of noticing if + // we happen to have a stray pointer to it still out there. + _ref_count = deleted_ref_count; #endif #ifdef DO_MEMORY_USAGE @@ -151,7 +157,7 @@ INLINE ReferenceCount:: //////////////////////////////////////////////////////////////////// // Function: ReferenceCount::get_ref_count -// Access: Public +// Access: Published // Description: Returns the current reference count. //////////////////////////////////////////////////////////////////// INLINE int ReferenceCount:: @@ -164,7 +170,7 @@ get_ref_count() const { //////////////////////////////////////////////////////////////////// // Function: ReferenceCount::ref -// Access: Public +// Access: Published // Description: Explicitly increments the reference count. User code // should avoid using ref() and unref() directly, which // can result in missed reference counts. Instead, let @@ -191,7 +197,7 @@ ref() const { // automatic (local variable) instance of a class that derives from // ReferenceCount. Or maybe your headers are out of sync, and you // need to make clean in direct or some higher tree. - nassertr(_ref_count != -100, 0); + nassertr(_ref_count != deleted_ref_count, 0); // If this assertion fails, the reference counts are all screwed // up altogether. Maybe some errant code stomped all over memory @@ -203,7 +209,7 @@ ref() const { //////////////////////////////////////////////////////////////////// // Function: ReferenceCount::unref -// Access: Public +// Access: Published // Description: Explicitly decrements the reference count. Note that // the object will not be implicitly deleted by unref() // simply because the reference count drops to zero. @@ -237,7 +243,7 @@ unref() const { // automatic (local variable) instance of a class that derives from // ReferenceCount. Or maybe your headers are out of sync, and you // need to make clean in direct or some higher tree. - nassertr(_ref_count != -100, false); + nassertr(_ref_count != deleted_ref_count, false); // If this assertion fails, the reference counts are all screwed // up altogether. Maybe some errant code stomped all over memory @@ -254,7 +260,7 @@ unref() const { //////////////////////////////////////////////////////////////////// // Function: ReferenceCount::test_ref_count_integrity -// Access: Public +// Access: Published // Description: Does some easy checks to make sure that the reference // count isn't completely bogus. //////////////////////////////////////////////////////////////////// @@ -270,7 +276,7 @@ test_ref_count_integrity() const { // automatic (local variable) instance of a class that derives from // ReferenceCount. Or maybe your headers are out of sync, and you // need to make clean in direct or some higher tree. - nassertv(_ref_count != -100); + nassertv(_ref_count != deleted_ref_count); // If this assertion fails, the reference counts are all screwed // up altogether. Maybe some errant code stomped all over memory @@ -279,6 +285,30 @@ test_ref_count_integrity() const { #endif } +//////////////////////////////////////////////////////////////////// +// Function: ReferenceCount::local_object +// Access: Public +// Description: This function should be called, once, immediately +// after creating a new instance of some +// ReferenceCount-derived object on the stack. +// +// This allows the object to be passed to functions that +// will increment and decrement the object's reference +// count temporarily, and it will prevent the object +// from being deleted (inappropriately), when the +// reference count returns to zero. It actually +// achieves this by setting a large positive value in +// the reference count field. +//////////////////////////////////////////////////////////////////// +INLINE void ReferenceCount:: +local_object() { + // If this assertion fails, you didn't call this immediately after + // creating a local object. + nassertv(_ref_count == 0); + + _ref_count = local_ref_count; +} + //////////////////////////////////////////////////////////////////// // Function: ReferenceCount::has_weak_list // Access: Public diff --git a/panda/src/express/referenceCount.h b/panda/src/express/referenceCount.h index 4ca2b21291..e1d22e25e9 100644 --- a/panda/src/express/referenceCount.h +++ b/panda/src/express/referenceCount.h @@ -54,6 +54,7 @@ PUBLISHED: INLINE void test_ref_count_integrity() const; public: + INLINE void local_object(); INLINE bool has_weak_list() const; INLINE WeakReferenceList *get_weak_list() const; @@ -61,6 +62,22 @@ public: INLINE void weak_unref(WeakPointerToVoid *ptv); private: + enum { + // We use this value as a flag to indicate an object has been + // indicated as a local object, and should not be deleted except + // by its own destructor. Really, any nonzero value would do, but + // having a large specific number makes the sanity checks easier. + local_ref_count = 10000000, + + // This value is used as a flag to indicate that an object has + // just been deleted, and you're looking at deallocated memory. + // It's not guaranteed to stick around, of course (since the + // deleted memory might be repurposed for anything else, including + // a new object), but if you ever do encounter this value in a + // reference count field, you screwed up. + deleted_ref_count = -100, + }; + int _ref_count; WeakReferenceList *_weak_list; @@ -137,8 +154,6 @@ private: static TypeHandle _type_handle; }; - - #include "referenceCount.I" #endif diff --git a/panda/src/gobj/config_gobj.cxx b/panda/src/gobj/config_gobj.cxx index ddc7cf58be..1a1ff3c6a3 100644 --- a/panda/src/gobj/config_gobj.cxx +++ b/panda/src/gobj/config_gobj.cxx @@ -206,14 +206,19 @@ ConfigVariableString fake_texture_image "the same texture file, which will presumably only be loaded " "once.")); -ConfigVariableInt vertex_convert_cache -("vertex-convert-cache", 4194304, // 4 MB - PRC_DESC("This is the amount of memory, in bytes, that should be set " - "aside for storing pre-processed data for rendering vertices. " - "This is not a limit on the actual vertex data, which is " - "determined by the model; it is also not a limit on the " - "amount of memory used by the video driver or the system " - "graphics interface, which Panda has no control over.")); +ConfigVariableInt geom_cache_size +("geom-cache-size", 5000, + PRC_DESC("Specifies the maximum number of entries in the cache " + "for storing pre-processed data for rendering " + "vertices. This limit is flexible, and may be " + "temporarily exceeded if many different Geoms are " + "pre-processed during the space of a single frame.")); + +ConfigVariableInt geom_cache_min_frames +("geom-cache-min-frames", 1, + PRC_DESC("Specifies the minimum number of frames any one particular " + "object will remain in the geom cache, even if geom-cache-size " + "is exceeded.")); ConfigVariableDouble default_near ("default-near", 1.0, diff --git a/panda/src/gobj/config_gobj.h b/panda/src/gobj/config_gobj.h index 750d91a4f3..6fe930acb1 100644 --- a/panda/src/gobj/config_gobj.h +++ b/panda/src/gobj/config_gobj.h @@ -66,7 +66,8 @@ extern EXPCL_PANDA ConfigVariableEnum textures_power_2; extern EXPCL_PANDA ConfigVariableEnum textures_square; extern EXPCL_PANDA ConfigVariableString fake_texture_image; -extern EXPCL_PANDA ConfigVariableInt vertex_convert_cache; +extern EXPCL_PANDA ConfigVariableInt geom_cache_size; +extern EXPCL_PANDA ConfigVariableInt geom_cache_min_frames; extern ConfigVariableDouble default_near; extern ConfigVariableDouble default_far; diff --git a/panda/src/gobj/qpgeom.I b/panda/src/gobj/qpgeom.I index 61babb7ce4..91439937bf 100644 --- a/panda/src/gobj/qpgeom.I +++ b/panda/src/gobj/qpgeom.I @@ -187,11 +187,34 @@ get_modified() const { //////////////////////////////////////////////////////////////////// INLINE qpGeom::CacheEntry:: CacheEntry(const qpGeomVertexData *source_data, const qpGeomMunger *modifier) : + _source(NULL), _source_data(source_data), - _modifier(modifier) + _modifier(modifier), + _geom_result(NULL), + _data_result(NULL) { } +//////////////////////////////////////////////////////////////////// +// Function: qpGeom::CacheEntry::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE qpGeom::CacheEntry:: +CacheEntry(qpGeom *source, const qpGeomVertexData *source_data, + const qpGeomMunger *modifier, const qpGeom *geom_result, + const qpGeomVertexData *data_result) : + _source(source), + _source_data(source_data), + _modifier(modifier), + _geom_result(geom_result), + _data_result(data_result) +{ + if (_geom_result != _source) { + _geom_result->ref(); + } +} + //////////////////////////////////////////////////////////////////// // Function: qpGeom::CacheEntry::operator < // Access: Public diff --git a/panda/src/gobj/qpgeom.cxx b/panda/src/gobj/qpgeom.cxx index 268eb9f6f7..25815df40e 100644 --- a/panda/src/gobj/qpgeom.cxx +++ b/panda/src/gobj/qpgeom.cxx @@ -469,7 +469,7 @@ munge_geom(const qpGeomMunger *munger, { CDReader cdata(_cycler); CacheEntry temp_entry(source_data, munger); - temp_entry.ref(); // big ugly hack to allow a stack-allocated ReferenceCount object. + temp_entry.local_object(); Cache::const_iterator ci = cdata->_cache.find(&temp_entry); if (ci != cdata->_cache.end()) { CacheEntry *entry = (*ci); @@ -483,7 +483,6 @@ munge_geom(const qpGeomMunger *munger, entry->refresh(); result = entry->_geom_result; data = entry->_data_result; - temp_entry.unref(); return; } @@ -496,7 +495,6 @@ munge_geom(const qpGeomMunger *munger, CDWriter cdataw(((qpGeom *)this)->_cycler, cdata); cdataw->_cache.erase(entry); } - temp_entry.unref(); } // Ok, invoke the munger. @@ -513,10 +511,8 @@ munge_geom(const qpGeomMunger *munger, CacheEntry *entry; { CDWriter cdata(((qpGeom *)this)->_cycler); - entry = new CacheEntry(source_data, munger); - entry->_source = (qpGeom *)this; - entry->_geom_result = result; - entry->_data_result = data; + entry = new CacheEntry((qpGeom *)this, source_data, munger, + result, data); bool inserted = cdata->_cache.insert(entry).second; nassertv(inserted); } @@ -880,6 +876,18 @@ fillin(DatagramIterator &scan, BamReader *manager) { manager->read_cdata(scan, _cycler); } +//////////////////////////////////////////////////////////////////// +// Function: qpGeom::CacheEntry::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +qpGeom::CacheEntry:: +~CacheEntry() { + if (_geom_result != _source) { + unref_delete(_geom_result); + } +} + //////////////////////////////////////////////////////////////////// // Function: qpGeom::CacheEntry::evict_callback // Access: Public, Virtual @@ -900,17 +908,6 @@ evict_callback() { _source->_cycler.release_write_stage(0, cdata); } -//////////////////////////////////////////////////////////////////// -// Function: qpGeom::CacheEntry::get_result_size -// Access: Public, Virtual -// Description: Returns the approximate number of bytes represented -// by the computed result. -//////////////////////////////////////////////////////////////////// -int qpGeom::CacheEntry:: -get_result_size() const { - return _geom_result->get_num_bytes() + _data_result->get_num_bytes(); -} - //////////////////////////////////////////////////////////////////// // Function: qpGeom::CacheEntry::output // Access: Public, Virtual diff --git a/panda/src/gobj/qpgeom.h b/panda/src/gobj/qpgeom.h index 81d382b960..458d5871e5 100644 --- a/panda/src/gobj/qpgeom.h +++ b/panda/src/gobj/qpgeom.h @@ -134,22 +134,27 @@ private: // We have to use reference-counting pointers here instead of having // explicit cleanup in the GeomVertexFormat destructor, because the // cache needs to be stored in the CycleData, which makes accurate - // cleanup more difficult. We use the GeomVertexCacheManager class - // to avoid cache bloat. + // cleanup more difficult. We use the GeomCacheManager class to + // avoid cache bloat. class CacheEntry : public qpGeomCacheEntry { public: INLINE CacheEntry(const qpGeomVertexData *source_data, const qpGeomMunger *modifier); + INLINE CacheEntry(qpGeom *source, + const qpGeomVertexData *source_data, + const qpGeomMunger *modifier, + const qpGeom *geom_result, + const qpGeomVertexData *data_result); + virtual ~CacheEntry(); INLINE bool operator < (const CacheEntry &other) const; virtual void evict_callback(); - virtual int get_result_size() const; virtual void output(ostream &out) const; qpGeom *_source; CPT(qpGeomVertexData) _source_data; CPT(qpGeomMunger) _modifier; - CPT(qpGeom) _geom_result; + const qpGeom *_geom_result; // ref-counted if not same as _source CPT(qpGeomVertexData) _data_result; }; typedef pset > Cache; diff --git a/panda/src/gobj/qpgeomCacheEntry.I b/panda/src/gobj/qpgeomCacheEntry.I index 946d04a050..7d1688954f 100644 --- a/panda/src/gobj/qpgeomCacheEntry.I +++ b/panda/src/gobj/qpgeomCacheEntry.I @@ -30,27 +30,6 @@ qpGeomCacheEntry() { #endif } -//////////////////////////////////////////////////////////////////// -// Function: qpGeomCacheEntry::refresh -// Access: Public -// Description: Marks the cache entry recently used, so it will not -// be evicted for a while. -//////////////////////////////////////////////////////////////////// -INLINE void qpGeomCacheEntry:: -refresh() { - nassertv(_next != (qpGeomCacheEntry *)NULL && _prev != (qpGeomCacheEntry *)NULL); - - qpGeomCacheManager *cache_mgr = qpGeomCacheManager::get_global_ptr(); - MutexHolder holder(cache_mgr->_lock); - - remove_from_list(); - insert_before(cache_mgr->_list); - - int new_size = get_result_size(); - cache_mgr->_total_size += (new_size - _result_size); - _result_size = new_size; -} - //////////////////////////////////////////////////////////////////// // Function: qpGeomCacheEntry::remove_from_list // Access: Private diff --git a/panda/src/gobj/qpgeomCacheEntry.cxx b/panda/src/gobj/qpgeomCacheEntry.cxx index ba1c8ef6be..f900872d06 100644 --- a/panda/src/gobj/qpgeomCacheEntry.cxx +++ b/panda/src/gobj/qpgeomCacheEntry.cxx @@ -20,6 +20,7 @@ #include "qpgeomCacheManager.h" #include "mutexHolder.h" #include "config_gobj.h" +#include "clockObject.h" //////////////////////////////////////////////////////////////////// // Function: qpGeomCacheEntry::Destructor @@ -41,21 +42,20 @@ record() { nassertr(_next == (qpGeomCacheEntry *)NULL && _prev == (qpGeomCacheEntry *)NULL, NULL); PT(qpGeomCacheEntry) keepme = this; - _result_size = get_result_size(); - qpGeomCacheManager *cache_mgr = qpGeomCacheManager::get_global_ptr(); MutexHolder holder(cache_mgr->_lock); if (gobj_cat.is_debug()) { gobj_cat.debug() << "recording cache entry: " << *this << ", total_size = " - << cache_mgr->_total_size + _result_size << "\n"; + << cache_mgr->_total_size + 1 << "\n"; } insert_before(cache_mgr->_list); - cache_mgr->_total_size += _result_size; + ++cache_mgr->_total_size; cache_mgr->_geom_cache_size_pcollector.set_level(cache_mgr->_total_size); cache_mgr->_geom_cache_record_pcollector.add_level(1); + _last_frame_used = ClockObject::get_global_clock()->get_frame_count(); // Increment our own reference count while we're in the queue, just // so we don't have to play games with it later--this is inner-loop @@ -70,6 +70,25 @@ record() { return this; } +//////////////////////////////////////////////////////////////////// +// Function: qpGeomCacheEntry::refresh +// Access: Public +// Description: Marks the cache entry recently used, so it will not +// be evicted for a while. +//////////////////////////////////////////////////////////////////// +void qpGeomCacheEntry:: +refresh() { + nassertv(_next != (qpGeomCacheEntry *)NULL && _prev != (qpGeomCacheEntry *)NULL); + + qpGeomCacheManager *cache_mgr = qpGeomCacheManager::get_global_ptr(); + MutexHolder holder(cache_mgr->_lock); + + remove_from_list(); + insert_before(cache_mgr->_list); + + _last_frame_used = ClockObject::get_global_clock()->get_frame_count(); +} + //////////////////////////////////////////////////////////////////// // Function: qpGeomCacheEntry::erase // Access: Public @@ -92,7 +111,7 @@ erase() { MutexHolder holder(cache_mgr->_lock); remove_from_list(); - cache_mgr->_total_size -= _result_size; + --cache_mgr->_total_size; cache_mgr->_geom_cache_size_pcollector.set_level(cache_mgr->_total_size); cache_mgr->_geom_cache_erase_pcollector.add_level(1); @@ -109,17 +128,6 @@ void qpGeomCacheEntry:: evict_callback() { } -//////////////////////////////////////////////////////////////////// -// Function: qpGeomCacheEntry::get_result_size -// Access: Public, Virtual -// Description: Returns the approximate number of bytes represented -// by the computed result. -//////////////////////////////////////////////////////////////////// -int qpGeomCacheEntry:: -get_result_size() const { - return 0; -} - //////////////////////////////////////////////////////////////////// // Function: qpGeomCacheEntry::output // Access: Public, Virtual diff --git a/panda/src/gobj/qpgeomCacheEntry.h b/panda/src/gobj/qpgeomCacheEntry.h index 6636ed40aa..0f914bc8be 100644 --- a/panda/src/gobj/qpgeomCacheEntry.h +++ b/panda/src/gobj/qpgeomCacheEntry.h @@ -43,15 +43,14 @@ public: virtual ~qpGeomCacheEntry(); PT(qpGeomCacheEntry) record(); - INLINE void refresh(); + void refresh(); PT(qpGeomCacheEntry) erase(); virtual void evict_callback(); - virtual int get_result_size() const; virtual void output(ostream &out) const; private: - int _result_size; + int _last_frame_used; INLINE void remove_from_list(); INLINE void insert_before(qpGeomCacheEntry *node); diff --git a/panda/src/gobj/qpgeomCacheManager.I b/panda/src/gobj/qpgeomCacheManager.I index 908ce786fa..2d4f29b0ba 100644 --- a/panda/src/gobj/qpgeomCacheManager.I +++ b/panda/src/gobj/qpgeomCacheManager.I @@ -20,37 +20,39 @@ //////////////////////////////////////////////////////////////////// // Function: qpGeomCacheManager::set_max_size // Access: Published -// Description: Specifies the amount of memory, in bytes, that should -// be set aside for storing pre-processed data for -// rendering vertices. This is not a limit on the -// actual vertex data, which is what it is; it is also -// not a limit on the amount of memory used by the video -// driver or the system graphics interface, which Panda -// has no control over. +// Description: Specifies the maximum number of entries in the cache +// for storing pre-processed data for rendering +// vertices. This limit is flexible, and may be +// temporarily exceeded if many different Geoms are +// pre-processed during the space of a single frame. +// +// This is not a limit on the actual vertex data, which +// is what it is; it is also not a limit on the amount +// of memory used by the video driver or the system +// graphics interface, which Panda has no control over. //////////////////////////////////////////////////////////////////// INLINE void qpGeomCacheManager:: set_max_size(int max_size) const { // We directly change the config variable. - vertex_convert_cache = max_size; + geom_cache_size = max_size; } //////////////////////////////////////////////////////////////////// // Function: qpGeomCacheManager::get_max_size // Access: Published -// Description: Returns the amount of memory, in bytes, that should -// be set aside for storing pre-processed data for -// rendering vertices. See set_max_size(). +// Description: Returns the maximum number of entries in the cache +// for storing pre-processed data for rendering +// vertices. See set_max_size(). //////////////////////////////////////////////////////////////////// INLINE int qpGeomCacheManager:: get_max_size() const { - return vertex_convert_cache; + return geom_cache_size; } //////////////////////////////////////////////////////////////////// // Function: qpGeomCacheManager::get_total_size // Access: Published -// Description: Returns the amount of memory, in bytes, currently -// consumed by the cache of pre-processed vertex data. +// Description: Returns the number of entries currently in the cache. //////////////////////////////////////////////////////////////////// INLINE int qpGeomCacheManager:: get_total_size() const { diff --git a/panda/src/gobj/qpgeomCacheManager.cxx b/panda/src/gobj/qpgeomCacheManager.cxx index 36769cd340..f7e9cf3473 100644 --- a/panda/src/gobj/qpgeomCacheManager.cxx +++ b/panda/src/gobj/qpgeomCacheManager.cxx @@ -77,21 +77,36 @@ void qpGeomCacheManager:: evict_old_entries() { MutexHolder holder(_lock); + int current_frame = ClockObject::get_global_clock()->get_frame_count(); + int min_frames = geom_cache_min_frames; + int max_size = get_max_size(); while (_total_size > max_size) { PT(qpGeomCacheEntry) entry = _list->_next; nassertv(entry != _list); + + if (current_frame - entry->_last_frame_used < min_frames) { + // Never mind, this one is too new. + if (gobj_cat.is_debug()) { + gobj_cat.debug() + << "Oldest element in cache is " + << current_frame - entry->_last_frame_used + << " frames; keeping cache at " << _total_size << " entries.\n"; + } + break; + } + entry->unref(); if (gobj_cat.is_debug()) { gobj_cat.debug() - << "cache total_size = " << _total_size << ", max_size = " + << "cache total_size = " << _total_size << " entries, max_size = " << max_size << ", removing " << *entry << "\n"; } entry->evict_callback(); - _total_size -= entry->_result_size; + --_total_size; entry->remove_from_list(); _geom_cache_evict_pcollector.add_level(1); } diff --git a/panda/src/gobj/qpgeomMunger.cxx b/panda/src/gobj/qpgeomMunger.cxx index 97fbf5137c..08c13f8a16 100644 --- a/panda/src/gobj/qpgeomMunger.cxx +++ b/panda/src/gobj/qpgeomMunger.cxx @@ -252,17 +252,6 @@ do_unregister() { _formats_by_animation.clear(); } -//////////////////////////////////////////////////////////////////// -// Function: qpGeomMunger::CacheEntry::get_result_size -// Access: Public, Virtual -// Description: Returns the approximate number of bytes represented -// by the computed result. -//////////////////////////////////////////////////////////////////// -int qpGeomMunger::CacheEntry:: -get_result_size() const { - return sizeof(qpGeomMunger); -} - //////////////////////////////////////////////////////////////////// // Function: qpGeomMunger::CacheEntry::output // Access: Public, Virtual diff --git a/panda/src/gobj/qpgeomMunger.h b/panda/src/gobj/qpgeomMunger.h index 14b8408097..2132683445 100644 --- a/panda/src/gobj/qpgeomMunger.h +++ b/panda/src/gobj/qpgeomMunger.h @@ -118,7 +118,6 @@ private: private: class CacheEntry : public qpGeomCacheEntry { public: - virtual int get_result_size() const; virtual void output(ostream &out) const; PT(qpGeomMunger) _munger; diff --git a/panda/src/gobj/qpgeomPrimitive.cxx b/panda/src/gobj/qpgeomPrimitive.cxx index b903f2c7e4..27f54d19bc 100644 --- a/panda/src/gobj/qpgeomPrimitive.cxx +++ b/panda/src/gobj/qpgeomPrimitive.cxx @@ -300,12 +300,7 @@ clear_vertices() { //////////////////////////////////////////////////////////////////// void qpGeomPrimitive:: offset_vertices(int offset) { - CDWriter cdata(_cycler); - - cdata->_got_minmax = false; - - qpGeomVertexRewriter index(cdata->_vertices, 0); - + qpGeomVertexRewriter index(modify_vertices(), 0); while (!index.is_at_end()) { index.set_data1i(index.get_data1i() + offset); } diff --git a/panda/src/gobj/qpgeomVertexArrayData.I b/panda/src/gobj/qpgeomVertexArrayData.I index ac88c892f2..6e0e36a080 100644 --- a/panda/src/gobj/qpgeomVertexArrayData.I +++ b/panda/src/gobj/qpgeomVertexArrayData.I @@ -136,3 +136,9 @@ CData(const qpGeomVertexArrayData::CData ©) : _modified(copy._modified) { } + +INLINE ostream & +operator << (ostream &out, const qpGeomVertexArrayData &obj) { + obj.output(out); + return out; +} diff --git a/panda/src/gobj/qpgeomVertexArrayData.cxx b/panda/src/gobj/qpgeomVertexArrayData.cxx index 678a2122f3..5331ac222f 100644 --- a/panda/src/gobj/qpgeomVertexArrayData.cxx +++ b/panda/src/gobj/qpgeomVertexArrayData.cxx @@ -159,6 +159,26 @@ set_usage_hint(qpGeomVertexArrayData::UsageHint usage_hint) { cdata->_modified = qpGeom::get_next_modified(); } +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexArrayData::output +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +void qpGeomVertexArrayData:: +output(ostream &out) const { + out << get_num_rows() << " rows: " << *get_array_format(); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexArrayData::write +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +void qpGeomVertexArrayData:: +write(ostream &out, int indent_level) const { + _array_format->write_with_data(out, indent_level, this); +} + //////////////////////////////////////////////////////////////////// // Function: qpGeomVertexArrayData::modify_data // Access: Public diff --git a/panda/src/gobj/qpgeomVertexArrayData.h b/panda/src/gobj/qpgeomVertexArrayData.h index 0d692169b7..ac2ee2ca4f 100644 --- a/panda/src/gobj/qpgeomVertexArrayData.h +++ b/panda/src/gobj/qpgeomVertexArrayData.h @@ -81,6 +81,9 @@ PUBLISHED: INLINE int get_data_size_bytes() const; INLINE UpdateSeq get_modified() const; + void output(ostream &out) const; + void write(ostream &out, int indent_level = 0) const; + public: INLINE CPTA_uchar get_data() const; PTA_uchar modify_data(); @@ -158,6 +161,8 @@ private: friend class PreparedGraphicsObjects; }; +INLINE ostream &operator << (ostream &out, const qpGeomVertexArrayData &obj); + #include "qpgeomVertexArrayData.I" #endif diff --git a/panda/src/gobj/qpgeomVertexArrayFormat.cxx b/panda/src/gobj/qpgeomVertexArrayFormat.cxx index 327fe64da1..de1ad27b8f 100644 --- a/panda/src/gobj/qpgeomVertexArrayFormat.cxx +++ b/panda/src/gobj/qpgeomVertexArrayFormat.cxx @@ -435,13 +435,13 @@ write(ostream &out, int indent_level) const { //////////////////////////////////////////////////////////////////// void qpGeomVertexArrayFormat:: write_with_data(ostream &out, int indent_level, - const qpGeomVertexData *data, int array_index) const { + const qpGeomVertexArrayData *array_data) const { consider_sort_columns(); - int num_vertices = data->get_num_rows(); + int num_rows = array_data->get_num_rows(); - qpGeomVertexReader reader(data); + qpGeomVertexReader reader(array_data); - for (int i = 0; i < num_vertices; i++) { + for (int i = 0; i < num_rows; i++) { indent(out, indent_level) << "row " << i << ":\n"; reader.set_row(i); @@ -449,7 +449,7 @@ write_with_data(ostream &out, int indent_level, for (ci = _columns.begin(); ci != _columns.end(); ++ci) { const qpGeomVertexColumn *column = (*ci); int num_values = min(column->get_num_values(), 4); - reader.set_column(array_index, column); + reader.set_column(0, column); const LVecBase4f &d = reader.get_data4f(); indent(out, indent_level + 2) diff --git a/panda/src/gobj/qpgeomVertexArrayFormat.h b/panda/src/gobj/qpgeomVertexArrayFormat.h index 22d15a96fc..68627b0e91 100644 --- a/panda/src/gobj/qpgeomVertexArrayFormat.h +++ b/panda/src/gobj/qpgeomVertexArrayFormat.h @@ -29,6 +29,7 @@ class qpGeomVertexFormat; class qpGeomVertexData; +class qpGeomVertexArrayData; class InternalName; class FactoryParams; class BamWriter; @@ -109,7 +110,7 @@ PUBLISHED: void output(ostream &out) const; void write(ostream &out, int indent_level = 0) const; void write_with_data(ostream &out, int indent_level, - const qpGeomVertexData *data, int array_index) const; + const qpGeomVertexArrayData *array_data) const; public: int compare_to(const qpGeomVertexArrayFormat &other) const; diff --git a/panda/src/gobj/qpgeomVertexData.cxx b/panda/src/gobj/qpgeomVertexData.cxx index dc574176ea..8478573f76 100644 --- a/panda/src/gobj/qpgeomVertexData.cxx +++ b/panda/src/gobj/qpgeomVertexData.cxx @@ -553,7 +553,8 @@ convert_to(const qpGeomVertexFormat *new_format) const { // Okay, convert the data to the new format. if (gobj_cat.is_debug()) { gobj_cat.debug() - << "Converting " << get_num_rows() << " rows.\n"; + << "Converting " << get_num_rows() << " rows from " << *_format + << " to " << *new_format << "\n"; } PStatTimer timer(_convert_pcollector); diff --git a/panda/src/gobj/qpgeomVertexFormat.cxx b/panda/src/gobj/qpgeomVertexFormat.cxx index ecac9edc36..ee8070252a 100644 --- a/panda/src/gobj/qpgeomVertexFormat.cxx +++ b/panda/src/gobj/qpgeomVertexFormat.cxx @@ -418,7 +418,7 @@ write_with_data(ostream &out, int indent_level, CPTA_uchar array_data = data->get_array(i)->get_data(); indent(out, indent_level) << "Array " << i << " (" << (void *)array_data.p() << "):\n"; - _arrays[i]->write_with_data(out, indent_level + 2, data, i); + _arrays[i]->write_with_data(out, indent_level + 2, data->get_array(i)); } } diff --git a/panda/src/pstatclient/pStatProperties.cxx b/panda/src/pstatclient/pStatProperties.cxx index 7772239f6e..71911ff832 100644 --- a/panda/src/pstatclient/pStatProperties.cxx +++ b/panda/src/pstatclient/pStatProperties.cxx @@ -169,8 +169,8 @@ static LevelCollectorProperties level_properties[] = { { 1, "Vertex buffer switch", { 0.0, 0.6, 0.8 }, "", 500 }, { 1, "Vertex buffer switch:Vertex", { 0.8, 0.0, 0.6 } }, { 1, "Vertex buffer switch:Index", { 0.8, 0.6, 0.3 } }, - { 1, "Geom cache size", { 1.0, 0.8, 0.6 }, "MB", 12, 1048576 }, - { 1, "Geom cache operations", { 1.0, 0.8, 0.6 }, "", 500 }, + { 1, "Geom cache size", { 0.6, 0.8, 0.6 }, "", 500 }, + { 1, "Geom cache operations", { 1.0, 0.6, 0.6 }, "", 500 }, { 1, "Geom cache operations:record", { 0.2, 0.4, 0.8 } }, { 1, "Geom cache operations:erase", { 0.4, 0.8, 0.2 } }, { 1, "Geom cache operations:evict", { 0.8, 0.2, 0.4 } },