Home | History | Annotate | Download | only in bench
      1 /*
      2  * Copyright 2011 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 "SkBitmap.h"
     10 #include "SkCanvas.h"
     11 #include "SkColorPriv.h"
     12 #include "SkPaint.h"
     13 #include "SkPath.h"
     14 #include "SkRandom.h"
     15 #include "SkShader.h"
     16 #include "SkString.h"
     17 #include "SkTArray.h"
     18 
     19 enum Flags {
     20     kStroke_Flag = 1 << 0,
     21     kBig_Flag    = 1 << 1
     22 };
     23 
     24 #define FLAGS00  Flags(0)
     25 #define FLAGS01  Flags(kStroke_Flag)
     26 #define FLAGS10  Flags(kBig_Flag)
     27 #define FLAGS11  Flags(kStroke_Flag | kBig_Flag)
     28 
     29 class PathBench : public Benchmark {
     30     SkPaint     fPaint;
     31     SkString    fName;
     32     Flags       fFlags;
     33 public:
     34     PathBench(Flags flags) : fFlags(flags) {
     35         fPaint.setStyle(flags & kStroke_Flag ? SkPaint::kStroke_Style :
     36                         SkPaint::kFill_Style);
     37         fPaint.setStrokeWidth(SkIntToScalar(5));
     38         fPaint.setStrokeJoin(SkPaint::kBevel_Join);
     39     }
     40 
     41     virtual void appendName(SkString*) = 0;
     42     virtual void makePath(SkPath*) = 0;
     43     virtual int complexity() { return 0; }
     44 
     45 protected:
     46     const char* onGetName() override {
     47         fName.printf("path_%s_%s_",
     48                      fFlags & kStroke_Flag ? "stroke" : "fill",
     49                      fFlags & kBig_Flag ? "big" : "small");
     50         this->appendName(&fName);
     51         return fName.c_str();
     52     }
     53 
     54     void onDraw(int loops, SkCanvas* canvas) override {
     55         SkPaint paint(fPaint);
     56         this->setupPaint(&paint);
     57 
     58         SkPath path;
     59         this->makePath(&path);
     60         if (fFlags & kBig_Flag) {
     61             const SkMatrix m = SkMatrix::MakeScale(SkIntToScalar(10), SkIntToScalar(10));
     62             path.transform(m);
     63         }
     64 
     65         for (int i = 0; i < loops; i++) {
     66             canvas->drawPath(path, paint);
     67         }
     68     }
     69 
     70 private:
     71     typedef Benchmark INHERITED;
     72 };
     73 
     74 class TrianglePathBench : public PathBench {
     75 public:
     76     TrianglePathBench(Flags flags) : INHERITED(flags) {}
     77 
     78     void appendName(SkString* name) override {
     79         name->append("triangle");
     80     }
     81     void makePath(SkPath* path) override {
     82         static const int gCoord[] = {
     83             10, 10, 15, 5, 20, 20
     84         };
     85         path->moveTo(SkIntToScalar(gCoord[0]), SkIntToScalar(gCoord[1]));
     86         path->lineTo(SkIntToScalar(gCoord[2]), SkIntToScalar(gCoord[3]));
     87         path->lineTo(SkIntToScalar(gCoord[4]), SkIntToScalar(gCoord[5]));
     88         path->close();
     89     }
     90 private:
     91     typedef PathBench INHERITED;
     92 };
     93 
     94 class RectPathBench : public PathBench {
     95 public:
     96     RectPathBench(Flags flags) : INHERITED(flags) {}
     97 
     98     void appendName(SkString* name) override {
     99         name->append("rect");
    100     }
    101     void makePath(SkPath* path) override {
    102         SkRect r = { 10, 10, 20, 20 };
    103         path->addRect(r);
    104     }
    105 private:
    106     typedef PathBench INHERITED;
    107 };
    108 
    109 class RotatedRectBench : public PathBench {
    110 public:
    111     RotatedRectBench(Flags flags, bool aa, int degrees) : INHERITED(flags) {
    112         fAA = aa;
    113         fDegrees = degrees;
    114     }
    115 
    116     void appendName(SkString* name) override {
    117         SkString suffix;
    118         suffix.printf("rotated_rect_%s_%d", fAA ? "aa" : "noaa", fDegrees);
    119         name->append(suffix);
    120     }
    121 
    122     void makePath(SkPath* path) override {
    123         SkRect r = { 10, 10, 20, 20 };
    124         path->addRect(r);
    125         SkMatrix rotateMatrix;
    126         rotateMatrix.setRotate((SkScalar)fDegrees);
    127         path->transform(rotateMatrix);
    128     }
    129 
    130     virtual void setupPaint(SkPaint* paint) override {
    131         PathBench::setupPaint(paint);
    132         paint->setAntiAlias(fAA);
    133     }
    134 private:
    135     typedef PathBench INHERITED;
    136     int fDegrees;
    137     bool fAA;
    138 };
    139 
    140 class OvalPathBench : public PathBench {
    141 public:
    142     OvalPathBench(Flags flags) : INHERITED(flags) {}
    143 
    144     void appendName(SkString* name) override {
    145         name->append("oval");
    146     }
    147     void makePath(SkPath* path) override {
    148         SkRect r = { 10, 10, 23, 20 };
    149         path->addOval(r);
    150     }
    151 private:
    152     typedef PathBench INHERITED;
    153 };
    154 
    155 class CirclePathBench: public PathBench {
    156 public:
    157     CirclePathBench(Flags flags) : INHERITED(flags) {}
    158 
    159     void appendName(SkString* name) override {
    160         name->append("circle");
    161     }
    162     void makePath(SkPath* path) override {
    163         path->addCircle(SkIntToScalar(20), SkIntToScalar(20),
    164                         SkIntToScalar(10));
    165     }
    166 private:
    167     typedef PathBench INHERITED;
    168 };
    169 
    170 class NonAACirclePathBench: public CirclePathBench {
    171 public:
    172     NonAACirclePathBench(Flags flags) : INHERITED(flags) {}
    173 
    174     void appendName(SkString* name) override {
    175         name->append("nonaacircle");
    176     }
    177 
    178     void setupPaint(SkPaint* paint) override {
    179         CirclePathBench::setupPaint(paint);
    180         paint->setAntiAlias(false);
    181     }
    182 
    183 private:
    184     typedef CirclePathBench INHERITED;
    185 };
    186 
    187 // Test max speedup of Analytic AA for concave paths
    188 class AAAConcavePathBench : public PathBench {
    189 public:
    190     AAAConcavePathBench(Flags flags) : INHERITED(flags) {}
    191 
    192     void appendName(SkString* name) override {
    193         name->append("concave_aaa");
    194     }
    195 
    196     void makePath(SkPath* path) override {
    197         path->moveTo(10, 10);
    198         path->lineTo(15, 10);
    199         path->lineTo(15, 5);
    200         path->lineTo(40, 40);
    201         path->close();
    202     }
    203 
    204 private:
    205     typedef PathBench INHERITED;
    206 };
    207 
    208 // Test max speedup of Analytic AA for convex paths
    209 class AAAConvexPathBench : public PathBench {
    210 public:
    211     AAAConvexPathBench(Flags flags) : INHERITED(flags) {}
    212 
    213     void appendName(SkString* name) override {
    214         name->append("convex_aaa");
    215     }
    216 
    217     void makePath(SkPath* path) override {
    218         path->moveTo(10, 10);
    219         path->lineTo(15, 10);
    220         path->lineTo(40, 50);
    221         path->close();
    222     }
    223 
    224 private:
    225     typedef PathBench INHERITED;
    226 };
    227 
    228 class SawToothPathBench : public PathBench {
    229 public:
    230     SawToothPathBench(Flags flags) : INHERITED(flags) {}
    231 
    232     void appendName(SkString* name) override {
    233         name->append("sawtooth");
    234     }
    235     void makePath(SkPath* path) override {
    236         SkScalar x = SkIntToScalar(20);
    237         SkScalar y = SkIntToScalar(20);
    238         const SkScalar x0 = x;
    239         const SkScalar dx = SK_Scalar1 * 5;
    240         const SkScalar dy = SK_Scalar1 * 10;
    241 
    242         path->moveTo(x, y);
    243         for (int i = 0; i < 32; i++) {
    244             x += dx;
    245             path->lineTo(x, y - dy);
    246             x += dx;
    247             path->lineTo(x, y + dy);
    248         }
    249         path->lineTo(x, y + 2 * dy);
    250         path->lineTo(x0, y + 2 * dy);
    251         path->close();
    252     }
    253     int complexity() override { return 1; }
    254 private:
    255     typedef PathBench INHERITED;
    256 };
    257 
    258 class LongCurvedPathBench : public PathBench {
    259 public:
    260     LongCurvedPathBench(Flags flags) : INHERITED(flags) {}
    261 
    262     void appendName(SkString* name) override {
    263         name->append("long_curved");
    264     }
    265     void makePath(SkPath* path) override {
    266         SkRandom rand (12);
    267         int i;
    268         for (i = 0; i < 100; i++) {
    269             path->quadTo(rand.nextUScalar1() * 640, rand.nextUScalar1() * 480,
    270                          rand.nextUScalar1() * 640, rand.nextUScalar1() * 480);
    271         }
    272         path->close();
    273     }
    274     int complexity() override { return 2; }
    275 private:
    276     typedef PathBench INHERITED;
    277 };
    278 
    279 class LongLinePathBench : public PathBench {
    280 public:
    281     LongLinePathBench(Flags flags) : INHERITED(flags) {}
    282 
    283     void appendName(SkString* name) override {
    284         name->append("long_line");
    285     }
    286     void makePath(SkPath* path) override {
    287         SkRandom rand;
    288         path->moveTo(rand.nextUScalar1() * 640, rand.nextUScalar1() * 480);
    289         for (size_t i = 1; i < 100; i++) {
    290             path->lineTo(rand.nextUScalar1() * 640, rand.nextUScalar1() * 480);
    291         }
    292     }
    293     int complexity() override { return 2; }
    294 private:
    295     typedef PathBench INHERITED;
    296 };
    297 
    298 class RandomPathBench : public Benchmark {
    299 public:
    300     bool isSuitableFor(Backend backend) override {
    301         return backend == kNonRendering_Backend;
    302     }
    303 
    304 protected:
    305     void createData(int minVerbs,
    306                     int maxVerbs,
    307                     bool allowMoves = true,
    308                     SkRect* bounds = nullptr) {
    309         SkRect tempBounds;
    310         if (nullptr == bounds) {
    311             tempBounds.setXYWH(0, 0, SK_Scalar1, SK_Scalar1);
    312             bounds = &tempBounds;
    313         }
    314         fVerbCnts.reset(kNumVerbCnts);
    315         for (int i = 0; i < kNumVerbCnts; ++i) {
    316             fVerbCnts[i] = fRandom.nextRangeU(minVerbs, maxVerbs + 1);
    317         }
    318         fVerbs.reset(kNumVerbs);
    319         for (int i = 0; i < kNumVerbs; ++i) {
    320             do {
    321                 fVerbs[i] = static_cast<SkPath::Verb>(fRandom.nextULessThan(SkPath::kDone_Verb));
    322             } while (!allowMoves && SkPath::kMove_Verb == fVerbs[i]);
    323         }
    324         fPoints.reset(kNumPoints);
    325         for (int i = 0; i < kNumPoints; ++i) {
    326             fPoints[i].set(fRandom.nextRangeScalar(bounds->fLeft, bounds->fRight),
    327                            fRandom.nextRangeScalar(bounds->fTop, bounds->fBottom));
    328         }
    329         this->restartMakingPaths();
    330     }
    331 
    332     void restartMakingPaths() {
    333         fCurrPath = 0;
    334         fCurrVerb = 0;
    335         fCurrPoint = 0;
    336     }
    337 
    338     void makePath(SkPath* path) {
    339         int vCount = fVerbCnts[(fCurrPath++) & (kNumVerbCnts - 1)];
    340         for (int v = 0; v < vCount; ++v) {
    341             int verb = fVerbs[(fCurrVerb++) & (kNumVerbs - 1)];
    342             switch (verb) {
    343                 case SkPath::kMove_Verb:
    344                     path->moveTo(fPoints[(fCurrPoint++) & (kNumPoints - 1)]);
    345                     break;
    346                 case SkPath::kLine_Verb:
    347                     path->lineTo(fPoints[(fCurrPoint++) & (kNumPoints - 1)]);
    348                     break;
    349                 case SkPath::kQuad_Verb:
    350                     path->quadTo(fPoints[(fCurrPoint + 0) & (kNumPoints - 1)],
    351                                  fPoints[(fCurrPoint + 1) & (kNumPoints - 1)]);
    352                     fCurrPoint += 2;
    353                     break;
    354                 case SkPath::kConic_Verb:
    355                     path->conicTo(fPoints[(fCurrPoint + 0) & (kNumPoints - 1)],
    356                                   fPoints[(fCurrPoint + 1) & (kNumPoints - 1)],
    357                                   SK_ScalarHalf);
    358                     fCurrPoint += 2;
    359                     break;
    360                 case SkPath::kCubic_Verb:
    361                     path->cubicTo(fPoints[(fCurrPoint + 0) & (kNumPoints - 1)],
    362                                   fPoints[(fCurrPoint + 1) & (kNumPoints - 1)],
    363                                   fPoints[(fCurrPoint + 2) & (kNumPoints - 1)]);
    364                     fCurrPoint += 3;
    365                     break;
    366                 case SkPath::kClose_Verb:
    367                     path->close();
    368                     break;
    369                 default:
    370                     SkDEBUGFAIL("Unexpected path verb");
    371                     break;
    372             }
    373         }
    374     }
    375 
    376     void finishedMakingPaths() {
    377         fVerbCnts.reset(0);
    378         fVerbs.reset(0);
    379         fPoints.reset(0);
    380     }
    381 
    382 private:
    383     enum {
    384         // these should all be pow 2
    385         kNumVerbCnts = 1 << 5,
    386         kNumVerbs    = 1 << 5,
    387         kNumPoints   = 1 << 5,
    388     };
    389     SkAutoTArray<int>           fVerbCnts;
    390     SkAutoTArray<SkPath::Verb>  fVerbs;
    391     SkAutoTArray<SkPoint>       fPoints;
    392     int                         fCurrPath;
    393     int                         fCurrVerb;
    394     int                         fCurrPoint;
    395     SkRandom                    fRandom;
    396     typedef Benchmark INHERITED;
    397 };
    398 
    399 class PathCreateBench : public RandomPathBench {
    400 public:
    401     PathCreateBench()  {
    402     }
    403 
    404 protected:
    405     const char* onGetName() override {
    406         return "path_create";
    407     }
    408 
    409     void onDelayedSetup() override {
    410         this->createData(10, 100);
    411     }
    412 
    413     void onDraw(int loops, SkCanvas*) override {
    414         for (int i = 0; i < loops; ++i) {
    415             if (i % 1000 == 0) {
    416                 fPath.reset();  // PathRef memory can grow without bound otherwise.
    417             }
    418             this->makePath(&fPath);
    419         }
    420         this->restartMakingPaths();
    421     }
    422 
    423 private:
    424     SkPath fPath;
    425 
    426     typedef RandomPathBench INHERITED;
    427 };
    428 
    429 class PathCopyBench : public RandomPathBench {
    430 public:
    431     PathCopyBench()  {
    432     }
    433 
    434 protected:
    435     const char* onGetName() override {
    436         return "path_copy";
    437     }
    438     void onDelayedSetup() override {
    439         this->createData(10, 100);
    440         fPaths.reset(kPathCnt);
    441         fCopies.reset(kPathCnt);
    442         for (int i = 0; i < kPathCnt; ++i) {
    443             this->makePath(&fPaths[i]);
    444         }
    445         this->finishedMakingPaths();
    446     }
    447     void onDraw(int loops, SkCanvas*) override {
    448         for (int i = 0; i < loops; ++i) {
    449             int idx = i & (kPathCnt - 1);
    450             fCopies[idx] = fPaths[idx];
    451         }
    452     }
    453 
    454 private:
    455     enum {
    456         // must be a pow 2
    457         kPathCnt = 1 << 5,
    458     };
    459     SkAutoTArray<SkPath> fPaths;
    460     SkAutoTArray<SkPath> fCopies;
    461 
    462     typedef RandomPathBench INHERITED;
    463 };
    464 
    465 class PathTransformBench : public RandomPathBench {
    466 public:
    467     PathTransformBench(bool inPlace) : fInPlace(inPlace) {}
    468 
    469 protected:
    470     const char* onGetName() override {
    471         return fInPlace ? "path_transform_in_place" : "path_transform_copy";
    472     }
    473 
    474     void onDelayedSetup() override {
    475         fMatrix.setScale(5 * SK_Scalar1, 6 * SK_Scalar1);
    476         this->createData(10, 100);
    477         fPaths.reset(kPathCnt);
    478         for (int i = 0; i < kPathCnt; ++i) {
    479             this->makePath(&fPaths[i]);
    480         }
    481         this->finishedMakingPaths();
    482         if (!fInPlace) {
    483             fTransformed.reset(kPathCnt);
    484         }
    485     }
    486 
    487     void onDraw(int loops, SkCanvas*) override {
    488         if (fInPlace) {
    489             for (int i = 0; i < loops; ++i) {
    490                 fPaths[i & (kPathCnt - 1)].transform(fMatrix);
    491             }
    492         } else {
    493             for (int i = 0; i < loops; ++i) {
    494                 int idx = i & (kPathCnt - 1);
    495                 fPaths[idx].transform(fMatrix, &fTransformed[idx]);
    496             }
    497         }
    498     }
    499 
    500 private:
    501     enum {
    502         // must be a pow 2
    503         kPathCnt = 1 << 5,
    504     };
    505     SkAutoTArray<SkPath> fPaths;
    506     SkAutoTArray<SkPath> fTransformed;
    507 
    508     SkMatrix fMatrix;
    509     bool fInPlace;
    510     typedef RandomPathBench INHERITED;
    511 };
    512 
    513 class PathEqualityBench : public RandomPathBench {
    514 public:
    515     PathEqualityBench() { }
    516 
    517 protected:
    518     const char* onGetName() override {
    519         return "path_equality_50%";
    520     }
    521 
    522     void onDelayedSetup() override {
    523         fParity = 0;
    524         this->createData(10, 100);
    525         fPaths.reset(kPathCnt);
    526         fCopies.reset(kPathCnt);
    527         for (int i = 0; i < kPathCnt; ++i) {
    528             this->makePath(&fPaths[i]);
    529             fCopies[i] = fPaths[i];
    530         }
    531         this->finishedMakingPaths();
    532     }
    533 
    534     void onDraw(int loops, SkCanvas*) override {
    535         for (int i = 0; i < loops; ++i) {
    536             int idx = i & (kPathCnt - 1);
    537             fParity ^= (fPaths[idx] == fCopies[idx & ~0x1]);
    538         }
    539     }
    540 
    541 private:
    542     bool fParity; // attempt to keep compiler from optimizing out the ==
    543     enum {
    544         // must be a pow 2
    545         kPathCnt = 1 << 5,
    546     };
    547     SkAutoTArray<SkPath> fPaths;
    548     SkAutoTArray<SkPath> fCopies;
    549     typedef RandomPathBench INHERITED;
    550 };
    551 
    552 class SkBench_AddPathTest : public RandomPathBench {
    553 public:
    554     enum AddType {
    555         kAdd_AddType,
    556         kAddTrans_AddType,
    557         kAddMatrix_AddType,
    558         kReverseAdd_AddType,
    559         kReversePathTo_AddType,
    560     };
    561 
    562     SkBench_AddPathTest(AddType type) : fType(type) {
    563         fMatrix.setRotate(60 * SK_Scalar1);
    564     }
    565 
    566 protected:
    567     const char* onGetName() override {
    568         switch (fType) {
    569             case kAdd_AddType:
    570                 return "path_add_path";
    571             case kAddTrans_AddType:
    572                 return "path_add_path_trans";
    573             case kAddMatrix_AddType:
    574                 return "path_add_path_matrix";
    575             case kReverseAdd_AddType:
    576                 return "path_reverse_add_path";
    577             case kReversePathTo_AddType:
    578                 return "path_reverse_path_to";
    579             default:
    580                 SkDEBUGFAIL("Bad add type");
    581                 return "";
    582         }
    583     }
    584 
    585     void onDelayedSetup() override {
    586         // reversePathTo assumes a single contour path.
    587         bool allowMoves = kReversePathTo_AddType != fType;
    588         this->createData(10, 100, allowMoves);
    589         fPaths0.reset(kPathCnt);
    590         fPaths1.reset(kPathCnt);
    591         for (int i = 0; i < kPathCnt; ++i) {
    592             this->makePath(&fPaths0[i]);
    593             this->makePath(&fPaths1[i]);
    594         }
    595         this->finishedMakingPaths();
    596     }
    597 
    598     void onDraw(int loops, SkCanvas*) override {
    599         switch (fType) {
    600             case kAdd_AddType:
    601                 for (int i = 0; i < loops; ++i) {
    602                     int idx = i & (kPathCnt - 1);
    603                     SkPath result = fPaths0[idx];
    604                     result.addPath(fPaths1[idx]);
    605                 }
    606                 break;
    607             case kAddTrans_AddType:
    608                 for (int i = 0; i < loops; ++i) {
    609                     int idx = i & (kPathCnt - 1);
    610                     SkPath result = fPaths0[idx];
    611                     result.addPath(fPaths1[idx], 2 * SK_Scalar1, 5 * SK_Scalar1);
    612                 }
    613                 break;
    614             case kAddMatrix_AddType:
    615                 for (int i = 0; i < loops; ++i) {
    616                     int idx = i & (kPathCnt - 1);
    617                     SkPath result = fPaths0[idx];
    618                     result.addPath(fPaths1[idx], fMatrix);
    619                 }
    620                 break;
    621             case kReverseAdd_AddType:
    622                 for (int i = 0; i < loops; ++i) {
    623                     int idx = i & (kPathCnt - 1);
    624                     SkPath result = fPaths0[idx];
    625                     result.reverseAddPath(fPaths1[idx]);
    626                 }
    627                 break;
    628             case kReversePathTo_AddType:
    629                 for (int i = 0; i < loops; ++i) {
    630                     int idx = i & (kPathCnt - 1);
    631                     SkPath result = fPaths0[idx];
    632                     result.reversePathTo(fPaths1[idx]);
    633                 }
    634                 break;
    635         }
    636     }
    637 
    638 private:
    639     AddType fType; // or reverseAddPath
    640     enum {
    641         // must be a pow 2
    642         kPathCnt = 1 << 5,
    643     };
    644     SkAutoTArray<SkPath> fPaths0;
    645     SkAutoTArray<SkPath> fPaths1;
    646     SkMatrix         fMatrix;
    647     typedef RandomPathBench INHERITED;
    648 };
    649 
    650 
    651 class CirclesBench : public Benchmark {
    652 protected:
    653     SkString            fName;
    654     Flags               fFlags;
    655 
    656 public:
    657     CirclesBench(Flags flags) : fFlags(flags) {
    658         fName.printf("circles_%s", fFlags & kStroke_Flag ? "stroke" : "fill");
    659     }
    660 
    661 protected:
    662     const char* onGetName() override {
    663         return fName.c_str();
    664     }
    665 
    666     void onDraw(int loops, SkCanvas* canvas) override {
    667         SkPaint paint;
    668 
    669         paint.setColor(SK_ColorBLACK);
    670         paint.setAntiAlias(true);
    671         if (fFlags & kStroke_Flag) {
    672             paint.setStyle(SkPaint::kStroke_Style);
    673         }
    674 
    675         SkRandom rand;
    676 
    677         SkRect r;
    678 
    679         for (int i = 0; i < loops; ++i) {
    680             SkScalar radius = rand.nextUScalar1() * 3;
    681             r.fLeft = rand.nextUScalar1() * 300;
    682             r.fTop =  rand.nextUScalar1() * 300;
    683             r.fRight =  r.fLeft + 2 * radius;
    684             r.fBottom = r.fTop + 2 * radius;
    685 
    686             if (fFlags & kStroke_Flag) {
    687                 paint.setStrokeWidth(rand.nextUScalar1() * 5.0f);
    688             }
    689 
    690             SkPath temp;
    691 
    692             // mimic how Chrome does circles
    693             temp.arcTo(r, 0, 0, false);
    694             temp.addOval(r, SkPath::kCCW_Direction);
    695             temp.arcTo(r, 360, 0, true);
    696             temp.close();
    697 
    698             canvas->drawPath(temp, paint);
    699         }
    700     }
    701 
    702 private:
    703     typedef Benchmark INHERITED;
    704 };
    705 
    706 
    707 // Chrome creates its own round rects with each corner possibly being different.
    708 // In its "zero radius" incarnation it creates degenerate round rects.
    709 // Note: PathTest::test_arb_round_rect_is_convex and
    710 // test_arb_zero_rad_round_rect_is_rect perform almost exactly
    711 // the same test (but with no drawing)
    712 class ArbRoundRectBench : public Benchmark {
    713 protected:
    714     SkString            fName;
    715 
    716 public:
    717     ArbRoundRectBench(bool zeroRad) : fZeroRad(zeroRad) {
    718         if (zeroRad) {
    719             fName.printf("zeroradroundrect");
    720         } else {
    721             fName.printf("arbroundrect");
    722         }
    723     }
    724 
    725 protected:
    726     const char* onGetName() override {
    727         return fName.c_str();
    728     }
    729 
    730     static void add_corner_arc(SkPath* path, const SkRect& rect,
    731                                SkScalar xIn, SkScalar yIn,
    732                                int startAngle)
    733     {
    734 
    735         SkScalar rx = SkMinScalar(rect.width(), xIn);
    736         SkScalar ry = SkMinScalar(rect.height(), yIn);
    737 
    738         SkRect arcRect;
    739         arcRect.set(-rx, -ry, rx, ry);
    740         switch (startAngle) {
    741         case 0:
    742             arcRect.offset(rect.fRight - arcRect.fRight, rect.fBottom - arcRect.fBottom);
    743             break;
    744         case 90:
    745             arcRect.offset(rect.fLeft - arcRect.fLeft, rect.fBottom - arcRect.fBottom);
    746             break;
    747         case 180:
    748             arcRect.offset(rect.fLeft - arcRect.fLeft, rect.fTop - arcRect.fTop);
    749             break;
    750         case 270:
    751             arcRect.offset(rect.fRight - arcRect.fRight, rect.fTop - arcRect.fTop);
    752             break;
    753         default:
    754             break;
    755         }
    756 
    757         path->arcTo(arcRect, SkIntToScalar(startAngle), SkIntToScalar(90), false);
    758     }
    759 
    760     static void make_arb_round_rect(SkPath* path, const SkRect& r,
    761                                     SkScalar xCorner, SkScalar yCorner) {
    762         // we are lazy here and use the same x & y for each corner
    763         add_corner_arc(path, r, xCorner, yCorner, 270);
    764         add_corner_arc(path, r, xCorner, yCorner, 0);
    765         add_corner_arc(path, r, xCorner, yCorner, 90);
    766         add_corner_arc(path, r, xCorner, yCorner, 180);
    767         path->close();
    768 
    769         SkASSERT(path->isConvex());
    770     }
    771 
    772     void onDraw(int loops, SkCanvas* canvas) override {
    773         SkRandom rand;
    774         SkRect r;
    775 
    776         for (int i = 0; i < loops; ++i) {
    777             SkPaint paint;
    778             paint.setColor(0xff000000 | rand.nextU());
    779             paint.setAntiAlias(true);
    780 
    781             SkScalar size = rand.nextUScalar1() * 30;
    782             if (size < SK_Scalar1) {
    783                 continue;
    784             }
    785             r.fLeft = rand.nextUScalar1() * 300;
    786             r.fTop =  rand.nextUScalar1() * 300;
    787             r.fRight =  r.fLeft + 2 * size;
    788             r.fBottom = r.fTop + 2 * size;
    789 
    790             SkPath temp;
    791 
    792             if (fZeroRad) {
    793                 make_arb_round_rect(&temp, r, 0, 0);
    794 
    795                 SkASSERT(temp.isRect(nullptr));
    796             } else {
    797                 make_arb_round_rect(&temp, r, r.width() / 10, r.height() / 15);
    798             }
    799 
    800             canvas->drawPath(temp, paint);
    801         }
    802     }
    803 
    804 private:
    805     bool fZeroRad;      // should 0 radius rounds rects be tested?
    806 
    807     typedef Benchmark INHERITED;
    808 };
    809 
    810 class ConservativelyContainsBench : public Benchmark {
    811 public:
    812     enum Type {
    813         kRect_Type,
    814         kRoundRect_Type,
    815         kOval_Type,
    816     };
    817 
    818     ConservativelyContainsBench(Type type)  {
    819         fParity = false;
    820         fName = "conservatively_contains_";
    821         switch (type) {
    822             case kRect_Type:
    823                 fName.append("rect");
    824                 fPath.addRect(kBaseRect);
    825                 break;
    826             case kRoundRect_Type:
    827                 fName.append("round_rect");
    828                 fPath.addRoundRect(kBaseRect, kRRRadii[0], kRRRadii[1]);
    829                 break;
    830             case kOval_Type:
    831                 fName.append("oval");
    832                 fPath.addOval(kBaseRect);
    833                 break;
    834         }
    835     }
    836 
    837     bool isSuitableFor(Backend backend) override {
    838         return backend == kNonRendering_Backend;
    839     }
    840 
    841 private:
    842     const char* onGetName() override {
    843         return fName.c_str();
    844     }
    845 
    846     void onDraw(int loops, SkCanvas*) override {
    847         for (int i = 0; i < loops; ++i) {
    848             const SkRect& rect = fQueryRects[i % kQueryRectCnt];
    849             fParity = fParity != fPath.conservativelyContainsRect(rect);
    850         }
    851     }
    852 
    853     void onDelayedSetup() override {
    854         fQueryRects.setCount(kQueryRectCnt);
    855 
    856         SkRandom rand;
    857         for (int i = 0; i < kQueryRectCnt; ++i) {
    858             SkSize size;
    859             SkPoint xy;
    860             size.fWidth = rand.nextRangeScalar(kQueryMin.fWidth,  kQueryMax.fWidth);
    861             size.fHeight = rand.nextRangeScalar(kQueryMin.fHeight, kQueryMax.fHeight);
    862             xy.fX = rand.nextRangeScalar(kBounds.fLeft, kBounds.fRight - size.fWidth);
    863             xy.fY = rand.nextRangeScalar(kBounds.fTop, kBounds.fBottom - size.fHeight);
    864 
    865             fQueryRects[i] = SkRect::MakeXYWH(xy.fX, xy.fY, size.fWidth, size.fHeight);
    866         }
    867     }
    868 
    869     enum {
    870         kQueryRectCnt = 400,
    871     };
    872     static const SkRect kBounds;   // bounds for all random query rects
    873     static const SkSize kQueryMin; // minimum query rect size, should be <= kQueryMax
    874     static const SkSize kQueryMax; // max query rect size, should < kBounds
    875     static const SkRect kBaseRect; // rect that is used to construct the path
    876     static const SkScalar kRRRadii[2]; // x and y radii for round rect
    877 
    878     SkString            fName;
    879     SkPath              fPath;
    880     bool                fParity;
    881     SkTDArray<SkRect>   fQueryRects;
    882 
    883     typedef Benchmark INHERITED;
    884 };
    885 
    886 ///////////////////////////////////////////////////////////////////////////////
    887 
    888 #include "SkGeometry.h"
    889 
    890 class ConicBench_Chop : public Benchmark {
    891 protected:
    892     SkConic fRQ, fDst[2];
    893     SkString fName;
    894 public:
    895     ConicBench_Chop() : fName("conic-chop") {
    896         fRQ.fPts[0].set(0, 0);
    897         fRQ.fPts[1].set(100, 0);
    898         fRQ.fPts[2].set(100, 100);
    899         fRQ.fW = SkScalarCos(SK_ScalarPI/4);
    900     }
    901 
    902     bool isSuitableFor(Backend backend) override {
    903         return backend == kNonRendering_Backend;
    904     }
    905 
    906 private:
    907     const char* onGetName() override { return fName.c_str(); }
    908 
    909     void onDraw(int loops, SkCanvas*) override {
    910         for (int i = 0; i < loops; ++i) {
    911             fRQ.chop(fDst);
    912         }
    913     }
    914 
    915     typedef Benchmark INHERITED;
    916 };
    917 DEF_BENCH( return new ConicBench_Chop; )
    918 
    919 class ConicBench_EvalPos : public ConicBench_Chop {
    920     const bool fUseV2;
    921 public:
    922     ConicBench_EvalPos(bool useV2) : fUseV2(useV2) {
    923         fName.printf("conic-eval-pos%d", useV2);
    924     }
    925     void onDraw(int loops, SkCanvas*) override {
    926         if (fUseV2) {
    927             for (int i = 0; i < loops; ++i) {
    928                 for (int j = 0; j < 1000; ++j) {
    929                     fDst[0].fPts[0] = fRQ.evalAt(0.4f);
    930                 }
    931             }
    932         } else {
    933             for (int i = 0; i < loops; ++i) {
    934                 for (int j = 0; j < 1000; ++j) {
    935                     fRQ.evalAt(0.4f, &fDst[0].fPts[0], nullptr);
    936                 }
    937             }
    938         }
    939     }
    940 };
    941 DEF_BENCH( return new ConicBench_EvalPos(false); )
    942 DEF_BENCH( return new ConicBench_EvalPos(true); )
    943 
    944 class ConicBench_EvalTan : public ConicBench_Chop {
    945     const bool fUseV2;
    946 public:
    947     ConicBench_EvalTan(bool useV2) : fUseV2(useV2) {
    948         fName.printf("conic-eval-tan%d", useV2);
    949     }
    950     void onDraw(int loops, SkCanvas*) override {
    951         if (fUseV2) {
    952             for (int i = 0; i < loops; ++i) {
    953                 for (int j = 0; j < 1000; ++j) {
    954                     fDst[0].fPts[0] = fRQ.evalTangentAt(0.4f);
    955                 }
    956             }
    957         } else {
    958             for (int i = 0; i < loops; ++i) {
    959                 for (int j = 0; j < 1000; ++j) {
    960                     fRQ.evalAt(0.4f, nullptr, &fDst[0].fPts[0]);
    961                 }
    962             }
    963         }
    964     }
    965 };
    966 DEF_BENCH( return new ConicBench_EvalTan(false); )
    967 DEF_BENCH( return new ConicBench_EvalTan(true); )
    968 
    969 ///////////////////////////////////////////////////////////////////////////////
    970 
    971 static void rand_conic(SkConic* conic, SkRandom& rand) {
    972     for (int i = 0; i < 3; ++i) {
    973         conic->fPts[i].set(rand.nextUScalar1() * 100, rand.nextUScalar1() * 100);
    974     }
    975     if (rand.nextUScalar1() > 0.5f) {
    976         conic->fW = rand.nextUScalar1();
    977     } else {
    978         conic->fW = 1 + rand.nextUScalar1() * 4;
    979     }
    980 }
    981 
    982 class ConicBench : public Benchmark {
    983 public:
    984     ConicBench()  {
    985         SkRandom rand;
    986         for (int i = 0; i < CONICS; ++i) {
    987             rand_conic(&fConics[i], rand);
    988         }
    989     }
    990 
    991     bool isSuitableFor(Backend backend) override {
    992         return backend == kNonRendering_Backend;
    993     }
    994 
    995 protected:
    996     enum {
    997         CONICS = 100
    998     };
    999     SkConic fConics[CONICS];
   1000 
   1001 private:
   1002     typedef Benchmark INHERITED;
   1003 };
   1004 
   1005 class ConicBench_ComputeError : public ConicBench {
   1006 public:
   1007     ConicBench_ComputeError()  {}
   1008 
   1009 protected:
   1010     const char* onGetName() override {
   1011         return "conic-compute-error";
   1012     }
   1013 
   1014     void onDraw(int loops, SkCanvas*) override {
   1015         SkVector err;
   1016         for (int i = 0; i < loops; ++i) {
   1017             for (int j = 0; j < CONICS; ++j) {
   1018                 fConics[j].computeAsQuadError(&err);
   1019             }
   1020         }
   1021     }
   1022 
   1023 private:
   1024     typedef ConicBench INHERITED;
   1025 };
   1026 
   1027 class ConicBench_asQuadTol : public ConicBench {
   1028 public:
   1029     ConicBench_asQuadTol()  {}
   1030 
   1031 protected:
   1032     const char* onGetName() override {
   1033         return "conic-asQuadTol";
   1034     }
   1035 
   1036     void onDraw(int loops, SkCanvas*) override {
   1037         for (int i = 0; i < loops; ++i) {
   1038             for (int j = 0; j < CONICS; ++j) {
   1039                 fConics[j].asQuadTol(SK_ScalarHalf);
   1040             }
   1041         }
   1042     }
   1043 
   1044 private:
   1045     typedef ConicBench INHERITED;
   1046 };
   1047 
   1048 class ConicBench_quadPow2 : public ConicBench {
   1049 public:
   1050     ConicBench_quadPow2()  {}
   1051 
   1052 protected:
   1053     const char* onGetName() override {
   1054         return "conic-quadPow2";
   1055     }
   1056 
   1057     void onDraw(int loops, SkCanvas*) override {
   1058         for (int i = 0; i < loops; ++i) {
   1059             for (int j = 0; j < CONICS; ++j) {
   1060                 fConics[j].computeQuadPOW2(SK_ScalarHalf);
   1061             }
   1062         }
   1063     }
   1064 
   1065 private:
   1066     typedef ConicBench INHERITED;
   1067 };
   1068 
   1069 ///////////////////////////////////////////////////////////////////////////////
   1070 
   1071 class TightBoundsBench : public Benchmark {
   1072     SkPath      fPath;
   1073     SkString    fName;
   1074     SkRect      (*fProc)(const SkPath&);
   1075 
   1076 public:
   1077     TightBoundsBench(SkRect (*proc)(const SkPath&), const char suffix[]) : fProc(proc) {
   1078         fName.printf("tight_bounds_%s", suffix);
   1079 
   1080         const int N = 100;
   1081         SkRandom rand;
   1082         for (int i = 0; i < N; ++i) {
   1083             fPath.moveTo(rand.nextF()*100, rand.nextF()*100);
   1084             fPath.lineTo(rand.nextF()*100, rand.nextF()*100);
   1085             fPath.quadTo(rand.nextF()*100, rand.nextF()*100, rand.nextF()*100, rand.nextF()*100);
   1086             fPath.conicTo(rand.nextF()*100, rand.nextF()*100, rand.nextF()*100, rand.nextF()*100,
   1087                           rand.nextF()*10);
   1088             fPath.cubicTo(rand.nextF()*100, rand.nextF()*100, rand.nextF()*100, rand.nextF()*100,
   1089                           rand.nextF()*100, rand.nextF()*100);
   1090         }
   1091     }
   1092 
   1093 protected:
   1094     bool isSuitableFor(Backend backend) override {
   1095         return backend == kNonRendering_Backend;
   1096     }
   1097 
   1098     const char* onGetName() override { return fName.c_str(); }
   1099 
   1100     void onDraw(int loops, SkCanvas* canvas) override {
   1101         for (int i = 0; i < loops*100; ++i) {
   1102             fProc(fPath);
   1103         }
   1104     }
   1105 
   1106 private:
   1107     typedef Benchmark INHERITED;
   1108 };
   1109 
   1110 
   1111 const SkRect ConservativelyContainsBench::kBounds = SkRect::MakeWH(SkIntToScalar(100), SkIntToScalar(100));
   1112 const SkSize ConservativelyContainsBench::kQueryMin = {SkIntToScalar(1), SkIntToScalar(1)};
   1113 const SkSize ConservativelyContainsBench::kQueryMax = {SkIntToScalar(40), SkIntToScalar(40)};
   1114 const SkRect ConservativelyContainsBench::kBaseRect = SkRect::MakeXYWH(SkIntToScalar(25), SkIntToScalar(25), SkIntToScalar(50), SkIntToScalar(50));
   1115 const SkScalar ConservativelyContainsBench::kRRRadii[2] = {SkIntToScalar(5), SkIntToScalar(10)};
   1116 
   1117 DEF_BENCH( return new TrianglePathBench(FLAGS00); )
   1118 DEF_BENCH( return new TrianglePathBench(FLAGS01); )
   1119 DEF_BENCH( return new TrianglePathBench(FLAGS10); )
   1120 DEF_BENCH( return new TrianglePathBench(FLAGS11); )
   1121 
   1122 DEF_BENCH( return new RectPathBench(FLAGS00); )
   1123 DEF_BENCH( return new RectPathBench(FLAGS01); )
   1124 DEF_BENCH( return new RectPathBench(FLAGS10); )
   1125 DEF_BENCH( return new RectPathBench(FLAGS11); )
   1126 
   1127 DEF_BENCH( return new RotatedRectBench(FLAGS00, false, 45));
   1128 DEF_BENCH( return new RotatedRectBench(FLAGS10, false, 45));
   1129 DEF_BENCH( return new RotatedRectBench(FLAGS00, true, 45));
   1130 DEF_BENCH( return new RotatedRectBench(FLAGS10, true, 45));
   1131 
   1132 DEF_BENCH( return new OvalPathBench(FLAGS00); )
   1133 DEF_BENCH( return new OvalPathBench(FLAGS01); )
   1134 DEF_BENCH( return new OvalPathBench(FLAGS10); )
   1135 DEF_BENCH( return new OvalPathBench(FLAGS11); )
   1136 
   1137 DEF_BENCH( return new CirclePathBench(FLAGS00); )
   1138 DEF_BENCH( return new CirclePathBench(FLAGS01); )
   1139 DEF_BENCH( return new CirclePathBench(FLAGS10); )
   1140 DEF_BENCH( return new CirclePathBench(FLAGS11); )
   1141 
   1142 DEF_BENCH( return new NonAACirclePathBench(FLAGS00); )
   1143 DEF_BENCH( return new NonAACirclePathBench(FLAGS10); )
   1144 
   1145 DEF_BENCH( return new AAAConcavePathBench(FLAGS00); )
   1146 DEF_BENCH( return new AAAConcavePathBench(FLAGS10); )
   1147 DEF_BENCH( return new AAAConvexPathBench(FLAGS00); )
   1148 DEF_BENCH( return new AAAConvexPathBench(FLAGS10); )
   1149 
   1150 DEF_BENCH( return new SawToothPathBench(FLAGS00); )
   1151 DEF_BENCH( return new SawToothPathBench(FLAGS01); )
   1152 
   1153 DEF_BENCH( return new LongCurvedPathBench(FLAGS00); )
   1154 DEF_BENCH( return new LongCurvedPathBench(FLAGS01); )
   1155 DEF_BENCH( return new LongLinePathBench(FLAGS00); )
   1156 DEF_BENCH( return new LongLinePathBench(FLAGS01); )
   1157 
   1158 DEF_BENCH( return new PathCreateBench(); )
   1159 DEF_BENCH( return new PathCopyBench(); )
   1160 DEF_BENCH( return new PathTransformBench(true); )
   1161 DEF_BENCH( return new PathTransformBench(false); )
   1162 DEF_BENCH( return new PathEqualityBench(); )
   1163 
   1164 DEF_BENCH( return new SkBench_AddPathTest(SkBench_AddPathTest::kAdd_AddType); )
   1165 DEF_BENCH( return new SkBench_AddPathTest(SkBench_AddPathTest::kAddTrans_AddType); )
   1166 DEF_BENCH( return new SkBench_AddPathTest(SkBench_AddPathTest::kAddMatrix_AddType); )
   1167 DEF_BENCH( return new SkBench_AddPathTest(SkBench_AddPathTest::kReverseAdd_AddType); )
   1168 DEF_BENCH( return new SkBench_AddPathTest(SkBench_AddPathTest::kReversePathTo_AddType); )
   1169 
   1170 DEF_BENCH( return new CirclesBench(FLAGS00); )
   1171 DEF_BENCH( return new CirclesBench(FLAGS01); )
   1172 DEF_BENCH( return new ArbRoundRectBench(false); )
   1173 DEF_BENCH( return new ArbRoundRectBench(true); )
   1174 DEF_BENCH( return new ConservativelyContainsBench(ConservativelyContainsBench::kRect_Type); )
   1175 DEF_BENCH( return new ConservativelyContainsBench(ConservativelyContainsBench::kRoundRect_Type); )
   1176 DEF_BENCH( return new ConservativelyContainsBench(ConservativelyContainsBench::kOval_Type); )
   1177 
   1178 #include "SkPathOps.h"
   1179 #include "SkPathPriv.h"
   1180 DEF_BENCH( return new TightBoundsBench([](const SkPath& path){ return path.computeTightBounds();},
   1181                                        "priv"); )
   1182 DEF_BENCH( return new TightBoundsBench([](const SkPath& path) {
   1183         SkRect bounds; TightBounds(path, &bounds); return bounds;
   1184     }, "pathops"); )
   1185 
   1186 // These seem to be optimized away, which is troublesome for timing.
   1187 /*
   1188 DEF_BENCH( return new ConicBench_Chop5() )
   1189 DEF_BENCH( return new ConicBench_ComputeError() )
   1190 DEF_BENCH( return new ConicBench_asQuadTol() )
   1191 DEF_BENCH( return new ConicBench_quadPow2() )
   1192 */
   1193