Cycles: Ignore preprocessing time in ETA calculation
[blender-staging.git] / intern / cycles / blender / blender_session.cpp
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 "background.h"
20 #include "buffers.h"
21 #include "camera.h"
22 #include "device.h"
23 #include "integrator.h"
24 #include "film.h"
25 #include "light.h"
26 #include "mesh.h"
27 #include "object.h"
28 #include "scene.h"
29 #include "session.h"
30 #include "shader.h"
31
32 #include "util_color.h"
33 #include "util_foreach.h"
34 #include "util_function.h"
35 #include "util_progress.h"
36 #include "util_time.h"
37
38 #include "blender_sync.h"
39 #include "blender_session.h"
40 #include "blender_util.h"
41
42 CCL_NAMESPACE_BEGIN
43
44 BlenderSession::BlenderSession(BL::RenderEngine b_engine_, BL::UserPreferences b_userpref_,
45         BL::BlendData b_data_, BL::Scene b_scene_)
46 : b_engine(b_engine_), b_userpref(b_userpref_), b_data(b_data_), b_render(b_engine_.render()), b_scene(b_scene_),
47   b_v3d(PointerRNA_NULL), b_rv3d(PointerRNA_NULL), python_thread_state(NULL)
48 {
49         /* offline render */
50
51         width = render_resolution_x(b_render);
52         height = render_resolution_y(b_render);
53
54         background = true;
55         last_redraw_time = 0.0;
56         start_resize_time = 0.0;
57 }
58
59 BlenderSession::BlenderSession(BL::RenderEngine b_engine_, BL::UserPreferences b_userpref_,
60         BL::BlendData b_data_, BL::Scene b_scene_,
61         BL::SpaceView3D b_v3d_, BL::RegionView3D b_rv3d_, int width_, int height_)
62 : b_engine(b_engine_), b_userpref(b_userpref_), b_data(b_data_), b_render(b_scene_.render()), b_scene(b_scene_),
63   b_v3d(b_v3d_), b_rv3d(b_rv3d_), python_thread_state(NULL)
64 {
65         /* 3d view render */
66
67         width = width_;
68         height = height_;
69         background = false;
70         last_redraw_time = 0.0;
71         start_resize_time = 0.0;
72 }
73
74 BlenderSession::~BlenderSession()
75 {
76         free_session();
77 }
78
79 void BlenderSession::create()
80 {
81         create_session();
82
83         if(b_v3d)
84                 session->start();
85 }
86
87 void BlenderSession::create_session()
88 {
89         SessionParams session_params = BlenderSync::get_session_params(b_engine, b_userpref, b_scene, background);
90         bool is_cpu = session_params.device.type == DEVICE_CPU;
91         SceneParams scene_params = BlenderSync::get_scene_params(b_scene, background, is_cpu);
92         bool session_pause = BlenderSync::get_session_pause(b_scene, background);
93
94         /* reset status/progress */
95         last_status = "";
96         last_error = "";
97         last_progress = -1.0f;
98         start_resize_time = 0.0;
99
100         /* create scene */
101         scene = new Scene(scene_params, session_params.device);
102
103         /* setup callbacks for builtin image support */
104         scene->image_manager->builtin_image_info_cb = function_bind(&BlenderSession::builtin_image_info, this, _1, _2, _3, _4, _5, _6, _7);
105         scene->image_manager->builtin_image_pixels_cb = function_bind(&BlenderSession::builtin_image_pixels, this, _1, _2, _3);
106         scene->image_manager->builtin_image_float_pixels_cb = function_bind(&BlenderSession::builtin_image_float_pixels, this, _1, _2, _3);
107
108         /* create session */
109         session = new Session(session_params);
110         session->scene = scene;
111         session->progress.set_update_callback(function_bind(&BlenderSession::tag_redraw, this));
112         session->progress.set_cancel_callback(function_bind(&BlenderSession::test_cancel, this));
113         session->set_pause(session_pause);
114
115         /* create sync */
116         sync = new BlenderSync(b_engine, b_data, b_scene, scene, !background, session->progress, is_cpu);
117
118         if(b_v3d) {
119                 if(session_pause == false) {
120                         /* full data sync */
121                         sync->sync_view(b_v3d, b_rv3d, width, height);
122                         sync->sync_data(b_v3d, b_engine.camera_override(), &python_thread_state);
123                 }
124         }
125         else {
126                 /* for final render we will do full data sync per render layer, only
127                  * do some basic syncing here, no objects or materials for speed */
128                 sync->sync_render_layers(b_v3d, NULL);
129                 sync->sync_integrator();
130                 sync->sync_camera(b_render, b_engine.camera_override(), width, height);
131         }
132
133         /* set buffer parameters */
134         BufferParams buffer_params = BlenderSync::get_buffer_params(b_render, b_scene, b_v3d, b_rv3d, scene->camera, width, height);
135         session->reset(buffer_params, session_params.samples);
136
137         b_engine.use_highlight_tiles(session_params.progressive_refine == false);
138 }
139
140 void BlenderSession::reset_session(BL::BlendData b_data_, BL::Scene b_scene_)
141 {
142         b_data = b_data_;
143         b_render = b_engine.render();
144         b_scene = b_scene_;
145
146         SessionParams session_params = BlenderSync::get_session_params(b_engine, b_userpref, b_scene, background);
147         const bool is_cpu = session_params.device.type == DEVICE_CPU;
148         SceneParams scene_params = BlenderSync::get_scene_params(b_scene, background, is_cpu);
149
150         width = render_resolution_x(b_render);
151         height = render_resolution_y(b_render);
152
153         if(scene->params.modified(scene_params) ||
154            session->params.modified(session_params) ||
155            !scene_params.persistent_data)
156         {
157                 /* if scene or session parameters changed, it's easier to simply re-create
158                  * them rather than trying to distinguish which settings need to be updated
159                  */
160
161                 delete session;
162
163                 create_session();
164
165                 return;
166         }
167
168         session->progress.reset();
169         scene->reset();
170
171         session->tile_manager.set_tile_order(session_params.tile_order);
172
173         /* peak memory usage should show current render peak, not peak for all renders
174          * made by this render session
175          */
176         session->stats.mem_peak = session->stats.mem_used;
177
178         /* sync object should be re-created */
179         sync = new BlenderSync(b_engine, b_data, b_scene, scene, !background, session->progress, is_cpu);
180
181         /* for final render we will do full data sync per render layer, only
182          * do some basic syncing here, no objects or materials for speed */
183         sync->sync_render_layers(b_v3d, NULL);
184         sync->sync_integrator();
185         sync->sync_camera(b_render, b_engine.camera_override(), width, height);
186
187         BufferParams buffer_params = BlenderSync::get_buffer_params(b_render, b_scene, PointerRNA_NULL, PointerRNA_NULL, scene->camera, width, height);
188         session->reset(buffer_params, session_params.samples);
189
190         b_engine.use_highlight_tiles(session_params.progressive_refine == false);
191
192         /* reset time */
193         start_resize_time = 0.0;
194 }
195
196 void BlenderSession::free_session()
197 {
198         if(sync)
199                 delete sync;
200
201         delete session;
202 }
203
204 static PassType get_pass_type(BL::RenderPass b_pass)
205 {
206         switch(b_pass.type()) {
207                 case BL::RenderPass::type_COMBINED:
208                         return PASS_COMBINED;
209
210                 case BL::RenderPass::type_Z:
211                         return PASS_DEPTH;
212                 case BL::RenderPass::type_MIST:
213                         return PASS_MIST;
214                 case BL::RenderPass::type_NORMAL:
215                         return PASS_NORMAL;
216                 case BL::RenderPass::type_OBJECT_INDEX:
217                         return PASS_OBJECT_ID;
218                 case BL::RenderPass::type_UV:
219                         return PASS_UV;
220                 case BL::RenderPass::type_VECTOR:
221                         return PASS_MOTION;
222                 case BL::RenderPass::type_MATERIAL_INDEX:
223                         return PASS_MATERIAL_ID;
224
225                 case BL::RenderPass::type_DIFFUSE_DIRECT:
226                         return PASS_DIFFUSE_DIRECT;
227                 case BL::RenderPass::type_GLOSSY_DIRECT:
228                         return PASS_GLOSSY_DIRECT;
229                 case BL::RenderPass::type_TRANSMISSION_DIRECT:
230                         return PASS_TRANSMISSION_DIRECT;
231                 case BL::RenderPass::type_SUBSURFACE_DIRECT:
232                         return PASS_SUBSURFACE_DIRECT;
233
234                 case BL::RenderPass::type_DIFFUSE_INDIRECT:
235                         return PASS_DIFFUSE_INDIRECT;
236                 case BL::RenderPass::type_GLOSSY_INDIRECT:
237                         return PASS_GLOSSY_INDIRECT;
238                 case BL::RenderPass::type_TRANSMISSION_INDIRECT:
239                         return PASS_TRANSMISSION_INDIRECT;
240                 case BL::RenderPass::type_SUBSURFACE_INDIRECT:
241                         return PASS_SUBSURFACE_INDIRECT;
242
243                 case BL::RenderPass::type_DIFFUSE_COLOR:
244                         return PASS_DIFFUSE_COLOR;
245                 case BL::RenderPass::type_GLOSSY_COLOR:
246                         return PASS_GLOSSY_COLOR;
247                 case BL::RenderPass::type_TRANSMISSION_COLOR:
248                         return PASS_TRANSMISSION_COLOR;
249                 case BL::RenderPass::type_SUBSURFACE_COLOR:
250                         return PASS_SUBSURFACE_COLOR;
251
252                 case BL::RenderPass::type_EMIT:
253                         return PASS_EMISSION;
254                 case BL::RenderPass::type_ENVIRONMENT:
255                         return PASS_BACKGROUND;
256                 case BL::RenderPass::type_AO:
257                         return PASS_AO;
258                 case BL::RenderPass::type_SHADOW:
259                         return PASS_SHADOW;
260
261                 case BL::RenderPass::type_DIFFUSE:
262                 case BL::RenderPass::type_COLOR:
263                 case BL::RenderPass::type_REFRACTION:
264                 case BL::RenderPass::type_SPECULAR:
265                 case BL::RenderPass::type_REFLECTION:
266                         return PASS_NONE;
267 #ifdef WITH_CYCLES_DEBUG
268                 case BL::RenderPass::type_DEBUG:
269                 {
270                         if(b_pass.debug_type() == BL::RenderPass::debug_type_BVH_TRAVERSAL_STEPS)
271                                 return PASS_BVH_TRAVERSAL_STEPS;
272                         break;
273                 }
274 #endif
275         }
276         
277         return PASS_NONE;
278 }
279
280 static ShaderEvalType get_shader_type(const string& pass_type)
281 {
282         const char *shader_type = pass_type.c_str();
283
284         /* data passes */
285         if(strcmp(shader_type, "NORMAL")==0)
286                 return SHADER_EVAL_NORMAL;
287         else if(strcmp(shader_type, "UV")==0)
288                 return SHADER_EVAL_UV;
289         else if(strcmp(shader_type, "DIFFUSE_COLOR")==0)
290                 return SHADER_EVAL_DIFFUSE_COLOR;
291         else if(strcmp(shader_type, "GLOSSY_COLOR")==0)
292                 return SHADER_EVAL_GLOSSY_COLOR;
293         else if(strcmp(shader_type, "TRANSMISSION_COLOR")==0)
294                 return SHADER_EVAL_TRANSMISSION_COLOR;
295         else if(strcmp(shader_type, "SUBSURFACE_COLOR")==0)
296                 return SHADER_EVAL_SUBSURFACE_COLOR;
297         else if(strcmp(shader_type, "EMIT")==0)
298                 return SHADER_EVAL_EMISSION;
299
300         /* light passes */
301         else if(strcmp(shader_type, "AO")==0)
302                 return SHADER_EVAL_AO;
303         else if(strcmp(shader_type, "COMBINED")==0)
304                 return SHADER_EVAL_COMBINED;
305         else if(strcmp(shader_type, "SHADOW")==0)
306                 return SHADER_EVAL_SHADOW;
307         else if(strcmp(shader_type, "DIFFUSE_DIRECT")==0)
308                 return SHADER_EVAL_DIFFUSE_DIRECT;
309         else if(strcmp(shader_type, "GLOSSY_DIRECT")==0)
310                 return SHADER_EVAL_GLOSSY_DIRECT;
311         else if(strcmp(shader_type, "TRANSMISSION_DIRECT")==0)
312                 return SHADER_EVAL_TRANSMISSION_DIRECT;
313         else if(strcmp(shader_type, "SUBSURFACE_DIRECT")==0)
314                 return SHADER_EVAL_SUBSURFACE_DIRECT;
315         else if(strcmp(shader_type, "DIFFUSE_INDIRECT")==0)
316                 return SHADER_EVAL_DIFFUSE_INDIRECT;
317         else if(strcmp(shader_type, "GLOSSY_INDIRECT")==0)
318                 return SHADER_EVAL_GLOSSY_INDIRECT;
319         else if(strcmp(shader_type, "TRANSMISSION_INDIRECT")==0)
320                 return SHADER_EVAL_TRANSMISSION_INDIRECT;
321         else if(strcmp(shader_type, "SUBSURFACE_INDIRECT")==0)
322                 return SHADER_EVAL_SUBSURFACE_INDIRECT;
323
324         /* extra */
325         else if(strcmp(shader_type, "ENVIRONMENT")==0)
326                 return SHADER_EVAL_ENVIRONMENT;
327
328         else
329                 return SHADER_EVAL_BAKE;
330 }
331
332 static BL::RenderResult begin_render_result(BL::RenderEngine b_engine, int x, int y, int w, int h, const char *layername)
333 {
334         return b_engine.begin_result(x, y, w, h, layername);
335 }
336
337 static void end_render_result(BL::RenderEngine b_engine, BL::RenderResult b_rr, bool cancel, bool do_merge_results)
338 {
339         b_engine.end_result(b_rr, (int)cancel, (int)do_merge_results);
340 }
341
342 void BlenderSession::do_write_update_render_tile(RenderTile& rtile, bool do_update_only)
343 {
344         BufferParams& params = rtile.buffers->params;
345         int x = params.full_x - session->tile_manager.params.full_x;
346         int y = params.full_y - session->tile_manager.params.full_y;
347         int w = params.width;
348         int h = params.height;
349
350         /* get render result */
351         BL::RenderResult b_rr = begin_render_result(b_engine, x, y, w, h, b_rlay_name.c_str());
352
353         /* can happen if the intersected rectangle gives 0 width or height */
354         if (b_rr.ptr.data == NULL) {
355                 return;
356         }
357
358         BL::RenderResult::layers_iterator b_single_rlay;
359         b_rr.layers.begin(b_single_rlay);
360
361         /* layer will be missing if it was disabled in the UI */
362         if(b_single_rlay == b_rr.layers.end())
363                 return;
364
365         BL::RenderLayer b_rlay = *b_single_rlay;
366
367         if (do_update_only) {
368                 /* update only needed */
369
370                 if (rtile.sample != 0) {
371                         /* sample would be zero at initial tile update, which is only needed
372                          * to tag tile form blender side as IN PROGRESS for proper highlight
373                          * no buffers should be sent to blender yet
374                          */
375                         update_render_result(b_rr, b_rlay, rtile);
376                 }
377
378                 end_render_result(b_engine, b_rr, true, true);
379         }
380         else {
381                 /* write result */
382                 write_render_result(b_rr, b_rlay, rtile);
383                 end_render_result(b_engine, b_rr, false, true);
384         }
385 }
386
387 void BlenderSession::write_render_tile(RenderTile& rtile)
388 {
389         do_write_update_render_tile(rtile, false);
390 }
391
392 void BlenderSession::update_render_tile(RenderTile& rtile)
393 {
394         /* use final write for preview renders, otherwise render result wouldn't be
395          * be updated in blender side
396          * would need to be investigated a bit further, but for now shall be fine
397          */
398         if (!b_engine.is_preview())
399                 do_write_update_render_tile(rtile, true);
400         else
401                 do_write_update_render_tile(rtile, false);
402 }
403
404 void BlenderSession::render()
405 {
406         /* set callback to write out render results */
407         session->write_render_tile_cb = function_bind(&BlenderSession::write_render_tile, this, _1);
408         session->update_render_tile_cb = function_bind(&BlenderSession::update_render_tile, this, _1);
409
410         /* get buffer parameters */
411         SessionParams session_params = BlenderSync::get_session_params(b_engine, b_userpref, b_scene, background);
412         BufferParams buffer_params = BlenderSync::get_buffer_params(b_render, b_scene, b_v3d, b_rv3d, scene->camera, width, height);
413
414         /* render each layer */
415         BL::RenderSettings r = b_scene.render();
416         BL::RenderSettings::layers_iterator b_iter;
417         
418         for(r.layers.begin(b_iter); b_iter != r.layers.end(); ++b_iter) {
419                 b_rlay_name = b_iter->name();
420
421                 /* temporary render result to find needed passes */
422                 BL::RenderResult b_rr = begin_render_result(b_engine, 0, 0, 1, 1, b_rlay_name.c_str());
423                 BL::RenderResult::layers_iterator b_single_rlay;
424                 b_rr.layers.begin(b_single_rlay);
425
426                 /* layer will be missing if it was disabled in the UI */
427                 if(b_single_rlay == b_rr.layers.end()) {
428                         end_render_result(b_engine, b_rr, true, false);
429                         continue;
430                 }
431
432                 BL::RenderLayer b_rlay = *b_single_rlay;
433
434                 /* add passes */
435                 vector<Pass> passes;
436                 Pass::add(PASS_COMBINED, passes);
437 #ifdef WITH_CYCLES_DEBUG
438                 Pass::add(PASS_BVH_TRAVERSAL_STEPS, passes);
439 #endif
440
441                 if(session_params.device.advanced_shading) {
442
443                         /* loop over passes */
444                         BL::RenderLayer::passes_iterator b_pass_iter;
445
446                         for(b_rlay.passes.begin(b_pass_iter); b_pass_iter != b_rlay.passes.end(); ++b_pass_iter) {
447                                 BL::RenderPass b_pass(*b_pass_iter);
448                                 PassType pass_type = get_pass_type(b_pass);
449
450                                 if(pass_type == PASS_MOTION && scene->integrator->motion_blur)
451                                         continue;
452                                 if(pass_type != PASS_NONE)
453                                         Pass::add(pass_type, passes);
454                         }
455                 }
456
457                 /* free result without merging */
458                 end_render_result(b_engine, b_rr, true, false);
459
460                 buffer_params.passes = passes;
461                 scene->film->pass_alpha_threshold = b_iter->pass_alpha_threshold();
462                 scene->film->tag_passes_update(scene, passes);
463                 scene->film->tag_update(scene);
464                 scene->integrator->tag_update(scene);
465
466                 /* update scene */
467                 sync->sync_camera(b_render, b_engine.camera_override(), width, height);
468                 sync->sync_data(b_v3d, b_engine.camera_override(), &python_thread_state, b_rlay_name.c_str());
469
470                 /* update number of samples per layer */
471                 int samples = sync->get_layer_samples();
472                 bool bound_samples = sync->get_layer_bound_samples();
473
474                 if(samples != 0 && (!bound_samples || (samples < session_params.samples)))
475                         session->reset(buffer_params, samples);
476                 else
477                         session->reset(buffer_params, session_params.samples);
478
479                 /* render */
480                 session->start();
481                 session->wait();
482
483                 if(session->progress.get_cancel())
484                         break;
485         }
486
487         /* clear callback */
488         session->write_render_tile_cb = NULL;
489         session->update_render_tile_cb = NULL;
490
491         /* free all memory used (host and device), so we wouldn't leave render
492          * engine with extra memory allocated
493          */
494
495         session->device_free();
496
497         delete sync;
498         sync = NULL;
499 }
500
501 static void populate_bake_data(BakeData *data, BL::BakePixel pixel_array, const int num_pixels)
502 {
503         BL::BakePixel bp = pixel_array;
504
505         int i;
506         for(i=0; i < num_pixels; i++) {
507                 data->set(i, bp.primitive_id(), bp.uv(), bp.du_dx(), bp.du_dy(), bp.dv_dx(), bp.dv_dy());
508                 bp = bp.next();
509         }
510 }
511
512 void BlenderSession::bake(BL::Object b_object, const string& pass_type, BL::BakePixel pixel_array, const size_t num_pixels, const int depth, float result[])
513 {
514         ShaderEvalType shader_type = get_shader_type(pass_type);
515         size_t object_index = OBJECT_NONE;
516         int tri_offset = 0;
517
518         /* ensure kernels are loaded before we do any scene updates */
519         session->load_kernels();
520
521         if(session->progress.get_cancel())
522                 return;
523
524         if(shader_type == SHADER_EVAL_UV) {
525                 /* force UV to be available */
526                 Pass::add(PASS_UV, scene->film->passes);
527         }
528
529         if(BakeManager::is_light_pass(shader_type)) {
530                 /* force use_light_pass to be true */
531                 Pass::add(PASS_LIGHT, scene->film->passes);
532         }
533
534         /* create device and update scene */
535         scene->film->tag_update(scene);
536         scene->integrator->tag_update(scene);
537
538         /* update scene */
539         sync->sync_camera(b_render, b_engine.camera_override(), width, height);
540         sync->sync_data(b_v3d, b_engine.camera_override(), &python_thread_state);
541
542         /* get buffer parameters */
543         SessionParams session_params = BlenderSync::get_session_params(b_engine, b_userpref, b_scene, background);
544         BufferParams buffer_params = BlenderSync::get_buffer_params(b_render, b_scene, b_v3d, b_rv3d, scene->camera, width, height);
545
546         scene->bake_manager->set_shader_limit((size_t)b_engine.tile_x(), (size_t)b_engine.tile_y());
547         scene->bake_manager->set_baking(true);
548
549         /* set number of samples */
550         session->tile_manager.set_samples(session_params.samples);
551         session->reset(buffer_params, session_params.samples);
552         session->update_scene();
553
554         /* find object index. todo: is arbitrary - copied from mesh_displace.cpp */
555         for(size_t i = 0; i < scene->objects.size(); i++) {
556                 if(strcmp(scene->objects[i]->name.c_str(), b_object.name().c_str()) == 0) {
557                         object_index = i;
558                         tri_offset = scene->objects[i]->mesh->tri_offset;
559                         break;
560                 }
561         }
562
563         /* when used, non-instanced convention: object = ~object */
564         int object = ~object_index;
565
566         BakeData *bake_data = scene->bake_manager->init(object, tri_offset, num_pixels);
567
568         populate_bake_data(bake_data, pixel_array, num_pixels);
569
570         /* set number of samples */
571         session->tile_manager.set_samples(session_params.samples);
572         session->reset(buffer_params, session_params.samples);
573         session->update_scene();
574
575         session->progress.set_update_callback(function_bind(&BlenderSession::update_bake_progress, this));
576
577         scene->bake_manager->bake(scene->device, &scene->dscene, scene, session->progress, shader_type, bake_data, result);
578
579         /* free all memory used (host and device), so we wouldn't leave render
580          * engine with extra memory allocated
581          */
582
583         session->device_free();
584
585         delete sync;
586         sync = NULL;
587 }
588
589 void BlenderSession::do_write_update_render_result(BL::RenderResult b_rr, BL::RenderLayer b_rlay, RenderTile& rtile, bool do_update_only)
590 {
591         RenderBuffers *buffers = rtile.buffers;
592
593         /* copy data from device */
594         if(!buffers->copy_from_device())
595                 return;
596
597         BufferParams& params = buffers->params;
598         float exposure = scene->film->exposure;
599
600         vector<float> pixels(params.width*params.height*4);
601
602         if (!do_update_only) {
603                 /* copy each pass */
604                 BL::RenderLayer::passes_iterator b_iter;
605
606                 for(b_rlay.passes.begin(b_iter); b_iter != b_rlay.passes.end(); ++b_iter) {
607                         BL::RenderPass b_pass(*b_iter);
608
609                         /* find matching pass type */
610                         PassType pass_type = get_pass_type(b_pass);
611                         int components = b_pass.channels();
612
613                         /* copy pixels */
614                         if(!buffers->get_pass_rect(pass_type, exposure, rtile.sample, components, &pixels[0]))
615                                 memset(&pixels[0], 0, pixels.size()*sizeof(float));
616
617                         b_pass.rect(&pixels[0]);
618                 }
619         }
620
621         /* copy combined pass */
622         if(buffers->get_pass_rect(PASS_COMBINED, exposure, rtile.sample, 4, &pixels[0]))
623                 b_rlay.rect(&pixels[0]);
624
625         /* tag result as updated */
626         b_engine.update_result(b_rr);
627 }
628
629 void BlenderSession::write_render_result(BL::RenderResult b_rr, BL::RenderLayer b_rlay, RenderTile& rtile)
630 {
631         do_write_update_render_result(b_rr, b_rlay, rtile, false);
632 }
633
634 void BlenderSession::update_render_result(BL::RenderResult b_rr, BL::RenderLayer b_rlay, RenderTile& rtile)
635 {
636         do_write_update_render_result(b_rr, b_rlay, rtile, true);
637 }
638
639 void BlenderSession::synchronize()
640 {
641         /* only used for viewport render */
642         if(!b_v3d)
643                 return;
644
645         /* on session/scene parameter changes, we recreate session entirely */
646         SessionParams session_params = BlenderSync::get_session_params(b_engine, b_userpref, b_scene, background);
647         const bool is_cpu = session_params.device.type == DEVICE_CPU;
648         SceneParams scene_params = BlenderSync::get_scene_params(b_scene, background, is_cpu);
649         bool session_pause = BlenderSync::get_session_pause(b_scene, background);
650
651         if(session->params.modified(session_params) ||
652            scene->params.modified(scene_params))
653         {
654                 free_session();
655                 create_session();
656                 session->start();
657                 return;
658         }
659
660         /* increase samples, but never decrease */
661         session->set_samples(session_params.samples);
662         session->set_pause(session_pause);
663
664         /* copy recalc flags, outside of mutex so we can decide to do the real
665          * synchronization at a later time to not block on running updates */
666         sync->sync_recalc();
667
668         /* don't do synchronization if on pause */
669         if(session_pause) {
670                 tag_update();
671                 return;
672         }
673
674         /* try to acquire mutex. if we don't want to or can't, come back later */
675         if(!session->ready_to_reset() || !session->scene->mutex.try_lock()) {
676                 tag_update();
677                 return;
678         }
679
680         /* data and camera synchronize */
681         sync->sync_data(b_v3d, b_engine.camera_override(), &python_thread_state);
682
683         if(b_rv3d)
684                 sync->sync_view(b_v3d, b_rv3d, width, height);
685         else
686                 sync->sync_camera(b_render, b_engine.camera_override(), width, height);
687
688         /* unlock */
689         session->scene->mutex.unlock();
690
691         /* reset if needed */
692         if(scene->need_reset()) {
693                 BufferParams buffer_params = BlenderSync::get_buffer_params(b_render, b_scene, b_v3d, b_rv3d, scene->camera, width, height);
694                 session->reset(buffer_params, session_params.samples);
695
696                 /* reset time */
697                 start_resize_time = 0.0;
698         }
699 }
700
701 bool BlenderSession::draw(int w, int h)
702 {
703         /* pause in redraw in case update is not being called due to final render */
704         session->set_pause(BlenderSync::get_session_pause(b_scene, background));
705
706         /* before drawing, we verify camera and viewport size changes, because
707          * we do not get update callbacks for those, we must detect them here */
708         if(session->ready_to_reset()) {
709                 bool reset = false;
710
711                 /* if dimensions changed, reset */
712                 if(width != w || height != h) {
713                         if(start_resize_time == 0.0) {
714                                 /* don't react immediately to resizes to avoid flickery resizing
715                                  * of the viewport, and some window managers changing the window
716                                  * size temporarily on unminimize */
717                                 start_resize_time = time_dt();
718                                 tag_redraw();
719                         }
720                         else if(time_dt() - start_resize_time < 0.2) {
721                                 tag_redraw();
722                         }
723                         else {
724                                 width = w;
725                                 height = h;
726                                 reset = true;
727                         }
728                 }
729
730                 /* try to acquire mutex. if we can't, come back later */
731                 if(!session->scene->mutex.try_lock()) {
732                         tag_update();
733                 }
734                 else {
735                         /* update camera from 3d view */
736
737                         sync->sync_view(b_v3d, b_rv3d, width, height);
738
739                         if(scene->camera->need_update)
740                                 reset = true;
741
742                         session->scene->mutex.unlock();
743                 }
744
745                 /* reset if requested */
746                 if(reset) {
747                         SessionParams session_params = BlenderSync::get_session_params(b_engine, b_userpref, b_scene, background);
748                         BufferParams buffer_params = BlenderSync::get_buffer_params(b_render, b_scene, b_v3d, b_rv3d, scene->camera, width, height);
749                         bool session_pause = BlenderSync::get_session_pause(b_scene, background);
750
751                         if(session_pause == false) {
752                                 session->reset(buffer_params, session_params.samples);
753                                 start_resize_time = 0.0;
754                         }
755                 }
756         }
757         else {
758                 tag_update();
759         }
760
761         /* update status and progress for 3d view draw */
762         update_status_progress();
763
764         /* draw */
765         BufferParams buffer_params = BlenderSync::get_buffer_params(b_render, b_scene, b_v3d, b_rv3d, scene->camera, width, height);
766         DeviceDrawParams draw_params;
767
768         if(session->params.display_buffer_linear) {
769                 draw_params.bind_display_space_shader_cb = function_bind(&BL::RenderEngine::bind_display_space_shader, &b_engine, b_scene);
770                 draw_params.unbind_display_space_shader_cb = function_bind(&BL::RenderEngine::unbind_display_space_shader, &b_engine);
771         }
772
773         return !session->draw(buffer_params, draw_params);
774 }
775
776 void BlenderSession::get_status(string& status, string& substatus)
777 {
778         session->progress.get_status(status, substatus);
779 }
780
781 void BlenderSession::get_progress(float& progress, double& total_time, double& render_time)
782 {
783         double tile_time;
784         int tile, sample, samples_per_tile;
785         int tile_total = session->tile_manager.state.num_tiles;
786         int samples = session->tile_manager.state.sample + 1;
787         int total_samples = session->tile_manager.num_samples;
788
789         session->progress.get_tile(tile, total_time, render_time, tile_time);
790
791         sample = session->progress.get_sample();
792         samples_per_tile = session->tile_manager.num_samples;
793
794         if(background && samples_per_tile && tile_total)
795                 progress = ((float)sample / (float)(tile_total * samples_per_tile));
796         else if(!background && samples > 0 && total_samples != USHRT_MAX)
797                 progress = ((double)samples) / total_samples;
798         else
799                 progress = 0.0;
800 }
801
802 void BlenderSession::update_bake_progress()
803 {
804         float progress;
805         int sample, samples_per_task, parts_total;
806
807         sample = session->progress.get_sample();
808         samples_per_task = scene->bake_manager->num_samples;
809         parts_total = scene->bake_manager->num_parts;
810
811         if(samples_per_task)
812                 progress = ((float)sample / (float)(parts_total * samples_per_task));
813         else
814                 progress = 0.0;
815
816         if(progress != last_progress) {
817                 b_engine.update_progress(progress);
818                 last_progress = progress;
819         }
820 }
821
822 void BlenderSession::update_status_progress()
823 {
824         string timestatus, status, substatus;
825         string scene = "";
826         float progress;
827         double total_time, remaining_time = 0, render_time;
828         char time_str[128];
829         float mem_used = (float)session->stats.mem_used / 1024.0f / 1024.0f;
830         float mem_peak = (float)session->stats.mem_peak / 1024.0f / 1024.0f;
831
832         get_status(status, substatus);
833         get_progress(progress, total_time, render_time);
834
835         if(progress > 0)
836                 remaining_time = (1.0 - (double)progress) * (render_time / (double)progress);
837
838         if(background) {
839                 scene += " | " + b_scene.name();
840                 if(b_rlay_name != "")
841                         scene += ", "  + b_rlay_name;
842         }
843         else {
844                 BLI_timestr(total_time, time_str, sizeof(time_str));
845                 timestatus = "Time:" + string(time_str) + " | ";
846         }
847
848         if(remaining_time > 0) {
849                 BLI_timestr(remaining_time, time_str, sizeof(time_str));
850                 timestatus += "Remaining:" + string(time_str) + " | ";
851         }
852
853         timestatus += string_printf("Mem:%.2fM, Peak:%.2fM", (double)mem_used, (double)mem_peak);
854
855         if(status.size() > 0)
856                 status = " | " + status;
857         if(substatus.size() > 0)
858                 status += " | " + substatus;
859
860         if(status != last_status) {
861                 b_engine.update_stats("", (timestatus + scene + status).c_str());
862                 b_engine.update_memory_stats(mem_used, mem_peak);
863                 last_status = status;
864         }
865         if(progress != last_progress) {
866                 b_engine.update_progress(progress);
867                 last_progress = progress;
868         }
869
870         if (session->progress.get_error()) {
871                 string error = session->progress.get_error_message();
872                 if(error != last_error) {
873                         /* TODO(sergey): Currently C++ RNA API doesn't let us to
874                          * use mnemonic name for the variable. Would be nice to
875                          * have this figured out.
876                          *
877                          * For until then, 1 << 5 means RPT_ERROR.
878                          */
879                         b_engine.report(1 << 5, error.c_str());
880                         b_engine.error_set(error.c_str());
881                         last_error = error;
882                 }
883         }
884 }
885
886 void BlenderSession::tag_update()
887 {
888         /* tell blender that we want to get another update callback */
889         b_engine.tag_update();
890 }
891
892 void BlenderSession::tag_redraw()
893 {
894         if(background) {
895                 /* update stats and progress, only for background here because
896                  * in 3d view we do it in draw for thread safety reasons */
897                 update_status_progress();
898
899                 /* offline render, redraw if timeout passed */
900                 if(time_dt() - last_redraw_time > 1.0) {
901                         b_engine.tag_redraw();
902                         last_redraw_time = time_dt();
903                 }
904         }
905         else {
906                 /* tell blender that we want to redraw */
907                 b_engine.tag_redraw();
908         }
909 }
910
911 void BlenderSession::test_cancel()
912 {
913         /* test if we need to cancel rendering */
914         if(background)
915                 if(b_engine.test_break())
916                         session->progress.set_cancel("Cancelled");
917 }
918
919 /* builtin image file name is actually an image datablock name with
920  * absolute sequence frame number concatenated via '@' character
921  *
922  * this function splits frame from builtin name
923  */
924 int BlenderSession::builtin_image_frame(const string &builtin_name)
925 {
926         int last = builtin_name.find_last_of('@');
927         return atoi(builtin_name.substr(last + 1, builtin_name.size() - last - 1).c_str());
928 }
929
930 void BlenderSession::builtin_image_info(const string &builtin_name, void *builtin_data, bool &is_float, int &width, int &height, int &depth, int &channels)
931 {
932         /* empty image */
933         is_float = false;
934         width = 0;
935         height = 0;
936         depth = 0;
937         channels = 0;
938
939         if(!builtin_data)
940                 return;
941
942         /* recover ID pointer */
943         PointerRNA ptr;
944         RNA_id_pointer_create((ID*)builtin_data, &ptr);
945         BL::ID b_id(ptr);
946
947         if(b_id.is_a(&RNA_Image)) {
948                 /* image data */
949                 BL::Image b_image(b_id);
950
951                 is_float = b_image.is_float();
952                 width = b_image.size()[0];
953                 height = b_image.size()[1];
954                 depth = 1;
955                 channels = b_image.channels();
956         }
957         else if(b_id.is_a(&RNA_Object)) {
958                 /* smoke volume data */
959                 BL::Object b_ob(b_id);
960                 BL::SmokeDomainSettings b_domain = object_smoke_domain_find(b_ob);
961
962                 if(!b_domain)
963                         return;
964
965                 if(builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_DENSITY) ||
966                    builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_FLAME))
967                         channels = 1;
968                 else if(builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_COLOR))
969                         channels = 4;
970                 else
971                         return;
972
973                 int3 resolution = get_int3(b_domain.domain_resolution());
974                 int amplify = (b_domain.use_high_resolution())? b_domain.amplify() + 1: 1;
975
976                 width = resolution.x * amplify;
977                 height = resolution.y * amplify;
978                 depth = resolution.z * amplify;
979
980                 is_float = true;
981         }
982 }
983
984 bool BlenderSession::builtin_image_pixels(const string &builtin_name, void *builtin_data, unsigned char *pixels)
985 {
986         if(!builtin_data)
987                 return false;
988
989         int frame = builtin_image_frame(builtin_name);
990
991         PointerRNA ptr;
992         RNA_id_pointer_create((ID*)builtin_data, &ptr);
993         BL::Image b_image(ptr);
994
995         int width = b_image.size()[0];
996         int height = b_image.size()[1];
997         int channels = b_image.channels();
998
999         unsigned char *image_pixels;
1000         image_pixels = image_get_pixels_for_frame(b_image, frame);
1001
1002         if(image_pixels) {
1003                 memcpy(pixels, image_pixels, width * height * channels * sizeof(unsigned char));
1004                 MEM_freeN(image_pixels);
1005         }
1006         else {
1007                 if(channels == 1) {
1008                         memset(pixels, 0, width * height * sizeof(unsigned char));
1009                 }
1010                 else {
1011                         unsigned char *cp = pixels;
1012                         for(int i = 0; i < width * height; i++, cp += channels) {
1013                                 cp[0] = 255;
1014                                 cp[1] = 0;
1015                                 cp[2] = 255;
1016                                 if(channels == 4)
1017                                         cp[3] = 255;
1018                         }
1019                 }
1020         }
1021
1022         /* premultiply, byte images are always straight for blender */
1023         unsigned char *cp = pixels;
1024         for(int i = 0; i < width * height; i++, cp += channels) {
1025                 cp[0] = (cp[0] * cp[3]) >> 8;
1026                 cp[1] = (cp[1] * cp[3]) >> 8;
1027                 cp[2] = (cp[2] * cp[3]) >> 8;
1028         }
1029
1030         return true;
1031 }
1032
1033 bool BlenderSession::builtin_image_float_pixels(const string &builtin_name, void *builtin_data, float *pixels)
1034 {
1035         if(!builtin_data)
1036                 return false;
1037
1038         PointerRNA ptr;
1039         RNA_id_pointer_create((ID*)builtin_data, &ptr);
1040         BL::ID b_id(ptr);
1041
1042         if(b_id.is_a(&RNA_Image)) {
1043                 /* image data */
1044                 BL::Image b_image(b_id);
1045                 int frame = builtin_image_frame(builtin_name);
1046
1047                 int width = b_image.size()[0];
1048                 int height = b_image.size()[1];
1049                 int channels = b_image.channels();
1050
1051                 float *image_pixels;
1052                 image_pixels = image_get_float_pixels_for_frame(b_image, frame);
1053
1054                 if(image_pixels) {
1055                         memcpy(pixels, image_pixels, width * height * channels * sizeof(float));
1056                         MEM_freeN(image_pixels);
1057                 }
1058                 else {
1059                         if(channels == 1) {
1060                                 memset(pixels, 0, width * height * sizeof(float));
1061                         }
1062                         else {
1063                                 float *fp = pixels;
1064                                 for(int i = 0; i < width * height; i++, fp += channels) {
1065                                         fp[0] = 1.0f;
1066                                         fp[1] = 0.0f;
1067                                         fp[2] = 1.0f;
1068                                         if(channels == 4)
1069                                                 fp[3] = 1.0f;
1070                                 }
1071                         }
1072                 }
1073
1074                 return true;
1075         }
1076         else if(b_id.is_a(&RNA_Object)) {
1077                 /* smoke volume data */
1078                 BL::Object b_ob(b_id);
1079                 BL::SmokeDomainSettings b_domain = object_smoke_domain_find(b_ob);
1080
1081                 if(!b_domain)
1082                         return false;
1083
1084                 int3 resolution = get_int3(b_domain.domain_resolution());
1085                 int length, amplify = (b_domain.use_high_resolution())? b_domain.amplify() + 1: 1;
1086
1087                 int width = resolution.x * amplify;
1088                 int height = resolution.y * amplify;
1089                 int depth = resolution.z * amplify;
1090
1091                 if(builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_DENSITY)) {
1092                         SmokeDomainSettings_density_grid_get_length(&b_domain.ptr, &length);
1093
1094                         if(length == width*height*depth) {
1095                                 SmokeDomainSettings_density_grid_get(&b_domain.ptr, pixels);
1096                                 return true;
1097                         }
1098                 }
1099                 else if(builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_FLAME)) {
1100                         /* this is in range 0..1, and interpreted by the OpenGL smoke viewer
1101                          * as 1500..3000 K with the first part faded to zero density */
1102                         SmokeDomainSettings_flame_grid_get_length(&b_domain.ptr, &length);
1103
1104                         if(length == width*height*depth) {
1105                                 SmokeDomainSettings_flame_grid_get(&b_domain.ptr, pixels);
1106                                 return true;
1107                         }
1108                 }
1109                 else if(builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_COLOR)) {
1110                         /* the RGB is "premultiplied" by density for better interpolation results */
1111                         SmokeDomainSettings_color_grid_get_length(&b_domain.ptr, &length);
1112
1113                         if(length == width*height*depth*4) {
1114                                 SmokeDomainSettings_color_grid_get(&b_domain.ptr, pixels);
1115                                 return true;
1116                         }
1117                 }
1118
1119                 fprintf(stderr, "Cycles error: unexpected smoke volume resolution, skipping\n");
1120         }
1121
1122         return false;
1123 }
1124
1125 CCL_NAMESPACE_END
1126