Cycles:
[blender-staging.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::BlendData b_data_, BL::Scene b_scene_)
43 : b_engine(b_engine_), b_data(b_data_), b_scene(b_scene_), b_v3d(PointerRNA_NULL), b_rv3d(PointerRNA_NULL)
44 {
45         /* offline render */
46         BL::RenderSettings r = b_scene.render();
47
48         width = (int)(r.resolution_x()*r.resolution_percentage()*0.01f);
49         height = (int)(r.resolution_y()*r.resolution_percentage()*0.01f);
50         background = true;
51         last_redraw_time = 0.0f;
52
53         create_session();
54 }
55
56 BlenderSession::BlenderSession(BL::RenderEngine b_engine_, BL::BlendData b_data_, BL::Scene b_scene_,
57         BL::SpaceView3D b_v3d_, BL::RegionView3D b_rv3d_, int width_, int height_)
58 : b_engine(b_engine_), b_data(b_data_), b_scene(b_scene_), b_v3d(b_v3d_), b_rv3d(b_rv3d_)
59 {
60         /* 3d view render */
61         width = width_;
62         height = height_;
63         background = false;
64         last_redraw_time = 0.0f;
65
66         create_session();
67 }
68
69 BlenderSession::~BlenderSession()
70 {
71         free_session();
72 }
73
74 void BlenderSession::create_session()
75 {
76         SceneParams scene_params = BlenderSync::get_scene_params(b_scene);
77         SessionParams session_params = BlenderSync::get_session_params(b_scene, background);
78
79         /* create scene */
80         scene = new Scene(scene_params);
81
82         /* create sync */
83         sync = new BlenderSync(b_data, b_scene, scene, !background);
84         sync->sync_data(b_v3d);
85
86         if(b_rv3d)
87                 sync->sync_view(b_v3d, b_rv3d, width, height);
88         else
89                 sync->sync_camera(width, height);
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
97         /* start rendering */
98         session->reset(width, height, session_params.passes);
99         session->start();
100 }
101
102 void BlenderSession::free_session()
103 {
104         delete sync;
105         delete session;
106 }
107
108 void BlenderSession::render()
109 {
110         session->wait();
111
112         if(session->progress.get_cancel())
113                 return;
114
115         /* write result */
116         write_render_result();
117 }
118
119 void BlenderSession::write_render_result()
120 {
121         /* get result */
122         RenderBuffers *buffers = session->buffers;
123         float exposure = scene->film->exposure;
124         double total_time, pass_time;
125         int pass;
126         session->progress.get_pass(pass, total_time, pass_time);
127
128         float4 *pixels = buffers->copy_from_device(exposure, pass);
129
130         if(!pixels)
131                 return;
132
133         struct RenderResult *rrp = RE_engine_begin_result((RenderEngine*)b_engine.ptr.data, 0, 0, width, height);
134         PointerRNA rrptr;
135         RNA_pointer_create(NULL, &RNA_RenderResult, rrp, &rrptr);
136         BL::RenderResult rr(rrptr);
137
138         BL::RenderResult::layers_iterator layer;
139         rr.layers.begin(layer);
140         rna_RenderLayer_rect_set(&layer->ptr, (float*)pixels);
141
142         RE_engine_end_result((RenderEngine*)b_engine.ptr.data, rrp);
143
144         delete [] pixels;
145 }
146
147 void BlenderSession::synchronize()
148 {
149         /* on session/scene parameter changes, we recreate session entirely */
150         SceneParams scene_params = BlenderSync::get_scene_params(b_scene);
151         SessionParams session_params = BlenderSync::get_session_params(b_scene, background);
152
153         if(session->params.modified(session_params) ||
154            scene->params.modified(scene_params)) {
155                 free_session();
156                 create_session();
157                 return;
158         }
159
160         /* increase passes, but never decrease */
161         session->set_passes(session_params.passes);
162
163         /* copy recalc flags, outside of mutex so we can decide to do the real
164            synchronization at a later time to not block on running updates */
165         sync->sync_recalc();
166
167         /* try to acquire mutex. if we don't want to or can't, come back later */
168         if(!session->ready_to_reset() || !session->scene->mutex.try_lock()) {
169                 tag_update();
170                 return;
171         }
172
173         /* data and camera synchronize */
174         sync->sync_data(b_v3d);
175
176         if(b_rv3d)
177                 sync->sync_view(b_v3d, b_rv3d, width, height);
178         else
179                 sync->sync_camera(width, height);
180
181         /* reset if needed */
182         if(scene->need_reset())
183                 session->reset(width, height, session_params.passes);
184
185         /* unlock */
186         session->scene->mutex.unlock();
187 }
188
189 bool BlenderSession::draw(int w, int h)
190 {
191         /* before drawing, we verify camera and viewport size changes, because
192            we do not get update callbacks for those, we must detect them here */
193         if(session->ready_to_reset()) {
194                 bool reset = false;
195
196                 /* try to acquire mutex. if we can't, come back later */
197                 if(!session->scene->mutex.try_lock()) {
198                         tag_update();
199                 }
200                 else {
201                         /* update camera from 3d view */
202                         bool need_update = scene->camera->need_update;
203
204                         sync->sync_view(b_v3d, b_rv3d, w, h);
205
206                         if(scene->camera->need_update && !need_update)
207                                 reset = true;
208
209                         session->scene->mutex.unlock();
210                 }
211
212                 /* if dimensions changed, reset */
213                 if(width != w || height != h) {
214                         width = w;
215                         height = h;
216                         reset = true;
217                 }
218
219                 /* reset if requested */
220                 if(reset) {
221                         SessionParams session_params = BlenderSync::get_session_params(b_scene, background);
222                         session->reset(width, height, session_params.passes);
223                 }
224         }
225
226         /* update status and progress for 3d view draw */
227         update_status_progress();
228
229         /* draw */
230         return !session->draw(width, height);
231 }
232
233 void BlenderSession::get_status(string& status, string& substatus)
234 {
235         session->progress.get_status(status, substatus);
236 }
237
238 void BlenderSession::get_progress(float& progress, double& total_time)
239 {
240         double pass_time;
241         int pass;
242
243         session->progress.get_pass(pass, total_time, pass_time);
244         progress = ((float)pass/(float)session->params.passes);
245 }
246
247 void BlenderSession::update_status_progress()
248 {
249         string status, substatus;
250         float progress;
251         double total_time;
252         char time_str[128];
253
254         get_status(status, substatus);
255         get_progress(progress, total_time);
256
257         if(!background) {
258                 BLI_timestr(total_time, time_str);
259                 status = "Time: " + string(time_str) + " | " + status;
260         }
261
262         if(substatus.size() > 0)
263                 status += " | " + substatus;
264
265         RE_engine_update_stats((RenderEngine*)b_engine.ptr.data, "", status.c_str());
266         RE_engine_update_progress((RenderEngine*)b_engine.ptr.data, progress);
267 }
268
269 void BlenderSession::tag_update()
270 {
271         /* tell blender that we want to get another update callback */
272         engine_tag_update((RenderEngine*)b_engine.ptr.data);
273 }
274
275 void BlenderSession::tag_redraw()
276 {
277         if(background) {
278                 /* update stats and progress, only for background here because
279                    in 3d view we do it in draw for thread safety reasons */
280                 update_status_progress();
281
282                 /* offline render, redraw if timeout passed */
283                 if(time_dt() - last_redraw_time > 1.0f) {
284                         write_render_result();
285                         engine_tag_redraw((RenderEngine*)b_engine.ptr.data);
286                         last_redraw_time = time_dt();
287                 }
288         }
289         else {
290                 /* tell blender that we want to redraw */
291                 engine_tag_redraw((RenderEngine*)b_engine.ptr.data);
292         }
293 }
294
295 void BlenderSession::test_cancel()
296 {
297         /* test if we need to cancel rendering */
298         if(background)
299                 if(RE_engine_test_break((RenderEngine*)b_engine.ptr.data))
300                         session->progress.set_cancel("Cancelled");
301 }
302
303 CCL_NAMESPACE_END
304