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