Blender V2.61 - r43446

session.cpp

Go to the documentation of this file.
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