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 #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
     22 DEFINE_string(shapesType, "mixed", "Type of shape to use in ShapesBench. Must be one of: "
     23                                    "rect, oval, rrect, mixed.");
     24 DEFINE_string(innerShapesType, "none", "Type of inner shape to use in ShapesBench. Must be one of: "
     25                                        "none, rect, oval, rrect, mixed.");
     26 DEFINE_int32(numShapes, 10000, "Number of shapes to draw in ShapesBench.");
     27 DEFINE_string(shapesSize, "32x32", "Size of shapes to draw in ShapesBench.");
     28 DEFINE_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  */
     37 class ShapesBench : public Benchmark {
     38 public:
     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 
    102 private:
    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 = nullptr;
    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 = nullptr;
    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 #if ENABLE_COMMAND_LINE_SHAPES_BENCH
    252 DEF_BENCH(return new ShapesBench;)
    253 #else
    254 // Small primitives (CPU bound, in theory):
    255 DEF_BENCH(return new ShapesBench(ShapesBench::kRect_ShapesType, ShapesBench::kNone_ShapesType,
    256                                  10000, SkISize::Make(32, 32), false);)
    257 DEF_BENCH(return new ShapesBench(ShapesBench::kOval_ShapesType, ShapesBench::kNone_ShapesType,
    258                                  10000, SkISize::Make(32, 32), false);)
    259 DEF_BENCH(return new ShapesBench(ShapesBench::kOval_ShapesType, ShapesBench::kNone_ShapesType,
    260                                  10000, SkISize::Make(32, 33), false);)
    261 DEF_BENCH(return new ShapesBench(ShapesBench::kRRect_ShapesType, ShapesBench::kNone_ShapesType,
    262                                  10000, SkISize::Make(32, 32), false);)
    263 DEF_BENCH(return new ShapesBench(ShapesBench::kMixed_ShapesType, ShapesBench::kNone_ShapesType,
    264                                  10000, SkISize::Make(32, 33), false);)
    265 
    266 // Large primitives (GPU bound, in theory):
    267 DEF_BENCH(return new ShapesBench(ShapesBench::kRect_ShapesType, ShapesBench::kNone_ShapesType,
    268                                  100, SkISize::Make(500, 500), false);)
    269 DEF_BENCH(return new ShapesBench(ShapesBench::kOval_ShapesType, ShapesBench::kNone_ShapesType,
    270                                  100, SkISize::Make(500, 500), false);)
    271 DEF_BENCH(return new ShapesBench(ShapesBench::kOval_ShapesType, ShapesBench::kNone_ShapesType,
    272                                  100, SkISize::Make(500, 501), false);)
    273 DEF_BENCH(return new ShapesBench(ShapesBench::kRRect_ShapesType, ShapesBench::kNone_ShapesType,
    274                                  100, SkISize::Make(500, 500), false);)
    275 DEF_BENCH(return new ShapesBench(ShapesBench::kMixed_ShapesType, ShapesBench::kNone_ShapesType,
    276                                  100, SkISize::Make(500, 501), false);)
    277 
    278 // Donuts (small and large). These fall-back to path rendering due to non-orthogonal rotation
    279 // making them quite slow. Thus, reduce the counts substantially:
    280 DEF_BENCH(return new ShapesBench(ShapesBench::kRect_ShapesType, ShapesBench::kRect_ShapesType,
    281                                  500, SkISize::Make(32, 32), false);)
    282 DEF_BENCH(return new ShapesBench(ShapesBench::kRRect_ShapesType, ShapesBench::kRRect_ShapesType,
    283                                  500, SkISize::Make(32, 32), false);)
    284 DEF_BENCH(return new ShapesBench(ShapesBench::kRect_ShapesType, ShapesBench::kRect_ShapesType,
    285                                  50, SkISize::Make(500, 500), false);)
    286 DEF_BENCH(return new ShapesBench(ShapesBench::kRRect_ShapesType, ShapesBench::kRRect_ShapesType,
    287                                  50, SkISize::Make(500, 500), false);)
    288 #endif
    289