| jcgregorio | 3b27ade | 2014-11-13 08:06:40 -0800 | [diff] [blame] | 1 | #include "CrashHandler.h" | 
| mtklein | 748ca3b | 2015-01-15 10:56:12 -0800 | [diff] [blame] | 2 | #include "DMJsonWriter.h" | 
 | 3 | #include "DMSrcSink.h" | 
 | 4 | #include "OverwriteLine.h" | 
 | 5 | #include "ProcStats.h" | 
 | 6 | #include "SkBBHFactory.h" | 
| caryclark | 17f0b6d | 2014-07-22 10:15:34 -0700 | [diff] [blame] | 7 | #include "SkCommonFlags.h" | 
| mtklein@google.com | d36522d | 2013-10-16 13:02:15 +0000 | [diff] [blame] | 8 | #include "SkForceLinking.h" | 
 | 9 | #include "SkGraphics.h" | 
| mtklein | 748ca3b | 2015-01-15 10:56:12 -0800 | [diff] [blame] | 10 | #include "SkMD5.h" | 
| mtklein | 1d0f164 | 2014-09-08 08:05:18 -0700 | [diff] [blame] | 11 | #include "SkOSFile.h" | 
| mtklein | 406654b | 2014-09-03 15:34:37 -0700 | [diff] [blame] | 12 | #include "SkTaskGroup.h" | 
| commit-bot@chromium.org | 0dc5bd1 | 2014-02-26 16:31:22 +0000 | [diff] [blame] | 13 | #include "Test.h" | 
| mtklein | 748ca3b | 2015-01-15 10:56:12 -0800 | [diff] [blame] | 14 | #include "Timer.h" | 
| mtklein@google.com | d36522d | 2013-10-16 13:02:15 +0000 | [diff] [blame] | 15 |  | 
| commit-bot@chromium.org | 0dc5bd1 | 2014-02-26 16:31:22 +0000 | [diff] [blame] | 16 | DEFINE_bool(tests, true, "Run tests?"); | 
| mtklein | 748ca3b | 2015-01-15 10:56:12 -0800 | [diff] [blame] | 17 | DEFINE_string(images, "resources", "Images to decode."); | 
 | 18 | //DEFINE_string(src, "gm skp image subset", "Source types to test."); | 
 | 19 | DEFINE_string(src, "gm skp", "Source types to test.  TEMPORARILY DISABLED"); | 
 | 20 | DEFINE_bool(nameByHash, false, | 
 | 21 |             "If true, write to FLAGS_writePath[0]/<hash>.png instead of " | 
 | 22 |             "to FLAGS_writePath[0]/<config>/<sourceType>/<name>.png"); | 
 | 23 | DEFINE_bool2(pathOpsExtended, x, false, "Run extended pathOps tests."); | 
 | 24 | DEFINE_string(matrix, "1 0 0 0 1 0 0 0 1", | 
 | 25 |               "Matrix to apply when using 'matrix' in config."); | 
| mtklein | 82d2843 | 2015-01-15 12:46:02 -0800 | [diff] [blame] | 26 | DEFINE_bool(gpu_threading, false, "Allow GPU work to run on multiple threads?"); | 
| mtklein@google.com | d36522d | 2013-10-16 13:02:15 +0000 | [diff] [blame] | 27 |  | 
| mtklein | a2ef642 | 2015-01-15 13:44:22 -0800 | [diff] [blame^] | 28 | DEFINE_string(blacklist, "", | 
 | 29 |         "Space-separated config/src/name triples to blacklist.  '_' matches anything.  E.g. \n" | 
 | 30 |         "'--blacklist gpu skp _' will blacklist all SKPs drawn into the gpu config.\n" | 
 | 31 |         "'--blacklist gpu skp _ 8888 gm aarects' will also blacklist the aarects GM on 8888."); | 
 | 32 |  | 
 | 33 |  | 
| mtklein@google.com | d36522d | 2013-10-16 13:02:15 +0000 | [diff] [blame] | 34 | __SK_FORCE_IMAGE_DECODER_LINKING; | 
| mtklein | 748ca3b | 2015-01-15 10:56:12 -0800 | [diff] [blame] | 35 | using namespace DM; | 
| mtklein@google.com | d36522d | 2013-10-16 13:02:15 +0000 | [diff] [blame] | 36 |  | 
| mtklein | 748ca3b | 2015-01-15 10:56:12 -0800 | [diff] [blame] | 37 | /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ | 
 | 38 |  | 
 | 39 | SK_DECLARE_STATIC_MUTEX(gFailuresMutex); | 
 | 40 | static SkTArray<SkString> gFailures; | 
 | 41 |  | 
 | 42 | static void fail(ImplicitString err) { | 
 | 43 |     SkAutoMutexAcquire lock(gFailuresMutex); | 
 | 44 |     SkDebugf("\n\nFAILURE: %s\n\n", err.c_str()); | 
 | 45 |     gFailures.push_back(err); | 
| halcanary | 9e398f7 | 2015-01-10 11:18:04 -0800 | [diff] [blame] | 46 | } | 
 | 47 |  | 
