| OLD | NEW |
| 1 // Main binary for DM. |
| 2 // For a high-level overview, please see dm/README. |
| 3 |
| 1 #include "CrashHandler.h" | 4 #include "CrashHandler.h" |
| 2 #include "DMJsonWriter.h" | 5 #include "LazyDecodeBitmap.h" |
| 3 #include "DMSrcSink.h" | |
| 4 #include "OverwriteLine.h" | |
| 5 #include "ProcStats.h" | |
| 6 #include "SkBBHFactory.h" | |
| 7 #include "SkCommonFlags.h" | 6 #include "SkCommonFlags.h" |
| 8 #include "SkForceLinking.h" | 7 #include "SkForceLinking.h" |
| 9 #include "SkGraphics.h" | 8 #include "SkGraphics.h" |
| 10 #include "SkMD5.h" | |
| 11 #include "SkOSFile.h" | 9 #include "SkOSFile.h" |
| 10 #include "SkPicture.h" |
| 11 #include "SkString.h" |
| 12 #include "SkTaskGroup.h" | 12 #include "SkTaskGroup.h" |
| 13 #include "Test.h" | 13 #include "Test.h" |
| 14 #include "Timer.h" | 14 #include "gm.h" |
| 15 | 15 #include "sk_tool_utils.h" |
| 16 #include "sk_tool_utils_flags.h" |
| 17 |
| 18 #include "DMCpuGMTask.h" |
| 19 #include "DMGpuGMTask.h" |
| 20 #include "DMGpuSupport.h" |
| 21 #include "DMImageTask.h" |
| 22 #include "DMJsonWriter.h" |
| 23 #include "DMPDFTask.h" |
| 24 #include "DMPDFRasterizeTask.h" |
| 25 #include "DMReporter.h" |
| 26 #include "DMSKPTask.h" |
| 27 #include "DMTask.h" |
| 28 #include "DMTaskRunner.h" |
| 29 #include "DMTestTask.h" |
| 30 |
| 31 #ifdef SK_BUILD_POPPLER |
| 32 # include "SkPDFRasterizer.h" |
| 33 # define RASTERIZE_PDF_PROC SkPopplerRasterizePDF |
| 34 #elif defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) |
| 35 # include "SkCGUtils.h" |
| 36 # define RASTERIZE_PDF_PROC SkPDFDocumentToBitmap |
| 37 #else |
| 38 # define RASTERIZE_PDF_PROC NULL |
| 39 #endif |
| 40 |
| 41 #include <ctype.h> |
| 42 |
| 43 using skiagm::GM; |
| 44 using skiagm::GMRegistry; |
| 45 using skiatest::Test; |
| 46 using skiatest::TestRegistry; |
| 47 |
| 48 static const char kGpuAPINameGL[] = "gl"; |
| 49 static const char kGpuAPINameGLES[] = "gles"; |
| 50 |
| 51 DEFINE_bool(gms, true, "Run GMs?"); |
| 16 DEFINE_bool(tests, true, "Run tests?"); | 52 DEFINE_bool(tests, true, "Run tests?"); |
| 17 DEFINE_string(images, "resources", "Images to decode."); | 53 DEFINE_bool(reportUsedChars, false, "Output test font construction data to be pa
sted into" |
| 18 DEFINE_string(src, "gm skp image subset", "Source types to test."); | 54 " create_test_font.cpp."); |
| 19 DEFINE_bool(nameByHash, false, | 55 DEFINE_string(images, "resources", "Path to directory containing images to decod
e."); |
| 20 "If true, write to FLAGS_writePath[0]/<hash>.png instead of " | 56 DEFINE_bool(rasterPDF, true, "Rasterize PDFs?"); |
| 21 "to FLAGS_writePath[0]/<config>/<sourceType>/<name>.png"); | |
| 22 DEFINE_bool2(pathOpsExtended, x, false, "Run extended pathOps tests."); | |
| 23 DEFINE_string(matrix, "1 0 0 0 1 0 0 0 1", | |
| 24 "Matrix to apply when using 'matrix' in config."); | |
| 25 | 57 |
| 26 __SK_FORCE_IMAGE_DECODER_LINKING; | 58 __SK_FORCE_IMAGE_DECODER_LINKING; |
| 27 using namespace DM; | 59 |
| 28 | 60 static DM::RasterizePdfProc get_pdf_rasterizer_proc() { |
| 29 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~*/ | 61 return reinterpret_cast<DM::RasterizePdfProc>( |
| 30 | 62 FLAGS_rasterPDF ? RASTERIZE_PDF_PROC : NULL); |
| 31 static int gPending = 0, gFailures = 0; | 63 } |
| 32 | 64 |
| 33 static void fail(ImplicitString err) { | 65 // "FooBar" -> "foobar". Obviously, ASCII only. |
| 34 SkDebugf("\n\nERROR: %s\n\n", err.c_str()); | 66 static SkString lowercase(SkString s) { |
| 35 sk_atomic_inc(&gFailures); | 67 for (size_t i = 0; i < s.size(); i++) { |
| 36 } | 68 s[i] = tolower(s[i]); |
| 37 | 69 } |
| 38 static void done(double ms, ImplicitString config, ImplicitString src, ImplicitS
tring name) { | 70 return s; |
| 39 SkDebugf("%s(%4dMB %5d) %s\t%s %s %s ", FLAGS_verbose ? "\n" : kSkOverwrite
Line | 71 } |
| 40 , sk_tools::getMaxResidentSetSizeMB() | 72 |
| 41 , sk_atomic_dec(&gPending)-1 | 73 static const GrContextFactory::GLContextType native = GrContextFactory::kNative_
GLContextType; |
| 42 , HumanizeMs(ms).c_str() | 74 static const GrContextFactory::GLContextType nvpr = GrContextFactory::kNVPR_GL
ContextType; |
| 43 , config.c_str() | 75 static const GrContextFactory::GLContextType null = GrContextFactory::kNull_GL
ContextType; |
| 44 , src.c_str() | 76 static const GrContextFactory::GLContextType debug = GrContextFactory::kDebug_G
LContextType; |
| 45 , name.c_str()); | 77 #if SK_ANGLE |
| 46 } | 78 static const GrContextFactory::GLContextType angle = GrContextFactory::kANGLE_G
LContextType; |
| 47 | 79 #endif |
| 48 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~*/ | 80 #if SK_MESA |
| 49 | 81 static const GrContextFactory::GLContextType mesa = GrContextFactory::kMESA_GL
ContextType; |
| 50 template <typename T> | 82 #endif |
| 51 struct Tagged : public SkAutoTDelete<T> { const char* tag; }; | 83 |
| 52 | 84 static void kick_off_gms(const SkTDArray<GMRegistry::Factory>& gms, |
| 53 static const bool kMemcpyOK = true; | 85 const SkTArray<SkString>& configs, |
| 54 | 86 GrGLStandard gpuAPI, |
| 55 static SkTArray<Tagged<Src>, kMemcpyOK> gSrcs; | 87 DM::Reporter* reporter, |
| 56 static SkTArray<Tagged<Sink>, kMemcpyOK> gSinks; | 88 DM::TaskRunner* tasks) { |
| 57 | 89 #define START(name, type, ...)
\ |
| 58 static void push_src(const char* tag, Src* s) { | 90 if (lowercase(configs[j]).equals(name)) {
\ |
| 59 SkAutoTDelete<Src> src(s); | 91 tasks->add(SkNEW_ARGS(DM::type, (name, reporter, tasks, gms[i], ## __VA_
ARGS__))); \ |
| 60 if (FLAGS_src.contains(tag) && | 92 } |
| 61 !SkCommandLineFlags::ShouldSkip(FLAGS_match, src->name().c_str())) { | 93 for (int i = 0; i < gms.count(); i++) { |
| 62 Tagged<Src>& s = gSrcs.push_back(); | 94 for (int j = 0; j < configs.count(); j++) { |
| 63 s.reset(src.detach()); | 95 |
| 64 s.tag = tag; | 96 START("565", CpuGMTask, kRGB_565_SkColorType); |
| 65 } | 97 START("8888", CpuGMTask, kN32_SkColorType); |
| 66 } | 98 START("gpu", GpuGMTask, native, gpuAPI, 0, false); |
| 67 | 99 START("msaa4", GpuGMTask, native, gpuAPI, 4, false); |
| 68 static void gather_srcs() { | 100 START("msaa16", GpuGMTask, native, gpuAPI, 16, false); |
| 69 for (const skiagm::GMRegistry* r = skiagm::GMRegistry::Head(); r; r = r->nex
t()) { | 101 START("nvprmsaa4", GpuGMTask, nvpr, gpuAPI, 4, false); |
| 70 push_src("gm", new GMSrc(r->factory())); | 102 START("nvprmsaa16", GpuGMTask, nvpr, gpuAPI, 16, false); |
| 71 } | 103 START("gpudft", GpuGMTask, native, gpuAPI, 0, true); |
| 72 if (!FLAGS_skps.isEmpty()) { | 104 START("gpunull", GpuGMTask, null, gpuAPI, 0, false); |
| 73 SkOSFile::Iter it(FLAGS_skps[0], "skp"); | 105 START("gpudebug", GpuGMTask, debug, gpuAPI, 0, false); |
| 74 for (SkString file; it.next(&file); ) { | 106 #if SK_ANGLE |
| 75 push_src("skp", new SKPSrc(SkOSPath::Join(FLAGS_skps[0], file.c_str(
)))); | 107 START("angle", GpuGMTask, angle, gpuAPI, 0, false); |
| 76 } | 108 #endif |
| 77 } | 109 #if SK_MESA |
| 78 if (!FLAGS_images.isEmpty()) { | 110 START("mesa", GpuGMTask, mesa, gpuAPI, 0, false); |
| 79 const char* exts[] = { | 111 #endif |
| 80 "bmp", "gif", "jpg", "jpeg", "png", "webp", "ktx", "astc", "wbmp", "
ico", | 112 START("pdf", PDFTask, get_pdf_rasterizer_proc()); |
| 81 "BMP", "GIF", "JPG", "JPEG", "PNG", "WEBP", "KTX", "ASTC", "WBMP", "
ICO", | 113 } |
| 82 }; | 114 } |
| 83 for (size_t i = 0; i < SK_ARRAY_COUNT(exts); i++) { | 115 #undef START |
| 84 SkOSFile::Iter it(FLAGS_images[0], exts[i]); | 116 } |
| 85 for (SkString file; it.next(&file); ) { | 117 |
| 86 SkString path = SkOSPath::Join(FLAGS_images[0], file.c_str()); | 118 static void kick_off_tests(const SkTDArray<TestRegistry::Factory>& tests, |
| 87 push_src("image", new ImageSrc(path)); // Decode entire ima
ge. | 119 DM::Reporter* reporter, |
| 88 push_src("subset", new ImageSrc(path, 5)); // Decode 5 random s
ubsets. | 120 DM::TaskRunner* tasks) { |
| 121 for (int i = 0; i < tests.count(); i++) { |
| 122 SkAutoTDelete<Test> test(tests[i](NULL)); |
| 123 if (test->isGPUTest()) { |
| 124 tasks->add(SkNEW_ARGS(DM::GpuTestTask, (reporter, tasks, tests[i])))
; |
| 125 } else { |
| 126 tasks->add(SkNEW_ARGS(DM::CpuTestTask, (reporter, tasks, tests[i])))
; |
| 127 } |
| 128 } |
| 129 } |
| 130 |
| 131 static void find_files(const char* dir, |
| 132 const char* suffixes[], |
| 133 size_t num_suffixes, |
| 134 SkTArray<SkString>* files) { |
| 135 if (0 == strcmp(dir, "")) { |
| 136 return; |
| 137 } |
| 138 |
| 139 SkString filename; |
| 140 for (size_t i = 0; i < num_suffixes; i++) { |
| 141 SkOSFile::Iter it(dir, suffixes[i]); |
| 142 while (it.next(&filename)) { |
| 143 if (!SkCommandLineFlags::ShouldSkip(FLAGS_match, filename.c_str()))
{ |
| 144 files->push_back(SkOSPath::Join(dir, filename.c_str())); |
| 89 } | 145 } |
| 90 } | 146 } |
| 91 } | 147 } |
| 92 } | 148 } |
| 93 | 149 |
| 94 static GrGLStandard get_gpu_api() { | 150 static void kick_off_skps(const SkTArray<SkString>& skps, |
| 95 if (FLAGS_gpuAPI.contains("gl")) { return kGL_GrGLStandard; } | 151 DM::Reporter* reporter, |
| 96 if (FLAGS_gpuAPI.contains("gles")) { return kGLES_GrGLStandard; } | 152 DM::TaskRunner* tasks) { |
| 97 return kNone_GrGLStandard; | 153 for (int i = 0; i < skps.count(); ++i) { |
| 98 } | 154 SkAutoTUnref<SkStream> stream(SkStream::NewFromFile(skps[i].c_str())); |
| 99 | 155 if (stream.get() == NULL) { |
| 100 static void push_sink(const char* tag, Sink* s) { | 156 SkDebugf("Could not read %s.\n", skps[i].c_str()); |
| 101 SkAutoTDelete<Sink> sink(s); | 157 exit(1); |
| 102 if (!FLAGS_config.contains(tag)) { | 158 } |
| 159 SkAutoTUnref<SkPicture> pic( |
| 160 SkPicture::CreateFromStream(stream.get(), &sk_tools::LazyDecodeB
itmap)); |
| 161 if (pic.get() == NULL) { |
| 162 SkDebugf("Could not read %s as an SkPicture.\n", skps[i].c_str()); |
| 163 exit(1); |
| 164 } |
| 165 |
| 166 SkString filename = SkOSPath::Basename(skps[i].c_str()); |
| 167 tasks->add(SkNEW_ARGS(DM::SKPTask, (reporter, tasks, pic, filename))); |
| 168 tasks->add(SkNEW_ARGS(DM::PDFTask, (reporter, tasks, pic, filename, |
| 169 get_pdf_rasterizer_proc()))); |
| 170 } |
| 171 } |
| 172 |
| 173 static void kick_off_images(const SkTArray<SkString>& images, |
| 174 DM::Reporter* reporter, |
| 175 DM::TaskRunner* tasks) { |
| 176 for (int i = 0; i < images.count(); i++) { |
| 177 SkAutoTUnref<SkData> image(SkData::NewFromFileName(images[i].c_str())); |
| 178 if (!image) { |
| 179 SkDebugf("Could not read %s.\n", images[i].c_str()); |
| 180 exit(1); |
| 181 } |
| 182 SkString filename = SkOSPath::Basename(images[i].c_str()); |
| 183 tasks->add(SkNEW_ARGS(DM::ImageTask, (reporter, tasks, image, filename))
); |
| 184 tasks->add(SkNEW_ARGS(DM::ImageTask, (reporter, tasks, image, filename,
5/*subsets*/))); |
| 185 } |
| 186 } |
| 187 |
| 188 |
| 189 static void report_failures(const SkTArray<SkString>& failures) { |
| 190 if (failures.count() == 0) { |
| 103 return; | 191 return; |
| 104 } | 192 } |
| 105 // Try a noop Src as a canary. If it fails, skip this sink. | 193 |
| 106 struct : public Src { | 194 SkDebugf("Failures:\n"); |
| 107 Error draw(SkCanvas*) const SK_OVERRIDE { return ""; } | 195 for (int i = 0; i < failures.count(); i++) { |
| 108 SkISize size() const SK_OVERRIDE { return SkISize::Make(16, 16); } | 196 SkDebugf(" %s\n", failures[i].c_str()); |
| 109 Name name() const SK_OVERRIDE { return "noop"; } | 197 } |
| 110 } noop; | 198 SkDebugf("%d failures.\n", failures.count()); |
| 111 | 199 } |
| 112 SkBitmap bitmap; | 200 |
| 113 SkDynamicMemoryWStream stream; | 201 static GrGLStandard get_gl_standard() { |
| 114 Error err = sink->draw(noop, &bitmap, &stream); | 202 if (FLAGS_gpuAPI.contains(kGpuAPINameGL)) { |
| 115 if (!err.isEmpty()) { | 203 return kGL_GrGLStandard; |
| 116 SkDebugf("Skipping %s: %s\n", tag, err.c_str()); | 204 } |
| 117 return; | 205 if (FLAGS_gpuAPI.contains(kGpuAPINameGLES)) { |
| 118 } | 206 return kGLES_GrGLStandard; |
| 119 | 207 } |
| 120 Tagged<Sink>& ts = gSinks.push_back(); | 208 return kNone_GrGLStandard; |
| 121 ts.reset(sink.detach()); | 209 } |
| 122 ts.tag = tag; | 210 |
| 123 } | 211 template <typename T, typename Registry> |
| 124 | 212 static void append_matching_factories(Registry* head, SkTDArray<typename Registr
y::Factory>* out) { |
| 125 static bool gpu_supported() { | 213 for (const Registry* reg = head; reg != NULL; reg = reg->next()) { |
| 126 #if SK_SUPPORT_GPU | 214 SkAutoTDelete<T> forName(reg->factory()(NULL)); |
| 127 return FLAGS_gpu; | 215 if (!SkCommandLineFlags::ShouldSkip(FLAGS_match, forName->getName())) { |
| 128 #else | 216 *out->append() = reg->factory(); |
| 129 return false; | 217 } |
| 130 #endif | 218 } |
| 131 } | 219 } |
| 132 | |
| 133 static Sink* create_sink(const char* tag) { | |
| 134 #define SINK(t, sink, ...) if (0 == strcmp(t, tag)) { return new sink(__VA_ARGS_
_); } | |
| 135 if (gpu_supported()) { | |
| 136 const GrGLStandard api = get_gpu_api(); | |
| 137 SINK("gpunull", GPUSink, GrContextFactory::kNull_GLContextType, api
, 0, false); | |
| 138 SINK("gpudebug", GPUSink, GrContextFactory::kDebug_GLContextType, api
, 0, false); | |
| 139 SINK("gpu", GPUSink, GrContextFactory::kNative_GLContextType, api
, 0, false); | |
| 140 SINK("gpudft", GPUSink, GrContextFactory::kNative_GLContextType, api
, 0, true); | |
| 141 SINK("msaa4", GPUSink, GrContextFactory::kNative_GLContextType, api
, 4, false); | |
| 142 SINK("msaa16", GPUSink, GrContextFactory::kNative_GLContextType, api
, 16, false); | |
| 143 SINK("nvprmsaa4", GPUSink, GrContextFactory::kNVPR_GLContextType, api
, 4, false); | |
| 144 SINK("nvprmsaa16", GPUSink, GrContextFactory::kNVPR_GLContextType, api
, 16, false); | |
| 145 #if SK_ANGLE | |
| 146 SINK("angle", GPUSink, GrContextFactory::kANGLE_GLContextType, api
, 0, false); | |
| 147 #endif | |
| 148 #if SK_MESA | |
| 149 SINK("mesa", GPUSink, GrContextFactory::kMESA_GLContextType, api
, 0, false); | |
| 150 #endif | |
| 151 } | |
| 152 | |
| 153 if (FLAGS_cpu) { | |
| 154 SINK("565", RasterSink, kRGB_565_SkColorType); | |
| 155 SINK("8888", RasterSink, kN32_SkColorType); | |
| 156 // TODO(mtklein): reenable once skiagold can handle .pdf uploads. | |
| 157 //SINK("pdf", PDFSink); | |
| 158 } | |
| 159 #undef SINK | |
| 160 return NULL; | |
| 161 } | |
| 162 | |
| 163 static Sink* create_via(const char* tag, Sink* wrapped) { | |
| 164 #define VIA(t, via, ...) if (0 == strcmp(t, tag)) { return new via(__VA_ARGS__);
} | |
| 165 VIA("serialize", ViaSerialization, wrapped); | |
| 166 | |
| 167 VIA("tiles", ViaTiles, 256, 256, NULL, wrapped); | |
| 168 VIA("tiles_rt", ViaTiles, 256, 256, new SkRTreeFactory, wrapped); | |
| 169 | |
| 170 const int xp = SkGPipeWriter::kCrossProcess_Flag, | |
| 171 sa = xp | SkGPipeWriter::kSharedAddressSpace_Flag; | |
| 172 VIA("pipe", ViaPipe, 0, wrapped); | |
| 173 VIA("pipe_xp", ViaPipe, xp, wrapped); | |
| 174 VIA("pipe_sa", ViaPipe, sa, wrapped); | |
| 175 | |
| 176 if (FLAGS_matrix.count() == 9) { | |
| 177 SkMatrix m; | |
| 178 for (int i = 0; i < 9; i++) { | |
| 179 m[i] = (SkScalar)atof(FLAGS_matrix[i]); | |
| 180 } | |
| 181 VIA("matrix", ViaMatrix, m, wrapped); | |
| 182 } | |
| 183 #undef VIA | |
| 184 return NULL; | |
| 185 } | |
| 186 | |
| 187 static void gather_sinks() { | |
| 188 for (int i = 0; i < FLAGS_config.count(); i++) { | |
| 189 const char* config = FLAGS_config[i]; | |
| 190 SkTArray<SkString> parts; | |
| 191 SkStrSplit(config, "-", &parts); | |
| 192 | |
| 193 Sink* sink = NULL; | |
| 194 for (int i = parts.count(); i-- > 0;) { | |
| 195 const char* part = parts[i].c_str(); | |
| 196 Sink* next = (sink == NULL) ? create_sink(part) : create_via(part, s
ink); | |
| 197 if (next == NULL) { | |
| 198 SkDebugf("Skipping %s: Don't understand '%s'.\n", config, part); | |
| 199 delete sink; | |
| 200 sink = NULL; | |
| 201 break; | |
| 202 } | |
| 203 sink = next; | |
| 204 } | |
| 205 if (sink) { | |
| 206 push_sink(config, sink); | |
| 207 } | |
| 208 } | |
| 209 } | |
| 210 | |
| 211 // The finest-grained unit of work we can run: draw a single Src into a single S
ink, | |
| 212 // report any errors, and perhaps write out the output: a .png of the bitmap, or
a raw stream. | |
| 213 struct Task { | |
| 214 Task(const Tagged<Src>& src, const Tagged<Sink>& sink) : src(src), sink(sink
) {} | |
| 215 const Tagged<Src>& src; | |
| 216 const Tagged<Sink>& sink; | |
| 217 | |
| 218 static void Run(Task* task) { | |
| 219 WallTimer timer; | |
| 220 timer.start(); | |
| 221 if (!FLAGS_dryRun) { | |
| 222 SkBitmap bitmap; | |
| 223 SkDynamicMemoryWStream stream; | |
| 224 Error err = task->sink->draw(*task->src, &bitmap, &stream); | |
| 225 if (!err.isEmpty()) { | |
| 226 fail(SkStringPrintf("%s %s %s: %s", | |
| 227 task->sink.tag, | |
| 228 task->src.tag, | |
| 229 task->src->name().c_str(), | |
| 230 err.c_str())); | |
| 231 } | |
| 232 if (!FLAGS_writePath.isEmpty()) { | |
| 233 const char* ext = task->sink->fileExtension(); | |
| 234 if (stream.bytesWritten() == 0) { | |
| 235 SkMemoryStream pixels(bitmap.getPixels(), bitmap.getSize()); | |
| 236 WriteToDisk(*task, &pixels, bitmap.getSize(), &bitmap, ext); | |
| 237 } else { | |
| 238 SkAutoTDelete<SkStreamAsset> data(stream.detachAsStream()); | |
| 239 WriteToDisk(*task, data, data->getLength(), NULL, ext); | |
| 240 } | |
| 241 } | |
| 242 } | |
| 243 timer.end(); | |
| 244 done(timer.fWall, task->sink.tag, task->src.tag, task->src->name()); | |
| 245 } | |
| 246 | |
| 247 static void WriteToDisk(const Task& task, | |
| 248 SkStream* data, size_t len, | |
| 249 const SkBitmap* bitmap, | |
| 250 const char* ext) { | |
| 251 SkMD5 hash; | |
| 252 hash.writeStream(data, len); | |
| 253 SkMD5::Digest digest; | |
| 254 hash.finish(digest); | |
| 255 | |
| 256 JsonWriter::BitmapResult result; | |
| 257 result.name = task.src->name(); | |
| 258 result.config = task.sink.tag; | |
| 259 result.sourceType = task.src.tag; | |
| 260 result.ext = ext; | |
| 261 for (int i = 0; i < 16; i++) { | |
| 262 result.md5.appendf("%02x", digest.data[i]); | |
| 263 } | |
| 264 JsonWriter::AddBitmapResult(result); | |
| 265 | |
| 266 const char* dir = FLAGS_writePath[0]; | |
| 267 if (0 == strcmp(dir, "@")) { // Needed for iOS. | |
| 268 dir = FLAGS_resourcePath[0]; | |
| 269 } | |
| 270 sk_mkdir(dir); | |
| 271 | |
| 272 SkString path; | |
| 273 if (FLAGS_nameByHash) { | |
| 274 path = SkOSPath::Join(dir, result.md5.c_str()); | |
| 275 path.append("."); | |
| 276 path.append(ext); | |
| 277 if (sk_exists(path.c_str())) { | |
| 278 return; // Content-addressed. If it exists already, we're done
. | |
| 279 } | |
| 280 } else { | |
| 281 path = SkOSPath::Join(dir, task.sink.tag); | |
| 282 sk_mkdir(path.c_str()); | |
| 283 path = SkOSPath::Join(path.c_str(), task.src.tag); | |
| 284 sk_mkdir(path.c_str()); | |
| 285 path = SkOSPath::Join(path.c_str(), task.src->name().c_str()); | |
| 286 path.append("."); | |
| 287 path.append(ext); | |
| 288 } | |
| 289 | |
| 290 SkFILEWStream file(path.c_str()); | |
| 291 if (!file.isValid()) { | |
| 292 fail(SkStringPrintf("Can't open %s for writing.\n", path.c_str())); | |
| 293 return; | |
| 294 } | |
| 295 | |
| 296 data->rewind(); | |
| 297 if (bitmap) { | |
| 298 // We can't encode A8 bitmaps as PNGs. Convert them to 8888 first. | |
| 299 SkBitmap converted; | |
| 300 if (bitmap->info().colorType() == kAlpha_8_SkColorType) { | |
| 301 if (!bitmap->copyTo(&converted, kN32_SkColorType)) { | |
| 302 fail("Can't convert A8 to 8888.\n"); | |
| 303 return; | |
| 304 } | |
| 305 bitmap = &converted; | |
| 306 } | |
| 307 if (!SkImageEncoder::EncodeStream(&file, *bitmap, SkImageEncoder::kP
NG_Type, 100)) { | |
| 308 fail(SkStringPrintf("Can't encode PNG to %s.\n", path.c_str())); | |
| 309 return; | |
| 310 } | |
| 311 } else { | |
| 312 if (!file.writeStream(data, len)) { | |
| 313 fail(SkStringPrintf("Can't write to %s.\n", path.c_str())); | |
| 314 return; | |
| 315 } | |
| 316 } | |
| 317 } | |
| 318 }; | |
| 319 | |
| 320 // Run all tasks in the same enclave serially on the same thread. | |
| 321 // They can't possibly run concurrently with each other. | |
| 322 static void run_enclave(SkTArray<Task>* tasks) { | |
| 323 for (int i = 0; i < tasks->count(); i++) { | |
| 324 Task::Run(tasks->begin() + i); | |
| 325 } | |
| 326 } | |
| 327 | |
| 328 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~*/ | |
| 329 | |
| 330 // Unit tests don't fit so well into the Src/Sink model, so we give them special
treatment. | |
| 331 | |
| 332 static struct : public skiatest::Reporter { | |
| 333 void onReportFailed(const skiatest::Failure& failure) SK_OVERRIDE { | |
| 334 SkString s; | |
| 335 failure.getFailureString(&s); | |
| 336 fail(s); | |
| 337 JsonWriter::AddTestFailure(failure); | |
| 338 } | |
| 339 bool allowExtendedTest() const SK_OVERRIDE { return FLAGS_pathOpsExtended; } | |
| 340 bool verbose() const SK_OVERRIDE { return FLAGS_veryVerbose; } | |
| 341 } gTestReporter; | |
| 342 | |
| 343 static SkTArray<SkAutoTDelete<skiatest::Test>, kMemcpyOK> gTests; | |
| 344 | |
| 345 static void gather_tests() { | |
| 346 if (!FLAGS_tests) { | |
| 347 return; | |
| 348 } | |
| 349 for (const skiatest::TestRegistry* r = skiatest::TestRegistry::Head(); r; r
= r->next()) { | |
| 350 SkAutoTDelete<skiatest::Test> test(r->factory()(NULL)); | |
| 351 if (SkCommandLineFlags::ShouldSkip(FLAGS_match, test->getName())) { | |
| 352 continue; | |
| 353 } | |
| 354 if (test->isGPUTest() && !gpu_supported()) { | |
| 355 continue; | |
| 356 } | |
| 357 if (!test->isGPUTest() && !FLAGS_cpu) { | |
| 358 continue; | |
| 359 } | |
| 360 test->setReporter(&gTestReporter); | |
| 361 gTests.push_back().reset(test.detach()); | |
| 362 } | |
| 363 } | |
| 364 | |
| 365 static void run_test(SkAutoTDelete<skiatest::Test>* t) { | |
| 366 WallTimer timer; | |
| 367 timer.start(); | |
| 368 skiatest::Test* test = t->get(); | |
| 369 if (!FLAGS_dryRun) { | |
| 370 GrContextFactory grFactory; | |
| 371 test->setGrContextFactory(&grFactory); | |
| 372 test->run(); | |
| 373 if (!test->passed()) { | |
| 374 fail(SkStringPrintf("test %s failed", test->getName())); | |
| 375 } | |
| 376 } | |
| 377 timer.end(); | |
| 378 done(timer.fWall, "test", "", test->getName()); | |
| 379 } | |
| 380 | |
| 381 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~*/ | |
| 382 | 220 |
| 383 int dm_main(); | 221 int dm_main(); |
| 384 int dm_main() { | 222 int dm_main() { |
| 385 SetupCrashHandler(); | 223 SetupCrashHandler(); |
| 386 SkAutoGraphics ag; | 224 SkAutoGraphics ag; |
| 387 SkTaskGroup::Enabler enabled(FLAGS_threads); | 225 SkTaskGroup::Enabler enabled(FLAGS_threads); |
| 388 | 226 |
| 389 gather_srcs(); | 227 if (FLAGS_dryRun || FLAGS_veryVerbose) { |
| 390 gather_sinks(); | 228 FLAGS_verbose = true; |
| 391 gather_tests(); | 229 } |
| 230 #if SK_ENABLE_INST_COUNT |
| 231 gPrintInstCount = FLAGS_leaks; |
| 232 #endif |
| 392 | 233 |
| 393 gPending = gSrcs.count() * gSinks.count() + gTests.count(); | 234 SkTArray<SkString> configs; |
| 394 SkDebugf("%d srcs * %d sinks + %d tests == %d tasks\n", | 235 for (int i = 0; i < FLAGS_config.count(); i++) { |
| 395 gSrcs.count(), gSinks.count(), gTests.count(), gPending); | 236 SkStrSplit(FLAGS_config[i], ", ", &configs); |
| 396 | |
| 397 // We try to exploit as much parallelism as is safe. Most Src/Sink pairs ru
n on any thread, | |
| 398 // but Sinks that identify as part of a particular enclave run serially on a
single thread. | |
| 399 // Tests run on any thread, with a separate GrContextFactory for each GPU te
st. | |
| 400 SkTArray<Task> enclaves[kNumEnclaves]; | |
| 401 for (int j = 0; j < gSinks.count(); j++) { | |
| 402 SkTArray<Task>& tasks = enclaves[gSinks[j]->enclave()]; | |
| 403 for (int i = 0; i < gSrcs.count(); i++) { | |
| 404 tasks.push_back(Task(gSrcs[i], gSinks[j])); | |
| 405 } | |
| 406 } | 237 } |
| 407 | 238 |
| 408 SK_COMPILE_ASSERT(kAnyThread_Enclave == 0, AnyThreadZero); | 239 GrGLStandard gpuAPI = get_gl_standard(); |
| 409 SkTaskGroup tg; | |
| 410 tg.batch( Task::Run, enclaves[0].begin(), enclaves[0].count()); | |
| 411 tg.batch(run_enclave, enclaves+1, kNumEnclaves-1); | |
| 412 tg.batch( run_test, gTests.begin(), gTests.count()); | |
| 413 tg.wait(); | |
| 414 | 240 |
| 415 if (!FLAGS_verbose) { | 241 SkTDArray<GMRegistry::Factory> gms; |
| 416 SkDebugf("\n"); | 242 if (FLAGS_gms) { |
| 243 append_matching_factories<GM>(GMRegistry::Head(), &gms); |
| 417 } | 244 } |
| 418 | 245 |
| 419 JsonWriter::DumpJson(); | 246 SkTDArray<TestRegistry::Factory> tests; |
| 420 return gPending == 0 && gFailures == 0 ? 0 : 1; | 247 if (FLAGS_tests) { |
| 248 append_matching_factories<Test>(TestRegistry::Head(), &tests); |
| 249 } |
| 250 |
| 251 |
| 252 SkTArray<SkString> skps; |
| 253 if (!FLAGS_skps.isEmpty()) { |
| 254 const char* suffixes[] = { "skp" }; |
| 255 find_files(FLAGS_skps[0], suffixes, SK_ARRAY_COUNT(suffixes), &skps); |
| 256 } |
| 257 |
| 258 SkTArray<SkString> images; |
| 259 if (!FLAGS_images.isEmpty()) { |
| 260 const char* suffixes[] = { |
| 261 "bmp", "gif", "jpg", "jpeg", "png", "webp", "ktx", "astc", "wbmp", "
ico", |
| 262 "BMP", "GIF", "JPG", "JPEG", "PNG", "WEBP", "KTX", "ASTC", "WBMP", "
ICO", |
| 263 }; |
| 264 find_files(FLAGS_images[0], suffixes, SK_ARRAY_COUNT(suffixes), &images)
; |
| 265 } |
| 266 |
| 267 SkDebugf("%d GMs x %d configs, %d tests, %d pictures, %d images\n", |
| 268 gms.count(), configs.count(), tests.count(), skps.count(), images.c
ount()); |
| 269 DM::Reporter reporter; |
| 270 |
| 271 DM::TaskRunner tasks; |
| 272 kick_off_tests(tests, &reporter, &tasks); |
| 273 kick_off_gms(gms, configs, gpuAPI, &reporter, &tasks); |
| 274 kick_off_skps(skps, &reporter, &tasks); |
| 275 kick_off_images(images, &reporter, &tasks); |
| 276 tasks.wait(); |
| 277 |
| 278 DM::JsonWriter::DumpJson(); |
| 279 |
| 280 SkDebugf("\n"); |
| 281 #ifdef SK_DEBUG |
| 282 if (FLAGS_portableFonts && FLAGS_reportUsedChars) { |
| 283 sk_tool_utils::report_used_chars(); |
| 284 } |
| 285 #endif |
| 286 |
| 287 SkTArray<SkString> failures; |
| 288 reporter.getFailures(&failures); |
| 289 report_failures(failures); |
| 290 return failures.count() > 0; |
| 421 } | 291 } |
| 422 | 292 |
| 423 #if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL) | 293 #if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL) |
| 424 int main(int argc, char** argv) { | 294 int main(int argc, char** argv) { |
| 425 SkCommandLineFlags::Parse(argc, argv); | 295 SkCommandLineFlags::Parse(argc, argv); |
| 426 return dm_main(); | 296 return dm_main(); |
| 427 } | 297 } |
| 428 #endif | 298 #endif |
| OLD | NEW |