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
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 @@
     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 @@
     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 @@
 
     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 @@
         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 @@
         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 @@
         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 @@
 }
 
 /**
+ * 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.
  * 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 @@
      * 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<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(
-        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 @@
  * 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 @@
             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 @@
             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 @@
     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 @@
                 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 @@
     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 @@
         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;