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