| mtklein | 748ca3b | 2015-01-15 10:56:12 -0800 | [diff] [blame] | 48 | static int32_t gPending = 0;  // Atomic. | 
 | 49 |  | 
 | 50 | static void done(double ms, ImplicitString config, ImplicitString src, ImplicitString name) { | 
 | 51 |     SkDebugf("%s(%4dMB %5d) %s\t%s %s %s  ", FLAGS_verbose ? "\n" : kSkOverwriteLine | 
 | 52 |                                            , sk_tools::getMaxResidentSetSizeMB() | 
 | 53 |                                            , sk_atomic_dec(&gPending)-1 | 
 | 54 |                                            , HumanizeMs(ms).c_str() | 
 | 55 |                                            , config.c_str() | 
 | 56 |                                            , src.c_str() | 
 | 57 |                                            , name.c_str()); | 
| mtklein@google.com | d36522d | 2013-10-16 13:02:15 +0000 | [diff] [blame] | 58 | } | 
 | 59 |  | 
| mtklein | 748ca3b | 2015-01-15 10:56:12 -0800 | [diff] [blame] | 60 | /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ | 
| mtklein | 114c3cd | 2015-01-15 10:15:02 -0800 | [diff] [blame] | 61 |  | 
| mtklein | 748ca3b | 2015-01-15 10:56:12 -0800 | [diff] [blame] | 62 | template <typename T> | 
 | 63 | struct Tagged : public SkAutoTDelete<T> { const char* tag; }; | 
 | 64 |  | 
 | 65 | static const bool kMemcpyOK = true; | 
 | 66 |  | 
 | 67 | static SkTArray<Tagged<Src>,  kMemcpyOK>  gSrcs; | 
 | 68 | static SkTArray<Tagged<Sink>, kMemcpyOK> gSinks; | 
 | 69 |  | 
 | 70 | static void push_src(const char* tag, Src* s) { | 
 | 71 |     SkAutoTDelete<Src> src(s); | 
 | 72 |     if (FLAGS_src.contains(tag) && | 
 | 73 |         !SkCommandLineFlags::ShouldSkip(FLAGS_match, src->name().c_str())) { | 
 | 74 |         Tagged<Src>& s = gSrcs.push_back(); | 
 | 75 |         s.reset(src.detach()); | 
 | 76 |         s.tag = tag; | 
| mtklein | 114c3cd | 2015-01-15 10:15:02 -0800 | [diff] [blame] | 77 |     } | 
| mtklein | 748ca3b | 2015-01-15 10:56:12 -0800 | [diff] [blame] | 78 | } | 
| mtklein | 114c3cd | 2015-01-15 10:15:02 -0800 | [diff] [blame] | 79 |  | 
| mtklein | 748ca3b | 2015-01-15 10:56:12 -0800 | [diff] [blame] | 80 | static void gather_srcs() { | 
 | 81 |     for (const skiagm::GMRegistry* r = skiagm::GMRegistry::Head(); r; r = r->next()) { | 
 | 82 |         push_src("gm", new GMSrc(r->factory())); | 
 | 83 |     } | 
 | 84 |     if (!FLAGS_skps.isEmpty()) { | 
 | 85 |         SkOSFile::Iter it(FLAGS_skps[0], "skp"); | 
 | 86 |         for (SkString file; it.next(&file); ) { | 
 | 87 |             push_src("skp", new SKPSrc(SkOSPath::Join(FLAGS_skps[0], file.c_str()))); | 
| mtklein | 114c3cd | 2015-01-15 10:15:02 -0800 | [diff] [blame] | 88 |         } | 
 | 89 |     } | 
| mtklein | 748ca3b | 2015-01-15 10:56:12 -0800 | [diff] [blame] | 90 |     if (!FLAGS_images.isEmpty()) { | 
 | 91 |         const char* exts[] = { | 
 | 92 |             "bmp", "gif", "jpg", "jpeg", "png", "webp", "ktx", "astc", "wbmp", "ico", | 
 | 93 |             "BMP", "GIF", "JPG", "JPEG", "PNG", "WEBP", "KTX", "ASTC", "WBMP", "ICO", | 
 | 94 |         }; | 
 | 95 |         for (size_t i = 0; i < SK_ARRAY_COUNT(exts); i++) { | 
 | 96 |             SkOSFile::Iter it(FLAGS_images[0], exts[i]); | 
 | 97 |             for (SkString file; it.next(&file); ) { | 
 | 98 |                 SkString path = SkOSPath::Join(FLAGS_images[0], file.c_str()); | 
 | 99 |                 push_src("image",  new ImageSrc(path));     // Decode entire image. | 
 | 100 |                 push_src("subset", new ImageSrc(path, 5));  // Decode 5 random subsets. | 
| mtklein | 114c3cd | 2015-01-15 10:15:02 -0800 | [diff] [blame] | 101 |             } | 
| mtklein | 709d2c3 | 2015-01-15 08:30:25 -0800 | [diff] [blame] | 102 |         } | 
| mtklein | 709d2c3 | 2015-01-15 08:30:25 -0800 | [diff] [blame] | 103 |     } | 
 | 104 | } | 
 | 105 |  | 
