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
Review URL: https://codereview.chromium.org/788243008
diff --git a/dm/DM.cpp b/dm/DM.cpp
index 7113ad8..d8efc58 100644
--- a/dm/DM.cpp
+++ b/dm/DM.cpp
@@ -1,293 +1,423 @@
-// Main binary for DM.
-// For a high-level overview, please see dm/README.
-
#include "CrashHandler.h"
-#include "LazyDecodeBitmap.h"
+#include "DMJsonWriter.h"
+#include "DMSrcSink.h"
+#include "OverwriteLine.h"
+#include "ProcStats.h"
+#include "SkBBHFactory.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 "gm.h"
-#include "sk_tool_utils.h"
-#include "sk_tool_utils_flags.h"
+#include "Timer.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_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?");
+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.");
__SK_FORCE_IMAGE_DECODER_LINKING;
+using namespace DM;
-static DM::RasterizePdfProc get_pdf_rasterizer_proc() {
- return reinterpret_cast<DM::RasterizePdfProc>(
- FLAGS_rasterPDF ? RASTERIZE_PDF_PROC : NULL);
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+static int gPending = 0, gFailures = 0;
+
+static void fail(ImplicitString err) {
+ SkDebugf("\n\nERROR: %s\n\n", err.c_str());
+ sk_atomic_inc(&gFailures);
}
-// "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;
+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());
}
-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__))); \
+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;
}
- 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());
+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())));
}
}
-#undef START
-}
-
-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 {
- tasks->add(SkNEW_ARGS(DM::CpuTestTask, (reporter, tasks, tests[i])));
- }
- }
-}
-
-static void find_files(const char* dir,
- const char* suffixes[],
- size_t num_suffixes,
- SkTArray<SkString>* files) {
- if (0 == strcmp(dir, "")) {
- return;
- }
-
- 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 (!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 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())));
- }
+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 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 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;
-
-static void report_failures(const SkTArray<SkString>& failures) {
- if (failures.count() == 0) {
+ SkBitmap bitmap;
+ SkDynamicMemoryWStream stream;
+ Error err = sink->draw(noop, &bitmap, &stream);
+ if (!err.isEmpty()) {
+ SkDebugf("Skipping %s: %s\n", tag, err.c_str());
return;
}
- SkDebugf("Failures:\n");
- for (int i = 0; i < failures.count(); i++) {
- SkDebugf(" %s\n", failures[i].c_str());
+ 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;
+#endif
+}
+
+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
}
- SkDebugf("%d failures.\n", failures.count());
+
+ 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 GrGLStandard get_gl_standard() {
- if (FLAGS_gpuAPI.contains(kGpuAPINameGL)) {
- return kGL_GrGLStandard;
- }
- if (FLAGS_gpuAPI.contains(kGpuAPINameGLES)) {
- return kGLES_GrGLStandard;
- }
- return kNone_GrGLStandard;
+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;
}
-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();
+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.
+ }
+ } 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);
+ }
+
+ 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) {
+ 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;
+ }
+ 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()));
+ }
+ }
+ timer.end();
+ done(timer.fWall, "test", "", test->getName());
+}
+
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
int dm_main();
int dm_main() {
SetupCrashHandler();
SkAutoGraphics ag;
SkTaskGroup::Enabler enabled(FLAGS_threads);
- if (FLAGS_dryRun || FLAGS_veryVerbose) {
- FLAGS_verbose = true;
- }
-#if SK_ENABLE_INST_COUNT
- gPrintInstCount = FLAGS_leaks;
-#endif
+ gather_srcs();
+ gather_sinks();
+ gather_tests();
- SkTArray<SkString> configs;
- for (int i = 0; i < FLAGS_config.count(); i++) {
- SkStrSplit(FLAGS_config[i], ", ", &configs);
+ 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]));
+ }
}
- GrGLStandard gpuAPI = get_gl_standard();
+ 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();
- SkTDArray<GMRegistry::Factory> gms;
- if (FLAGS_gms) {
- append_matching_factories<GM>(GMRegistry::Head(), &gms);
+ if (!FLAGS_verbose) {
+ SkDebugf("\n");
}
- 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;
+ JsonWriter::DumpJson();
+ return gPending == 0 && gFailures == 0 ? 0 : 1;
}
#if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL)