xref: /aosp_15_r20/external/skia/tests/GrClipStackTest.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker 
2*c8dee2aaSAndroid Build Coastguard Worker /*
3*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2020 Google LLC
4*c8dee2aaSAndroid Build Coastguard Worker  *
5*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
6*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
7*c8dee2aaSAndroid Build Coastguard Worker  */
8*c8dee2aaSAndroid Build Coastguard Worker 
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkClipOp.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColorSpace.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkMatrix.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPath.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPathTypes.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPoint.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRRect.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRect.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRefCnt.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRegion.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkScalar.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkShader.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkString.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSurfaceProps.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/ganesh/GrContextOptions.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/ganesh/GrDirectContext.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/ganesh/mock/GrMockTypes.h"
27*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTo.h"
28*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/gpu/ganesh/GrTypesPriv.h"
29*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkRRectPriv.h"
30*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ResourceKey.h"
31*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/SkBackingFit.h"
32*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/ClipStack.h"
33*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrAppliedClip.h"
34*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrClip.h"
35*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrDirectContextPriv.h"
36*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrPaint.h"
37*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrProcessorSet.h"
38*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrProxyProvider.h"
39*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrResourceCache.h"
40*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrScissorState.h"
41*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrWindowRectsState.h"
42*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/SurfaceDrawContext.h"
43*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/geometry/GrShape.h"
44*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/ops/GrDrawOp.h"
45*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/ops/GrOp.h"
46*c8dee2aaSAndroid Build Coastguard Worker #include "tests/CtsEnforcement.h"
47*c8dee2aaSAndroid Build Coastguard Worker #include "tests/Test.h"
48*c8dee2aaSAndroid Build Coastguard Worker 
49*c8dee2aaSAndroid Build Coastguard Worker #include <cstddef>
50*c8dee2aaSAndroid Build Coastguard Worker #include <initializer_list>
51*c8dee2aaSAndroid Build Coastguard Worker #include <memory>
52*c8dee2aaSAndroid Build Coastguard Worker #include <tuple>
53*c8dee2aaSAndroid Build Coastguard Worker #include <utility>
54*c8dee2aaSAndroid Build Coastguard Worker #include <vector>
55*c8dee2aaSAndroid Build Coastguard Worker 
56*c8dee2aaSAndroid Build Coastguard Worker class GrCaps;
57*c8dee2aaSAndroid Build Coastguard Worker class GrDstProxyView;
58*c8dee2aaSAndroid Build Coastguard Worker class GrOpFlushState;
59*c8dee2aaSAndroid Build Coastguard Worker class GrRecordingContext;
60*c8dee2aaSAndroid Build Coastguard Worker class GrSurfaceProxyView;
61*c8dee2aaSAndroid Build Coastguard Worker enum class GrXferBarrierFlags;
62*c8dee2aaSAndroid Build Coastguard Worker 
63*c8dee2aaSAndroid Build Coastguard Worker namespace {
64*c8dee2aaSAndroid Build Coastguard Worker 
65*c8dee2aaSAndroid Build Coastguard Worker class TestCaseBuilder;
66*c8dee2aaSAndroid Build Coastguard Worker 
67*c8dee2aaSAndroid Build Coastguard Worker enum class SavePolicy {
68*c8dee2aaSAndroid Build Coastguard Worker     kNever,
69*c8dee2aaSAndroid Build Coastguard Worker     kAtStart,
70*c8dee2aaSAndroid Build Coastguard Worker     kAtEnd,
71*c8dee2aaSAndroid Build Coastguard Worker     kBetweenEveryOp
72*c8dee2aaSAndroid Build Coastguard Worker };
73*c8dee2aaSAndroid Build Coastguard Worker // TODO: We could add a RestorePolicy enum that tests different places to restore, but that would
74*c8dee2aaSAndroid Build Coastguard Worker // make defining the test expectations and order independence more cumbersome.
75*c8dee2aaSAndroid Build Coastguard Worker 
76*c8dee2aaSAndroid Build Coastguard Worker class TestCase {
77*c8dee2aaSAndroid Build Coastguard Worker public:
78*c8dee2aaSAndroid Build Coastguard Worker     using ClipStack = skgpu::ganesh::ClipStack;
79*c8dee2aaSAndroid Build Coastguard Worker 
80*c8dee2aaSAndroid Build Coastguard Worker     // Provides fluent API to describe actual clip commands and expected clip elements:
81*c8dee2aaSAndroid Build Coastguard Worker     // TestCase test = TestCase::Build("example", deviceBounds)
82*c8dee2aaSAndroid Build Coastguard Worker     //                          .actual().rect(r, GrAA::kYes, SkClipOp::kIntersect)
83*c8dee2aaSAndroid Build Coastguard Worker     //                                   .localToDevice(matrix)
84*c8dee2aaSAndroid Build Coastguard Worker     //                                   .nonAA()
85*c8dee2aaSAndroid Build Coastguard Worker     //                                   .difference()
86*c8dee2aaSAndroid Build Coastguard Worker     //                                   .path(p1)
87*c8dee2aaSAndroid Build Coastguard Worker     //                                   .path(p2)
88*c8dee2aaSAndroid Build Coastguard Worker     //                                   .finishElements()
89*c8dee2aaSAndroid Build Coastguard Worker     //                          .expectedState(kDeviceRect)
90*c8dee2aaSAndroid Build Coastguard Worker     //                          .expectedBounds(r.roundOut())
91*c8dee2aaSAndroid Build Coastguard Worker     //                          .expect().rect(r, GrAA::kYes, SkClipOp::kIntersect)
92*c8dee2aaSAndroid Build Coastguard Worker     //                                   .finishElements()
93*c8dee2aaSAndroid Build Coastguard Worker     //                          .finishTest();
94*c8dee2aaSAndroid Build Coastguard Worker     static TestCaseBuilder Build(const char* name, const SkIRect& deviceBounds);
95*c8dee2aaSAndroid Build Coastguard Worker 
96*c8dee2aaSAndroid Build Coastguard Worker     void run(const std::vector<int>& order, SavePolicy policy, skiatest::Reporter* reporter) const;
97*c8dee2aaSAndroid Build Coastguard Worker 
deviceBounds() const98*c8dee2aaSAndroid Build Coastguard Worker     const SkIRect& deviceBounds() const { return fDeviceBounds; }
expectedState() const99*c8dee2aaSAndroid Build Coastguard Worker     ClipStack::ClipState expectedState() const { return fExpectedState; }
initialElements() const100*c8dee2aaSAndroid Build Coastguard Worker     const std::vector<ClipStack::Element>& initialElements() const { return fElements; }
expectedElements() const101*c8dee2aaSAndroid Build Coastguard Worker     const std::vector<ClipStack::Element>& expectedElements() const { return fExpectedElements; }
102*c8dee2aaSAndroid Build Coastguard Worker 
103*c8dee2aaSAndroid Build Coastguard Worker private:
104*c8dee2aaSAndroid Build Coastguard Worker     friend class TestCaseBuilder;
105*c8dee2aaSAndroid Build Coastguard Worker 
TestCase(SkString name,const SkIRect & deviceBounds,ClipStack::ClipState expectedState,std::vector<ClipStack::Element> actual,std::vector<ClipStack::Element> expected)106*c8dee2aaSAndroid Build Coastguard Worker     TestCase(SkString name,
107*c8dee2aaSAndroid Build Coastguard Worker              const SkIRect& deviceBounds,
108*c8dee2aaSAndroid Build Coastguard Worker              ClipStack::ClipState expectedState,
109*c8dee2aaSAndroid Build Coastguard Worker              std::vector<ClipStack::Element> actual,
110*c8dee2aaSAndroid Build Coastguard Worker              std::vector<ClipStack::Element> expected)
111*c8dee2aaSAndroid Build Coastguard Worker         : fName(std::move(name))
112*c8dee2aaSAndroid Build Coastguard Worker         , fElements(std::move(actual))
113*c8dee2aaSAndroid Build Coastguard Worker         , fDeviceBounds(deviceBounds)
114*c8dee2aaSAndroid Build Coastguard Worker         , fExpectedElements(std::move(expected))
115*c8dee2aaSAndroid Build Coastguard Worker         , fExpectedState(expectedState) {}
116*c8dee2aaSAndroid Build Coastguard Worker 
117*c8dee2aaSAndroid Build Coastguard Worker     SkString getTestName(const std::vector<int>& order, SavePolicy policy) const;
118*c8dee2aaSAndroid Build Coastguard Worker 
119*c8dee2aaSAndroid Build Coastguard Worker     // This may be tighter than ClipStack::getConservativeBounds() because this always accounts
120*c8dee2aaSAndroid Build Coastguard Worker     // for difference ops, whereas ClipStack only sometimes can subtract the inner bounds for a
121*c8dee2aaSAndroid Build Coastguard Worker     // difference op.
122*c8dee2aaSAndroid Build Coastguard Worker     std::pair<SkIRect, bool> getOptimalBounds() const;
123*c8dee2aaSAndroid Build Coastguard Worker 
124*c8dee2aaSAndroid Build Coastguard Worker     SkString fName;
125*c8dee2aaSAndroid Build Coastguard Worker 
126*c8dee2aaSAndroid Build Coastguard Worker     // The input shapes+state to ClipStack
127*c8dee2aaSAndroid Build Coastguard Worker     std::vector<ClipStack::Element> fElements;
128*c8dee2aaSAndroid Build Coastguard Worker     SkIRect fDeviceBounds;
129*c8dee2aaSAndroid Build Coastguard Worker 
130*c8dee2aaSAndroid Build Coastguard Worker     // The expected output of iterating over the ClipStack after all fElements are added, although
131*c8dee2aaSAndroid Build Coastguard Worker     // order is not important
132*c8dee2aaSAndroid Build Coastguard Worker     std::vector<ClipStack::Element> fExpectedElements;
133*c8dee2aaSAndroid Build Coastguard Worker     ClipStack::ClipState fExpectedState;
134*c8dee2aaSAndroid Build Coastguard Worker };
135*c8dee2aaSAndroid Build Coastguard Worker 
136*c8dee2aaSAndroid Build Coastguard Worker class ElementsBuilder {
137*c8dee2aaSAndroid Build Coastguard Worker public:
138*c8dee2aaSAndroid Build Coastguard Worker     using ClipStack = skgpu::ganesh::ClipStack;
139*c8dee2aaSAndroid Build Coastguard Worker 
140*c8dee2aaSAndroid Build Coastguard Worker     // Update the default matrix, aa, and op state for elements that are added.
localToDevice(const SkMatrix & m)141*c8dee2aaSAndroid Build Coastguard Worker     ElementsBuilder& localToDevice(const SkMatrix& m) {  fLocalToDevice = m; return *this; }
aa()142*c8dee2aaSAndroid Build Coastguard Worker     ElementsBuilder& aa() { fAA = GrAA::kYes; return *this; }
nonAA()143*c8dee2aaSAndroid Build Coastguard Worker     ElementsBuilder& nonAA() { fAA = GrAA::kNo; return *this; }
intersect()144*c8dee2aaSAndroid Build Coastguard Worker     ElementsBuilder& intersect() { fOp = SkClipOp::kIntersect; return *this; }
difference()145*c8dee2aaSAndroid Build Coastguard Worker     ElementsBuilder& difference() { fOp = SkClipOp::kDifference; return *this; }
146*c8dee2aaSAndroid Build Coastguard Worker 
147*c8dee2aaSAndroid Build Coastguard Worker     // Add rect, rrect, or paths to the list of elements, possibly overriding the last set
148*c8dee2aaSAndroid Build Coastguard Worker     // matrix, aa, and op state.
rect(const SkRect & rect)149*c8dee2aaSAndroid Build Coastguard Worker     ElementsBuilder& rect(const SkRect& rect) {
150*c8dee2aaSAndroid Build Coastguard Worker         return this->rect(rect, fLocalToDevice, fAA, fOp);
151*c8dee2aaSAndroid Build Coastguard Worker     }
rect(const SkRect & rect,GrAA aa,SkClipOp op)152*c8dee2aaSAndroid Build Coastguard Worker     ElementsBuilder& rect(const SkRect& rect, GrAA aa, SkClipOp op) {
153*c8dee2aaSAndroid Build Coastguard Worker         return this->rect(rect, fLocalToDevice, aa, op);
154*c8dee2aaSAndroid Build Coastguard Worker     }
rect(const SkRect & rect,const SkMatrix & m,GrAA aa,SkClipOp op)155*c8dee2aaSAndroid Build Coastguard Worker     ElementsBuilder& rect(const SkRect& rect, const SkMatrix& m, GrAA aa, SkClipOp op) {
156*c8dee2aaSAndroid Build Coastguard Worker         fElements->push_back({GrShape(rect), m, op, aa});
157*c8dee2aaSAndroid Build Coastguard Worker         return *this;
158*c8dee2aaSAndroid Build Coastguard Worker     }
159*c8dee2aaSAndroid Build Coastguard Worker 
rrect(const SkRRect & rrect)160*c8dee2aaSAndroid Build Coastguard Worker     ElementsBuilder& rrect(const SkRRect& rrect) {
161*c8dee2aaSAndroid Build Coastguard Worker         return this->rrect(rrect, fLocalToDevice, fAA, fOp);
162*c8dee2aaSAndroid Build Coastguard Worker     }
rrect(const SkRRect & rrect,GrAA aa,SkClipOp op)163*c8dee2aaSAndroid Build Coastguard Worker     ElementsBuilder& rrect(const SkRRect& rrect, GrAA aa, SkClipOp op) {
164*c8dee2aaSAndroid Build Coastguard Worker         return this->rrect(rrect, fLocalToDevice, aa, op);
165*c8dee2aaSAndroid Build Coastguard Worker     }
rrect(const SkRRect & rrect,const SkMatrix & m,GrAA aa,SkClipOp op)166*c8dee2aaSAndroid Build Coastguard Worker     ElementsBuilder& rrect(const SkRRect& rrect, const SkMatrix& m, GrAA aa, SkClipOp op) {
167*c8dee2aaSAndroid Build Coastguard Worker         fElements->push_back({GrShape(rrect), m, op, aa});
168*c8dee2aaSAndroid Build Coastguard Worker         return *this;
169*c8dee2aaSAndroid Build Coastguard Worker     }
170*c8dee2aaSAndroid Build Coastguard Worker 
path(const SkPath & path)171*c8dee2aaSAndroid Build Coastguard Worker     ElementsBuilder& path(const SkPath& path) {
172*c8dee2aaSAndroid Build Coastguard Worker         return this->path(path, fLocalToDevice, fAA, fOp);
173*c8dee2aaSAndroid Build Coastguard Worker     }
path(const SkPath & path,GrAA aa,SkClipOp op)174*c8dee2aaSAndroid Build Coastguard Worker     ElementsBuilder& path(const SkPath& path, GrAA aa, SkClipOp op) {
175*c8dee2aaSAndroid Build Coastguard Worker         return this->path(path, fLocalToDevice, aa, op);
176*c8dee2aaSAndroid Build Coastguard Worker     }
path(const SkPath & path,const SkMatrix & m,GrAA aa,SkClipOp op)177*c8dee2aaSAndroid Build Coastguard Worker     ElementsBuilder& path(const SkPath& path, const SkMatrix& m, GrAA aa, SkClipOp op) {
178*c8dee2aaSAndroid Build Coastguard Worker         fElements->push_back({GrShape(path), m, op, aa});
179*c8dee2aaSAndroid Build Coastguard Worker         return *this;
180*c8dee2aaSAndroid Build Coastguard Worker     }
181*c8dee2aaSAndroid Build Coastguard Worker 
182*c8dee2aaSAndroid Build Coastguard Worker     // Finish and return the original test case builder
finishElements()183*c8dee2aaSAndroid Build Coastguard Worker     TestCaseBuilder& finishElements() {
184*c8dee2aaSAndroid Build Coastguard Worker         return *fBuilder;
185*c8dee2aaSAndroid Build Coastguard Worker     }
186*c8dee2aaSAndroid Build Coastguard Worker 
187*c8dee2aaSAndroid Build Coastguard Worker private:
188*c8dee2aaSAndroid Build Coastguard Worker     friend class TestCaseBuilder;
189*c8dee2aaSAndroid Build Coastguard Worker 
ElementsBuilder(TestCaseBuilder * builder,std::vector<ClipStack::Element> * elements)190*c8dee2aaSAndroid Build Coastguard Worker     ElementsBuilder(TestCaseBuilder* builder, std::vector<ClipStack::Element>* elements)
191*c8dee2aaSAndroid Build Coastguard Worker             : fBuilder(builder)
192*c8dee2aaSAndroid Build Coastguard Worker             , fElements(elements) {}
193*c8dee2aaSAndroid Build Coastguard Worker 
194*c8dee2aaSAndroid Build Coastguard Worker     SkMatrix fLocalToDevice = SkMatrix::I();
195*c8dee2aaSAndroid Build Coastguard Worker     GrAA     fAA = GrAA::kNo;
196*c8dee2aaSAndroid Build Coastguard Worker     SkClipOp fOp = SkClipOp::kIntersect;
197*c8dee2aaSAndroid Build Coastguard Worker 
198*c8dee2aaSAndroid Build Coastguard Worker     TestCaseBuilder*                 fBuilder;
199*c8dee2aaSAndroid Build Coastguard Worker     std::vector<ClipStack::Element>* fElements;
200*c8dee2aaSAndroid Build Coastguard Worker };
201*c8dee2aaSAndroid Build Coastguard Worker 
202*c8dee2aaSAndroid Build Coastguard Worker class TestCaseBuilder {
203*c8dee2aaSAndroid Build Coastguard Worker public:
204*c8dee2aaSAndroid Build Coastguard Worker     using ClipStack = skgpu::ganesh::ClipStack;
205*c8dee2aaSAndroid Build Coastguard Worker 
actual()206*c8dee2aaSAndroid Build Coastguard Worker     ElementsBuilder actual() { return ElementsBuilder(this, &fActualElements); }
expect()207*c8dee2aaSAndroid Build Coastguard Worker     ElementsBuilder expect() { return ElementsBuilder(this, &fExpectedElements); }
208*c8dee2aaSAndroid Build Coastguard Worker 
expectActual()209*c8dee2aaSAndroid Build Coastguard Worker     TestCaseBuilder& expectActual() {
210*c8dee2aaSAndroid Build Coastguard Worker         fExpectedElements = fActualElements;
211*c8dee2aaSAndroid Build Coastguard Worker         return *this;
212*c8dee2aaSAndroid Build Coastguard Worker     }
213*c8dee2aaSAndroid Build Coastguard Worker 
state(ClipStack::ClipState state)214*c8dee2aaSAndroid Build Coastguard Worker     TestCaseBuilder& state(ClipStack::ClipState state) {
215*c8dee2aaSAndroid Build Coastguard Worker         fExpectedState = state;
216*c8dee2aaSAndroid Build Coastguard Worker         return *this;
217*c8dee2aaSAndroid Build Coastguard Worker     }
218*c8dee2aaSAndroid Build Coastguard Worker 
finishTest()219*c8dee2aaSAndroid Build Coastguard Worker     TestCase finishTest() {
220*c8dee2aaSAndroid Build Coastguard Worker         TestCase test(fName, fDeviceBounds, fExpectedState,
221*c8dee2aaSAndroid Build Coastguard Worker                       std::move(fActualElements), std::move(fExpectedElements));
222*c8dee2aaSAndroid Build Coastguard Worker 
223*c8dee2aaSAndroid Build Coastguard Worker         fExpectedState = ClipStack::ClipState::kWideOpen;
224*c8dee2aaSAndroid Build Coastguard Worker         return test;
225*c8dee2aaSAndroid Build Coastguard Worker     }
226*c8dee2aaSAndroid Build Coastguard Worker 
227*c8dee2aaSAndroid Build Coastguard Worker private:
228*c8dee2aaSAndroid Build Coastguard Worker     friend class TestCase;
229*c8dee2aaSAndroid Build Coastguard Worker 
TestCaseBuilder(const char * name,const SkIRect & deviceBounds)230*c8dee2aaSAndroid Build Coastguard Worker     explicit TestCaseBuilder(const char* name, const SkIRect& deviceBounds)
231*c8dee2aaSAndroid Build Coastguard Worker             : fName(name)
232*c8dee2aaSAndroid Build Coastguard Worker             , fDeviceBounds(deviceBounds)
233*c8dee2aaSAndroid Build Coastguard Worker             , fExpectedState(ClipStack::ClipState::kWideOpen) {}
234*c8dee2aaSAndroid Build Coastguard Worker 
235*c8dee2aaSAndroid Build Coastguard Worker     SkString fName;
236*c8dee2aaSAndroid Build Coastguard Worker     SkIRect  fDeviceBounds;
237*c8dee2aaSAndroid Build Coastguard Worker     ClipStack::ClipState fExpectedState;
238*c8dee2aaSAndroid Build Coastguard Worker 
239*c8dee2aaSAndroid Build Coastguard Worker     std::vector<ClipStack::Element> fActualElements;
240*c8dee2aaSAndroid Build Coastguard Worker     std::vector<ClipStack::Element> fExpectedElements;
241*c8dee2aaSAndroid Build Coastguard Worker };
242*c8dee2aaSAndroid Build Coastguard Worker 
Build(const char * name,const SkIRect & deviceBounds)243*c8dee2aaSAndroid Build Coastguard Worker TestCaseBuilder TestCase::Build(const char* name, const SkIRect& deviceBounds) {
244*c8dee2aaSAndroid Build Coastguard Worker     return TestCaseBuilder(name, deviceBounds);
245*c8dee2aaSAndroid Build Coastguard Worker }
246*c8dee2aaSAndroid Build Coastguard Worker 
getTestName(const std::vector<int> & order,SavePolicy policy) const247*c8dee2aaSAndroid Build Coastguard Worker SkString TestCase::getTestName(const std::vector<int>& order, SavePolicy policy) const {
248*c8dee2aaSAndroid Build Coastguard Worker     SkString name = fName;
249*c8dee2aaSAndroid Build Coastguard Worker 
250*c8dee2aaSAndroid Build Coastguard Worker     SkString policyName;
251*c8dee2aaSAndroid Build Coastguard Worker     switch(policy) {
252*c8dee2aaSAndroid Build Coastguard Worker         case SavePolicy::kNever:
253*c8dee2aaSAndroid Build Coastguard Worker             policyName = "never";
254*c8dee2aaSAndroid Build Coastguard Worker             break;
255*c8dee2aaSAndroid Build Coastguard Worker         case SavePolicy::kAtStart:
256*c8dee2aaSAndroid Build Coastguard Worker             policyName = "start";
257*c8dee2aaSAndroid Build Coastguard Worker             break;
258*c8dee2aaSAndroid Build Coastguard Worker         case SavePolicy::kAtEnd:
259*c8dee2aaSAndroid Build Coastguard Worker             policyName = "end";
260*c8dee2aaSAndroid Build Coastguard Worker             break;
261*c8dee2aaSAndroid Build Coastguard Worker         case SavePolicy::kBetweenEveryOp:
262*c8dee2aaSAndroid Build Coastguard Worker             policyName = "between";
263*c8dee2aaSAndroid Build Coastguard Worker             break;
264*c8dee2aaSAndroid Build Coastguard Worker     }
265*c8dee2aaSAndroid Build Coastguard Worker 
266*c8dee2aaSAndroid Build Coastguard Worker     name.appendf("(save %s, order [", policyName.c_str());
267*c8dee2aaSAndroid Build Coastguard Worker     for (size_t i = 0; i < order.size(); ++i) {
268*c8dee2aaSAndroid Build Coastguard Worker         if (i > 0) {
269*c8dee2aaSAndroid Build Coastguard Worker             name.append(",");
270*c8dee2aaSAndroid Build Coastguard Worker         }
271*c8dee2aaSAndroid Build Coastguard Worker         name.appendf("%d", order[i]);
272*c8dee2aaSAndroid Build Coastguard Worker     }
273*c8dee2aaSAndroid Build Coastguard Worker     name.append("])");
274*c8dee2aaSAndroid Build Coastguard Worker     return name;
275*c8dee2aaSAndroid Build Coastguard Worker }
276*c8dee2aaSAndroid Build Coastguard Worker 
getOptimalBounds() const277*c8dee2aaSAndroid Build Coastguard Worker std::pair<SkIRect, bool> TestCase::getOptimalBounds() const {
278*c8dee2aaSAndroid Build Coastguard Worker     if (fExpectedState == ClipStack::ClipState::kEmpty) {
279*c8dee2aaSAndroid Build Coastguard Worker         return {SkIRect::MakeEmpty(), true};
280*c8dee2aaSAndroid Build Coastguard Worker     }
281*c8dee2aaSAndroid Build Coastguard Worker 
282*c8dee2aaSAndroid Build Coastguard Worker     bool expectOptimal = true;
283*c8dee2aaSAndroid Build Coastguard Worker     SkRegion region(fDeviceBounds);
284*c8dee2aaSAndroid Build Coastguard Worker     for (const ClipStack::Element& e : fExpectedElements) {
285*c8dee2aaSAndroid Build Coastguard Worker         bool intersect = (e.fOp == SkClipOp::kIntersect && !e.fShape.inverted()) ||
286*c8dee2aaSAndroid Build Coastguard Worker                          (e.fOp == SkClipOp::kDifference && e.fShape.inverted());
287*c8dee2aaSAndroid Build Coastguard Worker 
288*c8dee2aaSAndroid Build Coastguard Worker         SkIRect elementBounds;
289*c8dee2aaSAndroid Build Coastguard Worker         SkRegion::Op op;
290*c8dee2aaSAndroid Build Coastguard Worker         if (intersect) {
291*c8dee2aaSAndroid Build Coastguard Worker             op = SkRegion::kIntersect_Op;
292*c8dee2aaSAndroid Build Coastguard Worker             expectOptimal &= e.fLocalToDevice.isIdentity();
293*c8dee2aaSAndroid Build Coastguard Worker             elementBounds = GrClip::GetPixelIBounds(e.fLocalToDevice.mapRect(e.fShape.bounds()),
294*c8dee2aaSAndroid Build Coastguard Worker                                                     e.fAA, GrClip::BoundsType::kExterior);
295*c8dee2aaSAndroid Build Coastguard Worker         } else {
296*c8dee2aaSAndroid Build Coastguard Worker             op = SkRegion::kDifference_Op;
297*c8dee2aaSAndroid Build Coastguard Worker             expectOptimal = false;
298*c8dee2aaSAndroid Build Coastguard Worker             if (e.fShape.isRect() && e.fLocalToDevice.isIdentity()) {
299*c8dee2aaSAndroid Build Coastguard Worker                 elementBounds = GrClip::GetPixelIBounds(e.fShape.rect(), e.fAA,
300*c8dee2aaSAndroid Build Coastguard Worker                                                         GrClip::BoundsType::kInterior);
301*c8dee2aaSAndroid Build Coastguard Worker             } else if (e.fShape.isRRect() && e.fLocalToDevice.isIdentity()) {
302*c8dee2aaSAndroid Build Coastguard Worker                 elementBounds = GrClip::GetPixelIBounds(SkRRectPriv::InnerBounds(e.fShape.rrect()),
303*c8dee2aaSAndroid Build Coastguard Worker                                                         e.fAA, GrClip::BoundsType::kInterior);
304*c8dee2aaSAndroid Build Coastguard Worker             } else {
305*c8dee2aaSAndroid Build Coastguard Worker                 elementBounds = SkIRect::MakeEmpty();
306*c8dee2aaSAndroid Build Coastguard Worker             }
307*c8dee2aaSAndroid Build Coastguard Worker         }
308*c8dee2aaSAndroid Build Coastguard Worker 
309*c8dee2aaSAndroid Build Coastguard Worker         region.op(SkRegion(elementBounds), op);
310*c8dee2aaSAndroid Build Coastguard Worker     }
311*c8dee2aaSAndroid Build Coastguard Worker     return {region.getBounds(), expectOptimal};
312*c8dee2aaSAndroid Build Coastguard Worker }
313*c8dee2aaSAndroid Build Coastguard Worker 
compare_elements(const skgpu::ganesh::ClipStack::Element & a,const skgpu::ganesh::ClipStack::Element & b)314*c8dee2aaSAndroid Build Coastguard Worker static bool compare_elements(const skgpu::ganesh::ClipStack::Element& a,
315*c8dee2aaSAndroid Build Coastguard Worker                              const skgpu::ganesh::ClipStack::Element& b) {
316*c8dee2aaSAndroid Build Coastguard Worker     if (a.fAA != b.fAA || a.fOp != b.fOp || a.fLocalToDevice != b.fLocalToDevice ||
317*c8dee2aaSAndroid Build Coastguard Worker         a.fShape.type() != b.fShape.type()) {
318*c8dee2aaSAndroid Build Coastguard Worker         return false;
319*c8dee2aaSAndroid Build Coastguard Worker     }
320*c8dee2aaSAndroid Build Coastguard Worker     switch(a.fShape.type()) {
321*c8dee2aaSAndroid Build Coastguard Worker         case GrShape::Type::kRect:
322*c8dee2aaSAndroid Build Coastguard Worker             return a.fShape.rect() == b.fShape.rect();
323*c8dee2aaSAndroid Build Coastguard Worker         case GrShape::Type::kRRect:
324*c8dee2aaSAndroid Build Coastguard Worker             return a.fShape.rrect() == b.fShape.rrect();
325*c8dee2aaSAndroid Build Coastguard Worker         case GrShape::Type::kPath:
326*c8dee2aaSAndroid Build Coastguard Worker             // A path's points are never transformed, the only modification is fill type which does
327*c8dee2aaSAndroid Build Coastguard Worker             // not change the generation ID. For convex polygons, we check == so that more complex
328*c8dee2aaSAndroid Build Coastguard Worker             // test cases can be evaluated.
329*c8dee2aaSAndroid Build Coastguard Worker             return a.fShape.path().getGenerationID() == b.fShape.path().getGenerationID() ||
330*c8dee2aaSAndroid Build Coastguard Worker                    (a.fShape.convex() &&
331*c8dee2aaSAndroid Build Coastguard Worker                     a.fShape.segmentMask() == SkPathSegmentMask::kLine_SkPathSegmentMask &&
332*c8dee2aaSAndroid Build Coastguard Worker                     a.fShape.path() == b.fShape.path());
333*c8dee2aaSAndroid Build Coastguard Worker         default:
334*c8dee2aaSAndroid Build Coastguard Worker             SkDEBUGFAIL("Shape type not handled by test case yet.");
335*c8dee2aaSAndroid Build Coastguard Worker             return false;
336*c8dee2aaSAndroid Build Coastguard Worker     }
337*c8dee2aaSAndroid Build Coastguard Worker }
338*c8dee2aaSAndroid Build Coastguard Worker 
run(const std::vector<int> & order,SavePolicy policy,skiatest::Reporter * reporter) const339*c8dee2aaSAndroid Build Coastguard Worker void TestCase::run(const std::vector<int>& order,
340*c8dee2aaSAndroid Build Coastguard Worker                    SavePolicy policy,
341*c8dee2aaSAndroid Build Coastguard Worker                    skiatest::Reporter* reporter) const {
342*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(fElements.size() == order.size());
343*c8dee2aaSAndroid Build Coastguard Worker 
344*c8dee2aaSAndroid Build Coastguard Worker     ClipStack cs(fDeviceBounds, &SkMatrix::I(), false);
345*c8dee2aaSAndroid Build Coastguard Worker 
346*c8dee2aaSAndroid Build Coastguard Worker     if (policy == SavePolicy::kAtStart) {
347*c8dee2aaSAndroid Build Coastguard Worker         cs.save();
348*c8dee2aaSAndroid Build Coastguard Worker     }
349*c8dee2aaSAndroid Build Coastguard Worker 
350*c8dee2aaSAndroid Build Coastguard Worker     for (int i : order) {
351*c8dee2aaSAndroid Build Coastguard Worker         if (policy == SavePolicy::kBetweenEveryOp) {
352*c8dee2aaSAndroid Build Coastguard Worker             cs.save();
353*c8dee2aaSAndroid Build Coastguard Worker         }
354*c8dee2aaSAndroid Build Coastguard Worker         const ClipStack::Element& e = fElements[i];
355*c8dee2aaSAndroid Build Coastguard Worker         switch(e.fShape.type()) {
356*c8dee2aaSAndroid Build Coastguard Worker             case GrShape::Type::kRect:
357*c8dee2aaSAndroid Build Coastguard Worker                 cs.clipRect(e.fLocalToDevice, e.fShape.rect(), e.fAA, e.fOp);
358*c8dee2aaSAndroid Build Coastguard Worker                 break;
359*c8dee2aaSAndroid Build Coastguard Worker             case GrShape::Type::kRRect:
360*c8dee2aaSAndroid Build Coastguard Worker                 cs.clipRRect(e.fLocalToDevice, e.fShape.rrect(), e.fAA, e.fOp);
361*c8dee2aaSAndroid Build Coastguard Worker                 break;
362*c8dee2aaSAndroid Build Coastguard Worker             case GrShape::Type::kPath:
363*c8dee2aaSAndroid Build Coastguard Worker                 cs.clipPath(e.fLocalToDevice, e.fShape.path(), e.fAA, e.fOp);
364*c8dee2aaSAndroid Build Coastguard Worker                 break;
365*c8dee2aaSAndroid Build Coastguard Worker             default:
366*c8dee2aaSAndroid Build Coastguard Worker                 SkDEBUGFAIL("Shape type not handled by test case yet.");
367*c8dee2aaSAndroid Build Coastguard Worker         }
368*c8dee2aaSAndroid Build Coastguard Worker     }
369*c8dee2aaSAndroid Build Coastguard Worker 
370*c8dee2aaSAndroid Build Coastguard Worker     if (policy == SavePolicy::kAtEnd) {
371*c8dee2aaSAndroid Build Coastguard Worker         cs.save();
372*c8dee2aaSAndroid Build Coastguard Worker     }
373*c8dee2aaSAndroid Build Coastguard Worker 
374*c8dee2aaSAndroid Build Coastguard Worker     // Now validate
375*c8dee2aaSAndroid Build Coastguard Worker     SkString name = this->getTestName(order, policy);
376*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, cs.clipState() == fExpectedState,
377*c8dee2aaSAndroid Build Coastguard Worker                     "%s, clip state expected %d, actual %d",
378*c8dee2aaSAndroid Build Coastguard Worker                     name.c_str(), (int) fExpectedState, (int) cs.clipState());
379*c8dee2aaSAndroid Build Coastguard Worker     SkIRect actualBounds = cs.getConservativeBounds();
380*c8dee2aaSAndroid Build Coastguard Worker     SkIRect optimalBounds;
381*c8dee2aaSAndroid Build Coastguard Worker     bool expectOptimal;
382*c8dee2aaSAndroid Build Coastguard Worker     std::tie(optimalBounds, expectOptimal) = this->getOptimalBounds();
383*c8dee2aaSAndroid Build Coastguard Worker 
384*c8dee2aaSAndroid Build Coastguard Worker     if (expectOptimal) {
385*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, actualBounds == optimalBounds,
386*c8dee2aaSAndroid Build Coastguard Worker                 "%s, bounds expected [%d %d %d %d], actual [%d %d %d %d]",
387*c8dee2aaSAndroid Build Coastguard Worker                 name.c_str(), optimalBounds.fLeft, optimalBounds.fTop,
388*c8dee2aaSAndroid Build Coastguard Worker                 optimalBounds.fRight, optimalBounds.fBottom,
389*c8dee2aaSAndroid Build Coastguard Worker                 actualBounds.fLeft, actualBounds.fTop,
390*c8dee2aaSAndroid Build Coastguard Worker                 actualBounds.fRight, actualBounds.fBottom);
391*c8dee2aaSAndroid Build Coastguard Worker     } else {
392*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, actualBounds.contains(optimalBounds),
393*c8dee2aaSAndroid Build Coastguard Worker                 "%s, bounds are not conservative, optimal [%d %d %d %d], actual [%d %d %d %d]",
394*c8dee2aaSAndroid Build Coastguard Worker                 name.c_str(), optimalBounds.fLeft, optimalBounds.fTop,
395*c8dee2aaSAndroid Build Coastguard Worker                 optimalBounds.fRight, optimalBounds.fBottom,
396*c8dee2aaSAndroid Build Coastguard Worker                 actualBounds.fLeft, actualBounds.fTop,
397*c8dee2aaSAndroid Build Coastguard Worker                 actualBounds.fRight, actualBounds.fBottom);
398*c8dee2aaSAndroid Build Coastguard Worker     }
399*c8dee2aaSAndroid Build Coastguard Worker 
400*c8dee2aaSAndroid Build Coastguard Worker     size_t matchedElements = 0;
401*c8dee2aaSAndroid Build Coastguard Worker     for (const ClipStack::Element& a : cs) {
402*c8dee2aaSAndroid Build Coastguard Worker         bool found = false;
403*c8dee2aaSAndroid Build Coastguard Worker         for (const ClipStack::Element& e : fExpectedElements) {
404*c8dee2aaSAndroid Build Coastguard Worker             if (compare_elements(a, e)) {
405*c8dee2aaSAndroid Build Coastguard Worker                 // shouldn't match multiple expected elements or it's a bad test case
406*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(!found);
407*c8dee2aaSAndroid Build Coastguard Worker                 found = true;
408*c8dee2aaSAndroid Build Coastguard Worker             }
409*c8dee2aaSAndroid Build Coastguard Worker         }
410*c8dee2aaSAndroid Build Coastguard Worker 
411*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, found,
412*c8dee2aaSAndroid Build Coastguard Worker                         "%s, unexpected clip element in stack: shape %d, aa %d, op %d",
413*c8dee2aaSAndroid Build Coastguard Worker                         name.c_str(), (int) a.fShape.type(), (int) a.fAA, (int) a.fOp);
414*c8dee2aaSAndroid Build Coastguard Worker         matchedElements += found ? 1 : 0;
415*c8dee2aaSAndroid Build Coastguard Worker     }
416*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, matchedElements == fExpectedElements.size(),
417*c8dee2aaSAndroid Build Coastguard Worker                     "%s, did not match all expected elements: expected %zu but matched only %zu",
418*c8dee2aaSAndroid Build Coastguard Worker                     name.c_str(), fExpectedElements.size(), matchedElements);
419*c8dee2aaSAndroid Build Coastguard Worker 
420*c8dee2aaSAndroid Build Coastguard Worker     // Validate restoration behavior
421*c8dee2aaSAndroid Build Coastguard Worker     if (policy == SavePolicy::kAtEnd) {
422*c8dee2aaSAndroid Build Coastguard Worker         ClipStack::ClipState oldState = cs.clipState();
423*c8dee2aaSAndroid Build Coastguard Worker         cs.restore();
424*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, cs.clipState() == oldState,
425*c8dee2aaSAndroid Build Coastguard Worker                         "%s, restoring an empty save record should not change clip state: "
426*c8dee2aaSAndroid Build Coastguard Worker                         "expected %d but got %d",
427*c8dee2aaSAndroid Build Coastguard Worker                         name.c_str(), (int) oldState, (int) cs.clipState());
428*c8dee2aaSAndroid Build Coastguard Worker     } else if (policy != SavePolicy::kNever) {
429*c8dee2aaSAndroid Build Coastguard Worker         int restoreCount = policy == SavePolicy::kAtStart ? 1 : (int) order.size();
430*c8dee2aaSAndroid Build Coastguard Worker         for (int i = 0; i < restoreCount; ++i) {
431*c8dee2aaSAndroid Build Coastguard Worker             cs.restore();
432*c8dee2aaSAndroid Build Coastguard Worker         }
433*c8dee2aaSAndroid Build Coastguard Worker         // Should be wide open if everything is restored to base state
434*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, cs.clipState() == ClipStack::ClipState::kWideOpen,
435*c8dee2aaSAndroid Build Coastguard Worker                         "%s, restore should make stack become wide-open, not %d",
436*c8dee2aaSAndroid Build Coastguard Worker                         name.c_str(), (int) cs.clipState());
437*c8dee2aaSAndroid Build Coastguard Worker     }
438*c8dee2aaSAndroid Build Coastguard Worker }
439*c8dee2aaSAndroid Build Coastguard Worker 
440*c8dee2aaSAndroid Build Coastguard Worker // All clip operations are commutative so applying actual elements in every possible order should
441*c8dee2aaSAndroid Build Coastguard Worker // always produce the same set of expected elements.
run_test_case(skiatest::Reporter * r,const TestCase & test)442*c8dee2aaSAndroid Build Coastguard Worker static void run_test_case(skiatest::Reporter* r, const TestCase& test) {
443*c8dee2aaSAndroid Build Coastguard Worker     int n = (int) test.initialElements().size();
444*c8dee2aaSAndroid Build Coastguard Worker     std::vector<int> order(n);
445*c8dee2aaSAndroid Build Coastguard Worker     std::vector<int> stack(n);
446*c8dee2aaSAndroid Build Coastguard Worker 
447*c8dee2aaSAndroid Build Coastguard Worker     // Initial order sequence and zeroed stack
448*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 0; i < n; ++i) {
449*c8dee2aaSAndroid Build Coastguard Worker         order[i] = i;
450*c8dee2aaSAndroid Build Coastguard Worker         stack[i] = 0;
451*c8dee2aaSAndroid Build Coastguard Worker     }
452*c8dee2aaSAndroid Build Coastguard Worker 
453*c8dee2aaSAndroid Build Coastguard Worker     auto runTest = [&]() {
454*c8dee2aaSAndroid Build Coastguard Worker         static const SavePolicy kPolicies[] = { SavePolicy::kNever, SavePolicy::kAtStart,
455*c8dee2aaSAndroid Build Coastguard Worker                                                 SavePolicy::kAtEnd, SavePolicy::kBetweenEveryOp };
456*c8dee2aaSAndroid Build Coastguard Worker         for (auto policy : kPolicies) {
457*c8dee2aaSAndroid Build Coastguard Worker             test.run(order, policy, r);
458*c8dee2aaSAndroid Build Coastguard Worker         }
459*c8dee2aaSAndroid Build Coastguard Worker     };
460*c8dee2aaSAndroid Build Coastguard Worker 
461*c8dee2aaSAndroid Build Coastguard Worker     // Heap's algorithm (non-recursive) to generate every permutation over the test case's elements
462*c8dee2aaSAndroid Build Coastguard Worker     // https://en.wikipedia.org/wiki/Heap%27s_algorithm
463*c8dee2aaSAndroid Build Coastguard Worker     runTest();
464*c8dee2aaSAndroid Build Coastguard Worker 
465*c8dee2aaSAndroid Build Coastguard Worker     static constexpr int kMaxRuns = 720; // Don't run more than 6! configurations, even if n > 6
466*c8dee2aaSAndroid Build Coastguard Worker     int testRuns = 1;
467*c8dee2aaSAndroid Build Coastguard Worker 
468*c8dee2aaSAndroid Build Coastguard Worker     int i = 0;
469*c8dee2aaSAndroid Build Coastguard Worker     while (i < n && testRuns < kMaxRuns) {
470*c8dee2aaSAndroid Build Coastguard Worker         if (stack[i] < i) {
471*c8dee2aaSAndroid Build Coastguard Worker             using std::swap;
472*c8dee2aaSAndroid Build Coastguard Worker             if (i % 2 == 0) {
473*c8dee2aaSAndroid Build Coastguard Worker                 swap(order[0], order[i]);
474*c8dee2aaSAndroid Build Coastguard Worker             } else {
475*c8dee2aaSAndroid Build Coastguard Worker                 swap(order[stack[i]], order[i]);
476*c8dee2aaSAndroid Build Coastguard Worker             }
477*c8dee2aaSAndroid Build Coastguard Worker 
478*c8dee2aaSAndroid Build Coastguard Worker             runTest();
479*c8dee2aaSAndroid Build Coastguard Worker             stack[i]++;
480*c8dee2aaSAndroid Build Coastguard Worker             i = 0;
481*c8dee2aaSAndroid Build Coastguard Worker             testRuns++;
482*c8dee2aaSAndroid Build Coastguard Worker         } else {
483*c8dee2aaSAndroid Build Coastguard Worker             stack[i] = 0;
484*c8dee2aaSAndroid Build Coastguard Worker             ++i;
485*c8dee2aaSAndroid Build Coastguard Worker         }
486*c8dee2aaSAndroid Build Coastguard Worker     }
487*c8dee2aaSAndroid Build Coastguard Worker }
488*c8dee2aaSAndroid Build Coastguard Worker 
make_octagon(const SkRect & r,SkScalar lr,SkScalar tb)489*c8dee2aaSAndroid Build Coastguard Worker static SkPath make_octagon(const SkRect& r, SkScalar lr, SkScalar tb) {
490*c8dee2aaSAndroid Build Coastguard Worker     SkPath p;
491*c8dee2aaSAndroid Build Coastguard Worker     p.moveTo(r.fLeft + lr, r.fTop);
492*c8dee2aaSAndroid Build Coastguard Worker     p.lineTo(r.fRight - lr, r.fTop);
493*c8dee2aaSAndroid Build Coastguard Worker     p.lineTo(r.fRight, r.fTop + tb);
494*c8dee2aaSAndroid Build Coastguard Worker     p.lineTo(r.fRight, r.fBottom - tb);
495*c8dee2aaSAndroid Build Coastguard Worker     p.lineTo(r.fRight - lr, r.fBottom);
496*c8dee2aaSAndroid Build Coastguard Worker     p.lineTo(r.fLeft + lr, r.fBottom);
497*c8dee2aaSAndroid Build Coastguard Worker     p.lineTo(r.fLeft, r.fBottom - tb);
498*c8dee2aaSAndroid Build Coastguard Worker     p.lineTo(r.fLeft, r.fTop + tb);
499*c8dee2aaSAndroid Build Coastguard Worker     p.close();
500*c8dee2aaSAndroid Build Coastguard Worker     return p;
501*c8dee2aaSAndroid Build Coastguard Worker }
502*c8dee2aaSAndroid Build Coastguard Worker 
make_octagon(const SkRect & r)503*c8dee2aaSAndroid Build Coastguard Worker static SkPath make_octagon(const SkRect& r) {
504*c8dee2aaSAndroid Build Coastguard Worker     SkScalar lr = 0.3f * r.width();
505*c8dee2aaSAndroid Build Coastguard Worker     SkScalar tb = 0.3f * r.height();
506*c8dee2aaSAndroid Build Coastguard Worker     return make_octagon(r, lr, tb);
507*c8dee2aaSAndroid Build Coastguard Worker }
508*c8dee2aaSAndroid Build Coastguard Worker 
509*c8dee2aaSAndroid Build Coastguard Worker static constexpr SkIRect kDeviceBounds = {0, 0, 100, 100};
510*c8dee2aaSAndroid Build Coastguard Worker 
511*c8dee2aaSAndroid Build Coastguard Worker class NoOp : public GrDrawOp {
512*c8dee2aaSAndroid Build Coastguard Worker public:
Get()513*c8dee2aaSAndroid Build Coastguard Worker     static NoOp* Get() {
514*c8dee2aaSAndroid Build Coastguard Worker         static NoOp gNoOp;
515*c8dee2aaSAndroid Build Coastguard Worker         return &gNoOp;
516*c8dee2aaSAndroid Build Coastguard Worker     }
517*c8dee2aaSAndroid Build Coastguard Worker private:
518*c8dee2aaSAndroid Build Coastguard Worker     DEFINE_OP_CLASS_ID
NoOp()519*c8dee2aaSAndroid Build Coastguard Worker     NoOp() : GrDrawOp(ClassID()) {}
name() const520*c8dee2aaSAndroid Build Coastguard Worker     const char* name() const override { return "NoOp"; }
finalize(const GrCaps &,const GrAppliedClip *,GrClampType)521*c8dee2aaSAndroid Build Coastguard Worker     GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*, GrClampType) override {
522*c8dee2aaSAndroid Build Coastguard Worker         return GrProcessorSet::EmptySetAnalysis();
523*c8dee2aaSAndroid Build Coastguard Worker     }
onPrePrepare(GrRecordingContext *,const GrSurfaceProxyView &,GrAppliedClip *,const GrDstProxyView &,GrXferBarrierFlags,GrLoadOp)524*c8dee2aaSAndroid Build Coastguard Worker     void onPrePrepare(GrRecordingContext*, const GrSurfaceProxyView&, GrAppliedClip*, const
525*c8dee2aaSAndroid Build Coastguard Worker                       GrDstProxyView&, GrXferBarrierFlags, GrLoadOp) override {}
onPrepare(GrOpFlushState *)526*c8dee2aaSAndroid Build Coastguard Worker     void onPrepare(GrOpFlushState*) override {}
onExecute(GrOpFlushState *,const SkRect &)527*c8dee2aaSAndroid Build Coastguard Worker     void onExecute(GrOpFlushState*, const SkRect&) override {}
528*c8dee2aaSAndroid Build Coastguard Worker };
529*c8dee2aaSAndroid Build Coastguard Worker 
530*c8dee2aaSAndroid Build Coastguard Worker } // anonymous namespace
531*c8dee2aaSAndroid Build Coastguard Worker 
532*c8dee2aaSAndroid Build Coastguard Worker ///////////////////////////////////////////////////////////////////////////////
533*c8dee2aaSAndroid Build Coastguard Worker // These tests use the TestCase infrastructure to define clip stacks and
534*c8dee2aaSAndroid Build Coastguard Worker // associated expectations.
535*c8dee2aaSAndroid Build Coastguard Worker 
536*c8dee2aaSAndroid Build Coastguard Worker // Tests that the initialized state of the clip stack is wide-open
DEF_TEST(ClipStack_InitialState,r)537*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(ClipStack_InitialState, r) {
538*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("initial-state", SkIRect::MakeWH(100, 100)).finishTest());
539*c8dee2aaSAndroid Build Coastguard Worker }
540*c8dee2aaSAndroid Build Coastguard Worker 
541*c8dee2aaSAndroid Build Coastguard Worker // Tests that intersection of rects combine to a single element when they have the same AA type,
542*c8dee2aaSAndroid Build Coastguard Worker // or are pixel-aligned.
DEF_TEST(ClipStack_RectRectAACombine,r)543*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(ClipStack_RectRectAACombine, r) {
544*c8dee2aaSAndroid Build Coastguard Worker     using ClipState = skgpu::ganesh::ClipStack::ClipState;
545*c8dee2aaSAndroid Build Coastguard Worker 
546*c8dee2aaSAndroid Build Coastguard Worker     SkRect pixelAligned = {0, 0, 10, 10};
547*c8dee2aaSAndroid Build Coastguard Worker     SkRect fracRect1 = pixelAligned.makeOffset(5.3f, 3.7f);
548*c8dee2aaSAndroid Build Coastguard Worker     SkRect fracRect2 = {fracRect1.fLeft + 0.75f * fracRect1.width(),
549*c8dee2aaSAndroid Build Coastguard Worker                         fracRect1.fTop + 0.75f * fracRect1.height(),
550*c8dee2aaSAndroid Build Coastguard Worker                         fracRect1.fRight, fracRect1.fBottom};
551*c8dee2aaSAndroid Build Coastguard Worker 
552*c8dee2aaSAndroid Build Coastguard Worker     SkRect fracIntersect;
553*c8dee2aaSAndroid Build Coastguard Worker     SkAssertResult(fracIntersect.intersect(fracRect1, fracRect2));
554*c8dee2aaSAndroid Build Coastguard Worker     SkRect alignedIntersect;
555*c8dee2aaSAndroid Build Coastguard Worker     SkAssertResult(alignedIntersect.intersect(pixelAligned, fracRect1));
556*c8dee2aaSAndroid Build Coastguard Worker 
557*c8dee2aaSAndroid Build Coastguard Worker     // Both AA combine to one element
558*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("aa", kDeviceBounds)
559*c8dee2aaSAndroid Build Coastguard Worker                               .actual().aa().intersect()
560*c8dee2aaSAndroid Build Coastguard Worker                                        .rect(fracRect1).rect(fracRect2)
561*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
562*c8dee2aaSAndroid Build Coastguard Worker                               .expect().aa().intersect().rect(fracIntersect).finishElements()
563*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kDeviceRect)
564*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
565*c8dee2aaSAndroid Build Coastguard Worker 
566*c8dee2aaSAndroid Build Coastguard Worker     // Both non-AA combine to one element
567*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("nonaa", kDeviceBounds)
568*c8dee2aaSAndroid Build Coastguard Worker                               .actual().nonAA().intersect()
569*c8dee2aaSAndroid Build Coastguard Worker                                        .rect(fracRect1).rect(fracRect2)
570*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
571*c8dee2aaSAndroid Build Coastguard Worker                               .expect().nonAA().intersect().rect(fracIntersect).finishElements()
572*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kDeviceRect)
573*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
574*c8dee2aaSAndroid Build Coastguard Worker 
575*c8dee2aaSAndroid Build Coastguard Worker     // Pixel-aligned AA and non-AA combine
576*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("aligned-aa+nonaa", kDeviceBounds)
577*c8dee2aaSAndroid Build Coastguard Worker                              .actual().intersect()
578*c8dee2aaSAndroid Build Coastguard Worker                                       .aa().rect(pixelAligned).nonAA().rect(fracRect1)
579*c8dee2aaSAndroid Build Coastguard Worker                                       .finishElements()
580*c8dee2aaSAndroid Build Coastguard Worker                              .expect().nonAA().intersect().rect(alignedIntersect).finishElements()
581*c8dee2aaSAndroid Build Coastguard Worker                              .state(ClipState::kDeviceRect)
582*c8dee2aaSAndroid Build Coastguard Worker                              .finishTest());
583*c8dee2aaSAndroid Build Coastguard Worker 
584*c8dee2aaSAndroid Build Coastguard Worker     // AA and pixel-aligned non-AA combine
585*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("aa+aligned-nonaa", kDeviceBounds)
586*c8dee2aaSAndroid Build Coastguard Worker                               .actual().intersect()
587*c8dee2aaSAndroid Build Coastguard Worker                                        .aa().rect(fracRect1).nonAA().rect(pixelAligned)
588*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
589*c8dee2aaSAndroid Build Coastguard Worker                               .expect().aa().intersect().rect(alignedIntersect).finishElements()
590*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kDeviceRect)
591*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
592*c8dee2aaSAndroid Build Coastguard Worker 
593*c8dee2aaSAndroid Build Coastguard Worker     // Other mixed AA modes do not combine
594*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("aa+nonaa", kDeviceBounds)
595*c8dee2aaSAndroid Build Coastguard Worker                               .actual().intersect()
596*c8dee2aaSAndroid Build Coastguard Worker                                        .aa().rect(fracRect1).nonAA().rect(fracRect2)
597*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
598*c8dee2aaSAndroid Build Coastguard Worker                               .expectActual()
599*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
600*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
601*c8dee2aaSAndroid Build Coastguard Worker }
602*c8dee2aaSAndroid Build Coastguard Worker 
603*c8dee2aaSAndroid Build Coastguard Worker // Tests that an intersection and a difference op do not combine, even if they would have if both
604*c8dee2aaSAndroid Build Coastguard Worker // were intersection ops.
DEF_TEST(ClipStack_DifferenceNoCombine,r)605*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(ClipStack_DifferenceNoCombine, r) {
606*c8dee2aaSAndroid Build Coastguard Worker     using ClipState = skgpu::ganesh::ClipStack::ClipState;
607*c8dee2aaSAndroid Build Coastguard Worker 
608*c8dee2aaSAndroid Build Coastguard Worker     SkRect r1 = {15.f, 14.f, 23.22f, 58.2f};
609*c8dee2aaSAndroid Build Coastguard Worker     SkRect r2 = r1.makeOffset(5.f, 8.f);
610*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(r1.intersects(r2));
611*c8dee2aaSAndroid Build Coastguard Worker 
612*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("no-combine", kDeviceBounds)
613*c8dee2aaSAndroid Build Coastguard Worker                               .actual().aa().intersect().rect(r1)
614*c8dee2aaSAndroid Build Coastguard Worker                                        .difference().rect(r2)
615*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
616*c8dee2aaSAndroid Build Coastguard Worker                               .expectActual()
617*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
618*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
619*c8dee2aaSAndroid Build Coastguard Worker }
620*c8dee2aaSAndroid Build Coastguard Worker 
621*c8dee2aaSAndroid Build Coastguard Worker // Tests that intersection of rects in the same coordinate space can still be combined, but do not
622*c8dee2aaSAndroid Build Coastguard Worker // when the spaces differ.
DEF_TEST(ClipStack_RectRectNonAxisAligned,r)623*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(ClipStack_RectRectNonAxisAligned, r) {
624*c8dee2aaSAndroid Build Coastguard Worker     using ClipState = skgpu::ganesh::ClipStack::ClipState;
625*c8dee2aaSAndroid Build Coastguard Worker 
626*c8dee2aaSAndroid Build Coastguard Worker     SkRect pixelAligned = {0, 0, 10, 10};
627*c8dee2aaSAndroid Build Coastguard Worker     SkRect fracRect1 = pixelAligned.makeOffset(5.3f, 3.7f);
628*c8dee2aaSAndroid Build Coastguard Worker     SkRect fracRect2 = {fracRect1.fLeft + 0.75f * fracRect1.width(),
629*c8dee2aaSAndroid Build Coastguard Worker                         fracRect1.fTop + 0.75f * fracRect1.height(),
630*c8dee2aaSAndroid Build Coastguard Worker                         fracRect1.fRight, fracRect1.fBottom};
631*c8dee2aaSAndroid Build Coastguard Worker 
632*c8dee2aaSAndroid Build Coastguard Worker     SkRect fracIntersect;
633*c8dee2aaSAndroid Build Coastguard Worker     SkAssertResult(fracIntersect.intersect(fracRect1, fracRect2));
634*c8dee2aaSAndroid Build Coastguard Worker 
635*c8dee2aaSAndroid Build Coastguard Worker     SkMatrix lm = SkMatrix::RotateDeg(45.f);
636*c8dee2aaSAndroid Build Coastguard Worker 
637*c8dee2aaSAndroid Build Coastguard Worker     // Both AA combine
638*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("aa", kDeviceBounds)
639*c8dee2aaSAndroid Build Coastguard Worker                               .actual().aa().intersect().localToDevice(lm)
640*c8dee2aaSAndroid Build Coastguard Worker                                        .rect(fracRect1).rect(fracRect2)
641*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
642*c8dee2aaSAndroid Build Coastguard Worker                               .expect().aa().intersect().localToDevice(lm)
643*c8dee2aaSAndroid Build Coastguard Worker                                        .rect(fracIntersect).finishElements()
644*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
645*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
646*c8dee2aaSAndroid Build Coastguard Worker 
647*c8dee2aaSAndroid Build Coastguard Worker     // Both non-AA combine
648*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("nonaa", kDeviceBounds)
649*c8dee2aaSAndroid Build Coastguard Worker                               .actual().nonAA().intersect().localToDevice(lm)
650*c8dee2aaSAndroid Build Coastguard Worker                                        .rect(fracRect1).rect(fracRect2)
651*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
652*c8dee2aaSAndroid Build Coastguard Worker                               .expect().nonAA().intersect().localToDevice(lm)
653*c8dee2aaSAndroid Build Coastguard Worker                                        .rect(fracIntersect).finishElements()
654*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
655*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
656*c8dee2aaSAndroid Build Coastguard Worker 
657*c8dee2aaSAndroid Build Coastguard Worker     // Integer-aligned coordinates under a local matrix with mixed AA don't combine, though
658*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("local-aa", kDeviceBounds)
659*c8dee2aaSAndroid Build Coastguard Worker                               .actual().intersect().localToDevice(lm)
660*c8dee2aaSAndroid Build Coastguard Worker                                        .aa().rect(pixelAligned).nonAA().rect(fracRect1)
661*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
662*c8dee2aaSAndroid Build Coastguard Worker                               .expectActual()
663*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
664*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
665*c8dee2aaSAndroid Build Coastguard Worker }
666*c8dee2aaSAndroid Build Coastguard Worker 
667*c8dee2aaSAndroid Build Coastguard Worker // Tests that intersection of two round rects can simplify to a single round rect when they have
668*c8dee2aaSAndroid Build Coastguard Worker // the same AA type.
DEF_TEST(ClipStack_RRectRRectAACombine,r)669*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(ClipStack_RRectRRectAACombine, r) {
670*c8dee2aaSAndroid Build Coastguard Worker     using ClipState = skgpu::ganesh::ClipStack::ClipState;
671*c8dee2aaSAndroid Build Coastguard Worker 
672*c8dee2aaSAndroid Build Coastguard Worker     SkRRect r1 = SkRRect::MakeRectXY(SkRect::MakeWH(12, 12), 2.f, 2.f);
673*c8dee2aaSAndroid Build Coastguard Worker     SkRRect r2 = r1.makeOffset(6.f, 6.f);
674*c8dee2aaSAndroid Build Coastguard Worker 
675*c8dee2aaSAndroid Build Coastguard Worker     SkRRect intersect = SkRRectPriv::ConservativeIntersect(r1, r2);
676*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!intersect.isEmpty());
677*c8dee2aaSAndroid Build Coastguard Worker 
678*c8dee2aaSAndroid Build Coastguard Worker     // Both AA combine
679*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("aa", kDeviceBounds)
680*c8dee2aaSAndroid Build Coastguard Worker                               .actual().aa().intersect()
681*c8dee2aaSAndroid Build Coastguard Worker                                        .rrect(r1).rrect(r2)
682*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
683*c8dee2aaSAndroid Build Coastguard Worker                               .expect().aa().intersect().rrect(intersect).finishElements()
684*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kDeviceRRect)
685*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
686*c8dee2aaSAndroid Build Coastguard Worker 
687*c8dee2aaSAndroid Build Coastguard Worker     // Both non-AA combine
688*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("nonaa", kDeviceBounds)
689*c8dee2aaSAndroid Build Coastguard Worker                               .actual().nonAA().intersect()
690*c8dee2aaSAndroid Build Coastguard Worker                                        .rrect(r1).rrect(r2)
691*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
692*c8dee2aaSAndroid Build Coastguard Worker                               .expect().nonAA().intersect().rrect(intersect).finishElements()
693*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kDeviceRRect)
694*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
695*c8dee2aaSAndroid Build Coastguard Worker 
696*c8dee2aaSAndroid Build Coastguard Worker     // Mixed do not combine
697*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("aa+nonaa", kDeviceBounds)
698*c8dee2aaSAndroid Build Coastguard Worker                               .actual().intersect()
699*c8dee2aaSAndroid Build Coastguard Worker                                        .aa().rrect(r1).nonAA().rrect(r2)
700*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
701*c8dee2aaSAndroid Build Coastguard Worker                               .expectActual()
702*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
703*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
704*c8dee2aaSAndroid Build Coastguard Worker 
705*c8dee2aaSAndroid Build Coastguard Worker     // Same AA state can combine in the same local coordinate space
706*c8dee2aaSAndroid Build Coastguard Worker     SkMatrix lm = SkMatrix::RotateDeg(45.f);
707*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("local-aa", kDeviceBounds)
708*c8dee2aaSAndroid Build Coastguard Worker                               .actual().aa().intersect().localToDevice(lm)
709*c8dee2aaSAndroid Build Coastguard Worker                                        .rrect(r1).rrect(r2)
710*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
711*c8dee2aaSAndroid Build Coastguard Worker                               .expect().aa().intersect().localToDevice(lm)
712*c8dee2aaSAndroid Build Coastguard Worker                                        .rrect(intersect).finishElements()
713*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
714*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
715*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("local-nonaa", kDeviceBounds)
716*c8dee2aaSAndroid Build Coastguard Worker                               .actual().nonAA().intersect().localToDevice(lm)
717*c8dee2aaSAndroid Build Coastguard Worker                                        .rrect(r1).rrect(r2)
718*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
719*c8dee2aaSAndroid Build Coastguard Worker                               .expect().nonAA().intersect().localToDevice(lm)
720*c8dee2aaSAndroid Build Coastguard Worker                                        .rrect(intersect).finishElements()
721*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
722*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
723*c8dee2aaSAndroid Build Coastguard Worker }
724*c8dee2aaSAndroid Build Coastguard Worker 
725*c8dee2aaSAndroid Build Coastguard Worker // Tests that intersection of a round rect and rect can simplify to a new round rect or even a rect.
DEF_TEST(ClipStack_RectRRectCombine,r)726*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(ClipStack_RectRRectCombine, r) {
727*c8dee2aaSAndroid Build Coastguard Worker     using ClipState = skgpu::ganesh::ClipStack::ClipState;
728*c8dee2aaSAndroid Build Coastguard Worker 
729*c8dee2aaSAndroid Build Coastguard Worker     SkRRect rrect = SkRRect::MakeRectXY({0, 0, 10, 10}, 2.f, 2.f);
730*c8dee2aaSAndroid Build Coastguard Worker     SkRect cutTop = {-10, -10, 10, 4};
731*c8dee2aaSAndroid Build Coastguard Worker     SkRect cutMid = {-10, 3, 10, 7};
732*c8dee2aaSAndroid Build Coastguard Worker 
733*c8dee2aaSAndroid Build Coastguard Worker     // Rect + RRect becomes a round rect with some square corners
734*c8dee2aaSAndroid Build Coastguard Worker     SkVector cutCorners[4] = {{2.f, 2.f}, {2.f, 2.f}, {0, 0}, {0, 0}};
735*c8dee2aaSAndroid Build Coastguard Worker     SkRRect cutRRect;
736*c8dee2aaSAndroid Build Coastguard Worker     cutRRect.setRectRadii({0, 0, 10, 4}, cutCorners);
737*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("still-rrect", kDeviceBounds)
738*c8dee2aaSAndroid Build Coastguard Worker                               .actual().intersect().aa().rrect(rrect).rect(cutTop).finishElements()
739*c8dee2aaSAndroid Build Coastguard Worker                               .expect().intersect().aa().rrect(cutRRect).finishElements()
740*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kDeviceRRect)
741*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
742*c8dee2aaSAndroid Build Coastguard Worker 
743*c8dee2aaSAndroid Build Coastguard Worker     // Rect + RRect becomes a rect
744*c8dee2aaSAndroid Build Coastguard Worker     SkRect cutRect = {0, 3, 10, 7};
745*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("to-rect", kDeviceBounds)
746*c8dee2aaSAndroid Build Coastguard Worker                                .actual().intersect().aa().rrect(rrect).rect(cutMid).finishElements()
747*c8dee2aaSAndroid Build Coastguard Worker                                .expect().intersect().aa().rect(cutRect).finishElements()
748*c8dee2aaSAndroid Build Coastguard Worker                                .state(ClipState::kDeviceRect)
749*c8dee2aaSAndroid Build Coastguard Worker                                .finishTest());
750*c8dee2aaSAndroid Build Coastguard Worker 
751*c8dee2aaSAndroid Build Coastguard Worker     // But they can only combine when the intersecting shape is representable as a [r]rect.
752*c8dee2aaSAndroid Build Coastguard Worker     cutRect = {0, 0, 1.5f, 5.f};
753*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("no-combine", kDeviceBounds)
754*c8dee2aaSAndroid Build Coastguard Worker                               .actual().intersect().aa().rrect(rrect).rect(cutRect).finishElements()
755*c8dee2aaSAndroid Build Coastguard Worker                               .expectActual()
756*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
757*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
758*c8dee2aaSAndroid Build Coastguard Worker }
759*c8dee2aaSAndroid Build Coastguard Worker 
760*c8dee2aaSAndroid Build Coastguard Worker // Tests that a rect shape is actually pre-clipped to the device bounds
DEF_TEST(ClipStack_RectDeviceClip,r)761*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(ClipStack_RectDeviceClip, r) {
762*c8dee2aaSAndroid Build Coastguard Worker     using ClipState = skgpu::ganesh::ClipStack::ClipState;
763*c8dee2aaSAndroid Build Coastguard Worker 
764*c8dee2aaSAndroid Build Coastguard Worker     SkRect crossesDeviceEdge = {20.f, kDeviceBounds.fTop - 13.2f,
765*c8dee2aaSAndroid Build Coastguard Worker                                 kDeviceBounds.fRight + 15.5f, 30.f};
766*c8dee2aaSAndroid Build Coastguard Worker     SkRect insideDevice = {20.f, kDeviceBounds.fTop, kDeviceBounds.fRight, 30.f};
767*c8dee2aaSAndroid Build Coastguard Worker 
768*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("device-aa-rect", kDeviceBounds)
769*c8dee2aaSAndroid Build Coastguard Worker                               .actual().intersect().aa().rect(crossesDeviceEdge).finishElements()
770*c8dee2aaSAndroid Build Coastguard Worker                               .expect().intersect().aa().rect(insideDevice).finishElements()
771*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kDeviceRect)
772*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
773*c8dee2aaSAndroid Build Coastguard Worker 
774*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("device-nonaa-rect", kDeviceBounds)
775*c8dee2aaSAndroid Build Coastguard Worker                               .actual().intersect().nonAA().rect(crossesDeviceEdge).finishElements()
776*c8dee2aaSAndroid Build Coastguard Worker                               .expect().intersect().nonAA().rect(insideDevice).finishElements()
777*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kDeviceRect)
778*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
779*c8dee2aaSAndroid Build Coastguard Worker }
780*c8dee2aaSAndroid Build Coastguard Worker 
781*c8dee2aaSAndroid Build Coastguard Worker // Tests that other shapes' bounds are contained by the device bounds, even if their shape is not.
DEF_TEST(ClipStack_ShapeDeviceBoundsClip,r)782*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(ClipStack_ShapeDeviceBoundsClip, r) {
783*c8dee2aaSAndroid Build Coastguard Worker     using ClipState = skgpu::ganesh::ClipStack::ClipState;
784*c8dee2aaSAndroid Build Coastguard Worker 
785*c8dee2aaSAndroid Build Coastguard Worker     SkRect crossesDeviceEdge = {20.f, kDeviceBounds.fTop - 13.2f,
786*c8dee2aaSAndroid Build Coastguard Worker                                 kDeviceBounds.fRight + 15.5f, 30.f};
787*c8dee2aaSAndroid Build Coastguard Worker 
788*c8dee2aaSAndroid Build Coastguard Worker     // RRect
789*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("device-rrect", kDeviceBounds)
790*c8dee2aaSAndroid Build Coastguard Worker                               .actual().intersect().aa()
791*c8dee2aaSAndroid Build Coastguard Worker                                        .rrect(SkRRect::MakeRectXY(crossesDeviceEdge, 4.f, 4.f))
792*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
793*c8dee2aaSAndroid Build Coastguard Worker                               .expectActual()
794*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kDeviceRRect)
795*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
796*c8dee2aaSAndroid Build Coastguard Worker 
797*c8dee2aaSAndroid Build Coastguard Worker     // Path
798*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("device-path", kDeviceBounds)
799*c8dee2aaSAndroid Build Coastguard Worker                               .actual().intersect().aa()
800*c8dee2aaSAndroid Build Coastguard Worker                                        .path(make_octagon(crossesDeviceEdge))
801*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
802*c8dee2aaSAndroid Build Coastguard Worker                               .expectActual()
803*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
804*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
805*c8dee2aaSAndroid Build Coastguard Worker }
806*c8dee2aaSAndroid Build Coastguard Worker 
807*c8dee2aaSAndroid Build Coastguard Worker // Tests that a simplifiable path turns into a simpler element type
DEF_TEST(ClipStack_PathSimplify,r)808*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(ClipStack_PathSimplify, r) {
809*c8dee2aaSAndroid Build Coastguard Worker     using ClipState = skgpu::ganesh::ClipStack::ClipState;
810*c8dee2aaSAndroid Build Coastguard Worker 
811*c8dee2aaSAndroid Build Coastguard Worker     // Empty, point, and line paths -> empty
812*c8dee2aaSAndroid Build Coastguard Worker     SkPath empty;
813*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("empty", kDeviceBounds)
814*c8dee2aaSAndroid Build Coastguard Worker                               .actual().path(empty).finishElements()
815*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kEmpty)
816*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
817*c8dee2aaSAndroid Build Coastguard Worker     SkPath point;
818*c8dee2aaSAndroid Build Coastguard Worker     point.moveTo({0.f, 0.f});
819*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("point", kDeviceBounds)
820*c8dee2aaSAndroid Build Coastguard Worker                               .actual().path(point).finishElements()
821*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kEmpty)
822*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
823*c8dee2aaSAndroid Build Coastguard Worker 
824*c8dee2aaSAndroid Build Coastguard Worker     SkPath line;
825*c8dee2aaSAndroid Build Coastguard Worker     line.moveTo({0.f, 0.f});
826*c8dee2aaSAndroid Build Coastguard Worker     line.lineTo({10.f, 5.f});
827*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("line", kDeviceBounds)
828*c8dee2aaSAndroid Build Coastguard Worker                               .actual().path(line).finishElements()
829*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kEmpty)
830*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
831*c8dee2aaSAndroid Build Coastguard Worker 
832*c8dee2aaSAndroid Build Coastguard Worker     // Rect path -> rect element
833*c8dee2aaSAndroid Build Coastguard Worker     SkRect rect = {0.f, 2.f, 10.f, 15.4f};
834*c8dee2aaSAndroid Build Coastguard Worker     SkPath rectPath;
835*c8dee2aaSAndroid Build Coastguard Worker     rectPath.addRect(rect);
836*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("rect", kDeviceBounds)
837*c8dee2aaSAndroid Build Coastguard Worker                               .actual().path(rectPath).finishElements()
838*c8dee2aaSAndroid Build Coastguard Worker                               .expect().rect(rect).finishElements()
839*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kDeviceRect)
840*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
841*c8dee2aaSAndroid Build Coastguard Worker 
842*c8dee2aaSAndroid Build Coastguard Worker     // Oval path -> rrect element
843*c8dee2aaSAndroid Build Coastguard Worker     SkPath ovalPath;
844*c8dee2aaSAndroid Build Coastguard Worker     ovalPath.addOval(rect);
845*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("oval", kDeviceBounds)
846*c8dee2aaSAndroid Build Coastguard Worker                               .actual().path(ovalPath).finishElements()
847*c8dee2aaSAndroid Build Coastguard Worker                               .expect().rrect(SkRRect::MakeOval(rect)).finishElements()
848*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kDeviceRRect)
849*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
850*c8dee2aaSAndroid Build Coastguard Worker 
851*c8dee2aaSAndroid Build Coastguard Worker     // RRect path -> rrect element
852*c8dee2aaSAndroid Build Coastguard Worker     SkRRect rrect = SkRRect::MakeRectXY(rect, 2.f, 2.f);
853*c8dee2aaSAndroid Build Coastguard Worker     SkPath rrectPath;
854*c8dee2aaSAndroid Build Coastguard Worker     rrectPath.addRRect(rrect);
855*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("rrect", kDeviceBounds)
856*c8dee2aaSAndroid Build Coastguard Worker                               .actual().path(rrectPath).finishElements()
857*c8dee2aaSAndroid Build Coastguard Worker                               .expect().rrect(rrect).finishElements()
858*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kDeviceRRect)
859*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
860*c8dee2aaSAndroid Build Coastguard Worker }
861*c8dee2aaSAndroid Build Coastguard Worker 
862*c8dee2aaSAndroid Build Coastguard Worker // Tests that repeated identical clip operations are idempotent
DEF_TEST(ClipStack_RepeatElement,r)863*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(ClipStack_RepeatElement, r) {
864*c8dee2aaSAndroid Build Coastguard Worker     using ClipState = skgpu::ganesh::ClipStack::ClipState;
865*c8dee2aaSAndroid Build Coastguard Worker 
866*c8dee2aaSAndroid Build Coastguard Worker     // Same rect
867*c8dee2aaSAndroid Build Coastguard Worker     SkRect rect = {5.3f, 62.f, 20.f, 85.f};
868*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("same-rects", kDeviceBounds)
869*c8dee2aaSAndroid Build Coastguard Worker                               .actual().rect(rect).rect(rect).rect(rect).finishElements()
870*c8dee2aaSAndroid Build Coastguard Worker                               .expect().rect(rect).finishElements()
871*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kDeviceRect)
872*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
873*c8dee2aaSAndroid Build Coastguard Worker     SkMatrix lm;
874*c8dee2aaSAndroid Build Coastguard Worker     lm.setRotate(30.f, rect.centerX(), rect.centerY());
875*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("same-local-rects", kDeviceBounds)
876*c8dee2aaSAndroid Build Coastguard Worker                               .actual().localToDevice(lm).rect(rect).rect(rect).rect(rect)
877*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
878*c8dee2aaSAndroid Build Coastguard Worker                               .expect().localToDevice(lm).rect(rect).finishElements()
879*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
880*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
881*c8dee2aaSAndroid Build Coastguard Worker 
882*c8dee2aaSAndroid Build Coastguard Worker     // Same rrect
883*c8dee2aaSAndroid Build Coastguard Worker     SkRRect rrect = SkRRect::MakeRectXY(rect, 5.f, 2.5f);
884*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("same-rrects", kDeviceBounds)
885*c8dee2aaSAndroid Build Coastguard Worker                               .actual().rrect(rrect).rrect(rrect).rrect(rrect).finishElements()
886*c8dee2aaSAndroid Build Coastguard Worker                               .expect().rrect(rrect).finishElements()
887*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kDeviceRRect)
888*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
889*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("same-local-rrects", kDeviceBounds)
890*c8dee2aaSAndroid Build Coastguard Worker                               .actual().localToDevice(lm).rrect(rrect).rrect(rrect).rrect(rrect)
891*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
892*c8dee2aaSAndroid Build Coastguard Worker                               .expect().localToDevice(lm).rrect(rrect).finishElements()
893*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
894*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
895*c8dee2aaSAndroid Build Coastguard Worker 
896*c8dee2aaSAndroid Build Coastguard Worker     // Same convex path, by ==
897*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("same-convex", kDeviceBounds)
898*c8dee2aaSAndroid Build Coastguard Worker                               .actual().path(make_octagon(rect)).path(make_octagon(rect))
899*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
900*c8dee2aaSAndroid Build Coastguard Worker                               .expect().path(make_octagon(rect)).finishElements()
901*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
902*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
903*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("same-local-convex", kDeviceBounds)
904*c8dee2aaSAndroid Build Coastguard Worker                               .actual().localToDevice(lm)
905*c8dee2aaSAndroid Build Coastguard Worker                                        .path(make_octagon(rect)).path(make_octagon(rect))
906*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
907*c8dee2aaSAndroid Build Coastguard Worker                               .expect().localToDevice(lm).path(make_octagon(rect))
908*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
909*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
910*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
911*c8dee2aaSAndroid Build Coastguard Worker 
912*c8dee2aaSAndroid Build Coastguard Worker     // Same complicated path by gen-id but not ==
913*c8dee2aaSAndroid Build Coastguard Worker     SkPath path; // an hour glass
914*c8dee2aaSAndroid Build Coastguard Worker     path.moveTo({0.f, 0.f});
915*c8dee2aaSAndroid Build Coastguard Worker     path.lineTo({20.f, 20.f});
916*c8dee2aaSAndroid Build Coastguard Worker     path.lineTo({0.f, 20.f});
917*c8dee2aaSAndroid Build Coastguard Worker     path.lineTo({20.f, 0.f});
918*c8dee2aaSAndroid Build Coastguard Worker     path.close();
919*c8dee2aaSAndroid Build Coastguard Worker 
920*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("same-path", kDeviceBounds)
921*c8dee2aaSAndroid Build Coastguard Worker                               .actual().path(path).path(path).path(path).finishElements()
922*c8dee2aaSAndroid Build Coastguard Worker                               .expect().path(path).finishElements()
923*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
924*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
925*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("same-local-path", kDeviceBounds)
926*c8dee2aaSAndroid Build Coastguard Worker                               .actual().localToDevice(lm)
927*c8dee2aaSAndroid Build Coastguard Worker                                        .path(path).path(path).path(path).finishElements()
928*c8dee2aaSAndroid Build Coastguard Worker                               .expect().localToDevice(lm).path(path)
929*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
930*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
931*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
932*c8dee2aaSAndroid Build Coastguard Worker }
933*c8dee2aaSAndroid Build Coastguard Worker 
934*c8dee2aaSAndroid Build Coastguard Worker // Tests that inverse-filled paths are canonicalized to a regular fill and a swapped clip op
DEF_TEST(ClipStack_InverseFilledPath,r)935*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(ClipStack_InverseFilledPath, r) {
936*c8dee2aaSAndroid Build Coastguard Worker     using ClipState = skgpu::ganesh::ClipStack::ClipState;
937*c8dee2aaSAndroid Build Coastguard Worker 
938*c8dee2aaSAndroid Build Coastguard Worker     SkRect rect = {0.f, 0.f, 16.f, 17.f};
939*c8dee2aaSAndroid Build Coastguard Worker     SkPath rectPath;
940*c8dee2aaSAndroid Build Coastguard Worker     rectPath.addRect(rect);
941*c8dee2aaSAndroid Build Coastguard Worker 
942*c8dee2aaSAndroid Build Coastguard Worker     SkPath inverseRectPath = rectPath;
943*c8dee2aaSAndroid Build Coastguard Worker     inverseRectPath.toggleInverseFillType();
944*c8dee2aaSAndroid Build Coastguard Worker 
945*c8dee2aaSAndroid Build Coastguard Worker     SkPath complexPath = make_octagon(rect);
946*c8dee2aaSAndroid Build Coastguard Worker     SkPath inverseComplexPath = complexPath;
947*c8dee2aaSAndroid Build Coastguard Worker     inverseComplexPath.toggleInverseFillType();
948*c8dee2aaSAndroid Build Coastguard Worker 
949*c8dee2aaSAndroid Build Coastguard Worker     // Inverse filled rect + intersect -> diff rect
950*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("inverse-rect-intersect", kDeviceBounds)
951*c8dee2aaSAndroid Build Coastguard Worker                               .actual().aa().intersect().path(inverseRectPath).finishElements()
952*c8dee2aaSAndroid Build Coastguard Worker                               .expect().aa().difference().rect(rect).finishElements()
953*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
954*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
955*c8dee2aaSAndroid Build Coastguard Worker 
956*c8dee2aaSAndroid Build Coastguard Worker     // Inverse filled rect + difference -> int. rect
957*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("inverse-rect-difference", kDeviceBounds)
958*c8dee2aaSAndroid Build Coastguard Worker                               .actual().aa().difference().path(inverseRectPath).finishElements()
959*c8dee2aaSAndroid Build Coastguard Worker                               .expect().aa().intersect().rect(rect).finishElements()
960*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kDeviceRect)
961*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
962*c8dee2aaSAndroid Build Coastguard Worker 
963*c8dee2aaSAndroid Build Coastguard Worker     // Inverse filled path + intersect -> diff path
964*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("inverse-path-intersect", kDeviceBounds)
965*c8dee2aaSAndroid Build Coastguard Worker                               .actual().aa().intersect().path(inverseComplexPath).finishElements()
966*c8dee2aaSAndroid Build Coastguard Worker                               .expect().aa().difference().path(complexPath).finishElements()
967*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
968*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
969*c8dee2aaSAndroid Build Coastguard Worker 
970*c8dee2aaSAndroid Build Coastguard Worker     // Inverse filled path + difference -> int. path
971*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("inverse-path-difference", kDeviceBounds)
972*c8dee2aaSAndroid Build Coastguard Worker                               .actual().aa().difference().path(inverseComplexPath).finishElements()
973*c8dee2aaSAndroid Build Coastguard Worker                               .expect().aa().intersect().path(complexPath).finishElements()
974*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
975*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
976*c8dee2aaSAndroid Build Coastguard Worker }
977*c8dee2aaSAndroid Build Coastguard Worker 
978*c8dee2aaSAndroid Build Coastguard Worker // Tests that clip operations that are offscreen either make the clip empty or stay wide open
DEF_TEST(ClipStack_Offscreen,r)979*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(ClipStack_Offscreen, r) {
980*c8dee2aaSAndroid Build Coastguard Worker     using ClipState = skgpu::ganesh::ClipStack::ClipState;
981*c8dee2aaSAndroid Build Coastguard Worker 
982*c8dee2aaSAndroid Build Coastguard Worker     SkRect offscreenRect = {kDeviceBounds.fRight + 10.f, kDeviceBounds.fTop + 20.f,
983*c8dee2aaSAndroid Build Coastguard Worker                             kDeviceBounds.fRight + 40.f, kDeviceBounds.fTop + 60.f};
984*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!offscreenRect.intersects(SkRect::Make(kDeviceBounds)));
985*c8dee2aaSAndroid Build Coastguard Worker 
986*c8dee2aaSAndroid Build Coastguard Worker     SkRRect offscreenRRect = SkRRect::MakeRectXY(offscreenRect, 5.f, 5.f);
987*c8dee2aaSAndroid Build Coastguard Worker     SkPath offscreenPath = make_octagon(offscreenRect);
988*c8dee2aaSAndroid Build Coastguard Worker 
989*c8dee2aaSAndroid Build Coastguard Worker     // Intersect -> empty
990*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("intersect-combo", kDeviceBounds)
991*c8dee2aaSAndroid Build Coastguard Worker                               .actual().aa().intersect()
992*c8dee2aaSAndroid Build Coastguard Worker                                        .rect(offscreenRect)
993*c8dee2aaSAndroid Build Coastguard Worker                                        .rrect(offscreenRRect)
994*c8dee2aaSAndroid Build Coastguard Worker                                        .path(offscreenPath)
995*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
996*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kEmpty)
997*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
998*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("intersect-rect", kDeviceBounds)
999*c8dee2aaSAndroid Build Coastguard Worker                               .actual().aa().intersect()
1000*c8dee2aaSAndroid Build Coastguard Worker                                        .rect(offscreenRect)
1001*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1002*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kEmpty)
1003*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1004*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("intersect-rrect", kDeviceBounds)
1005*c8dee2aaSAndroid Build Coastguard Worker                               .actual().aa().intersect()
1006*c8dee2aaSAndroid Build Coastguard Worker                                        .rrect(offscreenRRect)
1007*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1008*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kEmpty)
1009*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1010*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("intersect-path", kDeviceBounds)
1011*c8dee2aaSAndroid Build Coastguard Worker                               .actual().aa().intersect()
1012*c8dee2aaSAndroid Build Coastguard Worker                                        .path(offscreenPath)
1013*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1014*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kEmpty)
1015*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1016*c8dee2aaSAndroid Build Coastguard Worker 
1017*c8dee2aaSAndroid Build Coastguard Worker     // Difference -> wide open
1018*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("difference-combo", kDeviceBounds)
1019*c8dee2aaSAndroid Build Coastguard Worker                               .actual().aa().difference()
1020*c8dee2aaSAndroid Build Coastguard Worker                                        .rect(offscreenRect)
1021*c8dee2aaSAndroid Build Coastguard Worker                                        .rrect(offscreenRRect)
1022*c8dee2aaSAndroid Build Coastguard Worker                                        .path(offscreenPath)
1023*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1024*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kWideOpen)
1025*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1026*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("difference-rect", kDeviceBounds)
1027*c8dee2aaSAndroid Build Coastguard Worker                               .actual().aa().difference()
1028*c8dee2aaSAndroid Build Coastguard Worker                                        .rect(offscreenRect)
1029*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1030*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kWideOpen)
1031*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1032*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("difference-rrect", kDeviceBounds)
1033*c8dee2aaSAndroid Build Coastguard Worker                               .actual().aa().difference()
1034*c8dee2aaSAndroid Build Coastguard Worker                                        .rrect(offscreenRRect)
1035*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1036*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kWideOpen)
1037*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1038*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("difference-path", kDeviceBounds)
1039*c8dee2aaSAndroid Build Coastguard Worker                               .actual().aa().difference()
1040*c8dee2aaSAndroid Build Coastguard Worker                                        .path(offscreenPath)
1041*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1042*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kWideOpen)
1043*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1044*c8dee2aaSAndroid Build Coastguard Worker }
1045*c8dee2aaSAndroid Build Coastguard Worker 
1046*c8dee2aaSAndroid Build Coastguard Worker // Tests that an empty shape updates the clip state directly without needing an element
DEF_TEST(ClipStack_EmptyShape,r)1047*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(ClipStack_EmptyShape, r) {
1048*c8dee2aaSAndroid Build Coastguard Worker     using ClipState = skgpu::ganesh::ClipStack::ClipState;
1049*c8dee2aaSAndroid Build Coastguard Worker 
1050*c8dee2aaSAndroid Build Coastguard Worker     // Intersect -> empty
1051*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("empty-intersect", kDeviceBounds)
1052*c8dee2aaSAndroid Build Coastguard Worker                               .actual().intersect().rect(SkRect::MakeEmpty()).finishElements()
1053*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kEmpty)
1054*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1055*c8dee2aaSAndroid Build Coastguard Worker 
1056*c8dee2aaSAndroid Build Coastguard Worker     // Difference -> no-op
1057*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("empty-difference", kDeviceBounds)
1058*c8dee2aaSAndroid Build Coastguard Worker                               .actual().difference().rect(SkRect::MakeEmpty()).finishElements()
1059*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kWideOpen)
1060*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1061*c8dee2aaSAndroid Build Coastguard Worker 
1062*c8dee2aaSAndroid Build Coastguard Worker     SkRRect rrect = SkRRect::MakeRectXY({4.f, 10.f, 16.f, 32.f}, 2.f, 2.f);
1063*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("noop-difference", kDeviceBounds)
1064*c8dee2aaSAndroid Build Coastguard Worker                               .actual().difference().rrect(rrect).rect(SkRect::MakeEmpty())
1065*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1066*c8dee2aaSAndroid Build Coastguard Worker                               .expect().difference().rrect(rrect).finishElements()
1067*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
1068*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1069*c8dee2aaSAndroid Build Coastguard Worker }
1070*c8dee2aaSAndroid Build Coastguard Worker 
1071*c8dee2aaSAndroid Build Coastguard Worker // Tests that sufficiently large difference operations can shrink the conservative bounds
DEF_TEST(ClipStack_DifferenceBounds,r)1072*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(ClipStack_DifferenceBounds, r) {
1073*c8dee2aaSAndroid Build Coastguard Worker     using ClipState = skgpu::ganesh::ClipStack::ClipState;
1074*c8dee2aaSAndroid Build Coastguard Worker 
1075*c8dee2aaSAndroid Build Coastguard Worker     SkRect rightSide = {50.f, -10.f, 2.f * kDeviceBounds.fRight, kDeviceBounds.fBottom + 10.f};
1076*c8dee2aaSAndroid Build Coastguard Worker     SkRect clipped = rightSide;
1077*c8dee2aaSAndroid Build Coastguard Worker     SkAssertResult(clipped.intersect(SkRect::Make(kDeviceBounds)));
1078*c8dee2aaSAndroid Build Coastguard Worker 
1079*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("difference-cut", kDeviceBounds)
1080*c8dee2aaSAndroid Build Coastguard Worker                               .actual().nonAA().difference().rect(rightSide).finishElements()
1081*c8dee2aaSAndroid Build Coastguard Worker                               .expect().nonAA().difference().rect(clipped).finishElements()
1082*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
1083*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1084*c8dee2aaSAndroid Build Coastguard Worker }
1085*c8dee2aaSAndroid Build Coastguard Worker 
1086*c8dee2aaSAndroid Build Coastguard Worker // Tests that intersections can combine even if there's a difference operation in the middle
DEF_TEST(ClipStack_NoDifferenceInterference,r)1087*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(ClipStack_NoDifferenceInterference, r) {
1088*c8dee2aaSAndroid Build Coastguard Worker     using ClipState = skgpu::ganesh::ClipStack::ClipState;
1089*c8dee2aaSAndroid Build Coastguard Worker 
1090*c8dee2aaSAndroid Build Coastguard Worker     SkRect intR1 = {0.f, 0.f, 30.f, 30.f};
1091*c8dee2aaSAndroid Build Coastguard Worker     SkRect intR2 = {15.f, 15.f, 45.f, 45.f};
1092*c8dee2aaSAndroid Build Coastguard Worker     SkRect intCombo = {15.f, 15.f, 30.f, 30.f};
1093*c8dee2aaSAndroid Build Coastguard Worker     SkRect diff = {20.f, 6.f, 50.f, 50.f};
1094*c8dee2aaSAndroid Build Coastguard Worker 
1095*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("cross-diff-combine", kDeviceBounds)
1096*c8dee2aaSAndroid Build Coastguard Worker                               .actual().rect(intR1, GrAA::kYes, SkClipOp::kIntersect)
1097*c8dee2aaSAndroid Build Coastguard Worker                                        .rect(diff, GrAA::kYes, SkClipOp::kDifference)
1098*c8dee2aaSAndroid Build Coastguard Worker                                        .rect(intR2, GrAA::kYes, SkClipOp::kIntersect)
1099*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1100*c8dee2aaSAndroid Build Coastguard Worker                               .expect().rect(intCombo, GrAA::kYes, SkClipOp::kIntersect)
1101*c8dee2aaSAndroid Build Coastguard Worker                                        .rect(diff, GrAA::kYes, SkClipOp::kDifference)
1102*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1103*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
1104*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1105*c8dee2aaSAndroid Build Coastguard Worker }
1106*c8dee2aaSAndroid Build Coastguard Worker 
1107*c8dee2aaSAndroid Build Coastguard Worker // Tests that multiple path operations are all recorded, but not otherwise consolidated
DEF_TEST(ClipStack_MultiplePaths,r)1108*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(ClipStack_MultiplePaths, r) {
1109*c8dee2aaSAndroid Build Coastguard Worker     using ClipState = skgpu::ganesh::ClipStack::ClipState;
1110*c8dee2aaSAndroid Build Coastguard Worker 
1111*c8dee2aaSAndroid Build Coastguard Worker     // Chosen to be greater than the number of inline-allocated elements and save records of the
1112*c8dee2aaSAndroid Build Coastguard Worker     // ClipStack so that we test heap allocation as well.
1113*c8dee2aaSAndroid Build Coastguard Worker     static constexpr int kNumOps = 16;
1114*c8dee2aaSAndroid Build Coastguard Worker 
1115*c8dee2aaSAndroid Build Coastguard Worker     auto b = TestCase::Build("many-paths-difference", kDeviceBounds);
1116*c8dee2aaSAndroid Build Coastguard Worker     SkRect d = {0.f, 0.f, 12.f, 12.f};
1117*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 0; i < kNumOps; ++i) {
1118*c8dee2aaSAndroid Build Coastguard Worker         b.actual().path(make_octagon(d), GrAA::kNo, SkClipOp::kDifference);
1119*c8dee2aaSAndroid Build Coastguard Worker 
1120*c8dee2aaSAndroid Build Coastguard Worker         d.offset(15.f, 0.f);
1121*c8dee2aaSAndroid Build Coastguard Worker         if (d.fRight > kDeviceBounds.fRight) {
1122*c8dee2aaSAndroid Build Coastguard Worker             d.fLeft = 0.f;
1123*c8dee2aaSAndroid Build Coastguard Worker             d.fRight = 12.f;
1124*c8dee2aaSAndroid Build Coastguard Worker             d.offset(0.f, 15.f);
1125*c8dee2aaSAndroid Build Coastguard Worker         }
1126*c8dee2aaSAndroid Build Coastguard Worker     }
1127*c8dee2aaSAndroid Build Coastguard Worker 
1128*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, b.expectActual()
1129*c8dee2aaSAndroid Build Coastguard Worker                       .state(ClipState::kComplex)
1130*c8dee2aaSAndroid Build Coastguard Worker                       .finishTest());
1131*c8dee2aaSAndroid Build Coastguard Worker 
1132*c8dee2aaSAndroid Build Coastguard Worker     b = TestCase::Build("many-paths-intersect", kDeviceBounds);
1133*c8dee2aaSAndroid Build Coastguard Worker     d = {0.f, 0.f, 12.f, 12.f};
1134*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 0; i < kNumOps; ++i) {
1135*c8dee2aaSAndroid Build Coastguard Worker         b.actual().path(make_octagon(d), GrAA::kYes, SkClipOp::kIntersect);
1136*c8dee2aaSAndroid Build Coastguard Worker         d.offset(0.01f, 0.01f);
1137*c8dee2aaSAndroid Build Coastguard Worker     }
1138*c8dee2aaSAndroid Build Coastguard Worker 
1139*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, b.expectActual()
1140*c8dee2aaSAndroid Build Coastguard Worker                       .state(ClipState::kComplex)
1141*c8dee2aaSAndroid Build Coastguard Worker                       .finishTest());
1142*c8dee2aaSAndroid Build Coastguard Worker }
1143*c8dee2aaSAndroid Build Coastguard Worker 
1144*c8dee2aaSAndroid Build Coastguard Worker // Tests that a single rect is treated as kDeviceRect state when it's axis-aligned and intersect.
DEF_TEST(ClipStack_DeviceRect,r)1145*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(ClipStack_DeviceRect, r) {
1146*c8dee2aaSAndroid Build Coastguard Worker     using ClipState = skgpu::ganesh::ClipStack::ClipState;
1147*c8dee2aaSAndroid Build Coastguard Worker 
1148*c8dee2aaSAndroid Build Coastguard Worker     // Axis-aligned + intersect -> kDeviceRect
1149*c8dee2aaSAndroid Build Coastguard Worker     SkRect rect = {0, 0, 20, 20};
1150*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("device-rect", kDeviceBounds)
1151*c8dee2aaSAndroid Build Coastguard Worker                               .actual().intersect().aa().rect(rect).finishElements()
1152*c8dee2aaSAndroid Build Coastguard Worker                               .expectActual()
1153*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kDeviceRect)
1154*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1155*c8dee2aaSAndroid Build Coastguard Worker 
1156*c8dee2aaSAndroid Build Coastguard Worker     // Not axis-aligned -> kComplex
1157*c8dee2aaSAndroid Build Coastguard Worker     SkMatrix lm = SkMatrix::RotateDeg(15.f);
1158*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("unaligned-rect", kDeviceBounds)
1159*c8dee2aaSAndroid Build Coastguard Worker                               .actual().localToDevice(lm).intersect().aa().rect(rect)
1160*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1161*c8dee2aaSAndroid Build Coastguard Worker                               .expectActual()
1162*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
1163*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1164*c8dee2aaSAndroid Build Coastguard Worker 
1165*c8dee2aaSAndroid Build Coastguard Worker     // Not intersect -> kComplex
1166*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("diff-rect", kDeviceBounds)
1167*c8dee2aaSAndroid Build Coastguard Worker                               .actual().difference().aa().rect(rect).finishElements()
1168*c8dee2aaSAndroid Build Coastguard Worker                               .expectActual()
1169*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
1170*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1171*c8dee2aaSAndroid Build Coastguard Worker }
1172*c8dee2aaSAndroid Build Coastguard Worker 
1173*c8dee2aaSAndroid Build Coastguard Worker // Tests that a single rrect is treated as kDeviceRRect state when it's axis-aligned and intersect.
DEF_TEST(ClipStack_DeviceRRect,r)1174*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(ClipStack_DeviceRRect, r) {
1175*c8dee2aaSAndroid Build Coastguard Worker     using ClipState = skgpu::ganesh::ClipStack::ClipState;
1176*c8dee2aaSAndroid Build Coastguard Worker 
1177*c8dee2aaSAndroid Build Coastguard Worker     // Axis-aligned + intersect -> kDeviceRRect
1178*c8dee2aaSAndroid Build Coastguard Worker     SkRect rect = {0, 0, 20, 20};
1179*c8dee2aaSAndroid Build Coastguard Worker     SkRRect rrect = SkRRect::MakeRectXY(rect, 5.f, 5.f);
1180*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("device-rrect", kDeviceBounds)
1181*c8dee2aaSAndroid Build Coastguard Worker                               .actual().intersect().aa().rrect(rrect).finishElements()
1182*c8dee2aaSAndroid Build Coastguard Worker                               .expectActual()
1183*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kDeviceRRect)
1184*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1185*c8dee2aaSAndroid Build Coastguard Worker 
1186*c8dee2aaSAndroid Build Coastguard Worker     // Not axis-aligned -> kComplex
1187*c8dee2aaSAndroid Build Coastguard Worker     SkMatrix lm = SkMatrix::RotateDeg(15.f);
1188*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("unaligned-rrect", kDeviceBounds)
1189*c8dee2aaSAndroid Build Coastguard Worker                               .actual().localToDevice(lm).intersect().aa().rrect(rrect)
1190*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1191*c8dee2aaSAndroid Build Coastguard Worker                               .expectActual()
1192*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
1193*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1194*c8dee2aaSAndroid Build Coastguard Worker 
1195*c8dee2aaSAndroid Build Coastguard Worker     // Not intersect -> kComplex
1196*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("diff-rrect", kDeviceBounds)
1197*c8dee2aaSAndroid Build Coastguard Worker                               .actual().difference().aa().rrect(rrect).finishElements()
1198*c8dee2aaSAndroid Build Coastguard Worker                               .expectActual()
1199*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
1200*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1201*c8dee2aaSAndroid Build Coastguard Worker }
1202*c8dee2aaSAndroid Build Coastguard Worker 
1203*c8dee2aaSAndroid Build Coastguard Worker // Tests that scale+translate matrices are pre-applied to rects and rrects, which also then allows
1204*c8dee2aaSAndroid Build Coastguard Worker // elements with different scale+translate matrices to be consolidated as if they were in the same
1205*c8dee2aaSAndroid Build Coastguard Worker // coordinate space.
DEF_TEST(ClipStack_ScaleTranslate,r)1206*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(ClipStack_ScaleTranslate, r) {
1207*c8dee2aaSAndroid Build Coastguard Worker     using ClipState = skgpu::ganesh::ClipStack::ClipState;
1208*c8dee2aaSAndroid Build Coastguard Worker 
1209*c8dee2aaSAndroid Build Coastguard Worker     SkMatrix lm = SkMatrix::Scale(2.f, 4.f);
1210*c8dee2aaSAndroid Build Coastguard Worker     lm.postTranslate(15.5f, 14.3f);
1211*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(lm.preservesAxisAlignment() && lm.isScaleTranslate());
1212*c8dee2aaSAndroid Build Coastguard Worker 
1213*c8dee2aaSAndroid Build Coastguard Worker     // Rect -> matrix is applied up front
1214*c8dee2aaSAndroid Build Coastguard Worker     SkRect rect = {0.f, 0.f, 10.f, 10.f};
1215*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("st+rect", kDeviceBounds)
1216*c8dee2aaSAndroid Build Coastguard Worker                               .actual().rect(rect, lm, GrAA::kYes, SkClipOp::kIntersect)
1217*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1218*c8dee2aaSAndroid Build Coastguard Worker                               .expect().rect(lm.mapRect(rect), GrAA::kYes, SkClipOp::kIntersect)
1219*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1220*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kDeviceRect)
1221*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1222*c8dee2aaSAndroid Build Coastguard Worker 
1223*c8dee2aaSAndroid Build Coastguard Worker     // RRect -> matrix is applied up front
1224*c8dee2aaSAndroid Build Coastguard Worker     SkRRect localRRect = SkRRect::MakeRectXY(rect, 2.f, 2.f);
1225*c8dee2aaSAndroid Build Coastguard Worker     SkRRect deviceRRect;
1226*c8dee2aaSAndroid Build Coastguard Worker     SkAssertResult(localRRect.transform(lm, &deviceRRect));
1227*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("st+rrect", kDeviceBounds)
1228*c8dee2aaSAndroid Build Coastguard Worker                               .actual().rrect(localRRect, lm, GrAA::kYes, SkClipOp::kIntersect)
1229*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1230*c8dee2aaSAndroid Build Coastguard Worker                               .expect().rrect(deviceRRect, GrAA::kYes, SkClipOp::kIntersect)
1231*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1232*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kDeviceRRect)
1233*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1234*c8dee2aaSAndroid Build Coastguard Worker 
1235*c8dee2aaSAndroid Build Coastguard Worker     // Path -> matrix is NOT applied
1236*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("st+path", kDeviceBounds)
1237*c8dee2aaSAndroid Build Coastguard Worker                               .actual().intersect().localToDevice(lm).path(make_octagon(rect))
1238*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1239*c8dee2aaSAndroid Build Coastguard Worker                               .expectActual()
1240*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
1241*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1242*c8dee2aaSAndroid Build Coastguard Worker }
1243*c8dee2aaSAndroid Build Coastguard Worker 
1244*c8dee2aaSAndroid Build Coastguard Worker // Tests that rect-stays-rect matrices that are not scale+translate matrices are pre-applied.
DEF_TEST(ClipStack_PreserveAxisAlignment,r)1245*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(ClipStack_PreserveAxisAlignment, r) {
1246*c8dee2aaSAndroid Build Coastguard Worker     using ClipState = skgpu::ganesh::ClipStack::ClipState;
1247*c8dee2aaSAndroid Build Coastguard Worker 
1248*c8dee2aaSAndroid Build Coastguard Worker     SkMatrix lm = SkMatrix::RotateDeg(90.f);
1249*c8dee2aaSAndroid Build Coastguard Worker     lm.postTranslate(15.5f, 14.3f);
1250*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(lm.preservesAxisAlignment() && !lm.isScaleTranslate());
1251*c8dee2aaSAndroid Build Coastguard Worker 
1252*c8dee2aaSAndroid Build Coastguard Worker     // Rect -> matrix is applied up front
1253*c8dee2aaSAndroid Build Coastguard Worker     SkRect rect = {0.f, 0.f, 10.f, 10.f};
1254*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("r90+rect", kDeviceBounds)
1255*c8dee2aaSAndroid Build Coastguard Worker                               .actual().rect(rect, lm, GrAA::kYes, SkClipOp::kIntersect)
1256*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1257*c8dee2aaSAndroid Build Coastguard Worker                               .expect().rect(lm.mapRect(rect), GrAA::kYes, SkClipOp::kIntersect)
1258*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1259*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kDeviceRect)
1260*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1261*c8dee2aaSAndroid Build Coastguard Worker 
1262*c8dee2aaSAndroid Build Coastguard Worker     // RRect -> matrix is applied up front
1263*c8dee2aaSAndroid Build Coastguard Worker     SkRRect localRRect = SkRRect::MakeRectXY(rect, 2.f, 2.f);
1264*c8dee2aaSAndroid Build Coastguard Worker     SkRRect deviceRRect;
1265*c8dee2aaSAndroid Build Coastguard Worker     SkAssertResult(localRRect.transform(lm, &deviceRRect));
1266*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("r90+rrect", kDeviceBounds)
1267*c8dee2aaSAndroid Build Coastguard Worker                               .actual().rrect(localRRect, lm, GrAA::kYes, SkClipOp::kIntersect)
1268*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1269*c8dee2aaSAndroid Build Coastguard Worker                               .expect().rrect(deviceRRect, GrAA::kYes, SkClipOp::kIntersect)
1270*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1271*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kDeviceRRect)
1272*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1273*c8dee2aaSAndroid Build Coastguard Worker 
1274*c8dee2aaSAndroid Build Coastguard Worker     // Path -> matrix is NOT applied
1275*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("r90+path", kDeviceBounds)
1276*c8dee2aaSAndroid Build Coastguard Worker                               .actual().intersect().localToDevice(lm).path(make_octagon(rect))
1277*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1278*c8dee2aaSAndroid Build Coastguard Worker                               .expectActual()
1279*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
1280*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1281*c8dee2aaSAndroid Build Coastguard Worker }
1282*c8dee2aaSAndroid Build Coastguard Worker 
1283*c8dee2aaSAndroid Build Coastguard Worker // Tests that a convex path element can contain a rect or round rect, allowing the stack to be
1284*c8dee2aaSAndroid Build Coastguard Worker // simplified
DEF_TEST(ClipStack_ConvexPathContains,r)1285*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(ClipStack_ConvexPathContains, r) {
1286*c8dee2aaSAndroid Build Coastguard Worker     using ClipState = skgpu::ganesh::ClipStack::ClipState;
1287*c8dee2aaSAndroid Build Coastguard Worker 
1288*c8dee2aaSAndroid Build Coastguard Worker     SkRect rect = {15.f, 15.f, 30.f, 30.f};
1289*c8dee2aaSAndroid Build Coastguard Worker     SkRRect rrect = SkRRect::MakeRectXY(rect, 5.f, 5.f);
1290*c8dee2aaSAndroid Build Coastguard Worker     SkPath bigPath = make_octagon(rect.makeOutset(10.f, 10.f), 5.f, 5.f);
1291*c8dee2aaSAndroid Build Coastguard Worker 
1292*c8dee2aaSAndroid Build Coastguard Worker     // Intersect -> path element isn't kept
1293*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("convex+rect-intersect", kDeviceBounds)
1294*c8dee2aaSAndroid Build Coastguard Worker                               .actual().aa().intersect().rect(rect).path(bigPath).finishElements()
1295*c8dee2aaSAndroid Build Coastguard Worker                               .expect().aa().intersect().rect(rect).finishElements()
1296*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kDeviceRect)
1297*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1298*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("convex+rrect-intersect", kDeviceBounds)
1299*c8dee2aaSAndroid Build Coastguard Worker                               .actual().aa().intersect().rrect(rrect).path(bigPath).finishElements()
1300*c8dee2aaSAndroid Build Coastguard Worker                               .expect().aa().intersect().rrect(rrect).finishElements()
1301*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kDeviceRRect)
1302*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1303*c8dee2aaSAndroid Build Coastguard Worker 
1304*c8dee2aaSAndroid Build Coastguard Worker     // Difference -> path element is the only one left
1305*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("convex+rect-difference", kDeviceBounds)
1306*c8dee2aaSAndroid Build Coastguard Worker                               .actual().aa().difference().rect(rect).path(bigPath).finishElements()
1307*c8dee2aaSAndroid Build Coastguard Worker                               .expect().aa().difference().path(bigPath).finishElements()
1308*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
1309*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1310*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("convex+rrect-difference", kDeviceBounds)
1311*c8dee2aaSAndroid Build Coastguard Worker                               .actual().aa().difference().rrect(rrect).path(bigPath)
1312*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1313*c8dee2aaSAndroid Build Coastguard Worker                               .expect().aa().difference().path(bigPath).finishElements()
1314*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
1315*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1316*c8dee2aaSAndroid Build Coastguard Worker 
1317*c8dee2aaSAndroid Build Coastguard Worker     // Intersect small shape + difference big path -> empty
1318*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("convex-diff+rect-int", kDeviceBounds)
1319*c8dee2aaSAndroid Build Coastguard Worker                               .actual().aa().intersect().rect(rect)
1320*c8dee2aaSAndroid Build Coastguard Worker                                        .difference().path(bigPath).finishElements()
1321*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kEmpty)
1322*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1323*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("convex-diff+rrect-int", kDeviceBounds)
1324*c8dee2aaSAndroid Build Coastguard Worker                               .actual().aa().intersect().rrect(rrect)
1325*c8dee2aaSAndroid Build Coastguard Worker                                        .difference().path(bigPath).finishElements()
1326*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kEmpty)
1327*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1328*c8dee2aaSAndroid Build Coastguard Worker 
1329*c8dee2aaSAndroid Build Coastguard Worker     // Diff small shape + intersect big path -> both
1330*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("convex-int+rect-diff", kDeviceBounds)
1331*c8dee2aaSAndroid Build Coastguard Worker                               .actual().aa().intersect().path(bigPath).difference().rect(rect)
1332*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1333*c8dee2aaSAndroid Build Coastguard Worker                               .expectActual()
1334*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
1335*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1336*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("convex-int+rrect-diff", kDeviceBounds)
1337*c8dee2aaSAndroid Build Coastguard Worker                               .actual().aa().intersect().path(bigPath).difference().rrect(rrect)
1338*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1339*c8dee2aaSAndroid Build Coastguard Worker                               .expectActual()
1340*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
1341*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1342*c8dee2aaSAndroid Build Coastguard Worker }
1343*c8dee2aaSAndroid Build Coastguard Worker 
1344*c8dee2aaSAndroid Build Coastguard Worker // Tests that rects/rrects in different coordinate spaces can be consolidated when one is fully
1345*c8dee2aaSAndroid Build Coastguard Worker // contained by the other.
DEF_TEST(ClipStack_NonAxisAlignedContains,r)1346*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(ClipStack_NonAxisAlignedContains, r) {
1347*c8dee2aaSAndroid Build Coastguard Worker     using ClipState = skgpu::ganesh::ClipStack::ClipState;
1348*c8dee2aaSAndroid Build Coastguard Worker 
1349*c8dee2aaSAndroid Build Coastguard Worker     SkMatrix lm1 = SkMatrix::RotateDeg(45.f);
1350*c8dee2aaSAndroid Build Coastguard Worker     SkRect bigR = {-20.f, -20.f, 20.f, 20.f};
1351*c8dee2aaSAndroid Build Coastguard Worker     SkRRect bigRR = SkRRect::MakeRectXY(bigR, 1.f, 1.f);
1352*c8dee2aaSAndroid Build Coastguard Worker 
1353*c8dee2aaSAndroid Build Coastguard Worker     SkMatrix lm2 = SkMatrix::RotateDeg(-45.f);
1354*c8dee2aaSAndroid Build Coastguard Worker     SkRect smR = {-10.f, -10.f, 10.f, 10.f};
1355*c8dee2aaSAndroid Build Coastguard Worker     SkRRect smRR = SkRRect::MakeRectXY(smR, 1.f, 1.f);
1356*c8dee2aaSAndroid Build Coastguard Worker 
1357*c8dee2aaSAndroid Build Coastguard Worker     // I+I should select the smaller 2nd shape (r2 or rr2)
1358*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("rect-rect-ii", kDeviceBounds)
1359*c8dee2aaSAndroid Build Coastguard Worker                               .actual().rect(bigR, lm1, GrAA::kYes, SkClipOp::kIntersect)
1360*c8dee2aaSAndroid Build Coastguard Worker                                        .rect(smR, lm2, GrAA::kYes, SkClipOp::kIntersect)
1361*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1362*c8dee2aaSAndroid Build Coastguard Worker                               .expect().rect(smR, lm2, GrAA::kYes, SkClipOp::kIntersect)
1363*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1364*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
1365*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1366*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("rrect-rrect-ii", kDeviceBounds)
1367*c8dee2aaSAndroid Build Coastguard Worker                               .actual().rrect(bigRR, lm1, GrAA::kYes, SkClipOp::kIntersect)
1368*c8dee2aaSAndroid Build Coastguard Worker                                        .rrect(smRR, lm2, GrAA::kYes, SkClipOp::kIntersect)
1369*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1370*c8dee2aaSAndroid Build Coastguard Worker                               .expect().rrect(smRR, lm2, GrAA::kYes, SkClipOp::kIntersect)
1371*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1372*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
1373*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1374*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("rect-rrect-ii", kDeviceBounds)
1375*c8dee2aaSAndroid Build Coastguard Worker                               .actual().rect(bigR, lm1, GrAA::kYes, SkClipOp::kIntersect)
1376*c8dee2aaSAndroid Build Coastguard Worker                                        .rrect(smRR, lm2, GrAA::kYes, SkClipOp::kIntersect)
1377*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1378*c8dee2aaSAndroid Build Coastguard Worker                               .expect().rrect(smRR, lm2, GrAA::kYes, SkClipOp::kIntersect)
1379*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1380*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
1381*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1382*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("rrect-rect-ii", kDeviceBounds)
1383*c8dee2aaSAndroid Build Coastguard Worker                               .actual().rrect(bigRR, lm1, GrAA::kYes, SkClipOp::kIntersect)
1384*c8dee2aaSAndroid Build Coastguard Worker                                        .rect(smR, lm2, GrAA::kYes, SkClipOp::kIntersect)
1385*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1386*c8dee2aaSAndroid Build Coastguard Worker                               .expect().rect(smR, lm2, GrAA::kYes, SkClipOp::kIntersect)
1387*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1388*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
1389*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1390*c8dee2aaSAndroid Build Coastguard Worker 
1391*c8dee2aaSAndroid Build Coastguard Worker     // D+D should select the larger shape (r1 or rr1)
1392*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("rect-rect-dd", kDeviceBounds)
1393*c8dee2aaSAndroid Build Coastguard Worker                               .actual().rect(bigR, lm1, GrAA::kYes, SkClipOp::kDifference)
1394*c8dee2aaSAndroid Build Coastguard Worker                                        .rect(smR, lm2, GrAA::kYes, SkClipOp::kDifference)
1395*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1396*c8dee2aaSAndroid Build Coastguard Worker                               .expect().rect(bigR, lm1, GrAA::kYes, SkClipOp::kDifference)
1397*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1398*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
1399*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1400*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("rrect-rrect-dd", kDeviceBounds)
1401*c8dee2aaSAndroid Build Coastguard Worker                               .actual().rrect(bigRR, lm1, GrAA::kYes, SkClipOp::kDifference)
1402*c8dee2aaSAndroid Build Coastguard Worker                                        .rrect(smRR, lm2, GrAA::kYes, SkClipOp::kDifference)
1403*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1404*c8dee2aaSAndroid Build Coastguard Worker                               .expect().rrect(bigRR, lm1, GrAA::kYes, SkClipOp::kDifference)
1405*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1406*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
1407*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1408*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("rect-rrect-dd", kDeviceBounds)
1409*c8dee2aaSAndroid Build Coastguard Worker                               .actual().rect(bigR, lm1, GrAA::kYes, SkClipOp::kDifference)
1410*c8dee2aaSAndroid Build Coastguard Worker                                        .rrect(smRR, lm2, GrAA::kYes, SkClipOp::kDifference)
1411*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1412*c8dee2aaSAndroid Build Coastguard Worker                               .expect().rect(bigR, lm1, GrAA::kYes, SkClipOp::kDifference)
1413*c8dee2aaSAndroid Build Coastguard Worker                                          .finishElements()
1414*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
1415*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1416*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("rrect-rect-dd", kDeviceBounds)
1417*c8dee2aaSAndroid Build Coastguard Worker                               .actual().rrect(bigRR, lm1, GrAA::kYes, SkClipOp::kDifference)
1418*c8dee2aaSAndroid Build Coastguard Worker                                        .rect(smR, lm2, GrAA::kYes, SkClipOp::kDifference)
1419*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1420*c8dee2aaSAndroid Build Coastguard Worker                               .expect().rrect(bigRR, lm1, GrAA::kYes, SkClipOp::kDifference)
1421*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1422*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
1423*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1424*c8dee2aaSAndroid Build Coastguard Worker 
1425*c8dee2aaSAndroid Build Coastguard Worker     // D(1)+I(2) should result in empty
1426*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("rectD-rectI", kDeviceBounds)
1427*c8dee2aaSAndroid Build Coastguard Worker                               .actual().rect(bigR, lm1, GrAA::kYes, SkClipOp::kDifference)
1428*c8dee2aaSAndroid Build Coastguard Worker                                        .rect(smR, lm2, GrAA::kYes, SkClipOp::kIntersect)
1429*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1430*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kEmpty)
1431*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1432*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("rrectD-rrectI", kDeviceBounds)
1433*c8dee2aaSAndroid Build Coastguard Worker                               .actual().rrect(bigRR, lm1, GrAA::kYes, SkClipOp::kDifference)
1434*c8dee2aaSAndroid Build Coastguard Worker                                        .rrect(smRR, lm2, GrAA::kYes, SkClipOp::kIntersect)
1435*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1436*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kEmpty)
1437*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1438*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("rectD-rrectI", kDeviceBounds)
1439*c8dee2aaSAndroid Build Coastguard Worker                               .actual().rect(bigR, lm1, GrAA::kYes, SkClipOp::kDifference)
1440*c8dee2aaSAndroid Build Coastguard Worker                                        .rrect(smRR, lm2, GrAA::kYes, SkClipOp::kIntersect)
1441*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1442*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kEmpty)
1443*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1444*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("rrectD-rectI", kDeviceBounds)
1445*c8dee2aaSAndroid Build Coastguard Worker                               .actual().rrect(bigRR, lm1, GrAA::kYes, SkClipOp::kDifference)
1446*c8dee2aaSAndroid Build Coastguard Worker                                        .rect(smR, lm2, GrAA::kYes, SkClipOp::kIntersect)
1447*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1448*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kEmpty)
1449*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1450*c8dee2aaSAndroid Build Coastguard Worker 
1451*c8dee2aaSAndroid Build Coastguard Worker     // I(1)+D(2) should result in both shapes
1452*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("rectI+rectD", kDeviceBounds)
1453*c8dee2aaSAndroid Build Coastguard Worker                               .actual().rect(bigR, lm1, GrAA::kYes, SkClipOp::kIntersect)
1454*c8dee2aaSAndroid Build Coastguard Worker                                        .rect(smR, lm2, GrAA::kYes, SkClipOp::kDifference)
1455*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1456*c8dee2aaSAndroid Build Coastguard Worker                               .expectActual()
1457*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
1458*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1459*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("rrectI+rrectD", kDeviceBounds)
1460*c8dee2aaSAndroid Build Coastguard Worker                               .actual().rrect(bigRR, lm1, GrAA::kYes, SkClipOp::kIntersect)
1461*c8dee2aaSAndroid Build Coastguard Worker                                        .rrect(smRR, lm2, GrAA::kYes, SkClipOp::kDifference)
1462*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1463*c8dee2aaSAndroid Build Coastguard Worker                               .expectActual()
1464*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
1465*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1466*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("rrectI+rectD", kDeviceBounds)
1467*c8dee2aaSAndroid Build Coastguard Worker                               .actual().rrect(bigRR, lm1, GrAA::kYes, SkClipOp::kIntersect)
1468*c8dee2aaSAndroid Build Coastguard Worker                                        .rect(smR, lm2, GrAA::kYes, SkClipOp::kDifference)
1469*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1470*c8dee2aaSAndroid Build Coastguard Worker                               .expectActual()
1471*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
1472*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1473*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("rectI+rrectD", kDeviceBounds)
1474*c8dee2aaSAndroid Build Coastguard Worker                               .actual().rect(bigR, lm1, GrAA::kYes, SkClipOp::kIntersect)
1475*c8dee2aaSAndroid Build Coastguard Worker                                        .rrect(smRR, lm2, GrAA::kYes, SkClipOp::kDifference)
1476*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1477*c8dee2aaSAndroid Build Coastguard Worker                               .expectActual()
1478*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
1479*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1480*c8dee2aaSAndroid Build Coastguard Worker }
1481*c8dee2aaSAndroid Build Coastguard Worker 
1482*c8dee2aaSAndroid Build Coastguard Worker // Tests that shapes with mixed AA state that contain each other can still be consolidated,
1483*c8dee2aaSAndroid Build Coastguard Worker // unless they are too close to the edge and non-AA snapping can't be predicted
DEF_TEST(ClipStack_MixedAAContains,r)1484*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(ClipStack_MixedAAContains, r) {
1485*c8dee2aaSAndroid Build Coastguard Worker     using ClipState = skgpu::ganesh::ClipStack::ClipState;
1486*c8dee2aaSAndroid Build Coastguard Worker 
1487*c8dee2aaSAndroid Build Coastguard Worker     SkMatrix lm1 = SkMatrix::RotateDeg(45.f);
1488*c8dee2aaSAndroid Build Coastguard Worker     SkRect r1 = {-20.f, -20.f, 20.f, 20.f};
1489*c8dee2aaSAndroid Build Coastguard Worker 
1490*c8dee2aaSAndroid Build Coastguard Worker     SkMatrix lm2 = SkMatrix::RotateDeg(-45.f);
1491*c8dee2aaSAndroid Build Coastguard Worker     SkRect r2Safe = {-10.f, -10.f, 10.f, 10.f};
1492*c8dee2aaSAndroid Build Coastguard Worker     SkRect r2Unsafe = {-19.5f, -19.5f, 19.5f, 19.5f};
1493*c8dee2aaSAndroid Build Coastguard Worker 
1494*c8dee2aaSAndroid Build Coastguard Worker     // Non-AA sufficiently inside AA element can discard the outer AA element
1495*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("mixed-outeraa-combine", kDeviceBounds)
1496*c8dee2aaSAndroid Build Coastguard Worker                               .actual().rect(r1, lm1, GrAA::kYes, SkClipOp::kIntersect)
1497*c8dee2aaSAndroid Build Coastguard Worker                                        .rect(r2Safe, lm2, GrAA::kNo, SkClipOp::kIntersect)
1498*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1499*c8dee2aaSAndroid Build Coastguard Worker                               .expect().rect(r2Safe, lm2, GrAA::kNo, SkClipOp::kIntersect)
1500*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1501*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
1502*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1503*c8dee2aaSAndroid Build Coastguard Worker     // Vice versa
1504*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("mixed-inneraa-combine", kDeviceBounds)
1505*c8dee2aaSAndroid Build Coastguard Worker                               .actual().rect(r1, lm1, GrAA::kNo, SkClipOp::kIntersect)
1506*c8dee2aaSAndroid Build Coastguard Worker                                        .rect(r2Safe, lm2, GrAA::kYes, SkClipOp::kIntersect)
1507*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1508*c8dee2aaSAndroid Build Coastguard Worker                               .expect().rect(r2Safe, lm2, GrAA::kYes, SkClipOp::kIntersect)
1509*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1510*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
1511*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1512*c8dee2aaSAndroid Build Coastguard Worker 
1513*c8dee2aaSAndroid Build Coastguard Worker     // Non-AA too close to AA edges keeps both
1514*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("mixed-outeraa-nocombine", kDeviceBounds)
1515*c8dee2aaSAndroid Build Coastguard Worker                               .actual().rect(r1, lm1, GrAA::kYes, SkClipOp::kIntersect)
1516*c8dee2aaSAndroid Build Coastguard Worker                                        .rect(r2Unsafe, lm2, GrAA::kNo, SkClipOp::kIntersect)
1517*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1518*c8dee2aaSAndroid Build Coastguard Worker                               .expectActual()
1519*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
1520*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1521*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("mixed-inneraa-nocombine", kDeviceBounds)
1522*c8dee2aaSAndroid Build Coastguard Worker                               .actual().rect(r1, lm1, GrAA::kNo, SkClipOp::kIntersect)
1523*c8dee2aaSAndroid Build Coastguard Worker                                        .rect(r2Unsafe, lm2, GrAA::kYes, SkClipOp::kIntersect)
1524*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1525*c8dee2aaSAndroid Build Coastguard Worker                               .expectActual()
1526*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
1527*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1528*c8dee2aaSAndroid Build Coastguard Worker }
1529*c8dee2aaSAndroid Build Coastguard Worker 
1530*c8dee2aaSAndroid Build Coastguard Worker // Tests that a shape that contains the device bounds updates the clip state directly
DEF_TEST(ClipStack_ShapeContainsDevice,r)1531*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(ClipStack_ShapeContainsDevice, r) {
1532*c8dee2aaSAndroid Build Coastguard Worker     using ClipState = skgpu::ganesh::ClipStack::ClipState;
1533*c8dee2aaSAndroid Build Coastguard Worker 
1534*c8dee2aaSAndroid Build Coastguard Worker     SkRect rect = SkRect::Make(kDeviceBounds).makeOutset(10.f, 10.f);
1535*c8dee2aaSAndroid Build Coastguard Worker     SkRRect rrect = SkRRect::MakeRectXY(rect, 10.f, 10.f);
1536*c8dee2aaSAndroid Build Coastguard Worker     SkPath convex = make_octagon(rect, 10.f, 10.f);
1537*c8dee2aaSAndroid Build Coastguard Worker 
1538*c8dee2aaSAndroid Build Coastguard Worker     // Intersect -> no-op
1539*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("rect-intersect", kDeviceBounds)
1540*c8dee2aaSAndroid Build Coastguard Worker                               .actual().intersect().rect(rect).finishElements()
1541*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kWideOpen)
1542*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1543*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("rrect-intersect", kDeviceBounds)
1544*c8dee2aaSAndroid Build Coastguard Worker                               .actual().intersect().rrect(rrect).finishElements()
1545*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kWideOpen)
1546*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1547*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("convex-intersect", kDeviceBounds)
1548*c8dee2aaSAndroid Build Coastguard Worker                               .actual().intersect().path(convex).finishElements()
1549*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kWideOpen)
1550*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1551*c8dee2aaSAndroid Build Coastguard Worker 
1552*c8dee2aaSAndroid Build Coastguard Worker     // Difference -> empty
1553*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("rect-difference", kDeviceBounds)
1554*c8dee2aaSAndroid Build Coastguard Worker                               .actual().difference().rect(rect).finishElements()
1555*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kEmpty)
1556*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1557*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("rrect-difference", kDeviceBounds)
1558*c8dee2aaSAndroid Build Coastguard Worker                               .actual().difference().rrect(rrect).finishElements()
1559*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kEmpty)
1560*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1561*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("convex-difference", kDeviceBounds)
1562*c8dee2aaSAndroid Build Coastguard Worker                               .actual().difference().path(convex).finishElements()
1563*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kEmpty)
1564*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1565*c8dee2aaSAndroid Build Coastguard Worker }
1566*c8dee2aaSAndroid Build Coastguard Worker 
1567*c8dee2aaSAndroid Build Coastguard Worker // Tests that shapes that do not overlap make for an empty clip (when intersecting), pick just the
1568*c8dee2aaSAndroid Build Coastguard Worker // intersecting op (when mixed), or are all kept (when diff'ing).
DEF_TEST(ClipStack_DisjointShapes,r)1569*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(ClipStack_DisjointShapes, r) {
1570*c8dee2aaSAndroid Build Coastguard Worker     using ClipState = skgpu::ganesh::ClipStack::ClipState;
1571*c8dee2aaSAndroid Build Coastguard Worker 
1572*c8dee2aaSAndroid Build Coastguard Worker     SkRect rt = {10.f, 10.f, 20.f, 20.f};
1573*c8dee2aaSAndroid Build Coastguard Worker     SkRRect rr = SkRRect::MakeOval(rt.makeOffset({20.f, 0.f}));
1574*c8dee2aaSAndroid Build Coastguard Worker     SkPath p = make_octagon(rt.makeOffset({0.f, 20.f}));
1575*c8dee2aaSAndroid Build Coastguard Worker 
1576*c8dee2aaSAndroid Build Coastguard Worker     // I+I
1577*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("iii", kDeviceBounds)
1578*c8dee2aaSAndroid Build Coastguard Worker                               .actual().aa().intersect().rect(rt).rrect(rr).path(p).finishElements()
1579*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kEmpty)
1580*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1581*c8dee2aaSAndroid Build Coastguard Worker 
1582*c8dee2aaSAndroid Build Coastguard Worker     // D+D
1583*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("ddd", kDeviceBounds)
1584*c8dee2aaSAndroid Build Coastguard Worker                               .actual().nonAA().difference().rect(rt).rrect(rr).path(p)
1585*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1586*c8dee2aaSAndroid Build Coastguard Worker                               .expectActual()
1587*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
1588*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1589*c8dee2aaSAndroid Build Coastguard Worker 
1590*c8dee2aaSAndroid Build Coastguard Worker     // I+D from rect
1591*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("idd", kDeviceBounds)
1592*c8dee2aaSAndroid Build Coastguard Worker                               .actual().aa().intersect().rect(rt)
1593*c8dee2aaSAndroid Build Coastguard Worker                                        .nonAA().difference().rrect(rr).path(p)
1594*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1595*c8dee2aaSAndroid Build Coastguard Worker                               .expect().aa().intersect().rect(rt).finishElements()
1596*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kDeviceRect)
1597*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1598*c8dee2aaSAndroid Build Coastguard Worker 
1599*c8dee2aaSAndroid Build Coastguard Worker     // I+D from rrect
1600*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("did", kDeviceBounds)
1601*c8dee2aaSAndroid Build Coastguard Worker                               .actual().aa().intersect().rrect(rr)
1602*c8dee2aaSAndroid Build Coastguard Worker                                        .nonAA().difference().rect(rt).path(p)
1603*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1604*c8dee2aaSAndroid Build Coastguard Worker                               .expect().aa().intersect().rrect(rr).finishElements()
1605*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kDeviceRRect)
1606*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1607*c8dee2aaSAndroid Build Coastguard Worker 
1608*c8dee2aaSAndroid Build Coastguard Worker     // I+D from path
1609*c8dee2aaSAndroid Build Coastguard Worker     run_test_case(r, TestCase::Build("ddi", kDeviceBounds)
1610*c8dee2aaSAndroid Build Coastguard Worker                               .actual().aa().intersect().path(p)
1611*c8dee2aaSAndroid Build Coastguard Worker                                        .nonAA().difference().rect(rt).rrect(rr)
1612*c8dee2aaSAndroid Build Coastguard Worker                                        .finishElements()
1613*c8dee2aaSAndroid Build Coastguard Worker                               .expect().aa().intersect().path(p).finishElements()
1614*c8dee2aaSAndroid Build Coastguard Worker                               .state(ClipState::kComplex)
1615*c8dee2aaSAndroid Build Coastguard Worker                               .finishTest());
1616*c8dee2aaSAndroid Build Coastguard Worker }
1617*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(ClipStack_ComplexClip,reporter)1618*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(ClipStack_ComplexClip, reporter) {
1619*c8dee2aaSAndroid Build Coastguard Worker     using ClipStack = skgpu::ganesh::ClipStack;
1620*c8dee2aaSAndroid Build Coastguard Worker 
1621*c8dee2aaSAndroid Build Coastguard Worker     static constexpr float kN = 10.f;
1622*c8dee2aaSAndroid Build Coastguard Worker     static constexpr float kR = kN / 3.f;
1623*c8dee2aaSAndroid Build Coastguard Worker 
1624*c8dee2aaSAndroid Build Coastguard Worker     // 4 rectangles that overlap by kN x 2kN (horiz), 2kN x kN (vert), or kN x kN (diagonal)
1625*c8dee2aaSAndroid Build Coastguard Worker     static const SkRect kTL = {0.f, 0.f, 2.f * kN, 2.f * kN};
1626*c8dee2aaSAndroid Build Coastguard Worker     static const SkRect kTR = {kN,  0.f, 3.f * kN, 2.f * kN};
1627*c8dee2aaSAndroid Build Coastguard Worker     static const SkRect kBL = {0.f, kN,  2.f * kN, 3.f * kN};
1628*c8dee2aaSAndroid Build Coastguard Worker     static const SkRect kBR = {kN,  kN,  3.f * kN, 3.f * kN};
1629*c8dee2aaSAndroid Build Coastguard Worker 
1630*c8dee2aaSAndroid Build Coastguard Worker     enum ShapeType { kRect, kRRect, kConvex };
1631*c8dee2aaSAndroid Build Coastguard Worker 
1632*c8dee2aaSAndroid Build Coastguard Worker     SkRect rects[] = { kTL, kTR, kBL, kBR };
1633*c8dee2aaSAndroid Build Coastguard Worker     for (ShapeType type : { kRect, kRRect, kConvex }) {
1634*c8dee2aaSAndroid Build Coastguard Worker         for (int opBits = 6; opBits < 16; ++opBits) {
1635*c8dee2aaSAndroid Build Coastguard Worker             SkString name;
1636*c8dee2aaSAndroid Build Coastguard Worker             name.appendf("complex-%d-%d", (int) type, opBits);
1637*c8dee2aaSAndroid Build Coastguard Worker 
1638*c8dee2aaSAndroid Build Coastguard Worker             SkRect expectedRectIntersection = SkRect::Make(kDeviceBounds);
1639*c8dee2aaSAndroid Build Coastguard Worker             SkRRect expectedRRectIntersection = SkRRect::MakeRect(expectedRectIntersection);
1640*c8dee2aaSAndroid Build Coastguard Worker 
1641*c8dee2aaSAndroid Build Coastguard Worker             auto b = TestCase::Build(name.c_str(), kDeviceBounds);
1642*c8dee2aaSAndroid Build Coastguard Worker             for (int i = 0; i < 4; ++i) {
1643*c8dee2aaSAndroid Build Coastguard Worker                 SkClipOp op = (opBits & (1 << i)) ? SkClipOp::kIntersect : SkClipOp::kDifference;
1644*c8dee2aaSAndroid Build Coastguard Worker                 switch(type) {
1645*c8dee2aaSAndroid Build Coastguard Worker                     case kRect: {
1646*c8dee2aaSAndroid Build Coastguard Worker                         SkRect r = rects[i];
1647*c8dee2aaSAndroid Build Coastguard Worker                         if (op == SkClipOp::kDifference) {
1648*c8dee2aaSAndroid Build Coastguard Worker                             // Shrink the rect for difference ops, otherwise in the rect testcase
1649*c8dee2aaSAndroid Build Coastguard Worker                             // any difference op would remove the intersection of the other ops
1650*c8dee2aaSAndroid Build Coastguard Worker                             // given how the rects are defined, and that's just not interesting.
1651*c8dee2aaSAndroid Build Coastguard Worker                             r.inset(kR, kR);
1652*c8dee2aaSAndroid Build Coastguard Worker                         }
1653*c8dee2aaSAndroid Build Coastguard Worker                         b.actual().rect(r, GrAA::kYes, op);
1654*c8dee2aaSAndroid Build Coastguard Worker                         if (op == SkClipOp::kIntersect) {
1655*c8dee2aaSAndroid Build Coastguard Worker                             SkAssertResult(expectedRectIntersection.intersect(r));
1656*c8dee2aaSAndroid Build Coastguard Worker                         } else {
1657*c8dee2aaSAndroid Build Coastguard Worker                             b.expect().rect(r, GrAA::kYes, SkClipOp::kDifference);
1658*c8dee2aaSAndroid Build Coastguard Worker                         }
1659*c8dee2aaSAndroid Build Coastguard Worker                         break; }
1660*c8dee2aaSAndroid Build Coastguard Worker                     case kRRect: {
1661*c8dee2aaSAndroid Build Coastguard Worker                         SkRRect rrect = SkRRect::MakeRectXY(rects[i], kR, kR);
1662*c8dee2aaSAndroid Build Coastguard Worker                         b.actual().rrect(rrect, GrAA::kYes, op);
1663*c8dee2aaSAndroid Build Coastguard Worker                         if (op == SkClipOp::kIntersect) {
1664*c8dee2aaSAndroid Build Coastguard Worker                             expectedRRectIntersection = SkRRectPriv::ConservativeIntersect(
1665*c8dee2aaSAndroid Build Coastguard Worker                                     expectedRRectIntersection, rrect);
1666*c8dee2aaSAndroid Build Coastguard Worker                             SkASSERT(!expectedRRectIntersection.isEmpty());
1667*c8dee2aaSAndroid Build Coastguard Worker                         } else {
1668*c8dee2aaSAndroid Build Coastguard Worker                             b.expect().rrect(rrect, GrAA::kYes, SkClipOp::kDifference);
1669*c8dee2aaSAndroid Build Coastguard Worker                         }
1670*c8dee2aaSAndroid Build Coastguard Worker                         break; }
1671*c8dee2aaSAndroid Build Coastguard Worker                     case kConvex:
1672*c8dee2aaSAndroid Build Coastguard Worker                         b.actual().path(make_octagon(rects[i], kR, kR), GrAA::kYes, op);
1673*c8dee2aaSAndroid Build Coastguard Worker                         // NOTE: We don't set any expectations here, since convex just calls
1674*c8dee2aaSAndroid Build Coastguard Worker                         // expectActual() at the end.
1675*c8dee2aaSAndroid Build Coastguard Worker                         break;
1676*c8dee2aaSAndroid Build Coastguard Worker                 }
1677*c8dee2aaSAndroid Build Coastguard Worker             }
1678*c8dee2aaSAndroid Build Coastguard Worker 
1679*c8dee2aaSAndroid Build Coastguard Worker             // The expectations differ depending on the shape type
1680*c8dee2aaSAndroid Build Coastguard Worker             ClipStack::ClipState state = ClipStack::ClipState::kComplex;
1681*c8dee2aaSAndroid Build Coastguard Worker             if (type == kConvex) {
1682*c8dee2aaSAndroid Build Coastguard Worker                 // The simplest case is when the paths cannot be combined together, so we expect
1683*c8dee2aaSAndroid Build Coastguard Worker                 // the actual elements to be unmodified (both intersect and difference).
1684*c8dee2aaSAndroid Build Coastguard Worker                 b.expectActual();
1685*c8dee2aaSAndroid Build Coastguard Worker             } else if (opBits) {
1686*c8dee2aaSAndroid Build Coastguard Worker                 // All intersection ops were pre-computed into expectedR[R]ectIntersection
1687*c8dee2aaSAndroid Build Coastguard Worker                 // - difference ops already added in the for loop
1688*c8dee2aaSAndroid Build Coastguard Worker                 if (type == kRect) {
1689*c8dee2aaSAndroid Build Coastguard Worker                     SkASSERT(expectedRectIntersection != SkRect::Make(kDeviceBounds) &&
1690*c8dee2aaSAndroid Build Coastguard Worker                              !expectedRectIntersection.isEmpty());
1691*c8dee2aaSAndroid Build Coastguard Worker                     b.expect().rect(expectedRectIntersection, GrAA::kYes, SkClipOp::kIntersect);
1692*c8dee2aaSAndroid Build Coastguard Worker                     if (opBits == 0xf) {
1693*c8dee2aaSAndroid Build Coastguard Worker                         state = ClipStack::ClipState::kDeviceRect;
1694*c8dee2aaSAndroid Build Coastguard Worker                     }
1695*c8dee2aaSAndroid Build Coastguard Worker                 } else {
1696*c8dee2aaSAndroid Build Coastguard Worker                     SkASSERT(expectedRRectIntersection !=
1697*c8dee2aaSAndroid Build Coastguard Worker                                     SkRRect::MakeRect(SkRect::Make(kDeviceBounds)) &&
1698*c8dee2aaSAndroid Build Coastguard Worker                              !expectedRRectIntersection.isEmpty());
1699*c8dee2aaSAndroid Build Coastguard Worker                     b.expect().rrect(expectedRRectIntersection, GrAA::kYes, SkClipOp::kIntersect);
1700*c8dee2aaSAndroid Build Coastguard Worker                     if (opBits == 0xf) {
1701*c8dee2aaSAndroid Build Coastguard Worker                         state = ClipStack::ClipState::kDeviceRRect;
1702*c8dee2aaSAndroid Build Coastguard Worker                     }
1703*c8dee2aaSAndroid Build Coastguard Worker                 }
1704*c8dee2aaSAndroid Build Coastguard Worker             }
1705*c8dee2aaSAndroid Build Coastguard Worker 
1706*c8dee2aaSAndroid Build Coastguard Worker             run_test_case(reporter, b.state(state).finishTest());
1707*c8dee2aaSAndroid Build Coastguard Worker         }
1708*c8dee2aaSAndroid Build Coastguard Worker     }
1709*c8dee2aaSAndroid Build Coastguard Worker }
1710*c8dee2aaSAndroid Build Coastguard Worker 
1711*c8dee2aaSAndroid Build Coastguard Worker // ///////////////////////////////////////////////////////////////////////////////
1712*c8dee2aaSAndroid Build Coastguard Worker // // These tests do not use the TestCase infrastructure and manipulate a
1713*c8dee2aaSAndroid Build Coastguard Worker // // ClipStack directly.
1714*c8dee2aaSAndroid Build Coastguard Worker 
1715*c8dee2aaSAndroid Build Coastguard Worker // Tests that replaceClip() works as expected across save/restores
DEF_TEST(ClipStack_ReplaceClip,r)1716*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(ClipStack_ReplaceClip, r) {
1717*c8dee2aaSAndroid Build Coastguard Worker     using ClipStack = skgpu::ganesh::ClipStack;
1718*c8dee2aaSAndroid Build Coastguard Worker 
1719*c8dee2aaSAndroid Build Coastguard Worker     ClipStack cs(kDeviceBounds, nullptr, false);
1720*c8dee2aaSAndroid Build Coastguard Worker 
1721*c8dee2aaSAndroid Build Coastguard Worker     SkRRect rrect = SkRRect::MakeRectXY({15.f, 12.25f, 40.3f, 23.5f}, 4.f, 6.f);
1722*c8dee2aaSAndroid Build Coastguard Worker     cs.clipRRect(SkMatrix::I(), rrect, GrAA::kYes, SkClipOp::kIntersect);
1723*c8dee2aaSAndroid Build Coastguard Worker 
1724*c8dee2aaSAndroid Build Coastguard Worker     SkIRect replace = {50, 25, 75, 40}; // Is disjoint from the rrect element
1725*c8dee2aaSAndroid Build Coastguard Worker     cs.save();
1726*c8dee2aaSAndroid Build Coastguard Worker     cs.replaceClip(replace);
1727*c8dee2aaSAndroid Build Coastguard Worker 
1728*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, cs.clipState() == ClipStack::ClipState::kDeviceRect,
1729*c8dee2aaSAndroid Build Coastguard Worker                     "Clip did not become a device rect");
1730*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, cs.getConservativeBounds() == replace, "Unexpected replaced clip bounds");
1731*c8dee2aaSAndroid Build Coastguard Worker     const ClipStack::Element& replaceElement = *cs.begin();
1732*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, replaceElement.fShape.rect() == SkRect::Make(replace) &&
1733*c8dee2aaSAndroid Build Coastguard Worker                        replaceElement.fAA == GrAA::kNo &&
1734*c8dee2aaSAndroid Build Coastguard Worker                        replaceElement.fOp == SkClipOp::kIntersect &&
1735*c8dee2aaSAndroid Build Coastguard Worker                        replaceElement.fLocalToDevice == SkMatrix::I(),
1736*c8dee2aaSAndroid Build Coastguard Worker                     "Unexpected replace element state");
1737*c8dee2aaSAndroid Build Coastguard Worker 
1738*c8dee2aaSAndroid Build Coastguard Worker     // Restore should undo the replaced clip and bring back the rrect
1739*c8dee2aaSAndroid Build Coastguard Worker     cs.restore();
1740*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, cs.clipState() == ClipStack::ClipState::kDeviceRRect,
1741*c8dee2aaSAndroid Build Coastguard Worker                     "Unexpected state after restore, not kDeviceRRect");
1742*c8dee2aaSAndroid Build Coastguard Worker     const ClipStack::Element& rrectElem = *cs.begin();
1743*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, rrectElem.fShape.rrect() == rrect &&
1744*c8dee2aaSAndroid Build Coastguard Worker                        rrectElem.fAA == GrAA::kYes &&
1745*c8dee2aaSAndroid Build Coastguard Worker                        rrectElem.fOp == SkClipOp::kIntersect &&
1746*c8dee2aaSAndroid Build Coastguard Worker                        rrectElem.fLocalToDevice == SkMatrix::I(),
1747*c8dee2aaSAndroid Build Coastguard Worker                     "RRect element state not restored properly after replace clip undone");
1748*c8dee2aaSAndroid Build Coastguard Worker }
1749*c8dee2aaSAndroid Build Coastguard Worker 
1750*c8dee2aaSAndroid Build Coastguard Worker // Try to overflow the number of allowed window rects (see skbug.com/10989)
DEF_TEST(ClipStack_DiffRects,r)1751*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(ClipStack_DiffRects, r) {
1752*c8dee2aaSAndroid Build Coastguard Worker     using ClipStack = skgpu::ganesh::ClipStack;
1753*c8dee2aaSAndroid Build Coastguard Worker     using SurfaceDrawContext = skgpu::ganesh::SurfaceDrawContext;
1754*c8dee2aaSAndroid Build Coastguard Worker 
1755*c8dee2aaSAndroid Build Coastguard Worker     GrMockOptions options;
1756*c8dee2aaSAndroid Build Coastguard Worker     options.fMaxWindowRectangles = 8;
1757*c8dee2aaSAndroid Build Coastguard Worker 
1758*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<GrDirectContext> context = GrDirectContext::MakeMock(&options);
1759*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SurfaceDrawContext> sdc = SurfaceDrawContext::Make(
1760*c8dee2aaSAndroid Build Coastguard Worker             context.get(), GrColorType::kRGBA_8888, SkColorSpace::MakeSRGB(),
1761*c8dee2aaSAndroid Build Coastguard Worker             SkBackingFit::kExact, kDeviceBounds.size(), SkSurfaceProps(),
1762*c8dee2aaSAndroid Build Coastguard Worker             /*label=*/{});
1763*c8dee2aaSAndroid Build Coastguard Worker 
1764*c8dee2aaSAndroid Build Coastguard Worker     ClipStack cs(kDeviceBounds, &SkMatrix::I(), false);
1765*c8dee2aaSAndroid Build Coastguard Worker 
1766*c8dee2aaSAndroid Build Coastguard Worker     cs.save();
1767*c8dee2aaSAndroid Build Coastguard Worker     for (int y = 0; y < 10; ++y) {
1768*c8dee2aaSAndroid Build Coastguard Worker         for (int x = 0; x < 10; ++x) {
1769*c8dee2aaSAndroid Build Coastguard Worker             cs.clipRect(SkMatrix::I(), SkRect::MakeXYWH(10*x+1, 10*y+1, 8, 8),
1770*c8dee2aaSAndroid Build Coastguard Worker                         GrAA::kNo, SkClipOp::kDifference);
1771*c8dee2aaSAndroid Build Coastguard Worker         }
1772*c8dee2aaSAndroid Build Coastguard Worker     }
1773*c8dee2aaSAndroid Build Coastguard Worker 
1774*c8dee2aaSAndroid Build Coastguard Worker     GrAppliedClip out(kDeviceBounds.size());
1775*c8dee2aaSAndroid Build Coastguard Worker     SkRect drawBounds = SkRect::Make(kDeviceBounds);
1776*c8dee2aaSAndroid Build Coastguard Worker     GrClip::Effect effect = cs.apply(context.get(), sdc.get(), NoOp::Get(), GrAAType::kCoverage,
1777*c8dee2aaSAndroid Build Coastguard Worker                                      &out, &drawBounds);
1778*c8dee2aaSAndroid Build Coastguard Worker 
1779*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, effect == GrClip::Effect::kClipped);
1780*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, out.windowRectsState().numWindows() == 8);
1781*c8dee2aaSAndroid Build Coastguard Worker 
1782*c8dee2aaSAndroid Build Coastguard Worker     cs.restore();
1783*c8dee2aaSAndroid Build Coastguard Worker }
1784*c8dee2aaSAndroid Build Coastguard Worker 
1785*c8dee2aaSAndroid Build Coastguard Worker // Tests that when a stack is forced to always be AA, non-AA elements become AA
DEF_TEST(ClipStack_ForceAA,r)1786*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(ClipStack_ForceAA, r) {
1787*c8dee2aaSAndroid Build Coastguard Worker     using ClipStack = skgpu::ganesh::ClipStack;
1788*c8dee2aaSAndroid Build Coastguard Worker 
1789*c8dee2aaSAndroid Build Coastguard Worker     ClipStack cs(kDeviceBounds, nullptr, true);
1790*c8dee2aaSAndroid Build Coastguard Worker 
1791*c8dee2aaSAndroid Build Coastguard Worker     // AA will remain AA
1792*c8dee2aaSAndroid Build Coastguard Worker     SkRect aaRect = {0.25f, 12.43f, 25.2f, 23.f};
1793*c8dee2aaSAndroid Build Coastguard Worker     cs.clipRect(SkMatrix::I(), aaRect, GrAA::kYes, SkClipOp::kIntersect);
1794*c8dee2aaSAndroid Build Coastguard Worker 
1795*c8dee2aaSAndroid Build Coastguard Worker     // Non-AA will become AA
1796*c8dee2aaSAndroid Build Coastguard Worker     SkPath nonAAPath = make_octagon({2.f, 10.f, 16.f, 20.f});
1797*c8dee2aaSAndroid Build Coastguard Worker     cs.clipPath(SkMatrix::I(), nonAAPath, GrAA::kNo, SkClipOp::kIntersect);
1798*c8dee2aaSAndroid Build Coastguard Worker 
1799*c8dee2aaSAndroid Build Coastguard Worker     // Non-AA rects remain non-AA so they can be applied as a scissor
1800*c8dee2aaSAndroid Build Coastguard Worker     SkRect nonAARect = {4.5f, 5.f, 17.25f, 18.23f};
1801*c8dee2aaSAndroid Build Coastguard Worker     cs.clipRect(SkMatrix::I(), nonAARect, GrAA::kNo, SkClipOp::kIntersect);
1802*c8dee2aaSAndroid Build Coastguard Worker 
1803*c8dee2aaSAndroid Build Coastguard Worker     // The stack reports elements newest first, but the non-AA rect op was combined in place with
1804*c8dee2aaSAndroid Build Coastguard Worker     // the first aa rect, so we should see nonAAPath as AA, and then the intersection of rects.
1805*c8dee2aaSAndroid Build Coastguard Worker     auto elements = cs.begin();
1806*c8dee2aaSAndroid Build Coastguard Worker 
1807*c8dee2aaSAndroid Build Coastguard Worker     const ClipStack::Element& nonAARectElement = *elements;
1808*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, nonAARectElement.fShape.isRect(), "Expected rect element");
1809*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, nonAARectElement.fAA == GrAA::kNo,
1810*c8dee2aaSAndroid Build Coastguard Worker                     "Axis-aligned non-AA rect ignores forceAA");
1811*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, nonAARectElement.fShape.rect() == nonAARect,
1812*c8dee2aaSAndroid Build Coastguard Worker                     "Mixed AA rects should not combine");
1813*c8dee2aaSAndroid Build Coastguard Worker 
1814*c8dee2aaSAndroid Build Coastguard Worker     ++elements;
1815*c8dee2aaSAndroid Build Coastguard Worker     const ClipStack::Element& aaPathElement = *elements;
1816*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, aaPathElement.fShape.isPath(), "Expected path element");
1817*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, aaPathElement.fShape.path() == nonAAPath, "Wrong path element");
1818*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, aaPathElement.fAA == GrAA::kYes, "Path element not promoted to AA");
1819*c8dee2aaSAndroid Build Coastguard Worker 
1820*c8dee2aaSAndroid Build Coastguard Worker     ++elements;
1821*c8dee2aaSAndroid Build Coastguard Worker     const ClipStack::Element& aaRectElement = *elements;
1822*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, aaRectElement.fShape.isRect(), "Expected rect element");
1823*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, aaRectElement.fShape.rect() == aaRect,
1824*c8dee2aaSAndroid Build Coastguard Worker                     "Mixed AA rects should not combine");
1825*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, aaRectElement.fAA == GrAA::kYes, "Rect element stays AA");
1826*c8dee2aaSAndroid Build Coastguard Worker 
1827*c8dee2aaSAndroid Build Coastguard Worker     ++elements;
1828*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, !(elements != cs.end()), "Expected only three clip elements");
1829*c8dee2aaSAndroid Build Coastguard Worker }
1830*c8dee2aaSAndroid Build Coastguard Worker 
1831*c8dee2aaSAndroid Build Coastguard Worker // Tests preApply works as expected for device rects, rrects, and reports clipped-out, etc. as
1832*c8dee2aaSAndroid Build Coastguard Worker // expected.
DEF_TEST(ClipStack_PreApply,r)1833*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(ClipStack_PreApply, r) {
1834*c8dee2aaSAndroid Build Coastguard Worker     using ClipStack = skgpu::ganesh::ClipStack;
1835*c8dee2aaSAndroid Build Coastguard Worker 
1836*c8dee2aaSAndroid Build Coastguard Worker     ClipStack cs(kDeviceBounds, nullptr, false);
1837*c8dee2aaSAndroid Build Coastguard Worker 
1838*c8dee2aaSAndroid Build Coastguard Worker     // Offscreen is kClippedOut
1839*c8dee2aaSAndroid Build Coastguard Worker     GrClip::PreClipResult result = cs.preApply({-10.f, -10.f, -1.f, -1.f}, GrAA::kYes);
1840*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, result.fEffect == GrClip::Effect::kClippedOut,
1841*c8dee2aaSAndroid Build Coastguard Worker                     "Offscreen draw is kClippedOut");
1842*c8dee2aaSAndroid Build Coastguard Worker 
1843*c8dee2aaSAndroid Build Coastguard Worker     // Intersecting screen with wide-open clip is kUnclipped
1844*c8dee2aaSAndroid Build Coastguard Worker     result = cs.preApply({-10.f, -10.f, 10.f, 10.f}, GrAA::kYes);
1845*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, result.fEffect == GrClip::Effect::kUnclipped,
1846*c8dee2aaSAndroid Build Coastguard Worker                     "Wide open screen intersection is still kUnclipped");
1847*c8dee2aaSAndroid Build Coastguard Worker 
1848*c8dee2aaSAndroid Build Coastguard Worker     // Empty clip is clipped out
1849*c8dee2aaSAndroid Build Coastguard Worker     cs.save();
1850*c8dee2aaSAndroid Build Coastguard Worker     cs.clipRect(SkMatrix::I(), SkRect::MakeEmpty(), GrAA::kNo, SkClipOp::kIntersect);
1851*c8dee2aaSAndroid Build Coastguard Worker     result = cs.preApply({0.f, 0.f, 20.f, 20.f}, GrAA::kYes);
1852*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, result.fEffect == GrClip::Effect::kClippedOut,
1853*c8dee2aaSAndroid Build Coastguard Worker                     "Empty clip stack preApplies as kClippedOut");
1854*c8dee2aaSAndroid Build Coastguard Worker     cs.restore();
1855*c8dee2aaSAndroid Build Coastguard Worker 
1856*c8dee2aaSAndroid Build Coastguard Worker     // Contained inside clip is kUnclipped (using rrect for the outer clip element since paths
1857*c8dee2aaSAndroid Build Coastguard Worker     // don't support an inner bounds and anything complex is otherwise skipped in preApply).
1858*c8dee2aaSAndroid Build Coastguard Worker     SkRect rect = {10.f, 10.f, 40.f, 40.f};
1859*c8dee2aaSAndroid Build Coastguard Worker     SkRRect bigRRect = SkRRect::MakeRectXY(rect.makeOutset(5.f, 5.f), 5.f, 5.f);
1860*c8dee2aaSAndroid Build Coastguard Worker     cs.save();
1861*c8dee2aaSAndroid Build Coastguard Worker     cs.clipRRect(SkMatrix::I(), bigRRect, GrAA::kYes, SkClipOp::kIntersect);
1862*c8dee2aaSAndroid Build Coastguard Worker     result = cs.preApply(rect, GrAA::kYes);
1863*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, result.fEffect == GrClip::Effect::kUnclipped,
1864*c8dee2aaSAndroid Build Coastguard Worker                     "Draw contained within clip is kUnclipped");
1865*c8dee2aaSAndroid Build Coastguard Worker 
1866*c8dee2aaSAndroid Build Coastguard Worker     // Disjoint from clip (but still on screen) is kClippedOut
1867*c8dee2aaSAndroid Build Coastguard Worker     result = cs.preApply({50.f, 50.f, 60.f, 60.f}, GrAA::kYes);
1868*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, result.fEffect == GrClip::Effect::kClippedOut,
1869*c8dee2aaSAndroid Build Coastguard Worker                     "Draw not intersecting clip is kClippedOut");
1870*c8dee2aaSAndroid Build Coastguard Worker     cs.restore();
1871*c8dee2aaSAndroid Build Coastguard Worker 
1872*c8dee2aaSAndroid Build Coastguard Worker     // Intersecting clip is kClipped for complex shape
1873*c8dee2aaSAndroid Build Coastguard Worker     cs.save();
1874*c8dee2aaSAndroid Build Coastguard Worker     SkPath path = make_octagon(rect.makeOutset(5.f, 5.f), 5.f, 5.f);
1875*c8dee2aaSAndroid Build Coastguard Worker     cs.clipPath(SkMatrix::I(), path, GrAA::kYes, SkClipOp::kIntersect);
1876*c8dee2aaSAndroid Build Coastguard Worker     result = cs.preApply(path.getBounds(), GrAA::kNo);
1877*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, result.fEffect == GrClip::Effect::kClipped && !result.fIsRRect,
1878*c8dee2aaSAndroid Build Coastguard Worker                     "Draw with complex clip is kClipped, but is not an rrect");
1879*c8dee2aaSAndroid Build Coastguard Worker     cs.restore();
1880*c8dee2aaSAndroid Build Coastguard Worker 
1881*c8dee2aaSAndroid Build Coastguard Worker     // Intersecting clip is kDeviceRect for axis-aligned rect clip
1882*c8dee2aaSAndroid Build Coastguard Worker     cs.save();
1883*c8dee2aaSAndroid Build Coastguard Worker     cs.clipRect(SkMatrix::I(), rect, GrAA::kYes, SkClipOp::kIntersect);
1884*c8dee2aaSAndroid Build Coastguard Worker     result = cs.preApply(rect.makeOffset(2.f, 2.f), GrAA::kNo);
1885*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, result.fEffect == GrClip::Effect::kClipped &&
1886*c8dee2aaSAndroid Build Coastguard Worker                        result.fAA == GrAA::kYes &&
1887*c8dee2aaSAndroid Build Coastguard Worker                        result.fIsRRect &&
1888*c8dee2aaSAndroid Build Coastguard Worker                        result.fRRect == SkRRect::MakeRect(rect),
1889*c8dee2aaSAndroid Build Coastguard Worker                     "kDeviceRect clip stack should be reported by preApply");
1890*c8dee2aaSAndroid Build Coastguard Worker     cs.restore();
1891*c8dee2aaSAndroid Build Coastguard Worker 
1892*c8dee2aaSAndroid Build Coastguard Worker     // Intersecting clip is kDeviceRRect for axis-aligned rrect clip
1893*c8dee2aaSAndroid Build Coastguard Worker     cs.save();
1894*c8dee2aaSAndroid Build Coastguard Worker     SkRRect clipRRect = SkRRect::MakeRectXY(rect, 5.f, 5.f);
1895*c8dee2aaSAndroid Build Coastguard Worker     cs.clipRRect(SkMatrix::I(), clipRRect, GrAA::kYes, SkClipOp::kIntersect);
1896*c8dee2aaSAndroid Build Coastguard Worker     result = cs.preApply(rect.makeOffset(2.f, 2.f), GrAA::kNo);
1897*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, result.fEffect == GrClip::Effect::kClipped &&
1898*c8dee2aaSAndroid Build Coastguard Worker                        result.fAA == GrAA::kYes &&
1899*c8dee2aaSAndroid Build Coastguard Worker                        result.fIsRRect &&
1900*c8dee2aaSAndroid Build Coastguard Worker                        result.fRRect == clipRRect,
1901*c8dee2aaSAndroid Build Coastguard Worker                     "kDeviceRRect clip stack should be reported by preApply");
1902*c8dee2aaSAndroid Build Coastguard Worker     cs.restore();
1903*c8dee2aaSAndroid Build Coastguard Worker }
1904*c8dee2aaSAndroid Build Coastguard Worker 
1905*c8dee2aaSAndroid Build Coastguard Worker // Tests the clip shader entry point
DEF_TEST(ClipStack_Shader,r)1906*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(ClipStack_Shader, r) {
1907*c8dee2aaSAndroid Build Coastguard Worker     using ClipStack = skgpu::ganesh::ClipStack;
1908*c8dee2aaSAndroid Build Coastguard Worker     using SurfaceDrawContext = skgpu::ganesh::SurfaceDrawContext;
1909*c8dee2aaSAndroid Build Coastguard Worker 
1910*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkShader> shader = SkShaders::Color({0.f, 0.f, 0.f, 0.5f}, nullptr);
1911*c8dee2aaSAndroid Build Coastguard Worker 
1912*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<GrDirectContext> context = GrDirectContext::MakeMock(nullptr);
1913*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SurfaceDrawContext> sdc = SurfaceDrawContext::Make(
1914*c8dee2aaSAndroid Build Coastguard Worker             context.get(), GrColorType::kRGBA_8888, SkColorSpace::MakeSRGB(),
1915*c8dee2aaSAndroid Build Coastguard Worker             SkBackingFit::kExact, kDeviceBounds.size(), SkSurfaceProps(),
1916*c8dee2aaSAndroid Build Coastguard Worker             /*label=*/{});
1917*c8dee2aaSAndroid Build Coastguard Worker 
1918*c8dee2aaSAndroid Build Coastguard Worker     ClipStack cs(kDeviceBounds, &SkMatrix::I(), false);
1919*c8dee2aaSAndroid Build Coastguard Worker     cs.save();
1920*c8dee2aaSAndroid Build Coastguard Worker     cs.clipShader(shader);
1921*c8dee2aaSAndroid Build Coastguard Worker 
1922*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, cs.clipState() == ClipStack::ClipState::kComplex,
1923*c8dee2aaSAndroid Build Coastguard Worker                     "A clip shader should be reported as a complex clip");
1924*c8dee2aaSAndroid Build Coastguard Worker 
1925*c8dee2aaSAndroid Build Coastguard Worker     GrAppliedClip out(kDeviceBounds.size());
1926*c8dee2aaSAndroid Build Coastguard Worker     SkRect drawBounds = {10.f, 11.f, 16.f, 32.f};
1927*c8dee2aaSAndroid Build Coastguard Worker     GrClip::Effect effect = cs.apply(context.get(), sdc.get(), NoOp::Get(), GrAAType::kCoverage,
1928*c8dee2aaSAndroid Build Coastguard Worker                                      &out, &drawBounds);
1929*c8dee2aaSAndroid Build Coastguard Worker 
1930*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, effect == GrClip::Effect::kClipped,
1931*c8dee2aaSAndroid Build Coastguard Worker                     "apply() should return kClipped for a clip shader");
1932*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, out.hasCoverageFragmentProcessor(),
1933*c8dee2aaSAndroid Build Coastguard Worker                     "apply() should have converted clip shader to a coverage FP");
1934*c8dee2aaSAndroid Build Coastguard Worker 
1935*c8dee2aaSAndroid Build Coastguard Worker     GrAppliedClip out2(kDeviceBounds.size());
1936*c8dee2aaSAndroid Build Coastguard Worker     drawBounds = {-15.f, -10.f, -1.f, 10.f}; // offscreen
1937*c8dee2aaSAndroid Build Coastguard Worker     effect = cs.apply(context.get(), sdc.get(), NoOp::Get(), GrAAType::kCoverage, &out2,
1938*c8dee2aaSAndroid Build Coastguard Worker                       &drawBounds);
1939*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, effect == GrClip::Effect::kClippedOut,
1940*c8dee2aaSAndroid Build Coastguard Worker                     "apply() should still discard offscreen draws with a clip shader");
1941*c8dee2aaSAndroid Build Coastguard Worker 
1942*c8dee2aaSAndroid Build Coastguard Worker     cs.restore();
1943*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, cs.clipState() == ClipStack::ClipState::kWideOpen,
1944*c8dee2aaSAndroid Build Coastguard Worker                     "restore() should get rid of the clip shader");
1945*c8dee2aaSAndroid Build Coastguard Worker 
1946*c8dee2aaSAndroid Build Coastguard Worker 
1947*c8dee2aaSAndroid Build Coastguard Worker     // Adding a clip shader on top of a device rect clip should prevent preApply from reporting
1948*c8dee2aaSAndroid Build Coastguard Worker     // it as a device rect
1949*c8dee2aaSAndroid Build Coastguard Worker     cs.clipRect(SkMatrix::I(), {10, 15, 30, 30}, GrAA::kNo, SkClipOp::kIntersect);
1950*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(cs.clipState() == ClipStack::ClipState::kDeviceRect); // test precondition
1951*c8dee2aaSAndroid Build Coastguard Worker     cs.clipShader(shader);
1952*c8dee2aaSAndroid Build Coastguard Worker     GrClip::PreClipResult result = cs.preApply(SkRect::Make(kDeviceBounds), GrAA::kYes);
1953*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(r, result.fEffect == GrClip::Effect::kClipped && !result.fIsRRect,
1954*c8dee2aaSAndroid Build Coastguard Worker                     "A clip shader should not produce a device rect from preApply");
1955*c8dee2aaSAndroid Build Coastguard Worker }
1956*c8dee2aaSAndroid Build Coastguard Worker 
1957*c8dee2aaSAndroid Build Coastguard Worker // Tests apply() under simple circumstances, that don't require actual rendering of masks, or
1958*c8dee2aaSAndroid Build Coastguard Worker // atlases. This lets us define the test regularly instead of a GPU-only test.
1959*c8dee2aaSAndroid Build Coastguard Worker // - This is not exhaustive and is challenging to unit test, so apply() is predominantly tested by
1960*c8dee2aaSAndroid Build Coastguard Worker //   the GMs instead.
DEF_TEST(ClipStack_SimpleApply,r)1961*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(ClipStack_SimpleApply, r) {
1962*c8dee2aaSAndroid Build Coastguard Worker     using ClipStack = skgpu::ganesh::ClipStack;
1963*c8dee2aaSAndroid Build Coastguard Worker     using SurfaceDrawContext = skgpu::ganesh::SurfaceDrawContext;
1964*c8dee2aaSAndroid Build Coastguard Worker 
1965*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<GrDirectContext> context = GrDirectContext::MakeMock(nullptr);
1966*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SurfaceDrawContext> sdc = SurfaceDrawContext::Make(
1967*c8dee2aaSAndroid Build Coastguard Worker             context.get(), GrColorType::kRGBA_8888, SkColorSpace::MakeSRGB(),
1968*c8dee2aaSAndroid Build Coastguard Worker             SkBackingFit::kExact, kDeviceBounds.size(), SkSurfaceProps(),
1969*c8dee2aaSAndroid Build Coastguard Worker             /*label=*/{});
1970*c8dee2aaSAndroid Build Coastguard Worker 
1971*c8dee2aaSAndroid Build Coastguard Worker     ClipStack cs(kDeviceBounds, &SkMatrix::I(), false);
1972*c8dee2aaSAndroid Build Coastguard Worker 
1973*c8dee2aaSAndroid Build Coastguard Worker     // Offscreen draw is kClippedOut
1974*c8dee2aaSAndroid Build Coastguard Worker     {
1975*c8dee2aaSAndroid Build Coastguard Worker         SkRect drawBounds = {-15.f, -15.f, -1.f, -1.f};
1976*c8dee2aaSAndroid Build Coastguard Worker 
1977*c8dee2aaSAndroid Build Coastguard Worker         GrAppliedClip out(kDeviceBounds.size());
1978*c8dee2aaSAndroid Build Coastguard Worker         GrClip::Effect effect = cs.apply(context.get(), sdc.get(), NoOp::Get(), GrAAType::kCoverage,
1979*c8dee2aaSAndroid Build Coastguard Worker                                          &out, &drawBounds);
1980*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, effect == GrClip::Effect::kClippedOut, "Offscreen draw is clipped out");
1981*c8dee2aaSAndroid Build Coastguard Worker     }
1982*c8dee2aaSAndroid Build Coastguard Worker 
1983*c8dee2aaSAndroid Build Coastguard Worker     // Draw contained in clip is kUnclipped
1984*c8dee2aaSAndroid Build Coastguard Worker     {
1985*c8dee2aaSAndroid Build Coastguard Worker         SkRect drawBounds = {15.4f, 16.3f, 26.f, 32.f};
1986*c8dee2aaSAndroid Build Coastguard Worker         cs.save();
1987*c8dee2aaSAndroid Build Coastguard Worker         cs.clipPath(SkMatrix::I(), make_octagon(drawBounds.makeOutset(5.f, 5.f), 5.f, 5.f),
1988*c8dee2aaSAndroid Build Coastguard Worker                     GrAA::kYes, SkClipOp::kIntersect);
1989*c8dee2aaSAndroid Build Coastguard Worker 
1990*c8dee2aaSAndroid Build Coastguard Worker         GrAppliedClip out(kDeviceBounds.size());
1991*c8dee2aaSAndroid Build Coastguard Worker         GrClip::Effect effect = cs.apply(context.get(), sdc.get(), NoOp::Get(), GrAAType::kCoverage,
1992*c8dee2aaSAndroid Build Coastguard Worker                                          &out, &drawBounds);
1993*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, effect == GrClip::Effect::kUnclipped, "Draw inside clip is unclipped");
1994*c8dee2aaSAndroid Build Coastguard Worker         cs.restore();
1995*c8dee2aaSAndroid Build Coastguard Worker     }
1996*c8dee2aaSAndroid Build Coastguard Worker 
1997*c8dee2aaSAndroid Build Coastguard Worker     // Draw bounds are cropped to device space before checking contains
1998*c8dee2aaSAndroid Build Coastguard Worker     {
1999*c8dee2aaSAndroid Build Coastguard Worker         SkRect clipRect = {kDeviceBounds.fRight - 20.f, 10.f, kDeviceBounds.fRight, 20.f};
2000*c8dee2aaSAndroid Build Coastguard Worker         SkRect drawRect = clipRect.makeOffset(10.f, 0.f);
2001*c8dee2aaSAndroid Build Coastguard Worker 
2002*c8dee2aaSAndroid Build Coastguard Worker         cs.save();
2003*c8dee2aaSAndroid Build Coastguard Worker         cs.clipRect(SkMatrix::I(), clipRect, GrAA::kNo, SkClipOp::kIntersect);
2004*c8dee2aaSAndroid Build Coastguard Worker 
2005*c8dee2aaSAndroid Build Coastguard Worker         GrAppliedClip out(kDeviceBounds.size());
2006*c8dee2aaSAndroid Build Coastguard Worker         GrClip::Effect effect = cs.apply(context.get(), sdc.get(), NoOp::Get(), GrAAType::kCoverage,
2007*c8dee2aaSAndroid Build Coastguard Worker                                          &out, &drawRect);
2008*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, SkRect::Make(kDeviceBounds).contains(drawRect),
2009*c8dee2aaSAndroid Build Coastguard Worker                         "Draw rect should be clipped to device rect");
2010*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, effect == GrClip::Effect::kUnclipped,
2011*c8dee2aaSAndroid Build Coastguard Worker                         "After device clipping, this should be detected as contained within clip");
2012*c8dee2aaSAndroid Build Coastguard Worker         cs.restore();
2013*c8dee2aaSAndroid Build Coastguard Worker     }
2014*c8dee2aaSAndroid Build Coastguard Worker 
2015*c8dee2aaSAndroid Build Coastguard Worker     // Non-AA device rect intersect is just a scissor
2016*c8dee2aaSAndroid Build Coastguard Worker     {
2017*c8dee2aaSAndroid Build Coastguard Worker         SkRect clipRect = {15.3f, 17.23f, 30.2f, 50.8f};
2018*c8dee2aaSAndroid Build Coastguard Worker         SkRect drawRect = clipRect.makeOutset(10.f, 10.f);
2019*c8dee2aaSAndroid Build Coastguard Worker         SkIRect expectedScissor = clipRect.round();
2020*c8dee2aaSAndroid Build Coastguard Worker 
2021*c8dee2aaSAndroid Build Coastguard Worker         cs.save();
2022*c8dee2aaSAndroid Build Coastguard Worker         cs.clipRect(SkMatrix::I(), clipRect, GrAA::kNo, SkClipOp::kIntersect);
2023*c8dee2aaSAndroid Build Coastguard Worker 
2024*c8dee2aaSAndroid Build Coastguard Worker         GrAppliedClip out(kDeviceBounds.size());
2025*c8dee2aaSAndroid Build Coastguard Worker         GrClip::Effect effect = cs.apply(context.get(), sdc.get(), NoOp::Get(), GrAAType::kCoverage,
2026*c8dee2aaSAndroid Build Coastguard Worker                                          &out, &drawRect);
2027*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, effect == GrClip::Effect::kClipped, "Draw should be clipped by rect");
2028*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, !out.hasCoverageFragmentProcessor(), "Clip should not use coverage FPs");
2029*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, !out.hardClip().hasStencilClip(), "Clip should not need stencil");
2030*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, !out.hardClip().windowRectsState().enabled(),
2031*c8dee2aaSAndroid Build Coastguard Worker                         "Clip should not need window rects");
2032*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, out.scissorState().enabled() &&
2033*c8dee2aaSAndroid Build Coastguard Worker                            out.scissorState().rect() == expectedScissor,
2034*c8dee2aaSAndroid Build Coastguard Worker                         "Clip has unexpected scissor rectangle");
2035*c8dee2aaSAndroid Build Coastguard Worker         cs.restore();
2036*c8dee2aaSAndroid Build Coastguard Worker     }
2037*c8dee2aaSAndroid Build Coastguard Worker 
2038*c8dee2aaSAndroid Build Coastguard Worker     // Analytic coverage FPs
2039*c8dee2aaSAndroid Build Coastguard Worker     auto testHasCoverageFP = [&](SkRect drawBounds) {
2040*c8dee2aaSAndroid Build Coastguard Worker         GrAppliedClip out(kDeviceBounds.size());
2041*c8dee2aaSAndroid Build Coastguard Worker         GrClip::Effect effect = cs.apply(context.get(), sdc.get(), NoOp::Get(), GrAAType::kCoverage,
2042*c8dee2aaSAndroid Build Coastguard Worker                                          &out, &drawBounds);
2043*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, effect == GrClip::Effect::kClipped, "Draw should be clipped");
2044*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, out.scissorState().enabled(), "Coverage FPs should still set scissor");
2045*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, out.hasCoverageFragmentProcessor(), "Clip should use coverage FP");
2046*c8dee2aaSAndroid Build Coastguard Worker     };
2047*c8dee2aaSAndroid Build Coastguard Worker 
2048*c8dee2aaSAndroid Build Coastguard Worker     // Axis-aligned rect can be an analytic FP
2049*c8dee2aaSAndroid Build Coastguard Worker     {
2050*c8dee2aaSAndroid Build Coastguard Worker         cs.save();
2051*c8dee2aaSAndroid Build Coastguard Worker         cs.clipRect(SkMatrix::I(), {10.2f, 8.342f, 63.f, 23.3f}, GrAA::kYes,
2052*c8dee2aaSAndroid Build Coastguard Worker                     SkClipOp::kDifference);
2053*c8dee2aaSAndroid Build Coastguard Worker         testHasCoverageFP({9.f, 10.f, 30.f, 18.f});
2054*c8dee2aaSAndroid Build Coastguard Worker         cs.restore();
2055*c8dee2aaSAndroid Build Coastguard Worker     }
2056*c8dee2aaSAndroid Build Coastguard Worker 
2057*c8dee2aaSAndroid Build Coastguard Worker     // Axis-aligned round rect can be an analytic FP
2058*c8dee2aaSAndroid Build Coastguard Worker     {
2059*c8dee2aaSAndroid Build Coastguard Worker         SkRect rect = {4.f, 8.f, 20.f, 20.f};
2060*c8dee2aaSAndroid Build Coastguard Worker         cs.save();
2061*c8dee2aaSAndroid Build Coastguard Worker         cs.clipRRect(SkMatrix::I(), SkRRect::MakeRectXY(rect, 3.f, 3.f), GrAA::kYes,
2062*c8dee2aaSAndroid Build Coastguard Worker                      SkClipOp::kIntersect);
2063*c8dee2aaSAndroid Build Coastguard Worker         testHasCoverageFP(rect.makeOffset(2.f, 2.f));
2064*c8dee2aaSAndroid Build Coastguard Worker         cs.restore();
2065*c8dee2aaSAndroid Build Coastguard Worker     }
2066*c8dee2aaSAndroid Build Coastguard Worker 
2067*c8dee2aaSAndroid Build Coastguard Worker     // Transformed rect can be an analytic FP
2068*c8dee2aaSAndroid Build Coastguard Worker     {
2069*c8dee2aaSAndroid Build Coastguard Worker         SkRect rect = {14.f, 8.f, 30.f, 22.34f};
2070*c8dee2aaSAndroid Build Coastguard Worker         SkMatrix rot = SkMatrix::RotateDeg(34.f);
2071*c8dee2aaSAndroid Build Coastguard Worker         cs.save();
2072*c8dee2aaSAndroid Build Coastguard Worker         cs.clipRect(rot, rect, GrAA::kNo, SkClipOp::kIntersect);
2073*c8dee2aaSAndroid Build Coastguard Worker         testHasCoverageFP(rot.mapRect(rect));
2074*c8dee2aaSAndroid Build Coastguard Worker         cs.restore();
2075*c8dee2aaSAndroid Build Coastguard Worker     }
2076*c8dee2aaSAndroid Build Coastguard Worker 
2077*c8dee2aaSAndroid Build Coastguard Worker     // Convex polygons can be an analytic FP
2078*c8dee2aaSAndroid Build Coastguard Worker     {
2079*c8dee2aaSAndroid Build Coastguard Worker         SkRect rect = {15.f, 15.f, 45.f, 45.f};
2080*c8dee2aaSAndroid Build Coastguard Worker         cs.save();
2081*c8dee2aaSAndroid Build Coastguard Worker         cs.clipPath(SkMatrix::I(), make_octagon(rect), GrAA::kYes, SkClipOp::kIntersect);
2082*c8dee2aaSAndroid Build Coastguard Worker         testHasCoverageFP(rect.makeOutset(2.f, 2.f));
2083*c8dee2aaSAndroid Build Coastguard Worker         cs.restore();
2084*c8dee2aaSAndroid Build Coastguard Worker     }
2085*c8dee2aaSAndroid Build Coastguard Worker }
2086*c8dee2aaSAndroid Build Coastguard Worker 
2087*c8dee2aaSAndroid Build Coastguard Worker // Must disable tessellation in order to trigger SW mask generation when the clip stack is applied.
disable_tessellation_atlas(GrContextOptions * options)2088*c8dee2aaSAndroid Build Coastguard Worker static void disable_tessellation_atlas(GrContextOptions* options) {
2089*c8dee2aaSAndroid Build Coastguard Worker     options->fGpuPathRenderers = GpuPathRenderers::kNone;
2090*c8dee2aaSAndroid Build Coastguard Worker     options->fAvoidStencilBuffers = true;
2091*c8dee2aaSAndroid Build Coastguard Worker }
2092*c8dee2aaSAndroid Build Coastguard Worker 
DEF_GANESH_TEST_FOR_CONTEXTS(ClipStack_SWMask,skgpu::IsRenderingContext,r,ctxInfo,disable_tessellation_atlas,CtsEnforcement::kNever)2093*c8dee2aaSAndroid Build Coastguard Worker DEF_GANESH_TEST_FOR_CONTEXTS(ClipStack_SWMask,
2094*c8dee2aaSAndroid Build Coastguard Worker                              skgpu::IsRenderingContext,
2095*c8dee2aaSAndroid Build Coastguard Worker                              r,
2096*c8dee2aaSAndroid Build Coastguard Worker                              ctxInfo,
2097*c8dee2aaSAndroid Build Coastguard Worker                              disable_tessellation_atlas,
2098*c8dee2aaSAndroid Build Coastguard Worker                              CtsEnforcement::kNever) {
2099*c8dee2aaSAndroid Build Coastguard Worker     using ClipStack = skgpu::ganesh::ClipStack;
2100*c8dee2aaSAndroid Build Coastguard Worker     using SurfaceDrawContext = skgpu::ganesh::SurfaceDrawContext;
2101*c8dee2aaSAndroid Build Coastguard Worker 
2102*c8dee2aaSAndroid Build Coastguard Worker     GrDirectContext* context = ctxInfo.directContext();
2103*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SurfaceDrawContext> sdc = SurfaceDrawContext::Make(
2104*c8dee2aaSAndroid Build Coastguard Worker             context, GrColorType::kRGBA_8888, nullptr, SkBackingFit::kExact, kDeviceBounds.size(),
2105*c8dee2aaSAndroid Build Coastguard Worker             SkSurfaceProps(), /*label=*/{});
2106*c8dee2aaSAndroid Build Coastguard Worker 
2107*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<ClipStack> cs(new ClipStack(kDeviceBounds, &SkMatrix::I(), false));
2108*c8dee2aaSAndroid Build Coastguard Worker 
2109*c8dee2aaSAndroid Build Coastguard Worker     auto addMaskRequiringClip = [&](SkScalar x, SkScalar y, SkScalar radius) {
2110*c8dee2aaSAndroid Build Coastguard Worker         SkPath path;
2111*c8dee2aaSAndroid Build Coastguard Worker         path.addCircle(x, y, radius);
2112*c8dee2aaSAndroid Build Coastguard Worker         path.addCircle(x + radius / 2.f, y + radius / 2.f, radius);
2113*c8dee2aaSAndroid Build Coastguard Worker         path.setFillType(SkPathFillType::kEvenOdd);
2114*c8dee2aaSAndroid Build Coastguard Worker 
2115*c8dee2aaSAndroid Build Coastguard Worker         // Use AA so that clip application does not route through the stencil buffer
2116*c8dee2aaSAndroid Build Coastguard Worker         cs->clipPath(SkMatrix::I(), path, GrAA::kYes, SkClipOp::kIntersect);
2117*c8dee2aaSAndroid Build Coastguard Worker     };
2118*c8dee2aaSAndroid Build Coastguard Worker 
2119*c8dee2aaSAndroid Build Coastguard Worker     auto drawRect = [&](SkRect drawBounds) {
2120*c8dee2aaSAndroid Build Coastguard Worker         GrPaint paint;
2121*c8dee2aaSAndroid Build Coastguard Worker         paint.setColor4f({1.f, 1.f, 1.f, 1.f});
2122*c8dee2aaSAndroid Build Coastguard Worker         sdc->drawRect(cs.get(), std::move(paint), GrAA::kYes, SkMatrix::I(), drawBounds);
2123*c8dee2aaSAndroid Build Coastguard Worker     };
2124*c8dee2aaSAndroid Build Coastguard Worker 
2125*c8dee2aaSAndroid Build Coastguard Worker     auto generateMask = [&](SkRect drawBounds) {
2126*c8dee2aaSAndroid Build Coastguard Worker         skgpu::UniqueKey priorKey = cs->testingOnly_getLastSWMaskKey();
2127*c8dee2aaSAndroid Build Coastguard Worker         drawRect(drawBounds);
2128*c8dee2aaSAndroid Build Coastguard Worker         skgpu::UniqueKey newKey = cs->testingOnly_getLastSWMaskKey();
2129*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, priorKey != newKey, "Did not generate a new SW mask key as expected");
2130*c8dee2aaSAndroid Build Coastguard Worker         return newKey;
2131*c8dee2aaSAndroid Build Coastguard Worker     };
2132*c8dee2aaSAndroid Build Coastguard Worker 
2133*c8dee2aaSAndroid Build Coastguard Worker     auto verifyKeys = [&](const std::vector<skgpu::UniqueKey>& expectedKeys,
2134*c8dee2aaSAndroid Build Coastguard Worker                           const std::vector<skgpu::UniqueKey>& releasedKeys) {
2135*c8dee2aaSAndroid Build Coastguard Worker         context->flush();
2136*c8dee2aaSAndroid Build Coastguard Worker         GrProxyProvider* proxyProvider = context->priv().proxyProvider();
2137*c8dee2aaSAndroid Build Coastguard Worker 
2138*c8dee2aaSAndroid Build Coastguard Worker #ifdef SK_DEBUG
2139*c8dee2aaSAndroid Build Coastguard Worker         // The proxy providers key count fluctuates based on proxy lifetime, but we want to
2140*c8dee2aaSAndroid Build Coastguard Worker         // verify the resource count, and that requires using key tags that are debug-only.
2141*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(expectedKeys.size() > 0 || releasedKeys.size() > 0);
2142*c8dee2aaSAndroid Build Coastguard Worker         const char* tag = expectedKeys.size() > 0 ? expectedKeys[0].tag() : releasedKeys[0].tag();
2143*c8dee2aaSAndroid Build Coastguard Worker         GrResourceCache* cache = context->priv().getResourceCache();
2144*c8dee2aaSAndroid Build Coastguard Worker         int numProxies = cache->countUniqueKeysWithTag(tag);
2145*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(r, (int) expectedKeys.size() == numProxies,
2146*c8dee2aaSAndroid Build Coastguard Worker                         "Unexpected proxy count, got %d, not %d",
2147*c8dee2aaSAndroid Build Coastguard Worker                         numProxies, (int) expectedKeys.size());
2148*c8dee2aaSAndroid Build Coastguard Worker #endif
2149*c8dee2aaSAndroid Build Coastguard Worker 
2150*c8dee2aaSAndroid Build Coastguard Worker         for (const auto& key : expectedKeys) {
2151*c8dee2aaSAndroid Build Coastguard Worker             auto proxy = proxyProvider->findOrCreateProxyByUniqueKey(key);
2152*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(r, SkToBool(proxy), "Unable to find resource for expected mask key");
2153*c8dee2aaSAndroid Build Coastguard Worker         }
2154*c8dee2aaSAndroid Build Coastguard Worker         for (const auto& key : releasedKeys) {
2155*c8dee2aaSAndroid Build Coastguard Worker             auto proxy = proxyProvider->findOrCreateProxyByUniqueKey(key);
2156*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(r, !SkToBool(proxy), "SW mask not released as expected");
2157*c8dee2aaSAndroid Build Coastguard Worker         }
2158*c8dee2aaSAndroid Build Coastguard Worker     };
2159*c8dee2aaSAndroid Build Coastguard Worker 
2160*c8dee2aaSAndroid Build Coastguard Worker     // Creates a mask for a complex clip
2161*c8dee2aaSAndroid Build Coastguard Worker     cs->save();
2162*c8dee2aaSAndroid Build Coastguard Worker     addMaskRequiringClip(5.f, 5.f, 20.f);
2163*c8dee2aaSAndroid Build Coastguard Worker     skgpu::UniqueKey keyADepth1 = generateMask({0.f, 0.f, 20.f, 20.f});
2164*c8dee2aaSAndroid Build Coastguard Worker     skgpu::UniqueKey keyBDepth1 = generateMask({10.f, 10.f, 30.f, 30.f});
2165*c8dee2aaSAndroid Build Coastguard Worker     verifyKeys({keyADepth1, keyBDepth1}, {});
2166*c8dee2aaSAndroid Build Coastguard Worker 
2167*c8dee2aaSAndroid Build Coastguard Worker     // Creates a new mask for a new save record, but doesn't delete the old records
2168*c8dee2aaSAndroid Build Coastguard Worker     cs->save();
2169*c8dee2aaSAndroid Build Coastguard Worker     addMaskRequiringClip(6.f, 6.f, 15.f);
2170*c8dee2aaSAndroid Build Coastguard Worker     skgpu::UniqueKey keyADepth2 = generateMask({0.f, 0.f, 20.f, 20.f});
2171*c8dee2aaSAndroid Build Coastguard Worker     skgpu::UniqueKey keyBDepth2 = generateMask({10.f, 10.f, 30.f, 30.f});
2172*c8dee2aaSAndroid Build Coastguard Worker     verifyKeys({keyADepth1, keyBDepth1, keyADepth2, keyBDepth2}, {});
2173*c8dee2aaSAndroid Build Coastguard Worker 
2174*c8dee2aaSAndroid Build Coastguard Worker     // Release after modifying the current record (even if we don't draw anything)
2175*c8dee2aaSAndroid Build Coastguard Worker     addMaskRequiringClip(4.f, 4.f, 15.f);
2176*c8dee2aaSAndroid Build Coastguard Worker     skgpu::UniqueKey keyCDepth2 = generateMask({4.f, 4.f, 16.f, 20.f});
2177*c8dee2aaSAndroid Build Coastguard Worker     verifyKeys({keyADepth1, keyBDepth1, keyCDepth2}, {keyADepth2, keyBDepth2});
2178*c8dee2aaSAndroid Build Coastguard Worker 
2179*c8dee2aaSAndroid Build Coastguard Worker     // Release after restoring an older record
2180*c8dee2aaSAndroid Build Coastguard Worker     cs->restore();
2181*c8dee2aaSAndroid Build Coastguard Worker     verifyKeys({keyADepth1, keyBDepth1}, {keyCDepth2});
2182*c8dee2aaSAndroid Build Coastguard Worker 
2183*c8dee2aaSAndroid Build Coastguard Worker     // Drawing finds the old masks at depth 1 still w/o making new ones
2184*c8dee2aaSAndroid Build Coastguard Worker     drawRect({0.f, 0.f, 20.f, 20.f});
2185*c8dee2aaSAndroid Build Coastguard Worker     drawRect({10.f, 10.f, 30.f, 30.f});
2186*c8dee2aaSAndroid Build Coastguard Worker     verifyKeys({keyADepth1, keyBDepth1}, {});
2187*c8dee2aaSAndroid Build Coastguard Worker 
2188*c8dee2aaSAndroid Build Coastguard Worker     // Drawing something contained within a previous mask also does not make a new one
2189*c8dee2aaSAndroid Build Coastguard Worker     drawRect({5.f, 5.f, 15.f, 15.f});
2190*c8dee2aaSAndroid Build Coastguard Worker     verifyKeys({keyADepth1, keyBDepth1}, {});
2191*c8dee2aaSAndroid Build Coastguard Worker 
2192*c8dee2aaSAndroid Build Coastguard Worker     // Release on destruction
2193*c8dee2aaSAndroid Build Coastguard Worker     cs = nullptr;
2194*c8dee2aaSAndroid Build Coastguard Worker     verifyKeys({}, {keyADepth1, keyBDepth1});
2195*c8dee2aaSAndroid Build Coastguard Worker }
2196