Cycles: progressive refine option
authorSergey Sharybin <sergey.vfx@gmail.com>
Sat, 13 Oct 2012 12:38:32 +0000 (12:38 +0000)
committerSergey Sharybin <sergey.vfx@gmail.com>
Sat, 13 Oct 2012 12:38:32 +0000 (12:38 +0000)
Just makes progressive refine :)

This means the whole image would be refined gradually using as much
threads as it's set in performance settings. Having enough tiles is
required to have this option working as it's expected.

Technically it's implemented by repeatedly computing next sample for
all the tiles before switching to next sample.

This works around 7-12% slower than regular tile-based rendering, so
use this option only if you really need it.

This commit also fixes progressive update of image when Save Buffers
option is enabled.

And one more thing this commit fixes is handling display buffer with
Save Buffers option enabled. If this option is enabled image buffer
wouldn't have neither byte nor float buffer until image is fully
rendered which could backfire in missing image while rendering in
cases color management cache became full.

This issue solved by allocating byte buffer for image buffer from
tile update callback.

Patch was reviewed by Brecht. He also made some minor edits to
original version to patch. Thanks, man!

18 files changed:
intern/cycles/blender/addon/properties.py
intern/cycles/blender/addon/ui.py
intern/cycles/blender/blender_session.cpp
intern/cycles/blender/blender_sync.cpp
intern/cycles/device/device_cpu.cpp
intern/cycles/device/device_cuda.cpp
intern/cycles/device/device_opencl.cpp
intern/cycles/device/device_task.h
intern/cycles/render/session.cpp
intern/cycles/render/session.h
intern/cycles/render/tile.cpp
intern/cycles/render/tile.h
source/blender/blenkernel/intern/image.c
source/blender/compositor/operations/COM_ViewerBaseOperation.cpp
source/blender/editors/render/render_internal.c
source/blender/editors/sculpt_paint/paint_image.c
source/blender/imbuf/IMB_colormanagement.h
source/blender/imbuf/intern/colormanagement.c

index 0fadfa0afc813a1f10cdc9047ed1b422fb066cce..f2a88f0d96c9f2a68a11af4a063499eefb467db5 100644 (file)
@@ -284,6 +284,11 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
                 description="Cache last built BVH to disk for faster re-render if no geometry changed",
                 default=False,
                 )
+        cls.use_progressive_refine = BoolProperty(
+                name="Progressive Refine",
+                description="Instead of rendering each tile until it is finished, refine the whole image progressively so rendering can be stopped manually when the noise is low enough",
+                default=False,
+                )
 
     @classmethod
     def unregister(cls):
index ca43c345bfa6ad3ad1c3a076ae8d0eda2374d19a..93309f63a841df0a5732ae66719914f88fcd124e 100644 (file)
@@ -198,6 +198,8 @@ class CyclesRender_PT_performance(CyclesButtonsPanel, Panel):
         sub.prop(rd, "parts_x", text="X")
         sub.prop(rd, "parts_y", text="Y")
 
+        sub.prop(cscene, "use_progressive_refine")
+
         subsub = sub.column()
         subsub.enabled = not rd.use_border
         subsub.prop(rd, "use_save_buffers")
index 7b80c520e726131929db67110f6e3cfbd43cc2dc..0a09102bd4fe938741f3dad90cc7186f0090f119 100644 (file)
@@ -466,9 +466,12 @@ void BlenderSession::get_progress(float& progress, double& total_time)
        session->progress.get_tile(tile, total_time, tile_time);
 
        sample = session->progress.get_sample();
-       samples_per_tile = session->tile_manager.state.num_samples;
+       samples_per_tile = session->params.samples;
 
-       progress = ((float)sample/(float)(tile_total * samples_per_tile));
+       if(samples_per_tile)
+               progress = ((float)sample/(float)(tile_total * samples_per_tile));
+       else
+               progress = 0.0;
 }
 
 void BlenderSession::update_status_progress()
