blob: 7788d9b292395563e2c99429166d5818d88ec991 [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"
9#include "include/gpu/GrContext.h"
Chris Daltonf6bf5162020-05-13 19:18:46 -060010#include "src/core/SkPathPriv.h"
Chris Dalton7f0b8972020-04-23 15:52:24 -060011#include "src/gpu/GrContextPriv.h"
12#include "src/gpu/GrOpFlushState.h"
Chris Daltonb5391d92020-05-24 14:55:54 -060013#include "src/gpu/tessellate/GrMiddleOutPolygonTriangulator.h"
14#include "src/gpu/tessellate/GrResolveLevelCounter.h"
Chris Dalton7f0b8972020-04-23 15:52:24 -060015#include "src/gpu/tessellate/GrTessellatePathOp.h"
Chris Daltonf6bf5162020-05-13 19:18:46 -060016#include "src/gpu/tessellate/GrWangsFormula.h"
Chris Daltonf5132a02020-04-27 23:40:03 -060017#include "tools/ToolUtils.h"
Chris Dalton7f0b8972020-04-23 15:52:24 -060018
19// This is the number of cubics in desk_chalkboard.skp. (There are no quadratics in the chalkboard.)
20constexpr static int kNumCubicsInChalkboard = 47182;
21
22static SkPath make_cubic_path() {
23 SkRandom rand;
24 SkPath path;
25 for (int i = 0; i < kNumCubicsInChalkboard/2; ++i) {
Chris Dalton2f2d81c2020-05-13 17:57:37 -060026 float x = std::ldexp(rand.nextF(), (i % 18)) / 1e3f;
Chris Dalton7f0b8972020-04-23 15:52:24 -060027 path.cubicTo(111.625f*x, 308.188f*x, 764.62f*x, -435.688f*x, 742.63f*x, 85.187f*x);
28 path.cubicTo(764.62f*x, -435.688f*x, 111.625f*x, 308.188f*x, 0, 0);
29 }
30 return path;
31}
32
Chris Dalton2f2d81c2020-05-13 17:57:37 -060033// This is a dummy GrMeshDrawOp::Target implementation that just gives back pointers into
34// pre-allocated CPU buffers, rather than allocating and mapping GPU buffers.
35class BenchmarkTarget : public GrMeshDrawOp::Target {
36public:
Chris Dalton1443c9d2020-05-27 09:43:34 -060037 BenchmarkTarget() {
38 GrMockOptions mockOptions;
39 mockOptions.fDrawInstancedSupport = true;
Chris Daltonb96995d2020-06-04 16:44:29 -060040 mockOptions.fMaxTessellationSegments = 64;
Chris Dalton1443c9d2020-05-27 09:43:34 -060041 mockOptions.fMapBufferFlags = GrCaps::kCanMap_MapFlag;
42 mockOptions.fConfigOptions[(int)GrColorType::kAlpha_8].fRenderability =
43 GrMockOptions::ConfigOptions::Renderability::kMSAA;
44 mockOptions.fConfigOptions[(int)GrColorType::kAlpha_8].fTexturable = true;
45 mockOptions.fIntegerSupport = true;
46
47 GrContextOptions ctxOptions;
48 ctxOptions.fGpuPathRenderers = GpuPathRenderers::kTessellation;
49
50 fMockContext = GrContext::MakeMock(&mockOptions, ctxOptions);
51 }
52 const GrContext* mockContext() const { return fMockContext.get(); }
53 const GrCaps& caps() const override { return *fMockContext->priv().caps(); }
54 GrResourceProvider* resourceProvider() const override {
55 return fMockContext->priv().resourceProvider();
56 }
Chris Dalton2f2d81c2020-05-13 17:57:37 -060057 void resetAllocator() { fAllocator.reset(); }
58 SkArenaAlloc* allocator() override { return &fAllocator; }
59 void putBackVertices(int vertices, size_t vertexStride) override { /* no-op */ }
60
61 void* makeVertexSpace(size_t vertexSize, int vertexCount, sk_sp<const GrBuffer>*,
62 int* startVertex) override {
63 if (vertexSize * vertexCount > sizeof(fStaticVertexData)) {
John Stiles616da102020-06-12 14:07:41 -040064 SK_ABORT("FATAL: wanted %zu bytes of static vertex data; only have %zu.\n",
65 vertexSize * vertexCount, SK_ARRAY_COUNT(fStaticVertexData));
Chris Dalton2f2d81c2020-05-13 17:57:37 -060066 }
Chris Daltonb5391d92020-05-24 14:55:54 -060067 *startVertex = 0;
Chris Dalton2f2d81c2020-05-13 17:57:37 -060068 return fStaticVertexData;
69 }
70
71 GrDrawIndexedIndirectCommand* makeDrawIndexedIndirectSpace(
72 int drawCount, sk_sp<const GrBuffer>* buffer, size_t* offsetInBytes) override {
73 int staticBufferCount = (int)SK_ARRAY_COUNT(fStaticDrawIndexedIndirectData);
74 if (drawCount > staticBufferCount) {
John Stiles616da102020-06-12 14:07:41 -040075 SK_ABORT("FATAL: wanted %i static drawIndexedIndirect elements; only have %i.\n",
76 drawCount, staticBufferCount);
Chris Dalton2f2d81c2020-05-13 17:57:37 -060077 }
78 return fStaticDrawIndexedIndirectData;
79 }
80
81#define UNIMPL(...) __VA_ARGS__ override { SK_ABORT("unimplemented."); }
82 UNIMPL(void recordDraw(const GrGeometryProcessor*, const GrSimpleMesh[], int,
83 const GrSurfaceProxy* const[], GrPrimitiveType))
84 UNIMPL(uint16_t* makeIndexSpace(int, sk_sp<const GrBuffer>*, int*))
85 UNIMPL(void* makeVertexSpaceAtLeast(size_t, int, int, sk_sp<const GrBuffer>*, int*, int*))
86 UNIMPL(uint16_t* makeIndexSpaceAtLeast(int, int, sk_sp<const GrBuffer>*, int*, int*))
87 UNIMPL(GrDrawIndirectCommand* makeDrawIndirectSpace(int, sk_sp<const GrBuffer>*, size_t*))
88 UNIMPL(void putBackIndices(int))
89 UNIMPL(GrRenderTargetProxy* proxy() const)
90 UNIMPL(const GrSurfaceProxyView* writeView() const)
91 UNIMPL(const GrAppliedClip* appliedClip() const)
92 UNIMPL(GrAppliedClip detachAppliedClip())
93 UNIMPL(const GrXferProcessor::DstProxyView& dstProxyView() const)
Chris Dalton2f2d81c2020-05-13 17:57:37 -060094 UNIMPL(GrStrikeCache* strikeCache() const)
95 UNIMPL(GrAtlasManager* atlasManager() const)
96 UNIMPL(SkTArray<GrSurfaceProxy*, true>* sampledProxyArray())
Chris Dalton2f2d81c2020-05-13 17:57:37 -060097 UNIMPL(GrDeferredUploadTarget* deferredUploadTarget())
98#undef UNIMPL
99
100private:
Chris Dalton1443c9d2020-05-27 09:43:34 -0600101 sk_sp<GrContext> fMockContext;
Chris Daltonb5391d92020-05-24 14:55:54 -0600102 SkPoint fStaticVertexData[(kNumCubicsInChalkboard + 2) * 8];
Chris Dalton2f2d81c2020-05-13 17:57:37 -0600103 GrDrawIndexedIndirectCommand fStaticDrawIndexedIndirectData[32];
104 SkSTArenaAlloc<1024 * 1024> fAllocator;
105};
Chris Dalton7f0b8972020-04-23 15:52:24 -0600106
107// This serves as a base class for benchmarking individual methods on GrTessellatePathOp.
108class GrTessellatePathOp::TestingOnly_Benchmark : public Benchmark {
109public:
110 TestingOnly_Benchmark(const char* subName, SkPath path, const SkMatrix& m)
Chris Daltonb96995d2020-06-04 16:44:29 -0600111 : fOp(m, path, GrPaint(), GrAAType::kMSAA, GrTessellationPathRenderer::OpFlags::kNone) {
Chris Dalton7f0b8972020-04-23 15:52:24 -0600112 fName.printf("tessellate_%s", subName);
113 }
114
115 const char* onGetName() override { return fName.c_str(); }
116 bool isSuitableFor(Backend backend) final { return backend == kNonRendering_Backend; }
117
Chris Daltonb5391d92020-05-24 14:55:54 -0600118 class prepareMiddleOutStencilGeometry;
119 class prepareMiddleOutStencilGeometry_indirect;
120 class prepareIndirectOuterCubics;
121 class prepareTessellatedOuterCubics;
122 class prepareTessellatedCubicWedges;
123 class wangs_formula_cubic_log2;
124 class wangs_formula_cubic_log2_scale;
125 class wangs_formula_cubic_log2_affine;
126 class middle_out_triangulation;
Chris Dalton7f0b8972020-04-23 15:52:24 -0600127
128private:
129 void onDraw(int loops, SkCanvas*) final {
Chris Dalton1443c9d2020-05-27 09:43:34 -0600130 if (!fTarget.mockContext()) {
131 SkDebugf("ERROR: could not create mock context.");
132 return;
133 }
Chris Dalton7f0b8972020-04-23 15:52:24 -0600134 for (int i = 0; i < loops; ++i) {
135 fOp.fTriangleBuffer.reset();
136 fOp.fDoStencilTriangleBuffer = false;
137 fOp.fDoFillTriangleBuffer = false;
138 fOp.fCubicBuffer.reset();
139 fOp.fStencilCubicsShader = nullptr;
Chris Dalton2f2d81c2020-05-13 17:57:37 -0600140 this->runBench(&fTarget, &fOp);
141 fTarget.resetAllocator();
Chris Dalton7f0b8972020-04-23 15:52:24 -0600142 }
143 }
144
Chris Dalton2f2d81c2020-05-13 17:57:37 -0600145 virtual void runBench(GrMeshDrawOp::Target*, GrTessellatePathOp*) = 0;
Chris Dalton7f0b8972020-04-23 15:52:24 -0600146
Chris Dalton7f0b8972020-04-23 15:52:24 -0600147 GrTessellatePathOp fOp;
Chris Dalton2f2d81c2020-05-13 17:57:37 -0600148 BenchmarkTarget fTarget;
Chris Dalton7f0b8972020-04-23 15:52:24 -0600149 SkString fName;
150};
151
Chris Daltonb5391d92020-05-24 14:55:54 -0600152#define DEF_TESS_BENCH(NAME, PATH, MATRIX, TARGET, OP) \
153 class GrTessellatePathOp::TestingOnly_Benchmark::NAME \
154 : public GrTessellatePathOp::TestingOnly_Benchmark { \
155 public: \
156 NAME() : TestingOnly_Benchmark(#NAME, (PATH), (MATRIX)) {} \
157 void runBench(GrMeshDrawOp::Target* target, GrTessellatePathOp* op) override; \
158 }; \
159 DEF_BENCH( return new GrTessellatePathOp::TestingOnly_Benchmark::NAME(); ); \
160 void GrTessellatePathOp::TestingOnly_Benchmark::NAME::runBench( \
161 GrMeshDrawOp::Target* TARGET, GrTessellatePathOp* op)
Chris Dalton7f0b8972020-04-23 15:52:24 -0600162
Chris Daltonb5391d92020-05-24 14:55:54 -0600163DEF_TESS_BENCH(prepareMiddleOutStencilGeometry, make_cubic_path(), SkMatrix::I(), target, op) {
164 op->prepareMiddleOutTrianglesAndCubics(target);
165}
Chris Dalton7f0b8972020-04-23 15:52:24 -0600166
Chris Daltonb5391d92020-05-24 14:55:54 -0600167DEF_TESS_BENCH(prepareMiddleOutStencilGeometry_indirect, make_cubic_path(), SkMatrix::I(), target,
168 op) {
169 GrResolveLevelCounter resolveLevelCounter;
170 op->prepareMiddleOutTrianglesAndCubics(target, &resolveLevelCounter, true);
171}
Chris Dalton7f0b8972020-04-23 15:52:24 -0600172
Chris Daltonb5391d92020-05-24 14:55:54 -0600173DEF_TESS_BENCH(prepareIndirectOuterCubics, make_cubic_path(), SkMatrix::I(), target, op) {
174 GrResolveLevelCounter resolveLevelCounter;
175 resolveLevelCounter.reset(op->fPath, SkMatrix::I(), 4);
176 op->prepareIndirectOuterCubics(target, resolveLevelCounter);
177}
Chris Dalton7f0b8972020-04-23 15:52:24 -0600178
Chris Daltonb5391d92020-05-24 14:55:54 -0600179DEF_TESS_BENCH(prepareTessellatedOuterCubics, make_cubic_path(), SkMatrix::I(), target, op) {
180 op->prepareTessellatedOuterCubics(target, kNumCubicsInChalkboard);
181}
Chris Dalton7f0b8972020-04-23 15:52:24 -0600182
Chris Daltonb5391d92020-05-24 14:55:54 -0600183DEF_TESS_BENCH(prepareTessellatedCubicWedges, make_cubic_path(), SkMatrix::I(), target, op) {
184 op->prepareTessellatedCubicWedges(target);
185}
Chris Daltonf6bf5162020-05-13 19:18:46 -0600186
Chris Daltonb5391d92020-05-24 14:55:54 -0600187static void benchmark_wangs_formula_cubic_log2(const SkMatrix& matrix, const SkPath& path) {
188 int sum = 0;
189 GrVectorXform xform(matrix);
190 for (auto [verb, pts, w] : SkPathPriv::Iterate(path)) {
191 if (verb == SkPathVerb::kCubic) {
192 sum += GrWangsFormula::cubic_log2(4, pts, xform);
Chris Daltonf6bf5162020-05-13 19:18:46 -0600193 }
194 }
Chris Daltonb5391d92020-05-24 14:55:54 -0600195 // Don't let the compiler optimize away GrWangsFormula::cubic_log2.
196 if (sum <= 0) {
197 SK_ABORT("sum should be > 0.");
198 }
199}
Chris Daltonf6bf5162020-05-13 19:18:46 -0600200
Chris Daltonb5391d92020-05-24 14:55:54 -0600201DEF_TESS_BENCH(wangs_formula_cubic_log2, make_cubic_path(), SkMatrix::I(), target, op) {
202 benchmark_wangs_formula_cubic_log2(op->fViewMatrix, op->fPath);
203}
204
205DEF_TESS_BENCH(wangs_formula_cubic_log2_scale, make_cubic_path(), SkMatrix::Scale(1.1f, 0.9f),
206 target, op) {
207 benchmark_wangs_formula_cubic_log2(op->fViewMatrix, op->fPath);
208}
209
210DEF_TESS_BENCH(wangs_formula_cubic_log2_affine, make_cubic_path(),
211 SkMatrix::MakeAll(.9f,0.9f,0, 1.1f,1.1f,0, 0,0,1), target, op) {
212 benchmark_wangs_formula_cubic_log2(op->fViewMatrix, op->fPath);
213}
214
215DEF_TESS_BENCH(middle_out_triangulation,
216 ToolUtils::make_star(SkRect::MakeWH(500, 500), kNumCubicsInChalkboard),
217 SkMatrix::I(), target, op) {
218 int baseVertex;
219 auto vertexData = static_cast<SkPoint*>(target->makeVertexSpace(
220 sizeof(SkPoint), kNumCubicsInChalkboard, nullptr, &baseVertex));
221 GrMiddleOutPolygonTriangulator middleOut(vertexData, 3, kNumCubicsInChalkboard + 2);
222 for (auto [verb, pts, w] : SkPathPriv::Iterate(op->fPath)) {
223 switch (verb) {
224 case SkPathVerb::kMove:
225 middleOut.closeAndMove(pts[0]);
226 break;
227 case SkPathVerb::kLine:
228 middleOut.pushVertex(pts[1]);
229 break;
230 case SkPathVerb::kClose:
231 middleOut.close();
232 break;
233 case SkPathVerb::kQuad:
234 case SkPathVerb::kConic:
235 case SkPathVerb::kCubic:
236 SkUNREACHABLE;
237 }
238 middleOut.closeAndMove(pts[0]);
239 }
240}