xref: /aosp_15_r20/external/skia/bench/graphite/IntersectionTreeBench.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2021 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 #include "bench/Benchmark.h"
8 #include "include/core/SkPaint.h"
9 #include "include/core/SkPath.h"
10 #include "src/base/SkMathPriv.h"
11 #include "src/base/SkRandom.h"
12 #include "src/gpu/graphite/geom/IntersectionTree.h"
13 #include "tools/ToolUtils.h"
14 #include "tools/flags/CommandLineFlags.h"
15 
16 #if defined(SK_ENABLE_SVG)
17 #include "tools/SvgPathExtractor.h"
18 #endif
19 
20 using namespace skia_private;
21 
22 static DEFINE_string(intersectionTreeFile, "",
23                      "svg or skp for the IntersectionTree bench to sniff paths from.");
24 
25 namespace skgpu::graphite {
26 
27 class IntersectionTreeBench : public Benchmark {
28 protected:
onGetName()29     const char* onGetName() final { return fName.c_str(); }
30 
isSuitableFor(Backend backend)31     bool isSuitableFor(Backend backend) override {
32         return backend == Backend::kNonRendering;
33     }
34 
onDelayedSetup()35     void onDelayedSetup() final {
36         TArray<SkRect> rects;
37         this->gatherRects(&rects);
38         fRectCount = rects.size();
39         fRects = fAlignedAllocator.makeArray<Rect>(fRectCount);
40         for (int i = 0; i < fRectCount; ++i) {
41             fRects[i] = rects[i];
42         }
43         fRectBufferA = fAlignedAllocator.makeArray<Rect>(fRectCount);
44         fRectBufferB = fAlignedAllocator.makeArray<Rect>(fRectCount);
45     }
46 
47     virtual void gatherRects(TArray<SkRect>* rects) = 0;
48 
onDraw(int loops,SkCanvas *)49     void onDraw(int loops, SkCanvas*) final {
50         for (int i = 0; i < loops; ++i) {
51             this->doBench();
52         }
53     }
54 
doBench()55     void doBench() {
56         Rect* rects = fRects;
57         Rect* collided = fRectBufferA;
58         int rectCount = fRectCount;
59         fNumTrees = 0;
60         while (rectCount > 0) {
61             IntersectionTree intersectionTree;
62             int collidedCount = 0;
63             for (int i = 0; i < rectCount; ++i) {
64                 if (!intersectionTree.add(rects[i])) {
65                     collided[collidedCount++] = rects[i];
66                 }
67             }
68             std::swap(rects, collided);
69             if (collided == fRects) {
70                 collided = fRectBufferB;
71             }
72             rectCount = collidedCount;
73             ++fNumTrees;
74         }
75     }
76 
77     SkString fName;
78     SkArenaAlloc fAlignedAllocator{0};
79     int fRectCount;
80     Rect* fRects;
81     Rect* fRectBufferA;
82     Rect* fRectBufferB;
83     int fNumTrees = 0;
84 };
85 
86 class RandomIntersectionBench : public IntersectionTreeBench {
87 public:
RandomIntersectionBench(int numRandomRects)88     RandomIntersectionBench(int numRandomRects) : fNumRandomRects(numRandomRects) {
89         fName.printf("IntersectionTree_%i", numRandomRects);
90     }
91 
92 private:
gatherRects(TArray<SkRect> * rects)93     void gatherRects(TArray<SkRect>* rects) override {
94         SkRandom rand;
95         for (int i = 0; i < fNumRandomRects; ++i) {
96             rects->push_back(SkRect::MakeXYWH(rand.nextRangeF(0, 2000),
97                                               rand.nextRangeF(0, 2000),
98                                               rand.nextRangeF(0, 70),
99                                               rand.nextRangeF(0, 70)));
100         }
101     }
102 
103     const int fNumRandomRects;
104 };
105 
106 class FileIntersectionBench : public IntersectionTreeBench {
107 public:
FileIntersectionBench()108     FileIntersectionBench() {
109         if (FLAGS_intersectionTreeFile.isEmpty()) {
110             return;
111         }
112         const char* filename = strrchr(FLAGS_intersectionTreeFile[0], '/');
113         if (filename) {
114             ++filename;
115         } else {
116             filename = FLAGS_intersectionTreeFile[0];
117         }
118         fName.printf("IntersectionTree_file_%s", filename);
119     }
120 
121 private:
isSuitableFor(Backend backend)122     bool isSuitableFor(Backend backend) final {
123         if (FLAGS_intersectionTreeFile.isEmpty()) {
124             return false;
125         }
126         return IntersectionTreeBench::isSuitableFor(backend);
127     }
128 
gatherRects(TArray<SkRect> * rects)129     void gatherRects(TArray<SkRect>* rects) override {
130         if (FLAGS_intersectionTreeFile.isEmpty()) {
131             return;
132         }
133         auto callback = [&](const SkMatrix& matrix,
134                             const SkPath& path,
135                             const SkPaint& paint) {
136             if (paint.getStyle() == SkPaint::kStroke_Style) {
137                 return;  // Goes to stroker.
138             }
139             if (path.isConvex()) {
140                 return;  // Goes to convex renderer.
141             }
142             int numVerbs = path.countVerbs();
143             SkRect drawBounds = matrix.mapRect(path.getBounds());
144             float gpuFragmentWork = drawBounds.height() * drawBounds.width();
145             float cpuTessellationWork = numVerbs * SkNextLog2(numVerbs);  // N log N.
146             constexpr static float kCpuWeight = 512;
147             constexpr static float kMinNumPixelsToTriangulate = 256 * 256;
148             if (cpuTessellationWork * kCpuWeight + kMinNumPixelsToTriangulate < gpuFragmentWork) {
149                 return;  // Goes to inner triangulator.
150             }
151             rects->push_back(drawBounds);
152         };
153         const char* path = FLAGS_intersectionTreeFile[0];
154         if (const char* ext = strrchr(path, '.'); ext && !strcmp(ext, ".svg")) {
155 #if defined(SK_ENABLE_SVG)
156             ToolUtils::ExtractPathsFromSVG(path, callback);
157 #else
158             SK_ABORT("must compile with svg backend to process svgs");
159 #endif
160         } else {
161             ToolUtils::ExtractPathsFromSKP(path, callback);
162         }
163         SkDebugf(">> Found %i stencil/cover paths in %s <<\n",
164                  rects->size(), FLAGS_intersectionTreeFile[0]);
165     }
166 
onPerCanvasPostDraw(SkCanvas *)167     void onPerCanvasPostDraw(SkCanvas*) override {
168         if (FLAGS_intersectionTreeFile.isEmpty()) {
169             return;
170         }
171         SkDebugf(">> Reordered %s into %i different stencil/cover draws <<\n",
172                  FLAGS_intersectionTreeFile[0], fNumTrees);
173     }
174 };
175 
176 }  // namespace skgpu::graphite
177 
178 DEF_BENCH( return new skgpu::graphite::RandomIntersectionBench(100); )
179 DEF_BENCH( return new skgpu::graphite::RandomIntersectionBench(500); )
180 DEF_BENCH( return new skgpu::graphite::RandomIntersectionBench(1000); )
181 DEF_BENCH( return new skgpu::graphite::RandomIntersectionBench(5000); )
182 DEF_BENCH( return new skgpu::graphite::RandomIntersectionBench(10000); )
183 DEF_BENCH( return new skgpu::graphite::FileIntersectionBench(); )  // Sniffs --intersectionTreeFile
184