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