aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/gpu/GrMemoryPool.cpp153
-rw-r--r--src/gpu/GrMemoryPool.h79
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