Cycles: add denoising settings to the render properties
authorBrecht Van Lommel <brecht@blender.org>
Sun, 31 May 2020 21:49:10 +0000 (23:49 +0200)
committerBrecht Van Lommel <brecht@blender.org>
Wed, 24 Jun 2020 13:17:36 +0000 (15:17 +0200)
Enabling render and viewport denoising is now both done from the render
properties. View layers still can individually be enabled/disabled for
denoising and have their own denoising parameters.

Note that the denoising engine also affects how denoising data passes are
output even if no denoising happens on the render itself, to make the passes
compatible with the engine.

This includes internal refactoring for how denoising parameters are passed
along, trying to avoid code duplication and unclear naming.

Ref T76259

24 files changed:
intern/cycles/blender/addon/properties.py
intern/cycles/blender/addon/ui.py
intern/cycles/blender/blender_camera.cpp
intern/cycles/blender/blender_device.cpp
intern/cycles/blender/blender_session.cpp
intern/cycles/blender/blender_sync.cpp
intern/cycles/blender/blender_sync.h
intern/cycles/blender/blender_viewport.cpp
intern/cycles/blender/blender_viewport.h
intern/cycles/device/device.cpp
intern/cycles/device/device.h
intern/cycles/device/device_cpu.cpp
intern/cycles/device/device_cuda.cpp
intern/cycles/device/device_denoising.cpp
intern/cycles/device/device_denoising.h
intern/cycles/device/device_network.cpp
intern/cycles/device/device_opencl.cpp
intern/cycles/device/device_optix.cpp
intern/cycles/device/device_task.h
intern/cycles/render/denoising.cpp
intern/cycles/render/session.cpp
intern/cycles/render/session.h
source/blender/blenkernel/BKE_blender_version.h
source/blender/blenloader/intern/versioning_cycles.c

index b7e9b1511ecc17d637538609f3b35a7a78cdb33f..061e3784b0dca178945f34915486138f39db4a6e 100644 (file)
@@ -182,10 +182,20 @@ enum_aov_types = (
     ('COLOR', "Color", "Write a Color pass", 1),
 )
 
-enum_viewport_denoising = (
-    ('NONE', "None", "Disable viewport denoising", 0),
-    ('OPTIX', "OptiX AI-Accelerated", "Use the OptiX denoiser running on the GPU (requires at least one compatible OptiX device)", 1),
-)
+def enum_optix_denoiser(self, context):
+    if not context or bool(context.preferences.addons[__package__].preferences.get_devices_for_type('OPTIX')):
+        return [('OPTIX', "OptiX", "Use the OptiX AI denoiser with GPU acceleration, only available on NVIDIA GPUs", 2)]
+    return []
+
+def enum_preview_denoiser(self, context):
+    items = [('AUTO', "Auto", "Use the fastest available denoiser for viewport rendering", 0)]
+    items += enum_optix_denoiser(self, context)
+    return items
+
+def enum_denoiser(self, context):
+    items = [('NLM', "NLM", "Cycles native non-local means denoiser, running on any compute device", 1)]
+    items += enum_optix_denoiser(self, context)
+    return items
 
 enum_denoising_optix_input_passes = (
     ('RGB', "Color", "Use only color as input", 1),
@@ -224,11 +234,29 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
         description="Pause all viewport preview renders",
         default=False,
     )
