2 * Copyright 2011, Blender Foundation.
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.
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.
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.
19 #include "background.h"
23 #include "integrator.h"
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"
36 #include "blender_sync.h"
37 #include "blender_session.h"
38 #include "blender_util.h"
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)
49 width = b_engine.resolution_x();
50 height = b_engine.resolution_y();
53 last_redraw_time = 0.0f;
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_)
68 last_redraw_time = 0.0f;
74 BlenderSession::~BlenderSession()
79 void BlenderSession::create_session()
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);
84 /* reset status/progress */
86 last_progress = -1.0f;
89 scene = new Scene(scene_params, session_params.device);
92 sync = new BlenderSync(b_engine, b_data, b_scene, scene, !background);
93 sync->sync_data(b_v3d, b_engine.camera_override());
96 sync->sync_view(b_v3d, b_rv3d, width, height);
98 sync->sync_camera(b_engine.camera_override(), width, height);
101 session = new Session(session_params);
102 session->scene = scene;
103 session->progress.set_update_callback(function_bind(&BlenderSession::tag_redraw, this));
104 session->progress.set_cancel_callback(function_bind(&BlenderSession::test_cancel, this));
105 session->set_pause(BlenderSync::get_session_pause(b_scene, background));
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);
112 void BlenderSession::free_session()
118 static PassType get_pass_type(BL::RenderPass b_pass)
120 switch(b_pass.type()) {
121 case BL::RenderPass::type_COMBINED:
122 return PASS_COMBINED;
124 case BL::RenderPass::type_Z:
126 case BL::RenderPass::type_NORMAL:
128 case BL::RenderPass::type_OBJECT_INDEX:
129 return PASS_OBJECT_ID;
130 case BL::RenderPass::type_UV:
132 case BL::RenderPass::type_VECTOR:
134 case BL::RenderPass::type_MATERIAL_INDEX:
135 return PASS_MATERIAL_ID;
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;
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;
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;
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:
164 case BL::RenderPass::type_SHADOW:
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:
179 static BL::RenderResult begin_render_result(BL::RenderEngine b_engine, int x, int y, int w, int h, const char *layername)
181 RenderResult *rrp = RE_engine_begin_result((RenderEngine*)b_engine.ptr.data, x, y, w, h, layername);
183 RNA_pointer_create(NULL, &RNA_RenderResult, rrp, &rrptr);
184 return BL::RenderResult(rrptr);
187 static void end_render_result(BL::RenderEngine b_engine, BL::RenderResult b_rr, bool cancel = false)
189 RE_engine_end_result((RenderEngine*)b_engine.ptr.data, (RenderResult*)b_rr.ptr.data, (int)cancel);
192 void BlenderSession::do_write_update_render_buffers(RenderBuffers *buffers, bool do_update_only)
194 BufferParams& params = 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;
200 /* get render result */
201 BL::RenderResult b_rr = begin_render_result(b_engine, x, y, w, h, b_rlay_name.c_str());
203 /* can happen if the intersected rectangle gives 0 width or height */
204 if (b_rr.ptr.data == NULL) {
208 BL::RenderResult::layers_iterator b_single_rlay;
209 b_rr.layers.begin(b_single_rlay);
210 BL::RenderLayer b_rlay = *b_single_rlay;
212 if (do_update_only) {
213 /* update only needed */
214 update_render_result(b_rr, b_rlay, buffers);
215 end_render_result(b_engine, b_rr, true);
219 write_render_result(b_rr, b_rlay, buffers);
220 end_render_result(b_engine, b_rr);
224 void BlenderSession::write_render_buffers(RenderBuffers *buffers)
226 do_write_update_render_buffers(buffers, false);
229 void BlenderSession::update_render_buffers(RenderBuffers *buffers)
231 do_write_update_render_buffers(buffers, true);
234 void BlenderSession::render()
236 /* set callback to write out render results */
237 session->write_render_buffers_cb = function_bind(&BlenderSession::write_render_buffers, this, _1);
238 session->update_render_buffers_cb = function_bind(&BlenderSession::update_render_buffers, this, _1);
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);
244 /* render each layer */
245 BL::RenderSettings r = b_scene.render();
246 BL::RenderSettings::layers_iterator b_iter;
248 for(r.layers.begin(b_iter); b_iter != r.layers.end(); ++b_iter) {
249 b_rlay_name = b_iter->name();
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);
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);
262 BL::RenderLayer b_rlay = *b_single_rlay;
266 Pass::add(PASS_COMBINED, passes);
268 if(session_params.device.advanced_shading) {
270 /* loop over passes */
271 BL::RenderLayer::passes_iterator b_pass_iter;
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);
277 if(pass_type == PASS_MOTION && scene->integrator->motion_blur)
279 if(pass_type != PASS_NONE)
280 Pass::add(pass_type, passes);
284 /* free result without merging */
285 end_render_result(b_engine, b_rr, true);
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);
293 sync->sync_data(b_v3d, b_engine.camera_override(), b_rlay_name.c_str());
296 int samples = sync->get_layer_samples();
297 session->reset(buffer_params, (samples == 0)? session_params.samples: samples);
303 if(session->progress.get_cancel())
308 session->write_render_buffers_cb = NULL;
309 session->update_render_buffers_cb = NULL;
312 void BlenderSession::do_write_update_render_result(BL::RenderResult b_rr, BL::RenderLayer b_rlay, RenderBuffers *buffers, bool do_update_only)
314 /* copy data from device */
315 if(!buffers->copy_from_device())
318 BufferParams& params = buffers->params;
319 float exposure = scene->film->exposure;
320 double total_time, sample_time;
323 session->progress.get_sample(sample, total_time, sample_time);
325 vector<float> pixels(params.width*params.height*4);
327 if (!do_update_only) {
329 BL::RenderLayer::passes_iterator b_iter;
331 for(b_rlay.passes.begin(b_iter); b_iter != b_rlay.passes.end(); ++b_iter) {
332 BL::RenderPass b_pass(*b_iter);
334 /* find matching pass type */
335 PassType pass_type = get_pass_type(b_pass);
336 int components = b_pass.channels();
339 if(buffers->get_pass(pass_type, exposure, sample, components, &pixels[0]))
340 rna_RenderPass_rect_set(&b_pass.ptr, &pixels[0]);
344 /* copy combined pass */
345 if(buffers->get_pass(PASS_COMBINED, exposure, sample, 4, &pixels[0]))
346 rna_RenderLayer_rect_set(&b_rlay.ptr, &pixels[0]);
348 /* tag result as updated */
349 RE_engine_update_result((RenderEngine*)b_engine.ptr.data, (RenderResult*)b_rr.ptr.data);
352 void BlenderSession::write_render_result(BL::RenderResult b_rr, BL::RenderLayer b_rlay, RenderBuffers *buffers)
354 do_write_update_render_result(b_rr, b_rlay, buffers, false);
357 void BlenderSession::update_render_result(BL::RenderResult b_rr, BL::RenderLayer b_rlay, RenderBuffers *buffers)
359 do_write_update_render_result(b_rr, b_rlay, buffers, true);
362 void BlenderSession::synchronize()
364 /* on session/scene parameter changes, we recreate session entirely */
365 SceneParams scene_params = BlenderSync::get_scene_params(b_scene, background);
366 SessionParams session_params = BlenderSync::get_session_params(b_engine, b_userpref, b_scene, background);
368 if(session->params.modified(session_params) ||
369 scene->params.modified(scene_params))
377 /* increase samples, but never decrease */
378 session->set_samples(session_params.samples);
379 session->set_pause(BlenderSync::get_session_pause(b_scene, background));
381 /* copy recalc flags, outside of mutex so we can decide to do the real
382 * synchronization at a later time to not block on running updates */
385 /* try to acquire mutex. if we don't want to or can't, come back later */
386 if(!session->ready_to_reset() || !session->scene->mutex.try_lock()) {
391 /* data and camera synchronize */
392 sync->sync_data(b_v3d, b_engine.camera_override());
395 sync->sync_view(b_v3d, b_rv3d, width, height);
397 sync->sync_camera(b_engine.camera_override(), width, height);
400 session->scene->mutex.unlock();
402 /* reset if needed */
403 if(scene->need_reset()) {
404 BufferParams buffer_params = BlenderSync::get_buffer_params(b_scene, scene->camera, width, height);
405 session->reset(buffer_params, session_params.samples);
409 bool BlenderSession::draw(int w, int h)
411 /* before drawing, we verify camera and viewport size changes, because
412 * we do not get update callbacks for those, we must detect them here */
413 if(session->ready_to_reset()) {
416 /* try to acquire mutex. if we can't, come back later */
417 if(!session->scene->mutex.try_lock()) {
421 /* update camera from 3d view */
422 bool need_update = scene->camera->need_update;
424 sync->sync_view(b_v3d, b_rv3d, w, h);
426 if(scene->camera->need_update && !need_update)
429 session->scene->mutex.unlock();
432 /* if dimensions changed, reset */
433 if(width != w || height != h) {
439 /* reset if requested */
441 SessionParams session_params = BlenderSync::get_session_params(b_engine, b_userpref, b_scene, background);
442 BufferParams buffer_params = BlenderSync::get_buffer_params(b_scene, scene->camera, w, h);
444 session->reset(buffer_params, session_params.samples);
448 /* update status and progress for 3d view draw */
449 update_status_progress();
452 BufferParams buffer_params = BlenderSync::get_buffer_params(b_scene, scene->camera, width, height);
454 return !session->draw(buffer_params);
457 void BlenderSession::get_status(string& status, string& substatus)
459 session->progress.get_status(status, substatus);
462 void BlenderSession::get_progress(float& progress, double& total_time)
467 session->progress.get_sample(sample, total_time, sample_time);
468 progress = ((float)sample/(float)session->params.samples);
471 void BlenderSession::update_status_progress()
473 string timestatus, status, substatus;
478 get_status(status, substatus);
479 get_progress(progress, total_time);
481 BLI_timestr(total_time, time_str);
482 timestatus = "Elapsed: " + string(time_str) + " | ";
484 if(substatus.size() > 0)
485 status += " | " + substatus;
487 if(status != last_status) {
488 RE_engine_update_stats((RenderEngine*)b_engine.ptr.data, "", (timestatus + status).c_str());
489 last_status = status;
491 if(progress != last_progress) {
492 RE_engine_update_progress((RenderEngine*)b_engine.ptr.data, progress);
493 last_progress = progress;
497 void BlenderSession::tag_update()
499 /* tell blender that we want to get another update callback */
500 engine_tag_update((RenderEngine*)b_engine.ptr.data);
503 void BlenderSession::tag_redraw()
506 /* update stats and progress, only for background here because
507 * in 3d view we do it in draw for thread safety reasons */
508 update_status_progress();
510 /* offline render, redraw if timeout passed */
511 if(time_dt() - last_redraw_time > 1.0) {
512 engine_tag_redraw((RenderEngine*)b_engine.ptr.data);
513 last_redraw_time = time_dt();
517 /* tell blender that we want to redraw */
518 engine_tag_redraw((RenderEngine*)b_engine.ptr.data);
522 void BlenderSession::test_cancel()
524 /* test if we need to cancel rendering */
526 if(RE_engine_test_break((RenderEngine*)b_engine.ptr.data))
527 session->progress.set_cancel("Cancelled");