* Copyright 2011 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "include/private/base/SkTArray.h"
#include "include/private/base/SkTDArray.h"
#include "src/base/SkRandom.h"
#include "src/gpu/ganesh/GrMemoryPool.h"
#include "tests/Test.h"
#include <array>
#include <cstddef>
#include <cstdint>
#include <memory>
using namespace skia_private;
class A {
public:
A() {}
virtual void setValues(int v) {
fChar = static_cast<char>(v & 0xFF);
}
virtual bool checkValues(int v) {
return fChar == static_cast<char>(v & 0xFF);
}
virtual ~A() {}
void* operator new(size_t size) {
if (!gPool) {
return ::operator new(size);
} else {
return gPool->allocate(size);
}
}
void operator delete(void* p) {
if (!gPool) {
::operator delete(p);
} else {
return gPool->release(p);
}
}
static A* Create(SkRandom* r);
static void SetAllocator(size_t preallocSize, size_t minAllocSize) {
gPool = GrMemoryPool::Make(preallocSize, minAllocSize);
}
static void ResetAllocator() { gPool.reset(); }
static void ValidatePool() {
#ifdef SK_DEBUG
gPool->validate();
#endif
}
private:
static std::unique_ptr<GrMemoryPool> gPool;
char fChar;
};
std::unique_ptr<GrMemoryPool> A::gPool;
class B : public A {
public:
B() {}
void setValues(int v) override {
fDouble = static_cast<double>(v);
this->INHERITED::setValues(v);
}
bool checkValues(int v) override {
return fDouble == static_cast<double>(v) &&
this->INHERITED::checkValues(v);
}
private:
double fDouble;
using INHERITED = A;
};
class C : public A {
public:
C() {}
void setValues(int v) override {
fInt64 = static_cast<int64_t>(v);
this->INHERITED::setValues(v);
}
bool checkValues(int v) override {
return fInt64 == static_cast<int64_t>(v) &&
this->INHERITED::checkValues(v);
}
private:
int64_t fInt64;
using INHERITED = A;
};
class D : public C {
public:
D() {
fB = new B();
}
void setValues(int v) override {
fVoidStar = reinterpret_cast<void*>(static_cast<intptr_t>(v));
this->INHERITED::setValues(v);
fB->setValues(v);
}
bool checkValues(int v) override {
return fVoidStar == reinterpret_cast<void*>(static_cast<intptr_t>(v)) &&
fB->checkValues(v) &&
this->INHERITED::checkValues(v);
}
~D() override {
delete fB;
}
private:
void* fVoidStar;
B* fB;
using INHERITED = C;
};
class E : public A {
public:
E() {}
void setValues(int v) override {
for (size_t i = 0; i < std::size(fIntArray); ++i) {
fIntArray[i] = v;
}
this->INHERITED::setValues(v);
}
bool checkValues(int v) override {
bool ok = true;
for (size_t i = 0; ok && i < std::size(fIntArray); ++i) {
if (fIntArray[i] != v) {
ok = false;
}
}
return ok && this->INHERITED::checkValues(v);
}
private:
int fIntArray[20];
using INHERITED = A;
};
A* A::Create(SkRandom* r) {
switch (r->nextRangeU(0, 4)) {
case 0:
return new A;
case 1:
return new B;
case 2:
return new C;
case 3:
return new D;
case 4:
return new E;
default:
return nullptr;
}
}
struct Rec {
A* fInstance;
int fValue;
};
#if 0
DEF_TEST(GrMemoryPool, reporter) {
static const size_t gSizes[][2] = {
{0, 0},
{10 * sizeof(A), 20 * sizeof(A)},
{100 * sizeof(A), 100 * sizeof(A)},
{500 * sizeof(A), 500 * sizeof(A)},
{10000 * sizeof(A), 0},
{1, 100 * sizeof(A)},
};
static const float gCreateFraction[] = {1.f, .95f, 0.75f, .5f};
static const int kNumIters = 20000;
static const int kCheckPeriod = 500;
SkRandom r;
for (size_t s = 0; s < std::size(gSizes); ++s) {
A::SetAllocator(gSizes[s][0], gSizes[s][1]);
A::ValidatePool();
for (size_t c = 0; c < std::size(gCreateFraction); ++c) {
SkTDArray<Rec> instanceRecs;
for (int i = 0; i < kNumIters; ++i) {
float createOrDestroy = r.nextUScalar1();
if (createOrDestroy < gCreateFraction[c] ||
0 == instanceRecs.size()) {
Rec* rec = instanceRecs.append();
rec->fInstance = A::Create(&r);
rec->fValue = static_cast<int>(r.nextU());
rec->fInstance->setValues(rec->fValue);
} else {
int d = r.nextRangeU(0, instanceRecs.size() - 1);
Rec& rec = instanceRecs[d];
REPORTER_ASSERT(reporter, rec.fInstance->checkValues(rec.fValue));
delete rec.fInstance;
instanceRecs.removeShuffle(d);
}
if (0 == i % kCheckPeriod) {
A::ValidatePool();
for (Rec& rec : instanceRecs) {
REPORTER_ASSERT(reporter, rec.fInstance->checkValues(rec.fValue));
}
}
}
for (Rec& rec : instanceRecs) {
REPORTER_ASSERT(reporter, rec.fInstance->checkValues(rec.fValue));
delete rec.fInstance;
}
}
}
}
#endif
class AutoPoolReleaser {
public:
AutoPoolReleaser(GrMemoryPool& pool): fPool(pool) {
}
~AutoPoolReleaser() {
for (void* ptr: fAllocated) {
fPool.release(ptr);
}
}
void add(void* ptr) {
fAllocated.push_back(ptr);
}
private:
GrMemoryPool& fPool;
TArray<void*> fAllocated;
};
DEF_TEST(GrMemoryPoolAPI, reporter) {
constexpr size_t kSmallestMinAllocSize = GrMemoryPool::kMinAllocationSize;
auto allocateMemory = [](GrMemoryPool& pool, AutoPoolReleaser& r) {
size_t origPoolSize = pool.size();
while (pool.size() == origPoolSize) {
r.add(pool.allocate(31));
}
};
{
auto pool = GrMemoryPool::Make(0, 0);
REPORTER_ASSERT(reporter, pool->preallocSize() == kSmallestMinAllocSize);
}
{
auto pool = GrMemoryPool::Make(kSmallestMinAllocSize, kSmallestMinAllocSize / 2);
AutoPoolReleaser r(*pool);
allocateMemory(*pool, r);
REPORTER_ASSERT(reporter, pool->size() == kSmallestMinAllocSize);
}
{
constexpr size_t kPreallocSize = kSmallestMinAllocSize * 5;
auto pool = GrMemoryPool::Make(kPreallocSize, 0);
REPORTER_ASSERT(reporter, pool->preallocSize() == kPreallocSize);
}
{
constexpr size_t kMinAllocSize = kSmallestMinAllocSize * 7;
auto pool = GrMemoryPool::Make(0, kMinAllocSize);
AutoPoolReleaser r(*pool);
REPORTER_ASSERT(reporter, pool->size() == 0);
allocateMemory(*pool, r);
REPORTER_ASSERT(reporter, pool->size() == kMinAllocSize);
allocateMemory(*pool, r);
REPORTER_ASSERT(reporter, pool->size() == 2 * kMinAllocSize);
}
{
constexpr size_t kMinAllocSize = kSmallestMinAllocSize * 2;
auto pool = GrMemoryPool::Make(kSmallestMinAllocSize, kMinAllocSize);
AutoPoolReleaser r(*pool);
REPORTER_ASSERT(reporter, pool->size() == 0);
constexpr size_t hugeSize = 10 * kMinAllocSize;
r.add(pool->allocate(hugeSize));
REPORTER_ASSERT(reporter, pool->size() > hugeSize);
size_t hugeBlockSize = pool->size();
r.add(pool->allocate(0));
REPORTER_ASSERT(reporter, pool->size() == hugeBlockSize + kMinAllocSize);
}
}