Fixed [#32226] Black cadioptric lenses in CPU BokehBlur node
[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->addInputSocket(COM_DT_VALUE);
37         this->addOutputSocket(COM_DT_COLOR);
38         this->setComplex(true);
39         this->setOpenCL(true);
40
41         this->m_size = 1.0f;
42         this->m_sizeavailable = false;
43         this->m_inputProgram = NULL;
44         this->m_inputBokehProgram = NULL;
45         this->m_inputBoundingBoxReader = NULL;
46 }
47
48 void *BokehBlurOperation::initializeTileData(rcti *rect)
49 {
50         lockMutex();
51         if (!this->m_sizeavailable) {
52                 updateSize();
53         }
54         void *buffer = getInputOperation(0)->initializeTileData(NULL);
55         unlockMutex();
56         return buffer;
57 }
58
59 void BokehBlurOperation::initExecution()
60 {
61         initMutex();
62         this->m_inputProgram = getInputSocketReader(0);
63         this->m_inputBokehProgram = getInputSocketReader(1);
64         this->m_inputBoundingBoxReader = getInputSocketReader(2);
65
66         int width = this->m_inputBokehProgram->getWidth();
67         int height = this->m_inputBokehProgram->getHeight();
68
69         float dimension;
70         if (width < height) {
71                 dimension = width;
72         }
73         else {
74                 dimension = height;
75         }
76         this->m_bokehMidX = width / 2.0f;
77         this->m_bokehMidY = height / 2.0f;
78         this->m_bokehDimension = dimension / 2.0f;
79         QualityStepHelper::initExecution(COM_QH_INCREASE);
80 }
81
82 void BokehBlurOperation::executePixel(float *color, int x, int y, void *data)
83 {
84         float color_accum[4];
85         float tempBoundingBox[4];
86         float bokeh[4];
87
88         this->m_inputBoundingBoxReader->read(tempBoundingBox, x, y, COM_PS_NEAREST);
89         if (tempBoundingBox[0] > 0.0f) {
90                 float multiplier_accum[4] = {0.0f, 0.0f, 0.0f, 0.0f};
91                 MemoryBuffer *inputBuffer = (MemoryBuffer *)data;
92                 float *buffer = inputBuffer->getBuffer();
93                 int bufferwidth = inputBuffer->getWidth();
94                 int bufferstartx = inputBuffer->getRect()->xmin;
95                 int bufferstarty = inputBuffer->getRect()->ymin;
96                 int pixelSize = this->m_size * this->getWidth() / 100.0f;
97                 zero_v4(color_accum);
98
99                 if (pixelSize<2) {
100                         this->m_inputProgram->read(color_accum, x, y, COM_PS_NEAREST);
101                         multiplier_accum[0] = 1.0f;
102                         multiplier_accum[1] = 1.0f;
103                         multiplier_accum[2] = 1.0f;
104                         multiplier_accum[3] = 1.0f;
105                 }
106                 int miny = y - pixelSize;
107                 int maxy = y + pixelSize;
108                 int minx = x - pixelSize;
109                 int maxx = x + pixelSize;
110                 miny = max(miny, inputBuffer->getRect()->ymin);
111                 minx = max(minx, inputBuffer->getRect()->xmin);
112                 maxy = min(maxy, inputBuffer->getRect()->ymax);
113                 maxx = min(maxx, inputBuffer->getRect()->xmax);
114
115
116                 int step = getStep();
117                 int offsetadd = getOffsetAdd();
118
119                 float m = this->m_bokehDimension / pixelSize;
120                 for (int ny = miny; ny < maxy; ny += step) {
121                         int bufferindex = ((minx - bufferstartx) * 4) + ((ny - bufferstarty) * 4 * bufferwidth);
122                         for (int nx = minx; nx < maxx; nx += step) {
123                                 float u = this->m_bokehMidX - (nx - x) * m;
124                                 float v = this->m_bokehMidY - (ny - y) * m;
125                                 this->m_inputBokehProgram->read(bokeh, u, v, COM_PS_NEAREST);
126                                 madd_v4_v4v4(color_accum, bokeh, &buffer[bufferindex]);
127                                 add_v4_v4(multiplier_accum, bokeh);
128                                 bufferindex += offsetadd;
129                         }
130                 }
131                 color[0] = color_accum[0] * (1.0f / multiplier_accum[0]);
132                 color[1] = color_accum[1] * (1.0f / multiplier_accum[1]);
133                 color[2] = color_accum[2] * (1.0f / multiplier_accum[2]);
134                 color[3] = color_accum[3] * (1.0f / multiplier_accum[3]);
135         }
136         else {
137                 this->m_inputProgram->read(color, x, y, COM_PS_NEAREST);
138         }
139 }
140
141 void BokehBlurOperation::deinitExecution()
142 {
143         deinitMutex();
144         this->m_inputProgram = NULL;
145         this->m_inputBokehProgram = NULL;
146         this->m_inputBoundingBoxReader = NULL;
147 }
148
149 bool BokehBlurOperation::determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output)
150 {
151         rcti newInput;
152         rcti bokehInput;
153
154         if (this->m_sizeavailable) {
155                 newInput.xmax = input->xmax + (this->m_size * this->getWidth() / 100.0f);
156                 newInput.xmin = input->xmin - (this->m_size * this->getWidth() / 100.0f);
157                 newInput.ymax = input->ymax + (this->m_size * this->getWidth() / 100.0f);
158                 newInput.ymin = input->ymin - (this->m_size * this->getWidth() / 100.0f);
159         } else {
160                 newInput.xmax = input->xmax + (10.0f * this->getWidth() / 100.0f);
161                 newInput.xmin = input->xmin - (10.0f * this->getWidth() / 100.0f);
162                 newInput.ymax = input->ymax + (10.0f * this->getWidth() / 100.0f);
163                 newInput.ymin = input->ymin - (10.0f * this->getWidth() / 100.0f);
164         }
165
166         NodeOperation *operation = getInputOperation(1);
167         bokehInput.xmax = operation->getWidth();
168         bokehInput.xmin = 0;
169         bokehInput.ymax = operation->getHeight();
170         bokehInput.ymin = 0;
171         if (operation->determineDependingAreaOfInterest(&bokehInput, readOperation, output) ) {
172                 return true;
173         }
174         operation = getInputOperation(0);
175         if (operation->determineDependingAreaOfInterest(&newInput, readOperation, output) ) {
176                 return true;
177         }
178         operation = getInputOperation(2);
179         if (operation->determineDependingAreaOfInterest(input, readOperation, output) ) {
180                 return true;
181         }
182         if (!this->m_sizeavailable) {
183                 rcti sizeInput;
184                 sizeInput.xmin = 0;
185                 sizeInput.ymin = 0;
186                 sizeInput.xmax = 5;
187                 sizeInput.ymax = 5;
188                 operation = getInputOperation(3);
189                 if (operation->determineDependingAreaOfInterest(&sizeInput, readOperation, output) ) {
190                         return true;
191                 }
192         }
193         return false;
194 }
195
196 void BokehBlurOperation::executeOpenCL(OpenCLDevice* device,
197                                        MemoryBuffer *outputMemoryBuffer, cl_mem clOutputBuffer, 
198                                        MemoryBuffer **inputMemoryBuffers, list<cl_mem> *clMemToCleanUp, 
199                                        list<cl_kernel> *clKernelsToCleanUp) 
200 {
201         cl_kernel kernel = device->COM_clCreateKernel("bokehBlurKernel", NULL);
202         if (!this->m_sizeavailable) {
203                 updateSize();
204         }
205         cl_int radius = this->getWidth() * this->m_size / 100.0f;
206         cl_int step = this->getStep();
207         
208         device->COM_clAttachMemoryBufferToKernelParameter(kernel, 0, -1, clMemToCleanUp, inputMemoryBuffers, this->m_inputBoundingBoxReader);
209         device->COM_clAttachMemoryBufferToKernelParameter(kernel, 1,  4, clMemToCleanUp, inputMemoryBuffers, this->m_inputProgram);
210         device->COM_clAttachMemoryBufferToKernelParameter(kernel, 2,  -1, clMemToCleanUp, inputMemoryBuffers, this->m_inputBokehProgram);
211         device->COM_clAttachOutputMemoryBufferToKernelParameter(kernel, 3, clOutputBuffer);
212         device->COM_clAttachMemoryBufferOffsetToKernelParameter(kernel, 5, outputMemoryBuffer);
213         clSetKernelArg(kernel, 6, sizeof(cl_int), &radius);
214         clSetKernelArg(kernel, 7, sizeof(cl_int), &step);
215         device->COM_clAttachSizeToKernelParameter(kernel, 8, this);
216         
217         device->COM_clEnqueueRange(kernel, outputMemoryBuffer, 9, this);
218 }
219
220 void BokehBlurOperation::updateSize()
221 {
222         if (!this->m_sizeavailable) {
223                 float result[4];
224                 this->getInputSocketReader(3)->read(result, 0, 0, COM_PS_NEAREST);
225                 this->m_size = result[0];
226                 CLAMP(this->m_size, 0.0f, 10.0f);
227                 this->m_sizeavailable = true;
228         }
229 }