1 /* 2 * Copyright 2012 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #include "GrMemoryPool.h" 9 10 #ifdef SK_DEBUG 11 #define VALIDATE this->validate() 12 #else 13 #define VALIDATE 14 #endif 15 16 GrMemoryPool::GrMemoryPool(size_t preallocSize, size_t minAllocSize) { 17 SkDEBUGCODE(fAllocationCnt = 0); 18 19 minAllocSize = GrMax<size_t>(minAllocSize, 1 << 10); 20 fMinAllocSize = GrSizeAlignUp(minAllocSize + kPerAllocPad, kAlignment), 21 fPreallocSize = GrSizeAlignUp(preallocSize + kPerAllocPad, kAlignment); 22 fPreallocSize = GrMax(fPreallocSize, fMinAllocSize); 23 24 fHead = CreateBlock(fPreallocSize); 25 fTail = fHead; 26 fHead->fNext = NULL; 27 fHead->fPrev = NULL; 28 VALIDATE; 29 }; 30 31 GrMemoryPool::~GrMemoryPool() { 32 VALIDATE; 33 SkASSERT(0 == fAllocationCnt); 34 SkASSERT(fHead == fTail); 35 SkASSERT(0 == fHead->fLiveCount); 36 DeleteBlock(fHead); 37 }; 38 39 void* GrMemoryPool::allocate(size_t size) { 40 VALIDATE; 41 size = GrSizeAlignUp(size, kAlignment); 42 size += kPerAllocPad; 43 if (fTail->fFreeSize < size) { 44 size_t blockSize = size; 45 blockSize = GrMax<size_t>(blockSize, fMinAllocSize); 46 BlockHeader* block = CreateBlock(blockSize); 47 48 block->fPrev = fTail; 49 block->fNext = NULL; 50 SkASSERT(NULL == fTail->fNext); 51 fTail->fNext = block; 52 fTail = block; 53 } 54 SkASSERT(fTail->fFreeSize >= size); 55 intptr_t ptr = fTail->fCurrPtr; 56 // We stash a pointer to the block header, just before the allocated space, 57 // so that we can decrement the live count on delete in constant time. 58 *reinterpret_cast<BlockHeader**>(ptr) = fTail; 59 ptr += kPerAllocPad; 60 fTail->fPrevPtr = fTail->fCurrPtr; 61 fTail->fCurrPtr += size; 62 fTail->fFreeSize -= size; 63 fTail->fLiveCount += 1; 64 SkDEBUGCODE(++fAllocationCnt); 65 VALIDATE; 66 return reinterpret_cast<void*>(ptr); 67 } 68 69 void GrMemoryPool::release(void* p) { 70 VALIDATE; 71 intptr_t ptr = reinterpret_cast<intptr_t>(p) - kPerAllocPad; 72 BlockHeader* block = *reinterpret_cast<BlockHeader**>(ptr); 73 if (1 == block->fLiveCount) { 74 // the head block is special, it is reset rather than deleted 75 if (fHead == block) { 76 fHead->fCurrPtr = reinterpret_cast<intptr_t>(fHead) + 77 kHeaderSize; 78 fHead->fLiveCount = 0; 79 fHead->fFreeSize = fPreallocSize; 80 } else { 81 BlockHeader* prev = block->fPrev; 82 BlockHeader* next = block->fNext; 83 SkASSERT(prev); 84 prev->fNext = next; 85 if (next) { 86 next->fPrev = prev; 87 } else { 88 SkASSERT(fTail == block); 89 fTail = prev; 90 } 91 DeleteBlock(block); 92 } 93 } else { 94 --block->fLiveCount; 95 // Trivial reclaim: if we're releasing the most recent allocation, reuse it 96 if (block->fPrevPtr == ptr) { 97 block->fFreeSize += (block->fCurrPtr - block->fPrevPtr); 98 block->fCurrPtr = block->fPrevPtr; 99 } 100 } 101 SkDEBUGCODE(--fAllocationCnt); 102 VALIDATE; 103 } 104 105 GrMemoryPool::BlockHeader* GrMemoryPool::CreateBlock(size_t size) { 106 BlockHeader* block = 107 reinterpret_cast<BlockHeader*>(sk_malloc_throw(size + kHeaderSize)); 108 // we assume malloc gives us aligned memory 109 SkASSERT(!(reinterpret_cast<intptr_t>(block) % kAlignment)); 110 block->fLiveCount = 0; 111 block->fFreeSize = size; 112 block->fCurrPtr = reinterpret_cast<intptr_t>(block) + kHeaderSize; 113 block->fPrevPtr = 0; // gcc warns on assigning NULL to an intptr_t. 114 return block; 115 } 116 117 void GrMemoryPool::DeleteBlock(BlockHeader* block) { 118 sk_free(block); 119 } 120 121 void GrMemoryPool::validate() { 122 #ifdef SK_DEBUG 123 BlockHeader* block = fHead; 124 BlockHeader* prev = NULL; 125 SkASSERT(block); 126 int allocCount = 0; 127 do { 128 allocCount += block->fLiveCount; 129 SkASSERT(prev == block->fPrev); 130 if (NULL != prev) { 131 SkASSERT(prev->fNext == block); 132 } 133 134 intptr_t b = reinterpret_cast<intptr_t>(block); 135 size_t ptrOffset = block->fCurrPtr - b; 136 size_t totalSize = ptrOffset + block->fFreeSize; 137 size_t userSize = totalSize - kHeaderSize; 138 intptr_t userStart = b + kHeaderSize; 139 140 SkASSERT(!(b % kAlignment)); 141 SkASSERT(!(totalSize % kAlignment)); 142 SkASSERT(!(userSize % kAlignment)); 143 SkASSERT(!(block->fCurrPtr % kAlignment)); 144 if (fHead != block) { 145 SkASSERT(block->fLiveCount); 146 SkASSERT(userSize >= fMinAllocSize); 147 } else { 148 SkASSERT(userSize == fPreallocSize); 149 } 150 if (!block->fLiveCount) { 151 SkASSERT(ptrOffset == kHeaderSize); 152 SkASSERT(userStart == block->fCurrPtr); 153 } else { 154 SkASSERT(block == *reinterpret_cast<BlockHeader**>(userStart)); 155 } 156 prev = block; 157 } while ((block = block->fNext)); 158 SkASSERT(allocCount == fAllocationCnt); 159 SkASSERT(prev == fTail); 160 #endif 161 } 162