Cycles: Allow rendering a range of resumable chunks
authorSergey Sharybin <sergey.vfx@gmail.com>
Wed, 15 Mar 2017 14:52:27 +0000 (15:52 +0100)
committerSergey Sharybin <sergey.vfx@gmail.com>
Wed, 15 Mar 2017 15:00:01 +0000 (16:00 +0100)
The range is controlled using the following command line arguments:

  --cycles-resumable-start-chunk
  --cycles-resumable-end-chunk

Those are 1-based index of range for rendering.

intern/cycles/blender/addon/engine.py
intern/cycles/blender/blender_python.cpp
intern/cycles/blender/blender_session.cpp
intern/cycles/blender/blender_session.h

index c9ee827aeeb756ee3289db151a63642038ffd53c..ab57dd44bdb670152425efcbf5bbf63b20a152cc 100644 (file)
@@ -59,6 +59,12 @@ def _configure_argument_parser():
     parser.add_argument("--cycles-resumable-current-chunk",
                         help="Current chunk of samples range to render",
                         default=None)
+    parser.add_argument("--cycles-resumable-start-chunk",
+                        help="Start chunk to render",
+                        default=None)
+    parser.add_argument("--cycles-resumable-end-chunk",
+                        help="End chunk to render",
+                        default=None)
     return parser
 
 
@@ -72,12 +78,19 @@ def _parse_command_line():
     parser = _configure_argument_parser()
     args, unknown = parser.parse_known_args(argv[argv.index("--") + 1:])
 
-    if args.cycles_resumable_num_chunks is not None and \
-       args.cycles_resumable_current_chunk is not None:
-        import _cycles
-        _cycles.set_resumable_chunks(
-                int(args.cycles_resumable_num_chunks),
-                int(args.cycles_resumable_current_chunk))
+    if args.cycles_resumable_num_chunks is not None:
+        if args.cycles_resumable_current_chunk is not None:
+            import _cycles
+            _cycles.set_resumable_chunk(
+                    int(args.cycles_resumable_num_chunks),
+                    int(args.cycles_resumable_current_chunk))
+        elif args.cycles_resumable_start_chunk is not None and \
+             args.cycles_resumable_end_chunk:
+            import _cycles
+            _cycles.set_resumable_chunk_range(
+                    int(args.cycles_resumable_num_chunks),
+                    int(args.cycles_resumable_start_chunk),
+                    int(args.cycles_resumable_end_chunk))
 
 
 def init():
index fc0df3410c6d2fc90f3bae8830fee23458c09e56..557c4e8c5f0c6ee8e6143794151943f0fa95120f 100644 (file)
@@ -644,7 +644,7 @@ static PyObject *debug_flags_reset_func(PyObject * /*self*/, PyObject * /*args*/
        Py_RETURN_NONE;
 }
 
