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 = 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.
    497 void 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.
    523 static 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 
    555 static 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 
    577 static void cleanup_run(Target* target) {
    578     delete target;
    579 }
    580 
    581 static 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 
    596 class BenchmarkStream {
    597 public:
    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 
   1070 private:
   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.
   1120 static 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 
   1135 int 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