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:
PathBench(Flags flags)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;
complexity()43     virtual int complexity() { return 0; }
44 
45 protected:
onGetName()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 
onDraw(int loops,SkCanvas * canvas)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:
TrianglePathBench(Flags flags)76     TrianglePathBench(Flags flags) : INHERITED(flags) {}
77 
appendName(SkString * name)78     void appendName(SkString* name) override {
79         name->append("triangle");
80     }
makePath(SkPath * path)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:
RectPathBench(Flags flags)96     RectPathBench(Flags flags) : INHERITED(flags) {}
97 
appendName(SkString * name)98     void appendName(SkString* name) override {
99         name->append("rect");
100     }
makePath(SkPath * path)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:
RotatedRectBench(Flags flags,bool aa,int degrees)111     RotatedRectBench(Flags flags, bool aa, int degrees) : INHERITED(flags) {
112         fAA = aa;
113         fDegrees = degrees;
114     }
115 
appendName(SkString * name)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 
makePath(SkPath * path)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 
setupPaint(SkPaint * paint)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:
OvalPathBench(Flags flags)142     OvalPathBench(Flags flags) : INHERITED(flags) {}
143 
appendName(SkString * name)144     void appendName(SkString* name) override {
145         name->append("oval");
146     }
makePath(SkPath * path)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:
CirclePathBench(Flags flags)157     CirclePathBench(Flags flags) : INHERITED(flags) {}
158 
appendName(SkString * name)159     void appendName(SkString* name) override {
160         name->append("circle");
161     }
makePath(SkPath * path)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:
NonAACirclePathBench(Flags flags)172     NonAACirclePathBench(Flags flags) : INHERITED(flags) {}
173 
appendName(SkString * name)174     void appendName(SkString* name) override {
175         name->append("nonaacircle");
176     }
177 
setupPaint(SkPaint * paint)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:
AAAConcavePathBench(Flags flags)190     AAAConcavePathBench(Flags flags) : INHERITED(flags) {}
191 
appendName(SkString * name)192     void appendName(SkString* name) override {
193         name->append("concave_aaa");
194     }
195 
makePath(SkPath * path)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:
AAAConvexPathBench(Flags flags)211     AAAConvexPathBench(Flags flags) : INHERITED(flags) {}
212 
appendName(SkString * name)213     void appendName(SkString* name) override {
214         name->append("convex_aaa");
215     }
216 
makePath(SkPath * path)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:
SawToothPathBench(Flags flags)230     SawToothPathBench(Flags flags) : INHERITED(flags) {}
231 
appendName(SkString * name)232     void appendName(SkString* name) override {
233         name->append("sawtooth");
234     }
makePath(SkPath * path)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     }
complexity()253     int complexity() override { return 1; }
254 private:
255     typedef PathBench INHERITED;
256 };
257 
258 class LongCurvedPathBench : public PathBench {
259 public:
LongCurvedPathBench(Flags flags)260     LongCurvedPathBench(Flags flags) : INHERITED(flags) {}
261 
appendName(SkString * name)262     void appendName(SkString* name) override {
263         name->append("long_curved");
264     }
makePath(SkPath * path)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     }
complexity()274     int complexity() override { return 2; }
275 private:
276     typedef PathBench INHERITED;
277 };
278 
279 class LongLinePathBench : public PathBench {
280 public:
LongLinePathBench(Flags flags)281     LongLinePathBench(Flags flags) : INHERITED(flags) {}
282 
appendName(SkString * name)283     void appendName(SkString* name) override {
284         name->append("long_line");
285     }
makePath(SkPath * path)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     }
complexity()293     int complexity() override { return 2; }
294 private:
295     typedef PathBench INHERITED;
296 };
297 
298 class RandomPathBench : public Benchmark {
299 public:
isSuitableFor(Backend backend)300     bool isSuitableFor(Backend backend) override {
301         return backend == kNonRendering_Backend;
302     }
303 
304 protected:
createData(int minVerbs,int maxVerbs,bool allowMoves=true,SkRect * bounds=nullptr)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 
restartMakingPaths()332     void restartMakingPaths() {
333         fCurrPath = 0;
334         fCurrVerb = 0;
335         fCurrPoint = 0;
336     }
337 
makePath(SkPath * path)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 
finishedMakingPaths()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:
PathCreateBench()401     PathCreateBench()  {
402     }
403 
404 protected:
onGetName()405     const char* onGetName() override {
406         return "path_create";
407     }
408 
onDelayedSetup()409     void onDelayedSetup() override {
410         this->createData(10, 100);
411     }
412 
onDraw(int loops,SkCanvas *)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:
PathCopyBench()431     PathCopyBench()  {
432     }
433 
434 protected:
onGetName()435     const char* onGetName() override {
436         return "path_copy";
437     }
onDelayedSetup()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     }
onDraw(int loops,SkCanvas *)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:
PathTransformBench(bool inPlace)467     PathTransformBench(bool inPlace) : fInPlace(inPlace) {}
468 
469 protected:
onGetName()470     const char* onGetName() override {
471         return fInPlace ? "path_transform_in_place" : "path_transform_copy";
472     }
473 
onDelayedSetup()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 
onDraw(int loops,SkCanvas *)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:
PathEqualityBench()515     PathEqualityBench() { }
516 
517 protected:
onGetName()518     const char* onGetName() override {
519         return "path_equality_50%";
520     }
521 
onDelayedSetup()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 
onDraw(int loops,SkCanvas *)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 
SkBench_AddPathTest(AddType type)562     SkBench_AddPathTest(AddType type) : fType(type) {
563         fMatrix.setRotate(60 * SK_Scalar1);
564     }
565 
566 protected:
onGetName()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 
onDelayedSetup()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 
onDraw(int loops,SkCanvas *)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:
CirclesBench(Flags flags)657     CirclesBench(Flags flags) : fFlags(flags) {
658         fName.printf("circles_%s", fFlags & kStroke_Flag ? "stroke" : "fill");
659     }
660 
661 protected:
onGetName()662     const char* onGetName() override {
663         return fName.c_str();
664     }
665 
onDraw(int loops,SkCanvas * canvas)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:
ArbRoundRectBench(bool zeroRad)717     ArbRoundRectBench(bool zeroRad) : fZeroRad(zeroRad) {
718         if (zeroRad) {
719             fName.printf("zeroradroundrect");
720         } else {
721             fName.printf("arbroundrect");
722         }
723     }
724 
725 protected:
onGetName()726     const char* onGetName() override {
727         return fName.c_str();
728     }
729 
add_corner_arc(SkPath * path,const SkRect & rect,SkScalar xIn,SkScalar yIn,int startAngle)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 
make_arb_round_rect(SkPath * path,const SkRect & r,SkScalar xCorner,SkScalar yCorner)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 
onDraw(int loops,SkCanvas * canvas)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 
ConservativelyContainsBench(Type type)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 
isSuitableFor(Backend backend)837     bool isSuitableFor(Backend backend) override {
838         return backend == kNonRendering_Backend;
839     }
840 
841 private:
onGetName()842     const char* onGetName() override {
843         return fName.c_str();
844     }
845 
onDraw(int loops,SkCanvas *)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 
onDelayedSetup()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:
ConicBench_Chop()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 
isSuitableFor(Backend backend)902     bool isSuitableFor(Backend backend) override {
903         return backend == kNonRendering_Backend;
904     }
905 
906 private:
onGetName()907     const char* onGetName() override { return fName.c_str(); }
908 
onDraw(int loops,SkCanvas *)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:
ConicBench_EvalPos(bool useV2)922     ConicBench_EvalPos(bool useV2) : fUseV2(useV2) {
923         fName.printf("conic-eval-pos%d", useV2);
924     }
onDraw(int loops,SkCanvas *)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:
ConicBench_EvalTan(bool useV2)947     ConicBench_EvalTan(bool useV2) : fUseV2(useV2) {
948         fName.printf("conic-eval-tan%d", useV2);
949     }
onDraw(int loops,SkCanvas *)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); )
DEF_BENCH(return new ConicBench_EvalTan (true);)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:
ConicBench()984     ConicBench()  {
985         SkRandom rand;
986         for (int i = 0; i < CONICS; ++i) {
987             rand_conic(&fConics[i], rand);
988         }
989     }
990 
isSuitableFor(Backend backend)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:
ConicBench_ComputeError()1007     ConicBench_ComputeError()  {}
1008 
1009 protected:
onGetName()1010     const char* onGetName() override {
1011         return "conic-compute-error";
1012     }
1013 
onDraw(int loops,SkCanvas *)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:
ConicBench_asQuadTol()1029     ConicBench_asQuadTol()  {}
1030 
1031 protected:
onGetName()1032     const char* onGetName() override {
1033         return "conic-asQuadTol";
1034     }
1035 
onDraw(int loops,SkCanvas *)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:
ConicBench_quadPow2()1050     ConicBench_quadPow2()  {}
1051 
1052 protected:
onGetName()1053     const char* onGetName() override {
1054         return "conic-quadPow2";
1055     }
1056 
onDraw(int loops,SkCanvas *)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:
TightBoundsBench(SkRect (* proc)(const SkPath &),const char suffix[])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:
isSuitableFor(Backend backend)1094     bool isSuitableFor(Backend backend) override {
1095         return backend == kNonRendering_Backend;
1096     }
1097 
onGetName()1098     const char* onGetName() override { return fName.c_str(); }
1099 
onDraw(int loops,SkCanvas * canvas)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 
__anonf7fa272c0802(const SkPath& path)1181 DEF_BENCH( return new TightBoundsBench([](const SkPath& path){ return path.computeTightBounds();},
1182                                        "priv"); )
__anonf7fa272c0902(const SkPath& path) 1183 DEF_BENCH( return new TightBoundsBench([](const SkPath& path) {
1184         SkRect bounds; TightBounds(path, &bounds); return bounds;
1185     }, "pathops"); )
1186 
1187 // These seem to be optimized away, which is troublesome for timing.
1188 /*
1189 DEF_BENCH( return new ConicBench_Chop5() )
1190 DEF_BENCH( return new ConicBench_ComputeError() )
1191 DEF_BENCH( return new ConicBench_asQuadTol() )
1192 DEF_BENCH( return new ConicBench_quadPow2() )
1193 */
1194 
1195 class CommonConvexBench : public Benchmark {
1196 protected:
1197     SkString    fName;
1198     SkPath      fPath;
1199     const bool  fAA;
1200 
1201 public:
CommonConvexBench(int w,int h,bool forceConcave,bool aa)1202     CommonConvexBench(int w, int h, bool forceConcave, bool aa) : fAA(aa) {
1203         fName.printf("convex_path_%d_%d_%d_%d", w, h, forceConcave, aa);
1204 
1205         SkRect r = SkRect::MakeXYWH(10, 10, w*1.0f, h*1.0f);
1206         fPath.addRRect(SkRRect::MakeRectXY(r, w/8.0f, h/8.0f));
1207 
1208         if (forceConcave) {
1209             fPath.setConvexity(SkPath::kConcave_Convexity);
1210             SkASSERT(!fPath.isConvex());
1211         } else {
1212             SkASSERT(fPath.isConvex());
1213         }
1214     }
1215 
1216 protected:
onGetName()1217     const char* onGetName() override {
1218         return fName.c_str();
1219     }
1220 
onDraw(int loops,SkCanvas * canvas)1221     void onDraw(int loops, SkCanvas* canvas) override {
1222         SkPaint paint;
1223         paint.setAntiAlias(fAA);
1224 
1225         for (int i = 0; i < loops; ++i) {
1226             for (int inner = 0; inner < 100; ++inner) {
1227                 canvas->drawPath(fPath, paint);
1228             }
1229         }
1230     }
1231 
1232 private:
1233     typedef Benchmark INHERITED;
1234 };
1235 
1236 DEF_BENCH( return new CommonConvexBench( 16, 16, false, false); )
1237 DEF_BENCH( return new CommonConvexBench( 16, 16, true,  false); )
1238 DEF_BENCH( return new CommonConvexBench( 16, 16, false, true); )
1239 DEF_BENCH( return new CommonConvexBench( 16, 16, true,  true); )
1240 
1241 DEF_BENCH( return new CommonConvexBench(200, 16, false, false); )
1242 DEF_BENCH( return new CommonConvexBench(200, 16, true,  false); )
1243 DEF_BENCH( return new CommonConvexBench(200, 16, false, true); )
1244 DEF_BENCH( return new CommonConvexBench(200, 16, true,  true); )
1245