27#ifndef WITH_OPENIMAGEDENOISE
28 set_error(
"Failed to denoise, build has no OpenImageDenoise support");
31 set_error(
"OpenImageDenoiser is not supported on this CPU: missing SSE 4.1 support");
36#ifdef WITH_OPENIMAGEDENOISE
37static bool oidn_progress_monitor_function(
void *user_ptr,
double )
47 OIDNPass(
const BufferParams &buffer_params,
64 return name[0] !=
'\0';
70 const char *
name =
"";
74 int num_components = -1;
75 bool use_compositing =
false;
76 bool use_denoising_albedo =
true;
88 bool need_scale =
false;
91 bool is_filtered =
false;
94 array<float> scaled_buffer;
97class OIDNDenoiseContext {
99 OIDNDenoiseContext(OIDNDenoiser *denoiser,
100 const DenoiseParams &denoise_params,
101 const BufferParams &buffer_params,
102 RenderBuffers *render_buffers,
103 const int num_samples,
104 const bool allow_inplace_modification)
105 : denoiser_(denoiser),
106 denoise_params_(denoise_params),
107 buffer_params_(buffer_params),
108 render_buffers_(render_buffers),
109 num_samples_(num_samples),
110 allow_inplace_modification_(allow_inplace_modification),
113 if (denoise_params_.use_pass_albedo) {
114 oidn_albedo_pass_ = OIDNPass(buffer_params_,
"albedo", PASS_DENOISING_ALBEDO);
117 if (denoise_params_.use_pass_normal) {
118 oidn_normal_pass_ = OIDNPass(buffer_params_,
"normal", PASS_DENOISING_NORMAL);
121 const char *custom_weight_path = getenv(
"CYCLES_OIDN_CUSTOM_WEIGHTS");
122 if (custom_weight_path) {
124 LOG_ERROR <<
"Failed to load custom OpenImageDenoise weights";
129 bool need_denoising()
const
131 if (buffer_params_.width == 0 && buffer_params_.height == 0) {
139 void read_guiding_passes()
141 read_guiding_pass(oidn_albedo_pass_);
142 read_guiding_pass(oidn_normal_pass_);
145 void denoise_pass(
const PassType pass_type)
147 OIDNPass oidn_color_pass(buffer_params_,
"color", pass_type);
152 if (oidn_color_pass.use_denoising_albedo) {
153 if (albedo_replaced_with_fake_) {
154 LOG_ERROR <<
"Pass which requires albedo is denoised after fake albedo has been set.";
165 OIDNPass oidn_color_access_pass = read_input_pass(oidn_color_pass, oidn_output_pass);
167 oidn::DeviceRef oidn_device = oidn::newDevice(oidn::DeviceType::CPU);
168 oidn_device.set(
"setAffinity",
false);
169 oidn_device.commit();
173 oidn::FilterRef oidn_filter = oidn_device.newFilter(
"RT");
174 set_input_pass(oidn_filter, oidn_color_access_pass);
175 set_guiding_passes(oidn_filter, oidn_color_pass);
176 set_output_pass(oidn_filter, oidn_output_pass);
177 oidn_filter.setProgressMonitorFunction(oidn_progress_monitor_function, denoiser_);
178 oidn_filter.set(
"hdr",
true);
179 oidn_filter.set(
"srgb",
false);
180 if (!custom_weights.empty()) {
181 oidn_filter.setData(
"weights", custom_weights.data(), custom_weights.size());
183 set_quality(oidn_filter);
188 oidn_filter.set(
"cleanAux",
true);
190 oidn_filter.commit();
192 filter_guiding_pass_if_needed(oidn_device, oidn_albedo_pass_);
193 filter_guiding_pass_if_needed(oidn_device, oidn_normal_pass_);
196 oidn_filter.execute();
199 const char *error_message;
200 const oidn::Error
error = oidn_device.getError(error_message);
201 if (
error != oidn::Error::None &&
error != oidn::Error::Cancelled) {
202 denoiser_->set_error(
"OpenImageDenoise error: " +
string(error_message));
205 postprocess_output(oidn_color_pass, oidn_output_pass);
209 void filter_guiding_pass_if_needed(oidn::DeviceRef &oidn_device, OIDNPass &oidn_pass)
212 oidn_pass.is_filtered)
217 oidn::FilterRef oidn_filter = oidn_device.newFilter(
"RT");
218 set_pass(oidn_filter, oidn_pass);
219 set_output_pass(oidn_filter, oidn_pass);
220 set_quality(oidn_filter);
221 oidn_filter.commit();
222 oidn_filter.execute();
224 oidn_pass.is_filtered =
true;
228 void read_guiding_pass(OIDNPass &oidn_pass)
234 DCHECK(!oidn_pass.use_compositing);
237 !is_pass_scale_needed(oidn_pass))
243 if (allow_inplace_modification_) {
244 scale_pass_in_render_buffers(oidn_pass);
248 read_pass_pixels_into_buffer(oidn_pass);
254 OIDNPass read_input_pass(OIDNPass &oidn_input_pass,
const OIDNPass &oidn_output_pass)
256 const bool use_compositing = oidn_input_pass.use_compositing;
260 if (!use_compositing && !is_pass_scale_needed(oidn_input_pass)) {
261 return oidn_input_pass;
264 float *buffer_data = render_buffers_->buffer.data();
265 float *pass_data = buffer_data + oidn_output_pass.offset;
267 PassAccessor::Destination destination(pass_data, 3);
268 destination.pixel_stride = buffer_params_.pass_stride;
270 read_pass_pixels(oidn_input_pass, destination);
272 OIDNPass oidn_input_pass_at_output = oidn_input_pass;
273 oidn_input_pass_at_output.offset = oidn_output_pass.offset;
275 return oidn_input_pass_at_output;
279 void read_pass_pixels(
const OIDNPass &oidn_pass,
const PassAccessor::Destination &destination)
281 PassAccessor::PassAccessInfo pass_access_info;
282 pass_access_info.
type = oidn_pass.type;
283 pass_access_info.
mode = oidn_pass.mode;
284 pass_access_info.
offset = oidn_pass.offset;
296 const PassAccessorCPU pass_accessor(pass_access_info, 1.0f, num_samples_);
298 BufferParams buffer_params = buffer_params_;
304 pass_accessor.get_render_tile_pixels(render_buffers_, buffer_params, destination);
308 void read_pass_pixels_into_buffer(OIDNPass &oidn_pass)
310 LOG_DEBUG <<
"Allocating temporary buffer for pass " << oidn_pass.name <<
" ("
313 const int64_t width = buffer_params_.width;
314 const int64_t height = buffer_params_.height;
316 array<float> &scaled_buffer = oidn_pass.scaled_buffer;
317 scaled_buffer.
resize(width * height * 3);
319 const PassAccessor::Destination destination(scaled_buffer.
data(), 3);
321 read_pass_pixels(oidn_pass, destination);
326 void set_pass_referenced(oidn::FilterRef &oidn_filter,
328 const OIDNPass &oidn_pass)
330 const int64_t x = buffer_params_.full_x;
331 const int64_t y = buffer_params_.full_y;
332 const int64_t width = buffer_params_.width;
333 const int64_t height = buffer_params_.height;
334 const int64_t offset = buffer_params_.offset;
335 const int64_t stride = buffer_params_.stride;
336 const int64_t pass_stride = buffer_params_.pass_stride;
338 const int64_t pixel_index = offset +
x +
y * stride;
339 const int64_t buffer_offset = pixel_index * pass_stride;
341 float *buffer_data = render_buffers_->buffer.data();
343 oidn_filter.setImage(
name,
344 buffer_data + buffer_offset + oidn_pass.offset,
345 oidn::Format::Float3,
349 pass_stride *
sizeof(
float),
350 stride * pass_stride *
sizeof(
float));
353 void set_pass_from_buffer(oidn::FilterRef &oidn_filter,
const char *
name, OIDNPass &oidn_pass)
355 const int64_t width = buffer_params_.width;
356 const int64_t height = buffer_params_.height;
358 oidn_filter.setImage(
359 name, oidn_pass.scaled_buffer.data(), oidn::Format::Float3, width, height, 0, 0, 0);
362 void set_pass(oidn::FilterRef &oidn_filter, OIDNPass &oidn_pass)
364 set_pass(oidn_filter, oidn_pass.name, oidn_pass);
366 void set_pass(oidn::FilterRef &oidn_filter,
const char *
name, OIDNPass &oidn_pass)
368 if (oidn_pass.scaled_buffer.empty()) {
369 set_pass_referenced(oidn_filter,
name, oidn_pass);
372 set_pass_from_buffer(oidn_filter,
name, oidn_pass);
376 void set_input_pass(oidn::FilterRef &oidn_filter, OIDNPass &oidn_pass)
378 set_pass_referenced(oidn_filter, oidn_pass.name, oidn_pass);
381 void set_guiding_passes(oidn::FilterRef &oidn_filter, OIDNPass &oidn_pass)
383 if (oidn_albedo_pass_) {
384 if (oidn_pass.use_denoising_albedo) {
385 set_pass(oidn_filter, oidn_albedo_pass_);
390 set_fake_albedo_pass(oidn_filter);
394 if (oidn_normal_pass_) {
395 set_pass(oidn_filter, oidn_normal_pass_);
399 void set_fake_albedo_pass(oidn::FilterRef &oidn_filter)
401 const int64_t width = buffer_params_.width;
402 const int64_t height = buffer_params_.height;
404 if (!albedo_replaced_with_fake_) {
405 const int64_t num_pixel_components = width * height * 3;
406 oidn_albedo_pass_.scaled_buffer.resize(num_pixel_components);
408 for (
int i = 0;
i < num_pixel_components; ++
i) {
409 oidn_albedo_pass_.scaled_buffer[
i] = 0.5f;
412 albedo_replaced_with_fake_ =
true;
415 set_pass(oidn_filter, oidn_albedo_pass_);
418 void set_output_pass(oidn::FilterRef &oidn_filter, OIDNPass &oidn_pass)
420 set_pass(oidn_filter,
"output", oidn_pass);
423 void set_quality(oidn::FilterRef &oidn_filter)
425# if OIDN_VERSION_MAJOR >= 2
426 switch (denoise_params_.quality) {
428# if OIDN_VERSION >= 20300
429 oidn_filter.set(
"quality", OIDN_QUALITY_FAST);
433 oidn_filter.set(
"quality", OIDN_QUALITY_BALANCED);
437 oidn_filter.set(
"quality", OIDN_QUALITY_HIGH);
444 void postprocess_output(
const OIDNPass &oidn_input_pass,
const OIDNPass &oidn_output_pass)
446 kernel_assert(oidn_input_pass.num_components == oidn_output_pass.num_components);
448 const int64_t x = buffer_params_.full_x;
449 const int64_t y = buffer_params_.full_y;
450 const int64_t width = buffer_params_.width;
451 const int64_t height = buffer_params_.height;
452 const int64_t offset = buffer_params_.offset;
453 const int64_t stride = buffer_params_.stride;
454 const int64_t pass_stride = buffer_params_.pass_stride;
455 const int64_t row_stride = stride * pass_stride;
457 const int64_t pixel_offset = offset +
x +
y * stride;
458 const int64_t buffer_offset = (pixel_offset * pass_stride);
460 float *buffer_data = render_buffers_->buffer.data();
462 const bool has_pass_sample_count = (pass_sample_count_ !=
PASS_UNUSED);
463 const bool need_scale = has_pass_sample_count || oidn_input_pass.use_compositing;
465 for (
int y = 0;
y < height; ++
y) {
466 float *buffer_row = buffer_data + buffer_offset +
y * row_stride;
467 for (
int x = 0;
x < width; ++
x) {
468 float *buffer_pixel = buffer_row +
x * pass_stride;
469 float *denoised_pixel = buffer_pixel + oidn_output_pass.offset;
472 const float pixel_scale = has_pass_sample_count ?
476 denoised_pixel[0] = denoised_pixel[0] * pixel_scale;
477 denoised_pixel[1] = denoised_pixel[1] * pixel_scale;
478 denoised_pixel[2] = denoised_pixel[2] * pixel_scale;
481 if (oidn_output_pass.num_components == 3) {
484 else if (!oidn_input_pass.use_compositing) {
488 const float *noisy_pixel = buffer_pixel + oidn_input_pass.offset;
489 denoised_pixel[3] = noisy_pixel[3];
494 denoised_pixel[3] = 0;
500 bool is_pass_scale_needed(OIDNPass &oidn_pass)
const
508 if (!oidn_pass.need_scale) {
512 if (num_samples_ == 1) {
521 void scale_pass_in_render_buffers(OIDNPass &oidn_pass)
523 const int64_t x = buffer_params_.full_x;
524 const int64_t y = buffer_params_.full_y;
525 const int64_t width = buffer_params_.width;
526 const int64_t height = buffer_params_.height;
527 const int64_t offset = buffer_params_.offset;
528 const int64_t stride = buffer_params_.stride;
529 const int64_t pass_stride = buffer_params_.pass_stride;
530 const int64_t row_stride = stride * pass_stride;
532 const int64_t pixel_offset = offset +
x +
y * stride;
533 const int64_t buffer_offset = (pixel_offset * pass_stride);
535 float *buffer_data = render_buffers_->buffer.data();
537 const bool has_pass_sample_count = (pass_sample_count_ !=
PASS_UNUSED);
539 for (
int y = 0;
y < height; ++
y) {
540 float *buffer_row = buffer_data + buffer_offset +
y * row_stride;
541 for (
int x = 0;
x < width; ++
x) {
542 float *buffer_pixel = buffer_row +
x * pass_stride;
543 float *pass_pixel = buffer_pixel + oidn_pass.offset;
545 const float pixel_scale = 1.0f / (has_pass_sample_count ?
549 pass_pixel[0] = pass_pixel[0] * pixel_scale;
550 pass_pixel[1] = pass_pixel[1] * pixel_scale;
551 pass_pixel[2] = pass_pixel[2] * pixel_scale;
556 OIDNDenoiser *denoiser_ =
nullptr;
558 const DenoiseParams &denoise_params_;
559 const BufferParams &buffer_params_;
560 RenderBuffers *render_buffers_ =
nullptr;
561 int num_samples_ = 0;
562 bool allow_inplace_modification_ =
false;
565 vector<uint8_t> custom_weights;
568 OIDNPass oidn_albedo_pass_;
569 OIDNPass oidn_normal_pass_;
574 bool albedo_replaced_with_fake_ =
false;
590 queue->copy_from_device(render_buffers->
buffer);
591 queue->synchronize();
602 queue->copy_to_device(render_buffers->
buffer);
603 queue->synchronize();
614 const int num_samples,
615 bool allow_inplace_modification)
618 <<
"OpenImageDenoise is not supported on this platform or build.";
620#ifdef WITH_OPENIMAGEDENOISE
625 copy_render_buffers_from_device(queue, render_buffers);
627 OIDNDenoiseContext context(
628 this,
params_, buffer_params, render_buffers, num_samples, allow_inplace_modification);
630 if (context.need_denoising()) {
631 context.read_guiding_passes();
633 const std::array<PassType, 3> passes = {
642 for (
const PassType pass_type : passes) {
643 context.denoise_pass(pass_type);
651 copy_render_buffers_to_device(queue, render_buffers);
655 (void)render_buffers;
657 (void)allow_inplace_modification;
int get_pass_offset(PassType type, PassMode mode=PassMode::NOISY) const
void set_error(const string &error)
Denoiser(Device *denoiser_device, const DenoiseParams ¶ms)
bool is_cancelled() const
virtual unique_ptr< DeviceQueue > gpu_queue_create()
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)
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(const 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