From 2e915b32347aa2c2bcf82aba57c325b8fb81ec53 Mon Sep 17 00:00:00 2001 From: "commit-bot@chromium.org" Date: Tue, 20 Aug 2013 12:23:18 +0000 Subject: [PATCH] Add correctness test to bbh_shootout. R=caryclark@google.com, reed@google.com Author: sglez@google.com Review URL: https://chromiumcodereview.appspot.com/23121002 git-svn-id: http://skia.googlecode.com/svn/trunk@10809 2bbb7eff-a529-9590-31e7-b0007b416f81 --- tools/bbh_shootout.cpp | 126 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 87 insertions(+), 39 deletions(-) diff --git a/tools/bbh_shootout.cpp b/tools/bbh_shootout.cpp index 9de0bfc..76e8d07 100644 --- a/tools/bbh_shootout.cpp +++ b/tools/bbh_shootout.cpp @@ -19,7 +19,7 @@ static const int kNumNormalRecordings = SkBENCHLOOP(10); static const int kNumRTreeRecordings = SkBENCHLOOP(10); -static const int kNumPlaybacks = SkBENCHLOOP(4); +static const int kNumPlaybacks = SkBENCHLOOP(1); static const size_t kNumBaseBenchmarks = 3; static const size_t kNumTileSizes = 3; static const size_t kNumBbhPlaybackBenchmarks = 3; @@ -39,14 +39,19 @@ struct Histogram { SkString fPath; }; -typedef void (*BenchmarkFunction) - (BenchmarkType, const SkISize&, const SkString&, SkPicture*, BenchTimer*); +//////////////////////////////////////////////////////////////////////////////// // Defined below. +struct BenchmarkControl; + +typedef void (*BenchmarkFunction) + (const BenchmarkControl&, const SkString&, SkPicture*, BenchTimer*); + static void benchmark_playback( - BenchmarkType, const SkISize&, const SkString&, SkPicture*, BenchTimer*); + const BenchmarkControl&, const SkString&, SkPicture*, BenchTimer*); static void benchmark_recording( - BenchmarkType, const SkISize&, const SkString&, SkPicture*, BenchTimer*); + const BenchmarkControl&, const SkString&, SkPicture*, BenchTimer*); +//////////////////////////////////////////////////////////////////////////////// /** * Acts as a POD containing information needed to run a benchmark. @@ -64,10 +69,10 @@ struct BenchmarkControl { 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); + benchControl.fTileSize = GetTileSize(i); + benchControl.fType = GetBenchmarkType(i); + benchControl.fFunction = GetBenchmarkFunc(i); + benchControl.fName = GetBenchmarkName(i); return benchControl; } @@ -79,7 +84,7 @@ struct BenchmarkControl { static SkISize fTileSizes[kNumTileSizes]; - static SkISize getTileSize(size_t i) { + 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) { @@ -92,15 +97,15 @@ struct BenchmarkControl { return SkISize::Make(0, 0); } - static BenchmarkType getBenchmarkType(size_t i) { + 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; + case kNormalRecord: + return kNormal_BenchmarkType; + case kNormalPlayback: + return kNormal_BenchmarkType; + case kRTreeRecord: + return kRTree_BenchmarkType; } } if (i < kNumBenchmarks) { @@ -110,7 +115,7 @@ struct BenchmarkControl { return kRTree_BenchmarkType; } - static BenchmarkFunction getBenchmarkFunc(size_t i) { + static BenchmarkFunction GetBenchmarkFunc(size_t i) { // Base functions. switch (i) { case kNormalRecord: @@ -128,7 +133,7 @@ struct BenchmarkControl { return NULL; } - static SkString getBenchmarkName(size_t i) { + static SkString GetBenchmarkName(size_t i) { // Base benchmark names switch (i) { case kNormalRecord: @@ -171,10 +176,29 @@ static SkPicture* pic_from_path(const char path[]) { } /** + * 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 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. * 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 eill use a + * use a RecordPictureRenderer. If we want to measure rendering, we will use a * TiledPictureRenderer. */ static void do_benchmark_work(sk_tools::PictureRenderer* renderer, @@ -202,36 +226,50 @@ static void do_benchmark_work(sk_tools::PictureRenderer* renderer, * 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); // Render again to measure timer->start(); - bool result = renderer->render(NULL); + bool result = renderer->render(NULL, out); 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) { + if (isPlayback && !result) { SkDebugf("Error rendering during playback.\n"); } } - renderer->end(); + if (bitmap) { + SkAutoTDelete 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( - BenchmarkType benchmarkType, const SkISize& tileSize, + const BenchmarkControl& benchControl, const SkString& path, SkPicture* pic, BenchTimer* timer) { sk_tools::TiledPictureRenderer renderer; SkString message("tiled_playback"); - message.appendf("_%dx%d", tileSize.fWidth, tileSize.fHeight); - do_benchmark_work(&renderer, benchmarkType, + message.appendf("_%dx%d", benchControl.fTileSize.fWidth, benchControl.fTileSize.fHeight); + do_benchmark_work(&renderer, benchControl.fType, path, pic, kNumPlaybacks, message.c_str(), timer); } @@ -239,11 +277,11 @@ static void benchmark_playback( * Call do_benchmark_work with a RecordPictureRenderer. */ static void benchmark_recording( - BenchmarkType benchmarkType, const SkISize& tileSize, + const BenchmarkControl& benchControl, const SkString& path, SkPicture* pic, BenchTimer* timer) { sk_tools::RecordPictureRenderer renderer; int numRecordings = 0; - switch(benchmarkType) { + switch(benchControl.fType) { case kRTree_BenchmarkType: numRecordings = kNumRTreeRecordings; break; @@ -251,7 +289,8 @@ static void benchmark_recording( numRecordings = kNumNormalRecordings; break; } - do_benchmark_work(&renderer, benchmarkType, path, pic, numRecordings, "recording", timer); + do_benchmark_work(&renderer, benchControl.fType, + path, pic, numRecordings, "recording", timer); } /** @@ -277,8 +316,7 @@ static bool benchmark_loop( SkDebugf("Couldn't create picture. Ignoring path: %s\n", path.c_str()); continue; } - benchControl.fFunction(benchControl.fType, benchControl.fTileSize, path, pic, &timer); - SkAssertResult(timerData.appendTimes(&timer)); + benchControl.fFunction(benchControl, path, pic, &timer); histogram[index - 1].fPath = path; histogram[index - 1].fCpuTime = SkDoubleToScalar(timer.fCpu); @@ -293,7 +331,7 @@ static bool benchmark_loop( const char findStr[] = "= "; int pos = timerResult.find(findStr); if (-1 == pos) { - SkDebugf("Unexpected output from TimerData::getResult(...). Unable to parse."); + SkDebugf("Unexpected output from TimerData::getResult(...). Unable to parse.\n"); return false; } @@ -325,7 +363,7 @@ int tool_main(int argc, char** argv) { BenchmarkControl::Make(i), histograms[i]); if (!success) { - SkDebugf("benchmark_loop failed at index %d", i); + SkDebugf("benchmark_loop failed at index %d\n", i); } } @@ -338,24 +376,30 @@ int tool_main(int argc, char** argv) { playbackOut.writeText("# "); for (size_t i = 0; i < kNumBenchmarks; ++i) { SkString out; - out.printf("%s ", BenchmarkControl::getBenchmarkName(i).c_str()); - if (BenchmarkControl::getBenchmarkFunc(i) == &benchmark_recording) { + 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) { + if (BenchmarkControl::GetBenchmarkFunc(i) == &benchmark_playback) { playbackOut.writeText(out.c_str()); } } recordOut.writeText("\n"); playbackOut.writeText("\n"); - + // Write to file, and save recording averages. + SkScalar avgRecord = SkIntToScalar(0); + SkScalar avgRecordRTree = SkIntToScalar(0); for (int i = 0; i < argc - 1; ++i) { SkString pbLine; SkString recLine; // ==== Write record info recLine.printf("%d ", i); - recLine.appendf("%f ", histograms[0][i].fCpuTime); // Append normal_record time - recLine.appendf("%f", histograms[1][i].fCpuTime); // Append rtree_record time + 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; // ==== Write playback info pbLine.printf("%d ", i); @@ -370,6 +414,10 @@ int tool_main(int argc, char** argv) { playbackOut.writeText(pbLine.c_str()); recordOut.writeText(recLine.c_str()); } + avgRecord /= argc - 1; + avgRecordRTree /= argc - 1; + SkDebugf("Average base recording time: %.3fms\n", avgRecord); + SkDebugf("Average recording time with rtree: %.3fms\n", avgRecordRTree); SkDebugf("\nWrote data to gnuplot-readable files: %s %s\n", pbTitle, recTitle); return 0; -- 2.7.4