• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
2 /*
3  * Copyright 2016 Google Inc.
4  *
5  * Use of this source code is governed by a BSD-style license that can be
6  * found in the LICENSE file.
7  */
8 
9 #include "Benchmark.h"
10 #include "SkCanvas.h"
11 #include "SkCommandLineFlags.h"
12 #include "SkPaint.h"
13 #include "SkRandom.h"
14 #include "SkRRect.h"
15 #include "SkString.h"
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 DEFINE_string(shapesType, "mixed", "Type of shape to use in ShapesBench. Must be one of: "
24                                    "rect, oval, rrect, mixed.");
25 DEFINE_string(innerShapesType, "none", "Type of inner shape to use in ShapesBench. Must be one of: "
26                                        "none, rect, oval, rrect, mixed.");
27 DEFINE_int32(numShapes, 10000, "Number of shapes to draw in ShapesBench.");
28 DEFINE_string(shapesSize, "32x32", "Size of shapes to draw in ShapesBench.");
29 DEFINE_bool(shapesPersp, false, "Use slight perspective tilt in ShapesBench?");
30 #endif
31 
32 /*
33  * This class is used for several benchmarks that draw different primitive Skia shapes at various
34  * sizes. It is used to test both CPU-bound and GPU-bound rendering situations. It draws large
35  * amounts of shapes internally (rather than relying on nanobench selecting lots of loops) in order
36  * to take advantage of instanced rendering approaches.
37  */
38 class ShapesBench : public Benchmark {
39 public:
40     enum ShapesType {
41         kNone_ShapesType,
42         kRect_ShapesType,
43         kOval_ShapesType,
44         kRRect_ShapesType,
45         kMixed_ShapesType
46     };
47 
ShapesBench(ShapesType shapesType,ShapesType innerShapesType,int numShapes,const SkISize & shapesSize,bool perspective)48     ShapesBench(ShapesType shapesType, ShapesType innerShapesType,
49                 int numShapes, const SkISize& shapesSize, bool perspective)
50         : fShapesType(shapesType)
51         , fInnerShapesType(innerShapesType)
52         , fNumShapes(numShapes)
53         , fShapesSize(shapesSize)
54         , fPerspective(perspective) {
55         clampShapeSize();
56     }
57 
58 #if ENABLE_COMMAND_LINE_SHAPES_BENCH
ShapesBench()59     ShapesBench() {
60         if (!strcmp(FLAGS_shapesType[0], "rect")) {
61             fShapesType = kRect_ShapesType;
62         } else if (!strcmp(FLAGS_shapesType[0], "oval")) {
63             fShapesType = kOval_ShapesType;
64         } else if (!strcmp(FLAGS_shapesType[0], "rrect")) {
65             fShapesType = kRRect_ShapesType;
66         } else if (!strcmp(FLAGS_shapesType[0], "mixed")) {
67             fShapesType = kMixed_ShapesType;
68         } else {
69             SkDebugf("Invalid shapesType \"%s\". Must be one of: rect, oval, rrect, mixed.",
70                      FLAGS_shapesType[0]);
71             exit(-1);
72         }
73         if (!strcmp(FLAGS_innerShapesType[0], "none")) {
74             fInnerShapesType = kNone_ShapesType;
75         } else if (!strcmp(FLAGS_innerShapesType[0], "rect")) {
76             fInnerShapesType = kRect_ShapesType;
77         } else if (!strcmp(FLAGS_innerShapesType[0], "oval")) {
78             fInnerShapesType = kOval_ShapesType;
79         } else if (!strcmp(FLAGS_innerShapesType[0], "rrect")) {
80             fInnerShapesType = kRRect_ShapesType;
81         } else if (!strcmp(FLAGS_innerShapesType[0], "mixed")) {
82             fInnerShapesType = kMixed_ShapesType;
83         } else {
84             SkDebugf("Invalid innerShapesType \"%s\". Must be one of: "
85                      "none, rect, oval, rrect, mixed.", FLAGS_innerShapesType[0]);
86             exit(-1);
87         }
88         if (2 != sscanf(FLAGS_shapesSize[0], "%ix%i", &fShapesSize.fWidth, &fShapesSize.fHeight)) {
89             SkDebugf("Could not parse shapesSize from \"%s\". Expected \"%%ix%%i\"\n",
90                      FLAGS_shapesSize[0]);
91             exit(-1);
92         }
93 
94         fNumShapes = FLAGS_numShapes;
95         fPerspective = FLAGS_shapesPersp;
96 
97         clampShapeSize();
98     }
99 #endif
100 
isVisual()101     bool isVisual() override { return true; }
102 
103 private:
clampShapeSize()104     void clampShapeSize() {
105         float maxDiagonal = static_cast<float>(SkTMin(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;
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;
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     typedef Benchmark INHERITED;
250 };
251 
252 // Small primitives (CPU bound, in theory):
253 DEF_BENCH(return new ShapesBench(ShapesBench::kRect_ShapesType, ShapesBench::kNone_ShapesType,
254                                  10000, SkISize::Make(32, 32), false);)
255 DEF_BENCH(return new ShapesBench(ShapesBench::kOval_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, 33), false);)
259 DEF_BENCH(return new ShapesBench(ShapesBench::kRRect_ShapesType, ShapesBench::kNone_ShapesType,
260                                  10000, SkISize::Make(32, 32), false);)
261 DEF_BENCH(return new ShapesBench(ShapesBench::kMixed_ShapesType, ShapesBench::kNone_ShapesType,
262                                  10000, SkISize::Make(32, 33), false);)
263 
264 // Large primitives (GPU bound, in theory):
265 DEF_BENCH(return new ShapesBench(ShapesBench::kRect_ShapesType, ShapesBench::kNone_ShapesType,
266                                  100, SkISize::Make(500, 500), false);)
267 DEF_BENCH(return new ShapesBench(ShapesBench::kOval_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, 501), false);)
271 DEF_BENCH(return new ShapesBench(ShapesBench::kRRect_ShapesType, ShapesBench::kNone_ShapesType,
272                                  100, SkISize::Make(500, 500), false);)
273 DEF_BENCH(return new ShapesBench(ShapesBench::kMixed_ShapesType, ShapesBench::kNone_ShapesType,
274                                  100, SkISize::Make(500, 501), false);)
275 
276 // Donuts (small and large). These fall-back to path rendering due to non-orthogonal rotation
277 // making them quite slow. Thus, reduce the counts substantially:
278 DEF_BENCH(return new ShapesBench(ShapesBench::kRect_ShapesType, ShapesBench::kRect_ShapesType,
279                                  500, SkISize::Make(32, 32), false);)
280 DEF_BENCH(return new ShapesBench(ShapesBench::kRRect_ShapesType, ShapesBench::kRRect_ShapesType,
281                                  500, SkISize::Make(32, 32), false);)
282 DEF_BENCH(return new ShapesBench(ShapesBench::kRect_ShapesType, ShapesBench::kRect_ShapesType,
283                                  50, SkISize::Make(500, 500), false);)
284 DEF_BENCH(return new ShapesBench(ShapesBench::kRRect_ShapesType, ShapesBench::kRRect_ShapesType,
285                                  50, SkISize::Make(500, 500), false);)
286 
287 #if ENABLE_COMMAND_LINE_SHAPES_BENCH
288 DEF_BENCH(return new ShapesBench;)
289 #endif
290