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