From 91b6f32e07aa479fc634bfc6fc88fe949b933236 Mon Sep 17 00:00:00 2001 From: joshualitt Date: Tue, 30 Jun 2015 06:50:10 -0700 Subject: Move visualbench to its own folder BUG=skia: Review URL: https://codereview.chromium.org/1216973002 --- tools/VisualBench/VisualBench.cpp | 302 ++++++++++++++++++++++++++++++++++++++ tools/VisualBench/VisualBench.h | 84 +++++++++++ 2 files changed, 386 insertions(+) create mode 100644 tools/VisualBench/VisualBench.cpp create mode 100644 tools/VisualBench/VisualBench.h (limited to 'tools/VisualBench') 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& 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 fMeasurements; + }; + + enum State { + kPreWarmLoops_State, + kTuneLoops_State, + kPreWarmTiming_State, + kTiming_State, + }; + void preWarm(State nextState); + + int fCurrentPictureIdx; + SkAutoTUnref fPicture; + int fCurrentSample; + int fCurrentFrame; + int fFlushes; + int fLoops; + SkTArray fRecords; + WallTimer fTimer; + State fState; + + // support framework + SkAutoTUnref fSurface; + SkAutoTUnref fContext; + SkAutoTUnref fRenderTarget; + AttachmentInfo fAttachmentInfo; + SkAutoTUnref fInterface; + + typedef SkOSWindow INHERITED; +}; + +#endif -- cgit v1.2.3