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

Side by Side Diff: src/core/SkQuadTree.cpp

Issue 196383026: Slightly faster quadtree searching (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Use U8CPU for intersection bitmask Created 6 years, 9 months 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 unified diff | Download patch
« no previous file with comments | « src/core/SkQuadTree.h ('k') | src/core/SkTObjectPool.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright 2014 Google Inc. 2 * Copyright 2014 Google Inc.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license that can be 4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file. 5 * found in the LICENSE file.
6 */ 6 */
7 7
8 #include "SkQuadTree.h" 8 #include "SkQuadTree.h"
9 #include "SkTSort.h" 9 #include "SkTSort.h"
10 #include <stdio.h> 10 #include <stdio.h>
11 #include <vector> 11 #include <vector>
12 12
13 static const int kSplitThreshold = 8; 13 static const int kSplitThreshold = 8;
14 static const int kMinDimensions = 128; 14
15 enum {
16 kTopLeft,
17 kTopRight,
18 kBottomLeft,
19 kBottomRight,
20 };
21 enum {
22 kTopLeft_Bit = 1 << kTopLeft,
23 kTopRight_Bit = 1 << kTopRight,
24 kBottomLeft_Bit = 1 << kBottomLeft,
25 kBottomRight_Bit = 1 << kBottomRight,
26 };
27 enum {
28 kMaskLeft = kTopLeft_Bit | kBottomLeft_Bit,
29 kMaskRight = kTopRight_Bit | kBottomRight_Bit,
30 kMaskTop = kTopLeft_Bit | kTopRight_Bit,
31 kMaskBottom = kBottomLeft_Bit | kBottomRight_Bit,
32 };
33
34 static U8CPU child_intersect(const SkIRect& query, const SkIPoint& split) {
35 // fast quadrant test
36 U8CPU intersect = 0xf;
37 if (query.fRight < split.fX) {
38 intersect &= ~kMaskRight;
39 } else if(query.fLeft >= split.fX) {
40 intersect &= ~kMaskLeft;
41 }
42 if (query.fBottom < split.fY) {
43 intersect &= ~kMaskBottom;
44 } else if(query.fTop >= split.fY) {
45 intersect &= ~kMaskTop;
46 }
47 return intersect;
48 }
15 49
16 SkQuadTree::SkQuadTree(const SkIRect& bounds) 50 SkQuadTree::SkQuadTree(const SkIRect& bounds)
17 : fEntryCount(0) 51 : fRoot(NULL) {
18 , fRoot(NULL) {
19 SkASSERT((bounds.width() * bounds.height()) > 0); 52 SkASSERT((bounds.width() * bounds.height()) > 0);
20 fRoot = fNodePool.acquire(); 53 fRootBounds = bounds;
21 fRoot->fBounds = bounds;
22 } 54 }
23 55
24 SkQuadTree::~SkQuadTree() { 56 SkQuadTree::~SkQuadTree() {
25 } 57 }
26 58
27 SkQuadTree::Node* SkQuadTree::pickChild(Node* node,
28 const SkIRect& bounds) const {
29 // is it entirely to the left?
30 int index = 0;
31 if (bounds.fRight < node->fSplitPoint.fX) {
32 // Inside the left side
33 } else if(bounds.fLeft >= node->fSplitPoint.fX) {
34 // Inside the right side
35 index |= 1;
36 } else {
37 // Not inside any children
38 return NULL;
39 }
40 if (bounds.fBottom < node->fSplitPoint.fY) {
41 // Inside the top side
42 } else if(bounds.fTop >= node->fSplitPoint.fY) {
43 // Inside the bottom side
44 index |= 2;
45 } else {
46 // Not inside any children
47 return NULL;
48 }
49 return node->fChildren[index];
50 }
51
52 void SkQuadTree::insert(Node* node, Entry* entry) { 59 void SkQuadTree::insert(Node* node, Entry* entry) {
53 // does it belong in a child? 60 // does it belong in a child?
54 if (NULL != node->fChildren[0]) { 61 if (NULL != node->fChildren[0]) {
55 Node* child = pickChild(node, entry->fBounds); 62 switch(child_intersect(entry->fBounds, node->fSplitPoint)) {
56 if (NULL != child) { 63 case kTopLeft_Bit:
57 insert(child, entry); 64 this->insert(node->fChildren[kTopLeft], entry);
58 } else { 65 return;
59 node->fEntries.push(entry); 66 case kTopRight_Bit:
67 this->insert(node->fChildren[kTopRight], entry);
68 return;
69 case kBottomLeft_Bit:
70 this->insert(node->fChildren[kBottomLeft], entry);
71 return;
72 case kBottomRight_Bit:
73 this->insert(node->fChildren[kBottomRight], entry);
74 return;
75 default:
76 node->fEntries.push(entry);
77 return;
60 } 78 }
61 return;
62 } 79 }
63 // No children yet, add to this node 80 // No children yet, add to this node
64 node->fEntries.push(entry); 81 node->fEntries.push(entry);
65 // should I split? 82 // should I split?
66 if (node->fEntries.getCount() < kSplitThreshold) { 83 if (node->fEntries.getCount() > kSplitThreshold) {
67 return; 84 this->split(node);
68 } 85 }
86 }
69 87
70 if ((node->fBounds.width() < kMinDimensions) || 88 void SkQuadTree::split(Node* node) {
71 (node->fBounds.height() < kMinDimensions)) {
72 return;
73 }
74
75 // Build all the children 89 // Build all the children
76 node->fSplitPoint = SkIPoint::Make(node->fBounds.centerX(), 90 node->fSplitPoint = SkIPoint::Make(node->fBounds.centerX(),
77 node->fBounds.centerY()); 91 node->fBounds.centerY());
78 for(int index=0; index<kChildCount; ++index) { 92 for(int index=0; index<kChildCount; ++index) {
79 node->fChildren[index] = fNodePool.acquire(); 93 node->fChildren[index] = fNodePool.acquire();
80 } 94 }
81 node->fChildren[0]->fBounds = SkIRect::MakeLTRB( 95 node->fChildren[0]->fBounds = SkIRect::MakeLTRB(
82 node->fBounds.fLeft, node->fBounds.fTop, 96 node->fBounds.fLeft, node->fBounds.fTop,
83 node->fSplitPoint.fX, node->fSplitPoint.fY); 97 node->fSplitPoint.fX, node->fSplitPoint.fY);
84 node->fChildren[1]->fBounds = SkIRect::MakeLTRB( 98 node->fChildren[1]->fBounds = SkIRect::MakeLTRB(
85 node->fSplitPoint.fX, node->fBounds.fTop, 99 node->fSplitPoint.fX, node->fBounds.fTop,
86 node->fBounds.fRight, node->fSplitPoint.fY); 100 node->fBounds.fRight, node->fSplitPoint.fY);
87 node->fChildren[2]->fBounds = SkIRect::MakeLTRB( 101 node->fChildren[2]->fBounds = SkIRect::MakeLTRB(
88 node->fBounds.fLeft, node->fSplitPoint.fY, 102 node->fBounds.fLeft, node->fSplitPoint.fY,
89 node->fSplitPoint.fX, node->fBounds.fBottom); 103 node->fSplitPoint.fX, node->fBounds.fBottom);
90 node->fChildren[3]->fBounds = SkIRect::MakeLTRB( 104 node->fChildren[3]->fBounds = SkIRect::MakeLTRB(
91 node->fSplitPoint.fX, node->fSplitPoint.fY, 105 node->fSplitPoint.fX, node->fSplitPoint.fY,
92 node->fBounds.fRight, node->fBounds.fBottom); 106 node->fBounds.fRight, node->fBounds.fBottom);
93 // reinsert all the entries of this node to allow child trickle 107 // reinsert all the entries of this node to allow child trickle
94 SkTInternalSList<Entry> entries; 108 SkTInternalSList<Entry> entries;
95 entries.pushAll(&node->fEntries); 109 entries.pushAll(&node->fEntries);
96 while(!entries.isEmpty()) { 110 while(!entries.isEmpty()) {
97 insert(node, entries.pop()); 111 this->insert(node, entries.pop());
98 } 112 }
99 } 113 }
100 114
101 void SkQuadTree::search(Node* node, const SkIRect& query, 115 void SkQuadTree::search(Node* node, const SkIRect& query,
102 SkTDArray<void*>* results) const { 116 SkTDArray<void*>* results) const {
103 for (Entry* entry = node->fEntries.head(); NULL != entry; 117 for (Entry* entry = node->fEntries.head(); NULL != entry;
104 entry = entry->getSListNext()) { 118 entry = entry->getSListNext()) {
105 if (SkIRect::IntersectsNoEmptyCheck(entry->fBounds, query)) { 119 if (SkIRect::IntersectsNoEmptyCheck(entry->fBounds, query)) {
106 results->push(entry->fData); 120 results->push(entry->fData);
107 } 121 }
108 } 122 }
109 if (NULL == node->fChildren[0]) { 123 if (NULL == node->fChildren[0]) {
110 return; 124 return;
111 } 125 }
112 // fast quadrant test 126 U8CPU intersect = child_intersect(query, node->fSplitPoint);
113 bool left = true; 127 for(int index=0; index<kChildCount; ++index) {
114 bool right = true; 128 if (intersect & (1 << index)) {
115 if (query.fRight < node->fSplitPoint.fX) { 129 this->search(node->fChildren[index], query, results);
116 right = false; 130 }
117 } else if(query.fLeft >= node->fSplitPoint.fX) {
118 left = false;
119 }
120 bool top = true;
121 bool bottom = true;
122 if (query.fBottom < node->fSplitPoint.fY) {
123 bottom = false;
124 } else if(query.fTop >= node->fSplitPoint.fY) {
125 top = false;
126 }
127 // search all the active quadrants
128 if (top && left) {
129 search(node->fChildren[0], query, results);
130 }
131 if (top && right) {
132 search(node->fChildren[1], query, results);
133 }
134 if (bottom && left) {
135 search(node->fChildren[2], query, results);
136 }
137 if (bottom && right) {
138 search(node->fChildren[3], query, results);
139 } 131 }
140 } 132 }
141 133
142 void SkQuadTree::clear(Node* node) { 134 void SkQuadTree::clear(Node* node) {
143 // first clear the entries of this node 135 // first clear the entries of this node
144 fEntryPool.releaseAll(&node->fEntries); 136 fEntryPool.releaseAll(&node->fEntries);
145 // recurse into and clear all child nodes 137 // recurse into and clear all child nodes
146 for(int index=0; index<kChildCount; ++index) { 138 for(int index=0; index<kChildCount; ++index) {
147 Node* child = node->fChildren[index]; 139 Node* child = node->fChildren[index];
148 node->fChildren[index] = NULL; 140 node->fChildren[index] = NULL;
149 if (NULL != child) { 141 if (NULL != child) {
150 clear(child); 142 this->clear(child);
151 fNodePool.release(child); 143 fNodePool.release(child);
152 } 144 }
153 } 145 }
154 } 146 }
155 147
156 int SkQuadTree::getDepth(Node* node) const { 148 int SkQuadTree::getDepth(Node* node) const {
157 int maxDepth = 0; 149 int maxDepth = 0;
158 if (NULL != node->fChildren[0]) { 150 if (NULL != node) {
159 for(int index=0; index<kChildCount; ++index) { 151 for(int index=0; index<kChildCount; ++index) {
160 maxDepth = SkMax32(maxDepth, getDepth(node->fChildren[index])); 152 maxDepth = SkMax32(maxDepth, getDepth(node->fChildren[index]));
161 } 153 }
162 } 154 }
163 return maxDepth + 1; 155 return maxDepth + 1;
164 } 156 }
165 157
166 void SkQuadTree::insert(void* data, const SkIRect& bounds, bool) { 158 void SkQuadTree::insert(void* data, const SkIRect& bounds, bool) {
167 if (bounds.isEmpty()) { 159 if (bounds.isEmpty()) {
168 SkASSERT(false); 160 SkASSERT(false);
169 return; 161 return;
170 } 162 }
171 Entry* entry = fEntryPool.acquire(); 163 Entry* entry = fEntryPool.acquire();
172 entry->fData = data; 164 entry->fData = data;
173 entry->fBounds = bounds; 165 entry->fBounds = bounds;
174 ++fEntryCount; 166 if (NULL == fRoot) {
175 if (fRoot->fEntries.isEmpty() && (NULL == fRoot->fChildren[0])) {
176 fDeferred.push(entry); 167 fDeferred.push(entry);
177 } else { 168 } else {
178 insert(fRoot, entry); 169 this->insert(fRoot, entry);
179 } 170 }
180 } 171 }
181 172
182 void SkQuadTree::search(const SkIRect& query, SkTDArray<void*>* results) { 173 void SkQuadTree::search(const SkIRect& query, SkTDArray<void*>* results) {
183 SkASSERT(fDeferred.isEmpty()); 174 SkASSERT(NULL != fRoot);
184 SkASSERT(NULL != results); 175 SkASSERT(NULL != results);
185 if (SkIRect::Intersects(fRoot->fBounds, query)) { 176 if (SkIRect::Intersects(fRootBounds, query)) {
186 search(fRoot, query, results); 177 this->search(fRoot, query, results);
187 } 178 }
188 } 179 }
189 180
190 void SkQuadTree::clear() { 181 void SkQuadTree::clear() {
191 fEntryCount = 0; 182 if (NULL != fRoot) {
192 clear(fRoot); 183 this->clear(fRoot);
184 fNodePool.release(fRoot);
185 fRoot = NULL;
186 }
193 } 187 }
194 188
195 int SkQuadTree::getDepth() const { 189 int SkQuadTree::getDepth() const {
196 return getDepth(fRoot); 190 return this->getDepth(fRoot);
197 } 191 }
198 192
199 void SkQuadTree::rewindInserts() { 193 void SkQuadTree::rewindInserts() {
200 SkASSERT(fClient); 194 SkASSERT(fClient);
201 // Currently only supports deferred inserts 195 // Currently only supports deferred inserts
202 SkASSERT(fRoot->fEntries.isEmpty() && fRoot->fChildren[0] == NULL); 196 SkASSERT(NULL == fRoot);
203 SkTInternalSList<Entry> entries; 197 SkTInternalSList<Entry> entries;
204 entries.pushAll(&fDeferred); 198 entries.pushAll(&fDeferred);
205 while(!entries.isEmpty()) { 199 while(!entries.isEmpty()) {
206 Entry* entry = entries.pop(); 200 Entry* entry = entries.pop();
207 if (fClient->shouldRewind(entry->fData)) { 201 if (fClient->shouldRewind(entry->fData)) {
208 entry->fData = NULL; 202 entry->fData = NULL;
209 fEntryPool.release(entry); 203 fEntryPool.release(entry);
210 --fEntryCount;
211 } else { 204 } else {
212 fDeferred.push(entry); 205 fDeferred.push(entry);
213 } 206 }
214 } 207 }
215 } 208 }
216 209
217 void SkQuadTree::flushDeferredInserts() { 210 void SkQuadTree::flushDeferredInserts() {
211 if (NULL == fRoot) {
212 fRoot = fNodePool.acquire();
213 fRoot->fBounds = fRootBounds;
214 }
218 while(!fDeferred.isEmpty()) { 215 while(!fDeferred.isEmpty()) {
219 insert(fRoot, fDeferred.pop()); 216 this->insert(fRoot, fDeferred.pop());
220 } 217 }
221 } 218 }
OLDNEW
« no previous file with comments | « src/core/SkQuadTree.h ('k') | src/core/SkTObjectPool.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698