Home | History | Annotate | Download | only in tools
      1 /*
      2  * Copyright 2012 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "BenchTimer.h"
      9 #include "PictureBenchmark.h"
     10 #include "SkCanvas.h"
     11 #include "SkPicture.h"
     12 #include "SkString.h"
     13 #include "picture_utils.h"
     14 
     15 namespace sk_tools {
     16 
     17 PictureBenchmark::PictureBenchmark()
     18 : fRepeats(1)
     19 , fRenderer(NULL)
     20 , fTimerResult(TimerData::kAvg_Result)
     21 , fTimerTypes(0)
     22 , fTimeIndividualTiles(false)
     23 , fPurgeDecodedTex(false)
     24 , fPreprocess(false)
     25 , fWriter(NULL)
     26 {}
     27 
     28 PictureBenchmark::~PictureBenchmark() {
     29     SkSafeUnref(fRenderer);
     30 }
     31 
     32 void PictureBenchmark::setTimersToShow(bool wall,
     33                                        bool truncatedWall,
     34                                        bool cpu,
     35                                        bool truncatedCpu,
     36                                        bool gpu) {
     37     fTimerTypes = 0;
     38     fTimerTypes |= wall ? TimerData::kWall_Flag : 0;
     39     fTimerTypes |= truncatedWall ? TimerData::kTruncatedWall_Flag : 0;
     40     fTimerTypes |= cpu ? TimerData::kCpu_Flag : 0;
     41     fTimerTypes |= truncatedCpu ? TimerData::kTruncatedCpu_Flag : 0;
     42     fTimerTypes |= gpu ? TimerData::kGpu_Flag : 0;
     43 }
     44 
     45 BenchTimer* PictureBenchmark::setupTimer(bool useGLTimer) {
     46 #if SK_SUPPORT_GPU
     47     if (useGLTimer && fRenderer != NULL && fRenderer->isUsingGpuDevice()) {
     48         return SkNEW_ARGS(BenchTimer, (fRenderer->getGLContext()));
     49     }
     50 #endif
     51     return SkNEW_ARGS(BenchTimer, (NULL));
     52 }
     53 
     54 PictureRenderer* PictureBenchmark::setRenderer(sk_tools::PictureRenderer* renderer) {
     55     SkRefCnt_SafeAssign(fRenderer, renderer);
     56     return renderer;
     57 }
     58 
     59 void PictureBenchmark::run(SkPicture* pict) {
     60     SkASSERT(pict);
     61     if (NULL == pict) {
     62         return;
     63     }
     64 
     65     SkASSERT(fRenderer != NULL);
     66     if (NULL == fRenderer) {
     67         return;
     68     }
     69 
     70     fRenderer->init(pict, NULL, NULL, NULL, false);
     71 
     72     // We throw this away to remove first time effects (such as paging in this program)
     73     fRenderer->setup();
     74 
     75     if (fPreprocess) {
     76         if (NULL != fRenderer->getCanvas()) {
     77             fRenderer->getCanvas()->EXPERIMENTAL_optimize(pict);
     78         }
     79     }
     80 
     81     fRenderer->render(NULL);
     82     fRenderer->resetState(true);   // flush, swapBuffers and Finish
     83 
     84     if (fPreprocess) {
     85         if (NULL != fRenderer->getCanvas()) {
     86             fRenderer->getCanvas()->EXPERIMENTAL_purge(pict);
     87         }
     88     }
     89 
     90     if (fPurgeDecodedTex) {
     91         fRenderer->purgeTextures();
     92     }
     93 
     94     bool usingGpu = false;
     95 #if SK_SUPPORT_GPU
     96     usingGpu = fRenderer->isUsingGpuDevice();
     97 #endif
     98 
     99     uint32_t timerTypes = fTimerTypes;
    100     if (!usingGpu) {
    101         timerTypes &= ~TimerData::kGpu_Flag;
    102     }
    103 
    104     SkString timeFormat;
    105     if (TimerData::kPerIter_Result == fTimerResult) {
    106         timeFormat = fRenderer->getPerIterTimeFormat();
    107     } else {
    108         timeFormat = fRenderer->getNormalTimeFormat();
    109     }
    110 
    111     static const int kNumInnerLoops = 10;
    112     int numOuterLoops = 1;
    113     int numInnerLoops = fRepeats;
    114 
    115     if (TimerData::kPerIter_Result == fTimerResult && fRepeats > 1) {
    116         // interpret this flag combination to mean: generate 'fRepeats'
    117         // numbers by averaging each rendering 'kNumInnerLoops' times
    118         numOuterLoops = fRepeats;
    119         numInnerLoops = kNumInnerLoops;
    120     }
    121 
    122     if (fTimeIndividualTiles) {
    123         TiledPictureRenderer* tiledRenderer = fRenderer->getTiledRenderer();
    124         SkASSERT(tiledRenderer && tiledRenderer->supportsTimingIndividualTiles());
    125         if (NULL == tiledRenderer || !tiledRenderer->supportsTimingIndividualTiles()) {
    126             return;
    127         }
    128         int xTiles, yTiles;
    129         if (!tiledRenderer->tileDimensions(xTiles, yTiles)) {
    130             return;
    131         }
    132 
    133         int x, y;
    134         while (tiledRenderer->nextTile(x, y)) {
    135             // There are two timers, which will behave slightly differently:
    136             // 1) longRunningTimer, along with perTileTimerData, will time how long it takes to draw
    137             // one tile fRepeats times, and take the average. As such, it will not respect the
    138             // logPerIter or printMin options, since it does not know the time per iteration. It
    139             // will also be unable to call flush() for each tile.
    140             // The goal of this timer is to make up for a system timer that is not precise enough to
    141             // measure the small amount of time it takes to draw one tile once.
    142             //
    143             // 2) perTileTimer, along with perTileTimerData, will record each run separately, and
    144             // then take the average. As such, it supports logPerIter and printMin options.
    145             //
    146             // Although "legal", having two gpu timers running at the same time
    147             // seems to cause problems (i.e., INVALID_OPERATIONs) on several
    148             // platforms. To work around this, we disable the gpu timer on the
    149             // long running timer.
    150             SkAutoTDelete<BenchTimer> longRunningTimer(this->setupTimer());
    151             TimerData longRunningTimerData(numOuterLoops);
    152 
    153             for (int outer = 0; outer < numOuterLoops; ++outer) {
    154                 SkAutoTDelete<BenchTimer> perTileTimer(this->setupTimer(false));
    155                 TimerData perTileTimerData(numInnerLoops);
    156 
    157                 longRunningTimer->start();
    158                 for (int inner = 0; inner < numInnerLoops; ++inner) {
    159                     perTileTimer->start();
    160                     tiledRenderer->drawCurrentTile();
    161                     perTileTimer->truncatedEnd();
    162                     tiledRenderer->resetState(false);  // flush & swapBuffers, but don't Finish
    163                     perTileTimer->end();
    164                     SkAssertResult(perTileTimerData.appendTimes(perTileTimer.get()));
    165 
    166                     if (fPurgeDecodedTex) {
    167                         fRenderer->purgeTextures();
    168                     }
    169                 }
    170                 longRunningTimer->truncatedEnd();
    171                 tiledRenderer->resetState(true);       // flush, swapBuffers and Finish
    172                 longRunningTimer->end();
    173                 SkAssertResult(longRunningTimerData.appendTimes(longRunningTimer.get()));
    174             }
    175 
    176             fWriter->tileConfig(tiledRenderer->getConfigName());
    177             fWriter->tileMeta(x, y, xTiles, yTiles);
    178 
    179             // TODO(borenet): Turn off per-iteration tile time reporting for now.
    180             // Avoiding logging the time for every iteration for each tile cuts
    181             // down on data file size by a significant amount. Re-enable this once
    182             // we're loading the bench data directly into a data store and are no
    183             // longer generating SVG graphs.
    184 #if 0
    185             fWriter->tileData(
    186                     &perTileTimerData,
    187                     timeFormat.c_str(),
    188                     fTimerResult,
    189                     timerTypes);
    190 #endif
    191 
    192             if (fPurgeDecodedTex) {
    193                 fWriter->addTileFlag(PictureResultsWriter::kPurging);
    194             }
    195             fWriter->addTileFlag(PictureResultsWriter::kAvg);
    196             fWriter->tileData(
    197                 &longRunningTimerData,
    198                 tiledRenderer->getNormalTimeFormat().c_str(),
    199                 TimerData::kAvg_Result,
    200                 timerTypes,
    201                 numInnerLoops);
    202         }
    203     } else {
    204         SkAutoTDelete<BenchTimer> longRunningTimer(this->setupTimer());
    205         TimerData longRunningTimerData(numOuterLoops);
    206 
    207         for (int outer = 0; outer < numOuterLoops; ++outer) {
    208             SkAutoTDelete<BenchTimer> perRunTimer(this->setupTimer(false));
    209             TimerData perRunTimerData(numInnerLoops);
    210 
    211             longRunningTimer->start();
    212             for (int inner = 0; inner < numInnerLoops; ++inner) {
    213                 fRenderer->setup();
    214 
    215                 perRunTimer->start();
    216                 fRenderer->render(NULL);
    217                 perRunTimer->truncatedEnd();
    218                 fRenderer->resetState(false);   // flush & swapBuffers, but don't Finish
    219                 perRunTimer->end();
    220 
    221                 SkAssertResult(perRunTimerData.appendTimes(perRunTimer.get()));
    222 
    223                 if (fPreprocess) {
    224                     if (NULL != fRenderer->getCanvas()) {
    225                         fRenderer->getCanvas()->EXPERIMENTAL_purge(pict);
    226                     }
    227                 }
    228 
    229                 if (fPurgeDecodedTex) {
    230                     fRenderer->purgeTextures();
    231                 }
    232             }
    233             longRunningTimer->truncatedEnd();
    234             fRenderer->resetState(true);        // flush, swapBuffers and Finish
    235             longRunningTimer->end();
    236             SkAssertResult(longRunningTimerData.appendTimes(longRunningTimer.get()));
    237         }
    238 
    239         fWriter->tileConfig(fRenderer->getConfigName());
    240         if (fPurgeDecodedTex) {
    241             fWriter->addTileFlag(PictureResultsWriter::kPurging);
    242         }
    243 
    244         // Beware - since the per-run-timer doesn't ever include a glFinish it can
    245         // report a lower time then the long-running-timer
    246 #if 0
    247         fWriter->tileData(
    248                 &perRunTimerData,
    249                 timeFormat.c_str(),
    250                 fTimerResult,
    251                 timerTypes);
    252 #else
    253         fWriter->tileData(
    254                 &longRunningTimerData,
    255                 timeFormat.c_str(),
    256                 fTimerResult,
    257                 timerTypes,
    258                 numInnerLoops);
    259 #endif
    260     }
    261 
    262     fRenderer->end();
    263 }
    264 
    265 }
    266