Update rive-cpp to 2.0 version
[platform/core/uifw/rive-tizen.git] / submodule / skia / bench / TessellateBench.cpp
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"
9 #include "include/gpu/GrDirectContext.h"
10 #include "src/core/SkPathPriv.h"
11 #include "src/core/SkRectPriv.h"
12 #include "src/gpu/ganesh/GrDirectContextPriv.h"
13 #include "src/gpu/ganesh/mock/GrMockOpTarget.h"
14 #include "src/gpu/ganesh/tessellate/PathTessellator.h"
15 #include "src/gpu/ganesh/tessellate/StrokeTessellator.h"
16 #include "src/gpu/tessellate/AffineMatrix.h"
17 #include "src/gpu/tessellate/MiddleOutPolygonTriangulator.h"
18 #include "src/gpu/tessellate/WangsFormula.h"
19 #include "tools/ToolUtils.h"
20 #include <vector>
21
22 namespace skgpu::v1 {
23
24 // This is the number of cubics in desk_chalkboard.skp. (There are no quadratics in the chalkboard.)
25 constexpr static int kNumCubicsInChalkboard = 47182;
26
27 static sk_sp<GrDirectContext> make_mock_context() {
28     GrMockOptions mockOptions;
29     mockOptions.fDrawInstancedSupport = true;
30     mockOptions.fMapBufferFlags = GrCaps::kCanMap_MapFlag;
31     mockOptions.fConfigOptions[(int)GrColorType::kAlpha_8].fRenderability =
32             GrMockOptions::ConfigOptions::Renderability::kMSAA;
33     mockOptions.fConfigOptions[(int)GrColorType::kAlpha_8].fTexturable = true;
34     mockOptions.fIntegerSupport = true;
35
36     GrContextOptions ctxOptions;
37     ctxOptions.fGpuPathRenderers = GpuPathRenderers::kTessellation;
38
39     return GrDirectContext::MakeMock(&mockOptions, ctxOptions);
40 }
41
42 static SkPath make_cubic_path(int maxPow2) {
43     SkRandom rand;
44     SkPath path;
45     for (int i = 0; i < kNumCubicsInChalkboard/2; ++i) {
46         float x = std::ldexp(rand.nextF(), (i % maxPow2)) / 1e3f;
47         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
53 static SkPath make_conic_path() {
54     SkRandom rand;
55     SkPath path;
56     for (int i = 0; i < kNumCubicsInChalkboard / 40; ++i) {
57         for (int j = -10; j <= 10; j++) {
58             const float x = std::ldexp(rand.nextF(), (i % 18)) / 1e3f;
59             const float w = std::ldexp(1 + rand.nextF(), j);
60             path.conicTo(111.625f * x, 308.188f * x, 764.62f * x, -435.688f * x, w);
61         }
62     }
63     return path;
64 }
65
66 SK_MAYBE_UNUSED static SkPath make_quad_path(int maxPow2) {
67     SkRandom rand;
68     SkPath path;
69     for (int i = 0; i < kNumCubicsInChalkboard; ++i) {
70         float x = std::ldexp(rand.nextF(), (i % maxPow2)) / 1e3f;
71         path.quadTo(111.625f * x, 308.188f * x, 764.62f * x, -435.688f * x);
72     }
73     return path;
74 }
75
76 SK_MAYBE_UNUSED static SkPath make_line_path(int maxPow2) {
77     SkRandom rand;
78     SkPath path;
79     for (int i = 0; i < kNumCubicsInChalkboard; ++i) {
80         float x = std::ldexp(rand.nextF(), (i % maxPow2)) / 1e3f;
81         path.lineTo(764.62f * x, -435.688f * x);
82     }
83     return path;
84 }
85
86 // This serves as a base class for benchmarking individual methods on PathTessellateOp.
87 class PathTessellateBenchmark : public Benchmark {
88 public:
89     PathTessellateBenchmark(const char* subName, const SkPath& p, const SkMatrix& m)
90             : fPath(p), fMatrix(m) {
91         fName.printf("tessellate_%s", subName);
92     }
93
94     const char* onGetName() override { return fName.c_str(); }
95     bool isSuitableFor(Backend backend) final { return backend == kNonRendering_Backend; }
96
97 protected:
98     void onDelayedSetup() override {
99         fTarget = std::make_unique<GrMockOpTarget>(make_mock_context());
100     }
101
102     void onDraw(int loops, SkCanvas*) final {
103         if (!fTarget->mockContext()) {
104             SkDebugf("ERROR: could not create mock context.");
105             return;
106         }
107         for (int i = 0; i < loops; ++i) {
108             this->runBench();
109             fTarget->resetAllocator();
110         }
111     }
112
113     virtual void runBench() = 0;
114
115     SkString fName;
116     std::unique_ptr<GrMockOpTarget> fTarget;
117     const SkPath fPath;
118     const SkMatrix fMatrix;
119 };
120
121 #define DEF_PATH_TESS_BENCH(NAME, PATH, MATRIX) \
122     class PathTessellateBenchmark_##NAME : public PathTessellateBenchmark { \
123     public: \
124         PathTessellateBenchmark_##NAME() : PathTessellateBenchmark(#NAME, (PATH), (MATRIX)) {} \
125         void runBench() override; \
126     }; \
127     DEF_BENCH( return new PathTessellateBenchmark_##NAME(); ); \
128     void PathTessellateBenchmark_##NAME::runBench()
129
130 static const SkMatrix gAlmostIdentity = SkMatrix::MakeAll(
131         1.0001f, 0.0001f, 0.0001f,
132         -.0001f, 0.9999f, -.0001f,
133               0,       0,       1);
134
135 DEF_PATH_TESS_BENCH(GrPathCurveTessellator, make_cubic_path(8), SkMatrix::I()) {
136     SkArenaAlloc arena(1024);
137     GrPipeline noVaryingsPipeline(GrScissorTest::kDisabled, SkBlendMode::kSrcOver,
138                                   skgpu::Swizzle::RGBA());
139     auto tess = PathCurveTessellator::Make(&arena,
140                                            fTarget->caps().shaderCaps()->infinitySupport());
141     tess->prepare(fTarget.get(),
142                   fMatrix,
143                   {gAlmostIdentity, fPath, SK_PMColor4fTRANSPARENT},
144                   fPath.countVerbs());
145 }
146
147 DEF_PATH_TESS_BENCH(GrPathWedgeTessellator, make_cubic_path(8), SkMatrix::I()) {
148     SkArenaAlloc arena(1024);
149     GrPipeline noVaryingsPipeline(GrScissorTest::kDisabled, SkBlendMode::kSrcOver,
150                                   skgpu::Swizzle::RGBA());
151     auto tess = PathWedgeTessellator::Make(&arena,
152                                            fTarget->caps().shaderCaps()->infinitySupport());
153     tess->prepare(fTarget.get(),
154                   fMatrix,
155                   {gAlmostIdentity, fPath, SK_PMColor4fTRANSPARENT},
156                   fPath.countVerbs());
157 }
158
159 static void benchmark_wangs_formula_cubic_log2(const SkMatrix& matrix, const SkPath& path) {
160     int sum = 0;
161     wangs_formula::VectorXform xform(matrix);
162     for (auto [verb, pts, w] : SkPathPriv::Iterate(path)) {
163         if (verb == SkPathVerb::kCubic) {
164             sum += wangs_formula::cubic_log2(4, pts, xform);
165         }
166     }
167     // Don't let the compiler optimize away wangs_formula::cubic_log2.
168     if (sum <= 0) {
169         SK_ABORT("sum should be > 0.");
170     }
171 }
172
173 DEF_PATH_TESS_BENCH(wangs_formula_cubic_log2, make_cubic_path(18), SkMatrix::I()) {
174     benchmark_wangs_formula_cubic_log2(fMatrix, fPath);
175 }
176
177 DEF_PATH_TESS_BENCH(wangs_formula_cubic_log2_scale, make_cubic_path(18),
178                     SkMatrix::Scale(1.1f, 0.9f)) {
179     benchmark_wangs_formula_cubic_log2(fMatrix, fPath);
180 }
181
182 DEF_PATH_TESS_BENCH(wangs_formula_cubic_log2_affine, make_cubic_path(18),
183                     SkMatrix::MakeAll(.9f,0.9f,0,  1.1f,1.1f,0, 0,0,1)) {
184     benchmark_wangs_formula_cubic_log2(fMatrix, fPath);
185 }
186
187 static void benchmark_wangs_formula_conic(const SkMatrix& matrix, const SkPath& path) {
188     int sum = 0;
189     wangs_formula::VectorXform xform(matrix);
190     for (auto [verb, pts, w] : SkPathPriv::Iterate(path)) {
191         if (verb == SkPathVerb::kConic) {
192             sum += wangs_formula::conic(4, pts, *w, xform);
193         }
194     }
195     // Don't let the compiler optimize away wangs_formula::conic.
196     if (sum <= 0) {
197         SK_ABORT("sum should be > 0.");
198     }
199 }
200
201 static void benchmark_wangs_formula_conic_log2(const SkMatrix& matrix, const SkPath& path) {
202     int sum = 0;
203     wangs_formula::VectorXform xform(matrix);
204     for (auto [verb, pts, w] : SkPathPriv::Iterate(path)) {
205         if (verb == SkPathVerb::kConic) {
206             sum += wangs_formula::conic_log2(4, pts, *w, xform);
207         }
208     }
209     // Don't let the compiler optimize away wangs_formula::conic.
210     if (sum <= 0) {
211         SK_ABORT("sum should be > 0.");
212     }
213 }
214
215 DEF_PATH_TESS_BENCH(wangs_formula_conic, make_conic_path(), SkMatrix::I()) {
216     benchmark_wangs_formula_conic(fMatrix, fPath);
217 }
218
219 DEF_PATH_TESS_BENCH(wangs_formula_conic_log2, make_conic_path(), SkMatrix::I()) {
220     benchmark_wangs_formula_conic_log2(fMatrix, fPath);
221 }
222
223 DEF_PATH_TESS_BENCH(middle_out_triangulation,
224                     ToolUtils::make_star(SkRect::MakeWH(500, 500), kNumCubicsInChalkboard),
225                     SkMatrix::I()) {
226     // Conservative estimate of triangulation (see PathStencilCoverOp)
227     const int maxVerts = 3 * (kNumCubicsInChalkboard - 2);
228
229     sk_sp<const GrBuffer> buffer;
230     int baseVertex;
231     VertexWriter vertexWriter = fTarget->makeVertexWriter(
232             sizeof(SkPoint), maxVerts, &buffer, &baseVertex);
233     tess::AffineMatrix m(gAlmostIdentity);
234     for (tess::PathMiddleOutFanIter it(fPath); !it.done();) {
235         for (auto [p0, p1, p2] : it.nextStack()) {
236             vertexWriter << m.map2Points(p0, p1) << m.mapPoint(p2);
237         }
238     }
239 }
240
241 using PathStrokeList = StrokeTessellator::PathStrokeList;
242 using MakePathStrokesFn = std::vector<PathStrokeList>(*)();
243
244 static std::vector<PathStrokeList> make_simple_cubic_path() {
245     auto path = SkPath().moveTo(0, 0);
246     for (int i = 0; i < kNumCubicsInChalkboard/2; ++i) {
247         path.cubicTo(100, 0, 50, 100, 100, 100);
248         path.cubicTo(0, -100, 200, 100, 0, 0);
249     }
250     SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
251     stroke.setStrokeStyle(8);
252     stroke.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kMiter_Join, 4);
253     return {{path, stroke, SK_PMColor4fWHITE}};
254 }
255
256 // Generates a list of paths that resemble the MotionMark benchmark.
257 static std::vector<PathStrokeList> make_motionmark_paths() {
258     std::vector<PathStrokeList> pathStrokes;
259     SkRandom rand;
260     for (int i = 0; i < 8702; ++i) {
261         // The number of paths with a given number of verbs in the MotionMark bench gets cut in half
262         // every time the number of verbs increases by 1.
263         int numVerbs = 28 - SkNextLog2(rand.nextRangeU(0, (1 << 27) - 1));
264         SkPath path;
265         for (int j = 0; j < numVerbs; ++j) {
266             switch (rand.nextU() & 3) {
267                 case 0:
268                 case 1:
269                     path.lineTo(rand.nextRangeF(0, 150), rand.nextRangeF(0, 150));
270                     break;
271                 case 2:
272                     if (rand.nextULessThan(10) == 0) {
273                         // Cusp.
274                         auto [x, y] = (path.isEmpty())
275                                 ? SkPoint{0,0}
276                                 : SkPathPriv::PointData(path)[path.countPoints() - 1];
277                         path.quadTo(x + rand.nextRangeF(0, 150), y, x - rand.nextRangeF(0, 150), y);
278                     } else {
279                         path.quadTo(rand.nextRangeF(0, 150), rand.nextRangeF(0, 150),
280                                     rand.nextRangeF(0, 150), rand.nextRangeF(0, 150));
281                     }
282                     break;
283                 case 3:
284                     if (rand.nextULessThan(10) == 0) {
285                         // Cusp.
286                         float y = (path.isEmpty())
287                                 ? 0 : SkPathPriv::PointData(path)[path.countPoints() - 1].fY;
288                         path.cubicTo(rand.nextRangeF(0, 150), y, rand.nextRangeF(0, 150), y,
289                                      rand.nextRangeF(0, 150), y);
290                     } else {
291                         path.cubicTo(rand.nextRangeF(0, 150), rand.nextRangeF(0, 150),
292                                      rand.nextRangeF(0, 150), rand.nextRangeF(0, 150),
293                                      rand.nextRangeF(0, 150), rand.nextRangeF(0, 150));
294                     }
295                     break;
296             }
297         }
298         SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
299         // The number of paths with a given stroke width in the MotionMark bench gets cut in half
300         // every time the stroke width increases by 1.
301         float strokeWidth = 21 - log2f(rand.nextRangeF(0, 1 << 20));
302         stroke.setStrokeStyle(strokeWidth);
303         stroke.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kBevel_Join, 0);
304         pathStrokes.emplace_back(path, stroke, SK_PMColor4fWHITE);
305     }
306     return pathStrokes;
307 }
308
309 using PatchAttribs = tess::PatchAttribs;
310
311 class TessPrepareBench : public Benchmark {
312 public:
313     TessPrepareBench(MakePathStrokesFn makePathStrokesFn,
314                      PatchAttribs attribs,
315                      float matrixScale,
316                      const char* suffix)
317             : fMakePathStrokesFn(makePathStrokesFn)
318             , fPatchAttribs(attribs)
319             , fMatrixScale(matrixScale) {
320         fName.printf("tessellate_%s", suffix);
321     }
322
323 private:
324     const char* onGetName() override { return fName.c_str(); }
325     bool isSuitableFor(Backend backend) final { return backend == kNonRendering_Backend; }
326
327     void onDelayedSetup() override {
328         fTarget = std::make_unique<GrMockOpTarget>(make_mock_context());
329         if (!fTarget->mockContext()) {
330             SkDebugf("ERROR: could not create mock context.");
331             return;
332         }
333
334         fPathStrokes = fMakePathStrokesFn();
335         for (size_t i = 0; i < fPathStrokes.size(); ++i) {
336             if (i + 1 < fPathStrokes.size()) {
337                 fPathStrokes[i].fNext = &fPathStrokes[i + 1];
338             }
339             fTotalVerbCount += fPathStrokes[i].fPath.countVerbs();
340         }
341
342         fTessellator = std::make_unique<StrokeTessellator>(fPatchAttribs);
343     }
344
345     void onDraw(int loops, SkCanvas*) final {
346         for (int i = 0; i < loops; ++i) {
347             fTessellator->prepare(fTarget.get(),
348                                   SkMatrix::Scale(fMatrixScale, fMatrixScale),
349                                   fPathStrokes.data(),
350                                   fTotalVerbCount);
351             fTarget->resetAllocator();
352         }
353     }
354
355     SkString fName;
356     MakePathStrokesFn fMakePathStrokesFn;
357     const PatchAttribs fPatchAttribs;
358     float fMatrixScale;
359     std::unique_ptr<GrMockOpTarget> fTarget;
360     std::vector<PathStrokeList> fPathStrokes;
361     std::unique_ptr<StrokeTessellator> fTessellator;
362     SkArenaAlloc fPersistentArena{1024};
363     int fTotalVerbCount = 0;
364 };
365
366 DEF_BENCH(return new TessPrepareBench(
367         make_simple_cubic_path, PatchAttribs::kNone, 1,
368         "GrStrokeFixedCountTessellator");
369 )
370
371 DEF_BENCH(return new TessPrepareBench(
372         make_simple_cubic_path, PatchAttribs::kNone, 5,
373         "GrStrokeFixedCountTessellator_one_chop");
374 )
375
376 DEF_BENCH(return new TessPrepareBench(
377         make_motionmark_paths, PatchAttribs::kStrokeParams, 1,
378         "GrStrokeFixedCountTessellator_motionmark");
379 )
380
381 }  // namespace skgpu::v1