From a2ebc5268f2b98ee7335e0054c177c849a45cfba Mon Sep 17 00:00:00 2001 From: Lukas Stockner Date: Sat, 26 Nov 2016 04:22:34 +0100 Subject: Cycles: Refactor Progress system to provide better estimates The Progress system in Cycles had two limitations so far: - It just counted tiles, but ignored their size. For example, when rendering a 600x500 image with 512x512 tiles, the right 88x500 tile would count for 50% of the progress, although it only covers 15% of the image. - Scene update time was incorrectly counted as rendering time - therefore, the remaining time started very long and gradually decreased. This patch fixes both problems: First of all, the Progress now has a function to ignore time spans, and that is used to ignore scene update time. The larger change is the tile size: Instead of counting samples per tile, so that the final value is num_samples*num_tiles, the code now counts every sample for every pixel, so that the final value is num_samples*num_pixels. Along with that, some unused variables were removed from the Progress and Session classes. Reviewers: brecht, sergey, #cycles Subscribers: brecht, candreacchio, sergey Differential Revision: https://developer.blender.org/D2214 --- intern/cycles/render/bake.cpp | 20 +++---- intern/cycles/render/bake.h | 3 +- intern/cycles/render/session.cpp | 110 ++++++++++++--------------------------- intern/cycles/render/session.h | 9 ++-- intern/cycles/render/tile.cpp | 37 ++++++++++--- intern/cycles/render/tile.h | 6 ++- 6 files changed, 79 insertions(+), 106 deletions(-) (limited to 'intern/cycles/render') diff --git a/intern/cycles/render/bake.cpp b/intern/cycles/render/bake.cpp index 13310a61761..d9a297002c6 100644 --- a/intern/cycles/render/bake.cpp +++ b/intern/cycles/render/bake.cpp @@ -135,20 +135,16 @@ bool BakeManager::bake(Device *device, DeviceScene *dscene, Scene *scene, Progre { size_t num_pixels = bake_data->size(); - progress.reset_sample(); - this->num_parts = 0; + int num_samples = is_aa_pass(shader_type)? scene->integrator->aa_samples : 1; - /* calculate the total parts for the progress bar */ + /* calculate the total pixel samples for the progress bar */ + total_pixel_samples = 0; for(size_t shader_offset = 0; shader_offset < num_pixels; shader_offset += m_shader_limit) { size_t shader_size = (size_t)fminf(num_pixels - shader_offset, m_shader_limit); - - DeviceTask task(DeviceTask::SHADER); - task.shader_w = shader_size; - - this->num_parts += device->get_split_task_count(task); + total_pixel_samples += shader_size * num_samples; } - - this->num_samples = is_aa_pass(shader_type)? scene->integrator->aa_samples : 1; + progress.reset_sample(); + progress.set_total_pixel_samples(total_pixel_samples); for(size_t shader_offset = 0; shader_offset < num_pixels; shader_offset += m_shader_limit) { size_t shader_size = (size_t)fminf(num_pixels - shader_offset, m_shader_limit); @@ -187,9 +183,9 @@ bool BakeManager::bake(Device *device, DeviceScene *dscene, Scene *scene, Progre task.shader_x = 0; task.offset = shader_offset; task.shader_w = d_output.size(); - task.num_samples = this->num_samples; + task.num_samples = num_samples; task.get_cancel = function_bind(&Progress::get_cancel, &progress); - task.update_progress_sample = function_bind(&Progress::increment_sample_update, &progress); + task.update_progress_sample = function_bind(&Progress::add_samples_update, &progress, _1, _2); device->task_add(task); device->task_wait(); diff --git a/intern/cycles/render/bake.h b/intern/cycles/render/bake.h index 8377e387197..25f5eb3c897 100644 --- a/intern/cycles/render/bake.h +++ b/intern/cycles/render/bake.h @@ -73,8 +73,7 @@ public: bool need_update; - int num_samples; - int num_parts; + int total_pixel_samples; private: BakeData *m_bake_data; diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp index 9d8c9fed7af..8e902243211 100644 --- a/intern/cycles/render/session.cpp +++ b/intern/cycles/render/session.cpp @@ -67,10 +67,7 @@ Session::Session(const SessionParams& params_) session_thread = NULL; scene = NULL; - start_time = 0.0; reset_time = 0.0; - preview_time = 0.0; - paused_time = 0.0; last_update_time = 0.0; delayed_reset.do_reset = false; @@ -201,12 +198,10 @@ void Session::run_gpu() { bool tiles_written = false; - start_time = time_dt(); reset_time = time_dt(); - paused_time = 0.0; last_update_time = time_dt(); - progress.set_render_start_time(start_time + paused_time); + progress.set_render_start_time(); while(!progress.get_cancel()) { /* advance to next tile */ @@ -233,13 +228,9 @@ void Session::run_gpu() update_status_time(pause, no_tiles); while(1) { - double pause_start = time_dt(); + scoped_timer pause_timer; pause_cond.wait(pause_lock); - paused_time += time_dt() - pause_start; - - if(!params.background) - progress.set_start_time(start_time + paused_time); - progress.set_render_start_time(start_time + paused_time); + progress.add_skip_time(pause_timer, params.background); update_status_time(pause, no_tiles); progress.set_update(); @@ -255,7 +246,9 @@ void Session::run_gpu() if(!no_tiles) { /* update scene */ + scoped_timer update_timer; update_scene(); + progress.add_skip_time(update_timer, params.background); if(!device->error_message().empty()) progress.set_error(device->error_message()); @@ -523,13 +516,9 @@ void Session::run_cpu() update_status_time(pause, no_tiles); while(1) { - double pause_start = time_dt(); + scoped_timer pause_timer; pause_cond.wait(pause_lock); - paused_time += time_dt() - pause_start; - - if(!params.background) - progress.set_start_time(start_time + paused_time); - progress.set_render_start_time(start_time + paused_time); + progress.add_skip_time(pause_timer, params.background); update_status_time(pause, no_tiles); progress.set_update(); @@ -550,7 +539,9 @@ void Session::run_cpu() thread_scoped_lock buffers_lock(buffers_mutex); /* update scene */ + scoped_timer update_timer; update_scene(); + progress.add_skip_time(update_timer, params.background); if(!device->error_message().empty()) progress.set_error(device->error_message()); @@ -718,14 +709,14 @@ void Session::reset_(BufferParams& buffer_params, int samples) } tile_manager.reset(buffer_params, samples); + progress.reset_sample(); - start_time = time_dt(); - preview_time = 0.0; - paused_time = 0.0; + 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(start_time); - progress.set_render_start_time(start_time); + progress.set_start_time(); + progress.set_render_start_time(); } void Session::reset(BufferParams& buffer_params, int samples) @@ -827,61 +818,40 @@ void Session::update_scene() void Session::update_status_time(bool show_pause, bool show_done) { - int sample = tile_manager.state.sample; - int resolution = tile_manager.state.resolution_divider; - int num_tiles = tile_manager.state.num_tiles; + int progressive_sample = tile_manager.state.sample; + int num_samples = tile_manager.get_num_effective_samples(); + int tile = tile_manager.state.num_rendered_tiles; + int num_tiles = tile_manager.state.num_tiles; /* update status */ string status, substatus; if(!params.progressive) { - const int progress_sample = progress.get_sample(), - num_samples = tile_manager.get_num_effective_samples(); - const bool is_gpu = params.device.type == DEVICE_CUDA || params.device.type == DEVICE_OPENCL; - const bool is_multidevice = params.device.multi_devices.size() > 1; const bool is_cpu = params.device.type == DEVICE_CPU; - const bool is_last_tile = (num_samples * num_tiles - progress_sample) < num_samples; + const bool is_last_tile = (progress.get_finished_tiles() + 1) == num_tiles; substatus = string_printf("Path Tracing Tile %d/%d", tile, num_tiles); - if((is_gpu && !is_multidevice && !device->info.use_split_kernel) || - (is_cpu && (num_tiles == 1 || is_last_tile))) + if(device->show_samples() || (is_cpu && is_last_tile)) { - /* When using split-kernel (OpenCL) each thread in a tile will be working on a different - * sample. Can't display sample number when device uses split-kernel + /* 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. */ - - /* when rendering on GPU multithreading happens within single tile, as in - * tiles are handling sequentially and in this case we could display - * currently rendering sample number - * this helps a lot from feedback point of view. - * also display the info on CPU, when using 1 tile only - */ - - int status_sample = progress_sample; - if(tile > 1) { - /* sample counter is global for all tiles, subtract samples - * from already finished tiles to get sample counter for - * current tile only - */ - if(is_cpu && is_last_tile && num_tiles > 1) { - status_sample = num_samples - (num_samples * num_tiles - progress_sample); - } - else { - status_sample -= (tile - 1) * num_samples; - } - } - - substatus += string_printf(", Sample %d/%d", status_sample, num_samples); + substatus += string_printf(", Sample %d/%d", progress.get_current_sample(), num_samples); } } else if(tile_manager.num_samples == INT_MAX) - substatus = string_printf("Path Tracing Sample %d", sample+1); + substatus = string_printf("Path Tracing Sample %d", progressive_sample+1); else substatus = string_printf("Path Tracing Sample %d/%d", - sample+1, - tile_manager.get_num_effective_samples()); + progressive_sample+1, + num_samples); if(show_pause) { status = "Paused"; @@ -895,22 +865,6 @@ void Session::update_status_time(bool show_pause, bool show_done) } progress.set_status(status, substatus); - - /* update timing */ - if(preview_time == 0.0 && resolution == 1) - preview_time = time_dt(); - - double tile_time = (tile == 0 || sample == 0)? 0.0: (time_dt() - preview_time - paused_time) / sample; - - /* negative can happen when we pause a bit before rendering, can discard that */ - if(preview_time < 0.0) preview_time = 0.0; - - progress.set_tile(tile, tile_time); -} - -void Session::update_progress_sample() -{ - progress.increment_sample(); } void Session::path_trace() @@ -922,7 +876,7 @@ void Session::path_trace() task.release_tile = function_bind(&Session::release_tile, this, _1); 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(&Session::update_progress_sample, this); + 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; diff --git a/intern/cycles/render/session.h b/intern/cycles/render/session.h index 1db4692e171..c7ff1446171 100644 --- a/intern/cycles/render/session.h +++ b/intern/cycles/render/session.h @@ -145,6 +145,10 @@ public: void device_free(); + /* Returns the rendering progress or 0 if no progress can be determined + * (for example, when rendering with unlimited samples). */ + float get_progress(); + protected: struct DelayedReset { thread_mutex mutex; @@ -173,8 +177,6 @@ protected: void update_tile_sample(RenderTile& tile); void release_tile(RenderTile& tile); - void update_progress_sample(); - bool device_use_gl; thread *session_thread; @@ -194,10 +196,7 @@ protected: bool kernels_loaded; - double start_time; double reset_time; - double preview_time; - double paused_time; /* progressive refine */ double last_update_time; diff --git a/intern/cycles/render/tile.cpp b/intern/cycles/render/tile.cpp index 3a6dfea11a7..e59d0c843a3 100644 --- a/intern/cycles/render/tile.cpp +++ b/intern/cycles/render/tile.cpp @@ -108,36 +108,57 @@ TileManager::~TileManager() { } -void TileManager::reset(BufferParams& params_, int num_samples_) +static int get_divider(int w, int h, int start_resolution) { - params = params_; - int divider = 1; - int w = params.width, h = params.height; - if(start_resolution != INT_MAX) { while(w*h > start_resolution*start_resolution) { w = max(1, w/2); h = max(1, h/2); - divider *= 2; + divider <<= 1; } } + return divider; +} - num_samples = num_samples_; +void TileManager::reset(BufferParams& params_, int num_samples_) +{ + params = params_; + + set_samples(num_samples_); state.buffer = BufferParams(); state.sample = range_start_sample - 1; state.num_tiles = 0; state.num_rendered_tiles = 0; state.num_samples = 0; - state.resolution_divider = divider; + state.resolution_divider = get_divider(params.width, params.height, start_resolution); state.tiles.clear(); } void TileManager::set_samples(int num_samples_) { num_samples = num_samples_; + + /* No real progress indication is possible when using unlimited samples. */ + if(num_samples == INT_MAX) { + state.total_pixel_samples = 0; + } + else { + uint64_t pixel_samples = 0; + /* While rendering in the viewport, the initial preview resolution is increased to the native resolution + * before the actual rendering begins. Therefore, additional pixel samples will be rendered. */ + int divider = get_divider(params.width, params.height, start_resolution) / 2; + while(divider > 1) { + int image_w = max(1, params.width/divider); + int image_h = max(1, params.height/divider); + pixel_samples += image_w * image_h; + divider >>= 1; + } + + state.total_pixel_samples = pixel_samples + get_num_effective_samples() * params.width*params.height; + } } /* If sliced is false, splits image into tiles and assigns equal amount of tiles to every render device. diff --git a/intern/cycles/render/tile.h b/intern/cycles/render/tile.h index af1b1ed8b0f..5d92ebac355 100644 --- a/intern/cycles/render/tile.h +++ b/intern/cycles/render/tile.h @@ -64,6 +64,10 @@ public: int resolution_divider; int num_tiles; int num_rendered_tiles; + + /* Total samples over all pixels: Generally num_samples*num_pixels, + * but can be higher due to the initial resolution division for previews. */ + uint64_t total_pixel_samples; /* This vector contains a list of tiles for every logical device in the session. * In each list, the tiles are sorted according to the tile order setting. */ vector > tiles; @@ -91,7 +95,7 @@ public: /* Number to samples in the rendering range. */ int range_num_samples; - /* get number of actual samples to render. */ + /* Get number of actual samples to render. */ int get_num_effective_samples(); protected: -- cgit v1.2.3