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