[Bf-blender-cvs] [669befdfbe4] master: Cycles: add Intel OpenImageDenoise support for viewport denoising

Brecht Van Lommel noreply at git.blender.org
Wed Jun 24 15:19:36 CEST 2020


Commit: 669befdfbe487f76c65f54e3da0013d140d56893
Author: Brecht Van Lommel
Date:   Mon Jun 1 00:11:17 2020 +0200
Branches: master
https://developer.blender.org/rB669befdfbe487f76c65f54e3da0013d140d56893

Cycles: add Intel OpenImageDenoise support for viewport denoising

Compared to Optix denoise, this is usually slower since there is no GPU
acceleration. Some optimizations may still be possible, in avoid copies
to the GPU and/or denoising less often.

The main thing is that this adds viewport denoising support for computers
without an NVIDIA GPU (as long as the CPU supports SSE 4.1, which is nearly
all of them).

Ref T76259

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

M	intern/cycles/blender/CMakeLists.txt
M	intern/cycles/blender/addon/properties.py
M	intern/cycles/blender/addon/ui.py
M	intern/cycles/blender/blender_python.cpp
M	intern/cycles/blender/blender_sync.cpp
M	intern/cycles/device/CMakeLists.txt
M	intern/cycles/device/device.cpp
M	intern/cycles/device/device_cpu.cpp
M	intern/cycles/device/device_task.h
M	intern/cycles/render/session.cpp
M	intern/cycles/util/CMakeLists.txt
A	intern/cycles/util/util_openimagedenoise.h

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

diff --git a/intern/cycles/blender/CMakeLists.txt b/intern/cycles/blender/CMakeLists.txt
index 4589f4573d9..2316800e21e 100644
--- a/intern/cycles/blender/CMakeLists.txt
+++ b/intern/cycles/blender/CMakeLists.txt
@@ -102,6 +102,13 @@ if(WITH_OPENVDB)
   )
 endif()
 
+if(WITH_OPENIMAGEDENOISE)
+  add_definitions(-DWITH_OPENIMAGEDENOISE)
+  list(APPEND INC_SYS
+    ${OPENIMAGEDENOISE_INCLUDE_DIRS}
+  )
+endif()
+
 blender_add_lib(bf_intern_cycles "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
 
 # avoid link failure with clang 3.4 debug
diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py
index 061e3784b0d..053de16529c 100644
--- a/intern/cycles/blender/addon/properties.py
+++ b/intern/cycles/blender/addon/properties.py
@@ -182,14 +182,30 @@ enum_aov_types = (
     ('COLOR', "Color", "Write a Color pass", 1),
 )
 
+def enum_openimagedenoise_denoiser(self, context):
+    if _cycles.with_openimagedenoise:
+        return [('OPENIMAGEDENOISE', "OpenImageDenoise", "Use Intel OpenImageDenoise AI denoiser running on the CPU", 4)]
+    return []
+
 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)
+    optix_items = enum_optix_denoiser(self, context)
+    oidn_items = enum_openimagedenoise_denoiser(self, context)
+
+    if len(optix_items):
+        auto_label = "Fastest (Optix)"
+    elif len(oidn_items):
+        auto_label = "Fatest (OpenImageDenoise)"
+    else:
+        auto_label = "None"
+
+    items = [('AUTO', auto_label, "Use the fastest available denoiser for viewport rendering", 0)]
+    items += optix_items
+    items += oidn_items
     return items
 
 def enum_denoiser(self, context):
diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py
index e689ec90983..aa0a47eb9c7 100644
--- a/intern/cycles/blender/addon/ui.py
+++ b/intern/cycles/blender/addon/ui.py
@@ -1006,6 +1006,8 @@ class CYCLES_RENDER_PT_denoising(CyclesButtonsPanel, Panel):
         if denoiser == 'OPTIX':
             col.prop(cycles_view_layer, "denoising_optix_input_passes")
             return
+        elif denoiser == 'OPENIMAGEDENOISE':
+            return
 
         col.prop(cycles_view_layer, "denoising_radius", text="Radius")
 
diff --git a/intern/cycles/blender/blender_python.cpp b/intern/cycles/blender/blender_python.cpp
index 5595d657640..3e595c3ee52 100644
--- a/intern/cycles/blender/blender_python.cpp
+++ b/intern/cycles/blender/blender_python.cpp
@@ -31,6 +31,7 @@
 #include "util/util_logging.h"
 #include "util/util_md5.h"
 #include "util/util_opengl.h"
