Blender V5.0
denoiser.cpp
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
6
7#include "device/device.h"
8
11#ifdef WITH_OPENIMAGEDENOISE
13#endif
14#ifdef WITH_OPTIX
16#endif
17#include "session/buffers.h"
18
19#include "util/log.h"
21#include "util/progress.h"
22
24
25/* Check whether given device is single (not a MultiDevice). */
26static bool is_single_device(const Device *device)
27{
28 if (device->info.type == DEVICE_MULTI) {
29 /* Assume multi-device is never created with a single sub-device.
30 * If one requests such configuration it should be checked on the session level. */
31 return false;
32 }
33
34 if (!device->info.multi_devices.empty()) {
35 /* Some configurations will use multi_devices, but keep the type of an individual device.
36 * This does simplify checks for homogeneous setups, but here we really need a single device.
37 */
38 return false;
39 }
40
41 return true;
42}
43
44/* Find best suitable device to perform denoiser on. Will iterate over possible sub-devices of
45 * multi-device. */
47 const DenoiserType type,
48 const GraphicsInteropDevice &interop_device)
49{
50 Device *best_device = nullptr;
51
52 device->foreach_device([&](Device *sub_device) {
53 if ((sub_device->info.denoisers & type) == 0) {
54 return;
55 }
56
57 if (!best_device) {
58 best_device = sub_device;
59 }
60 else {
61 /* Prefer non-CPU devices over CPU for performance reasons. */
62 if (sub_device->info.type != DEVICE_CPU && best_device->info.type == DEVICE_CPU) {
63 best_device = sub_device;
64 }
65
66 /* Prefer a device that can use graphics interop for faster display update. */
67 if (sub_device->should_use_graphics_interop(interop_device) &&
68 !best_device->should_use_graphics_interop(interop_device))
69 {
70 best_device = sub_device;
71 }
72
73 /* TODO(sergey): Choose fastest device from available ones. Taking into account performance
74 * of the device and data transfer cost. */
75 }
76 });
77
78 return best_device;
79}
80
81bool use_optix_denoiser(Device *denoiser_device, const DenoiseParams &params)
82{
83#ifdef WITH_OPTIX
84 return (params.type == DENOISER_OPTIX &&
85 OptiXDenoiser::is_device_supported(denoiser_device->info));
86#else
87 (void)denoiser_device;
88 (void)params;
89 return false;
90#endif
91}
92
93bool use_gpu_oidn_denoiser(Device *denoiser_device, const DenoiseParams &params)
94{
95#ifdef WITH_OPENIMAGEDENOISE
96 return (params.type == DENOISER_OPENIMAGEDENOISE && params.use_gpu &&
97 OIDNDenoiserGPU::is_device_supported(denoiser_device->info));
98#else
99 (void)denoiser_device;
100 (void)params;
101 return false;
102#endif
103}
104
106 Device *cpu_fallback_device,
107 const DenoiseParams &params,
108 const GraphicsInteropDevice &interop_device,
109 Device *&single_denoiser_device)
110{
111 DCHECK(params.use);
112
113 DenoiseParams effective_denoise_params = params;
114
115 single_denoiser_device = nullptr;
116 if (is_single_device(denoiser_device)) {
117 /* Simple case: denoising happens on a single device. */
118 single_denoiser_device = denoiser_device;
119 }
120 else {
121 /* Find best device from the ones which are proposed for denoising.
122 * The choice is expected to be between a few GPUs, or between a GPU and a CPU
123 * or between a few GPUs and a CPU. */
124 single_denoiser_device = find_best_device(denoiser_device, params.type, interop_device);
125 }
126 /* Ensure that we have a device to be used later in the code below. */
127 if (single_denoiser_device == nullptr) {
128 single_denoiser_device = cpu_fallback_device;
129 }
130
131 const bool is_cpu_denoiser_device = single_denoiser_device->info.type == DEVICE_CPU;
132 if (is_cpu_denoiser_device == false) {
133 if (use_optix_denoiser(single_denoiser_device, effective_denoise_params) ||
134 use_gpu_oidn_denoiser(single_denoiser_device, effective_denoise_params))
135 {
136 /* Denoising parameters are correct and there is no need to fall back to CPU OIDN. */
137 return effective_denoise_params;
138 }
139 }
140
141 /* Always fallback to OIDN on CPU. */
142 effective_denoise_params.type = DENOISER_OPENIMAGEDENOISE;
143 effective_denoise_params.use_gpu = false;
144
145 return effective_denoise_params;
146}
147
149 Device *cpu_fallback_device,
150 const DenoiseParams &params,
151 const GraphicsInteropDevice &interop_device)
152{
153
154 Device *single_denoiser_device;
155 const DenoiseParams effective_denoiser_params = get_effective_denoise_params(
156 denoiser_device, cpu_fallback_device, params, interop_device, single_denoiser_device);
157
158 const bool is_cpu_denoiser_device = single_denoiser_device->info.type == DEVICE_CPU;
159 if (is_cpu_denoiser_device == false) {
160#ifdef WITH_OPTIX
161 if (use_optix_denoiser(single_denoiser_device, effective_denoiser_params)) {
162 return make_unique<OptiXDenoiser>(single_denoiser_device, effective_denoiser_params);
163 }
164#endif
165
166#ifdef WITH_OPENIMAGEDENOISE
167 /* If available and allowed, then we will use OpenImageDenoise on GPU, otherwise on CPU. */
168 if (use_gpu_oidn_denoiser(single_denoiser_device, effective_denoiser_params)) {
169 return make_unique<OIDNDenoiserGPU>(single_denoiser_device, effective_denoiser_params);
170 }
171#endif
172 }
173
175 return nullptr;
176 }
177
178 /* Used preference CPU when possible, and fallback on CPU fallback device otherwise. */
179 return make_unique<OIDNDenoiser>(is_cpu_denoiser_device ? single_denoiser_device :
180 cpu_fallback_device,
181 effective_denoiser_params);
182}
183
185{
186#ifdef WITH_OPENIMAGEDENOISE
187 if (denoise_device_info.type != DEVICE_CPU &&
188 OIDNDenoiserGPU::is_device_supported(denoise_device_info))
189 {
191 }
192#else
193 (void)denoise_device_info;
194#endif
195
196#ifdef WITH_OPTIX
197 if (OptiXDenoiser::is_device_supported(denoise_device_info)) {
198 return DENOISER_OPTIX;
199 }
200#endif
201
202#ifdef WITH_OPENIMAGEDENOISE
205 }
206#endif
207
208 return DENOISER_NONE;
209}
210
217
219{
220 DCHECK_EQ(params.type, params_.type);
221
222 if (params.type == params_.type) {
223 params_ = params;
224 }
225 else {
226 LOG_ERROR << "Attempt to change denoiser type.";
227 }
228}
229
231{
232 return params_;
233}
234
236{
237 /* If we have successfully loaded kernels once, then there is no need to repeat this again. */
240 }
241
242 if (progress) {
243 progress->set_status("Loading denoising kernels (may take a few minutes the first time)");
244 }
245
246 if (!denoiser_device_) {
247 set_error("No device available to denoise on");
248 return false;
249 }
250
251 /* Only need denoising feature, everything else is unused. */
252 if (!denoiser_device_->load_kernels(KERNEL_FEATURE_DENOISING)) {
253 string message = denoiser_device_->error_message();
254 if (message.empty()) {
255 message = "Failed loading denoising kernel, see console for errors";
256 }
257 set_error(message);
258 return false;
259 }
260
261 LOG_DEBUG << "Will denoise on " << denoiser_device_->info.description << " ("
262 << denoiser_device_->info.id << ")";
263
265 return true;
266}
267
272
DenoiserType type
Definition denoise.h:59
bool use_gpu
Definition denoise.h:73
static unique_ptr< Denoiser > create(Device *denoiser_device, Device *cpu_fallback_device, const DenoiseParams &params, const GraphicsInteropDevice &interop_device)
Definition denoiser.cpp:148
void set_error(const string &error)
Definition denoiser.h:119
void set_params(const DenoiseParams &params)
Definition denoiser.cpp:218
static DenoiserType automatic_viewport_denoiser_type(const DeviceInfo &denoise_device_info)
Definition denoiser.cpp:184
DenoiseParams params_
Definition denoiser.h:133
Denoiser(Device *denoiser_device, const DenoiseParams &params)
Definition denoiser.cpp:211
virtual bool load_kernels(Progress *progress)
Definition denoiser.cpp:235
bool denoise_kernels_are_loaded_
Definition denoiser.h:132
Device * get_denoiser_device() const
Definition denoiser.cpp:268
Device * denoiser_device_
Definition denoiser.h:131
const DenoiseParams & get_params() const
Definition denoiser.cpp:230
vector< DeviceInfo > multi_devices
DenoiserTypeMask denoisers
DeviceType type
virtual bool should_use_graphics_interop(const GraphicsInteropDevice &, const bool=false)
DeviceInfo info
virtual void foreach_device(const std::function< void(Device *)> &callback)
void set_status(const string &status_, const string &substatus_="")
Definition progress.h:248
DenoiserType
Definition denoise.h:11
@ DENOISER_NONE
Definition denoise.h:16
@ DENOISER_OPTIX
Definition denoise.h:12
@ DENOISER_OPENIMAGEDENOISE
Definition denoise.h:13
bool use_gpu_oidn_denoiser(Device *denoiser_device, const DenoiseParams &params)
Definition denoiser.cpp:93
static Device * find_best_device(Device *device, const DenoiserType type, const GraphicsInteropDevice &interop_device)
Definition denoiser.cpp:46
bool use_optix_denoiser(Device *denoiser_device, const DenoiseParams &params)
Definition denoiser.cpp:81
static CCL_NAMESPACE_BEGIN bool is_single_device(const Device *device)
Definition denoiser.cpp:26
DenoiseParams get_effective_denoise_params(Device *denoiser_device, Device *cpu_fallback_device, const DenoiseParams &params, const GraphicsInteropDevice &interop_device, Device *&single_denoiser_device)
Definition denoiser.cpp:105
bool use_gpu_oidn_denoiser(Device *denoiser_device, const DenoiseParams &params)
Definition denoiser.cpp:93
bool use_optix_denoiser(Device *denoiser_device, const DenoiseParams &params)
Definition denoiser.cpp:81
DenoiseParams get_effective_denoise_params(Device *denoiser_device, Device *cpu_fallback_device, const DenoiseParams &params, const GraphicsInteropDevice &interop_device, Device *&single_denoiser_device)
Definition denoiser.cpp:105
#define KERNEL_FEATURE_DENOISING
#define CCL_NAMESPACE_END
@ DEVICE_MULTI
@ DEVICE_CPU
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
#define DCHECK(expression)
Definition log.h:135
#define DCHECK_EQ(a, b)
Definition log.h:144
#define LOG_DEBUG
Definition log.h:107
#define LOG_ERROR
Definition log.h:101
static CCL_NAMESPACE_BEGIN bool openimagedenoise_supported()