| /* | |
| * 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" | |
| #ifdef SK_DEBUG | |
| #define VALIDATE this->validate() | |
| #else | |
| #define VALIDATE | |
| #endif | |
| GrMemoryPool::GrMemoryPool(size_t preallocSize, size_t minAllocSize) { | |
| SkDEBUGCODE(fAllocationCnt = 0); | |
| minAllocSize = SkTMax<size_t>(minAllocSize, 1 << 10); | |
| fMinAllocSize = GrSizeAlignUp(minAllocSize + kPerAllocPad, kAlignment), | |
| fPreallocSize = GrSizeAlignUp(preallocSize + kPerAllocPad, kAlignment); | |
| fPreallocSize = SkTMax(fPreallocSize, fMinAllocSize); | |
| fHead = CreateBlock(fPreallocSize); | |
| fTail = fHead; | |
| fHead->fNext = NULL; | |
| fHead->fPrev = NULL; | |
| VALIDATE; | |
| }; | |
| GrMemoryPool::~GrMemoryPool() { | |
| VALIDATE; | |
| SkASSERT(0 == fAllocationCnt); | |
| SkASSERT(fHead == fTail); | |
| SkASSERT(0 == fHead->fLiveCount); | |
| DeleteBlock(fHead); | |
| }; | |
| void* GrMemoryPool::allocate(size_t size) { | |
| VALIDATE; | |
| size = GrSizeAlignUp(size, kAlignment); | |
| size += kPerAllocPad; | |
| if (fTail->fFreeSize < size) { | |
| size_t blockSize = size; | |
| blockSize = SkTMax<size_t>(blockSize, fMinAllocSize); | |
| BlockHeader* block = CreateBlock(blockSize); | |
| block->fPrev = fTail; | |
| block->fNext = NULL; | |
| SkASSERT(NULL == fTail->fNext); | |
| fTail->fNext = block; | |
| fTail = block; | |
| } | |
| SkASSERT(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->fPrevPtr = fTail->fCurrPtr; | |
| fTail->fCurrPtr += size; | |
| fTail->fFreeSize -= size; | |
| fTail->fLiveCount += 1; | |
| SkDEBUGCODE(++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; | |
| SkASSERT(prev); | |
| prev->fNext = next; | |
| if (next) { | |
| next->fPrev = prev; | |
| } else { | |
| SkASSERT(fTail == block); | |
| fTail = prev; | |
| } | |
| DeleteBlock(block); | |
| } | |
| } else { | |
| --block->fLiveCount; | |
| // Trivial reclaim: if we're releasing the most recent allocation, reuse it | |
| if (block->fPrevPtr == ptr) { | |
| block->fFreeSize += (block->fCurrPtr - block->fPrevPtr); | |
| block->fCurrPtr = block->fPrevPtr; | |
| } | |
| } | |
| SkDEBUGCODE(--fAllocationCnt); | |
| VALIDATE; | |
| } | |
| GrMemoryPool::BlockHeader* GrMemoryPool::CreateBlock(size_t size) { | |
| BlockHeader* block = | |
| reinterpret_cast<BlockHeader*>(sk_malloc_throw(size + kHeaderSize)); | |
| // we assume malloc gives us aligned memory | |
| SkASSERT(!(reinterpret_cast<intptr_t>(block) % kAlignment)); | |
| block->fLiveCount = 0; | |
| block->fFreeSize = size; | |
| block->fCurrPtr = reinterpret_cast<intptr_t>(block) + kHeaderSize; | |
| block->fPrevPtr = 0; // gcc warns on assigning NULL to an intptr_t. | |
| return block; | |
| } | |
| void GrMemoryPool::DeleteBlock(BlockHeader* block) { | |
| sk_free(block); | |
| } | |
| void GrMemoryPool::validate() { | |
| #ifdef SK_DEBUG | |
| BlockHeader* block = fHead; | |
| BlockHeader* prev = NULL; | |
| SkASSERT(block); | |
| int allocCount = 0; | |
| do { | |
| allocCount += block->fLiveCount; | |
| SkASSERT(prev == block->fPrev); | |
| if (NULL != prev) { | |
| SkASSERT(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; | |
| SkASSERT(!(b % kAlignment)); | |
| SkASSERT(!(totalSize % kAlignment)); | |
| SkASSERT(!(userSize % kAlignment)); | |
| SkASSERT(!(block->fCurrPtr % kAlignment)); | |
| if (fHead != block) { | |
| SkASSERT(block->fLiveCount); | |
| SkASSERT(userSize >= fMinAllocSize); | |
| } else { | |
| SkASSERT(userSize == fPreallocSize); | |
| } | |
| if (!block->fLiveCount) { | |
| SkASSERT(ptrOffset == kHeaderSize); | |
| SkASSERT(userStart == block->fCurrPtr); | |
| } else { | |
| SkASSERT(block == *reinterpret_cast<BlockHeader**>(userStart)); | |
| } | |
| prev = block; | |
| } while ((block = block->fNext)); | |
| SkASSERT(allocCount == fAllocationCnt); | |
| SkASSERT(prev == fTail); | |
| #endif | |
| } |