aboutsummaryrefslogtreecommitdiffhomepage
path: root/tools/VisualBench
diff options
context:
space:
mode:
authorGravatar joshualitt <joshualitt@chromium.org>2015-06-30 06:50:10 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2015-06-30 06:50:10 -0700
commit91b6f32e07aa479fc634bfc6fc88fe949b933236 (patch)
tree7fb174f2ef43b67e9609518c9e022bf2190342e0 /tools/VisualBench
parent7b971f0152299ae9a924252a9bfd220318497bdd (diff)
Move visualbench to its own folder
Diffstat (limited to 'tools/VisualBench')
-rw-r--r--tools/VisualBench/VisualBench.cpp302
-rw-r--r--tools/VisualBench/VisualBench.h84
2 files changed, 386 insertions, 0 deletions
diff --git a/tools/VisualBench/VisualBench.cpp b/tools/VisualBench/VisualBench.cpp
new file mode 100644
index 0000000000..ac53b43167
--- /dev/null
+++ b/tools/VisualBench/VisualBench.cpp
@@ -0,0 +1,302 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ */
+
+#include "VisualBench.h"
+
+#include "ProcStats.h"
+#include "SkApplication.h"
+#include "SkCanvas.h"
+#include "SkCommandLineFlags.h"
+#include "SkCommonFlags.h"
+#include "SkForceLinking.h"
+#include "SkGraphics.h"
+#include "SkGr.h"
+#include "SkImageDecoder.h"
+#include "SkOSFile.h"
+#include "SkStream.h"
+#include "Stats.h"
+#include "gl/GrGLInterface.h"
+
+__SK_FORCE_IMAGE_DECODER_LINKING;
+
+// Between samples we reset context
+// Between frames we swap buffers
+// Between flushes we call flush on GrContext
+
+DEFINE_int32(gpuFrameLag, 5, "Overestimate of maximum number of frames GPU allows to lag.");
+DEFINE_int32(samples, 10, "Number of times to time each skp.");
+DEFINE_int32(frames, 5, "Number of frames of each skp to render per sample.");
+DEFINE_double(flushMs, 20, "Target flush time in millseconds.");
+DEFINE_double(loopMs, 5, "Target loop time in millseconds.");
+DEFINE_int32(msaa, 0, "Number of msaa samples.");
+DEFINE_bool2(fullscreen, f, true, "Run fullscreen.");
+
+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)
+ , fCurrentPictureIdx(-1)
+ , fCurrentSample(0)
+ , fCurrentFrame(0)
+ , fFlushes(1)
+ , fLoops(1)
+ , fState(kPreWarmLoops_State) {
+ SkCommandLineFlags::Parse(argc, argv);
+
+ // read all the skp file names.
+ for (int i = 0; i < FLAGS_skps.count(); i++) {
+ if (SkStrEndsWith(FLAGS_skps[i], ".skp")) {
+ fRecords.push_back().fFilename = FLAGS_skps[i];
+ } else {
+ SkOSFile::Iter it(FLAGS_skps[i], ".skp");
+ SkString path;
+ while (it.next(&path)) {
+ fRecords.push_back().fFilename = SkOSPath::Join(FLAGS_skps[i], path.c_str());;
+ }
+ }
+ }
+
+ if (fRecords.empty()) {
+ SkDebugf("no valid skps found\n");
+ }
+
+ this->setTitle();
+ this->setupBackend();
+
+ // Print header
+ SkDebugf("curr/maxrss\tloops\tflushes\tmin\tmedian\tmean\tmax\tstddev\tbench\n");
+}
+
+VisualBench::~VisualBench() {
+ INHERITED::detach();
+}
+
+void VisualBench::setTitle() {
+ SkString title("VisualBench");
+ INHERITED::setTitle(title.c_str());
+}
+
+SkSurface* VisualBench::createSurface() {
+ SkSurfaceProps props(INHERITED::getSurfaceProps());
+ return SkSurface::NewRenderTargetDirect(fRenderTarget, &props);
+}
+
+bool VisualBench::setupBackend() {
+ this->setColorType(kRGBA_8888_SkColorType);
+ this->setVisibleP(true);
+ this->setClipToBounds(false);
+
+ if (FLAGS_fullscreen) {
+ if (!this->makeFullscreen()) {
+ SkDebugf("Could not go fullscreen!");
+ }
+ }
+ if (!this->attach(kNativeGL_BackEndType, FLAGS_msaa, &fAttachmentInfo)) {
+ SkDebugf("Not possible to create backend.\n");
+ INHERITED::detach();
+ return false;
+ }
+
+ this->setVsync(false);
+ this->resetContext();
+ return true;
+}
+
+void VisualBench::resetContext() {
+ fInterface.reset(GrGLCreateNativeInterface());
+ SkASSERT(fInterface);
+
+ // setup contexts
+ fContext.reset(GrContext::Create(kOpenGL_GrBackend, (GrBackendContext)fInterface.get()));
+ SkASSERT(fContext);
+
+ // setup rendertargets
+ this->setupRenderTarget();
+}
+
+void VisualBench::setupRenderTarget() {
+ if (fContext) {
+ fRenderTarget.reset(this->renderTarget(fAttachmentInfo, fInterface, fContext));
+ }
+}
+
+inline void VisualBench::renderFrame(SkCanvas* canvas) {
+ for (int flush = 0; flush < fFlushes; flush++) {
+ for (int loop = 0; loop < fLoops; loop++) {
+ canvas->drawPicture(fPicture);
+ }
+ canvas->flush();
+ }
+ INHERITED::present();
+}
+
+void VisualBench::printStats() {
+ const SkTArray<double>& measurements = fRecords[fCurrentPictureIdx].fMeasurements;
+ SkString shortName = SkOSPath::Basename(fRecords[fCurrentPictureIdx].fFilename.c_str());
+ if (FLAGS_verbose) {
+ for (int i = 0; i < measurements.count(); i++) {
+ SkDebugf("%s ", HUMANIZE(measurements[i]));
+ }
+ SkDebugf("%s\n", shortName.c_str());
+ } else {
+ SkASSERT(measurements.count());
+ Stats stats(measurements);
+ const double stdDevPercent = 100 * sqrt(stats.var) / stats.mean;
+ SkDebugf("%4d/%-4dMB\t%d\t%d\t%s\t%s\t%s\t%s\t%.0f%%\t%s\n",
+ sk_tools::getCurrResidentSetSizeMB(),
+ sk_tools::getMaxResidentSetSizeMB(),
+ fLoops,
+ fFlushes,
+ HUMANIZE(stats.min),
+ HUMANIZE(stats.median),
+ HUMANIZE(stats.mean),
+ HUMANIZE(stats.max),
+ stdDevPercent,
+ shortName.c_str());
+ }
+}
+
+bool VisualBench::advanceRecordIfNecessary() {
+ if (fPicture) {
+ return true;
+ }
+ ++fCurrentPictureIdx;
+ while (true) {
+ if (fCurrentPictureIdx >= fRecords.count()) {
+ return false;
+ }
+ if (this->loadPicture()) {
+ return true;
+ }
+ fRecords.removeShuffle(fCurrentPictureIdx);
+ }
+}
+
+bool VisualBench::loadPicture() {
+ const char* fileName = fRecords[fCurrentPictureIdx].fFilename.c_str();
+ SkFILEStream stream(fileName);
+ if (stream.isValid()) {
+ fPicture.reset(SkPicture::CreateFromStream(&stream));
+ if (SkToBool(fPicture)) {
+ return true;
+ }
+ }
+ SkDebugf("couldn't load picture at \"%s\"\n", fileName);
+ return false;
+}
+
+void VisualBench::preWarm(State nextState) {
+ if (fCurrentFrame >= FLAGS_gpuFrameLag) {
+ // we currently time across all frames to make sure we capture all GPU work
+ fState = nextState;
+ fCurrentFrame = 0;
+ fTimer.start();
+ } else {
+ fCurrentFrame++;
+ }
+}
+
+void VisualBench::draw(SkCanvas* canvas) {
+ if (!this->advanceRecordIfNecessary()) {
+ this->closeWindow();
+ return;
+ }
+ this->renderFrame(canvas);
+ switch (fState) {
+ case kPreWarmLoops_State: {
+ this->preWarm(kTuneLoops_State);
+ break;
+ }
+ case kTuneLoops_State: {
+ if (1 << 30 == fLoops) {
+ // We're about to wrap. Something's wrong with the bench.
+ SkDebugf("InnerLoops wrapped\n");
+ fLoops = 0;
+ } else {
+ fTimer.end();
+ double elapsed = fTimer.fWall;
+ if (elapsed > FLAGS_loopMs) {
+ fState = kPreWarmTiming_State;
+
+ // Scale back the number of loops
+ fLoops = (int)ceil(fLoops * FLAGS_loopMs / elapsed);
+ fFlushes = (int)ceil(FLAGS_flushMs / elapsed);
+ } else {
+ fState = kPreWarmLoops_State;
+ fLoops *= 2;
+ }
+
+ fCurrentFrame = 0;
+ fTimer = WallTimer();
+ this->resetContext();
+ }
+ break;
+ }
+ case kPreWarmTiming_State: {
+ this->preWarm(kTiming_State);
+ break;
+ }
+ case kTiming_State: {
+ if (fCurrentFrame >= FLAGS_frames) {
+ fTimer.end();
+ fRecords[fCurrentPictureIdx].fMeasurements.push_back(
+ fTimer.fWall / (FLAGS_frames * fLoops * fFlushes));
+ if (fCurrentSample++ >= FLAGS_samples) {
+ fState = kPreWarmLoops_State;
+ this->printStats();
+ fPicture.reset(NULL);
+ fCurrentSample = 0;
+ fFlushes = 1;
+ fLoops = 1;
+ } else {
+ fState = kPreWarmTiming_State;
+ }
+ fTimer = WallTimer();
+ this->resetContext();
+ fCurrentFrame = 0;
+ } else {
+ fCurrentFrame++;
+ }
+ break;
+ }
+ }
+
+ // Invalidate the window to force a redraw. Poor man's animation mechanism.
+ this->inval(NULL);
+}
+
+void VisualBench::onSizeChange() {
+ this->setupRenderTarget();
+}
+
+bool VisualBench::onHandleChar(SkUnichar unichar) {
+ return true;
+}
+
+// Externally declared entry points
+void application_init() {
+ SkGraphics::Init();
+ SkEvent::Init();
+}
+
+void application_term() {
+ SkEvent::Term();
+ SkGraphics::Term();
+}
+
+SkOSWindow* create_sk_window(void* hwnd, int argc, char** argv) {
+ return new VisualBench(hwnd, argc, argv);
+}
+
diff --git a/tools/VisualBench/VisualBench.h b/tools/VisualBench/VisualBench.h
new file mode 100644
index 0000000000..332fe82eb4
--- /dev/null
+++ b/tools/VisualBench/VisualBench.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ */
+
+#ifndef VisualBench_DEFINED
+#define VisualBench_DEFINED
+
+#include "SkWindow.h"
+
+#include "SkPicture.h"
+#include "SkString.h"
+#include "SkSurface.h"
+#include "Timer.h"
+#include "gl/SkGLContext.h"
+
+class GrContext;
+struct GrGLInterface;
+class GrRenderTarget;
+class SkCanvas;
+
+/*
+ * A Visual benchmarking tool for gpu benchmarking
+ */
+class VisualBench : public SkOSWindow {
+public:
+ VisualBench(void* hwnd, int argc, char** argv);
+ ~VisualBench() override;
+
+protected:
+ SkSurface* createSurface() override;
+
+ void draw(SkCanvas* canvas) override;
+
+ void onSizeChange() override;
+
+private:
+ void setTitle();
+ bool setupBackend();
+ void resetContext();
+ void setupRenderTarget();
+ bool onHandleChar(SkUnichar unichar) override;
+ void printStats();
+ bool loadPicture();
+ bool advanceRecordIfNecessary();
+ inline void renderFrame(SkCanvas*);
+
+ struct Record {
+ SkString fFilename;
+ SkTArray<double> fMeasurements;
+ };
+
+ enum State {
+ kPreWarmLoops_State,
+ kTuneLoops_State,
+ kPreWarmTiming_State,
+ kTiming_State,
+ };
+ void preWarm(State nextState);
+
+ int fCurrentPictureIdx;
+ SkAutoTUnref<SkPicture> fPicture;
+ int fCurrentSample;
+ int fCurrentFrame;
+ int fFlushes;
+ int fLoops;
+ SkTArray<Record> fRecords;
+ WallTimer fTimer;
+ State fState;
+
+ // support framework
+ SkAutoTUnref<SkSurface> fSurface;
+ SkAutoTUnref<GrContext> fContext;
+ SkAutoTUnref<GrRenderTarget> fRenderTarget;
+ AttachmentInfo fAttachmentInfo;
+ SkAutoTUnref<const GrGLInterface> fInterface;
+
+ typedef SkOSWindow INHERITED;
+};
+
+#endif