• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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