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