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