diff --git a/panda/src/gobj/geomVertexArrayData.h b/panda/src/gobj/geomVertexArrayData.h index f55675d51f..ed27b6d1f1 100644 --- a/panda/src/gobj/geomVertexArrayData.h +++ b/panda/src/gobj/geomVertexArrayData.h @@ -31,7 +31,6 @@ #include "cycleDataStageReader.h" #include "cycleDataStageWriter.h" #include "pipelineCycler.h" -#include "pStatCollector.h" #include "pmap.h" #include "reMutex.h" #include "simpleLru.h" diff --git a/panda/src/gobj/simpleAllocator.I b/panda/src/gobj/simpleAllocator.I index a7a13a5bff..29e5592074 100644 --- a/panda/src/gobj/simpleAllocator.I +++ b/panda/src/gobj/simpleAllocator.I @@ -31,6 +31,17 @@ SimpleAllocator(size_t max_size) : { } +//////////////////////////////////////////////////////////////////// +// Function: SimpleAllocator::is_empty +// Access: Published +// Description: Returns true if there are no blocks allocated on this +// page, or false if there is at least one. +//////////////////////////////////////////////////////////////////// +INLINE bool SimpleAllocator:: +is_empty() const { + return (_next == this); +} + //////////////////////////////////////////////////////////////////// // Function: SimpleAllocator::get_total_size // Access: Published diff --git a/panda/src/gobj/simpleAllocator.cxx b/panda/src/gobj/simpleAllocator.cxx index 8628f6c7cc..9ddcaf1c57 100644 --- a/panda/src/gobj/simpleAllocator.cxx +++ b/panda/src/gobj/simpleAllocator.cxx @@ -28,6 +28,7 @@ SimpleAllocator:: // We're shutting down. Force-free everything remaining. while (_next != (LinkedListNode *)this) { nassertv(_next != (LinkedListNode *)NULL); + cerr << "force-deleting " << _next << "\n"; ((SimpleAllocatorBlock *)_next)->free(); } } @@ -65,6 +66,8 @@ alloc(size_t size) { size_t free_size = next->_start - end; if (size <= free_size) { SimpleAllocatorBlock *new_block = make_block(end, size); + nassertr(new_block->get_allocator() == this, NULL); + new_block->insert_before(next); _total_size += size; return new_block; @@ -82,6 +85,8 @@ alloc(size_t size) { size_t free_size = _max_size - end; if (size <= free_size) { SimpleAllocatorBlock *new_block = make_block(end, size); + nassertr(new_block->get_allocator() == this, NULL); + new_block->insert_before(this); _total_size += size; return new_block; diff --git a/panda/src/gobj/simpleAllocator.h b/panda/src/gobj/simpleAllocator.h index 47fa19acd4..ccdeea6926 100644 --- a/panda/src/gobj/simpleAllocator.h +++ b/panda/src/gobj/simpleAllocator.h @@ -39,6 +39,7 @@ PUBLISHED: SimpleAllocatorBlock *alloc(size_t size); + INLINE bool is_empty() const; INLINE size_t get_total_size() const; INLINE size_t get_max_size() const; INLINE void set_max_size(size_t max_size); diff --git a/panda/src/gobj/texture.cxx b/panda/src/gobj/texture.cxx index b6124da11e..7869fc86f8 100644 --- a/panda/src/gobj/texture.cxx +++ b/panda/src/gobj/texture.cxx @@ -38,9 +38,11 @@ #include "zStream.h" #include "indent.h" #include "cmath.h" +#include "pStatTimer.h" #include +PStatCollector Texture::_texture_read_pcollector("*:Texture:Read"); TypeHandle Texture::_type_handle; //////////////////////////////////////////////////////////////////// @@ -1760,6 +1762,8 @@ do_read(const Filename &fullpath, const Filename &alpha_fullpath, int primary_file_num_channels, int alpha_file_channel, int z, int n, bool read_pages, bool read_mipmaps, bool header_only, BamCacheRecord *record) { + PStatTimer timer(_texture_read_pcollector); + if (record != (BamCacheRecord *)NULL) { header_only = false; } diff --git a/panda/src/gobj/texture.h b/panda/src/gobj/texture.h index cde40e901c..41df34a77b 100644 --- a/panda/src/gobj/texture.h +++ b/panda/src/gobj/texture.h @@ -29,6 +29,7 @@ #include "updateSeq.h" #include "pmap.h" #include "config_gobj.h" +#include "pStatCollector.h" class PNMImage; class TextureContext; @@ -535,6 +536,8 @@ private: typedef pmap AuxData; AuxData _aux_data; + static PStatCollector _texture_read_pcollector; + // Datagram stuff public: static void register_with_read_factory(); diff --git a/panda/src/gobj/vertexDataBook.I b/panda/src/gobj/vertexDataBook.I index 1e3918c1d0..cf5b25bcd4 100644 --- a/panda/src/gobj/vertexDataBook.I +++ b/panda/src/gobj/vertexDataBook.I @@ -79,21 +79,6 @@ check_resident() const { nassertv(_size == _uncompressed_size); } -//////////////////////////////////////////////////////////////////// -// Function: VertexDataPage::alloc -// Access: Published -// Description: Allocates a new block. Returns NULL if a block of the -// requested size cannot be allocated. -// -// To free the allocated block, call block->free(), or -// simply delete the block pointer. -//////////////////////////////////////////////////////////////////// -INLINE VertexDataBlock *VertexDataPage:: -alloc(size_t size) { - check_resident(); - return (VertexDataBlock *)SimpleAllocator::alloc(size); -} - //////////////////////////////////////////////////////////////////// // Function: VertexDataPage::get_first_block // Access: Published @@ -197,6 +182,7 @@ get_page() const { //////////////////////////////////////////////////////////////////// INLINE unsigned char *VertexDataBlock:: get_pointer() const { + nassertr(get_page() != (VertexDataPage *)NULL, NULL); return get_page()->get_page_data() + get_start(); } diff --git a/panda/src/gobj/vertexDataBook.cxx b/panda/src/gobj/vertexDataBook.cxx index 52a63f8fcd..1fb3140ee7 100644 --- a/panda/src/gobj/vertexDataBook.cxx +++ b/panda/src/gobj/vertexDataBook.cxx @@ -61,7 +61,6 @@ SimpleLru *VertexDataPage::_global_lru[RC_end_of_list] = { &VertexDataPage::_resident_lru, &VertexDataPage::_compressed_lru, &VertexDataPage::_disk_lru, - &VertexDataPage::_disk_lru, }; size_t VertexDataPage::_total_page_size = 0; @@ -110,12 +109,13 @@ alloc(size_t size) { _next_pi = pi; return block; } - if (_pages[pi]->get_total_size() == 0) { + if (_pages[pi]->is_empty()) { // This page is empty, but must have been too small. Create a // new page in its place. delete _pages[pi]; _pages[pi] = create_new_page(size); - return _pages[pi]->alloc(size); + VertexDataBlock *block = _pages[pi]->alloc(size); + return block; } ++pi; } @@ -129,7 +129,7 @@ alloc(size_t size) { _next_pi = pi; return block; } - if (_pages[pi]->get_total_size() == 0) { + if (_pages[pi]->is_empty()) { // This page is empty, but must have been too small. Create a // new page in its place. delete _pages[pi]; @@ -143,7 +143,24 @@ alloc(size_t size) { // large enough to hold this requested block. VertexDataPage *page = create_new_page(size); _pages.push_back(page); - return page->alloc(size); + VertexDataBlock *block = page->alloc(size); + return block; +} + +//////////////////////////////////////////////////////////////////// +// Function: VertexDataBook::save_to_disk +// Access: Published +// Description: Writes all pages to disk immediately, just in case +// they get evicted later. It makes sense to make this +// call just before taking down a loading screen, to +// minimize chugs from saving pages inadvertently later. +//////////////////////////////////////////////////////////////////// +void VertexDataBook:: +save_to_disk() { + Pages::iterator pi; + for (pi = _pages.begin(); pi != _pages.end(); ++pi) { + (*pi)->save_to_disk(); + } } @@ -157,7 +174,6 @@ VertexDataPage(size_t page_size) : SimpleAllocator(page_size), SimpleLruPage(pag _page_data = new unsigned char[get_max_size()]; _size = page_size; _uncompressed_size = _size; - _saved_block = NULL; _total_page_size += _size; get_class_type().inc_memory_usage(TypeHandle::MC_array, _size); set_ram_class(RC_resident); @@ -176,10 +192,6 @@ VertexDataPage:: if (_page_data != NULL) { delete[] _page_data; } - - if (_saved_block != (SimpleAllocatorBlock *)NULL) { - delete _saved_block; - } } //////////////////////////////////////////////////////////////////// @@ -196,7 +208,7 @@ make_resident() { return; } - if (_ram_class == RC_disk || _ram_class == RC_compressed_disk) { + if (_ram_class == RC_disk) { restore_from_disk(); } @@ -249,7 +261,7 @@ make_compressed() { return; } - if (_ram_class == RC_disk || _ram_class == RC_compressed_disk) { + if (_ram_class == RC_disk) { restore_from_disk(); } @@ -306,24 +318,15 @@ make_compressed() { //////////////////////////////////////////////////////////////////// void VertexDataPage:: make_disk() { - if (_ram_class == RC_disk || _ram_class == RC_compressed_disk) { - // If we're already compressed, just mark the page recently used. + if (_ram_class == RC_disk) { + // If we're already on disk, just mark the page recently used. mark_used_lru(); return; } if (_ram_class == RC_resident || _ram_class == RC_compressed) { - nassertv(_saved_block == (SimpleAllocatorBlock *)NULL); - PStatTimer timer(_vdata_save_pcollector); - - if (gobj_cat.is_debug()) { - gobj_cat.debug() - << "Storing page, " << _size << " bytes, to disk\n"; - } - - _saved_block = get_save_file()->write_data(_page_data, _size); - if (_saved_block == NULL) { - // Can't write it to disk. Too bad. + if (!save_to_disk()) { + // Can't save it to disk for some reason. mark_used_lru(); return; } @@ -335,12 +338,47 @@ make_disk() { _page_data = NULL; _size = 0; - if (_ram_class == RC_resident) { - set_ram_class(RC_disk); + set_ram_class(RC_disk); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: VertexDataPage::save_to_disk +// Access: Published +// Description: Writes the page to disk, but does not evict it from +// memory or affect its LRU status. If it gets evicted +// later without having been modified, it will not need +// to write itself to disk again. +// +// Returns true on success, false on failure. +//////////////////////////////////////////////////////////////////// +bool VertexDataPage:: +save_to_disk() { + if (_ram_class == RC_resident || _ram_class == RC_compressed) { + PStatTimer timer(_vdata_save_pcollector); + + if (_saved_block == (VertexDataSaveBlock *)NULL) { + if (gobj_cat.is_debug()) { + gobj_cat.debug() + << "Storing page, " << _size << " bytes, to disk\n"; + } + + bool compressed = (_ram_class == RC_compressed); + + _saved_block = get_save_file()->write_data(_page_data, _size, compressed); + if (_saved_block == (VertexDataSaveBlock *)NULL) { + // Can't write it to disk. Too bad. + return false; + } } else { - set_ram_class(RC_compressed_disk); + if (gobj_cat.is_debug()) { + gobj_cat.debug() + << "Page already stored: " << _size << " bytes\n"; + } } } + + return true; } //////////////////////////////////////////////////////////////////// @@ -352,8 +390,8 @@ make_disk() { //////////////////////////////////////////////////////////////////// void VertexDataPage:: restore_from_disk() { - if (_ram_class == RC_disk || _ram_class == RC_compressed_disk) { - nassertv(_saved_block != (VertexDataBlock *)NULL); + if (_ram_class == RC_disk) { + nassertv(_saved_block != (VertexDataSaveBlock *)NULL); nassertv(_page_data == (unsigned char *)NULL && _size == 0); PStatTimer timer(_vdata_restore_pcollector); @@ -375,11 +413,8 @@ restore_from_disk() { get_class_type().inc_memory_usage(TypeHandle::MC_array, _size); _total_page_size += _size; - delete _saved_block; - _saved_block = NULL; - set_lru_size(_size); - if (_ram_class == RC_compressed_disk) { + if (_saved_block->get_compressed()) { set_ram_class(RC_compressed); } else { set_ram_class(RC_resident); @@ -387,6 +422,29 @@ restore_from_disk() { } } +//////////////////////////////////////////////////////////////////// +// Function: VertexDataPage::alloc +// Access: Published +// Description: Allocates a new block. Returns NULL if a block of the +// requested size cannot be allocated. +// +// To free the allocated block, call block->free(), or +// simply delete the block pointer. +//////////////////////////////////////////////////////////////////// +VertexDataBlock *VertexDataPage:: +alloc(size_t size) { + check_resident(); + VertexDataBlock *block = (VertexDataBlock *)SimpleAllocator::alloc(size); + + if (block != (VertexDataBlock *)NULL) { + // When we allocate a new block within the page, we have to clear + // the disk cache (since we have just invalidated it). + _saved_block.clear(); + } + + return block; +} + //////////////////////////////////////////////////////////////////// // Function: VertexDataPage::make_block // Access: Protected, Virtual @@ -429,7 +487,6 @@ evict_lru() { break; case RC_disk: - case RC_compressed_disk: gobj_cat.warning() << "Cannot evict array data from disk.\n"; break; diff --git a/panda/src/gobj/vertexDataBook.h b/panda/src/gobj/vertexDataBook.h index ef2384677b..7cb3ba7587 100644 --- a/panda/src/gobj/vertexDataBook.h +++ b/panda/src/gobj/vertexDataBook.h @@ -24,10 +24,10 @@ #include "simpleAllocator.h" #include "referenceCount.h" #include "pStatCollector.h" +#include "vertexDataSaveFile.h" class VertexDataPage; class VertexDataBlock; -class VertexDataSaveFile; //////////////////////////////////////////////////////////////////// // Class : VertexDataBook @@ -44,6 +44,8 @@ PUBLISHED: INLINE int get_num_pages() const; INLINE VertexDataPage *get_page(int n) const; + void save_to_disk(); + private: INLINE VertexDataPage *create_new_page(size_t size); @@ -73,7 +75,6 @@ PUBLISHED: RC_resident, RC_compressed, RC_disk, - RC_compressed_disk, RC_end_of_list, // list marker; do not use }; @@ -84,9 +85,10 @@ PUBLISHED: void make_resident(); void make_compressed(); void make_disk(); + bool save_to_disk(); void restore_from_disk(); - INLINE VertexDataBlock *alloc(size_t size); + VertexDataBlock *alloc(size_t size); INLINE VertexDataBlock *get_first_block() const; INLINE static size_t get_total_page_size(); @@ -107,7 +109,7 @@ private: unsigned char *_page_data; size_t _size, _uncompressed_size; RamClass _ram_class; - SimpleAllocatorBlock *_saved_block; + PT(VertexDataSaveBlock) _saved_block; static SimpleLru _resident_lru; static SimpleLru _compressed_lru; diff --git a/panda/src/gobj/vertexDataBuffer.cxx b/panda/src/gobj/vertexDataBuffer.cxx index 963c34135f..610d9b766b 100644 --- a/panda/src/gobj/vertexDataBuffer.cxx +++ b/panda/src/gobj/vertexDataBuffer.cxx @@ -91,7 +91,10 @@ page_out(VertexDataBook &book) { // We only need to allocate a block if we don't have a source // file. _block = book.alloc(_size); - memcpy(_block->get_pointer(), _resident_data, _size); + nassertv(_block != (VertexDataBlock *)NULL); + unsigned char *pointer = _block->get_pointer(); + nassertv(pointer != (unsigned char *)NULL); + memcpy(pointer, _resident_data, _size); } free(_resident_data); diff --git a/panda/src/gobj/vertexDataSaveFile.I b/panda/src/gobj/vertexDataSaveFile.I index 9f5653e1e7..04eca35920 100644 --- a/panda/src/gobj/vertexDataSaveFile.I +++ b/panda/src/gobj/vertexDataSaveFile.I @@ -48,6 +48,43 @@ get_total_file_size() const { //////////////////////////////////////////////////////////////////// INLINE size_t VertexDataSaveFile:: get_used_file_size() const { - return _allocator.get_total_size(); + return SimpleAllocator::get_total_size(); } + +//////////////////////////////////////////////////////////////////// +// Function: VertexDataSaveBlock::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE VertexDataSaveBlock:: +VertexDataSaveBlock(VertexDataSaveFile *file, size_t start, size_t size) : + SimpleAllocatorBlock(file, start, size) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: VertexDataSaveBlock::set_compressed +// Access: Public +// Description: Sets the compressed flag. This is true to indicate +// the data is written in zlib-compressed form to the +// save file; false to indicate the data is +// uncompressed. +//////////////////////////////////////////////////////////////////// +INLINE void VertexDataSaveBlock:: +set_compressed(bool compressed) { + _compressed = compressed; +} + +//////////////////////////////////////////////////////////////////// +// Function: VertexDataSaveBlock::get_compressed +// Access: Public +// Description: Returns the compressed flag. This is true to +// indicate the data is written in zlib-compressed form +// to the save file; false to indicate the data is +// uncompressed. +//////////////////////////////////////////////////////////////////// +INLINE bool VertexDataSaveBlock:: +get_compressed() const { + return _compressed; +} diff --git a/panda/src/gobj/vertexDataSaveFile.cxx b/panda/src/gobj/vertexDataSaveFile.cxx index 76ea7cd01a..232ae8b89e 100644 --- a/panda/src/gobj/vertexDataSaveFile.cxx +++ b/panda/src/gobj/vertexDataSaveFile.cxx @@ -26,7 +26,7 @@ VertexDataSaveFile:: VertexDataSaveFile(const Filename &directory, const string &prefix, size_t max_size) : - _allocator(max_size) + SimpleAllocator(max_size) { Filename dir; if (directory.empty()) { @@ -168,14 +168,15 @@ VertexDataSaveFile:: // cannot be written (e.g. no remaining space on the // file). //////////////////////////////////////////////////////////////////// -SimpleAllocatorBlock *VertexDataSaveFile:: -write_data(const unsigned char *data, size_t size) { +PT(VertexDataSaveBlock) VertexDataSaveFile:: +write_data(const unsigned char *data, size_t size, bool compressed) { if (!_is_valid) { return NULL; } - SimpleAllocatorBlock *block = _allocator.alloc(size); - if (block != (SimpleAllocatorBlock *)NULL) { + PT(VertexDataSaveBlock) block = (VertexDataSaveBlock *)SimpleAllocator::alloc(size); + if (block != (VertexDataSaveBlock *)NULL) { + block->set_compressed(compressed); #ifdef _WIN32 if (SetFilePointer(_handle, block->get_start(), NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) { @@ -189,7 +190,6 @@ write_data(const unsigned char *data, size_t size) { bytes_written != size) { gobj_cat.error() << "Error writing " << size << " bytes to save file. Disk full?\n"; - delete block; return NULL; } @@ -201,11 +201,10 @@ write_data(const unsigned char *data, size_t size) { return false; } - ssize_t result = write(_fd, data, size); + ssize_t result = ::write(_fd, data, size); if (result != (ssize_t)size) { gobj_cat.error() << "Error writing " << size << " bytes to save file. Disk full?\n"; - delete block; return NULL; } #endif // _WIN32 @@ -223,7 +222,7 @@ write_data(const unsigned char *data, size_t size) { // on success, false on failure. //////////////////////////////////////////////////////////////////// bool VertexDataSaveFile:: -read_data(unsigned char *data, size_t size, SimpleAllocatorBlock *block) { +read_data(unsigned char *data, size_t size, VertexDataSaveBlock *block) { if (!_is_valid) { return false; } @@ -263,3 +262,15 @@ read_data(unsigned char *data, size_t size, SimpleAllocatorBlock *block) { return true; } + +//////////////////////////////////////////////////////////////////// +// Function: VertexDataSaveFile::make_block +// Access: Protected, Virtual +// Description: Creates a new SimpleAllocatorBlock object. Override +// this function to specialize the block type returned. +//////////////////////////////////////////////////////////////////// +SimpleAllocatorBlock *VertexDataSaveFile:: +make_block(size_t start, size_t size) { + return new VertexDataSaveBlock(this, start, size); +} + diff --git a/panda/src/gobj/vertexDataSaveFile.h b/panda/src/gobj/vertexDataSaveFile.h index a4dead37df..10865fb8e8 100644 --- a/panda/src/gobj/vertexDataSaveFile.h +++ b/panda/src/gobj/vertexDataSaveFile.h @@ -28,6 +28,8 @@ #include #endif +class VertexDataSaveBlock; + //////////////////////////////////////////////////////////////////// // Class : VertexDataSaveFile // Description : A temporary file to hold the vertex data that has @@ -35,7 +37,7 @@ // vertex data arrays are written into one large flat // file. //////////////////////////////////////////////////////////////////// -class EXPCL_PANDA VertexDataSaveFile { +class EXPCL_PANDA VertexDataSaveFile : public SimpleAllocator { public: VertexDataSaveFile(const Filename &directory, const string &prefix, size_t max_size); @@ -46,12 +48,15 @@ public: INLINE size_t get_total_file_size() const; INLINE size_t get_used_file_size() const; - SimpleAllocatorBlock *write_data(const unsigned char *data, size_t size); + PT(VertexDataSaveBlock) write_data(const unsigned char *data, size_t size, + bool compressed); bool read_data(unsigned char *data, size_t size, - SimpleAllocatorBlock *block); + VertexDataSaveBlock *block); + +protected: + virtual SimpleAllocatorBlock *make_block(size_t start, size_t size); private: - SimpleAllocator _allocator; Filename _filename; bool _is_valid; size_t _total_file_size; @@ -63,6 +68,28 @@ private: #endif // _WIN32 }; +//////////////////////////////////////////////////////////////////// +// Class : VertexDataSaveBlock +// Description : A block of bytes on the save file. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA VertexDataSaveBlock : public SimpleAllocatorBlock, public ReferenceCount { +protected: + INLINE VertexDataSaveBlock(VertexDataSaveFile *file, + size_t start, size_t size); + +public: + INLINE void set_compressed(bool compressed); + INLINE bool get_compressed() const; + +private: + bool _compressed; + +public: + INLINE unsigned char *get_pointer() const; + + friend class VertexDataSaveFile; +}; + #include "vertexDataSaveFile.I" #endif