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