OLD | NEW |
---|---|
(Empty) | |
1 | |
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 | |
17 SkCLImageDiffer::SkCLImageDiffer() { | |
18 fIsGood = false; | |
19 } | |
20 | |
21 | |
22 bool 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(queue Err)); | |
30 fIsGood = false; | |
31 return false; | |
32 } | |
33 | |
34 bool initResult = this->onInit(); | |
djsollen
2013/06/13 13:33:02
fIsGood = this->onInit();
return fIsGood;
| |
35 fIsGood = initResult; | |
36 return initResult; | |
37 } | |
38 | |
39 bool SkCLImageDiffer::loadKernelFile(const char file[], const char name[], cl_ke rnel* kernel) { | |
40 // Open the kernel source file | |
41 SkFILEStream sourceStream(file); | |
42 if (!sourceStream.isValid()) { | |
43 SkDebugf("Failed to open kernel source file"); | |
44 return false; | |
45 } | |
46 | |
47 return loadKernelStream(&sourceStream, name, kernel); | |
48 } | |
49 | |
50 bool SkCLImageDiffer::loadKernelStream(SkStream* stream, const char name[], cl_k ernel* kernel) { | |
51 // Read the kernel source into memory | |
52 SkString sourceString; | |
53 sourceString.resize(stream->getLength()); | |
54 size_t bytesRead = stream->read(sourceString.writable_str(), sourceString.si ze()); | |
55 if (bytesRead != sourceString.size()) { | |
56 SkDebugf("Failed to read kernel source file"); | |
57 return false; | |
58 } | |
59 | |
60 return loadKernelSource(sourceString.c_str(), name, kernel); | |
61 } | |
62 | |
63 bool SkCLImageDiffer::loadKernelSource(const char source[], const char name[], c l_kernel* kernel) { | |
64 // Build the kernel source | |
65 size_t sourceLen = strlen(source); | |
66 cl_program program = clCreateProgramWithSource(fContext, 1, &source, &source Len, NULL); | |
67 cl_int programErr = clBuildProgram(program, 1, &fDevice, "", NULL, NULL); | |
68 if (CL_SUCCESS != programErr) { | |
69 SkDebugf("Program creation failed: %s\n", cl_error_to_string(programErr) ); | |
70 | |
71 // Attempt to get information about why the build failed | |
72 char buildLog[4096]; | |
73 clGetProgramBuildInfo(program, fDevice, CL_PROGRAM_BUILD_LOG, sizeof(bui ldLog), buildLog, NULL); | |
74 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 | |
89 bool 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(imageEr r)); | |
120 return false; | |
121 } | |
122 | |
123 return true; | |
124 } | |
125 | |
126 | |
127 //////////////////////////////////////////////////////////////// | |
128 | |
129 const char* SkDifferentPixelsImageDiffer::getName() { | |
130 return "Find Different Pixels"; | |
131 } | |
132 | |
133 int SkDifferentPixelsImageDiffer::queueDiff(SkBitmap * baseline, SkBitmap * test ) { | |
134 int diffID = fQueuedDiffs.count(); | |
135 double startTime = get_seconds(); | |
136 QueuedDiff* diff = fQueuedDiffs.push(); | |
137 | |
138 // Ensure the images are comparable | |
139 if (baseline->width() != test->width() || baseline->height() != test->height () || | |
140 baseline->width() <= 0 || baseline->height() <= 0) { | |
141 diff->finished = true; | |
142 diff->result = 0.0; | |
143 return diffID; | |
144 } | |
145 | |
146 // Upload images to the CL device | |
147 if (!this->makeImage2D(baseline, &diff->baseline) || !this->makeImage2D(test , &diff->test)) { | |
148 diff->finished = true; | |
149 diff->result = 0.0; | |
150 fIsGood = false; | |
151 return -1; | |
djsollen
2013/06/13 13:33:02
not required but you may want to consider returnin
| |
152 } | |
153 | |
154 diff->result = 1.0 / ((double)baseline->width() * baseline->height()); | |
djsollen
2013/06/13 13:33:02
what is this number used for?
| |
155 | |
156 // Make a buffer to store results into | |
157 int numDiffPixels = 0; | |
158 diff->resultsBuffer = clCreateBuffer(fContext, CL_MEM_READ_WRITE | CL_MEM_CO PY_HOST_PTR, | |
159 sizeof(int), &numDiffPixels, NULL); | |
160 | |
161 // Set all kernel arguments | |
162 cl_int setArgErr = clSetKernelArg(fKernel, 0, sizeof(cl_mem), &diff->baselin e); | |
163 setArgErr |= clSetKernelArg(fKernel, 1, sizeof(cl_mem), &diff->test); | |
164 setArgErr |= clSetKernelArg(fKernel, 2, sizeof(cl_mem), &diff->results Buffer); | |
165 if (CL_SUCCESS != setArgErr) { | |
166 SkDebugf("Set arg failed: %s\n", cl_error_to_string(setArgErr)); | |
167 fIsGood = false; | |
168 return -1; | |
169 } | |
170 | |
171 // Queue this diff on the CL device | |
172 cl_event event; | |
173 const size_t workSize[] = { baseline->width(), baseline->height() }; | |
174 cl_int enqueueErr; | |
175 enqueueErr = clEnqueueNDRangeKernel(fCommandQueue, fKernel, 2, NULL, workSiz e, NULL, 0, NULL, &event); | |
176 if (CL_SUCCESS != enqueueErr) { | |
177 SkDebugf("Enqueue failed: %s\n", cl_error_to_string(enqueueErr)); | |
178 fIsGood = false; | |
179 return -1; | |
180 } | |
181 | |
182 // This makes things totally synchronous. Actual queue is not ready yet | |
183 clWaitForEvents(1, &event); | |
184 diff->finished = true; | |
185 | |
186 // Immediate read back the results | |
187 clEnqueueReadBuffer(fCommandQueue, diff->resultsBuffer, CL_TRUE, 0, sizeof(i nt), &numDiffPixels, 0, NULL, NULL); | |
188 diff->result *= (double)numDiffPixels; | |
189 diff->result = (1.0 - diff->result); | |
190 SkDebugf("Time: %f\n", (get_seconds() - startTime)); | |
191 | |
192 return diffID; | |
193 } | |
194 | |
195 bool SkDifferentPixelsImageDiffer::isFinished(int id) { | |
196 return fQueuedDiffs[id].finished; | |
197 } | |
198 | |
199 double SkDifferentPixelsImageDiffer::getResult(int id) { | |
200 return fQueuedDiffs[id].result; | |
201 } | |
202 | |
203 | |
204 bool SkDifferentPixelsImageDiffer::onInit() { | |
205 if (!loadKernelFile("experimental/skpdiff/diff_pixels.cl", "diff", &fKernel) ) { | |
206 return false; | |
207 } | |
208 | |
209 return true; | |
210 } | |
OLD | NEW |