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