blob: a8dc005264eb8413691e937cd871bb35d536cb67 [file] [log] [blame]
Chris Dalton7f0b8972020-04-23 15:52:24 -06001/*
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"
Robert Phillipsf0288102020-07-06 13:45:34 -04009#include "include/gpu/GrDirectContext.h"
Chris Daltonf6bf5162020-05-13 19:18:46 -060010#include "src/core/SkPathPriv.h"
Adlai Hollera0693042020-10-14 11:23:11 -040011#include "src/gpu/GrDirectContextPriv.h"
Chris Dalton7f0b8972020-04-23 15:52:24 -060012#include "src/gpu/GrOpFlushState.h"
Chris Dalton90ad0fe2020-11-09 14:13:39 -070013#include "src/gpu/mock/GrMockOpTarget.h"
Chris Daltonb5391d92020-05-24 14:55:54 -060014#include "src/gpu/tessellate/GrMiddleOutPolygonTriangulator.h"
Chris Dalton078f8752020-07-30 19:50:46 -060015#include "src/gpu/tessellate/GrPathTessellateOp.h"
Chris Daltonc2a17462020-12-09 16:46:22 -070016#include "src/gpu/tessellate/GrStrokeIndirectOp.h"
Chris Dalton0e543092020-11-03 14:09:16 -070017#include "src/gpu/tessellate/GrStrokeTessellateOp.h"
Chris Daltonf6bf5162020-05-13 19:18:46 -060018#include "src/gpu/tessellate/GrWangsFormula.h"
Chris Daltonf5132a02020-04-27 23:40:03 -060019#include "tools/ToolUtils.h"
Chris Daltonc2a17462020-12-09 16:46:22 -070020#include <vector>
Chris Dalton7f0b8972020-04-23 15:52:24 -060021
22// This is the number of cubics in desk_chalkboard.skp. (There are no quadratics in the chalkboard.)
23constexpr static int kNumCubicsInChalkboard = 47182;
24
Chris Dalton90ad0fe2020-11-09 14:13:39 -070025static sk_sp<GrDirectContext> make_mock_context() {
26 GrMockOptions mockOptions;
27 mockOptions.fDrawInstancedSupport = true;
28 mockOptions.fMaxTessellationSegments = 64;
29 mockOptions.fMapBufferFlags = GrCaps::kCanMap_MapFlag;
30 mockOptions.fConfigOptions[(int)GrColorType::kAlpha_8].fRenderability =
31 GrMockOptions::ConfigOptions::Renderability::kMSAA;
32 mockOptions.fConfigOptions[(int)GrColorType::kAlpha_8].fTexturable = true;
33 mockOptions.fIntegerSupport = true;
34
35 GrContextOptions ctxOptions;
36 ctxOptions.fGpuPathRenderers = GpuPathRenderers::kTessellation;
Chris Dalton15f51842020-12-15 19:55:10 -070037 ctxOptions.fSuppressTessellationShaders = false;
Chris Dalton90ad0fe2020-11-09 14:13:39 -070038
39 return GrDirectContext::MakeMock(&mockOptions, ctxOptions);
40}
41
Chris Dalton7f0b8972020-04-23 15:52:24 -060042static SkPath make_cubic_path() {
43 SkRandom rand;
44 SkPath path;
45 for (int i = 0; i < kNumCubicsInChalkboard/2; ++i) {
Chris Dalton2f2d81c2020-05-13 17:57:37 -060046 float x = std::ldexp(rand.nextF(), (i % 18)) / 1e3f;
Chris Dalton7f0b8972020-04-23 15:52:24 -060047 path.cubicTo(111.625f*x, 308.188f*x, 764.62f*x, -435.688f*x, 742.63f*x, 85.187f*x);
48 path.cubicTo(764.62f*x, -435.688f*x, 111.625f*x, 308.188f*x, 0, 0);
49 }
50 return path;
51}
52
Chris Dalton078f8752020-07-30 19:50:46 -060053// This serves as a base class for benchmarking individual methods on GrPathTessellateOp.
Chris Daltond7177432021-01-15 13:12:50 -070054class PathTessellateBenchmark : public Benchmark {
Chris Dalton7f0b8972020-04-23 15:52:24 -060055public:
Chris Daltond7177432021-01-15 13:12:50 -070056 PathTessellateBenchmark(const char* subName, const SkPath& p, const SkMatrix& m)
57 : fPath(p), fMatrix(m) {
Chris Dalton7f0b8972020-04-23 15:52:24 -060058 fName.printf("tessellate_%s", subName);
59 }
60
61 const char* onGetName() override { return fName.c_str(); }
62 bool isSuitableFor(Backend backend) final { return backend == kNonRendering_Backend; }
63
Chris Daltond7177432021-01-15 13:12:50 -070064protected:
Chris Dalton0e543092020-11-03 14:09:16 -070065 void onDelayedSetup() override {
Chris Dalton90ad0fe2020-11-09 14:13:39 -070066 fTarget = std::make_unique<GrMockOpTarget>(make_mock_context());
Chris Dalton0e543092020-11-03 14:09:16 -070067 }
68
Chris Dalton7f0b8972020-04-23 15:52:24 -060069 void onDraw(int loops, SkCanvas*) final {
Chris Dalton0e543092020-11-03 14:09:16 -070070 if (!fTarget->mockContext()) {
Chris Dalton1443c9d2020-05-27 09:43:34 -060071 SkDebugf("ERROR: could not create mock context.");
72 return;
73 }
Chris Dalton7f0b8972020-04-23 15:52:24 -060074 for (int i = 0; i < loops; ++i) {
Chris Daltond7177432021-01-15 13:12:50 -070075 this->runBench();
Chris Dalton0e543092020-11-03 14:09:16 -070076 fTarget->resetAllocator();
Chris Dalton7f0b8972020-04-23 15:52:24 -060077 }
78 }
79
Chris Daltond7177432021-01-15 13:12:50 -070080 virtual void runBench() = 0;
Chris Dalton7f0b8972020-04-23 15:52:24 -060081
Chris Dalton7f0b8972020-04-23 15:52:24 -060082 SkString fName;
Chris Daltond7177432021-01-15 13:12:50 -070083 std::unique_ptr<GrMockOpTarget> fTarget;
84 const SkPath fPath;
85 const SkMatrix& fMatrix;
Chris Dalton7f0b8972020-04-23 15:52:24 -060086};
87
Chris Daltond7177432021-01-15 13:12:50 -070088#define DEF_PATH_TESS_BENCH(NAME, PATH, MATRIX) \
89 class PathTessellateBenchmark_##NAME : public PathTessellateBenchmark { \
Chris Daltonb5391d92020-05-24 14:55:54 -060090 public: \
Chris Daltond7177432021-01-15 13:12:50 -070091 PathTessellateBenchmark_##NAME() : PathTessellateBenchmark(#NAME, (PATH), (MATRIX)) {} \
92 void runBench() override; \
Chris Daltonb5391d92020-05-24 14:55:54 -060093 }; \
Chris Daltond7177432021-01-15 13:12:50 -070094 DEF_BENCH( return new PathTessellateBenchmark_##NAME(); ); \
95 void PathTessellateBenchmark_##NAME::runBench()
Chris Dalton7f0b8972020-04-23 15:52:24 -060096
Chris Daltond7177432021-01-15 13:12:50 -070097DEF_PATH_TESS_BENCH(GrPathIndirectTessellator, make_cubic_path(), SkMatrix::I()) {
98 GrPathIndirectTessellator tess(fMatrix, fPath, GrPathIndirectTessellator::DrawInnerFan::kNo);
99 tess.prepare(fTarget.get(), fMatrix, fPath);
Chris Daltonb5391d92020-05-24 14:55:54 -0600100}
Chris Dalton7f0b8972020-04-23 15:52:24 -0600101
Chris Daltond7177432021-01-15 13:12:50 -0700102DEF_PATH_TESS_BENCH(GrPathOuterCurveTessellator, make_cubic_path(), SkMatrix::I()) {
103 GrPathOuterCurveTessellator tess;
104 tess.prepare(fTarget.get(), fMatrix, fPath);
Chris Daltonb5391d92020-05-24 14:55:54 -0600105}
Chris Dalton7f0b8972020-04-23 15:52:24 -0600106
Chris Daltond7177432021-01-15 13:12:50 -0700107DEF_PATH_TESS_BENCH(GrPathWedgeTessellator, make_cubic_path(), SkMatrix::I()) {
108 GrPathWedgeTessellator tess;
109 tess.prepare(fTarget.get(), fMatrix, fPath);
Chris Daltonb5391d92020-05-24 14:55:54 -0600110}
Chris Daltonf6bf5162020-05-13 19:18:46 -0600111
Chris Daltonb5391d92020-05-24 14:55:54 -0600112static void benchmark_wangs_formula_cubic_log2(const SkMatrix& matrix, const SkPath& path) {
113 int sum = 0;
114 GrVectorXform xform(matrix);
115 for (auto [verb, pts, w] : SkPathPriv::Iterate(path)) {
116 if (verb == SkPathVerb::kCubic) {
117 sum += GrWangsFormula::cubic_log2(4, pts, xform);
Chris Daltonf6bf5162020-05-13 19:18:46 -0600118 }
119 }
Chris Daltonb5391d92020-05-24 14:55:54 -0600120 // Don't let the compiler optimize away GrWangsFormula::cubic_log2.
121 if (sum <= 0) {
122 SK_ABORT("sum should be > 0.");
123 }
124}
Chris Daltonf6bf5162020-05-13 19:18:46 -0600125
Chris Daltond7177432021-01-15 13:12:50 -0700126DEF_PATH_TESS_BENCH(wangs_formula_cubic_log2, make_cubic_path(), SkMatrix::I()) {
127 benchmark_wangs_formula_cubic_log2(fMatrix, fPath);
Chris Daltonb5391d92020-05-24 14:55:54 -0600128}
129
Chris Daltond7177432021-01-15 13:12:50 -0700130DEF_PATH_TESS_BENCH(wangs_formula_cubic_log2_scale, make_cubic_path(),
131 SkMatrix::Scale(1.1f, 0.9f)) {
132 benchmark_wangs_formula_cubic_log2(fMatrix, fPath);
Chris Daltonb5391d92020-05-24 14:55:54 -0600133}
134
Chris Daltone2067642020-09-23 11:07:20 -0600135DEF_PATH_TESS_BENCH(wangs_formula_cubic_log2_affine, make_cubic_path(),
Chris Daltond7177432021-01-15 13:12:50 -0700136 SkMatrix::MakeAll(.9f,0.9f,0, 1.1f,1.1f,0, 0,0,1)) {
137 benchmark_wangs_formula_cubic_log2(fMatrix, fPath);
Chris Daltonb5391d92020-05-24 14:55:54 -0600138}
139
Chris Daltone2067642020-09-23 11:07:20 -0600140DEF_PATH_TESS_BENCH(middle_out_triangulation,
141 ToolUtils::make_star(SkRect::MakeWH(500, 500), kNumCubicsInChalkboard),
Chris Daltond7177432021-01-15 13:12:50 -0700142 SkMatrix::I()) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600143 int baseVertex;
Chris Daltond7177432021-01-15 13:12:50 -0700144 auto vertexData = static_cast<SkPoint*>(fTarget->makeVertexSpace(
Chris Daltonb5391d92020-05-24 14:55:54 -0600145 sizeof(SkPoint), kNumCubicsInChalkboard, nullptr, &baseVertex));
Chris Daltond7177432021-01-15 13:12:50 -0700146 GrMiddleOutPolygonTriangulator::WritePathInnerFan(vertexData, 3, fPath);
Chris Daltonb5391d92020-05-24 14:55:54 -0600147}
Chris Daltone2067642020-09-23 11:07:20 -0600148
Chris Dalton0e543092020-11-03 14:09:16 -0700149class GrStrokeTessellateOp::TestingOnly_Benchmark : public Benchmark {
Chris Dalton2882e702020-11-02 12:43:06 -0700150public:
Chris Dalton0e543092020-11-03 14:09:16 -0700151 TestingOnly_Benchmark(float matrixScale, const char* suffix) : fMatrixScale(matrixScale) {
152 fName.printf("tessellate_GrStrokeTessellateOp_prepare%s", suffix);
Chris Dalton2882e702020-11-02 12:43:06 -0700153 }
154
155private:
156 const char* onGetName() override { return fName.c_str(); }
Chris Daltone2067642020-09-23 11:07:20 -0600157 bool isSuitableFor(Backend backend) final { return backend == kNonRendering_Backend; }
158
159 void onDelayedSetup() override {
Chris Dalton90ad0fe2020-11-09 14:13:39 -0700160 fTarget = std::make_unique<GrMockOpTarget>(make_mock_context());
Chris Daltone2067642020-09-23 11:07:20 -0600161 fPath.reset().moveTo(0, 0);
162 for (int i = 0; i < kNumCubicsInChalkboard/2; ++i) {
Chris Dalton2882e702020-11-02 12:43:06 -0700163 fPath.cubicTo(100, 0, 50, 100, 100, 100);
164 fPath.cubicTo(0, -100, 200, 100, 0, 0);
Chris Daltone2067642020-09-23 11:07:20 -0600165 }
166 fStrokeRec.setStrokeStyle(8);
167 fStrokeRec.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kMiter_Join, 4);
168 }
169
170 void onDraw(int loops, SkCanvas*) final {
Chris Dalton0e543092020-11-03 14:09:16 -0700171 if (!fTarget->mockContext()) {
Chris Daltone2067642020-09-23 11:07:20 -0600172 SkDebugf("ERROR: could not create mock context.");
173 return;
174 }
175 for (int i = 0; i < loops; ++i) {
Chris Dalton0e543092020-11-03 14:09:16 -0700176 GrStrokeTessellateOp op(GrAAType::kMSAA, SkMatrix::Scale(fMatrixScale, fMatrixScale),
177 fStrokeRec, fPath, GrPaint());
178 op.fTarget = fTarget.get();
179 op.prepareBuffers();
Chris Daltone2067642020-09-23 11:07:20 -0600180 }
181 }
182
Chris Dalton2882e702020-11-02 12:43:06 -0700183 const float fMatrixScale;
184 SkString fName;
Chris Dalton90ad0fe2020-11-09 14:13:39 -0700185 std::unique_ptr<GrMockOpTarget> fTarget;
Chris Daltone2067642020-09-23 11:07:20 -0600186 SkPath fPath;
187 SkStrokeRec fStrokeRec = SkStrokeRec(SkStrokeRec::kFill_InitStyle);
Chris Daltone2067642020-09-23 11:07:20 -0600188};
189
Chris Dalton0e543092020-11-03 14:09:16 -0700190DEF_BENCH( return new GrStrokeTessellateOp::TestingOnly_Benchmark(1, ""); )
191DEF_BENCH( return new GrStrokeTessellateOp::TestingOnly_Benchmark(5, "_one_chop"); )
Chris Daltonc2a17462020-12-09 16:46:22 -0700192
193class GrStrokeIndirectOp::Benchmark : public ::Benchmark {
Chris Daltonad482802020-12-28 09:02:47 -0700194protected:
195 Benchmark(const char* nameSuffix, SkPaint::Join join) : fJoin(join) {
Chris Daltonc2a17462020-12-09 16:46:22 -0700196 fName.printf("tessellate_GrStrokeIndirectOpBench%s", nameSuffix);
197 }
198
Chris Daltonad482802020-12-28 09:02:47 -0700199 const SkPaint::Join fJoin;
Chris Daltonc2a17462020-12-09 16:46:22 -0700200
Chris Daltonad482802020-12-28 09:02:47 -0700201private:
202 const char* onGetName() final { return fName.c_str(); }
203 bool isSuitableFor(Backend backend) final { return backend == kNonRendering_Backend; }
204 void onDelayedSetup() final {
Chris Daltonc2a17462020-12-09 16:46:22 -0700205 fTarget = std::make_unique<GrMockOpTarget>(make_mock_context());
Chris Daltonc2a17462020-12-09 16:46:22 -0700206 fStrokeRec.setStrokeStyle(8);
207 fStrokeRec.setStrokeParams(SkPaint::kButt_Cap, fJoin, 4);
Chris Daltonad482802020-12-28 09:02:47 -0700208 this->setupPaths(&fPaths);
Chris Daltonc2a17462020-12-09 16:46:22 -0700209 }
Chris Daltonc2a17462020-12-09 16:46:22 -0700210 void onDraw(int loops, SkCanvas*) final {
211 if (!fTarget->mockContext()) {
212 SkDebugf("ERROR: could not create mock context.");
213 return;
214 }
215 for (int i = 0; i < loops; ++i) {
Chris Daltonad482802020-12-28 09:02:47 -0700216 for (const SkPath& path : fPaths) {
217 GrStrokeIndirectOp op(GrAAType::kMSAA, SkMatrix::I(), path, fStrokeRec, GrPaint());
218 op.prePrepareResolveLevels(fTarget->allocator());
219 op.prepareBuffers(fTarget.get());
220 }
221 fTarget->resetAllocator();
222 }
223 }
224 virtual void setupPaths(SkTArray<SkPath>*) = 0;
225
226 SkString fName;
227 std::unique_ptr<GrMockOpTarget> fTarget;
228 SkTArray<SkPath> fPaths;
229 SkStrokeRec fStrokeRec{SkStrokeRec::kHairline_InitStyle};
230};
231
232class StrokeIndirectBenchmark : public GrStrokeIndirectOp::Benchmark {
233public:
234 StrokeIndirectBenchmark(const char* nameSuffix, SkPaint::Join join, std::vector<SkPoint> pts)
235 : Benchmark(nameSuffix, join), fPts(std::move(pts)) {}
236
237private:
238 void setupPaths(SkTArray<SkPath>* paths) final {
239 SkPath& path = paths->push_back();
240 if (fJoin == SkPaint::kRound_Join) {
241 path.reset().moveTo(fPts.back());
242 for (size_t i = 0; i < kNumCubicsInChalkboard/fPts.size(); ++i) {
243 for (size_t j = 0; j < fPts.size(); ++j) {
244 path.lineTo(fPts[j]);
245 }
246 }
247 } else {
248 path.reset().moveTo(fPts[0]);
249 for (int i = 0; i < kNumCubicsInChalkboard/2; ++i) {
250 if (fPts.size() == 4) {
251 path.cubicTo(fPts[1], fPts[2], fPts[3]);
252 path.cubicTo(fPts[2], fPts[1], fPts[0]);
253 } else {
254 SkASSERT(fPts.size() == 3);
255 path.quadTo(fPts[1], fPts[2]);
256 path.quadTo(fPts[2], fPts[1]);
257 }
258 }
Chris Daltonc2a17462020-12-09 16:46:22 -0700259 }
260 }
261
Chris Daltonad482802020-12-28 09:02:47 -0700262 const std::vector<SkPoint> fPts;
Chris Daltonc2a17462020-12-09 16:46:22 -0700263};
264
Chris Daltonad482802020-12-28 09:02:47 -0700265DEF_BENCH( return new StrokeIndirectBenchmark(
Chris Daltonc2a17462020-12-09 16:46:22 -0700266 "_inflect1", SkPaint::kBevel_Join, {{0,0}, {100,0}, {0,100}, {100,100}}); )
267
Chris Daltonad482802020-12-28 09:02:47 -0700268DEF_BENCH( return new StrokeIndirectBenchmark(
Chris Daltonc2a17462020-12-09 16:46:22 -0700269 "_inflect2", SkPaint::kBevel_Join, {{37,162}, {412,160}, {249,65}, {112,360}}); )
270
Chris Daltonad482802020-12-28 09:02:47 -0700271DEF_BENCH( return new StrokeIndirectBenchmark(
Chris Daltonc2a17462020-12-09 16:46:22 -0700272 "_loop", SkPaint::kBevel_Join, {{0,0}, {100,0}, {0,100}, {0,0}}); )
273
Chris Daltonad482802020-12-28 09:02:47 -0700274DEF_BENCH( return new StrokeIndirectBenchmark(
Chris Daltonc2a17462020-12-09 16:46:22 -0700275 "_nochop", SkPaint::kBevel_Join, {{0,0}, {50,0}, {100,50}, {100,100}}); )
276
Chris Daltonad482802020-12-28 09:02:47 -0700277DEF_BENCH( return new StrokeIndirectBenchmark(
Chris Daltonc2a17462020-12-09 16:46:22 -0700278 "_quad", SkPaint::kBevel_Join, {{0,0}, {50,100}, {100,0}}); )
279
Chris Daltonad482802020-12-28 09:02:47 -0700280DEF_BENCH( return new StrokeIndirectBenchmark(
Chris Daltonc2a17462020-12-09 16:46:22 -0700281 "_roundjoin", SkPaint::kRound_Join, {{0,0}, {50,100}, {100,0}}); )
Chris Daltonad482802020-12-28 09:02:47 -0700282
283class SingleVerbStrokeIndirectBenchmark : public GrStrokeIndirectOp::Benchmark {
284public:
285 SingleVerbStrokeIndirectBenchmark(const char* nameSuffix, SkPathVerb verb)
286 : Benchmark(nameSuffix, SkPaint::kBevel_Join), fVerb(verb) {}
287
288private:
289 void setupPaths(SkTArray<SkPath>* paths) override {
290 SkRandom rand;
291 for (int i = 0; i < kNumCubicsInChalkboard; ++i) {
292 switch (fVerb) {
293 case SkPathVerb::kQuad:
294 paths->push_back().quadTo(rand.nextF(), rand.nextF(), rand.nextF(),
295 rand.nextF());
296 break;
297 case SkPathVerb::kCubic:
298 switch (i % 3) {
299 case 0:
300 paths->push_back().cubicTo(100, 0, 0, 100, 100, 100); // 1 inflection.
301 break;
302 case 1:
303 paths->push_back().cubicTo(100, 0, 0, 100, 0, 0); // loop.
304 break;
305 case 2:
306 paths->push_back().cubicTo(50, 0, 100, 50, 100, 100); // no chop.
307 break;
308 }
309 break;
310 default:
311 SkUNREACHABLE;
312 }
313 }
314 }
315
316 const SkPathVerb fVerb;
317};
318
319DEF_BENCH( return new SingleVerbStrokeIndirectBenchmark("_singlequads", SkPathVerb::kQuad); )
320DEF_BENCH( return new SingleVerbStrokeIndirectBenchmark("_singlecubics", SkPathVerb::kCubic); )