static void path_fuzz_stroker(SkBitmap* bitmap, int seed) { ThreadState states[100]; for (size_t i = 0; i < SK_ARRAY_COUNT(states); i++) { states[i].fSeed = seed + (int) i; states[i].fBitmap = bitmap; } SkTaskGroup tg; tg.batch(test_fuzz, states, SK_ARRAY_COUNT(states)); }
int dm_main() { SetupCrashHandler(); SkAutoGraphics ag; SkTaskGroup::Enabler enabled(FLAGS_threads); gCreateTypefaceDelegate = &create_from_name; start_keepalive(); gather_gold(); gather_uninteresting_hashes(); gather_srcs(); gather_sinks(); gather_tests(); gPending = gSrcs.count() * gSinks.count() + gThreadedTests.count() + gGPUTests.count(); SkDebugf("%d srcs * %d sinks + %d tests == %d tasks\n", gSrcs.count(), gSinks.count(), gThreadedTests.count() + gGPUTests.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. // CPU tests run on any thread. GPU tests depend on --gpu_threading. 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])); } } SkTaskGroup tg; tg.batch(run_test, gThreadedTests.begin(), gThreadedTests.count()); for (int i = 0; i < kNumEnclaves; i++) { switch(i) { case kAnyThread_Enclave: tg.batch(Task::Run, enclaves[i].begin(), enclaves[i].count()); break; case kGPU_Enclave: tg.add(run_enclave_and_gpu_tests, &enclaves[i]); break; default: tg.add(run_enclave, &enclaves[i]); break; } } tg.wait(); // At this point we're back in single-threaded land. sk_tool_utils::release_portable_typefaces(); SkDebugf("\n"); if (gFailures.count() > 0) { SkDebugf("Failures:\n"); for (int i = 0; i < gFailures.count(); i++) { SkDebugf("\t%s\n", gFailures[i].c_str()); } SkDebugf("%d failures\n", gFailures.count()); return 1; } if (gPending > 0) { SkDebugf("Hrm, we didn't seem to run everything we intended to! Please file a bug.\n"); return 1; } return 0; }
static void Run(const Task& task) { SkString name = task.src->name(); // We'll skip drawing this Src/Sink pair if: // - the Src vetoes the Sink; // - this Src / Sink combination is on the blacklist; // - it's a dry run. SkString note(task.src->veto(task.sink->flags()) ? " (veto)" : ""); SkString whyBlacklisted = is_blacklisted(task.sink.tag.c_str(), task.src.tag.c_str(), task.src.options.c_str(), name.c_str()); if (!whyBlacklisted.isEmpty()) { note.appendf(" (--blacklist %s)", whyBlacklisted.c_str()); } SkString log; auto timerStart = now_ms(); if (!FLAGS_dryRun && note.isEmpty()) { SkBitmap bitmap; SkDynamicMemoryWStream stream; if (FLAGS_pre_log) { SkDebugf("\nRunning %s->%s", name.c_str(), task.sink.tag.c_str()); } start(task.sink.tag.c_str(), task.src.tag, task.src.options, name.c_str()); Error err = task.sink->draw(*task.src, &bitmap, &stream, &log); if (!err.isEmpty()) { if (err.isFatal()) { fail(SkStringPrintf("%s %s %s %s: %s", task.sink.tag.c_str(), task.src.tag.c_str(), task.src.options.c_str(), name.c_str(), err.c_str())); } else { note.appendf(" (skipped: %s)", err.c_str()); auto elapsed = now_ms() - timerStart; done(elapsed, task.sink.tag.c_str(), task.src.tag, task.src.options, name, note, log); return; } } // We're likely switching threads here, so we must capture by value, [=] or [foo,bar]. SkStreamAsset* data = stream.detachAsStream(); gDefinitelyThreadSafeWork.add([task,name,bitmap,data]{ SkAutoTDelete<SkStreamAsset> ownedData(data); // Why doesn't the copy constructor do this when we have pre-locked pixels? bitmap.lockPixels(); SkString md5; if (!FLAGS_writePath.isEmpty() || !FLAGS_readPath.isEmpty()) { SkMD5 hash; if (data->getLength()) { hash.writeStream(data, data->getLength()); data->rewind(); } else { // If we're BGRA (Linux, Windows), swizzle over to RGBA (Mac, Android). // This helps eliminate multiple 0-pixel-diff hashes on gold.skia.org. // (Android's general slow speed breaks the tie arbitrarily in RGBA's favor.) // We might consider promoting 565 to RGBA too. if (bitmap.colorType() == kBGRA_8888_SkColorType) { SkBitmap swizzle; SkAssertResult(bitmap.copyTo(&swizzle, kRGBA_8888_SkColorType)); hash.write(swizzle.getPixels(), swizzle.getSize()); } else { hash.write(bitmap.getPixels(), bitmap.getSize()); } } SkMD5::Digest digest; hash.finish(digest); for (int i = 0; i < 16; i++) { md5.appendf("%02x", digest.data[i]); } } if (!FLAGS_readPath.isEmpty() && !gGold.contains(Gold(task.sink.tag.c_str(), task.src.tag.c_str(), task.src.options.c_str(), name, md5))) { fail(SkStringPrintf("%s not found for %s %s %s %s in %s", md5.c_str(), task.sink.tag.c_str(), task.src.tag.c_str(), task.src.options.c_str(), name.c_str(), FLAGS_readPath[0])); } if (!FLAGS_writePath.isEmpty()) { const char* ext = task.sink->fileExtension(); if (data->getLength()) { WriteToDisk(task, md5, ext, data, data->getLength(), nullptr); SkASSERT(bitmap.drawsNothing()); } else if (!bitmap.drawsNothing()) { WriteToDisk(task, md5, ext, nullptr, 0, &bitmap); } } }); } auto elapsed = now_ms() - timerStart; done(elapsed, task.sink.tag.c_str(), task.src.tag.c_str(), task.src.options.c_str(), name, note, log); }
int dm_main() { SetupCrashHandler(); JsonWriter::DumpJson(); // It's handy for the bots to assume this is ~never missing. SkAutoGraphics ag; SkTaskGroup::Enabler enabled(FLAGS_threads); gCreateTypefaceDelegate = &create_from_name; start_keepalive(); gather_gold(); gather_uninteresting_hashes(); if (!gather_srcs()) { return 1; } gather_sinks(); gather_tests(); gPending = gSrcs.count() * gSinks.count() + gParallelTests.count() + gSerialTests.count(); SkDebugf("%d srcs * %d sinks + %d tests == %d tasks\n", gSrcs.count(), gSinks.count(), gParallelTests.count() + gSerialTests.count(), gPending); // Kick off as much parallel work as we can, making note of any serial work we'll need to do. SkTaskGroup parallel; SkTArray<Task> serial; for (auto& sink : gSinks) for (auto& src : gSrcs) { Task task(src, sink); if (src->serial() || sink->serial()) { serial.push_back(task); } else { parallel.add([task] { Task::Run(task); }); } } for (auto test : gParallelTests) { parallel.add([test] { run_test(test); }); } // With the parallel work running, run serial tasks and tests here on main thread. for (auto task : serial) { Task::Run(task); } for (auto test : gSerialTests) { run_test(test); } // Wait for any remaining parallel work to complete (including any spun off of serial tasks). parallel.wait(); gDefinitelyThreadSafeWork.wait(); // At this point we're back in single-threaded land. sk_tool_utils::release_portable_typefaces(); if (FLAGS_verbose && gNoteTally.count() > 0) { SkDebugf("\nNote tally:\n"); gNoteTally.foreach([](const SkString& note, int* tally) { SkDebugf("%dx\t%s\n", *tally, note.c_str()); }); } SkDebugf("\n"); if (gFailures.count() > 0) { SkDebugf("Failures:\n"); for (int i = 0; i < gFailures.count(); i++) { SkDebugf("\t%s\n", gFailures[i].c_str()); } SkDebugf("%d failures\n", gFailures.count()); return 1; } if (gPending > 0) { SkDebugf("Hrm, we didn't seem to run everything we intended to! Please file a bug.\n"); return 1; } #ifdef SK_PDF_IMAGE_STATS SkPDFImageDumpStats(); #endif // SK_PDF_IMAGE_STATS SkDebugf("Finished!\n"); return 0; }
int test_main() { SetupCrashHandler(); SkAutoGraphics ag; { SkString header("Skia UnitTests:"); if (!FLAGS_match.isEmpty()) { header.appendf(" --match"); for (int index = 0; index < FLAGS_match.count(); ++index) { header.appendf(" %s", FLAGS_match[index]); } } SkString tmpDir = skiatest::GetTmpDir(); if (!tmpDir.isEmpty()) { header.appendf(" --tmpDir %s", tmpDir.c_str()); } SkString resourcePath = GetResourcePath(); if (!resourcePath.isEmpty()) { header.appendf(" --resourcePath %s", resourcePath.c_str()); } #ifdef SK_DEBUG header.append(" SK_DEBUG"); #else header.append(" SK_RELEASE"); #endif if (FLAGS_veryVerbose) { header.appendf("\n"); } SkDebugf("%s", header.c_str()); } // Count tests first. int total = 0; int toRun = 0; for (const TestRegistry* iter = TestRegistry::Head(); iter; iter = iter->next()) { const Test& test = iter->factory(); if (should_run(test.name, test.needsGpu)) { toRun++; } total++; } // Now run them. int skipCount = 0; SkTaskGroup::Enabler enabled(FLAGS_threads); SkTaskGroup cpuTests; SkTArray<const Test*> gpuTests; Status status(toRun); for (const TestRegistry* iter = TestRegistry::Head(); iter; iter = iter->next()) { const Test& test = iter->factory(); if (!should_run(test.name, test.needsGpu)) { ++skipCount; } else if (test.needsGpu) { gpuTests.push_back(&test); } else { cpuTests.add(new SkTestRunnable(test, &status)); } } GrContextFactory* grContextFactoryPtr = NULL; #if SK_SUPPORT_GPU // Give GPU tests a context factory if that makes sense on this machine. GrContextFactory grContextFactory; grContextFactoryPtr = &grContextFactory; #endif // Run GPU tests on this thread. for (int i = 0; i < gpuTests.count(); i++) { (new SkTestRunnable(*gpuTests[i], &status, grContextFactoryPtr))->run(); } // Block until threaded tests finish. cpuTests.wait(); if (FLAGS_verbose) { SkDebugf( "\nFinished %d tests, %d failures, %d skipped. " "(%d internal tests)", toRun, status.failCount(), skipCount, status.testCount()); } SkDebugf("\n"); return (status.failCount() == 0) ? 0 : 1; }
int dm_main() { setbuf(stdout, nullptr); setup_crash_handler(); if (FLAGS_verbose) { gVLog = stderr; } else if (!FLAGS_writePath.isEmpty()) { sk_mkdir(FLAGS_writePath[0]); gVLog = freopen(SkOSPath::Join(FLAGS_writePath[0], "verbose.log").c_str(), "w", stderr); } JsonWriter::DumpJson(); // It's handy for the bots to assume this is ~never missing. SkAutoGraphics ag; SkTaskGroup::Enabler enabled(FLAGS_threads); gCreateTypefaceDelegate = &create_from_name; { SkString testResourcePath = GetResourcePath("color_wheel.png"); SkFILEStream testResource(testResourcePath.c_str()); if (!testResource.isValid()) { info("Some resources are missing. Do you need to set --resourcePath?\n"); } } gather_gold(); gather_uninteresting_hashes(); if (!gather_srcs()) { return 1; } if (!gather_sinks()) { return 1; } gather_tests(); gPending = gSrcs.count() * gSinks.count() + gParallelTests.count() + gSerialTests.count(); info("%d srcs * %d sinks + %d tests == %d tasks", gSrcs.count(), gSinks.count(), gParallelTests.count() + gSerialTests.count(), gPending); SkAutoTDelete<SkThread> statusThread(start_status_thread()); // Kick off as much parallel work as we can, making note of any serial work we'll need to do. SkTaskGroup parallel; SkTArray<Task> serial; for (auto& sink : gSinks) for (auto& src : gSrcs) { if (src->veto(sink->flags()) || is_blacklisted(sink.tag.c_str(), src.tag.c_str(), src.options.c_str(), src->name().c_str())) { SkAutoMutexAcquire lock(gMutex); gPending--; continue; } Task task(src, sink); if (src->serial() || sink->serial()) { serial.push_back(task); } else { parallel.add([task] { Task::Run(task); }); } } for (auto test : gParallelTests) { parallel.add([test] { run_test(test); }); } // With the parallel work running, run serial tasks and tests here on main thread. for (auto task : serial) { Task::Run(task); } for (auto test : gSerialTests) { run_test(test); } // Wait for any remaining parallel work to complete (including any spun off of serial tasks). parallel.wait(); gDefinitelyThreadSafeWork.wait(); // We'd better have run everything. SkASSERT(gPending == 0); // Make sure we've flushed all our results to disk. JsonWriter::DumpJson(); // At this point we're back in single-threaded land. sk_tool_utils::release_portable_typefaces(); if (gFailures.count() > 0) { info("Failures:\n"); for (int i = 0; i < gFailures.count(); i++) { info("\t%s\n", gFailures[i].c_str()); } info("%d failures\n", gFailures.count()); return 1; } #ifdef SK_PDF_IMAGE_STATS SkPDFImageDumpStats(); #endif // SK_PDF_IMAGE_STATS print_status(); info("Finished!\n"); return 0; }
void TestRunner::render() { SkTaskGroup tg; for (int index = 0; index < fRunnables.count(); ++ index) { tg.add(fRunnables[index]); } }