From be8b867498355045a9722f7e34ab15bd189c0c19 Mon Sep 17 00:00:00 2001 From: David Rose Date: Fri, 19 Oct 2007 15:37:19 +0000 Subject: [PATCH] released-vbuffer-cache --- panda/src/gobj/config_gobj.cxx | 18 ++ panda/src/gobj/config_gobj.h | 2 + panda/src/gobj/geomEnums.cxx | 64 +++++ panda/src/gobj/geomEnums.h | 4 +- panda/src/gobj/preparedGraphicsObjects.I | 34 +++ panda/src/gobj/preparedGraphicsObjects.cxx | 268 ++++++++++++++++----- panda/src/gobj/preparedGraphicsObjects.h | 40 ++- 7 files changed, 362 insertions(+), 68 deletions(-) diff --git a/panda/src/gobj/config_gobj.cxx b/panda/src/gobj/config_gobj.cxx index 5f0e149462..4beaf07010 100644 --- a/panda/src/gobj/config_gobj.cxx +++ b/panda/src/gobj/config_gobj.cxx @@ -250,6 +250,24 @@ ConfigVariableInt geom_cache_min_frames "object will remain in the geom cache, even if geom-cache-size " "is exceeded.")); +ConfigVariableInt released_vbuffer_cache_size +("released-vbuffer-cache-size", 1048576, + PRC_DESC("Specifies the size in bytes of the cache of vertex " + "buffers that have recently been released. If a new vertex " + "buffer is prepared while a recently-released one of the same " + "size is still in the cache, that same buffer is recycled. This " + "cuts down on the overhead of creating and destroying vertex " + "buffers on the graphics card.")); + +ConfigVariableInt released_ibuffer_cache_size +("released-ibuffer-cache-size", 102400, + PRC_DESC("Specifies the size in bytes of the cache of index " + "buffers that have recently been released. If a new index " + "buffer is prepared while a recently-released one of the same " + "size is still in the cache, that same buffer is recycled. This " + "cuts down on the overhead of creating and destroying index " + "buffers on the graphics card.")); + ConfigVariableDouble default_near ("default-near", 1.0, PRC_DESC("The default near clipping distance for all cameras.")); diff --git a/panda/src/gobj/config_gobj.h b/panda/src/gobj/config_gobj.h index 14e408fc20..84c87fd89c 100644 --- a/panda/src/gobj/config_gobj.h +++ b/panda/src/gobj/config_gobj.h @@ -63,6 +63,8 @@ extern EXPCL_PANDA_GOBJ ConfigVariableBool textures_header_only; extern EXPCL_PANDA_GOBJ ConfigVariableInt geom_cache_size; extern EXPCL_PANDA_GOBJ ConfigVariableInt geom_cache_min_frames; +extern EXPCL_PANDA_GOBJ ConfigVariableInt released_vbuffer_cache_size; +extern EXPCL_PANDA_GOBJ ConfigVariableInt released_ibuffer_cache_size; extern EXPCL_PANDA_GOBJ ConfigVariableDouble default_near; extern EXPCL_PANDA_GOBJ ConfigVariableDouble default_far; diff --git a/panda/src/gobj/geomEnums.cxx b/panda/src/gobj/geomEnums.cxx index 9f91c9c5d7..091f151619 100644 --- a/panda/src/gobj/geomEnums.cxx +++ b/panda/src/gobj/geomEnums.cxx @@ -17,8 +17,68 @@ //////////////////////////////////////////////////////////////////// #include "geomEnums.h" +#include "string_utils.h" +#include "config_gobj.h" +//////////////////////////////////////////////////////////////////// +// Function: GeomEnums::UsageHint output operator +// Description: +//////////////////////////////////////////////////////////////////// +ostream & +operator << (ostream &out, GeomEnums::UsageHint usage_hint) { + switch (usage_hint) { + case GeomEnums::UH_client: + return out << "client"; + + case GeomEnums::UH_stream: + return out << "stream"; + + case GeomEnums::UH_dynamic: + return out << "dynamic"; + + case GeomEnums::UH_static: + return out << "static"; + + case GeomEnums::UH_unspecified: + return out << "unspecified"; + } + + return out << "**invalid usage hint (" << (int)usage_hint << ")**"; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomEnums::UsageHint input operator +// Description: +//////////////////////////////////////////////////////////////////// +istream & +operator >> (istream &in, GeomEnums::UsageHint &usage_hint) { + string word; + in >> word; + + if (cmp_nocase(word, "client") == 0) { + usage_hint = GeomEnums::UH_client; + } else if (cmp_nocase(word, "stream") == 0) { + usage_hint = GeomEnums::UH_stream; + } else if (cmp_nocase(word, "dynamic") == 0) { + usage_hint = GeomEnums::UH_dynamic; + } else if (cmp_nocase(word, "static") == 0) { + usage_hint = GeomEnums::UH_static; + } else if (cmp_nocase(word, "unspecified") == 0) { + usage_hint = GeomEnums::UH_unspecified; + + } else { + gobj_cat->error() << "Invalid usage hint value: " << word << "\n"; + usage_hint = GeomEnums::UH_unspecified; + } + + return in; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomEnums::NumericType output operator +// Description: +//////////////////////////////////////////////////////////////////// ostream & operator << (ostream &out, GeomEnums::NumericType numeric_type) { switch (numeric_type) { @@ -44,6 +104,10 @@ operator << (ostream &out, GeomEnums::NumericType numeric_type) { return out << "**invalid numeric type (" << (int)numeric_type << ")**"; } +//////////////////////////////////////////////////////////////////// +// Function: GeomEnums::Contents output operator +// Description: +//////////////////////////////////////////////////////////////////// ostream & operator << (ostream &out, GeomEnums::Contents contents) { switch (contents) { diff --git a/panda/src/gobj/geomEnums.h b/panda/src/gobj/geomEnums.h index a0dc3af6a6..97d098e2d0 100644 --- a/panda/src/gobj/geomEnums.h +++ b/panda/src/gobj/geomEnums.h @@ -67,7 +67,7 @@ PUBLISHED: UH_static, // UH_unspecified: the usage is unspecified. This is intended as - // a "don't care" option for abstract objects, it should not be + // a "don't care" option for abstract objects; it should not be // applied to any actual geometry to be rendered. You take your // chances if a geom actually gets into the scene graph with this // set. @@ -211,6 +211,8 @@ PUBLISHED: }; }; +EXPCL_PANDA_GOBJ ostream &operator << (ostream &out, GeomEnums::UsageHint usage_hint); +EXPCL_PANDA_GOBJ istream &operator >> (istream &in, GeomEnums::UsageHint &usage_hint); EXPCL_PANDA_GOBJ ostream &operator << (ostream &out, GeomEnums::NumericType numeric_type); EXPCL_PANDA_GOBJ ostream &operator << (ostream &out, GeomEnums::Contents contents); diff --git a/panda/src/gobj/preparedGraphicsObjects.I b/panda/src/gobj/preparedGraphicsObjects.I index fbfe779d4a..09845be1f7 100644 --- a/panda/src/gobj/preparedGraphicsObjects.I +++ b/panda/src/gobj/preparedGraphicsObjects.I @@ -72,3 +72,37 @@ get_num_prepared() const { get_num_prepared_vertex_buffers() + get_num_prepared_index_buffers()); } + +//////////////////////////////////////////////////////////////////// +// Function: PreparedGraphicsObjects::BufferCacheKey::operator < +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool PreparedGraphicsObjects::BufferCacheKey:: +operator < (const PreparedGraphicsObjects::BufferCacheKey &other) const { + if (_data_size_bytes != other._data_size_bytes) { + return _data_size_bytes < other._data_size_bytes; + } + return (int)_usage_hint < (int)other._usage_hint; +} + +//////////////////////////////////////////////////////////////////// +// Function: PreparedGraphicsObjects::BufferCacheKey::operator == +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool PreparedGraphicsObjects::BufferCacheKey:: +operator == (const PreparedGraphicsObjects::BufferCacheKey &other) const { + return (_data_size_bytes == other._data_size_bytes && + _usage_hint == other._usage_hint); +} + +//////////////////////////////////////////////////////////////////// +// Function: PreparedGraphicsObjects::BufferCacheKey::operator != +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool PreparedGraphicsObjects::BufferCacheKey:: +operator != (const PreparedGraphicsObjects::BufferCacheKey &other) const { + return !operator == (other); +} diff --git a/panda/src/gobj/preparedGraphicsObjects.cxx b/panda/src/gobj/preparedGraphicsObjects.cxx index b09a3fc59a..83ea8e39bd 100644 --- a/panda/src/gobj/preparedGraphicsObjects.cxx +++ b/panda/src/gobj/preparedGraphicsObjects.cxx @@ -42,7 +42,9 @@ PreparedGraphicsObjects() : _name(init_name()), _texture_residency(_name, "texture"), _vbuffer_residency(_name, "vbuffer"), - _ibuffer_residency(_name, "ibuffer") + _ibuffer_residency(_name, "ibuffer"), + _vertex_buffer_cache_size(0), + _index_buffer_cache_size(0) { } @@ -59,68 +61,41 @@ PreparedGraphicsObjects:: // cleaned up. Quietly erase these remaining objects. ReMutexHolder holder(_lock); + release_all_textures(); Textures::iterator tci; - for (tci = _prepared_textures.begin(); - tci != _prepared_textures.end(); + for (tci = _released_textures.begin(); + tci != _released_textures.end(); ++tci) { TextureContext *tc = (*tci); - tc->get_texture()->clear_prepared(this); tc->set_owning_chain(NULL); } - - _prepared_textures.clear(); _released_textures.clear(); - _enqueued_textures.clear(); - Geoms::iterator gci; - for (gci = _prepared_geoms.begin(); - gci != _prepared_geoms.end(); - ++gci) { - GeomContext *gc = (*gci); - gc->_geom->clear_prepared(this); - } - - _prepared_geoms.clear(); + release_all_geoms(); _released_geoms.clear(); - _enqueued_geoms.clear(); - Shaders::iterator sci; - for (sci = _prepared_shaders.begin(); - sci != _prepared_shaders.end(); - ++sci) { - ShaderContext *sc = (*sci); - sc->_expansion->clear_prepared(this); - } - - _prepared_shaders.clear(); + release_all_shaders(); _released_shaders.clear(); - _enqueued_shaders.clear(); - VertexBuffers::iterator vbci; - for (vbci = _prepared_vertex_buffers.begin(); - vbci != _prepared_vertex_buffers.end(); + release_all_vertex_buffers(); + Buffers::iterator vbci; + for (vbci = _released_vertex_buffers.begin(); + vbci != _released_vertex_buffers.end(); ++vbci) { - VertexBufferContext *vbc = (*vbci); - vbc->_data->clear_prepared(this); + VertexBufferContext *vbc = (VertexBufferContext *)(*vbci); vbc->set_owning_chain(NULL); } - - _prepared_vertex_buffers.clear(); _released_vertex_buffers.clear(); - _enqueued_vertex_buffers.clear(); - IndexBuffers::iterator ibci; - for (ibci = _prepared_index_buffers.begin(); - ibci != _prepared_index_buffers.end(); + release_all_index_buffers(); + Buffers::iterator ibci; + for (ibci = _released_index_buffers.begin(); + ibci != _released_index_buffers.end(); ++ibci) { - IndexBufferContext *ibc = (*ibci); - ibc->_data->clear_prepared(this); + IndexBufferContext *ibc = (IndexBufferContext *)(*ibci); ibc->set_owning_chain(NULL); } - - _prepared_index_buffers.clear(); _released_index_buffers.clear(); - _enqueued_index_buffers.clear(); } @@ -772,6 +747,9 @@ release_vertex_buffer(VertexBufferContext *vbc) { vbc->_data->clear_prepared(this); + size_t data_size_bytes = vbc->_data->get_data_size_bytes(); + GeomEnums::UsageHint usage_hint = vbc->_data->get_usage_hint(); + // We have to set the Data pointer to NULL at this point, since // the Data itself might destruct at any time after it has been // released. @@ -780,7 +758,11 @@ release_vertex_buffer(VertexBufferContext *vbc) { bool removed = (_prepared_vertex_buffers.erase(vbc) != 0); nassertv(removed); - _released_vertex_buffers.insert(vbc); + cache_unprepared_buffer(vbc, data_size_bytes, usage_hint, + _vertex_buffer_cache, + _vertex_buffer_cache_lru, _vertex_buffer_cache_size, + released_vbuffer_cache_size, + _released_vertex_buffers); } //////////////////////////////////////////////////////////////////// @@ -797,11 +779,11 @@ release_all_vertex_buffers() { int num_vertex_buffers = (int)_prepared_vertex_buffers.size() + (int)_enqueued_vertex_buffers.size(); - VertexBuffers::iterator vbci; + Buffers::iterator vbci; for (vbci = _prepared_vertex_buffers.begin(); vbci != _prepared_vertex_buffers.end(); ++vbci) { - VertexBufferContext *vbc = (*vbci); + VertexBufferContext *vbc = (VertexBufferContext *)(*vbci); vbc->_data->clear_prepared(this); vbc->_data = (GeomVertexArrayData *)NULL; @@ -811,6 +793,23 @@ release_all_vertex_buffers() { _prepared_vertex_buffers.clear(); _enqueued_vertex_buffers.clear(); + // Also clear the cache of recently-unprepared vertex buffers. + BufferCache::iterator bci; + for (bci = _vertex_buffer_cache.begin(); + bci != _vertex_buffer_cache.end(); + ++bci) { + BufferList &buffer_list = (*bci).second; + nassertr(!buffer_list.empty(), num_vertex_buffers); + BufferList::iterator li; + for (li = buffer_list.begin(); li != buffer_list.end(); ++li) { + VertexBufferContext *vbc = (VertexBufferContext *)(*li); + _released_vertex_buffers.insert(vbc); + } + } + _vertex_buffer_cache.clear(); + _vertex_buffer_cache_lru.clear(); + _vertex_buffer_cache_size = 0; + return num_vertex_buffers; } @@ -862,11 +861,24 @@ VertexBufferContext *PreparedGraphicsObjects:: prepare_vertex_buffer_now(GeomVertexArrayData *data, GraphicsStateGuardianBase *gsg) { ReMutexHolder holder(_lock); - // Ask the GSG to create a brand new VertexBufferContext. There might - // be several GSG's sharing the same set of datas; if so, it - // doesn't matter which of them creates the context (since they're - // all shared anyway). - VertexBufferContext *vbc = gsg->prepare_vertex_buffer(data); + // First, see if there might be a cached context of the appropriate + // size. + size_t data_size_bytes = data->get_data_size_bytes(); + GeomEnums::UsageHint usage_hint = data->get_usage_hint(); + VertexBufferContext *vbc = (VertexBufferContext *) + get_cached_buffer(data_size_bytes, usage_hint, + _vertex_buffer_cache, _vertex_buffer_cache_lru, + _vertex_buffer_cache_size); + if (vbc != (VertexBufferContext *)NULL) { + vbc->_data = data; + + } else { + // Ask the GSG to create a brand new VertexBufferContext. There + // might be several GSG's sharing the same set of datas; if so, it + // doesn't matter which of them creates the context (since they're + // all shared anyway). + vbc = gsg->prepare_vertex_buffer(data); + } if (vbc != (VertexBufferContext *)NULL) { bool prepared = _prepared_vertex_buffers.insert(vbc).second; @@ -960,6 +972,9 @@ release_index_buffer(IndexBufferContext *ibc) { ibc->_data->clear_prepared(this); + size_t data_size_bytes = ibc->_data->get_data_size_bytes(); + GeomEnums::UsageHint usage_hint = ibc->_data->get_usage_hint(); + // We have to set the Data pointer to NULL at this point, since // the Data itself might destruct at any time after it has been // released. @@ -968,7 +983,11 @@ release_index_buffer(IndexBufferContext *ibc) { bool removed = (_prepared_index_buffers.erase(ibc) != 0); nassertv(removed); - _released_index_buffers.insert(ibc); + cache_unprepared_buffer(ibc, data_size_bytes, usage_hint, + _index_buffer_cache, + _index_buffer_cache_lru, _index_buffer_cache_size, + released_ibuffer_cache_size, + _released_index_buffers); } //////////////////////////////////////////////////////////////////// @@ -985,11 +1004,11 @@ release_all_index_buffers() { int num_index_buffers = (int)_prepared_index_buffers.size() + (int)_enqueued_index_buffers.size(); - IndexBuffers::iterator ibci; + Buffers::iterator ibci; for (ibci = _prepared_index_buffers.begin(); ibci != _prepared_index_buffers.end(); ++ibci) { - IndexBufferContext *ibc = (*ibci); + IndexBufferContext *ibc = (IndexBufferContext *)(*ibci); ibc->_data->clear_prepared(this); ibc->_data = (GeomPrimitive *)NULL; @@ -999,6 +1018,23 @@ release_all_index_buffers() { _prepared_index_buffers.clear(); _enqueued_index_buffers.clear(); + // Also clear the cache of recently-unprepared index buffers. + BufferCache::iterator bci; + for (bci = _index_buffer_cache.begin(); + bci != _index_buffer_cache.end(); + ++bci) { + BufferList &buffer_list = (*bci).second; + nassertr(!buffer_list.empty(), num_index_buffers); + BufferList::iterator li; + for (li = buffer_list.begin(); li != buffer_list.end(); ++li) { + IndexBufferContext *vbc = (IndexBufferContext *)(*li); + _released_index_buffers.insert(vbc); + } + } + _index_buffer_cache.clear(); + _index_buffer_cache_lru.clear(); + _index_buffer_cache_size = 0; + return num_index_buffers; } @@ -1050,11 +1086,24 @@ IndexBufferContext *PreparedGraphicsObjects:: prepare_index_buffer_now(GeomPrimitive *data, GraphicsStateGuardianBase *gsg) { ReMutexHolder holder(_lock); - // Ask the GSG to create a brand new IndexBufferContext. There might - // be several GSG's sharing the same set of datas; if so, it - // doesn't matter which of them creates the context (since they're - // all shared anyway). - IndexBufferContext *ibc = gsg->prepare_index_buffer(data); + // First, see if there might be a cached context of the appropriate + // size. + size_t data_size_bytes = data->get_data_size_bytes(); + GeomEnums::UsageHint usage_hint = data->get_usage_hint(); + IndexBufferContext *ibc = (IndexBufferContext *) + get_cached_buffer(data_size_bytes, usage_hint, + _index_buffer_cache, _index_buffer_cache_lru, + _index_buffer_cache_size); + if (ibc != (IndexBufferContext *)NULL) { + ibc->_data = data; + + } else { + // Ask the GSG to create a brand new IndexBufferContext. There + // might be several GSG's sharing the same set of datas; if so, it + // doesn't matter which of them creates the context (since they're + // all shared anyway). + ibc = gsg->prepare_index_buffer(data); + } if (ibc != (IndexBufferContext *)NULL) { bool prepared = _prepared_index_buffers.insert(ibc).second; @@ -1114,21 +1163,21 @@ begin_frame(GraphicsStateGuardianBase *gsg, Thread *current_thread) { _released_shaders.clear(); - VertexBuffers::iterator vbci; + Buffers::iterator vbci; for (vbci = _released_vertex_buffers.begin(); vbci != _released_vertex_buffers.end(); ++vbci) { - VertexBufferContext *vbc = (*vbci); + VertexBufferContext *vbc = (VertexBufferContext *)(*vbci); gsg->release_vertex_buffer(vbc); } _released_vertex_buffers.clear(); - IndexBuffers::iterator ibci; + Buffers::iterator ibci; for (ibci = _released_index_buffers.begin(); ibci != _released_index_buffers.end(); ++ibci) { - IndexBufferContext *ibc = (*ibci); + IndexBufferContext *ibc = (IndexBufferContext *)(*ibci); gsg->release_index_buffer(ibc); } @@ -1221,3 +1270,96 @@ init_name() { strm << "context" << _name_index; return strm.str(); } + +//////////////////////////////////////////////////////////////////// +// Function: PreparedGraphicsObjects::cache_unprepared_buffer +// Access: Private +// Description: Called when a vertex or index buffer is no longer +// officially "prepared". However, we still have the +// context on the graphics card, and we might be able to +// reuse that context if we're about to re-prepare a +// different buffer, especially one exactly the same +// size. So instead of immediately enqueuing the vertex +// buffer for release, we cache it. +//////////////////////////////////////////////////////////////////// +void PreparedGraphicsObjects:: +cache_unprepared_buffer(BufferContext *buffer, size_t data_size_bytes, + GeomEnums::UsageHint usage_hint, + PreparedGraphicsObjects::BufferCache &buffer_cache, + PreparedGraphicsObjects::BufferCacheLRU &buffer_cache_lru, + size_t &buffer_cache_size, + int released_buffer_cache_size, + PreparedGraphicsObjects::Buffers &released_buffers) { + BufferCacheKey key; + key._data_size_bytes = data_size_bytes; + key._usage_hint = usage_hint; + + buffer_cache[key].push_back(buffer); + buffer_cache_size += data_size_bytes; + + // Move the key to the head of the LRU. + BufferCacheLRU::iterator li = + find(buffer_cache_lru.begin(), buffer_cache_lru.end(), key); + if (li != buffer_cache_lru.end()) { + buffer_cache_lru.erase(li); + } + buffer_cache_lru.insert(buffer_cache_lru.begin(), key); + + // Now release not-recently-used buffers until we fit within the + // constrained size. + while ((int)buffer_cache_size > released_buffer_cache_size) { + nassertv(!buffer_cache_lru.empty()); + const BufferCacheKey &release_key = *buffer_cache_lru.rbegin(); + BufferList &buffer_list = buffer_cache[release_key]; + while (!buffer_list.empty() && + (int)buffer_cache_size > released_buffer_cache_size) { + BufferContext *released_buffer = buffer_list.back(); + buffer_list.pop_back(); + released_buffers.insert(released_buffer); + buffer_cache_size -= release_key._data_size_bytes; + } + + if (buffer_list.empty()) { + buffer_cache.erase(release_key); + buffer_cache_lru.pop_back(); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PreparedGraphicsObjects::get_cached_buffer +// Access: Private +// Description: Returns a previously-cached buffer from the cache, or +// NULL if there is no such buffer. +//////////////////////////////////////////////////////////////////// +BufferContext *PreparedGraphicsObjects:: +get_cached_buffer(size_t data_size_bytes, GeomEnums::UsageHint usage_hint, + PreparedGraphicsObjects::BufferCache &buffer_cache, + PreparedGraphicsObjects::BufferCacheLRU &buffer_cache_lru, + size_t &buffer_cache_size) { + BufferCacheKey key; + key._data_size_bytes = data_size_bytes; + key._usage_hint = usage_hint; + + BufferCache::iterator bci = buffer_cache.find(key); + if (bci == buffer_cache.end()) { + return NULL; + } + + BufferList &buffer_list = (*bci).second; + nassertr(!buffer_list.empty(), NULL); + + BufferContext *buffer = buffer_list.back(); + buffer_list.pop_back(); + if (buffer_list.empty()) { + buffer_cache.erase(bci); + BufferCacheLRU::iterator li = + find(buffer_cache_lru.begin(), buffer_cache_lru.end(), key); + if (li != buffer_cache_lru.end()) { + buffer_cache_lru.erase(li); + } + } + + buffer_cache_size -= data_size_bytes; + return buffer; +} diff --git a/panda/src/gobj/preparedGraphicsObjects.h b/panda/src/gobj/preparedGraphicsObjects.h index 4543a98cbf..3a8360c346 100644 --- a/panda/src/gobj/preparedGraphicsObjects.h +++ b/panda/src/gobj/preparedGraphicsObjects.h @@ -144,11 +144,35 @@ private: typedef phash_set< PT(Geom) > EnqueuedGeoms; typedef phash_set Shaders; typedef phash_set< PT(ShaderExpansion) > EnqueuedShaders; - typedef phash_set VertexBuffers; + typedef phash_set Buffers; typedef phash_set< PT(GeomVertexArrayData) > EnqueuedVertexBuffers; - typedef phash_set IndexBuffers; typedef phash_set< PT(GeomPrimitive) > EnqueuedIndexBuffers; + class BufferCacheKey { + public: + INLINE bool operator < (const BufferCacheKey &other) const; + INLINE bool operator == (const BufferCacheKey &other) const; + INLINE bool operator != (const BufferCacheKey &other) const; + size_t _data_size_bytes; + GeomEnums::UsageHint _usage_hint; + }; + typedef pvector BufferList; + typedef pmap BufferCache; + typedef plist BufferCacheLRU; + + void cache_unprepared_buffer(BufferContext *buffer, size_t data_size_bytes, + GeomEnums::UsageHint usage_hint, + BufferCache &buffer_cache, + BufferCacheLRU &buffer_cache_lru, + size_t &buffer_cache_size, + int released_buffer_cache_size, + Buffers &released_buffers); + BufferContext *get_cached_buffer(size_t data_size_bytes, + GeomEnums::UsageHint usage_hint, + BufferCache &buffer_cache, + BufferCacheLRU &buffer_cache_lru, + size_t &buffer_cache_size); + ReMutex _lock; string _name; Textures _prepared_textures, _released_textures; @@ -157,11 +181,19 @@ private: EnqueuedGeoms _enqueued_geoms; Shaders _prepared_shaders, _released_shaders; EnqueuedShaders _enqueued_shaders; - VertexBuffers _prepared_vertex_buffers, _released_vertex_buffers; + Buffers _prepared_vertex_buffers, _released_vertex_buffers; EnqueuedVertexBuffers _enqueued_vertex_buffers; - IndexBuffers _prepared_index_buffers, _released_index_buffers; + Buffers _prepared_index_buffers, _released_index_buffers; EnqueuedIndexBuffers _enqueued_index_buffers; + BufferCache _vertex_buffer_cache; + BufferCacheLRU _vertex_buffer_cache_lru; + size_t _vertex_buffer_cache_size; + + BufferCache _index_buffer_cache; + BufferCacheLRU _index_buffer_cache_lru; + size_t _index_buffer_cache_size; + public: BufferResidencyTracker _texture_residency; BufferResidencyTracker _vbuffer_residency;