| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright 2015 Google Inc. | 2 * Copyright 2015 Google Inc. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
| 5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
| 6 * | 6 * |
| 7 */ | 7 */ |
| 8 | 8 |
| 9 #include "VisualLightweightBenchModule.h" | 9 #include "VisualLightweightBenchModule.h" |
| 10 | 10 |
| 11 #include "ProcStats.h" | 11 #include "ProcStats.h" |
| 12 #include "SkApplication.h" | 12 #include "SkApplication.h" |
| 13 #include "SkCanvas.h" | 13 #include "SkCanvas.h" |
| 14 #include "SkCommandLineFlags.h" | 14 #include "SkCommandLineFlags.h" |
| 15 #include "SkForceLinking.h" | 15 #include "SkForceLinking.h" |
| 16 #include "SkGraphics.h" | 16 #include "SkGraphics.h" |
| 17 #include "SkGr.h" | 17 #include "SkGr.h" |
| 18 #include "SkImageDecoder.h" | 18 #include "SkImageDecoder.h" |
| 19 #include "SkOSFile.h" | 19 #include "SkOSFile.h" |
| 20 #include "SkStream.h" | 20 #include "SkStream.h" |
| 21 #include "Stats.h" | 21 #include "Stats.h" |
| 22 #include "gl/GrGLInterface.h" | 22 #include "gl/GrGLInterface.h" |
| 23 | 23 |
| 24 __SK_FORCE_IMAGE_DECODER_LINKING; | 24 __SK_FORCE_IMAGE_DECODER_LINKING; |
| 25 | 25 |
| 26 // Between samples we reset context | 26 // Between samples we reset context |
| 27 // Between frames we swap buffers | 27 // Between frames we swap buffers |
| 28 | |
| 29 DEFINE_int32(maxWarmupFrames, 100, "maxmium frames to try and tune for sane timi
ngs"); | |
| 30 DEFINE_int32(gpuFrameLag, 5, "Overestimate of maximum number of frames GPU allow
s to lag."); | |
| 31 DEFINE_int32(samples, 10, "Number of times to time each skp."); | |
| 32 DEFINE_int32(frames, 5, "Number of frames of each skp to render per sample."); | |
| 33 DEFINE_double(loopMs, 5, "Target loop time in millseconds."); | |
| 34 DEFINE_bool2(verbose, v, false, "enable verbose output from the test driver."); | 28 DEFINE_bool2(verbose, v, false, "enable verbose output from the test driver."); |
| 35 DEFINE_string(outResultsFile, "", "If given, write results here as JSON."); | 29 DEFINE_string(outResultsFile, "", "If given, write results here as JSON."); |
| 36 DEFINE_string(key, "", | 30 DEFINE_string(key, "", |
| 37 "Space-separated key/value pairs to add to JSON identifying this b
uilder."); | 31 "Space-separated key/value pairs to add to JSON identifying this b
uilder."); |
| 38 DEFINE_string(properties, "", | 32 DEFINE_string(properties, "", |
| 39 "Space-separated key/value pairs to add to JSON identifying this r
un."); | 33 "Space-separated key/value pairs to add to JSON identifying this r
un."); |
| 34 DEFINE_int32(samples, 10, "Number of times to time each skp."); |
| 40 | 35 |
| 41 static SkString humanize(double ms) { | 36 static SkString humanize(double ms) { |
| 42 if (FLAGS_verbose) { | 37 if (FLAGS_verbose) { |
| 43 return SkStringPrintf("%llu", (uint64_t)(ms*1e6)); | 38 return SkStringPrintf("%llu", (uint64_t)(ms*1e6)); |
| 44 } | 39 } |
| 45 return HumanizeMs(ms); | 40 return HumanizeMs(ms); |
| 46 } | 41 } |
| 47 | 42 |
| 48 #define HUMANIZE(time) humanize(time).c_str() | 43 #define HUMANIZE(time) humanize(time).c_str() |
| 49 | 44 |
| 50 // We draw a big nonAA path to warmup the gpu / cpu | |
| 51 class WarmupBench : public Benchmark { | |
| 52 public: | |
| 53 WarmupBench() { | |
| 54 make_path(fPath); | |
| 55 } | |
| 56 private: | |
| 57 static void make_path(SkPath& path) { | |
| 58 #include "BigPathBench.inc" | |
| 59 } | |
| 60 const char* onGetName() override { return "warmupbench"; } | |
| 61 void onDraw(int loops, SkCanvas* canvas) override { | |
| 62 SkPaint paint; | |
| 63 paint.setStyle(SkPaint::kStroke_Style); | |
| 64 paint.setStrokeWidth(2); | |
| 65 for (int i = 0; i < loops; i++) { | |
| 66 canvas->drawPath(fPath, paint); | |
| 67 } | |
| 68 } | |
| 69 SkPath fPath; | |
| 70 }; | |
| 71 | |
| 72 VisualLightweightBenchModule::VisualLightweightBenchModule(VisualBench* owner) | 45 VisualLightweightBenchModule::VisualLightweightBenchModule(VisualBench* owner) |
| 73 : fCurrentSample(0) | 46 : fCurrentSample(0) |
| 74 , fCurrentFrame(0) | |
| 75 , fLoops(1) | |
| 76 , fState(kWarmup_State) | |
| 77 , fBenchmark(nullptr) | |
| 78 , fOwner(SkRef(owner)) | 47 , fOwner(SkRef(owner)) |
| 79 , fResults(new ResultsWriter) { | 48 , fResults(new ResultsWriter) { |
| 80 fBenchmarkStream.reset(new VisualBenchmarkStream); | 49 fBenchmarkStream.reset(new VisualBenchmarkStream); |
| 81 | 50 |
| 82 // Print header | 51 // Print header |
| 83 SkDebugf("curr/maxrss\tloops\tmin\tmedian\tmean\tmax\tstddev\t%-*s\tbench\n"
, FLAGS_samples, | 52 SkDebugf("curr/maxrss\tloops\tmin\tmedian\tmean\tmax\tstddev\t%-*s\tbench\n"
, FLAGS_samples, |
| 84 "samples"); | 53 "samples"); |
| 85 | 54 |
| 86 // setup json logging if required | 55 // setup json logging if required |
| 87 if (!FLAGS_outResultsFile.isEmpty()) { | 56 if (!FLAGS_outResultsFile.isEmpty()) { |
| (...skipping 11 matching lines...) Expand all Loading... |
| 99 if (1 == FLAGS_properties.count() % 2) { | 68 if (1 == FLAGS_properties.count() % 2) { |
| 100 SkDebugf("ERROR: --properties must be passed with an even number of argu
ments.\n"); | 69 SkDebugf("ERROR: --properties must be passed with an even number of argu
ments.\n"); |
| 101 } else { | 70 } else { |
| 102 for (int i = 1; i < FLAGS_properties.count(); i += 2) { | 71 for (int i = 1; i < FLAGS_properties.count(); i += 2) { |
| 103 fResults->property(FLAGS_properties[i - 1], FLAGS_properties[i]); | 72 fResults->property(FLAGS_properties[i - 1], FLAGS_properties[i]); |
| 104 } | 73 } |
| 105 } | 74 } |
| 106 } | 75 } |
| 107 | 76 |
| 108 inline void VisualLightweightBenchModule::renderFrame(SkCanvas* canvas) { | 77 inline void VisualLightweightBenchModule::renderFrame(SkCanvas* canvas) { |
| 109 fBenchmark->draw(fLoops, canvas); | 78 fBenchmark->draw(fTSM.loops(), canvas); |
| 110 canvas->flush(); | 79 canvas->flush(); |
| 111 fOwner->present(); | 80 fOwner->present(); |
| 112 } | 81 } |
| 113 | 82 |
| 114 void VisualLightweightBenchModule::printStats() { | 83 void VisualLightweightBenchModule::printStats() { |
| 115 const SkTArray<double>& measurements = fRecords.back().fMeasurements; | 84 const SkTArray<double>& measurements = fRecords.back().fMeasurements; |
| 116 const char* shortName = fBenchmark->getUniqueName(); | 85 const char* shortName = fBenchmark->getUniqueName(); |
| 117 | 86 |
| 118 // update log | 87 // update log |
| 119 // Note: We currently log only the minimum. It would be interesting to log
more information | 88 // Note: We currently log only the minimum. It would be interesting to log
more information |
| (...skipping 13 matching lines...) Expand all Loading... |
| 133 if (FLAGS_verbose) { | 102 if (FLAGS_verbose) { |
| 134 for (int i = 0; i < measurements.count(); i++) { | 103 for (int i = 0; i < measurements.count(); i++) { |
| 135 SkDebugf("%s ", HUMANIZE(measurements[i])); | 104 SkDebugf("%s ", HUMANIZE(measurements[i])); |
| 136 } | 105 } |
| 137 SkDebugf("%s\n", shortName); | 106 SkDebugf("%s\n", shortName); |
| 138 } else { | 107 } else { |
| 139 const double stdDevPercent = 100 * sqrt(stats.var) / stats.mean; | 108 const double stdDevPercent = 100 * sqrt(stats.var) / stats.mean; |
| 140 SkDebugf("%4d/%-4dMB\t%d\t%s\t%s\t%s\t%s\t%.0f%%\t%s\t%s\n", | 109 SkDebugf("%4d/%-4dMB\t%d\t%s\t%s\t%s\t%s\t%.0f%%\t%s\t%s\n", |
| 141 sk_tools::getCurrResidentSetSizeMB(), | 110 sk_tools::getCurrResidentSetSizeMB(), |
| 142 sk_tools::getMaxResidentSetSizeMB(), | 111 sk_tools::getMaxResidentSetSizeMB(), |
| 143 fLoops, | 112 fTSM.loops(), |
| 144 HUMANIZE(stats.min), | 113 HUMANIZE(stats.min), |
| 145 HUMANIZE(stats.median), | 114 HUMANIZE(stats.median), |
| 146 HUMANIZE(stats.mean), | 115 HUMANIZE(stats.mean), |
| 147 HUMANIZE(stats.max), | 116 HUMANIZE(stats.max), |
| 148 stdDevPercent, | 117 stdDevPercent, |
| 149 stats.plot.c_str(), | 118 stats.plot.c_str(), |
| 150 shortName); | 119 shortName); |
| 151 } | 120 } |
| 152 } | 121 } |
| 153 | 122 |
| 154 bool VisualLightweightBenchModule::advanceRecordIfNecessary(SkCanvas* canvas) { | 123 bool VisualLightweightBenchModule::advanceRecordIfNecessary(SkCanvas* canvas) { |
| 155 if (!fBenchmark && fState == kWarmup_State) { | |
| 156 fOwner->clear(canvas, SK_ColorWHITE, 2); | |
| 157 fBenchmark.reset(new WarmupBench); | |
| 158 return true; | |
| 159 } | |
| 160 | |
| 161 if (fBenchmark) { | 124 if (fBenchmark) { |
| 162 return true; | 125 return true; |
| 163 } | 126 } |
| 164 | 127 |
| 165 fBenchmark.reset(fBenchmarkStream->next()); | 128 fBenchmark.reset(fBenchmarkStream->next()); |
| 166 if (!fBenchmark) { | 129 if (!fBenchmark) { |
| 167 return false; | 130 return false; |
| 168 } | 131 } |
| 169 | 132 |
| 170 fOwner->clear(canvas, SK_ColorWHITE, 2); | 133 fOwner->clear(canvas, SK_ColorWHITE, 2); |
| 171 | 134 |
| 172 fBenchmark->delayedSetup(); | 135 fBenchmark->delayedSetup(); |
| 173 fRecords.push_back(); | 136 fRecords.push_back(); |
| 174 | 137 |
| 175 // Log bench name | 138 // Log bench name |
| 176 fResults->bench(fBenchmark->getUniqueName(), fBenchmark->getSize().fX, | 139 fResults->bench(fBenchmark->getUniqueName(), fBenchmark->getSize().fX, |
| 177 fBenchmark->getSize().fY); | 140 fBenchmark->getSize().fY); |
| 178 return true; | 141 return true; |
| 179 } | 142 } |
| 180 | 143 |
| 181 inline void VisualLightweightBenchModule::nextState(State nextState) { | |
| 182 fState = nextState; | |
| 183 } | |
| 184 | |
| 185 void VisualLightweightBenchModule::perCanvasPreDraw(SkCanvas* canvas, State next
State) { | |
| 186 fBenchmark->perCanvasPreDraw(canvas); | |
| 187 fBenchmark->preDraw(canvas); | |
| 188 fCurrentFrame = 0; | |
| 189 this->nextState(nextState); | |
| 190 } | |
| 191 | |
| 192 void VisualLightweightBenchModule::warmup(SkCanvas* canvas) { | |
| 193 if (fCurrentFrame >= FLAGS_maxWarmupFrames) { | |
| 194 this->nextState(kPreWarmLoopsPerCanvasPreDraw_State); | |
| 195 fBenchmark.reset(nullptr); | |
| 196 this->resetTimingState(); | |
| 197 fLoops = 1; | |
| 198 } else { | |
| 199 bool isEven = (fCurrentFrame++ % 2) == 0; | |
| 200 if (isEven) { | |
| 201 fTimer.start(); | |
| 202 } else { | |
| 203 double elapsedMs = this->elapsed(); | |
| 204 if (elapsedMs < FLAGS_loopMs) { | |
| 205 fLoops *= 2; | |
| 206 } | |
| 207 fTimer = WallTimer(); | |
| 208 fOwner->reset(); | |
| 209 } | |
| 210 } | |
| 211 } | |
| 212 | |
| 213 void VisualLightweightBenchModule::preWarm(State nextState) { | |
| 214 if (fCurrentFrame >= FLAGS_gpuFrameLag) { | |
| 215 // we currently time across all frames to make sure we capture all GPU w
ork | |
| 216 this->nextState(nextState); | |
| 217 fCurrentFrame = 0; | |
| 218 fTimer.start(); | |
| 219 } else { | |
| 220 fCurrentFrame++; | |
| 221 } | |
| 222 } | |
| 223 | |
| 224 void VisualLightweightBenchModule::draw(SkCanvas* canvas) { | 144 void VisualLightweightBenchModule::draw(SkCanvas* canvas) { |
| 225 if (!this->advanceRecordIfNecessary(canvas)) { | 145 if (!this->advanceRecordIfNecessary(canvas)) { |
| 226 SkDebugf("Exiting VisualBench successfully\n"); | 146 SkDebugf("Exiting VisualBench successfully\n"); |
| 227 fOwner->closeWindow(); | 147 fOwner->closeWindow(); |
| 228 return; | 148 return; |
| 229 } | 149 } |
| 230 this->renderFrame(canvas); | 150 this->renderFrame(canvas); |
| 231 switch (fState) { | 151 TimingStateMachine::ParentEvents event = fTSM.nextFrame(canvas, fBenchmark); |
| 232 case kWarmup_State: { | 152 switch (event) { |
| 233 this->warmup(canvas); | 153 case TimingStateMachine::kReset_ParentEvents: |
| 154 fOwner->reset(); |
| 234 break; | 155 break; |
| 235 } | 156 case TimingStateMachine::kTiming_ParentEvents: |
| 236 case kPreWarmLoopsPerCanvasPreDraw_State: { | |
| 237 this->perCanvasPreDraw(canvas, kPreWarmLoops_State); | |
| 238 break; | 157 break; |
| 239 } | 158 case TimingStateMachine::kTimingFinished_ParentEvents: |
| 240 case kPreWarmLoops_State: { | 159 fOwner->reset(); |
| 241 this->preWarm(kTuneLoops_State); | 160 fRecords.back().fMeasurements.push_back(fTSM.lastMeasurement()); |
| 161 if (++fCurrentSample > FLAGS_samples) { |
| 162 this->printStats(); |
| 163 fTSM.nextBenchmark(canvas, fBenchmark); |
| 164 fCurrentSample = 0; |
| 165 fBenchmark.reset(nullptr); |
| 166 } |
| 242 break; | 167 break; |
| 243 } | |
| 244 case kTuneLoops_State: { | |
| 245 this->tuneLoops(); | |
| 246 break; | |
| 247 } | |
| 248 case kPreWarmTimingPerCanvasPreDraw_State: { | |
| 249 this->perCanvasPreDraw(canvas, kPreWarmTiming_State); | |
| 250 break; | |
| 251 } | |
| 252 case kPreWarmTiming_State: { | |
| 253 this->preWarm(kTiming_State); | |
| 254 break; | |
| 255 } | |
| 256 case kTiming_State: { | |
| 257 this->timing(canvas); | |
| 258 break; | |
| 259 } | |
| 260 } | 168 } |
| 261 } | |
| 262 | 169 |
| 263 inline double VisualLightweightBenchModule::elapsed() { | |
| 264 fTimer.end(); | |
| 265 return fTimer.fWall; | |
| 266 } | |
| 267 | |
| 268 void VisualLightweightBenchModule::resetTimingState() { | |
| 269 fCurrentFrame = 0; | |
| 270 fTimer = WallTimer(); | |
| 271 fOwner->reset(); | |
| 272 } | |
| 273 | |
| 274 inline void VisualLightweightBenchModule::tuneLoops() { | |
| 275 if (1 << 30 == fLoops) { | |
| 276 // We're about to wrap. Something's wrong with the bench. | |
| 277 SkDebugf("InnerLoops wrapped\n"); | |
| 278 fLoops = 1; | |
| 279 } else { | |
| 280 double elapsedMs = this->elapsed(); | |
| 281 if (elapsedMs > FLAGS_loopMs) { | |
| 282 this->nextState(kPreWarmTimingPerCanvasPreDraw_State); | |
| 283 } else { | |
| 284 fLoops *= 2; | |
| 285 this->nextState(kPreWarmLoops_State); | |
| 286 } | |
| 287 this->resetTimingState(); | |
| 288 } | |
| 289 } | |
| 290 | |
| 291 void VisualLightweightBenchModule::recordMeasurement() { | |
| 292 double measurement = this->elapsed() / (FLAGS_frames * fLoops); | |
| 293 fRecords.back().fMeasurements.push_back(measurement); | |
| 294 } | |
| 295 | |
| 296 void VisualLightweightBenchModule::postDraw(SkCanvas* canvas) { | |
| 297 fBenchmark->postDraw(canvas); | |
| 298 fBenchmark->perCanvasPostDraw(canvas); | |
| 299 fBenchmark.reset(nullptr); | |
| 300 fCurrentSample = 0; | |
| 301 fLoops = 1; | |
| 302 } | |
| 303 | |
| 304 inline void VisualLightweightBenchModule::timing(SkCanvas* canvas) { | |
| 305 if (fCurrentFrame >= FLAGS_frames) { | |
| 306 this->recordMeasurement(); | |
| 307 if (fCurrentSample++ >= FLAGS_samples) { | |
| 308 this->printStats(); | |
| 309 this->postDraw(canvas); | |
| 310 this->nextState(kPreWarmLoopsPerCanvasPreDraw_State); | |
| 311 } else { | |
| 312 this->nextState(kPreWarmTimingPerCanvasPreDraw_State); | |
| 313 } | |
| 314 this->resetTimingState(); | |
| 315 } else { | |
| 316 fCurrentFrame++; | |
| 317 } | |
| 318 } | 170 } |
| 319 | 171 |
| 320 bool VisualLightweightBenchModule::onHandleChar(SkUnichar c) { | 172 bool VisualLightweightBenchModule::onHandleChar(SkUnichar c) { |
| 321 return true; | 173 return true; |
| 322 } | 174 } |
| OLD | NEW |