index 3d36eba0c4bde76a7e6439cce5fde4f23243e43f..00130f357dd26c9182b8c69ae8e567fa12a944fe 100644 (file)
@@ -380,8 +380,14 @@ SessionParams BlenderSync::get_session_params(BL::RenderEngine b_engine, BL::Use
        params.reset_timeout = get_float(cscene, "debug_reset_timeout");
        params.text_timeout = get_float(cscene, "debug_text_timeout");
 
+       params.progressive_refine = get_boolean(cscene, "use_progressive_refine");
+
        if(background) {
-               params.progressive = false;
+               if(params.progressive_refine)
+                       params.progressive = true;
+               else
+                       params.progressive = false;
+
                params.start_resolution = INT_MAX;
        }
        else
index 4c54671b0d0a494bfae406db723ab4d2f7a5393d..b727a83d024cb05d6f29e11e0f26a2d5b368962b 100644 (file)
@@ -135,8 +135,10 @@ public:
 
        void thread_path_trace(DeviceTask& task)
        {
-               if(task_pool.cancelled())
-                       return;
+               if(task_pool.cancelled()) {
+                       if(task.need_finish_queue == false)
+                               return;
+               }
 
 #ifdef WITH_OSL
                if(kernel_osl_use(kg))
@@ -154,8 +156,10 @@ public:
 #ifdef WITH_OPTIMIZED_KERNEL
                        if(system_cpu_support_optimized()) {
                                for(int sample = start_sample; sample < end_sample; sample++) {
-                                       if (task.get_cancel() || task_pool.cancelled())
-                                               break;
+                                       if (task.get_cancel() || task_pool.cancelled()) {
+                                               if(task.need_finish_queue == false)
+                                                       break;
+                                       }
 
                                        for(int y = tile.y; y < tile.y + tile.h; y++) {
                                                for(int x = tile.x; x < tile.x + tile.w; x++) {
@@ -173,8 +177,10 @@ public:
 #endif
                        {
                                for(int sample = start_sample; sample < end_sample; sample++) {
-                                       if (task.get_cancel() || task_pool.cancelled())
-                                               break;
+                                       if (task.get_cancel() || task_pool.cancelled()) {
+                                               if(task.need_finish_queue == false)
+                                                       break;
+                                       }
 
                                        for(int y = tile.y; y < tile.y + tile.h; y++) {
                                                for(int x = tile.x; x < tile.x + tile.w; x++) {
@@ -191,8 +197,10 @@ public:
 
                        task.release_tile(tile);
 
-                       if(task_pool.cancelled())
-                               break;
+                       if(task_pool.cancelled()) {
+                               if(task.need_finish_queue == false)
+                                       break;
+                       }
                }
 
 #ifdef WITH_OSL
index c8dcfdc2f3d7cc4292d7997e89866f33df5b1a20..04b4cb0950a0be994305c06aa97f9c9109e0ffc3 100644 (file)
@@ -837,8 +837,10 @@ public:
                                int end_sample = tile.start_sample + tile.num_samples;
 
                                for(int sample = start_sample; sample < end_sample; sample++) {
-                                       if (task->get_cancel())
-                                               break;
+                                       if (task->get_cancel()) {
+                                               if(task->need_finish_queue == false)
+                                                       break;
+                                       }
 
                                        path_trace(tile, sample);
 
index 673ffdf79fd8a4fbf6ea1fe495448070242d0661..aa4f17ea3251883c69ca59ad0982295da6c00954 100644 (file)
@@ -686,8 +686,10 @@ public:
                                int end_sample = tile.start_sample + tile.num_samples;
 
                                for(int sample = start_sample; sample < end_sample; sample++) {
-                                       if (task->get_cancel())
-                                               break;
+                                       if (task->get_cancel()) {
+                                               if(task->need_finish_queue == false)
+                                                       break;
+                                       }
 
                                        path_trace(tile, sample);
 
index cfb3d8d988ec4adac6e0f6a70a3f3107dce459dd..8ca8b88ea49037b318efb231a58d37ee278ec372 100644 (file)
@@ -65,6 +65,7 @@ public:
        boost::function<void(RenderTile&)> release_tile;
        boost::function<bool(void)> get_cancel;
 
+       bool need_finish_queue;
 protected:
        double last_update_time;
 };
index cd410e4e0118a452e8183298dba5eb33593cbfba..65b20f1dd2cc45c4f56ef72f000863d224e5fae4 100644 (file)
@@ -96,6 +96,9 @@ Session::~Session()
                display->write(device, params.output_path);
        }
 
+       foreach(RenderBuffers *buffers, tile_buffers)
+               delete buffers;
+
        delete buffers;
        delete display;
        delete scene;
@@ -173,6 +176,8 @@ 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;
@@ -267,10 +272,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 */
@@ -313,8 +323,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);
 
@@ -338,7 +352,7 @@ bool Session::acquire_tile(Device *tile_device, RenderTile& rtile)
 
        /* in case of a permant 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,9 +374,35 @@ bool Session::acquire_tile(Device *tile_device, RenderTile& rtile)
 
        buffer_params.get_offset_stride(rtile.offset, rtile.stride);
 
-       /* allocate buffers */
        RenderBuffers *tilebuffers = new RenderBuffers(tile_device);
-       tilebuffers->reset(tile_device, buffer_params);
+
+       /* allocate buffers */
+       if(params.progressive_refine) {
+               int tile_x = rtile.x / params.tile_size.x;
+               int tile_y = rtile.y / params.tile_size.y;
+
+               int tile_index = tile_y * tile_manager.state.tile_w + tile_x;
+
+               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;
@@ -377,9 +417,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 +432,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 +445,8 @@ void Session::release_tile(RenderTile& rtile)
 
 void Session::run_cpu()
 {
+       bool tiles_written = false;
+
        {
                /* reset once to start */
                thread_scoped_lock reset_lock(delayed_reset.mutex);
@@ -502,10 +548,15 @@ void Session::run_cpu()
 
                        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()
@@ -722,6 +773,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);
 }
@@ -752,5 +804,24 @@ void Session::tonemap()
        display_outdated = false;
 }
 
-CCL_NAMESPACE_END
+bool Session::update_progressive_refine(bool cancel)
+{
+       int sample = tile_manager.state.sample + 1;
+
+       if(params.progressive_refine) {
+               foreach(RenderBuffers *buffers, tile_buffers) {
+                       RenderTile rtile;
+                       rtile.buffers = buffers;
+                       rtile.sample = sample;
+
+                       if(rtile.sample == params.samples || cancel)
+                               write_render_tile_cb(rtile);
+                       else
+                               update_render_tile_cb(rtile);
+               }
+       }
+
+       return sample == params.samples;
+}
 
+CCL_NAMESPACE_END
index eda8b3da60e5bbbc2a7b920cb41be0a53d4f1ac4..dc3e7504766053e87bbd0482e5797e17e316fab0 100644 (file)
@@ -25,6 +25,7 @@
 
 #include "util_progress.h"
 #include "util_thread.h"
+#include "util_vector.h"
 
 CCL_NAMESPACE_BEGIN
 
@@ -42,6 +43,7 @@ class SessionParams {
 public:
        DeviceInfo device;
        bool background;
+       bool progressive_refine;
        string output_path;
 
        bool progressive;
@@ -58,6 +60,7 @@ public:
        SessionParams()
        {
                background = false;
+               progressive_refine = false;
                output_path = "";
 
                progressive = false;
@@ -76,6 +79,7 @@ public:
        { return !(device.type == params.device.type
                && device.id == params.device.id
                && background == params.background
+               && progressive_refine == params.progressive_refine
                && output_path == params.output_path
                /* && samples == params.samples */
                && progressive == params.progressive
@@ -173,6 +177,11 @@ protected:
        double reset_time;
        double preview_time;
        double paused_time;
+
+       /* progressive refine */
+       bool update_progressive_refine(bool cancel);
+
+       vector<RenderBuffers *> tile_buffers;
 };
 
 CCL_NAMESPACE_END
index 874f1a6b3aa8e3a8ba461f1a2dc8aa0e3da8872a..02c0ee640a3657f3c41db98cb57c610f7e5fab43 100644 (file)
@@ -102,6 +102,8 @@ void TileManager::set_tiles()
        }
 
        state.num_tiles = state.tiles.size();
+       state.tile_w = (tile_size.x >= image_w) ? 1 : (image_w + tile_size.x - 1) / tile_size.x;
+       state.tile_h = (tile_size.y >= image_h) ? 1 : (image_h + tile_size.y - 1) / tile_size.y;
 
        state.buffer.width = image_w;
        state.buffer.height = image_h;
index d820f74e3bd96512be0511bedd9f5ccb50aa593f..587dfbe4f1a911c2568675453b6a8fecebf3fa3f 100644 (file)
@@ -54,6 +54,8 @@ public:
                int resolution_divider;
                int num_tiles;
                int num_rendered_tiles;
+               int tile_w;
+               int tile_h;
                list<Tile> tiles;
        } state;
 
index c003a86a0b789268cb027720902625109608e1d0..9a1ea15da97113dadf014e764b5df0432efbe4d5 100644 (file)
@@ -2617,11 +2617,21 @@ static ImBuf *image_get_render_result(Image *ima, ImageUser *iuser, void **lock_
        /* free rect buffer if float buffer changes, so it can be recreated with
         * the updated result, and also in case we got byte buffer from sequencer,
         * so we don't keep reference to freed buffer */
-       if (ibuf->rect_float != rectf || rect || !rectf)
+       if (ibuf->rect_float != rectf || rect)
                imb_freerectImBuf(ibuf);
 
-       if (rect)
+       if (rect) {
                ibuf->rect = rect;
+       }
+       else {
+               /* byte buffer of render result has been freed, make sure image buffers
+                * does not reference to this buffer anymore
+                * need check for whether byte buffer was allocated and owned by image itself
+                * or if it's reusing buffer from render result
+                */
+               if ((ibuf->mall & IB_rect) == 0)
+                       ibuf->rect = NULL;
+       }
 
        if (rectf) {
                ibuf->rect_float = rectf;
index d9ca131721fe03bcfd936e37abf4b1e27a29c2c9..55a001530ee030c4ef0c56c171bae9019c610fb1 100644 (file)
@@ -107,7 +107,7 @@ void ViewerBaseOperation:: updateImage(rcti *rect)
 {
        IMB_partial_display_buffer_update(this->m_ibuf, this->m_outputBuffer, NULL, getWidth(), 0, 0,
                                          this->m_viewSettings, this->m_displaySettings,
-                                         rect->xmin, rect->ymin, rect->xmax, rect->ymax);
+                                         rect->xmin, rect->ymin, rect->xmax, rect->ymax, FALSE);
 
        WM_main_add_notifier(NC_WINDOW | ND_DRAW, NULL);
 }
index c08ea2b6429d31de0aa7f6c8351c9c28384437b8..b61280f14cee1472fe7c9b4e7b04103c2d815a46 100644 (file)
@@ -80,7 +80,6 @@ void image_buffer_rect_update(Scene *scene, RenderResult *rr, ImBuf *ibuf, volat
        float *rectf = NULL;
        int ymin, ymax, xmin, xmax;
        int rymin, rxmin;
-       /* unsigned char *rectc; */  /* UNUSED */
 
        /* if renrect argument, we only refresh scanlines */
        if (renrect) {
@@ -143,11 +142,10 @@ void image_buffer_rect_update(Scene *scene, RenderResult *rr, ImBuf *ibuf, volat
                imb_addrectImBuf(ibuf);
        
        rectf += 4 * (rr->rectx * ymin + xmin);
-       /* rectc = (unsigned char *)(ibuf->rect + ibuf->x * rymin + rxmin); */  /* UNUSED */
 
        IMB_partial_display_buffer_update(ibuf, rectf, NULL, rr->rectx, rxmin, rymin,
                                          &scene->view_settings, &scene->display_settings,
-                                         rxmin, rymin, rxmin + xmax, rymin + ymax);
+                                         rxmin, rymin, rxmin + xmax, rymin + ymax, TRUE);
 }
 
 /* ****************************** render invoking ***************** */
index f60d34428b37fdd04f92a8827f9d7ed2dd3478ba..480d4ee6ac1ea8d98441bedd1f2e6da33cb31380 100644 (file)
@@ -4249,7 +4249,7 @@ static void imapaint_image_update(Scene *scene, SpaceImage *sima, Image *image,
                IMB_partial_display_buffer_update(ibuf, ibuf->rect_float, (unsigned char *) ibuf->rect, ibuf->x, 0, 0,
                                                  &scene->view_settings, &scene->display_settings,
                                                  imapaintpartial.x1, imapaintpartial.y1,
-                                                 imapaintpartial.x2, imapaintpartial.y2);
+                                                 imapaintpartial.x2, imapaintpartial.y2, FALSE);
        }
        else {
                ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID;
index e2604241cafe1d38648999318984f87a18fe0fc8..0653956e1130d035f61ef0e9b7f492a18039f102 100644 (file)
@@ -130,7 +130,8 @@ void IMB_colormanagement_colorspace_items_add(struct EnumPropertyItem **items, i
 void IMB_partial_display_buffer_update(struct ImBuf *ibuf, const float *linear_buffer, const unsigned char *buffer_byte,
                                        int stride, int offset_x, int offset_y, const struct ColorManagedViewSettings *view_settings,
                                        const struct ColorManagedDisplaySettings *display_settings,
-                                       int xmin, int ymin, int xmax, int ymax);
+                                       int xmin, int ymin, int xmax, int ymax,
+                                       int update_orig_byte_buffer);
 
 /* ** Pixel processor functions ** */
 struct ColormanageProcessor *IMB_colormanagement_display_processor_new(const struct ColorManagedViewSettings *view_settings,
index 37510c10e9a55691743c5840c02dc97ab53f604d..40b07c02cd7aff8dfbc6c303deb178c29116c13f 100644 (file)
@@ -2382,9 +2382,9 @@ static void partial_buffer_update_rect(ImBuf *ibuf, unsigned char *display_buffe
 void IMB_partial_display_buffer_update(ImBuf *ibuf, const float *linear_buffer, const unsigned char *byte_buffer,
                                        int stride, int offset_x, int offset_y, const ColorManagedViewSettings *view_settings,
                                        const ColorManagedDisplaySettings *display_settings,
-                                       int xmin, int ymin, int xmax, int ymax)
+                                       int xmin, int ymin, int xmax, int ymax, int update_orig_byte_buffer)
 {
-       if (ibuf->rect && ibuf->rect_float) {
+       if ((ibuf->rect && ibuf->rect_float) || update_orig_byte_buffer)  {
                /* update byte buffer created by legacy color management */
 
                unsigned char *rect = (unsigned char *) ibuf->rect;