• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "include/utils/SkRandom.h"
14  #include "tools/flags/CommandLineFlags.h"
15  
16  #include <stdio.h>
17  #include <stdlib.h>
18  #include <functional>
19  
20  #define ENABLE_COMMAND_LINE_SHAPES_BENCH 0
21  
22  #if ENABLE_COMMAND_LINE_SHAPES_BENCH
23  static DEFINE_string(shapesType, "mixed",
24                       "Type of shape to use in ShapesBench. Must be one of: "
25                       "rect, oval, rrect, mixed.");
26  static DEFINE_string(innerShapesType, "none",
27                       "Type of inner shape to use in ShapesBench. Must be one of: "
28                       "none, rect, oval, rrect, mixed.");
29  static DEFINE_int(numShapes, 10000, "Number of shapes to draw in ShapesBench.");
30  static DEFINE_string(shapesSize, "32x32", "Size of shapes to draw in ShapesBench.");
31  static DEFINE_bool(shapesPersp, false, "Use slight perspective tilt in ShapesBench?");
32  #endif
33  
34  /*
35   * This class is used for several benchmarks that draw different primitive Skia shapes at various
36   * sizes. It is used to test both CPU-bound and GPU-bound rendering situations. It draws large
37   * amounts of shapes internally (rather than relying on nanobench selecting lots of loops) in order
38   * to take advantage of instanced rendering approaches.
39   */
40  class ShapesBench : public Benchmark {
41  public:
42      enum ShapesType {
43          kNone_ShapesType,
44          kRect_ShapesType,
45          kOval_ShapesType,
46          kRRect_ShapesType,
47          kMixed_ShapesType
48      };
49  
ShapesBench(ShapesType shapesType,ShapesType innerShapesType,int numShapes,const SkISize & shapesSize,bool perspective)50      ShapesBench(ShapesType shapesType, ShapesType innerShapesType,
51                  int numShapes, const SkISize& shapesSize, bool perspective)
52          : fShapesType(shapesType)
53          , fInnerShapesType(innerShapesType)
54          , fNumShapes(numShapes)
55          , fShapesSize(shapesSize)
56          , fPerspective(perspective) {
57          clampShapeSize();
58      }
59  
60  #if ENABLE_COMMAND_LINE_SHAPES_BENCH
ShapesBench()61      ShapesBench() {
62          if (!strcmp(FLAGS_shapesType[0], "rect")) {
63              fShapesType = kRect_ShapesType;
64          } else if (!strcmp(FLAGS_shapesType[0], "oval")) {
65              fShapesType = kOval_ShapesType;
66          } else if (!strcmp(FLAGS_shapesType[0], "rrect")) {
67              fShapesType = kRRect_ShapesType;
68          } else if (!strcmp(FLAGS_shapesType[0], "mixed")) {
69              fShapesType = kMixed_ShapesType;
70          } else {
71              SkDebugf("Invalid shapesType \"%s\". Must be one of: rect, oval, rrect, mixed.",
72                       FLAGS_shapesType[0]);
73              exit(-1);
74          }
75          if (!strcmp(FLAGS_innerShapesType[0], "none")) {
76              fInnerShapesType = kNone_ShapesType;
77          } else if (!strcmp(FLAGS_innerShapesType[0], "rect")) {
78              fInnerShapesType = kRect_ShapesType;
79          } else if (!strcmp(FLAGS_innerShapesType[0], "oval")) {
80              fInnerShapesType = kOval_ShapesType;
81          } else if (!strcmp(FLAGS_innerShapesType[0], "rrect")) {
82              fInnerShapesType = kRRect_ShapesType;
83          } else if (!strcmp(FLAGS_innerShapesType[0], "mixed")) {
84              fInnerShapesType = kMixed_ShapesType;
85          } else {
86              SkDebugf("Invalid innerShapesType \"%s\". Must be one of: "
87                       "none, rect, oval, rrect, mixed.", FLAGS_innerShapesType[0]);
88              exit(-1);
89          }
90          if (2 != sscanf(FLAGS_shapesSize[0], "%ix%i", &fShapesSize.fWidth, &fShapesSize.fHeight)) {
91              SkDebugf("Could not parse shapesSize from \"%s\". Expected \"%%ix%%i\"\n",
92                       FLAGS_shapesSize[0]);
93              exit(-1);
94          }
95  
96          fNumShapes = FLAGS_numShapes;
97          fPerspective = FLAGS_shapesPersp;
98  
99          clampShapeSize();
100      }
101  #endif
102  
103  private:
clampShapeSize()104      void clampShapeSize() {
105          float maxDiagonal = static_cast<float>(std::min(kBenchWidth, kBenchHeight));
106          float diagonal = sqrtf(static_cast<float>(fShapesSize.width() * fShapesSize.width()) +
107                                 static_cast<float>(fShapesSize.height() * fShapesSize.height()));
108          if (diagonal > maxDiagonal) {
109              fShapesSize.fWidth = static_cast<int>(fShapesSize.width() * maxDiagonal / diagonal);
110              fShapesSize.fHeight = static_cast<int>(fShapesSize.height() * maxDiagonal / diagonal);
111          }
112      }
113  
onGetName()114      const char* onGetName() override {
115          const char* shapeTypeNames[] = {
116              "none", "rect", "oval", "rrect", "mixed"
117          };
118  
119          fName.printf("shapes_%s", shapeTypeNames[fShapesType]);
120  
121          if (kNone_ShapesType != fInnerShapesType) {
122              fName.appendf("_inner_%s", shapeTypeNames[fInnerShapesType]);
123          }
124  
125          fName.appendf("_%i_%ix%i", fNumShapes, fShapesSize.width(), fShapesSize.height());
126  
127          if (fPerspective) {
128              fName.append("_persp");
129          }
130  
131          return fName.c_str();
132      }
onGetSize()133      SkIPoint onGetSize() override { return SkIPoint::Make(kBenchWidth, kBenchHeight); }
134  
onDelayedSetup()135      void onDelayedSetup() override {
136          SkScalar w = SkIntToScalar(fShapesSize.width());
137          SkScalar h = SkIntToScalar(fShapesSize.height());
138  
139          fRect.setRect(SkRect::MakeXYWH(-w / 2, -h / 2, w, h));
140          fOval.setOval(fRect.rect());
141          fRRect.setNinePatch(fRect.rect(), w / 8, h / 13, w / 11, h / 7);
142  
143          if (kNone_ShapesType != fInnerShapesType) {
144              fRect.inset(w / 7, h / 11, &fInnerRect);
145              fInnerRect.offset(w / 28, h / 44);
146              fInnerOval.setOval(fInnerRect.rect());
147              fInnerRRect.setRectXY(fInnerRect.rect(), w / 13, w / 7);
148          }
149  
150          SkRandom rand;
151          fShapes.push_back_n(fNumShapes);
152          for (int i = 0; i < fNumShapes; i++) {
153              float pad = sqrtf(static_cast<float>(fShapesSize.width() * fShapesSize.width()) +
154                                static_cast<float>(fShapesSize.height() * fShapesSize.height()));
155              fShapes[i].fMatrix.setTranslate(0.5f * pad + rand.nextF() * (kBenchWidth - pad),
156                                              0.5f * pad + rand.nextF() * (kBenchHeight - pad));
157              fShapes[i].fMatrix.preRotate(rand.nextF() * 360.0f);
158              if (fPerspective) {
159                  fShapes[i].fMatrix.setPerspX(0.00015f);
160                  fShapes[i].fMatrix.setPerspY(-0.00015f);
161              }
162              fShapes[i].fColor = rand.nextU() | 0xff808080;
163          }
164          for (int i = 0; i < fNumShapes; i++) {
165              // Do this in a separate loop so mixed shapes get the same random numbers during
166              // placement as non-mixed do.
167              int shapeType = fShapesType;
168              if (kMixed_ShapesType == shapeType) {
169                  shapeType = rand.nextRangeU(kRect_ShapesType, kRRect_ShapesType);
170              }
171              int innerShapeType = fInnerShapesType;
172              if (kMixed_ShapesType == innerShapeType) {
173                  innerShapeType = rand.nextRangeU(kRect_ShapesType, kRRect_ShapesType);
174              }
175              if (kNone_ShapesType == innerShapeType) {
176                  switch (shapeType) {
177                      using namespace std;
178                      using namespace std::placeholders;
179                      case kRect_ShapesType:
180                          fShapes[i].fDraw = bind(&SkCanvas::drawRect, _1, cref(fRect.rect()), _2);
181                          break;
182                      case kOval_ShapesType:
183                          fShapes[i].fDraw = bind(&SkCanvas::drawOval, _1, cref(fOval.rect()), _2);
184                          break;
185                      case kRRect_ShapesType:
186                          fShapes[i].fDraw = bind(&SkCanvas::drawRRect, _1, cref(fRRect), _2);
187                          break;
188                  }
189              } else {
190                  const SkRRect* outer = nullptr;
191                  switch (shapeType) {
192                      case kRect_ShapesType: outer = &fRect; break;
193                      case kOval_ShapesType: outer = &fOval; break;
194                      case kRRect_ShapesType: outer = &fRRect; break;
195                  }
196                  const SkRRect* inner = nullptr;
197                  switch (innerShapeType) {
198                      case kRect_ShapesType: inner = &fInnerRect; break;
199                      case kOval_ShapesType: inner = &fInnerOval; break;
200                      case kRRect_ShapesType: inner = &fInnerRRect; break;
201                  }
202                  fShapes[i].fDraw = std::bind(&SkCanvas::drawDRRect, std::placeholders::_1,
203                                               std::cref(*outer), std::cref(*inner),
204                                               std::placeholders::_2);
205              }
206          }
207      }
208  
onDraw(int loops,SkCanvas * canvas)209      void onDraw(int loops, SkCanvas* canvas) override {
210          SkPaint paint;
211          this->setupPaint(&paint);
212          for (int j = 0; j < loops; j++) {
213              for (int i = 0; i < fNumShapes; i++) {
214                  canvas->save();
215                  canvas->setMatrix(fShapes[i].fMatrix);
216                  paint.setColor(fShapes[i].fColor);
217                  fShapes[i].fDraw(canvas, paint);
218                  canvas->restore();
219              }
220          }
221      }
222  
223      enum {
224          kBenchWidth = 1000,
225          kBenchHeight = 1000
226      };
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      SkTArray<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