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