diff --git a/panda/src/gobj/vertexDataBook.I b/panda/src/gobj/vertexDataBook.I index 9014c767d0..329c21cbc4 100644 --- a/panda/src/gobj/vertexDataBook.I +++ b/panda/src/gobj/vertexDataBook.I @@ -49,5 +49,5 @@ get_num_pages() const { INLINE VertexDataPage *VertexDataBook:: create_new_page(size_t size) { size_t page_size = ((size + _block_size - 1) / _block_size) * _block_size; - return new VertexDataPage(this, page_size); + return new VertexDataPage(this, page_size, _block_size); } diff --git a/panda/src/gobj/vertexDataBook.cxx b/panda/src/gobj/vertexDataBook.cxx index ff0beaafc0..a5b55598a2 100644 --- a/panda/src/gobj/vertexDataBook.cxx +++ b/panda/src/gobj/vertexDataBook.cxx @@ -19,13 +19,41 @@ #include "vertexDataBook.h" #include "reMutexHolder.h" +#ifdef WIN32 + +// Windows case. +#define WIN32_LEAN_AND_MEAN +#include + +#else + +// Posix case. +#include + +#endif // WIN32 + //////////////////////////////////////////////////////////////////// // Function: VertexDataBook::Constructor // Access: Published // Description: //////////////////////////////////////////////////////////////////// VertexDataBook:: -VertexDataBook(size_t block_size) : _block_size(block_size) { +VertexDataBook(size_t block_size) { + // Make sure the block_size is an integer multiple of the system's + // page size. + size_t system_page_size; +#ifdef WIN32 + // Windows case. + SYSTEM_INFO sysinfo; + GetSystemInfo(&sysinfo); + + system_page_size = (size_t)sysinfo.dwPageSize; +#else + // Posix case. + system_page_size = getpagesize(); +#endif // WIN32 + + _block_size = ((block_size + system_page_size - 1) / system_page_size) * system_page_size; } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/gobj/vertexDataPage.I b/panda/src/gobj/vertexDataPage.I index 0267d5a124..bc1fcb52ec 100644 --- a/panda/src/gobj/vertexDataPage.I +++ b/panda/src/gobj/vertexDataPage.I @@ -231,6 +231,17 @@ set_ram_class(RamClass rclass) { adjust_book_size(); } +//////////////////////////////////////////////////////////////////// +// Function: VertexDataPage::round_up +// Access: Private +// Description: Round page_size up to the next multiple of +// _block_size. +//////////////////////////////////////////////////////////////////// +INLINE size_t VertexDataPage:: +round_up(size_t page_size) const { + return ((page_size + _block_size - 1) / _block_size) * _block_size; +} + //////////////////////////////////////////////////////////////////// // Function: VertexDataPage::PageThread::Constructor // Access: Public diff --git a/panda/src/gobj/vertexDataPage.cxx b/panda/src/gobj/vertexDataPage.cxx index d9ef00e957..3063fb4754 100644 --- a/panda/src/gobj/vertexDataPage.cxx +++ b/panda/src/gobj/vertexDataPage.cxx @@ -27,6 +27,21 @@ #include #endif +#ifdef WIN32 + +// Windows case (VirtualAlloc) +#define WIN32_LEAN_AND_MEAN +#include + +#else + +// Posix case (mmap) +#include +#include +#include + +#endif + ConfigVariableInt max_resident_vertex_data ("max-resident-vertex-data", -1, PRC_DESC("Specifies the maximum number of bytes of all vertex data " @@ -95,6 +110,7 @@ VertexDataPage(size_t book_size) : SimpleAllocator(book_size, _unused_mutex), SimpleLruPage(book_size), _book_size(book_size), + _block_size(0), _book(NULL) { _page_data = NULL; @@ -110,14 +126,15 @@ VertexDataPage(size_t book_size) : // Description: //////////////////////////////////////////////////////////////////// VertexDataPage:: -VertexDataPage(VertexDataBook *book, size_t page_size) : +VertexDataPage(VertexDataBook *book, size_t page_size, size_t block_size) : SimpleAllocator(page_size, book->_lock), SimpleLruPage(page_size), _book_size(page_size), + _block_size(block_size), _book(book) { - get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)page_size); - _page_data = new unsigned char[page_size]; + _allocated_size = round_up(page_size); + _page_data = alloc_page_data(_allocated_size); _size = page_size; _uncompressed_size = _size; @@ -146,8 +163,7 @@ VertexDataPage:: } if (_page_data != NULL) { - get_class_type().dec_memory_usage(TypeHandle::MC_array, (int)_size); - delete[] _page_data; + free_page_data(_page_data, _allocated_size); _size = 0; } } @@ -322,7 +338,8 @@ make_resident() { << "Expanding page from " << _size << " to " << _uncompressed_size << "\n"; } - unsigned char *new_data = new unsigned char[_uncompressed_size]; + size_t new_allocated_size = round_up(_uncompressed_size); + unsigned char *new_data = alloc_page_data(new_allocated_size); uLongf dest_len = _uncompressed_size; int result = uncompress(new_data, &dest_len, _page_data, _size); if (result != Z_OK) { @@ -332,10 +349,10 @@ make_resident() { } nassertv(dest_len == _uncompressed_size); - get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)_uncompressed_size - (int)_size); - delete[] _page_data; + free_page_data(_page_data, _allocated_size); _page_data = new_data; _size = _uncompressed_size; + _allocated_size = new_allocated_size; #endif set_lru_size(_size); @@ -383,14 +400,15 @@ make_compressed() { << "Couldn't compress: zlib error " << result << "\n"; nassert_raise("zlib error"); } - - unsigned char *new_data = new unsigned char[buffer_size]; + + size_t new_allocated_size = round_up(buffer_size); + unsigned char *new_data = alloc_page_data(new_allocated_size); memcpy(new_data, buffer, buffer_size); - get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)buffer_size - (int)_size); - delete[] _page_data; + free_page_data(_page_data, _allocated_size); _page_data = new_data; _size = buffer_size; + _allocated_size = new_allocated_size; if (gobj_cat.is_debug()) { gobj_cat.debug() @@ -428,8 +446,7 @@ make_disk() { return; } - get_class_type().dec_memory_usage(TypeHandle::MC_array, (int)_size); - delete[] _page_data; + free_page_data(_page_data, _allocated_size); _page_data = NULL; _size = 0; @@ -461,7 +478,7 @@ do_save_to_disk() { bool compressed = (_ram_class == RC_compressed); - _saved_block = get_save_file()->write_data(_page_data, _size, compressed); + _saved_block = get_save_file()->write_data(_page_data, _allocated_size, compressed); if (_saved_block == (VertexDataSaveBlock *)NULL) { // Can't write it to disk. Too bad. return false; @@ -500,15 +517,16 @@ do_restore_from_disk() { << "Restoring page, " << buffer_size << " bytes, from disk\n"; } - unsigned char *new_data = new unsigned char[buffer_size]; - if (!get_save_file()->read_data(new_data, buffer_size, _saved_block)) { + size_t new_allocated_size = round_up(buffer_size); + unsigned char *new_data = alloc_page_data(new_allocated_size); + if (!get_save_file()->read_data(new_data, new_allocated_size, _saved_block)) { nassert_raise("read error"); } - get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)buffer_size); nassertv(_page_data == (unsigned char *)NULL); _page_data = new_data; _size = buffer_size; + _allocated_size = new_allocated_size; set_lru_size(_size); if (_saved_block->get_compressed()) { @@ -606,6 +624,46 @@ make_save_file() { _save_file = new VertexDataSaveFile(vertex_save_file_directory, vertex_save_file_prefix, max_size); } + +//////////////////////////////////////////////////////////////////// +// Function: VertexDataPage::alloc_page_data +// Access: Private +// Description: Allocates and returns a freshly-allocated buffer of +// at least the indicated size for holding vertex data. +//////////////////////////////////////////////////////////////////// +unsigned char *VertexDataPage:: +alloc_page_data(size_t page_size) const { + get_class_type().inc_memory_usage(TypeHandle::MC_array, page_size); + + unsigned char *buffer; +#ifdef WIN32 + // Windows case. + buffer = (unsigned char *)VirtualAlloc(NULL, page_size, MEM_COMMIT | MEM_RESERVE, + PAGE_READWRITE); +#else + // Posix case. + buffer = (unsigned char *)mmap(NULL, page_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, -1, 0); +#endif + + return buffer; +} + +//////////////////////////////////////////////////////////////////// +// Function: VertexDataPage::free_page_data +// Access: Private +// Description: Releases a buffer allocated via alloc_page_data(). +//////////////////////////////////////////////////////////////////// +void VertexDataPage:: +free_page_data(unsigned char *page_data, size_t page_size) const { + get_class_type().dec_memory_usage(TypeHandle::MC_array, page_size); + +#ifdef WIN32 + VirtualFree(page_data, 0, MEM_RELEASE); +#else + munmap(page_data, page_size); +#endif +} //////////////////////////////////////////////////////////////////// // Function: VertexDataPage::PageThread::add_page diff --git a/panda/src/gobj/vertexDataPage.h b/panda/src/gobj/vertexDataPage.h index 6018ef6d4c..565c424327 100644 --- a/panda/src/gobj/vertexDataPage.h +++ b/panda/src/gobj/vertexDataPage.h @@ -43,7 +43,7 @@ class VertexDataBlock; class EXPCL_PANDA VertexDataPage : public SimpleAllocator, public SimpleLruPage { public: VertexDataPage(size_t book_size); - VertexDataPage(VertexDataBook *book, size_t page_size); + VertexDataPage(VertexDataBook *book, size_t page_size, size_t block_size); ~VertexDataPage(); PUBLISHED: @@ -104,6 +104,10 @@ private: INLINE void set_ram_class(RamClass ram_class); static void make_save_file(); + INLINE size_t round_up(size_t page_size) const; + unsigned char *alloc_page_data(size_t page_size) const; + void free_page_data(unsigned char *page_data, size_t page_size) const; + typedef pdeque PendingPages; class PageThread : public Thread { @@ -129,10 +133,11 @@ private: static Mutex _tlock; // Protects the thread members. unsigned char *_page_data; - size_t _size, _uncompressed_size; + size_t _size, _allocated_size, _uncompressed_size; RamClass _ram_class; PT(VertexDataSaveBlock) _saved_block; size_t _book_size; + size_t _block_size; //Mutex _lock; // Inherited from SimpleAllocator. Protects above members. diff --git a/panda/src/gobj/vertexDataSaveFile.cxx b/panda/src/gobj/vertexDataSaveFile.cxx index f3d3b6428d..f66d51b0f2 100644 --- a/panda/src/gobj/vertexDataSaveFile.cxx +++ b/panda/src/gobj/vertexDataSaveFile.cxx @@ -63,10 +63,13 @@ VertexDataSaveFile(const Filename &directory, const string &prefix, #ifdef _WIN32 // Windows case. + DWORD flags = FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE | FILE_FLAG_RANDOM_ACCESS; +#if defined(HAVE_THREADS) && defined(SIMPLE_THREADS) + // In SIMPLE_THREADS mode, we use "overlapped" I/O. + flags |= FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING; +#endif _handle = CreateFile(os_specific.c_str(), GENERIC_READ | GENERIC_WRITE, - 0, NULL, CREATE_ALWAYS, - FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE | FILE_FLAG_RANDOM_ACCESS, - NULL); + 0, NULL, CREATE_ALWAYS, flags, NULL); if (_handle != INVALID_HANDLE_VALUE) { // The file was successfully opened and locked. break; @@ -194,19 +197,23 @@ write_data(const unsigned char *data, size_t size, bool compressed) { block->set_compressed(compressed); #ifdef _WIN32 - if (SetFilePointer(_handle, block->get_start(), NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) { - gobj_cat.error() - << "Error seeking to position " << block->get_start() << " in save file.\n"; - return false; - } + OVERLAPPED overlapped; + memset(&overlapped, 0, sizeof(overlapped)); + overlapped.Offset = block->get_start(); + WriteFile(_handle, data, size, NULL, &overlapped); DWORD bytes_written; - if (!WriteFile(_handle, data, size, &bytes_written, NULL) || - bytes_written != size) { - gobj_cat.error() - << "Error writing " << size << " bytes to save file. Disk full?\n"; - return NULL; + while (!GetOverlappedResult(_handle, &overlapped, &bytes_written, false)) { + if (GetLastError() == ERROR_IO_INCOMPLETE) { + // Wait for more later. + Thread::force_yield(); + } else { + gobj_cat.error() + << "Error writing " << size << " bytes to save file. Disk full?\n"; + return NULL; + } } + nassertr(bytes_written == size, NULL); #else // Posix case. @@ -257,20 +264,23 @@ read_data(unsigned char *data, size_t size, VertexDataSaveBlock *block) { nassertr(size == block->get_size(), false); #ifdef _WIN32 - if (SetFilePointer(_handle, block->get_start(), NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) { - gobj_cat.error() - << "Error seeking to position " << block->get_start() << " in save file.\n"; - return false; - } + OVERLAPPED overlapped; + memset(&overlapped, 0, sizeof(overlapped)); + overlapped.Offset = block->get_start(); + ReadFile(_handle, data, size, NULL, &overlapped); DWORD bytes_read; - if (!ReadFile(_handle, data, size, &bytes_read, NULL) || - bytes_read != size) { - gobj_cat.error() - << "Error writing " << size << " bytes to save file. Disk full?\n"; - delete block; - return NULL; + while (!GetOverlappedResult(_handle, &overlapped, &bytes_read, false)) { + if (GetLastError() == ERROR_IO_INCOMPLETE) { + // Wait for more later. + Thread::force_yield(); + } else { + gobj_cat.error() + << "Error reading " << size << " bytes from save file.\n"; + return NULL; + } } + nassertr(bytes_read == size, NULL); #else // Posix case.