| /* |
| * 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<size_t>(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<size_t>(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<AllocHeader*>(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<void*>(ptr); |
| } |
| |
| void GrMemoryPool::release(void* p) { |
| VALIDATE; |
| intptr_t ptr = reinterpret_cast<intptr_t>(p) - kPerAllocPad; |
| AllocHeader* allocData = reinterpret_cast<AllocHeader*>(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<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; |
| } |
| 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<BlockHeader*>(sk_malloc_throw(paddedSize)); |
| // we assume malloc gives us aligned memory |
| SkASSERT(!(reinterpret_cast<intptr_t>(block) % kAlignment)); |
| SkDEBUGCODE(block->fBlockSentinal = kAssignedMarker); |
| block->fLiveCount = 0; |
| block->fFreeSize = size; |
| block->fCurrPtr = reinterpret_cast<intptr_t>(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<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 { |
| AllocHeader* allocData = reinterpret_cast<AllocHeader*>(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 |
| } |