blob: c9ab87032f01156a2899d862b969e46f65429111 [file] [log] [blame]
Mike Klein735c7ba2019-03-25 12:57:58 -05001// 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 "CommandLineFlags.h"
5#include "CommonFlags.h"
6#include "EventTracingPriv.h"
7#include "GrContextFactory.h"
8#include "GrContextOptions.h"
9#include "GrContextPriv.h"
10#include "GrGpu.h"
11#include "HashAndEncode.h"
12#include "SkCodec.h"
13#include "SkColorSpace.h"
14#include "SkColorSpacePriv.h"
15#include "SkGraphics.h"
16#include "SkMD5.h"
17#include "SkOSFile.h"
18#include "SkOSPath.h"
Mike Kleinc245bd92019-03-27 14:31:09 -050019#include "SkPDFDocument.h"
Mike Klein735c7ba2019-03-25 12:57:58 -050020#include "SkPicture.h"
Mike Klein9b462092019-03-27 13:52:35 -050021#include "SkPictureRecorder.h"
Mike Klein6253d902019-03-27 15:09:12 -050022#include "SkSVGDOM.h"
Mike Klein735c7ba2019-03-25 12:57:58 -050023#include "ToolUtils.h"
24#include "gm.h"
25#include <chrono>
26#include <functional>
27#include <stdio.h>
28#include <stdlib.h>
29
30using sk_gpu_test::GrContextFactory;
31
32static DEFINE_string2(sources, s, "", "Which GMs, .skps, or images to draw.");
33static DEFINE_string2(backend, b, "", "Backend used to create a canvas to draw into.");
34
35static DEFINE_string(ct , "8888", "The color type for any raster backend.");
36static DEFINE_string(at , "premul", "The alpha type for any raster backend.");
37static DEFINE_string(gamut, "srgb", "The color gamut for any raster backend.");
38static DEFINE_string(tf , "srgb", "The transfer function for any raster backend.");
39
40static DEFINE_int (samples , 0, "Samples per pixel in GPU backends.");
41static DEFINE_bool (nvpr , false, "Use NV_path_rendering in GPU backends?");
42static DEFINE_bool (stencils, true, "If false, avoid stencil buffers in GPU backends.");
43static DEFINE_bool (dit , false, "Use device-independent text in GPU backends.");
44static DEFINE_string(surf , "default", "Backing store for GPU backend surfaces.");
45
46static DEFINE_bool( preAbandonGpuContext, false, "Abandon the GrContext before drawing.");
47static DEFINE_bool( abandonGpuContext, false, "Abandon the GrContext after drawing.");
48static DEFINE_bool(releaseAndAbandonGpuContext, false,
49 "Release all GPU resources and abandon the GrContext after drawing.");
50
51static DEFINE_bool(decodeToDst, false,
52 "Decode images to destination format rather than suggested natural format.");
53
Mike Kleinc245bd92019-03-27 14:31:09 -050054static DEFINE_double(rasterDPI, SK_ScalarDefaultRasterDPI,
55 "DPI for rasterized content in vector backends like --backend pdf.");
56static DEFINE_bool(PDFA, false, "Create PDF/A with --backend pdf?");
57
Mike Klein735c7ba2019-03-25 12:57:58 -050058static DEFINE_bool (cpuDetect, true, "Detect CPU features for runtime optimizations?");
59static DEFINE_string2(writePath, w, "", "Write .pngs to this directory if set.");
60static DEFINE_bool2 (verbose, v, false, "Print progress to stdout.");
61
62static DEFINE_string(key, "", "Metadata passed through to .png encoder and .json output.");
63static DEFINE_string(parameters, "", "Metadata passed through to .png encoder and .json output.");
64
65template <typename T>
66struct FlagOption {
67 const char* label;
68 T value;
69};
70
71template <typename T, int N>
72static bool parse_flag(const CommandLineFlags::StringArray& flag,
73 const char* flag_name,
74 const FlagOption<T> (&array)[N],
75 T* value) {
76 for (auto entry : array) {
77 if (flag.contains(entry.label)) {
78 *value = entry.value;
79 return true;
80 }
81 }
82 fprintf(stderr, "Known values for --%s:\n", flag_name);
83 for (auto entry : array) {
84 fprintf(stderr, " --%s %s\n", flag_name, entry.label);
85 }
86 return false;
87}
88
89static void exit_with_failure() {
90 // TODO: dump stack trace, debug trap, print currently running job, etc?
91 exit(1);
92}
93
94struct Source {
95 SkString name;
96 SkISize size;
97 std::function<void(SkCanvas*)> draw;
98 std::function<void(GrContextOptions*)> tweak;
99};
100
101static Source gm_source(std::shared_ptr<skiagm::GM> gm) {
102 return {
103 SkString{gm->getName()},
104 gm->getISize(),
105 [gm](SkCanvas* canvas) {
106 SkString err;
107 if (skiagm::DrawResult::kFail == gm->draw(canvas, &err)) {
108 fprintf(stderr, "Drawing GM %s failed: %s\n",
109 gm->getName(), err.c_str());
110 exit_with_failure();
111 }
112 },
113 [gm](GrContextOptions* options) { gm->modifyGrContextOptions(options); },
114 };
115}
116
117static Source picture_source(SkString name, sk_sp<SkPicture> pic) {
118 return {
119 name,
120 pic->cullRect().roundOut().size(),
121 [pic](SkCanvas* canvas) {
122 canvas->drawPicture(pic);
123 },
124 [](GrContextOptions*) {},
125 };
126}
127
128static Source codec_source(SkString name, std::shared_ptr<SkCodec> codec) {
129 return {
130 name,
131 codec->dimensions(),
132 [codec](SkCanvas* canvas) {
133 SkImageInfo info = codec->getInfo();
134 if (FLAGS_decodeToDst) {
135 info = canvas->imageInfo().makeWH(info.width(),
136 info.height());
137 }
138
139 SkBitmap bm;
140 bm.allocPixels(info);
141
142 switch (auto result = codec->getPixels(info, bm.getPixels(), bm.rowBytes())) {
143 case SkCodec::kSuccess:
144 case SkCodec::kErrorInInput:
145 case SkCodec::kIncompleteInput:
146 canvas->drawBitmap(bm, 0,0);
147 break;
148 default:
149 fprintf(stderr, "SkCodec::getPixels failed: %d.", result);
150 exit_with_failure();
151 }
152 },
153 [](GrContextOptions*) {},
154 };
155}
156
Mike Klein6253d902019-03-27 15:09:12 -0500157static Source svg_source(SkString name, sk_sp<SkSVGDOM> svg) {
158 return {
159 name,
160 svg->containerSize().isEmpty() ? SkISize{1000,1000}
161 : svg->containerSize().toCeil(),
162 [svg](SkCanvas* canvas) { svg->render(canvas); },
163 [](GrContextOptions*) {},
164 };
165}
166
167
Mike Klein735c7ba2019-03-25 12:57:58 -0500168static sk_sp<SkImage> draw_with_cpu(std::function<void(SkCanvas*)> draw,
169 SkImageInfo info) {
170 if (sk_sp<SkSurface> surface = SkSurface::MakeRaster(info)) {
171 draw(surface->getCanvas());
172 return surface->makeImageSnapshot();
173 }
174 return nullptr;
175}
176
Mike Klein9b462092019-03-27 13:52:35 -0500177static sk_sp<SkData> draw_as_skp(std::function<void(SkCanvas*)> draw,
178 SkImageInfo info) {
179 SkPictureRecorder recorder;
180 draw(recorder.beginRecording(info.width(), info.height()));
181 return recorder.finishRecordingAsPicture()->serialize();
182}
183
Mike Kleinc245bd92019-03-27 14:31:09 -0500184static sk_sp<SkData> draw_as_pdf(std::function<void(SkCanvas*)> draw,
185 SkImageInfo info,
186 SkString name) {
187 SkPDF::Metadata metadata;
188 metadata.fTitle = name;
189 metadata.fCreator = "Skia/FM";
190 metadata.fRasterDPI = FLAGS_rasterDPI;
191 metadata.fPDFA = FLAGS_PDFA;
192
193 SkDynamicMemoryWStream stream;
194 if (sk_sp<SkDocument> doc = SkPDF::MakeDocument(&stream, metadata)) {
195 draw(doc->beginPage(info.width(), info.height()));
196 doc->endPage();
197 doc->close();
198 return stream.detachAsData();
199 }
200 return nullptr;
201}
202
Mike Klein735c7ba2019-03-25 12:57:58 -0500203static sk_sp<SkImage> draw_with_gpu(std::function<void(SkCanvas*)> draw,
204 SkImageInfo info,
205 GrContextFactory::ContextType api,
206 GrContextFactory* factory) {
207 enum class SurfaceType { kDefault, kBackendTexture, kBackendRenderTarget };
208 const FlagOption<SurfaceType> kSurfaceTypes[] = {
209 { "default", SurfaceType::kDefault },
210 { "betex" , SurfaceType::kBackendTexture },
211 { "bert" , SurfaceType::kBackendRenderTarget },
212 };
213 SurfaceType surfaceType;
214 if (!parse_flag(FLAGS_surf, "surf", kSurfaceTypes, &surfaceType)) {
215 return nullptr;
216 }
217
218 auto overrides = FLAGS_nvpr ? GrContextFactory::ContextOverrides::kRequireNVPRSupport
219 : GrContextFactory::ContextOverrides::kDisableNVPR;
220 if (!FLAGS_stencils) { overrides |= GrContextFactory::ContextOverrides::kAvoidStencilBuffers; }
221
222 GrContext* context = factory->getContextInfo(api, overrides)
223 .grContext();
224
225 uint32_t flags = FLAGS_dit ? SkSurfaceProps::kUseDeviceIndependentFonts_Flag
226 : 0;
227 SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType);
228
229 sk_sp<SkSurface> surface;
230 GrBackendTexture backendTexture;
231 GrBackendRenderTarget backendRT;
232
233 switch (surfaceType) {
234 case SurfaceType::kDefault:
235 surface = SkSurface::MakeRenderTarget(context,
236 SkBudgeted::kNo,
237 info,
238 FLAGS_samples,
239 &props);
240 break;
241
242 case SurfaceType::kBackendTexture:
243 backendTexture = context->priv().getGpu()
244 ->createTestingOnlyBackendTexture(nullptr,
245 info.width(),
246 info.height(),
247 info.colorType(),
248 true,
249 GrMipMapped::kNo);
250 surface = SkSurface::MakeFromBackendTexture(context,
251 backendTexture,
252 kTopLeft_GrSurfaceOrigin,
253 FLAGS_samples,
254 info.colorType(),
255 info.refColorSpace(),
256 &props);
257 break;
258
259 case SurfaceType::kBackendRenderTarget:
260 backendRT = context->priv().getGpu()
261 ->createTestingOnlyBackendRenderTarget(info.width(),
262 info.height(),
263 SkColorTypeToGrColorType(info.colorType()));
264 surface = SkSurface::MakeFromBackendRenderTarget(context,
265 backendRT,
266 kBottomLeft_GrSurfaceOrigin,
267 info.colorType(),
268 info.refColorSpace(),
269 &props);
270 break;
271 }
272
273 if (!surface) {
274 fprintf(stderr, "Could not create GPU surface.\n");
275 return nullptr;
276 }
277
278 if (FLAGS_preAbandonGpuContext) {
279 factory->abandonContexts();
280 }
281
282 draw(surface->getCanvas());
283 sk_sp<SkImage> image = surface->makeImageSnapshot();
284
285 if (FLAGS_abandonGpuContext) {
286 factory->abandonContexts();
287 } else if (FLAGS_releaseAndAbandonGpuContext) {
288 factory->releaseResourcesAndAbandonContexts();
289 }
290
291 if (!context->abandoned()) {
292 surface.reset();
293 if (backendTexture.isValid()) {
294 context->priv().getGpu()->deleteTestingOnlyBackendTexture(backendTexture);
295 }
296 if (backendRT.isValid()) {
297 context->priv().getGpu()->deleteTestingOnlyBackendRenderTarget(backendRT);
298 }
299 }
300
301 return image;
302}
303
304int main(int argc, char** argv) {
305 CommandLineFlags::Parse(argc, argv);
306
307 if (FLAGS_cpuDetect) {
308 SkGraphics::Init();
309 }
310 initializeEventTracingForTools();
311 ToolUtils::SetDefaultFontMgr();
312 SetAnalyticAAFromCommonFlags();
313
314 GrContextOptions baseOptions;
315 SetCtxOptionsFromCommonFlags(&baseOptions);
316
317
318 SkTArray<Source> sources;
319 for (skiagm::GMFactory factory : skiagm::GMRegistry::Range()) {
320 std::shared_ptr<skiagm::GM> gm{factory(nullptr)};
321
322 if (FLAGS_sources.isEmpty()) {
323 fprintf(stdout, "%s\n", gm->getName());
324 } else if (FLAGS_sources.contains(gm->getName())) {
325 sources.push_back(gm_source(gm));
326 }
327 }
328 for (const SkString& source : FLAGS_sources) {
329 if (sk_sp<SkData> blob = SkData::MakeFromFileName(source.c_str())) {
330 const SkString name = SkOSPath::Basename(source.c_str());
331
Mike Kleince90d6f2019-03-29 11:14:14 -0500332 if (name.endsWith(".skp")) {
333 if (sk_sp<SkPicture> pic = SkPicture::MakeFromData(blob.get())) {
334 sources.push_back(picture_source(name, pic));
335 }
336 } else if (name.endsWith(".svg")) {
337 SkMemoryStream stream{blob};
338 if (sk_sp<SkSVGDOM> svg = SkSVGDOM::MakeFromStream(stream)) {
339 sources.push_back(svg_source(name, svg));
340 }
341 } else if (std::shared_ptr<SkCodec> codec = SkCodec::MakeFromData(blob)) {
Mike Klein735c7ba2019-03-25 12:57:58 -0500342 sources.push_back(codec_source(name, codec));
343 }
344 }
345 }
346 if (sources.empty()) {
347 return 0;
348 }
349
Mike Klein9b462092019-03-27 13:52:35 -0500350 enum NonGpuBackends {
351 kCPU_Backend = -1,
352 kSKP_Backend = -2,
Mike Kleinc245bd92019-03-27 14:31:09 -0500353 kPDF_Backend = -3,
Mike Klein9b462092019-03-27 13:52:35 -0500354 };
355 const FlagOption<int> kBackends[] = {
356 { "cpu" , kCPU_Backend },
357 { "skp" , kSKP_Backend },
Mike Kleinc245bd92019-03-27 14:31:09 -0500358 { "pdf" , kPDF_Backend },
Mike Klein9b462092019-03-27 13:52:35 -0500359 { "gl" , GrContextFactory::kGL_ContextType },
360 { "gles" , GrContextFactory::kGLES_ContextType },
361 { "angle_d3d9_es2" , GrContextFactory::kANGLE_D3D9_ES2_ContextType },
362 { "angle_d3d11_es2", GrContextFactory::kANGLE_D3D11_ES2_ContextType },
363 { "angle_d3d11_es3", GrContextFactory::kANGLE_D3D11_ES3_ContextType },
364 { "angle_gl_es2" , GrContextFactory::kANGLE_GL_ES2_ContextType },
365 { "angle_gl_es3" , GrContextFactory::kANGLE_GL_ES3_ContextType },
366 { "commandbuffer" , GrContextFactory::kCommandBuffer_ContextType },
367 { "vk" , GrContextFactory::kVulkan_ContextType },
368 { "mtl" , GrContextFactory::kMetal_ContextType },
369 { "mock" , GrContextFactory::kMock_ContextType },
370 };
Mike Klein735c7ba2019-03-25 12:57:58 -0500371 const FlagOption<SkColorType> kColorTypes[] = {
372 { "a8", kAlpha_8_SkColorType },
373 { "g8", kGray_8_SkColorType },
374 { "565", kRGB_565_SkColorType },
375 { "4444", kARGB_4444_SkColorType },
376 { "8888", kN32_SkColorType },
377 { "888x", kRGB_888x_SkColorType },
378 { "1010102", kRGBA_1010102_SkColorType },
379 { "101010x", kRGB_101010x_SkColorType },
380 { "f16norm", kRGBA_F16Norm_SkColorType },
381 { "f16", kRGBA_F16_SkColorType },
382 { "f32", kRGBA_F32_SkColorType },
383 { "rgba", kRGBA_8888_SkColorType },
384 { "bgra", kBGRA_8888_SkColorType },
385 };
386 const FlagOption<SkAlphaType> kAlphaTypes[] = {
387 { "premul", kPremul_SkAlphaType },
388 { "unpremul", kUnpremul_SkAlphaType },
389 };
390 const FlagOption<skcms_Matrix3x3> kGamuts[] = {
391 { "srgb", SkNamedGamut::kSRGB },
392 { "p3", SkNamedGamut::kDCIP3 },
393 { "rec2020", SkNamedGamut::kRec2020 },
394 { "adobe", SkNamedGamut::kAdobeRGB },
395 { "narrow", gNarrow_toXYZD50},
396 };
397 const FlagOption<skcms_TransferFunction> kTransferFunctions[] = {
398 { "srgb" , SkNamedTransferFn::kSRGB },
399 { "rec2020", {2.22222f, 0.909672f, 0.0903276f, 0.222222f, 0.0812429f, 0, 0} },
400 { "2.2" , SkNamedTransferFn::k2Dot2 },
401 { "linear" , SkNamedTransferFn::kLinear },
402 };
403
Mike Klein735c7ba2019-03-25 12:57:58 -0500404
Mike Klein9b462092019-03-27 13:52:35 -0500405 int backend;
Mike Klein735c7ba2019-03-25 12:57:58 -0500406 SkColorType ct;
407 SkAlphaType at;
408 skcms_Matrix3x3 gamut;
409 skcms_TransferFunction tf;
Mike Klein735c7ba2019-03-25 12:57:58 -0500410
Mike Klein9b462092019-03-27 13:52:35 -0500411 if (!parse_flag(FLAGS_backend, "backend", kBackends , &backend) ||
412 !parse_flag(FLAGS_ct , "ct" , kColorTypes , &ct) ||
Mike Klein735c7ba2019-03-25 12:57:58 -0500413 !parse_flag(FLAGS_at , "at" , kAlphaTypes , &at) ||
414 !parse_flag(FLAGS_gamut , "gamut" , kGamuts , &gamut) ||
Mike Klein9b462092019-03-27 13:52:35 -0500415 !parse_flag(FLAGS_tf , "tf" , kTransferFunctions, &tf)) {
Mike Klein735c7ba2019-03-25 12:57:58 -0500416 return 1;
417 }
418
419 const SkImageInfo unsized_info = SkImageInfo::Make(0,0, ct,at, SkColorSpace::MakeRGB(tf,gamut));
420
421 for (auto source : sources) {
422 const auto start = std::chrono::steady_clock::now();
423 if (FLAGS_verbose) {
424 fprintf(stdout, "%50s", source.name.c_str());
425 }
426
427 const SkImageInfo info = unsized_info.makeWH(source.size.width(),
428 source.size.height());
429
430 GrContextOptions options = baseOptions;
431 source.tweak(&options);
432 GrContextFactory factory(options); // N.B. factory must outlive image
433
434 sk_sp<SkImage> image;
Mike Klein9b462092019-03-27 13:52:35 -0500435 sk_sp<SkData> blob;
436 const char* ext = ".png";
Mike Klein735c7ba2019-03-25 12:57:58 -0500437 switch (backend) {
438 case kCPU_Backend:
439 image = draw_with_cpu(source.draw, info);
440 break;
Mike Klein9b462092019-03-27 13:52:35 -0500441 case kSKP_Backend:
442 blob = draw_as_skp(source.draw, info);
443 ext = ".skp";
444 break;
Mike Kleinc245bd92019-03-27 14:31:09 -0500445 case kPDF_Backend:
446 blob = draw_as_pdf(source.draw, info, source.name);
447 ext = ".pdf";
448 break;
Mike Klein735c7ba2019-03-25 12:57:58 -0500449 default:
450 image = draw_with_gpu(source.draw, info,
451 (GrContextFactory::ContextType)backend, &factory);
452 break;
453 }
454
Mike Klein9b462092019-03-27 13:52:35 -0500455 if (!image && !blob) {
456 fprintf(stderr, "FM backend returned a no image or data blob.\n");
Mike Klein735c7ba2019-03-25 12:57:58 -0500457 exit_with_failure();
458 }
459
460 SkBitmap bitmap;
Mike Klein9b462092019-03-27 13:52:35 -0500461 if (image && !image->asLegacyBitmap(&bitmap)) {
Mike Klein735c7ba2019-03-25 12:57:58 -0500462 fprintf(stderr, "SkImage::asLegacyBitmap() failed.\n");
463 exit_with_failure();
464 }
465
466 HashAndEncode hashAndEncode{bitmap};
467 SkString md5;
468 {
469 SkMD5 hash;
Mike Klein9b462092019-03-27 13:52:35 -0500470 if (image) {
471 hashAndEncode.write(&hash);
472 } else {
473 hash.write(blob->data(), blob->size());
474 }
Mike Klein735c7ba2019-03-25 12:57:58 -0500475
476 SkMD5::Digest digest;
477 hash.finish(digest);
478 for (int i = 0; i < 16; i++) {
479 md5.appendf("%02x", digest.data[i]);
480 }
481 }
482
483 if (!FLAGS_writePath.isEmpty()) {
484 sk_mkdir(FLAGS_writePath[0]);
Mike Klein9b462092019-03-27 13:52:35 -0500485 SkString path = SkStringPrintf("%s/%s%s", FLAGS_writePath[0], source.name.c_str(), ext);
Mike Klein735c7ba2019-03-25 12:57:58 -0500486
Mike Klein9b462092019-03-27 13:52:35 -0500487 if (image) {
488 if (!hashAndEncode.writePngTo(path.c_str(), md5.c_str(),
489 FLAGS_key, FLAGS_parameters)) {
490 fprintf(stderr, "Could not write to %s.\n", path.c_str());
491 exit_with_failure();
492 }
493 } else {
494 SkFILEWStream file(path.c_str());
495 file.write(blob->data(), blob->size());
Mike Klein735c7ba2019-03-25 12:57:58 -0500496 }
Mike Klein735c7ba2019-03-25 12:57:58 -0500497 }
498
499 if (FLAGS_verbose) {
500 const auto elapsed = std::chrono::steady_clock::now() - start;
501 fprintf(stdout, "\t%s\t%7dms\n",
502 md5.c_str(),
503 (int)std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count());
504 }
505 }
506
507 return 0;
508}