Chris Dalton | 0cfe5e1 | 2021-05-13 10:42:30 -0600 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2021 Google Inc. |
| 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 | |
| 8 | #ifndef GrCullTest_DEFINED |
| 9 | #define GrCullTest_DEFINED |
| 10 | |
| 11 | #include "include/core/SkMatrix.h" |
| 12 | #include "include/private/SkVx.h" |
| 13 | #include "src/gpu/GrVx.h" |
| 14 | |
| 15 | // This class determines whether the given local-space points will be contained in the cull bounds |
| 16 | // post transform. For the versions that take >1 point, it returns whether any region of their |
| 17 | // device-space bounding box will be in the cull bounds. |
| 18 | // |
| 19 | // NOTE: Our view matrix is not a normal matrix. M*p maps to the float4 [x, y, -x, -y] in device |
| 20 | // space. We do this to aid in quick bounds calculations. The matrix also does not have a |
| 21 | // translation element. Instead we unapply the translation to the cull bounds ahead of time. |
| 22 | class GrCullTest { |
| 23 | public: |
Chris Dalton | 17eaf62 | 2021-07-28 09:43:52 -0600 | [diff] [blame] | 24 | GrCullTest() = default; |
| 25 | |
| 26 | GrCullTest(const SkRect& devCullBounds, const SkMatrix& m) { |
| 27 | this->set(devCullBounds, m); |
| 28 | } |
| 29 | |
| 30 | void set(const SkRect& devCullBounds, const SkMatrix& m) { |
Chris Dalton | 0cfe5e1 | 2021-05-13 10:42:30 -0600 | [diff] [blame] | 31 | SkASSERT(!m.hasPerspective()); |
Chris Dalton | 17eaf62 | 2021-07-28 09:43:52 -0600 | [diff] [blame] | 32 | // [fMatX, fMatY] maps path coordinates to the float4 [x, y, -x, -y] in device space. |
| 33 | fMatX = {m.getScaleX(), m.getSkewY(), -m.getScaleX(), -m.getSkewY()}; |
| 34 | fMatY = {m.getSkewX(), m.getScaleY(), -m.getSkewX(), -m.getScaleY()}; |
| 35 | // Store the cull bounds as [l, t, -r, -b] for faster math. |
| 36 | // Also subtract the matrix translate from the cull bounds ahead of time, rather than adding |
| 37 | // it to every point every time we test. |
| 38 | fCullBounds = {devCullBounds.fLeft - m.getTranslateX(), |
| 39 | devCullBounds.fTop - m.getTranslateY(), |
| 40 | m.getTranslateX() - devCullBounds.fRight, |
| 41 | m.getTranslateY() - devCullBounds.fBottom}; |
Chris Dalton | 0cfe5e1 | 2021-05-13 10:42:30 -0600 | [diff] [blame] | 42 | } |
| 43 | |
| 44 | // Returns whether M*p will be in the viewport. |
| 45 | bool isVisible(SkPoint p) const { |
| 46 | // devPt = [x, y, -x, -y] in device space. |
Chris Dalton | 89ba8e3 | 2021-10-08 10:18:01 -0600 | [diff] [blame] | 47 | auto devPt = fMatX*p.fX + fMatY*p.fY; |
Chris Dalton | 0cfe5e1 | 2021-05-13 10:42:30 -0600 | [diff] [blame] | 48 | // i.e., l < x && t < y && r > x && b > y. |
| 49 | return skvx::all(fCullBounds < devPt); |
| 50 | } |
| 51 | |
| 52 | // Returns whether any region of the bounding box of M * p0..2 will be in the viewport. |
| 53 | bool areVisible3(const SkPoint p[3]) const { |
| 54 | // Transform p0..2 to device space. |
| 55 | auto val0 = fMatY * p[0].fY; |
| 56 | auto val1 = fMatY * p[1].fY; |
| 57 | auto val2 = fMatY * p[2].fY; |
Chris Dalton | 89ba8e3 | 2021-10-08 10:18:01 -0600 | [diff] [blame] | 58 | val0 = fMatX*p[0].fX + val0; |
| 59 | val1 = fMatX*p[1].fX + val1; |
| 60 | val2 = fMatX*p[2].fX + val2; |
Chris Dalton | 0cfe5e1 | 2021-05-13 10:42:30 -0600 | [diff] [blame] | 61 | // At this point: valN = {xN, yN, -xN, -yN} in device space. |
| 62 | |
| 63 | // Find the device-space bounding box of p0..2. |
| 64 | val0 = skvx::max(val0, val1); |
| 65 | val0 = skvx::max(val0, val2); |
| 66 | // At this point: val0 = [r, b, -l, -t] of the device-space bounding box of p0..2. |
| 67 | |
| 68 | // Does fCullBounds intersect the device-space bounding box of p0..2? |
| 69 | // i.e., l0 < r1 && t0 < b1 && r0 > l1 && b0 > t1. |
| 70 | return skvx::all(fCullBounds < val0); |
| 71 | } |
| 72 | |
| 73 | // Returns whether any region of the bounding box of M * p0..3 will be in the viewport. |
| 74 | bool areVisible4(const SkPoint p[4]) const { |
| 75 | // Transform p0..3 to device space. |
| 76 | auto val0 = fMatY * p[0].fY; |
| 77 | auto val1 = fMatY * p[1].fY; |
| 78 | auto val2 = fMatY * p[2].fY; |
| 79 | auto val3 = fMatY * p[3].fY; |
Chris Dalton | 89ba8e3 | 2021-10-08 10:18:01 -0600 | [diff] [blame] | 80 | val0 = fMatX*p[0].fX + val0; |
| 81 | val1 = fMatX*p[1].fX + val1; |
| 82 | val2 = fMatX*p[2].fX + val2; |
| 83 | val3 = fMatX*p[3].fX + val3; |
Chris Dalton | 0cfe5e1 | 2021-05-13 10:42:30 -0600 | [diff] [blame] | 84 | // At this point: valN = {xN, yN, -xN, -yN} in device space. |
| 85 | |
| 86 | // Find the device-space bounding box of p0..3. |
| 87 | val0 = skvx::max(val0, val1); |
| 88 | val2 = skvx::max(val2, val3); |
| 89 | val0 = skvx::max(val0, val2); |
| 90 | // At this point: val0 = [r, b, -l, -t] of the device-space bounding box of p0..3. |
| 91 | |
| 92 | // Does fCullBounds intersect the device-space bounding box of p0..3? |
| 93 | // i.e., l0 < r1 && t0 < b1 && r0 > l1 && b0 > t1. |
| 94 | return skvx::all(fCullBounds < val0); |
| 95 | } |
| 96 | |
| 97 | private: |
| 98 | // [fMatX, fMatY] maps path coordinates to the float4 [x, y, -x, -y] in device space. |
| 99 | grvx::float4 fMatX; |
| 100 | grvx::float4 fMatY; |
| 101 | grvx::float4 fCullBounds; // [l, t, -r, -b] |
| 102 | }; |
| 103 | |
| 104 | #endif |