Cycles Hair: Strand Minimum Pixel Size
[blender.git] / intern / cycles / render / session.cpp
index 2fb1f49e563328f17b104dcd51de53791eed0c7e..569c29e0b094e7ef08eba7f005acb4829f6d3668 100644 (file)
 
 CCL_NAMESPACE_BEGIN
 
+/* Note about  preserve_tile_device option for tile manager:
+ * progressive refine and viewport rendering does requires tiles to
+ * always be allocated for the same device
+ */
 Session::Session(const SessionParams& params_)
 : params(params_),
-  tile_manager(params.progressive, params.samples, params.tile_size, params.resolution,
-       (params.background)? 1: max(params.device.multi_devices.size(), 1))
+  tile_manager(params.progressive, params.samples, params.tile_size, params.start_resolution,
+       params.background == false || params.progressive_refine, params.background, params.tile_order,
+       max(params.device.multi_devices.size(), 1)),
+  stats()
 {
        device_use_gl = ((params.device.type != DEVICE_CPU) && !params.background);
 
        TaskScheduler::init(params.threads);
 
-       device = Device::create(params.device, params.background, params.threads);
+       device = Device::create(params.device, stats, params.background);
 
        if(params.background) {
                buffers = NULL;
@@ -96,6 +102,9 @@ Session::~Session()
                display->write(device, params.output_path);
        }
 
+       foreach(RenderBuffers *buffers, tile_buffers)
+               delete buffers;
+
        delete buffers;
        delete display;
        delete scene;
@@ -123,6 +132,8 @@ bool Session::ready_to_reset()
 
 void Session::reset_gpu(BufferParams& buffer_params, int samples)
 {
+       thread_scoped_lock pause_lock(pause_mutex);
+
        /* block for buffer acces and reset immediately. we can't do this
         * in the thread, because we need to allocate an OpenGL buffer, and
         * that only works in the main thread */
@@ -173,9 +184,12 @@ bool Session::draw_gpu(BufferParams& buffer_params)
 
 void Session::run_gpu()
 {
+       bool tiles_written = false;
+
        start_time = time_dt();
        reset_time = time_dt();
        paused_time = 0.0;
+       last_update_time = time_dt();
 
        if(!params.background)
                progress.set_start_time(start_time + paused_time);
@@ -196,7 +210,12 @@ void Session::run_gpu()
                         * wait for pause condition notify to wake up again */
                        thread_scoped_lock pause_lock(pause_mutex);
 
-                       if(pause || no_tiles) {
+                       if(!pause && !tile_manager.done()) {
+                               /* reset could have happened after no_tiles was set, before this lock.
+                                * in this case we shall not wait for pause condition
+                                */
+                       }
+                       else if(pause || no_tiles) {
                                update_status_time(pause, no_tiles);
 
                                while(1) {
@@ -267,10 +286,15 @@ void Session::run_gpu()
                        if(device->error_message() != "")
                                progress.set_cancel(device->error_message());
 
+                       tiles_written = update_progressive_refine(progress.get_cancel());
+
                        if(progress.get_cancel())
                                break;
                }
        }
+
+       if(!tiles_written)
+               update_progressive_refine(true);
 }
 
 /* CPU Session */
@@ -278,6 +302,7 @@ void Session::run_gpu()
 void Session::reset_cpu(BufferParams& buffer_params, int samples)
 {
        thread_scoped_lock reset_lock(delayed_reset.mutex);
+       thread_scoped_lock pause_lock(pause_mutex);
 
        display_outdated = true;
        reset_time = time_dt();
@@ -313,8 +338,12 @@ bool Session::draw_cpu(BufferParams& buffer_params)
 
 bool Session::acquire_tile(Device *tile_device, RenderTile& rtile)
 {
-       if(progress.get_cancel())
-               return false;
+       if(progress.get_cancel()) {
+               if(params.progressive_refine == false) {
+                       /* for progressive refine current sample should be finished for all tiles */
+                       return false;
+               }
+       }
 
        thread_scoped_lock tile_lock(tile_mutex);
 
@@ -332,13 +361,13 @@ bool Session::acquire_tile(Device *tile_device, RenderTile& rtile)
        rtile.h = tile.h;
        rtile.start_sample = tile_manager.state.sample;
        rtile.num_samples = tile_manager.state.num_samples;
-       rtile.resolution = tile_manager.state.resolution;
+       rtile.resolution = tile_manager.state.resolution_divider;
 
        tile_lock.unlock();
 
-       /* in case of a permant buffer, return it, otherwise we will allocate
+       /* in case of a permanent buffer, return it, otherwise we will allocate
         * a new temporary buffer */
-       if(!write_render_tile_cb) {
+       if(!params.background) {
                tile_manager.state.buffer.get_offset_stride(rtile.offset, rtile.stride);
 
                rtile.buffer = buffers->buffer.device_pointer;
@@ -360,15 +389,42 @@ bool Session::acquire_tile(Device *tile_device, RenderTile& rtile)
 
        buffer_params.get_offset_stride(rtile.offset, rtile.stride);
 
+       RenderBuffers *tilebuffers;
+
        /* allocate buffers */
-       RenderBuffers *tilebuffers = new RenderBuffers(tile_device);
-       tilebuffers->reset(tile_device, buffer_params);
+       if(params.progressive_refine) {
+               tile_lock.lock();
+
+               if(tile_buffers.size() == 0)
+                       tile_buffers.resize(tile_manager.state.num_tiles, NULL);
+
+               tilebuffers = tile_buffers[tile.index];
+               if(tilebuffers == NULL) {
+                       tilebuffers = new RenderBuffers(tile_device);
+                       tile_buffers[tile.index] = tilebuffers;
+
+                       tilebuffers->reset(tile_device, buffer_params);
+               }
+
+               tile_lock.unlock();
+       }
+       else {
+               tilebuffers = new RenderBuffers(tile_device);
+
+               tilebuffers->reset(tile_device, buffer_params);
+       }
 
        rtile.buffer = tilebuffers->buffer.device_pointer;
        rtile.rng_state = tilebuffers->rng_state.device_pointer;
        rtile.rgba = 0;
        rtile.buffers = tilebuffers;
 
+       /* this will tag tile as IN PROGRESS in blender-side render pipeline,
+        * which is needed to highlight currently rendering tile before first
+        * sample was processed for it
+        */
+       update_tile_sample(rtile);
+
        return true;
 }
 
@@ -377,9 +433,11 @@ void Session::update_tile_sample(RenderTile& rtile)
        thread_scoped_lock tile_lock(tile_mutex);
 
        if(update_render_tile_cb) {
-               /* todo: optimize this by making it thread safe and removing lock */
+               if(params.progressive_refine == false) {
+                       /* todo: optimize this by making it thread safe and removing lock */
 
-               update_render_tile_cb(rtile);
+                       update_render_tile_cb(rtile);
+               }
        }
 
        update_status_time();
@@ -390,10 +448,12 @@ void Session::release_tile(RenderTile& rtile)
        thread_scoped_lock tile_lock(tile_mutex);
 
        if(write_render_tile_cb) {
-               /* todo: optimize this by making it thread safe and removing lock */
-               write_render_tile_cb(rtile);
+               if(params.progressive_refine == false) {
+                       /* todo: optimize this by making it thread safe and removing lock */
+                       write_render_tile_cb(rtile);
 
-               delete rtile.buffers;
+                       delete rtile.buffers;
+               }
        }
 
        update_status_time();
@@ -401,6 +461,10 @@ void Session::release_tile(RenderTile& rtile)
 
 void Session::run_cpu()
 {
+       bool tiles_written = false;
+
+       last_update_time = time_dt();
+
        {
                /* reset once to start */
                thread_scoped_lock reset_lock(delayed_reset.mutex);
@@ -428,7 +492,16 @@ void Session::run_cpu()
                         * wait for pause condition notify to wake up again */
                        thread_scoped_lock pause_lock(pause_mutex);
 
-                       if(pause || no_tiles) {
+                       if(!pause && delayed_reset.do_reset) {
+                               /* reset once to start */
+                               thread_scoped_lock reset_lock(delayed_reset.mutex);
+                               thread_scoped_lock buffers_lock(buffers_mutex);
+                               thread_scoped_lock display_lock(display_mutex);
+
+                               reset_(delayed_reset.params, delayed_reset.samples);
+                               delayed_reset.do_reset = false;
+                       }
+                       else if(pause || no_tiles) {
                                update_status_time(pause, no_tiles);
 
                                while(1) {
@@ -496,16 +569,21 @@ void Session::run_cpu()
                        }
                        else if(need_tonemap) {
                                /* tonemap only if we do not reset, we don't we don't
-                                * wan't to show the result of an incomplete sample*/
+                                * want to show the result of an incomplete sample*/
                                tonemap();
                        }
 
                        if(device->error_message() != "")
                                progress.set_cancel(device->error_message());
+
+                       tiles_written = update_progressive_refine(progress.get_cancel());
                }
 
                progress.set_update();
        }
+
+       if(!tiles_written)
+               update_progressive_refine(true);
 }
 
 void Session::run()
@@ -582,6 +660,15 @@ void Session::reset(BufferParams& buffer_params, int samples)
                reset_gpu(buffer_params, samples);
        else
                reset_cpu(buffer_params, samples);
+
+       if(params.progressive_refine) {
+               thread_scoped_lock buffers_lock(buffers_mutex);
+
+               foreach(RenderBuffers *buffers, tile_buffers)
+                       delete buffers;
+
+               tile_buffers.clear();
+       }
 }
 
 void Session::set_samples(int samples)
@@ -632,10 +719,12 @@ void Session::update_scene()
        Camera *cam = scene->camera;
        int width = tile_manager.state.buffer.full_width;
        int height = tile_manager.state.buffer.full_height;
+       int resolution = tile_manager.state.resolution_divider;
 
        if(width != cam->width || height != cam->height) {
                cam->width = width;
                cam->height = height;
+               cam->resolution = resolution;
                cam->tag_update();
        }
 
@@ -649,7 +738,7 @@ void Session::update_scene()
 void Session::update_status_time(bool show_pause, bool show_done)
 {
        int sample = tile_manager.state.sample;
-       int resolution = tile_manager.state.resolution;
+       int resolution = tile_manager.state.resolution_divider;
        int num_tiles = tile_manager.state.num_tiles;
        int tile = tile_manager.state.num_rendered_tiles;
 
@@ -657,10 +746,13 @@ void Session::update_status_time(bool show_pause, bool show_done)
        string status, substatus;
 
        if(!params.progressive) {
+               bool is_gpu = params.device.type == DEVICE_CUDA || params.device.type == DEVICE_OPENCL;
+               bool is_multidevice = params.device.multi_devices.size() > 1;
+               bool is_cpu = params.device.type == DEVICE_CPU;
+
                substatus = string_printf("Path Tracing Tile %d/%d", tile, num_tiles);
 
-               if(params.device.type == DEVICE_CUDA || params.device.type == DEVICE_OPENCL ||
-                       (params.device.type == DEVICE_CPU && num_tiles == 1)) {
+               if((is_gpu && !is_multidevice) || (is_cpu && num_tiles == 1)) {
                        /* when rendering on GPU multithreading happens within single tile, as in
                         * tiles are handling sequentially and in this case we could display
                         * currently rendering sample number
@@ -668,7 +760,7 @@ void Session::update_status_time(bool show_pause, bool show_done)
                         * also display the info on CPU, when using 1 tile only
                         */
 
-                       int sample = progress.get_sample(), num_samples = tile_manager.state.num_samples;
+                       int sample = progress.get_sample(), num_samples = tile_manager.num_samples;
 
                        if(tile > 1) {
                                /* sample counter is global for all tiles, subtract samples
@@ -681,10 +773,10 @@ void Session::update_status_time(bool show_pause, bool show_done)
                        substatus += string_printf(", Sample %d/%d", sample, num_samples);
                }
        }
-       else if(params.samples == INT_MAX)
+       else if(tile_manager.num_samples == INT_MAX)
                substatus = string_printf("Path Tracing Sample %d", sample+1);
        else
-               substatus = string_printf("Path Tracing Sample %d/%d", sample+1, params.samples);
+               substatus = string_printf("Path Tracing Sample %d/%d", sample+1, tile_manager.num_samples);
        
        if(show_pause)
                status = "Paused";
@@ -699,7 +791,7 @@ void Session::update_status_time(bool show_pause, bool show_done)
        if(preview_time == 0.0 && resolution == 1)
                preview_time = time_dt();
        
-       double tile_time = (tile == 0)? 0.0: (time_dt() - preview_time - paused_time)/(sample);
+       double tile_time = (tile == 0 || sample == 0)? 0.0: (time_dt() - preview_time - paused_time) / sample;
 
        /* negative can happen when we pause a bit before rendering, can discard that */
        if(preview_time < 0.0) preview_time = 0.0;
@@ -722,6 +814,7 @@ void Session::path_trace()
        task.get_cancel = function_bind(&Progress::get_cancel, &this->progress);
        task.update_tile_sample = function_bind(&Session::update_tile_sample, this, _1);
        task.update_progress_sample = function_bind(&Session::update_progress_sample, this);
+       task.need_finish_queue = params.progressive_refine;
 
        device->task_add(task);
 }
@@ -738,7 +831,7 @@ void Session::tonemap()
        task.rgba = display->rgba.device_pointer;
        task.buffer = buffers->buffer.device_pointer;
        task.sample = tile_manager.state.sample;
-       task.resolution = tile_manager.state.resolution;
+       task.resolution = tile_manager.state.resolution_divider;
        tile_manager.state.buffer.get_offset_stride(task.offset, task.stride);
 
        if(task.w > 0 && task.h > 0) {
@@ -752,5 +845,49 @@ void Session::tonemap()
        display_outdated = false;
 }
 
-CCL_NAMESPACE_END
+bool Session::update_progressive_refine(bool cancel)
+{
+       int sample = tile_manager.state.sample + 1;
+       bool write = sample == tile_manager.num_samples || cancel;
+
+       double current_time = time_dt();
+
+       if (current_time - last_update_time < 1.0) {
+               /* if last sample was processed, we need to write buffers anyway  */
+               if (!write)
+                       return false;
+       }
 
+       if(params.progressive_refine) {
+               foreach(RenderBuffers *buffers, tile_buffers) {
+                       RenderTile rtile;
+                       rtile.buffers = buffers;
+                       rtile.sample = sample;
+
+                       if(write)
+                               write_render_tile_cb(rtile);
+                       else
+                               update_render_tile_cb(rtile);
+               }
+       }
+
+       last_update_time = current_time;
+
+       return write;
+}
+
+void Session::device_free()
+{
+       scene->device_free();
+
+       foreach(RenderBuffers *buffers, tile_buffers)
+               delete buffers;
+
+       tile_buffers.clear();
+
+       /* used from background render only, so no need to
+        * re-create render/display buffers here
+        */
+}
+
+CCL_NAMESPACE_END