• 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.get()) {
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.get()) {
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         GrMemoryPool* pool = new GrMemoryPool(preallocSize, minAllocSize);
49         gPool.reset(pool);
50     }
51 
ResetAllocator()52     static void ResetAllocator() {
53         gPool.reset(nullptr);
54     }
55 
56 private:
57     static std::unique_ptr<GrMemoryPool> gPool;
58     char fChar;
59 };
60 
61 std::unique_ptr<GrMemoryPool> A::gPool;
62 
63 class B : public A {
64 public:
B()65     B() {}
setValues(int v)66     virtual void setValues(int v) {
67         fDouble = static_cast<double>(v);
68         this->INHERITED::setValues(v);
69     }
checkValues(int v)70     virtual bool checkValues(int v) {
71         return fDouble == static_cast<double>(v) &&
72                this->INHERITED::checkValues(v);
73     }
~B()74     virtual ~B() {}
75 
76 private:
77     double fDouble;
78 
79     typedef A INHERITED;
80 };
81 
82 class C : public A {
83 public:
C()84     C() {}
setValues(int v)85     virtual void setValues(int v) {
86         fInt64 = static_cast<int64_t>(v);
87         this->INHERITED::setValues(v);
88     }
checkValues(int v)89     virtual bool checkValues(int v) {
90         return fInt64 == static_cast<int64_t>(v) &&
91                this->INHERITED::checkValues(v);
92     }
~C()93     virtual ~C() {}
94 
95 private:
96     int64_t fInt64;
97 
98     typedef A INHERITED;
99 };
100 
101 // D derives from C and owns a dynamically created B
102 class D : public C {
103 public:
D()104     D() {
105         fB = new B();
106     }
setValues(int v)107     virtual void setValues(int v) {
108         fVoidStar = reinterpret_cast<void*>(static_cast<intptr_t>(v));
109         this->INHERITED::setValues(v);
110         fB->setValues(v);
111     }
checkValues(int v)112     virtual bool checkValues(int v) {
113         return fVoidStar == reinterpret_cast<void*>(static_cast<intptr_t>(v)) &&
114                fB->checkValues(v) &&
115                this->INHERITED::checkValues(v);
116     }
~D()117     virtual ~D() {
118         delete fB;
119     }
120 private:
121     void*   fVoidStar;
122     B*      fB;
123 
124     typedef C INHERITED;
125 };
126 
127 class E : public A {
128 public:
E()129     E() {}
setValues(int v)130     virtual void setValues(int v) {
131         for (size_t i = 0; i < SK_ARRAY_COUNT(fIntArray); ++i) {
132             fIntArray[i] = v;
133         }
134         this->INHERITED::setValues(v);
135     }
checkValues(int v)136     virtual bool checkValues(int v) {
137         bool ok = true;
138         for (size_t i = 0; ok && i < SK_ARRAY_COUNT(fIntArray); ++i) {
139             if (fIntArray[i] != v) {
140                 ok = false;
141             }
142         }
143         return ok && this->INHERITED::checkValues(v);
144     }
~E()145     virtual ~E() {}
146 private:
147     int   fIntArray[20];
148 
149     typedef A INHERITED;
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     // different percentages of creation vs deletion
186     static const float gCreateFraction[] = {1.f, .95f, 0.75f, .5f};
187     // number of create/destroys per test
188     static const int kNumIters = 20000;
189     // check that all the values stored in A objects are correct after this
190     // number of iterations
191     static const int kCheckPeriod = 500;
192 
193     SkRandom r;
194     for (size_t s = 0; s < SK_ARRAY_COUNT(gSizes); ++s) {
195         A::SetAllocator(gSizes[s][0], gSizes[s][1]);
196         for (size_t c = 0; c < SK_ARRAY_COUNT(gCreateFraction); ++c) {
197             SkTDArray<Rec> instanceRecs;
198             for (int i = 0; i < kNumIters; ++i) {
199                 float createOrDestroy = r.nextUScalar1();
200                 if (createOrDestroy < gCreateFraction[c] ||
201                     0 == instanceRecs.count()) {
202                     Rec* rec = instanceRecs.append();
203                     rec->fInstance = A::Create(&r);
204                     rec->fValue = static_cast<int>(r.nextU());
205                     rec->fInstance->setValues(rec->fValue);
206                 } else {
207                     int d = r.nextRangeU(0, instanceRecs.count() - 1);
208                     Rec& rec = instanceRecs[d];
209                     REPORTER_ASSERT(reporter, rec.fInstance->checkValues(rec.fValue));
210                     delete rec.fInstance;
211                     instanceRecs.removeShuffle(d);
212                 }
213                 if (0 == i % kCheckPeriod) {
214                     for (int r = 0; r < instanceRecs.count(); ++r) {
215                         Rec& rec = instanceRecs[r];
216                         REPORTER_ASSERT(reporter, rec.fInstance->checkValues(rec.fValue));
217                     }
218                 }
219             }
220             for (int i = 0; i < instanceRecs.count(); ++i) {
221                 Rec& rec = instanceRecs[i];
222                 REPORTER_ASSERT(reporter, rec.fInstance->checkValues(rec.fValue));
223                 delete rec.fInstance;
224             }
225         }
226     }
227 }
228 
229 // GrMemoryPool requires that it's empty at the point of destruction. This helps
230 // achieving that by releasing all added memory in the destructor.
231 class AutoPoolReleaser {
232 public:
AutoPoolReleaser(GrMemoryPool & pool)233     AutoPoolReleaser(GrMemoryPool& pool): fPool(pool) {
234     }
~AutoPoolReleaser()235     ~AutoPoolReleaser() {
236         for (void* ptr: fAllocated) {
237             fPool.release(ptr);
238         }
239     }
add(void * ptr)240     void add(void* ptr) {
241         fAllocated.push_back(ptr);
242     }
243 private:
244     GrMemoryPool& fPool;
245     SkTArray<void*> fAllocated;
246 };
247 
DEF_TEST(GrMemoryPoolAPI,reporter)248 DEF_TEST(GrMemoryPoolAPI, reporter) {
249     constexpr size_t kSmallestMinAllocSize = GrMemoryPool::kSmallestMinAllocSize;
250 
251     // Allocates memory until pool adds a new block (pool.size() changes).
252     auto allocateMemory = [](GrMemoryPool& pool, AutoPoolReleaser& r) {
253         size_t origPoolSize = pool.size();
254         while (pool.size() == origPoolSize) {
255             r.add(pool.allocate(31));
256         }
257     };
258 
259     // Effective prealloc space capacity is >= kSmallestMinAllocSize.
260     {
261         GrMemoryPool pool(0, 0);
262         REPORTER_ASSERT(reporter, pool.preallocSize() == kSmallestMinAllocSize);
263     }
264 
265     // Effective prealloc space capacity is >= minAllocSize.
266     {
267         constexpr size_t kMinAllocSize = kSmallestMinAllocSize * 2;
268         GrMemoryPool pool(kSmallestMinAllocSize, kMinAllocSize);
269         REPORTER_ASSERT(reporter, pool.preallocSize() == kMinAllocSize);
270     }
271 
272     // Effective block size capacity >= kSmallestMinAllocSize.
273     {
274         GrMemoryPool pool(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         GrMemoryPool pool(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         GrMemoryPool pool(0, kMinAllocSize);
292         AutoPoolReleaser r(pool);
293 
294         allocateMemory(pool, r);
295         REPORTER_ASSERT(reporter, pool.size() == kMinAllocSize);
296 
297         allocateMemory(pool, r);
298         REPORTER_ASSERT(reporter, pool.size() == 2 * kMinAllocSize);
299     }
300 
301     // When asked to allocate amount > minAllocSize, pool allocates larger block
302     // to accommodate all internal structures.
303     {
304         constexpr size_t kMinAllocSize = kSmallestMinAllocSize * 2;
305         GrMemoryPool pool(kSmallestMinAllocSize, kMinAllocSize);
306         AutoPoolReleaser r(pool);
307 
308         REPORTER_ASSERT(reporter, pool.size() == 0);
309 
310         constexpr size_t hugeSize = 10 * kMinAllocSize;
311         r.add(pool.allocate(hugeSize));
312         REPORTER_ASSERT(reporter, pool.size() > hugeSize);
313 
314         // Block size allocated to accommodate huge request doesn't include any extra
315         // space, so next allocation request allocates a new block.
316         size_t hugeBlockSize = pool.size();
317         r.add(pool.allocate(0));
318         REPORTER_ASSERT(reporter, pool.size() == hugeBlockSize + kMinAllocSize);
319     }
320 }
321