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 Session::Session(const SessionParams& params_)
39 tile_manager(params.progressive, params.samples, params.tile_size, params.min_size,
40 (params.background)? 1: max(params.device.multi_devices.size(), 1))
42 device_use_gl = ((params.device.type != DEVICE_CPU) && !params.background);
44 TaskScheduler::init(params.threads);
46 device = Device::create(params.device, params.background, params.threads);
48 if(params.background) {
53 buffers = new RenderBuffers(device);
54 display = new DisplayBuffer(device);
57 session_thread = NULL;
66 delayed_reset.do_reset = false;
67 delayed_reset.samples = 0;
69 display_outdated = false;
70 gpu_draw_ready = false;
71 gpu_need_tonemap = false;
73 kernels_loaded = false;
79 progress.set_cancel("Exiting");
81 gpu_need_tonemap = false;
82 gpu_need_tonemap_cond.notify_all();
85 thread_scoped_lock pause_lock(pause_mutex);
88 pause_cond.notify_all();
93 if(display && params.output_path != "") {
96 progress.set_status("Writing Image", params.output_path);
97 display->write(device, params.output_path);
105 TaskScheduler::exit();
108 void Session::start()
110 session_thread = new thread(function_bind(&Session::run, this));
113 bool Session::ready_to_reset()
115 double dt = time_dt() - reset_time;
117 if(!display_outdated)
118 return (dt > params.reset_timeout);
120 return (dt > params.cancel_timeout);
125 void Session::reset_gpu(BufferParams& buffer_params, int samples)
127 /* block for buffer acces and reset immediately. we can't do this
128 * in the thread, because we need to allocate an OpenGL buffer, and
129 * that only works in the main thread */
130 thread_scoped_lock display_lock(display_mutex);
131 thread_scoped_lock buffers_lock(buffers_mutex);
133 display_outdated = true;
134 reset_time = time_dt();
136 reset_(buffer_params, samples);
138 gpu_need_tonemap = false;
139 gpu_need_tonemap_cond.notify_all();
141 pause_cond.notify_all();
144 bool Session::draw_gpu(BufferParams& buffer_params)
146 /* block for buffer access */
147 thread_scoped_lock display_lock(display_mutex);
149 /* first check we already rendered something */
151 /* then verify the buffers have the expected size, so we don't
152 * draw previous results in a resized window */
153 if(!buffer_params.modified(display->params)) {
154 /* for CUDA we need to do tonemapping still, since we can
155 * only access GL buffers from the main thread */
156 if(gpu_need_tonemap) {
157 thread_scoped_lock buffers_lock(buffers_mutex);
159 gpu_need_tonemap = false;
160 gpu_need_tonemap_cond.notify_all();
163 display->draw(device);
165 if(display_outdated && (time_dt() - reset_time) > params.text_timeout)
175 void Session::run_gpu()
177 start_time = time_dt();
178 reset_time = time_dt();
181 if(!params.background)
182 progress.set_start_time(start_time + paused_time);
184 while(!progress.get_cancel()) {
185 /* advance to next tile */
186 bool no_tiles = !tile_manager.next();
188 if(params.background) {
189 /* if no work left and in background mode, we can stop immediately */
191 progress.set_status("Finished");
196 /* if in interactive mode, and we are either paused or done for now,
197 * wait for pause condition notify to wake up again */
198 thread_scoped_lock pause_lock(pause_mutex);
200 if(pause || no_tiles) {
201 update_status_time(pause, no_tiles);
204 double pause_start = time_dt();
205 pause_cond.wait(pause_lock);
206 paused_time += time_dt() - pause_start;
208 if(!params.background)
209 progress.set_start_time(start_time + paused_time);
211 update_status_time(pause, no_tiles);
212 progress.set_update();
219 if(progress.get_cancel())
227 if(device->error_message() != "")
228 progress.set_cancel(device->error_message());
230 if(progress.get_cancel())
235 /* buffers mutex is locked entirely while rendering each
236 * sample, and released/reacquired on each iteration to allow
237 * reset and draw in between */
238 thread_scoped_lock buffers_lock(buffers_mutex);
240 /* update status and timing */
241 update_status_time();
248 if(device->error_message() != "")
249 progress.set_cancel(device->error_message());
251 /* update status and timing */
252 update_status_time();
254 gpu_need_tonemap = true;
255 gpu_draw_ready = true;
256 progress.set_update();
258 /* wait for tonemap */
259 if(!params.background) {
260 while(gpu_need_tonemap) {
261 if(progress.get_cancel())
264 gpu_need_tonemap_cond.wait(buffers_lock);
268 if(device->error_message() != "")
269 progress.set_cancel(device->error_message());
271 if(progress.get_cancel())
279 void Session::reset_cpu(BufferParams& buffer_params, int samples)
281 thread_scoped_lock reset_lock(delayed_reset.mutex);
283 display_outdated = true;
284 reset_time = time_dt();
286 delayed_reset.params = buffer_params;
287 delayed_reset.samples = samples;
288 delayed_reset.do_reset = true;
289 device->task_cancel();
291 pause_cond.notify_all();
294 bool Session::draw_cpu(BufferParams& buffer_params)
296 thread_scoped_lock display_lock(display_mutex);
298 /* first check we already rendered something */
299 if(display->draw_ready()) {
300 /* then verify the buffers have the expected size, so we don't
301 * draw previous results in a resized window */
302 if(!buffer_params.modified(display->params)) {
303 display->draw(device);
305 if(display_outdated && (time_dt() - reset_time) > params.text_timeout)
315 bool Session::acquire_tile(Device *tile_device, RenderTile& rtile)
317 if(progress.get_cancel())
320 thread_scoped_lock tile_lock(tile_mutex);
322 /* get next tile from manager */
324 int device_num = device->device_number(tile_device);
326 if(!tile_manager.next_tile(tile, device_num))
329 /* fill render tile */
330 rtile.x = tile_manager.state.buffer.full_x + tile.x;
331 rtile.y = tile_manager.state.buffer.full_y + tile.y;
334 rtile.start_sample = tile_manager.state.sample;
335 rtile.num_samples = tile_manager.state.num_samples;
336 rtile.resolution = tile_manager.state.resolution;
340 /* in case of a permant buffer, return it, otherwise we will allocate
341 * a new temporary buffer */
342 if(!write_render_buffers_cb) {
343 tile_manager.state.buffer.get_offset_stride(rtile.offset, rtile.stride);
345 rtile.buffer = buffers->buffer.device_pointer;
346 rtile.rng_state = buffers->rng_state.device_pointer;
347 rtile.rgba = display->rgba.device_pointer;
348 rtile.buffers = buffers;
350 device->map_tile(tile_device, rtile);
355 /* fill buffer parameters */
356 BufferParams buffer_params = tile_manager.params;
357 buffer_params.full_x = rtile.x;
358 buffer_params.full_y = rtile.y;
359 buffer_params.width = rtile.w;
360 buffer_params.height = rtile.h;
362 buffer_params.get_offset_stride(rtile.offset, rtile.stride);
364 /* allocate buffers */
365 RenderBuffers *tilebuffers = new RenderBuffers(tile_device);
366 tilebuffers->reset(tile_device, buffer_params);
368 rtile.buffer = tilebuffers->buffer.device_pointer;
369 rtile.rng_state = tilebuffers->rng_state.device_pointer;
371 rtile.buffers = tilebuffers;
376 void Session::update_tile_sample(RenderTile& rtile)
378 thread_scoped_lock tile_lock(tile_mutex);
380 if(update_render_buffers_cb) {
381 /* todo: optimize this by making it thread safe and removing lock */
383 if(!progress.get_cancel())
384 update_render_buffers_cb(rtile.buffers);
387 update_status_time();
390 void Session::release_tile(RenderTile& rtile)
392 thread_scoped_lock tile_lock(tile_mutex);
394 if(write_render_buffers_cb) {
395 /* todo: optimize this by making it thread safe and removing lock */
396 if(!progress.get_cancel())
397 write_render_buffers_cb(rtile.buffers);
398 delete rtile.buffers;
401 update_status_time();
404 void Session::run_cpu()
407 /* reset once to start */
408 thread_scoped_lock reset_lock(delayed_reset.mutex);
409 thread_scoped_lock buffers_lock(buffers_mutex);
410 thread_scoped_lock display_lock(display_mutex);
412 reset_(delayed_reset.params, delayed_reset.samples);
413 delayed_reset.do_reset = false;
416 while(!progress.get_cancel()) {
417 /* advance to next tile */
418 bool no_tiles = !tile_manager.next();
419 bool need_tonemap = false;
421 if(params.background) {
422 /* if no work left and in background mode, we can stop immediately */
424 progress.set_status("Finished");
429 /* if in interactive mode, and we are either paused or done for now,
430 * wait for pause condition notify to wake up again */
431 thread_scoped_lock pause_lock(pause_mutex);
433 if(pause || no_tiles) {
434 update_status_time(pause, no_tiles);
437 double pause_start = time_dt();
438 pause_cond.wait(pause_lock);
439 paused_time += time_dt() - pause_start;
441 if(!params.background)
442 progress.set_start_time(start_time + paused_time);
444 update_status_time(pause, no_tiles);
445 progress.set_update();
452 if(progress.get_cancel())
457 /* buffers mutex is locked entirely while rendering each
458 * sample, and released/reacquired on each iteration to allow
459 * reset and draw in between */
460 thread_scoped_lock buffers_lock(buffers_mutex);
465 if(device->error_message() != "")
466 progress.set_cancel(device->error_message());
468 if(progress.get_cancel())
471 /* update status and timing */
472 update_status_time();
477 /* update status and timing */
478 update_status_time();
480 if(!params.background)
483 if(device->error_message() != "")
484 progress.set_cancel(device->error_message());
490 thread_scoped_lock reset_lock(delayed_reset.mutex);
491 thread_scoped_lock buffers_lock(buffers_mutex);
492 thread_scoped_lock display_lock(display_mutex);
494 if(delayed_reset.do_reset) {
495 /* reset rendering if request from main thread */
496 delayed_reset.do_reset = false;
497 reset_(delayed_reset.params, delayed_reset.samples);
499 else if(need_tonemap) {
500 /* tonemap only if we do not reset, we don't we don't
501 * wan't to show the result of an incomplete sample*/
505 if(device->error_message() != "")
506 progress.set_cancel(device->error_message());
509 progress.set_update();
516 if(!kernels_loaded) {
517 progress.set_status("Loading render kernels (may take a few minutes the first time)");
519 if(!device->load_kernels(params.experimental)) {
520 string message = device->error_message();
522 message = "Failed loading render kernel, see console for errors";
524 progress.set_status("Error", message);
525 progress.set_update();
529 kernels_loaded = true;
532 /* session thread loop */
533 progress.set_status("Waiting for render to start");
536 if(!progress.get_cancel()) {
543 /* progress update */
544 if(progress.get_cancel())
545 progress.set_status("Cancel", progress.get_cancel_message());
547 progress.set_update();
550 bool Session::draw(BufferParams& buffer_params)
553 return draw_gpu(buffer_params);
555 return draw_cpu(buffer_params);
558 void Session::reset_(BufferParams& buffer_params, int samples)
561 if(buffer_params.modified(buffers->params)) {
562 gpu_draw_ready = false;
563 buffers->reset(device, buffer_params);
564 display->reset(device, buffer_params);
568 tile_manager.reset(buffer_params, samples);
570 start_time = time_dt();
575 if(!params.background)
576 progress.set_start_time(start_time + paused_time);
579 void Session::reset(BufferParams& buffer_params, int samples)
582 reset_gpu(buffer_params, samples);
584 reset_cpu(buffer_params, samples);
587 void Session::set_samples(int samples)
589 if(samples != params.samples) {
590 params.samples = samples;
591 tile_manager.set_samples(samples);
594 thread_scoped_lock pause_lock(pause_mutex);
596 pause_cond.notify_all();
600 void Session::set_pause(bool pause_)
605 thread_scoped_lock pause_lock(pause_mutex);
607 if(pause != pause_) {
614 pause_cond.notify_all();
619 session_thread->join();
620 delete session_thread;
622 session_thread = NULL;
625 void Session::update_scene()
627 thread_scoped_lock scene_lock(scene->mutex);
629 /* update camera if dimensions changed for progressive render. the camera
630 * knows nothing about progressive or cropped rendering, it just gets the
631 * image dimensions passed in */
632 Camera *cam = scene->camera;
633 int width = tile_manager.state.buffer.full_width;
634 int height = tile_manager.state.buffer.full_height;
636 if(width != cam->width || height != cam->height) {
638 cam->height = height;
643 if(scene->need_update()) {
644 progress.set_status("Updating Scene");
645 scene->device_update(device, progress);
649 void Session::update_status_time(bool show_pause, bool show_done)
651 int sample = tile_manager.state.sample;
652 int num_samples = tile_manager.state.num_samples;
653 int resolution = tile_manager.state.resolution;
654 int num_tiles = tile_manager.state.num_tiles;
655 int tile = num_tiles - tile_manager.state.tiles.size();
658 string status, substatus;
660 if(!params.progressive)
661 substatus = string_printf("Path Tracing Tile %d/%d", tile, num_tiles);
662 else if(params.samples == INT_MAX)
663 substatus = string_printf("Path Tracing Sample %d", sample+1);
665 substatus = string_printf("Path Tracing Sample %d/%d", sample+1, params.samples);
672 status = "Rendering";
674 progress.set_status(status, substatus);
677 if(preview_time == 0.0 && resolution == 1)
678 preview_time = time_dt();
680 double sample_time = (sample == 0)? 0.0: (time_dt() - preview_time - paused_time)/(sample);
682 /* negative can happen when we pause a bit before rendering, can discard that */
683 if(preview_time < 0.0) preview_time = 0.0;
685 progress.set_sample(sample + num_samples, sample_time);
688 void Session::path_trace()
690 /* add path trace task */
691 DeviceTask task(DeviceTask::PATH_TRACE);
693 task.acquire_tile = function_bind(&Session::acquire_tile, this, _1, _2);
694 task.release_tile = function_bind(&Session::release_tile, this, _1);
695 task.get_cancel = function_bind(&Progress::get_cancel, &this->progress);
696 task.update_tile_sample = function_bind(&Session::update_tile_sample, this, _1);
698 device->task_add(task);
701 void Session::tonemap()
703 /* add tonemap task */
704 DeviceTask task(DeviceTask::TONEMAP);
706 task.x = tile_manager.state.buffer.full_x;
707 task.y = tile_manager.state.buffer.full_y;
708 task.w = tile_manager.state.buffer.width;
709 task.h = tile_manager.state.buffer.height;
710 task.rgba = display->rgba.device_pointer;
711 task.buffer = buffers->buffer.device_pointer;
712 task.sample = tile_manager.state.sample;
713 task.resolution = tile_manager.state.resolution;
714 tile_manager.state.buffer.get_offset_stride(task.offset, task.stride);
716 if(task.w > 0 && task.h > 0) {
717 device->task_add(task);
720 /* set display to new size */
721 display->draw_set(task.w, task.h);
724 display_outdated = false;