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