2
* Copyright 2011, Blender Foundation.
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.
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.
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.
28
#include "util_foreach.h"
29
#include "util_function.h"
30
#include "util_time.h"
34
Session::Session(const SessionParams& params_)
36
tile_manager(params.progressive, params.samples, params.tile_size, params.min_size)
38
device_use_gl = ((params.device.type != DEVICE_CPU) && !params.background);
40
device = Device::create(params.device, params.background, params.threads);
41
buffers = new RenderBuffers(device);
42
display = new DisplayBuffer(device);
44
session_thread = NULL;
53
delayed_reset.do_reset = false;
54
delayed_reset.samples = 0;
56
display_outdated = false;
57
gpu_draw_ready = false;
58
gpu_need_tonemap = false;
60
kernels_loaded = false;
66
progress.set_cancel("Exiting");
68
gpu_need_tonemap = false;
69
gpu_need_tonemap_cond.notify_all();
72
thread_scoped_lock pause_lock(pause_mutex);
75
pause_cond.notify_all();
80
if(params.output_path != "") {
83
progress.set_status("Writing Image", params.output_path);
84
display->write(device, params.output_path);
95
session_thread = new thread(function_bind(&Session::run, this));
98
bool Session::ready_to_reset()
100
double dt = time_dt() - reset_time;
102
if(!display_outdated)
103
return (dt > params.reset_timeout);
105
return (dt > params.cancel_timeout);
110
void Session::reset_gpu(BufferParams& buffer_params, int samples)
112
/* block for buffer acces and reset immediately. we can't do this
113
in the thread, because we need to allocate an OpenGL buffer, and
114
that only works in the main thread */
115
thread_scoped_lock display_lock(display->mutex);
116
thread_scoped_lock buffers_lock(buffers->mutex);
118
display_outdated = true;
119
reset_time = time_dt();
121
reset_(buffer_params, samples);
123
gpu_need_tonemap = false;
124
gpu_need_tonemap_cond.notify_all();
126
pause_cond.notify_all();
129
bool Session::draw_gpu(BufferParams& buffer_params)
131
/* block for buffer access */
132
thread_scoped_lock display_lock(display->mutex);
134
/* first check we already rendered something */
136
/* then verify the buffers have the expected size, so we don't
137
draw previous results in a resized window */
138
if(!buffer_params.modified(display->params)) {
139
/* for CUDA we need to do tonemapping still, since we can
140
only access GL buffers from the main thread */
141
if(gpu_need_tonemap) {
142
thread_scoped_lock buffers_lock(buffers->mutex);
144
gpu_need_tonemap = false;
145
gpu_need_tonemap_cond.notify_all();
148
display->draw(device);
150
if(display_outdated && (time_dt() - reset_time) > params.text_timeout)
160
void Session::run_gpu()
162
start_time = time_dt();
163
reset_time = time_dt();
166
if(!params.background)
167
progress.set_start_time(start_time + paused_time);
169
while(!progress.get_cancel()) {
170
/* advance to next tile */
171
bool no_tiles = !tile_manager.next();
173
if(params.background) {
174
/* if no work left and in background mode, we can stop immediately */
176
progress.set_status("Finished");
181
/* if in interactive mode, and we are either paused or done for now,
182
wait for pause condition notify to wake up again */
183
thread_scoped_lock pause_lock(pause_mutex);
185
if(pause || no_tiles) {
186
update_status_time(pause, no_tiles);
189
double pause_start = time_dt();
190
pause_cond.wait(pause_lock);
191
paused_time += time_dt() - pause_start;
193
if(!params.background)
194
progress.set_start_time(start_time + paused_time);
196
update_status_time(pause, no_tiles);
197
progress.set_update();
204
if(progress.get_cancel())
212
if(device->error_message() != "")
213
progress.set_cancel(device->error_message());
215
if(progress.get_cancel())
220
/* buffers mutex is locked entirely while rendering each
221
sample, and released/reacquired on each iteration to allow
222
reset and draw in between */
223
thread_scoped_lock buffers_lock(buffers->mutex);
225
/* update status and timing */
226
update_status_time();
229
foreach(Tile& tile, tile_manager.state.tiles) {
234
if(device->error_message() != "")
235
progress.set_cancel(device->error_message());
237
if(progress.get_cancel())
241
/* update status and timing */
242
update_status_time();
244
gpu_need_tonemap = true;
245
gpu_draw_ready = true;
246
progress.set_update();
248
/* wait for tonemap */
249
if(!params.background) {
250
while(gpu_need_tonemap) {
251
if(progress.get_cancel())
254
gpu_need_tonemap_cond.wait(buffers_lock);
258
if(device->error_message() != "")
259
progress.set_cancel(device->error_message());
261
if(progress.get_cancel())
269
void Session::reset_cpu(BufferParams& buffer_params, int samples)
271
thread_scoped_lock reset_lock(delayed_reset.mutex);
273
display_outdated = true;
274
reset_time = time_dt();
276
delayed_reset.params = buffer_params;
277
delayed_reset.samples = samples;
278
delayed_reset.do_reset = true;
279
device->task_cancel();
281
pause_cond.notify_all();
284
bool Session::draw_cpu(BufferParams& buffer_params)
286
thread_scoped_lock display_lock(display->mutex);
288
/* first check we already rendered something */
289
if(display->draw_ready()) {
290
/* then verify the buffers have the expected size, so we don't
291
draw previous results in a resized window */
292
if(!buffer_params.modified(display->params)) {
293
display->draw(device);
295
if(display_outdated && (time_dt() - reset_time) > params.text_timeout)
305
void Session::run_cpu()
308
/* reset once to start */
309
thread_scoped_lock reset_lock(delayed_reset.mutex);
310
thread_scoped_lock buffers_lock(buffers->mutex);
311
thread_scoped_lock display_lock(display->mutex);
313
reset_(delayed_reset.params, delayed_reset.samples);
314
delayed_reset.do_reset = false;
317
while(!progress.get_cancel()) {
318
/* advance to next tile */
319
bool no_tiles = !tile_manager.next();
320
bool need_tonemap = false;
322
if(params.background) {
323
/* if no work left and in background mode, we can stop immediately */
325
progress.set_status("Finished");
330
/* if in interactive mode, and we are either paused or done for now,
331
wait for pause condition notify to wake up again */
332
thread_scoped_lock pause_lock(pause_mutex);
334
if(pause || no_tiles) {
335
update_status_time(pause, no_tiles);
338
double pause_start = time_dt();
339
pause_cond.wait(pause_lock);
340
paused_time += time_dt() - pause_start;
342
if(!params.background)
343
progress.set_start_time(start_time + paused_time);
345
update_status_time(pause, no_tiles);
346
progress.set_update();
353
if(progress.get_cancel())
358
/* buffers mutex is locked entirely while rendering each
359
sample, and released/reacquired on each iteration to allow
360
reset and draw in between */
361
thread_scoped_lock buffers_lock(buffers->mutex);
366
if(device->error_message() != "")
367
progress.set_cancel(device->error_message());
369
if(progress.get_cancel())
372
/* update status and timing */
373
update_status_time();
376
foreach(Tile& tile, tile_manager.state.tiles)
379
/* update status and timing */
380
update_status_time();
382
if(!params.background)
385
if(device->error_message() != "")
386
progress.set_cancel(device->error_message());
392
thread_scoped_lock reset_lock(delayed_reset.mutex);
393
thread_scoped_lock buffers_lock(buffers->mutex);
394
thread_scoped_lock display_lock(display->mutex);
396
if(delayed_reset.do_reset) {
397
/* reset rendering if request from main thread */
398
delayed_reset.do_reset = false;
399
reset_(delayed_reset.params, delayed_reset.samples);
401
else if(need_tonemap) {
402
/* tonemap only if we do not reset, we don't we don't
403
want to show the result of an incomplete sample*/
407
if(device->error_message() != "")
408
progress.set_cancel(device->error_message());
411
progress.set_update();
418
if(!kernels_loaded) {
419
progress.set_status("Loading render kernels (may take a few minutes the first time)");
421
if(!device->load_kernels(params.experimental)) {
422
string message = device->error_message();
424
message = "Failed loading render kernel, see console for errors";
426
progress.set_status("Error", message);
427
progress.set_update();
431
kernels_loaded = true;
434
/* session thread loop */
435
progress.set_status("Waiting for render to start");
438
if(!progress.get_cancel()) {
445
/* progress update */
446
if(progress.get_cancel())
447
progress.set_status("Cancel", progress.get_cancel_message());
449
progress.set_update();
452
bool Session::draw(BufferParams& buffer_params)
455
return draw_gpu(buffer_params);
457
return draw_cpu(buffer_params);
460
void Session::reset_(BufferParams& buffer_params, int samples)
462
if(buffer_params.modified(buffers->params)) {
463
gpu_draw_ready = false;
464
buffers->reset(device, buffer_params);
465
display->reset(device, buffer_params);
468
tile_manager.reset(buffer_params, samples);
470
start_time = time_dt();
475
if(!params.background)
476
progress.set_start_time(start_time + paused_time);
479
void Session::reset(BufferParams& buffer_params, int samples)
482
reset_gpu(buffer_params, samples);
484
reset_cpu(buffer_params, samples);
487
void Session::set_samples(int samples)
489
if(samples != params.samples) {
490
params.samples = samples;
491
tile_manager.set_samples(samples);
494
thread_scoped_lock pause_lock(pause_mutex);
496
pause_cond.notify_all();
500
void Session::set_pause(bool pause_)
505
thread_scoped_lock pause_lock(pause_mutex);
507
if(pause != pause_) {
514
pause_cond.notify_all();
519
session_thread->join();
520
delete session_thread;
522
session_thread = NULL;
525
void Session::update_scene()
527
thread_scoped_lock scene_lock(scene->mutex);
529
progress.set_status("Updating Scene");
531
/* update camera if dimensions changed for progressive render. the camera
532
knows nothing about progressive or cropped rendering, it just gets the
533
image dimensions passed in */
534
Camera *cam = scene->camera;
535
int width = tile_manager.state.buffer.full_width;
536
int height = tile_manager.state.buffer.full_height;
538
if(width != cam->width || height != cam->height) {
540
cam->height = height;
545
if(scene->need_update())
546
scene->device_update(device, progress);
549
void Session::update_status_time(bool show_pause, bool show_done)
551
int sample = tile_manager.state.sample;
552
int resolution = tile_manager.state.resolution;
555
string status, substatus;
557
if(!params.progressive)
558
substatus = "Path Tracing";
559
else if(params.samples == INT_MAX)
560
substatus = string_printf("Path Tracing Sample %d", sample+1);
562
substatus = string_printf("Path Tracing Sample %d/%d", sample+1, params.samples);
569
status = "Rendering";
571
progress.set_status(status, substatus);
574
if(preview_time == 0.0 && resolution == 1)
575
preview_time = time_dt();
577
double sample_time = (sample == 0)? 0.0: (time_dt() - preview_time - paused_time)/(sample);
579
/* negative can happen when we pause a bit before rendering, can discard that */
580
if(preview_time < 0.0) preview_time = 0.0;
582
progress.set_sample(sample + 1, sample_time);
585
void Session::path_trace(Tile& tile)
587
/* add path trace task */
588
DeviceTask task(DeviceTask::PATH_TRACE);
590
task.x = tile_manager.state.buffer.full_x + tile.x;
591
task.y = tile_manager.state.buffer.full_y + tile.y;
594
task.buffer = buffers->buffer.device_pointer;
595
task.rng_state = buffers->rng_state.device_pointer;
596
task.sample = tile_manager.state.sample;
597
task.resolution = tile_manager.state.resolution;
598
tile_manager.state.buffer.get_offset_stride(task.offset, task.stride);
600
device->task_add(task);
603
void Session::tonemap()
605
/* add tonemap task */
606
DeviceTask task(DeviceTask::TONEMAP);
608
task.x = tile_manager.state.buffer.full_x;
609
task.y = tile_manager.state.buffer.full_y;
610
task.w = tile_manager.state.buffer.width;
611
task.h = tile_manager.state.buffer.height;
612
task.rgba = display->rgba.device_pointer;
613
task.buffer = buffers->buffer.device_pointer;
614
task.sample = tile_manager.state.sample;
615
task.resolution = tile_manager.state.resolution;
616
tile_manager.state.buffer.get_offset_stride(task.offset, task.stride);
618
if(task.w > 0 && task.h > 0) {
619
device->task_add(task);
622
/* set display to new size */
623
display->draw_set(task.w, task.h);
626
display_outdated = false;