+#include "util/util_openimagedenoise.h"
 #include "util/util_path.h"
 #include "util/util_string.h"
 #include "util/util_task.h"
@@ -1076,5 +1077,14 @@ void *CCL_python_module_init()
   Py_INCREF(Py_False);
 #endif /* WITH_EMBREE */
 
+  if (ccl::openimagedenoise_supported()) {
+    PyModule_AddObject(mod, "with_openimagedenoise", Py_True);
+    Py_INCREF(Py_True);
+  }
+  else {
+    PyModule_AddObject(mod, "with_openimagedenoise", Py_False);
+    Py_INCREF(Py_False);
+  }
+
   return (void *)mod;
 }
diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp
index aed92cf1376..bf065cc5492 100644
--- a/intern/cycles/blender/blender_sync.cpp
+++ b/intern/cycles/blender/blender_sync.cpp
@@ -38,6 +38,7 @@
 #include "util/util_foreach.h"
 #include "util/util_hash.h"
 #include "util/util_opengl.h"
+#include "util/util_openimagedenoise.h"
 
 CCL_NAMESPACE_BEGIN
 
@@ -957,6 +958,9 @@ DenoiseParams BlenderSync::get_denoise_params(BL::Scene &b_scene,
       if (!Device::available_devices(DEVICE_MASK_OPTIX).empty()) {
         denoising.type = DENOISER_OPTIX;
       }
+      else if (openimagedenoise_supported()) {
+        denoising.type = DENOISER_OPENIMAGEDENOISE;
+      }
       else {
         denoising.use = false;
       }
diff --git a/intern/cycles/device/CMakeLists.txt b/intern/cycles/device/CMakeLists.txt
index aa5b65a2b73..ca366722eb7 100644
--- a/intern/cycles/device/CMakeLists.txt
+++ b/intern/cycles/device/CMakeLists.txt
@@ -99,6 +99,18 @@ if(WITH_CYCLES_DEVICE_MULTI)
   add_definitions(-DWITH_MULTI)
 endif()
 
+if(WITH_OPENIMAGEDENOISE)
+  add_definitions(-DWITH_OPENIMAGEDENOISE)
+  add_definitions(-DOIDN_STATIC_LIB)
+  list(APPEND INC_SYS
+    ${OPENIMAGEDENOISE_INCLUDE_DIRS}
+  )
+  list(APPEND LIB
+    ${OPENIMAGEDENOISE_LIBRARIES}
+    ${TBB_LIBRARIES}
+  )
+endif()
+
 include_directories(${INC})
 include_directories(SYSTEM ${INC_SYS})
 
diff --git a/intern/cycles/device/device.cpp b/intern/cycles/device/device.cpp
index 73415d5f9c6..9dbb33980b4 100644
--- a/intern/cycles/device/device.cpp
+++ b/intern/cycles/device/device.cpp
@@ -706,6 +706,18 @@ void DeviceInfo::add_denoising_devices(DenoiserType denoiser_type)
       denoisers = denoiser_type;
     }
   }
+  else if (denoiser_type == DENOISER_OPENIMAGEDENOISE && type != DEVICE_CPU) {
+    /* Convert to a special multi device with separate denoising devices. */
+    if (multi_devices.empty()) {
+      multi_devices.push_back(*this);
+    }
+
+    /* Add CPU denoising devices. */
+    DeviceInfo cpu_device = Device::available_devices(DEVICE_MASK_CPU).front();
+    denoising_devices.push_back(cpu_device);
+
+    denoisers = denoiser_type;
+  }
 }
 
 CCL_NAMESPACE_END
diff --git a/intern/cycles/device/device_cpu.cpp b/intern/cycles/device/device_cpu.cpp
index 1f760a15530..8f68e66a1b4 100644
--- a/intern/cycles/device/device_cpu.cpp
+++ b/intern/cycles/device/device_cpu.cpp
@@ -51,6 +51,7 @@
 #include "util/util_function.h"
 #include "util/util_logging.h"
 #include "util/util_map.h"
+#include "util/util_openimagedenoise.h"
 #include "util/util_opengl.h"
 #include "util/util_optimization.h"
 #include "util/util_progress.h"
