blob: 0c27e0989b6c852fdfbf169821cfc0a5fbb2a721 [file] [log] [blame]
commit-bot@chromium.orgbe19b9e2013-06-14 17:26:54 +00001
2/*
3 * Copyright 2013 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9#include <cstring>
10
11#include "SkBitmap.h"
12#include "SkStream.h"
13
14#include "SkCLImageDiffer.h"
15#include "skpdiff_util.h"
16
17SkCLImageDiffer::SkCLImageDiffer() {
18 fIsGood = false;
19}
20
21
22bool SkCLImageDiffer::init(cl_device_id device, cl_context context) {
23 fContext = context;
24 fDevice = device;
25
26 cl_int queueErr;
27 fCommandQueue = clCreateCommandQueue(fContext, fDevice, 0, &queueErr);
28 if (CL_SUCCESS != queueErr) {
29 SkDebugf("Command queue creation failed: %s\n", cl_error_to_string(queueErr));
30 fIsGood = false;
31 return false;
32 }
33
34 fIsGood = this->onInit();
35 return fIsGood;
36}
37
38bool SkCLImageDiffer::loadKernelFile(const char file[], const char name[], cl_kernel* kernel) {
39 // Open the kernel source file
40 SkFILEStream sourceStream(file);
41 if (!sourceStream.isValid()) {
42 SkDebugf("Failed to open kernel source file");
43 return false;
44 }
45
46 return loadKernelStream(&sourceStream, name, kernel);
47}
48
49bool SkCLImageDiffer::loadKernelStream(SkStream* stream, const char name[], cl_kernel* kernel) {
50 // Read the kernel source into memory
51 SkString sourceString;
52 sourceString.resize(stream->getLength());
53 size_t bytesRead = stream->read(sourceString.writable_str(), sourceString.size());
54 if (bytesRead != sourceString.size()) {
55 SkDebugf("Failed to read kernel source file");
56 return false;
57 }
58
59 return loadKernelSource(sourceString.c_str(), name, kernel);
60}
61
62bool SkCLImageDiffer::loadKernelSource(const char source[], const char name[], cl_kernel* kernel) {
63 // Build the kernel source
64 size_t sourceLen = strlen(source);
65 cl_program program = clCreateProgramWithSource(fContext, 1, &source, &sourceLen, NULL);
66 cl_int programErr = clBuildProgram(program, 1, &fDevice, "", NULL, NULL);
67 if (CL_SUCCESS != programErr) {
68 SkDebugf("Program creation failed: %s\n", cl_error_to_string(programErr));
69
70 // Attempt to get information about why the build failed
71 char buildLog[4096];
zachr@google.com572b54d2013-06-28 16:27:33 +000072 clGetProgramBuildInfo(program, fDevice, CL_PROGRAM_BUILD_LOG, sizeof(buildLog),
73 buildLog, NULL);
commit-bot@chromium.orgbe19b9e2013-06-14 17:26:54 +000074 SkDebugf("Build log: %s\n", buildLog);
75
76 return false;
77 }
78
79 cl_int kernelErr;
80 *kernel = clCreateKernel(program, name, &kernelErr);
81 if (CL_SUCCESS != kernelErr) {
82 SkDebugf("Kernel creation failed: %s\n", cl_error_to_string(kernelErr));
83 return false;
84 }
85
86 return true;
87}
88
89bool SkCLImageDiffer::makeImage2D(SkBitmap* bitmap, cl_mem* image) {
90 cl_int imageErr;
91 cl_image_format bitmapFormat;
92 switch (bitmap->config()) {
93 case SkBitmap::kA8_Config:
94 bitmapFormat.image_channel_order = CL_A;
95 bitmapFormat.image_channel_data_type = CL_UNSIGNED_INT8;
96 break;
97 case SkBitmap::kRGB_565_Config:
98 bitmapFormat.image_channel_order = CL_RGB;
99 bitmapFormat.image_channel_data_type = CL_UNORM_SHORT_565;
100 break;
101 case SkBitmap::kARGB_8888_Config:
102 bitmapFormat.image_channel_order = CL_RGBA;
103 bitmapFormat.image_channel_data_type = CL_UNSIGNED_INT8;
104 break;
105 default:
106 SkDebugf("Image format is unsupported\n");
107 return false;
108 }
109
110 // Upload the bitmap data to OpenCL
111 bitmap->lockPixels();
112 *image = clCreateImage2D(fContext, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
113 &bitmapFormat, bitmap->width(), bitmap->height(),
114 bitmap->rowBytes(), bitmap->getPixels(),
115 &imageErr);
116 bitmap->unlockPixels();
117
118 if (CL_SUCCESS != imageErr) {
119 SkDebugf("Input image creation failed: %s\n", cl_error_to_string(imageErr));
120 return false;
121 }
122
123 return true;
124}
125
126
127////////////////////////////////////////////////////////////////
128
zachr@google.com572b54d2013-06-28 16:27:33 +0000129struct SkDifferentPixelsImageDiffer::QueuedDiff {
130 bool finished;
131 double result;
132 int numDiffPixels;
133 SkIPoint* poi;
134 cl_mem baseline;
135 cl_mem test;
136 cl_mem resultsBuffer;
137 cl_mem poiBuffer;
138};
139
commit-bot@chromium.orgbe19b9e2013-06-14 17:26:54 +0000140const char* SkDifferentPixelsImageDiffer::getName() {
zachr@google.comdb54dd32013-06-27 17:51:35 +0000141 return "different_pixels";
commit-bot@chromium.orgbe19b9e2013-06-14 17:26:54 +0000142}
143
144int SkDifferentPixelsImageDiffer::queueDiff(SkBitmap * baseline, SkBitmap * test) {
145 int diffID = fQueuedDiffs.count();
146 double startTime = get_seconds();
147 QueuedDiff* diff = fQueuedDiffs.push();
148
zachr@google.com572b54d2013-06-28 16:27:33 +0000149 // If we never end up running the kernel, include some safe defaults in the result.
150 diff->finished = false;
151 diff->result = -1.0;
152 diff->numDiffPixels = 0;
153 diff->poi = NULL;
154
commit-bot@chromium.orgbe19b9e2013-06-14 17:26:54 +0000155 // Ensure the images are comparable
156 if (baseline->width() != test->width() || baseline->height() != test->height() ||
157 baseline->width() <= 0 || baseline->height() <= 0) {
158 diff->finished = true;
commit-bot@chromium.orgbe19b9e2013-06-14 17:26:54 +0000159 return diffID;
160 }
161
162 // Upload images to the CL device
163 if (!this->makeImage2D(baseline, &diff->baseline) || !this->makeImage2D(test, &diff->test)) {
164 diff->finished = true;
commit-bot@chromium.orgbe19b9e2013-06-14 17:26:54 +0000165 fIsGood = false;
166 return -1;
167 }
168
169 // A small hack that makes calculating percentage difference easier later on.
170 diff->result = 1.0 / ((double)baseline->width() * baseline->height());
171
zachr@google.com572b54d2013-06-28 16:27:33 +0000172 // Make a buffer to store results into. It must be initialized with pointers to memory.
173 static const int kZero = 0;
174 // We know OpenCL won't write to it because we use CL_MEM_COPY_HOST_PTR
commit-bot@chromium.orgbe19b9e2013-06-14 17:26:54 +0000175 diff->resultsBuffer = clCreateBuffer(fContext, CL_MEM_READ_WRITE | CL_MEM_COPY_HOST_PTR,
zachr@google.com572b54d2013-06-28 16:27:33 +0000176 sizeof(int), (int*)&kZero, NULL);
177
178 diff->poiBuffer = clCreateBuffer(fContext, CL_MEM_WRITE_ONLY,
179 sizeof(int) * 2 * baseline->width() * baseline->height(),
180 NULL, NULL);
commit-bot@chromium.orgbe19b9e2013-06-14 17:26:54 +0000181
182 // Set all kernel arguments
183 cl_int setArgErr = clSetKernelArg(fKernel, 0, sizeof(cl_mem), &diff->baseline);
184 setArgErr |= clSetKernelArg(fKernel, 1, sizeof(cl_mem), &diff->test);
185 setArgErr |= clSetKernelArg(fKernel, 2, sizeof(cl_mem), &diff->resultsBuffer);
zachr@google.com572b54d2013-06-28 16:27:33 +0000186 setArgErr |= clSetKernelArg(fKernel, 3, sizeof(cl_mem), &diff->poiBuffer);
commit-bot@chromium.orgbe19b9e2013-06-14 17:26:54 +0000187 if (CL_SUCCESS != setArgErr) {
188 SkDebugf("Set arg failed: %s\n", cl_error_to_string(setArgErr));
189 fIsGood = false;
190 return -1;
191 }
192
193 // Queue this diff on the CL device
194 cl_event event;
195 const size_t workSize[] = { baseline->width(), baseline->height() };
196 cl_int enqueueErr;
zachr@google.com572b54d2013-06-28 16:27:33 +0000197 enqueueErr = clEnqueueNDRangeKernel(fCommandQueue, fKernel, 2, NULL, workSize,
198 NULL, 0, NULL, &event);
commit-bot@chromium.orgbe19b9e2013-06-14 17:26:54 +0000199 if (CL_SUCCESS != enqueueErr) {
200 SkDebugf("Enqueue failed: %s\n", cl_error_to_string(enqueueErr));
201 fIsGood = false;
202 return -1;
203 }
204
205 // This makes things totally synchronous. Actual queue is not ready yet
206 clWaitForEvents(1, &event);
207 diff->finished = true;
208
209 // Immediate read back the results
zachr@google.com572b54d2013-06-28 16:27:33 +0000210 clEnqueueReadBuffer(fCommandQueue, diff->resultsBuffer, CL_TRUE, 0,
211 sizeof(int), &diff->numDiffPixels, 0, NULL, NULL);
212 diff->result *= (double)diff->numDiffPixels;
commit-bot@chromium.orgbe19b9e2013-06-14 17:26:54 +0000213 diff->result = (1.0 - diff->result);
zachr@google.com5eef11b2013-06-28 15:37:15 +0000214
zachr@google.com572b54d2013-06-28 16:27:33 +0000215 diff->poi = SkNEW_ARRAY(SkIPoint, diff->numDiffPixels);
216 clEnqueueReadBuffer(fCommandQueue, diff->poiBuffer, CL_TRUE, 0,
217 sizeof(SkIPoint) * diff->numDiffPixels, diff->poi, 0, NULL, NULL);
218
zachr@google.com5eef11b2013-06-28 15:37:15 +0000219 // Release all the buffers created
zachr@google.com572b54d2013-06-28 16:27:33 +0000220 clReleaseMemObject(diff->poiBuffer);
zachr@google.com5eef11b2013-06-28 15:37:15 +0000221 clReleaseMemObject(diff->resultsBuffer);
222 clReleaseMemObject(diff->baseline);
223 clReleaseMemObject(diff->test);
224
commit-bot@chromium.orgbe19b9e2013-06-14 17:26:54 +0000225 SkDebugf("Time: %f\n", (get_seconds() - startTime));
226
227 return diffID;
228}
229
zachr@google.com572b54d2013-06-28 16:27:33 +0000230void SkDifferentPixelsImageDiffer::deleteDiff(int id) {
231 QueuedDiff* diff = &fQueuedDiffs[id];
232 if (NULL != diff->poi) {
233 SkDELETE_ARRAY(diff->poi);
234 diff->poi = NULL;
235 }
236}
237
commit-bot@chromium.orgbe19b9e2013-06-14 17:26:54 +0000238bool SkDifferentPixelsImageDiffer::isFinished(int id) {
239 return fQueuedDiffs[id].finished;
240}
241
242double SkDifferentPixelsImageDiffer::getResult(int id) {
243 return fQueuedDiffs[id].result;
244}
245
zachr@google.com572b54d2013-06-28 16:27:33 +0000246int SkDifferentPixelsImageDiffer::getPointsOfInterestCount(int id) {
247 return fQueuedDiffs[id].numDiffPixels;
248}
249
250SkIPoint* SkDifferentPixelsImageDiffer::getPointsOfInterest(int id) {
251 return fQueuedDiffs[id].poi;
252}
commit-bot@chromium.orgbe19b9e2013-06-14 17:26:54 +0000253
254bool SkDifferentPixelsImageDiffer::onInit() {
255 if (!loadKernelFile("experimental/skpdiff/diff_pixels.cl", "diff", &fKernel)) {
256 return false;
257 }
258
259 return true;
260}