DEF_BENCH(
return SkNEW_ARGS(BBoxBuildBench, ("XYordered", &make_XYordered_rects,
- SkQuadTree::Create(QUAD_TREE_BOUNDS)));
+ SkNEW_ARGS(SkQuadTree, (QUAD_TREE_BOUNDS))));
)
DEF_BENCH(
return SkNEW_ARGS(BBoxQueryBench, ("XYordered", &make_XYordered_rects,
- BBoxQueryBench::kRandom_QueryType, SkQuadTree::Create(QUAD_TREE_BOUNDS)));
+ BBoxQueryBench::kRandom_QueryType,
+ SkNEW_ARGS(SkQuadTree, (QUAD_TREE_BOUNDS))));
)
DEF_BENCH(
return SkNEW_ARGS(BBoxBuildBench, ("YXordered", &make_YXordered_rects,
- SkQuadTree::Create(QUAD_TREE_BOUNDS)));
+ SkNEW_ARGS(SkQuadTree, (QUAD_TREE_BOUNDS))));
)
DEF_BENCH(
return SkNEW_ARGS(BBoxQueryBench, ("YXordered", &make_YXordered_rects,
- BBoxQueryBench::kRandom_QueryType, SkQuadTree::Create(QUAD_TREE_BOUNDS)));
+ BBoxQueryBench::kRandom_QueryType,
+ SkNEW_ARGS(SkQuadTree, (QUAD_TREE_BOUNDS))));
)
DEF_BENCH(
return SkNEW_ARGS(BBoxBuildBench, ("random", &make_random_rects,
- SkQuadTree::Create(QUAD_TREE_BOUNDS)));
+ SkNEW_ARGS(SkQuadTree, (QUAD_TREE_BOUNDS))));
)
DEF_BENCH(
return SkNEW_ARGS(BBoxQueryBench, ("random", &make_random_rects,
- BBoxQueryBench::kRandom_QueryType, SkQuadTree::Create(QUAD_TREE_BOUNDS)));
+ BBoxQueryBench::kRandom_QueryType,
+ SkNEW_ARGS(SkQuadTree, (QUAD_TREE_BOUNDS))));
)
DEF_BENCH(
return SkNEW_ARGS(BBoxBuildBench, ("concentric", &make_concentric_rects_increasing,
- SkQuadTree::Create(QUAD_TREE_BOUNDS)));
+ SkNEW_ARGS(SkQuadTree, (QUAD_TREE_BOUNDS))));
)
DEF_BENCH(
return SkNEW_ARGS(BBoxQueryBench, ("concentric", &make_concentric_rects_increasing,
- BBoxQueryBench::kRandom_QueryType, SkQuadTree::Create(QUAD_TREE_BOUNDS)));
+ BBoxQueryBench::kRandom_QueryType,
+ SkNEW_ARGS(SkQuadTree, (QUAD_TREE_BOUNDS))));
)
'../tests/MessageBusTest.cpp',
'../tests/MetaDataTest.cpp',
'../tests/MipMapTest.cpp',
+ '../tests/ObjectPoolTest.cpp',
'../tests/OSPathTest.cpp',
'../tests/OnceTest.cpp',
'../tests/PDFPrimitivesTest.cpp',
'../tests/ShaderImageFilterTest.cpp',
'../tests/ShaderOpacityTest.cpp',
'../tests/SkBase64Test.cpp',
+ '../tests/SListTest.cpp',
'../tests/SmallAllocatorTest.cpp',
'../tests/SortTest.cpp',
'../tests/SrcOverTest.cpp',
#include <stdio.h>
#include <vector>
-class SkQuadTree::QuadTreeNode {
-public:
- struct Data {
- Data(const SkIRect& bounds, void* data) : fBounds(bounds), fInnerBounds(bounds), fData(data) {}
- SkIRect fBounds;
- SkIRect fInnerBounds;
- void* fData;
- };
-
- QuadTreeNode(const SkIRect& bounds)
- : fBounds(bounds)
- , fTopLeft(NULL)
- , fTopRight(NULL)
- , fBottomLeft(NULL)
- , fBottomRight(NULL)
- , fCanSubdivide((fBounds.width() * fBounds.height()) > 0) {}
-
- ~QuadTreeNode() {
- clear();
- }
-
- void clear() {
- SkDELETE(fTopLeft);
- fTopLeft = NULL;
- SkDELETE(fTopRight);
- fTopRight = NULL;
- SkDELETE(fBottomLeft);
- fBottomLeft = NULL;
- SkDELETE(fBottomRight);
- fBottomRight = NULL;
- fData.reset();
- }
-
- const SkIRect& getBounds() const { return fBounds; }
-
- // Insert data into the QuadTreeNode
- bool insert(Data& data) {
- // Ignore objects which do not belong in this quad tree
- return data.fInnerBounds.intersect(fBounds) && doInsert(data);
- }
-
- // Find all data which appear within a range
- void queryRange(const SkIRect& range, SkTDArray<void*>* dataInRange) const {
- // Automatically abort if the range does not collide with this quad
- if (!SkIRect::Intersects(fBounds, range)) {
- return; // nothing added to the list
- }
+static const int kSplitThreshold = 8;
+static const int kMinDimensions = 128;
- // Check objects at this quad level
- for (int i = 0; i < fData.count(); ++i) {
- if (SkIRect::Intersects(fData[i].fBounds, range)) {
- dataInRange->push(fData[i].fData);
- }
- }
+SkQuadTree::SkQuadTree(const SkIRect& bounds)
+ : fEntryCount(0)
+ , fRoot(NULL) {
+ SkASSERT((bounds.width() * bounds.height()) > 0);
+ fRoot = fNodePool.acquire();
+ fRoot->fBounds = bounds;
+}
- // Terminate here, if there are no children
- if (!hasChildren()) {
- return;
- }
+SkQuadTree::~SkQuadTree() {
+}
- // Otherwise, add the data from the children
- fTopLeft->queryRange(range, dataInRange);
- fTopRight->queryRange(range, dataInRange);
- fBottomLeft->queryRange(range, dataInRange);
- fBottomRight->queryRange(range, dataInRange);
+SkQuadTree::Node* SkQuadTree::pickChild(Node* node,
+ const SkIRect& bounds) const {
+ // is it entirely to the left?
+ int index = 0;
+ if (bounds.fRight < node->fSplitPoint.fX) {
+ // Inside the left side
+ } else if(bounds.fLeft >= node->fSplitPoint.fX) {
+ // Inside the right side
+ index |= 1;
+ } else {
+ // Not inside any children
+ return NULL;
}
-
- int getDepth(int i = 1) const {
- if (hasChildren()) {
- int depthTL = fTopLeft->getDepth(++i);
- int depthTR = fTopRight->getDepth(i);
- int depthBL = fBottomLeft->getDepth(i);
- int depthBR = fBottomRight->getDepth(i);
- return SkTMax(SkTMax(depthTL, depthTR), SkTMax(depthBL, depthBR));
- }
- return i;
+ if (bounds.fBottom < node->fSplitPoint.fY) {
+ // Inside the top side
+ } else if(bounds.fTop >= node->fSplitPoint.fY) {
+ // Inside the bottom side
+ index |= 2;
+ } else {
+ // Not inside any children
+ return NULL;
}
+ return node->fChildren[index];
+}
- void rewindInserts(SkBBoxHierarchyClient* client) {
- for (int i = fData.count() - 1; i >= 0; --i) {
- if (client->shouldRewind(fData[i].fData)) {
- fData.remove(i);
- }
- }
- if (hasChildren()) {
- fTopLeft->rewindInserts(client);
- fTopRight->rewindInserts(client);
- fBottomLeft->rewindInserts(client);
- fBottomRight->rewindInserts(client);
+void SkQuadTree::insert(Node* node, Entry* entry) {
+ // does it belong in a child?
+ if (NULL != node->fChildren[0]) {
+ Node* child = pickChild(node, entry->fBounds);
+ if (NULL != child) {
+ insert(child, entry);
+ } else {
+ node->fEntries.push(entry);
}
+ return;
}
-
-private:
- // create four children which fully divide this quad into four quads of equal area
- void subdivide() {
- if (!hasChildren() && fCanSubdivide) {
- SkIPoint center = SkIPoint::Make(fBounds.centerX(), fBounds.centerY());
- fTopLeft = SkNEW_ARGS(QuadTreeNode, (SkIRect::MakeLTRB(
- fBounds.fLeft, fBounds.fTop, center.fX, center.fY)));
- fTopRight = SkNEW_ARGS(QuadTreeNode, (SkIRect::MakeLTRB(
- center.fX, fBounds.fTop, fBounds.fRight, center.fY)));
- fBottomLeft = SkNEW_ARGS(QuadTreeNode, (SkIRect::MakeLTRB(
- fBounds.fLeft, center.fY, center.fX, fBounds.fBottom)));
- fBottomRight = SkNEW_ARGS(QuadTreeNode, (SkIRect::MakeLTRB(
- center.fX, center.fY, fBounds.fRight, fBounds.fBottom)));
-
- // If any of the data can fit entirely into a subregion, move it down now
- for (int i = fData.count() - 1; i >= 0; --i) {
- // If the data fits entirely into one of the 4 subregions, move that data
- // down to that subregion.
- if (fTopLeft->doInsert(fData[i]) ||
- fTopRight->doInsert(fData[i]) ||
- fBottomLeft->doInsert(fData[i]) ||
- fBottomRight->doInsert(fData[i])) {
- fData.remove(i);
- }
- }
- }
+ // No children yet, add to this node
+ node->fEntries.push(entry);
+ // should I split?
+ if (node->fEntries.getCount() < kSplitThreshold) {
+ return;
}
- bool doInsert(const Data& data) {
- if (!fBounds.contains(data.fInnerBounds)) {
- return false;
- }
+ if ((node->fBounds.width() < kMinDimensions) ||
+ (node->fBounds.height() < kMinDimensions)) {
+ return;
+ }
- if (fData.count() > kQuadTreeNodeCapacity) {
- subdivide();
- }
+ // Build all the children
+ node->fSplitPoint = SkIPoint::Make(node->fBounds.centerX(),
+ node->fBounds.centerY());
+ for(int index=0; index<kChildCount; ++index) {
+ node->fChildren[index] = fNodePool.acquire();
+ }
+ node->fChildren[0]->fBounds = SkIRect::MakeLTRB(
+ node->fBounds.fLeft, node->fBounds.fTop,
+ node->fSplitPoint.fX, node->fSplitPoint.fY);
+ node->fChildren[1]->fBounds = SkIRect::MakeLTRB(
+ node->fSplitPoint.fX, node->fBounds.fTop,
+ node->fBounds.fRight, node->fSplitPoint.fY);
+ node->fChildren[2]->fBounds = SkIRect::MakeLTRB(
+ node->fBounds.fLeft, node->fSplitPoint.fY,
+ node->fSplitPoint.fX, node->fBounds.fBottom);
+ node->fChildren[3]->fBounds = SkIRect::MakeLTRB(
+ node->fSplitPoint.fX, node->fSplitPoint.fY,
+ node->fBounds.fRight, node->fBounds.fBottom);
+ // reinsert all the entries of this node to allow child trickle
+ SkTInternalSList<Entry> entries;
+ entries.pushAll(&node->fEntries);
+ while(!entries.isEmpty()) {
+ insert(node, entries.pop());
+ }
+}
- // If there is space in this quad tree, add the object here
- // If this quadtree can't be subdivided, we have no choice but to add it here
- if ((fData.count() <= kQuadTreeNodeCapacity) || !fCanSubdivide) {
- if (fData.isEmpty()) {
- fData.setReserve(kQuadTreeNodeCapacity);
- }
- fData.push(data);
- } else if (!fTopLeft->doInsert(data) && !fTopRight->doInsert(data) &&
- !fBottomLeft->doInsert(data) && !fBottomRight->doInsert(data)) {
- // Can't be pushed down to children ? keep it here
- fData.push(data);
+void SkQuadTree::search(Node* node, const SkIRect& query,
+ SkTDArray<void*>* results) const {
+ for (Entry* entry = node->fEntries.head(); NULL != entry;
+ entry = entry->getSListNext()) {
+ if (SkIRect::IntersectsNoEmptyCheck(entry->fBounds, query)) {
+ results->push(entry->fData);
}
-
- return true;
}
-
- bool hasChildren() const {
- return (NULL != fTopLeft);
+ if (NULL == node->fChildren[0]) {
+ return;
+ }
+ // fast quadrant test
+ bool left = true;
+ bool right = true;
+ if (query.fRight < node->fSplitPoint.fX) {
+ right = false;
+ } else if(query.fLeft >= node->fSplitPoint.fX) {
+ left = false;
+ }
+ bool top = true;
+ bool bottom = true;
+ if (query.fBottom < node->fSplitPoint.fY) {
+ bottom = false;
+ } else if(query.fTop >= node->fSplitPoint.fY) {
+ top = false;
+ }
+ // search all the active quadrants
+ if (top && left) {
+ search(node->fChildren[0], query, results);
+ }
+ if (top && right) {
+ search(node->fChildren[1], query, results);
+ }
+ if (bottom && left) {
+ search(node->fChildren[2], query, results);
+ }
+ if (bottom && right) {
+ search(node->fChildren[3], query, results);
}
-
- // Arbitrary constant to indicate how many elements can be stored in this quad tree node
- enum { kQuadTreeNodeCapacity = 4 };
-
- // Bounds of this quad tree
- SkIRect fBounds;
-
- // Data in this quad tree node
- SkTDArray<Data> fData;
-
- // Children
- QuadTreeNode* fTopLeft;
- QuadTreeNode* fTopRight;
- QuadTreeNode* fBottomLeft;
- QuadTreeNode* fBottomRight;
-
- // Whether or not this node can have children
- bool fCanSubdivide;
-};
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-SkQuadTree* SkQuadTree::Create(const SkIRect& bounds) {
- return new SkQuadTree(bounds);
}
-SkQuadTree::SkQuadTree(const SkIRect& bounds)
- : fCount(0)
- , fRoot(SkNEW_ARGS(QuadTreeNode, (bounds))) {
- SkASSERT((bounds.width() * bounds.height()) > 0);
+void SkQuadTree::clear(Node* node) {
+ // first clear the entries of this node
+ fEntryPool.releaseAll(&node->fEntries);
+ // recurse into and clear all child nodes
+ for(int index=0; index<kChildCount; ++index) {
+ Node* child = node->fChildren[index];
+ node->fChildren[index] = NULL;
+ if (NULL != child) {
+ clear(child);
+ fNodePool.release(child);
+ }
+ }
}
-SkQuadTree::~SkQuadTree() {
- SkDELETE(fRoot);
+int SkQuadTree::getDepth(Node* node) const {
+ int maxDepth = 0;
+ if (NULL != node->fChildren[0]) {
+ for(int index=0; index<kChildCount; ++index) {
+ maxDepth = SkMax32(maxDepth, getDepth(node->fChildren[index]));
+ }
+ }
+ return maxDepth + 1;
}
void SkQuadTree::insert(void* data, const SkIRect& bounds, bool) {
SkASSERT(false);
return;
}
-
- QuadTreeNode::Data quadTreeData(bounds, data);
- fRoot->insert(quadTreeData);
- ++fCount;
+ Entry* entry = fEntryPool.acquire();
+ entry->fData = data;
+ entry->fBounds = bounds;
+ ++fEntryCount;
+ if (fRoot->fEntries.isEmpty() && (NULL == fRoot->fChildren[0])) {
+ fDeferred.push(entry);
+ } else {
+ insert(fRoot, entry);
+ }
}
void SkQuadTree::search(const SkIRect& query, SkTDArray<void*>* results) {
+ SkASSERT(fDeferred.isEmpty());
SkASSERT(NULL != results);
- fRoot->queryRange(query, results);
+ if (SkIRect::Intersects(fRoot->fBounds, query)) {
+ search(fRoot, query, results);
+ }
}
void SkQuadTree::clear() {
- fCount = 0;
- fRoot->clear();
+ fEntryCount = 0;
+ clear(fRoot);
}
int SkQuadTree::getDepth() const {
- return fRoot->getDepth();
+ return getDepth(fRoot);
}
void SkQuadTree::rewindInserts() {
SkASSERT(fClient);
- fRoot->rewindInserts(fClient);
+ // Currently only supports deferred inserts
+ SkASSERT(fRoot->fEntries.isEmpty() && fRoot->fChildren[0] == NULL);
+ SkTInternalSList<Entry> entries;
+ entries.pushAll(&fDeferred);
+ while(!entries.isEmpty()) {
+ Entry* entry = entries.pop();
+ if (fClient->shouldRewind(entry->fData)) {
+ entry->fData = NULL;
+ fEntryPool.release(entry);
+ --fEntryCount;
+ } else {
+ fDeferred.push(entry);
+ }
+ }
+}
+
+void SkQuadTree::flushDeferredInserts() {
+ while(!fDeferred.isEmpty()) {
+ insert(fRoot, fDeferred.pop());
+ }
}
#include "SkRect.h"
#include "SkTDArray.h"
#include "SkBBoxHierarchy.h"
+#include "SkTInternalSList.h"
+#include "SkTObjectPool.h"
/**
- * An QuadTree implementation. In short, it is a tree containing a hierarchy of bounding rectangles
+ * A QuadTree implementation. In short, it is a tree containing a hierarchy of bounding rectangles
* in which each internal node has exactly four children.
*
* For more details see:
SK_DECLARE_INST_COUNT(SkQuadTree)
/**
- * Create a new QuadTree
+ * Quad tree constructor.
+ * @param bounds The bounding box for the root of the quad tree.
+ * giving the quad tree bounds that fall outside the root
+ * bounds may result in pathological but correct behavior.
*/
- static SkQuadTree* Create(const SkIRect& bounds);
+ SkQuadTree(const SkIRect& bounds);
+
virtual ~SkQuadTree();
/**
/**
* If any inserts have been deferred, this will add them into the tree
*/
- virtual void flushDeferredInserts() SK_OVERRIDE {}
+ virtual void flushDeferredInserts() SK_OVERRIDE;
/**
* Given a query rectangle, populates the passed-in array with the elements it intersects
/**
* This gets the insertion count (rather than the node count)
*/
- virtual int getCount() const SK_OVERRIDE { return fCount; }
+ virtual int getCount() const SK_OVERRIDE { return fEntryCount; }
virtual void rewindInserts() SK_OVERRIDE;
private:
- class QuadTreeNode;
-
- SkQuadTree(const SkIRect& bounds);
-
- // This is the count of data elements (rather than total nodes in the tree)
- int fCount;
-
- QuadTreeNode* fRoot;
+ struct Entry {
+ Entry() : fData(NULL) {}
+ SkIRect fBounds;
+ void* fData;
+ SK_DECLARE_INTERNAL_SLIST_INTERFACE(Entry);
+ };
+
+ static const int kChildCount = 4;
+
+ struct Node {
+ Node() {
+ for (int index=0; index<kChildCount; ++index) {
+ fChildren[index] = NULL;
+ }
+ }
+ SkTInternalSList<Entry> fEntries;
+ SkIRect fBounds;
+ SkIPoint fSplitPoint; // Only valid if the node has children.
+ Node* fChildren[kChildCount];
+ SK_DECLARE_INTERNAL_SLIST_ADAPTER(Node, fChildren[0]);
+ };
+
+ SkTObjectPool<Entry> fEntryPool;
+ SkTObjectPool<Node> fNodePool;
+ int fEntryCount;
+ Node* fRoot;
+ SkTInternalSList<Entry> fDeferred;
+
+ Node* pickChild(Node* node, const SkIRect& bounds) const;
+ void insert(Node* node, Entry* entry);
+ void search(Node* node, const SkIRect& query, SkTDArray<void*>* results) const;
+ void clear(Node* node);
+ int getDepth(Node* node) const;
typedef SkBBoxHierarchy INHERITED;
};
#include "SkQuadTree.h"
SkBBoxHierarchy* SkQuadTreePicture::createBBoxHierarchy() const {
- return SkQuadTree::Create(fBounds);
+ return SkNEW_ARGS(SkQuadTree, (fBounds));
}
--- /dev/null
+/*
+ * 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 SkTInternalSList_DEFINED
+#define SkTInternalSList_DEFINED
+
+#include "SkTInternalLList.h" // for SkPtrWrapper
+
+/**
+ * This macro creates the methods required by the SkTInternalSList class.
+ * It should be instantiated in the private block of the class you want to put
+ * into an SkTInternalSList.
+ * For most use cases you should use SK_DECLARE_INTERNAL_SLIST_INTERFACE and not
+ * this macro. If you care about the field name, or want to re-use an existing
+ * field, then you can use this macro to declare the methods pointing to a
+ * specific field.
+ * Unlike SK_DECLARE_INTERNAL_SLIST_INTERFACE this does not declare the field
+ * itself.
+ * It also makes SkTInternalSList<ClassName> a friend to give it access to the
+ * methods.
+ */
+#define SK_DECLARE_INTERNAL_SLIST_ADAPTER(ClassName, field) \
+ ClassName* getSListNext() { \
+ return this->field; \
+ } \
+ void setSListNext(ClassName* next) { \
+ this->field = next; \
+ } \
+ friend class SkTInternalSList<ClassName>
+
+/**
+ * This macro declares an fSListNext that auto initializes to NULL and then
+ * uses SK_DECLARE_INTERNAL_SLIST_ADAPTER to add the methods needed by
+ * SkTInternalSList.
+ * It should be instantiated in the private block of the class you want to put
+ * into an SkTInternalSList.
+ */
+#define SK_DECLARE_INTERNAL_SLIST_INTERFACE(ClassName) \
+ SK_DECLARE_INTERNAL_SLIST_ADAPTER(ClassName, fSListNext); \
+ SkPtrWrapper<ClassName> fSListNext
+
+/**
+ * An implementation of an intrusive singly linked list.
+ * The type T must have a methods getSListNext and setSListNext that are visible
+ * to the list. The easiest way to do this is with
+ * SK_DECLARE_INTERNAL_SLIST_INTERFACE.
+ * The list does not maintain ownership of any of its elements, or ever delete
+ * them.
+ */
+template<typename T> class SkTInternalSList {
+public:
+ SkTInternalSList() : fHead(NULL), fCount(0) {}
+
+ /**
+ * Push an item onto the head of the list.
+ * This method is *not* thread safe.
+ */
+ void push(T* entry) {
+ SkASSERT(entry->getSListNext() == NULL);
+ entry->setSListNext(fHead);
+ fHead = entry;
+ ++fCount;
+ }
+
+ /**
+ * Takes all the items from another list and pushes them into this list.
+ * No ordering guarantees are made, the other list will be emptied.
+ * This method is *not* thread safe.
+ */
+ void pushAll(SkTInternalSList<T>* other) {
+ if (this->isEmpty()) {
+ this->swap(other);
+ return;
+ }
+ while (!other->isEmpty()) {
+ this->push(other->pop());
+ }
+ }
+
+ /**
+ * Pop an item from the head of the list.
+ * Returns NULL if the list is empty.
+ * This method is *not* thread safe.
+ */
+ T* pop() {
+ if (NULL == fHead) {
+ return NULL;
+ }
+ T* result = fHead;
+ fHead = result->getSListNext();
+ result->setSListNext(NULL);
+ --fCount;
+ return result;
+ }
+
+ T* head() const {
+ return fHead;
+ }
+
+ /**
+ * Returns true if the list has no elements.
+ */
+ bool isEmpty() const {
+ return NULL == fHead;
+ }
+
+ /**
+ * Swaps the contents of this list with another one.
+ * This method is *not* thread safe.
+ */
+ void swap(SkTInternalSList<T>* other) {
+ SkTSwap(fHead, other->fHead);
+ SkTSwap(fCount, other->fCount);
+ }
+
+ /**
+ * Returns the count of elements in the list.
+ */
+ int getCount() const {
+ return fCount;
+ }
+private:
+ T* fHead;
+ int fCount;
+};
+
+
+#endif
--- /dev/null
+/*
+ * 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 SkFreeList_DEFINED
+#define SkFreeList_DEFINED
+
+#include "SkTInternalSList.h"
+
+/**
+ * An implementation of a self growing pool of objects.
+ * It maintains a pool of fully initialized objects. If an attempt is made to
+ * acquire one, and there are none left, it makes some more.
+ * It does not automatically reclaim them, they have to be given back to it.
+ * Constructors will be called on objects allocated by the pool at allocation
+ * time.
+ * All allocated objects will be destroyed and memory will be reclaimed when
+ * the pool is destroyed, so the pool must survive longer than you are using
+ * any item taken from it.
+ */
+template<typename T, int numItemsPerBlock = 4096/sizeof(T)> class SkTObjectPool {
+public:
+ SkTObjectPool() {}
+ ~SkTObjectPool() {
+ while (!fBlocks.isEmpty()) {
+ SkDELETE(fBlocks.pop());
+ }
+ }
+
+ /**
+ * Get an item from the pool.
+ * If the pool has no free items, it will allocate and construct some more.
+ * The returned item is only valid as long as the pool has not been
+ * destroyed, at that point all memory allocated by grow will have been
+ * reclaimed.
+ * This method is *not* thread safe.
+ */
+ T* acquire() {
+ if (fAvailable.isEmpty()) {
+ grow();
+ }
+ return fAvailable.pop();
+ }
+
+ /**
+ * Release an item into the pool.
+ * The item does not have to have come from the pool, but if it did not
+ * it must have a lifetime greater than the pool does.
+ * This method is *not* thread safe.
+ */
+ void release(T* entry) {
+ fAvailable.push(entry);
+ }
+
+ /**
+ * Takes all the items from an SkTInternalSList and adds them back to this
+ * pool. The other list will be left empty.
+ */
+ void releaseAll(SkTInternalSList<T>* other) {
+ fAvailable.pushAll(other);
+ }
+
+ /**
+ * Returns the number of items immediately available without having to
+ * construct any new ones.
+ */
+ int available() const { return fAvailable.getCount(); }
+
+ /**
+ * Returns the number of blocks of items the pool has allocated so far.
+ */
+ int blocks() const { return fBlocks.getCount(); }
+
+private:
+ /**
+ * The type for a new block of entries for the list.
+ */
+ struct Block {
+ T entries[numItemsPerBlock];
+ SK_DECLARE_INTERNAL_SLIST_INTERFACE(Block);
+ };
+ SkTInternalSList<Block> fBlocks;
+ SkTInternalSList<T> fAvailable;
+
+ /**
+ * When the free list runs out of items, this method is called to allocate
+ * a new block of them.
+ * It calls the constructors and then pushes the nodes into the available
+ * list.
+ */
+ void grow() {
+ Block* block = SkNEW(Block);
+ fBlocks.push(block);
+ for(int index = 0; index < numItemsPerBlock; ++index) {
+ fAvailable.push(&block->entries[index]);
+ }
+ }
+
+};
+
+#endif
static const size_t RTREE_MIN_CHILDREN = 6;
static const size_t RTREE_MAX_CHILDREN = 11;
-static const size_t QUADTREE_MIN_CHILDREN = 4;
+static const size_t QUADTREE_MIN_CHILDREN = 0;
static const size_t QUADTREE_MAX_CHILDREN = 0; // No hard limit for quadtree
static const int NUM_RECTS = 200;
REPORTER_ASSERT(reporter, 0 == tree->getCount());
// Then try immediate inserts
- for (int i = 0; i < NUM_RECTS; ++i) {
+ tree->insert(rects[0].data, rects[0].rect);
+ tree->flushDeferredInserts();
+ for (int i = 1; i < NUM_RECTS; ++i) {
tree->insert(rects[i].data, rects[i].rect);
}
run_queries(reporter, rand, rects, *tree);
REPORTER_ASSERT(reporter, 0 == tree->getCount());
// And for good measure try immediate inserts, but in reversed order
- for (int i = NUM_RECTS - 1; i >= 0; --i) {
+ tree->insert(rects[NUM_RECTS - 1].data, rects[NUM_RECTS - 1].rect);
+ tree->flushDeferredInserts();
+ for (int i = NUM_RECTS - 2; i >= 0; --i) {
tree->insert(rects[i].data, rects[i].rect);
}
run_queries(reporter, rand, rects, *tree);
// QuadTree
{
- SkQuadTree* quadtree = SkQuadTree::Create(
- SkIRect::MakeLTRB(-MAX_SIZE, -MAX_SIZE, MAX_SIZE, MAX_SIZE));
+ SkQuadTree* quadtree = SkNEW_ARGS(SkQuadTree, (
+ SkIRect::MakeLTRB(-MAX_SIZE, -MAX_SIZE, MAX_SIZE, MAX_SIZE)));
SkAutoUnref au(quadtree);
tree_test_main(quadtree, QUADTREE_MIN_CHILDREN, QUADTREE_MAX_CHILDREN, reporter);
// QuadTree that orders input rectangles on deferred insert.
- SkQuadTree* unsortedQuadTree = SkQuadTree::Create(
- SkIRect::MakeLTRB(-MAX_SIZE, -MAX_SIZE, MAX_SIZE, MAX_SIZE));
+ SkQuadTree* unsortedQuadTree = SkNEW_ARGS(SkQuadTree, (
+ SkIRect::MakeLTRB(-MAX_SIZE, -MAX_SIZE, MAX_SIZE, MAX_SIZE)));
SkAutoUnref auo(unsortedQuadTree);
tree_test_main(unsortedQuadTree, QUADTREE_MIN_CHILDREN, QUADTREE_MAX_CHILDREN, reporter);
}
--- /dev/null
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkTObjectPool.h"
+#include "SkTObjectPool.h"
+#include "Test.h"
+
+class PoolEntry {
+public:
+private:
+ SK_DECLARE_INTERNAL_SLIST_INTERFACE(PoolEntry);
+};
+
+static const int kNumItemsPerBlock = 3;
+typedef SkTObjectPool<PoolEntry, kNumItemsPerBlock> ObjectPoolType;
+
+static bool verifyPool(skiatest::Reporter* reporter,
+ const ObjectPoolType& pool,
+ const char* stage,
+ int available, int blocks) {
+ if (available != pool.available()) {
+ ERRORF(reporter, "%s - Pool available is %d not %d",
+ stage, pool.available(), available);
+ return false;
+ }
+ if (blocks != pool.blocks()) {
+ ERRORF(reporter, "%s - Pool blocks is %d not %d",
+ stage, pool.blocks(), blocks);
+ return false;
+ }
+ return true;
+}
+
+static const int kNumToAcquire = kNumItemsPerBlock * 5;
+static void testObjectPool(skiatest::Reporter* reporter) {
+ ObjectPoolType pool;
+ SkTInternalSList<PoolEntry> used;
+ verifyPool(reporter, pool, "empty", 0, 0);
+ for (int index = 0; index < kNumToAcquire; ++index) {
+ used.push(pool.acquire());
+ int blocks = (index / kNumItemsPerBlock) + 1;
+ int available = (blocks * kNumItemsPerBlock) - (index + 1);
+ if (!verifyPool(reporter, pool, "acquire", available, blocks)) {
+ return;
+ }
+ }
+ int available = pool.available();
+ int blocks = pool.blocks();
+ for (int index = 0; index < kNumToAcquire / 2; ++index) {
+ pool.release(used.pop());
+ ++available;
+ if (!verifyPool(reporter, pool, "release", available, blocks)) {
+ return;
+ }
+ }
+ available += used.getCount();
+ pool.releaseAll(&used);
+ REPORTER_ASSERT(reporter, used.isEmpty());
+ verifyPool(reporter, pool, "releaseAll", available, blocks);
+}
+
+DEF_TEST(ObjectPool, reporter) {
+ testObjectPool(reporter);
+}
--- /dev/null
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkTInternalSList.h"
+#include "Test.h"
+
+class SListEntry {
+public:
+ SListEntry* next() { return getSListNext(); }
+private:
+ SK_DECLARE_INTERNAL_SLIST_INTERFACE(SListEntry);
+};
+
+static bool verifyEmptyList(skiatest::Reporter* reporter,
+ const SkTInternalSList<SListEntry>& list,
+ const char* stage) {
+
+ if (!list.isEmpty()) {
+ ERRORF(reporter, "%s - List not empty", stage);
+ return false;
+ }
+ if (0 != list.getCount()) {
+ ERRORF(reporter, "%s - List count is not zero, %d instead", stage, list.getCount());
+ return false;
+ }
+ if (NULL != list.head()) {
+ ERRORF(reporter, "%s - List has elements when empty", stage);
+ return false;
+ }
+ return true;
+}
+
+static bool verifyList(skiatest::Reporter* reporter,
+ const SkTInternalSList<SListEntry>& list,
+ const char* stage,
+ SListEntry* start, int count, int step = 1) {
+ SListEntry* next = list.head();
+ if (list.getCount() != count) {
+ ERRORF(reporter, "%s - List was too short, %d instead of %d", stage, list.getCount(), count);
+ return false;
+ }
+ int index = 0;
+ for(SListEntry* value = start; index < count; value += step, ++index) {
+ if (NULL == next) {
+ ERRORF(reporter, "%s - List too short, should be %d", stage, count);
+ return false;
+ }
+ if (next!= value) {
+ ERRORF(reporter, "%s - List entries at index %d of %d don't match", stage, index, count);
+ return false;
+ }
+ next = next->next();
+ }
+ if (NULL != next) {
+ ERRORF(reporter, "%s - List too long, should be %d", stage, count);
+ return false;
+ }
+ return true;
+}
+
+static void testTInternalSList(skiatest::Reporter* reporter) {
+ // Build a test array of data
+ static const int testArraySize = 10;
+ SListEntry testArray[testArraySize];
+ // Basic add remove tests
+ SkTInternalSList<SListEntry> list;
+ verifyEmptyList(reporter, list, "start");
+ // Push values in, testing on the way
+ for (int index = 0; index < testArraySize; ++index) {
+ list.push(&testArray[index]);
+ if (!verifyList(reporter, list, "push", &testArray[index], index+1, -1)) {
+ return;
+ }
+ }
+ // Now remove them again
+ for (int index = testArraySize - 1; index >= 0; --index) {
+ REPORTER_ASSERT(reporter, &testArray[index] == list.pop());
+ if ((index != 0) &&
+ !verifyList(reporter, list, "pop", &testArray[index-1], index, -1)) {
+ return;
+ }
+ }
+ verifyEmptyList(reporter, list, "end");
+ // Move between list tests
+ for (int index = 0; index < testArraySize; ++index) {
+ list.push(&testArray[index]);
+ }
+ verifyList(reporter, list, "swap", &testArray[testArraySize-1], testArraySize, -1);
+ SkTInternalSList<SListEntry> other;
+ // Check swap moves the list over unchanged
+ other.swap(&list);
+ verifyEmptyList(reporter, list, "swap");
+ verifyList(reporter, other, "swap", &testArray[testArraySize-1], testArraySize, -1);
+ // Check pushAll optimizes to a swap when one of the is empty
+ list.pushAll(&other);
+ verifyList(reporter, list, "pushAll-empty", &testArray[testArraySize-1], testArraySize, -1);
+ verifyEmptyList(reporter, other, "pushAll-empty");
+ // Check pushAll when non empty works
+ other.push(list.pop());
+ other.pushAll(&list);
+ verifyEmptyList(reporter, list, "pushAll");
+ verifyList(reporter, other, "pushAll", &testArray[0], testArraySize, 1);
+}
+
+DEF_TEST(SList, reporter) {
+ testTInternalSList(reporter);
+}