Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2100)

Unified Diff: src/core/SkRTree.cpp

Issue 734723002: Prune SkRTree (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: nits Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: src/core/SkRTree.cpp
diff --git a/src/core/SkRTree.cpp b/src/core/SkRTree.cpp
index 93f914276fac6bda3bc3c750ea961376e12a8189..6c7803119df757cc576ef2f4d6543b8b250910d7 100644
--- a/src/core/SkRTree.cpp
+++ b/src/core/SkRTree.cpp
@@ -6,445 +6,167 @@
*/
#include "SkRTree.h"
-#include "SkTSort.h"
-static inline uint32_t get_area(const SkIRect& rect);
-static inline uint32_t get_overlap(const SkIRect& rect1, const SkIRect& rect2);
-static inline uint32_t get_margin(const SkIRect& rect);
-static inline uint32_t get_area_increase(const SkIRect& rect1, SkIRect rect2);
-static inline void join_no_empty_check(const SkIRect& joinWith, SkIRect* out);
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-SkRTree* SkRTree::Create(int minChildren, int maxChildren, SkScalar aspectRatio,
- bool sortWhenBulkLoading) {
- if (minChildren < maxChildren && (maxChildren + 1) / 2 >= minChildren &&
- minChildren > 0 && maxChildren < static_cast<int>(SK_MaxU16)) {
- return new SkRTree(minChildren, maxChildren, aspectRatio, sortWhenBulkLoading);
- }
- return NULL;
-}
-
-SkRTree::SkRTree(int minChildren, int maxChildren, SkScalar aspectRatio,
- bool sortWhenBulkLoading)
- : fMinChildren(minChildren)
- , fMaxChildren(maxChildren)
- , fNodeSize(sizeof(Node) + sizeof(Branch) * maxChildren)
- , fCount(0)
- , fNodes(fNodeSize * 256)
- , fAspectRatio(aspectRatio)
- , fSortWhenBulkLoading(sortWhenBulkLoading) {
- SkASSERT(minChildren < maxChildren && minChildren > 0 && maxChildren <
- static_cast<int>(SK_MaxU16));
- SkASSERT((maxChildren + 1) / 2 >= minChildren);
- this->validate();
-}
-
-SkRTree::~SkRTree() {
- this->clear();
-}
+SkRTree::SkRTree(SkScalar aspectRatio) : fCount(0), fAspectRatio(aspectRatio) {}
void SkRTree::insert(SkAutoTMalloc<SkRect>* boundsArray, int N) {
- SkASSERT(this->isEmpty());
- this->validate();
+ SkASSERT(0 == fCount);
- SkTDArray<Branch> deferred;
- deferred.setReserve(N);
+ SkTDArray<Branch> branches;
+ branches.setReserve(N);
for (int i = 0; i < N; i++) {
- SkIRect bounds;
- (*boundsArray)[i].roundOut(&bounds);
+ const SkRect& bounds = (*boundsArray)[i];
if (bounds.isEmpty()) {
continue;
}
- Branch newBranch;
- newBranch.fBounds = bounds;
- newBranch.fChild.opIndex = i;
-
- deferred.push(newBranch);
+ Branch* b = branches.push();
+ b->fBounds = bounds;
+ b->fOpIndex = i;
}
- fCount = deferred.count();
+ fCount = branches.count();
if (fCount) {
if (1 == fCount) {
- fRoot.fChild.subtree = this->allocateNode(0);
- fRoot.fChild.subtree->fNumChildren = 0;
- this->insert(fRoot.fChild.subtree, &deferred[0]);
- fRoot.fBounds = deferred[0].fBounds;
+ fNodes.setReserve(1);
+ Node* n = this->allocateNodeAtLevel(0);
+ n->fNumChildren = 1;
+ n->fChildren[0] = branches[0];
+ fRoot.fSubtree = n;
+ fRoot.fBounds = branches[0].fBounds;
} else {
- fRoot = this->bulkLoad(&deferred);
+ fNodes.setReserve(CountNodes(fCount, fAspectRatio));
+ fRoot = this->bulkLoad(&branches);
}
}
-
- this->validate();
}
-void SkRTree::search(const SkRect& fquery, SkTDArray<unsigned>* results) const {
- SkIRect query;
- fquery.roundOut(&query);
- this->validate();
- if (!this->isEmpty() && SkIRect::IntersectsNoEmptyCheck(fRoot.fBounds, query)) {
- this->search(fRoot.fChild.subtree, query, results);
- }
- this->validate();
-}
-
-void SkRTree::clear() {
- this->validate();
- fNodes.reset();
- fCount = 0;
- this->validate();
-}
-
-SkRTree::Node* SkRTree::allocateNode(uint16_t level) {
- Node* out = static_cast<Node*>(fNodes.allocThrow(fNodeSize));
+SkRTree::Node* SkRTree::allocateNodeAtLevel(uint16_t level) {
+ SkDEBUGCODE(Node* p = fNodes.begin());
+ Node* out = fNodes.push();
+ SkASSERT(fNodes.begin() == p); // If this fails, we didn't setReserve() enough.
out->fNumChildren = 0;
out->fLevel = level;
return out;
}
-SkRTree::Branch* SkRTree::insert(Node* root, Branch* branch, uint16_t level) {
- Branch* toInsert = branch;
- if (root->fLevel != level) {
- int childIndex = this->chooseSubtree(root, branch);
- toInsert = this->insert(root->child(childIndex)->fChild.subtree, branch, level);
- root->child(childIndex)->fBounds = this->computeBounds(
- root->child(childIndex)->fChild.subtree);
+// This function parallels bulkLoad, but just counts how many nodes bulkLoad would allocate.
+int SkRTree::CountNodes(int branches, SkScalar aspectRatio) {
+ if (branches == 1) {
+ return 1;
}
- if (toInsert) {
- if (root->fNumChildren == fMaxChildren) {
- // handle overflow by splitting. TODO: opportunistic reinsertion
-
- // decide on a distribution to divide with
- Node* newSibling = this->allocateNode(root->fLevel);
- Branch* toDivide = SkNEW_ARRAY(Branch, fMaxChildren + 1);
- for (int i = 0; i < fMaxChildren; ++i) {
- toDivide[i] = *root->child(i);
- }
- toDivide[fMaxChildren] = *toInsert;
- int splitIndex = this->distributeChildren(toDivide);
-
- // divide up the branches
- root->fNumChildren = splitIndex;
- newSibling->fNumChildren = fMaxChildren + 1 - splitIndex;
- for (int i = 0; i < splitIndex; ++i) {
- *root->child(i) = toDivide[i];
- }
- for (int i = splitIndex; i < fMaxChildren + 1; ++i) {
- *newSibling->child(i - splitIndex) = toDivide[i];
- }
- SkDELETE_ARRAY(toDivide);
-
- // pass the new sibling branch up to the parent
- branch->fChild.subtree = newSibling;
- branch->fBounds = this->computeBounds(newSibling);
- return branch;
+ int numBranches = branches / kMaxChildren;
+ int remainder = branches % kMaxChildren;
+ if (remainder > 0) {
+ numBranches++;
+ if (remainder >= kMinChildren) {
+ remainder = 0;
} else {
- *root->child(root->fNumChildren) = *toInsert;
- ++root->fNumChildren;
- return NULL;
+ remainder = kMinChildren - remainder;
}
}
- return NULL;
-}
-
-int SkRTree::chooseSubtree(Node* root, Branch* branch) {
- SkASSERT(!root->isLeaf());
- if (1 < root->fLevel) {
- // root's child pointers do not point to leaves, so minimize area increase
- int32_t minAreaIncrease = SK_MaxS32;
- int32_t minArea = SK_MaxS32;
- int32_t bestSubtree = -1;
- for (int i = 0; i < root->fNumChildren; ++i) {
- const SkIRect& subtreeBounds = root->child(i)->fBounds;
- int32_t areaIncrease = get_area_increase(subtreeBounds, branch->fBounds);
- // break ties in favor of subtree with smallest area
- if (areaIncrease < minAreaIncrease || (areaIncrease == minAreaIncrease &&
- static_cast<int32_t>(get_area(subtreeBounds)) < minArea)) {
- minAreaIncrease = areaIncrease;
- minArea = get_area(subtreeBounds);
- bestSubtree = i;
- }
- }
- SkASSERT(-1 != bestSubtree);
- return bestSubtree;
- } else if (1 == root->fLevel) {
- // root's child pointers do point to leaves, so minimize overlap increase
- int32_t minOverlapIncrease = SK_MaxS32;
- int32_t minAreaIncrease = SK_MaxS32;
- int32_t bestSubtree = -1;
- for (int32_t i = 0; i < root->fNumChildren; ++i) {
- const SkIRect& subtreeBounds = root->child(i)->fBounds;
- SkIRect expandedBounds = subtreeBounds;
- join_no_empty_check(branch->fBounds, &expandedBounds);
- int32_t overlap = 0;
- for (int32_t j = 0; j < root->fNumChildren; ++j) {
- if (j == i) continue;
- // Note: this would be more correct if we subtracted the original pre-expanded
- // overlap, but computing overlaps is expensive and omitting it doesn't seem to
- // hurt query performance. See get_overlap_increase()
- overlap += get_overlap(expandedBounds, root->child(j)->fBounds);
- }
- // break ties with lowest area increase
- if (overlap < minOverlapIncrease || (overlap == minOverlapIncrease &&
- static_cast<int32_t>(get_area_increase(branch->fBounds, subtreeBounds)) <
- minAreaIncrease)) {
- minOverlapIncrease = overlap;
- minAreaIncrease = get_area_increase(branch->fBounds, subtreeBounds);
- bestSubtree = i;
- }
- }
- return bestSubtree;
- } else {
- SkASSERT(false);
- return 0;
- }
-}
-
-SkIRect SkRTree::computeBounds(Node* n) {
- SkIRect r = n->child(0)->fBounds;
- for (int i = 1; i < n->fNumChildren; ++i) {
- join_no_empty_check(n->child(i)->fBounds, &r);
- }
- return r;
-}
-
-int SkRTree::distributeChildren(Branch* children) {
- // We have two sides to sort by on each of two axes:
- const static SortSide sorts[2][2] = {
- {&SkIRect::fLeft, &SkIRect::fRight},
- {&SkIRect::fTop, &SkIRect::fBottom}
- };
-
- // We want to choose an axis to split on, then a distribution along that axis; we'll need
- // three pieces of info: the split axis, the side to sort by on that axis, and the index
- // to split the sorted array on.
- int32_t sortSide = -1;
- int32_t k = -1;
- int32_t axis = -1;
- int32_t bestS = SK_MaxS32;
-
- // Evaluate each axis, we want the min summed margin-value (s) over all distributions
- for (int i = 0; i < 2; ++i) {
- int32_t minOverlap = SK_MaxS32;
- int32_t minArea = SK_MaxS32;
- int32_t axisBestK = 0;
- int32_t axisBestSide = 0;
- int32_t s = 0;
-
- // Evaluate each sort
- for (int j = 0; j < 2; ++j) {
- SkTQSort(children, children + fMaxChildren, RectLessThan(sorts[i][j]));
-
- // Evaluate each split index
- for (int32_t k = 1; k <= fMaxChildren - 2 * fMinChildren + 2; ++k) {
- SkIRect r1 = children[0].fBounds;
- SkIRect r2 = children[fMinChildren + k - 1].fBounds;
- for (int32_t l = 1; l < fMinChildren - 1 + k; ++l) {
- join_no_empty_check(children[l].fBounds, &r1);
- }
- for (int32_t l = fMinChildren + k; l < fMaxChildren + 1; ++l) {
- join_no_empty_check(children[l].fBounds, &r2);
- }
-
- int32_t area = get_area(r1) + get_area(r2);
- int32_t overlap = get_overlap(r1, r2);
- s += get_margin(r1) + get_margin(r2);
-
- if (overlap < minOverlap || (overlap == minOverlap && area < minArea)) {
- minOverlap = overlap;
- minArea = area;
- axisBestSide = j;
- axisBestK = k;
+ int numStrips = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(numBranches) / aspectRatio));
+ int numTiles = SkScalarCeilToInt(SkIntToScalar(numBranches) / SkIntToScalar(numStrips));
+ int currentBranch = 0;
+ int nodes = 0;
+ for (int i = 0; i < numStrips; ++i) {
+ for (int j = 0; j < numTiles && currentBranch < branches; ++j) {
+ int incrementBy = kMaxChildren;
+ if (remainder != 0) {
+ if (remainder <= kMaxChildren - kMinChildren) {
+ incrementBy -= remainder;
+ remainder = 0;
+ } else {
+ incrementBy = kMinChildren;
+ remainder -= kMaxChildren - kMinChildren;
}
}
- }
-
- if (s < bestS) {
- bestS = s;
- axis = i;
- sortSide = axisBestSide;
- k = axisBestK;
- }
- }
-
- // replicate the sort of the winning distribution, (we can skip this if the last
- // sort ended up being best)
- if (!(axis == 1 && sortSide == 1)) {
- SkTQSort(children, children + fMaxChildren, RectLessThan(sorts[axis][sortSide]));
- }
-
- return fMinChildren - 1 + k;
-}
-
-void SkRTree::search(Node* root, const SkIRect query, SkTDArray<unsigned>* results) const {
- for (int i = 0; i < root->fNumChildren; ++i) {
- if (SkIRect::IntersectsNoEmptyCheck(root->child(i)->fBounds, query)) {
- if (root->isLeaf()) {
- results->push(root->child(i)->fChild.opIndex);
- } else {
- this->search(root->child(i)->fChild.subtree, query, results);
+ nodes++;
+ currentBranch++;
+ for (int k = 1; k < incrementBy && currentBranch < branches; ++k) {
+ currentBranch++;
}
}
}
+ return nodes + CountNodes(nodes, aspectRatio);
}
SkRTree::Branch SkRTree::bulkLoad(SkTDArray<Branch>* branches, int level) {
- if (branches->count() == 1) {
- // Only one branch: it will be the root
- Branch out = (*branches)[0];
- branches->rewind();
- return out;
- } else {
- // We sort the whole list by y coordinates, if we are told to do so.
- //
- // We expect Webkit / Blink to give us a reasonable x,y order.
- // Avoiding this call resulted in a 17% win for recording with
- // negligible difference in playback speed.
- if (fSortWhenBulkLoading) {
- SkTQSort(branches->begin(), branches->end() - 1, RectLessY());
- }
-
- int numBranches = branches->count() / fMaxChildren;
- int remainder = branches->count() % fMaxChildren;
- int newBranches = 0;
-
- if (0 != remainder) {
- ++numBranches;
- // If the remainder isn't enough to fill a node, we'll need to add fewer nodes to
- // some other branches to make up for it
- if (remainder >= fMinChildren) {
- remainder = 0;
- } else {
- remainder = fMinChildren - remainder;
- }
+ if (branches->count() == 1) { // Only one branch. It will be the root.
+ return (*branches)[0];
+ }
+
+ // We might sort our branches here, but we expect Blink gives us a reasonable x,y order.
+ // Skipping a call to sort (in Y) here resulted in a 17% win for recording with negligible
+ // difference in playback speed.
+ int numBranches = branches->count() / kMaxChildren;
+ int remainder = branches->count() % kMaxChildren;
+ int newBranches = 0;
+
+ if (remainder > 0) {
+ ++numBranches;
+ // If the remainder isn't enough to fill a node, we'll add fewer nodes to other branches.
+ if (remainder >= kMinChildren) {
+ remainder = 0;
+ } else {
+ remainder = kMinChildren - remainder;
}
+ }
- int numStrips = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(numBranches) *
- SkScalarInvert(fAspectRatio)));
- int numTiles = SkScalarCeilToInt(SkIntToScalar(numBranches) /
- SkIntToScalar(numStrips));
- int currentBranch = 0;
-
- for (int i = 0; i < numStrips; ++i) {
- // Once again, if we are told to do so, we sort by x.
- if (fSortWhenBulkLoading) {
- int begin = currentBranch;
- int end = currentBranch + numTiles * fMaxChildren - SkMin32(remainder,
- (fMaxChildren - fMinChildren) * numTiles);
- if (end > branches->count()) {
- end = branches->count();
+ int numStrips = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(numBranches) / fAspectRatio));
+ int numTiles = SkScalarCeilToInt(SkIntToScalar(numBranches) / SkIntToScalar(numStrips));
+ int currentBranch = 0;
+
+ for (int i = 0; i < numStrips; ++i) {
+ // Might be worth sorting by X here too.
+ for (int j = 0; j < numTiles && currentBranch < branches->count(); ++j) {
+ int incrementBy = kMaxChildren;
+ if (remainder != 0) {
+ // if need be, omit some nodes to make up for remainder
+ if (remainder <= kMaxChildren - kMinChildren) {
+ incrementBy -= remainder;
+ remainder = 0;
+ } else {
+ incrementBy = kMinChildren;
+ remainder -= kMaxChildren - kMinChildren;
}
-
- // Now we sort horizontal strips of rectangles by their x coords
- SkTQSort(branches->begin() + begin, branches->begin() + end - 1, RectLessX());
}
-
- for (int j = 0; j < numTiles && currentBranch < branches->count(); ++j) {
- int incrementBy = fMaxChildren;
- if (remainder != 0) {
- // if need be, omit some nodes to make up for remainder
- if (remainder <= fMaxChildren - fMinChildren) {
- incrementBy -= remainder;
- remainder = 0;
- } else {
- incrementBy = fMinChildren;
- remainder -= fMaxChildren - fMinChildren;
- }
- }
- Node* n = allocateNode(level);
- n->fNumChildren = 1;
- *n->child(0) = (*branches)[currentBranch];
- Branch b;
- b.fBounds = (*branches)[currentBranch].fBounds;
- b.fChild.subtree = n;
+ Node* n = allocateNodeAtLevel(level);
+ n->fNumChildren = 1;
+ n->fChildren[0] = (*branches)[currentBranch];
+ Branch b;
+ b.fBounds = (*branches)[currentBranch].fBounds;
+ b.fSubtree = n;
+ ++currentBranch;
+ for (int k = 1; k < incrementBy && currentBranch < branches->count(); ++k) {
+ b.fBounds.join((*branches)[currentBranch].fBounds);
+ n->fChildren[k] = (*branches)[currentBranch];
+ ++n->fNumChildren;
++currentBranch;
- for (int k = 1; k < incrementBy && currentBranch < branches->count(); ++k) {
- b.fBounds.join((*branches)[currentBranch].fBounds);
- *n->child(k) = (*branches)[currentBranch];
- ++n->fNumChildren;
- ++currentBranch;
- }
- (*branches)[newBranches] = b;
- ++newBranches;
}
+ (*branches)[newBranches] = b;
+ ++newBranches;
}
- branches->setCount(newBranches);
- return this->bulkLoad(branches, level + 1);
}
+ branches->setCount(newBranches);
+ return this->bulkLoad(branches, level + 1);
}
-void SkRTree::validate() const {
-#ifdef SK_DEBUG
- if (this->isEmpty()) {
- return;
+void SkRTree::search(const SkRect& query, SkTDArray<unsigned>* results) const {
+ if (fCount > 0 && SkRect::Intersects(fRoot.fBounds, query)) {
+ this->search(fRoot.fSubtree, query, results);
}
- SkASSERT(fCount == this->validateSubtree(fRoot.fChild.subtree, fRoot.fBounds, true));
-#endif
}
-int SkRTree::validateSubtree(Node* root, SkIRect bounds, bool isRoot) const {
- // make sure the pointer is pointing to a valid place
- SkASSERT(fNodes.contains(static_cast<void*>(root)));
-
- if (isRoot) {
- // If the root of this subtree is the overall root, we have looser standards:
- if (root->isLeaf()) {
- SkASSERT(root->fNumChildren >= 1 && root->fNumChildren <= fMaxChildren);
- } else {
- SkASSERT(root->fNumChildren >= 2 && root->fNumChildren <= fMaxChildren);
- }
- } else {
- SkASSERT(root->fNumChildren >= fMinChildren && root->fNumChildren <= fMaxChildren);
- }
-
- for (int i = 0; i < root->fNumChildren; ++i) {
- SkASSERT(bounds.contains(root->child(i)->fBounds));
- }
-
- if (root->isLeaf()) {
- SkASSERT(0 == root->fLevel);
- return root->fNumChildren;
- } else {
- int childCount = 0;
- for (int i = 0; i < root->fNumChildren; ++i) {
- SkASSERT(root->child(i)->fChild.subtree->fLevel == root->fLevel - 1);
- childCount += this->validateSubtree(root->child(i)->fChild.subtree,
- root->child(i)->fBounds);
+void SkRTree::search(Node* node, const SkRect& query, SkTDArray<unsigned>* results) const {
+ for (int i = 0; i < node->fNumChildren; ++i) {
+ if (SkRect::Intersects(node->fChildren[i].fBounds, query)) {
+ if (0 == node->fLevel) {
+ results->push(node->fChildren[i].fOpIndex);
+ } else {
+ this->search(node->fChildren[i].fSubtree, query, results);
+ }
}
- return childCount;
}
}
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-static inline uint32_t get_area(const SkIRect& rect) {
- return rect.width() * rect.height();
-}
-
-static inline uint32_t get_overlap(const SkIRect& rect1, const SkIRect& rect2) {
- // I suspect there's a more efficient way of computing this...
- return SkMax32(0, SkMin32(rect1.fRight, rect2.fRight) - SkMax32(rect1.fLeft, rect2.fLeft)) *
- SkMax32(0, SkMin32(rect1.fBottom, rect2.fBottom) - SkMax32(rect1.fTop, rect2.fTop));
-}
-
-// Get the margin (aka perimeter)
-static inline uint32_t get_margin(const SkIRect& rect) {
- return 2 * (rect.width() + rect.height());
-}
-
-static inline uint32_t get_area_increase(const SkIRect& rect1, SkIRect rect2) {
- join_no_empty_check(rect1, &rect2);
- return get_area(rect2) - get_area(rect1);
-}
-
-// Expand 'out' to include 'joinWith'
-static inline void join_no_empty_check(const SkIRect& joinWith, SkIRect* out) {
- // since we check for empty bounds on insert, we know we'll never have empty rects
- // and we can save the empty check that SkIRect::join requires
- if (joinWith.fLeft < out->fLeft) { out->fLeft = joinWith.fLeft; }
- if (joinWith.fTop < out->fTop) { out->fTop = joinWith.fTop; }
- if (joinWith.fRight > out->fRight) { out->fRight = joinWith.fRight; }
- if (joinWith.fBottom > out->fBottom) { out->fBottom = joinWith.fBottom; }
-}
« src/core/SkRTree.h ('K') | « src/core/SkRTree.h ('k') | tests/RTreeTest.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698