blob: 92f1307eca1d5a76435787affe1bd6cf588550e8 [file] [log] [blame]
commit-bot@chromium.orgbe19b9e2013-06-14 17:26:54 +00001/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
epoger54f1ad82014-07-02 07:43:04 -07008// TODO(djsollen): Rename this whole package (perhaps to "SkMultiDiffer").
9// It's not just for "pdiff" (perceptual diffs)--it's a harness that allows
10// the execution of an arbitrary set of difference algorithms.
11// See http://skbug.com/2711 ('rename skpdiff')
12
zachr@google.comd6585682013-07-17 19:29:19 +000013#if SK_SUPPORT_OPENCL
zachr@google.com35f02fb2013-07-22 17:05:24 +000014
commit-bot@chromium.orgbe19b9e2013-06-14 17:26:54 +000015#define __NO_STD_VECTOR // Uses cl::vectpr instead of std::vectpr
16#define __NO_STD_STRING // Uses cl::STRING_CLASS instead of std::string
tfarina6b87df22014-10-06 10:46:50 -070017#if defined(SK_BUILD_FOR_MAC)
zachr@google.com35f02fb2013-07-22 17:05:24 +000018// Note that some macs don't have this header and it can be downloaded from the Khronos registry
19# include <OpenCL/cl.hpp>
20#else
21# include <CL/cl.hpp>
22#endif
23
zachr@google.comd6585682013-07-17 19:29:19 +000024#endif
commit-bot@chromium.orgbe19b9e2013-06-14 17:26:54 +000025
zachr@google.comdb54dd32013-06-27 17:51:35 +000026#include "SkCommandLineFlags.h"
27#include "SkGraphics.h"
zachr@google.com945708a2013-07-02 19:55:32 +000028#include "SkStream.h"
commit-bot@chromium.orgbe19b9e2013-06-14 17:26:54 +000029#include "SkTDArray.h"
mtklein406654b2014-09-03 15:34:37 -070030#include "SkTaskGroup.h"
commit-bot@chromium.orgbe19b9e2013-06-14 17:26:54 +000031
zachr@google.comd6585682013-07-17 19:29:19 +000032#include "SkDifferentPixelsMetric.h"
zachr@google.com945708a2013-07-02 19:55:32 +000033#include "SkDiffContext.h"
34#include "SkImageDiffer.h"
zachr@google.comc0a75a82013-06-28 15:34:56 +000035#include "SkPMetric.h"
commit-bot@chromium.orgbe19b9e2013-06-14 17:26:54 +000036#include "skpdiff_util.h"
37
zachr@google.comdb54dd32013-06-27 17:51:35 +000038#include "SkForceLinking.h"
39__SK_FORCE_IMAGE_DECODER_LINKING;
40
41// Command line argument definitions go here
42DEFINE_bool2(list, l, false, "List out available differs");
43DEFINE_string2(differs, d, "", "The names of the differs to use or all of them by default");
44DEFINE_string2(folders, f, "", "Compare two folders with identical subfile names: <baseline folder> <test folder>");
45DEFINE_string2(patterns, p, "", "Use two patterns to compare images: <baseline> <test>");
epoger54f1ad82014-07-02 07:43:04 -070046DEFINE_string2(output, o, "", "Writes a JSON summary of these diffs to file: <filepath>");
47DEFINE_string(alphaDir, "", "If the differ can generate an alpha mask, write it into directory: <dirpath>");
48DEFINE_string(rgbDiffDir, "", "If the differ can generate an image showing the RGB diff at each pixel, write it into directory: <dirpath>");
49DEFINE_string(whiteDiffDir, "", "If the differ can generate an image showing every changed pixel in white, write it into directory: <dirpath>");
zachr@google.coma95959c2013-07-08 15:04:45 +000050DEFINE_bool(jsonp, true, "Output JSON with padding");
epoger54f1ad82014-07-02 07:43:04 -070051DEFINE_string(csv, "", "Writes the output of these diffs to a csv file: <filepath>");
djsollen@google.comcbbf1ca2013-10-16 18:36:49 +000052DEFINE_int32(threads, -1, "run N threads in parallel [default is derived from CPUs available]");
stephana21b342d2014-08-13 10:36:06 -070053DEFINE_bool(longnames, false, "Output image names are a combination of baseline and test names");
zachr@google.comdb54dd32013-06-27 17:51:35 +000054
zachr@google.comd6585682013-07-17 19:29:19 +000055#if SK_SUPPORT_OPENCL
commit-bot@chromium.orgbe19b9e2013-06-14 17:26:54 +000056/// A callback for any OpenCL errors
zachr@google.com35f02fb2013-07-22 17:05:24 +000057static void CL_CALLBACK error_notify(const char* errorInfo, const void* privateInfoSize, ::size_t cb, void* userData) {
commit-bot@chromium.orgbe19b9e2013-06-14 17:26:54 +000058 SkDebugf("OpenCL error notify: %s\n", errorInfo);
59 exit(1);
60}
61
62/// Creates a device and context with OpenCL
63static bool init_device_and_context(cl::Device* device, cl::Context* context) {
64 // Query for a platform
65 cl::vector<cl::Platform> platformList;
66 cl::Platform::get(&platformList);
67 SkDebugf("The number of platforms is %u\n", platformList.size());
68
69 // Print some information about the platform for debugging
70 cl::Platform& platform = platformList[0];
71 cl::STRING_CLASS platformName;
72 platform.getInfo(CL_PLATFORM_NAME, &platformName);
73 SkDebugf("Platform index 0 is named %s\n", platformName.c_str());
74
75 // Query for a device
76 cl::vector<cl::Device> deviceList;
zachr@google.com35f02fb2013-07-22 17:05:24 +000077 platform.getDevices(CL_DEVICE_TYPE_ALL, &deviceList);
78 SkDebugf("The number of devices is %u\n", deviceList.size());
commit-bot@chromium.orgbe19b9e2013-06-14 17:26:54 +000079
80 // Print some information about the device for debugging
81 *device = deviceList[0];
82 cl::STRING_CLASS deviceName;
83 device->getInfo(CL_DEVICE_NAME, &deviceName);
84 SkDebugf("Device index 0 is named %s\n", deviceName.c_str());
85
86 // Create a CL context and check for all errors
87 cl_int contextErr = CL_SUCCESS;
88 *context = cl::Context(deviceList, NULL, error_notify, NULL, &contextErr);
89 if (contextErr != CL_SUCCESS) {
90 SkDebugf("Context creation failed: %s\n", cl_error_to_string(contextErr));
91 return false;
92 }
93
94 return true;
95}
96
zachr@google.comc0a75a82013-06-28 15:34:56 +000097static bool init_cl_diff(SkImageDiffer* differ) {
commit-bot@chromium.orgbe19b9e2013-06-14 17:26:54 +000098 // Setup OpenCL
99 cl::Device device;
100 cl::Context context;
101 if (!init_device_and_context(&device, &context)) {
zachr@google.comdb54dd32013-06-27 17:51:35 +0000102 return false;
commit-bot@chromium.orgbe19b9e2013-06-14 17:26:54 +0000103 }
104
105 // Setup our differ of choice
zachr@google.comdb54dd32013-06-27 17:51:35 +0000106 SkCLImageDiffer* clDiffer = (SkCLImageDiffer*)differ;
107 return clDiffer->init(device(), context());
108}
zachr@google.comd6585682013-07-17 19:29:19 +0000109#endif
zachr@google.comc0a75a82013-06-28 15:34:56 +0000110
zachr@google.comdb54dd32013-06-27 17:51:35 +0000111// TODO Find a better home for the diff registry. One possibility is to have the differs self
112// register.
113
114// List here every differ
zachr@google.comd6585682013-07-17 19:29:19 +0000115SkDifferentPixelsMetric gDiffPixel;
zachr@google.comc0a75a82013-06-28 15:34:56 +0000116SkPMetric gPDiff;
zachr@google.comdb54dd32013-06-27 17:51:35 +0000117
zachr@google.comc0a75a82013-06-28 15:34:56 +0000118// A null terminated array of pointer to every differ declared above
119SkImageDiffer* gDiffers[] = { &gDiffPixel, &gPDiff, NULL };
zachr@google.comdb54dd32013-06-27 17:51:35 +0000120
zachr@google.com35f02fb2013-07-22 17:05:24 +0000121int tool_main(int argc, char * argv[]);
122int tool_main(int argc, char * argv[]) {
zachr@google.comdb54dd32013-06-27 17:51:35 +0000123 // Setup command line parsing
124 SkCommandLineFlags::SetUsage("Compare images using various metrics.");
125 SkCommandLineFlags::Parse(argc, argv);
126
127 // Needed by various Skia components
128 SkAutoGraphics ag;
mtklein406654b2014-09-03 15:34:37 -0700129 SkTaskGroup::Enabler enabled;
zachr@google.comdb54dd32013-06-27 17:51:35 +0000130
131 if (FLAGS_list) {
132 SkDebugf("Available Metrics:\n");
commit-bot@chromium.orgbe19b9e2013-06-14 17:26:54 +0000133 }
134
zachr@google.comdb54dd32013-06-27 17:51:35 +0000135 // Figure which differs the user chose, and optionally print them if the user requests it
zachr@google.com945708a2013-07-02 19:55:32 +0000136 SkTDArray<SkImageDiffer*> chosenDiffers;
bsalomon49f085d2014-09-05 13:34:00 -0700137 for (int differIndex = 0; gDiffers[differIndex]; differIndex++) {
zachr@google.com945708a2013-07-02 19:55:32 +0000138 SkImageDiffer* differ = gDiffers[differIndex];
zachr@google.comdb54dd32013-06-27 17:51:35 +0000139 if (FLAGS_list) {
zachr@google.com945708a2013-07-02 19:55:32 +0000140 SkDebugf(" %s", differ->getName());
zachr@google.comdb54dd32013-06-27 17:51:35 +0000141 SkDebugf("\n");
142 }
143
zachr@google.com945708a2013-07-02 19:55:32 +0000144 // Check if this differ was chosen by any of the flags. Initialize them if they were chosen.
zachr@google.comdb54dd32013-06-27 17:51:35 +0000145 if (FLAGS_differs.isEmpty()) {
146 // If no differs were chosen, they all get added
zachr@google.comd6585682013-07-17 19:29:19 +0000147 if (differ->requiresOpenCL()) {
148#if SK_SUPPORT_OPENCL
149 init_cl_diff(differ);
150 chosenDiffers.push(differ);
151#endif
152 } else {
153 chosenDiffers.push(differ);
154 }
zachr@google.comdb54dd32013-06-27 17:51:35 +0000155 } else {
156 for (int flagIndex = 0; flagIndex < FLAGS_differs.count(); flagIndex++) {
zachr@google.com945708a2013-07-02 19:55:32 +0000157 if (SkString(FLAGS_differs[flagIndex]).equals(differ->getName())) {
zachr@google.comd6585682013-07-17 19:29:19 +0000158 // Initialize OpenCL for the differ if it needs it and support was compiled in.
159 if (differ->requiresOpenCL()) {
160#if SK_SUPPORT_OPENCL
161 init_cl_diff(differ);
162 chosenDiffers.push(differ);
163#endif
164 } else {
165 chosenDiffers.push(differ);
166 }
zachr@google.comdb54dd32013-06-27 17:51:35 +0000167 break;
168 }
169 }
170 }
171 }
172
173 // Don't attempt to initialize the differ if we aren't going to use it
174 if (FLAGS_folders.isEmpty() && FLAGS_patterns.isEmpty()) {
175 return 0;
176 }
177
178 // Validate command line flags
179 if (!FLAGS_folders.isEmpty()) {
180 if (2 != FLAGS_folders.count()) {
181 SkDebugf("Folders flag expects two arguments: <baseline folder> <test folder>\n");
182 return 1;
183 }
184 }
185
186 if (!FLAGS_patterns.isEmpty()) {
187 if (2 != FLAGS_patterns.count()) {
188 SkDebugf("Patterns flag expects two arguments: <baseline pattern> <test pattern>\n");
189 return 1;
190 }
191 }
192
edisonn@google.comc93c8ac2013-07-22 15:24:26 +0000193 if (!FLAGS_csv.isEmpty()) {
194 if (1 != FLAGS_csv.count()) {
195 SkDebugf("csv flag expects one argument: <csv file>\n");
196 return 1;
197 }
198 }
199
djsollen@google.com513a7bf2013-11-07 19:24:06 +0000200 if (!FLAGS_alphaDir.isEmpty()) {
201 if (1 != FLAGS_alphaDir.count()) {
202 SkDebugf("alphaDir flag expects one argument: <directory>\n");
203 return 1;
204 }
205 }
epoger54f1ad82014-07-02 07:43:04 -0700206 if (!FLAGS_rgbDiffDir.isEmpty()) {
207 if (1 != FLAGS_rgbDiffDir.count()) {
208 SkDebugf("rgbDiffDir flag expects one argument: <directory>\n");
209 return 1;
210 }
211 }
stephana21b342d2014-08-13 10:36:06 -0700212
epoger54f1ad82014-07-02 07:43:04 -0700213 if (!FLAGS_whiteDiffDir.isEmpty()) {
214 if (1 != FLAGS_whiteDiffDir.count()) {
215 SkDebugf("whiteDiffDir flag expects one argument: <directory>\n");
216 return 1;
217 }
218 }
djsollen@google.com513a7bf2013-11-07 19:24:06 +0000219
zachr@google.com945708a2013-07-02 19:55:32 +0000220 SkDiffContext ctx;
221 ctx.setDiffers(chosenDiffers);
stephana21b342d2014-08-13 10:36:06 -0700222 ctx.setLongNames(FLAGS_longnames);
zachr@google.comdb54dd32013-06-27 17:51:35 +0000223
djsollen@google.com513a7bf2013-11-07 19:24:06 +0000224 if (!FLAGS_alphaDir.isEmpty()) {
epoger54f1ad82014-07-02 07:43:04 -0700225 ctx.setAlphaMaskDir(SkString(FLAGS_alphaDir[0]));
226 }
227 if (!FLAGS_rgbDiffDir.isEmpty()) {
228 ctx.setRgbDiffDir(SkString(FLAGS_rgbDiffDir[0]));
229 }
230 if (!FLAGS_whiteDiffDir.isEmpty()) {
231 ctx.setWhiteDiffDir(SkString(FLAGS_whiteDiffDir[0]));
djsollen@google.com513a7bf2013-11-07 19:24:06 +0000232 }
233
djsollen@google.comcbbf1ca2013-10-16 18:36:49 +0000234 if (FLAGS_threads >= 0) {
235 ctx.setThreadCount(FLAGS_threads);
236 }
237
zachr@google.com945708a2013-07-02 19:55:32 +0000238 // Perform a folder diff if one is requested
239 if (!FLAGS_folders.isEmpty()) {
240 ctx.diffDirectories(FLAGS_folders[0], FLAGS_folders[1]);
241 }
zachr@google.comc0a75a82013-06-28 15:34:56 +0000242
zachr@google.com945708a2013-07-02 19:55:32 +0000243 // Perform a pattern diff if one is requested
244 if (!FLAGS_patterns.isEmpty()) {
245 ctx.diffPatterns(FLAGS_patterns[0], FLAGS_patterns[1]);
246 }
zachr@google.comdb54dd32013-06-27 17:51:35 +0000247
zachr@google.com945708a2013-07-02 19:55:32 +0000248 // Output to the file specified
249 if (!FLAGS_output.isEmpty()) {
250 SkFILEWStream outputStream(FLAGS_output[0]);
zachr@google.coma95959c2013-07-08 15:04:45 +0000251 ctx.outputRecords(outputStream, FLAGS_jsonp);
zachr@google.comdb54dd32013-06-27 17:51:35 +0000252 }
commit-bot@chromium.orgbe19b9e2013-06-14 17:26:54 +0000253
edisonn@google.comc93c8ac2013-07-22 15:24:26 +0000254 if (!FLAGS_csv.isEmpty()) {
255 SkFILEWStream outputStream(FLAGS_csv[0]);
256 ctx.outputCsv(outputStream);
257 }
258
commit-bot@chromium.orgbe19b9e2013-06-14 17:26:54 +0000259 return 0;
260}
zachr@google.com35f02fb2013-07-22 17:05:24 +0000261
262#if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL)
263int main(int argc, char * argv[]) {
264 return tool_main(argc, (char**) argv);
265}
266#endif