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