a79a3591e0f4fa584de00d38ff5147a323cb2a56
[blender-staging.git] / intern / cycles / render / buffers.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
19 #include <stdlib.h>
20
21 #include "buffers.h"
22 #include "device.h"
23
24 #include "util_debug.h"
25 #include "util_foreach.h"
26 #include "util_hash.h"
27 #include "util_image.h"
28 #include "util_math.h"
29 #include "util_opengl.h"
30 #include "util_time.h"
31 #include "util_types.h"
32
33 CCL_NAMESPACE_BEGIN
34
35 /* Buffer Params */
36
37 BufferParams::BufferParams()
38 {
39         width = 0;
40         height = 0;
41
42         full_x = 0;
43         full_y = 0;
44         full_width = 0;
45         full_height = 0;
46
47         Pass::add(PASS_COMBINED, passes);
48 }
49
50 void BufferParams::get_offset_stride(int& offset, int& stride)
51 {
52         offset = -(full_x + full_y*width);
53         stride = width;
54 }
55
56 bool BufferParams::modified(const BufferParams& params)
57 {
58         return !(full_x == params.full_x
59                 && full_y == params.full_y
60                 && width == params.width
61                 && height == params.height
62                 && full_width == params.full_width
63                 && full_height == params.full_height
64                 && Pass::equals(passes, params.passes));
65 }
66
67 int BufferParams::get_passes_size()
68 {
69         int size = 0;
70
71         foreach(Pass& pass, passes)
72                 size += pass.components;
73         
74         return align_up(size, 4);
75 }
76
77 /* Render Buffers */
78
79 RenderBuffers::RenderBuffers(Device *device_)
80 {
81         device = device_;
82 }
83
84 RenderBuffers::~RenderBuffers()
85 {
86         device_free();
87 }
88
89 void RenderBuffers::device_free()
90 {
91         if(buffer.device_pointer) {
92                 device->mem_free(buffer);
93                 buffer.clear();
94         }
95
96         if(rng_state.device_pointer) {
97                 device->mem_free(rng_state);
98                 rng_state.clear();
99         }
100 }
101
102 void RenderBuffers::reset(Device *device, BufferParams& params_)
103 {
104         params = params_;
105
106         /* free existing buffers */
107         device_free();
108         
109         /* allocate buffer */
110         buffer.resize(params.width*params.height*params.get_passes_size());
111         device->mem_alloc(buffer, MEM_READ_WRITE);
112         device->mem_zero(buffer);
113
114         /* allocate rng state */
115         rng_state.resize(params.width, params.height);
116
117         uint *init_state = rng_state.resize(params.width, params.height);
118         int x, y, width = params.width, height = params.height;
119         
120         for(x = 0; x < width; x++)
121                 for(y = 0; y < height; y++)
122                         init_state[x + y*width] = hash_int_2d(params.full_x+x, params.full_y+y);
123
124         device->mem_alloc(rng_state, MEM_READ_WRITE);
125         device->mem_copy_to(rng_state);
126 }
127
128 bool RenderBuffers::copy_from_device()
129 {
130         if(!buffer.device_pointer)
131                 return false;
132
133         device->mem_copy_from(buffer, 0, params.width, params.height, params.get_passes_size()*sizeof(float));
134
135         return true;
136 }
137
138 bool RenderBuffers::get_pass(PassType type, float exposure, int sample, int components, float *pixels)
139 {
140         int pass_offset = 0;
141
142         foreach(Pass& pass, params.passes) {
143                 if(pass.type != type) {
144                         pass_offset += pass.components;
145                         continue;
146                 }
147
148                 float *in = (float*)buffer.data_pointer + pass_offset;
149                 int pass_stride = params.get_passes_size();
150
151                 float scale = (pass.filter)? 1.0f/(float)sample: 1.0f;
152                 float scale_exposure = (pass.exposure)? scale*exposure: scale;
153
154                 int size = params.width*params.height;
155
156                 if(components == 1) {
157                         assert(pass.components == components);
158
159                         /* scalar */
160                         if(type == PASS_DEPTH) {
161                                 for(int i = 0; i < size; i++, in += pass_stride, pixels++) {
162                                         float f = *in;
163                                         pixels[0] = (f == 0.0f)? 1e10f: f*scale_exposure;
164                                 }
165                         }
166                         else {
167                                 for(int i = 0; i < size; i++, in += pass_stride, pixels++) {
168                                         float f = *in;
169                                         pixels[0] = f*scale_exposure;
170                                 }
171                         }
172                 }
173                 else if(components == 3) {
174                         assert(pass.components == 4);
175
176                         if(pass.divide_type != PASS_NONE) {
177                                 /* RGB lighting passes that need to divide out color */
178                                 pass_offset = 0;
179                                 foreach(Pass& color_pass, params.passes) {
180                                         if(color_pass.type == pass.divide_type)
181                                                 break;
182                                         pass_offset += color_pass.components;
183                                 }
184
185                                 float *in_divide = (float*)buffer.data_pointer + pass_offset;
186
187                                 for(int i = 0; i < size; i++, in += pass_stride, in_divide += pass_stride, pixels += 3) {
188                                         float3 f = make_float3(in[0], in[1], in[2]);
189                                         float3 f_divide = make_float3(in_divide[0], in_divide[1], in_divide[2]);
190
191                                         f = safe_divide_color(f*exposure, f_divide);
192
193                                         pixels[0] = f.x;
194                                         pixels[1] = f.y;
195                                         pixels[2] = f.z;
196                                 }
197                         }
198                         else {
199                                 /* RGB/vector */
200                                 for(int i = 0; i < size; i++, in += pass_stride, pixels += 3) {
201                                         float3 f = make_float3(in[0], in[1], in[2]);
202
203                                         pixels[0] = f.x*scale_exposure;
204                                         pixels[1] = f.y*scale_exposure;
205                                         pixels[2] = f.z*scale_exposure;
206                                 }
207                         }
208                 }
209                 else if(components == 4) {
210                         assert(pass.components == components);
211
212                         /* RGBA */
213                         if(type == PASS_SHADOW) {
214                                 for(int i = 0; i < size; i++, in += pass_stride, pixels += 4) {
215                                         float4 f = make_float4(in[0], in[1], in[2], in[3]);
216                                         float invw = (f.w > 0.0f)? 1.0f/f.w: 1.0f;
217
218                                         pixels[0] = f.x*invw;
219                                         pixels[1] = f.y*invw;
220                                         pixels[2] = f.z*invw;
221                                         pixels[3] = 1.0f;
222                                 }
223                         }
224                         else if(type == PASS_MOTION) {
225                                 /* need to normalize by number of samples accumulated for motion */
226                                 pass_offset = 0;
227                                 foreach(Pass& color_pass, params.passes) {
228                                         if(color_pass.type == PASS_MOTION_WEIGHT)
229                                                 break;
230                                         pass_offset += color_pass.components;
231                                 }
232
233                                 float *in_weight = (float*)buffer.data_pointer + pass_offset;
234
235                                 for(int i = 0; i < size; i++, in += pass_stride, in_weight += pass_stride, pixels += 4) {
236                                         float4 f = make_float4(in[0], in[1], in[2], in[3]);
237                                         float w = in_weight[0];
238                                         float invw = (w > 0.0f)? 1.0f/w: 0.0f;
239
240                                         pixels[0] = f.x*invw;
241                                         pixels[1] = f.y*invw;
242                                         pixels[2] = f.z*invw;
243                                         pixels[3] = f.w*invw;
244                                 }
245                         }
246                         else {
247                                 for(int i = 0; i < size; i++, in += pass_stride, pixels += 4) {
248                                         float4 f = make_float4(in[0], in[1], in[2], in[3]);
249
250                                         pixels[0] = f.x*scale_exposure;
251                                         pixels[1] = f.y*scale_exposure;
252                                         pixels[2] = f.z*scale_exposure;
253
254                                         /* clamp since alpha might be > 1.0 due to russian roulette */
255                                         pixels[3] = clamp(f.w*scale, 0.0f, 1.0f);
256                                 }
257                         }
258                 }
259
260                 return true;
261         }
262
263         return false;
264 }
265
266 /* Display Buffer */
267
268 DisplayBuffer::DisplayBuffer(Device *device_)
269 {
270         device = device_;
271         draw_width = 0;
272         draw_height = 0;
273         transparent = true; /* todo: determine from background */
274 }
275
276 DisplayBuffer::~DisplayBuffer()
277 {
278         device_free();
279 }
280
281 void DisplayBuffer::device_free()
282 {
283         if(rgba.device_pointer) {
284                 device->pixels_free(rgba);
285                 rgba.clear();
286         }
287 }
288
289 void DisplayBuffer::reset(Device *device, BufferParams& params_)
290 {
291         draw_width = 0;
292         draw_height = 0;
293
294         params = params_;
295
296         /* free existing buffers */
297         device_free();
298
299         /* allocate display pixels */
300         rgba.resize(params.width, params.height);
301         device->pixels_alloc(rgba);
302 }
303
304 void DisplayBuffer::draw_set(int width, int height)
305 {
306         assert(width <= params.width && height <= params.height);
307
308         draw_width = width;
309         draw_height = height;
310 }
311
312 void DisplayBuffer::draw(Device *device)
313 {
314         if(draw_width != 0 && draw_height != 0) {
315                 glPushMatrix();
316                 glTranslatef(params.full_x, params.full_y, 0.0f);
317
318                 device->draw_pixels(rgba, 0, draw_width, draw_height, 0, params.width, params.height, transparent);
319
320                 glPopMatrix();
321         }
322 }
323
324 bool DisplayBuffer::draw_ready()
325 {
326         return (draw_width != 0 && draw_height != 0);
327 }
328
329 void DisplayBuffer::write(Device *device, const string& filename)
330 {
331         int w = draw_width;
332         int h = draw_height;
333
334         if(w == 0 || h == 0)
335                 return;
336
337         /* read buffer from device */
338         device->pixels_copy_from(rgba, 0, w, h);
339
340         /* write image */
341         ImageOutput *out = ImageOutput::create(filename);
342         ImageSpec spec(w, h, 4, TypeDesc::UINT8);
343         int scanlinesize = w*4*sizeof(uchar);
344
345         out->open(filename, spec);
346
347         /* conversion for different top/bottom convention */
348         out->write_image(TypeDesc::UINT8,
349                 (uchar*)rgba.data_pointer + (h-1)*scanlinesize,
350                 AutoStride,
351                 -scanlinesize,
352                 AutoStride);
353
354         out->close();
355
356         delete out;
357 }
358
359 CCL_NAMESPACE_END
360