blob: d8efc5844812f9b3d8637aa2aad21f3eb571f5e4 [file] [log] [blame]
jcgregorio3b27ade2014-11-13 08:06:40 -08001#include "CrashHandler.h"
mtklein709d2c32015-01-15 08:30:25 -08002#include "DMJsonWriter.h"
3#include "DMSrcSink.h"
4#include "OverwriteLine.h"
5#include "ProcStats.h"
6#include "SkBBHFactory.h"
caryclark17f0b6d2014-07-22 10:15:34 -07007#include "SkCommonFlags.h"
mtklein@google.comd36522d2013-10-16 13:02:15 +00008#include "SkForceLinking.h"
9#include "SkGraphics.h"
mtklein709d2c32015-01-15 08:30:25 -080010#include "SkMD5.h"
mtklein1d0f1642014-09-08 08:05:18 -070011#include "SkOSFile.h"
mtklein406654b2014-09-03 15:34:37 -070012#include "SkTaskGroup.h"
commit-bot@chromium.org0dc5bd12014-02-26 16:31:22 +000013#include "Test.h"
mtklein709d2c32015-01-15 08:30:25 -080014#include "Timer.h"
mtklein@google.comd36522d2013-10-16 13:02:15 +000015
commit-bot@chromium.org0dc5bd12014-02-26 16:31:22 +000016DEFINE_bool(tests, true, "Run tests?");
mtklein709d2c32015-01-15 08:30:25 -080017DEFINE_string(images, "resources", "Images to decode.");
18DEFINE_string(src, "gm skp image subset", "Source types to test.");
19DEFINE_bool(nameByHash, false,
20 "If true, write to FLAGS_writePath[0]/<hash>.png instead of "
21 "to FLAGS_writePath[0]/<config>/<sourceType>/<name>.png");
22DEFINE_bool2(pathOpsExtended, x, false, "Run extended pathOps tests.");
23DEFINE_string(matrix, "1 0 0 0 1 0 0 0 1",
24 "Matrix to apply when using 'matrix' in config.");
mtklein@google.comd36522d2013-10-16 13:02:15 +000025
26__SK_FORCE_IMAGE_DECODER_LINKING;
mtklein709d2c32015-01-15 08:30:25 -080027using namespace DM;
mtklein@google.comd36522d2013-10-16 13:02:15 +000028
mtklein709d2c32015-01-15 08:30:25 -080029/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
30
31static int gPending = 0, gFailures = 0;
32
33static void fail(ImplicitString err) {
34 SkDebugf("\n\nERROR: %s\n\n", err.c_str());
35 sk_atomic_inc(&gFailures);
halcanary9e398f72015-01-10 11:18:04 -080036}
37
mtklein709d2c32015-01-15 08:30:25 -080038static void done(double ms, ImplicitString config, ImplicitString src, ImplicitString name) {
39 SkDebugf("%s(%4dMB %5d) %s\t%s %s %s ", FLAGS_verbose ? "\n" : kSkOverwriteLine
40 , sk_tools::getMaxResidentSetSizeMB()
41 , sk_atomic_dec(&gPending)-1
42 , HumanizeMs(ms).c_str()
43 , config.c_str()
44 , src.c_str()
45 , name.c_str());
mtklein@google.comd36522d2013-10-16 13:02:15 +000046}
47
mtklein709d2c32015-01-15 08:30:25 -080048/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
commit-bot@chromium.org38aeb0f2014-02-26 23:01:57 +000049
mtklein709d2c32015-01-15 08:30:25 -080050template <typename T>
51struct Tagged : public SkAutoTDelete<T> { const char* tag; };
52
53static const bool kMemcpyOK = true;
54
55static SkTArray<Tagged<Src>, kMemcpyOK> gSrcs;
56static SkTArray<Tagged<Sink>, kMemcpyOK> gSinks;
57
58static void push_src(const char* tag, Src* s) {
59 SkAutoTDelete<Src> src(s);
60 if (FLAGS_src.contains(tag) &&
61 !SkCommandLineFlags::ShouldSkip(FLAGS_match, src->name().c_str())) {
62 Tagged<Src>& s = gSrcs.push_back();
63 s.reset(src.detach());
64 s.tag = tag;
mtklein@google.comd36522d2013-10-16 13:02:15 +000065 }
mtklein709d2c32015-01-15 08:30:25 -080066}
kkinnunen80549fc2014-06-30 06:36:31 -070067
mtklein709d2c32015-01-15 08:30:25 -080068static void gather_srcs() {
69 for (const skiagm::GMRegistry* r = skiagm::GMRegistry::Head(); r; r = r->next()) {
70 push_src("gm", new GMSrc(r->factory()));
71 }
72 if (!FLAGS_skps.isEmpty()) {
73 SkOSFile::Iter it(FLAGS_skps[0], "skp");
74 for (SkString file; it.next(&file); ) {
75 push_src("skp", new SKPSrc(SkOSPath::Join(FLAGS_skps[0], file.c_str())));
commit-bot@chromium.org38aeb0f2014-02-26 23:01:57 +000076 }
77 }
mtklein709d2c32015-01-15 08:30:25 -080078 if (!FLAGS_images.isEmpty()) {
79 const char* exts[] = {
80 "bmp", "gif", "jpg", "jpeg", "png", "webp", "ktx", "astc", "wbmp", "ico",
81 "BMP", "GIF", "JPG", "JPEG", "PNG", "WEBP", "KTX", "ASTC", "WBMP", "ICO",
82 };
83 for (size_t i = 0; i < SK_ARRAY_COUNT(exts); i++) {
84 SkOSFile::Iter it(FLAGS_images[0], exts[i]);
85 for (SkString file; it.next(&file); ) {
86 SkString path = SkOSPath::Join(FLAGS_images[0], file.c_str());
87 push_src("image", new ImageSrc(path)); // Decode entire image.
88 push_src("subset", new ImageSrc(path, 5)); // Decode 5 random subsets.
mtkleinf6139f72014-12-12 16:41:12 -080089 }
commit-bot@chromium.org90b5a2a2014-05-14 17:55:32 +000090 }
halcanarya98683b2014-07-28 07:21:24 -070091 }
92}
commit-bot@chromium.org90b5a2a2014-05-14 17:55:32 +000093
mtklein709d2c32015-01-15 08:30:25 -080094static GrGLStandard get_gpu_api() {
95 if (FLAGS_gpuAPI.contains("gl")) { return kGL_GrGLStandard; }
96 if (FLAGS_gpuAPI.contains("gles")) { return kGLES_GrGLStandard; }
97 return kNone_GrGLStandard;
commit-bot@chromium.org90b5a2a2014-05-14 17:55:32 +000098}
99
mtklein709d2c32015-01-15 08:30:25 -0800100static void push_sink(const char* tag, Sink* s) {
101 SkAutoTDelete<Sink> sink(s);
102 if (!FLAGS_config.contains(tag)) {
103 return;
mtkleinf6139f72014-12-12 16:41:12 -0800104 }
mtklein709d2c32015-01-15 08:30:25 -0800105 // Try a noop Src as a canary. If it fails, skip this sink.
106 struct : public Src {
107 Error draw(SkCanvas*) const SK_OVERRIDE { return ""; }
108 SkISize size() const SK_OVERRIDE { return SkISize::Make(16, 16); }
109 Name name() const SK_OVERRIDE { return "noop"; }
110 } noop;
mtkleinf6139f72014-12-12 16:41:12 -0800111
mtklein709d2c32015-01-15 08:30:25 -0800112 SkBitmap bitmap;
113 SkDynamicMemoryWStream stream;
114 Error err = sink->draw(noop, &bitmap, &stream);
115 if (!err.isEmpty()) {
116 SkDebugf("Skipping %s: %s\n", tag, err.c_str());
mtklein@google.comd36522d2013-10-16 13:02:15 +0000117 return;
118 }
119
mtklein709d2c32015-01-15 08:30:25 -0800120 Tagged<Sink>& ts = gSinks.push_back();
121 ts.reset(sink.detach());
122 ts.tag = tag;
123}
124
125static bool gpu_supported() {
126#if SK_SUPPORT_GPU
127 return FLAGS_gpu;
128#else
129 return false;
130#endif
131}
132
133static 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
mtklein@google.comd36522d2013-10-16 13:02:15 +0000151 }
mtklein709d2c32015-01-15 08:30:25 -0800152
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;
mtklein@google.comd36522d2013-10-16 13:02:15 +0000161}
162
mtklein709d2c32015-01-15 08:30:25 -0800163static 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;
kkinnunen80549fc2014-06-30 06:36:31 -0700185}
186
mtklein709d2c32015-01-15 08:30:25 -0800187static 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, sink);
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);
commit-bot@chromium.org38aeb0f2014-02-26 23:01:57 +0000207 }
208 }
209}
210
mtklein709d2c32015-01-15 08:30:25 -0800211// The finest-grained unit of work we can run: draw a single Src into a single Sink,
212// report any errors, and perhaps write out the output: a .png of the bitmap, or a raw stream.
213struct 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::kPNG_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.
322static 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
332static 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
343static SkTArray<SkAutoTDelete<skiatest::Test>, kMemcpyOK> gTests;
344
345static 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
365static 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
jcgregorio3b27ade2014-11-13 08:06:40 -0800383int dm_main();
caryclark17f0b6d2014-07-22 10:15:34 -0700384int dm_main() {
jcgregorio3b27ade2014-11-13 08:06:40 -0800385 SetupCrashHandler();
commit-bot@chromium.org39e8d932014-05-29 20:14:48 +0000386 SkAutoGraphics ag;
mtklein406654b2014-09-03 15:34:37 -0700387 SkTaskGroup::Enabler enabled(FLAGS_threads);
commit-bot@chromium.orga65e2fd2014-05-30 17:23:31 +0000388
mtklein709d2c32015-01-15 08:30:25 -0800389 gather_srcs();
390 gather_sinks();
391 gather_tests();
commit-bot@chromium.org0dc5bd12014-02-26 16:31:22 +0000392
mtklein709d2c32015-01-15 08:30:25 -0800393 gPending = gSrcs.count() * gSinks.count() + gTests.count();
394 SkDebugf("%d srcs * %d sinks + %d tests == %d tasks\n",
395 gSrcs.count(), gSinks.count(), gTests.count(), gPending);
396
397 // We try to exploit as much parallelism as is safe. Most Src/Sink pairs run 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 test.
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 }
commit-bot@chromium.org38aeb0f2014-02-26 23:01:57 +0000406 }
407
mtklein709d2c32015-01-15 08:30:25 -0800408 SK_COMPILE_ASSERT(kAnyThread_Enclave == 0, AnyThreadZero);
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();
kkinnunen80549fc2014-06-30 06:36:31 -0700414
mtklein709d2c32015-01-15 08:30:25 -0800415 if (!FLAGS_verbose) {
416 SkDebugf("\n");
mtklein@google.comd36522d2013-10-16 13:02:15 +0000417 }
418
mtklein709d2c32015-01-15 08:30:25 -0800419 JsonWriter::DumpJson();
420 return gPending == 0 && gFailures == 0 ? 0 : 1;
mtklein@google.comd36522d2013-10-16 13:02:15 +0000421}
jcgregorio3b27ade2014-11-13 08:06:40 -0800422
423#if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL)
424int main(int argc, char** argv) {
425 SkCommandLineFlags::Parse(argc, argv);
426 return dm_main();
427}
428#endif