1/*
2 * Copyright 2014 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 <ctype.h>
9
10#include "nanobench.h"
11
12#include "AndroidCodecBench.h"
13#include "Benchmark.h"
14#include "BitmapRegionDecoderBench.h"
15#include "CodecBench.h"
16#include "CodecBenchPriv.h"
17#include "ColorCodecBench.h"
18#include "CrashHandler.h"
19#include "GMBench.h"
20#include "ProcStats.h"
21#include "RecordingBench.h"
22#include "ResultsWriter.h"
23#include "SKPAnimationBench.h"
24#include "SKPBench.h"
25#include "SkAndroidCodec.h"
26#include "SkAutoMalloc.h"
27#include "SkBBoxHierarchy.h"
28#include "SkBitmapRegionDecoder.h"
29#include "SkCanvas.h"
30#include "SkCodec.h"
31#include "SkCommonFlags.h"
32#include "SkCommonFlagsConfig.h"
33#include "SkCommonFlagsGpu.h"
34#include "SkData.h"
35#include "SkDebugfTracer.h"
36#include "SkEventTracingPriv.h"
37#include "SkGraphics.h"
38#include "SkLeanWindows.h"
39#include "SkOSFile.h"
40#include "SkOSPath.h"
41#include "SkPictureRecorder.h"
42#include "SkSVGDOM.h"
43#include "SkScan.h"
44#include "SkString.h"
45#include "SkSurface.h"
46#include "SkTaskGroup.h"
47#include "SkTraceEvent.h"
48#include "Stats.h"
49#include "ios_utils.h"
50
51#include <stdlib.h>
52#include <thread>
53
54extern bool gSkForceRasterPipelineBlitter;
55
56#ifndef SK_BUILD_FOR_WIN
57    #include <unistd.h>
58#endif
59
60#if SK_SUPPORT_GPU
61    #include "gl/GrGLDefines.h"
62    #include "GrCaps.h"
63    #include "GrContextFactory.h"
64    #include "gl/GrGLUtil.h"
65    #include "SkGr.h"
66    using sk_gpu_test::ContextInfo;
67    using sk_gpu_test::GrContextFactory;
68    using sk_gpu_test::TestContext;
69    GrContextOptions grContextOpts;
70#endif
71
72    struct GrContextOptions;
73
74static const int kAutoTuneLoops = 0;
75
76#if !defined(__has_feature)
77    #define  __has_feature(x) 0
78#endif
79
80static const int kDefaultLoops =
81#if defined(SK_DEBUG) || __has_feature(address_sanitizer)
82    1;
83#else
84    kAutoTuneLoops;
85#endif
86
87static SkString loops_help_txt() {
88    SkString help;
89    help.printf("Number of times to run each bench. Set this to %d to auto-"
90                "tune for each bench. Timings are only reported when auto-tuning.",
91                kAutoTuneLoops);
92    return help;
93}
94
95static SkString to_string(int n) {
96    SkString str;
97    str.appendS32(n);
98    return str;
99}
100
101DECLARE_bool(undefok);
102
103DEFINE_int32(loops, kDefaultLoops, loops_help_txt().c_str());
104
105DEFINE_int32(samples, 10, "Number of samples to measure for each bench.");
106DEFINE_int32(ms, 0, "If >0, run each bench for this many ms instead of obeying --samples.");
107DEFINE_int32(overheadLoops, 100000, "Loops to estimate timer overhead.");
108DEFINE_double(overheadGoal, 0.0001,
109              "Loop until timer overhead is at most this fraction of our measurments.");
110DEFINE_double(gpuMs, 5, "Target bench time in millseconds for GPU.");
111DEFINE_int32(gpuFrameLag, 5, "If unknown, estimated maximum number of frames GPU allows to lag.");
112
113DEFINE_string(outResultsFile, "", "If given, write results here as JSON.");
114DEFINE_int32(maxCalibrationAttempts, 3,
115             "Try up to this many times to guess loops for a bench, or skip the bench.");
116DEFINE_int32(maxLoops, 1000000, "Never run a bench more times than this.");
117DEFINE_string(clip, "0,0,1000,1000", "Clip for SKPs.");
118DEFINE_string(scales, "1.0", "Space-separated scales for SKPs.");
119DEFINE_string(zoom, "1.0,0", "Comma-separated zoomMax,zoomPeriodMs factors for a periodic SKP zoom "
120                             "function that ping-pongs between 1.0 and zoomMax.");
121DEFINE_bool(bbh, true, "Build a BBH for SKPs?");
122DEFINE_bool(lite, false, "Use SkLiteRecorder in recording benchmarks?");
123DEFINE_bool(mpd, true, "Use MultiPictureDraw for the SKPs?");
124DEFINE_bool(loopSKP, true, "Loop SKPs like we do for micro benches?");
125DEFINE_int32(flushEvery, 10, "Flush --outResultsFile every Nth run.");
126DEFINE_bool(gpuStats, false, "Print GPU stats after each gpu benchmark?");
127DEFINE_bool(gpuStatsDump, false, "Dump GPU states after each benchmark to json");
128DEFINE_bool(keepAlive, false, "Print a message every so often so that we don't time out");
129DEFINE_bool(csv, false, "Print status in CSV format");
130DEFINE_string(sourceType, "",
131        "Apply usual --match rules to source type: bench, gm, skp, image, etc.");
132DEFINE_string(benchType,  "",
133        "Apply usual --match rules to bench type: micro, recording, piping, playback, skcodec, etc.");
134
135DEFINE_bool(forceRasterPipeline, false, "sets gSkForceRasterPipelineBlitter");
136
137static double now_ms() { return SkTime::GetNSecs() * 1e-6; }
138
139static SkString humanize(double ms) {
140    if (FLAGS_verbose) return SkStringPrintf("%llu", (uint64_t)(ms*1e6));
141    return HumanizeMs(ms);
142}
143#define HUMANIZE(ms) humanize(ms).c_str()
144
145bool Target::init(SkImageInfo info, Benchmark* bench) {
146    if (Benchmark::kRaster_Backend == config.backend) {
147        this->surface = SkSurface::MakeRaster(info);
148        if (!this->surface) {
149            return false;
150        }
151    }
152    return true;
153}
154bool Target::capturePixels(SkBitmap* bmp) {
155    SkCanvas* canvas = this->getCanvas();
156    if (!canvas) {
157        return false;
158    }
159    bmp->allocPixels(canvas->imageInfo());
160    if (!canvas->readPixels(*bmp, 0, 0)) {
161        SkDebugf("Can't read canvas pixels.\n");
162        return false;
163    }
164    return true;
165}
166
167#if SK_SUPPORT_GPU
168struct GPUTarget : public Target {
169    explicit GPUTarget(const Config& c) : Target(c) {}
170    ContextInfo contextInfo;
171    std::unique_ptr<GrContextFactory> factory;
172
173    void setup() override {
174        this->contextInfo.testContext()->makeCurrent();
175        // Make sure we're done with whatever came before.
176        this->contextInfo.testContext()->finish();
177    }
178    void endTiming() override {
179        if (this->contextInfo.testContext()) {
180            this->contextInfo.testContext()->waitOnSyncOrSwap();
181        }
182    }
183    void fence() override { this->contextInfo.testContext()->finish(); }
184
185    bool needsFrameTiming(int* maxFrameLag) const override {
186        if (!this->contextInfo.testContext()->getMaxGpuFrameLag(maxFrameLag)) {
187            // Frame lag is unknown.
188            *maxFrameLag = FLAGS_gpuFrameLag;
189        }
190        return true;
191    }
192    bool init(SkImageInfo info, Benchmark* bench) override {
193        GrContextOptions options = grContextOpts;
194        bench->modifyGrContextOptions(&options);
195        this->factory.reset(new GrContextFactory(options));
196        uint32_t flags = this->config.useDFText ? SkSurfaceProps::kUseDeviceIndependentFonts_Flag :
197                                                  0;
198        SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType);
199        this->surface = SkSurface::MakeRenderTarget(
200                this->factory->get(this->config.ctxType, this->config.ctxOverrides),
201                SkBudgeted::kNo, info, this->config.samples, &props);
202        this->contextInfo =
203                this->factory->getContextInfo(this->config.ctxType, this->config.ctxOverrides);
204        if (!this->surface.get()) {
205            return false;
206        }
207        if (!this->contextInfo.testContext()->fenceSyncSupport()) {
208            SkDebugf("WARNING: GL context for config \"%s\" does not support fence sync. "
209                     "Timings might not be accurate.\n", this->config.name.c_str());
210        }
211        return true;
212    }
213    void fillOptions(ResultsWriter* log) override {
214        const GrGLubyte* version;
215        if (this->contextInfo.backend() == kOpenGL_GrBackend) {
216            const GrGLInterface* gl = reinterpret_cast<const GrGLInterface*>(
217                    this->contextInfo.testContext()->backendContext());
218            GR_GL_CALL_RET(gl, version, GetString(GR_GL_VERSION));
219            log->configOption("GL_VERSION", (const char*)(version));
220
221            GR_GL_CALL_RET(gl, version, GetString(GR_GL_RENDERER));
222            log->configOption("GL_RENDERER", (const char*) version);
223
224            GR_GL_CALL_RET(gl, version, GetString(GR_GL_VENDOR));
225            log->configOption("GL_VENDOR", (const char*) version);
226
227            GR_GL_CALL_RET(gl, version, GetString(GR_GL_SHADING_LANGUAGE_VERSION));
228            log->configOption("GL_SHADING_LANGUAGE_VERSION", (const char*) version);
229        }
230    }
231
232    void dumpStats() override {
233        this->contextInfo.grContext()->printCacheStats();
234        this->contextInfo.grContext()->printGpuStats();
235    }
236};
237
238#endif
239
240static double time(int loops, Benchmark* bench, Target* target) {
241    SkCanvas* canvas = target->getCanvas();
242    if (canvas) {
243        canvas->clear(SK_ColorWHITE);
244    }
245    bench->preDraw(canvas);
246    double start = now_ms();
247    canvas = target->beginTiming(canvas);
248    bench->draw(loops, canvas);
249    if (canvas) {
250        canvas->flush();
251    }
252    target->endTiming();
253    double elapsed = now_ms() - start;
254    bench->postDraw(canvas);
255    return elapsed;
256}
257
258static double estimate_timer_overhead() {
259    double overhead = 0;
260    for (int i = 0; i < FLAGS_overheadLoops; i++) {
261        double start = now_ms();
262        overhead += now_ms() - start;
263    }
264    return overhead / FLAGS_overheadLoops;
265}
266
267static int detect_forever_loops(int loops) {
268    // look for a magic run-forever value
269    if (loops < 0) {
270        loops = SK_MaxS32;
271    }
272    return loops;
273}
274
275static int clamp_loops(int loops) {
276    if (loops < 1) {
277        SkDebugf("ERROR: clamping loops from %d to 1. "
278                 "There's probably something wrong with the bench.\n", loops);
279        return 1;
280    }
281    if (loops > FLAGS_maxLoops) {
282        SkDebugf("WARNING: clamping loops from %d to FLAGS_maxLoops, %d.\n", loops, FLAGS_maxLoops);
283        return FLAGS_maxLoops;
284    }
285    return loops;
286}
287
288static bool write_canvas_png(Target* target, const SkString& filename) {
289
290    if (filename.isEmpty()) {
291        return false;
292    }
293    if (target->getCanvas() &&
294        kUnknown_SkColorType == target->getCanvas()->imageInfo().colorType()) {
295        return false;
296    }
297
298    SkBitmap bmp;
299
300    if (!target->capturePixels(&bmp)) {
301        return false;
302    }
303
304    SkString dir = SkOSPath::Dirname(filename.c_str());
305    if (!sk_mkdir(dir.c_str())) {
306        SkDebugf("Can't make dir %s.\n", dir.c_str());
307        return false;
308    }
309    SkFILEWStream stream(filename.c_str());
310    if (!stream.isValid()) {
311        SkDebugf("Can't write %s.\n", filename.c_str());
312        return false;
313    }
314    if (!SkEncodeImage(&stream, bmp, SkEncodedImageFormat::kPNG, 100)) {
315        SkDebugf("Can't encode a PNG.\n");
316        return false;
317    }
318    return true;
319}
320
321static int kFailedLoops = -2;
322static int setup_cpu_bench(const double overhead, Target* target, Benchmark* bench) {
323    // First figure out approximately how many loops of bench it takes to make overhead negligible.
324    double bench_plus_overhead = 0.0;
325    int round = 0;
326    int loops = bench->calculateLoops(FLAGS_loops);
327    if (kAutoTuneLoops == loops) {
328        while (bench_plus_overhead < overhead) {
329            if (round++ == FLAGS_maxCalibrationAttempts) {
330                SkDebugf("WARNING: Can't estimate loops for %s (%s vs. %s); skipping.\n",
331                         bench->getUniqueName(), HUMANIZE(bench_plus_overhead), HUMANIZE(overhead));
332                return kFailedLoops;
333            }
334            bench_plus_overhead = time(1, bench, target);
335        }
336    }
337
338    // Later we'll just start and stop the timer once but loop N times.
339    // We'll pick N to make timer overhead negligible:
340    //
341    //          overhead
342    //  -------------------------  < FLAGS_overheadGoal
343    //  overhead + N * Bench Time
344    //
345    // where bench_plus_overhead ~=~ overhead + Bench Time.
346    //
347    // Doing some math, we get:
348    //
349    //  (overhead / FLAGS_overheadGoal) - overhead
350    //  ------------------------------------------  < N
351    //       bench_plus_overhead - overhead)
352    //
353    // Luckily, this also works well in practice. :)
354    if (kAutoTuneLoops == loops) {
355        const double numer = overhead / FLAGS_overheadGoal - overhead;
356        const double denom = bench_plus_overhead - overhead;
357        loops = (int)ceil(numer / denom);
358        loops = clamp_loops(loops);
359    } else {
360        loops = detect_forever_loops(loops);
361    }
362
363    return loops;
364}
365
366static int setup_gpu_bench(Target* target, Benchmark* bench, int maxGpuFrameLag) {
367    // First, figure out how many loops it'll take to get a frame up to FLAGS_gpuMs.
368    int loops = bench->calculateLoops(FLAGS_loops);
369    if (kAutoTuneLoops == loops) {
370        loops = 1;
371        double elapsed = 0;
372        do {
373            if (1<<30 == loops) {
374                // We're about to wrap.  Something's wrong with the bench.
375                loops = 0;
376                break;
377            }
378            loops *= 2;
379            // If the GPU lets frames lag at all, we need to make sure we're timing
380            // _this_ round, not still timing last round.
381            for (int i = 0; i < maxGpuFrameLag; i++) {
382                elapsed = time(loops, bench, target);
383            }
384        } while (elapsed < FLAGS_gpuMs);
385
386        // We've overshot at least a little.  Scale back linearly.
387        loops = (int)ceil(loops * FLAGS_gpuMs / elapsed);
388        loops = clamp_loops(loops);
389
390        // Make sure we're not still timing our calibration.
391        target->fence();
392    } else {
393        loops = detect_forever_loops(loops);
394    }
395    // Pretty much the same deal as the calibration: do some warmup to make
396    // sure we're timing steady-state pipelined frames.
397    for (int i = 0; i < maxGpuFrameLag; i++) {
398        time(loops, bench, target);
399    }
400
401    return loops;
402}
403
404#if SK_SUPPORT_GPU
405#define kBogusContextType GrContextFactory::kGL_ContextType
406#define kBogusContextOverrides GrContextFactory::ContextOverrides::kNone
407#else
408#define kBogusContextType 0
409#define kBogusContextOverrides 0
410#endif
411
412static void create_config(const SkCommandLineConfig* config, SkTArray<Config>* configs) {
413
414#if SK_SUPPORT_GPU
415    if (const auto* gpuConfig = config->asConfigGpu()) {
416        if (!FLAGS_gpu) {
417            SkDebugf("Skipping config '%s' as requested.\n", config->getTag().c_str());
418            return;
419        }
420
421        const auto ctxType = gpuConfig->getContextType();
422        const auto ctxOverrides = gpuConfig->getContextOverrides();
423        const auto sampleCount = gpuConfig->getSamples();
424        const auto colorType = gpuConfig->getColorType();
425        auto colorSpace = gpuConfig->getColorSpace();
426
427        GrContextFactory factory(grContextOpts);
428        if (const GrContext* ctx = factory.get(ctxType, ctxOverrides)) {
429            GrPixelConfig grPixConfig = SkImageInfo2GrPixelConfig(colorType, colorSpace,
430                                                                  *ctx->caps());
431            int supportedSampleCount = ctx->caps()->getSampleCount(sampleCount, grPixConfig);
432            if (sampleCount != supportedSampleCount) {
433                SkDebugf("Configuration '%s' sample count %d is not a supported sample count.\n",
434                         config->getTag().c_str(), sampleCount);
435                return;
436            }
437        } else {
438            SkDebugf("No context was available matching config '%s'.\n",
439                     config->getTag().c_str());
440            return;
441        }
442
443        Config target = {
444            gpuConfig->getTag(),
445            Benchmark::kGPU_Backend,
446            colorType,
447            kPremul_SkAlphaType,
448            sk_ref_sp(colorSpace),
449            sampleCount,
450            ctxType,
451            ctxOverrides,
452            gpuConfig->getUseDIText()
453        };
454
455        configs->push_back(target);
456        return;
457    }
458#endif
459
460    #define CPU_CONFIG(name, backend, color, alpha, colorSpace)                \
461        if (config->getTag().equals(#name)) {                                  \
462            if (!FLAGS_cpu) {                                                  \
463                SkDebugf("Skipping config '%s' as requested.\n",               \
464                         config->getTag().c_str());                            \
465                return;                                                        \
466            }                                                                  \
467            Config config = {                                                  \
468                SkString(#name), Benchmark::backend, color, alpha, colorSpace, \
469                0, kBogusContextType, kBogusContextOverrides, false            \
470            };                                                                 \
471            configs->push_back(config);                                        \
472            return;                                                            \
473        }
474
475    CPU_CONFIG(nonrendering, kNonRendering_Backend,
476               kUnknown_SkColorType, kUnpremul_SkAlphaType, nullptr)
477
478    CPU_CONFIG(a8, kRaster_Backend,
479               kAlpha_8_SkColorType, kPremul_SkAlphaType, nullptr)
480    CPU_CONFIG(8888, kRaster_Backend,
481               kN32_SkColorType, kPremul_SkAlphaType, nullptr)
482    CPU_CONFIG(565,  kRaster_Backend,
483               kRGB_565_SkColorType, kOpaque_SkAlphaType, nullptr)
484    auto srgbColorSpace = SkColorSpace::MakeSRGB();
485    CPU_CONFIG(srgb, kRaster_Backend,
486               kN32_SkColorType,  kPremul_SkAlphaType, srgbColorSpace)
487    auto srgbLinearColorSpace = SkColorSpace::MakeSRGBLinear();
488    CPU_CONFIG(f16,  kRaster_Backend,
489               kRGBA_F16_SkColorType, kPremul_SkAlphaType, srgbLinearColorSpace)
490
491    #undef CPU_CONFIG
492
493    SkDebugf("Unknown config '%s'.\n", config->getTag().c_str());
494}
495
496// Append all configs that are enabled and supported.
497void create_configs(SkTArray<Config>* configs) {
498    SkCommandLineConfigArray array;
499    ParseConfigs(FLAGS_config, &array);
500    for (int i = 0; i < array.count(); ++i) {
501        create_config(array[i].get(), configs);
502    }
503
504    // If no just default configs were requested, then we're okay.
505    if (array.count() == 0 || FLAGS_config.count() == 0 ||
506        // If we've been told to ignore undefined flags, we're okay.
507        FLAGS_undefok ||
508        // Otherwise, make sure that all specified configs have been created.
509        array.count() == configs->count()) {
510        return;
511    }
512    SkDebugf("Invalid --config. Use --undefok to bypass this warning.\n");
513    exit(1);
514}
515
516// disable warning : switch statement contains default but no 'case' labels
517#if defined _WIN32
518#pragma warning ( push )
519#pragma warning ( disable : 4065 )
520#endif
521
522// If bench is enabled for config, returns a Target* for it, otherwise nullptr.
523static Target* is_enabled(Benchmark* bench, const Config& config) {
524    if (!bench->isSuitableFor(config.backend)) {
525        return nullptr;
526    }
527
528    SkImageInfo info = SkImageInfo::Make(bench->getSize().fX, bench->getSize().fY,
529                                         config.color, config.alpha, config.colorSpace);
530
531    Target* target = nullptr;
532
533    switch (config.backend) {
534#if SK_SUPPORT_GPU
535    case Benchmark::kGPU_Backend:
536        target = new GPUTarget(config);
537        break;
538#endif
539    default:
540        target = new Target(config);
541        break;
542    }
543
544    if (!target->init(info, bench)) {
545        delete target;
546        return nullptr;
547    }
548    return target;
549}
550
551#if defined _WIN32
552#pragma warning ( pop )
553#endif
554
555static bool valid_brd_bench(sk_sp<SkData> encoded, SkColorType colorType, uint32_t sampleSize,
556        uint32_t minOutputSize, int* width, int* height) {
557    std::unique_ptr<SkBitmapRegionDecoder> brd(
558            SkBitmapRegionDecoder::Create(encoded, SkBitmapRegionDecoder::kAndroidCodec_Strategy));
559    if (nullptr == brd.get()) {
560        // This is indicates that subset decoding is not supported for a particular image format.
561        return false;
562    }
563
564    if (sampleSize * minOutputSize > (uint32_t) brd->width() || sampleSize * minOutputSize >
565            (uint32_t) brd->height()) {
566        // This indicates that the image is not large enough to decode a
567        // minOutputSize x minOutputSize subset at the given sampleSize.
568        return false;
569    }
570
571    // Set the image width and height.  The calling code will use this to choose subsets to decode.
572    *width = brd->width();
573    *height = brd->height();
574    return true;
575}
576
577static void cleanup_run(Target* target) {
578    delete target;
579}
580
581static void collect_files(const SkCommandLineFlags::StringArray& paths, const char* ext,
582                          SkTArray<SkString>* list) {
583    for (int i = 0; i < paths.count(); ++i) {
584        if (SkStrEndsWith(paths[i], ext)) {
585            list->push_back(SkString(paths[i]));
586        } else {
587            SkOSFile::Iter it(paths[i], ext);
588            SkString path;
589            while (it.next(&path)) {
590                list->push_back(SkOSPath::Join(paths[i], path.c_str()));
591            }
592        }
593    }
594}
595
596class BenchmarkStream {
597public:
598    BenchmarkStream() : fBenches(BenchRegistry::Head())
599                      , fGMs(skiagm::GMRegistry::Head())
600                      , fCurrentRecording(0)
601                      , fCurrentPiping(0)
602                      , fCurrentDeserialPicture(0)
603                      , fCurrentScale(0)
604                      , fCurrentSKP(0)
605                      , fCurrentSVG(0)
606                      , fCurrentUseMPD(0)
607                      , fCurrentCodec(0)
608                      , fCurrentAndroidCodec(0)
609                      , fCurrentBRDImage(0)
610                      , fCurrentColorImage(0)
611                      , fCurrentColorType(0)
612                      , fCurrentAlphaType(0)
613                      , fCurrentSubsetType(0)
614                      , fCurrentSampleSize(0)
615                      , fCurrentAnimSKP(0) {
616        collect_files(FLAGS_skps, ".skp", &fSKPs);
617        collect_files(FLAGS_svgs, ".svg", &fSVGs);
618
619        if (4 != sscanf(FLAGS_clip[0], "%d,%d,%d,%d",
620                        &fClip.fLeft, &fClip.fTop, &fClip.fRight, &fClip.fBottom)) {
621            SkDebugf("Can't parse %s from --clip as an SkIRect.\n", FLAGS_clip[0]);
622            exit(1);
623        }
624
625        for (int i = 0; i < FLAGS_scales.count(); i++) {
626            if (1 != sscanf(FLAGS_scales[i], "%f", &fScales.push_back())) {
627                SkDebugf("Can't parse %s from --scales as an SkScalar.\n", FLAGS_scales[i]);
628                exit(1);
629            }
630        }
631
632        if (2 != sscanf(FLAGS_zoom[0], "%f,%lf", &fZoomMax, &fZoomPeriodMs)) {
633            SkDebugf("Can't parse %s from --zoom as a zoomMax,zoomPeriodMs.\n", FLAGS_zoom[0]);
634            exit(1);
635        }
636
637        if (FLAGS_mpd) {
638            fUseMPDs.push_back() = true;
639        }
640        fUseMPDs.push_back() = false;
641
642        // Prepare the images for decoding
643        if (!CollectImages(FLAGS_images, &fImages)) {
644            exit(1);
645        }
646        if (!CollectImages(FLAGS_colorImages, &fColorImages)) {
647            exit(1);
648        }
649
650        // Choose the candidate color types for image decoding
651        fColorTypes.push_back(kN32_SkColorType);
652        if (!FLAGS_simpleCodec) {
653            fColorTypes.push_back(kRGB_565_SkColorType);
654            fColorTypes.push_back(kAlpha_8_SkColorType);
655            fColorTypes.push_back(kGray_8_SkColorType);
656        }
657    }
658
659    static sk_sp<SkPicture> ReadPicture(const char* path) {
660        // Not strictly necessary, as it will be checked again later,
661        // but helps to avoid a lot of pointless work if we're going to skip it.
662        if (SkCommandLineFlags::ShouldSkip(FLAGS_match, SkOSPath::Basename(path).c_str())) {
663            return nullptr;
664        }
665
666        std::unique_ptr<SkStream> stream = SkStream::MakeFromFile(path);
667        if (!stream) {
668            SkDebugf("Could not read %s.\n", path);
669            return nullptr;
670        }
671
672        return SkPicture::MakeFromStream(stream.get());
673    }
674
675    static sk_sp<SkPicture> ReadSVGPicture(const char* path) {
676        SkFILEStream stream(path);
677        if (!stream.isValid()) {
678            SkDebugf("Could not read %s.\n", path);
679            return nullptr;
680        }
681
682        sk_sp<SkSVGDOM> svgDom = SkSVGDOM::MakeFromStream(stream);
683        if (!svgDom) {
684            SkDebugf("Could not parse %s.\n", path);
685            return nullptr;
686        }
687
688        // Use the intrinsic SVG size if available, otherwise fall back to a default value.
689        static const SkSize kDefaultContainerSize = SkSize::Make(128, 128);
690        if (svgDom->containerSize().isEmpty()) {
691            svgDom->setContainerSize(kDefaultContainerSize);
692        }
693
694        SkPictureRecorder recorder;
695        svgDom->render(recorder.beginRecording(svgDom->containerSize().width(),
696                                               svgDom->containerSize().height()));
697        return recorder.finishRecordingAsPicture();
698    }
699
700    Benchmark* next() {
701        std::unique_ptr<Benchmark> bench;
702        do {
703            bench.reset(this->rawNext());
704            if (!bench) {
705                return nullptr;
706            }
707        } while(SkCommandLineFlags::ShouldSkip(FLAGS_sourceType, fSourceType) ||
708                SkCommandLineFlags::ShouldSkip(FLAGS_benchType,  fBenchType));
709        return bench.release();
710    }
711
712    Benchmark* rawNext() {
713        if (fBenches) {
714            Benchmark* bench = fBenches->factory()(nullptr);
715            fBenches = fBenches->next();
716            fSourceType = "bench";
717            fBenchType  = "micro";
718            return bench;
719        }
720
721        while (fGMs) {
722            std::unique_ptr<skiagm::GM> gm(fGMs->factory()(nullptr));
723            fGMs = fGMs->next();
724            if (gm->runAsBench()) {
725                fSourceType = "gm";
726                fBenchType  = "micro";
727                return new GMBench(gm.release());
728            }
729        }
730
731        // First add all .skps as RecordingBenches.
732        while (fCurrentRecording < fSKPs.count()) {
733            const SkString& path = fSKPs[fCurrentRecording++];
734            sk_sp<SkPicture> pic = ReadPicture(path.c_str());
735            if (!pic) {
736                continue;
737            }
738            SkString name = SkOSPath::Basename(path.c_str());
739            fSourceType = "skp";
740            fBenchType  = "recording";
741            fSKPBytes = static_cast<double>(pic->approximateBytesUsed());
742            fSKPOps   = pic->approximateOpCount();
743            return new RecordingBench(name.c_str(), pic.get(), FLAGS_bbh, FLAGS_lite);
744        }
745
746        // Add all .skps as PipeBenches.
747        while (fCurrentPiping < fSKPs.count()) {
748            const SkString& path = fSKPs[fCurrentPiping++];
749            sk_sp<SkPicture> pic = ReadPicture(path.c_str());
750            if (!pic) {
751                continue;
752            }
753            SkString name = SkOSPath::Basename(path.c_str());
754            fSourceType = "skp";
755            fBenchType  = "piping";
756            fSKPBytes = static_cast<double>(pic->approximateBytesUsed());
757            fSKPOps   = pic->approximateOpCount();
758            return new PipingBench(name.c_str(), pic.get());
759        }
760
761        // Add all .skps as DeserializePictureBenchs.
762        while (fCurrentDeserialPicture < fSKPs.count()) {
763            const SkString& path = fSKPs[fCurrentDeserialPicture++];
764            sk_sp<SkData> data = SkData::MakeFromFileName(path.c_str());
765            if (!data) {
766                continue;
767            }
768            SkString name = SkOSPath::Basename(path.c_str());
769            fSourceType = "skp";
770            fBenchType  = "deserial";
771            fSKPBytes = static_cast<double>(data->size());
772            fSKPOps   = 0;
773            return new DeserializePictureBench(name.c_str(), std::move(data));
774        }
775
776        // Then once each for each scale as SKPBenches (playback).
777        while (fCurrentScale < fScales.count()) {
778            while (fCurrentSKP < fSKPs.count()) {
779                const SkString& path = fSKPs[fCurrentSKP];
780                sk_sp<SkPicture> pic = ReadPicture(path.c_str());
781                if (!pic) {
782                    fCurrentSKP++;
783                    continue;
784                }
785
786                while (fCurrentUseMPD < fUseMPDs.count()) {
787                    if (FLAGS_bbh) {
788                        // The SKP we read off disk doesn't have a BBH.  Re-record so it grows one.
789                        SkRTreeFactory factory;
790                        SkPictureRecorder recorder;
791                        pic->playback(recorder.beginRecording(pic->cullRect().width(),
792                                                              pic->cullRect().height(),
793                                                              &factory,
794                                                              0));
795                        pic = recorder.finishRecordingAsPicture();
796                    }
797                    SkString name = SkOSPath::Basename(path.c_str());
798                    fSourceType = "skp";
799                    fBenchType = "playback";
800                    return new SKPBench(name.c_str(), pic.get(), fClip, fScales[fCurrentScale],
801                                        fUseMPDs[fCurrentUseMPD++], FLAGS_loopSKP);
802                }
803                fCurrentUseMPD = 0;
804                fCurrentSKP++;
805            }
806
807            while (fCurrentSVG++ < fSVGs.count()) {
808                const char* path = fSVGs[fCurrentSVG - 1].c_str();
809                if (sk_sp<SkPicture> pic = ReadSVGPicture(path)) {
810                    fSourceType = "svg";
811                    fBenchType = "playback";
812                    return new SKPBench(SkOSPath::Basename(path).c_str(), pic.get(), fClip,
813                                        fScales[fCurrentScale], false, FLAGS_loopSKP);
814                }
815            }
816
817            fCurrentSKP = 0;
818            fCurrentSVG = 0;
819            fCurrentScale++;
820        }
821
822        // Now loop over each skp again if we have an animation
823        if (fZoomMax != 1.0f && fZoomPeriodMs > 0) {
824            while (fCurrentAnimSKP < fSKPs.count()) {
825                const SkString& path = fSKPs[fCurrentAnimSKP];
826                sk_sp<SkPicture> pic = ReadPicture(path.c_str());
827                if (!pic) {
828                    fCurrentAnimSKP++;
829                    continue;
830                }
831
832                fCurrentAnimSKP++;
833                SkString name = SkOSPath::Basename(path.c_str());
834                sk_sp<SKPAnimationBench::Animation> animation(
835                    SKPAnimationBench::CreateZoomAnimation(fZoomMax, fZoomPeriodMs));
836                return new SKPAnimationBench(name.c_str(), pic.get(), fClip, animation.get(),
837                                             FLAGS_loopSKP);
838            }
839        }
840
841        for (; fCurrentCodec < fImages.count(); fCurrentCodec++) {
842            fSourceType = "image";
843            fBenchType = "skcodec";
844            const SkString& path = fImages[fCurrentCodec];
845            if (SkCommandLineFlags::ShouldSkip(FLAGS_match, path.c_str())) {
846                continue;
847            }
848            sk_sp<SkData> encoded(SkData::MakeFromFileName(path.c_str()));
849            std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(encoded));
850            if (!codec) {
851                // Nothing to time.
852                SkDebugf("Cannot find codec for %s\n", path.c_str());
853                continue;
854            }
855
856            while (fCurrentColorType < fColorTypes.count()) {
857                const SkColorType colorType = fColorTypes[fCurrentColorType];
858
859                SkAlphaType alphaType = codec->getInfo().alphaType();
860                if (FLAGS_simpleCodec) {
861                    if (kUnpremul_SkAlphaType == alphaType) {
862                        alphaType = kPremul_SkAlphaType;
863                    }
864
865                    fCurrentColorType++;
866                } else {
867                    switch (alphaType) {
868                        case kOpaque_SkAlphaType:
869                            // We only need to test one alpha type (opaque).
870                            fCurrentColorType++;
871                            break;
872                        case kUnpremul_SkAlphaType:
873                        case kPremul_SkAlphaType:
874                            if (0 == fCurrentAlphaType) {
875                                // Test unpremul first.
876                                alphaType = kUnpremul_SkAlphaType;
877                                fCurrentAlphaType++;
878                            } else {
879                                // Test premul.
880                                alphaType = kPremul_SkAlphaType;
881                                fCurrentAlphaType = 0;
882                                fCurrentColorType++;
883                            }
884                            break;
885                        default:
886                            SkASSERT(false);
887                            fCurrentColorType++;
888                            break;
889                    }
890                }
891
892                // Make sure we can decode to this color type and alpha type.
893                SkImageInfo info =
894                        codec->getInfo().makeColorType(colorType).makeAlphaType(alphaType);
895                const size_t rowBytes = info.minRowBytes();
896                SkAutoMalloc storage(info.computeByteSize(rowBytes));
897
898                const SkCodec::Result result = codec->getPixels(
899                        info, storage.get(), rowBytes);
900                switch (result) {
901                    case SkCodec::kSuccess:
902                    case SkCodec::kIncompleteInput:
903                        return new CodecBench(SkOSPath::Basename(path.c_str()),
904                                              encoded.get(), colorType, alphaType);
905                    case SkCodec::kInvalidConversion:
906                        // This is okay. Not all conversions are valid.
907                        break;
908                    default:
909                        // This represents some sort of failure.
910                        SkASSERT(false);
911                        break;
912                }
913            }
914            fCurrentColorType = 0;
915        }
916
917        // Run AndroidCodecBenches
918        const int sampleSizes[] = { 2, 4, 8 };
919        for (; fCurrentAndroidCodec < fImages.count(); fCurrentAndroidCodec++) {
920            fSourceType = "image";
921            fBenchType = "skandroidcodec";
922
923            const SkString& path = fImages[fCurrentAndroidCodec];
924            if (SkCommandLineFlags::ShouldSkip(FLAGS_match, path.c_str())) {
925                continue;
926            }
927            sk_sp<SkData> encoded(SkData::MakeFromFileName(path.c_str()));
928            std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::MakeFromData(encoded));
929            if (!codec) {
930                // Nothing to time.
931                SkDebugf("Cannot find codec for %s\n", path.c_str());
932                continue;
933            }
934
935            while (fCurrentSampleSize < (int) SK_ARRAY_COUNT(sampleSizes)) {
936                int sampleSize = sampleSizes[fCurrentSampleSize];
937                fCurrentSampleSize++;
938                if (10 * sampleSize > SkTMin(codec->getInfo().width(), codec->getInfo().height())) {
939                    // Avoid benchmarking scaled decodes of already small images.
940                    break;
941                }
942
943                return new AndroidCodecBench(SkOSPath::Basename(path.c_str()),
944                                             encoded.get(), sampleSize);
945            }
946            fCurrentSampleSize = 0;
947        }
948
949        // Run the BRDBenches
950        // We intend to create benchmarks that model the use cases in
951        // android/libraries/social/tiledimage.  In this library, an image is decoded in 512x512
952        // tiles.  The image can be translated freely, so the location of a tile may be anywhere in
953        // the image.  For that reason, we will benchmark decodes in five representative locations
954        // in the image.  Additionally, this use case utilizes power of two scaling, so we will
955        // test on power of two sample sizes.  The output tile is always 512x512, so, when a
956        // sampleSize is used, the size of the subset that is decoded is always
957        // (sampleSize*512)x(sampleSize*512).
958        // There are a few good reasons to only test on power of two sample sizes at this time:
959        //     All use cases we are aware of only scale by powers of two.
960        //     PNG decodes use the indicated sampling strategy regardless of the sample size, so
961        //         these tests are sufficient to provide good coverage of our scaling options.
962        const uint32_t brdSampleSizes[] = { 1, 2, 4, 8, 16 };
963        const uint32_t minOutputSize = 512;
964        for (; fCurrentBRDImage < fImages.count(); fCurrentBRDImage++) {
965            fSourceType = "image";
966            fBenchType = "BRD";
967
968            const SkString& path = fImages[fCurrentBRDImage];
969            if (SkCommandLineFlags::ShouldSkip(FLAGS_match, path.c_str())) {
970                continue;
971            }
972
973            while (fCurrentColorType < fColorTypes.count()) {
974                while (fCurrentSampleSize < (int) SK_ARRAY_COUNT(brdSampleSizes)) {
975                    while (fCurrentSubsetType <= kLastSingle_SubsetType) {
976
977                        sk_sp<SkData> encoded(SkData::MakeFromFileName(path.c_str()));
978                        const SkColorType colorType = fColorTypes[fCurrentColorType];
979                        uint32_t sampleSize = brdSampleSizes[fCurrentSampleSize];
980                        int currentSubsetType = fCurrentSubsetType++;
981
982                        int width = 0;
983                        int height = 0;
984                        if (!valid_brd_bench(encoded, colorType, sampleSize, minOutputSize,
985                                &width, &height)) {
986                            break;
987                        }
988
989                        SkString basename = SkOSPath::Basename(path.c_str());
990                        SkIRect subset;
991                        const uint32_t subsetSize = sampleSize * minOutputSize;
992                        switch (currentSubsetType) {
993                            case kTopLeft_SubsetType:
994                                basename.append("_TopLeft");
995                                subset = SkIRect::MakeXYWH(0, 0, subsetSize, subsetSize);
996                                break;
997                            case kTopRight_SubsetType:
998                                basename.append("_TopRight");
999                                subset = SkIRect::MakeXYWH(width - subsetSize, 0, subsetSize,
1000                                        subsetSize);
1001                                break;
1002                            case kMiddle_SubsetType:
1003                                basename.append("_Middle");
1004                                subset = SkIRect::MakeXYWH((width - subsetSize) / 2,
1005                                        (height - subsetSize) / 2, subsetSize, subsetSize);
1006                                break;
1007                            case kBottomLeft_SubsetType:
1008                                basename.append("_BottomLeft");
1009                                subset = SkIRect::MakeXYWH(0, height - subsetSize, subsetSize,
1010                                        subsetSize);
1011                                break;
1012                            case kBottomRight_SubsetType:
1013                                basename.append("_BottomRight");
1014                                subset = SkIRect::MakeXYWH(width - subsetSize,
1015                                        height - subsetSize, subsetSize, subsetSize);
1016                                break;
1017                            default:
1018                                SkASSERT(false);
1019                        }
1020
1021                        return new BitmapRegionDecoderBench(basename.c_str(), encoded.get(),
1022                                colorType, sampleSize, subset);
1023                    }
1024                    fCurrentSubsetType = 0;
1025                    fCurrentSampleSize++;
1026                }
1027                fCurrentSampleSize = 0;
1028                fCurrentColorType++;
1029            }
1030            fCurrentColorType = 0;
1031        }
1032
1033        while (fCurrentColorImage < fColorImages.count()) {
1034            fSourceType = "colorimage";
1035            fBenchType = "skcolorcodec";
1036            const SkString& path = fColorImages[fCurrentColorImage];
1037            fCurrentColorImage++;
1038            sk_sp<SkData> encoded = SkData::MakeFromFileName(path.c_str());
1039            if (encoded) {
1040                return new ColorCodecBench(SkOSPath::Basename(path.c_str()).c_str(),
1041                                           std::move(encoded));
1042            } else {
1043                SkDebugf("Could not read file %s.\n", path.c_str());
1044            }
1045        }
1046
1047        return nullptr;
1048    }
1049
1050    void fillCurrentOptions(ResultsWriter* log) const {
1051        log->configOption("source_type", fSourceType);
1052        log->configOption("bench_type",  fBenchType);
1053        if (0 == strcmp(fSourceType, "skp")) {
1054            log->configOption("clip",
1055                    SkStringPrintf("%d %d %d %d", fClip.fLeft, fClip.fTop,
1056                                                  fClip.fRight, fClip.fBottom).c_str());
1057            SkASSERT_RELEASE(fCurrentScale < fScales.count());  // debugging paranoia
1058            log->configOption("scale", SkStringPrintf("%.2g", fScales[fCurrentScale]).c_str());
1059            if (fCurrentUseMPD > 0) {
1060                SkASSERT(1 == fCurrentUseMPD || 2 == fCurrentUseMPD);
1061                log->configOption("multi_picture_draw", fUseMPDs[fCurrentUseMPD-1] ? "true" : "false");
1062            }
1063        }
1064        if (0 == strcmp(fBenchType, "recording")) {
1065            log->metric("bytes", fSKPBytes);
1066            log->metric("ops",   fSKPOps);
1067        }
1068    }
1069
1070private:
1071    enum SubsetType {
1072        kTopLeft_SubsetType     = 0,
1073        kTopRight_SubsetType    = 1,
1074        kMiddle_SubsetType      = 2,
1075        kBottomLeft_SubsetType  = 3,
1076        kBottomRight_SubsetType = 4,
1077        kTranslate_SubsetType   = 5,
1078        kZoom_SubsetType        = 6,
1079        kLast_SubsetType        = kZoom_SubsetType,
1080        kLastSingle_SubsetType  = kBottomRight_SubsetType,
1081    };
1082
1083    const BenchRegistry* fBenches;
1084    const skiagm::GMRegistry* fGMs;
1085    SkIRect            fClip;
1086    SkTArray<SkScalar> fScales;
1087    SkTArray<SkString> fSKPs;
1088    SkTArray<SkString> fSVGs;
1089    SkTArray<bool>     fUseMPDs;
1090    SkTArray<SkString> fImages;
1091    SkTArray<SkString> fColorImages;
1092    SkTArray<SkColorType, true> fColorTypes;
1093    SkScalar           fZoomMax;
1094    double             fZoomPeriodMs;
1095
1096    double fSKPBytes, fSKPOps;
1097
1098    const char* fSourceType;  // What we're benching: bench, GM, SKP, ...
1099    const char* fBenchType;   // How we bench it: micro, recording, playback, ...
1100    int fCurrentRecording;
1101    int fCurrentPiping;
1102    int fCurrentDeserialPicture;
1103    int fCurrentScale;
1104    int fCurrentSKP;
1105    int fCurrentSVG;
1106    int fCurrentUseMPD;
1107    int fCurrentCodec;
1108    int fCurrentAndroidCodec;
1109    int fCurrentBRDImage;
1110    int fCurrentColorImage;
1111    int fCurrentColorType;
1112    int fCurrentAlphaType;
1113    int fCurrentSubsetType;
1114    int fCurrentSampleSize;
1115    int fCurrentAnimSKP;
1116};
1117
1118// Some runs (mostly, Valgrind) are so slow that the bot framework thinks we've hung.
1119// This prints something every once in a while so that it knows we're still working.
1120static void start_keepalive() {
1121    static std::thread* intentionallyLeaked = new std::thread([]{
1122        for (;;) {
1123            static const int kSec = 1200;
1124        #if defined(SK_BUILD_FOR_WIN)
1125            Sleep(kSec * 1000);
1126        #else
1127            sleep(kSec);
1128        #endif
1129            SkDebugf("\nBenchmarks still running...\n");
1130        }
1131    });
1132    (void)intentionallyLeaked;
1133}
1134
1135int main(int argc, char** argv) {
1136    SkCommandLineFlags::Parse(argc, argv);
1137
1138    initializeEventTracingForTools();
1139
1140#if defined(SK_BUILD_FOR_IOS)
1141    cd_Documents();
1142#endif
1143    SetupCrashHandler();
1144    SkAutoGraphics ag;
1145    SkTaskGroup::Enabler enabled(FLAGS_threads);
1146
1147#if SK_SUPPORT_GPU
1148    SetCtxOptionsFromCommonFlags(&grContextOpts);
1149#endif
1150
1151    if (FLAGS_veryVerbose) {
1152        FLAGS_verbose = true;
1153    }
1154
1155    if (kAutoTuneLoops != FLAGS_loops) {
1156        FLAGS_samples     = 1;
1157        FLAGS_gpuFrameLag = 0;
1158    }
1159
1160    if (!FLAGS_writePath.isEmpty()) {
1161        SkDebugf("Writing files to %s.\n", FLAGS_writePath[0]);
1162        if (!sk_mkdir(FLAGS_writePath[0])) {
1163            SkDebugf("Could not create %s. Files won't be written.\n", FLAGS_writePath[0]);
1164            FLAGS_writePath.set(0, nullptr);
1165        }
1166    }
1167
1168    std::unique_ptr<ResultsWriter> log(new ResultsWriter);
1169    if (!FLAGS_outResultsFile.isEmpty()) {
1170#if defined(SK_RELEASE)
1171        log.reset(new NanoJSONResultsWriter(FLAGS_outResultsFile[0]));
1172#else
1173        SkDebugf("I'm ignoring --outResultsFile because this is a Debug build.");
1174        return 1;
1175#endif
1176    }
1177
1178    if (1 == FLAGS_properties.count() % 2) {
1179        SkDebugf("ERROR: --properties must be passed with an even number of arguments.\n");
1180        return 1;
1181    }
1182    for (int i = 1; i < FLAGS_properties.count(); i += 2) {
1183        log->property(FLAGS_properties[i-1], FLAGS_properties[i]);
1184    }
1185
1186    if (1 == FLAGS_key.count() % 2) {
1187        SkDebugf("ERROR: --key must be passed with an even number of arguments.\n");
1188        return 1;
1189    }
1190    for (int i = 1; i < FLAGS_key.count(); i += 2) {
1191        log->key(FLAGS_key[i-1], FLAGS_key[i]);
1192    }
1193
1194    const double overhead = estimate_timer_overhead();
1195    SkDebugf("Timer overhead: %s\n", HUMANIZE(overhead));
1196
1197    SkTArray<double> samples;
1198
1199    if (kAutoTuneLoops != FLAGS_loops) {
1200        SkDebugf("Fixed number of loops; times would only be misleading so we won't print them.\n");
1201    } else if (FLAGS_quiet) {
1202        SkDebugf("! -> high variance, ? -> moderate variance\n");
1203        SkDebugf("    micros   \tbench\n");
1204    } else if (FLAGS_ms) {
1205        SkDebugf("curr/maxrss\tloops\tmin\tmedian\tmean\tmax\tstddev\tsamples\tconfig\tbench\n");
1206    } else {
1207        SkDebugf("curr/maxrss\tloops\tmin\tmedian\tmean\tmax\tstddev\t%-*s\tconfig\tbench\n",
1208                 FLAGS_samples, "samples");
1209    }
1210
1211    SkTArray<Config> configs;
1212    create_configs(&configs);
1213
1214    if (FLAGS_keepAlive) {
1215        start_keepalive();
1216    }
1217
1218    gSkUseAnalyticAA = FLAGS_analyticAA;
1219    gSkUseDeltaAA = FLAGS_deltaAA;
1220
1221    if (FLAGS_forceDeltaAA) {
1222        gSkForceDeltaAA = true;
1223    }
1224    if (FLAGS_forceAnalyticAA) {
1225        gSkForceAnalyticAA = true;
1226    }
1227    if (FLAGS_forceRasterPipeline) {
1228        gSkForceRasterPipelineBlitter = true;
1229    }
1230
1231    int runs = 0;
1232    BenchmarkStream benchStream;
1233    while (Benchmark* b = benchStream.next()) {
1234        std::unique_ptr<Benchmark> bench(b);
1235        if (SkCommandLineFlags::ShouldSkip(FLAGS_match, bench->getUniqueName())) {
1236            continue;
1237        }
1238
1239        if (!configs.empty()) {
1240            log->bench(bench->getUniqueName(), bench->getSize().fX, bench->getSize().fY);
1241            bench->delayedSetup();
1242        }
1243        for (int i = 0; i < configs.count(); ++i) {
1244            Target* target = is_enabled(b, configs[i]);
1245            if (!target) {
1246                continue;
1247            }
1248
1249            // During HWUI output this canvas may be nullptr.
1250            SkCanvas* canvas = target->getCanvas();
1251            const char* config = target->config.name.c_str();
1252
1253            if (FLAGS_pre_log || FLAGS_dryRun) {
1254                SkDebugf("Running %s\t%s\n"
1255                         , bench->getUniqueName()
1256                         , config);
1257                if (FLAGS_dryRun) {
1258                    continue;
1259                }
1260            }
1261
1262            TRACE_EVENT2("skia", "Benchmark", "name", TRACE_STR_COPY(bench->getUniqueName()),
1263                                              "config", TRACE_STR_COPY(config));
1264
1265            target->setup();
1266            bench->perCanvasPreDraw(canvas);
1267
1268            int maxFrameLag;
1269            int loops = target->needsFrameTiming(&maxFrameLag)
1270                ? setup_gpu_bench(target, bench.get(), maxFrameLag)
1271                : setup_cpu_bench(overhead, target, bench.get());
1272
1273            if (kFailedLoops == loops) {
1274                // Can't be timed.  A warning note has already been printed.
1275                cleanup_run(target);
1276                continue;
1277            }
1278
1279            if (runs == 0 && FLAGS_ms < 1000) {
1280                // Run the first bench for 1000ms to warm up the nanobench if FLAGS_ms < 1000.
1281                // Otherwise, the first few benches' measurements will be inaccurate.
1282                auto stop = now_ms() + 1000;
1283                do {
1284                    time(loops, bench.get(), target);
1285                } while (now_ms() < stop);
1286            }
1287
1288            if (FLAGS_ms) {
1289                samples.reset();
1290                auto stop = now_ms() + FLAGS_ms;
1291                do {
1292                    samples.push_back(time(loops, bench.get(), target) / loops);
1293                } while (now_ms() < stop);
1294            } else {
1295                samples.reset(FLAGS_samples);
1296                for (int s = 0; s < FLAGS_samples; s++) {
1297                    samples[s] = time(loops, bench.get(), target) / loops;
1298                }
1299            }
1300
1301#if SK_SUPPORT_GPU
1302            SkTArray<SkString> keys;
1303            SkTArray<double> values;
1304            bool gpuStatsDump = FLAGS_gpuStatsDump && Benchmark::kGPU_Backend == configs[i].backend;
1305            if (gpuStatsDump) {
1306                // TODO cache stats
1307                bench->getGpuStats(canvas, &keys, &values);
1308            }
1309#endif
1310
1311            bench->perCanvasPostDraw(canvas);
1312
1313            if (Benchmark::kNonRendering_Backend != target->config.backend &&
1314                !FLAGS_writePath.isEmpty() && FLAGS_writePath[0]) {
1315                SkString pngFilename = SkOSPath::Join(FLAGS_writePath[0], config);
1316                pngFilename = SkOSPath::Join(pngFilename.c_str(), bench->getUniqueName());
1317                pngFilename.append(".png");
1318                write_canvas_png(target, pngFilename);
1319            }
1320
1321            Stats stats(samples);
1322            log->config(config);
1323            log->configOption("name", bench->getName());
1324            benchStream.fillCurrentOptions(log.get());
1325            target->fillOptions(log.get());
1326            log->metric("min_ms",    stats.min);
1327            log->metrics("samples",    samples);
1328#if SK_SUPPORT_GPU
1329            if (gpuStatsDump) {
1330                // dump to json, only SKPBench currently returns valid keys / values
1331                SkASSERT(keys.count() == values.count());
1332                for (int i = 0; i < keys.count(); i++) {
1333                    log->metric(keys[i].c_str(), values[i]);
1334                }
1335            }
1336#endif
1337
1338            if (runs++ % FLAGS_flushEvery == 0) {
1339                log->flush();
1340            }
1341
1342            if (kAutoTuneLoops != FLAGS_loops) {
1343                if (configs.count() == 1) {
1344                    config = ""; // Only print the config if we run the same bench on more than one.
1345                }
1346                SkDebugf("%4d/%-4dMB\t%s\t%s\n"
1347                         , sk_tools::getCurrResidentSetSizeMB()
1348                         , sk_tools::getMaxResidentSetSizeMB()
1349                         , bench->getUniqueName()
1350                         , config);
1351            } else if (FLAGS_quiet) {
1352                const char* mark = " ";
1353                const double stddev_percent = 100 * sqrt(stats.var) / stats.mean;
1354                if (stddev_percent >  5) mark = "?";
1355                if (stddev_percent > 10) mark = "!";
1356
1357                SkDebugf("%10.2f %s\t%s\t%s\n",
1358                         stats.median*1e3, mark, bench->getUniqueName(), config);
1359            } else if (FLAGS_csv) {
1360                const double stddev_percent = 100 * sqrt(stats.var) / stats.mean;
1361                SkDebugf("%g,%g,%g,%g,%g,%s,%s\n"
1362                         , stats.min
1363                         , stats.median
1364                         , stats.mean
1365                         , stats.max
1366                         , stddev_percent
1367                         , config
1368                         , bench->getUniqueName()
1369                         );
1370            } else {
1371                const char* format = "%4d/%-4dMB\t%d\t%s\t%s\t%s\t%s\t%.0f%%\t%s\t%s\t%s\n";
1372                const double stddev_percent = 100 * sqrt(stats.var) / stats.mean;
1373                SkDebugf(format
1374                        , sk_tools::getCurrResidentSetSizeMB()
1375                        , sk_tools::getMaxResidentSetSizeMB()
1376                        , loops
1377                        , HUMANIZE(stats.min)
1378                        , HUMANIZE(stats.median)
1379                        , HUMANIZE(stats.mean)
1380                        , HUMANIZE(stats.max)
1381                        , stddev_percent
1382                        , FLAGS_ms ? to_string(samples.count()).c_str() : stats.plot.c_str()
1383                        , config
1384                        , bench->getUniqueName()
1385                        );
1386            }
1387
1388#if SK_SUPPORT_GPU
1389            if (FLAGS_gpuStats && Benchmark::kGPU_Backend == configs[i].backend) {
1390                target->dumpStats();
1391            }
1392#endif
1393
1394            if (FLAGS_verbose) {
1395                SkDebugf("Samples:  ");
1396                for (int i = 0; i < samples.count(); i++) {
1397                    SkDebugf("%s  ", HUMANIZE(samples[i]));
1398                }
1399                SkDebugf("%s\n", bench->getUniqueName());
1400            }
1401            cleanup_run(target);
1402        }
1403    }
1404
1405    SkGraphics::PurgeAllCaches();
1406
1407    log->bench("memory_usage", 0,0);
1408    log->config("meta");
1409    log->metric("max_rss_mb", sk_tools::getMaxResidentSetSizeMB());
1410
1411    return 0;
1412}
1413