• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
lowercase(SkString s)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 
kick_off_gms(const SkTDArray<GMRegistry::Factory> & gms,const SkTArray<SkString> & configs,const DM::Expectations & expectations,DM::Reporter * reporter,DM::TaskRunner * tasks)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 
kick_off_benches(const SkTDArray<BenchRegistry::Factory> & benches,const SkTArray<SkString> & configs,DM::Reporter * reporter,DM::TaskRunner * tasks)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 
kick_off_tests(const SkTDArray<TestRegistry::Factory> & tests,DM::Reporter * reporter,DM::TaskRunner * tasks)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 
kick_off_skps(DM::Reporter * reporter,DM::TaskRunner * tasks)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 
report_failures(const SkTArray<SkString> & failures)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>
append_matching_factories(Registry * head,SkTDArray<typename Registry::Factory> * out)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);
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)
main(int argc,char ** argv)279 int main(int argc, char** argv) {
280     return tool_main(argc, argv);
281 }
282 #endif
283