diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/gpu/GrMemoryPool.cpp | 153 | ||||
-rw-r--r-- | src/gpu/GrMemoryPool.h | 79 |
2 files changed, 232 insertions, 0 deletions
diff --git a/src/gpu/GrMemoryPool.cpp b/src/gpu/GrMemoryPool.cpp new file mode 100644 index 0000000000..597f88cbb8 --- /dev/null +++ b/src/gpu/GrMemoryPool.cpp @@ -0,0 +1,153 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "GrMemoryPool.h" + +#if GR_DEBUG + #define VALIDATE this->validate() +#else + #define VALIDATE +#endif + +GrMemoryPool::GrMemoryPool(size_t preallocSize, size_t minAllocSize) { + GR_DEBUGCODE(fAllocationCnt = 0); + + minAllocSize = GrMax<size_t>(minAllocSize, 1 << 10); + fMinAllocSize = GrSizeAlignUp(minAllocSize + kPerAllocPad, kAlignment), + fPreallocSize = GrSizeAlignUp(preallocSize + kPerAllocPad, kAlignment); + fPreallocSize = GrMax(fPreallocSize, fMinAllocSize); + + fHead = CreateBlock(fPreallocSize); + fTail = fHead; + fHead->fNext = NULL; + fHead->fPrev = NULL; + VALIDATE; +}; + +GrMemoryPool::~GrMemoryPool() { + VALIDATE; + GrAssert(0 == fAllocationCnt); + GrAssert(fHead == fTail); + GrAssert(0 == fHead->fLiveCount); + DeleteBlock(fHead); +}; + +void* GrMemoryPool::allocate(size_t size) { + VALIDATE; + size = GrSizeAlignUp(size, kAlignment); + size += kPerAllocPad; + if (fTail->fFreeSize < size) { + int blockSize = size; + blockSize = GrMax<size_t>(blockSize, fMinAllocSize); + BlockHeader* block = CreateBlock(blockSize); + + block->fPrev = fTail; + block->fNext = NULL; + GrAssert(NULL == fTail->fNext); + fTail->fNext = block; + fTail = block; + } + GrAssert(fTail->fFreeSize >= size); + intptr_t ptr = fTail->fCurrPtr; + // We stash a pointer to the block header, just before the allocated space, + // so that we can decrement the live count on delete in constant time. + *reinterpret_cast<BlockHeader**>(ptr) = fTail; + ptr += kPerAllocPad; + fTail->fCurrPtr += size; + fTail->fFreeSize -= size; + fTail->fLiveCount += 1; + GR_DEBUGCODE(++fAllocationCnt); + VALIDATE; + return reinterpret_cast<void*>(ptr); +} + +void GrMemoryPool::release(void* p) { + VALIDATE; + intptr_t ptr = reinterpret_cast<intptr_t>(p) - kPerAllocPad; + BlockHeader* block = *reinterpret_cast<BlockHeader**>(ptr); + if (1 == block->fLiveCount) { + // the head block is special, it is reset rather than deleted + if (fHead == block) { + fHead->fCurrPtr = reinterpret_cast<intptr_t>(fHead) + + kHeaderSize; + fHead->fLiveCount = 0; + fHead->fFreeSize = fPreallocSize; + } else { + BlockHeader* prev = block->fPrev; + BlockHeader* next = block->fNext; + GrAssert(prev); + prev->fNext = next; + if (next) { + next->fPrev = prev; + } else { + GrAssert(fTail == block); + fTail = prev; + } + DeleteBlock(block); + } + } else { + --block->fLiveCount; + } + GR_DEBUGCODE(--fAllocationCnt); + VALIDATE; +} + +GrMemoryPool::BlockHeader* GrMemoryPool::CreateBlock(size_t size) { + BlockHeader* block = + reinterpret_cast<BlockHeader*>(GrMalloc(size + kHeaderSize)); + // we assume malloc gives us aligned memory + GrAssert(!(reinterpret_cast<intptr_t>(block) % kAlignment)); + block->fLiveCount = 0; + block->fFreeSize = size; + block->fCurrPtr = reinterpret_cast<intptr_t>(block) + kHeaderSize; + return block; +} + +void GrMemoryPool::DeleteBlock(BlockHeader* block) { + GrFree(block); +} + +void GrMemoryPool::validate() { + BlockHeader* block = fHead; + BlockHeader* prev = NULL; + GrAssert(block); + int allocCount = 0; + do { + allocCount += block->fLiveCount; + GrAssert(prev == block->fPrev); + if (NULL != prev) { + GrAssert(prev->fNext == block); + } + + intptr_t b = reinterpret_cast<intptr_t>(block); + size_t ptrOffset = block->fCurrPtr - b; + size_t totalSize = ptrOffset + block->fFreeSize; + size_t userSize = totalSize - kHeaderSize; + intptr_t userStart = b + kHeaderSize; + + GrAssert(!(b % kAlignment)); + GrAssert(!(totalSize % kAlignment)); + GrAssert(!(userSize % kAlignment)); + GrAssert(!(block->fCurrPtr % kAlignment)); + if (fHead != block) { + GrAssert(block->fLiveCount); + GrAssert(userSize >= fMinAllocSize); + } else { + GrAssert(userSize == fPreallocSize); + } + if (!block->fLiveCount) { + GrAssert(ptrOffset == kHeaderSize); + GrAssert(userStart == block->fCurrPtr); + } else { + GrAssert(block == *reinterpret_cast<BlockHeader**>(userStart)); + } + prev = block; + } while ((block = block->fNext)); + GrAssert(allocCount == fAllocationCnt); + GrAssert(prev == fTail); +} + diff --git a/src/gpu/GrMemoryPool.h b/src/gpu/GrMemoryPool.h new file mode 100644 index 0000000000..08c9ee2bbe --- /dev/null +++ b/src/gpu/GrMemoryPool.h @@ -0,0 +1,79 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrMemoryPool_DEFINED +#define GrMemoryPool_DEFINED + +#include "GrTypes.h" + +/** + * Allocates memory in blocks and parcels out space in the blocks for allocation + * requests. It is optimized for allocate / release speed over memory + * effeciency. The interface is designed to be used to implement operator new + * and delete overrides. All allocations are expected to be released before the + * pool's destructor is called. Allocations will be 8-byte aligned. + */ +class GrMemoryPool { +public: + /** + * Prealloc size is the amount of space to make available at pool creation + * time and keep around until pool destruction. The min alloc size is the + * smallest allowed size of additional allocations. + */ + GrMemoryPool(size_t preallocSize, size_t minAllocSize); + + ~GrMemoryPool(); + + /** + * Allocates memory. The memory must be freed with release(). + */ + void* allocate(size_t size); + + /** + * p must have been returned by allocate() + */ + void release(void* p); + + /** + * Returns true if there are no unreleased allocations. + */ + bool isEmpty() const { return fTail == fHead && !fHead->fLiveCount; } + +private: + struct BlockHeader; + + BlockHeader* CreateBlock(size_t size); + + void DeleteBlock(BlockHeader* block); + + void validate(); + + struct BlockHeader { + BlockHeader* fNext; // doubly-linked list of blocks. + BlockHeader* fPrev; + int fLiveCount; // number of outstanding allocations in the + // block. + intptr_t fCurrPtr; // ptr to the start of blocks free space. + size_t fFreeSize; // amount of free space left in the block. + }; + + enum { + // We assume this alignment is good enough for everybody. + kAlignment = 8, + kHeaderSize = GR_CT_ALIGN_UP(sizeof(BlockHeader), kAlignment), + kPerAllocPad = GR_CT_ALIGN_UP(sizeof(BlockHeader*), kAlignment), + }; + size_t fPreallocSize; + size_t fMinAllocSize; + BlockHeader* fHead; + BlockHeader* fTail; +#if GR_DEBUG + int fAllocationCnt; +#endif +}; + +#endif |