blob: 7d085eaec8401487774d219d9ea498f6daf13735 [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
8#include "GrMemoryPool.h"
Herb Derbyb549cc32017-03-27 13:35:15 -04009#include "SkMalloc.h"
Brian Salomona5002c32017-03-28 16:51:02 -040010#ifdef SK_DEBUG
11#include "SkAtomics.h"
12#endif
Robert Phillips7c525e62018-06-12 10:11:12 -040013#include "ops/GrOp.h"
Ethan Nicholas222e2752018-10-11 11:21:34 -040014#include "effects/GrSkSLFP.h"
Herb Derbyd7b34a52017-03-20 11:19:23 -040015
commit-bot@chromium.org515dcd32013-08-28 14:17:03 +000016#ifdef SK_DEBUG
bsalomon@google.com4da34e32012-06-19 15:40:27 +000017 #define VALIDATE this->validate()
18#else
19 #define VALIDATE
20#endif
21
Ethan Nicholas222e2752018-10-11 11:21:34 -040022enum {
23 // We assume this alignment is good enough for everybody.
24 kAlignment = alignof(GrSkSLFP),
25 kHeaderSize = GR_CT_ALIGN_UP(GrMemoryPool::kBlockHeaderSize, kAlignment),
26 kPerAllocPad = GR_CT_ALIGN_UP(GrMemoryPool::kAllocHeaderSize, kAlignment),
27};
28
Robert Phillips7c525e62018-06-12 10:11:12 -040029void GrOpMemoryPool::release(std::unique_ptr<GrOp> op) {
30 GrOp* tmp = op.release();
31 SkASSERT(tmp);
32 tmp->~GrOp();
33 fMemoryPool.release(tmp);
34}
35
dskibae4cd0062016-11-29 06:50:35 -080036constexpr size_t GrMemoryPool::kSmallestMinAllocSize;
37
bsalomon@google.com4da34e32012-06-19 15:40:27 +000038GrMemoryPool::GrMemoryPool(size_t preallocSize, size_t minAllocSize) {
commit-bot@chromium.org1acc3d72013-09-06 23:13:05 +000039 SkDEBUGCODE(fAllocationCnt = 0);
joshualittb7133be2015-04-08 09:08:31 -070040 SkDEBUGCODE(fAllocBlockCnt = 0);
bsalomon@google.com4da34e32012-06-19 15:40:27 +000041
dskibae4cd0062016-11-29 06:50:35 -080042 minAllocSize = SkTMax<size_t>(GrSizeAlignUp(minAllocSize, kAlignment), kSmallestMinAllocSize);
43 preallocSize = SkTMax<size_t>(GrSizeAlignUp(preallocSize, kAlignment), minAllocSize);
44
45 fMinAllocSize = minAllocSize;
joshualitt22c6f5c2016-01-05 08:07:18 -080046 fSize = 0;
bsalomon@google.com4da34e32012-06-19 15:40:27 +000047
dskibae4cd0062016-11-29 06:50:35 -080048 fHead = CreateBlock(preallocSize);
bsalomon@google.com4da34e32012-06-19 15:40:27 +000049 fTail = fHead;
halcanary96fcdcc2015-08-27 07:41:13 -070050 fHead->fNext = nullptr;
51 fHead->fPrev = nullptr;
bsalomon@google.com4da34e32012-06-19 15:40:27 +000052 VALIDATE;
53};
54
55GrMemoryPool::~GrMemoryPool() {
56 VALIDATE;
Brian Salomona5002c32017-03-28 16:51:02 -040057#ifdef SK_DEBUG
58 int i = 0;
59 int n = fAllocatedIDs.count();
60 fAllocatedIDs.foreach([&i, n] (int32_t id) {
61 if (++i == 1) {
62 SkDebugf("Leaked IDs (in no particular order): %d", id);
63 } else if (i < 11) {
64 SkDebugf(", %d%s", id, (n == i ? "\n" : ""));
65 } else if (i == 11) {
66 SkDebugf(", ...\n");
67 }
68 });
69#endif
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +000070 SkASSERT(0 == fAllocationCnt);
71 SkASSERT(fHead == fTail);
72 SkASSERT(0 == fHead->fLiveCount);
bsalomon@google.com4da34e32012-06-19 15:40:27 +000073 DeleteBlock(fHead);
74};
75
76void* GrMemoryPool::allocate(size_t size) {
77 VALIDATE;
bsalomon@google.com4da34e32012-06-19 15:40:27 +000078 size += kPerAllocPad;
robertphillips19c62502016-01-06 07:04:46 -080079 size = GrSizeAlignUp(size, kAlignment);
bsalomon@google.com4da34e32012-06-19 15:40:27 +000080 if (fTail->fFreeSize < size) {
dskibae4cd0062016-11-29 06:50:35 -080081 size_t blockSize = size + kHeaderSize;
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +000082 blockSize = SkTMax<size_t>(blockSize, fMinAllocSize);
bsalomon@google.com4da34e32012-06-19 15:40:27 +000083 BlockHeader* block = CreateBlock(blockSize);
84
85 block->fPrev = fTail;
halcanary96fcdcc2015-08-27 07:41:13 -070086 block->fNext = nullptr;
87 SkASSERT(nullptr == fTail->fNext);
bsalomon@google.com4da34e32012-06-19 15:40:27 +000088 fTail->fNext = block;
89 fTail = block;
joshualittb7133be2015-04-08 09:08:31 -070090 fSize += block->fSize;
91 SkDEBUGCODE(++fAllocBlockCnt);
bsalomon@google.com4da34e32012-06-19 15:40:27 +000092 }
robertphillipsb7f4b8e2016-01-07 10:12:16 -080093 SkASSERT(kAssignedMarker == fTail->fBlockSentinal);
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +000094 SkASSERT(fTail->fFreeSize >= size);
bsalomon@google.com4da34e32012-06-19 15:40:27 +000095 intptr_t ptr = fTail->fCurrPtr;
96 // We stash a pointer to the block header, just before the allocated space,
97 // so that we can decrement the live count on delete in constant time.
robertphillips19c62502016-01-06 07:04:46 -080098 AllocHeader* allocData = reinterpret_cast<AllocHeader*>(ptr);
99 SkDEBUGCODE(allocData->fSentinal = kAssignedMarker);
Brian Salomona5002c32017-03-28 16:51:02 -0400100 SkDEBUGCODE(allocData->fID = []{static int32_t gID; return sk_atomic_inc(&gID) + 1;}());
101 // You can set a breakpoint here when a leaked ID is allocated to see the stack frame.
102 SkDEBUGCODE(fAllocatedIDs.add(allocData->fID));
robertphillips19c62502016-01-06 07:04:46 -0800103 allocData->fHeader = fTail;
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000104 ptr += kPerAllocPad;
tomhudson@google.comdcba4c22012-07-24 21:36:16 +0000105 fTail->fPrevPtr = fTail->fCurrPtr;
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000106 fTail->fCurrPtr += size;
107 fTail->fFreeSize -= size;
108 fTail->fLiveCount += 1;
commit-bot@chromium.org1acc3d72013-09-06 23:13:05 +0000109 SkDEBUGCODE(++fAllocationCnt);
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000110 VALIDATE;
111 return reinterpret_cast<void*>(ptr);
112}
113
114void GrMemoryPool::release(void* p) {
115 VALIDATE;
116 intptr_t ptr = reinterpret_cast<intptr_t>(p) - kPerAllocPad;
robertphillips19c62502016-01-06 07:04:46 -0800117 AllocHeader* allocData = reinterpret_cast<AllocHeader*>(ptr);
118 SkASSERT(kAssignedMarker == allocData->fSentinal);
119 SkDEBUGCODE(allocData->fSentinal = kFreedMarker);
Brian Salomona5002c32017-03-28 16:51:02 -0400120 SkDEBUGCODE(fAllocatedIDs.remove(allocData->fID));
robertphillips19c62502016-01-06 07:04:46 -0800121 BlockHeader* block = allocData->fHeader;
robertphillipsb7f4b8e2016-01-07 10:12:16 -0800122 SkASSERT(kAssignedMarker == block->fBlockSentinal);
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000123 if (1 == block->fLiveCount) {
124 // the head block is special, it is reset rather than deleted
125 if (fHead == block) {
joshualittb7133be2015-04-08 09:08:31 -0700126 fHead->fCurrPtr = reinterpret_cast<intptr_t>(fHead) + kHeaderSize;
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000127 fHead->fLiveCount = 0;
dskibae4cd0062016-11-29 06:50:35 -0800128 fHead->fFreeSize = fHead->fSize - kHeaderSize;
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000129 } else {
130 BlockHeader* prev = block->fPrev;
131 BlockHeader* next = block->fNext;
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000132 SkASSERT(prev);
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000133 prev->fNext = next;
134 if (next) {
135 next->fPrev = prev;
136 } else {
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000137 SkASSERT(fTail == block);
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000138 fTail = prev;
139 }
joshualittb7133be2015-04-08 09:08:31 -0700140 fSize -= block->fSize;
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000141 DeleteBlock(block);
joshualittb7133be2015-04-08 09:08:31 -0700142 SkDEBUGCODE(fAllocBlockCnt--);
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000143 }
144 } else {
145 --block->fLiveCount;
tomhudson@google.comdcba4c22012-07-24 21:36:16 +0000146 // Trivial reclaim: if we're releasing the most recent allocation, reuse it
147 if (block->fPrevPtr == ptr) {
148 block->fFreeSize += (block->fCurrPtr - block->fPrevPtr);
149 block->fCurrPtr = block->fPrevPtr;
150 }
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000151 }
commit-bot@chromium.org1acc3d72013-09-06 23:13:05 +0000152 SkDEBUGCODE(--fAllocationCnt);
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000153 VALIDATE;
154}
155
dskibae4cd0062016-11-29 06:50:35 -0800156GrMemoryPool::BlockHeader* GrMemoryPool::CreateBlock(size_t blockSize) {
157 blockSize = SkTMax<size_t>(blockSize, kHeaderSize);
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000158 BlockHeader* block =
dskibae4cd0062016-11-29 06:50:35 -0800159 reinterpret_cast<BlockHeader*>(sk_malloc_throw(blockSize));
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000160 // we assume malloc gives us aligned memory
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000161 SkASSERT(!(reinterpret_cast<intptr_t>(block) % kAlignment));
robertphillipsb7f4b8e2016-01-07 10:12:16 -0800162 SkDEBUGCODE(block->fBlockSentinal = kAssignedMarker);
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000163 block->fLiveCount = 0;
dskibae4cd0062016-11-29 06:50:35 -0800164 block->fFreeSize = blockSize - kHeaderSize;
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000165 block->fCurrPtr = reinterpret_cast<intptr_t>(block) + kHeaderSize;
halcanary96fcdcc2015-08-27 07:41:13 -0700166 block->fPrevPtr = 0; // gcc warns on assigning nullptr to an intptr_t.
dskibae4cd0062016-11-29 06:50:35 -0800167 block->fSize = blockSize;
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000168 return block;
169}
170
171void GrMemoryPool::DeleteBlock(BlockHeader* block) {
robertphillipsb7f4b8e2016-01-07 10:12:16 -0800172 SkASSERT(kAssignedMarker == block->fBlockSentinal);
173 SkDEBUGCODE(block->fBlockSentinal = kFreedMarker); // FWIW
reed@google.com939ca7c2013-09-26 19:56:51 +0000174 sk_free(block);
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000175}
176
177void GrMemoryPool::validate() {
humper@google.com0e515772013-01-07 19:54:40 +0000178#ifdef SK_DEBUG
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000179 BlockHeader* block = fHead;
halcanary96fcdcc2015-08-27 07:41:13 -0700180 BlockHeader* prev = nullptr;
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000181 SkASSERT(block);
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000182 int allocCount = 0;
183 do {
robertphillipsb7f4b8e2016-01-07 10:12:16 -0800184 SkASSERT(kAssignedMarker == block->fBlockSentinal);
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000185 allocCount += block->fLiveCount;
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000186 SkASSERT(prev == block->fPrev);
bsalomon49f085d2014-09-05 13:34:00 -0700187 if (prev) {
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000188 SkASSERT(prev->fNext == block);
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000189 }
190
191 intptr_t b = reinterpret_cast<intptr_t>(block);
192 size_t ptrOffset = block->fCurrPtr - b;
193 size_t totalSize = ptrOffset + block->fFreeSize;
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000194 intptr_t userStart = b + kHeaderSize;
195
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000196 SkASSERT(!(b % kAlignment));
197 SkASSERT(!(totalSize % kAlignment));
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000198 SkASSERT(!(block->fCurrPtr % kAlignment));
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000199 if (fHead != block) {
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000200 SkASSERT(block->fLiveCount);
dskibae4cd0062016-11-29 06:50:35 -0800201 SkASSERT(totalSize >= fMinAllocSize);
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000202 } else {
dskibae4cd0062016-11-29 06:50:35 -0800203 SkASSERT(totalSize == block->fSize);
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000204 }
205 if (!block->fLiveCount) {
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000206 SkASSERT(ptrOffset == kHeaderSize);
207 SkASSERT(userStart == block->fCurrPtr);
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000208 } else {
robertphillips19c62502016-01-06 07:04:46 -0800209 AllocHeader* allocData = reinterpret_cast<AllocHeader*>(userStart);
halcanary9d524f22016-03-29 09:03:52 -0700210 SkASSERT(allocData->fSentinal == kAssignedMarker ||
robertphillips19c62502016-01-06 07:04:46 -0800211 allocData->fSentinal == kFreedMarker);
212 SkASSERT(block == allocData->fHeader);
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000213 }
robertphillips19c62502016-01-06 07:04:46 -0800214
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000215 prev = block;
216 } while ((block = block->fNext));
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000217 SkASSERT(allocCount == fAllocationCnt);
Brian Salomona5002c32017-03-28 16:51:02 -0400218 SkASSERT(fAllocationCnt == fAllocatedIDs.count());
tfarina@chromium.orgf6de4752013-08-17 00:02:59 +0000219 SkASSERT(prev == fTail);
joshualitt22c6f5c2016-01-05 08:07:18 -0800220 SkASSERT(fAllocBlockCnt != 0 || fSize == 0);
humper@google.com0e515772013-01-07 19:54:40 +0000221#endif
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000222}