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