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