Refactoring of tiles opencl implementation:
[blender.git] / source / blender / compositor / operations / COM_DilateErodeOperation.cpp
1 /*
2  * Copyright 2011, Blender Foundation.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * Contributor: 
19  *              Jeroen Bakker 
20  *              Monique Dewanchand
21  */
22
23 #include "COM_DilateErodeOperation.h"
24 #include "BLI_math.h"
25 #include "COM_OpenCLDevice.h"
26
27 // DilateErode Distance Threshold
28 DilateErodeThresholdOperation::DilateErodeThresholdOperation() : NodeOperation()
29 {
30         this->addInputSocket(COM_DT_VALUE);
31         this->addOutputSocket(COM_DT_VALUE);
32         this->setComplex(true);
33         this->inputProgram = NULL;
34         this->inset = 0.0f;
35         this->_switch = 0.5f;
36         this->distance = 0.0f;
37 }
38 void DilateErodeThresholdOperation::initExecution()
39 {
40         this->inputProgram = this->getInputSocketReader(0);
41         if (this->distance < 0.0f) {
42                 this->scope = -this->distance + this->inset;
43         }
44         else {
45                 if (this->inset * 2 > this->distance) {
46                         this->scope = max(this->inset * 2 - this->distance, this->distance);
47                 }
48                 else {
49                         this->scope = distance;
50                 }
51         }
52         if (scope < 3) {
53                 scope = 3;
54         }
55 }
56
57 void *DilateErodeThresholdOperation::initializeTileData(rcti *rect, MemoryBuffer **memoryBuffers)
58 {
59         void *buffer = inputProgram->initializeTileData(NULL, memoryBuffers);
60         return buffer;
61 }
62
63 void DilateErodeThresholdOperation::executePixel(float *color, int x, int y, MemoryBuffer *inputBuffers[], void *data)
64 {
65         float inputValue[4];
66         const float sw = this->_switch;
67         const float distance = this->distance;
68         float pixelvalue;
69         const float rd = scope * scope;
70         const float inset = this->inset;
71         float mindist = rd * 2;
72
73         MemoryBuffer *inputBuffer = (MemoryBuffer *)data;
74         float *buffer = inputBuffer->getBuffer();
75         rcti *rect = inputBuffer->getRect();
76         const int minx = max(x - scope, rect->xmin);
77         const int miny = max(y - scope, rect->ymin);
78         const int maxx = min(x + scope, rect->xmax);
79         const int maxy = min(y + scope, rect->ymax);
80         const int bufferWidth = rect->xmax - rect->xmin;
81         int offset;
82
83         this->inputProgram->read(inputValue, x, y, inputBuffers, NULL);
84         if (inputValue[0] > sw) {
85                 for (int yi = miny; yi < maxy; yi++) {
86                         const float dy = yi - y;
87                         offset = ((yi - rect->ymin) * bufferWidth + (minx - rect->xmin)) * 4;
88                         for (int xi = minx; xi < maxx; xi++) {
89                                 if (buffer[offset] < sw) {
90                                         const float dx = xi - x;
91                                         const float dis = dx * dx + dy * dy;
92                                         mindist = min(mindist, dis);
93                                 }
94                                 offset += 4;
95                         }
96                 }
97                 pixelvalue = -sqrtf(mindist);
98         }
99         else {
100                 for (int yi = miny; yi < maxy; yi++) {
101                         const float dy = yi - y;
102                         offset = ((yi - rect->ymin) * bufferWidth + (minx - rect->xmin)) * 4;
103                         for (int xi = minx; xi < maxx; xi++) {
104                                 if (buffer[offset] > sw) {
105                                         const float dx = xi - x;
106                                         const float dis = dx * dx + dy * dy;
107                                         mindist = min(mindist, dis);
108                                 }
109                                 offset += 4;
110
111                         }
112                 }
113                 pixelvalue = sqrtf(mindist);
114         }
115
116         if (distance > 0.0f) {
117                 const float delta = distance - pixelvalue;
118                 if (delta >= 0.0f) {
119                         if (delta >= inset) {
120                                 color[0] = 1.0f;
121                         }
122                         else {
123                                 color[0] = delta / inset;
124                         }
125                 }
126                 else {
127                         color[0] = 0.0f;
128                 }
129         }
130         else {
131                 const float delta = -distance + pixelvalue;
132                 if (delta < 0.0f) {
133                         if (delta < -inset) {
134                                 color[0] = 1.0f;
135                         }
136                         else {
137                                 color[0] = (-delta) / inset;
138                         }
139                 }
140                 else {
141                         color[0] = 0.0f;
142                 }
143         }
144 }
145
146 void DilateErodeThresholdOperation::deinitExecution()
147 {
148         this->inputProgram = NULL;
149 }
150
151 bool DilateErodeThresholdOperation::determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output)
152 {
153         rcti newInput;
154
155         newInput.xmax = input->xmax + scope;
156         newInput.xmin = input->xmin - scope;
157         newInput.ymax = input->ymax + scope;
158         newInput.ymin = input->ymin - scope;
159
160         return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
161 }
162
163 // Dilate Distance
164 DilateDistanceOperation::DilateDistanceOperation() : NodeOperation()
165 {
166         this->addInputSocket(COM_DT_VALUE);
167         this->addOutputSocket(COM_DT_VALUE);
168         this->setComplex(true);
169         this->inputProgram = NULL;
170         this->distance = 0.0f;
171         this->setOpenCL(true);
172 }
173 void DilateDistanceOperation::initExecution()
174 {
175         this->inputProgram = this->getInputSocketReader(0);
176         this->scope = distance;
177         if (scope < 3) {
178                 scope = 3;
179         }
180 }
181
182 void *DilateDistanceOperation::initializeTileData(rcti *rect, MemoryBuffer **memoryBuffers)
183 {
184         void *buffer = inputProgram->initializeTileData(NULL, memoryBuffers);
185         return buffer;
186 }
187
188 void DilateDistanceOperation::executePixel(float *color, int x, int y, MemoryBuffer *inputBuffers[], void *data)
189 {
190         const float distance = this->distance;
191         const float mindist = distance * distance;
192
193         MemoryBuffer *inputBuffer = (MemoryBuffer *)data;
194         float *buffer = inputBuffer->getBuffer();
195         rcti *rect = inputBuffer->getRect();
196         const int minx = max(x - scope, rect->xmin);
197         const int miny = max(y - scope, rect->ymin);
198         const int maxx = min(x + scope, rect->xmax);
199         const int maxy = min(y + scope, rect->ymax);
200         const int bufferWidth = rect->xmax - rect->xmin;
201         int offset;
202         
203         float value = 0.0f;
204
205         for (int yi = miny; yi < maxy; yi++) {
206                 const float dy = yi - y;
207                 offset = ((yi - rect->ymin) * bufferWidth + (minx - rect->xmin)) * 4;
208                 for (int xi = minx; xi < maxx; xi++) {
209                         const float dx = xi - x;
210                         const float dis = dx * dx + dy * dy;
211                         if (dis <= mindist) {
212                                 value = max(buffer[offset], value);
213                         }
214                         offset += 4;
215                 }
216         }
217         color[0] = value;
218 }
219
220 void DilateDistanceOperation::deinitExecution()
221 {
222         this->inputProgram = NULL;
223 }
224
225 bool DilateDistanceOperation::determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output)
226 {
227         rcti newInput;
228
229         newInput.xmax = input->xmax + scope;
230         newInput.xmin = input->xmin - scope;
231         newInput.ymax = input->ymax + scope;
232         newInput.ymin = input->ymin - scope;
233
234         return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
235 }
236
237 static cl_kernel dilateKernel = 0;
238 void DilateDistanceOperation::executeOpenCL(OpenCLDevice* device,
239                                             MemoryBuffer *outputMemoryBuffer, cl_mem clOutputBuffer,
240                                             MemoryBuffer **inputMemoryBuffers, list<cl_mem> *clMemToCleanUp,
241                                             list<cl_kernel> *clKernelsToCleanUp)
242 {
243         if (!dilateKernel) {
244                 dilateKernel = device->COM_clCreateKernel("dilateKernel", NULL);
245         }
246         cl_int distanceSquared = this->distance * this->distance;
247         cl_int scope = this->scope;
248         
249         device->COM_clAttachMemoryBufferToKernelParameter(dilateKernel, 0,  2, clMemToCleanUp, inputMemoryBuffers, this->inputProgram);
250         device->COM_clAttachOutputMemoryBufferToKernelParameter(dilateKernel, 1, clOutputBuffer);
251         device->COM_clAttachMemoryBufferOffsetToKernelParameter(dilateKernel, 3, outputMemoryBuffer);
252         clSetKernelArg(dilateKernel, 4, sizeof(cl_int), &scope);
253         clSetKernelArg(dilateKernel, 5, sizeof(cl_int), &distanceSquared);
254         device->COM_clAttachSizeToKernelParameter(dilateKernel, 6, this);
255         device->COM_clEnqueueRange(dilateKernel, outputMemoryBuffer, 7, this);
256 }
257
258 // Erode Distance
259 ErodeDistanceOperation::ErodeDistanceOperation() : DilateDistanceOperation() 
260 {
261         /* pass */
262 }
263
264 void ErodeDistanceOperation::executePixel(float *color, int x, int y, MemoryBuffer *inputBuffers[], void *data)
265 {
266         const float distance = this->distance;
267         const float mindist = distance * distance;
268
269         MemoryBuffer *inputBuffer = (MemoryBuffer *)data;
270         float *buffer = inputBuffer->getBuffer();
271         rcti *rect = inputBuffer->getRect();
272         const int minx = max(x - scope, rect->xmin);
273         const int miny = max(y - scope, rect->ymin);
274         const int maxx = min(x + scope, rect->xmax);
275         const int maxy = min(y + scope, rect->ymax);
276         const int bufferWidth = rect->xmax - rect->xmin;
277         int offset;
278         
279         float value = 1.0f;
280
281         for (int yi = miny; yi < maxy; yi++) {
282                 const float dy = yi - y;
283                 offset = ((yi - rect->ymin) * bufferWidth + (minx - rect->xmin)) * 4;
284                 for (int xi = minx; xi < maxx; xi++) {
285                         const float dx = xi - x;
286                         const float dis = dx * dx + dy * dy;
287                         if (dis <= mindist) {
288                                 value = min(buffer[offset], value);
289                         }
290                         offset += 4;
291                 }
292         }
293         color[0] = value;
294 }
295
296 static cl_kernel erodeKernel = 0;
297 void ErodeDistanceOperation::executeOpenCL(OpenCLDevice* device,
298                                            MemoryBuffer *outputMemoryBuffer, cl_mem clOutputBuffer,
299                                            MemoryBuffer **inputMemoryBuffers, list<cl_mem> *clMemToCleanUp,
300                                            list<cl_kernel> *clKernelsToCleanUp)
301 {
302         if (!erodeKernel) {
303                 erodeKernel = device->COM_clCreateKernel("erodeKernel", NULL);
304         }
305         cl_int distanceSquared = this->distance * this->distance;
306         cl_int scope = this->scope;
307         
308         device->COM_clAttachMemoryBufferToKernelParameter(erodeKernel, 0,  2, clMemToCleanUp, inputMemoryBuffers, this->inputProgram);
309         device->COM_clAttachOutputMemoryBufferToKernelParameter(erodeKernel, 1, clOutputBuffer);
310         device->COM_clAttachMemoryBufferOffsetToKernelParameter(erodeKernel, 3, outputMemoryBuffer);
311         clSetKernelArg(erodeKernel, 4, sizeof(cl_int), &scope);
312         clSetKernelArg(erodeKernel, 5, sizeof(cl_int), &distanceSquared);
313         device->COM_clAttachSizeToKernelParameter(erodeKernel, 6, this);
314         device->COM_clEnqueueRange(erodeKernel, outputMemoryBuffer, 7, this);
315 }
316
317 // Dilate step
318 DilateStepOperation::DilateStepOperation() : NodeOperation()
319 {
320         this->addInputSocket(COM_DT_VALUE);
321         this->addOutputSocket(COM_DT_VALUE);
322         this->setComplex(true);
323         this->inputProgram = NULL;
324 }
325 void DilateStepOperation::initExecution()
326 {
327         this->inputProgram = this->getInputSocketReader(0);
328         this->cached_buffer = NULL;
329         this->initMutex();
330 }
331
332 void *DilateStepOperation::initializeTileData(rcti *rect, MemoryBuffer **memoryBuffers)
333 {
334         if (this->cached_buffer != NULL) {
335                 return this->cached_buffer;
336         }
337         lockMutex();
338         if (this->cached_buffer == NULL) {
339                 MemoryBuffer *buffer = (MemoryBuffer *)inputProgram->initializeTileData(NULL, memoryBuffers);
340                 float *rectf = buffer->convertToValueBuffer();
341                 int x, y, i;
342                 float *p;
343                 int bwidth = buffer->getWidth();
344                 int bheight = buffer->getHeight();
345                 for (i = 0; i < this->iterations; i++) {
346                         for (y = 0; y < bheight; y++) {
347                                 for (x = 0; x < bwidth - 1; x++) {
348                                         p = rectf + (bwidth * y + x);
349                                         *p = MAX2(*p, *(p + 1));
350                                 }
351                         }
352                 
353                         for (y = 0; y < bheight; y++) {
354                                 for (x = bwidth - 1; x >= 1; x--) {
355                                         p = rectf + (bwidth * y + x);
356                                         *p = MAX2(*p, *(p - 1));
357                                 }
358                         }
359                 
360                         for (x = 0; x < bwidth; x++) {
361                                 for (y = 0; y < bheight - 1; y++) {
362                                         p = rectf + (bwidth * y + x);
363                                         *p = MAX2(*p, *(p + bwidth));
364                                 }
365                         }
366                 
367                         for (x = 0; x < bwidth; x++) {
368                                 for (y = bheight - 1; y >= 1; y--) {
369                                         p = rectf + (bwidth * y + x);
370                                         *p = MAX2(*p, *(p - bwidth));
371                                 }
372                         }
373                 }
374                 this->cached_buffer = rectf;
375         }
376         unlockMutex();
377         return this->cached_buffer;
378 }
379
380
381 void DilateStepOperation::executePixel(float *color, int x, int y, MemoryBuffer *inputBuffers[], void *data)
382 {
383         color[0] = this->cached_buffer[y * this->getWidth() + x];
384 }
385
386 void DilateStepOperation::deinitExecution()
387 {
388         this->inputProgram = NULL;
389         this->deinitMutex();
390         if (this->cached_buffer) {
391                 delete cached_buffer;
392                 this->cached_buffer = NULL;
393         }
394 }
395
396 bool DilateStepOperation::determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output)
397 {
398         if (this->cached_buffer) {
399                 return false;
400         }
401         else {
402                 rcti newInput;
403         
404                 newInput.xmax = getWidth();
405                 newInput.xmin = 0;
406                 newInput.ymax = getHeight();
407                 newInput.ymin = 0;
408         
409                 return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
410         }
411 }
412
413 // Erode step
414 ErodeStepOperation::ErodeStepOperation() : DilateStepOperation()
415 {
416         /* pass */
417 }
418
419 void *ErodeStepOperation::initializeTileData(rcti *rect, MemoryBuffer **memoryBuffers)
420 {
421         if (this->cached_buffer != NULL) {
422                 return this->cached_buffer;
423         }
424         lockMutex();
425         if (this->cached_buffer == NULL) {
426                 MemoryBuffer *buffer = (MemoryBuffer *)inputProgram->initializeTileData(NULL, memoryBuffers);
427                 float *rectf = buffer->convertToValueBuffer();
428                 int x, y, i;
429                 float *p;
430                 int bwidth = buffer->getWidth();
431                 int bheight = buffer->getHeight();
432                 for (i = 0; i < this->iterations; i++) {
433                         for (y = 0; y < bheight; y++) {
434                                 for (x = 0; x < bwidth - 1; x++) {
435                                         p = rectf + (bwidth * y + x);
436                                         *p = MIN2(*p, *(p + 1));
437                                 }
438                         }
439                 
440                         for (y = 0; y < bheight; y++) {
441                                 for (x = bwidth - 1; x >= 1; x--) {
442                                         p = rectf + (bwidth * y + x);
443                                         *p = MIN2(*p, *(p - 1));
444                                 }
445                         }
446                 
447                         for (x = 0; x < bwidth; x++) {
448                                 for (y = 0; y < bheight - 1; y++) {
449                                         p = rectf + (bwidth * y + x);
450                                         *p = MIN2(*p, *(p + bwidth));
451                                 }
452                         }
453                 
454                         for (x = 0; x < bwidth; x++) {
455                                 for (y = bheight - 1; y >= 1; y--) {
456                                         p = rectf + (bwidth * y + x);
457                                         *p = MIN2(*p, *(p - bwidth));
458                                 }
459                         }
460                 }
461                 this->cached_buffer = rectf;
462         }
463         unlockMutex();
464         return this->cached_buffer;
465 }