| Index: experimental/skpdiff/SkCLImageDiffer.cpp
|
| diff --git a/experimental/skpdiff/SkCLImageDiffer.cpp b/experimental/skpdiff/SkCLImageDiffer.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..4bbb18ce83ee009d41696431207c64b6bb7c876f
|
| --- /dev/null
|
| +++ b/experimental/skpdiff/SkCLImageDiffer.cpp
|
| @@ -0,0 +1,210 @@
|
| +
|
| +/*
|
| + * Copyright 2013 Google Inc.
|
| + *
|
| + * Use of this source code is governed by a BSD-style license that can be
|
| + * found in the LICENSE file.
|
| + */
|
| +
|
| +#include <cstring>
|
| +
|
| +#include "SkBitmap.h"
|
| +#include "SkStream.h"
|
| +
|
| +#include "SkCLImageDiffer.h"
|
| +#include "skpdiff_util.h"
|
| +
|
| +SkCLImageDiffer::SkCLImageDiffer() {
|
| + fIsGood = false;
|
| +}
|
| +
|
| +
|
| +bool SkCLImageDiffer::init(cl_device_id device, cl_context context) {
|
| + fContext = context;
|
| + fDevice = device;
|
| +
|
| + cl_int queueErr;
|
| + fCommandQueue = clCreateCommandQueue(fContext, fDevice, 0, &queueErr);
|
| + if (CL_SUCCESS != queueErr) {
|
| + SkDebugf("Command queue creation failed: %s\n", cl_error_to_string(queueErr));
|
| + fIsGood = false;
|
| + return false;
|
| + }
|
| +
|
| + fIsGood = this->onInit();
|
| + return fIsGood;
|
| +}
|
| +
|
| +bool SkCLImageDiffer::loadKernelFile(const char file[], const char name[], cl_kernel* kernel) {
|
| + // Open the kernel source file
|
| + SkFILEStream sourceStream(file);
|
| + if (!sourceStream.isValid()) {
|
| + SkDebugf("Failed to open kernel source file");
|
| + return false;
|
| + }
|
| +
|
| + return loadKernelStream(&sourceStream, name, kernel);
|
| +}
|
| +
|
| +bool SkCLImageDiffer::loadKernelStream(SkStream* stream, const char name[], cl_kernel* kernel) {
|
| + // Read the kernel source into memory
|
| + SkString sourceString;
|
| + sourceString.resize(stream->getLength());
|
| + size_t bytesRead = stream->read(sourceString.writable_str(), sourceString.size());
|
| + if (bytesRead != sourceString.size()) {
|
| + SkDebugf("Failed to read kernel source file");
|
| + return false;
|
| + }
|
| +
|
| + return loadKernelSource(sourceString.c_str(), name, kernel);
|
| +}
|
| +
|
| +bool SkCLImageDiffer::loadKernelSource(const char source[], const char name[], cl_kernel* kernel) {
|
| + // Build the kernel source
|
| + size_t sourceLen = strlen(source);
|
| + cl_program program = clCreateProgramWithSource(fContext, 1, &source, &sourceLen, NULL);
|
| + cl_int programErr = clBuildProgram(program, 1, &fDevice, "", NULL, NULL);
|
| + if (CL_SUCCESS != programErr) {
|
| + SkDebugf("Program creation failed: %s\n", cl_error_to_string(programErr));
|
| +
|
| + // Attempt to get information about why the build failed
|
| + char buildLog[4096];
|
| + clGetProgramBuildInfo(program, fDevice, CL_PROGRAM_BUILD_LOG, sizeof(buildLog), buildLog, NULL);
|
| + SkDebugf("Build log: %s\n", buildLog);
|
| +
|
| + return false;
|
| + }
|
| +
|
| + cl_int kernelErr;
|
| + *kernel = clCreateKernel(program, name, &kernelErr);
|
| + if (CL_SUCCESS != kernelErr) {
|
| + SkDebugf("Kernel creation failed: %s\n", cl_error_to_string(kernelErr));
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +bool SkCLImageDiffer::makeImage2D(SkBitmap* bitmap, cl_mem* image) {
|
| + cl_int imageErr;
|
| + cl_image_format bitmapFormat;
|
| + switch (bitmap->config()) {
|
| + case SkBitmap::kA8_Config:
|
| + bitmapFormat.image_channel_order = CL_A;
|
| + bitmapFormat.image_channel_data_type = CL_UNSIGNED_INT8;
|
| + break;
|
| + case SkBitmap::kRGB_565_Config:
|
| + bitmapFormat.image_channel_order = CL_RGB;
|
| + bitmapFormat.image_channel_data_type = CL_UNORM_SHORT_565;
|
| + break;
|
| + case SkBitmap::kARGB_8888_Config:
|
| + bitmapFormat.image_channel_order = CL_RGBA;
|
| + bitmapFormat.image_channel_data_type = CL_UNSIGNED_INT8;
|
| + break;
|
| + default:
|
| + SkDebugf("Image format is unsupported\n");
|
| + return false;
|
| + }
|
| +
|
| + // Upload the bitmap data to OpenCL
|
| + bitmap->lockPixels();
|
| + *image = clCreateImage2D(fContext, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
|
| + &bitmapFormat, bitmap->width(), bitmap->height(),
|
| + bitmap->rowBytes(), bitmap->getPixels(),
|
| + &imageErr);
|
| + bitmap->unlockPixels();
|
| +
|
| + if (CL_SUCCESS != imageErr) {
|
| + SkDebugf("Input image creation failed: %s\n", cl_error_to_string(imageErr));
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +
|
| +////////////////////////////////////////////////////////////////
|
| +
|
| +const char* SkDifferentPixelsImageDiffer::getName() {
|
| + return "Find Different Pixels";
|
| +}
|
| +
|
| +int SkDifferentPixelsImageDiffer::queueDiff(SkBitmap * baseline, SkBitmap * test) {
|
| + int diffID = fQueuedDiffs.count();
|
| + double startTime = get_seconds();
|
| + QueuedDiff* diff = fQueuedDiffs.push();
|
| +
|
| + // Ensure the images are comparable
|
| + if (baseline->width() != test->width() || baseline->height() != test->height() ||
|
| + baseline->width() <= 0 || baseline->height() <= 0) {
|
| + diff->finished = true;
|
| + diff->result = 0.0;
|
| + return diffID;
|
| + }
|
| +
|
| + // Upload images to the CL device
|
| + if (!this->makeImage2D(baseline, &diff->baseline) || !this->makeImage2D(test, &diff->test)) {
|
| + diff->finished = true;
|
| + diff->result = 0.0;
|
| + fIsGood = false;
|
| + return -1;
|
| + }
|
| +
|
| + // A small hack that makes calculating percentage difference easier later on.
|
| + diff->result = 1.0 / ((double)baseline->width() * baseline->height());
|
| +
|
| + // Make a buffer to store results into
|
| + int numDiffPixels = 0;
|
| + diff->resultsBuffer = clCreateBuffer(fContext, CL_MEM_READ_WRITE | CL_MEM_COPY_HOST_PTR,
|
| + sizeof(int), &numDiffPixels, NULL);
|
| +
|
| + // Set all kernel arguments
|
| + cl_int setArgErr = clSetKernelArg(fKernel, 0, sizeof(cl_mem), &diff->baseline);
|
| + setArgErr |= clSetKernelArg(fKernel, 1, sizeof(cl_mem), &diff->test);
|
| + setArgErr |= clSetKernelArg(fKernel, 2, sizeof(cl_mem), &diff->resultsBuffer);
|
| + if (CL_SUCCESS != setArgErr) {
|
| + SkDebugf("Set arg failed: %s\n", cl_error_to_string(setArgErr));
|
| + fIsGood = false;
|
| + return -1;
|
| + }
|
| +
|
| + // Queue this diff on the CL device
|
| + cl_event event;
|
| + const size_t workSize[] = { baseline->width(), baseline->height() };
|
| + cl_int enqueueErr;
|
| + enqueueErr = clEnqueueNDRangeKernel(fCommandQueue, fKernel, 2, NULL, workSize, NULL, 0, NULL, &event);
|
| + if (CL_SUCCESS != enqueueErr) {
|
| + SkDebugf("Enqueue failed: %s\n", cl_error_to_string(enqueueErr));
|
| + fIsGood = false;
|
| + return -1;
|
| + }
|
| +
|
| + // This makes things totally synchronous. Actual queue is not ready yet
|
| + clWaitForEvents(1, &event);
|
| + diff->finished = true;
|
| +
|
| + // Immediate read back the results
|
| + clEnqueueReadBuffer(fCommandQueue, diff->resultsBuffer, CL_TRUE, 0, sizeof(int), &numDiffPixels, 0, NULL, NULL);
|
| + diff->result *= (double)numDiffPixels;
|
| + diff->result = (1.0 - diff->result);
|
| + SkDebugf("Time: %f\n", (get_seconds() - startTime));
|
| +
|
| + return diffID;
|
| +}
|
| +
|
| +bool SkDifferentPixelsImageDiffer::isFinished(int id) {
|
| + return fQueuedDiffs[id].finished;
|
| +}
|
| +
|
| +double SkDifferentPixelsImageDiffer::getResult(int id) {
|
| + return fQueuedDiffs[id].result;
|
| +}
|
| +
|
| +
|
| +bool SkDifferentPixelsImageDiffer::onInit() {
|
| + if (!loadKernelFile("experimental/skpdiff/diff_pixels.cl", "diff", &fKernel)) {
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
|
|