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