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