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