diff options
Diffstat (limited to 'intern/cycles/render/session.cpp')
-rw-r--r-- | intern/cycles/render/session.cpp | 1901 |
1 files changed, 951 insertions, 950 deletions
diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp index e88d960d9d2..29eb779a7d6 100644 --- a/intern/cycles/render/session.cpp +++ b/intern/cycles/render/session.cpp @@ -43,1121 +43,1122 @@ CCL_NAMESPACE_BEGIN * progressive refine and viewport rendering does requires tiles to * always be allocated for the same device */ -Session::Session(const SessionParams& params_) -: params(params_), - tile_manager(params.progressive, params.samples, params.tile_size, params.start_resolution, - params.background == false || params.progressive_refine, params.background, params.tile_order, - max(params.device.multi_devices.size(), 1), params.pixel_size), - stats(), - profiler() +Session::Session(const SessionParams ¶ms_) + : params(params_), + tile_manager(params.progressive, + params.samples, + params.tile_size, + params.start_resolution, + params.background == false || params.progressive_refine, + params.background, + params.tile_order, + max(params.device.multi_devices.size(), 1), + params.pixel_size), + stats(), + profiler() { - device_use_gl = ((params.device.type != DEVICE_CPU) && !params.background); + device_use_gl = ((params.device.type != DEVICE_CPU) && !params.background); - TaskScheduler::init(params.threads); + TaskScheduler::init(params.threads); - device = Device::create(params.device, stats, profiler, params.background); + device = Device::create(params.device, stats, profiler, params.background); - if(params.background && !params.write_render_cb) { - buffers = NULL; - display = NULL; - } - else { - buffers = new RenderBuffers(device); - display = new DisplayBuffer(device, params.display_buffer_linear); - } + if (params.background && !params.write_render_cb) { + buffers = NULL; + display = NULL; + } + else { + buffers = new RenderBuffers(device); + display = new DisplayBuffer(device, params.display_buffer_linear); + } - session_thread = NULL; - scene = NULL; + session_thread = NULL; + scene = NULL; - reset_time = 0.0; - last_update_time = 0.0; + reset_time = 0.0; + last_update_time = 0.0; - delayed_reset.do_reset = false; - delayed_reset.samples = 0; + delayed_reset.do_reset = false; + delayed_reset.samples = 0; - display_outdated = false; - gpu_draw_ready = false; - gpu_need_tonemap = false; - pause = false; - kernels_loaded = false; + display_outdated = false; + gpu_draw_ready = false; + gpu_need_tonemap = false; + pause = false; + kernels_loaded = false; - /* TODO(sergey): Check if it's indeed optimal value for the split kernel. */ - max_closure_global = 1; + /* TODO(sergey): Check if it's indeed optimal value for the split kernel. */ + max_closure_global = 1; } Session::~Session() { - if(session_thread) { - /* wait for session thread to end */ - progress.set_cancel("Exiting"); + if (session_thread) { + /* wait for session thread to end */ + progress.set_cancel("Exiting"); - gpu_need_tonemap = false; - gpu_need_tonemap_cond.notify_all(); + gpu_need_tonemap = false; + gpu_need_tonemap_cond.notify_all(); - { - thread_scoped_lock pause_lock(pause_mutex); - pause = false; - } - pause_cond.notify_all(); + { + thread_scoped_lock pause_lock(pause_mutex); + pause = false; + } + pause_cond.notify_all(); - wait(); - } + wait(); + } - if(params.write_render_cb) { - /* tonemap and write out image if requested */ - delete display; + if (params.write_render_cb) { + /* tonemap and write out image if requested */ + delete display; - display = new DisplayBuffer(device, false); - display->reset(buffers->params); - tonemap(params.samples); + display = new DisplayBuffer(device, false); + display->reset(buffers->params); + tonemap(params.samples); - int w = display->draw_width; - int h = display->draw_height; - uchar4 *pixels = display->rgba_byte.copy_from_device(0, w, h); - params.write_render_cb((uchar*)pixels, w, h, 4); - } + int w = display->draw_width; + int h = display->draw_height; + uchar4 *pixels = display->rgba_byte.copy_from_device(0, w, h); + params.write_render_cb((uchar *)pixels, w, h, 4); + } - /* clean up */ - tile_manager.device_free(); + /* clean up */ + tile_manager.device_free(); - delete buffers; - delete display; - delete scene; - delete device; + delete buffers; + delete display; + delete scene; + delete device; - TaskScheduler::exit(); + TaskScheduler::exit(); } void Session::start() { - if (!session_thread) { - session_thread = new thread(function_bind(&Session::run, this)); - } + if (!session_thread) { + session_thread = new thread(function_bind(&Session::run, this)); + } } bool Session::ready_to_reset() { - double dt = time_dt() - reset_time; + double dt = time_dt() - reset_time; - if(!display_outdated) - return (dt > params.reset_timeout); - else - return (dt > params.cancel_timeout); + if (!display_outdated) + return (dt > params.reset_timeout); + else + return (dt > params.cancel_timeout); } /* GPU Session */ -void Session::reset_gpu(BufferParams& buffer_params, int samples) +void Session::reset_gpu(BufferParams &buffer_params, int samples) { - thread_scoped_lock pause_lock(pause_mutex); + thread_scoped_lock pause_lock(pause_mutex); - /* block for buffer access and reset immediately. we can't do this - * in the thread, because we need to allocate an OpenGL buffer, and - * that only works in the main thread */ - thread_scoped_lock display_lock(display_mutex); - thread_scoped_lock buffers_lock(buffers_mutex); + /* block for buffer access and reset immediately. we can't do this + * in the thread, because we need to allocate an OpenGL buffer, and + * that only works in the main thread */ + thread_scoped_lock display_lock(display_mutex); + thread_scoped_lock buffers_lock(buffers_mutex); - display_outdated = true; - reset_time = time_dt(); + display_outdated = true; + reset_time = time_dt(); - reset_(buffer_params, samples); + reset_(buffer_params, samples); - gpu_need_tonemap = false; - gpu_need_tonemap_cond.notify_all(); + gpu_need_tonemap = false; + gpu_need_tonemap_cond.notify_all(); - pause_cond.notify_all(); + pause_cond.notify_all(); } -bool Session::draw_gpu(BufferParams& buffer_params, DeviceDrawParams& draw_params) +bool Session::draw_gpu(BufferParams &buffer_params, DeviceDrawParams &draw_params) { - /* block for buffer access */ - thread_scoped_lock display_lock(display_mutex); - - /* first check we already rendered something */ - if(gpu_draw_ready) { - /* then verify the buffers have the expected size, so we don't - * draw previous results in a resized window */ - if(!buffer_params.modified(display->params)) { - /* for CUDA we need to do tonemapping still, since we can - * only access GL buffers from the main thread */ - if(gpu_need_tonemap) { - thread_scoped_lock buffers_lock(buffers_mutex); - tonemap(tile_manager.state.sample); - gpu_need_tonemap = false; - gpu_need_tonemap_cond.notify_all(); - } - - display->draw(device, draw_params); - - if(display_outdated && (time_dt() - reset_time) > params.text_timeout) - return false; - - return true; - } - } - - return false; + /* block for buffer access */ + thread_scoped_lock display_lock(display_mutex); + + /* first check we already rendered something */ + if (gpu_draw_ready) { + /* then verify the buffers have the expected size, so we don't + * draw previous results in a resized window */ + if (!buffer_params.modified(display->params)) { + /* for CUDA we need to do tonemapping still, since we can + * only access GL buffers from the main thread */ + if (gpu_need_tonemap) { + thread_scoped_lock buffers_lock(buffers_mutex); + tonemap(tile_manager.state.sample); + gpu_need_tonemap = false; + gpu_need_tonemap_cond.notify_all(); + } + + display->draw(device, draw_params); + + if (display_outdated && (time_dt() - reset_time) > params.text_timeout) + return false; + + return true; + } + } + + return false; } void Session::run_gpu() { - bool tiles_written = false; - - reset_time = time_dt(); - last_update_time = time_dt(); - - progress.set_render_start_time(); - - while(!progress.get_cancel()) { - /* advance to next tile */ - bool no_tiles = !tile_manager.next(); - - DeviceKernelStatus kernel_state = DEVICE_KERNEL_UNKNOWN; - if (no_tiles) { - kernel_state = device->get_active_kernel_switch_state(); - } - - if(params.background) { - /* if no work left and in background mode, we can stop immediately */ - if(no_tiles) { - progress.set_status("Finished"); - break; - } - } - - /* Don't go in pause mode when image was rendered with preview kernels - * When feature kernels become available the session will be resetted. */ - else if (no_tiles && kernel_state == DEVICE_KERNEL_WAITING_FOR_FEATURE_KERNEL) { - time_sleep(0.1); - } - else if (no_tiles && kernel_state == DEVICE_KERNEL_FEATURE_KERNEL_AVAILABLE) { - reset_gpu(tile_manager.params, params.samples); - } - - else { - /* if in interactive mode, and we are either paused or done for now, - * wait for pause condition notify to wake up again */ - thread_scoped_lock pause_lock(pause_mutex); - - if(!pause && !tile_manager.done()) { - /* reset could have happened after no_tiles was set, before this lock. - * in this case we shall not wait for pause condition - */ - } - else if(pause || no_tiles) { - update_status_time(pause, no_tiles); - - while(1) { - scoped_timer pause_timer; - pause_cond.wait(pause_lock); - if(pause) { - progress.add_skip_time(pause_timer, params.background); - } - - update_status_time(pause, no_tiles); - progress.set_update(); - - if(!pause) - break; - } - } - - if(progress.get_cancel()) - break; - } - - if(!no_tiles) { - /* update scene */ - scoped_timer update_timer; - if(update_scene()) { - profiler.reset(scene->shaders.size(), scene->objects.size()); - } - progress.add_skip_time(update_timer, params.background); - - if(!device->error_message().empty()) - progress.set_error(device->error_message()); - - if(progress.get_cancel()) - break; - } - - if(!no_tiles) { - /* buffers mutex is locked entirely while rendering each - * sample, and released/reacquired on each iteration to allow - * reset and draw in between */ - thread_scoped_lock buffers_lock(buffers_mutex); - - /* update status and timing */ - update_status_time(); - - /* render */ - render(); - - device->task_wait(); - - if(!device->error_message().empty()) - progress.set_cancel(device->error_message()); - - /* update status and timing */ - update_status_time(); - - gpu_need_tonemap = true; - gpu_draw_ready = true; - progress.set_update(); - - /* wait for tonemap */ - if(!params.background) { - while(gpu_need_tonemap) { - if(progress.get_cancel()) - break; - - gpu_need_tonemap_cond.wait(buffers_lock); - } - } - - if(!device->error_message().empty()) - progress.set_error(device->error_message()); - - tiles_written = update_progressive_refine(progress.get_cancel()); - - if(progress.get_cancel()) - break; - } - } - - if(!tiles_written) - update_progressive_refine(true); + bool tiles_written = false; + + reset_time = time_dt(); + last_update_time = time_dt(); + + progress.set_render_start_time(); + + while (!progress.get_cancel()) { + /* advance to next tile */ + bool no_tiles = !tile_manager.next(); + + DeviceKernelStatus kernel_state = DEVICE_KERNEL_UNKNOWN; + if (no_tiles) { + kernel_state = device->get_active_kernel_switch_state(); + } + + if (params.background) { + /* if no work left and in background mode, we can stop immediately */ + if (no_tiles) { + progress.set_status("Finished"); + break; + } + } + + /* Don't go in pause mode when image was rendered with preview kernels + * When feature kernels become available the session will be resetted. */ + else if (no_tiles && kernel_state == DEVICE_KERNEL_WAITING_FOR_FEATURE_KERNEL) { + time_sleep(0.1); + } + else if (no_tiles && kernel_state == DEVICE_KERNEL_FEATURE_KERNEL_AVAILABLE) { + reset_gpu(tile_manager.params, params.samples); + } + + else { + /* if in interactive mode, and we are either paused or done for now, + * wait for pause condition notify to wake up again */ + thread_scoped_lock pause_lock(pause_mutex); + + if (!pause && !tile_manager.done()) { + /* reset could have happened after no_tiles was set, before this lock. + * in this case we shall not wait for pause condition + */ + } + else if (pause || no_tiles) { + update_status_time(pause, no_tiles); + + while (1) { + scoped_timer pause_timer; + pause_cond.wait(pause_lock); + if (pause) { + progress.add_skip_time(pause_timer, params.background); + } + + update_status_time(pause, no_tiles); + progress.set_update(); + + if (!pause) + break; + } + } + + if (progress.get_cancel()) + break; + } + + if (!no_tiles) { + /* update scene */ + scoped_timer update_timer; + if (update_scene()) { + profiler.reset(scene->shaders.size(), scene->objects.size()); + } + progress.add_skip_time(update_timer, params.background); + + if (!device->error_message().empty()) + progress.set_error(device->error_message()); + + if (progress.get_cancel()) + break; + } + + if (!no_tiles) { + /* buffers mutex is locked entirely while rendering each + * sample, and released/reacquired on each iteration to allow + * reset and draw in between */ + thread_scoped_lock buffers_lock(buffers_mutex); + + /* update status and timing */ + update_status_time(); + + /* render */ + render(); + + device->task_wait(); + + if (!device->error_message().empty()) + progress.set_cancel(device->error_message()); + + /* update status and timing */ + update_status_time(); + + gpu_need_tonemap = true; + gpu_draw_ready = true; + progress.set_update(); + + /* wait for tonemap */ + if (!params.background) { + while (gpu_need_tonemap) { + if (progress.get_cancel()) + break; + + gpu_need_tonemap_cond.wait(buffers_lock); + } + } + + if (!device->error_message().empty()) + progress.set_error(device->error_message()); + + tiles_written = update_progressive_refine(progress.get_cancel()); + + if (progress.get_cancel()) + break; + } + } + + if (!tiles_written) + update_progressive_refine(true); } /* CPU Session */ -void Session::reset_cpu(BufferParams& buffer_params, int samples) +void Session::reset_cpu(BufferParams &buffer_params, int samples) { - thread_scoped_lock reset_lock(delayed_reset.mutex); - thread_scoped_lock pause_lock(pause_mutex); + thread_scoped_lock reset_lock(delayed_reset.mutex); + thread_scoped_lock pause_lock(pause_mutex); - display_outdated = true; - reset_time = time_dt(); + display_outdated = true; + reset_time = time_dt(); - delayed_reset.params = buffer_params; - delayed_reset.samples = samples; - delayed_reset.do_reset = true; - device->task_cancel(); + delayed_reset.params = buffer_params; + delayed_reset.samples = samples; + delayed_reset.do_reset = true; + device->task_cancel(); - pause_cond.notify_all(); + pause_cond.notify_all(); } -bool Session::draw_cpu(BufferParams& buffer_params, DeviceDrawParams& draw_params) +bool Session::draw_cpu(BufferParams &buffer_params, DeviceDrawParams &draw_params) { - thread_scoped_lock display_lock(display_mutex); + thread_scoped_lock display_lock(display_mutex); - /* first check we already rendered something */ - if(display->draw_ready()) { - /* then verify the buffers have the expected size, so we don't - * draw previous results in a resized window */ - if(!buffer_params.modified(display->params)) { - display->draw(device, draw_params); + /* first check we already rendered something */ + if (display->draw_ready()) { + /* then verify the buffers have the expected size, so we don't + * draw previous results in a resized window */ + if (!buffer_params.modified(display->params)) { + display->draw(device, draw_params); - if(display_outdated && (time_dt() - reset_time) > params.text_timeout) - return false; + if (display_outdated && (time_dt() - reset_time) > params.text_timeout) + return false; - return true; - } - } + return true; + } + } - return false; + return false; } -bool Session::acquire_tile(Device *tile_device, RenderTile& rtile) +bool Session::acquire_tile(Device *tile_device, RenderTile &rtile) { - if(progress.get_cancel()) { - if(params.progressive_refine == false) { - /* for progressive refine current sample should be finished for all tiles */ - return false; - } - } - - thread_scoped_lock tile_lock(tile_mutex); - - /* get next tile from manager */ - Tile *tile; - int device_num = device->device_number(tile_device); - - if(!tile_manager.next_tile(tile, device_num)) - return false; - - /* fill render tile */ - rtile.x = tile_manager.state.buffer.full_x + tile->x; - rtile.y = tile_manager.state.buffer.full_y + tile->y; - rtile.w = tile->w; - rtile.h = tile->h; - rtile.start_sample = tile_manager.state.sample; - rtile.num_samples = tile_manager.state.num_samples; - rtile.resolution = tile_manager.state.resolution_divider; - rtile.tile_index = tile->index; - rtile.task = (tile->state == Tile::DENOISE)? RenderTile::DENOISE: RenderTile::PATH_TRACE; - - tile_lock.unlock(); - - /* in case of a permanent buffer, return it, otherwise we will allocate - * a new temporary buffer */ - if(buffers) { - tile_manager.state.buffer.get_offset_stride(rtile.offset, rtile.stride); - - rtile.buffer = buffers->buffer.device_pointer; - rtile.buffers = buffers; - - device->map_tile(tile_device, rtile); - - return true; - } - - if(tile->buffers == NULL) { - /* fill buffer parameters */ - BufferParams buffer_params = tile_manager.params; - buffer_params.full_x = rtile.x; - buffer_params.full_y = rtile.y; - buffer_params.width = rtile.w; - buffer_params.height = rtile.h; - - /* allocate buffers */ - tile->buffers = new RenderBuffers(tile_device); - tile->buffers->reset(buffer_params); - } - - tile->buffers->params.get_offset_stride(rtile.offset, rtile.stride); - - rtile.buffer = tile->buffers->buffer.device_pointer; - rtile.buffers = tile->buffers; - rtile.sample = tile_manager.state.sample; - - /* this will tag tile as IN PROGRESS in blender-side render pipeline, - * which is needed to highlight currently rendering tile before first - * sample was processed for it - */ - update_tile_sample(rtile); - - return true; + if (progress.get_cancel()) { + if (params.progressive_refine == false) { + /* for progressive refine current sample should be finished for all tiles */ + return false; + } + } + + thread_scoped_lock tile_lock(tile_mutex); + + /* get next tile from manager */ + Tile *tile; + int device_num = device->device_number(tile_device); + + if (!tile_manager.next_tile(tile, device_num)) + return false; + + /* fill render tile */ + rtile.x = tile_manager.state.buffer.full_x + tile->x; + rtile.y = tile_manager.state.buffer.full_y + tile->y; + rtile.w = tile->w; + rtile.h = tile->h; + rtile.start_sample = tile_manager.state.sample; + rtile.num_samples = tile_manager.state.num_samples; + rtile.resolution = tile_manager.state.resolution_divider; + rtile.tile_index = tile->index; + rtile.task = (tile->state == Tile::DENOISE) ? RenderTile::DENOISE : RenderTile::PATH_TRACE; + + tile_lock.unlock(); + + /* in case of a permanent buffer, return it, otherwise we will allocate + * a new temporary buffer */ + if (buffers) { + tile_manager.state.buffer.get_offset_stride(rtile.offset, rtile.stride); + + rtile.buffer = buffers->buffer.device_pointer; + rtile.buffers = buffers; + + device->map_tile(tile_device, rtile); + + return true; + } + + if (tile->buffers == NULL) { + /* fill buffer parameters */ + BufferParams buffer_params = tile_manager.params; + buffer_params.full_x = rtile.x; + buffer_params.full_y = rtile.y; + buffer_params.width = rtile.w; + buffer_params.height = rtile.h; + + /* allocate buffers */ + tile->buffers = new RenderBuffers(tile_device); + tile->buffers->reset(buffer_params); + } + + tile->buffers->params.get_offset_stride(rtile.offset, rtile.stride); + + rtile.buffer = tile->buffers->buffer.device_pointer; + rtile.buffers = tile->buffers; + rtile.sample = tile_manager.state.sample; + + /* this will tag tile as IN PROGRESS in blender-side render pipeline, + * which is needed to highlight currently rendering tile before first + * sample was processed for it + */ + update_tile_sample(rtile); + + return true; } -void Session::update_tile_sample(RenderTile& rtile) +void Session::update_tile_sample(RenderTile &rtile) { - thread_scoped_lock tile_lock(tile_mutex); + thread_scoped_lock tile_lock(tile_mutex); - if(update_render_tile_cb) { - if(params.progressive_refine == false) { - /* todo: optimize this by making it thread safe and removing lock */ + if (update_render_tile_cb) { + if (params.progressive_refine == false) { + /* todo: optimize this by making it thread safe and removing lock */ - update_render_tile_cb(rtile, true); - } - } + update_render_tile_cb(rtile, true); + } + } - update_status_time(); + update_status_time(); } -void Session::release_tile(RenderTile& rtile) +void Session::release_tile(RenderTile &rtile) { - thread_scoped_lock tile_lock(tile_mutex); + thread_scoped_lock tile_lock(tile_mutex); - progress.add_finished_tile(rtile.task == RenderTile::DENOISE); + progress.add_finished_tile(rtile.task == RenderTile::DENOISE); - bool delete_tile; + bool delete_tile; - if(tile_manager.finish_tile(rtile.tile_index, delete_tile)) { - if(write_render_tile_cb && params.progressive_refine == false) { - write_render_tile_cb(rtile); - } + if (tile_manager.finish_tile(rtile.tile_index, delete_tile)) { + if (write_render_tile_cb && params.progressive_refine == false) { + write_render_tile_cb(rtile); + } - if(delete_tile) { - delete rtile.buffers; - tile_manager.state.tiles[rtile.tile_index].buffers = NULL; - } - } - else { - if(update_render_tile_cb && params.progressive_refine == false) { - update_render_tile_cb(rtile, false); - } - } + if (delete_tile) { + delete rtile.buffers; + tile_manager.state.tiles[rtile.tile_index].buffers = NULL; + } + } + else { + if (update_render_tile_cb && params.progressive_refine == false) { + update_render_tile_cb(rtile, false); + } + } - update_status_time(); + update_status_time(); } void Session::map_neighbor_tiles(RenderTile *tiles, Device *tile_device) { - thread_scoped_lock tile_lock(tile_mutex); - - int center_idx = tiles[4].tile_index; - assert(tile_manager.state.tiles[center_idx].state == Tile::DENOISE); - BufferParams buffer_params = tile_manager.params; - int4 image_region = make_int4(buffer_params.full_x, buffer_params.full_y, - buffer_params.full_x + buffer_params.width, buffer_params.full_y + buffer_params.height); - - for(int dy = -1, i = 0; dy <= 1; dy++) { - for(int dx = -1; dx <= 1; dx++, i++) { - int px = tiles[4].x + dx*params.tile_size.x; - int py = tiles[4].y + dy*params.tile_size.y; - if(px >= image_region.x && py >= image_region.y && - px < image_region.z && py < image_region.w) { - int tile_index = center_idx + dy*tile_manager.state.tile_stride + dx; - Tile *tile = &tile_manager.state.tiles[tile_index]; - assert(tile->buffers); - - tiles[i].buffer = tile->buffers->buffer.device_pointer; - tiles[i].x = tile_manager.state.buffer.full_x + tile->x; - tiles[i].y = tile_manager.state.buffer.full_y + tile->y; - tiles[i].w = tile->w; - tiles[i].h = tile->h; - tiles[i].buffers = tile->buffers; - - tile->buffers->params.get_offset_stride(tiles[i].offset, tiles[i].stride); - } - else { - tiles[i].buffer = (device_ptr)NULL; - tiles[i].buffers = NULL; - tiles[i].x = clamp(px, image_region.x, image_region.z); - tiles[i].y = clamp(py, image_region.y, image_region.w); - tiles[i].w = tiles[i].h = 0; - } - } - } - - assert(tiles[4].buffers); - device->map_neighbor_tiles(tile_device, tiles); - - /* The denoised result is written back to the original tile. */ - tiles[9] = tiles[4]; + thread_scoped_lock tile_lock(tile_mutex); + + int center_idx = tiles[4].tile_index; + assert(tile_manager.state.tiles[center_idx].state == Tile::DENOISE); + BufferParams buffer_params = tile_manager.params; + int4 image_region = make_int4(buffer_params.full_x, + buffer_params.full_y, + buffer_params.full_x + buffer_params.width, + buffer_params.full_y + buffer_params.height); + + for (int dy = -1, i = 0; dy <= 1; dy++) { + for (int dx = -1; dx <= 1; dx++, i++) { + int px = tiles[4].x + dx * params.tile_size.x; + int py = tiles[4].y + dy * params.tile_size.y; + if (px >= image_region.x && py >= image_region.y && px < image_region.z && + py < image_region.w) { + int tile_index = center_idx + dy * tile_manager.state.tile_stride + dx; + Tile *tile = &tile_manager.state.tiles[tile_index]; + assert(tile->buffers); + + tiles[i].buffer = tile->buffers->buffer.device_pointer; + tiles[i].x = tile_manager.state.buffer.full_x + tile->x; + tiles[i].y = tile_manager.state.buffer.full_y + tile->y; + tiles[i].w = tile->w; + tiles[i].h = tile->h; + tiles[i].buffers = tile->buffers; + + tile->buffers->params.get_offset_stride(tiles[i].offset, tiles[i].stride); + } + else { + tiles[i].buffer = (device_ptr)NULL; + tiles[i].buffers = NULL; + tiles[i].x = clamp(px, image_region.x, image_region.z); + tiles[i].y = clamp(py, image_region.y, image_region.w); + tiles[i].w = tiles[i].h = 0; + } + } + } + + assert(tiles[4].buffers); + device->map_neighbor_tiles(tile_device, tiles); + + /* The denoised result is written back to the original tile. */ + tiles[9] = tiles[4]; } void Session::unmap_neighbor_tiles(RenderTile *tiles, Device *tile_device) { - thread_scoped_lock tile_lock(tile_mutex); - device->unmap_neighbor_tiles(tile_device, tiles); + thread_scoped_lock tile_lock(tile_mutex); + device->unmap_neighbor_tiles(tile_device, tiles); } void Session::run_cpu() { - bool tiles_written = false; - - last_update_time = time_dt(); - - { - /* reset once to start */ - thread_scoped_lock reset_lock(delayed_reset.mutex); - thread_scoped_lock buffers_lock(buffers_mutex); - thread_scoped_lock display_lock(display_mutex); - - reset_(delayed_reset.params, delayed_reset.samples); - delayed_reset.do_reset = false; - } - - while(!progress.get_cancel()) { - /* advance to next tile */ - bool no_tiles = !tile_manager.next(); - bool need_tonemap = false; - - DeviceKernelStatus kernel_state = DEVICE_KERNEL_UNKNOWN; - if (no_tiles) { - kernel_state = device->get_active_kernel_switch_state(); - } - - if(params.background) { - /* if no work left and in background mode, we can stop immediately */ - if(no_tiles) { - progress.set_status("Finished"); - break; - } - } - - /* Don't go in pause mode when preview kernels are used - * When feature kernels become available the session will be resetted. */ - else if (no_tiles && kernel_state == DEVICE_KERNEL_WAITING_FOR_FEATURE_KERNEL) { - time_sleep(0.1); - } - else if (no_tiles && kernel_state == DEVICE_KERNEL_FEATURE_KERNEL_AVAILABLE) { - reset_cpu(tile_manager.params, params.samples); - } - - else { - /* if in interactive mode, and we are either paused or done for now, - * wait for pause condition notify to wake up again */ - thread_scoped_lock pause_lock(pause_mutex); - - if(!pause && delayed_reset.do_reset) { - /* reset once to start */ - thread_scoped_lock reset_lock(delayed_reset.mutex); - thread_scoped_lock buffers_lock(buffers_mutex); - thread_scoped_lock display_lock(display_mutex); - - reset_(delayed_reset.params, delayed_reset.samples); - delayed_reset.do_reset = false; - } - else if(pause || no_tiles) { - update_status_time(pause, no_tiles); - - while(1) { - scoped_timer pause_timer; - pause_cond.wait(pause_lock); - if(pause) { - progress.add_skip_time(pause_timer, params.background); - } - - update_status_time(pause, no_tiles); - progress.set_update(); - - if(!pause) - break; - } - } - - if(progress.get_cancel()) - break; - } - - if(!no_tiles) { - /* buffers mutex is locked entirely while rendering each - * sample, and released/reacquired on each iteration to allow - * reset and draw in between */ - thread_scoped_lock buffers_lock(buffers_mutex); - - /* update scene */ - scoped_timer update_timer; - if(update_scene()) { - profiler.reset(scene->shaders.size(), scene->objects.size()); - } - progress.add_skip_time(update_timer, params.background); - - if(!device->error_message().empty()) - progress.set_error(device->error_message()); - - if(progress.get_cancel()) - break; - - /* update status and timing */ - update_status_time(); - - /* render */ - render(); - - /* update status and timing */ - update_status_time(); - - if(!params.background) - need_tonemap = true; - - if(!device->error_message().empty()) - progress.set_error(device->error_message()); - } - - device->task_wait(); - - { - thread_scoped_lock reset_lock(delayed_reset.mutex); - thread_scoped_lock buffers_lock(buffers_mutex); - thread_scoped_lock display_lock(display_mutex); - - if(delayed_reset.do_reset) { - /* reset rendering if request from main thread */ - delayed_reset.do_reset = false; - reset_(delayed_reset.params, delayed_reset.samples); - } - else if(need_tonemap) { - /* tonemap only if we do not reset, we don't we don't - * want to show the result of an incomplete sample */ - tonemap(tile_manager.state.sample); - } - - if(!device->error_message().empty()) - progress.set_error(device->error_message()); - - tiles_written = update_progressive_refine(progress.get_cancel()); - } - - progress.set_update(); - } - - if(!tiles_written) - update_progressive_refine(true); + bool tiles_written = false; + + last_update_time = time_dt(); + + { + /* reset once to start */ + thread_scoped_lock reset_lock(delayed_reset.mutex); + thread_scoped_lock buffers_lock(buffers_mutex); + thread_scoped_lock display_lock(display_mutex); + + reset_(delayed_reset.params, delayed_reset.samples); + delayed_reset.do_reset = false; + } + + while (!progress.get_cancel()) { + /* advance to next tile */ + bool no_tiles = !tile_manager.next(); + bool need_tonemap = false; + + DeviceKernelStatus kernel_state = DEVICE_KERNEL_UNKNOWN; + if (no_tiles) { + kernel_state = device->get_active_kernel_switch_state(); + } + + if (params.background) { + /* if no work left and in background mode, we can stop immediately */ + if (no_tiles) { + progress.set_status("Finished"); + break; + } + } + + /* Don't go in pause mode when preview kernels are used + * When feature kernels become available the session will be resetted. */ + else if (no_tiles && kernel_state == DEVICE_KERNEL_WAITING_FOR_FEATURE_KERNEL) { + time_sleep(0.1); + } + else if (no_tiles && kernel_state == DEVICE_KERNEL_FEATURE_KERNEL_AVAILABLE) { + reset_cpu(tile_manager.params, params.samples); + } + + else { + /* if in interactive mode, and we are either paused or done for now, + * wait for pause condition notify to wake up again */ + thread_scoped_lock pause_lock(pause_mutex); + + if (!pause && delayed_reset.do_reset) { + /* reset once to start */ + thread_scoped_lock reset_lock(delayed_reset.mutex); + thread_scoped_lock buffers_lock(buffers_mutex); + thread_scoped_lock display_lock(display_mutex); + + reset_(delayed_reset.params, delayed_reset.samples); + delayed_reset.do_reset = false; + } + else if (pause || no_tiles) { + update_status_time(pause, no_tiles); + + while (1) { + scoped_timer pause_timer; + pause_cond.wait(pause_lock); + if (pause) { + progress.add_skip_time(pause_timer, params.background); + } + + update_status_time(pause, no_tiles); + progress.set_update(); + + if (!pause) + break; + } + } + + if (progress.get_cancel()) + break; + } + + if (!no_tiles) { + /* buffers mutex is locked entirely while rendering each + * sample, and released/reacquired on each iteration to allow + * reset and draw in between */ + thread_scoped_lock buffers_lock(buffers_mutex); + + /* update scene */ + scoped_timer update_timer; + if (update_scene()) { + profiler.reset(scene->shaders.size(), scene->objects.size()); + } + progress.add_skip_time(update_timer, params.background); + + if (!device->error_message().empty()) + progress.set_error(device->error_message()); + + if (progress.get_cancel()) + break; + + /* update status and timing */ + update_status_time(); + + /* render */ + render(); + + /* update status and timing */ + update_status_time(); + + if (!params.background) + need_tonemap = true; + + if (!device->error_message().empty()) + progress.set_error(device->error_message()); + } + + device->task_wait(); + + { + thread_scoped_lock reset_lock(delayed_reset.mutex); + thread_scoped_lock buffers_lock(buffers_mutex); + thread_scoped_lock display_lock(display_mutex); + + if (delayed_reset.do_reset) { + /* reset rendering if request from main thread */ + delayed_reset.do_reset = false; + reset_(delayed_reset.params, delayed_reset.samples); + } + else if (need_tonemap) { + /* tonemap only if we do not reset, we don't we don't + * want to show the result of an incomplete sample */ + tonemap(tile_manager.state.sample); + } + + if (!device->error_message().empty()) + progress.set_error(device->error_message()); + + tiles_written = update_progressive_refine(progress.get_cancel()); + } + + progress.set_update(); + } + + if (!tiles_written) + update_progressive_refine(true); } DeviceRequestedFeatures Session::get_requested_device_features() { - /* TODO(sergey): Consider moving this to the Scene level. */ - DeviceRequestedFeatures requested_features; - requested_features.experimental = params.experimental; - - scene->shader_manager->get_requested_features( - scene, - &requested_features); - - /* This features are not being tweaked as often as shaders, - * so could be done selective magic for the viewport as well. - */ - bool use_motion = scene->need_motion() == Scene::MotionType::MOTION_BLUR; - requested_features.use_hair = false; - requested_features.use_object_motion = false; - requested_features.use_camera_motion = use_motion && scene->camera->use_motion(); - foreach(Object *object, scene->objects) { - Mesh *mesh = object->mesh; - if(mesh->num_curves()) { - requested_features.use_hair = true; - } - if (use_motion) { - requested_features.use_object_motion |= object->use_motion() | mesh->use_motion_blur; - requested_features.use_camera_motion |= mesh->use_motion_blur; - } + /* TODO(sergey): Consider moving this to the Scene level. */ + DeviceRequestedFeatures requested_features; + requested_features.experimental = params.experimental; + + scene->shader_manager->get_requested_features(scene, &requested_features); + + /* This features are not being tweaked as often as shaders, + * so could be done selective magic for the viewport as well. + */ + bool use_motion = scene->need_motion() == Scene::MotionType::MOTION_BLUR; + requested_features.use_hair = false; + requested_features.use_object_motion = false; + requested_features.use_camera_motion = use_motion && scene->camera->use_motion(); + foreach (Object *object, scene->objects) { + Mesh *mesh = object->mesh; + if (mesh->num_curves()) { + requested_features.use_hair = true; + } + if (use_motion) { + requested_features.use_object_motion |= object->use_motion() | mesh->use_motion_blur; + requested_features.use_camera_motion |= mesh->use_motion_blur; + } #ifdef WITH_OPENSUBDIV - if(mesh->subdivision_type != Mesh::SUBDIVISION_NONE) { - requested_features.use_patch_evaluation = true; - } + if (mesh->subdivision_type != Mesh::SUBDIVISION_NONE) { + requested_features.use_patch_evaluation = true; + } #endif - if(object->is_shadow_catcher) { - requested_features.use_shadow_tricks = true; - } - requested_features.use_true_displacement |= mesh->has_true_displacement(); - } - - requested_features.use_background_light = scene->light_manager->has_background_light(scene); - - BakeManager *bake_manager = scene->bake_manager; - requested_features.use_baking = bake_manager->get_baking(); - requested_features.use_integrator_branched = (scene->integrator->method == Integrator::BRANCHED_PATH); - if(params.run_denoising) { - requested_features.use_denoising = true; - requested_features.use_shadow_tricks = true; - } - - return requested_features; + if (object->is_shadow_catcher) { + requested_features.use_shadow_tricks = true; + } + requested_features.use_true_displacement |= mesh->has_true_displacement(); + } + + requested_features.use_background_light = scene->light_manager->has_background_light(scene); + + BakeManager *bake_manager = scene->bake_manager; + requested_features.use_baking = bake_manager->get_baking(); + requested_features.use_integrator_branched = (scene->integrator->method == + Integrator::BRANCHED_PATH); + if (params.run_denoising) { + requested_features.use_denoising = true; + requested_features.use_shadow_tricks = true; + } + + return requested_features; } bool Session::load_kernels(bool lock_scene) { - thread_scoped_lock scene_lock; - if(lock_scene) { - scene_lock = thread_scoped_lock(scene->mutex); - } - - DeviceRequestedFeatures requested_features = get_requested_device_features(); - - if(!kernels_loaded || loaded_kernel_features.modified(requested_features)) { - progress.set_status("Loading render kernels (may take a few minutes the first time)"); - - scoped_timer timer; - - VLOG(2) << "Requested features:\n" << requested_features; - if(!device->load_kernels(requested_features)) { - string message = device->error_message(); - if(message.empty()) - message = "Failed loading render kernel, see console for errors"; - - progress.set_error(message); - progress.set_status("Error", message); - progress.set_update(); - return false; - } - - progress.add_skip_time(timer, false); - VLOG(1) << "Total time spent loading kernels: " << time_dt() - timer.get_start(); - - kernels_loaded = true; - loaded_kernel_features = requested_features; - return true; - } - return false; + thread_scoped_lock scene_lock; + if (lock_scene) { + scene_lock = thread_scoped_lock(scene->mutex); + } + + DeviceRequestedFeatures requested_features = get_requested_device_features(); + + if (!kernels_loaded || loaded_kernel_features.modified(requested_features)) { + progress.set_status("Loading render kernels (may take a few minutes the first time)"); + + scoped_timer timer; + + VLOG(2) << "Requested features:\n" << requested_features; + if (!device->load_kernels(requested_features)) { + string message = device->error_message(); + if (message.empty()) + message = "Failed loading render kernel, see console for errors"; + + progress.set_error(message); + progress.set_status("Error", message); + progress.set_update(); + return false; + } + + progress.add_skip_time(timer, false); + VLOG(1) << "Total time spent loading kernels: " << time_dt() - timer.get_start(); + + kernels_loaded = true; + loaded_kernel_features = requested_features; + return true; + } + return false; } void Session::run() { - if(params.use_profiling && (params.device.type == DEVICE_CPU)) { - profiler.start(); - } - - /* session thread loop */ - progress.set_status("Waiting for render to start"); - - /* run */ - if(!progress.get_cancel()) { - /* reset number of rendered samples */ - progress.reset_sample(); - - if(device_use_gl) - run_gpu(); - else - run_cpu(); - } - - profiler.stop(); - - /* progress update */ - if(progress.get_cancel()) - progress.set_status("Cancel", progress.get_cancel_message()); - else - progress.set_update(); + if (params.use_profiling && (params.device.type == DEVICE_CPU)) { + profiler.start(); + } + + /* session thread loop */ + progress.set_status("Waiting for render to start"); + + /* run */ + if (!progress.get_cancel()) { + /* reset number of rendered samples */ + progress.reset_sample(); + + if (device_use_gl) + run_gpu(); + else + run_cpu(); + } + + profiler.stop(); + + /* progress update */ + if (progress.get_cancel()) + progress.set_status("Cancel", progress.get_cancel_message()); + else + progress.set_update(); } -bool Session::draw(BufferParams& buffer_params, DeviceDrawParams &draw_params) +bool Session::draw(BufferParams &buffer_params, DeviceDrawParams &draw_params) { - if(device_use_gl) - return draw_gpu(buffer_params, draw_params); - else - return draw_cpu(buffer_params, draw_params); + if (device_use_gl) + return draw_gpu(buffer_params, draw_params); + else + return draw_cpu(buffer_params, draw_params); } -void Session::reset_(BufferParams& buffer_params, int samples) +void Session::reset_(BufferParams &buffer_params, int samples) { - if(buffers && buffer_params.modified(tile_manager.params)) { - gpu_draw_ready = false; - buffers->reset(buffer_params); - if(display) { - display->reset(buffer_params); - } - } - - tile_manager.reset(buffer_params, samples); - progress.reset_sample(); - - bool show_progress = params.background || tile_manager.get_num_effective_samples() != INT_MAX; - progress.set_total_pixel_samples(show_progress? tile_manager.state.total_pixel_samples : 0); - - if(!params.background) - progress.set_start_time(); - progress.set_render_start_time(); + if (buffers && buffer_params.modified(tile_manager.params)) { + gpu_draw_ready = false; + buffers->reset(buffer_params); + if (display) { + display->reset(buffer_params); + } + } + + tile_manager.reset(buffer_params, samples); + progress.reset_sample(); + + bool show_progress = params.background || tile_manager.get_num_effective_samples() != INT_MAX; + progress.set_total_pixel_samples(show_progress ? tile_manager.state.total_pixel_samples : 0); + + if (!params.background) + progress.set_start_time(); + progress.set_render_start_time(); } -void Session::reset(BufferParams& buffer_params, int samples) +void Session::reset(BufferParams &buffer_params, int samples) { - if(device_use_gl) - reset_gpu(buffer_params, samples); - else - reset_cpu(buffer_params, samples); + if (device_use_gl) + reset_gpu(buffer_params, samples); + else + reset_cpu(buffer_params, samples); } void Session::set_samples(int samples) { - if(samples != params.samples) { - params.samples = samples; - tile_manager.set_samples(samples); - - { - thread_scoped_lock pause_lock(pause_mutex); - } - pause_cond.notify_all(); - } + if (samples != params.samples) { + params.samples = samples; + tile_manager.set_samples(samples); + + { + thread_scoped_lock pause_lock(pause_mutex); + } + pause_cond.notify_all(); + } } void Session::set_pause(bool pause_) { - bool notify = false; + bool notify = false; - { - thread_scoped_lock pause_lock(pause_mutex); + { + thread_scoped_lock pause_lock(pause_mutex); - if(pause != pause_) { - pause = pause_; - notify = true; - } - } + if (pause != pause_) { + pause = pause_; + notify = true; + } + } - if(notify) - pause_cond.notify_all(); + if (notify) + pause_cond.notify_all(); } void Session::wait() { - if (session_thread) { - session_thread->join(); - delete session_thread; - } + if (session_thread) { + session_thread->join(); + delete session_thread; + } - session_thread = NULL; + session_thread = NULL; } bool Session::update_scene() { - thread_scoped_lock scene_lock(scene->mutex); - - /* update camera if dimensions changed for progressive render. the camera - * knows nothing about progressive or cropped rendering, it just gets the - * image dimensions passed in */ - Camera *cam = scene->camera; - int width = tile_manager.state.buffer.full_width; - int height = tile_manager.state.buffer.full_height; - int resolution = tile_manager.state.resolution_divider; - - if(width != cam->width || height != cam->height) { - cam->width = width; - cam->height = height; - cam->resolution = resolution; - cam->tag_update(); - } - - /* number of samples is needed by multi jittered - * sampling pattern and by baking */ - Integrator *integrator = scene->integrator; - BakeManager *bake_manager = scene->bake_manager; - - if(integrator->sampling_pattern == SAMPLING_PATTERN_CMJ || - bake_manager->get_baking()) - { - int aa_samples = tile_manager.num_samples; - - if(aa_samples != integrator->aa_samples) { - integrator->aa_samples = aa_samples; - integrator->tag_update(scene); - } - } - - /* update scene */ - if(scene->need_update()) { - bool new_kernels_needed = load_kernels(false); - - /* Update max_closures. */ - KernelIntegrator *kintegrator = &scene->dscene.data.integrator; - if(params.background) { - kintegrator->max_closures = get_max_closure_count(); - } - else { - /* Currently viewport render is faster with higher max_closures, needs investigating. */ - kintegrator->max_closures = MAX_CLOSURE; - } - - progress.set_status("Updating Scene"); - MEM_GUARDED_CALL(&progress, scene->device_update, device, progress); - - DeviceKernelStatus kernel_switch_status = device->get_active_kernel_switch_state(); - bool kernel_switch_needed = kernel_switch_status == DEVICE_KERNEL_FEATURE_KERNEL_AVAILABLE || - kernel_switch_status == DEVICE_KERNEL_FEATURE_KERNEL_INVALID; - if (kernel_switch_status == DEVICE_KERNEL_WAITING_FOR_FEATURE_KERNEL) { - progress.set_kernel_status("Compiling render kernels"); - } - if (new_kernels_needed || kernel_switch_needed) { - progress.set_kernel_status("Compiling render kernels"); - device->wait_for_availability(loaded_kernel_features); - progress.set_kernel_status(""); - } - - if (kernel_switch_needed) { - reset(tile_manager.params, params.samples); - } - return true; - } - return false; + thread_scoped_lock scene_lock(scene->mutex); + + /* update camera if dimensions changed for progressive render. the camera + * knows nothing about progressive or cropped rendering, it just gets the + * image dimensions passed in */ + Camera *cam = scene->camera; + int width = tile_manager.state.buffer.full_width; + int height = tile_manager.state.buffer.full_height; + int resolution = tile_manager.state.resolution_divider; + + if (width != cam->width || height != cam->height) { + cam->width = width; + cam->height = height; + cam->resolution = resolution; + cam->tag_update(); + } + + /* number of samples is needed by multi jittered + * sampling pattern and by baking */ + Integrator *integrator = scene->integrator; + BakeManager *bake_manager = scene->bake_manager; + + if (integrator->sampling_pattern == SAMPLING_PATTERN_CMJ || bake_manager->get_baking()) { + int aa_samples = tile_manager.num_samples; + + if (aa_samples != integrator->aa_samples) { + integrator->aa_samples = aa_samples; + integrator->tag_update(scene); + } + } + + /* update scene */ + if (scene->need_update()) { + bool new_kernels_needed = load_kernels(false); + + /* Update max_closures. */ + KernelIntegrator *kintegrator = &scene->dscene.data.integrator; + if (params.background) { + kintegrator->max_closures = get_max_closure_count(); + } + else { + /* Currently viewport render is faster with higher max_closures, needs investigating. */ + kintegrator->max_closures = MAX_CLOSURE; + } + + progress.set_status("Updating Scene"); + MEM_GUARDED_CALL(&progress, scene->device_update, device, progress); + + DeviceKernelStatus kernel_switch_status = device->get_active_kernel_switch_state(); + bool kernel_switch_needed = kernel_switch_status == DEVICE_KERNEL_FEATURE_KERNEL_AVAILABLE || + kernel_switch_status == DEVICE_KERNEL_FEATURE_KERNEL_INVALID; + if (kernel_switch_status == DEVICE_KERNEL_WAITING_FOR_FEATURE_KERNEL) { + progress.set_kernel_status("Compiling render kernels"); + } + if (new_kernels_needed || kernel_switch_needed) { + progress.set_kernel_status("Compiling render kernels"); + device->wait_for_availability(loaded_kernel_features); + progress.set_kernel_status(""); + } + + if (kernel_switch_needed) { + reset(tile_manager.params, params.samples); + } + return true; + } + return false; } void Session::update_status_time(bool show_pause, bool show_done) { - int progressive_sample = tile_manager.state.sample; - int num_samples = tile_manager.get_num_effective_samples(); - - int tile = progress.get_rendered_tiles(); - int num_tiles = tile_manager.state.num_tiles; - - /* update status */ - string status, substatus; - - if(!params.progressive) { - const bool is_cpu = params.device.type == DEVICE_CPU; - const bool rendering_finished = (tile == num_tiles); - const bool is_last_tile = (tile + 1) == num_tiles; - - substatus = string_printf("Rendered %d/%d Tiles", tile, num_tiles); - - if(!rendering_finished && (device->show_samples() || (is_cpu && is_last_tile))) { - /* Some devices automatically support showing the sample number: - * - CUDADevice - * - OpenCLDevice when using the megakernel (the split kernel renders multiple - * samples at the same time, so the current sample isn't really defined) - * - CPUDevice when using one thread - * For these devices, the current sample is always shown. - * - * The other option is when the last tile is currently being rendered by the CPU. - */ - substatus += string_printf(", Sample %d/%d", progress.get_current_sample(), num_samples); - } - if(params.full_denoising) { - substatus += string_printf(", Denoised %d tiles", progress.get_denoised_tiles()); - } - else if(params.run_denoising) { - substatus += string_printf(", Prefiltered %d tiles", progress.get_denoised_tiles()); - } - } - else if(tile_manager.num_samples == INT_MAX) - substatus = string_printf("Path Tracing Sample %d", progressive_sample+1); - else - substatus = string_printf("Path Tracing Sample %d/%d", - progressive_sample+1, - num_samples); - - if(show_pause) { - status = "Rendering Paused"; - } - else if(show_done) { - status = "Rendering Done"; - progress.set_end_time(); /* Save end time so that further calls to get_time are accurate. */ - } - else { - status = substatus; - substatus.clear(); - } - - progress.set_status(status, substatus); + int progressive_sample = tile_manager.state.sample; + int num_samples = tile_manager.get_num_effective_samples(); + + int tile = progress.get_rendered_tiles(); + int num_tiles = tile_manager.state.num_tiles; + + /* update status */ + string status, substatus; + + if (!params.progressive) { + const bool is_cpu = params.device.type == DEVICE_CPU; + const bool rendering_finished = (tile == num_tiles); + const bool is_last_tile = (tile + 1) == num_tiles; + + substatus = string_printf("Rendered %d/%d Tiles", tile, num_tiles); + + if (!rendering_finished && (device->show_samples() || (is_cpu && is_last_tile))) { + /* Some devices automatically support showing the sample number: + * - CUDADevice + * - OpenCLDevice when using the megakernel (the split kernel renders multiple + * samples at the same time, so the current sample isn't really defined) + * - CPUDevice when using one thread + * For these devices, the current sample is always shown. + * + * The other option is when the last tile is currently being rendered by the CPU. + */ + substatus += string_printf(", Sample %d/%d", progress.get_current_sample(), num_samples); + } + if (params.full_denoising) { + substatus += string_printf(", Denoised %d tiles", progress.get_denoised_tiles()); + } + else if (params.run_denoising) { + substatus += string_printf(", Prefiltered %d tiles", progress.get_denoised_tiles()); + } + } + else if (tile_manager.num_samples == INT_MAX) + substatus = string_printf("Path Tracing Sample %d", progressive_sample + 1); + else + substatus = string_printf("Path Tracing Sample %d/%d", progressive_sample + 1, num_samples); + + if (show_pause) { + status = "Rendering Paused"; + } + else if (show_done) { + status = "Rendering Done"; + progress.set_end_time(); /* Save end time so that further calls to get_time are accurate. */ + } + else { + status = substatus; + substatus.clear(); + } + + progress.set_status(status, substatus); } void Session::render() { - /* Clear buffers. */ - if(buffers && tile_manager.state.sample == tile_manager.range_start_sample) { - buffers->zero(); - } - - /* Add path trace task. */ - DeviceTask task(DeviceTask::RENDER); - - task.acquire_tile = function_bind(&Session::acquire_tile, this, _1, _2); - task.release_tile = function_bind(&Session::release_tile, this, _1); - task.map_neighbor_tiles = function_bind(&Session::map_neighbor_tiles, this, _1, _2); - task.unmap_neighbor_tiles = function_bind(&Session::unmap_neighbor_tiles, this, _1, _2); - task.get_cancel = function_bind(&Progress::get_cancel, &this->progress); - task.update_tile_sample = function_bind(&Session::update_tile_sample, this, _1); - task.update_progress_sample = function_bind(&Progress::add_samples, &this->progress, _1, _2); - task.need_finish_queue = params.progressive_refine; - task.integrator_branched = scene->integrator->method == Integrator::BRANCHED_PATH; - task.requested_tile_size = params.tile_size; - task.passes_size = tile_manager.params.get_passes_size(); - - if(params.run_denoising) { - task.denoising = params.denoising; - - assert(!scene->film->need_update); - task.pass_stride = scene->film->pass_stride; - task.target_pass_stride = task.pass_stride; - task.pass_denoising_data = scene->film->denoising_data_offset; - task.pass_denoising_clean = scene->film->denoising_clean_offset; - - task.denoising_from_render = true; - task.denoising_do_filter = params.full_denoising; - task.denoising_write_passes = params.write_denoising_passes; - } - - device->task_add(task); + /* Clear buffers. */ + if (buffers && tile_manager.state.sample == tile_manager.range_start_sample) { + buffers->zero(); + } + + /* Add path trace task. */ + DeviceTask task(DeviceTask::RENDER); + + task.acquire_tile = function_bind(&Session::acquire_tile, this, _1, _2); + task.release_tile = function_bind(&Session::release_tile, this, _1); + task.map_neighbor_tiles = function_bind(&Session::map_neighbor_tiles, this, _1, _2); + task.unmap_neighbor_tiles = function_bind(&Session::unmap_neighbor_tiles, this, _1, _2); + task.get_cancel = function_bind(&Progress::get_cancel, &this->progress); + task.update_tile_sample = function_bind(&Session::update_tile_sample, this, _1); + task.update_progress_sample = function_bind(&Progress::add_samples, &this->progress, _1, _2); + task.need_finish_queue = params.progressive_refine; + task.integrator_branched = scene->integrator->method == Integrator::BRANCHED_PATH; + task.requested_tile_size = params.tile_size; + task.passes_size = tile_manager.params.get_passes_size(); + + if (params.run_denoising) { + task.denoising = params.denoising; + + assert(!scene->film->need_update); + task.pass_stride = scene->film->pass_stride; + task.target_pass_stride = task.pass_stride; + task.pass_denoising_data = scene->film->denoising_data_offset; + task.pass_denoising_clean = scene->film->denoising_clean_offset; + + task.denoising_from_render = true; + task.denoising_do_filter = params.full_denoising; + task.denoising_write_passes = params.write_denoising_passes; + } + + device->task_add(task); } void Session::tonemap(int sample) { - /* add tonemap task */ - DeviceTask task(DeviceTask::FILM_CONVERT); - - task.x = tile_manager.state.buffer.full_x; - task.y = tile_manager.state.buffer.full_y; - task.w = tile_manager.state.buffer.width; - task.h = tile_manager.state.buffer.height; - task.rgba_byte = display->rgba_byte.device_pointer; - task.rgba_half = display->rgba_half.device_pointer; - task.buffer = buffers->buffer.device_pointer; - task.sample = sample; - tile_manager.state.buffer.get_offset_stride(task.offset, task.stride); - - if(task.w > 0 && task.h > 0) { - device->task_add(task); - device->task_wait(); - - /* set display to new size */ - display->draw_set(task.w, task.h); - } - - display_outdated = false; + /* add tonemap task */ + DeviceTask task(DeviceTask::FILM_CONVERT); + + task.x = tile_manager.state.buffer.full_x; + task.y = tile_manager.state.buffer.full_y; + task.w = tile_manager.state.buffer.width; + task.h = tile_manager.state.buffer.height; + task.rgba_byte = display->rgba_byte.device_pointer; + task.rgba_half = display->rgba_half.device_pointer; + task.buffer = buffers->buffer.device_pointer; + task.sample = sample; + tile_manager.state.buffer.get_offset_stride(task.offset, task.stride); + + if (task.w > 0 && task.h > 0) { + device->task_add(task); + device->task_wait(); + + /* set display to new size */ + display->draw_set(task.w, task.h); + } + + display_outdated = false; } bool Session::update_progressive_refine(bool cancel) { - int sample = tile_manager.state.sample + 1; - bool write = sample == tile_manager.num_samples || cancel; - - double current_time = time_dt(); - - if(current_time - last_update_time < params.progressive_update_timeout) { - /* if last sample was processed, we need to write buffers anyway */ - if(!write && sample != 1) - return false; - } - - if(params.progressive_refine) { - foreach(Tile& tile, tile_manager.state.tiles) { - if(!tile.buffers) { - continue; - } - - RenderTile rtile; - rtile.x = tile_manager.state.buffer.full_x + tile.x; - rtile.y = tile_manager.state.buffer.full_y + tile.y; - rtile.w = tile.w; - rtile.h = tile.h; - rtile.sample = sample; - rtile.buffers = tile.buffers; - - if(write) { - if(write_render_tile_cb) - write_render_tile_cb(rtile); - } - else { - if(update_render_tile_cb) - update_render_tile_cb(rtile, true); - } - } - } - - last_update_time = current_time; - - return write; + int sample = tile_manager.state.sample + 1; + bool write = sample == tile_manager.num_samples || cancel; + + double current_time = time_dt(); + + if (current_time - last_update_time < params.progressive_update_timeout) { + /* if last sample was processed, we need to write buffers anyway */ + if (!write && sample != 1) + return false; + } + + if (params.progressive_refine) { + foreach (Tile &tile, tile_manager.state.tiles) { + if (!tile.buffers) { + continue; + } + + RenderTile rtile; + rtile.x = tile_manager.state.buffer.full_x + tile.x; + rtile.y = tile_manager.state.buffer.full_y + tile.y; + rtile.w = tile.w; + rtile.h = tile.h; + rtile.sample = sample; + rtile.buffers = tile.buffers; + + if (write) { + if (write_render_tile_cb) + write_render_tile_cb(rtile); + } + else { + if (update_render_tile_cb) + update_render_tile_cb(rtile, true); + } + } + } + + last_update_time = current_time; + + return write; } void Session::device_free() { - scene->device_free(); + scene->device_free(); - tile_manager.device_free(); + tile_manager.device_free(); - /* used from background render only, so no need to - * re-create render/display buffers here - */ + /* used from background render only, so no need to + * re-create render/display buffers here + */ } void Session::collect_statistics(RenderStats *render_stats) { - scene->collect_statistics(render_stats); - if(params.use_profiling && (params.device.type == DEVICE_CPU)) { - render_stats->collect_profiling(scene, profiler); - } + scene->collect_statistics(render_stats); + if (params.use_profiling && (params.device.type == DEVICE_CPU)) { + render_stats->collect_profiling(scene, profiler); + } } int Session::get_max_closure_count() { - if (scene->shader_manager->use_osl()) { - /* OSL always needs the maximum as we can't predict the - * number of closures a shader might generate. */ - return MAX_CLOSURE; - } - - int max_closures = 0; - for(int i = 0; i < scene->shaders.size(); i++) { - int num_closures = scene->shaders[i]->graph->get_num_closures(); - max_closures = max(max_closures, num_closures); - } - max_closure_global = max(max_closure_global, max_closures); - - if (max_closure_global > MAX_CLOSURE) { - /* This is usually harmless as more complex shader tend to get many - * closures discarded due to mixing or low weights. We need to limit - * to MAX_CLOSURE as this is hardcoded in CPU/mega kernels, and it - * avoids excessive memory usage for split kernels. */ - VLOG(2) << "Maximum number of closures exceeded: " - << max_closure_global - << " > " - << MAX_CLOSURE; - - max_closure_global = MAX_CLOSURE; - } - - return max_closure_global; + if (scene->shader_manager->use_osl()) { + /* OSL always needs the maximum as we can't predict the + * number of closures a shader might generate. */ + return MAX_CLOSURE; + } + + int max_closures = 0; + for (int i = 0; i < scene->shaders.size(); i++) { + int num_closures = scene->shaders[i]->graph->get_num_closures(); + max_closures = max(max_closures, num_closures); + } + max_closure_global = max(max_closure_global, max_closures); + + if (max_closure_global > MAX_CLOSURE) { + /* This is usually harmless as more complex shader tend to get many + * closures discarded due to mixing or low weights. We need to limit + * to MAX_CLOSURE as this is hardcoded in CPU/mega kernels, and it + * avoids excessive memory usage for split kernels. */ + VLOG(2) << "Maximum number of closures exceeded: " << max_closure_global << " > " + << MAX_CLOSURE; + + max_closure_global = MAX_CLOSURE; + } + + return max_closure_global; } CCL_NAMESPACE_END |