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