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 #include "GrContext.h"
     13 #include "GrRenderTarget.h"
     14 
     15 #include "SkBenchmark.h"
     16 #include "SkCanvas.h"
     17 #include "SkColorPriv.h"
     18 #include "SkGpuDevice.h"
     19 #include "SkGraphics.h"
     20 #include "SkImageEncoder.h"
     21 #include "gl/SkNativeGLContext.h"
     22 #include "gl/SkNullGLContext.h"
     23 #include "SkNWayCanvas.h"
     24 #include "SkPicture.h"
     25 #include "SkString.h"
     26 
     27 #ifdef SK_BUILD_FOR_ANDROID
     28 static void log_error(const char msg[]) { SkDebugf("%s", msg); }
     29 static void log_progress(const char msg[]) { SkDebugf("%s", msg); }
     30 #else
     31 static void log_error(const char msg[]) { fprintf(stderr, "%s", msg); }
     32 static void log_progress(const char msg[]) { printf("%s", msg); }
     33 #endif
     34 
     35 static void log_error(const SkString& str) { log_error(str.c_str()); }
     36 static void log_progress(const SkString& str) { log_progress(str.c_str()); }
     37 
     38 ///////////////////////////////////////////////////////////////////////////////
     39 
     40 static void erase(SkBitmap& bm) {
     41     if (bm.config() == SkBitmap::kA8_Config) {
     42         bm.eraseColor(0);
     43     } else {
     44         bm.eraseColor(SK_ColorWHITE);
     45     }
     46 }
     47 
     48 #if 0
     49 static bool equal(const SkBitmap& bm1, const SkBitmap& bm2) {
     50     if (bm1.width() != bm2.width() ||
     51         bm1.height() != bm2.height() ||
     52         bm1.config() != bm2.config()) {
     53         return false;
     54     }
     55 
     56     size_t pixelBytes = bm1.width() * bm1.bytesPerPixel();
     57     for (int y = 0; y < bm1.height(); y++) {
     58         if (memcmp(bm1.getAddr(0, y), bm2.getAddr(0, y), pixelBytes)) {
     59             return false;
     60         }
     61     }
     62     return true;
     63 }
     64 #endif
     65 
     66 class Iter {
     67 public:
     68     Iter(void* param) {
     69         fBench = BenchRegistry::Head();
     70         fParam = param;
     71     }
     72 
     73     SkBenchmark* next() {
     74         if (fBench) {
     75             BenchRegistry::Factory f = fBench->factory();
     76             fBench = fBench->next();
     77             return f(fParam);
     78         }
     79         return NULL;
     80     }
     81 
     82 private:
     83     const BenchRegistry* fBench;
     84     void* fParam;
     85 };
     86 
     87 static void make_filename(const char name[], SkString* path) {
     88     path->set(name);
     89     for (int i = 0; name[i]; i++) {
     90         switch (name[i]) {
     91             case '/':
     92             case '\\':
     93             case ' ':
     94             case ':':
     95                 path->writable_str()[i] = '-';
     96                 break;
     97             default:
     98                 break;
     99         }
    100     }
    101 }
    102 
    103 static void saveFile(const char name[], const char config[], const char dir[],
    104                      const SkBitmap& bm) {
    105     SkBitmap copy;
    106     if (!bm.copyTo(&copy, SkBitmap::kARGB_8888_Config)) {
    107         return;
    108     }
    109 
    110     if (bm.config() == SkBitmap::kA8_Config) {
    111         // turn alpha into gray-scale
    112         size_t size = copy.getSize() >> 2;
    113         SkPMColor* p = copy.getAddr32(0, 0);
    114         for (size_t i = 0; i < size; i++) {
    115             int c = (*p >> SK_A32_SHIFT) & 0xFF;
    116             c = 255 - c;
    117             c |= (c << 24) | (c << 16) | (c << 8);
    118             *p++ = c | (SK_A32_MASK << SK_A32_SHIFT);
    119         }
    120     }
    121 
    122     SkString str;
    123     make_filename(name, &str);
    124     str.appendf("_%s.png", config);
    125     str.prepend(dir);
    126     ::remove(str.c_str());
    127     SkImageEncoder::EncodeFile(str.c_str(), copy, SkImageEncoder::kPNG_Type,
    128                                100);
    129 }
    130 
    131 static void performClip(SkCanvas* canvas, int w, int h) {
    132     SkRect r;
    133 
    134     r.set(SkIntToScalar(10), SkIntToScalar(10),
    135           SkIntToScalar(w*2/3), SkIntToScalar(h*2/3));
    136     canvas->clipRect(r, SkRegion::kIntersect_Op);
    137 
    138     r.set(SkIntToScalar(w/3), SkIntToScalar(h/3),
    139           SkIntToScalar(w-10), SkIntToScalar(h-10));
    140     canvas->clipRect(r, SkRegion::kXOR_Op);
    141 }
    142 
    143 static void performRotate(SkCanvas* canvas, int w, int h) {
    144     const SkScalar x = SkIntToScalar(w) / 2;
    145     const SkScalar y = SkIntToScalar(h) / 2;
    146 
    147     canvas->translate(x, y);
    148     canvas->rotate(SkIntToScalar(35));
    149     canvas->translate(-x, -y);
    150 }
    151 
    152 static void performScale(SkCanvas* canvas, int w, int h) {
    153     const SkScalar x = SkIntToScalar(w) / 2;
    154     const SkScalar y = SkIntToScalar(h) / 2;
    155 
    156     canvas->translate(x, y);
    157     // just enough so we can't take the sprite case
    158     canvas->scale(SK_Scalar1 * 99/100, SK_Scalar1 * 99/100);
    159     canvas->translate(-x, -y);
    160 }
    161 
    162 static bool parse_bool_arg(char * const* argv, char* const* stop, bool* var) {
    163     if (argv < stop) {
    164         *var = atoi(*argv) != 0;
    165         return true;
    166     }
    167     return false;
    168 }
    169 
    170 enum Backend {
    171     kRaster_Backend,
    172     kGPU_Backend,
    173     kPDF_Backend,
    174 };
    175 
    176 class GLHelper {
    177 public:
    178     GLHelper() {
    179     }
    180 
    181     bool init(SkGLContext* glCtx, int width, int height) {
    182         GrContext* grCtx;
    183         GrRenderTarget* rt;
    184         if (glCtx->init(width, height)) {
    185             GrPlatform3DContext ctx =
    186                 reinterpret_cast<GrPlatform3DContext>(glCtx->gl());
    187             grCtx = GrContext::Create(kOpenGL_Shaders_GrEngine, ctx);
    188             if (NULL != grCtx) {
    189                 GrPlatformRenderTargetDesc desc;
    190                 desc.fConfig = kSkia8888_PM_GrPixelConfig;
    191                 desc.fWidth = width;
    192                 desc.fHeight = height;
    193                 desc.fStencilBits = 8;
    194                 desc.fRenderTargetHandle = glCtx->getFBOID();
    195                 rt = grCtx->createPlatformRenderTarget(desc);
    196                 if (NULL == rt) {
    197                     grCtx->unref();
    198                     return false;
    199                 }
    200             }
    201         } else {
    202             return false;
    203         }
    204         glCtx->ref();
    205         fGLContext.reset(glCtx);
    206         fGrContext.reset(grCtx);
    207         fRenderTarget.reset(rt);
    208         return true;
    209     }
    210 
    211     bool isValid() {
    212         return NULL != fGLContext.get();
    213     }
    214 
    215     SkGLContext* glContext() {
    216         return fGLContext.get();
    217     }
    218 
    219     GrRenderTarget* renderTarget() {
    220         return fRenderTarget.get();
    221     }
    222 
    223     GrContext* grContext() {
    224         return fGrContext.get();
    225     }
    226 private:
    227     SkAutoTUnref<SkGLContext> fGLContext;
    228     SkAutoTUnref<GrContext> fGrContext;
    229     SkAutoTUnref<GrRenderTarget> fRenderTarget;
    230 };
    231 
    232 static GLHelper gRealGLHelper;
    233 static GLHelper gNullGLHelper;
    234 
    235 static SkDevice* make_device(SkBitmap::Config config, const SkIPoint& size,
    236                              Backend backend, GLHelper* glHelper) {
    237     SkDevice* device = NULL;
    238     SkBitmap bitmap;
    239     bitmap.setConfig(config, size.fX, size.fY);
    240 
    241     switch (backend) {
    242         case kRaster_Backend:
    243             bitmap.allocPixels();
    244             erase(bitmap);
    245             device = new SkDevice(bitmap);
    246             break;
    247         case kGPU_Backend:
    248             device = new SkGpuDevice(glHelper->grContext(),
    249                                      glHelper->renderTarget());
    250             break;
    251         case kPDF_Backend:
    252         default:
    253             SkASSERT(!"unsupported");
    254     }
    255     return device;
    256 }
    257 
    258 static const struct {
    259     SkBitmap::Config    fConfig;
    260     const char*         fName;
    261     Backend             fBackend;
    262     GLHelper*           fGLHelper;
    263 } gConfigs[] = {
    264     { SkBitmap::kARGB_8888_Config,  "8888",     kRaster_Backend, NULL },
    265     { SkBitmap::kRGB_565_Config,    "565",      kRaster_Backend, NULL },
    266     { SkBitmap::kARGB_8888_Config,  "GPU",      kGPU_Backend, &gRealGLHelper },
    267     { SkBitmap::kARGB_8888_Config,  "NULLGPU",  kGPU_Backend, &gNullGLHelper },
    268 };
    269 
    270 static int findConfig(const char config[]) {
    271     for (size_t i = 0; i < SK_ARRAY_COUNT(gConfigs); i++) {
    272         if (!strcmp(config, gConfigs[i].fName)) {
    273             return i;
    274         }
    275     }
    276     return -1;
    277 }
    278 
    279 static void determine_gpu_context_size(SkTDict<const char*>& defineDict,
    280                                        int* contextWidth,
    281                                        int* contextHeight) {
    282     Iter iter(&defineDict);
    283     SkBenchmark* bench;
    284     while ((bench = iter.next()) != NULL) {
    285         SkIPoint dim = bench->getSize();
    286         if (*contextWidth < dim.fX) {
    287             *contextWidth = dim.fX;
    288         }
    289         if (*contextHeight < dim.fY) {
    290             *contextHeight = dim.fY;
    291         }
    292     }
    293 }
    294 
    295 static bool skip_name(const SkTDArray<const char*> array, const char name[]) {
    296     if (0 == array.count()) {
    297         // no names, so don't skip anything
    298         return false;
    299     }
    300     for (int i = 0; i < array.count(); ++i) {
    301         if (strstr(name, array[i])) {
    302             // found the name, so don't skip
    303             return false;
    304         }
    305     }
    306     return true;
    307 }
    308 
    309 int main (int argc, char * const argv[]) {
    310     SkAutoGraphics ag;
    311 
    312     SkTDict<const char*> defineDict(1024);
    313     int repeatDraw = 1;
    314     int forceAlpha = 0xFF;
    315     bool forceAA = true;
    316     bool forceFilter = false;
    317     SkTriState::State forceDither = SkTriState::kDefault;
    318     bool timerWall = false;
    319     bool timerCpu = true;
    320     bool timerGpu = true;
    321     bool doScale = false;
    322     bool doRotate = false;
    323     bool doClip = false;
    324     bool hasStrokeWidth = false;
    325     float strokeWidth;
    326     SkTDArray<const char*> fMatches;
    327 
    328     SkString outDir;
    329     SkBitmap::Config outConfig = SkBitmap::kNo_Config;
    330     GLHelper* glHelper = NULL;
    331     const char* configName = "";
    332     Backend backend = kRaster_Backend;  // for warning
    333     int configCount = SK_ARRAY_COUNT(gConfigs);
    334 
    335     char* const* stop = argv + argc;
    336     for (++argv; argv < stop; ++argv) {
    337         if (strcmp(*argv, "-o") == 0) {
    338             argv++;
    339             if (argv < stop && **argv) {
    340                 outDir.set(*argv);
    341                 if (outDir.c_str()[outDir.size() - 1] != '/') {
    342                     outDir.append("/");
    343                 }
    344             }
    345         } else if (strcmp(*argv, "-repeat") == 0) {
    346             argv++;
    347             if (argv < stop) {
    348                 repeatDraw = atoi(*argv);
    349                 if (repeatDraw < 1) {
    350                     repeatDraw = 1;
    351                 }
    352             } else {
    353                 log_error("missing arg for -repeat\n");
    354                 return -1;
    355             }
    356         } else if (strcmp(*argv, "-timers") == 0) {
    357             argv++;
    358             if (argv < stop) {
    359                 timerWall = false;
    360                 timerCpu = false;
    361                 timerGpu = false;
    362                 for (char* t = *argv; *t; ++t) {
    363                     switch (*t) {
    364                     case 'w': timerWall = true; break;
    365                     case 'c': timerCpu = true; break;
    366                     case 'g': timerGpu = true; break;
    367                     }
    368                 }
    369             } else {
    370                 log_error("missing arg for -timers\n");
    371                 return -1;
    372             }
    373         } else if (!strcmp(*argv, "-rotate")) {
    374             doRotate = true;
    375         } else if (!strcmp(*argv, "-scale")) {
    376             doScale = true;
    377         } else if (!strcmp(*argv, "-clip")) {
    378             doClip = true;
    379         } else if (strcmp(*argv, "-forceAA") == 0) {
    380             if (!parse_bool_arg(++argv, stop, &forceAA)) {
    381                 log_error("missing arg for -forceAA\n");
    382                 return -1;
    383             }
    384         } else if (strcmp(*argv, "-forceFilter") == 0) {
    385             if (!parse_bool_arg(++argv, stop, &forceFilter)) {
    386                 log_error("missing arg for -forceFilter\n");
    387                 return -1;
    388             }
    389         } else if (strcmp(*argv, "-forceDither") == 0) {
    390             bool tmp;
    391             if (!parse_bool_arg(++argv, stop, &tmp)) {
    392                 log_error("missing arg for -forceDither\n");
    393                 return -1;
    394             }
    395             forceDither = tmp ? SkTriState::kTrue : SkTriState::kFalse;
    396         } else if (strcmp(*argv, "-forceBlend") == 0) {
    397             bool wantAlpha = false;
    398             if (!parse_bool_arg(++argv, stop, &wantAlpha)) {
    399                 log_error("missing arg for -forceBlend\n");
    400                 return -1;
    401             }
    402             forceAlpha = wantAlpha ? 0x80 : 0xFF;
    403         } else if (strcmp(*argv, "-strokeWidth") == 0) {
    404             argv++;
    405             if (argv < stop) {
    406                 const char *strokeWidthStr = *argv;
    407                 if (sscanf(strokeWidthStr, "%f", &strokeWidth) != 1) {
    408                   log_error("bad arg for -strokeWidth\n");
    409                   return -1;
    410                 }
    411                 hasStrokeWidth = true;
    412             } else {
    413                 log_error("missing arg for -strokeWidth\n");
    414                 return -1;
    415             }
    416         } else if (strcmp(*argv, "-match") == 0) {
    417             argv++;
    418             if (argv < stop) {
    419                 *fMatches.append() = *argv;
    420             } else {
    421                 log_error("missing arg for -match\n");
    422                 return -1;
    423             }
    424         } else if (strcmp(*argv, "-config") == 0) {
    425             argv++;
    426             if (argv < stop) {
    427                 int index = findConfig(*argv);
    428                 if (index >= 0) {
    429                     outConfig = gConfigs[index].fConfig;
    430                     configName = gConfigs[index].fName;
    431                     backend = gConfigs[index].fBackend;
    432                     glHelper = gConfigs[index].fGLHelper;
    433                     configCount = 1;
    434                 } else {
    435                     SkString str;
    436                     str.printf("unrecognized config %s\n", *argv);
    437                     log_error(str);
    438                     return -1;
    439                 }
    440             } else {
    441                 log_error("missing arg for -config\n");
    442                 return -1;
    443             }
    444         } else if (strlen(*argv) > 2 && strncmp(*argv, "-D", 2) == 0) {
    445             argv++;
    446             if (argv < stop) {
    447                 defineDict.set(argv[-1] + 2, *argv);
    448             } else {
    449                 log_error("incomplete '-Dfoo bar' definition\n");
    450                 return -1;
    451             }
    452         } else {
    453             SkString str;
    454             str.printf("unrecognized arg %s\n", *argv);
    455             log_error(str);
    456             return -1;
    457         }
    458     }
    459 
    460     // report our current settings
    461     {
    462         SkString str;
    463         str.printf("skia bench: alpha=0x%02X antialias=%d filter=%d",
    464                    forceAlpha, forceAA, forceFilter);
    465         str.appendf(" rotate=%d scale=%d clip=%d",
    466                    doRotate, doScale, doClip);
    467 
    468         const char * ditherName;
    469         switch (forceDither) {
    470             case SkTriState::kDefault: ditherName = "default"; break;
    471             case SkTriState::kTrue: ditherName = "true"; break;
    472             case SkTriState::kFalse: ditherName = "false"; break;
    473             default: ditherName = "<invalid>"; break;
    474         }
    475         str.appendf(" dither=%s", ditherName);
    476 
    477         if (hasStrokeWidth) {
    478             str.appendf(" strokeWidth=%f", strokeWidth);
    479         } else {
    480             str.append(" strokeWidth=none");
    481         }
    482 
    483 #if defined(SK_SCALAR_IS_FLOAT)
    484         str.append(" scalar=float");
    485 #elif defined(SK_SCALAR_IS_FIXED)
    486         str.append(" scalar=fixed");
    487 #endif
    488 
    489 #if defined(SK_BUILD_FOR_WIN32)
    490         str.append(" system=WIN32");
    491 #elif defined(SK_BUILD_FOR_MAC)
    492         str.append(" system=MAC");
    493 #elif defined(SK_BUILD_FOR_ANDROID)
    494         str.append(" system=ANDROID");
    495 #elif defined(SK_BUILD_FOR_UNIX)
    496         str.append(" system=UNIX");
    497 #else
    498         str.append(" system=other");
    499 #endif
    500 
    501 #if defined(SK_DEBUG)
    502         str.append(" DEBUG");
    503 #endif
    504         str.append("\n");
    505         log_progress(str);
    506     }
    507 
    508     //Don't do GL when fixed.
    509 #if !defined(SK_SCALAR_IS_FIXED)
    510     int contextWidth = 1024;
    511     int contextHeight = 1024;
    512     determine_gpu_context_size(defineDict, &contextWidth, &contextHeight);
    513     SkAutoTUnref<SkGLContext> realGLCtx(new SkNativeGLContext);
    514     SkAutoTUnref<SkGLContext> nullGLCtx(new SkNullGLContext);
    515     gRealGLHelper.init(realGLCtx.get(), contextWidth, contextHeight);
    516     gNullGLHelper.init(nullGLCtx.get(), contextWidth, contextHeight);
    517 #endif
    518     BenchTimer timer = BenchTimer(gRealGLHelper.glContext());
    519 
    520     Iter iter(&defineDict);
    521     SkBenchmark* bench;
    522     while ((bench = iter.next()) != NULL) {
    523         SkIPoint dim = bench->getSize();
    524         if (dim.fX <= 0 || dim.fY <= 0) {
    525             continue;
    526         }
    527 
    528         bench->setForceAlpha(forceAlpha);
    529         bench->setForceAA(forceAA);
    530         bench->setForceFilter(forceFilter);
    531         bench->setDither(forceDither);
    532         if (hasStrokeWidth) {
    533             bench->setStrokeWidth(strokeWidth);
    534         }
    535 
    536         // only run benchmarks if their name contains matchStr
    537         if (skip_name(fMatches, bench->getName())) {
    538             continue;
    539         }
    540 
    541         {
    542             SkString str;
    543             str.printf("running bench [%d %d] %28s", dim.fX, dim.fY,
    544                        bench->getName());
    545             log_progress(str);
    546         }
    547 
    548         for (int configIndex = 0; configIndex < configCount; configIndex++) {
    549             if (configCount > 1) {
    550                 outConfig = gConfigs[configIndex].fConfig;
    551                 configName = gConfigs[configIndex].fName;
    552                 backend = gConfigs[configIndex].fBackend;
    553                 glHelper = gConfigs[configIndex].fGLHelper;
    554             }
    555 
    556             if (kGPU_Backend == backend &&
    557                 (NULL == glHelper || !glHelper->isValid())) {
    558                 continue;
    559             }
    560 
    561             SkDevice* device = make_device(outConfig, dim, backend, glHelper);
    562             SkCanvas canvas(device);
    563             device->unref();
    564 
    565             if (doClip) {
    566                 performClip(&canvas, dim.fX, dim.fY);
    567             }
    568             if (doScale) {
    569                 performScale(&canvas, dim.fX, dim.fY);
    570             }
    571             if (doRotate) {
    572                 performRotate(&canvas, dim.fX, dim.fY);
    573             }
    574 
    575             //warm up caches if needed
    576             if (repeatDraw > 1) {
    577                 SkAutoCanvasRestore acr(&canvas, true);
    578                 bench->draw(&canvas);
    579                 if (glHelper) {
    580                     glHelper->grContext()->flush();
    581                     SK_GL(*glHelper->glContext(), Finish());
    582                 }
    583             }
    584 
    585             timer.start();
    586             for (int i = 0; i < repeatDraw; i++) {
    587                 SkAutoCanvasRestore acr(&canvas, true);
    588                 bench->draw(&canvas);
    589                 if (glHelper) {
    590                     glHelper->grContext()->flush();
    591                 }
    592             }
    593            if (glHelper) {
    594                 SK_GL(*glHelper->glContext(), Finish());
    595            }
    596            timer.end();
    597 
    598             if (repeatDraw > 1) {
    599                 SkString str;
    600                 str.printf("  %4s:", configName);
    601                 if (timerWall) {
    602                     str.appendf(" msecs = %6.2f", timer.fWall / repeatDraw);
    603                 }
    604                 if (timerCpu) {
    605                     str.appendf(" cmsecs = %6.2f", timer.fCpu / repeatDraw);
    606                 }
    607                 if (timerGpu && glHelper && timer.fGpu > 0) {
    608                     str.appendf(" gmsecs = %6.2f", timer.fGpu / repeatDraw);
    609                 }
    610                 log_progress(str);
    611             }
    612             if (outDir.size() > 0) {
    613                 saveFile(bench->getName(), configName, outDir.c_str(),
    614                          device->accessBitmap(false));
    615             }
    616         }
    617         log_progress("\n");
    618     }
    619 
    620     return 0;
    621 }
    622