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_math.h"
31 #include "util_opengl.h"
32 #include "util_task.h"
33 #include "util_time.h"
37 /* Note about preserve_tile_device option for tile manager:
38 * progressive refine and viewport rendering does requires tiles to
39 * always be allocated for the same device
41 Session::Session(const SessionParams& params_)
43 tile_manager(params.progressive, params.samples, params.tile_size, params.start_resolution,
44 params.background == false || params.progressive_refine, params.background,
45 max(params.device.multi_devices.size(), 1)),
48 device_use_gl = ((params.device.type != DEVICE_CPU) && !params.background);
50 TaskScheduler::init(params.threads);
52 device = Device::create(params.device, stats, params.background, params.threads);
54 if(params.background) {
59 buffers = new RenderBuffers(device);
60 display = new DisplayBuffer(device);
63 session_thread = NULL;
71 delayed_reset.do_reset = false;
72 delayed_reset.samples = 0;
74 display_outdated = false;
75 gpu_draw_ready = false;
76 gpu_need_tonemap = false;
78 kernels_loaded = false;
84 progress.set_cancel("Exiting");
86 gpu_need_tonemap = false;
87 gpu_need_tonemap_cond.notify_all();
90 thread_scoped_lock pause_lock(pause_mutex);
93 pause_cond.notify_all();
98 if(display && params.output_path != "") {
101 progress.set_status("Writing Image", params.output_path);
102 display->write(device, params.output_path);
105 foreach(RenderBuffers *buffers, tile_buffers)
113 TaskScheduler::exit();
116 void Session::start()
118 session_thread = new thread(function_bind(&Session::run, this));
121 bool Session::ready_to_reset()
123 double dt = time_dt() - reset_time;
125 if(!display_outdated)
126 return (dt > params.reset_timeout);
128 return (dt > params.cancel_timeout);
133 void Session::reset_gpu(BufferParams& buffer_params, int samples)
135 /* block for buffer acces and reset immediately. we can't do this
136 * in the thread, because we need to allocate an OpenGL buffer, and
137 * that only works in the main thread */
138 thread_scoped_lock display_lock(display_mutex);
139 thread_scoped_lock buffers_lock(buffers_mutex);
141 display_outdated = true;
142 reset_time = time_dt();
144 reset_(buffer_params, samples);
146 gpu_need_tonemap = false;
147 gpu_need_tonemap_cond.notify_all();
149 pause_cond.notify_all();
152 bool Session::draw_gpu(BufferParams& buffer_params)
154 /* block for buffer access */
155 thread_scoped_lock display_lock(display_mutex);
157 /* first check we already rendered something */
159 /* then verify the buffers have the expected size, so we don't
160 * draw previous results in a resized window */
161 if(!buffer_params.modified(display->params)) {
162 /* for CUDA we need to do tonemapping still, since we can
163 * only access GL buffers from the main thread */
164 if(gpu_need_tonemap) {
165 thread_scoped_lock buffers_lock(buffers_mutex);
167 gpu_need_tonemap = false;
168 gpu_need_tonemap_cond.notify_all();
171 display->draw(device);
173 if(display_outdated && (time_dt() - reset_time) > params.text_timeout)
183 void Session::run_gpu()
185 bool tiles_written = false;
187 start_time = time_dt();
188 reset_time = time_dt();
190 last_update_time = time_dt();
192 if(!params.background)
193 progress.set_start_time(start_time + paused_time);
195 while(!progress.get_cancel()) {
196 /* advance to next tile */
197 bool no_tiles = !tile_manager.next();
199 if(params.background) {
200 /* if no work left and in background mode, we can stop immediately */
202 progress.set_status("Finished");
207 /* if in interactive mode, and we are either paused or done for now,
208 * wait for pause condition notify to wake up again */
209 thread_scoped_lock pause_lock(pause_mutex);
211 if(pause || no_tiles) {
212 update_status_time(pause, no_tiles);
215 double pause_start = time_dt();
216 pause_cond.wait(pause_lock);
217 paused_time += time_dt() - pause_start;
219 if(!params.background)
220 progress.set_start_time(start_time + paused_time);
222 update_status_time(pause, no_tiles);
223 progress.set_update();
230 if(progress.get_cancel())
238 if(device->error_message() != "")
239 progress.set_cancel(device->error_message());
241 if(progress.get_cancel())
246 /* buffers mutex is locked entirely while rendering each
247 * sample, and released/reacquired on each iteration to allow
248 * reset and draw in between */
249 thread_scoped_lock buffers_lock(buffers_mutex);
251 /* update status and timing */
252 update_status_time();
259 if(device->error_message() != "")
260 progress.set_cancel(device->error_message());
262 /* update status and timing */
263 update_status_time();
265 gpu_need_tonemap = true;
266 gpu_draw_ready = true;
267 progress.set_update();
269 /* wait for tonemap */
270 if(!params.background) {
271 while(gpu_need_tonemap) {
272 if(progress.get_cancel())
275 gpu_need_tonemap_cond.wait(buffers_lock);
279 if(device->error_message() != "")
280 progress.set_cancel(device->error_message());
282 tiles_written = update_progressive_refine(progress.get_cancel());
284 if(progress.get_cancel())
290 update_progressive_refine(true);
295 void Session::reset_cpu(BufferParams& buffer_params, int samples)
297 thread_scoped_lock reset_lock(delayed_reset.mutex);
299 display_outdated = true;
300 reset_time = time_dt();
302 delayed_reset.params = buffer_params;
303 delayed_reset.samples = samples;
304 delayed_reset.do_reset = true;
305 device->task_cancel();
307 pause_cond.notify_all();
310 bool Session::draw_cpu(BufferParams& buffer_params)
312 thread_scoped_lock display_lock(display_mutex);
314 /* first check we already rendered something */
315 if(display->draw_ready()) {
316 /* then verify the buffers have the expected size, so we don't
317 * draw previous results in a resized window */
318 if(!buffer_params.modified(display->params)) {
319 display->draw(device);
321 if(display_outdated && (time_dt() - reset_time) > params.text_timeout)
331 bool Session::acquire_tile(Device *tile_device, RenderTile& rtile)
333 if(progress.get_cancel()) {
334 if(params.progressive_refine == false) {
335 /* for progressive refine current sample should be finished for all tiles */
340 thread_scoped_lock tile_lock(tile_mutex);
342 /* get next tile from manager */
344 int device_num = device->device_number(tile_device);
346 if(!tile_manager.next_tile(tile, device_num))
349 /* fill render tile */
350 rtile.x = tile_manager.state.buffer.full_x + tile.x;
351 rtile.y = tile_manager.state.buffer.full_y + tile.y;
354 rtile.start_sample = tile_manager.state.sample;
355 rtile.num_samples = tile_manager.state.num_samples;
356 rtile.resolution = tile_manager.state.resolution_divider;
360 /* in case of a permant buffer, return it, otherwise we will allocate
361 * a new temporary buffer */
362 if(!params.background) {
363 tile_manager.state.buffer.get_offset_stride(rtile.offset, rtile.stride);
365 rtile.buffer = buffers->buffer.device_pointer;
366 rtile.rng_state = buffers->rng_state.device_pointer;
367 rtile.rgba = display->rgba.device_pointer;
368 rtile.buffers = buffers;
370 device->map_tile(tile_device, rtile);
375 /* fill buffer parameters */
376 BufferParams buffer_params = tile_manager.params;
377 buffer_params.full_x = rtile.x;
378 buffer_params.full_y = rtile.y;
379 buffer_params.width = rtile.w;
380 buffer_params.height = rtile.h;
382 buffer_params.get_offset_stride(rtile.offset, rtile.stride);
384 RenderBuffers *tilebuffers = new RenderBuffers(tile_device);
386 /* allocate buffers */
387 if(params.progressive_refine) {
390 if(tile_buffers.size() == 0)
391 tile_buffers.resize(tile_manager.state.num_tiles, NULL);
393 tilebuffers = tile_buffers[tile.index];
394 if(tilebuffers == NULL) {
395 tilebuffers = new RenderBuffers(tile_device);
396 tile_buffers[tile.index] = tilebuffers;
398 tilebuffers->reset(tile_device, buffer_params);
404 tilebuffers = new RenderBuffers(tile_device);
406 tilebuffers->reset(tile_device, buffer_params);
409 rtile.buffer = tilebuffers->buffer.device_pointer;
410 rtile.rng_state = tilebuffers->rng_state.device_pointer;
412 rtile.buffers = tilebuffers;
417 void Session::update_tile_sample(RenderTile& rtile)
419 thread_scoped_lock tile_lock(tile_mutex);
421 if(update_render_tile_cb) {
422 if(params.progressive_refine == false) {
423 /* todo: optimize this by making it thread safe and removing lock */
425 update_render_tile_cb(rtile);
429 update_status_time();
432 void Session::release_tile(RenderTile& rtile)
434 thread_scoped_lock tile_lock(tile_mutex);
436 if(write_render_tile_cb) {
437 if(params.progressive_refine == false) {
438 /* todo: optimize this by making it thread safe and removing lock */
439 write_render_tile_cb(rtile);
441 delete rtile.buffers;
445 update_status_time();
448 void Session::run_cpu()
450 bool tiles_written = false;
452 last_update_time = time_dt();
455 /* reset once to start */
456 thread_scoped_lock reset_lock(delayed_reset.mutex);
457 thread_scoped_lock buffers_lock(buffers_mutex);
458 thread_scoped_lock display_lock(display_mutex);
460 reset_(delayed_reset.params, delayed_reset.samples);
461 delayed_reset.do_reset = false;
464 while(!progress.get_cancel()) {
465 /* advance to next tile */
466 bool no_tiles = !tile_manager.next();
467 bool need_tonemap = false;
469 if(params.background) {
470 /* if no work left and in background mode, we can stop immediately */
472 progress.set_status("Finished");
477 /* if in interactive mode, and we are either paused or done for now,
478 * wait for pause condition notify to wake up again */
479 thread_scoped_lock pause_lock(pause_mutex);
481 if(pause || no_tiles) {
482 update_status_time(pause, no_tiles);
485 double pause_start = time_dt();
486 pause_cond.wait(pause_lock);
487 paused_time += time_dt() - pause_start;
489 if(!params.background)
490 progress.set_start_time(start_time + paused_time);
492 update_status_time(pause, no_tiles);
493 progress.set_update();
500 if(progress.get_cancel())
505 /* buffers mutex is locked entirely while rendering each
506 * sample, and released/reacquired on each iteration to allow
507 * reset and draw in between */
508 thread_scoped_lock buffers_lock(buffers_mutex);
513 if(device->error_message() != "")
514 progress.set_cancel(device->error_message());
516 if(progress.get_cancel())
519 /* update status and timing */
520 update_status_time();
525 /* update status and timing */
526 update_status_time();
528 if(!params.background)
531 if(device->error_message() != "")
532 progress.set_cancel(device->error_message());
538 thread_scoped_lock reset_lock(delayed_reset.mutex);
539 thread_scoped_lock buffers_lock(buffers_mutex);
540 thread_scoped_lock display_lock(display_mutex);
542 if(delayed_reset.do_reset) {
543 /* reset rendering if request from main thread */
544 delayed_reset.do_reset = false;
545 reset_(delayed_reset.params, delayed_reset.samples);
547 else if(need_tonemap) {
548 /* tonemap only if we do not reset, we don't we don't
549 * wan't to show the result of an incomplete sample*/
553 if(device->error_message() != "")
554 progress.set_cancel(device->error_message());
556 tiles_written = update_progressive_refine(progress.get_cancel());
559 progress.set_update();
563 update_progressive_refine(true);
569 if(!kernels_loaded) {
570 progress.set_status("Loading render kernels (may take a few minutes the first time)");
572 if(!device->load_kernels(params.experimental)) {
573 string message = device->error_message();
575 message = "Failed loading render kernel, see console for errors";
577 progress.set_status("Error", message);
578 progress.set_update();
582 kernels_loaded = true;
585 /* session thread loop */
586 progress.set_status("Waiting for render to start");
589 if(!progress.get_cancel()) {
590 /* reset number of rendered samples */
591 progress.reset_sample();
599 /* progress update */
600 if(progress.get_cancel())
601 progress.set_status("Cancel", progress.get_cancel_message());
603 progress.set_update();
606 bool Session::draw(BufferParams& buffer_params)
609 return draw_gpu(buffer_params);
611 return draw_cpu(buffer_params);
614 void Session::reset_(BufferParams& buffer_params, int samples)
617 if(buffer_params.modified(buffers->params)) {
618 gpu_draw_ready = false;
619 buffers->reset(device, buffer_params);
620 display->reset(device, buffer_params);
624 tile_manager.reset(buffer_params, samples);
626 start_time = time_dt();
630 if(!params.background)
631 progress.set_start_time(start_time + paused_time);
634 void Session::reset(BufferParams& buffer_params, int samples)
637 reset_gpu(buffer_params, samples);
639 reset_cpu(buffer_params, samples);
642 void Session::set_samples(int samples)
644 if(samples != params.samples) {
645 params.samples = samples;
646 tile_manager.set_samples(samples);
649 thread_scoped_lock pause_lock(pause_mutex);
651 pause_cond.notify_all();
655 void Session::set_pause(bool pause_)
660 thread_scoped_lock pause_lock(pause_mutex);
662 if(pause != pause_) {
669 pause_cond.notify_all();
674 session_thread->join();
675 delete session_thread;
677 session_thread = NULL;
680 void Session::update_scene()
682 thread_scoped_lock scene_lock(scene->mutex);
684 /* update camera if dimensions changed for progressive render. the camera
685 * knows nothing about progressive or cropped rendering, it just gets the
686 * image dimensions passed in */
687 Camera *cam = scene->camera;
688 int width = tile_manager.state.buffer.full_width;
689 int height = tile_manager.state.buffer.full_height;
691 if(width != cam->width || height != cam->height) {
693 cam->height = height;
698 if(scene->need_update()) {
699 progress.set_status("Updating Scene");
700 scene->device_update(device, progress);
704 void Session::update_status_time(bool show_pause, bool show_done)
706 int sample = tile_manager.state.sample;
707 int resolution = tile_manager.state.resolution_divider;
708 int num_tiles = tile_manager.state.num_tiles;
709 int tile = tile_manager.state.num_rendered_tiles;
712 string status, substatus;
714 if(!params.progressive) {
715 bool is_gpu = params.device.type == DEVICE_CUDA || params.device.type == DEVICE_OPENCL;
716 bool is_multidevice = params.device.multi_devices.size() > 1;
717 bool is_cpu = params.device.type == DEVICE_CPU;
719 substatus = string_printf("Path Tracing Tile %d/%d", tile, num_tiles);
721 if((is_gpu && !is_multidevice) || (is_cpu && num_tiles == 1)) {
722 /* when rendering on GPU multithreading happens within single tile, as in
723 * tiles are handling sequentially and in this case we could display
724 * currently rendering sample number
725 * this helps a lot from feedback point of view.
726 * also display the info on CPU, when using 1 tile only
729 int sample = progress.get_sample(), num_samples = tile_manager.state.num_samples;
732 /* sample counter is global for all tiles, subtract samples
733 * from already finished tiles to get sample counter for
736 sample -= (tile - 1) * num_samples;
739 substatus += string_printf(", Sample %d/%d", sample, num_samples);
742 else if(params.samples == INT_MAX)
743 substatus = string_printf("Path Tracing Sample %d", sample+1);
745 substatus = string_printf("Path Tracing Sample %d/%d", sample+1, params.samples);
752 status = "Rendering";
754 progress.set_status(status, substatus);
757 if(preview_time == 0.0 && resolution == 1)
758 preview_time = time_dt();
760 double tile_time = (tile == 0)? 0.0: (time_dt() - preview_time - paused_time)/(sample);
762 /* negative can happen when we pause a bit before rendering, can discard that */
763 if(preview_time < 0.0) preview_time = 0.0;
765 progress.set_tile(tile, tile_time);
768 void Session::update_progress_sample()
770 progress.increment_sample();
773 void Session::path_trace()
775 /* add path trace task */
776 DeviceTask task(DeviceTask::PATH_TRACE);
778 task.acquire_tile = function_bind(&Session::acquire_tile, this, _1, _2);
779 task.release_tile = function_bind(&Session::release_tile, this, _1);
780 task.get_cancel = function_bind(&Progress::get_cancel, &this->progress);
781 task.update_tile_sample = function_bind(&Session::update_tile_sample, this, _1);
782 task.update_progress_sample = function_bind(&Session::update_progress_sample, this);
783 task.need_finish_queue = params.progressive_refine;
785 device->task_add(task);
788 void Session::tonemap()
790 /* add tonemap task */
791 DeviceTask task(DeviceTask::TONEMAP);
793 task.x = tile_manager.state.buffer.full_x;
794 task.y = tile_manager.state.buffer.full_y;
795 task.w = tile_manager.state.buffer.width;
796 task.h = tile_manager.state.buffer.height;
797 task.rgba = display->rgba.device_pointer;
798 task.buffer = buffers->buffer.device_pointer;
799 task.sample = tile_manager.state.sample;
800 task.resolution = tile_manager.state.resolution_divider;
801 tile_manager.state.buffer.get_offset_stride(task.offset, task.stride);
803 if(task.w > 0 && task.h > 0) {
804 device->task_add(task);
807 /* set display to new size */
808 display->draw_set(task.w, task.h);
811 display_outdated = false;
814 bool Session::update_progressive_refine(bool cancel)
816 int sample = tile_manager.state.sample + 1;
817 bool write = sample == params.samples || cancel;
819 double current_time = time_dt();
821 if (current_time - last_update_time < 1.0f) {
822 /* if last sample was processed, we need to write buffers anyway */
827 if(params.progressive_refine) {
828 foreach(RenderBuffers *buffers, tile_buffers) {
830 rtile.buffers = buffers;
831 rtile.sample = sample;
834 write_render_tile_cb(rtile);
836 update_render_tile_cb(rtile);
840 last_update_time = current_time;