Home | History | Annotate | Download | only in skpdiff
      1 /*
      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 #if SK_SUPPORT_OPENCL
      9 
     10 #define __NO_STD_VECTOR // Uses cl::vectpr instead of std::vectpr
     11 #define __NO_STD_STRING // Uses cl::STRING_CLASS instead of std::string
     12 #if SK_BUILD_FOR_MAC
     13 // Note that some macs don't have this header and it can be downloaded from the Khronos registry
     14 #   include <OpenCL/cl.hpp>
     15 #else
     16 #   include <CL/cl.hpp>
     17 #endif
     18 
     19 #endif
     20 
     21 #include "SkCommandLineFlags.h"
     22 #include "SkGraphics.h"
     23 #include "SkStream.h"
     24 #include "SkTDArray.h"
     25 
     26 #include "SkDifferentPixelsMetric.h"
     27 #include "SkDiffContext.h"
     28 #include "SkImageDiffer.h"
     29 #include "SkPMetric.h"
     30 #include "skpdiff_util.h"
     31 
     32 #include "SkForceLinking.h"
     33 __SK_FORCE_IMAGE_DECODER_LINKING;
     34 
     35 // Command line argument definitions go here
     36 DEFINE_bool2(list, l, false, "List out available differs");
     37 DEFINE_string2(differs, d, "", "The names of the differs to use or all of them by default");
     38 DEFINE_string2(folders, f, "", "Compare two folders with identical subfile names: <baseline folder> <test folder>");
     39 DEFINE_string2(patterns, p, "", "Use two patterns to compare images: <baseline> <test>");
     40 DEFINE_string2(output, o, "", "Writes the output of these diffs to output: <output>");
     41 DEFINE_string(alphaDir, "", "Writes the alpha mask of these diffs to output: <output>");
     42 DEFINE_bool(jsonp, true, "Output JSON with padding");
     43 DEFINE_string(csv, "", "Writes the output of these diffs to a csv file");
     44 DEFINE_int32(threads, -1, "run N threads in parallel [default is derived from CPUs available]");
     45 
     46 #if SK_SUPPORT_OPENCL
     47 /// A callback for any OpenCL errors
     48 static void CL_CALLBACK error_notify(const char* errorInfo, const void* privateInfoSize, ::size_t cb, void* userData) {
     49     SkDebugf("OpenCL error notify: %s\n", errorInfo);
     50     exit(1);
     51 }
     52 
     53 /// Creates a device and context with OpenCL
     54 static bool init_device_and_context(cl::Device* device, cl::Context* context) {
     55     // Query for a platform
     56     cl::vector<cl::Platform> platformList;
     57     cl::Platform::get(&platformList);
     58     SkDebugf("The number of platforms is %u\n", platformList.size());
     59 
     60     // Print some information about the platform for debugging
     61     cl::Platform& platform = platformList[0];
     62     cl::STRING_CLASS platformName;
     63     platform.getInfo(CL_PLATFORM_NAME, &platformName);
     64     SkDebugf("Platform index 0 is named %s\n", platformName.c_str());
     65 
     66     // Query for a device
     67     cl::vector<cl::Device> deviceList;
     68     platform.getDevices(CL_DEVICE_TYPE_ALL, &deviceList);
     69     SkDebugf("The number of devices is %u\n", deviceList.size());
     70 
     71     // Print some information about the device for debugging
     72     *device = deviceList[0];
     73     cl::STRING_CLASS deviceName;
     74     device->getInfo(CL_DEVICE_NAME, &deviceName);
     75     SkDebugf("Device index 0 is named %s\n", deviceName.c_str());
     76 
     77     // Create a CL context and check for all errors
     78     cl_int contextErr = CL_SUCCESS;
     79     *context = cl::Context(deviceList, NULL, error_notify, NULL, &contextErr);
     80     if (contextErr != CL_SUCCESS) {
     81         SkDebugf("Context creation failed: %s\n", cl_error_to_string(contextErr));
     82         return false;
     83     }
     84 
     85     return true;
     86 }
     87 
     88 static bool init_cl_diff(SkImageDiffer* differ) {
     89     // Setup OpenCL
     90     cl::Device device;
     91     cl::Context context;
     92     if (!init_device_and_context(&device, &context)) {
     93         return false;
     94     }
     95 
     96     // Setup our differ of choice
     97     SkCLImageDiffer* clDiffer = (SkCLImageDiffer*)differ;
     98     return clDiffer->init(device(), context());
     99 }
    100 #endif
    101 
    102 // TODO Find a better home for the diff registry. One possibility is to have the differs self
    103 // register.
    104 
    105 // List here every differ
    106 SkDifferentPixelsMetric gDiffPixel;
    107 SkPMetric gPDiff;
    108 
    109 // A null terminated array of pointer to every differ declared above
    110 SkImageDiffer* gDiffers[] = { &gDiffPixel, &gPDiff, NULL };
    111 
    112 int tool_main(int argc, char * argv[]);
    113 int tool_main(int argc, char * argv[]) {
    114     // Setup command line parsing
    115     SkCommandLineFlags::SetUsage("Compare images using various metrics.");
    116     SkCommandLineFlags::Parse(argc, argv);
    117 
    118     // Needed by various Skia components
    119     SkAutoGraphics ag;
    120 
    121     if (FLAGS_list) {
    122         SkDebugf("Available Metrics:\n");
    123     }
    124 
    125     // Figure which differs the user chose, and optionally print them if the user requests it
    126     SkTDArray<SkImageDiffer*> chosenDiffers;
    127     for (int differIndex = 0; NULL != gDiffers[differIndex]; differIndex++) {
    128         SkImageDiffer* differ = gDiffers[differIndex];
    129         if (FLAGS_list) {
    130             SkDebugf("    %s", differ->getName());
    131             SkDebugf("\n");
    132         }
    133 
    134         // Check if this differ was chosen by any of the flags. Initialize them if they were chosen.
    135         if (FLAGS_differs.isEmpty()) {
    136             // If no differs were chosen, they all get added
    137             if (differ->requiresOpenCL()) {
    138 #if SK_SUPPORT_OPENCL
    139                 init_cl_diff(differ);
    140                 chosenDiffers.push(differ);
    141 #endif
    142             } else {
    143                 chosenDiffers.push(differ);
    144             }
    145         } else {
    146             for (int flagIndex = 0; flagIndex < FLAGS_differs.count(); flagIndex++) {
    147                 if (SkString(FLAGS_differs[flagIndex]).equals(differ->getName())) {
    148                     // Initialize OpenCL for the differ if it needs it and support was compiled in.
    149                     if (differ->requiresOpenCL()) {
    150 #if SK_SUPPORT_OPENCL
    151                         init_cl_diff(differ);
    152                         chosenDiffers.push(differ);
    153 #endif
    154                     } else {
    155                         chosenDiffers.push(differ);
    156                     }
    157                     break;
    158                 }
    159             }
    160         }
    161     }
    162 
    163     // Don't attempt to initialize the differ if we aren't going to use it
    164     if (FLAGS_folders.isEmpty() && FLAGS_patterns.isEmpty()) {
    165         return 0;
    166     }
    167 
    168     // Validate command line flags
    169     if (!FLAGS_folders.isEmpty()) {
    170         if (2 != FLAGS_folders.count()) {
    171             SkDebugf("Folders flag expects two arguments: <baseline folder> <test folder>\n");
    172             return 1;
    173         }
    174     }
    175 
    176     if (!FLAGS_patterns.isEmpty()) {
    177         if (2 != FLAGS_patterns.count()) {
    178             SkDebugf("Patterns flag expects two arguments: <baseline pattern> <test pattern>\n");
    179             return 1;
    180         }
    181     }
    182 
    183     if (!FLAGS_csv.isEmpty()) {
    184         if (1 != FLAGS_csv.count()) {
    185             SkDebugf("csv flag expects one argument: <csv file>\n");
    186             return 1;
    187         }
    188     }
    189 
    190     if (!FLAGS_alphaDir.isEmpty()) {
    191         if (1 != FLAGS_alphaDir.count()) {
    192             SkDebugf("alphaDir flag expects one argument: <directory>\n");
    193             return 1;
    194         }
    195     }
    196 
    197     SkDiffContext ctx;
    198     ctx.setDiffers(chosenDiffers);
    199 
    200     if (!FLAGS_alphaDir.isEmpty()) {
    201         ctx.setDifferenceDir(SkString(FLAGS_alphaDir[0]));
    202     }
    203 
    204     if (FLAGS_threads >= 0) {
    205         ctx.setThreadCount(FLAGS_threads);
    206     }
    207 
    208     // Perform a folder diff if one is requested
    209     if (!FLAGS_folders.isEmpty()) {
    210         ctx.diffDirectories(FLAGS_folders[0], FLAGS_folders[1]);
    211     }
    212 
    213     // Perform a pattern diff if one is requested
    214     if (!FLAGS_patterns.isEmpty()) {
    215         ctx.diffPatterns(FLAGS_patterns[0], FLAGS_patterns[1]);
    216     }
    217 
    218     // Output to the file specified
    219     if (!FLAGS_output.isEmpty()) {
    220         SkFILEWStream outputStream(FLAGS_output[0]);
    221         ctx.outputRecords(outputStream, FLAGS_jsonp);
    222     }
    223 
    224     if (!FLAGS_csv.isEmpty()) {
    225         SkFILEWStream outputStream(FLAGS_csv[0]);
    226         ctx.outputCsv(outputStream);
    227     }
    228 
    229     return 0;
    230 }
    231 
    232 #if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL)
    233 int main(int argc, char * argv[]) {
    234     return tool_main(argc, (char**) argv);
    235 }
    236 #endif
    237