blob: 2d31edc470e9d3335dd88bdc8d3874b2b3901248 [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 Daltonb5391d92020-05-24 14:55:54 -060016#include "src/gpu/tessellate/GrResolveLevelCounter.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 Dalton7f0b8972020-04-23 15:52:24 -060020
21// This is the number of cubics in desk_chalkboard.skp. (There are no quadratics in the chalkboard.)
22constexpr static int kNumCubicsInChalkboard = 47182;
23
Chris Dalton90ad0fe2020-11-09 14:13:39 -070024static sk_sp<GrDirectContext> make_mock_context() {
25 GrMockOptions mockOptions;
26 mockOptions.fDrawInstancedSupport = true;
27 mockOptions.fMaxTessellationSegments = 64;
28 mockOptions.fMapBufferFlags = GrCaps::kCanMap_MapFlag;
29 mockOptions.fConfigOptions[(int)GrColorType::kAlpha_8].fRenderability =
30 GrMockOptions::ConfigOptions::Renderability::kMSAA;
31 mockOptions.fConfigOptions[(int)GrColorType::kAlpha_8].fTexturable = true;
32 mockOptions.fIntegerSupport = true;
33
34 GrContextOptions ctxOptions;
35 ctxOptions.fGpuPathRenderers = GpuPathRenderers::kTessellation;
36
37 return GrDirectContext::MakeMock(&mockOptions, ctxOptions);
38}
39
Chris Dalton7f0b8972020-04-23 15:52:24 -060040static SkPath make_cubic_path() {
41 SkRandom rand;
42 SkPath path;
43 for (int i = 0; i < kNumCubicsInChalkboard/2; ++i) {
Chris Dalton2f2d81c2020-05-13 17:57:37 -060044 float x = std::ldexp(rand.nextF(), (i % 18)) / 1e3f;
Chris Dalton7f0b8972020-04-23 15:52:24 -060045 path.cubicTo(111.625f*x, 308.188f*x, 764.62f*x, -435.688f*x, 742.63f*x, 85.187f*x);
46 path.cubicTo(764.62f*x, -435.688f*x, 111.625f*x, 308.188f*x, 0, 0);
47 }
48 return path;
49}
50
Chris Dalton078f8752020-07-30 19:50:46 -060051// This serves as a base class for benchmarking individual methods on GrPathTessellateOp.
52class GrPathTessellateOp::TestingOnly_Benchmark : public Benchmark {
Chris Dalton7f0b8972020-04-23 15:52:24 -060053public:
54 TestingOnly_Benchmark(const char* subName, SkPath path, const SkMatrix& m)
Chris Daltonb96995d2020-06-04 16:44:29 -060055 : fOp(m, path, GrPaint(), GrAAType::kMSAA, GrTessellationPathRenderer::OpFlags::kNone) {
Chris Dalton7f0b8972020-04-23 15:52:24 -060056 fName.printf("tessellate_%s", subName);
57 }
58
59 const char* onGetName() override { return fName.c_str(); }
60 bool isSuitableFor(Backend backend) final { return backend == kNonRendering_Backend; }
61
Chris Daltonb5391d92020-05-24 14:55:54 -060062 class prepareMiddleOutStencilGeometry;
63 class prepareMiddleOutStencilGeometry_indirect;
64 class prepareIndirectOuterCubics;
65 class prepareTessellatedOuterCubics;
66 class prepareTessellatedCubicWedges;
67 class wangs_formula_cubic_log2;
68 class wangs_formula_cubic_log2_scale;
69 class wangs_formula_cubic_log2_affine;
70 class middle_out_triangulation;
Chris Dalton7f0b8972020-04-23 15:52:24 -060071
72private:
Chris Dalton0e543092020-11-03 14:09:16 -070073 void onDelayedSetup() override {
Chris Dalton90ad0fe2020-11-09 14:13:39 -070074 fTarget = std::make_unique<GrMockOpTarget>(make_mock_context());
Chris Dalton0e543092020-11-03 14:09:16 -070075 }
76
Chris Dalton7f0b8972020-04-23 15:52:24 -060077 void onDraw(int loops, SkCanvas*) final {
Chris Dalton0e543092020-11-03 14:09:16 -070078 if (!fTarget->mockContext()) {
Chris Dalton1443c9d2020-05-27 09:43:34 -060079 SkDebugf("ERROR: could not create mock context.");
80 return;
81 }
Chris Dalton7f0b8972020-04-23 15:52:24 -060082 for (int i = 0; i < loops; ++i) {
83 fOp.fTriangleBuffer.reset();
Chris Daltone74cebe2020-09-24 09:32:54 -060084 fOp.fTriangleVertexCount = 0;
Chris Dalton5e1545f2020-09-25 16:24:03 -060085 fOp.fPipelineForStencils = nullptr;
86 fOp.fPipelineForFills = nullptr;
Chris Daltone74cebe2020-09-24 09:32:54 -060087 fOp.fStencilTrianglesProgram = nullptr;
Chris Dalton5e1545f2020-09-25 16:24:03 -060088 fOp.fFillTrianglesProgram = nullptr;
Chris Dalton7f0b8972020-04-23 15:52:24 -060089 fOp.fCubicBuffer.reset();
Chris Daltone74cebe2020-09-24 09:32:54 -060090 fOp.fCubicVertexCount = 0;
Chris Dalton5e1545f2020-09-25 16:24:03 -060091 // Make fStencilCubicsProgram non-null to keep assertions happy.
Chris Daltone74cebe2020-09-24 09:32:54 -060092 fOp.fStencilCubicsProgram = (GrProgramInfo*)-1;
Chris Dalton5e1545f2020-09-25 16:24:03 -060093 fOp.fFillPathProgram = nullptr;
Chris Dalton0e543092020-11-03 14:09:16 -070094 this->runBench(fTarget.get(), &fOp);
95 fTarget->resetAllocator();
Chris Dalton7f0b8972020-04-23 15:52:24 -060096 }
97 }
98
Chris Dalton078f8752020-07-30 19:50:46 -060099 virtual void runBench(GrMeshDrawOp::Target*, GrPathTessellateOp*) = 0;
Chris Dalton7f0b8972020-04-23 15:52:24 -0600100
Chris Dalton078f8752020-07-30 19:50:46 -0600101 GrPathTessellateOp fOp;
Chris Dalton90ad0fe2020-11-09 14:13:39 -0700102 std::unique_ptr<GrMockOpTarget> fTarget;
Chris Dalton7f0b8972020-04-23 15:52:24 -0600103 SkString fName;
104};
105
Chris Daltone2067642020-09-23 11:07:20 -0600106#define DEF_PATH_TESS_BENCH(NAME, PATH, MATRIX, TARGET, OP) \
Chris Dalton078f8752020-07-30 19:50:46 -0600107 class GrPathTessellateOp::TestingOnly_Benchmark::NAME \
108 : public GrPathTessellateOp::TestingOnly_Benchmark { \
Chris Daltonb5391d92020-05-24 14:55:54 -0600109 public: \
110 NAME() : TestingOnly_Benchmark(#NAME, (PATH), (MATRIX)) {} \
Chris Dalton078f8752020-07-30 19:50:46 -0600111 void runBench(GrMeshDrawOp::Target* target, GrPathTessellateOp* op) override; \
Chris Daltonb5391d92020-05-24 14:55:54 -0600112 }; \
Chris Dalton078f8752020-07-30 19:50:46 -0600113 DEF_BENCH( return new GrPathTessellateOp::TestingOnly_Benchmark::NAME(); ); \
114 void GrPathTessellateOp::TestingOnly_Benchmark::NAME::runBench( \
115 GrMeshDrawOp::Target* TARGET, GrPathTessellateOp* op)
Chris Dalton7f0b8972020-04-23 15:52:24 -0600116
Chris Daltone2067642020-09-23 11:07:20 -0600117DEF_PATH_TESS_BENCH(prepareMiddleOutStencilGeometry, make_cubic_path(), SkMatrix::I(), target, op) {
Chris Daltone74cebe2020-09-24 09:32:54 -0600118 // Make fStencilTrianglesProgram non-null so we benchmark the tessellation path with separate
119 // triangles.
120 op->fStencilTrianglesProgram = (GrProgramInfo*)-1;
Chris Daltonb5391d92020-05-24 14:55:54 -0600121 op->prepareMiddleOutTrianglesAndCubics(target);
122}
Chris Dalton7f0b8972020-04-23 15:52:24 -0600123
Chris Daltone2067642020-09-23 11:07:20 -0600124DEF_PATH_TESS_BENCH(prepareMiddleOutStencilGeometry_indirect, make_cubic_path(), SkMatrix::I(),
125 target, op) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600126 GrResolveLevelCounter resolveLevelCounter;
Chris Daltone74cebe2020-09-24 09:32:54 -0600127 op->prepareMiddleOutTrianglesAndCubics(target, &resolveLevelCounter);
Chris Daltonb5391d92020-05-24 14:55:54 -0600128}
Chris Dalton7f0b8972020-04-23 15:52:24 -0600129
Chris Daltone2067642020-09-23 11:07:20 -0600130DEF_PATH_TESS_BENCH(prepareIndirectOuterCubics, make_cubic_path(), SkMatrix::I(), target, op) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600131 GrResolveLevelCounter resolveLevelCounter;
132 resolveLevelCounter.reset(op->fPath, SkMatrix::I(), 4);
133 op->prepareIndirectOuterCubics(target, resolveLevelCounter);
134}
Chris Dalton7f0b8972020-04-23 15:52:24 -0600135
Chris Daltone2067642020-09-23 11:07:20 -0600136DEF_PATH_TESS_BENCH(prepareTessellatedOuterCubics, make_cubic_path(), SkMatrix::I(), target, op) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600137 op->prepareTessellatedOuterCubics(target, kNumCubicsInChalkboard);
138}
Chris Dalton7f0b8972020-04-23 15:52:24 -0600139
Chris Daltone2067642020-09-23 11:07:20 -0600140DEF_PATH_TESS_BENCH(prepareTessellatedCubicWedges, make_cubic_path(), SkMatrix::I(), target, op) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600141 op->prepareTessellatedCubicWedges(target);
142}
Chris Daltonf6bf5162020-05-13 19:18:46 -0600143
Chris Daltonb5391d92020-05-24 14:55:54 -0600144static void benchmark_wangs_formula_cubic_log2(const SkMatrix& matrix, const SkPath& path) {
145 int sum = 0;
146 GrVectorXform xform(matrix);
147 for (auto [verb, pts, w] : SkPathPriv::Iterate(path)) {
148 if (verb == SkPathVerb::kCubic) {
149 sum += GrWangsFormula::cubic_log2(4, pts, xform);
Chris Daltonf6bf5162020-05-13 19:18:46 -0600150 }
151 }
Chris Daltonb5391d92020-05-24 14:55:54 -0600152 // Don't let the compiler optimize away GrWangsFormula::cubic_log2.
153 if (sum <= 0) {
154 SK_ABORT("sum should be > 0.");
155 }
156}
Chris Daltonf6bf5162020-05-13 19:18:46 -0600157
Chris Daltone2067642020-09-23 11:07:20 -0600158DEF_PATH_TESS_BENCH(wangs_formula_cubic_log2, make_cubic_path(), SkMatrix::I(), target, op) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600159 benchmark_wangs_formula_cubic_log2(op->fViewMatrix, op->fPath);
160}
161
Chris Daltone2067642020-09-23 11:07:20 -0600162DEF_PATH_TESS_BENCH(wangs_formula_cubic_log2_scale, make_cubic_path(), SkMatrix::Scale(1.1f, 0.9f),
163 target, op) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600164 benchmark_wangs_formula_cubic_log2(op->fViewMatrix, op->fPath);
165}
166
Chris Daltone2067642020-09-23 11:07:20 -0600167DEF_PATH_TESS_BENCH(wangs_formula_cubic_log2_affine, make_cubic_path(),
168 SkMatrix::MakeAll(.9f,0.9f,0, 1.1f,1.1f,0, 0,0,1), target, op) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600169 benchmark_wangs_formula_cubic_log2(op->fViewMatrix, op->fPath);
170}
171
Chris Daltone2067642020-09-23 11:07:20 -0600172DEF_PATH_TESS_BENCH(middle_out_triangulation,
173 ToolUtils::make_star(SkRect::MakeWH(500, 500), kNumCubicsInChalkboard),
174 SkMatrix::I(), target, op) {
Chris Daltonb5391d92020-05-24 14:55:54 -0600175 int baseVertex;
176 auto vertexData = static_cast<SkPoint*>(target->makeVertexSpace(
177 sizeof(SkPoint), kNumCubicsInChalkboard, nullptr, &baseVertex));
178 GrMiddleOutPolygonTriangulator middleOut(vertexData, 3, kNumCubicsInChalkboard + 2);
179 for (auto [verb, pts, w] : SkPathPriv::Iterate(op->fPath)) {
180 switch (verb) {
181 case SkPathVerb::kMove:
182 middleOut.closeAndMove(pts[0]);
183 break;
184 case SkPathVerb::kLine:
185 middleOut.pushVertex(pts[1]);
186 break;
187 case SkPathVerb::kClose:
188 middleOut.close();
189 break;
190 case SkPathVerb::kQuad:
191 case SkPathVerb::kConic:
192 case SkPathVerb::kCubic:
193 SkUNREACHABLE;
194 }
195 middleOut.closeAndMove(pts[0]);
196 }
197}
Chris Daltone2067642020-09-23 11:07:20 -0600198
Chris Dalton0e543092020-11-03 14:09:16 -0700199class GrStrokeTessellateOp::TestingOnly_Benchmark : public Benchmark {
Chris Dalton2882e702020-11-02 12:43:06 -0700200public:
Chris Dalton0e543092020-11-03 14:09:16 -0700201 TestingOnly_Benchmark(float matrixScale, const char* suffix) : fMatrixScale(matrixScale) {
202 fName.printf("tessellate_GrStrokeTessellateOp_prepare%s", suffix);
Chris Dalton2882e702020-11-02 12:43:06 -0700203 }
204
205private:
206 const char* onGetName() override { return fName.c_str(); }
Chris Daltone2067642020-09-23 11:07:20 -0600207 bool isSuitableFor(Backend backend) final { return backend == kNonRendering_Backend; }
208
209 void onDelayedSetup() override {
Chris Dalton90ad0fe2020-11-09 14:13:39 -0700210 fTarget = std::make_unique<GrMockOpTarget>(make_mock_context());
Chris Daltone2067642020-09-23 11:07:20 -0600211 fPath.reset().moveTo(0, 0);
212 for (int i = 0; i < kNumCubicsInChalkboard/2; ++i) {
Chris Dalton2882e702020-11-02 12:43:06 -0700213 fPath.cubicTo(100, 0, 50, 100, 100, 100);
214 fPath.cubicTo(0, -100, 200, 100, 0, 0);
Chris Daltone2067642020-09-23 11:07:20 -0600215 }
216 fStrokeRec.setStrokeStyle(8);
217 fStrokeRec.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kMiter_Join, 4);
218 }
219
220 void onDraw(int loops, SkCanvas*) final {
Chris Dalton0e543092020-11-03 14:09:16 -0700221 if (!fTarget->mockContext()) {
Chris Daltone2067642020-09-23 11:07:20 -0600222 SkDebugf("ERROR: could not create mock context.");
223 return;
224 }
225 for (int i = 0; i < loops; ++i) {
Chris Dalton0e543092020-11-03 14:09:16 -0700226 GrStrokeTessellateOp op(GrAAType::kMSAA, SkMatrix::Scale(fMatrixScale, fMatrixScale),
227 fStrokeRec, fPath, GrPaint());
228 op.fTarget = fTarget.get();
229 op.prepareBuffers();
Chris Daltone2067642020-09-23 11:07:20 -0600230 }
231 }
232
Chris Dalton2882e702020-11-02 12:43:06 -0700233 const float fMatrixScale;
234 SkString fName;
Chris Dalton90ad0fe2020-11-09 14:13:39 -0700235 std::unique_ptr<GrMockOpTarget> fTarget;
Chris Daltone2067642020-09-23 11:07:20 -0600236 SkPath fPath;
237 SkStrokeRec fStrokeRec = SkStrokeRec(SkStrokeRec::kFill_InitStyle);
Chris Daltone2067642020-09-23 11:07:20 -0600238};
239
Chris Dalton0e543092020-11-03 14:09:16 -0700240DEF_BENCH( return new GrStrokeTessellateOp::TestingOnly_Benchmark(1, ""); )
241DEF_BENCH( return new GrStrokeTessellateOp::TestingOnly_Benchmark(5, "_one_chop"); )