30#ifndef WITH_OPENIMAGEDENOISE
31 set_error(
"Failed to denoise, build has no OpenImageDenoise support");
34 set_error(
"OpenImageDenoiser is not supported on this CPU: missing SSE 4.1 support");
39#ifdef WITH_OPENIMAGEDENOISE
40static bool oidn_progress_monitor_function(
void *user_ptr,
double )
54 : name(name), type(type), mode(mode)
65 inline operator bool()
const
67 return name[0] !=
'\0';
73 const char *name =
"";
77 int num_components = -1;
78 bool use_compositing =
false;
79 bool use_denoising_albedo =
true;
91 bool need_scale =
false;
94 bool is_filtered =
false;
100class OIDNDenoiseContext {
106 const int num_samples,
107 const bool allow_inplace_modification)
108 : denoiser_(denoiser),
109 denoise_params_(denoise_params),
110 buffer_params_(buffer_params),
111 render_buffers_(render_buffers),
112 num_samples_(num_samples),
113 allow_inplace_modification_(allow_inplace_modification),
116 if (denoise_params_.use_pass_albedo) {
120 if (denoise_params_.use_pass_normal) {
124 const char *custom_weight_path = getenv(
"CYCLES_OIDN_CUSTOM_WEIGHTS");
125 if (custom_weight_path) {
127 fprintf(stderr,
"Cycles: Failed to load custom OIDN weights!");
132 bool need_denoising()
const
134 if (buffer_params_.width == 0 && buffer_params_.height == 0) {
142 void read_guiding_passes()
144 read_guiding_pass(oidn_albedo_pass_);
145 read_guiding_pass(oidn_normal_pass_);
148 void denoise_pass(
const PassType pass_type)
150 OIDNPass oidn_color_pass(buffer_params_,
"color", pass_type);
155 if (oidn_color_pass.use_denoising_albedo) {
156 if (albedo_replaced_with_fake_) {
157 LOG(ERROR) <<
"Pass which requires albedo is denoised after fake albedo has been set.";
168 OIDNPass oidn_color_access_pass = read_input_pass(oidn_color_pass, oidn_output_pass);
170 oidn::DeviceRef oidn_device = oidn::newDevice(oidn::DeviceType::CPU);
171 oidn_device.set(
"setAffinity",
false);
172 oidn_device.commit();
176 oidn::FilterRef oidn_filter = oidn_device.newFilter(
"RT");
177 set_input_pass(oidn_filter, oidn_color_access_pass);
178 set_guiding_passes(oidn_filter, oidn_color_pass);
179 set_output_pass(oidn_filter, oidn_output_pass);
180 oidn_filter.setProgressMonitorFunction(oidn_progress_monitor_function, denoiser_);
181 oidn_filter.set(
"hdr",
true);
182 oidn_filter.set(
"srgb",
false);
183 if (custom_weights.size()) {
184 oidn_filter.setData(
"weights", custom_weights.data(), custom_weights.size());
186 set_quality(oidn_filter);
191 oidn_filter.set(
"cleanAux",
true);
193 oidn_filter.commit();
195 filter_guiding_pass_if_needed(oidn_device, oidn_albedo_pass_);
196 filter_guiding_pass_if_needed(oidn_device, oidn_normal_pass_);
199 oidn_filter.execute();
202 const char *error_message;
203 const oidn::Error
error = oidn_device.getError(error_message);
204 if (
error != oidn::Error::None &&
error != oidn::Error::Cancelled) {
205 denoiser_->set_error(
"OpenImageDenoise error: " +
string(error_message));
208 postprocess_output(oidn_color_pass, oidn_output_pass);
212 void filter_guiding_pass_if_needed(oidn::DeviceRef &oidn_device, OIDNPass &oidn_pass)
215 oidn_pass.is_filtered)
220 oidn::FilterRef oidn_filter = oidn_device.newFilter(
"RT");
221 set_pass(oidn_filter, oidn_pass);
222 set_output_pass(oidn_filter, oidn_pass);
223 set_quality(oidn_filter);
224 oidn_filter.commit();
225 oidn_filter.execute();
227 oidn_pass.is_filtered =
true;
231 void read_guiding_pass(OIDNPass &oidn_pass)
237 DCHECK(!oidn_pass.use_compositing);
240 !is_pass_scale_needed(oidn_pass))
246 if (allow_inplace_modification_) {
247 scale_pass_in_render_buffers(oidn_pass);
251 read_pass_pixels_into_buffer(oidn_pass);
257 OIDNPass read_input_pass(OIDNPass &oidn_input_pass,
const OIDNPass &oidn_output_pass)
259 const bool use_compositing = oidn_input_pass.use_compositing;
263 if (!use_compositing && !is_pass_scale_needed(oidn_input_pass)) {
264 return oidn_input_pass;
267 float *buffer_data = render_buffers_->buffer.data();
268 float *pass_data = buffer_data + oidn_output_pass.offset;
271 destination.pixel_stride = buffer_params_.pass_stride;
273 read_pass_pixels(oidn_input_pass, destination);
275 OIDNPass oidn_input_pass_at_output = oidn_input_pass;
276 oidn_input_pass_at_output.offset = oidn_output_pass.offset;
278 return oidn_input_pass_at_output;
285 pass_access_info.
type = oidn_pass.type;
286 pass_access_info.
mode = oidn_pass.mode;
287 pass_access_info.
offset = oidn_pass.offset;
299 const PassAccessorCPU pass_accessor(pass_access_info, 1.0f, num_samples_);
307 pass_accessor.get_render_tile_pixels(render_buffers_, buffer_params, destination);
311 void read_pass_pixels_into_buffer(OIDNPass &oidn_pass)
313 VLOG_WORK <<
"Allocating temporary buffer for pass " << oidn_pass.name <<
" ("
316 const int64_t width = buffer_params_.width;
317 const int64_t height = buffer_params_.height;
320 scaled_buffer.
resize(width * height * 3);
324 read_pass_pixels(oidn_pass, destination);
329 void set_pass_referenced(oidn::FilterRef &oidn_filter,
331 const OIDNPass &oidn_pass)
333 const int64_t x = buffer_params_.full_x;
334 const int64_t y = buffer_params_.full_y;
335 const int64_t width = buffer_params_.width;
336 const int64_t height = buffer_params_.height;
337 const int64_t offset = buffer_params_.offset;
338 const int64_t stride = buffer_params_.stride;
339 const int64_t pass_stride = buffer_params_.pass_stride;
341 const int64_t pixel_index = offset + x + y * stride;
342 const int64_t buffer_offset = pixel_index * pass_stride;
344 float *buffer_data = render_buffers_->buffer.data();
346 oidn_filter.setImage(name,
347 buffer_data + buffer_offset + oidn_pass.offset,
348 oidn::Format::Float3,
352 pass_stride *
sizeof(
float),
353 stride * pass_stride *
sizeof(
float));
356 void set_pass_from_buffer(oidn::FilterRef &oidn_filter,
const char *name, OIDNPass &oidn_pass)
358 const int64_t width = buffer_params_.width;
359 const int64_t height = buffer_params_.height;
361 oidn_filter.setImage(
362 name, oidn_pass.scaled_buffer.data(), oidn::Format::Float3, width, height, 0, 0, 0);
365 void set_pass(oidn::FilterRef &oidn_filter, OIDNPass &oidn_pass)
367 set_pass(oidn_filter, oidn_pass.name, oidn_pass);
369 void set_pass(oidn::FilterRef &oidn_filter,
const char *name, OIDNPass &oidn_pass)
371 if (oidn_pass.scaled_buffer.empty()) {
372 set_pass_referenced(oidn_filter, name, oidn_pass);
375 set_pass_from_buffer(oidn_filter, name, oidn_pass);
379 void set_input_pass(oidn::FilterRef &oidn_filter, OIDNPass &oidn_pass)
381 set_pass_referenced(oidn_filter, oidn_pass.name, oidn_pass);
384 void set_guiding_passes(oidn::FilterRef &oidn_filter, OIDNPass &oidn_pass)
386 if (oidn_albedo_pass_) {
387 if (oidn_pass.use_denoising_albedo) {
388 set_pass(oidn_filter, oidn_albedo_pass_);
393 set_fake_albedo_pass(oidn_filter);
397 if (oidn_normal_pass_) {
398 set_pass(oidn_filter, oidn_normal_pass_);
402 void set_fake_albedo_pass(oidn::FilterRef &oidn_filter)
404 const int64_t width = buffer_params_.width;
405 const int64_t height = buffer_params_.height;
407 if (!albedo_replaced_with_fake_) {
408 const int64_t num_pixel_components = width * height * 3;
409 oidn_albedo_pass_.scaled_buffer.resize(num_pixel_components);
411 for (
int i = 0; i < num_pixel_components; ++i) {
412 oidn_albedo_pass_.scaled_buffer[i] = 0.5f;
415 albedo_replaced_with_fake_ =
true;
418 set_pass(oidn_filter, oidn_albedo_pass_);
421 void set_output_pass(oidn::FilterRef &oidn_filter, OIDNPass &oidn_pass)
423 set_pass(oidn_filter,
"output", oidn_pass);
426 void set_quality(oidn::FilterRef &oidn_filter)
428# if OIDN_VERSION_MAJOR >= 2
429 switch (denoise_params_.quality) {
431# if OIDN_VERSION >= 20300
432 oidn_filter.set(
"quality", OIDN_QUALITY_FAST);
436 oidn_filter.set(
"quality", OIDN_QUALITY_BALANCED);
440 oidn_filter.set(
"quality", OIDN_QUALITY_HIGH);
447 void postprocess_output(
const OIDNPass &oidn_input_pass,
const OIDNPass &oidn_output_pass)
449 kernel_assert(oidn_input_pass.num_components == oidn_output_pass.num_components);
451 const int64_t x = buffer_params_.full_x;
452 const int64_t y = buffer_params_.full_y;
453 const int64_t width = buffer_params_.width;
454 const int64_t height = buffer_params_.height;
455 const int64_t offset = buffer_params_.offset;
456 const int64_t stride = buffer_params_.stride;
457 const int64_t pass_stride = buffer_params_.pass_stride;
458 const int64_t row_stride = stride * pass_stride;
460 const int64_t pixel_offset = offset + x + y * stride;
461 const int64_t buffer_offset = (pixel_offset * pass_stride);
463 float *buffer_data = render_buffers_->buffer.data();
465 const bool has_pass_sample_count = (pass_sample_count_ !=
PASS_UNUSED);
466 const bool need_scale = has_pass_sample_count || oidn_input_pass.use_compositing;
468 for (
int y = 0; y < height; ++
y) {
469 float *buffer_row = buffer_data + buffer_offset + y * row_stride;
470 for (
int x = 0; x < width; ++
x) {
471 float *buffer_pixel = buffer_row + x * pass_stride;
472 float *denoised_pixel = buffer_pixel + oidn_output_pass.offset;
475 const float pixel_scale = has_pass_sample_count ?
479 denoised_pixel[0] = denoised_pixel[0] * pixel_scale;
480 denoised_pixel[1] = denoised_pixel[1] * pixel_scale;
481 denoised_pixel[2] = denoised_pixel[2] * pixel_scale;
484 if (oidn_output_pass.num_components == 3) {
487 else if (!oidn_input_pass.use_compositing) {
491 const float *noisy_pixel = buffer_pixel + oidn_input_pass.offset;
492 denoised_pixel[3] = noisy_pixel[3];
497 denoised_pixel[3] = 0;
503 bool is_pass_scale_needed(OIDNPass &oidn_pass)
const
511 if (!oidn_pass.need_scale) {
515 if (num_samples_ == 1) {
524 void scale_pass_in_render_buffers(OIDNPass &oidn_pass)
526 const int64_t x = buffer_params_.full_x;
527 const int64_t y = buffer_params_.full_y;
528 const int64_t width = buffer_params_.width;
529 const int64_t height = buffer_params_.height;
530 const int64_t offset = buffer_params_.offset;
531 const int64_t stride = buffer_params_.stride;
532 const int64_t pass_stride = buffer_params_.pass_stride;
533 const int64_t row_stride = stride * pass_stride;
535 const int64_t pixel_offset = offset + x + y * stride;
536 const int64_t buffer_offset = (pixel_offset * pass_stride);
538 float *buffer_data = render_buffers_->buffer.data();
540 const bool has_pass_sample_count = (pass_sample_count_ !=
PASS_UNUSED);
542 for (
int y = 0; y < height; ++
y) {
543 float *buffer_row = buffer_data + buffer_offset + y * row_stride;
544 for (
int x = 0; x < width; ++
x) {
545 float *buffer_pixel = buffer_row + x * pass_stride;
546 float *pass_pixel = buffer_pixel + oidn_pass.offset;
548 const float pixel_scale = 1.0f / (has_pass_sample_count ?
552 pass_pixel[0] = pass_pixel[0] * pixel_scale;
553 pass_pixel[1] = pass_pixel[1] * pixel_scale;
554 pass_pixel[2] = pass_pixel[2] * pixel_scale;
564 int num_samples_ = 0;
565 bool allow_inplace_modification_ =
false;
571 OIDNPass oidn_albedo_pass_;
572 OIDNPass oidn_normal_pass_;
577 bool albedo_replaced_with_fake_ =
false;
580static unique_ptr<DeviceQueue> create_device_queue(
const RenderBuffers *render_buffers)
589static void copy_render_buffers_from_device(unique_ptr<DeviceQueue> &queue,
593 queue->copy_from_device(render_buffers->
buffer);
594 queue->synchronize();
601static void copy_render_buffers_to_device(unique_ptr<DeviceQueue> &queue,
605 queue->copy_to_device(render_buffers->
buffer);
606 queue->synchronize();
617 const int num_samples,
618 bool allow_inplace_modification)
621 <<
"OpenImageDenoise is not supported on this platform or build.";
623#ifdef WITH_OPENIMAGEDENOISE
627 unique_ptr<DeviceQueue> queue = create_device_queue(render_buffers);
628 copy_render_buffers_from_device(queue, render_buffers);
630 OIDNDenoiseContext context(
631 this,
params_, buffer_params, render_buffers, num_samples, allow_inplace_modification);
633 if (context.need_denoising()) {
634 context.read_guiding_passes();
636 const std::array<PassType, 3> passes = {
645 for (
const PassType pass_type : passes) {
646 context.denoise_pass(pass_type);
654 copy_render_buffers_to_device(queue, render_buffers);
658 (void)render_buffers;
660 (void)allow_inplace_modification;
int get_pass_offset(PassType type, PassMode mode=PassMode::NOISY) const
void set_error(const string &error)
bool is_cancelled() const
virtual unique_ptr< DeviceQueue > gpu_queue_create()
virtual bool denoise_buffer(const BufferParams &buffer_params, RenderBuffers *render_buffers, const int num_samples, bool allow_inplace_modification) override
OIDNDenoiser(Device *denoiser_device, const DenoiseParams ¶ms)
virtual uint get_device_type_mask() const override
static thread_mutex mutex_
bool use_approximate_shadow_catcher
bool use_approximate_shadow_catcher_background
PassInfo get_info() const
device_vector< float > buffer
T * resize(size_t newsize)
@ DENOISER_QUALITY_BALANCED
@ DENOISER_PREFILTER_NONE
@ DENOISER_PREFILTER_ACCURATE
@ DENOISER_OPENIMAGEDENOISE
#define kernel_assert(cond)
#define CCL_NAMESPACE_END
@ PASS_SHADOW_CATCHER_MATTE
#define DCHECK(expression)
static void error(const char *str)
static CCL_NAMESPACE_BEGIN bool openimagedenoise_supported()
CCL_NAMESPACE_BEGIN const char * pass_type_as_string(const PassType type)
bool path_read_binary(const string &path, vector< uint8_t > &binary)
bool use_denoising_albedo
std::unique_lock< std::mutex > thread_scoped_lock
CCL_NAMESPACE_BEGIN typedef std::mutex thread_mutex