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