Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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 =
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;
46 SkPath path;
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;
57 SkPath path;
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;
70 SkPath path;
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;
80 SkPath path;
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(); }
97 bool isSuitableFor(Backend backend) final { return backend == Backend::kNonRendering; }
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
161static void benchmark_wangs_formula_cubic_log2(const SkMatrix& matrix, const SkPath& path) {
162 int sum = 0;
163 wangs_formula::VectorXform xform(matrix);
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
189static void benchmark_wangs_formula_conic(const SkMatrix& matrix, const SkPath& path) {
190 int sum = 0;
191 wangs_formula::VectorXform xform(matrix);
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
203static void benchmark_wangs_formula_conic_log2(const SkMatrix& matrix, const SkPath& path) {
204 int sum = 0;
205 wangs_formula::VectorXform xform(matrix);
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
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));
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
369 make_simple_cubic_path, PatchAttribs::kNone, 1,
370 "GrStrokeFixedCountTessellator");
371)
372
373DEF_BENCH(return new TessPrepareBench(
374 make_simple_cubic_path, PatchAttribs::kNone, 5,
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
#define DEF_BENCH(code)
Definition Benchmark.h:20
static const int strokeWidth
Definition BlurTest.cpp:60
const char * backend
#define SK_ABORT(message,...)
Definition SkAssert.h:70
@ kSrcOver
r = s + (1-sa)*d
constexpr SkPMColor4f SK_PMColor4fWHITE
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.
#define DEF_PATH_TESS_BENCH(NAME, PATH, MATRIX)
Type::kYUV Type::kRGBA() int(0.7 *637)
@ 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()
@ 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
SkPath & moveTo(SkScalar x, SkScalar y)
Definition SkPath.cpp:678
int countVerbs() const
Definition SkPath.cpp:546
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
void setStrokeStyle(SkScalar width, bool strokeAndFill=false)
void setStrokeParams(SkPaint::Cap cap, SkPaint::Join join, SkScalar miterLimit)
Definition SkStrokeRec.h:65
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)
const char * onGetName() override
void onDraw(int loops, SkCanvas *) final
bool isSuitableFor(Backend backend) final
TessPrepareBench(MakePathStrokesFn makePathStrokesFn, PatchAttribs attribs, float matrixScale, const char *suffix)
static const uint8_t buffer[]
double y
double x
SkPath make_star(const SkRect &bounds, int numPts, int step)
static SkPath make_line_path(int maxPow2)
static void benchmark_wangs_formula_conic_log2(const SkMatrix &matrix, const SkPath &path)
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()
AI float conic(float tolerance, const SkPoint pts[], float w, const VectorXform &vectorXform=VectorXform())
AI int conic_log2(float tolerance, const SkPoint pts[], float w, const VectorXform &vectorXform=VectorXform())
AI int cubic_log2(float precision, const SkPoint pts[], const VectorXform &vectorXform=VectorXform())
SkScalar w
uint32_t fMapBufferFlags
bool fDrawInstancedSupport
ConfigOptions fConfigOptions[kGrColorTypeCnt]
float fY
y-axis value
static constexpr SkRect MakeWH(float w, float h)
Definition SkRect.h:609