better algorithm for finding available pages

This commit is contained in:
David Rose 2007-06-07 21:47:37 +00:00
parent 68c9ef5207
commit 67771fb75c
12 changed files with 480 additions and 218 deletions

View File

@ -723,9 +723,11 @@ copy_data_from(const GeomVertexArrayDataHandle *other) {
other->mark_used();
_cdata->_buffer.unclean_realloc(other->_cdata->_buffer.get_size());
memcpy(_cdata->_buffer.get_write_pointer(),
other->_cdata->_buffer.get_read_pointer(true),
other->_cdata->_buffer.get_size());
unsigned char *dest = _cdata->_buffer.get_write_pointer();
const unsigned char *source = other->_cdata->_buffer.get_read_pointer(true);
size_t size = other->_cdata->_buffer.get_size();
memcpy(dest, source, size);
_cdata->_modified = Geom::get_next_modified();

View File

@ -23,14 +23,30 @@
// Description:
////////////////////////////////////////////////////////////////////
INLINE SimpleAllocator::
SimpleAllocator(size_t max_size) :
SimpleAllocator(size_t max_size, Mutex &lock) :
LinkedListNode(true),
_total_size(0),
_max_size(max_size),
_contiguous(max_size)
_contiguous(max_size),
_lock(lock)
{
}
////////////////////////////////////////////////////////////////////
// Function: SimpleAllocator::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.
////////////////////////////////////////////////////////////////////
SimpleAllocatorBlock *SimpleAllocator::
alloc(size_t size) {
MutexHolder holder(_lock);
return do_alloc(size);
}
////////////////////////////////////////////////////////////////////
// Function: SimpleAllocator::is_empty
// Access: Published
@ -39,7 +55,8 @@ SimpleAllocator(size_t max_size) :
////////////////////////////////////////////////////////////////////
INLINE bool SimpleAllocator::
is_empty() const {
return (_next == this);
MutexHolder holder(_lock);
return do_is_empty();
}
////////////////////////////////////////////////////////////////////
@ -49,6 +66,7 @@ is_empty() const {
////////////////////////////////////////////////////////////////////
INLINE size_t SimpleAllocator::
get_total_size() const {
MutexHolder holder(_lock);
return _total_size;
}
@ -59,6 +77,7 @@ get_total_size() const {
////////////////////////////////////////////////////////////////////
INLINE size_t SimpleAllocator::
get_max_size() const {
MutexHolder holder(_lock);
return _max_size;
}
@ -71,6 +90,7 @@ get_max_size() const {
////////////////////////////////////////////////////////////////////
INLINE void SimpleAllocator::
set_max_size(size_t max_size) {
MutexHolder holder(_lock);
_max_size = max_size;
}
@ -86,6 +106,7 @@ set_max_size(size_t max_size) {
////////////////////////////////////////////////////////////////////
INLINE size_t SimpleAllocator::
get_contiguous() const {
MutexHolder holder(_lock);
return _contiguous;
}
@ -97,15 +118,31 @@ get_contiguous() const {
////////////////////////////////////////////////////////////////////
INLINE SimpleAllocatorBlock *SimpleAllocator::
get_first_block() const {
MutexHolder holder(_lock);
return (_next == this) ? (SimpleAllocatorBlock *)NULL : (SimpleAllocatorBlock *)_next;
}
////////////////////////////////////////////////////////////////////
// Function: SimpleAllocator::increase_contiguous
// Function: SimpleAllocator::do_is_empty
// Access: Protected
// Description: Returns true if there are no blocks allocated on this
// page, or false if there is at least one.
//
// Assumes the lock is already held.
////////////////////////////////////////////////////////////////////
INLINE bool SimpleAllocator::
do_is_empty() const {
return (_next == this);
}
////////////////////////////////////////////////////////////////////
// Function: SimpleAllocator::mark_contiguous
// Access: Protected
// Description: Some space has been made available following the
// indicated block. Increase the contiguous space
// accordingly.
//
// Assumes the lock is already held.
////////////////////////////////////////////////////////////////////
INLINE void SimpleAllocator::
mark_contiguous(const LinkedListNode *block) {
@ -119,10 +156,11 @@ mark_contiguous(const LinkedListNode *block) {
space = ((SimpleAllocatorBlock *)_next)->get_start();
}
} else {
space = ((SimpleAllocatorBlock *)block)->get_max_size() - ((SimpleAllocatorBlock *)block)->get_size();
space = ((SimpleAllocatorBlock *)block)->do_get_max_size() - ((SimpleAllocatorBlock *)block)->get_size();
}
if (space > _contiguous) {
_contiguous = space;
changed_contiguous();
}
}
@ -160,11 +198,8 @@ INLINE SimpleAllocatorBlock::
INLINE void SimpleAllocatorBlock::
free() {
if (_allocator != (SimpleAllocator *)NULL) {
_allocator->_total_size -= _size;
LinkedListNode *prev = _prev;
remove_from_list();
_allocator->mark_contiguous(prev);
_allocator = NULL;
MutexHolder holder(_allocator->_lock);
do_free();
}
}
@ -223,14 +258,8 @@ is_free() const {
INLINE size_t SimpleAllocatorBlock::
get_max_size() const {
nassertr(_allocator != (SimpleAllocator *)NULL, 0);
size_t end;
if (_next == _allocator) {
end = _allocator->get_max_size();
} else {
end = ((SimpleAllocatorBlock *)_next)->_start;
}
return end - _start;
MutexHolder holder(_allocator->_lock);
return do_get_max_size();
}
////////////////////////////////////////////////////////////////////
@ -242,7 +271,73 @@ get_max_size() const {
////////////////////////////////////////////////////////////////////
INLINE bool SimpleAllocatorBlock::
realloc(size_t size) {
if (size > get_max_size()) {
nassertr(_allocator != (SimpleAllocator *)NULL, false);
MutexHolder holder(_allocator->_lock);
return do_realloc(size);
}
////////////////////////////////////////////////////////////////////
// Function: SimpleAllocatorBlock::get_next_block
// Access: Published
// Description: Returns a pointer to the next allocated block in the
// chain, or NULL if there are no more allocated blocks.
////////////////////////////////////////////////////////////////////
INLINE SimpleAllocatorBlock *SimpleAllocatorBlock::
get_next_block() const {
nassertr(_allocator != (SimpleAllocator *)NULL, NULL);
MutexHolder holder(_allocator->_lock);
return (_next == _allocator) ? (SimpleAllocatorBlock *)NULL : (SimpleAllocatorBlock *)_next;
}
////////////////////////////////////////////////////////////////////
// Function: SimpleAllocatorBlock::do_free
// Access: Protected
// Description: Releases the allocated space.
//
// Assumes the lock is already held.
////////////////////////////////////////////////////////////////////
INLINE void SimpleAllocatorBlock::
do_free() {
nassertv(_allocator != (SimpleAllocator *)NULL);
_allocator->_total_size -= _size;
LinkedListNode *prev = _prev;
remove_from_list();
_allocator->mark_contiguous(prev);
_allocator = NULL;
}
////////////////////////////////////////////////////////////////////
// Function: SimpleAllocatorBlock::do_get_max_size
// Access: Protected
// Description: Returns the maximum size this block can be
// reallocated to, as limited by the following block.
//
// Assumes the lock is already held.
////////////////////////////////////////////////////////////////////
INLINE size_t SimpleAllocatorBlock::
do_get_max_size() const {
size_t end;
if (_next == _allocator) {
end = _allocator->_max_size;
} else {
end = ((SimpleAllocatorBlock *)_next)->_start;
}
return end - _start;
}
////////////////////////////////////////////////////////////////////
// Function: SimpleAllocatorBlock::do_realloc
// Access: Protected
// Description: Changes the size of this block to the specified size.
// Returns true if the change is accepted, false if
// there was not enough room.
//
// Assumes the lock is already held.
////////////////////////////////////////////////////////////////////
INLINE bool SimpleAllocatorBlock::
do_realloc(size_t size) {
if (size > do_get_max_size()) {
return false;
}
@ -259,14 +354,3 @@ realloc(size_t size) {
}
return true;
}
////////////////////////////////////////////////////////////////////
// Function: SimpleAllocatorBlock::get_next_block
// Access: Published
// Description: Returns a pointer to the next allocated block in the
// chain, or NULL if there are no more allocated blocks.
////////////////////////////////////////////////////////////////////
INLINE SimpleAllocatorBlock *SimpleAllocatorBlock::
get_next_block() const {
return (_next == _allocator) ? (SimpleAllocatorBlock *)NULL : (SimpleAllocatorBlock *)_next;
}

View File

@ -26,24 +26,60 @@
SimpleAllocator::
~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();
if (_next != (LinkedListNode *)this) {
MutexHolder holder(_lock);
while (_next != (LinkedListNode *)this) {
nassertv(_next != (LinkedListNode *)NULL);
((SimpleAllocatorBlock *)_next)->do_free();
}
}
}
////////////////////////////////////////////////////////////////////
// Function: SimpleAllocator::alloc
// Function: SimpleAllocator::output
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
void SimpleAllocator::
output(ostream &out) const {
MutexHolder holder(_lock);
out << "SimpleAllocator, " << _total_size << " of " << _max_size
<< " allocated";
}
////////////////////////////////////////////////////////////////////
// Function: SimpleAllocator::write
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
void SimpleAllocator::
write(ostream &out) const {
MutexHolder holder(_lock);
out << "SimpleAllocator, " << _total_size << " of " << _max_size
<< " allocated";
SimpleAllocatorBlock *block = (SimpleAllocatorBlock *)_next;
while (block->_next != this) {
SimpleAllocatorBlock *next = (SimpleAllocatorBlock *)block->_next;
out << " " << *block << "\n";
block = next;
}
}
////////////////////////////////////////////////////////////////////
// Function: SimpleAllocator::do_alloc
// Access: Protected
// 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.
//
// Assumes the lock is already held.
////////////////////////////////////////////////////////////////////
SimpleAllocatorBlock *SimpleAllocator::
alloc(size_t size) {
do_alloc(size_t size) {
if (size > _contiguous) {
// Don't even bother.
return NULL;
@ -70,6 +106,14 @@ alloc(size_t size) {
new_block->insert_before(next);
_total_size += size;
if (_max_size - _total_size < _contiguous) {
// Since we only have (_max_size - _total_size) bytes
// remaining, it follows that our largest contiguous block
// must be no larger than this.
_contiguous = _max_size - _total_size;
changed_contiguous();
}
return new_block;
}
if (free_size > best) {
@ -89,6 +133,14 @@ alloc(size_t size) {
new_block->insert_before(this);
_total_size += size;
if (_max_size - _total_size < _contiguous) {
// Since we only have (_max_size - _total_size) bytes
// remaining, it follows that our largest contiguous block
// must be no larger than this.
_contiguous = _max_size - _total_size;
changed_contiguous();
}
return new_block;
}
@ -98,56 +150,15 @@ alloc(size_t size) {
// Now that we've walked through the entire list of blocks, we
// really do know accurately what the largest contiguous block is.
_contiguous = best;
if (_contiguous != best) {
_contiguous = best;
changed_contiguous();
}
// No room for this block.
return NULL;
}
////////////////////////////////////////////////////////////////////
// Function: SimpleAllocator::output
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
void SimpleAllocator::
output(ostream &out) const {
out << "SimpleAllocator, " << _total_size << " of " << _max_size
<< " allocated";
}
////////////////////////////////////////////////////////////////////
// Function: SimpleAllocatorBlock::output
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
void SimpleAllocatorBlock::
output(ostream &out) const {
if (_allocator == (SimpleAllocator *)NULL) {
out << "free block\n";
} else {
out << "block of size " << _size << " at " << _start;
}
}
////////////////////////////////////////////////////////////////////
// Function: SimpleAllocator::write
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
void SimpleAllocator::
write(ostream &out) const {
out << *this << ":\n";
SimpleAllocatorBlock *block = (SimpleAllocatorBlock *)_next;
while (block->_next != this) {
SimpleAllocatorBlock *next = (SimpleAllocatorBlock *)block->_next;
out << " " << *block << "\n";
block = next;
}
}
////////////////////////////////////////////////////////////////////
// Function: SimpleAllocator::make_block
// Access: Protected, Virtual
@ -159,3 +170,28 @@ make_block(size_t start, size_t size) {
return new SimpleAllocatorBlock(this, start, size);
}
////////////////////////////////////////////////////////////////////
// Function: SimpleAllocator::changed_contiguous
// Access: Protected, Virtual
// Description: This callback function is made whenever the estimate
// of contiguous available space changes, either through
// an alloc or free. The lock will be held.
////////////////////////////////////////////////////////////////////
void SimpleAllocator::
changed_contiguous() {
}
////////////////////////////////////////////////////////////////////
// Function: SimpleAllocatorBlock::output
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
void SimpleAllocatorBlock::
output(ostream &out) const {
if (_allocator == (SimpleAllocator *)NULL) {
out << "free block\n";
} else {
MutexHolder holder(_allocator->_lock);
out << "block of size " << _size << " at " << _start;
}
}

View File

@ -21,6 +21,8 @@
#include "pandabase.h"
#include "linkedListNode.h"
#include "pmutex.h"
#include "mutexHolder.h"
class SimpleAllocatorBlock;
@ -31,17 +33,13 @@ class SimpleAllocatorBlock;
// integers within a specified upper limit; it uses a
// simple first-fit algorithm to find the next available
// space.
//
// Note that this class is not inherently thread-safe;
// derived classes are responsible for protecting any
// calls into it within mutexes, if necessary.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA SimpleAllocator : public LinkedListNode {
PUBLISHED:
INLINE SimpleAllocator(size_t max_size);
INLINE SimpleAllocator(size_t max_size, Mutex &lock);
virtual ~SimpleAllocator();
SimpleAllocatorBlock *alloc(size_t size);
INLINE SimpleAllocatorBlock *alloc(size_t size);
INLINE bool is_empty() const;
INLINE size_t get_total_size() const;
@ -55,10 +53,14 @@ PUBLISHED:
void write(ostream &out) const;
protected:
SimpleAllocatorBlock *do_alloc(size_t size);
INLINE bool do_is_empty() const;
virtual SimpleAllocatorBlock *make_block(size_t start, size_t size);
INLINE void mark_contiguous(const LinkedListNode *block);
virtual void changed_contiguous();
private:
protected:
// This is implemented as a linked-list chain of allocated blocks.
// Free blocks are implicit. Blocks are kept in sorted order from
// beginning to end. Allocating a block means creating a new entry
@ -76,6 +78,16 @@ private:
// it will not be smaller.
size_t _contiguous;
// This mutex protects all operations within this class. The caller
// must pass the reference to a mutex in to the constructor, and the
// caller remains responsible for owning the mutex. This allows the
// mutex to be shared where appropriate.
// A derived class may also use it to protect itself as well, but
// take care to call do_alloc() instead of alloc() etc. as
// necessary.
Mutex &_lock;
friend class SimpleAllocatorBlock;
};
@ -106,6 +118,11 @@ PUBLISHED:
void output(ostream &out) const;
protected:
INLINE void do_free();
INLINE size_t do_get_max_size() const;
INLINE bool do_realloc(size_t size);
private:
SimpleAllocator *_allocator;
size_t _start;

View File

@ -17,6 +17,18 @@
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: VertexDataBook::alloc
// Access: Published
// Description: Allocates and returns a new VertexDataBuffer of the
// requested size.
////////////////////////////////////////////////////////////////////
INLINE VertexDataBlock *VertexDataBook::
alloc(size_t size) {
MutexHolder holder(_lock);
return do_alloc(size);
}
////////////////////////////////////////////////////////////////////
// Function: VertexDataBook::get_num_pages
// Access: Published
@ -27,17 +39,6 @@ get_num_pages() const {
return _pages.size();
}
////////////////////////////////////////////////////////////////////
// Function: VertexDataBook::get_page
// Access: Published
// Description: Returns the nth page created for the book.
////////////////////////////////////////////////////////////////////
INLINE VertexDataPage *VertexDataBook::
get_page(int n) const {
nassertr(n >= 0 && n < (int)_pages.size(), NULL);
return _pages[n];
}
////////////////////////////////////////////////////////////////////
// Function: VertexDataBook::create_new_page
// Access: Private

View File

@ -17,7 +17,7 @@
////////////////////////////////////////////////////////////////////
#include "vertexDataBook.h"
#include "mutexHolder.h"
#include "reMutexHolder.h"
////////////////////////////////////////////////////////////////////
// Function: VertexDataBook::Constructor
@ -26,7 +26,6 @@
////////////////////////////////////////////////////////////////////
VertexDataBook::
VertexDataBook(size_t block_size) : _block_size(block_size) {
_next_pi = 0;
}
////////////////////////////////////////////////////////////////////
@ -38,71 +37,6 @@ VertexDataBook::
~VertexDataBook() {
}
////////////////////////////////////////////////////////////////////
// Function: VertexDataBook::alloc
// Access: Published
// Description: Allocates and returns a new VertexDataBuffer of the
// requested size.
////////////////////////////////////////////////////////////////////
VertexDataBlock *VertexDataBook::
alloc(size_t size) {
MutexHolder holder(_lock);
// First, try to allocate from the last page that worked; then
// continue to the end of the list. We consider only pages that are
// currently resident (or that are empty), to minimize unnecessary
// swapping.
size_t pi = _next_pi;
while (pi < _pages.size()) {
if (_pages[pi]->get_ram_class() == VertexDataPage::RC_resident ||
_pages[pi]->is_empty()) {
VertexDataBlock *block = _pages[pi]->alloc(size);
if (block != (VertexDataBlock *)NULL) {
_next_pi = pi;
return block;
}
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);
VertexDataBlock *block = _pages[pi]->alloc(size);
return block;
}
}
++pi;
}
// Then, go back to the beginning and try those pages.
pi = 0;
_next_pi = min(_next_pi, _pages.size());
while (pi < _next_pi) {
if (_pages[pi]->get_ram_class() == VertexDataPage::RC_resident ||
_pages[pi]->is_empty()) {
VertexDataBlock *block = _pages[pi]->alloc(size);
if (block != (VertexDataBlock *)NULL) {
_next_pi = pi;
return block;
}
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);
}
}
++pi;
}
// No page was good enough. Create a new page. Make it at least
// large enough to hold this requested block.
VertexDataPage *page = create_new_page(size);
_pages.push_back(page);
VertexDataBlock *block = page->alloc(size);
return block;
}
////////////////////////////////////////////////////////////////////
// Function: VertexDataBook::count_total_page_size
// Access: Published
@ -111,6 +45,8 @@ alloc(size_t size) {
////////////////////////////////////////////////////////////////////
size_t VertexDataBook::
count_total_page_size() const {
MutexHolder holder(_lock);
size_t total = 0;
Pages::const_iterator pi;
for (pi = _pages.begin(); pi != _pages.end(); ++pi) {
@ -128,6 +64,8 @@ count_total_page_size() const {
////////////////////////////////////////////////////////////////////
size_t VertexDataBook::
count_total_page_size(VertexDataPage::RamClass ram_class) const {
MutexHolder holder(_lock);
size_t total = 0;
Pages::const_iterator pi;
for (pi = _pages.begin(); pi != _pages.end(); ++pi) {
@ -146,6 +84,8 @@ count_total_page_size(VertexDataPage::RamClass ram_class) const {
////////////////////////////////////////////////////////////////////
size_t VertexDataBook::
count_allocated_size() const {
MutexHolder holder(_lock);
size_t total = 0;
Pages::const_iterator pi;
for (pi = _pages.begin(); pi != _pages.end(); ++pi) {
@ -163,6 +103,8 @@ count_allocated_size() const {
////////////////////////////////////////////////////////////////////
size_t VertexDataBook::
count_allocated_size(VertexDataPage::RamClass ram_class) const {
MutexHolder holder(_lock);
size_t total = 0;
Pages::const_iterator pi;
for (pi = _pages.begin(); pi != _pages.end(); ++pi) {
@ -183,9 +125,61 @@ count_allocated_size(VertexDataPage::RamClass ram_class) const {
////////////////////////////////////////////////////////////////////
void VertexDataBook::
save_to_disk() {
MutexHolder holder(_lock);
Pages::iterator pi;
for (pi = _pages.begin(); pi != _pages.end(); ++pi) {
(*pi)->save_to_disk();
}
}
////////////////////////////////////////////////////////////////////
// Function: VertexDataBook::do_alloc
// Access: Private
// Description: Allocates and returns a new VertexDataBuffer of the
// requested size.
//
// Assumes the lock is already held.
////////////////////////////////////////////////////////////////////
VertexDataBlock *VertexDataBook::
do_alloc(size_t size) {
// Look for an empty page of the appropriate size. The _pages set
// is sorted so that the pages with the smallest available blocks
// are at the front.
// Create a dummy page to use to search the set.
VertexDataPage size_page(size);
Pages::iterator pi = _pages.lower_bound(&size_page);
// Now we can start from the first element of the set that is
// possibly large enough to contain this block, and work up from
// there.
while (pi != _pages.end()) {
Pages::iterator pnext = pi;
++pnext;
VertexDataPage *page = (*pi);
// Allocating a block may change the page's available contiguous
// size, and thereby change its position in the set, invalidating
// the iterator pi. This is why we've already computed pnext.
VertexDataBlock *block = page->do_alloc(size);
if (block != (VertexDataBlock *)NULL) {
// This page worked.
return block;
}
// Try the next page.
pi = pnext;
}
// No page was good enough. Create a new page. Make it at least
// large enough to hold this requested block.
VertexDataPage *page = create_new_page(size);
_pages.insert(page);
VertexDataBlock *block = page->do_alloc(size);
return block;
}

View File

@ -23,6 +23,8 @@
#include "pmutex.h"
#include "mutexHolder.h"
#include "vertexDataPage.h"
#include "indirectLess.h"
#include "plist.h"
class VertexDataBlock;
@ -36,10 +38,9 @@ PUBLISHED:
VertexDataBook(size_t block_size);
~VertexDataBook();
VertexDataBlock *alloc(size_t size);
INLINE VertexDataBlock *alloc(size_t size);
INLINE int get_num_pages() const;
INLINE VertexDataPage *get_page(int n) const;
size_t count_total_page_size() const;
size_t count_total_page_size(VertexDataPage::RamClass ram_class) const;
@ -48,15 +49,21 @@ PUBLISHED:
void save_to_disk();
public:
void reorder_page(VertexDataPage *page);
private:
INLINE VertexDataPage *create_new_page(size_t size);
VertexDataBlock *do_alloc(size_t size);
private:
size_t _block_size;
typedef pvector<VertexDataPage *> Pages;
typedef pset<VertexDataPage *, IndirectLess<VertexDataPage> > Pages;
Pages _pages;
size_t _next_pi;
Mutex _lock;
friend class VertexDataPage;
};
#include "vertexDataBook.I"

View File

@ -141,7 +141,7 @@ clean_realloc(size_t size) {
INLINE void VertexDataBuffer::
unclean_realloc(size_t size) {
MutexHolder holder(_lock);
do_clean_realloc(size);
do_unclean_realloc(size);
}
////////////////////////////////////////////////////////////////////

View File

@ -59,6 +59,21 @@ request_resident() {
}
}
////////////////////////////////////////////////////////////////////
// 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) {
MutexHolder holder(_lock);
return do_alloc(size);
}
////////////////////////////////////////////////////////////////////
// Function: VertexDataPage::get_first_block
// Access: Published
@ -164,7 +179,7 @@ has_thread() {
INLINE unsigned char *VertexDataPage::
get_page_data(bool force) {
MutexHolder holder(_lock);
if (_ram_class != RC_resident) {
if (_ram_class != RC_resident || _pending_ram_class != RC_resident) {
if (force) {
make_resident_now();
} else {
@ -180,6 +195,26 @@ get_page_data(bool force) {
return _page_data;
}
////////////////////////////////////////////////////////////////////
// Function: VertexDataPage::operator
// Access: Public
// Description: This comparison method is used to order pages within
// a book.
////////////////////////////////////////////////////////////////////
INLINE bool VertexDataPage::
operator < (const VertexDataPage &other) const {
// We sort pages so that the pages with the smallest number of
// available contiguous bytes come up first. We store our best
// estimate of continguous bytes here.
if (_book_size != other._book_size) {
return _book_size < other._book_size;
}
// For pages of equal size, we sort based on pointers, to make it
// easy to quickly find a specific page.
return this < &other;
}
////////////////////////////////////////////////////////////////////
// Function: VertexDataPage::set_ram_class
// Access: Private
@ -190,6 +225,10 @@ INLINE void VertexDataPage::
set_ram_class(RamClass rclass) {
_ram_class = rclass;
mark_used_lru(_global_lru[rclass]);
// Changing the ram class might make our effective available space 0
// and thereby change the placement within the book.
adjust_book_size();
}
////////////////////////////////////////////////////////////////////

View File

@ -71,6 +71,10 @@ SimpleLru *VertexDataPage::_global_lru[RC_end_of_list] = {
VertexDataSaveFile *VertexDataPage::_save_file;
// This mutex is (mostly) unused. We just need a Mutex to pass to the
// Book Constructor, below.
Mutex VertexDataPage::_unused_mutex;
PStatCollector VertexDataPage::_vdata_compress_pcollector("*:Vertex Data:Compress");
PStatCollector VertexDataPage::_vdata_decompress_pcollector("*:Vertex Data:Decompress");
PStatCollector VertexDataPage::_vdata_save_pcollector("*:Vertex Data:Save");
@ -79,19 +83,39 @@ PStatCollector VertexDataPage::_thread_wait_pcollector("*:Wait:Idle");
TypeHandle VertexDataPage::_type_handle;
////////////////////////////////////////////////////////////////////
// Function: VertexDataPage::Book Constructor
// Access: Public
// Description: This constructor is used only by VertexDataBook, to
// create a mostly-empty object that can be used to
// search for a particular page size in the set.
////////////////////////////////////////////////////////////////////
VertexDataPage::
VertexDataPage(size_t book_size) :
SimpleAllocator(book_size, _unused_mutex),
SimpleLruPage(book_size),
_book_size(book_size),
_book(NULL)
{
_page_data = NULL;
_size = 0;
_uncompressed_size = 0;
_ram_class = RC_resident;
_pending_ram_class = RC_resident;
}
////////////////////////////////////////////////////////////////////
// Function: VertexDataPage::Constructor
// Access: Published
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
VertexDataPage::
VertexDataPage(VertexDataBook *book, size_t page_size) :
SimpleAllocator(page_size),
SimpleAllocator(page_size, book->_lock),
SimpleLruPage(page_size),
_book_size(page_size),
_book(book)
{
nassertv(page_size == get_max_size());
get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)page_size);
_page_data = new unsigned char[page_size];
_size = page_size;
@ -103,12 +127,15 @@ VertexDataPage(VertexDataBook *book, size_t page_size) :
////////////////////////////////////////////////////////////////////
// Function: VertexDataPage::Destructor
// Access: Published
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
VertexDataPage::
~VertexDataPage() {
MutexHolder holder(_lock);
// Since the only way to delete a page is via the
// changed_contiguous() method, the lock will already be held.
// MutexHolder holder(_lock);
{
MutexHolder holder2(_tlock);
@ -125,29 +152,6 @@ VertexDataPage::
}
}
////////////////////////////////////////////////////////////////////
// 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) {
MutexHolder holder(_lock);
VertexDataBlock *block = (VertexDataBlock *)SimpleAllocator::alloc(size);
if (block != (VertexDataBlock *)NULL && _ram_class != RC_disk) {
// When we allocate a new block within a resident page, we have to
// clear the disk cache (since we have just invalidated it).
_saved_block.clear();
}
return block;
}
////////////////////////////////////////////////////////////////////
// Function: VertexDataPage::stop_thread
// Access: Published, Static
@ -182,6 +186,27 @@ make_block(size_t start, size_t size) {
return new VertexDataBlock(this, start, size);
}
////////////////////////////////////////////////////////////////////
// Function: VertexDataPage::changed_contiguous
// Access: Protected, Virtual
// Description: This callback function is made whenever the estimate
// of contiguous available space changes, either through
// an alloc or free. The lock will be held.
////////////////////////////////////////////////////////////////////
void VertexDataPage::
changed_contiguous() {
if (do_is_empty()) {
// If the page is now empty, delete it.
VertexDataBook::Pages::iterator pi = _book->_pages.find(this);
nassertv(pi != _book->_pages.end());
_book->_pages.erase(pi);
delete this;
return;
}
adjust_book_size();
}
////////////////////////////////////////////////////////////////////
// Function: VertexDataPage::evict_lru
// Access: Public, Virtual
@ -223,6 +248,30 @@ evict_lru() {
}
}
////////////////////////////////////////////////////////////////////
// Function: VertexDataPage::do_alloc
// Access: Private
// 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.
//
// Assumes the lock is already held.
////////////////////////////////////////////////////////////////////
VertexDataBlock *VertexDataPage::
do_alloc(size_t size) {
VertexDataBlock *block = (VertexDataBlock *)SimpleAllocator::do_alloc(size);
if (block != (VertexDataBlock *)NULL && _ram_class != RC_disk) {
// When we allocate a new block within a resident page, we have to
// clear the disk cache (since we have just invalidated it).
_saved_block.clear();
}
return block;
}
////////////////////////////////////////////////////////////////////
// Function: VertexDataPage::make_resident_now
// Access: Private
@ -287,9 +336,8 @@ make_resident() {
delete[] _page_data;
_page_data = new_data;
_size = _uncompressed_size;
#endif
set_lru_size(_size);
set_ram_class(RC_resident);
}
@ -471,6 +519,33 @@ do_restore_from_disk() {
}
}
////////////////////////////////////////////////////////////////////
// Function: VertexDataPage::adjust_book_size
// Access: Private
// Description: Called when the "book size"--the size of the page as
// recorded in its book's table--has changed for some
// reason. Assumes the lock is held.
////////////////////////////////////////////////////////////////////
void VertexDataPage::
adjust_book_size() {
size_t new_size = _contiguous;
if (_ram_class != RC_resident) {
// Let's not attempt to allocate new buffers from non-resident
// pages.
new_size = 0;
}
if (new_size != _book_size) {
VertexDataBook::Pages::iterator pi = _book->_pages.find(this);
nassertv(pi != _book->_pages.end());
_book->_pages.erase(pi);
_book_size = new_size;
bool inserted = _book->_pages.insert(this).second;
nassertv(inserted);
}
}
////////////////////////////////////////////////////////////////////
// Function: VertexDataPage::request_ram_class
// Access: Private
@ -483,12 +558,6 @@ do_restore_from_disk() {
////////////////////////////////////////////////////////////////////
void VertexDataPage::
request_ram_class(RamClass ram_class) {
if (ram_class == _ram_class) {
gobj_cat.warning()
<< "Page " << this << " already has ram class " << ram_class << "\n";
return;
}
if (!vertex_data_threaded_paging || !Thread::is_threading_supported()) {
// No threads. Do it immediately.
switch (ram_class) {
@ -613,6 +682,9 @@ remove_page(VertexDataPage *page) {
}
page->_pending_ram_class = page->_ram_class;
// Put the page back on its proper LRU.
page->mark_used_lru(_global_lru[page->_ram_class]);
}
////////////////////////////////////////////////////////////////////

View File

@ -42,10 +42,11 @@ class VertexDataBlock;
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA VertexDataPage : public SimpleAllocator, public SimpleLruPage {
public:
VertexDataPage(size_t book_size);
VertexDataPage(VertexDataBook *book, size_t page_size);
PUBLISHED:
~VertexDataPage();
PUBLISHED:
// These are used to indicate the current residency state of the
// page, which may or may not have been temporarily evicted to
// satisfy memory requirements.
@ -61,7 +62,7 @@ PUBLISHED:
INLINE RamClass get_pending_ram_class() const;
INLINE void request_resident();
VertexDataBlock *alloc(size_t size);
INLINE VertexDataBlock *alloc(size_t size);
INLINE VertexDataBlock *get_first_block() const;
INLINE VertexDataBook *get_book() const;
@ -77,14 +78,18 @@ PUBLISHED:
public:
INLINE unsigned char *get_page_data(bool force);
INLINE bool operator < (const VertexDataPage &other) const;
protected:
virtual SimpleAllocatorBlock *make_block(size_t start, size_t size);
virtual void changed_contiguous();
virtual void evict_lru();
private:
class PageThread;
VertexDataBlock *do_alloc(size_t size);
void make_resident_now();
void make_resident();
void make_compressed();
@ -93,7 +98,8 @@ private:
bool do_save_to_disk();
void do_restore_from_disk();
PageThread *get_thread();
void adjust_book_size();
void request_ram_class(RamClass ram_class);
INLINE void set_ram_class(RamClass ram_class);
static void make_save_file();
@ -126,8 +132,9 @@ private:
size_t _size, _uncompressed_size;
RamClass _ram_class;
PT(VertexDataSaveBlock) _saved_block;
size_t _book_size;
Mutex _lock; // Protects above members
//Mutex _lock; // Inherited from SimpleAllocator. Protects above members.
RamClass _pending_ram_class; // Protected by _tlock.
@ -141,6 +148,8 @@ private:
static VertexDataSaveFile *_save_file;
static Mutex _unused_mutex;
static PStatCollector _vdata_compress_pcollector;
static PStatCollector _vdata_decompress_pcollector;
static PStatCollector _vdata_save_pcollector;
@ -159,6 +168,7 @@ private:
static TypeHandle _type_handle;
friend class PageThread;
friend class VertexDataBook;
};
#include "vertexDataPage.I"

View File

@ -33,7 +33,7 @@
VertexDataSaveFile::
VertexDataSaveFile(const Filename &directory, const string &prefix,
size_t max_size) :
SimpleAllocator(max_size)
SimpleAllocator(max_size, _lock)
{
Filename dir;
if (directory.empty()) {
@ -183,7 +183,7 @@ write_data(const unsigned char *data, size_t size, bool compressed) {
return NULL;
}
PT(VertexDataSaveBlock) block = (VertexDataSaveBlock *)SimpleAllocator::alloc(size);
PT(VertexDataSaveBlock) block = (VertexDataSaveBlock *)SimpleAllocator::do_alloc(size);
if (block != (VertexDataSaveBlock *)NULL) {
block->set_compressed(compressed);