| mtklein | 748ca3b | 2015-01-15 10:56:12 -0800 | [diff] [blame] | 106 | static GrGLStandard get_gpu_api() { | 
 | 107 |     if (FLAGS_gpuAPI.contains("gl"))   { return kGL_GrGLStandard; } | 
 | 108 |     if (FLAGS_gpuAPI.contains("gles")) { return kGLES_GrGLStandard; } | 
 | 109 |     return kNone_GrGLStandard; | 
| mtklein | 709d2c3 | 2015-01-15 08:30:25 -0800 | [diff] [blame] | 110 | } | 
 | 111 |  | 
| mtklein | 748ca3b | 2015-01-15 10:56:12 -0800 | [diff] [blame] | 112 | static void push_sink(const char* tag, Sink* s) { | 
 | 113 |     SkAutoTDelete<Sink> sink(s); | 
 | 114 |     if (!FLAGS_config.contains(tag)) { | 
 | 115 |         return; | 
| mtklein | 114c3cd | 2015-01-15 10:15:02 -0800 | [diff] [blame] | 116 |     } | 
| mtklein | 748ca3b | 2015-01-15 10:56:12 -0800 | [diff] [blame] | 117 |     // Try a noop Src as a canary.  If it fails, skip this sink. | 
 | 118 |     struct : public Src { | 
 | 119 |         Error draw(SkCanvas*) const SK_OVERRIDE { return ""; } | 
 | 120 |         SkISize size() const SK_OVERRIDE { return SkISize::Make(16, 16); } | 
 | 121 |         Name name() const SK_OVERRIDE { return "noop"; } | 
 | 122 |     } noop; | 
| mtklein | 114c3cd | 2015-01-15 10:15:02 -0800 | [diff] [blame] | 123 |  | 
| mtklein | 748ca3b | 2015-01-15 10:56:12 -0800 | [diff] [blame] | 124 |     SkBitmap bitmap; | 
 | 125 |     SkDynamicMemoryWStream stream; | 
 | 126 |     Error err = sink->draw(noop, &bitmap, &stream); | 
 | 127 |     if (!err.isEmpty()) { | 
 | 128 |         SkDebugf("Skipping %s: %s\n", tag, err.c_str()); | 
| mtklein | 114c3cd | 2015-01-15 10:15:02 -0800 | [diff] [blame] | 129 |         return; | 
 | 130 |     } | 
 | 131 |  | 
| mtklein | 748ca3b | 2015-01-15 10:56:12 -0800 | [diff] [blame] | 132 |     Tagged<Sink>& ts = gSinks.push_back(); | 
 | 133 |     ts.reset(sink.detach()); | 
 | 134 |     ts.tag = tag; | 
 | 135 | } | 
 | 136 |  | 
 | 137 | static bool gpu_supported() { | 
 | 138 | #if SK_SUPPORT_GPU | 
 | 139 |     return FLAGS_gpu; | 
 | 140 | #else | 
 | 141 |     return false; | 
 | 142 | #endif | 
 | 143 | } | 
 | 144 |  | 
 | 145 | static Sink* create_sink(const char* tag) { | 
 | 146 | #define SINK(t, sink, ...) if (0 == strcmp(t, tag)) { return new sink(__VA_ARGS__); } | 
 | 147 |     if (gpu_supported()) { | 
| mtklein | 82d2843 | 2015-01-15 12:46:02 -0800 | [diff] [blame] | 148 |         typedef GrContextFactory Gr; | 
| mtklein | 748ca3b | 2015-01-15 10:56:12 -0800 | [diff] [blame] | 149 |         const GrGLStandard api = get_gpu_api(); | 
| mtklein | 82d2843 | 2015-01-15 12:46:02 -0800 | [diff] [blame] | 150 |         SINK("gpunull",    GPUSink, Gr::kNull_GLContextType,   api,  0, false, FLAGS_gpu_threading); | 
 | 151 |         SINK("gpudebug",   GPUSink, Gr::kDebug_GLContextType,  api,  0, false, FLAGS_gpu_threading); | 
 | 152 |         SINK("gpu",        GPUSink, Gr::kNative_GLContextType, api,  0, false, FLAGS_gpu_threading); | 
 | 153 |         SINK("gpudft",     GPUSink, Gr::kNative_GLContextType, api,  0,  true, FLAGS_gpu_threading); | 
 | 154 |         SINK("msaa4",      GPUSink, Gr::kNative_GLContextType, api,  4, false, FLAGS_gpu_threading); | 
 | 155 |         SINK("msaa16",     GPUSink, Gr::kNative_GLContextType, api, 16, false, FLAGS_gpu_threading); | 
 | 156 |         SINK("nvprmsaa4",  GPUSink, Gr::kNVPR_GLContextType,   api,  4, false, FLAGS_gpu_threading); | 
 | 157 |         SINK("nvprmsaa16", GPUSink, Gr::kNVPR_GLContextType,   api, 16, false, FLAGS_gpu_threading); | 
| mtklein | 748ca3b | 2015-01-15 10:56:12 -0800 | [diff] [blame] | 158 |     #if SK_ANGLE | 
| mtklein | 82d2843 | 2015-01-15 12:46:02 -0800 | [diff] [blame] | 159 |         SINK("angle",      GPUSink, Gr::kANGLE_GLContextType,  api,  0, false, FLAGS_gpu_threading); | 
| mtklein | 748ca3b | 2015-01-15 10:56:12 -0800 | [diff] [blame] | 160 |     #endif | 
 | 161 |     #if SK_MESA | 
| mtklein | 82d2843 | 2015-01-15 12:46:02 -0800 | [diff] [blame] | 162 |         SINK("mesa",       GPUSink, Gr::kMESA_GLContextType,   api,  0, false, FLAGS_gpu_threading); | 
| mtklein | 748ca3b | 2015-01-15 10:56:12 -0800 | [diff] [blame] | 163 |     #endif | 
| mtklein | 114c3cd | 2015-01-15 10:15:02 -0800 | [diff] [blame] | 164 |     } | 
| mtklein | 748ca3b | 2015-01-15 10:56:12 -0800 | [diff] [blame] | 165 |  | 
 | 166 |     if (FLAGS_cpu) { | 
 | 167 |         SINK("565",  RasterSink, kRGB_565_SkColorType); | 
 | 168 |         SINK("8888", RasterSink, kN32_SkColorType); | 
 | 169 |         // TODO(mtklein): reenable once skiagold can handle .pdf uploads. | 
 | 170 |         //SINK("pdf",  PDFSink); | 
 | 171 |     } | 
 | 172 | #undef SINK | 
 | 173 |     return NULL; | 
| mtklein | 114c3cd | 2015-01-15 10:15:02 -0800 | [diff] [blame] | 174 | } | 
 | 175 |  | 