-static PyObject *set_resumable_chunks_func(PyObject * /*self*/, PyObject *args)
+static PyObject *set_resumable_chunk_func(PyObject * /*self*/, PyObject *args)
 {
        int num_resumable_chunks, current_resumable_chunk;
        if(!PyArg_ParseTuple(args, "ii",
@@ -679,6 +679,53 @@ static PyObject *set_resumable_chunks_func(PyObject * /*self*/, PyObject *args)
        Py_RETURN_NONE;
 }
 
+static PyObject *set_resumable_chunk_range_func(PyObject * /*self*/, PyObject *args)
+{
+       int num_chunks, start_chunk, end_chunk;
+       if(!PyArg_ParseTuple(args, "iii",
+                            &num_chunks,
+                            &start_chunk,
+                            &end_chunk)) {
+               Py_RETURN_NONE;
+       }
+
+       if(num_chunks <= 0) {
+               fprintf(stderr, "Cycles: Bad value for number of resumable chunks.\n");
+               abort();
+               Py_RETURN_NONE;
+       }
+       if(start_chunk < 1 || start_chunk > num_chunks) {
+               fprintf(stderr, "Cycles: Bad value for start chunk number.\n");
+               abort();
+               Py_RETURN_NONE;
+       }
+       if(end_chunk < 1 || end_chunk > num_chunks) {
+               fprintf(stderr, "Cycles: Bad value for start chunk number.\n");
+               abort();
+               Py_RETURN_NONE;
+       }
+       if(start_chunk > end_chunk) {
+               fprintf(stderr, "Cycles: End chunk should be higher than start one.\n");
+               abort();
+               Py_RETURN_NONE;
+       }
+
+       VLOG(1) << "Initialized resumable render: "
+               << "num_resumable_chunks=" << num_chunks << ", "
+               << "start_resumable_chunk=" << start_chunk
+               << "end_resumable_chunk=" << end_chunk;
+       BlenderSession::num_resumable_chunks = num_chunks;
+       BlenderSession::start_resumable_chunk = start_chunk;
+       BlenderSession::end_resumable_chunk = end_chunk;
+
+       printf("Cycles: Will render chunks %d to %d of %d\n",
+              start_chunk,
+              end_chunk,
+              num_chunks);
+
+       Py_RETURN_NONE;
+}
+
 static PyObject *get_device_types_func(PyObject * /*self*/, PyObject * /*args*/)
 {
        vector<DeviceInfo>& devices = Device::available_devices();
@@ -718,7 +765,8 @@ static PyMethodDef methods[] = {
        {"debug_flags_reset", debug_flags_reset_func, METH_NOARGS, ""},
 
        /* Resumable render */
-       {"set_resumable_chunks", set_resumable_chunks_func, METH_VARARGS, ""},
+       {"set_resumable_chunk", set_resumable_chunk_func, METH_VARARGS, ""},
+       {"set_resumable_chunk_range", set_resumable_chunk_range_func, METH_VARARGS, ""},
 
        /* Compute Device selection */
        {"get_device_types", get_device_types_func, METH_VARARGS, ""},
index 2f30cbd961f581eed2eff63428db3701b6e8fa7f..c5f5ffe9928a35d35c2910055158612f127d00ca 100644 (file)
@@ -46,6 +46,8 @@ CCL_NAMESPACE_BEGIN
 bool BlenderSession::headless = false;
 int BlenderSession::num_resumable_chunks = 0;
 int BlenderSession::current_resumable_chunk = 0;
+int BlenderSession::start_resumable_chunk = 0;
+int BlenderSession::end_resumable_chunk = 0;
 
 BlenderSession::BlenderSession(BL::RenderEngine& b_engine,
                                BL::UserPreferences& b_userpref,
@@ -1342,9 +1344,21 @@ void BlenderSession::update_resumable_tile_manager(int num_samples)
                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;
+       const int num_samples_per_chunk = (int)ceilf((float)num_samples / num_resumable_chunks);
+
+       int range_start_sample, range_num_samples;
+       if(current_resumable_chunk != 0) {
+               /* Single chunk rendering. */
+               range_start_sample = num_samples_per_chunk * (current_resumable_chunk - 1);
+               range_num_samples = num_samples_per_chunk;
+       }
+       else {
+               /* Ranged-chunks. */
+               const int num_chunks = end_resumable_chunk - start_resumable_chunk + 1;
+               range_start_sample = num_samples_per_chunk * (start_resumable_chunk - 1);
+               range_num_samples = num_chunks * num_samples_per_chunk;
+       }
+       /* Make sure we don't overshoot. */
        if(range_start_sample + range_num_samples > num_samples) {
                range_num_samples = num_samples - range_num_samples;
        }
index 82fe218b4cea67b993080edd400f97fdc937019d..700b8acec1b3dffda00b1994302eb34162223053 100644 (file)
@@ -137,6 +137,10 @@ public:
        /* Current resumable chunk index to render. */
        static int current_resumable_chunk;
 
+       /* Alternative to single-chunk rendering to render a range of chunks. */
+       static int start_resumable_chunk;
+       static int end_resumable_chunk;
+
 protected:
        void do_write_update_render_result(BL::RenderResult& b_rr,
                                           BL::RenderLayer& b_rlay,