Report data from bench_pictures in the same fashion as bench.

Move SkBenchLogger into separate files and make bench_pictures use it.
Remove sk_tools::print_msg, since SkBenchLogger is now used instead.

Combine picture_benchmark with bench_pictures, since that is the
only project that uses it.

Refactor the aggregator for bench timer data into its own class and
make bench_pictures use it.

Consolidate the various virtual PictureBenchmark::run functions
into one for reuse.

BUG=https://code.google.com/p/skia/issues/detail?id=822

Review URL: https://codereview.appspot.com/6488086

git-svn-id: http://skia.googlecode.com/svn/trunk@5432 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
scroggo@google.com 2012-09-07 15:21:18 +00:00
parent 2d8edaf175
commit 9a4125283a
15 changed files with 515 additions and 467 deletions

30
bench/SkBenchLogger.cpp Normal file
View File

@ -0,0 +1,30 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkBenchLogger.h"
#include "SkStream.h"
SkBenchLogger::SkBenchLogger()
: fFileStream(NULL) {}
SkBenchLogger::~SkBenchLogger() {
if (fFileStream) {
SkDELETE(fFileStream);
}
}
bool SkBenchLogger::SetLogFile(const char *file) {
fFileStream = SkNEW_ARGS(SkFILEWStream, (file));
return fFileStream->isValid();
}
void SkBenchLogger::fileWrite(const char msg[], size_t size) {
if (fFileStream && fFileStream->isValid()) {
fFileStream->write(msg, size);
}
}

77
bench/SkBenchLogger.h Normal file
View File

@ -0,0 +1,77 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkBenchLogger_DEFINED
#define SkBenchLogger_DEFINED
#include "SkTypes.h"
#include "SkString.h"
class SkFILEWStream;
/**
* Class that allows logging to a file while simultaneously logging to stdout/stderr.
*/
class SkBenchLogger {
public:
SkBenchLogger();
/**
* Not virtual, since this class is not intended to be subclassed.
*/
~SkBenchLogger();
/**
* Specify a file to write progress logs to. Unless this is called with a valid file path,
* SkBenchLogger will only write to stdout/stderr.
*/
bool SetLogFile(const char file[]);
/**
* Log an error to stderr, taking a C style string as input.
*/
void logError(const char msg[]) { this->nativeLogError(msg); }
/**
* Log an error to stderr, taking an SkString as input.
*/
void logError(const SkString& str) { this->nativeLogError(str.c_str()); }
/**
* Log the progress of the bench tool to both stdout and the log file specified by SetLogFile,
* if any, taking a C style string as input.
*/
void logProgress(const char msg[]) {
this->nativeLogProgress(msg);
this->fileWrite(msg, strlen(msg));
}
/**
* Log the progress of the bench tool to both stdout and the log file specified by SetLogFile,
* if any, taking an SkString as input.
*/
void logProgress(const SkString& str) {
this->nativeLogProgress(str.c_str());
this->fileWrite(str.c_str(), str.size());
}
private:
#ifdef SK_BUILD_FOR_ANDROID
void nativeLogError(const char msg[]) { SkDebugf("%s", msg); }
void nativeLogProgress(const char msg[]) { SkDebugf("%s", msg); }
#else
void nativeLogError(const char msg[]) { fprintf(stderr, "%s", msg); }
void nativeLogProgress(const char msg[]) { printf("%s", msg); }
#endif
void fileWrite(const char msg[], size_t size);
SkFILEWStream* fFileStream;
};
#endif // SkBenchLogger_DEFINED

108
bench/TimerData.cpp Normal file
View File

@ -0,0 +1,108 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "TimerData.h"
#include "BenchTimer.h"
#include <limits>
using namespace std;
TimerData::TimerData(const SkString& perIterTimeFormat, const SkString& normalTimeFormat)
: fWallStr(" msecs = ")
, fTruncatedWallStr(" Wmsecs = ")
, fCpuStr(" cmsecs = ")
, fTruncatedCpuStr(" Cmsecs = ")
, fGpuStr(" gmsecs = ")
, fWallSum(0.0)
, fWallMin(numeric_limits<double>::max())
, fTruncatedWallSum(0.0)
, fTruncatedWallMin(numeric_limits<double>::max())
, fCpuSum(0.0)
, fCpuMin(numeric_limits<double>::max())
, fTruncatedCpuSum(0.0)
, fTruncatedCpuMin(numeric_limits<double>::max())
, fGpuSum(0.0)
, fGpuMin(numeric_limits<double>::max())
, fPerIterTimeFormat(perIterTimeFormat)
, fNormalTimeFormat(normalTimeFormat)
{}
static double Min(double a, double b) {
return (a < b) ? a : b;
}
void TimerData::appendTimes(BenchTimer* timer, bool last) {
SkASSERT(timer != NULL);
SkString formatString(fPerIterTimeFormat);
if (!last) {
formatString.append(",");
}
const char* format = formatString.c_str();
fWallStr.appendf(format, timer->fWall);
fCpuStr.appendf(format, timer->fCpu);
fTruncatedWallStr.appendf(format, timer->fTruncatedWall);
fTruncatedCpuStr.appendf(format, timer->fTruncatedCpu);
fGpuStr.appendf(format, timer->fGpu);
// Store the minimum values. We do not need to special case the first time since we initialized
// to max double.
fWallMin = Min(fWallMin, timer->fWall);
fCpuMin = Min(fCpuMin, timer->fCpu);
fTruncatedWallMin = Min(fTruncatedWallMin, timer->fTruncatedWall);
fTruncatedCpuMin = Min(fTruncatedCpuMin, timer->fTruncatedCpu);
fGpuMin = Min(fGpuMin, timer->fGpu);
// Tally the sum of each timer type.
fWallSum += timer->fWall;
fCpuSum += timer->fCpu;
fTruncatedWallSum += timer->fTruncatedWall;
fTruncatedCpuSum += timer->fTruncatedCpu;
fGpuSum += timer->fGpu;
}
SkString TimerData::getResult(bool logPerIter, bool printMin, int repeatDraw,
const char *configName, bool showWallTime, bool showTruncatedWallTime,
bool showCpuTime, bool showTruncatedCpuTime, bool showGpuTime) {
// output each repeat (no average) if logPerIter is set,
// otherwise output only the average
if (!logPerIter) {
const char* format = fNormalTimeFormat.c_str();
fWallStr.set(" msecs = ");
fWallStr.appendf(format, printMin ? fWallMin : fWallSum / repeatDraw);
fCpuStr.set(" cmsecs = ");
fCpuStr.appendf(format, printMin ? fCpuMin : fCpuSum / repeatDraw);
fTruncatedWallStr.set(" Wmsecs = ");
fTruncatedWallStr.appendf(format,
printMin ? fTruncatedWallMin : fTruncatedWallSum / repeatDraw);
fTruncatedCpuStr.set(" Cmsecs = ");
fTruncatedCpuStr.appendf(format,
printMin ? fTruncatedCpuMin : fTruncatedCpuSum / repeatDraw);
fGpuStr.set(" gmsecs = ");
fGpuStr.appendf(format, printMin ? fGpuMin : fGpuSum / repeatDraw);
}
SkString str;
str.printf(" %4s:", configName);
if (showWallTime) {
str += fWallStr;
}
if (showTruncatedWallTime) {
str += fTruncatedWallStr;
}
if (showCpuTime) {
str += fCpuStr;
}
if (showTruncatedCpuTime) {
str += fTruncatedCpuStr;
}
if (showGpuTime && fGpuSum > 0) {
str += fGpuStr;
}
return str;
}

