1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2020 Google LLC
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/ClipStack.h"
8*c8dee2aaSAndroid Build Coastguard Worker
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkAlphaType.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkBlendMode.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkClipOp.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColorSpace.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkMatrix.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPath.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRRect.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRegion.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSamplingOptions.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkScalar.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/GpuTypes.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/ganesh/GrBackendSurface.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/ganesh/GrDirectContext.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/ganesh/GrRecordingContext.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/ganesh/GrTypes.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkPoint_impl.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTArray.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTo.h"
27*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/gpu/ganesh/GrTypesPriv.h"
28*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkVx.h"
29*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkPathPriv.h"
30*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkRRectPriv.h"
31*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkRectPriv.h"
32*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkTaskGroup.h"
33*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkTraceEvent.h"
34*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/SkBackingFit.h"
35*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/Swizzle.h"
36*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrAppliedClip.h"
37*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrCaps.h"
38*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrClip.h"
39*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrColorInfo.h"
40*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrDeferredProxyUploader.h"
41*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrDirectContextPriv.h"
42*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrDrawingManager.h"
43*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrFPArgs.h"
44*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrFragmentProcessor.h"
45*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrFragmentProcessors.h"
46*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrProxyProvider.h"
47*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrRecordingContextPriv.h"
48*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrRenderTargetProxy.h"
49*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrSWMaskHelper.h"
50*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrSamplerState.h"
51*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrSurfaceProxy.h"
52*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrSurfaceProxyView.h"
53*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrTextureProxy.h"
54*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrTextureProxyPriv.h"
55*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrWindowRectangles.h"
56*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrWindowRectsState.h"
57*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/StencilMaskHelper.h"
58*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/SurfaceDrawContext.h"
59*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/effects/GrBlendFragmentProcessor.h"
60*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/effects/GrConvexPolyEffect.h"
61*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/effects/GrRRectEffect.h"
62*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/effects/GrTextureEffect.h"
63*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/geometry/GrQuad.h"
64*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/geometry/GrQuadUtils.h"
65*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/ops/AtlasPathRenderer.h"
66*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/ops/GrDrawOp.h"
67*c8dee2aaSAndroid Build Coastguard Worker
68*c8dee2aaSAndroid Build Coastguard Worker #include <algorithm>
69*c8dee2aaSAndroid Build Coastguard Worker #include <atomic>
70*c8dee2aaSAndroid Build Coastguard Worker #include <functional>
71*c8dee2aaSAndroid Build Coastguard Worker #include <tuple>
72*c8dee2aaSAndroid Build Coastguard Worker #include <utility>
73*c8dee2aaSAndroid Build Coastguard Worker
74*c8dee2aaSAndroid Build Coastguard Worker class GrOp;
75*c8dee2aaSAndroid Build Coastguard Worker struct GrShaderCaps;
76*c8dee2aaSAndroid Build Coastguard Worker
77*c8dee2aaSAndroid Build Coastguard Worker using namespace skia_private;
78*c8dee2aaSAndroid Build Coastguard Worker
79*c8dee2aaSAndroid Build Coastguard Worker namespace {
80*c8dee2aaSAndroid Build Coastguard Worker
81*c8dee2aaSAndroid Build Coastguard Worker // This captures which of the two elements in (A op B) would be required when they are combined,
82*c8dee2aaSAndroid Build Coastguard Worker // where op is intersect or difference.
83*c8dee2aaSAndroid Build Coastguard Worker enum class ClipGeometry {
84*c8dee2aaSAndroid Build Coastguard Worker kEmpty,
85*c8dee2aaSAndroid Build Coastguard Worker kAOnly,
86*c8dee2aaSAndroid Build Coastguard Worker kBOnly,
87*c8dee2aaSAndroid Build Coastguard Worker kBoth
88*c8dee2aaSAndroid Build Coastguard Worker };
89*c8dee2aaSAndroid Build Coastguard Worker
90*c8dee2aaSAndroid Build Coastguard Worker // A and B can be Element, SaveRecord, or Draw. Supported combinations are, order not mattering,
91*c8dee2aaSAndroid Build Coastguard Worker // (Element, Element), (Element, SaveRecord), (Element, Draw), and (SaveRecord, Draw).
92*c8dee2aaSAndroid Build Coastguard Worker template<typename A, typename B>
get_clip_geometry(const A & a,const B & b)93*c8dee2aaSAndroid Build Coastguard Worker ClipGeometry get_clip_geometry(const A& a, const B& b) {
94*c8dee2aaSAndroid Build Coastguard Worker // NOTE: SkIRect::Intersects() returns false when two rectangles touch at an edge (so the result
95*c8dee2aaSAndroid Build Coastguard Worker // is empty). This behavior is desired for the following clip effect policies.
96*c8dee2aaSAndroid Build Coastguard Worker if (a.op() == SkClipOp::kIntersect) {
97*c8dee2aaSAndroid Build Coastguard Worker if (b.op() == SkClipOp::kIntersect) {
98*c8dee2aaSAndroid Build Coastguard Worker // Intersect (A) + Intersect (B)
99*c8dee2aaSAndroid Build Coastguard Worker if (!SkIRect::Intersects(a.outerBounds(), b.outerBounds())) {
100*c8dee2aaSAndroid Build Coastguard Worker // Regions with non-zero coverage are disjoint, so intersection = empty
101*c8dee2aaSAndroid Build Coastguard Worker return ClipGeometry::kEmpty;
102*c8dee2aaSAndroid Build Coastguard Worker } else if (b.contains(a)) {
103*c8dee2aaSAndroid Build Coastguard Worker // B's full coverage region contains entirety of A, so intersection = A
104*c8dee2aaSAndroid Build Coastguard Worker return ClipGeometry::kAOnly;
105*c8dee2aaSAndroid Build Coastguard Worker } else if (a.contains(b)) {
106*c8dee2aaSAndroid Build Coastguard Worker // A's full coverage region contains entirety of B, so intersection = B
107*c8dee2aaSAndroid Build Coastguard Worker return ClipGeometry::kBOnly;
108*c8dee2aaSAndroid Build Coastguard Worker } else {
109*c8dee2aaSAndroid Build Coastguard Worker // The shapes intersect in some non-trivial manner
110*c8dee2aaSAndroid Build Coastguard Worker return ClipGeometry::kBoth;
111*c8dee2aaSAndroid Build Coastguard Worker }
112*c8dee2aaSAndroid Build Coastguard Worker } else {
113*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(b.op() == SkClipOp::kDifference);
114*c8dee2aaSAndroid Build Coastguard Worker // Intersect (A) + Difference (B)
115*c8dee2aaSAndroid Build Coastguard Worker if (!SkIRect::Intersects(a.outerBounds(), b.outerBounds())) {
116*c8dee2aaSAndroid Build Coastguard Worker // A only intersects B's full coverage region, so intersection = A
117*c8dee2aaSAndroid Build Coastguard Worker return ClipGeometry::kAOnly;
118*c8dee2aaSAndroid Build Coastguard Worker } else if (b.contains(a)) {
119*c8dee2aaSAndroid Build Coastguard Worker // B's zero coverage region completely contains A, so intersection = empty
120*c8dee2aaSAndroid Build Coastguard Worker return ClipGeometry::kEmpty;
121*c8dee2aaSAndroid Build Coastguard Worker } else {
122*c8dee2aaSAndroid Build Coastguard Worker // Intersection cannot be simplified. Note that the combination of a intersect
123*c8dee2aaSAndroid Build Coastguard Worker // and difference op in this order cannot produce kBOnly
124*c8dee2aaSAndroid Build Coastguard Worker return ClipGeometry::kBoth;
125*c8dee2aaSAndroid Build Coastguard Worker }
126*c8dee2aaSAndroid Build Coastguard Worker }
127*c8dee2aaSAndroid Build Coastguard Worker } else {
128*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(a.op() == SkClipOp::kDifference);
129*c8dee2aaSAndroid Build Coastguard Worker if (b.op() == SkClipOp::kIntersect) {
130*c8dee2aaSAndroid Build Coastguard Worker // Difference (A) + Intersect (B) - the mirror of Intersect(A) + Difference(B),
131*c8dee2aaSAndroid Build Coastguard Worker // but combining is commutative so this is equivalent barring naming.
132*c8dee2aaSAndroid Build Coastguard Worker if (!SkIRect::Intersects(b.outerBounds(), a.outerBounds())) {
133*c8dee2aaSAndroid Build Coastguard Worker // B only intersects A's full coverage region, so intersection = B
134*c8dee2aaSAndroid Build Coastguard Worker return ClipGeometry::kBOnly;
135*c8dee2aaSAndroid Build Coastguard Worker } else if (a.contains(b)) {
136*c8dee2aaSAndroid Build Coastguard Worker // A's zero coverage region completely contains B, so intersection = empty
137*c8dee2aaSAndroid Build Coastguard Worker return ClipGeometry::kEmpty;
138*c8dee2aaSAndroid Build Coastguard Worker } else {
139*c8dee2aaSAndroid Build Coastguard Worker // Cannot be simplified
140*c8dee2aaSAndroid Build Coastguard Worker return ClipGeometry::kBoth;
141*c8dee2aaSAndroid Build Coastguard Worker }
142*c8dee2aaSAndroid Build Coastguard Worker } else {
143*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(b.op() == SkClipOp::kDifference);
144*c8dee2aaSAndroid Build Coastguard Worker // Difference (A) + Difference (B)
145*c8dee2aaSAndroid Build Coastguard Worker if (a.contains(b)) {
146*c8dee2aaSAndroid Build Coastguard Worker // A's zero coverage region contains B, so B doesn't remove any extra
147*c8dee2aaSAndroid Build Coastguard Worker // coverage from their intersection.
148*c8dee2aaSAndroid Build Coastguard Worker return ClipGeometry::kAOnly;
149*c8dee2aaSAndroid Build Coastguard Worker } else if (b.contains(a)) {
150*c8dee2aaSAndroid Build Coastguard Worker // Mirror of the above case, intersection = B instead
151*c8dee2aaSAndroid Build Coastguard Worker return ClipGeometry::kBOnly;
152*c8dee2aaSAndroid Build Coastguard Worker } else {
153*c8dee2aaSAndroid Build Coastguard Worker // Intersection of the two differences cannot be simplified. Note that for
154*c8dee2aaSAndroid Build Coastguard Worker // this op combination it is not possible to produce kEmpty.
155*c8dee2aaSAndroid Build Coastguard Worker return ClipGeometry::kBoth;
156*c8dee2aaSAndroid Build Coastguard Worker }
157*c8dee2aaSAndroid Build Coastguard Worker }
158*c8dee2aaSAndroid Build Coastguard Worker }
159*c8dee2aaSAndroid Build Coastguard Worker }
160*c8dee2aaSAndroid Build Coastguard Worker
161*c8dee2aaSAndroid Build Coastguard Worker // a.contains(b) where a's local space is defined by 'aToDevice', and b's possibly separate local
162*c8dee2aaSAndroid Build Coastguard Worker // space is defined by 'bToDevice'. 'a' and 'b' geometry are provided in their local spaces.
163*c8dee2aaSAndroid Build Coastguard Worker // Automatically takes into account if the anti-aliasing policies differ. When the policies match,
164*c8dee2aaSAndroid Build Coastguard Worker // we assume that coverage AA or GPU's non-AA rasterization will apply to A and B equivalently, so
165*c8dee2aaSAndroid Build Coastguard Worker // we can compare the original shapes. When the modes are mixed, we outset B in device space first.
shape_contains_rect(const GrShape & a,const SkMatrix & aToDevice,const SkMatrix & deviceToA,const SkRect & b,const SkMatrix & bToDevice,bool mixedAAMode)166*c8dee2aaSAndroid Build Coastguard Worker bool shape_contains_rect(const GrShape& a, const SkMatrix& aToDevice, const SkMatrix& deviceToA,
167*c8dee2aaSAndroid Build Coastguard Worker const SkRect& b, const SkMatrix& bToDevice, bool mixedAAMode) {
168*c8dee2aaSAndroid Build Coastguard Worker if (!a.convex()) {
169*c8dee2aaSAndroid Build Coastguard Worker return false;
170*c8dee2aaSAndroid Build Coastguard Worker }
171*c8dee2aaSAndroid Build Coastguard Worker
172*c8dee2aaSAndroid Build Coastguard Worker if (!mixedAAMode && aToDevice == bToDevice) {
173*c8dee2aaSAndroid Build Coastguard Worker // A and B are in the same coordinate space, so don't bother mapping
174*c8dee2aaSAndroid Build Coastguard Worker return a.conservativeContains(b);
175*c8dee2aaSAndroid Build Coastguard Worker } else if (bToDevice.isIdentity() && aToDevice.preservesAxisAlignment()) {
176*c8dee2aaSAndroid Build Coastguard Worker // Optimize the common case of draws (B, with identity matrix) and axis-aligned shapes,
177*c8dee2aaSAndroid Build Coastguard Worker // instead of checking the four corners separately.
178*c8dee2aaSAndroid Build Coastguard Worker SkRect bInA = b;
179*c8dee2aaSAndroid Build Coastguard Worker if (mixedAAMode) {
180*c8dee2aaSAndroid Build Coastguard Worker bInA.outset(0.5f, 0.5f);
181*c8dee2aaSAndroid Build Coastguard Worker }
182*c8dee2aaSAndroid Build Coastguard Worker SkAssertResult(deviceToA.mapRect(&bInA));
183*c8dee2aaSAndroid Build Coastguard Worker return a.conservativeContains(bInA);
184*c8dee2aaSAndroid Build Coastguard Worker }
185*c8dee2aaSAndroid Build Coastguard Worker
186*c8dee2aaSAndroid Build Coastguard Worker // Test each corner for contains; since a is convex, if all 4 corners of b's bounds are
187*c8dee2aaSAndroid Build Coastguard Worker // contained, then the entirety of b is within a.
188*c8dee2aaSAndroid Build Coastguard Worker GrQuad deviceQuad = GrQuad::MakeFromRect(b, bToDevice);
189*c8dee2aaSAndroid Build Coastguard Worker if (mixedAAMode) {
190*c8dee2aaSAndroid Build Coastguard Worker // Outset it so its edges are 1/2px out, giving us a buffer to avoid cases where a non-AA
191*c8dee2aaSAndroid Build Coastguard Worker // clip or draw would snap outside an aa element.
192*c8dee2aaSAndroid Build Coastguard Worker GrQuadUtils::Outset({0.5f, 0.5f, 0.5f, 0.5f}, &deviceQuad);
193*c8dee2aaSAndroid Build Coastguard Worker }
194*c8dee2aaSAndroid Build Coastguard Worker if (any(deviceQuad.w4f() < SkPathPriv::kW0PlaneDistance)) {
195*c8dee2aaSAndroid Build Coastguard Worker // Something in B actually projects behind the W = 0 plane and would be clipped to infinity,
196*c8dee2aaSAndroid Build Coastguard Worker // so it's extremely unlikely that A can contain B.
197*c8dee2aaSAndroid Build Coastguard Worker return false;
198*c8dee2aaSAndroid Build Coastguard Worker }
199*c8dee2aaSAndroid Build Coastguard Worker
200*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < 4; ++i) {
201*c8dee2aaSAndroid Build Coastguard Worker SkPoint cornerInA = deviceQuad.point(i);
202*c8dee2aaSAndroid Build Coastguard Worker deviceToA.mapPoints(&cornerInA, 1);
203*c8dee2aaSAndroid Build Coastguard Worker if (!a.conservativeContains(cornerInA)) {
204*c8dee2aaSAndroid Build Coastguard Worker return false;
205*c8dee2aaSAndroid Build Coastguard Worker }
206*c8dee2aaSAndroid Build Coastguard Worker }
207*c8dee2aaSAndroid Build Coastguard Worker
208*c8dee2aaSAndroid Build Coastguard Worker return true;
209*c8dee2aaSAndroid Build Coastguard Worker }
210*c8dee2aaSAndroid Build Coastguard Worker
subtract(const SkIRect & a,const SkIRect & b,bool exact)211*c8dee2aaSAndroid Build Coastguard Worker SkIRect subtract(const SkIRect& a, const SkIRect& b, bool exact) {
212*c8dee2aaSAndroid Build Coastguard Worker SkIRect diff;
213*c8dee2aaSAndroid Build Coastguard Worker if (SkRectPriv::Subtract(a, b, &diff) || !exact) {
214*c8dee2aaSAndroid Build Coastguard Worker // Either A-B is exactly the rectangle stored in diff, or we don't need an exact answer
215*c8dee2aaSAndroid Build Coastguard Worker // and can settle for the subrect of A excluded from B (which is also 'diff')
216*c8dee2aaSAndroid Build Coastguard Worker return diff;
217*c8dee2aaSAndroid Build Coastguard Worker } else {
218*c8dee2aaSAndroid Build Coastguard Worker // For our purposes, we want the original A when A-B cannot be exactly represented
219*c8dee2aaSAndroid Build Coastguard Worker return a;
220*c8dee2aaSAndroid Build Coastguard Worker }
221*c8dee2aaSAndroid Build Coastguard Worker }
222*c8dee2aaSAndroid Build Coastguard Worker
get_clip_edge_type(SkClipOp op,GrAA aa)223*c8dee2aaSAndroid Build Coastguard Worker GrClipEdgeType get_clip_edge_type(SkClipOp op, GrAA aa) {
224*c8dee2aaSAndroid Build Coastguard Worker if (op == SkClipOp::kIntersect) {
225*c8dee2aaSAndroid Build Coastguard Worker return aa == GrAA::kYes ? GrClipEdgeType::kFillAA : GrClipEdgeType::kFillBW;
226*c8dee2aaSAndroid Build Coastguard Worker } else {
227*c8dee2aaSAndroid Build Coastguard Worker return aa == GrAA::kYes ? GrClipEdgeType::kInverseFillAA : GrClipEdgeType::kInverseFillBW;
228*c8dee2aaSAndroid Build Coastguard Worker }
229*c8dee2aaSAndroid Build Coastguard Worker }
230*c8dee2aaSAndroid Build Coastguard Worker
231*c8dee2aaSAndroid Build Coastguard Worker static uint32_t kInvalidGenID = 0;
232*c8dee2aaSAndroid Build Coastguard Worker static uint32_t kEmptyGenID = 1;
233*c8dee2aaSAndroid Build Coastguard Worker static uint32_t kWideOpenGenID = 2;
234*c8dee2aaSAndroid Build Coastguard Worker
next_gen_id()235*c8dee2aaSAndroid Build Coastguard Worker uint32_t next_gen_id() {
236*c8dee2aaSAndroid Build Coastguard Worker // 0-2 are reserved for invalid, empty & wide-open
237*c8dee2aaSAndroid Build Coastguard Worker static const uint32_t kFirstUnreservedGenID = 3;
238*c8dee2aaSAndroid Build Coastguard Worker static std::atomic<uint32_t> nextID{kFirstUnreservedGenID};
239*c8dee2aaSAndroid Build Coastguard Worker
240*c8dee2aaSAndroid Build Coastguard Worker uint32_t id;
241*c8dee2aaSAndroid Build Coastguard Worker do {
242*c8dee2aaSAndroid Build Coastguard Worker id = nextID.fetch_add(1, std::memory_order_relaxed);
243*c8dee2aaSAndroid Build Coastguard Worker } while (id < kFirstUnreservedGenID);
244*c8dee2aaSAndroid Build Coastguard Worker return id;
245*c8dee2aaSAndroid Build Coastguard Worker }
246*c8dee2aaSAndroid Build Coastguard Worker
247*c8dee2aaSAndroid Build Coastguard Worker // Functions for rendering / applying clip shapes in various ways
248*c8dee2aaSAndroid Build Coastguard Worker // The general strategy is:
249*c8dee2aaSAndroid Build Coastguard Worker // - Represent the clip element as an analytic FP that tests sk_FragCoord vs. its device shape
250*c8dee2aaSAndroid Build Coastguard Worker // - Render the clip element to the stencil, if stencil is allowed and supports the AA, and the
251*c8dee2aaSAndroid Build Coastguard Worker // size of the element indicates stenciling will be worth it, vs. making a mask.
252*c8dee2aaSAndroid Build Coastguard Worker // - Try to put the individual element into a clip atlas, which is then sampled during the draw
253*c8dee2aaSAndroid Build Coastguard Worker // - Render the element into a SW mask and upload it. If possible, the SW rasterization happens
254*c8dee2aaSAndroid Build Coastguard Worker // in parallel.
255*c8dee2aaSAndroid Build Coastguard Worker static constexpr GrSurfaceOrigin kMaskOrigin = kTopLeft_GrSurfaceOrigin;
256*c8dee2aaSAndroid Build Coastguard Worker
analytic_clip_fp(const skgpu::ganesh::ClipStack::Element & e,const GrShaderCaps & caps,std::unique_ptr<GrFragmentProcessor> fp)257*c8dee2aaSAndroid Build Coastguard Worker GrFPResult analytic_clip_fp(const skgpu::ganesh::ClipStack::Element& e,
258*c8dee2aaSAndroid Build Coastguard Worker const GrShaderCaps& caps,
259*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<GrFragmentProcessor> fp) {
260*c8dee2aaSAndroid Build Coastguard Worker // All analytic clip shape FPs need to be in device space
261*c8dee2aaSAndroid Build Coastguard Worker GrClipEdgeType edgeType = get_clip_edge_type(e.fOp, e.fAA);
262*c8dee2aaSAndroid Build Coastguard Worker if (e.fLocalToDevice.isIdentity()) {
263*c8dee2aaSAndroid Build Coastguard Worker if (e.fShape.isRect()) {
264*c8dee2aaSAndroid Build Coastguard Worker return GrFPSuccess(GrFragmentProcessor::Rect(std::move(fp), edgeType, e.fShape.rect()));
265*c8dee2aaSAndroid Build Coastguard Worker } else if (e.fShape.isRRect()) {
266*c8dee2aaSAndroid Build Coastguard Worker return GrRRectEffect::Make(std::move(fp), edgeType, e.fShape.rrect(), caps);
267*c8dee2aaSAndroid Build Coastguard Worker }
268*c8dee2aaSAndroid Build Coastguard Worker }
269*c8dee2aaSAndroid Build Coastguard Worker
270*c8dee2aaSAndroid Build Coastguard Worker // A convex hull can be transformed into device space (this will handle rect shapes with a
271*c8dee2aaSAndroid Build Coastguard Worker // non-identity transform).
272*c8dee2aaSAndroid Build Coastguard Worker if (e.fShape.segmentMask() == SkPath::kLine_SegmentMask && e.fShape.convex()) {
273*c8dee2aaSAndroid Build Coastguard Worker SkPath devicePath;
274*c8dee2aaSAndroid Build Coastguard Worker e.fShape.asPath(&devicePath);
275*c8dee2aaSAndroid Build Coastguard Worker devicePath.transform(e.fLocalToDevice);
276*c8dee2aaSAndroid Build Coastguard Worker return GrConvexPolyEffect::Make(std::move(fp), edgeType, devicePath);
277*c8dee2aaSAndroid Build Coastguard Worker }
278*c8dee2aaSAndroid Build Coastguard Worker
279*c8dee2aaSAndroid Build Coastguard Worker return GrFPFailure(std::move(fp));
280*c8dee2aaSAndroid Build Coastguard Worker }
281*c8dee2aaSAndroid Build Coastguard Worker
282*c8dee2aaSAndroid Build Coastguard Worker // TODO: Currently this only works with tessellation because the tessellation path renderer owns and
283*c8dee2aaSAndroid Build Coastguard Worker // manages the atlas. The high-level concept could be generalized to support any path renderer going
284*c8dee2aaSAndroid Build Coastguard Worker // into a shared atlas.
clip_atlas_fp(const skgpu::ganesh::SurfaceDrawContext * sdc,const GrOp * opBeingClipped,skgpu::ganesh::AtlasPathRenderer * atlasPathRenderer,const SkIRect & scissorBounds,const skgpu::ganesh::ClipStack::Element & e,std::unique_ptr<GrFragmentProcessor> inputFP)285*c8dee2aaSAndroid Build Coastguard Worker GrFPResult clip_atlas_fp(const skgpu::ganesh::SurfaceDrawContext* sdc,
286*c8dee2aaSAndroid Build Coastguard Worker const GrOp* opBeingClipped,
287*c8dee2aaSAndroid Build Coastguard Worker skgpu::ganesh::AtlasPathRenderer* atlasPathRenderer,
288*c8dee2aaSAndroid Build Coastguard Worker const SkIRect& scissorBounds,
289*c8dee2aaSAndroid Build Coastguard Worker const skgpu::ganesh::ClipStack::Element& e,
290*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<GrFragmentProcessor> inputFP) {
291*c8dee2aaSAndroid Build Coastguard Worker if (e.fAA != GrAA::kYes) {
292*c8dee2aaSAndroid Build Coastguard Worker return GrFPFailure(std::move(inputFP));
293*c8dee2aaSAndroid Build Coastguard Worker }
294*c8dee2aaSAndroid Build Coastguard Worker SkPath path;
295*c8dee2aaSAndroid Build Coastguard Worker e.fShape.asPath(&path);
296*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!path.isInverseFillType());
297*c8dee2aaSAndroid Build Coastguard Worker if (e.fOp == SkClipOp::kDifference) {
298*c8dee2aaSAndroid Build Coastguard Worker // Toggling fill type does not affect the path's "generationID" key.
299*c8dee2aaSAndroid Build Coastguard Worker path.toggleInverseFillType();
300*c8dee2aaSAndroid Build Coastguard Worker }
301*c8dee2aaSAndroid Build Coastguard Worker return atlasPathRenderer->makeAtlasClipEffect(sdc, opBeingClipped, std::move(inputFP),
302*c8dee2aaSAndroid Build Coastguard Worker scissorBounds, e.fLocalToDevice, path);
303*c8dee2aaSAndroid Build Coastguard Worker }
304*c8dee2aaSAndroid Build Coastguard Worker
draw_to_sw_mask(GrSWMaskHelper * helper,const skgpu::ganesh::ClipStack::Element & e,bool clearMask)305*c8dee2aaSAndroid Build Coastguard Worker void draw_to_sw_mask(GrSWMaskHelper* helper,
306*c8dee2aaSAndroid Build Coastguard Worker const skgpu::ganesh::ClipStack::Element& e,
307*c8dee2aaSAndroid Build Coastguard Worker bool clearMask) {
308*c8dee2aaSAndroid Build Coastguard Worker // If the first element to draw is an intersect, we clear to 0 and will draw it directly with
309*c8dee2aaSAndroid Build Coastguard Worker // coverage 1 (subsequent intersect elements will be inverse-filled and draw 0 outside).
310*c8dee2aaSAndroid Build Coastguard Worker // If the first element to draw is a difference, we clear to 1, and in all cases we draw the
311*c8dee2aaSAndroid Build Coastguard Worker // difference element directly with coverage 0.
312*c8dee2aaSAndroid Build Coastguard Worker if (clearMask) {
313*c8dee2aaSAndroid Build Coastguard Worker helper->clear(e.fOp == SkClipOp::kIntersect ? 0x00 : 0xFF);
314*c8dee2aaSAndroid Build Coastguard Worker }
315*c8dee2aaSAndroid Build Coastguard Worker
316*c8dee2aaSAndroid Build Coastguard Worker uint8_t alpha;
317*c8dee2aaSAndroid Build Coastguard Worker bool invert;
318*c8dee2aaSAndroid Build Coastguard Worker if (e.fOp == SkClipOp::kIntersect) {
319*c8dee2aaSAndroid Build Coastguard Worker // Intersect modifies pixels outside of its geometry. If this isn't the first op, we
320*c8dee2aaSAndroid Build Coastguard Worker // draw the inverse-filled shape with 0 coverage to erase everything outside the element
321*c8dee2aaSAndroid Build Coastguard Worker // But if we are the first element, we can draw directly with coverage 1 since we
322*c8dee2aaSAndroid Build Coastguard Worker // cleared to 0.
323*c8dee2aaSAndroid Build Coastguard Worker if (clearMask) {
324*c8dee2aaSAndroid Build Coastguard Worker alpha = 0xFF;
325*c8dee2aaSAndroid Build Coastguard Worker invert = false;
326*c8dee2aaSAndroid Build Coastguard Worker } else {
327*c8dee2aaSAndroid Build Coastguard Worker alpha = 0x00;
328*c8dee2aaSAndroid Build Coastguard Worker invert = true;
329*c8dee2aaSAndroid Build Coastguard Worker }
330*c8dee2aaSAndroid Build Coastguard Worker } else {
331*c8dee2aaSAndroid Build Coastguard Worker // For difference ops, can always just subtract the shape directly by drawing 0 coverage
332*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(e.fOp == SkClipOp::kDifference);
333*c8dee2aaSAndroid Build Coastguard Worker alpha = 0x00;
334*c8dee2aaSAndroid Build Coastguard Worker invert = false;
335*c8dee2aaSAndroid Build Coastguard Worker }
336*c8dee2aaSAndroid Build Coastguard Worker
337*c8dee2aaSAndroid Build Coastguard Worker // Draw the shape; based on how we've initialized the buffer and chosen alpha+invert,
338*c8dee2aaSAndroid Build Coastguard Worker // every element is drawn with the kReplace_Op
339*c8dee2aaSAndroid Build Coastguard Worker if (invert) {
340*c8dee2aaSAndroid Build Coastguard Worker // Must invert the path
341*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!e.fShape.inverted());
342*c8dee2aaSAndroid Build Coastguard Worker // TODO: this is an extra copy effectively, just so we can toggle inversion; would be
343*c8dee2aaSAndroid Build Coastguard Worker // better perhaps to just call a drawPath() since we know it'll use path rendering w/
344*c8dee2aaSAndroid Build Coastguard Worker // the inverse fill type.
345*c8dee2aaSAndroid Build Coastguard Worker GrShape inverted(e.fShape);
346*c8dee2aaSAndroid Build Coastguard Worker inverted.setInverted(true);
347*c8dee2aaSAndroid Build Coastguard Worker helper->drawShape(inverted, e.fLocalToDevice, e.fAA, alpha);
348*c8dee2aaSAndroid Build Coastguard Worker } else {
349*c8dee2aaSAndroid Build Coastguard Worker helper->drawShape(e.fShape, e.fLocalToDevice, e.fAA, alpha);
350*c8dee2aaSAndroid Build Coastguard Worker }
351*c8dee2aaSAndroid Build Coastguard Worker }
352*c8dee2aaSAndroid Build Coastguard Worker
render_sw_mask(GrRecordingContext * context,const SkIRect & bounds,const skgpu::ganesh::ClipStack::Element ** elements,int count)353*c8dee2aaSAndroid Build Coastguard Worker GrSurfaceProxyView render_sw_mask(GrRecordingContext* context,
354*c8dee2aaSAndroid Build Coastguard Worker const SkIRect& bounds,
355*c8dee2aaSAndroid Build Coastguard Worker const skgpu::ganesh::ClipStack::Element** elements,
356*c8dee2aaSAndroid Build Coastguard Worker int count) {
357*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(count > 0);
358*c8dee2aaSAndroid Build Coastguard Worker
359*c8dee2aaSAndroid Build Coastguard Worker SkTaskGroup* taskGroup = nullptr;
360*c8dee2aaSAndroid Build Coastguard Worker if (auto direct = context->asDirectContext()) {
361*c8dee2aaSAndroid Build Coastguard Worker taskGroup = direct->priv().getTaskGroup();
362*c8dee2aaSAndroid Build Coastguard Worker }
363*c8dee2aaSAndroid Build Coastguard Worker
364*c8dee2aaSAndroid Build Coastguard Worker if (taskGroup) {
365*c8dee2aaSAndroid Build Coastguard Worker const GrCaps* caps = context->priv().caps();
366*c8dee2aaSAndroid Build Coastguard Worker GrProxyProvider* proxyProvider = context->priv().proxyProvider();
367*c8dee2aaSAndroid Build Coastguard Worker
368*c8dee2aaSAndroid Build Coastguard Worker // Create our texture proxy
369*c8dee2aaSAndroid Build Coastguard Worker GrBackendFormat format = caps->getDefaultBackendFormat(GrColorType::kAlpha_8,
370*c8dee2aaSAndroid Build Coastguard Worker GrRenderable::kNo);
371*c8dee2aaSAndroid Build Coastguard Worker
372*c8dee2aaSAndroid Build Coastguard Worker skgpu::Swizzle swizzle = context->priv().caps()->getReadSwizzle(format,
373*c8dee2aaSAndroid Build Coastguard Worker GrColorType::kAlpha_8);
374*c8dee2aaSAndroid Build Coastguard Worker auto proxy = proxyProvider->createProxy(format,
375*c8dee2aaSAndroid Build Coastguard Worker bounds.size(),
376*c8dee2aaSAndroid Build Coastguard Worker GrRenderable::kNo,
377*c8dee2aaSAndroid Build Coastguard Worker 1,
378*c8dee2aaSAndroid Build Coastguard Worker skgpu::Mipmapped::kNo,
379*c8dee2aaSAndroid Build Coastguard Worker SkBackingFit::kApprox,
380*c8dee2aaSAndroid Build Coastguard Worker skgpu::Budgeted::kYes,
381*c8dee2aaSAndroid Build Coastguard Worker GrProtected::kNo,
382*c8dee2aaSAndroid Build Coastguard Worker /*label=*/"ClipStack_RenderSwMask");
383*c8dee2aaSAndroid Build Coastguard Worker
384*c8dee2aaSAndroid Build Coastguard Worker // Since this will be rendered on another thread, make a copy of the elements in case
385*c8dee2aaSAndroid Build Coastguard Worker // the clip stack is modified on the main thread
386*c8dee2aaSAndroid Build Coastguard Worker using Uploader = GrTDeferredProxyUploader<TArray<skgpu::ganesh::ClipStack::Element>>;
387*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<Uploader> uploader = std::make_unique<Uploader>(count);
388*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < count; ++i) {
389*c8dee2aaSAndroid Build Coastguard Worker uploader->data().push_back(*(elements[i]));
390*c8dee2aaSAndroid Build Coastguard Worker }
391*c8dee2aaSAndroid Build Coastguard Worker
392*c8dee2aaSAndroid Build Coastguard Worker Uploader* uploaderRaw = uploader.get();
393*c8dee2aaSAndroid Build Coastguard Worker auto drawAndUploadMask = [uploaderRaw, bounds] {
394*c8dee2aaSAndroid Build Coastguard Worker TRACE_EVENT0("skia.gpu", "Threaded SW Clip Mask Render");
395*c8dee2aaSAndroid Build Coastguard Worker GrSWMaskHelper helper(uploaderRaw->getPixels());
396*c8dee2aaSAndroid Build Coastguard Worker if (helper.init(bounds)) {
397*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < uploaderRaw->data().size(); ++i) {
398*c8dee2aaSAndroid Build Coastguard Worker draw_to_sw_mask(&helper, uploaderRaw->data()[i], i == 0);
399*c8dee2aaSAndroid Build Coastguard Worker }
400*c8dee2aaSAndroid Build Coastguard Worker } else {
401*c8dee2aaSAndroid Build Coastguard Worker SkDEBUGFAIL("Unable to allocate SW clip mask.");
402*c8dee2aaSAndroid Build Coastguard Worker }
403*c8dee2aaSAndroid Build Coastguard Worker uploaderRaw->signalAndFreeData();
404*c8dee2aaSAndroid Build Coastguard Worker };
405*c8dee2aaSAndroid Build Coastguard Worker
406*c8dee2aaSAndroid Build Coastguard Worker taskGroup->add(std::move(drawAndUploadMask));
407*c8dee2aaSAndroid Build Coastguard Worker proxy->texPriv().setDeferredUploader(std::move(uploader));
408*c8dee2aaSAndroid Build Coastguard Worker
409*c8dee2aaSAndroid Build Coastguard Worker return {std::move(proxy), kMaskOrigin, swizzle};
410*c8dee2aaSAndroid Build Coastguard Worker } else {
411*c8dee2aaSAndroid Build Coastguard Worker GrSWMaskHelper helper;
412*c8dee2aaSAndroid Build Coastguard Worker if (!helper.init(bounds)) {
413*c8dee2aaSAndroid Build Coastguard Worker return {};
414*c8dee2aaSAndroid Build Coastguard Worker }
415*c8dee2aaSAndroid Build Coastguard Worker
416*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < count; ++i) {
417*c8dee2aaSAndroid Build Coastguard Worker draw_to_sw_mask(&helper,*(elements[i]), i == 0);
418*c8dee2aaSAndroid Build Coastguard Worker }
419*c8dee2aaSAndroid Build Coastguard Worker
420*c8dee2aaSAndroid Build Coastguard Worker return helper.toTextureView(context, SkBackingFit::kApprox);
421*c8dee2aaSAndroid Build Coastguard Worker }
422*c8dee2aaSAndroid Build Coastguard Worker }
423*c8dee2aaSAndroid Build Coastguard Worker
render_stencil_mask(GrRecordingContext * rContext,skgpu::ganesh::SurfaceDrawContext * sdc,uint32_t genID,const SkIRect & bounds,const skgpu::ganesh::ClipStack::Element ** elements,int count,GrAppliedClip * out)424*c8dee2aaSAndroid Build Coastguard Worker void render_stencil_mask(GrRecordingContext* rContext,
425*c8dee2aaSAndroid Build Coastguard Worker skgpu::ganesh::SurfaceDrawContext* sdc,
426*c8dee2aaSAndroid Build Coastguard Worker uint32_t genID,
427*c8dee2aaSAndroid Build Coastguard Worker const SkIRect& bounds,
428*c8dee2aaSAndroid Build Coastguard Worker const skgpu::ganesh::ClipStack::Element** elements,
429*c8dee2aaSAndroid Build Coastguard Worker int count,
430*c8dee2aaSAndroid Build Coastguard Worker GrAppliedClip* out) {
431*c8dee2aaSAndroid Build Coastguard Worker skgpu::ganesh::StencilMaskHelper helper(rContext, sdc);
432*c8dee2aaSAndroid Build Coastguard Worker if (helper.init(bounds, genID, out->windowRectsState().windows(), 0)) {
433*c8dee2aaSAndroid Build Coastguard Worker // This follows the same logic as in draw_sw_mask
434*c8dee2aaSAndroid Build Coastguard Worker bool startInside = elements[0]->fOp == SkClipOp::kDifference;
435*c8dee2aaSAndroid Build Coastguard Worker helper.clear(startInside);
436*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < count; ++i) {
437*c8dee2aaSAndroid Build Coastguard Worker const skgpu::ganesh::ClipStack::Element& e = *(elements[i]);
438*c8dee2aaSAndroid Build Coastguard Worker SkRegion::Op op;
439*c8dee2aaSAndroid Build Coastguard Worker if (e.fOp == SkClipOp::kIntersect) {
440*c8dee2aaSAndroid Build Coastguard Worker op = (i == 0) ? SkRegion::kReplace_Op : SkRegion::kIntersect_Op;
441*c8dee2aaSAndroid Build Coastguard Worker } else {
442*c8dee2aaSAndroid Build Coastguard Worker op = SkRegion::kDifference_Op;
443*c8dee2aaSAndroid Build Coastguard Worker }
444*c8dee2aaSAndroid Build Coastguard Worker helper.drawShape(e.fShape, e.fLocalToDevice, op, e.fAA);
445*c8dee2aaSAndroid Build Coastguard Worker }
446*c8dee2aaSAndroid Build Coastguard Worker helper.finish();
447*c8dee2aaSAndroid Build Coastguard Worker }
448*c8dee2aaSAndroid Build Coastguard Worker out->hardClip().addStencilClip(genID);
449*c8dee2aaSAndroid Build Coastguard Worker }
450*c8dee2aaSAndroid Build Coastguard Worker
451*c8dee2aaSAndroid Build Coastguard Worker } // anonymous namespace
452*c8dee2aaSAndroid Build Coastguard Worker
453*c8dee2aaSAndroid Build Coastguard Worker namespace skgpu::ganesh {
454*c8dee2aaSAndroid Build Coastguard Worker
455*c8dee2aaSAndroid Build Coastguard Worker class ClipStack::Draw {
456*c8dee2aaSAndroid Build Coastguard Worker public:
Draw(const SkRect & drawBounds,GrAA aa)457*c8dee2aaSAndroid Build Coastguard Worker Draw(const SkRect& drawBounds, GrAA aa)
458*c8dee2aaSAndroid Build Coastguard Worker : fBounds(GrClip::GetPixelIBounds(drawBounds, aa, BoundsType::kExterior))
459*c8dee2aaSAndroid Build Coastguard Worker , fAA(aa) {
460*c8dee2aaSAndroid Build Coastguard Worker // Be slightly more forgiving on whether or not a draw is inside a clip element.
461*c8dee2aaSAndroid Build Coastguard Worker fOriginalBounds = drawBounds.makeInset(GrClip::kBoundsTolerance, GrClip::kBoundsTolerance);
462*c8dee2aaSAndroid Build Coastguard Worker if (fOriginalBounds.isEmpty()) {
463*c8dee2aaSAndroid Build Coastguard Worker fOriginalBounds = drawBounds;
464*c8dee2aaSAndroid Build Coastguard Worker }
465*c8dee2aaSAndroid Build Coastguard Worker }
466*c8dee2aaSAndroid Build Coastguard Worker
467*c8dee2aaSAndroid Build Coastguard Worker // Common clip type interface
op() const468*c8dee2aaSAndroid Build Coastguard Worker SkClipOp op() const { return SkClipOp::kIntersect; }
outerBounds() const469*c8dee2aaSAndroid Build Coastguard Worker const SkIRect& outerBounds() const { return fBounds; }
470*c8dee2aaSAndroid Build Coastguard Worker
471*c8dee2aaSAndroid Build Coastguard Worker // Draw does not have inner bounds so cannot contain anything.
contains(const RawElement & e) const472*c8dee2aaSAndroid Build Coastguard Worker bool contains(const RawElement& e) const { return false; }
contains(const SaveRecord & s) const473*c8dee2aaSAndroid Build Coastguard Worker bool contains(const SaveRecord& s) const { return false; }
474*c8dee2aaSAndroid Build Coastguard Worker
applyDeviceBounds(const SkIRect & deviceBounds)475*c8dee2aaSAndroid Build Coastguard Worker bool applyDeviceBounds(const SkIRect& deviceBounds) {
476*c8dee2aaSAndroid Build Coastguard Worker return fBounds.intersect(deviceBounds);
477*c8dee2aaSAndroid Build Coastguard Worker }
478*c8dee2aaSAndroid Build Coastguard Worker
bounds() const479*c8dee2aaSAndroid Build Coastguard Worker const SkRect& bounds() const { return fOriginalBounds; }
aa() const480*c8dee2aaSAndroid Build Coastguard Worker GrAA aa() const { return fAA; }
481*c8dee2aaSAndroid Build Coastguard Worker
482*c8dee2aaSAndroid Build Coastguard Worker private:
483*c8dee2aaSAndroid Build Coastguard Worker SkRect fOriginalBounds;
484*c8dee2aaSAndroid Build Coastguard Worker SkIRect fBounds;
485*c8dee2aaSAndroid Build Coastguard Worker GrAA fAA;
486*c8dee2aaSAndroid Build Coastguard Worker };
487*c8dee2aaSAndroid Build Coastguard Worker
488*c8dee2aaSAndroid Build Coastguard Worker ///////////////////////////////////////////////////////////////////////////////
489*c8dee2aaSAndroid Build Coastguard Worker // ClipStack::Element
490*c8dee2aaSAndroid Build Coastguard Worker
RawElement(const SkMatrix & localToDevice,const GrShape & shape,GrAA aa,SkClipOp op)491*c8dee2aaSAndroid Build Coastguard Worker ClipStack::RawElement::RawElement(const SkMatrix& localToDevice, const GrShape& shape,
492*c8dee2aaSAndroid Build Coastguard Worker GrAA aa, SkClipOp op)
493*c8dee2aaSAndroid Build Coastguard Worker : Element{shape, localToDevice, op, aa}
494*c8dee2aaSAndroid Build Coastguard Worker , fInnerBounds(SkIRect::MakeEmpty())
495*c8dee2aaSAndroid Build Coastguard Worker , fOuterBounds(SkIRect::MakeEmpty())
496*c8dee2aaSAndroid Build Coastguard Worker , fInvalidatedByIndex(-1) {
497*c8dee2aaSAndroid Build Coastguard Worker if (!localToDevice.invert(&fDeviceToLocal)) {
498*c8dee2aaSAndroid Build Coastguard Worker // If the transform can't be inverted, it means that two dimensions are collapsed to 0 or
499*c8dee2aaSAndroid Build Coastguard Worker // 1 dimension, making the device-space geometry effectively empty.
500*c8dee2aaSAndroid Build Coastguard Worker fShape.reset();
501*c8dee2aaSAndroid Build Coastguard Worker }
502*c8dee2aaSAndroid Build Coastguard Worker }
503*c8dee2aaSAndroid Build Coastguard Worker
markInvalid(const SaveRecord & current)504*c8dee2aaSAndroid Build Coastguard Worker void ClipStack::RawElement::markInvalid(const SaveRecord& current) {
505*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!this->isInvalid());
506*c8dee2aaSAndroid Build Coastguard Worker fInvalidatedByIndex = current.firstActiveElementIndex();
507*c8dee2aaSAndroid Build Coastguard Worker }
508*c8dee2aaSAndroid Build Coastguard Worker
restoreValid(const SaveRecord & current)509*c8dee2aaSAndroid Build Coastguard Worker void ClipStack::RawElement::restoreValid(const SaveRecord& current) {
510*c8dee2aaSAndroid Build Coastguard Worker if (current.firstActiveElementIndex() < fInvalidatedByIndex) {
511*c8dee2aaSAndroid Build Coastguard Worker fInvalidatedByIndex = -1;
512*c8dee2aaSAndroid Build Coastguard Worker }
513*c8dee2aaSAndroid Build Coastguard Worker }
514*c8dee2aaSAndroid Build Coastguard Worker
contains(const Draw & d) const515*c8dee2aaSAndroid Build Coastguard Worker bool ClipStack::RawElement::contains(const Draw& d) const {
516*c8dee2aaSAndroid Build Coastguard Worker if (fInnerBounds.contains(d.outerBounds())) {
517*c8dee2aaSAndroid Build Coastguard Worker return true;
518*c8dee2aaSAndroid Build Coastguard Worker } else {
519*c8dee2aaSAndroid Build Coastguard Worker // If the draw is non-AA, use the already computed outer bounds so we don't need to use
520*c8dee2aaSAndroid Build Coastguard Worker // device-space outsetting inside shape_contains_rect.
521*c8dee2aaSAndroid Build Coastguard Worker SkRect queryBounds = d.aa() == GrAA::kYes ? d.bounds() : SkRect::Make(d.outerBounds());
522*c8dee2aaSAndroid Build Coastguard Worker return shape_contains_rect(fShape, fLocalToDevice, fDeviceToLocal,
523*c8dee2aaSAndroid Build Coastguard Worker queryBounds, SkMatrix::I(), /* mixed-aa */ false);
524*c8dee2aaSAndroid Build Coastguard Worker }
525*c8dee2aaSAndroid Build Coastguard Worker }
526*c8dee2aaSAndroid Build Coastguard Worker
contains(const SaveRecord & s) const527*c8dee2aaSAndroid Build Coastguard Worker bool ClipStack::RawElement::contains(const SaveRecord& s) const {
528*c8dee2aaSAndroid Build Coastguard Worker if (fInnerBounds.contains(s.outerBounds())) {
529*c8dee2aaSAndroid Build Coastguard Worker return true;
530*c8dee2aaSAndroid Build Coastguard Worker } else {
531*c8dee2aaSAndroid Build Coastguard Worker // This is very similar to contains(Draw) but we just have outerBounds to work with.
532*c8dee2aaSAndroid Build Coastguard Worker SkRect queryBounds = SkRect::Make(s.outerBounds());
533*c8dee2aaSAndroid Build Coastguard Worker return shape_contains_rect(fShape, fLocalToDevice, fDeviceToLocal,
534*c8dee2aaSAndroid Build Coastguard Worker queryBounds, SkMatrix::I(), /* mixed-aa */ false);
535*c8dee2aaSAndroid Build Coastguard Worker }
536*c8dee2aaSAndroid Build Coastguard Worker }
537*c8dee2aaSAndroid Build Coastguard Worker
contains(const RawElement & e) const538*c8dee2aaSAndroid Build Coastguard Worker bool ClipStack::RawElement::contains(const RawElement& e) const {
539*c8dee2aaSAndroid Build Coastguard Worker // This is similar to how RawElement checks containment for a Draw, except that both the tester
540*c8dee2aaSAndroid Build Coastguard Worker // and testee have a transform that needs to be considered.
541*c8dee2aaSAndroid Build Coastguard Worker if (fInnerBounds.contains(e.fOuterBounds)) {
542*c8dee2aaSAndroid Build Coastguard Worker return true;
543*c8dee2aaSAndroid Build Coastguard Worker }
544*c8dee2aaSAndroid Build Coastguard Worker
545*c8dee2aaSAndroid Build Coastguard Worker bool mixedAA = fAA != e.fAA;
546*c8dee2aaSAndroid Build Coastguard Worker if (!mixedAA && fLocalToDevice == e.fLocalToDevice) {
547*c8dee2aaSAndroid Build Coastguard Worker // Test the shapes directly against each other, with a special check for a rrect+rrect
548*c8dee2aaSAndroid Build Coastguard Worker // containment (a intersect b == a implies b contains a) and paths (same gen ID, or same
549*c8dee2aaSAndroid Build Coastguard Worker // path for small paths means they contain each other).
550*c8dee2aaSAndroid Build Coastguard Worker static constexpr int kMaxPathComparePoints = 16;
551*c8dee2aaSAndroid Build Coastguard Worker if (fShape.isRRect() && e.fShape.isRRect()) {
552*c8dee2aaSAndroid Build Coastguard Worker return SkRRectPriv::ConservativeIntersect(fShape.rrect(), e.fShape.rrect())
553*c8dee2aaSAndroid Build Coastguard Worker == e.fShape.rrect();
554*c8dee2aaSAndroid Build Coastguard Worker } else if (fShape.isPath() && e.fShape.isPath()) {
555*c8dee2aaSAndroid Build Coastguard Worker return fShape.path().getGenerationID() == e.fShape.path().getGenerationID() ||
556*c8dee2aaSAndroid Build Coastguard Worker (fShape.path().getPoints(nullptr, 0) <= kMaxPathComparePoints &&
557*c8dee2aaSAndroid Build Coastguard Worker fShape.path() == e.fShape.path());
558*c8dee2aaSAndroid Build Coastguard Worker } // else fall through to shape_contains_rect
559*c8dee2aaSAndroid Build Coastguard Worker }
560*c8dee2aaSAndroid Build Coastguard Worker
561*c8dee2aaSAndroid Build Coastguard Worker return shape_contains_rect(fShape, fLocalToDevice, fDeviceToLocal,
562*c8dee2aaSAndroid Build Coastguard Worker e.fShape.bounds(), e.fLocalToDevice, mixedAA);
563*c8dee2aaSAndroid Build Coastguard Worker
564*c8dee2aaSAndroid Build Coastguard Worker }
565*c8dee2aaSAndroid Build Coastguard Worker
simplify(const SkIRect & deviceBounds,bool forceAA)566*c8dee2aaSAndroid Build Coastguard Worker void ClipStack::RawElement::simplify(const SkIRect& deviceBounds, bool forceAA) {
567*c8dee2aaSAndroid Build Coastguard Worker // Make sure the shape is not inverted. An inverted shape is equivalent to a non-inverted shape
568*c8dee2aaSAndroid Build Coastguard Worker // with the clip op toggled.
569*c8dee2aaSAndroid Build Coastguard Worker if (fShape.inverted()) {
570*c8dee2aaSAndroid Build Coastguard Worker fOp = fOp == SkClipOp::kIntersect ? SkClipOp::kDifference : SkClipOp::kIntersect;
571*c8dee2aaSAndroid Build Coastguard Worker fShape.setInverted(false);
572*c8dee2aaSAndroid Build Coastguard Worker }
573*c8dee2aaSAndroid Build Coastguard Worker
574*c8dee2aaSAndroid Build Coastguard Worker // Then simplify the base shape, if it becomes empty, no need to update the bounds
575*c8dee2aaSAndroid Build Coastguard Worker fShape.simplify();
576*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!fShape.inverted());
577*c8dee2aaSAndroid Build Coastguard Worker if (fShape.isEmpty()) {
578*c8dee2aaSAndroid Build Coastguard Worker return;
579*c8dee2aaSAndroid Build Coastguard Worker }
580*c8dee2aaSAndroid Build Coastguard Worker
581*c8dee2aaSAndroid Build Coastguard Worker // Lines and points should have been turned into empty since we assume everything is filled
582*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!fShape.isPoint() && !fShape.isLine());
583*c8dee2aaSAndroid Build Coastguard Worker // Validity check, we have no public API to create an arc at the moment
584*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!fShape.isArc());
585*c8dee2aaSAndroid Build Coastguard Worker
586*c8dee2aaSAndroid Build Coastguard Worker SkRect outer = fLocalToDevice.mapRect(fShape.bounds());
587*c8dee2aaSAndroid Build Coastguard Worker if (!outer.intersect(SkRect::Make(deviceBounds))) {
588*c8dee2aaSAndroid Build Coastguard Worker // A non-empty shape is offscreen, so treat it as empty
589*c8dee2aaSAndroid Build Coastguard Worker fShape.reset();
590*c8dee2aaSAndroid Build Coastguard Worker return;
591*c8dee2aaSAndroid Build Coastguard Worker }
592*c8dee2aaSAndroid Build Coastguard Worker
593*c8dee2aaSAndroid Build Coastguard Worker // Except for axis-aligned clip rects, upgrade to AA when forced. We skip axis-aligned clip
594*c8dee2aaSAndroid Build Coastguard Worker // rects because a non-AA axis aligned rect can always be set as just a scissor test or window
595*c8dee2aaSAndroid Build Coastguard Worker // rect, avoiding an expensive stencil mask generation.
596*c8dee2aaSAndroid Build Coastguard Worker if (forceAA && !(fShape.isRect() && fLocalToDevice.preservesAxisAlignment())) {
597*c8dee2aaSAndroid Build Coastguard Worker fAA = GrAA::kYes;
598*c8dee2aaSAndroid Build Coastguard Worker }
599*c8dee2aaSAndroid Build Coastguard Worker
600*c8dee2aaSAndroid Build Coastguard Worker // Except for non-AA axis-aligned rects, the outer bounds is the rounded-out device-space
601*c8dee2aaSAndroid Build Coastguard Worker // mapped bounds of the shape.
602*c8dee2aaSAndroid Build Coastguard Worker fOuterBounds = GrClip::GetPixelIBounds(outer, fAA, BoundsType::kExterior);
603*c8dee2aaSAndroid Build Coastguard Worker
604*c8dee2aaSAndroid Build Coastguard Worker if (fLocalToDevice.preservesAxisAlignment()) {
605*c8dee2aaSAndroid Build Coastguard Worker if (fShape.isRect()) {
606*c8dee2aaSAndroid Build Coastguard Worker // The actual geometry can be updated to the device-intersected bounds and we can
607*c8dee2aaSAndroid Build Coastguard Worker // know the inner bounds
608*c8dee2aaSAndroid Build Coastguard Worker fShape.rect() = outer;
609*c8dee2aaSAndroid Build Coastguard Worker fLocalToDevice.setIdentity();
610*c8dee2aaSAndroid Build Coastguard Worker fDeviceToLocal.setIdentity();
611*c8dee2aaSAndroid Build Coastguard Worker
612*c8dee2aaSAndroid Build Coastguard Worker if (fAA == GrAA::kNo && outer.width() >= 1.f && outer.height() >= 1.f) {
613*c8dee2aaSAndroid Build Coastguard Worker // NOTE: Legacy behavior to avoid performance regressions. For non-aa axis-aligned
614*c8dee2aaSAndroid Build Coastguard Worker // clip rects we always just round so that they can be scissor-only (avoiding the
615*c8dee2aaSAndroid Build Coastguard Worker // uncertainty in how a GPU might actually round an edge on fractional coords).
616*c8dee2aaSAndroid Build Coastguard Worker fOuterBounds = outer.round();
617*c8dee2aaSAndroid Build Coastguard Worker fInnerBounds = fOuterBounds;
618*c8dee2aaSAndroid Build Coastguard Worker } else {
619*c8dee2aaSAndroid Build Coastguard Worker fInnerBounds = GrClip::GetPixelIBounds(outer, fAA, BoundsType::kInterior);
620*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fOuterBounds.contains(fInnerBounds) || fInnerBounds.isEmpty());
621*c8dee2aaSAndroid Build Coastguard Worker }
622*c8dee2aaSAndroid Build Coastguard Worker } else if (fShape.isRRect()) {
623*c8dee2aaSAndroid Build Coastguard Worker // Can't transform in place and must still check transform result since some very
624*c8dee2aaSAndroid Build Coastguard Worker // ill-formed scale+translate matrices can cause invalid rrect radii.
625*c8dee2aaSAndroid Build Coastguard Worker SkRRect src;
626*c8dee2aaSAndroid Build Coastguard Worker if (fShape.rrect().transform(fLocalToDevice, &src)) {
627*c8dee2aaSAndroid Build Coastguard Worker fShape.rrect() = src;
628*c8dee2aaSAndroid Build Coastguard Worker fLocalToDevice.setIdentity();
629*c8dee2aaSAndroid Build Coastguard Worker fDeviceToLocal.setIdentity();
630*c8dee2aaSAndroid Build Coastguard Worker
631*c8dee2aaSAndroid Build Coastguard Worker SkRect inner = SkRRectPriv::InnerBounds(fShape.rrect());
632*c8dee2aaSAndroid Build Coastguard Worker fInnerBounds = GrClip::GetPixelIBounds(inner, fAA, BoundsType::kInterior);
633*c8dee2aaSAndroid Build Coastguard Worker if (!fInnerBounds.intersect(deviceBounds)) {
634*c8dee2aaSAndroid Build Coastguard Worker fInnerBounds = SkIRect::MakeEmpty();
635*c8dee2aaSAndroid Build Coastguard Worker }
636*c8dee2aaSAndroid Build Coastguard Worker }
637*c8dee2aaSAndroid Build Coastguard Worker }
638*c8dee2aaSAndroid Build Coastguard Worker }
639*c8dee2aaSAndroid Build Coastguard Worker
640*c8dee2aaSAndroid Build Coastguard Worker if (fOuterBounds.isEmpty()) {
641*c8dee2aaSAndroid Build Coastguard Worker // This can happen if we have non-AA shapes smaller than a pixel that do not cover a pixel
642*c8dee2aaSAndroid Build Coastguard Worker // center. We could round out, but rasterization would still result in an empty clip.
643*c8dee2aaSAndroid Build Coastguard Worker fShape.reset();
644*c8dee2aaSAndroid Build Coastguard Worker }
645*c8dee2aaSAndroid Build Coastguard Worker
646*c8dee2aaSAndroid Build Coastguard Worker // Post-conditions on inner and outer bounds
647*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fShape.isEmpty() || (!fOuterBounds.isEmpty() && deviceBounds.contains(fOuterBounds)));
648*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fShape.isEmpty() || fInnerBounds.isEmpty() || fOuterBounds.contains(fInnerBounds));
649*c8dee2aaSAndroid Build Coastguard Worker }
650*c8dee2aaSAndroid Build Coastguard Worker
combine(const RawElement & other,const SaveRecord & current)651*c8dee2aaSAndroid Build Coastguard Worker bool ClipStack::RawElement::combine(const RawElement& other, const SaveRecord& current) {
652*c8dee2aaSAndroid Build Coastguard Worker // To reduce the number of possibilities, only consider intersect+intersect. Difference and
653*c8dee2aaSAndroid Build Coastguard Worker // mixed op cases could be analyzed to simplify one of the shapes, but that is a rare
654*c8dee2aaSAndroid Build Coastguard Worker // occurrence and the math is much more complicated.
655*c8dee2aaSAndroid Build Coastguard Worker if (other.fOp != SkClipOp::kIntersect || fOp != SkClipOp::kIntersect) {
656*c8dee2aaSAndroid Build Coastguard Worker return false;
657*c8dee2aaSAndroid Build Coastguard Worker }
658*c8dee2aaSAndroid Build Coastguard Worker
659*c8dee2aaSAndroid Build Coastguard Worker // At the moment, only rect+rect or rrect+rrect are supported (although rect+rrect is
660*c8dee2aaSAndroid Build Coastguard Worker // treated as a degenerate case of rrect+rrect).
661*c8dee2aaSAndroid Build Coastguard Worker bool shapeUpdated = false;
662*c8dee2aaSAndroid Build Coastguard Worker if (fShape.isRect() && other.fShape.isRect()) {
663*c8dee2aaSAndroid Build Coastguard Worker bool aaMatch = fAA == other.fAA;
664*c8dee2aaSAndroid Build Coastguard Worker if (fLocalToDevice.isIdentity() && other.fLocalToDevice.isIdentity() && !aaMatch) {
665*c8dee2aaSAndroid Build Coastguard Worker if (GrClip::IsPixelAligned(fShape.rect())) {
666*c8dee2aaSAndroid Build Coastguard Worker // Our AA type doesn't really matter, take other's since its edges may not be
667*c8dee2aaSAndroid Build Coastguard Worker // pixel aligned, so after intersection clip behavior should respect its aa type.
668*c8dee2aaSAndroid Build Coastguard Worker fAA = other.fAA;
669*c8dee2aaSAndroid Build Coastguard Worker } else if (!GrClip::IsPixelAligned(other.fShape.rect())) {
670*c8dee2aaSAndroid Build Coastguard Worker // Neither shape is pixel aligned and AA types don't match so can't combine
671*c8dee2aaSAndroid Build Coastguard Worker return false;
672*c8dee2aaSAndroid Build Coastguard Worker }
673*c8dee2aaSAndroid Build Coastguard Worker // Either we've updated this->fAA to actually match, or other->fAA doesn't matter so
674*c8dee2aaSAndroid Build Coastguard Worker // this can be set to true. We just can't modify other to set it's aa to this->fAA.
675*c8dee2aaSAndroid Build Coastguard Worker // But since 'this' becomes the combo of the two, other will be deleted so that's fine.
676*c8dee2aaSAndroid Build Coastguard Worker aaMatch = true;
677*c8dee2aaSAndroid Build Coastguard Worker }
678*c8dee2aaSAndroid Build Coastguard Worker
679*c8dee2aaSAndroid Build Coastguard Worker if (aaMatch && fLocalToDevice == other.fLocalToDevice) {
680*c8dee2aaSAndroid Build Coastguard Worker if (!fShape.rect().intersect(other.fShape.rect())) {
681*c8dee2aaSAndroid Build Coastguard Worker // By floating point, it turns out the combination should be empty
682*c8dee2aaSAndroid Build Coastguard Worker this->fShape.reset();
683*c8dee2aaSAndroid Build Coastguard Worker this->markInvalid(current);
684*c8dee2aaSAndroid Build Coastguard Worker return true;
685*c8dee2aaSAndroid Build Coastguard Worker }
686*c8dee2aaSAndroid Build Coastguard Worker shapeUpdated = true;
687*c8dee2aaSAndroid Build Coastguard Worker }
688*c8dee2aaSAndroid Build Coastguard Worker } else if ((fShape.isRect() || fShape.isRRect()) &&
689*c8dee2aaSAndroid Build Coastguard Worker (other.fShape.isRect() || other.fShape.isRRect())) {
690*c8dee2aaSAndroid Build Coastguard Worker // No such pixel-aligned disregard for AA for round rects
691*c8dee2aaSAndroid Build Coastguard Worker if (fAA == other.fAA && fLocalToDevice == other.fLocalToDevice) {
692*c8dee2aaSAndroid Build Coastguard Worker // Treat rrect+rect intersections as rrect+rrect
693*c8dee2aaSAndroid Build Coastguard Worker SkRRect a = fShape.isRect() ? SkRRect::MakeRect(fShape.rect()) : fShape.rrect();
694*c8dee2aaSAndroid Build Coastguard Worker SkRRect b = other.fShape.isRect() ? SkRRect::MakeRect(other.fShape.rect())
695*c8dee2aaSAndroid Build Coastguard Worker : other.fShape.rrect();
696*c8dee2aaSAndroid Build Coastguard Worker
697*c8dee2aaSAndroid Build Coastguard Worker SkRRect joined = SkRRectPriv::ConservativeIntersect(a, b);
698*c8dee2aaSAndroid Build Coastguard Worker if (!joined.isEmpty()) {
699*c8dee2aaSAndroid Build Coastguard Worker // Can reduce to a single element
700*c8dee2aaSAndroid Build Coastguard Worker if (joined.isRect()) {
701*c8dee2aaSAndroid Build Coastguard Worker // And with a simplified type
702*c8dee2aaSAndroid Build Coastguard Worker fShape.setRect(joined.rect());
703*c8dee2aaSAndroid Build Coastguard Worker } else {
704*c8dee2aaSAndroid Build Coastguard Worker fShape.setRRect(joined);
705*c8dee2aaSAndroid Build Coastguard Worker }
706*c8dee2aaSAndroid Build Coastguard Worker shapeUpdated = true;
707*c8dee2aaSAndroid Build Coastguard Worker } else if (!a.getBounds().intersects(b.getBounds())) {
708*c8dee2aaSAndroid Build Coastguard Worker // Like the rect+rect combination, the intersection is actually empty
709*c8dee2aaSAndroid Build Coastguard Worker fShape.reset();
710*c8dee2aaSAndroid Build Coastguard Worker this->markInvalid(current);
711*c8dee2aaSAndroid Build Coastguard Worker return true;
712*c8dee2aaSAndroid Build Coastguard Worker }
713*c8dee2aaSAndroid Build Coastguard Worker }
714*c8dee2aaSAndroid Build Coastguard Worker }
715*c8dee2aaSAndroid Build Coastguard Worker
716*c8dee2aaSAndroid Build Coastguard Worker if (shapeUpdated) {
717*c8dee2aaSAndroid Build Coastguard Worker // This logic works under the assumption that both combined elements were intersect, so we
718*c8dee2aaSAndroid Build Coastguard Worker // don't do the full bounds computations like in simplify().
719*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fOp == SkClipOp::kIntersect && other.fOp == SkClipOp::kIntersect);
720*c8dee2aaSAndroid Build Coastguard Worker SkAssertResult(fOuterBounds.intersect(other.fOuterBounds));
721*c8dee2aaSAndroid Build Coastguard Worker if (!fInnerBounds.intersect(other.fInnerBounds)) {
722*c8dee2aaSAndroid Build Coastguard Worker fInnerBounds = SkIRect::MakeEmpty();
723*c8dee2aaSAndroid Build Coastguard Worker }
724*c8dee2aaSAndroid Build Coastguard Worker return true;
725*c8dee2aaSAndroid Build Coastguard Worker } else {
726*c8dee2aaSAndroid Build Coastguard Worker return false;
727*c8dee2aaSAndroid Build Coastguard Worker }
728*c8dee2aaSAndroid Build Coastguard Worker }
729*c8dee2aaSAndroid Build Coastguard Worker
updateForElement(RawElement * added,const SaveRecord & current)730*c8dee2aaSAndroid Build Coastguard Worker void ClipStack::RawElement::updateForElement(RawElement* added, const SaveRecord& current) {
731*c8dee2aaSAndroid Build Coastguard Worker if (this->isInvalid()) {
732*c8dee2aaSAndroid Build Coastguard Worker // Already doesn't do anything, so skip this element
733*c8dee2aaSAndroid Build Coastguard Worker return;
734*c8dee2aaSAndroid Build Coastguard Worker }
735*c8dee2aaSAndroid Build Coastguard Worker
736*c8dee2aaSAndroid Build Coastguard Worker // 'A' refers to this element, 'B' refers to 'added'.
737*c8dee2aaSAndroid Build Coastguard Worker switch (get_clip_geometry(*this, *added)) {
738*c8dee2aaSAndroid Build Coastguard Worker case ClipGeometry::kEmpty:
739*c8dee2aaSAndroid Build Coastguard Worker // Mark both elements as invalid to signal that the clip is fully empty
740*c8dee2aaSAndroid Build Coastguard Worker this->markInvalid(current);
741*c8dee2aaSAndroid Build Coastguard Worker added->markInvalid(current);
742*c8dee2aaSAndroid Build Coastguard Worker break;
743*c8dee2aaSAndroid Build Coastguard Worker
744*c8dee2aaSAndroid Build Coastguard Worker case ClipGeometry::kAOnly:
745*c8dee2aaSAndroid Build Coastguard Worker // This element already clips more than 'added', so mark 'added' is invalid to skip it
746*c8dee2aaSAndroid Build Coastguard Worker added->markInvalid(current);
747*c8dee2aaSAndroid Build Coastguard Worker break;
748*c8dee2aaSAndroid Build Coastguard Worker
749*c8dee2aaSAndroid Build Coastguard Worker case ClipGeometry::kBOnly:
750*c8dee2aaSAndroid Build Coastguard Worker // 'added' clips more than this element, so mark this as invalid
751*c8dee2aaSAndroid Build Coastguard Worker this->markInvalid(current);
752*c8dee2aaSAndroid Build Coastguard Worker break;
753*c8dee2aaSAndroid Build Coastguard Worker
754*c8dee2aaSAndroid Build Coastguard Worker case ClipGeometry::kBoth:
755*c8dee2aaSAndroid Build Coastguard Worker // Else the bounds checks think we need to keep both, but depending on the combination
756*c8dee2aaSAndroid Build Coastguard Worker // of the ops and shape kinds, we may be able to do better.
757*c8dee2aaSAndroid Build Coastguard Worker if (added->combine(*this, current)) {
758*c8dee2aaSAndroid Build Coastguard Worker // 'added' now fully represents the combination of the two elements
759*c8dee2aaSAndroid Build Coastguard Worker this->markInvalid(current);
760*c8dee2aaSAndroid Build Coastguard Worker }
761*c8dee2aaSAndroid Build Coastguard Worker break;
762*c8dee2aaSAndroid Build Coastguard Worker }
763*c8dee2aaSAndroid Build Coastguard Worker }
764*c8dee2aaSAndroid Build Coastguard Worker
clipType() const765*c8dee2aaSAndroid Build Coastguard Worker ClipStack::ClipState ClipStack::RawElement::clipType() const {
766*c8dee2aaSAndroid Build Coastguard Worker // Map from the internal shape kind to the clip state enum
767*c8dee2aaSAndroid Build Coastguard Worker switch (fShape.type()) {
768*c8dee2aaSAndroid Build Coastguard Worker case GrShape::Type::kEmpty:
769*c8dee2aaSAndroid Build Coastguard Worker return ClipState::kEmpty;
770*c8dee2aaSAndroid Build Coastguard Worker
771*c8dee2aaSAndroid Build Coastguard Worker case GrShape::Type::kRect:
772*c8dee2aaSAndroid Build Coastguard Worker return fOp == SkClipOp::kIntersect && fLocalToDevice.isIdentity()
773*c8dee2aaSAndroid Build Coastguard Worker ? ClipState::kDeviceRect : ClipState::kComplex;
774*c8dee2aaSAndroid Build Coastguard Worker
775*c8dee2aaSAndroid Build Coastguard Worker case GrShape::Type::kRRect:
776*c8dee2aaSAndroid Build Coastguard Worker return fOp == SkClipOp::kIntersect && fLocalToDevice.isIdentity()
777*c8dee2aaSAndroid Build Coastguard Worker ? ClipState::kDeviceRRect : ClipState::kComplex;
778*c8dee2aaSAndroid Build Coastguard Worker
779*c8dee2aaSAndroid Build Coastguard Worker case GrShape::Type::kArc:
780*c8dee2aaSAndroid Build Coastguard Worker case GrShape::Type::kLine:
781*c8dee2aaSAndroid Build Coastguard Worker case GrShape::Type::kPoint:
782*c8dee2aaSAndroid Build Coastguard Worker // These types should never become RawElements
783*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(false);
784*c8dee2aaSAndroid Build Coastguard Worker [[fallthrough]];
785*c8dee2aaSAndroid Build Coastguard Worker
786*c8dee2aaSAndroid Build Coastguard Worker case GrShape::Type::kPath:
787*c8dee2aaSAndroid Build Coastguard Worker return ClipState::kComplex;
788*c8dee2aaSAndroid Build Coastguard Worker }
789*c8dee2aaSAndroid Build Coastguard Worker SkUNREACHABLE;
790*c8dee2aaSAndroid Build Coastguard Worker }
791*c8dee2aaSAndroid Build Coastguard Worker
792*c8dee2aaSAndroid Build Coastguard Worker ///////////////////////////////////////////////////////////////////////////////
793*c8dee2aaSAndroid Build Coastguard Worker // ClipStack::Mask
794*c8dee2aaSAndroid Build Coastguard Worker
Mask(const SaveRecord & current,const SkIRect & drawBounds)795*c8dee2aaSAndroid Build Coastguard Worker ClipStack::Mask::Mask(const SaveRecord& current, const SkIRect& drawBounds)
796*c8dee2aaSAndroid Build Coastguard Worker : fBounds(drawBounds)
797*c8dee2aaSAndroid Build Coastguard Worker , fGenID(current.genID()) {
798*c8dee2aaSAndroid Build Coastguard Worker static const UniqueKey::Domain kDomain = UniqueKey::GenerateDomain();
799*c8dee2aaSAndroid Build Coastguard Worker
800*c8dee2aaSAndroid Build Coastguard Worker // The gen ID should not be invalid, empty, or wide open, since those do not require masks
801*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fGenID != kInvalidGenID && fGenID != kEmptyGenID && fGenID != kWideOpenGenID);
802*c8dee2aaSAndroid Build Coastguard Worker
803*c8dee2aaSAndroid Build Coastguard Worker UniqueKey::Builder builder(&fKey, kDomain, 5, "clip_mask");
804*c8dee2aaSAndroid Build Coastguard Worker builder[0] = fGenID;
805*c8dee2aaSAndroid Build Coastguard Worker builder[1] = drawBounds.fLeft;
806*c8dee2aaSAndroid Build Coastguard Worker builder[2] = drawBounds.fRight;
807*c8dee2aaSAndroid Build Coastguard Worker builder[3] = drawBounds.fTop;
808*c8dee2aaSAndroid Build Coastguard Worker builder[4] = drawBounds.fBottom;
809*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fKey.isValid());
810*c8dee2aaSAndroid Build Coastguard Worker
811*c8dee2aaSAndroid Build Coastguard Worker SkDEBUGCODE(fOwner = ¤t;)
812*c8dee2aaSAndroid Build Coastguard Worker }
813*c8dee2aaSAndroid Build Coastguard Worker
appliesToDraw(const SaveRecord & current,const SkIRect & drawBounds) const814*c8dee2aaSAndroid Build Coastguard Worker bool ClipStack::Mask::appliesToDraw(const SaveRecord& current, const SkIRect& drawBounds) const {
815*c8dee2aaSAndroid Build Coastguard Worker // For the same save record, a larger mask will have the same or more elements
816*c8dee2aaSAndroid Build Coastguard Worker // baked into it, so it can be reused to clip the smaller draw.
817*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fGenID != current.genID() || ¤t == fOwner);
818*c8dee2aaSAndroid Build Coastguard Worker return fGenID == current.genID() && fBounds.contains(drawBounds);
819*c8dee2aaSAndroid Build Coastguard Worker }
820*c8dee2aaSAndroid Build Coastguard Worker
invalidate(GrProxyProvider * proxyProvider)821*c8dee2aaSAndroid Build Coastguard Worker void ClipStack::Mask::invalidate(GrProxyProvider* proxyProvider) {
822*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(proxyProvider);
823*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fKey.isValid()); // Should only be invalidated once
824*c8dee2aaSAndroid Build Coastguard Worker proxyProvider->processInvalidUniqueKey(
825*c8dee2aaSAndroid Build Coastguard Worker fKey, nullptr, GrProxyProvider::InvalidateGPUResource::kYes);
826*c8dee2aaSAndroid Build Coastguard Worker fKey.reset();
827*c8dee2aaSAndroid Build Coastguard Worker }
828*c8dee2aaSAndroid Build Coastguard Worker
829*c8dee2aaSAndroid Build Coastguard Worker ///////////////////////////////////////////////////////////////////////////////
830*c8dee2aaSAndroid Build Coastguard Worker // ClipStack::SaveRecord
831*c8dee2aaSAndroid Build Coastguard Worker
SaveRecord(const SkIRect & deviceBounds)832*c8dee2aaSAndroid Build Coastguard Worker ClipStack::SaveRecord::SaveRecord(const SkIRect& deviceBounds)
833*c8dee2aaSAndroid Build Coastguard Worker : fInnerBounds(deviceBounds)
834*c8dee2aaSAndroid Build Coastguard Worker , fOuterBounds(deviceBounds)
835*c8dee2aaSAndroid Build Coastguard Worker , fShader(nullptr)
836*c8dee2aaSAndroid Build Coastguard Worker , fStartingMaskIndex(0)
837*c8dee2aaSAndroid Build Coastguard Worker , fStartingElementIndex(0)
838*c8dee2aaSAndroid Build Coastguard Worker , fOldestValidIndex(0)
839*c8dee2aaSAndroid Build Coastguard Worker , fDeferredSaveCount(0)
840*c8dee2aaSAndroid Build Coastguard Worker , fStackOp(SkClipOp::kIntersect)
841*c8dee2aaSAndroid Build Coastguard Worker , fState(ClipState::kWideOpen)
842*c8dee2aaSAndroid Build Coastguard Worker , fGenID(kInvalidGenID) {}
843*c8dee2aaSAndroid Build Coastguard Worker
SaveRecord(const SaveRecord & prior,int startingMaskIndex,int startingElementIndex)844*c8dee2aaSAndroid Build Coastguard Worker ClipStack::SaveRecord::SaveRecord(const SaveRecord& prior,
845*c8dee2aaSAndroid Build Coastguard Worker int startingMaskIndex,
846*c8dee2aaSAndroid Build Coastguard Worker int startingElementIndex)
847*c8dee2aaSAndroid Build Coastguard Worker : fInnerBounds(prior.fInnerBounds)
848*c8dee2aaSAndroid Build Coastguard Worker , fOuterBounds(prior.fOuterBounds)
849*c8dee2aaSAndroid Build Coastguard Worker , fShader(prior.fShader)
850*c8dee2aaSAndroid Build Coastguard Worker , fStartingMaskIndex(startingMaskIndex)
851*c8dee2aaSAndroid Build Coastguard Worker , fStartingElementIndex(startingElementIndex)
852*c8dee2aaSAndroid Build Coastguard Worker , fOldestValidIndex(prior.fOldestValidIndex)
853*c8dee2aaSAndroid Build Coastguard Worker , fDeferredSaveCount(0)
854*c8dee2aaSAndroid Build Coastguard Worker , fStackOp(prior.fStackOp)
855*c8dee2aaSAndroid Build Coastguard Worker , fState(prior.fState)
856*c8dee2aaSAndroid Build Coastguard Worker , fGenID(kInvalidGenID) {
857*c8dee2aaSAndroid Build Coastguard Worker // If the prior record never needed a mask, this one will insert into the same index
858*c8dee2aaSAndroid Build Coastguard Worker // (that's okay since we'll remove it when this record is popped off the stack).
859*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(startingMaskIndex >= prior.fStartingMaskIndex);
860*c8dee2aaSAndroid Build Coastguard Worker // The same goes for elements (the prior could have been wide open).
861*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(startingElementIndex >= prior.fStartingElementIndex);
862*c8dee2aaSAndroid Build Coastguard Worker }
863*c8dee2aaSAndroid Build Coastguard Worker
genID() const864*c8dee2aaSAndroid Build Coastguard Worker uint32_t ClipStack::SaveRecord::genID() const {
865*c8dee2aaSAndroid Build Coastguard Worker if (fState == ClipState::kEmpty) {
866*c8dee2aaSAndroid Build Coastguard Worker return kEmptyGenID;
867*c8dee2aaSAndroid Build Coastguard Worker } else if (fState == ClipState::kWideOpen) {
868*c8dee2aaSAndroid Build Coastguard Worker return kWideOpenGenID;
869*c8dee2aaSAndroid Build Coastguard Worker } else {
870*c8dee2aaSAndroid Build Coastguard Worker // The gen ID shouldn't be empty or wide open, since they are reserved for the above
871*c8dee2aaSAndroid Build Coastguard Worker // if-cases. It may be kInvalid if the record hasn't had any elements added to it yet.
872*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fGenID != kEmptyGenID && fGenID != kWideOpenGenID);
873*c8dee2aaSAndroid Build Coastguard Worker return fGenID;
874*c8dee2aaSAndroid Build Coastguard Worker }
875*c8dee2aaSAndroid Build Coastguard Worker }
876*c8dee2aaSAndroid Build Coastguard Worker
state() const877*c8dee2aaSAndroid Build Coastguard Worker ClipStack::ClipState ClipStack::SaveRecord::state() const {
878*c8dee2aaSAndroid Build Coastguard Worker if (fShader && fState != ClipState::kEmpty) {
879*c8dee2aaSAndroid Build Coastguard Worker return ClipState::kComplex;
880*c8dee2aaSAndroid Build Coastguard Worker } else {
881*c8dee2aaSAndroid Build Coastguard Worker return fState;
882*c8dee2aaSAndroid Build Coastguard Worker }
883*c8dee2aaSAndroid Build Coastguard Worker }
884*c8dee2aaSAndroid Build Coastguard Worker
contains(const ClipStack::Draw & draw) const885*c8dee2aaSAndroid Build Coastguard Worker bool ClipStack::SaveRecord::contains(const ClipStack::Draw& draw) const {
886*c8dee2aaSAndroid Build Coastguard Worker return fInnerBounds.contains(draw.outerBounds());
887*c8dee2aaSAndroid Build Coastguard Worker }
888*c8dee2aaSAndroid Build Coastguard Worker
contains(const ClipStack::RawElement & element) const889*c8dee2aaSAndroid Build Coastguard Worker bool ClipStack::SaveRecord::contains(const ClipStack::RawElement& element) const {
890*c8dee2aaSAndroid Build Coastguard Worker return fInnerBounds.contains(element.outerBounds());
891*c8dee2aaSAndroid Build Coastguard Worker }
892*c8dee2aaSAndroid Build Coastguard Worker
removeElements(RawElement::Stack * elements)893*c8dee2aaSAndroid Build Coastguard Worker void ClipStack::SaveRecord::removeElements(RawElement::Stack* elements) {
894*c8dee2aaSAndroid Build Coastguard Worker while (elements->count() > fStartingElementIndex) {
895*c8dee2aaSAndroid Build Coastguard Worker elements->pop_back();
896*c8dee2aaSAndroid Build Coastguard Worker }
897*c8dee2aaSAndroid Build Coastguard Worker }
898*c8dee2aaSAndroid Build Coastguard Worker
restoreElements(RawElement::Stack * elements)899*c8dee2aaSAndroid Build Coastguard Worker void ClipStack::SaveRecord::restoreElements(RawElement::Stack* elements) {
900*c8dee2aaSAndroid Build Coastguard Worker // Presumably this SaveRecord is the new top of the stack, and so it owns the elements
901*c8dee2aaSAndroid Build Coastguard Worker // from its starting index to restoreCount - 1. Elements from the old save record have
902*c8dee2aaSAndroid Build Coastguard Worker // been destroyed already, so their indices would have been >= restoreCount, and any
903*c8dee2aaSAndroid Build Coastguard Worker // still-present element can be un-invalidated based on that.
904*c8dee2aaSAndroid Build Coastguard Worker int i = elements->count() - 1;
905*c8dee2aaSAndroid Build Coastguard Worker for (RawElement& e : elements->ritems()) {
906*c8dee2aaSAndroid Build Coastguard Worker if (i < fOldestValidIndex) {
907*c8dee2aaSAndroid Build Coastguard Worker break;
908*c8dee2aaSAndroid Build Coastguard Worker }
909*c8dee2aaSAndroid Build Coastguard Worker e.restoreValid(*this);
910*c8dee2aaSAndroid Build Coastguard Worker --i;
911*c8dee2aaSAndroid Build Coastguard Worker }
912*c8dee2aaSAndroid Build Coastguard Worker }
913*c8dee2aaSAndroid Build Coastguard Worker
invalidateMasks(GrProxyProvider * proxyProvider,Mask::Stack * masks)914*c8dee2aaSAndroid Build Coastguard Worker void ClipStack::SaveRecord::invalidateMasks(GrProxyProvider* proxyProvider,
915*c8dee2aaSAndroid Build Coastguard Worker Mask::Stack* masks) {
916*c8dee2aaSAndroid Build Coastguard Worker // Must explicitly invalidate the key before removing the mask object from the stack
917*c8dee2aaSAndroid Build Coastguard Worker while (masks->count() > fStartingMaskIndex) {
918*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(masks->back().owner() == this && proxyProvider);
919*c8dee2aaSAndroid Build Coastguard Worker masks->back().invalidate(proxyProvider);
920*c8dee2aaSAndroid Build Coastguard Worker masks->pop_back();
921*c8dee2aaSAndroid Build Coastguard Worker }
922*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(masks->empty() || masks->back().genID() != fGenID);
923*c8dee2aaSAndroid Build Coastguard Worker }
924*c8dee2aaSAndroid Build Coastguard Worker
reset(const SkIRect & bounds)925*c8dee2aaSAndroid Build Coastguard Worker void ClipStack::SaveRecord::reset(const SkIRect& bounds) {
926*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(this->canBeUpdated());
927*c8dee2aaSAndroid Build Coastguard Worker fOldestValidIndex = fStartingElementIndex;
928*c8dee2aaSAndroid Build Coastguard Worker fOuterBounds = bounds;
929*c8dee2aaSAndroid Build Coastguard Worker fInnerBounds = bounds;
930*c8dee2aaSAndroid Build Coastguard Worker fStackOp = SkClipOp::kIntersect;
931*c8dee2aaSAndroid Build Coastguard Worker fState = ClipState::kWideOpen;
932*c8dee2aaSAndroid Build Coastguard Worker fShader = nullptr;
933*c8dee2aaSAndroid Build Coastguard Worker }
934*c8dee2aaSAndroid Build Coastguard Worker
addShader(sk_sp<SkShader> shader)935*c8dee2aaSAndroid Build Coastguard Worker void ClipStack::SaveRecord::addShader(sk_sp<SkShader> shader) {
936*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(shader);
937*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(this->canBeUpdated());
938*c8dee2aaSAndroid Build Coastguard Worker if (!fShader) {
939*c8dee2aaSAndroid Build Coastguard Worker fShader = std::move(shader);
940*c8dee2aaSAndroid Build Coastguard Worker } else {
941*c8dee2aaSAndroid Build Coastguard Worker // The total coverage is computed by multiplying the coverage from each element (shape or
942*c8dee2aaSAndroid Build Coastguard Worker // shader), but since multiplication is associative, we can use kSrcIn blending to make
943*c8dee2aaSAndroid Build Coastguard Worker // a new shader that represents 'shader' * 'fShader'
944*c8dee2aaSAndroid Build Coastguard Worker fShader = SkShaders::Blend(SkBlendMode::kSrcIn, std::move(shader), fShader);
945*c8dee2aaSAndroid Build Coastguard Worker }
946*c8dee2aaSAndroid Build Coastguard Worker }
947*c8dee2aaSAndroid Build Coastguard Worker
addElement(RawElement && toAdd,RawElement::Stack * elements)948*c8dee2aaSAndroid Build Coastguard Worker bool ClipStack::SaveRecord::addElement(RawElement&& toAdd, RawElement::Stack* elements) {
949*c8dee2aaSAndroid Build Coastguard Worker // Validity check the element's state first; if the shape class isn't empty, the outer bounds
950*c8dee2aaSAndroid Build Coastguard Worker // shouldn't be empty; if the inner bounds are not empty, they must be contained in outer.
951*c8dee2aaSAndroid Build Coastguard Worker SkASSERT((toAdd.shape().isEmpty() || !toAdd.outerBounds().isEmpty()) &&
952*c8dee2aaSAndroid Build Coastguard Worker (toAdd.innerBounds().isEmpty() || toAdd.outerBounds().contains(toAdd.innerBounds())));
953*c8dee2aaSAndroid Build Coastguard Worker // And we shouldn't be adding an element if we have a deferred save
954*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(this->canBeUpdated());
955*c8dee2aaSAndroid Build Coastguard Worker
956*c8dee2aaSAndroid Build Coastguard Worker if (fState == ClipState::kEmpty) {
957*c8dee2aaSAndroid Build Coastguard Worker // The clip is already empty, and we only shrink, so there's no need to record this element.
958*c8dee2aaSAndroid Build Coastguard Worker return false;
959*c8dee2aaSAndroid Build Coastguard Worker } else if (toAdd.shape().isEmpty()) {
960*c8dee2aaSAndroid Build Coastguard Worker // An empty difference op should have been detected earlier, since it's a no-op
961*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(toAdd.op() == SkClipOp::kIntersect);
962*c8dee2aaSAndroid Build Coastguard Worker fState = ClipState::kEmpty;
963*c8dee2aaSAndroid Build Coastguard Worker return true;
964*c8dee2aaSAndroid Build Coastguard Worker }
965*c8dee2aaSAndroid Build Coastguard Worker
966*c8dee2aaSAndroid Build Coastguard Worker // In this invocation, 'A' refers to the existing stack's bounds and 'B' refers to the new
967*c8dee2aaSAndroid Build Coastguard Worker // element.
968*c8dee2aaSAndroid Build Coastguard Worker switch (get_clip_geometry(*this, toAdd)) {
969*c8dee2aaSAndroid Build Coastguard Worker case ClipGeometry::kEmpty:
970*c8dee2aaSAndroid Build Coastguard Worker // The combination results in an empty clip
971*c8dee2aaSAndroid Build Coastguard Worker fState = ClipState::kEmpty;
972*c8dee2aaSAndroid Build Coastguard Worker return true;
973*c8dee2aaSAndroid Build Coastguard Worker
974*c8dee2aaSAndroid Build Coastguard Worker case ClipGeometry::kAOnly:
975*c8dee2aaSAndroid Build Coastguard Worker // The combination would not be any different than the existing clip
976*c8dee2aaSAndroid Build Coastguard Worker return false;
977*c8dee2aaSAndroid Build Coastguard Worker
978*c8dee2aaSAndroid Build Coastguard Worker case ClipGeometry::kBOnly:
979*c8dee2aaSAndroid Build Coastguard Worker // The combination would invalidate the entire existing stack and can be replaced with
980*c8dee2aaSAndroid Build Coastguard Worker // just the new element.
981*c8dee2aaSAndroid Build Coastguard Worker this->replaceWithElement(std::move(toAdd), elements);
982*c8dee2aaSAndroid Build Coastguard Worker return true;
983*c8dee2aaSAndroid Build Coastguard Worker
984*c8dee2aaSAndroid Build Coastguard Worker case ClipGeometry::kBoth:
985*c8dee2aaSAndroid Build Coastguard Worker // The new element combines in a complex manner, so update the stack's bounds based on
986*c8dee2aaSAndroid Build Coastguard Worker // the combination of its and the new element's ops (handled below)
987*c8dee2aaSAndroid Build Coastguard Worker break;
988*c8dee2aaSAndroid Build Coastguard Worker }
989*c8dee2aaSAndroid Build Coastguard Worker
990*c8dee2aaSAndroid Build Coastguard Worker if (fState == ClipState::kWideOpen) {
991*c8dee2aaSAndroid Build Coastguard Worker // When the stack was wide open and the clip effect was kBoth, the "complex" manner is
992*c8dee2aaSAndroid Build Coastguard Worker // simply to keep the element and update the stack bounds to be the element's intersected
993*c8dee2aaSAndroid Build Coastguard Worker // with the device.
994*c8dee2aaSAndroid Build Coastguard Worker this->replaceWithElement(std::move(toAdd), elements);
995*c8dee2aaSAndroid Build Coastguard Worker return true;
996*c8dee2aaSAndroid Build Coastguard Worker }
997*c8dee2aaSAndroid Build Coastguard Worker
998*c8dee2aaSAndroid Build Coastguard Worker // Some form of actual clip element(s) to combine with.
999*c8dee2aaSAndroid Build Coastguard Worker if (fStackOp == SkClipOp::kIntersect) {
1000*c8dee2aaSAndroid Build Coastguard Worker if (toAdd.op() == SkClipOp::kIntersect) {
1001*c8dee2aaSAndroid Build Coastguard Worker // Intersect (stack) + Intersect (toAdd)
1002*c8dee2aaSAndroid Build Coastguard Worker // - Bounds updates is simply the paired intersections of outer and inner.
1003*c8dee2aaSAndroid Build Coastguard Worker SkAssertResult(fOuterBounds.intersect(toAdd.outerBounds()));
1004*c8dee2aaSAndroid Build Coastguard Worker if (!fInnerBounds.intersect(toAdd.innerBounds())) {
1005*c8dee2aaSAndroid Build Coastguard Worker // NOTE: this does the right thing if either rect is empty, since we set the
1006*c8dee2aaSAndroid Build Coastguard Worker // inner bounds to empty here
1007*c8dee2aaSAndroid Build Coastguard Worker fInnerBounds = SkIRect::MakeEmpty();
1008*c8dee2aaSAndroid Build Coastguard Worker }
1009*c8dee2aaSAndroid Build Coastguard Worker } else {
1010*c8dee2aaSAndroid Build Coastguard Worker // Intersect (stack) + Difference (toAdd)
1011*c8dee2aaSAndroid Build Coastguard Worker // - Shrink the stack's outer bounds if the difference op's inner bounds completely
1012*c8dee2aaSAndroid Build Coastguard Worker // cuts off an edge.
1013*c8dee2aaSAndroid Build Coastguard Worker // - Shrink the stack's inner bounds to completely exclude the op's outer bounds.
1014*c8dee2aaSAndroid Build Coastguard Worker fOuterBounds = subtract(fOuterBounds, toAdd.innerBounds(), /* exact */ true);
1015*c8dee2aaSAndroid Build Coastguard Worker fInnerBounds = subtract(fInnerBounds, toAdd.outerBounds(), /* exact */ false);
1016*c8dee2aaSAndroid Build Coastguard Worker }
1017*c8dee2aaSAndroid Build Coastguard Worker } else {
1018*c8dee2aaSAndroid Build Coastguard Worker if (toAdd.op() == SkClipOp::kIntersect) {
1019*c8dee2aaSAndroid Build Coastguard Worker // Difference (stack) + Intersect (toAdd)
1020*c8dee2aaSAndroid Build Coastguard Worker // - Bounds updates are just the mirror of Intersect(stack) + Difference(toAdd)
1021*c8dee2aaSAndroid Build Coastguard Worker SkIRect oldOuter = fOuterBounds;
1022*c8dee2aaSAndroid Build Coastguard Worker fOuterBounds = subtract(toAdd.outerBounds(), fInnerBounds, /* exact */ true);
1023*c8dee2aaSAndroid Build Coastguard Worker fInnerBounds = subtract(toAdd.innerBounds(), oldOuter, /* exact */ false);
1024*c8dee2aaSAndroid Build Coastguard Worker } else {
1025*c8dee2aaSAndroid Build Coastguard Worker // Difference (stack) + Difference (toAdd)
1026*c8dee2aaSAndroid Build Coastguard Worker // - The updated outer bounds is the union of outer bounds and the inner becomes the
1027*c8dee2aaSAndroid Build Coastguard Worker // largest of the two possible inner bounds
1028*c8dee2aaSAndroid Build Coastguard Worker fOuterBounds.join(toAdd.outerBounds());
1029*c8dee2aaSAndroid Build Coastguard Worker if (toAdd.innerBounds().width() * toAdd.innerBounds().height() >
1030*c8dee2aaSAndroid Build Coastguard Worker fInnerBounds.width() * fInnerBounds.height()) {
1031*c8dee2aaSAndroid Build Coastguard Worker fInnerBounds = toAdd.innerBounds();
1032*c8dee2aaSAndroid Build Coastguard Worker }
1033*c8dee2aaSAndroid Build Coastguard Worker }
1034*c8dee2aaSAndroid Build Coastguard Worker }
1035*c8dee2aaSAndroid Build Coastguard Worker
1036*c8dee2aaSAndroid Build Coastguard Worker // If we get here, we're keeping the new element and the stack's bounds have been updated.
1037*c8dee2aaSAndroid Build Coastguard Worker // We ought to have caught the cases where the stack bounds resemble an empty or wide open
1038*c8dee2aaSAndroid Build Coastguard Worker // clip, so assert that's the case.
1039*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!fOuterBounds.isEmpty() &&
1040*c8dee2aaSAndroid Build Coastguard Worker (fInnerBounds.isEmpty() || fOuterBounds.contains(fInnerBounds)));
1041*c8dee2aaSAndroid Build Coastguard Worker
1042*c8dee2aaSAndroid Build Coastguard Worker return this->appendElement(std::move(toAdd), elements);
1043*c8dee2aaSAndroid Build Coastguard Worker }
1044*c8dee2aaSAndroid Build Coastguard Worker
appendElement(RawElement && toAdd,RawElement::Stack * elements)1045*c8dee2aaSAndroid Build Coastguard Worker bool ClipStack::SaveRecord::appendElement(RawElement&& toAdd, RawElement::Stack* elements) {
1046*c8dee2aaSAndroid Build Coastguard Worker // Update past elements to account for the new element
1047*c8dee2aaSAndroid Build Coastguard Worker int i = elements->count() - 1;
1048*c8dee2aaSAndroid Build Coastguard Worker
1049*c8dee2aaSAndroid Build Coastguard Worker // After the loop, elements between [max(youngestValid, startingIndex)+1, count-1] can be
1050*c8dee2aaSAndroid Build Coastguard Worker // removed from the stack (these are the active elements that have been invalidated by the
1051*c8dee2aaSAndroid Build Coastguard Worker // newest element; since it's the active part of the stack, no restore() can bring them back).
1052*c8dee2aaSAndroid Build Coastguard Worker int youngestValid = fStartingElementIndex - 1;
1053*c8dee2aaSAndroid Build Coastguard Worker // After the loop, elements between [0, oldestValid-1] are all invalid. The value of oldestValid
1054*c8dee2aaSAndroid Build Coastguard Worker // becomes the save record's new fLastValidIndex value.
1055*c8dee2aaSAndroid Build Coastguard Worker int oldestValid = elements->count();
1056*c8dee2aaSAndroid Build Coastguard Worker // After the loop, this is the earliest active element that was invalidated. It may be
1057*c8dee2aaSAndroid Build Coastguard Worker // older in the stack than earliestValid, so cannot be popped off, but can be used to store
1058*c8dee2aaSAndroid Build Coastguard Worker // the new element instead of allocating more.
1059*c8dee2aaSAndroid Build Coastguard Worker RawElement* oldestActiveInvalid = nullptr;
1060*c8dee2aaSAndroid Build Coastguard Worker int oldestActiveInvalidIndex = elements->count();
1061*c8dee2aaSAndroid Build Coastguard Worker
1062*c8dee2aaSAndroid Build Coastguard Worker for (RawElement& existing : elements->ritems()) {
1063*c8dee2aaSAndroid Build Coastguard Worker if (i < fOldestValidIndex) {
1064*c8dee2aaSAndroid Build Coastguard Worker break;
1065*c8dee2aaSAndroid Build Coastguard Worker }
1066*c8dee2aaSAndroid Build Coastguard Worker // We don't need to pass the actual index that toAdd will be saved to; just the minimum
1067*c8dee2aaSAndroid Build Coastguard Worker // index of this save record, since that will result in the same restoration behavior later.
1068*c8dee2aaSAndroid Build Coastguard Worker existing.updateForElement(&toAdd, *this);
1069*c8dee2aaSAndroid Build Coastguard Worker
1070*c8dee2aaSAndroid Build Coastguard Worker if (toAdd.isInvalid()) {
1071*c8dee2aaSAndroid Build Coastguard Worker if (existing.isInvalid()) {
1072*c8dee2aaSAndroid Build Coastguard Worker // Both new and old invalid implies the entire clip becomes empty
1073*c8dee2aaSAndroid Build Coastguard Worker fState = ClipState::kEmpty;
1074*c8dee2aaSAndroid Build Coastguard Worker return true;
1075*c8dee2aaSAndroid Build Coastguard Worker } else {
1076*c8dee2aaSAndroid Build Coastguard Worker // The new element doesn't change the clip beyond what the old element already does
1077*c8dee2aaSAndroid Build Coastguard Worker return false;
1078*c8dee2aaSAndroid Build Coastguard Worker }
1079*c8dee2aaSAndroid Build Coastguard Worker } else if (existing.isInvalid()) {
1080*c8dee2aaSAndroid Build Coastguard Worker // The new element cancels out the old element. The new element may have been modified
1081*c8dee2aaSAndroid Build Coastguard Worker // to account for the old element's geometry.
1082*c8dee2aaSAndroid Build Coastguard Worker if (i >= fStartingElementIndex) {
1083*c8dee2aaSAndroid Build Coastguard Worker // Still active, so the invalidated index could be used to store the new element
1084*c8dee2aaSAndroid Build Coastguard Worker oldestActiveInvalid = &existing;
1085*c8dee2aaSAndroid Build Coastguard Worker oldestActiveInvalidIndex = i;
1086*c8dee2aaSAndroid Build Coastguard Worker }
1087*c8dee2aaSAndroid Build Coastguard Worker } else {
1088*c8dee2aaSAndroid Build Coastguard Worker // Keep both new and old elements
1089*c8dee2aaSAndroid Build Coastguard Worker oldestValid = i;
1090*c8dee2aaSAndroid Build Coastguard Worker if (i > youngestValid) {
1091*c8dee2aaSAndroid Build Coastguard Worker youngestValid = i;
1092*c8dee2aaSAndroid Build Coastguard Worker }
1093*c8dee2aaSAndroid Build Coastguard Worker }
1094*c8dee2aaSAndroid Build Coastguard Worker
1095*c8dee2aaSAndroid Build Coastguard Worker --i;
1096*c8dee2aaSAndroid Build Coastguard Worker }
1097*c8dee2aaSAndroid Build Coastguard Worker
1098*c8dee2aaSAndroid Build Coastguard Worker // Post-iteration validity check
1099*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(oldestValid == elements->count() ||
1100*c8dee2aaSAndroid Build Coastguard Worker (oldestValid >= fOldestValidIndex && oldestValid < elements->count()));
1101*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(youngestValid == fStartingElementIndex - 1 ||
1102*c8dee2aaSAndroid Build Coastguard Worker (youngestValid >= fStartingElementIndex && youngestValid < elements->count()));
1103*c8dee2aaSAndroid Build Coastguard Worker SkASSERT((oldestActiveInvalid && oldestActiveInvalidIndex >= fStartingElementIndex &&
1104*c8dee2aaSAndroid Build Coastguard Worker oldestActiveInvalidIndex < elements->count()) || !oldestActiveInvalid);
1105*c8dee2aaSAndroid Build Coastguard Worker
1106*c8dee2aaSAndroid Build Coastguard Worker // Update final state
1107*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(oldestValid >= fOldestValidIndex);
1108*c8dee2aaSAndroid Build Coastguard Worker fOldestValidIndex = std::min(oldestValid, oldestActiveInvalidIndex);
1109*c8dee2aaSAndroid Build Coastguard Worker fState = oldestValid == elements->count() ? toAdd.clipType() : ClipState::kComplex;
1110*c8dee2aaSAndroid Build Coastguard Worker if (fStackOp == SkClipOp::kDifference && toAdd.op() == SkClipOp::kIntersect) {
1111*c8dee2aaSAndroid Build Coastguard Worker // The stack remains in difference mode only as long as all elements are difference
1112*c8dee2aaSAndroid Build Coastguard Worker fStackOp = SkClipOp::kIntersect;
1113*c8dee2aaSAndroid Build Coastguard Worker }
1114*c8dee2aaSAndroid Build Coastguard Worker
1115*c8dee2aaSAndroid Build Coastguard Worker int targetCount = youngestValid + 1;
1116*c8dee2aaSAndroid Build Coastguard Worker if (!oldestActiveInvalid || oldestActiveInvalidIndex >= targetCount) {
1117*c8dee2aaSAndroid Build Coastguard Worker // toAdd will be stored right after youngestValid
1118*c8dee2aaSAndroid Build Coastguard Worker targetCount++;
1119*c8dee2aaSAndroid Build Coastguard Worker oldestActiveInvalid = nullptr;
1120*c8dee2aaSAndroid Build Coastguard Worker }
1121*c8dee2aaSAndroid Build Coastguard Worker while (elements->count() > targetCount) {
1122*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(oldestActiveInvalid != &elements->back()); // shouldn't delete what we'll reuse
1123*c8dee2aaSAndroid Build Coastguard Worker elements->pop_back();
1124*c8dee2aaSAndroid Build Coastguard Worker }
1125*c8dee2aaSAndroid Build Coastguard Worker if (oldestActiveInvalid) {
1126*c8dee2aaSAndroid Build Coastguard Worker *oldestActiveInvalid = std::move(toAdd);
1127*c8dee2aaSAndroid Build Coastguard Worker } else if (elements->count() < targetCount) {
1128*c8dee2aaSAndroid Build Coastguard Worker elements->push_back(std::move(toAdd));
1129*c8dee2aaSAndroid Build Coastguard Worker } else {
1130*c8dee2aaSAndroid Build Coastguard Worker elements->back() = std::move(toAdd);
1131*c8dee2aaSAndroid Build Coastguard Worker }
1132*c8dee2aaSAndroid Build Coastguard Worker
1133*c8dee2aaSAndroid Build Coastguard Worker // Changing this will prompt ClipStack to invalidate any masks associated with this record.
1134*c8dee2aaSAndroid Build Coastguard Worker fGenID = next_gen_id();
1135*c8dee2aaSAndroid Build Coastguard Worker return true;
1136*c8dee2aaSAndroid Build Coastguard Worker }
1137*c8dee2aaSAndroid Build Coastguard Worker
replaceWithElement(RawElement && toAdd,RawElement::Stack * elements)1138*c8dee2aaSAndroid Build Coastguard Worker void ClipStack::SaveRecord::replaceWithElement(RawElement&& toAdd, RawElement::Stack* elements) {
1139*c8dee2aaSAndroid Build Coastguard Worker // The aggregate state of the save record mirrors the element
1140*c8dee2aaSAndroid Build Coastguard Worker fInnerBounds = toAdd.innerBounds();
1141*c8dee2aaSAndroid Build Coastguard Worker fOuterBounds = toAdd.outerBounds();
1142*c8dee2aaSAndroid Build Coastguard Worker fStackOp = toAdd.op();
1143*c8dee2aaSAndroid Build Coastguard Worker fState = toAdd.clipType();
1144*c8dee2aaSAndroid Build Coastguard Worker
1145*c8dee2aaSAndroid Build Coastguard Worker // All prior active element can be removed from the stack: [startingIndex, count - 1]
1146*c8dee2aaSAndroid Build Coastguard Worker int targetCount = fStartingElementIndex + 1;
1147*c8dee2aaSAndroid Build Coastguard Worker while (elements->count() > targetCount) {
1148*c8dee2aaSAndroid Build Coastguard Worker elements->pop_back();
1149*c8dee2aaSAndroid Build Coastguard Worker }
1150*c8dee2aaSAndroid Build Coastguard Worker if (elements->count() < targetCount) {
1151*c8dee2aaSAndroid Build Coastguard Worker elements->push_back(std::move(toAdd));
1152*c8dee2aaSAndroid Build Coastguard Worker } else {
1153*c8dee2aaSAndroid Build Coastguard Worker elements->back() = std::move(toAdd);
1154*c8dee2aaSAndroid Build Coastguard Worker }
1155*c8dee2aaSAndroid Build Coastguard Worker
1156*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(elements->count() == fStartingElementIndex + 1);
1157*c8dee2aaSAndroid Build Coastguard Worker
1158*c8dee2aaSAndroid Build Coastguard Worker // This invalidates all older elements that are owned by save records lower in the clip stack.
1159*c8dee2aaSAndroid Build Coastguard Worker fOldestValidIndex = fStartingElementIndex;
1160*c8dee2aaSAndroid Build Coastguard Worker fGenID = next_gen_id();
1161*c8dee2aaSAndroid Build Coastguard Worker }
1162*c8dee2aaSAndroid Build Coastguard Worker
1163*c8dee2aaSAndroid Build Coastguard Worker ///////////////////////////////////////////////////////////////////////////////
1164*c8dee2aaSAndroid Build Coastguard Worker // ClipStack
1165*c8dee2aaSAndroid Build Coastguard Worker
1166*c8dee2aaSAndroid Build Coastguard Worker // NOTE: Based on draw calls in all GMs, SKPs, and SVGs as of 08/20, 98% use a clip stack with
1167*c8dee2aaSAndroid Build Coastguard Worker // one Element and up to two SaveRecords, thus the inline size for RawElement::Stack and
1168*c8dee2aaSAndroid Build Coastguard Worker // SaveRecord::Stack (this conveniently keeps the size of ClipStack manageable). The max
1169*c8dee2aaSAndroid Build Coastguard Worker // encountered element stack depth was 5 and the max save depth was 6. Using an increment of 8 for
1170*c8dee2aaSAndroid Build Coastguard Worker // these stacks means that clip management will incur a single allocation for the remaining 2%
1171*c8dee2aaSAndroid Build Coastguard Worker // of the draws, with extra head room for more complex clips encountered in the wild.
1172*c8dee2aaSAndroid Build Coastguard Worker //
1173*c8dee2aaSAndroid Build Coastguard Worker // The mask stack increment size was chosen to be smaller since only 0.2% of the evaluated draw call
1174*c8dee2aaSAndroid Build Coastguard Worker // set ever used a mask (which includes stencil masks), or up to 0.3% when the atlas is disabled.
1175*c8dee2aaSAndroid Build Coastguard Worker static constexpr int kElementStackIncrement = 8;
1176*c8dee2aaSAndroid Build Coastguard Worker static constexpr int kSaveStackIncrement = 8;
1177*c8dee2aaSAndroid Build Coastguard Worker static constexpr int kMaskStackIncrement = 4;
1178*c8dee2aaSAndroid Build Coastguard Worker
1179*c8dee2aaSAndroid Build Coastguard Worker // And from this same draw call set, the most complex clip could only use 5 analytic coverage FPs.
1180*c8dee2aaSAndroid Build Coastguard Worker // Historically we limited it to 4 based on Blink's call pattern, so we keep the limit as-is since
1181*c8dee2aaSAndroid Build Coastguard Worker // it's so close to the empirically encountered max.
1182*c8dee2aaSAndroid Build Coastguard Worker static constexpr int kMaxAnalyticFPs = 4;
1183*c8dee2aaSAndroid Build Coastguard Worker // The number of stack-allocated mask pointers to store before extending the arrays.
1184*c8dee2aaSAndroid Build Coastguard Worker // Stack size determined empirically, the maximum number of elements put in a SW mask was 4
1185*c8dee2aaSAndroid Build Coastguard Worker // across our set of GMs, SKPs, and SVGs used for testing.
1186*c8dee2aaSAndroid Build Coastguard Worker static constexpr int kNumStackMasks = 4;
1187*c8dee2aaSAndroid Build Coastguard Worker
ClipStack(const SkIRect & deviceBounds,const SkMatrix * ctm,bool forceAA)1188*c8dee2aaSAndroid Build Coastguard Worker ClipStack::ClipStack(const SkIRect& deviceBounds, const SkMatrix* ctm, bool forceAA)
1189*c8dee2aaSAndroid Build Coastguard Worker : fElements(kElementStackIncrement)
1190*c8dee2aaSAndroid Build Coastguard Worker , fSaves(kSaveStackIncrement)
1191*c8dee2aaSAndroid Build Coastguard Worker , fMasks(kMaskStackIncrement)
1192*c8dee2aaSAndroid Build Coastguard Worker , fProxyProvider(nullptr)
1193*c8dee2aaSAndroid Build Coastguard Worker , fDeviceBounds(deviceBounds)
1194*c8dee2aaSAndroid Build Coastguard Worker , fCTM(ctm)
1195*c8dee2aaSAndroid Build Coastguard Worker , fForceAA(forceAA) {
1196*c8dee2aaSAndroid Build Coastguard Worker // Start with a save record that is wide open
1197*c8dee2aaSAndroid Build Coastguard Worker fSaves.emplace_back(deviceBounds);
1198*c8dee2aaSAndroid Build Coastguard Worker }
1199*c8dee2aaSAndroid Build Coastguard Worker
~ClipStack()1200*c8dee2aaSAndroid Build Coastguard Worker ClipStack::~ClipStack() {
1201*c8dee2aaSAndroid Build Coastguard Worker // Invalidate all mask keys that remain. Since we're tearing the clip stack down, we don't need
1202*c8dee2aaSAndroid Build Coastguard Worker // to go through SaveRecord.
1203*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fProxyProvider || fMasks.empty());
1204*c8dee2aaSAndroid Build Coastguard Worker if (fProxyProvider) {
1205*c8dee2aaSAndroid Build Coastguard Worker for (Mask& m : fMasks.ritems()) {
1206*c8dee2aaSAndroid Build Coastguard Worker m.invalidate(fProxyProvider);
1207*c8dee2aaSAndroid Build Coastguard Worker }
1208*c8dee2aaSAndroid Build Coastguard Worker }
1209*c8dee2aaSAndroid Build Coastguard Worker }
1210*c8dee2aaSAndroid Build Coastguard Worker
save()1211*c8dee2aaSAndroid Build Coastguard Worker void ClipStack::save() {
1212*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!fSaves.empty());
1213*c8dee2aaSAndroid Build Coastguard Worker fSaves.back().pushSave();
1214*c8dee2aaSAndroid Build Coastguard Worker }
1215*c8dee2aaSAndroid Build Coastguard Worker
restore()1216*c8dee2aaSAndroid Build Coastguard Worker void ClipStack::restore() {
1217*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!fSaves.empty());
1218*c8dee2aaSAndroid Build Coastguard Worker SaveRecord& current = fSaves.back();
1219*c8dee2aaSAndroid Build Coastguard Worker if (current.popSave()) {
1220*c8dee2aaSAndroid Build Coastguard Worker // This was just a deferred save being undone, so the record doesn't need to be removed yet
1221*c8dee2aaSAndroid Build Coastguard Worker return;
1222*c8dee2aaSAndroid Build Coastguard Worker }
1223*c8dee2aaSAndroid Build Coastguard Worker
1224*c8dee2aaSAndroid Build Coastguard Worker // When we remove a save record, we delete all elements >= its starting index and any masks
1225*c8dee2aaSAndroid Build Coastguard Worker // that were rasterized for it.
1226*c8dee2aaSAndroid Build Coastguard Worker current.removeElements(&fElements);
1227*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fProxyProvider || fMasks.empty());
1228*c8dee2aaSAndroid Build Coastguard Worker if (fProxyProvider) {
1229*c8dee2aaSAndroid Build Coastguard Worker current.invalidateMasks(fProxyProvider, &fMasks);
1230*c8dee2aaSAndroid Build Coastguard Worker }
1231*c8dee2aaSAndroid Build Coastguard Worker fSaves.pop_back();
1232*c8dee2aaSAndroid Build Coastguard Worker // Restore any remaining elements that were only invalidated by the now-removed save record.
1233*c8dee2aaSAndroid Build Coastguard Worker fSaves.back().restoreElements(&fElements);
1234*c8dee2aaSAndroid Build Coastguard Worker }
1235*c8dee2aaSAndroid Build Coastguard Worker
getConservativeBounds() const1236*c8dee2aaSAndroid Build Coastguard Worker SkIRect ClipStack::getConservativeBounds() const {
1237*c8dee2aaSAndroid Build Coastguard Worker const SaveRecord& current = this->currentSaveRecord();
1238*c8dee2aaSAndroid Build Coastguard Worker if (current.state() == ClipState::kEmpty) {
1239*c8dee2aaSAndroid Build Coastguard Worker return SkIRect::MakeEmpty();
1240*c8dee2aaSAndroid Build Coastguard Worker } else if (current.state() == ClipState::kWideOpen) {
1241*c8dee2aaSAndroid Build Coastguard Worker return fDeviceBounds;
1242*c8dee2aaSAndroid Build Coastguard Worker } else {
1243*c8dee2aaSAndroid Build Coastguard Worker if (current.op() == SkClipOp::kDifference) {
1244*c8dee2aaSAndroid Build Coastguard Worker // The outer/inner bounds represent what's cut out, so full bounds remains the device
1245*c8dee2aaSAndroid Build Coastguard Worker // bounds, minus any fully clipped content that spans the device edge.
1246*c8dee2aaSAndroid Build Coastguard Worker return subtract(fDeviceBounds, current.innerBounds(), /* exact */ true);
1247*c8dee2aaSAndroid Build Coastguard Worker } else {
1248*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fDeviceBounds.contains(current.outerBounds()));
1249*c8dee2aaSAndroid Build Coastguard Worker return current.outerBounds();
1250*c8dee2aaSAndroid Build Coastguard Worker }
1251*c8dee2aaSAndroid Build Coastguard Worker }
1252*c8dee2aaSAndroid Build Coastguard Worker }
1253*c8dee2aaSAndroid Build Coastguard Worker
preApply(const SkRect & bounds,GrAA aa) const1254*c8dee2aaSAndroid Build Coastguard Worker GrClip::PreClipResult ClipStack::preApply(const SkRect& bounds, GrAA aa) const {
1255*c8dee2aaSAndroid Build Coastguard Worker Draw draw(bounds, fForceAA ? GrAA::kYes : aa);
1256*c8dee2aaSAndroid Build Coastguard Worker if (!draw.applyDeviceBounds(fDeviceBounds)) {
1257*c8dee2aaSAndroid Build Coastguard Worker return GrClip::Effect::kClippedOut;
1258*c8dee2aaSAndroid Build Coastguard Worker }
1259*c8dee2aaSAndroid Build Coastguard Worker
1260*c8dee2aaSAndroid Build Coastguard Worker const SaveRecord& cs = this->currentSaveRecord();
1261*c8dee2aaSAndroid Build Coastguard Worker // Early out if we know a priori that the clip is full 0s or full 1s.
1262*c8dee2aaSAndroid Build Coastguard Worker if (cs.state() == ClipState::kEmpty) {
1263*c8dee2aaSAndroid Build Coastguard Worker return GrClip::Effect::kClippedOut;
1264*c8dee2aaSAndroid Build Coastguard Worker } else if (cs.state() == ClipState::kWideOpen) {
1265*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!cs.shader());
1266*c8dee2aaSAndroid Build Coastguard Worker return GrClip::Effect::kUnclipped;
1267*c8dee2aaSAndroid Build Coastguard Worker }
1268*c8dee2aaSAndroid Build Coastguard Worker
1269*c8dee2aaSAndroid Build Coastguard Worker // Given argument order, 'A' == current clip, 'B' == draw
1270*c8dee2aaSAndroid Build Coastguard Worker switch (get_clip_geometry(cs, draw)) {
1271*c8dee2aaSAndroid Build Coastguard Worker case ClipGeometry::kEmpty:
1272*c8dee2aaSAndroid Build Coastguard Worker // Can ignore the shader since the geometry removed everything already
1273*c8dee2aaSAndroid Build Coastguard Worker return GrClip::Effect::kClippedOut;
1274*c8dee2aaSAndroid Build Coastguard Worker
1275*c8dee2aaSAndroid Build Coastguard Worker case ClipGeometry::kBOnly:
1276*c8dee2aaSAndroid Build Coastguard Worker // Geometrically, the draw is unclipped, but can't ignore a shader
1277*c8dee2aaSAndroid Build Coastguard Worker return cs.shader() ? GrClip::Effect::kClipped : GrClip::Effect::kUnclipped;
1278*c8dee2aaSAndroid Build Coastguard Worker
1279*c8dee2aaSAndroid Build Coastguard Worker case ClipGeometry::kAOnly:
1280*c8dee2aaSAndroid Build Coastguard Worker // Shouldn't happen since the inner bounds of a draw are unknown
1281*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(false);
1282*c8dee2aaSAndroid Build Coastguard Worker // But if it did, it technically means the draw covered the clip and should be
1283*c8dee2aaSAndroid Build Coastguard Worker // considered kClipped or similar, which is what the next case handles.
1284*c8dee2aaSAndroid Build Coastguard Worker [[fallthrough]];
1285*c8dee2aaSAndroid Build Coastguard Worker
1286*c8dee2aaSAndroid Build Coastguard Worker case ClipGeometry::kBoth: {
1287*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fElements.count() > 0);
1288*c8dee2aaSAndroid Build Coastguard Worker const RawElement& back = fElements.back();
1289*c8dee2aaSAndroid Build Coastguard Worker if (cs.state() == ClipState::kDeviceRect) {
1290*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(back.clipType() == ClipState::kDeviceRect);
1291*c8dee2aaSAndroid Build Coastguard Worker return {back.shape().rect(), back.aa()};
1292*c8dee2aaSAndroid Build Coastguard Worker } else if (cs.state() == ClipState::kDeviceRRect) {
1293*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(back.clipType() == ClipState::kDeviceRRect);
1294*c8dee2aaSAndroid Build Coastguard Worker return {back.shape().rrect(), back.aa()};
1295*c8dee2aaSAndroid Build Coastguard Worker } else {
1296*c8dee2aaSAndroid Build Coastguard Worker // The clip stack has complex shapes, multiple elements, or a shader; we could
1297*c8dee2aaSAndroid Build Coastguard Worker // iterate per element like we would in apply(), but preApply() is meant to be
1298*c8dee2aaSAndroid Build Coastguard Worker // conservative and efficient.
1299*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(cs.state() == ClipState::kComplex);
1300*c8dee2aaSAndroid Build Coastguard Worker return GrClip::Effect::kClipped;
1301*c8dee2aaSAndroid Build Coastguard Worker }
1302*c8dee2aaSAndroid Build Coastguard Worker }
1303*c8dee2aaSAndroid Build Coastguard Worker }
1304*c8dee2aaSAndroid Build Coastguard Worker
1305*c8dee2aaSAndroid Build Coastguard Worker SkUNREACHABLE;
1306*c8dee2aaSAndroid Build Coastguard Worker }
1307*c8dee2aaSAndroid Build Coastguard Worker
apply(GrRecordingContext * rContext,SurfaceDrawContext * sdc,GrDrawOp * op,GrAAType aa,GrAppliedClip * out,SkRect * bounds) const1308*c8dee2aaSAndroid Build Coastguard Worker GrClip::Effect ClipStack::apply(GrRecordingContext* rContext,
1309*c8dee2aaSAndroid Build Coastguard Worker SurfaceDrawContext* sdc,
1310*c8dee2aaSAndroid Build Coastguard Worker GrDrawOp* op,
1311*c8dee2aaSAndroid Build Coastguard Worker GrAAType aa,
1312*c8dee2aaSAndroid Build Coastguard Worker GrAppliedClip* out,
1313*c8dee2aaSAndroid Build Coastguard Worker SkRect* bounds) const {
1314*c8dee2aaSAndroid Build Coastguard Worker // TODO: Once we no longer store SW masks, we don't need to sneak the provider in like this
1315*c8dee2aaSAndroid Build Coastguard Worker if (!fProxyProvider) {
1316*c8dee2aaSAndroid Build Coastguard Worker fProxyProvider = rContext->priv().proxyProvider();
1317*c8dee2aaSAndroid Build Coastguard Worker }
1318*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fProxyProvider == rContext->priv().proxyProvider());
1319*c8dee2aaSAndroid Build Coastguard Worker const GrCaps* caps = rContext->priv().caps();
1320*c8dee2aaSAndroid Build Coastguard Worker
1321*c8dee2aaSAndroid Build Coastguard Worker // Convert the bounds to a Draw and apply device bounds clipping, making our query as tight
1322*c8dee2aaSAndroid Build Coastguard Worker // as possible.
1323*c8dee2aaSAndroid Build Coastguard Worker Draw draw(*bounds, GrAA(fForceAA || aa != GrAAType::kNone));
1324*c8dee2aaSAndroid Build Coastguard Worker if (!draw.applyDeviceBounds(fDeviceBounds)) {
1325*c8dee2aaSAndroid Build Coastguard Worker return Effect::kClippedOut;
1326*c8dee2aaSAndroid Build Coastguard Worker }
1327*c8dee2aaSAndroid Build Coastguard Worker SkAssertResult(bounds->intersect(SkRect::Make(fDeviceBounds)));
1328*c8dee2aaSAndroid Build Coastguard Worker
1329*c8dee2aaSAndroid Build Coastguard Worker const SaveRecord& cs = this->currentSaveRecord();
1330*c8dee2aaSAndroid Build Coastguard Worker // Early out if we know a priori that the clip is full 0s or full 1s.
1331*c8dee2aaSAndroid Build Coastguard Worker if (cs.state() == ClipState::kEmpty) {
1332*c8dee2aaSAndroid Build Coastguard Worker return Effect::kClippedOut;
1333*c8dee2aaSAndroid Build Coastguard Worker } else if (cs.state() == ClipState::kWideOpen) {
1334*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!cs.shader());
1335*c8dee2aaSAndroid Build Coastguard Worker return Effect::kUnclipped;
1336*c8dee2aaSAndroid Build Coastguard Worker }
1337*c8dee2aaSAndroid Build Coastguard Worker
1338*c8dee2aaSAndroid Build Coastguard Worker // Convert any clip shader first, since it's not geometrically related to the draw bounds
1339*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<GrFragmentProcessor> clipFP = nullptr;
1340*c8dee2aaSAndroid Build Coastguard Worker if (cs.shader()) {
1341*c8dee2aaSAndroid Build Coastguard Worker static const GrColorInfo kCoverageColorInfo{GrColorType::kUnknown, kPremul_SkAlphaType,
1342*c8dee2aaSAndroid Build Coastguard Worker nullptr};
1343*c8dee2aaSAndroid Build Coastguard Worker GrFPArgs args(
1344*c8dee2aaSAndroid Build Coastguard Worker rContext, &kCoverageColorInfo, sdc->surfaceProps(), GrFPArgs::Scope::kDefault);
1345*c8dee2aaSAndroid Build Coastguard Worker clipFP = GrFragmentProcessors::Make(cs.shader(), args, *fCTM);
1346*c8dee2aaSAndroid Build Coastguard Worker if (clipFP) {
1347*c8dee2aaSAndroid Build Coastguard Worker // The initial input is the coverage from the geometry processor, so this ensures it
1348*c8dee2aaSAndroid Build Coastguard Worker // is multiplied properly with the alpha of the clip shader.
1349*c8dee2aaSAndroid Build Coastguard Worker clipFP = GrFragmentProcessor::MulInputByChildAlpha(std::move(clipFP));
1350*c8dee2aaSAndroid Build Coastguard Worker }
1351*c8dee2aaSAndroid Build Coastguard Worker }
1352*c8dee2aaSAndroid Build Coastguard Worker
1353*c8dee2aaSAndroid Build Coastguard Worker // A refers to the entire clip stack, B refers to the draw
1354*c8dee2aaSAndroid Build Coastguard Worker switch (get_clip_geometry(cs, draw)) {
1355*c8dee2aaSAndroid Build Coastguard Worker case ClipGeometry::kEmpty:
1356*c8dee2aaSAndroid Build Coastguard Worker return Effect::kClippedOut;
1357*c8dee2aaSAndroid Build Coastguard Worker
1358*c8dee2aaSAndroid Build Coastguard Worker case ClipGeometry::kBOnly:
1359*c8dee2aaSAndroid Build Coastguard Worker // Geometrically unclipped, but may need to add the shader as a coverage FP
1360*c8dee2aaSAndroid Build Coastguard Worker if (clipFP) {
1361*c8dee2aaSAndroid Build Coastguard Worker out->addCoverageFP(std::move(clipFP));
1362*c8dee2aaSAndroid Build Coastguard Worker return Effect::kClipped;
1363*c8dee2aaSAndroid Build Coastguard Worker } else {
1364*c8dee2aaSAndroid Build Coastguard Worker return Effect::kUnclipped;
1365*c8dee2aaSAndroid Build Coastguard Worker }
1366*c8dee2aaSAndroid Build Coastguard Worker
1367*c8dee2aaSAndroid Build Coastguard Worker case ClipGeometry::kAOnly:
1368*c8dee2aaSAndroid Build Coastguard Worker // Shouldn't happen since draws don't report inner bounds
1369*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(false);
1370*c8dee2aaSAndroid Build Coastguard Worker [[fallthrough]];
1371*c8dee2aaSAndroid Build Coastguard Worker
1372*c8dee2aaSAndroid Build Coastguard Worker case ClipGeometry::kBoth:
1373*c8dee2aaSAndroid Build Coastguard Worker // The draw is combined with the saved clip elements; the below logic tries to skip
1374*c8dee2aaSAndroid Build Coastguard Worker // as many elements as possible.
1375*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(cs.state() == ClipState::kDeviceRect ||
1376*c8dee2aaSAndroid Build Coastguard Worker cs.state() == ClipState::kDeviceRRect ||
1377*c8dee2aaSAndroid Build Coastguard Worker cs.state() == ClipState::kComplex);
1378*c8dee2aaSAndroid Build Coastguard Worker break;
1379*c8dee2aaSAndroid Build Coastguard Worker }
1380*c8dee2aaSAndroid Build Coastguard Worker
1381*c8dee2aaSAndroid Build Coastguard Worker // We can determine a scissor based on the draw and the overall stack bounds.
1382*c8dee2aaSAndroid Build Coastguard Worker SkIRect scissorBounds;
1383*c8dee2aaSAndroid Build Coastguard Worker if (cs.op() == SkClipOp::kIntersect) {
1384*c8dee2aaSAndroid Build Coastguard Worker // Initially we keep this as large as possible; if the clip is applied solely with coverage
1385*c8dee2aaSAndroid Build Coastguard Worker // FPs then using a loose scissor increases the chance we can batch the draws.
1386*c8dee2aaSAndroid Build Coastguard Worker // We tighten it later if any form of mask or atlas element is needed.
1387*c8dee2aaSAndroid Build Coastguard Worker scissorBounds = cs.outerBounds();
1388*c8dee2aaSAndroid Build Coastguard Worker } else {
1389*c8dee2aaSAndroid Build Coastguard Worker scissorBounds = subtract(draw.outerBounds(), cs.innerBounds(), /* exact */ true);
1390*c8dee2aaSAndroid Build Coastguard Worker }
1391*c8dee2aaSAndroid Build Coastguard Worker
1392*c8dee2aaSAndroid Build Coastguard Worker // We mark this true once we have a coverage FP (since complex clipping is occurring), or we
1393*c8dee2aaSAndroid Build Coastguard Worker // have an element that wouldn't affect the scissored draw bounds, but does affect the regular
1394*c8dee2aaSAndroid Build Coastguard Worker // draw bounds. In that case, the scissor is sufficient for clipping and we can skip the
1395*c8dee2aaSAndroid Build Coastguard Worker // element but definitely cannot then drop the scissor.
1396*c8dee2aaSAndroid Build Coastguard Worker bool scissorIsNeeded = SkToBool(cs.shader());
1397*c8dee2aaSAndroid Build Coastguard Worker SkDEBUGCODE(bool opClippedInternally = false;)
1398*c8dee2aaSAndroid Build Coastguard Worker
1399*c8dee2aaSAndroid Build Coastguard Worker int remainingAnalyticFPs = kMaxAnalyticFPs;
1400*c8dee2aaSAndroid Build Coastguard Worker
1401*c8dee2aaSAndroid Build Coastguard Worker // If window rectangles are supported, we can use them to exclude inner bounds of difference ops
1402*c8dee2aaSAndroid Build Coastguard Worker int maxWindowRectangles = sdc->maxWindowRectangles();
1403*c8dee2aaSAndroid Build Coastguard Worker GrWindowRectangles windowRects;
1404*c8dee2aaSAndroid Build Coastguard Worker
1405*c8dee2aaSAndroid Build Coastguard Worker // Elements not represented as an analytic FP or skipped will be collected here and later
1406*c8dee2aaSAndroid Build Coastguard Worker // applied by using the stencil buffer or a cached SW mask.
1407*c8dee2aaSAndroid Build Coastguard Worker STArray<kNumStackMasks, const Element*> elementsForMask;
1408*c8dee2aaSAndroid Build Coastguard Worker
1409*c8dee2aaSAndroid Build Coastguard Worker bool maskRequiresAA = false;
1410*c8dee2aaSAndroid Build Coastguard Worker auto atlasPathRenderer = rContext->priv().drawingManager()->getAtlasPathRenderer();
1411*c8dee2aaSAndroid Build Coastguard Worker
1412*c8dee2aaSAndroid Build Coastguard Worker int i = fElements.count();
1413*c8dee2aaSAndroid Build Coastguard Worker for (const RawElement& e : fElements.ritems()) {
1414*c8dee2aaSAndroid Build Coastguard Worker --i;
1415*c8dee2aaSAndroid Build Coastguard Worker if (i < cs.oldestElementIndex()) {
1416*c8dee2aaSAndroid Build Coastguard Worker // All earlier elements have been invalidated by elements already processed
1417*c8dee2aaSAndroid Build Coastguard Worker break;
1418*c8dee2aaSAndroid Build Coastguard Worker } else if (e.isInvalid()) {
1419*c8dee2aaSAndroid Build Coastguard Worker continue;
1420*c8dee2aaSAndroid Build Coastguard Worker }
1421*c8dee2aaSAndroid Build Coastguard Worker
1422*c8dee2aaSAndroid Build Coastguard Worker switch (get_clip_geometry(e, draw)) {
1423*c8dee2aaSAndroid Build Coastguard Worker case ClipGeometry::kEmpty:
1424*c8dee2aaSAndroid Build Coastguard Worker // This can happen for difference op elements that have a larger fInnerBounds than
1425*c8dee2aaSAndroid Build Coastguard Worker // can be preserved at the next level.
1426*c8dee2aaSAndroid Build Coastguard Worker return Effect::kClippedOut;
1427*c8dee2aaSAndroid Build Coastguard Worker
1428*c8dee2aaSAndroid Build Coastguard Worker case ClipGeometry::kBOnly:
1429*c8dee2aaSAndroid Build Coastguard Worker // We don't need to produce a coverage FP or mask for the element
1430*c8dee2aaSAndroid Build Coastguard Worker break;
1431*c8dee2aaSAndroid Build Coastguard Worker
1432*c8dee2aaSAndroid Build Coastguard Worker case ClipGeometry::kAOnly:
1433*c8dee2aaSAndroid Build Coastguard Worker // Shouldn't happen for draws, fall through to regular element processing
1434*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(false);
1435*c8dee2aaSAndroid Build Coastguard Worker [[fallthrough]];
1436*c8dee2aaSAndroid Build Coastguard Worker
1437*c8dee2aaSAndroid Build Coastguard Worker case ClipGeometry::kBoth: {
1438*c8dee2aaSAndroid Build Coastguard Worker // The element must apply coverage to the draw, enable the scissor to limit overdraw
1439*c8dee2aaSAndroid Build Coastguard Worker scissorIsNeeded = true;
1440*c8dee2aaSAndroid Build Coastguard Worker
1441*c8dee2aaSAndroid Build Coastguard Worker // First apply using HW methods (scissor and window rects). When the inner and outer
1442*c8dee2aaSAndroid Build Coastguard Worker // bounds match, nothing else needs to be done.
1443*c8dee2aaSAndroid Build Coastguard Worker bool fullyApplied = false;
1444*c8dee2aaSAndroid Build Coastguard Worker
1445*c8dee2aaSAndroid Build Coastguard Worker // First check if the op knows how to apply this clip internally.
1446*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!e.shape().inverted());
1447*c8dee2aaSAndroid Build Coastguard Worker auto result = op->clipToShape(sdc, e.op(), e.localToDevice(), e.shape(),
1448*c8dee2aaSAndroid Build Coastguard Worker GrAA(e.aa() == GrAA::kYes || fForceAA));
1449*c8dee2aaSAndroid Build Coastguard Worker if (result != GrDrawOp::ClipResult::kFail) {
1450*c8dee2aaSAndroid Build Coastguard Worker if (result == GrDrawOp::ClipResult::kClippedOut) {
1451*c8dee2aaSAndroid Build Coastguard Worker return Effect::kClippedOut;
1452*c8dee2aaSAndroid Build Coastguard Worker }
1453*c8dee2aaSAndroid Build Coastguard Worker if (result == GrDrawOp::ClipResult::kClippedGeometrically) {
1454*c8dee2aaSAndroid Build Coastguard Worker // The op clipped its own geometry. Tighten the draw bounds.
1455*c8dee2aaSAndroid Build Coastguard Worker bounds->intersect(SkRect::Make(e.outerBounds()));
1456*c8dee2aaSAndroid Build Coastguard Worker }
1457*c8dee2aaSAndroid Build Coastguard Worker fullyApplied = true;
1458*c8dee2aaSAndroid Build Coastguard Worker SkDEBUGCODE(opClippedInternally = true;)
1459*c8dee2aaSAndroid Build Coastguard Worker }
1460*c8dee2aaSAndroid Build Coastguard Worker
1461*c8dee2aaSAndroid Build Coastguard Worker if (!fullyApplied) {
1462*c8dee2aaSAndroid Build Coastguard Worker if (e.op() == SkClipOp::kIntersect) {
1463*c8dee2aaSAndroid Build Coastguard Worker // The second test allows clipped draws that are scissored by multiple
1464*c8dee2aaSAndroid Build Coastguard Worker // elements to remain scissor-only.
1465*c8dee2aaSAndroid Build Coastguard Worker fullyApplied = e.innerBounds() == e.outerBounds() ||
1466*c8dee2aaSAndroid Build Coastguard Worker e.innerBounds().contains(scissorBounds);
1467*c8dee2aaSAndroid Build Coastguard Worker } else {
1468*c8dee2aaSAndroid Build Coastguard Worker if (!e.innerBounds().isEmpty() &&
1469*c8dee2aaSAndroid Build Coastguard Worker windowRects.count() < maxWindowRectangles) {
1470*c8dee2aaSAndroid Build Coastguard Worker // TODO: If we have more difference ops than available window rects, we
1471*c8dee2aaSAndroid Build Coastguard Worker // should prioritize those with the largest inner bounds.
1472*c8dee2aaSAndroid Build Coastguard Worker windowRects.addWindow(e.innerBounds());
1473*c8dee2aaSAndroid Build Coastguard Worker fullyApplied = e.innerBounds() == e.outerBounds();
1474*c8dee2aaSAndroid Build Coastguard Worker }
1475*c8dee2aaSAndroid Build Coastguard Worker }
1476*c8dee2aaSAndroid Build Coastguard Worker }
1477*c8dee2aaSAndroid Build Coastguard Worker
1478*c8dee2aaSAndroid Build Coastguard Worker if (!fullyApplied && remainingAnalyticFPs > 0) {
1479*c8dee2aaSAndroid Build Coastguard Worker std::tie(fullyApplied, clipFP) = analytic_clip_fp(e.asElement(),
1480*c8dee2aaSAndroid Build Coastguard Worker *caps->shaderCaps(),
1481*c8dee2aaSAndroid Build Coastguard Worker std::move(clipFP));
1482*c8dee2aaSAndroid Build Coastguard Worker if (!fullyApplied && atlasPathRenderer) {
1483*c8dee2aaSAndroid Build Coastguard Worker std::tie(fullyApplied, clipFP) = clip_atlas_fp(sdc, op,
1484*c8dee2aaSAndroid Build Coastguard Worker atlasPathRenderer,
1485*c8dee2aaSAndroid Build Coastguard Worker scissorBounds, e.asElement(),
1486*c8dee2aaSAndroid Build Coastguard Worker std::move(clipFP));
1487*c8dee2aaSAndroid Build Coastguard Worker }
1488*c8dee2aaSAndroid Build Coastguard Worker if (fullyApplied) {
1489*c8dee2aaSAndroid Build Coastguard Worker remainingAnalyticFPs--;
1490*c8dee2aaSAndroid Build Coastguard Worker }
1491*c8dee2aaSAndroid Build Coastguard Worker }
1492*c8dee2aaSAndroid Build Coastguard Worker
1493*c8dee2aaSAndroid Build Coastguard Worker if (!fullyApplied) {
1494*c8dee2aaSAndroid Build Coastguard Worker elementsForMask.push_back(&e.asElement());
1495*c8dee2aaSAndroid Build Coastguard Worker maskRequiresAA |= (e.aa() == GrAA::kYes);
1496*c8dee2aaSAndroid Build Coastguard Worker }
1497*c8dee2aaSAndroid Build Coastguard Worker
1498*c8dee2aaSAndroid Build Coastguard Worker break;
1499*c8dee2aaSAndroid Build Coastguard Worker }
1500*c8dee2aaSAndroid Build Coastguard Worker }
1501*c8dee2aaSAndroid Build Coastguard Worker }
1502*c8dee2aaSAndroid Build Coastguard Worker
1503*c8dee2aaSAndroid Build Coastguard Worker if (!scissorIsNeeded) {
1504*c8dee2aaSAndroid Build Coastguard Worker // More detailed analysis of the element shapes determined no clip is needed
1505*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(elementsForMask.empty() && !clipFP);
1506*c8dee2aaSAndroid Build Coastguard Worker return Effect::kUnclipped;
1507*c8dee2aaSAndroid Build Coastguard Worker }
1508*c8dee2aaSAndroid Build Coastguard Worker
1509*c8dee2aaSAndroid Build Coastguard Worker // Fill out the GrAppliedClip with what we know so far, possibly with a tightened scissor
1510*c8dee2aaSAndroid Build Coastguard Worker if (cs.op() == SkClipOp::kIntersect && !elementsForMask.empty()) {
1511*c8dee2aaSAndroid Build Coastguard Worker SkAssertResult(scissorBounds.intersect(draw.outerBounds()));
1512*c8dee2aaSAndroid Build Coastguard Worker }
1513*c8dee2aaSAndroid Build Coastguard Worker if (!GrClip::IsInsideClip(scissorBounds, *bounds, draw.aa())) {
1514*c8dee2aaSAndroid Build Coastguard Worker out->hardClip().addScissor(scissorBounds, bounds);
1515*c8dee2aaSAndroid Build Coastguard Worker }
1516*c8dee2aaSAndroid Build Coastguard Worker if (!windowRects.empty()) {
1517*c8dee2aaSAndroid Build Coastguard Worker out->hardClip().addWindowRectangles(windowRects, GrWindowRectsState::Mode::kExclusive);
1518*c8dee2aaSAndroid Build Coastguard Worker }
1519*c8dee2aaSAndroid Build Coastguard Worker
1520*c8dee2aaSAndroid Build Coastguard Worker // Now rasterize any remaining elements, either to the stencil or a SW mask. All elements are
1521*c8dee2aaSAndroid Build Coastguard Worker // flattened into a single mask.
1522*c8dee2aaSAndroid Build Coastguard Worker if (!elementsForMask.empty()) {
1523*c8dee2aaSAndroid Build Coastguard Worker bool stencilUnavailable =
1524*c8dee2aaSAndroid Build Coastguard Worker !sdc->asRenderTargetProxy()->canUseStencil(*rContext->priv().caps());
1525*c8dee2aaSAndroid Build Coastguard Worker
1526*c8dee2aaSAndroid Build Coastguard Worker bool hasSWMask = false;
1527*c8dee2aaSAndroid Build Coastguard Worker if ((sdc->numSamples() <= 1 && !sdc->canUseDynamicMSAA() && maskRequiresAA) ||
1528*c8dee2aaSAndroid Build Coastguard Worker stencilUnavailable) {
1529*c8dee2aaSAndroid Build Coastguard Worker // Must use a texture mask to represent the combined clip elements since the stencil
1530*c8dee2aaSAndroid Build Coastguard Worker // cannot be used, or cannot handle smooth clips.
1531*c8dee2aaSAndroid Build Coastguard Worker std::tie(hasSWMask, clipFP) = GetSWMaskFP(
1532*c8dee2aaSAndroid Build Coastguard Worker rContext, &fMasks, cs, scissorBounds, elementsForMask.begin(),
1533*c8dee2aaSAndroid Build Coastguard Worker elementsForMask.size(), std::move(clipFP));
1534*c8dee2aaSAndroid Build Coastguard Worker }
1535*c8dee2aaSAndroid Build Coastguard Worker
1536*c8dee2aaSAndroid Build Coastguard Worker if (!hasSWMask) {
1537*c8dee2aaSAndroid Build Coastguard Worker if (stencilUnavailable) {
1538*c8dee2aaSAndroid Build Coastguard Worker SkDebugf("WARNING: Clip mask requires stencil, but stencil unavailable. "
1539*c8dee2aaSAndroid Build Coastguard Worker "Draw will be ignored.\n");
1540*c8dee2aaSAndroid Build Coastguard Worker return Effect::kClippedOut;
1541*c8dee2aaSAndroid Build Coastguard Worker } else {
1542*c8dee2aaSAndroid Build Coastguard Worker // Rasterize the remaining elements to the stencil buffer
1543*c8dee2aaSAndroid Build Coastguard Worker render_stencil_mask(rContext, sdc, cs.genID(), scissorBounds,
1544*c8dee2aaSAndroid Build Coastguard Worker elementsForMask.begin(), elementsForMask.size(), out);
1545*c8dee2aaSAndroid Build Coastguard Worker }
1546*c8dee2aaSAndroid Build Coastguard Worker }
1547*c8dee2aaSAndroid Build Coastguard Worker }
1548*c8dee2aaSAndroid Build Coastguard Worker
1549*c8dee2aaSAndroid Build Coastguard Worker if (clipFP) {
1550*c8dee2aaSAndroid Build Coastguard Worker // This will include all analytic FPs, all atlas FPs, and a SW mask FP.
1551*c8dee2aaSAndroid Build Coastguard Worker out->addCoverageFP(std::move(clipFP));
1552*c8dee2aaSAndroid Build Coastguard Worker }
1553*c8dee2aaSAndroid Build Coastguard Worker
1554*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(out->doesClip() || opClippedInternally);
1555*c8dee2aaSAndroid Build Coastguard Worker return Effect::kClipped;
1556*c8dee2aaSAndroid Build Coastguard Worker }
1557*c8dee2aaSAndroid Build Coastguard Worker
writableSaveRecord(bool * wasDeferred)1558*c8dee2aaSAndroid Build Coastguard Worker ClipStack::SaveRecord& ClipStack::writableSaveRecord(bool* wasDeferred) {
1559*c8dee2aaSAndroid Build Coastguard Worker SaveRecord& current = fSaves.back();
1560*c8dee2aaSAndroid Build Coastguard Worker if (current.canBeUpdated()) {
1561*c8dee2aaSAndroid Build Coastguard Worker // Current record is still open, so it can be modified directly
1562*c8dee2aaSAndroid Build Coastguard Worker *wasDeferred = false;
1563*c8dee2aaSAndroid Build Coastguard Worker return current;
1564*c8dee2aaSAndroid Build Coastguard Worker } else {
1565*c8dee2aaSAndroid Build Coastguard Worker // Must undefer the save to get a new record.
1566*c8dee2aaSAndroid Build Coastguard Worker SkAssertResult(current.popSave());
1567*c8dee2aaSAndroid Build Coastguard Worker *wasDeferred = true;
1568*c8dee2aaSAndroid Build Coastguard Worker return fSaves.emplace_back(current, fMasks.count(), fElements.count());
1569*c8dee2aaSAndroid Build Coastguard Worker }
1570*c8dee2aaSAndroid Build Coastguard Worker }
1571*c8dee2aaSAndroid Build Coastguard Worker
clipShader(sk_sp<SkShader> shader)1572*c8dee2aaSAndroid Build Coastguard Worker void ClipStack::clipShader(sk_sp<SkShader> shader) {
1573*c8dee2aaSAndroid Build Coastguard Worker // Shaders can't bring additional coverage
1574*c8dee2aaSAndroid Build Coastguard Worker if (this->currentSaveRecord().state() == ClipState::kEmpty) {
1575*c8dee2aaSAndroid Build Coastguard Worker return;
1576*c8dee2aaSAndroid Build Coastguard Worker }
1577*c8dee2aaSAndroid Build Coastguard Worker
1578*c8dee2aaSAndroid Build Coastguard Worker bool wasDeferred;
1579*c8dee2aaSAndroid Build Coastguard Worker this->writableSaveRecord(&wasDeferred).addShader(std::move(shader));
1580*c8dee2aaSAndroid Build Coastguard Worker // Masks and geometry elements are not invalidated by updating the clip shader
1581*c8dee2aaSAndroid Build Coastguard Worker }
1582*c8dee2aaSAndroid Build Coastguard Worker
replaceClip(const SkIRect & rect)1583*c8dee2aaSAndroid Build Coastguard Worker void ClipStack::replaceClip(const SkIRect& rect) {
1584*c8dee2aaSAndroid Build Coastguard Worker bool wasDeferred;
1585*c8dee2aaSAndroid Build Coastguard Worker SaveRecord& save = this->writableSaveRecord(&wasDeferred);
1586*c8dee2aaSAndroid Build Coastguard Worker
1587*c8dee2aaSAndroid Build Coastguard Worker if (!wasDeferred) {
1588*c8dee2aaSAndroid Build Coastguard Worker save.removeElements(&fElements);
1589*c8dee2aaSAndroid Build Coastguard Worker save.invalidateMasks(fProxyProvider, &fMasks);
1590*c8dee2aaSAndroid Build Coastguard Worker }
1591*c8dee2aaSAndroid Build Coastguard Worker
1592*c8dee2aaSAndroid Build Coastguard Worker save.reset(fDeviceBounds);
1593*c8dee2aaSAndroid Build Coastguard Worker if (rect != fDeviceBounds) {
1594*c8dee2aaSAndroid Build Coastguard Worker this->clipRect(SkMatrix::I(), SkRect::Make(rect), GrAA::kNo, SkClipOp::kIntersect);
1595*c8dee2aaSAndroid Build Coastguard Worker }
1596*c8dee2aaSAndroid Build Coastguard Worker }
1597*c8dee2aaSAndroid Build Coastguard Worker
clip(RawElement && element)1598*c8dee2aaSAndroid Build Coastguard Worker void ClipStack::clip(RawElement&& element) {
1599*c8dee2aaSAndroid Build Coastguard Worker if (this->currentSaveRecord().state() == ClipState::kEmpty) {
1600*c8dee2aaSAndroid Build Coastguard Worker return;
1601*c8dee2aaSAndroid Build Coastguard Worker }
1602*c8dee2aaSAndroid Build Coastguard Worker
1603*c8dee2aaSAndroid Build Coastguard Worker // Reduce the path to anything simpler, will apply the transform if it's a scale+translate
1604*c8dee2aaSAndroid Build Coastguard Worker // and ensures the element's bounds are clipped to the device (NOT the conservative clip bounds,
1605*c8dee2aaSAndroid Build Coastguard Worker // since those are based on the net effect of all elements while device bounds clipping happens
1606*c8dee2aaSAndroid Build Coastguard Worker // implicitly. During addElement, we may still be able to invalidate some older elements).
1607*c8dee2aaSAndroid Build Coastguard Worker element.simplify(fDeviceBounds, fForceAA);
1608*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!element.shape().inverted());
1609*c8dee2aaSAndroid Build Coastguard Worker
1610*c8dee2aaSAndroid Build Coastguard Worker // An empty op means do nothing (for difference), or close the save record, so we try and detect
1611*c8dee2aaSAndroid Build Coastguard Worker // that early before doing additional unnecessary save record allocation.
1612*c8dee2aaSAndroid Build Coastguard Worker if (element.shape().isEmpty()) {
1613*c8dee2aaSAndroid Build Coastguard Worker if (element.op() == SkClipOp::kDifference) {
1614*c8dee2aaSAndroid Build Coastguard Worker // If the shape is empty and we're subtracting, this has no effect on the clip
1615*c8dee2aaSAndroid Build Coastguard Worker return;
1616*c8dee2aaSAndroid Build Coastguard Worker }
1617*c8dee2aaSAndroid Build Coastguard Worker // else we will make the clip empty, but we need a new save record to record that change
1618*c8dee2aaSAndroid Build Coastguard Worker // in the clip state; fall through to below and updateForElement() will handle it.
1619*c8dee2aaSAndroid Build Coastguard Worker }
1620*c8dee2aaSAndroid Build Coastguard Worker
1621*c8dee2aaSAndroid Build Coastguard Worker bool wasDeferred;
1622*c8dee2aaSAndroid Build Coastguard Worker SaveRecord& save = this->writableSaveRecord(&wasDeferred);
1623*c8dee2aaSAndroid Build Coastguard Worker SkDEBUGCODE(uint32_t oldGenID = save.genID();)
1624*c8dee2aaSAndroid Build Coastguard Worker SkDEBUGCODE(int elementCount = fElements.count();)
1625*c8dee2aaSAndroid Build Coastguard Worker if (!save.addElement(std::move(element), &fElements)) {
1626*c8dee2aaSAndroid Build Coastguard Worker if (wasDeferred) {
1627*c8dee2aaSAndroid Build Coastguard Worker // We made a new save record, but ended up not adding an element to the stack.
1628*c8dee2aaSAndroid Build Coastguard Worker // So instead of keeping an empty save record around, pop it off and restore the counter
1629*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(elementCount == fElements.count());
1630*c8dee2aaSAndroid Build Coastguard Worker fSaves.pop_back();
1631*c8dee2aaSAndroid Build Coastguard Worker fSaves.back().pushSave();
1632*c8dee2aaSAndroid Build Coastguard Worker } else {
1633*c8dee2aaSAndroid Build Coastguard Worker // Should not have changed gen ID if the element and save were not modified
1634*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(oldGenID == save.genID());
1635*c8dee2aaSAndroid Build Coastguard Worker }
1636*c8dee2aaSAndroid Build Coastguard Worker } else {
1637*c8dee2aaSAndroid Build Coastguard Worker // The gen ID should be new, and should not be invalid
1638*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(oldGenID != save.genID() && save.genID() != kInvalidGenID);
1639*c8dee2aaSAndroid Build Coastguard Worker if (fProxyProvider && !wasDeferred) {
1640*c8dee2aaSAndroid Build Coastguard Worker // We modified an active save record so any old masks it had can be invalidated
1641*c8dee2aaSAndroid Build Coastguard Worker save.invalidateMasks(fProxyProvider, &fMasks);
1642*c8dee2aaSAndroid Build Coastguard Worker }
1643*c8dee2aaSAndroid Build Coastguard Worker }
1644*c8dee2aaSAndroid Build Coastguard Worker }
1645*c8dee2aaSAndroid Build Coastguard Worker
GetSWMaskFP(GrRecordingContext * context,Mask::Stack * masks,const SaveRecord & current,const SkIRect & bounds,const Element ** elements,int count,std::unique_ptr<GrFragmentProcessor> clipFP)1646*c8dee2aaSAndroid Build Coastguard Worker GrFPResult ClipStack::GetSWMaskFP(GrRecordingContext* context, Mask::Stack* masks,
1647*c8dee2aaSAndroid Build Coastguard Worker const SaveRecord& current, const SkIRect& bounds,
1648*c8dee2aaSAndroid Build Coastguard Worker const Element** elements, int count,
1649*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<GrFragmentProcessor> clipFP) {
1650*c8dee2aaSAndroid Build Coastguard Worker GrProxyProvider* proxyProvider = context->priv().proxyProvider();
1651*c8dee2aaSAndroid Build Coastguard Worker GrSurfaceProxyView maskProxy;
1652*c8dee2aaSAndroid Build Coastguard Worker
1653*c8dee2aaSAndroid Build Coastguard Worker SkIRect maskBounds; // may not be 'bounds' if we reuse a large clip mask
1654*c8dee2aaSAndroid Build Coastguard Worker // Check the existing masks from this save record for compatibility
1655*c8dee2aaSAndroid Build Coastguard Worker for (const Mask& m : masks->ritems()) {
1656*c8dee2aaSAndroid Build Coastguard Worker if (m.genID() != current.genID()) {
1657*c8dee2aaSAndroid Build Coastguard Worker break;
1658*c8dee2aaSAndroid Build Coastguard Worker }
1659*c8dee2aaSAndroid Build Coastguard Worker if (m.appliesToDraw(current, bounds)) {
1660*c8dee2aaSAndroid Build Coastguard Worker maskProxy = proxyProvider->findCachedProxyWithColorTypeFallback(
1661*c8dee2aaSAndroid Build Coastguard Worker m.key(), kMaskOrigin, GrColorType::kAlpha_8, 1);
1662*c8dee2aaSAndroid Build Coastguard Worker if (maskProxy) {
1663*c8dee2aaSAndroid Build Coastguard Worker maskBounds = m.bounds();
1664*c8dee2aaSAndroid Build Coastguard Worker break;
1665*c8dee2aaSAndroid Build Coastguard Worker }
1666*c8dee2aaSAndroid Build Coastguard Worker }
1667*c8dee2aaSAndroid Build Coastguard Worker }
1668*c8dee2aaSAndroid Build Coastguard Worker
1669*c8dee2aaSAndroid Build Coastguard Worker if (!maskProxy) {
1670*c8dee2aaSAndroid Build Coastguard Worker // No existing mask was found, so need to render a new one
1671*c8dee2aaSAndroid Build Coastguard Worker maskProxy = render_sw_mask(context, bounds, elements, count);
1672*c8dee2aaSAndroid Build Coastguard Worker if (!maskProxy) {
1673*c8dee2aaSAndroid Build Coastguard Worker // If we still don't have one, there's nothing we can do
1674*c8dee2aaSAndroid Build Coastguard Worker return GrFPFailure(std::move(clipFP));
1675*c8dee2aaSAndroid Build Coastguard Worker }
1676*c8dee2aaSAndroid Build Coastguard Worker
1677*c8dee2aaSAndroid Build Coastguard Worker // Register the mask for later invalidation
1678*c8dee2aaSAndroid Build Coastguard Worker Mask& mask = masks->emplace_back(current, bounds);
1679*c8dee2aaSAndroid Build Coastguard Worker proxyProvider->assignUniqueKeyToProxy(mask.key(), maskProxy.asTextureProxy());
1680*c8dee2aaSAndroid Build Coastguard Worker maskBounds = bounds;
1681*c8dee2aaSAndroid Build Coastguard Worker }
1682*c8dee2aaSAndroid Build Coastguard Worker
1683*c8dee2aaSAndroid Build Coastguard Worker // Wrap the mask in an FP that samples it for coverage
1684*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(maskProxy && maskProxy.origin() == kMaskOrigin);
1685*c8dee2aaSAndroid Build Coastguard Worker
1686*c8dee2aaSAndroid Build Coastguard Worker GrSamplerState samplerState(GrSamplerState::WrapMode::kClampToBorder,
1687*c8dee2aaSAndroid Build Coastguard Worker GrSamplerState::Filter::kNearest);
1688*c8dee2aaSAndroid Build Coastguard Worker // Maps the device coords passed to the texture effect to the top-left corner of the mask, and
1689*c8dee2aaSAndroid Build Coastguard Worker // make sure that the draw bounds are pre-mapped into the mask's space as well.
1690*c8dee2aaSAndroid Build Coastguard Worker auto m = SkMatrix::Translate(-maskBounds.fLeft, -maskBounds.fTop);
1691*c8dee2aaSAndroid Build Coastguard Worker auto subset = SkRect::Make(bounds);
1692*c8dee2aaSAndroid Build Coastguard Worker subset.offset(-maskBounds.fLeft, -maskBounds.fTop);
1693*c8dee2aaSAndroid Build Coastguard Worker // We scissor to bounds. The mask's texel centers are aligned to device space
1694*c8dee2aaSAndroid Build Coastguard Worker // pixel centers. Hence this domain of texture coordinates.
1695*c8dee2aaSAndroid Build Coastguard Worker auto domain = subset.makeInset(0.5, 0.5);
1696*c8dee2aaSAndroid Build Coastguard Worker auto fp = GrTextureEffect::MakeSubset(std::move(maskProxy), kPremul_SkAlphaType, m,
1697*c8dee2aaSAndroid Build Coastguard Worker samplerState, subset, domain, *context->priv().caps());
1698*c8dee2aaSAndroid Build Coastguard Worker fp = GrFragmentProcessor::DeviceSpace(std::move(fp));
1699*c8dee2aaSAndroid Build Coastguard Worker
1700*c8dee2aaSAndroid Build Coastguard Worker // Must combine the coverage sampled from the texture effect with the previous coverage
1701*c8dee2aaSAndroid Build Coastguard Worker fp = GrBlendFragmentProcessor::Make<SkBlendMode::kDstIn>(std::move(fp), std::move(clipFP));
1702*c8dee2aaSAndroid Build Coastguard Worker return GrFPSuccess(std::move(fp));
1703*c8dee2aaSAndroid Build Coastguard Worker }
1704*c8dee2aaSAndroid Build Coastguard Worker
1705*c8dee2aaSAndroid Build Coastguard Worker } // namespace skgpu::ganesh
1706