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