• 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 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 SkMatrix& viewMatrix,
34                                     const SkPath& path,
35                                     GrPaint&& paint) {
36     SkASSERT(!path.isConvex() || path.isInverseFillType());
37     int numVerbs = path.countVerbs();
38     if (numVerbs > 0 && !path.isInverseFillType()) {
39         // Check if the path is large and/or simple enough that we can triangulate the inner fan
40         // on the CPU. This is our fastest approach. It allows us to stencil only the curves,
41         // and then fill the inner fan directly to the final render target, thus drawing the
42         // majority of pixels in a single render pass.
43         float gpuFragmentWork = drawBounds.height() * drawBounds.width();
44         float cpuTessellationWork = numVerbs * SkNextLog2(numVerbs);  // N log N.
45         constexpr static float kCpuWeight = 512;
46         constexpr static float kMinNumPixelsToTriangulate = 256 * 256;
47         if (cpuTessellationWork * kCpuWeight + kMinNumPixelsToTriangulate < gpuFragmentWork) {
48             return GrOp::Make<skgpu::v1::PathInnerTriangulateOp>(rContext,
49                                                                  viewMatrix,
50                                                                  path,
51                                                                  std::move(paint),
52                                                                  aaType,
53                                                                  fillPathFlags,
54                                                                  drawBounds);
55         }
56     }
57     return GrOp::Make<skgpu::v1::PathStencilCoverOp>(rContext,
58                                                      arena,
59                                                      viewMatrix,
60                                                      path,
61                                                      std::move(paint),
62                                                      aaType,
63                                                      fillPathFlags,
64                                                      drawBounds);
65 }
66 
67 } // anonymous namespace
68 
69 namespace skgpu::v1 {
70 
IsSupported(const GrCaps & caps)71 bool TessellationPathRenderer::IsSupported(const GrCaps& caps) {
72     return !caps.avoidStencilBuffers() &&
73            caps.drawInstancedSupport() &&
74            !caps.disableTessellationPathRenderer();
75 }
76 
onGetStencilSupport(const GrStyledShape & shape) const77 PathRenderer::StencilSupport TessellationPathRenderer::onGetStencilSupport(
78         const GrStyledShape& shape) const {
79     if (!shape.style().isSimpleFill() || shape.inverseFilled()) {
80         // Don't bother with stroke stencilling or inverse fills yet. The Skia API doesn't support
81         // clipping by a stroke, and the stencilling code already knows how to invert a fill.
82         return kNoSupport_StencilSupport;
83     }
84     return shape.knownToBeConvex() ? kNoRestriction_StencilSupport : kStencilOnly_StencilSupport;
85 }
86 
onCanDrawPath(const CanDrawPathArgs & args) const87 PathRenderer::CanDrawPath TessellationPathRenderer::onCanDrawPath(
88         const CanDrawPathArgs& args) const {
89     const GrStyledShape& shape = *args.fShape;
90     if (args.fAAType == GrAAType::kCoverage ||
91         shape.style().hasPathEffect() ||
92         args.fViewMatrix->hasPerspective() ||
93         shape.style().strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style ||
94         !args.fProxy->canUseStencil(*args.fCaps)) {
95         return CanDrawPath::kNo;
96     }
97     if (!shape.style().isSimpleFill()) {
98         if (shape.inverseFilled()) {
99             return CanDrawPath::kNo;
100         }
101     }
102     if (args.fHasUserStencilSettings) {
103         // Non-convex paths and strokes use the stencil buffer internally, so they can't support
104         // draws with stencil settings.
105         if (!shape.style().isSimpleFill() || !shape.knownToBeConvex() || shape.inverseFilled()) {
106             return CanDrawPath::kNo;
107         }
108     }
109     return CanDrawPath::kYes;
110 }
111 
onDrawPath(const DrawPathArgs & args)112 bool TessellationPathRenderer::onDrawPath(const DrawPathArgs& args) {
113     auto sdc = args.fSurfaceDrawContext;
114 
115     SkPath path;
116     args.fShape->asPath(&path);
117 
118     const SkRect pathDevBounds = args.fViewMatrix->mapRect(args.fShape->bounds());
119     float n = wangs_formula::worst_case_cubic_pow4(kTessellationPrecision,
120                                                    pathDevBounds.width(),
121                                                    pathDevBounds.height());
122     if (n > pow4(kMaxTessellationSegmentsPerCurve)) {
123         // The path is extremely large. Pre-chop its curves to keep the number of tessellation
124         // segments tractable. This will also flatten curves that fall completely outside the
125         // viewport.
126         SkRect viewport = SkRect::Make(*args.fClipConservativeBounds);
127         if (!args.fShape->style().isSimpleFill()) {
128             // Outset the viewport to pad for the stroke width.
129             const SkStrokeRec& stroke = args.fShape->style().strokeRec();
130             float inflationRadius;
131             if (stroke.isHairlineStyle()) {
132                 // SkStrokeRec::getInflationRadius() doesn't handle hairlines robustly. Instead
133                 // find the inflation of an equivalent stroke in device space with a width of 1.
134                 inflationRadius = SkStrokeRec::GetInflationRadius(stroke.getJoin(),
135                                                                   stroke.getMiter(),
136                                                                   stroke.getCap(), 1);
137             } else {
138                 inflationRadius = stroke.getInflationRadius() * args.fViewMatrix->getMaxScale();
139             }
140             viewport.outset(inflationRadius, inflationRadius);
141         }
142         path = PreChopPathCurves(path, *args.fViewMatrix, viewport);
143     }
144 
145     // Handle strokes first.
146     if (!args.fShape->style().isSimpleFill()) {
147         SkASSERT(!path.isInverseFillType());  // See onGetStencilSupport().
148         SkASSERT(args.fUserStencilSettings->isUnused());
149         const SkStrokeRec& stroke = args.fShape->style().strokeRec();
150         SkASSERT(stroke.getStyle() != SkStrokeRec::kStrokeAndFill_Style);
151         auto op = GrOp::Make<StrokeTessellateOp>(args.fContext, args.fAAType, *args.fViewMatrix,
152                                                  path, stroke, std::move(args.fPaint));
153         sdc->addDrawOp(args.fClip, std::move(op));
154         return true;
155     }
156 
157     // Handle empty paths.
158     if (pathDevBounds.isEmpty()) {
159         if (path.isInverseFillType()) {
160             args.fSurfaceDrawContext->drawPaint(args.fClip, std::move(args.fPaint),
161                                                 *args.fViewMatrix);
162         }
163         return true;
164     }
165 
166     // Handle convex paths.
167     if (args.fShape->knownToBeConvex() && !path.isInverseFillType()) {
168         auto op = GrOp::Make<PathTessellateOp>(args.fContext,
169                                                args.fSurfaceDrawContext->arenaAlloc(),
170                                                args.fAAType,
171                                                args.fUserStencilSettings,
172                                                *args.fViewMatrix,
173                                                path,
174                                                std::move(args.fPaint),
175                                                pathDevBounds);
176         sdc->addDrawOp(args.fClip, std::move(op));
177         return true;
178     }
179 
180     SkASSERT(args.fUserStencilSettings->isUnused());  // See onGetStencilSupport().
181     const SkRect& drawBounds = path.isInverseFillType()
182             ? args.fSurfaceDrawContext->asSurfaceProxy()->backingStoreBoundsRect()
183             : pathDevBounds;
184     auto op = make_non_convex_fill_op(args.fContext,
185                                       args.fSurfaceDrawContext->arenaAlloc(),
186                                       FillPathFlags::kNone,
187                                       args.fAAType,
188                                       drawBounds,
189                                       *args.fViewMatrix,
190                                       path,
191                                       std::move(args.fPaint));
192     sdc->addDrawOp(args.fClip, std::move(op));
193     return true;
194 }
195 
onStencilPath(const StencilPathArgs & args)196 void TessellationPathRenderer::onStencilPath(const StencilPathArgs& args) {
197     SkASSERT(args.fShape->style().isSimpleFill());  // See onGetStencilSupport().
198     SkASSERT(!args.fShape->inverseFilled());  // See onGetStencilSupport().
199 
200     auto sdc = args.fSurfaceDrawContext;
201     GrAAType aaType = (GrAA::kYes == args.fDoStencilMSAA) ? GrAAType::kMSAA : GrAAType::kNone;
202 
203     SkRect pathDevBounds;
204     args.fViewMatrix->mapRect(&pathDevBounds, args.fShape->bounds());
205 
206     SkPath path;
207     args.fShape->asPath(&path);
208 
209     float n = wangs_formula::worst_case_cubic_pow4(kTessellationPrecision,
210                                                    pathDevBounds.width(),
211                                                    pathDevBounds.height());
212     if (n > pow4(kMaxTessellationSegmentsPerCurve)) {
213         SkRect viewport = SkRect::Make(*args.fClipConservativeBounds);
214         path = PreChopPathCurves(path, *args.fViewMatrix, viewport);
215     }
216 
217     if (args.fShape->knownToBeConvex()) {
218         constexpr static GrUserStencilSettings kMarkStencil(
219             GrUserStencilSettings::StaticInit<
220                 0x0001,
221                 GrUserStencilTest::kAlways,
222                 0xffff,
223                 GrUserStencilOp::kReplace,
224                 GrUserStencilOp::kKeep,
225                 0xffff>());
226 
227         GrPaint stencilPaint;
228         stencilPaint.setXPFactory(GrDisableColorXPFactory::Get());
229         auto op = GrOp::Make<PathTessellateOp>(args.fContext,
230                                                args.fSurfaceDrawContext->arenaAlloc(),
231                                                aaType,
232                                                &kMarkStencil,
233                                                *args.fViewMatrix,
234                                                path,
235                                                std::move(stencilPaint),
236                                                pathDevBounds);
237         sdc->addDrawOp(args.fClip, std::move(op));
238         return;
239     }
240 
241     auto op = make_non_convex_fill_op(args.fContext,
242                                       args.fSurfaceDrawContext->arenaAlloc(),
243                                       FillPathFlags::kStencilOnly,
244                                       aaType,
245                                       pathDevBounds,
246                                       *args.fViewMatrix,
247                                       path,
248                                       GrPaint());
249     sdc->addDrawOp(args.fClip, std::move(op));
250 }
251 
252 } // namespace skgpu::v1
253