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