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