blob: 1dd1c7304a21cf6d627aae774045b79c768e4f42 [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
Michael Ludwigcd019792020-03-17 10:14:48 -040053 static void ValidatePool() {
54#ifdef SK_DEBUG
55 gPool->validate();
56#endif
57 }
58
bsalomon@google.com4da34e32012-06-19 15:40:27 +000059private:
Ben Wagner145dbcd2016-11-03 14:40:50 -040060 static std::unique_ptr<GrMemoryPool> gPool;
bsalomon@google.com4da34e32012-06-19 15:40:27 +000061 char fChar;
62};
commit-bot@chromium.orgab1c1382013-12-05 12:08:12 +000063
Ben Wagner145dbcd2016-11-03 14:40:50 -040064std::unique_ptr<GrMemoryPool> A::gPool;
bsalomon@google.com4da34e32012-06-19 15:40:27 +000065
66class B : public A {
67public:
Mike Kleinfc6c37b2016-09-27 09:34:10 -040068 B() {}
Brian Salomond0072812020-07-21 17:03:56 -040069 void setValues(int v) override {
bsalomon@google.com4da34e32012-06-19 15:40:27 +000070 fDouble = static_cast<double>(v);
71 this->INHERITED::setValues(v);
72 }
Brian Salomond0072812020-07-21 17:03:56 -040073 bool checkValues(int v) override {
bsalomon@google.com4da34e32012-06-19 15:40:27 +000074 return fDouble == static_cast<double>(v) &&
75 this->INHERITED::checkValues(v);
76 }
bsalomon@google.com4da34e32012-06-19 15:40:27 +000077
78private:
79 double fDouble;
80
John Stiles7571f9e2020-09-02 22:42:33 -040081 using INHERITED = A;
bsalomon@google.com4da34e32012-06-19 15:40:27 +000082};
83
84class C : public A {
85public:
Mike Kleinfc6c37b2016-09-27 09:34:10 -040086 C() {}
Brian Salomond0072812020-07-21 17:03:56 -040087 void setValues(int v) override {
bsalomon@google.com4da34e32012-06-19 15:40:27 +000088 fInt64 = static_cast<int64_t>(v);
89 this->INHERITED::setValues(v);
90 }
Brian Salomond0072812020-07-21 17:03:56 -040091 bool checkValues(int v) override {
bsalomon@google.com4da34e32012-06-19 15:40:27 +000092 return fInt64 == static_cast<int64_t>(v) &&
93 this->INHERITED::checkValues(v);
94 }
bsalomon@google.com4da34e32012-06-19 15:40:27 +000095
96private:
97 int64_t fInt64;
98
John Stiles7571f9e2020-09-02 22:42:33 -040099 using INHERITED = A;
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000100};
101
102// D derives from C and owns a dynamically created B
103class D : public C {
104public:
105 D() {
106 fB = new B();
107 }
Brian Salomond0072812020-07-21 17:03:56 -0400108 void setValues(int v) override {
bsalomonebc1c102015-08-06 17:33:16 -0700109 fVoidStar = reinterpret_cast<void*>(static_cast<intptr_t>(v));
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000110 this->INHERITED::setValues(v);
111 fB->setValues(v);
112 }
Brian Salomond0072812020-07-21 17:03:56 -0400113 bool checkValues(int v) override {
bsalomonebc1c102015-08-06 17:33:16 -0700114 return fVoidStar == reinterpret_cast<void*>(static_cast<intptr_t>(v)) &&
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000115 fB->checkValues(v) &&
116 this->INHERITED::checkValues(v);
117 }
Brian Salomond0072812020-07-21 17:03:56 -0400118 ~D() override {
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000119 delete fB;
120 }
121private:
122 void* fVoidStar;
123 B* fB;
124
John Stiles7571f9e2020-09-02 22:42:33 -0400125 using INHERITED = C;
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000126};
127
128class E : public A {
129public:
130 E() {}
Brian Salomond0072812020-07-21 17:03:56 -0400131 void setValues(int v) override {
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000132 for (size_t i = 0; i < SK_ARRAY_COUNT(fIntArray); ++i) {
133 fIntArray[i] = v;
134 }
135 this->INHERITED::setValues(v);
136 }
Brian Salomond0072812020-07-21 17:03:56 -0400137 bool checkValues(int v) override {
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000138 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 }
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000146private:
147 int fIntArray[20];
148
John Stiles7571f9e2020-09-02 22:42:33 -0400149 using INHERITED = A;
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000150};
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 };
Michael Ludwigcd019792020-03-17 10:14:48 -0400185
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000186 // 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
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000194 SkRandom r;
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000195 for (size_t s = 0; s < SK_ARRAY_COUNT(gSizes); ++s) {
196 A::SetAllocator(gSizes[s][0], gSizes[s][1]);
Michael Ludwigcd019792020-03-17 10:14:48 -0400197 A::ValidatePool();
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000198 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) {
Michael Ludwigcd019792020-03-17 10:14:48 -0400216 A::ValidatePool();
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000217 for (int r = 0; r < instanceRecs.count(); ++r) {
218 Rec& rec = instanceRecs[r];
219 REPORTER_ASSERT(reporter, rec.fInstance->checkValues(rec.fValue));
220 }
221 }
222 }
223 for (int i = 0; i < instanceRecs.count(); ++i) {
224 Rec& rec = instanceRecs[i];
225 REPORTER_ASSERT(reporter, rec.fInstance->checkValues(rec.fValue));
226 delete rec.fInstance;
227 }
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000228 }
229 }
230}
231
dskibae4cd0062016-11-29 06:50:35 -0800232// GrMemoryPool requires that it's empty at the point of destruction. This helps
233// achieving that by releasing all added memory in the destructor.
234class AutoPoolReleaser {
235public:
236 AutoPoolReleaser(GrMemoryPool& pool): fPool(pool) {
237 }
238 ~AutoPoolReleaser() {
239 for (void* ptr: fAllocated) {
240 fPool.release(ptr);
241 }
242 }
243 void add(void* ptr) {
244 fAllocated.push_back(ptr);
245 }
246private:
247 GrMemoryPool& fPool;
248 SkTArray<void*> fAllocated;
249};
250
251DEF_TEST(GrMemoryPoolAPI, reporter) {
Brian Salomon6986c652019-12-12 10:58:47 -0500252 constexpr size_t kSmallestMinAllocSize = GrMemoryPool::kMinAllocationSize;
dskibae4cd0062016-11-29 06:50:35 -0800253
Brian Salomon6986c652019-12-12 10:58:47 -0500254 // Allocates memory until pool adds a new block (pool->size() changes).
dskibae4cd0062016-11-29 06:50:35 -0800255 auto allocateMemory = [](GrMemoryPool& pool, AutoPoolReleaser& r) {
256 size_t origPoolSize = pool.size();
257 while (pool.size() == origPoolSize) {
258 r.add(pool.allocate(31));
259 }
260 };
261
Brian Salomon6986c652019-12-12 10:58:47 -0500262 // Effective prealloc space capacity is >= kMinAllocationSize.
dskibae4cd0062016-11-29 06:50:35 -0800263 {
Brian Salomon6986c652019-12-12 10:58:47 -0500264 auto pool = GrMemoryPool::Make(0, 0);
265 REPORTER_ASSERT(reporter, pool->preallocSize() == kSmallestMinAllocSize);
dskibae4cd0062016-11-29 06:50:35 -0800266 }
267
Brian Salomon6986c652019-12-12 10:58:47 -0500268 // Effective block size capacity >= kMinAllocationSize.
dskibae4cd0062016-11-29 06:50:35 -0800269 {
Brian Salomon6986c652019-12-12 10:58:47 -0500270 auto pool = GrMemoryPool::Make(kSmallestMinAllocSize, kSmallestMinAllocSize / 2);
271 AutoPoolReleaser r(*pool);
dskibae4cd0062016-11-29 06:50:35 -0800272
Brian Salomon6986c652019-12-12 10:58:47 -0500273 allocateMemory(*pool, r);
274 REPORTER_ASSERT(reporter, pool->size() == kSmallestMinAllocSize);
dskibae4cd0062016-11-29 06:50:35 -0800275 }
276
277 // Pool allocates exactly preallocSize on creation.
278 {
279 constexpr size_t kPreallocSize = kSmallestMinAllocSize * 5;
Brian Salomon6986c652019-12-12 10:58:47 -0500280 auto pool = GrMemoryPool::Make(kPreallocSize, 0);
281 REPORTER_ASSERT(reporter, pool->preallocSize() == kPreallocSize);
dskibae4cd0062016-11-29 06:50:35 -0800282 }
283
284 // Pool allocates exactly minAllocSize when it expands.
285 {
286 constexpr size_t kMinAllocSize = kSmallestMinAllocSize * 7;
Brian Salomon6986c652019-12-12 10:58:47 -0500287 auto pool = GrMemoryPool::Make(0, kMinAllocSize);
288 AutoPoolReleaser r(*pool);
Michael Ludwigcd019792020-03-17 10:14:48 -0400289 REPORTER_ASSERT(reporter, pool->size() == 0);
dskibae4cd0062016-11-29 06:50:35 -0800290
Brian Salomon6986c652019-12-12 10:58:47 -0500291 allocateMemory(*pool, r);
292 REPORTER_ASSERT(reporter, pool->size() == kMinAllocSize);
dskibae4cd0062016-11-29 06:50:35 -0800293
Brian Salomon6986c652019-12-12 10:58:47 -0500294 allocateMemory(*pool, r);
295 REPORTER_ASSERT(reporter, pool->size() == 2 * kMinAllocSize);
dskibae4cd0062016-11-29 06:50:35 -0800296 }
297
298 // When asked to allocate amount > minAllocSize, pool allocates larger block
299 // to accommodate all internal structures.
300 {
301 constexpr size_t kMinAllocSize = kSmallestMinAllocSize * 2;
Brian Salomon6986c652019-12-12 10:58:47 -0500302 auto pool = GrMemoryPool::Make(kSmallestMinAllocSize, kMinAllocSize);
303 AutoPoolReleaser r(*pool);
dskibae4cd0062016-11-29 06:50:35 -0800304
Brian Salomon6986c652019-12-12 10:58:47 -0500305 REPORTER_ASSERT(reporter, pool->size() == 0);
dskibae4cd0062016-11-29 06:50:35 -0800306
307 constexpr size_t hugeSize = 10 * kMinAllocSize;
Brian Salomon6986c652019-12-12 10:58:47 -0500308 r.add(pool->allocate(hugeSize));
309 REPORTER_ASSERT(reporter, pool->size() > hugeSize);
dskibae4cd0062016-11-29 06:50:35 -0800310
311 // Block size allocated to accommodate huge request doesn't include any extra
312 // space, so next allocation request allocates a new block.
Brian Salomon6986c652019-12-12 10:58:47 -0500313 size_t hugeBlockSize = pool->size();
314 r.add(pool->allocate(0));
315 REPORTER_ASSERT(reporter, pool->size() == hugeBlockSize + kMinAllocSize);
dskibae4cd0062016-11-29 06:50:35 -0800316 }
317}