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