Simplify path allocation, clean up resources correctly

Simplify path id allocation in NVPR backend. Instead of using
an AVL tree of path id ranges for the first 65535 ids, use just
a simple stategy of overallocation and "bump index".

Fixes the bug where previously overallocated ids were not deleted.

The advantage is that the implementation is simple and all allocations
go through overallocation, not just the first 65535 of the 1-range
allocations.

Removes the logic where paths were cleared with setting path data to
null instead of deleting the whole path. Now deleted paths are just
deleted normally. These operations should have equivalent performance on
command buffer. Deleting the path should enable the driver to do more
maintainance.

Removes the GLNameAllocator, as it was only used for paths. In order for
it to be used for other IDs, it probably would need to be re-written to
support cleanup and arbitrary ranges. Also, the interface would probably
need to be changed to not requiring the block to be allocated before it
could be managed by the structure.
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1150243003

Review URL: https://codereview.chromium.org/1150243003
This commit is contained in:
kkinnunen 2016-01-13 23:36:45 -08:00 committed by Commit bot
parent 7ddfeeddf7
commit 702501ddca
7 changed files with 60 additions and 673 deletions

View File

@ -326,8 +326,6 @@
'<(skia_src_path)/gpu/gl/GrGLIndexBuffer.h',
'<(skia_src_path)/gpu/gl/GrGLInterface.cpp',
'<(skia_src_path)/gpu/gl/GrGLIRect.h',
'<(skia_src_path)/gpu/gl/GrGLNameAllocator.cpp',
'<(skia_src_path)/gpu/gl/GrGLNameAllocator.h',
'<(skia_src_path)/gpu/gl/GrGLNoOpInterface.cpp',
'<(skia_src_path)/gpu/gl/GrGLNoOpInterface.h',
'<(skia_src_path)/gpu/gl/GrGLPath.cpp',

View File

