From 5ad57abaf54c4c2716cf629bd7b7da355b6c8034 Mon Sep 17 00:00:00 2001 From: David Rose Date: Tue, 22 Mar 2005 01:24:49 +0000 Subject: [PATCH] simply geom caching system --- panda/src/display/colorMunger.cxx | 24 +- panda/src/display/colorMunger.h | 1 + panda/src/glstuff/glGeomMunger_src.cxx | 7 +- .../glstuff/glGraphicsStateGuardian_src.cxx | 17 + panda/src/gobj/Sources.pp | 9 +- panda/src/gobj/gobj_composite1.cxx | 3 +- panda/src/gobj/qpgeom.I | 22 ++ panda/src/gobj/qpgeom.cxx | 129 ++++---- panda/src/gobj/qpgeom.h | 27 +- panda/src/gobj/qpgeomCacheEntry.I | 93 ++++++ panda/src/gobj/qpgeomCacheEntry.cxx | 127 ++++++++ panda/src/gobj/qpgeomCacheEntry.h | 69 ++++ panda/src/gobj/qpgeomCacheManager.I | 58 ++++ panda/src/gobj/qpgeomCacheManager.cxx | 92 ++++++ panda/src/gobj/qpgeomCacheManager.h | 91 ++++++ panda/src/gobj/qpgeomMunger.cxx | 61 +++- panda/src/gobj/qpgeomMunger.h | 20 ++ panda/src/gobj/qpgeomPrimitive.cxx | 99 +++--- panda/src/gobj/qpgeomPrimitive.h | 16 +- panda/src/gobj/qpgeomVertexArrayData.h | 2 +- panda/src/gobj/qpgeomVertexCacheManager.I | 303 ------------------ panda/src/gobj/qpgeomVertexCacheManager.cxx | 196 ----------- panda/src/gobj/qpgeomVertexCacheManager.h | 167 ---------- 23 files changed, 842 insertions(+), 791 deletions(-) create mode 100644 panda/src/gobj/qpgeomCacheEntry.I create mode 100644 panda/src/gobj/qpgeomCacheEntry.cxx create mode 100644 panda/src/gobj/qpgeomCacheEntry.h create mode 100644 panda/src/gobj/qpgeomCacheManager.I create mode 100644 panda/src/gobj/qpgeomCacheManager.cxx create mode 100644 panda/src/gobj/qpgeomCacheManager.h delete mode 100644 panda/src/gobj/qpgeomVertexCacheManager.I delete mode 100644 panda/src/gobj/qpgeomVertexCacheManager.cxx delete mode 100644 panda/src/gobj/qpgeomVertexCacheManager.h diff --git a/panda/src/display/colorMunger.cxx b/panda/src/display/colorMunger.cxx index 3d99b72695..771b6a3fec 100644 --- a/panda/src/display/colorMunger.cxx +++ b/panda/src/display/colorMunger.cxx @@ -97,5 +97,27 @@ compare_to_impl(const qpGeomMunger *other) const { if (_color_scale != om->_color_scale) { return _color_scale < om->_color_scale ? -1 : 1; } - return 0; + + return qpGeomMunger::compare_to_impl(other); +} + +//////////////////////////////////////////////////////////////////// +// Function: ColorMunger::geom_compare_to_impl +// Access: Protected, Virtual +// Description: Called to compare two GeomMungers who are known to be +// of the same type, for an apples-to-apples comparison. +// This will never be called on two pointers of a +// different type. +//////////////////////////////////////////////////////////////////// +int ColorMunger:: +geom_compare_to_impl(const qpGeomMunger *other) const { + const ColorMunger *om = DCAST(ColorMunger, other); + if (_color != om->_color) { + return _color < om->_color ? -1 : 1; + } + if (_color_scale != om->_color_scale) { + return _color_scale < om->_color_scale ? -1 : 1; + } + + return qpGeomMunger::geom_compare_to_impl(other); } diff --git a/panda/src/display/colorMunger.h b/panda/src/display/colorMunger.h index 34bc22d216..47149fa216 100644 --- a/panda/src/display/colorMunger.h +++ b/panda/src/display/colorMunger.h @@ -42,6 +42,7 @@ public: protected: virtual CPT(qpGeomVertexData) munge_data_impl(const qpGeomVertexData *data); virtual int compare_to_impl(const qpGeomMunger *other) const; + virtual int geom_compare_to_impl(const qpGeomMunger *other) const; private: int _num_components; diff --git a/panda/src/glstuff/glGeomMunger_src.cxx b/panda/src/glstuff/glGeomMunger_src.cxx index c48e96c092..72788b25b0 100644 --- a/panda/src/glstuff/glGeomMunger_src.cxx +++ b/panda/src/glstuff/glGeomMunger_src.cxx @@ -84,6 +84,7 @@ compare_to_impl(const qpGeomMunger *other) const { if (_tex_gen != om->_tex_gen) { return _tex_gen < om->_tex_gen ? -1 : 1; } + return ColorMunger::compare_to_impl(other); } @@ -97,8 +98,8 @@ compare_to_impl(const qpGeomMunger *other) const { //////////////////////////////////////////////////////////////////// int CLP(GeomMunger):: geom_compare_to_impl(const qpGeomMunger *other) const { - // We don't consider _texture and _tex_gen for these purposes; they + // We don't consider _texture and _tex_gen for this purpose; they // affect only whether the GL display list should be regenerated or - // not. - return ColorMunger::compare_to_impl(other); + // not, and don't require reconverting the vertices. + return ColorMunger::geom_compare_to_impl(other); } diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index 1b2be482ae..fdc36c1ea0 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -2057,6 +2057,11 @@ begin_draw_primitives(const qpGeom *geom, const qpGeomMunger *munger, UpdateSeq modified = max(geom->get_modified(), _vertex_data->get_modified()); if (ggc->_modified == modified) { // If it hasn't been modified, just play the display list again. + if (GLCAT.is_spam()) { + GLCAT.spam() + << "calling display list " << ggc->_index << "\n"; + } + GLP(CallList)(ggc->_index); #ifdef DO_PSTATS _vertices_display_list_pcollector.add_level(ggc->_num_verts); @@ -2065,6 +2070,15 @@ begin_draw_primitives(const qpGeom *geom, const qpGeomMunger *munger, // And now we don't need to do anything else for this geom. return false; } + + if (GLCAT.is_debug()) { + GLCAT.debug() + << "compiling display list " << ggc->_index << "\n"; + cerr << "ggc = " << ggc << "," << ggc->_modified + << " geom = " << geom << "," << geom->get_modified() + << " vertex_data = " << _vertex_data << "," + << _vertex_data->get_modified() << "\n"; + } // If it has been modified, or this is the first time, then we // need to build the display list up. @@ -2149,15 +2163,18 @@ begin_draw_primitives(const qpGeom *geom, const qpGeomMunger *munger, GLP(TexCoordPointer)(num_components, get_numeric_type(numeric_type), stride, client_pointer + start); GLP(EnableClientState)(GL_TEXTURE_COORD_ARRAY); + cerr << "sending " << *name << "\n"; } else { // The vertex data doesn't have texcoords for this stage (even // though they're needed). GLP(DisableClientState)(GL_TEXTURE_COORD_ARRAY); + cerr << "not defined " << *name << "\n"; } } else { // No texcoords are needed for this stage. GLP(DisableClientState)(GL_TEXTURE_COORD_ARRAY); + cerr << "not sending " << *stage->get_texcoord_name() << "\n"; } ++stage_index; diff --git a/panda/src/gobj/Sources.pp b/panda/src/gobj/Sources.pp index 3e7975c660..bb5238114d 100644 --- a/panda/src/gobj/Sources.pp +++ b/panda/src/gobj/Sources.pp @@ -27,7 +27,8 @@ qpgeomUsageHint.h \ qpgeomVertexArrayData.h qpgeomVertexArrayData.I \ qpgeomVertexArrayFormat.h qpgeomVertexArrayFormat.I \ - qpgeomVertexCacheManager.h qpgeomVertexCacheManager.I \ + qpgeomCacheEntry.h qpgeomCacheEntry.I \ + qpgeomCacheManager.h qpgeomCacheManager.I \ qpgeomVertexData.h qpgeomVertexData.I \ qpgeomVertexDataType.h qpgeomVertexDataType.I \ qpgeomVertexFormat.h qpgeomVertexFormat.I \ @@ -63,7 +64,8 @@ qpgeomTrifans.cxx \ qpgeomVertexArrayData.cxx \ qpgeomVertexArrayFormat.cxx \ - qpgeomVertexCacheManager.cxx \ + qpgeomCacheEntry.cxx \ + qpgeomCacheManager.cxx \ qpgeomVertexData.cxx \ qpgeomVertexDataType.cxx \ qpgeomVertexFormat.cxx \ @@ -97,7 +99,8 @@ qpgeomUsageHint.h \ qpgeomVertexArrayData.h qpgeomVertexArrayData.I \ qpgeomVertexArrayFormat.h qpgeomVertexArrayFormat.I \ - qpgeomVertexCacheManager.h qpgeomVertexCacheManager.I \ + qpgeomCacheEntry.h qpgeomCacheEntry.I \ + qpgeomCacheManager.h qpgeomCacheManager.I \ qpgeomVertexData.h qpgeomVertexData.I \ qpgeomVertexDataType.h qpgeomVertexDataType.I \ qpgeomVertexFormat.h qpgeomVertexFormat.I \ diff --git a/panda/src/gobj/gobj_composite1.cxx b/panda/src/gobj/gobj_composite1.cxx index e1169a0e5f..54ac764c22 100644 --- a/panda/src/gobj/gobj_composite1.cxx +++ b/panda/src/gobj/gobj_composite1.cxx @@ -19,7 +19,8 @@ #include "qpgeomTrifans.cxx" #include "qpgeomVertexArrayData.cxx" #include "qpgeomVertexArrayFormat.cxx" -#include "qpgeomVertexCacheManager.cxx" +#include "qpgeomCacheEntry.cxx" +#include "qpgeomCacheManager.cxx" #include "qpgeomVertexData.cxx" #include "qpgeomVertexDataType.cxx" #include "qpgeomVertexFormat.cxx" diff --git a/panda/src/gobj/qpgeom.I b/panda/src/gobj/qpgeom.I index 0165b692a6..7a735afa88 100644 --- a/panda/src/gobj/qpgeom.I +++ b/panda/src/gobj/qpgeom.I @@ -137,6 +137,28 @@ get_modified() const { return cdata->_modified; } +//////////////////////////////////////////////////////////////////// +// Function: qpGeom::CacheEntry::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE qpGeom::CacheEntry:: +CacheEntry(const qpGeomMunger *modifier) : + _modifier(modifier) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeom::CacheEntry::operator < +// Access: Public +// Description: Provides a unique ordering within the set. +//////////////////////////////////////////////////////////////////// +INLINE bool qpGeom::CacheEntry:: +operator < (const CacheEntry &other) const { + return (_modifier < other._modifier); +} + + //////////////////////////////////////////////////////////////////// // Function: qpGeom::CData::Constructor // Access: Public diff --git a/panda/src/gobj/qpgeom.cxx b/panda/src/gobj/qpgeom.cxx index 9e8bf7a6a0..589f96969b 100644 --- a/panda/src/gobj/qpgeom.cxx +++ b/panda/src/gobj/qpgeom.cxx @@ -17,7 +17,6 @@ //////////////////////////////////////////////////////////////////// #include "qpgeom.h" -#include "qpgeomVertexCacheManager.h" #include "pStatTimer.h" #include "bamReader.h" #include "bamWriter.h" @@ -77,19 +76,17 @@ qpGeom:: // When we destruct, we should ensure that all of our cached // entries, across all pipeline stages, are properly removed from // the cache manager. - qpGeomVertexCacheManager *cache_mgr = - qpGeomVertexCacheManager::get_global_ptr(); - int num_stages = _cycler.get_num_stages(); for (int i = 0; i < num_stages; i++) { if (_cycler.is_stage_unique(i)) { CData *cdata = _cycler.write_stage(i); - for (MungedCache::iterator ci = cdata->_munged_cache.begin(); - ci != cdata->_munged_cache.end(); + for (Cache::iterator ci = cdata->_cache.begin(); + ci != cdata->_cache.end(); ++ci) { - cache_mgr->remove_geom(this, (*ci).first); + CacheEntry *entry = (*ci); + entry->erase(); } - cdata->_munged_cache.clear(); + cdata->_cache.clear(); _cycler.release_write_stage(i, cdata); } } @@ -236,21 +233,22 @@ munge_geom(const qpGeomMunger *munger, // call to record_geom() might recursively call back into this // object, and require a write. const CData *cdata = _cycler.read(); - MungedCache::const_iterator ci = cdata->_munged_cache.find(munger); - if (ci != cdata->_munged_cache.end()) { + CacheEntry temp_entry(munger); + temp_entry.ref(); // big ugly hack to allow a static ReferenceCount object. + Cache::const_iterator ci = cdata->_cache.find(&temp_entry); + if (ci != cdata->_cache.end()) { _cycler.release_read(cdata); + CacheEntry *entry = (*ci); + // Record a cache hit, so this element will stay in the cache a // while longer. - qpGeomVertexCacheManager *cache_mgr = - qpGeomVertexCacheManager::get_global_ptr(); - cache_mgr->record_geom(this, munger, - (*ci).second._geom->get_num_bytes() + - (*ci).second._data->get_num_bytes()); - - result = (*ci).second._geom; - data = (*ci).second._data; + entry->refresh(); + result = entry->_geom_result; + data = entry->_data_result; + temp_entry.unref(); return; } + temp_entry.unref(); _cycler.release_read(cdata); } @@ -263,20 +261,21 @@ munge_geom(const qpGeomMunger *munger, { // Record the new result in the cache. + CacheEntry *entry; { CDWriter cdata(((qpGeom *)this)->_cycler); - MungeResult &mr = cdata->_munged_cache[munger]; - mr._geom = result; - mr._data = data; + entry = new CacheEntry(munger); + entry->_source = (qpGeom *)this; + entry->_geom_result = result; + entry->_data_result = data; + bool inserted = cdata->_cache.insert(entry).second; + nassertv(inserted); } // And tell the cache manager about the new entry. (It might - // immediately request a delete from the cache of the thing we just - // added.) - qpGeomVertexCacheManager *cache_mgr = - qpGeomVertexCacheManager::get_global_ptr(); - cache_mgr->record_geom(this, munger, - result->get_num_bytes() + data->get_num_bytes()); + // immediately request a delete from the cache of the thing we + // just added.) + entry->record(); } } @@ -334,16 +333,14 @@ void qpGeom:: clear_cache() { // Probably we shouldn't do anything at all here unless we are // running in pipeline stage 0. - qpGeomVertexCacheManager *cache_mgr = - qpGeomVertexCacheManager::get_global_ptr(); - CData *cdata = CDWriter(_cycler); - for (MungedCache::iterator ci = cdata->_munged_cache.begin(); - ci != cdata->_munged_cache.end(); + for (Cache::iterator ci = cdata->_cache.begin(); + ci != cdata->_cache.end(); ++ci) { - cache_mgr->remove_geom(this, (*ci).first); + CacheEntry *entry = (*ci); + entry->erase(); } - cdata->_munged_cache.clear(); + cdata->_cache.clear(); } //////////////////////////////////////////////////////////////////// @@ -473,27 +470,6 @@ recompute_bound() { return bound; } -//////////////////////////////////////////////////////////////////// -// Function: qpGeom::remove_cache_entry -// Access: Private -// Description: Removes a particular entry from the local cache; it -// has already been removed from the cache manager. -// This is only called from GeomVertexCacheManager. -//////////////////////////////////////////////////////////////////// -void qpGeom:: -remove_cache_entry(const qpGeomMunger *modifier) const { - // We have to operate on stage 0 of the pipeline, since that's where - // the cache really counts. Because of the multistage pipeline, we - // might not actually have a cache entry there (it might have been - // added to stage 1 instead). No big deal if we don't. - CData *cdata = ((qpGeom *)this)->_cycler.write_stage(0); - MungedCache::iterator ci = cdata->_munged_cache.find(modifier); - if (ci != cdata->_munged_cache.end()) { - cdata->_munged_cache.erase(ci); - } - ((qpGeom *)this)->_cycler.release_write_stage(0, cdata); -} - //////////////////////////////////////////////////////////////////// // Function: qpGeom::reset_usage_hint // Access: Private @@ -569,6 +545,49 @@ fillin(DatagramIterator &scan, BamReader *manager) { manager->read_cdata(scan, _cycler); } +//////////////////////////////////////////////////////////////////// +// Function: qpGeom::CacheEntry::evict_callback +// Access: Public, Virtual +// Description: Called when the entry is evicted from the cache, this +// should clean up the owning object appropriately. +//////////////////////////////////////////////////////////////////// +void qpGeom::CacheEntry:: +evict_callback() { + // We have to operate on stage 0 of the pipeline, since that's where + // the cache really counts. Because of the multistage pipeline, we + // might not actually have a cache entry there (it might have been + // added to stage 1 instead). No big deal if we don't. + CData *cdata = _source->_cycler.write_stage(0); + Cache::iterator ci = cdata->_cache.find(this); + if (ci != cdata->_cache.end()) { + cdata->_cache.erase(ci); + } + _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 +// Description: +//////////////////////////////////////////////////////////////////// +void qpGeom::CacheEntry:: +output(ostream &out) const { + out << "geom " << (void *)_source << ", " + << (const void *)_modifier; +} + + //////////////////////////////////////////////////////////////////// // Function: qpGeom::CData::make_copy // Access: Public, Virtual diff --git a/panda/src/gobj/qpgeom.h b/panda/src/gobj/qpgeom.h index b537e2d93a..c1b94db86a 100644 --- a/panda/src/gobj/qpgeom.h +++ b/panda/src/gobj/qpgeom.h @@ -30,9 +30,12 @@ #include "qpgeomPrimitive.h" #include "qpgeomMunger.h" #include "qpgeomUsageHint.h" +#include "qpgeomCacheEntry.h" #include "updateSeq.h" #include "pointerTo.h" #include "geom.h" +#include "indirectLess.h" +#include "pset.h" //////////////////////////////////////////////////////////////////// // Class : qpGeom @@ -98,9 +101,6 @@ public: protected: virtual BoundingVolume *recompute_bound(); -private: - void remove_cache_entry(const qpGeomMunger *modifier) const; - private: typedef pvector Primitives; @@ -109,12 +109,21 @@ private: // cache needs to be stored in the CycleData, which makes accurate // cleanup more difficult. We use the GeomVertexCacheManager class // to avoid cache bloat. - class MungeResult { + class CacheEntry : public qpGeomCacheEntry { public: - CPT(qpGeom) _geom; - CPT(qpGeomVertexData) _data; + INLINE CacheEntry(const qpGeomMunger *modifier); + 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(qpGeomMunger) _modifier; + CPT(qpGeom) _geom_result; + CPT(qpGeomVertexData) _data_result; }; - typedef pmap MungedCache; + typedef pset > Cache; // This is the data that must be cycled between pipeline stages. class EXPCL_PANDA CData : public CycleData { @@ -131,7 +140,7 @@ private: qpGeomUsageHint::UsageHint _usage_hint; bool _got_usage_hint; UpdateSeq _modified; - MungedCache _munged_cache; + Cache _cache; }; PipelineCycler _cycler; @@ -171,7 +180,7 @@ public: private: static TypeHandle _type_handle; - friend class qpGeomVertexCacheManager; + friend class CacheEntry; }; INLINE ostream &operator << (ostream &out, const qpGeom &obj); diff --git a/panda/src/gobj/qpgeomCacheEntry.I b/panda/src/gobj/qpgeomCacheEntry.I new file mode 100644 index 0000000000..946d04a050 --- /dev/null +++ b/panda/src/gobj/qpgeomCacheEntry.I @@ -0,0 +1,93 @@ +// Filename: qpgeomCacheEntry.I +// Created by: drose (21Mar05) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomCacheEntry::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE qpGeomCacheEntry:: +qpGeomCacheEntry() { +#ifndef NDEBUG + _next = NULL; + _prev = NULL; +#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 +// Description: Removes a GeomCacheEntry record from the +// doubly-linked list. +//////////////////////////////////////////////////////////////////// +INLINE void qpGeomCacheEntry:: +remove_from_list() { + nassertv(_prev->_next == this && _next->_prev == this); + _prev->_next = _next; + _next->_prev = _prev; +#ifndef NDEBUG + _next = NULL; + _prev = NULL; +#endif +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomCacheEntry::insert_before +// Access: Private +// Description: Adds a GeomCacheEntry record before the indicated +// node in the doubly-linked list. +//////////////////////////////////////////////////////////////////// +INLINE void qpGeomCacheEntry:: +insert_before(qpGeomCacheEntry *node) { + nassertv(node->_prev->_next == node && node->_next->_prev == node); + nassertv(_prev == (qpGeomCacheEntry *)NULL && + _next == (qpGeomCacheEntry *)NULL); + _prev = node->_prev; + _next = node; + _prev->_next = this; + node->_prev = this; +} + +INLINE ostream & +operator << (ostream &out, const qpGeomCacheEntry &entry) { + entry.output(out); + return out; +} + diff --git a/panda/src/gobj/qpgeomCacheEntry.cxx b/panda/src/gobj/qpgeomCacheEntry.cxx new file mode 100644 index 0000000000..929ab7099a --- /dev/null +++ b/panda/src/gobj/qpgeomCacheEntry.cxx @@ -0,0 +1,127 @@ +// Filename: qpgeomCacheEntry.cxx +// Created by: drose (21Mar05) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#include "qpgeomCacheEntry.h" +#include "qpgeomCacheManager.h" +#include "mutexHolder.h" +#include "config_gobj.h" + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomCacheEntry::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +qpGeomCacheEntry:: +~qpGeomCacheEntry() { +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomCacheEntry::record +// Access: Public +// Description: Records the entry in the global cache for the first +// time. +//////////////////////////////////////////////////////////////////// +PT(qpGeomCacheEntry) qpGeomCacheEntry:: +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"; + } + + insert_before(cache_mgr->_list); + cache_mgr->_total_size += _result_size; + + // 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 + // stuff. + ref(); + + // Now remove any old entries if our cache is over the limit. This may + // also remove the entry we just added, especially if our cache size + // is set to 0. This may actually remove this very object. + cache_mgr->evict_old_entries(); + + return this; +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomCacheEntry::erase +// Access: Public +// Description: Removes the entry from the queue, returning a pointer +// to the entry. Does not call evict_callback(). +//////////////////////////////////////////////////////////////////// +PT(qpGeomCacheEntry) qpGeomCacheEntry:: +erase() { + nassertr(_next != (qpGeomCacheEntry *)NULL && _prev != (qpGeomCacheEntry *)NULL, NULL); + + PT(qpGeomCacheEntry) keepme = this; + unref(); + + if (gobj_cat.is_debug()) { + gobj_cat.debug() + << "remove_entry(" << *this << ")\n"; + } + + qpGeomCacheManager *cache_mgr = qpGeomCacheManager::get_global_ptr(); + MutexHolder holder(cache_mgr->_lock); + + remove_from_list(); + cache_mgr->_total_size -= _result_size; + + return this; +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomCacheEntry::evict_callback +// Access: Public, Virtual +// Description: Called when the entry is evicted from the cache, this +// should clean up the owning object appropriately. +//////////////////////////////////////////////////////////////////// +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 +// Description: +//////////////////////////////////////////////////////////////////// +void qpGeomCacheEntry:: +output(ostream &out) const { + out << "[ unknown ]"; +} diff --git a/panda/src/gobj/qpgeomCacheEntry.h b/panda/src/gobj/qpgeomCacheEntry.h new file mode 100644 index 0000000000..6636ed40aa --- /dev/null +++ b/panda/src/gobj/qpgeomCacheEntry.h @@ -0,0 +1,69 @@ +// Filename: qpgeomCacheEntry.h +// Created by: drose (21Mar05) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#ifndef qpGEOMCACHEENTRY_H +#define qpGEOMCACHEENTRY_H + +#include "pandabase.h" +#include "qpgeomCacheManager.h" +#include "referenceCount.h" +#include "config_gobj.h" +#include "pointerTo.h" +#include "mutexHolder.h" + +class qpGeom; +class qpGeomPrimitive; + +//////////////////////////////////////////////////////////////////// +// Class : qpGeomCacheEntry +// Description : This object contains a single cache entry in the +// GeomCacheManager. This is actually the base class of +// any number of individual cache types. +// +// This is part of the experimental Geom rewrite. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA qpGeomCacheEntry : public ReferenceCount { +public: + INLINE qpGeomCacheEntry(); + virtual ~qpGeomCacheEntry(); + + PT(qpGeomCacheEntry) record(); + INLINE 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; + + INLINE void remove_from_list(); + INLINE void insert_before(qpGeomCacheEntry *node); + +private: + qpGeomCacheEntry *_prev, *_next; + + friend class qpGeomCacheManager; +}; + +INLINE ostream &operator << (ostream &out, const qpGeomCacheEntry &entry); + +#include "qpgeomCacheEntry.I" + +#endif diff --git a/panda/src/gobj/qpgeomCacheManager.I b/panda/src/gobj/qpgeomCacheManager.I new file mode 100644 index 0000000000..908ce786fa --- /dev/null +++ b/panda/src/gobj/qpgeomCacheManager.I @@ -0,0 +1,58 @@ +// Filename: qpgeomCacheManager.I +// Created by: drose (11Mar05) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// 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. +//////////////////////////////////////////////////////////////////// +INLINE void qpGeomCacheManager:: +set_max_size(int max_size) const { + // We directly change the config variable. + vertex_convert_cache = 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(). +//////////////////////////////////////////////////////////////////// +INLINE int qpGeomCacheManager:: +get_max_size() const { + return vertex_convert_cache; +} + +//////////////////////////////////////////////////////////////////// +// 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. +//////////////////////////////////////////////////////////////////// +INLINE int qpGeomCacheManager:: +get_total_size() const { + return _total_size; +} diff --git a/panda/src/gobj/qpgeomCacheManager.cxx b/panda/src/gobj/qpgeomCacheManager.cxx new file mode 100644 index 0000000000..7586d15092 --- /dev/null +++ b/panda/src/gobj/qpgeomCacheManager.cxx @@ -0,0 +1,92 @@ +// Filename: qpgeomCacheManager.cxx +// Created by: drose (11Mar05) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#include "qpgeomCacheManager.h" +#include "qpgeomCacheEntry.h" +#include "mutexHolder.h" + +qpGeomCacheManager *qpGeomCacheManager::_global_ptr = NULL; + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomCacheManager::Constructor +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +qpGeomCacheManager:: +qpGeomCacheManager() : + _total_size(0) +{ + // We deliberately hang on to this pointer forever. + _list = new qpGeomCacheEntry; + _list->ref(); + _list->_next = _list; + _list->_prev = _list; +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomCacheManager::Destructor +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +qpGeomCacheManager:: +~qpGeomCacheManager() { + // Shouldn't be deleting this global object. + nassertv(false); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomCacheManager::get_global_ptr +// Access: Published, Static +// Description: Returns the global cache manager pointer. +//////////////////////////////////////////////////////////////////// +qpGeomCacheManager *qpGeomCacheManager:: +get_global_ptr() { + if (_global_ptr == (qpGeomCacheManager *)NULL) { + _global_ptr = new qpGeomCacheManager; + } + return _global_ptr; +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomCacheManager::evict_old_entries +// Access: Private +// Description: Trims the cache size down to get_max_size() by +// evicting old cache entries as needed. +//////////////////////////////////////////////////////////////////// +void qpGeomCacheManager:: +evict_old_entries() { + MutexHolder holder(_lock); + + int max_size = get_max_size(); + while (_total_size > max_size) { + PT(qpGeomCacheEntry) entry = _list->_next; + nassertv(entry != _list); + entry->unref(); + + if (gobj_cat.is_debug()) { + gobj_cat.debug() + << "cache total_size = " << _total_size << ", max_size = " + << max_size << ", removing " << *entry << "\n"; + } + + entry->evict_callback(); + + _total_size -= entry->_result_size; + entry->remove_from_list(); + } +} diff --git a/panda/src/gobj/qpgeomCacheManager.h b/panda/src/gobj/qpgeomCacheManager.h new file mode 100644 index 0000000000..3b529f8767 --- /dev/null +++ b/panda/src/gobj/qpgeomCacheManager.h @@ -0,0 +1,91 @@ +// Filename: qpgeomCacheManager.h +// Created by: drose (11Mar05) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#ifndef qpGEOMCACHEMANAGER_H +#define qpGEOMCACHEMANAGER_H + +#include "pandabase.h" +#include "config_gobj.h" +#include "pmutex.h" + +class qpGeomCacheEntry; + +//////////////////////////////////////////////////////////////////// +// Class : qpGeomCacheManager +// Description : This is used to keep track of, and limit the size of, +// the cache of munged vertices, which would otherwise +// be distributed through all of the GeomVertexData +// objects in the system. +// +// The actual data in the cache is not stored here, but +// rather it is distributed among the various +// GeomVertexData source objects. This allows the cache +// data to propagate through the multiprocess pipeline. +// +// This structure actually caches any of a number of +// different types of pointers, and mixes them all up in +// the same LRU cache list. Some of them (such as +// GeomMunger) are reference-counted here in the cache; +// most are not. +// +// This is part of the experimental Geom rewrite. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA qpGeomCacheManager { +protected: + qpGeomCacheManager(); + ~qpGeomCacheManager(); + +PUBLISHED: + INLINE void set_max_size(int max_size) const; + INLINE int get_max_size() const; + + INLINE int get_total_size() const; + + static qpGeomCacheManager *get_global_ptr(); + +public: + void evict_old_entries(); + +private: + // This mutex protects all operations on this object, especially the + // linked-list operations. + Mutex _lock; + + int _total_size; + + // We maintain a doubly-linked list to keep the cache entries in + // least-recently-used order: the items at the head of the list are + // ready to be flushed. We use our own doubly-linked list instead + // of an STL list, just so we can avoid a tiny bit of overhead, + // especially in keeping the pointer directly into the list from the + // calling objects. + + // The tail and the head of the list are both kept by the _prev and + // _next pointers, respectively, within the following object, which + // always exists solely to keep a handle to the list. Keeping a + // token of the list this way avoids special cases for an empty + // list. + qpGeomCacheEntry *_list; + + static qpGeomCacheManager *_global_ptr; + friend class qpGeomCacheEntry; +}; + +#include "qpgeomCacheManager.I" + +#endif diff --git a/panda/src/gobj/qpgeomMunger.cxx b/panda/src/gobj/qpgeomMunger.cxx index c7ce369ef6..3cac6de72f 100644 --- a/panda/src/gobj/qpgeomMunger.cxx +++ b/panda/src/gobj/qpgeomMunger.cxx @@ -17,7 +17,7 @@ //////////////////////////////////////////////////////////////////// #include "qpgeomMunger.h" -#include "qpgeomVertexCacheManager.h" +#include "qpgeomCacheManager.h" #include "mutexHolder.h" #include "pStatTimer.h" @@ -35,6 +35,29 @@ qpGeomMunger:: qpGeomMunger(const GraphicsStateGuardianBase *, const RenderState *) : _is_registered(false) { + _registered_key = get_registry()->_mungers.end(); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomMunger::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +qpGeomMunger:: +qpGeomMunger(const qpGeomMunger ©) : + _is_registered(false) +{ + _registered_key = get_registry()->_mungers.end(); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomMunger::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void qpGeomMunger:: +operator = (const qpGeomMunger ©) { + nassertv(!_is_registered); } //////////////////////////////////////////////////////////////////// @@ -205,7 +228,7 @@ compare_to_impl(const qpGeomMunger *other) const { //////////////////////////////////////////////////////////////////// int qpGeomMunger:: geom_compare_to_impl(const qpGeomMunger *other) const { - return compare_to_impl(other); + return 0; } //////////////////////////////////////////////////////////////////// @@ -237,9 +260,9 @@ do_register() { // Tell the cache manager to hang on to this new GeomMunger, so we // don't waste our time re-registering the same GeomMunger over and // over again. - qpGeomVertexCacheManager *cache_mgr = - qpGeomVertexCacheManager::get_global_ptr(); - cache_mgr->record_munger(this); + CacheEntry *entry = new CacheEntry; + entry->_munger = this; + entry->record(); _is_registered = true; } @@ -277,6 +300,27 @@ do_unregister() { _formats.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 +// Description: +//////////////////////////////////////////////////////////////////// +void qpGeomMunger::CacheEntry:: +output(ostream &out) const { + out << "munger " << _munger; +} + //////////////////////////////////////////////////////////////////// // Function: qpGeomMunger::Registry::Constructor // Access: Public @@ -315,6 +359,7 @@ register_munger(qpGeomMunger *munger) { Mungers::iterator mi = _mungers.insert(munger).first; qpGeomMunger *new_munger = (*mi); if (!new_munger->is_registered()) { + new_munger->_registered_key = mi; new_munger->do_register(); } @@ -331,8 +376,8 @@ register_munger(qpGeomMunger *munger) { void qpGeomMunger::Registry:: unregister_munger(qpGeomMunger *munger) { nassertv(munger->is_registered()); - Mungers::iterator mi = _mungers.find(munger); - nassertv(mi != _mungers.end()); - _mungers.erase(mi); + nassertv(munger->_registered_key != _mungers.end()); + _mungers.erase(munger->_registered_key); + munger->_registered_key = _mungers.end(); munger->do_unregister(); } diff --git a/panda/src/gobj/qpgeomMunger.h b/panda/src/gobj/qpgeomMunger.h index e4e60b9a40..98de151535 100644 --- a/panda/src/gobj/qpgeomMunger.h +++ b/panda/src/gobj/qpgeomMunger.h @@ -23,6 +23,7 @@ #include "typedReferenceCount.h" #include "qpgeomVertexFormat.h" #include "qpgeomVertexData.h" +#include "qpgeomCacheEntry.h" #include "indirectCompareTo.h" #include "pStatCollector.h" #include "pointerTo.h" @@ -59,6 +60,8 @@ class qpGeom; class EXPCL_PANDA qpGeomMunger : public TypedReferenceCount { public: qpGeomMunger(const GraphicsStateGuardianBase *gsg, const RenderState *state); + qpGeomMunger(const qpGeomMunger ©); + void operator = (const qpGeomMunger ©); virtual ~qpGeomMunger(); INLINE bool is_registered() const; @@ -94,6 +97,14 @@ private: void do_unregister(); private: + class CacheEntry : public qpGeomCacheEntry { + public: + virtual int get_result_size() const; + virtual void output(ostream &out) const; + + PT(qpGeomMunger) _munger; + }; + typedef pmap Formats; Formats _formats; @@ -108,6 +119,15 @@ private: Mungers _mungers; }; + // We store the iterator into the above registry, while we are + // registered. This makes it easier to remove our own entry, + // especially when the destructor is called. Since it's a virtual + // destructor, we can't reliably look up our pointer in the map once + // we have reached the base class destructor (since the object has + // changed types by then, and the sorting in the map depends partly + // on type). + Mungers::iterator _registered_key; + static Registry *_registry; static PStatCollector _munge_pcollector; diff --git a/panda/src/gobj/qpgeomPrimitive.cxx b/panda/src/gobj/qpgeomPrimitive.cxx index bb251d3735..996bd40969 100644 --- a/panda/src/gobj/qpgeomPrimitive.cxx +++ b/panda/src/gobj/qpgeomPrimitive.cxx @@ -21,7 +21,6 @@ #include "qpgeomVertexData.h" #include "qpgeomVertexArrayFormat.h" #include "qpgeomVertexDataType.h" -#include "qpgeomVertexCacheManager.h" #include "preparedGraphicsObjects.h" #include "internalName.h" #include "bamReader.h" @@ -66,16 +65,13 @@ qpGeomPrimitive:: // When we destruct, we should ensure that all of our cached // entries, across all pipeline stages, are properly removed from // the cache manager. - qpGeomVertexCacheManager *cache_mgr = - qpGeomVertexCacheManager::get_global_ptr(); - int num_stages = _cycler.get_num_stages(); for (int i = 0; i < num_stages; i++) { if (_cycler.is_stage_unique(i)) { CData *cdata = _cycler.write_stage(i); - if (cdata->_decomposed != (qpGeomPrimitive *)NULL) { - cache_mgr->remove_primitive(this); - cdata->_decomposed = NULL; + if (cdata->_cache != (CacheEntry *)NULL) { + cdata->_cache->erase(); + cdata->_cache = NULL; } _cycler.release_write_stage(i, cdata); } @@ -446,14 +442,12 @@ decompose() const { // call to record_primitive() might recursively call back into // this object, and require a write. const CData *cdata = _cycler.read(); - if (cdata->_decomposed != (qpGeomPrimitive *)NULL) { - result = cdata->_decomposed; + if (cdata->_cache != (CacheEntry *)NULL) { + result = cdata->_cache->_decomposed; _cycler.release_read(cdata); // Record a cache hit, so this element will stay in the cache a // while longer. - qpGeomVertexCacheManager *cache_mgr = - qpGeomVertexCacheManager::get_global_ptr(); - cache_mgr->record_primitive(this, result->get_num_bytes()); + cdata->_cache->refresh(); return result; } @@ -472,13 +466,17 @@ decompose() const { } // Record the result for the future. - CDWriter cdata(((qpGeomPrimitive *)this)->_cycler); - cdata->_decomposed = result; + CacheEntry *entry; + { + CDWriter cdata(((qpGeomPrimitive *)this)->_cycler); + entry = new CacheEntry; + entry->_source = (qpGeomPrimitive *)this; + entry->_decomposed = result; + cdata->_cache = entry; + } // And add *this* object to the cache manager. - qpGeomVertexCacheManager *cache_mgr = - qpGeomVertexCacheManager::get_global_ptr(); - cache_mgr->record_primitive(this, result->get_num_bytes()); + entry->record(); return result; } @@ -526,13 +524,10 @@ void qpGeomPrimitive:: clear_cache() { // Probably we shouldn't do anything at all here unless we are // running in pipeline stage 0. - qpGeomVertexCacheManager *cache_mgr = - qpGeomVertexCacheManager::get_global_ptr(); - CData *cdata = CDWriter(_cycler); - if (cdata->_decomposed != (qpGeomPrimitive *)NULL) { - cache_mgr->remove_primitive(this); - cdata->_decomposed = NULL; + if (cdata->_cache != (CacheEntry *)NULL) { + cdata->_cache->erase(); + cdata->_cache = NULL; } // This, on the other hand, should be applied to the current @@ -779,24 +774,6 @@ do_rotate() { cdataw->_rotated_vertices = rotated_vertices; } -//////////////////////////////////////////////////////////////////// -// Function: qpGeomPrimitive::remove_cache_entry -// Access: Private -// Description: Removes a particular entry from the local cache; it -// has already been removed from the cache manager. -// This is only called from GeomVertexCacheManager. -//////////////////////////////////////////////////////////////////// -void qpGeomPrimitive:: -remove_cache_entry() const { - // We have to operate on stage 0 of the pipeline, since that's where - // the cache really counts. Because of the multistage pipeline, we - // might not actually have a cache entry there (it might have been - // added to stage 1 instead). No big deal if we don't. - CData *cdata = ((qpGeomPrimitive *)this)->_cycler.write_stage(0); - cdata->_decomposed = NULL; - ((qpGeomPrimitive *)this)->_cycler.release_write_stage(0, cdata); -} - //////////////////////////////////////////////////////////////////// // Function: qpGeomPrimitive::recompute_minmax // Access: Private @@ -896,6 +873,46 @@ fillin(DatagramIterator &scan, BamReader *manager) { manager->read_cdata(scan, _cycler); } +//////////////////////////////////////////////////////////////////// +// Function: qpGeomPrimitive::CacheEntry::evict_callback +// Access: Public, Virtual +// Description: Called when the entry is evicted from the cache, this +// should clean up the owning object appropriately. +//////////////////////////////////////////////////////////////////// +void qpGeomPrimitive::CacheEntry:: +evict_callback() { + // We have to operate on stage 0 of the pipeline, since that's where + // the cache really counts. Because of the multistage pipeline, we + // might not actually have a cache entry there (it might have been + // added to stage 1 instead). No big deal if we don't. + CData *cdata = _source->_cycler.write_stage(0); + if (cdata->_cache == this) { + cdata->_cache = NULL; + } + _source->_cycler.release_write_stage(0, cdata); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomPrimitive::CacheEntry::get_result_size +// Access: Public, Virtual +// Description: Returns the approximate number of bytes represented +// by the computed result. +//////////////////////////////////////////////////////////////////// +int qpGeomPrimitive::CacheEntry:: +get_result_size() const { + return _decomposed->get_num_bytes(); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomPrimitive::CacheEntry::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void qpGeomPrimitive::CacheEntry:: +output(ostream &out) const { + out << "primitive " << (void *)_source; +} + //////////////////////////////////////////////////////////////////// // Function: qpGeomPrimitive::CData::make_copy // Access: Public, Virtual diff --git a/panda/src/gobj/qpgeomPrimitive.h b/panda/src/gobj/qpgeomPrimitive.h index 358c93d8ec..d8bd4603a0 100644 --- a/panda/src/gobj/qpgeomPrimitive.h +++ b/panda/src/gobj/qpgeomPrimitive.h @@ -21,6 +21,7 @@ #include "pandabase.h" #include "qpgeomUsageHint.h" +#include "qpgeomCacheEntry.h" #include "typedWritableReferenceCount.h" #include "luse.h" #include "updateSeq.h" @@ -163,7 +164,6 @@ protected: private: void do_rotate(); - void remove_cache_entry() const; protected: static PStatCollector _rotate_pcollector; @@ -179,6 +179,17 @@ private: typedef pmap Contexts; Contexts _contexts; + class CacheEntry : public qpGeomCacheEntry { + public: + virtual void evict_callback(); + virtual int get_result_size() const; + virtual void output(ostream &out) const; + + qpGeomPrimitive *_source; + CPT(qpGeomPrimitive) _decomposed; + }; + + // This is the data that must be cycled between pipeline stages. class EXPCL_PANDA CData : public CycleData { public: @@ -201,7 +212,7 @@ private: unsigned short _min_vertex; unsigned short _max_vertex; - CPT(qpGeomPrimitive) _decomposed; + PT(CacheEntry) _cache; }; PipelineCycler _cycler; @@ -234,7 +245,6 @@ private: static TypeHandle _type_handle; friend class qpGeom; - friend class qpGeomVertexCacheManager; friend class PreparedGraphicsObjects; }; diff --git a/panda/src/gobj/qpgeomVertexArrayData.h b/panda/src/gobj/qpgeomVertexArrayData.h index 779ec7851e..6fa204fbf8 100644 --- a/panda/src/gobj/qpgeomVertexArrayData.h +++ b/panda/src/gobj/qpgeomVertexArrayData.h @@ -141,7 +141,7 @@ public: private: static TypeHandle _type_handle; - friend class qpGeomVertexCacheManager; + friend class qpGeomCacheManager; friend class PreparedGraphicsObjects; }; diff --git a/panda/src/gobj/qpgeomVertexCacheManager.I b/panda/src/gobj/qpgeomVertexCacheManager.I deleted file mode 100644 index 9c615990a2..0000000000 --- a/panda/src/gobj/qpgeomVertexCacheManager.I +++ /dev/null @@ -1,303 +0,0 @@ -// Filename: qpgeomVertexCacheManager.I -// Created by: drose (11Mar05) -// -//////////////////////////////////////////////////////////////////// -// -// PANDA 3D SOFTWARE -// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved -// -// All use of this software is subject to the terms of the Panda 3d -// Software license. You should have received a copy of this license -// along with this source code; you will also find a current copy of -// the license at http://etc.cmu.edu/panda3d/docs/license/ . -// -// To contact the maintainers of this program write to -// panda3d-general@lists.sourceforge.net . -// -//////////////////////////////////////////////////////////////////// - - -//////////////////////////////////////////////////////////////////// -// Function: qpGeomVertexCacheManager::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. -//////////////////////////////////////////////////////////////////// -INLINE void qpGeomVertexCacheManager:: -set_max_size(int max_size) const { - // We directly change the config variable. - vertex_convert_cache = max_size; -} - -//////////////////////////////////////////////////////////////////// -// Function: qpGeomVertexCacheManager::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(). -//////////////////////////////////////////////////////////////////// -INLINE int qpGeomVertexCacheManager:: -get_max_size() const { - return vertex_convert_cache; -} - -//////////////////////////////////////////////////////////////////// -// Function: qpGeomVertexCacheManager::get_total_size -// Access: Published -// Description: Returns the amount of memory, in bytes, currently -// consumed by the cache of pre-processed vertex data. -//////////////////////////////////////////////////////////////////// -INLINE int qpGeomVertexCacheManager:: -get_total_size() const { - return _total_size; -} - -//////////////////////////////////////////////////////////////////// -// Function: qpGeomVertexCacheManager::record_munger -// Access: Private -// Description: Records a new GeomMunger in the cache, or marks a -// cache hit for a previously-recorded munger. This -// should only be called by GeomMunger. -// -// The cache manager will hold a reference on the -// GeomMunger pointer until it expires from the cache. -//////////////////////////////////////////////////////////////////// -INLINE void qpGeomVertexCacheManager:: -record_munger(const qpGeomMunger *munger) { - // Make up a nominal result_size for a munger. - record_entry(Entry(munger, 100)); -} - -//////////////////////////////////////////////////////////////////// -// Function: qpGeomVertexCacheManager::record_primitive -// Access: Private -// Description: Records a new entry in the cache, or marks a cache -// hit for a previous entry in the cache. This should -// only be called by GeomPrimitive. -// -// The cache manager will not hold a reference on the -// GeomPrimitive pointer; if it destructs, it should -// call remove_primitive() to remove itself from the cache. -//////////////////////////////////////////////////////////////////// -INLINE void qpGeomVertexCacheManager:: -record_primitive(const qpGeomPrimitive *primitive, int result_size) { - record_entry(Entry(primitive, result_size)); -} - -//////////////////////////////////////////////////////////////////// -// Function: qpGeomVertexCacheManager::remove_primitive -// Access: Private -// Description: Removes an entry from the cache, if it is there. -// Quietly ignores it if it is not. This should only be -// called by GeomPrimitive. -//////////////////////////////////////////////////////////////////// -INLINE void qpGeomVertexCacheManager:: -remove_primitive(const qpGeomPrimitive *primitive) { - remove_entry(Entry(primitive, 0)); -} - -//////////////////////////////////////////////////////////////////// -// Function: qpGeomVertexCacheManager::record_geom -// Access: Private -// Description: Records a new entry in the cache, or marks a cache -// hit for a previous entry in the cache. This should -// only be called by Geom. -// -// The cache manager will not hold a reference on the -// Geom pointer; if it destructs, it should call -// remove_geom() to remove itself from the cache. -//////////////////////////////////////////////////////////////////// -INLINE void qpGeomVertexCacheManager:: -record_geom(const qpGeom *source, const qpGeomMunger *modifier, - int result_size) { - record_entry(Entry(source, modifier, result_size)); -} - -//////////////////////////////////////////////////////////////////// -// Function: qpGeomVertexCacheManager::remove_geom -// Access: Private -// Description: Removes an entry from the cache, if it is there. -// Quietly ignores it if it is not. This should only be -// called by GeomVertexData. -//////////////////////////////////////////////////////////////////// -INLINE void qpGeomVertexCacheManager:: -remove_geom(const qpGeom *source, const qpGeomMunger *modifier) { - remove_entry(Entry(source, modifier, 0)); -} - -//////////////////////////////////////////////////////////////////// -// Function: qpGeomVertexCacheManager::dequeue_entry -// Access: Private -// Description: Removes an Entry record from the doubly-linked list. -//////////////////////////////////////////////////////////////////// -INLINE void qpGeomVertexCacheManager:: -dequeue_entry(qpGeomVertexCacheManager::Entry *entry) { - nassertv(entry->_prev->_next == entry && - entry->_next->_prev == entry); - entry->_prev->_next = entry->_next; - entry->_next->_prev = entry->_prev; -} - -//////////////////////////////////////////////////////////////////// -// Function: qpGeomVertexCacheManager::enqueue_entry -// Access: Private -// Description: Adds an Entry record to the tail of the doubly-linked -// list. -//////////////////////////////////////////////////////////////////// -INLINE void qpGeomVertexCacheManager:: -enqueue_entry(qpGeomVertexCacheManager::Entry *entry) { - nassertv(_list->_prev->_next == _list && - _list->_next->_prev == _list); - entry->_prev = _list->_prev; - entry->_next = _list; - entry->_prev->_next = entry; - _list->_prev = entry; -} - -//////////////////////////////////////////////////////////////////// -// Function: qpGeomVertexCacheManager::Entry::Constructor -// Access: Public -// Description: -//////////////////////////////////////////////////////////////////// -INLINE qpGeomVertexCacheManager::Entry:: -Entry() : - _cache_type(CT_none), - _result_size(0) -{ -} - -//////////////////////////////////////////////////////////////////// -// Function: qpGeomVertexCacheManager::Entry::Constructor -// Access: Public -// Description: -//////////////////////////////////////////////////////////////////// -INLINE qpGeomVertexCacheManager::Entry:: -Entry(const qpGeomMunger *munger, int result_size) : - _cache_type(CT_munger), - _result_size(result_size) -{ - _u._munger = munger; - _u._munger->ref(); -} - -//////////////////////////////////////////////////////////////////// -// Function: qpGeomVertexCacheManager::Entry::Constructor -// Access: Public -// Description: -//////////////////////////////////////////////////////////////////// -INLINE qpGeomVertexCacheManager::Entry:: -Entry(const qpGeomPrimitive *primitive, int result_size) : - _cache_type(CT_primitive), - _result_size(result_size) -{ - _u._primitive = primitive; -} - -//////////////////////////////////////////////////////////////////// -// Function: qpGeomVertexCacheManager::Entry::Constructor -// Access: Public -// Description: -//////////////////////////////////////////////////////////////////// -INLINE qpGeomVertexCacheManager::Entry:: -Entry(const qpGeom *source, const qpGeomMunger *modifier, - int result_size) : - _cache_type(CT_geom), - _result_size(result_size) -{ - _u._geom._source = source; - _u._geom._modifier = modifier; -} - -//////////////////////////////////////////////////////////////////// -// Function: qpGeomVertexCacheManager::Entry::Copy Constructor -// Access: Public -// Description: -//////////////////////////////////////////////////////////////////// -INLINE qpGeomVertexCacheManager::Entry:: -Entry(const qpGeomVertexCacheManager::Entry ©) : - _cache_type(copy._cache_type), - _result_size(copy._result_size) -{ - _u = copy._u; - if (_cache_type == CT_munger) { - _u._munger->ref(); - } -} - -//////////////////////////////////////////////////////////////////// -// Function: qpGeomVertexCacheManager::Entry::Copy Assignment Operator -// Access: Public -// Description: -//////////////////////////////////////////////////////////////////// -INLINE void qpGeomVertexCacheManager::Entry:: -operator = (const qpGeomVertexCacheManager::Entry ©) { - if (_cache_type == CT_munger) { - unref_delete(_u._munger); - } - _cache_type = copy._cache_type; - _result_size = copy._result_size; - _u = copy._u; - if (_cache_type == CT_munger) { - _u._munger->ref(); - } -} - -//////////////////////////////////////////////////////////////////// -// Function: qpGeomVertexCacheManager::Entry::Destructor -// Access: Public -// Description: -//////////////////////////////////////////////////////////////////// -INLINE qpGeomVertexCacheManager::Entry:: -~Entry() { - if (_cache_type == CT_munger) { - unref_delete(_u._munger); - } -} - -//////////////////////////////////////////////////////////////////// -// Function: qpGeomVertexCacheManager::Entry::operator < -// Access: Public -// Description: Provides a unique ordering for EntriesIndex. -//////////////////////////////////////////////////////////////////// -INLINE bool qpGeomVertexCacheManager::Entry:: -operator < (const qpGeomVertexCacheManager::Entry &other) const { - if (_cache_type != other._cache_type) { - return (int)_cache_type < (int)other._cache_type; - } - - switch (_cache_type) { - case CT_none: - // We shouldn't be adding the end-of-list token to the index. - nassertr(false, false); - return false; - - case CT_munger: - return _u._munger < other._u._munger; - - case CT_primitive: - return _u._primitive < other._u._primitive; - - case CT_geom: - if (_u._geom._source != other._u._geom._source) { - return _u._geom._source < other._u._geom._source; - } - if (_u._geom._modifier != other._u._geom._modifier) { - return _u._geom._modifier->geom_compare_to(*other._u._geom._modifier) < 0; - } - return 0; - } - - return false; -} - -INLINE ostream & -operator << (ostream &out, const qpGeomVertexCacheManager::Entry &entry) { - entry.output(out); - return out; -} - diff --git a/panda/src/gobj/qpgeomVertexCacheManager.cxx b/panda/src/gobj/qpgeomVertexCacheManager.cxx deleted file mode 100644 index 2c75cb0235..0000000000 --- a/panda/src/gobj/qpgeomVertexCacheManager.cxx +++ /dev/null @@ -1,196 +0,0 @@ -// Filename: qpgeomVertexCacheManager.cxx -// Created by: drose (11Mar05) -// -//////////////////////////////////////////////////////////////////// -// -// PANDA 3D SOFTWARE -// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved -// -// All use of this software is subject to the terms of the Panda 3d -// Software license. You should have received a copy of this license -// along with this source code; you will also find a current copy of -// the license at http://etc.cmu.edu/panda3d/docs/license/ . -// -// To contact the maintainers of this program write to -// panda3d-general@lists.sourceforge.net . -// -//////////////////////////////////////////////////////////////////// - -#include "qpgeomVertexCacheManager.h" -#include "mutexHolder.h" - -qpGeomVertexCacheManager *qpGeomVertexCacheManager::_global_ptr = NULL; - -//////////////////////////////////////////////////////////////////// -// Function: qpGeomVertexCacheManager::Constructor -// Access: Protected -// Description: -//////////////////////////////////////////////////////////////////// -qpGeomVertexCacheManager:: -qpGeomVertexCacheManager() : - _total_size(0) -{ - _list = new Entry; - _list->_next = _list; - _list->_prev = _list; -} - -//////////////////////////////////////////////////////////////////// -// Function: qpGeomVertexCacheManager::Destructor -// Access: Protected -// Description: -//////////////////////////////////////////////////////////////////// -qpGeomVertexCacheManager:: -~qpGeomVertexCacheManager() { - // Shouldn't be deleting this global object. - nassertv(false); -} - -//////////////////////////////////////////////////////////////////// -// Function: qpGeomVertexCacheManager::get_global_ptr -// Access: Published, Static -// Description: Returns the global cache manager pointer. -//////////////////////////////////////////////////////////////////// -qpGeomVertexCacheManager *qpGeomVertexCacheManager:: -get_global_ptr() { - if (_global_ptr == (qpGeomVertexCacheManager *)NULL) { - _global_ptr = new qpGeomVertexCacheManager; - } - return _global_ptr; -} - -//////////////////////////////////////////////////////////////////// -// Function: qpGeomVertexCacheManager::record_data -// Access: Private -// Description: Records a new generic entry in the cache, or marks a -// cache hit for a previous entry in the cache. -//////////////////////////////////////////////////////////////////// -void qpGeomVertexCacheManager:: -record_entry(const qpGeomVertexCacheManager::Entry &const_entry) { - MutexHolder holder(_lock); - - EntriesIndex::iterator ii = _entries_index.find((Entry *)&const_entry); - if (ii != _entries_index.end()) { - // We already had this entry in the cache. Refresh it. - if (gobj_cat.is_spam()) { - gobj_cat.spam() - << "refreshing cache entry: " << const_entry << "\n"; - } - - // Move the previous cache entry to the tail of the list. - Entry *entry = (*ii); - dequeue_entry(entry); - enqueue_entry(entry); - - _total_size += (const_entry._result_size - entry->_result_size); - entry->_result_size = const_entry._result_size; - - } else { - // There was no such entry already in the cache. Add it. - if (gobj_cat.is_debug()) { - gobj_cat.debug() - << "recording cache entry: " << const_entry << ", total_size = " - << _total_size + const_entry._result_size << "\n"; - } - - Entry *entry = new Entry(const_entry); - enqueue_entry(entry); - _total_size += entry->_result_size; - - // Also record an index entry. - bool inserted = _entries_index.insert(entry).second; - nassertv(inserted); - } - - // Now remove any old entries if our cache is over the limit. This may - // also remove the entry we just added, especially if our cache size - // is set to 0. - int max_size = get_max_size(); - while (_total_size > max_size) { - Entry *entry = _list->_next; - nassertv(entry != _list); - - if (gobj_cat.is_debug()) { - gobj_cat.debug() - << "cache total_size = " << _total_size << ", max_size = " - << max_size << ", removing " << *entry << "\n"; - } - - ii = _entries_index.find(entry); - nassertv(ii != _entries_index.end()); - - switch (entry->_cache_type) { - case CT_primitive: - entry->_u._primitive->remove_cache_entry(); - break; - - case CT_geom: - entry->_u._geom._source->remove_cache_entry - (entry->_u._geom._modifier); - break; - - default: - break; - } - _total_size -= entry->_result_size; - _entries_index.erase(ii); - - dequeue_entry(entry); - delete entry; - } -} - -//////////////////////////////////////////////////////////////////// -// Function: qpGeomVertexCacheManager::remove_entry -// Access: Private -// Description: Removes an entry from the cache, if it is there. -// Quietly ignores it if it is not. -//////////////////////////////////////////////////////////////////// -void qpGeomVertexCacheManager:: -remove_entry(const qpGeomVertexCacheManager::Entry &const_entry) { - if (gobj_cat.is_debug()) { - gobj_cat.debug() - << "remove_entry(" << const_entry << ")\n"; - } - MutexHolder holder(_lock); - - EntriesIndex::iterator ii = _entries_index.find((Entry *)&const_entry); - if (ii != _entries_index.end()) { - Entry *entry = (*ii); - _total_size -= entry->_result_size; - _entries_index.erase(ii); - dequeue_entry(entry); - delete entry; - } -} - -//////////////////////////////////////////////////////////////////// -// Function: qpGeomVertexCacheManager::Entry::output -// Access: Public -// Description: -//////////////////////////////////////////////////////////////////// -void qpGeomVertexCacheManager::Entry:: -output(ostream &out) const { - out << "[ "; - switch (_cache_type) { - case CT_none: - out << "end-of-list token"; - break; - - case CT_munger: - out << "munger " << (void *)_u._munger << ":" - << _u._munger->get_ref_count(); - break; - - case CT_primitive: - out << "primitive " << (void *)_u._primitive; - break; - - case CT_geom: - out << "geom " << (void *)_u._geom._source << ", " - << (void *)_u._geom._modifier; - break; - } - - out << ", result_size = " << _result_size << " ]"; -} diff --git a/panda/src/gobj/qpgeomVertexCacheManager.h b/panda/src/gobj/qpgeomVertexCacheManager.h deleted file mode 100644 index 5b6ec5cee0..0000000000 --- a/panda/src/gobj/qpgeomVertexCacheManager.h +++ /dev/null @@ -1,167 +0,0 @@ -// Filename: qpgeomVertexCacheManager.h -// Created by: drose (11Mar05) -// -//////////////////////////////////////////////////////////////////// -// -// PANDA 3D SOFTWARE -// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved -// -// All use of this software is subject to the terms of the Panda 3d -// Software license. You should have received a copy of this license -// along with this source code; you will also find a current copy of -// the license at http://etc.cmu.edu/panda3d/docs/license/ . -// -// To contact the maintainers of this program write to -// panda3d-general@lists.sourceforge.net . -// -//////////////////////////////////////////////////////////////////// - -#ifndef qpGEOMVERTEXCACHEMANAGER_H -#define qpGEOMVERTEXCACHEMANAGER_H - -#include "pandabase.h" -#include "qpgeomMunger.h" -#include "config_gobj.h" -#include "pointerTo.h" -#include "pmutex.h" -#include "indirectLess.h" -#include "plist.h" -#include "pmap.h" - -class qpGeom; -class qpGeomPrimitive; -class qpGeomVertexData; -class qpGeomVertexFormat; - -//////////////////////////////////////////////////////////////////// -// Class : qpGeomVertexCacheManager -// Description : This is used to keep track of, and limit the size of, -// the cache of munged vertices, which would otherwise -// be distributed through all of the GeomVertexData -// objects in the system. -// -// The actual data in the cache is not stored here, but -// rather it is distributed among the various -// GeomVertexData source objects. This allows the cache -// data to propagate through the multiprocess pipeline. -// -// This structure actually caches any of a number of -// different types of pointers, and mixes them all up in -// the same LRU cache list. Some of them (such as -// GeomMunger) are reference-counted here in the cache; -// most are not. -// -// This is part of the experimental Geom rewrite. -//////////////////////////////////////////////////////////////////// -class EXPCL_PANDA qpGeomVertexCacheManager { -protected: - qpGeomVertexCacheManager(); - ~qpGeomVertexCacheManager(); - -public: - class Entry; - -PUBLISHED: - INLINE void set_max_size(int max_size) const; - INLINE int get_max_size() const; - - INLINE int get_total_size() const; - - static qpGeomVertexCacheManager *get_global_ptr(); - -private: - INLINE void record_munger(const qpGeomMunger *munger); - INLINE void record_primitive(const qpGeomPrimitive *primitive, - int result_size); - INLINE void remove_primitive(const qpGeomPrimitive *primitive); - INLINE void record_geom(const qpGeom *source, - const qpGeomMunger *modifier, - int result_size); - INLINE void remove_geom(const qpGeom *source, - const qpGeomMunger *modifier); - - void record_entry(const Entry &const_entry); - void remove_entry(const Entry &const_entry); - - INLINE void dequeue_entry(Entry *entry); - INLINE void enqueue_entry(Entry *entry); - -private: - // This mutex protects all operations on this object. - Mutex _lock; - - int _total_size; - - enum CacheType { - CT_none, - CT_munger, - CT_primitive, - CT_geom, - }; - -public: - // This class is public only so we can declare the global ostream - // output operator. It doesn't need to be visible outside this - // class. It contains a single cache entry, which might actually be - // any of a handful of different pointer types. The enumerated type - // declared above, and the union declared below, serve to implement - // this C-style polymorphism. - class Entry { - public: - INLINE Entry(); - INLINE Entry(const qpGeomMunger *munger, int result_size); - INLINE Entry(const qpGeomPrimitive *primitive, int result_size); - INLINE Entry(const qpGeom *source, const qpGeomMunger *modifier, - int result_size); - INLINE Entry(const Entry ©); - INLINE void operator = (const Entry ©); - INLINE ~Entry(); - INLINE bool operator < (const Entry &other) const; - - void output(ostream &out) const; - - CacheType _cache_type; - int _result_size; - union { - const qpGeomMunger *_munger; - const qpGeomPrimitive *_primitive; - struct { - const qpGeom *_source; - const qpGeomMunger *_modifier; - } _geom; - } _u; - - Entry *_prev, *_next; - }; - -private: - // We maintain a doubly-linked list to keep the cache entries in - // least-recently-used order: the items at the head of the list are - // ready to be flushed. We use our own doubly-linked list instead - // of an STL list, just so we can avoid a tiny bit of overhead, - // especially with managing the pointer to the entry in - // _entries_index. - - // The tail and the head of the list are both kept by the _prev and - // _next pointers, respectively, within the following object, which - // always exists solely to keep a handle to the list. Keeping a - // token of the list this way avoids special cases for an empty - // list. - Entry *_list; - - // And this indexes into the above list, for fast lookup. - typedef pset > EntriesIndex; - EntriesIndex _entries_index; - - static qpGeomVertexCacheManager *_global_ptr; - - friend class qpGeomMunger; - friend class qpGeomPrimitive; - friend class qpGeom; -}; - -INLINE ostream &operator << (ostream &out, const qpGeomVertexCacheManager::Entry &entry); - -#include "qpgeomVertexCacheManager.I" - -#endif