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