| mtklein | 748ca3b | 2015-01-15 10:56:12 -0800 | [diff] [blame] | 176 | static Sink* create_via(const char* tag, Sink* wrapped) { | 
 | 177 | #define VIA(t, via, ...) if (0 == strcmp(t, tag)) { return new via(__VA_ARGS__); } | 
 | 178 |     VIA("serialize", ViaSerialization, wrapped); | 
 | 179 |  | 
 | 180 |     VIA("tiles",    ViaTiles, 256, 256,               NULL, wrapped); | 
 | 181 |     VIA("tiles_rt", ViaTiles, 256, 256, new SkRTreeFactory, wrapped); | 
 | 182 |  | 
 | 183 |     const int xp = SkGPipeWriter::kCrossProcess_Flag, | 
 | 184 |               sa = xp | SkGPipeWriter::kSharedAddressSpace_Flag; | 
 | 185 |     VIA("pipe",    ViaPipe,  0, wrapped); | 
 | 186 |     VIA("pipe_xp", ViaPipe, xp, wrapped); | 
 | 187 |     VIA("pipe_sa", ViaPipe, sa, wrapped); | 
 | 188 |  | 
 | 189 |     if (FLAGS_matrix.count() == 9) { | 
 | 190 |         SkMatrix m; | 
 | 191 |         for (int i = 0; i < 9; i++) { | 
 | 192 |             m[i] = (SkScalar)atof(FLAGS_matrix[i]); | 
 | 193 |         } | 
 | 194 |         VIA("matrix", ViaMatrix, m, wrapped); | 
 | 195 |     } | 
 | 196 | #undef VIA | 
 | 197 |     return NULL; | 
| mtklein | 114c3cd | 2015-01-15 10:15:02 -0800 | [diff] [blame] | 198 | } | 
 | 199 |  | 
| mtklein | 748ca3b | 2015-01-15 10:56:12 -0800 | [diff] [blame] | 200 | static void gather_sinks() { | 
 | 201 |     for (int i = 0; i < FLAGS_config.count(); i++) { | 
 | 202 |         const char* config = FLAGS_config[i]; | 
 | 203 |         SkTArray<SkString> parts; | 
 | 204 |         SkStrSplit(config, "-", &parts); | 
 | 205 |  | 
 | 206 |         Sink* sink = NULL; | 
 | 207 |         for (int i = parts.count(); i-- > 0;) { | 
 | 208 |             const char* part = parts[i].c_str(); | 
 | 209 |             Sink* next = (sink == NULL) ? create_sink(part) : create_via(part, sink); | 
 | 210 |             if (next == NULL) { | 
 | 211 |                 SkDebugf("Skipping %s: Don't understand '%s'.\n", config, part); | 
 | 212 |                 delete sink; | 
 | 213 |                 sink = NULL; | 
 | 214 |                 break; | 
 | 215 |             } | 
 | 216 |             sink = next; | 
 | 217 |         } | 
 | 218 |         if (sink) { | 
 | 219 |             push_sink(config, sink); | 
| mtklein | 114c3cd | 2015-01-15 10:15:02 -0800 | [diff] [blame] | 220 |         } | 
 | 221 |     } | 
 | 222 | } | 
