| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright 2015 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 | |
| 9 #include "VisualBench.h" | |
| 10 | |
| 11 #include "ProcStats.h" | |
| 12 #include "SkApplication.h" | |
| 13 #include "SkCanvas.h" | |
| 14 #include "SkCommandLineFlags.h" | |
| 15 #include "SkCommonFlags.h" | |
| 16 #include "SkForceLinking.h" | |
| 17 #include "SkGraphics.h" | |
| 18 #include "SkGr.h" | |
| 19 #include "SkImageDecoder.h" | |
| 20 #include "SkOSFile.h" | |
| 21 #include "SkStream.h" | |
| 22 #include "Stats.h" | |
| 23 #include "gl/GrGLInterface.h" | |
| 24 | |
| 25 __SK_FORCE_IMAGE_DECODER_LINKING; | |
| 26 | |
| 27 // Between samples we reset context | |
| 28 // Between frames we swap buffers | |
| 29 // Between flushes we call flush on GrContext | |
| 30 | |
| 31 DEFINE_int32(gpuFrameLag, 5, "Overestimate of maximum number of frames GPU allow
s to lag."); | |
| 32 DEFINE_int32(samples, 10, "Number of times to time each skp."); | |
| 33 DEFINE_int32(frames, 5, "Number of frames of each skp to render per sample."); | |
| 34 DEFINE_double(flushMs, 20, "Target flush time in millseconds."); | |
| 35 DEFINE_double(loopMs, 5, "Target loop time in millseconds."); | |
| 36 DEFINE_int32(msaa, 0, "Number of msaa samples."); | |
| 37 DEFINE_bool2(fullscreen, f, true, "Run fullscreen."); | |
| 38 | |
| 39 static SkString humanize(double ms) { | |
| 40 if (FLAGS_verbose) { | |
| 41 return SkStringPrintf("%llu", (uint64_t)(ms*1e6)); | |
| 42 } | |
| 43 return HumanizeMs(ms); | |
| 44 } | |
| 45 | |
| 46 #define HUMANIZE(time) humanize(time).c_str() | |
| 47 | |
| 48 VisualBench::VisualBench(void* hwnd, int argc, char** argv) | |
| 49 : INHERITED(hwnd) | |
| 50 , fCurrentPictureIdx(-1) | |
| 51 , fCurrentSample(0) | |
| 52 , fCurrentFrame(0) | |
| 53 , fFlushes(1) | |
| 54 , fLoops(1) | |
| 55 , fState(kPreWarmLoops_State) { | |
| 56 SkCommandLineFlags::Parse(argc, argv); | |
| 57 | |
| 58 // read all the skp file names. | |
| 59 for (int i = 0; i < FLAGS_skps.count(); i++) { | |
| 60 if (SkStrEndsWith(FLAGS_skps[i], ".skp")) { | |
| 61 fRecords.push_back().fFilename = FLAGS_skps[i]; | |
| 62 } else { | |
| 63 SkOSFile::Iter it(FLAGS_skps[i], ".skp"); | |
| 64 SkString path; | |
| 65 while (it.next(&path)) { | |
| 66 fRecords.push_back().fFilename = SkOSPath::Join(FLAGS_skps[i], p
ath.c_str());; | |
| 67 } | |
| 68 } | |
| 69 } | |
| 70 | |
| 71 if (fRecords.empty()) { | |
| 72 SkDebugf("no valid skps found\n"); | |
| 73 } | |
| 74 | |
| 75 this->setTitle(); | |
| 76 this->setupBackend(); | |
| 77 | |
| 78 // Print header | |
| 79 SkDebugf("curr/maxrss\tloops\tflushes\tmin\tmedian\tmean\tmax\tstddev\tbench
\n"); | |
| 80 } | |
| 81 | |
| 82 VisualBench::~VisualBench() { | |
| 83 INHERITED::detach(); | |
| 84 } | |
| 85 | |
| 86 void VisualBench::setTitle() { | |
| 87 SkString title("VisualBench"); | |
| 88 INHERITED::setTitle(title.c_str()); | |
| 89 } | |
| 90 | |
| 91 SkSurface* VisualBench::createSurface() { | |
| 92 SkSurfaceProps props(INHERITED::getSurfaceProps()); | |
| 93 return SkSurface::NewRenderTargetDirect(fRenderTarget, &props); | |
| 94 } | |
| 95 | |
| 96 bool VisualBench::setupBackend() { | |
| 97 this->setColorType(kRGBA_8888_SkColorType); | |
| 98 this->setVisibleP(true); | |
| 99 this->setClipToBounds(false); | |
| 100 | |
| 101 if (FLAGS_fullscreen) { | |
| 102 if (!this->makeFullscreen()) { | |
| 103 SkDebugf("Could not go fullscreen!"); | |
| 104 } | |
| 105 } | |
| 106 if (!this->attach(kNativeGL_BackEndType, FLAGS_msaa, &fAttachmentInfo)) { | |
| 107 SkDebugf("Not possible to create backend.\n"); | |
| 108 INHERITED::detach(); | |
| 109 return false; | |
| 110 } | |
| 111 | |
| 112 this->setVsync(false); | |
| 113 this->resetContext(); | |
| 114 return true; | |
| 115 } | |
| 116 | |
| 117 void VisualBench::resetContext() { | |
| 118 fInterface.reset(GrGLCreateNativeInterface()); | |
| 119 SkASSERT(fInterface); | |
| 120 | |
| 121 // setup contexts | |
| 122 fContext.reset(GrContext::Create(kOpenGL_GrBackend, (GrBackendContext)fInter
face.get())); | |
| 123 SkASSERT(fContext); | |
| 124 | |
| 125 // setup rendertargets | |
| 126 this->setupRenderTarget(); | |
| 127 } | |
| 128 | |
| 129 void VisualBench::setupRenderTarget() { | |
| 130 if (fContext) { | |
| 131 fRenderTarget.reset(this->renderTarget(fAttachmentInfo, fInterface, fCon
text)); | |
| 132 } | |
| 133 } | |
| 134 | |
| 135 inline void VisualBench::renderFrame(SkCanvas* canvas) { | |
| 136 for (int flush = 0; flush < fFlushes; flush++) { | |
| 137 for (int loop = 0; loop < fLoops; loop++) { | |
| 138 canvas->drawPicture(fPicture); | |
| 139 } | |
| 140 canvas->flush(); | |
| 141 } | |
| 142 INHERITED::present(); | |
| 143 } | |
| 144 | |
| 145 void VisualBench::printStats() { | |
| 146 const SkTArray<double>& measurements = fRecords[fCurrentPictureIdx].fMeasure
ments; | |
| 147 SkString shortName = SkOSPath::Basename(fRecords[fCurrentPictureIdx].fFilena
me.c_str()); | |
| 148 if (FLAGS_verbose) { | |
| 149 for (int i = 0; i < measurements.count(); i++) { | |
| 150 SkDebugf("%s ", HUMANIZE(measurements[i])); | |
| 151 } | |
| 152 SkDebugf("%s\n", shortName.c_str()); | |
| 153 } else { | |
| 154 SkASSERT(measurements.count()); | |
| 155 Stats stats(measurements); | |
| 156 const double stdDevPercent = 100 * sqrt(stats.var) / stats.mean; | |
| 157 SkDebugf("%4d/%-4dMB\t%d\t%d\t%s\t%s\t%s\t%s\t%.0f%%\t%s\n", | |
| 158 sk_tools::getCurrResidentSetSizeMB(), | |
| 159 sk_tools::getMaxResidentSetSizeMB(), | |
| 160 fLoops, | |
| 161 fFlushes, | |
| 162 HUMANIZE(stats.min), | |
| 163 HUMANIZE(stats.median), | |
| 164 HUMANIZE(stats.mean), | |
| 165 HUMANIZE(stats.max), | |
| 166 stdDevPercent, | |
| 167 shortName.c_str()); | |
| 168 } | |
| 169 } | |
| 170 | |
| 171 bool VisualBench::advanceRecordIfNecessary() { | |
| 172 if (fPicture) { | |
| 173 return true; | |
| 174 } | |
| 175 ++fCurrentPictureIdx; | |
| 176 while (true) { | |
| 177 if (fCurrentPictureIdx >= fRecords.count()) { | |
| 178 return false; | |
| 179 } | |
| 180 if (this->loadPicture()) { | |
| 181 return true; | |
| 182 } | |
| 183 fRecords.removeShuffle(fCurrentPictureIdx); | |
| 184 } | |
| 185 } | |
| 186 | |
| 187 bool VisualBench::loadPicture() { | |
| 188 const char* fileName = fRecords[fCurrentPictureIdx].fFilename.c_str(); | |
| 189 SkFILEStream stream(fileName); | |
| 190 if (stream.isValid()) { | |
| 191 fPicture.reset(SkPicture::CreateFromStream(&stream)); | |
| 192 if (SkToBool(fPicture)) { | |
| 193 return true; | |
| 194 } | |
| 195 } | |
| 196 SkDebugf("couldn't load picture at \"%s\"\n", fileName); | |
| 197 return false; | |
| 198 } | |
| 199 | |
| 200 void VisualBench::preWarm(State nextState) { | |
| 201 if (fCurrentFrame >= FLAGS_gpuFrameLag) { | |
| 202 // we currently time across all frames to make sure we capture all GPU w
ork | |
| 203 fState = nextState; | |
| 204 fCurrentFrame = 0; | |
| 205 fTimer.start(); | |
| 206 } else { | |
| 207 fCurrentFrame++; | |
| 208 } | |
| 209 } | |
| 210 | |
| 211 void VisualBench::draw(SkCanvas* canvas) { | |
| 212 if (!this->advanceRecordIfNecessary()) { | |
| 213 this->closeWindow(); | |
| 214 return; | |
| 215 } | |
| 216 this->renderFrame(canvas); | |
| 217 switch (fState) { | |
| 218 case kPreWarmLoops_State: { | |
| 219 this->preWarm(kTuneLoops_State); | |
| 220 break; | |
| 221 } | |
| 222 case kTuneLoops_State: { | |
| 223 if (1 << 30 == fLoops) { | |
| 224 // We're about to wrap. Something's wrong with the bench. | |
| 225 SkDebugf("InnerLoops wrapped\n"); | |
| 226 fLoops = 0; | |
| 227 } else { | |
| 228 fTimer.end(); | |
| 229 double elapsed = fTimer.fWall; | |
| 230 if (elapsed > FLAGS_loopMs) { | |
| 231 fState = kPreWarmTiming_State; | |
| 232 | |
| 233 // Scale back the number of loops | |
| 234 fLoops = (int)ceil(fLoops * FLAGS_loopMs / elapsed); | |
| 235 fFlushes = (int)ceil(FLAGS_flushMs / elapsed); | |
| 236 } else { | |
| 237 fState = kPreWarmLoops_State; | |
| 238 fLoops *= 2; | |
| 239 } | |
| 240 | |
| 241 fCurrentFrame = 0; | |
| 242 fTimer = WallTimer(); | |
| 243 this->resetContext(); | |
| 244 } | |
| 245 break; | |
| 246 } | |
| 247 case kPreWarmTiming_State: { | |
| 248 this->preWarm(kTiming_State); | |
| 249 break; | |
| 250 } | |
| 251 case kTiming_State: { | |
| 252 if (fCurrentFrame >= FLAGS_frames) { | |
| 253 fTimer.end(); | |
| 254 fRecords[fCurrentPictureIdx].fMeasurements.push_back( | |
| 255 fTimer.fWall / (FLAGS_frames * fLoops * fFlushes)); | |
| 256 if (fCurrentSample++ >= FLAGS_samples) { | |
| 257 fState = kPreWarmLoops_State; | |
| 258 this->printStats(); | |
| 259 fPicture.reset(NULL); | |
| 260 fCurrentSample = 0; | |
| 261 fFlushes = 1; | |
| 262 fLoops = 1; | |
| 263 } else { | |
| 264 fState = kPreWarmTiming_State; | |
| 265 } | |
| 266 fTimer = WallTimer(); | |
| 267 this->resetContext(); | |
| 268 fCurrentFrame = 0; | |
| 269 } else { | |
| 270 fCurrentFrame++; | |
| 271 } | |
| 272 break; | |
| 273 } | |
| 274 } | |
| 275 | |
| 276 // Invalidate the window to force a redraw. Poor man's animation mechanism. | |
| 277 this->inval(NULL); | |
| 278 } | |
| 279 | |
| 280 void VisualBench::onSizeChange() { | |
| 281 this->setupRenderTarget(); | |
| 282 } | |
| 283 | |
| 284 bool VisualBench::onHandleChar(SkUnichar unichar) { | |
| 285 return true; | |
| 286 } | |
| 287 | |
| 288 // Externally declared entry points | |
| 289 void application_init() { | |
| 290 SkGraphics::Init(); | |
| 291 SkEvent::Init(); | |
| 292 } | |
| 293 | |
| 294 void application_term() { | |
| 295 SkEvent::Term(); | |
| 296 SkGraphics::Term(); | |
| 297 } | |
| 298 | |
| 299 SkOSWindow* create_sk_window(void* hwnd, int argc, char** argv) { | |
| 300 return new VisualBench(hwnd, argc, argv); | |
| 301 } | |
| 302 | |
| OLD | NEW |