Revert of Sketch DM refactor. (patchset #45 id:850001 of https://codereview.chromium.org/788243008/)

Reason for revert:
plenty of data

Original issue's description:
> Sketch DM refactor.
>
> BUG=skia:3255
>
>
> I think this supports everything DM used to, but has completely refactored how
> it works to fit the design in the bug.
>
> Configs like "tiles-gpu" are automatically wired up.
>
> I wouldn't suggest looking at this as a diff.  There's just a bunch of deleted
> files, a few new files, and one new file that shares a name with a deleted file
> (DM.cpp).
>
> NOTREECHECKS=true
>
> Committed: https://skia.googlesource.com/skia/+/709d2c3e5062c5b57f91273bfc11a751f5b2bb88

TBR=bsalomon@google.com,mtklein@chromium.org
NOTREECHECKS=true
NOTRY=true
BUG=skia:3255

Review URL: https://codereview.chromium.org/853883004
diff --git a/dm/DM.cpp b/dm/DM.cpp
index d8efc58..7113ad8 100644
--- a/dm/DM.cpp
+++ b/dm/DM.cpp
@@ -1,384 +1,222 @@
+// Main binary for DM.
+// For a high-level overview, please see dm/README.
+
 #include "CrashHandler.h"
-#include "DMJsonWriter.h"
-#include "DMSrcSink.h"
-#include "OverwriteLine.h"
-#include "ProcStats.h"
-#include "SkBBHFactory.h"
+#include "LazyDecodeBitmap.h"
 #include "SkCommonFlags.h"
 #include "SkForceLinking.h"
 #include "SkGraphics.h"
-#include "SkMD5.h"
 #include "SkOSFile.h"
+#include "SkPicture.h"
+#include "SkString.h"
 #include "SkTaskGroup.h"
 #include "Test.h"
-#include "Timer.h"
+#include "gm.h"
+#include "sk_tool_utils.h"
+#include "sk_tool_utils_flags.h"
 
+#include "DMCpuGMTask.h"
+#include "DMGpuGMTask.h"
+#include "DMGpuSupport.h"
+#include "DMImageTask.h"
+#include "DMJsonWriter.h"
+#include "DMPDFTask.h"
+#include "DMPDFRasterizeTask.h"
+#include "DMReporter.h"
+#include "DMSKPTask.h"
+#include "DMTask.h"
+#include "DMTaskRunner.h"
+#include "DMTestTask.h"
+
+#ifdef SK_BUILD_POPPLER
+#  include "SkPDFRasterizer.h"
+#  define RASTERIZE_PDF_PROC SkPopplerRasterizePDF
+#elif defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
+#  include "SkCGUtils.h"
+#  define RASTERIZE_PDF_PROC SkPDFDocumentToBitmap
+#else
+#  define RASTERIZE_PDF_PROC NULL
+#endif
+
+#include <ctype.h>
+
+using skiagm::GM;
+using skiagm::GMRegistry;
+using skiatest::Test;
+using skiatest::TestRegistry;
+
+static const char kGpuAPINameGL[] = "gl";
+static const char kGpuAPINameGLES[] = "gles";
+
+DEFINE_bool(gms, true, "Run GMs?");
 DEFINE_bool(tests, true, "Run tests?");
-DEFINE_string(images, "resources", "Images to decode.");
-DEFINE_string(src, "gm skp image subset", "Source types to test.");
-DEFINE_bool(nameByHash, false,
-            "If true, write to FLAGS_writePath[0]/<hash>.png instead of "
-            "to FLAGS_writePath[0]/<config>/<sourceType>/<name>.png");
-DEFINE_bool2(pathOpsExtended, x, false, "Run extended pathOps tests.");
-DEFINE_string(matrix, "1 0 0 0 1 0 0 0 1",
-              "Matrix to apply when using 'matrix' in config.");
+DEFINE_bool(reportUsedChars, false, "Output test font construction data to be pasted into"
+                                    " create_test_font.cpp.");
+DEFINE_string(images, "resources", "Path to directory containing images to decode.");
+DEFINE_bool(rasterPDF, true, "Rasterize PDFs?");
 
 __SK_FORCE_IMAGE_DECODER_LINKING;
-using namespace DM;
 
-/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-
-static int gPending = 0, gFailures = 0;
-
-static void fail(ImplicitString err) {
-    SkDebugf("\n\nERROR: %s\n\n", err.c_str());
-    sk_atomic_inc(&gFailures);
+static DM::RasterizePdfProc get_pdf_rasterizer_proc() {
+    return reinterpret_cast<DM::RasterizePdfProc>(
+            FLAGS_rasterPDF ? RASTERIZE_PDF_PROC : NULL);
 }
 
-static void done(double ms, ImplicitString config, ImplicitString src, ImplicitString name) {
-    SkDebugf("%s(%4dMB %5d) %s\t%s %s %s  ", FLAGS_verbose ? "\n" : kSkOverwriteLine
-                                           , sk_tools::getMaxResidentSetSizeMB()
-                                           , sk_atomic_dec(&gPending)-1
-                                           , HumanizeMs(ms).c_str()
-                                           , config.c_str()
-                                           , src.c_str()
-                                           , name.c_str());
+// "FooBar" -> "foobar".  Obviously, ASCII only.
+static SkString lowercase(SkString s) {
+    for (size_t i = 0; i < s.size(); i++) {
+        s[i] = tolower(s[i]);
+    }
+    return s;
 }
 
-/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-
-template <typename T>
-struct Tagged : public SkAutoTDelete<T> { const char* tag; };
-
-static const bool kMemcpyOK = true;
-
-static SkTArray<Tagged<Src>,  kMemcpyOK>  gSrcs;
-static SkTArray<Tagged<Sink>, kMemcpyOK> gSinks;
-
-static void push_src(const char* tag, Src* s) {
-    SkAutoTDelete<Src> src(s);
-    if (FLAGS_src.contains(tag) &&
-        !SkCommandLineFlags::ShouldSkip(FLAGS_match, src->name().c_str())) {
-        Tagged<Src>& s = gSrcs.push_back();
-        s.reset(src.detach());
-        s.tag = tag;
-    }
-}
-
-static void gather_srcs() {
-    for (const skiagm::GMRegistry* r = skiagm::GMRegistry::Head(); r; r = r->next()) {
-        push_src("gm", new GMSrc(r->factory()));
-    }
-    if (!FLAGS_skps.isEmpty()) {
-        SkOSFile::Iter it(FLAGS_skps[0], "skp");
-        for (SkString file; it.next(&file); ) {
-            push_src("skp", new SKPSrc(SkOSPath::Join(FLAGS_skps[0], file.c_str())));
-        }
-    }
-    if (!FLAGS_images.isEmpty()) {
-        const char* exts[] = {
-            "bmp", "gif", "jpg", "jpeg", "png", "webp", "ktx", "astc", "wbmp", "ico",
-            "BMP", "GIF", "JPG", "JPEG", "PNG", "WEBP", "KTX", "ASTC", "WBMP", "ICO",
-        };
-        for (size_t i = 0; i < SK_ARRAY_COUNT(exts); i++) {
-            SkOSFile::Iter it(FLAGS_images[0], exts[i]);
-            for (SkString file; it.next(&file); ) {
-                SkString path = SkOSPath::Join(FLAGS_images[0], file.c_str());
-                push_src("image",  new ImageSrc(path));     // Decode entire image.
-                push_src("subset", new ImageSrc(path, 5));  // Decode 5 random subsets.
-            }
-        }
-    }
-}
-
-static GrGLStandard get_gpu_api() {
-    if (FLAGS_gpuAPI.contains("gl"))   { return kGL_GrGLStandard; }
-    if (FLAGS_gpuAPI.contains("gles")) { return kGLES_GrGLStandard; }
-    return kNone_GrGLStandard;
-}
-
-static void push_sink(const char* tag, Sink* s) {
-    SkAutoTDelete<Sink> sink(s);
-    if (!FLAGS_config.contains(tag)) {
-        return;
-    }
-    // Try a noop Src as a canary.  If it fails, skip this sink.
-    struct : public Src {
-        Error draw(SkCanvas*) const SK_OVERRIDE { return ""; }
-        SkISize size() const SK_OVERRIDE { return SkISize::Make(16, 16); }
-        Name name() const SK_OVERRIDE { return "noop"; }
-    } noop;
-
-    SkBitmap bitmap;
-    SkDynamicMemoryWStream stream;
-    Error err = sink->draw(noop, &bitmap, &stream);
-    if (!err.isEmpty()) {
-        SkDebugf("Skipping %s: %s\n", tag, err.c_str());
-        return;
-    }
-
-    Tagged<Sink>& ts = gSinks.push_back();
-    ts.reset(sink.detach());
-    ts.tag = tag;
-}
-
-static bool gpu_supported() {
-#if SK_SUPPORT_GPU
-    return FLAGS_gpu;
-#else
-    return false;
+static const GrContextFactory::GLContextType native = GrContextFactory::kNative_GLContextType;
+static const GrContextFactory::GLContextType nvpr   = GrContextFactory::kNVPR_GLContextType;
+static const GrContextFactory::GLContextType null   = GrContextFactory::kNull_GLContextType;
+static const GrContextFactory::GLContextType debug  = GrContextFactory::kDebug_GLContextType;
+#if SK_ANGLE
+static const GrContextFactory::GLContextType angle  = GrContextFactory::kANGLE_GLContextType;
 #endif
+#if SK_MESA
+static const GrContextFactory::GLContextType mesa   = GrContextFactory::kMESA_GLContextType;
+#endif
+
+static void kick_off_gms(const SkTDArray<GMRegistry::Factory>& gms,
+                         const SkTArray<SkString>& configs,
+                         GrGLStandard gpuAPI,
+                         DM::Reporter* reporter,
+                         DM::TaskRunner* tasks) {
+#define START(name, type, ...)                                                              \
+    if (lowercase(configs[j]).equals(name)) {                                               \
+        tasks->add(SkNEW_ARGS(DM::type, (name, reporter, tasks, gms[i], ## __VA_ARGS__)));  \
+    }
+    for (int i = 0; i < gms.count(); i++) {
+        for (int j = 0; j < configs.count(); j++) {
+
+            START("565",        CpuGMTask, kRGB_565_SkColorType);
+            START("8888",       CpuGMTask, kN32_SkColorType);
+            START("gpu",        GpuGMTask, native, gpuAPI, 0,  false);
+            START("msaa4",      GpuGMTask, native, gpuAPI, 4,  false);
+            START("msaa16",     GpuGMTask, native, gpuAPI, 16, false);
+            START("nvprmsaa4",  GpuGMTask, nvpr,   gpuAPI, 4,  false);
+            START("nvprmsaa16", GpuGMTask, nvpr,   gpuAPI, 16, false);
+            START("gpudft",     GpuGMTask, native, gpuAPI, 0,  true);
+            START("gpunull",    GpuGMTask, null,   gpuAPI, 0,  false);
+            START("gpudebug",   GpuGMTask, debug,  gpuAPI, 0,  false);
+#if SK_ANGLE
+            START("angle",      GpuGMTask, angle,  gpuAPI, 0,  false);
+#endif
+#if SK_MESA
+            START("mesa",       GpuGMTask, mesa,   gpuAPI, 0,  false);
+#endif
+            START("pdf",        PDFTask,   get_pdf_rasterizer_proc());
+        }
+    }
+#undef START
 }
 
-static Sink* create_sink(const char* tag) {
-#define SINK(t, sink, ...) if (0 == strcmp(t, tag)) { return new sink(__VA_ARGS__); }
-    if (gpu_supported()) {
-        const GrGLStandard api = get_gpu_api();
-        SINK("gpunull",    GPUSink, GrContextFactory::kNull_GLContextType,   api,  0, false);
-        SINK("gpudebug",   GPUSink, GrContextFactory::kDebug_GLContextType,  api,  0, false);
-        SINK("gpu",        GPUSink, GrContextFactory::kNative_GLContextType, api,  0, false);
-        SINK("gpudft",     GPUSink, GrContextFactory::kNative_GLContextType, api,  0,  true);
-        SINK("msaa4",      GPUSink, GrContextFactory::kNative_GLContextType, api,  4, false);
-        SINK("msaa16",     GPUSink, GrContextFactory::kNative_GLContextType, api, 16, false);
-        SINK("nvprmsaa4",  GPUSink, GrContextFactory::kNVPR_GLContextType,   api,  4, false);
-        SINK("nvprmsaa16", GPUSink, GrContextFactory::kNVPR_GLContextType,   api, 16, false);
-    #if SK_ANGLE
-        SINK("angle",      GPUSink, GrContextFactory::kANGLE_GLContextType,  api,  0, false);
-    #endif
-    #if SK_MESA
-        SINK("mesa",       GPUSink, GrContextFactory::kMESA_GLContextType,   api,  0, false);
-    #endif
-    }
-
-    if (FLAGS_cpu) {
-        SINK("565",  RasterSink, kRGB_565_SkColorType);
-        SINK("8888", RasterSink, kN32_SkColorType);
-        // TODO(mtklein): reenable once skiagold can handle .pdf uploads.
-        //SINK("pdf",  PDFSink);
-    }
-#undef SINK
-    return NULL;
-}
-
-static Sink* create_via(const char* tag, Sink* wrapped) {
-#define VIA(t, via, ...) if (0 == strcmp(t, tag)) { return new via(__VA_ARGS__); }
-    VIA("serialize", ViaSerialization, wrapped);
-
-    VIA("tiles",    ViaTiles, 256, 256,               NULL, wrapped);
-    VIA("tiles_rt", ViaTiles, 256, 256, new SkRTreeFactory, wrapped);
-
-    const int xp = SkGPipeWriter::kCrossProcess_Flag,
-              sa = xp | SkGPipeWriter::kSharedAddressSpace_Flag;
-    VIA("pipe",    ViaPipe,  0, wrapped);
-    VIA("pipe_xp", ViaPipe, xp, wrapped);
-    VIA("pipe_sa", ViaPipe, sa, wrapped);
-
-    if (FLAGS_matrix.count() == 9) {
-        SkMatrix m;
-        for (int i = 0; i < 9; i++) {
-            m[i] = (SkScalar)atof(FLAGS_matrix[i]);
-        }
-        VIA("matrix", ViaMatrix, m, wrapped);
-    }
-#undef VIA
-    return NULL;
-}
-
-static void gather_sinks() {
-    for (int i = 0; i < FLAGS_config.count(); i++) {
-        const char* config = FLAGS_config[i];
-        SkTArray<SkString> parts;
-        SkStrSplit(config, "-", &parts);
-
-        Sink* sink = NULL;
-        for (int i = parts.count(); i-- > 0;) {
-            const char* part = parts[i].c_str();
-            Sink* next = (sink == NULL) ? create_sink(part) : create_via(part, sink);
-            if (next == NULL) {
-                SkDebugf("Skipping %s: Don't understand '%s'.\n", config, part);
-                delete sink;
-                sink = NULL;
-                break;
-            }
-            sink = next;
-        }
-        if (sink) {
-            push_sink(config, sink);
-        }
-    }
-}
-
-// The finest-grained unit of work we can run: draw a single Src into a single Sink,
-// report any errors, and perhaps write out the output: a .png of the bitmap, or a raw stream.
-struct Task {
-    Task(const Tagged<Src>& src, const Tagged<Sink>& sink) : src(src), sink(sink) {}
-    const Tagged<Src>&  src;
-    const Tagged<Sink>& sink;
-
-    static void Run(Task* task) {
-        WallTimer timer;
-        timer.start();
-        if (!FLAGS_dryRun) {
-            SkBitmap bitmap;
-            SkDynamicMemoryWStream stream;
-            Error err = task->sink->draw(*task->src, &bitmap, &stream);
-            if (!err.isEmpty()) {
-                fail(SkStringPrintf("%s %s %s: %s",
-                                    task->sink.tag,
-                                    task->src.tag,
-                                    task->src->name().c_str(),
-                                    err.c_str()));
-            }
-            if (!FLAGS_writePath.isEmpty()) {
-                const char* ext = task->sink->fileExtension();
-                if (stream.bytesWritten() == 0) {
-                    SkMemoryStream pixels(bitmap.getPixels(), bitmap.getSize());
-                    WriteToDisk(*task, &pixels, bitmap.getSize(), &bitmap, ext);
-                } else {
-                    SkAutoTDelete<SkStreamAsset> data(stream.detachAsStream());
-                    WriteToDisk(*task, data, data->getLength(), NULL, ext);
-                }
-            }
-        }
-        timer.end();
-        done(timer.fWall, task->sink.tag, task->src.tag, task->src->name());
-    }
-
-    static void WriteToDisk(const Task& task,
-                            SkStream* data, size_t len,
-                            const SkBitmap* bitmap,
-                            const char* ext) {
-        SkMD5 hash;
-        hash.writeStream(data, len);
-        SkMD5::Digest digest;
-        hash.finish(digest);
-
-        JsonWriter::BitmapResult result;
-        result.name       = task.src->name();
-        result.config     = task.sink.tag;
-        result.sourceType = task.src.tag;
-        result.ext        = ext;
-        for (int i = 0; i < 16; i++) {
-            result.md5.appendf("%02x", digest.data[i]);
-        }
-        JsonWriter::AddBitmapResult(result);
-
-        const char* dir = FLAGS_writePath[0];
-        if (0 == strcmp(dir, "@")) {  // Needed for iOS.
-            dir = FLAGS_resourcePath[0];
-        }
-        sk_mkdir(dir);
-
-        SkString path;
-        if (FLAGS_nameByHash) {
-            path = SkOSPath::Join(dir, result.md5.c_str());
-            path.append(".");
-            path.append(ext);
-            if (sk_exists(path.c_str())) {
-                return;  // Content-addressed.  If it exists already, we're done.
-            }
+static void kick_off_tests(const SkTDArray<TestRegistry::Factory>& tests,
+                           DM::Reporter* reporter,
+                           DM::TaskRunner* tasks) {
+    for (int i = 0; i < tests.count(); i++) {
+        SkAutoTDelete<Test> test(tests[i](NULL));
+        if (test->isGPUTest()) {
+            tasks->add(SkNEW_ARGS(DM::GpuTestTask, (reporter, tasks, tests[i])));
         } else {
-            path = SkOSPath::Join(dir, task.sink.tag);
-            sk_mkdir(path.c_str());
-            path = SkOSPath::Join(path.c_str(), task.src.tag);
-            sk_mkdir(path.c_str());
-            path = SkOSPath::Join(path.c_str(), task.src->name().c_str());
-            path.append(".");
-            path.append(ext);
+            tasks->add(SkNEW_ARGS(DM::CpuTestTask, (reporter, tasks, tests[i])));
         }
-
-        SkFILEWStream file(path.c_str());
-        if (!file.isValid()) {
-            fail(SkStringPrintf("Can't open %s for writing.\n", path.c_str()));
-            return;
-        }
-
-        data->rewind();
-        if (bitmap) {
-            // We can't encode A8 bitmaps as PNGs.  Convert them to 8888 first.
-            SkBitmap converted;
-            if (bitmap->info().colorType() == kAlpha_8_SkColorType) {
-                if (!bitmap->copyTo(&converted, kN32_SkColorType)) {
-                    fail("Can't convert A8 to 8888.\n");
-                    return;
-                }
-                bitmap = &converted;
-            }
-            if (!SkImageEncoder::EncodeStream(&file, *bitmap, SkImageEncoder::kPNG_Type, 100)) {
-                fail(SkStringPrintf("Can't encode PNG to %s.\n", path.c_str()));
-                return;
-            }
-        } else {
-            if (!file.writeStream(data, len)) {
-                fail(SkStringPrintf("Can't write to %s.\n", path.c_str()));
-                return;
-            }
-        }
-    }
-};
-
-// Run all tasks in the same enclave serially on the same thread.
-// They can't possibly run concurrently with each other.
-static void run_enclave(SkTArray<Task>* tasks) {
-    for (int i = 0; i < tasks->count(); i++) {
-        Task::Run(tasks->begin() + i);
     }
 }
 
-/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
-
-// Unit tests don't fit so well into the Src/Sink model, so we give them special treatment.
-
-static struct : public skiatest::Reporter {
-    void onReportFailed(const skiatest::Failure& failure) SK_OVERRIDE {
-        SkString s;
-        failure.getFailureString(&s);
-        fail(s);
-        JsonWriter::AddTestFailure(failure);
-    }
-    bool allowExtendedTest() const SK_OVERRIDE { return FLAGS_pathOpsExtended; }
-    bool verbose()           const SK_OVERRIDE { return FLAGS_veryVerbose; }
-} gTestReporter;
-
-static SkTArray<SkAutoTDelete<skiatest::Test>, kMemcpyOK> gTests;
-
-static void gather_tests() {
-    if (!FLAGS_tests) {
+static void find_files(const char* dir,
+                       const char* suffixes[],
+                       size_t num_suffixes,
+                       SkTArray<SkString>* files) {
+    if (0 == strcmp(dir, "")) {
         return;
     }
-    for (const skiatest::TestRegistry* r = skiatest::TestRegistry::Head(); r; r = r->next()) {
-        SkAutoTDelete<skiatest::Test> test(r->factory()(NULL));
-        if (SkCommandLineFlags::ShouldSkip(FLAGS_match, test->getName())) {
-            continue;
+
+    SkString filename;
+    for (size_t i = 0; i < num_suffixes; i++) {
+        SkOSFile::Iter it(dir, suffixes[i]);
+        while (it.next(&filename)) {
+            if (!SkCommandLineFlags::ShouldSkip(FLAGS_match, filename.c_str())) {
+                files->push_back(SkOSPath::Join(dir, filename.c_str()));
+            }
         }
-        if (test->isGPUTest() && !gpu_supported()) {
-            continue;
-        }
-        if (!test->isGPUTest() && !FLAGS_cpu) {
-            continue;
-        }
-        test->setReporter(&gTestReporter);
-        gTests.push_back().reset(test.detach());
     }
 }
 
-static void run_test(SkAutoTDelete<skiatest::Test>* t) {
-    WallTimer timer;
-    timer.start();
-    skiatest::Test* test = t->get();
-    if (!FLAGS_dryRun) {
-        GrContextFactory grFactory;
-        test->setGrContextFactory(&grFactory);
-        test->run();
-        if (!test->passed()) {
-            fail(SkStringPrintf("test %s failed", test->getName()));
+static void kick_off_skps(const SkTArray<SkString>& skps,
+                          DM::Reporter* reporter,
+                          DM::TaskRunner* tasks) {
+    for (int i = 0; i < skps.count(); ++i) {
+        SkAutoTUnref<SkStream> stream(SkStream::NewFromFile(skps[i].c_str()));
+        if (stream.get() == NULL) {
+            SkDebugf("Could not read %s.\n", skps[i].c_str());
+            exit(1);
         }
+        SkAutoTUnref<SkPicture> pic(
+                SkPicture::CreateFromStream(stream.get(), &sk_tools::LazyDecodeBitmap));
+        if (pic.get() == NULL) {
+            SkDebugf("Could not read %s as an SkPicture.\n", skps[i].c_str());
+            exit(1);
+        }
+
+        SkString filename = SkOSPath::Basename(skps[i].c_str());
+        tasks->add(SkNEW_ARGS(DM::SKPTask, (reporter, tasks, pic, filename)));
+        tasks->add(SkNEW_ARGS(DM::PDFTask, (reporter, tasks, pic, filename,
+                                            get_pdf_rasterizer_proc())));
     }
-    timer.end();
-    done(timer.fWall, "test", "", test->getName());
 }
 
-/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+static void kick_off_images(const SkTArray<SkString>& images,
+                            DM::Reporter* reporter,
+                            DM::TaskRunner* tasks) {
+    for (int i = 0; i < images.count(); i++) {
+        SkAutoTUnref<SkData> image(SkData::NewFromFileName(images[i].c_str()));
+        if (!image) {
+            SkDebugf("Could not read %s.\n", images[i].c_str());
+            exit(1);
+        }
+        SkString filename = SkOSPath::Basename(images[i].c_str());
+        tasks->add(SkNEW_ARGS(DM::ImageTask, (reporter, tasks, image, filename)));
+        tasks->add(SkNEW_ARGS(DM::ImageTask, (reporter, tasks, image, filename, 5/*subsets*/)));
+    }
+}
+
+
+static void report_failures(const SkTArray<SkString>& failures) {
+    if (failures.count() == 0) {
+        return;
+    }
+
+    SkDebugf("Failures:\n");
+    for (int i = 0; i < failures.count(); i++) {
+        SkDebugf("  %s\n", failures[i].c_str());
+    }
+    SkDebugf("%d failures.\n", failures.count());
+}
+
+static GrGLStandard get_gl_standard() {
+  if (FLAGS_gpuAPI.contains(kGpuAPINameGL)) {
+      return kGL_GrGLStandard;
+  }
+  if (FLAGS_gpuAPI.contains(kGpuAPINameGLES)) {
+      return kGLES_GrGLStandard;
+  }
+  return kNone_GrGLStandard;
+}
+
+template <typename T, typename Registry>
+static void append_matching_factories(Registry* head, SkTDArray<typename Registry::Factory>* out) {
+    for (const Registry* reg = head; reg != NULL; reg = reg->next()) {
+        SkAutoTDelete<T> forName(reg->factory()(NULL));
+        if (!SkCommandLineFlags::ShouldSkip(FLAGS_match, forName->getName())) {
+            *out->append() = reg->factory();
+        }
+    }
+}
 
 int dm_main();
 int dm_main() {
@@ -386,38 +224,70 @@
     SkAutoGraphics ag;
     SkTaskGroup::Enabler enabled(FLAGS_threads);
 
-    gather_srcs();
-    gather_sinks();
-    gather_tests();
+    if (FLAGS_dryRun || FLAGS_veryVerbose) {
+        FLAGS_verbose = true;
+    }
+#if SK_ENABLE_INST_COUNT
+    gPrintInstCount = FLAGS_leaks;
+#endif
 
-    gPending = gSrcs.count() * gSinks.count() + gTests.count();
-    SkDebugf("%d srcs * %d sinks + %d tests == %d tasks\n",
-             gSrcs.count(), gSinks.count(), gTests.count(), gPending);
-
-    // We try to exploit as much parallelism as is safe.  Most Src/Sink pairs run on any thread,
-    // but Sinks that identify as part of a particular enclave run serially on a single thread.
-    // Tests run on any thread, with a separate GrContextFactory for each GPU test.
-    SkTArray<Task> enclaves[kNumEnclaves];
-    for (int j = 0; j < gSinks.count(); j++) {
-        SkTArray<Task>& tasks = enclaves[gSinks[j]->enclave()];
-        for (int i = 0; i < gSrcs.count(); i++) {
-            tasks.push_back(Task(gSrcs[i], gSinks[j]));
-        }
+    SkTArray<SkString> configs;
+    for (int i = 0; i < FLAGS_config.count(); i++) {
+        SkStrSplit(FLAGS_config[i], ", ", &configs);
     }
 
-    SK_COMPILE_ASSERT(kAnyThread_Enclave == 0, AnyThreadZero);
-    SkTaskGroup tg;
-        tg.batch(  Task::Run, enclaves[0].begin(), enclaves[0].count());
-        tg.batch(run_enclave,          enclaves+1,      kNumEnclaves-1);
-        tg.batch(   run_test,      gTests.begin(),      gTests.count());
-    tg.wait();
+    GrGLStandard gpuAPI = get_gl_standard();
 
-    if (!FLAGS_verbose) {
-        SkDebugf("\n");
+    SkTDArray<GMRegistry::Factory> gms;
+    if (FLAGS_gms) {
+        append_matching_factories<GM>(GMRegistry::Head(), &gms);
     }
 
-    JsonWriter::DumpJson();
-    return gPending == 0 && gFailures == 0 ? 0 : 1;
+    SkTDArray<TestRegistry::Factory> tests;
+    if (FLAGS_tests) {
+        append_matching_factories<Test>(TestRegistry::Head(), &tests);
+    }
+
+
+    SkTArray<SkString> skps;
+    if (!FLAGS_skps.isEmpty()) {
+        const char* suffixes[] = { "skp" };
+        find_files(FLAGS_skps[0], suffixes, SK_ARRAY_COUNT(suffixes), &skps);
+    }
+
+    SkTArray<SkString> images;
+    if (!FLAGS_images.isEmpty()) {
+        const char* suffixes[] = {
+            "bmp", "gif", "jpg", "jpeg", "png", "webp", "ktx", "astc", "wbmp", "ico",
+            "BMP", "GIF", "JPG", "JPEG", "PNG", "WEBP", "KTX", "ASTC", "WBMP", "ICO",
+        };
+        find_files(FLAGS_images[0], suffixes, SK_ARRAY_COUNT(suffixes), &images);
+    }
+
+    SkDebugf("%d GMs x %d configs, %d tests, %d pictures, %d images\n",
+             gms.count(), configs.count(), tests.count(), skps.count(), images.count());
+    DM::Reporter reporter;
+
+    DM::TaskRunner tasks;
+    kick_off_tests(tests, &reporter, &tasks);
+    kick_off_gms(gms, configs, gpuAPI, &reporter, &tasks);
+    kick_off_skps(skps, &reporter, &tasks);
+    kick_off_images(images, &reporter, &tasks);
+    tasks.wait();
+
+    DM::JsonWriter::DumpJson();
+
+    SkDebugf("\n");
+#ifdef SK_DEBUG
+    if (FLAGS_portableFonts && FLAGS_reportUsedChars) {
+        sk_tool_utils::report_used_chars();
+    }
+#endif
+
+    SkTArray<SkString> failures;
+    reporter.getFailures(&failures);
+    report_failures(failures);
+    return failures.count() > 0;
 }
 
 #if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL)