Blender V4.3
COM_DenoiseOperation.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2019 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
6#include "BLI_system.h"
7#ifdef WITH_OPENIMAGEDENOISE
8# include "BLI_threads.h"
9# include <OpenImageDenoise/oidn.hpp>
10static pthread_mutex_t oidn_lock = BLI_MUTEX_INITIALIZER;
11#endif
12
13namespace blender::compositor {
14
16{
17#ifdef WITH_OPENIMAGEDENOISE
18# ifdef __APPLE__
19 /* Always supported through Accelerate framework BNNS on macOS. */
20 return true;
21# elif defined(__aarch64__) || defined(_M_ARM64)
22 /* OIDN 2.2 and up supports ARM64 on Windows and Linux. */
23 return true;
24# else
25 return BLI_cpu_support_sse42();
26# endif
27
28#else
29 return false;
30#endif
31}
32
33#ifdef WITH_OPENIMAGEDENOISE
34static bool oidn_progress_monitor_function(void *user_ptr, double /*n*/)
35{
36 const NodeOperation *operation = static_cast<const NodeOperation *>(user_ptr);
37 return !operation->is_braked();
38}
39#endif
40
42 private:
43#ifdef WITH_OPENIMAGEDENOISE
44 oidn::DeviceRef device_;
45 oidn::FilterRef filter_;
46 bool initialized_ = false;
47#endif
48
49 public:
50#ifdef WITH_OPENIMAGEDENOISE
52 {
53 BLI_assert(!initialized_);
54 }
55
56 void init_and_lock_denoiser(NodeOperation *operation, MemoryBuffer *output)
57 {
58 /* Since it's memory intensive, it's better to run only one instance of OIDN at a time.
59 * OpenImageDenoise is multithreaded internally and should use all available cores
60 * nonetheless. */
61 BLI_mutex_lock(&oidn_lock);
62
63 device_ = oidn::newDevice(oidn::DeviceType::CPU);
64 device_.set("setAffinity", false);
65 device_.commit();
66 filter_ = device_.newFilter("RT");
67 filter_.setProgressMonitorFunction(oidn_progress_monitor_function, operation);
68 initialized_ = true;
69 set_image("output", output);
70 }
71
73 {
74 BLI_mutex_unlock(&oidn_lock);
75 initialized_ = false;
76 }
77
78 void set_image(const StringRef name, MemoryBuffer *buffer)
79 {
80 BLI_assert(initialized_);
81 BLI_assert(!buffer->is_a_single_elem());
82 filter_.setImage(name.data(),
83 buffer->get_buffer(),
84 oidn::Format::Float3,
85 buffer->get_width(),
86 buffer->get_height(),
87 0,
88 buffer->get_elem_bytes_len());
89 }
90
91 template<typename T> void set(const StringRef option_name, T value)
92 {
93 BLI_assert(initialized_);
94 filter_.set(option_name.data(), value);
95 }
96
97 void execute()
98 {
99 BLI_assert(initialized_);
100 filter_.commit();
101 filter_.execute();
102 }
103
104#else
105 void init_and_lock_denoiser(NodeOperation * /*operation*/, MemoryBuffer * /*output*/) {}
106
108
109 void set_image(const StringRef /*name*/, MemoryBuffer * /*buffer*/) {}
110
111 template<typename T> void set(const StringRef /*option_name*/, T /*value*/) {}
112
113 void execute() {}
114#endif
115};
116
122
124 const rcti & /*output_area*/,
125 rcti &r_input_area)
126{
127 r_input_area = this->get_canvas();
128}
129
138
139static bool are_guiding_passes_noise_free(const NodeDenoise *settings)
140{
141 switch (settings->prefilter) {
143 case CMP_NODE_DENOISE_PREFILTER_ACCURATE: /* Prefiltered with #DenoisePrefilterOperation. */
144 return true;
146 default:
147 return false;
148 }
149}
150
152{
153 if (settings_) {
154 hash_params(int(settings_->hdr), are_guiding_passes_noise_free(settings_));
155 }
156}
157
159 MemoryBuffer *input_color,
160 MemoryBuffer *input_normal,
161 MemoryBuffer *input_albedo,
162 const NodeDenoise *settings)
163{
164 if (input_color->is_a_single_elem()) {
165 output->fill(output->get_rect(), input_color->get_elem(0, 0));
166 return;
167 }
168
170
172 filter.init_and_lock_denoiser(this, output);
173
174 filter.set_image("color", input_color);
175 if (!input_albedo->is_a_single_elem()) {
176 filter.set_image("albedo", input_albedo);
177 if (!input_normal->is_a_single_elem()) {
178 filter.set_image("normal", input_normal);
179 }
180 }
181
182 BLI_assert(settings);
183 if (settings) {
184 filter.set("hdr", settings->hdr);
185 filter.set("srgb", false);
186 filter.set("cleanAux", are_guiding_passes_noise_free(settings));
187 }
188
189 filter.execute();
190 filter.deinit_and_unlock_denoiser();
191
192 /* Copy the alpha channel, OpenImageDenoise currently only supports RGB. */
193 output->copy_from(input_color, input_color->get_rect(), 3, COM_DATA_TYPE_VALUE_CHANNELS, 3);
194}
195
197 const rcti & /*area*/,
199{
200 if (!output_rendered_) {
201 this->generate_denoise(output, inputs[0], inputs[1], inputs[2], settings_);
202 output_rendered_ = true;
203 }
204}
205
207{
208 this->add_input_socket(data_type);
209 this->add_output_socket(data_type);
210 image_name_ = "";
211}
212
217
218void DenoisePrefilterOperation::generate_denoise(MemoryBuffer *output, MemoryBuffer *input)
219{
220 if (input->is_a_single_elem()) {
221 copy_v4_v4(output->get_elem(0, 0), input->get_elem(0, 0));
222 return;
223 }
224
226
227 DenoiseFilter filter;
228 filter.init_and_lock_denoiser(this, output);
229 filter.set_image(image_name_, input);
230 filter.execute();
231 filter.deinit_and_unlock_denoiser();
232}
233
235 const rcti & /*area*/,
237{
238 if (!output_rendered_) {
239 this->generate_denoise(output, inputs[0]);
240 output_rendered_ = true;
241 }
242}
243
244} // namespace blender::compositor
#define BLI_assert(a)
Definition BLI_assert.h:50
MINLINE void copy_v4_v4(float r[4], const float a[4])
int BLI_cpu_support_sse42(void)
Definition system.c:160
#define BLI_MUTEX_INITIALIZER
Definition BLI_threads.h:84
void BLI_mutex_lock(ThreadMutex *mutex)
Definition threads.cc:345
void BLI_mutex_unlock(ThreadMutex *mutex)
Definition threads.cc:350
@ CMP_NODE_DENOISE_PREFILTER_FAST
@ CMP_NODE_DENOISE_PREFILTER_NONE
@ CMP_NODE_DENOISE_PREFILTER_ACCURATE
constexpr const char * data() const
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override
Get input operation area being read by this operation on rendering given output area.
void init_and_lock_denoiser(NodeOperation *, MemoryBuffer *)
void set_image(const StringRef, MemoryBuffer *)
void update_memory_buffer(MemoryBuffer *output, const rcti &area, Span< MemoryBuffer * > inputs) override
void generate_denoise(MemoryBuffer *output, MemoryBuffer *input_color, MemoryBuffer *input_normal, MemoryBuffer *input_albedo, const NodeDenoise *settings)
void update_memory_buffer(MemoryBuffer *output, const rcti &area, Span< MemoryBuffer * > inputs) override
a MemoryBuffer contains access to the data
const rcti & get_rect() const
get the rect of this MemoryBuffer
const int get_width() const
get the width of this MemoryBuffer
const int get_height() const
get the height of this MemoryBuffer
float * get_buffer()
get the data of this MemoryBuffer
NodeOperation contains calculation logic.
void add_output_socket(DataType datatype)
void add_input_socket(DataType datatype, ResizeMode resize_mode=ResizeMode::Center)
void hash_params(T1 param1, T2 param2)
DataType
possible data types for sockets
Definition COM_defines.h:21
@ Vector
Vector data type.
DO_INLINE void filter(lfVector *V, fmatrix3x3 *S)
constexpr int COM_DATA_TYPE_VALUE_CHANNELS
Definition COM_defines.h:55
static bool are_guiding_passes_noise_free(const NodeDenoise *settings)
DepsgraphFromCollectionIDsFilter filter_