Cycles: Refactor Progress system to provide better estimates
authorLukas Stockner <lukas.stockner@freenet.de>
Sat, 26 Nov 2016 03:22:34 +0000 (04:22 +0100)
committerLukas Stockner <lukas.stockner@freenet.de>
Sat, 3 Dec 2016 04:02:21 +0000 (05:02 +0100)
The Progress system in Cycles had two limitations so far:
 - It just counted tiles, but ignored their size. For example, when rendering a 600x500 image with 512x512 tiles, the right 88x500 tile would count for 50% of the progress, although it only covers 15% of the image.
 - Scene update time was incorrectly counted as rendering time - therefore, the remaining time started very long and gradually decreased.

This patch fixes both problems:
First of all, the Progress now has a function to ignore time spans, and that is used to ignore scene update time.
The larger change is the tile size: Instead of counting samples per tile, so that the final value is num_samples*num_tiles, the code now counts every sample for every pixel, so that the final value is num_samples*num_pixels.

Along with that, some unused variables were removed from the Progress and Session classes.

Reviewers: brecht, sergey, #cycles

Subscribers: brecht, candreacchio, sergey

Differential Revision: https://developer.blender.org/D2214

19 files changed:
intern/cycles/app/cycles_standalone.cpp
intern/cycles/blender/blender_session.cpp
intern/cycles/device/device.h
intern/cycles/device/device_cpu.cpp
intern/cycles/device/device_cuda.cpp
intern/cycles/device/device_multi.cpp
intern/cycles/device/device_network.cpp
intern/cycles/device/device_task.cpp
intern/cycles/device/device_task.h
intern/cycles/device/opencl/opencl_mega.cpp
intern/cycles/device/opencl/opencl_split.cpp
intern/cycles/render/bake.cpp
intern/cycles/render/bake.h
intern/cycles/render/session.cpp
intern/cycles/render/session.h
intern/cycles/render/tile.cpp
intern/cycles/render/tile.h
intern/cycles/util/util_progress.h
intern/cycles/util/util_time.h

index b21e8630cdbb45079067ce1fbdcb280fa9ea6f73..9816d614a7cb0e002d3913094a52ff61b0897c18 100644 (file)
@@ -72,20 +72,17 @@ static void session_print(const string& str)
 
 static void session_print_status()
 {
-       int sample, tile;
-       double total_time, sample_time, render_time;
        string status, substatus;
 
        /* get status */
-       sample = options.session->progress.get_sample();
-       options.session->progress.get_tile(tile, total_time, sample_time, render_time);
+       float progress = options.session->progress.get_progress();
        options.session->progress.get_status(status, substatus);
 
        if(substatus != "")
                status += ": " + substatus;
 
        /* print status */
-       status = string_printf("Sample %d   %s", sample, status.c_str());
+       status = string_printf("Progress %05.2f   %s", (double) progress*100, status.c_str());
        session_print(status);
 }
 
@@ -167,13 +164,12 @@ static void display_info(Progress& progress)
        latency = (elapsed - last);
        last = elapsed;
 
-       int sample, tile;
-       double total_time, sample_time, render_time;
+       double total_time, sample_time;
        string status, substatus;
 
-       sample = progress.get_sample();
-       progress.get_tile(tile, total_time, sample_time, render_time);
+       progress.get_time(total_time, sample_time);
        progress.get_status(status, substatus);
+       float progress_val = progress.get_progress();
 
        if(substatus != "")
                status += ": " + substatus;
@@ -184,10 +180,10 @@ static void display_info(Progress& progress)
                "%s"
                "        Time: %.2f"
                "        Latency: %.4f"
-               "        Sample: %d"
+               "        Progress: %05.2f"
                "        Average: %.4f"
                "        Interactive: %s",
-               status.c_str(), total_time, latency, sample, sample_time, interactive.c_str());
+               status.c_str(), total_time, latency, (double) progress_val*100, sample_time, interactive.c_str());
 
        view_display_info(str.c_str());
 
