| 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)
|
|
|