@@ -177,6 +178,10 @@ class CPUDevice : public Device {
 #ifdef WITH_OSL
   OSLGlobals osl_globals;
 #endif
+#ifdef WITH_OPENIMAGEDENOISE
+  oidn::DeviceRef oidn_device;
+  oidn::FilterRef oidn_filter;
+#endif
 
   bool use_split_kernel;
 
@@ -943,6 +948,70 @@ class CPUDevice : public Device {
     }
   }
 
+  void denoise_openimagedenoise(DeviceTask &task, RenderTile &rtile)
+  {
+#ifdef WITH_OPENIMAGEDENOISE
+    assert(openimagedenoise_supported());
+
+    /* Only one at a time, since OpenImageDenoise itself is multithreaded. */
+    static thread_mutex mutex;
+    thread_scoped_lock lock(mutex);
+
+    /* Create device and filter, cached for reuse. */
+    if (!oidn_device) {
+      oidn_device = oidn::newDevice();
+      oidn_device.commit();
+    }
+    if (!oidn_filter) {
+      oidn_filter = oidn_device.newFilter("RT");
+    }
+
+    /* Copy pixels from compute device to CPU (no-op for CPU device). */
+    rtile.buffers->buffer.copy_from_device();
+
+    /* Set images with appropriate stride for our interleaved pass storage. */
+    const struct {
+      const char *name;
+      int offset;
+    } passes[] = {{"color", task.pass_denoising_data + DENOISING_PASS_COLOR},
+                  {"normal", task.pass_denoising_data + DENOISING_PASS_NORMAL},
+                  {"albedo", task.pass_denoising_data + DENOISING_PASS_ALBEDO},
+                  {"output", 0},
+                  { NULL,
+                    0 }};
+
+    for (int i = 0; passes[i].name; i++) {
+      const int64_t offset = rtile.offset + rtile.x + rtile.y * rtile.stride;
+      const int64_t buffer_offset = (offset * task.pass_stride + passes[i].offset) * sizeof(float);
+      const int64_t pixel_stride = task.pass_stride * sizeof(float);
+      const int64_t row_stride = rtile.stride * pixel_stride;
+
+      oidn_filter.setImage(passes[i].name,
+                           (char *)rtile.buffer + buffer_offset,
+                           oidn::Format::Float3,
+                           rtile.w,
+                           rtile.h,
+                           0,
+                           pixel_stride,
+                           row_stride);
+    }
+
+    /* Execute filter. */
+    oidn_filter.set("hdr", true);
+    oidn_filter.set("srgb", false);
+    oidn_filter.commit();
+    oidn_filter.execute();
+
+    /* todo: it may be possible to avoid this copy, but we have to ensure that
+     * when other code copies data from the device it doesn't overwrite the
+     * denoiser buffers. */
+    rtile.buffers->buffer.copy_to_device();
+#else
+    (void)task;
+    (void)rtile;
+#endif
+  }
+
   void denoise_nlm(DenoisingTask &denoising, RenderTile &tile)
   {
     ProfilingHelper profiling(denoising.profiler, PROFILING_DENOISING);
@@ -1018,7 +1087,10 @@ class CPUDevice : public Device {
         render(task, tile, kg);
       }
       else if (tile.task == RenderTile::DENOISE) {
-        if (task.denoising.type == DENOISER_NLM) {
+        if (task.denoising.type == DENOISER_OPENIMAGEDENOISE) {
+          denoise_openimagedenoise(task, tile);
+        }
+        else if (task.denoising.type == DENOISER_NLM) {
           if (denoising == NULL) {
             denoising = new DenoisingTask(this, task);
             denoising->profiler = &kg->profiler;
@@ -1060,16 +1132,22 @@ class CPUDevice : public Device {
     tile.stride = task.stride;
     tile.buffers = task.buffers;
 
-    DenoisingTask denoising(this, task);
+    if (task.denoising.type == DENOISER_OPENIMAGEDENOISE) {
+      denoise_openimagedenoise(task, tile);
+    }
+    else {
+      DenoisingTask denoising(this, task);
 
-    ProfilingState denoising_profiler_state;
-    profiler.add_state(&denoising_profiler_state);
-    denoising.profiler = &denoising_profiler_state;
+      ProfilingState denoising_profiler_state;
+      profiler.add_state(&denoising_profiler_state);
+      denoising.profiler = &denoising_profiler_state;
 
-    denoise_nlm(denoising, tile);
-    task.update_progress(&tile, tile.w * tile.h);
+      denoise_nlm(denoising, tile);
 
-    profiler.remove_state(&denoising_profil

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list