Merging r59182 through r59257 from trunk into soc-2013-depsgraph_mt
[blender-staging.git] / intern / cycles / blender / blender_session.cpp
1 /*
2  * Copyright 2011-2013 Blender Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16
17 #include "background.h"
18 #include "buffers.h"
19 #include "camera.h"
20 #include "device.h"
21 #include "integrator.h"
22 #include "film.h"
23 #include "light.h"
24 #include "scene.h"
25 #include "session.h"
26 #include "shader.h"
27
28 #include "util_color.h"
29 #include "util_foreach.h"
30 #include "util_function.h"
31 #include "util_progress.h"
32 #include "util_time.h"
33
34 #include "blender_sync.h"
35 #include "blender_session.h"
36 #include "blender_util.h"
37
38 CCL_NAMESPACE_BEGIN
39
40 BlenderSession::BlenderSession(BL::RenderEngine b_engine_, BL::UserPreferences b_userpref_,
41         BL::BlendData b_data_, BL::Scene b_scene_)
42 : b_engine(b_engine_), b_userpref(b_userpref_), b_data(b_data_), b_render(b_engine_.render()), b_scene(b_scene_),
43   b_v3d(PointerRNA_NULL), b_rv3d(PointerRNA_NULL)
44 {
45         /* offline render */
46
47         width = render_resolution_x(b_render);
48         height = render_resolution_y(b_render);
49
50         background = true;
51         last_redraw_time = 0.0;
52         start_resize_time = 0.0;
53
54         create_session();
55 }
56
57 BlenderSession::BlenderSession(BL::RenderEngine b_engine_, BL::UserPreferences b_userpref_,
58         BL::BlendData b_data_, BL::Scene b_scene_,
59         BL::SpaceView3D b_v3d_, BL::RegionView3D b_rv3d_, int width_, int height_)
60 : b_engine(b_engine_), b_userpref(b_userpref_), b_data(b_data_), b_render(b_scene_.render()), b_scene(b_scene_),
61   b_v3d(b_v3d_), b_rv3d(b_rv3d_)
62 {
63         /* 3d view render */
64
65         width = width_;
66         height = height_;
67         background = false;
68         last_redraw_time = 0.0;
69         start_resize_time = 0.0;
70
71         create_session();
72         session->start();
73 }
74
75 BlenderSession::~BlenderSession()
76 {
77         free_session();
78 }
79
80 void BlenderSession::create_session()
81 {
82         SceneParams scene_params = BlenderSync::get_scene_params(b_scene, background);
83         SessionParams session_params = BlenderSync::get_session_params(b_engine, b_userpref, b_scene, background);
84
85         /* reset status/progress */
86         last_status = "";
87         last_progress = -1.0f;
88         start_resize_time = 0.0;
89
90         /* create scene */
91         scene = new Scene(scene_params, session_params.device);
92
93         /* create session */
94         session = new Session(session_params);
95         session->scene = scene;
96         session->progress.set_update_callback(function_bind(&BlenderSession::tag_redraw, this));
97         session->progress.set_cancel_callback(function_bind(&BlenderSession::test_cancel, this));
98         session->set_pause(BlenderSync::get_session_pause(b_scene, background));
99
100         /* create sync */
101         sync = new BlenderSync(b_engine, b_data, b_scene, scene, !background, session->progress, session_params.device.type == DEVICE_CPU);
102
103         if(b_v3d) {
104                 /* full data sync */
105                 sync->sync_data(b_v3d, b_engine.camera_override());
106                 sync->sync_view(b_v3d, b_rv3d, width, height);
107         }
108         else {
109                 /* for final render we will do full data sync per render layer, only
110                  * do some basic syncing here, no objects or materials for speed */
111                 sync->sync_render_layers(b_v3d, NULL);
112                 sync->sync_integrator();
113                 sync->sync_camera(b_render, b_engine.camera_override(), width, height);
114         }
115
116         /* set buffer parameters */
117         BufferParams buffer_params = BlenderSync::get_buffer_params(b_render, b_scene, b_v3d, b_rv3d, scene->camera, width, height);
118         session->reset(buffer_params, session_params.samples);
119
120         b_engine.use_highlight_tiles(session_params.progressive_refine == false);
121
122         /* setup callbacks for builtin image support */
123         scene->image_manager->builtin_image_info_cb = function_bind(&BlenderSession::builtin_image_info, this, _1, _2, _3, _4, _5, _6);
124         scene->image_manager->builtin_image_pixels_cb = function_bind(&BlenderSession::builtin_image_pixels, this, _1, _2, _3);
125         scene->image_manager->builtin_image_float_pixels_cb = function_bind(&BlenderSession::builtin_image_float_pixels, this, _1, _2, _3);
126 }
127
128 void BlenderSession::reset_session(BL::BlendData b_data_, BL::Scene b_scene_)
129 {
130         b_data = b_data_;
131         b_render = b_engine.render();
132         b_scene = b_scene_;
133
134         SceneParams scene_params = BlenderSync::get_scene_params(b_scene, background);
135         SessionParams session_params = BlenderSync::get_session_params(b_engine, b_userpref, b_scene, background);
136
137         width = render_resolution_x(b_render);
138         height = render_resolution_y(b_render);
139
140         if(scene->params.modified(scene_params) ||
141            session->params.modified(session_params) ||
142            !scene_params.persistent_data)
143         {
144                 /* if scene or session parameters changed, it's easier to simply re-create
145                  * them rather than trying to distinguish which settings need to be updated
146                  */
147
148                 delete session;
149
150                 create_session();
151
152                 return;
153         }
154
155         session->progress.reset();
156         scene->reset();
157
158         session->tile_manager.set_tile_order(session_params.tile_order);
159
160         /* peak memory usage should show current render peak, not peak for all renders
161          * made by this render session
162          */
163         session->stats.mem_peak = session->stats.mem_used;
164
165         /* sync object should be re-created */
166         sync = new BlenderSync(b_engine, b_data, b_scene, scene, !background, session->progress, session_params.device.type == DEVICE_CPU);
167
168         if(b_rv3d) {
169                 sync->sync_data(b_v3d, b_engine.camera_override());
170                 sync->sync_camera(b_render, b_engine.camera_override(), width, height);
171         }
172
173         BufferParams buffer_params = BlenderSync::get_buffer_params(b_render, b_scene, PointerRNA_NULL, PointerRNA_NULL, scene->camera, width, height);
174         session->reset(buffer_params, session_params.samples);
175
176         b_engine.use_highlight_tiles(session_params.progressive_refine == false);
177
178         /* reset time */
179         start_resize_time = 0.0;
180 }
181
182 void BlenderSession::free_session()
183 {
184         if(sync)
185                 delete sync;
186
187         delete session;
188 }
189
190 static PassType get_pass_type(BL::RenderPass b_pass)
191 {
192         switch(b_pass.type()) {
193                 case BL::RenderPass::type_COMBINED:
194                         return PASS_COMBINED;
195
196                 case BL::RenderPass::type_Z:
197                         return PASS_DEPTH;
198                 case BL::RenderPass::type_MIST:
199                         return PASS_MIST;
200                 case BL::RenderPass::type_NORMAL:
201                         return PASS_NORMAL;
202                 case BL::RenderPass::type_OBJECT_INDEX:
203                         return PASS_OBJECT_ID;
204                 case BL::RenderPass::type_UV:
205                         return PASS_UV;
206                 case BL::RenderPass::type_VECTOR:
207                         return PASS_MOTION;
208                 case BL::RenderPass::type_MATERIAL_INDEX:
209                         return PASS_MATERIAL_ID;
210
211                 case BL::RenderPass::type_DIFFUSE_DIRECT:
212                         return PASS_DIFFUSE_DIRECT;
213                 case BL::RenderPass::type_GLOSSY_DIRECT:
214                         return PASS_GLOSSY_DIRECT;
215                 case BL::RenderPass::type_TRANSMISSION_DIRECT:
216                         return PASS_TRANSMISSION_DIRECT;
217                 case BL::RenderPass::type_SUBSURFACE_DIRECT:
218                         return PASS_SUBSURFACE_DIRECT;
219
220                 case BL::RenderPass::type_DIFFUSE_INDIRECT:
221                         return PASS_DIFFUSE_INDIRECT;
222                 case BL::RenderPass::type_GLOSSY_INDIRECT:
223                         return PASS_GLOSSY_INDIRECT;
224                 case BL::RenderPass::type_TRANSMISSION_INDIRECT:
225                         return PASS_TRANSMISSION_INDIRECT;
226                 case BL::RenderPass::type_SUBSURFACE_INDIRECT:
227                         return PASS_SUBSURFACE_INDIRECT;
228
229                 case BL::RenderPass::type_DIFFUSE_COLOR:
230                         return PASS_DIFFUSE_COLOR;
231                 case BL::RenderPass::type_GLOSSY_COLOR:
232                         return PASS_GLOSSY_COLOR;
233                 case BL::RenderPass::type_TRANSMISSION_COLOR:
234                         return PASS_TRANSMISSION_COLOR;
235                 case BL::RenderPass::type_SUBSURFACE_COLOR:
236                         return PASS_SUBSURFACE_COLOR;
237
238                 case BL::RenderPass::type_EMIT:
239                         return PASS_EMISSION;
240                 case BL::RenderPass::type_ENVIRONMENT:
241                         return PASS_BACKGROUND;
242                 case BL::RenderPass::type_AO:
243                         return PASS_AO;
244                 case BL::RenderPass::type_SHADOW:
245                         return PASS_SHADOW;
246
247                 case BL::RenderPass::type_DIFFUSE:
248                 case BL::RenderPass::type_COLOR:
249                 case BL::RenderPass::type_REFRACTION:
250                 case BL::RenderPass::type_SPECULAR:
251                 case BL::RenderPass::type_REFLECTION:
252                         return PASS_NONE;
253         }
254         
255         return PASS_NONE;
256 }
257
258 static BL::RenderResult begin_render_result(BL::RenderEngine b_engine, int x, int y, int w, int h, const char *layername)
259 {
260         return b_engine.begin_result(x, y, w, h, layername);
261 }
262
263 static void end_render_result(BL::RenderEngine b_engine, BL::RenderResult b_rr, bool cancel = false)
264 {
265         b_engine.end_result(b_rr, (int)cancel);
266 }
267
268 void BlenderSession::do_write_update_render_tile(RenderTile& rtile, bool do_update_only)
269 {
270         BufferParams& params = rtile.buffers->params;
271         int x = params.full_x - session->tile_manager.params.full_x;
272         int y = params.full_y - session->tile_manager.params.full_y;
273         int w = params.width;
274         int h = params.height;
275
276         /* get render result */
277         BL::RenderResult b_rr = begin_render_result(b_engine, x, y, w, h, b_rlay_name.c_str());
278
279         /* can happen if the intersected rectangle gives 0 width or height */
280         if (b_rr.ptr.data == NULL) {
281                 return;
282         }
283
284         BL::RenderResult::layers_iterator b_single_rlay;
285         b_rr.layers.begin(b_single_rlay);
286
287         /* layer will be missing if it was disabled in the UI */
288         if(b_single_rlay == b_rr.layers.end())
289                 return;
290
291         BL::RenderLayer b_rlay = *b_single_rlay;
292
293         if (do_update_only) {
294                 /* update only needed */
295
296                 if (rtile.sample != 0) {
297                         /* sample would be zero at initial tile update, which is only needed
298                          * to tag tile form blender side as IN PROGRESS for proper highlight
299                          * no buffers should be sent to blender yet
300                          */
301                         update_render_result(b_rr, b_rlay, rtile);
302                 }
303
304                 end_render_result(b_engine, b_rr, true);
305         }
306         else {
307                 /* write result */
308                 write_render_result(b_rr, b_rlay, rtile);
309                 end_render_result(b_engine, b_rr);
310         }
311 }
312
313 void BlenderSession::write_render_tile(RenderTile& rtile)
314 {
315         do_write_update_render_tile(rtile, false);
316 }
317
318 void BlenderSession::update_render_tile(RenderTile& rtile)
319 {
320         /* use final write for preview renders, otherwise render result wouldn't be
321          * be updated in blender side
322          * would need to be investigated a bit further, but for now shall be fine
323          */
324         if (!b_engine.is_preview())
325                 do_write_update_render_tile(rtile, true);
326         else
327                 do_write_update_render_tile(rtile, false);
328 }
329
330 void BlenderSession::render()
331 {
332         /* set callback to write out render results */
333         session->write_render_tile_cb = function_bind(&BlenderSession::write_render_tile, this, _1);
334         session->update_render_tile_cb = function_bind(&BlenderSession::update_render_tile, this, _1);
335
336         /* get buffer parameters */
337         SessionParams session_params = BlenderSync::get_session_params(b_engine, b_userpref, b_scene, background);
338         BufferParams buffer_params = BlenderSync::get_buffer_params(b_render, b_scene, b_v3d, b_rv3d, scene->camera, width, height);
339
340         /* render each layer */
341         BL::RenderSettings r = b_scene.render();
342         BL::RenderSettings::layers_iterator b_iter;
343         
344         for(r.layers.begin(b_iter); b_iter != r.layers.end(); ++b_iter) {
345                 b_rlay_name = b_iter->name();
346
347                 /* temporary render result to find needed passes */
348                 BL::RenderResult b_rr = begin_render_result(b_engine, 0, 0, 1, 1, b_rlay_name.c_str());
349                 BL::RenderResult::layers_iterator b_single_rlay;
350                 b_rr.layers.begin(b_single_rlay);
351
352                 /* layer will be missing if it was disabled in the UI */
353                 if(b_single_rlay == b_rr.layers.end()) {
354                         end_render_result(b_engine, b_rr, true);
355                         continue;
356                 }
357
358                 BL::RenderLayer b_rlay = *b_single_rlay;
359
360                 /* add passes */
361                 vector<Pass> passes;
362                 Pass::add(PASS_COMBINED, passes);
363
364                 if(session_params.device.advanced_shading) {
365
366                         /* loop over passes */
367                         BL::RenderLayer::passes_iterator b_pass_iter;
368
369                         for(b_rlay.passes.begin(b_pass_iter); b_pass_iter != b_rlay.passes.end(); ++b_pass_iter) {
370                                 BL::RenderPass b_pass(*b_pass_iter);
371                                 PassType pass_type = get_pass_type(b_pass);
372
373                                 if(pass_type == PASS_MOTION && scene->integrator->motion_blur)
374                                         continue;
375                                 if(pass_type != PASS_NONE)
376                                         Pass::add(pass_type, passes);
377                         }
378                 }
379
380                 /* free result without merging */
381                 end_render_result(b_engine, b_rr, true);
382
383                 buffer_params.passes = passes;
384                 scene->film->tag_passes_update(scene, passes);
385                 scene->film->tag_update(scene);
386                 scene->integrator->tag_update(scene);
387
388                 /* update scene */
389                 sync->sync_camera(b_render, b_engine.camera_override(), width, height);
390                 sync->sync_data(b_v3d, b_engine.camera_override(), b_rlay_name.c_str());
391
392                 /* update number of samples per layer */
393                 int samples = sync->get_layer_samples();
394                 bool bound_samples = sync->get_layer_bound_samples();
395
396                 if(samples != 0 && (!bound_samples || (samples < session_params.samples)))
397                         session->reset(buffer_params, samples);
398                 else
399                         session->reset(buffer_params, session_params.samples);
400
401                 /* render */
402                 session->start();
403                 session->wait();
404
405                 if(session->progress.get_cancel())
406                         break;
407         }
408
409         /* clear callback */
410         session->write_render_tile_cb = NULL;
411         session->update_render_tile_cb = NULL;
412
413         /* free all memory used (host and device), so we wouldn't leave render
414          * engine with extra memory allocated
415          */
416
417         session->device_free();
418
419         delete sync;
420         sync = NULL;
421 }
422
423 void BlenderSession::do_write_update_render_result(BL::RenderResult b_rr, BL::RenderLayer b_rlay, RenderTile& rtile, bool do_update_only)
424 {
425         RenderBuffers *buffers = rtile.buffers;
426
427         /* copy data from device */
428         if(!buffers->copy_from_device())
429                 return;
430
431         BufferParams& params = buffers->params;
432         float exposure = scene->film->exposure;
433
434         vector<float> pixels(params.width*params.height*4);
435
436         if (!do_update_only) {
437                 /* copy each pass */
438                 BL::RenderLayer::passes_iterator b_iter;
439
440                 for(b_rlay.passes.begin(b_iter); b_iter != b_rlay.passes.end(); ++b_iter) {
441                         BL::RenderPass b_pass(*b_iter);
442
443                         /* find matching pass type */
444                         PassType pass_type = get_pass_type(b_pass);
445                         int components = b_pass.channels();
446
447                         /* copy pixels */
448                         if(!buffers->get_pass_rect(pass_type, exposure, rtile.sample, components, &pixels[0]))
449                                 memset(&pixels[0], 0, pixels.size()*sizeof(float));
450
451                         b_pass.rect(&pixels[0]);
452                 }
453         }
454
455         /* copy combined pass */
456         if(buffers->get_pass_rect(PASS_COMBINED, exposure, rtile.sample, 4, &pixels[0]))
457                 b_rlay.rect(&pixels[0]);
458
459         /* tag result as updated */
460         b_engine.update_result(b_rr);
461 }
462
463 void BlenderSession::write_render_result(BL::RenderResult b_rr, BL::RenderLayer b_rlay, RenderTile& rtile)
464 {
465         do_write_update_render_result(b_rr, b_rlay, rtile, false);
466 }
467
468 void BlenderSession::update_render_result(BL::RenderResult b_rr, BL::RenderLayer b_rlay, RenderTile& rtile)
469 {
470         do_write_update_render_result(b_rr, b_rlay, rtile, true);
471 }
472
473 void BlenderSession::synchronize()
474 {
475         /* only used for viewport render */
476         if(!b_v3d)
477                 return;
478
479         /* on session/scene parameter changes, we recreate session entirely */
480         SceneParams scene_params = BlenderSync::get_scene_params(b_scene, background);
481         SessionParams session_params = BlenderSync::get_session_params(b_engine, b_userpref, b_scene, background);
482
483         if(session->params.modified(session_params) ||
484            scene->params.modified(scene_params))
485         {
486                 free_session();
487                 create_session();
488                 session->start();
489                 return;
490         }
491
492         /* increase samples, but never decrease */
493         session->set_samples(session_params.samples);
494         session->set_pause(BlenderSync::get_session_pause(b_scene, background));
495
496         /* copy recalc flags, outside of mutex so we can decide to do the real
497          * synchronization at a later time to not block on running updates */
498         sync->sync_recalc();
499
500         /* try to acquire mutex. if we don't want to or can't, come back later */
501         if(!session->ready_to_reset() || !session->scene->mutex.try_lock()) {
502                 tag_update();
503                 return;
504         }
505
506         /* data and camera synchronize */
507         sync->sync_data(b_v3d, b_engine.camera_override());
508
509         if(b_rv3d)
510                 sync->sync_view(b_v3d, b_rv3d, width, height);
511         else
512                 sync->sync_camera(b_render, b_engine.camera_override(), width, height);
513
514         /* unlock */
515         session->scene->mutex.unlock();
516
517         /* reset if needed */
518         if(scene->need_reset()) {
519                 BufferParams buffer_params = BlenderSync::get_buffer_params(b_render, b_scene, b_v3d, b_rv3d, scene->camera, width, height);
520                 session->reset(buffer_params, session_params.samples);
521
522                 /* reset time */
523                 start_resize_time = 0.0;
524         }
525 }
526
527 bool BlenderSession::draw(int w, int h)
528 {
529         /* pause in redraw in case update is not being called due to final render */
530         session->set_pause(BlenderSync::get_session_pause(b_scene, background));
531
532         /* before drawing, we verify camera and viewport size changes, because
533          * we do not get update callbacks for those, we must detect them here */
534         if(session->ready_to_reset()) {
535                 bool reset = false;
536
537                 /* if dimensions changed, reset */
538                 if(width != w || height != h) {
539                         if(start_resize_time == 0.0) {
540                                 /* don't react immediately to resizes to avoid flickery resizing
541                                  * of the viewport, and some window managers changing the window
542                                  * size temporarily on unminimize */
543                                 start_resize_time = time_dt();
544                                 tag_redraw();
545                         }
546                         else if(time_dt() - start_resize_time < 0.2) {
547                                 tag_redraw();
548                         }
549                         else {
550                                 width = w;
551                                 height = h;
552                                 reset = true;
553                         }
554                 }
555
556                 /* try to acquire mutex. if we can't, come back later */
557                 if(!session->scene->mutex.try_lock()) {
558                         tag_update();
559                 }
560                 else {
561                         /* update camera from 3d view */
562
563                         sync->sync_view(b_v3d, b_rv3d, width, height);
564
565                         if(scene->camera->need_update)
566                                 reset = true;
567
568                         session->scene->mutex.unlock();
569                 }
570
571                 /* reset if requested */
572                 if(reset) {
573                         SessionParams session_params = BlenderSync::get_session_params(b_engine, b_userpref, b_scene, background);
574                         BufferParams buffer_params = BlenderSync::get_buffer_params(b_render, b_scene, b_v3d, b_rv3d, scene->camera, width, height);
575
576                         session->reset(buffer_params, session_params.samples);
577
578                         start_resize_time = 0.0;
579                 }
580         }
581         else {
582                 tag_update();
583         }
584
585         /* update status and progress for 3d view draw */
586         update_status_progress();
587
588         /* draw */
589         BufferParams buffer_params = BlenderSync::get_buffer_params(b_render, b_scene, b_v3d, b_rv3d, scene->camera, width, height);
590
591         return !session->draw(buffer_params);
592 }
593
594 void BlenderSession::get_status(string& status, string& substatus)
595 {
596         session->progress.get_status(status, substatus);
597 }
598
599 void BlenderSession::get_progress(float& progress, double& total_time)
600 {
601         double tile_time;
602         int tile, sample, samples_per_tile;
603         int tile_total = session->tile_manager.state.num_tiles;
604
605         session->progress.get_tile(tile, total_time, tile_time);
606
607         sample = session->progress.get_sample();
608         samples_per_tile = session->tile_manager.num_samples;
609
610         if(samples_per_tile && tile_total)
611                 progress = ((float)sample / (float)(tile_total * samples_per_tile));
612         else
613                 progress = 0.0;
614 }
615
616 void BlenderSession::update_status_progress()
617 {
618         string timestatus, status, substatus;
619         float progress;
620         double total_time;
621         char time_str[128];
622         float mem_used = (float)session->stats.mem_used / 1024.0f / 1024.0f;
623         float mem_peak = (float)session->stats.mem_peak / 1024.0f / 1024.0f;
624
625         get_status(status, substatus);
626         get_progress(progress, total_time);
627
628         timestatus = string_printf("Mem:%.2fM, Peak:%.2fM", mem_used, mem_peak);
629
630         if(background) {
631                 timestatus += " | " + b_scene.name();
632                 if(b_rlay_name != "")
633                         timestatus += ", "  + b_rlay_name;
634         }
635         else {
636                 timestatus += " | ";
637
638                 BLI_timestr(total_time, time_str, sizeof(time_str));
639                 timestatus += "Time:" + string(time_str);
640         }
641
642         if(status.size() > 0)
643                 status = " | " + status;
644         if(substatus.size() > 0)
645                 status += " | " + substatus;
646
647         if(status != last_status) {
648                 b_engine.update_stats("", (timestatus + status).c_str());
649                 b_engine.update_memory_stats(mem_used, mem_peak);
650                 last_status = status;
651         }
652         if(progress != last_progress) {
653                 b_engine.update_progress(progress);
654                 last_progress = progress;
655         }
656 }
657
658 void BlenderSession::tag_update()
659 {
660         /* tell blender that we want to get another update callback */
661         b_engine.tag_update();
662 }
663
664 void BlenderSession::tag_redraw()
665 {
666         if(background) {
667                 /* update stats and progress, only for background here because
668                  * in 3d view we do it in draw for thread safety reasons */
669                 update_status_progress();
670
671                 /* offline render, redraw if timeout passed */
672                 if(time_dt() - last_redraw_time > 1.0) {
673                         b_engine.tag_redraw();
674                         last_redraw_time = time_dt();
675                 }
676         }
677         else {
678                 /* tell blender that we want to redraw */
679                 b_engine.tag_redraw();
680         }
681 }
682
683 void BlenderSession::test_cancel()
684 {
685         /* test if we need to cancel rendering */
686         if(background)
687                 if(b_engine.test_break())
688                         session->progress.set_cancel("Cancelled");
689 }
690
691 /* builtin image file name is actually an image datablock name with
692  * absolute sequence frame number concatenated via '@' character
693  *
694  * this function splits frame from builtin name
695  */
696 int BlenderSession::builtin_image_frame(const string &builtin_name)
697 {
698         int last = builtin_name.find_last_of('@');
699         return atoi(builtin_name.substr(last + 1, builtin_name.size() - last - 1).c_str());
700 }
701
702 void BlenderSession::builtin_image_info(const string &builtin_name, void *builtin_data, bool &is_float, int &width, int &height, int &channels)
703 {
704         PointerRNA ptr;
705         RNA_id_pointer_create((ID*)builtin_data, &ptr);
706         BL::Image b_image(ptr);
707
708         if(b_image) {
709                 is_float = b_image.is_float();
710                 width = b_image.size()[0];
711                 height = b_image.size()[1];
712                 channels = b_image.channels();
713         }
714         else {
715                 is_float = false;
716                 width = 0;
717                 height = 0;
718                 channels = 0;
719         }
720 }
721
722 bool BlenderSession::builtin_image_pixels(const string &builtin_name, void *builtin_data, unsigned char *pixels)
723 {
724         int frame = builtin_image_frame(builtin_name);
725
726         PointerRNA ptr;
727         RNA_id_pointer_create((ID*)builtin_data, &ptr);
728         BL::Image b_image(ptr);
729
730         if(b_image) {
731                 int width = b_image.size()[0];
732                 int height = b_image.size()[1];
733                 int channels = b_image.channels();
734
735                 unsigned char *image_pixels;
736                 image_pixels = image_get_pixels_for_frame(b_image, frame);
737
738                 if(image_pixels) {
739                         memcpy(pixels, image_pixels, width * height * channels * sizeof(unsigned char));
740                         MEM_freeN(image_pixels);
741                 }
742                 else {
743                         if(channels == 1) {
744                                 memset(pixels, 0, width * height * sizeof(unsigned char));
745                         }
746                         else {
747                                 unsigned char *cp = pixels;
748                                 for(int i = 0; i < width * height; i++, cp += channels) {
749                                         cp[0] = 255;
750                                         cp[1] = 0;
751                                         cp[2] = 255;
752                                         if(channels == 4)
753                                                 cp[3] = 255;
754                                 }
755                         }
756                 }
757
758                 /* premultiply, byte images are always straight for blender */
759                 unsigned char *cp = pixels;
760                 for(int i = 0; i < width * height; i++, cp += channels) {
761                         cp[0] = (cp[0] * cp[3]) >> 8;
762                         cp[1] = (cp[1] * cp[3]) >> 8;
763                         cp[2] = (cp[2] * cp[3]) >> 8;
764                 }
765
766                 return true;
767         }
768
769         return false;
770 }
771
772 bool BlenderSession::builtin_image_float_pixels(const string &builtin_name, void *builtin_data, float *pixels)
773 {
774         int frame = builtin_image_frame(builtin_name);
775
776         PointerRNA ptr;
777         RNA_id_pointer_create((ID*)builtin_data, &ptr);
778         BL::Image b_image(ptr);
779
780         if(b_image) {
781                 int width = b_image.size()[0];
782                 int height = b_image.size()[1];
783                 int channels = b_image.channels();
784
785                 float *image_pixels;
786                 image_pixels = image_get_float_pixels_for_frame(b_image, frame);
787
788                 if(image_pixels) {
789                         memcpy(pixels, image_pixels, width * height * channels * sizeof(float));
790                         MEM_freeN(image_pixels);
791                 }
792                 else {
793                         if(channels == 1) {
794                                 memset(pixels, 0, width * height * sizeof(float));
795                         }
796                         else {
797                                 float *fp = pixels;
798                                 for(int i = 0; i < width * height; i++, fp += channels) {
799                                         fp[0] = 1.0f;
800                                         fp[1] = 0.0f;
801                                         fp[2] = 1.0f;
802                                         if(channels == 4)
803                                                 fp[3] = 1.0f;
804                                 }
805                         }
806                 }
807
808                 return true;
809         }
810
811         return false;
812 }
813
814 CCL_NAMESPACE_END
815