Blender V5.0
render_scheduler.h
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
5#pragma once
6
9
10#include "session/buffers.h"
11
12#include "util/string.h"
13
15
16class SessionParams;
17class TileManager;
18
20 public:
22
23 /* Initialize render buffers.
24 * Includes steps like zeroing the buffer on the device, and optional reading of pixels from the
25 * baking target. */
26 bool init_render_buffers = false;
27
28 /* Path tracing samples information. */
29 struct {
30 int start_sample = 0;
31 int num_samples = 0;
34
35 struct {
36 /* Check for convergency and filter the mask. */
37 bool filter = false;
38
39 float threshold = 0.0f;
40
41 /* Reset convergency flag when filtering, forcing a re-check of whether pixel did converge. */
42 bool reset = false;
44
45 struct {
46 bool postprocess = false;
48
49 /* Work related on the current tile. */
50 struct {
51 /* Write render buffers of the current tile.
52 *
53 * It is up to the path trace to decide whether writing should happen via user-provided
54 * callback into the rendering software, or via tile manager into a partial file. */
55 bool write = false;
56
57 bool denoise = false;
59
60 /* Work related on the full-frame render buffer. */
61 struct {
62 /* Write full render result.
63 * Implies reading the partial file from disk. */
64 bool write = false;
66
67 /* Display which is used to visualize render result. */
68 struct {
69 /* Display needs to be updated for the new render. */
70 bool update = false;
71
72 /* Display can use denoised result if available. */
75
76 /* Re-balance multi-device scheduling after rendering this work.
77 * Note that the scheduler does not know anything about devices, so if there is only a single
78 * device used, then it is up for the PathTracer to ignore the balancing. */
79 bool rebalance = false;
80
81 /* Conversion to bool, to simplify checks about whether there is anything to be done for this
82 * work. */
83 operator bool() const
84 {
85 return path_trace.num_samples || adaptive_sampling.filter || display.update || tile.denoise ||
86 tile.write || full.write;
87 }
88};
89
91 public:
92 RenderScheduler(TileManager &tile_manager, const SessionParams &params);
93
94 /* Specify whether cryptomatte-related works are to be scheduled. */
95 void set_need_schedule_cryptomatte(bool need_schedule_cryptomatte);
96
97 /* Allows to disable work re-balancing works, allowing to schedule as much to a single device
98 * as possible. */
99 void set_need_schedule_rebalance(bool need_schedule_rebalance);
100
101 bool is_background() const;
102
104 bool is_denoiser_gpu_used() const;
105
106 void set_adaptive_sampling(const AdaptiveSampling &adaptive_sampling);
107 bool is_adaptive_sampling_used() const;
108
109 /* Setup parameters defining the sampling range.
110 *
111 * It is a single function setting up multiple parameters because there are inter-dependencies
112 * between these parameters.
113 *
114 * In simple cases the subset is not used and the given num_samples samples is rendered, and the
115 * subset length and offset are ignored.
116 *
117 * It is possible to render a subset of the overall samples. This is typically used to distribute
118 * rendering of a single frame across multiple computers. This subset rendering is enabled by
119 * setting use_sample_subset=true, and giving the desired offset and length of the subset. The
120 * subset offset is a 0-based sample index to start sampling from, and the length is the number
121 * of samples to render in this subset.
122 *
123 * When the subset rendering is enabled, num_samples is expected to be set to the overall number
124 * of samples to be rendered, and it is internally used to clamp the number of samples rendered
125 * by a subset. */
126 void set_sample_params(const int num_samples,
127 const bool use_sample_subset,
128 const int sample_subset_offset,
129 const int sample_subset_length);
130
131 /* Number of samples to render, starting from start sample.
132 * The scheduler will schedule work in the range of
133 * [start_sample, start_sample + num_samples - 1], inclusively. */
134 int get_num_samples() const;
135
136 /* For sample subset rendering, extra offset to be added to sample index
137 * for the sampling pattern to be shifted. */
138 int get_sample_offset() const;
139
140 /* Time limit for the path tracing tasks, in minutes.
141 * Zero disables the limit. */
142 void set_time_limit(const double time_limit);
143 double get_time_limit() const;
144
145 /* Get sample up to which rendering has been done.
146 * This is an absolute 0-based value.
147 *
148 * For example, if start sample is 10 and 5 samples were rendered, then this call will
149 * return 14.
150 *
151 * If there were no samples rendered, then the behavior is undefined. */
152 int get_rendered_sample() const;
153
154 /* Get number of samples rendered within the current scheduling session.
155 *
156 * For example, if start sample is 10 and 5 samples were rendered, then this call will
157 * return 5.
158 *
159 * Note that this is based on the scheduling information. In practice this means that if someone
160 * requested for work to render the scheduler considers the work done. */
161 int get_num_rendered_samples() const;
162
163 /* Reset scheduler, indicating that rendering will happen from scratch.
164 * Resets current rendered state, as well as scheduling information. */
165 void reset(const BufferParams &buffer_params);
166
167 /* Reset scheduler upon switching to a next tile.
168 * Will keep the same number of samples and full-frame render parameters, but will reset progress
169 * and allow schedule renders works from the beginning of the new tile. */
170 void reset_for_next_tile();
171
172 /* Reschedule adaptive sampling work when all pixels did converge.
173 * If there is nothing else to be done for the adaptive sampling (pixels did converge to the
174 * final threshold) then false is returned and the render scheduler will stop scheduling path
175 * tracing works. Otherwise will modify the work's adaptive sampling settings to continue with
176 * a lower threshold. */
178
179 /* Reschedule adaptive sampling work when the device is mostly on idle, but not all pixels yet
180 * converged.
181 * If re-scheduling is not possible (adaptive sampling is happening with the final threshold, and
182 * the path tracer is to finish the current pixels) then false is returned. */
184
185 /* Reschedule work when rendering has been requested to cancel.
186 *
187 * Will skip all work which is not needed anymore because no more samples will be added (for
188 * example, adaptive sampling filtering and convergence check will be skipped).
189 * Will enable all work needed to make sure all passes are communicated to the software.
190 *
191 * NOTE: Should be used before passing work to `PathTrace::render_samples()`. */
193
195
196 /* Report that the path tracer started to work, after scene update and loading kernels. */
197 void report_work_begin(const RenderWork &render_work);
198
199 /* Report time (in seconds) which corresponding part of work took. */
200 void report_path_trace_time(const RenderWork &render_work, const double time, bool is_cancelled);
201 void report_path_trace_occupancy(const RenderWork &render_work, const float occupancy);
202 void report_adaptive_filter_time(const RenderWork &render_work,
203 const double time,
204 bool is_cancelled);
205 void report_denoise_time(const RenderWork &render_work, const double time);
206 void report_display_update_time(const RenderWork &render_work, const double time);
207 void report_rebalance_time(const RenderWork &render_work,
208 const double time,
209 bool balance_changed);
210 void report_volume_guiding_denoise_time(const RenderWork &render_work, const double time);
211
212 bool volume_guiding_need_denoise() const;
213
214 /* Generate full multi-line report of the rendering process, including rendering parameters,
215 * times, and so on. */
216 string full_report() const;
217
218 void set_limit_samples_per_update(const int limit_samples);
219
220 protected:
221 /* Check whether all work has been scheduled and time limit was not exceeded.
222 *
223 * NOTE: Tricky bit: if the time limit was reached the done() is considered to be true, but some
224 * extra work needs to be scheduled to denoise and write final result. */
225 bool done() const;
226
227 /* Update scheduling state for a newly scheduled work.
228 * Takes care of things like checking whether work was ever denoised, tile was written and states
229 * like that. */
230 void update_state_for_render_work(const RenderWork &render_work);
231
232 /* Returns true if any work was scheduled. */
233 bool set_postprocess_render_work(RenderWork *render_work);
234
235 /* Set work which is to be performed after all tiles has been rendered. */
236 void set_full_frame_render_work(RenderWork *render_work);
237
238 /* Update start resolution divider based on the accumulated timing information, preserving nice
239 * feeling navigation feel. */
241
242 /* Calculate desired update interval in seconds based on the current timings and settings.
243 * Will give an interval which provides good feeling updates during viewport navigation. */
245
246 /* Check whether denoising is active during interactive update while resolution divider is not
247 * unit. */
249
250 /* Heuristic which aims to give perceptually pleasant update of display interval in a way that at
251 * lower samples and near the beginning of rendering, updates happen more often, but with higher
252 * number of samples and later in the render, updates happen less often but device occupancy
253 * goes higher. */
256 const int num_rendered_samples) const;
258 int num_rendered_samples) const;
259
260 /* Calculate number of samples which can be rendered within current desired update interval which
261 * is calculated by `guess_update_interval_in_seconds()`. */
263
264 /* Get start sample and the number of samples which are to be path traces in the current work. */
267
268 /* Calculate how many samples there are to be rendered for the very first path trace after reset.
269 */
271
272 /* Whether adaptive sampling convergence check and filter is to happen. */
273 bool work_need_adaptive_filter() const;
274
275 /* Calculate threshold for adaptive sampling. */
276 float work_adaptive_threshold() const;
277
278 /* Check whether current work needs denoising.
279 * Denoising is not needed if the denoiser is not configured, or when denoising is happening too
280 * often.
281 *
282 * The delayed will be true when the denoiser is configured for use, but it was delayed for a
283 * later sample, to reduce overhead.
284 *
285 * ready_to_display will be false if we may have a denoised result that is outdated due to
286 * increased samples. */
287 bool work_need_denoise(bool &delayed, bool &ready_to_display);
288
289 /* Check whether current work need to update display.
290 *
291 * The `denoiser_delayed` is what `work_need_denoise()` returned as delayed denoiser flag. */
292 bool work_need_update_display(const bool denoiser_delayed);
293
294 /* Check whether it is time to perform rebalancing for the render work, */
295 bool work_need_rebalance();
296
297 /* Check whether timing of the given work are usable to store timings in the `first_render_time_`
298 * for the resolution divider calculation. */
300
301 /* Check whether timing report about the given work need to reset accumulated average time. */
302 bool work_report_reset_average(const RenderWork &render_work);
303
304 /* Check whether render time limit has been reached (or exceeded), and if so store related
305 * information in the state so that rendering is considered finished, and is possible to report
306 * average render time information. */
308
309 /* Helper class to keep track of task timing.
310 *
311 * Contains two parts: wall time and average. The wall time is an actual wall time of how long it
312 * took to complete all tasks of a type. Is always advanced when PathTracer reports time update.
313 *
314 * The average time is used for scheduling purposes. It is estimated to be a time of how long it
315 * takes to perform task on the final resolution. */
317 public:
318 void reset()
319 {
320 total_wall_time_ = 0.0;
321
324
325 last_sample_time_ = 0.0;
326 }
327
328 void add_wall(const double time)
329 {
330 total_wall_time_ += time;
331 }
332
333 void add_average(const double time, const int num_measurements = 1)
334 {
336 num_average_times_ += num_measurements;
337 last_sample_time_ = time / num_measurements;
338 }
339
340 double get_wall() const
341 {
342 return total_wall_time_;
343 }
344
345 double get_average() const
346 {
347 if (num_average_times_ == 0) {
348 return 0;
349 }
351 }
352
353 double get_last_sample_time() const
354 {
355 return last_sample_time_;
356 }
357
359 {
362 }
363
364 protected:
365 double total_wall_time_ = 0.0;
366
369
370 double last_sample_time_ = 0.0;
371 };
372
373 struct {
374 bool user_is_navigating = false;
375
377
378 /* Number of rendered samples on top of the start sample. */
380
381 /* Point in time the latest PathTraceDisplay work has been scheduled. */
383 /* Value of -1 means display was never updated. */
385
386 /* Point in time at which last rebalance has been performed. */
388
389 /* Number of rebalance works which has been requested to be performed.
390 * The path tracer might ignore the work if there is a single device rendering. */
392
393 /* Number of rebalance works handled which did change balance across devices. */
395
397
398 /* Denotes whether the latest performed rebalance work cause an actual rebalance of work across
399 * devices. */
401
402 /* Threshold for adaptive sampling which will be scheduled to work when not using progressive
403 * noise floor. */
405
411
413 bool time_limit_reached = false;
414
415 /* Time at which rendering started and finished. */
416 double start_render_time = 0.0;
417 double end_render_time = 0.0;
418
419 /* Measured occupancy of the render devices measured normalized to the number of samples.
420 *
421 * In a way it is "trailing": when scheduling new work this occupancy is measured when the
422 * previous work was rendered. */
424 float occupancy = 1.0f;
426
427 /* Timing of tasks which were performed at the very first render work at 100% of the
428 * resolution. This timing information is used to estimate resolution divider for fats
429 * navigation. */
430 struct {
435
442
443 /* Whether cryptomatte-related work will be scheduled. */
445
446 /* Whether to schedule device load rebalance works.
447 * Rebalancing requires some special treatment for update intervals and such, so if it's known
448 * that the rebalance will be ignored (due to single-device rendering i.e.) is better to fully
449 * ignore rebalancing logic. */
451
452 /* Path tracing work will be scheduled for samples from within
453 * [sample_offset_, sample_offset_ + num_samples_ - 1] range, inclusively. */
456
457 /* Limit in seconds for how long path tracing is allowed to happen.
458 * Zero means no limit is applied. */
459 double time_limit_ = 0.0;
460
461 /* Headless rendering without interface. */
463
464 /* Background (offline) rendering. */
466
467 /* Pixel size is used to force lower resolution render for final pass. Useful for retina or other
468 * types of hi-dpi displays. */
469 int pixel_size_ = 1;
470
472
475
477
478 /* Progressively lower adaptive sampling threshold level, keeping the image at a uniform noise
479 * level. */
481
482 /* Default value for the resolution divider which will be used when there is no render time
483 * information available yet.
484 * It is also what defines the upper limit of the automatically calculated resolution divider. */
486
487 /* Initial resolution divider which will be used on render scheduler reset. */
489
490 /* Calculate smallest resolution divider which will bring down actual rendering time below the
491 * desired one. This call assumes linear dependency of render time from number of pixels
492 * (quadratic dependency from the resolution divider): resolution divider of 2 brings render time
493 * down by a factor of 4. */
494 int calculate_resolution_divider_for_time(const double desired_time, const double actual_time);
495
496 /* If the number of samples per rendering progression should be limited because of path guiding
497 * being activated or is still inside its training phase */
499};
500
502 const int height,
503 const int resolution);
504
505int calculate_resolution_for_divider(const int width,
506 const int height,
507 const int resolution_divider);
508
void reset()
clear internal cached data and reset random seed
void add_average(const double time, const int num_measurements=1)
void add_wall(const double time)
AdaptiveSampling adaptive_sampling_
DenoiseParams denoiser_params_
void set_time_limit(const double time_limit)
void update_state_for_render_work(const RenderWork &render_work)
void report_path_trace_occupancy(const RenderWork &render_work, const float occupancy)
double guess_display_update_interval_in_seconds_for_num_samples(const int num_rendered_samples) const
float work_adaptive_threshold() const
int calculate_num_samples_per_update() const
int get_rendered_sample() const
void report_adaptive_filter_time(const RenderWork &render_work, const double time, bool is_cancelled)
struct RenderScheduler::@160041044210107046321320116341171004134167053234 state_
bool work_need_update_display(const bool denoiser_delayed)
BufferParams buffer_params_
void set_need_schedule_rebalance(bool need_schedule_rebalance)
double guess_display_update_interval_in_seconds() const
bool work_need_denoise(bool &delayed, bool &ready_to_display)
bool work_need_adaptive_filter() const
int get_num_rendered_samples() const
string full_report() const
bool need_schedule_rebalance_works_
bool is_denoise_active_during_update() const
void report_denoise_time(const RenderWork &render_work, const double time)
bool set_postprocess_render_work(RenderWork *render_work)
struct RenderScheduler::@363331175056261256267056367351034116315347004137 first_render_time_
double get_time_limit() const
double guess_display_update_interval_in_seconds_for_num_samples_no_limit(int num_rendered_samples) const
bool work_is_usable_for_first_render_estimation(const RenderWork &render_work)
int calculate_resolution_divider_for_time(const double desired_time, const double actual_time)
TimeWithAverage volume_guiding_denoise_time_
bool is_adaptive_sampling_used() const
double last_display_update_time
void set_denoiser_params(const DenoiseParams &params)
bool render_work_reschedule_on_idle(RenderWork &render_work)
int get_num_samples() const
TimeWithAverage adaptive_filter_time_
void report_display_update_time(const RenderWork &render_work, const double time)
TimeWithAverage rebalance_time_
void set_sample_params(const int num_samples, const bool use_sample_subset, const int sample_subset_offset, const int sample_subset_length)
int get_sample_offset() const
int default_start_resolution_divider_
bool is_denoiser_gpu_used() const
TimeWithAverage display_update_time_
void set_full_frame_render_work(RenderWork *render_work)
double guess_viewport_navigation_update_interval_in_seconds() const
void update_start_resolution_divider()
void report_path_trace_time(const RenderWork &render_work, const double time, bool is_cancelled)
void set_adaptive_sampling(const AdaptiveSampling &adaptive_sampling)
int get_num_samples_during_navigation(const int resolution_divider) const
bool render_work_reschedule_on_converge(RenderWork &render_work)
TileManager & tile_manager_
bool work_report_reset_average(const RenderWork &render_work)
bool volume_guiding_need_denoise() const
TimeWithAverage path_trace_time_
int get_start_sample_to_path_trace() const
int get_num_samples_to_path_trace() const
void render_work_reschedule_on_cancel(RenderWork &render_work)
float adaptive_sampling_threshold
void report_work_begin(const RenderWork &render_work)
TimeWithAverage denoise_time_
void set_need_schedule_cryptomatte(bool need_schedule_cryptomatte)
void report_rebalance_time(const RenderWork &render_work, const double time, bool balance_changed)
void set_limit_samples_per_update(const int limit_samples)
RenderWork get_render_work()
RenderScheduler(TileManager &tile_manager, const SessionParams &params)
bool is_background() const
void report_volume_guiding_denoise_time(const RenderWork &render_work, const double time)
struct RenderWork::@274302037211333357242061375066056076354104241257 cryptomatte
bool use_denoised_result
struct RenderWork::@143272241044374345203044061122063322004171016113 tile
bool init_render_buffers
struct RenderWork::@332011177057136214007215372066145235154020032310 path_trace
struct RenderWork::@165363207370354371245137325023006326210217053004 adaptive_sampling
struct RenderWork::@226364033061301324161311275016362103243040016376 display
struct RenderWork::@200361311027272113377377324353154145102345065344 full
#define CCL_NAMESPACE_END
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
const ccl_global KernelWorkTile * tile
int calculate_resolution_for_divider(const int width, const int height, const int resolution_divider)
int calculate_resolution_divider_for_resolution(const int width, const int height, const int resolution)