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