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