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