2 * Copyright 2011, Blender Foundation.
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.
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.
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.
28 #include "util_foreach.h"
29 #include "util_function.h"
30 #include "util_opengl.h"
31 #include "util_task.h"
32 #include "util_time.h"
36 Session::Session(const SessionParams& params_)
38 tile_manager(params.progressive, params.samples, params.tile_size, params.min_size)
40 device_use_gl = ((params.device.type != DEVICE_CPU) && !params.background);
42 TaskScheduler::init(params.threads);
44 device = Device::create(params.device, params.background, params.threads);
45 buffers = new RenderBuffers(device);
46 display = new DisplayBuffer(device);
48 session_thread = NULL;
57 delayed_reset.do_reset = false;
58 delayed_reset.samples = 0;
60 display_outdated = false;
61 gpu_draw_ready = false;
62 gpu_need_tonemap = false;
64 kernels_loaded = false;
70 progress.set_cancel("Exiting");
72 gpu_need_tonemap = false;
73 gpu_need_tonemap_cond.notify_all();
76 thread_scoped_lock pause_lock(pause_mutex);
79 pause_cond.notify_all();
84 if(params.output_path != "") {
87 progress.set_status("Writing Image", params.output_path);
88 display->write(device, params.output_path);
96 TaskScheduler::exit();
101 session_thread = new thread(function_bind(&Session::run, this));
104 bool Session::ready_to_reset()
106 double dt = time_dt() - reset_time;
108 if(!display_outdated)
109 return (dt > params.reset_timeout);
111 return (dt > params.cancel_timeout);
116 void Session::reset_gpu(BufferParams& buffer_params, int samples)
118 /* block for buffer acces and reset immediately. we can't do this
119 * in the thread, because we need to allocate an OpenGL buffer, and
120 * that only works in the main thread */
121 thread_scoped_lock display_lock(display->mutex);
122 thread_scoped_lock buffers_lock(buffers->mutex);
124 display_outdated = true;
125 reset_time = time_dt();
127 reset_(buffer_params, samples);
129 gpu_need_tonemap = false;
130 gpu_need_tonemap_cond.notify_all();
132 pause_cond.notify_all();
135 bool Session::draw_gpu(BufferParams& buffer_params)
137 /* block for buffer access */
138 thread_scoped_lock display_lock(display->mutex);
140 /* first check we already rendered something */
142 /* then verify the buffers have the expected size, so we don't
143 * draw previous results in a resized window */
144 if(!buffer_params.modified(display->params)) {
145 /* for CUDA we need to do tonemapping still, since we can
146 * only access GL buffers from the main thread */
147 if(gpu_need_tonemap) {
148 thread_scoped_lock buffers_lock(buffers->mutex);
150 gpu_need_tonemap = false;
151 gpu_need_tonemap_cond.notify_all();
154 display->draw(device);
156 if(display_outdated && (time_dt() - reset_time) > params.text_timeout)
166 void Session::run_gpu()
168 start_time = time_dt();
169 reset_time = time_dt();
172 if(!params.background)
173 progress.set_start_time(start_time + paused_time);
175 while(!progress.get_cancel()) {
176 /* advance to next tile */
177 bool no_tiles = !tile_manager.next();
179 if(params.background) {
180 /* if no work left and in background mode, we can stop immediately */
182 progress.set_status("Finished");
187 /* if in interactive mode, and we are either paused or done for now,
188 * wait for pause condition notify to wake up again */
189 thread_scoped_lock pause_lock(pause_mutex);
191 if(pause || no_tiles) {
192 update_status_time(pause, no_tiles);
195 double pause_start = time_dt();
196 pause_cond.wait(pause_lock);
197 paused_time += time_dt() - pause_start;
199 if(!params.background)
200 progress.set_start_time(start_time + paused_time);
202 update_status_time(pause, no_tiles);
203 progress.set_update();
210 if(progress.get_cancel())
218 if(device->error_message() != "")
219 progress.set_cancel(device->error_message());
221 if(progress.get_cancel())
226 /* buffers mutex is locked entirely while rendering each
227 * sample, and released/reacquired on each iteration to allow
228 * reset and draw in between */
229 thread_scoped_lock buffers_lock(buffers->mutex);
231 /* update status and timing */
232 update_status_time();
235 foreach(Tile& tile, tile_manager.state.tiles) {
240 if(device->error_message() != "")
241 progress.set_cancel(device->error_message());
243 if(progress.get_cancel())
247 /* update status and timing */
248 update_status_time();
250 gpu_need_tonemap = true;
251 gpu_draw_ready = true;
252 progress.set_update();
254 /* wait for tonemap */
255 if(!params.background) {
256 while(gpu_need_tonemap) {
257 if(progress.get_cancel())
260 gpu_need_tonemap_cond.wait(buffers_lock);
264 if(device->error_message() != "")
265 progress.set_cancel(device->error_message());
267 if(progress.get_cancel())
275 void Session::reset_cpu(BufferParams& buffer_params, int samples)
277 thread_scoped_lock reset_lock(delayed_reset.mutex);
279 display_outdated = true;
280 reset_time = time_dt();
282 delayed_reset.params = buffer_params;
283 delayed_reset.samples = samples;
284 delayed_reset.do_reset = true;
285 device->task_cancel();
287 pause_cond.notify_all();
290 bool Session::draw_cpu(BufferParams& buffer_params)
292 thread_scoped_lock display_lock(display->mutex);
294 /* first check we already rendered something */
295 if(display->draw_ready()) {
296 /* then verify the buffers have the expected size, so we don't
297 * draw previous results in a resized window */
298 if(!buffer_params.modified(display->params)) {
299 display->draw(device);
301 if(display_outdated && (time_dt() - reset_time) > params.text_timeout)
311 void Session::run_cpu()
314 /* reset once to start */
315 thread_scoped_lock reset_lock(delayed_reset.mutex);
316 thread_scoped_lock buffers_lock(buffers->mutex);
317 thread_scoped_lock display_lock(display->mutex);
319 reset_(delayed_reset.params, delayed_reset.samples);
320 delayed_reset.do_reset = false;
323 while(!progress.get_cancel()) {
324 /* advance to next tile */
325 bool no_tiles = !tile_manager.next();
326 bool need_tonemap = false;
328 if(params.background) {
329 /* if no work left and in background mode, we can stop immediately */
331 progress.set_status("Finished");
336 /* if in interactive mode, and we are either paused or done for now,
337 * wait for pause condition notify to wake up again */
338 thread_scoped_lock pause_lock(pause_mutex);
340 if(pause || no_tiles) {
341 update_status_time(pause, no_tiles);
344 double pause_start = time_dt();
345 pause_cond.wait(pause_lock);
346 paused_time += time_dt() - pause_start;
348 if(!params.background)
349 progress.set_start_time(start_time + paused_time);
351 update_status_time(pause, no_tiles);
352 progress.set_update();
359 if(progress.get_cancel())
364 /* buffers mutex is locked entirely while rendering each
365 * sample, and released/reacquired on each iteration to allow
366 * reset and draw in between */
367 thread_scoped_lock buffers_lock(buffers->mutex);
372 if(device->error_message() != "")
373 progress.set_cancel(device->error_message());
375 if(progress.get_cancel())
378 /* update status and timing */
379 update_status_time();
382 foreach(Tile& tile, tile_manager.state.tiles)
385 /* update status and timing */
386 update_status_time();
388 if(!params.background)
391 if(device->error_message() != "")
392 progress.set_cancel(device->error_message());
398 thread_scoped_lock reset_lock(delayed_reset.mutex);
399 thread_scoped_lock buffers_lock(buffers->mutex);
400 thread_scoped_lock display_lock(display->mutex);
402 if(delayed_reset.do_reset) {
403 /* reset rendering if request from main thread */
404 delayed_reset.do_reset = false;
405 reset_(delayed_reset.params, delayed_reset.samples);
407 else if(need_tonemap) {
408 /* tonemap only if we do not reset, we don't we don't
409 * wan't to show the result of an incomplete sample*/
413 if(device->error_message() != "")
414 progress.set_cancel(device->error_message());
417 progress.set_update();
424 if(!kernels_loaded) {
425 progress.set_status("Loading render kernels (may take a few minutes the first time)");
427 if(!device->load_kernels(params.experimental)) {
428 string message = device->error_message();
430 message = "Failed loading render kernel, see console for errors";
432 progress.set_status("Error", message);
433 progress.set_update();
437 kernels_loaded = true;
440 /* session thread loop */
441 progress.set_status("Waiting for render to start");
444 if(!progress.get_cancel()) {
451 /* progress update */
452 if(progress.get_cancel())
453 progress.set_status("Cancel", progress.get_cancel_message());
455 progress.set_update();
458 bool Session::draw(BufferParams& buffer_params)
461 return draw_gpu(buffer_params);
463 return draw_cpu(buffer_params);
466 void Session::reset_(BufferParams& buffer_params, int samples)
468 if(buffer_params.modified(buffers->params)) {
469 gpu_draw_ready = false;
470 buffers->reset(device, buffer_params);
471 display->reset(device, buffer_params);
474 tile_manager.reset(buffer_params, samples);
476 start_time = time_dt();
481 if(!params.background)
482 progress.set_start_time(start_time + paused_time);
485 void Session::reset(BufferParams& buffer_params, int samples)
488 reset_gpu(buffer_params, samples);
490 reset_cpu(buffer_params, samples);
493 void Session::set_samples(int samples)
495 if(samples != params.samples) {
496 params.samples = samples;
497 tile_manager.set_samples(samples);
500 thread_scoped_lock pause_lock(pause_mutex);
502 pause_cond.notify_all();
506 void Session::set_pause(bool pause_)
511 thread_scoped_lock pause_lock(pause_mutex);
513 if(pause != pause_) {
520 pause_cond.notify_all();
525 session_thread->join();
526 delete session_thread;
528 session_thread = NULL;
531 void Session::update_scene()
533 thread_scoped_lock scene_lock(scene->mutex);
535 progress.set_status("Updating Scene");
537 /* update camera if dimensions changed for progressive render. the camera
538 * knows nothing about progressive or cropped rendering, it just gets the
539 * image dimensions passed in */
540 Camera *cam = scene->camera;
541 int width = tile_manager.state.buffer.full_width;
542 int height = tile_manager.state.buffer.full_height;
544 if(width != cam->width || height != cam->height) {
546 cam->height = height;
551 if(scene->need_update())
552 scene->device_update(device, progress);
555 void Session::update_status_time(bool show_pause, bool show_done)
557 int sample = tile_manager.state.sample;
558 int resolution = tile_manager.state.resolution;
561 string status, substatus;
563 if(!params.progressive)
564 substatus = "Path Tracing";
565 else if(params.samples == INT_MAX)
566 substatus = string_printf("Path Tracing Sample %d", sample+1);
568 substatus = string_printf("Path Tracing Sample %d/%d", sample+1, params.samples);
575 status = "Rendering";
577 progress.set_status(status, substatus);
580 if(preview_time == 0.0 && resolution == 1)
581 preview_time = time_dt();
583 double sample_time = (sample == 0)? 0.0: (time_dt() - preview_time - paused_time)/(sample);
585 /* negative can happen when we pause a bit before rendering, can discard that */
586 if(preview_time < 0.0) preview_time = 0.0;
588 progress.set_sample(sample + 1, sample_time);
591 void Session::path_trace(Tile& tile)
593 /* add path trace task */
594 DeviceTask task(DeviceTask::PATH_TRACE);
596 task.x = tile_manager.state.buffer.full_x + tile.x;
597 task.y = tile_manager.state.buffer.full_y + tile.y;
600 task.buffer = buffers->buffer.device_pointer;
601 task.rng_state = buffers->rng_state.device_pointer;
602 task.sample = tile_manager.state.sample;
603 task.resolution = tile_manager.state.resolution;
604 tile_manager.state.buffer.get_offset_stride(task.offset, task.stride);
606 device->task_add(task);
609 void Session::tonemap()
611 /* add tonemap task */
612 DeviceTask task(DeviceTask::TONEMAP);
614 task.x = tile_manager.state.buffer.full_x;
615 task.y = tile_manager.state.buffer.full_y;
616 task.w = tile_manager.state.buffer.width;
617 task.h = tile_manager.state.buffer.height;
618 task.rgba = display->rgba.device_pointer;
619 task.buffer = buffers->buffer.device_pointer;
620 task.sample = tile_manager.state.sample;
621 task.resolution = tile_manager.state.resolution;
622 tile_manager.state.buffer.get_offset_stride(task.offset, task.stride);
624 if(task.w > 0 && task.h > 0) {
625 device->task_add(task);
628 /* set display to new size */
629 display->draw_set(task.w, task.h);
632 display_outdated = false;