DM: Add --skps.

This does render_pictures, plus checks SkRecord optimizations.

Disable an SkRecord optimization that draws several bot SKPs wrong.  (To be investigated.)

BUG=skia:2378
R=reed@google.com, mtklein@google.com

Author: mtklein@chromium.org

Review URL: https://codereview.chromium.org/270543004

git-svn-id: http://skia.googlecode.com/svn/trunk@14739 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/dm/DM.cpp b/dm/DM.cpp
index becdc50..f4eefa6 100644
--- a/dm/DM.cpp
+++ b/dm/DM.cpp
@@ -5,6 +5,7 @@
 #include "SkCommandLineFlags.h"
 #include "SkForceLinking.h"
 #include "SkGraphics.h"
+#include "SkPicture.h"
 #include "SkString.h"
 #include "Test.h"
 #include "gm.h"
@@ -14,6 +15,7 @@
 #include "DMGpuGMTask.h"
 #include "DMGpuSupport.h"
 #include "DMReporter.h"
+#include "DMSKPTask.h"
 #include "DMTask.h"
 #include "DMTaskRunner.h"
 #include "DMTestTask.h"
@@ -43,6 +45,7 @@
 DEFINE_string(config, "565 8888 gpu nonrendering",
               "Options: 565 8888 gpu nonrendering msaa4 msaa16 nvprmsaa4 nvprmsaa16 gpunull gpudebug angle mesa");
 DEFINE_bool(leaks, false, "Print leaked instance-counted objects at exit?");
+DEFINE_string(skps, "", "Directory to read skps from.");
 
 DEFINE_bool(gms, true, "Run GMs?");
 DEFINE_bool(benches, true, "Run benches?  Does not run GMs-as-benches.");
@@ -143,6 +146,35 @@
     }
 }
 
