blob: 38d68c1cd76cc7a116e916448551496df67718de [file] [log] [blame]
bsalomon@google.com4da34e32012-06-19 15:40:27 +00001/*
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
bsalomon@google.coma68937c2012-08-03 15:00:52 +00008#include "Test.h"
bsalomon@google.com4da34e32012-06-19 15:40:27 +00009#include "GrMemoryPool.h"
10#include "SkRandom.h"
dskibae4cd0062016-11-29 06:50:35 -080011#include "SkTArray.h"
bsalomon@google.com4da34e32012-06-19 15:40:27 +000012#include "SkTDArray.h"
reed@google.com9d1cff12013-04-18 18:43:26 +000013#include "SkTemplates.h"
bsalomon@google.com4da34e32012-06-19 15:40:27 +000014
bsalomon@google.com4da34e32012-06-19 15:40:27 +000015// 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.
18class A {
19public:
Mike Kleinfc6c37b2016-09-27 09:34:10 -040020 A() {}
bsalomon@google.com4da34e32012-06-19 15:40:27 +000021 virtual void setValues(int v) {
Brian Osman50ea3c02019-02-04 10:01:53 -050022 fChar = static_cast<char>(v & 0xFF);
bsalomon@google.com4da34e32012-06-19 15:40:27 +000023 }
24 virtual bool checkValues(int v) {
Brian Osman50ea3c02019-02-04 10:01:53 -050025 return fChar == static_cast<char>(v & 0xFF);
bsalomon@google.com4da34e32012-06-19 15:40:27 +000026 }
Mike Kleinfc6c37b2016-09-27 09:34:10 -040027 virtual ~A() {}
bsalomon@google.com4da34e32012-06-19 15:40:27 +000028
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
37 void operator delete(void* p) {
38 if (!gPool.get()) {
39 ::operator delete(p);
40 } else {
41 return gPool->release(p);
42 }
43 }
44
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +000045 static A* Create(SkRandom* r);
bsalomon@google.com4da34e32012-06-19 15:40:27 +000046
47 static void SetAllocator(size_t preallocSize, size_t minAllocSize) {
bsalomon@google.com4da34e32012-06-19 15:40:27 +000048 GrMemoryPool* pool = new GrMemoryPool(preallocSize, minAllocSize);
49 gPool.reset(pool);
50 }
51
52 static void ResetAllocator() {
halcanary96fcdcc2015-08-27 07:41:13 -070053 gPool.reset(nullptr);
bsalomon@google.com4da34e32012-06-19 15:40:27 +000054 }
55
56private:
Ben Wagner145dbcd2016-11-03 14:40:50 -040057 static std::unique_ptr<GrMemoryPool> gPool;
bsalomon@google.com4da34e32012-06-19 15:40:27 +000058 char fChar;
59};
commit-bot@chromium.orgab1c1382013-12-05 12:08:12 +000060
Ben Wagner145dbcd2016-11-03 14:40:50 -040061std::unique_ptr<GrMemoryPool> A::gPool;
bsalomon@google.com4da34e32012-06-19 15:40:27 +000062
63class B : public A {
64public:
Mike Kleinfc6c37b2016-09-27 09:34:10 -040065 B() {}
bsalomon@google.com4da34e32012-06-19 15:40:27 +000066 virtual void setValues(int v) {
67 fDouble = static_cast<double>(v);
68 this->INHERITED::setValues(v);
69 }
70 virtual bool checkValues(int v) {
71 return fDouble == static_cast<double>(v) &&
72 this->INHERITED::checkValues(v);
73 }
Mike Kleinfc6c37b2016-09-27 09:34:10 -040074 virtual ~B() {}
bsalomon@google.com4da34e32012-06-19 15:40:27 +000075
76private:
77 double fDouble;
78
79 typedef A INHERITED;
80};
81
82class C : public A {
83public:
Mike Kleinfc6c37b2016-09-27 09:34:10 -040084 C() {}
bsalomon@google.com4da34e32012-06-19 15:40:27 +000085 virtual void setValues(int v) {
86 fInt64 = static_cast<int64_t>(v);
87 this->INHERITED::setValues(v);
88 }
89 virtual bool checkValues(int v) {
90 return fInt64 == static_cast<int64_t>(v) &&
91 this->INHERITED::checkValues(v);
92 }
Mike Kleinfc6c37b2016-09-27 09:34:10 -040093 virtual ~C() {}
bsalomon@google.com4da34e32012-06-19 15:40:27 +000094
95private:
96 int64_t fInt64;
97
98 typedef A INHERITED;
99};
100
101// D derives from C and owns a dynamically created B
102class D : public C {
103public:
104 D() {
105 fB = new B();
106 }
107 virtual void setValues(int v) {
bsalomonebc1c102015-08-06 17:33:16 -0700108 fVoidStar = reinterpret_cast<void*>(static_cast<intptr_t>(v));
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000109 this->INHERITED::setValues(v);
110 fB->setValues(v);
111 }
112 virtual bool checkValues(int v) {
bsalomonebc1c102015-08-06 17:33:16 -0700113 return fVoidStar == reinterpret_cast<void*>(static_cast<intptr_t>(v)) &&
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000114 fB->checkValues(v) &&
115 this->INHERITED::checkValues(v);
116 }
117 virtual ~D() {
118 delete fB;
119 }
120private:
121 void* fVoidStar;
122 B* fB;
123
124 typedef C INHERITED;
125};
126
127class E : public A {
128public:
129 E() {}
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 }
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 }
145 virtual ~E() {}
146private:
147 int fIntArray[20];
148
149 typedef A INHERITED;
150};
151
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000152A* A::Create(SkRandom* r) {
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000153 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
halcanary96fcdcc2015-08-27 07:41:13 -0700166 return nullptr;
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000167 }
168}
commit-bot@chromium.orgddf94cf2013-10-12 17:25:17 +0000169
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000170struct Rec {
171 A* fInstance;
172 int fValue;
173};
174
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +0000175DEF_TEST(GrMemoryPool, reporter) {
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000176 // 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
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000193 SkRandom r;
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000194 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 }
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000225 }
226 }
227}
228
dskibae4cd0062016-11-29 06:50:35 -0800229// GrMemoryPool requires that it's empty at the point of destruction. This helps
230// achieving that by releasing all added memory in the destructor.
231class AutoPoolReleaser {
232public:
233 AutoPoolReleaser(GrMemoryPool& pool): fPool(pool) {
234 }
235 ~AutoPoolReleaser() {
236 for (void* ptr: fAllocated) {
237 fPool.release(ptr);
238 }
239 }
240 void add(void* ptr) {
241 fAllocated.push_back(ptr);
242 }
243private:
244 GrMemoryPool& fPool;
245 SkTArray<void*> fAllocated;
246};
247
248DEF_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}