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 "include/utils/SkRandom.h" 14 #include "tools/flags/CommandLineFlags.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 static DEFINE_string(shapesType, "mixed", 24 "Type of shape to use in ShapesBench. Must be one of: " 25 "rect, oval, rrect, mixed."); 26 static DEFINE_string(innerShapesType, "none", 27 "Type of inner shape to use in ShapesBench. Must be one of: " 28 "none, rect, oval, rrect, mixed."); 29 static DEFINE_int(numShapes, 10000, "Number of shapes to draw in ShapesBench."); 30 static DEFINE_string(shapesSize, "32x32", "Size of shapes to draw in ShapesBench."); 31 static DEFINE_bool(shapesPersp, false, "Use slight perspective tilt in ShapesBench?"); 32 #endif 33 34 /* 35 * This class is used for several benchmarks that draw different primitive Skia shapes at various 36 * sizes. It is used to test both CPU-bound and GPU-bound rendering situations. It draws large 37 * amounts of shapes internally (rather than relying on nanobench selecting lots of loops) in order 38 * to take advantage of instanced rendering approaches. 39 */ 40 class ShapesBench : public Benchmark { 41 public: 42 enum ShapesType { 43 kNone_ShapesType, 44 kRect_ShapesType, 45 kOval_ShapesType, 46 kRRect_ShapesType, 47 kMixed_ShapesType 48 }; 49 ShapesBench(ShapesType shapesType,ShapesType innerShapesType,int numShapes,const SkISize & shapesSize,bool perspective)50 ShapesBench(ShapesType shapesType, ShapesType innerShapesType, 51 int numShapes, const SkISize& shapesSize, bool perspective) 52 : fShapesType(shapesType) 53 , fInnerShapesType(innerShapesType) 54 , fNumShapes(numShapes) 55 , fShapesSize(shapesSize) 56 , fPerspective(perspective) { 57 clampShapeSize(); 58 } 59 60 #if ENABLE_COMMAND_LINE_SHAPES_BENCH ShapesBench()61 ShapesBench() { 62 if (!strcmp(FLAGS_shapesType[0], "rect")) { 63 fShapesType = kRect_ShapesType; 64 } else if (!strcmp(FLAGS_shapesType[0], "oval")) { 65 fShapesType = kOval_ShapesType; 66 } else if (!strcmp(FLAGS_shapesType[0], "rrect")) { 67 fShapesType = kRRect_ShapesType; 68 } else if (!strcmp(FLAGS_shapesType[0], "mixed")) { 69 fShapesType = kMixed_ShapesType; 70 } else { 71 SkDebugf("Invalid shapesType \"%s\". Must be one of: rect, oval, rrect, mixed.", 72 FLAGS_shapesType[0]); 73 exit(-1); 74 } 75 if (!strcmp(FLAGS_innerShapesType[0], "none")) { 76 fInnerShapesType = kNone_ShapesType; 77 } else if (!strcmp(FLAGS_innerShapesType[0], "rect")) { 78 fInnerShapesType = kRect_ShapesType; 79 } else if (!strcmp(FLAGS_innerShapesType[0], "oval")) { 80 fInnerShapesType = kOval_ShapesType; 81 } else if (!strcmp(FLAGS_innerShapesType[0], "rrect")) { 82 fInnerShapesType = kRRect_ShapesType; 83 } else if (!strcmp(FLAGS_innerShapesType[0], "mixed")) { 84 fInnerShapesType = kMixed_ShapesType; 85 } else { 86 SkDebugf("Invalid innerShapesType \"%s\". Must be one of: " 87 "none, rect, oval, rrect, mixed.", FLAGS_innerShapesType[0]); 88 exit(-1); 89 } 90 if (2 != sscanf(FLAGS_shapesSize[0], "%ix%i", &fShapesSize.fWidth, &fShapesSize.fHeight)) { 91 SkDebugf("Could not parse shapesSize from \"%s\". Expected \"%%ix%%i\"\n", 92 FLAGS_shapesSize[0]); 93 exit(-1); 94 } 95 96 fNumShapes = FLAGS_numShapes; 97 fPerspective = FLAGS_shapesPersp; 98 99 clampShapeSize(); 100 } 101 #endif 102 103 private: clampShapeSize()104 void clampShapeSize() { 105 float maxDiagonal = static_cast<float>(std::min(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 = nullptr; 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 = nullptr; 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 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