-    preview_denoising: EnumProperty(
-        name="Viewport Denoising",
-        description="Denoise the image after each preview update with the selected denoiser engine",
-        items=enum_viewport_denoising,
-        default='NONE',
+
+    use_denoising: BoolProperty(
+        name="Use Denoising",
+        description="Denoise the rendered image",
+        default=False,
+    )
+    use_preview_denoising: BoolProperty(
+        name="Use Viewport Denoising",
+        description="Denoise the image in the 3D viewport",
+        default=False,
+    )
+
+    denoiser: EnumProperty(
+        name="Denoiser",
+        description="Denoise the image with the selected denoiser",
+        items=enum_denoiser,
+        default=1,
+    )
+    preview_denoiser: EnumProperty(
+        name="Viewport Denoiser",
+        description="Denoise the image after each preview update with the selected denoiser",
+        items=enum_preview_denoiser,
+        default=0,
     )
 
     use_square_samples: BoolProperty(
@@ -244,7 +272,7 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
         default=128,
     )
     preview_samples: IntProperty(
-        name="Preview Samples",
+        name="Viewport Samples",
         description="Number of samples to render in the viewport, unlimited if 0",
         min=0, max=(1 << 24),
         default=32,
@@ -464,7 +492,7 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
         subtype='PIXEL'
     )
     preview_dicing_rate: FloatProperty(
-        name="Preview Dicing Rate",
+        name="Viewport Dicing Rate",
         description="Size of a micropolygon in pixels during preview render",
         min=0.1, max=1000.0, soft_min=0.5,
         default=8.0,
@@ -1330,7 +1358,7 @@ class CyclesRenderLayerSettings(bpy.types.PropertyGroup):
     use_denoising: BoolProperty(
         name="Use Denoising",
         description="Denoise the rendered image",
-        default=False,
+        default=True,
         update=update_render_passes,
     )
     denoising_diffuse_direct: BoolProperty(
@@ -1400,12 +1428,6 @@ class CyclesRenderLayerSettings(bpy.types.PropertyGroup):
         default=0,
     )
 
-    use_optix_denoising: BoolProperty(
-        name="OptiX AI-Accelerated",
-        description="Use the OptiX denoiser to denoise the rendered image",
-        default=False,
-        update=update_render_passes,
-    )
     denoising_optix_input_passes: EnumProperty(
         name="Input Passes",
         description="Passes handed over to the OptiX denoiser (this can have different effects on the denoised image)",
index 85da1a17c2b89a42e2a3749f80d04c82a4831cab..e689ec90983850963a4f56354619e3218a4a1e73 100644 (file)
@@ -112,10 +112,6 @@ def show_device_active(context):
         return True
     return context.preferences.addons[__package__].preferences.has_active_device()
 
-def show_optix_denoising(context):
-    # OptiX AI denoiser can be used when at least one device supports OptiX
-    return bool(context.preferences.addons[__package__].preferences.get_devices_for_type('OPTIX'))
-
 
 def draw_samples_info(layout, context):
     cscene = context.scene.cycles
@@ -190,11 +186,6 @@ class CYCLES_RENDER_PT_sampling(CyclesButtonsPanel, Panel):
             col.prop(cscene, "aa_samples", text="Render")
             col.prop(cscene, "preview_aa_samples", text="Viewport")
 
-        # Viewport denoising is currently only supported with OptiX
-        if show_optix_denoising(context):
-            col = layout.column()
-            col.prop(cscene, "preview_denoising")
-
         if not use_branched_path(context):
             draw_samples_info(layout, context)
 
@@ -256,6 +247,39 @@ class CYCLES_RENDER_PT_sampling_adaptive(CyclesButtonsPanel, Panel):
         col.prop(cscene, "adaptive_threshold", text="Noise Threshold")
         col.prop(cscene, "adaptive_min_samples", text="Min Samples")
 
+
+class CYCLES_RENDER_PT_sampling_denoising(CyclesButtonsPanel, Panel):
+    bl_label = "Denoising"
+    bl_parent_id = "CYCLES_RENDER_PT_sampling"
+    bl_options = {'DEFAULT_CLOSED'}
+
+    def draw(self, context):
+        layout = self.layout
+        layout.use_property_split = True
+        layout.use_property_decorate = False
+
+        scene = context.scene
+        cscene = scene.cycles
+
+        heading = layout.column(align=True, heading="Render")
+        row = heading.row(align=True)
+        row.prop(cscene, "use_denoising", text="")
+        sub = row.row()
+        sub.active = cscene.use_denoising
+        sub.prop(cscene, "denoiser", text="")
+
+        heading = layout.column(align=True, heading="Viewport")
+        row = heading.row(align=True)
+        row.prop(cscene, "use_preview_denoising", text="")
+        sub = row.row()
+        sub.active = cscene.use_preview_denoising
+        sub.prop(cscene, "preview_denoiser", text="")
+
+        sub = heading.row(align=True)
+        sub.active = cscene.use_preview_denoising
+        sub.prop(cscene, "preview_denoising_start_sample", text="Start Sample")
+
+
 class CYCLES_RENDER_PT_sampling_advanced(CyclesButtonsPanel, Panel):
     bl_label = "Advanced"
     bl_parent_id = "CYCLES_RENDER_PT_sampling"
@@ -730,11 +754,6 @@ class CYCLES_RENDER_PT_performance_viewport(CyclesButtonsPanel, Panel):
         col.prop(rd, "preview_pixel_size", text="Pixel Size")
         col.prop(cscene, "preview_start_resolution", text="Start Pixels")
 
-        if show_optix_denoising(context):
-            sub = col.row(align=True)
-            sub.active = cscene.preview_denoising != 'NONE'
-            sub.prop(cscene, "preview_denoising_start_sample", text="Denoising Start Sample")
-
 
 class CYCLES_RENDER_PT_filter(CyclesButtonsPanel, Panel):
     bl_label = "Filter"
@@ -957,12 +976,17 @@ class CYCLES_RENDER_PT_denoising(CyclesButtonsPanel, Panel):
     bl_context = "view_layer"
     bl_options = {'DEFAULT_CLOSED'}
 
+    @classmethod
+    def poll(cls, context):
+        cscene = context.scene.cycles
+        return CyclesButtonsPanel.poll(context) and cscene.use_denoising
+
     def draw_header(self, context):
         scene = context.scene
         view_layer = context.view_layer
         cycles_view_layer = view_layer.cycles
-        layout = self.layout
 
+        layout = self.layout
         layout.prop(cycles_view_layer, "use_denoising", text="")
 
     def draw(self, context):
@@ -973,18 +997,15 @@ class CYCLES_RENDER_PT_denoising(CyclesButtonsPanel, Panel):
         scene = context.scene
         view_layer = context.view_layer
         cycles_view_layer = view_layer.cycles
+        denoiser = scene.cycles.denoiser
 
-        layout.active = cycles_view_layer.use_denoising
+        layout.active = denoiser != 'NONE' and cycles_view_layer.use_denoising
 
         col = layout.column()
 
-        if show_optix_denoising(context):
-            col.prop(cycles_view_layer, "use_optix_denoising")
-            col.separator(factor=2.0)
-
-            if cycles_view_layer.use_optix_denoising:
-                col.prop(cycles_view_layer, "denoising_optix_input_passes")
-                return
+        if denoiser == 'OPTIX':
+            col.prop(cycles_view_layer, "denoising_optix_input_passes")
+            return
 
         col.prop(cycles_view_layer, "denoising_radius", text="Radius")
 
@@ -2237,6 +2258,7 @@ classes = (
     CYCLES_RENDER_PT_sampling,
     CYCLES_RENDER_PT_sampling_sub_samples,
     CYCLES_RENDER_PT_sampling_adaptive,
+    CYCLES_RENDER_PT_sampling_denoising,
     CYCLES_RENDER_PT_sampling_advanced,
     CYCLES_RENDER_PT_light_paths,
     CYCLES_RENDER_PT_light_paths_max_bounces,
index 40a1a2c2edc3d248e668af0078cba76e9b3a8996..d9c63bec73718c83d49dd3f654d862add7d39fa6 100644 (file)
@@ -873,7 +873,8 @@ BufferParams BlenderSync::get_buffer_params(BL::Scene &b_scene,
                                             BL::RegionView3D &b_rv3d,
                                             Camera *cam,
                                             int width,
-                                            int height)
+                                            int height,
+                                            const bool use_denoiser)
 {
   BufferParams params;
   bool use_border = false;
@@ -907,8 +908,7 @@ BufferParams BlenderSync::get_buffer_params(BL::Scene &b_scene,
   PassType display_pass = update_viewport_display_passes(b_v3d, params.passes);
 
   /* Can only denoise the combined image pass */
-  params.denoising_data_pass = display_pass == PASS_COMBINED &&
-                               update_viewport_display_denoising(b_v3d, b_scene);
+  params.denoising_data_pass = display_pass == PASS_COMBINED && use_denoiser;
 
   return params;
 }
index ac52948806c22aa5d41721848d5218ad924a66e8..fb9ab9e8c9710f5f9206502698d5dc9522a03455 100644 (file)
 
 CCL_NAMESPACE_BEGIN
 
-enum DenoiserType {
-  DENOISER_NONE = 0,
-  DENOISER_OPTIX = 1,
-
-  DENOISER_NUM
-};
-
 enum ComputeDevice {
   COMPUTE_DEVICE_CPU = 0,
   COMPUTE_DEVICE_CUDA = 1,
@@ -120,49 +113,6 @@ DeviceInfo blender_device_info(BL::Preferences &b_preferences, BL::Scene &b_scen
     }
   }
 
-  /* Ensure there is an OptiX device when using the OptiX denoiser. */
-  bool use_optix_denoising = get_enum(cscene, "preview_denoising", DENOISER_NUM, DENOISER_NONE) ==
-                                 DENOISER_OPTIX &&
-                             !background;
-  BL::Scene::view_layers_iterator b_view_layer;
-  for (b_scene.view_layers.begin(b_view_layer); b_view_layer != b_scene.view_layers.end();
-       ++b_view_layer) {
-    PointerRNA crl = RNA_pointer_get(&b_view_layer->ptr, "cycles");
-    if (get_boolean(crl, "use_optix_denoising")) {
-      use_optix_denoising = true;
-    }
-  }
-
-  if (use_optix_denoising && device.type != DEVICE_OPTIX) {
-    vector<DeviceInfo> optix_devices = Device::available_devices(DEVICE_MASK_OPTIX);
-    if (!optix_devices.empty()) {
-      /* Convert to a special multi device with separate denoising devices. */
-      if (device.multi_devices.empty()) {
-        device.multi_devices.push_back(device);
-      }
-
-      /* Try to use the same physical devices for denoising. */
-      for (const DeviceInfo &cuda_device : device.multi_devices) {
-        if (cuda_device.type == DEVICE_CUDA) {
-          for (const DeviceInfo &optix_device : optix_devices) {
-            if (cuda_device.num == optix_device.num) {
-              device.id += optix_device.id;
-              device.denoising_devices.push_back(optix_device);
-              break;
-            }
-          }
-        }
-      }
-
-      if (device.denoising_devices.empty()) {
-        /* Simply use the first available OptiX device. */
-        const DeviceInfo optix_device = optix_devices.front();
-        device.id += optix_device.id; /* Uniquely identify this special multi device. */
-        device.denoising_devices.push_back(optix_device);
-      }
-    }
-  }
-
   return device;
 }
 
index dbe87ce2b13c964039a51fb6f4b6511c9b54ab6c..2874ccb64701efe05411252c39d168309fb53882 100644 (file)
@@ -157,8 +157,14 @@ void BlenderSession::create_session()
   }
 
   /* set buffer parameters */
-  BufferParams buffer_params = BlenderSync::get_buffer_params(
-      b_scene, b_render, b_v3d, b_rv3d, scene->camera, width, height);
+  BufferParams buffer_params = BlenderSync::get_buffer_params(b_scene,
+                                                              b_render,
+                                                              b_v3d,
+                                                              b_rv3d,
+                                                              scene->camera,
+                                                              width,
+                                                              height,
+                                                              session_params.denoising.use);
   session->reset(buffer_params, session_params.samples);
 
   b_engine.use_highlight_tiles(session_params.progressive_refine == false);
@@ -239,8 +245,14 @@ void BlenderSession::reset_session(BL::BlendData &b_data, BL::Depsgraph &b_depsg
 
   BL::SpaceView3D b_null_space_view3d(PointerRNA_NULL);
   BL::RegionView3D b_null_region_view3d(PointerRNA_NULL);
-  BufferParams buffer_params = BlenderSync::get_buffer_params(
-      b_scene, b_render, b_null_space_view3d, b_null_region_view3d, scene->camera, width, height);
+  BufferParams buffer_params = BlenderSync::get_buffer_params(b_scene,
+                                                              b_render,
+                                                              b_null_space_view3d,
+                                                              b_null_region_view3d,
+                                                              scene->camera,
+                                                              width,
+                                                              height,
+                                                              session_params.denoising.use);
   session->reset(buffer_params, session_params.samples);
 
   b_engine.use_highlight_tiles(session_params.progressive_refine == false);
@@ -468,14 +480,19 @@ void BlenderSession::render(BL::Depsgraph &b_depsgraph_)
   session->update_render_tile_cb = function_bind(
       &BlenderSession::update_render_tile, this, _1, _2);
 
+  BL::ViewLayer b_view_layer = b_depsgraph.view_layer_eval();
+
   /* get buffer parameters */
   SessionParams session_params = BlenderSync::get_session_params(
-      b_engine, b_userpref, b_scene, background);
-  BufferParams buffer_params = BlenderSync::get_buffer_params(
-      b_scene, b_render, b_v3d, b_rv3d, scene->camera, width, height);
-
-  /* render each layer */
-  BL::ViewLayer b_view_layer = b_depsgraph.view_layer_eval();
+      b_engine, b_userpref, b_scene, background, b_view_layer);
+  BufferParams buffer_params = BlenderSync::get_buffer_params(b_scene,
+                                                              b_render,
+                                                              b_v3d,
+                                                              b_rv3d,
+                                                              scene->camera,
+                                                              width,
+                                                              height,
+                                                              session_params.denoising.use);
 
   /* temporary render result to find needed passes and views */
   BL::RenderResult b_rr = begin_render_result(
@@ -485,35 +502,26 @@ void BlenderSession::render(BL::Depsgraph &b_depsgraph_)
   BL::RenderLayer b_rlay = *b_single_rlay;
   b_rlay_name = b_view_layer.name();
 
-  /* add passes */
-  vector<Pass> passes = sync->sync_render_passes(
-      b_rlay, b_view_layer, session_params.adaptive_sampling);
-  buffer_params.passes = passes;
+  /* Update denoising parameters. */
+  session->set_denoising(session_params.denoising);
 
-  PointerRNA crl = RNA_pointer_get(&b_view_layer.ptr, "cycles");
-  bool use_denoising = get_boolean(crl, "use_denoising");
-  bool use_optix_denoising = get_boolean(crl, "use_optix_denoising");
-  bool write_denoising_passes = get_boolean(crl, "denoising_store_passes");
+  bool use_denoising = session_params.denoising.use;
+  bool store_denoising_passes = session_params.denoising.store_passes;
 
-  buffer_params.denoising_data_pass = use_denoising || write_denoising_passes;
+  buffer_params.denoising_data_pass = use_denoising || store_denoising_passes;
   buffer_params.denoising_clean_pass = (scene->film->denoising_flags & DENOISING_CLEAN_ALL_PASSES);
-  buffer_params.denoising_prefiltered_pass = write_denoising_passes && !use_optix_denoising;
-
-  session->params.run_denoising = use_denoising || write_denoising_passes;
-  session->params.full_denoising = use_denoising && !use_optix_denoising;
-  session->params.optix_denoising = use_denoising && use_optix_denoising;
-  session->params.write_denoising_passes = write_denoising_passes && !use_optix_denoising;
-  session->params.denoising.radius = get_int(crl, "denoising_radius");
-  session->params.denoising.strength = get_float(crl, "denoising_strength");
-  session->params.denoising.feature_strength = get_float(crl, "denoising_feature_strength");
-  session->params.denoising.relative_pca = get_boolean(crl, "denoising_relative_pca");
-  session->params.denoising.optix_input_passes = get_enum(crl, "denoising_optix_input_passes");
-  session->tile_manager.schedule_denoising = session->params.run_denoising;
+  buffer_params.denoising_prefiltered_pass = store_denoising_passes &&
+                                             session_params.denoising.type == DENOISER_NLM;
 
   scene->film->denoising_data_pass = buffer_params.denoising_data_pass;
   scene->film->denoising_clean_pass = buffer_params.denoising_clean_pass;
   scene->film->denoising_prefiltered_pass = buffer_params.denoising_prefiltered_pass;
 
+  /* Add passes */
+  vector<Pass> passes = sync->sync_render_passes(
+      b_rlay, b_view_layer, session_params.adaptive_sampling, session_params.denoising);
+  buffer_params.passes = passes;
+
   scene->film->pass_alpha_threshold = b_view_layer.pass_alpha_threshold();
   scene->film->tag_passes_update(scene, passes);
   scene->film->tag_update(scene);
@@ -798,7 +806,7 @@ void BlenderSession::synchronize(BL::Depsgraph &b_depsgraph_)
 
   /* increase samples, but never decrease */
   session->set_samples(session_params.samples);
-  session->set_denoising_start_sample(session_params.denoising_start_sample);
+  session->set_denoising_start_sample(session_params.denoising.start_sample);
   session->set_pause(session_pause);
 
   /* copy recalc flags, outside of mutex so we can decide to do the real
@@ -830,22 +838,24 @@ void BlenderSession::synchronize(BL::Depsgraph &b_depsgraph_)
     sync->sync_camera(b_render, b_camera_override, width, height, "");
 
   /* get buffer parameters */
-  BufferParams buffer_params = BlenderSync::get_buffer_params(
-      b_scene, b_render, b_v3d, b_rv3d, scene->camera, width, height);
-
-  if (session_params.device.type != DEVICE_OPTIX &&
-      session_params.device.denoising_devices.empty()) {
-    /* cannot use OptiX denoising when it is not supported by the device. */
-    buffer_params.denoising_data_pass = false;
-  }
-  else {
-    session->set_denoising(buffer_params.denoising_data_pass, true);
+  BufferParams buffer_params = BlenderSync::get_buffer_params(b_scene,
+                                                              b_render,
+                                                              b_v3d,
+                                                              b_rv3d,
+                                                              scene->camera,
+                                                              width,
+                                                              height,
+                                                              session_params.denoising.use);
+
+  if (!buffer_params.denoising_data_pass) {
+    session_params.denoising.use = false;
   }
 
+  session->set_denoising(session_params.denoising);
+
+  /* Update film if denoising data was enabled or disabled. */
   if (scene->film->denoising_data_pass != buffer_params.denoising_data_pass) {
     scene->film->denoising_data_pass = buffer_params.denoising_data_pass;
-
-    /* Force a scene and session reset below. */
     scene->film->tag_update(scene);
   }
 
@@ -916,8 +926,14 @@ bool BlenderSession::draw(int w, int h)
     if (reset) {
       SessionParams session_params = BlenderSync::get_session_params(
           b_engine, b_userpref, b_scene, background);
-      BufferParams buffer_params = BlenderSync::get_buffer_params(
-          b_scene, b_render, b_v3d, b_rv3d, scene->camera, width, height);
+      BufferParams buffer_params = BlenderSync::get_buffer_params(b_scene,
+                                                                  b_render,
+                                                                  b_v3d,
+                                                                  b_rv3d,
+                                                                  scene->camera,
+                                                                  width,
+                                                                  height,
+                                                                  session_params.denoising.use);
       bool session_pause = BlenderSync::get_session_pause(b_scene, background);
 
       if (session_pause == false) {
@@ -934,8 +950,14 @@ bool BlenderSession::draw(int w, int h)
   update_status_progress();
 
   /* draw */
-  BufferParams buffer_params = BlenderSync::get_buffer_params(
-      b_scene, b_render, b_v3d, b_rv3d, scene->camera, width, height);
+  BufferParams buffer_params = BlenderSync::get_buffer_params(b_scene,
+                                                              b_render,
+                                                              b_v3d,
+                                                              b_rv3d,
+                                                              scene->camera,
+                                                              width,
+                                                              height,
+                                                              session->params.denoising.use);
   DeviceDrawParams draw_params;
 
   if (session->params.display_buffer_linear) {
index b40c8434395498bb8f0fe3b784494fe017bcd139..aed92cf137640889d9deed17b6b847e8eb5fdd49 100644 (file)
@@ -537,7 +537,8 @@ int BlenderSync::get_denoising_pass(BL::RenderPass &b_pass)
 
 vector<Pass> BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay,
                                              BL::ViewLayer &b_view_layer,
-                                             bool adaptive_sampling)
+                                             bool adaptive_sampling,
+                                             const DenoiseParams &denoising)
 {
   vector<Pass> passes;
 
@@ -554,16 +555,13 @@ vector<Pass> BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay,
       Pass::add(pass_type, passes, b_pass.name().c_str());
   }
 
-  PointerRNA crp = RNA_pointer_get(&b_view_layer.ptr, "cycles");
-  bool use_denoising = get_boolean(crp, "use_denoising");
-  bool use_optix_denoising = get_boolean(crp, "use_optix_denoising");
-  bool write_denoising_passes = get_boolean(crp, "denoising_store_passes");
+  PointerRNA crl = RNA_pointer_get(&b_view_layer.ptr, "cycles");
 
   scene->film->denoising_flags = 0;
-  if (use_denoising || write_denoising_passes) {
-    if (!use_optix_denoising) {
+  if (denoising.use || denoising.store_passes) {
+    if (denoising.type == DENOISER_NLM) {
 #define MAP_OPTION(name, flag) \
-  if (!get_boolean(crp, name)) \
+  if (!get_boolean(crl, name)) \
     scene->film->denoising_flags |= flag;
       MAP_OPTION("denoising_diffuse_direct", DENOISING_CLEAN_DIFFUSE_DIR);
       MAP_OPTION("denoising_diffuse_indirect", DENOISING_CLEAN_DIFFUSE_IND);
@@ -576,11 +574,11 @@ vector<Pass> BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay,
     b_engine.add_pass("Noisy Image", 4, "RGBA", b_view_layer.name().c_str());
   }
 
-  if (write_denoising_passes) {
+  if (denoising.store_passes) {
     b_engine.add_pass("Denoising Normal", 3, "XYZ", b_view_layer.name().c_str());
     b_engine.add_pass("Denoising Albedo", 3, "RGB", b_view_layer.name().c_str());
     b_engine.add_pass("Denoising Depth", 1, "Z", b_view_layer.name().c_str());
-    if (!use_optix_denoising) {
+    if (denoising.type == DENOISER_NLM) {
       b_engine.add_pass("Denoising Shadowing", 1, "X", b_view_layer.name().c_str());
       b_engine.add_pass("Denoising Variance", 3, "RGB", b_view_layer.name().c_str());
       b_engine.add_pass("Denoising Intensity", 1, "X", b_view_layer.name().c_str());
@@ -592,46 +590,46 @@ vector<Pass> BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay,
   }
 
 #ifdef __KERNEL_DEBUG__
-  if (get_boolean(crp, "pass_debug_bvh_traversed_nodes")) {
+  if (get_boolean(crl, "pass_debug_bvh_traversed_nodes")) {
     b_engine.add_pass("Debug BVH Traversed Nodes", 1, "X", b_view_layer.name().c_str());
     Pass::add(PASS_BVH_TRAVERSED_NODES, passes, "Debug BVH Traversed Nodes");
   }
-  if (get_boolean(crp, "pass_debug_bvh_traversed_instances")) {
+  if (get_boolean(crl, "pass_debug_bvh_traversed_instances")) {
     b_engine.add_pass("Debug BVH Traversed Instances", 1, "X", b_view_layer.name().c_str());
     Pass::add(PASS_BVH_TRAVERSED_INSTANCES, passes, "Debug BVH Traversed Instances");
   }
-  if (get_boolean(crp, "pass_debug_bvh_intersections")) {
+  if (get_boolean(crl, "pass_debug_bvh_intersections")) {
     b_engine.add_pass("Debug BVH Intersections", 1, "X", b_view_layer.name().c_str());
     Pass::add(PASS_BVH_INTERSECTIONS, passes, "Debug BVH Intersections");
   }
-  if (get_boolean(crp, "pass_debug_ray_bounces")) {
+  if (get_boolean(crl, "pass_debug_ray_bounces")) {
     b_engine.add_pass("Debug Ray Bounces", 1, "X", b_view_layer.name().c_str());
     Pass::add(PASS_RAY_BOUNCES, passes, "Debug Ray Bounces");
   }
 #endif
-  if (get_boolean(crp, "pass_debug_render_time")) {
+  if (get_boolean(crl, "pass_debug_render_time")) {
     b_engine.add_pass("Debug Render Time", 1, "X", b_view_layer.name().c_str());
     Pass::add(PASS_RENDER_TIME, passes, "Debug Render Time");
   }
-  if (get_boolean(crp, "pass_debug_sample_count")) {
+  if (get_boolean(crl, "pass_debug_sample_count")) {
     b_engine.add_pass("Debug Sample Count", 1, "X", b_view_layer.name().c_str());
     Pass::add(PASS_SAMPLE_COUNT, passes, "Debug Sample Count");
   }
-  if (get_boolean(crp, "use_pass_volume_direct")) {
+  if (get_boolean(crl, "use_pass_volume_direct")) {
     b_engine.add_pass("VolumeDir", 3, "RGB", b_view_layer.name().c_str());
     Pass::add(PASS_VOLUME_DIRECT, passes, "VolumeDir");
   }
-  if (get_boolean(crp, "use_pass_volume_indirect")) {
+  if (get_boolean(crl, "use_pass_volume_indirect")) {
     b_engine.add_pass("VolumeInd", 3, "RGB", b_view_layer.name().c_str());
     Pass::add(PASS_VOLUME_INDIRECT, passes, "VolumeInd");
   }
 
   /* Cryptomatte stores two ID/weight pairs per RGBA layer.
    * User facing parameter is the number of pairs. */
-  int crypto_depth = divide_up(min(16, get_int(crp, "pass_crypto_depth")), 2);
+  int crypto_depth = divide_up(min(16, get_int(crl, "pass_crypto_depth")), 2);
   scene->film->cryptomatte_depth = crypto_depth;
   scene->film->cryptomatte_passes = CRYPT_NONE;
-  if (get_boolean(crp, "use_pass_crypto_object")) {
+  if (get_boolean(crl, "use_pass_crypto_object")) {
     for (int i = 0; i < crypto_depth; i++) {
       string passname = cryptomatte_prefix + string_printf("Object%02d", i);
       b_engine.add_pass(passname.c_str(), 4, "RGBA", b_view_layer.name().c_str());
@@ -640,7 +638,7 @@ vector<Pass> BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay,
     scene->film->cryptomatte_passes = (CryptomatteType)(scene->film->cryptomatte_passes |
                                                         CRYPT_OBJECT);
   }
-  if (get_boolean(crp, "use_pass_crypto_material")) {
+  if (get_boolean(crl, "use_pass_crypto_material")) {
     for (int i = 0; i < crypto_depth; i++) {
       string passname = cryptomatte_prefix + string_printf("Material%02d", i);
       b_engine.add_pass(passname.c_str(), 4, "RGBA", b_view_layer.name().c_str());
@@ -649,7 +647,7 @@ vector<Pass> BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay,
     scene->film->cryptomatte_passes = (CryptomatteType)(scene->film->cryptomatte_passes |
                                                         CRYPT_MATERIAL);
   }
-  if (get_boolean(crp, "use_pass_crypto_asset")) {
+  if (get_boolean(crl, "use_pass_crypto_asset")) {
     for (int i = 0; i < crypto_depth; i++) {
       string passname = cryptomatte_prefix + string_printf("Asset%02d", i);
       b_engine.add_pass(passname.c_str(), 4, "RGBA", b_view_layer.name().c_str());
@@ -658,19 +656,19 @@ vector<Pass> BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay,
     scene->film->cryptomatte_passes = (CryptomatteType)(scene->film->cryptomatte_passes |
                                                         CRYPT_ASSET);
   }
-  if (get_boolean(crp, "pass_crypto_accurate") && scene->film->cryptomatte_passes != CRYPT_NONE) {
+  if (get_boolean(crl, "pass_crypto_accurate") && scene->film->cryptomatte_passes != CRYPT_NONE) {
     scene->film->cryptomatte_passes = (CryptomatteType)(scene->film->cryptomatte_passes |
                                                         CRYPT_ACCURATE);
   }
 
   if (adaptive_sampling) {
     Pass::add(PASS_ADAPTIVE_AUX_BUFFER, passes);
-    if (!get_boolean(crp, "pass_debug_sample_count")) {
+    if (!get_boolean(crl, "pass_debug_sample_count")) {
       Pass::add(PASS_SAMPLE_COUNT, passes);
     }
   }
 
-  RNA_BEGIN (&crp, b_aov, "aovs") {
+  RNA_BEGIN (&crl, b_aov, "aovs") {
     bool is_color = (get_enum(b_aov, "type") == 1);
     string name = get_string(b_aov, "name");
 
@@ -773,7 +771,8 @@ bool BlenderSync::get_session_pause(BL::Scene &b_scene, bool background)
 SessionParams BlenderSync::get_session_params(BL::RenderEngine &b_engine,
                                               BL::Preferences &b_preferences,
                                               BL::Scene &b_scene,
-                                              bool background)
+                                              bool background,
+                                              BL::ViewLayer b_view_layer)
 {
   SessionParams params;
   PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
@@ -851,9 +850,22 @@ SessionParams BlenderSync::get_session_params(BL::RenderEngine &b_engine,
     params.tile_order = TILE_BOTTOM_TO_TOP;
   }
 
-  /* other parameters */
+  /* Denoising */
+  params.denoising = get_denoise_params(b_scene, b_view_layer, background);
+
+  if (params.denoising.use) {
+    /* Add additional denoising devices if we are rendering and denoising
+     * with different devices. */
+    params.device.add_denoising_devices(params.denoising.type);
+
+    /* Check if denoiser is supported by device. */
+    if (!(params.device.denoisers & params.denoising.type)) {
+      params.denoising.use = false;
+    }
+  }
+
+  /* Viewport Performance */
   params.start_resolution = get_int(cscene, "preview_start_resolution");
-  params.denoising_start_sample = get_int(cscene, "preview_denoising_start_sample");
   params.pixel_size = b_engine.get_preview_pixel_size(b_scene);
 
   /* other parameters */
@@ -906,4 +918,52 @@ SessionParams BlenderSync::get_session_params(BL::RenderEngine &b_engine,
   return params;
 }
 
+DenoiseParams BlenderSync::get_denoise_params(BL::Scene &b_scene,
+                                              BL::ViewLayer &b_view_layer,
+                                              bool background)
+{
+  DenoiseParams denoising;
+  PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
+
+  if (background) {
+    /* Final Render Denoising */
+    denoising.use = get_boolean(cscene, "use_denoising");
+    denoising.type = (DenoiserType)get_enum(cscene, "denoiser", DENOISER_NUM, DENOISER_NONE);
+
+    if (b_view_layer) {
+      PointerRNA clayer = RNA_pointer_get(&b_view_layer.ptr, "cycles");
+      if (!get_boolean(clayer, "use_denoising")) {
+        denoising.use = false;
+      }
+
+      denoising.radius = get_int(clayer, "denoising_radius");
+      denoising.strength = get_float(clayer, "denoising_strength");
+      denoising.feature_strength = get_float(clayer, "denoising_feature_strength");
+      denoising.relative_pca = get_boolean(clayer, "denoising_relative_pca");
+      denoising.optix_input_passes = get_enum(clayer, "denoising_optix_input_passes");
+
+      denoising.store_passes = get_boolean(clayer, "denoising_store_passes");
+    }
+  }
+  else {
+    /* Viewport Denoising */
+    denoising.use = get_boolean(cscene, "use_preview_denoising");
+    denoising.type = (DenoiserType)get_enum(
+        cscene, "preview_denoiser", DENOISER_NUM, DENOISER_NONE);
+    denoising.start_sample = get_int(cscene, "preview_denoising_start_sample");
+
+    /* Auto select fastest denoiser. */
+    if (denoising.type == DENOISER_NONE) {
+      if (!Device::available_devices(DEVICE_MASK_OPTIX).empty()) {
+        denoising.type = DENOISER_OPTIX;
+      }
+      else {
+        denoising.use = false;
+      }
+    }
+  }
+
+  return denoising;
+}
+
 CCL_NAMESPACE_END
index 25032e8d0fa6d0bcc790447349d79140cd693cc3..85789cd00f63ae1ed4881c719975c70efb9a5809 100644 (file)
@@ -75,7 +75,8 @@ class BlenderSync {
   void sync_view_layer(BL::SpaceView3D &b_v3d, BL::ViewLayer &b_view_layer);
   vector<Pass> sync_render_passes(BL::RenderLayer &b_render_layer,
                                   BL::ViewLayer &b_view_layer,
-                                  bool adaptive_sampling);
+                                  bool adaptive_sampling,
+                                  const DenoiseParams &denoising);
   void sync_integrator();
   void sync_camera(BL::RenderSettings &b_render,
                    BL::Object &b_override,
@@ -94,10 +95,12 @@ class BlenderSync {
 
   /* get parameters */
   static SceneParams get_scene_params(BL::Scene &b_scene, bool background);
-  static SessionParams get_session_params(BL::RenderEngine &b_engine,
-                                          BL::Preferences &b_userpref,
-                                          BL::Scene &b_scene,
-                                          bool background);
+  static SessionParams get_session_params(
+      BL::RenderEngine &b_engine,
+      BL::Preferences &b_userpref,
+      BL::Scene &b_scene,
+      bool background,
+      BL::ViewLayer b_view_layer = BL::ViewLayer(PointerRNA_NULL));
   static bool get_session_pause(BL::Scene &b_scene, bool background);
   static BufferParams get_buffer_params(BL::Scene &b_scene,
                                         BL::RenderSettings &b_render,
@@ -105,12 +108,17 @@ class BlenderSync {
                                         BL::RegionView3D &b_rv3d,
                                         Camera *cam,
                                         int width,
-                                        int height);
+                                        int height,
+                                        const bool use_denoiser);
 
   static PassType get_pass_type(BL::RenderPass &b_pass);
   static int get_denoising_pass(BL::RenderPass &b_pass);
 
  private:
+  static DenoiseParams get_denoise_params(BL::Scene &b_scene,
+                                          BL::ViewLayer &b_view_layer,
+                                          bool background);
+
   /* sync */
   void sync_lights(BL::Depsgraph &b_depsgraph, bool update_all);
   void sync_materials(BL::Depsgraph &b_depsgraph, bool update_all);
index 93e84e2803297f45ffe5e4387c24d1d5503873f1..73ef5f94720c2aa94b3ad0b22e545200be2ab26e 100644 (file)
@@ -61,17 +61,6 @@ const bool BlenderViewportParameters::custom_viewport_parameters() const
   return !(use_scene_world && use_scene_lights);
 }
 
-bool BlenderViewportParameters::get_viewport_display_denoising(BL::SpaceView3D &b_v3d,
-                                                               BL::Scene &b_scene)
-{
-  bool use_denoising = false;
-  if (b_v3d) {
-    PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
-    use_denoising = get_enum(cscene, "preview_denoising") != 0;
-  }
-  return use_denoising;
-}
-
 PassType BlenderViewportParameters::get_viewport_display_render_pass(BL::SpaceView3D &b_v3d)
 {
   PassType display_pass = PASS_NONE;
@@ -83,11 +72,6 @@ PassType BlenderViewportParameters::get_viewport_display_render_pass(BL::SpaceVi
   return display_pass;
 }
 
-bool update_viewport_display_denoising(BL::SpaceView3D &b_v3d, BL::Scene &b_scene)
-{
-  return BlenderViewportParameters::get_viewport_display_denoising(b_v3d, b_scene);
-}
-
 PassType update_viewport_display_passes(BL::SpaceView3D &b_v3d, vector<Pass> &passes)
 {
   if (b_v3d) {
index 3e44e552f1da616d7eda7b5b4e38ca1f02b48847..7c6c9c4d274650901ed5948c1717ee6252feda79 100644 (file)
@@ -44,15 +44,11 @@ class BlenderViewportParameters {
   friend class BlenderSync;
 
  public:
-  /* Get whether to enable denoising data pass in viewport. */
-  static bool get_viewport_display_denoising(BL::SpaceView3D &b_v3d, BL::Scene &b_scene);
   /* Retrieve the render pass that needs to be displayed on the given `SpaceView3D`
    * When the `b_v3d` parameter is not given `PASS_NONE` will be returned. */
   static PassType get_viewport_display_render_pass(BL::SpaceView3D &b_v3d);
 };
 
-bool update_viewport_display_denoising(BL::SpaceView3D &b_v3d, BL::Scene &b_scene);
-
 PassType update_viewport_display_passes(BL::SpaceView3D &b_v3d, vector<Pass> &passes);
 
 CCL_NAMESPACE_END
index 263d3d24b1339cd0e03a848e10cfd4a3fe0cb014..73415d5f9c62ad40c9fed1e0dbf3062ea3066077 100644 (file)
@@ -603,6 +603,7 @@ DeviceInfo Device::get_multi_device(const vector<DeviceInfo> &subdevices,
   info.has_osl = true;
   info.has_profiling = true;
   info.has_peer_memory = false;
+  info.denoisers = DENOISER_ALL;
 
   foreach (const DeviceInfo &device, subdevices) {
     /* Ensure CPU device does not slow down GPU. */
@@ -647,6 +648,7 @@ DeviceInfo Device::get_multi_device(const vector<DeviceInfo> &subdevices,
     info.has_osl &= device.has_osl;
     info.has_profiling &= device.has_profiling;
     info.has_peer_memory |= device.has_peer_memory;
+    info.denoisers &= device.denoisers;
   }
 
   return info;
@@ -667,4 +669,43 @@ void Device::free_memory()
   network_devices.free_memory();
 }
 
+/* DeviceInfo */
+
+void DeviceInfo::add_denoising_devices(DenoiserType denoiser_type)
+{
+  assert(denoising_devices.empty());
+
+  if (denoiser_type == DENOISER_OPTIX && type != DEVICE_OPTIX) {
+    vector<DeviceInfo> optix_devices = Device::available_devices(DEVICE_MASK_OPTIX);
+    if (!optix_devices.empty()) {
+      /* Convert to a special multi device with separate denoising devices. */
+      if (multi_devices.empty()) {
+        multi_devices.push_back(*this);
+      }
+
+      /* Try to use the same physical devices for denoising. */
+      for (const DeviceInfo &cuda_device : multi_devices) {
+        if (cuda_device.type == DEVICE_CUDA) {
+          for (const DeviceInfo &optix_device : optix_devices) {
+            if (cuda_device.num == optix_device.num) {
+              id += optix_device.id;
+              denoising_devices.push_back(optix_device);
+              break;
+            }
+          }
+        }
+      }
+
+      if (denoising_devices.empty()) {
+        /* Simply use the first available OptiX device. */
+        const DeviceInfo optix_device = optix_devices.front();
+        id += optix_device.id; /* Uniquely identify this special multi device. */
+        denoising_devices.push_back(optix_device);
+      }
+
+      denoisers = denoiser_type;
+    }
+  }
+}
+
 CCL_NAMESPACE_END
index 69f22aeb35cd33ae560456505c0f48558b91b487..a5833369a17be2e0356fa3a6635a65f73c5ed581 100644 (file)
@@ -83,6 +83,7 @@ class DeviceInfo {
   bool use_split_kernel;             /* Use split or mega kernel. */
   bool has_profiling;                /* Supports runtime collection of profiling info. */
   bool has_peer_memory;              /* GPU has P2P access to memory of another GPU. */
+  DenoiserTypeMask denoisers;        /* Supported denoiser types. */
   int cpu_threads;
   vector<DeviceInfo> multi_devices;
   vector<DeviceInfo> denoising_devices;
@@ -101,6 +102,7 @@ class DeviceInfo {
     use_split_kernel = false;
     has_profiling = false;
     has_peer_memory = false;
+    denoisers = DENOISER_NONE;
   }
 
   bool operator==(const DeviceInfo &info)
@@ -110,6 +112,9 @@ class DeviceInfo {
            (type == info.type && num == info.num && description == info.description));
     return id == info.id;
   }
+
+  /* Add additional devices needed for the specified denoiser. */
+  void add_denoising_devices(DenoiserType denoiser_type);
 };
 
 class DeviceRequestedFeatures {
index a36c76c852a317b570b3453d2dce5a37c9f2ff48..1f760a1553074fec0b8ceb867c327904fdd71562 100644 (file)
@@ -943,7 +943,7 @@ class CPUDevice : public Device {
     }
   }
 
-  void denoise(DenoisingTask &denoising, RenderTile &tile)
+  void denoise_nlm(DenoisingTask &denoising, RenderTile &tile)
   {
     ProfilingHelper profiling(denoising.profiler, PROFILING_DENOISING);
 
@@ -1001,10 +1001,9 @@ class CPUDevice : public Device {
       }
     }
 
-    RenderTile tile;
-    DenoisingTask denoising(this, task);
-    denoising.profiler = &kg->profiler;
+    DenoisingTask *denoising = NULL;
 
+    RenderTile tile;
     while (task.acquire_tile(this, tile, task.tile_types)) {
       if (tile.task == RenderTile::PATH_TRACE) {
         if (use_split_kernel) {
@@ -1019,7 +1018,13 @@ class CPUDevice : public Device {
         render(task, tile, kg);
       }
       else if (tile.task == RenderTile::DENOISE) {
-        denoise(denoising, tile);
+        if (task.denoising.type == DENOISER_NLM) {
+          if (denoising == NULL) {
+            denoising = new DenoisingTask(this, task);
+            denoising->profiler = &kg->profiler;
+          }
+          denoise_nlm(*denoising, tile);
+        }
         task.update_progress(&tile, tile.w * tile.h);
       }
 
@@ -1037,6 +1042,7 @@ class CPUDevice : public Device {
     kg->~KernelGlobals();
     kgbuffer.free();
     delete split_kernel;
+    delete denoising;
   }
 
   void thread_denoise(DeviceTask &task)
@@ -1060,7 +1066,7 @@ class CPUDevice : public Device {
     profiler.add_state(&denoising_profiler_state);
     denoising.profiler = &denoising_profiler_state;
 
-    denoise(denoising, tile);
+    denoise_nlm(denoising, tile);
     task.update_progress(&tile, tile.w * tile.h);
 
     profiler.remove_state(&denoising_profiler_state);
@@ -1344,6 +1350,7 @@ void device_cpu_info(vector<DeviceInfo> &devices)
   info.has_osl = true;
   info.has_half_images = true;
   info.has_profiling = true;
+  info.denoisers = DENOISER_NLM;
 
   devices.insert(devices.begin(), info);
 }
index 04c0476131109d6bb8a4dd339a1d9eb39f7aa449..d9ffcceb06e2f1375c6cfc3e61e46d0451e07b15 100644 (file)
@@ -130,6 +130,7 @@ void device_cuda_info(vector<DeviceInfo> &devices)
     info.has_half_images = (major >= 3);
     info.has_volume_decoupled = false;
     info.has_adaptive_stop_per_sample = false;
+    info.denoisers = DENOISER_NLM;
 
     /* Check if the device has P2P access to any other device in the system. */
     for (int peer_num = 0; peer_num < count && !info.has_peer_memory; peer_num++) {
index ac17c02a4277998f31dbb65cf2b6c457c1f883e4..91ebb7c266e0627b0f24cdb935a862bef7e1bbbc 100644 (file)
@@ -56,8 +56,8 @@ DenoisingTask::DenoisingTask(Device *device, const DeviceTask &task)
     tile_info->frames[i] = task.denoising_frames[i - 1];
   }
 
-  write_passes = task.denoising_write_passes;
-  do_filter = task.denoising_do_filter;
+  do_prefilter = task.denoising.store_passes && task.denoising.type == DENOISER_NLM;
+  do_filter = task.denoising_do_filter && task.denoising.type == DENOISER_NLM;
 }
 
 DenoisingTask::~DenoisingTask()
@@ -91,7 +91,7 @@ void DenoisingTask::set_render_buffer(RenderTile *rtiles)
   target_buffer.stride = rtiles[9].stride;
   target_buffer.ptr = rtiles[9].buffer;
 
-  if (write_passes && rtiles[9].buffers) {
+  if (do_prefilter && rtiles[9].buffers) {
     target_buffer.denoising_output_offset =
         rtiles[9].buffers->params.get_denoising_prefiltered_offset();
   }
@@ -111,7 +111,7 @@ void DenoisingTask::setup_denoising_buffer()
   rect = rect_clip(rect,
                    make_int4(tile_info->x[0], tile_info->y[0], tile_info->x[3], tile_info->y[3]));
 
-  buffer.use_intensity = write_passes || (tile_info->num_frames > 1);
+  buffer.use_intensity = do_prefilter || (tile_info->num_frames > 1);
   buffer.passes = buffer.use_intensity ? 15 : 14;
   buffer.width = rect.z - rect.x;
   buffer.stride = align_up(buffer.width, 4);
@@ -343,7 +343,7 @@ void DenoisingTask::run_denoising(RenderTile *tile)
     reconstruct();
   }
 
-  if (write_passes) {
+  if (do_prefilter) {
     write_buffer();
   }
 
index bd1d0193dbdbd8338c81a302a6732a64a2517d2f..4c122e981eb958100dc7fa61384032983d70c681 100644 (file)
@@ -60,7 +60,7 @@ class DenoisingTask {
   int4 rect;
   int4 filter_area;
 
-  bool write_passes;
+  bool do_prefilter;
   bool do_filter;
 
   struct DeviceFunctions {
index 0933d51f3210b2bd6b7c475712a90f9134a9d34e..8904b517e92bec10831ddd26426f1b9fc77a97e3 100644 (file)
@@ -313,6 +313,7 @@ void device_network_info(vector<DeviceInfo> &devices)
   info.has_volume_decoupled = false;
   info.has_adaptive_stop_per_sample = false;
   info.has_osl = false;
+  info.denoisers = DENOISER_NONE;
 
   devices.push_back(info);
 }
index 8a0b128697f3bbba5a4acb91ecfcbe5d486215d6..39b9ef701920663a7fb304fc63fc8469dfd90287 100644 (file)
@@ -120,6 +120,7 @@ void device_opencl_info(vector<DeviceInfo> &devices)
     info.use_split_kernel = true;
     info.has_volume_decoupled = false;
     info.has_adaptive_stop_per_sample = false;
+    info.denoisers = DENOISER_NLM;
     info.id = id;
 
     /* Check OpenCL extensions */
index 7aab2c96db49323254cf7bbd993227c0bc6bd99b..d4d859e8593ced195fb3176f448d0259aeb4fcc0 100644 (file)
@@ -721,7 +721,7 @@ class OptiXDevice : public CUDADevice {
     const CUDAContextScope scope(cuContext);
 
     // Choose between OptiX and NLM denoising
-    if (task.denoising_use_optix) {
+    if (task.denoising.type == DENOISER_OPTIX) {
       // Map neighboring tiles onto this device, indices are as following:
       // Where index 4 is the center tile and index 9 is the target for the result.
       //   0 1 2
@@ -1561,6 +1561,7 @@ void device_optix_info(const vector<DeviceInfo> &cuda_devices, vector<DeviceInfo
 
     info.type = DEVICE_OPTIX;
     info.id += "_OptiX";
+    info.denoisers |= DENOISER_OPTIX;
 
     devices.push_back(info);
   }
index 5f675bf7e0410457396a21d76944deb7b8aa2740..1ad8e0d9485bfcfb9a47d78bad2a57f62313c04d 100644 (file)
@@ -31,8 +31,32 @@ class RenderBuffers;
 class RenderTile;
 class Tile;
 
+enum DenoiserType {
+  DENOISER_NLM = 1,
+  DENOISER_OPTIX = 2,
+  DENOISER_NUM,
+
+  DENOISER_NONE = 0,
+  DENOISER_ALL = ~0,
+};
+
+typedef int DenoiserTypeMask;
+
 class DenoiseParams {
  public:
+  /* Apply denoiser to image. */
+  bool use;
+  /* Output denoising data passes (possibly without applying the denoiser). */
+  bool store_passes;
+
+  /* Denoiser type. */
+  DenoiserType type;
+
+  /* Viewport start sample. */
+  int start_sample;
+
+  /** Native Denoiser **/
+
   /* Pixel radius for neighboring pixels to take into account. */
   int radius;
   /* Controls neighbor pixel weighting for the denoising filter. */
@@ -46,18 +70,36 @@ class DenoiseParams {
   int neighbor_frames;
   /* Clamp the input to the range of +-1e8. Should be enough for any legitimate data. */
   bool clamp_input;
+
+  /** Optix Denoiser **/
+
   /* Passes handed over to the OptiX denoiser (default to color + albedo). */
   int optix_input_passes;
 
   DenoiseParams()
   {
+    use = false;
+    store_passes = false;
+
+    type = DENOISER_NLM;
+
     radius = 8;
     strength = 0.5f;
     feature_strength = 0.5f;
     relative_pca = false;
     neighbor_frames = 2;
     clamp_input = true;
+
     optix_input_passes = 2;
+
+    start_sample = 0;
+  }
+
+  /* Test if a denoising task needs to run, also to prefilter passes for the native
+   * denoiser when we are not applying denoising to the combined image. */
+  bool need_denoising_task() const
+  {
+    return (use || (store_passes && type == DENOISER_NLM));
   }
 };
 
@@ -116,8 +158,7 @@ class DeviceTask {
   vector<int> denoising_frames;
 
   bool denoising_do_filter;
-  bool denoising_use_optix;
-  bool denoising_write_passes;
+  bool denoising_do_prefilter;
 
   int pass_stride;
   int frame_stride;
index 93815ebfbc412900fc8b94270c9f2c35d09954b3..4055bc4773b1eee74073415ca6652dd96b40bef4 100644 (file)
@@ -378,8 +378,9 @@ void DenoiseTask::create_task(DeviceTask &task)
 
   /* Denoising parameters. */
   task.denoising = denoiser->params;
-  task.denoising_do_filter = true;
-  task.denoising_write_passes = false;
+  task.denoising.type = DENOISER_NLM;
+  task.denoising.use = true;
+  task.denoising.store_passes = false;
   task.denoising_from_render = false;
 
   task.denoising_frames.resize(neighbor_frames.size());
index 9d5e57404f08817c5e7930c3a62a74a4d6983b37..f11722ac9a93bf4bd3ff05ee1955434dace71a59 100644 (file)
@@ -805,7 +805,7 @@ DeviceRequestedFeatures Session::get_requested_device_features()
   requested_features.use_baking = bake_manager->get_baking();
   requested_features.use_integrator_branched = (scene->integrator->method ==
                                                 Integrator::BRANCHED_PATH);
-  if (params.run_denoising) {
+  if (params.denoising.use || params.denoising.store_passes) {
     requested_features.use_denoising = true;
     requested_features.use_shadow_tricks = true;
   }
@@ -942,24 +942,31 @@ void Session::set_pause(bool pause_)
     pause_cond.notify_all();
 }
 
-void Session::set_denoising(bool denoising, bool optix_denoising)
+void Session::set_denoising(const DenoiseParams &denoising)
 {
+  const bool need_denoise = denoising.need_denoising_task();
+
+  if (need_denoise && !(params.device.denoisers & denoising.type)) {
+    progress.set_error("Denoiser type not supported by compute device");
+    return;
+  }
+
   /* Lock buffers so no denoising operation is triggered while the settings are changed here. */
   thread_scoped_lock buffers_lock(buffers_mutex);
-
-  params.run_denoising = denoising;
-  params.full_denoising = !optix_denoising;
-  params.optix_denoising = optix_denoising;
+  params.denoising = denoising;
 
   // TODO(pmours): Query the required overlap value for denoising from the device?
-  tile_manager.slice_overlap = denoising && !params.background ? 64 : 0;
-  tile_manager.schedule_denoising = denoising && !buffers;
+  tile_manager.slice_overlap = need_denoise && !params.background ? 64 : 0;
+
+  /* Schedule per tile denoising for final renders if we are either denoising or
+   * need prefiltered passes for the native denoiser. */
+  tile_manager.schedule_denoising = need_denoise && !buffers;
 }
 
 void Session::set_denoising_start_sample(int sample)
 {
-  if (sample != params.denoising_start_sample) {
-    params.denoising_start_sample = sample;
+  if (sample != params.denoising.start_sample) {
+    params.denoising.start_sample = sample;
 
     pause_cond.notify_all();
   }
@@ -1079,10 +1086,10 @@ void Session::update_status_time(bool show_pause, bool show_done)
        */
       substatus += string_printf(", Sample %d/%d", progress.get_current_sample(), num_samples);
     }
-    if (params.full_denoising || params.optix_denoising) {
+    if (params.denoising.use) {
       substatus += string_printf(", Denoised %d tiles", progress.get_denoised_tiles());
     }
-    else if (params.run_denoising) {
+    else if (params.denoising.store_passes && params.denoising.type == DENOISER_NLM) {
       substatus += string_printf(", Prefiltered %d tiles", progress.get_denoised_tiles());
     }
   }
@@ -1111,7 +1118,7 @@ bool Session::render_need_denoise(bool &delayed)
   delayed = false;
 
   /* Denoising enabled? */
-  if (!params.run_denoising) {
+  if (!params.denoising.need_denoising_task()) {
     return false;
   }
 
@@ -1128,7 +1135,7 @@ bool Session::render_need_denoise(bool &delayed)
   }
 
   /* Do not denoise until the sample at which denoising should start is reached. */
-  if (tile_manager.state.sample < params.denoising_start_sample) {
+  if (tile_manager.state.sample < params.denoising.start_sample) {
     return false;
   }
 
@@ -1179,9 +1186,6 @@ void Session::render(bool need_denoise)
     task.pass_denoising_clean = scene->film->denoising_clean_offset;
 
     task.denoising_from_render = true;
-    task.denoising_do_filter = params.full_denoising;
-    task.denoising_use_optix = params.optix_denoising;
-    task.denoising_write_passes = params.write_denoising_passes;
 
     if (tile_manager.schedule_denoising) {
       /* Acquire denoising tiles during rendering. */
index 2707eed5531b26462a4bb5bc3afdd031ab6a5e4c..0141629762c507ab372bab45f0f4604a3a5bf748 100644 (file)
@@ -62,10 +62,6 @@ class SessionParams {
 
   bool display_buffer_linear;
 
-  bool run_denoising;
-  bool write_denoising_passes;
-  bool full_denoising;
-  bool optix_denoising;
   DenoiseParams denoising;
 
   double cancel_timeout;
@@ -94,11 +90,6 @@ class SessionParams {
 
     use_profiling = false;
 
-    run_denoising = false;
-    write_denoising_passes = false;
-    full_denoising = false;
-    optix_denoising = false;
-
     display_buffer_linear = false;
 
     cancel_timeout = 0.1;
@@ -125,7 +116,8 @@ class SessionParams {
              cancel_timeout == params.cancel_timeout && reset_timeout == params.reset_timeout &&
              text_timeout == params.text_timeout &&
              progressive_update_timeout == params.progressive_update_timeout &&
-             tile_order == params.tile_order && shadingsystem == params.shadingsystem);
+             tile_order == params.tile_order && shadingsystem == params.shadingsystem &&
+             denoising.type == params.denoising.type);
   }
 };
 
@@ -161,7 +153,7 @@ class Session {
   void reset(BufferParams &params, int samples);
   void set_pause(bool pause);
   void set_samples(int samples);
-  void set_denoising(bool denoising, bool optix_denoising);
+  void set_denoising(const DenoiseParams &denoising);
   void set_denoising_start_sample(int sample);
 
   bool update_scene();
index 9d948dfd57bca9d44aadf1eb6753526a5c7122f2..229b5193e9ca712e272dc6d37a6b5309fd2cfaf7 100644 (file)
@@ -40,7 +40,7 @@ extern "C" {
 
 /* Blender file format version. */
 #define BLENDER_FILE_VERSION BLENDER_VERSION
-#define BLENDER_FILE_SUBVERSION 4
+#define BLENDER_FILE_SUBVERSION 5
 
 /* Minimum Blender version that supports reading file written with the current
  * version. Older Blender versions will test this and show a warning if the file
index ff3d4574561af79921328854f662e036f7ed8329..72ee4c5ec4d7da1a269eaf31cfa78de34d5d481a 100644 (file)
@@ -78,22 +78,45 @@ static IDProperty *cycles_properties_from_ID(ID *id)
   return (idprop) ? IDP_GetPropertyTypeFromGroup(idprop, "cycles", IDP_GROUP) : NULL;
 }
 
+static IDProperty *cycles_properties_from_view_layer(ViewLayer *view_layer)
+{
+  IDProperty *idprop = view_layer->id_properties;
+  return (idprop) ? IDP_GetPropertyTypeFromGroup(idprop, "cycles", IDP_GROUP) : NULL;
+}
+
 static float cycles_property_float(IDProperty *idprop, const char *name, float default_value)
 {
   IDProperty *prop = IDP_GetPropertyTypeFromGroup(idprop, name, IDP_FLOAT);
   return (prop) ? IDP_Float(prop) : default_value;
 }
 
-static float cycles_property_int(IDProperty *idprop, const char *name, int default_value)
+static int cycles_property_int(IDProperty *idprop, const char *name, int default_value)
 {
   IDProperty *prop = IDP_GetPropertyTypeFromGroup(idprop, name, IDP_INT);
   return (prop) ? IDP_Int(prop) : default_value;
 }
 
-static bool cycles_property_boolean(IDProperty *idprop, const char *name, bool default_value)
+static void cycles_property_int_set(IDProperty *idprop, const char *name, int value)
 {
   IDProperty *prop = IDP_GetPropertyTypeFromGroup(idprop, name, IDP_INT);
-  return (prop) ? IDP_Int(prop) : default_value;
+  if (prop) {
+    IDP_Int(prop) = value;
+  }
+  else {
+    IDPropertyTemplate val = {0};
+    val.i = value;
+    IDP_AddToGroup(idprop, IDP_New(IDP_INT, &val, name));
+  }
+}
+
+static bool cycles_property_boolean(IDProperty *idprop, const char *name, bool default_value)
+{
+  return cycles_property_int(idprop, name, default_value);
+}
+
+static void cycles_property_boolean_set(IDProperty *idprop, const char *name, bool value)
+{
+  cycles_property_int_set(idprop, name, value);
 }
 
 static void displacement_node_insert(bNodeTree *ntree)
@@ -1524,4 +1547,52 @@ void do_versions_after_linking_cycles(Main *bmain)
     }
     FOREACH_NODETREE_END;
   }
+
+  if (!MAIN_VERSION_ATLEAST(bmain, 290, 5)) {
+    /* New denoiser settings. */
+    for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) {
+      IDProperty *cscene = cycles_properties_from_ID(&scene->id);
+
+      /* Check if any view layers had (optix) denoising enabled. */
+      bool use_optix = false;
+      bool use_denoising = false;
+      for (ViewLayer *view_layer = scene->view_layers.first; view_layer;
+           view_layer = view_layer->next) {
+        IDProperty *cview_layer = cycles_properties_from_view_layer(view_layer);
+        if (cview_layer) {
+          use_denoising = use_denoising ||
+                          cycles_property_boolean(cview_layer, "use_denoising", false);
+          use_optix = use_optix ||
+                      cycles_property_boolean(cview_layer, "use_optix_denoising", false);
+        }
+      }
+
+      if (cscene) {
+        const int DENOISER_NLM = 1;
+        const int DENOISER_OPTIX = 2;
+
+        /* Enable denoiser if it was enabled for one view layer before. */
+        cycles_property_int_set(cscene, "denoiser", (use_optix) ? DENOISER_OPTIX : DENOISER_NLM);
+        cycles_property_boolean_set(cscene, "use_denoising", use_denoising);
+
+        /* Migrate Optix denoiser to new settings. */
+        if (cycles_property_int(cscene, "preview_denoising", 0)) {
+          cycles_property_boolean_set(cscene, "use_preview_denoising", true);
+          cycles_property_boolean_set(cscene, "preview_denoiser", DENOISER_OPTIX);
+        }
+      }
+
+      /* Enable denoising in all view layer if there was no denoising before,
+       * so that enabling the scene settings auto enables it for all view layers. */
+      if (!use_denoising) {
+        for (ViewLayer *view_layer = scene->view_layers.first; view_layer;
+             view_layer = view_layer->next) {
+          IDProperty *cview_layer = cycles_properties_from_view_layer(view_layer);
+          if (cview_layer) {
+            cycles_property_boolean_set(cview_layer, "use_denoising", true);
+          }
+        }
+      }
+    }
+  }
 }