| mtklein | 709d2c3 | 2015-01-15 08:30:25 -0800 | [diff] [blame] | 223 |  | 
| mtklein | a2ef642 | 2015-01-15 13:44:22 -0800 | [diff] [blame^] | 224 | static bool match(const char* needle, const char* haystack) { | 
 | 225 |     return 0 == strcmp("_", needle) || NULL != strstr(haystack, needle); | 
 | 226 | } | 
 | 227 |  | 
 | 228 | static ImplicitString is_blacklisted(const char* sink, const char* src, const char* name) { | 
 | 229 |     for (int i = 0; i < FLAGS_blacklist.count() - 2; i += 3) { | 
 | 230 |         if (match(FLAGS_blacklist[i+0], sink) && | 
 | 231 |             match(FLAGS_blacklist[i+1],  src) && | 
 | 232 |             match(FLAGS_blacklist[i+2], name)) { | 
 | 233 |             return SkStringPrintf("%s %s %s", | 
 | 234 |                                   FLAGS_blacklist[i+0], FLAGS_blacklist[i+1], FLAGS_blacklist[i+2]); | 
 | 235 |         } | 
 | 236 |     } | 
 | 237 |     return ""; | 
 | 238 | } | 
 | 239 |  | 
| mtklein | 748ca3b | 2015-01-15 10:56:12 -0800 | [diff] [blame] | 240 | // The finest-grained unit of work we can run: draw a single Src into a single Sink, | 
 | 241 | // report any errors, and perhaps write out the output: a .png of the bitmap, or a raw stream. | 
 | 242 | struct Task { | 
 | 243 |     Task(const Tagged<Src>& src, const Tagged<Sink>& sink) : src(src), sink(sink) {} | 
 | 244 |     const Tagged<Src>&  src; | 
 | 245 |     const Tagged<Sink>& sink; | 
 | 246 |  | 
 | 247 |     static void Run(Task* task) { | 
| mtklein | a2ef642 | 2015-01-15 13:44:22 -0800 | [diff] [blame^] | 248 |         SkString name = task->src->name(); | 
 | 249 |         SkString whyBlacklisted = is_blacklisted(task->sink.tag, task->src.tag, name.c_str()); | 
| mtklein | 748ca3b | 2015-01-15 10:56:12 -0800 | [diff] [blame] | 250 |         WallTimer timer; | 
 | 251 |         timer.start(); | 
| mtklein | a2ef642 | 2015-01-15 13:44:22 -0800 | [diff] [blame^] | 252 |         if (!FLAGS_dryRun && whyBlacklisted.isEmpty()) { | 
| mtklein | 748ca3b | 2015-01-15 10:56:12 -0800 | [diff] [blame] | 253 |             SkBitmap bitmap; | 
 | 254 |             SkDynamicMemoryWStream stream; | 
 | 255 |             Error err = task->sink->draw(*task->src, &bitmap, &stream); | 
 | 256 |             if (!err.isEmpty()) { | 
 | 257 |                 fail(SkStringPrintf("%s %s %s: %s", | 
 | 258 |                                     task->sink.tag, | 
 | 259 |                                     task->src.tag, | 
| mtklein | a2ef642 | 2015-01-15 13:44:22 -0800 | [diff] [blame^] | 260 |                                     name.c_str(), | 
| mtklein | 748ca3b | 2015-01-15 10:56:12 -0800 | [diff] [blame] | 261 |                                     err.c_str())); | 
 | 262 |             } | 
 | 263 |             if (!FLAGS_writePath.isEmpty()) { | 
 | 264 |                 const char* ext = task->sink->fileExtension(); | 
 | 265 |                 if (stream.bytesWritten() == 0) { | 
 | 266 |                     SkMemoryStream pixels(bitmap.getPixels(), bitmap.getSize()); | 
 | 267 |                     WriteToDisk(*task, &pixels, bitmap.getSize(), &bitmap, ext); | 
 | 268 |                 } else { | 
 | 269 |                     SkAutoTDelete<SkStreamAsset> data(stream.detachAsStream()); | 
 | 270 |                     WriteToDisk(*task, data, data->getLength(), NULL, ext); | 
 | 271 |                 } | 
 | 272 |             } | 
 | 273 |         } | 
 | 274 |         timer.end(); | 
| mtklein | a2ef642 | 2015-01-15 13:44:22 -0800 | [diff] [blame^] | 275 |         if (!whyBlacklisted.isEmpty()) { | 
 | 276 |             name.appendf(" (--blacklist, %s)", whyBlacklisted.c_str()); | 
 | 277 |         } | 
 | 278 |         done(timer.fWall, task->sink.tag, task->src.tag, name); | 
| mtklein | 748ca3b | 2015-01-15 10:56:12 -0800 | [diff] [blame] | 279 |     } | 
 | 280 |  | 
 | 281 |     static void WriteToDisk(const Task& task, | 
 | 282 |                             SkStream* data, size_t len, | 
 | 283 |                             const SkBitmap* bitmap, | 
 | 284 |                             const char* ext) { | 
 | 285 |         SkMD5 hash; | 
 | 286 |         hash.writeStream(data, len); | 
 | 287 |         SkMD5::Digest digest; | 
 | 288 |         hash.finish(digest); | 
 | 289 |  | 
 | 290 |         JsonWriter::BitmapResult result; | 
 | 291 |         result.name       = task.src->name(); | 
 | 292 |         result.config     = task.sink.tag; | 
 | 293 |         result.sourceType = task.src.tag; | 
 | 294 |         result.ext        = ext; | 
 | 295 |         for (int i = 0; i < 16; i++) { | 
 | 296 |             result.md5.appendf("%02x", digest.data[i]); | 
 | 297 |         } | 
 | 298 |         JsonWriter::AddBitmapResult(result); | 
 | 299 |  | 
 | 300 |         const char* dir = FLAGS_writePath[0]; | 
 | 301 |         if (0 == strcmp(dir, "@")) {  // Needed for iOS. | 
 | 302 |             dir = FLAGS_resourcePath[0]; | 
 | 303 |         } | 
 | 304 |         sk_mkdir(dir); | 
 | 305 |  | 
 | 306 |         SkString path; | 
 | 307 |         if (FLAGS_nameByHash) { | 
 | 308 |             path = SkOSPath::Join(dir, result.md5.c_str()); | 
 | 309 |             path.append("."); | 
 | 310 |             path.append(ext); | 
 | 311 |             if (sk_exists(path.c_str())) { | 
 | 312 |                 return;  // Content-addressed.  If it exists already, we're done. | 
 | 313 |             } | 
 | 314 |         } else { | 
 | 315 |             path = SkOSPath::Join(dir, task.sink.tag); | 
 | 316 |             sk_mkdir(path.c_str()); | 
 | 317 |             path = SkOSPath::Join(path.c_str(), task.src.tag); | 
 | 318 |             sk_mkdir(path.c_str()); | 
 | 319 |             path = SkOSPath::Join(path.c_str(), task.src->name().c_str()); | 
 | 320 |             path.append("."); | 
 | 321 |             path.append(ext); | 
 | 322 |         } | 
 | 323 |  | 
 | 324 |         SkFILEWStream file(path.c_str()); | 
 | 325 |         if (!file.isValid()) { | 
 | 326 |             fail(SkStringPrintf("Can't open %s for writing.\n", path.c_str())); | 
 | 327 |             return; | 
 | 328 |         } | 
 | 329 |  | 
 | 330 |         data->rewind(); | 
 | 331 |         if (bitmap) { | 
 | 332 |             // We can't encode A8 bitmaps as PNGs.  Convert them to 8888 first. | 
 | 333 |             SkBitmap converted; | 
 | 334 |             if (bitmap->info().colorType() == kAlpha_8_SkColorType) { | 
 | 335 |                 if (!bitmap->copyTo(&converted, kN32_SkColorType)) { | 
 | 336 |                     fail("Can't convert A8 to 8888.\n"); | 
 | 337 |                     return; | 
 | 338 |                 } | 
 | 339 |                 bitmap = &converted; | 
 | 340 |             } | 
 | 341 |             if (!SkImageEncoder::EncodeStream(&file, *bitmap, SkImageEncoder::kPNG_Type, 100)) { | 
 | 342 |                 fail(SkStringPrintf("Can't encode PNG to %s.\n", path.c_str())); | 
 | 343 |                 return; | 
 | 344 |             } | 
 | 345 |         } else { | 
 | 346 |             if (!file.writeStream(data, len)) { | 
 | 347 |                 fail(SkStringPrintf("Can't write to %s.\n", path.c_str())); | 
 | 348 |                 return; | 
 | 349 |             } | 
 | 350 |         } | 
 | 351 |     } | 
 | 352 | }; | 
 | 353 |  | 
 | 354 | // Run all tasks in the same enclave serially on the same thread. | 
 | 355 | // They can't possibly run concurrently with each other. | 
 | 356 | static void run_enclave(SkTArray<Task>* tasks) { | 
 | 357 |     for (int i = 0; i < tasks->count(); i++) { | 
 | 358 |         Task::Run(tasks->begin() + i); | 
 | 359 |     } | 
 | 360 | } | 
 | 361 |  | 
 | 362 | /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ | 
 | 363 |  | 
 | 364 | // Unit tests don't fit so well into the Src/Sink model, so we give them special treatment. | 
 | 365 |  | 
 | 366 | static struct : public skiatest::Reporter { | 
 | 367 |     void onReportFailed(const skiatest::Failure& failure) SK_OVERRIDE { | 
 | 368 |         SkString s; | 
 | 369 |         failure.getFailureString(&s); | 
 | 370 |         fail(s); | 
 | 371 |         JsonWriter::AddTestFailure(failure); | 
 | 372 |     } | 
 | 373 |     bool allowExtendedTest() const SK_OVERRIDE { return FLAGS_pathOpsExtended; } | 
 | 374 |     bool verbose()           const SK_OVERRIDE { return FLAGS_veryVerbose; } | 
 | 375 | } gTestReporter; | 
 | 376 |  | 