+static void kick_off_skps(DM::Reporter* reporter, DM::TaskRunner* tasks) {
+    if (FLAGS_skps.isEmpty()) {
+        return;
+    }
+
+    SkOSFile::Iter it(FLAGS_skps[0], ".skp");
+    SkString filename;
+    while (it.next(&filename)) {
+        if (SkCommandLineFlags::ShouldSkip(FLAGS_match, filename.c_str())) {
+            continue;
+        }
+
+        const SkString path = SkOSPath::SkPathJoin(FLAGS_skps[0], filename.c_str());
+
+        SkAutoTDelete<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);
+        }
+
+        tasks->add(SkNEW_ARGS(DM::SKPTask, (reporter, tasks, pic.detach(), filename)));
+    }
+}
+
 static void report_failures(const DM::Reporter& reporter) {
     SkTArray<SkString> failures;
     reporter.getFailures(&failures);
@@ -215,6 +247,7 @@
     kick_off_gms(gms, configs, *expectations, &reporter, &tasks);
     kick_off_benches(benches, configs, &reporter, &tasks);
     kick_off_tests(tests, &reporter, &tasks);
+    kick_off_skps(&reporter, &tasks);
     tasks.wait();
 
     SkDebugf("\n");
diff --git a/dm/DMCpuGMTask.cpp b/dm/DMCpuGMTask.cpp
index 071697b..eb7ed99 100644
--- a/dm/DMCpuGMTask.cpp
+++ b/dm/DMCpuGMTask.cpp
@@ -36,14 +36,14 @@
 #define SPAWN(ChildTask, ...) this->spawnChild(SkNEW_ARGS(ChildTask, (*this, __VA_ARGS__)))
     SPAWN(ExpectationsTask, fExpectations, bitmap);
 
-    SPAWN(PipeTask, fGMFactory(NULL), bitmap, false, false);
-    SPAWN(PipeTask, fGMFactory(NULL), bitmap, true, false);
-    SPAWN(PipeTask, fGMFactory(NULL), bitmap, true, true);
+    SPAWN(PipeTask, fGMFactory(NULL), bitmap, PipeTask::kInProcess_Mode);
+    SPAWN(PipeTask, fGMFactory(NULL), bitmap, PipeTask::kCrossProcess_Mode);
+    SPAWN(PipeTask, fGMFactory(NULL), bitmap, PipeTask::kSharedAddress_Mode);
     SPAWN(QuiltTask, fGMFactory(NULL), bitmap);
-    SPAWN(RecordTask, fGMFactory(NULL), bitmap, true);
-    SPAWN(RecordTask, fGMFactory(NULL), bitmap, false);
-    SPAWN(ReplayTask, fGMFactory(NULL), bitmap, false);
-    SPAWN(ReplayTask, fGMFactory(NULL), bitmap, true);
+    SPAWN(RecordTask, fGMFactory(NULL), bitmap, RecordTask::kOptimize_Mode);
+    SPAWN(RecordTask, fGMFactory(NULL), bitmap, RecordTask::kNoOptimize_Mode);
+    SPAWN(ReplayTask, fGMFactory(NULL), bitmap, ReplayTask::kNormal_Mode);
+    SPAWN(ReplayTask, fGMFactory(NULL), bitmap, ReplayTask::kRTree_Mode);
     SPAWN(SerializeTask, fGMFactory(NULL), bitmap);
 
     SPAWN(WriteTask, bitmap);
diff --git a/dm/DMPipeTask.cpp b/dm/DMPipeTask.cpp
index 513594e..13636d6 100644
--- a/dm/DMPipeTask.cpp
+++ b/dm/DMPipeTask.cpp
@@ -10,14 +10,13 @@
 
 namespace DM {
 
-static uint32_t get_flags(bool crossProcess, bool sharedAddressSpace) {
-    SkASSERT(!(!crossProcess && sharedAddressSpace));
+static uint32_t get_flags(PipeTask::Mode mode) {
     uint32_t flags = 0;
-    if (crossProcess) {
+    if (mode != PipeTask::kInProcess_Mode) {
         flags |= SkGPipeWriter::kCrossProcess_Flag;
-        if (sharedAddressSpace) {
-            flags |= SkGPipeWriter::kSharedAddressSpace_Flag;
-        }
+    }
+    if (mode == PipeTask::kSharedAddress_Mode) {
+        flags |= SkGPipeWriter::kSharedAddressSpace_Flag;
     }
     return flags;
 }
@@ -25,7 +24,7 @@
 static const char* get_name(const uint32_t flags) {
     if (flags & SkGPipeWriter::kCrossProcess_Flag &&
         flags & SkGPipeWriter::kSharedAddressSpace_Flag) {
-        return "cross_process_shared_address_space_pipe";
+        return "shared_address_space_pipe";
     } else if (flags & SkGPipeWriter::kCrossProcess_Flag) {
         return "cross_process_pipe";
     } else {
@@ -36,10 +35,9 @@
 PipeTask::PipeTask(const Task& parent,
                    skiagm::GM* gm,
                    SkBitmap reference,
-                   bool crossProcess,
-                   bool sharedAddressSpace)
+                   Mode mode)
     : CpuTask(parent)
-    , fFlags(get_flags(crossProcess, sharedAddressSpace))
+    , fFlags(get_flags(mode))
     , fName(UnderJoin(parent.name().c_str(), get_name(fFlags)))
     , fGM(gm)
     , fReference(reference)
diff --git a/dm/DMPipeTask.h b/dm/DMPipeTask.h
index c251d08..bca74e0 100644
--- a/dm/DMPipeTask.h
+++ b/dm/DMPipeTask.h
@@ -14,11 +14,16 @@
 class PipeTask : public CpuTask {
 
 public:
+    enum Mode {
+        kInProcess_Mode,
+        kCrossProcess_Mode,
+        kSharedAddress_Mode,
+    };
+
     PipeTask(const Task& parent,        // PipeTask must be a child task.  Pass its parent here.
              skiagm::GM*,               // GM to run through a pipe.  Takes ownership.
              SkBitmap reference,        // Bitmap to compare pipe results to.
-             bool crossProcess,         // Should we set up a cross process pipe?
-             bool sharedAddressSpace);  // If cross process, should it assume shared address space?
+             Mode);
 
     virtual void draw() SK_OVERRIDE;
     virtual bool shouldSkip() const SK_OVERRIDE;
diff --git a/dm/DMRecordTask.cpp b/dm/DMRecordTask.cpp
index 531fe57..ce27aaa 100644
--- a/dm/DMRecordTask.cpp
+++ b/dm/DMRecordTask.cpp
@@ -11,21 +11,35 @@
 
 namespace DM {
 
-RecordTask::RecordTask(const Task& parent, skiagm::GM* gm, SkBitmap reference, bool optimize)
+RecordTask::RecordTask(const Task& parent, skiagm::GM* gm, SkBitmap reference, Mode mode)
     : CpuTask(parent)
-    , fName(UnderJoin(parent.name().c_str(), optimize ? "skr" : "skr-noopt"))
+    , fOptimize(mode == kOptimize_Mode)
+    , fName(UnderJoin(parent.name().c_str(), fOptimize ? "skr" : "skr-noopt"))
     , fGM(gm)
     , fReference(reference)
-    , fOptimize(optimize)
+    {}
+
+RecordTask::RecordTask(const Task& parent, SkPicture* pic, SkBitmap reference, Mode mode)
+    : CpuTask(parent)
+    , fOptimize(mode == kOptimize_Mode)
+    , fName(UnderJoin(parent.name().c_str(), fOptimize ? "skr" : "skr-noopt"))
+    , fPicture(SkRef(pic))
+    , fReference(reference)
     {}
 
 void RecordTask::draw() {
-    // Record the GM into an SkRecord.
+    // Record into an SkRecord.
     SkRecord record;
     SkRecorder recorder(SkRecorder::kWriteOnly_Mode, &record,
                         fReference.width(), fReference.height());
-    recorder.concat(fGM->getInitialTransform());
-    fGM->draw(&recorder);
+
+    if (fGM.get()) {
+        recorder.concat(fGM->getInitialTransform());
+        fGM->draw(&recorder);
+    } else {
+        fPicture->draw(&recorder);
+    }
+
 
     if (fOptimize) {
         SkRecordOptimize(&record);
@@ -33,7 +47,11 @@
 
     // Draw the SkRecord back into a bitmap.
     SkBitmap bitmap;
-    SetupBitmap(fReference.colorType(), fGM.get(), &bitmap);
+    if (fGM.get()) {
+        SetupBitmap(fReference.colorType(), fGM.get(), &bitmap);
+    } else {
+        SetupBitmap(fReference.colorType(), *fPicture, &bitmap);
+    }
     SkCanvas target(bitmap);
     SkRecordDraw(record, &target);
 
diff --git a/dm/DMRecordTask.h b/dm/DMRecordTask.h
index 94ac412..1420724 100644
--- a/dm/DMRecordTask.h
+++ b/dm/DMRecordTask.h
@@ -3,28 +3,35 @@
 
 #include "DMTask.h"
 #include "SkBitmap.h"
+#include "SkPicture.h"
 #include "SkString.h"
 #include "SkTemplates.h"
 #include "gm.h"
 
-// Records a GM through an SkRecord, draws it, and compares against the reference bitmap.
+// Records a GM or SKP through an SkRecord, draws it, and compares against the reference bitmap.
 
 namespace DM {
 
 class RecordTask : public CpuTask {
 
 public:
-    RecordTask(const Task& parent, skiagm::GM*, SkBitmap reference, bool optimize);
+    enum Mode {
+        kNoOptimize_Mode,
+        kOptimize_Mode,
+    };
+    RecordTask(const Task& parent, skiagm::GM*, SkBitmap reference, Mode);
+    RecordTask(const Task& parent, SkPicture*,  SkBitmap reference, Mode);
 
     virtual void draw() SK_OVERRIDE;
     virtual bool shouldSkip() const SK_OVERRIDE;
     virtual SkString name() const SK_OVERRIDE { return fName; }
 
 private:
+    bool fOptimize;
     const SkString fName;
+    SkAutoTUnref<SkPicture> fPicture;
     SkAutoTDelete<skiagm::GM> fGM;
     const SkBitmap fReference;
-    bool fOptimize;
 };
 
 }  // namespace DM
diff --git a/dm/DMReplayTask.cpp b/dm/DMReplayTask.cpp
index e4a9e44..a126f2c 100644
--- a/dm/DMReplayTask.cpp
+++ b/dm/DMReplayTask.cpp
@@ -14,12 +14,12 @@
 ReplayTask::ReplayTask(const Task& parent,
                        skiagm::GM* gm,
                        SkBitmap reference,
-                       bool useRTree)
+                       Mode mode)
     : CpuTask(parent)
-    , fName(UnderJoin(parent.name().c_str(), useRTree ? "rtree" : "replay"))
+    , fUseRTree(mode == kRTree_Mode)
+    , fName(UnderJoin(parent.name().c_str(), fUseRTree ? "rtree" : "replay"))
     , fGM(gm)
     , fReference(reference)
-    , fUseRTree(useRTree)
     {}
 
 void ReplayTask::draw() {
diff --git a/dm/DMReplayTask.h b/dm/DMReplayTask.h
index 78bef0d..de28172 100644
--- a/dm/DMReplayTask.h
+++ b/dm/DMReplayTask.h
@@ -14,20 +14,24 @@
 class ReplayTask : public CpuTask {
 
 public:
+    enum Mode {
+        kNormal_Mode,
+        kRTree_Mode,
+    };
     ReplayTask(const Task& parent,  // ReplayTask must be a child task.  Pass its parent here.
                skiagm::GM*,         // GM to run through a picture.  Takes ownership.
                SkBitmap reference,  // Bitmap to compare picture replay results to.
-               bool useRTree);      // Record with an RTree?
+               Mode);
 
     virtual void draw() SK_OVERRIDE;
     virtual bool shouldSkip() const SK_OVERRIDE;
     virtual SkString name() const SK_OVERRIDE { return fName; }
 
 private:
+    const bool fUseRTree;
     const SkString fName;
     SkAutoTDelete<skiagm::GM> fGM;
     const SkBitmap fReference;
-    const bool fUseRTree;
 };
 
 }  // namespace DM
diff --git a/dm/DMSKPTask.cpp b/dm/DMSKPTask.cpp
new file mode 100644
index 0000000..3eb4c5d
--- /dev/null
+++ b/dm/DMSKPTask.cpp
@@ -0,0 +1,23 @@
+#include "DMRecordTask.h"
+#include "DMSKPTask.h"
+#include "DMUtil.h"
+#include "DMWriteTask.h"
+
+namespace DM {
+
+SKPTask::SKPTask(Reporter* r, TaskRunner* tr, SkPicture* pic, SkString name)
+    : CpuTask(r, tr), fPicture(SkRef(pic)), fName(name) {}
+
+void SKPTask::draw() {
+    SkBitmap bitmap;
+    SetupBitmap(kN32_SkColorType, *fPicture, &bitmap);
+    DrawPicture(fPicture, &bitmap);
+
+    this->spawnChild(SkNEW_ARGS(RecordTask,
+                                (*this, fPicture, bitmap, RecordTask::kNoOptimize_Mode)));
+    this->spawnChild(SkNEW_ARGS(RecordTask,
+                                (*this, fPicture, bitmap, RecordTask::kOptimize_Mode)));
+    this->spawnChild(SkNEW_ARGS(WriteTask, (*this, bitmap, WriteTask::kVerbatim_Mode)));
+}
+
+}  // namespace DM
diff --git a/dm/DMSKPTask.h b/dm/DMSKPTask.h
new file mode 100644
index 0000000..ef465af
--- /dev/null
+++ b/dm/DMSKPTask.h
@@ -0,0 +1,30 @@
+#ifndef DMSKPTask_DEFINED
+#define DMSKPTask_DEFINED
+
+#include "DMReporter.h"
+#include "DMTask.h"
+#include "DMTaskRunner.h"
+#include "SkPicture.h"
+#include "SkString.h"
+#include "SkTemplates.h"
+
+// Draws an SKP to a raster canvas, then compares it with some other modes.
+
+namespace DM {
+
+class SKPTask : public CpuTask {
+public:
+    SKPTask(Reporter*, TaskRunner*, SkPicture*, SkString name);
+
+    virtual void draw() SK_OVERRIDE;
+    virtual bool shouldSkip() const SK_OVERRIDE { return false; }
+    virtual SkString name() const SK_OVERRIDE { return fName; }
+
+private:
+    SkAutoTUnref<SkPicture> fPicture;
+    const SkString fName;
+};
+
+}  // namespace DM
+
+#endif // DMSKPTask_DEFINED
diff --git a/dm/DMUtil.cpp b/dm/DMUtil.cpp
index 9a4765a..5c75171 100644
--- a/dm/DMUtil.cpp
+++ b/dm/DMUtil.cpp
@@ -26,14 +26,18 @@
     bitmap->eraseColor(0x00000000);
 }
 
-void SetupBitmap(const SkColorType ct, skiagm::GM* gm, SkBitmap* bitmap) {
+void SetupBitmap(SkColorType ct, skiagm::GM* gm, SkBitmap* bitmap) {
     setup_bitmap(ct, gm->getISize().width(), gm->getISize().height(), bitmap);
 }
 
-void SetupBitmap(const SkColorType ct, SkBenchmark* bench, SkBitmap* bitmap) {
+void SetupBitmap(SkColorType ct, SkBenchmark* bench, SkBitmap* bitmap) {
     setup_bitmap(ct, bench->getSize().x(), bench->getSize().y(), bitmap);
 }
 
+void SetupBitmap(SkColorType ct, const SkPicture& pic, SkBitmap* bitmap) {
+    setup_bitmap(ct, pic.width(), pic.height(), bitmap);
+}
+
 void DrawPicture(SkPicture* picture, SkBitmap* bitmap) {
     SkASSERT(picture != NULL);
     SkASSERT(bitmap != NULL);
diff --git a/dm/DMUtil.h b/dm/DMUtil.h
index 1145f28..c2083ba 100644
--- a/dm/DMUtil.h
+++ b/dm/DMUtil.h
@@ -20,10 +20,11 @@
                          uint32_t recordFlags = 0,
                          SkBBHFactory* factory = NULL);
 
-// Prepare bitmap to have gm or bench draw into it with this config.
+// Prepare bitmap to have gm, bench or picture draw into it with this config.
 // TODO(mtklein): make SkBenchmark::getSize()/GM::getISize() const.
-void SetupBitmap(const SkColorType, skiagm::GM* gm, SkBitmap* bitmap);
-void SetupBitmap(const SkColorType, SkBenchmark* bench, SkBitmap* bitmap);
+void SetupBitmap(SkColorType, skiagm::GM* gm, SkBitmap* bitmap);
+void SetupBitmap(SkColorType, SkBenchmark* bench, SkBitmap* bitmap);
+void SetupBitmap(SkColorType, const SkPicture& picture, SkBitmap* bitmap);
 
 // Draw picture to bitmap.
 void DrawPicture(SkPicture* picture, SkBitmap* bitmap);
diff --git a/dm/DMWriteTask.cpp b/dm/DMWriteTask.cpp
index f7fa014..cfac415 100644
--- a/dm/DMWriteTask.cpp
+++ b/dm/DMWriteTask.cpp
@@ -26,11 +26,16 @@
     return consumed;
 }
 
-WriteTask::WriteTask(const Task& parent, SkBitmap bitmap) : CpuTask(parent), fBitmap(bitmap) {
-    const int suffixes = parent.depth() + 1;
-    const SkString& name = parent.name();
-    const int totalSuffixLength = split_suffixes(suffixes, name.c_str(), &fSuffixes);
-    fGmName.set(name.c_str(), name.size()-totalSuffixLength);
+WriteTask::WriteTask(const Task& parent, SkBitmap bitmap, Mode mode)
+    : CpuTask(parent), fBitmap(bitmap) {
+    if (mode == kVerbatim_Mode) {
+        fGmName.set(parent.name());
+    } else {
+        const int suffixes = parent.depth() + 1;
+        const SkString& name = parent.name();
+        const int totalSuffixLength = split_suffixes(suffixes, name.c_str(), &fSuffixes);
+        fGmName.set(name.c_str(), name.size()-totalSuffixLength);
+    }
 }
 
 void WriteTask::makeDirOrFail(SkString dir) {
diff --git a/dm/DMWriteTask.h b/dm/DMWriteTask.h
index 839abd7..121dc0d 100644
--- a/dm/DMWriteTask.h
+++ b/dm/DMWriteTask.h
@@ -15,8 +15,13 @@
 class WriteTask : public CpuTask {
 
 public:
-    WriteTask(const Task& parent,  // WriteTask must be a child Task.  Pass its parent here.
-              SkBitmap bitmap);    // Bitmap to write.
+    enum Mode {
+        kParseName_Mode,  // Parse the parent's name into directories by underscores.
+        kVerbatim_Mode,   // Don't parse the name at all.
+    };
+    WriteTask(const Task& parent,    // WriteTask must be a child Task.  Pass its parent here.
+              SkBitmap bitmap,       // Bitmap to write.
+              Mode = kParseName_Mode);
 
     virtual void draw() SK_OVERRIDE;
     virtual bool shouldSkip() const SK_OVERRIDE;
diff --git a/gyp/dm.gyp b/gyp/dm.gyp
index 4cfcdc6..ac72aee 100644
--- a/gyp/dm.gyp
+++ b/gyp/dm.gyp
@@ -38,6 +38,7 @@
             '../dm/DMReplayTask.cpp',
             '../dm/DMReporter.cpp',
             '../dm/DMSerializeTask.cpp',
+            '../dm/DMSKPTask.cpp',
             '../dm/DMTask.cpp',
             '../dm/DMTaskRunner.cpp',
             '../dm/DMTestTask.cpp',
diff --git a/src/record/SkRecordOpts.cpp b/src/record/SkRecordOpts.cpp
index 1322c0f..a0bce23 100644
--- a/src/record/SkRecordOpts.cpp
+++ b/src/record/SkRecordOpts.cpp
@@ -17,7 +17,8 @@
     // TODO(mtklein): fuse independent optimizations to reduce number of passes?
     SkRecordNoopCulls(record);
     SkRecordNoopSaveRestores(record);
-    SkRecordNoopSaveLayerDrawRestores(record);
+    // TODO(mtklein): figure out why we draw differently and reenable
+    //SkRecordNoopSaveLayerDrawRestores(record);
 
     SkRecordAnnotateCullingPairs(record);
     SkRecordReduceDrawPosTextStrength(record);  // Helpful to run this before BoundDrawPosTextH.