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