Blender V2.61 - r43446
|
00001 /* 00002 * Copyright 2011, Blender Foundation. 00003 * 00004 * This program is free software; you can redistribute it and/or 00005 * modify it under the terms of the GNU General Public License 00006 * as published by the Free Software Foundation; either version 2 00007 * of the License, or (at your option) any later version. 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU General Public License for more details. 00013 * 00014 * You should have received a copy of the GNU General Public License 00015 * along with this program; if not, write to the Free Software Foundation, 00016 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00017 */ 00018 00019 #include <string.h> 00020 #include <limits.h> 00021 00022 #include "buffers.h" 00023 #include "camera.h" 00024 #include "device.h" 00025 #include "scene.h" 00026 #include "session.h" 00027 00028 #include "util_foreach.h" 00029 #include "util_function.h" 00030 #include "util_time.h" 00031 00032 CCL_NAMESPACE_BEGIN 00033 00034 Session::Session(const SessionParams& params_) 00035 : params(params_), 00036 tile_manager(params.progressive, params.samples, params.tile_size, params.min_size) 00037 { 00038 device_use_gl = ((params.device.type != DEVICE_CPU) && !params.background); 00039 00040 device = Device::create(params.device, params.background, params.threads); 00041 buffers = new RenderBuffers(device); 00042 display = new DisplayBuffer(device); 00043 00044 session_thread = NULL; 00045 scene = NULL; 00046 00047 start_time = 0.0; 00048 reset_time = 0.0; 00049 preview_time = 0.0; 00050 paused_time = 0.0; 00051 sample = 0; 00052 00053 delayed_reset.do_reset = false; 00054 delayed_reset.samples = 0; 00055 00056 display_outdated = false; 00057 gpu_draw_ready = false; 00058 gpu_need_tonemap = false; 00059 pause = false; 00060 } 00061 00062 Session::~Session() 00063 { 00064 if(session_thread) { 00065 progress.set_cancel("Exiting"); 00066 00067 gpu_need_tonemap = false; 00068 gpu_need_tonemap_cond.notify_all(); 00069 00070 { 00071 thread_scoped_lock pause_lock(pause_mutex); 00072 pause = false; 00073 } 00074 pause_cond.notify_all(); 00075 00076 wait(); 00077 } 00078 00079 if(params.output_path != "") { 00080 tonemap(); 00081 00082 progress.set_status("Writing Image", params.output_path); 00083 display->write(device, params.output_path); 00084 } 00085 00086 delete buffers; 00087 delete display; 00088 delete scene; 00089 delete device; 00090 } 00091 00092 void Session::start() 00093 { 00094 session_thread = new thread(function_bind(&Session::run, this)); 00095 } 00096 00097 bool Session::ready_to_reset() 00098 { 00099 double dt = time_dt() - reset_time; 00100 00101 if(!display_outdated) 00102 return (dt > params.reset_timeout); 00103 else 00104 return (dt > params.cancel_timeout); 00105 } 00106 00107 /* GPU Session */ 00108 00109 void Session::reset_gpu(BufferParams& buffer_params, int samples) 00110 { 00111 /* block for buffer acces and reset immediately. we can't do this 00112 in the thread, because we need to allocate an OpenGL buffer, and 00113 that only works in the main thread */ 00114 thread_scoped_lock display_lock(display->mutex); 00115 thread_scoped_lock buffers_lock(buffers->mutex); 00116 00117 display_outdated = true; 00118 reset_time = time_dt(); 00119 00120 reset_(buffer_params, samples); 00121 00122 gpu_need_tonemap = false; 00123 gpu_need_tonemap_cond.notify_all(); 00124 00125 pause_cond.notify_all(); 00126 } 00127 00128 bool Session::draw_gpu(BufferParams& buffer_params) 00129 { 00130 /* block for buffer access */ 00131 thread_scoped_lock display_lock(display->mutex); 00132 00133 /* first check we already rendered something */ 00134 if(gpu_draw_ready) { 00135 /* then verify the buffers have the expected size, so we don't 00136 draw previous results in a resized window */ 00137 if(!buffer_params.modified(display->params)) { 00138 /* for CUDA we need to do tonemapping still, since we can 00139 only access GL buffers from the main thread */ 00140 if(gpu_need_tonemap) { 00141 thread_scoped_lock buffers_lock(buffers->mutex); 00142 tonemap(); 00143 gpu_need_tonemap = false; 00144 gpu_need_tonemap_cond.notify_all(); 00145 } 00146 00147 display->draw(device); 00148 00149 if(display_outdated && (time_dt() - reset_time) > params.text_timeout) 00150 return false; 00151 00152 return true; 00153 } 00154 } 00155 00156 return false; 00157 } 00158 00159 void Session::run_gpu() 00160 { 00161 start_time = time_dt(); 00162 reset_time = time_dt(); 00163 paused_time = 0.0; 00164 00165 if(!params.background) 00166 progress.set_start_time(start_time - paused_time); 00167 00168 while(!progress.get_cancel()) { 00169 /* advance to next tile */ 00170 bool no_tiles = !tile_manager.next(); 00171 00172 if(params.background) { 00173 /* if no work left and in background mode, we can stop immediately */ 00174 if(no_tiles) { 00175 progress.set_status("Finished"); 00176 break; 00177 } 00178 } 00179 else { 00180 /* if in interactive mode, and we are either paused or done for now, 00181 wait for pause condition notify to wake up again */ 00182 thread_scoped_lock pause_lock(pause_mutex); 00183 00184 if(pause || no_tiles) { 00185 update_status_time(pause, no_tiles); 00186 00187 while(1) { 00188 double pause_start = time_dt(); 00189 pause_cond.wait(pause_lock); 00190 paused_time += time_dt() - pause_start; 00191 00192 if(!params.background) 00193 progress.set_start_time(start_time - paused_time); 00194 00195 update_status_time(pause, no_tiles); 00196 progress.set_update(); 00197 00198 if(!pause) 00199 break; 00200 } 00201 } 00202 00203 if(progress.get_cancel()) 00204 break; 00205 } 00206 00207 if(!no_tiles) { 00208 /* update scene */ 00209 update_scene(); 00210 00211 if(device->error_message() != "") 00212 progress.set_cancel(device->error_message()); 00213 00214 if(progress.get_cancel()) 00215 break; 00216 } 00217 00218 if(!no_tiles) { 00219 /* buffers mutex is locked entirely while rendering each 00220 sample, and released/reacquired on each iteration to allow 00221 reset and draw in between */ 00222 thread_scoped_lock buffers_lock(buffers->mutex); 00223 00224 /* update status and timing */ 00225 update_status_time(); 00226 00227 /* path trace */ 00228 foreach(Tile& tile, tile_manager.state.tiles) { 00229 path_trace(tile); 00230 00231 device->task_wait(); 00232 00233 if(device->error_message() != "") 00234 progress.set_cancel(device->error_message()); 00235 00236 if(progress.get_cancel()) 00237 break; 00238 } 00239 00240 /* update status and timing */ 00241 update_status_time(); 00242 00243 gpu_need_tonemap = true; 00244 gpu_draw_ready = true; 00245 progress.set_update(); 00246 00247 /* wait for tonemap */ 00248 if(!params.background) { 00249 while(gpu_need_tonemap) { 00250 if(progress.get_cancel()) 00251 break; 00252 00253 gpu_need_tonemap_cond.wait(buffers_lock); 00254 } 00255 } 00256 00257 if(device->error_message() != "") 00258 progress.set_cancel(device->error_message()); 00259 00260 if(progress.get_cancel()) 00261 break; 00262 } 00263 } 00264 } 00265 00266 /* CPU Session */ 00267 00268 void Session::reset_cpu(BufferParams& buffer_params, int samples) 00269 { 00270 thread_scoped_lock reset_lock(delayed_reset.mutex); 00271 00272 display_outdated = true; 00273 reset_time = time_dt(); 00274 00275 delayed_reset.params = buffer_params; 00276 delayed_reset.samples = samples; 00277 delayed_reset.do_reset = true; 00278 device->task_cancel(); 00279 00280 pause_cond.notify_all(); 00281 } 00282 00283 bool Session::draw_cpu(BufferParams& buffer_params) 00284 { 00285 thread_scoped_lock display_lock(display->mutex); 00286 00287 /* first check we already rendered something */ 00288 if(display->draw_ready()) { 00289 /* then verify the buffers have the expected size, so we don't 00290 draw previous results in a resized window */ 00291 if(!buffer_params.modified(display->params)) { 00292 display->draw(device); 00293 00294 if(display_outdated && (time_dt() - reset_time) > params.text_timeout) 00295 return false; 00296 00297 return true; 00298 } 00299 } 00300 00301 return false; 00302 } 00303 00304 void Session::run_cpu() 00305 { 00306 { 00307 /* reset once to start */ 00308 thread_scoped_lock reset_lock(delayed_reset.mutex); 00309 thread_scoped_lock buffers_lock(buffers->mutex); 00310 thread_scoped_lock display_lock(display->mutex); 00311 00312 reset_(delayed_reset.params, delayed_reset.samples); 00313 delayed_reset.do_reset = false; 00314 } 00315 00316 while(!progress.get_cancel()) { 00317 /* advance to next tile */ 00318 bool no_tiles = !tile_manager.next(); 00319 bool need_tonemap = false; 00320 00321 if(params.background) { 00322 /* if no work left and in background mode, we can stop immediately */ 00323 if(no_tiles) { 00324 progress.set_status("Finished"); 00325 break; 00326 } 00327 } 00328 else { 00329 /* if in interactive mode, and we are either paused or done for now, 00330 wait for pause condition notify to wake up again */ 00331 thread_scoped_lock pause_lock(pause_mutex); 00332 00333 if(pause || no_tiles) { 00334 update_status_time(pause, no_tiles); 00335 00336 while(1) { 00337 double pause_start = time_dt(); 00338 pause_cond.wait(pause_lock); 00339 paused_time += time_dt() - pause_start; 00340 00341 if(!params.background) 00342 progress.set_start_time(start_time - paused_time); 00343 00344 update_status_time(pause, no_tiles); 00345 progress.set_update(); 00346 00347 if(!pause) 00348 break; 00349 } 00350 } 00351 00352 if(progress.get_cancel()) 00353 break; 00354 } 00355 00356 if(!no_tiles) { 00357 /* buffers mutex is locked entirely while rendering each 00358 sample, and released/reacquired on each iteration to allow 00359 reset and draw in between */ 00360 thread_scoped_lock buffers_lock(buffers->mutex); 00361 00362 /* update scene */ 00363 update_scene(); 00364 00365 if(device->error_message() != "") 00366 progress.set_cancel(device->error_message()); 00367 00368 if(progress.get_cancel()) 00369 break; 00370 00371 /* update status and timing */ 00372 update_status_time(); 00373 00374 /* path trace */ 00375 foreach(Tile& tile, tile_manager.state.tiles) 00376 path_trace(tile); 00377 00378 /* update status and timing */ 00379 update_status_time(); 00380 00381 if(!params.background) 00382 need_tonemap = true; 00383 00384 if(device->error_message() != "") 00385 progress.set_cancel(device->error_message()); 00386 } 00387 00388 device->task_wait(); 00389 00390 { 00391 thread_scoped_lock reset_lock(delayed_reset.mutex); 00392 thread_scoped_lock buffers_lock(buffers->mutex); 00393 thread_scoped_lock display_lock(display->mutex); 00394 00395 if(delayed_reset.do_reset) { 00396 /* reset rendering if request from main thread */ 00397 delayed_reset.do_reset = false; 00398 reset_(delayed_reset.params, delayed_reset.samples); 00399 } 00400 else if(need_tonemap) { 00401 /* tonemap only if we do not reset, we don't we don't 00402 want to show the result of an incomplete sample*/ 00403 tonemap(); 00404 } 00405 00406 if(device->error_message() != "") 00407 progress.set_cancel(device->error_message()); 00408 } 00409 00410 progress.set_update(); 00411 } 00412 } 00413 00414 void Session::run() 00415 { 00416 /* load kernels */ 00417 progress.set_status("Loading render kernels (may take a few minutes the first time)"); 00418 00419 if(!device->load_kernels(params.experimental)) { 00420 string message = device->error_message(); 00421 if(message == "") 00422 message = "Failed loading render kernel, see console for errors"; 00423 00424 progress.set_status("Error", message); 00425 progress.set_update(); 00426 return; 00427 } 00428 00429 /* session thread loop */ 00430 progress.set_status("Waiting for render to start"); 00431 00432 /* run */ 00433 if(!progress.get_cancel()) { 00434 if(device_use_gl) 00435 run_gpu(); 00436 else 00437 run_cpu(); 00438 } 00439 00440 /* progress update */ 00441 if(progress.get_cancel()) 00442 progress.set_status("Cancel", progress.get_cancel_message()); 00443 else 00444 progress.set_update(); 00445 } 00446 00447 bool Session::draw(BufferParams& buffer_params) 00448 { 00449 if(device_use_gl) 00450 return draw_gpu(buffer_params); 00451 else 00452 return draw_cpu(buffer_params); 00453 } 00454 00455 void Session::reset_(BufferParams& buffer_params, int samples) 00456 { 00457 if(buffer_params.modified(buffers->params)) { 00458 gpu_draw_ready = false; 00459 buffers->reset(device, buffer_params); 00460 display->reset(device, buffer_params); 00461 } 00462 00463 tile_manager.reset(buffer_params, samples); 00464 00465 start_time = time_dt(); 00466 preview_time = 0.0; 00467 paused_time = 0.0; 00468 sample = 0; 00469 00470 if(!params.background) 00471 progress.set_start_time(start_time - paused_time); 00472 } 00473 00474 void Session::reset(BufferParams& buffer_params, int samples) 00475 { 00476 if(device_use_gl) 00477 reset_gpu(buffer_params, samples); 00478 else 00479 reset_cpu(buffer_params, samples); 00480 } 00481 00482 void Session::set_samples(int samples) 00483 { 00484 if(samples != params.samples) { 00485 params.samples = samples; 00486 tile_manager.set_samples(samples); 00487 00488 { 00489 thread_scoped_lock pause_lock(pause_mutex); 00490 } 00491 pause_cond.notify_all(); 00492 } 00493 } 00494 00495 void Session::set_pause(bool pause_) 00496 { 00497 bool notify = false; 00498 00499 { 00500 thread_scoped_lock pause_lock(pause_mutex); 00501 00502 if(pause != pause_) { 00503 pause = pause_; 00504 notify = true; 00505 } 00506 } 00507 00508 if(notify) 00509 pause_cond.notify_all(); 00510 } 00511 00512 void Session::wait() 00513 { 00514 session_thread->join(); 00515 delete session_thread; 00516 00517 session_thread = NULL; 00518 } 00519 00520 void Session::update_scene() 00521 { 00522 thread_scoped_lock scene_lock(scene->mutex); 00523 00524 progress.set_status("Updating Scene"); 00525 00526 /* update camera if dimensions changed for progressive render. the camera 00527 knows nothing about progressive or cropped rendering, it just gets the 00528 image dimensions passed in */ 00529 Camera *cam = scene->camera; 00530 int width = tile_manager.state.buffer.full_width; 00531 int height = tile_manager.state.buffer.full_height; 00532 00533 if(width != cam->width || height != cam->height) { 00534 cam->width = width; 00535 cam->height = height; 00536 cam->tag_update(); 00537 } 00538 00539 /* update scene */ 00540 if(scene->need_update()) 00541 scene->device_update(device, progress); 00542 } 00543 00544 void Session::update_status_time(bool show_pause, bool show_done) 00545 { 00546 int sample = tile_manager.state.sample; 00547 int resolution = tile_manager.state.resolution; 00548 00549 /* update status */ 00550 string status, substatus; 00551 00552 if(!params.progressive) 00553 substatus = "Path Tracing"; 00554 else if(params.samples == INT_MAX) 00555 substatus = string_printf("Path Tracing Sample %d", sample+1); 00556 else 00557 substatus = string_printf("Path Tracing Sample %d/%d", sample+1, params.samples); 00558 00559 if(show_pause) 00560 status = "Paused"; 00561 else if(show_done) 00562 status = "Done"; 00563 else 00564 status = "Rendering"; 00565 00566 progress.set_status(status, substatus); 00567 00568 /* update timing */ 00569 if(preview_time == 0.0 && resolution == 1) 00570 preview_time = time_dt(); 00571 00572 double sample_time = (sample == 0)? 0.0: (time_dt() - preview_time - paused_time)/(sample); 00573 00574 /* negative can happen when we pause a bit before rendering, can discard that */ 00575 if(preview_time < 0.0) preview_time = 0.0; 00576 00577 progress.set_sample(sample + 1, sample_time); 00578 } 00579 00580 void Session::path_trace(Tile& tile) 00581 { 00582 /* add path trace task */ 00583 DeviceTask task(DeviceTask::PATH_TRACE); 00584 00585 task.x = tile_manager.state.buffer.full_x + tile.x; 00586 task.y = tile_manager.state.buffer.full_y + tile.y; 00587 task.w = tile.w; 00588 task.h = tile.h; 00589 task.buffer = buffers->buffer.device_pointer; 00590 task.rng_state = buffers->rng_state.device_pointer; 00591 task.sample = tile_manager.state.sample; 00592 task.resolution = tile_manager.state.resolution; 00593 tile_manager.state.buffer.get_offset_stride(task.offset, task.stride); 00594 00595 device->task_add(task); 00596 } 00597 00598 void Session::tonemap() 00599 { 00600 /* add tonemap task */ 00601 DeviceTask task(DeviceTask::TONEMAP); 00602 00603 task.x = tile_manager.state.buffer.full_x; 00604 task.y = tile_manager.state.buffer.full_y; 00605 task.w = tile_manager.state.buffer.width; 00606 task.h = tile_manager.state.buffer.height; 00607 task.rgba = display->rgba.device_pointer; 00608 task.buffer = buffers->buffer.device_pointer; 00609 task.sample = tile_manager.state.sample; 00610 task.resolution = tile_manager.state.resolution; 00611 tile_manager.state.buffer.get_offset_stride(task.offset, task.stride); 00612 00613 if(task.w > 0 && task.h > 0) { 00614 device->task_add(task); 00615 device->task_wait(); 00616 00617 /* set display to new size */ 00618 display->draw_set(task.w, task.h); 00619 } 00620 00621 display_outdated = false; 00622 } 00623 00624 CCL_NAMESPACE_END 00625