diff options
author | Sergey Sharybin <sergey.vfx@gmail.com> | 2016-03-30 16:55:12 +0300 |
---|---|---|
committer | Sergey Sharybin <sergey.vfx@gmail.com> | 2016-03-30 17:02:27 +0300 |
commit | f8b9f4e9bbc10675de9bbc3088f2841381e23f78 (patch) | |
tree | b03a144fc66280350a72fc856c69ff59dfc763bb /intern | |
parent | ee364b63582c606977cc7dc54435d02fb9e1981c (diff) |
Cycles: Resumable render implementation for Cycles
This feature is also known by the name Samples Offset, which allows
artists to render animation with given amount of samples N, but then
render more samples, starting from N and ending with M (where M > N)
and merge renders together as if they rendered exactly M samples.
Surely such effect could be achieved by changing Seed variable, but
that has possible issues with correlation artifacts and requiring to
manually deal with per render layer samples and such.
While we can't support all possible renderfarm-related features in
Cycles it's nice to support really commonly used stuff.
Here's a command how to run Blender with the new feature enabled:
blender -- --cycles-resumable-num-chunks 24 --cycles-resumable-current-chunk 2
This command will divide samples range in 24 parts and render
range #2 (chunk number is 1-based).
This feature might be changed a bit after we'll do some tests here
in the studio with it.
Diffstat (limited to 'intern')
-rw-r--r-- | intern/cycles/blender/addon/engine.py | 31 | ||||
-rw-r--r-- | intern/cycles/blender/blender_python.cpp | 24 | ||||
-rw-r--r-- | intern/cycles/blender/blender_session.cpp | 55 | ||||
-rw-r--r-- | intern/cycles/blender/blender_session.h | 22 | ||||
-rw-r--r-- | intern/cycles/render/tile.cpp | 21 | ||||
-rw-r--r-- | intern/cycles/render/tile.h | 11 |
6 files changed, 151 insertions, 13 deletions
diff --git a/intern/cycles/blender/addon/engine.py b/intern/cycles/blender/addon/engine.py index 96dc3a59ef2..d4b7535b9ee 100644 --- a/intern/cycles/blender/addon/engine.py +++ b/intern/cycles/blender/addon/engine.py @@ -50,10 +50,39 @@ def _workaround_buggy_drivers(): _cycles.opencl_disable() +def _parse_command_line(): + import sys + + argv = sys.argv + if "--" not in argv: + return + + argv = argv[argv.index("--") + 1:] + + num_resumable_chunks = None + current_resumable_chunk = None + + # TODO(sergey): Add some nice error ptins if argument is not used properly. + idx = 0 + while idx < len(argv) - 1: + arg = argv[idx] + if arg == '--cycles-resumable-num-chunks': + num_resumable_chunks = int(argv[idx + 1]) + elif arg == '--cycles-resumable-current-chunk': + current_resumable_chunk = int(argv[idx + 1]) + idx += 1 + + if num_resumable_chunks is not None and current_resumable_chunk is not None: + import _cycles + _cycles.set_resumable_chunks(num_resumable_chunks, + current_resumable_chunk) + + def init(): import bpy import _cycles import os.path + import sys # Workaround possibly buggy legacy drivers which crashes on the OpenCL # device enumeration. @@ -72,7 +101,7 @@ def init(): user_path = os.path.dirname(os.path.abspath(bpy.utils.user_resource('CONFIG', ''))) _cycles.init(path, user_path, bpy.app.background) - + _parse_command_line() def exit(): import _cycles diff --git a/intern/cycles/blender/blender_python.cpp b/intern/cycles/blender/blender_python.cpp index b2294c8b68e..989ba80c001 100644 --- a/intern/cycles/blender/blender_python.cpp +++ b/intern/cycles/blender/blender_python.cpp @@ -638,6 +638,24 @@ static PyObject *debug_flags_reset_func(PyObject * /*self*/, PyObject * /*args*/ Py_RETURN_NONE; } +static PyObject *set_resumable_chunks_func(PyObject * /*self*/, PyObject *args) +{ + int num_resumable_chunks, current_resumable_chunk; + if(!PyArg_ParseTuple(args, "ii", + &num_resumable_chunks, + ¤t_resumable_chunk)) { + return NULL; + } + + VLOG(1) << "Initialized resumable render: " + << "num_resumable_chunks=" << num_resumable_chunks << ", " + << "current_resumable_chunk=" << current_resumable_chunk; + BlenderSession::num_resumable_chunks = num_resumable_chunks; + BlenderSession::current_resumable_chunk = current_resumable_chunk; + + Py_RETURN_NONE; +} + static PyMethodDef methods[] = { {"init", init_func, METH_VARARGS, ""}, {"exit", exit_func, METH_VARARGS, ""}, @@ -657,8 +675,14 @@ static PyMethodDef methods[] = { #ifdef WITH_OPENCL {"opencl_disable", opencl_disable_func, METH_NOARGS, ""}, #endif + + /* Debugging routines */ {"debug_flags_update", debug_flags_update_func, METH_VARARGS, ""}, {"debug_flags_reset", debug_flags_reset_func, METH_NOARGS, ""}, + + /* Resumable render */ + {"set_resumable_chunks", set_resumable_chunks_func, METH_VARARGS, ""}, + {NULL, NULL, 0, NULL}, }; diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp index 977ba3d34cf..92b5b7277f4 100644 --- a/intern/cycles/blender/blender_session.cpp +++ b/intern/cycles/blender/blender_session.cpp @@ -43,6 +43,8 @@ CCL_NAMESPACE_BEGIN bool BlenderSession::headless = false; +int BlenderSession::num_resumable_chunks = 0; +int BlenderSession::current_resumable_chunk = 0; BlenderSession::BlenderSession(BL::RenderEngine& b_engine, BL::UserPreferences& b_userpref, @@ -161,6 +163,8 @@ void BlenderSession::create_session() session->reset(buffer_params, session_params.samples); b_engine.use_highlight_tiles(session_params.progressive_refine == false); + + update_resumable_tile_manager(session_params.samples); } void BlenderSession::reset_session(BL::BlendData& b_data_, BL::Scene& b_scene_) @@ -510,14 +514,21 @@ void BlenderSession::render() &python_thread_state, b_rlay_name.c_str()); - /* update number of samples per layer */ + /* Update number of samples per layer. */ int samples = sync->get_layer_samples(); bool bound_samples = sync->get_layer_bound_samples(); + int effective_layer_samples; if(samples != 0 && (!bound_samples || (samples < session_params.samples))) - session->reset(buffer_params, samples); + effective_layer_samples = samples; else - session->reset(buffer_params, session_params.samples); + effective_layer_samples = session_params.samples; + + /* Update tile manager if we're doing resumable render. */ + update_resumable_tile_manager(effective_layer_samples); + + /* Update session itself. */ + session->reset(buffer_params, effective_layer_samples); /* render */ session->start(); @@ -712,6 +723,13 @@ void BlenderSession::do_write_update_render_result(BL::RenderResult& b_rr, vector<float> pixels(params.width*params.height*4); + /* Adjust absolute sample number to the range. */ + int sample = rtile.sample; + const int range_start_sample = session->tile_manager.range_start_sample; + if(range_start_sample != -1) { + sample -= range_start_sample; + } + if(!do_update_only) { /* copy each pass */ BL::RenderLayer::passes_iterator b_iter; @@ -724,7 +742,7 @@ void BlenderSession::do_write_update_render_result(BL::RenderResult& b_rr, int components = b_pass.channels(); /* copy pixels */ - if(!buffers->get_pass_rect(pass_type, exposure, rtile.sample, components, &pixels[0])) + if(!buffers->get_pass_rect(pass_type, exposure, sample, components, &pixels[0])) memset(&pixels[0], 0, pixels.size()*sizeof(float)); b_pass.rect(&pixels[0]); @@ -733,7 +751,7 @@ void BlenderSession::do_write_update_render_result(BL::RenderResult& b_rr, else { /* copy combined pass */ BL::RenderPass b_combined_pass(b_rlay.passes.find_by_type(BL::RenderPass::type_COMBINED, b_rview_name.c_str())); - if(buffers->get_pass_rect(PASS_COMBINED, exposure, rtile.sample, 4, &pixels[0])) + if(buffers->get_pass_rect(PASS_COMBINED, exposure, sample, 4, &pixels[0])) b_combined_pass.rect(&pixels[0]); } @@ -909,12 +927,12 @@ void BlenderSession::get_progress(float& progress, double& total_time, double& r int tile, sample, samples_per_tile; int tile_total = session->tile_manager.state.num_tiles; int samples = session->tile_manager.state.sample + 1; - int total_samples = session->tile_manager.num_samples; + int total_samples = session->tile_manager.get_num_effective_samples(); session->progress.get_tile(tile, total_time, render_time, tile_time); sample = session->progress.get_sample(); - samples_per_tile = session->tile_manager.num_samples; + samples_per_tile = session->tile_manager.get_num_effective_samples(); if(background && samples_per_tile && tile_total) progress = ((float)sample / (float)(tile_total * samples_per_tile)); @@ -1277,5 +1295,26 @@ bool BlenderSession::builtin_image_float_pixels(const string &builtin_name, void return false; } -CCL_NAMESPACE_END +void BlenderSession::update_resumable_tile_manager(int num_samples) +{ + const int num_resumable_chunks = BlenderSession::num_resumable_chunks, + current_resumable_chunk = BlenderSession::current_resumable_chunk; + if(num_resumable_chunks == 0) { + return; + } + + int num_samples_per_chunk = (int)ceilf((float)num_samples / num_resumable_chunks); + int range_start_sample = num_samples_per_chunk * (current_resumable_chunk - 1); + int range_num_samples = num_samples_per_chunk; + if(range_start_sample + range_num_samples > num_samples) { + range_num_samples = num_samples - range_num_samples; + } + VLOG(1) << "Samples range start is " << range_start_sample << ", " + << "number of samples to render is " << range_num_samples; + + session->tile_manager.range_start_sample = range_start_sample; + session->tile_manager.range_num_samples = range_num_samples; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/blender/blender_session.h b/intern/cycles/blender/blender_session.h index 8492d9f2c6b..66a6945cbc1 100644 --- a/intern/cycles/blender/blender_session.h +++ b/intern/cycles/blender/blender_session.h @@ -95,7 +95,6 @@ public: void update_bake_progress(); bool background; - static bool headless; Session *session; Scene *scene; BlenderSync *sync; @@ -120,6 +119,24 @@ public: void *python_thread_state; + /* Global state which is common for all render sessions created from Blender. + * Usually denotes command line arguments. + */ + + /* Blender is running from the command line, no windows are shown and some + * extra render optimization is possible (possible to free draw-only data and + * so on. + */ + static bool headless; + + /* ** Resumable render ** */ + + /* Overall number of chunks in which the sample range is to be devided. */ + static int num_resumable_chunks; + + /* Current resumable chunk index to render. */ + static int current_resumable_chunk; + protected: void do_write_update_render_result(BL::RenderResult& b_rr, BL::RenderLayer& b_rlay, @@ -131,6 +148,9 @@ protected: void builtin_image_info(const string &builtin_name, void *builtin_data, bool &is_float, int &width, int &height, int &depth, int &channels); bool builtin_image_pixels(const string &builtin_name, void *builtin_data, unsigned char *pixels); bool builtin_image_float_pixels(const string &builtin_name, void *builtin_data, float *pixels); + + /* Update tile manager to reflect resumable render settings. */ + void update_resumable_tile_manager(int num_samples); }; CCL_NAMESPACE_END diff --git a/intern/cycles/render/tile.cpp b/intern/cycles/render/tile.cpp index 3fb60735b65..3a6dfea11a7 100644 --- a/intern/cycles/render/tile.cpp +++ b/intern/cycles/render/tile.cpp @@ -97,6 +97,9 @@ TileManager::TileManager(bool progressive_, int num_samples_, int2 tile_size_, i preserve_tile_device = preserve_tile_device_; background = background_; + range_start_sample = 0; + range_num_samples = -1; + BufferParams buffer_params; reset(buffer_params, 0); } @@ -124,7 +127,7 @@ void TileManager::reset(BufferParams& params_, int num_samples_) num_samples = num_samples_; state.buffer = BufferParams(); - state.sample = -1; + state.sample = range_start_sample - 1; state.num_tiles = 0; state.num_rendered_tiles = 0; state.num_samples = 0; @@ -325,7 +328,11 @@ bool TileManager::next_tile(Tile& tile, int device) bool TileManager::done() { - return (state.sample+state.num_samples >= num_samples && state.resolution_divider == 1); + int end_sample = (range_num_samples == -1) + ? num_samples + : range_start_sample + range_num_samples; + return (state.resolution_divider == 1) && + (state.sample+state.num_samples >= end_sample); } bool TileManager::next() @@ -344,8 +351,10 @@ bool TileManager::next() if(progressive) state.num_samples = 1; - else + else if(range_num_samples == -1) state.num_samples = num_samples; + else + state.num_samples = range_num_samples; state.resolution_divider = 1; set_tiles(); @@ -354,5 +363,11 @@ bool TileManager::next() return true; } +int TileManager::get_num_effective_samples() +{ + return (range_num_samples == -1) ? num_samples + : range_num_samples; +} + CCL_NAMESPACE_END diff --git a/intern/cycles/render/tile.h b/intern/cycles/render/tile.h index 700e00c9e0a..af1b1ed8b0f 100644 --- a/intern/cycles/render/tile.h +++ b/intern/cycles/render/tile.h @@ -82,6 +82,17 @@ public: bool done(); void set_tile_order(TileOrder tile_order_) { tile_order = tile_order_; } + + /* ** Sample range rendering. ** */ + + /* Start sample in the range. */ + int range_start_sample; + + /* Number to samples in the rendering range. */ + int range_num_samples; + + /* get number of actual samples to render. */ + int get_num_effective_samples(); protected: void set_tiles(); |