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 "GrRenderTarget.h"
     15 #if SK_ANGLE
     16 #include "gl/SkANGLEGLContext.h"
     17 #endif // SK_ANGLE
     18 #include "gl/SkNativeGLContext.h"
     19 #include "gl/SkNullGLContext.h"
     20 #include "gl/SkDebugGLContext.h"
     21 #include "SkGpuDevice.h"
     22 #endif // SK_SUPPORT_GPU
     23 
     24 #include "SkBenchLogger.h"
     25 #include "SkBenchmark.h"
     26 #include "SkCanvas.h"
     27 #include "SkDeferredCanvas.h"
     28 #include "SkDevice.h"
     29 #include "SkColorPriv.h"
     30 #include "SkGraphics.h"
     31 #include "SkImageEncoder.h"
     32 #include "SkNWayCanvas.h"
     33 #include "SkPicture.h"
     34 #include "SkString.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     kRaster_Backend,
    191     kGPU_Backend,
    192     kPDF_Backend,
    193 };
    194 
    195 #if SK_SUPPORT_GPU
    196 class GLHelper {
    197 public:
    198     GLHelper() {
    199     }
    200 
    201     bool init(SkGLContext* glCtx, int width, int height) {
    202         GrContext* grCtx;
    203         if (!glCtx->init(width, height)) {
    204             return false;
    205         }
    206         GrBackendContext ctx = reinterpret_cast<GrBackendContext>(glCtx->gl());
    207         grCtx = GrContext::Create(kOpenGL_GrBackend, ctx);
    208         if (NULL != grCtx) {
    209             GrBackendRenderTargetDesc desc;
    210             desc.fConfig = kSkia8888_PM_GrPixelConfig;
    211             desc.fWidth = width;
    212             desc.fHeight = height;
    213             desc.fStencilBits = 8;
    214             desc.fRenderTargetHandle = glCtx->getFBOID();
    215             GrRenderTarget* rt = grCtx->wrapBackendRenderTarget(desc);
    216             if (NULL == rt) {
    217                 grCtx->unref();
    218                 return false;
    219             }
    220             glCtx->ref();
    221             fGLContext.reset(glCtx);
    222             fGrContext.reset(grCtx);
    223             fRenderTarget.reset(rt);
    224         }
    225         return true;
    226     }
    227 
    228     void cleanup() {
    229         fGLContext.reset(NULL);
    230         fGrContext.reset(NULL);
    231         fRenderTarget.reset(NULL);
    232     }
    233 
    234     bool isValid() {
    235         return NULL != fGLContext.get();
    236     }
    237 
    238     SkGLContext* glContext() {
    239         return fGLContext.get();
    240     }
    241 
    242     GrRenderTarget* renderTarget() {
    243         return fRenderTarget.get();
    244     }
    245 
    246     GrContext* grContext() {
    247         return fGrContext.get();
    248     }
    249 private:
    250     SkAutoTUnref<SkGLContext> fGLContext;
    251     SkAutoTUnref<GrContext> fGrContext;
    252     SkAutoTUnref<GrRenderTarget> fRenderTarget;
    253 };
    254 
    255 static GLHelper gRealGLHelper;
    256 static GLHelper gNullGLHelper;
    257 static GLHelper gDebugGLHelper;
    258 #if SK_ANGLE
    259 static GLHelper gANGLEGLHelper;
    260 #endif // SK_ANGLE
    261 #else  // !SK_SUPPORT_GPU
    262 class GLHelper;
    263 class SkGLContext;
    264 #endif // !SK_SUPPORT_GPU
    265 static SkDevice* make_device(SkBitmap::Config config, const SkIPoint& size,
    266                              Backend backend, GLHelper* glHelper) {
    267     SkDevice* device = NULL;
    268     SkBitmap bitmap;
    269     bitmap.setConfig(config, size.fX, size.fY);
    270 
    271     switch (backend) {
    272         case kRaster_Backend:
    273             bitmap.allocPixels();
    274             erase(bitmap);
    275             device = new SkDevice(bitmap);
    276             break;
    277 #if SK_SUPPORT_GPU
    278         case kGPU_Backend:
    279             device = new SkGpuDevice(glHelper->grContext(),
    280                                      glHelper->renderTarget());
    281             break;
    282 #endif
    283         case kPDF_Backend:
    284         default:
    285             SkASSERT(!"unsupported");
    286     }
    287     return device;
    288 }
    289 
    290 static const struct {
    291     SkBitmap::Config    fConfig;
    292     const char*         fName;
    293     Backend             fBackend;
    294     GLHelper*           fGLHelper;
    295 } gConfigs[] = {
    296     { SkBitmap::kARGB_8888_Config,  "8888",     kRaster_Backend, NULL },
    297     { SkBitmap::kRGB_565_Config,    "565",      kRaster_Backend, NULL },
    298 #if SK_SUPPORT_GPU
    299     { SkBitmap::kARGB_8888_Config,  "GPU",      kGPU_Backend, &gRealGLHelper },
    300 #if SK_ANGLE
    301     { SkBitmap::kARGB_8888_Config,  "ANGLE",    kGPU_Backend, &gANGLEGLHelper },
    302 #endif // SK_ANGLE
    303 #ifdef SK_DEBUG
    304     { SkBitmap::kARGB_8888_Config,  "Debug",    kGPU_Backend, &gDebugGLHelper },
    305 #endif // SK_DEBUG
    306     { SkBitmap::kARGB_8888_Config,  "NULLGPU",  kGPU_Backend, &gNullGLHelper },
    307 #endif // SK_SUPPORT_GPU
    308 };
    309 
    310 static int findConfig(const char config[]) {
    311     for (size_t i = 0; i < SK_ARRAY_COUNT(gConfigs); i++) {
    312         if (!strcmp(config, gConfigs[i].fName)) {
    313             return i;
    314         }
    315     }
    316     return -1;
    317 }
    318 
    319 static void determine_gpu_context_size(SkTDict<const char*>& defineDict,
    320                                        int* contextWidth,
    321                                        int* contextHeight) {
    322     Iter iter(&defineDict);
    323     SkBenchmark* bench;
    324     while ((bench = iter.next()) != NULL) {
    325         SkIPoint dim = bench->getSize();
    326         if (*contextWidth < dim.fX) {
    327             *contextWidth = dim.fX;
    328         }
    329         if (*contextHeight < dim.fY) {
    330             *contextHeight = dim.fY;
    331         }
    332         bench->unref();
    333     }
    334 }
    335 
    336 static bool skip_name(const SkTDArray<const char*> array, const char name[]) {
    337     if (0 == array.count()) {
    338         // no names, so don't skip anything
    339         return false;
    340     }
    341     for (int i = 0; i < array.count(); ++i) {
    342         if (strstr(name, array[i])) {
    343             // found the name, so don't skip
    344             return false;
    345         }
    346     }
    347     return true;
    348 }
    349 
    350 static void help() {
    351     SkDebugf("Usage: bench [-o outDir] [--repeat nr] [--logPerIter 1|0] "
    352                           "[--timers [wcgWC]*] [--rotate]\n"
    353              "    [--scale] [--clip] [--min] [--forceAA 1|0] [--forceFilter 1|0]\n"
    354              "    [--forceDither 1|0] [--forceBlend 1|0] [--strokeWidth width]\n"
    355              "    [--match name] [--mode normal|deferred|deferredSilent|record|picturerecord]\n"
    356              "    [--config 8888|565|GPU|ANGLE|NULLGPU] [-Dfoo bar] [--logFile filename]\n"
    357              "    [-h|--help]");
    358     SkDebugf("\n\n");
    359     SkDebugf("    -o outDir : Image of each bench will be put in outDir.\n");
    360     SkDebugf("    --repeat nr : Each bench repeats for nr times.\n");
    361     SkDebugf("    --logPerIter 1|0 : "
    362              "Log each repeat timer instead of mean, default is disabled.\n");
    363     SkDebugf("    --timers [wcgWC]* : "
    364              "Display wall, cpu, gpu, truncated wall or truncated cpu time for each bench.\n");
    365     SkDebugf("    --rotate : Rotate before each bench runs.\n");
    366     SkDebugf("    --scale : Scale before each bench runs.\n");
    367     SkDebugf("    --clip : Clip before each bench runs.\n");
    368     SkDebugf("    --min : Print the minimum times (instead of average).\n");
    369     SkDebugf("    --forceAA 1|0 : "
    370              "Enable/disable anti-aliased, default is enabled.\n");
    371     SkDebugf("    --forceFilter 1|0 : "
    372              "Enable/disable bitmap filtering, default is disabled.\n");
    373     SkDebugf("    --forceDither 1|0 : "
    374              "Enable/disable dithering, default is disabled.\n");
    375     SkDebugf("    --forceBlend 1|0 : "
    376              "Enable/disable dithering, default is disabled.\n");
    377     SkDebugf("    --strokeWidth width : The width for path stroke.\n");
    378     SkDebugf("    --match name : Only run bench whose name is matched.\n");
    379     SkDebugf("    --mode normal|deferred|deferredSilent|record|picturerecord :\n"
    380              "             Run in the corresponding mode\n"
    381              "                 normal, Use a normal canvas to draw to;\n"
    382              "                 deferred, Use a deferrred canvas when drawing;\n"
    383              "                 deferredSilent, deferred with silent playback;\n"
    384              "                 record, Benchmark the time to record to an SkPicture;\n"
    385              "                 picturerecord, Benchmark the time to do record from a \n"
    386              "                                SkPicture to a SkPicture.\n");
    387     SkDebugf("    --logFile filename : destination for writing log output, in addition to stdout.\n");
    388 #if SK_SUPPORT_GPU
    389     SkDebugf("    --config 8888|565|GPU|ANGLE|NULLGPU : "
    390              "Run bench in corresponding config mode.\n");
    391 #else
    392     SkDebugf("    --config 8888|565: "
    393              "Run bench in corresponding config mode.\n");
    394 #endif
    395     SkDebugf("    -Dfoo bar : Add extra definition to bench.\n");
    396     SkDebugf("    -h|--help : Show this help message.\n");
    397 }
    398 
    399 int tool_main(int argc, char** argv);
    400 int tool_main(int argc, char** argv) {
    401 #if SK_ENABLE_INST_COUNT
    402     gPrintInstCount = true;
    403 #endif
    404     SkAutoGraphics ag;
    405 
    406     SkTDict<const char*> defineDict(1024);
    407     int repeatDraw = 1;
    408     bool logPerIter = false;
    409     int forceAlpha = 0xFF;
    410     bool forceAA = true;
    411     bool forceFilter = false;
    412     SkTriState::State forceDither = SkTriState::kDefault;
    413     bool timerWall = false;
    414     bool truncatedTimerWall = false;
    415     bool timerCpu = true;
    416     bool truncatedTimerCpu = false;
    417     bool timerGpu = true;
    418     bool doScale = false;
    419     bool doRotate = false;
    420     bool doClip = false;
    421     bool printMin = false;
    422     bool hasStrokeWidth = false;
    423     float strokeWidth;
    424     SkTDArray<const char*> fMatches;
    425     benchModes benchMode = kNormal_benchModes;
    426     SkString perIterTimeformat("%.2f");
    427     SkString normalTimeFormat("%6.2f");
    428 
    429     SkString outDir;
    430     SkBitmap::Config outConfig = SkBitmap::kNo_Config;
    431     GLHelper* glHelper = NULL;
    432     const char* configName = "";
    433     Backend backend = kRaster_Backend;  // for warning
    434     SkTDArray<int> configs;
    435     bool userConfig = false;
    436 
    437     SkBenchLogger logger;
    438 
    439     char* const* stop = argv + argc;
    440     for (++argv; argv < stop; ++argv) {
    441         if (strcmp(*argv, "-o") == 0) {
    442             argv++;
    443             if (argv < stop && **argv) {
    444                 outDir.set(*argv);
    445                 if (outDir.c_str()[outDir.size() - 1] != '/') {
    446                     outDir.append("/");
    447                 }
    448             }
    449         } else if (strcmp(*argv, "--repeat") == 0) {
    450             argv++;
    451             if (argv < stop) {
    452                 repeatDraw = atoi(*argv);
    453                 if (repeatDraw < 1) {
    454                     repeatDraw = 1;
    455                 }
    456             } else {
    457                 logger.logError("missing arg for --repeat\n");
    458                 help();
    459                 return -1;
    460             }
    461         } else if (strcmp(*argv, "--logPerIter") == 0) {
    462             if (!parse_bool_arg(++argv, stop, &logPerIter)) {
    463                 logger.logError("missing arg for --logPerIter\n");
    464                 help();
    465                 return -1;
    466             }
    467         } else if (strcmp(*argv, "--timers") == 0) {
    468             argv++;
    469             if (argv < stop) {
    470                 timerWall = false;
    471                 truncatedTimerWall = false;
    472                 timerCpu = false;
    473                 truncatedTimerCpu = false;
    474                 timerGpu = false;
    475                 for (char* t = *argv; *t; ++t) {
    476                     switch (*t) {
    477                     case 'w': timerWall = true; break;
    478                     case 'c': timerCpu = true; break;
    479                     case 'W': truncatedTimerWall = true; break;
    480                     case 'C': truncatedTimerCpu = true; break;
    481                     case 'g': timerGpu = true; break;
    482                     }
    483                 }
    484             } else {
    485                 logger.logError("missing arg for --timers\n");
    486                 help();
    487                 return -1;
    488             }
    489         } else if (!strcmp(*argv, "--rotate")) {
    490             doRotate = true;
    491         } else if (!strcmp(*argv, "--scale")) {
    492             doScale = true;
    493         } else if (!strcmp(*argv, "--clip")) {
    494             doClip = true;
    495         } else if (!strcmp(*argv, "--min")) {
    496             printMin = true;
    497         } else if (strcmp(*argv, "--forceAA") == 0) {
    498             if (!parse_bool_arg(++argv, stop, &forceAA)) {
    499                 logger.logError("missing arg for --forceAA\n");
    500                 help();
    501                 return -1;
    502             }
    503         } else if (strcmp(*argv, "--forceFilter") == 0) {
    504             if (!parse_bool_arg(++argv, stop, &forceFilter)) {
    505                 logger.logError("missing arg for --forceFilter\n");
    506                 help();
    507                 return -1;
    508             }
    509         } else if (strcmp(*argv, "--forceDither") == 0) {
    510             bool tmp;
    511             if (!parse_bool_arg(++argv, stop, &tmp)) {
    512                 logger.logError("missing arg for --forceDither\n");
    513                 help();
    514                 return -1;
    515             }
    516             forceDither = tmp ? SkTriState::kTrue : SkTriState::kFalse;
    517         } else if (strcmp(*argv, "--forceBlend") == 0) {
    518             bool wantAlpha = false;
    519             if (!parse_bool_arg(++argv, stop, &wantAlpha)) {
    520                 logger.logError("missing arg for --forceBlend\n");
    521                 help();
    522                 return -1;
    523             }
    524             forceAlpha = wantAlpha ? 0x80 : 0xFF;
    525         } else if (strcmp(*argv, "--mode") == 0) {
    526             argv++;
    527             if (argv < stop) {
    528                 if (strcmp(*argv, "normal") == 0) {
    529                     benchMode = kNormal_benchModes;
    530                 } else if (strcmp(*argv, "deferred") == 0) {
    531                     benchMode = kDeferred_benchModes;
    532                 } else if (strcmp(*argv, "deferredSilent") == 0) {
    533                     benchMode = kDeferredSilent_benchModes;
    534                 } else if (strcmp(*argv, "record") == 0) {
    535                     benchMode = kRecord_benchModes;
    536                 } else if (strcmp(*argv, "picturerecord") == 0) {
    537                     benchMode = kPictureRecord_benchModes;
    538                 } else {
    539                     logger.logError("bad arg for --mode\n");
    540                     help();
    541                     return -1;
    542                 }
    543             } else {
    544                 logger.logError("missing arg for --mode\n");
    545                 help();
    546                 return -1;
    547             }
    548         } else if (strcmp(*argv, "--strokeWidth") == 0) {
    549             argv++;
    550             if (argv < stop) {
    551                 const char *strokeWidthStr = *argv;
    552                 if (sscanf(strokeWidthStr, "%f", &strokeWidth) != 1) {
    553                   logger.logError("bad arg for --strokeWidth\n");
    554                   help();
    555                   return -1;
    556                 }
    557                 hasStrokeWidth = true;
    558             } else {
    559                 logger.logError("missing arg for --strokeWidth\n");
    560                 help();
    561                 return -1;
    562             }
    563         } else if (strcmp(*argv, "--match") == 0) {
    564             argv++;
    565             if (argv < stop) {
    566                 *fMatches.append() = *argv;
    567             } else {
    568                 logger.logError("missing arg for --match\n");
    569                 help();
    570                 return -1;
    571             }
    572         } else if (strcmp(*argv, "--config") == 0) {
    573             argv++;
    574             if (argv < stop) {
    575                 int index = findConfig(*argv);
    576                 if (index >= 0) {
    577                     *configs.append() = index;
    578                     userConfig = true;
    579                 } else {
    580                     SkString str;
    581                     str.printf("unrecognized config %s\n", *argv);
    582                     logger.logError(str);
    583                     help();
    584                     return -1;
    585                 }
    586             } else {
    587                 logger.logError("missing arg for --config\n");
    588                 help();
    589                 return -1;
    590             }
    591         } else if (strcmp(*argv, "--logFile") == 0) {
    592             argv++;
    593             if (argv < stop) {
    594                 if (!logger.SetLogFile(*argv)) {
    595                     SkString str;
    596                     str.printf("Could not open %s for writing.", *argv);
    597                     logger.logError(str);
    598                     return -1;
    599                 }
    600             } else {
    601                 logger.logError("missing arg for --logFile\n");
    602                 help();
    603                 return -1;
    604             }
    605         } else if (strlen(*argv) > 2 && strncmp(*argv, "-D", 2) == 0) {
    606             argv++;
    607             if (argv < stop) {
    608                 defineDict.set(argv[-1] + 2, *argv);
    609             } else {
    610                 logger.logError("incomplete '-Dfoo bar' definition\n");
    611                 help();
    612                 return -1;
    613             }
    614         } else if (strcmp(*argv, "--help") == 0 || strcmp(*argv, "-h") == 0) {
    615             help();
    616             return 0;
    617         } else {
    618             SkString str;
    619             str.printf("unrecognized arg %s\n", *argv);
    620             logger.logError(str);
    621             help();
    622             return -1;
    623         }
    624     }
    625     if ((benchMode == kRecord_benchModes || benchMode == kPictureRecord_benchModes)
    626             && !outDir.isEmpty()) {
    627         logger.logError("'--mode record' and '--mode picturerecord' are not"
    628                   " compatible with -o.\n");
    629         return -1;
    630     }
    631     if ((benchMode == kRecord_benchModes || benchMode == kPictureRecord_benchModes)) {
    632         perIterTimeformat.set("%.4f");
    633         normalTimeFormat.set("%6.4f");
    634     }
    635     if (!userConfig) {
    636         // if no config is specified by user, we add them all.
    637         for (unsigned int i = 0; i < SK_ARRAY_COUNT(gConfigs); ++i) {
    638             *configs.append() = i;
    639         }
    640     }
    641 
    642     // report our current settings
    643     {
    644         SkString str;
    645         const char* deferredMode = benchMode == kDeferred_benchModes ? "yes" :
    646             (benchMode == kDeferredSilent_benchModes ? "silent" : "no");
    647         str.printf("skia bench: alpha=0x%02X antialias=%d filter=%d "
    648                    "deferred=%s logperiter=%d",
    649                    forceAlpha, forceAA, forceFilter, deferredMode,
    650                    logPerIter);
    651         str.appendf(" rotate=%d scale=%d clip=%d min=%d",
    652                    doRotate, doScale, doClip, printMin);
    653         str.appendf(" record=%d picturerecord=%d",
    654                     benchMode == kRecord_benchModes,
    655                     benchMode == kPictureRecord_benchModes);
    656         const char * ditherName;
    657         switch (forceDither) {
    658             case SkTriState::kDefault: ditherName = "default"; break;
    659             case SkTriState::kTrue: ditherName = "true"; break;
    660             case SkTriState::kFalse: ditherName = "false"; break;
    661             default: ditherName = "<invalid>"; break;
    662         }
    663         str.appendf(" dither=%s", ditherName);
    664 
    665         if (hasStrokeWidth) {
    666             str.appendf(" strokeWidth=%f", strokeWidth);
    667         } else {
    668             str.append(" strokeWidth=none");
    669         }
    670 
    671 #if defined(SK_SCALAR_IS_FLOAT)
    672         str.append(" scalar=float");
    673 #elif defined(SK_SCALAR_IS_FIXED)
    674         str.append(" scalar=fixed");
    675 #endif
    676 
    677 #if defined(SK_BUILD_FOR_WIN32)
    678         str.append(" system=WIN32");
    679 #elif defined(SK_BUILD_FOR_MAC)
    680         str.append(" system=MAC");
    681 #elif defined(SK_BUILD_FOR_ANDROID)
    682         str.append(" system=ANDROID");
    683 #elif defined(SK_BUILD_FOR_UNIX)
    684         str.append(" system=UNIX");
    685 #else
    686         str.append(" system=other");
    687 #endif
    688 
    689 #if defined(SK_DEBUG)
    690         str.append(" DEBUG");
    691 #endif
    692         str.append("\n");
    693         logger.logProgress(str);
    694     }
    695 
    696     SkGLContext* timerCtx = NULL;
    697     //Don't do GL when fixed.
    698 #if !defined(SK_SCALAR_IS_FIXED) && SK_SUPPORT_GPU
    699     int contextWidth = 1024;
    700     int contextHeight = 1024;
    701     determine_gpu_context_size(defineDict, &contextWidth, &contextHeight);
    702     SkAutoTUnref<SkGLContext> realGLCtx(new SkNativeGLContext);
    703     SkAutoTUnref<SkGLContext> nullGLCtx(new SkNullGLContext);
    704     SkAutoTUnref<SkGLContext> debugGLCtx(new SkDebugGLContext);
    705     gRealGLHelper.init(realGLCtx.get(), contextWidth, contextHeight);
    706     gNullGLHelper.init(nullGLCtx.get(), contextWidth, contextHeight);
    707     gDebugGLHelper.init(debugGLCtx.get(), contextWidth, contextHeight);
    708 #if SK_ANGLE
    709     SkAutoTUnref<SkGLContext> angleGLCtx(new SkANGLEGLContext);
    710     gANGLEGLHelper.init(angleGLCtx.get(), contextWidth, contextHeight);
    711 #endif // SK_ANGLE
    712     timerCtx = gRealGLHelper.glContext();
    713 #endif // !defined(SK_SCALAR_IS_FIXED) && SK_SUPPORT_GPU
    714 
    715     BenchTimer timer = BenchTimer(timerCtx);
    716     Iter iter(&defineDict);
    717     SkBenchmark* bench;
    718     while ((bench = iter.next()) != NULL) {
    719         SkAutoTUnref<SkBenchmark> benchUnref(bench);
    720 
    721         SkIPoint dim = bench->getSize();
    722         if (dim.fX <= 0 || dim.fY <= 0) {
    723             continue;
    724         }
    725 
    726         bench->setForceAlpha(forceAlpha);
    727         bench->setForceAA(forceAA);
    728         bench->setForceFilter(forceFilter);
    729         bench->setDither(forceDither);
    730         if (hasStrokeWidth) {
    731             bench->setStrokeWidth(strokeWidth);
    732         }
    733 
    734         // only run benchmarks if their name contains matchStr
    735         if (skip_name(fMatches, bench->getName())) {
    736             continue;
    737         }
    738 
    739         {
    740             SkString str;
    741             str.printf("running bench [%d %d] %28s", dim.fX, dim.fY,
    742                        bench->getName());
    743             logger.logProgress(str);
    744         }
    745 
    746         AutoPrePostDraw appd(bench);
    747 
    748         bool runOnce = false;
    749         for (int x = 0; x < configs.count(); ++x) {
    750             if (!bench->isRendering() && runOnce) {
    751                 continue;
    752             }
    753             runOnce = true;
    754 
    755             int configIndex = configs[x];
    756 
    757             outConfig = gConfigs[configIndex].fConfig;
    758             configName = gConfigs[configIndex].fName;
    759             backend = gConfigs[configIndex].fBackend;
    760             glHelper = gConfigs[configIndex].fGLHelper;
    761 
    762 #if SK_SUPPORT_GPU
    763             if (kGPU_Backend == backend &&
    764                 (NULL == glHelper || !glHelper->isValid())) {
    765                 continue;
    766             }
    767 #endif
    768             SkDevice* device = make_device(outConfig, dim, backend, glHelper);
    769             SkCanvas* canvas = NULL;
    770             SkPicture pictureRecordFrom;
    771             SkPicture pictureRecordTo;
    772             switch(benchMode) {
    773                 case kDeferredSilent_benchModes:
    774                 case kDeferred_benchModes:
    775                     canvas = new SkDeferredCanvas(device);
    776                     break;
    777                 case kRecord_benchModes:
    778                     canvas = pictureRecordTo.beginRecording(dim.fX, dim.fY,
    779                         SkPicture::kUsePathBoundsForClip_RecordingFlag);
    780                     canvas->ref();
    781                     break;
    782                 case kPictureRecord_benchModes: {
    783                     // This sets up picture-to-picture recording.
    784                     // The C++ drawing calls for the benchmark are recorded into
    785                     // pictureRecordFrom. As the benchmark, we will time how
    786                     // long it takes to playback pictureRecordFrom into
    787                     // pictureRecordTo.
    788                     SkCanvas* tempCanvas = pictureRecordFrom.beginRecording(dim.fX, dim.fY,
    789                         SkPicture::kUsePathBoundsForClip_RecordingFlag);
    790                     bench->draw(tempCanvas);
    791                     pictureRecordFrom.endRecording();
    792                     canvas = pictureRecordTo.beginRecording(dim.fX, dim.fY,
    793                         SkPicture::kUsePathBoundsForClip_RecordingFlag);
    794                     canvas->ref();
    795                     break;
    796                 }
    797                 case kNormal_benchModes:
    798                     canvas = new SkCanvas(device);
    799                     break;
    800                 default:
    801                     SkASSERT(0);
    802             }
    803             device->unref();
    804             SkAutoUnref canvasUnref(canvas);
    805 
    806             if (doClip) {
    807                 performClip(canvas, dim.fX, dim.fY);
    808             }
    809             if (doScale) {
    810                 performScale(canvas, dim.fX, dim.fY);
    811             }
    812             if (doRotate) {
    813                 performRotate(canvas, dim.fX, dim.fY);
    814             }
    815 
    816             // warm up caches if needed
    817             if (repeatDraw > 1) {
    818 #if SK_SUPPORT_GPU
    819                 if (glHelper) {
    820                     // purge the GPU resources to reduce variance
    821                     glHelper->grContext()->freeGpuResources();
    822                 }
    823 #endif
    824                 SkAutoCanvasRestore acr(canvas, true);
    825                 if (benchMode == kPictureRecord_benchModes) {
    826                     pictureRecordFrom.draw(canvas);
    827                 } else {
    828                     bench->draw(canvas);
    829                 }
    830 
    831                 if (kDeferredSilent_benchModes == benchMode) {
    832                     static_cast<SkDeferredCanvas*>(canvas)->silentFlush();
    833                 } else {
    834                     canvas->flush();
    835                 }
    836 #if SK_SUPPORT_GPU
    837                 if (glHelper) {
    838                     glHelper->grContext()->flush();
    839                     SK_GL(*glHelper->glContext(), Finish());
    840                 }
    841 #endif
    842             }
    843 
    844             // record timer values for each repeat, and their sum
    845             TimerData timerData(perIterTimeformat, normalTimeFormat);
    846             for (int i = 0; i < repeatDraw; i++) {
    847                 if ((benchMode == kRecord_benchModes
    848                      || benchMode == kPictureRecord_benchModes)) {
    849                     // This will clear the recorded commands so that they do not
    850                     // acculmulate.
    851                     canvas = pictureRecordTo.beginRecording(dim.fX, dim.fY,
    852                         SkPicture::kUsePathBoundsForClip_RecordingFlag);
    853                 }
    854 
    855                 timer.start();
    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 
    869                 // stop the truncated timer after the last canvas call but
    870                 // don't wait for all the GL calls to complete
    871                 timer.truncatedEnd();
    872 #if SK_SUPPORT_GPU
    873                 if (glHelper) {
    874                     glHelper->grContext()->flush();
    875                     SK_GL(*glHelper->glContext(), Finish());
    876                 }
    877 #endif
    878                 // stop the inclusive and gpu timers once all the GL calls
    879                 // have completed
    880                 timer.end();
    881 
    882                 timerData.appendTimes(&timer, repeatDraw - 1 == i);
    883 
    884             }
    885             if (repeatDraw > 1) {
    886                 SkString result = timerData.getResult(logPerIter, printMin, repeatDraw, configName,
    887                                                       timerWall, truncatedTimerWall, timerCpu,
    888                                                       truncatedTimerCpu, timerGpu && glHelper);
    889                 logger.logProgress(result);
    890             }
    891             if (outDir.size() > 0) {
    892                 saveFile(bench->getName(), configName, outDir.c_str(),
    893                          device->accessBitmap(false));
    894                 canvas->clear(SK_ColorWHITE);
    895             }
    896         }
    897         logger.logProgress(SkString("\n"));
    898     }
    899 #if SK_SUPPORT_GPU
    900 #if GR_CACHE_STATS
    901     gRealGLHelper.grContext()->printCacheStats();
    902 #endif
    903 
    904     // need to clean up here rather than post-main to allow leak detection to work
    905     gRealGLHelper.cleanup();
    906     gDebugGLHelper.cleanup();
    907     gNullGLHelper.cleanup();
    908 #if SK_ANGLE
    909     gANGLEGLHelper.cleanup();
    910 #endif // SK_ANGLE
    911 #endif
    912 
    913     return 0;
    914 }
    915 
    916 #if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL)
    917 int main(int argc, char * const argv[]) {
    918     return tool_main(argc, (char**) argv);
    919 }
    920 #endif
    921