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