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