index e16cea0ebaf4643d9538a701a1713b69d4655825..71c1eefe65f6232a2da556362effd2b85f5ddb2b 100644 (file)
@@ -930,38 +930,13 @@ void BlenderSession::get_status(string& status, string& substatus)
 
 void BlenderSession::get_progress(float& progress, double& total_time, double& render_time)
 {
-       double tile_time;
-       int tile, sample, samples_per_tile;
-       int tile_total = session->tile_manager.state.num_tiles;
-       int samples = session->tile_manager.state.sample + 1;
-       int total_samples = session->tile_manager.get_num_effective_samples();
-
-       session->progress.get_tile(tile, total_time, render_time, tile_time);
-
-       sample = session->progress.get_sample();
-       samples_per_tile = session->tile_manager.get_num_effective_samples();
-
-       if(background && samples_per_tile && tile_total)
-               progress = ((float)sample / (float)(tile_total * samples_per_tile));
-       else if(!background && samples > 0 && total_samples != INT_MAX)
-               progress = ((float)samples) / total_samples;
-       else
-               progress = 0.0;
+       session->progress.get_time(total_time, render_time);
+       progress = session->progress.get_progress();
 }
 
 void BlenderSession::update_bake_progress()
 {
-       float progress;
-       int sample, samples_per_task, parts_total;
-
-       sample = session->progress.get_sample();
-       samples_per_task = scene->bake_manager->num_samples;
-       parts_total = scene->bake_manager->num_parts;
-
-       if(samples_per_task)
-               progress = ((float)sample / (float)(parts_total * samples_per_task));
-       else
-               progress = 0.0;
+       float progress = session->progress.get_progress();
 
        if(progress != last_progress) {
                b_engine.update_progress(progress);
index b9bdffa2618830f5b129217b1543fbce0eb3550f..988ad10607dde4926bb73902e89fba9d83dbba7c 100644 (file)
@@ -220,6 +220,7 @@ public:
        DeviceInfo info;
        virtual const string& error_message() { return error_msg; }
        bool have_error() { return !error_message().empty(); }
+       virtual bool show_samples() const { return false; }
 
        /* statistics */
        Stats &stats;
index aed86d8d85377c2825484188099689aa0b4c88cf..c8e001ec2fd99eb6e3c034bc20a46627304fbb7a 100644 (file)
@@ -112,6 +112,11 @@ public:
                task_pool.stop();
        }
 
+       virtual bool show_samples() const
+       {
+               return (TaskScheduler::num_threads() == 1);
+       }
+
        void mem_alloc(device_memory& mem, MemoryType /*type*/)
        {
                mem.device_pointer = mem.data_pointer;
@@ -275,7 +280,7 @@ public:
 
                                tile.sample = sample + 1;
 
-                               task.update_progress(&tile);
+                               task.update_progress(&tile, tile.w*tile.h);
                        }
 
                        task.release_tile(tile);
index fbb97f78e70f7d36696feb82ee2c985fd4337425..233f94be1bf9662b970e9eab3002a52aba471747 100644 (file)
@@ -115,6 +115,12 @@ public:
                return path_exists(cubins_path);
        }
 
