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 <unistd.h> 9 10 #include "CrashHandler.h" 11 #include "DMJsonWriter.h" 12 #include "DMSrcSink.h" 13 #include "DMSrcSinkAndroid.h" 14 #include "ProcStats.h" 15 #include "Resources.h" 16 #include "SkBBHFactory.h" 17 #include "SkChecksum.h" 18 #include "SkCodec.h" 19 #include "SkCommonFlags.h" 20 #include "SkCommonFlagsConfig.h" 21 #include "SkFontMgr.h" 22 #include "SkGraphics.h" 23 #include "SkMD5.h" 24 #include "SkMutex.h" 25 #include "SkOSFile.h" 26 #include "SkSpinlock.h" 27 #include "SkTHash.h" 28 #include "SkTaskGroup.h" 29 #include "SkThreadUtils.h" 30 #include "Test.h" 31 #include "Timer.h" 32 #include "sk_tool_utils.h" 33 34 #ifdef SK_PDF_IMAGE_STATS 35 extern void SkPDFImageDumpStats(); 36 #endif 37 38 #include "png.h" 39 40 #include <stdlib.h> 41 42 #ifndef SK_BUILD_FOR_WIN32 43 #include <unistd.h> 44 #endif 45 46 DEFINE_string(src, "tests gm skp image", "Source types to test."); 47 DEFINE_bool(nameByHash, false, 48 "If true, write to FLAGS_writePath[0]/<hash>.png instead of " 49 "to FLAGS_writePath[0]/<config>/<sourceType>/<sourceOptions>/<name>.png"); 50 DEFINE_bool2(pathOpsExtended, x, false, "Run extended pathOps tests."); 51 DEFINE_string(matrix, "1 0 0 1", 52 "2x2 scale+skew matrix to apply or upright when using " 53 "'matrix' or 'upright' in config."); 54 DEFINE_bool(gpu_threading, false, "Allow GPU work to run on multiple threads?"); 55 56 DEFINE_string(blacklist, "", 57 "Space-separated config/src/srcOptions/name quadruples to blacklist. '_' matches anything. E.g. \n" 58 "'--blacklist gpu skp _ _' will blacklist all SKPs drawn into the gpu config.\n" 59 "'--blacklist gpu skp _ _ 8888 gm _ aarects' will also blacklist the aarects GM on 8888."); 60 61 DEFINE_string2(readPath, r, "", "If set check for equality with golden results in this directory."); 62 63 DEFINE_string(uninterestingHashesFile, "", 64 "File containing a list of uninteresting hashes. If a result hashes to something in " 65 "this list, no image is written for that result."); 66 67 DEFINE_int32(shards, 1, "We're splitting source data into this many shards."); 68 DEFINE_int32(shard, 0, "Which shard do I run?"); 69 DEFINE_bool(simpleCodec, false, "Only decode images to native scale"); 70 71 using namespace DM; 72 73 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 74 75 SK_DECLARE_STATIC_MUTEX(gFailuresMutex); 76 static SkTArray<SkString> gFailures; 77 78 static void fail(const SkString& err) { 79 SkAutoMutexAcquire lock(gFailuresMutex); 80 SkDebugf("\n\nFAILURE: %s\n\n", err.c_str()); 81 gFailures.push_back(err); 82 } 83 84 85 // We use a spinlock to make locking this in a signal handler _somewhat_ safe. 86 SK_DECLARE_STATIC_SPINLOCK(gMutex); 87 static int32_t gPending; 88 static SkTArray<SkString> gRunning; 89 90 static void done(const char* config, const char* src, const char* srcOptions, const char* name) { 91 SkString id = SkStringPrintf("%s %s %s %s", config, src, srcOptions, name); 92 int pending; 93 { 94 SkAutoTAcquire<SkPODSpinlock> lock(gMutex); 95 for (int i = 0; i < gRunning.count(); i++) { 96 if (gRunning[i] == id) { 97 gRunning.removeShuffle(i); 98 break; 99 } 100 } 101 pending = --gPending; 102 } 103 // We write our dm.json file every once in a while in case we crash. 104 // Notice this also handles the final dm.json when pending == 0. 105 if (pending % 500 == 0) { 106 JsonWriter::DumpJson(); 107 } 108 } 109 110 static void start(const char* config, const char* src, const char* srcOptions, const char* name) { 111 SkString id = SkStringPrintf("%s %s %s %s", config, src, srcOptions, name); 112 SkAutoTAcquire<SkPODSpinlock> lock(gMutex); 113 gRunning.push_back(id); 114 } 115 116 static void print_status() { 117 static SkMSec start_ms = SkTime::GetMSecs(); 118 119 int curr = sk_tools::getCurrResidentSetSizeMB(), 120 peak = sk_tools::getMaxResidentSetSizeMB(); 121 SkString elapsed = HumanizeMs(SkTime::GetMSecs() - start_ms); 122 123 SkAutoTAcquire<SkPODSpinlock> lock(gMutex); 124 SkDebugf("\n%s elapsed, %d active, %d queued, %dMB RAM, %dMB peak\n", 125 elapsed.c_str(), gRunning.count(), gPending - gRunning.count(), curr, peak); 126 for (auto& task : gRunning) { 127 SkDebugf("\t%s\n", task.c_str()); 128 } 129 } 130 131 #if defined(SK_BUILD_FOR_WIN32) 132 static void setup_crash_handler() { 133 // TODO: custom crash handler like below to print out what was running 134 SetupCrashHandler(); 135 } 136 137 #else 138 #include <signal.h> 139 static void setup_crash_handler() { 140 const int kSignals[] = { SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV }; 141 for (int sig : kSignals) { 142 signal(sig, [](int sig) { 143 SkDebugf("\nCaught signal %d [%s].\n", sig, strsignal(sig)); 144 print_status(); 145 }); 146 } 147 } 148 #endif 149 150 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 151 152 struct Gold : public SkString { 153 Gold() : SkString("") {} 154 Gold(const SkString& sink, const SkString& src, 155 const SkString& srcOptions, const SkString& name, 156 const SkString& md5) 157 : SkString("") { 158 this->append(sink); 159 this->append(src); 160 this->append(srcOptions); 161 this->append(name); 162 this->append(md5); 163 } 164 struct Hash { 165 uint32_t operator()(const Gold& g) const { 166 return SkGoodHash()((const SkString&)g); 167 } 168 }; 169 }; 170 static SkTHashSet<Gold, Gold::Hash> gGold; 171 172 static void add_gold(JsonWriter::BitmapResult r) { 173 gGold.add(Gold(r.config, r.sourceType, r.sourceOptions, r.name, r.md5)); 174 } 175 176 static void gather_gold() { 177 if (!FLAGS_readPath.isEmpty()) { 178 SkString path(FLAGS_readPath[0]); 179 path.append("/dm.json"); 180 if (!JsonWriter::ReadJson(path.c_str(), add_gold)) { 181 fail(SkStringPrintf("Couldn't read %s for golden results.", path.c_str())); 182 } 183 } 184 } 185 186 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 187 188 static SkTHashSet<SkString> gUninterestingHashes; 189 190 static void gather_uninteresting_hashes() { 191 if (!FLAGS_uninterestingHashesFile.isEmpty()) { 192 SkAutoTUnref<SkData> data(SkData::NewFromFileName(FLAGS_uninterestingHashesFile[0])); 193 if (!data) { 194 SkDebugf("WARNING: unable to read uninteresting hashes from %s\n", 195 FLAGS_uninterestingHashesFile[0]); 196 return; 197 } 198 SkTArray<SkString> hashes; 199 SkStrSplit((const char*)data->data(), "\n", &hashes); 200 for (const SkString& hash : hashes) { 201 gUninterestingHashes.add(hash); 202 } 203 SkDebugf("FYI: loaded %d distinct uninteresting hashes from %d lines\n", 204 gUninterestingHashes.count(), hashes.count()); 205 } 206 } 207 208 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 209 210 struct TaggedSrc : public SkAutoTDelete<Src> { 211 SkString tag; 212 SkString options; 213 }; 214 215 struct TaggedSink : public SkAutoTDelete<Sink> { 216 SkString tag; 217 }; 218 219 static const bool kMemcpyOK = true; 220 221 static SkTArray<TaggedSrc, kMemcpyOK> gSrcs; 222 static SkTArray<TaggedSink, kMemcpyOK> gSinks; 223 224 static bool in_shard() { 225 static int N = 0; 226 return N++ % FLAGS_shards == FLAGS_shard; 227 } 228 229 static void push_src(const char* tag, ImplicitString options, Src* s) { 230 SkAutoTDelete<Src> src(s); 231 if (in_shard() && 232 FLAGS_src.contains(tag) && 233 !SkCommandLineFlags::ShouldSkip(FLAGS_match, src->name().c_str())) { 234 TaggedSrc& s = gSrcs.push_back(); 235 s.reset(src.detach()); 236 s.tag = tag; 237 s.options = options; 238 } 239 } 240 241 static void push_codec_src(Path path, CodecSrc::Mode mode, CodecSrc::DstColorType dstColorType, 242 SkAlphaType dstAlphaType, float scale) { 243 if (FLAGS_simpleCodec) { 244 if (mode != CodecSrc::kCodec_Mode || dstColorType != CodecSrc::kGetFromCanvas_DstColorType 245 || scale != 1.0f) 246 // Only decode in the simple case. 247 return; 248 } 249 SkString folder; 250 switch (mode) { 251 case CodecSrc::kCodec_Mode: 252 folder.append("codec"); 253 break; 254 case CodecSrc::kCodecZeroInit_Mode: 255 folder.append("codec_zero_init"); 256 break; 257 case CodecSrc::kScanline_Mode: 258 folder.append("scanline"); 259 break; 260 case CodecSrc::kStripe_Mode: 261 folder.append("stripe"); 262 break; 263 case CodecSrc::kCroppedScanline_Mode: 264 folder.append("crop"); 265 break; 266 case CodecSrc::kSubset_Mode: 267 folder.append("codec_subset"); 268 break; 269 case CodecSrc::kGen_Mode: 270 folder.append("gen"); 271 break; 272 } 273 274 switch (dstColorType) { 275 case CodecSrc::kGrayscale_Always_DstColorType: 276 folder.append("_kGray8"); 277 break; 278 case CodecSrc::kIndex8_Always_DstColorType: 279 folder.append("_kIndex8"); 280 break; 281 default: 282 break; 283 } 284 285 switch (dstAlphaType) { 286 case kOpaque_SkAlphaType: 287 folder.append("_opaque"); 288 break; 289 case kPremul_SkAlphaType: 290 folder.append("_premul"); 291 break; 292 case kUnpremul_SkAlphaType: 293 folder.append("_unpremul"); 294 break; 295 default: 296 break; 297 } 298 299 if (1.0f != scale) { 300 folder.appendf("_%.3f", scale); 301 } 302 303 CodecSrc* src = new CodecSrc(path, mode, dstColorType, dstAlphaType, scale); 304 push_src("image", folder, src); 305 } 306 307 static void push_android_codec_src(Path path, AndroidCodecSrc::Mode mode, 308 CodecSrc::DstColorType dstColorType, SkAlphaType dstAlphaType, int sampleSize) { 309 SkString folder; 310 switch (mode) { 311 case AndroidCodecSrc::kFullImage_Mode: 312 folder.append("scaled_codec"); 313 break; 314 case AndroidCodecSrc::kDivisor_Mode: 315 folder.append("scaled_codec_divisor"); 316 break; 317 } 318 319 switch (dstColorType) { 320 case CodecSrc::kGrayscale_Always_DstColorType: 321 folder.append("_kGray8"); 322 break; 323 case CodecSrc::kIndex8_Always_DstColorType: 324 folder.append("_kIndex8"); 325 break; 326 default: 327 break; 328 } 329 330 switch (dstAlphaType) { 331 case kOpaque_SkAlphaType: 332 folder.append("_opaque"); 333 break; 334 case kPremul_SkAlphaType: 335 folder.append("_premul"); 336 break; 337 case kUnpremul_SkAlphaType: 338 folder.append("_unpremul"); 339 break; 340 default: 341 break; 342 } 343 344 if (1 != sampleSize) { 345 folder.appendf("_%.3f", 1.0f / (float) sampleSize); 346 } 347 348 AndroidCodecSrc* src = new AndroidCodecSrc(path, mode, dstColorType, dstAlphaType, sampleSize); 349 push_src("image", folder, src); 350 } 351 352 static void push_codec_srcs(Path path) { 353 SkAutoTUnref<SkData> encoded(SkData::NewFromFileName(path.c_str())); 354 if (!encoded) { 355 SkDebugf("Couldn't read %s.", path.c_str()); 356 return; 357 } 358 SkAutoTDelete<SkCodec> codec(SkCodec::NewFromData(encoded)); 359 if (nullptr == codec.get()) { 360 SkDebugf("Couldn't create codec for %s.", path.c_str()); 361 return; 362 } 363 364 // Native Scales 365 // SkJpegCodec natively supports scaling to: 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875 366 const float nativeScales[] = { 0.125f, 0.25f, 0.375f, 0.5f, 0.625f, 0.750f, 0.875f, 1.0f }; 367 368 SkTArray<CodecSrc::Mode> nativeModes; 369 nativeModes.push_back(CodecSrc::kCodec_Mode); 370 nativeModes.push_back(CodecSrc::kCodecZeroInit_Mode); 371 nativeModes.push_back(CodecSrc::kGen_Mode); 372 switch (codec->getEncodedFormat()) { 373 case SkEncodedFormat::kJPEG_SkEncodedFormat: 374 nativeModes.push_back(CodecSrc::kScanline_Mode); 375 nativeModes.push_back(CodecSrc::kStripe_Mode); 376 nativeModes.push_back(CodecSrc::kCroppedScanline_Mode); 377 break; 378 case SkEncodedFormat::kWEBP_SkEncodedFormat: 379 nativeModes.push_back(CodecSrc::kSubset_Mode); 380 break; 381 case SkEncodedFormat::kRAW_SkEncodedFormat: 382 break; 383 default: 384 nativeModes.push_back(CodecSrc::kScanline_Mode); 385 nativeModes.push_back(CodecSrc::kStripe_Mode); 386 break; 387 } 388 389 SkTArray<CodecSrc::DstColorType> colorTypes; 390 colorTypes.push_back(CodecSrc::kGetFromCanvas_DstColorType); 391 switch (codec->getInfo().colorType()) { 392 case kGray_8_SkColorType: 393 colorTypes.push_back(CodecSrc::kGrayscale_Always_DstColorType); 394 if (kWBMP_SkEncodedFormat == codec->getEncodedFormat()) { 395 colorTypes.push_back(CodecSrc::kIndex8_Always_DstColorType); 396 } 397 break; 398 case kIndex_8_SkColorType: 399 colorTypes.push_back(CodecSrc::kIndex8_Always_DstColorType); 400 break; 401 default: 402 break; 403 } 404 405 SkTArray<SkAlphaType> alphaModes; 406 alphaModes.push_back(kPremul_SkAlphaType); 407 alphaModes.push_back(kUnpremul_SkAlphaType); 408 if (codec->getInfo().alphaType() == kOpaque_SkAlphaType) { 409 alphaModes.push_back(kOpaque_SkAlphaType); 410 } 411 412 for (CodecSrc::Mode mode : nativeModes) { 413 // SkCodecImageGenerator only runs for the default colorType 414 // recommended by SkCodec. There is no need to generate multiple 415 // tests for different colorTypes. 416 // TODO (msarett): Add scaling support to SkCodecImageGenerator. 417 if (CodecSrc::kGen_Mode == mode) { 418 // FIXME: The gpu backend does not draw kGray sources correctly. (skbug.com/4822) 419 if (kGray_8_SkColorType != codec->getInfo().colorType()) { 420 push_codec_src(path, mode, CodecSrc::kGetFromCanvas_DstColorType, 421 codec->getInfo().alphaType(), 1.0f); 422 } 423 continue; 424 } 425 426 for (float scale : nativeScales) { 427 for (CodecSrc::DstColorType colorType : colorTypes) { 428 for (SkAlphaType alphaType : alphaModes) { 429 // Only test kCroppedScanline_Mode when the alpha type is opaque. The test is 430 // slow and won't be interestingly different with different alpha types. 431 if (CodecSrc::kCroppedScanline_Mode == mode && 432 kOpaque_SkAlphaType != alphaType) { 433 continue; 434 } 435 436 push_codec_src(path, mode, colorType, alphaType, scale); 437 } 438 } 439 } 440 } 441 442 if (FLAGS_simpleCodec) { 443 return; 444 } 445 446 // https://bug.skia.org/4428 447 bool subset = false; 448 // The following image types are supported by BitmapRegionDecoder, 449 // so we will test full image decodes and subset decodes. 450 static const char* const exts[] = { 451 "jpg", "jpeg", "png", "webp", 452 "JPG", "JPEG", "PNG", "WEBP", 453 }; 454 for (const char* ext : exts) { 455 if (path.endsWith(ext)) { 456 subset = true; 457 break; 458 } 459 } 460 461 const int sampleSizes[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; 462 463 for (int sampleSize : sampleSizes) { 464 for (CodecSrc::DstColorType colorType : colorTypes) { 465 for (SkAlphaType alphaType : alphaModes) { 466 push_android_codec_src(path, AndroidCodecSrc::kFullImage_Mode, colorType, 467 alphaType, sampleSize); 468 if (subset) { 469 push_android_codec_src(path, AndroidCodecSrc::kDivisor_Mode, colorType, 470 alphaType, sampleSize); 471 } 472 } 473 } 474 } 475 } 476 477 static bool brd_color_type_supported(SkBitmapRegionDecoder::Strategy strategy, 478 CodecSrc::DstColorType dstColorType) { 479 switch (strategy) { 480 case SkBitmapRegionDecoder::kCanvas_Strategy: 481 if (CodecSrc::kGetFromCanvas_DstColorType == dstColorType) { 482 return true; 483 } 484 return false; 485 case SkBitmapRegionDecoder::kAndroidCodec_Strategy: 486 switch (dstColorType) { 487 case CodecSrc::kGetFromCanvas_DstColorType: 488 case CodecSrc::kIndex8_Always_DstColorType: 489 case CodecSrc::kGrayscale_Always_DstColorType: 490 return true; 491 default: 492 return false; 493 } 494 default: 495 SkASSERT(false); 496 return false; 497 } 498 } 499 500 static void push_brd_src(Path path, SkBitmapRegionDecoder::Strategy strategy, 501 CodecSrc::DstColorType dstColorType, BRDSrc::Mode mode, uint32_t sampleSize) { 502 SkString folder; 503 switch (strategy) { 504 case SkBitmapRegionDecoder::kCanvas_Strategy: 505 folder.append("brd_canvas"); 506 break; 507 case SkBitmapRegionDecoder::kAndroidCodec_Strategy: 508 folder.append("brd_android_codec"); 509 break; 510 default: 511 SkASSERT(false); 512 return; 513 } 514 515 switch (mode) { 516 case BRDSrc::kFullImage_Mode: 517 break; 518 case BRDSrc::kDivisor_Mode: 519 folder.append("_divisor"); 520 break; 521 default: 522 SkASSERT(false); 523 return; 524 } 525 526 switch (dstColorType) { 527 case CodecSrc::kGetFromCanvas_DstColorType: 528 break; 529 case CodecSrc::kIndex8_Always_DstColorType: 530 folder.append("_kIndex"); 531 break; 532 case CodecSrc::kGrayscale_Always_DstColorType: 533 folder.append("_kGray"); 534 break; 535 default: 536 SkASSERT(false); 537 return; 538 } 539 540 if (1 != sampleSize) { 541 folder.appendf("_%.3f", 1.0f / (float) sampleSize); 542 } 543 544 BRDSrc* src = new BRDSrc(path, strategy, mode, dstColorType, sampleSize); 545 push_src("image", folder, src); 546 } 547 548 static void push_brd_srcs(Path path) { 549 550 const SkBitmapRegionDecoder::Strategy strategies[] = { 551 SkBitmapRegionDecoder::kCanvas_Strategy, 552 SkBitmapRegionDecoder::kAndroidCodec_Strategy, 553 }; 554 555 // Test on a variety of sampleSizes, making sure to include: 556 // - 2, 4, and 8, which are natively supported by jpeg 557 // - multiples of 2 which are not divisible by 4 (analogous for 4) 558 // - larger powers of two, since BRD clients generally use powers of 2 559 // We will only produce output for the larger sizes on large images. 560 const uint32_t sampleSizes[] = { 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 24, 32, 64 }; 561 562 // We will only test to one backend (8888), but we will test all of the 563 // color types that we need to decode to on this backend. 564 const CodecSrc::DstColorType dstColorTypes[] = { 565 CodecSrc::kGetFromCanvas_DstColorType, 566 CodecSrc::kIndex8_Always_DstColorType, 567 CodecSrc::kGrayscale_Always_DstColorType, 568 }; 569 570 const BRDSrc::Mode modes[] = { 571 BRDSrc::kFullImage_Mode, 572 BRDSrc::kDivisor_Mode, 573 }; 574 575 for (SkBitmapRegionDecoder::Strategy strategy : strategies) { 576 for (uint32_t sampleSize : sampleSizes) { 577 for (CodecSrc::DstColorType dstColorType : dstColorTypes) { 578 if (brd_color_type_supported(strategy, dstColorType)) { 579 for (BRDSrc::Mode mode : modes) { 580 push_brd_src(path, strategy, dstColorType, mode, sampleSize); 581 } 582 } 583 } 584 } 585 } 586 } 587 588 static bool brd_supported(const char* ext) { 589 static const char* const exts[] = { 590 "jpg", "jpeg", "png", "webp", 591 "JPG", "JPEG", "PNG", "WEBP", 592 }; 593 594 for (uint32_t i = 0; i < SK_ARRAY_COUNT(exts); i++) { 595 if (0 == strcmp(exts[i], ext)) { 596 return true; 597 } 598 } 599 return false; 600 } 601 602 static bool gather_srcs() { 603 for (const skiagm::GMRegistry* r = skiagm::GMRegistry::Head(); r; r = r->next()) { 604 push_src("gm", "", new GMSrc(r->factory())); 605 } 606 for (int i = 0; i < FLAGS_skps.count(); i++) { 607 const char* path = FLAGS_skps[i]; 608 if (sk_isdir(path)) { 609 SkOSFile::Iter it(path, "skp"); 610 for (SkString file; it.next(&file); ) { 611 push_src("skp", "", new SKPSrc(SkOSPath::Join(path, file.c_str()))); 612 } 613 } else { 614 push_src("skp", "", new SKPSrc(path)); 615 } 616 } 617 618 SkTArray<SkString> images; 619 if (!CollectImages(&images)) { 620 return false; 621 } 622 623 for (auto image : images) { 624 push_codec_srcs(image); 625 if (FLAGS_simpleCodec) { 626 continue; 627 } 628 629 const char* ext = strrchr(image.c_str(), '.'); 630 if (ext && brd_supported(ext+1)) { 631 push_brd_srcs(image); 632 } 633 } 634 635 return true; 636 } 637 638 static void push_sink(const SkCommandLineConfig& config, Sink* s) { 639 SkAutoTDelete<Sink> sink(s); 640 641 // Try a simple Src as a canary. If it fails, skip this sink. 642 struct : public Src { 643 Error draw(SkCanvas* c) const override { 644 c->drawRect(SkRect::MakeWH(1,1), SkPaint()); 645 return ""; 646 } 647 SkISize size() const override { return SkISize::Make(16, 16); } 648 Name name() const override { return "justOneRect"; } 649 } justOneRect; 650 651 SkBitmap bitmap; 652 SkDynamicMemoryWStream stream; 653 SkString log; 654 Error err = sink->draw(justOneRect, &bitmap, &stream, &log); 655 if (err.isFatal()) { 656 SkDebugf("Could not run %s: %s\n", config.getTag().c_str(), err.c_str()); 657 exit(1); 658 } 659 660 TaggedSink& ts = gSinks.push_back(); 661 ts.reset(sink.detach()); 662 ts.tag = config.getTag(); 663 } 664 665 static bool gpu_supported() { 666 #if SK_SUPPORT_GPU 667 return FLAGS_gpu; 668 #else 669 return false; 670 #endif 671 } 672 673 static Sink* create_sink(const SkCommandLineConfig* config) { 674 #if SK_SUPPORT_GPU 675 if (gpu_supported()) { 676 if (const SkCommandLineConfigGpu* gpuConfig = config->asConfigGpu()) { 677 GrContextFactory::GLContextType contextType = gpuConfig->getContextType(); 678 GrContextFactory::GLContextOptions contextOptions = 679 GrContextFactory::kNone_GLContextOptions; 680 if (gpuConfig->getUseNVPR()) { 681 contextOptions = static_cast<GrContextFactory::GLContextOptions>( 682 contextOptions | GrContextFactory::kEnableNVPR_GLContextOptions); 683 } 684 GrContextFactory testFactory; 685 if (!testFactory.get(contextType, contextOptions)) { 686 SkDebugf("WARNING: can not create GPU context for config '%s'. " 687 "GM tests will be skipped.\n", gpuConfig->getTag().c_str()); 688 return nullptr; 689 } 690 return new GPUSink(contextType, contextOptions, gpuConfig->getSamples(), 691 gpuConfig->getUseDIText(), FLAGS_gpu_threading); 692 } 693 } 694 #endif 695 696 #define SINK(t, sink, ...) if (config->getBackend().equals(t)) { return new sink(__VA_ARGS__); } 697 698 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK 699 SINK("hwui", HWUISink); 700 #endif 701 702 if (FLAGS_cpu) { 703 SINK("565", RasterSink, kRGB_565_SkColorType); 704 SINK("8888", RasterSink, kN32_SkColorType); 705 SINK("pdf", PDFSink, "Pdfium"); 706 SINK("pdf_poppler", PDFSink, "Poppler"); 707 SINK("skp", SKPSink); 708 SINK("svg", SVGSink); 709 SINK("null", NullSink); 710 SINK("xps", XPSSink); 711 } 712 #undef SINK 713 return nullptr; 714 } 715 716 static Sink* create_via(const SkString& tag, Sink* wrapped) { 717 #define VIA(t, via, ...) if (tag.equals(t)) { return new via(__VA_ARGS__); } 718 VIA("twice", ViaTwice, wrapped); 719 VIA("serialize", ViaSerialization, wrapped); 720 VIA("pic", ViaPicture, wrapped); 721 VIA("2ndpic", ViaSecondPicture, wrapped); 722 VIA("sp", ViaSingletonPictures, wrapped); 723 VIA("tiles", ViaTiles, 256, 256, nullptr, wrapped); 724 VIA("tiles_rt", ViaTiles, 256, 256, new SkRTreeFactory, wrapped); 725 VIA("remote", ViaRemote, false, wrapped); 726 VIA("remote_cache", ViaRemote, true, wrapped); 727 VIA("mojo", ViaMojo, wrapped); 728 729 if (FLAGS_matrix.count() == 4) { 730 SkMatrix m; 731 m.reset(); 732 m.setScaleX((SkScalar)atof(FLAGS_matrix[0])); 733 m.setSkewX ((SkScalar)atof(FLAGS_matrix[1])); 734 m.setSkewY ((SkScalar)atof(FLAGS_matrix[2])); 735 m.setScaleY((SkScalar)atof(FLAGS_matrix[3])); 736 VIA("matrix", ViaMatrix, m, wrapped); 737 VIA("upright", ViaUpright, m, wrapped); 738 } 739 740 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK 741 VIA("androidsdk", ViaAndroidSDK, wrapped); 742 #endif 743 744 #undef VIA 745 return nullptr; 746 } 747 748 static void gather_sinks() { 749 SkCommandLineConfigArray configs; 750 ParseConfigs(FLAGS_config, &configs); 751 for (int i = 0; i < configs.count(); i++) { 752 const SkCommandLineConfig& config = *configs[i]; 753 Sink* sink = create_sink(&config); 754 if (sink == nullptr) { 755 SkDebugf("Skipping config %s: Don't understand '%s'.\n", config.getTag().c_str(), 756 config.getTag().c_str()); 757 continue; 758 } 759 760 const SkTArray<SkString>& parts = config.getViaParts(); 761 for (int j = parts.count(); j-- > 0;) { 762 const SkString& part = parts[j]; 763 Sink* next = create_via(part, sink); 764 if (next == nullptr) { 765 SkDebugf("Skipping config %s: Don't understand '%s'.\n", config.getTag().c_str(), 766 part.c_str()); 767 delete sink; 768 sink = nullptr; 769 break; 770 } 771 sink = next; 772 } 773 if (sink) { 774 push_sink(config, sink); 775 } 776 } 777 } 778 779 static bool dump_png(SkBitmap bitmap, const char* path, const char* md5) { 780 const int w = bitmap.width(), 781 h = bitmap.height(); 782 783 // First get the bitmap into N32 color format. The next step will work only there. 784 if (bitmap.colorType() != kN32_SkColorType) { 785 SkBitmap n32; 786 if (!bitmap.copyTo(&n32, kN32_SkColorType)) { 787 return false; 788 } 789 bitmap = n32; 790 } 791 792 // Convert our N32 bitmap into unpremul RGBA for libpng. 793 SkAutoTMalloc<uint32_t> rgba(w*h); 794 if (!bitmap.readPixels(SkImageInfo::Make(w,h, kRGBA_8888_SkColorType, kUnpremul_SkAlphaType), 795 rgba, 4*w, 0,0)) { 796 return false; 797 } 798 799 // We don't need bitmap anymore. Might as well drop our ref. 800 bitmap.reset(); 801 802 FILE* f = fopen(path, "wb"); 803 if (!f) { return false; } 804 805 png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); 806 if (!png) { 807 fclose(f); 808 return false; 809 } 810 811 png_infop info = png_create_info_struct(png); 812 if (!info) { 813 png_destroy_write_struct(&png, &info); 814 fclose(f); 815 return false; 816 } 817 818 SkString description; 819 description.append("Key: "); 820 for (int i = 0; i < FLAGS_key.count(); i++) { 821 description.appendf("%s ", FLAGS_key[i]); 822 } 823 description.append("Properties: "); 824 for (int i = 0; i < FLAGS_properties.count(); i++) { 825 description.appendf("%s ", FLAGS_properties[i]); 826 } 827 description.appendf("MD5: %s", md5); 828 829 png_text text[2]; 830 text[0].key = (png_charp)"Author"; 831 text[0].text = (png_charp)"DM dump_png()"; 832 text[0].compression = PNG_TEXT_COMPRESSION_NONE; 833 text[1].key = (png_charp)"Description"; 834 text[1].text = (png_charp)description.c_str(); 835 text[1].compression = PNG_TEXT_COMPRESSION_NONE; 836 png_set_text(png, info, text, 2); 837 838 png_init_io(png, f); 839 png_set_IHDR(png, info, (png_uint_32)w, (png_uint_32)h, 8, 840 PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, 841 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); 842 png_write_info(png, info); 843 for (int j = 0; j < h; j++) { 844 png_bytep row = (png_bytep)(rgba.get() + w*j); 845 png_write_rows(png, &row, 1); 846 } 847 png_write_end(png, info); 848 849 png_destroy_write_struct(&png, &info); 850 fclose(f); 851 return true; 852 } 853 854 static bool match(const char* needle, const char* haystack) { 855 return 0 == strcmp("_", needle) || nullptr != strstr(haystack, needle); 856 } 857 858 static bool is_blacklisted(const char* sink, const char* src, 859 const char* srcOptions, const char* name) { 860 for (int i = 0; i < FLAGS_blacklist.count() - 3; i += 4) { 861 if (match(FLAGS_blacklist[i+0], sink) && 862 match(FLAGS_blacklist[i+1], src) && 863 match(FLAGS_blacklist[i+2], srcOptions) && 864 match(FLAGS_blacklist[i+3], name)) { 865 return true; 866 } 867 } 868 return false; 869 } 870 871 // Even when a Task Sink reports to be non-threadsafe (e.g. GPU), we know things like 872 // .png encoding are definitely thread safe. This lets us offload that work to CPU threads. 873 static SkTaskGroup gDefinitelyThreadSafeWork; 874 875 // The finest-grained unit of work we can run: draw a single Src into a single Sink, 876 // report any errors, and perhaps write out the output: a .png of the bitmap, or a raw stream. 877 struct Task { 878 Task(const TaggedSrc& src, const TaggedSink& sink) : src(src), sink(sink) {} 879 const TaggedSrc& src; 880 const TaggedSink& sink; 881 882 static void Run(const Task& task) { 883 SkString name = task.src->name(); 884 885 SkString log; 886 if (!FLAGS_dryRun) { 887 SkBitmap bitmap; 888 SkDynamicMemoryWStream stream; 889 start(task.sink.tag.c_str(), task.src.tag.c_str(), 890 task.src.options.c_str(), name.c_str()); 891 Error err = task.sink->draw(*task.src, &bitmap, &stream, &log); 892 if (!err.isEmpty()) { 893 if (err.isFatal()) { 894 fail(SkStringPrintf("%s %s %s %s: %s", 895 task.sink.tag.c_str(), 896 task.src.tag.c_str(), 897 task.src.options.c_str(), 898 name.c_str(), 899 err.c_str())); 900 } else { 901 done(task.sink.tag.c_str(), task.src.tag.c_str(), 902 task.src.options.c_str(), name.c_str()); 903 return; 904 } 905 } 906 907 // We're likely switching threads here, so we must capture by value, [=] or [foo,bar]. 908 SkStreamAsset* data = stream.detachAsStream(); 909 gDefinitelyThreadSafeWork.add([task,name,bitmap,data]{ 910 SkAutoTDelete<SkStreamAsset> ownedData(data); 911 912 // Why doesn't the copy constructor do this when we have pre-locked pixels? 913 bitmap.lockPixels(); 914 915 SkString md5; 916 if (!FLAGS_writePath.isEmpty() || !FLAGS_readPath.isEmpty()) { 917 SkMD5 hash; 918 if (data->getLength()) { 919 hash.writeStream(data, data->getLength()); 920 data->rewind(); 921 } else { 922 // If we're BGRA (Linux, Windows), swizzle over to RGBA (Mac, Android). 923 // This helps eliminate multiple 0-pixel-diff hashes on gold.skia.org. 924 // (Android's general slow speed breaks the tie arbitrarily in RGBA's favor.) 925 // We might consider promoting 565 to RGBA too. 926 if (bitmap.colorType() == kBGRA_8888_SkColorType) { 927 SkBitmap swizzle; 928 SkAssertResult(bitmap.copyTo(&swizzle, kRGBA_8888_SkColorType)); 929 hash.write(swizzle.getPixels(), swizzle.getSize()); 930 } else { 931 hash.write(bitmap.getPixels(), bitmap.getSize()); 932 } 933 } 934 SkMD5::Digest digest; 935 hash.finish(digest); 936 for (int i = 0; i < 16; i++) { 937 md5.appendf("%02x", digest.data[i]); 938 } 939 } 940 941 if (!FLAGS_readPath.isEmpty() && 942 !gGold.contains(Gold(task.sink.tag, task.src.tag, 943 task.src.options, name, md5))) { 944 fail(SkStringPrintf("%s not found for %s %s %s %s in %s", 945 md5.c_str(), 946 task.sink.tag.c_str(), 947 task.src.tag.c_str(), 948 task.src.options.c_str(), 949 name.c_str(), 950 FLAGS_readPath[0])); 951 } 952 953 if (!FLAGS_writePath.isEmpty()) { 954 const char* ext = task.sink->fileExtension(); 955 if (data->getLength()) { 956 WriteToDisk(task, md5, ext, data, data->getLength(), nullptr); 957 SkASSERT(bitmap.drawsNothing()); 958 } else if (!bitmap.drawsNothing()) { 959 WriteToDisk(task, md5, ext, nullptr, 0, &bitmap); 960 } 961 } 962 }); 963 } 964 done(task.sink.tag.c_str(), task.src.tag.c_str(), task.src.options.c_str(), name.c_str()); 965 } 966 967 static void WriteToDisk(const Task& task, 968 SkString md5, 969 const char* ext, 970 SkStream* data, size_t len, 971 const SkBitmap* bitmap) { 972 JsonWriter::BitmapResult result; 973 result.name = task.src->name(); 974 result.config = task.sink.tag; 975 result.sourceType = task.src.tag; 976 result.sourceOptions = task.src.options; 977 result.ext = ext; 978 result.md5 = md5; 979 JsonWriter::AddBitmapResult(result); 980 981 // If an MD5 is uninteresting, we want it noted in the JSON file, 982 // but don't want to dump it out as a .png (or whatever ext is). 983 if (gUninterestingHashes.contains(md5)) { 984 return; 985 } 986 987 const char* dir = FLAGS_writePath[0]; 988 if (0 == strcmp(dir, "@")) { // Needed for iOS. 989 dir = FLAGS_resourcePath[0]; 990 } 991 sk_mkdir(dir); 992 993 SkString path; 994 if (FLAGS_nameByHash) { 995 path = SkOSPath::Join(dir, result.md5.c_str()); 996 path.append("."); 997 path.append(ext); 998 if (sk_exists(path.c_str())) { 999 return; // Content-addressed. If it exists already, we're done. 1000 } 1001 } else { 1002 path = SkOSPath::Join(dir, task.sink.tag.c_str()); 1003 sk_mkdir(path.c_str()); 1004 path = SkOSPath::Join(path.c_str(), task.src.tag.c_str()); 1005 sk_mkdir(path.c_str()); 1006 if (strcmp(task.src.options.c_str(), "") != 0) { 1007 path = SkOSPath::Join(path.c_str(), task.src.options.c_str()); 1008 sk_mkdir(path.c_str()); 1009 } 1010 path = SkOSPath::Join(path.c_str(), task.src->name().c_str()); 1011 path.append("."); 1012 path.append(ext); 1013 } 1014 1015 if (bitmap) { 1016 if (!dump_png(*bitmap, path.c_str(), result.md5.c_str())) { 1017 fail(SkStringPrintf("Can't encode PNG to %s.\n", path.c_str())); 1018 return; 1019 } 1020 } else { 1021 SkFILEWStream file(path.c_str()); 1022 if (!file.isValid()) { 1023 fail(SkStringPrintf("Can't open %s for writing.\n", path.c_str())); 1024 return; 1025 } 1026 if (!file.writeStream(data, len)) { 1027 fail(SkStringPrintf("Can't write to %s.\n", path.c_str())); 1028 return; 1029 } 1030 } 1031 } 1032 }; 1033 1034 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 1035 1036 // Unit tests don't fit so well into the Src/Sink model, so we give them special treatment. 1037 1038 static SkTDArray<skiatest::Test> gParallelTests, gSerialTests; 1039 1040 static void gather_tests() { 1041 if (!FLAGS_src.contains("tests")) { 1042 return; 1043 } 1044 for (const skiatest::TestRegistry* r = skiatest::TestRegistry::Head(); r; r = r->next()) { 1045 if (!in_shard()) { 1046 continue; 1047 } 1048 // Despite its name, factory() is returning a reference to 1049 // link-time static const POD data. 1050 const skiatest::Test& test = r->factory(); 1051 if (SkCommandLineFlags::ShouldSkip(FLAGS_match, test.name)) { 1052 continue; 1053 } 1054 if (test.needsGpu && gpu_supported()) { 1055 (FLAGS_gpu_threading ? gParallelTests : gSerialTests).push(test); 1056 } else if (!test.needsGpu && FLAGS_cpu) { 1057 gParallelTests.push(test); 1058 } 1059 } 1060 } 1061 1062 static void run_test(skiatest::Test test) { 1063 struct : public skiatest::Reporter { 1064 void reportFailed(const skiatest::Failure& failure) override { 1065 fail(failure.toString()); 1066 JsonWriter::AddTestFailure(failure); 1067 } 1068 bool allowExtendedTest() const override { 1069 return FLAGS_pathOpsExtended; 1070 } 1071 bool verbose() const override { return FLAGS_veryVerbose; } 1072 } reporter; 1073 1074 if (!FLAGS_dryRun && !is_blacklisted("_", "tests", "_", test.name)) { 1075 start("unit", "test", "", test.name); 1076 GrContextFactory factory; 1077 test.proc(&reporter, &factory); 1078 } 1079 done("unit", "test", "", test.name); 1080 } 1081 1082 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 1083 1084 DEFINE_int32(status_sec, 15, "Print status this often (and if we crash)."); 1085 1086 SkThread* start_status_thread() { 1087 auto thread = new SkThread([] (void*) { 1088 for (;;) { 1089 print_status(); 1090 #if defined(SK_BUILD_FOR_WIN) 1091 Sleep(FLAGS_status_sec * 1000); 1092 #else 1093 sleep(FLAGS_status_sec); 1094 #endif 1095 } 1096 }); 1097 thread->start(); 1098 return thread; 1099 } 1100 1101 #define PORTABLE_FONT_PREFIX "Toy Liberation " 1102 1103 static SkTypeface* create_from_name(const char familyName[], SkTypeface::Style style) { 1104 if (familyName && strlen(familyName) > sizeof(PORTABLE_FONT_PREFIX) 1105 && !strncmp(familyName, PORTABLE_FONT_PREFIX, sizeof(PORTABLE_FONT_PREFIX) - 1)) { 1106 return sk_tool_utils::create_portable_typeface(familyName, style); 1107 } 1108 return nullptr; 1109 } 1110 1111 #undef PORTABLE_FONT_PREFIX 1112 1113 extern SkTypeface* (*gCreateTypefaceDelegate)(const char [], SkTypeface::Style ); 1114 1115 int dm_main(); 1116 int dm_main() { 1117 setup_crash_handler(); 1118 1119 JsonWriter::DumpJson(); // It's handy for the bots to assume this is ~never missing. 1120 SkAutoGraphics ag; 1121 SkTaskGroup::Enabler enabled(FLAGS_threads); 1122 gCreateTypefaceDelegate = &create_from_name; 1123 1124 { 1125 SkString testResourcePath = GetResourcePath("color_wheel.png"); 1126 SkFILEStream testResource(testResourcePath.c_str()); 1127 if (!testResource.isValid()) { 1128 SkDebugf("Some resources are missing. Do you need to set --resourcePath?\n"); 1129 } 1130 } 1131 gather_gold(); 1132 gather_uninteresting_hashes(); 1133 1134 if (!gather_srcs()) { 1135 return 1; 1136 } 1137 gather_sinks(); 1138 gather_tests(); 1139 1140 gPending = gSrcs.count() * gSinks.count() + gParallelTests.count() + gSerialTests.count(); 1141 SkDebugf("%d srcs * %d sinks + %d tests == %d tasks", 1142 gSrcs.count(), gSinks.count(), gParallelTests.count() + gSerialTests.count(), gPending); 1143 SkAutoTDelete<SkThread> statusThread(start_status_thread()); 1144 1145 // Kick off as much parallel work as we can, making note of any serial work we'll need to do. 1146 SkTaskGroup parallel; 1147 SkTArray<Task> serial; 1148 1149 for (auto& sink : gSinks) 1150 for (auto& src : gSrcs) { 1151 if (src->veto(sink->flags()) || 1152 is_blacklisted(sink.tag.c_str(), src.tag.c_str(), 1153 src.options.c_str(), src->name().c_str())) { 1154 SkAutoTAcquire<SkPODSpinlock> lock(gMutex); 1155 gPending--; 1156 continue; 1157 } 1158 1159 Task task(src, sink); 1160 if (src->serial() || sink->serial()) { 1161 serial.push_back(task); 1162 } else { 1163 parallel.add([task] { Task::Run(task); }); 1164 } 1165 } 1166 for (auto test : gParallelTests) { 1167 parallel.add([test] { run_test(test); }); 1168 } 1169 1170 // With the parallel work running, run serial tasks and tests here on main thread. 1171 for (auto task : serial) { Task::Run(task); } 1172 for (auto test : gSerialTests) { run_test(test); } 1173 1174 // Wait for any remaining parallel work to complete (including any spun off of serial tasks). 1175 parallel.wait(); 1176 gDefinitelyThreadSafeWork.wait(); 1177 1178 // We'd better have run everything. 1179 SkASSERT(gPending == 0); 1180 1181 // At this point we're back in single-threaded land. 1182 sk_tool_utils::release_portable_typefaces(); 1183 1184 if (gFailures.count() > 0) { 1185 SkDebugf("Failures:\n"); 1186 for (int i = 0; i < gFailures.count(); i++) { 1187 SkDebugf("\t%s\n", gFailures[i].c_str()); 1188 } 1189 SkDebugf("%d failures\n", gFailures.count()); 1190 return 1; 1191 } 1192 1193 #ifdef SK_PDF_IMAGE_STATS 1194 SkPDFImageDumpStats(); 1195 #endif // SK_PDF_IMAGE_STATS 1196 1197 print_status(); 1198 SkDebugf("Finished!\n"); 1199 return 0; 1200 } 1201 1202 // TODO: currently many GPU tests are declared outside SK_SUPPORT_GPU guards. 1203 // Thus we export the empty RunWithGPUTestContexts when SK_SUPPORT_GPU=0. 1204 namespace skiatest { 1205 namespace { 1206 typedef void(*TestWithGrContext)(skiatest::Reporter*, GrContext*); 1207 typedef void(*TestWithGrContextAndGLContext)(skiatest::Reporter*, GrContext*, SkGLContext*); 1208 #if SK_SUPPORT_GPU 1209 template<typename T> 1210 void call_test(T test, skiatest::Reporter* reporter, const GrContextFactory::ContextInfo& context); 1211 template<> 1212 void call_test(TestWithGrContext test, skiatest::Reporter* reporter, 1213 const GrContextFactory::ContextInfo& context) { 1214 test(reporter, context.fGrContext); 1215 } 1216 template<> 1217 void call_test(TestWithGrContextAndGLContext test, skiatest::Reporter* reporter, 1218 const GrContextFactory::ContextInfo& context) { 1219 test(reporter, context.fGrContext, context.fGLContext); 1220 } 1221 #endif 1222 } // namespace 1223 1224 template<typename T> 1225 void RunWithGPUTestContexts(T test, GPUTestContexts testContexts, Reporter* reporter, 1226 GrContextFactory* factory) { 1227 #if SK_SUPPORT_GPU 1228 // Iterate over context types, except use "native" instead of explicitly trying OpenGL and 1229 // OpenGL ES. Do not use GLES on desktop, since tests do not account for not fixing 1230 // http://skbug.com/2809 1231 GrContextFactory::GLContextType contextTypes[] = { 1232 GrContextFactory::kNative_GLContextType, 1233 #if SK_ANGLE 1234 #ifdef SK_BUILD_FOR_WIN 1235 GrContextFactory::kANGLE_GLContextType, 1236 #endif 1237 GrContextFactory::kANGLE_GL_GLContextType, 1238 #endif 1239 #if SK_COMMAND_BUFFER 1240 GrContextFactory::kCommandBufferES2_GLContextType, 1241 GrContextFactory::kCommandBufferES3_GLContextType, 1242 #endif 1243 #if SK_MESA 1244 GrContextFactory::kMESA_GLContextType, 1245 #endif 1246 GrContextFactory::kNull_GLContextType, 1247 GrContextFactory::kDebug_GLContextType, 1248 }; 1249 static_assert(SK_ARRAY_COUNT(contextTypes) == GrContextFactory::kGLContextTypeCnt - 2, 1250 "Skipping unexpected GLContextType for GPU tests"); 1251 1252 for (auto& contextType : contextTypes) { 1253 int contextSelector = kNone_GPUTestContexts; 1254 if (GrContextFactory::IsRenderingGLContext(contextType)) { 1255 contextSelector |= kAllRendering_GPUTestContexts; 1256 } else if (contextType == GrContextFactory::kNative_GLContextType) { 1257 contextSelector |= kNative_GPUTestContexts; 1258 } else if (contextType == GrContextFactory::kNull_GLContextType) { 1259 contextSelector |= kNull_GPUTestContexts; 1260 } else if (contextType == GrContextFactory::kDebug_GLContextType) { 1261 contextSelector |= kDebug_GPUTestContexts; 1262 } 1263 if ((testContexts & contextSelector) == 0) { 1264 continue; 1265 } 1266 GrContextFactory::ContextInfo context = factory->getContextInfo(contextType); 1267 if (context.fGrContext) { 1268 call_test(test, reporter, context); 1269 } 1270 context = factory->getContextInfo(contextType, 1271 GrContextFactory::kEnableNVPR_GLContextOptions); 1272 if (context.fGrContext) { 1273 call_test(test, reporter, context); 1274 } 1275 } 1276 #endif 1277 } 1278 1279 template 1280 void RunWithGPUTestContexts<TestWithGrContext>(TestWithGrContext test, 1281 GPUTestContexts testContexts, 1282 Reporter* reporter, 1283 GrContextFactory* factory); 1284 template 1285 void RunWithGPUTestContexts<TestWithGrContextAndGLContext>(TestWithGrContextAndGLContext test, 1286 GPUTestContexts testContexts, 1287 Reporter* reporter, 1288 GrContextFactory* factory); 1289 } // namespace skiatest 1290 1291 #if !defined(SK_BUILD_FOR_IOS) 1292 int main(int argc, char** argv) { 1293 SkCommandLineFlags::Parse(argc, argv); 1294 return dm_main(); 1295 } 1296 #endif 1297