OLD | NEW |
| 1 // Main binary for DM. |
| 2 // For a high-level overview, please see dm/README. |
| 3 |
1 #include "CrashHandler.h" | 4 #include "CrashHandler.h" |
2 #include "DMJsonWriter.h" | 5 #include "LazyDecodeBitmap.h" |
3 #include "DMSrcSink.h" | |
4 #include "OverwriteLine.h" | |
5 #include "ProcStats.h" | |
6 #include "SkBBHFactory.h" | |
7 #include "SkCommonFlags.h" | 6 #include "SkCommonFlags.h" |
8 #include "SkForceLinking.h" | 7 #include "SkForceLinking.h" |
9 #include "SkGraphics.h" | 8 #include "SkGraphics.h" |
10 #include "SkMD5.h" | |
11 #include "SkOSFile.h" | 9 #include "SkOSFile.h" |
| 10 #include "SkPicture.h" |
| 11 #include "SkString.h" |
12 #include "SkTaskGroup.h" | 12 #include "SkTaskGroup.h" |
13 #include "Test.h" | 13 #include "Test.h" |
14 #include "Timer.h" | 14 #include "gm.h" |
15 | 15 #include "sk_tool_utils.h" |
| 16 #include "sk_tool_utils_flags.h" |
| 17 |
| 18 #include "DMCpuGMTask.h" |
| 19 #include "DMGpuGMTask.h" |
| 20 #include "DMGpuSupport.h" |
| 21 #include "DMImageTask.h" |
| 22 #include "DMJsonWriter.h" |
| 23 #include "DMPDFTask.h" |
| 24 #include "DMPDFRasterizeTask.h" |
| 25 #include "DMReporter.h" |
| 26 #include "DMSKPTask.h" |
| 27 #include "DMTask.h" |
| 28 #include "DMTaskRunner.h" |
| 29 #include "DMTestTask.h" |
| 30 |
| 31 #ifdef SK_BUILD_POPPLER |
| 32 # include "SkPDFRasterizer.h" |
| 33 # define RASTERIZE_PDF_PROC SkPopplerRasterizePDF |
| 34 #elif defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) |
| 35 # include "SkCGUtils.h" |
| 36 # define RASTERIZE_PDF_PROC SkPDFDocumentToBitmap |
| 37 #else |
| 38 # define RASTERIZE_PDF_PROC NULL |
| 39 #endif |
| 40 |
| 41 #include <ctype.h> |
| 42 |
| 43 using skiagm::GM; |
| 44 using skiagm::GMRegistry; |
| 45 using skiatest::Test; |
| 46 using skiatest::TestRegistry; |
| 47 |
| 48 static const char kGpuAPINameGL[] = "gl"; |
| 49 static const char kGpuAPINameGLES[] = "gles"; |
| 50 |
| 51 DEFINE_bool(gms, true, "Run GMs?"); |
16 DEFINE_bool(tests, true, "Run tests?"); | 52 DEFINE_bool(tests, true, "Run tests?"); |
17 DEFINE_string(images, "resources", "Images to decode."); | 53 DEFINE_bool(reportUsedChars, false, "Output test font construction data to be pa
sted into" |
18 DEFINE_string(src, "gm skp image subset", "Source types to test."); | 54 " create_test_font.cpp."); |
19 DEFINE_bool(nameByHash, false, | 55 DEFINE_string(images, "resources", "Path to directory containing images to decod
e."); |
20 "If true, write to FLAGS_writePath[0]/<hash>.png instead of " | 56 DEFINE_bool(rasterPDF, true, "Rasterize PDFs?"); |
21 "to FLAGS_writePath[0]/<config>/<sourceType>/<name>.png"); | |
22 DEFINE_bool2(pathOpsExtended, x, false, "Run extended pathOps tests."); | |
23 DEFINE_string(matrix, "1 0 0 0 1 0 0 0 1", | |
24 "Matrix to apply when using 'matrix' in config."); | |
25 | 57 |
26 __SK_FORCE_IMAGE_DECODER_LINKING; | 58 __SK_FORCE_IMAGE_DECODER_LINKING; |
27 using namespace DM; | 59 |
28 | 60 static DM::RasterizePdfProc get_pdf_rasterizer_proc() { |
29 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~*/ | 61 return reinterpret_cast<DM::RasterizePdfProc>( |
30 | 62 FLAGS_rasterPDF ? RASTERIZE_PDF_PROC : NULL); |
31 static int gPending = 0, gFailures = 0; | 63 } |
32 | 64 |
33 static void fail(ImplicitString err) { | 65 // "FooBar" -> "foobar". Obviously, ASCII only. |
34 SkDebugf("\n\nERROR: %s\n\n", err.c_str()); | 66 static SkString lowercase(SkString s) { |
35 sk_atomic_inc(&gFailures); | 67 for (size_t i = 0; i < s.size(); i++) { |
36 } | 68 s[i] = tolower(s[i]); |
37 | 69 } |
38 static void done(double ms, ImplicitString config, ImplicitString src, ImplicitS
tring name) { | 70 return s; |
39 SkDebugf("%s(%4dMB %5d) %s\t%s %s %s ", FLAGS_verbose ? "\n" : kSkOverwrite
Line | 71 } |
40 , sk_tools::getMaxResidentSetSizeMB() | 72 |
41 , sk_atomic_dec(&gPending)-1 | 73 static const GrContextFactory::GLContextType native = GrContextFactory::kNative_
GLContextType; |
42 , HumanizeMs(ms).c_str() | 74 static const GrContextFactory::GLContextType nvpr = GrContextFactory::kNVPR_GL
ContextType; |
43 , config.c_str() | 75 static const GrContextFactory::GLContextType null = GrContextFactory::kNull_GL
ContextType; |
44 , src.c_str() | 76 static const GrContextFactory::GLContextType debug = GrContextFactory::kDebug_G
LContextType; |
45 , name.c_str()); | 77 #if SK_ANGLE |
46 } | 78 static const GrContextFactory::GLContextType angle = GrContextFactory::kANGLE_G
LContextType; |
47 | 79 #endif |
48 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~*/ | 80 #if SK_MESA |
49 | 81 static const GrContextFactory::GLContextType mesa = GrContextFactory::kMESA_GL
ContextType; |
50 template <typename T> | 82 #endif |
51 struct Tagged : public SkAutoTDelete<T> { const char* tag; }; | 83 |
52 | 84 static void kick_off_gms(const SkTDArray<GMRegistry::Factory>& gms, |
53 static const bool kMemcpyOK = true; | 85 const SkTArray<SkString>& configs, |
54 | 86 GrGLStandard gpuAPI, |
55 static SkTArray<Tagged<Src>, kMemcpyOK> gSrcs; | 87 DM::Reporter* reporter, |
56 static SkTArray<Tagged<Sink>, kMemcpyOK> gSinks; | 88 DM::TaskRunner* tasks) { |
57 | 89 #define START(name, type, ...)
\ |
58 static void push_src(const char* tag, Src* s) { | 90 if (lowercase(configs[j]).equals(name)) {
\ |
59 SkAutoTDelete<Src> src(s); | 91 tasks->add(SkNEW_ARGS(DM::type, (name, reporter, tasks, gms[i], ## __VA_
ARGS__))); \ |
60 if (FLAGS_src.contains(tag) && | 92 } |
61 !SkCommandLineFlags::ShouldSkip(FLAGS_match, src->name().c_str())) { | 93 for (int i = 0; i < gms.count(); i++) { |
62 Tagged<Src>& s = gSrcs.push_back(); | 94 for (int j = 0; j < configs.count(); j++) { |
63 s.reset(src.detach()); | 95 |
64 s.tag = tag; | 96 START("565", CpuGMTask, kRGB_565_SkColorType); |
65 } | 97 START("8888", CpuGMTask, kN32_SkColorType); |
66 } | 98 START("gpu", GpuGMTask, native, gpuAPI, 0, false); |
67 | 99 START("msaa4", GpuGMTask, native, gpuAPI, 4, false); |
68 static void gather_srcs() { | 100 START("msaa16", GpuGMTask, native, gpuAPI, 16, false); |
69 for (const skiagm::GMRegistry* r = skiagm::GMRegistry::Head(); r; r = r->nex
t()) { | 101 START("nvprmsaa4", GpuGMTask, nvpr, gpuAPI, 4, false); |
70 push_src("gm", new GMSrc(r->factory())); | 102 START("nvprmsaa16", GpuGMTask, nvpr, gpuAPI, 16, false); |
71 } | 103 START("gpudft", GpuGMTask, native, gpuAPI, 0, true); |
72 if (!FLAGS_skps.isEmpty()) { | 104 START("gpunull", GpuGMTask, null, gpuAPI, 0, false); |
73 SkOSFile::Iter it(FLAGS_skps[0], "skp"); | 105 START("gpudebug", GpuGMTask, debug, gpuAPI, 0, false); |
74 for (SkString file; it.next(&file); ) { | 106 #if SK_ANGLE |
75 push_src("skp", new SKPSrc(SkOSPath::Join(FLAGS_skps[0], file.c_str(
)))); | 107 START("angle", GpuGMTask, angle, gpuAPI, 0, false); |
76 } | 108 #endif |
77 } | 109 #if SK_MESA |
78 if (!FLAGS_images.isEmpty()) { | 110 START("mesa", GpuGMTask, mesa, gpuAPI, 0, false); |
79 const char* exts[] = { | 111 #endif |
80 "bmp", "gif", "jpg", "jpeg", "png", "webp", "ktx", "astc", "wbmp", "
ico", | 112 START("pdf", PDFTask, get_pdf_rasterizer_proc()); |
81 "BMP", "GIF", "JPG", "JPEG", "PNG", "WEBP", "KTX", "ASTC", "WBMP", "
ICO", | 113 } |
82 }; | 114 } |
83 for (size_t i = 0; i < SK_ARRAY_COUNT(exts); i++) { | 115 #undef START |
84 SkOSFile::Iter it(FLAGS_images[0], exts[i]); | 116 } |
85 for (SkString file; it.next(&file); ) { | 117 |
86 SkString path = SkOSPath::Join(FLAGS_images[0], file.c_str()); | 118 static void kick_off_tests(const SkTDArray<TestRegistry::Factory>& tests, |
87 push_src("image", new ImageSrc(path)); // Decode entire ima
ge. | 119 DM::Reporter* reporter, |
88 push_src("subset", new ImageSrc(path, 5)); // Decode 5 random s
ubsets. | 120 DM::TaskRunner* tasks) { |
| 121 for (int i = 0; i < tests.count(); i++) { |
| 122 SkAutoTDelete<Test> test(tests[i](NULL)); |
| 123 if (test->isGPUTest()) { |
| 124 tasks->add(SkNEW_ARGS(DM::GpuTestTask, (reporter, tasks, tests[i])))
; |
| 125 } else { |
| 126 tasks->add(SkNEW_ARGS(DM::CpuTestTask, (reporter, tasks, tests[i])))
; |
| 127 } |
| 128 } |
| 129 } |
| 130 |
| 131 static void find_files(const char* dir, |
| 132 const char* suffixes[], |
| 133 size_t num_suffixes, |
| 134 SkTArray<SkString>* files) { |
| 135 if (0 == strcmp(dir, "")) { |
| 136 return; |
| 137 } |
| 138 |
| 139 SkString filename; |
| 140 for (size_t i = 0; i < num_suffixes; i++) { |
| 141 SkOSFile::Iter it(dir, suffixes[i]); |
| 142 while (it.next(&filename)) { |
| 143 if (!SkCommandLineFlags::ShouldSkip(FLAGS_match, filename.c_str()))
{ |
| 144 files->push_back(SkOSPath::Join(dir, filename.c_str())); |
89 } | 145 } |
90 } | 146 } |
91 } | 147 } |
92 } | 148 } |
93 | 149 |
94 static GrGLStandard get_gpu_api() { | 150 static void kick_off_skps(const SkTArray<SkString>& skps, |
95 if (FLAGS_gpuAPI.contains("gl")) { return kGL_GrGLStandard; } | 151 DM::Reporter* reporter, |
96 if (FLAGS_gpuAPI.contains("gles")) { return kGLES_GrGLStandard; } | 152 DM::TaskRunner* tasks) { |
97 return kNone_GrGLStandard; | 153 for (int i = 0; i < skps.count(); ++i) { |
98 } | 154 SkAutoTUnref<SkStream> stream(SkStream::NewFromFile(skps[i].c_str())); |
99 | 155 if (stream.get() == NULL) { |
100 static void push_sink(const char* tag, Sink* s) { | 156 SkDebugf("Could not read %s.\n", skps[i].c_str()); |
101 SkAutoTDelete<Sink> sink(s); | 157 exit(1); |
102 if (!FLAGS_config.contains(tag)) { | 158 } |
| 159 SkAutoTUnref<SkPicture> pic( |
| 160 SkPicture::CreateFromStream(stream.get(), &sk_tools::LazyDecodeB
itmap)); |
| 161 if (pic.get() == NULL) { |
| 162 SkDebugf("Could not read %s as an SkPicture.\n", skps[i].c_str()); |
| 163 exit(1); |
| 164 } |
| 165 |
| 166 SkString filename = SkOSPath::Basename(skps[i].c_str()); |
| 167 tasks->add(SkNEW_ARGS(DM::SKPTask, (reporter, tasks, pic, filename))); |
| 168 tasks->add(SkNEW_ARGS(DM::PDFTask, (reporter, tasks, pic, filename, |
| 169 get_pdf_rasterizer_proc()))); |
| 170 } |
| 171 } |
| 172 |
| 173 static void kick_off_images(const SkTArray<SkString>& images, |
| 174 DM::Reporter* reporter, |
| 175 DM::TaskRunner* tasks) { |
| 176 for (int i = 0; i < images.count(); i++) { |
| 177 SkAutoTUnref<SkData> image(SkData::NewFromFileName(images[i].c_str())); |
| 178 if (!image) { |
| 179 SkDebugf("Could not read %s.\n", images[i].c_str()); |
| 180 exit(1); |
| 181 } |
| 182 SkString filename = SkOSPath::Basename(images[i].c_str()); |
| 183 tasks->add(SkNEW_ARGS(DM::ImageTask, (reporter, tasks, image, filename))
); |
| 184 tasks->add(SkNEW_ARGS(DM::ImageTask, (reporter, tasks, image, filename,
5/*subsets*/))); |
| 185 } |
| 186 } |
| 187 |
| 188 |
| 189 static void report_failures(const SkTArray<SkString>& failures) { |
| 190 if (failures.count() == 0) { |
103 return; | 191 return; |
104 } | 192 } |
105 // Try a noop Src as a canary. If it fails, skip this sink. | 193 |
106 struct : public Src { | 194 SkDebugf("Failures:\n"); |
107 Error draw(SkCanvas*) const SK_OVERRIDE { return ""; } | 195 for (int i = 0; i < failures.count(); i++) { |
108 SkISize size() const SK_OVERRIDE { return SkISize::Make(16, 16); } | 196 SkDebugf(" %s\n", failures[i].c_str()); |
109 Name name() const SK_OVERRIDE { return "noop"; } | 197 } |
110 } noop; | 198 SkDebugf("%d failures.\n", failures.count()); |
111 | 199 } |
112 SkBitmap bitmap; | 200 |
113 SkDynamicMemoryWStream stream; | 201 static GrGLStandard get_gl_standard() { |
114 Error err = sink->draw(noop, &bitmap, &stream); | 202 if (FLAGS_gpuAPI.contains(kGpuAPINameGL)) { |
115 if (!err.isEmpty()) { | 203 return kGL_GrGLStandard; |
116 SkDebugf("Skipping %s: %s\n", tag, err.c_str()); | 204 } |
117 return; | 205 if (FLAGS_gpuAPI.contains(kGpuAPINameGLES)) { |
118 } | 206 return kGLES_GrGLStandard; |
119 | 207 } |
120 Tagged<Sink>& ts = gSinks.push_back(); | 208 return kNone_GrGLStandard; |
121 ts.reset(sink.detach()); | 209 } |
122 ts.tag = tag; | 210 |
123 } | 211 template <typename T, typename Registry> |
124 | 212 static void append_matching_factories(Registry* head, SkTDArray<typename Registr
y::Factory>* out) { |
125 static bool gpu_supported() { | 213 for (const Registry* reg = head; reg != NULL; reg = reg->next()) { |
126 #if SK_SUPPORT_GPU | 214 SkAutoTDelete<T> forName(reg->factory()(NULL)); |
127 return FLAGS_gpu; | 215 if (!SkCommandLineFlags::ShouldSkip(FLAGS_match, forName->getName())) { |
128 #else | 216 *out->append() = reg->factory(); |
129 return false; | 217 } |
130 #endif | 218 } |
131 } | 219 } |
132 | |
133 static 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 | |
151 } | |
152 | |
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; | |
161 } | |
162 | |
163 static 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; | |
185 } | |
186 | |
187 static 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, s
ink); | |
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); | |
207 } | |
208 } | |
209 } | |
210 | |
211 // The finest-grained unit of work we can run: draw a single Src into a single S
ink, | |
212 // report any errors, and perhaps write out the output: a .png of the bitmap, or
a raw stream. | |
213 struct 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::kP
NG_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. | |
322 static 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 | |
332 static 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 | |
343 static SkTArray<SkAutoTDelete<skiatest::Test>, kMemcpyOK> gTests; | |
344 | |
345 static 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 | |
365 static 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 | 220 |
383 int dm_main(); | 221 int dm_main(); |
384 int dm_main() { | 222 int dm_main() { |
385 SetupCrashHandler(); | 223 SetupCrashHandler(); |
386 SkAutoGraphics ag; | 224 SkAutoGraphics ag; |
387 SkTaskGroup::Enabler enabled(FLAGS_threads); | 225 SkTaskGroup::Enabler enabled(FLAGS_threads); |
388 | 226 |
389 gather_srcs(); | 227 if (FLAGS_dryRun || FLAGS_veryVerbose) { |
390 gather_sinks(); | 228 FLAGS_verbose = true; |
391 gather_tests(); | 229 } |
| 230 #if SK_ENABLE_INST_COUNT |
| 231 gPrintInstCount = FLAGS_leaks; |
| 232 #endif |
392 | 233 |
393 gPending = gSrcs.count() * gSinks.count() + gTests.count(); | 234 SkTArray<SkString> configs; |
394 SkDebugf("%d srcs * %d sinks + %d tests == %d tasks\n", | 235 for (int i = 0; i < FLAGS_config.count(); i++) { |
395 gSrcs.count(), gSinks.count(), gTests.count(), gPending); | 236 SkStrSplit(FLAGS_config[i], ", ", &configs); |
396 | |
397 // We try to exploit as much parallelism as is safe. Most Src/Sink pairs ru
n 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 te
st. | |
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 } | |
406 } | 237 } |
407 | 238 |
408 SK_COMPILE_ASSERT(kAnyThread_Enclave == 0, AnyThreadZero); | 239 GrGLStandard gpuAPI = get_gl_standard(); |
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(); | |
414 | 240 |
415 if (!FLAGS_verbose) { | 241 SkTDArray<GMRegistry::Factory> gms; |
416 SkDebugf("\n"); | 242 if (FLAGS_gms) { |
| 243 append_matching_factories<GM>(GMRegistry::Head(), &gms); |
417 } | 244 } |
418 | 245 |
419 JsonWriter::DumpJson(); | 246 SkTDArray<TestRegistry::Factory> tests; |
420 return gPending == 0 && gFailures == 0 ? 0 : 1; | 247 if (FLAGS_tests) { |
| 248 append_matching_factories<Test>(TestRegistry::Head(), &tests); |
| 249 } |
| 250 |
| 251 |
| 252 SkTArray<SkString> skps; |
| 253 if (!FLAGS_skps.isEmpty()) { |
| 254 const char* suffixes[] = { "skp" }; |
| 255 find_files(FLAGS_skps[0], suffixes, SK_ARRAY_COUNT(suffixes), &skps); |
| 256 } |
| 257 |
| 258 SkTArray<SkString> images; |
| 259 if (!FLAGS_images.isEmpty()) { |
| 260 const char* suffixes[] = { |
| 261 "bmp", "gif", "jpg", "jpeg", "png", "webp", "ktx", "astc", "wbmp", "
ico", |
| 262 "BMP", "GIF", "JPG", "JPEG", "PNG", "WEBP", "KTX", "ASTC", "WBMP", "
ICO", |
| 263 }; |
| 264 find_files(FLAGS_images[0], suffixes, SK_ARRAY_COUNT(suffixes), &images)
; |
| 265 } |
| 266 |
| 267 SkDebugf("%d GMs x %d configs, %d tests, %d pictures, %d images\n", |
| 268 gms.count(), configs.count(), tests.count(), skps.count(), images.c
ount()); |
| 269 DM::Reporter reporter; |
| 270 |
| 271 DM::TaskRunner tasks; |
| 272 kick_off_tests(tests, &reporter, &tasks); |
| 273 kick_off_gms(gms, configs, gpuAPI, &reporter, &tasks); |
| 274 kick_off_skps(skps, &reporter, &tasks); |
| 275 kick_off_images(images, &reporter, &tasks); |
| 276 tasks.wait(); |
| 277 |
| 278 DM::JsonWriter::DumpJson(); |
| 279 |
| 280 SkDebugf("\n"); |
| 281 #ifdef SK_DEBUG |
| 282 if (FLAGS_portableFonts && FLAGS_reportUsedChars) { |
| 283 sk_tool_utils::report_used_chars(); |
| 284 } |
| 285 #endif |
| 286 |
| 287 SkTArray<SkString> failures; |
| 288 reporter.getFailures(&failures); |
| 289 report_failures(failures); |
| 290 return failures.count() > 0; |
421 } | 291 } |
422 | 292 |
423 #if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL) | 293 #if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL) |
424 int main(int argc, char** argv) { | 294 int main(int argc, char** argv) { |
425 SkCommandLineFlags::Parse(argc, argv); | 295 SkCommandLineFlags::Parse(argc, argv); |
426 return dm_main(); | 296 return dm_main(); |
427 } | 297 } |
428 #endif | 298 #endif |
OLD | NEW |