simply geom caching system

This commit is contained in:
David Rose 2005-03-22 01:24:49 +00:00
parent 398186266d
commit 5ad57abaf5
23 changed files with 842 additions and 791 deletions

View File

@ -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);
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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;

View File

@ -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 \

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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<PT(qpGeomPrimitive) > 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<CPT(qpGeomMunger), MungeResult> MungedCache;
typedef pset<PT(CacheEntry), IndirectLess<CacheEntry> > 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<CData> _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);

View File

@ -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;
}

View File

@ -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 ]";
}

View File

@ -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

View File

@ -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;
}

View File

@ -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();
}
}

View File

@ -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

View File

@ -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 &copy) :
_is_registered(false)
{
_registered_key = get_registry()->_mungers.end();
}
////////////////////////////////////////////////////////////////////
// Function: qpGeomMunger::Copy Assignment Operator
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
void qpGeomMunger::
operator = (const qpGeomMunger &copy) {
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();
}

View File

@ -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 &copy);
void operator = (const qpGeomMunger &copy);
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<const qpGeomVertexFormat *, const qpGeomVertexFormat *> 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;

View File

@ -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

View File

@ -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<PreparedGraphicsObjects *, IndexBufferContext *> 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<CData> _cycler;
@ -234,7 +245,6 @@ private:
static TypeHandle _type_handle;
friend class qpGeom;
friend class qpGeomVertexCacheManager;
friend class PreparedGraphicsObjects;
};

View File

@ -141,7 +141,7 @@ public:
private:
static TypeHandle _type_handle;
friend class qpGeomVertexCacheManager;
friend class qpGeomCacheManager;
friend class PreparedGraphicsObjects;
};

View File

@ -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 &copy) :
_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 &copy) {
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;
}

View File

@ -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 << " ]";
}

View File

@ -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 &copy);
INLINE void operator = (const Entry &copy);
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<Entry *, IndirectLess<Entry> > 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