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