Flutter Engine
The Flutter Engine
TessellationPathRenderer.cpp
Go to the documentation of this file.
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
9
10#include "src/core/SkPathPriv.h"
24
25namespace {
26
27using namespace skgpu::tess;
28
29GrOp::Owner make_non_convex_fill_op(GrRecordingContext* rContext,
30 SkArenaAlloc* arena,
31 skgpu::ganesh::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::ganesh::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::ganesh::PathStencilCoverOp>(
66 rContext, arena, viewMatrix, path, std::move(paint), aaType, fillPathFlags, drawBounds);
67}
68
69} // anonymous namespace
70
71namespace skgpu::ganesh {
72
73namespace {
74
75// `chopped_path` may be null, in which case no chopping actually happens. Returns true on success,
76// false on failure (chopping not allowed).
77bool ChopPathIfNecessary(const SkMatrix& viewMatrix,
78 const GrStyledShape& shape,
79 const SkIRect& clipConservativeBounds,
80 const SkStrokeRec& stroke,
81 SkPath* chopped_path) {
82 const SkRect pathDevBounds = viewMatrix.mapRect(shape.bounds());
84 pathDevBounds.width(),
85 pathDevBounds.height());
87 // The path is extremely large. Pre-chop its curves to keep the number of tessellation
88 // segments tractable. This will also flatten curves that fall completely outside the
89 // viewport.
90 SkRect viewport = SkRect::Make(clipConservativeBounds);
91 if (!shape.style().isSimpleFill()) {
92 // Outset the viewport to pad for the stroke width.
93 float inflationRadius;
94 if (stroke.isHairlineStyle()) {
95 // SkStrokeRec::getInflationRadius() doesn't handle hairlines robustly. Instead
96 // find the inflation of an equivalent stroke in device space with a width of 1.
97 inflationRadius = SkStrokeRec::GetInflationRadius(stroke.getJoin(),
98 stroke.getMiter(),
99 stroke.getCap(), 1);
100 } else {
101 inflationRadius = stroke.getInflationRadius() * viewMatrix.getMaxScale();
102 }
103 viewport.outset(inflationRadius, inflationRadius);
104 }
107 viewport.width(),
108 viewport.height()) > kMaxSegmentsPerCurve) {
109 return false;
110 }
111 if (chopped_path) {
112 *chopped_path = PreChopPathCurves(tess::kPrecision, *chopped_path, viewMatrix,
113 viewport);
114 }
115 }
116 return true;
117}
118
119} // anonymous namespace
120
122 return !caps.avoidStencilBuffers() &&
123 caps.drawInstancedSupport() &&
125}
126
127PathRenderer::StencilSupport TessellationPathRenderer::onGetStencilSupport(
128 const GrStyledShape& shape) const {
129 if (!shape.style().isSimpleFill() || shape.inverseFilled()) {
130 // Don't bother with stroke stencilling or inverse fills yet. The Skia API doesn't support
131 // clipping by a stroke, and the stencilling code already knows how to invert a fill.
133 }
135}
136
137PathRenderer::CanDrawPath TessellationPathRenderer::onCanDrawPath(
138 const CanDrawPathArgs& args) const {
139 const GrStyledShape& shape = *args.fShape;
140 if (args.fAAType == GrAAType::kCoverage ||
141 shape.style().hasPathEffect() ||
142 args.fViewMatrix->hasPerspective() ||
144 !args.fProxy->canUseStencil(*args.fCaps)) {
145 return CanDrawPath::kNo;
146 }
147 if (!shape.style().isSimpleFill()) {
148 if (shape.inverseFilled()) {
149 return CanDrawPath::kNo;
150 }
151 if (shape.style().strokeRec().getWidth() * args.fViewMatrix->getMaxScale() > 10000) {
152 // crbug.com/1266446 -- Don't draw massively wide strokes with the tessellator. Since we
153 // outset the viewport by stroke width for pre-chopping, astronomically wide strokes can
154 // result in an astronomical viewport size, and therefore an exponential explosion chops
155 // and memory usage. It is also simply inefficient to tessellate these strokes due to
156 // the number of radial edges required. We're better off just converting them to a path
157 // after a certain point.
158 return CanDrawPath::kNo;
159 }
160 }
161 if (args.fHasUserStencilSettings) {
162 // Non-convex paths and strokes use the stencil buffer internally, so they can't support
163 // draws with stencil settings.
164 if (!shape.style().isSimpleFill() || !shape.knownToBeConvex() || shape.inverseFilled()) {
165 return CanDrawPath::kNo;
166 }
167 }
168
169 // By passing in null for the chopped-path no chopping happens. Rather this returns whether
170 // chopping is possible.
171 if (!ChopPathIfNecessary(*args.fViewMatrix, shape, *args.fClipConservativeBounds,
172 shape.style().strokeRec(), nullptr)) {
173 return CanDrawPath::kNo;
174 }
175
176 return CanDrawPath::kYes;
177}
178
179bool TessellationPathRenderer::onDrawPath(const DrawPathArgs& args) {
180 auto sdc = args.fSurfaceDrawContext;
181
182 SkPath path;
183 args.fShape->asPath(&path);
184
185 // onDrawPath() should only be called if ChopPathIfNecessary() succeeded.
186 SkAssertResult(ChopPathIfNecessary(*args.fViewMatrix, *args.fShape,
187 *args.fClipConservativeBounds,
188 args.fShape->style().strokeRec(), &path));
189
190 // Handle strokes first.
191 if (!args.fShape->style().isSimpleFill()) {
192 SkASSERT(!path.isInverseFillType()); // See onGetStencilSupport().
193 SkASSERT(args.fUserStencilSettings->isUnused());
194 const SkStrokeRec& stroke = args.fShape->style().strokeRec();
196 auto op = GrOp::Make<StrokeTessellateOp>(args.fContext, args.fAAType, *args.fViewMatrix,
197 path, stroke, std::move(args.fPaint));
198 sdc->addDrawOp(args.fClip, std::move(op));
199 return true;
200 }
201
202 // Handle empty paths.
203 const SkRect pathDevBounds = args.fViewMatrix->mapRect(args.fShape->bounds());
204 if (pathDevBounds.isEmpty()) {
205 if (path.isInverseFillType()) {
206 args.fSurfaceDrawContext->drawPaint(args.fClip, std::move(args.fPaint),
207 *args.fViewMatrix);
208 }
209 return true;
210 }
211
212 // Handle convex paths. Make sure to check 'path' for convexity since it may have been
213 // pre-chopped, not 'fShape'.
214 if (path.isConvex() && !path.isInverseFillType()) {
215 auto op = GrOp::Make<PathTessellateOp>(args.fContext,
216 args.fSurfaceDrawContext->arenaAlloc(),
217 args.fAAType,
218 args.fUserStencilSettings,
219 *args.fViewMatrix,
220 path,
221 std::move(args.fPaint),
222 pathDevBounds);
223 sdc->addDrawOp(args.fClip, std::move(op));
224 return true;
225 }
226
227 SkASSERT(args.fUserStencilSettings->isUnused()); // See onGetStencilSupport().
228 const SkRect& drawBounds = path.isInverseFillType()
229 ? args.fSurfaceDrawContext->asSurfaceProxy()->backingStoreBoundsRect()
230 : pathDevBounds;
231 auto op = make_non_convex_fill_op(args.fContext,
232 args.fSurfaceDrawContext->arenaAlloc(),
234 args.fAAType,
235 drawBounds,
236 *args.fClipConservativeBounds,
237 *args.fViewMatrix,
238 path,
239 std::move(args.fPaint));
240 sdc->addDrawOp(args.fClip, std::move(op));
241 return true;
242}
243
244void TessellationPathRenderer::onStencilPath(const StencilPathArgs& args) {
245 SkASSERT(args.fShape->style().isSimpleFill()); // See onGetStencilSupport().
246 SkASSERT(!args.fShape->inverseFilled()); // See onGetStencilSupport().
247
248 auto sdc = args.fSurfaceDrawContext;
249 GrAAType aaType = (GrAA::kYes == args.fDoStencilMSAA) ? GrAAType::kMSAA : GrAAType::kNone;
250
251 SkRect pathDevBounds;
252 args.fViewMatrix->mapRect(&pathDevBounds, args.fShape->bounds());
253
254 SkPath path;
255 args.fShape->asPath(&path);
256
258 pathDevBounds.width(),
259 pathDevBounds.height());
261 SkRect viewport = SkRect::Make(*args.fClipConservativeBounds);
262 path = PreChopPathCurves(tess::kPrecision, path, *args.fViewMatrix, viewport);
263 }
264
265 // Make sure to check 'path' for convexity since it may have been pre-chopped, not 'fShape'.
266 if (path.isConvex()) {
267 constexpr static GrUserStencilSettings kMarkStencil(
269 0x0001,
271 0xffff,
274 0xffff>());
275
276 GrPaint stencilPaint;
278 auto op = GrOp::Make<PathTessellateOp>(args.fContext,
279 args.fSurfaceDrawContext->arenaAlloc(),
280 aaType,
282 *args.fViewMatrix,
283 path,
284 std::move(stencilPaint),
285 pathDevBounds);
286 sdc->addDrawOp(args.fClip, std::move(op));
287 return;
288 }
289
290 auto op = make_non_convex_fill_op(args.fContext,
291 args.fSurfaceDrawContext->arenaAlloc(),
293 aaType,
294 pathDevBounds,
295 *args.fClipConservativeBounds,
296 *args.fViewMatrix,
297 path,
298 GrPaint());
299 sdc->addDrawOp(args.fClip, std::move(op));
300}
301
302} // namespace skgpu::ganesh
SkAssertResult(font.textToGlyphs("Hello", 5, SkTextEncoding::kUTF8, glyphs, std::size(glyphs))==count)
GrAAType
Definition: GrTypesPriv.h:200
#define SkASSERT(cond)
Definition: SkAssert.h:116
static int SkNextLog2(uint32_t value)
Definition: SkMathPriv.h:238
Definition: GrCaps.h:57
bool avoidStencilBuffers() const
Definition: GrCaps.h:139
bool drawInstancedSupport() const
Definition: GrCaps.h:80
bool disableTessellationPathRenderer() const
Definition: GrCaps.h:441
static const GrDisableColorXPFactory * Get()
std::unique_ptr< GrOp > Owner
Definition: GrOp.h:72
void setXPFactory(const GrXPFactory *xpFactory)
Definition: GrPaint.h:53
bool hasPathEffect() const
Definition: GrStyle.h:122
bool isSimpleFill() const
Definition: GrStyle.h:114
const SkStrokeRec & strokeRec() const
Definition: GrStyle.h:140
bool knownToBeConvex() const
bool inverseFilled() const
const GrStyle & style() const
SkRect bounds() const
uint32_t segmentMask() const
SkScalar getMaxScale() const
Definition: SkMatrix.cpp:1531
bool mapRect(SkRect *dst, const SkRect &src, SkApplyPerspectiveClip pc=SkApplyPerspectiveClip::kYes) const
Definition: SkMatrix.cpp:1141
Definition: SkPath.h:59
@ kLine_SegmentMask
Definition: SkPath.h:1445
Style getStyle() const
Definition: SkStrokeRec.cpp:71
@ kStrokeAndFill_Style
Definition: SkStrokeRec.h:36
static SkScalar GetInflationRadius(const SkPaint &, SkPaint::Style)
SkScalar getWidth() const
Definition: SkStrokeRec.h:42
const Paint & paint
Definition: color_source.cc:38
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
static constexpr GrUserStencilSettings kMarkStencil(GrUserStencilSettings::StaticInit< 0x0001, GrUserStencilTest::kLessIfInClip, 0x0000, GrUserStencilOp::kZero, GrUserStencilOp::kReplace, 0xffff >())
static constexpr float kMaxSegmentsPerCurve_p4
Definition: Tessellation.h:53
SkPath PreChopPathCurves(float tessellationPrecision, const SkPath &path, const SkMatrix &matrix, const SkRect &viewport)
static constexpr float kMaxSegmentsPerCurve
Definition: Tessellation.h:51
static constexpr float kPrecision
Definition: Tessellation.h:29
AI float worst_case_cubic(float precision, float devWidth, float devHeight)
Definition: WangsFormula.h:221
AI float worst_case_cubic_p4(float precision, float devWidth, float devHeight)
Definition: WangsFormula.h:214
static constexpr Init< Ref, Test, TestMask, PassOp, FailOp, WriteMask > StaticInit()
Definition: SkRect.h:32
static SkRect Make(const SkISize &size)
Definition: SkRect.h:669
bool intersect(const SkRect &r)
Definition: SkRect.cpp:114
void outset(float dx, float dy)
Definition: SkRect.h:1077
constexpr float height() const
Definition: SkRect.h:769
constexpr float width() const
Definition: SkRect.h:762
bool isEmpty() const
Definition: SkRect.h:693