xref: /aosp_15_r20/external/skia/tests/GrMemoryPoolTest.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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 
DEF_TEST(GrMemoryPool,reporter)181 DEF_TEST(GrMemoryPool, reporter) {
182     // prealloc and min alloc sizes for the pool
183     static const size_t gSizes[][2] = {
184         {0, 0},
185         {10 * sizeof(A), 20 * sizeof(A)},
186         {100 * sizeof(A), 100 * sizeof(A)},
187         {500 * sizeof(A), 500 * sizeof(A)},
188         {10000 * sizeof(A), 0},
189         {1, 100 * sizeof(A)},
190     };
191 
192     // different percentages of creation vs deletion
193     static const float gCreateFraction[] = {1.f, .95f, 0.75f, .5f};
194     // number of create/destroys per test
195     static const int kNumIters = 20000;
196     // check that all the values stored in A objects are correct after this
197     // number of iterations
198     static const int kCheckPeriod = 500;
199 
200     SkRandom r;
201     for (size_t s = 0; s < std::size(gSizes); ++s) {
202         A::SetAllocator(gSizes[s][0], gSizes[s][1]);
203         A::ValidatePool();
204         for (size_t c = 0; c < std::size(gCreateFraction); ++c) {
205             SkTDArray<Rec> instanceRecs;
206             for (int i = 0; i < kNumIters; ++i) {
207                 float createOrDestroy = r.nextUScalar1();
208                 if (createOrDestroy < gCreateFraction[c] ||
209                     0 == instanceRecs.size()) {
210                     Rec* rec = instanceRecs.append();
211                     rec->fInstance = A::Create(&r);
212                     rec->fValue = static_cast<int>(r.nextU());
213                     rec->fInstance->setValues(rec->fValue);
214                 } else {
215                     int d = r.nextRangeU(0, instanceRecs.size() - 1);
216                     Rec& rec = instanceRecs[d];
217                     REPORTER_ASSERT(reporter, rec.fInstance->checkValues(rec.fValue));
218                     delete rec.fInstance;
219                     instanceRecs.removeShuffle(d);
220                 }
221                 if (0 == i % kCheckPeriod) {
222                     A::ValidatePool();
223                     for (Rec& rec : instanceRecs) {
224                         REPORTER_ASSERT(reporter, rec.fInstance->checkValues(rec.fValue));
225                     }
226                 }
227             }
228             for (Rec& rec : instanceRecs) {
229                 REPORTER_ASSERT(reporter, rec.fInstance->checkValues(rec.fValue));
230                 delete rec.fInstance;
231             }
232         }
233     }
234 }
235 
236 // GrMemoryPool requires that it's empty at the point of destruction. This helps
237 // achieving that by releasing all added memory in the destructor.
238 class AutoPoolReleaser {
239 public:
AutoPoolReleaser(GrMemoryPool & pool)240     AutoPoolReleaser(GrMemoryPool& pool): fPool(pool) {
241     }
~AutoPoolReleaser()242     ~AutoPoolReleaser() {
243         for (void* ptr: fAllocated) {
244             fPool.release(ptr);
245         }
246     }
add(void * ptr)247     void add(void* ptr) {
248         fAllocated.push_back(ptr);
249     }
250 private:
251     GrMemoryPool& fPool;
252     TArray<void*> fAllocated;
253 };
254 
DEF_TEST(GrMemoryPoolAPI,reporter)255 DEF_TEST(GrMemoryPoolAPI, reporter) {
256     constexpr size_t kSmallestMinAllocSize = GrMemoryPool::kMinAllocationSize;
257 
258     // Allocates memory until pool adds a new block (pool->size() changes).
259     auto allocateMemory = [](GrMemoryPool& pool, AutoPoolReleaser& r) {
260         size_t origPoolSize = pool.size();
261         while (pool.size() == origPoolSize) {
262             r.add(pool.allocate(31));
263         }
264     };
265 
266     // Effective prealloc space capacity is >= kMinAllocationSize.
267     {
268         auto pool = GrMemoryPool::Make(0, 0);
269         REPORTER_ASSERT(reporter, pool->preallocSize() == kSmallestMinAllocSize);
270     }
271 
272     // Effective block size capacity >= kMinAllocationSize.
273     {
274         auto pool = GrMemoryPool::Make(kSmallestMinAllocSize, kSmallestMinAllocSize / 2);
275         AutoPoolReleaser r(*pool);
276 
277         allocateMemory(*pool, r);
278         REPORTER_ASSERT(reporter, pool->size() == kSmallestMinAllocSize);
279     }
280 
281     // Pool allocates exactly preallocSize on creation.
282     {
283         constexpr size_t kPreallocSize = kSmallestMinAllocSize * 5;
284         auto pool = GrMemoryPool::Make(kPreallocSize, 0);
285         REPORTER_ASSERT(reporter, pool->preallocSize() == kPreallocSize);
286     }
287 
288     // Pool allocates exactly minAllocSize when it expands.
289     {
290         constexpr size_t kMinAllocSize = kSmallestMinAllocSize * 7;
291         auto pool = GrMemoryPool::Make(0, kMinAllocSize);
292         AutoPoolReleaser r(*pool);
293         REPORTER_ASSERT(reporter, pool->size() == 0);
294 
295         allocateMemory(*pool, r);
296         REPORTER_ASSERT(reporter, pool->size() == kMinAllocSize);
297 
298         allocateMemory(*pool, r);
299         REPORTER_ASSERT(reporter, pool->size() == 2 * kMinAllocSize);
300     }
301 
302     // When asked to allocate amount > minAllocSize, pool allocates larger block
303     // to accommodate all internal structures.
304     {
305         constexpr size_t kMinAllocSize = kSmallestMinAllocSize * 2;
306         auto pool = GrMemoryPool::Make(kSmallestMinAllocSize, kMinAllocSize);
307         AutoPoolReleaser r(*pool);
308 
309         REPORTER_ASSERT(reporter, pool->size() == 0);
310 
311         constexpr size_t hugeSize = 10 * kMinAllocSize;
312         r.add(pool->allocate(hugeSize));
313         REPORTER_ASSERT(reporter, pool->size() > hugeSize);
314 
315         // Block size allocated to accommodate huge request doesn't include any extra
316         // space, so next allocation request allocates a new block.
317         size_t hugeBlockSize = pool->size();
318         r.add(pool->allocate(0));
319         REPORTER_ASSERT(reporter, pool->size() == hugeBlockSize + kMinAllocSize);
320     }
321 }
322