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