Blender V4.5
blender/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 <cstdlib>
6
7#include "device/device.h"
8
9#include "scene/background.h"
10#include "scene/bake.h"
11#include "scene/camera.h"
12#include "scene/film.h"
13#include "scene/integrator.h"
14#include "scene/light.h"
15#include "scene/mesh.h"
16#include "scene/object.h"
17#include "scene/scene.h"
18#include "scene/shader.h"
19#include "scene/stats.h"
20
21#include "session/buffers.h"
22#include "session/session.h"
23
24#include "util/hash.h"
25#include "util/log.h"
26#include "util/murmurhash.h"
27#include "util/path.h"
28#include "util/progress.h"
29#include "util/time.h"
30
33#include "blender/session.h"
34#include "blender/sync.h"
35#include "blender/util.h"
36
38
40bool BlenderSession::headless = false;
42
44 BL::Preferences &b_userpref,
45 BL::BlendData &b_data,
46 bool preview_osl)
58 width(0),
59 height(0),
62 use_developer_ui(b_userpref.experimental().use_cycles_debug() &&
63 b_userpref.view().show_developer_ui())
64{
65 /* offline render */
66 background = true;
67 last_redraw_time = 0.0;
69 last_status_time = 0.0;
70}
71
73 BL::Preferences &b_userpref,
74 BL::BlendData &b_data,
75 BL::SpaceView3D &b_v3d,
76 BL::RegionView3D &b_rv3d,
77 const int width,
78 const int height)
88 b_v3d(b_v3d),
90 width(width),
94 use_developer_ui(b_userpref.experimental().use_cycles_debug() &&
95 b_userpref.view().show_developer_ui())
96{
97 /* 3d view render */
98 background = false;
99 last_redraw_time = 0.0;
100 start_resize_time = 0.0;
101 last_status_time = 0.0;
102}
103
108
110{
111 const SessionParams session_params = BlenderSync::get_session_params(
113 const SceneParams scene_params = BlenderSync::get_scene_params(
115 const bool session_pause = BlenderSync::get_session_pause(b_scene, background);
116
117 /* reset status/progress */
118 last_status = "";
119 last_error = "";
120 last_progress = -1.0;
121 start_resize_time = 0.0;
122
123 /* create session */
124 session = make_unique<Session>(session_params, scene_params);
125 session->progress.set_update_callback([this] { tag_redraw(); });
126 session->progress.set_cancel_callback([this] { test_cancel(); });
127 session->set_pause(session_pause);
128
129 /* create scene */
130 scene = session->scene.get();
131 scene->name = b_scene.name();
132
133 /* create sync */
134 sync = make_unique<BlenderSync>(
136 BL::Object b_camera_override(b_engine.camera_override());
137 if (b_v3d) {
138 sync->sync_view(b_v3d, b_rv3d, width, height);
139 }
140 else {
141 sync->sync_camera(b_render, b_camera_override, width, height, "");
142 }
143
144 /* set buffer parameters */
145 const BufferParams buffer_params = BlenderSync::get_buffer_params(
146 b_v3d, b_rv3d, scene->camera, width, height);
147 session->reset(session_params, buffer_params);
148
149 /* Viewport and preview (as in, material preview) does not do tiled rendering, so can inform
150 * engine that no tracking of the tiles state is needed.
151 * The offline rendering will make a decision when tile is being written. The penalty of asking
152 * the engine to keep track of tiles state is minimal, so there is nothing to worry about here
153 * about possible single-tiled final render. */
154 if (!b_engine.is_preview() && !b_v3d) {
155 b_engine.use_highlight_tiles(true);
156 }
157}
158
159void BlenderSession::reset_session(BL::BlendData &b_data, BL::Depsgraph &b_depsgraph)
160{
161 /* Update data, scene and depsgraph pointers. These can change after undo. */
162 this->b_data = b_data;
163 this->b_depsgraph = b_depsgraph;
164 this->b_scene = b_depsgraph.scene_eval();
165 if (sync) {
166 sync->reset(this->b_data, this->b_scene);
167 }
168
169 if (preview_osl) {
170 PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
171 RNA_boolean_set(&cscene, "shading_system", preview_osl);
172 }
173
174 if (b_v3d) {
175 this->b_render = b_scene.render();
176 }
177 else {
178 this->b_render = b_engine.render();
181 }
182
183 const bool is_new_session = (session == nullptr);
184 if (is_new_session) {
185 /* Initialize session and remember it was just created so not to
186 * re-create it below.
187 */
189 }
190
191 if (b_v3d) {
192 /* NOTE: We need to create session, but all the code from below
193 * will make viewport render to stuck on initialization.
194 */
195 return;
196 }
197
198 const SessionParams session_params = BlenderSync::get_session_params(
200 const SceneParams scene_params = BlenderSync::get_scene_params(
202
203 if (scene->params.modified(scene_params) || session->params.modified(session_params) ||
204 !this->b_render.use_persistent_data())
205 {
206 /* if scene or session parameters changed, it's easier to simply re-create
207 * them rather than trying to distinguish which settings need to be updated
208 */
209 if (!is_new_session) {
210 free_session();
212 }
213 return;
214 }
215
216 session->progress.reset();
217
218 /* peak memory usage should show current render peak, not peak for all renders
219 * made by this render session
220 */
221 session->stats.mem_peak = session->stats.mem_used;
222
223 if (is_new_session) {
224 /* Sync object should be re-created for new scene. */
225 sync = make_unique<BlenderSync>(
227 }
228 else {
229 /* Sync recalculations to do just the required updates. */
230 sync->sync_recalc(b_depsgraph, b_v3d, b_rv3d);
231 }
232
233 BL::Object b_camera_override(b_engine.camera_override());
234 sync->sync_camera(b_render, b_camera_override, width, height, "");
235
236 BL::SpaceView3D b_null_space_view3d(PointerRNA_NULL);
237 BL::RegionView3D b_null_region_view3d(PointerRNA_NULL);
238 const BufferParams buffer_params = BlenderSync::get_buffer_params(
239 b_null_space_view3d, b_null_region_view3d, scene->camera, width, height);
240 session->reset(session_params, buffer_params);
241
242 /* reset time */
243 start_resize_time = 0.0;
244
245 {
247 draw_state_.last_pass_index = -1;
248 }
249}
250
252{
253 if (session) {
254 session->cancel(true);
255 }
256
257 sync.reset();
258 session.reset();
259
260 display_driver_ = nullptr;
261}
262
263void BlenderSession::full_buffer_written(string_view filename)
264{
265 full_buffer_files_.emplace_back(filename);
266}
267
268static void add_cryptomatte_layer(BL::RenderResult &b_rr, string name, string manifest)
269{
270 const string identifier = string_printf("%08x",
271 util_murmur_hash3(name.c_str(), name.length(), 0));
272 const string prefix = "cryptomatte/" + identifier.substr(0, 7) + "/";
273
274 render_add_metadata(b_rr, prefix + "name", name);
275 render_add_metadata(b_rr, prefix + "hash", "MurmurHash3_32");
276 render_add_metadata(b_rr, prefix + "conversion", "uint32_to_float32");
277 render_add_metadata(b_rr, prefix + "manifest", manifest);
278}
279
280void BlenderSession::stamp_view_layer_metadata(Scene *scene, const string &view_layer_name)
281{
282 BL::RenderResult b_rr = b_engine.get_result();
283 const string prefix = "cycles." + view_layer_name + ".";
284
285 /* Configured number of samples for the view layer. */
286 b_rr.stamp_data_add_field((prefix + "samples").c_str(),
287 to_string(session->params.samples).c_str());
288
289 /* Store ranged samples information. */
290 /* TODO(sergey): Need to bring this information back. */
291#if 0
292 if (session->tile_manager.range_num_samples != -1) {
293 b_rr.stamp_data_add_field((prefix + "range_start_sample").c_str(),
294 to_string(session->tile_manager.range_start_sample).c_str());
295 b_rr.stamp_data_add_field((prefix + "range_num_samples").c_str(),
296 to_string(session->tile_manager.range_num_samples).c_str());
297 }
298#endif
299
300 /* Write cryptomatte metadata. */
301 if (scene->film->get_cryptomatte_passes() & CRYPT_OBJECT) {
303 view_layer_name + ".CryptoObject",
304 scene->object_manager->get_cryptomatte_objects(scene));
305 }
306 if (scene->film->get_cryptomatte_passes() & CRYPT_MATERIAL) {
308 view_layer_name + ".CryptoMaterial",
309 scene->shader_manager->get_cryptomatte_materials(scene));
310 }
311 if (scene->film->get_cryptomatte_passes() & CRYPT_ASSET) {
313 view_layer_name + ".CryptoAsset",
314 scene->object_manager->get_cryptomatte_assets(scene));
315 }
316
317 /* Store synchronization and bare-render times. */
318 double total_time;
319 double render_time;
320 session->progress.get_time(total_time, render_time);
321 b_rr.stamp_data_add_field((prefix + "total_time").c_str(),
323 b_rr.stamp_data_add_field((prefix + "render_time").c_str(),
324 time_human_readable_from_seconds(render_time).c_str());
325 b_rr.stamp_data_add_field((prefix + "synchronization_time").c_str(),
326 time_human_readable_from_seconds(total_time - render_time).c_str());
327}
328
329void BlenderSession::render(BL::Depsgraph &b_depsgraph_)
330{
331 b_depsgraph = b_depsgraph_;
332
333 if (session->progress.get_cancel()) {
335 return;
336 }
337
338 /* Create driver to write out render results. */
340 session->set_output_driver(make_unique<BlenderOutputDriver>(b_engine));
341
342 session->full_buffer_written_cb = [&](string_view filename) { full_buffer_written(filename); };
343
344 BL::ViewLayer b_view_layer = b_depsgraph.view_layer_eval();
345
346 /* get buffer parameters */
347 const SessionParams session_params = BlenderSync::get_session_params(
350 b_v3d, b_rv3d, scene->camera, width, height);
351
352 /* temporary render result to find needed passes and views */
353 BL::RenderResult b_rr = b_engine.begin_result(0, 0, 1, 1, b_view_layer.name().c_str(), nullptr);
354 BL::RenderResult::layers_iterator b_single_rlay;
355 b_rr.layers.begin(b_single_rlay);
356 BL::RenderLayer b_rlay = *b_single_rlay;
357
358 {
360 b_rlay_name = b_view_layer.name();
361
362 /* Signal that the display pass is to be updated. */
363 draw_state_.last_pass_index = -1;
364 }
365
366 /* Compute render passes and film settings. */
367 sync->sync_render_passes(b_rlay, b_view_layer);
368
369 BL::RenderResult::views_iterator b_view_iter;
370
371 int num_views = 0;
372 for (b_rr.views.begin(b_view_iter); b_view_iter != b_rr.views.end(); ++b_view_iter) {
373 num_views++;
374 }
375
376 int view_index = 0;
377 for (b_rr.views.begin(b_view_iter); b_view_iter != b_rr.views.end(); ++b_view_iter, ++view_index)
378 {
379 b_rview_name = b_view_iter->name();
380
381 buffer_params.layer = b_view_layer.name();
382 buffer_params.view = b_rview_name;
383
384 /* set the current view */
385 b_engine.active_view_set(b_rview_name.c_str());
386
387 /* Force update in this case, since the camera transform on each frame changes
388 * in different views. This could be optimized by somehow storing the animated
389 * camera transforms separate from the fixed stereo transform. */
390 if ((scene->need_motion() != Scene::MOTION_NONE) && view_index > 0) {
391 sync->tag_update();
392 }
393
394 /* update scene */
395 BL::Object b_camera_override(b_engine.camera_override());
396 sync->sync_camera(b_render, b_camera_override, width, height, b_rview_name.c_str());
397 sync->sync_data(b_render,
399 b_v3d,
400 b_camera_override,
401 width,
402 height,
404 session_params.denoise_device);
405
406 /* At the moment we only free if we are not doing multi-view
407 * (or if we are rendering the last view). See #58142/D4239 for discussion.
408 */
409 const bool can_free_cache = (view_index == num_views - 1);
410 if (can_free_cache) {
411 sync->free_data_after_sync(b_depsgraph);
412 }
413
415
416 /* Attempt to free all data which is held by Blender side, since at this
417 * point we know that we've got everything to render current view layer.
418 */
419 if (can_free_cache) {
421 }
422
423 /* Make sure all views have different noise patterns. - hardcoded value just to make it random
424 */
425 if (view_index != 0) {
426 int seed = scene->integrator->get_seed();
427 seed += hash_uint2(seed, hash_uint2(view_index * 0xdeadbeef, 0));
428 scene->integrator->set_seed(seed);
429 }
430
431 /* Update number of samples per layer. */
432 const int samples = sync->get_layer_samples();
433 const bool bound_samples = sync->get_layer_bound_samples();
434
435 SessionParams effective_session_params = session_params;
436 if (samples != 0 && (!bound_samples || (samples < session_params.samples))) {
437 effective_session_params.samples = samples;
438 }
439
440 /* Update session itself. */
441 session->reset(effective_session_params, buffer_params);
442
443 /* render */
444 if (!b_engine.is_preview() && background && print_render_stats) {
445 scene->enable_update_stats();
446 }
447
448 session->start();
449 session->wait();
450
451 if (!b_engine.is_preview() && background && print_render_stats) {
452 RenderStats stats;
453 session->collect_statistics(&stats);
454 printf("Render statistics:\n%s\n", stats.full_report().c_str());
455 }
456
457 if (session->progress.get_cancel()) {
458 break;
459 }
460 }
461
462 /* add metadata */
464
465 /* free result without merging */
466 b_engine.end_result(b_rr, true, false, false);
467
468 /* When tiled rendering is used there will be no "write" done for the tile. Forcefully clear
469 * highlighted tiles now, so that the highlight will be removed while processing full frame from
470 * file. */
471 b_engine.tile_highlight_clear_all();
472
473 double total_time;
474 double render_time;
475 session->progress.get_time(total_time, render_time);
476 VLOG_INFO << "Total render time: " << total_time;
477 VLOG_INFO << "Render time (without synchronization): " << render_time;
478}
479
481{
482 /* Processing of all layers and views is done. Clear the strings so that we can communicate
483 * progress about reading files and denoising them. */
484 b_rlay_name = "";
485 b_rview_name = "";
486
487 if (!b_render.use_persistent_data()) {
488 /* Free the sync object so that it can properly dereference nodes from the scene graph before
489 * the graph is freed. */
490 sync.reset();
491
492 session->device_free();
493 }
494
495 for (const string_view filename : full_buffer_files_) {
496 session->process_full_buffer_from_disk(filename);
498 break;
499 }
500 }
501
502 for (const string_view filename : full_buffer_files_) {
503 path_remove(filename);
504 }
505
506 /* Clear output driver. */
507 session->set_output_driver(nullptr);
508 session->full_buffer_written_cb = nullptr;
509
510 /* The display driver is the source of drawing context for both drawing and possible graphics
511 * interoperability objects in the path trace. Once the frame is finished the OpenGL context
512 * might be freed form Blender side. Need to ensure that all GPU resources are freed prior to
513 * that point.
514 * Ideally would only do this when OpenGL context is actually destroyed, but there is no way to
515 * know when this happens (at least in the code at the time when this comment was written).
516 * The penalty of re-creating resources on every frame is unlikely to be noticed. */
517 display_driver_ = nullptr;
518 session->set_display_driver(nullptr);
519
520 /* All the files are handled.
521 * Clear the list so that this session can be re-used by Persistent Data. */
522 full_buffer_files_.clear();
523}
524
525static bool bake_setup_pass(Scene *scene, const string &bake_type, const int bake_filter)
526{
527 Integrator *integrator = scene->integrator;
528 Film *film = scene->film;
529
530 const bool filter_direct = (bake_filter & BL::BakeSettings::pass_filter_DIRECT) != 0;
531 const bool filter_indirect = (bake_filter & BL::BakeSettings::pass_filter_INDIRECT) != 0;
532 const bool filter_color = (bake_filter & BL::BakeSettings::pass_filter_COLOR) != 0;
533
534 PassType type = PASS_NONE;
535 bool use_direct_light = false;
536 bool use_indirect_light = false;
537 bool include_albedo = false;
538
539 /* Data passes. */
540 if (bake_type == "POSITION") {
541 type = PASS_POSITION;
542 }
543 else if (bake_type == "NORMAL") {
544 type = PASS_NORMAL;
545 }
546 else if (bake_type == "UV") {
547 type = PASS_UV;
548 }
549 else if (bake_type == "ROUGHNESS") {
550 type = PASS_ROUGHNESS;
551 }
552 else if (bake_type == "EMIT") {
553 type = PASS_EMISSION;
554 }
555 /* Environment pass. */
556 else if (bake_type == "ENVIRONMENT") {
557 type = PASS_BACKGROUND;
558 }
559 /* AO pass. */
560 else if (bake_type == "AO") {
561 type = PASS_AO;
562 }
563 /* Shadow pass. */
564 else if (bake_type == "SHADOW") {
565 /* Bake as combined pass, together with marking the object as a shadow catcher. */
566 type = PASS_SHADOW_CATCHER;
567 film->set_use_approximate_shadow_catcher(true);
568
569 use_direct_light = true;
570 use_indirect_light = true;
571 include_albedo = true;
572
573 integrator->set_use_diffuse(true);
574 integrator->set_use_glossy(true);
575 integrator->set_use_transmission(true);
576 integrator->set_use_emission(true);
577 }
578 /* Combined pass. */
579 else if (bake_type == "COMBINED") {
580 type = PASS_COMBINED;
581 film->set_use_approximate_shadow_catcher(true);
582
583 use_direct_light = filter_direct;
584 use_indirect_light = filter_indirect;
585 include_albedo = filter_color;
586
587 integrator->set_use_diffuse((bake_filter & BL::BakeSettings::pass_filter_DIFFUSE) != 0);
588 integrator->set_use_glossy((bake_filter & BL::BakeSettings::pass_filter_GLOSSY) != 0);
589 integrator->set_use_transmission((bake_filter & BL::BakeSettings::pass_filter_TRANSMISSION) !=
590 0);
591 integrator->set_use_emission((bake_filter & BL::BakeSettings::pass_filter_EMIT) != 0);
592 }
593 /* Light component passes. */
594 else if ((bake_type == "DIFFUSE") || (bake_type == "GLOSSY") || (bake_type == "TRANSMISSION")) {
595 use_direct_light = filter_direct;
596 use_indirect_light = filter_indirect;
597 include_albedo = filter_color;
598
599 integrator->set_use_diffuse(bake_type == "DIFFUSE");
600 integrator->set_use_glossy(bake_type == "GLOSSY");
601 integrator->set_use_transmission(bake_type == "TRANSMISSION");
602
603 if (bake_type == "DIFFUSE") {
604 if (filter_direct && filter_indirect) {
605 type = PASS_DIFFUSE;
606 }
607 else if (filter_direct) {
608 type = PASS_DIFFUSE_DIRECT;
609 }
610 else if (filter_indirect) {
612 }
613 else {
614 type = PASS_DIFFUSE_COLOR;
615 }
616 }
617 else if (bake_type == "GLOSSY") {
618 if (filter_direct && filter_indirect) {
619 type = PASS_GLOSSY;
620 }
621 else if (filter_direct) {
622 type = PASS_GLOSSY_DIRECT;
623 }
624 else if (filter_indirect) {
626 }
627 else {
628 type = PASS_GLOSSY_COLOR;
629 }
630 }
631 else if (bake_type == "TRANSMISSION") {
632 if (filter_direct && filter_indirect) {
633 type = PASS_TRANSMISSION;
634 }
635 else if (filter_direct) {
637 }
638 else if (filter_indirect) {
640 }
641 else {
643 }
644 }
645 }
646
647 if (type == PASS_NONE) {
648 return false;
649 }
650
651 /* Create pass. */
652 Pass *pass = scene->create_node<Pass>();
653 pass->set_name(ustring("Combined"));
654 pass->set_type(type);
655 pass->set_include_albedo(include_albedo);
656
657 /* Disable direct indirect light for performance when not needed. */
658 integrator->set_use_direct_light(use_direct_light);
659 integrator->set_use_indirect_light(use_indirect_light);
660
661 /* Disable denoiser if the pass does not support it.
662 * For the passes which support denoising follow the user configuration. */
663 const PassInfo pass_info = Pass::get_info(type);
664 if (integrator->get_use_denoise() && !pass_info.support_denoise) {
665 integrator->set_use_denoise(false);
666 }
667
668 return true;
669}
670
671void BlenderSession::bake(BL::Depsgraph &b_depsgraph_,
672 BL::Object &b_object,
673 const string &bake_type,
674 const int bake_filter,
675 const int bake_width,
676 const int bake_height)
677{
678 b_depsgraph = b_depsgraph_;
679
680 /* Get session parameters. */
681 const SessionParams session_params = BlenderSync::get_session_params(
683
684 /* Initialize bake manager, before we load the baking kernels. */
685 scene->bake_manager->set_baking(scene, true);
686
687 session->set_display_driver(nullptr);
688 session->set_output_driver(make_unique<BlenderOutputDriver>(b_engine));
689 session->full_buffer_written_cb = [&](string_view filename) { full_buffer_written(filename); };
690
691 /* Sync scene. */
692 BL::Object b_camera_override(b_engine.camera_override());
693 sync->set_bake_target(b_object);
694 sync->sync_camera(b_render, b_camera_override, width, height, "");
695 sync->sync_data(b_render,
697 b_v3d,
698 b_camera_override,
699 width,
700 height,
702 session_params.denoise_device);
703
704 /* Save the current state of the denoiser, as it might be disabled by the pass configuration (for
705 * passed which do not support denoising). */
706 Integrator *integrator = scene->integrator;
707 const bool was_denoiser_enabled = integrator->get_use_denoise();
708
709 /* Add render pass that we want to bake, and name it Combined so that it is
710 * used as that on the Blender side. */
711 if (!bake_setup_pass(scene, bake_type, bake_filter)) {
712 session->cancel(true);
713 }
714
715 /* Always use transparent background for baking. */
716 scene->background->set_transparent(true);
717
718 if (!session->progress.get_cancel()) {
719 /* Load built-in images from Blender. */
721 }
722
723 /* Object might have been disabled for rendering or excluded in some
724 * other way, in that case Blender will report a warning afterwards. */
725 Object *bake_object = nullptr;
726 if (!session->progress.get_cancel()) {
727 for (Object *ob : scene->objects) {
728 if (ob->get_is_bake_target()) {
729 bake_object = ob;
730 break;
731 }
732 }
733 }
734
735 /* For the shadow pass, temporarily mark the object as a shadow catcher. */
736 const bool was_shadow_catcher = (bake_object) ? bake_object->get_is_shadow_catcher() : false;
737 if (bake_object && bake_type == "SHADOW") {
738 bake_object->set_is_shadow_catcher(true);
739 }
740
741 if (bake_object && !session->progress.get_cancel()) {
742 /* Get buffer parameters. */
743 BufferParams buffer_params;
744 buffer_params.width = bake_width;
745 buffer_params.height = bake_height;
746 buffer_params.window_width = bake_width;
747 buffer_params.window_height = bake_height;
748 /* Unique layer name for multi-image baking. */
749 buffer_params.layer = string_printf("bake_%d\n", bake_id++);
750
751 /* Update session. */
752 session->reset(session_params, buffer_params);
753
754 session->progress.set_update_callback([this] { update_bake_progress(); });
755 }
756
757 /* Perform bake. Check cancel to avoid crash with incomplete scene data. */
758 if (bake_object && !session->progress.get_cancel()) {
759 session->start();
760 session->wait();
761 }
762
763 /* Restore object state. */
764 if (bake_object) {
765 bake_object->set_is_shadow_catcher(was_shadow_catcher);
766 }
767
768 /* Restore the state of denoiser to before it was possibly disabled by the pass, so that the
769 * next baking pass can use the original value. */
770 integrator->set_use_denoise(was_denoiser_enabled);
771}
772
773void BlenderSession::synchronize(BL::Depsgraph &b_depsgraph_)
774{
775 /* only used for viewport render */
776 if (!b_v3d) {
777 return;
778 }
779
780 /* on session/scene parameter changes, we recreate session entirely */
781 const SessionParams session_params = BlenderSync::get_session_params(
783 const SceneParams scene_params = BlenderSync::get_scene_params(
785 const bool session_pause = BlenderSync::get_session_pause(b_scene, background);
786
787 if (session->params.modified(session_params) || scene->params.modified(scene_params)) {
788 free_session();
790 }
791
793
794 /* increase samples and render time, but never decrease */
795 session->set_samples(session_params.samples);
796 session->set_time_limit(session_params.time_limit);
797 session->set_pause(session_pause);
798
799 /* copy recalc flags, outside of mutex so we can decide to do the real
800 * synchronization at a later time to not block on running updates */
801 sync->sync_recalc(b_depsgraph_, b_v3d, b_rv3d);
802
803 /* don't do synchronization if on pause */
804 if (session_pause) {
805 tag_update();
806 return;
807 }
808
809 /* try to acquire mutex. if we don't want to or can't, come back later */
810 if (!session->ready_to_reset() || !session->scene->mutex.try_lock()) {
811 tag_update();
812 return;
813 }
814
815 /* data and camera synchronize */
816 b_depsgraph = b_depsgraph_;
817
818 BL::Object b_camera_override(b_engine.camera_override());
819 sync->sync_data(b_render,
821 b_v3d,
822 b_camera_override,
823 width,
824 height,
826 session_params.denoise_device);
827
828 if (b_rv3d) {
829 sync->sync_view(b_v3d, b_rv3d, width, height);
830 }
831 else {
832 sync->sync_camera(b_render, b_camera_override, width, height, "");
833 }
834
835 /* get buffer parameters */
836 const BufferParams buffer_params = BlenderSync::get_buffer_params(
837 b_v3d, b_rv3d, scene->camera, width, height);
838
839 /* reset if needed */
840 if (scene->need_reset()) {
841 session->reset(session_params, buffer_params);
842
843 /* After session reset, so device is not accessing image data anymore. */
845
846 /* reset time */
847 start_resize_time = 0.0;
848 }
849
850 /* unlock */
851 session->scene->mutex.unlock();
852
853 /* Start rendering thread, if it's not running already. Do this
854 * after all scene data has been synced at least once. */
855 session->start();
856}
857
858void BlenderSession::draw(BL::SpaceImageEditor &space_image)
859{
860 if (!session || !session->scene) {
861 /* Offline render drawing does not force the render engine update, which means it's possible
862 * that the Session is not created yet. */
863 return;
864 }
865
867
868 const int pass_index = space_image.image_user().multilayer_pass();
869 if (pass_index != draw_state_.last_pass_index) {
870 BL::RenderPass b_display_pass(b_engine.pass_by_index_get(b_rlay_name.c_str(), pass_index));
871 if (!b_display_pass) {
872 return;
873 }
874
875 Scene *scene = session->scene.get();
876
877 const thread_scoped_lock lock(scene->mutex);
878
879 const Pass *pass = Pass::find(scene->passes, b_display_pass.name());
880 if (!pass) {
881 return;
882 }
883
884 scene->film->set_display_pass(pass->get_type());
885
886 draw_state_.last_pass_index = pass_index;
887 }
888
889 if (display_driver_) {
890 BL::Array<float, 2> zoom = space_image.zoom();
891 display_driver_->set_zoom(zoom[0], zoom[1]);
892 }
893
894 session->draw();
895}
896
897void BlenderSession::view_draw(const int w, const int h)
898{
899 /* pause in redraw in case update is not being called due to final render */
901
902 /* before drawing, we verify camera and viewport size changes, because
903 * we do not get update callbacks for those, we must detect them here */
904 if (session->ready_to_reset()) {
905 bool reset = false;
906
907 /* if dimensions changed, reset */
908 if (width != w || height != h) {
909 if (start_resize_time == 0.0) {
910 /* don't react immediately to resizes to avoid flickery resizing
911 * of the viewport, and some window managers changing the window
912 * size temporarily on unminimize */
914 tag_redraw();
915 }
916 else if (time_dt() - start_resize_time < 0.2) {
917 tag_redraw();
918 }
919 else {
920 width = w;
921 height = h;
922 reset = true;
923 }
924 }
925
926 /* try to acquire mutex. if we can't, come back later */
927 if (!session->scene->mutex.try_lock()) {
928 tag_update();
929 }
930 else {
931 /* update camera from 3d view */
932
933 sync->sync_view(b_v3d, b_rv3d, width, height);
934
935 if (scene->camera->is_modified()) {
936 reset = true;
937 }
938
939 session->scene->mutex.unlock();
940 }
941
942 /* reset if requested */
943 if (reset) {
944 const SessionParams session_params = BlenderSync::get_session_params(
946 const BufferParams buffer_params = BlenderSync::get_buffer_params(
947 b_v3d, b_rv3d, scene->camera, width, height);
948 const bool session_pause = BlenderSync::get_session_pause(b_scene, background);
949
950 if (session_pause == false) {
951 session->reset(session_params, buffer_params);
952 start_resize_time = 0.0;
953 }
954 }
955 }
956 else {
957 tag_update();
958 }
959
960 /* update status and progress for 3d view draw */
962
963 /* draw */
964 session->draw();
965}
966
967void BlenderSession::get_status(string &status, string &substatus)
968{
969 session->progress.get_status(status, substatus);
970}
971
972void BlenderSession::get_progress(double &progress, double &total_time, double &render_time)
973{
974 session->progress.get_time(total_time, render_time);
975 progress = session->progress.get_progress();
976}
977
979{
980 const double progress = session->progress.get_progress();
981
982 if (progress != last_progress) {
983 b_engine.update_progress((float)progress);
985 }
986}
987
989{
990 string timestatus;
991 string status;
992 string substatus;
993 string scene_status;
994 double progress;
995 double total_time;
996 double remaining_time = 0;
997 double render_time;
998 const float mem_used = (float)session->stats.mem_used / 1024.0f / 1024.0f;
999 const float mem_peak = (float)session->stats.mem_peak / 1024.0f / 1024.0f;
1000
1001 get_status(status, substatus);
1002 get_progress(progress, total_time, render_time);
1003
1004 if (progress > 0) {
1005 remaining_time = session->get_estimated_remaining_time();
1006 }
1007
1008 if (background) {
1009 if (scene) {
1010 scene_status += " | " + scene->name;
1011 }
1012 if (!b_rlay_name.empty()) {
1013 scene_status += ", " + b_rlay_name;
1014 }
1015
1016 if (!b_rview_name.empty()) {
1017 scene_status += ", " + b_rview_name;
1018 }
1019
1020 if (remaining_time > 0) {
1021 timestatus += "Remaining:" + time_human_readable_from_seconds(remaining_time) + " | ";
1022 }
1023
1024 timestatus += string_printf("Mem:%.2fM, Peak:%.2fM", (double)mem_used, (double)mem_peak);
1025
1026 if (!status.empty()) {
1027 status = " | " + status;
1028 }
1029 if (!substatus.empty()) {
1030 status += " | " + substatus;
1031 }
1032 }
1033
1034 const double current_time = time_dt();
1035 /* When rendering in a window, redraw the status at least once per second to keep the elapsed
1036 * and remaining time up-to-date. For headless rendering, only report when something
1037 * significant changes to keep the console output readable. */
1038 if (status != last_status || (!headless && (current_time - last_status_time) > 1.0)) {
1039 b_engine.update_stats("", (timestatus + scene_status + status).c_str());
1040 b_engine.update_memory_stats(mem_used, mem_peak);
1041 last_status = status;
1042 last_status_time = current_time;
1043 }
1044 if (progress != last_progress) {
1045 b_engine.update_progress((float)progress);
1047 }
1048
1050}
1051
1053{
1054 if (!session->progress.get_error()) {
1055 return false;
1056 }
1057
1058 const string error = session->progress.get_error_message();
1059 if (error != last_error) {
1060 /* TODO(sergey): Currently C++ RNA API doesn't let us to use mnemonic name for the variable.
1061 * Would be nice to have this figured out.
1062 *
1063 * For until then, 1 << 5 means RPT_ERROR. */
1064 b_engine.report(1 << 5, error.c_str());
1065 b_engine.error_set(error.c_str());
1066 last_error = error;
1067 }
1068
1069 return true;
1070}
1071
1073{
1074 /* tell blender that we want to get another update callback */
1075 b_engine.tag_update();
1076}
1077
1079{
1080 if (background) {
1081 /* update stats and progress, only for background here because
1082 * in 3d view we do it in draw for thread safety reasons */
1084
1085 /* offline render, redraw if timeout passed */
1086 if (time_dt() - last_redraw_time > 1.0) {
1087 b_engine.tag_redraw();
1089 }
1090 }
1091 else {
1092 /* tell blender that we want to redraw */
1093 b_engine.tag_redraw();
1094 }
1095}
1096
1098{
1099 /* test if we need to cancel rendering */
1100 if (background) {
1101 if (b_engine.test_break()) {
1102 session->progress.set_cancel("Cancelled");
1103 }
1104 }
1105}
1106
1108{
1109 if (!background) {
1110 /* During interactive render we can not free anything: attempts to save
1111 * memory would cause things to be allocated and evaluated for every
1112 * updated sample.
1113 */
1114 return;
1115 }
1116 b_engine.free_blender_memory();
1117}
1118
1120{
1121 if (display_driver_) {
1122 /* Driver is already created. */
1123 return;
1124 }
1125
1126 if (headless) {
1127 /* No display needed for headless. */
1128 return;
1129 }
1130
1131 if (b_engine.is_preview()) {
1132 /* TODO(sergey): Investigate whether DisplayDriver can be used for the preview as well. */
1133 return;
1134 }
1135
1136 unique_ptr<BlenderDisplayDriver> display_driver = make_unique<BlenderDisplayDriver>(
1138 display_driver_ = display_driver.get();
1139 session->set_display_driver(std::move(display_driver));
1140}
1141
static AppView * view
float progress
Definition WM_types.hh:1019
volatile int lock
static void add_cryptomatte_layer(BL::RenderResult &b_rr, string name, string manifest)
static bool bake_setup_pass(Scene *scene, const string &bake_type, const int bake_filter)
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
static unsigned long seed
Definition btSoftBody.h:39
void reset()
clear internal cached data and reset random seed
void stamp_view_layer_metadata(Scene *scene, const string &view_layer_name)
BL::RenderSettings b_render
static bool headless
static bool print_render_stats
BL::RegionView3D b_rv3d
bool check_and_report_session_error()
BL::RenderEngine b_engine
void full_buffer_written(string_view filename)
void synchronize(BL::Depsgraph &b_depsgraph)
vector< string > full_buffer_files_
struct BlenderSession::@165223306110125113174150302226233170150374213332 draw_state_
void ensure_display_driver_if_needed()
void bake(BL::Depsgraph &b_depsgraph_, BL::Object &b_object, const string &bake_type, const int bake_filter, const int bake_width, const int bake_height)
void free_blender_memory_if_possible()
BL::Preferences b_userpref
void reset_session(BL::BlendData &b_data, BL::Depsgraph &b_depsgraph)
BlenderDisplayDriver * display_driver_
unique_ptr< BlenderSync > sync
BL::BlendData b_data
void render(BL::Depsgraph &b_depsgraph)
void draw(BL::SpaceImageEditor &space_image)
void view_draw(const int w, const int h)
BL::SpaceView3D b_v3d
unique_ptr< Session > session
BlenderSession(BL::RenderEngine &b_engine, BL::Preferences &b_userpref, BL::BlendData &b_data, bool preview_osl)
void get_status(string &status, string &substatus)
void get_progress(double &progress, double &total_time, double &render_time)
BL::Depsgraph b_depsgraph
static DeviceTypeMask device_override
static bool get_session_pause(BL::Scene &b_scene, bool background)
Definition sync.cpp:918
static SessionParams get_session_params(BL::RenderEngine &b_engine, BL::Preferences &b_preferences, BL::Scene &b_scene, bool background)
Definition sync.cpp:924
static SceneParams get_scene_params(BL::Scene &b_scene, const bool background, const bool use_developer_ui)
Definition sync.cpp:863
static BufferParams get_buffer_params(BL::SpaceView3D &b_v3d, BL::RegionView3D &b_rv3d, Camera *cam, const int width, const int height)
ustring view
Definition buffers.h:97
ustring layer
Definition buffers.h:96
int window_height
Definition buffers.h:80
int window_width
Definition buffers.h:79
NODE_DECLARE int width
Definition buffers.h:70
Definition film.h:29
Definition pass.h:49
static const Pass * find(const unique_ptr_vector< Pass > &passes, const string &name)
Definition pass.cpp:380
PassInfo get_info() const
Definition pass.cpp:140
DeviceInfo denoise_device
static int render_resolution_x(BL::RenderSettings &b_render)
static int render_resolution_y(BL::RenderSettings &b_render)
static void render_add_metadata(BL::RenderResult &b_rr, string name, string value)
#define CCL_NAMESPACE_END
DeviceTypeMask
@ DEVICE_MASK_ALL
static const char * to_string(const Interpolation &interp)
Definition gl_shader.cc:109
#define this
#define printf(...)
ccl_device_inline uint hash_uint2(const uint kx, const uint ky)
Definition hash.h:90
@ CRYPT_ASSET
@ CRYPT_OBJECT
@ CRYPT_MATERIAL
PassType
@ PASS_EMISSION
@ PASS_POSITION
@ PASS_BACKGROUND
@ PASS_TRANSMISSION_DIRECT
@ PASS_UV
@ PASS_TRANSMISSION_COLOR
@ PASS_TRANSMISSION_INDIRECT
@ PASS_SHADOW_CATCHER
@ PASS_ROUGHNESS
@ PASS_DIFFUSE_DIRECT
@ PASS_AO
@ PASS_COMBINED
@ PASS_DIFFUSE
@ PASS_DIFFUSE_INDIRECT
@ PASS_GLOSSY
@ PASS_NONE
@ PASS_TRANSMISSION
@ PASS_NORMAL
@ PASS_DIFFUSE_COLOR
@ PASS_GLOSSY_DIRECT
@ PASS_GLOSSY_COLOR
@ PASS_GLOSSY_INDIRECT
#define VLOG_INFO
Definition log.h:71
static void error(const char *str)
uint32_t util_murmur_hash3(const void *key, const int len, const uint32_t seed)
bool path_remove(const string &path)
Definition path.cpp:778
PointerRNA RNA_pointer_get(PointerRNA *ptr, const char *name)
void RNA_boolean_set(PointerRNA *ptr, const char *name, bool value)
const PointerRNA PointerRNA_NULL
CCL_NAMESPACE_BEGIN string string_printf(const char *format,...)
Definition string.cpp:23
bool support_denoise
Definition pass.h:46
string full_report()
Definition stats.cpp:290
Film * film
Definition scene.h:128
T * create_node(Args &&...)=delete
@ MOTION_NONE
Definition scene.h:184
Integrator * integrator
Definition scene.h:130
std::unique_lock< std::mutex > thread_scoped_lock
Definition thread.h:28
string time_human_readable_from_seconds(const double seconds)
Definition time.cpp:69
CCL_NAMESPACE_BEGIN double time_dt()
Definition time.cpp:38
double total_time