1 // Copyright 2019 Google LLC.
2 // Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
3 
4 #include "experimental/svg/model/SkSVGDOM.h"
5 #include "gm/gm.h"
6 #include "include/codec/SkCodec.h"
7 #include "include/core/SkColorSpace.h"
8 #include "include/core/SkGraphics.h"
9 #include "include/core/SkPicture.h"
10 #include "include/core/SkPictureRecorder.h"
11 #include "include/docs/SkPDFDocument.h"
12 #include "include/gpu/GrContextOptions.h"
13 #include "include/private/SkTHash.h"
14 #include "src/core/SkColorSpacePriv.h"
15 #include "src/core/SkMD5.h"
16 #include "src/core/SkOSFile.h"
17 #include "src/gpu/GrContextPriv.h"
18 #include "src/gpu/GrGpu.h"
19 #include "src/utils/SkOSPath.h"
20 #include "tests/Test.h"
21 #include "tools/AutoreleasePool.h"
22 #include "tools/CrashHandler.h"
23 #include "tools/HashAndEncode.h"
24 #include "tools/ToolUtils.h"
25 #include "tools/flags/CommandLineFlags.h"
26 #include "tools/flags/CommonFlags.h"
27 #include "tools/gpu/GrContextFactory.h"
28 #include "tools/gpu/MemoryCache.h"
29 #include "tools/trace/EventTracingPriv.h"
30 #include <chrono>
31 #include <functional>
32 #include <stdio.h>
33 #include <stdlib.h>
34 
35 #if defined(SK_ENABLE_SKOTTIE)
36     #include "modules/skottie/include/Skottie.h"
37     #include "modules/skresources/include/SkResources.h"
38 #endif
39 
40 using sk_gpu_test::GrContextFactory;
41 
42 static DEFINE_bool(listGMs  , false, "Print GM names and exit.");
43 static DEFINE_bool(listTests, false, "Print unit test names and exit.");
44 
45 static DEFINE_string2(sources, s, "", "Which GMs, .skps, or images to draw.");
46 static DEFINE_string2(backend, b, "", "Backend used to create a canvas to draw into.");
47 
48 static DEFINE_string(ct    ,   "8888", "The color type for any raster backend.");
49 static DEFINE_string(at    , "premul", "The alpha type for any raster backend.");
50 static DEFINE_string(gamut ,   "srgb", "The color gamut for any raster backend.");
51 static DEFINE_string(tf    ,   "srgb", "The transfer function for any raster backend.");
52 static DEFINE_bool  (legacy,    false, "Use a null SkColorSpace instead of --gamut and --tf?");
53 static DEFINE_bool  (skvm  ,    false, "Use SkVMBlitter when supported?");
54 static DEFINE_bool  (dylib ,    false, "Use SkVM via dylib?");
55 
56 static DEFINE_int   (samples ,         0, "Samples per pixel in GPU backends.");
57 static DEFINE_bool  (stencils,      true, "If false, avoid stencil buffers in GPU backends.");
58 static DEFINE_bool  (dit     ,     false, "Use device-independent text in GPU backends.");
59 static DEFINE_string(surf    , "default", "Backing store for GPU backend surfaces.");
60 
61 static DEFINE_bool(       preAbandonGpuContext, false, "Abandon the GrContext before drawing.");
62 static DEFINE_bool(          abandonGpuContext, false, "Abandon the GrContext after drawing.");
63 static DEFINE_bool(releaseAndAbandonGpuContext, false,
64                    "Release all GPU resources and abandon the GrContext after drawing.");
65 
66 static DEFINE_bool(decodeToDst, false,
67                    "Decode images to destination format rather than suggested natural format.");
68 
69 static DEFINE_double(rasterDPI, SK_ScalarDefaultRasterDPI,
70                      "DPI for rasterized content in vector backends like --backend pdf.");
71 static DEFINE_bool(PDFA, false, "Create PDF/A with --backend pdf?");
72 
73 static DEFINE_bool   (cpuDetect, true, "Detect CPU features for runtime optimizations?");
74 static DEFINE_string2(writePath, w, "", "Write .pngs to this directory if set.");
75 static DEFINE_bool   (quick, false, "Skip image hashing and encoding?");
76 
77 static DEFINE_string(writeShaders, "", "Write GLSL shaders to this directory if set.");
78 
79 static DEFINE_string(key,        "", "Metadata passed through to .png encoder and .json output.");
80 static DEFINE_string(properties, "", "Metadata passed through to .png encoder and .json output.");
81 
82 template <typename T>
83 struct FlagOption {
84     const char* label;
85     T           value;
86 };
87 
88 template <typename T, int N>
parse_flag(const CommandLineFlags::StringArray & flag,const char * flag_name,const FlagOption<T> (& array)[N],T * value)89 static bool parse_flag(const CommandLineFlags::StringArray& flag,
90                        const char* flag_name,
91                        const FlagOption<T> (&array)[N],
92                        T* value) {
93     for (auto entry : array) {
94         if (flag.contains(entry.label)) {
95             *value = entry.value;
96             return true;
97         }
98     }
99     fprintf(stderr, "Known values for --%s:\n", flag_name);
100     for (auto entry : array) {
101         fprintf(stderr, "    --%s %s\n", flag_name, entry.label);
102     }
103     return false;
104 }
105 
106 struct Result {
107     enum { Ok, Skip, Fail} status;
108     SkString               failure;
109 };
110 static const Result ok = {Result::Ok,   {}},
111                   skip = {Result::Skip, {}};
112 
113 template <typename... Args>
fail(const char * why,Args...args)114 static Result fail(const char* why, Args... args) {
115     return { Result::Fail, SkStringPrintf(why, args...) };
116 }
117 
118 
119 struct Source {
120     SkString                               name;
121     SkISize                                size;
122     std::function<Result(SkCanvas*)>       draw;
__anon4965af360202Source123     std::function<void(GrContextOptions*)> tweak = [](GrContextOptions*){};
124 };
125 
init(Source * source,std::shared_ptr<skiagm::GM> gm)126 static void init(Source* source, std::shared_ptr<skiagm::GM> gm) {
127     source->size  = gm->getISize();
128     source->tweak = [gm](GrContextOptions* options) { gm->modifyGrContextOptions(options); };
129     source->draw  = [gm](SkCanvas* canvas) {
130         SkString err;
131         switch (gm->draw(canvas, &err)) {
132             case skiagm::DrawResult::kOk:   break;
133             case skiagm::DrawResult::kSkip: return skip;
134             case skiagm::DrawResult::kFail: return fail(err.c_str());
135         }
136         return ok;
137     };
138 }
139 
init(Source * source,sk_sp<SkPicture> pic)140 static void init(Source* source, sk_sp<SkPicture> pic) {
141     source->size = pic->cullRect().roundOut().size();
142     source->draw = [pic](SkCanvas* canvas) {
143         canvas->drawPicture(pic);
144         return ok;
145     };
146 }
147 
init(Source * source,std::shared_ptr<SkCodec> codec)148 static void init(Source* source, std::shared_ptr<SkCodec> codec) {
149     source->size = codec->dimensions();
150     source->draw = [codec](SkCanvas* canvas) {
151         SkImageInfo info = codec->getInfo();
152         if (FLAGS_decodeToDst) {
153             info = canvas->imageInfo().makeDimensions(info.dimensions());
154         }
155 
156         SkBitmap bm;
157         bm.allocPixels(info);
158         switch (SkCodec::Result result = codec->getPixels(info, bm.getPixels(), bm.rowBytes())) {
159             case SkCodec::kSuccess:
160             case SkCodec::kErrorInInput:
161             case SkCodec::kIncompleteInput: canvas->drawBitmap(bm, 0,0);
162                                             break;
163             default: return fail("codec->getPixels() failed: %d\n", result);
164         }
165         return ok;
166     };
167 }
168 
init(Source * source,sk_sp<SkSVGDOM> svg)169 static void init(Source* source, sk_sp<SkSVGDOM> svg) {
170     source->size = svg->containerSize().isEmpty() ? SkISize{1000,1000}
171                                                   : svg->containerSize().toCeil();
172     source->draw = [svg](SkCanvas* canvas) {
173         svg->render(canvas);
174         return ok;
175     };
176 }
177 
178 #if defined(SK_ENABLE_SKOTTIE)
init(Source * source,sk_sp<skottie::Animation> animation)179 static void init(Source* source, sk_sp<skottie::Animation> animation) {
180     source->size = {1000,1000};
181     source->draw = [animation](SkCanvas* canvas) {
182         canvas->clear(SK_ColorWHITE);
183 
184         // Draw frames in a shuffled order to exercise nonlinear frame progression.
185         // The film strip will still be in time order, just drawn out of order.
186         const int order[] = { 4, 0, 3, 1, 2 };
187         const int tiles = SK_ARRAY_COUNT(order);
188         const float dim = 1000.0f / tiles;
189 
190         const float dt = 1.0f / (tiles*tiles - 1);
191 
192         for (int y : order)
193         for (int x : order) {
194             SkRect dst = {x*dim, y*dim, (x+1)*dim, (y+1)*dim};
195 
196             SkAutoCanvasRestore _(canvas, true/*save now*/);
197             canvas->clipRect(dst, /*aa=*/true);
198             canvas->concat(SkMatrix::MakeRectToRect(SkRect::MakeSize(animation->size()),
199                                                     dst,
200                                                     SkMatrix::kCenter_ScaleToFit));
201             float t = (y*tiles + x) * dt;
202             animation->seek(t);
203             animation->render(canvas);
204         }
205         return ok;
206     };
207 }
208 #endif
209 
init(Source * source,const skiatest::Test & test)210 static void init(Source* source, const skiatest::Test& test) {
211     source->size  = {1,1};
212     source->draw  = [test](SkCanvas* canvas) {
213         struct Reporter : public skiatest::Reporter {
214             SkString msg;
215 
216             void reportFailed(const skiatest::Failure& failure) override {
217                 msg = failure.toString();
218             }
219         } reporter;
220 
221         test.run(&reporter, GrContextOptions{});
222 
223         if (reporter.msg.isEmpty()) {
224             canvas->clear(SK_ColorGREEN);
225             return ok;
226         }
227 
228         canvas->clear(SK_ColorRED);
229         return fail(reporter.msg.c_str());
230     };
231 }
232 
draw_with_cpu(std::function<bool (SkCanvas *)> draw,SkImageInfo info)233 static sk_sp<SkImage> draw_with_cpu(std::function<bool(SkCanvas*)> draw,
234                                     SkImageInfo info) {
235     if (sk_sp<SkSurface> surface = SkSurface::MakeRaster(info)) {
236         if (draw(surface->getCanvas())) {
237             return surface->makeImageSnapshot();
238         }
239     }
240     return nullptr;
241 }
242 
draw_as_skp(std::function<bool (SkCanvas *)> draw,SkImageInfo info)243 static sk_sp<SkData> draw_as_skp(std::function<bool(SkCanvas*)> draw,
244                                  SkImageInfo info) {
245     SkPictureRecorder recorder;
246     if (draw(recorder.beginRecording(info.width(), info.height()))) {
247         return recorder.finishRecordingAsPicture()->serialize();
248     }
249     return nullptr;
250 }
251 
draw_as_pdf(std::function<bool (SkCanvas *)> draw,SkImageInfo info,SkString name)252 static sk_sp<SkData> draw_as_pdf(std::function<bool(SkCanvas*)> draw,
253                                  SkImageInfo info,
254                                  SkString name) {
255     SkPDF::Metadata metadata;
256     metadata.fTitle     = name;
257     metadata.fCreator   = "Skia/FM";
258     metadata.fRasterDPI = FLAGS_rasterDPI;
259     metadata.fPDFA      = FLAGS_PDFA;
260 
261     SkDynamicMemoryWStream stream;
262     if (sk_sp<SkDocument> doc = SkPDF::MakeDocument(&stream, metadata)) {
263         if (draw(doc->beginPage(info.width(), info.height()))) {
264             doc->endPage();
265             doc->close();
266             return stream.detachAsData();
267         }
268     }
269     return nullptr;
270 }
271 
draw_with_gpu(std::function<bool (SkCanvas *)> draw,SkImageInfo info,GrContextFactory::ContextType api,GrContextFactory * factory)272 static sk_sp<SkImage> draw_with_gpu(std::function<bool(SkCanvas*)> draw,
273                                     SkImageInfo info,
274                                     GrContextFactory::ContextType api,
275                                     GrContextFactory* factory) {
276     enum class SurfaceType { kDefault, kBackendTexture, kBackendRenderTarget };
277     const FlagOption<SurfaceType> kSurfaceTypes[] = {
278         { "default", SurfaceType::kDefault },
279         { "betex"  , SurfaceType::kBackendTexture },
280         { "bert"   , SurfaceType::kBackendRenderTarget },
281     };
282     SurfaceType surfaceType;
283     if (!parse_flag(FLAGS_surf, "surf", kSurfaceTypes, &surfaceType)) {
284         return nullptr;
285     }
286 
287     auto overrides = GrContextFactory::ContextOverrides::kNone;
288     if (!FLAGS_stencils) { overrides |= GrContextFactory::ContextOverrides::kAvoidStencilBuffers; }
289 
290     GrContext* context = factory->getContextInfo(api, overrides)
291                                  .grContext();
292 
293     uint32_t flags = FLAGS_dit ? SkSurfaceProps::kUseDeviceIndependentFonts_Flag
294                                : 0;
295     SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType);
296 
297     sk_sp<SkSurface> surface;
298     GrBackendTexture backendTexture;
299     GrBackendRenderTarget backendRT;
300 
301     switch (surfaceType) {
302         case SurfaceType::kDefault:
303             surface = SkSurface::MakeRenderTarget(context,
304                                                   SkBudgeted::kNo,
305                                                   info,
306                                                   FLAGS_samples,
307                                                   &props);
308             break;
309 
310         case SurfaceType::kBackendTexture:
311             backendTexture = context->createBackendTexture(info.width(),
312                                                            info.height(),
313                                                            info.colorType(),
314                                                            GrMipMapped::kNo,
315                                                            GrRenderable::kYes,
316                                                            GrProtected::kNo);
317             surface = SkSurface::MakeFromBackendTexture(context,
318                                                         backendTexture,
319                                                         kTopLeft_GrSurfaceOrigin,
320                                                         FLAGS_samples,
321                                                         info.colorType(),
322                                                         info.refColorSpace(),
323                                                         &props);
324             break;
325 
326         case SurfaceType::kBackendRenderTarget:
327             backendRT = context->priv().getGpu()
328                 ->createTestingOnlyBackendRenderTarget(info.width(),
329                                                        info.height(),
330                                                        SkColorTypeToGrColorType(info.colorType()));
331             surface = SkSurface::MakeFromBackendRenderTarget(context,
332                                                              backendRT,
333                                                              kBottomLeft_GrSurfaceOrigin,
334                                                              info.colorType(),
335                                                              info.refColorSpace(),
336                                                              &props);
337             break;
338     }
339 
340     if (!surface) {
341         fprintf(stderr, "Could not create GPU surface.\n");
342         return nullptr;
343     }
344 
345     if (FLAGS_preAbandonGpuContext) {
346         factory->abandonContexts();
347     }
348 
349     sk_sp<SkImage> image;
350     if (draw(surface->getCanvas())) {
351         image = surface->makeImageSnapshot();
352     }
353 
354     if (FLAGS_abandonGpuContext) {
355         factory->abandonContexts();
356     } else if (FLAGS_releaseAndAbandonGpuContext) {
357         factory->releaseResourcesAndAbandonContexts();
358     }
359 
360     if (!context->abandoned()) {
361         surface.reset();
362         if (backendTexture.isValid()) {
363             context->deleteBackendTexture(backendTexture);
364         }
365         if (backendRT.isValid()) {
366             context->priv().getGpu()->deleteTestingOnlyBackendRenderTarget(backendRT);
367         }
368     }
369 
370     return image;
371 }
372 
373 extern bool gUseSkVMBlitter;
374 extern bool gSkVMJITViaDylib;
375 
main(int argc,char ** argv)376 int main(int argc, char** argv) {
377     CommandLineFlags::Parse(argc, argv);
378     SetupCrashHandler();
379 
380     if (FLAGS_cpuDetect) {
381         SkGraphics::Init();
382     }
383     gUseSkVMBlitter  = FLAGS_skvm;
384     gSkVMJITViaDylib = FLAGS_dylib;
385 
386     initializeEventTracingForTools();
387     ToolUtils::SetDefaultFontMgr();
388     SetAnalyticAAFromCommonFlags();
389 
390     GrContextOptions baseOptions;
391     SetCtxOptionsFromCommonFlags(&baseOptions);
392 
393     sk_gpu_test::MemoryCache memoryCache;
394     if (!FLAGS_writeShaders.isEmpty()) {
395         baseOptions.fPersistentCache = &memoryCache;
396         baseOptions.fShaderCacheStrategy = GrContextOptions::ShaderCacheStrategy::kBackendSource;
397     }
398 
399     SkTHashMap<SkString, skiagm::GMFactory> gm_factories;
400     for (skiagm::GMFactory factory : skiagm::GMRegistry::Range()) {
401         std::unique_ptr<skiagm::GM> gm{factory()};
402         if (FLAGS_listGMs) {
403             fprintf(stdout, "%s\n", gm->getName());
404         } else {
405             gm_factories.set(SkString{gm->getName()}, factory);
406         }
407     }
408 
409     SkTHashMap<SkString, const skiatest::Test*> tests;
410     for (const skiatest::Test& test : skiatest::TestRegistry::Range()) {
411         if (test.needsGpu) {
412             continue;  // TODO
413         }
414         if (FLAGS_listTests) {
415             fprintf(stdout, "%s\n", test.name);
416         } else {
417             tests.set(SkString{test.name}, &test);
418         }
419     }
420 
421     if (FLAGS_listGMs || FLAGS_listTests) {
422         return 0;
423     }
424     if (FLAGS_sources.isEmpty()) {
425         fprintf(stderr, "Please give me something to run using -s/--sources!\n");
426         return 1;
427     }
428 
429     SkTArray<Source> sources;
430     for (const SkString& name : FLAGS_sources) {
431         Source* source = &sources.push_back();
432 
433         if (skiagm::GMFactory* factory = gm_factories.find(name)) {
434             std::shared_ptr<skiagm::GM> gm{(*factory)()};
435             source->name = name;
436             init(source, std::move(gm));
437             continue;
438         }
439 
440         if (const skiatest::Test** test = tests.find(name)) {
441             source->name = name;
442             init(source, **test);
443             continue;
444         }
445 
446         if (sk_sp<SkData> blob = SkData::MakeFromFileName(name.c_str())) {
447             source->name = SkOSPath::Basename(name.c_str());
448 
449             if (name.endsWith(".skp")) {
450                 if (sk_sp<SkPicture> pic = SkPicture::MakeFromData(blob.get())) {
451                     init(source, pic);
452                     continue;
453                 }
454             } else if (name.endsWith(".svg")) {
455                 SkMemoryStream stream{blob};
456                 if (sk_sp<SkSVGDOM> svg = SkSVGDOM::MakeFromStream(stream)) {
457                     init(source, svg);
458                     continue;
459                 }
460             }
461 #if defined(SK_ENABLE_SKOTTIE)
462             else if (name.endsWith(".json")) {
463                 const SkString dir  = SkOSPath::Dirname(name.c_str());
464                 if (sk_sp<skottie::Animation> animation = skottie::Animation::Builder()
465                         .setResourceProvider(skresources::FileResourceProvider::Make(dir))
466                         .make((const char*)blob->data(), blob->size())) {
467                     init(source, animation);
468                     continue;
469                 }
470             }
471 #endif
472             else if (std::shared_ptr<SkCodec> codec = SkCodec::MakeFromData(blob)) {
473                 init(source, codec);
474                 continue;
475             }
476         }
477 
478         fprintf(stderr, "Don't understand source '%s'... bailing out.\n", name.c_str());
479         return 1;
480     }
481 
482     enum NonGpuBackends {
483         kCPU_Backend = -1,
484         kSKP_Backend = -2,
485         kPDF_Backend = -3,
486     };
487     const FlagOption<int> kBackends[] = {
488         { "cpu"            , kCPU_Backend },
489         { "skp"            , kSKP_Backend },
490         { "pdf"            , kPDF_Backend },
491         { "gl"             , GrContextFactory::kGL_ContextType },
492         { "gles"           , GrContextFactory::kGLES_ContextType },
493         { "angle_d3d9_es2" , GrContextFactory::kANGLE_D3D9_ES2_ContextType },
494         { "angle_d3d11_es2", GrContextFactory::kANGLE_D3D11_ES2_ContextType },
495         { "angle_d3d11_es3", GrContextFactory::kANGLE_D3D11_ES3_ContextType },
496         { "angle_gl_es2"   , GrContextFactory::kANGLE_GL_ES2_ContextType },
497         { "angle_gl_es3"   , GrContextFactory::kANGLE_GL_ES3_ContextType },
498         { "commandbuffer"  , GrContextFactory::kCommandBuffer_ContextType },
499         { "vk"             , GrContextFactory::kVulkan_ContextType },
500         { "mtl"            , GrContextFactory::kMetal_ContextType },
501         { "mock"           , GrContextFactory::kMock_ContextType },
502     };
503     const FlagOption<SkColorType> kColorTypes[] = {
504         { "a8",               kAlpha_8_SkColorType },
505         { "g8",                kGray_8_SkColorType },
506         { "565",              kRGB_565_SkColorType },
507         { "4444",           kARGB_4444_SkColorType },
508         { "8888",                 kN32_SkColorType },
509         { "888x",            kRGB_888x_SkColorType },
510         { "1010102",     kRGBA_1010102_SkColorType },
511         { "101010x",      kRGB_101010x_SkColorType },
512         { "bgra1010102", kBGRA_1010102_SkColorType },
513         { "bgr101010x",   kBGR_101010x_SkColorType },
514         { "f16norm",     kRGBA_F16Norm_SkColorType },
515         { "f16",             kRGBA_F16_SkColorType },
516         { "f32",             kRGBA_F32_SkColorType },
517         { "rgba",           kRGBA_8888_SkColorType },
518         { "bgra",           kBGRA_8888_SkColorType },
519     };
520     const FlagOption<SkAlphaType> kAlphaTypes[] = {
521         {   "premul",   kPremul_SkAlphaType },
522         { "unpremul", kUnpremul_SkAlphaType },
523     };
524     const FlagOption<skcms_Matrix3x3> kGamuts[] = {
525         { "srgb",    SkNamedGamut::kSRGB },
526         { "p3",      SkNamedGamut::kDisplayP3 },
527         { "rec2020", SkNamedGamut::kRec2020 },
528         { "adobe",   SkNamedGamut::kAdobeRGB },
529         { "narrow",  gNarrow_toXYZD50},
530     };
531     const FlagOption<skcms_TransferFunction> kTransferFunctions[] = {
532         { "srgb"   , SkNamedTransferFn::kSRGB },
533         { "rec2020", SkNamedTransferFn::kRec2020 },
534         { "2.2"    , SkNamedTransferFn::k2Dot2 },
535         { "linear" , SkNamedTransferFn::kLinear },
536     };
537 
538 
539     int                      backend;
540     SkColorType              ct;
541     SkAlphaType              at;
542     skcms_Matrix3x3          gamut;
543     skcms_TransferFunction   tf;
544 
545     if (!parse_flag(FLAGS_backend, "backend", kBackends         , &backend) ||
546         !parse_flag(FLAGS_ct     , "ct"     , kColorTypes       , &ct)      ||
547         !parse_flag(FLAGS_at     , "at"     , kAlphaTypes       , &at)      ||
548         !parse_flag(FLAGS_gamut  , "gamut"  , kGamuts           , &gamut)   ||
549         !parse_flag(FLAGS_tf     , "tf"     , kTransferFunctions, &tf)) {
550         return 1;
551     }
552 
553     sk_sp<SkColorSpace> cs = FLAGS_legacy ? nullptr
554                                           : SkColorSpace::MakeRGB(tf,gamut);
555     const SkImageInfo unsized_info = SkImageInfo::Make(0,0, ct,at,cs);
556 
557     AutoreleasePool pool;
558     for (auto source : sources) {
559         const auto start = std::chrono::steady_clock::now();
560         fprintf(stdout, "%50s", source.name.c_str());
561         fflush(stdout);
562 
563         const SkImageInfo info = unsized_info.makeDimensions(source.size);
564 
565         auto draw = [&source](SkCanvas* canvas) {
566             Result result = source.draw(canvas);
567             switch (result.status) {
568                 case Result::Ok:   break;
569                 case Result::Skip: return false;
570                 case Result::Fail:
571                     SK_ABORT(result.failure.c_str());
572             }
573             return true;
574         };
575 
576         GrContextOptions options = baseOptions;
577         source.tweak(&options);
578         GrContextFactory factory(options);  // N.B. factory must outlive image
579 
580         sk_sp<SkImage> image;
581         sk_sp<SkData>  blob;
582         const char*    ext = ".png";
583         switch (backend) {
584             case kCPU_Backend:
585                 image = draw_with_cpu(draw, info);
586                 break;
587             case kSKP_Backend:
588                 blob = draw_as_skp(draw, info);
589                 ext  = ".skp";
590                 break;
591             case kPDF_Backend:
592                 blob = draw_as_pdf(draw, info, source.name);
593                 ext  = ".pdf";
594                 break;
595             default:
596                 image = draw_with_gpu(draw, info, (GrContextFactory::ContextType)backend, &factory);
597                 break;
598         }
599 
600         if (!image && !blob) {
601             fprintf(stdout, "\tskipped\n");
602             continue;
603         }
604 
605         // We read back a bitmap even when --quick is set and we won't use it,
606         // to keep us honest about deferred work, flushing pipelines, etc.
607         SkBitmap bitmap;
608         if (image && !image->asLegacyBitmap(&bitmap)) {
609             SK_ABORT("SkImage::asLegacyBitmap() failed.");
610         }
611 
612         SkString md5;
613         if (!FLAGS_quick) {
614             HashAndEncode hashAndEncode{bitmap};
615             {
616                 SkMD5 hash;
617                 if (image) {
618                     hashAndEncode.write(&hash);
619                 } else {
620                     hash.write(blob->data(), blob->size());
621                 }
622 
623                 SkMD5::Digest digest = hash.finish();
624                 for (int i = 0; i < 16; i++) {
625                     md5.appendf("%02x", digest.data[i]);
626                 }
627             }
628 
629             if (!FLAGS_writePath.isEmpty()) {
630                 sk_mkdir(FLAGS_writePath[0]);
631                 SkString path = SkStringPrintf("%s/%s%s",
632                                                FLAGS_writePath[0], source.name.c_str(), ext);
633 
634                 if (image) {
635                     if (!hashAndEncode.writePngTo(path.c_str(), md5.c_str(),
636                                                   FLAGS_key, FLAGS_properties)) {
637                         SK_ABORT("Could not write .png.");
638                     }
639                 } else {
640                     SkFILEWStream file(path.c_str());
641                     file.write(blob->data(), blob->size());
642                 }
643             }
644         }
645 
646         const auto elapsed = std::chrono::steady_clock::now() - start;
647         fprintf(stdout, "\t%s\t%7dms\n",
648                 md5.c_str(),
649                 (int)std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count());
650         pool.drain();
651     }
652 
653     if (!FLAGS_writeShaders.isEmpty()) {
654         sk_mkdir(FLAGS_writeShaders[0]);
655         GrBackendApi api =
656                 GrContextFactory::ContextTypeBackend((GrContextFactory::ContextType)backend);
657         memoryCache.writeShadersToDisk(FLAGS_writeShaders[0], api);
658 
659     }
660 
661     return 0;
662 }
663