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