| Index: tools/bbh_shootout.cpp
|
| diff --git a/tools/bbh_shootout.cpp b/tools/bbh_shootout.cpp
|
| index 6475ab18b45e5f08eaeed534a5dd06beb6581767..fa1739c3b573a34bc904af925678391afc86bdb9 100644
|
| --- a/tools/bbh_shootout.cpp
|
| +++ b/tools/bbh_shootout.cpp
|
| @@ -15,30 +15,155 @@
|
| #include "SkStream.h"
|
| #include "SkString.h"
|
| #include "SkTArray.h"
|
| -#include "SkCommandLineFlags.h"
|
| -
|
| -typedef sk_tools::PictureRenderer::BBoxHierarchyType BBoxType;
|
| -static const int kBBoxTypeCount = sk_tools::PictureRenderer::kLast_BBoxHierarchyType + 1;
|
| -
|
| -
|
| -DEFINE_string2(skps, r, "", "The list of SKPs to benchmark.");
|
| -DEFINE_string(bbh, "", "The set of bbox types to test. If empty, all are tested. "
|
| - "Should be one or more of none, quadtree, rtree, tilegrid.");
|
| -DEFINE_int32(record, 100, "Number of times to record each SKP.");
|
| -DEFINE_int32(playback, 1, "Number of times to playback each SKP.");
|
| -DEFINE_int32(tilesize, 256, "The size of a tile.");
|
| -
|
| -struct Measurement {
|
| +#include "TimerData.h"
|
| +
|
| +static const int kNumNormalRecordings = 10;
|
| +static const int kNumRTreeRecordings = 10;
|
| +static const int kNumPlaybacks = 1;
|
| +static const size_t kNumBaseBenchmarks = 3;
|
| +static const size_t kNumTileSizes = 3;
|
| +static const size_t kNumBbhPlaybackBenchmarks = 3;
|
| +static const size_t kNumBenchmarks = kNumBaseBenchmarks + kNumBbhPlaybackBenchmarks;
|
| +
|
| +enum BenchmarkType {
|
| + kNormal_BenchmarkType = 0,
|
| + kRTree_BenchmarkType,
|
| +};
|
| +
|
| +struct Histogram {
|
| + Histogram() {
|
| + // Make fCpuTime negative so that we don't mess with stats:
|
| + fCpuTime = SkIntToScalar(-1);
|
| + }
|
| + SkScalar fCpuTime;
|
| + SkString fPath;
|
| +};
|
| +
|
| +
|
| +////////////////////////////////////////////////////////////////////////////////
|
| +// Defined below.
|
| +struct BenchmarkControl;
|
| +
|
| +typedef void (*BenchmarkFunction)
|
| + (const BenchmarkControl&, const SkString&, SkPicture*, BenchTimer*);
|
| +
|
| +static void benchmark_playback(
|
| + const BenchmarkControl&, const SkString&, SkPicture*, BenchTimer*);
|
| +static void benchmark_recording(
|
| + const BenchmarkControl&, const SkString&, SkPicture*, BenchTimer*);
|
| +////////////////////////////////////////////////////////////////////////////////
|
| +
|
| +/**
|
| + * Acts as a POD containing information needed to run a benchmark.
|
| + * Provides static methods to poll benchmark info from an index.
|
| + */
|
| +struct BenchmarkControl {
|
| + SkISize fTileSize;
|
| + BenchmarkType fType;
|
| + BenchmarkFunction fFunction;
|
| SkString fName;
|
| - SkScalar fRecordAverage[kBBoxTypeCount];
|
| - SkScalar fPlaybackAverage[kBBoxTypeCount];
|
| +
|
| + /**
|
| + * Will construct a BenchmarkControl instance from an index between 0 an kNumBenchmarks.
|
| + */
|
| + static BenchmarkControl Make(size_t i) {
|
| + SkASSERT(kNumBenchmarks > i);
|
| + BenchmarkControl benchControl;
|
| + benchControl.fTileSize = GetTileSize(i);
|
| + benchControl.fType = GetBenchmarkType(i);
|
| + benchControl.fFunction = GetBenchmarkFunc(i);
|
| + benchControl.fName = GetBenchmarkName(i);
|
| + return benchControl;
|
| + }
|
| +
|
| + enum BaseBenchmarks {
|
| + kNormalRecord = 0,
|
| + kRTreeRecord,
|
| + kNormalPlayback,
|
| + };
|
| +
|
| + static SkISize fTileSizes[kNumTileSizes];
|
| +
|
| + static SkISize GetTileSize(size_t i) {
|
| + // Two of the base benchmarks don't need a tile size. But to maintain simplicity
|
| + // down the pipeline we have to let a couple of values unused.
|
| + if (i < kNumBaseBenchmarks) {
|
| + return SkISize::Make(256, 256);
|
| + }
|
| + if (i >= kNumBaseBenchmarks && i < kNumBenchmarks) {
|
| + return fTileSizes[i - kNumBaseBenchmarks];
|
| + }
|
| + SkASSERT(0);
|
| + return SkISize::Make(0, 0);
|
| + }
|
| +
|
| + static BenchmarkType GetBenchmarkType(size_t i) {
|
| + if (i < kNumBaseBenchmarks) {
|
| + switch (i) {
|
| + case kNormalRecord:
|
| + return kNormal_BenchmarkType;
|
| + case kNormalPlayback:
|
| + return kNormal_BenchmarkType;
|
| + case kRTreeRecord:
|
| + return kRTree_BenchmarkType;
|
| + }
|
| + }
|
| + if (i < kNumBenchmarks) {
|
| + return kRTree_BenchmarkType;
|
| + }
|
| + SkASSERT(0);
|
| + return kRTree_BenchmarkType;
|
| + }
|
| +
|
| + static BenchmarkFunction GetBenchmarkFunc(size_t i) {
|
| + // Base functions.
|
| + switch (i) {
|
| + case kNormalRecord:
|
| + return benchmark_recording;
|
| + case kNormalPlayback:
|
| + return benchmark_playback;
|
| + case kRTreeRecord:
|
| + return benchmark_recording;
|
| + }
|
| + // RTree playbacks
|
| + if (i < kNumBenchmarks) {
|
| + return benchmark_playback;
|
| + }
|
| + SkASSERT(0);
|
| + return NULL;
|
| + }
|
| +
|
| + static SkString GetBenchmarkName(size_t i) {
|
| + // Base benchmark names
|
| + switch (i) {
|
| + case kNormalRecord:
|
| + return SkString("normal_recording");
|
| + case kNormalPlayback:
|
| + return SkString("normal_playback");
|
| + case kRTreeRecord:
|
| + return SkString("rtree_recording");
|
| + }
|
| + // RTree benchmark names.
|
| + if (i < kNumBenchmarks) {
|
| + SkASSERT(i >= kNumBaseBenchmarks);
|
| + SkString name;
|
| + name.printf("rtree_playback_%dx%d",
|
| + fTileSizes[i - kNumBaseBenchmarks].fWidth,
|
| + fTileSizes[i - kNumBaseBenchmarks].fHeight);
|
| + return name;
|
| +
|
| + } else {
|
| + SkASSERT(0);
|
| + }
|
| + return SkString("");
|
| + }
|
| +
|
| };
|
|
|
| -const char* kBBoxHierarchyTypeNames[kBBoxTypeCount] = {
|
| - "none", // kNone_BBoxHierarchyType
|
| - "quadtree", // kQuadTree_BBoxHierarchyType
|
| - "rtree", // kRTree_BBoxHierarchyType
|
| - "tilegrid", // kTileGrid_BBoxHierarchyType
|
| +SkISize BenchmarkControl::fTileSizes[kNumTileSizes] = {
|
| + SkISize::Make(256, 256),
|
| + SkISize::Make(512, 512),
|
| + SkISize::Make(1024, 1024),
|
| };
|
|
|
| static SkPicture* pic_from_path(const char path[]) {
|
| @@ -51,90 +176,162 @@
|
| }
|
|
|
| /**
|
| + * Returns true when a tiled renderer with no bounding box hierarchy produces the given bitmap.
|
| + */
|
| +static bool compare_picture(const SkString& path, const SkBitmap& inBitmap, SkPicture* pic) {
|
| + SkBitmap* bitmap;
|
| + sk_tools::TiledPictureRenderer renderer;
|
| + renderer.setBBoxHierarchyType(sk_tools::PictureRenderer::kNone_BBoxHierarchyType);
|
| + renderer.init(pic);
|
| + renderer.setup();
|
| + renderer.render(&path, &bitmap);
|
| + SkAutoTDelete<SkBitmap> bmDeleter(bitmap);
|
| + renderer.end();
|
| +
|
| + if (bitmap->getSize() != inBitmap.getSize()) {
|
| + return false;
|
| + }
|
| + return !memcmp(bitmap->getPixels(), inBitmap.getPixels(), bitmap->getSize());
|
| +}
|
| +
|
| +/**
|
| * This function is the sink to which all work ends up going.
|
| - * @param renderer The renderer to use to perform the work.
|
| - * To measure rendering, use a TiledPictureRenderer.
|
| - * To measure recording, use a RecordPictureRenderer.
|
| - * @param bBoxType The bounding box hierarchy type to use.
|
| - * @param pic The picture to draw to the renderer.
|
| - * @param numRepeats The number of times to repeat the draw.
|
| - * @param timer The timer used to benchmark the work.
|
| + * Renders the picture into the renderer. It may or may not use an RTree.
|
| + * The renderer is chosen upstream. If we want to measure recording, we will
|
| + * use a RecordPictureRenderer. If we want to measure rendering, we will use a
|
| + * TiledPictureRenderer.
|
| */
|
| static void do_benchmark_work(sk_tools::PictureRenderer* renderer,
|
| - BBoxType bBoxType,
|
| - SkPicture* pic,
|
| - const int numRepeats,
|
| - BenchTimer* timer) {
|
| - renderer->setBBoxHierarchyType(bBoxType);
|
| - renderer->setGridSize(FLAGS_tilesize, FLAGS_tilesize);
|
| + int benchmarkType, const SkString& path, SkPicture* pic,
|
| + const int numRepeats, const char *msg, BenchTimer* timer) {
|
| + SkString msgPrefix;
|
| +
|
| + switch (benchmarkType){
|
| + case kNormal_BenchmarkType:
|
| + msgPrefix.set("Normal");
|
| + renderer->setBBoxHierarchyType(sk_tools::PictureRenderer::kNone_BBoxHierarchyType);
|
| + break;
|
| + case kRTree_BenchmarkType:
|
| + msgPrefix.set("RTree");
|
| + renderer->setBBoxHierarchyType(sk_tools::PictureRenderer::kRTree_BBoxHierarchyType);
|
| + break;
|
| + default:
|
| + SkASSERT(0);
|
| + break;
|
| + }
|
| +
|
| renderer->init(pic);
|
|
|
| - SkDebugf("%s %d times...\n", renderer->getConfigName().c_str(), numRepeats);
|
| + /**
|
| + * If the renderer is not tiled, assume we are measuring recording.
|
| + */
|
| + bool isPlayback = (NULL != renderer->getTiledRenderer());
|
| + // Will be non-null during RTree picture playback. For correctness test.
|
| + SkBitmap* bitmap = NULL;
|
| +
|
| + SkDebugf("%s %s %s %d times...\n", msgPrefix.c_str(), msg, path.c_str(), numRepeats);
|
| for (int i = 0; i < numRepeats; ++i) {
|
| + // Set up the bitmap.
|
| + SkBitmap** out = NULL;
|
| + if (i == 0 && kRTree_BenchmarkType == benchmarkType && isPlayback) {
|
| + out = &bitmap;
|
| + }
|
| +
|
| renderer->setup();
|
| - // Render once to fill caches
|
| - renderer->render(NULL, NULL);
|
| + // Render once to fill caches. Fill bitmap during the first iteration.
|
| + renderer->render(NULL, out);
|
| // Render again to measure
|
| timer->start();
|
| - renderer->render(NULL);
|
| + bool result = renderer->render(NULL);
|
| timer->end();
|
| +
|
| + // We only care about a false result on playback. RecordPictureRenderer::render will always
|
| + // return false because we are passing a NULL file name on purpose; which is fine.
|
| + if (isPlayback && !result) {
|
| + SkDebugf("Error rendering during playback.\n");
|
| + }
|
| + }
|
| + if (bitmap) {
|
| + SkAutoTDelete<SkBitmap> bmDeleter(bitmap);
|
| + if (!compare_picture(path, *bitmap, pic)) {
|
| + SkDebugf("Error: RTree produced different bitmap\n");
|
| + }
|
| + }
|
| +}
|
| +
|
| +/**
|
| + * Call do_benchmark_work with a tiled renderer using the default tile dimensions.
|
| + */
|
| +static void benchmark_playback(
|
| + const BenchmarkControl& benchControl,
|
| + const SkString& path, SkPicture* pic, BenchTimer* timer) {
|
| + sk_tools::TiledPictureRenderer renderer;
|
| +
|
| + SkString message("tiled_playback");
|
| + message.appendf("_%dx%d", benchControl.fTileSize.fWidth, benchControl.fTileSize.fHeight);
|
| + do_benchmark_work(&renderer, benchControl.fType,
|
| + path, pic, kNumPlaybacks, message.c_str(), timer);
|
| +}
|
| +
|
| +/**
|
| + * Call do_benchmark_work with a RecordPictureRenderer.
|
| + */
|
| +static void benchmark_recording(
|
| + const BenchmarkControl& benchControl,
|
| + const SkString& path, SkPicture* pic, BenchTimer* timer) {
|
| + sk_tools::RecordPictureRenderer renderer;
|
| + int numRecordings = 0;
|
| + switch(benchControl.fType) {
|
| + case kRTree_BenchmarkType:
|
| + numRecordings = kNumRTreeRecordings;
|
| + break;
|
| + case kNormal_BenchmarkType:
|
| + numRecordings = kNumNormalRecordings;
|
| + break;
|
| + }
|
| + do_benchmark_work(&renderer, benchControl.fType,
|
| + path, pic, numRecordings, "recording", timer);
|
| +}
|
| +
|
| +/**
|
| + * Takes argc,argv along with one of the benchmark functions defined above.
|
| + * Will loop along all skp files and perform measurments.
|
| + */
|
| +static void benchmark_loop(
|
| + int argc,
|
| + char **argv,
|
| + const BenchmarkControl& benchControl,
|
| + SkTArray<Histogram>& histogram) {
|
| + for (int index = 1; index < argc; ++index) {
|
| + BenchTimer timer;
|
| + SkString path(argv[index]);
|
| + SkAutoTUnref<SkPicture> pic(pic_from_path(path.c_str()));
|
| + if (NULL == pic) {
|
| + SkDebugf("Couldn't create picture. Ignoring path: %s\n", path.c_str());
|
| + continue;
|
| + }
|
| + benchControl.fFunction(benchControl, path, pic, &timer);
|
| + histogram[index - 1].fPath = path;
|
| + histogram[index - 1].fCpuTime = SkDoubleToScalar(timer.fCpu);
|
| }
|
| }
|
|
|
| int tool_main(int argc, char** argv);
|
| int tool_main(int argc, char** argv) {
|
| - SkCommandLineFlags::Parse(argc, argv);
|
| SkAutoGraphics ag;
|
| - bool includeBBoxType[kBBoxTypeCount];
|
| - for (int bBoxType = 0; bBoxType < kBBoxTypeCount; ++bBoxType) {
|
| - includeBBoxType[bBoxType] = (FLAGS_bbh.count() == 0) ||
|
| - FLAGS_bbh.contains(kBBoxHierarchyTypeNames[bBoxType]);
|
| - }
|
| - // go through all the pictures
|
| - SkTArray<Measurement> measurements;
|
| - for (int index = 0; index < FLAGS_skps.count(); ++index) {
|
| - const char* path = FLAGS_skps[index];
|
| - SkPicture* picture = pic_from_path(path);
|
| - if (NULL == picture) {
|
| - SkDebugf("Couldn't create picture. Ignoring path: %s\n", path);
|
| - continue;
|
| - }
|
| - SkDebugf("Benchmarking path: %s\n", path);
|
| - Measurement& measurement = measurements.push_back();
|
| - measurement.fName = path;
|
| - for (int bBoxType = 0; bBoxType < kBBoxTypeCount; ++bBoxType) {
|
| - if (!includeBBoxType[bBoxType]) { continue; }
|
| - if (FLAGS_playback > 0) {
|
| - sk_tools::TiledPictureRenderer playbackRenderer;
|
| - BenchTimer playbackTimer;
|
| - do_benchmark_work(&playbackRenderer, (BBoxType)bBoxType,
|
| - picture, FLAGS_playback, &playbackTimer);
|
| - measurement.fPlaybackAverage[bBoxType] = playbackTimer.fCpu;
|
| - }
|
| - if (FLAGS_record > 0) {
|
| - sk_tools::RecordPictureRenderer recordRenderer;
|
| - BenchTimer recordTimer;
|
| - do_benchmark_work(&recordRenderer, (BBoxType)bBoxType,
|
| - picture, FLAGS_record, &recordTimer);
|
| - measurement.fRecordAverage[bBoxType] = recordTimer.fCpu;
|
| - }
|
| - }
|
| - }
|
| -
|
| - Measurement globalMeasurement;
|
| - for (int bBoxType = 0; bBoxType < kBBoxTypeCount; ++bBoxType) {
|
| - if (!includeBBoxType[bBoxType]) { continue; }
|
| - globalMeasurement.fPlaybackAverage[bBoxType] = 0;
|
| - globalMeasurement.fRecordAverage[bBoxType] = 0;
|
| - for (int index = 0; index < measurements.count(); ++index) {
|
| - const Measurement& measurement = measurements[index];
|
| - globalMeasurement.fPlaybackAverage[bBoxType] +=
|
| - measurement.fPlaybackAverage[bBoxType];
|
| - globalMeasurement.fRecordAverage[bBoxType] +=
|
| - measurement.fRecordAverage[bBoxType];
|
| - }
|
| - globalMeasurement.fPlaybackAverage[bBoxType] /= measurements.count();
|
| - globalMeasurement.fRecordAverage[bBoxType] /= measurements.count();
|
| + SkString usage;
|
| + usage.printf("Usage: filename [filename]*\n");
|
| +
|
| + if (argc < 2) {
|
| + SkDebugf("%s\n", usage.c_str());
|
| + return -1;
|
| + }
|
| +
|
| + SkTArray<Histogram> histograms[kNumBenchmarks];
|
| +
|
| + for (size_t i = 0; i < kNumBenchmarks; ++i) {
|
| + histograms[i].reset(argc - 1);
|
| + benchmark_loop(argc, argv, BenchmarkControl::Make(i), histograms[i]);
|
| }
|
|
|
| // Output gnuplot readable histogram data..
|
| @@ -144,46 +341,62 @@
|
| SkFILEWStream recordOut(recTitle);
|
| recordOut.writeText("# ");
|
| playbackOut.writeText("# ");
|
| - SkDebugf("---\n");
|
| - for (int bBoxType = 0; bBoxType < kBBoxTypeCount; ++bBoxType) {
|
| - if (!includeBBoxType[bBoxType]) { continue; }
|
| + for (size_t i = 0; i < kNumBenchmarks; ++i) {
|
| SkString out;
|
| - out.printf("%s ", kBBoxHierarchyTypeNames[bBoxType]);
|
| - recordOut.writeText(out.c_str());
|
| - playbackOut.writeText(out.c_str());
|
| -
|
| - if (FLAGS_record > 0) {
|
| - SkDebugf("Average %s recording time: %.3fms\n",
|
| - kBBoxHierarchyTypeNames[bBoxType],
|
| - globalMeasurement.fRecordAverage[bBoxType]);
|
| - }
|
| - if (FLAGS_playback > 0) {
|
| - SkDebugf("Average %s playback time: %.3fms\n",
|
| - kBBoxHierarchyTypeNames[bBoxType],
|
| - globalMeasurement.fPlaybackAverage[bBoxType]);
|
| + out.printf("%s ", BenchmarkControl::GetBenchmarkName(i).c_str());
|
| + if (BenchmarkControl::GetBenchmarkFunc(i) == &benchmark_recording) {
|
| + recordOut.writeText(out.c_str());
|
| + }
|
| + if (BenchmarkControl::GetBenchmarkFunc(i) == &benchmark_playback) {
|
| + playbackOut.writeText(out.c_str());
|
| }
|
| }
|
| recordOut.writeText("\n");
|
| playbackOut.writeText("\n");
|
| // Write to file, and save recording averages.
|
| - for (int index = 0; index < measurements.count(); ++index) {
|
| - const Measurement& measurement = measurements[index];
|
| + SkScalar avgRecord = SkIntToScalar(0);
|
| + SkScalar avgPlayback = SkIntToScalar(0);
|
| + SkScalar avgRecordRTree = SkIntToScalar(0);
|
| + SkScalar avgPlaybackRTree = SkIntToScalar(0);
|
| + for (int i = 0; i < argc - 1; ++i) {
|
| SkString pbLine;
|
| SkString recLine;
|
| -
|
| - pbLine.printf("%d", index);
|
| - recLine.printf("%d", index);
|
| - for (int bBoxType = 0; bBoxType < kBBoxTypeCount; ++bBoxType) {
|
| - if (!includeBBoxType[bBoxType]) { continue; }
|
| - pbLine.appendf(" %f", measurement.fPlaybackAverage[bBoxType]);
|
| - recLine.appendf(" %f", measurement.fRecordAverage[bBoxType]);
|
| - }
|
| + // ==== Write record info
|
| + recLine.printf("%d ", i);
|
| + SkScalar cpuTime = histograms[BenchmarkControl::kNormalRecord][i].fCpuTime;
|
| + recLine.appendf("%f ", cpuTime);
|
| + avgRecord += cpuTime;
|
| + cpuTime = histograms[BenchmarkControl::kRTreeRecord][i].fCpuTime;
|
| + recLine.appendf("%f", cpuTime);
|
| + avgRecordRTree += cpuTime;
|
| + avgPlaybackRTree += cpuTime;
|
| +
|
| + // ==== Write playback info
|
| + pbLine.printf("%d ", i);
|
| + pbLine.appendf("%f ", histograms[2][i].fCpuTime); // Start with normal playback time.
|
| + avgPlayback += histograms[kNumBbhPlaybackBenchmarks - 1][i].fCpuTime;
|
| + avgPlaybackRTree += histograms[kNumBbhPlaybackBenchmarks][i].fCpuTime;
|
| + // Append all playback benchmark times.
|
| + for (size_t j = kNumBbhPlaybackBenchmarks; j < kNumBenchmarks; ++j) {
|
| + pbLine.appendf("%f ", histograms[j][i].fCpuTime);
|
| + }
|
| + pbLine.remove(pbLine.size() - 1, 1); // Remove trailing space from line.
|
| pbLine.appendf("\n");
|
| recLine.appendf("\n");
|
| playbackOut.writeText(pbLine.c_str());
|
| recordOut.writeText(recLine.c_str());
|
| }
|
| + avgRecord /= argc - 1;
|
| + avgRecordRTree /= argc - 1;
|
| + avgPlayback /= argc - 1;
|
| + avgPlaybackRTree /= argc - 1;
|
| + SkDebugf("Average base recording time: %.3fms\n", avgRecord);
|
| + SkDebugf("Average recording time with rtree: %.3fms\n", avgRecordRTree);
|
| + SkDebugf("Average base playback time: %.3fms\n", avgPlayback);
|
| + SkDebugf("Average playback time with rtree: %.3fms\n", avgPlaybackRTree);
|
| +
|
| SkDebugf("\nWrote data to gnuplot-readable files: %s %s\n", pbTitle, recTitle);
|
| +
|
| return 0;
|
| }
|
|
|
|
|