| mtklein | 82d2843 | 2015-01-15 12:46:02 -0800 | [diff] [blame] | 377 | static SkTArray<SkAutoTDelete<skiatest::Test>, kMemcpyOK> gCPUTests, gGPUTests; | 
| mtklein | 748ca3b | 2015-01-15 10:56:12 -0800 | [diff] [blame] | 378 |  | 
 | 379 | static void gather_tests() { | 
 | 380 |     if (!FLAGS_tests) { | 
 | 381 |         return; | 
 | 382 |     } | 
 | 383 |     for (const skiatest::TestRegistry* r = skiatest::TestRegistry::Head(); r; r = r->next()) { | 
 | 384 |         SkAutoTDelete<skiatest::Test> test(r->factory()(NULL)); | 
 | 385 |         if (SkCommandLineFlags::ShouldSkip(FLAGS_match, test->getName())) { | 
 | 386 |             continue; | 
 | 387 |         } | 
| mtklein | 82d2843 | 2015-01-15 12:46:02 -0800 | [diff] [blame] | 388 |  | 
| mtklein | 748ca3b | 2015-01-15 10:56:12 -0800 | [diff] [blame] | 389 |         test->setReporter(&gTestReporter); | 
| mtklein | 82d2843 | 2015-01-15 12:46:02 -0800 | [diff] [blame] | 390 |         if (test->isGPUTest() && gpu_supported()) { | 
 | 391 |             gGPUTests.push_back().reset(test.detach()); | 
 | 392 |         } else if (!test->isGPUTest() && FLAGS_cpu) { | 
 | 393 |             gCPUTests.push_back().reset(test.detach()); | 
 | 394 |         } | 
| mtklein | 748ca3b | 2015-01-15 10:56:12 -0800 | [diff] [blame] | 395 |     } | 
 | 396 | } | 
 | 397 |  | 
 | 398 | static void run_test(SkAutoTDelete<skiatest::Test>* t) { | 
 | 399 |     WallTimer timer; | 
 | 400 |     timer.start(); | 
 | 401 |     skiatest::Test* test = t->get(); | 
 | 402 |     if (!FLAGS_dryRun) { | 
| mtklein | 82d2843 | 2015-01-15 12:46:02 -0800 | [diff] [blame] | 403 |         test->setGrContextFactory(GetThreadLocalGrContextFactory()); | 
| mtklein | 748ca3b | 2015-01-15 10:56:12 -0800 | [diff] [blame] | 404 |         test->run(); | 
 | 405 |         if (!test->passed()) { | 
 | 406 |             fail(SkStringPrintf("test %s failed", test->getName())); | 
 | 407 |         } | 
 | 408 |     } | 
 | 409 |     timer.end(); | 
 | 410 |     done(timer.fWall, "unit", "test", test->getName()); | 
 | 411 | } | 
 | 412 |  | 
 | 413 | /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ | 
 | 414 |  | 
