SKPs-as-benches in nanobench

This is meant to replace bench_pictures.

CQ_EXTRA_TRYBOTS=tryserver.skia:Build-Mac10.7-Clang-Arm7-Release-iOS-Trybot

BUG=skia:
R=bsalomon@google.com, jcgregorio@google.com, mtklein@google.com

Author: mtklein@chromium.org

Review URL: https://codereview.chromium.org/425393004
diff --git a/bench/GMBench.cpp b/bench/GMBench.cpp
index dbd1d7b..bdf3290 100644
--- a/bench/GMBench.cpp
+++ b/bench/GMBench.cpp
@@ -49,3 +49,4 @@
     SkISize size = fGM->getISize();
     return SkIPoint::Make(size.fWidth, size.fHeight);
 }
+
diff --git a/bench/GMBench.h b/bench/GMBench.h
index 75cee6c..e224052 100644
--- a/bench/GMBench.h
+++ b/bench/GMBench.h
@@ -4,6 +4,8 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
+#ifndef GMBench_DEFINED
+#define GMBench_DEFINED
 
 #include "Benchmark.h"
 #include "SkCanvas.h"
@@ -29,3 +31,5 @@
     SkString    fName;
     typedef Benchmark INHERITED;
 };
+
+#endif
diff --git a/bench/SKPBench.cpp b/bench/SKPBench.cpp
new file mode 100644
index 0000000..cd7d4b0
--- /dev/null
+++ b/bench/SKPBench.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SKPBench.h"
+
+SKPBench::SKPBench(const char* name, const SkPicture* pic, const SkIRect& clip, SkScalar scale)
+    : fPic(SkRef(pic))
+    , fClip(clip)
+    , fScale(scale) {
+    fName.printf("%s_%.2g", name, scale);
+}
+
+const char* SKPBench::onGetName() {
+    return fName.c_str();
+}
+
+bool SKPBench::isSuitableFor(Backend backend) {
+    return backend != kNonRendering_Backend;
+}
+
+SkIPoint SKPBench::onGetSize() {
+    return SkIPoint::Make(fClip.width(), fClip.height());
+}
+
+void SKPBench::onDraw(const int loops, SkCanvas* canvas) {
+    canvas->save();
+        canvas->scale(fScale, fScale);
+        for (int i = 0; i < loops; i++) {
+            fPic->draw(canvas);
+            canvas->flush();
+        }
+    canvas->restore();
+}
diff --git a/bench/SKPBench.h b/bench/SKPBench.h
new file mode 100644
index 0000000..9cc9192
--- /dev/null
+++ b/bench/SKPBench.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKPBench_DEFINED
+#define SKPBench_DEFINED
+
+#include "Benchmark.h"
+#include "SkCanvas.h"
+#include "SkPicture.h"
+
+/**
+ * Runs an SkPicture as a benchmark by repeatedly drawing it scaled inside a device clip.
+ */
+class SKPBench : public Benchmark {
+public:
+    SKPBench(const char* name, const SkPicture*, const SkIRect& devClip, SkScalar scale);
+
+protected:
+    virtual const char* onGetName() SK_OVERRIDE;
+    virtual bool isSuitableFor(Backend backend) SK_OVERRIDE;
+    virtual void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE;
+    virtual SkIPoint onGetSize() SK_OVERRIDE;
+
+private:
+    SkAutoTUnref<const SkPicture> fPic;
+    const SkIRect fClip;
+    const SkScalar fScale;
+    SkString fName;
+
+    typedef Benchmark INHERITED;
+};
+
+#endif
diff --git a/bench/nanobench.cpp b/bench/nanobench.cpp
index 742a6ed..f6fd90e 100644
--- a/bench/nanobench.cpp
+++ b/bench/nanobench.cpp
@@ -11,9 +11,11 @@
 #include "CrashHandler.h"
 #include "GMBench.h"
 #include "ResultsWriter.h"
+#include "SKPBench.h"
 #include "Stats.h"
 #include "Timer.h"
 
+#include "SkOSFile.h"
 #include "SkCanvas.h"
 #include "SkCommonFlags.h"
 #include "SkForceLinking.h"
@@ -49,6 +51,9 @@
 DEFINE_string(key, "", "Space-separated key/value pairs to add to JSON.");
 DEFINE_string(gitHash, "", "Git hash to add to JSON.");
 