46
bench/TimerData.h Normal file
View File

@ -0,0 +1,46 @@
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef TimerData_DEFINED
#define TimerData_DEFINED
#include "SkString.h"
class BenchTimer;
class TimerData {
public:
TimerData(const SkString& perIterTimeFormat, const SkString& normalTimeFormat);
/**
* Append the value from each timer in BenchTimer to our various strings, and update the
* minimum and sum times.
* @param BenchTimer Must not be null.
* @param last True if this is the last set of times to add.
*/
void appendTimes(BenchTimer*, bool last);
SkString getResult(bool logPerIter, bool printMin, int repeatDraw, const char* configName,
bool showWallTime, bool showTruncatedWallTime, bool showCpuTime,
bool showTruncatedCpuTime, bool showGpuTime);
private:
SkString fWallStr;
SkString fTruncatedWallStr;
SkString fCpuStr;
SkString fTruncatedCpuStr;
SkString fGpuStr;
double fWallSum, fWallMin;
double fTruncatedWallSum, fTruncatedWallMin;
double fCpuSum, fCpuMin;
double fTruncatedCpuSum, fTruncatedCpuMin;
double fGpuSum, fGpuMin;
SkString fPerIterTimeFormat;
SkString fNormalTimeFormat;
};
#endif // TimerData_DEFINED

View File

