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 "Timer.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 Timer* PictureBenchmark::setupTimer(bool useGLTimer) {
     46 #if SK_SUPPORT_GPU
     47     if (useGLTimer && fRenderer != NULL && fRenderer->isUsingGpuDevice()) {
     48         return SkNEW_ARGS(Timer, (fRenderer->getGLContext()));
     49     }
     50 #endif
     51     return SkNEW_ARGS(Timer, (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 (fRenderer->getCanvas()) {
     77             fRenderer->getCanvas()->EXPERIMENTAL_optimize(fRenderer->getPicture());
     78         }
     79     }
     80 
     81     fRenderer->render(NULL);
     82     fRenderer->resetState(true);   // flush, swapBuffers and Finish
     83 
     84     if (fPurgeDecodedTex) {
     85         fRenderer->purgeTextures();
     86     }
     87 
     88     bool usingGpu = false;
     89 #if SK_SUPPORT_GPU
     90     usingGpu = fRenderer->isUsingGpuDevice();
     91 #endif
     92 
     93     uint32_t timerTypes = fTimerTypes;
     94     if (!usingGpu) {
     95         timerTypes &= ~TimerData::kGpu_Flag;
     96     }
     97 
     98     SkString timeFormat;
     99     if (TimerData::kPerIter_Result == fTimerResult) {
    100         timeFormat = fRenderer->getPerIterTimeFormat();
    101     } else {
    102         timeFormat = fRenderer->getNormalTimeFormat();
    103     }
    104 
    105     static const int kNumInnerLoops = 10;
    106     int numOuterLoops = 1;
    107     int numInnerLoops = fRepeats;
    108 
    109     if (TimerData::kPerIter_Result == fTimerResult && fRepeats > 1) {
    110         // interpret this flag combination to mean: generate 'fRepeats'
    111         // numbers by averaging each rendering 'kNumInnerLoops' times
    112         numOuterLoops = fRepeats;
    113         numInnerLoops = kNumInnerLoops;
    114     }
    115 
    116     if (fTimeIndividualTiles) {
    117         TiledPictureRenderer* tiledRenderer = fRenderer->getTiledRenderer();
    118         SkASSERT(tiledRenderer && tiledRenderer->supportsTimingIndividualTiles());
    119         if (NULL == tiledRenderer || !tiledRenderer->supportsTimingIndividualTiles()) {
    120             return;
    121         }
    122         int xTiles, yTiles;
    123         if (!tiledRenderer->tileDimensions(xTiles, yTiles)) {
    124             return;
    125         }
    126 
    127         int x, y;
    128         while (tiledRenderer->nextTile(x, y)) {
    129             // There are two timers, which will behave slightly differently:
    130             // 1) longRunningTimer, along with perTileTimerData, will time how long it takes to draw
    131             // one tile fRepeats times, and take the average. As such, it will not respect the
    132             // logPerIter or printMin options, since it does not know the time per iteration. It
    133             // will also be unable to call flush() for each tile.
    134             // The goal of this timer is to make up for a system timer that is not precise enough to
    135             // measure the small amount of time it takes to draw one tile once.
    136             //
    137             // 2) perTileTimer, along with perTileTimerData, will record each run separately, and
    138             // then take the average. As such, it supports logPerIter and printMin options.
    139             //
    140             // Although "legal", having two gpu timers running at the same time
    141             // seems to cause problems (i.e., INVALID_OPERATIONs) on several
    142             // platforms. To work around this, we disable the gpu timer on the
    143             // long running timer.
    144             SkAutoTDelete<Timer> longRunningTimer(this->setupTimer());
    145             TimerData longRunningTimerData(numOuterLoops);
    146 
    147             for (int outer = 0; outer < numOuterLoops; ++outer) {
    148                 SkAutoTDelete<Timer> perTileTimer(this->setupTimer(false));
    149                 TimerData perTileTimerData(numInnerLoops);
    150 
    151                 longRunningTimer->start();
    152                 for (int inner = 0; inner < numInnerLoops; ++inner) {
    153                     perTileTimer->start();
    154                     tiledRenderer->drawCurrentTile();
    155                     perTileTimer->truncatedEnd();
    156                     tiledRenderer->resetState(false);  // flush & swapBuffers, but don't Finish
    157                     perTileTimer->end();
    158                     SkAssertResult(perTileTimerData.appendTimes(perTileTimer.get()));
    159 
    160                     if (fPurgeDecodedTex) {
    161                         fRenderer->purgeTextures();
    162                     }
    163                 }
    164                 longRunningTimer->truncatedEnd();
    165                 tiledRenderer->resetState(true);       // flush, swapBuffers and Finish
    166                 longRunningTimer->end();
    167                 SkAssertResult(longRunningTimerData.appendTimes(longRunningTimer.get()));
    168             }
    169 
    170             fWriter->logRenderer(tiledRenderer);
    171             fWriter->tileMeta(x, y, xTiles, yTiles);
    172 
    173             // TODO(borenet): Turn off per-iteration tile time reporting for now.
    174             // Avoiding logging the time for every iteration for each tile cuts
    175             // down on data file size by a significant amount. Re-enable this once
    176             // we're loading the bench data directly into a data store and are no
    177             // longer generating SVG graphs.
    178 #if 0
    179             fWriter->tileData(
    180                     &perTileTimerData,
    181                     timeFormat.c_str(),
    182                     fTimerResult,
    183                     timerTypes);
    184 #endif
    185 
    186             if (fPurgeDecodedTex) {
    187                 fWriter->addTileFlag(PictureResultsWriter::kPurging);
    188             }
    189             fWriter->addTileFlag(PictureResultsWriter::kAvg);
    190             fWriter->tileData(
    191                 &longRunningTimerData,
    192                 tiledRenderer->getNormalTimeFormat().c_str(),
    193                 TimerData::kAvg_Result,
    194                 timerTypes,
    195                 numInnerLoops);
    196         }
    197     } else {
    198         SkAutoTDelete<Timer> longRunningTimer(this->setupTimer());
    199         TimerData longRunningTimerData(numOuterLoops);
    200 
    201         for (int outer = 0; outer < numOuterLoops; ++outer) {
    202             SkAutoTDelete<Timer> perRunTimer(this->setupTimer(false));
    203             TimerData perRunTimerData(numInnerLoops);
    204 
    205             longRunningTimer->start();
    206             for (int inner = 0; inner < numInnerLoops; ++inner) {
    207                 fRenderer->setup();
    208 
    209                 perRunTimer->start();
    210                 fRenderer->render(NULL);
    211                 perRunTimer->truncatedEnd();
    212                 fRenderer->resetState(false);   // flush & swapBuffers, but don't Finish
    213                 perRunTimer->end();
    214 
    215                 SkAssertResult(perRunTimerData.appendTimes(perRunTimer.get()));
    216 
    217                 if (fPurgeDecodedTex) {
    218                     fRenderer->purgeTextures();
    219                 }
    220             }
    221             longRunningTimer->truncatedEnd();
    222             fRenderer->resetState(true);        // flush, swapBuffers and Finish
    223             longRunningTimer->end();
    224             SkAssertResult(longRunningTimerData.appendTimes(longRunningTimer.get()));
    225         }
    226 
    227         fWriter->logRenderer(fRenderer);
    228         if (fPurgeDecodedTex) {
    229             fWriter->addTileFlag(PictureResultsWriter::kPurging);
    230         }
    231 
    232         // Beware - since the per-run-timer doesn't ever include a glFinish it can
    233         // report a lower time then the long-running-timer
    234 #if 0
    235         fWriter->tileData(
    236                 &perRunTimerData,
    237                 timeFormat.c_str(),
    238                 fTimerResult,
    239                 timerTypes);
    240 #else
    241         fWriter->tileData(
    242                 &longRunningTimerData,
    243                 timeFormat.c_str(),
    244                 fTimerResult,
    245                 timerTypes,
    246                 numInnerLoops);
    247 #endif
    248     }
    249 
    250     fRenderer->end();
    251 }
    252 
    253 }
    254