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