xref: /aosp_15_r20/external/skia/src/gpu/ganesh/ops/TessellationPathRenderer.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2019 Google LLC.
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/ops/TessellationPathRenderer.h"
8*c8dee2aaSAndroid Build Coastguard Worker 
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkMatrix.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPath.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRect.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkStrokeRec.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkAssert.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/gpu/ganesh/GrTypesPriv.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkMathPriv.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrCaps.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrClip.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrPaint.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrRenderTargetProxy.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrStyle.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrSurfaceProxy.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrUserStencilSettings.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/SurfaceDrawContext.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/effects/GrDisableColorXP.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/geometry/GrStyledShape.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/ops/FillPathFlags.h"
27*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/ops/GrOp.h"
28*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/ops/PathInnerTriangulateOp.h"
29*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/ops/PathStencilCoverOp.h"
30*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/ops/PathTessellateOp.h"
31*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/ops/StrokeTessellateOp.h"
32*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/tessellate/Tessellation.h"
33*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/tessellate/WangsFormula.h"
34*c8dee2aaSAndroid Build Coastguard Worker 
35*c8dee2aaSAndroid Build Coastguard Worker #include <utility>
36*c8dee2aaSAndroid Build Coastguard Worker 
37*c8dee2aaSAndroid Build Coastguard Worker class GrRecordingContext;
38*c8dee2aaSAndroid Build Coastguard Worker class SkArenaAlloc;
39*c8dee2aaSAndroid Build Coastguard Worker 
40*c8dee2aaSAndroid Build Coastguard Worker namespace {
41*c8dee2aaSAndroid Build Coastguard Worker 
42*c8dee2aaSAndroid Build Coastguard Worker using namespace skgpu::tess;
43*c8dee2aaSAndroid Build Coastguard Worker 
make_non_convex_fill_op(GrRecordingContext * rContext,SkArenaAlloc * arena,skgpu::ganesh::FillPathFlags fillPathFlags,GrAAType aaType,const SkRect & drawBounds,const SkIRect & clipBounds,const SkMatrix & viewMatrix,const SkPath & path,GrPaint && paint)44*c8dee2aaSAndroid Build Coastguard Worker GrOp::Owner make_non_convex_fill_op(GrRecordingContext* rContext,
45*c8dee2aaSAndroid Build Coastguard Worker                                     SkArenaAlloc* arena,
46*c8dee2aaSAndroid Build Coastguard Worker                                     skgpu::ganesh::FillPathFlags fillPathFlags,
47*c8dee2aaSAndroid Build Coastguard Worker                                     GrAAType aaType,
48*c8dee2aaSAndroid Build Coastguard Worker                                     const SkRect& drawBounds,
49*c8dee2aaSAndroid Build Coastguard Worker                                     const SkIRect& clipBounds,
50*c8dee2aaSAndroid Build Coastguard Worker                                     const SkMatrix& viewMatrix,
51*c8dee2aaSAndroid Build Coastguard Worker                                     const SkPath& path,
52*c8dee2aaSAndroid Build Coastguard Worker                                     GrPaint&& paint) {
53*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!path.isConvex() || path.isInverseFillType());
54*c8dee2aaSAndroid Build Coastguard Worker #if !defined(SK_ENABLE_OPTIMIZE_SIZE)
55*c8dee2aaSAndroid Build Coastguard Worker     int numVerbs = path.countVerbs();
56*c8dee2aaSAndroid Build Coastguard Worker     if (numVerbs > 0 && !path.isInverseFillType()) {
57*c8dee2aaSAndroid Build Coastguard Worker         // Check if the path is large and/or simple enough that we can triangulate the inner fan
58*c8dee2aaSAndroid Build Coastguard Worker         // on the CPU. This is our fastest approach. It allows us to stencil only the curves,
59*c8dee2aaSAndroid Build Coastguard Worker         // and then fill the inner fan directly to the final render target, thus drawing the
60*c8dee2aaSAndroid Build Coastguard Worker         // majority of pixels in a single render pass.
61*c8dee2aaSAndroid Build Coastguard Worker         SkRect clippedDrawBounds = SkRect::Make(clipBounds);
62*c8dee2aaSAndroid Build Coastguard Worker         if (clippedDrawBounds.intersect(drawBounds)) {
63*c8dee2aaSAndroid Build Coastguard Worker             float gpuFragmentWork = clippedDrawBounds.height() * clippedDrawBounds.width();
64*c8dee2aaSAndroid Build Coastguard Worker             float cpuTessellationWork = numVerbs * SkNextLog2(numVerbs);  // N log N.
65*c8dee2aaSAndroid Build Coastguard Worker             constexpr static float kCpuWeight = 512;
66*c8dee2aaSAndroid Build Coastguard Worker             constexpr static float kMinNumPixelsToTriangulate = 256 * 256;
67*c8dee2aaSAndroid Build Coastguard Worker             if (cpuTessellationWork * kCpuWeight + kMinNumPixelsToTriangulate < gpuFragmentWork) {
68*c8dee2aaSAndroid Build Coastguard Worker                 return GrOp::Make<skgpu::ganesh::PathInnerTriangulateOp>(rContext,
69*c8dee2aaSAndroid Build Coastguard Worker                                                                          viewMatrix,
70*c8dee2aaSAndroid Build Coastguard Worker                                                                          path,
71*c8dee2aaSAndroid Build Coastguard Worker                                                                          std::move(paint),
72*c8dee2aaSAndroid Build Coastguard Worker                                                                          aaType,
73*c8dee2aaSAndroid Build Coastguard Worker                                                                          fillPathFlags,
74*c8dee2aaSAndroid Build Coastguard Worker                                                                          drawBounds);
75*c8dee2aaSAndroid Build Coastguard Worker             }
76*c8dee2aaSAndroid Build Coastguard Worker         } // we should be clipped out when the GrClip is analyzed, so just return the default op
77*c8dee2aaSAndroid Build Coastguard Worker     }
78*c8dee2aaSAndroid Build Coastguard Worker #endif
79*c8dee2aaSAndroid Build Coastguard Worker 
80*c8dee2aaSAndroid Build Coastguard Worker     return GrOp::Make<skgpu::ganesh::PathStencilCoverOp>(
81*c8dee2aaSAndroid Build Coastguard Worker             rContext, arena, viewMatrix, path, std::move(paint), aaType, fillPathFlags, drawBounds);
82*c8dee2aaSAndroid Build Coastguard Worker }
83*c8dee2aaSAndroid Build Coastguard Worker 
84*c8dee2aaSAndroid Build Coastguard Worker } // anonymous namespace
85*c8dee2aaSAndroid Build Coastguard Worker 
86*c8dee2aaSAndroid Build Coastguard Worker namespace skgpu::ganesh {
87*c8dee2aaSAndroid Build Coastguard Worker 
88*c8dee2aaSAndroid Build Coastguard Worker namespace {
89*c8dee2aaSAndroid Build Coastguard Worker 
90*c8dee2aaSAndroid Build Coastguard Worker // `chopped_path` may be null, in which case no chopping actually happens. Returns true on success,
91*c8dee2aaSAndroid Build Coastguard Worker // false on failure (chopping not allowed).
ChopPathIfNecessary(const SkMatrix & viewMatrix,const GrStyledShape & shape,const SkIRect & clipConservativeBounds,const SkStrokeRec & stroke,SkPath * chopped_path)92*c8dee2aaSAndroid Build Coastguard Worker bool ChopPathIfNecessary(const SkMatrix& viewMatrix,
93*c8dee2aaSAndroid Build Coastguard Worker                          const GrStyledShape& shape,
94*c8dee2aaSAndroid Build Coastguard Worker                          const SkIRect& clipConservativeBounds,
95*c8dee2aaSAndroid Build Coastguard Worker                          const SkStrokeRec& stroke,
96*c8dee2aaSAndroid Build Coastguard Worker                          SkPath* chopped_path) {
97*c8dee2aaSAndroid Build Coastguard Worker     const SkRect pathDevBounds = viewMatrix.mapRect(shape.bounds());
98*c8dee2aaSAndroid Build Coastguard Worker     float n4 = wangs_formula::worst_case_cubic_p4(tess::kPrecision,
99*c8dee2aaSAndroid Build Coastguard Worker                                                   pathDevBounds.width(),
100*c8dee2aaSAndroid Build Coastguard Worker                                                   pathDevBounds.height());
101*c8dee2aaSAndroid Build Coastguard Worker     if (n4 > tess::kMaxSegmentsPerCurve_p4 && shape.segmentMask() != SkPath::kLine_SegmentMask) {
102*c8dee2aaSAndroid Build Coastguard Worker         // The path is extremely large. Pre-chop its curves to keep the number of tessellation
103*c8dee2aaSAndroid Build Coastguard Worker         // segments tractable. This will also flatten curves that fall completely outside the
104*c8dee2aaSAndroid Build Coastguard Worker         // viewport.
105*c8dee2aaSAndroid Build Coastguard Worker         SkRect viewport = SkRect::Make(clipConservativeBounds);
106*c8dee2aaSAndroid Build Coastguard Worker         if (!shape.style().isSimpleFill()) {
107*c8dee2aaSAndroid Build Coastguard Worker             // Outset the viewport to pad for the stroke width.
108*c8dee2aaSAndroid Build Coastguard Worker             float inflationRadius;
109*c8dee2aaSAndroid Build Coastguard Worker             if (stroke.isHairlineStyle()) {
110*c8dee2aaSAndroid Build Coastguard Worker                 // SkStrokeRec::getInflationRadius() doesn't handle hairlines robustly. Instead
111*c8dee2aaSAndroid Build Coastguard Worker                 // find the inflation of an equivalent stroke in device space with a width of 1.
112*c8dee2aaSAndroid Build Coastguard Worker                 inflationRadius = SkStrokeRec::GetInflationRadius(stroke.getJoin(),
113*c8dee2aaSAndroid Build Coastguard Worker                                                                   stroke.getMiter(),
114*c8dee2aaSAndroid Build Coastguard Worker                                                                   stroke.getCap(), 1);
115*c8dee2aaSAndroid Build Coastguard Worker             } else {
116*c8dee2aaSAndroid Build Coastguard Worker                 inflationRadius = stroke.getInflationRadius() * viewMatrix.getMaxScale();
117*c8dee2aaSAndroid Build Coastguard Worker             }
118*c8dee2aaSAndroid Build Coastguard Worker             viewport.outset(inflationRadius, inflationRadius);
119*c8dee2aaSAndroid Build Coastguard Worker         }
120*c8dee2aaSAndroid Build Coastguard Worker         if (wangs_formula::worst_case_cubic(
121*c8dee2aaSAndroid Build Coastguard Worker                      tess::kPrecision,
122*c8dee2aaSAndroid Build Coastguard Worker                      viewport.width(),
123*c8dee2aaSAndroid Build Coastguard Worker                      viewport.height()) > kMaxSegmentsPerCurve) {
124*c8dee2aaSAndroid Build Coastguard Worker             return false;
125*c8dee2aaSAndroid Build Coastguard Worker         }
126*c8dee2aaSAndroid Build Coastguard Worker         if (chopped_path) {
127*c8dee2aaSAndroid Build Coastguard Worker             *chopped_path = PreChopPathCurves(tess::kPrecision, *chopped_path, viewMatrix,
128*c8dee2aaSAndroid Build Coastguard Worker                                               viewport);
129*c8dee2aaSAndroid Build Coastguard Worker         }
130*c8dee2aaSAndroid Build Coastguard Worker     }
131*c8dee2aaSAndroid Build Coastguard Worker     return true;
132*c8dee2aaSAndroid Build Coastguard Worker }
133*c8dee2aaSAndroid Build Coastguard Worker 
134*c8dee2aaSAndroid Build Coastguard Worker } // anonymous namespace
135*c8dee2aaSAndroid Build Coastguard Worker 
IsSupported(const GrCaps & caps)136*c8dee2aaSAndroid Build Coastguard Worker bool TessellationPathRenderer::IsSupported(const GrCaps& caps) {
137*c8dee2aaSAndroid Build Coastguard Worker     return !caps.avoidStencilBuffers() &&
138*c8dee2aaSAndroid Build Coastguard Worker            caps.drawInstancedSupport() &&
139*c8dee2aaSAndroid Build Coastguard Worker            !caps.disableTessellationPathRenderer();
140*c8dee2aaSAndroid Build Coastguard Worker }
141*c8dee2aaSAndroid Build Coastguard Worker 
onGetStencilSupport(const GrStyledShape & shape) const142*c8dee2aaSAndroid Build Coastguard Worker PathRenderer::StencilSupport TessellationPathRenderer::onGetStencilSupport(
143*c8dee2aaSAndroid Build Coastguard Worker         const GrStyledShape& shape) const {
144*c8dee2aaSAndroid Build Coastguard Worker     if (!shape.style().isSimpleFill() || shape.inverseFilled()) {
145*c8dee2aaSAndroid Build Coastguard Worker         // Don't bother with stroke stencilling or inverse fills yet. The Skia API doesn't support
146*c8dee2aaSAndroid Build Coastguard Worker         // clipping by a stroke, and the stencilling code already knows how to invert a fill.
147*c8dee2aaSAndroid Build Coastguard Worker         return kNoSupport_StencilSupport;
148*c8dee2aaSAndroid Build Coastguard Worker     }
149*c8dee2aaSAndroid Build Coastguard Worker     return shape.knownToBeConvex() ? kNoRestriction_StencilSupport : kStencilOnly_StencilSupport;
150*c8dee2aaSAndroid Build Coastguard Worker }
151*c8dee2aaSAndroid Build Coastguard Worker 
onCanDrawPath(const CanDrawPathArgs & args) const152*c8dee2aaSAndroid Build Coastguard Worker PathRenderer::CanDrawPath TessellationPathRenderer::onCanDrawPath(
153*c8dee2aaSAndroid Build Coastguard Worker         const CanDrawPathArgs& args) const {
154*c8dee2aaSAndroid Build Coastguard Worker     const GrStyledShape& shape = *args.fShape;
155*c8dee2aaSAndroid Build Coastguard Worker     if (args.fAAType == GrAAType::kCoverage ||
156*c8dee2aaSAndroid Build Coastguard Worker         shape.style().hasPathEffect() ||
157*c8dee2aaSAndroid Build Coastguard Worker         args.fViewMatrix->hasPerspective() ||
158*c8dee2aaSAndroid Build Coastguard Worker         shape.style().strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style ||
159*c8dee2aaSAndroid Build Coastguard Worker         !args.fProxy->canUseStencil(*args.fCaps)) {
160*c8dee2aaSAndroid Build Coastguard Worker         return CanDrawPath::kNo;
161*c8dee2aaSAndroid Build Coastguard Worker     }
162*c8dee2aaSAndroid Build Coastguard Worker     if (!shape.style().isSimpleFill()) {
163*c8dee2aaSAndroid Build Coastguard Worker         if (shape.inverseFilled()) {
164*c8dee2aaSAndroid Build Coastguard Worker             return CanDrawPath::kNo;
165*c8dee2aaSAndroid Build Coastguard Worker         }
166*c8dee2aaSAndroid Build Coastguard Worker         if (shape.style().strokeRec().getWidth() * args.fViewMatrix->getMaxScale() > 10000) {
167*c8dee2aaSAndroid Build Coastguard Worker             // crbug.com/1266446 -- Don't draw massively wide strokes with the tessellator. Since we
168*c8dee2aaSAndroid Build Coastguard Worker             // outset the viewport by stroke width for pre-chopping, astronomically wide strokes can
169*c8dee2aaSAndroid Build Coastguard Worker             // result in an astronomical viewport size, and therefore an exponential explosion chops
170*c8dee2aaSAndroid Build Coastguard Worker             // and memory usage. It is also simply inefficient to tessellate these strokes due to
171*c8dee2aaSAndroid Build Coastguard Worker             // the number of radial edges required. We're better off just converting them to a path
172*c8dee2aaSAndroid Build Coastguard Worker             // after a certain point.
173*c8dee2aaSAndroid Build Coastguard Worker             return CanDrawPath::kNo;
174*c8dee2aaSAndroid Build Coastguard Worker         }
175*c8dee2aaSAndroid Build Coastguard Worker     }
176*c8dee2aaSAndroid Build Coastguard Worker     if (args.fHasUserStencilSettings) {
177*c8dee2aaSAndroid Build Coastguard Worker         // Non-convex paths and strokes use the stencil buffer internally, so they can't support
178*c8dee2aaSAndroid Build Coastguard Worker         // draws with stencil settings.
179*c8dee2aaSAndroid Build Coastguard Worker         if (!shape.style().isSimpleFill() || !shape.knownToBeConvex() || shape.inverseFilled()) {
180*c8dee2aaSAndroid Build Coastguard Worker             return CanDrawPath::kNo;
181*c8dee2aaSAndroid Build Coastguard Worker         }
182*c8dee2aaSAndroid Build Coastguard Worker     }
183*c8dee2aaSAndroid Build Coastguard Worker 
184*c8dee2aaSAndroid Build Coastguard Worker     // By passing in null for the chopped-path no chopping happens. Rather this returns whether
185*c8dee2aaSAndroid Build Coastguard Worker     // chopping is possible.
186*c8dee2aaSAndroid Build Coastguard Worker     if (!ChopPathIfNecessary(*args.fViewMatrix, shape, *args.fClipConservativeBounds,
187*c8dee2aaSAndroid Build Coastguard Worker                              shape.style().strokeRec(), nullptr)) {
188*c8dee2aaSAndroid Build Coastguard Worker         return CanDrawPath::kNo;
189*c8dee2aaSAndroid Build Coastguard Worker     }
190*c8dee2aaSAndroid Build Coastguard Worker 
191*c8dee2aaSAndroid Build Coastguard Worker     return CanDrawPath::kYes;
192*c8dee2aaSAndroid Build Coastguard Worker }
193*c8dee2aaSAndroid Build Coastguard Worker 
onDrawPath(const DrawPathArgs & args)194*c8dee2aaSAndroid Build Coastguard Worker bool TessellationPathRenderer::onDrawPath(const DrawPathArgs& args) {
195*c8dee2aaSAndroid Build Coastguard Worker     auto sdc = args.fSurfaceDrawContext;
196*c8dee2aaSAndroid Build Coastguard Worker 
197*c8dee2aaSAndroid Build Coastguard Worker     SkPath path;
198*c8dee2aaSAndroid Build Coastguard Worker     args.fShape->asPath(&path);
199*c8dee2aaSAndroid Build Coastguard Worker 
200*c8dee2aaSAndroid Build Coastguard Worker     // onDrawPath() should only be called if ChopPathIfNecessary() succeeded.
201*c8dee2aaSAndroid Build Coastguard Worker     SkAssertResult(ChopPathIfNecessary(*args.fViewMatrix, *args.fShape,
202*c8dee2aaSAndroid Build Coastguard Worker                                        *args.fClipConservativeBounds,
203*c8dee2aaSAndroid Build Coastguard Worker                                        args.fShape->style().strokeRec(), &path));
204*c8dee2aaSAndroid Build Coastguard Worker 
205*c8dee2aaSAndroid Build Coastguard Worker     // Handle strokes first.
206*c8dee2aaSAndroid Build Coastguard Worker     if (!args.fShape->style().isSimpleFill()) {
207*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(!path.isInverseFillType());  // See onGetStencilSupport().
208*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(args.fUserStencilSettings->isUnused());
209*c8dee2aaSAndroid Build Coastguard Worker         const SkStrokeRec& stroke = args.fShape->style().strokeRec();
210*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(stroke.getStyle() != SkStrokeRec::kStrokeAndFill_Style);
211*c8dee2aaSAndroid Build Coastguard Worker         auto op = GrOp::Make<StrokeTessellateOp>(args.fContext, args.fAAType, *args.fViewMatrix,
212*c8dee2aaSAndroid Build Coastguard Worker                                                  path, stroke, std::move(args.fPaint));
213*c8dee2aaSAndroid Build Coastguard Worker         sdc->addDrawOp(args.fClip, std::move(op));
214*c8dee2aaSAndroid Build Coastguard Worker         return true;
215*c8dee2aaSAndroid Build Coastguard Worker     }
216*c8dee2aaSAndroid Build Coastguard Worker 
217*c8dee2aaSAndroid Build Coastguard Worker     // Handle empty paths.
218*c8dee2aaSAndroid Build Coastguard Worker     const SkRect pathDevBounds = args.fViewMatrix->mapRect(args.fShape->bounds());
219*c8dee2aaSAndroid Build Coastguard Worker     if (pathDevBounds.isEmpty()) {
220*c8dee2aaSAndroid Build Coastguard Worker         if (path.isInverseFillType()) {
221*c8dee2aaSAndroid Build Coastguard Worker             args.fSurfaceDrawContext->drawPaint(args.fClip, std::move(args.fPaint),
222*c8dee2aaSAndroid Build Coastguard Worker                                                 *args.fViewMatrix);
223*c8dee2aaSAndroid Build Coastguard Worker         }
224*c8dee2aaSAndroid Build Coastguard Worker         return true;
225*c8dee2aaSAndroid Build Coastguard Worker     }
226*c8dee2aaSAndroid Build Coastguard Worker 
227*c8dee2aaSAndroid Build Coastguard Worker     // Handle convex paths. Make sure to check 'path' for convexity since it may have been
228*c8dee2aaSAndroid Build Coastguard Worker     // pre-chopped, not 'fShape'.
229*c8dee2aaSAndroid Build Coastguard Worker     if (path.isConvex() && !path.isInverseFillType()) {
230*c8dee2aaSAndroid Build Coastguard Worker         auto op = GrOp::Make<PathTessellateOp>(args.fContext,
231*c8dee2aaSAndroid Build Coastguard Worker                                                args.fSurfaceDrawContext->arenaAlloc(),
232*c8dee2aaSAndroid Build Coastguard Worker                                                args.fAAType,
233*c8dee2aaSAndroid Build Coastguard Worker                                                args.fUserStencilSettings,
234*c8dee2aaSAndroid Build Coastguard Worker                                                *args.fViewMatrix,
235*c8dee2aaSAndroid Build Coastguard Worker                                                path,
236*c8dee2aaSAndroid Build Coastguard Worker                                                std::move(args.fPaint),
237*c8dee2aaSAndroid Build Coastguard Worker                                                pathDevBounds);
238*c8dee2aaSAndroid Build Coastguard Worker         sdc->addDrawOp(args.fClip, std::move(op));
239*c8dee2aaSAndroid Build Coastguard Worker         return true;
240*c8dee2aaSAndroid Build Coastguard Worker     }
241*c8dee2aaSAndroid Build Coastguard Worker 
242*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(args.fUserStencilSettings->isUnused());  // See onGetStencilSupport().
243*c8dee2aaSAndroid Build Coastguard Worker     const SkRect& drawBounds = path.isInverseFillType()
244*c8dee2aaSAndroid Build Coastguard Worker             ? args.fSurfaceDrawContext->asSurfaceProxy()->backingStoreBoundsRect()
245*c8dee2aaSAndroid Build Coastguard Worker             : pathDevBounds;
246*c8dee2aaSAndroid Build Coastguard Worker     auto op = make_non_convex_fill_op(args.fContext,
247*c8dee2aaSAndroid Build Coastguard Worker                                       args.fSurfaceDrawContext->arenaAlloc(),
248*c8dee2aaSAndroid Build Coastguard Worker                                       FillPathFlags::kNone,
249*c8dee2aaSAndroid Build Coastguard Worker                                       args.fAAType,
250*c8dee2aaSAndroid Build Coastguard Worker                                       drawBounds,
251*c8dee2aaSAndroid Build Coastguard Worker                                       *args.fClipConservativeBounds,
252*c8dee2aaSAndroid Build Coastguard Worker                                       *args.fViewMatrix,
253*c8dee2aaSAndroid Build Coastguard Worker                                       path,
254*c8dee2aaSAndroid Build Coastguard Worker                                       std::move(args.fPaint));
255*c8dee2aaSAndroid Build Coastguard Worker     sdc->addDrawOp(args.fClip, std::move(op));
256*c8dee2aaSAndroid Build Coastguard Worker     return true;
257*c8dee2aaSAndroid Build Coastguard Worker }
258*c8dee2aaSAndroid Build Coastguard Worker 
onStencilPath(const StencilPathArgs & args)259*c8dee2aaSAndroid Build Coastguard Worker void TessellationPathRenderer::onStencilPath(const StencilPathArgs& args) {
260*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(args.fShape->style().isSimpleFill());  // See onGetStencilSupport().
261*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!args.fShape->inverseFilled());  // See onGetStencilSupport().
262*c8dee2aaSAndroid Build Coastguard Worker 
263*c8dee2aaSAndroid Build Coastguard Worker     auto sdc = args.fSurfaceDrawContext;
264*c8dee2aaSAndroid Build Coastguard Worker     GrAAType aaType = (GrAA::kYes == args.fDoStencilMSAA) ? GrAAType::kMSAA : GrAAType::kNone;
265*c8dee2aaSAndroid Build Coastguard Worker 
266*c8dee2aaSAndroid Build Coastguard Worker     SkRect pathDevBounds;
267*c8dee2aaSAndroid Build Coastguard Worker     args.fViewMatrix->mapRect(&pathDevBounds, args.fShape->bounds());
268*c8dee2aaSAndroid Build Coastguard Worker 
269*c8dee2aaSAndroid Build Coastguard Worker     SkPath path;
270*c8dee2aaSAndroid Build Coastguard Worker     args.fShape->asPath(&path);
271*c8dee2aaSAndroid Build Coastguard Worker 
272*c8dee2aaSAndroid Build Coastguard Worker     float n4 = wangs_formula::worst_case_cubic_p4(tess::kPrecision,
273*c8dee2aaSAndroid Build Coastguard Worker                                                   pathDevBounds.width(),
274*c8dee2aaSAndroid Build Coastguard Worker                                                   pathDevBounds.height());
275*c8dee2aaSAndroid Build Coastguard Worker     if (n4 > tess::kMaxSegmentsPerCurve_p4) {
276*c8dee2aaSAndroid Build Coastguard Worker         SkRect viewport = SkRect::Make(*args.fClipConservativeBounds);
277*c8dee2aaSAndroid Build Coastguard Worker         path = PreChopPathCurves(tess::kPrecision, path, *args.fViewMatrix, viewport);
278*c8dee2aaSAndroid Build Coastguard Worker     }
279*c8dee2aaSAndroid Build Coastguard Worker 
280*c8dee2aaSAndroid Build Coastguard Worker     // Make sure to check 'path' for convexity since it may have been pre-chopped, not 'fShape'.
281*c8dee2aaSAndroid Build Coastguard Worker     if (path.isConvex()) {
282*c8dee2aaSAndroid Build Coastguard Worker         constexpr static GrUserStencilSettings kMarkStencil(
283*c8dee2aaSAndroid Build Coastguard Worker             GrUserStencilSettings::StaticInit<
284*c8dee2aaSAndroid Build Coastguard Worker                 0x0001,
285*c8dee2aaSAndroid Build Coastguard Worker                 GrUserStencilTest::kAlways,
286*c8dee2aaSAndroid Build Coastguard Worker                 0xffff,
287*c8dee2aaSAndroid Build Coastguard Worker                 GrUserStencilOp::kReplace,
288*c8dee2aaSAndroid Build Coastguard Worker                 GrUserStencilOp::kKeep,
289*c8dee2aaSAndroid Build Coastguard Worker                 0xffff>());
290*c8dee2aaSAndroid Build Coastguard Worker 
291*c8dee2aaSAndroid Build Coastguard Worker         GrPaint stencilPaint;
292*c8dee2aaSAndroid Build Coastguard Worker         stencilPaint.setXPFactory(GrDisableColorXPFactory::Get());
293*c8dee2aaSAndroid Build Coastguard Worker         auto op = GrOp::Make<PathTessellateOp>(args.fContext,
294*c8dee2aaSAndroid Build Coastguard Worker                                                args.fSurfaceDrawContext->arenaAlloc(),
295*c8dee2aaSAndroid Build Coastguard Worker                                                aaType,
296*c8dee2aaSAndroid Build Coastguard Worker                                                &kMarkStencil,
297*c8dee2aaSAndroid Build Coastguard Worker                                                *args.fViewMatrix,
298*c8dee2aaSAndroid Build Coastguard Worker                                                path,
299*c8dee2aaSAndroid Build Coastguard Worker                                                std::move(stencilPaint),
300*c8dee2aaSAndroid Build Coastguard Worker                                                pathDevBounds);
301*c8dee2aaSAndroid Build Coastguard Worker         sdc->addDrawOp(args.fClip, std::move(op));
302*c8dee2aaSAndroid Build Coastguard Worker         return;
303*c8dee2aaSAndroid Build Coastguard Worker     }
304*c8dee2aaSAndroid Build Coastguard Worker 
305*c8dee2aaSAndroid Build Coastguard Worker     auto op = make_non_convex_fill_op(args.fContext,
306*c8dee2aaSAndroid Build Coastguard Worker                                       args.fSurfaceDrawContext->arenaAlloc(),
307*c8dee2aaSAndroid Build Coastguard Worker                                       FillPathFlags::kStencilOnly,
308*c8dee2aaSAndroid Build Coastguard Worker                                       aaType,
309*c8dee2aaSAndroid Build Coastguard Worker                                       pathDevBounds,
310*c8dee2aaSAndroid Build Coastguard Worker                                       *args.fClipConservativeBounds,
311*c8dee2aaSAndroid Build Coastguard Worker                                       *args.fViewMatrix,
312*c8dee2aaSAndroid Build Coastguard Worker                                       path,
313*c8dee2aaSAndroid Build Coastguard Worker                                       GrPaint());
314*c8dee2aaSAndroid Build Coastguard Worker     sdc->addDrawOp(args.fClip, std::move(op));
315*c8dee2aaSAndroid Build Coastguard Worker }
316*c8dee2aaSAndroid Build Coastguard Worker 
317*c8dee2aaSAndroid Build Coastguard Worker }  // namespace skgpu::ganesh
318