Home | History | Annotate | Download | only in bench
      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