Revert r50528: "Performance fix for Cycles: Don't wait in the main UI thread when...
[blender.git] / intern / cycles / blender / blender_session.cpp
1 /*
2  * Copyright 2011, Blender Foundation.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  */
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 "scene.h"
27 #include "session.h"
28 #include "shader.h"
29
30 #include "util_color.h"
31 #include "util_foreach.h"
32 #include "util_function.h"
33 #include "util_progress.h"
34 #include "util_time.h"
35
36 #include "blender_sync.h"
37 #include "blender_session.h"
38 #include "blender_util.h"
39
40 CCL_NAMESPACE_BEGIN
41
42 BlenderSession::BlenderSession(BL::RenderEngine b_engine_, BL::UserPreferences b_userpref_,
43         BL::BlendData b_data_, BL::Scene b_scene_)
44 : b_engine(b_engine_), b_userpref(b_userpref_), b_data(b_data_), b_scene(b_scene_),
45   b_v3d(PointerRNA_NULL), b_rv3d(PointerRNA_NULL)
46 {
47         /* offline render */
48
49         width = b_engine.resolution_x();
50         height = b_engine.resolution_y();
51
52         background = true;
53         last_redraw_time = 0.0f;
54
55         create_session();
56 }
57
58 BlenderSession::BlenderSession(BL::RenderEngine b_engine_, BL::UserPreferences b_userpref_,
59         BL::BlendData b_data_, BL::Scene b_scene_,
60         BL::SpaceView3D b_v3d_, BL::RegionView3D b_rv3d_, int width_, int height_)
61 : b_engine(b_engine_), b_userpref(b_userpref_), b_data(b_data_), b_scene(b_scene_),
62   b_v3d(b_v3d_), b_rv3d(b_rv3d_)
63 {
64         /* 3d view render */
65         width = width_;
66         height = height_;
67         background = false;
68         last_redraw_time = 0.0f;
69
70         create_session();
71         session->start();
72 }
73
74 BlenderSession::~BlenderSession()
75 {
76         free_session();
77 }
78
79 void BlenderSession::create_session()
80 {
81         SceneParams scene_params = BlenderSync::get_scene_params(b_scene, background);
82         SessionParams session_params = BlenderSync::get_session_params(b_engine, b_userpref, b_scene, background);
83
84         /* reset status/progress */
85         last_status = "";
86         last_progress = -1.0f;
87
88         /* create scene */
89         scene = new Scene(scene_params, session_params.device);
90
91         /* create session */
92         session = new Session(session_params);
93         session->scene = scene;
94         session->progress.set_update_callback(function_bind(&BlenderSession::tag_redraw, this));
95         session->progress.set_cancel_callback(function_bind(&BlenderSession::test_cancel, this));
96         session->set_pause(BlenderSync::get_session_pause(b_scene, background));
97
98         /* create sync */
99         sync = new BlenderSync(b_engine, b_data, b_scene, scene, !background, session->progress);
100         sync->sync_data(b_v3d, b_engine.camera_override());
101
102         if(b_rv3d)
103                 sync->sync_view(b_v3d, b_rv3d, width, height);
104         else
105                 sync->sync_camera(b_engine.camera_override(), width, height);
106
107         /* set buffer parameters */
108         BufferParams buffer_params = BlenderSync::get_buffer_params(b_scene, scene->camera, width, height);
109         session->reset(buffer_params, session_params.samples);
110 }
111
112 void BlenderSession::free_session()
113 {
114         delete sync;
115         delete session;
116 }
117
118 static PassType get_pass_type(BL::RenderPass b_pass)
119 {
120         switch(b_pass.type()) {
121                 case BL::RenderPass::type_COMBINED:
122                         return PASS_COMBINED;
123
124                 case BL::RenderPass::type_Z:
125                         return PASS_DEPTH;
126                 case BL::RenderPass::type_NORMAL:
127                         return PASS_NORMAL;
128                 case BL::RenderPass::type_OBJECT_INDEX:
129                         return PASS_OBJECT_ID;
130                 case BL::RenderPass::type_UV:
131                         return PASS_UV;
132                 case BL::RenderPass::type_VECTOR:
133                         return PASS_MOTION;
134                 case BL::RenderPass::type_MATERIAL_INDEX:
135                         return PASS_MATERIAL_ID;
136
137                 case BL::RenderPass::type_DIFFUSE_DIRECT:
138                         return PASS_DIFFUSE_DIRECT;
139                 case BL::RenderPass::type_GLOSSY_DIRECT:
140                         return PASS_GLOSSY_DIRECT;
141                 case BL::RenderPass::type_TRANSMISSION_DIRECT:
142                         return PASS_TRANSMISSION_DIRECT;
143
144                 case BL::RenderPass::type_DIFFUSE_INDIRECT:
145                         return PASS_DIFFUSE_INDIRECT;
146                 case BL::RenderPass::type_GLOSSY_INDIRECT:
147                         return PASS_GLOSSY_INDIRECT;
148                 case BL::RenderPass::type_TRANSMISSION_INDIRECT:
149                         return PASS_TRANSMISSION_INDIRECT;
150
151                 case BL::RenderPass::type_DIFFUSE_COLOR:
152                         return PASS_DIFFUSE_COLOR;
153                 case BL::RenderPass::type_GLOSSY_COLOR:
154                         return PASS_GLOSSY_COLOR;
155                 case BL::RenderPass::type_TRANSMISSION_COLOR:
156                         return PASS_TRANSMISSION_COLOR;
157
158                 case BL::RenderPass::type_EMIT:
159                         return PASS_EMISSION;
160                 case BL::RenderPass::type_ENVIRONMENT:
161                         return PASS_BACKGROUND;
162                 case BL::RenderPass::type_AO:
163                         return PASS_AO;
164                 case BL::RenderPass::type_SHADOW:
165                         return PASS_SHADOW;
166
167                 case BL::RenderPass::type_DIFFUSE:
168                 case BL::RenderPass::type_COLOR:
169                 case BL::RenderPass::type_REFRACTION:
170                 case BL::RenderPass::type_SPECULAR:
171                 case BL::RenderPass::type_REFLECTION:
172                 case BL::RenderPass::type_MIST:
173                         return PASS_NONE;
174         }
175         
176         return PASS_NONE;
177 }
178
179 static BL::RenderResult begin_render_result(BL::RenderEngine b_engine, int x, int y, int w, int h, const char *layername)
180 {
181         RenderResult *rrp = RE_engine_begin_result((RenderEngine*)b_engine.ptr.data, x, y, w, h, layername);
182         PointerRNA rrptr;
183         RNA_pointer_create(NULL, &RNA_RenderResult, rrp, &rrptr);
184         return BL::RenderResult(rrptr);
185 }
186
187 static void end_render_result(BL::RenderEngine b_engine, BL::RenderResult b_rr, bool cancel = false)
188 {
189         RE_engine_end_result((RenderEngine*)b_engine.ptr.data, (RenderResult*)b_rr.ptr.data, (int)cancel);
190 }
191
192 void BlenderSession::do_write_update_render_tile(RenderTile& rtile, bool do_update_only)
193 {
194         BufferParams& params = rtile.buffers->params;
195         int x = params.full_x - session->tile_manager.params.full_x;
196         int y = params.full_y - session->tile_manager.params.full_y;
197         int w = params.width;
198         int h = params.height;
199
200         /* get render result */
201         BL::RenderResult b_rr = begin_render_result(b_engine, x, y, w, h, b_rlay_name.c_str());
202
203         /* can happen if the intersected rectangle gives 0 width or height */
204         if (b_rr.ptr.data == NULL) {
205                 return;
206         }
207
208         BL::RenderResult::layers_iterator b_single_rlay;
209         b_rr.layers.begin(b_single_rlay);
210         BL::RenderLayer b_rlay = *b_single_rlay;
211
212         if (do_update_only) {
213                 /* update only needed */
214                 update_render_result(b_rr, b_rlay, rtile);
215                 end_render_result(b_engine, b_rr, true);
216         }
217         else {
218                 /* write result */
219                 write_render_result(b_rr, b_rlay, rtile);
220                 end_render_result(b_engine, b_rr);
221         }
222 }
223
224 void BlenderSession::write_render_tile(RenderTile& rtile)
225 {
226         do_write_update_render_tile(rtile, false);
227 }
228
229 void BlenderSession::update_render_tile(RenderTile& rtile)
230 {
231         do_write_update_render_tile(rtile, true);
232 }
233
234 void BlenderSession::render()
235 {
236         /* set callback to write out render results */
237         session->write_render_tile_cb = function_bind(&BlenderSession::write_render_tile, this, _1);
238         session->update_render_tile_cb = function_bind(&BlenderSession::update_render_tile, this, _1);
239
240         /* get buffer parameters */
241         SessionParams session_params = BlenderSync::get_session_params(b_engine, b_userpref, b_scene, background);
242         BufferParams buffer_params = BlenderSync::get_buffer_params(b_scene, scene->camera, width, height);
243
244         /* render each layer */
245         BL::RenderSettings r = b_scene.render();
246         BL::RenderSettings::layers_iterator b_iter;
247         
248         for(r.layers.begin(b_iter); b_iter != r.layers.end(); ++b_iter) {
249                 b_rlay_name = b_iter->name();
250
251                 /* temporary render result to find needed passes */
252                 BL::RenderResult b_rr = begin_render_result(b_engine, 0, 0, 1, 1, b_rlay_name.c_str());
253                 BL::RenderResult::layers_iterator b_single_rlay;
254                 b_rr.layers.begin(b_single_rlay);
255
256                 /* layer will be missing if it was disabled in the UI */
257                 if(b_single_rlay == b_rr.layers.end()) {
258                         end_render_result(b_engine, b_rr, true);
259                         continue;
260                 }
261
262                 BL::RenderLayer b_rlay = *b_single_rlay;
263
264                 /* add passes */
265                 vector<Pass> passes;
266                 Pass::add(PASS_COMBINED, passes);
267
268                 if(session_params.device.advanced_shading) {
269
270                         /* loop over passes */
271                         BL::RenderLayer::passes_iterator b_pass_iter;
272
273                         for(b_rlay.passes.begin(b_pass_iter); b_pass_iter != b_rlay.passes.end(); ++b_pass_iter) {
274                                 BL::RenderPass b_pass(*b_pass_iter);
275                                 PassType pass_type = get_pass_type(b_pass);
276
277                                 if(pass_type == PASS_MOTION && scene->integrator->motion_blur)
278                                         continue;
279                                 if(pass_type != PASS_NONE)
280                                         Pass::add(pass_type, passes);
281                         }
282                 }
283
284                 /* free result without merging */
285                 end_render_result(b_engine, b_rr, true);
286
287                 buffer_params.passes = passes;
288                 scene->film->tag_passes_update(scene, passes);
289                 scene->film->tag_update(scene);
290                 scene->integrator->tag_update(scene);
291
292                 /* update scene */
293                 sync->sync_data(b_v3d, b_engine.camera_override(), b_rlay_name.c_str());
294
295                 /* update session */
296                 int samples = sync->get_layer_samples();
297                 session->reset(buffer_params, (samples == 0)? session_params.samples: samples);
298
299                 /* render */
300                 session->start();
301                 session->wait();
302
303                 if(session->progress.get_cancel())
304                         break;
305         }
306
307         /* clear callback */
308         session->write_render_tile_cb = NULL;
309         session->update_render_tile_cb = NULL;
310 }
311
312 void BlenderSession::do_write_update_render_result(BL::RenderResult b_rr, BL::RenderLayer b_rlay, RenderTile& rtile, bool do_update_only)
313 {
314         RenderBuffers *buffers = rtile.buffers;
315
316         /* copy data from device */
317         if(!buffers->copy_from_device())
318                 return;
319
320         BufferParams& params = buffers->params;
321         float exposure = scene->film->exposure;
322
323         vector<float> pixels(params.width*params.height*4);
324
325         if (!do_update_only) {
326                 /* copy each pass */
327                 BL::RenderLayer::passes_iterator b_iter;
328
329                 for(b_rlay.passes.begin(b_iter); b_iter != b_rlay.passes.end(); ++b_iter) {
330                         BL::RenderPass b_pass(*b_iter);
331
332                         /* find matching pass type */
333                         PassType pass_type = get_pass_type(b_pass);
334                         int components = b_pass.channels();
335
336                         /* copy pixels */
337                         if(buffers->get_pass_rect(pass_type, exposure, rtile.sample, components, &pixels[0]))
338                                 rna_RenderPass_rect_set(&b_pass.ptr, &pixels[0]);
339                 }
340         }
341
342         /* copy combined pass */
343         if(buffers->get_pass_rect(PASS_COMBINED, exposure, rtile.sample, 4, &pixels[0]))
344                 rna_RenderLayer_rect_set(&b_rlay.ptr, &pixels[0]);
345
346         /* tag result as updated */
347         RE_engine_update_result((RenderEngine*)b_engine.ptr.data, (RenderResult*)b_rr.ptr.data);
348 }
349
350 void BlenderSession::write_render_result(BL::RenderResult b_rr, BL::RenderLayer b_rlay, RenderTile& rtile)
351 {
352         do_write_update_render_result(b_rr, b_rlay, rtile, false);
353 }
354
355 void BlenderSession::update_render_result(BL::RenderResult b_rr, BL::RenderLayer b_rlay, RenderTile& rtile)
356 {
357         do_write_update_render_result(b_rr, b_rlay, rtile, true);
358 }
359
360 void BlenderSession::synchronize()
361 {
362         /* on session/scene parameter changes, we recreate session entirely */
363         SceneParams scene_params = BlenderSync::get_scene_params(b_scene, background);
364         SessionParams session_params = BlenderSync::get_session_params(b_engine, b_userpref, b_scene, background);
365
366         if(session->params.modified(session_params) ||
367            scene->params.modified(scene_params))
368         {
369                 free_session();
370                 create_session();
371                 session->start();
372                 return;
373         }
374
375         /* increase samples, but never decrease */
376         session->set_samples(session_params.samples);
377         session->set_pause(BlenderSync::get_session_pause(b_scene, background));
378
379         /* copy recalc flags, outside of mutex so we can decide to do the real
380          * synchronization at a later time to not block on running updates */
381         sync->sync_recalc();
382
383         /* try to acquire mutex. if we don't want to or can't, come back later */
384         if(!session->ready_to_reset() || !session->scene->mutex.try_lock()) {
385                 tag_update();
386                 return;
387         }
388
389         /* data and camera synchronize */
390         sync->sync_data(b_v3d, b_engine.camera_override());
391
392         if(b_rv3d)
393                 sync->sync_view(b_v3d, b_rv3d, width, height);
394         else
395                 sync->sync_camera(b_engine.camera_override(), width, height);
396
397         /* unlock */
398         session->scene->mutex.unlock();
399
400         /* reset if needed */
401         if(scene->need_reset()) {
402                 BufferParams buffer_params = BlenderSync::get_buffer_params(b_scene, scene->camera, width, height);
403                 session->reset(buffer_params, session_params.samples);
404         }
405 }
406
407 bool BlenderSession::draw(int w, int h)
408 {
409         /* before drawing, we verify camera and viewport size changes, because
410          * we do not get update callbacks for those, we must detect them here */
411         if(session->ready_to_reset()) {
412                 bool reset = false;
413
414                 /* try to acquire mutex. if we can't, come back later */
415                 if(!session->scene->mutex.try_lock()) {
416                         tag_update();
417                 }
418                 else {
419                         /* update camera from 3d view */
420                         bool need_update = scene->camera->need_update;
421
422                         sync->sync_view(b_v3d, b_rv3d, w, h);
423
424                         if(scene->camera->need_update && !need_update)
425                                 reset = true;
426
427                         session->scene->mutex.unlock();
428                 }
429
430                 /* if dimensions changed, reset */
431                 if(width != w || height != h) {
432                         width = w;
433                         height = h;
434                         reset = true;
435                 }
436
437                 /* reset if requested */
438                 if(reset) {
439                         SessionParams session_params = BlenderSync::get_session_params(b_engine, b_userpref, b_scene, background);
440                         BufferParams buffer_params = BlenderSync::get_buffer_params(b_scene, scene->camera, w, h);
441
442                         session->reset(buffer_params, session_params.samples);
443                 }
444         }
445
446         /* update status and progress for 3d view draw */
447         update_status_progress();
448
449         /* draw */
450         BufferParams buffer_params = BlenderSync::get_buffer_params(b_scene, scene->camera, width, height);
451
452         return !session->draw(buffer_params);
453 }
454
455 void BlenderSession::get_status(string& status, string& substatus)
456 {
457         session->progress.get_status(status, substatus);
458 }
459
460 void BlenderSession::get_progress(float& progress, double& total_time)
461 {
462         double tile_time;
463         int tile, sample, samples_per_tile;
464         int tile_total = session->tile_manager.state.num_tiles;
465
466         session->progress.get_tile(tile, total_time, tile_time);
467
468         sample = session->progress.get_sample();
469         samples_per_tile = session->tile_manager.state.num_samples;
470
471         progress = ((float)sample/(float)(tile_total * samples_per_tile));
472 }
473
474 void BlenderSession::update_status_progress()
475 {
476         string timestatus, status, substatus;
477         float progress;
478         double total_time;
479         char time_str[128];
480
481         get_status(status, substatus);
482         get_progress(progress, total_time);
483
484         timestatus = b_scene.name();
485         if(b_rlay_name != "")
486                 timestatus += ", "  + b_rlay_name;
487         timestatus += " | ";
488
489         BLI_timestr(total_time, time_str);
490         timestatus += "Elapsed: " + string(time_str) + " | ";
491
492         if(substatus.size() > 0)
493                 status += " | " + substatus;
494
495         if(status != last_status) {
496                 RE_engine_update_stats((RenderEngine*)b_engine.ptr.data, "", (timestatus + status).c_str());
497                 last_status = status;
498         }
499         if(progress != last_progress) {
500                 RE_engine_update_progress((RenderEngine*)b_engine.ptr.data, progress);
501                 last_progress = progress;
502         }
503 }
504
505 void BlenderSession::tag_update()
506 {
507         /* tell blender that we want to get another update callback */
508         engine_tag_update((RenderEngine*)b_engine.ptr.data);
509 }
510
511 void BlenderSession::tag_redraw()
512 {
513         if(background) {
514                 /* update stats and progress, only for background here because
515                  * in 3d view we do it in draw for thread safety reasons */
516                 update_status_progress();
517
518                 /* offline render, redraw if timeout passed */
519                 if(time_dt() - last_redraw_time > 1.0) {
520                         engine_tag_redraw((RenderEngine*)b_engine.ptr.data);
521                         last_redraw_time = time_dt();
522                 }
523         }
524         else {
525                 /* tell blender that we want to redraw */
526                 engine_tag_redraw((RenderEngine*)b_engine.ptr.data);
527         }
528 }
529
530 void BlenderSession::test_cancel()
531 {
532         /* test if we need to cancel rendering */
533         if(background)
534                 if(RE_engine_test_break((RenderEngine*)b_engine.ptr.data))
535                         session->progress.set_cancel("Cancelled");
536 }
537
538 CCL_NAMESPACE_END
539