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