Flutter Engine
The Flutter Engine
TessellateBench.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2020 Google Inc.
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 "bench/Benchmark.h"
10#include "src/core/SkPathPriv.h"
11#include "src/core/SkRectPriv.h"
20#include "tools/ToolUtils.h"
21
22#include <vector>
23
24namespace skgpu::ganesh {
25
26// This is the number of cubics in desk_chalkboard.skp. (There are no quadratics in the chalkboard.)
27constexpr static int kNumCubicsInChalkboard = 47182;
28
30 GrMockOptions mockOptions;
31 mockOptions.fDrawInstancedSupport = true;
33 mockOptions.fConfigOptions[(int)GrColorType::kAlpha_8].fRenderability =
34 GrMockOptions::ConfigOptions::Renderability::kMSAA;
35 mockOptions.fConfigOptions[(int)GrColorType::kAlpha_8].fTexturable = true;
36 mockOptions.fIntegerSupport = true;
37
38 GrContextOptions ctxOptions;
39 ctxOptions.fGpuPathRenderers = GpuPathRenderers::kTessellation;
40
41 return GrDirectContext::MakeMock(&mockOptions, ctxOptions);
42}
43
44static SkPath make_cubic_path(int maxPow2) {
45 SkRandom rand;
47 for (int i = 0; i < kNumCubicsInChalkboard/2; ++i) {
48 float x = std::ldexp(rand.nextF(), (i % maxPow2)) / 1e3f;
49 path.cubicTo(111.625f*x, 308.188f*x, 764.62f*x, -435.688f*x, 742.63f*x, 85.187f*x);
50 path.cubicTo(764.62f*x, -435.688f*x, 111.625f*x, 308.188f*x, 0, 0);
51 }
52 return path;
53}
54
56 SkRandom rand;
58 for (int i = 0; i < kNumCubicsInChalkboard / 40; ++i) {
59 for (int j = -10; j <= 10; j++) {
60 const float x = std::ldexp(rand.nextF(), (i % 18)) / 1e3f;
61 const float w = std::ldexp(1 + rand.nextF(), j);
62 path.conicTo(111.625f * x, 308.188f * x, 764.62f * x, -435.688f * x, w);
63 }
64 }
65 return path;
66}
67
68[[maybe_unused]] static SkPath make_quad_path(int maxPow2) {
69 SkRandom rand;
71 for (int i = 0; i < kNumCubicsInChalkboard; ++i) {
72 float x = std::ldexp(rand.nextF(), (i % maxPow2)) / 1e3f;
73 path.quadTo(111.625f * x, 308.188f * x, 764.62f * x, -435.688f * x);
74 }
75 return path;
76}
77
78[[maybe_unused]] static SkPath make_line_path(int maxPow2) {
79 SkRandom rand;
81 for (int i = 0; i < kNumCubicsInChalkboard; ++i) {
82 float x = std::ldexp(rand.nextF(), (i % maxPow2)) / 1e3f;
83 path.lineTo(764.62f * x, -435.688f * x);
84 }
85 return path;
86}
87
88// This serves as a base class for benchmarking individual methods on PathTessellateOp.
90public:
91 PathTessellateBenchmark(const char* subName, const SkPath& p, const SkMatrix& m)
92 : fPath(p), fMatrix(m) {
93 fName.printf("tessellate_%s", subName);
94 }
95
96 const char* onGetName() override { return fName.c_str(); }
98
99protected:
100 void onDelayedSetup() override {
101 fTarget = std::make_unique<GrMockOpTarget>(make_mock_context());
102 }
103
104 void onDraw(int loops, SkCanvas*) final {
105 if (!fTarget->mockContext()) {
106 SkDebugf("ERROR: could not create mock context.");
107 return;
108 }
109 for (int i = 0; i < loops; ++i) {
110 this->runBench();
111 fTarget->resetAllocator();
112 }
113 }
114
115 virtual void runBench() = 0;
116
118 std::unique_ptr<GrMockOpTarget> fTarget;
121};
122
123#define DEF_PATH_TESS_BENCH(NAME, PATH, MATRIX) \
124 class PathTessellateBenchmark_##NAME : public PathTessellateBenchmark { \
125 public: \
126 PathTessellateBenchmark_##NAME() : PathTessellateBenchmark(#NAME, (PATH), (MATRIX)) {} \
127 void runBench() override; \
128 }; \
129 DEF_BENCH( return new PathTessellateBenchmark_##NAME(); ); \
130 void PathTessellateBenchmark_##NAME::runBench()
131
133 1.0001f, 0.0001f, 0.0001f,
134 -.0001f, 0.9999f, -.0001f,
135 0, 0, 1);
136
137DEF_PATH_TESS_BENCH(GrPathCurveTessellator, make_cubic_path(8), SkMatrix::I()) {
138 SkArenaAlloc arena(1024);
141 auto tess = PathCurveTessellator::Make(&arena,
142 fTarget->caps().shaderCaps()->fInfinitySupport);
143 tess->prepare(fTarget.get(),
144 fMatrix,
145 {gAlmostIdentity, fPath, SK_PMColor4fTRANSPARENT},
146 fPath.countVerbs());
147}
148
149DEF_PATH_TESS_BENCH(GrPathWedgeTessellator, make_cubic_path(8), SkMatrix::I()) {
150 SkArenaAlloc arena(1024);
153 auto tess = PathWedgeTessellator::Make(&arena,
154 fTarget->caps().shaderCaps()->fInfinitySupport);
155 tess->prepare(fTarget.get(),
156 fMatrix,
157 {gAlmostIdentity, fPath, SK_PMColor4fTRANSPARENT},
158 fPath.countVerbs());
159}
160
162 int sum = 0;
164 for (auto [verb, pts, w] : SkPathPriv::Iterate(path)) {
165 if (verb == SkPathVerb::kCubic) {
166 sum += wangs_formula::cubic_log2(4, pts, xform);
167 }
168 }
169 // Don't let the compiler optimize away wangs_formula::cubic_log2.
170 if (sum <= 0) {
171 SK_ABORT("sum should be > 0.");
172 }
173}
174
175DEF_PATH_TESS_BENCH(wangs_formula_cubic_log2, make_cubic_path(18), SkMatrix::I()) {
177}
178
179DEF_PATH_TESS_BENCH(wangs_formula_cubic_log2_scale, make_cubic_path(18),
180 SkMatrix::Scale(1.1f, 0.9f)) {
182}
183
184DEF_PATH_TESS_BENCH(wangs_formula_cubic_log2_affine, make_cubic_path(18),
185 SkMatrix::MakeAll(.9f,0.9f,0, 1.1f,1.1f,0, 0,0,1)) {
187}
188
190 int sum = 0;
192 for (auto [verb, pts, w] : SkPathPriv::Iterate(path)) {
193 if (verb == SkPathVerb::kConic) {
194 sum += wangs_formula::conic(4, pts, *w, xform);
195 }
196 }
197 // Don't let the compiler optimize away wangs_formula::conic.
198 if (sum <= 0) {
199 SK_ABORT("sum should be > 0.");
200 }
201}
202
204 int sum = 0;
206 for (auto [verb, pts, w] : SkPathPriv::Iterate(path)) {
207 if (verb == SkPathVerb::kConic) {
208 sum += wangs_formula::conic_log2(4, pts, *w, xform);
209 }
210 }
211 // Don't let the compiler optimize away wangs_formula::conic.
212 if (sum <= 0) {
213 SK_ABORT("sum should be > 0.");
214 }
215}
216
219}
220
221DEF_PATH_TESS_BENCH(wangs_formula_conic_log2, make_conic_path(), SkMatrix::I()) {
223}
224
225DEF_PATH_TESS_BENCH(middle_out_triangulation,
227 SkMatrix::I()) {
228 // Conservative estimate of triangulation (see PathStencilCoverOp)
229 const int maxVerts = 3 * (kNumCubicsInChalkboard - 2);
230
232 int baseVertex;
233 VertexWriter vertexWriter = fTarget->makeVertexWriter(
234 sizeof(SkPoint), maxVerts, &buffer, &baseVertex);
236 for (tess::PathMiddleOutFanIter it(fPath); !it.done();) {
237 for (auto [p0, p1, p2] : it.nextStack()) {
238 vertexWriter << m.map2Points(p0, p1) << m.mapPoint(p2);
239 }
240 }
241}
242
244using MakePathStrokesFn = std::vector<PathStrokeList>(*)();
245
246static std::vector<PathStrokeList> make_simple_cubic_path() {
247 auto path = SkPath().moveTo(0, 0);
248 for (int i = 0; i < kNumCubicsInChalkboard/2; ++i) {
249 path.cubicTo(100, 0, 50, 100, 100, 100);
250 path.cubicTo(0, -100, 200, 100, 0, 0);
251 }
253 stroke.setStrokeStyle(8);
255 return {{path, stroke, SK_PMColor4fWHITE}};
256}
257
258// Generates a list of paths that resemble the MotionMark benchmark.
259static std::vector<PathStrokeList> make_motionmark_paths() {
260 std::vector<PathStrokeList> pathStrokes;
261 SkRandom rand;
262 for (int i = 0; i < 8702; ++i) {
263 // The number of paths with a given number of verbs in the MotionMark bench gets cut in half
264 // every time the number of verbs increases by 1.
265 int numVerbs = 28 - SkNextLog2(rand.nextRangeU(0, (1 << 27) - 1));
266 SkPath path;
267 for (int j = 0; j < numVerbs; ++j) {
268 switch (rand.nextU() & 3) {
269 case 0:
270 case 1:
271 path.lineTo(rand.nextRangeF(0, 150), rand.nextRangeF(0, 150));
272 break;
273 case 2:
274 if (rand.nextULessThan(10) == 0) {
275 // Cusp.
276 auto [x, y] = (path.isEmpty())
277 ? SkPoint{0,0}
278 : SkPathPriv::PointData(path)[path.countPoints() - 1];
279 path.quadTo(x + rand.nextRangeF(0, 150), y, x - rand.nextRangeF(0, 150), y);
280 } else {
281 path.quadTo(rand.nextRangeF(0, 150), rand.nextRangeF(0, 150),
282 rand.nextRangeF(0, 150), rand.nextRangeF(0, 150));
283 }
284 break;
285 case 3:
286 if (rand.nextULessThan(10) == 0) {
287 // Cusp.
288 float y = (path.isEmpty())
289 ? 0 : SkPathPriv::PointData(path)[path.countPoints() - 1].fY;
290 path.cubicTo(rand.nextRangeF(0, 150), y, rand.nextRangeF(0, 150), y,
291 rand.nextRangeF(0, 150), y);
292 } else {
293 path.cubicTo(rand.nextRangeF(0, 150), rand.nextRangeF(0, 150),
294 rand.nextRangeF(0, 150), rand.nextRangeF(0, 150),
295 rand.nextRangeF(0, 150), rand.nextRangeF(0, 150));
296 }
297 break;
298 }
299 }
301 // The number of paths with a given stroke width in the MotionMark bench gets cut in half
302 // every time the stroke width increases by 1.
303 float strokeWidth = 21 - log2f(rand.nextRangeF(0, 1 << 20));
304 stroke.setStrokeStyle(strokeWidth);
306 pathStrokes.emplace_back(path, stroke, SK_PMColor4fWHITE);
307 }
308 return pathStrokes;
309}
310
312
314public:
316 PatchAttribs attribs,
317 float matrixScale,
318 const char* suffix)
319 : fMakePathStrokesFn(makePathStrokesFn)
320 , fPatchAttribs(attribs)
321 , fMatrixScale(matrixScale) {
322 fName.printf("tessellate_%s", suffix);
323 }
324
325private:
326 const char* onGetName() override { return fName.c_str(); }
327 bool isSuitableFor(Backend backend) final { return backend == Backend::kNonRendering; }
328
329 void onDelayedSetup() override {
330 fTarget = std::make_unique<GrMockOpTarget>(make_mock_context());
331 if (!fTarget->mockContext()) {
332 SkDebugf("ERROR: could not create mock context.");
333 return;
334 }
335
336 fPathStrokes = fMakePathStrokesFn();
337 for (size_t i = 0; i < fPathStrokes.size(); ++i) {
338 if (i + 1 < fPathStrokes.size()) {
339 fPathStrokes[i].fNext = &fPathStrokes[i + 1];
340 }
341 fTotalVerbCount += fPathStrokes[i].fPath.countVerbs();
342 }
343
344 fTessellator = std::make_unique<StrokeTessellator>(fPatchAttribs);
345 }
346
347 void onDraw(int loops, SkCanvas*) final {
348 for (int i = 0; i < loops; ++i) {
349 fTessellator->prepare(fTarget.get(),
350 SkMatrix::Scale(fMatrixScale, fMatrixScale),
351 fPathStrokes.data(),
352 fTotalVerbCount);
353 fTarget->resetAllocator();
354 }
355 }
356
357 SkString fName;
358 MakePathStrokesFn fMakePathStrokesFn;
359 const PatchAttribs fPatchAttribs;
360 float fMatrixScale;
361 std::unique_ptr<GrMockOpTarget> fTarget;
362 std::vector<PathStrokeList> fPathStrokes;
363 std::unique_ptr<StrokeTessellator> fTessellator;
364 SkArenaAlloc fPersistentArena{1024};
365 int fTotalVerbCount = 0;
366};
367
370 "GrStrokeFixedCountTessellator");
371)
372
373DEF_BENCH(return new TessPrepareBench(
375 "GrStrokeFixedCountTessellator_one_chop");
376)
377
378DEF_BENCH(return new TessPrepareBench(
379 make_motionmark_paths, PatchAttribs::kStrokeParams, 1,
380 "GrStrokeFixedCountTessellator_motionmark");
381)
382
383} // namespace skgpu::ganesh
SkPath fPath
static const int strokeWidth
Definition: BlurTest.cpp:60
const char * backend
SkMatrix fMatrix
Definition: FillRRectOp.cpp:74
#define SK_ABORT(message,...)
Definition: SkAssert.h:70
@ kSrcOver
r = s + (1-sa)*d
constexpr SkPMColor4f SK_PMColor4fWHITE
Definition: SkColorData.h:380
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
static int SkNextLog2(uint32_t value)
Definition: SkMathPriv.h:238
@ kCubic
SkPath::RawIter returns 4 points.
@ kConic
SkPath::RawIter returns 3 points + 1 weight.
@ kCanMap_MapFlag
Definition: GrCaps.h:199
static sk_sp< GrDirectContext > MakeMock(const GrMockOptions *, const GrContextOptions &)
static SkMatrix Scale(SkScalar sx, SkScalar sy)
Definition: SkMatrix.h:75
static SkMatrix MakeAll(SkScalar scaleX, SkScalar skewX, SkScalar transX, SkScalar skewY, SkScalar scaleY, SkScalar transY, SkScalar pers0, SkScalar pers1, SkScalar pers2)
Definition: SkMatrix.h:179
static const SkMatrix & I()
Definition: SkMatrix.cpp:1544
@ kButt_Cap
no stroke extension
Definition: SkPaint.h:334
@ kMiter_Join
extends to miter limit
Definition: SkPaint.h:359
@ kBevel_Join
connects outside edges
Definition: SkPaint.h:361
static const SkPoint * PointData(const SkPath &path)
Definition: SkPathPriv.h:203
Definition: SkPath.h:59
SkPath & moveTo(SkScalar x, SkScalar y)
Definition: SkPath.cpp:688
int countVerbs() const
Definition: SkPath.cpp:556
uint32_t nextU()
Definition: SkRandom.h:42
float nextF()
Definition: SkRandom.h:55
uint32_t nextULessThan(uint32_t count)
Definition: SkRandom.h:93
float nextRangeF(float min, float max)
Definition: SkRandom.h:64
uint32_t nextRangeU(uint32_t min, uint32_t max)
Definition: SkRandom.h:80
void printf(const char format[],...) SK_PRINTF_LIKE(2
Definition: SkString.cpp:534
const char * c_str() const
Definition: SkString.h:133
static constexpr Swizzle RGBA()
Definition: Swizzle.h:66
static PathCurveTessellator * Make(SkArenaAlloc *arena, bool infinitySupport, PatchAttribs attribs=PatchAttribs::kNone)
PathTessellateBenchmark(const char *subName, const SkPath &p, const SkMatrix &m)
void onDraw(int loops, SkCanvas *) final
std::unique_ptr< GrMockOpTarget > fTarget
bool isSuitableFor(Backend backend) final
static PathWedgeTessellator * Make(SkArenaAlloc *arena, bool infinitySupport, PatchAttribs attribs=PatchAttribs::kNone)
TessPrepareBench(MakePathStrokesFn makePathStrokesFn, PatchAttribs attribs, float matrixScale, const char *suffix)
double y
double x
unsigned useCenter Optional< SkMatrix > matrix
Definition: SkRecords.h:258
SkPath make_star(const SkRect &bounds, int numPts, int step)
Definition: ToolUtils.cpp:269
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
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 to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
static TessellatorLibtess tess
DEF_BENCH(return new TessPrepareBench(make_simple_cubic_path, PatchAttribs::kNone, 1, "GrStrokeFixedCountTessellator");) DEF_BENCH(return new TessPrepareBench(make_simple_cubic_path
static SkPath make_line_path(int maxPow2)
tess::PatchAttribs PatchAttribs
static void benchmark_wangs_formula_conic_log2(const SkMatrix &matrix, const SkPath &path)
StrokeTessellator::PathStrokeList PathStrokeList
static SkPath make_cubic_path(int maxPow2)
static constexpr int kNumCubicsInChalkboard
static SkPath make_quad_path(int maxPow2)
static std::vector< PathStrokeList > make_simple_cubic_path()
static void benchmark_wangs_formula_cubic_log2(const SkMatrix &matrix, const SkPath &path)
std::vector< PathStrokeList >(*)() MakePathStrokesFn
static sk_sp< GrDirectContext > make_mock_context()
static const SkMatrix gAlmostIdentity
static void benchmark_wangs_formula_conic(const SkMatrix &matrix, const SkPath &path)
static SkPath make_conic_path()
static std::vector< PathStrokeList > make_motionmark_paths()
DEF_PATH_TESS_BENCH(GrPathCurveTessellator, make_cubic_path(8), SkMatrix::I())
AI float conic(float tolerance, const SkPoint pts[], float w, const VectorXform &vectorXform=VectorXform())
Definition: WangsFormula.h:287
AI int conic_log2(float tolerance, const SkPoint pts[], float w, const VectorXform &vectorXform=VectorXform())
Definition: WangsFormula.h:296
AI int cubic_log2(float precision, const SkPoint pts[], const VectorXform &vectorXform=VectorXform())
Definition: WangsFormula.h:203
SkScalar w
uint32_t fMapBufferFlags
Definition: GrMockTypes.h:146
bool fIntegerSupport
Definition: GrMockTypes.h:155
bool fDrawInstancedSupport
Definition: GrMockTypes.h:144
ConfigOptions fConfigOptions[kGrColorTypeCnt]
Definition: GrMockTypes.h:151
float fY
y-axis value
Definition: SkPoint_impl.h:165
static constexpr SkRect MakeWH(float w, float h)
Definition: SkRect.h:609