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 "Benchmark.h"
     13 #include "CodecBench.h"
     14 #include "CrashHandler.h"
     15 #include "DecodingBench.h"
     16 #include "DecodingSubsetBench.h"
     17 #include "GMBench.h"
     18 #include "ProcStats.h"
     19 #include "ResultsWriter.h"
     20 #include "RecordingBench.h"
     21 #include "SKPAnimationBench.h"
     22 #include "SKPBench.h"
     23 #include "Stats.h"
     24 #include "Timer.h"
     25 
     26 #include "SkBBoxHierarchy.h"
     27 #include "SkCanvas.h"
     28 #include "SkCodec.h"
     29 #include "SkCommonFlags.h"
     30 #include "SkData.h"
     31 #include "SkForceLinking.h"
     32 #include "SkGraphics.h"
     33 #include "SkOSFile.h"
     34 #include "SkPictureRecorder.h"
     35 #include "SkPictureUtils.h"
     36 #include "SkString.h"
     37 #include "SkSurface.h"
     38 #include "SkTaskGroup.h"
     39 
     40 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
     41     #include "nanobenchAndroid.h"
     42 #endif
     43 
     44 #if SK_SUPPORT_GPU
     45     #include "gl/GrGLDefines.h"
     46     #include "GrContextFactory.h"
     47     SkAutoTDelete<GrContextFactory> gGrFactory;
     48 #endif
     49 
     50 __SK_FORCE_IMAGE_DECODER_LINKING;
     51 
     52 static const int kAutoTuneLoops = 0;
     53 
     54 static const int kDefaultLoops =
     55 #ifdef SK_DEBUG
     56     1;
     57 #else
     58     kAutoTuneLoops;
     59 #endif
     60 
     61 static SkString loops_help_txt() {
     62     SkString help;
     63     help.printf("Number of times to run each bench. Set this to %d to auto-"
     64                 "tune for each bench. Timings are only reported when auto-tuning.",
     65                 kAutoTuneLoops);
     66     return help;
     67 }
     68 
     69 DEFINE_int32(loops, kDefaultLoops, loops_help_txt().c_str());
     70 
     71 DEFINE_int32(samples, 10, "Number of samples to measure for each bench.");
     72 DEFINE_int32(overheadLoops, 100000, "Loops to estimate timer overhead.");
     73 DEFINE_double(overheadGoal, 0.0001,
     74               "Loop until timer overhead is at most this fraction of our measurments.");
     75 DEFINE_double(gpuMs, 5, "Target bench time in millseconds for GPU.");
     76 DEFINE_int32(gpuFrameLag, 5, "Overestimate of maximum number of frames GPU allows to lag.");
     77 DEFINE_bool(gpuCompressAlphaMasks, false, "Compress masks generated from falling back to "
     78                                           "software path rendering.");
     79 
     80 DEFINE_string(outResultsFile, "", "If given, write results here as JSON.");
     81 DEFINE_int32(maxCalibrationAttempts, 3,
     82              "Try up to this many times to guess loops for a bench, or skip the bench.");
     83 DEFINE_int32(maxLoops, 1000000, "Never run a bench more times than this.");
     84 DEFINE_string(clip, "0,0,1000,1000", "Clip for SKPs.");
     85 DEFINE_string(scales, "1.0", "Space-separated scales for SKPs.");
     86 DEFINE_string(zoom, "1.0,1", "Comma-separated scale,step zoom factors for SKPs.");
     87 DEFINE_bool(bbh, true, "Build a BBH for SKPs?");
     88 DEFINE_bool(mpd, true, "Use MultiPictureDraw for the SKPs?");
     89 DEFINE_int32(flushEvery, 10, "Flush --outResultsFile every Nth run.");
     90 DEFINE_bool(resetGpuContext, true, "Reset the GrContext before running each test.");
     91 DEFINE_bool(gpuStats, false, "Print GPU stats after each gpu benchmark?");
     92 
     93 static SkString humanize(double ms) {
     94     if (FLAGS_verbose) return SkStringPrintf("%llu", (uint64_t)(ms*1e6));
     95     return HumanizeMs(ms);
     96 }
     97 #define HUMANIZE(ms) humanize(ms).c_str()
     98 
     99 bool Target::init(SkImageInfo info, Benchmark* bench) {
    100     if (Benchmark::kRaster_Backend == config.backend) {
    101         this->surface.reset(SkSurface::NewRaster(info));
    102         if (!this->surface.get()) {
    103             return false;
    104         }
    105     }
    106     return true;
    107 }
    108 bool Target::capturePixels(SkBitmap* bmp) {
    109     SkCanvas* canvas = this->getCanvas();
    110     if (!canvas) {
    111         return false;
    112     }
    113     bmp->setInfo(canvas->imageInfo());
    114     if (!canvas->readPixels(bmp, 0, 0)) {
    115         SkDebugf("Can't read canvas pixels.\n");
    116         return false;
    117     }
    118     return true;
    119 }
    120 
    121 #if SK_SUPPORT_GPU
    122 struct GPUTarget : public Target {
    123     explicit GPUTarget(const Config& c) : Target(c), gl(NULL) { }
    124     SkGLContext* gl;
    125 
    126     void setup() override {
    127         this->gl->makeCurrent();
    128         // Make sure we're done with whatever came before.
    129         SK_GL(*this->gl, Finish());
    130     }
    131     void endTiming() override {
    132         if (this->gl) {
    133             SK_GL(*this->gl, Flush());
    134             this->gl->swapBuffers();
    135         }
    136     }
    137     void fence() override {
    138         SK_GL(*this->gl, Finish());
    139     }
    140 
    141     bool needsFrameTiming() const override { return true; }
    142     bool init(SkImageInfo info, Benchmark* bench) override {
    143         uint32_t flags = this->config.useDFText ? SkSurfaceProps::kUseDistanceFieldFonts_Flag : 0;
    144         SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType);
    145         this->surface.reset(SkSurface::NewRenderTarget(gGrFactory->get(this->config.ctxType),
    146                                                          SkSurface::kNo_Budgeted, info,
    147                                                          this->config.samples, &props));
    148         this->gl = gGrFactory->getGLContext(this->config.ctxType);
    149         if (!this->surface.get()) {
    150             return false;
    151         }
    152         return true;
    153     }
    154     void fillOptions(ResultsWriter* log) override {
    155         const GrGLubyte* version;
    156         SK_GL_RET(*this->gl, version, GetString(GR_GL_VERSION));
    157         log->configOption("GL_VERSION", (const char*)(version));
    158 
    159         SK_GL_RET(*this->gl, version, GetString(GR_GL_RENDERER));
    160         log->configOption("GL_RENDERER", (const char*) version);
    161 
    162         SK_GL_RET(*this->gl, version, GetString(GR_GL_VENDOR));
    163         log->configOption("GL_VENDOR", (const char*) version);
    164 
    165         SK_GL_RET(*this->gl, version, GetString(GR_GL_SHADING_LANGUAGE_VERSION));
    166         log->configOption("GL_SHADING_LANGUAGE_VERSION", (const char*) version);
    167     }
    168 };
    169 
    170 #endif
    171 
    172 static double time(int loops, Benchmark* bench, Target* target) {
    173     SkCanvas* canvas = target->getCanvas();
    174     if (canvas) {
    175         canvas->clear(SK_ColorWHITE);
    176     }
    177     WallTimer timer;
    178     timer.start();
    179     canvas = target->beginTiming(canvas);
    180     bench->draw(loops, canvas);
    181     if (canvas) {
    182         canvas->flush();
    183     }
    184     target->endTiming();
    185     timer.end();
    186     return timer.fWall;
    187 }
    188 
    189 static double estimate_timer_overhead() {
    190     double overhead = 0;
    191     for (int i = 0; i < FLAGS_overheadLoops; i++) {
    192         WallTimer timer;
    193         timer.start();
    194         timer.end();
    195         overhead += timer.fWall;
    196     }
    197     return overhead / FLAGS_overheadLoops;
    198 }
    199 
    200 static int detect_forever_loops(int loops) {
    201     // look for a magic run-forever value
    202     if (loops < 0) {
    203         loops = SK_MaxS32;
    204     }
    205     return loops;
    206 }
    207 
    208 static int clamp_loops(int loops) {
    209     if (loops < 1) {
    210         SkDebugf("ERROR: clamping loops from %d to 1. "
    211                  "There's probably something wrong with the bench.\n", loops);
    212         return 1;
    213     }
    214     if (loops > FLAGS_maxLoops) {
    215         SkDebugf("WARNING: clamping loops from %d to FLAGS_maxLoops, %d.\n", loops, FLAGS_maxLoops);
    216         return FLAGS_maxLoops;
    217     }
    218     return loops;
    219 }
    220 
    221 static bool write_canvas_png(Target* target, const SkString& filename) {
    222 
    223     if (filename.isEmpty()) {
    224         return false;
    225     }
    226     if (target->getCanvas() &&
    227         kUnknown_SkColorType == target->getCanvas()->imageInfo().colorType()) {
    228         return false;
    229     }
    230 
    231     SkBitmap bmp;
    232 
    233     if (!target->capturePixels(&bmp)) {
    234         return false;
    235     }
    236 
    237     SkString dir = SkOSPath::Dirname(filename.c_str());
    238     if (!sk_mkdir(dir.c_str())) {
    239         SkDebugf("Can't make dir %s.\n", dir.c_str());
    240         return false;
    241     }
    242     SkFILEWStream stream(filename.c_str());
    243     if (!stream.isValid()) {
    244         SkDebugf("Can't write %s.\n", filename.c_str());
    245         return false;
    246     }
    247     if (!SkImageEncoder::EncodeStream(&stream, bmp, SkImageEncoder::kPNG_Type, 100)) {
    248         SkDebugf("Can't encode a PNG.\n");
    249         return false;
    250     }
    251     return true;
    252 }
    253 
    254 static int kFailedLoops = -2;
    255 static int cpu_bench(const double overhead, Target* target, Benchmark* bench, double* samples) {
    256     // First figure out approximately how many loops of bench it takes to make overhead negligible.
    257     double bench_plus_overhead = 0.0;
    258     int round = 0;
    259     if (kAutoTuneLoops == FLAGS_loops) {
    260         while (bench_plus_overhead < overhead) {
    261             if (round++ == FLAGS_maxCalibrationAttempts) {
    262                 SkDebugf("WARNING: Can't estimate loops for %s (%s vs. %s); skipping.\n",
    263                          bench->getUniqueName(), HUMANIZE(bench_plus_overhead), HUMANIZE(overhead));
    264                 return kFailedLoops;
    265             }
    266             bench_plus_overhead = time(1, bench, target);
    267         }
    268     }
    269 
    270     // Later we'll just start and stop the timer once but loop N times.
    271     // We'll pick N to make timer overhead negligible:
    272     //
    273     //          overhead
    274     //  -------------------------  < FLAGS_overheadGoal
    275     //  overhead + N * Bench Time
    276     //
    277     // where bench_plus_overhead  overhead + Bench Time.
    278     //
    279     // Doing some math, we get:
    280     //
    281     //  (overhead / FLAGS_overheadGoal) - overhead
    282     //  ------------------------------------------  < N
    283     //       bench_plus_overhead - overhead)
    284     //
    285     // Luckily, this also works well in practice. :)
    286     int loops = FLAGS_loops;
    287     if (kAutoTuneLoops == loops) {
    288         const double numer = overhead / FLAGS_overheadGoal - overhead;
    289         const double denom = bench_plus_overhead - overhead;
    290         loops = (int)ceil(numer / denom);
    291         loops = clamp_loops(loops);
    292     } else {
    293         loops = detect_forever_loops(loops);
    294     }
    295 
    296     for (int i = 0; i < FLAGS_samples; i++) {
    297         samples[i] = time(loops, bench, target) / loops;
    298     }
    299     return loops;
    300 }
    301 
    302 static int gpu_bench(Target* target,
    303                      Benchmark* bench,
    304                      double* samples) {
    305     // First, figure out how many loops it'll take to get a frame up to FLAGS_gpuMs.
    306     int loops = FLAGS_loops;
    307     if (kAutoTuneLoops == loops) {
    308         loops = 1;
    309         double elapsed = 0;
    310         do {
    311             if (1<<30 == loops) {
    312                 // We're about to wrap.  Something's wrong with the bench.
    313                 loops = 0;
    314                 break;
    315             }
    316             loops *= 2;
    317             // If the GPU lets frames lag at all, we need to make sure we're timing
    318             // _this_ round, not still timing last round.  We force this by looping
    319             // more times than any reasonable GPU will allow frames to lag.
    320             for (int i = 0; i < FLAGS_gpuFrameLag; i++) {
    321                 elapsed = time(loops, bench, target);
    322             }
    323         } while (elapsed < FLAGS_gpuMs);
    324 
    325         // We've overshot at least a little.  Scale back linearly.
    326         loops = (int)ceil(loops * FLAGS_gpuMs / elapsed);
    327         loops = clamp_loops(loops);
    328 
    329         // Make sure we're not still timing our calibration.
    330         target->fence();
    331     } else {
    332         loops = detect_forever_loops(loops);
    333     }
    334 
    335     // Pretty much the same deal as the calibration: do some warmup to make
    336     // sure we're timing steady-state pipelined frames.
    337     for (int i = 0; i < FLAGS_gpuFrameLag; i++) {
    338         time(loops, bench, target);
    339     }
    340 
    341     // Now, actually do the timing!
    342     for (int i = 0; i < FLAGS_samples; i++) {
    343         samples[i] = time(loops, bench, target) / loops;
    344     }
    345 
    346     return loops;
    347 }
    348 
    349 static SkString to_lower(const char* str) {
    350     SkString lower(str);
    351     for (size_t i = 0; i < lower.size(); i++) {
    352         lower[i] = tolower(lower[i]);
    353     }
    354     return lower;
    355 }
    356 
    357 static bool is_cpu_config_allowed(const char* name) {
    358     for (int i = 0; i < FLAGS_config.count(); i++) {
    359         if (to_lower(FLAGS_config[i]).equals(name)) {
    360             return true;
    361         }
    362     }
    363     return false;
    364 }
    365 
    366 #if SK_SUPPORT_GPU
    367 static bool is_gpu_config_allowed(const char* name, GrContextFactory::GLContextType ctxType,
    368                                   int sampleCnt) {
    369     if (!is_cpu_config_allowed(name)) {
    370         return false;
    371     }
    372     if (const GrContext* ctx = gGrFactory->get(ctxType)) {
    373         return sampleCnt <= ctx->getMaxSampleCount();
    374     }
    375     return false;
    376 }
    377 #endif
    378 
    379 #if SK_SUPPORT_GPU
    380 #define kBogusGLContextType GrContextFactory::kNative_GLContextType
    381 #else
    382 #define kBogusGLContextType 0
    383 #endif
    384 
    385 // Append all configs that are enabled and supported.
    386 static void create_configs(SkTDArray<Config>* configs) {
    387     #define CPU_CONFIG(name, backend, color, alpha)                       \
    388         if (is_cpu_config_allowed(#name)) {                               \
    389             Config config = { #name, Benchmark::backend, color, alpha, 0, \
    390                               kBogusGLContextType, false };               \
    391             configs->push(config);                                        \
    392         }
    393 
    394     if (FLAGS_cpu) {
    395         CPU_CONFIG(nonrendering, kNonRendering_Backend, kUnknown_SkColorType, kUnpremul_SkAlphaType)
    396         CPU_CONFIG(8888, kRaster_Backend, kN32_SkColorType, kPremul_SkAlphaType)
    397         CPU_CONFIG(565, kRaster_Backend, kRGB_565_SkColorType, kOpaque_SkAlphaType)
    398     }
    399 
    400 #if SK_SUPPORT_GPU
    401     #define GPU_CONFIG(name, ctxType, samples, useDFText)                        \
    402         if (is_gpu_config_allowed(#name, GrContextFactory::ctxType, samples)) {  \
    403             Config config = {                                                    \
    404                 #name,                                                           \
    405                 Benchmark::kGPU_Backend,                                         \
    406                 kN32_SkColorType,                                                \
    407                 kPremul_SkAlphaType,                                             \
    408                 samples,                                                         \
    409                 GrContextFactory::ctxType,                                       \
    410                 useDFText };                                                     \
    411             configs->push(config);                                               \
    412         }
    413 
    414     if (FLAGS_gpu) {
    415         GPU_CONFIG(gpu, kNative_GLContextType, 0, false)
    416         GPU_CONFIG(msaa4, kNative_GLContextType, 4, false)
    417         GPU_CONFIG(msaa16, kNative_GLContextType, 16, false)
    418         GPU_CONFIG(nvprmsaa4, kNVPR_GLContextType, 4, false)
    419         GPU_CONFIG(nvprmsaa16, kNVPR_GLContextType, 16, false)
    420         GPU_CONFIG(gpudft, kNative_GLContextType, 0, true)
    421         GPU_CONFIG(debug, kDebug_GLContextType, 0, false)
    422         GPU_CONFIG(nullgpu, kNull_GLContextType, 0, false)
    423 #ifdef SK_ANGLE
    424         GPU_CONFIG(angle, kANGLE_GLContextType, 0, false)
    425 #endif
    426     }
    427 #endif
    428 
    429 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
    430     if (is_cpu_config_allowed("hwui")) {
    431         Config config = { "hwui", Benchmark::kHWUI_Backend, kRGBA_8888_SkColorType,
    432                           kPremul_SkAlphaType, 0, kBogusGLContextType, false };
    433         configs->push(config);
    434     }
    435 #endif
    436 }
    437 
    438 // If bench is enabled for config, returns a Target* for it, otherwise NULL.
    439 static Target* is_enabled(Benchmark* bench, const Config& config) {
    440     if (!bench->isSuitableFor(config.backend)) {
    441         return NULL;
    442     }
    443 
    444     SkImageInfo info = SkImageInfo::Make(bench->getSize().fX, bench->getSize().fY,
    445                                          config.color, config.alpha);
    446 
    447     Target* target = NULL;
    448 
    449     switch (config.backend) {
    450 #if SK_SUPPORT_GPU
    451     case Benchmark::kGPU_Backend:
    452         target = new GPUTarget(config);
    453         break;
    454 #endif
    455 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
    456     case Benchmark::kHWUI_Backend:
    457         target = new HWUITarget(config, bench);
    458         break;
    459 #endif
    460     default:
    461         target = new Target(config);
    462         break;
    463     }
    464 
    465     if (!target->init(info, bench)) {
    466         delete target;
    467         return NULL;
    468     }
    469     return target;
    470 }
    471 
    472 // Creates targets for a benchmark and a set of configs.
    473 static void create_targets(SkTDArray<Target*>* targets, Benchmark* b,
    474                            const SkTDArray<Config>& configs) {
    475     for (int i = 0; i < configs.count(); ++i) {
    476         if (Target* t = is_enabled(b, configs[i])) {
    477             targets->push(t);
    478         }
    479 
    480     }
    481 }
    482 
    483 
    484 class BenchmarkStream {
    485 public:
    486     BenchmarkStream() : fBenches(BenchRegistry::Head())
    487                       , fGMs(skiagm::GMRegistry::Head())
    488                       , fCurrentRecording(0)
    489                       , fCurrentScale(0)
    490                       , fCurrentSKP(0)
    491                       , fCurrentUseMPD(0)
    492                       , fCurrentCodec(0)
    493                       , fCurrentImage(0)
    494                       , fCurrentSubsetImage(0)
    495                       , fCurrentColorType(0)
    496                       , fCurrentAnimSKP(0)
    497                       , fDivisor(2) {
    498         for (int i = 0; i < FLAGS_skps.count(); i++) {
    499             if (SkStrEndsWith(FLAGS_skps[i], ".skp")) {
    500                 fSKPs.push_back() = FLAGS_skps[i];
    501             } else {
    502                 SkOSFile::Iter it(FLAGS_skps[i], ".skp");
    503                 SkString path;
    504                 while (it.next(&path)) {
    505                     fSKPs.push_back() = SkOSPath::Join(FLAGS_skps[0], path.c_str());
    506                 }
    507             }
    508         }
    509 
    510         if (4 != sscanf(FLAGS_clip[0], "%d,%d,%d,%d",
    511                         &fClip.fLeft, &fClip.fTop, &fClip.fRight, &fClip.fBottom)) {
    512             SkDebugf("Can't parse %s from --clip as an SkIRect.\n", FLAGS_clip[0]);
    513             exit(1);
    514         }
    515 
    516         for (int i = 0; i < FLAGS_scales.count(); i++) {
    517             if (1 != sscanf(FLAGS_scales[i], "%f", &fScales.push_back())) {
    518                 SkDebugf("Can't parse %s from --scales as an SkScalar.\n", FLAGS_scales[i]);
    519                 exit(1);
    520             }
    521         }
    522 
    523         if (2 != sscanf(FLAGS_zoom[0], "%f,%d", &fZoomScale, &fZoomSteps)) {
    524             SkDebugf("Can't parse %s from --zoom as a scale,step.\n", FLAGS_zoom[0]);
    525             exit(1);
    526         }
    527 
    528         fUseMPDs.push_back() = false;
    529         if (FLAGS_mpd) {
    530             fUseMPDs.push_back() = true;
    531         }
    532 
    533         // Prepare the images for decoding
    534         for (int i = 0; i < FLAGS_images.count(); i++) {
    535             const char* flag = FLAGS_images[i];
    536             if (sk_isdir(flag)) {
    537                 // If the value passed in is a directory, add all the images
    538                 SkOSFile::Iter it(flag);
    539                 SkString file;
    540                 while (it.next(&file)) {
    541                     fImages.push_back() = SkOSPath::Join(flag, file.c_str());
    542                 }
    543             } else if (sk_exists(flag)) {
    544                 // Also add the value if it is a single image
    545                 fImages.push_back() = flag;
    546             }
    547         }
    548 
    549         // Choose the candidate color types for image decoding
    550         const SkColorType colorTypes[] =
    551             { kN32_SkColorType, kRGB_565_SkColorType, kAlpha_8_SkColorType, kIndex_8_SkColorType };
    552         fColorTypes.push_back_n(SK_ARRAY_COUNT(colorTypes), colorTypes);
    553     }
    554 
    555     static bool ReadPicture(const char* path, SkAutoTUnref<SkPicture>* pic) {
    556         // Not strictly necessary, as it will be checked again later,
    557         // but helps to avoid a lot of pointless work if we're going to skip it.
    558         if (SkCommandLineFlags::ShouldSkip(FLAGS_match, path)) {
    559             return false;
    560         }
    561 
    562         SkAutoTDelete<SkStream> stream(SkStream::NewFromFile(path));
    563         if (stream.get() == NULL) {
    564             SkDebugf("Could not read %s.\n", path);
    565             return false;
    566         }
    567 
    568         pic->reset(SkPicture::CreateFromStream(stream.get()));
    569         if (pic->get() == NULL) {
    570             SkDebugf("Could not read %s as an SkPicture.\n", path);
    571             return false;
    572         }
    573         return true;
    574     }
    575 
    576     Benchmark* next() {
    577         if (fBenches) {
    578             Benchmark* bench = fBenches->factory()(NULL);
    579             fBenches = fBenches->next();
    580             fSourceType = "bench";
    581             fBenchType  = "micro";
    582             return bench;
    583         }
    584 
    585         while (fGMs) {
    586             SkAutoTDelete<skiagm::GM> gm(fGMs->factory()(NULL));
    587             fGMs = fGMs->next();
    588             if (gm->runAsBench()) {
    589                 fSourceType = "gm";
    590                 fBenchType  = "micro";
    591                 return SkNEW_ARGS(GMBench, (gm.detach()));
    592             }
    593         }
    594 
    595         // First add all .skps as RecordingBenches.
    596         while (fCurrentRecording < fSKPs.count()) {
    597             const SkString& path = fSKPs[fCurrentRecording++];
    598             SkAutoTUnref<SkPicture> pic;
    599             if (!ReadPicture(path.c_str(), &pic)) {
    600                 continue;
    601             }
    602             SkString name = SkOSPath::Basename(path.c_str());
    603             fSourceType = "skp";
    604             fBenchType  = "recording";
    605             fSKPBytes = static_cast<double>(SkPictureUtils::ApproximateBytesUsed(pic));
    606             fSKPOps   = pic->approximateOpCount();
    607             return SkNEW_ARGS(RecordingBench, (name.c_str(), pic.get(), FLAGS_bbh));
    608         }
    609 
    610         // Then once each for each scale as SKPBenches (playback).
    611         while (fCurrentScale < fScales.count()) {
    612             while (fCurrentSKP < fSKPs.count()) {
    613                 const SkString& path = fSKPs[fCurrentSKP];
    614                 SkAutoTUnref<SkPicture> pic;
    615                 if (!ReadPicture(path.c_str(), &pic)) {
    616                     fCurrentSKP++;
    617                     continue;
    618                 }
    619 
    620                 while (fCurrentUseMPD < fUseMPDs.count()) {
    621                     if (FLAGS_bbh) {
    622                         // The SKP we read off disk doesn't have a BBH.  Re-record so it grows one.
    623                         SkRTreeFactory factory;
    624                         SkPictureRecorder recorder;
    625                         static const int kFlags = SkPictureRecorder::kComputeSaveLayerInfo_RecordFlag;
    626                         pic->playback(recorder.beginRecording(pic->cullRect().width(),
    627                                                               pic->cullRect().height(),
    628                                                               &factory,
    629                                                               fUseMPDs[fCurrentUseMPD] ? kFlags : 0));
    630                         pic.reset(recorder.endRecording());
    631                     }
    632                     SkString name = SkOSPath::Basename(path.c_str());
    633                     fSourceType = "skp";
    634                     fBenchType = "playback";
    635                     return SkNEW_ARGS(SKPBench,
    636                                       (name.c_str(), pic.get(), fClip,
    637                                        fScales[fCurrentScale], fUseMPDs[fCurrentUseMPD++]));
    638 
    639                 }
    640                 fCurrentUseMPD = 0;
    641                 fCurrentSKP++;
    642             }
    643             fCurrentSKP = 0;
    644             fCurrentScale++;
    645         }
    646 
    647         // Now loop over each skp again if we have an animation
    648         if (fZoomScale != 1.0f && fZoomSteps != 1) {
    649             while (fCurrentAnimSKP < fSKPs.count()) {
    650                 const SkString& path = fSKPs[fCurrentAnimSKP];
    651                 SkAutoTUnref<SkPicture> pic;
    652                 if (!ReadPicture(path.c_str(), &pic)) {
    653                     fCurrentAnimSKP++;
    654                     continue;
    655                 }
    656 
    657                 fCurrentAnimSKP++;
    658                 SkString name = SkOSPath::Basename(path.c_str());
    659                 SkMatrix anim = SkMatrix::I();
    660                 anim.setScale(fZoomScale, fZoomScale);
    661                 return SkNEW_ARGS(SKPAnimationBench, (name.c_str(), pic.get(), fClip, anim,
    662                                   fZoomSteps));
    663             }
    664         }
    665 
    666 
    667         for (; fCurrentCodec < fImages.count(); fCurrentCodec++) {
    668             const SkString& path = fImages[fCurrentCodec];
    669             SkAutoTUnref<SkData> encoded(SkData::NewFromFileName(path.c_str()));
    670             SkAutoTDelete<SkCodec> codec(SkCodec::NewFromData(encoded));
    671             if (!codec) {
    672                 // Nothing to time.
    673                 SkDebugf("Cannot find codec for %s\n", path.c_str());
    674                 continue;
    675             }
    676 
    677             while (fCurrentColorType < fColorTypes.count()) {
    678                 const SkColorType colorType = fColorTypes[fCurrentColorType];
    679                 fCurrentColorType++;
    680 
    681                 // Make sure we can decode to this color type.
    682                 SkImageInfo info = codec->getInfo().makeColorType(colorType);
    683                 SkAlphaType alphaType;
    684                 if (!SkColorTypeValidateAlphaType(colorType, info.alphaType(),
    685                                                   &alphaType)) {
    686                     continue;
    687                 }
    688                 if (alphaType != info.alphaType()) {
    689                     info = info.makeAlphaType(alphaType);
    690                 }
    691 
    692                 const size_t rowBytes = info.minRowBytes();
    693                 SkAutoMalloc storage(info.getSafeSize(rowBytes));
    694 
    695                 // Used if fCurrentColorType is kIndex_8_SkColorType
    696                 int colorCount = 256;
    697                 SkPMColor colors[256];
    698 
    699                 const SkImageGenerator::Result result = codec->getPixels(
    700                         info, storage.get(), rowBytes, NULL, colors,
    701                         &colorCount);
    702                 switch (result) {
    703                     case SkImageGenerator::kSuccess:
    704                     case SkImageGenerator::kIncompleteInput:
    705                         return new CodecBench(SkOSPath::Basename(path.c_str()),
    706                                 encoded, colorType);
    707                     case SkImageGenerator::kInvalidConversion:
    708                         // This is okay. Not all conversions are valid.
    709                         break;
    710                     default:
    711                         // This represents some sort of failure.
    712                         SkASSERT(false);
    713                         break;
    714                 }
    715             }
    716             fCurrentColorType = 0;
    717         }
    718 
    719         // Run the DecodingBenches
    720         while (fCurrentImage < fImages.count()) {
    721             while (fCurrentColorType < fColorTypes.count()) {
    722                 const SkString& path = fImages[fCurrentImage];
    723                 SkColorType colorType = fColorTypes[fCurrentColorType];
    724                 fCurrentColorType++;
    725                 // Check if the image decodes to the right color type
    726                 // before creating the benchmark
    727                 SkBitmap bitmap;
    728                 if (SkImageDecoder::DecodeFile(path.c_str(), &bitmap,
    729                         colorType, SkImageDecoder::kDecodePixels_Mode)
    730                         && bitmap.colorType() == colorType) {
    731                     return new DecodingBench(path, colorType);
    732                 }
    733             }
    734             fCurrentColorType = 0;
    735             fCurrentImage++;
    736         }
    737 
    738         // Run the DecodingSubsetBenches
    739         while (fCurrentSubsetImage < fImages.count()) {
    740             while (fCurrentColorType < fColorTypes.count()) {
    741                 const SkString& path = fImages[fCurrentSubsetImage];
    742                 SkColorType colorType = fColorTypes[fCurrentColorType];
    743                 fCurrentColorType++;
    744                 // Check if the image decodes before creating the benchmark
    745                 SkAutoTUnref<SkData> encoded(
    746                         SkData::NewFromFileName(path.c_str()));
    747                 SkAutoTDelete<SkMemoryStream> stream(
    748                         new SkMemoryStream(encoded));
    749                 SkAutoTDelete<SkImageDecoder>
    750                     decoder(SkImageDecoder::Factory(stream.get()));
    751                 if (!decoder) {
    752                     SkDebugf("Cannot find decoder for %s\n", path.c_str());
    753                 } else {
    754                     stream->rewind();
    755                     int w, h;
    756                     bool success;
    757                     if (!decoder->buildTileIndex(stream.detach(), &w, &h)
    758                             || w*h == 1) {
    759                         // This is not an error, but in this case we still
    760                         // do not want to run the benchmark.
    761                         success = false;
    762                     } else if (fDivisor > w || fDivisor > h) {
    763                         SkDebugf("Divisor %d is too big for %s %dx%d\n",
    764                                 fDivisor, path.c_str(), w, h);
    765                         success = false;
    766                     } else {
    767                         const int sW  = w / fDivisor;
    768                         const int sH = h / fDivisor;
    769                         SkBitmap bitmap;
    770                         success = true;
    771                         for (int y = 0; y < h; y += sH) {
    772                             for (int x = 0; x < w; x += sW) {
    773                                 SkIRect rect = SkIRect::MakeXYWH(x, y, sW, sH);
    774                                 success &= decoder->decodeSubset(&bitmap, rect,
    775                                                                  colorType);
    776                             }
    777                         }
    778                     }
    779                     // Create the benchmark if successful
    780                     if (success) {
    781                         return new DecodingSubsetBench(path, colorType,
    782                                                        fDivisor);
    783                     }
    784                 }
    785             }
    786             fCurrentColorType = 0;
    787             fCurrentSubsetImage++;
    788         }
    789 
    790         return NULL;
    791     }
    792 
    793     void fillCurrentOptions(ResultsWriter* log) const {
    794         log->configOption("source_type", fSourceType);
    795         log->configOption("bench_type",  fBenchType);
    796         if (0 == strcmp(fSourceType, "skp")) {
    797             log->configOption("clip",
    798                     SkStringPrintf("%d %d %d %d", fClip.fLeft, fClip.fTop,
    799                                                   fClip.fRight, fClip.fBottom).c_str());
    800             log->configOption("scale", SkStringPrintf("%.2g", fScales[fCurrentScale]).c_str());
    801             if (fCurrentUseMPD > 0) {
    802                 SkASSERT(1 == fCurrentUseMPD || 2 == fCurrentUseMPD);
    803                 log->configOption("multi_picture_draw", fUseMPDs[fCurrentUseMPD-1] ? "true" : "false");
    804             }
    805         }
    806         if (0 == strcmp(fBenchType, "recording")) {
    807             log->metric("bytes", fSKPBytes);
    808             log->metric("ops",   fSKPOps);
    809         }
    810     }
    811 
    812 private:
    813     const BenchRegistry* fBenches;
    814     const skiagm::GMRegistry* fGMs;
    815     SkIRect            fClip;
    816     SkTArray<SkScalar> fScales;
    817     SkTArray<SkString> fSKPs;
    818     SkTArray<bool>     fUseMPDs;
    819     SkTArray<SkString> fImages;
    820     SkTArray<SkColorType> fColorTypes;
    821     SkScalar           fZoomScale;
    822     int                fZoomSteps;
    823 
    824     double fSKPBytes, fSKPOps;
    825 
    826     const char* fSourceType;  // What we're benching: bench, GM, SKP, ...
    827     const char* fBenchType;   // How we bench it: micro, recording, playback, ...
    828     int fCurrentRecording;
    829     int fCurrentScale;
    830     int fCurrentSKP;
    831     int fCurrentUseMPD;
    832     int fCurrentCodec;
    833     int fCurrentImage;
    834     int fCurrentSubsetImage;
    835     int fCurrentColorType;
    836     int fCurrentAnimSKP;
    837     const int fDivisor;
    838 };
    839 
    840 int nanobench_main();
    841 int nanobench_main() {
    842     SetupCrashHandler();
    843     SkAutoGraphics ag;
    844     SkTaskGroup::Enabler enabled;
    845 
    846 #if SK_SUPPORT_GPU
    847     GrContext::Options grContextOpts;
    848     grContextOpts.fDrawPathToCompressedTexture = FLAGS_gpuCompressAlphaMasks;
    849     gGrFactory.reset(SkNEW_ARGS(GrContextFactory, (grContextOpts)));
    850 #endif
    851 
    852     if (FLAGS_veryVerbose) {
    853         FLAGS_verbose = true;
    854     }
    855 
    856     if (kAutoTuneLoops != FLAGS_loops) {
    857         FLAGS_samples     = 1;
    858         FLAGS_gpuFrameLag = 0;
    859     }
    860 
    861     if (!FLAGS_writePath.isEmpty()) {
    862         SkDebugf("Writing files to %s.\n", FLAGS_writePath[0]);
    863         if (!sk_mkdir(FLAGS_writePath[0])) {
    864             SkDebugf("Could not create %s. Files won't be written.\n", FLAGS_writePath[0]);
    865             FLAGS_writePath.set(0, NULL);
    866         }
    867     }
    868 
    869     SkAutoTDelete<ResultsWriter> log(SkNEW(ResultsWriter));
    870     if (!FLAGS_outResultsFile.isEmpty()) {
    871         log.reset(SkNEW(NanoJSONResultsWriter(FLAGS_outResultsFile[0])));
    872     }
    873 
    874     if (1 == FLAGS_properties.count() % 2) {
    875         SkDebugf("ERROR: --properties must be passed with an even number of arguments.\n");
    876         return 1;
    877     }
    878     for (int i = 1; i < FLAGS_properties.count(); i += 2) {
    879         log->property(FLAGS_properties[i-1], FLAGS_properties[i]);
    880     }
    881 
    882     if (1 == FLAGS_key.count() % 2) {
    883         SkDebugf("ERROR: --key must be passed with an even number of arguments.\n");
    884         return 1;
    885     }
    886     for (int i = 1; i < FLAGS_key.count(); i += 2) {
    887         log->key(FLAGS_key[i-1], FLAGS_key[i]);
    888     }
    889 
    890     const double overhead = estimate_timer_overhead();
    891     SkDebugf("Timer overhead: %s\n", HUMANIZE(overhead));
    892 
    893     SkAutoTMalloc<double> samples(FLAGS_samples);
    894 
    895     if (kAutoTuneLoops != FLAGS_loops) {
    896         SkDebugf("Fixed number of loops; times would only be misleading so we won't print them.\n");
    897     } else if (FLAGS_verbose) {
    898         // No header.
    899     } else if (FLAGS_quiet) {
    900         SkDebugf("median\tbench\tconfig\n");
    901     } else {
    902         SkDebugf("curr/maxrss\tloops\tmin\tmedian\tmean\tmax\tstddev\t%-*s\tconfig\tbench\n",
    903                  FLAGS_samples, "samples");
    904     }
    905 
    906     SkTDArray<Config> configs;
    907     create_configs(&configs);
    908 
    909     int runs = 0;
    910     BenchmarkStream benchStream;
    911     while (Benchmark* b = benchStream.next()) {
    912         SkAutoTDelete<Benchmark> bench(b);
    913         if (SkCommandLineFlags::ShouldSkip(FLAGS_match, bench->getUniqueName())) {
    914             continue;
    915         }
    916 
    917         SkTDArray<Target*> targets;
    918         create_targets(&targets, bench.get(), configs);
    919 
    920         if (!targets.isEmpty()) {
    921             log->bench(bench->getUniqueName(), bench->getSize().fX, bench->getSize().fY);
    922             bench->preDraw();
    923         }
    924         for (int j = 0; j < targets.count(); j++) {
    925             // During HWUI output this canvas may be NULL.
    926             SkCanvas* canvas = targets[j]->getCanvas();
    927             const char* config = targets[j]->config.name;
    928 
    929             targets[j]->setup();
    930             bench->perCanvasPreDraw(canvas);
    931 
    932             const int loops =
    933                 targets[j]->needsFrameTiming()
    934                 ? gpu_bench(targets[j], bench.get(), samples.get())
    935                 : cpu_bench(overhead, targets[j], bench.get(), samples.get());
    936 
    937             bench->perCanvasPostDraw(canvas);
    938 
    939             if (Benchmark::kNonRendering_Backend != targets[j]->config.backend &&
    940                 !FLAGS_writePath.isEmpty() && FLAGS_writePath[0]) {
    941                 SkString pngFilename = SkOSPath::Join(FLAGS_writePath[0], config);
    942                 pngFilename = SkOSPath::Join(pngFilename.c_str(), bench->getUniqueName());
    943                 pngFilename.append(".png");
    944                 write_canvas_png(targets[j], pngFilename);
    945             }
    946 
    947             if (kFailedLoops == loops) {
    948                 // Can't be timed.  A warning note has already been printed.
    949                 continue;
    950             }
    951 
    952             Stats stats(samples.get(), FLAGS_samples);
    953             log->config(config);
    954             log->configOption("name", bench->getName());
    955             benchStream.fillCurrentOptions(log.get());
    956             targets[j]->fillOptions(log.get());
    957             log->metric("min_ms",    stats.min);
    958             if (runs++ % FLAGS_flushEvery == 0) {
    959                 log->flush();
    960             }
    961 
    962             if (kAutoTuneLoops != FLAGS_loops) {
    963                 if (targets.count() == 1) {
    964                     config = ""; // Only print the config if we run the same bench on more than one.
    965                 }
    966                 SkDebugf("%4d/%-4dMB\t%s\t%s\n"
    967                          , sk_tools::getCurrResidentSetSizeMB()
    968                          , sk_tools::getMaxResidentSetSizeMB()
    969                          , bench->getUniqueName()
    970                          , config);
    971             } else if (FLAGS_verbose) {
    972                 for (int i = 0; i < FLAGS_samples; i++) {
    973                     SkDebugf("%s  ", HUMANIZE(samples[i]));
    974                 }
    975                 SkDebugf("%s\n", bench->getUniqueName());
    976             } else if (FLAGS_quiet) {
    977                 if (targets.count() == 1) {
    978                     config = ""; // Only print the config if we run the same bench on more than one.
    979                 }
    980                 SkDebugf("%s\t%s\t%s\n", HUMANIZE(stats.median), bench->getUniqueName(), config);
    981             } else {
    982                 const double stddev_percent = 100 * sqrt(stats.var) / stats.mean;
    983                 SkDebugf("%4d/%-4dMB\t%d\t%s\t%s\t%s\t%s\t%.0f%%\t%s\t%s\t%s\n"
    984                         , sk_tools::getCurrResidentSetSizeMB()
    985                         , sk_tools::getMaxResidentSetSizeMB()
    986                         , loops
    987                         , HUMANIZE(stats.min)
    988                         , HUMANIZE(stats.median)
    989                         , HUMANIZE(stats.mean)
    990                         , HUMANIZE(stats.max)
    991                         , stddev_percent
    992                         , stats.plot.c_str()
    993                         , config
    994                         , bench->getUniqueName()
    995                         );
    996             }
    997 #if SK_SUPPORT_GPU
    998             if (FLAGS_gpuStats &&
    999                 Benchmark::kGPU_Backend == targets[j]->config.backend) {
   1000                 gGrFactory->get(targets[j]->config.ctxType)->printCacheStats();
   1001                 gGrFactory->get(targets[j]->config.ctxType)->printGpuStats();
   1002             }
   1003 #endif
   1004         }
   1005         targets.deleteAll();
   1006 
   1007 #if SK_SUPPORT_GPU
   1008         if (FLAGS_abandonGpuContext) {
   1009             gGrFactory->abandonContexts();
   1010         }
   1011         if (FLAGS_resetGpuContext || FLAGS_abandonGpuContext) {
   1012             gGrFactory->destroyContexts();
   1013         }
   1014 #endif
   1015     }
   1016 
   1017     log->bench("memory_usage", 0,0);
   1018     log->config("meta");
   1019     log->metric("max_rss_mb", sk_tools::getMaxResidentSetSizeMB());
   1020 
   1021 #if SK_SUPPORT_GPU
   1022     // Make sure we clean up the global GrContextFactory here, otherwise we might race with the
   1023     // SkEventTracer destructor
   1024     gGrFactory.reset(NULL);
   1025 #endif
   1026 
   1027     return 0;
   1028 }
   1029 
   1030 #if !defined SK_BUILD_FOR_IOS
   1031 int main(int argc, char** argv) {
   1032     SkCommandLineFlags::Parse(argc, argv);
   1033     return nanobench_main();
   1034 }
   1035 #endif
   1036