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