+DEFINE_string(clip, "0,0,1000,1000", "Clip for SKPs.");
+DEFINE_string(scales, "1.0", "Space-separated scales for SKPs.");
+
 static SkString humanize(double ms) {
     if (ms > 1e+3) return SkStringPrintf("%.3gs",  ms/1e3);
     if (ms < 1e-3) return SkStringPrintf("%.3gns", ms*1e6);
@@ -357,28 +362,108 @@
 
 class BenchmarkStream {
 public:
-    BenchmarkStream() : fBenches(BenchRegistry::Head()) , fGMs(skiagm::GMRegistry::Head()) {}
+    BenchmarkStream() : fBenches(BenchRegistry::Head())
+                      , fGMs(skiagm::GMRegistry::Head())
+                      , fCurrentScale(0)
+                      , fCurrentSKP(0) {
+        for (int i = 0; i < FLAGS_skps.count(); i++) {
+            if (SkStrEndsWith(FLAGS_skps[i], ".skp")) {
+                fSKPs.push_back() = FLAGS_skps[i];
+            } else {
+                SkOSFile::Iter it(FLAGS_skps[i], ".skp");
+                SkString path;
+                while (it.next(&path)) {
+                    fSKPs.push_back() = SkOSPath::Join(FLAGS_skps[0], path.c_str());
+                }
+            }
+        }
 
-    Benchmark* next(const char** sourceType) {
+        if (4 != sscanf(FLAGS_clip[0], "%d,%d,%d,%d",
+                        &fClip.fLeft, &fClip.fTop, &fClip.fRight, &fClip.fBottom)) {
+            SkDebugf("Can't parse %s from --clip as an SkIRect.\n", FLAGS_clip[0]);
+            exit(1);
+        }
+
+        for (int i = 0; i < FLAGS_scales.count(); i++) {
+            if (1 != sscanf(FLAGS_scales[i], "%f", &fScales.push_back())) {
+                SkDebugf("Can't parse %s from --scales as an SkScalar.\n", FLAGS_scales[i]);
+                exit(1);
+            }
+        }
+    }
+
+    Benchmark* next() {
         if (fBenches) {
             Benchmark* bench = fBenches->factory()(NULL);
             fBenches = fBenches->next();
-            *sourceType = "bench";
+            fSourceType = "bench";
             return bench;
         }
+
         while (fGMs) {
             SkAutoTDelete<skiagm::GM> gm(fGMs->factory()(NULL));
             fGMs = fGMs->next();
             if (gm->getFlags() & skiagm::GM::kAsBench_Flag) {
-                *sourceType = "gm";
+                fSourceType = "gm";
                 return SkNEW_ARGS(GMBench, (gm.detach()));
             }
         }
+
+        while (fCurrentScale < fScales.count()) {
+            while (fCurrentSKP < fSKPs.count()) {
+                const SkString& path = fSKPs[fCurrentSKP++];
+
+                // Not strictly necessary, as it will be checked again later,
+                // but helps to avoid a lot of pointless work if we're going to skip it.
+                if (SkCommandLineFlags::ShouldSkip(FLAGS_match, path.c_str())) {
+                    continue;
+                }
+
+                SkAutoTUnref<SkStream> stream(SkStream::NewFromFile(path.c_str()));
+                if (stream.get() == NULL) {
+                    SkDebugf("Could not read %s.\n", path.c_str());
+                    exit(1);
+                }
+
+                SkAutoTUnref<SkPicture> pic(SkPicture::CreateFromStream(stream.get()));
+                if (pic.get() == NULL) {
+                    SkDebugf("Could not read %s as an SkPicture.\n", path.c_str());
+                    exit(1);
+                }
+
+                SkString name = SkOSPath::Basename(path.c_str());
+
+                fSourceType = "skp";
+                return SkNEW_ARGS(SKPBench,
+                        (name.c_str(), pic.get(), fClip, fScales[fCurrentScale]));
+            }
+            fCurrentSKP = 0;
+            fCurrentScale++;
+        }
+
         return NULL;
     }
+
+    void fillCurrentOptions(ResultsWriter* log) const {
+        log->configOption("source_type", fSourceType);
+        if (0 == strcmp(fSourceType, "skp")) {
+            log->configOption("clip",
+                    SkStringPrintf("%d %d %d %d", fClip.fLeft, fClip.fTop,
+                                                  fClip.fRight, fClip.fBottom).c_str());
+            log->configOption("scale", SkStringPrintf("%.2g", fScales[fCurrentScale]).c_str());
+        }
+    }
+
 private:
     const BenchRegistry* fBenches;
     const skiagm::GMRegistry* fGMs;
+    SkIRect            fClip;
+    SkTArray<SkScalar> fScales;
+    SkTArray<SkString> fSKPs;
+
+    const char* fSourceType;
+    int fCurrentScale;
+    int fCurrentSKP;
 };
 
 int nanobench_main();
@@ -427,9 +512,8 @@
     SkTDArray<Config> configs;
     create_configs(&configs);
 
-    BenchmarkStream benches;
-    const char* sourceType;
-    while (Benchmark* b = benches.next(&sourceType)) {
+    BenchmarkStream benchStream;
+    while (Benchmark* b = benchStream.next()) {
         SkAutoTDelete<Benchmark> bench(b);
         if (SkCommandLineFlags::ShouldSkip(FLAGS_match, bench->getName())) {
             continue;
@@ -446,6 +530,18 @@
             SkCanvas* canvas = targets[j]->surface.get() ? targets[j]->surface->getCanvas() : NULL;
             const char* config = targets[j]->config.name;
 
+#if SK_DEBUG
+            // skia:2797  Some SKPs SkASSERT in debug mode.  Skip them for now.
+            if (0 == strcmp("565", config)
+                    && (  SkStrStartsWith(bench->getName(), "desk_carsvg.skp")
+                       || SkStrStartsWith(bench->getName(), "desk_forecastio.skp")
+                       || SkStrStartsWith(bench->getName(), "tabl_cnet.skp")
+                       || SkStrStartsWith(bench->getName(), "tabl_googlecalendar.skp"))) {
+                SkDebugf("Skipping 565 %s.  It'd assert.\n", bench->getName());
+                continue;
+            }
+#endif
+
             const int loops =
 #if SK_SUPPORT_GPU
                 Benchmark::kGPU_Backend == targets[j]->config.backend
@@ -462,7 +558,7 @@
 
             Stats stats(samples.get(), FLAGS_samples);
             log.config(config);
-            log.configOption("source_type", sourceType);
+            benchStream.fillCurrentOptions(&log);
 #if SK_SUPPORT_GPU
             if (Benchmark::kGPU_Backend == targets[j]->config.backend) {
                 fill_gpu_options(&log, targets[j]->gl);
diff --git a/dm/DM.cpp b/dm/DM.cpp
index fd15eef..76dc414 100644
--- a/dm/DM.cpp
+++ b/dm/DM.cpp
@@ -46,8 +46,6 @@
                "If a file, compare generated images against JSON expectations at this path."
 );
 
-DEFINE_string(skps, "", "Directory to read skps from.");
-
 DEFINE_bool(gms, true, "Run GMs?");
 DEFINE_bool(tests, true, "Run tests?");
 DEFINE_bool(reportUsedChars, false, "Output test font construction data to be pasted into"
@@ -247,7 +245,7 @@
         sk_tool_utils::report_used_chars();
     }
 #endif
-    
+
     SkTArray<SkString> failures;
     reporter.getFailures(&failures);
     report_failures(failures);
diff --git a/gyp/bench.gyp b/gyp/bench.gyp
index d627590..2a5071a 100644
--- a/gyp/bench.gyp
+++ b/gyp/bench.gyp
@@ -38,6 +38,7 @@
       'type': 'executable',
       'sources': [
         '../bench/GMBench.cpp',
+        '../bench/SKPBench.cpp',
         '../bench/ResultsWriter.cpp',
         '../bench/nanobench.cpp',
       ],
diff --git a/gyp/iOSShell.gyp b/gyp/iOSShell.gyp
index 366e554..45f1eff 100644
--- a/gyp/iOSShell.gyp
+++ b/gyp/iOSShell.gyp
@@ -19,6 +19,7 @@
           ],
           'sources': [
             '../bench/GMBench.cpp',
+            '../bench/SKPBench.cpp',
             '../bench/ResultsWriter.cpp',
             '../bench/nanobench.cpp',
             '../tests/skia_test.cpp',
diff --git a/tools/flags/SkCommonFlags.cpp b/tools/flags/SkCommonFlags.cpp
index a30ba7e..beeb6ee 100644
--- a/tools/flags/SkCommonFlags.cpp
+++ b/tools/flags/SkCommonFlags.cpp
@@ -44,6 +44,8 @@
 
 DEFINE_bool2(single, z, false, "run tests on a single thread internally.");
 
+DEFINE_string(skps, "", "Directory to read skps from.");
+
 DEFINE_int32(threads, SkThreadPool::kThreadPerCore,
              "run threadsafe tests on a threadpool with this many threads.");
 
diff --git a/tools/flags/SkCommonFlags.h b/tools/flags/SkCommonFlags.h
index a30d1ae..4abf201 100644
--- a/tools/flags/SkCommonFlags.h
+++ b/tools/flags/SkCommonFlags.h
@@ -21,6 +21,7 @@
 DECLARE_bool(resetGpuContext);
 DECLARE_bool(abandonGpuContext);
 DECLARE_bool(single);
+DECLARE_string(skps);
 DECLARE_int32(threads);
 DECLARE_string(resourcePath);
 DECLARE_bool(verbose);