[Bf-blender-cvs] [e3ca9ccc98a] cycles-x: Cycles X: Initial support for the constant time rendering

Sergey Sharybin noreply at git.blender.org
Thu Jun 17 17:11:44 CEST 2021


Commit: e3ca9ccc98af408d8bb7ef040261cd71479b5081
Author: Sergey Sharybin
Date:   Thu Jun 17 16:13:08 2021 +0200
Branches: cycles-x
https://developer.blender.org/rBe3ca9ccc98af408d8bb7ef040261cd71479b5081

Cycles X: Initial support for the constant time rendering

Allows to limit render time, so that rendering will either happen
until all samples are finished, or the given time limit is reached.

Currently is only implemented for background and headless renders,
as those seems to benefit the most. In the viewport would still
like to investigate idea of keeping rendering until desired noise
floor is reached before adding extra settings there.

The usecases for the non-interactive (background, headless) renders
includes:

- Rendering a shot previews for movies.
- Ensuring some "bogus" frames do not stall the render farm.
- Allows to implement benchmark which will work reliable for both
  very powerful and weak hardware configurations (as an example,
  top-end GPUs and mobile GPUs) without requiring to wait forever
  on a non-powerful hardware.

One of the tricky parts which is not solved in this version yet is
support of constant time rendering with multiple big tiles. However,
support of big tiles will be an addition on top of the existing code
as some sort of time limit would need to be enforced for every tile
(in other words, support of constant time render for multiple tiles
will be implemented on a higher level).

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

===================================================================

M	intern/cycles/blender/addon/properties.py
M	intern/cycles/blender/addon/ui.py
M	intern/cycles/blender/blender_session.cpp
M	intern/cycles/blender/blender_sync.cpp
M	intern/cycles/integrator/render_scheduler.cpp
M	intern/cycles/integrator/render_scheduler.h
M	intern/cycles/render/session.cpp
M	intern/cycles/render/session.h

===================================================================

diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py
index 64990f1653b..6afce0829f8 100644
--- a/intern/cycles/blender/addon/properties.py
+++ b/intern/cycles/blender/addon/properties.py
@@ -279,6 +279,15 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
         default=32,
     )
 
+    # TODO: Use proper subtype to show units in the UI.
+    time_limit: FloatProperty(
+        name="Time Limit",
+        description="Limit the render time (excluding synchronization time)."
+        "Zero disables the limit",
+        min=0.0,
+        default=0.0,
+    )
+
     sampling_pattern: EnumProperty(
         name="Sampling Pattern",
         description="Random sampling pattern used by the integrator",
diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py
index 140b781d505..4d6418ed84a 100644
--- a/intern/cycles/blender/addon/ui.py
+++ b/intern/cycles/blender/addon/ui.py
@@ -203,6 +203,11 @@ class CYCLES_RENDER_PT_sampling_render(CyclesButtonsPanel, Panel):
         sub.active = cscene.use_denoising
         sub.prop(cscene, "denoiser", text="")
 
+        layout.separator()
+
+        col = layout.column(align=True)
+        col.prop(cscene, "time_limit")
+
 
 class CYCLES_RENDER_PT_sampling_adaptive(CyclesButtonsPanel, Panel):
     bl_label = "Adaptive Sampling"
diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp
index 69d1e3003dd..61f20b018af 100644
--- a/intern/cycles/blender/blender_session.cpp
+++ b/intern/cycles/blender/blender_session.cpp
@@ -725,8 +725,9 @@ void BlenderSession::synchronize(BL::Depsgraph &b_depsgraph_)
     create_session();
   }
 
-  /* increase samples, but never decrease */
+  /* increase samples and render time, but never decrease */
   session->set_samples(session_params.samples);
