Cycles: Code cleanup, spaces around keywords
[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         progress.set_render_start_time(start_time + paused_time);
203
204         while(!progress.get_cancel()) {
205                 /* advance to next tile */
206                 bool no_tiles = !tile_manager.next();
207
208                 if(params.background) {
209                         /* if no work left and in background mode, we can stop immediately */
210                         if(no_tiles) {
211                                 progress.set_status("Finished");
212                                 break;
213                         }
214                 }
215                 else {
216                         /* if in interactive mode, and we are either paused or done for now,
217                          * wait for pause condition notify to wake up again */
218                         thread_scoped_lock pause_lock(pause_mutex);
219
220                         if(!pause && !tile_manager.done()) {
221                                 /* reset could have happened after no_tiles was set, before this lock.
222                                  * in this case we shall not wait for pause condition
223                                  */
224                         }
225                         else if(pause || no_tiles) {
226                                 update_status_time(pause, no_tiles);
227
228                                 while(1) {
229                                         double pause_start = time_dt();
230                                         pause_cond.wait(pause_lock);
231                                         paused_time += time_dt() - pause_start;
232
233                                         if(!params.background)
234                                                 progress.set_start_time(start_time + paused_time);
235                                         progress.set_render_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_error(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_error(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                                         progress.set_render_start_time(start_time + paused_time);
521
522                                         update_status_time(pause, no_tiles);
523                                         progress.set_update();
524
525                                         if(!pause)
526                                                 break;
527                                 }
528                         }
529
530                         if(progress.get_cancel())
531                                 break;
532                 }
533
534                 if(!no_tiles) {
535                         /* buffers mutex is locked entirely while rendering each
536                          * sample, and released/reacquired on each iteration to allow
537                          * reset and draw in between */
538                         thread_scoped_lock buffers_lock(buffers_mutex);
539
540                         /* update scene */
541                         update_scene();
542
543                         if(!device->error_message().empty())
544                                 progress.set_error(device->error_message());
545
546                         if(progress.get_cancel())
547                                 break;
548
549                         /* update status and timing */
550                         update_status_time();
551
552                         /* path trace */
553                         path_trace();
554
555                         /* update status and timing */
556                         update_status_time();
557
558                         if(!params.background)
559                                 need_tonemap = true;
560
561                         if(!device->error_message().empty())
562                                 progress.set_error(device->error_message());
563                 }
564
565                 device->task_wait();
566
567                 {
568                         thread_scoped_lock reset_lock(delayed_reset.mutex);
569                         thread_scoped_lock buffers_lock(buffers_mutex);
570                         thread_scoped_lock display_lock(display_mutex);
571
572                         if(delayed_reset.do_reset) {
573                                 /* reset rendering if request from main thread */
574                                 delayed_reset.do_reset = false;
575                                 reset_(delayed_reset.params, delayed_reset.samples);
576                         }
577                         else if(need_tonemap) {
578                                 /* tonemap only if we do not reset, we don't we don't
579                                  * want to show the result of an incomplete sample */
580                                 tonemap(tile_manager.state.sample);
581                         }
582
583                         if(!device->error_message().empty())
584                                 progress.set_error(device->error_message());
585
586                         tiles_written = update_progressive_refine(progress.get_cancel());
587                 }
588
589                 progress.set_update();
590         }
591
592         if(!tiles_written)
593                 update_progressive_refine(true);
594 }
595
596 void Session::load_kernels()
597 {
598         thread_scoped_lock scene_lock(scene->mutex);
599
600         if(!kernels_loaded) {
601                 progress.set_status("Loading render kernels (may take a few minutes the first time)");
602
603                 if(!device->load_kernels(params.experimental)) {
604                         string message = device->error_message();
605                         if(message.empty())
606                                 message = "Failed loading render kernel, see console for errors";
607
608                         progress.set_error(message);
609                         progress.set_status("Error", message);
610                         progress.set_update();
611                         return;
612                 }
613
614                 kernels_loaded = true;
615         }
616 }
617
618 void Session::run()
619 {
620         /* load kernels */
621         load_kernels();
622
623         /* session thread loop */
624         progress.set_status("Waiting for render to start");
625
626         /* run */
627         if(!progress.get_cancel()) {
628                 /* reset number of rendered samples */
629                 progress.reset_sample();
630
631                 if(device_use_gl)
632                         run_gpu();
633                 else
634                         run_cpu();
635         }
636
637         /* progress update */
638         if(progress.get_cancel())
639                 progress.set_status("Cancel", progress.get_cancel_message());
640         else
641                 progress.set_update();
642 }
643
644 bool Session::draw(BufferParams& buffer_params, DeviceDrawParams &draw_params)
645 {
646         if(device_use_gl)
647                 return draw_gpu(buffer_params, draw_params);
648         else
649                 return draw_cpu(buffer_params, draw_params);
650 }
651
652 void Session::reset_(BufferParams& buffer_params, int samples)
653 {
654         if(buffers) {
655                 if(buffer_params.modified(buffers->params)) {
656                         gpu_draw_ready = false;
657                         buffers->reset(device, buffer_params);
658                         display->reset(device, buffer_params);
659                 }
660         }
661
662         tile_manager.reset(buffer_params, samples);
663
664         start_time = time_dt();
665         preview_time = 0.0;
666         paused_time = 0.0;
667
668         if(!params.background)
669                 progress.set_start_time(start_time);
670         progress.set_render_start_time(start_time);
671 }
672
673 void Session::reset(BufferParams& buffer_params, int samples)
674 {
675         if(device_use_gl)
676                 reset_gpu(buffer_params, samples);
677         else
678                 reset_cpu(buffer_params, samples);
679
680         if(params.progressive_refine) {
681                 thread_scoped_lock buffers_lock(buffers_mutex);
682
683                 foreach(RenderBuffers *buffers, tile_buffers)
684                         delete buffers;
685
686                 tile_buffers.clear();
687         }
688 }
689
690 void Session::set_samples(int samples)
691 {
692         if(samples != params.samples) {
693                 params.samples = samples;
694                 tile_manager.set_samples(samples);
695
696                 {
697                         thread_scoped_lock pause_lock(pause_mutex);
698                 }
699                 pause_cond.notify_all();
700         }
701 }
702
703 void Session::set_pause(bool pause_)
704 {
705         bool notify = false;
706
707         {
708                 thread_scoped_lock pause_lock(pause_mutex);
709
710                 if(pause != pause_) {
711                         pause = pause_;
712                         notify = true;
713                 }
714         }
715
716         if(notify)
717                 pause_cond.notify_all();
718 }
719
720 void Session::wait()
721 {
722         session_thread->join();
723         delete session_thread;
724
725         session_thread = NULL;
726 }
727
728 void Session::update_scene()
729 {
730         thread_scoped_lock scene_lock(scene->mutex);
731
732         /* update camera if dimensions changed for progressive render. the camera
733          * knows nothing about progressive or cropped rendering, it just gets the
734          * image dimensions passed in */
735         Camera *cam = scene->camera;
736         int width = tile_manager.state.buffer.full_width;
737         int height = tile_manager.state.buffer.full_height;
738         int resolution = tile_manager.state.resolution_divider;
739
740         if(width != cam->width || height != cam->height) {
741                 cam->width = width;
742                 cam->height = height;
743                 cam->resolution = resolution;
744                 cam->tag_update();
745         }
746
747         /* number of samples is needed by multi jittered
748          * sampling pattern and by baking */
749         Integrator *integrator = scene->integrator;
750         BakeManager *bake_manager = scene->bake_manager;
751
752         if(integrator->sampling_pattern == SAMPLING_PATTERN_CMJ ||
753            bake_manager->get_baking())
754         {
755                 int aa_samples = tile_manager.num_samples;
756
757                 if(aa_samples != integrator->aa_samples) {
758                         integrator->aa_samples = aa_samples;
759                         integrator->tag_update(scene);
760                 }
761         }
762
763         /* update scene */
764         if(scene->need_update()) {
765                 progress.set_status("Updating Scene");
766                 scene->device_update(device, progress);
767         }
768 }
769
770 void Session::update_status_time(bool show_pause, bool show_done)
771 {
772         int sample = tile_manager.state.sample;
773         int resolution = tile_manager.state.resolution_divider;
774         int num_tiles = tile_manager.state.num_tiles;
775         int tile = tile_manager.state.num_rendered_tiles;
776
777         /* update status */
778         string status, substatus;
779
780         if(!params.progressive) {
781                 bool is_gpu = params.device.type == DEVICE_CUDA || params.device.type == DEVICE_OPENCL;
782                 bool is_multidevice = params.device.multi_devices.size() > 1;
783                 bool is_cpu = params.device.type == DEVICE_CPU;
784
785                 substatus = string_printf("Path Tracing Tile %d/%d", tile, num_tiles);
786
787                 if((is_gpu && !is_multidevice) || (is_cpu && num_tiles == 1)) {
788                         /* when rendering on GPU multithreading happens within single tile, as in
789                          * tiles are handling sequentially and in this case we could display
790                          * currently rendering sample number
791                          * this helps a lot from feedback point of view.
792                          * also display the info on CPU, when using 1 tile only
793                          */
794
795                         int sample = progress.get_sample(), num_samples = tile_manager.num_samples;
796
797                         if(tile > 1) {
798                                 /* sample counter is global for all tiles, subtract samples
799                                  * from already finished tiles to get sample counter for
800                                  * current tile only
801                                  */
802                                 sample -= (tile - 1) * num_samples;
803                         }
804
805                         substatus += string_printf(", Sample %d/%d", sample, num_samples);
806                 }
807         }
808         else if(tile_manager.num_samples == USHRT_MAX)
809                 substatus = string_printf("Path Tracing Sample %d", sample+1);
810         else
811                 substatus = string_printf("Path Tracing Sample %d/%d", sample+1, tile_manager.num_samples);
812         
813         if(show_pause) {
814                 status = "Paused";
815         }
816         else if(show_done) {
817                 status = "Done";
818         }
819         else {
820                 status = substatus;
821                 substatus.clear();
822         }
823
824         progress.set_status(status, substatus);
825
826         /* update timing */
827         if(preview_time == 0.0 && resolution == 1)
828                 preview_time = time_dt();
829         
830         double tile_time = (tile == 0 || sample == 0)? 0.0: (time_dt() - preview_time - paused_time) / sample;
831
832         /* negative can happen when we pause a bit before rendering, can discard that */
833         if(preview_time < 0.0) preview_time = 0.0;
834
835         progress.set_tile(tile, tile_time);
836 }
837
838 void Session::update_progress_sample()
839 {
840         progress.increment_sample();
841 }
842
843 void Session::path_trace()
844 {
845         /* add path trace task */
846         DeviceTask task(DeviceTask::PATH_TRACE);
847         
848         task.acquire_tile = function_bind(&Session::acquire_tile, this, _1, _2);
849         task.release_tile = function_bind(&Session::release_tile, this, _1);
850         task.get_cancel = function_bind(&Progress::get_cancel, &this->progress);
851         task.update_tile_sample = function_bind(&Session::update_tile_sample, this, _1);
852         task.update_progress_sample = function_bind(&Session::update_progress_sample, this);
853         task.need_finish_queue = params.progressive_refine;
854         task.integrator_branched = scene->integrator->method == Integrator::BRANCHED_PATH;
855
856         device->task_add(task);
857 }
858
859 void Session::tonemap(int sample)
860 {
861         /* add tonemap task */
862         DeviceTask task(DeviceTask::FILM_CONVERT);
863
864         task.x = tile_manager.state.buffer.full_x;
865         task.y = tile_manager.state.buffer.full_y;
866         task.w = tile_manager.state.buffer.width;
867         task.h = tile_manager.state.buffer.height;
868         task.rgba_byte = display->rgba_byte.device_pointer;
869         task.rgba_half = display->rgba_half.device_pointer;
870         task.buffer = buffers->buffer.device_pointer;
871         task.sample = sample;
872         tile_manager.state.buffer.get_offset_stride(task.offset, task.stride);
873
874         if(task.w > 0 && task.h > 0) {
875                 device->task_add(task);
876                 device->task_wait();
877
878                 /* set display to new size */
879                 display->draw_set(task.w, task.h);
880         }
881
882         display_outdated = false;
883 }
884
885 bool Session::update_progressive_refine(bool cancel)
886 {
887         int sample = tile_manager.state.sample + 1;
888         bool write = sample == tile_manager.num_samples || cancel;
889
890         double current_time = time_dt();
891
892         if(current_time - last_update_time < params.progressive_update_timeout) {
893                 /* if last sample was processed, we need to write buffers anyway  */
894                 if(!write)
895                         return false;
896         }
897
898         if(params.progressive_refine) {
899                 foreach(RenderBuffers *buffers, tile_buffers) {
900                         RenderTile rtile;
901                         rtile.buffers = buffers;
902                         rtile.sample = sample;
903
904                         if(write)
905                                 write_render_tile_cb(rtile);
906                         else
907                                 update_render_tile_cb(rtile);
908                 }
909         }
910
911         last_update_time = current_time;
912
913         return write;
914 }
915
916 void Session::device_free()
917 {
918         scene->device_free();
919
920         foreach(RenderBuffers *buffers, tile_buffers)
921                 delete buffers;
922
923         tile_buffers.clear();
924
925         /* used from background render only, so no need to
926          * re-create render/display buffers here
927          */
928 }
929
930 CCL_NAMESPACE_END