56fbadcee084d45386ebc1f21329df64e4377067
[blender-staging.git] / intern / cycles / render / session.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 <string.h>
20 #include <limits.h>
21
22 #include "buffers.h"
23 #include "camera.h"
24 #include "device.h"
25 #include "scene.h"
26 #include "session.h"
27
28 #include "util_foreach.h"
29 #include "util_function.h"
30 #include "util_time.h"
31
32 CCL_NAMESPACE_BEGIN
33
34 Session::Session(const SessionParams& params_)
35 : params(params_),
36   tile_manager(params.progressive, params.passes, params.tile_size, params.min_size)
37 {
38         device_use_gl = (params.device_type == DEVICE_CUDA && !params.background);
39
40         device = Device::create(params.device_type, params.background, params.threads);
41         buffers = new RenderBuffers(device);
42         display = new DisplayBuffer(device);
43
44         session_thread = NULL;
45         scene = NULL;
46
47         start_time = 0.0;
48         reset_time = 0.0;
49         preview_time = 0.0;
50         pass = 0;
51
52         delayed_reset.do_reset = false;
53         delayed_reset.w = 0;
54         delayed_reset.h = 0;
55
56         display_outdated = false;
57         gpu_draw_ready = false;
58         gpu_need_tonemap = false;
59 }
60
61 Session::~Session()
62 {
63         if(session_thread) {
64                 progress.set_cancel("Exiting");
65                 gpu_need_tonemap = false;
66                 gpu_need_tonemap_cond.notify_all();
67                 wait();
68         }
69
70         if(params.output_path != "") {
71                 progress.set_status("Writing Image", params.output_path);
72                 display->write(device, params.output_path);
73         }
74
75         delete buffers;
76         delete display;
77         delete scene;
78         delete device;
79 }
80
81 void Session::start()
82 {
83         session_thread = new thread(function_bind(&Session::run, this));
84 }
85
86 bool Session::ready_to_reset()
87 {
88         double dt = time_dt() - reset_time;
89
90         if(!display_outdated)
91                 return (dt > params.reset_timeout);
92         else
93                 return (dt > params.cancel_timeout);
94 }
95
96 /* GPU Session */
97
98 void Session::reset_gpu(int w, int h)
99 {
100         /* block for buffer acces and reset immediately. we can't do this
101            in the thread, because we need to allocate an OpenGL buffer, and
102            that only works in the main thread */
103         thread_scoped_lock display_lock(display->mutex);
104         thread_scoped_lock buffers_lock(buffers->mutex);
105
106         display_outdated = true;
107         reset_time = time_dt();
108
109         reset_(w, h);
110
111         gpu_need_tonemap = false;
112         gpu_need_tonemap_cond.notify_all();
113 }
114
115 bool Session::draw_gpu(int w, int h)
116 {
117         /* block for buffer access */
118         thread_scoped_lock display_lock(display->mutex);
119
120         /* first check we already rendered something */
121         if(gpu_draw_ready) {
122                 /* then verify the buffers have the expected size, so we don't
123                    draw previous results in a resized window */
124                 if(w == display->width && h == display->height) {
125                         /* for CUDA we need to do tonemapping still, since we can
126                            only access GL buffers from the main thread */
127                         if(gpu_need_tonemap) {
128                                 thread_scoped_lock buffers_lock(buffers->mutex);
129                                 tonemap();
130                                 gpu_need_tonemap = false;
131                                 gpu_need_tonemap_cond.notify_all();
132                         }
133
134                         display->draw(device);
135
136                         if(display_outdated && (time_dt() - reset_time) > params.text_timeout)
137                                 return false;
138
139                         return true;
140                 }
141         }
142
143         return false;
144 }
145
146 void Session::run_gpu()
147 {
148         start_time = time_dt();
149         reset_time = time_dt();
150
151         while(!progress.get_cancel() && tile_manager.next()) {
152                 /* buffers mutex is locked entirely while rendering each
153                    pass, and released/reacquired on each iteration to allow
154                    reset and draw in between */
155                 thread_scoped_lock buffers_lock(buffers->mutex);
156
157                 /* update scene */
158                 update_scene();
159                 if(progress.get_cancel())
160                         break;
161
162                 /* update status and timing */
163                 update_status_time();
164
165                 /* path trace */
166                 foreach(Tile& tile, tile_manager.state.tiles) {
167                         path_trace(tile);
168
169                         device->task_wait();
170
171                         if(progress.get_cancel())
172                                 break;
173                 }
174
175                 gpu_need_tonemap = true;
176                 gpu_draw_ready = true;
177                 progress.set_update();
178
179                 /* wait for tonemap */
180                 if(!params.background) {
181                         while(gpu_need_tonemap) {
182                                 if(progress.get_cancel())
183                                         break;
184
185                                 gpu_need_tonemap_cond.wait(buffers_lock);
186                         }
187                 }
188
189                 if(progress.get_cancel())
190                         break;
191         }
192 }
193
194 /* CPU Session */
195
196 void Session::reset_cpu(int w, int h)
197 {
198         thread_scoped_lock reset_lock(delayed_reset.mutex);
199
200         display_outdated = true;
201         reset_time = time_dt();
202
203         delayed_reset.w = w;
204         delayed_reset.h = h;
205         delayed_reset.do_reset = true;
206         device->task_cancel();
207 }
208
209 bool Session::draw_cpu(int w, int h)
210 {
211         thread_scoped_lock display_lock(display->mutex);
212
213         /* first check we already rendered something */
214         if(display->draw_ready()) {
215                 /* then verify the buffers have the expected size, so we don't
216                    draw previous results in a resized window */
217                 if(w == display->width && h == display->height) {
218                         display->draw(device);
219
220                         if(display_outdated && (time_dt() - reset_time) > params.text_timeout)
221                                 return false;
222
223                         return true;
224                 }
225         }
226
227         return false;
228 }
229
230 void Session::run_cpu()
231 {
232         {
233                 /* reset once to start */
234                 thread_scoped_lock reset_lock(delayed_reset.mutex);
235                 thread_scoped_lock buffers_lock(buffers->mutex);
236                 thread_scoped_lock display_lock(display->mutex);
237
238                 reset_(delayed_reset.w, delayed_reset.h);
239                 delayed_reset.do_reset = false;
240         }
241
242         while(!progress.get_cancel() && tile_manager.next()) {
243                 {
244                         thread_scoped_lock buffers_lock(buffers->mutex);
245
246                         /* update scene */
247                         update_scene();
248                         if(progress.get_cancel())
249                                 break;
250
251                         /* update status and timing */
252                         update_status_time();
253
254                         /* path trace all tiles */
255                         foreach(Tile& tile, tile_manager.state.tiles)
256                                 path_trace(tile);
257                 }
258
259                 device->task_wait();
260
261                 {
262                         thread_scoped_lock reset_lock(delayed_reset.mutex);
263                         thread_scoped_lock buffers_lock(buffers->mutex);
264                         thread_scoped_lock display_lock(display->mutex);
265
266                         if(delayed_reset.do_reset) {
267                                 /* reset rendering if request from main thread */
268                                 delayed_reset.do_reset = false;
269                                 reset_(delayed_reset.w, delayed_reset.h);
270                         }
271                         else {
272                                 /* tonemap only if we do not reset, we don't we don't
273                                    want to show the result of an incomplete pass*/
274                                 tonemap();
275                         }
276                 }
277
278                 progress.set_update();
279         }
280 }
281
282 void Session::run()
283 {
284         /* session thread loop */
285         progress.set_status("Waiting for render to start");
286
287         /* first scene update */
288         if(!progress.get_cancel()) {
289                 thread_scoped_lock scene_lock(scene->mutex);
290                 scene->device_update(device, progress);
291         }
292
293         /* run */
294         if(!progress.get_cancel()) {
295                 if(device_use_gl)
296                         run_gpu();
297                 else
298                         run_cpu();
299         }
300
301         /* progress update */
302         if(progress.get_cancel())
303                 progress.set_status(progress.get_cancel_message());
304         else
305                 progress.set_update();
306 }
307
308 bool Session::draw(int w, int h)
309 {
310         if(device_use_gl)
311                 return draw_gpu(w, h);
312         else
313                 return draw_cpu(w, h);
314 }
315
316 void Session::reset_(int w, int h)
317 {
318         if(w != buffers->width || h != buffers->height) {
319                 gpu_draw_ready = false;
320                 buffers->reset(device, w, h);
321                 display->reset(device, w, h);
322         }
323
324         tile_manager.reset(w, h);
325
326         start_time = time_dt();
327         preview_time = 0.0;
328         pass = 0;
329 }
330
331 void Session::reset(int w, int h)
332 {
333         if(device_use_gl)
334                 reset_gpu(w, h);
335         else
336                 reset_cpu(w, h);
337 }
338
339 void Session::wait()
340 {
341         session_thread->join();
342         delete session_thread;
343
344         session_thread = NULL;
345 }
346
347 void Session::update_scene()
348 {
349         thread_scoped_lock scene_lock(scene->mutex);
350
351         progress.set_status("Updating Scene");
352
353         /* update camera if dimensions changed for progressive render */
354         Camera *cam = scene->camera;
355         int w = tile_manager.state.width;
356         int h = tile_manager.state.height;
357
358         if(cam->width != w || cam->height != h) {
359                 cam->width = w;
360                 cam->height = h;
361                 cam->tag_update();
362         }
363
364         /* update scene */
365         if(scene->need_update())
366                 scene->device_update(device, progress);
367 }
368
369 void Session::update_status_time()
370 {
371         int pass = tile_manager.state.pass;
372         int resolution = tile_manager.state.resolution;
373
374         /* update status */
375         string substatus;
376         if(!params.progressive)
377                 substatus = "Path Tracing";
378         else if(params.passes == INT_MAX)
379                 substatus = string_printf("Path Tracing Pass %d", pass+1);
380         else
381                 substatus = string_printf("Path Tracing Pass %d/%d", pass+1, params.passes);
382         progress.set_status("Rendering", substatus);
383
384         /* update timing */
385         if(preview_time == 0.0 && resolution == 1)
386                 preview_time = time_dt();
387
388         double total_time = (time_dt() - start_time);
389         double pass_time = (pass == 0)? 0.0: (time_dt() - preview_time)/(pass);
390
391         progress.set_pass(pass + 1, total_time, pass_time);
392 }
393
394 void Session::path_trace(Tile& tile)
395 {
396         /* add path trace task */
397         DeviceTask task(DeviceTask::PATH_TRACE);
398
399         task.x = tile.x;
400         task.y = tile.y;
401         task.w = tile.w;
402         task.h = tile.h;
403         task.buffer = buffers->buffer.device_pointer;
404         task.rng_state = buffers->rng_state.device_pointer;
405         task.pass = tile_manager.state.pass;
406         task.resolution = tile_manager.state.resolution;
407
408         device->task_add(task);
409 }
410
411 void Session::tonemap()
412 {
413         /* add tonemap task */
414         DeviceTask task(DeviceTask::TONEMAP);
415
416         task.x = 0;
417         task.y = 0;
418         task.w = tile_manager.state.width;
419         task.h = tile_manager.state.height;
420         task.rgba = display->rgba.device_pointer;
421         task.buffer = buffers->buffer.device_pointer;
422         task.pass = tile_manager.state.pass;
423         task.resolution = tile_manager.state.resolution;
424
425         device->task_add(task);
426         device->task_wait();
427
428         /* set display to new size */
429         display->draw_set(tile_manager.state.width, tile_manager.state.height);
430
431         display_outdated = false;
432 }
433
434 CCL_NAMESPACE_END
435