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