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