Index: dm/DM.cpp |
diff --git a/dm/DM.cpp b/dm/DM.cpp |
index 7113ad839a02e4e61cc028adf608c389e389edea..df7c32f9e7bc91d28608148f94bb50ffd1f5ddd7 100644 |
--- a/dm/DM.cpp |
+++ b/dm/DM.cpp |
@@ -1,293 +1,443 @@ |
-// 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 "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 "Timer.h" |
-#include <ctype.h> |
+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_string(src, "gm skp", "Source types to test. TEMPORARILY DISABLED"); |
+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."); |
-using skiagm::GM; |
-using skiagm::GMRegistry; |
-using skiatest::Test; |
-using skiatest::TestRegistry; |
+__SK_FORCE_IMAGE_DECODER_LINKING; |
+using namespace DM; |
-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?"); |
+SK_DECLARE_STATIC_MUTEX(gFailuresMutex); |
+static SkTArray<SkString> gFailures; |
-__SK_FORCE_IMAGE_DECODER_LINKING; |
+static void fail(ImplicitString err) { |
+ SkAutoMutexAcquire lock(gFailuresMutex); |
+ SkDebugf("\n\nFAILURE: %s\n\n", err.c_str()); |
+ gFailures.push_back(err); |
+} |
-static DM::RasterizePdfProc get_pdf_rasterizer_proc() { |
- return reinterpret_cast<DM::RasterizePdfProc>( |
- FLAGS_rasterPDF ? RASTERIZE_PDF_PROC : NULL); |
+static int32_t gPending = 0; // Atomic. |
+ |
+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]); |
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ |
+ |
+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; |
} |
- return s; |
} |
-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__))); \ |
+static void gather_srcs() { |
+ for (const skiagm::GMRegistry* r = skiagm::GMRegistry::Head(); r; r = r->next()) { |
+ push_src("gm", new GMSrc(r->factory())); |
} |
- 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()); |
+ 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]))); |
+ 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 find_files(const char* dir, |
- const char* suffixes[], |
- size_t num_suffixes, |
- SkTArray<SkString>* files) { |
- if (0 == strcmp(dir, "")) { |
+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; |
} |
- 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())); |
- } |
+ 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 |
+ } |
+ |
+ 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 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); |
+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; |
} |
- 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); |
+ if (sink) { |
+ push_sink(config, sink); |
} |
- |
- 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 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); |
+// 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); |
+ } |
+ } |
} |
- 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*/))); |
+ 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); |
-static void report_failures(const SkTArray<SkString>& failures) { |
- if (failures.count() == 0) { |
- return; |
+ 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; |
+ } |
+ } |
} |
+}; |
- SkDebugf("Failures:\n"); |
- for (int i = 0; i < failures.count(); i++) { |
- SkDebugf(" %s\n", failures[i].c_str()); |
+// 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); |
} |
- 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; |
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ |
+ |
+// 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()*/) { // TEMPORARILY DISABLED |
+ continue; |
+ } |
+ if (!test->isGPUTest() && !FLAGS_cpu) { |
+ continue; |
+ } |
+ test->setReporter(&gTestReporter); |
+ gTests.push_back().reset(test.detach()); |
+ } |
} |
-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 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, "unit", "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 |
- |
- SkTArray<SkString> configs; |
- for (int i = 0; i < FLAGS_config.count(); i++) { |
- SkStrSplit(FLAGS_config[i], ", ", &configs); |
+ gather_srcs(); |
+ gather_sinks(); |
+ gather_tests(); |
+ |
+ 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); |
- } |
+ // At this point we're back in single-threaded land. |
- SkTDArray<TestRegistry::Factory> tests; |
- if (FLAGS_tests) { |
- append_matching_factories<Test>(TestRegistry::Head(), &tests); |
+ if (!FLAGS_verbose) { |
+ SkDebugf("\n"); |
} |
+ JsonWriter::DumpJson(); |
- 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); |
+ if (gFailures.count() > 0) { |
+ SkDebugf("Failures:\n"); |
+ for (int i = 0; i < gFailures.count(); i++) { |
+ SkDebugf("\t%s", gFailures[i].c_str()); |
+ } |
+ SkDebugf("%d failures\n", gFailures.count()); |
+ return 1; |
} |
- |
- 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(); |
+ if (gPending > 0) { |
+ SkDebugf("Hrm, we didn't seem to run everything we intended to! Please file a bug.\n"); |
+ return 1; |
} |
-#endif |
- |
- SkTArray<SkString> failures; |
- reporter.getFailures(&failures); |
- report_failures(failures); |
- return failures.count() > 0; |
+ return 0; |
} |
#if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL) |