Cycles Bake
[blender-staging.git] / intern / cycles / render / 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 <string.h>
18 #include <limits.h>
19
20 #include "buffers.h"
21 #include "camera.h"
22 #include "device.h"
23 #include "integrator.h"
24 #include "scene.h"
25 #include "session.h"
26 #include "bake.h"
27
28 #include "util_foreach.h"
29 #include "util_function.h"
30 #include "util_math.h"
31 #include "util_opengl.h"
32 #include "util_task.h"
33 #include "util_time.h"
34
35 CCL_NAMESPACE_BEGIN
36
37 /* Note about  preserve_tile_device option for tile manager:
38  * progressive refine and viewport rendering does requires tiles to
39  * always be allocated for the same device
40  */
41 Session::Session(const SessionParams& params_)
42 : params(params_),
43   tile_manager(params.progressive, params.samples, params.tile_size, params.start_resolution,
44        params.background == false || params.progressive_refine, params.background, params.tile_order,
45        max(params.device.multi_devices.size(), 1)),
46   stats()
47 {
48         device_use_gl = ((params.device.type != DEVICE_CPU) && !params.background);
49
50         TaskScheduler::init(params.threads);
51
52         device = Device::create(params.device, stats, params.background);
53
54         if(params.background && params.output_path.empty()) {
55                 buffers = NULL;
56                 display = NULL;
57         }
58         else {
59                 buffers = new RenderBuffers(device);
60                 display = new DisplayBuffer(device, params.display_buffer_linear);
61         }
62
63         session_thread = NULL;
64         scene = NULL;
65
66         start_time = 0.0;
67         reset_time = 0.0;
68         preview_time = 0.0;
69         paused_time = 0.0;
70         last_update_time = 0.0;
71
72         delayed_reset.do_reset = false;
73         delayed_reset.samples = 0;
74
75         display_outdated = false;
76         gpu_draw_ready = false;
77         gpu_need_tonemap = false;
78         pause = false;
79         kernels_loaded = false;
80 }
81
82 Session::~Session()
83 {
84         if(session_thread) {
85                 /* wait for session thread to end */
86                 progress.set_cancel("Exiting");
87
88                 gpu_need_tonemap = false;
89                 gpu_need_tonemap_cond.notify_all();
90
91                 {
92                         thread_scoped_lock pause_lock(pause_mutex);
93                         pause = false;
94                 }
95                 pause_cond.notify_all();
96
97                 wait();
98         }
99
100         if(!params.output_path.empty()) {
101                 /* tonemap and write out image if requested */
102                 delete display;
103
104                 display = new DisplayBuffer(device, false);
105                 display->reset(device, buffers->params);
106                 tonemap(params.samples);
107
108                 progress.set_status("Writing Image", params.output_path);
109                 display->write(device, params.output_path);
110         }
111
112         /* clean up */
113         foreach(RenderBuffers *buffers, tile_buffers)
114                 delete buffers;
115
116         delete buffers;
117         delete display;
118         delete scene;
119         delete device;
120
121         TaskScheduler::exit();
122 }
123
124 void Session::start()
125 {
126         session_thread = new thread(function_bind(&Session::run, this));
127 }
128
129 bool Session::ready_to_reset()
130 {
131         double dt = time_dt() - reset_time;
132
133         if(!display_outdated)
134                 return (dt > params.reset_timeout);
135         else
136                 return (dt > params.cancel_timeout);
137 }
138
139 /* GPU Session */
140
141 void Session::reset_gpu(BufferParams& buffer_params, int samples)
142 {
143         thread_scoped_lock pause_lock(pause_mutex);
144
145         /* block for buffer access and reset immediately. we can't do this
146          * in the thread, because we need to allocate an OpenGL buffer, and
147          * that only works in the main thread */
148         thread_scoped_lock display_lock(display_mutex);
149         thread_scoped_lock buffers_lock(buffers_mutex);
150
151         display_outdated = true;
152         reset_time = time_dt();
153
154         reset_(buffer_params, samples);
155
156         gpu_need_tonemap = false;
157         gpu_need_tonemap_cond.notify_all();
158
159         pause_cond.notify_all();
160 }
161
162 bool Session::draw_gpu(BufferParams& buffer_params, DeviceDrawParams& draw_params)
163 {
164         /* block for buffer access */
165         thread_scoped_lock display_lock(display_mutex);
166
167         /* first check we already rendered something */
168         if(gpu_draw_ready) {
169                 /* then verify the buffers have the expected size, so we don't
170                  * draw previous results in a resized window */
171                 if(!buffer_params.modified(display->params)) {
172                         /* for CUDA we need to do tonemapping still, since we can
173                          * only access GL buffers from the main thread */
174                         if(gpu_need_tonemap) {
175                                 thread_scoped_lock buffers_lock(buffers_mutex);
176                                 tonemap(tile_manager.state.sample);
177                                 gpu_need_tonemap = false;
178                                 gpu_need_tonemap_cond.notify_all();
179                         }
180
181                         display->draw(device, draw_params);
182
183                         if(display_outdated && (time_dt() - reset_time) > params.text_timeout)
184                                 return false;
185
186                         return true;
187                 }
188         }
189
190         return false;
191 }
192
193 void Session::run_gpu()
194 {
195         bool tiles_written = false;
196
197         start_time = time_dt();
198         reset_time = time_dt();
199         paused_time = 0.0;
200         last_update_time = time_dt();
201
202         if(!params.background)
203                 progress.set_start_time(start_time + paused_time);
204
205         while(!progress.get_cancel()) {
206                 /* advance to next tile */
207                 bool no_tiles = !tile_manager.next();
208
209                 if(params.background) {
210                         /* if no work left and in background mode, we can stop immediately */
211                         if(no_tiles) {
212                                 progress.set_status("Finished");
213                                 break;
214                         }
215                 }
216                 else {
217                         /* if in interactive mode, and we are either paused or done for now,
218                          * wait for pause condition notify to wake up again */
219                         thread_scoped_lock pause_lock(pause_mutex);
220
221                         if(!pause && !tile_manager.done()) {
222                                 /* reset could have happened after no_tiles was set, before this lock.
223                                  * in this case we shall not wait for pause condition
224                                  */
225                         }
226                         else if(pause || no_tiles) {
227                                 update_status_time(pause, no_tiles);
228
229                                 while(1) {
230                                         double pause_start = time_dt();
231                                         pause_cond.wait(pause_lock);
232                                         paused_time += time_dt() - pause_start;
233
234                                         if(!params.background)
235                                                 progress.set_start_time(start_time + paused_time);
236
237                                         update_status_time(pause, no_tiles);
238                                         progress.set_update();
239
240                                         if(!pause)
241                                                 break;
242                                 }
243                         }
244
245                         if(progress.get_cancel())
246                                 break;
247                 }
248
249                 if(!no_tiles) {
250                         /* update scene */
251                         update_scene();
252
253                         if(!device->error_message().empty())
254                                 progress.set_cancel(device->error_message());
255
256                         if(progress.get_cancel())
257                                 break;
258                 }
259
260                 if(!no_tiles) {
261                         /* buffers mutex is locked entirely while rendering each
262                          * sample, and released/reacquired on each iteration to allow
263                          * reset and draw in between */
264                         thread_scoped_lock buffers_lock(buffers_mutex);
265
266                         /* update status and timing */
267                         update_status_time();
268
269                         /* path trace */
270                         path_trace();
271
272                         device->task_wait();
273
274                         if(!device->error_message().empty())
275                                 progress.set_cancel(device->error_message());
276
277                         /* update status and timing */
278                         update_status_time();
279
280                         gpu_need_tonemap = true;
281                         gpu_draw_ready = true;
282                         progress.set_update();
283
284                         /* wait for tonemap */
285                         if(!params.background) {
286                                 while(gpu_need_tonemap) {
287                                         if(progress.get_cancel())
288                                                 break;
289
290                                         gpu_need_tonemap_cond.wait(buffers_lock);
291                                 }
292                         }
293
294                         if(!device->error_message().empty())
295                                 progress.set_cancel(device->error_message());
296
297                         tiles_written = update_progressive_refine(progress.get_cancel());
298
299                         if(progress.get_cancel())
300                                 break;
301                 }
302         }
303
304         if(!tiles_written)
305                 update_progressive_refine(true);
306 }
307
308 /* CPU Session */
309
310 void Session::reset_cpu(BufferParams& buffer_params, int samples)
311 {
312         thread_scoped_lock reset_lock(delayed_reset.mutex);
313         thread_scoped_lock pause_lock(pause_mutex);
314
315         display_outdated = true;
316         reset_time = time_dt();
317
318         delayed_reset.params = buffer_params;
319         delayed_reset.samples = samples;
320         delayed_reset.do_reset = true;
321         device->task_cancel();
322
323         pause_cond.notify_all();
324 }
325
326 bool Session::draw_cpu(BufferParams& buffer_params, DeviceDrawParams& draw_params)
327 {
328         thread_scoped_lock display_lock(display_mutex);
329
330         /* first check we already rendered something */
331         if(display->draw_ready()) {
332                 /* then verify the buffers have the expected size, so we don't
333                  * draw previous results in a resized window */
334                 if(!buffer_params.modified(display->params)) {
335                         display->draw(device, draw_params);
336
337                         if(display_outdated && (time_dt() - reset_time) > params.text_timeout)
338                                 return false;
339
340                         return true;
341                 }
342         }
343
344         return false;
345 }
346
347 bool Session::acquire_tile(Device *tile_device, RenderTile& rtile)
348 {
349         if(progress.get_cancel()) {
350                 if(params.progressive_refine == false) {
351                         /* for progressive refine current sample should be finished for all tiles */
352                         return false;
353                 }
354         }
355
356         thread_scoped_lock tile_lock(tile_mutex);
357
358         /* get next tile from manager */
359         Tile tile;
360         int device_num = device->device_number(tile_device);
361
362         if(!tile_manager.next_tile(tile, device_num))
363                 return false;
364         
365         /* fill render tile */
366         rtile.x = tile_manager.state.buffer.full_x + tile.x;
367         rtile.y = tile_manager.state.buffer.full_y + tile.y;
368         rtile.w = tile.w;
369         rtile.h = tile.h;
370         rtile.start_sample = tile_manager.state.sample;
371         rtile.num_samples = tile_manager.state.num_samples;
372         rtile.resolution = tile_manager.state.resolution_divider;
373
374         tile_lock.unlock();
375
376         /* in case of a permanent buffer, return it, otherwise we will allocate
377          * a new temporary buffer */
378         if(!(params.background && params.output_path.empty())) {
379                 tile_manager.state.buffer.get_offset_stride(rtile.offset, rtile.stride);
380
381                 rtile.buffer = buffers->buffer.device_pointer;
382                 rtile.rng_state = buffers->rng_state.device_pointer;
383                 rtile.buffers = buffers;
384
385                 device->map_tile(tile_device, rtile);
386
387                 return true;
388         }
389
390         /* fill buffer parameters */
391         BufferParams buffer_params = tile_manager.params;
392         buffer_params.full_x = rtile.x;
393         buffer_params.full_y = rtile.y;
394         buffer_params.width = rtile.w;
395         buffer_params.height = rtile.h;
396
397         buffer_params.get_offset_stride(rtile.offset, rtile.stride);
398
399         RenderBuffers *tilebuffers;
400
401         /* allocate buffers */
402         if(params.progressive_refine) {
403                 tile_lock.lock();
404
405                 if(tile_buffers.size() == 0)
406                         tile_buffers.resize(tile_manager.state.num_tiles, NULL);
407
408                 tilebuffers = tile_buffers[tile.index];
409                 if(tilebuffers == NULL) {
410                         tilebuffers = new RenderBuffers(tile_device);
411                         tile_buffers[tile.index] = tilebuffers;
412
413                         tilebuffers->reset(tile_device, buffer_params);
414                 }
415
416                 tile_lock.unlock();
417         }
418         else {
419                 tilebuffers = new RenderBuffers(tile_device);
420
421                 tilebuffers->reset(tile_device, buffer_params);
422         }
423
424         rtile.buffer = tilebuffers->buffer.device_pointer;
425         rtile.rng_state = tilebuffers->rng_state.device_pointer;
426         rtile.buffers = tilebuffers;
427
428         /* this will tag tile as IN PROGRESS in blender-side render pipeline,
429          * which is needed to highlight currently rendering tile before first
430          * sample was processed for it
431          */
432         update_tile_sample(rtile);
433
434         return true;
435 }
436
437 void Session::update_tile_sample(RenderTile& rtile)
438 {
439         thread_scoped_lock tile_lock(tile_mutex);
440
441         if(update_render_tile_cb) {
442                 if(params.progressive_refine == false) {
443                         /* todo: optimize this by making it thread safe and removing lock */
444
445                         update_render_tile_cb(rtile);
446                 }
447         }
448
449         update_status_time();
450 }
451
452 void Session::release_tile(RenderTile& rtile)
453 {
454         thread_scoped_lock tile_lock(tile_mutex);
455
456         if(write_render_tile_cb) {
457                 if(params.progressive_refine == false) {
458                         /* todo: optimize this by making it thread safe and removing lock */
459                         write_render_tile_cb(rtile);
460
461                         delete rtile.buffers;
462                 }
463         }
464
465         update_status_time();
466 }
467
468 void Session::run_cpu()
469 {
470         bool tiles_written = false;
471
472         last_update_time = time_dt();
473
474         {
475                 /* reset once to start */
476                 thread_scoped_lock reset_lock(delayed_reset.mutex);
477                 thread_scoped_lock buffers_lock(buffers_mutex);
478                 thread_scoped_lock display_lock(display_mutex);
479
480                 reset_(delayed_reset.params, delayed_reset.samples);
481                 delayed_reset.do_reset = false;
482         }
483
484         while(!progress.get_cancel()) {
485                 /* advance to next tile */
486                 bool no_tiles = !tile_manager.next();
487                 bool need_tonemap = false;
488
489                 if(params.background) {
490                         /* if no work left and in background mode, we can stop immediately */
491                         if(no_tiles) {
492                                 progress.set_status("Finished");
493                                 break;
494                         }
495                 }
496                 else {
497                         /* if in interactive mode, and we are either paused or done for now,
498                          * wait for pause condition notify to wake up again */
499                         thread_scoped_lock pause_lock(pause_mutex);
500
501                         if(!pause && delayed_reset.do_reset) {
502                                 /* reset once to start */
503                                 thread_scoped_lock reset_lock(delayed_reset.mutex);
504                                 thread_scoped_lock buffers_lock(buffers_mutex);
505                                 thread_scoped_lock display_lock(display_mutex);
506
507                                 reset_(delayed_reset.params, delayed_reset.samples);
508                                 delayed_reset.do_reset = false;
509                         }
510                         else if(pause || no_tiles) {
511                                 update_status_time(pause, no_tiles);
512
513                                 while(1) {
514                                         double pause_start = time_dt();
515                                         pause_cond.wait(pause_lock);
516                                         paused_time += time_dt() - pause_start;
517
518                                         if(!params.background)
519                                                 progress.set_start_time(start_time + paused_time);
520
521                                         update_status_time(pause, no_tiles);
522                                         progress.set_update();
523
524                                         if(!pause)
525                                                 break;
526                                 }
527                         }
528
529                         if(progress.get_cancel())
530                                 break;
531                 }
532
533                 if(!no_tiles) {
534                         /* buffers mutex is locked entirely while rendering each
535                          * sample, and released/reacquired on each iteration to allow
536                          * reset and draw in between */
537                         thread_scoped_lock buffers_lock(buffers_mutex);
538
539                         /* update scene */
540                         update_scene();
541
542                         if(!device->error_message().empty())
543                                 progress.set_cancel(device->error_message());
544
545                         if(progress.get_cancel())
546                                 break;
547
548                         /* update status and timing */
549                         update_status_time();
550
551                         /* path trace */
552                         path_trace();
553
554                         /* update status and timing */
555                         update_status_time();
556
557                         if(!params.background)
558                                 need_tonemap = true;
559
560                         if(!device->error_message().empty())
561                                 progress.set_cancel(device->error_message());
562                 }
563
564                 device->task_wait();
565
566                 {
567                         thread_scoped_lock reset_lock(delayed_reset.mutex);
568                         thread_scoped_lock buffers_lock(buffers_mutex);
569                         thread_scoped_lock display_lock(display_mutex);
570
571                         if(delayed_reset.do_reset) {
572                                 /* reset rendering if request from main thread */
573                                 delayed_reset.do_reset = false;
574                                 reset_(delayed_reset.params, delayed_reset.samples);
575                         }
576                         else if(need_tonemap) {
577                                 /* tonemap only if we do not reset, we don't we don't
578                                  * want to show the result of an incomplete sample */
579                                 tonemap(tile_manager.state.sample);
580                         }
581
582                         if(!device->error_message().empty())
583                                 progress.set_cancel(device->error_message());
584
585                         tiles_written = update_progressive_refine(progress.get_cancel());
586                 }
587
588                 progress.set_update();
589         }
590
591         if(!tiles_written)
592                 update_progressive_refine(true);
593 }
594
595 void Session::run()
596 {
597         /* load kernels */
598         if(!kernels_loaded) {
599                 progress.set_status("Loading render kernels (may take a few minutes the first time)");
600
601                 if(!device->load_kernels(params.experimental)) {
602                         string message = device->error_message();
603                         if(message.empty())
604                                 message = "Failed loading render kernel, see console for errors";
605
606                         progress.set_status("Error", message);
607                         progress.set_update();
608                         return;
609                 }
610
611                 kernels_loaded = true;
612         }
613
614         /* session thread loop */
615         progress.set_status("Waiting for render to start");
616
617         /* run */
618         if(!progress.get_cancel()) {
619                 /* reset number of rendered samples */
620                 progress.reset_sample();
621
622                 if(device_use_gl)
623                         run_gpu();
624                 else
625                         run_cpu();
626         }
627
628         /* progress update */
629         if(progress.get_cancel())
630                 progress.set_status("Cancel", progress.get_cancel_message());
631         else
632                 progress.set_update();
633 }
634
635 bool Session::draw(BufferParams& buffer_params, DeviceDrawParams &draw_params)
636 {
637         if(device_use_gl)
638                 return draw_gpu(buffer_params, draw_params);
639         else
640                 return draw_cpu(buffer_params, draw_params);
641 }
642
643 void Session::reset_(BufferParams& buffer_params, int samples)
644 {
645         if(buffers) {
646                 if(buffer_params.modified(buffers->params)) {
647                         gpu_draw_ready = false;
648                         buffers->reset(device, buffer_params);
649                         display->reset(device, buffer_params);
650                 }
651         }
652
653         tile_manager.reset(buffer_params, samples);
654
655         start_time = time_dt();
656         preview_time = 0.0;
657         paused_time = 0.0;
658
659         if(!params.background)
660                 progress.set_start_time(start_time + paused_time);
661 }
662
663 void Session::reset(BufferParams& buffer_params, int samples)
664 {
665         if(device_use_gl)
666                 reset_gpu(buffer_params, samples);
667         else
668                 reset_cpu(buffer_params, samples);
669
670         if(params.progressive_refine) {
671                 thread_scoped_lock buffers_lock(buffers_mutex);
672
673                 foreach(RenderBuffers *buffers, tile_buffers)
674                         delete buffers;
675
676                 tile_buffers.clear();
677         }
678 }
679
680 void Session::set_samples(int samples)
681 {
682         if(samples != params.samples) {
683                 params.samples = samples;
684                 tile_manager.set_samples(samples);
685
686                 {
687                         thread_scoped_lock pause_lock(pause_mutex);
688                 }
689                 pause_cond.notify_all();
690         }
691 }
692
693 void Session::set_pause(bool pause_)
694 {
695         bool notify = false;
696
697         {
698                 thread_scoped_lock pause_lock(pause_mutex);
699
700                 if(pause != pause_) {
701                         pause = pause_;
702                         notify = true;
703                 }
704         }
705
706         if(notify)
707                 pause_cond.notify_all();
708 }
709
710 void Session::wait()
711 {
712         session_thread->join();
713         delete session_thread;
714
715         session_thread = NULL;
716 }
717
718 void Session::update_scene()
719 {
720         thread_scoped_lock scene_lock(scene->mutex);
721
722         /* update camera if dimensions changed for progressive render. the camera
723          * knows nothing about progressive or cropped rendering, it just gets the
724          * image dimensions passed in */
725         Camera *cam = scene->camera;
726         int width = tile_manager.state.buffer.full_width;
727         int height = tile_manager.state.buffer.full_height;
728         int resolution = tile_manager.state.resolution_divider;
729
730         if(width != cam->width || height != cam->height) {
731                 cam->width = width;
732                 cam->height = height;
733                 cam->resolution = resolution;
734                 cam->tag_update();
735         }
736
737         /* number of samples is needed by multi jittered
738          * sampling pattern and by baking */
739         Integrator *integrator = scene->integrator;
740         BakeManager *bake_manager = scene->bake_manager;
741
742         if(integrator->sampling_pattern == SAMPLING_PATTERN_CMJ ||
743            bake_manager->get_baking())
744         {
745                 int aa_samples = tile_manager.num_samples;
746
747                 if(aa_samples != integrator->aa_samples) {
748                         integrator->aa_samples = aa_samples;
749                         integrator->tag_update(scene);
750                 }
751         }
752
753         /* update scene */
754         if(scene->need_update()) {
755                 progress.set_status("Updating Scene");
756                 scene->device_update(device, progress);
757         }
758 }
759
760 void Session::update_status_time(bool show_pause, bool show_done)
761 {
762         int sample = tile_manager.state.sample;
763         int resolution = tile_manager.state.resolution_divider;
764         int num_tiles = tile_manager.state.num_tiles;
765         int tile = tile_manager.state.num_rendered_tiles;
766
767         /* update status */
768         string status, substatus;
769
770         if(!params.progressive) {
771                 bool is_gpu = params.device.type == DEVICE_CUDA || params.device.type == DEVICE_OPENCL;
772                 bool is_multidevice = params.device.multi_devices.size() > 1;
773                 bool is_cpu = params.device.type == DEVICE_CPU;
774
775                 substatus = string_printf("Path Tracing Tile %d/%d", tile, num_tiles);
776
777                 if((is_gpu && !is_multidevice) || (is_cpu && num_tiles == 1)) {
778                         /* when rendering on GPU multithreading happens within single tile, as in
779                          * tiles are handling sequentially and in this case we could display
780                          * currently rendering sample number
781                          * this helps a lot from feedback point of view.
782                          * also display the info on CPU, when using 1 tile only
783                          */
784
785                         int sample = progress.get_sample(), num_samples = tile_manager.num_samples;
786
787                         if(tile > 1) {
788                                 /* sample counter is global for all tiles, subtract samples
789                                  * from already finished tiles to get sample counter for
790                                  * current tile only
791                                  */
792                                 sample -= (tile - 1) * num_samples;
793                         }
794
795                         substatus += string_printf(", Sample %d/%d", sample, num_samples);
796                 }
797         }
798         else if(tile_manager.num_samples == USHRT_MAX)
799                 substatus = string_printf("Path Tracing Sample %d", sample+1);
800         else
801                 substatus = string_printf("Path Tracing Sample %d/%d", sample+1, tile_manager.num_samples);
802         
803         if(show_pause) {
804                 status = "Paused";
805         }
806         else if(show_done) {
807                 status = "Done";
808         }
809         else {
810                 status = substatus;
811                 substatus.clear();
812         }
813
814         progress.set_status(status, substatus);
815
816         /* update timing */
817         if(preview_time == 0.0 && resolution == 1)
818                 preview_time = time_dt();
819         
820         double tile_time = (tile == 0 || sample == 0)? 0.0: (time_dt() - preview_time - paused_time) / sample;
821
822         /* negative can happen when we pause a bit before rendering, can discard that */
823         if(preview_time < 0.0) preview_time = 0.0;
824
825         progress.set_tile(tile, tile_time);
826 }
827
828 void Session::update_progress_sample()
829 {
830         progress.increment_sample();
831 }
832
833 void Session::path_trace()
834 {
835         /* add path trace task */
836         DeviceTask task(DeviceTask::PATH_TRACE);
837         
838         task.acquire_tile = function_bind(&Session::acquire_tile, this, _1, _2);
839         task.release_tile = function_bind(&Session::release_tile, this, _1);
840         task.get_cancel = function_bind(&Progress::get_cancel, &this->progress);
841         task.update_tile_sample = function_bind(&Session::update_tile_sample, this, _1);
842         task.update_progress_sample = function_bind(&Session::update_progress_sample, this);
843         task.need_finish_queue = params.progressive_refine;
844         task.integrator_branched = scene->integrator->method == Integrator::BRANCHED_PATH;
845
846         device->task_add(task);
847 }
848
849 void Session::tonemap(int sample)
850 {
851         /* add tonemap task */
852         DeviceTask task(DeviceTask::FILM_CONVERT);
853
854         task.x = tile_manager.state.buffer.full_x;
855         task.y = tile_manager.state.buffer.full_y;
856         task.w = tile_manager.state.buffer.width;
857         task.h = tile_manager.state.buffer.height;
858         task.rgba_byte = display->rgba_byte.device_pointer;
859         task.rgba_half = display->rgba_half.device_pointer;
860         task.buffer = buffers->buffer.device_pointer;
861         task.sample = sample;
862         tile_manager.state.buffer.get_offset_stride(task.offset, task.stride);
863
864         if(task.w > 0 && task.h > 0) {
865                 device->task_add(task);
866                 device->task_wait();
867
868                 /* set display to new size */
869                 display->draw_set(task.w, task.h);
870         }
871
872         display_outdated = false;
873 }
874
875 bool Session::update_progressive_refine(bool cancel)
876 {
877         int sample = tile_manager.state.sample + 1;
878         bool write = sample == tile_manager.num_samples || cancel;
879
880         double current_time = time_dt();
881
882         if (current_time - last_update_time < 1.0) {
883                 /* if last sample was processed, we need to write buffers anyway  */
884                 if (!write)
885                         return false;
886         }
887
888         if(params.progressive_refine) {
889                 foreach(RenderBuffers *buffers, tile_buffers) {
890                         RenderTile rtile;
891                         rtile.buffers = buffers;
892                         rtile.sample = sample;
893
894                         if(write)
895                                 write_render_tile_cb(rtile);
896                         else
897                                 update_render_tile_cb(rtile);
898                 }
899         }
900
901         last_update_time = current_time;
902
903         return write;
904 }
905
906 void Session::device_free()
907 {
908         scene->device_free();
909
910         foreach(RenderBuffers *buffers, tile_buffers)
911                 delete buffers;
912
913         tile_buffers.clear();
914
915         /* used from background render only, so no need to
916          * re-create render/display buffers here
917          */
918 }
919
920 CCL_NAMESPACE_END