Cycles: improved rounding of sample chunks for resumable renders
authorSybren A. Stüvel <sybren@stuvel.eu>
Tue, 29 Jan 2019 17:08:12 +0000 (18:08 +0100)
committerSybren A. Stüvel <sybren@stuvel.eu>
Wed, 30 Jan 2019 09:51:06 +0000 (10:51 +0100)
When using `--cycles-resumable-num-chunks N` to render a subset of the
samples, having N close to the total number of samples causes rounding
issues.

For example, a file configured for 250 samples and 150 chunks should
have 1.6666 sample per chunk. The old code rounded this to 2 samples per
chunk, which would result in too many samples being rendered. When
rendering a single chunk this doesn't matter much, but when larger chunk
ranges are rendered with `--cycles-resumable-start-chunk` and
`--cycles-resumable-end-chunk` the rounding errors start to add up.

By multiplying with the number of chunks to render first, and only round
to integers after that, this issue is solved. In the above example,
rendering 3 chunks will correctly render 5 samples rather than 6.

When the requested number of chunks is larger than the number of samples
there will be duplicate samples (that is, sample N appearing both in
chunk M and M+1). In this case a warning is printed to stderr.

This is needed for T50977 Progressive render: use non-uniform sample
chunks.

Reviewed by: sergey

Differential Revision: https://developer.blender.org/D4282

intern/cycles/blender/blender_session.cpp

index 9949660169f62852d179b0f72b6004166b98dacf..7373d091ab7b192eab282b7a14958043b89d69a5 100644 (file)
@@ -30,6 +30,7 @@
 #include "render/shader.h"
 #include "render/stats.h"
 
+#include "util/util_algorithm.h"
 #include "util/util_color.h"
 #include "util/util_foreach.h"
 #include "util/util_function.h"
@@ -1395,9 +1396,15 @@ void BlenderSession::update_resumable_tile_manager(int num_samples)
                return;
        }
 
-       const int num_samples_per_chunk = (int)ceilf((float)num_samples / num_resumable_chunks);
+       if (num_resumable_chunks > num_samples) {
+               fprintf(stderr, "Cycles warning: more sample chunks (%d) than samples (%d), "
+                       "this will cause some samples to be included in multiple chunks.\n",
+                       num_resumable_chunks, num_samples);
+       }
+
+       const float num_samples_per_chunk = (float)num_samples / num_resumable_chunks;
 
-       int range_start_sample, range_num_samples;
+       float 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);
@@ -1409,19 +1416,25 @@ void BlenderSession::update_resumable_tile_manager(int num_samples)
                range_start_sample = num_samples_per_chunk * (start_resumable_chunk - 1);
                range_num_samples = num_chunks * num_samples_per_chunk;
        }
+
+       /* Round after doing the multiplications with num_chunks and num_samples_per_chunk
+        * to allow for many small chunks. */
+       int rounded_range_start_sample = (int)floor(range_start_sample + 0.5f);
+       int rounded_range_num_samples = max((int)floor(range_num_samples + 0.5f), 1);
+
        /* Make sure we don't overshoot. */
-       if(range_start_sample + range_num_samples > num_samples) {
-               range_num_samples = num_samples - range_num_samples;
+       if(rounded_range_start_sample + rounded_range_num_samples > num_samples) {
+               rounded_range_num_samples = num_samples - rounded_range_num_samples;
        }
 
        VLOG(1) << "Samples range start is " << range_start_sample << ", "
                << "number of samples to render is " << range_num_samples;
 
-       scene->integrator->start_sample = range_start_sample;
+       scene->integrator->start_sample = rounded_range_start_sample;
        scene->integrator->tag_update(scene);
 
-       session->tile_manager.range_start_sample = range_start_sample;
-       session->tile_manager.range_num_samples = range_num_samples;
+       session->tile_manager.range_start_sample = rounded_range_start_sample;
+       session->tile_manager.range_num_samples = rounded_range_num_samples;
 }
 
 void BlenderSession::free_blender_memory_if_possible()