blob: 81b1d633341deb13f9486caea592b299268d21a2 [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"
21#include "skpdiff_util.h"
22
zachr@google.comdb54dd32013-06-27 17:51:35 +000023#include "SkForceLinking.h"
24__SK_FORCE_IMAGE_DECODER_LINKING;
25
26// Command line argument definitions go here
27DEFINE_bool2(list, l, false, "List out available differs");
28DEFINE_string2(differs, d, "", "The names of the differs to use or all of them by default");
29DEFINE_string2(folders, f, "", "Compare two folders with identical subfile names: <baseline folder> <test folder>");
30DEFINE_string2(patterns, p, "", "Use two patterns to compare images: <baseline> <test>");
31
commit-bot@chromium.orgbe19b9e2013-06-14 17:26:54 +000032/// A callback for any OpenCL errors
33CL_CALLBACK void error_notify(const char* errorInfo, const void* privateInfoSize, ::size_t cb, void* userData) {
34 SkDebugf("OpenCL error notify: %s\n", errorInfo);
35 exit(1);
36}
37
38/// Creates a device and context with OpenCL
39static bool init_device_and_context(cl::Device* device, cl::Context* context) {
40 // Query for a platform
41 cl::vector<cl::Platform> platformList;
42 cl::Platform::get(&platformList);
43 SkDebugf("The number of platforms is %u\n", platformList.size());
44
45 // Print some information about the platform for debugging
46 cl::Platform& platform = platformList[0];
47 cl::STRING_CLASS platformName;
48 platform.getInfo(CL_PLATFORM_NAME, &platformName);
49 SkDebugf("Platform index 0 is named %s\n", platformName.c_str());
50
51 // Query for a device
52 cl::vector<cl::Device> deviceList;
53 platform.getDevices(CL_DEVICE_TYPE_GPU, &deviceList);
54 SkDebugf("The number of GPU devices is %u\n", deviceList.size());
55
56 // Print some information about the device for debugging
57 *device = deviceList[0];
58 cl::STRING_CLASS deviceName;
59 device->getInfo(CL_DEVICE_NAME, &deviceName);
60 SkDebugf("Device index 0 is named %s\n", deviceName.c_str());
61
62 // Create a CL context and check for all errors
63 cl_int contextErr = CL_SUCCESS;
64 *context = cl::Context(deviceList, NULL, error_notify, NULL, &contextErr);
65 if (contextErr != CL_SUCCESS) {
66 SkDebugf("Context creation failed: %s\n", cl_error_to_string(contextErr));
67 return false;
68 }
69
70 return true;
71}
72
73/// Compares two directories of images with the given differ
74static void diff_directories(const char baselinePath[], const char testPath[], SkImageDiffer* differ) {
75 // Get the files in the baseline, we will then look for those inside the test path
76 SkTArray<SkString> baselineEntries;
77 if (!get_directory(baselinePath, &baselineEntries)) {
78 SkDebugf("Unable to open path \"%s\"\n", baselinePath);
79 return;
80 }
81
82 SkTDArray<int> queuedDiffIDs;
83 for (int baselineIndex = 0; baselineIndex < baselineEntries.count(); baselineIndex++) {
84 const char* baseFilename = baselineEntries[baselineIndex].c_str();
85 SkDebugf("%s\n", baseFilename);
86
87 // Find the real location of each file to compare
88 SkString baselineFile = SkOSPath::SkPathJoin(baselinePath, baseFilename);
89 SkString testFile = SkOSPath::SkPathJoin(testPath, baseFilename);
90
91 // Check that the test file exists and is a file
92 if (sk_exists(testFile.c_str()) && !sk_isdir(testFile.c_str())) {
93 // Queue up the comparison with the differ
94 int diffID = differ->queueDiffOfFile(baselineFile.c_str(), testFile.c_str());
95 if (diffID >= 0) {
96 queuedDiffIDs.push(diffID);
97 SkDebugf("Result: %f\n", differ->getResult(diffID));
98 }
99 } else {
100 SkDebugf("Baseline file \"%s\" has no corresponding test file\n", baselineFile.c_str());
101 }
102 }
103}
104
commit-bot@chromium.orgbe19b9e2013-06-14 17:26:54 +0000105
zachr@google.comdb54dd32013-06-27 17:51:35 +0000106/// Compares two sets of images identified by glob style patterns with the given differ
107static void diff_patterns(const char baselinePattern[], const char testPattern[], SkImageDiffer* differ) {
108 // Get the files in the baseline and test patterns. Because they are in sorted order, it's easy
109 // to find corresponding images by matching entry indices.
110 //
111 SkTArray<SkString> baselineEntries;
112 if (!glob_files(baselinePattern, &baselineEntries)) {
113 SkDebugf("Unable to get pattern \"%s\"\n", baselinePattern);
114 return;
commit-bot@chromium.orgbe19b9e2013-06-14 17:26:54 +0000115 }
116
zachr@google.comdb54dd32013-06-27 17:51:35 +0000117 SkTArray<SkString> testEntries;
118 if (!glob_files(testPattern, &testEntries)) {
119 SkDebugf("Unable to get pattern \"%s\"\n", testPattern);
120 return;
121 }
122
123 if (baselineEntries.count() != testEntries.count()) {
124 SkDebugf("Baseline and test patterns do not yield corresponding number of files\n");
125 return;
126 }
127
128 SkTDArray<int> queuedDiffIDs;
129 for (int entryIndex = 0; entryIndex < baselineEntries.count(); entryIndex++) {
130 const char* baselineFilename = baselineEntries[entryIndex].c_str();
131 const char* testFilename = testEntries [entryIndex].c_str();
132 SkDebugf("%s %s\n", baselineFilename, testFilename);
133
134 int diffID = differ->queueDiffOfFile(baselineFilename, testFilename);
135 if (diffID >= 0) {
136 queuedDiffIDs.push(diffID);
137 SkDebugf("Result: %f\n", differ->getResult(diffID));
138 }
139 }
140}
141
142
143static bool init_cl_diff(SkImageDiffer* differ)
144{
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
157// TODO Find a better home for the diff registry. One possibility is to have the differs self
158// register.
159
160// List here every differ
161SkDifferentPixelsImageDiffer gDiffPixel;
162
163/// A null terminated array of pointer to every differ declared above
164SkImageDiffer* gDiffers[] = { &gDiffPixel, NULL };
165
166/// A parallel array of functions to initialize the above differs
167bool (*gDiffInits[])(SkImageDiffer*) = { init_cl_diff, NULL };
168
169
170int main(int argc, char** argv) {
171 // Setup command line parsing
172 SkCommandLineFlags::SetUsage("Compare images using various metrics.");
173 SkCommandLineFlags::Parse(argc, argv);
174
175 // Needed by various Skia components
176 SkAutoGraphics ag;
177
178 if (FLAGS_list) {
179 SkDebugf("Available Metrics:\n");
commit-bot@chromium.orgbe19b9e2013-06-14 17:26:54 +0000180 }
181
zachr@google.comdb54dd32013-06-27 17:51:35 +0000182 // Figure which differs the user chose, and optionally print them if the user requests it
183 SkTDArray<int> chosenDiffers;
184 for (int differIndex = 0; NULL != gDiffers[differIndex]; differIndex++) {
185 if (FLAGS_list) {
186 SkDebugf(" %s", gDiffers[differIndex]->getName());
187 SkDebugf("\n");
188 }
189
190 // Check if this differ was chosen by any of the flags
191 if (FLAGS_differs.isEmpty()) {
192 // If no differs were chosen, they all get added
193 chosenDiffers.push(differIndex);
194 } else {
195 for (int flagIndex = 0; flagIndex < FLAGS_differs.count(); flagIndex++) {
196 if (SkString(FLAGS_differs[flagIndex]).equals(gDiffers[differIndex]->getName())) {
197 chosenDiffers.push(differIndex);
198 break;
199 }
200 }
201 }
202 }
203
204 // Don't attempt to initialize the differ if we aren't going to use it
205 if (FLAGS_folders.isEmpty() && FLAGS_patterns.isEmpty()) {
206 return 0;
207 }
208
209 // Validate command line flags
210 if (!FLAGS_folders.isEmpty()) {
211 if (2 != FLAGS_folders.count()) {
212 SkDebugf("Folders flag expects two arguments: <baseline folder> <test folder>\n");
213 return 1;
214 }
215 }
216
217 if (!FLAGS_patterns.isEmpty()) {
218 if (2 != FLAGS_patterns.count()) {
219 SkDebugf("Patterns flag expects two arguments: <baseline pattern> <test pattern>\n");
220 return 1;
221 }
222 }
223
224 // TODO Move the differ loop to after the bitmaps are decoded and/or uploaded to the OpenCL
225 // device. Those are often the slowest processes and should not be done more than once if it can
226 // be helped.
227
228 // Perform each requested diff
229 for (int differIndex = 0; differIndex < chosenDiffers.count(); differIndex++) {
230 // Get the chosen differ and say which one they chose
231 SkImageDiffer * differ = gDiffers[differIndex];
232 SkDebugf("Using differ \"%s\"\n", differ->getName());
233
234 // Initialize the differ using the global list of init functions that match the list of
235 // differs
236 gDiffInits[differIndex](differ);
237
238 // Perform a folder diff if one is requested
239 if (!FLAGS_folders.isEmpty()) {
240 diff_directories(FLAGS_folders[0], FLAGS_folders[1], differ);
241 }
242
243 // Perform a pattern diff if one is requested
244 if (!FLAGS_patterns.isEmpty()) {
245 diff_patterns(FLAGS_patterns[0], FLAGS_patterns[1], differ);
246 }
247 }
commit-bot@chromium.orgbe19b9e2013-06-14 17:26:54 +0000248
249 return 0;
250}