+       virtual bool show_samples() const
+       {
+               /* The CUDADevice only processes one tile at a time, so showing samples is fine. */
+               return true;
+       }
+
 /*#ifdef NDEBUG
 #define cuda_abort()
 #else
@@ -1267,7 +1273,7 @@ public:
 
                                        tile.sample = sample + 1;
 
-                                       task->update_progress(&tile);
+                                       task->update_progress(&tile, tile.w*tile.h);
                                }
 
                                task->release_tile(tile);
index 48fd159d50888f0149643ff507db82533d869fbd..31b800640d390d93c7b0ae684edd823392dba1ac 100644 (file)
@@ -89,6 +89,14 @@ public:
                return error_msg;
        }
 
+       virtual bool show_samples() const
+       {
+               if(devices.size() > 1) {
+                       return false;
+               }
+               return devices.front().device->show_samples();
+       }
+
        bool load_kernels(const DeviceRequestedFeatures& requested_features)
        {
                foreach(SubDevice& sub, devices)
index 3eb5ad2d2dbb63f8aa7ea9439b4c14681d601f41..53eef6cf1999cf717410c3593c326df8df5ca08f 100644 (file)
@@ -51,6 +51,11 @@ public:
 
        thread_mutex rpc_lock;
 
+       virtual bool show_samples() const
+       {
+               return false;
+       }
+
        NetworkDevice(DeviceInfo& info, Stats &stats, const char *address)
        : Device(info, stats, true), socket(io_service)
        {
index 1f1128a28f858fdd23326de3ba459c6350328a6e..48d18035c13727c1debcfeb509b78b903a2e15c9 100644 (file)
@@ -19,6 +19,8 @@
 
 #include "device_task.h"
 
+#include "buffers.h"
+
 #include "util_algorithm.h"
 #include "util_time.h"
 
@@ -99,14 +101,18 @@ void DeviceTask::split(list<DeviceTask>& tasks, int num, int max_size)
        }
 }
 
-void DeviceTask::update_progress(RenderTile *rtile)
+void DeviceTask::update_progress(RenderTile *rtile, int pixel_samples)
 {
        if((type != PATH_TRACE) &&
           (type != SHADER))
                return;
 
-       if(update_progress_sample)
-               update_progress_sample();
+       if(update_progress_sample) {
+               if(pixel_samples == -1) {
+                       pixel_samples = shader_w;
+               }
+               update_progress_sample(pixel_samples, rtile? rtile->sample : 0);
+       }
 
        if(update_tile_sample) {
                double current_time = time_dt();
index 8423e83bdfd14687ad2c04094278dddacb43e09c..8bd54c3d2b0a88721b618c39dd7ec4b0ced1eba1 100644 (file)
@@ -56,10 +56,10 @@ public:
        int get_subtask_count(int num, int max_size = 0);
        void split(list<DeviceTask>& tasks, int num, int max_size = 0);
 
-       void update_progress(RenderTile *rtile);
+       void update_progress(RenderTile *rtile, int pixel_samples = -1);
 
        function<bool(Device *device, RenderTile&)> acquire_tile;
-       function<void(void)> update_progress_sample;
+       function<void(long, int)> update_progress_sample;
        function<void(RenderTile&)> update_tile_sample;
        function<void(RenderTile&)> release_tile;
        function<bool(void)> get_cancel;
index 369c086df570d85656d0577339865617a05c311f..6ea7619e022ab9e036497bdeb4230a0b2d4b57ac 100644 (file)
@@ -39,6 +39,10 @@ public:
        {
        }
 
+       virtual bool show_samples() const {
+               return true;
+       }
+
        virtual void load_kernels(const DeviceRequestedFeatures& /*requested_features*/,
                                  vector<OpenCLProgram*> &programs)
        {
@@ -120,7 +124,7 @@ public:
 
                                        tile.sample = sample + 1;
 
-                                       task->update_progress(&tile);
+                                       task->update_progress(&tile, tile.w*tile.h);
                                }
 
                                /* Complete kernel execution before release tile */
index 239e73a40fdd8110ee1fc4a9593907f709764bee..3c3c21501281abb7938b5e2858f72464ffab4870 100644 (file)
@@ -247,6 +247,10 @@ public:
                }
        }
 
