blob: b5249e18136f1ff75e1a8d3670592b1339907988 [file] [log] [blame]
bsalomon@google.com4da34e32012-06-19 15:40:27 +00001/*
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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/private/SkMalloc.h"
9#include "src/gpu/GrMemoryPool.h"
10#include "src/gpu/ops/GrOp.h"
Mike Klein0ec1c572018-12-04 11:52:51 -050011#ifdef SK_DEBUG
12 #include <atomic>
13#endif
Herb Derbyd7b34a52017-03-20 11:19:23 -040014
commit-bot@chromium.org515dcd32013-08-28 14:17:03 +000015#ifdef SK_DEBUG
bsalomon@google.com4da34e32012-06-19 15:40:27 +000016 #define VALIDATE this->validate()
17#else
18 #define VALIDATE
19#endif
20
Brian Salomon6986c652019-12-12 10:58:47 -050021std::unique_ptr<GrMemoryPool> GrMemoryPool::Make(size_t preallocSize, size_t minAllocSize) {
22 preallocSize = std::max(preallocSize, kMinAllocationSize);
Brian Salomone9943802020-01-03 13:07:07 -050023 static constexpr size_t kPoolSize = GrAlignTo(sizeof(GrMemoryPool), kAlignment);
Brian Salomon6986c652019-12-12 10:58:47 -050024 size_t size = kPoolSize + preallocSize;
25 void* mem = operator new(size);
26 void* preallocStart = static_cast<char*>(mem) + kPoolSize;
27 return std::unique_ptr<GrMemoryPool>(
28 new (mem) GrMemoryPool(preallocStart, preallocSize, minAllocSize));
Robert Phillips7c525e62018-06-12 10:11:12 -040029}
30
Brian Salomon6986c652019-12-12 10:58:47 -050031GrMemoryPool::GrMemoryPool(void* preallocStart, size_t preallocSize, size_t minAllocSize) {
commit-bot@chromium.org1acc3d72013-09-06 23:13:05 +000032 SkDEBUGCODE(fAllocationCnt = 0);
joshualittb7133be2015-04-08 09:08:31 -070033 SkDEBUGCODE(fAllocBlockCnt = 0);
bsalomon@google.com4da34e32012-06-19 15:40:27 +000034
Brian Salomon6986c652019-12-12 10:58:47 -050035 minAllocSize = std::max(minAllocSize, kMinAllocationSize);
dskibae4cd0062016-11-29 06:50:35 -080036
37 fMinAllocSize = minAllocSize;
joshualitt22c6f5c2016-01-05 08:07:18 -080038 fSize = 0;
bsalomon@google.com4da34e32012-06-19 15:40:27 +000039
Brian Salomon6986c652019-12-12 10:58:47 -050040 fHead = InitBlock(preallocStart, preallocSize);
bsalomon@google.com4da34e32012-06-19 15:40:27 +000041 fTail = fHead;
halcanary96fcdcc2015-08-27 07:41:13 -070042 fHead->fNext = nullptr;
43 fHead->fPrev = nullptr;
bsalomon@google.com4da34e32012-06-19 15:40:27 +000044 VALIDATE;
45};
46
47GrMemoryPool::~GrMemoryPool() {
48 VALIDATE;
Brian Salomona5002c32017-03-28 16:51:02 -040049#ifdef SK_DEBUG
50 int i = 0;
51 int n = fAllocatedIDs.count();
52 fAllocatedIDs.foreach([&i, n] (int32_t id) {
53 if (++i == 1) {
Robert Phillips19f466d2020-02-26 10:27:07 -050054 SkDebugf("Leaked %d IDs (in no particular order): %d%s", n, id, (n == i) ? "\n" : "");
Brian Salomona5002c32017-03-28 16:51:02 -040055 } else if (i < 11) {
56 SkDebugf(", %d%s", id, (n == i ? "\n" : ""));
57 } else if (i == 11) {
58 SkDebugf(", ...\n");
59 }
60 });
61#endif
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +000062 SkASSERT(0 == fAllocationCnt);
63 SkASSERT(fHead == fTail);
64 SkASSERT(0 == fHead->fLiveCount);
Brian Salomon6986c652019-12-12 10:58:47 -050065 SkASSERT(kAssignedMarker == fHead->fBlockSentinal);
bsalomon@google.com4da34e32012-06-19 15:40:27 +000066};
67
68void* GrMemoryPool::allocate(size_t size) {
69 VALIDATE;
bsalomon@google.com4da34e32012-06-19 15:40:27 +000070 size += kPerAllocPad;
Brian Salomone9943802020-01-03 13:07:07 -050071 size = GrAlignTo(size, kAlignment);
bsalomon@google.com4da34e32012-06-19 15:40:27 +000072 if (fTail->fFreeSize < size) {
dskibae4cd0062016-11-29 06:50:35 -080073 size_t blockSize = size + kHeaderSize;
Brian Salomon6986c652019-12-12 10:58:47 -050074 blockSize = std::max(blockSize, fMinAllocSize);
bsalomon@google.com4da34e32012-06-19 15:40:27 +000075 BlockHeader* block = CreateBlock(blockSize);
76
77 block->fPrev = fTail;
halcanary96fcdcc2015-08-27 07:41:13 -070078 block->fNext = nullptr;
79 SkASSERT(nullptr == fTail->fNext);
bsalomon@google.com4da34e32012-06-19 15:40:27 +000080 fTail->fNext = block;
81 fTail = block;
joshualittb7133be2015-04-08 09:08:31 -070082 fSize += block->fSize;
83 SkDEBUGCODE(++fAllocBlockCnt);
bsalomon@google.com4da34e32012-06-19 15:40:27 +000084 }
robertphillipsb7f4b8e2016-01-07 10:12:16 -080085 SkASSERT(kAssignedMarker == fTail->fBlockSentinal);
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +000086 SkASSERT(fTail->fFreeSize >= size);
bsalomon@google.com4da34e32012-06-19 15:40:27 +000087 intptr_t ptr = fTail->fCurrPtr;
88 // We stash a pointer to the block header, just before the allocated space,
89 // so that we can decrement the live count on delete in constant time.
robertphillips19c62502016-01-06 07:04:46 -080090 AllocHeader* allocData = reinterpret_cast<AllocHeader*>(ptr);
91 SkDEBUGCODE(allocData->fSentinal = kAssignedMarker);
Mike Klein0ec1c572018-12-04 11:52:51 -050092 SkDEBUGCODE(allocData->fID = []{
93 static std::atomic<int32_t> nextID{1};
94 return nextID++;
95 }());
Brian Salomona5002c32017-03-28 16:51:02 -040096 // You can set a breakpoint here when a leaked ID is allocated to see the stack frame.
97 SkDEBUGCODE(fAllocatedIDs.add(allocData->fID));
robertphillips19c62502016-01-06 07:04:46 -080098 allocData->fHeader = fTail;
bsalomon@google.com4da34e32012-06-19 15:40:27 +000099 ptr += kPerAllocPad;
tomhudson@google.comdcba4c22012-07-24 21:36:16 +0000100 fTail->fPrevPtr = fTail->fCurrPtr;
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000101 fTail->fCurrPtr += size;
102 fTail->fFreeSize -= size;
103 fTail->fLiveCount += 1;
commit-bot@chromium.org1acc3d72013-09-06 23:13:05 +0000104 SkDEBUGCODE(++fAllocationCnt);
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000105 VALIDATE;
106 return reinterpret_cast<void*>(ptr);
107}
108
109void GrMemoryPool::release(void* p) {
110 VALIDATE;
111 intptr_t ptr = reinterpret_cast<intptr_t>(p) - kPerAllocPad;
robertphillips19c62502016-01-06 07:04:46 -0800112 AllocHeader* allocData = reinterpret_cast<AllocHeader*>(ptr);
113 SkASSERT(kAssignedMarker == allocData->fSentinal);
114 SkDEBUGCODE(allocData->fSentinal = kFreedMarker);
Brian Salomona5002c32017-03-28 16:51:02 -0400115 SkDEBUGCODE(fAllocatedIDs.remove(allocData->fID));
robertphillips19c62502016-01-06 07:04:46 -0800116 BlockHeader* block = allocData->fHeader;
robertphillipsb7f4b8e2016-01-07 10:12:16 -0800117 SkASSERT(kAssignedMarker == block->fBlockSentinal);
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000118 if (1 == block->fLiveCount) {
119 // the head block is special, it is reset rather than deleted
120 if (fHead == block) {
joshualittb7133be2015-04-08 09:08:31 -0700121 fHead->fCurrPtr = reinterpret_cast<intptr_t>(fHead) + kHeaderSize;
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000122 fHead->fLiveCount = 0;
dskibae4cd0062016-11-29 06:50:35 -0800123 fHead->fFreeSize = fHead->fSize - kHeaderSize;
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000124 } else {
125 BlockHeader* prev = block->fPrev;
126 BlockHeader* next = block->fNext;
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000127 SkASSERT(prev);
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000128 prev->fNext = next;
129 if (next) {
130 next->fPrev = prev;
131 } else {
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000132 SkASSERT(fTail == block);
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000133 fTail = prev;
134 }
joshualittb7133be2015-04-08 09:08:31 -0700135 fSize -= block->fSize;
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000136 DeleteBlock(block);
joshualittb7133be2015-04-08 09:08:31 -0700137 SkDEBUGCODE(fAllocBlockCnt--);
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000138 }
139 } else {
140 --block->fLiveCount;
tomhudson@google.comdcba4c22012-07-24 21:36:16 +0000141 // Trivial reclaim: if we're releasing the most recent allocation, reuse it
142 if (block->fPrevPtr == ptr) {
143 block->fFreeSize += (block->fCurrPtr - block->fPrevPtr);
144 block->fCurrPtr = block->fPrevPtr;
145 }
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000146 }
commit-bot@chromium.org1acc3d72013-09-06 23:13:05 +0000147 SkDEBUGCODE(--fAllocationCnt);
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000148 VALIDATE;
149}
150
dskibae4cd0062016-11-29 06:50:35 -0800151GrMemoryPool::BlockHeader* GrMemoryPool::CreateBlock(size_t blockSize) {
Brian Salomon6986c652019-12-12 10:58:47 -0500152 blockSize = std::max(blockSize, kHeaderSize);
153 return InitBlock(sk_malloc_throw(blockSize), blockSize);
154}
155
156auto GrMemoryPool::InitBlock(void* mem, size_t blockSize) -> BlockHeader* {
157 SkASSERT(!(reinterpret_cast<intptr_t>(mem) % kAlignment));
158 auto block = reinterpret_cast<BlockHeader*>(mem);
robertphillipsb7f4b8e2016-01-07 10:12:16 -0800159 SkDEBUGCODE(block->fBlockSentinal = kAssignedMarker);
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000160 block->fLiveCount = 0;
dskibae4cd0062016-11-29 06:50:35 -0800161 block->fFreeSize = blockSize - kHeaderSize;
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000162 block->fCurrPtr = reinterpret_cast<intptr_t>(block) + kHeaderSize;
halcanary96fcdcc2015-08-27 07:41:13 -0700163 block->fPrevPtr = 0; // gcc warns on assigning nullptr to an intptr_t.
dskibae4cd0062016-11-29 06:50:35 -0800164 block->fSize = blockSize;
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000165 return block;
166}
167
168void GrMemoryPool::DeleteBlock(BlockHeader* block) {
robertphillipsb7f4b8e2016-01-07 10:12:16 -0800169 SkASSERT(kAssignedMarker == block->fBlockSentinal);
170 SkDEBUGCODE(block->fBlockSentinal = kFreedMarker); // FWIW
reed@google.com939ca7c2013-09-26 19:56:51 +0000171 sk_free(block);
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000172}
173
174void GrMemoryPool::validate() {
humper@google.com0e515772013-01-07 19:54:40 +0000175#ifdef SK_DEBUG
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000176 BlockHeader* block = fHead;
halcanary96fcdcc2015-08-27 07:41:13 -0700177 BlockHeader* prev = nullptr;
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000178 SkASSERT(block);
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000179 int allocCount = 0;
180 do {
robertphillipsb7f4b8e2016-01-07 10:12:16 -0800181 SkASSERT(kAssignedMarker == block->fBlockSentinal);
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000182 allocCount += block->fLiveCount;
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000183 SkASSERT(prev == block->fPrev);
bsalomon49f085d2014-09-05 13:34:00 -0700184 if (prev) {
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000185 SkASSERT(prev->fNext == block);
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000186 }
187
188 intptr_t b = reinterpret_cast<intptr_t>(block);
189 size_t ptrOffset = block->fCurrPtr - b;
190 size_t totalSize = ptrOffset + block->fFreeSize;
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000191 intptr_t userStart = b + kHeaderSize;
192
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000193 SkASSERT(!(b % kAlignment));
194 SkASSERT(!(totalSize % kAlignment));
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000195 SkASSERT(!(block->fCurrPtr % kAlignment));
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000196 if (fHead != block) {
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000197 SkASSERT(block->fLiveCount);
dskibae4cd0062016-11-29 06:50:35 -0800198 SkASSERT(totalSize >= fMinAllocSize);
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000199 } else {
dskibae4cd0062016-11-29 06:50:35 -0800200 SkASSERT(totalSize == block->fSize);
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000201 }
202 if (!block->fLiveCount) {
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000203 SkASSERT(ptrOffset == kHeaderSize);
204 SkASSERT(userStart == block->fCurrPtr);
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000205 } else {
robertphillips19c62502016-01-06 07:04:46 -0800206 AllocHeader* allocData = reinterpret_cast<AllocHeader*>(userStart);
halcanary9d524f22016-03-29 09:03:52 -0700207 SkASSERT(allocData->fSentinal == kAssignedMarker ||
robertphillips19c62502016-01-06 07:04:46 -0800208 allocData->fSentinal == kFreedMarker);
209 SkASSERT(block == allocData->fHeader);
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000210 }
robertphillips19c62502016-01-06 07:04:46 -0800211
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000212 prev = block;
213 } while ((block = block->fNext));
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000214 SkASSERT(allocCount == fAllocationCnt);
Brian Salomona5002c32017-03-28 16:51:02 -0400215 SkASSERT(fAllocationCnt == fAllocatedIDs.count());
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000216 SkASSERT(prev == fTail);
joshualitt22c6f5c2016-01-05 08:07:18 -0800217 SkASSERT(fAllocBlockCnt != 0 || fSize == 0);
humper@google.com0e515772013-01-07 19:54:40 +0000218#endif
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000219}
Brian Salomon6986c652019-12-12 10:58:47 -0500220
221////////////////////////////////////////////////////////////////////////////////////////
222
Brian Salomone9943802020-01-03 13:07:07 -0500223static constexpr size_t kOpPoolSize = GrAlignTo(sizeof(GrOpMemoryPool), GrMemoryPool::kAlignment);
Brian Salomon6986c652019-12-12 10:58:47 -0500224
225GrOpMemoryPool::~GrOpMemoryPool() { this->pool()->~GrMemoryPool(); }
226
227std::unique_ptr<GrOpMemoryPool> GrOpMemoryPool::Make(size_t preallocSize, size_t minAllocSize) {
228 preallocSize = std::max(preallocSize, GrMemoryPool::kMinAllocationSize);
229 static constexpr size_t kOpPoolSize =
Brian Salomone9943802020-01-03 13:07:07 -0500230 GrAlignTo(sizeof(GrOpMemoryPool), GrMemoryPool::kAlignment);
231 static constexpr size_t kPoolSize = GrAlignTo(sizeof(GrMemoryPool), GrMemoryPool::kAlignment);
Brian Salomon6986c652019-12-12 10:58:47 -0500232 size_t size = kOpPoolSize + kPoolSize + preallocSize;
233 void* mem = operator new(size);
234 void* memPoolPtr = static_cast<char*>(mem) + kOpPoolSize;
235 void* preallocStart = static_cast<char*>(mem) + kOpPoolSize + kPoolSize;
236 new (memPoolPtr) GrMemoryPool(preallocStart, preallocSize, minAllocSize);
237 return std::unique_ptr<GrOpMemoryPool>(new (mem) GrOpMemoryPool());
238}
239
240void GrOpMemoryPool::release(std::unique_ptr<GrOp> op) {
241 GrOp* tmp = op.release();
242 SkASSERT(tmp);
243 tmp->~GrOp();
244 this->pool()->release(tmp);
245}
246
247GrMemoryPool* GrOpMemoryPool::pool() const {
248 auto addr = reinterpret_cast<const char*>(this) + kOpPoolSize;
249 return reinterpret_cast<GrMemoryPool*>(const_cast<char*>(addr));
250}