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