xref: /aosp_15_r20/external/skia/bench/ShapesBench.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2016 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/core/SkCanvas.h"
10 #include "include/core/SkPaint.h"
11 #include "include/core/SkRRect.h"
12 #include "include/core/SkString.h"
13 #include "src/base/SkRandom.h"
14 #include "tools/flags/CommandLineFlags.h"
15 
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <functional>
19 
20 using namespace skia_private;
21 
22 #define ENABLE_COMMAND_LINE_SHAPES_BENCH 0
23 
24 #if ENABLE_COMMAND_LINE_SHAPES_BENCH
25 static DEFINE_string(shapesType, "mixed",
26                      "Type of shape to use in ShapesBench. Must be one of: "
27                      "rect, oval, rrect, mixed.");
28 static DEFINE_string(innerShapesType, "none",
29                      "Type of inner shape to use in ShapesBench. Must be one of: "
30                      "none, rect, oval, rrect, mixed.");
31 static DEFINE_int(numShapes, 10000, "Number of shapes to draw in ShapesBench.");
32 static DEFINE_string(shapesSize, "32x32", "Size of shapes to draw in ShapesBench.");
33 static DEFINE_bool(shapesPersp, false, "Use slight perspective tilt in ShapesBench?");
34 #endif
35 
36 /*
37  * This class is used for several benchmarks that draw different primitive Skia shapes at various
38  * sizes. It is used to test both CPU-bound and GPU-bound rendering situations. It draws large
39  * amounts of shapes internally (rather than relying on nanobench selecting lots of loops) in order
40  * to take advantage of instanced rendering approaches.
41  */
42 class ShapesBench : public Benchmark {
43 public:
44     enum ShapesType {
45         kNone_ShapesType,
46         kRect_ShapesType,
47         kOval_ShapesType,
48         kRRect_ShapesType,
49         kMixed_ShapesType
50     };
51 
ShapesBench(ShapesType shapesType,ShapesType innerShapesType,int numShapes,const SkISize & shapesSize,bool perspective)52     ShapesBench(ShapesType shapesType, ShapesType innerShapesType,
53                 int numShapes, const SkISize& shapesSize, bool perspective)
54         : fShapesType(shapesType)
55         , fInnerShapesType(innerShapesType)
56         , fNumShapes(numShapes)
57         , fShapesSize(shapesSize)
58         , fPerspective(perspective) {
59         clampShapeSize();
60     }
61 
62 #if ENABLE_COMMAND_LINE_SHAPES_BENCH
ShapesBench()63     ShapesBench() {
64         if (!strcmp(FLAGS_shapesType[0], "rect")) {
65             fShapesType = kRect_ShapesType;
66         } else if (!strcmp(FLAGS_shapesType[0], "oval")) {
67             fShapesType = kOval_ShapesType;
68         } else if (!strcmp(FLAGS_shapesType[0], "rrect")) {
69             fShapesType = kRRect_ShapesType;
70         } else if (!strcmp(FLAGS_shapesType[0], "mixed")) {
71             fShapesType = kMixed_ShapesType;
72         } else {
73             SkDebugf("Invalid shapesType \"%s\". Must be one of: rect, oval, rrect, mixed.",
74                      FLAGS_shapesType[0]);
75             exit(-1);
76         }
77         if (!strcmp(FLAGS_innerShapesType[0], "none")) {
78             fInnerShapesType = kNone_ShapesType;
79         } else if (!strcmp(FLAGS_innerShapesType[0], "rect")) {
80             fInnerShapesType = kRect_ShapesType;
81         } else if (!strcmp(FLAGS_innerShapesType[0], "oval")) {
82             fInnerShapesType = kOval_ShapesType;
83         } else if (!strcmp(FLAGS_innerShapesType[0], "rrect")) {
84             fInnerShapesType = kRRect_ShapesType;
85         } else if (!strcmp(FLAGS_innerShapesType[0], "mixed")) {
86             fInnerShapesType = kMixed_ShapesType;
87         } else {
88             SkDebugf("Invalid innerShapesType \"%s\". Must be one of: "
89                      "none, rect, oval, rrect, mixed.", FLAGS_innerShapesType[0]);
90             exit(-1);
91         }
92         if (2 != sscanf(FLAGS_shapesSize[0], "%ix%i", &fShapesSize.fWidth, &fShapesSize.fHeight)) {
93             SkDebugf("Could not parse shapesSize from \"%s\". Expected \"%%ix%%i\"\n",
94                      FLAGS_shapesSize[0]);
95             exit(-1);
96         }
97 
98         fNumShapes = FLAGS_numShapes;
99         fPerspective = FLAGS_shapesPersp;
100 
101         clampShapeSize();
102     }
103 #endif
104 
105 private:
clampShapeSize()106     void clampShapeSize() {
107         float maxDiagonal = static_cast<float>(std::min(kBenchWidth, kBenchHeight));
108         float diagonal = sqrtf(static_cast<float>(fShapesSize.width() * fShapesSize.width()) +
109                                static_cast<float>(fShapesSize.height() * fShapesSize.height()));
110         if (diagonal > maxDiagonal) {
111             fShapesSize.fWidth = static_cast<int>(fShapesSize.width() * maxDiagonal / diagonal);
112             fShapesSize.fHeight = static_cast<int>(fShapesSize.height() * maxDiagonal / diagonal);
113         }
114     }
115 
onGetName()116     const char* onGetName() override {
117         const char* shapeTypeNames[] = {
118             "none", "rect", "oval", "rrect", "mixed"
119         };
120 
121         fName.printf("shapes_%s", shapeTypeNames[fShapesType]);
122 
123         if (kNone_ShapesType != fInnerShapesType) {
124             fName.appendf("_inner_%s", shapeTypeNames[fInnerShapesType]);
125         }
126 
127         fName.appendf("_%i_%ix%i", fNumShapes, fShapesSize.width(), fShapesSize.height());
128 
129         if (fPerspective) {
130             fName.append("_persp");
131         }
132 
133         return fName.c_str();
134     }
onGetSize()135     SkISize onGetSize() override { return SkISize::Make(kBenchWidth, kBenchHeight); }
136 
onDelayedSetup()137     void onDelayedSetup() override {
138         SkScalar w = SkIntToScalar(fShapesSize.width());
139         SkScalar h = SkIntToScalar(fShapesSize.height());
140 
141         fRect.setRect(SkRect::MakeXYWH(-w / 2, -h / 2, w, h));
142         fOval.setOval(fRect.rect());
143         fRRect.setNinePatch(fRect.rect(), w / 8, h / 13, w / 11, h / 7);
144 
145         if (kNone_ShapesType != fInnerShapesType) {
146             fRect.inset(w / 7, h / 11, &fInnerRect);
147             fInnerRect.offset(w / 28, h / 44);
148             fInnerOval.setOval(fInnerRect.rect());
149             fInnerRRect.setRectXY(fInnerRect.rect(), w / 13, w / 7);
150         }
151 
152         SkRandom rand;
153         fShapes.push_back_n(fNumShapes);
154         for (int i = 0; i < fNumShapes; i++) {
155             float pad = sqrtf(static_cast<float>(fShapesSize.width() * fShapesSize.width()) +
156                               static_cast<float>(fShapesSize.height() * fShapesSize.height()));
157             fShapes[i].fMatrix.setTranslate(0.5f * pad + rand.nextF() * (kBenchWidth - pad),
158                                             0.5f * pad + rand.nextF() * (kBenchHeight - pad));
159             fShapes[i].fMatrix.preRotate(rand.nextF() * 360.0f);
160             if (fPerspective) {
161                 fShapes[i].fMatrix.setPerspX(0.00015f);
162                 fShapes[i].fMatrix.setPerspY(-0.00015f);
163             }
164             fShapes[i].fColor = rand.nextU() | 0xff808080;
165         }
166         for (int i = 0; i < fNumShapes; i++) {
167             // Do this in a separate loop so mixed shapes get the same random numbers during
168             // placement as non-mixed do.
169             int shapeType = fShapesType;
170             if (kMixed_ShapesType == shapeType) {
171                 shapeType = rand.nextRangeU(kRect_ShapesType, kRRect_ShapesType);
172             }
173             int innerShapeType = fInnerShapesType;
174             if (kMixed_ShapesType == innerShapeType) {
175                 innerShapeType = rand.nextRangeU(kRect_ShapesType, kRRect_ShapesType);
176             }
177             if (kNone_ShapesType == innerShapeType) {
178                 switch (shapeType) {
179                     using namespace std;
180                     using namespace std::placeholders;
181                     case kRect_ShapesType:
182                         fShapes[i].fDraw = bind(&SkCanvas::drawRect, _1, cref(fRect.rect()), _2);
183                         break;
184                     case kOval_ShapesType:
185                         fShapes[i].fDraw = bind(&SkCanvas::drawOval, _1, cref(fOval.rect()), _2);
186                         break;
187                     case kRRect_ShapesType:
188                         fShapes[i].fDraw = bind(&SkCanvas::drawRRect, _1, cref(fRRect), _2);
189                         break;
190                 }
191             } else {
192                 const SkRRect* outer = nullptr;
193                 switch (shapeType) {
194                     case kRect_ShapesType: outer = &fRect; break;
195                     case kOval_ShapesType: outer = &fOval; break;
196                     case kRRect_ShapesType: outer = &fRRect; break;
197                 }
198                 const SkRRect* inner = nullptr;
199                 switch (innerShapeType) {
200                     case kRect_ShapesType: inner = &fInnerRect; break;
201                     case kOval_ShapesType: inner = &fInnerOval; break;
202                     case kRRect_ShapesType: inner = &fInnerRRect; break;
203                 }
204                 fShapes[i].fDraw = std::bind(&SkCanvas::drawDRRect, std::placeholders::_1,
205                                              std::cref(*outer), std::cref(*inner),
206                                              std::placeholders::_2);
207             }
208         }
209     }
210 
onDraw(int loops,SkCanvas * canvas)211     void onDraw(int loops, SkCanvas* canvas) override {
212         SkPaint paint;
213         this->setupPaint(&paint);
214         for (int j = 0; j < loops; j++) {
215             for (int i = 0; i < fNumShapes; i++) {
216                 canvas->save();
217                 canvas->setMatrix(fShapes[i].fMatrix);
218                 paint.setColor(fShapes[i].fColor);
219                 fShapes[i].fDraw(canvas, paint);
220                 canvas->restore();
221             }
222         }
223     }
224 
225     static constexpr int kBenchWidth = 1000;
226     static constexpr int kBenchHeight = 1000;
227 
228     struct ShapeInfo {
229         SkMatrix   fMatrix;
230         SkColor    fColor;
231         std::function<void(SkCanvas*, const SkPaint&)> fDraw;
232     };
233 
234     ShapesType            fShapesType;
235     ShapesType            fInnerShapesType;
236     int                   fNumShapes;
237     SkISize               fShapesSize;
238     bool                  fPerspective;
239     SkString              fName;
240     SkRRect               fRect;
241     SkRRect               fOval;
242     SkRRect               fRRect;
243     SkRRect               fInnerRect;
244     SkRRect               fInnerOval;
245     SkRRect               fInnerRRect;
246     TArray<ShapeInfo>   fShapes;
247 
248 
249     using INHERITED = Benchmark;
250 };
251 
252 #if ENABLE_COMMAND_LINE_SHAPES_BENCH
253 DEF_BENCH(return new ShapesBench;)
254 #else
255 // Small primitives (CPU bound, in theory):
256 DEF_BENCH(return new ShapesBench(ShapesBench::kRect_ShapesType, ShapesBench::kNone_ShapesType,
257                                  10000, SkISize::Make(32, 32), false);)
258 DEF_BENCH(return new ShapesBench(ShapesBench::kOval_ShapesType, ShapesBench::kNone_ShapesType,
259                                  10000, SkISize::Make(32, 32), false);)
260 DEF_BENCH(return new ShapesBench(ShapesBench::kOval_ShapesType, ShapesBench::kNone_ShapesType,
261                                  10000, SkISize::Make(32, 33), false);)
262 DEF_BENCH(return new ShapesBench(ShapesBench::kRRect_ShapesType, ShapesBench::kNone_ShapesType,
263                                  10000, SkISize::Make(32, 32), false);)
264 DEF_BENCH(return new ShapesBench(ShapesBench::kMixed_ShapesType, ShapesBench::kNone_ShapesType,
265                                  10000, SkISize::Make(32, 33), false);)
266 
267 // Large primitives (GPU bound, in theory):
268 DEF_BENCH(return new ShapesBench(ShapesBench::kRect_ShapesType, ShapesBench::kNone_ShapesType,
269                                  100, SkISize::Make(500, 500), false);)
270 DEF_BENCH(return new ShapesBench(ShapesBench::kOval_ShapesType, ShapesBench::kNone_ShapesType,
271                                  100, SkISize::Make(500, 500), false);)
272 DEF_BENCH(return new ShapesBench(ShapesBench::kOval_ShapesType, ShapesBench::kNone_ShapesType,
273                                  100, SkISize::Make(500, 501), false);)
274 DEF_BENCH(return new ShapesBench(ShapesBench::kRRect_ShapesType, ShapesBench::kNone_ShapesType,
275                                  100, SkISize::Make(500, 500), false);)
276 DEF_BENCH(return new ShapesBench(ShapesBench::kMixed_ShapesType, ShapesBench::kNone_ShapesType,
277                                  100, SkISize::Make(500, 501), false);)
278 
279 // Donuts (small and large). These fall-back to path rendering due to non-orthogonal rotation
280 // making them quite slow. Thus, reduce the counts substantially:
281 DEF_BENCH(return new ShapesBench(ShapesBench::kRect_ShapesType, ShapesBench::kRect_ShapesType,
282                                  500, SkISize::Make(32, 32), false);)
283 DEF_BENCH(return new ShapesBench(ShapesBench::kRRect_ShapesType, ShapesBench::kRRect_ShapesType,
284                                  500, SkISize::Make(32, 32), false);)
285 DEF_BENCH(return new ShapesBench(ShapesBench::kRect_ShapesType, ShapesBench::kRect_ShapesType,
286                                  50, SkISize::Make(500, 500), false);)
287 DEF_BENCH(return new ShapesBench(ShapesBench::kRRect_ShapesType, ShapesBench::kRRect_ShapesType,
288                                  50, SkISize::Make(500, 500), false);)
289 #endif
290