blob: df7c32f9e7bc91d28608148f94bb50ffd1f5ddd7 [file] [log] [blame]
jcgregorio3b27ade2014-11-13 08:06:40 -08001#include "CrashHandler.h"
mtklein748ca3b2015-01-15 10:56:12 -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"
mtklein748ca3b2015-01-15 10:56:12 -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"
mtklein748ca3b2015-01-15 10:56:12 -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?");
mtklein748ca3b2015-01-15 10:56:12 -080017DEFINE_string(images, "resources", "Images to decode.");
18//DEFINE_string(src, "gm skp image subset", "Source types to test.");
19DEFINE_string(src, "gm skp", "Source types to test. TEMPORARILY DISABLED");
20DEFINE_bool(nameByHash, false,
21 "If true, write to FLAGS_writePath[0]/<hash>.png instead of "
22 "to FLAGS_writePath[0]/<config>/<sourceType>/<name>.png");
23DEFINE_bool2(pathOpsExtended, x, false, "Run extended pathOps tests.");
24DEFINE_string(matrix, "1 0 0 0 1 0 0 0 1",
25 "Matrix to apply when using 'matrix' in config.");
mtklein@google.comd36522d2013-10-16 13:02:15 +000026
27__SK_FORCE_IMAGE_DECODER_LINKING;
mtklein748ca3b2015-01-15 10:56:12 -080028using namespace DM;
mtklein@google.comd36522d2013-10-16 13:02:15 +000029
mtklein748ca3b2015-01-15 10:56:12 -080030/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
31
32SK_DECLARE_STATIC_MUTEX(gFailuresMutex);
33static SkTArray<SkString> gFailures;
34
35static void fail(ImplicitString err) {
36 SkAutoMutexAcquire lock(gFailuresMutex);
37 SkDebugf("\n\nFAILURE: %s\n\n", err.c_str());
38 gFailures.push_back(err);
halcanary9e398f72015-01-10 11:18:04 -080039}
40
mtklein748ca3b2015-01-15 10:56:12 -080041static int32_t gPending = 0; // Atomic.
42
43static void done(double ms, ImplicitString config, ImplicitString src, ImplicitString name) {
44 SkDebugf("%s(%4dMB %5d) %s\t%s %s %s ", FLAGS_verbose ? "\n" : kSkOverwriteLine
45 , sk_tools::getMaxResidentSetSizeMB()
46 , sk_atomic_dec(&gPending)-1
47 , HumanizeMs(ms).c_str()
48 , config.c_str()
49 , src.c_str()
50 , name.c_str());
mtklein@google.comd36522d2013-10-16 13:02:15 +000051}
52
mtklein748ca3b2015-01-15 10:56:12 -080053/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
mtklein114c3cd2015-01-15 10:15:02 -080054
mtklein748ca3b2015-01-15 10:56:12 -080055template <typename T>
56struct Tagged : public SkAutoTDelete<T> { const char* tag; };
57
58static const bool kMemcpyOK = true;
59
60static SkTArray<Tagged<Src>, kMemcpyOK> gSrcs;
61static SkTArray<Tagged<Sink>, kMemcpyOK> gSinks;
62
63static void push_src(const char* tag, Src* s) {
64 SkAutoTDelete<Src> src(s);
65 if (FLAGS_src.contains(tag) &&
66 !SkCommandLineFlags::ShouldSkip(FLAGS_match, src->name().c_str())) {
67 Tagged<Src>& s = gSrcs.push_back();
68 s.reset(src.detach());
69 s.tag = tag;
mtklein114c3cd2015-01-15 10:15:02 -080070 }
mtklein748ca3b2015-01-15 10:56:12 -080071}
mtklein114c3cd2015-01-15 10:15:02 -080072
mtklein748ca3b2015-01-15 10:56:12 -080073static void gather_srcs() {
74 for (const skiagm::GMRegistry* r = skiagm::GMRegistry::Head(); r; r = r->next()) {
75 push_src("gm", new GMSrc(r->factory()));
76 }
77 if (!FLAGS_skps.isEmpty()) {
78 SkOSFile::Iter it(FLAGS_skps[0], "skp");
79 for (SkString file; it.next(&file); ) {
80 push_src("skp", new SKPSrc(SkOSPath::Join(FLAGS_skps[0], file.c_str())));
mtklein114c3cd2015-01-15 10:15:02 -080081 }
82 }
mtklein748ca3b2015-01-15 10:56:12 -080083 if (!FLAGS_images.isEmpty()) {
84 const char* exts[] = {
85 "bmp", "gif", "jpg", "jpeg", "png", "webp", "ktx", "astc", "wbmp", "ico",
86 "BMP", "GIF", "JPG", "JPEG", "PNG", "WEBP", "KTX", "ASTC", "WBMP", "ICO",
87 };
88 for (size_t i = 0; i < SK_ARRAY_COUNT(exts); i++) {
89 SkOSFile::Iter it(FLAGS_images[0], exts[i]);
90 for (SkString file; it.next(&file); ) {
91 SkString path = SkOSPath::Join(FLAGS_images[0], file.c_str());
92 push_src("image", new ImageSrc(path)); // Decode entire image.
93 push_src("subset", new ImageSrc(path, 5)); // Decode 5 random subsets.
mtklein114c3cd2015-01-15 10:15:02 -080094 }
mtklein709d2c32015-01-15 08:30:25 -080095 }
mtklein709d2c32015-01-15 08:30:25 -080096 }
97}
98
mtklein748ca3b2015-01-15 10:56:12 -080099static GrGLStandard get_gpu_api() {
100 if (FLAGS_gpuAPI.contains("gl")) { return kGL_GrGLStandard; }
101 if (FLAGS_gpuAPI.contains("gles")) { return kGLES_GrGLStandard; }
102 return kNone_GrGLStandard;
mtklein709d2c32015-01-15 08:30:25 -0800103}
104
mtklein748ca3b2015-01-15 10:56:12 -0800105static void push_sink(const char* tag, Sink* s) {
106 SkAutoTDelete<Sink> sink(s);
107 if (!FLAGS_config.contains(tag)) {
108 return;
mtklein114c3cd2015-01-15 10:15:02 -0800109 }
mtklein748ca3b2015-01-15 10:56:12 -0800110 // Try a noop Src as a canary. If it fails, skip this sink.
111 struct : public Src {
112 Error draw(SkCanvas*) const SK_OVERRIDE { return ""; }
113 SkISize size() const SK_OVERRIDE { return SkISize::Make(16, 16); }
114 Name name() const SK_OVERRIDE { return "noop"; }
115 } noop;
mtklein114c3cd2015-01-15 10:15:02 -0800116
mtklein748ca3b2015-01-15 10:56:12 -0800117 SkBitmap bitmap;
118 SkDynamicMemoryWStream stream;
119 Error err = sink->draw(noop, &bitmap, &stream);
120 if (!err.isEmpty()) {
121 SkDebugf("Skipping %s: %s\n", tag, err.c_str());
mtklein114c3cd2015-01-15 10:15:02 -0800122 return;
123 }
124
mtklein748ca3b2015-01-15 10:56:12 -0800125 Tagged<Sink>& ts = gSinks.push_back();
126 ts.reset(sink.detach());
127 ts.tag = tag;
128}
129
130static bool gpu_supported() {
131#if SK_SUPPORT_GPU
132 return FLAGS_gpu;
133#else
134 return false;
135#endif
136}
137
138static Sink* create_sink(const char* tag) {
139#define SINK(t, sink, ...) if (0 == strcmp(t, tag)) { return new sink(__VA_ARGS__); }
140 if (gpu_supported()) {
141 const GrGLStandard api = get_gpu_api();
142 SINK("gpunull", GPUSink, GrContextFactory::kNull_GLContextType, api, 0, false);
143 SINK("gpudebug", GPUSink, GrContextFactory::kDebug_GLContextType, api, 0, false);
144 SINK("gpu", GPUSink, GrContextFactory::kNative_GLContextType, api, 0, false);
145 SINK("gpudft", GPUSink, GrContextFactory::kNative_GLContextType, api, 0, true);
146 SINK("msaa4", GPUSink, GrContextFactory::kNative_GLContextType, api, 4, false);
147 SINK("msaa16", GPUSink, GrContextFactory::kNative_GLContextType, api, 16, false);
148 SINK("nvprmsaa4", GPUSink, GrContextFactory::kNVPR_GLContextType, api, 4, false);
149 SINK("nvprmsaa16", GPUSink, GrContextFactory::kNVPR_GLContextType, api, 16, false);
150 #if SK_ANGLE
151 SINK("angle", GPUSink, GrContextFactory::kANGLE_GLContextType, api, 0, false);
152 #endif
153 #if SK_MESA
154 SINK("mesa", GPUSink, GrContextFactory::kMESA_GLContextType, api, 0, false);
155 #endif
mtklein114c3cd2015-01-15 10:15:02 -0800156 }
mtklein748ca3b2015-01-15 10:56:12 -0800157
158 if (FLAGS_cpu) {
159 SINK("565", RasterSink, kRGB_565_SkColorType);
160 SINK("8888", RasterSink, kN32_SkColorType);
161 // TODO(mtklein): reenable once skiagold can handle .pdf uploads.
162 //SINK("pdf", PDFSink);
163 }
164#undef SINK
165 return NULL;
mtklein114c3cd2015-01-15 10:15:02 -0800166}
167
mtklein748ca3b2015-01-15 10:56:12 -0800168static Sink* create_via(const char* tag, Sink* wrapped) {
169#define VIA(t, via, ...) if (0 == strcmp(t, tag)) { return new via(__VA_ARGS__); }
170 VIA("serialize", ViaSerialization, wrapped);
171
172 VIA("tiles", ViaTiles, 256, 256, NULL, wrapped);
173 VIA("tiles_rt", ViaTiles, 256, 256, new SkRTreeFactory, wrapped);
174
175 const int xp = SkGPipeWriter::kCrossProcess_Flag,
176 sa = xp | SkGPipeWriter::kSharedAddressSpace_Flag;
177 VIA("pipe", ViaPipe, 0, wrapped);
178 VIA("pipe_xp", ViaPipe, xp, wrapped);
179 VIA("pipe_sa", ViaPipe, sa, wrapped);
180
181 if (FLAGS_matrix.count() == 9) {
182 SkMatrix m;
183 for (int i = 0; i < 9; i++) {
184 m[i] = (SkScalar)atof(FLAGS_matrix[i]);
185 }
186 VIA("matrix", ViaMatrix, m, wrapped);
187 }
188#undef VIA
189 return NULL;
mtklein114c3cd2015-01-15 10:15:02 -0800190}
191
mtklein748ca3b2015-01-15 10:56:12 -0800192static void gather_sinks() {
193 for (int i = 0; i < FLAGS_config.count(); i++) {
194 const char* config = FLAGS_config[i];
195 SkTArray<SkString> parts;
196 SkStrSplit(config, "-", &parts);
197
198 Sink* sink = NULL;
199 for (int i = parts.count(); i-- > 0;) {
200 const char* part = parts[i].c_str();
201 Sink* next = (sink == NULL) ? create_sink(part) : create_via(part, sink);
202 if (next == NULL) {
203 SkDebugf("Skipping %s: Don't understand '%s'.\n", config, part);
204 delete sink;
205 sink = NULL;
206 break;
207 }
208 sink = next;
209 }
210 if (sink) {
211 push_sink(config, sink);
mtklein114c3cd2015-01-15 10:15:02 -0800212 }
213 }
214}
mtklein709d2c32015-01-15 08:30:25 -0800215
mtklein748ca3b2015-01-15 10:56:12 -0800216// The finest-grained unit of work we can run: draw a single Src into a single Sink,
217// report any errors, and perhaps write out the output: a .png of the bitmap, or a raw stream.
218struct Task {
219 Task(const Tagged<Src>& src, const Tagged<Sink>& sink) : src(src), sink(sink) {}
220 const Tagged<Src>& src;
221 const Tagged<Sink>& sink;
222
223 static void Run(Task* task) {
224 WallTimer timer;
225 timer.start();
226 if (!FLAGS_dryRun) {
227 SkBitmap bitmap;
228 SkDynamicMemoryWStream stream;
229 Error err = task->sink->draw(*task->src, &bitmap, &stream);
230 if (!err.isEmpty()) {
231 fail(SkStringPrintf("%s %s %s: %s",
232 task->sink.tag,
233 task->src.tag,
234 task->src->name().c_str(),
235 err.c_str()));
236 }
237 if (!FLAGS_writePath.isEmpty()) {
238 const char* ext = task->sink->fileExtension();
239 if (stream.bytesWritten() == 0) {
240 SkMemoryStream pixels(bitmap.getPixels(), bitmap.getSize());
241 WriteToDisk(*task, &pixels, bitmap.getSize(), &bitmap, ext);
242 } else {
243 SkAutoTDelete<SkStreamAsset> data(stream.detachAsStream());
244 WriteToDisk(*task, data, data->getLength(), NULL, ext);
245 }
246 }
247 }
248 timer.end();
249 done(timer.fWall, task->sink.tag, task->src.tag, task->src->name());
250 }
251
252 static void WriteToDisk(const Task& task,
253 SkStream* data, size_t len,
254 const SkBitmap* bitmap,
255 const char* ext) {
256 SkMD5 hash;
257 hash.writeStream(data, len);
258 SkMD5::Digest digest;
259 hash.finish(digest);
260
261 JsonWriter::BitmapResult result;
262 result.name = task.src->name();
263 result.config = task.sink.tag;
264 result.sourceType = task.src.tag;
265 result.ext = ext;
266 for (int i = 0; i < 16; i++) {
267 result.md5.appendf("%02x", digest.data[i]);
268 }
269 JsonWriter::AddBitmapResult(result);
270
271 const char* dir = FLAGS_writePath[0];
272 if (0 == strcmp(dir, "@")) { // Needed for iOS.
273 dir = FLAGS_resourcePath[0];
274 }
275 sk_mkdir(dir);
276
277 SkString path;
278 if (FLAGS_nameByHash) {
279 path = SkOSPath::Join(dir, result.md5.c_str());
280 path.append(".");
281 path.append(ext);
282 if (sk_exists(path.c_str())) {
283 return; // Content-addressed. If it exists already, we're done.
284 }
285 } else {
286 path = SkOSPath::Join(dir, task.sink.tag);
287 sk_mkdir(path.c_str());
288 path = SkOSPath::Join(path.c_str(), task.src.tag);
289 sk_mkdir(path.c_str());
290 path = SkOSPath::Join(path.c_str(), task.src->name().c_str());
291 path.append(".");
292 path.append(ext);
293 }
294
295 SkFILEWStream file(path.c_str());
296 if (!file.isValid()) {
297 fail(SkStringPrintf("Can't open %s for writing.\n", path.c_str()));
298 return;
299 }
300
301 data->rewind();
302 if (bitmap) {
303 // We can't encode A8 bitmaps as PNGs. Convert them to 8888 first.
304 SkBitmap converted;
305 if (bitmap->info().colorType() == kAlpha_8_SkColorType) {
306 if (!bitmap->copyTo(&converted, kN32_SkColorType)) {
307 fail("Can't convert A8 to 8888.\n");
308 return;
309 }
310 bitmap = &converted;
311 }
312 if (!SkImageEncoder::EncodeStream(&file, *bitmap, SkImageEncoder::kPNG_Type, 100)) {
313 fail(SkStringPrintf("Can't encode PNG to %s.\n", path.c_str()));
314 return;
315 }
316 } else {
317 if (!file.writeStream(data, len)) {
318 fail(SkStringPrintf("Can't write to %s.\n", path.c_str()));
319 return;
320 }
321 }
322 }
323};
324
325// Run all tasks in the same enclave serially on the same thread.
326// They can't possibly run concurrently with each other.
327static void run_enclave(SkTArray<Task>* tasks) {
328 for (int i = 0; i < tasks->count(); i++) {
329 Task::Run(tasks->begin() + i);
330 }
331}
332
333/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
334
335// Unit tests don't fit so well into the Src/Sink model, so we give them special treatment.
336
337static struct : public skiatest::Reporter {
338 void onReportFailed(const skiatest::Failure& failure) SK_OVERRIDE {
339 SkString s;
340 failure.getFailureString(&s);
341 fail(s);
342 JsonWriter::AddTestFailure(failure);
343 }
344 bool allowExtendedTest() const SK_OVERRIDE { return FLAGS_pathOpsExtended; }
345 bool verbose() const SK_OVERRIDE { return FLAGS_veryVerbose; }
346} gTestReporter;
347
348static SkTArray<SkAutoTDelete<skiatest::Test>, kMemcpyOK> gTests;
349
350static void gather_tests() {
351 if (!FLAGS_tests) {
352 return;
353 }
354 for (const skiatest::TestRegistry* r = skiatest::TestRegistry::Head(); r; r = r->next()) {
355 SkAutoTDelete<skiatest::Test> test(r->factory()(NULL));
356 if (SkCommandLineFlags::ShouldSkip(FLAGS_match, test->getName())) {
357 continue;
358 }
359 if (test->isGPUTest() /*&& !gpu_supported()*/) { // TEMPORARILY DISABLED
360 continue;
361 }
362 if (!test->isGPUTest() && !FLAGS_cpu) {
363 continue;
364 }
365 test->setReporter(&gTestReporter);
366 gTests.push_back().reset(test.detach());
367 }
368}
369
370static void run_test(SkAutoTDelete<skiatest::Test>* t) {
371 WallTimer timer;
372 timer.start();
373 skiatest::Test* test = t->get();
374 if (!FLAGS_dryRun) {
375 GrContextFactory grFactory;
376 test->setGrContextFactory(&grFactory);
377 test->run();
378 if (!test->passed()) {
379 fail(SkStringPrintf("test %s failed", test->getName()));
380 }
381 }
382 timer.end();
383 done(timer.fWall, "unit", "test", test->getName());
384}
385
386/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
387
jcgregorio3b27ade2014-11-13 08:06:40 -0800388int dm_main();
caryclark17f0b6d2014-07-22 10:15:34 -0700389int dm_main() {
jcgregorio3b27ade2014-11-13 08:06:40 -0800390 SetupCrashHandler();
commit-bot@chromium.org39e8d932014-05-29 20:14:48 +0000391 SkAutoGraphics ag;
mtklein406654b2014-09-03 15:34:37 -0700392 SkTaskGroup::Enabler enabled(FLAGS_threads);
commit-bot@chromium.orga65e2fd2014-05-30 17:23:31 +0000393
mtklein748ca3b2015-01-15 10:56:12 -0800394 gather_srcs();
395 gather_sinks();
396 gather_tests();
commit-bot@chromium.org0dc5bd12014-02-26 16:31:22 +0000397
mtklein748ca3b2015-01-15 10:56:12 -0800398 gPending = gSrcs.count() * gSinks.count() + gTests.count();
399 SkDebugf("%d srcs * %d sinks + %d tests == %d tasks\n",
400 gSrcs.count(), gSinks.count(), gTests.count(), gPending);
401
402 // We try to exploit as much parallelism as is safe. Most Src/Sink pairs run on any thread,
403 // but Sinks that identify as part of a particular enclave run serially on a single thread.
404 // Tests run on any thread, with a separate GrContextFactory for each GPU test.
405 SkTArray<Task> enclaves[kNumEnclaves];
406 for (int j = 0; j < gSinks.count(); j++) {
407 SkTArray<Task>& tasks = enclaves[gSinks[j]->enclave()];
408 for (int i = 0; i < gSrcs.count(); i++) {
409 tasks.push_back(Task(gSrcs[i], gSinks[j]));
410 }
commit-bot@chromium.org38aeb0f2014-02-26 23:01:57 +0000411 }
412
mtklein748ca3b2015-01-15 10:56:12 -0800413 SK_COMPILE_ASSERT(kAnyThread_Enclave == 0, AnyThreadZero);
414 SkTaskGroup tg;
415 tg.batch( Task::Run, enclaves[0].begin(), enclaves[0].count());
416 tg.batch(run_enclave, enclaves+1, kNumEnclaves-1);
417 tg.batch( run_test, gTests.begin(), gTests.count());
418 tg.wait();
kkinnunen80549fc2014-06-30 06:36:31 -0700419
mtklein748ca3b2015-01-15 10:56:12 -0800420 // At this point we're back in single-threaded land.
421
422 if (!FLAGS_verbose) {
423 SkDebugf("\n");
mtklein@google.comd36522d2013-10-16 13:02:15 +0000424 }
425
mtklein748ca3b2015-01-15 10:56:12 -0800426 JsonWriter::DumpJson();
427
428 if (gFailures.count() > 0) {
429 SkDebugf("Failures:\n");
430 for (int i = 0; i < gFailures.count(); i++) {
431 SkDebugf("\t%s", gFailures[i].c_str());
432 }
433 SkDebugf("%d failures\n", gFailures.count());
434 return 1;
mtklein114c3cd2015-01-15 10:15:02 -0800435 }
mtklein748ca3b2015-01-15 10:56:12 -0800436 if (gPending > 0) {
437 SkDebugf("Hrm, we didn't seem to run everything we intended to! Please file a bug.\n");
438 return 1;
mtklein114c3cd2015-01-15 10:15:02 -0800439 }
mtklein748ca3b2015-01-15 10:56:12 -0800440 return 0;
mtklein@google.comd36522d2013-10-16 13:02:15 +0000441}
jcgregorio3b27ade2014-11-13 08:06:40 -0800442
443#if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL)
444int main(int argc, char** argv) {
445 SkCommandLineFlags::Parse(argc, argv);
446 return dm_main();
447}
448#endif