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 (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<Timer> longRunningTimer(this->setupTimer()); 151 TimerData longRunningTimerData(numOuterLoops); 152 153 for (int outer = 0; outer < numOuterLoops; ++outer) { 154 SkAutoTDelete<Timer> 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<Timer> longRunningTimer(this->setupTimer()); 205 TimerData longRunningTimerData(numOuterLoops); 206 207 for (int outer = 0; outer < numOuterLoops; ++outer) { 208 SkAutoTDelete<Timer> 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