blob: 08c953180e26ff3f403e1021178b7de58be36f0d [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
Kevin Lubickdc6cc022023-01-13 11:24:27 -05008#include "include/private/base/SkTArray.h"
9#include "include/private/base/SkTDArray.h"
Kevin Lubick0d4d1142023-02-13 09:13:10 -050010#include "src/base/SkRandom.h"
Greg Daniel719239c2022-04-07 11:20:24 -040011#include "src/gpu/ganesh/GrMemoryPool.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050012#include "tests/Test.h"
bsalomon@google.com4da34e32012-06-19 15:40:27 +000013
Kevin Lubick8c73a592022-10-17 15:25:35 -040014#include <array>
15#include <cstddef>
16#include <cstdint>
17#include <memory>
18
Herb Derby99967012023-03-29 10:01:23 -040019using namespace skia_private;
20
bsalomon@google.com4da34e32012-06-19 15:40:27 +000021// A is the top of an inheritance tree of classes that overload op new and
22// and delete to use a GrMemoryPool. The objects have values of different types
23// that can be set and checked.
24class A {
25public:
Mike Kleinfc6c37b2016-09-27 09:34:10 -040026 A() {}
bsalomon@google.com4da34e32012-06-19 15:40:27 +000027 virtual void setValues(int v) {
Brian Osman50ea3c02019-02-04 10:01:53 -050028 fChar = static_cast<char>(v & 0xFF);
bsalomon@google.com4da34e32012-06-19 15:40:27 +000029 }
30 virtual bool checkValues(int v) {
Brian Osman50ea3c02019-02-04 10:01:53 -050031 return fChar == static_cast<char>(v & 0xFF);
bsalomon@google.com4da34e32012-06-19 15:40:27 +000032 }
Mike Kleinfc6c37b2016-09-27 09:34:10 -040033 virtual ~A() {}
bsalomon@google.com4da34e32012-06-19 15:40:27 +000034
35 void* operator new(size_t size) {
Brian Salomon6986c652019-12-12 10:58:47 -050036 if (!gPool) {
bsalomon@google.com4da34e32012-06-19 15:40:27 +000037 return ::operator new(size);
38 } else {
39 return gPool->allocate(size);
40 }
41 }
42
43 void operator delete(void* p) {
Brian Salomon6986c652019-12-12 10:58:47 -050044 if (!gPool) {
bsalomon@google.com4da34e32012-06-19 15:40:27 +000045 ::operator delete(p);
46 } else {
47 return gPool->release(p);
48 }
49 }
50
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +000051 static A* Create(SkRandom* r);
bsalomon@google.com4da34e32012-06-19 15:40:27 +000052
53 static void SetAllocator(size_t preallocSize, size_t minAllocSize) {
Brian Salomon6986c652019-12-12 10:58:47 -050054 gPool = GrMemoryPool::Make(preallocSize, minAllocSize);
bsalomon@google.com4da34e32012-06-19 15:40:27 +000055 }
56
Brian Salomon6986c652019-12-12 10:58:47 -050057 static void ResetAllocator() { gPool.reset(); }
bsalomon@google.com4da34e32012-06-19 15:40:27 +000058
Michael Ludwigcd019792020-03-17 10:14:48 -040059 static void ValidatePool() {
60#ifdef SK_DEBUG
61 gPool->validate();
62#endif
63 }
64
bsalomon@google.com4da34e32012-06-19 15:40:27 +000065private:
Ben Wagner145dbcd2016-11-03 14:40:50 -040066 static std::unique_ptr<GrMemoryPool> gPool;
bsalomon@google.com4da34e32012-06-19 15:40:27 +000067 char fChar;
68};
commit-bot@chromium.orgab1c1382013-12-05 12:08:12 +000069
Ben Wagner145dbcd2016-11-03 14:40:50 -040070std::unique_ptr<GrMemoryPool> A::gPool;
bsalomon@google.com4da34e32012-06-19 15:40:27 +000071
72class B : public A {
73public:
Mike Kleinfc6c37b2016-09-27 09:34:10 -040074 B() {}
Brian Salomond0072812020-07-21 17:03:56 -040075 void setValues(int v) override {
bsalomon@google.com4da34e32012-06-19 15:40:27 +000076 fDouble = static_cast<double>(v);
77 this->INHERITED::setValues(v);
78 }
Brian Salomond0072812020-07-21 17:03:56 -040079 bool checkValues(int v) override {
bsalomon@google.com4da34e32012-06-19 15:40:27 +000080 return fDouble == static_cast<double>(v) &&
81 this->INHERITED::checkValues(v);
82 }
bsalomon@google.com4da34e32012-06-19 15:40:27 +000083
84private:
85 double fDouble;
86
John Stiles7571f9e2020-09-02 22:42:33 -040087 using INHERITED = A;
bsalomon@google.com4da34e32012-06-19 15:40:27 +000088};
89
90class C : public A {
91public:
Mike Kleinfc6c37b2016-09-27 09:34:10 -040092 C() {}
Brian Salomond0072812020-07-21 17:03:56 -040093 void setValues(int v) override {
bsalomon@google.com4da34e32012-06-19 15:40:27 +000094 fInt64 = static_cast<int64_t>(v);
95 this->INHERITED::setValues(v);
96 }
Brian Salomond0072812020-07-21 17:03:56 -040097 bool checkValues(int v) override {
bsalomon@google.com4da34e32012-06-19 15:40:27 +000098 return fInt64 == static_cast<int64_t>(v) &&
99 this->INHERITED::checkValues(v);
100 }
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000101
102private:
103 int64_t fInt64;
104
John Stiles7571f9e2020-09-02 22:42:33 -0400105 using INHERITED = A;
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000106};
107
108// D derives from C and owns a dynamically created B
109class D : public C {
110public:
111 D() {
112 fB = new B();
113 }
Brian Salomond0072812020-07-21 17:03:56 -0400114 void setValues(int v) override {
bsalomonebc1c102015-08-06 17:33:16 -0700115 fVoidStar = reinterpret_cast<void*>(static_cast<intptr_t>(v));
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000116 this->INHERITED::setValues(v);
117 fB->setValues(v);
118 }
Brian Salomond0072812020-07-21 17:03:56 -0400119 bool checkValues(int v) override {
bsalomonebc1c102015-08-06 17:33:16 -0700120 return fVoidStar == reinterpret_cast<void*>(static_cast<intptr_t>(v)) &&
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000121 fB->checkValues(v) &&
122 this->INHERITED::checkValues(v);
123 }
Brian Salomond0072812020-07-21 17:03:56 -0400124 ~D() override {
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000125 delete fB;
126 }
127private:
128 void* fVoidStar;
129 B* fB;
130
John Stiles7571f9e2020-09-02 22:42:33 -0400131 using INHERITED = C;
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000132};
133
134class E : public A {
135public:
136 E() {}
Brian Salomond0072812020-07-21 17:03:56 -0400137 void setValues(int v) override {
Herb Derbyc8366922022-06-27 15:45:25 -0400138 for (size_t i = 0; i < std::size(fIntArray); ++i) {
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000139 fIntArray[i] = v;
140 }
141 this->INHERITED::setValues(v);
142 }
Brian Salomond0072812020-07-21 17:03:56 -0400143 bool checkValues(int v) override {
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000144 bool ok = true;
Herb Derbyc8366922022-06-27 15:45:25 -0400145 for (size_t i = 0; ok && i < std::size(fIntArray); ++i) {
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000146 if (fIntArray[i] != v) {
147 ok = false;
148 }
149 }
150 return ok && this->INHERITED::checkValues(v);
151 }
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000152private:
153 int fIntArray[20];
154
John Stiles7571f9e2020-09-02 22:42:33 -0400155 using INHERITED = A;
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000156};
157
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000158A* A::Create(SkRandom* r) {
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000159 switch (r->nextRangeU(0, 4)) {
160 case 0:
161 return new A;
162 case 1:
163 return new B;
164 case 2:
165 return new C;
166 case 3:
167 return new D;
168 case 4:
169 return new E;
170 default:
171 // suppress warning
halcanary96fcdcc2015-08-27 07:41:13 -0700172 return nullptr;
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000173 }
174}
commit-bot@chromium.orgddf94cf2013-10-12 17:25:17 +0000175
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000176struct Rec {
177 A* fInstance;
178 int fValue;
179};
180
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +0000181DEF_TEST(GrMemoryPool, reporter) {
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000182 // prealloc and min alloc sizes for the pool
183 static const size_t gSizes[][2] = {
184 {0, 0},
185 {10 * sizeof(A), 20 * sizeof(A)},
186 {100 * sizeof(A), 100 * sizeof(A)},
187 {500 * sizeof(A), 500 * sizeof(A)},
188 {10000 * sizeof(A), 0},
189 {1, 100 * sizeof(A)},
190 };
Michael Ludwigcd019792020-03-17 10:14:48 -0400191
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000192 // different percentages of creation vs deletion
193 static const float gCreateFraction[] = {1.f, .95f, 0.75f, .5f};
194 // number of create/destroys per test
195 static const int kNumIters = 20000;
196 // check that all the values stored in A objects are correct after this
197 // number of iterations
198 static const int kCheckPeriod = 500;
199
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000200 SkRandom r;
Herb Derbyc8366922022-06-27 15:45:25 -0400201 for (size_t s = 0; s < std::size(gSizes); ++s) {
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000202 A::SetAllocator(gSizes[s][0], gSizes[s][1]);
Michael Ludwigcd019792020-03-17 10:14:48 -0400203 A::ValidatePool();
Herb Derbyc8366922022-06-27 15:45:25 -0400204 for (size_t c = 0; c < std::size(gCreateFraction); ++c) {
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000205 SkTDArray<Rec> instanceRecs;
206 for (int i = 0; i < kNumIters; ++i) {
207 float createOrDestroy = r.nextUScalar1();
208 if (createOrDestroy < gCreateFraction[c] ||
Herb Derby3050ef52022-09-29 21:12:37 -0400209 0 == instanceRecs.size()) {
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000210 Rec* rec = instanceRecs.append();
211 rec->fInstance = A::Create(&r);
212 rec->fValue = static_cast<int>(r.nextU());
213 rec->fInstance->setValues(rec->fValue);
214 } else {
Herb Derby3050ef52022-09-29 21:12:37 -0400215 int d = r.nextRangeU(0, instanceRecs.size() - 1);
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000216 Rec& rec = instanceRecs[d];
217 REPORTER_ASSERT(reporter, rec.fInstance->checkValues(rec.fValue));
218 delete rec.fInstance;
219 instanceRecs.removeShuffle(d);
220 }
221 if (0 == i % kCheckPeriod) {
Michael Ludwigcd019792020-03-17 10:14:48 -0400222 A::ValidatePool();
John Stiles97659732021-08-12 10:48:09 -0400223 for (Rec& rec : instanceRecs) {
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000224 REPORTER_ASSERT(reporter, rec.fInstance->checkValues(rec.fValue));
225 }
226 }
227 }
John Stiles97659732021-08-12 10:48:09 -0400228 for (Rec& rec : instanceRecs) {
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000229 REPORTER_ASSERT(reporter, rec.fInstance->checkValues(rec.fValue));
230 delete rec.fInstance;
231 }
bsalomon@google.com4da34e32012-06-19 15:40:27 +0000232 }
233 }
234}
235
dskibae4cd0062016-11-29 06:50:35 -0800236// GrMemoryPool requires that it's empty at the point of destruction. This helps
237// achieving that by releasing all added memory in the destructor.
238class AutoPoolReleaser {
239public:
240 AutoPoolReleaser(GrMemoryPool& pool): fPool(pool) {
241 }
242 ~AutoPoolReleaser() {
243 for (void* ptr: fAllocated) {
244 fPool.release(ptr);
245 }
246 }
247 void add(void* ptr) {
248 fAllocated.push_back(ptr);
249 }
250private:
251 GrMemoryPool& fPool;
Herb Derby99967012023-03-29 10:01:23 -0400252 TArray<void*> fAllocated;
dskibae4cd0062016-11-29 06:50:35 -0800253};
254
255DEF_TEST(GrMemoryPoolAPI, reporter) {
Brian Salomon6986c652019-12-12 10:58:47 -0500256 constexpr size_t kSmallestMinAllocSize = GrMemoryPool::kMinAllocationSize;
dskibae4cd0062016-11-29 06:50:35 -0800257
Brian Salomon6986c652019-12-12 10:58:47 -0500258 // Allocates memory until pool adds a new block (pool->size() changes).
dskibae4cd0062016-11-29 06:50:35 -0800259 auto allocateMemory = [](GrMemoryPool& pool, AutoPoolReleaser& r) {
260 size_t origPoolSize = pool.size();
261 while (pool.size() == origPoolSize) {
262 r.add(pool.allocate(31));
263 }
264 };
265
Brian Salomon6986c652019-12-12 10:58:47 -0500266 // Effective prealloc space capacity is >= kMinAllocationSize.
dskibae4cd0062016-11-29 06:50:35 -0800267 {
Brian Salomon6986c652019-12-12 10:58:47 -0500268 auto pool = GrMemoryPool::Make(0, 0);
269 REPORTER_ASSERT(reporter, pool->preallocSize() == kSmallestMinAllocSize);
dskibae4cd0062016-11-29 06:50:35 -0800270 }
271
Brian Salomon6986c652019-12-12 10:58:47 -0500272 // Effective block size capacity >= kMinAllocationSize.
dskibae4cd0062016-11-29 06:50:35 -0800273 {
Brian Salomon6986c652019-12-12 10:58:47 -0500274 auto pool = GrMemoryPool::Make(kSmallestMinAllocSize, kSmallestMinAllocSize / 2);
275 AutoPoolReleaser r(*pool);
dskibae4cd0062016-11-29 06:50:35 -0800276
Brian Salomon6986c652019-12-12 10:58:47 -0500277 allocateMemory(*pool, r);
278 REPORTER_ASSERT(reporter, pool->size() == kSmallestMinAllocSize);
dskibae4cd0062016-11-29 06:50:35 -0800279 }
280
281 // Pool allocates exactly preallocSize on creation.
282 {
283 constexpr size_t kPreallocSize = kSmallestMinAllocSize * 5;
Brian Salomon6986c652019-12-12 10:58:47 -0500284 auto pool = GrMemoryPool::Make(kPreallocSize, 0);
285 REPORTER_ASSERT(reporter, pool->preallocSize() == kPreallocSize);
dskibae4cd0062016-11-29 06:50:35 -0800286 }
287
288 // Pool allocates exactly minAllocSize when it expands.
289 {
290 constexpr size_t kMinAllocSize = kSmallestMinAllocSize * 7;
Brian Salomon6986c652019-12-12 10:58:47 -0500291 auto pool = GrMemoryPool::Make(0, kMinAllocSize);
292 AutoPoolReleaser r(*pool);
Michael Ludwigcd019792020-03-17 10:14:48 -0400293 REPORTER_ASSERT(reporter, pool->size() == 0);
dskibae4cd0062016-11-29 06:50:35 -0800294
Brian Salomon6986c652019-12-12 10:58:47 -0500295 allocateMemory(*pool, r);
296 REPORTER_ASSERT(reporter, pool->size() == kMinAllocSize);
dskibae4cd0062016-11-29 06:50:35 -0800297
Brian Salomon6986c652019-12-12 10:58:47 -0500298 allocateMemory(*pool, r);
299 REPORTER_ASSERT(reporter, pool->size() == 2 * kMinAllocSize);
dskibae4cd0062016-11-29 06:50:35 -0800300 }
301
302 // When asked to allocate amount > minAllocSize, pool allocates larger block
303 // to accommodate all internal structures.
304 {
305 constexpr size_t kMinAllocSize = kSmallestMinAllocSize * 2;
Brian Salomon6986c652019-12-12 10:58:47 -0500306 auto pool = GrMemoryPool::Make(kSmallestMinAllocSize, kMinAllocSize);
307 AutoPoolReleaser r(*pool);
dskibae4cd0062016-11-29 06:50:35 -0800308
Brian Salomon6986c652019-12-12 10:58:47 -0500309 REPORTER_ASSERT(reporter, pool->size() == 0);
dskibae4cd0062016-11-29 06:50:35 -0800310
311 constexpr size_t hugeSize = 10 * kMinAllocSize;
Brian Salomon6986c652019-12-12 10:58:47 -0500312 r.add(pool->allocate(hugeSize));
313 REPORTER_ASSERT(reporter, pool->size() > hugeSize);
dskibae4cd0062016-11-29 06:50:35 -0800314
315 // Block size allocated to accommodate huge request doesn't include any extra
316 // space, so next allocation request allocates a new block.
Brian Salomon6986c652019-12-12 10:58:47 -0500317 size_t hugeBlockSize = pool->size();
318 r.add(pool->allocate(0));
319 REPORTER_ASSERT(reporter, pool->size() == hugeBlockSize + kMinAllocSize);
dskibae4cd0062016-11-29 06:50:35 -0800320 }
321}