Blender V4.3
session/session.cpp
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
5#include <limits.h>
6#include <string.h>
7
8#include "device/cpu/device.h"
9#include "device/device.h"
12#include "scene/background.h"
13#include "scene/bake.h"
14#include "scene/camera.h"
15#include "scene/integrator.h"
16#include "scene/light.h"
17#include "scene/mesh.h"
18#include "scene/object.h"
19#include "scene/scene.h"
20#include "scene/shader_graph.h"
21#include "session/buffers.h"
24#include "session/session.h"
25
26#include "util/foreach.h"
27#include "util/function.h"
28#include "util/log.h"
29#include "util/math.h"
30#include "util/task.h"
31#include "util/time.h"
32
34
35Session::Session(const SessionParams &params_, const SceneParams &scene_params)
36 : params(params_), render_scheduler_(tile_manager_, params)
37{
39
41
42 pause_ = false;
43 new_work_added_ = false;
44
46
47 if (device->have_error()) {
49 }
50
51 scene = new Scene(scene_params, device);
52
55 }
56 else {
58
61 }
62 }
63
64 /* Configure path tracer. */
65 path_trace_ = make_unique<PathTrace>(
66 device, denoise_device, scene->film, &scene->dscene, render_scheduler_, tile_manager_);
67 path_trace_->set_progress(&progress);
68 path_trace_->progress_update_cb = [&]() { update_status_time(); };
69
70 tile_manager_.full_buffer_written_cb = [&](string_view filename) {
72 return;
73 }
74 full_buffer_written_cb(filename);
75 };
76
77 /* Create session thread. */
79}
80
82{
83 /* Cancel any ongoing render operation. */
84 cancel();
85
86 /* Signal session thread to end. */
87 {
88 thread_scoped_lock session_thread_lock(session_thread_mutex_);
90 }
91 session_thread_cond_.notify_all();
92
93 /* Destroy session thread. */
95 delete session_thread_;
96
97 /* Destroy path tracer, before the device. This is needed because destruction might need to
98 * access device for device memory free.
99 * TODO(sergey): Convert device to be unique_ptr, and rely on C++ to destruct objects in the
100 * pre-defined order. */
101 path_trace_.reset();
102
103 /* Destroy scene and device. */
104 delete scene;
105 if (denoise_device != device) {
106 delete denoise_device;
107 }
108 delete device;
109
110 /* Stop task scheduler. */
112}
113
115{
116 {
117 /* Signal session thread to start rendering. */
118 thread_scoped_lock session_thread_lock(session_thread_mutex_);
120 /* Already rendering, nothing to do. */
121 return;
122 }
124 }
125
126 session_thread_cond_.notify_all();
127}
128
129void Session::cancel(bool quick)
130{
131 /* Cancel any long running device operations (e.g. shader compilations). */
132 device->cancel();
133
134 /* Check if session thread is rendering. */
135 const bool rendering = is_session_thread_rendering();
136
137 if (rendering) {
138 /* Cancel path trace operations. */
139 if (quick && path_trace_) {
140 path_trace_->cancel();
141 }
142
143 /* Cancel other operations. */
144 progress.set_cancel("Exiting");
145
146 /* Signal unpause in case the render was paused. */
147 {
149 pause_ = false;
150 }
151 pause_cond_.notify_all();
152
153 /* Wait for render thread to be cancelled or finished. */
154 wait();
155 }
156}
157
159{
160 return path_trace_->ready_to_reset();
161}
162
164{
165 path_trace_->clear_display();
166
167 while (true) {
169
170 if (!render_work) {
171 if (VLOG_INFO_IS_ON) {
172 double total_time, render_time;
173 progress.get_time(total_time, render_time);
174 VLOG_INFO << "Rendering in main loop is done in " << render_time << " seconds.";
175 VLOG_INFO << path_trace_->full_report();
176 }
177
178 if (params.background) {
179 /* if no work left and in background mode, we can stop immediately. */
180 progress.set_status("Finished");
181 break;
182 }
183 }
184
185 const bool did_cancel = progress.get_cancel();
186 if (did_cancel) {
188 if (!render_work) {
189 break;
190 }
191 }
192 else if (run_wait_for_work(render_work)) {
193 continue;
194 }
195
196 /* Stop rendering if error happened during scene update or other step of preparing scene
197 * for render. */
198 if (device->have_error()) {
200 break;
201 }
202
203 {
204 /* buffers mutex is locked entirely while rendering each
205 * sample, and released/reacquired on each iteration to allow
206 * reset and draw in between */
208
209 /* update status and timing */
211
212 /* render */
213 path_trace_->render(render_work);
214
215 /* update status and timing */
217
218 /* Stop rendering if error happened during path tracing. */
219 if (device->have_error()) {
221 break;
222 }
223 }
224
226
227 if (did_cancel) {
228 break;
229 }
230 }
231}
232
234{
235 while (true) {
236 {
237 thread_scoped_lock session_thread_lock(session_thread_mutex_);
238
240 /* Continue waiting for any signal from the main thread. */
241 session_thread_cond_.wait(session_thread_lock);
242 continue;
243 }
245 /* End thread immediately. */
246 break;
247 }
248 }
249
250 /* Execute a render. */
252
253 /* Go back from rendering to waiting. */
254 {
255 thread_scoped_lock session_thread_lock(session_thread_mutex_);
258 }
259 }
260 session_thread_cond_.notify_all();
261 }
262
263 /* Flush any remaining operations and destroy display driver here. This ensure
264 * graphics API resources are created and destroyed all in the session thread,
265 * which can avoid problems contexts and multiple threads. */
266 path_trace_->flush_display();
267 path_trace_->set_display_driver(nullptr);
268}
269
271{
273 profiler.start();
274 }
275
276 /* session thread loop */
277 progress.set_status("Waiting for render to start");
278
279 /* run */
280 if (!progress.get_cancel()) {
281 /* reset number of rendered samples */
283
285 }
286
287 profiler.stop();
288
289 /* progress update */
290 if (progress.get_cancel()) {
292 }
293 else {
295 }
296}
297
303
305{
306 RenderWork render_work;
307
308 thread_scoped_lock scene_lock(scene->mutex);
309
310 bool have_tiles = true;
311 bool switched_to_new_tile = false;
312 bool did_reset = false;
313
314 /* Perform delayed reset if requested. */
315 {
318 did_reset = true;
319
322
323 /* After reset make sure the tile manager is at the first big tile. */
324 have_tiles = tile_manager_.next();
325 switched_to_new_tile = true;
326 }
327 }
328
329 /* Update number of samples in the integrator.
330 * Ideally this would need to happen once in `Session::set_samples()`, but the issue there is
331 * the initial configuration when Session is created where the `set_samples()` is not used.
332 *
333 * NOTE: Unless reset was requested only allow increasing number of samples. */
334 if (did_reset || scene->integrator->get_aa_samples() < params.samples) {
335 scene->integrator->set_aa_samples(params.samples);
336 }
337
338 /* Update denoiser settings. */
339 {
340 const DenoiseParams denoise_params = scene->integrator->get_denoise_params();
341 path_trace_->set_denoiser_params(denoise_params);
342 }
343
344 /* Update adaptive sampling. */
345 {
346 const AdaptiveSampling adaptive_sampling = scene->integrator->get_adaptive_sampling();
347 path_trace_->set_adaptive_sampling(adaptive_sampling);
348 }
349
350 /* Update path guiding. */
351 {
352 const GuidingParams guiding_params = scene->integrator->get_guiding_params(device);
353 const bool guiding_reset = (guiding_params.use) ? scene->need_reset(false) : false;
354 path_trace_->set_guiding_params(guiding_params, guiding_reset);
355 }
356
360
361 while (have_tiles) {
362 render_work = render_scheduler_.get_render_work();
363 if (render_work) {
364 break;
365 }
366
368
369 have_tiles = tile_manager_.next();
370 if (have_tiles) {
372 switched_to_new_tile = true;
373 }
374 }
375
376 if (render_work) {
377 scoped_timer update_timer;
378
379 if (switched_to_new_tile) {
380 BufferParams tile_params = buffer_params_;
381
383
384 tile_params.width = tile.width;
385 tile_params.height = tile.height;
386
387 tile_params.window_x = tile.window_x;
388 tile_params.window_y = tile.window_y;
389 tile_params.window_width = tile.window_width;
390 tile_params.window_height = tile.window_height;
391
392 tile_params.full_x = tile.x + buffer_params_.full_x;
393 tile_params.full_y = tile.y + buffer_params_.full_y;
396
397 tile_params.update_offset_stride();
398
399 path_trace_->reset(buffer_params_, tile_params, did_reset);
400 }
401
402 const int resolution = render_work.resolution_divider;
403 const int width = max(1, buffer_params_.full_width / resolution);
404 const int height = max(1, buffer_params_.full_height / resolution);
405
406 {
407 /* Load render kernels, before device update where we upload data to the GPU.
408 * Do it outside of the scene mutex since the heavy part of the loading (i.e. kernel
409 * compilation) does not depend on the scene and some other functionality (like display
410 * driver) might be waiting on the scene mutex to synchronize display pass.
411 *
412 * The scene will lock itself for the short period if it needs to update kernel features. */
413 scene_lock.unlock();
414 scene->load_kernels(progress);
415 scene_lock.lock();
416 }
417
418 if (update_scene(width, height)) {
419 profiler.reset(scene->shaders.size(), scene->objects.size());
420 }
421
422 /* Unlock scene mutex before loading denoiser kernels, since that may attempt to activate
423 * graphics interop, which can deadlock when the scene mutex is still being held. */
424 scene_lock.unlock();
425
426 path_trace_->load_kernels();
427 path_trace_->alloc_work_memory();
428
429 /* Wait for device to be ready (e.g. finish any background compilations). */
430 string device_status;
431 while (!device->is_ready(device_status)) {
432 progress.set_status(device_status);
433 if (progress.get_cancel()) {
434 break;
435 }
436 std::this_thread::sleep_for(std::chrono::milliseconds(200));
437 }
438
440 }
441
442 return render_work;
443}
444
446{
447 /* In an offline rendering there is no pause, and no tiles will mean the job is fully done. */
448 if (params.background) {
449 return false;
450 }
451
453
454 if (!pause_ && render_work) {
455 /* Rendering is not paused and there is work to be done. No need to wait for anything. */
456 return false;
457 }
458
459 const bool no_work = !render_work;
460 update_status_time(pause_, no_work);
461
462 /* Only leave the loop when rendering is not paused. But even if the current render is
463 * un-paused but there is nothing to render keep waiting until new work is added. */
464 while (!progress.get_cancel()) {
465 scoped_timer pause_timer;
466
467 if (!pause_ && (render_work || new_work_added_ || delayed_reset_.do_reset)) {
468 break;
469 }
470
471 /* Wait for either pause state changed, or extra samples added to render. */
472 pause_cond_.wait(pause_lock);
473
474 if (pause_) {
476 }
477
478 update_status_time(pause_, no_work);
480 }
481
482 new_work_added_ = false;
483
484 return no_work;
485}
486
488{
489 path_trace_->draw();
490}
491
493{
494 const int image_width = buffer_params_.width;
496
497 if (!params.use_auto_tile) {
499 }
500
501 const int64_t image_area = static_cast<int64_t>(image_width) * image_height;
502
503 /* TODO(sergey): Take available memory into account, and if there is enough memory do not
504 * tile and prefer optimal performance. */
505
507 const int64_t actual_tile_area = static_cast<int64_t>(tile_size) * tile_size;
508
509 if (actual_tile_area >= image_area && image_width <= TileManager::MAX_TILE_SIZE &&
511 {
513 }
514
515 return make_int2(tile_size, tile_size);
516}
517
519{
521 return;
522 }
523 delayed_reset_.do_reset = false;
524
527
528 /* Store parameters used for buffers access outside of scene graph. */
530 buffer_params_.exposure = scene->film->get_exposure();
532 scene->film->get_use_approximate_shadow_catcher();
533 buffer_params_.use_transparent_background = scene->background->get_transparent();
534
535 /* Tile and work scheduling. */
538
539 /* Passes. */
540 /* When multiple tiles are used SAMPLE_COUNT pass is used to keep track of possible partial
541 * tile results. It is safe to use generic update function here which checks for changes since
542 * changes in tile settings re-creates session, which ensures film is fully updated on tile
543 * changes. */
544 scene->film->update_passes(scene, tile_manager_.has_multiple_tiles());
545
546 /* Update for new state of scene and passes. */
547 buffer_params_.update_passes(scene->passes);
549
550 /* Update temp directory on reset.
551 * This potentially allows to finish the existing rendering with a previously configure
552 * temporary
553 * directory in the host software and switch to a new temp directory when new render starts. */
555
556 /* Progress. */
560
561 if (!params.background) {
563 }
564 const double time_limit = params.time_limit * ((double)tile_manager_.get_num_tiles());
566 progress.set_time_limit(time_limit);
567}
568
569void Session::reset(const SessionParams &session_params, const BufferParams &buffer_params)
570{
571 {
574
576 delayed_reset_.session_params = session_params;
577 delayed_reset_.buffer_params = buffer_params;
578
579 path_trace_->cancel();
580 }
581
582 pause_cond_.notify_all();
583}
584
585void Session::set_samples(int samples)
586{
587 if (samples == params.samples) {
588 return;
589 }
590
591 params.samples = samples;
592
593 {
595 new_work_added_ = true;
596 }
597
598 pause_cond_.notify_all();
599}
600
601void Session::set_time_limit(double time_limit)
602{
603 if (time_limit == params.time_limit) {
604 return;
605 }
606
607 params.time_limit = time_limit;
608
609 {
611 new_work_added_ = true;
612 }
613
614 pause_cond_.notify_all();
615}
616
617void Session::set_pause(bool pause)
618{
619 bool notify = false;
620
621 {
623
624 if (pause != pause_) {
625 pause_ = pause;
626 notify = true;
627 }
628 }
629
631 if (notify) {
632 pause_cond_.notify_all();
633 }
634 }
635 else if (pause_) {
637 }
638}
639
640void Session::set_output_driver(unique_ptr<OutputDriver> driver)
641{
642 path_trace_->set_output_driver(std::move(driver));
643}
644
645void Session::set_display_driver(unique_ptr<DisplayDriver> driver)
646{
647 path_trace_->set_display_driver(std::move(driver));
648}
649
651{
652 const double completed = progress.get_progress();
653 if (completed == 0.0) {
654 return 0.0;
655 }
656
657 double total_time, render_time;
658 progress.get_time(total_time, render_time);
659 double remaining = (1.0 - (double)completed) * (render_time / (double)completed);
660
661 const double time_limit = render_scheduler_.get_time_limit() *
663 if (time_limit != 0.0) {
664 remaining = min(remaining, max(time_limit - render_time, 0.0));
665 }
666
667 return remaining;
668}
669
671{
672 /* Wait until session thread either is waiting or ending. */
673 while (true) {
674 thread_scoped_lock session_thread_lock(session_thread_mutex_);
676 break;
677 }
678 session_thread_cond_.wait(session_thread_lock);
679 }
680}
681
682bool Session::update_scene(int width, int height)
683{
684 /* Update camera if dimensions changed for progressive render. the camera
685 * knows nothing about progressive or cropped rendering, it just gets the
686 * image dimensions passed in. */
687 Camera *cam = scene->camera;
688 cam->set_screen_size(width, height);
689
690 return scene->update(progress);
691}
692
693static string status_append(const string &status, const string &suffix)
694{
695 string prefix = status;
696 if (!prefix.empty()) {
697 prefix += ", ";
698 }
699 return prefix + suffix;
700}
701
702void Session::update_status_time(bool show_pause, bool show_done)
703{
704 string status, substatus;
705
706 const int current_tile = progress.get_rendered_tiles();
708
709 const int current_sample = progress.get_current_sample();
710 const int num_samples = render_scheduler_.get_num_samples();
711
712 /* TIle. */
714 substatus = status_append(substatus,
715 string_printf("Rendered %d/%d Tiles", current_tile, num_tiles));
716 }
717
718 /* Sample. */
719 if (!params.background && num_samples == Integrator::MAX_SAMPLES) {
720 substatus = status_append(substatus, string_printf("Sample %d", current_sample));
721 }
722 else {
723 substatus = status_append(substatus,
724 string_printf("Sample %d/%d", current_sample, num_samples));
725 }
726
727 /* Append any device-specific status (such as background kernel optimization) */
728 string device_status;
729 if (device->is_ready(device_status) && !device_status.empty()) {
730 substatus += string_printf(" (%s)", device_status.c_str());
731 }
732
733 /* TODO(sergey): Denoising status from the path trace. */
734
735 if (show_pause) {
736 status = "Rendering Paused";
737 }
738 else if (show_done) {
739 status = "Rendering Done";
740 progress.set_end_time(); /* Save end time so that further calls to get_time are accurate. */
741 }
742 else {
743 status = substatus;
744 substatus.clear();
745 }
746
747 progress.set_status(status, substatus);
748}
749
751{
752 scene->device_free();
753 path_trace_->device_free();
754}
755
757{
758 scene->collect_statistics(render_stats);
760 render_stats->collect_profiling(scene, profiler);
761 }
762}
763
764/* --------------------------------------------------------------------
765 * Full-frame on-disk storage.
766 */
767
769{
770 path_trace_->process_full_buffer_from_disk(filename);
771}
772
static constexpr int image_width
static constexpr int image_height
typedef double(DMatrix)[4][4]
struct Scene Scene
int full_width
Definition buffers.h:87
bool use_approximate_shadow_catcher
Definition buffers.h:102
float exposure
Definition buffers.h:101
int window_y
Definition buffers.h:80
void update_offset_stride()
Definition buffers.cpp:221
int full_height
Definition buffers.h:88
int window_height
Definition buffers.h:82
int window_width
Definition buffers.h:81
NODE_DECLARE int width
Definition buffers.h:72
int window_x
Definition buffers.h:79
bool use_transparent_background
Definition buffers.h:103
void update_passes()
Definition buffers.cpp:120
DeviceType type
virtual const string & error_message()
virtual bool is_ready(string &) const
static Device * create(const DeviceInfo &info, Stats &stats, Profiler &profiler, bool headless)
virtual void cancel()
bool have_error()
static const int MAX_SAMPLES
Definition integrator.h:80
void start()
Definition profiling.cpp:75
void reset(int num_shaders, int num_objects)
Definition profiling.cpp:55
void stop()
Definition profiling.cpp:82
int get_current_sample() const
Definition progress.h:241
void set_total_pixel_samples(uint64_t total_pixel_samples_)
Definition progress.h:193
void set_cancel(const string &cancel_message_)
Definition progress.h:86
void set_end_time()
Definition progress.h:178
string get_cancel_message() const
Definition progress.h:101
void get_time(double &total_time_, double &render_time_) const
Definition progress.h:168
bool get_cancel() const
Definition progress.h:93
void reset_sample()
Definition progress.h:183
void set_status(const string &status_, const string &substatus_="")
Definition progress.h:263
void set_time_limit(double time_limit_)
Definition progress.h:151
void set_update()
Definition progress.h:321
void set_error(const string &error_message_)
Definition progress.h:113
double get_progress() const
Definition progress.h:200
void set_render_start_time()
Definition progress.h:144
void add_finished_tile(bool denoised)
Definition progress.h:229
void add_skip_time(const scoped_timer &start_timer, bool only_render)
Definition progress.h:158
int get_rendered_tiles() const
Definition progress.h:249
void set_start_time()
Definition progress.h:136
void set_time_limit(double time_limit)
void reset(const BufferParams &buffer_params, int num_samples, int sample_offset)
double get_time_limit() const
int get_num_samples() const
void set_num_samples(int num_samples)
void set_start_sample(int start_sample)
void render_work_reschedule_on_cancel(RenderWork &render_work)
RenderWork get_render_work()
DeviceInfo denoise_device
DeviceInfo device
void thread_run()
void collect_statistics(RenderStats *stats)
RenderScheduler render_scheduler_
Device * device
void update_status_time(bool show_pause=false, bool show_done=false)
void set_pause(bool pause)
thread_mutex pause_mutex_
int2 get_effective_tile_size() const
void process_full_buffer_from_disk(string_view filename)
void set_display_driver(unique_ptr< DisplayDriver > driver)
bool ready_to_reset()
void set_time_limit(double time_limit)
void cancel(bool quick=false)
void do_delayed_reset()
thread_condition_variable pause_cond_
Device * denoise_device
Progress progress
Profiler profiler
bool run_wait_for_work(const RenderWork &render_work)
double get_estimated_remaining_time() const
SessionParams params
thread_condition_variable session_thread_cond_
Session(const SessionParams &params, const SceneParams &scene_params)
bool update_scene(int width, int height)
function< void(string_view)> full_buffer_written_cb
void run_main_render_loop()
void thread_render()
thread_mutex session_thread_mutex_
@ SESSION_THREAD_RENDER
BufferParams buffer_params_
RenderWork run_update_for_next_iteration()
void reset(const SessionParams &session_params, const BufferParams &buffer_params)
thread * session_thread_
enum Session::@1464 session_thread_state_
bool is_session_thread_rendering()
void device_free()
thread_mutex buffers_mutex_
Scene * scene
void set_output_driver(unique_ptr< OutputDriver > driver)
struct Session::DelayedReset delayed_reset_
void set_samples(int samples)
unique_ptr< PathTrace > path_trace_
TileManager tile_manager_
bool new_work_added_
static void exit()
Definition task.cpp:82
static void init(int num_threads=0)
Definition task.cpp:61
void set_temp_dir(const string &temp_dir)
function< void(string_view)> full_buffer_written_cb
void reset_scheduling(const BufferParams &params, int2 tile_size)
int compute_render_tile_size(const int suggested_tile_size) const
bool has_multiple_tiles() const
void update(const BufferParams &params, const Scene *scene)
const Tile & get_current_tile() const
int get_num_tiles() const
static const int MAX_TILE_SIZE
bool join()
Definition thread.cpp:43
#define function_bind
#define CCL_NAMESPACE_END
@ DEVICE_CPU
ccl_device_forceinline int2 make_int2(const int x, const int y)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
ccl_gpu_kernel_postfix ccl_global KernelWorkTile const int num_tiles
ccl_global const KernelWorkTile * tile
#define VLOG_INFO
Definition log.h:72
#define VLOG_INFO_IS_ON
Definition log.h:73
static string status_append(const string &status, const string &suffix)
#define min(a, b)
Definition sort.c:32
__int64 int64_t
Definition stdint.h:89
unsigned __int64 uint64_t
Definition stdint.h:90
CCL_NAMESPACE_BEGIN string string_printf(const char *format,...)
Definition string.cpp:23
void set_screen_size(int width_, int height_)
void collect_profiling(Scene *scene, Profiler &prof)
Definition stats.cpp:232
SessionParams session_params
std::unique_lock< std::mutex > thread_scoped_lock
Definition thread.h:30
float max
double total_time