| jcgregorio | 3b27ade | 2014-11-13 08:06:40 -0800 | [diff] [blame] | 415 | int dm_main(); | 
| caryclark | 17f0b6d | 2014-07-22 10:15:34 -0700 | [diff] [blame] | 416 | int dm_main() { | 
| jcgregorio | 3b27ade | 2014-11-13 08:06:40 -0800 | [diff] [blame] | 417 |     SetupCrashHandler(); | 
| commit-bot@chromium.org | 39e8d93 | 2014-05-29 20:14:48 +0000 | [diff] [blame] | 418 |     SkAutoGraphics ag; | 
| mtklein | 406654b | 2014-09-03 15:34:37 -0700 | [diff] [blame] | 419 |     SkTaskGroup::Enabler enabled(FLAGS_threads); | 
| commit-bot@chromium.org | a65e2fd | 2014-05-30 17:23:31 +0000 | [diff] [blame] | 420 |  | 
| mtklein | 748ca3b | 2015-01-15 10:56:12 -0800 | [diff] [blame] | 421 |     gather_srcs(); | 
 | 422 |     gather_sinks(); | 
 | 423 |     gather_tests(); | 
| commit-bot@chromium.org | 0dc5bd1 | 2014-02-26 16:31:22 +0000 | [diff] [blame] | 424 |  | 
| mtklein | 82d2843 | 2015-01-15 12:46:02 -0800 | [diff] [blame] | 425 |     gPending = gSrcs.count() * gSinks.count() + gCPUTests.count() + gGPUTests.count(); | 
| mtklein | 748ca3b | 2015-01-15 10:56:12 -0800 | [diff] [blame] | 426 |     SkDebugf("%d srcs * %d sinks + %d tests == %d tasks\n", | 
| mtklein | 82d2843 | 2015-01-15 12:46:02 -0800 | [diff] [blame] | 427 |              gSrcs.count(), gSinks.count(), gCPUTests.count() + gGPUTests.count(), gPending); | 
| mtklein | 748ca3b | 2015-01-15 10:56:12 -0800 | [diff] [blame] | 428 |  | 
 | 429 |     // We try to exploit as much parallelism as is safe.  Most Src/Sink pairs run on any thread, | 
 | 430 |     // but Sinks that identify as part of a particular enclave run serially on a single thread. | 
| mtklein | 82d2843 | 2015-01-15 12:46:02 -0800 | [diff] [blame] | 431 |     // CPU tests run on any thread.  GPU tests depend on --gpu_threading. | 
| mtklein | 748ca3b | 2015-01-15 10:56:12 -0800 | [diff] [blame] | 432 |     SkTArray<Task> enclaves[kNumEnclaves]; | 
 | 433 |     for (int j = 0; j < gSinks.count(); j++) { | 
 | 434 |         SkTArray<Task>& tasks = enclaves[gSinks[j]->enclave()]; | 
 | 435 |         for (int i = 0; i < gSrcs.count(); i++) { | 
 | 436 |             tasks.push_back(Task(gSrcs[i], gSinks[j])); | 
 | 437 |         } | 
| commit-bot@chromium.org | 38aeb0f | 2014-02-26 23:01:57 +0000 | [diff] [blame] | 438 |     } | 
 | 439 |  | 
| mtklein | 748ca3b | 2015-01-15 10:56:12 -0800 | [diff] [blame] | 440 |     SK_COMPILE_ASSERT(kAnyThread_Enclave == 0, AnyThreadZero); | 
 | 441 |     SkTaskGroup tg; | 
 | 442 |         tg.batch(  Task::Run, enclaves[0].begin(), enclaves[0].count()); | 
 | 443 |         tg.batch(run_enclave,          enclaves+1,      kNumEnclaves-1); | 
| mtklein | 82d2843 | 2015-01-15 12:46:02 -0800 | [diff] [blame] | 444 |         tg.batch(   run_test,   gCPUTests.begin(),   gCPUTests.count()); | 
 | 445 |         if (FLAGS_gpu_threading) { | 
 | 446 |             tg.batch(run_test,  gGPUTests.begin(),   gGPUTests.count()); | 
 | 447 |         } else { | 
 | 448 |             for (int i = 0; i < gGPUTests.count(); i++) { | 
 | 449 |                 run_test(&gGPUTests[i]); | 
 | 450 |             } | 
 | 451 |         } | 
| mtklein | 748ca3b | 2015-01-15 10:56:12 -0800 | [diff] [blame] | 452 |     tg.wait(); | 
| kkinnunen | 80549fc | 2014-06-30 06:36:31 -0700 | [diff] [blame] | 453 |  | 
| mtklein | 748ca3b | 2015-01-15 10:56:12 -0800 | [diff] [blame] | 454 |     // At this point we're back in single-threaded land. | 
| mtklein | a2ef642 | 2015-01-15 13:44:22 -0800 | [diff] [blame^] | 455 |     SkDebugf("\n"); | 
| mtklein@google.com | d36522d | 2013-10-16 13:02:15 +0000 | [diff] [blame] | 456 |  | 
| mtklein | 748ca3b | 2015-01-15 10:56:12 -0800 | [diff] [blame] | 457 |     JsonWriter::DumpJson(); | 
 | 458 |  | 
 | 459 |     if (gFailures.count() > 0) { | 
 | 460 |         SkDebugf("Failures:\n"); | 
 | 461 |         for (int i = 0; i < gFailures.count(); i++) { | 
 | 462 |             SkDebugf("\t%s", gFailures[i].c_str()); | 
 | 463 |         } | 
 | 464 |         SkDebugf("%d failures\n", gFailures.count()); | 
 | 465 |         return 1; | 
| mtklein | 114c3cd | 2015-01-15 10:15:02 -0800 | [diff] [blame] | 466 |     } | 
| mtklein | 748ca3b | 2015-01-15 10:56:12 -0800 | [diff] [blame] | 467 |     if (gPending > 0) { | 
 | 468 |         SkDebugf("Hrm, we didn't seem to run everything we intended to!  Please file a bug.\n"); | 
 | 469 |         return 1; | 
| mtklein | 114c3cd | 2015-01-15 10:15:02 -0800 | [diff] [blame] | 470 |     } | 
| mtklein | 748ca3b | 2015-01-15 10:56:12 -0800 | [diff] [blame] | 471 |     return 0; | 
| mtklein@google.com | d36522d | 2013-10-16 13:02:15 +0000 | [diff] [blame] | 472 | } | 
| jcgregorio | 3b27ade | 2014-11-13 08:06:40 -0800 | [diff] [blame] | 473 |  | 
 | 474 | #if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL) | 
 | 475 | int main(int argc, char** argv) { | 
 | 476 |     SkCommandLineFlags::Parse(argc, argv); | 
 | 477 |     return dm_main(); | 
 | 478 | } | 
 | 479 | #endif |