diff options
-rw-r--r-- | gyp/gpu.gypi | 2 | ||||
-rw-r--r-- | src/gpu/gl/GrGLGpu.cpp | 4 | ||||
-rw-r--r-- | src/gpu/gl/GrGLNameAllocator.cpp | 370 | ||||
-rw-r--r-- | src/gpu/gl/GrGLNameAllocator.h | 86 | ||||
-rw-r--r-- | src/gpu/gl/GrGLPathRendering.cpp | 97 | ||||
-rw-r--r-- | src/gpu/gl/GrGLPathRendering.h | 3 | ||||
-rw-r--r-- | tests/NameAllocatorTest.cpp | 169 |
7 files changed, 59 insertions, 672 deletions
diff --git a/gyp/gpu.gypi b/gyp/gpu.gypi index 44c1d1a7c3..a637e8ee5a 100644 --- a/gyp/gpu.gypi +++ b/gyp/gpu.gypi @@ -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', diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp index 972049aa56..9b03a2e23d 100644 --- a/src/gpu/gl/GrGLGpu.cpp +++ b/src/gpu/gl/GrGLGpu.cpp @@ -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 diff --git a/src/gpu/gl/GrGLNameAllocator.cpp b/src/gpu/gl/GrGLNameAllocator.cpp deleted file mode 100644 index 03123a6d86..0000000000 --- a/src/gpu/gl/GrGLNameAllocator.cpp +++ /dev/null @@ -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)); -} diff --git a/src/gpu/gl/GrGLNameAllocator.h b/src/gpu/gl/GrGLNameAllocator.h deleted file mode 100644 index 8b0b2a244a..0000000000 --- a/src/gpu/gl/GrGLNameAllocator.h +++ /dev/null @@ -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 diff --git a/src/gpu/gl/GrGLPathRendering.cpp b/src/gpu/gl/GrGLPathRendering.cpp index 4c9ef86786..3cfec749ac 100644 --- a/src/gpu/gl/GrGLPathRendering.cpp +++ b/src/gpu/gl/GrGLPathRendering.cpp @@ -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; } - - if (nullptr == fPathNameAllocator.get()) { - static const int range = 65536; - GrGLuint firstName; - GL_CALL_RET(firstName, GenPaths(range)); - fPathNameAllocator.reset(new GrGLNameAllocator(firstName, firstName + range)); + // 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; + } } - - // 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)); + // Failed to allocate with preallocation. Remove existing preallocation and try to allocate just + // the range. + if (fPreallocatedPathCount > 0) { + this->deletePaths(fFirstPreallocatedPathID, fPreallocatedPathCount); + fPreallocatedPathCount = 0; } - return name; + GL_CALL_RET(firstID, GenPaths(range)); + if (firstID == 0) { + SkDebugf("Warning: Failed to allocate path\n"); + } + 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) { diff --git a/src/gpu/gl/GrGLPathRendering.h b/src/gpu/gl/GrGLPathRendering.h index 57209fddc1..c3e5317808 100644 --- a/src/gpu/gl/GrGLPathRendering.h +++ b/src/gpu/gl/GrGLPathRendering.h @@ -111,7 +111,8 @@ private: }; GrGLGpu* gpu(); - SkAutoTDelete<GrGLNameAllocator> fPathNameAllocator; + GrGLuint fFirstPreallocatedPathID; + GrGLsizei fPreallocatedPathCount; MatrixState fHWProjectionMatrixState; GrStencilSettings fHWPathStencilSettings; Caps fCaps; diff --git a/tests/NameAllocatorTest.cpp b/tests/NameAllocatorTest.cpp deleted file mode 100644 index 86efdb2f72..0000000000 --- a/tests/NameAllocatorTest.cpp +++ /dev/null @@ -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 |