| 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 "VisualBench.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 // Between flushes we call flush on GrContext | |
| 29 | 28 |
| 30 DEFINE_int32(gpuFrameLag, 5, "Overestimate of maximum number of frames GPU allow
s to lag."); | 29 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."); | 30 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."); | 31 DEFINE_int32(frames, 5, "Number of frames of each skp to render per sample."); |
| 33 DEFINE_double(loopMs, 5, "Target loop time in millseconds."); | 32 DEFINE_double(loopMs, 5, "Target loop time in millseconds."); |
| 34 DEFINE_int32(msaa, 0, "Number of msaa samples."); | |
| 35 DEFINE_bool2(fullscreen, f, true, "Run fullscreen."); | |
| 36 DEFINE_bool2(verbose, v, false, "enable verbose output from the test driver."); | 33 DEFINE_bool2(verbose, v, false, "enable verbose output from the test driver."); |
| 37 DEFINE_string(key, "", ""); // dummy to enable gm tests that have platform-spec
ific names | 34 DEFINE_string(key, "", ""); // dummy to enable gm tests that have platform-spec
ific names |
| 38 DEFINE_string(outResultsFile, "", "If given, write results here as JSON."); | 35 DEFINE_string(outResultsFile, "", "If given, write results here as JSON."); |
| 39 DEFINE_string(properties, "", | 36 DEFINE_string(properties, "", |
| 40 "Space-separated key/value pairs to add to JSON identifying this r
un."); | 37 "Space-separated key/value pairs to add to JSON identifying this r
un."); |
| 41 | 38 |
| 42 static SkString humanize(double ms) { | 39 static SkString humanize(double ms) { |
| 43 if (FLAGS_verbose) { | 40 if (FLAGS_verbose) { |
| 44 return SkStringPrintf("%llu", (uint64_t)(ms*1e6)); | 41 return SkStringPrintf("%llu", (uint64_t)(ms*1e6)); |
| 45 } | 42 } |
| 46 return HumanizeMs(ms); | 43 return HumanizeMs(ms); |
| 47 } | 44 } |
| 48 | 45 |
| 49 #define HUMANIZE(time) humanize(time).c_str() | 46 #define HUMANIZE(time) humanize(time).c_str() |
| 50 | 47 |
| 51 VisualBench::VisualBench(void* hwnd, int argc, char** argv) | 48 VisualLightweightBenchModule::VisualLightweightBenchModule(VisualBench* owner) |
| 52 : INHERITED(hwnd) | 49 : fCurrentSample(0) |
| 53 , fCurrentSample(0) | |
| 54 , fCurrentFrame(0) | 50 , fCurrentFrame(0) |
| 55 , fLoops(1) | 51 , fLoops(1) |
| 56 , fState(kPreWarmLoops_State) | 52 , fState(kPreWarmLoops_State) |
| 57 , fBenchmark(nullptr) | 53 , fBenchmark(nullptr) |
| 54 , fOwner(SkRef(owner)) |
| 58 , fResults(new ResultsWriter) { | 55 , fResults(new ResultsWriter) { |
| 59 SkCommandLineFlags::Parse(argc, argv); | |
| 60 | |
| 61 this->setTitle(); | |
| 62 this->setupBackend(); | |
| 63 | |
| 64 fBenchmarkStream.reset(new VisualBenchmarkStream); | 56 fBenchmarkStream.reset(new VisualBenchmarkStream); |
| 65 | 57 |
| 66 // Print header | 58 // Print header |
| 67 SkDebugf("curr/maxrss\tloops\tflushes\tmin\tmedian\tmean\tmax\tstddev\tbench
\n"); | 59 SkDebugf("curr/maxrss\tloops\tflushes\tmin\tmedian\tmean\tmax\tstddev\tbench
\n"); |
| 68 | 60 |
| 69 // setup json logging if required | 61 // setup json logging if required |
| 70 if (!FLAGS_outResultsFile.isEmpty()) { | 62 if (!FLAGS_outResultsFile.isEmpty()) { |
| 71 fResults.reset(new NanoJSONResultsWriter(FLAGS_outResultsFile[0])); | 63 fResults.reset(new NanoJSONResultsWriter(FLAGS_outResultsFile[0])); |
| 72 } | 64 } |
| 73 | 65 |
| 74 if (1 == FLAGS_properties.count() % 2) { | 66 if (1 == FLAGS_properties.count() % 2) { |
| 75 SkDebugf("ERROR: --properties must be passed with an even number of argu
ments.\n"); | 67 SkDebugf("ERROR: --properties must be passed with an even number of argu
ments.\n"); |
| 76 } else { | 68 } else { |
| 77 for (int i = 1; i < FLAGS_properties.count(); i += 2) { | 69 for (int i = 1; i < FLAGS_properties.count(); i += 2) { |
| 78 fResults->property(FLAGS_properties[i - 1], FLAGS_properties[i]); | 70 fResults->property(FLAGS_properties[i - 1], FLAGS_properties[i]); |
| 79 } | 71 } |
| 80 } | 72 } |
| 81 } | 73 } |
| 82 | 74 |
| 83 VisualBench::~VisualBench() { | 75 inline void VisualLightweightBenchModule::renderFrame(SkCanvas* canvas) { |
| 84 INHERITED::detach(); | 76 fBenchmark->draw(fLoops, canvas); |
| 77 canvas->flush(); |
| 78 fOwner->present(); |
| 85 } | 79 } |
| 86 | 80 |
| 87 void VisualBench::setTitle() { | 81 void VisualLightweightBenchModule::printStats() { |
| 88 SkString title("VisualBench"); | |
| 89 INHERITED::setTitle(title.c_str()); | |
| 90 } | |
| 91 | |
| 92 SkSurface* VisualBench::createSurface() { | |
| 93 if (!fSurface) { | |
| 94 SkSurfaceProps props(INHERITED::getSurfaceProps()); | |
| 95 fSurface.reset(SkSurface::NewRenderTargetDirect(fRenderTarget, &props)); | |
| 96 } | |
| 97 | |
| 98 // The caller will wrap the SkSurface in an SkAutoTUnref | |
| 99 return SkRef(fSurface.get()); | |
| 100 } | |
| 101 | |
| 102 bool VisualBench::setupBackend() { | |
| 103 this->setColorType(kRGBA_8888_SkColorType); | |
| 104 this->setVisibleP(true); | |
| 105 this->setClipToBounds(false); | |
| 106 | |
| 107 if (FLAGS_fullscreen) { | |
| 108 if (!this->makeFullscreen()) { | |
| 109 SkDebugf("Could not go fullscreen!"); | |
| 110 } | |
| 111 } | |
| 112 if (!this->attach(kNativeGL_BackEndType, FLAGS_msaa, &fAttachmentInfo)) { | |
| 113 SkDebugf("Not possible to create backend.\n"); | |
| 114 INHERITED::detach(); | |
| 115 return false; | |
| 116 } | |
| 117 | |
| 118 this->setVsync(false); | |
| 119 this->resetContext(); | |
| 120 return true; | |
| 121 } | |
| 122 | |
| 123 void VisualBench::resetContext() { | |
| 124 fSurface.reset(nullptr); | |
| 125 | |
| 126 fInterface.reset(GrGLCreateNativeInterface()); | |
| 127 SkASSERT(fInterface); | |
| 128 | |
| 129 // setup contexts | |
| 130 fContext.reset(GrContext::Create(kOpenGL_GrBackend, (GrBackendContext)fInter
face.get())); | |
| 131 SkASSERT(fContext); | |
| 132 | |
| 133 // setup rendertargets | |
| 134 this->setupRenderTarget(); | |
| 135 } | |
| 136 | |
| 137 void VisualBench::setupRenderTarget() { | |
| 138 if (fContext) { | |
| 139 fRenderTarget.reset(this->renderTarget(fAttachmentInfo, fInterface, fCon
text)); | |
| 140 } | |
| 141 } | |
| 142 | |
| 143 inline void VisualBench::renderFrame(SkCanvas* canvas) { | |
| 144 fBenchmark->draw(fLoops, canvas); | |
| 145 canvas->flush(); | |
| 146 INHERITED::present(); | |
| 147 } | |
| 148 | |
| 149 void VisualBench::printStats() { | |
| 150 const SkTArray<double>& measurements = fRecords.back().fMeasurements; | 82 const SkTArray<double>& measurements = fRecords.back().fMeasurements; |
| 151 const char* shortName = fBenchmark->getUniqueName(); | 83 const char* shortName = fBenchmark->getUniqueName(); |
| 152 | 84 |
| 153 // update log | 85 // update log |
| 154 // Note: We currently log only the minimum. It would be interesting to log
more information | 86 // Note: We currently log only the minimum. It would be interesting to log
more information |
| 155 SkString configName; | 87 SkString configName; |
| 156 if (FLAGS_msaa > 0) { | 88 if (FLAGS_msaa > 0) { |
| 157 configName.appendf("msaa_%d", FLAGS_msaa); | 89 configName.appendf("msaa_%d", FLAGS_msaa); |
| 158 } else { | 90 } else { |
| 159 configName.appendf("gpu"); | 91 configName.appendf("gpu"); |
| (...skipping 18 matching lines...) Expand all Loading... |
| 178 fLoops, | 110 fLoops, |
| 179 HUMANIZE(stats.min), | 111 HUMANIZE(stats.min), |
| 180 HUMANIZE(stats.median), | 112 HUMANIZE(stats.median), |
| 181 HUMANIZE(stats.mean), | 113 HUMANIZE(stats.mean), |
| 182 HUMANIZE(stats.max), | 114 HUMANIZE(stats.max), |
| 183 stdDevPercent, | 115 stdDevPercent, |
| 184 shortName); | 116 shortName); |
| 185 } | 117 } |
| 186 } | 118 } |
| 187 | 119 |
| 188 bool VisualBench::advanceRecordIfNecessary(SkCanvas* canvas) { | 120 bool VisualLightweightBenchModule::advanceRecordIfNecessary(SkCanvas* canvas) { |
| 189 if (fBenchmark) { | 121 if (fBenchmark) { |
| 190 return true; | 122 return true; |
| 191 } | 123 } |
| 192 | 124 |
| 193 fBenchmark.reset(fBenchmarkStream->next()); | 125 fBenchmark.reset(fBenchmarkStream->next()); |
| 194 if (!fBenchmark) { | 126 if (!fBenchmark) { |
| 195 return false; | 127 return false; |
| 196 } | 128 } |
| 197 | 129 |
| 198 canvas->clear(0xffffffff); | 130 canvas->clear(0xffffffff); |
| 199 fBenchmark->preDraw(); | 131 fBenchmark->preDraw(); |
| 200 fRecords.push_back(); | 132 fRecords.push_back(); |
| 201 | 133 |
| 202 // Log bench name | 134 // Log bench name |
| 203 fResults->bench(fBenchmark->getUniqueName(), fBenchmark->getSize().fX, | 135 fResults->bench(fBenchmark->getUniqueName(), fBenchmark->getSize().fX, |
| 204 fBenchmark->getSize().fY); | 136 fBenchmark->getSize().fY); |
| 205 return true; | 137 return true; |
| 206 } | 138 } |
| 207 | 139 |
| 208 inline void VisualBench::nextState(State nextState) { | 140 inline void VisualLightweightBenchModule::nextState(State nextState) { |
| 209 fState = nextState; | 141 fState = nextState; |
| 210 } | 142 } |
| 211 | 143 |
| 212 void VisualBench::perCanvasPreDraw(SkCanvas* canvas, State nextState) { | 144 void VisualLightweightBenchModule::perCanvasPreDraw(SkCanvas* canvas, State next
State) { |
| 213 fBenchmark->perCanvasPreDraw(canvas); | 145 fBenchmark->perCanvasPreDraw(canvas); |
| 214 fCurrentFrame = 0; | 146 fCurrentFrame = 0; |
| 215 this->nextState(nextState); | 147 this->nextState(nextState); |
| 216 } | 148 } |
| 217 | 149 |
| 218 void VisualBench::preWarm(State nextState) { | 150 void VisualLightweightBenchModule::preWarm(State nextState) { |
| 219 if (fCurrentFrame >= FLAGS_gpuFrameLag) { | 151 if (fCurrentFrame >= FLAGS_gpuFrameLag) { |
| 220 // we currently time across all frames to make sure we capture all GPU w
ork | 152 // we currently time across all frames to make sure we capture all GPU w
ork |
| 221 this->nextState(nextState); | 153 this->nextState(nextState); |
| 222 fCurrentFrame = 0; | 154 fCurrentFrame = 0; |
| 223 fTimer.start(); | 155 fTimer.start(); |
| 224 } else { | 156 } else { |
| 225 fCurrentFrame++; | 157 fCurrentFrame++; |
| 226 } | 158 } |
| 227 } | 159 } |
| 228 | 160 |
| 229 void VisualBench::draw(SkCanvas* canvas) { | 161 void VisualLightweightBenchModule::draw(SkCanvas* canvas) { |
| 230 if (!this->advanceRecordIfNecessary(canvas)) { | 162 if (!this->advanceRecordIfNecessary(canvas)) { |
| 231 SkDebugf("Exiting VisualBench successfully\n"); | 163 SkDebugf("Exiting VisualBench successfully\n"); |
| 232 this->closeWindow(); | 164 fOwner->closeWindow(); |
| 233 return; | 165 return; |
| 234 } | 166 } |
| 235 this->renderFrame(canvas); | 167 this->renderFrame(canvas); |
| 236 switch (fState) { | 168 switch (fState) { |
| 237 case kPreWarmLoopsPerCanvasPreDraw_State: { | 169 case kPreWarmLoopsPerCanvasPreDraw_State: { |
| 238 this->perCanvasPreDraw(canvas, kPreWarmLoops_State); | 170 this->perCanvasPreDraw(canvas, kPreWarmLoops_State); |
| 239 break; | 171 break; |
| 240 } | 172 } |
| 241 case kPreWarmLoops_State: { | 173 case kPreWarmLoops_State: { |
| 242 this->preWarm(kTuneLoops_State); | 174 this->preWarm(kTuneLoops_State); |
| 243 break; | 175 break; |
| 244 } | 176 } |
| 245 case kTuneLoops_State: { | 177 case kTuneLoops_State: { |
| 246 this->tuneLoops(); | 178 this->tuneLoops(); |
| 247 break; | 179 break; |
| 248 } | 180 } |
| 249 case kPreWarmTimingPerCanvasPreDraw_State: { | 181 case kPreWarmTimingPerCanvasPreDraw_State: { |
| 250 this->perCanvasPreDraw(canvas, kPreWarmTiming_State); | 182 this->perCanvasPreDraw(canvas, kPreWarmTiming_State); |
| 251 break; | 183 break; |
| 252 } | 184 } |
| 253 case kPreWarmTiming_State: { | 185 case kPreWarmTiming_State: { |
| 254 this->preWarm(kTiming_State); | 186 this->preWarm(kTiming_State); |
| 255 break; | 187 break; |
| 256 } | 188 } |
| 257 case kTiming_State: { | 189 case kTiming_State: { |
| 258 this->timing(canvas); | 190 this->timing(canvas); |
| 259 break; | 191 break; |
| 260 } | 192 } |
| 261 } | 193 } |
| 262 | |
| 263 // Invalidate the window to force a redraw. Poor man's animation mechanism. | |
| 264 this->inval(nullptr); | |
| 265 } | 194 } |
| 266 | 195 |
| 267 inline double VisualBench::elapsed() { | 196 inline double VisualLightweightBenchModule::elapsed() { |
| 268 fTimer.end(); | 197 fTimer.end(); |
| 269 return fTimer.fWall; | 198 return fTimer.fWall; |
| 270 } | 199 } |
| 271 | 200 |
| 272 void VisualBench::resetTimingState() { | 201 void VisualLightweightBenchModule::resetTimingState() { |
| 273 fCurrentFrame = 0; | 202 fCurrentFrame = 0; |
| 274 fTimer = WallTimer(); | 203 fTimer = WallTimer(); |
| 275 this->resetContext(); | 204 fOwner->reset(); |
| 276 } | 205 } |
| 277 | 206 |
| 278 void VisualBench::scaleLoops(double elapsedMs) { | 207 void VisualLightweightBenchModule::scaleLoops(double elapsedMs) { |
| 279 // Scale back the number of loops | 208 // Scale back the number of loops |
| 280 fLoops = (int)ceil(fLoops * FLAGS_loopMs / elapsedMs); | 209 fLoops = (int)ceil(fLoops * FLAGS_loopMs / elapsedMs); |
| 281 } | 210 } |
| 282 | 211 |
| 283 inline void VisualBench::tuneLoops() { | 212 inline void VisualLightweightBenchModule::tuneLoops() { |
| 284 if (1 << 30 == fLoops) { | 213 if (1 << 30 == fLoops) { |
| 285 // We're about to wrap. Something's wrong with the bench. | 214 // We're about to wrap. Something's wrong with the bench. |
| 286 SkDebugf("InnerLoops wrapped\n"); | 215 SkDebugf("InnerLoops wrapped\n"); |
| 287 fLoops = 0; | 216 fLoops = 0; |
| 288 } else { | 217 } else { |
| 289 double elapsedMs = this->elapsed(); | 218 double elapsedMs = this->elapsed(); |
| 290 if (elapsedMs > FLAGS_loopMs) { | 219 if (elapsedMs > FLAGS_loopMs) { |
| 291 this->scaleLoops(elapsedMs); | 220 this->scaleLoops(elapsedMs); |
| 292 this->nextState(kPreWarmTimingPerCanvasPreDraw_State); | 221 this->nextState(kPreWarmTimingPerCanvasPreDraw_State); |
| 293 } else { | 222 } else { |
| 294 fLoops *= 2; | 223 fLoops *= 2; |
| 295 this->nextState(kPreWarmLoops_State); | 224 this->nextState(kPreWarmLoops_State); |
| 296 } | 225 } |
| 297 this->resetTimingState(); | 226 this->resetTimingState(); |
| 298 } | 227 } |
| 299 } | 228 } |
| 300 | 229 |
| 301 void VisualBench::recordMeasurement() { | 230 void VisualLightweightBenchModule::recordMeasurement() { |
| 302 double measurement = this->elapsed() / (FLAGS_frames * fLoops); | 231 double measurement = this->elapsed() / (FLAGS_frames * fLoops); |
| 303 fRecords.back().fMeasurements.push_back(measurement); | 232 fRecords.back().fMeasurements.push_back(measurement); |
| 304 } | 233 } |
| 305 | 234 |
| 306 void VisualBench::postDraw(SkCanvas* canvas) { | 235 void VisualLightweightBenchModule::postDraw(SkCanvas* canvas) { |
| 307 fBenchmark->perCanvasPostDraw(canvas); | 236 fBenchmark->perCanvasPostDraw(canvas); |
| 308 fBenchmark.reset(nullptr); | 237 fBenchmark.reset(nullptr); |
| 309 fCurrentSample = 0; | 238 fCurrentSample = 0; |
| 310 fLoops = 1; | 239 fLoops = 1; |
| 311 } | 240 } |
| 312 | 241 |
| 313 inline void VisualBench::timing(SkCanvas* canvas) { | 242 inline void VisualLightweightBenchModule::timing(SkCanvas* canvas) { |
| 314 if (fCurrentFrame >= FLAGS_frames) { | 243 if (fCurrentFrame >= FLAGS_frames) { |
| 315 this->recordMeasurement(); | 244 this->recordMeasurement(); |
| 316 if (fCurrentSample++ >= FLAGS_samples) { | 245 if (fCurrentSample++ >= FLAGS_samples) { |
| 317 this->printStats(); | 246 this->printStats(); |
| 318 this->postDraw(canvas); | 247 this->postDraw(canvas); |
| 319 this->nextState(kPreWarmLoopsPerCanvasPreDraw_State); | 248 this->nextState(kPreWarmLoopsPerCanvasPreDraw_State); |
| 320 } else { | 249 } else { |
| 321 this->nextState(kPreWarmTimingPerCanvasPreDraw_State); | 250 this->nextState(kPreWarmTimingPerCanvasPreDraw_State); |
| 322 } | 251 } |
| 323 this->resetTimingState(); | 252 this->resetTimingState(); |
| 324 } else { | 253 } else { |
| 325 fCurrentFrame++; | 254 fCurrentFrame++; |
| 326 } | 255 } |
| 327 } | 256 } |
| 328 | |
| 329 void VisualBench::onSizeChange() { | |
| 330 this->setupRenderTarget(); | |
| 331 } | |
| 332 | |
| 333 bool VisualBench::onHandleChar(SkUnichar unichar) { | |
| 334 return true; | |
| 335 } | |
| 336 | |
| 337 // Externally declared entry points | |
| 338 void application_init() { | |
| 339 SkGraphics::Init(); | |
| 340 SkEvent::Init(); | |
| 341 } | |
| 342 | |
| 343 void application_term() { | |
| 344 SkEvent::Term(); | |
| 345 SkGraphics::Term(); | |
| 346 } | |
| 347 | |
| 348 SkOSWindow* create_sk_window(void* hwnd, int argc, char** argv) { | |
| 349 return new VisualBench(hwnd, argc, argv); | |
| 350 } | |
| 351 | |
| OLD | NEW |