/* * 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 NULL 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 NULL 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(); } virtual SparseNameRange* SK_WARN_UNUSED_RESULT internalAllocate(GrGLuint* outName) SK_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 (NULL == 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(); } virtual SparseNameRange* SK_WARN_UNUSED_RESULT removeLeftmostContiguousRange(GrGLuint* removedCount) SK_OVERRIDE { fLeft.reset(fLeft->removeLeftmostContiguousRange(removedCount)); if (NULL == fLeft) { return fRight.detach(); } this->updateStats(); return this->rebalance(); } virtual GrGLuint appendNames(GrGLuint count) SK_OVERRIDE { SkASSERT(fEnd + count > fEnd); // Check for integer wrap. GrGLuint name = fRight->appendNames(count); SkASSERT(fRight->end() == fEnd + count); this->updateStats(); return name; } virtual GrGLuint prependNames(GrGLuint count) SK_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; } virtual SparseNameRange* SK_WARN_UNUSED_RESULT free(GrGLuint name) SK_OVERRIDE { if (name < fLeft->end()) { fLeft.reset(fLeft->free(name)); if (NULL == fLeft) { // fLeft became empty after the free. return fRight.detach(); } this->updateStats(); return this->rebalance(); } else { fRight.reset(fRight->free(name)); if (NULL == fRight) { // fRight became empty after the free. return fLeft.detach(); } this->updateStats(); return this->rebalance(); } } private: typedef SkAutoTUnref 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 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((this->*Tall).get()); if ((tallChild->*Tall)->height() < (tallChild->*Short)->height()) { (this->*Tall).reset(tallChild->rotate()); } // Perform a rotation to balance the tree. return this->rotate(); } /** * Perform a node rotation, as described in the AVL algorithm: * http://en.wikipedia.org/wiki/AVL_tree#Insertion */ template SparseNameRange* SK_WARN_UNUSED_RESULT rotate() { SparseNameTree* newRoot = static_cast((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 fLeft; SkAutoTUnref 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; } virtual SparseNameRange* SK_WARN_UNUSED_RESULT internalAllocate(GrGLuint* outName) SK_OVERRIDE { *outName = 0; // No internal gaps, we are contiguous. return this->takeRef(); } virtual SparseNameRange* SK_WARN_UNUSED_RESULT removeLeftmostContiguousRange(GrGLuint* removedCount) SK_OVERRIDE { *removedCount = fEnd - fFirst; return NULL; } virtual GrGLuint appendNames(GrGLuint count) SK_OVERRIDE { SkASSERT(fEnd + count > fEnd); // Check for integer wrap. GrGLuint name = fEnd; fEnd += count; return name; } virtual GrGLuint prependNames(GrGLuint count) SK_OVERRIDE { SkASSERT(fFirst > count); // We can't allocate at or below 0. fFirst -= count; return fFirst; } virtual SparseNameRange* SK_WARN_UNUSED_RESULT free(GrGLuint name) SK_OVERRIDE { if (name < fFirst || name >= fEnd) { // Not-allocated names are silently ignored. return this->takeRef(); } if (fFirst == name) { ++fFirst; return (fEnd == fFirst) ? NULL : this->takeRef(); } if (fEnd == name + 1) { --fEnd; return this->takeRef(); } SparseNameRange* left = SkNEW_ARGS(ContiguousNameRange, (fFirst, name)); SparseNameRange* right = this->takeRef(); fFirst = name + 1; return SkNEW_ARGS(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 (NULL == fAllocatedNames.get()) { fAllocatedNames.reset(SkNEW_ARGS(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)); }