Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
IntersectionTree.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2021 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
9
11#include "src/base/SkVx.h"
12
13#include <algorithm>
14#include <limits>
15
16namespace skgpu::graphite {
17
18// BSP node. Space is partitioned by an either vertical or horizontal line. Note that if a rect
19// straddles the partition line, it will need to go on both sides of the tree.
20template<IntersectionTree::SplitType kSplitType>
21class IntersectionTree::TreeNode final : public Node {
22public:
23 TreeNode(float splitCoord, Node* lo, Node* hi)
24 : fSplitCoord(splitCoord), fLo(lo), fHi(hi) {
25 }
26
27 bool intersects(Rect rect) override {
28 if (GetLoVal(rect) < fSplitCoord && fLo->intersects(rect)) {
29 return true;
30 }
31 if (GetHiVal(rect) > fSplitCoord && fHi->intersects(rect)) {
32 return true;
33 }
34 return false;
35 }
36
37 Node* addNonIntersecting(Rect rect, SkArenaAlloc* arena) override {
38 if (GetLoVal(rect) < fSplitCoord) {
39 fLo = fLo->addNonIntersecting(rect, arena);
40 }
41 if (GetHiVal(rect) > fSplitCoord) {
42 fHi = fHi->addNonIntersecting(rect, arena);
43 }
44 return this;
45 }
46
47private:
48 SK_ALWAYS_INLINE static float GetLoVal(const Rect& rect) {
49 return (kSplitType == SplitType::kX) ? rect.left() : rect.top();
50 }
51 SK_ALWAYS_INLINE static float GetHiVal(const Rect& rect) {
52 return (kSplitType == SplitType::kX) ? rect.right() : rect.bot();
53 }
54
55 float fSplitCoord;
56 Node* fLo;
57 Node* fHi;
58};
59
60// Leaf node. Rects are kept in a simple list and intersection testing is performed by brute force.
61class IntersectionTree::LeafNode final : public Node {
62public:
63 // Max number of rects to store in this node before splitting. With SSE/NEON optimizations, ~64
64 // brute force rect comparisons seems to be the optimal number.
65 constexpr static int kMaxRectsInList = 64;
66
68 this->popAll();
69 // Initialize our arrays with maximally negative rects. These have the advantage of always
70 // failing intersection tests, thus allowing us to test for intersection beyond fNumRects
71 // without failing.
72 constexpr static float infinity = std::numeric_limits<float>::infinity();
73 std::fill_n(fLefts, kMaxRectsInList, infinity);
74 std::fill_n(fTops, kMaxRectsInList, infinity);
75 std::fill_n(fNegRights, kMaxRectsInList, infinity);
76 std::fill_n(fNegBots, kMaxRectsInList, infinity);
77 }
78
79 void popAll() {
80 fNumRects = 0;
81 fSplittableBounds = -std::numeric_limits<float>::infinity();
82 fRectValsSum = 0;
83 // Leave the rect arrays untouched. Since we know they are either already valid in the tree,
84 // or else maximally negative, this allows the future list to check for intersection beyond
85 // fNumRects without failing.
86 }
87
88 bool intersects(Rect rect) override {
89 // Test for intersection in sets of 4. Since all the data in our rect arrays is either
90 // maximally negative, or valid from somewhere else in the tree, we can test beyond
91 // fNumRects without failing.
92 static_assert(kMaxRectsInList % 4 == 0);
93 SkASSERT(fNumRects <= kMaxRectsInList);
94 auto comp = Rect::ComplementRect(rect).fVals;
95 for (int i = 0; i < fNumRects; i += 4) {
96 auto l = skvx::float4::Load(fLefts + i);
97 auto t = skvx::float4::Load(fTops + i);
98 auto nr = skvx::float4::Load(fNegRights + i);
99 auto nb = skvx::float4::Load(fNegBots + i);
100 if (any((l < comp[0]) &
101 (t < comp[1]) &
102 (nr < comp[2]) &
103 (nb < comp[3]))) {
104 return true;
105 }
106 }
107 return false;
108 }
109
110 Node* addNonIntersecting(Rect rect, SkArenaAlloc* arena) override {
111 if (fNumRects == kMaxRectsInList) {
112 // The new rect doesn't fit. Split our rect list first and then add.
113 return this->split(arena)->addNonIntersecting(rect, arena);
114 }
115 this->appendToList(rect);
116 return this;
117 }
118
119private:
120 void appendToList(Rect rect) {
121 SkASSERT(fNumRects < kMaxRectsInList);
122 int i = fNumRects++;
123 // [maxLeft, maxTop, -minRight, -minBot]
124 fSplittableBounds = max(fSplittableBounds, rect.vals());
125 fRectValsSum += rect.vals(); // [sum(left), sum(top), -sum(right), -sum(bot)]
126 fLefts[i] = rect.vals()[0];
127 fTops[i] = rect.vals()[1];
128 fNegRights[i] = rect.vals()[2];
129 fNegBots[i] = rect.vals()[3];
130 }
131
132 Rect loadRect(int i) const {
133 return Rect::FromVals({fLefts[i], fTops[i], fNegRights[i], fNegBots[i]});
134 }
135
136 // Splits this node with a new LeafNode, then returns a TreeNode that reuses our "this" pointer
137 // along with the new node.
138 IntersectionTree::Node* split(SkArenaAlloc* arena) {
139 // This should only get called when our list is full.
140 SkASSERT(fNumRects == kMaxRectsInList);
141
142 // Since rects cannot overlap, there will always be a split that places at least one pairing
143 // of rects on opposite sides. The region:
144 //
145 // fSplittableBounds == [maxLeft, maxTop, -minRight, -minBot] == [r, b, -l, -t]
146 //
147 // Represents the region of splits that guarantee a strict subdivision of our rect list.
148 auto splittableSize = fSplittableBounds.xy() + fSplittableBounds.zw(); // == [r-l, b-t]
149 SkASSERT(max(splittableSize) >= 0);
150 SplitType splitType = (splittableSize.x() > splittableSize.y()) ? SplitType::kX
151 : SplitType::kY;
152
153 float splitCoord;
154 const float *loVals, *negHiVals;
155 if (splitType == SplitType::kX) {
156 // Split horizontally, at the geometric midpoint if it falls within the splittable
157 // bounds.
158 splitCoord = (fRectValsSum.x() - fRectValsSum.z()) * (.5f/kMaxRectsInList);
159 splitCoord = SkTPin(splitCoord, -fSplittableBounds.z(), fSplittableBounds.x());
160 loVals = fLefts;
161 negHiVals = fNegRights;
162 } else {
163 // Split vertically, at the geometric midpoint if it falls within the splittable bounds.
164 splitCoord = (fRectValsSum.y() - fRectValsSum.w()) * (.5f/kMaxRectsInList);
165 splitCoord = SkTPin(splitCoord, -fSplittableBounds.w(), fSplittableBounds.y());
166 loVals = fTops;
167 negHiVals = fNegBots;
168 }
169
170 // Split "this", leaving all rects below "splitCoord" in this, and placing all rects above
171 // splitCoord in "hiNode". There may be some reduncancy between lists, but we made sure to
172 // select a split that would leave both lists strictly smaller than the original.
173 LeafNode* hiNode = arena->make<LeafNode>();
174 int numCombinedRects = fNumRects;
175 float negSplitCoord = -splitCoord;
176 this->popAll();
177 for (int i = 0; i < numCombinedRects; ++i) {
178 Rect rect = this->loadRect(i);
179 if (loVals[i] < splitCoord) {
180 this->appendToList(rect);
181 }
182 if (negHiVals[i] < negSplitCoord) {
183 hiNode->appendToList(rect);
184 }
185 }
186
187 SkASSERT(0 < fNumRects && fNumRects < numCombinedRects);
188 SkASSERT(0 < hiNode->fNumRects && hiNode->fNumRects < numCombinedRects);
189
190 return (splitType == SplitType::kX)
191 ? (Node*)arena->make<TreeNode<SplitType::kX>>(splitCoord, this, hiNode)
192 : (Node*)arena->make<TreeNode<SplitType::kY>>(splitCoord, this, hiNode);
193 }
194
195 int fNumRects;
196 skvx::float4 fSplittableBounds; // [maxLeft, maxTop, -minRight, -minBot]
197 skvx::float4 fRectValsSum; // [sum(left), sum(top), -sum(right), -sum(bot)]
198 alignas(Rect) float fLefts[kMaxRectsInList];
199 alignas(Rect) float fTops[kMaxRectsInList];
200 alignas(Rect) float fNegRights[kMaxRectsInList];
201 alignas(Rect) float fNegBots[kMaxRectsInList];
202 static_assert((kMaxRectsInList * sizeof(float)) % sizeof(Rect) == 0);
203};
204
206 : fRoot(fArena.make<LeafNode>()) {
207 static_assert(kTreeNodeSize == sizeof(TreeNode<SplitType::kX>));
208 static_assert(kTreeNodeSize == sizeof(TreeNode<SplitType::kY>));
209 static_assert(kLeafNodeSize == sizeof(LeafNode));
210}
211
212} // namespace skgpu::graphite
#define SkASSERT(cond)
Definition SkAssert.h:116
#define SK_ALWAYS_INLINE
static constexpr const T & SkTPin(const T &x, const T &lo, const T &hi)
Definition SkTPin.h:19
auto make(Ctor &&ctor) -> decltype(ctor(nullptr))
Node * addNonIntersecting(Rect rect, SkArenaAlloc *arena) override
Node * addNonIntersecting(Rect rect, SkArenaAlloc *arena) override
TreeNode(float splitCoord, Node *lo, Node *hi)
static AI Rect FromVals(float4 vals)
Definition Rect.h:55
static float max(float r, float g, float b)
Definition hsl.cpp:49
Definition dart.idl:29
static sk_sp< SkImage > make(sk_sp< SkColorSpace > cs)
Definition mipmap.cpp:65
sk_sp< SkBlender > blender SkRect rect
Definition SkRecords.h:350
TRect< Scalar > Rect
Definition rect.h:746
static SKVX_ALWAYS_INLINE Vec Load(const void *ptr)
Definition SkVx.h:109