Home | History | Annotate | Download | only in bench
      1 
      2 /*
      3  * Copyright 2011 Google Inc.
      4  *
      5  * Use of this source code is governed by a BSD-style license that can be
      6  * found in the LICENSE file.
      7  */
      8 
      9 
     10 #include "BenchTimer.h"
     11 
     12 #if SK_SUPPORT_GPU
     13 #include "GrContext.h"
     14 #include "GrContextFactory.h"
     15 #include "gl/GrGLDefines.h"
     16 #include "GrRenderTarget.h"
     17 #include "SkGpuDevice.h"
     18 #else
     19 class GrContext;
     20 #endif // SK_SUPPORT_GPU
     21 
     22 #include "SkBenchLogger.h"
     23 #include "SkBenchmark.h"
     24 #include "SkCanvas.h"
     25 #include "SkCommandLineFlags.h"
     26 #include "SkDeferredCanvas.h"
     27 #include "SkDevice.h"
     28 #include "SkColorPriv.h"
     29 #include "SkGraphics.h"
     30 #include "SkImageEncoder.h"
     31 #include "SkNWayCanvas.h"
     32 #include "SkPicture.h"
     33 #include "SkString.h"
     34 #include "SkTArray.h"
     35 #include "TimerData.h"
     36 
     37 enum benchModes {
     38     kNormal_benchModes,
     39     kDeferred_benchModes,
     40     kDeferredSilent_benchModes,
     41     kRecord_benchModes,
     42     kPictureRecord_benchModes
     43 };
     44 
     45 ///////////////////////////////////////////////////////////////////////////////
     46 
     47 static void erase(SkBitmap& bm) {
     48     if (bm.config() == SkBitmap::kA8_Config) {
     49         bm.eraseColor(SK_ColorTRANSPARENT);
     50     } else {
     51         bm.eraseColor(SK_ColorWHITE);
     52     }
     53 }
     54 
     55 #if 0
     56 static bool equal(const SkBitmap& bm1, const SkBitmap& bm2) {
     57     if (bm1.width() != bm2.width() ||
     58         bm1.height() != bm2.height() ||
     59         bm1.config() != bm2.config()) {
     60         return false;
     61     }
     62 
     63     size_t pixelBytes = bm1.width() * bm1.bytesPerPixel();
     64     for (int y = 0; y < bm1.height(); y++) {
     65         if (memcmp(bm1.getAddr(0, y), bm2.getAddr(0, y), pixelBytes)) {
     66             return false;
     67         }
     68     }
     69     return true;
     70 }
     71 #endif
     72 
     73 class Iter {
     74 public:
     75     Iter(void* param) {
     76         fBench = BenchRegistry::Head();
     77         fParam = param;
     78     }
     79 
     80     SkBenchmark* next() {
     81         if (fBench) {
     82             BenchRegistry::Factory f = fBench->factory();
     83             fBench = fBench->next();
     84             return f(fParam);
     85         }
     86         return NULL;
     87     }
     88 
     89 private:
     90     const BenchRegistry* fBench;
     91     void* fParam;
     92 };
     93 
     94 class AutoPrePostDraw {
     95 public:
     96     AutoPrePostDraw(SkBenchmark* bench) : fBench(bench) {
     97         fBench->preDraw();
     98     }
     99     ~AutoPrePostDraw() {
    100         fBench->postDraw();
    101     }
    102 private:
    103     SkBenchmark* fBench;
    104 };
    105 
    106 static void make_filename(const char name[], SkString* path) {
    107     path->set(name);
    108     for (int i = 0; name[i]; i++) {
    109         switch (name[i]) {
    110             case '/':
    111             case '\\':
    112             case ' ':
    113             case ':':
    114                 path->writable_str()[i] = '-';
    115                 break;
    116             default:
    117                 break;
    118         }
    119     }
    120 }
    121 
    122 static void saveFile(const char name[], const char config[], const char dir[],
    123                      const SkBitmap& bm) {
    124     SkBitmap copy;
    125     if (!bm.copyTo(&copy, SkBitmap::kARGB_8888_Config)) {
    126         return;
    127     }
    128 
    129     if (bm.config() == SkBitmap::kA8_Config) {
    130         // turn alpha into gray-scale
    131         size_t size = copy.getSize() >> 2;
    132         SkPMColor* p = copy.getAddr32(0, 0);
    133         for (size_t i = 0; i < size; i++) {
    134             int c = (*p >> SK_A32_SHIFT) & 0xFF;
    135             c = 255 - c;
    136             c |= (c << 24) | (c << 16) | (c << 8);
    137             *p++ = c | (SK_A32_MASK << SK_A32_SHIFT);
    138         }
    139     }
    140 
    141     SkString str;
    142     make_filename(name, &str);
    143     str.appendf("_%s.png", config);
    144     str.prepend(dir);
    145     ::remove(str.c_str());
    146     SkImageEncoder::EncodeFile(str.c_str(), copy, SkImageEncoder::kPNG_Type,
    147                                100);
    148 }
    149 
    150 static void performClip(SkCanvas* canvas, int w, int h) {
    151     SkRect r;
    152 
    153     r.set(SkIntToScalar(10), SkIntToScalar(10),
    154           SkIntToScalar(w*2/3), SkIntToScalar(h*2/3));
    155     canvas->clipRect(r, SkRegion::kIntersect_Op);
    156 
    157     r.set(SkIntToScalar(w/3), SkIntToScalar(h/3),
    158           SkIntToScalar(w-10), SkIntToScalar(h-10));
    159     canvas->clipRect(r, SkRegion::kXOR_Op);
    160 }
    161 
    162 static void performRotate(SkCanvas* canvas, int w, int h) {
    163     const SkScalar x = SkIntToScalar(w) / 2;
    164     const SkScalar y = SkIntToScalar(h) / 2;
    165 
    166     canvas->translate(x, y);
    167     canvas->rotate(SkIntToScalar(35));
    168     canvas->translate(-x, -y);
    169 }
    170 
    171 static void performScale(SkCanvas* canvas, int w, int h) {
    172     const SkScalar x = SkIntToScalar(w) / 2;
    173     const SkScalar y = SkIntToScalar(h) / 2;
    174 
    175     canvas->translate(x, y);
    176     // just enough so we can't take the sprite case
    177     canvas->scale(SK_Scalar1 * 99/100, SK_Scalar1 * 99/100);
    178     canvas->translate(-x, -y);
    179 }
    180 
    181 static bool parse_bool_arg(char * const* argv, char* const* stop, bool* var) {
    182     if (argv < stop) {
    183         *var = atoi(*argv) != 0;
    184         return true;
    185     }
    186     return false;
    187 }
    188 
    189 enum Backend {
    190     kNonRendering_Backend,
    191     kRaster_Backend,
    192     kGPU_Backend,
    193     kPDF_Backend,
    194 };
    195 
    196 static SkDevice* make_device(SkBitmap::Config config, const SkIPoint& size,
    197                              Backend backend, int sampleCount, GrContext* context) {
    198     SkDevice* device = NULL;
    199     SkBitmap bitmap;
    200     bitmap.setConfig(config, size.fX, size.fY);
    201 
    202     switch (backend) {
    203         case kRaster_Backend:
    204             bitmap.allocPixels();
    205             erase(bitmap);
    206             device = SkNEW_ARGS(SkDevice, (bitmap));
    207             break;
    208 #if SK_SUPPORT_GPU
    209         case kGPU_Backend: {
    210             GrTextureDesc desc;
    211             desc.fConfig = kSkia8888_GrPixelConfig;
    212             desc.fFlags = kRenderTarget_GrTextureFlagBit;
    213             desc.fWidth = size.fX;
    214             desc.fHeight = size.fY;
    215             desc.fSampleCnt = sampleCount;
    216             SkAutoTUnref<GrTexture> texture(context->createUncachedTexture(desc, NULL, 0));
    217             if (!texture) {
    218                 return NULL;
    219             }
    220             device = SkNEW_ARGS(SkGpuDevice, (context, texture.get()));
    221             break;
    222         }
    223 #endif
    224         case kPDF_Backend:
    225         default:
    226             SkASSERT(!"unsupported");
    227     }
    228     return device;
    229 }
    230 
    231 #if SK_SUPPORT_GPU
    232 GrContextFactory gContextFactory;
    233 typedef GrContextFactory::GLContextType GLContextType;
    234 static const GLContextType kDontCareGLCtxType = GrContextFactory::kNative_GLContextType;
    235 #else
    236 typedef int GLContextType;
    237 static const GLContextType kDontCareGLCtxType = 0;
    238 #endif
    239 
    240 static const struct {
    241     SkBitmap::Config    fConfig;
    242     const char*         fName;
    243     int                 fSampleCnt;
    244     Backend             fBackend;
    245     GLContextType       fContextType;
    246     bool                fRunByDefault;
    247 } gConfigs[] = {
    248     { SkBitmap::kNo_Config,         "NONRENDERING", 0, kNonRendering_Backend, kDontCareGLCtxType,                      true     },
    249     { SkBitmap::kARGB_8888_Config,  "8888",         0, kRaster_Backend,       kDontCareGLCtxType,                      true     },
    250     { SkBitmap::kRGB_565_Config,    "565",          0, kRaster_Backend,       kDontCareGLCtxType,                      true     },
    251 #if SK_SUPPORT_GPU
    252     { SkBitmap::kARGB_8888_Config,  "GPU",          0, kGPU_Backend,          GrContextFactory::kNative_GLContextType, true     },
    253     { SkBitmap::kARGB_8888_Config,  "MSAA4",        4, kGPU_Backend,          GrContextFactory::kNative_GLContextType, false    },
    254     { SkBitmap::kARGB_8888_Config,  "MSAA16",      16, kGPU_Backend,          GrContextFactory::kNative_GLContextType, false    },
    255 #if SK_ANGLE
    256     { SkBitmap::kARGB_8888_Config,  "ANGLE",        0, kGPU_Backend,          GrContextFactory::kANGLE_GLContextType,  true     },
    257 #endif // SK_ANGLE
    258 #ifdef SK_DEBUG
    259     { SkBitmap::kARGB_8888_Config,  "Debug",        0, kGPU_Backend,          GrContextFactory::kDebug_GLContextType,  GR_DEBUG },
    260 #endif // SK_DEBUG
    261     { SkBitmap::kARGB_8888_Config,  "NULLGPU",      0, kGPU_Backend,          GrContextFactory::kNull_GLContextType,   true     },
    262 #endif // SK_SUPPORT_GPU
    263 };
    264 
    265 static int findConfig(const char config[]) {
    266     for (size_t i = 0; i < SK_ARRAY_COUNT(gConfigs); i++) {
    267         if (!strcmp(config, gConfigs[i].fName)) {
    268             return i;
    269         }
    270     }
    271     return -1;
    272 }
    273 
    274 static void help() {
    275     SkString configsStr;
    276     static const size_t kConfigCount = SK_ARRAY_COUNT(gConfigs);
    277     for (size_t i = 0; i < kConfigCount; ++i) {
    278         configsStr.appendf("%s%s", gConfigs[i].fName, ((i == kConfigCount - 1) ? "" : "|"));
    279     }
    280 
    281     SkDebugf("Usage: bench [-o outDir] [--repeat nr] [--logPerIter] "
    282                           "[--timers [wcgWC]*] [--rotate]\n"
    283              "    [--scale] [--clip] [--min] [--forceAA 1|0] [--forceFilter 1|0]\n"
    284              "    [--forceDither 1|0] [--forceBlend 1|0]"
    285 #if SK_SUPPORT_GPU
    286              " [--gpuCacheSize <bytes> <count>]"
    287 #endif
    288              "\n"
    289              "    [--strokeWidth width] [--match name]\n"
    290              "    [--mode normal|deferred|deferredSilent|record|picturerecord]\n"
    291              "    [--config ");
    292     SkDebugf("%s]\n", configsStr.c_str());
    293     SkDebugf("    [-Dfoo bar] [--logFile filename] [-h|--help]");
    294     SkDebugf("\n\n");
    295     SkDebugf("    -o outDir : Image of each bench will be put in outDir.\n");
    296     SkDebugf("    --repeat nr : Each bench repeats for nr times.\n");
    297     SkDebugf("    --logPerIter : "
    298              "Log each repeat timer instead of mean, default is disabled.\n");
    299     SkDebugf("    --timers [wcgWC]* : "
    300              "Display wall, cpu, gpu, truncated wall or truncated cpu time for each bench.\n");
    301     SkDebugf("    --rotate : Rotate before each bench runs.\n");
    302     SkDebugf("    --scale : Scale before each bench runs.\n");
    303     SkDebugf("    --clip : Clip before each bench runs.\n");
    304     SkDebugf("    --min : Print the minimum times (instead of average).\n");
    305     SkDebugf("    --forceAA 1|0 : "
    306              "Enable/disable anti-aliased, default is enabled.\n");
    307     SkDebugf("    --forceFilter 1|0 : "
    308              "Enable/disable bitmap filtering, default is disabled.\n");
    309     SkDebugf("    --forceDither 1|0 : "
    310              "Enable/disable dithering, default is disabled.\n");
    311     SkDebugf("    --forceBlend 1|0 : "
    312              "Enable/disable dithering, default is disabled.\n");
    313 #if SK_SUPPORT_GPU
    314     SkDebugf("    --gpuCacheSize <bytes> <count>: "
    315              "limits gpu cache to  bytes size or object count.\n");
    316     SkDebugf("      -1 for either value means use the default. 0 for either disables the cache.\n");
    317 #endif
    318     SkDebugf("    --strokeWidth width : The width for path stroke.\n");
    319     SkDebugf("    --match [~][^]substring[$] [...] of test name to run.\n"
    320              "             Multiple matches may be separated by spaces.\n"
    321              "             ~ causes a matching test to always be skipped\n"
    322              "             ^ requires the start of the test to match\n"
    323              "             $ requires the end of the test to match\n"
    324              "             ^ and $ requires an exact match\n"
    325              "             If a test does not match any list entry,\n"
    326              "             it is skipped unless some list entry starts with ~\n");
    327     SkDebugf("    --mode normal|deferred|deferredSilent|record|picturerecord :\n"
    328              "             Run in the corresponding mode\n"
    329              "                 normal, Use a normal canvas to draw to;\n"
    330              "                 deferred, Use a deferrred canvas when drawing;\n"
    331              "                 deferredSilent, deferred with silent playback;\n"
    332              "                 record, Benchmark the time to record to an SkPicture;\n"
    333              "                 picturerecord, Benchmark the time to do record from a \n"
    334              "                                SkPicture to a SkPicture.\n");
    335     SkDebugf("    --logFile filename : destination for writing log output, in addition to stdout.\n");
    336     SkDebugf("    --config %s:\n", configsStr.c_str());
    337     SkDebugf("             Run bench in corresponding config mode.\n");
    338     SkDebugf("    -Dfoo bar : Add extra definition to bench.\n");
    339     SkDebugf("    -h|--help : Show this help message.\n");
    340 }
    341 
    342 int tool_main(int argc, char** argv);
    343 int tool_main(int argc, char** argv) {
    344 #if SK_ENABLE_INST_COUNT
    345     gPrintInstCount = true;
    346 #endif
    347     SkAutoGraphics ag;
    348 
    349     SkTDict<const char*> defineDict(1024);
    350     int repeatDraw = 1;
    351 
    352     int forceAlpha = 0xFF;
    353     bool forceAA = true;
    354     bool forceFilter = false;
    355     SkTriState::State forceDither = SkTriState::kDefault;
    356 
    357     static const uint32_t kDefaultTimerTypes = TimerData::kCpu_Flag | TimerData::kGpu_Flag;
    358     static const TimerData::Result kDefaultTimerResult = TimerData::kAvg_Result;
    359     uint32_t timerTypes = kDefaultTimerTypes;
    360     TimerData::Result timerResult = kDefaultTimerResult;
    361 
    362     bool doScale = false;
    363     bool doRotate = false;
    364     bool doClip = false;
    365     bool hasStrokeWidth = false;
    366 
    367 #if SK_SUPPORT_GPU
    368     struct {
    369         int     fBytes;
    370         int     fCount;
    371     } gpuCacheSize = { -1, -1 }; // -1s mean use the default
    372 #endif
    373 
    374     float strokeWidth;
    375     SkTDArray<const char*> fMatches;
    376     benchModes benchMode = kNormal_benchModes;
    377     SkString perIterTimeformat("%.2f");
    378     SkString normalTimeFormat("%6.2f");
    379 
    380     SkString outDir;
    381     SkBitmap::Config outConfig = SkBitmap::kNo_Config;
    382     const char* configName = "";
    383     Backend backend = kRaster_Backend;  // for warning
    384     int sampleCount = 0;
    385     SkTDArray<int> configs;
    386     bool userConfig = false;
    387 
    388     SkBenchLogger logger;
    389 
    390     char* const* stop = argv + argc;
    391     for (++argv; argv < stop; ++argv) {
    392         if (strcmp(*argv, "-o") == 0) {
    393             argv++;
    394             if (argv < stop && **argv) {
    395                 outDir.set(*argv);
    396                 if (outDir.c_str()[outDir.size() - 1] != '/') {
    397                     outDir.append("/");
    398                 }
    399             }
    400         } else if (strcmp(*argv, "--repeat") == 0) {
    401             argv++;
    402             if (argv < stop) {
    403                 repeatDraw = atoi(*argv);
    404                 if (repeatDraw < 1) {
    405                     repeatDraw = 1;
    406                 }
    407             } else {
    408                 logger.logError("missing arg for --repeat\n");
    409                 help();
    410                 return -1;
    411             }
    412         } else if (strcmp(*argv, "--logPerIter") == 0) {
    413             timerResult = TimerData::kPerIter_Result;
    414         } else if (strcmp(*argv, "--timers") == 0) {
    415             argv++;
    416             if (argv < stop) {
    417                 timerTypes = 0;
    418                 for (char* t = *argv; *t; ++t) {
    419                     switch (*t) {
    420                     case 'w': timerTypes |= TimerData::kWall_Flag; break;
    421                     case 'c': timerTypes |= TimerData::kCpu_Flag; break;
    422                     case 'W': timerTypes |= TimerData::kTruncatedWall_Flag; break;
    423                     case 'C': timerTypes |= TimerData::kTruncatedCpu_Flag; break;
    424                     case 'g': timerTypes |= TimerData::kGpu_Flag; break;
    425                     }
    426                 }
    427             } else {
    428                 logger.logError("missing arg for --timers\n");
    429                 help();
    430                 return -1;
    431             }
    432         } else if (!strcmp(*argv, "--rotate")) {
    433             doRotate = true;
    434         } else if (!strcmp(*argv, "--scale")) {
    435             doScale = true;
    436         } else if (!strcmp(*argv, "--clip")) {
    437             doClip = true;
    438         } else if (!strcmp(*argv, "--min")) {
    439             timerResult = TimerData::kMin_Result;
    440         } else if (strcmp(*argv, "--forceAA") == 0) {
    441             if (!parse_bool_arg(++argv, stop, &forceAA)) {
    442                 logger.logError("missing arg for --forceAA\n");
    443                 help();
    444                 return -1;
    445             }
    446         } else if (strcmp(*argv, "--forceFilter") == 0) {
    447             if (!parse_bool_arg(++argv, stop, &forceFilter)) {
    448                 logger.logError("missing arg for --forceFilter\n");
    449                 help();
    450                 return -1;
    451             }
    452         } else if (strcmp(*argv, "--forceDither") == 0) {
    453             bool tmp;
    454             if (!parse_bool_arg(++argv, stop, &tmp)) {
    455                 logger.logError("missing arg for --forceDither\n");
    456                 help();
    457                 return -1;
    458             }
    459             forceDither = tmp ? SkTriState::kTrue : SkTriState::kFalse;
    460         } else if (strcmp(*argv, "--forceBlend") == 0) {
    461             bool wantAlpha = false;
    462             if (!parse_bool_arg(++argv, stop, &wantAlpha)) {
    463                 logger.logError("missing arg for --forceBlend\n");
    464                 help();
    465                 return -1;
    466             }
    467             forceAlpha = wantAlpha ? 0x80 : 0xFF;
    468 #if SK_SUPPORT_GPU
    469         } else if (strcmp(*argv, "--gpuCacheSize") == 0) {
    470             if (stop - argv > 2) {
    471                 gpuCacheSize.fBytes = atoi(*++argv);
    472                 gpuCacheSize.fCount = atoi(*++argv);
    473             } else {
    474                 SkDebugf("missing arg for --gpuCacheSize\n");
    475                 help();
    476                 return -1;
    477             }
    478 #endif
    479         } else if (strcmp(*argv, "--mode") == 0) {
    480             argv++;
    481             if (argv < stop) {
    482                 if (strcmp(*argv, "normal") == 0) {
    483                     benchMode = kNormal_benchModes;
    484                 } else if (strcmp(*argv, "deferred") == 0) {
    485                     benchMode = kDeferred_benchModes;
    486                 } else if (strcmp(*argv, "deferredSilent") == 0) {
    487                     benchMode = kDeferredSilent_benchModes;
    488                 } else if (strcmp(*argv, "record") == 0) {
    489                     benchMode = kRecord_benchModes;
    490                 } else if (strcmp(*argv, "picturerecord") == 0) {
    491                     benchMode = kPictureRecord_benchModes;
    492                 } else {
    493                     logger.logError("bad arg for --mode\n");
    494                     help();
    495                     return -1;
    496                 }
    497             } else {
    498                 logger.logError("missing arg for --mode\n");
    499                 help();
    500                 return -1;
    501             }
    502         } else if (strcmp(*argv, "--strokeWidth") == 0) {
    503             argv++;
    504             if (argv < stop) {
    505                 const char *strokeWidthStr = *argv;
    506                 if (sscanf(strokeWidthStr, "%f", &strokeWidth) != 1) {
    507                   logger.logError("bad arg for --strokeWidth\n");
    508                   help();
    509                   return -1;
    510                 }
    511                 hasStrokeWidth = true;
    512             } else {
    513                 logger.logError("missing arg for --strokeWidth\n");
    514                 help();
    515                 return -1;
    516             }
    517         } else if (strcmp(*argv, "--match") == 0) {
    518             argv++;
    519             while (argv < stop && (*argv)[0] != '-') {
    520                 *fMatches.append() = *argv++;
    521             }
    522             argv--;
    523             if (!fMatches.count()) {
    524                 logger.logError("missing arg for --match\n");
    525                 help();
    526                 return -1;
    527             }
    528         } else if (strcmp(*argv, "--config") == 0) {
    529             argv++;
    530             if (argv < stop) {
    531                 int index = findConfig(*argv);
    532                 if (index >= 0) {
    533                     *configs.append() = index;
    534                     userConfig = true;
    535                 } else {
    536                     SkString str;
    537                     str.printf("unrecognized config %s\n", *argv);
    538                     logger.logError(str);
    539                     help();
    540                     return -1;
    541                 }
    542             } else {
    543                 logger.logError("missing arg for --config\n");
    544                 help();
    545                 return -1;
    546             }
    547         } else if (strcmp(*argv, "--logFile") == 0) {
    548             argv++;
    549             if (argv < stop) {
    550                 if (!logger.SetLogFile(*argv)) {
    551                     SkString str;
    552                     str.printf("Could not open %s for writing.", *argv);
    553                     logger.logError(str);
    554                     return -1;
    555                 }
    556             } else {
    557                 logger.logError("missing arg for --logFile\n");
    558                 help();
    559                 return -1;
    560             }
    561         } else if (strlen(*argv) > 2 && strncmp(*argv, "-D", 2) == 0) {
    562             argv++;
    563             if (argv < stop) {
    564                 defineDict.set(argv[-1] + 2, *argv);
    565             } else {
    566                 logger.logError("incomplete '-Dfoo bar' definition\n");
    567                 help();
    568                 return -1;
    569             }
    570         } else if (strcmp(*argv, "--help") == 0 || strcmp(*argv, "-h") == 0) {
    571             help();
    572             return 0;
    573         } else {
    574             SkString str;
    575             str.printf("unrecognized arg %s\n", *argv);
    576             logger.logError(str);
    577             help();
    578             return -1;
    579         }
    580     }
    581     if ((benchMode == kRecord_benchModes || benchMode == kPictureRecord_benchModes)
    582             && !outDir.isEmpty()) {
    583         logger.logError("'--mode record' and '--mode picturerecord' are not"
    584                   " compatible with -o.\n");
    585         return -1;
    586     }
    587     if ((benchMode == kRecord_benchModes || benchMode == kPictureRecord_benchModes)) {
    588         perIterTimeformat.set("%.4f");
    589         normalTimeFormat.set("%6.4f");
    590     }
    591     if (!userConfig) {
    592         // if no config is specified by user, add the default configs
    593         for (unsigned int i = 0; i < SK_ARRAY_COUNT(gConfigs); ++i) {
    594             if (gConfigs[i].fRunByDefault) {
    595                 *configs.append() = i;
    596             }
    597         }
    598     }
    599     if (kNormal_benchModes != benchMode) {
    600         // Non-rendering configs only run in normal mode
    601         for (int i = 0; i < configs.count(); ++i) {
    602             int configIdx = configs[i];
    603             if (kNonRendering_Backend == gConfigs[configIdx].fBackend) {
    604                 configs.remove(i, 1);
    605                 --i;
    606             }
    607         }
    608     }
    609 
    610 #if SK_SUPPORT_GPU
    611     for (int i = 0; i < configs.count(); ++i) {
    612         int configIdx = configs[i];
    613 
    614         if (kGPU_Backend == gConfigs[configIdx].fBackend && gConfigs[configIdx].fSampleCnt > 0) {
    615             GrContext* context = gContextFactory.get(gConfigs[configIdx].fContextType);
    616             if (NULL == context) {
    617                 SkString error;
    618                 error.printf("Error creating GrContext for config %s. Config will be skipped.\n",
    619                              gConfigs[configIdx].fName);
    620                 logger.logError(error.c_str());
    621                 configs.remove(i);
    622                 --i;
    623                 continue;
    624             }
    625             if (gConfigs[configIdx].fSampleCnt > context->getMaxSampleCount()){
    626                 SkString error;
    627                 error.printf("Sample count (%d) for config %s is unsupported. "
    628                              "Config will be skipped.\n",
    629                              gConfigs[configIdx].fSampleCnt, gConfigs[configIdx].fName);
    630                 logger.logError(error.c_str());
    631                 configs.remove(i);
    632                 --i;
    633                 continue;
    634             }
    635         }
    636     }
    637 #endif
    638 
    639     // report our current settings
    640     {
    641         SkString str;
    642         const char* deferredMode = benchMode == kDeferred_benchModes ? "yes" :
    643             (benchMode == kDeferredSilent_benchModes ? "silent" : "no");
    644         str.printf("skia bench: alpha=0x%02X antialias=%d filter=%d "
    645                    "deferred=%s logperiter=%d",
    646                    forceAlpha, forceAA, forceFilter, deferredMode,
    647                    TimerData::kPerIter_Result == timerResult);
    648         str.appendf(" rotate=%d scale=%d clip=%d min=%d",
    649                    doRotate, doScale, doClip, TimerData::kMin_Result == timerResult);
    650         str.appendf(" record=%d picturerecord=%d",
    651                     benchMode == kRecord_benchModes,
    652                     benchMode == kPictureRecord_benchModes);
    653         const char * ditherName;
    654         switch (forceDither) {
    655             case SkTriState::kDefault: ditherName = "default"; break;
    656             case SkTriState::kTrue: ditherName = "true"; break;
    657             case SkTriState::kFalse: ditherName = "false"; break;
    658             default: ditherName = "<invalid>"; break;
    659         }
    660         str.appendf(" dither=%s", ditherName);
    661 
    662         if (hasStrokeWidth) {
    663             str.appendf(" strokeWidth=%f", strokeWidth);
    664         } else {
    665             str.append(" strokeWidth=none");
    666         }
    667 
    668 #if defined(SK_SCALAR_IS_FLOAT)
    669         str.append(" scalar=float");
    670 #elif defined(SK_SCALAR_IS_FIXED)
    671         str.append(" scalar=fixed");
    672 #endif
    673 
    674 #if defined(SK_BUILD_FOR_WIN32)
    675         str.append(" system=WIN32");
    676 #elif defined(SK_BUILD_FOR_MAC)
    677         str.append(" system=MAC");
    678 #elif defined(SK_BUILD_FOR_ANDROID)
    679         str.append(" system=ANDROID");
    680 #elif defined(SK_BUILD_FOR_UNIX)
    681         str.append(" system=UNIX");
    682 #else
    683         str.append(" system=other");
    684 #endif
    685 
    686 #if defined(SK_DEBUG)
    687         str.append(" DEBUG");
    688 #endif
    689         str.append("\n");
    690         logger.logProgress(str);
    691     }
    692 
    693     SkTArray<BenchTimer*> timers(SK_ARRAY_COUNT(gConfigs));
    694     for (size_t i = 0; i < SK_ARRAY_COUNT(gConfigs); ++i) {
    695 #if SK_SUPPORT_GPU
    696         SkGLContextHelper* glCtx = NULL;
    697         if (kGPU_Backend == gConfigs[i].fBackend) {
    698             GrContext* context = gContextFactory.get(gConfigs[i].fContextType);
    699             if (NULL != context) {
    700                 // Set the user specified cache limits if non-default.
    701                 size_t bytes;
    702                 int count;
    703                 context->getTextureCacheLimits(&count, &bytes);
    704                 if (-1 != gpuCacheSize.fBytes) {
    705                     bytes = static_cast<size_t>(gpuCacheSize.fBytes);
    706                 }
    707                 if (-1 != gpuCacheSize.fCount) {
    708                     count = gpuCacheSize.fCount;
    709                 }
    710                 context->setTextureCacheLimits(count, bytes);
    711             }
    712             glCtx = gContextFactory.getGLContext(gConfigs[i].fContextType);
    713         }
    714         timers.push_back(SkNEW_ARGS(BenchTimer, (glCtx)));
    715 #else
    716         timers.push_back(SkNEW(BenchTimer));
    717 #endif
    718     }
    719 
    720     Iter iter(&defineDict);
    721     SkBenchmark* bench;
    722     while ((bench = iter.next()) != NULL) {
    723         SkAutoTUnref<SkBenchmark> benchUnref(bench);
    724 
    725         SkIPoint dim = bench->getSize();
    726         if (dim.fX <= 0 || dim.fY <= 0) {
    727             continue;
    728         }
    729 
    730         bench->setForceAlpha(forceAlpha);
    731         bench->setForceAA(forceAA);
    732         bench->setForceFilter(forceFilter);
    733         bench->setDither(forceDither);
    734         if (hasStrokeWidth) {
    735             bench->setStrokeWidth(strokeWidth);
    736         }
    737 
    738         // only run benchmarks if their name contains matchStr
    739         if (SkCommandLineFlags::ShouldSkip(fMatches, bench->getName())) {
    740             continue;
    741         }
    742 
    743         bool loggedBenchStart = false;
    744 
    745         AutoPrePostDraw appd(bench);
    746 
    747         for (int x = 0; x < configs.count(); ++x) {
    748             int configIndex = configs[x];
    749 
    750             bool setupFailed = false;
    751 
    752             if (kNonRendering_Backend == gConfigs[configIndex].fBackend) {
    753                 if (bench->isRendering()) {
    754                     continue;
    755                 }
    756             } else {
    757                 if (!bench->isRendering()) {
    758                     continue;
    759                 }
    760             }
    761 
    762             outConfig = gConfigs[configIndex].fConfig;
    763             configName = gConfigs[configIndex].fName;
    764             backend = gConfigs[configIndex].fBackend;
    765             sampleCount = gConfigs[configIndex].fSampleCnt;
    766             GrContext* context = NULL;
    767             BenchTimer* timer = timers[configIndex];
    768 
    769 #if SK_SUPPORT_GPU
    770             SkGLContextHelper* glContext = NULL;
    771             if (kGPU_Backend == backend) {
    772                 context = gContextFactory.get(gConfigs[configIndex].fContextType);
    773                 if (NULL == context) {
    774                     continue;
    775                 }
    776                 glContext = gContextFactory.getGLContext(gConfigs[configIndex].fContextType);
    777             }
    778 #endif
    779             SkDevice* device = NULL;
    780             SkCanvas* canvas = NULL;
    781             SkPicture pictureRecordFrom;
    782             SkPicture pictureRecordTo;
    783 
    784             if (kNonRendering_Backend != backend) {
    785                 device = make_device(outConfig, dim, backend, sampleCount, context);
    786                 if (NULL == device) {
    787                     SkString error;
    788                     error.printf("Device creation failure for config %s. Will skip.\n", configName);
    789                     logger.logError(error.c_str());
    790                     setupFailed = true;
    791                 } else {
    792                     switch(benchMode) {
    793                         case kDeferredSilent_benchModes:
    794                         case kDeferred_benchModes:
    795                             canvas = SkDeferredCanvas::Create(device);
    796                             break;
    797                         case kRecord_benchModes:
    798                             canvas = pictureRecordTo.beginRecording(dim.fX, dim.fY,
    799                                 SkPicture::kUsePathBoundsForClip_RecordingFlag);
    800                             canvas->ref();
    801                             break;
    802                         case kPictureRecord_benchModes: {
    803                             // This sets up picture-to-picture recording.
    804                             // The C++ drawing calls for the benchmark are recorded into
    805                             // pictureRecordFrom. As the benchmark, we will time how
    806                             // long it takes to playback pictureRecordFrom into
    807                             // pictureRecordTo.
    808                             SkCanvas* tempCanvas = pictureRecordFrom.beginRecording(dim.fX, dim.fY,
    809                                 SkPicture::kUsePathBoundsForClip_RecordingFlag);
    810                             bench->draw(tempCanvas);
    811                             pictureRecordFrom.endRecording();
    812                             canvas = pictureRecordTo.beginRecording(dim.fX, dim.fY,
    813                                 SkPicture::kUsePathBoundsForClip_RecordingFlag);
    814                             canvas->ref();
    815                             break;
    816                         }
    817                         case kNormal_benchModes:
    818                             canvas = new SkCanvas(device);
    819                             break;
    820                         default:
    821                             SkASSERT(0);
    822                     }
    823                     device->unref();
    824                     canvas->clear(SK_ColorWHITE);
    825                 }
    826             }
    827             SkAutoUnref canvasUnref(canvas);
    828             if (!setupFailed) {
    829                 if (NULL != canvas) {
    830                     if (doClip) {
    831                         performClip(canvas, dim.fX, dim.fY);
    832                     }
    833                     if (doScale) {
    834                         performScale(canvas, dim.fX, dim.fY);
    835                     }
    836                     if (doRotate) {
    837                         performRotate(canvas, dim.fX, dim.fY);
    838                     }
    839                 }
    840 
    841                 if (!loggedBenchStart) {
    842                     loggedBenchStart = true;
    843                     SkString str;
    844                     str.printf("running bench [%d %d] %28s", dim.fX, dim.fY, bench->getName());
    845                     logger.logProgress(str);
    846                 }
    847 
    848                 // warm up caches if needed
    849                 if (repeatDraw > 1 && NULL != canvas) {
    850 #if SK_SUPPORT_GPU
    851                     // purge the GPU resources to reduce variance
    852                     if (NULL != context) {
    853                         context->freeGpuResources();
    854                     }
    855 #endif
    856                     SkAutoCanvasRestore acr(canvas, true);
    857                     if (benchMode == kPictureRecord_benchModes) {
    858                         pictureRecordFrom.draw(canvas);
    859                     } else {
    860                         bench->draw(canvas);
    861                     }
    862 
    863                     if (kDeferredSilent_benchModes == benchMode) {
    864                         static_cast<SkDeferredCanvas*>(canvas)->silentFlush();
    865                     } else {
    866                         canvas->flush();
    867                     }
    868 #if SK_SUPPORT_GPU
    869                     if (NULL != context) {
    870                         context->flush();
    871                         SK_GL(*glContext, Finish());
    872                     }
    873 #endif
    874                 }
    875 
    876                 // record timer values for each repeat, and their sum
    877                 TimerData timerData(repeatDraw);
    878                 for (int i = 0; i < repeatDraw; i++) {
    879                     if ((benchMode == kRecord_benchModes || benchMode == kPictureRecord_benchModes)) {
    880                         // This will clear the recorded commands so that they do not
    881                         // accumulate.
    882                         canvas = pictureRecordTo.beginRecording(dim.fX, dim.fY,
    883                             SkPicture::kUsePathBoundsForClip_RecordingFlag);
    884                     }
    885 
    886                     timer->start(bench->getDurationScale());
    887                     if (NULL != canvas) {
    888                         canvas->save();
    889                     }
    890                     if (benchMode == kPictureRecord_benchModes) {
    891                         pictureRecordFrom.draw(canvas);
    892                     } else {
    893                         bench->draw(canvas);
    894                     }
    895 
    896                     if (kDeferredSilent_benchModes == benchMode) {
    897                         static_cast<SkDeferredCanvas*>(canvas)->silentFlush();
    898                     } else if (NULL != canvas) {
    899                         canvas->flush();
    900                     }
    901 
    902                     if (NULL != canvas) {
    903                         canvas->restore();
    904                     }
    905 
    906                     // stop the truncated timer after the last canvas call but
    907                     // don't wait for all the GL calls to complete
    908                     timer->truncatedEnd();
    909 #if SK_SUPPORT_GPU
    910                     if (NULL != glContext) {
    911                         context->flush();
    912                         SK_GL(*glContext, Finish());
    913                     }
    914 #endif
    915                     // stop the inclusive and gpu timers once all the GL calls
    916                     // have completed
    917                     timer->end();
    918 
    919                     SkAssertResult(timerData.appendTimes(timer));
    920 
    921                 }
    922                 if (repeatDraw > 1) {
    923                     const char* timeFormat;
    924                     if (TimerData::kPerIter_Result == timerResult) {
    925                         timeFormat = perIterTimeformat.c_str();
    926                     } else {
    927                         timeFormat = normalTimeFormat.c_str();
    928                     }
    929                     uint32_t filteredTimerTypes = timerTypes;
    930                     if (NULL == context) {
    931                         filteredTimerTypes &= ~TimerData::kGpu_Flag;
    932                     }
    933                     SkString result = timerData.getResult(timeFormat,
    934                                         timerResult,
    935                                         configName,
    936                                         filteredTimerTypes);
    937                     logger.logProgress(result);
    938                 }
    939                 if (outDir.size() > 0 && kNonRendering_Backend != backend) {
    940                     saveFile(bench->getName(), configName, outDir.c_str(),
    941                              device->accessBitmap(false));
    942                 }
    943             }
    944         }
    945         if (loggedBenchStart) {
    946             logger.logProgress(SkString("\n"));
    947         }
    948     }
    949 #if SK_SUPPORT_GPU
    950 #if GR_CACHE_STATS
    951     for (int i = 0; i <= GrContextFactory::kLastGLContextType; ++i) {
    952         GrContextFactory::GLContextType ctxType = (GrContextFactory::GLContextType)i;
    953         GrContext* context = gContextFactory.get(ctxType);
    954         if (NULL != context) {
    955             SkDebugf("Cache Stats for %s context:\n", GrContextFactory::GLContextTypeName(ctxType));
    956             context->printCacheStats();
    957             SkDebugf("\n");
    958         }
    959     }
    960 #endif
    961     // Destroy the GrContext before the inst tracking printing at main() exit occurs.
    962     gContextFactory.destroyContexts();
    963 #endif
    964     for (size_t i = 0; i < SK_ARRAY_COUNT(gConfigs); ++i) {
    965         SkDELETE(timers[i]);
    966     }
    967 
    968     return 0;
    969 }
    970 
    971 #if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL)
    972 int main(int argc, char * const argv[]) {
    973     return tool_main(argc, (char**) argv);
    974 }
    975 #endif
    976