1 /*
2 * Copyright 2011 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 "include/private/base/SkTArray.h"
9 #include "include/private/base/SkTDArray.h"
10 #include "src/base/SkRandom.h"
11 #include "src/gpu/ganesh/GrMemoryPool.h"
12 #include "tests/Test.h"
13
14 #include <array>
15 #include <cstddef>
16 #include <cstdint>
17 #include <memory>
18
19 using namespace skia_private;
20
21 // A is the top of an inheritance tree of classes that overload op new and
22 // and delete to use a GrMemoryPool. The objects have values of different types
23 // that can be set and checked.
24 class A {
25 public:
A()26 A() {}
setValues(int v)27 virtual void setValues(int v) {
28 fChar = static_cast<char>(v & 0xFF);
29 }
checkValues(int v)30 virtual bool checkValues(int v) {
31 return fChar == static_cast<char>(v & 0xFF);
32 }
~A()33 virtual ~A() {}
34
operator new(size_t size)35 void* operator new(size_t size) {
36 if (!gPool) {
37 return ::operator new(size);
38 } else {
39 return gPool->allocate(size);
40 }
41 }
42
operator delete(void * p)43 void operator delete(void* p) {
44 if (!gPool) {
45 ::operator delete(p);
46 } else {
47 return gPool->release(p);
48 }
49 }
50
51 static A* Create(SkRandom* r);
52
SetAllocator(size_t preallocSize,size_t minAllocSize)53 static void SetAllocator(size_t preallocSize, size_t minAllocSize) {
54 gPool = GrMemoryPool::Make(preallocSize, minAllocSize);
55 }
56
ResetAllocator()57 static void ResetAllocator() { gPool.reset(); }
58
ValidatePool()59 static void ValidatePool() {
60 #ifdef SK_DEBUG
61 gPool->validate();
62 #endif
63 }
64
65 private:
66 static std::unique_ptr<GrMemoryPool> gPool;
67 char fChar;
68 };
69
70 std::unique_ptr<GrMemoryPool> A::gPool;
71
72 class B : public A {
73 public:
B()74 B() {}
setValues(int v)75 void setValues(int v) override {
76 fDouble = static_cast<double>(v);
77 this->INHERITED::setValues(v);
78 }
checkValues(int v)79 bool checkValues(int v) override {
80 return fDouble == static_cast<double>(v) &&
81 this->INHERITED::checkValues(v);
82 }
83
84 private:
85 double fDouble;
86
87 using INHERITED = A;
88 };
89
90 class C : public A {
91 public:
C()92 C() {}
setValues(int v)93 void setValues(int v) override {
94 fInt64 = static_cast<int64_t>(v);
95 this->INHERITED::setValues(v);
96 }
checkValues(int v)97 bool checkValues(int v) override {
98 return fInt64 == static_cast<int64_t>(v) &&
99 this->INHERITED::checkValues(v);
100 }
101
102 private:
103 int64_t fInt64;
104
105 using INHERITED = A;
106 };
107
108 // D derives from C and owns a dynamically created B
109 class D : public C {
110 public:
D()111 D() {
112 fB = new B();
113 }
setValues(int v)114 void setValues(int v) override {
115 fVoidStar = reinterpret_cast<void*>(static_cast<intptr_t>(v));
116 this->INHERITED::setValues(v);
117 fB->setValues(v);
118 }
checkValues(int v)119 bool checkValues(int v) override {
120 return fVoidStar == reinterpret_cast<void*>(static_cast<intptr_t>(v)) &&
121 fB->checkValues(v) &&
122 this->INHERITED::checkValues(v);
123 }
~D()124 ~D() override {
125 delete fB;
126 }
127 private:
128 void* fVoidStar;
129 B* fB;
130
131 using INHERITED = C;
132 };
133
134 class E : public A {
135 public:
E()136 E() {}
setValues(int v)137 void setValues(int v) override {
138 for (size_t i = 0; i < std::size(fIntArray); ++i) {
139 fIntArray[i] = v;
140 }
141 this->INHERITED::setValues(v);
142 }
checkValues(int v)143 bool checkValues(int v) override {
144 bool ok = true;
145 for (size_t i = 0; ok && i < std::size(fIntArray); ++i) {
146 if (fIntArray[i] != v) {
147 ok = false;
148 }
149 }
150 return ok && this->INHERITED::checkValues(v);
151 }
152 private:
153 int fIntArray[20];
154
155 using INHERITED = A;
156 };
157
Create(SkRandom * r)158 A* A::Create(SkRandom* r) {
159 switch (r->nextRangeU(0, 4)) {
160 case 0:
161 return new A;
162 case 1:
163 return new B;
164 case 2:
165 return new C;
166 case 3:
167 return new D;
168 case 4:
169 return new E;
170 default:
171 // suppress warning
172 return nullptr;
173 }
174 }
175
176 struct Rec {
177 A* fInstance;
178 int fValue;
179 };
180
181 #if 0 // TODO: 133.The test is failing and will cause the dm (--src tests) to crash.
182 DEF_TEST(GrMemoryPool, reporter) {
183 // prealloc and min alloc sizes for the pool
184 static const size_t gSizes[][2] = {
185 {0, 0},
186 {10 * sizeof(A), 20 * sizeof(A)},
187 {100 * sizeof(A), 100 * sizeof(A)},
188 {500 * sizeof(A), 500 * sizeof(A)},
189 {10000 * sizeof(A), 0},
190 {1, 100 * sizeof(A)},
191 };
192
193 // different percentages of creation vs deletion
194 static const float gCreateFraction[] = {1.f, .95f, 0.75f, .5f};
195 // number of create/destroys per test
196 static const int kNumIters = 20000;
197 // check that all the values stored in A objects are correct after this
198 // number of iterations
199 static const int kCheckPeriod = 500;
200
201 SkRandom r;
202 for (size_t s = 0; s < std::size(gSizes); ++s) {
203 A::SetAllocator(gSizes[s][0], gSizes[s][1]);
204 A::ValidatePool();
205 for (size_t c = 0; c < std::size(gCreateFraction); ++c) {
206 SkTDArray<Rec> instanceRecs;
207 for (int i = 0; i < kNumIters; ++i) {
208 float createOrDestroy = r.nextUScalar1();
209 if (createOrDestroy < gCreateFraction[c] ||
210 0 == instanceRecs.size()) {
211 Rec* rec = instanceRecs.append();
212 rec->fInstance = A::Create(&r);
213 rec->fValue = static_cast<int>(r.nextU());
214 rec->fInstance->setValues(rec->fValue);
215 } else {
216 int d = r.nextRangeU(0, instanceRecs.size() - 1);
217 Rec& rec = instanceRecs[d];
218 REPORTER_ASSERT(reporter, rec.fInstance->checkValues(rec.fValue));
219 delete rec.fInstance;
220 instanceRecs.removeShuffle(d);
221 }
222 if (0 == i % kCheckPeriod) {
223 A::ValidatePool();
224 for (Rec& rec : instanceRecs) {
225 REPORTER_ASSERT(reporter, rec.fInstance->checkValues(rec.fValue));
226 }
227 }
228 }
229 for (Rec& rec : instanceRecs) {
230 REPORTER_ASSERT(reporter, rec.fInstance->checkValues(rec.fValue));
231 delete rec.fInstance;
232 }
233 }
234 }
235 }
236 #endif
237
238 // GrMemoryPool requires that it's empty at the point of destruction. This helps
239 // achieving that by releasing all added memory in the destructor.
240 class AutoPoolReleaser {
241 public:
AutoPoolReleaser(GrMemoryPool & pool)242 AutoPoolReleaser(GrMemoryPool& pool): fPool(pool) {
243 }
~AutoPoolReleaser()244 ~AutoPoolReleaser() {
245 for (void* ptr: fAllocated) {
246 fPool.release(ptr);
247 }
248 }
add(void * ptr)249 void add(void* ptr) {
250 fAllocated.push_back(ptr);
251 }
252 private:
253 GrMemoryPool& fPool;
254 TArray<void*> fAllocated;
255 };
256
DEF_TEST(GrMemoryPoolAPI,reporter)257 DEF_TEST(GrMemoryPoolAPI, reporter) {
258 constexpr size_t kSmallestMinAllocSize = GrMemoryPool::kMinAllocationSize;
259
260 // Allocates memory until pool adds a new block (pool->size() changes).
261 auto allocateMemory = [](GrMemoryPool& pool, AutoPoolReleaser& r) {
262 size_t origPoolSize = pool.size();
263 while (pool.size() == origPoolSize) {
264 r.add(pool.allocate(31));
265 }
266 };
267
268 // Effective prealloc space capacity is >= kMinAllocationSize.
269 {
270 auto pool = GrMemoryPool::Make(0, 0);
271 REPORTER_ASSERT(reporter, pool->preallocSize() == kSmallestMinAllocSize);
272 }
273
274 // Effective block size capacity >= kMinAllocationSize.
275 {
276 auto pool = GrMemoryPool::Make(kSmallestMinAllocSize, kSmallestMinAllocSize / 2);
277 AutoPoolReleaser r(*pool);
278
279 allocateMemory(*pool, r);
280 REPORTER_ASSERT(reporter, pool->size() == kSmallestMinAllocSize);
281 }
282
283 // Pool allocates exactly preallocSize on creation.
284 {
285 constexpr size_t kPreallocSize = kSmallestMinAllocSize * 5;
286 auto pool = GrMemoryPool::Make(kPreallocSize, 0);
287 REPORTER_ASSERT(reporter, pool->preallocSize() == kPreallocSize);
288 }
289
290 // Pool allocates exactly minAllocSize when it expands.
291 {
292 constexpr size_t kMinAllocSize = kSmallestMinAllocSize * 7;
293 auto pool = GrMemoryPool::Make(0, kMinAllocSize);
294 AutoPoolReleaser r(*pool);
295 REPORTER_ASSERT(reporter, pool->size() == 0);
296
297 allocateMemory(*pool, r);
298 REPORTER_ASSERT(reporter, pool->size() == kMinAllocSize);
299
300 allocateMemory(*pool, r);
301 REPORTER_ASSERT(reporter, pool->size() == 2 * kMinAllocSize);
302 }
303
304 // When asked to allocate amount > minAllocSize, pool allocates larger block
305 // to accommodate all internal structures.
306 {
307 constexpr size_t kMinAllocSize = kSmallestMinAllocSize * 2;
308 auto pool = GrMemoryPool::Make(kSmallestMinAllocSize, kMinAllocSize);
309 AutoPoolReleaser r(*pool);
310
311 REPORTER_ASSERT(reporter, pool->size() == 0);
312
313 constexpr size_t hugeSize = 10 * kMinAllocSize;
314 r.add(pool->allocate(hugeSize));
315 REPORTER_ASSERT(reporter, pool->size() > hugeSize);
316
317 // Block size allocated to accommodate huge request doesn't include any extra
318 // space, so next allocation request allocates a new block.
319 size_t hugeBlockSize = pool->size();
320 r.add(pool->allocate(0));
321 REPORTER_ASSERT(reporter, pool->size() == hugeBlockSize + kMinAllocSize);
322 }
323 }
324