@ -21,6 +21,7 @@
#include "SkGpuDevice.h"
#endif // SK_SUPPORT_GPU
#include "SkBenchLogger.h"
#include "SkBenchmark.h"
#include "SkCanvas.h"
#include "SkDeferredCanvas.h"
@ -30,56 +31,8 @@
#include "SkImageEncoder.h"
#include "SkNWayCanvas.h"
#include "SkPicture.h"
#include "SkStream.h"
#include "SkString.h"
template <typename T> const T& Min(const T& a, const T& b) {
return (a < b) ? a : b;
}
class SkBenchLogger {
public:
SkBenchLogger() : fFileStream(NULL) {}
~SkBenchLogger() {
if (fFileStream)
SkDELETE(fFileStream);
}
bool SetLogFile(const char file[]) {
fFileStream = SkNEW_ARGS(SkFILEWStream, (file));
return fFileStream->isValid();
}
void logError(const char msg[]) { nativeLogError(msg); }
void logError(const SkString& str) { nativeLogError(str.c_str()); }
void logProgress(const char msg[]) {
nativeLogProgress(msg);
fileWrite(msg, strlen(msg));
}
void logProgress(const SkString& str) {
nativeLogProgress(str.c_str());
fileWrite(str.c_str(), str.size());
}
private:
#ifdef SK_BUILD_FOR_ANDROID
void nativeLogError(const char msg[]) { SkDebugf("%s", msg); }
void nativeLogProgress(const char msg[]) { SkDebugf("%s", msg); }
#else
void nativeLogError(const char msg[]) { fprintf(stderr, "%s", msg); }
void nativeLogProgress(const char msg[]) { printf("%s", msg); }
#endif
void fileWrite(const char msg[], size_t size) {
if (fFileStream && fFileStream->isValid())
fFileStream->write(msg, size);
}
SkFILEWStream* fFileStream;
} logger;
///////////////////////////////////////////////////////////////////////////////
#include "TimerData.h"
enum benchModes {
kNormal_benchModes,
@ -400,7 +353,7 @@ static void help() {
" [-scale] [-clip] [-min] [-forceAA 1|0] [-forceFilter 1|0]\n"
" [-forceDither 1|0] [-forceBlend 1|0] [-strokeWidth width]\n"
" [-match name] [-mode normal|deferred|record|picturerecord]\n"
" [-config 8888|565|GPU|ANGLE|NULLGPU] [-Dfoo bar]\n"
" [-config 8888|565|GPU|ANGLE|NULLGPU] [-Dfoo bar] [-logFile filename]\n"
" [-h|--help]");
SkDebugf("\n\n");
SkDebugf(" -o outDir : Image of each bench will be put in outDir.\n");
@ -429,7 +382,7 @@ static void help() {
" record, Benchmark the time to record to an SkPicture;\n"
" picturerecord, Benchmark the time to do record from a \n"
" SkPicture to a SkPicture.\n");
SkDebugf(" -logFile : destination for writing log output, in addition to stdout.\n");
SkDebugf(" -logFile filename : destination for writing log output, in addition to stdout.\n");
#if SK_SUPPORT_GPU
SkDebugf(" -config 8888|565|GPU|ANGLE|NULLGPU : "
"Run bench in corresponding config mode.\n");
@ -478,6 +431,8 @@ int main (int argc, char * const argv[]) {
SkTDArray<int> configs;
bool userConfig = false;
SkBenchLogger logger;
char* const* stop = argv + argc;
for (++argv; argv < stop; ++argv) {
if (strcmp(*argv, "-o") == 0) {
@ -634,6 +589,7 @@ int main (int argc, char * const argv[]) {
if (!logger.SetLogFile(*argv)) {
SkString str;
str.printf("Could not open %s for writing.", *argv);
logger.logError(str);
return -1;
}
} else {
@ -864,16 +820,7 @@ int main (int argc, char * const argv[]) {
}
// record timer values for each repeat, and their sum
SkString fWallStr(" msecs = ");
SkString fTruncatedWallStr(" Wmsecs = ");
SkString fCpuStr(" cmsecs = ");
SkString fTruncatedCpuStr(" Cmsecs = ");
SkString fGpuStr(" gmsecs = ");
double fWallSum = 0.0, fWallMin;
double fTruncatedWallSum = 0.0, fTruncatedWallMin;
double fCpuSum = 0.0, fCpuMin;
double fTruncatedCpuSum = 0.0, fTruncatedCpuMin;
double fGpuSum = 0.0, fGpuMin;
TimerData timerData(perIterTimeformat, normalTimeFormat);
for (int i = 0; i < repeatDraw; i++) {
if ((benchMode == kRecord_benchModes
|| benchMode == kPictureRecord_benchModes)) {
@ -904,84 +851,14 @@ int main (int argc, char * const argv[]) {
// have completed
timer.end();
if (i == repeatDraw - 1) {
// no comma after the last value
fWallStr.appendf(perIterTimeformat.c_str(), timer.fWall);
fCpuStr.appendf(perIterTimeformat.c_str(), timer.fCpu);
fTruncatedWallStr.appendf(perIterTimeformat.c_str(), timer.fTruncatedWall);
fTruncatedCpuStr.appendf(perIterTimeformat.c_str(), timer.fTruncatedCpu);
fGpuStr.appendf(perIterTimeformat.c_str(), timer.fGpu);
} else {
fWallStr.appendf(perIterTimeformat.c_str(), timer.fWall);
fWallStr.appendf(",");
fCpuStr.appendf(perIterTimeformat.c_str(), timer.fCpu);
fCpuStr.appendf(",");
fTruncatedWallStr.appendf(perIterTimeformat.c_str(), timer.fTruncatedWall);
fTruncatedWallStr.appendf(",");
fTruncatedCpuStr.appendf(perIterTimeformat.c_str(), timer.fTruncatedCpu);
fTruncatedCpuStr.appendf(",");
fGpuStr.appendf(perIterTimeformat.c_str(), timer.fGpu);
fGpuStr.appendf(",");
}
timerData.appendTimes(&timer, repeatDraw - 1 == i);
if (0 == i) {
fWallMin = timer.fWall;
fCpuMin = timer.fCpu;
fTruncatedWallMin = timer.fTruncatedWall;
fTruncatedCpuMin = timer.fTruncatedCpu;
fGpuMin = timer.fGpu;
} else {
fWallMin = Min(fWallMin, timer.fWall);
fCpuMin = Min(fCpuMin, timer.fCpu);
fTruncatedWallMin = Min(fTruncatedWallMin, timer.fTruncatedWall);
fTruncatedCpuMin = Min(fTruncatedCpuMin, timer.fTruncatedCpu);
fGpuMin = Min(fGpuMin, timer.fGpu);
}
fWallSum += timer.fWall;
fCpuSum += timer.fCpu;
fTruncatedWallSum += timer.fTruncatedWall;
fTruncatedCpuSum += timer.fTruncatedCpu;
fGpuSum += timer.fGpu;
}
if (repeatDraw > 1) {
// output each repeat (no average) if logPerIter is set,
// otherwise output only the average
if (!logPerIter) {
fWallStr.set(" msecs = ");
fWallStr.appendf(normalTimeFormat.c_str(),
printMin ? fWallMin : fWallSum / repeatDraw);
fCpuStr.set(" cmsecs = ");
fCpuStr.appendf(normalTimeFormat.c_str(),
printMin ? fCpuMin : fCpuSum / repeatDraw);
fTruncatedWallStr.set(" Wmsecs = ");
fTruncatedWallStr.appendf(normalTimeFormat.c_str(),
printMin ? fTruncatedWallMin : fTruncatedWallSum / repeatDraw);
fTruncatedCpuStr.set(" Cmsecs = ");
fTruncatedCpuStr.appendf(normalTimeFormat.c_str(),
printMin ? fTruncatedCpuMin : fTruncatedCpuSum / repeatDraw);
fGpuStr.set(" gmsecs = ");
fGpuStr.appendf(normalTimeFormat.c_str(),
printMin ? fGpuMin : fGpuSum / repeatDraw);
}
SkString str;
str.printf(" %4s:", configName);
if (timerWall) {
str += fWallStr;
}
if (truncatedTimerWall) {
str += fTruncatedWallStr;
}
if (timerCpu) {
str += fCpuStr;
}
if (truncatedTimerCpu) {
str += fTruncatedCpuStr;
}
if (timerGpu && glHelper && fGpuSum > 0) {
str += fGpuStr;
}
logger.logProgress(str);
SkString result = timerData.getResult(logPerIter, printMin, repeatDraw, configName,
timerWall, truncatedTimerWall, timerCpu,
truncatedTimerCpu, timerGpu && glHelper);
logger.logProgress(result);
}
if (outDir.size() > 0) {
saveFile(bench->getName(), configName, outDir.c_str(),

View File

@ -38,6 +38,11 @@
'../bench/TextBench.cpp',
'../bench/VertBench.cpp',
'../bench/WriterBench.cpp',
'../bench/SkBenchLogger.h',
'../bench/SkBenchLogger.cpp',
'../bench/TimerData.h',
'../bench/TimerData.cpp',
],
}

View File

@ -85,7 +85,12 @@
'target_name': 'bench_pictures',
'type': 'executable',
'sources': [
'../bench/SkBenchLogger.h',
'../bench/SkBenchLogger.cpp',
'../bench/TimerData.h',
'../bench/TimerData.cpp',
'../tools/bench_pictures_main.cpp',
'../tools/PictureBenchmark.cpp',
],
'include_dirs': [
'../bench',
@ -95,27 +100,9 @@
'effects.gyp:effects',
'ports.gyp:ports',
'tools.gyp:picture_utils',
'tools.gyp:picture_benchmark',
],
},
{
'target_name': 'picture_benchmark',
'type': 'static_library',
'sources': [
'../tools/PictureBenchmark.cpp',
],
'include_dirs': [
'../bench',
],
'dependencies': [
'core.gyp:core',
'tools.gyp:picture_utils',
'tools.gyp:picture_renderer',
'bench.gyp:bench_timer',
],
'export_dependent_settings': [
'tools.gyp:picture_renderer',
]
],
},
{
'target_name': 'picture_renderer',

View File

@ -6,12 +6,14 @@
*/
#include "SkTypes.h"
#include "SkBenchLogger.h"
#include "BenchTimer.h"
#include "PictureBenchmark.h"
#include "SkCanvas.h"
#include "SkPicture.h"
#include "SkString.h"
#include "picture_utils.h"
#include "TimerData.h"
namespace sk_tools {
@ -29,224 +31,67 @@ BenchTimer* PictureBenchmark::setupTimer() {
#endif
}
void PipePictureBenchmark::run(SkPicture* pict) {
void PictureBenchmark::logProgress(const char msg[]) {
if (fLogger != NULL) {
fLogger->logProgress(msg);
}
}
void PictureBenchmark::run(SkPicture* pict) {
SkASSERT(pict);
if (NULL == pict) {
return;
}
fRenderer.init(pict);
PictureRenderer* renderer = this->getRenderer();
SkASSERT(renderer != NULL);
if (NULL == renderer) {
return;
}
renderer->init(pict);
// We throw this away to remove first time effects (such as paging in this
// program)
fRenderer.render();
fRenderer.resetState();
renderer->setup();
renderer->render(false);
renderer->resetState();
BenchTimer* timer = this->setupTimer();
double wall_time = 0, truncated_wall_time = 0;
bool usingGpu = false;
#if SK_SUPPORT_GPU
double gpu_time = 0;
usingGpu = renderer->isUsingGpuDevice();
#endif
TimerData timerData(renderer->getPerIterTimeFormat(), renderer->getNormalTimeFormat());
for (int i = 0; i < fRepeats; ++i) {
timer->start();
fRenderer.render();
timer->end();
fRenderer.resetState();
wall_time += timer->fWall;
truncated_wall_time += timer->fTruncatedWall;
#if SK_SUPPORT_GPU
if (fRenderer.isUsingGpuDevice()) {
gpu_time += timer->fGpu;
}
#endif
}
SkString result;
result.printf("pipe: msecs = %6.2f", wall_time / fRepeats);
#if SK_SUPPORT_GPU
if (fRenderer.isUsingGpuDevice()) {
result.appendf(" gmsecs = %6.2f", gpu_time / fRepeats);
}
#endif
result.appendf("\n");
sk_tools::print_msg(result.c_str());
fRenderer.end();
SkDELETE(timer);
}
void RecordPictureBenchmark::run(SkPicture* pict) {
SkASSERT(pict);
if (NULL == pict) {
return;
}
BenchTimer* timer = setupTimer();
double wall_time = 0, truncated_wall_time = 0;
for (int i = 0; i < fRepeats + 1; ++i) {
SkPicture replayer;
renderer->setup();
timer->start();
SkCanvas* recorder = replayer.beginRecording(pict->width(), pict->height());
pict->draw(recorder);
replayer.endRecording();
renderer->render(false);
timer->truncatedEnd();
// Finishes gl context
renderer->resetState();
timer->end();
// We want to ignore first time effects
if (i > 0) {
wall_time += timer->fWall;
truncated_wall_time += timer->fTruncatedWall;
}
timerData.appendTimes(timer, fRepeats - 1 == i);
}
SkString result;
result.printf("record: msecs = %6.5f\n", wall_time / fRepeats);
sk_tools::print_msg(result.c_str());
SkDELETE(timer);
}
void SimplePictureBenchmark::run(SkPicture* pict) {
SkASSERT(pict);
if (NULL == pict) {
return;
}
fRenderer.init(pict);
// We throw this away to remove first time effects (such as paging in this
// program)
fRenderer.render();
fRenderer.resetState();
BenchTimer* timer = this->setupTimer();
double wall_time = 0, truncated_wall_time = 0;
#if SK_SUPPORT_GPU
double gpu_time = 0;
#endif
for (int i = 0; i < fRepeats; ++i) {
timer->start();
fRenderer.render();
timer->end();
fRenderer.resetState();
wall_time += timer->fWall;
truncated_wall_time += timer->fTruncatedWall;
#if SK_SUPPORT_GPU
if (fRenderer.isUsingGpuDevice()) {
gpu_time += timer->fGpu;
}
#endif
}
SkString result;
result.printf("simple: msecs = %6.2f", wall_time / fRepeats);
#if SK_SUPPORT_GPU
if (fRenderer.isUsingGpuDevice()) {
result.appendf(" gmsecs = %6.2f", gpu_time / fRepeats);
}
#endif
result.appendf("\n");
sk_tools::print_msg(result.c_str());
fRenderer.end();
SkDELETE(timer);
}
void TiledPictureBenchmark::run(SkPicture* pict) {
SkASSERT(pict);
if (NULL == pict) {
return;
}
fRenderer.init(pict);
// We throw this away to remove first time effects (such as paging in this
// program)
fRenderer.drawTiles();
fRenderer.resetState();
BenchTimer* timer = setupTimer();
double wall_time = 0, truncated_wall_time = 0;
#if SK_SUPPORT_GPU
double gpu_time = 0;
#endif
for (int i = 0; i < fRepeats; ++i) {
timer->start();
fRenderer.drawTiles();
timer->end();
fRenderer.resetState();
wall_time += timer->fWall;
truncated_wall_time += timer->fTruncatedWall;
#if SK_SUPPORT_GPU
if (fRenderer.isUsingGpuDevice()) {
gpu_time += timer->fGpu;
}
#endif
}
SkString result;
if (fRenderer.isMultiThreaded()) {
result.printf("multithreaded using %s ", (fRenderer.isUsePipe() ? "pipe" : "picture"));
}
if (fRenderer.getTileMinPowerOf2Width() > 0) {
result.appendf("%i_pow2tiles_%iminx%i: msecs = %6.2f", fRenderer.numTiles(),
fRenderer.getTileMinPowerOf2Width(), fRenderer.getTileHeight(),
wall_time / fRepeats);
} else {
result.appendf("%i_tiles_%ix%i: msecs = %6.2f", fRenderer.numTiles(),
fRenderer.getTileWidth(), fRenderer.getTileHeight(), wall_time / fRepeats);
}
#if SK_SUPPORT_GPU
if (fRenderer.isUsingGpuDevice()) {
result.appendf(" gmsecs = %6.2f", gpu_time / fRepeats);
}
#endif
result.appendf("\n");
sk_tools::print_msg(result.c_str());
fRenderer.end();
SkDELETE(timer);
}
void UnflattenPictureBenchmark::run(SkPicture* pict) {
SkASSERT(pict);
if (NULL == pict) {
return;
}
BenchTimer* timer = setupTimer();
double wall_time = 0, truncated_wall_time = 0;
for (int i = 0; i < fRepeats + 1; ++i) {
SkPicture replayer;
SkCanvas* recorder = replayer.beginRecording(pict->width(), pict->height());
recorder->drawPicture(*pict);
timer->start();
replayer.endRecording();
timer->end();
// We want to ignore first time effects
if (i > 0) {
wall_time += timer->fWall;
truncated_wall_time += timer->fTruncatedWall;
}
}
SkString result;
result.printf("unflatten: msecs = %6.4f\n", wall_time / fRepeats);
sk_tools::print_msg(result.c_str());
// FIXME: Pass these options on the command line.
bool logPerIter = false;
bool printMin = false;
const char* configName = usingGpu ? "gpu" : "raster";
bool showWallTime = true;
bool showTruncatedWallTime = false;
bool showCpuTime = false;
bool showTruncatedCpuTime = false;
SkString result = timerData.getResult(logPerIter, printMin, fRepeats,
configName, showWallTime, showTruncatedWallTime,
showCpuTime, showTruncatedCpuTime, usingGpu);
result.append("\n");
this->logProgress(result.c_str());
renderer->end();
SkDELETE(timer);
}

View File

@ -12,6 +12,7 @@
#include "PictureRenderer.h"
class BenchTimer;
class SkBenchLogger;
class SkPicture;
class SkString;
@ -19,16 +20,16 @@ namespace sk_tools {
class PictureBenchmark : public SkRefCnt {
public:
virtual void run(SkPicture* pict) = 0;
PictureBenchmark()
: fRepeats(1)
, fLogger(NULL) {}
void run(SkPicture* pict);
void setRepeats(int repeats) {
fRepeats = repeats;
}
int getRepeats() const {
return fRepeats;
}
void setDeviceType(PictureRenderer::SkDeviceTypes deviceType) {
sk_tools::PictureRenderer* renderer = getRenderer();
@ -37,54 +38,58 @@ public:
}
}
void setLogger(SkBenchLogger* logger) { fLogger = logger; }
private:
int fRepeats;
SkBenchLogger* fLogger;
void logProgress(const char msg[]);
virtual sk_tools::PictureRenderer* getRenderer() = 0;
BenchTimer* setupTimer();
protected:
int fRepeats;
private:
typedef SkRefCnt INHERITED;
virtual sk_tools::PictureRenderer* getRenderer() {
return NULL;
}
};
// TODO: Use just one PictureBenchmark with different renderers.
class PipePictureBenchmark : public PictureBenchmark {
public:
virtual void run(SkPicture* pict) SK_OVERRIDE;
private:
PipePictureRenderer fRenderer;
typedef PictureBenchmark INHERITED;
virtual sk_tools::PictureRenderer* getRenderer() SK_OVERRIDE {
return &fRenderer;
}
typedef PictureBenchmark INHERITED;
};
class RecordPictureBenchmark : public PictureBenchmark {
public:
virtual void run(SkPicture* pict) SK_OVERRIDE;
private:
RecordPictureRenderer fRenderer;
virtual sk_tools::PictureRenderer* getRenderer() SK_OVERRIDE {
return &fRenderer;
}
typedef PictureBenchmark INHERITED;
};
class SimplePictureBenchmark : public PictureBenchmark {
public:
virtual void run(SkPicture* pict) SK_OVERRIDE;
private:
SimplePictureRenderer fRenderer;
typedef PictureBenchmark INHERITED;
virtual sk_tools::PictureRenderer* getRenderer() SK_OVERRIDE {
return &fRenderer;
}
typedef PictureBenchmark INHERITED;
};
class TiledPictureBenchmark : public PictureBenchmark {
public:
virtual void run(SkPicture* pict) SK_OVERRIDE;
void setTileWidth(int width) {
fRenderer.setTileWidth(width);
}
@ -135,17 +140,22 @@ public:
private:
TiledPictureRenderer fRenderer;
typedef PictureBenchmark INHERITED;
virtual sk_tools::PictureRenderer* getRenderer() SK_OVERRIDE{
return &fRenderer;
}
typedef PictureBenchmark INHERITED;
};
class UnflattenPictureBenchmark : public PictureBenchmark {
public:
virtual void run(SkPicture* pict) SK_OVERRIDE;
class PlaybackCreationBenchmark : public PictureBenchmark {
private:
PlaybackCreationRenderer fRenderer;
virtual sk_tools::PictureRenderer* getRenderer() SK_OVERRIDE{
return &fRenderer;
}
typedef PictureBenchmark INHERITED;
};

View File

@ -83,23 +83,6 @@ void PictureRenderer::end() {
}
void PictureRenderer::resetState() {
#if SK_SUPPORT_GPU
if (this->isUsingGpuDevice()) {
SkGLContext* glContext = fGrContextFactory.getGLContext(
GrContextFactory::kNative_GLContextType);
SK_GL(*glContext, Finish());
}
#endif
}
void PictureRenderer::finishDraw() {
SkASSERT(fCanvas.get() != NULL);
if (NULL == fCanvas.get()) {
return;
}
fCanvas->flush();
#if SK_SUPPORT_GPU
if (this->isUsingGpuDevice()) {
SkGLContext* glContext = fGrContextFactory.getGLContext(
@ -110,6 +93,7 @@ void PictureRenderer::finishDraw() {
return;
}
fGrContext->flush();
SK_GL(*glContext, Finish());
}
#endif
@ -131,7 +115,14 @@ bool PictureRenderer::write(const SkString& path) const {
return SkImageEncoder::EncodeFile(path.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
}
void PipePictureRenderer::render() {
void RecordPictureRenderer::render(bool doExtraWorkToDrawToBaseCanvas) {
SkPicture replayer;
SkCanvas* recorder = replayer.beginRecording(fPicture->width(), fPicture->height());
fPicture->draw(recorder);
replayer.endRecording();
}
void PipePictureRenderer::render(bool doExtraWorkToDrawToBaseCanvas) {
SkASSERT(fCanvas.get() != NULL);
SkASSERT(fPicture != NULL);
if (NULL == fCanvas.get() || NULL == fPicture) {
@ -143,10 +134,10 @@ void PipePictureRenderer::render() {
SkCanvas* pipeCanvas = writer.startRecording(&pipeController);
pipeCanvas->drawPicture(*fPicture);
writer.endRecording();
this->finishDraw();
fCanvas->flush();
}
void SimplePictureRenderer::render() {
void SimplePictureRenderer::render(bool doExtraWorkToDrawToBaseCanvas) {
SkASSERT(fCanvas.get() != NULL);
SkASSERT(fPicture != NULL);
if (NULL == fCanvas.get() || NULL == fPicture) {
@ -154,7 +145,7 @@ void SimplePictureRenderer::render() {
}
fCanvas->drawPicture(*fPicture);
this->finishDraw();
fCanvas->flush();
}
TiledPictureRenderer::TiledPictureRenderer()
@ -189,7 +180,7 @@ void TiledPictureRenderer::init(SkPicture* pict) {
}
}
void TiledPictureRenderer::render() {
void TiledPictureRenderer::render(bool doExtraWorkToDrawToBaseCanvas) {
SkASSERT(fCanvas.get() != NULL);
SkASSERT(fPicture != NULL);
if (NULL == fCanvas.get() || NULL == fPicture) {
@ -197,8 +188,9 @@ void TiledPictureRenderer::render() {
}
this->drawTiles();
this->copyTilesToCanvas();
this->finishDraw();
if (doExtraWorkToDrawToBaseCanvas) {
this->copyTilesToCanvas();
}
}
void TiledPictureRenderer::end() {
@ -293,6 +285,7 @@ static void DrawTile(void* data) {
SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024);
TileData* tileData = static_cast<TileData*>(data);
tileData->fController->playback(tileData->fCanvas);
tileData->fCanvas->flush();
}
TileData::TileData(SkCanvas* canvas, ThreadSafePipeController* controller)
@ -314,6 +307,7 @@ static void DrawClonedTile(void* data) {
SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024);
CloneData* cloneData = static_cast<CloneData*>(data);
cloneData->fCanvas->drawPicture(*cloneData->fClone);
cloneData->fCanvas->flush();
}
CloneData::CloneData(SkCanvas* target, SkPicture* clone)
@ -367,30 +361,11 @@ void TiledPictureRenderer::drawTiles() {
} else {
for (int i = 0; i < fTiles.count(); ++i) {
fTiles[i]->drawPicture(*(fPicture));
fTiles[i]->flush();
}
}
}
void TiledPictureRenderer::finishDraw() {
for (int i = 0; i < fTiles.count(); ++i) {
fTiles[i]->flush();
}
#if SK_SUPPORT_GPU
if (this->isUsingGpuDevice()) {
SkGLContext* glContext = fGrContextFactory.getGLContext(
GrContextFactory::kNative_GLContextType);
SkASSERT(glContext != NULL);
if (NULL == glContext) {
return;
}
SK_GL(*glContext, Finish());
}
#endif
}
void TiledPictureRenderer::copyTilesToCanvas() {
for (int i = 0; i < fTiles.count(); ++i) {
// Since SkPicture performs a save and restore when being drawn to a
@ -404,6 +379,16 @@ void TiledPictureRenderer::copyTilesToCanvas() {
fCanvas->drawBitmap(source, -tile_x_start, -tile_y_start);
}
fCanvas->flush();
}
void PlaybackCreationRenderer::setup() {
SkCanvas* recorder = fReplayer.beginRecording(fPicture->width(), fPicture->height());
fPicture->draw(recorder);
}
void PlaybackCreationRenderer::render(bool doExtraWorkToDrawToBaseCanvas) {
fReplayer.endRecording();
}
}

View File

@ -8,9 +8,11 @@
#ifndef PictureRenderer_DEFINED
#define PictureRenderer_DEFINED
#include "SkMath.h"
#include "SkPicture.h"
#include "SkTypes.h"
#include "SkTDArray.h"
#include "SkRefCnt.h"
#include "SkString.h"
#if SK_SUPPORT_GPU
#include "GrContextFactory.h"
@ -20,8 +22,6 @@
class SkBitmap;
class SkCanvas;
class SkGLContext;
class SkPicture;
class SkString;
namespace sk_tools {
@ -35,7 +35,23 @@ public:
};
virtual void init(SkPicture* pict);
virtual void render() = 0;
/**
* Perform any setup that should done prior to each iteration of render() which should not be
* timed.
*/
virtual void setup() {}
/**
* Perform work that is to be timed. Typically this is rendering, but is also used for recording
* and preparing picture for playback by the subclasses which do those.
* @param doExtraWorkToDrawToBaseCanvas Perform extra work to draw to fCanvas. Some subclasses
* will automatically draw to fCanvas, but in the tiled
* case, for example, true needs to be passed so that
* the tiles will be stitched together on fCanvas.
*/
virtual void render(bool doExtraWorkToDrawToBaseCanvas) = 0;
virtual void end();
void resetState();
@ -47,6 +63,10 @@ public:
return kBitmap_DeviceType == fDeviceType;
}
virtual SkString getPerIterTimeFormat() { return SkString("%.2f"); }
virtual SkString getNormalTimeFormat() { return SkString("%6.2f"); }
#if SK_SUPPORT_GPU
bool isUsingGpuDevice() {
return kGPU_DeviceType == fDeviceType;
@ -72,7 +92,6 @@ public:
bool write(const SkString& path) const;
protected:
virtual void finishDraw();
SkCanvas* setupCanvas();
SkCanvas* setupCanvas(int width, int height);
@ -89,9 +108,21 @@ private:
typedef SkRefCnt INHERITED;
};
/**
* This class does not do any rendering, but its render function executes recording, which we want
* to time.
*/
class RecordPictureRenderer : public PictureRenderer {
virtual void render(bool doExtraWorkToDrawToBaseCanvas) SK_OVERRIDE;
virtual SkString getPerIterTimeFormat() SK_OVERRIDE { return SkString("%.4f"); }
virtual SkString getNormalTimeFormat() SK_OVERRIDE { return SkString("%6.4f"); }
};
class PipePictureRenderer : public PictureRenderer {
public:
virtual void render() SK_OVERRIDE;
virtual void render(bool doExtraWorkToDrawToBaseCanvas) SK_OVERRIDE;
private:
typedef PictureRenderer INHERITED;
@ -99,7 +130,7 @@ private:
class SimplePictureRenderer : public PictureRenderer {
public:
virtual void render () SK_OVERRIDE;
virtual void render(bool doExtraWorkToDrawToBaseCanvas) SK_OVERRIDE;
private:
typedef PictureRenderer INHERITED;
@ -110,7 +141,7 @@ public:
TiledPictureRenderer();
virtual void init(SkPicture* pict) SK_OVERRIDE;
virtual void render() SK_OVERRIDE;
virtual void render(bool doExtraWorkToDrawToBaseCanvas) SK_OVERRIDE;
virtual void end() SK_OVERRIDE;
void drawTiles();
@ -181,9 +212,6 @@ public:
~TiledPictureRenderer();
protected:
virtual void finishDraw();
private:
bool fMultiThreaded;
bool fUsePipe;
@ -209,6 +237,25 @@ private:
typedef PictureRenderer INHERITED;
};
/**
* This class does not do any rendering, but its render function executes turning an SkPictureRecord
* into an SkPicturePlayback, which we want to time.
*/
class PlaybackCreationRenderer : public PictureRenderer {
public:
virtual void setup() SK_OVERRIDE;
virtual void render(bool doExtraWorkToDrawToBaseCanvas) SK_OVERRIDE;
virtual SkString getPerIterTimeFormat() SK_OVERRIDE { return SkString("%.4f"); }
virtual SkString getNormalTimeFormat() SK_OVERRIDE { return SkString("%6.4f"); }
private:
SkPicture fReplayer;
typedef PictureRenderer INHERITED;
};
}
#endif // PictureRenderer_DEFINED

View File

@ -7,6 +7,7 @@
#include "BenchTimer.h"
#include "PictureBenchmark.h"
#include "SkBenchLogger.h"
#include "SkCanvas.h"
#include "SkMath.h"
#include "SkOSFile.h"
@ -22,9 +23,10 @@ static void usage(const char* argv0) {
SkDebugf("\n"
"Usage: \n"
" %s <inputDir>...\n"
" [--logFile filename]\n"
" [--repeat] \n"
" [--mode pow2tile minWidth height[] (multi) | record | simple\n"
" | tile width[] height[] (multi) | unflatten]\n"
" | tile width[] height[] (multi) | playbackCreation]\n"
" [--pipe]\n"
" [--device bitmap"
#if SK_SUPPORT_GPU
@ -35,10 +37,11 @@ static void usage(const char* argv0) {
SkDebugf("\n\n");
SkDebugf(
" inputDir: A list of directories and files to use as input. Files are\n"
" expected to have the .skp extension.\n\n");
" expected to have the .skp extension.\n\n"
" --logFile filename : destination for writing log output, in addition to stdout.\n");
SkDebugf(
" --mode pow2tile minWidht height[] (multi) | record | simple\n"
" | tile width[] height[] (multi) | unflatten:\n"
" | tile width[] height[] (multi) | playbackCreation:\n"
" Run in the corresponding mode.\n"
" Default is simple.\n");
SkDebugf(
@ -63,7 +66,7 @@ static void usage(const char* argv0) {
" Append \"multi\" for multithreaded\n"
" drawing.\n");
SkDebugf(
" unflatten, Benchmark picture unflattening.\n");
" playbackCreation, Benchmark creation of the SkPicturePlayback.\n");
SkDebugf("\n");
SkDebugf(
" --pipe: Benchmark SkGPipe rendering. Compatible with tiled, multithreaded rendering.\n");
@ -86,13 +89,17 @@ static void usage(const char* argv0) {
" Default is %i.\n", DEFAULT_REPEATS);
}
SkBenchLogger gLogger;
static void run_single_benchmark(const SkString& inputPath,
sk_tools::PictureBenchmark& benchmark) {
SkFILEStream inputStream;
inputStream.setPath(inputPath.c_str());
if (!inputStream.isValid()) {
SkDebugf("Could not open file %s\n", inputPath.c_str());
SkString err;
err.printf("Could not open file %s\n", inputPath.c_str());
gLogger.logError(err);
return;
}
@ -104,7 +111,7 @@ static void run_single_benchmark(const SkString& inputPath,
SkString result;
result.printf("running bench [%i %i] %s ", picture.width(), picture.height(),
filename.c_str());
sk_tools::print_msg(result.c_str());
gLogger.logProgress(result);
benchmark.run(&picture);
}
@ -118,6 +125,14 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>*
sk_tools::PictureRenderer::SkDeviceTypes deviceType =
sk_tools::PictureRenderer::kBitmap_DeviceType;
// Create a string to show our current settings.
// TODO: Make it prettier. Currently it just repeats the command line.
SkString commandLine("bench_pictures:");
for (int i = 1; i < argc; i++) {
commandLine.appendf(" %s", *(argv+i));
}
commandLine.append("\n");
bool usePipe = false;
bool multiThreaded = false;
bool useTiles = false;
@ -132,23 +147,38 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>*
repeats = atoi(*argv);
if (repeats < 1) {
SkDELETE(benchmark);
SkDebugf("--repeat must be given a value > 0\n");
gLogger.logError("--repeat must be given a value > 0\n");
exit(-1);
}
} else {
SkDELETE(benchmark);
SkDebugf("Missing arg for --repeat\n");
gLogger.logError("Missing arg for --repeat\n");
usage(argv0);
exit(-1);
}
} else if (0 == strcmp(*argv, "--pipe")) {
usePipe = true;
} else if (0 == strcmp(*argv, "--logFile")) {
argv++;
if (argv < stop) {
if (!gLogger.SetLogFile(*argv)) {
SkString str;
str.printf("Could not open %s for writing.", *argv);
gLogger.logError(str);
usage(argv0);
exit(-1);
}
} else {
gLogger.logError("Missing arg for --logFile\n");
usage(argv0);
exit(-1);
}
} else if (0 == strcmp(*argv, "--mode")) {
SkDELETE(benchmark);
++argv;
if (argv >= stop) {
SkDebugf("Missing mode for --mode\n");
gLogger.logError("Missing mode for --mode\n");
usage(argv0);
exit(-1);
}
@ -167,7 +197,9 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>*
++argv;
if (argv >= stop) {
SkDebugf("Missing width for --mode %s\n", mode);
SkString err;
err.printf("Missing width for --mode %s\n", mode);
gLogger.logError(err);
usage(argv0);
exit(-1);
}
@ -175,7 +207,7 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>*
widthString = *argv;
++argv;
if (argv >= stop) {
SkDebugf("Missing height for --mode tile\n");
gLogger.logError("Missing height for --mode tile\n");
usage(argv0);
exit(-1);
}
@ -187,17 +219,19 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>*
} else {
--argv;
}
} else if (0 == strcmp(*argv, "unflatten")) {
benchmark = SkNEW(sk_tools::UnflattenPictureBenchmark);
} else if (0 == strcmp(*argv, "playbackCreation")) {
benchmark = SkNEW(sk_tools::PlaybackCreationBenchmark);
} else {
SkDebugf("%s is not a valid mode for --mode\n", *argv);
SkString err;
err.printf("%s is not a valid mode for --mode\n", *argv);
gLogger.logError(err);
usage(argv0);
exit(-1);
}
} else if (0 == strcmp(*argv, "--device")) {
++argv;
if (argv >= stop) {
SkDebugf("Missing mode for --deivce\n");
gLogger.logError("Missing mode for --deivce\n");
usage(argv0);
exit(-1);
}
@ -211,7 +245,9 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>*
}
#endif
else {
SkDebugf("%s is not a valid mode for --device\n", *argv);
SkString err;
err.printf("%s is not a valid mode for --device\n", *argv);
gLogger.logError(err);
usage(argv0);
exit(-1);
}
@ -231,8 +267,10 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>*
int minWidth = atoi(widthString);
if (!SkIsPow2(minWidth) || minWidth < 0) {
SkDELETE(tileBenchmark);
SkDebugf("--mode %s must be given a width"
SkString err;
err.printf("--mode %s must be given a width"
" value that is a power of two\n", mode);
gLogger.logError(err);
exit(-1);
}
tileBenchmark->setTileMinPowerOf2Width(minWidth);
@ -240,14 +278,14 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>*
tileBenchmark->setTileWidthPercentage(atof(widthString));
if (!(tileBenchmark->getTileWidthPercentage() > 0)) {
SkDELETE(tileBenchmark);
SkDebugf("--mode tile must be given a width percentage > 0\n");
gLogger.logError("--mode tile must be given a width percentage > 0\n");
exit(-1);
}
} else {
tileBenchmark->setTileWidth(atoi(widthString));
if (!(tileBenchmark->getTileWidth() > 0)) {
SkDELETE(tileBenchmark);
SkDebugf("--mode tile must be given a width > 0\n");
gLogger.logError("--mode tile must be given a width > 0\n");
exit(-1);
}
}
@ -256,14 +294,14 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>*
tileBenchmark->setTileHeightPercentage(atof(heightString));
if (!(tileBenchmark->getTileHeightPercentage() > 0)) {
SkDELETE(tileBenchmark);
SkDebugf("--mode tile must be given a height percentage > 0\n");
gLogger.logError("--mode tile must be given a height percentage > 0\n");
exit(-1);
}
} else {
tileBenchmark->setTileHeight(atoi(heightString));
if (!(tileBenchmark->getTileHeight() > 0)) {
SkDELETE(tileBenchmark);
SkDebugf("--mode tile must be given a height > 0\n");
gLogger.logError("--mode tile must be given a height > 0\n");
exit(-1);
}
}
@ -286,6 +324,9 @@ static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>*
benchmark->setRepeats(repeats);
benchmark->setDeviceType(deviceType);
benchmark->setLogger(&gLogger);
// Report current settings:
gLogger.logProgress(commandLine);
}
static void process_input(const SkString& input, sk_tools::PictureBenchmark& benchmark) {

View File

@ -85,13 +85,6 @@ namespace sk_tools {
return skString.endsWith("%");
}
// This copies how bench does printing of test results.
#ifdef SK_BUILD_FOR_ANDROID
void print_msg(const char msg[]) { SkDebugf("%s", msg); }
#else
void print_msg(const char msg[]) { printf("%s", msg); }
#endif
void setup_bitmap(SkBitmap* bitmap, int width, int height) {
bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
bitmap->allocPixels();

View File

@ -36,11 +36,6 @@ namespace sk_tools {
// Returns true if the string ends with %
bool is_percentage(const char* const string);
// Prints to STDOUT so that test results can be easily seperated from the
// error stream. Note, that this still prints to the same stream as SkDebugf
// on Andoid.
void print_msg(const char msg[]);
// Prepares the bitmap so that it can be written.
//
// Specifically, it configures the bitmap, allocates pixels and then

View File

@ -104,7 +104,9 @@ static void render_picture(const SkString& inputPath, const SkString& outputDir,
renderer.init(&picture);
renderer.render();
renderer.render(true);
renderer.resetState();
write_output(outputDir, inputFilename, renderer);