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 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 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 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 101 private: 102 void clampShapeSize() { 103 float maxDiagonal = static_cast<float>(SkTMin(kBenchWidth, kBenchHeight)); 104 float diagonal = sqrtf(static_cast<float>(fShapesSize.width() * fShapesSize.width()) + 105 static_cast<float>(fShapesSize.height() * fShapesSize.height())); 106 if (diagonal > maxDiagonal) { 107 fShapesSize.fWidth = static_cast<int>(fShapesSize.width() * maxDiagonal / diagonal); 108 fShapesSize.fHeight = static_cast<int>(fShapesSize.height() * maxDiagonal / diagonal); 109 } 110 } 111 112 const char* onGetName() override { 113 const char* shapeTypeNames[] = { 114 "none", "rect", "oval", "rrect", "mixed" 115 }; 116 117 fName.printf("shapes_%s", shapeTypeNames[fShapesType]); 118 119 if (kNone_ShapesType != fInnerShapesType) { 120 fName.appendf("_inner_%s", shapeTypeNames[fInnerShapesType]); 121 } 122 123 fName.appendf("_%i_%ix%i", fNumShapes, fShapesSize.width(), fShapesSize.height()); 124 125 if (fPerspective) { 126 fName.append("_persp"); 127 } 128 129 return fName.c_str(); 130 } 131 SkIPoint onGetSize() override { return SkIPoint::Make(kBenchWidth, kBenchHeight); } 132 133 void onDelayedSetup() override { 134 SkScalar w = SkIntToScalar(fShapesSize.width()); 135 SkScalar h = SkIntToScalar(fShapesSize.height()); 136 137 fRect.setRect(SkRect::MakeXYWH(-w / 2, -h / 2, w, h)); 138 fOval.setOval(fRect.rect()); 139 fRRect.setNinePatch(fRect.rect(), w / 8, h / 13, w / 11, h / 7); 140 141 if (kNone_ShapesType != fInnerShapesType) { 142 fRect.inset(w / 7, h / 11, &fInnerRect); 143 fInnerRect.offset(w / 28, h / 44); 144 fInnerOval.setOval(fInnerRect.rect()); 145 fInnerRRect.setRectXY(fInnerRect.rect(), w / 13, w / 7); 146 } 147 148 SkRandom rand; 149 fShapes.push_back_n(fNumShapes); 150 for (int i = 0; i < fNumShapes; i++) { 151 float pad = sqrtf(static_cast<float>(fShapesSize.width() * fShapesSize.width()) + 152 static_cast<float>(fShapesSize.height() * fShapesSize.height())); 153 fShapes[i].fMatrix.setTranslate(0.5f * pad + rand.nextF() * (kBenchWidth - pad), 154 0.5f * pad + rand.nextF() * (kBenchHeight - pad)); 155 fShapes[i].fMatrix.preRotate(rand.nextF() * 360.0f); 156 if (fPerspective) { 157 fShapes[i].fMatrix.setPerspX(0.00015f); 158 fShapes[i].fMatrix.setPerspY(-0.00015f); 159 } 160 fShapes[i].fColor = rand.nextU() | 0xff808080; 161 } 162 for (int i = 0; i < fNumShapes; i++) { 163 // Do this in a separate loop so mixed shapes get the same random numbers during 164 // placement as non-mixed do. 165 int shapeType = fShapesType; 166 if (kMixed_ShapesType == shapeType) { 167 shapeType = rand.nextRangeU(kRect_ShapesType, kRRect_ShapesType); 168 } 169 int innerShapeType = fInnerShapesType; 170 if (kMixed_ShapesType == innerShapeType) { 171 innerShapeType = rand.nextRangeU(kRect_ShapesType, kRRect_ShapesType); 172 } 173 if (kNone_ShapesType == innerShapeType) { 174 switch (shapeType) { 175 using namespace std; 176 using namespace std::placeholders; 177 case kRect_ShapesType: 178 fShapes[i].fDraw = bind(&SkCanvas::drawRect, _1, cref(fRect.rect()), _2); 179 break; 180 case kOval_ShapesType: 181 fShapes[i].fDraw = bind(&SkCanvas::drawOval, _1, cref(fOval.rect()), _2); 182 break; 183 case kRRect_ShapesType: 184 fShapes[i].fDraw = bind(&SkCanvas::drawRRect, _1, cref(fRRect), _2); 185 break; 186 } 187 } else { 188 const SkRRect* outer = nullptr; 189 switch (shapeType) { 190 case kRect_ShapesType: outer = &fRect; break; 191 case kOval_ShapesType: outer = &fOval; break; 192 case kRRect_ShapesType: outer = &fRRect; break; 193 } 194 const SkRRect* inner = nullptr; 195 switch (innerShapeType) { 196 case kRect_ShapesType: inner = &fInnerRect; break; 197 case kOval_ShapesType: inner = &fInnerOval; break; 198 case kRRect_ShapesType: inner = &fInnerRRect; break; 199 } 200 fShapes[i].fDraw = std::bind(&SkCanvas::drawDRRect, std::placeholders::_1, 201 std::cref(*outer), std::cref(*inner), 202 std::placeholders::_2); 203 } 204 } 205 } 206 207 void onDraw(int loops, SkCanvas* canvas) override { 208 SkPaint paint; 209 this->setupPaint(&paint); 210 for (int j = 0; j < loops; j++) { 211 for (int i = 0; i < fNumShapes; i++) { 212 canvas->save(); 213 canvas->setMatrix(fShapes[i].fMatrix); 214 paint.setColor(fShapes[i].fColor); 215 fShapes[i].fDraw(canvas, paint); 216 canvas->restore(); 217 } 218 } 219 } 220 221 enum { 222 kBenchWidth = 1000, 223 kBenchHeight = 1000 224 }; 225 226 struct ShapeInfo { 227 SkMatrix fMatrix; 228 SkColor fColor; 229 std::function<void(SkCanvas*, const SkPaint&)> fDraw; 230 }; 231 232 ShapesType fShapesType; 233 ShapesType fInnerShapesType; 234 int fNumShapes; 235 SkISize fShapesSize; 236 bool fPerspective; 237 SkString fName; 238 SkRRect fRect; 239 SkRRect fOval; 240 SkRRect fRRect; 241 SkRRect fInnerRect; 242 SkRRect fInnerOval; 243 SkRRect fInnerRRect; 244 SkTArray<ShapeInfo> fShapes; 245 246 247 typedef Benchmark INHERITED; 248 }; 249 250 #if ENABLE_COMMAND_LINE_SHAPES_BENCH 251 DEF_BENCH(return new ShapesBench;) 252 #else 253 // Small primitives (CPU bound, in theory): 254 DEF_BENCH(return new ShapesBench(ShapesBench::kRect_ShapesType, ShapesBench::kNone_ShapesType, 255 10000, SkISize::Make(32, 32), false);) 256 DEF_BENCH(return new ShapesBench(ShapesBench::kOval_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, 33), false);) 260 DEF_BENCH(return new ShapesBench(ShapesBench::kRRect_ShapesType, ShapesBench::kNone_ShapesType, 261 10000, SkISize::Make(32, 32), false);) 262 DEF_BENCH(return new ShapesBench(ShapesBench::kMixed_ShapesType, ShapesBench::kNone_ShapesType, 263 10000, SkISize::Make(32, 33), false);) 264 265 // Large primitives (GPU bound, in theory): 266 DEF_BENCH(return new ShapesBench(ShapesBench::kRect_ShapesType, ShapesBench::kNone_ShapesType, 267 100, SkISize::Make(500, 500), false);) 268 DEF_BENCH(return new ShapesBench(ShapesBench::kOval_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, 501), false);) 272 DEF_BENCH(return new ShapesBench(ShapesBench::kRRect_ShapesType, ShapesBench::kNone_ShapesType, 273 100, SkISize::Make(500, 500), false);) 274 DEF_BENCH(return new ShapesBench(ShapesBench::kMixed_ShapesType, ShapesBench::kNone_ShapesType, 275 100, SkISize::Make(500, 501), false);) 276 277 // Donuts (small and large). These fall-back to path rendering due to non-orthogonal rotation 278 // making them quite slow. Thus, reduce the counts substantially: 279 DEF_BENCH(return new ShapesBench(ShapesBench::kRect_ShapesType, ShapesBench::kRect_ShapesType, 280 500, SkISize::Make(32, 32), false);) 281 DEF_BENCH(return new ShapesBench(ShapesBench::kRRect_ShapesType, ShapesBench::kRRect_ShapesType, 282 500, SkISize::Make(32, 32), false);) 283 DEF_BENCH(return new ShapesBench(ShapesBench::kRect_ShapesType, ShapesBench::kRect_ShapesType, 284 50, SkISize::Make(500, 500), false);) 285 DEF_BENCH(return new ShapesBench(ShapesBench::kRRect_ShapesType, ShapesBench::kRRect_ShapesType, 286 50, SkISize::Make(500, 500), false);) 287 #endif 288