Index: tools/VisualBench.cpp |
diff --git a/tools/VisualBench.cpp b/tools/VisualBench.cpp |
index 3f8d8036aada0d12fd3bcb370dda2fb2c588ad58..fd0662a008fe7b9b2ca1001b27eefa5cd9f8b7b0 100644 |
--- a/tools/VisualBench.cpp |
+++ b/tools/VisualBench.cpp |
@@ -8,6 +8,7 @@ |
#include "VisualBench.h" |
+#include "ProcStats.h" |
#include "SkApplication.h" |
#include "SkCanvas.h" |
#include "SkCommandLineFlags.h" |
@@ -18,35 +19,49 @@ |
#include "SkImageDecoder.h" |
#include "SkOSFile.h" |
#include "SkStream.h" |
-#include "Timer.h" |
+#include "Stats.h" |
#include "gl/GrGLInterface.h" |
__SK_FORCE_IMAGE_DECODER_LINKING; |
+DEFINE_int32(gpuFrameLag, 5, "Overestimate of maximum number of frames GPU allows to lag."); |
+DEFINE_int32(samples, 10, "Number of times to render each skp."); |
+DEFINE_int32(loops, 5, "Number of times to time."); |
+DEFINE_int32(msaa, 0, "Number of msaa samples."); |
+ |
+static SkString humanize(double ms) { |
+ if (FLAGS_verbose) { |
+ return SkStringPrintf("%llu", (uint64_t)(ms*1e6)); |
+ } |
+ return HumanizeMs(ms); |
+} |
+ |
+#define HUMANIZE(time) humanize(time).c_str() |
+ |
VisualBench::VisualBench(void* hwnd, int argc, char** argv) |
: INHERITED(hwnd) |
- , fCurrentLoops(1) |
+ , fLoop(0) |
, fCurrentPicture(0) |
- , fCurrentFrame(0) { |
+ , fCurrentSample(0) |
+ , fState(kPreWarm_State) { |
SkCommandLineFlags::Parse(argc, argv); |
+ // load all SKPs |
SkTArray<SkString> skps; |
for (int i = 0; i < FLAGS_skps.count(); i++) { |
if (SkStrEndsWith(FLAGS_skps[i], ".skp")) { |
skps.push_back() = FLAGS_skps[i]; |
+ fTimings.push_back().fName = FLAGS_skps[i]; |
} else { |
SkOSFile::Iter it(FLAGS_skps[i], ".skp"); |
SkString path; |
while (it.next(&path)) { |
skps.push_back() = SkOSPath::Join(FLAGS_skps[0], path.c_str()); |
+ fTimings.push_back().fName = path.c_str(); |
} |
} |
} |
- this->setTitle(); |
- this->setupBackend(); |
- |
- // Load picture for playback |
for (int i = 0; i < skps.count(); i++) { |
SkFILEStream stream(skps[i].c_str()); |
if (stream.isValid()) { |
@@ -55,6 +70,13 @@ VisualBench::VisualBench(void* hwnd, int argc, char** argv) |
SkDebugf("couldn't load picture at \"path\"\n", skps[i].c_str()); |
} |
} |
+ |
+ if (fPictures.empty()) { |
+ SkDebugf("no valid skps found\n"); |
+ } |
+ |
+ this->setTitle(); |
+ this->setupBackend(); |
} |
VisualBench::~VisualBench() { |
@@ -79,7 +101,7 @@ bool VisualBench::setupBackend() { |
this->setVisibleP(true); |
this->setClipToBounds(false); |
- if (!this->attach(kNativeGL_BackEndType, 0 /*msaa*/, &fAttachmentInfo)) { |
+ if (!this->attach(kNativeGL_BackEndType, FLAGS_msaa, &fAttachmentInfo)) { |
SkDebugf("Not possible to create backend.\n"); |
INHERITED::detach(); |
return false; |
@@ -87,7 +109,11 @@ bool VisualBench::setupBackend() { |
this->setFullscreen(true); |
this->setVsync(false); |
+ this->resetContext(); |
+ return true; |
+} |
+void VisualBench::resetContext() { |
fInterface.reset(GrGLCreateNativeInterface()); |
SkASSERT(fInterface); |
@@ -97,26 +123,84 @@ bool VisualBench::setupBackend() { |
// setup rendertargets |
this->setupRenderTarget(); |
- return true; |
} |
void VisualBench::setupRenderTarget() { |
fRenderTarget.reset(this->renderTarget(fAttachmentInfo, fInterface, fContext)); |
} |
-void VisualBench::draw(SkCanvas* canvas) { |
- fCurrentFrame++; |
- WallTimer timer; |
- timer.start(); |
- for (int i = 0; i < fCurrentLoops; i++) { |
- canvas->drawPicture(fPictures[fCurrentPicture]); |
- } |
- // in case we have queued drawing calls |
+inline void VisualBench::renderFrame(SkCanvas* canvas) { |
+ canvas->drawPicture(fPictures[fCurrentPicture]); |
fContext->flush(); |
INHERITED::present(); |
- timer.end(); |
+} |
+ |
+void VisualBench::printStats() { |
+ const SkTArray<double>& measurements = fTimings[fCurrentPicture].fMeasurements; |
+ if (FLAGS_verbose) { |
+ for (int i = 0; i < measurements.count(); i++) { |
+ SkDebugf("%s ", HUMANIZE(measurements[i])); |
+ } |
+ SkDebugf("%s\n", fTimings[fCurrentPicture].fName.c_str()); |
+ } else { |
+ SkASSERT(measurements.count()); |
+ Stats stats(measurements.begin(), measurements.count()); |
+ const double stdDevPercent = 100 * sqrt(stats.var) / stats.mean; |
+ SkDebugf("%4d/%-4dMB\t%s\t%s\t%s\t%s\t%.0f%%\t%s\n", |
+ sk_tools::getCurrResidentSetSizeMB(), |
+ sk_tools::getMaxResidentSetSizeMB(), |
+ HUMANIZE(stats.min), |
+ HUMANIZE(stats.median), |
+ HUMANIZE(stats.mean), |
+ HUMANIZE(stats.max), |
+ stdDevPercent, |
+ fTimings[fCurrentPicture].fName.c_str()); |
+ } |
+} |
+ |
+void VisualBench::timePicture(SkCanvas* canvas) { |
+ this->renderFrame(canvas); |
+ switch (fState) { |
+ case kPreWarm_State: { |
+ if (fCurrentSample >= FLAGS_gpuFrameLag) { |
+ // TODO we currently time across all frames to make sure we capture all GPU work |
+ // We should also rendering an empty SKP to get a baseline to subtract from |
+ // our timing |
+ fState = kTiming_State; |
+ fCurrentSample -= FLAGS_gpuFrameLag; |
+ fTimer.start(); |
+ } else { |
+ fCurrentSample++; |
+ } |
+ break; |
+ } |
+ case kTiming_State: { |
+ if (fCurrentSample >= FLAGS_samples) { |
+ fTimer.end(); |
+ fTimings[fCurrentPicture].fMeasurements.push_back(fTimer.fWall / FLAGS_samples); |
+ this->resetContext(); |
+ fTimer = WallTimer(); |
+ fState = kPreWarm_State; |
+ fCurrentSample = 0; |
+ if (fLoop++ > FLAGS_loops) { |
+ this->printStats(); |
+ fCurrentPicture++; |
+ fLoop = 0; |
+ } |
+ } else { |
+ fCurrentSample++; |
+ } |
+ break; |
+ } |
+ } |
+} |
- SkDebugf("%s\n", HumanizeMs(timer.fWall).c_str()); |
+void VisualBench::draw(SkCanvas* canvas) { |
+ if (fCurrentPicture < fPictures.count()) { |
+ this->timePicture(canvas); |
+ } else { |
+ this->closeWindow(); |
+ } |
// Invalidate the window to force a redraw. Poor man's animation mechanism. |
this->inval(NULL); |