+  session->set_time_limit(session_params.time_limit);
   session->set_pause(session_pause);
 
   /* copy recalc flags, outside of mutex so we can decide to do the real
diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp
index 98d37878356..9659acc2cdd 100644
--- a/intern/cycles/blender/blender_sync.cpp
+++ b/intern/cycles/blender/blender_sync.cpp
@@ -848,6 +848,19 @@ SessionParams BlenderSync::get_session_params(BL::RenderEngine &b_engine,
   else if (shadingsystem == 1)
     params.shadingsystem = SHADINGSYSTEM_OSL;
 
+  /* Time limit. */
+  if (background) {
+    params.time_limit = get_float(cscene, "time_limit");
+  }
+  else {
+    /* For the viewport it kind of makes more sense to think in terms of the noise floor, which is
+     * usually higher than acceptable level for the final frame. */
+    /* TODO: It might be useful to support time limit in the viewport as well, but needs some
+     * extra thoughts and input. */
+    params.time_limit = 0.0;
+  }
+
+  /* Profiling. */
   params.use_profiling = params.device.has_profiling && !b_engine.is_preview() && background &&
                          BlenderSession::print_render_stats;
 
diff --git a/intern/cycles/integrator/render_scheduler.cpp b/intern/cycles/integrator/render_scheduler.cpp
index 7d98bfe1298..06fcc1d0b80 100644
--- a/intern/cycles/integrator/render_scheduler.cpp
+++ b/intern/cycles/integrator/render_scheduler.cpp
@@ -70,6 +70,16 @@ int RenderScheduler::get_num_samples() const
   return num_samples_;
 }
 
+void RenderScheduler::set_time_limit(double time_limit)
+{
+  time_limit_ = time_limit;
+}
+
+double RenderScheduler::get_time_limit() const
+{
+  return time_limit_;
+}
+
 int RenderScheduler::get_rendered_sample() const
 {
   DCHECK_GT(get_num_rendered_samples(), 0);
@@ -111,6 +121,10 @@ void RenderScheduler::reset(const BufferParams &buffer_params, int num_samples)
 
   state_.path_trace_finished = false;
 
+  state_.start_render_time = 0.0;
+  state_.end_render_time = 0.0;
+  state_.time_limit_reached = false;
+
   first_render_time_.path_trace_per_sample = 0.0;
   first_render_time_.denoise_time = 0.0;
   first_render_time_.display_update_time = 0.0;
@@ -176,7 +190,7 @@ bool RenderScheduler::done() const
     return false;
   }
 
