OLD | NEW |
1 // Main binary for DM. | |
2 // For a high-level overview, please see dm/README. | |
3 | |
4 #include "CrashHandler.h" | 1 #include "CrashHandler.h" |
5 #include "LazyDecodeBitmap.h" | 2 #include "DMJsonWriter.h" |
| 3 #include "DMSrcSink.h" |
| 4 #include "OverwriteLine.h" |
| 5 #include "ProcStats.h" |
| 6 #include "SkBBHFactory.h" |
6 #include "SkCommonFlags.h" | 7 #include "SkCommonFlags.h" |
7 #include "SkForceLinking.h" | 8 #include "SkForceLinking.h" |
8 #include "SkGraphics.h" | 9 #include "SkGraphics.h" |
| 10 #include "SkMD5.h" |
9 #include "SkOSFile.h" | 11 #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 "gm.h" | 14 #include "Timer.h" |
15 #include "sk_tool_utils.h" | 15 |
16 #include "sk_tool_utils_flags.h" | 16 DEFINE_bool(tests, true, "Run tests?"); |
17 | 17 DEFINE_string(images, "resources", "Images to decode."); |
18 #include "DMCpuGMTask.h" | 18 //DEFINE_string(src, "gm skp image subset", "Source types to test."); |
19 #include "DMGpuGMTask.h" | 19 DEFINE_string(src, "gm skp", "Source types to test. TEMPORARILY DISABLED"); |
20 #include "DMGpuSupport.h" | 20 DEFINE_bool(nameByHash, false, |
21 #include "DMImageTask.h" | 21 "If true, write to FLAGS_writePath[0]/<hash>.png instead of " |
22 #include "DMJsonWriter.h" | 22 "to FLAGS_writePath[0]/<config>/<sourceType>/<name>.png"); |
23 #include "DMPDFTask.h" | 23 DEFINE_bool2(pathOpsExtended, x, false, "Run extended pathOps tests."); |
24 #include "DMPDFRasterizeTask.h" | 24 DEFINE_string(matrix, "1 0 0 0 1 0 0 0 1", |
25 #include "DMReporter.h" | 25 "Matrix to apply when using 'matrix' in config."); |
26 #include "DMSKPTask.h" | 26 |
27 #include "DMTask.h" | 27 __SK_FORCE_IMAGE_DECODER_LINKING; |
28 #include "DMTaskRunner.h" | 28 using namespace DM; |
29 #include "DMTestTask.h" | 29 |
30 | 30 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~*/ |
31 #ifdef SK_BUILD_POPPLER | 31 |
32 # include "SkPDFRasterizer.h" | 32 SK_DECLARE_STATIC_MUTEX(gFailuresMutex); |
33 # define RASTERIZE_PDF_PROC SkPopplerRasterizePDF | 33 static SkTArray<SkString> gFailures; |
34 #elif defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) | 34 |
35 # include "SkCGUtils.h" | 35 static void fail(ImplicitString err) { |
36 # define RASTERIZE_PDF_PROC SkPDFDocumentToBitmap | 36 SkAutoMutexAcquire lock(gFailuresMutex); |
| 37 SkDebugf("\n\nFAILURE: %s\n\n", err.c_str()); |
| 38 gFailures.push_back(err); |
| 39 } |
| 40 |
| 41 static int32_t gPending = 0; // Atomic. |
| 42 |
| 43 static void done(double ms, ImplicitString config, ImplicitString src, ImplicitS
tring name) { |
| 44 SkDebugf("%s(%4dMB %5d) %s\t%s %s %s ", FLAGS_verbose ? "\n" : kSkOverwrite
Line |
| 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()); |
| 51 } |
| 52 |
| 53 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~*/ |
| 54 |
| 55 template <typename T> |
| 56 struct Tagged : public SkAutoTDelete<T> { const char* tag; }; |
| 57 |
| 58 static const bool kMemcpyOK = true; |
| 59 |
| 60 static SkTArray<Tagged<Src>, kMemcpyOK> gSrcs; |
| 61 static SkTArray<Tagged<Sink>, kMemcpyOK> gSinks; |
| 62 |
| 63 static 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; |
| 70 } |
| 71 } |
| 72 |
| 73 static void gather_srcs() { |
| 74 for (const skiagm::GMRegistry* r = skiagm::GMRegistry::Head(); r; r = r->nex
t()) { |
| 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(
)))); |
| 81 } |
| 82 } |
| 83 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 ima
ge. |
| 93 push_src("subset", new ImageSrc(path, 5)); // Decode 5 random s
ubsets. |
| 94 } |
| 95 } |
| 96 } |
| 97 } |
| 98 |
| 99 static 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; |
| 103 } |
| 104 |
| 105 static void push_sink(const char* tag, Sink* s) { |
| 106 SkAutoTDelete<Sink> sink(s); |
| 107 if (!FLAGS_config.contains(tag)) { |
| 108 return; |
| 109 } |
| 110 // 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; |
| 116 |
| 117 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()); |
| 122 return; |
| 123 } |
| 124 |
| 125 Tagged<Sink>& ts = gSinks.push_back(); |
| 126 ts.reset(sink.detach()); |
| 127 ts.tag = tag; |
| 128 } |
| 129 |
| 130 static bool gpu_supported() { |
| 131 #if SK_SUPPORT_GPU |
| 132 return FLAGS_gpu; |
37 #else | 133 #else |
38 # define RASTERIZE_PDF_PROC NULL | 134 return false; |
39 #endif | 135 #endif |
40 | 136 } |
41 #include <ctype.h> | 137 |
42 | 138 static Sink* create_sink(const char* tag) { |
43 using skiagm::GM; | 139 #define SINK(t, sink, ...) if (0 == strcmp(t, tag)) { return new sink(__VA_ARGS_
_); } |
44 using skiagm::GMRegistry; | 140 if (gpu_supported()) { |
45 using skiatest::Test; | 141 const GrGLStandard api = get_gpu_api(); |
46 using skiatest::TestRegistry; | 142 SINK("gpunull", GPUSink, GrContextFactory::kNull_GLContextType, api
, 0, false); |
47 | 143 SINK("gpudebug", GPUSink, GrContextFactory::kDebug_GLContextType, api
, 0, false); |
48 static const char kGpuAPINameGL[] = "gl"; | 144 SINK("gpu", GPUSink, GrContextFactory::kNative_GLContextType, api
, 0, false); |
49 static const char kGpuAPINameGLES[] = "gles"; | 145 SINK("gpudft", GPUSink, GrContextFactory::kNative_GLContextType, api
, 0, true); |
50 | 146 SINK("msaa4", GPUSink, GrContextFactory::kNative_GLContextType, api
, 4, false); |
51 DEFINE_bool(gms, true, "Run GMs?"); | 147 SINK("msaa16", GPUSink, GrContextFactory::kNative_GLContextType, api
, 16, false); |
52 DEFINE_bool(tests, true, "Run tests?"); | 148 SINK("nvprmsaa4", GPUSink, GrContextFactory::kNVPR_GLContextType, api
, 4, false); |
53 DEFINE_bool(reportUsedChars, false, "Output test font construction data to be pa
sted into" | 149 SINK("nvprmsaa16", GPUSink, GrContextFactory::kNVPR_GLContextType, api
, 16, false); |
54 " create_test_font.cpp."); | 150 #if SK_ANGLE |
55 DEFINE_string(images, "resources", "Path to directory containing images to decod
e."); | 151 SINK("angle", GPUSink, GrContextFactory::kANGLE_GLContextType, api
, 0, false); |
56 DEFINE_bool(rasterPDF, true, "Rasterize PDFs?"); | 152 #endif |
57 | 153 #if SK_MESA |
58 __SK_FORCE_IMAGE_DECODER_LINKING; | 154 SINK("mesa", GPUSink, GrContextFactory::kMESA_GLContextType, api
, 0, false); |
59 | 155 #endif |
60 static DM::RasterizePdfProc get_pdf_rasterizer_proc() { | 156 } |
61 return reinterpret_cast<DM::RasterizePdfProc>( | 157 |
62 FLAGS_rasterPDF ? RASTERIZE_PDF_PROC : NULL); | 158 if (FLAGS_cpu) { |
63 } | 159 SINK("565", RasterSink, kRGB_565_SkColorType); |
64 | 160 SINK("8888", RasterSink, kN32_SkColorType); |
65 // "FooBar" -> "foobar". Obviously, ASCII only. | 161 // TODO(mtklein): reenable once skiagold can handle .pdf uploads. |
66 static SkString lowercase(SkString s) { | 162 //SINK("pdf", PDFSink); |
67 for (size_t i = 0; i < s.size(); i++) { | 163 } |
68 s[i] = tolower(s[i]); | 164 #undef SINK |
69 } | 165 return NULL; |
70 return s; | 166 } |
71 } | 167 |
72 | 168 static Sink* create_via(const char* tag, Sink* wrapped) { |
73 static const GrContextFactory::GLContextType native = GrContextFactory::kNative_
GLContextType; | 169 #define VIA(t, via, ...) if (0 == strcmp(t, tag)) { return new via(__VA_ARGS__);
} |
74 static const GrContextFactory::GLContextType nvpr = GrContextFactory::kNVPR_GL
ContextType; | 170 VIA("serialize", ViaSerialization, wrapped); |
75 static const GrContextFactory::GLContextType null = GrContextFactory::kNull_GL
ContextType; | 171 |
76 static const GrContextFactory::GLContextType debug = GrContextFactory::kDebug_G
LContextType; | 172 VIA("tiles", ViaTiles, 256, 256, NULL, wrapped); |
77 #if SK_ANGLE | 173 VIA("tiles_rt", ViaTiles, 256, 256, new SkRTreeFactory, wrapped); |
78 static const GrContextFactory::GLContextType angle = GrContextFactory::kANGLE_G
LContextType; | 174 |
79 #endif | 175 const int xp = SkGPipeWriter::kCrossProcess_Flag, |
80 #if SK_MESA | 176 sa = xp | SkGPipeWriter::kSharedAddressSpace_Flag; |
81 static const GrContextFactory::GLContextType mesa = GrContextFactory::kMESA_GL
ContextType; | 177 VIA("pipe", ViaPipe, 0, wrapped); |
82 #endif | 178 VIA("pipe_xp", ViaPipe, xp, wrapped); |
83 | 179 VIA("pipe_sa", ViaPipe, sa, wrapped); |
84 static void kick_off_gms(const SkTDArray<GMRegistry::Factory>& gms, | 180 |
85 const SkTArray<SkString>& configs, | 181 if (FLAGS_matrix.count() == 9) { |
86 GrGLStandard gpuAPI, | 182 SkMatrix m; |
87 DM::Reporter* reporter, | 183 for (int i = 0; i < 9; i++) { |
88 DM::TaskRunner* tasks) { | 184 m[i] = (SkScalar)atof(FLAGS_matrix[i]); |
89 #define START(name, type, ...)
\ | 185 } |
90 if (lowercase(configs[j]).equals(name)) {
\ | 186 VIA("matrix", ViaMatrix, m, wrapped); |
91 tasks->add(SkNEW_ARGS(DM::type, (name, reporter, tasks, gms[i], ## __VA_
ARGS__))); \ | 187 } |
92 } | 188 #undef VIA |
93 for (int i = 0; i < gms.count(); i++) { | 189 return NULL; |
94 for (int j = 0; j < configs.count(); j++) { | 190 } |
95 | 191 |
96 START("565", CpuGMTask, kRGB_565_SkColorType); | 192 static void gather_sinks() { |
97 START("8888", CpuGMTask, kN32_SkColorType); | 193 for (int i = 0; i < FLAGS_config.count(); i++) { |
98 START("gpu", GpuGMTask, native, gpuAPI, 0, false); | 194 const char* config = FLAGS_config[i]; |
99 START("msaa4", GpuGMTask, native, gpuAPI, 4, false); | 195 SkTArray<SkString> parts; |
100 START("msaa16", GpuGMTask, native, gpuAPI, 16, false); | 196 SkStrSplit(config, "-", &parts); |
101 START("nvprmsaa4", GpuGMTask, nvpr, gpuAPI, 4, false); | 197 |
102 START("nvprmsaa16", GpuGMTask, nvpr, gpuAPI, 16, false); | 198 Sink* sink = NULL; |
103 START("gpudft", GpuGMTask, native, gpuAPI, 0, true); | 199 for (int i = parts.count(); i-- > 0;) { |
104 START("gpunull", GpuGMTask, null, gpuAPI, 0, false); | 200 const char* part = parts[i].c_str(); |
105 START("gpudebug", GpuGMTask, debug, gpuAPI, 0, false); | 201 Sink* next = (sink == NULL) ? create_sink(part) : create_via(part, s
ink); |
106 #if SK_ANGLE | 202 if (next == NULL) { |
107 START("angle", GpuGMTask, angle, gpuAPI, 0, false); | 203 SkDebugf("Skipping %s: Don't understand '%s'.\n", config, part); |
108 #endif | 204 delete sink; |
109 #if SK_MESA | 205 sink = NULL; |
110 START("mesa", GpuGMTask, mesa, gpuAPI, 0, false); | 206 break; |
111 #endif | 207 } |
112 START("pdf", PDFTask, get_pdf_rasterizer_proc()); | 208 sink = next; |
113 } | 209 } |
114 } | 210 if (sink) { |
115 #undef START | 211 push_sink(config, sink); |
116 } | 212 } |
117 | 213 } |
118 static void kick_off_tests(const SkTDArray<TestRegistry::Factory>& tests, | 214 } |
119 DM::Reporter* reporter, | 215 |
120 DM::TaskRunner* tasks) { | 216 // The finest-grained unit of work we can run: draw a single Src into a single S
ink, |
121 for (int i = 0; i < tests.count(); i++) { | 217 // report any errors, and perhaps write out the output: a .png of the bitmap, or
a raw stream. |
122 SkAutoTDelete<Test> test(tests[i](NULL)); | 218 struct Task { |
123 if (test->isGPUTest()) { | 219 Task(const Tagged<Src>& src, const Tagged<Sink>& sink) : src(src), sink(sink
) {} |
124 tasks->add(SkNEW_ARGS(DM::GpuTestTask, (reporter, tasks, tests[i])))
; | 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 } |
125 } else { | 285 } else { |
126 tasks->add(SkNEW_ARGS(DM::CpuTestTask, (reporter, tasks, tests[i])))
; | 286 path = SkOSPath::Join(dir, task.sink.tag); |
127 } | 287 sk_mkdir(path.c_str()); |
128 } | 288 path = SkOSPath::Join(path.c_str(), task.src.tag); |
129 } | 289 sk_mkdir(path.c_str()); |
130 | 290 path = SkOSPath::Join(path.c_str(), task.src->name().c_str()); |
131 static void find_files(const char* dir, | 291 path.append("."); |
132 const char* suffixes[], | 292 path.append(ext); |
133 size_t num_suffixes, | 293 } |
134 SkTArray<SkString>* files) { | 294 |
135 if (0 == strcmp(dir, "")) { | 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::kP
NG_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. |
| 327 static 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 |
| 337 static 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 |
| 348 static SkTArray<SkAutoTDelete<skiatest::Test>, kMemcpyOK> gTests; |
| 349 |
| 350 static void gather_tests() { |
| 351 if (!FLAGS_tests) { |
136 return; | 352 return; |
137 } | 353 } |
138 | 354 for (const skiatest::TestRegistry* r = skiatest::TestRegistry::Head(); r; r
= r->next()) { |
139 SkString filename; | 355 SkAutoTDelete<skiatest::Test> test(r->factory()(NULL)); |
140 for (size_t i = 0; i < num_suffixes; i++) { | 356 if (SkCommandLineFlags::ShouldSkip(FLAGS_match, test->getName())) { |
141 SkOSFile::Iter it(dir, suffixes[i]); | 357 continue; |
142 while (it.next(&filename)) { | 358 } |
143 if (!SkCommandLineFlags::ShouldSkip(FLAGS_match, filename.c_str()))
{ | 359 if (test->isGPUTest() /*&& !gpu_supported()*/) { // TEMPORARILY DISABLE
D |
144 files->push_back(SkOSPath::Join(dir, filename.c_str())); | 360 continue; |
145 } | 361 } |
146 } | 362 if (!test->isGPUTest() && !FLAGS_cpu) { |
147 } | 363 continue; |
148 } | 364 } |
149 | 365 test->setReporter(&gTestReporter); |
150 static void kick_off_skps(const SkTArray<SkString>& skps, | 366 gTests.push_back().reset(test.detach()); |
151 DM::Reporter* reporter, | 367 } |
152 DM::TaskRunner* tasks) { | 368 } |
153 for (int i = 0; i < skps.count(); ++i) { | 369 |
154 SkAutoTUnref<SkStream> stream(SkStream::NewFromFile(skps[i].c_str())); | 370 static void run_test(SkAutoTDelete<skiatest::Test>* t) { |
155 if (stream.get() == NULL) { | 371 WallTimer timer; |
156 SkDebugf("Could not read %s.\n", skps[i].c_str()); | 372 timer.start(); |
157 exit(1); | 373 skiatest::Test* test = t->get(); |
158 } | 374 if (!FLAGS_dryRun) { |
159 SkAutoTUnref<SkPicture> pic( | 375 GrContextFactory grFactory; |
160 SkPicture::CreateFromStream(stream.get(), &sk_tools::LazyDecodeB
itmap)); | 376 test->setGrContextFactory(&grFactory); |
161 if (pic.get() == NULL) { | 377 test->run(); |
162 SkDebugf("Could not read %s as an SkPicture.\n", skps[i].c_str()); | 378 if (!test->passed()) { |
163 exit(1); | 379 fail(SkStringPrintf("test %s failed", test->getName())); |
164 } | 380 } |
165 | 381 } |
166 SkString filename = SkOSPath::Basename(skps[i].c_str()); | 382 timer.end(); |
167 tasks->add(SkNEW_ARGS(DM::SKPTask, (reporter, tasks, pic, filename))); | 383 done(timer.fWall, "unit", "test", test->getName()); |
168 tasks->add(SkNEW_ARGS(DM::PDFTask, (reporter, tasks, pic, filename, | 384 } |
169 get_pdf_rasterizer_proc()))); | 385 |
170 } | 386 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~*/ |
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) { | |
191 return; | |
192 } | |
193 | |
194 SkDebugf("Failures:\n"); | |
195 for (int i = 0; i < failures.count(); i++) { | |
196 SkDebugf(" %s\n", failures[i].c_str()); | |
197 } | |
198 SkDebugf("%d failures.\n", failures.count()); | |
199 } | |
200 | |
201 static GrGLStandard get_gl_standard() { | |
202 if (FLAGS_gpuAPI.contains(kGpuAPINameGL)) { | |
203 return kGL_GrGLStandard; | |
204 } | |
205 if (FLAGS_gpuAPI.contains(kGpuAPINameGLES)) { | |
206 return kGLES_GrGLStandard; | |
207 } | |
208 return kNone_GrGLStandard; | |
209 } | |
210 | |
211 template <typename T, typename Registry> | |
212 static void append_matching_factories(Registry* head, SkTDArray<typename Registr
y::Factory>* out) { | |
213 for (const Registry* reg = head; reg != NULL; reg = reg->next()) { | |
214 SkAutoTDelete<T> forName(reg->factory()(NULL)); | |
215 if (!SkCommandLineFlags::ShouldSkip(FLAGS_match, forName->getName())) { | |
216 *out->append() = reg->factory(); | |
217 } | |
218 } | |
219 } | |
220 | 387 |
221 int dm_main(); | 388 int dm_main(); |
222 int dm_main() { | 389 int dm_main() { |
223 SetupCrashHandler(); | 390 SetupCrashHandler(); |
224 SkAutoGraphics ag; | 391 SkAutoGraphics ag; |
225 SkTaskGroup::Enabler enabled(FLAGS_threads); | 392 SkTaskGroup::Enabler enabled(FLAGS_threads); |
226 | 393 |
227 if (FLAGS_dryRun || FLAGS_veryVerbose) { | 394 gather_srcs(); |
228 FLAGS_verbose = true; | 395 gather_sinks(); |
229 } | 396 gather_tests(); |
230 #if SK_ENABLE_INST_COUNT | 397 |
231 gPrintInstCount = FLAGS_leaks; | 398 gPending = gSrcs.count() * gSinks.count() + gTests.count(); |
232 #endif | 399 SkDebugf("%d srcs * %d sinks + %d tests == %d tasks\n", |
233 | 400 gSrcs.count(), gSinks.count(), gTests.count(), gPending); |
234 SkTArray<SkString> configs; | 401 |
235 for (int i = 0; i < FLAGS_config.count(); i++) { | 402 // We try to exploit as much parallelism as is safe. Most Src/Sink pairs ru
n on any thread, |
236 SkStrSplit(FLAGS_config[i], ", ", &configs); | 403 // but Sinks that identify as part of a particular enclave run serially on a
single thread. |
237 } | 404 // Tests run on any thread, with a separate GrContextFactory for each GPU te
st. |
238 | 405 SkTArray<Task> enclaves[kNumEnclaves]; |
239 GrGLStandard gpuAPI = get_gl_standard(); | 406 for (int j = 0; j < gSinks.count(); j++) { |
240 | 407 SkTArray<Task>& tasks = enclaves[gSinks[j]->enclave()]; |
241 SkTDArray<GMRegistry::Factory> gms; | 408 for (int i = 0; i < gSrcs.count(); i++) { |
242 if (FLAGS_gms) { | 409 tasks.push_back(Task(gSrcs[i], gSinks[j])); |
243 append_matching_factories<GM>(GMRegistry::Head(), &gms); | 410 } |
244 } | 411 } |
245 | 412 |
246 SkTDArray<TestRegistry::Factory> tests; | 413 SK_COMPILE_ASSERT(kAnyThread_Enclave == 0, AnyThreadZero); |
247 if (FLAGS_tests) { | 414 SkTaskGroup tg; |
248 append_matching_factories<Test>(TestRegistry::Head(), &tests); | 415 tg.batch( Task::Run, enclaves[0].begin(), enclaves[0].count()); |
249 } | 416 tg.batch(run_enclave, enclaves+1, kNumEnclaves-1); |
250 | 417 tg.batch( run_test, gTests.begin(), gTests.count()); |
251 | 418 tg.wait(); |
252 SkTArray<SkString> skps; | 419 |
253 if (!FLAGS_skps.isEmpty()) { | 420 // At this point we're back in single-threaded land. |
254 const char* suffixes[] = { "skp" }; | 421 |
255 find_files(FLAGS_skps[0], suffixes, SK_ARRAY_COUNT(suffixes), &skps); | 422 if (!FLAGS_verbose) { |
256 } | 423 SkDebugf("\n"); |
257 | 424 } |
258 SkTArray<SkString> images; | 425 |
259 if (!FLAGS_images.isEmpty()) { | 426 JsonWriter::DumpJson(); |
260 const char* suffixes[] = { | 427 |
261 "bmp", "gif", "jpg", "jpeg", "png", "webp", "ktx", "astc", "wbmp", "
ico", | 428 if (gFailures.count() > 0) { |
262 "BMP", "GIF", "JPG", "JPEG", "PNG", "WEBP", "KTX", "ASTC", "WBMP", "
ICO", | 429 SkDebugf("Failures:\n"); |
263 }; | 430 for (int i = 0; i < gFailures.count(); i++) { |
264 find_files(FLAGS_images[0], suffixes, SK_ARRAY_COUNT(suffixes), &images)
; | 431 SkDebugf("\t%s", gFailures[i].c_str()); |
265 } | 432 } |
266 | 433 SkDebugf("%d failures\n", gFailures.count()); |
267 SkDebugf("%d GMs x %d configs, %d tests, %d pictures, %d images\n", | 434 return 1; |
268 gms.count(), configs.count(), tests.count(), skps.count(), images.c
ount()); | 435 } |
269 DM::Reporter reporter; | 436 if (gPending > 0) { |
270 | 437 SkDebugf("Hrm, we didn't seem to run everything we intended to! Please
file a bug.\n"); |
271 DM::TaskRunner tasks; | 438 return 1; |
272 kick_off_tests(tests, &reporter, &tasks); | 439 } |
273 kick_off_gms(gms, configs, gpuAPI, &reporter, &tasks); | 440 return 0; |
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; | |
291 } | 441 } |
292 | 442 |
293 #if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL) | 443 #if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL) |
294 int main(int argc, char** argv) { | 444 int main(int argc, char** argv) { |
295 SkCommandLineFlags::Parse(argc, argv); | 445 SkCommandLineFlags::Parse(argc, argv); |
296 return dm_main(); | 446 return dm_main(); |
297 } | 447 } |
298 #endif | 448 #endif |
OLD | NEW |