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