Home | History | Annotate | Download | only in bench
      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 
     54 extern 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 
     74 static const int kAutoTuneLoops = 0;
     75 
     76 #if !defined(__has_feature)
     77     #define  __has_feature(x) 0
     78 #endif
     79 
     80 static const int kDefaultLoops =
     81 #if defined(SK_DEBUG) || __has_feature(address_sanitizer)
     82     1;
     83 #else
     84     kAutoTuneLoops;
     85 #endif
     86 
     87 static 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 
     95 static SkString to_string(int n) {
     96     SkString str;
     97     str.appendS32(n);
     98     return str;
     99 }
    100 
    101 DECLARE_bool(undefok);
    102 
    103 DEFINE_int32(loops, kDefaultLoops, loops_help_txt().c_str());
    104 
    105 DEFINE_int32(samples, 10, "Number of samples to measure for each bench.");
    106 DEFINE_int32(ms, 0, "If >0, run each bench for this many ms instead of obeying --samples.");
    107 DEFINE_int32(overheadLoops, 100000, "Loops to estimate timer overhead.");
    108 DEFINE_double(overheadGoal, 0.0001,
    109               "Loop until timer overhead is at most this fraction of our measurments.");
    110 DEFINE_double(gpuMs, 5, "Target bench time in millseconds for GPU.");
    111 DEFINE_int32(gpuFrameLag, 5, "If unknown, estimated maximum number of frames GPU allows to lag.");
    112 
    113 DEFINE_string(outResultsFile, "", "If given, write results here as JSON.");
    114 DEFINE_int32(maxCalibrationAttempts, 3,
    115              "Try up to this many times to guess loops for a bench, or skip the bench.");
    116 DEFINE_int32(maxLoops, 1000000, "Never run a bench more times than this.");
    117 DEFINE_string(clip, "0,0,1000,1000", "Clip for SKPs.");
    118 DEFINE_string(scales, "1.0", "Space-separated scales for SKPs.");
    119 DEFINE_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.");
    121 DEFINE_bool(bbh, true, "Build a BBH for SKPs?");
    122 DEFINE_bool(lite, false, "Use SkLiteRecorder in recording benchmarks?");
    123 DEFINE_bool(mpd, true, "Use MultiPictureDraw for the SKPs?");
    124 DEFINE_bool(loopSKP, true, "Loop SKPs like we do for micro benches?");
    125 DEFINE_int32(flushEvery, 10, "Flush --outResultsFile every Nth run.");
    126 DEFINE_bool(gpuStats, false, "Print GPU stats after each gpu benchmark?");
    127 DEFINE_bool(gpuStatsDump, false, "Dump GPU states after each benchmark to json");
    128 DEFINE_bool(keepAlive, false, "Print a message every so often so that we don't time out");
    129 DEFINE_bool(csv, false, "Print status in CSV format");
    130 DEFINE_string(sourceType, "",
    131         "Apply usual --match rules to source type: bench, gm, skp, image, etc.");
    132 DEFINE_string(benchType,  "",
    133         "Apply usual --match rules to bench type: micro, recording, piping, playback, skcodec, etc.");
    134 
    135 DEFINE_bool(forceRasterPipeline, false, "sets gSkForceRasterPipelineBlitter");
    136 
    137 static double now_ms() { return SkTime::GetNSecs() * 1e-6; }
    138 
    139 static 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 
    145 bool 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 }
    154 bool 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
    168 struct 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 
    240 static 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 
    258 static 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 
    267 static 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 
    275 static 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 
    288 static 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 
    321 static int kFailedLoops = -2;
    322 static 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 
    366 static 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 
    412 static 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 =
    432                     ctx->caps()->getRenderTargetSampleCount(sampleCount, grPixConfig);
    433             if (sampleCount != supportedSampleCount) {
    434                 SkDebugf("Configuration '%s' sample count %d is not a supported sample count.\n",
    435                          config->getTag().c_str(), sampleCount);
    436                 return;
    437             }
    438         } else {
    439             SkDebugf("No context was available matching config '%s'.\n",
    440                      config->getTag().c_str());
    441             return;
    442         }
    443 
    444         Config target = {
    445             gpuConfig->getTag(),
    446             Benchmark::kGPU_Backend,
    447             colorType,
    448             kPremul_SkAlphaType,
    449             sk_ref_sp(colorSpace),
    450             sampleCount,
    451             ctxType,
    452             ctxOverrides,
    453             gpuConfig->getUseDIText()
    454         };
    455 
    456         configs->push_back(target);
    457         return;
    458     }
    459 #endif
    460 
    461     #define CPU_CONFIG(name, backend, color, alpha, colorSpace)                \
    462         if (config->getTag().equals(#name)) {                                  \
    463             if (!FLAGS_cpu) {                                                  \
    464                 SkDebugf("Skipping config '%s' as requested.\n",               \
    465                          config->getTag().c_str());                            \
    466                 return;                                                        \
    467             }                                                                  \
    468             Config config = {                                                  \
    469                 SkString(#name), Benchmark::backend, color, alpha, colorSpace, \
    470                 0, kBogusContextType, kBogusContextOverrides, false            \
    471             };                                                                 \
    472             configs->push_back(config);                                        \
    473             return;                                                            \
    474         }
    475 
    476     CPU_CONFIG(nonrendering, kNonRendering_Backend,
    477                kUnknown_SkColorType, kUnpremul_SkAlphaType, nullptr)
    478 
    479     CPU_CONFIG(a8, kRaster_Backend,
    480                kAlpha_8_SkColorType, kPremul_SkAlphaType, nullptr)
    481     CPU_CONFIG(8888, kRaster_Backend,
    482                kN32_SkColorType, kPremul_SkAlphaType, nullptr)
    483     CPU_CONFIG(565,  kRaster_Backend,
    484                kRGB_565_SkColorType, kOpaque_SkAlphaType, nullptr)
    485     auto srgbColorSpace = SkColorSpace::MakeSRGB();
    486     CPU_CONFIG(srgb, kRaster_Backend,
    487                kN32_SkColorType,  kPremul_SkAlphaType, srgbColorSpace)
    488     auto srgbLinearColorSpace = SkColorSpace::MakeSRGBLinear();
    489     CPU_CONFIG(f16,  kRaster_Backend,
    490                kRGBA_F16_SkColorType, kPremul_SkAlphaType, srgbLinearColorSpace)
    491 
    492     #undef CPU_CONFIG
    493 
    494     SkDebugf("Unknown config '%s'.\n", config->getTag().c_str());
    495 }
    496 
    497 // Append all configs that are enabled and supported.
    498 void create_configs(SkTArray<Config>* configs) {
    499     SkCommandLineConfigArray array;
    500     ParseConfigs(FLAGS_config, &array);
    501     for (int i = 0; i < array.count(); ++i) {
    502         create_config(array[i].get(), configs);
    503     }
    504 
    505     // If no just default configs were requested, then we're okay.
    506     if (array.count() == 0 || FLAGS_config.count() == 0 ||
    507         // If we've been told to ignore undefined flags, we're okay.
    508         FLAGS_undefok ||
    509         // Otherwise, make sure that all specified configs have been created.
    510         array.count() == configs->count()) {
    511         return;
    512     }
    513     SkDebugf("Invalid --config. Use --undefok to bypass this warning.\n");
    514     exit(1);
    515 }
    516 
    517 // disable warning : switch statement contains default but no 'case' labels
    518 #if defined _WIN32
    519 #pragma warning ( push )
    520 #pragma warning ( disable : 4065 )
    521 #endif
    522 
    523 // If bench is enabled for config, returns a Target* for it, otherwise nullptr.
    524 static Target* is_enabled(Benchmark* bench, const Config& config) {
    525     if (!bench->isSuitableFor(config.backend)) {
    526         return nullptr;
    527     }
    528 
    529     SkImageInfo info = SkImageInfo::Make(bench->getSize().fX, bench->getSize().fY,
    530                                          config.color, config.alpha, config.colorSpace);
    531 
    532     Target* target = nullptr;
    533 
    534     switch (config.backend) {
    535 #if SK_SUPPORT_GPU
    536     case Benchmark::kGPU_Backend:
    537         target = new GPUTarget(config);
    538         break;
    539 #endif
    540     default:
    541         target = new Target(config);
    542         break;
    543     }
    544 
    545     if (!target->init(info, bench)) {
    546         delete target;
    547         return nullptr;
    548     }
    549     return target;
    550 }
    551 
    552 #if defined _WIN32
    553 #pragma warning ( pop )
    554 #endif
    555 
    556 static bool valid_brd_bench(sk_sp<SkData> encoded, SkColorType colorType, uint32_t sampleSize,
    557         uint32_t minOutputSize, int* width, int* height) {
    558     std::unique_ptr<SkBitmapRegionDecoder> brd(
    559             SkBitmapRegionDecoder::Create(encoded, SkBitmapRegionDecoder::kAndroidCodec_Strategy));
    560     if (nullptr == brd.get()) {
    561         // This is indicates that subset decoding is not supported for a particular image format.
    562         return false;
    563     }
    564 
    565     if (sampleSize * minOutputSize > (uint32_t) brd->width() || sampleSize * minOutputSize >
    566             (uint32_t) brd->height()) {
    567         // This indicates that the image is not large enough to decode a
    568         // minOutputSize x minOutputSize subset at the given sampleSize.
    569         return false;
    570     }
    571 
    572     // Set the image width and height.  The calling code will use this to choose subsets to decode.
    573     *width = brd->width();
    574     *height = brd->height();
    575     return true;
    576 }
    577 
    578 static void cleanup_run(Target* target) {
    579     delete target;
    580 }
    581 
    582 static void collect_files(const SkCommandLineFlags::StringArray& paths, const char* ext,
    583                           SkTArray<SkString>* list) {
    584     for (int i = 0; i < paths.count(); ++i) {
    585         if (SkStrEndsWith(paths[i], ext)) {
    586             list->push_back(SkString(paths[i]));
    587         } else {
    588             SkOSFile::Iter it(paths[i], ext);
    589             SkString path;
    590             while (it.next(&path)) {
    591                 list->push_back(SkOSPath::Join(paths[i], path.c_str()));
    592             }
    593         }
    594     }
    595 }
    596 
    597 class BenchmarkStream {
    598 public:
    599     BenchmarkStream() : fBenches(BenchRegistry::Head())
    600                       , fGMs(skiagm::GMRegistry::Head())
    601                       , fCurrentRecording(0)
    602                       , fCurrentPiping(0)
    603                       , fCurrentDeserialPicture(0)
    604                       , fCurrentScale(0)
    605                       , fCurrentSKP(0)
    606                       , fCurrentSVG(0)
    607                       , fCurrentUseMPD(0)
    608                       , fCurrentCodec(0)
    609                       , fCurrentAndroidCodec(0)
    610                       , fCurrentBRDImage(0)
    611                       , fCurrentColorImage(0)
    612                       , fCurrentColorType(0)
    613                       , fCurrentAlphaType(0)
    614                       , fCurrentSubsetType(0)
    615                       , fCurrentSampleSize(0)
    616                       , fCurrentAnimSKP(0) {
    617         collect_files(FLAGS_skps, ".skp", &fSKPs);
    618         collect_files(FLAGS_svgs, ".svg", &fSVGs);
    619 
    620         if (4 != sscanf(FLAGS_clip[0], "%d,%d,%d,%d",
    621                         &fClip.fLeft, &fClip.fTop, &fClip.fRight, &fClip.fBottom)) {
    622             SkDebugf("Can't parse %s from --clip as an SkIRect.\n", FLAGS_clip[0]);
    623             exit(1);
    624         }
    625 
    626         for (int i = 0; i < FLAGS_scales.count(); i++) {
    627             if (1 != sscanf(FLAGS_scales[i], "%f", &fScales.push_back())) {
    628                 SkDebugf("Can't parse %s from --scales as an SkScalar.\n", FLAGS_scales[i]);
    629                 exit(1);
    630             }
    631         }
    632 
    633         if (2 != sscanf(FLAGS_zoom[0], "%f,%lf", &fZoomMax, &fZoomPeriodMs)) {
    634             SkDebugf("Can't parse %s from --zoom as a zoomMax,zoomPeriodMs.\n", FLAGS_zoom[0]);
    635             exit(1);
    636         }
    637 
    638         if (FLAGS_mpd) {
    639             fUseMPDs.push_back() = true;
    640         }
    641         fUseMPDs.push_back() = false;
    642 
    643         // Prepare the images for decoding
    644         if (!CollectImages(FLAGS_images, &fImages)) {
    645             exit(1);
    646         }
    647         if (!CollectImages(FLAGS_colorImages, &fColorImages)) {
    648             exit(1);
    649         }
    650 
    651         // Choose the candidate color types for image decoding
    652         fColorTypes.push_back(kN32_SkColorType);
    653         if (!FLAGS_simpleCodec) {
    654             fColorTypes.push_back(kRGB_565_SkColorType);
    655             fColorTypes.push_back(kAlpha_8_SkColorType);
    656             fColorTypes.push_back(kGray_8_SkColorType);
    657         }
    658     }
    659 
    660     static sk_sp<SkPicture> ReadPicture(const char* path) {
    661         // Not strictly necessary, as it will be checked again later,
    662         // but helps to avoid a lot of pointless work if we're going to skip it.
    663         if (SkCommandLineFlags::ShouldSkip(FLAGS_match, SkOSPath::Basename(path).c_str())) {
    664             return nullptr;
    665         }
    666 
    667         std::unique_ptr<SkStream> stream = SkStream::MakeFromFile(path);
    668         if (!stream) {
    669             SkDebugf("Could not read %s.\n", path);
    670             return nullptr;
    671         }
    672 
    673         return SkPicture::MakeFromStream(stream.get());
    674     }
    675 
    676     static sk_sp<SkPicture> ReadSVGPicture(const char* path) {
    677         SkFILEStream stream(path);
    678         if (!stream.isValid()) {
    679             SkDebugf("Could not read %s.\n", path);
    680             return nullptr;
    681         }
    682 
    683         sk_sp<SkSVGDOM> svgDom = SkSVGDOM::MakeFromStream(stream);
    684         if (!svgDom) {
    685             SkDebugf("Could not parse %s.\n", path);
    686             return nullptr;
    687         }
    688 
    689         // Use the intrinsic SVG size if available, otherwise fall back to a default value.
    690         static const SkSize kDefaultContainerSize = SkSize::Make(128, 128);
    691         if (svgDom->containerSize().isEmpty()) {
    692             svgDom->setContainerSize(kDefaultContainerSize);
    693         }
    694 
    695         SkPictureRecorder recorder;
    696         svgDom->render(recorder.beginRecording(svgDom->containerSize().width(),
    697                                                svgDom->containerSize().height()));
    698         return recorder.finishRecordingAsPicture();
    699     }
    700 
    701     Benchmark* next() {
    702         std::unique_ptr<Benchmark> bench;
    703         do {
    704             bench.reset(this->rawNext());
    705             if (!bench) {
    706                 return nullptr;
    707             }
    708         } while(SkCommandLineFlags::ShouldSkip(FLAGS_sourceType, fSourceType) ||
    709                 SkCommandLineFlags::ShouldSkip(FLAGS_benchType,  fBenchType));
    710         return bench.release();
    711     }
    712 
    713     Benchmark* rawNext() {
    714         if (fBenches) {
    715             Benchmark* bench = fBenches->factory()(nullptr);
    716             fBenches = fBenches->next();
    717             fSourceType = "bench";
    718             fBenchType  = "micro";
    719             return bench;
    720         }
    721 
    722         while (fGMs) {
    723             std::unique_ptr<skiagm::GM> gm(fGMs->factory()(nullptr));
    724             fGMs = fGMs->next();
    725             if (gm->runAsBench()) {
    726                 fSourceType = "gm";
    727                 fBenchType  = "micro";
    728                 return new GMBench(gm.release());
    729             }
    730         }
    731 
    732         // First add all .skps as RecordingBenches.
    733         while (fCurrentRecording < fSKPs.count()) {
    734             const SkString& path = fSKPs[fCurrentRecording++];
    735             sk_sp<SkPicture> pic = ReadPicture(path.c_str());
    736             if (!pic) {
    737                 continue;
    738             }
    739             SkString name = SkOSPath::Basename(path.c_str());
    740             fSourceType = "skp";
    741             fBenchType  = "recording";
    742             fSKPBytes = static_cast<double>(pic->approximateBytesUsed());
    743             fSKPOps   = pic->approximateOpCount();
    744             return new RecordingBench(name.c_str(), pic.get(), FLAGS_bbh, FLAGS_lite);
    745         }
    746 
    747         // Add all .skps as PipeBenches.
    748         while (fCurrentPiping < fSKPs.count()) {
    749             const SkString& path = fSKPs[fCurrentPiping++];
    750             sk_sp<SkPicture> pic = ReadPicture(path.c_str());
    751             if (!pic) {
    752                 continue;
    753             }
    754             SkString name = SkOSPath::Basename(path.c_str());
    755             fSourceType = "skp";
    756             fBenchType  = "piping";
    757             fSKPBytes = static_cast<double>(pic->approximateBytesUsed());
    758             fSKPOps   = pic->approximateOpCount();
    759             return new PipingBench(name.c_str(), pic.get());
    760         }
    761 
    762         // Add all .skps as DeserializePictureBenchs.
    763         while (fCurrentDeserialPicture < fSKPs.count()) {
    764             const SkString& path = fSKPs[fCurrentDeserialPicture++];
    765             sk_sp<SkData> data = SkData::MakeFromFileName(path.c_str());
    766             if (!data) {
    767                 continue;
    768             }
    769             SkString name = SkOSPath::Basename(path.c_str());
    770             fSourceType = "skp";
    771             fBenchType  = "deserial";
    772             fSKPBytes = static_cast<double>(data->size());
    773             fSKPOps   = 0;
    774             return new DeserializePictureBench(name.c_str(), std::move(data));
    775         }
    776 
    777         // Then once each for each scale as SKPBenches (playback).
    778         while (fCurrentScale < fScales.count()) {
    779             while (fCurrentSKP < fSKPs.count()) {
    780                 const SkString& path = fSKPs[fCurrentSKP];
    781                 sk_sp<SkPicture> pic = ReadPicture(path.c_str());
    782                 if (!pic) {
    783                     fCurrentSKP++;
    784                     continue;
    785                 }
    786 
    787                 while (fCurrentUseMPD < fUseMPDs.count()) {
    788                     if (FLAGS_bbh) {
    789                         // The SKP we read off disk doesn't have a BBH.  Re-record so it grows one.
    790                         SkRTreeFactory factory;
    791                         SkPictureRecorder recorder;
    792                         pic->playback(recorder.beginRecording(pic->cullRect().width(),
    793                                                               pic->cullRect().height(),
    794                                                               &factory,
    795                                                               0));
    796                         pic = recorder.finishRecordingAsPicture();
    797                     }
    798                     SkString name = SkOSPath::Basename(path.c_str());
    799                     fSourceType = "skp";
    800                     fBenchType = "playback";
    801                     return new SKPBench(name.c_str(), pic.get(), fClip, fScales[fCurrentScale],
    802                                         fUseMPDs[fCurrentUseMPD++], FLAGS_loopSKP);
    803                 }
    804                 fCurrentUseMPD = 0;
    805                 fCurrentSKP++;
    806             }
    807 
    808             while (fCurrentSVG++ < fSVGs.count()) {
    809                 const char* path = fSVGs[fCurrentSVG - 1].c_str();
    810                 if (sk_sp<SkPicture> pic = ReadSVGPicture(path)) {
    811                     fSourceType = "svg";
    812                     fBenchType = "playback";
    813                     return new SKPBench(SkOSPath::Basename(path).c_str(), pic.get(), fClip,
    814                                         fScales[fCurrentScale], false, FLAGS_loopSKP);
    815                 }
    816             }
    817 
    818             fCurrentSKP = 0;
    819             fCurrentSVG = 0;
    820             fCurrentScale++;
    821         }
    822 
    823         // Now loop over each skp again if we have an animation
    824         if (fZoomMax != 1.0f && fZoomPeriodMs > 0) {
    825             while (fCurrentAnimSKP < fSKPs.count()) {
    826                 const SkString& path = fSKPs[fCurrentAnimSKP];
    827                 sk_sp<SkPicture> pic = ReadPicture(path.c_str());
    828                 if (!pic) {
    829                     fCurrentAnimSKP++;
    830                     continue;
    831                 }
    832 
    833                 fCurrentAnimSKP++;
    834                 SkString name = SkOSPath::Basename(path.c_str());
    835                 sk_sp<SKPAnimationBench::Animation> animation(
    836                     SKPAnimationBench::CreateZoomAnimation(fZoomMax, fZoomPeriodMs));
    837                 return new SKPAnimationBench(name.c_str(), pic.get(), fClip, animation.get(),
    838                                              FLAGS_loopSKP);
    839             }
    840         }
    841 
    842         for (; fCurrentCodec < fImages.count(); fCurrentCodec++) {
    843             fSourceType = "image";
    844             fBenchType = "skcodec";
    845             const SkString& path = fImages[fCurrentCodec];
    846             if (SkCommandLineFlags::ShouldSkip(FLAGS_match, path.c_str())) {
    847                 continue;
    848             }
    849             sk_sp<SkData> encoded(SkData::MakeFromFileName(path.c_str()));
    850             std::unique_ptr<SkCodec> codec(SkCodec::MakeFromData(encoded));
    851             if (!codec) {
    852                 // Nothing to time.
    853                 SkDebugf("Cannot find codec for %s\n", path.c_str());
    854                 continue;
    855             }
    856 
    857             while (fCurrentColorType < fColorTypes.count()) {
    858                 const SkColorType colorType = fColorTypes[fCurrentColorType];
    859 
    860                 SkAlphaType alphaType = codec->getInfo().alphaType();
    861                 if (FLAGS_simpleCodec) {
    862                     if (kUnpremul_SkAlphaType == alphaType) {
    863                         alphaType = kPremul_SkAlphaType;
    864                     }
    865 
    866                     fCurrentColorType++;
    867                 } else {
    868                     switch (alphaType) {
    869                         case kOpaque_SkAlphaType:
    870                             // We only need to test one alpha type (opaque).
    871                             fCurrentColorType++;
    872                             break;
    873                         case kUnpremul_SkAlphaType:
    874                         case kPremul_SkAlphaType:
    875                             if (0 == fCurrentAlphaType) {
    876                                 // Test unpremul first.
    877                                 alphaType = kUnpremul_SkAlphaType;
    878                                 fCurrentAlphaType++;
    879                             } else {
    880                                 // Test premul.
    881                                 alphaType = kPremul_SkAlphaType;
    882                                 fCurrentAlphaType = 0;
    883                                 fCurrentColorType++;
    884                             }
    885                             break;
    886                         default:
    887                             SkASSERT(false);
    888                             fCurrentColorType++;
    889                             break;
    890                     }
    891                 }
    892 
    893                 // Make sure we can decode to this color type and alpha type.
    894                 SkImageInfo info =
    895                         codec->getInfo().makeColorType(colorType).makeAlphaType(alphaType);
    896                 const size_t rowBytes = info.minRowBytes();
    897                 SkAutoMalloc storage(info.computeByteSize(rowBytes));
    898 
    899                 const SkCodec::Result result = codec->getPixels(
    900                         info, storage.get(), rowBytes);
    901                 switch (result) {
    902                     case SkCodec::kSuccess:
    903                     case SkCodec::kIncompleteInput:
    904                         return new CodecBench(SkOSPath::Basename(path.c_str()),
    905                                               encoded.get(), colorType, alphaType);
    906                     case SkCodec::kInvalidConversion:
    907                         // This is okay. Not all conversions are valid.
    908                         break;
    909                     default:
    910                         // This represents some sort of failure.
    911                         SkASSERT(false);
    912                         break;
    913                 }
    914             }
    915             fCurrentColorType = 0;
    916         }
    917 
    918         // Run AndroidCodecBenches
    919         const int sampleSizes[] = { 2, 4, 8 };
    920         for (; fCurrentAndroidCodec < fImages.count(); fCurrentAndroidCodec++) {
    921             fSourceType = "image";
    922             fBenchType = "skandroidcodec";
    923 
    924             const SkString& path = fImages[fCurrentAndroidCodec];
    925             if (SkCommandLineFlags::ShouldSkip(FLAGS_match, path.c_str())) {
    926                 continue;
    927             }
    928             sk_sp<SkData> encoded(SkData::MakeFromFileName(path.c_str()));
    929             std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::MakeFromData(encoded));
    930             if (!codec) {
    931                 // Nothing to time.
    932                 SkDebugf("Cannot find codec for %s\n", path.c_str());
    933                 continue;
    934             }
    935 
    936             while (fCurrentSampleSize < (int) SK_ARRAY_COUNT(sampleSizes)) {
    937                 int sampleSize = sampleSizes[fCurrentSampleSize];
    938                 fCurrentSampleSize++;
    939                 if (10 * sampleSize > SkTMin(codec->getInfo().width(), codec->getInfo().height())) {
    940                     // Avoid benchmarking scaled decodes of already small images.
    941                     break;
    942                 }
    943 
    944                 return new AndroidCodecBench(SkOSPath::Basename(path.c_str()),
    945                                              encoded.get(), sampleSize);
    946             }
    947             fCurrentSampleSize = 0;
    948         }
    949 
    950         // Run the BRDBenches
    951         // We intend to create benchmarks that model the use cases in
    952         // android/libraries/social/tiledimage.  In this library, an image is decoded in 512x512
    953         // tiles.  The image can be translated freely, so the location of a tile may be anywhere in
    954         // the image.  For that reason, we will benchmark decodes in five representative locations
    955         // in the image.  Additionally, this use case utilizes power of two scaling, so we will
    956         // test on power of two sample sizes.  The output tile is always 512x512, so, when a
    957         // sampleSize is used, the size of the subset that is decoded is always
    958         // (sampleSize*512)x(sampleSize*512).
    959         // There are a few good reasons to only test on power of two sample sizes at this time:
    960         //     All use cases we are aware of only scale by powers of two.
    961         //     PNG decodes use the indicated sampling strategy regardless of the sample size, so
    962         //         these tests are sufficient to provide good coverage of our scaling options.
    963         const uint32_t brdSampleSizes[] = { 1, 2, 4, 8, 16 };
    964         const uint32_t minOutputSize = 512;
    965         for (; fCurrentBRDImage < fImages.count(); fCurrentBRDImage++) {
    966             fSourceType = "image";
    967             fBenchType = "BRD";
    968 
    969             const SkString& path = fImages[fCurrentBRDImage];
    970             if (SkCommandLineFlags::ShouldSkip(FLAGS_match, path.c_str())) {
    971                 continue;
    972             }
    973 
    974             while (fCurrentColorType < fColorTypes.count()) {
    975                 while (fCurrentSampleSize < (int) SK_ARRAY_COUNT(brdSampleSizes)) {
    976                     while (fCurrentSubsetType <= kLastSingle_SubsetType) {
    977 
    978                         sk_sp<SkData> encoded(SkData::MakeFromFileName(path.c_str()));
    979                         const SkColorType colorType = fColorTypes[fCurrentColorType];
    980                         uint32_t sampleSize = brdSampleSizes[fCurrentSampleSize];
    981                         int currentSubsetType = fCurrentSubsetType++;
    982 
    983                         int width = 0;
    984                         int height = 0;
    985                         if (!valid_brd_bench(encoded, colorType, sampleSize, minOutputSize,
    986                                 &width, &height)) {
    987                             break;
    988                         }
    989 
    990                         SkString basename = SkOSPath::Basename(path.c_str());
    991                         SkIRect subset;
    992                         const uint32_t subsetSize = sampleSize * minOutputSize;
    993                         switch (currentSubsetType) {
    994                             case kTopLeft_SubsetType:
    995                                 basename.append("_TopLeft");
    996                                 subset = SkIRect::MakeXYWH(0, 0, subsetSize, subsetSize);
    997                                 break;
    998                             case kTopRight_SubsetType:
    999                                 basename.append("_TopRight");
   1000                                 subset = SkIRect::MakeXYWH(width - subsetSize, 0, subsetSize,
   1001                                         subsetSize);
   1002                                 break;
   1003                             case kMiddle_SubsetType:
   1004                                 basename.append("_Middle");
   1005                                 subset = SkIRect::MakeXYWH((width - subsetSize) / 2,
   1006                                         (height - subsetSize) / 2, subsetSize, subsetSize);
   1007                                 break;
   1008                             case kBottomLeft_SubsetType:
   1009                                 basename.append("_BottomLeft");
   1010                                 subset = SkIRect::MakeXYWH(0, height - subsetSize, subsetSize,
   1011                                         subsetSize);
   1012                                 break;
   1013                             case kBottomRight_SubsetType:
   1014                                 basename.append("_BottomRight");
   1015                                 subset = SkIRect::MakeXYWH(width - subsetSize,
   1016                                         height - subsetSize, subsetSize, subsetSize);
   1017                                 break;
   1018                             default:
   1019                                 SkASSERT(false);
   1020                         }
   1021 
   1022                         return new BitmapRegionDecoderBench(basename.c_str(), encoded.get(),
   1023                                 colorType, sampleSize, subset);
   1024                     }
   1025                     fCurrentSubsetType = 0;
   1026                     fCurrentSampleSize++;
   1027                 }
   1028                 fCurrentSampleSize = 0;
   1029                 fCurrentColorType++;
   1030             }
   1031             fCurrentColorType = 0;
   1032         }
   1033 
   1034         while (fCurrentColorImage < fColorImages.count()) {
   1035             fSourceType = "colorimage";
   1036             fBenchType = "skcolorcodec";
   1037             const SkString& path = fColorImages[fCurrentColorImage];
   1038             fCurrentColorImage++;
   1039             sk_sp<SkData> encoded = SkData::MakeFromFileName(path.c_str());
   1040             if (encoded) {
   1041                 return new ColorCodecBench(SkOSPath::Basename(path.c_str()).c_str(),
   1042                                            std::move(encoded));
   1043             } else {
   1044                 SkDebugf("Could not read file %s.\n", path.c_str());
   1045             }
   1046         }
   1047 
   1048         return nullptr;
   1049     }
   1050 
   1051     void fillCurrentOptions(ResultsWriter* log) const {
   1052         log->configOption("source_type", fSourceType);
   1053         log->configOption("bench_type",  fBenchType);
   1054         if (0 == strcmp(fSourceType, "skp")) {
   1055             log->configOption("clip",
   1056                     SkStringPrintf("%d %d %d %d", fClip.fLeft, fClip.fTop,
   1057                                                   fClip.fRight, fClip.fBottom).c_str());
   1058             SkASSERT_RELEASE(fCurrentScale < fScales.count());  // debugging paranoia
   1059             log->configOption("scale", SkStringPrintf("%.2g", fScales[fCurrentScale]).c_str());
   1060             if (fCurrentUseMPD > 0) {
   1061                 SkASSERT(1 == fCurrentUseMPD || 2 == fCurrentUseMPD);
   1062                 log->configOption("multi_picture_draw", fUseMPDs[fCurrentUseMPD-1] ? "true" : "false");
   1063             }
   1064         }
   1065         if (0 == strcmp(fBenchType, "recording")) {
   1066             log->metric("bytes", fSKPBytes);
   1067             log->metric("ops",   fSKPOps);
   1068         }
   1069     }
   1070 
   1071 private:
   1072     enum SubsetType {
   1073         kTopLeft_SubsetType     = 0,
   1074         kTopRight_SubsetType    = 1,
   1075         kMiddle_SubsetType      = 2,
   1076         kBottomLeft_SubsetType  = 3,
   1077         kBottomRight_SubsetType = 4,
   1078         kTranslate_SubsetType   = 5,
   1079         kZoom_SubsetType        = 6,
   1080         kLast_SubsetType        = kZoom_SubsetType,
   1081         kLastSingle_SubsetType  = kBottomRight_SubsetType,
   1082     };
   1083 
   1084     const BenchRegistry* fBenches;
   1085     const skiagm::GMRegistry* fGMs;
   1086     SkIRect            fClip;
   1087     SkTArray<SkScalar> fScales;
   1088     SkTArray<SkString> fSKPs;
   1089     SkTArray<SkString> fSVGs;
   1090     SkTArray<bool>     fUseMPDs;
   1091     SkTArray<SkString> fImages;
   1092     SkTArray<SkString> fColorImages;
   1093     SkTArray<SkColorType, true> fColorTypes;
   1094     SkScalar           fZoomMax;
   1095     double             fZoomPeriodMs;
   1096 
   1097     double fSKPBytes, fSKPOps;
   1098 
   1099     const char* fSourceType;  // What we're benching: bench, GM, SKP, ...
   1100     const char* fBenchType;   // How we bench it: micro, recording, playback, ...
   1101     int fCurrentRecording;
   1102     int fCurrentPiping;
   1103     int fCurrentDeserialPicture;
   1104     int fCurrentScale;
   1105     int fCurrentSKP;
   1106     int fCurrentSVG;
   1107     int fCurrentUseMPD;
   1108     int fCurrentCodec;
   1109     int fCurrentAndroidCodec;
   1110     int fCurrentBRDImage;
   1111     int fCurrentColorImage;
   1112     int fCurrentColorType;
   1113     int fCurrentAlphaType;
   1114     int fCurrentSubsetType;
   1115     int fCurrentSampleSize;
   1116     int fCurrentAnimSKP;
   1117 };
   1118 
   1119 // Some runs (mostly, Valgrind) are so slow that the bot framework thinks we've hung.
   1120 // This prints something every once in a while so that it knows we're still working.
   1121 static void start_keepalive() {
   1122     static std::thread* intentionallyLeaked = new std::thread([]{
   1123         for (;;) {
   1124             static const int kSec = 1200;
   1125         #if defined(SK_BUILD_FOR_WIN)
   1126             Sleep(kSec * 1000);
   1127         #else
   1128             sleep(kSec);
   1129         #endif
   1130             SkDebugf("\nBenchmarks still running...\n");
   1131         }
   1132     });
   1133     (void)intentionallyLeaked;
   1134 }
   1135 
   1136 int main(int argc, char** argv) {
   1137     SkCommandLineFlags::Parse(argc, argv);
   1138 
   1139     initializeEventTracingForTools();
   1140 
   1141 #if defined(SK_BUILD_FOR_IOS)
   1142     cd_Documents();
   1143 #endif
   1144     SetupCrashHandler();
   1145     SkAutoGraphics ag;
   1146     SkTaskGroup::Enabler enabled(FLAGS_threads);
   1147 
   1148 #if SK_SUPPORT_GPU
   1149     SetCtxOptionsFromCommonFlags(&grContextOpts);
   1150 #endif
   1151 
   1152     if (FLAGS_veryVerbose) {
   1153         FLAGS_verbose = true;
   1154     }
   1155 
   1156     if (kAutoTuneLoops != FLAGS_loops) {
   1157         FLAGS_samples     = 1;
   1158         FLAGS_gpuFrameLag = 0;
   1159     }
   1160 
   1161     if (!FLAGS_writePath.isEmpty()) {
   1162         SkDebugf("Writing files to %s.\n", FLAGS_writePath[0]);
   1163         if (!sk_mkdir(FLAGS_writePath[0])) {
   1164             SkDebugf("Could not create %s. Files won't be written.\n", FLAGS_writePath[0]);
   1165             FLAGS_writePath.set(0, nullptr);
   1166         }
   1167     }
   1168 
   1169     std::unique_ptr<ResultsWriter> log(new ResultsWriter);
   1170     if (!FLAGS_outResultsFile.isEmpty()) {
   1171 #if defined(SK_RELEASE)
   1172         log.reset(new NanoJSONResultsWriter(FLAGS_outResultsFile[0]));
   1173 #else
   1174         SkDebugf("I'm ignoring --outResultsFile because this is a Debug build.");
   1175         return 1;
   1176 #endif
   1177     }
   1178 
   1179     if (1 == FLAGS_properties.count() % 2) {
   1180         SkDebugf("ERROR: --properties must be passed with an even number of arguments.\n");
   1181         return 1;
   1182     }
   1183     for (int i = 1; i < FLAGS_properties.count(); i += 2) {
   1184         log->property(FLAGS_properties[i-1], FLAGS_properties[i]);
   1185     }
   1186 
   1187     if (1 == FLAGS_key.count() % 2) {
   1188         SkDebugf("ERROR: --key must be passed with an even number of arguments.\n");
   1189         return 1;
   1190     }
   1191     for (int i = 1; i < FLAGS_key.count(); i += 2) {
   1192         log->key(FLAGS_key[i-1], FLAGS_key[i]);
   1193     }
   1194 
   1195     const double overhead = estimate_timer_overhead();
   1196     SkDebugf("Timer overhead: %s\n", HUMANIZE(overhead));
   1197 
   1198     SkTArray<double> samples;
   1199 
   1200     if (kAutoTuneLoops != FLAGS_loops) {
   1201         SkDebugf("Fixed number of loops; times would only be misleading so we won't print them.\n");
   1202     } else if (FLAGS_quiet) {
   1203         SkDebugf("! -> high variance, ? -> moderate variance\n");
   1204         SkDebugf("    micros   \tbench\n");
   1205     } else if (FLAGS_ms) {
   1206         SkDebugf("curr/maxrss\tloops\tmin\tmedian\tmean\tmax\tstddev\tsamples\tconfig\tbench\n");
   1207     } else {
   1208         SkDebugf("curr/maxrss\tloops\tmin\tmedian\tmean\tmax\tstddev\t%-*s\tconfig\tbench\n",
   1209                  FLAGS_samples, "samples");
   1210     }
   1211 
   1212     SkTArray<Config> configs;
   1213     create_configs(&configs);
   1214 
   1215     if (FLAGS_keepAlive) {
   1216         start_keepalive();
   1217     }
   1218 
   1219     gSkUseAnalyticAA = FLAGS_analyticAA;
   1220     gSkUseDeltaAA = FLAGS_deltaAA;
   1221 
   1222     if (FLAGS_forceDeltaAA) {
   1223         gSkForceDeltaAA = true;
   1224     }
   1225     if (FLAGS_forceAnalyticAA) {
   1226         gSkForceAnalyticAA = true;
   1227     }
   1228     if (FLAGS_forceRasterPipeline) {
   1229         gSkForceRasterPipelineBlitter = true;
   1230     }
   1231 
   1232     int runs = 0;
   1233     BenchmarkStream benchStream;
   1234     while (Benchmark* b = benchStream.next()) {
   1235         std::unique_ptr<Benchmark> bench(b);
   1236         if (SkCommandLineFlags::ShouldSkip(FLAGS_match, bench->getUniqueName())) {
   1237             continue;
   1238         }
   1239 
   1240         if (!configs.empty()) {
   1241             log->bench(bench->getUniqueName(), bench->getSize().fX, bench->getSize().fY);
   1242             bench->delayedSetup();
   1243         }
   1244         for (int i = 0; i < configs.count(); ++i) {
   1245             Target* target = is_enabled(b, configs[i]);
   1246             if (!target) {
   1247                 continue;
   1248             }
   1249 
   1250             // During HWUI output this canvas may be nullptr.
   1251             SkCanvas* canvas = target->getCanvas();
   1252             const char* config = target->config.name.c_str();
   1253 
   1254             if (FLAGS_pre_log || FLAGS_dryRun) {
   1255                 SkDebugf("Running %s\t%s\n"
   1256                          , bench->getUniqueName()
   1257                          , config);
   1258                 if (FLAGS_dryRun) {
   1259                     continue;
   1260                 }
   1261             }
   1262 
   1263             TRACE_EVENT2("skia", "Benchmark", "name", TRACE_STR_COPY(bench->getUniqueName()),
   1264                                               "config", TRACE_STR_COPY(config));
   1265 
   1266             target->setup();
   1267             bench->perCanvasPreDraw(canvas);
   1268 
   1269             int maxFrameLag;
   1270             int loops = target->needsFrameTiming(&maxFrameLag)
   1271                 ? setup_gpu_bench(target, bench.get(), maxFrameLag)
   1272                 : setup_cpu_bench(overhead, target, bench.get());
   1273 
   1274             if (kFailedLoops == loops) {
   1275                 // Can't be timed.  A warning note has already been printed.
   1276                 cleanup_run(target);
   1277                 continue;
   1278             }
   1279 
   1280             if (runs == 0 && FLAGS_ms < 1000) {
   1281                 // Run the first bench for 1000ms to warm up the nanobench if FLAGS_ms < 1000.
   1282                 // Otherwise, the first few benches' measurements will be inaccurate.
   1283                 auto stop = now_ms() + 1000;
   1284                 do {
   1285                     time(loops, bench.get(), target);
   1286                 } while (now_ms() < stop);
   1287             }
   1288 
   1289             if (FLAGS_ms) {
   1290                 samples.reset();
   1291                 auto stop = now_ms() + FLAGS_ms;
   1292                 do {
   1293                     samples.push_back(time(loops, bench.get(), target) / loops);
   1294                 } while (now_ms() < stop);
   1295             } else {
   1296                 samples.reset(FLAGS_samples);
   1297                 for (int s = 0; s < FLAGS_samples; s++) {
   1298                     samples[s] = time(loops, bench.get(), target) / loops;
   1299                 }
   1300             }
   1301 
   1302 #if SK_SUPPORT_GPU
   1303             SkTArray<SkString> keys;
   1304             SkTArray<double> values;
   1305             bool gpuStatsDump = FLAGS_gpuStatsDump && Benchmark::kGPU_Backend == configs[i].backend;
   1306             if (gpuStatsDump) {
   1307                 // TODO cache stats
   1308                 bench->getGpuStats(canvas, &keys, &values);
   1309             }
   1310 #endif
   1311 
   1312             bench->perCanvasPostDraw(canvas);
   1313 
   1314             if (Benchmark::kNonRendering_Backend != target->config.backend &&
   1315                 !FLAGS_writePath.isEmpty() && FLAGS_writePath[0]) {
   1316                 SkString pngFilename = SkOSPath::Join(FLAGS_writePath[0], config);
   1317                 pngFilename = SkOSPath::Join(pngFilename.c_str(), bench->getUniqueName());
   1318                 pngFilename.append(".png");
   1319                 write_canvas_png(target, pngFilename);
   1320             }
   1321 
   1322             Stats stats(samples);
   1323             log->config(config);
   1324             log->configOption("name", bench->getName());
   1325             benchStream.fillCurrentOptions(log.get());
   1326             target->fillOptions(log.get());
   1327             log->metric("min_ms",    stats.min);
   1328             log->metrics("samples",    samples);
   1329 #if SK_SUPPORT_GPU
   1330             if (gpuStatsDump) {
   1331                 // dump to json, only SKPBench currently returns valid keys / values
   1332                 SkASSERT(keys.count() == values.count());
   1333                 for (int i = 0; i < keys.count(); i++) {
   1334                     log->metric(keys[i].c_str(), values[i]);
   1335                 }
   1336             }
   1337 #endif
   1338 
   1339             if (runs++ % FLAGS_flushEvery == 0) {
   1340                 log->flush();
   1341             }
   1342 
   1343             if (kAutoTuneLoops != FLAGS_loops) {
   1344                 if (configs.count() == 1) {
   1345                     config = ""; // Only print the config if we run the same bench on more than one.
   1346                 }
   1347                 SkDebugf("%4d/%-4dMB\t%s\t%s\n"
   1348                          , sk_tools::getCurrResidentSetSizeMB()
   1349                          , sk_tools::getMaxResidentSetSizeMB()
   1350                          , bench->getUniqueName()
   1351                          , config);
   1352             } else if (FLAGS_quiet) {
   1353                 const char* mark = " ";
   1354                 const double stddev_percent = 100 * sqrt(stats.var) / stats.mean;
   1355                 if (stddev_percent >  5) mark = "?";
   1356                 if (stddev_percent > 10) mark = "!";
   1357 
   1358                 SkDebugf("%10.2f %s\t%s\t%s\n",
   1359                          stats.median*1e3, mark, bench->getUniqueName(), config);
   1360             } else if (FLAGS_csv) {
   1361                 const double stddev_percent = 100 * sqrt(stats.var) / stats.mean;
   1362                 SkDebugf("%g,%g,%g,%g,%g,%s,%s\n"
   1363                          , stats.min
   1364                          , stats.median
   1365                          , stats.mean
   1366                          , stats.max
   1367                          , stddev_percent
   1368                          , config
   1369                          , bench->getUniqueName()
   1370                          );
   1371             } else {
   1372                 const char* format = "%4d/%-4dMB\t%d\t%s\t%s\t%s\t%s\t%.0f%%\t%s\t%s\t%s\n";
   1373                 const double stddev_percent = 100 * sqrt(stats.var) / stats.mean;
   1374                 SkDebugf(format
   1375                         , sk_tools::getCurrResidentSetSizeMB()
   1376                         , sk_tools::getMaxResidentSetSizeMB()
   1377                         , loops
   1378                         , HUMANIZE(stats.min)
   1379                         , HUMANIZE(stats.median)
   1380                         , HUMANIZE(stats.mean)
   1381                         , HUMANIZE(stats.max)
   1382                         , stddev_percent
   1383                         , FLAGS_ms ? to_string(samples.count()).c_str() : stats.plot.c_str()
   1384                         , config
   1385                         , bench->getUniqueName()
   1386                         );
   1387             }
   1388 
   1389 #if SK_SUPPORT_GPU
   1390             if (FLAGS_gpuStats && Benchmark::kGPU_Backend == configs[i].backend) {
   1391                 target->dumpStats();
   1392             }
   1393 #endif
   1394 
   1395             if (FLAGS_verbose) {
   1396                 SkDebugf("Samples:  ");
   1397                 for (int i = 0; i < samples.count(); i++) {
   1398                     SkDebugf("%s  ", HUMANIZE(samples[i]));
   1399                 }
   1400                 SkDebugf("%s\n", bench->getUniqueName());
   1401             }
   1402             cleanup_run(target);
   1403         }
   1404     }
   1405 
   1406     SkGraphics::PurgeAllCaches();
   1407 
   1408     log->bench("memory_usage", 0,0);
   1409     log->config("meta");
   1410     log->metric("max_rss_mb", sk_tools::getMaxResidentSetSizeMB());
   1411 
   1412     return 0;
   1413 }
   1414