Refactoring of tiles opencl implementation:
[blender.git] / source / blender / compositor / operations / COM_BokehBlurOperation.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_BokehBlurOperation.h"
24 #include "BLI_math.h"
25 #include "COM_OpenCLDevice.h"
26
27 extern "C" {
28         #include "RE_pipeline.h"
29 }
30
31 BokehBlurOperation::BokehBlurOperation() : NodeOperation()
32 {
33         this->addInputSocket(COM_DT_COLOR);
34         this->addInputSocket(COM_DT_COLOR, COM_SC_NO_RESIZE);
35         this->addInputSocket(COM_DT_VALUE);
36         this->addOutputSocket(COM_DT_COLOR);
37         this->setComplex(true);
38         this->setOpenCL(true);
39
40         this->size = 1.0f;
41
42         this->inputProgram = NULL;
43         this->inputBokehProgram = NULL;
44         this->inputBoundingBoxReader = NULL;
45 }
46
47 void *BokehBlurOperation::initializeTileData(rcti *rect, MemoryBuffer **memoryBuffers)
48 {
49         void *buffer = getInputOperation(0)->initializeTileData(NULL, memoryBuffers);
50         return buffer;
51 }
52
53 void BokehBlurOperation::initExecution()
54 {
55         this->inputProgram = getInputSocketReader(0);
56         this->inputBokehProgram = getInputSocketReader(1);
57         this->inputBoundingBoxReader = getInputSocketReader(2);
58
59         int width = inputBokehProgram->getWidth();
60         int height = inputBokehProgram->getHeight();
61
62         float dimension;
63         if (width < height) {
64                 dimension = width;
65         }
66         else {
67                 dimension = height;
68         }
69         this->bokehMidX = width / 2.0f;
70         this->bokehMidY = height / 2.0f;
71         this->bokehDimension = dimension / 2.0f;
72         QualityStepHelper::initExecution(COM_QH_INCREASE);
73 }
74
75 void BokehBlurOperation::executePixel(float *color, int x, int y, MemoryBuffer *inputBuffers[], void *data)
76 {
77         float color_accum[4];
78         float tempBoundingBox[4];
79         float bokeh[4];
80
81         inputBoundingBoxReader->read(tempBoundingBox, x, y, COM_PS_NEAREST, inputBuffers);
82         if (tempBoundingBox[0] > 0.0f) {
83                 float multiplier_accum[4] = {0.0f, 0.0f, 0.0f, 0.0f};
84                 MemoryBuffer *inputBuffer = (MemoryBuffer *)data;
85                 float *buffer = inputBuffer->getBuffer();
86                 int bufferwidth = inputBuffer->getWidth();
87                 int bufferstartx = inputBuffer->getRect()->xmin;
88                 int bufferstarty = inputBuffer->getRect()->ymin;
89                 int pixelSize = this->size * this->getWidth() / 100.0f;
90
91                 int miny = y - pixelSize;
92                 int maxy = y + pixelSize;
93                 int minx = x - pixelSize;
94                 int maxx = x + pixelSize;
95                 miny = max(miny, inputBuffer->getRect()->ymin);
96                 minx = max(minx, inputBuffer->getRect()->xmin);
97                 maxy = min(maxy, inputBuffer->getRect()->ymax);
98                 maxx = min(maxx, inputBuffer->getRect()->xmax);
99
100                 zero_v4(color_accum);
101
102                 int step = getStep();
103                 int offsetadd = getOffsetAdd();
104
105                 float m = this->bokehDimension / pixelSize;
106                 for (int ny = miny; ny < maxy; ny += step) {
107                         int bufferindex = ((minx - bufferstartx) * 4) + ((ny - bufferstarty) * 4 * bufferwidth);
108                         for (int nx = minx; nx < maxx; nx += step) {
109                                 float u = this->bokehMidX - (nx - x) * m;
110                                 float v = this->bokehMidY - (ny - y) * m;
111                                 inputBokehProgram->read(bokeh, u, v, COM_PS_NEAREST, inputBuffers);
112                                 madd_v4_v4v4(color_accum, bokeh, &buffer[bufferindex]);
113                                 add_v4_v4(multiplier_accum, bokeh);
114                                 bufferindex += offsetadd;
115                         }
116                 }
117                 color[0] = color_accum[0] * (1.0f / multiplier_accum[0]);
118                 color[1] = color_accum[1] * (1.0f / multiplier_accum[1]);
119                 color[2] = color_accum[2] * (1.0f / multiplier_accum[2]);
120                 color[3] = color_accum[3] * (1.0f / multiplier_accum[3]);
121         }
122         else {
123                 inputProgram->read(color, x, y, COM_PS_NEAREST, inputBuffers);
124         }
125 }
126
127 void BokehBlurOperation::deinitExecution()
128 {
129         this->inputProgram = NULL;
130         this->inputBokehProgram = NULL;
131         this->inputBoundingBoxReader = NULL;
132 }
133
134 bool BokehBlurOperation::determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output)
135 {
136         rcti newInput;
137         rcti bokehInput;
138
139         newInput.xmax = input->xmax + (size * this->getWidth() / 100.0f);
140         newInput.xmin = input->xmin - (size * this->getWidth() / 100.0f);
141         newInput.ymax = input->ymax + (size * this->getWidth() / 100.0f);
142         newInput.ymin = input->ymin - (size * this->getWidth() / 100.0f);
143
144         NodeOperation *operation = getInputOperation(1);
145         bokehInput.xmax = operation->getWidth();
146         bokehInput.xmin = 0;
147         bokehInput.ymax = operation->getHeight();
148         bokehInput.ymin = 0;
149         if (operation->determineDependingAreaOfInterest(&bokehInput, readOperation, output) ) {
150                 return true;
151         }
152         operation = getInputOperation(0);
153         if (operation->determineDependingAreaOfInterest(&newInput, readOperation, output) ) {
154                 return true;
155         }
156         operation = getInputOperation(2);
157         if (operation->determineDependingAreaOfInterest(input, readOperation, output) ) {
158                 return true;
159         }
160         return false;
161 }
162
163 static cl_kernel kernel = 0;
164 void BokehBlurOperation::executeOpenCL(OpenCLDevice* device,
165                                        MemoryBuffer *outputMemoryBuffer, cl_mem clOutputBuffer, 
166                                        MemoryBuffer **inputMemoryBuffers, list<cl_mem> *clMemToCleanUp, 
167                                        list<cl_kernel> *clKernelsToCleanUp) 
168 {
169         if (!kernel) {
170                 kernel = device->COM_clCreateKernel("bokehBlurKernel", NULL);
171         }
172         cl_int radius = this->getWidth() * this->size / 100.0f;
173         cl_int step = this->getStep();
174         
175         device->COM_clAttachMemoryBufferToKernelParameter(kernel, 0, -1, clMemToCleanUp, inputMemoryBuffers, this->inputBoundingBoxReader);
176         device->COM_clAttachMemoryBufferToKernelParameter(kernel, 1,  4, clMemToCleanUp, inputMemoryBuffers, this->inputProgram);
177         device->COM_clAttachMemoryBufferToKernelParameter(kernel, 2,  -1, clMemToCleanUp, inputMemoryBuffers, this->inputBokehProgram);
178         device->COM_clAttachOutputMemoryBufferToKernelParameter(kernel, 3, clOutputBuffer);
179         device->COM_clAttachMemoryBufferOffsetToKernelParameter(kernel, 5, outputMemoryBuffer);
180         clSetKernelArg(kernel, 6, sizeof(cl_int), &radius);
181         clSetKernelArg(kernel, 7, sizeof(cl_int), &step);
182         device->COM_clAttachSizeToKernelParameter(kernel, 8, this);
183         
184         device->COM_clEnqueueRange(kernel, outputMemoryBuffer, 9, this);
185 }