@ -227,6 +227,10 @@ GrGLGpu::GrGLGpu(GrGLContext* ctx, GrContext* context)
}
GrGLGpu::~GrGLGpu() {
// Delete the path rendering explicitly, since it will need working gpu object to release the
// resources the object itself holds.
fPathRendering.reset();
if (0 != fHWProgramID) {
// detach the current program so there is no confusion on OpenGL's part
// that we want it to be deleted

View File

@ -1,370 +0,0 @@
/*
* Copyright 2014 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "GrGLNameAllocator.h"
/**
* This is the abstract base class for a nonempty AVL tree that tracks allocated
* names within the half-open range [fFirst, fEnd). The inner nodes can be
* sparse (meaning not every name within the range is necessarily allocated),
* but the bounds are tight, so fFirst *is* guaranteed to be allocated, and so
* is fEnd - 1.
*/
class GrGLNameAllocator::SparseNameRange : public SkRefCnt {
public:
virtual ~SparseNameRange() {}
/**
* Return the beginning of the range. first() is guaranteed to be allocated.
*
* @return The first name in the range.
*/
GrGLuint first() const { return fFirst; }
/**
* Return the end of the range. end() - 1 is guaranteed to be allocated.
*
* @return One plus the final name in the range.
*/
GrGLuint end() const { return fEnd; }
/**
* Return the height of the tree. This can only be nonzero at an inner node.
*
* @return 0 if the implementation is a leaf node,
* The nonzero height of the tree otherwise.
*/
GrGLuint height() const { return fHeight; }
/**
* Allocate a name from strictly inside this range. The call will fail if
* there is not a free name within.
*
* @param outName A pointer that receives the allocated name. outName will
* be set to zero if there were no free names within the
* range [fFirst, fEnd).
* @return The resulting SparseNameRange after the allocation. Note that
* this call is destructive, so the original SparseNameRange will no
* longer be valid afterward. The caller must always update its
* pointer with the new SparseNameRange.
*/
virtual SparseNameRange* SK_WARN_UNUSED_RESULT internalAllocate(GrGLuint* outName) = 0;
/**
* Remove the leftmost leaf node from this range (or the entire thing if it
* *is* a leaf node). This is an internal helper method that is used after
* an allocation if one contiguous range became adjacent to another. (The
* range gets removed so the one immediately before can be extended,
* collapsing the two into one.)
*
* @param removedCount A pointer that receives the size of the contiguous
range that was removed.
* @return The resulting SparseNameRange after the removal (or nullptr if it
* became empty). Note that this call is destructive, so the
* original SparseNameRange will no longer be valid afterward. The
* caller must always update its pointer with the new
* SparseNameRange.
*/
virtual SparseNameRange* SK_WARN_UNUSED_RESULT removeLeftmostContiguousRange(GrGLuint* removedCount) = 0;
/**
* Append adjacent allocated names to the end of this range. This operation
* does not affect the structure of the tree. The caller is responsible for
* ensuring the new names won't overlap sibling ranges, if any.
*
* @param count The number of adjacent names to append.
* @return The first name appended.
*/
virtual GrGLuint appendNames(GrGLuint count) = 0;
/**
* Prepend adjacent allocated names behind the beginning of this range. This
* operation does not affect the structure of the tree. The caller is
* responsible for ensuring the new names won't overlap sibling ranges, if
* any.
*
* @param count The number of adjacent names to prepend.
* @return The final name prepended (the one with the lowest value).
*/
virtual GrGLuint prependNames(GrGLuint count) = 0;
/**
* Free a name so it is no longer tracked as allocated. If the name is at
* the very beginning or very end of the range, the boundaries [fFirst, fEnd)
* will be tightened.
*
* @param name The name to free. Not-allocated names are silently ignored
* the same way they are in the OpenGL spec.
* @return The resulting SparseNameRange after the free (or nullptr if it
* became empty). Note that this call is destructive, so the
* original SparseNameRange will no longer be valid afterward. The
* caller must always update its pointer with the new
* SparseNameRange.
*/
virtual SparseNameRange* SK_WARN_UNUSED_RESULT free(GrGLuint name) = 0;
protected:
SparseNameRange* takeRef() {
this->ref();
return this;
}
GrGLuint fFirst;
GrGLuint fEnd;
GrGLuint fHeight;
};
/**
* This class is the SparseNameRange implementation for an inner node. It is an
* AVL tree with non-null, non-adjacent left and right children.
*/
class GrGLNameAllocator::SparseNameTree : public SparseNameRange {
public:
SparseNameTree(SparseNameRange* left, SparseNameRange* right)
: fLeft(left),
fRight(right) {
SkASSERT(fLeft.get());
SkASSERT(fRight.get());
this->updateStats();
}
SparseNameRange* SK_WARN_UNUSED_RESULT internalAllocate(GrGLuint* outName) override {
// Try allocating the range inside fLeft's internal gaps.
fLeft.reset(fLeft->internalAllocate(outName));
if (0 != *outName) {
this->updateStats();
return this->rebalance();
}
if (fLeft->end() + 1 == fRight->first()) {
// It closed the gap between fLeft and fRight; merge.
GrGLuint removedCount;
fRight.reset(fRight->removeLeftmostContiguousRange(&removedCount));
*outName = fLeft->appendNames(1 + removedCount);
if (nullptr == fRight.get()) {
return fLeft.detach();
}
this->updateStats();
return this->rebalance();
}
// There is guaranteed to be a gap between fLeft and fRight, and the
// "size 1" case has already been covered.
SkASSERT(fLeft->end() + 1 < fRight->first());
*outName = fLeft->appendNames(1);
return this->takeRef();
}
SparseNameRange* SK_WARN_UNUSED_RESULT removeLeftmostContiguousRange(GrGLuint* removedCount) override {
fLeft.reset(fLeft->removeLeftmostContiguousRange(removedCount));
if (nullptr == fLeft) {
return fRight.detach();
}
this->updateStats();
return this->rebalance();
}
GrGLuint appendNames(GrGLuint count) override {
SkASSERT(fEnd + count > fEnd); // Check for integer wrap.
GrGLuint name = fRight->appendNames(count);
SkASSERT(fRight->end() == fEnd + count);
this->updateStats();
return name;
}
GrGLuint prependNames(GrGLuint count) override {
SkASSERT(fFirst > count); // We can't allocate at or below 0.
GrGLuint name = fLeft->prependNames(count);
SkASSERT(fLeft->first() == fFirst - count);
this->updateStats();
return name;
}
SparseNameRange* SK_WARN_UNUSED_RESULT free(GrGLuint name) override {
if (name < fLeft->end()) {
fLeft.reset(fLeft->free(name));
if (nullptr == fLeft) {
// fLeft became empty after the free.
return fRight.detach();
}
this->updateStats();
return this->rebalance();
} else {
fRight.reset(fRight->free(name));
if (nullptr == fRight) {
// fRight became empty after the free.
return fLeft.detach();
}
this->updateStats();
return this->rebalance();
}
}
private:
typedef SkAutoTUnref<SparseNameRange> SparseNameTree::* ChildRange;
SparseNameRange* SK_WARN_UNUSED_RESULT rebalance() {
if (fLeft->height() > fRight->height() + 1) {
return this->rebalanceImpl<&SparseNameTree::fLeft, &SparseNameTree::fRight>();
}
if (fRight->height() > fLeft->height() + 1) {
return this->rebalanceImpl<&SparseNameTree::fRight, &SparseNameTree::fLeft>();
}
return this->takeRef();
}
/**
* Rebalance the tree using rotations, as described in the AVL algorithm:
* http://en.wikipedia.org/wiki/AVL_tree#Insertion
*/
template<ChildRange Tall, ChildRange Short>
SparseNameRange* SK_WARN_UNUSED_RESULT rebalanceImpl() {
// We should be calling rebalance() enough that the tree never gets more
// than one rotation off balance.
SkASSERT(2 == (this->*Tall)->height() - (this->*Short)->height());
// Ensure we are in the 'Left Left' or 'Right Right' case:
// http://en.wikipedia.org/wiki/AVL_tree#Insertion
SparseNameTree* tallChild = static_cast<SparseNameTree*>((this->*Tall).get());
if ((tallChild->*Tall)->height() < (tallChild->*Short)->height()) {
(this->*Tall).reset(tallChild->rotate<Short, Tall>());
}
// Perform a rotation to balance the tree.
return this->rotate<Tall, Short>();
}
/**
* Perform a node rotation, as described in the AVL algorithm:
* http://en.wikipedia.org/wiki/AVL_tree#Insertion
*/
template<ChildRange Tall, ChildRange Short>
SparseNameRange* SK_WARN_UNUSED_RESULT rotate() {
SparseNameTree* newRoot = static_cast<SparseNameTree*>((this->*Tall).detach());
(this->*Tall).reset((newRoot->*Short).detach());
this->updateStats();
(newRoot->*Short).reset(this->takeRef());
newRoot->updateStats();
return newRoot;
}
void updateStats() {
SkASSERT(fLeft->end() < fRight->first()); // There must be a gap between left and right.
fFirst = fLeft->first();
fEnd = fRight->end();
fHeight = 1 + SkMax32(fLeft->height(), fRight->height());
}
SkAutoTUnref<SparseNameRange> fLeft;
SkAutoTUnref<SparseNameRange> fRight;
};
/**
* This class is the SparseNameRange implementation for a leaf node. It just a
* contiguous range of allocated names.
*/
class GrGLNameAllocator::ContiguousNameRange : public SparseNameRange {
public:
ContiguousNameRange(GrGLuint first, GrGLuint end) {
SkASSERT(first < end);
fFirst = first;
fEnd = end;
fHeight = 0;
}
SparseNameRange* SK_WARN_UNUSED_RESULT internalAllocate(GrGLuint* outName) override {
*outName = 0; // No internal gaps, we are contiguous.
return this->takeRef();
}
SparseNameRange* SK_WARN_UNUSED_RESULT removeLeftmostContiguousRange(GrGLuint* removedCount) override {
*removedCount = fEnd - fFirst;
return nullptr;
}
GrGLuint appendNames(GrGLuint count) override {
SkASSERT(fEnd + count > fEnd); // Check for integer wrap.
GrGLuint name = fEnd;
fEnd += count;
return name;
}
GrGLuint prependNames(GrGLuint count) override {
SkASSERT(fFirst > count); // We can't allocate at or below 0.
fFirst -= count;
return fFirst;
}
SparseNameRange* SK_WARN_UNUSED_RESULT free(GrGLuint name) override {
if (name < fFirst || name >= fEnd) {
// Not-allocated names are silently ignored.
return this->takeRef();
}
if (fFirst == name) {
++fFirst;
return (fEnd == fFirst) ? nullptr : this->takeRef();
}
if (fEnd == name + 1) {
--fEnd;
return this->takeRef();
}
SparseNameRange* left = new ContiguousNameRange(fFirst, name);
SparseNameRange* right = this->takeRef();
fFirst = name + 1;
return new SparseNameTree(left, right);
}
};
GrGLNameAllocator::GrGLNameAllocator(GrGLuint firstName, GrGLuint endName)
: fFirstName(firstName),
fEndName(endName) {
SkASSERT(firstName > 0);
SkASSERT(endName > firstName);
}
GrGLNameAllocator::~GrGLNameAllocator() {
}
GrGLuint GrGLNameAllocator::allocateName() {
if (nullptr == fAllocatedNames.get()) {
fAllocatedNames.reset(new ContiguousNameRange(fFirstName, fFirstName + 1));
return fFirstName;
}
if (fAllocatedNames->first() > fFirstName) {
return fAllocatedNames->prependNames(1);
}
GrGLuint name;
fAllocatedNames.reset(fAllocatedNames->internalAllocate(&name));
if (0 != name) {
return name;
}
if (fAllocatedNames->end() < fEndName) {
return fAllocatedNames->appendNames(1);
}
// Out of names.
return 0;
}
void GrGLNameAllocator::free(GrGLuint name) {
if (!fAllocatedNames.get()) {
// Not-allocated names are silently ignored.
return;
}
fAllocatedNames.reset(fAllocatedNames->free(name));
}

View File

@ -1,86 +0,0 @@
/*
* Copyright 2014 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrGLNameAllocator_DEFINED
#define GrGLNameAllocator_DEFINED
#include "SkRefCnt.h"
#include "gl/GrGLTypes.h"
/**
* This class assumes ownership of an explicit range of OpenGL object names and
* manages allocations within that range. This allows the app to generate new
* objects on the client side without making round trips to the GL server.
*/
class GrGLNameAllocator {
public:
/**
* Constructs a name allocator that produces names within the explicit
* half-open range [firstName, end). Note that the caller will most likely
* need to call glGen* beforehand to reserve a range within the GL driver,
* and then invoke this constructor with that range.
*
* @param firstName The first name in the range owned by this class. Must be
greater than zero.
* @param endName The first past-the-end name beyond the range owned by
this class. Must be >= firstName.
*/
GrGLNameAllocator(GrGLuint firstName, GrGLuint endName);
/**
* Destructs the name allocator. The caller is responsible for calling the
* appropriate glDelete* on the range if necessary.
*/
~GrGLNameAllocator();
/**
* Return the beginning of this class's range.
*
* @return The first name in the range owned by this class.
*/
GrGLuint firstName() const { return fFirstName; }
/**
* Return the end of this class's range. Note that endName() is not owned by
* this class.
*
* @return One plus the final name in the range owned by this class.
*/
GrGLuint endName() const { return fEndName; }
/**
* Allocate an OpenGL object name from within this class's range.
*
* @return The name if one was available,
0 if all the names in the range were already in use.
*/
GrGLuint allocateName();
/**
* Free an OpenGL object name, allowing it to be returned by a future call
* to allocateName(). Note that the caller should most likely redefine the
* object as empty to deallocate any underlying GPU memory before calling
* this method (but not call glDelete*, since that would free up the name
* within the driver itself).
*
* @param name The object name to free. Not-allocated names are silently
* ignored the same way they are in the OpenGL spec.
*/
void free(GrGLuint name);
private:
class SparseNameRange;
class SparseNameTree;
class ContiguousNameRange;
const GrGLuint fFirstName;
const GrGLuint fEndName;
SkAutoTUnref<SparseNameRange> fAllocatedNames;
};
#endif

View File

@ -6,7 +6,6 @@
*/
#include "gl/GrGLPathRendering.h"
#include "gl/GrGLNameAllocator.h"
#include "gl/GrGLUtil.h"
#include "gl/GrGLGpu.h"
@ -20,6 +19,9 @@
#define GL_CALL(X) GR_GL_CALL(this->gpu()->glInterface(), X)
#define GL_CALL_RET(RET, X) GR_GL_CALL_RET(this->gpu()->glInterface(), RET, X)
// Number of paths to allocate per glGenPaths call. The call can be overly slow on command buffer GL
// implementation. The call has a result value, and thus waiting for the call completion is needed.
static const GrGLsizei kPathIDPreallocationAmount = 65536;
static const GrGLenum gIndexType2GLType[] = {
GR_GL_UNSIGNED_BYTE,
@ -60,17 +62,21 @@ static GrGLenum gr_stencil_op_to_gl_path_rendering_fill_mode(GrStencilOp op) {
}
GrGLPathRendering::GrGLPathRendering(GrGLGpu* gpu)
: GrPathRendering(gpu) {
: GrPathRendering(gpu)
, fPreallocatedPathCount(0) {
const GrGLInterface* glInterface = gpu->glInterface();
fCaps.bindFragmentInputSupport =
nullptr != glInterface->fFunctions.fBindFragmentInputLocation;
}
GrGLPathRendering::~GrGLPathRendering() {
if (fPreallocatedPathCount > 0) {
this->deletePaths(fFirstPreallocatedPathID, fPreallocatedPathCount);
}
}
void GrGLPathRendering::abandonGpuResources() {
fPathNameAllocator.reset(nullptr);
fPreallocatedPathCount = 0;
}
void GrGLPathRendering::resetContext() {
@ -230,54 +236,57 @@ void GrGLPathRendering::setProjectionMatrix(const SkMatrix& matrix,
}
GrGLuint GrGLPathRendering::genPaths(GrGLsizei range) {
if (range > 1) {
GrGLuint name;
GL_CALL_RET(name, GenPaths(range));
return name;
SkASSERT(range > 0);
GrGLuint firstID;
if (fPreallocatedPathCount >= range) {
firstID = fFirstPreallocatedPathID;
fPreallocatedPathCount -= range;
fFirstPreallocatedPathID += range;
return firstID;
}
// Allocate range + the amount to fill up preallocation amount. If succeed, either join with
// the existing preallocation range or delete the existing and use the new (potentially partial)
// preallocation range.
GrGLsizei allocAmount = range + (kPathIDPreallocationAmount - fPreallocatedPathCount);
if (allocAmount >= range) {
GL_CALL_RET(firstID, GenPaths(allocAmount));
if (firstID != 0) {
if (fPreallocatedPathCount > 0 &&
firstID == fFirstPreallocatedPathID + fPreallocatedPathCount) {
firstID = fFirstPreallocatedPathID;
fPreallocatedPathCount += allocAmount - range;
fFirstPreallocatedPathID += range;
return firstID;
}
if (allocAmount > range) {
if (fPreallocatedPathCount > 0) {
this->deletePaths(fFirstPreallocatedPathID, fPreallocatedPathCount);
}
fFirstPreallocatedPathID = firstID + range;
fPreallocatedPathCount = allocAmount - range;
}
// Special case: if allocAmount == range, we have full preallocated range.
return firstID;
}
}
// Failed to allocate with preallocation. Remove existing preallocation and try to allocate just
// the range.
if (fPreallocatedPathCount > 0) {
this->deletePaths(fFirstPreallocatedPathID, fPreallocatedPathCount);
fPreallocatedPathCount = 0;
}
if (nullptr == fPathNameAllocator.get()) {
static const int range = 65536;
GrGLuint firstName;
GL_CALL_RET(firstName, GenPaths(range));
fPathNameAllocator.reset(new GrGLNameAllocator(firstName, firstName + range));
GL_CALL_RET(firstID, GenPaths(range));
if (firstID == 0) {
SkDebugf("Warning: Failed to allocate path\n");
}
// When allocating names one at a time, pull from a client-side pool of
// available names in order to save a round trip to the GL server.
GrGLuint name = fPathNameAllocator->allocateName();
if (0 == name) {
// Our reserved path names are all in use. Fall back on GenPaths.
GL_CALL_RET(name, GenPaths(1));
}
return name;
return firstID;
}
void GrGLPathRendering::deletePaths(GrGLuint path, GrGLsizei range) {
if (range > 1) {
// It is not supported to delete names in ranges that were allocated
// individually using GrGLPathNameAllocator.
SkASSERT(nullptr == fPathNameAllocator.get() ||
path + range <= fPathNameAllocator->firstName() ||
path >= fPathNameAllocator->endName());
GL_CALL(DeletePaths(path, range));
return;
}
if (nullptr == fPathNameAllocator.get() ||
path < fPathNameAllocator->firstName() ||
path >= fPathNameAllocator->endName()) {
// If we aren't inside fPathNameAllocator's range then this name was
// generated by the GenPaths fallback (or else was never allocated).
GL_CALL(DeletePaths(path, 1));
return;
}
// Make the path empty to save memory, but don't free the name in the driver.
GL_CALL(PathCommands(path, 0, nullptr, 0, GR_GL_FLOAT, nullptr));
fPathNameAllocator->free(path);
GL_CALL(DeletePaths(path, range));
}
void GrGLPathRendering::flushPathStencilSettings(const GrStencilSettings& stencilSettings) {

View File

@ -111,7 +111,8 @@ private:
};
GrGLGpu* gpu();
SkAutoTDelete<GrGLNameAllocator> fPathNameAllocator;
GrGLuint fFirstPreallocatedPathID;
GrGLsizei fPreallocatedPathCount;
MatrixState fHWProjectionMatrixState;
GrStencilSettings fHWPathStencilSettings;
Caps fCaps;

View File

@ -1,169 +0,0 @@
/*
* Copyright 2014 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#if SK_SUPPORT_GPU
#include "gl/GrGLNameAllocator.h"
#include "Test.h"
////////////////////////////////////////////////////////////////////////////////
class NameLeakTest {
static const GrGLuint kFirstName = 101;
static const GrGLuint kRange = 1013;
public:
NameLeakTest(skiatest::Reporter* reporter)
: fReporter(reporter),
fAllocator(kFirstName, kFirstName + kRange),
fAllocatedCount(0),
fRandomName(kFirstName + 4 * kRange / 7) {
memset(fAllocatedNames, 0, sizeof(fAllocatedNames));
}
bool run() {
if (!this->allocateAllRemaining()) {
return false;
}
for (GrGLuint freeCount = 1; freeCount <= kRange; ++freeCount) {
if (!this->freeRandomNames(freeCount)) {
return false;
}
if (!this->allocateAllRemaining()) {
return false;
}
}
return true;
}
private:
bool isAllocated(GrGLuint name) const {
return fAllocatedNames[name - kFirstName];
}
void setAllocated(GrGLuint name, bool allocated) {
fAllocatedNames[name - kFirstName] = allocated;
}
bool allocateAllRemaining() {
for (; fAllocatedCount < kRange; ++fAllocatedCount) {
GrGLuint name = fAllocator.allocateName();
if (0 == name) {
ERRORF(fReporter,
"Name allocate failed, but there should still be %u free names",
kRange - fAllocatedCount);
return false;
}
if (name < kFirstName || name >= kFirstName + kRange) {
ERRORF(fReporter,
"Name allocate returned name %u outside its bounds [%u, %u)",
name, kFirstName, kFirstName + kRange);
return false;
}
if (this->isAllocated(name)) {
ERRORF(fReporter, "Name allocate returned name that is already allocated");
return false;
}
this->setAllocated(name, true);
}
// Ensure it returns 0 once all the names are allocated.
GrGLuint name = fAllocator.allocateName();
if (0 != name) {
ERRORF(fReporter,
"Name allocate did not fail when all names were already in use");
return false;
}
// Ensure every unique name is allocated.
for (GrGLuint i = 0; i < kRange; ++i) {
if (!this->isAllocated(kFirstName + i)) {
ERRORF(fReporter, "Not all unique names are allocated after allocateAllRemaining()");
return false;
}
}
return true;
}
bool freeRandomNames(GrGLuint count) {
// The values a and c make up an LCG (pseudo-random generator). These
// values must satisfy the Hull-Dobell Theorem (with m=kRange):
// http://en.wikipedia.org/wiki/Linear_congruential_generator
// We use our own generator to guarantee it hits each unique value
// within kRange exactly once before repeating.
const GrGLuint seed = (count + fRandomName) / 2;
const GrGLuint a = seed * kRange + 1;
const GrGLuint c = (seed * 743) % kRange;
for (GrGLuint i = 0; i < count; ++i) {
fRandomName = (a * fRandomName + c) % kRange;
const GrGLuint name = kFirstName + fRandomName;
if (!this->isAllocated(name)) {
ERRORF(fReporter, "Test bug: Should not free a not-allocated name at this point (%u)", i);
return false;
}
fAllocator.free(name);
this->setAllocated(name, false);
--fAllocatedCount;
}
return true;
}
skiatest::Reporter* fReporter;
GrGLNameAllocator fAllocator;
bool fAllocatedNames[kRange];
GrGLuint fAllocatedCount;
GrGLuint fRandomName;
};
DEF_GPUTEST(NameAllocator, reporter, factory) {
// Ensure no names are leaked or double-allocated during heavy usage.
{
NameLeakTest nameLeakTest(reporter);
nameLeakTest.run();
}
static const GrGLuint range = 32;
GrGLNameAllocator allocator(1, 1 + range);
for (GrGLuint i = 1; i <= range; ++i) {
allocator.allocateName();
}
REPORTER_ASSERT(reporter, 0 == allocator.allocateName());
// Test freeing names out of range.
allocator.free(allocator.firstName() - 1);
allocator.free(allocator.endName());
REPORTER_ASSERT(reporter, 0 == allocator.allocateName());
// Test freeing not-allocated names.
for (GrGLuint i = 1; i <= range/2; i += 2) {
allocator.free(i);
}
for (GrGLuint i = 1; i <= range/2; i += 2) {
// None of these names will be allocated.
allocator.free(i);
}
for (GrGLuint i = 1; i <= range/2; ++i) {
// Every other name will not be be allocated.
allocator.free(i);
}
for (GrGLuint i = 1; i <= range/2; ++i) {
if (0 == allocator.allocateName()) {
ERRORF(reporter, "Name allocate failed when there should be free names");
break;
}
}
REPORTER_ASSERT(reporter, 0 == allocator.allocateName());
}
#endif