blob: e5b8faea3aa3db30b3ccd554311179f333ab8cce [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
8#define __NO_STD_VECTOR // Uses cl::vectpr instead of std::vectpr
9#define __NO_STD_STRING // Uses cl::STRING_CLASS instead of std::string
10#include <CL/cl.hpp>
11
zachr@google.comdb54dd32013-06-27 17:51:35 +000012#include "SkCommandLineFlags.h"
13#include "SkGraphics.h"
commit-bot@chromium.orgbe19b9e2013-06-14 17:26:54 +000014#include "SkOSFile.h"
commit-bot@chromium.orgbe19b9e2013-06-14 17:26:54 +000015#include "SkString.h"
16#include "SkTArray.h"
17#include "SkTDArray.h"
18
19#include "SkImageDiffer.h"
20#include "SkCLImageDiffer.h"
zachr@google.comc0a75a82013-06-28 15:34:56 +000021#include "SkPMetric.h"
commit-bot@chromium.orgbe19b9e2013-06-14 17:26:54 +000022#include "skpdiff_util.h"
23
zachr@google.comdb54dd32013-06-27 17:51:35 +000024#include "SkForceLinking.h"
25__SK_FORCE_IMAGE_DECODER_LINKING;
26
27// Command line argument definitions go here
28DEFINE_bool2(list, l, false, "List out available differs");
29DEFINE_string2(differs, d, "", "The names of the differs to use or all of them by default");
30DEFINE_string2(folders, f, "", "Compare two folders with identical subfile names: <baseline folder> <test folder>");
31DEFINE_string2(patterns, p, "", "Use two patterns to compare images: <baseline> <test>");
32
commit-bot@chromium.orgbe19b9e2013-06-14 17:26:54 +000033/// A callback for any OpenCL errors
34CL_CALLBACK void error_notify(const char* errorInfo, const void* privateInfoSize, ::size_t cb, void* userData) {
35 SkDebugf("OpenCL error notify: %s\n", errorInfo);
36 exit(1);
37}
38
39/// Creates a device and context with OpenCL
40static bool init_device_and_context(cl::Device* device, cl::Context* context) {
41 // Query for a platform
42 cl::vector<cl::Platform> platformList;
43 cl::Platform::get(&platformList);
44 SkDebugf("The number of platforms is %u\n", platformList.size());
45
46 // Print some information about the platform for debugging
47 cl::Platform& platform = platformList[0];
48 cl::STRING_CLASS platformName;
49 platform.getInfo(CL_PLATFORM_NAME, &platformName);
50 SkDebugf("Platform index 0 is named %s\n", platformName.c_str());
51
52 // Query for a device
53 cl::vector<cl::Device> deviceList;
54 platform.getDevices(CL_DEVICE_TYPE_GPU, &deviceList);
55 SkDebugf("The number of GPU devices is %u\n", deviceList.size());
56
57 // Print some information about the device for debugging
58 *device = deviceList[0];
59 cl::STRING_CLASS deviceName;
60 device->getInfo(CL_DEVICE_NAME, &deviceName);
61 SkDebugf("Device index 0 is named %s\n", deviceName.c_str());
62
63 // Create a CL context and check for all errors
64 cl_int contextErr = CL_SUCCESS;
65 *context = cl::Context(deviceList, NULL, error_notify, NULL, &contextErr);
66 if (contextErr != CL_SUCCESS) {
67 SkDebugf("Context creation failed: %s\n", cl_error_to_string(contextErr));
68 return false;
69 }
70
71 return true;
72}
73
74/// Compares two directories of images with the given differ
75static void diff_directories(const char baselinePath[], const char testPath[], SkImageDiffer* differ) {
76 // Get the files in the baseline, we will then look for those inside the test path
77 SkTArray<SkString> baselineEntries;
78 if (!get_directory(baselinePath, &baselineEntries)) {
79 SkDebugf("Unable to open path \"%s\"\n", baselinePath);
80 return;
81 }
82
83 SkTDArray<int> queuedDiffIDs;
84 for (int baselineIndex = 0; baselineIndex < baselineEntries.count(); baselineIndex++) {
85 const char* baseFilename = baselineEntries[baselineIndex].c_str();
86 SkDebugf("%s\n", baseFilename);
87
88 // Find the real location of each file to compare
89 SkString baselineFile = SkOSPath::SkPathJoin(baselinePath, baseFilename);
90 SkString testFile = SkOSPath::SkPathJoin(testPath, baseFilename);
91
92 // Check that the test file exists and is a file
93 if (sk_exists(testFile.c_str()) && !sk_isdir(testFile.c_str())) {
94 // Queue up the comparison with the differ
95 int diffID = differ->queueDiffOfFile(baselineFile.c_str(), testFile.c_str());
96 if (diffID >= 0) {
97 queuedDiffIDs.push(diffID);
98 SkDebugf("Result: %f\n", differ->getResult(diffID));
99 }
100 } else {
101 SkDebugf("Baseline file \"%s\" has no corresponding test file\n", baselineFile.c_str());
102 }
103 }
104}
105
commit-bot@chromium.orgbe19b9e2013-06-14 17:26:54 +0000106
zachr@google.comdb54dd32013-06-27 17:51:35 +0000107/// Compares two sets of images identified by glob style patterns with the given differ
108static void diff_patterns(const char baselinePattern[], const char testPattern[], SkImageDiffer* differ) {
109 // Get the files in the baseline and test patterns. Because they are in sorted order, it's easy
110 // to find corresponding images by matching entry indices.
zachr@google.comc0a75a82013-06-28 15:34:56 +0000111
zachr@google.comdb54dd32013-06-27 17:51:35 +0000112 SkTArray<SkString> baselineEntries;
113 if (!glob_files(baselinePattern, &baselineEntries)) {
114 SkDebugf("Unable to get pattern \"%s\"\n", baselinePattern);
115 return;
commit-bot@chromium.orgbe19b9e2013-06-14 17:26:54 +0000116 }
117
zachr@google.comdb54dd32013-06-27 17:51:35 +0000118 SkTArray<SkString> testEntries;
119 if (!glob_files(testPattern, &testEntries)) {
120 SkDebugf("Unable to get pattern \"%s\"\n", testPattern);
121 return;
122 }
123
124 if (baselineEntries.count() != testEntries.count()) {
125 SkDebugf("Baseline and test patterns do not yield corresponding number of files\n");
126 return;
127 }
128
129 SkTDArray<int> queuedDiffIDs;
130 for (int entryIndex = 0; entryIndex < baselineEntries.count(); entryIndex++) {
131 const char* baselineFilename = baselineEntries[entryIndex].c_str();
132 const char* testFilename = testEntries [entryIndex].c_str();
133 SkDebugf("%s %s\n", baselineFilename, testFilename);
134
135 int diffID = differ->queueDiffOfFile(baselineFilename, testFilename);
136 if (diffID >= 0) {
137 queuedDiffIDs.push(diffID);
138 SkDebugf("Result: %f\n", differ->getResult(diffID));
139 }
140 }
141}
142
143
zachr@google.comc0a75a82013-06-28 15:34:56 +0000144static bool init_cl_diff(SkImageDiffer* differ) {
commit-bot@chromium.orgbe19b9e2013-06-14 17:26:54 +0000145 // Setup OpenCL
146 cl::Device device;
147 cl::Context context;
148 if (!init_device_and_context(&device, &context)) {
zachr@google.comdb54dd32013-06-27 17:51:35 +0000149 return false;
commit-bot@chromium.orgbe19b9e2013-06-14 17:26:54 +0000150 }
151
152 // Setup our differ of choice
zachr@google.comdb54dd32013-06-27 17:51:35 +0000153 SkCLImageDiffer* clDiffer = (SkCLImageDiffer*)differ;
154 return clDiffer->init(device(), context());
155}
156
zachr@google.comc0a75a82013-06-28 15:34:56 +0000157static bool init_dummy(SkImageDiffer* differ) {
158 return true;
159}
160
161
zachr@google.comdb54dd32013-06-27 17:51:35 +0000162// TODO Find a better home for the diff registry. One possibility is to have the differs self
163// register.
164
165// List here every differ
166SkDifferentPixelsImageDiffer gDiffPixel;
zachr@google.comc0a75a82013-06-28 15:34:56 +0000167SkPMetric gPDiff;
zachr@google.comdb54dd32013-06-27 17:51:35 +0000168
zachr@google.comc0a75a82013-06-28 15:34:56 +0000169// A null terminated array of pointer to every differ declared above
170SkImageDiffer* gDiffers[] = { &gDiffPixel, &gPDiff, NULL };
zachr@google.comdb54dd32013-06-27 17:51:35 +0000171
zachr@google.comc0a75a82013-06-28 15:34:56 +0000172// A parallel array of functions to initialize the above differs. The reason we don't initialize
173// everything immediately is that certain differs may require special initialization, but we still
174// want to construct all of them globally so they can be queried for things like their name and
175// description.
176bool (*gDiffInits[])(SkImageDiffer*) = { init_cl_diff, init_dummy, NULL };
zachr@google.comdb54dd32013-06-27 17:51:35 +0000177
178
179int main(int argc, char** argv) {
180 // Setup command line parsing
181 SkCommandLineFlags::SetUsage("Compare images using various metrics.");
182 SkCommandLineFlags::Parse(argc, argv);
183
184 // Needed by various Skia components
185 SkAutoGraphics ag;
186
187 if (FLAGS_list) {
188 SkDebugf("Available Metrics:\n");
commit-bot@chromium.orgbe19b9e2013-06-14 17:26:54 +0000189 }
190
zachr@google.comdb54dd32013-06-27 17:51:35 +0000191 // Figure which differs the user chose, and optionally print them if the user requests it
192 SkTDArray<int> chosenDiffers;
193 for (int differIndex = 0; NULL != gDiffers[differIndex]; differIndex++) {
194 if (FLAGS_list) {
195 SkDebugf(" %s", gDiffers[differIndex]->getName());
196 SkDebugf("\n");
197 }
198
199 // Check if this differ was chosen by any of the flags
200 if (FLAGS_differs.isEmpty()) {
201 // If no differs were chosen, they all get added
202 chosenDiffers.push(differIndex);
203 } else {
204 for (int flagIndex = 0; flagIndex < FLAGS_differs.count(); flagIndex++) {
205 if (SkString(FLAGS_differs[flagIndex]).equals(gDiffers[differIndex]->getName())) {
206 chosenDiffers.push(differIndex);
207 break;
208 }
209 }
210 }
211 }
212
213 // Don't attempt to initialize the differ if we aren't going to use it
214 if (FLAGS_folders.isEmpty() && FLAGS_patterns.isEmpty()) {
215 return 0;
216 }
217
218 // Validate command line flags
219 if (!FLAGS_folders.isEmpty()) {
220 if (2 != FLAGS_folders.count()) {
221 SkDebugf("Folders flag expects two arguments: <baseline folder> <test folder>\n");
222 return 1;
223 }
224 }
225
226 if (!FLAGS_patterns.isEmpty()) {
227 if (2 != FLAGS_patterns.count()) {
228 SkDebugf("Patterns flag expects two arguments: <baseline pattern> <test pattern>\n");
229 return 1;
230 }
231 }
232
233 // TODO Move the differ loop to after the bitmaps are decoded and/or uploaded to the OpenCL
234 // device. Those are often the slowest processes and should not be done more than once if it can
235 // be helped.
236
237 // Perform each requested diff
zachr@google.comc0a75a82013-06-28 15:34:56 +0000238 for (int chosenDifferIndex = 0; chosenDifferIndex < chosenDiffers.count(); chosenDifferIndex++) {
239 int differIndex = chosenDiffers[chosenDifferIndex];
240
zachr@google.comdb54dd32013-06-27 17:51:35 +0000241 // Get the chosen differ and say which one they chose
242 SkImageDiffer * differ = gDiffers[differIndex];
zachr@google.comc0a75a82013-06-28 15:34:56 +0000243 SkDebugf("Using metric \"%s\"\n", differ->getName());
zachr@google.comdb54dd32013-06-27 17:51:35 +0000244
245 // Initialize the differ using the global list of init functions that match the list of
246 // differs
247 gDiffInits[differIndex](differ);
248
249 // Perform a folder diff if one is requested
250 if (!FLAGS_folders.isEmpty()) {
251 diff_directories(FLAGS_folders[0], FLAGS_folders[1], differ);
252 }
253
254 // Perform a pattern diff if one is requested
255 if (!FLAGS_patterns.isEmpty()) {
256 diff_patterns(FLAGS_patterns[0], FLAGS_patterns[1], differ);
257 }
258 }
commit-bot@chromium.orgbe19b9e2013-06-14 17:26:54 +0000259
260 return 0;
261}