1/* 2 * Copyright 2013 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 "DMFontMgr.h" 9#include "DMJsonWriter.h" 10#include "DMSrcSink.h" 11#include "ProcStats.h" 12#include "Resources.h" 13#include "SkBBHFactory.h" 14#include "SkChecksum.h" 15#include "SkChromeTracingTracer.h" 16#include "SkCodec.h" 17#include "SkColorPriv.h" 18#include "SkColorSpace.h" 19#include "SkColorSpacePriv.h" 20#include "SkCommonFlags.h" 21#include "SkCommonFlagsConfig.h" 22#include "SkCommonFlagsGpu.h" 23#include "SkData.h" 24#include "SkDebugfTracer.h" 25#include "SkDocument.h" 26#include "SkEventTracingPriv.h" 27#include "SkFontMgr.h" 28#include "SkFontMgrPriv.h" 29#include "SkGraphics.h" 30#include "SkHalf.h" 31#include "SkLeanWindows.h" 32#include "SkMD5.h" 33#include "SkMutex.h" 34#include "SkOSFile.h" 35#include "SkOSPath.h" 36#include "SkPM4fPriv.h" 37#include "SkPngEncoder.h" 38#include "SkScan.h" 39#include "SkSpinlock.h" 40#include "SkTHash.h" 41#include "SkTaskGroup.h" 42#include "SkTypeface_win.h" 43#include "Test.h" 44#include "Timer.h" 45#include "ios_utils.h" 46#include "picture_utils.h" 47#include "sk_tool_utils.h" 48 49#include <vector> 50 51#ifdef SK_PDF_IMAGE_STATS 52extern void SkPDFImageDumpStats(); 53#endif 54 55#include "png.h" 56 57#include <stdlib.h> 58 59#ifndef SK_BUILD_FOR_WIN 60 #include <unistd.h> 61#endif 62 63extern bool gSkForceRasterPipelineBlitter; 64 65DECLARE_bool(undefok); 66DEFINE_string(src, "tests gm skp image", "Source types to test."); 67DEFINE_bool(nameByHash, false, 68 "If true, write to FLAGS_writePath[0]/<hash>.png instead of " 69 "to FLAGS_writePath[0]/<config>/<sourceType>/<sourceOptions>/<name>.png"); 70DEFINE_bool2(pathOpsExtended, x, false, "Run extended pathOps tests."); 71DEFINE_string(matrix, "1 0 0 1", 72 "2x2 scale+skew matrix to apply or upright when using " 73 "'matrix' or 'upright' in config."); 74DEFINE_bool(gpu_threading, false, "Allow GPU work to run on multiple threads?"); 75 76DEFINE_string(blacklist, "", 77 "Space-separated config/src/srcOptions/name quadruples to blacklist. " 78 "'_' matches anything. '~' negates the match. E.g. \n" 79 "'--blacklist gpu skp _ _' will blacklist all SKPs drawn into the gpu config.\n" 80 "'--blacklist gpu skp _ _ 8888 gm _ aarects' will also blacklist the aarects GM on 8888.\n" 81 "'--blacklist ~8888 svg _ svgparse_' blocks non-8888 SVGs that contain \"svgparse_\" in " 82 "the name."); 83 84DEFINE_string2(readPath, r, "", "If set check for equality with golden results in this directory."); 85 86DEFINE_string(uninterestingHashesFile, "", 87 "File containing a list of uninteresting hashes. If a result hashes to something in " 88 "this list, no image is written for that result."); 89 90DEFINE_int32(shards, 1, "We're splitting source data into this many shards."); 91DEFINE_int32(shard, 0, "Which shard do I run?"); 92 93DEFINE_string(mskps, "", "Directory to read mskps from, or a single mskp file."); 94DEFINE_bool(forceRasterPipeline, false, "sets gSkForceRasterPipelineBlitter"); 95 96DEFINE_bool(ddl, false, "If true, use DeferredDisplayLists for GPU SKP rendering."); 97 98DEFINE_bool(ignoreSigInt, false, "ignore SIGINT signals during test execution"); 99 100DEFINE_string(dont_write, "", "File extensions to skip writing to --writePath."); // See skia:6821 101 102DEFINE_bool(nativeFonts, true, "If true, use native font manager and rendering. " 103 "If false, fonts will draw as portably as possible."); 104DEFINE_bool(gdi, false, "On Windows, use GDI instead of DirectWrite for font rendering."); 105 106using namespace DM; 107using sk_gpu_test::GrContextFactory; 108using sk_gpu_test::GLTestContext; 109using sk_gpu_test::ContextInfo; 110 111/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 112 113static const double kStartMs = SkTime::GetMSecs(); 114 115static FILE* gVLog; 116 117template <typename... Args> 118static void vlog(const char* fmt, Args&&... args) { 119 if (gVLog) { 120 char s[64]; 121 HumanizeMs(s, 64, SkTime::GetMSecs() - kStartMs); 122 fprintf(gVLog, "%s\t", s); 123 fprintf(gVLog, fmt, args...); 124 fflush(gVLog); 125 } 126} 127 128template <typename... Args> 129static void info(const char* fmt, Args&&... args) { 130 vlog(fmt, args...); 131 if (!FLAGS_quiet) { 132 printf(fmt, args...); 133 } 134} 135static void info(const char* fmt) { 136 if (!FLAGS_quiet) { 137 printf("%s", fmt); // Clang warns printf(fmt) is insecure. 138 } 139} 140 141SK_DECLARE_STATIC_MUTEX(gFailuresMutex); 142static SkTArray<SkString> gFailures; 143 144static void fail(const SkString& err) { 145 SkAutoMutexAcquire lock(gFailuresMutex); 146 SkDebugf("\n\nFAILURE: %s\n\n", err.c_str()); 147 gFailures.push_back(err); 148} 149 150struct Running { 151 SkString id; 152 SkThreadID thread; 153 154 void dump() const { 155 info("\t%s\n", id.c_str()); 156 } 157}; 158 159// We use a spinlock to make locking this in a signal handler _somewhat_ safe. 160static SkSpinlock gMutex; 161static int gPending; 162static SkTArray<Running> gRunning; 163 164static void done(const char* config, const char* src, const char* srcOptions, const char* name) { 165 SkString id = SkStringPrintf("%s %s %s %s", config, src, srcOptions, name); 166 vlog("done %s\n", id.c_str()); 167 int pending; 168 { 169 SkAutoMutexAcquire lock(gMutex); 170 for (int i = 0; i < gRunning.count(); i++) { 171 if (gRunning[i].id == id) { 172 gRunning.removeShuffle(i); 173 break; 174 } 175 } 176 pending = --gPending; 177 } 178 179 // We write out dm.json file and print out a progress update every once in a while. 180 // Notice this also handles the final dm.json and progress update when pending == 0. 181 if (pending % 500 == 0) { 182 JsonWriter::DumpJson(); 183 184 int curr = sk_tools::getCurrResidentSetSizeMB(), 185 peak = sk_tools::getMaxResidentSetSizeMB(); 186 SkString elapsed = HumanizeMs(SkTime::GetMSecs() - kStartMs); 187 188 SkAutoMutexAcquire lock(gMutex); 189 info("\n%dMB RAM, %dMB peak, %s elapsed, %d queued, %d active:\n", 190 curr, peak, elapsed.c_str(), gPending - gRunning.count(), gRunning.count()); 191 for (auto& task : gRunning) { 192 task.dump(); 193 } 194 } 195} 196 197static void start(const char* config, const char* src, const char* srcOptions, const char* name) { 198 SkString id = SkStringPrintf("%s %s %s %s", config, src, srcOptions, name); 199 vlog("start %s\n", id.c_str()); 200 SkAutoMutexAcquire lock(gMutex); 201 gRunning.push_back({id,SkGetThreadID()}); 202} 203 204static void find_culprit() { 205 // Assumes gMutex is locked. 206 SkThreadID thisThread = SkGetThreadID(); 207 for (auto& task : gRunning) { 208 if (task.thread == thisThread) { 209 info("Likely culprit:\n"); 210 task.dump(); 211 } 212 } 213} 214 215#if defined(SK_BUILD_FOR_WIN) 216 static LONG WINAPI crash_handler(EXCEPTION_POINTERS* e) { 217 static const struct { 218 const char* name; 219 DWORD code; 220 } kExceptions[] = { 221 #define _(E) {#E, E} 222 _(EXCEPTION_ACCESS_VIOLATION), 223 _(EXCEPTION_BREAKPOINT), 224 _(EXCEPTION_INT_DIVIDE_BY_ZERO), 225 _(EXCEPTION_STACK_OVERFLOW), 226 // TODO: more? 227 #undef _ 228 }; 229 230 SkAutoMutexAcquire lock(gMutex); 231 232 const DWORD code = e->ExceptionRecord->ExceptionCode; 233 info("\nCaught exception %u", code); 234 for (const auto& exception : kExceptions) { 235 if (exception.code == code) { 236 info(" %s", exception.name); 237 } 238 } 239 info(", was running:\n"); 240 for (auto& task : gRunning) { 241 task.dump(); 242 } 243 find_culprit(); 244 fflush(stdout); 245 246 // Execute default exception handler... hopefully, exit. 247 return EXCEPTION_EXECUTE_HANDLER; 248 } 249 250 static void setup_crash_handler() { 251 SetUnhandledExceptionFilter(crash_handler); 252 } 253#else 254 #include <signal.h> 255 #if !defined(SK_BUILD_FOR_ANDROID) 256 #include <execinfo.h> 257 258#endif 259 260 static constexpr int max_of() { return 0; } 261 template <typename... Rest> 262 static constexpr int max_of(int x, Rest... rest) { 263 return x > max_of(rest...) ? x : max_of(rest...); 264 } 265 266 static void (*previous_handler[max_of(SIGABRT,SIGBUS,SIGFPE,SIGILL,SIGSEGV)+1])(int); 267 268 static void crash_handler(int sig) { 269 SkAutoMutexAcquire lock(gMutex); 270 271 info("\nCaught signal %d [%s], was running:\n", sig, strsignal(sig)); 272 for (auto& task : gRunning) { 273 task.dump(); 274 } 275 find_culprit(); 276 277 #if !defined(SK_BUILD_FOR_ANDROID) 278 void* stack[64]; 279 int count = backtrace(stack, SK_ARRAY_COUNT(stack)); 280 char** symbols = backtrace_symbols(stack, count); 281 info("\nStack trace:\n"); 282 for (int i = 0; i < count; i++) { 283 info(" %s\n", symbols[i]); 284 } 285 #endif 286 fflush(stdout); 287 288 signal(sig, previous_handler[sig]); 289 raise(sig); 290 } 291 292 static void setup_crash_handler() { 293 const int kSignals[] = { SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV }; 294 for (int sig : kSignals) { 295 previous_handler[sig] = signal(sig, crash_handler); 296 } 297 298 if (FLAGS_ignoreSigInt) { 299 signal(SIGINT, SIG_IGN); 300 } 301 } 302#endif 303 304/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 305 306struct Gold : public SkString { 307 Gold() : SkString("") {} 308 Gold(const SkString& sink, const SkString& src, 309 const SkString& srcOptions, const SkString& name, 310 const SkString& md5) 311 : SkString("") { 312 this->append(sink); 313 this->append(src); 314 this->append(srcOptions); 315 this->append(name); 316 this->append(md5); 317 } 318 struct Hash { 319 uint32_t operator()(const Gold& g) const { 320 return SkGoodHash()((const SkString&)g); 321 } 322 }; 323}; 324static SkTHashSet<Gold, Gold::Hash> gGold; 325 326static void add_gold(JsonWriter::BitmapResult r) { 327 gGold.add(Gold(r.config, r.sourceType, r.sourceOptions, r.name, r.md5)); 328} 329 330static void gather_gold() { 331 if (!FLAGS_readPath.isEmpty()) { 332 SkString path(FLAGS_readPath[0]); 333 path.append("/dm.json"); 334 if (!JsonWriter::ReadJson(path.c_str(), add_gold)) { 335 fail(SkStringPrintf("Couldn't read %s for golden results.", path.c_str())); 336 } 337 } 338} 339 340/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 341 342#if defined(SK_BUILD_FOR_WIN) 343 static const char* kNewline = "\r\n"; 344#else 345 static const char* kNewline = "\n"; 346#endif 347 348static SkTHashSet<SkString> gUninterestingHashes; 349 350static void gather_uninteresting_hashes() { 351 if (!FLAGS_uninterestingHashesFile.isEmpty()) { 352 sk_sp<SkData> data(SkData::MakeFromFileName(FLAGS_uninterestingHashesFile[0])); 353 if (!data) { 354 info("WARNING: unable to read uninteresting hashes from %s\n", 355 FLAGS_uninterestingHashesFile[0]); 356 return; 357 } 358 359 // Copy to a string to make sure SkStrSplit has a terminating \0 to find. 360 SkString contents((const char*)data->data(), data->size()); 361 362 SkTArray<SkString> hashes; 363 SkStrSplit(contents.c_str(), kNewline, &hashes); 364 for (const SkString& hash : hashes) { 365 gUninterestingHashes.add(hash); 366 } 367 info("FYI: loaded %d distinct uninteresting hashes from %d lines\n", 368 gUninterestingHashes.count(), hashes.count()); 369 } 370} 371 372/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 373 374struct TaggedSrc : public std::unique_ptr<Src> { 375 SkString tag; 376 SkString options; 377}; 378 379struct TaggedSink : public std::unique_ptr<Sink> { 380 SkString tag; 381}; 382 383static const bool kMemcpyOK = true; 384 385static SkTArray<TaggedSrc, kMemcpyOK> gSrcs; 386static SkTArray<TaggedSink, kMemcpyOK> gSinks; 387 388static bool in_shard() { 389 static int N = 0; 390 return N++ % FLAGS_shards == FLAGS_shard; 391} 392 393static void push_src(const char* tag, ImplicitString options, Src* s) { 394 std::unique_ptr<Src> src(s); 395 if (in_shard() && 396 FLAGS_src.contains(tag) && 397 !SkCommandLineFlags::ShouldSkip(FLAGS_match, src->name().c_str())) { 398 TaggedSrc& s = gSrcs.push_back(); 399 s.reset(src.release()); 400 s.tag = tag; 401 s.options = options; 402 } 403} 404 405static void push_codec_src(Path path, CodecSrc::Mode mode, CodecSrc::DstColorType dstColorType, 406 SkAlphaType dstAlphaType, float scale) { 407 if (FLAGS_simpleCodec) { 408 const bool simple = CodecSrc::kCodec_Mode == mode || CodecSrc::kAnimated_Mode == mode; 409 if (!simple || dstColorType != CodecSrc::kGetFromCanvas_DstColorType || scale != 1.0f) { 410 // Only decode in the simple case. 411 return; 412 } 413 } 414 SkString folder; 415 switch (mode) { 416 case CodecSrc::kCodec_Mode: 417 folder.append("codec"); 418 break; 419 case CodecSrc::kCodecZeroInit_Mode: 420 folder.append("codec_zero_init"); 421 break; 422 case CodecSrc::kScanline_Mode: 423 folder.append("scanline"); 424 break; 425 case CodecSrc::kStripe_Mode: 426 folder.append("stripe"); 427 break; 428 case CodecSrc::kCroppedScanline_Mode: 429 folder.append("crop"); 430 break; 431 case CodecSrc::kSubset_Mode: 432 folder.append("codec_subset"); 433 break; 434 case CodecSrc::kAnimated_Mode: 435 folder.append("codec_animated"); 436 break; 437 } 438 439 switch (dstColorType) { 440 case CodecSrc::kGrayscale_Always_DstColorType: 441 folder.append("_kGray8"); 442 break; 443 case CodecSrc::kNonNative8888_Always_DstColorType: 444 folder.append("_kNonNative"); 445 break; 446 default: 447 break; 448 } 449 450 switch (dstAlphaType) { 451 case kPremul_SkAlphaType: 452 folder.append("_premul"); 453 break; 454 case kUnpremul_SkAlphaType: 455 folder.append("_unpremul"); 456 break; 457 default: 458 break; 459 } 460 461 if (1.0f != scale) { 462 folder.appendf("_%.3f", scale); 463 } 464 465 CodecSrc* src = new CodecSrc(path, mode, dstColorType, dstAlphaType, scale); 466 push_src("image", folder, src); 467} 468 469static void push_android_codec_src(Path path, CodecSrc::DstColorType dstColorType, 470 SkAlphaType dstAlphaType, int sampleSize) { 471 SkString folder; 472 folder.append("scaled_codec"); 473 474 switch (dstColorType) { 475 case CodecSrc::kGrayscale_Always_DstColorType: 476 folder.append("_kGray8"); 477 break; 478 case CodecSrc::kNonNative8888_Always_DstColorType: 479 folder.append("_kNonNative"); 480 break; 481 default: 482 break; 483 } 484 485 switch (dstAlphaType) { 486 case kPremul_SkAlphaType: 487 folder.append("_premul"); 488 break; 489 case kUnpremul_SkAlphaType: 490 folder.append("_unpremul"); 491 break; 492 default: 493 break; 494 } 495 496 if (1 != sampleSize) { 497 folder.appendf("_%.3f", 1.0f / (float) sampleSize); 498 } 499 500 AndroidCodecSrc* src = new AndroidCodecSrc(path, dstColorType, dstAlphaType, sampleSize); 501 push_src("image", folder, src); 502} 503 504static void push_image_gen_src(Path path, ImageGenSrc::Mode mode, SkAlphaType alphaType, bool isGpu) 505{ 506 SkString folder; 507 switch (mode) { 508 case ImageGenSrc::kCodec_Mode: 509 folder.append("gen_codec"); 510 break; 511 case ImageGenSrc::kPlatform_Mode: 512 folder.append("gen_platform"); 513 break; 514 } 515 516 if (isGpu) { 517 folder.append("_gpu"); 518 } else { 519 switch (alphaType) { 520 case kOpaque_SkAlphaType: 521 folder.append("_opaque"); 522 break; 523 case kPremul_SkAlphaType: 524 folder.append("_premul"); 525 break; 526 case kUnpremul_SkAlphaType: 527 folder.append("_unpremul"); 528 break; 529 default: 530 break; 531 } 532 } 533 534 ImageGenSrc* src = new ImageGenSrc(path, mode, alphaType, isGpu); 535 push_src("image", folder, src); 536} 537 538static void push_brd_src(Path path, CodecSrc::DstColorType dstColorType, BRDSrc::Mode mode, 539 uint32_t sampleSize) { 540 SkString folder("brd_android_codec"); 541 switch (mode) { 542 case BRDSrc::kFullImage_Mode: 543 break; 544 case BRDSrc::kDivisor_Mode: 545 folder.append("_divisor"); 546 break; 547 default: 548 SkASSERT(false); 549 return; 550 } 551 552 switch (dstColorType) { 553 case CodecSrc::kGetFromCanvas_DstColorType: 554 break; 555 case CodecSrc::kGrayscale_Always_DstColorType: 556 folder.append("_kGray"); 557 break; 558 default: 559 SkASSERT(false); 560 return; 561 } 562 563 if (1 != sampleSize) { 564 folder.appendf("_%.3f", 1.0f / (float) sampleSize); 565 } 566 567 BRDSrc* src = new BRDSrc(path, mode, dstColorType, sampleSize); 568 push_src("image", folder, src); 569} 570 571static void push_brd_srcs(Path path, bool gray) { 572 if (gray) { 573 // Only run grayscale to one sampleSize and Mode. Though interesting 574 // to test grayscale, it should not reveal anything across various 575 // sampleSizes and Modes 576 // Arbitrarily choose Mode and sampleSize. 577 push_brd_src(path, CodecSrc::kGrayscale_Always_DstColorType, 578 BRDSrc::kFullImage_Mode, 2); 579 } 580 581 // Test on a variety of sampleSizes, making sure to include: 582 // - 2, 4, and 8, which are natively supported by jpeg 583 // - multiples of 2 which are not divisible by 4 (analogous for 4) 584 // - larger powers of two, since BRD clients generally use powers of 2 585 // We will only produce output for the larger sizes on large images. 586 const uint32_t sampleSizes[] = { 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 24, 32, 64 }; 587 588 const BRDSrc::Mode modes[] = { BRDSrc::kFullImage_Mode, BRDSrc::kDivisor_Mode, }; 589 590 for (uint32_t sampleSize : sampleSizes) { 591 for (BRDSrc::Mode mode : modes) { 592 push_brd_src(path, CodecSrc::kGetFromCanvas_DstColorType, mode, sampleSize); 593 } 594 } 595} 596 597static void push_codec_srcs(Path path) { 598 sk_sp<SkData> encoded(SkData::MakeFromFileName(path.c_str())); 599 if (!encoded) { 600 info("Couldn't read %s.", path.c_str()); 601 return; 602 } 603 std::unique_ptr<SkCodec> codec = SkCodec::MakeFromData(encoded); 604 if (nullptr == codec.get()) { 605 info("Couldn't create codec for %s.", path.c_str()); 606 return; 607 } 608 609 // native scaling is only supported by WEBP and JPEG 610 bool supportsNativeScaling = false; 611 612 SkTArray<CodecSrc::Mode> nativeModes; 613 nativeModes.push_back(CodecSrc::kCodec_Mode); 614 nativeModes.push_back(CodecSrc::kCodecZeroInit_Mode); 615 switch (codec->getEncodedFormat()) { 616 case SkEncodedImageFormat::kJPEG: 617 nativeModes.push_back(CodecSrc::kScanline_Mode); 618 nativeModes.push_back(CodecSrc::kStripe_Mode); 619 nativeModes.push_back(CodecSrc::kCroppedScanline_Mode); 620 supportsNativeScaling = true; 621 break; 622 case SkEncodedImageFormat::kWEBP: 623 nativeModes.push_back(CodecSrc::kSubset_Mode); 624 supportsNativeScaling = true; 625 break; 626 case SkEncodedImageFormat::kDNG: 627 break; 628 default: 629 nativeModes.push_back(CodecSrc::kScanline_Mode); 630 break; 631 } 632 633 SkTArray<CodecSrc::DstColorType> colorTypes; 634 colorTypes.push_back(CodecSrc::kGetFromCanvas_DstColorType); 635 colorTypes.push_back(CodecSrc::kNonNative8888_Always_DstColorType); 636 switch (codec->getInfo().colorType()) { 637 case kGray_8_SkColorType: 638 colorTypes.push_back(CodecSrc::kGrayscale_Always_DstColorType); 639 break; 640 default: 641 break; 642 } 643 644 SkTArray<SkAlphaType> alphaModes; 645 alphaModes.push_back(kPremul_SkAlphaType); 646 if (codec->getInfo().alphaType() != kOpaque_SkAlphaType) { 647 alphaModes.push_back(kUnpremul_SkAlphaType); 648 } 649 650 for (CodecSrc::Mode mode : nativeModes) { 651 for (CodecSrc::DstColorType colorType : colorTypes) { 652 for (SkAlphaType alphaType : alphaModes) { 653 // Only test kCroppedScanline_Mode when the alpha type is premul. The test is 654 // slow and won't be interestingly different with different alpha types. 655 if (CodecSrc::kCroppedScanline_Mode == mode && 656 kPremul_SkAlphaType != alphaType) { 657 continue; 658 } 659 660 push_codec_src(path, mode, colorType, alphaType, 1.0f); 661 662 // Skip kNonNative on different native scales. It won't be interestingly 663 // different. 664 if (supportsNativeScaling && 665 CodecSrc::kNonNative8888_Always_DstColorType == colorType) { 666 // Native Scales 667 // SkJpegCodec natively supports scaling to the following: 668 for (auto scale : { 0.125f, 0.25f, 0.375f, 0.5f, 0.625f, 0.750f, 0.875f }) { 669 push_codec_src(path, mode, colorType, alphaType, scale); 670 } 671 } 672 } 673 } 674 } 675 676 { 677 std::vector<SkCodec::FrameInfo> frameInfos = codec->getFrameInfo(); 678 if (frameInfos.size() > 1) { 679 for (auto dstCT : { CodecSrc::kNonNative8888_Always_DstColorType, 680 CodecSrc::kGetFromCanvas_DstColorType }) { 681 for (auto at : { kUnpremul_SkAlphaType, kPremul_SkAlphaType }) { 682 push_codec_src(path, CodecSrc::kAnimated_Mode, dstCT, at, 1.0f); 683 } 684 } 685 } 686 687 } 688 689 if (FLAGS_simpleCodec) { 690 return; 691 } 692 693 const int sampleSizes[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; 694 695 for (int sampleSize : sampleSizes) { 696 for (CodecSrc::DstColorType colorType : colorTypes) { 697 for (SkAlphaType alphaType : alphaModes) { 698 // We can exercise all of the kNonNative support code in the swizzler with just a 699 // few sample sizes. Skip the rest. 700 if (CodecSrc::kNonNative8888_Always_DstColorType == colorType && sampleSize > 3) { 701 continue; 702 } 703 704 push_android_codec_src(path, colorType, alphaType, sampleSize); 705 } 706 } 707 } 708 709 const char* ext = strrchr(path.c_str(), '.'); 710 if (ext) { 711 ext++; 712 713 static const char* const rawExts[] = { 714 "arw", "cr2", "dng", "nef", "nrw", "orf", "raf", "rw2", "pef", "srw", 715 "ARW", "CR2", "DNG", "NEF", "NRW", "ORF", "RAF", "RW2", "PEF", "SRW", 716 }; 717 for (const char* rawExt : rawExts) { 718 if (0 == strcmp(rawExt, ext)) { 719 // RAW is not supported by image generator (skbug.com/5079) or BRD. 720 return; 721 } 722 } 723 724 static const char* const brdExts[] = { 725 "jpg", "jpeg", "png", "webp", 726 "JPG", "JPEG", "PNG", "WEBP", 727 }; 728 for (const char* brdExt : brdExts) { 729 if (0 == strcmp(brdExt, ext)) { 730 bool gray = codec->getInfo().colorType() == kGray_8_SkColorType; 731 push_brd_srcs(path, gray); 732 break; 733 } 734 } 735 } 736 737 // Push image generator GPU test. 738 push_image_gen_src(path, ImageGenSrc::kCodec_Mode, codec->getInfo().alphaType(), true); 739 740 // Push image generator CPU tests. 741 for (SkAlphaType alphaType : alphaModes) { 742 push_image_gen_src(path, ImageGenSrc::kCodec_Mode, alphaType, false); 743 744#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) 745 if (SkEncodedImageFormat::kWEBP != codec->getEncodedFormat() && 746 SkEncodedImageFormat::kWBMP != codec->getEncodedFormat() && 747 kUnpremul_SkAlphaType != alphaType) 748 { 749 push_image_gen_src(path, ImageGenSrc::kPlatform_Mode, alphaType, false); 750 } 751#elif defined(SK_BUILD_FOR_WIN) 752 if (SkEncodedImageFormat::kWEBP != codec->getEncodedFormat() && 753 SkEncodedImageFormat::kWBMP != codec->getEncodedFormat()) 754 { 755 push_image_gen_src(path, ImageGenSrc::kPlatform_Mode, alphaType, false); 756 } 757#endif 758 } 759} 760 761template <typename T> 762void gather_file_srcs(const SkCommandLineFlags::StringArray& flags, const char* ext) { 763 for (int i = 0; i < flags.count(); i++) { 764 const char* path = flags[i]; 765 if (sk_isdir(path)) { 766 SkOSFile::Iter it(path, ext); 767 for (SkString file; it.next(&file); ) { 768 push_src(ext, "", new T(SkOSPath::Join(path, file.c_str()))); 769 } 770 } else { 771 push_src(ext, "", new T(path)); 772 } 773 } 774} 775 776static bool gather_srcs() { 777 for (const skiagm::GMRegistry* r = skiagm::GMRegistry::Head(); r; r = r->next()) { 778 push_src("gm", "", new GMSrc(r->factory())); 779 } 780 781 if (FLAGS_ddl) { 782 gather_file_srcs<DDLSKPSrc>(FLAGS_skps, "skp"); 783 } else { 784 gather_file_srcs<SKPSrc>(FLAGS_skps, "skp"); 785 } 786 gather_file_srcs<MSKPSrc>(FLAGS_mskps, "mskp"); 787#if !defined(SK_BUILD_FOR_GOOGLE3) 788 gather_file_srcs<SkottieSrc>(FLAGS_jsons, "json"); 789#endif 790#if defined(SK_XML) 791 gather_file_srcs<SVGSrc>(FLAGS_svgs, "svg"); 792#endif 793 794 SkTArray<SkString> images; 795 if (!CollectImages(FLAGS_images, &images)) { 796 return false; 797 } 798 799 for (auto image : images) { 800 push_codec_srcs(image); 801 } 802 803 SkTArray<SkString> colorImages; 804 if (!CollectImages(FLAGS_colorImages, &colorImages)) { 805 return false; 806 } 807 808 for (auto colorImage : colorImages) { 809 ColorCodecSrc* src = new ColorCodecSrc(colorImage, ColorCodecSrc::kBaseline_Mode, 810 kN32_SkColorType); 811 push_src("colorImage", "color_codec_baseline", src); 812 813 src = new ColorCodecSrc(colorImage, ColorCodecSrc::kDst_HPZR30w_Mode, kN32_SkColorType); 814 push_src("colorImage", "color_codec_HPZR30w", src); 815 // TODO (msarett): 816 // Should we test this Dst in F16 mode (even though the Dst gamma is 2.2 instead of sRGB)? 817 818 src = new ColorCodecSrc(colorImage, ColorCodecSrc::kDst_sRGB_Mode, kN32_SkColorType); 819 push_src("colorImage", "color_codec_sRGB_kN32", src); 820 src = new ColorCodecSrc(colorImage, ColorCodecSrc::kDst_sRGB_Mode, kRGBA_F16_SkColorType); 821 push_src("colorImage", "color_codec_sRGB_kF16", src); 822 } 823 824 return true; 825} 826 827static void push_sink(const SkCommandLineConfig& config, Sink* s) { 828 std::unique_ptr<Sink> sink(s); 829 830 // Try a simple Src as a canary. If it fails, skip this sink. 831 struct : public Src { 832 Error draw(SkCanvas* c) const override { 833 c->drawRect(SkRect::MakeWH(1,1), SkPaint()); 834 return ""; 835 } 836 SkISize size() const override { return SkISize::Make(16, 16); } 837 Name name() const override { return "justOneRect"; } 838 } justOneRect; 839 840 SkBitmap bitmap; 841 SkDynamicMemoryWStream stream; 842 SkString log; 843 Error err = sink->draw(justOneRect, &bitmap, &stream, &log); 844 if (err.isFatal()) { 845 info("Could not run %s: %s\n", config.getTag().c_str(), err.c_str()); 846 exit(1); 847 } 848 849 TaggedSink& ts = gSinks.push_back(); 850 ts.reset(sink.release()); 851 ts.tag = config.getTag(); 852} 853 854static bool gpu_supported() { 855#if SK_SUPPORT_GPU 856 return FLAGS_gpu; 857#else 858 return false; 859#endif 860} 861 862static Sink* create_sink(const GrContextOptions& grCtxOptions, const SkCommandLineConfig* config) { 863#if SK_SUPPORT_GPU 864 if (gpu_supported()) { 865 if (const SkCommandLineConfigGpu* gpuConfig = config->asConfigGpu()) { 866 GrContextFactory::ContextType contextType = gpuConfig->getContextType(); 867 GrContextFactory::ContextOverrides contextOverrides = gpuConfig->getContextOverrides(); 868 GrContextFactory testFactory(grCtxOptions); 869 if (!testFactory.get(contextType, contextOverrides)) { 870 info("WARNING: can not create GPU context for config '%s'. " 871 "GM tests will be skipped.\n", gpuConfig->getTag().c_str()); 872 return nullptr; 873 } 874 if (gpuConfig->getTestThreading()) { 875 return new GPUThreadTestingSink(contextType, contextOverrides, 876 gpuConfig->getSamples(), gpuConfig->getUseDIText(), 877 gpuConfig->getColorType(), 878 gpuConfig->getAlphaType(), 879 sk_ref_sp(gpuConfig->getColorSpace()), 880 FLAGS_gpu_threading, grCtxOptions); 881 } else { 882 return new GPUSink(contextType, contextOverrides, gpuConfig->getSamples(), 883 gpuConfig->getUseDIText(), gpuConfig->getColorType(), 884 gpuConfig->getAlphaType(), sk_ref_sp(gpuConfig->getColorSpace()), 885 FLAGS_gpu_threading, grCtxOptions); 886 } 887 } 888 } 889#endif 890 891#define SINK(t, sink, ...) if (config->getBackend().equals(t)) { return new sink(__VA_ARGS__); } 892 893 if (FLAGS_cpu) { 894 auto srgbColorSpace = SkColorSpace::MakeSRGB(); 895 auto srgbLinearColorSpace = SkColorSpace::MakeSRGBLinear(); 896 897 SINK("g8", RasterSink, kGray_8_SkColorType); 898 SINK("565", RasterSink, kRGB_565_SkColorType); 899 SINK("4444", RasterSink, kARGB_4444_SkColorType); 900 SINK("8888", RasterSink, kN32_SkColorType); 901 SINK("srgb", RasterSink, kN32_SkColorType, srgbColorSpace); 902 SINK("rgba", RasterSink, kRGBA_8888_SkColorType); 903 SINK("bgra", RasterSink, kBGRA_8888_SkColorType); 904 SINK("rgbx", RasterSink, kRGB_888x_SkColorType); 905 SINK("1010102", RasterSink, kRGBA_1010102_SkColorType); 906 SINK("101010x", RasterSink, kRGB_101010x_SkColorType); 907 SINK("f16", RasterSink, kRGBA_F16_SkColorType, srgbLinearColorSpace); 908 SINK("pdf", PDFSink, false, SK_ScalarDefaultRasterDPI); 909 SINK("skp", SKPSink); 910 SINK("pipe", PipeSink); 911 SINK("svg", SVGSink); 912 SINK("null", NullSink); 913 SINK("xps", XPSSink); 914 SINK("pdfa", PDFSink, true, SK_ScalarDefaultRasterDPI); 915 SINK("pdf300", PDFSink, false, 300); 916 SINK("jsdebug", DebugSink); 917 } 918#undef SINK 919 return nullptr; 920} 921 922static sk_sp<SkColorSpace> adobe_rgb() { 923 return SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, 924 SkColorSpace::kAdobeRGB_Gamut); 925} 926 927static sk_sp<SkColorSpace> rgb_to_gbr() { 928 return SkColorSpace::MakeSRGB()->makeColorSpin(); 929} 930 931static Sink* create_via(const SkString& tag, Sink* wrapped) { 932#define VIA(t, via, ...) if (tag.equals(t)) { return new via(__VA_ARGS__); } 933 VIA("adobe", ViaCSXform, wrapped, adobe_rgb(), false); 934 VIA("gbr", ViaCSXform, wrapped, rgb_to_gbr(), true); 935 VIA("lite", ViaLite, wrapped); 936 VIA("pipe", ViaPipe, wrapped); 937#ifdef TEST_VIA_SVG 938 VIA("svg", ViaSVG, wrapped); 939#endif 940 VIA("serialize", ViaSerialization, wrapped); 941 VIA("pic", ViaPicture, wrapped); 942 VIA("tiles", ViaTiles, 256, 256, nullptr, wrapped); 943 VIA("tiles_rt", ViaTiles, 256, 256, new SkRTreeFactory, wrapped); 944 945 if (FLAGS_matrix.count() == 4) { 946 SkMatrix m; 947 m.reset(); 948 m.setScaleX((SkScalar)atof(FLAGS_matrix[0])); 949 m.setSkewX ((SkScalar)atof(FLAGS_matrix[1])); 950 m.setSkewY ((SkScalar)atof(FLAGS_matrix[2])); 951 m.setScaleY((SkScalar)atof(FLAGS_matrix[3])); 952 VIA("matrix", ViaMatrix, m, wrapped); 953 VIA("upright", ViaUpright, m, wrapped); 954 } 955 956#undef VIA 957 return nullptr; 958} 959 960static bool gather_sinks(const GrContextOptions& grCtxOptions, bool defaultConfigs) { 961 SkCommandLineConfigArray configs; 962 ParseConfigs(FLAGS_config, &configs); 963 for (int i = 0; i < configs.count(); i++) { 964 const SkCommandLineConfig& config = *configs[i]; 965 Sink* sink = create_sink(grCtxOptions, &config); 966 if (sink == nullptr) { 967 info("Skipping config %s: Don't understand '%s'.\n", config.getTag().c_str(), 968 config.getTag().c_str()); 969 continue; 970 } 971 972 const SkTArray<SkString>& parts = config.getViaParts(); 973 for (int j = parts.count(); j-- > 0;) { 974 const SkString& part = parts[j]; 975 Sink* next = create_via(part, sink); 976 if (next == nullptr) { 977 info("Skipping config %s: Don't understand '%s'.\n", config.getTag().c_str(), 978 part.c_str()); 979 delete sink; 980 sink = nullptr; 981 break; 982 } 983 sink = next; 984 } 985 if (sink) { 986 push_sink(config, sink); 987 } 988 } 989 990 // If no configs were requested (just running tests, perhaps?), then we're okay. 991 if (configs.count() == 0 || 992 // If we're using the default configs, we're okay. 993 defaultConfigs || 994 // If we've been told to ignore undefined flags, we're okay. 995 FLAGS_undefok || 996 // Otherwise, make sure that all specified configs have become sinks. 997 configs.count() == gSinks.count()) { 998 return true; 999 } 1000 info("Invalid --config. Use --undefok to bypass this warning.\n"); 1001 return false; 1002} 1003 1004static bool dump_png(SkBitmap bitmap, const char* path, const char* md5) { 1005 SkPixmap pm; 1006 if (!bitmap.peekPixels(&pm)) { 1007 return false; // Ought to never happen... we're already read-back at this point. 1008 } 1009 SkFILEWStream dst{path}; 1010 1011 SkString description; 1012 description.append("Key: "); 1013 for (int i = 0; i < FLAGS_key.count(); i++) { 1014 description.appendf("%s ", FLAGS_key[i]); 1015 } 1016 description.append("Properties: "); 1017 for (int i = 0; i < FLAGS_properties.count(); i++) { 1018 description.appendf("%s ", FLAGS_properties[i]); 1019 } 1020 description.appendf("MD5: %s", md5); 1021 1022 const char* comments[] = { 1023 "Author", "DM dump_png()", 1024 "Description", description.c_str(), 1025 }; 1026 size_t lengths[] = { 1027 strlen(comments[0])+1, strlen(comments[1])+1, 1028 strlen(comments[2])+1, strlen(comments[3])+1, 1029 }; 1030 1031 SkPngEncoder::Options options; 1032 options.fComments = SkDataTable::MakeCopyArrays((const void**)comments, lengths, 4); 1033 options.fFilterFlags = SkPngEncoder::FilterFlag::kNone; 1034 options.fZLibLevel = 1; 1035 options.fUnpremulBehavior = pm.colorSpace() ? SkTransferFunctionBehavior::kRespect 1036 : SkTransferFunctionBehavior::kIgnore; 1037 return SkPngEncoder::Encode(&dst, pm, options); 1038} 1039 1040static bool match(const char* needle, const char* haystack) { 1041 if ('~' == needle[0]) { 1042 return !match(needle + 1, haystack); 1043 } 1044 if (0 == strcmp("_", needle)) { 1045 return true; 1046 } 1047 return nullptr != strstr(haystack, needle); 1048} 1049 1050static bool is_blacklisted(const char* sink, const char* src, 1051 const char* srcOptions, const char* name) { 1052 for (int i = 0; i < FLAGS_blacklist.count() - 3; i += 4) { 1053 if (match(FLAGS_blacklist[i+0], sink) && 1054 match(FLAGS_blacklist[i+1], src) && 1055 match(FLAGS_blacklist[i+2], srcOptions) && 1056 match(FLAGS_blacklist[i+3], name)) { 1057 return true; 1058 } 1059 } 1060 return false; 1061} 1062 1063// Even when a Task Sink reports to be non-threadsafe (e.g. GPU), we know things like 1064// .png encoding are definitely thread safe. This lets us offload that work to CPU threads. 1065static SkTaskGroup gDefinitelyThreadSafeWork; 1066 1067// The finest-grained unit of work we can run: draw a single Src into a single Sink, 1068// report any errors, and perhaps write out the output: a .png of the bitmap, or a raw stream. 1069struct Task { 1070 Task(const TaggedSrc& src, const TaggedSink& sink) : src(src), sink(sink) {} 1071 const TaggedSrc& src; 1072 const TaggedSink& sink; 1073 1074 static void Run(const Task& task) { 1075 SkString name = task.src->name(); 1076 1077 SkString log; 1078 if (!FLAGS_dryRun) { 1079 SkBitmap bitmap; 1080 SkDynamicMemoryWStream stream; 1081 start(task.sink.tag.c_str(), task.src.tag.c_str(), 1082 task.src.options.c_str(), name.c_str()); 1083 Error err = task.sink->draw(*task.src, &bitmap, &stream, &log); 1084 if (!log.isEmpty()) { 1085 info("%s %s %s %s:\n%s\n", task.sink.tag.c_str() 1086 , task.src.tag.c_str() 1087 , task.src.options.c_str() 1088 , name.c_str() 1089 , log.c_str()); 1090 } 1091 if (!err.isEmpty()) { 1092 if (err.isFatal()) { 1093 fail(SkStringPrintf("%s %s %s %s: %s", 1094 task.sink.tag.c_str(), 1095 task.src.tag.c_str(), 1096 task.src.options.c_str(), 1097 name.c_str(), 1098 err.c_str())); 1099 } else { 1100 done(task.sink.tag.c_str(), task.src.tag.c_str(), 1101 task.src.options.c_str(), name.c_str()); 1102 return; 1103 } 1104 } 1105 1106 // We're likely switching threads here, so we must capture by value, [=] or [foo,bar]. 1107 SkStreamAsset* data = stream.detachAsStream().release(); 1108 gDefinitelyThreadSafeWork.add([task,name,bitmap,data]{ 1109 std::unique_ptr<SkStreamAsset> ownedData(data); 1110 1111 SkString md5; 1112 if (!FLAGS_writePath.isEmpty() || !FLAGS_readPath.isEmpty()) { 1113 SkMD5 hash; 1114 if (data->getLength()) { 1115 hash.writeStream(data, data->getLength()); 1116 data->rewind(); 1117 } else { 1118 // If we're BGRA (Linux, Windows), swizzle over to RGBA (Mac, Android). 1119 // This helps eliminate multiple 0-pixel-diff hashes on gold.skia.org. 1120 // (Android's general slow speed breaks the tie arbitrarily in RGBA's favor.) 1121 // We might consider promoting 565 to RGBA too. 1122 if (bitmap.colorType() == kBGRA_8888_SkColorType) { 1123 SkBitmap swizzle; 1124 SkAssertResult(sk_tool_utils::copy_to(&swizzle, kRGBA_8888_SkColorType, 1125 bitmap)); 1126 hash.write(swizzle.getPixels(), swizzle.computeByteSize()); 1127 } else { 1128 hash.write(bitmap.getPixels(), bitmap.computeByteSize()); 1129 } 1130 } 1131 SkMD5::Digest digest; 1132 hash.finish(digest); 1133 for (int i = 0; i < 16; i++) { 1134 md5.appendf("%02x", digest.data[i]); 1135 } 1136 } 1137 1138 if (!FLAGS_readPath.isEmpty() && 1139 !gGold.contains(Gold(task.sink.tag, task.src.tag, 1140 task.src.options, name, md5))) { 1141 fail(SkStringPrintf("%s not found for %s %s %s %s in %s", 1142 md5.c_str(), 1143 task.sink.tag.c_str(), 1144 task.src.tag.c_str(), 1145 task.src.options.c_str(), 1146 name.c_str(), 1147 FLAGS_readPath[0])); 1148 } 1149 1150 if (!FLAGS_writePath.isEmpty()) { 1151 const char* ext = task.sink->fileExtension(); 1152 if (ext && !FLAGS_dont_write.contains(ext)) { 1153 if (data->getLength()) { 1154 WriteToDisk(task, md5, ext, data, data->getLength(), nullptr); 1155 SkASSERT(bitmap.drawsNothing()); 1156 } else if (!bitmap.drawsNothing()) { 1157 WriteToDisk(task, md5, ext, nullptr, 0, &bitmap); 1158 } 1159 } 1160 } 1161 }); 1162 } 1163 done(task.sink.tag.c_str(), task.src.tag.c_str(), task.src.options.c_str(), name.c_str()); 1164 } 1165 1166 static void WriteToDisk(const Task& task, 1167 SkString md5, 1168 const char* ext, 1169 SkStream* data, size_t len, 1170 const SkBitmap* bitmap) { 1171 bool gammaCorrect = false; 1172 if (bitmap) { 1173 gammaCorrect = SkToBool(bitmap->info().colorSpace()); 1174 } 1175 1176 JsonWriter::BitmapResult result; 1177 result.name = task.src->name(); 1178 result.config = task.sink.tag; 1179 result.sourceType = task.src.tag; 1180 result.sourceOptions = task.src.options; 1181 result.ext = ext; 1182 result.gammaCorrect = gammaCorrect; 1183 result.md5 = md5; 1184 JsonWriter::AddBitmapResult(result); 1185 1186 // If an MD5 is uninteresting, we want it noted in the JSON file, 1187 // but don't want to dump it out as a .png (or whatever ext is). 1188 if (gUninterestingHashes.contains(md5)) { 1189 return; 1190 } 1191 1192 const char* dir = FLAGS_writePath[0]; 1193 if (0 == strcmp(dir, "@")) { // Needed for iOS. 1194 dir = FLAGS_resourcePath[0]; 1195 } 1196 sk_mkdir(dir); 1197 1198 SkString path; 1199 if (FLAGS_nameByHash) { 1200 path = SkOSPath::Join(dir, result.md5.c_str()); 1201 path.append("."); 1202 path.append(ext); 1203 if (sk_exists(path.c_str())) { 1204 return; // Content-addressed. If it exists already, we're done. 1205 } 1206 } else { 1207 path = SkOSPath::Join(dir, task.sink.tag.c_str()); 1208 sk_mkdir(path.c_str()); 1209 path = SkOSPath::Join(path.c_str(), task.src.tag.c_str()); 1210 sk_mkdir(path.c_str()); 1211 if (strcmp(task.src.options.c_str(), "") != 0) { 1212 path = SkOSPath::Join(path.c_str(), task.src.options.c_str()); 1213 sk_mkdir(path.c_str()); 1214 } 1215 path = SkOSPath::Join(path.c_str(), task.src->name().c_str()); 1216 path.append("."); 1217 path.append(ext); 1218 } 1219 1220 if (bitmap) { 1221 if (!dump_png(*bitmap, path.c_str(), result.md5.c_str())) { 1222 fail(SkStringPrintf("Can't encode PNG to %s.\n", path.c_str())); 1223 return; 1224 } 1225 } else { 1226 SkFILEWStream file(path.c_str()); 1227 if (!file.isValid()) { 1228 fail(SkStringPrintf("Can't open %s for writing.\n", path.c_str())); 1229 return; 1230 } 1231 if (!file.writeStream(data, len)) { 1232 fail(SkStringPrintf("Can't write to %s.\n", path.c_str())); 1233 return; 1234 } 1235 } 1236 } 1237}; 1238 1239/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 1240 1241// Unit tests don't fit so well into the Src/Sink model, so we give them special treatment. 1242 1243static SkTDArray<skiatest::Test> gParallelTests, gSerialTests; 1244 1245static void gather_tests() { 1246 if (!FLAGS_src.contains("tests")) { 1247 return; 1248 } 1249 for (const skiatest::TestRegistry* r = skiatest::TestRegistry::Head(); r; r = r->next()) { 1250 if (!in_shard()) { 1251 continue; 1252 } 1253 // Despite its name, factory() is returning a reference to 1254 // link-time static const POD data. 1255 const skiatest::Test& test = r->factory(); 1256 if (SkCommandLineFlags::ShouldSkip(FLAGS_match, test.name)) { 1257 continue; 1258 } 1259 if (test.needsGpu && gpu_supported()) { 1260 (FLAGS_gpu_threading ? gParallelTests : gSerialTests).push(test); 1261 } else if (!test.needsGpu && FLAGS_cpu) { 1262 gParallelTests.push(test); 1263 } 1264 } 1265} 1266 1267static void run_test(skiatest::Test test, const GrContextOptions& grCtxOptions) { 1268 struct : public skiatest::Reporter { 1269 void reportFailed(const skiatest::Failure& failure) override { 1270 fail(failure.toString()); 1271 JsonWriter::AddTestFailure(failure); 1272 } 1273 bool allowExtendedTest() const override { 1274 return FLAGS_pathOpsExtended; 1275 } 1276 bool verbose() const override { return FLAGS_veryVerbose; } 1277 } reporter; 1278 1279 if (!FLAGS_dryRun && !is_blacklisted("_", "tests", "_", test.name)) { 1280 GrContextOptions options = grCtxOptions; 1281 test.modifyGrContextOptions(&options); 1282 1283 start("unit", "test", "", test.name); 1284 test.run(&reporter, options); 1285 } 1286 done("unit", "test", "", test.name); 1287} 1288 1289/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 1290 1291#define PORTABLE_FONT_PREFIX "Toy Liberation " 1292 1293static sk_sp<SkTypeface> create_from_name(const char familyName[], SkFontStyle style) { 1294 if (familyName && strlen(familyName) > sizeof(PORTABLE_FONT_PREFIX) 1295 && !strncmp(familyName, PORTABLE_FONT_PREFIX, sizeof(PORTABLE_FONT_PREFIX) - 1)) { 1296 return sk_tool_utils::create_portable_typeface(familyName, style); 1297 } 1298 return nullptr; 1299} 1300 1301#undef PORTABLE_FONT_PREFIX 1302 1303extern sk_sp<SkTypeface> (*gCreateTypefaceDelegate)(const char [], SkFontStyle ); 1304 1305int main(int argc, char** argv) { 1306 SkCommandLineFlags::Parse(argc, argv); 1307 1308 if (!FLAGS_nativeFonts) { 1309 gSkFontMgr_DefaultFactory = &DM::MakeFontMgr; 1310 } 1311 1312#if defined(SK_BUILD_FOR_WIN) 1313 if (FLAGS_gdi) { 1314 gSkFontMgr_DefaultFactory = &SkFontMgr_New_GDI; 1315 } 1316#endif 1317 1318 initializeEventTracingForTools(); 1319 1320#if !defined(SK_BUILD_FOR_GOOGLE3) && defined(SK_BUILD_FOR_IOS) 1321 cd_Documents(); 1322#endif 1323 setbuf(stdout, nullptr); 1324 setup_crash_handler(); 1325 1326 gSkUseAnalyticAA = FLAGS_analyticAA; 1327 gSkUseDeltaAA = FLAGS_deltaAA; 1328 1329 if (FLAGS_forceAnalyticAA) { 1330 gSkForceAnalyticAA = true; 1331 } 1332 if (FLAGS_forceDeltaAA) { 1333 gSkForceDeltaAA = true; 1334 } 1335 if (FLAGS_forceRasterPipeline) { 1336 gSkForceRasterPipelineBlitter = true; 1337 } 1338 1339 // The bots like having a verbose.log to upload, so always touch the file even if --verbose. 1340 if (!FLAGS_writePath.isEmpty()) { 1341 sk_mkdir(FLAGS_writePath[0]); 1342 gVLog = fopen(SkOSPath::Join(FLAGS_writePath[0], "verbose.log").c_str(), "w"); 1343 } 1344 if (FLAGS_verbose) { 1345 gVLog = stderr; 1346 } 1347 1348 GrContextOptions grCtxOptions; 1349#if SK_SUPPORT_GPU 1350 SetCtxOptionsFromCommonFlags(&grCtxOptions); 1351#endif 1352 1353 JsonWriter::DumpJson(); // It's handy for the bots to assume this is ~never missing. 1354 SkAutoGraphics ag; 1355 SkTaskGroup::Enabler enabled(FLAGS_threads); 1356 gCreateTypefaceDelegate = &create_from_name; 1357 1358 if (nullptr == GetResourceAsData("images/color_wheel.png")) { 1359 info("Some resources are missing. Do you need to set --resourcePath?\n"); 1360 } 1361 gather_gold(); 1362 gather_uninteresting_hashes(); 1363 1364 if (!gather_srcs()) { 1365 return 1; 1366 } 1367 // TODO(dogben): This is a bit ugly. Find a cleaner way to do this. 1368 bool defaultConfigs = true; 1369 for (int i = 0; i < argc; i++) { 1370 static const char* kConfigArg = "--config"; 1371 if (strcmp(argv[i], kConfigArg) == 0) { 1372 defaultConfigs = false; 1373 break; 1374 } 1375 } 1376 if (!gather_sinks(grCtxOptions, defaultConfigs)) { 1377 return 1; 1378 } 1379 gather_tests(); 1380 gPending = gSrcs.count() * gSinks.count() + gParallelTests.count() + gSerialTests.count(); 1381 info("%d srcs * %d sinks + %d tests == %d tasks\n", 1382 gSrcs.count(), gSinks.count(), gParallelTests.count() + gSerialTests.count(), gPending); 1383 1384 // Kick off as much parallel work as we can, making note of any serial work we'll need to do. 1385 SkTaskGroup parallel; 1386 SkTArray<Task> serial; 1387 1388 for (auto& sink : gSinks) 1389 for (auto& src : gSrcs) { 1390 if (src->veto(sink->flags()) || 1391 is_blacklisted(sink.tag.c_str(), src.tag.c_str(), 1392 src.options.c_str(), src->name().c_str())) { 1393 SkAutoMutexAcquire lock(gMutex); 1394 gPending--; 1395 continue; 1396 } 1397 1398 Task task(src, sink); 1399 if (src->serial() || sink->serial()) { 1400 serial.push_back(task); 1401 } else { 1402 parallel.add([task] { Task::Run(task); }); 1403 } 1404 } 1405 for (auto test : gParallelTests) { 1406 parallel.add([test, grCtxOptions] { run_test(test, grCtxOptions); }); 1407 } 1408 1409 // With the parallel work running, run serial tasks and tests here on main thread. 1410 for (auto task : serial) { Task::Run(task); } 1411 for (auto test : gSerialTests) { run_test(test, grCtxOptions); } 1412 1413 // Wait for any remaining parallel work to complete (including any spun off of serial tasks). 1414 parallel.wait(); 1415 gDefinitelyThreadSafeWork.wait(); 1416 1417 // We'd better have run everything. 1418 SkASSERT(gPending == 0); 1419 // Make sure we've flushed all our results to disk. 1420 JsonWriter::DumpJson(); 1421 1422 // At this point we're back in single-threaded land. 1423 sk_tool_utils::release_portable_typefaces(); 1424 1425 if (gFailures.count() > 0) { 1426 info("Failures:\n"); 1427 for (int i = 0; i < gFailures.count(); i++) { 1428 info("\t%s\n", gFailures[i].c_str()); 1429 } 1430 info("%d failures\n", gFailures.count()); 1431 return 1; 1432 } 1433 1434#ifdef SK_PDF_IMAGE_STATS 1435 SkPDFImageDumpStats(); 1436#endif // SK_PDF_IMAGE_STATS 1437 1438 SkGraphics::PurgeAllCaches(); 1439 info("Finished!\n"); 1440 1441 return 0; 1442} 1443