+       virtual bool show_samples() const {
+               return false;
+       }
+
        /* Split kernel utility functions. */
        size_t get_tex_size(const char *tex_name)
        {
index 13310a61761b4d645a3e3bba813b5e792a67b8bc..d9a297002c6e5cddfa7e635927a9c15cf2eb3d44 100644 (file)
@@ -135,20 +135,16 @@ bool BakeManager::bake(Device *device, DeviceScene *dscene, Scene *scene, Progre
 {
        size_t num_pixels = bake_data->size();
 
-       progress.reset_sample();
-       this->num_parts = 0;
+       int num_samples = is_aa_pass(shader_type)? scene->integrator->aa_samples : 1;
 
-       /* calculate the total parts for the progress bar */
+       /* calculate the total pixel samples for the progress bar */
+       total_pixel_samples = 0;
        for(size_t shader_offset = 0; shader_offset < num_pixels; shader_offset += m_shader_limit) {
                size_t shader_size = (size_t)fminf(num_pixels - shader_offset, m_shader_limit);
-
-               DeviceTask task(DeviceTask::SHADER);
-               task.shader_w = shader_size;
-
-               this->num_parts += device->get_split_task_count(task);
+               total_pixel_samples += shader_size * num_samples;
        }
-
-       this->num_samples = is_aa_pass(shader_type)? scene->integrator->aa_samples : 1;
+       progress.reset_sample();
+       progress.set_total_pixel_samples(total_pixel_samples);
 
        for(size_t shader_offset = 0; shader_offset < num_pixels; shader_offset += m_shader_limit) {
                size_t shader_size = (size_t)fminf(num_pixels - shader_offset, m_shader_limit);
@@ -187,9 +183,9 @@ bool BakeManager::bake(Device *device, DeviceScene *dscene, Scene *scene, Progre
                task.shader_x = 0;
                task.offset = shader_offset;
                task.shader_w = d_output.size();
-               task.num_samples = this->num_samples;
+               task.num_samples = num_samples;
                task.get_cancel = function_bind(&Progress::get_cancel, &progress);
-               task.update_progress_sample = function_bind(&Progress::increment_sample_update, &progress);
+               task.update_progress_sample = function_bind(&Progress::add_samples_update, &progress, _1, _2);
 
                device->task_add(task);
                device->task_wait();
index 8377e3871976e900580067931f073b90b2d17a0e..25f5eb3c8970e5413d979a8531dce11c70924bba 100644 (file)
@@ -73,8 +73,7 @@ public:
 
        bool need_update;
 
-       int num_samples;
-       int num_parts;
+       int total_pixel_samples;
 
 private:
        BakeData *m_bake_data;
index 9d8c9fed7af4bac86b41eb027fc697160cc8622e..8e90224321135bd1ba0ba768f70457c7186593b2 100644 (file)
@@ -67,10 +67,7 @@ Session::Session(const SessionParams& params_)
        session_thread = NULL;
        scene = NULL;
 
-       start_time = 0.0;
        reset_time = 0.0;
-       preview_time = 0.0;
-       paused_time = 0.0;
        last_update_time = 0.0;
 
        delayed_reset.do_reset = false;
@@ -201,12 +198,10 @@ 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();
 
-       progress.set_render_start_time(start_time + paused_time);
+       progress.set_render_start_time();
 
        while(!progress.get_cancel()) {
                /* advance to next tile */
@@ -233,13 +228,9 @@ void Session::run_gpu()
                                update_status_time(pause, no_tiles);
 
                                while(1) {
-                                       double pause_start = time_dt();
+                                       scoped_timer pause_timer;
                                        pause_cond.wait(pause_lock);
-                                       paused_time += time_dt() - pause_start;
-
-                                       if(!params.background)
-                                               progress.set_start_time(start_time + paused_time);
-                                       progress.set_render_start_time(start_time + paused_time);
+                                       progress.add_skip_time(pause_timer, params.background);
 
                                        update_status_time(pause, no_tiles);
                                        progress.set_update();
@@ -255,7 +246,9 @@ void Session::run_gpu()
 
                if(!no_tiles) {
                        /* update scene */
+                       scoped_timer update_timer;
                        update_scene();
+                       progress.add_skip_time(update_timer, params.background);
 
                        if(!device->error_message().empty())
                                progress.set_error(device->error_message());
@@ -523,13 +516,9 @@ void Session::run_cpu()
                                update_status_time(pause, no_tiles);
 
                                while(1) {
-                                       double pause_start = time_dt();
+                                       scoped_timer pause_timer;
                                        pause_cond.wait(pause_lock);
-                                       paused_time += time_dt() - pause_start;
-
-                                       if(!params.background)
-                                               progress.set_start_time(start_time + paused_time);
-                                       progress.set_render_start_time(start_time + paused_time);
+                                       progress.add_skip_time(pause_timer, params.background);
 
                                        update_status_time(pause, no_tiles);
                                        progress.set_update();
@@ -550,7 +539,9 @@ void Session::run_cpu()
                        thread_scoped_lock buffers_lock(buffers_mutex);
 
                        /* update scene */
+                       scoped_timer update_timer;
                        update_scene();
+                       progress.add_skip_time(update_timer, params.background);
 
                        if(!device->error_message().empty())
                                progress.set_error(device->error_message());
@@ -718,14 +709,14 @@ void Session::reset_(BufferParams& buffer_params, int samples)
        }
 
        tile_manager.reset(buffer_params, samples);
+       progress.reset_sample();
 
-       start_time = time_dt();
-       preview_time = 0.0;
-       paused_time = 0.0;
+       bool show_progress = params.background || tile_manager.get_num_effective_samples() != INT_MAX;
+       progress.set_total_pixel_samples(show_progress? tile_manager.state.total_pixel_samples : 0);
 
        if(!params.background)
-               progress.set_start_time(start_time);
-       progress.set_render_start_time(start_time);
+               progress.set_start_time();
+       progress.set_render_start_time();
 }
 
 void Session::reset(BufferParams& buffer_params, int samples)
@@ -827,61 +818,40 @@ 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_divider;
-       int num_tiles = tile_manager.state.num_tiles;
+       int progressive_sample = tile_manager.state.sample;
+       int num_samples = tile_manager.get_num_effective_samples();
+
        int tile = tile_manager.state.num_rendered_tiles;
+       int num_tiles = tile_manager.state.num_tiles;
 
        /* update status */
        string status, substatus;
 
        if(!params.progressive) {
-               const int progress_sample = progress.get_sample(),
-                         num_samples = tile_manager.get_num_effective_samples();
-               const bool is_gpu = params.device.type == DEVICE_CUDA || params.device.type == DEVICE_OPENCL;
-               const bool is_multidevice = params.device.multi_devices.size() > 1;
                const bool is_cpu = params.device.type == DEVICE_CPU;
-               const bool is_last_tile = (num_samples * num_tiles - progress_sample) < num_samples;
+               const bool is_last_tile = (progress.get_finished_tiles() + 1) == num_tiles;
 
                substatus = string_printf("Path Tracing Tile %d/%d", tile, num_tiles);
 
-               if((is_gpu && !is_multidevice && !device->info.use_split_kernel) ||
-                  (is_cpu && (num_tiles == 1 || is_last_tile)))
+               if(device->show_samples() || (is_cpu && is_last_tile))
                {
-                       /* When using split-kernel (OpenCL) each thread in a tile will be working on a different
-                        * sample. Can't display sample number when device uses split-kernel
+                       /* Some devices automatically support showing the sample number:
+                        * - CUDADevice
+                        * - OpenCLDevice when using the megakernel (the split kernel renders multiple samples at the same time, so the current sample isn't really defined)
+                        * - CPUDevice when using one thread
+                        * For these devices, the current sample is always shown.
+                        *
+                        * The other option is when the last tile is currently being rendered by the CPU.
                         */
-
-                       /* 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
-                        * this helps a lot from feedback point of view.
-                        * also display the info on CPU, when using 1 tile only
-                        */
-
-                       int status_sample = progress_sample;
-                       if(tile > 1) {
-                               /* sample counter is global for all tiles, subtract samples
-                                * from already finished tiles to get sample counter for
-                                * current tile only
-                                */
-                               if(is_cpu && is_last_tile && num_tiles > 1) {
-                                       status_sample = num_samples - (num_samples * num_tiles - progress_sample);
-                               }
-                               else {
-                                       status_sample -= (tile - 1) * num_samples;
-                               }
-                       }
-
-                       substatus += string_printf(", Sample %d/%d", status_sample, num_samples);
+                       substatus += string_printf(", Sample %d/%d", progress.get_current_sample(), num_samples);
                }
        }
        else if(tile_manager.num_samples == INT_MAX)
-               substatus = string_printf("Path Tracing Sample %d", sample+1);
+               substatus = string_printf("Path Tracing Sample %d", progressive_sample+1);
        else
                substatus = string_printf("Path Tracing Sample %d/%d",
-                                         sample+1,
-                                         tile_manager.get_num_effective_samples());
+                                         progressive_sample+1,
+                                         num_samples);
        
        if(show_pause) {
                status = "Paused";
@@ -895,22 +865,6 @@ void Session::update_status_time(bool show_pause, bool show_done)
        }
 
        progress.set_status(status, substatus);
-
-       /* update timing */
-       if(preview_time == 0.0 && resolution == 1)
-               preview_time = time_dt();
-       
-       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;
-
-       progress.set_tile(tile, tile_time);
-}
-
-void Session::update_progress_sample()
-{
-       progress.increment_sample();
 }
 
 void Session::path_trace()
@@ -922,7 +876,7 @@ void Session::path_trace()
        task.release_tile = function_bind(&Session::release_tile, this, _1);
        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.update_progress_sample = function_bind(&Progress::add_samples, &this->progress, _1, _2);
        task.need_finish_queue = params.progressive_refine;
        task.integrator_branched = scene->integrator->method == Integrator::BRANCHED_PATH;
        task.requested_tile_size = params.tile_size;
index 1db4692e171a94436db26cb2b92631102a6233c8..c7ff1446171dda56ac7d862ca7eb2264f7006c81 100644 (file)
@@ -145,6 +145,10 @@ public:
 
        void device_free();
 
+       /* Returns the rendering progress or 0 if no progress can be determined
+        * (for example, when rendering with unlimited samples). */
+       float get_progress();
+
 protected:
        struct DelayedReset {
                thread_mutex mutex;
@@ -173,8 +177,6 @@ protected:
        void update_tile_sample(RenderTile& tile);
        void release_tile(RenderTile& tile);
 
-       void update_progress_sample();
-
        bool device_use_gl;
 
        thread *session_thread;
@@ -194,10 +196,7 @@ protected:
 
        bool kernels_loaded;
 
-       double start_time;
        double reset_time;
-       double preview_time;
-       double paused_time;
 
        /* progressive refine */
        double last_update_time;
index 3a6dfea11a737f50a14a05f461bd724337667f2a..e59d0c843a361bda26ed26b64bb866b9ef5bff1e 100644 (file)
@@ -108,36 +108,57 @@ TileManager::~TileManager()
 {
 }
 
-void TileManager::reset(BufferParams& params_, int num_samples_)
+static int get_divider(int w, int h, int start_resolution)
 {
-       params = params_;
-
        int divider = 1;
-       int w = params.width, h = params.height;
-
        if(start_resolution != INT_MAX) {
                while(w*h > start_resolution*start_resolution) {
                        w = max(1, w/2);
                        h = max(1, h/2);
 
-                       divider *= 2;
+                       divider <<= 1;
                }
        }
+       return divider;
+}
 
-       num_samples = num_samples_;
+void TileManager::reset(BufferParams& params_, int num_samples_)
+{
+       params = params_;
+
+       set_samples(num_samples_);
 
        state.buffer = BufferParams();
        state.sample = range_start_sample - 1;
        state.num_tiles = 0;
        state.num_rendered_tiles = 0;
        state.num_samples = 0;
-       state.resolution_divider = divider;
+       state.resolution_divider = get_divider(params.width, params.height, start_resolution);
        state.tiles.clear();
 }
 
 void TileManager::set_samples(int num_samples_)
 {
        num_samples = num_samples_;
+
+       /* No real progress indication is possible when using unlimited samples. */
+       if(num_samples == INT_MAX) {
+               state.total_pixel_samples = 0;
+       }
+       else {
+               uint64_t pixel_samples = 0;
+               /* While rendering in the viewport, the initial preview resolution is increased to the native resolution
+                * before the actual rendering begins. Therefore, additional pixel samples will be rendered. */
+               int divider = get_divider(params.width, params.height, start_resolution) / 2;
+               while(divider > 1) {
+                       int image_w = max(1, params.width/divider);
+                       int image_h = max(1, params.height/divider);
+                       pixel_samples += image_w * image_h;
+                       divider >>= 1;
+               }
+
+               state.total_pixel_samples = pixel_samples + get_num_effective_samples() * params.width*params.height;
+       }
 }
 
 /* If sliced is false, splits image into tiles and assigns equal amount of tiles to every render device.
index af1b1ed8b0f25a2acf715458f362ceb3cc8645b3..5d92ebac355772b5ab020f7382862b50d239b21e 100644 (file)
@@ -64,6 +64,10 @@ public:
                int resolution_divider;
                int num_tiles;
                int num_rendered_tiles;
+
+               /* Total samples over all pixels: Generally num_samples*num_pixels,
+                * but can be higher due to the initial resolution division for previews. */
+               uint64_t total_pixel_samples;
                /* This vector contains a list of tiles for every logical device in the session.
                 * In each list, the tiles are sorted according to the tile order setting. */
                vector<list<Tile> > tiles;
@@ -91,7 +95,7 @@ public:
        /* Number to samples in the rendering range. */
        int range_num_samples;
 
-       /* get number of actual samples to render. */
+       /* Get number of actual samples to render. */
        int get_num_effective_samples();
 protected:
 
index 4ae1d61dd17039946531123022a4ac4e7724ab2a..142150568405c36a02e7317f298151309b7f0ff6 100644 (file)
@@ -34,12 +34,12 @@ class Progress {
 public:
        Progress()
        {
-               tile = 0;
-               sample = 0;
+               pixel_samples = 0;
+               total_pixel_samples = 0;
+               current_tile_sample = 0;
+               finished_tiles = 0;
                start_time = time_dt();
-               total_time = 0.0;
-               render_time = 0.0;
-               tile_time = 0.0;
+               render_start_time = time_dt();
                status = "Initializing";
                substatus = "";
                sync_status = "";
@@ -62,22 +62,22 @@ public:
                thread_scoped_lock lock(progress.progress_mutex);
 
                progress.get_status(status, substatus);
-               progress.get_tile(tile, total_time, render_time, tile_time);
 
-               sample = progress.get_sample();
+               pixel_samples = progress.pixel_samples;
+               total_pixel_samples = progress.total_pixel_samples;
+               current_tile_sample = progress.get_current_sample();
 
                return *this;
        }
 
        void reset()
        {
-               tile = 0;
-               sample = 0;
+               pixel_samples = 0;
+               total_pixel_samples = 0;
+               current_tile_sample = 0;
+               finished_tiles = 0;
                start_time = time_dt();
                render_start_time = time_dt();
-               total_time = 0.0;
-               render_time = 0.0;
-               tile_time = 0.0;
                status = "Initializing";
                substatus = "";
                sync_status = "";
@@ -139,69 +139,93 @@ public:
 
        /* tile and timing information */
 
-       void set_start_time(double start_time_)
+       void set_start_time()
        {
                thread_scoped_lock lock(progress_mutex);
 
-               start_time = start_time_;
+               start_time = time_dt();
        }
 
-       void set_render_start_time(double render_start_time_)
+       void set_render_start_time()
        {
                thread_scoped_lock lock(progress_mutex);
 
-               render_start_time = render_start_time_;
+               render_start_time = time_dt();
        }
 
-       void set_tile(int tile_, double tile_time_)
+       void add_skip_time(const scoped_timer &start_timer, bool only_render)
        {
-               thread_scoped_lock lock(progress_mutex);
+               double skip_time = time_dt() - start_timer.get_start();
 
-               tile = tile_;
-               total_time = time_dt() - start_time;
-               render_time = time_dt() - render_start_time;
-               tile_time = tile_time_;
+               render_start_time += skip_time;
+               if(!only_render) {
+                       start_time += skip_time;
+               }
        }
 
-       void get_tile(int& tile_, double& total_time_, double& render_time_, double& tile_time_)
+       void get_time(double& total_time_, double& render_time_)
        {
                thread_scoped_lock lock(progress_mutex);
 
-               tile_ = tile;
-               total_time_ = (total_time > 0.0)? total_time: 0.0;
-               render_time_ = (render_time > 0.0)? render_time: 0.0;
-               tile_time_ = tile_time;
+               total_time_ = time_dt() - start_time;
+               render_time_ = time_dt() - render_start_time;
        }
 
-       void get_time(double& total_time_, double& render_time_)
+       void reset_sample()
        {
-               total_time_ = (total_time > 0.0)? total_time: 0.0;
-               render_time_ = (render_time > 0.0)? render_time: 0.0;
+               thread_scoped_lock lock(progress_mutex);
+
+               pixel_samples = 0;
+               current_tile_sample = 0;
+               finished_tiles = 0;
        }
 
-       void reset_sample()
+       void set_total_pixel_samples(uint64_t total_pixel_samples_)
        {
                thread_scoped_lock lock(progress_mutex);
 
-               sample = 0;
+               total_pixel_samples = total_pixel_samples_;
        }
 
-       void increment_sample()
+       float get_progress()
+       {
+               if(total_pixel_samples > 0) {
+                       return ((float) pixel_samples) / total_pixel_samples;
+               }
+               return 0.0f;
+       }
+
+       void add_samples(uint64_t pixel_samples_, int tile_sample)
        {
                thread_scoped_lock lock(progress_mutex);
 
-               sample++;
+               pixel_samples += pixel_samples_;
+               current_tile_sample = tile_sample;
        }
 
-       void increment_sample_update()
+       void add_samples_update(uint64_t pixel_samples_, int tile_sample)
        {
-               increment_sample();
+               add_samples(pixel_samples_, tile_sample);
                set_update();
        }
 
-       int get_sample()
+       void add_finished_tile()
+       {
+               thread_scoped_lock lock(progress_mutex);
+
+               finished_tiles++;
+       }
+
+       int get_current_sample()
+       {
+               /* Note that the value here always belongs to the last tile that updated,
+                * so it's only useful if there is only one active tile. */
+               return current_tile_sample;
+       }
+
+       int get_finished_tiles()
        {
-               return sample;
+               return finished_tiles;
        }
 
        /* status messages */
@@ -212,8 +236,6 @@ public:
                        thread_scoped_lock lock(progress_mutex);
                        status = status_;
                        substatus = substatus_;
-                       total_time = time_dt() - start_time;
-                       render_time = time_dt() - render_start_time;
                }
 
                set_update();
@@ -224,8 +246,6 @@ public:
                {
                        thread_scoped_lock lock(progress_mutex);
                        substatus = substatus_;
-                       total_time = time_dt() - start_time;
-                       render_time = time_dt() - render_start_time;
                }
 
                set_update();
@@ -237,8 +257,6 @@ public:
                        thread_scoped_lock lock(progress_mutex);
                        sync_status = status_;
                        sync_substatus = substatus_;
-                       total_time = time_dt() - start_time;
-                       render_time = time_dt() - render_start_time;
                }
 
                set_update();
@@ -250,8 +268,6 @@ public:
                {
                        thread_scoped_lock lock(progress_mutex);
                        sync_substatus = substatus_;
-                       total_time = time_dt() - start_time;
-                       render_time = time_dt() - render_start_time;
                }
 
                set_update();
@@ -292,12 +308,19 @@ protected:
        function<void(void)> update_cb;
        function<void(void)> cancel_cb;
 
-       int tile;    /* counter for rendered tiles */
-       int sample;  /* counter of rendered samples, global for all tiles */
+       /* pixel_samples counts how many samples have been rendered over all pixel, not just per pixel.
+        * This makes the progress estimate more accurate when tiles with different sizes are used.
+        *
+        * total_pixel_samples is the total amount of pixel samples that will be rendered. */
+       uint64_t pixel_samples, total_pixel_samples;
+       /* Stores the current sample count of the last tile that called the update function.
+        * It's used to display the sample count if only one tile is active. */
+       int current_tile_sample;
+       /* Stores the number of tiles that's already finished.
+        * Used to determine whether all but the last tile are finished rendering, in which case the current_tile_sample is displayed. */
+       int finished_tiles;
 
        double start_time, render_start_time;
-       double total_time, render_time;
-       double tile_time;
 
        string status;
        string substatus;
index a5b074bffa0958378751f7fc1fe86a2252b49fbc..65798244111d0e6b1b77e094321aaca32b5e6f92 100644 (file)
@@ -29,7 +29,7 @@ void time_sleep(double t);
 
 class scoped_timer {
 public:
-       explicit scoped_timer(double *value) : value_(value)
+       explicit scoped_timer(double *value = NULL) : value_(value)
        {
                time_start_ = time_dt();
        }
@@ -40,6 +40,12 @@ public:
                        *value_ = time_dt() - time_start_;
                }
        }
+
+       double get_start() const
+       {
+               return time_start_;
+       }
+
 protected:
        double *value_;
        double time_start_;