/* * 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); SkDEBUGCODE(fAllocBlockCnt = 0); minAllocSize = SkTMax(minAllocSize, 1 << 10); fMinAllocSize = GrSizeAlignUp(minAllocSize + kPerAllocPad, kAlignment), fPreallocSize = GrSizeAlignUp(preallocSize + kPerAllocPad, kAlignment); fPreallocSize = SkTMax(fPreallocSize, fMinAllocSize); fSize = 0; fHead = CreateBlock(fPreallocSize); fTail = fHead; fHead->fNext = nullptr; fHead->fPrev = nullptr; VALIDATE; }; GrMemoryPool::~GrMemoryPool() { VALIDATE; SkASSERT(0 == fAllocationCnt); SkASSERT(fHead == fTail); SkASSERT(0 == fHead->fLiveCount); DeleteBlock(fHead); }; void* GrMemoryPool::allocate(size_t size) { VALIDATE; size += kPerAllocPad; size = GrSizeAlignUp(size, kAlignment); if (fTail->fFreeSize < size) { size_t blockSize = size; blockSize = SkTMax(blockSize, fMinAllocSize); BlockHeader* block = CreateBlock(blockSize); block->fPrev = fTail; block->fNext = nullptr; SkASSERT(nullptr == fTail->fNext); fTail->fNext = block; fTail = block; fSize += block->fSize; SkDEBUGCODE(++fAllocBlockCnt); } SkASSERT(kAssignedMarker == fTail->fBlockSentinal); 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. AllocHeader* allocData = reinterpret_cast(ptr); SkDEBUGCODE(allocData->fSentinal = kAssignedMarker); allocData->fHeader = fTail; ptr += kPerAllocPad; fTail->fPrevPtr = fTail->fCurrPtr; fTail->fCurrPtr += size; fTail->fFreeSize -= size; fTail->fLiveCount += 1; SkDEBUGCODE(++fAllocationCnt); VALIDATE; return reinterpret_cast(ptr); } void GrMemoryPool::release(void* p) { VALIDATE; intptr_t ptr = reinterpret_cast(p) - kPerAllocPad; AllocHeader* allocData = reinterpret_cast(ptr); SkASSERT(kAssignedMarker == allocData->fSentinal); SkDEBUGCODE(allocData->fSentinal = kFreedMarker); BlockHeader* block = allocData->fHeader; SkASSERT(kAssignedMarker == block->fBlockSentinal); if (1 == block->fLiveCount) { // the head block is special, it is reset rather than deleted if (fHead == block) { fHead->fCurrPtr = reinterpret_cast(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; } fSize -= block->fSize; DeleteBlock(block); SkDEBUGCODE(fAllocBlockCnt--); } } 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) { size_t paddedSize = size + kHeaderSize; BlockHeader* block = reinterpret_cast(sk_malloc_throw(paddedSize)); // we assume malloc gives us aligned memory SkASSERT(!(reinterpret_cast(block) % kAlignment)); SkDEBUGCODE(block->fBlockSentinal = kAssignedMarker); block->fLiveCount = 0; block->fFreeSize = size; block->fCurrPtr = reinterpret_cast(block) + kHeaderSize; block->fPrevPtr = 0; // gcc warns on assigning nullptr to an intptr_t. block->fSize = paddedSize; return block; } void GrMemoryPool::DeleteBlock(BlockHeader* block) { SkASSERT(kAssignedMarker == block->fBlockSentinal); SkDEBUGCODE(block->fBlockSentinal = kFreedMarker); // FWIW sk_free(block); } void GrMemoryPool::validate() { #ifdef SK_DEBUG BlockHeader* block = fHead; BlockHeader* prev = nullptr; SkASSERT(block); int allocCount = 0; do { SkASSERT(kAssignedMarker == block->fBlockSentinal); allocCount += block->fLiveCount; SkASSERT(prev == block->fPrev); if (prev) { SkASSERT(prev->fNext == block); } intptr_t b = reinterpret_cast(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 { AllocHeader* allocData = reinterpret_cast(userStart); SkASSERT(allocData->fSentinal == kAssignedMarker || allocData->fSentinal == kFreedMarker); SkASSERT(block == allocData->fHeader); } prev = block; } while ((block = block->fNext)); SkASSERT(allocCount == fAllocationCnt); SkASSERT(prev == fTail); SkASSERT(fAllocBlockCnt != 0 || fSize == 0); #endif }