2013-06-14 17:26:54 +00:00
|
|
|
/*
|
|
|
|
* Copyright 2013 Google Inc.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
|
|
* found in the LICENSE file.
|
|
|
|
*/
|
|
|
|
|
2014-07-02 14:43:04 +00:00
|
|
|
// TODO(djsollen): Rename this whole package (perhaps to "SkMultiDiffer").
|
|
|
|
// It's not just for "pdiff" (perceptual diffs)--it's a harness that allows
|
|
|
|
// the execution of an arbitrary set of difference algorithms.
|
|
|
|
// See http://skbug.com/2711 ('rename skpdiff')
|
|
|
|
|
2013-07-17 19:29:19 +00:00
|
|
|
#if SK_SUPPORT_OPENCL
|
2013-07-22 17:05:24 +00:00
|
|
|
|
2013-06-14 17:26:54 +00:00
|
|
|
#define __NO_STD_VECTOR // Uses cl::vectpr instead of std::vectpr
|
|
|
|
#define __NO_STD_STRING // Uses cl::STRING_CLASS instead of std::string
|
2013-07-22 17:05:24 +00:00
|
|
|
#if SK_BUILD_FOR_MAC
|
|
|
|
// Note that some macs don't have this header and it can be downloaded from the Khronos registry
|
|
|
|
# include <OpenCL/cl.hpp>
|
|
|
|
#else
|
|
|
|
# include <CL/cl.hpp>
|
|
|
|
#endif
|
|
|
|
|
2013-07-17 19:29:19 +00:00
|
|
|
#endif
|
2013-06-14 17:26:54 +00:00
|
|
|
|
2013-06-27 17:51:35 +00:00
|
|
|
#include "SkCommandLineFlags.h"
|
|
|
|
#include "SkGraphics.h"
|
2013-07-02 19:55:32 +00:00
|
|
|
#include "SkStream.h"
|
2013-06-14 17:26:54 +00:00
|
|
|
#include "SkTDArray.h"
|
SkThreadPool ~~> SkTaskGroup
SkTaskGroup is like SkThreadPool except the threads stay in
one global pool. Each SkTaskGroup itself is tiny (4 bytes)
and its wait() method applies only to tasks add()ed to that
instance, not the whole thread pool.
This means we don't need to bring up new thread pools when
tests themselves want to use multithreading (e.g. pathops,
quilt). We just create a new SkTaskGroup and wait for that
to complete. This should be more efficient, and allow us
to expand where we use threads to really latency sensitive
places. E.g. we can probably now use these in nanobench
for CPU .skp rendering.
Now that all threads are sharing the same pool, I think we
can remove most of the custom mechanism pathops tests use
to control threading. They'll just ride on the global pool
with all other tests now.
This (temporarily?) removes the GPU multithreading feature
from DM, which we don't use.
On my desktop, DM runs a little faster (57s -> 55s) in
Debug, and a lot faster in Release (36s -> 24s). The bots
show speedups of similar proportions, cutting more than a
minute off the N4/Release and Win7/Debug runtimes.
BUG=skia:
Committed: https://skia.googlesource.com/skia/+/9c7207b5dc71dc5a96a2eb107d401133333d5b6f
R=caryclark@google.com, bsalomon@google.com, bungeman@google.com, mtklein@google.com, reed@google.com
Author: mtklein@chromium.org
Review URL: https://codereview.chromium.org/531653002
2014-09-03 22:34:37 +00:00
|
|
|
#include "SkTaskGroup.h"
|
2013-06-14 17:26:54 +00:00
|
|
|
|
2013-07-17 19:29:19 +00:00
|
|
|
#include "SkDifferentPixelsMetric.h"
|
2013-07-02 19:55:32 +00:00
|
|
|
#include "SkDiffContext.h"
|
|
|
|
#include "SkImageDiffer.h"
|
2013-06-28 15:34:56 +00:00
|
|
|
#include "SkPMetric.h"
|
2013-06-14 17:26:54 +00:00
|
|
|
#include "skpdiff_util.h"
|
|
|
|
|
2013-06-27 17:51:35 +00:00
|
|
|
#include "SkForceLinking.h"
|
|
|
|
__SK_FORCE_IMAGE_DECODER_LINKING;
|
|
|
|
|
|
|
|
// Command line argument definitions go here
|
|
|
|
DEFINE_bool2(list, l, false, "List out available differs");
|
|
|
|
DEFINE_string2(differs, d, "", "The names of the differs to use or all of them by default");
|
|
|
|
DEFINE_string2(folders, f, "", "Compare two folders with identical subfile names: <baseline folder> <test folder>");
|
|
|
|
DEFINE_string2(patterns, p, "", "Use two patterns to compare images: <baseline> <test>");
|
2014-07-02 14:43:04 +00:00
|
|
|
DEFINE_string2(output, o, "", "Writes a JSON summary of these diffs to file: <filepath>");
|
|
|
|
DEFINE_string(alphaDir, "", "If the differ can generate an alpha mask, write it into directory: <dirpath>");
|
|
|
|
DEFINE_string(rgbDiffDir, "", "If the differ can generate an image showing the RGB diff at each pixel, write it into directory: <dirpath>");
|
|
|
|
DEFINE_string(whiteDiffDir, "", "If the differ can generate an image showing every changed pixel in white, write it into directory: <dirpath>");
|
2013-07-08 15:04:45 +00:00
|
|
|
DEFINE_bool(jsonp, true, "Output JSON with padding");
|
2014-07-02 14:43:04 +00:00
|
|
|
DEFINE_string(csv, "", "Writes the output of these diffs to a csv file: <filepath>");
|
2013-10-16 18:36:49 +00:00
|
|
|
DEFINE_int32(threads, -1, "run N threads in parallel [default is derived from CPUs available]");
|
2014-08-13 17:36:06 +00:00
|
|
|
DEFINE_bool(longnames, false, "Output image names are a combination of baseline and test names");
|
2013-06-27 17:51:35 +00:00
|
|
|
|
2013-07-17 19:29:19 +00:00
|
|
|
#if SK_SUPPORT_OPENCL
|
2013-06-14 17:26:54 +00:00
|
|
|
/// A callback for any OpenCL errors
|
2013-07-22 17:05:24 +00:00
|
|
|
static void CL_CALLBACK error_notify(const char* errorInfo, const void* privateInfoSize, ::size_t cb, void* userData) {
|
2013-06-14 17:26:54 +00:00
|
|
|
SkDebugf("OpenCL error notify: %s\n", errorInfo);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Creates a device and context with OpenCL
|
|
|
|
static bool init_device_and_context(cl::Device* device, cl::Context* context) {
|
|
|
|
// Query for a platform
|
|
|
|
cl::vector<cl::Platform> platformList;
|
|
|
|
cl::Platform::get(&platformList);
|
|
|
|
SkDebugf("The number of platforms is %u\n", platformList.size());
|
|
|
|
|
|
|
|
// Print some information about the platform for debugging
|
|
|
|
cl::Platform& platform = platformList[0];
|
|
|
|
cl::STRING_CLASS platformName;
|
|
|
|
platform.getInfo(CL_PLATFORM_NAME, &platformName);
|
|
|
|
SkDebugf("Platform index 0 is named %s\n", platformName.c_str());
|
|
|
|
|
|
|
|
// Query for a device
|
|
|
|
cl::vector<cl::Device> deviceList;
|
2013-07-22 17:05:24 +00:00
|
|
|
platform.getDevices(CL_DEVICE_TYPE_ALL, &deviceList);
|
|
|
|
SkDebugf("The number of devices is %u\n", deviceList.size());
|
2013-06-14 17:26:54 +00:00
|
|
|
|
|
|
|
// Print some information about the device for debugging
|
|
|
|
*device = deviceList[0];
|
|
|
|
cl::STRING_CLASS deviceName;
|
|
|
|
device->getInfo(CL_DEVICE_NAME, &deviceName);
|
|
|
|
SkDebugf("Device index 0 is named %s\n", deviceName.c_str());
|
|
|
|
|
|
|
|
// Create a CL context and check for all errors
|
|
|
|
cl_int contextErr = CL_SUCCESS;
|
|
|
|
*context = cl::Context(deviceList, NULL, error_notify, NULL, &contextErr);
|
|
|
|
if (contextErr != CL_SUCCESS) {
|
|
|
|
SkDebugf("Context creation failed: %s\n", cl_error_to_string(contextErr));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-06-28 15:34:56 +00:00
|
|
|
static bool init_cl_diff(SkImageDiffer* differ) {
|
2013-06-14 17:26:54 +00:00
|
|
|
// Setup OpenCL
|
|
|
|
cl::Device device;
|
|
|
|
cl::Context context;
|
|
|
|
if (!init_device_and_context(&device, &context)) {
|
2013-06-27 17:51:35 +00:00
|
|
|
return false;
|
2013-06-14 17:26:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Setup our differ of choice
|
2013-06-27 17:51:35 +00:00
|
|
|
SkCLImageDiffer* clDiffer = (SkCLImageDiffer*)differ;
|
|
|
|
return clDiffer->init(device(), context());
|
|
|
|
}
|
2013-07-17 19:29:19 +00:00
|
|
|
#endif
|
2013-06-28 15:34:56 +00:00
|
|
|
|
2013-06-27 17:51:35 +00:00
|
|
|
// TODO Find a better home for the diff registry. One possibility is to have the differs self
|
|
|
|
// register.
|
|
|
|
|
|
|
|
// List here every differ
|
2013-07-17 19:29:19 +00:00
|
|
|
SkDifferentPixelsMetric gDiffPixel;
|
2013-06-28 15:34:56 +00:00
|
|
|
SkPMetric gPDiff;
|
2013-06-27 17:51:35 +00:00
|
|
|
|
2013-06-28 15:34:56 +00:00
|
|
|
// A null terminated array of pointer to every differ declared above
|
|
|
|
SkImageDiffer* gDiffers[] = { &gDiffPixel, &gPDiff, NULL };
|
2013-06-27 17:51:35 +00:00
|
|
|
|
2013-07-22 17:05:24 +00:00
|
|
|
int tool_main(int argc, char * argv[]);
|
|
|
|
int tool_main(int argc, char * argv[]) {
|
2013-06-27 17:51:35 +00:00
|
|
|
// Setup command line parsing
|
|
|
|
SkCommandLineFlags::SetUsage("Compare images using various metrics.");
|
|
|
|
SkCommandLineFlags::Parse(argc, argv);
|
|
|
|
|
|
|
|
// Needed by various Skia components
|
|
|
|
SkAutoGraphics ag;
|
SkThreadPool ~~> SkTaskGroup
SkTaskGroup is like SkThreadPool except the threads stay in
one global pool. Each SkTaskGroup itself is tiny (4 bytes)
and its wait() method applies only to tasks add()ed to that
instance, not the whole thread pool.
This means we don't need to bring up new thread pools when
tests themselves want to use multithreading (e.g. pathops,
quilt). We just create a new SkTaskGroup and wait for that
to complete. This should be more efficient, and allow us
to expand where we use threads to really latency sensitive
places. E.g. we can probably now use these in nanobench
for CPU .skp rendering.
Now that all threads are sharing the same pool, I think we
can remove most of the custom mechanism pathops tests use
to control threading. They'll just ride on the global pool
with all other tests now.
This (temporarily?) removes the GPU multithreading feature
from DM, which we don't use.
On my desktop, DM runs a little faster (57s -> 55s) in
Debug, and a lot faster in Release (36s -> 24s). The bots
show speedups of similar proportions, cutting more than a
minute off the N4/Release and Win7/Debug runtimes.
BUG=skia:
Committed: https://skia.googlesource.com/skia/+/9c7207b5dc71dc5a96a2eb107d401133333d5b6f
R=caryclark@google.com, bsalomon@google.com, bungeman@google.com, mtklein@google.com, reed@google.com
Author: mtklein@chromium.org
Review URL: https://codereview.chromium.org/531653002
2014-09-03 22:34:37 +00:00
|
|
|
SkTaskGroup::Enabler enabled;
|
2013-06-27 17:51:35 +00:00
|
|
|
|
|
|
|
if (FLAGS_list) {
|
|
|
|
SkDebugf("Available Metrics:\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Figure which differs the user chose, and optionally print them if the user requests it
|
2013-07-02 19:55:32 +00:00
|
|
|
SkTDArray<SkImageDiffer*> chosenDiffers;
|
2014-09-05 20:34:00 +00:00
|
|
|
for (int differIndex = 0; gDiffers[differIndex]; differIndex++) {
|
2013-07-02 19:55:32 +00:00
|
|
|
SkImageDiffer* differ = gDiffers[differIndex];
|
2013-06-27 17:51:35 +00:00
|
|
|
if (FLAGS_list) {
|
2013-07-02 19:55:32 +00:00
|
|
|
SkDebugf(" %s", differ->getName());
|
2013-06-27 17:51:35 +00:00
|
|
|
SkDebugf("\n");
|
|
|
|
}
|
|
|
|
|
2013-07-02 19:55:32 +00:00
|
|
|
// Check if this differ was chosen by any of the flags. Initialize them if they were chosen.
|
2013-06-27 17:51:35 +00:00
|
|
|
if (FLAGS_differs.isEmpty()) {
|
|
|
|
// If no differs were chosen, they all get added
|
2013-07-17 19:29:19 +00:00
|
|
|
if (differ->requiresOpenCL()) {
|
|
|
|
#if SK_SUPPORT_OPENCL
|
|
|
|
init_cl_diff(differ);
|
|
|
|
chosenDiffers.push(differ);
|
|
|
|
#endif
|
|
|
|
} else {
|
|
|
|
chosenDiffers.push(differ);
|
|
|
|
}
|
2013-06-27 17:51:35 +00:00
|
|
|
} else {
|
|
|
|
for (int flagIndex = 0; flagIndex < FLAGS_differs.count(); flagIndex++) {
|
2013-07-02 19:55:32 +00:00
|
|
|
if (SkString(FLAGS_differs[flagIndex]).equals(differ->getName())) {
|
2013-07-17 19:29:19 +00:00
|
|
|
// Initialize OpenCL for the differ if it needs it and support was compiled in.
|
|
|
|
if (differ->requiresOpenCL()) {
|
|
|
|
#if SK_SUPPORT_OPENCL
|
|
|
|
init_cl_diff(differ);
|
|
|
|
chosenDiffers.push(differ);
|
|
|
|
#endif
|
|
|
|
} else {
|
|
|
|
chosenDiffers.push(differ);
|
|
|
|
}
|
2013-06-27 17:51:35 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Don't attempt to initialize the differ if we aren't going to use it
|
|
|
|
if (FLAGS_folders.isEmpty() && FLAGS_patterns.isEmpty()) {
|
|
|
|
return 0;
|
2013-06-14 17:26:54 +00:00
|
|
|
}
|
|
|
|
|
2013-06-27 17:51:35 +00:00
|
|
|
// Validate command line flags
|
|
|
|
if (!FLAGS_folders.isEmpty()) {
|
|
|
|
if (2 != FLAGS_folders.count()) {
|
|
|
|
SkDebugf("Folders flag expects two arguments: <baseline folder> <test folder>\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!FLAGS_patterns.isEmpty()) {
|
|
|
|
if (2 != FLAGS_patterns.count()) {
|
|
|
|
SkDebugf("Patterns flag expects two arguments: <baseline pattern> <test pattern>\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-22 15:24:26 +00:00
|
|
|
if (!FLAGS_csv.isEmpty()) {
|
|
|
|
if (1 != FLAGS_csv.count()) {
|
|
|
|
SkDebugf("csv flag expects one argument: <csv file>\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-07 19:24:06 +00:00
|
|
|
if (!FLAGS_alphaDir.isEmpty()) {
|
|
|
|
if (1 != FLAGS_alphaDir.count()) {
|
|
|
|
SkDebugf("alphaDir flag expects one argument: <directory>\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
2014-07-02 14:43:04 +00:00
|
|
|
if (!FLAGS_rgbDiffDir.isEmpty()) {
|
|
|
|
if (1 != FLAGS_rgbDiffDir.count()) {
|
|
|
|
SkDebugf("rgbDiffDir flag expects one argument: <directory>\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
2014-08-13 17:36:06 +00:00
|
|
|
|
2014-07-02 14:43:04 +00:00
|
|
|
if (!FLAGS_whiteDiffDir.isEmpty()) {
|
|
|
|
if (1 != FLAGS_whiteDiffDir.count()) {
|
|
|
|
SkDebugf("whiteDiffDir flag expects one argument: <directory>\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
2013-11-07 19:24:06 +00:00
|
|
|
|
2013-07-02 19:55:32 +00:00
|
|
|
SkDiffContext ctx;
|
|
|
|
ctx.setDiffers(chosenDiffers);
|
2014-08-13 17:36:06 +00:00
|
|
|
ctx.setLongNames(FLAGS_longnames);
|
2013-06-28 15:34:56 +00:00
|
|
|
|
2013-11-07 19:24:06 +00:00
|
|
|
if (!FLAGS_alphaDir.isEmpty()) {
|
2014-07-02 14:43:04 +00:00
|
|
|
ctx.setAlphaMaskDir(SkString(FLAGS_alphaDir[0]));
|
|
|
|
}
|
|
|
|
if (!FLAGS_rgbDiffDir.isEmpty()) {
|
|
|
|
ctx.setRgbDiffDir(SkString(FLAGS_rgbDiffDir[0]));
|
|
|
|
}
|
|
|
|
if (!FLAGS_whiteDiffDir.isEmpty()) {
|
|
|
|
ctx.setWhiteDiffDir(SkString(FLAGS_whiteDiffDir[0]));
|
2013-11-07 19:24:06 +00:00
|
|
|
}
|
|
|
|
|
2013-10-16 18:36:49 +00:00
|
|
|
if (FLAGS_threads >= 0) {
|
|
|
|
ctx.setThreadCount(FLAGS_threads);
|
|
|
|
}
|
|
|
|
|
2013-07-02 19:55:32 +00:00
|
|
|
// Perform a folder diff if one is requested
|
|
|
|
if (!FLAGS_folders.isEmpty()) {
|
|
|
|
ctx.diffDirectories(FLAGS_folders[0], FLAGS_folders[1]);
|
|
|
|
}
|
2013-06-27 17:51:35 +00:00
|
|
|
|
2013-07-02 19:55:32 +00:00
|
|
|
// Perform a pattern diff if one is requested
|
|
|
|
if (!FLAGS_patterns.isEmpty()) {
|
|
|
|
ctx.diffPatterns(FLAGS_patterns[0], FLAGS_patterns[1]);
|
|
|
|
}
|
2013-06-27 17:51:35 +00:00
|
|
|
|
2013-07-02 19:55:32 +00:00
|
|
|
// Output to the file specified
|
|
|
|
if (!FLAGS_output.isEmpty()) {
|
|
|
|
SkFILEWStream outputStream(FLAGS_output[0]);
|
2013-07-08 15:04:45 +00:00
|
|
|
ctx.outputRecords(outputStream, FLAGS_jsonp);
|
2013-06-27 17:51:35 +00:00
|
|
|
}
|
2013-06-14 17:26:54 +00:00
|
|
|
|
2013-07-22 15:24:26 +00:00
|
|
|
if (!FLAGS_csv.isEmpty()) {
|
|
|
|
SkFILEWStream outputStream(FLAGS_csv[0]);
|
|
|
|
ctx.outputCsv(outputStream);
|
|
|
|
}
|
|
|
|
|
2013-06-14 17:26:54 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2013-07-22 17:05:24 +00:00
|
|
|
|
|
|
|
#if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL)
|
|
|
|
int main(int argc, char * argv[]) {
|
|
|
|
return tool_main(argc, (char**) argv);
|
|
|
|
}
|
|
|
|
#endif
|