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