1 // Main binary for DM. 2 // For a high-level overview, please see dm/README. 3 4 #include "Benchmark.h" 5 #include "CrashHandler.h" 6 #include "SkCommandLineFlags.h" 7 #include "SkForceLinking.h" 8 #include "SkGraphics.h" 9 #include "SkPicture.h" 10 #include "SkString.h" 11 #include "Test.h" 12 #include "gm.h" 13 14 #include "DMBenchTask.h" 15 #include "DMCpuGMTask.h" 16 #include "DMGpuGMTask.h" 17 #include "DMGpuSupport.h" 18 #include "DMPDFTask.h" 19 #include "DMReporter.h" 20 #include "DMSKPTask.h" 21 #include "DMTask.h" 22 #include "DMTaskRunner.h" 23 #include "DMTestTask.h" 24 #include "DMWriteTask.h" 25 26 #ifdef SK_BUILD_POPPLER 27 # include "SkPDFRasterizer.h" 28 # define RASTERIZE_PDF_PROC SkPopplerRasterizePDF 29 #else 30 # define RASTERIZE_PDF_PROC NULL 31 #endif 32 33 #include <ctype.h> 34 35 using skiagm::GM; 36 using skiagm::GMRegistry; 37 using skiatest::Test; 38 using skiatest::TestRegistry; 39 40 DEFINE_int32(threads, -1, "Threads for CPU work. Default NUM_CPUS."); 41 DEFINE_int32(gpuThreads, 1, "Threads for GPU work."); 42 DEFINE_string2(expectations, r, "", 43 "If a directory, compare generated images against images under this path. " 44 "If a file, compare generated images against JSON expectations at this path." 45 ); 46 DEFINE_string2(resources, i, "resources", "Path to resources directory."); 47 DEFINE_string(match, "", "[~][^]substring[$] [...] of GM name to run.\n" 48 "Multiple matches may be separated by spaces.\n" 49 "~ causes a matching GM to always be skipped\n" 50 "^ requires the start of the GM to match\n" 51 "$ requires the end of the GM to match\n" 52 "^ and $ requires an exact match\n" 53 "If a GM does not match any list entry,\n" 54 "it is skipped unless some list entry starts with ~"); 55 DEFINE_string(config, "565 8888 pdf gpu nonrendering", 56 "Options: 565 8888 pdf gpu nonrendering msaa4 msaa16 nvprmsaa4 nvprmsaa16 " 57 "gpunull gpudebug angle mesa"); 58 DEFINE_bool(dryRun, false, 59 "Just print the tests that would be run, without actually running them."); 60 DEFINE_bool(leaks, false, "Print leaked instance-counted objects at exit?"); 61 DEFINE_string(skps, "", "Directory to read skps from."); 62 63 DEFINE_bool(gms, true, "Run GMs?"); 64 DEFINE_bool(benches, true, "Run benches? Does not run GMs-as-benches."); 65 DEFINE_bool(tests, true, "Run tests?"); 66 67 DECLARE_bool(verbose); 68 69 __SK_FORCE_IMAGE_DECODER_LINKING; 70 71 // "FooBar" -> "foobar". Obviously, ASCII only. 72 static SkString lowercase(SkString s) { 73 for (size_t i = 0; i < s.size(); i++) { 74 s[i] = tolower(s[i]); 75 } 76 return s; 77 } 78 79 static const GrContextFactory::GLContextType native = GrContextFactory::kNative_GLContextType; 80 static const GrContextFactory::GLContextType nvpr = GrContextFactory::kNVPR_GLContextType; 81 static const GrContextFactory::GLContextType null = GrContextFactory::kNull_GLContextType; 82 static const GrContextFactory::GLContextType debug = GrContextFactory::kDebug_GLContextType; 83 static const GrContextFactory::GLContextType angle = 84 #if SK_ANGLE 85 GrContextFactory::kANGLE_GLContextType; 86 #else 87 native; 88 #endif 89 static const GrContextFactory::GLContextType mesa = 90 #if SK_MESA 91 GrContextFactory::kMESA_GLContextType; 92 #else 93 native; 94 #endif 95 96 static void kick_off_gms(const SkTDArray<GMRegistry::Factory>& gms, 97 const SkTArray<SkString>& configs, 98 const DM::Expectations& expectations, 99 DM::Reporter* reporter, 100 DM::TaskRunner* tasks) { 101 #define START(name, type, ...) \ 102 if (lowercase(configs[j]).equals(name)) { \ 103 tasks->add(SkNEW_ARGS(DM::type, (name, reporter, tasks, gms[i], ## __VA_ARGS__))); \ 104 } 105 for (int i = 0; i < gms.count(); i++) { 106 for (int j = 0; j < configs.count(); j++) { 107 START("565", CpuGMTask, expectations, kRGB_565_SkColorType); 108 START("8888", CpuGMTask, expectations, kN32_SkColorType); 109 START("gpu", GpuGMTask, expectations, native, 0); 110 START("msaa4", GpuGMTask, expectations, native, 4); 111 START("msaa16", GpuGMTask, expectations, native, 16); 112 START("nvprmsaa4", GpuGMTask, expectations, nvpr, 4); 113 START("nvprmsaa16", GpuGMTask, expectations, nvpr, 16); 114 START("gpunull", GpuGMTask, expectations, null, 0); 115 START("gpudebug", GpuGMTask, expectations, debug, 0); 116 START("angle", GpuGMTask, expectations, angle, 0); 117 START("mesa", GpuGMTask, expectations, mesa, 0); 118 START("pdf", PDFTask, RASTERIZE_PDF_PROC); 119 } 120 } 121 #undef START 122 } 123 124 static void kick_off_benches(const SkTDArray<BenchRegistry::Factory>& benches, 125 const SkTArray<SkString>& configs, 126 DM::Reporter* reporter, 127 DM::TaskRunner* tasks) { 128 #define START(name, type, ...) \ 129 if (lowercase(configs[j]).equals(name)) { \ 130 tasks->add(SkNEW_ARGS(DM::type, (name, reporter, tasks, benches[i], ## __VA_ARGS__))); \ 131 } 132 for (int i = 0; i < benches.count(); i++) { 133 for (int j = 0; j < configs.count(); j++) { 134 START("nonrendering", NonRenderingBenchTask); 135 START("565", CpuBenchTask, kRGB_565_SkColorType); 136 START("8888", CpuBenchTask, kN32_SkColorType); 137 START("gpu", GpuBenchTask, native, 0); 138 START("msaa4", GpuBenchTask, native, 4); 139 START("msaa16", GpuBenchTask, native, 16); 140 START("nvprmsaa4", GpuBenchTask, nvpr, 4); 141 START("nvprmsaa16", GpuBenchTask, nvpr, 16); 142 START("gpunull", GpuBenchTask, null, 0); 143 START("gpudebug", GpuBenchTask, debug, 0); 144 START("angle", GpuBenchTask, angle, 0); 145 START("mesa", GpuBenchTask, mesa, 0); 146 } 147 } 148 #undef START 149 } 150 151 static void kick_off_tests(const SkTDArray<TestRegistry::Factory>& tests, 152 DM::Reporter* reporter, 153 DM::TaskRunner* tasks) { 154 for (int i = 0; i < tests.count(); i++) { 155 SkAutoTDelete<Test> test(tests[i](NULL)); 156 if (test->isGPUTest()) { 157 tasks->add(SkNEW_ARGS(DM::GpuTestTask, (reporter, tasks, tests[i]))); 158 } else { 159 tasks->add(SkNEW_ARGS(DM::CpuTestTask, (reporter, tasks, tests[i]))); 160 } 161 } 162 } 163 164 static void kick_off_skps(DM::Reporter* reporter, DM::TaskRunner* tasks) { 165 if (FLAGS_skps.isEmpty()) { 166 return; 167 } 168 169 SkOSFile::Iter it(FLAGS_skps[0], ".skp"); 170 SkString filename; 171 while (it.next(&filename)) { 172 if (SkCommandLineFlags::ShouldSkip(FLAGS_match, filename.c_str())) { 173 continue; 174 } 175 176 const SkString path = SkOSPath::SkPathJoin(FLAGS_skps[0], filename.c_str()); 177 178 SkAutoTDelete<SkStream> stream(SkStream::NewFromFile(path.c_str())); 179 if (stream.get() == NULL) { 180 SkDebugf("Could not read %s.\n", path.c_str()); 181 exit(1); 182 } 183 SkAutoTUnref<SkPicture> pic(SkPicture::CreateFromStream(stream.get())); 184 if (pic.get() == NULL) { 185 SkDebugf("Could not read %s as an SkPicture.\n", path.c_str()); 186 exit(1); 187 } 188 189 tasks->add(SkNEW_ARGS(DM::SKPTask, (reporter, tasks, pic->clone(), filename))); 190 tasks->add(SkNEW_ARGS(DM::PDFTask, (reporter, tasks, pic->clone(), filename, 191 RASTERIZE_PDF_PROC))); 192 } 193 } 194 195 static void report_failures(const SkTArray<SkString>& failures) { 196 if (failures.count() == 0) { 197 return; 198 } 199 200 SkDebugf("Failures:\n"); 201 for (int i = 0; i < failures.count(); i++) { 202 SkDebugf(" %s\n", failures[i].c_str()); 203 } 204 SkDebugf("%d failures.\n", failures.count()); 205 } 206 207 template <typename T, typename Registry> 208 static void append_matching_factories(Registry* head, SkTDArray<typename Registry::Factory>* out) { 209 for (const Registry* reg = head; reg != NULL; reg = reg->next()) { 210 SkAutoTDelete<T> forName(reg->factory()(NULL)); 211 if (!SkCommandLineFlags::ShouldSkip(FLAGS_match, forName->getName())) { 212 *out->append() = reg->factory(); 213 } 214 } 215 } 216 217 int tool_main(int argc, char** argv); 218 int tool_main(int argc, char** argv) { 219 SetupCrashHandler(); 220 SkAutoGraphics ag; 221 SkCommandLineFlags::Parse(argc, argv); 222 223 if (FLAGS_dryRun) { 224 FLAGS_verbose = true; 225 } 226 #if SK_ENABLE_INST_COUNT 227 gPrintInstCount = FLAGS_leaks; 228 #endif 229 230 SkTArray<SkString> configs; 231 for (int i = 0; i < FLAGS_config.count(); i++) { 232 SkStrSplit(FLAGS_config[i], ", ", &configs); 233 } 234 235 SkTDArray<GMRegistry::Factory> gms; 236 SkAutoTDelete<DM::Expectations> expectations(SkNEW(DM::NoExpectations)); 237 if (FLAGS_gms) { 238 append_matching_factories<GM>(GMRegistry::Head(), &gms); 239 240 if (FLAGS_expectations.count() > 0) { 241 const char* path = FLAGS_expectations[0]; 242 if (sk_isdir(path)) { 243 expectations.reset(SkNEW_ARGS(DM::WriteTask::Expectations, (path))); 244 } else { 245 expectations.reset(SkNEW_ARGS(DM::JsonExpectations, (path))); 246 } 247 } 248 } 249 250 SkTDArray<BenchRegistry::Factory> benches; 251 if (FLAGS_benches) { 252 append_matching_factories<Benchmark>(BenchRegistry::Head(), &benches); 253 } 254 255 SkTDArray<TestRegistry::Factory> tests; 256 if (FLAGS_tests) { 257 append_matching_factories<Test>(TestRegistry::Head(), &tests); 258 } 259 260 SkDebugf("(%d GMs, %d benches) x %d configs, %d tests\n", 261 gms.count(), benches.count(), configs.count(), tests.count()); 262 DM::Reporter reporter; 263 DM::TaskRunner tasks(FLAGS_threads, FLAGS_gpuThreads); 264 kick_off_gms(gms, configs, *expectations, &reporter, &tasks); 265 kick_off_benches(benches, configs, &reporter, &tasks); 266 kick_off_tests(tests, &reporter, &tasks); 267 kick_off_skps(&reporter, &tasks); 268 tasks.wait(); 269 270 SkDebugf("\n"); 271 272 SkTArray<SkString> failures; 273 reporter.getFailures(&failures); 274 report_failures(failures); 275 return failures.count() > 0; 276 } 277 278 #if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL) 279 int main(int argc, char** argv) { 280 return tool_main(argc, argv); 281 } 282 #endif 283