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