blob: f5b341eb08b8b359ca5a988cfcb4a07b6b3e7958 [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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#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"
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) {
Brian Salomon6986c652019-12-12 10:58:47 -050030 if (!gPool) {
bsalomon@google.com4da34e32012-06-19 15:40:27 +000031 return ::operator new(size);
32 } else {
33 return gPool->allocate(size);
34 }
35 }
36
37 void operator delete(void* p) {
Brian Salomon6986c652019-12-12 10:58:47 -050038 if (!gPool) {
bsalomon@google.com4da34e32012-06-19 15:40:27 +000039 ::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) {
Brian Salomon6986c652019-12-12 10:58:47 -050048 gPool = GrMemoryPool::Make(preallocSize, minAllocSize);
bsalomon@google.com4da34e32012-06-19 15:40:27 +000049 }
50
Brian Salomon6986c652019-12-12 10:58:47 -050051 static void ResetAllocator() { gPool.reset(); }
bsalomon@google.com4da34e32012-06-19 15:40:27 +000052
53private:
Ben Wagner145dbcd2016-11-03 14:40:50 -040054 static std::unique_ptr<GrMemoryPool> gPool;
bsalomon@google.com4da34e32012-06-19 15:40:27 +000055 char fChar;
56};
commit-bot@chromium.orgab1c1382013-12-05 12:08:12 +000057
Ben Wagner145dbcd2016-11-03 14:40:50 -040058std::unique_ptr<GrMemoryPool> A::gPool;
bsalomon@google.com4da34e32012-06-19 15:40:27 +000059
60class B : public A {
61public:
Mike Kleinfc6c37b2016-09-27 09:34:10 -040062 B() {}
bsalomon@google.com4da34e32012-06-19 15:40:27 +000063 virtual void setValues(int v) {
64 fDouble = static_cast<double>(v);
65 this->INHERITED::setValues(v);
66 }
67 virtual bool checkValues(int v) {
68 return fDouble == static_cast<double>(v) &&
69 this->INHERITED::checkValues(v);
70 }
Mike Kleinfc6c37b2016-09-27 09:34:10 -040071 virtual ~B() {}
bsalomon@google.com4da34e32012-06-19 15:40:27 +000072
73private:
74 double fDouble;
75
76 typedef A INHERITED;
77};
78
79class C : public A {
80public:
Mike Kleinfc6c37b2016-09-27 09:34:10 -040081 C() {}
bsalomon@google.com4da34e32012-06-19 15:40:27 +000082 virtual void setValues(int v) {
83 fInt64 = static_cast<int64_t>(v);
84 this->INHERITED::setValues(v);
85 }
86 virtual bool checkValues(int v) {
87 return fInt64 == static_cast<int64_t>(v) &&
88 this->INHERITED::checkValues(v);
89 }
Mike Kleinfc6c37b2016-09-27 09:34:10 -040090 virtual ~C() {}
bsalomon@google.com4da34e32012-06-19 15:40:27 +000091
92private:
93 int64_t fInt64;
94
95 typedef A INHERITED;
96};
97
98// D derives from C and owns a dynamically created B
99class D : public C {
100public:
101 D() {
102 fB = new B();
103 }
104 virtual void setValues(int v) {
bsalomonebc1c102015-08-06 17:33:16 -0700105 fVoidStar = reinterpret_cast<void*>(static_cast<intptr_t>(v));
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000106 this->INHERITED::setValues(v);
107 fB->setValues(v);
108 }
109 virtual bool checkValues(int v) {
bsalomonebc1c102015-08-06 17:33:16 -0700110 return fVoidStar == reinterpret_cast<void*>(static_cast<intptr_t>(v)) &&
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000111 fB->checkValues(v) &&
112 this->INHERITED::checkValues(v);
113 }
114 virtual ~D() {
115 delete fB;
116 }
117private:
118 void* fVoidStar;
119 B* fB;
120
121 typedef C INHERITED;
122};
123
124class E : public A {
125public:
126 E() {}
127 virtual void setValues(int v) {
128 for (size_t i = 0; i < SK_ARRAY_COUNT(fIntArray); ++i) {
129 fIntArray[i] = v;
130 }
131 this->INHERITED::setValues(v);
132 }
133 virtual bool checkValues(int v) {
134 bool ok = true;
135 for (size_t i = 0; ok && i < SK_ARRAY_COUNT(fIntArray); ++i) {
136 if (fIntArray[i] != v) {
137 ok = false;
138 }
139 }
140 return ok && this->INHERITED::checkValues(v);
141 }
142 virtual ~E() {}
143private:
144 int fIntArray[20];
145
146 typedef A INHERITED;
147};
148
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000149A* A::Create(SkRandom* r) {
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000150 switch (r->nextRangeU(0, 4)) {
151 case 0:
152 return new A;
153 case 1:
154 return new B;
155 case 2:
156 return new C;
157 case 3:
158 return new D;
159 case 4:
160 return new E;
161 default:
162 // suppress warning
halcanary96fcdcc2015-08-27 07:41:13 -0700163 return nullptr;
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000164 }
165}
commit-bot@chromium.orgddf94cf2013-10-12 17:25:17 +0000166
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000167struct Rec {
168 A* fInstance;
169 int fValue;
170};
171
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +0000172DEF_TEST(GrMemoryPool, reporter) {
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000173 // prealloc and min alloc sizes for the pool
174 static const size_t gSizes[][2] = {
175 {0, 0},
176 {10 * sizeof(A), 20 * sizeof(A)},
177 {100 * sizeof(A), 100 * sizeof(A)},
178 {500 * sizeof(A), 500 * sizeof(A)},
179 {10000 * sizeof(A), 0},
180 {1, 100 * sizeof(A)},
181 };
182 // different percentages of creation vs deletion
183 static const float gCreateFraction[] = {1.f, .95f, 0.75f, .5f};
184 // number of create/destroys per test
185 static const int kNumIters = 20000;
186 // check that all the values stored in A objects are correct after this
187 // number of iterations
188 static const int kCheckPeriod = 500;
189
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000190 SkRandom r;
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000191 for (size_t s = 0; s < SK_ARRAY_COUNT(gSizes); ++s) {
192 A::SetAllocator(gSizes[s][0], gSizes[s][1]);
193 for (size_t c = 0; c < SK_ARRAY_COUNT(gCreateFraction); ++c) {
194 SkTDArray<Rec> instanceRecs;
195 for (int i = 0; i < kNumIters; ++i) {
196 float createOrDestroy = r.nextUScalar1();
197 if (createOrDestroy < gCreateFraction[c] ||
198 0 == instanceRecs.count()) {
199 Rec* rec = instanceRecs.append();
200 rec->fInstance = A::Create(&r);
201 rec->fValue = static_cast<int>(r.nextU());
202 rec->fInstance->setValues(rec->fValue);
203 } else {
204 int d = r.nextRangeU(0, instanceRecs.count() - 1);
205 Rec& rec = instanceRecs[d];
206 REPORTER_ASSERT(reporter, rec.fInstance->checkValues(rec.fValue));
207 delete rec.fInstance;
208 instanceRecs.removeShuffle(d);
209 }
210 if (0 == i % kCheckPeriod) {
211 for (int r = 0; r < instanceRecs.count(); ++r) {
212 Rec& rec = instanceRecs[r];
213 REPORTER_ASSERT(reporter, rec.fInstance->checkValues(rec.fValue));
214 }
215 }
216 }
217 for (int i = 0; i < instanceRecs.count(); ++i) {
218 Rec& rec = instanceRecs[i];
219 REPORTER_ASSERT(reporter, rec.fInstance->checkValues(rec.fValue));
220 delete rec.fInstance;
221 }
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000222 }
223 }
224}
225
dskibae4cd0062016-11-29 06:50:35 -0800226// GrMemoryPool requires that it's empty at the point of destruction. This helps
227// achieving that by releasing all added memory in the destructor.
228class AutoPoolReleaser {
229public:
230 AutoPoolReleaser(GrMemoryPool& pool): fPool(pool) {
231 }
232 ~AutoPoolReleaser() {
233 for (void* ptr: fAllocated) {
234 fPool.release(ptr);
235 }
236 }
237 void add(void* ptr) {
238 fAllocated.push_back(ptr);
239 }
240private:
241 GrMemoryPool& fPool;
242 SkTArray<void*> fAllocated;
243};
244
245DEF_TEST(GrMemoryPoolAPI, reporter) {
Brian Salomon6986c652019-12-12 10:58:47 -0500246 constexpr size_t kSmallestMinAllocSize = GrMemoryPool::kMinAllocationSize;
dskibae4cd0062016-11-29 06:50:35 -0800247
Brian Salomon6986c652019-12-12 10:58:47 -0500248 // Allocates memory until pool adds a new block (pool->size() changes).
dskibae4cd0062016-11-29 06:50:35 -0800249 auto allocateMemory = [](GrMemoryPool& pool, AutoPoolReleaser& r) {
250 size_t origPoolSize = pool.size();
251 while (pool.size() == origPoolSize) {
252 r.add(pool.allocate(31));
253 }
254 };
255
Brian Salomon6986c652019-12-12 10:58:47 -0500256 // Effective prealloc space capacity is >= kMinAllocationSize.
dskibae4cd0062016-11-29 06:50:35 -0800257 {
Brian Salomon6986c652019-12-12 10:58:47 -0500258 auto pool = GrMemoryPool::Make(0, 0);
259 REPORTER_ASSERT(reporter, pool->preallocSize() == kSmallestMinAllocSize);
dskibae4cd0062016-11-29 06:50:35 -0800260 }
261
Brian Salomon6986c652019-12-12 10:58:47 -0500262 // Effective block size capacity >= kMinAllocationSize.
dskibae4cd0062016-11-29 06:50:35 -0800263 {
Brian Salomon6986c652019-12-12 10:58:47 -0500264 auto pool = GrMemoryPool::Make(kSmallestMinAllocSize, kSmallestMinAllocSize / 2);
265 AutoPoolReleaser r(*pool);
dskibae4cd0062016-11-29 06:50:35 -0800266
Brian Salomon6986c652019-12-12 10:58:47 -0500267 allocateMemory(*pool, r);
268 REPORTER_ASSERT(reporter, pool->size() == kSmallestMinAllocSize);
dskibae4cd0062016-11-29 06:50:35 -0800269 }
270
271 // Pool allocates exactly preallocSize on creation.
272 {
273 constexpr size_t kPreallocSize = kSmallestMinAllocSize * 5;
Brian Salomon6986c652019-12-12 10:58:47 -0500274 auto pool = GrMemoryPool::Make(kPreallocSize, 0);
275 REPORTER_ASSERT(reporter, pool->preallocSize() == kPreallocSize);
dskibae4cd0062016-11-29 06:50:35 -0800276 }
277
278 // Pool allocates exactly minAllocSize when it expands.
279 {
280 constexpr size_t kMinAllocSize = kSmallestMinAllocSize * 7;
Brian Salomon6986c652019-12-12 10:58:47 -0500281 auto pool = GrMemoryPool::Make(0, kMinAllocSize);
282 AutoPoolReleaser r(*pool);
dskibae4cd0062016-11-29 06:50:35 -0800283
Brian Salomon6986c652019-12-12 10:58:47 -0500284 allocateMemory(*pool, r);
285 REPORTER_ASSERT(reporter, pool->size() == kMinAllocSize);
dskibae4cd0062016-11-29 06:50:35 -0800286
Brian Salomon6986c652019-12-12 10:58:47 -0500287 allocateMemory(*pool, r);
288 REPORTER_ASSERT(reporter, pool->size() == 2 * kMinAllocSize);
dskibae4cd0062016-11-29 06:50:35 -0800289 }
290
291 // When asked to allocate amount > minAllocSize, pool allocates larger block
292 // to accommodate all internal structures.
293 {
294 constexpr size_t kMinAllocSize = kSmallestMinAllocSize * 2;
Brian Salomon6986c652019-12-12 10:58:47 -0500295 auto pool = GrMemoryPool::Make(kSmallestMinAllocSize, kMinAllocSize);
296 AutoPoolReleaser r(*pool);
dskibae4cd0062016-11-29 06:50:35 -0800297
Brian Salomon6986c652019-12-12 10:58:47 -0500298 REPORTER_ASSERT(reporter, pool->size() == 0);
dskibae4cd0062016-11-29 06:50:35 -0800299
300 constexpr size_t hugeSize = 10 * kMinAllocSize;
Brian Salomon6986c652019-12-12 10:58:47 -0500301 r.add(pool->allocate(hugeSize));
302 REPORTER_ASSERT(reporter, pool->size() > hugeSize);
dskibae4cd0062016-11-29 06:50:35 -0800303
304 // Block size allocated to accommodate huge request doesn't include any extra
305 // space, so next allocation request allocates a new block.
Brian Salomon6986c652019-12-12 10:58:47 -0500306 size_t hugeBlockSize = pool->size();
307 r.add(pool->allocate(0));
308 REPORTER_ASSERT(reporter, pool->size() == hugeBlockSize + kMinAllocSize);
dskibae4cd0062016-11-29 06:50:35 -0800309 }
310}