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