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 )
50 OIDNPass(
const BufferParams &buffer_params,
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;
97 array<float> scaled_buffer;
100class OIDNDenoiseContext {
102 OIDNDenoiseContext(OIDNDenoiser *denoiser,
103 const DenoiseParams &denoise_params,
104 const BufferParams &buffer_params,
105 RenderBuffers *render_buffers,
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) {
117 oidn_albedo_pass_ = OIDNPass(buffer_params_,
"albedo", PASS_DENOISING_ALBEDO);
120 if (denoise_params_.use_pass_normal) {
121 oidn_normal_pass_ = OIDNPass(buffer_params_,
"normal", PASS_DENOISING_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;
270 PassAccessor::Destination destination(pass_data, 3);
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;
282 void read_pass_pixels(
const OIDNPass &oidn_pass,
const PassAccessor::Destination &destination)
284 PassAccessor::PassAccessInfo pass_access_info;
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_);
301 BufferParams buffer_params = buffer_params_;
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;
319 array<float> &scaled_buffer = oidn_pass.scaled_buffer;
320 scaled_buffer.
resize(width * height * 3);
322 const PassAccessor::Destination destination(scaled_buffer.
data(), 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;
559 OIDNDenoiser *denoiser_ =
nullptr;
561 const DenoiseParams &denoise_params_;
562 const BufferParams &buffer_params_;
563 RenderBuffers *render_buffers_ =
nullptr;
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;
594 queue->synchronize();
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
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;
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a vector
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()
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)
ThreadQueue * queue
all scheduled work for the cpu
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