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