-  if (state_.path_trace_finished) {
+  if (state_.path_trace_finished || state_.time_limit_reached) {
     return true;
   }
 
@@ -185,7 +199,12 @@ bool RenderScheduler::done() const
 
 RenderWork RenderScheduler::get_render_work()
 {
+  check_time_limit_reached();
+
   if (done()) {
+    if (state_.end_render_time == 0.0) {
+      state_.end_render_time = time_dt();
+    }
     return RenderWork();
   }
 
@@ -197,6 +216,8 @@ RenderWork RenderScheduler::get_render_work()
     state_.last_display_update_sample = -1;
   }
 
+  set_start_render_time_if_needed();
+
   render_work.resolution_divider = state_.resolution_divider;
 
   render_work.path_trace.start_sample = get_start_sample_to_path_trace();
@@ -315,6 +336,9 @@ void RenderScheduler::report_display_update_time(const RenderWork &render_work,
 
 string RenderScheduler::full_report() const
 {
+  const double render_wall_time = state_.end_render_time - state_.start_render_time;
+  const int num_rendered_samples = get_num_rendered_samples();
+
   string result = "\nRender Scheduler Summary\n\n";
 
   {
@@ -385,7 +409,17 @@ string RenderScheduler::full_report() const
 
   const double total_time = path_trace_time_.get_wall() + adaptive_filter_time_.get_wall() +
                             denoise_time_.get_wall() + display_update_time_.get_wall();
-  result += "\n  Total: " + to_string(total_time);
+  result += "\n  Total: " + to_string(total_time) + "\n";
+
+  result += string_printf(
+      "\nRendered %d samples in %f seconds\n", num_rendered_samples, render_wall_time);
+
+  /* When adaptive sampling is used the average time becomes meaningless, because different samples
+   * will likely render different number of pixels. */
+  if (!adaptive_sampling_.use) {
+    result += string_printf("Average time per sample: %f seconds\n",
+                            render_wall_time / num_rendered_samples);
+  }
 
   return result;
 }
@@ -395,9 +429,25 @@ double RenderScheduler::guess_display_update_interval_in_seconds() const
   return guess_display_update_interval_in_seconds_for_num_samples(state_.num_rendered_samples);
 }
 
+double RenderScheduler::guess_display_update_interval_in_seconds_for_num_samples(
+    int num_rendered_samples) const
+{
+  double update_interval = guess_display_update_interval_in_seconds_for_num_samples_no_limit(
+      num_rendered_samples);
+
+  if (time_limit_ != 0.0 && state_.start_render_time != 0.0) {
+    const double remaining_render_time = max(0.0,
+                                             time_limit_ - (time_dt() - state_.start_render_time));
+
+    update_interval = min(update_interval, remaining_render_time);
+  }
+
+  return update_interval;
+}
+
 /* TODO(sergey): This is just a quick implementation, exact values might need to be tweaked based
  * on a more careful experiments with viewport rendering. */
-double RenderScheduler::guess_display_update_interval_in_seconds_for_num_samples(
+double RenderScheduler::guess_display_update_interval_in_seconds_for_num_samples_no_limit(
     int num_rendered_samples) const
 {
   /* TODO(sergey): Need a decision on whether this should be using number of samples rendered
@@ -734,6 +784,37 @@ bool RenderScheduler::work_is_usable_for_first_render_estimation(const RenderWor
          render_work.path_trace.start_sample == start_sample_;
 }
 
+void RenderScheduler::set_start_render_time_if_needed()
+{
+  /* Start counting render time when rendering sampels at their final resolution. */
+  if (state_.resolution_divider == pixel_size_ && get_num_rendered_samples() == 0) {
+    state_.start_render_time = time_dt();
+  }
+}
+
+void RenderScheduler::check_time_limit_reached()
+{
+  if (time_limit_ == 0.0) {
+    /* No limit is enforced. */
+    return;
+  }
+
+  if (state_.start_render_time == 0.0) {
+    /* Rendering did not start yet. */
+    return;
+  }
+
+  const double current_time = time_dt();
+
+  if (current_time - state_.start_render_time < time_limit_) {
+    /* Time limit is not reached yet. */
+    return;
+  }
+
+  state_.time_limit_reached = true;
+  state_.end_render_time = current_time;
+}
+
 /* --------------------------------------------------------------------
  * Utility functions.
  */
diff --git a/intern/cycles/integrator/render_scheduler.h b/intern/cycles/integrator/render_scheduler.h
index 50872c27ec2..bdf4cde40b3 100644
--- a/intern/cycles/integrator/render_scheduler.h
+++ b/intern/cycles/integrator/render_scheduler.h
@@ -76,6 +76,11 @@ class RenderScheduler {
   void set_num_samples(int num_samples);
   int get_num_samples() const;
 
+  /* Time limit for the path tracing tasks, in minutes.
+   * Zero disables the limit. */
+  void set_time_limit(double time_limit);
+  double get_time_limit() const;
+
   /* Get sample up to which rendering has been done.
    * This is an absolute 0-based value.
    *
@@ -144,6 +149,8 @@ class RenderScheduler {
    * less often but the device occupancy goes higher. */
   double guess_display_update_interval_in_seconds() const;
   double guess_display_update_interval_in_seconds_for_num_samples(int num_rendered_samples) const;
+  double guess_display_update_interval_in_seconds_for_num_samples_no_limit(
+      int num_rendered_samples) const;
 
   /* Calculate number of samples which can be rendered within current desred update interval which
    * is calculated by `guess_update_interval_in_seconds()`. */
@@ -180,6 +187,13 @@ class RenderScheduler {
    * for the resolution divider calculation. */
   bool work_is_usable_for_first_render_estimation(const RenderWork &render_work);
 
+  void set_start_render_time_if_needed();
+
+  /* CHeck whether render time limit has been reached (or exceeded), and if so store related
+   * information in the state so that rendering is considered finished, and is possible to report
+   * average render time information. */
+  void check_time_limit_reached();
+
   /* Helper class to keep track of task timing.
    *
    * Contains two parts: wall time and average. The wall time is an actual wall time of how long it
@@ -244,6 +258,11 @@ class RenderScheduler {
     float 

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list