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