1 /*
2 * Copyright 2011-2013 Blender Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <stdlib.h>
18
19 #include "device/device.h"
20 #include "render/background.h"
21 #include "render/buffers.h"
22 #include "render/camera.h"
23 #include "render/colorspace.h"
24 #include "render/film.h"
25 #include "render/integrator.h"
26 #include "render/light.h"
27 #include "render/mesh.h"
28 #include "render/object.h"
29 #include "render/scene.h"
30 #include "render/session.h"
31 #include "render/shader.h"
32 #include "render/stats.h"
33
34 #include "util/util_algorithm.h"
35 #include "util/util_color.h"
36 #include "util/util_foreach.h"
37 #include "util/util_function.h"
38 #include "util/util_hash.h"
39 #include "util/util_logging.h"
40 #include "util/util_murmurhash.h"
41 #include "util/util_progress.h"
42 #include "util/util_time.h"
43
44 #include "blender/blender_session.h"
45 #include "blender/blender_sync.h"
46 #include "blender/blender_util.h"
47
48 CCL_NAMESPACE_BEGIN
49
50 DeviceTypeMask BlenderSession::device_override = DEVICE_MASK_ALL;
51 bool BlenderSession::headless = false;
52 int BlenderSession::num_resumable_chunks = 0;
53 int BlenderSession::current_resumable_chunk = 0;
54 int BlenderSession::start_resumable_chunk = 0;
55 int BlenderSession::end_resumable_chunk = 0;
56 bool BlenderSession::print_render_stats = false;
57
BlenderSession(BL::RenderEngine & b_engine,BL::Preferences & b_userpref,BL::BlendData & b_data,bool preview_osl)58 BlenderSession::BlenderSession(BL::RenderEngine &b_engine,
59 BL::Preferences &b_userpref,
60 BL::BlendData &b_data,
61 bool preview_osl)
62 : session(NULL),
63 scene(NULL),
64 sync(NULL),
65 b_engine(b_engine),
66 b_userpref(b_userpref),
67 b_data(b_data),
68 b_render(b_engine.render()),
69 b_depsgraph(PointerRNA_NULL),
70 b_scene(PointerRNA_NULL),
71 b_v3d(PointerRNA_NULL),
72 b_rv3d(PointerRNA_NULL),
73 width(0),
74 height(0),
75 preview_osl(preview_osl),
76 python_thread_state(NULL)
77 {
78 /* offline render */
79 background = true;
80 last_redraw_time = 0.0;
81 start_resize_time = 0.0;
82 last_status_time = 0.0;
83 }
84
BlenderSession(BL::RenderEngine & b_engine,BL::Preferences & b_userpref,BL::BlendData & b_data,BL::SpaceView3D & b_v3d,BL::RegionView3D & b_rv3d,int width,int height)85 BlenderSession::BlenderSession(BL::RenderEngine &b_engine,
86 BL::Preferences &b_userpref,
87 BL::BlendData &b_data,
88 BL::SpaceView3D &b_v3d,
89 BL::RegionView3D &b_rv3d,
90 int width,
91 int height)
92 : session(NULL),
93 scene(NULL),
94 sync(NULL),
95 b_engine(b_engine),
96 b_userpref(b_userpref),
97 b_data(b_data),
98 b_render(b_engine.render()),
99 b_depsgraph(PointerRNA_NULL),
100 b_scene(PointerRNA_NULL),
101 b_v3d(b_v3d),
102 b_rv3d(b_rv3d),
103 width(width),
104 height(height),
105 preview_osl(false),
106 python_thread_state(NULL)
107 {
108 /* 3d view render */
109 background = false;
110 last_redraw_time = 0.0;
111 start_resize_time = 0.0;
112 last_status_time = 0.0;
113 }
114
~BlenderSession()115 BlenderSession::~BlenderSession()
116 {
117 free_session();
118 }
119
create_session()120 void BlenderSession::create_session()
121 {
122 SessionParams session_params = BlenderSync::get_session_params(
123 b_engine, b_userpref, b_scene, background);
124 SceneParams scene_params = BlenderSync::get_scene_params(b_scene, background);
125 bool session_pause = BlenderSync::get_session_pause(b_scene, background);
126
127 /* reset status/progress */
128 last_status = "";
129 last_error = "";
130 last_progress = -1.0f;
131 start_resize_time = 0.0;
132
133 /* create session */
134 session = new Session(session_params);
135 session->scene = scene;
136 session->progress.set_update_callback(function_bind(&BlenderSession::tag_redraw, this));
137 session->progress.set_cancel_callback(function_bind(&BlenderSession::test_cancel, this));
138 session->set_pause(session_pause);
139
140 /* create scene */
141 scene = new Scene(scene_params, session->device);
142 scene->name = b_scene.name();
143
144 session->scene = scene;
145
146 /* There is no single depsgraph to use for the entire render.
147 * So we need to handle this differently.
148 *
149 * We could loop over the final render result render layers in pipeline and keep Cycles unaware
150 * of multiple layers, or perhaps move syncing further down in the pipeline.
151 */
152 /* create sync */
153 sync = new BlenderSync(b_engine, b_data, b_scene, scene, !background, session->progress);
154 BL::Object b_camera_override(b_engine.camera_override());
155 if (b_v3d) {
156 sync->sync_view(b_v3d, b_rv3d, width, height);
157 }
158 else {
159 sync->sync_camera(b_render, b_camera_override, width, height, "");
160 }
161
162 /* set buffer parameters */
163 BufferParams buffer_params = BlenderSync::get_buffer_params(
164 b_render, b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use);
165 session->reset(buffer_params, session_params.samples);
166
167 b_engine.use_highlight_tiles(session_params.progressive_refine == false);
168
169 update_resumable_tile_manager(session_params.samples);
170 }
171
reset_session(BL::BlendData & b_data,BL::Depsgraph & b_depsgraph)172 void BlenderSession::reset_session(BL::BlendData &b_data, BL::Depsgraph &b_depsgraph)
173 {
174 /* Update data, scene and depsgraph pointers. These can change after undo. */
175 this->b_data = b_data;
176 this->b_depsgraph = b_depsgraph;
177 this->b_scene = b_depsgraph.scene_eval();
178 if (sync) {
179 sync->reset(this->b_data, this->b_scene);
180 }
181
182 if (preview_osl) {
183 PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
184 RNA_boolean_set(&cscene, "shading_system", preview_osl);
185 }
186
187 if (b_v3d) {
188 this->b_render = b_scene.render();
189 }
190 else {
191 this->b_render = b_engine.render();
192 width = render_resolution_x(b_render);
193 height = render_resolution_y(b_render);
194 }
195
196 bool is_new_session = (session == NULL);
197 if (is_new_session) {
198 /* Initialize session and remember it was just created so not to
199 * re-create it below.
200 */
201 create_session();
202 }
203
204 if (b_v3d) {
205 /* NOTE: We need to create session, but all the code from below
206 * will make viewport render to stuck on initialization.
207 */
208 return;
209 }
210
211 SessionParams session_params = BlenderSync::get_session_params(
212 b_engine, b_userpref, b_scene, background);
213 SceneParams scene_params = BlenderSync::get_scene_params(b_scene, background);
214
215 if (scene->params.modified(scene_params) || session->params.modified(session_params) ||
216 !scene_params.persistent_data) {
217 /* if scene or session parameters changed, it's easier to simply re-create
218 * them rather than trying to distinguish which settings need to be updated
219 */
220 if (!is_new_session) {
221 free_session();
222 create_session();
223 }
224 return;
225 }
226
227 session->progress.reset();
228 scene->reset();
229
230 session->tile_manager.set_tile_order(session_params.tile_order);
231
232 /* peak memory usage should show current render peak, not peak for all renders
233 * made by this render session
234 */
235 session->stats.mem_peak = session->stats.mem_used;
236
237 /* There is no single depsgraph to use for the entire render.
238 * See note on create_session().
239 */
240 /* sync object should be re-created */
241 delete sync;
242 sync = new BlenderSync(b_engine, b_data, b_scene, scene, !background, session->progress);
243
244 BL::SpaceView3D b_null_space_view3d(PointerRNA_NULL);
245 BL::RegionView3D b_null_region_view3d(PointerRNA_NULL);
246 BufferParams buffer_params = BlenderSync::get_buffer_params(b_render,
247 b_null_space_view3d,
248 b_null_region_view3d,
249 scene->camera,
250 width,
251 height,
252 session_params.denoising.use);
253 session->reset(buffer_params, session_params.samples);
254
255 b_engine.use_highlight_tiles(session_params.progressive_refine == false);
256
257 /* reset time */
258 start_resize_time = 0.0;
259 }
260
free_session()261 void BlenderSession::free_session()
262 {
263 session->cancel();
264
265 delete sync;
266 delete session;
267 }
268
get_shader_type(const string & pass_type)269 static ShaderEvalType get_shader_type(const string &pass_type)
270 {
271 const char *shader_type = pass_type.c_str();
272
273 /* data passes */
274 if (strcmp(shader_type, "NORMAL") == 0)
275 return SHADER_EVAL_NORMAL;
276 else if (strcmp(shader_type, "UV") == 0)
277 return SHADER_EVAL_UV;
278 else if (strcmp(shader_type, "ROUGHNESS") == 0)
279 return SHADER_EVAL_ROUGHNESS;
280 else if (strcmp(shader_type, "DIFFUSE_COLOR") == 0)
281 return SHADER_EVAL_DIFFUSE_COLOR;
282 else if (strcmp(shader_type, "GLOSSY_COLOR") == 0)
283 return SHADER_EVAL_GLOSSY_COLOR;
284 else if (strcmp(shader_type, "TRANSMISSION_COLOR") == 0)
285 return SHADER_EVAL_TRANSMISSION_COLOR;
286 else if (strcmp(shader_type, "EMIT") == 0)
287 return SHADER_EVAL_EMISSION;
288
289 /* light passes */
290 else if (strcmp(shader_type, "AO") == 0)
291 return SHADER_EVAL_AO;
292 else if (strcmp(shader_type, "COMBINED") == 0)
293 return SHADER_EVAL_COMBINED;
294 else if (strcmp(shader_type, "SHADOW") == 0)
295 return SHADER_EVAL_SHADOW;
296 else if (strcmp(shader_type, "DIFFUSE") == 0)
297 return SHADER_EVAL_DIFFUSE;
298 else if (strcmp(shader_type, "GLOSSY") == 0)
299 return SHADER_EVAL_GLOSSY;
300 else if (strcmp(shader_type, "TRANSMISSION") == 0)
301 return SHADER_EVAL_TRANSMISSION;
302
303 /* extra */
304 else if (strcmp(shader_type, "ENVIRONMENT") == 0)
305 return SHADER_EVAL_ENVIRONMENT;
306
307 else
308 return SHADER_EVAL_BAKE;
309 }
310
begin_render_result(BL::RenderEngine & b_engine,int x,int y,int w,int h,const char * layername,const char * viewname)311 static BL::RenderResult begin_render_result(BL::RenderEngine &b_engine,
312 int x,
313 int y,
314 int w,
315 int h,
316 const char *layername,
317 const char *viewname)
318 {
319 return b_engine.begin_result(x, y, w, h, layername, viewname);
320 }
321
end_render_result(BL::RenderEngine & b_engine,BL::RenderResult & b_rr,bool cancel,bool highlight,bool do_merge_results)322 static void end_render_result(BL::RenderEngine &b_engine,
323 BL::RenderResult &b_rr,
324 bool cancel,
325 bool highlight,
326 bool do_merge_results)
327 {
328 b_engine.end_result(b_rr, (int)cancel, (int)highlight, (int)do_merge_results);
329 }
330
do_write_update_render_tile(RenderTile & rtile,bool do_update_only,bool do_read_only,bool highlight)331 void BlenderSession::do_write_update_render_tile(RenderTile &rtile,
332 bool do_update_only,
333 bool do_read_only,
334 bool highlight)
335 {
336 int x = rtile.x - session->tile_manager.params.full_x;
337 int y = rtile.y - session->tile_manager.params.full_y;
338 int w = rtile.w;
339 int h = rtile.h;
340
341 /* get render result */
342 BL::RenderResult b_rr = begin_render_result(
343 b_engine, x, y, w, h, b_rlay_name.c_str(), b_rview_name.c_str());
344
345 /* can happen if the intersected rectangle gives 0 width or height */
346 if (b_rr.ptr.data == NULL) {
347 return;
348 }
349
350 BL::RenderResult::layers_iterator b_single_rlay;
351 b_rr.layers.begin(b_single_rlay);
352
353 /* layer will be missing if it was disabled in the UI */
354 if (b_single_rlay == b_rr.layers.end())
355 return;
356
357 BL::RenderLayer b_rlay = *b_single_rlay;
358
359 if (do_read_only) {
360 /* copy each pass */
361 BL::RenderLayer::passes_iterator b_iter;
362
363 for (b_rlay.passes.begin(b_iter); b_iter != b_rlay.passes.end(); ++b_iter) {
364 BL::RenderPass b_pass(*b_iter);
365
366 /* find matching pass type */
367 PassType pass_type = BlenderSync::get_pass_type(b_pass);
368 int components = b_pass.channels();
369
370 rtile.buffers->set_pass_rect(
371 pass_type, components, (float *)b_pass.rect(), rtile.num_samples);
372 }
373
374 end_render_result(b_engine, b_rr, false, false, false);
375 }
376 else if (do_update_only) {
377 /* Sample would be zero at initial tile update, which is only needed
378 * to tag tile form blender side as IN PROGRESS for proper highlight
379 * no buffers should be sent to blender yet. For denoise we also
380 * keep showing the noisy buffers until denoise is done. */
381 bool merge = (rtile.sample != 0) && (rtile.task != RenderTile::DENOISE);
382
383 if (merge) {
384 update_render_result(b_rlay, rtile);
385 }
386
387 end_render_result(b_engine, b_rr, true, highlight, merge);
388 }
389 else {
390 /* Write final render result. */
391 write_render_result(b_rlay, rtile);
392 end_render_result(b_engine, b_rr, false, false, true);
393 }
394 }
395
read_render_tile(RenderTile & rtile)396 void BlenderSession::read_render_tile(RenderTile &rtile)
397 {
398 do_write_update_render_tile(rtile, false, true, false);
399 }
400
write_render_tile(RenderTile & rtile)401 void BlenderSession::write_render_tile(RenderTile &rtile)
402 {
403 do_write_update_render_tile(rtile, false, false, false);
404 }
405
update_render_tile(RenderTile & rtile,bool highlight)406 void BlenderSession::update_render_tile(RenderTile &rtile, bool highlight)
407 {
408 /* use final write for preview renders, otherwise render result wouldn't be
409 * be updated in blender side
410 * would need to be investigated a bit further, but for now shall be fine
411 */
412 if (!b_engine.is_preview())
413 do_write_update_render_tile(rtile, true, false, highlight);
414 else
415 do_write_update_render_tile(rtile, false, false, false);
416 }
417
add_cryptomatte_layer(BL::RenderResult & b_rr,string name,string manifest)418 static void add_cryptomatte_layer(BL::RenderResult &b_rr, string name, string manifest)
419 {
420 string identifier = string_printf("%08x", util_murmur_hash3(name.c_str(), name.length(), 0));
421 string prefix = "cryptomatte/" + identifier.substr(0, 7) + "/";
422
423 render_add_metadata(b_rr, prefix + "name", name);
424 render_add_metadata(b_rr, prefix + "hash", "MurmurHash3_32");
425 render_add_metadata(b_rr, prefix + "conversion", "uint32_to_float32");
426 render_add_metadata(b_rr, prefix + "manifest", manifest);
427 }
428
stamp_view_layer_metadata(Scene * scene,const string & view_layer_name)429 void BlenderSession::stamp_view_layer_metadata(Scene *scene, const string &view_layer_name)
430 {
431 BL::RenderResult b_rr = b_engine.get_result();
432 string prefix = "cycles." + view_layer_name + ".";
433
434 /* Configured number of samples for the view layer. */
435 b_rr.stamp_data_add_field((prefix + "samples").c_str(),
436 to_string(session->params.samples).c_str());
437
438 /* Store ranged samples information. */
439 if (session->tile_manager.range_num_samples != -1) {
440 b_rr.stamp_data_add_field((prefix + "range_start_sample").c_str(),
441 to_string(session->tile_manager.range_start_sample).c_str());
442 b_rr.stamp_data_add_field((prefix + "range_num_samples").c_str(),
443 to_string(session->tile_manager.range_num_samples).c_str());
444 }
445
446 /* Write cryptomatte metadata. */
447 if (scene->film->cryptomatte_passes & CRYPT_OBJECT) {
448 add_cryptomatte_layer(b_rr,
449 view_layer_name + ".CryptoObject",
450 scene->object_manager->get_cryptomatte_objects(scene));
451 }
452 if (scene->film->cryptomatte_passes & CRYPT_MATERIAL) {
453 add_cryptomatte_layer(b_rr,
454 view_layer_name + ".CryptoMaterial",
455 scene->shader_manager->get_cryptomatte_materials(scene));
456 }
457 if (scene->film->cryptomatte_passes & CRYPT_ASSET) {
458 add_cryptomatte_layer(b_rr,
459 view_layer_name + ".CryptoAsset",
460 scene->object_manager->get_cryptomatte_assets(scene));
461 }
462
463 /* Store synchronization and bare-render times. */
464 double total_time, render_time;
465 session->progress.get_time(total_time, render_time);
466 b_rr.stamp_data_add_field((prefix + "total_time").c_str(),
467 time_human_readable_from_seconds(total_time).c_str());
468 b_rr.stamp_data_add_field((prefix + "render_time").c_str(),
469 time_human_readable_from_seconds(render_time).c_str());
470 b_rr.stamp_data_add_field((prefix + "synchronization_time").c_str(),
471 time_human_readable_from_seconds(total_time - render_time).c_str());
472 }
473
render(BL::Depsgraph & b_depsgraph_)474 void BlenderSession::render(BL::Depsgraph &b_depsgraph_)
475 {
476 b_depsgraph = b_depsgraph_;
477
478 /* set callback to write out render results */
479 session->write_render_tile_cb = function_bind(&BlenderSession::write_render_tile, this, _1);
480 session->update_render_tile_cb = function_bind(
481 &BlenderSession::update_render_tile, this, _1, _2);
482
483 BL::ViewLayer b_view_layer = b_depsgraph.view_layer_eval();
484
485 /* get buffer parameters */
486 SessionParams session_params = BlenderSync::get_session_params(
487 b_engine, b_userpref, b_scene, background, b_view_layer);
488 BufferParams buffer_params = BlenderSync::get_buffer_params(
489 b_render, b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use);
490
491 /* temporary render result to find needed passes and views */
492 BL::RenderResult b_rr = begin_render_result(
493 b_engine, 0, 0, 1, 1, b_view_layer.name().c_str(), NULL);
494 BL::RenderResult::layers_iterator b_single_rlay;
495 b_rr.layers.begin(b_single_rlay);
496 BL::RenderLayer b_rlay = *b_single_rlay;
497 b_rlay_name = b_view_layer.name();
498
499 /* Update denoising parameters. */
500 session->set_denoising(session_params.denoising);
501
502 /* Compute render passes and film settings. */
503 vector<Pass> passes = sync->sync_render_passes(
504 b_rlay, b_view_layer, session_params.adaptive_sampling, session_params.denoising);
505
506 /* Set buffer params, using film settings from sync_render_passes. */
507 buffer_params.passes = passes;
508 buffer_params.denoising_data_pass = scene->film->denoising_data_pass;
509 buffer_params.denoising_clean_pass = scene->film->denoising_clean_pass;
510 buffer_params.denoising_prefiltered_pass = scene->film->denoising_prefiltered_pass;
511
512 BL::RenderResult::views_iterator b_view_iter;
513
514 int num_views = 0;
515 for (b_rr.views.begin(b_view_iter); b_view_iter != b_rr.views.end(); ++b_view_iter) {
516 num_views++;
517 }
518
519 int view_index = 0;
520 for (b_rr.views.begin(b_view_iter); b_view_iter != b_rr.views.end();
521 ++b_view_iter, ++view_index) {
522 b_rview_name = b_view_iter->name();
523
524 /* set the current view */
525 b_engine.active_view_set(b_rview_name.c_str());
526
527 /* update scene */
528 BL::Object b_camera_override(b_engine.camera_override());
529 sync->sync_camera(b_render, b_camera_override, width, height, b_rview_name.c_str());
530 sync->sync_data(
531 b_render, b_depsgraph, b_v3d, b_camera_override, width, height, &python_thread_state);
532 builtin_images_load();
533
534 /* Attempt to free all data which is held by Blender side, since at this
535 * point we know that we've got everything to render current view layer.
536 */
537 /* At the moment we only free if we are not doing multi-view
538 * (or if we are rendering the last view). See T58142/D4239 for discussion.
539 */
540 if (view_index == num_views - 1) {
541 free_blender_memory_if_possible();
542 }
543
544 /* Make sure all views have different noise patterns. - hardcoded value just to make it random
545 */
546 if (view_index != 0) {
547 scene->integrator->seed += hash_uint2(scene->integrator->seed,
548 hash_uint2(view_index * 0xdeadbeef, 0));
549 scene->integrator->tag_update(scene);
550 }
551
552 /* Update number of samples per layer. */
553 int samples = sync->get_layer_samples();
554 bool bound_samples = sync->get_layer_bound_samples();
555 int effective_layer_samples;
556
557 if (samples != 0 && (!bound_samples || (samples < session_params.samples)))
558 effective_layer_samples = samples;
559 else
560 effective_layer_samples = session_params.samples;
561
562 /* Update tile manager if we're doing resumable render. */
563 update_resumable_tile_manager(effective_layer_samples);
564
565 /* Update session itself. */
566 session->reset(buffer_params, effective_layer_samples);
567
568 /* render */
569 if (!b_engine.is_preview() && background && print_render_stats) {
570 scene->enable_update_stats();
571 }
572
573 session->start();
574 session->wait();
575
576 if (!b_engine.is_preview() && background && print_render_stats) {
577 RenderStats stats;
578 session->collect_statistics(&stats);
579 printf("Render statistics:\n%s\n", stats.full_report().c_str());
580 }
581
582 if (session->progress.get_cancel())
583 break;
584 }
585
586 /* add metadata */
587 stamp_view_layer_metadata(scene, b_rlay_name);
588
589 /* free result without merging */
590 end_render_result(b_engine, b_rr, true, true, false);
591
592 double total_time, render_time;
593 session->progress.get_time(total_time, render_time);
594 VLOG(1) << "Total render time: " << total_time;
595 VLOG(1) << "Render time (without synchronization): " << render_time;
596
597 /* clear callback */
598 session->write_render_tile_cb = function_null;
599 session->update_render_tile_cb = function_null;
600
601 /* TODO: find a way to clear this data for persistent data render */
602 #if 0
603 /* free all memory used (host and device), so we wouldn't leave render
604 * engine with extra memory allocated
605 */
606
607 session->device_free();
608
609 delete sync;
610 sync = NULL;
611 #endif
612 }
613
bake_pass_filter_get(const int pass_filter)614 static int bake_pass_filter_get(const int pass_filter)
615 {
616 int flag = BAKE_FILTER_NONE;
617
618 if ((pass_filter & BL::BakeSettings::pass_filter_DIRECT) != 0)
619 flag |= BAKE_FILTER_DIRECT;
620 if ((pass_filter & BL::BakeSettings::pass_filter_INDIRECT) != 0)
621 flag |= BAKE_FILTER_INDIRECT;
622 if ((pass_filter & BL::BakeSettings::pass_filter_COLOR) != 0)
623 flag |= BAKE_FILTER_COLOR;
624
625 if ((pass_filter & BL::BakeSettings::pass_filter_DIFFUSE) != 0)
626 flag |= BAKE_FILTER_DIFFUSE;
627 if ((pass_filter & BL::BakeSettings::pass_filter_GLOSSY) != 0)
628 flag |= BAKE_FILTER_GLOSSY;
629 if ((pass_filter & BL::BakeSettings::pass_filter_TRANSMISSION) != 0)
630 flag |= BAKE_FILTER_TRANSMISSION;
631
632 if ((pass_filter & BL::BakeSettings::pass_filter_EMIT) != 0)
633 flag |= BAKE_FILTER_EMISSION;
634 if ((pass_filter & BL::BakeSettings::pass_filter_AO) != 0)
635 flag |= BAKE_FILTER_AO;
636
637 return flag;
638 }
639
bake(BL::Depsgraph & b_depsgraph_,BL::Object & b_object,const string & pass_type,const int pass_filter,const int bake_width,const int bake_height)640 void BlenderSession::bake(BL::Depsgraph &b_depsgraph_,
641 BL::Object &b_object,
642 const string &pass_type,
643 const int pass_filter,
644 const int bake_width,
645 const int bake_height)
646 {
647 b_depsgraph = b_depsgraph_;
648
649 ShaderEvalType shader_type = get_shader_type(pass_type);
650 int bake_pass_filter = bake_pass_filter_get(pass_filter);
651
652 /* Initialize bake manager, before we load the baking kernels. */
653 scene->bake_manager->set(scene, b_object.name(), shader_type, bake_pass_filter);
654
655 /* Passes are identified by name, so in order to return the combined pass we need to set the
656 * name. */
657 Pass::add(PASS_COMBINED, scene->passes, "Combined");
658
659 session->read_bake_tile_cb = function_bind(&BlenderSession::read_render_tile, this, _1);
660 session->write_render_tile_cb = function_bind(&BlenderSession::write_render_tile, this, _1);
661
662 if (!session->progress.get_cancel()) {
663 /* Sync scene. */
664 BL::Object b_camera_override(b_engine.camera_override());
665 sync->sync_camera(b_render, b_camera_override, width, height, "");
666 sync->sync_data(
667 b_render, b_depsgraph, b_v3d, b_camera_override, width, height, &python_thread_state);
668 builtin_images_load();
669 }
670
671 /* Object might have been disabled for rendering or excluded in some
672 * other way, in that case Blender will report a warning afterwards. */
673 bool object_found = false;
674 foreach (Object *ob, scene->objects) {
675 if (ob->name == b_object.name()) {
676 object_found = true;
677 break;
678 }
679 }
680
681 if (object_found && !session->progress.get_cancel()) {
682 /* Get session and buffer parameters. */
683 SessionParams session_params = BlenderSync::get_session_params(
684 b_engine, b_userpref, b_scene, background);
685 session_params.progressive_refine = false;
686
687 BufferParams buffer_params;
688 buffer_params.width = bake_width;
689 buffer_params.height = bake_height;
690 buffer_params.passes = scene->passes;
691
692 /* Update session. */
693 session->tile_manager.set_samples(session_params.samples);
694 session->reset(buffer_params, session_params.samples);
695
696 session->progress.set_update_callback(
697 function_bind(&BlenderSession::update_bake_progress, this));
698 }
699
700 /* Perform bake. Check cancel to avoid crash with incomplete scene data. */
701 if (object_found && !session->progress.get_cancel()) {
702 session->start();
703 session->wait();
704 }
705
706 session->read_bake_tile_cb = function_null;
707 session->write_render_tile_cb = function_null;
708 }
709
do_write_update_render_result(BL::RenderLayer & b_rlay,RenderTile & rtile,bool do_update_only)710 void BlenderSession::do_write_update_render_result(BL::RenderLayer &b_rlay,
711 RenderTile &rtile,
712 bool do_update_only)
713 {
714 RenderBuffers *buffers = rtile.buffers;
715
716 /* copy data from device */
717 if (!buffers->copy_from_device())
718 return;
719
720 float exposure = scene->film->exposure;
721
722 vector<float> pixels(rtile.w * rtile.h * 4);
723
724 /* Adjust absolute sample number to the range. */
725 int sample = rtile.sample;
726 const int range_start_sample = session->tile_manager.range_start_sample;
727 if (range_start_sample != -1) {
728 sample -= range_start_sample;
729 }
730
731 if (!do_update_only) {
732 /* copy each pass */
733 BL::RenderLayer::passes_iterator b_iter;
734
735 for (b_rlay.passes.begin(b_iter); b_iter != b_rlay.passes.end(); ++b_iter) {
736 BL::RenderPass b_pass(*b_iter);
737 int components = b_pass.channels();
738
739 /* Copy pixels from regular render passes. */
740 bool read = buffers->get_pass_rect(b_pass.name(), exposure, sample, components, &pixels[0]);
741
742 /* If denoising pass, */
743 if (!read) {
744 int denoising_offset = BlenderSync::get_denoising_pass(b_pass);
745 if (denoising_offset >= 0) {
746 read = buffers->get_denoising_pass_rect(
747 denoising_offset, exposure, sample, components, &pixels[0]);
748 }
749 }
750
751 if (!read) {
752 memset(&pixels[0], 0, pixels.size() * sizeof(float));
753 }
754
755 b_pass.rect(&pixels[0]);
756 }
757 }
758 else {
759 /* copy combined pass */
760 BL::RenderPass b_combined_pass(b_rlay.passes.find_by_name("Combined", b_rview_name.c_str()));
761 if (buffers->get_pass_rect("Combined", exposure, sample, 4, &pixels[0]))
762 b_combined_pass.rect(&pixels[0]);
763 }
764 }
765
write_render_result(BL::RenderLayer & b_rlay,RenderTile & rtile)766 void BlenderSession::write_render_result(BL::RenderLayer &b_rlay, RenderTile &rtile)
767 {
768 do_write_update_render_result(b_rlay, rtile, false);
769 }
770
update_render_result(BL::RenderLayer & b_rlay,RenderTile & rtile)771 void BlenderSession::update_render_result(BL::RenderLayer &b_rlay, RenderTile &rtile)
772 {
773 do_write_update_render_result(b_rlay, rtile, true);
774 }
775
synchronize(BL::Depsgraph & b_depsgraph_)776 void BlenderSession::synchronize(BL::Depsgraph &b_depsgraph_)
777 {
778 /* only used for viewport render */
779 if (!b_v3d)
780 return;
781
782 /* on session/scene parameter changes, we recreate session entirely */
783 SessionParams session_params = BlenderSync::get_session_params(
784 b_engine, b_userpref, b_scene, background);
785 SceneParams scene_params = BlenderSync::get_scene_params(b_scene, background);
786 bool session_pause = BlenderSync::get_session_pause(b_scene, background);
787
788 if (session->params.modified(session_params) || scene->params.modified(scene_params)) {
789 free_session();
790 create_session();
791 }
792
793 /* increase samples, but never decrease */
794 session->set_samples(session_params.samples);
795 session->set_denoising_start_sample(session_params.denoising.start_sample);
796 session->set_pause(session_pause);
797
798 /* copy recalc flags, outside of mutex so we can decide to do the real
799 * synchronization at a later time to not block on running updates */
800 sync->sync_recalc(b_depsgraph_, b_v3d);
801
802 /* don't do synchronization if on pause */
803 if (session_pause) {
804 tag_update();
805 return;
806 }
807
808 /* try to acquire mutex. if we don't want to or can't, come back later */
809 if (!session->ready_to_reset() || !session->scene->mutex.try_lock()) {
810 tag_update();
811 return;
812 }
813
814 /* data and camera synchronize */
815 b_depsgraph = b_depsgraph_;
816
817 BL::Object b_camera_override(b_engine.camera_override());
818 sync->sync_data(
819 b_render, b_depsgraph, b_v3d, b_camera_override, width, height, &python_thread_state);
820
821 if (b_rv3d)
822 sync->sync_view(b_v3d, b_rv3d, width, height);
823 else
824 sync->sync_camera(b_render, b_camera_override, width, height, "");
825
826 /* get buffer parameters */
827 BufferParams buffer_params = BlenderSync::get_buffer_params(
828 b_render, b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use);
829
830 if (!buffer_params.denoising_data_pass) {
831 session_params.denoising.use = false;
832 }
833
834 session->set_denoising(session_params.denoising);
835
836 /* Update film if denoising data was enabled or disabled. */
837 if (scene->film->denoising_data_pass != buffer_params.denoising_data_pass) {
838 scene->film->denoising_data_pass = buffer_params.denoising_data_pass;
839 scene->film->tag_update(scene);
840 }
841
842 /* reset if needed */
843 if (scene->need_reset()) {
844 session->reset(buffer_params, session_params.samples);
845
846 /* After session reset, so device is not accessing image data anymore. */
847 builtin_images_load();
848
849 /* reset time */
850 start_resize_time = 0.0;
851 }
852
853 /* unlock */
854 session->scene->mutex.unlock();
855
856 /* Start rendering thread, if it's not running already. Do this
857 * after all scene data has been synced at least once. */
858 session->start();
859 }
860
draw(int w,int h)861 bool BlenderSession::draw(int w, int h)
862 {
863 /* pause in redraw in case update is not being called due to final render */
864 session->set_pause(BlenderSync::get_session_pause(b_scene, background));
865
866 /* before drawing, we verify camera and viewport size changes, because
867 * we do not get update callbacks for those, we must detect them here */
868 if (session->ready_to_reset()) {
869 bool reset = false;
870
871 /* if dimensions changed, reset */
872 if (width != w || height != h) {
873 if (start_resize_time == 0.0) {
874 /* don't react immediately to resizes to avoid flickery resizing
875 * of the viewport, and some window managers changing the window
876 * size temporarily on unminimize */
877 start_resize_time = time_dt();
878 tag_redraw();
879 }
880 else if (time_dt() - start_resize_time < 0.2) {
881 tag_redraw();
882 }
883 else {
884 width = w;
885 height = h;
886 reset = true;
887 }
888 }
889
890 /* try to acquire mutex. if we can't, come back later */
891 if (!session->scene->mutex.try_lock()) {
892 tag_update();
893 }
894 else {
895 /* update camera from 3d view */
896
897 sync->sync_view(b_v3d, b_rv3d, width, height);
898
899 if (scene->camera->need_update)
900 reset = true;
901
902 session->scene->mutex.unlock();
903 }
904
905 /* reset if requested */
906 if (reset) {
907 SessionParams session_params = BlenderSync::get_session_params(
908 b_engine, b_userpref, b_scene, background);
909 BufferParams buffer_params = BlenderSync::get_buffer_params(
910 b_render, b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use);
911 bool session_pause = BlenderSync::get_session_pause(b_scene, background);
912
913 if (session_pause == false) {
914 session->reset(buffer_params, session_params.samples);
915 start_resize_time = 0.0;
916 }
917 }
918 }
919 else {
920 tag_update();
921 }
922
923 /* update status and progress for 3d view draw */
924 update_status_progress();
925
926 /* draw */
927 BufferParams buffer_params = BlenderSync::get_buffer_params(
928 b_render, b_v3d, b_rv3d, scene->camera, width, height, session->params.denoising.use);
929 DeviceDrawParams draw_params;
930
931 if (session->params.display_buffer_linear) {
932 draw_params.bind_display_space_shader_cb = function_bind(
933 &BL::RenderEngine::bind_display_space_shader, &b_engine, b_scene);
934 draw_params.unbind_display_space_shader_cb = function_bind(
935 &BL::RenderEngine::unbind_display_space_shader, &b_engine);
936 }
937
938 return !session->draw(buffer_params, draw_params);
939 }
940
get_status(string & status,string & substatus)941 void BlenderSession::get_status(string &status, string &substatus)
942 {
943 session->progress.get_status(status, substatus);
944 }
945
get_kernel_status(string & kernel_status)946 void BlenderSession::get_kernel_status(string &kernel_status)
947 {
948 session->progress.get_kernel_status(kernel_status);
949 }
950
get_progress(float & progress,double & total_time,double & render_time)951 void BlenderSession::get_progress(float &progress, double &total_time, double &render_time)
952 {
953 session->progress.get_time(total_time, render_time);
954 progress = session->progress.get_progress();
955 }
956
update_bake_progress()957 void BlenderSession::update_bake_progress()
958 {
959 float progress = session->progress.get_progress();
960
961 if (progress != last_progress) {
962 b_engine.update_progress(progress);
963 last_progress = progress;
964 }
965 }
966
update_status_progress()967 void BlenderSession::update_status_progress()
968 {
969 string timestatus, status, substatus, kernel_status;
970 string scene_status = "";
971 float progress;
972 double total_time, remaining_time = 0, render_time;
973 float mem_used = (float)session->stats.mem_used / 1024.0f / 1024.0f;
974 float mem_peak = (float)session->stats.mem_peak / 1024.0f / 1024.0f;
975
976 get_status(status, substatus);
977 get_kernel_status(kernel_status);
978 get_progress(progress, total_time, render_time);
979
980 if (progress > 0)
981 remaining_time = (1.0 - (double)progress) * (render_time / (double)progress);
982
983 if (background) {
984 if (scene)
985 scene_status += " | " + scene->name;
986 if (b_rlay_name != "")
987 scene_status += ", " + b_rlay_name;
988
989 if (b_rview_name != "")
990 scene_status += ", " + b_rview_name;
991
992 if (remaining_time > 0) {
993 timestatus += "Remaining:" + time_human_readable_from_seconds(remaining_time) + " | ";
994 }
995
996 timestatus += string_printf("Mem:%.2fM, Peak:%.2fM", (double)mem_used, (double)mem_peak);
997
998 if (status.size() > 0)
999 status = " | " + status;
1000 if (substatus.size() > 0)
1001 status += " | " + substatus;
1002 if (kernel_status.size() > 0)
1003 status += " | " + kernel_status;
1004 }
1005
1006 double current_time = time_dt();
1007 /* When rendering in a window, redraw the status at least once per second to keep the elapsed and
1008 * remaining time up-to-date. For headless rendering, only report when something significant
1009 * changes to keep the console output readable. */
1010 if (status != last_status || (!headless && (current_time - last_status_time) > 1.0)) {
1011 b_engine.update_stats("", (timestatus + scene_status + status).c_str());
1012 b_engine.update_memory_stats(mem_used, mem_peak);
1013 last_status = status;
1014 last_status_time = current_time;
1015 }
1016 if (progress != last_progress) {
1017 b_engine.update_progress(progress);
1018 last_progress = progress;
1019 }
1020
1021 if (session->progress.get_error()) {
1022 string error = session->progress.get_error_message();
1023 if (error != last_error) {
1024 /* TODO(sergey): Currently C++ RNA API doesn't let us to
1025 * use mnemonic name for the variable. Would be nice to
1026 * have this figured out.
1027 *
1028 * For until then, 1 << 5 means RPT_ERROR.
1029 */
1030 b_engine.report(1 << 5, error.c_str());
1031 b_engine.error_set(error.c_str());
1032 last_error = error;
1033 }
1034 }
1035 }
1036
tag_update()1037 void BlenderSession::tag_update()
1038 {
1039 /* tell blender that we want to get another update callback */
1040 b_engine.tag_update();
1041 }
1042
tag_redraw()1043 void BlenderSession::tag_redraw()
1044 {
1045 if (background) {
1046 /* update stats and progress, only for background here because
1047 * in 3d view we do it in draw for thread safety reasons */
1048 update_status_progress();
1049
1050 /* offline render, redraw if timeout passed */
1051 if (time_dt() - last_redraw_time > 1.0) {
1052 b_engine.tag_redraw();
1053 last_redraw_time = time_dt();
1054 }
1055 }
1056 else {
1057 /* tell blender that we want to redraw */
1058 b_engine.tag_redraw();
1059 }
1060 }
1061
test_cancel()1062 void BlenderSession::test_cancel()
1063 {
1064 /* test if we need to cancel rendering */
1065 if (background)
1066 if (b_engine.test_break())
1067 session->progress.set_cancel("Cancelled");
1068 }
1069
update_resumable_tile_manager(int num_samples)1070 void BlenderSession::update_resumable_tile_manager(int num_samples)
1071 {
1072 const int num_resumable_chunks = BlenderSession::num_resumable_chunks,
1073 current_resumable_chunk = BlenderSession::current_resumable_chunk;
1074 if (num_resumable_chunks == 0) {
1075 return;
1076 }
1077
1078 if (num_resumable_chunks > num_samples) {
1079 fprintf(stderr,
1080 "Cycles warning: more sample chunks (%d) than samples (%d), "
1081 "this will cause some samples to be included in multiple chunks.\n",
1082 num_resumable_chunks,
1083 num_samples);
1084 }
1085
1086 const float num_samples_per_chunk = (float)num_samples / num_resumable_chunks;
1087
1088 float range_start_sample, range_num_samples;
1089 if (current_resumable_chunk != 0) {
1090 /* Single chunk rendering. */
1091 range_start_sample = num_samples_per_chunk * (current_resumable_chunk - 1);
1092 range_num_samples = num_samples_per_chunk;
1093 }
1094 else {
1095 /* Ranged-chunks. */
1096 const int num_chunks = end_resumable_chunk - start_resumable_chunk + 1;
1097 range_start_sample = num_samples_per_chunk * (start_resumable_chunk - 1);
1098 range_num_samples = num_chunks * num_samples_per_chunk;
1099 }
1100
1101 /* Round after doing the multiplications with num_chunks and num_samples_per_chunk
1102 * to allow for many small chunks. */
1103 int rounded_range_start_sample = (int)floorf(range_start_sample + 0.5f);
1104 int rounded_range_num_samples = max((int)floorf(range_num_samples + 0.5f), 1);
1105
1106 /* Make sure we don't overshoot. */
1107 if (rounded_range_start_sample + rounded_range_num_samples > num_samples) {
1108 rounded_range_num_samples = num_samples - rounded_range_num_samples;
1109 }
1110
1111 VLOG(1) << "Samples range start is " << range_start_sample << ", "
1112 << "number of samples to render is " << range_num_samples;
1113
1114 scene->integrator->start_sample = rounded_range_start_sample;
1115 scene->integrator->tag_update(scene);
1116
1117 session->tile_manager.range_start_sample = rounded_range_start_sample;
1118 session->tile_manager.range_num_samples = rounded_range_num_samples;
1119 }
1120
free_blender_memory_if_possible()1121 void BlenderSession::free_blender_memory_if_possible()
1122 {
1123 if (!background) {
1124 /* During interactive render we can not free anything: attempts to save
1125 * memory would cause things to be allocated and evaluated for every
1126 * updated sample.
1127 */
1128 return;
1129 }
1130 b_engine.free_blender_memory();
1131 }
1132
1133 CCL_NAMESPACE_END
1134