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