static SkString humanize(double ms) {
if (FLAGS_verbose) return SkStringPrintf("%llu", (uint64_t)(ms*1e6));
- if (ms > 1e+3) return SkStringPrintf("%.3gs", ms/1e3);
- if (ms < 1e-3) return SkStringPrintf("%.3gns", ms*1e6);
-#ifdef SK_BUILD_FOR_WIN
- if (ms < 1) return SkStringPrintf("%.3gus", ms*1e3);
-#else
- if (ms < 1) return SkStringPrintf("%.3gµs", ms*1e3);
-#endif
- return SkStringPrintf("%.3gms", ms);
+ return HumanizeMs(ms);
}
#define HUMANIZE(ms) humanize(ms).c_str()
static const int kFlags = SkPictureRecorder::kComputeSaveLayerInfo_RecordFlag;
pic->playback(recorder.beginRecording(pic->cullRect().width(),
pic->cullRect().height(),
- &factory,
+ &factory,
fUseMPDs[fCurrentUseMPD] ? kFlags : 0));
pic.reset(recorder.endRecording());
}
-// 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_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?");
+static int gPending = 0, gFailures = 0;
-__SK_FORCE_IMAGE_DECODER_LINKING;
+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]);
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+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;
}
-
- 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()));
- }
- }
+ // 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 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);
- }
+static bool gpu_supported() {
+#if SK_SUPPORT_GPU
+ return FLAGS_gpu;
+#else
+ return false;
+#endif
+}
- 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 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
}
-}
-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*/)));
+ 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);
-static void report_failures(const SkTArray<SkString>& failures) {
- if (failures.count() == 0) {
- return;
- }
+ 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);
- SkDebugf("Failures:\n");
- for (int i = 0; i < failures.count(); i++) {
- SkDebugf(" %s\n", failures[i].c_str());
+ 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);
}
- SkDebugf("%d failures.\n", failures.count());
+#undef VIA
+ 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 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);
+ }
+ }
}
-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();
+// 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());
}
-}
-int dm_main();
-int dm_main() {
- SetupCrashHandler();
- SkAutoGraphics ag;
- SkTaskGroup::Enabler enabled(FLAGS_threads);
+ 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);
- if (FLAGS_dryRun || FLAGS_veryVerbose) {
- FLAGS_verbose = true;
+ 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;
+ }
+ }
}
-#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);
+// 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);
}
+}
- GrGLStandard gpuAPI = get_gl_standard();
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
- SkTDArray<GMRegistry::Factory> gms;
- if (FLAGS_gms) {
- append_matching_factories<GM>(GMRegistry::Head(), &gms);
- }
+// Unit tests don't fit so well into the Src/Sink model, so we give them special treatment.
- SkTDArray<TestRegistry::Factory> tests;
- if (FLAGS_tests) {
- append_matching_factories<Test>(TestRegistry::Head(), &tests);
+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;
- SkTArray<SkString> skps;
- if (!FLAGS_skps.isEmpty()) {
- const char* suffixes[] = { "skp" };
- find_files(FLAGS_skps[0], suffixes, SK_ARRAY_COUNT(suffixes), &skps);
+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());
}
+}
- 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);
+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());
+}
- 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;
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+int dm_main();
+int dm_main() {
+ SetupCrashHandler();
+ SkAutoGraphics ag;
+ SkTaskGroup::Enabler enabled(FLAGS_threads);
- 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();
+ 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]));
+ }
+ }
- DM::JsonWriter::DumpJson();
+ 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();
- SkDebugf("\n");
-#ifdef SK_DEBUG
- if (FLAGS_portableFonts && FLAGS_reportUsedChars) {
- sk_tool_utils::report_used_chars();
+ if (!FLAGS_verbose) {
+ SkDebugf("\n");
}
-#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)
+++ /dev/null
-#include "DMCpuGMTask.h"
-#include "DMPipeTask.h"
-#include "DMQuiltTask.h"
-#include "DMSerializeTask.h"
-#include "DMUtil.h"
-#include "DMWriteTask.h"
-
-namespace DM {
-
-CpuGMTask::CpuGMTask(const char* config,
- Reporter* reporter,
- TaskRunner* taskRunner,
- skiagm::GMRegistry::Factory gmFactory,
- SkColorType colorType)
- : CpuTask(reporter, taskRunner)
- , fGMFactory(gmFactory)
- , fGM(fGMFactory(NULL))
- , fName(UnderJoin(fGM->getName(), config))
- , fColorType(colorType)
- {}
-
-void CpuGMTask::draw() {
- SkBitmap bm;
- AllocatePixels(fColorType, fGM->getISize().width(), fGM->getISize().height(), &bm);
-
- SkCanvas canvas(bm);
- CanvasPreflight(&canvas);
- canvas.concat(fGM->getInitialTransform());
- fGM->draw(&canvas);
- canvas.flush();
-
-#define SPAWN(ChildTask, ...) this->spawnChild(SkNEW_ARGS(ChildTask, (*this, __VA_ARGS__)))
- SPAWN(PipeTask, fGMFactory(NULL), bm, PipeTask::kInProcess_Mode);
- SPAWN(PipeTask, fGMFactory(NULL), bm, PipeTask::kCrossProcess_Mode);
- SPAWN(PipeTask, fGMFactory(NULL), bm, PipeTask::kSharedAddress_Mode);
-
- SPAWN(QuiltTask, fGMFactory(NULL), bm, QuiltTask::kNone_BBH);
- SPAWN(QuiltTask, fGMFactory(NULL), bm, QuiltTask::kRTree_BBH);
-
- SPAWN(SerializeTask, fGMFactory(NULL), bm);
-
- SPAWN(WriteTask, "GM", bm);
-#undef SPAWN
-}
-
-bool CpuGMTask::shouldSkip() const {
- if (kRGB_565_SkColorType == fColorType && (fGM->getFlags() & skiagm::GM::kSkip565_Flag)) {
- return true;
- }
- if (fGM->getFlags() & skiagm::GM::kGPUOnly_Flag) {
- return true;
- }
- return false;
-}
-
-} // namespace DM
+++ /dev/null
-#ifndef DMCpuGMTask_DEFINED
-#define DMCpuGMTask_DEFINED
-
-#include "DMReporter.h"
-#include "DMTask.h"
-#include "DMTaskRunner.h"
-#include "SkBitmap.h"
-#include "SkString.h"
-#include "SkTemplates.h"
-#include "gm.h"
-
-// This is the main entry point for drawing GMs with the CPU. Commandline
-// flags control whether this kicks off various comparison tasks when done.
-
-namespace DM {
-
-class CpuGMTask : public CpuTask {
-public:
- CpuGMTask(const char* config,
- Reporter*,
- TaskRunner*,
- skiagm::GMRegistry::Factory,
- SkColorType);
-
- void draw() SK_OVERRIDE;
- bool shouldSkip() const SK_OVERRIDE;
- SkString name() const SK_OVERRIDE { return fName; }
-
-private:
- skiagm::GMRegistry::Factory fGMFactory;
- SkAutoTDelete<skiagm::GM> fGM;
- const SkString fName;
- const SkColorType fColorType;
-};
-
-} // namespace DM
-
-#endif // DMCpuGMTask_DEFINED
+++ /dev/null
-#include "DMGpuGMTask.h"
-#include "DMUtil.h"
-#include "DMWriteTask.h"
-#include "SkCommonFlags.h"
-#include "SkSurface.h"
-#include "SkTLS.h"
-
-namespace DM {
-
-GpuGMTask::GpuGMTask(const char* config,
- Reporter* reporter,
- TaskRunner* taskRunner,
- skiagm::GMRegistry::Factory gmFactory,
- GrContextFactory::GLContextType contextType,
- GrGLStandard gpuAPI,
- int sampleCount,
- bool useDFText)
- : GpuTask(reporter, taskRunner)
- , fGM(gmFactory(NULL))
- , fName(UnderJoin(fGM->getName(), config))
- , fContextType(contextType)
- , fGpuAPI(gpuAPI)
- , fSampleCount(sampleCount)
- , fUseDFText(useDFText)
- {}
-
-static bool gAlreadyWarned[GrContextFactory::kGLContextTypeCnt][kGrGLStandardCnt];
-
-void GpuGMTask::draw(GrContextFactory* grFactory) {
- SkImageInfo info = SkImageInfo::Make(SkScalarCeilToInt(fGM->width()),
- SkScalarCeilToInt(fGM->height()),
- kN32_SkColorType,
- kPremul_SkAlphaType);
- SkAutoTUnref<SkSurface> surface(NewGpuSurface(grFactory, fContextType, fGpuAPI, info,
- fSampleCount, fUseDFText));
- if (!surface) {
- if (!gAlreadyWarned[fContextType][fGpuAPI]) {
- SkDebugf("FYI: couldn't create GPU context, type %d API %d. Will skip.\n",
- fContextType, fGpuAPI);
- gAlreadyWarned[fContextType][fGpuAPI] = true;
- }
- return;
- }
- SkCanvas* canvas = surface->getCanvas();
- CanvasPreflight(canvas);
-
- canvas->concat(fGM->getInitialTransform());
- fGM->draw(canvas);
- canvas->flush();
-#if GR_CACHE_STATS && SK_SUPPORT_GPU
- if (FLAGS_veryVerbose) {
- grFactory->get(fContextType)->printCacheStats();
- }
-#endif
-
- SkBitmap bitmap;
- bitmap.setInfo(info);
- canvas->readPixels(&bitmap, 0, 0);
-
- this->spawnChild(SkNEW_ARGS(WriteTask, (*this, "GM", bitmap)));
-}
-
-bool GpuGMTask::shouldSkip() const {
- return kGPUDisabled || SkToBool(fGM->getFlags() & skiagm::GM::kSkipGPU_Flag);
-}
-
-} // namespace DM
+++ /dev/null
-#ifndef DMGpuGMTask_DEFINED
-#define DMGpuGMTask_DEFINED
-
-#include "DMGpuSupport.h"
-#include "DMReporter.h"
-#include "DMTask.h"
-#include "DMTaskRunner.h"
-#include "SkBitmap.h"
-#include "SkString.h"
-#include "SkTemplates.h"
-#include "gm.h"
-
-// This is the main entry point for drawing GMs with the GPU.
-
-namespace DM {
-
-class GpuGMTask : public GpuTask {
-public:
- GpuGMTask(const char* config,
- Reporter*,
- TaskRunner*,
- skiagm::GMRegistry::Factory,
- GrContextFactory::GLContextType,
- GrGLStandard gpuAPI,
- int sampleCount,
- bool useDFText);
-
- void draw(GrContextFactory*) SK_OVERRIDE;
- bool shouldSkip() const SK_OVERRIDE;
- SkString name() const SK_OVERRIDE { return fName; }
-
-private:
- SkAutoTDelete<skiagm::GM> fGM;
- const SkString fName;
- const GrContextFactory::GLContextType fContextType;
- GrGLStandard fGpuAPI;
- const int fSampleCount;
- const bool fUseDFText;
-};
-
-} // namespace DM
-
-#endif // DMGpuGMTask_DEFINED
+++ /dev/null
-#include "DMImageTask.h"
-#include "DMUtil.h"
-#include "DMWriteTask.h"
-#include "SkImageDecoder.h"
-#include "SkRandom.h"
-
-#include <string.h>
-
-namespace DM {
-
-// This converts file names like "mandrill_128.r11.ktx" into
-// "mandrill-128-r11_ktx" or "mandrill-128-r11-5-subsets_ktx".
-static SkString task_name(SkString filename, int subsets) {
- const char* ext = strrchr(filename.c_str(), '.');
- SkString name(filename.c_str(), ext - filename.c_str());
- if (subsets > 0) {
- name.appendf("_%d_subsets", subsets);
- }
- name = FileToTaskName(name); // Promote any stray '.' in the filename to '_'.
- name.append(ext); // Tack on the extension, including the '.'.
- return FileToTaskName(name); // Promote that last '.' to '_', other '_' to '-'.
-}
-
-ImageTask::ImageTask(Reporter* r, TaskRunner* t, const SkData* encoded, SkString name, int subsets)
- : CpuTask(r, t)
- , fEncoded(SkRef(encoded))
- , fName(task_name(name, subsets))
- , fSubsets(subsets) {}
-
-void ImageTask::draw() {
- if (fSubsets == 0) {
- // Decoding the whole image is considerably simpler than decoding subsets!
- SkBitmap bitmap;
- if (!SkImageDecoder::DecodeMemory(fEncoded->data(), fEncoded->size(), &bitmap)) {
- return this->fail("Can't DecodeMemory");
- }
- this->spawnChild(SkNEW_ARGS(WriteTask, (*this, "image", bitmap)));
- return;
- }
-
- SkMemoryStream stream(fEncoded->data(), fEncoded->size());
- SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(&stream));
- if (!decoder) {
- return this->fail("Can't find good decoder.");
- }
-
- int w,h;
- if (!decoder->buildTileIndex(&stream, &w, &h) || w*h == 1) {
- return; // Subset decoding is not always supported.
- }
-
- SkBitmap composite;
- composite.allocN32Pixels(w,h); // We're lazy here and just always use native 8888.
- composite.eraseColor(SK_ColorTRANSPARENT);
- SkCanvas canvas(composite);
-
- SkRandom rand;
- for (int i = 0; i < fSubsets; i++) {
- SkIRect rect;
- do {
- rect.fLeft = rand.nextULessThan(w);
- rect.fTop = rand.nextULessThan(h);
- rect.fRight = rand.nextULessThan(w);
- rect.fBottom = rand.nextULessThan(h);
- rect.sort();
- } while (rect.isEmpty());
-
- SkBitmap subset;
- if (!decoder->decodeSubset(&subset, rect, kN32_SkColorType)) {
- return this->fail("Could not decode subset.");
- }
- canvas.drawBitmap(subset, SkIntToScalar(rect.fLeft), SkIntToScalar(rect.fTop));
- }
- canvas.flush();
- this->spawnChild(SkNEW_ARGS(WriteTask, (*this, "image", composite)));
-}
-
-} // namespace DM
+++ /dev/null
-#ifndef DMImageTask_DEFINED
-#define DMImageTask_DEFINED
-
-#include "DMReporter.h"
-#include "DMTask.h"
-#include "DMTaskRunner.h"
-#include "SkData.h"
-#include "SkString.h"
-
-// Decode an image into its natural bitmap, perhaps decoding random subsets.
-
-namespace DM {
-
-class ImageTask : public CpuTask {
-public:
- ImageTask(Reporter*, TaskRunner*, const SkData*, SkString name, int subsets = 0);
-
- void draw() SK_OVERRIDE;
- bool shouldSkip() const SK_OVERRIDE { return false; }
- SkString name() const SK_OVERRIDE { return fName; }
-
-private:
- SkAutoTUnref<const SkData> fEncoded;
- const SkString fName;
- int fSubsets;
-};
-
-} // namespace DM
-
-#endif // DMImageTask_DEFINED
Json::Value result;
result["key"]["name"] = gBitmapResults[i].name.c_str();
result["key"]["config"] = gBitmapResults[i].config.c_str();
- result["key"]["mode"] = gBitmapResults[i].mode.c_str();
result["key"]["source_type"] = gBitmapResults[i].sourceType.c_str();
+ result["ext"] = gBitmapResults[i].ext.c_str();
result["md5"] = gBitmapResults[i].md5.c_str();
root["results"].append(result);
* Info describing a single run.
*/
struct BitmapResult {
- SkString name; // E.g. "ninepatch-stretch", "desk-gws_skp"
- SkString config; // "gpu", "8888"
- SkString mode; // "direct", "default-tilegrid", "pipe"
- SkString sourceType; // "GM", "SKP"
+ SkString name; // E.g. "ninepatch-stretch", "desk_gws.skp"
+ SkString config; // "gpu", "8888", "serialize", "pipe"
+ SkString sourceType; // "gm", "skp", "image"
SkString md5; // In ASCII, so 32 bytes long.
+ SkString ext; // Extension of file we wrote: "png", "pdf", ...
};
/**
+++ /dev/null
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "DMPDFRasterizeTask.h"
-#include "DMUtil.h"
-#include "DMWriteTask.h"
-#include "SkBitmap.h"
-#include "SkCanvas.h"
-#include "SkStream.h"
-
-namespace DM {
-
-PDFRasterizeTask::PDFRasterizeTask(const Task& parent,
- SkStreamAsset* pdf,
- RasterizePdfProc proc)
- : CpuTask(parent)
- , fName(UnderJoin(parent.name().c_str(), "rasterize"))
- , fPdf(pdf)
- , fRasterize(proc) {
- SkASSERT(fPdf.get());
- SkASSERT(fPdf->unique());
-}
-
-void PDFRasterizeTask::draw() {
- SkBitmap bitmap;
-
- if (fRasterize(fPdf.get(), &bitmap)) {
- this->spawnChild(SkNEW_ARGS(WriteTask, (*this, "PDF", bitmap)));
- } else {
- this->fail();
- }
-}
-
-} // namespace DM
+++ /dev/null
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef DMPDFRasterizeTask_DEFINED
-#define DMPDFRasterizeTask_DEFINED
-
-#include "DMTask.h"
-#include "SkBitmap.h"
-#include "SkData.h"
-#include "SkStream.h"
-#include "SkString.h"
-#include "SkTemplates.h"
-
-namespace DM {
-
-typedef bool (*RasterizePdfProc)(SkStream* pdf, SkBitmap* output);
-
-class PDFRasterizeTask : public CpuTask {
-public:
- // takes ownership of SkStreamAsset.
- PDFRasterizeTask(const Task& parent,
- SkStreamAsset* pdf,
- RasterizePdfProc);
-
- void draw() SK_OVERRIDE;
- bool shouldSkip() const SK_OVERRIDE { return NULL == fRasterize; }
- SkString name() const SK_OVERRIDE { return fName; }
-
-private:
- const SkString fName;
- SkAutoTDelete<SkStreamAsset> fPdf;
- RasterizePdfProc fRasterize;
-};
-
-} // namespace DM
-
-#endif // DMPDFRasterizeTask_DEFINED
+++ /dev/null
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "DMPDFTask.h"
-#include "DMPDFRasterizeTask.h"
-#include "DMUtil.h"
-#include "DMWriteTask.h"
-#include "SkCommandLineFlags.h"
-#include "SkDocument.h"
-
-// The PDF backend is not threadsafe. If you run dm with --pdf repeatedly, you
-// will quickly find yourself crashed. (while catchsegv out/Release/dm;; end).
-//
-// TODO(mtklein): re-enable by default, maybe moving to its own single thread.
-DEFINE_bool(pdf, false, "PDF backend master switch.");
-
-namespace DM {
-
-PDFTask::PDFTask(const char* config,
- Reporter* reporter,
- TaskRunner* taskRunner,
- skiagm::GMRegistry::Factory factory,
- RasterizePdfProc rasterizePdfProc)
- : CpuTask(reporter, taskRunner)
- , fGM(factory(NULL))
- , fName(UnderJoin(fGM->getName(), config))
- , fRasterize(rasterizePdfProc) {}
-
-PDFTask::PDFTask(Reporter* reporter,
- TaskRunner* taskRunner,
- const SkPicture* picture,
- SkString filename,
- RasterizePdfProc rasterizePdfProc)
- : CpuTask(reporter, taskRunner)
- , fPicture(SkRef(picture))
- , fName(UnderJoin(FileToTaskName(filename).c_str(), "pdf"))
- , fRasterize(rasterizePdfProc) {}
-
-namespace {
-
-class SinglePagePDF {
-public:
- SinglePagePDF(SkScalar width, SkScalar height)
- : fDocument(SkDocument::CreatePDF(&fWriteStream))
- , fCanvas(fDocument->beginPage(width, height)) {}
-
- SkCanvas* canvas() { return fCanvas; }
-
- SkStreamAsset* end() {
- fCanvas->flush();
- fDocument->endPage();
- fDocument->close();
- return fWriteStream.detachAsStream();
- }
-
-private:
- SkDynamicMemoryWStream fWriteStream;
- SkAutoTUnref<SkDocument> fDocument;
- SkCanvas* fCanvas;
-};
-
-} // namespace
-
-void PDFTask::draw() {
- SkAutoTDelete<SkStreamAsset> pdfData;
- bool rasterize = true;
- if (fGM.get()) {
- rasterize = 0 == (fGM->getFlags() & skiagm::GM::kSkipPDFRasterization_Flag);
- SinglePagePDF pdf(fGM->width(), fGM->height());
- CanvasPreflight(pdf.canvas());
- //TODO(mtklein): GM doesn't do this. Why not?
- //pdf.canvas()->concat(fGM->getInitialTransform());
- fGM->draw(pdf.canvas());
- pdfData.reset(pdf.end());
- } else {
- SinglePagePDF pdf(fPicture->cullRect().width(), fPicture->cullRect().height());
- CanvasPreflight(pdf.canvas());
- fPicture->playback(pdf.canvas());
- pdfData.reset(pdf.end());
- }
-
- SkASSERT(pdfData.get());
- if (rasterize) {
- this->spawnChild(SkNEW_ARGS(PDFRasterizeTask,
- (*this, pdfData->duplicate(), fRasterize)));
- }
- const char* sourceType = fGM.get() ? "GM" : "SKP";
- this->spawnChild(SkNEW_ARGS(WriteTask,
- (*this, sourceType, pdfData->duplicate(), ".pdf")));
-}
-
-bool PDFTask::shouldSkip() const {
- if (!FLAGS_pdf) {
- return true;
- }
- if (fGM.get() && 0 != (fGM->getFlags() & skiagm::GM::kSkipPDF_Flag)) {
- return true;
- }
- return false;
-}
-
-} // namespace DM
+++ /dev/null
-#ifndef DMPDFTask_DEFINED
-#define DMPDFTask_DEFINED
-
-#include "DMPDFRasterizeTask.h"
-#include "DMTask.h"
-#include "SkBitmap.h"
-#include "SkPicture.h"
-#include "SkString.h"
-#include "SkTemplates.h"
-#include "gm.h"
-
-namespace DM {
-
-// This task renders a GM or SKP using Skia's PDF backend.
-// If rasterizePdfProc is non-NULL, it will spawn a PDFRasterizeTask.
-class PDFTask : public CpuTask {
-public:
- PDFTask(const char*,
- Reporter*,
- TaskRunner*,
- skiagm::GMRegistry::Factory,
- RasterizePdfProc);
-
- PDFTask(Reporter*,
- TaskRunner*,
- const SkPicture*,
- SkString name,
- RasterizePdfProc);
-
- void draw() SK_OVERRIDE;
-
- bool shouldSkip() const SK_OVERRIDE;
-
- SkString name() const SK_OVERRIDE { return fName; }
-
-private:
- // One of these two will be set.
- SkAutoTDelete<skiagm::GM> fGM;
- SkAutoTUnref<const SkPicture> fPicture;
-
- const SkString fName;
- RasterizePdfProc fRasterize;
-};
-
-} // namespace DM
-
-#endif // DMPDFTask_DEFINED
+++ /dev/null
-#include "DMPipeTask.h"
-#include "DMUtil.h"
-#include "DMWriteTask.h"
-
-#include "SamplePipeControllers.h"
-#include "SkCommandLineFlags.h"
-#include "SkGPipe.h"
-
-DEFINE_bool(pipe, true, "If true, check several pipe variants against the reference bitmap.");
-
-namespace DM {
-
-static uint32_t get_flags(PipeTask::Mode mode) {
- uint32_t flags = 0;
- if (mode != PipeTask::kInProcess_Mode) {
- flags |= SkGPipeWriter::kCrossProcess_Flag;
- }
- if (mode == PipeTask::kSharedAddress_Mode) {
- flags |= SkGPipeWriter::kSharedAddressSpace_Flag;
- }
- return flags;
-}
-
-static const char* get_name(const uint32_t flags) {
- if (flags & SkGPipeWriter::kCrossProcess_Flag &&
- flags & SkGPipeWriter::kSharedAddressSpace_Flag) {
- return "shared-address-space-pipe";
- } else if (flags & SkGPipeWriter::kCrossProcess_Flag) {
- return "cross-process-pipe";
- } else {
- return "pipe";
- }
-}
-
-PipeTask::PipeTask(const Task& parent,
- skiagm::GM* gm,
- SkBitmap reference,
- Mode mode)
- : CpuTask(parent)
- , fFlags(get_flags(mode))
- , fName(UnderJoin(parent.name().c_str(), get_name(fFlags)))
- , fGM(gm)
- , fReference(reference)
- {}
-
-void PipeTask::draw() {
- SkBitmap bitmap;
- AllocatePixels(fReference, &bitmap);
-
- SkCanvas canvas(bitmap);
- PipeController pipeController(&canvas, &SkImageDecoder::DecodeMemory);
- SkGPipeWriter writer;
-
- SkCanvas* pipeCanvas = writer.startRecording(&pipeController,
- fFlags,
- bitmap.width(),
- bitmap.height());
- CanvasPreflight(pipeCanvas);
- pipeCanvas->concat(fGM->getInitialTransform());
- fGM->draw(pipeCanvas);
- writer.endRecording();
-
- if (!BitmapsEqual(bitmap, fReference)) {
- this->fail();
- this->spawnChild(SkNEW_ARGS(WriteTask, (*this, "GM", bitmap)));
- }
-}
-
-bool PipeTask::shouldSkip() const {
- if (!FLAGS_pipe) {
- return true;
- }
- if (fGM->getFlags() & skiagm::GM::kSkipPipe_Flag) {
- return true;
- }
- if (fFlags == SkGPipeWriter::kCrossProcess_Flag &&
- fGM->getFlags() & skiagm::GM::kSkipPipeCrossProcess_Flag) {
- return true;
- }
- return false;
-}
-
-} // namespace DM
+++ /dev/null
-#ifndef DMPipeTask_DEFINED
-#define DMPipeTask_DEFINED
-
-#include "DMTask.h"
-#include "SkBitmap.h"
-#include "SkString.h"
-#include "SkTemplates.h"
-#include "gm.h"
-
-// Sends a GM through a pipe, draws it, and compares against the reference bitmap.
-
-namespace DM {
-
-class PipeTask : public CpuTask {
-
-public:
- enum Mode {
- kInProcess_Mode,
- kCrossProcess_Mode,
- kSharedAddress_Mode,
- };
-
- PipeTask(const Task& parent, // PipeTask must be a child task. Pass its parent here.
- skiagm::GM*, // GM to run through a pipe. Takes ownership.
- SkBitmap reference, // Bitmap to compare pipe results to.
- Mode);
-
- void draw() SK_OVERRIDE;
- bool shouldSkip() const SK_OVERRIDE;
- SkString name() const SK_OVERRIDE { return fName; }
-
-private:
- const uint32_t fFlags;
- const SkString fName;
- SkAutoTDelete<skiagm::GM> fGM;
- const SkBitmap fReference;
-};
-
-} // namespace DM
-
-#endif // DMPipeTask_DEFINED
+++ /dev/null
-#include "DMQuiltTask.h"
-#include "DMUtil.h"
-#include "DMWriteTask.h"
-
-#include "SkBBHFactory.h"
-#include "SkCommandLineFlags.h"
-#include "SkPicture.h"
-#include "SkTaskGroup.h"
-
-DEFINE_bool(quilt, true, "If true, draw GM via a picture into a quilt of small tiles and compare.");
-DEFINE_int32(quiltTile, 256, "Dimension of (square) quilt tile.");
-
-namespace DM {
-
-static const char* kBBHs[] = { "nobbh", "rtree", "tilegrid" };
-QuiltTask::QuiltTask(const Task& parent, skiagm::GM* gm, SkBitmap reference, QuiltTask::BBH bbh)
- : CpuTask(parent)
- , fBBH(bbh)
- , fName(UnderJoin(parent.name().c_str(), kBBHs[bbh]))
- , fGM(gm)
- , fReference(reference)
- {}
-
-static int tiles_needed(int fullDimension, int tileDimension) {
- return (fullDimension + tileDimension - 1) / tileDimension;
-}
-
-struct DrawTileArgs {
- int x, y;
- const SkPicture* picture;
- SkBitmap* quilt;
-};
-
-static void draw_tile(DrawTileArgs* arg) {
- const DrawTileArgs& a = *arg;
- SkBitmap tile;
- a.quilt->extractSubset(&tile, SkIRect::MakeXYWH(a.x, a.y, FLAGS_quiltTile, FLAGS_quiltTile));
- SkCanvas tileCanvas(tile);
- tileCanvas.translate(SkIntToScalar(-a.x), SkIntToScalar(-a.y));
- a.picture->playback(&tileCanvas);
- tileCanvas.flush();
-}
-
-void QuiltTask::draw() {
- SkAutoTDelete<SkBBHFactory> factory;
- switch (fBBH) {
- case kNone_BBH: break;
- case kRTree_BBH:
- factory.reset(SkNEW(SkRTreeFactory));
- break;
- }
-
- // A couple GMs draw wrong when using a bounding box hierarchy.
- // This almost certainly means we have a bug to fix, but for now
- // just draw without a bounding box hierarchy.
- if (fGM->getFlags() & skiagm::GM::kNoBBH_Flag) {
- factory.reset(NULL);
- }
-
- SkAutoTUnref<const SkPicture> recorded(RecordPicture(fGM.get(), factory.get()));
-
- SkBitmap full;
- AllocatePixels(fReference, &full);
-
- if (fGM->getFlags() & skiagm::GM::kSkipTiled_Flag) {
- // Some GMs don't draw exactly the same when tiled. Draw them in one go.
- SkCanvas canvas(full);
- recorded->playback(&canvas);
- canvas.flush();
- } else {
- // Draw tiles in parallel into the same bitmap, simulating aggressive impl-side painting.
- int xTiles = tiles_needed(full.width(), FLAGS_quiltTile),
- yTiles = tiles_needed(full.height(), FLAGS_quiltTile);
- SkTDArray<DrawTileArgs> args;
- args.setCount(xTiles*yTiles);
- for (int y = 0; y < yTiles; y++) {
- for (int x = 0; x < xTiles; x++) {
- DrawTileArgs arg = { x*FLAGS_quiltTile, y*FLAGS_quiltTile, recorded, &full };
- args[y*xTiles + x] = arg;
- }
- }
- SkTaskGroup().batch(draw_tile, args.begin(), args.count());
- }
-
- if (!BitmapsEqual(full, fReference)) {
- this->fail();
- this->spawnChild(SkNEW_ARGS(WriteTask, (*this, "GM", full)));
- }
-}
-
-bool QuiltTask::shouldSkip() const {
- if (fGM->getFlags() & skiagm::GM::kSkipPicture_Flag) {
- return true;
- }
- return !FLAGS_quilt;
-}
-
-} // namespace DM
+++ /dev/null
-#ifndef DMQuiltTask_DEFINED
-#define DMQuiltTask_DEFINED
-
-#include "DMTask.h"
-#include "SkBitmap.h"
-#include "SkString.h"
-#include "SkTemplates.h"
-#include "gm.h"
-
-// Records a GM through an SkPicture, draws it in tiles, and compares against the reference bitmap.
-
-namespace DM {
-
-class QuiltTask : public CpuTask {
-public:
- enum BBH {
- kNone_BBH,
- kRTree_BBH,
- };
-
- QuiltTask(const Task& parent, // QuiltTask must be a child task. Pass its parent here.
- skiagm::GM*, // GM to run through a picture. Takes ownership.
- SkBitmap reference, // Bitmap to compare picture replay results to.
- BBH);
-
- void draw() SK_OVERRIDE;
- bool shouldSkip() const SK_OVERRIDE;
- SkString name() const SK_OVERRIDE { return fName; }
-
-private:
- const BBH fBBH;
- const SkString fName;
- SkAutoTDelete<skiagm::GM> fGM;
- const SkBitmap fReference;
-};
-
-} // namespace DM
-
-#endif // DMReplayTask_DEFINED
+++ /dev/null
-#include "DMReporter.h"
-
-#include "SkDynamicAnnotations.h"
-#include "SkCommonFlags.h"
-#include "OverwriteLine.h"
-#include "ProcStats.h"
-
-namespace DM {
-
-void Reporter::printStatus(SkString name, SkMSec timeMs) const {
- if (FLAGS_quiet) {
- return;
- }
-
- // It's okay if these are a little off---they're just for show---so we can read unprotectedly.
- const int32_t failed = SK_ANNOTATE_UNPROTECTED_READ(fFailed);
- const int32_t pending = SK_ANNOTATE_UNPROTECTED_READ(fPending) - 1;
-
- SkString status;
- status.printf("%s%d tasks left", FLAGS_verbose ? "\n" : kSkOverwriteLine, pending);
- if (failed > 0) {
- status.appendf(", %d failed", failed);
- }
- if (FLAGS_verbose) {
- int max_rss_mb = sk_tools::getMaxResidentSetSizeMB();
- if (max_rss_mb >= 0) {
- status.appendf("\t%4dM peak", max_rss_mb);
- }
- status.appendf("\t%5dms\t%s", timeMs, name.c_str());
- }
- SkDebugf("%s", status.c_str());
-}
-
-void Reporter::fail(SkString msg) {
- sk_atomic_inc(&fFailed);
-
- SkAutoMutexAcquire writer(&fMutex);
- fFailures.push_back(msg);
-}
-
-void Reporter::getFailures(SkTArray<SkString>* failures) const {
- SkAutoMutexAcquire reader(&fMutex);
- *failures = fFailures;
-}
-
-} // namespace DM
+++ /dev/null
-#ifndef DMReporter_DEFINED
-#define DMReporter_DEFINED
-
-#include "SkString.h"
-#include "SkTArray.h"
-#include "SkThread.h"
-#include "SkTime.h"
-#include "SkTypes.h"
-
-// Used to report status changes including failures. All public methods are threadsafe.
-namespace DM {
-
-class Reporter : SkNoncopyable {
-public:
- Reporter() : fPending(0), fFailed(0) {}
-
- void taskCreated() { sk_atomic_inc(&fPending); }
- void taskDestroyed() { sk_atomic_dec(&fPending); }
- void fail(SkString msg);
-
- void printStatus(SkString name, SkMSec timeMs) const;
-
- void getFailures(SkTArray<SkString>*) const;
-
-private:
- int32_t fPending; // atomic
- int32_t fFailed; // atomic, == fFailures.count().
-
- mutable SkMutex fMutex; // Guards fFailures.
- SkTArray<SkString> fFailures;
-};
-
-
-} // namespace DM
-
-#endif // DMReporter_DEFINED
+++ /dev/null
-#include "DMSKPTask.h"
-#include "DMUtil.h"
-#include "DMWriteTask.h"
-
-#include "SkCommandLineFlags.h"
-#include "SkPictureRecorder.h"
-
-DEFINE_int32(skpMaxWidth, 1000, "Max SKPTask viewport width.");
-DEFINE_int32(skpMaxHeight, 1000, "Max SKPTask viewport height.");
-
-namespace DM {
-
-SKPTask::SKPTask(Reporter* r,
- TaskRunner* tr,
- const SkPicture* pic,
- SkString filename)
- : CpuTask(r, tr)
- , fPicture(SkRef(pic))
- , fName(FileToTaskName(filename)) {}
-
-void SKPTask::draw() {
- const int width = SkTMin(SkScalarCeilToInt(fPicture->cullRect().width()), FLAGS_skpMaxWidth),
- height = SkTMin(SkScalarCeilToInt(fPicture->cullRect().height()), FLAGS_skpMaxHeight);
- SkBitmap bitmap;
- AllocatePixels(kN32_SkColorType, width, height, &bitmap);
- DrawPicture(*fPicture, &bitmap);
-
- this->spawnChild(SkNEW_ARGS(WriteTask, (*this, "SKP", bitmap)));
-}
-
-} // namespace DM
+++ /dev/null
-#ifndef DMSKPTask_DEFINED
-#define DMSKPTask_DEFINED
-
-#include "DMReporter.h"
-#include "DMTask.h"
-#include "DMTaskRunner.h"
-#include "SkPicture.h"
-#include "SkString.h"
-#include "SkTemplates.h"
-
-// Draws an SKP to a raster canvas, then compares it with some other modes.
-
-namespace DM {
-
-class SKPTask : public CpuTask {
-public:
- SKPTask(Reporter*, TaskRunner*, const SkPicture*, SkString name);
-
- void draw() SK_OVERRIDE;
- bool shouldSkip() const SK_OVERRIDE { return false; }
- SkString name() const SK_OVERRIDE { return fName; }
-
-private:
- SkAutoTUnref<const SkPicture> fPicture;
- const SkString fName;
-};
-
-} // namespace DM
-
-#endif // DMSKPTask_DEFINED
+++ /dev/null
-#include "DMSerializeTask.h"
-#include "DMUtil.h"
-#include "DMWriteTask.h"
-
-#include "SkCommandLineFlags.h"
-#include "SkPicture.h"
-#include "SkPixelRef.h"
-
-DEFINE_bool(serialize, true, "If true, run picture serialization tests via SkPictureData.");
-
-namespace DM {
-
-SerializeTask::SerializeTask(const Task& parent, skiagm::GM* gm, SkBitmap reference)
- : CpuTask(parent)
- , fName(UnderJoin(parent.name().c_str(), "serialize"))
- , fGM(gm)
- , fReference(reference)
- {}
-
-void SerializeTask::draw() {
- SkAutoTUnref<SkPicture> recorded(RecordPicture(fGM.get(), NULL/*no BBH*/));
-
- SkDynamicMemoryWStream wStream;
- recorded->serialize(&wStream);
- SkAutoTUnref<SkStream> rStream(wStream.detachAsStream());
- SkAutoTUnref<SkPicture> reconstructed(SkPicture::CreateFromStream(rStream));
-
- SkBitmap bitmap;
- AllocatePixels(fReference, &bitmap);
- DrawPicture(*reconstructed, &bitmap);
- if (!BitmapsEqual(bitmap, fReference)) {
- this->fail();
- this->spawnChild(SkNEW_ARGS(WriteTask, (*this, "GM", bitmap)));
- }
-}
-
-bool SerializeTask::shouldSkip() const {
- if (fGM->getFlags() & skiagm::GM::kSkipPicture_Flag) {
- return true;
- }
- return !FLAGS_serialize;
-}
-
-} // namespace DM
+++ /dev/null
-#ifndef DMSerializeTask_DEFINED
-#define DMSerializeTask_DEFINED
-
-#include "DMTask.h"
-#include "SkBitmap.h"
-#include "SkString.h"
-#include "SkTemplates.h"
-#include "gm.h"
-
-// Record a picture, serialize it, deserialize it, then draw it and compare to reference bitmap.
-
-namespace DM {
-
-class SerializeTask : public CpuTask {
-
-public:
- SerializeTask(const Task& parent, skiagm::GM*, SkBitmap reference);
-
- void draw() SK_OVERRIDE;
- bool shouldSkip() const SK_OVERRIDE;
- SkString name() const SK_OVERRIDE { return fName; }
-
-private:
- const SkString fName;
- SkAutoTDelete<skiagm::GM> fGM;
- const SkBitmap fReference;
-};
-
-} // namespace DM
-
-#endif // DMSerializeTask_DEFINED
--- /dev/null
+#include "DMSrcSink.h"
+#include "SamplePipeControllers.h"
+#include "SkCommonFlags.h"
+#include "SkDocument.h"
+#include "SkMultiPictureDraw.h"
+#include "SkOSFile.h"
+#include "SkPictureRecorder.h"
+#include "SkRandom.h"
+#include "SkTLS.h"
+
+namespace DM {
+
+void SafeUnref(SkPicture* p) { SkSafeUnref(p); }
+void SafeUnref(SkData* d) { SkSafeUnref(d); }
+
+// FIXME: the GM objects themselves are not threadsafe, so we create and destroy them as needed.
+
+GMSrc::GMSrc(skiagm::GMRegistry::Factory factory) : fFactory(factory) {}
+
+Error GMSrc::draw(SkCanvas* canvas) const {
+ SkAutoTDelete<skiagm::GM> gm(fFactory(NULL));
+ canvas->concat(gm->getInitialTransform());
+ gm->draw(canvas);
+ return "";
+}
+
+SkISize GMSrc::size() const {
+ SkAutoTDelete<skiagm::GM> gm(fFactory(NULL));
+ return gm->getISize();
+}
+
+Name GMSrc::name() const {
+ SkAutoTDelete<skiagm::GM> gm(fFactory(NULL));
+ return gm->getName();
+}
+
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+// The first call to draw() or size() will mmap the file to an SkData. ~ImageSrc unrefs it.
+struct LazyLoadImage {
+ LazyLoadImage(const char* path) : path(path) {}
+ const char* path;
+
+ SkData* operator()() const { return SkData::NewFromFileName(path); }
+};
+
+ImageSrc::ImageSrc(SkString path, int subsets) : fPath(path), fSubsets(subsets) {}
+
+Error ImageSrc::draw(SkCanvas* canvas) const {
+ const SkData* encoded = fEncoded.get(LazyLoadImage(fPath.c_str()));
+ if (!encoded) {
+ return SkStringPrintf("Couldn't read %s.", fPath.c_str());
+ }
+ if (fSubsets == 0) {
+ // Decode the full image.
+ SkBitmap bitmap;
+ if (!SkImageDecoder::DecodeMemory(encoded->data(), encoded->size(), &bitmap)) {
+ return SkStringPrintf("Couldn't decode %s.", fPath.c_str());
+ }
+ canvas->drawBitmap(bitmap, 0,0);
+ return "";
+ }
+ // Decode random subsets. This is a little involved.
+ SkMemoryStream stream(encoded->data(), encoded->size());
+ SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(&stream));
+ if (!decoder) {
+ return SkStringPrintf("Can't find a good decoder for %s.", fPath.c_str());
+ }
+ int w,h;
+ if (!decoder->buildTileIndex(&stream, &w, &h) || w*h == 1) {
+ return ""; // Not an error. Subset decoding is not always supported.
+ }
+ SkRandom rand;
+ for (int i = 0; i < fSubsets; i++) {
+ SkIRect rect;
+ do {
+ rect.fLeft = rand.nextULessThan(w);
+ rect.fTop = rand.nextULessThan(h);
+ rect.fRight = rand.nextULessThan(w);
+ rect.fBottom = rand.nextULessThan(h);
+ rect.sort();
+ } while (rect.isEmpty());
+ SkBitmap subset;
+ if (!decoder->decodeSubset(&subset, rect, kUnknown_SkColorType/*use best fit*/)) {
+ return SkStringPrintf("Could not decode subset %d.\n", i);
+ }
+ canvas->drawBitmap(subset, SkIntToScalar(rect.fLeft), SkIntToScalar(rect.fTop));
+ }
+ return "";
+}
+
+SkISize ImageSrc::size() const {
+ const SkData* encoded = fEncoded.get(LazyLoadImage(fPath.c_str()));
+ SkBitmap bitmap;
+ if (!encoded || !SkImageDecoder::DecodeMemory(encoded->data(),
+ encoded->size(),
+ &bitmap,
+ kUnknown_SkColorType,
+ SkImageDecoder::kDecodeBounds_Mode)) {
+ return SkISize::Make(0,0);
+ }
+ return bitmap.dimensions();
+}
+
+Name ImageSrc::name() const { return SkOSPath::Basename(fPath.c_str()); }
+
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+static const SkRect kSKPViewport = {0,0, 1000,1000};
+
+// The first call to draw() or size() will read the file into an SkPicture. ~SKPSrc unrefs it.
+struct LazyLoadPicture {
+ LazyLoadPicture(const char* path) : path(path) {}
+ const char* path;
+
+ SkPicture* operator()() const {
+ SkAutoTUnref<SkStream> stream(SkStream::NewFromFile(path));
+ if (!stream) {
+ return NULL;
+ }
+ return SkPicture::CreateFromStream(stream);
+ }
+};
+
+SKPSrc::SKPSrc(SkString path) : fPath(path) {}
+
+Error SKPSrc::draw(SkCanvas* canvas) const {
+ const SkPicture* pic = fPic.get(LazyLoadPicture(fPath.c_str()));
+ if (!pic) {
+ return SkStringPrintf("Couldn't read %s.", fPath.c_str());
+ }
+ canvas->clipRect(kSKPViewport);
+ canvas->drawPicture(pic);
+ return "";
+}
+
+SkISize SKPSrc::size() const {
+ const SkPicture* pic = fPic.get(LazyLoadPicture(fPath.c_str()));
+ if (!pic) {
+ return SkISize::Make(0,0);
+ }
+ SkRect cull = pic->cullRect();
+ if (!cull.intersect(kSKPViewport)) {
+ sk_throw();
+ }
+ SkIRect bounds;
+ cull.roundOut(&bounds);
+ SkISize size = { bounds.width(), bounds.height() };
+ return size;
+}
+
+Name SKPSrc::name() const { return SkOSPath::Basename(fPath.c_str()); }
+
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+DEFINE_string(gpu_threading, "none",
+ "none: single thread,\n"
+ "tls: any thread, GrContextFactory in TLS (crashy),\n"
+ "stack: any thread, GrContextFactory on stack (less crashy, differently so)");
+
+GPUSink::GPUSink(GrContextFactory::GLContextType ct, GrGLStandard api, int samples, bool dfText)
+ : fContextType(ct)
+ , fGpuAPI(api)
+ , fSampleCount(samples)
+ , fUseDFText(dfText) {}
+
+int GPUSink::enclave() const {
+ return FLAGS_gpu_threading.contains("none") ? kGPUSink_Enclave : kAnyThread_Enclave;
+}
+
+static void* CreateGrFactory() { return new GrContextFactory; }
+static void DeleteGrFactory(void* p) { delete (GrContextFactory*)p; }
+
+Error GPUSink::draw(const Src& src, SkBitmap* dst, SkWStream*) const {
+ GrContextFactory local, *factory = &local;
+ if (!FLAGS_gpu_threading.contains("stack")) {
+ factory = (GrContextFactory*)SkTLS::Get(CreateGrFactory, DeleteGrFactory);
+ }
+ // Does abandoning / resetting contexts make any sense if we have stack-scoped factories?
+ if (FLAGS_abandonGpuContext) {
+ factory->abandonContexts();
+ }
+ if (FLAGS_resetGpuContext || FLAGS_abandonGpuContext) {
+ factory->destroyContexts();
+ }
+ const SkISize size = src.size();
+ const SkImageInfo info =
+ SkImageInfo::Make(size.width(), size.height(), kN32_SkColorType, kPremul_SkAlphaType);
+ SkAutoTUnref<SkSurface> surface(
+ NewGpuSurface(factory, fContextType, fGpuAPI, info, fSampleCount, fUseDFText));
+ if (!surface) {
+ return "Could not create a surface.";
+ }
+ SkCanvas* canvas = surface->getCanvas();
+ Error err = src.draw(canvas);
+ if (!err.isEmpty()) {
+ return err;
+ }
+ canvas->flush();
+ dst->allocPixels(info);
+ canvas->readPixels(dst, 0,0);
+ return "";
+}
+
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+PDFSink::PDFSink() {}
+
+Error PDFSink::draw(const Src& src, SkBitmap*, SkWStream* dst) const {
+ SkSize size;
+ size = src.size();
+ SkAutoTUnref<SkDocument> doc(SkDocument::CreatePDF(dst));
+ SkCanvas* canvas = doc->beginPage(size.width(), size.height());
+
+ Error err = src.draw(canvas);
+ if (!err.isEmpty()) {
+ return err;
+ }
+ canvas->flush();
+ doc->endPage();
+ doc->close();
+ return "";
+}
+
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+RasterSink::RasterSink(SkColorType colorType) : fColorType(colorType) {}
+
+Error RasterSink::draw(const Src& src, SkBitmap* dst, SkWStream*) const {
+ const SkISize size = src.size();
+ // If there's an appropriate alpha type for this color type, use it, otherwise use premul.
+ SkAlphaType alphaType = kPremul_SkAlphaType;
+ (void)SkColorTypeValidateAlphaType(fColorType, alphaType, &alphaType);
+
+ dst->allocPixels(SkImageInfo::Make(size.width(), size.height(), fColorType, alphaType));
+ dst->eraseColor(SK_ColorTRANSPARENT);
+ SkCanvas canvas(*dst);
+ return src.draw(&canvas);
+}
+
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+ViaMatrix::ViaMatrix(SkMatrix matrix, Sink* sink) : fMatrix(matrix), fSink(sink) {}
+
+Error ViaMatrix::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream) const {
+ // We turn our arguments into a Src, then draw that Src into our Sink to fill bitmap or stream.
+ struct ProxySrc : public Src {
+ const Src& fSrc;
+ SkMatrix fMatrix;
+ ProxySrc(const Src& src, SkMatrix matrix) : fSrc(src), fMatrix(matrix) {}
+
+ Error draw(SkCanvas* canvas) const SK_OVERRIDE {
+ canvas->concat(fMatrix);
+ return fSrc.draw(canvas);
+ }
+ SkISize size() const SK_OVERRIDE { return fSrc.size(); }
+ Name name() const SK_OVERRIDE { sk_throw(); return ""; } // No one should be calling this.
+ } proxy(src, fMatrix);
+ return fSink->draw(proxy, bitmap, stream);
+}
+
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+ViaPipe::ViaPipe(int flags, Sink* sink) : fFlags((SkGPipeWriter::Flags)flags), fSink(sink) {}
+
+Error ViaPipe::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream) const {
+ // We turn our arguments into a Src, then draw that Src into our Sink to fill bitmap or stream.
+ struct ProxySrc : public Src {
+ const Src& fSrc;
+ SkGPipeWriter::Flags fFlags;
+ ProxySrc(const Src& src, SkGPipeWriter::Flags flags) : fSrc(src), fFlags(flags) {}
+
+ Error draw(SkCanvas* canvas) const SK_OVERRIDE {
+ SkISize size = this->size();
+ // TODO: is DecodeMemory really required? Might help RAM usage to be lazy if we can.
+ PipeController controller(canvas, &SkImageDecoder::DecodeMemory);
+ SkGPipeWriter pipe;
+ return fSrc.draw(pipe.startRecording(&controller, fFlags, size.width(), size.height()));
+ }
+ SkISize size() const SK_OVERRIDE { return fSrc.size(); }
+ Name name() const SK_OVERRIDE { sk_throw(); return ""; } // No one should be calling this.
+ } proxy(src, fFlags);
+ return fSink->draw(proxy, bitmap, stream);
+}
+
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+ViaSerialization::ViaSerialization(Sink* sink) : fSink(sink) {}
+
+Error ViaSerialization::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream) const {
+ // Record our Src into a picture.
+ SkSize size;
+ size = src.size();
+ SkPictureRecorder recorder;
+ Error err = src.draw(recorder.beginRecording(size.width(), size.height()));
+ if (!err.isEmpty()) {
+ return err;
+ }
+ SkAutoTUnref<SkPicture> pic(recorder.endRecording());
+
+ // Serialize it and then deserialize it.
+ SkDynamicMemoryWStream wStream;
+ pic->serialize(&wStream);
+ SkAutoTUnref<SkStream> rStream(wStream.detachAsStream());
+ SkAutoTUnref<SkPicture> deserialized(SkPicture::CreateFromStream(rStream));
+
+ // Turn that deserialized picture into a Src, draw it into our Sink to fill bitmap or stream.
+ struct ProxySrc : public Src {
+ const SkPicture* fPic;
+ const SkISize fSize;
+ ProxySrc(const SkPicture* pic, SkISize size) : fPic(pic), fSize(size) {}
+
+ Error draw(SkCanvas* canvas) const SK_OVERRIDE {
+ canvas->drawPicture(fPic);
+ return "";
+ }
+ SkISize size() const SK_OVERRIDE { return fSize; }
+ Name name() const SK_OVERRIDE { sk_throw(); return ""; } // No one should be calling this.
+ } proxy(deserialized, src.size());
+ return fSink->draw(proxy, bitmap, stream);
+}
+
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+ViaTiles::ViaTiles(int w, int h, SkBBHFactory* factory, Sink* sink)
+ : fW(w)
+ , fH(h)
+ , fFactory(factory)
+ , fSink(sink) {}
+
+Error ViaTiles::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream) const {
+ // Record our Src into a picture.
+ SkSize size;
+ size = src.size();
+ SkPictureRecorder recorder;
+ Error err = src.draw(recorder.beginRecording(size.width(), size.height(), fFactory.get()));
+ if (!err.isEmpty()) {
+ return err;
+ }
+ SkAutoTUnref<SkPicture> pic(recorder.endRecording());
+
+ // Turn that picture into a Src that draws into our Sink via tiles + MPD.
+ struct ProxySrc : public Src {
+ const int fW, fH;
+ const SkPicture* fPic;
+ const SkISize fSize;
+ ProxySrc(int w, int h, const SkPicture* pic, SkISize size)
+ : fW(w), fH(h), fPic(pic), fSize(size) {}
+
+ Error draw(SkCanvas* canvas) const SK_OVERRIDE {
+ const int xTiles = (fSize.width() + fW - 1) / fW,
+ yTiles = (fSize.height() + fH - 1) / fH;
+ SkMultiPictureDraw mpd(xTiles*yTiles);
+ SkTDArray<SkSurface*> surfaces;
+ surfaces.setReserve(xTiles*yTiles);
+
+ SkImageInfo info = canvas->imageInfo().makeWH(fW, fH);
+ for (int j = 0; j < yTiles; j++) {
+ for (int i = 0; i < xTiles; i++) {
+ // This lets our ultimate Sink determine the best kind of surface.
+ // E.g., if it's a GpuSink, the surfaces and images are textures.
+ SkSurface* s = canvas->newSurface(info);
+ if (!s) {
+ s = SkSurface::NewRaster(info); // Some canvases can't create surfaces.
+ }
+ surfaces.push(s);
+ SkCanvas* c = s->getCanvas();
+ c->translate(SkIntToScalar(-i * fW),
+ SkIntToScalar(-j * fH)); // Line up the canvas with this tile.
+ mpd.add(c, fPic);
+ }
+ }
+ mpd.draw();
+ for (int j = 0; j < yTiles; j++) {
+ for (int i = 0; i < xTiles; i++) {
+ SkAutoTUnref<SkImage> image(surfaces[i+xTiles*j]->newImageSnapshot());
+ canvas->drawImage(image, SkIntToScalar(i*fW), SkIntToScalar(j*fH));
+ }
+ }
+ surfaces.unrefAll();
+ return "";
+ }
+ SkISize size() const SK_OVERRIDE { return fSize; }
+ Name name() const SK_OVERRIDE { sk_throw(); return ""; } // No one should be calling this.
+ } proxy(fW, fH, pic, src.size());
+ return fSink->draw(proxy, bitmap, stream);
+}
+
+} // namespace DM
--- /dev/null
+#ifndef DMSrcSink_DEFINED
+#define DMSrcSink_DEFINED
+
+#include "DMGpuSupport.h"
+#include "SkBBHFactory.h"
+#include "SkBBoxHierarchy.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkData.h"
+#include "SkGPipe.h"
+#include "SkPicture.h"
+#include "SkStream.h"
+#include "gm.h"
+
+namespace DM {
+
+// This is just convenience. It lets you use either return "foo" or return SkStringPrintf(...).
+struct ImplicitString : public SkString {
+ template <typename T>
+ ImplicitString(const T& s) : SkString(s) {}
+};
+typedef ImplicitString Error;
+typedef ImplicitString Name;
+
+struct Src {
+ // All Srcs must be thread safe.
+ virtual ~Src() {}
+ virtual Error SK_WARN_UNUSED_RESULT draw(SkCanvas*) const = 0;
+ virtual SkISize size() const = 0;
+ virtual Name name() const = 0;
+};
+
+struct Sink {
+ virtual ~Sink() {}
+ // You may write to either the bitmap or stream.
+ virtual Error SK_WARN_UNUSED_RESULT draw(const Src&, SkBitmap*, SkWStream*) const
+ = 0;
+ // Sinks in the same enclave (except kAnyThread_Enclave) will run serially on the same thread.
+ virtual int enclave() const = 0;
+
+ // File extension for the content draw() outputs, e.g. "png", "pdf".
+ virtual const char* fileExtension() const = 0;
+};
+
+enum { kAnyThread_Enclave, kGPUSink_Enclave, kPDFSink_Enclave };
+static const int kNumEnclaves = kPDFSink_Enclave + 1;
+
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+void SafeUnref(SkPicture*); // These need external linkage (and specific types).
+void SafeUnref(SkData*);
+
+class GMSrc : public Src {
+public:
+ explicit GMSrc(skiagm::GMRegistry::Factory);
+
+ Error draw(SkCanvas*) const SK_OVERRIDE;
+ SkISize size() const SK_OVERRIDE;
+ Name name() const SK_OVERRIDE;
+private:
+ skiagm::GMRegistry::Factory fFactory;
+};
+
+class ImageSrc : public Src {
+public:
+ explicit ImageSrc(SkString path, int subsets = 0);
+
+ Error draw(SkCanvas*) const SK_OVERRIDE;
+ SkISize size() const SK_OVERRIDE;
+ Name name() const SK_OVERRIDE;
+private:
+ SkString fPath;
+ int fSubsets;
+ SkLazyPtr<SkData, SafeUnref> fEncoded;
+};
+
+class SKPSrc : public Src {
+public:
+ explicit SKPSrc(SkString path);
+
+ Error draw(SkCanvas*) const SK_OVERRIDE;
+ SkISize size() const SK_OVERRIDE;
+ Name name() const SK_OVERRIDE;
+private:
+ SkString fPath;
+ SkLazyPtr<SkPicture, SafeUnref> fPic;
+};
+
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+class GPUSink : public Sink {
+public:
+ GPUSink(GrContextFactory::GLContextType, GrGLStandard, int samples, bool dfText);
+
+ Error draw(const Src&, SkBitmap*, SkWStream*) const SK_OVERRIDE;
+ int enclave() const SK_OVERRIDE;
+ const char* fileExtension() const SK_OVERRIDE { return "png"; }
+private:
+ GrContextFactory::GLContextType fContextType;
+ GrGLStandard fGpuAPI;
+ int fSampleCount;
+ bool fUseDFText;
+};
+
+class PDFSink : public Sink {
+public:
+ PDFSink();
+
+ Error draw(const Src&, SkBitmap*, SkWStream*) const SK_OVERRIDE;
+ int enclave() const SK_OVERRIDE { return kPDFSink_Enclave; }
+ const char* fileExtension() const SK_OVERRIDE { return "pdf"; }
+};
+
+class RasterSink : public Sink {
+public:
+ explicit RasterSink(SkColorType);
+
+ Error draw(const Src&, SkBitmap*, SkWStream*) const SK_OVERRIDE;
+ int enclave() const SK_OVERRIDE { return kAnyThread_Enclave; }
+ const char* fileExtension() const SK_OVERRIDE { return "png"; }
+private:
+ SkColorType fColorType;
+};
+
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+class ViaMatrix : public Sink {
+public:
+ ViaMatrix(SkMatrix, Sink*);
+
+ Error draw(const Src&, SkBitmap*, SkWStream*) const SK_OVERRIDE;
+ int enclave() const SK_OVERRIDE { return fSink->enclave(); }
+ const char* fileExtension() const SK_OVERRIDE { return fSink->fileExtension(); }
+private:
+ SkMatrix fMatrix;
+ SkAutoTDelete<Sink> fSink;
+};
+
+class ViaPipe : public Sink {
+public:
+ ViaPipe(int flags, Sink*);
+
+ Error draw(const Src&, SkBitmap*, SkWStream*) const SK_OVERRIDE;
+ int enclave() const SK_OVERRIDE { return fSink->enclave(); }
+ const char* fileExtension() const SK_OVERRIDE { return fSink->fileExtension(); }
+private:
+ SkGPipeWriter::Flags fFlags;
+ SkAutoTDelete<Sink> fSink;
+};
+
+class ViaSerialization : public Sink {
+public:
+ explicit ViaSerialization(Sink*);
+
+ Error draw(const Src&, SkBitmap*, SkWStream*) const SK_OVERRIDE;
+ int enclave() const SK_OVERRIDE { return fSink->enclave(); }
+ const char* fileExtension() const SK_OVERRIDE { return fSink->fileExtension(); }
+private:
+ SkAutoTDelete<Sink> fSink;
+};
+
+class ViaTiles : public Sink {
+public:
+ ViaTiles(int w, int h, SkBBHFactory*, Sink*);
+
+ Error draw(const Src&, SkBitmap*, SkWStream*) const SK_OVERRIDE;
+ int enclave() const SK_OVERRIDE { return fSink->enclave(); }
+ const char* fileExtension() const SK_OVERRIDE { return fSink->fileExtension(); }
+private:
+ const int fW, fH;
+ SkAutoTDelete<SkBBHFactory> fFactory;
+ SkAutoTDelete<Sink> fSink;
+};
+
+} // namespace DM
+
+#endif//DMSrcSink_DEFINED
+++ /dev/null
-#include "DMTask.h"
-#include "DMTaskRunner.h"
-#include "SkCommonFlags.h"
-
-namespace DM {
-
-Task::Task(Reporter* reporter, TaskRunner* taskRunner)
- : fReporter(reporter)
- , fTaskRunner(taskRunner)
- , fDepth(0) {
- fReporter->taskCreated();
-}
-
-Task::Task(const Task& parent)
- : fReporter(parent.fReporter)
- , fTaskRunner(parent.fTaskRunner)
- , fDepth(parent.depth() + 1) {
- fReporter->taskCreated();
-}
-
-Task::~Task() {
- fReporter->taskDestroyed();
-}
-
-void Task::fail(const char* msg) {
- SkString failure(this->name());
- if (msg) {
- failure.appendf(": %s", msg);
- }
- fReporter->fail(failure);
-}
-
-void Task::start() {
- fStart = SkTime::GetMSecs();
-}
-
-void Task::finish() {
- fReporter->printStatus(this->name(), SkTime::GetMSecs() - fStart);
-}
-
-void Task::reallySpawnChild(CpuTask* task) {
- fTaskRunner->add(task);
-}
-
-CpuTask::CpuTask(Reporter* reporter, TaskRunner* taskRunner) : Task(reporter, taskRunner) {}
-CpuTask::CpuTask(const Task& parent) : Task(parent) {}
-
-void CpuTask::run() {
- // If the task says skip, or if we're starting a top-level CPU task and we don't want to, skip.
- const bool skip = this->shouldSkip() || (this->depth() == 0 && !FLAGS_cpu);
- if (!skip) {
- this->start();
- if (!FLAGS_dryRun) this->draw();
- this->finish();
- }
- SkDELETE(this);
-}
-
-void CpuTask::spawnChild(CpuTask* task) {
- // Run children serially on this (CPU) thread. This tends to save RAM and is usually no slower.
- // Calling reallySpawnChild() is nearly equivalent, but it'd pointlessly contend on the
- // threadpool; reallySpawnChild() is most useful when you want to change threadpools.
- task->run();
-}
-
-GpuTask::GpuTask(Reporter* reporter, TaskRunner* taskRunner) : Task(reporter, taskRunner) {}
-
-void GpuTask::run(GrContextFactory* factory) {
- // If the task says skip, or if we're starting a top-level GPU task and we don't want to, skip.
- const bool skip = this->shouldSkip() || (this->depth() == 0 && !FLAGS_gpu);
- if (!skip) {
- this->start();
- if (!FLAGS_dryRun) this->draw(factory);
- this->finish();
- if (FLAGS_abandonGpuContext) {
- factory->abandonContexts();
- }
- if (FLAGS_resetGpuContext || FLAGS_abandonGpuContext) {
- factory->destroyContexts();
- }
- }
- SkDELETE(this);
-}
-
-void GpuTask::spawnChild(CpuTask* task) {
- // Spawn a new task so it runs on the CPU threadpool instead of the GPU one we're on now.
- // It goes on the front of the queue to minimize the time we must hold reference bitmaps in RAM.
- this->reallySpawnChild(task);
-}
-
-} // namespace DM
+++ /dev/null
-#ifndef DMTask_DEFINED
-#define DMTask_DEFINED
-
-#include "DMGpuSupport.h"
-#include "DMReporter.h"
-#include "SkRunnable.h"
-#include "SkTaskGroup.h"
-#include "SkTime.h"
-
-// DM will run() these tasks on one of two threadpools.
-// Subclasses can call fail() to mark this task as failed, or make any number of spawnChild() calls
-// to kick off dependent tasks.
-//
-// Tasks delete themselves when run.
-
-namespace DM {
-
-class TaskRunner;
-
-class CpuTask;
-
-class Task {
-public:
- virtual bool shouldSkip() const = 0;
- virtual SkString name() const = 0;
-
- // Returns the number of parents above this task.
- // Top-level tasks return 0, their children 1, and so on.
- int depth() const { return fDepth; }
-
-protected:
- Task(Reporter* reporter, TaskRunner* taskRunner);
- Task(const Task& parent);
- virtual ~Task();
-
- void start();
- void fail(const char* msg = NULL);
- void finish();
-
- void reallySpawnChild(CpuTask* task); // For now we don't allow GPU child tasks.
-
-private:
- Reporter* fReporter; // Unowned.
- TaskRunner* fTaskRunner; // Unowned.
- int fDepth;
- SkMSec fStart;
-};
-
-class CpuTask : public Task, public SkRunnable {
-public:
- CpuTask(Reporter* reporter, TaskRunner* taskRunner);
- CpuTask(const Task& parent);
- virtual ~CpuTask() {}
-
- void run() SK_OVERRIDE;
- virtual void draw() = 0;
-
- void spawnChild(CpuTask* task);
-};
-
-class GpuTask : public Task {
- public:
- GpuTask(Reporter* reporter, TaskRunner* taskRunner);
- virtual ~GpuTask() {}
-
- void run(GrContextFactory*);
- virtual void draw(GrContextFactory*) = 0;
-
- void spawnChild(CpuTask* task);
-};
-
-} // namespace DM
-
-#endif // DMTask_DEFINED
+++ /dev/null
-#include "DMTaskRunner.h"
-#include "DMTask.h"
-
-namespace DM {
-
-void TaskRunner::add(CpuTask* task) { fCpuWork.add(task); }
-void TaskRunner::add(GpuTask* task) { fGpuWork.push(task); }
-
-void TaskRunner::wait() {
- GrContextFactory factory;
- for (int i = 0; i < fGpuWork.count(); i++) {
- fGpuWork[i]->run(&factory);
- }
- fCpuWork.wait();
-}
-
-} // namespace DM
+++ /dev/null
-#ifndef DMTaskRunner_DEFINED
-#define DMTaskRunner_DEFINED
-
-#include "DMGpuSupport.h"
-#include "SkTDArray.h"
-#include "SkTaskGroup.h"
-#include "SkTypes.h"
-
-namespace DM {
-
-class CpuTask;
-class GpuTask;
-
-class TaskRunner : SkNoncopyable {
-public:
- TaskRunner() {}
-
- void add(CpuTask* task);
- void add(GpuTask* task);
- void wait();
-
-private:
- SkTaskGroup fCpuWork;
- SkTDArray<GpuTask*> fGpuWork;
-};
-
-} // namespace DM
-
-#endif // DMTaskRunner_DEFINED
+++ /dev/null
-#include "DMTestTask.h"
-#include "DMUtil.h"
-#include "SkCommandLineFlags.h"
-#include "SkCommonFlags.h"
-
-DEFINE_bool2(pathOpsExtended, x, false, "Run extended pathOps tests.");
-
-namespace DM {
-
-bool TestReporter::allowExtendedTest() const { return FLAGS_pathOpsExtended; }
-bool TestReporter::verbose() const { return FLAGS_veryVerbose; }
-
-static SkString test_name(const char* name) {
- SkString result("test ");
- result.append(name);
- return result;
-}
-
-CpuTestTask::CpuTestTask(Reporter* reporter,
- TaskRunner* taskRunner,
- skiatest::TestRegistry::Factory factory)
- : CpuTask(reporter, taskRunner)
- , fTest(factory(NULL))
- , fName(test_name(fTest->getName())) {}
-
-GpuTestTask::GpuTestTask(Reporter* reporter,
- TaskRunner* taskRunner,
- skiatest::TestRegistry::Factory factory)
- : GpuTask(reporter, taskRunner)
- , fTest(factory(NULL))
- , fName(test_name(fTest->getName())) {}
-
-
-void CpuTestTask::draw() {
- fTest->setReporter(&fTestReporter);
- fTest->run();
- if (!fTest->passed()) {
- const SkTArray<SkString>& failures = fTestReporter.failures();
- for (int i = 0; i < failures.count(); i++) {
- this->fail(failures[i].c_str());
- }
- }
-}
-
-void GpuTestTask::draw(GrContextFactory* grFactory) {
- fTest->setGrContextFactory(grFactory);
- fTest->setReporter(&fTestReporter);
- fTest->run();
- if (!fTest->passed()) {
- const SkTArray<SkString>& failures = fTestReporter.failures();
- for (int i = 0; i < failures.count(); i++) {
- this->fail(failures[i].c_str());
- }
- }
-}
-
-bool GpuTestTask::shouldSkip() const {
- return kGPUDisabled;
-}
-
-} // namespace DM
+++ /dev/null
-#ifndef DMTestTask_DEFINED
-#define DMTestTask_DEFINED
-
-#include "DMReporter.h"
-#include "DMJsonWriter.h"
-#include "DMTask.h"
-#include "DMTaskRunner.h"
-#include "SkString.h"
-#include "SkTemplates.h"
-#include "Test.h"
-
-// Runs a unit test.
-namespace DM {
-
-class TestReporter : public skiatest::Reporter {
-public:
- TestReporter() {}
-
- const SkTArray<SkString>& failures() const { return fFailures; }
-
-private:
- bool allowExtendedTest() const SK_OVERRIDE;
- bool verbose() const SK_OVERRIDE;
-
- void onReportFailed(const skiatest::Failure& failure) SK_OVERRIDE {
- JsonWriter::AddTestFailure(failure);
-
- SkString newFailure;
- failure.getFailureString(&newFailure);
- fFailures.push_back(newFailure);
- }
-
- SkTArray<SkString> fFailures;
-};
-
-class CpuTestTask : public CpuTask {
-public:
- CpuTestTask(Reporter*, TaskRunner*, skiatest::TestRegistry::Factory);
-
- void draw() SK_OVERRIDE;
- bool shouldSkip() const SK_OVERRIDE { return false; }
- SkString name() const SK_OVERRIDE { return fName; }
-
-private:
- TestReporter fTestReporter;
- SkAutoTDelete<skiatest::Test> fTest;
- const SkString fName;
-};
-
-class GpuTestTask : public GpuTask {
-public:
- GpuTestTask(Reporter*, TaskRunner*, skiatest::TestRegistry::Factory);
-
- void draw(GrContextFactory*) SK_OVERRIDE;
- bool shouldSkip() const SK_OVERRIDE;
- SkString name() const SK_OVERRIDE { return fName; }
-
-private:
- TestReporter fTestReporter;
- SkAutoTDelete<skiatest::Test> fTest;
- const SkString fName;
-};
-
-} // namespace DM
-
-#endif // DMTestTask_DEFINED
+++ /dev/null
-#include "DMUtil.h"
-
-#include "SkColorPriv.h"
-#include "SkCommandLineFlags.h"
-#include "SkPicture.h"
-#include "SkPictureRecorder.h"
-
-DEFINE_string(matrix, "1 0 0 0 1 0 0 0 1",
- "Matrix to apply to the canvas before drawing.");
-
-namespace DM {
-
-void CanvasPreflight(SkCanvas* canvas) {
- if (FLAGS_matrix.count() == 9) {
- SkMatrix m;
- for (int i = 0; i < 9; i++) {
- m[i] = (SkScalar)atof(FLAGS_matrix[i]);
- }
- canvas->concat(m);
- }
-}
-
-SkString UnderJoin(const char* a, const char* b) {
- SkString s;
- s.appendf("%s_%s", a, b);
- return s;
-}
-
-SkString FileToTaskName(SkString filename) {
- for (size_t i = 0; i < filename.size(); i++) {
- if ('_' == filename[i]) { filename[i] = '-'; }
- if ('.' == filename[i]) { filename[i] = '_'; }
- }
- return filename;
-}
-
-SkPicture* RecordPicture(skiagm::GM* gm, SkBBHFactory* factory) {
- const SkScalar w = SkIntToScalar(gm->getISize().width()),
- h = SkIntToScalar(gm->getISize().height());
- SkPictureRecorder recorder;
-
- SkCanvas* canvas = recorder.beginRecording(w, h, factory);
- CanvasPreflight(canvas);
- canvas->concat(gm->getInitialTransform());
- gm->draw(canvas);
- canvas->flush();
- return recorder.endRecording();
-}
-
-void AllocatePixels(SkColorType ct, int width, int height, SkBitmap* bitmap) {
- bitmap->allocPixels(SkImageInfo::Make(width, height, ct, kPremul_SkAlphaType));
- bitmap->eraseColor(0x00000000);
-}
-
-void AllocatePixels(const SkBitmap& reference, SkBitmap* bitmap) {
- AllocatePixels(reference.colorType(), reference.width(), reference.height(), bitmap);
-}
-
-void DrawPicture(const SkPicture& picture, SkBitmap* bitmap) {
- SkASSERT(bitmap != NULL);
- SkCanvas canvas(*bitmap);
- canvas.drawPicture(&picture);
- canvas.flush();
-}
-
-static void unpack_565(uint16_t pixel, unsigned* r, unsigned* g, unsigned* b) {
- *r = SkGetPackedR16(pixel);
- *g = SkGetPackedG16(pixel);
- *b = SkGetPackedB16(pixel);
-}
-
-// Returns |a-b|.
-static unsigned abs_diff(unsigned a, unsigned b) {
- return a > b ? a - b : b - a;
-}
-
-unsigned MaxComponentDifference(const SkBitmap& a, const SkBitmap& b) {
- if (a.info() != b.info()) {
- SkFAIL("Can't compare bitmaps of different shapes.");
- }
-
- unsigned max = 0;
-
- const SkAutoLockPixels lockA(a), lockB(b);
- if (a.info().colorType() == kRGB_565_SkColorType) {
- // 565 is special/annoying because its 3 components straddle 2 bytes.
- const uint16_t* aPixels = (const uint16_t*)a.getPixels();
- const uint16_t* bPixels = (const uint16_t*)b.getPixels();
- for (size_t i = 0; i < a.getSize() / 2; i++) {
- unsigned ar, ag, ab,
- br, bg, bb;
- unpack_565(aPixels[i], &ar, &ag, &ab);
- unpack_565(bPixels[i], &br, &bg, &bb);
- max = SkTMax(max, abs_diff(ar, br));
- max = SkTMax(max, abs_diff(ag, bg));
- max = SkTMax(max, abs_diff(ab, bb));
- }
- } else {
- // Everything else we produce is byte aligned, so max component diff == max byte diff.
- const uint8_t* aBytes = (const uint8_t*)a.getPixels();
- const uint8_t* bBytes = (const uint8_t*)b.getPixels();
- for (size_t i = 0; i < a.getSize(); i++) {
- max = SkTMax(max, abs_diff(aBytes[i], bBytes[i]));
- }
- }
-
- return max;
-}
-
-bool BitmapsEqual(const SkBitmap& a, const SkBitmap& b) {
- if (a.info() != b.info()) {
- return false;
- }
- const SkAutoLockPixels lockA(a), lockB(b);
- return 0 == memcmp(a.getPixels(), b.getPixels(), a.getSize());
-}
-
-} // namespace DM
+++ /dev/null
-#ifndef DMUtil_DEFINED
-#define DMUtil_DEFINED
-
-#include "SkBitmap.h"
-#include "SkCanvas.h"
-#include "SkString.h"
-#include "gm.h"
-
-class SkBBHFactory;
-
-// Small free functions used in more than one place in DM.
-
-namespace DM {
-
-// UnderJoin("a", "b") -> "a_b"
-SkString UnderJoin(const char* a, const char* b);
-
-// "foo_bar.skp" -> "foo-bar_skp"
-SkString FileToTaskName(SkString);
-
-// Draw gm to picture.
-SkPicture* RecordPicture(skiagm::GM* gm, SkBBHFactory* factory = NULL);
-
-// Allocate an empty bitmap with this size and depth.
-void AllocatePixels(SkColorType, int w, int h, SkBitmap* bitmap);
-// Allocate an empty bitmap the same size and depth as reference.
-void AllocatePixels(const SkBitmap& reference, SkBitmap* bitmap);
-
-// Draw picture to bitmap.
-void DrawPicture(const SkPicture& picture, SkBitmap* bitmap);
-
-// What is the maximum component difference in these bitmaps?
-unsigned MaxComponentDifference(const SkBitmap& a, const SkBitmap& b);
-
-// Are these identical bitmaps?
-bool BitmapsEqual(const SkBitmap& a, const SkBitmap& b);
-
-// Hook to modify canvas using global flag values (e.g. --matrix).
-void CanvasPreflight(SkCanvas*);
-
-} // namespace DM
-
-#endif // DMUtil_DEFINED
+++ /dev/null
-#include "DMWriteTask.h"
-
-#include "DMJsonWriter.h"
-#include "DMUtil.h"
-#include "SkColorPriv.h"
-#include "SkCommonFlags.h"
-#include "SkData.h"
-#include "SkImageEncoder.h"
-#include "SkMD5.h"
-#include "SkMallocPixelRef.h"
-#include "SkOSFile.h"
-#include "SkStream.h"
-#include "SkString.h"
-
-DEFINE_bool(nameByHash, false, "If true, write .../hash.png instead of .../mode/config/name.png");
-
-namespace DM {
-
-// Splits off the last N suffixes of name (splitting on _) and appends them to out.
-// Returns the total number of characters consumed.
-static int split_suffixes(int N, const char* name, SkTArray<SkString>* out) {
- SkTArray<SkString> split;
- SkStrSplit(name, "_", &split);
- int consumed = 0;
- for (int i = 0; i < N; i++) {
- // We're splitting off suffixes from the back to front.
- out->push_back(split[split.count()-i-1]);
- consumed += SkToInt(out->back().size() + 1); // Add one for the _.
- }
- return consumed;
-}
-
-inline static SkString find_base_name(const Task& parent, SkTArray<SkString>* suffixList) {
- const int suffixes = parent.depth() + 1;
- const SkString& name = parent.name();
- const int totalSuffixLength = split_suffixes(suffixes, name.c_str(), suffixList);
- return SkString(name.c_str(), name.size() - totalSuffixLength);
-}
-
-WriteTask::WriteTask(const Task& parent, const char* sourceType, SkBitmap bitmap)
- : CpuTask(parent)
- , fBaseName(find_base_name(parent, &fSuffixes))
- , fSourceType(sourceType)
- , fBitmap(bitmap)
- , fData(NULL)
- , fExtension(".png") {
-}
-
-WriteTask::WriteTask(const Task& parent,
- const char* sourceType,
- SkStreamAsset *data,
- const char* ext)
- : CpuTask(parent)
- , fBaseName(find_base_name(parent, &fSuffixes))
- , fSourceType(sourceType)
- , fData(data)
- , fExtension(ext) {
- SkASSERT(fData.get());
- SkASSERT(fData->unique());
-}
-
-void WriteTask::makeDirOrFail(SkString dir) {
- // This can be a little racy, so if it fails check to see if someone else succeeded.
- if (!sk_mkdir(dir.c_str()) && !sk_isdir(dir.c_str())) {
- this->fail("Can't make directory.");
- }
-}
-
-static SkString get_md5_string(SkMD5* hasher) {
- SkMD5::Digest digest;
- hasher->finish(digest);
-
- SkString md5;
- for (int i = 0; i < 16; i++) {
- md5.appendf("%02x", digest.data[i]);
- }
- return md5;
-}
-
-static SkString get_md5(const void* ptr, size_t len) {
- SkMD5 hasher;
- hasher.write(ptr, len);
- return get_md5_string(&hasher);
-}
-
-static bool write_asset(SkStreamAsset* input, SkWStream* output) {
- return input->rewind() && output->writeStream(input, input->getLength());
-}
-
-static SkString get_md5(SkStreamAsset* stream) {
- SkMD5 hasher;
- write_asset(stream, &hasher);
- return get_md5_string(&hasher);
-}
-
-static bool encode_png(const SkBitmap& src, SkFILEWStream* file) {
- SkBitmap bm;
- // We can't encode A8 bitmaps as PNGs. Convert them to 8888 first.
- if (src.info().colorType() == kAlpha_8_SkColorType) {
- if (!src.copyTo(&bm, kN32_SkColorType)) {
- return false;
- }
- } else {
- bm = src;
- }
- return SkImageEncoder::EncodeStream(file, bm, SkImageEncoder::kPNG_Type, 100);
-}
-
-void WriteTask::draw() {
- SkString md5;
- {
- SkAutoLockPixels lock(fBitmap);
- md5 = fData ? get_md5(fData)
- : get_md5(fBitmap.getPixels(), fBitmap.getSize());
- }
-
- SkASSERT(fSuffixes.count() > 0);
- SkString config = fSuffixes.back();
- SkString mode("direct");
- if (fSuffixes.count() > 1) {
- mode = fSuffixes.fromBack(1);
- }
-
- {
- const JsonWriter::BitmapResult entry = { fBaseName,
- config,
- mode,
- fSourceType,
- md5 };
- JsonWriter::AddBitmapResult(entry);
- }
-
- SkString dir(FLAGS_writePath[0]);
-#if defined(SK_BUILD_FOR_IOS)
- if (dir.equals("@")) {
- dir.set(FLAGS_resourcePath[0]);
- }
-#endif
- this->makeDirOrFail(dir);
-
- SkString path;
- if (FLAGS_nameByHash) {
- // Flat directory of hash-named files.
- path = SkOSPath::Join(dir.c_str(), md5.c_str());
- path.append(fExtension);
- // We're content-addressed, so it's possible two threads race to write
- // this file. We let the first one win. This also means we won't
- // overwrite identical files from previous runs.
- if (sk_exists(path.c_str())) {
- return;
- }
- } else {
- // Nested by mode, config, etc.
- for (int i = 0; i < fSuffixes.count(); i++) {
- dir = SkOSPath::Join(dir.c_str(), fSuffixes[i].c_str());
- this->makeDirOrFail(dir);
- }
- path = SkOSPath::Join(dir.c_str(), fBaseName.c_str());
- path.append(fExtension);
- // The path is unique, so two threads can't both write to the same file.
- // If already present we overwrite here, since the content may have changed.
- }
-
- SkFILEWStream file(path.c_str());
- if (!file.isValid()) {
- return this->fail("Can't open file.");
- }
-
- bool ok = fData ? write_asset(fData, &file)
- : encode_png(fBitmap, &file);
- if (!ok) {
- return this->fail("Can't write to file.");
- }
-}
-
-SkString WriteTask::name() const {
- SkString name("writing ");
- for (int i = 0; i < fSuffixes.count(); i++) {
- name.appendf("%s/", fSuffixes[i].c_str());
- }
- name.append(fBaseName.c_str());
- return name;
-}
-
-bool WriteTask::shouldSkip() const {
- return FLAGS_writePath.isEmpty();
-}
-
-} // namespace DM
+++ /dev/null
-#ifndef DMWriteTask_DEFINED
-#define DMWriteTask_DEFINED
-
-#include "DMTask.h"
-#include "SkBitmap.h"
-#include "SkStream.h"
-#include "SkString.h"
-#include "SkTArray.h"
-
-
-// Writes a bitmap to a file.
-
-namespace DM {
-
-class WriteTask : public CpuTask {
-
-public:
- WriteTask(const Task& parent, // WriteTask must be a child task.
- const char* sourceType, // E.g. "GM", "SKP". For humans.
- SkBitmap bitmap); // Bitmap to encode to PNG and write to disk.
-
- // Takes ownership of SkStreamAsset
- WriteTask(const Task& parent, // WriteTask must be a child task.
- const char* sourceType, // E.g. "GM", "SKP". For humans.
- SkStreamAsset* data, // Pre-encoded data to write to disk.
- const char* ext); // File extension.
-
- void draw() SK_OVERRIDE;
- bool shouldSkip() const SK_OVERRIDE;
- SkString name() const SK_OVERRIDE;
-
-private:
- SkTArray<SkString> fSuffixes;
- const SkString fBaseName;
- const SkString fSourceType;
- const SkBitmap fBitmap;
- SkAutoTDelete<SkStreamAsset> fData;
- const char* fExtension;
-
- void makeDirOrFail(SkString dir);
-};
-
-} // namespace DM
-
-#endif // DMWriteTask_DEFINED
+++ /dev/null
-DM (Diamond Master, a.k.a Dungeon master, a.k.a GM 2).
-
-DM is like GM, but multithreaded. It doesn't do everything GM does.
-
-DM's design is based around Tasks and a TaskRunner.
-
-A Task represents an independent unit of work that might fail. We make a task
-for each GM/configuration pair we want to run. Tasks can kick off new tasks
-themselves. For example, a CpuTask can kick off a ReplayTask to make sure
-recording and playing back an SkPicture gives the same result as direct
-rendering.
-
-The TaskRunner runs all tasks on one of two threadpools, whose sizes are
-configurable by --cpuThreads and --gpuThreads. Ideally we'd run these on a
-single threadpool but it can swamp the GPU if we shove too much work into it at
-once. --cpuThreads defaults to the number of cores on the machine.
---gpuThreads defaults to 1, but you may find 2 or 4 runs a little faster.
-
-So the main flow of DM is:
-
- for each GM:
- for each configuration:
- kick off a new task
- < tasks run, maybe fail, and maybe kick off new tasks >
- wait for all tasks to finish
- report failures
-
'tools.gyp:crash_handler',
'tools.gyp:proc_stats',
'tools.gyp:sk_tool_utils',
+ 'tools.gyp:timer',
],
'includes': [
'gmslides.gypi',
],
'sources': [
'../dm/DM.cpp',
- '../dm/DMCpuGMTask.cpp',
- '../dm/DMGpuGMTask.cpp',
- '../dm/DMPDFRasterizeTask.cpp',
- '../dm/DMImageTask.cpp',
+ '../dm/DMSrcSink.cpp',
'../dm/DMJsonWriter.cpp',
- '../dm/DMPDFTask.cpp',
- '../dm/DMPipeTask.cpp',
- '../dm/DMQuiltTask.cpp',
- '../dm/DMReporter.cpp',
- '../dm/DMSKPTask.cpp',
- '../dm/DMSerializeTask.cpp',
- '../dm/DMTask.cpp',
- '../dm/DMTaskRunner.cpp',
- '../dm/DMTestTask.cpp',
- '../dm/DMUtil.cpp',
- '../dm/DMWriteTask.cpp',
'../gm/gm.cpp',
'../src/pipe/utils/SamplePipeControllers.cpp',
#include "SkCommonFlags.h"
-DEFINE_string(config, "565 8888 pdf gpu nonrendering angle nvprmsaa4",
+DEFINE_string(config, "565 8888 gpu nonrendering angle nvprmsaa4 ",
"Options: 565 8888 pdf gpu nonrendering msaa4 msaa16 nvprmsaa4 nvprmsaa16 "
- "gpudft gpunull gpudebug angle mesa");
+ "gpudft gpunull gpudebug angle mesa (and many more)");
DEFINE_bool(cpu, true, "master switch for running CPU-bound work.");
void WallTimer::end() {
fWall = fSysTimer.endWall();
}
+
+SkString HumanizeMs(double ms) {
+ if (ms > 1e+3) return SkStringPrintf("%.3gs", ms/1e3);
+ if (ms < 1e-3) return SkStringPrintf("%.3gns", ms*1e6);
+#ifdef SK_BUILD_FOR_WIN
+ if (ms < 1) return SkStringPrintf("%.3gus", ms*1e3);
+#else
+ if (ms < 1) return SkStringPrintf("%.3gµs", ms*1e3);
+#endif
+ return SkStringPrintf("%.3gms", ms);
+}
#define Timer_DEFINED
#include "SkTypes.h"
+#include "SkString.h"
#if defined(SK_BUILD_FOR_WIN32)
#include "SysTimer_windows.h"
SysTimer fSysTimer;
};
+SkString HumanizeMs(double);
+
#endif