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