diff --git a/panda/src/gobj/Sources.pp b/panda/src/gobj/Sources.pp index a1138c0661..4248516b2e 100644 --- a/panda/src/gobj/Sources.pp +++ b/panda/src/gobj/Sources.pp @@ -50,6 +50,7 @@ savedContext.I savedContext.h \ shaderContext.h shaderContext.I \ shaderExpansion.h shaderExpansion.I \ + simpleAllocator.h simpleAllocator.I \ simpleLru.h simpleLru.I \ sliderTable.I sliderTable.h \ texture.I texture.h \ @@ -63,6 +64,7 @@ userVertexSlider.I userVertexSlider.h \ userVertexTransform.I userVertexTransform.h \ vertexBufferContext.I vertexBufferContext.h \ + vertexDataSaveFile.I vertexDataSaveFile.h \ vertexSlider.I vertexSlider.h \ vertexTransform.I vertexTransform.h \ videoTexture.I videoTexture.h @@ -107,6 +109,7 @@ savedContext.cxx \ shaderContext.cxx \ shaderExpansion.cxx \ + simpleAllocator.cxx \ simpleLru.cxx \ sliderTable.cxx \ texture.cxx textureContext.cxx texturePool.cxx \ @@ -118,6 +121,7 @@ userVertexSlider.cxx \ userVertexTransform.cxx \ vertexBufferContext.cxx \ + vertexDataSaveFile.cxx \ vertexSlider.cxx \ vertexTransform.cxx \ videoTexture.cxx @@ -164,6 +168,7 @@ savedContext.I savedContext.h \ shaderContext.h shaderContext.I \ shaderExpansion.h shaderExpansion.I \ + simpleAllocator.h simpleAllocator.I \ simpleLru.h simpleLru.I \ sliderTable.I sliderTable.h \ texture.I texture.h \ @@ -177,6 +182,7 @@ userVertexSlider.I userVertexSlider.h \ userVertexTransform.I userVertexTransform.h \ vertexBufferContext.I vertexBufferContext.h \ + vertexDataSaveFile.I vertexDataSaveFile.h \ vertexSlider.I vertexSlider.h \ vertexTransform.I vertexTransform.h \ videoTexture.I videoTexture.h diff --git a/panda/src/gobj/config_gobj.cxx b/panda/src/gobj/config_gobj.cxx index 5b726ac0d7..45537d0163 100644 --- a/panda/src/gobj/config_gobj.cxx +++ b/panda/src/gobj/config_gobj.cxx @@ -270,6 +270,20 @@ ConfigVariableDouble default_keystone ("default-keystone", 0.0f, PRC_DESC("The default keystone correction, as an x y pair, for all cameras.")); +ConfigVariableFilename vertex_save_file_directory +("vertex-save-file-directory", "", + PRC_DESC("The directory in which the saved vertex data file is created " + "for saving vertex buffers that have been evicted from RAM. If " + "this is the empty string, or an invalid directory, a system " + "default directory will be chosen.")); + +ConfigVariableString vertex_save_file_prefix +("vertex-save-file-prefix", "p3d_vdata_", + PRC_DESC("A prefix used to generate the filename for the saved vertex " + "data file which is created for saving vertex buffers that have " + "been evicted from RAM. A uniquifying sequence number and " + "filename extension will be appended to this string.")); + ConfigureFn(config_gobj) { diff --git a/panda/src/gobj/config_gobj.h b/panda/src/gobj/config_gobj.h index 924990d4bf..2529b9b76a 100644 --- a/panda/src/gobj/config_gobj.h +++ b/panda/src/gobj/config_gobj.h @@ -25,6 +25,8 @@ #include "configVariableInt.h" #include "configVariableEnum.h" #include "configVariableDouble.h" +#include "configVariableFilename.h" +#include "configVariableString.h" NotifyCategoryDecl(gobj, EXPCL_PANDA, EXPTP_PANDA); @@ -68,6 +70,9 @@ extern ConfigVariableDouble default_iod; extern ConfigVariableDouble default_converge; extern ConfigVariableDouble default_keystone; +extern ConfigVariableFilename vertex_save_file_directory; +extern ConfigVariableString vertex_save_file_prefix; + #endif diff --git a/panda/src/gobj/geomEnums.h b/panda/src/gobj/geomEnums.h index 9ca67f0d54..8f0ea4d0b5 100644 --- a/panda/src/gobj/geomEnums.h +++ b/panda/src/gobj/geomEnums.h @@ -217,6 +217,7 @@ PUBLISHED: RC_resident, RC_compressed, RC_disk, + RC_compressed_disk, RC_end_of_list, // list marker; do not use }; diff --git a/panda/src/gobj/geomVertexArrayData.I b/panda/src/gobj/geomVertexArrayData.I index 4a91b2dcee..046cfc0014 100644 --- a/panda/src/gobj/geomVertexArrayData.I +++ b/panda/src/gobj/geomVertexArrayData.I @@ -199,7 +199,22 @@ get_ram_class() const { INLINE SimpleLru *GeomVertexArrayData:: get_global_lru(RamClass rclass) { nassertr(rclass >= 0 && rclass < RC_end_of_list, NULL); - return &_global_lru[rclass]; + return _global_lru[rclass]; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomVertexArrayData::get_save_file +// Access: Published, Static +// Description: Returns the global VertexDataSaveFile that will be +// used to save vertex data buffers to disk when +// necessary. +//////////////////////////////////////////////////////////////////// +INLINE VertexDataSaveFile *GeomVertexArrayData:: +get_save_file() { + if (_save_file == (VertexDataSaveFile *)NULL) { + make_save_file(); + } + return _save_file; } //////////////////////////////////////////////////////////////////// @@ -210,7 +225,7 @@ get_global_lru(RamClass rclass) { INLINE void GeomVertexArrayData:: set_ram_class(RamClass rclass) { _ram_class = rclass; - mark_used_lru(&_global_lru[rclass]); + mark_used_lru(_global_lru[rclass]); } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/gobj/geomVertexArrayData.cxx b/panda/src/gobj/geomVertexArrayData.cxx index b71ada7b45..4e2138815f 100644 --- a/panda/src/gobj/geomVertexArrayData.cxx +++ b/panda/src/gobj/geomVertexArrayData.cxx @@ -25,26 +25,35 @@ #include "pset.h" #include "config_gobj.h" #include "pStatTimer.h" +#include "configVariableInt.h" +#include "vertexDataSaveFile.h" +#include "simpleAllocator.h" #ifdef HAVE_ZLIB #include #endif ConfigVariableInt max_ram_vertex_data -("max-ram-vertex-data", 0, +("max-ram-vertex-data", -1, PRC_DESC("Specifies the maximum number of bytes of all vertex data " "that is allowed to remain resident in system RAM at one time. " "If more than this number of bytes of vertices are created, " "the least-recently-used ones will be temporarily compressed in " - "system RAM until they are needed. Set it to 0 for no limit.")); + "system RAM until they are needed. Set it to -1 for no limit.")); ConfigVariableInt max_compressed_vertex_data -("max-compressed-vertex-data", 0, +("max-compressed-vertex-data", -1, PRC_DESC("Specifies the maximum number of bytes of all vertex data " "that is allowed to remain compressed in system RAM at one time. " "If more than this number of bytes of vertices are created, " "the least-recently-used ones will be temporarily flushed to " - "disk until they are needed. Set it to 0 for no limit.")); + "disk until they are needed. Set it to -1 for no limit.")); + +ConfigVariableInt max_disk_vertex_data +("max-disk-vertex-data", -1, + PRC_DESC("Specifies the maximum number of bytes of vertex data " + "that is allowed to be written to disk. Set it to -1 for no " + "limit.")); // We make this a static constant rather than a dynamic variable, // since we can't tolerate this value changing at runtime. @@ -57,16 +66,23 @@ static const size_t min_vertex_data_compress_size = "storage. Buffers smaller than this are assumed to be likely to " "have minimal compression gains (or even end up larger).")); +SimpleLru GeomVertexArrayData::_ram_lru(max_ram_vertex_data); +SimpleLru GeomVertexArrayData::_compressed_lru(max_compressed_vertex_data); +SimpleLru GeomVertexArrayData::_disk_lru(0); - -SimpleLru GeomVertexArrayData::_global_lru[RC_end_of_list] = { - SimpleLru(max_ram_vertex_data), - SimpleLru(max_compressed_vertex_data), - SimpleLru(0) +SimpleLru *GeomVertexArrayData::_global_lru[RC_end_of_list] = { + &GeomVertexArrayData::_ram_lru, + &GeomVertexArrayData::_compressed_lru, + &GeomVertexArrayData::_disk_lru, + &GeomVertexArrayData::_disk_lru, }; +VertexDataSaveFile *GeomVertexArrayData::_save_file; + PStatCollector GeomVertexArrayData::_vdata_compress_pcollector("*:Vertex Data:Compress"); PStatCollector GeomVertexArrayData::_vdata_decompress_pcollector("*:Vertex Data:Decompress"); +PStatCollector GeomVertexArrayData::_vdata_save_pcollector("*:Vertex Data:Save"); +PStatCollector GeomVertexArrayData::_vdata_restore_pcollector("*:Vertex Data:Restore"); TypeHandle GeomVertexArrayData::_type_handle; @@ -83,6 +99,7 @@ GeomVertexArrayData:: GeomVertexArrayData() : SimpleLruPage(0) { _endian_reversed = false; _ram_class = RC_resident; + _saved_block = NULL; // Can't put it in the LRU until it has been read in and made valid. } @@ -118,7 +135,8 @@ GeomVertexArrayData(const GeomVertexArrayFormat *array_format, _endian_reversed = false; _ram_class = RC_resident; - mark_used_lru(&_global_lru[RC_resident]); + _saved_block = NULL; + mark_used_lru(_global_lru[RC_resident]); nassertv(_array_format->is_registered()); } @@ -138,7 +156,8 @@ GeomVertexArrayData(const GeomVertexArrayData ©) : _endian_reversed = false; _ram_class = copy._ram_class; - mark_used_lru(&_global_lru[_ram_class]); + _saved_block = NULL; + mark_used_lru(_global_lru[_ram_class]); nassertv(_array_format->is_registered()); } @@ -175,6 +194,10 @@ operator = (const GeomVertexArrayData ©) { GeomVertexArrayData:: ~GeomVertexArrayData() { release_all(); + + if (_saved_block != (SimpleAllocatorBlock *)NULL) { + delete _saved_block; + } } //////////////////////////////////////////////////////////////////// @@ -340,9 +363,11 @@ release_all() { //////////////////////////////////////////////////////////////////// void GeomVertexArrayData:: lru_epoch() { - for (int i = 0; i < (int)RC_end_of_list; ++i) { - get_global_lru((RamClass)i)->consider_evict(); - } + _ram_lru.consider_evict(); + _compressed_lru.consider_evict(); + + // No automatic eviction from the Disk LRU. + //_disk_lru.consider_evict(); } //////////////////////////////////////////////////////////////////// @@ -354,6 +379,17 @@ lru_epoch() { void GeomVertexArrayData:: make_resident() { // TODO: make this work with pipelining properly. + + if (_ram_class == RC_resident) { + // If we're already resident, just mark the page recently used. + mark_used_lru(); + return; + } + + if (_ram_class == RC_disk || _ram_class == RC_compressed_disk) { + restore_from_disk(); + } + if (_ram_class == RC_compressed) { CDWriter cdata(_cycler, true); #ifdef HAVE_ZLIB @@ -372,15 +408,15 @@ make_resident() { if (result != Z_OK) { gobj_cat.error() << "Couldn't expand: zlib error " << result << "\n"; + nassert_raise("zlib error"); } nassertv(dest_len == new_data.size()); cdata->_data.swap(new_data); } #endif set_lru_size(cdata->_data.size()); + set_ram_class(RC_resident); } - - set_ram_class(RC_resident); } //////////////////////////////////////////////////////////////////// @@ -392,8 +428,18 @@ make_resident() { void GeomVertexArrayData:: make_compressed() { // TODO: make this work with pipelining properly. - if (_ram_class == RC_resident) { + if (_ram_class == RC_compressed) { + // If we're already compressed, just mark the page recently used. + mark_used_lru(); + return; + } + + if (_ram_class == RC_disk || _ram_class == RC_compressed_disk) { + restore_from_disk(); + } + + if (_ram_class == RC_resident) { CDWriter cdata(_cycler, true); #ifdef HAVE_ZLIB if (cdata->_data_full_size > min_vertex_data_compress_size) { @@ -410,6 +456,7 @@ make_compressed() { if (result != Z_OK) { gobj_cat.error() << "Couldn't compress: zlib error " << result << "\n"; + nassert_raise("zlib error"); } Data new_data(buffer_size, get_class_type()); @@ -424,8 +471,8 @@ make_compressed() { } } #endif - set_ram_class(RC_compressed); set_lru_size(cdata->_data.size()); + set_ram_class(RC_compressed); } } @@ -438,7 +485,84 @@ make_compressed() { void GeomVertexArrayData:: make_disk() { // TODO: make this work with pipelining properly. - if (_ram_class == RC_compressed) { + + if (_ram_class == RC_disk || _ram_class == RC_compressed_disk) { + // If we're already compressed, just mark the page recently used. + mark_used_lru(); + return; + } + + if (_ram_class == RC_resident || _ram_class == RC_compressed) { + nassertv(_saved_block == (SimpleAllocatorBlock *)NULL); + CDWriter cdata(_cycler, true); + + PStatTimer timer(_vdata_save_pcollector); + + if (gobj_cat.is_debug()) { + gobj_cat.debug() + << "Storing " << *this << ", " << cdata->_data.size() + << " bytes, to disk\n"; + } + + const unsigned char *data = &cdata->_data[0]; + size_t size = cdata->_data.size(); + _saved_block = get_save_file()->write_data(data, size); + if (_saved_block == NULL) { + // Can't write it to disk. Too bad. + mark_used_lru(); + return; + } + + // We swap the pvector with an empty one, to really make it free + // its memory. Otherwise it might optimistically keep the memory + // allocated. + Data new_data(get_class_type()); + cdata->_data.swap(new_data); + + if (_ram_class == RC_resident) { + set_ram_class(RC_disk); + } else { + set_ram_class(RC_compressed_disk); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomVertexArrayData::restore_from_disk +// Access: Published +// Description: Restores the vertex data from disk and makes it +// either compressed or resident (according to whether +// it was stored compressed on disk). +//////////////////////////////////////////////////////////////////// +void GeomVertexArrayData:: +restore_from_disk() { + if (_ram_class == RC_disk || _ram_class == RC_compressed_disk) { + nassertv(_saved_block != (SimpleAllocatorBlock *)NULL); + CDWriter cdata(_cycler, true); + + PStatTimer timer(_vdata_restore_pcollector); + + size_t size = _saved_block->get_size(); + if (gobj_cat.is_debug()) { + gobj_cat.debug() + << "Restoring " << *this << ", " << size + << " bytes, from disk\n"; + } + + Data new_data(size, get_class_type()); + if (!get_save_file()->read_data(&new_data[0], size, _saved_block)) { + nassert_raise("read error"); + } + cdata->_data.swap(new_data); + + delete _saved_block; + _saved_block = NULL; + + if (_ram_class == RC_compressed_disk) { + set_ram_class(RC_compressed); + } else { + set_ram_class(RC_resident); + } } } @@ -463,7 +587,11 @@ evict_lru() { switch (_ram_class) { case RC_resident: - make_compressed(); + if (_compressed_lru.get_max_size() == 0) { + make_disk(); + } else { + make_compressed(); + } break; case RC_compressed: @@ -471,6 +599,7 @@ evict_lru() { break; case RC_disk: + case RC_compressed_disk: gobj_cat.warning() << "Cannot evict array data from disk.\n"; break; @@ -539,6 +668,21 @@ reverse_data_endianness(unsigned char *dest, const unsigned char *source, } } +//////////////////////////////////////////////////////////////////// +// Function: GeomVertexArrayData::make_save_file +// Access: Private, Static +// Description: Creates the global VertexDataSaveFile that will be +// used to save vertex data buffers to disk when +// necessary. +//////////////////////////////////////////////////////////////////// +void GeomVertexArrayData:: +make_save_file() { + size_t max_size = (size_t)max_disk_vertex_data; + + _save_file = new VertexDataSaveFile(vertex_save_file_directory, + vertex_save_file_prefix, max_size); +} + //////////////////////////////////////////////////////////////////// // Function: GeomVertexArrayData::register_with_read_factory // Access: Public, Static diff --git a/panda/src/gobj/geomVertexArrayData.h b/panda/src/gobj/geomVertexArrayData.h index b70f219d80..f621eabe90 100644 --- a/panda/src/gobj/geomVertexArrayData.h +++ b/panda/src/gobj/geomVertexArrayData.h @@ -39,8 +39,9 @@ class PreparedGraphicsObjects; class VertexBufferContext; class GraphicsStateGuardianBase; - class GeomVertexArrayDataHandle; +class VertexDataSaveFile; +class SimpleAllocatorBlock; //////////////////////////////////////////////////////////////////// // Class : GeomVertexArrayData @@ -109,10 +110,12 @@ PUBLISHED: INLINE static SimpleLru *get_global_lru(RamClass rclass); static void lru_epoch(); + INLINE static VertexDataSaveFile *get_save_file(); void make_resident(); void make_compressed(); void make_disk(); + void restore_from_disk(); public: virtual void evict_lru(); @@ -123,6 +126,7 @@ private: const unsigned char *source, size_t size); INLINE void set_ram_class(RamClass rclass); + static void make_save_file(); CPT(GeomVertexArrayFormat) _array_format; @@ -140,6 +144,7 @@ private: bool _endian_reversed; RamClass _ram_class; + SimpleAllocatorBlock *_saved_block; typedef pvector Data; @@ -190,10 +195,16 @@ private: typedef CycleDataStageReader CDStageReader; typedef CycleDataStageWriter CDStageWriter; - static SimpleLru _global_lru[RC_end_of_list]; + static SimpleLru _ram_lru; + static SimpleLru _compressed_lru; + static SimpleLru _disk_lru; + static SimpleLru *_global_lru[RC_end_of_list]; + static VertexDataSaveFile *_save_file; static PStatCollector _vdata_compress_pcollector; static PStatCollector _vdata_decompress_pcollector; + static PStatCollector _vdata_save_pcollector; + static PStatCollector _vdata_restore_pcollector; public: static void register_with_read_factory(); diff --git a/panda/src/gobj/gobj_composite1.cxx b/panda/src/gobj/gobj_composite1.cxx index 97e241f8a8..0f4de25e18 100644 --- a/panda/src/gobj/gobj_composite1.cxx +++ b/panda/src/gobj/gobj_composite1.cxx @@ -26,3 +26,4 @@ #include "geomVertexWriter.cxx" #include "indexBufferContext.cxx" #include "internalName.cxx" +#include "lens.cxx" diff --git a/panda/src/gobj/gobj_composite2.cxx b/panda/src/gobj/gobj_composite2.cxx index 98a613a91d..550f9cb03a 100644 --- a/panda/src/gobj/gobj_composite2.cxx +++ b/panda/src/gobj/gobj_composite2.cxx @@ -1,4 +1,3 @@ -#include "lens.cxx" #include "material.cxx" #include "materialPool.cxx" #include "matrixLens.cxx" @@ -10,6 +9,7 @@ #include "savedContext.cxx" #include "shaderContext.cxx" #include "shaderExpansion.cxx" +#include "simpleAllocator.cxx" #include "simpleLru.cxx" #include "sliderTable.cxx" #include "texture.cxx" @@ -23,6 +23,7 @@ #include "userVertexSlider.cxx" #include "userVertexTransform.cxx" #include "vertexBufferContext.cxx" +#include "vertexDataSaveFile.cxx" #include "vertexSlider.cxx" #include "vertexTransform.cxx" #include "videoTexture.cxx" diff --git a/panda/src/gobj/simpleLru.I b/panda/src/gobj/simpleLru.I index d0199a3a01..4fd1bea4de 100644 --- a/panda/src/gobj/simpleLru.I +++ b/panda/src/gobj/simpleLru.I @@ -55,6 +55,8 @@ get_max_size() const { // Access: Published // Description: Changes the max size of all objects that are allowed // to be active on the LRU. +// +// If the size is (size_t)-1, there is no limit. //////////////////////////////////////////////////////////////////// INLINE void SimpleLru:: set_max_size(size_t max_size) { @@ -69,7 +71,7 @@ set_max_size(size_t max_size) { //////////////////////////////////////////////////////////////////// INLINE void SimpleLru:: consider_evict() { - if (_max_size != 0 && _total_size > _max_size) { + if (_total_size > _max_size) { do_evict(); } } diff --git a/panda/src/gobj/simpleLru.cxx b/panda/src/gobj/simpleLru.cxx index 3983cca535..9da6784e89 100644 --- a/panda/src/gobj/simpleLru.cxx +++ b/panda/src/gobj/simpleLru.cxx @@ -21,7 +21,7 @@ //////////////////////////////////////////////////////////////////// // Function: SimpleLru::Destructor -// Access: Protected, Virtual +// Access: Published, Virtual // Description: //////////////////////////////////////////////////////////////////// SimpleLru:: @@ -30,7 +30,8 @@ SimpleLru:: // We're shutting down. Force-remove everything remaining, but // don't explicitly evict it (that would force vertex buffers to // write themselves to disk unnecessarily). - while (_next != (LinkedListNode *)NULL) { + while (_next != (LinkedListNode *)this) { + nassertv(_next != (LinkedListNode *)NULL); ((SimpleLruPage *)_next)->_lru = NULL; ((SimpleLruPage *)_next)->remove_from_list(); }