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