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 = ¤t;) 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() || ¤t == 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