Fix: Cycles OIDN GPU denoiser bugs

- Incorrect accurate prefiltering of albedo and normal (lower than expected quality)
- Changing the prefiltering mode has no immediate effect
- Default memory limit is too high (more than OIDN default)
- Memory limit is applied only to the main filter
- Quality setting applied only to the main filter

Pull Request: https://projects.blender.org/blender/blender/pulls/117930
This commit is contained in:
Attila Áfra 2024-02-07 13:34:35 +01:00 committed by Brecht Van Lommel
parent 4db1426818
commit c1f55288e9
2 changed files with 88 additions and 91 deletions

View File

@ -149,9 +149,55 @@ OIDNFilter OIDNDenoiserGPU::create_filter()
denoiser_device_->set_error(error_message);
}
}
# if OIDN_VERSION_MAJOR >= 2
switch (quality_) {
case DENOISER_QUALITY_BALANCED:
oidnSetFilterInt(filter, "quality", OIDN_QUALITY_BALANCED);
break;
case DENOISER_QUALITY_HIGH:
default:
oidnSetFilterInt(filter, "quality", OIDN_QUALITY_HIGH);
}
# endif
return filter;
}
bool OIDNDenoiserGPU::commit_and_execute_filter(OIDNFilter filter, ExecMode mode)
{
const char *error_message = nullptr;
OIDNError err = OIDN_ERROR_NONE;
for (;;) {
oidnCommitFilter(filter);
if (mode == ExecMode::ASYNC) {
oidnExecuteFilterAsync(filter);
}
else {
oidnExecuteFilter(filter);
}
/* If OIDN runs out of memory, reduce mem limit and retry */
err = oidnGetDeviceError(oidn_device_, (const char **)&error_message);
if (err != OIDN_ERROR_OUT_OF_MEMORY || max_mem_ < 200) {
break;
}
max_mem_ = max_mem_ / 2;
oidnSetFilterInt(oidn_filter_, "maxMemoryMB", max_mem_);
}
if (err != OIDN_ERROR_NONE) {
if (error_message == nullptr) {
error_message = "Unspecified OIDN error";
}
LOG(ERROR) << "OIDN error: " << error_message;
denoiser_device_->set_error(error_message);
return false;
}
return true;
}
bool OIDNDenoiserGPU::denoise_create_if_needed(DenoiseContext &context)
{
const bool recreate_denoiser = (oidn_device_ == nullptr) || (oidn_filter_ == nullptr) ||
@ -224,24 +270,8 @@ bool OIDNDenoiserGPU::denoise_create_if_needed(DenoiseContext &context)
oidnSetFilterBool(oidn_filter_, "hdr", true);
oidnSetFilterBool(oidn_filter_, "srgb", false);
oidnSetFilterInt(oidn_filter_, "maxMemoryMB", max_mem_);
if (params_.prefilter == DENOISER_PREFILTER_NONE ||
params_.prefilter == DENOISER_PREFILTER_ACCURATE)
{
oidnSetFilterInt(oidn_filter_, "cleanAux", true);
}
# if OIDN_VERSION_MAJOR >= 2
switch (params_.quality) {
case DENOISER_QUALITY_BALANCED:
oidnSetFilterInt(oidn_filter_, "quality", OIDN_QUALITY_BALANCED);
break;
case DENOISER_QUALITY_HIGH:
default:
oidnSetFilterInt(oidn_filter_, "quality", OIDN_QUALITY_HIGH);
}
quality_ = params_.quality;
# endif
if (context.use_pass_albedo) {
albedo_filter_ = create_filter();
@ -295,6 +325,7 @@ bool OIDNDenoiserGPU::denoise_run(const DenoiseContext &context, const DenoisePa
pass.denoised_offset * sizeof(float),
pass_stride_in_bytes,
pass_stride_in_bytes * context.buffer_params.stride);
set_filter_pass(oidn_filter_,
"output",
context.render_buffers->buffer.device_pointer,
@ -312,8 +343,18 @@ bool OIDNDenoiserGPU::denoise_run(const DenoiseContext &context, const DenoisePa
const int64_t row_stride_in_bytes = context.guiding_params.stride * pixel_stride_in_bytes;
if (context.use_pass_albedo) {
if (params_.prefilter == DENOISER_PREFILTER_NONE) {
set_filter_pass(oidn_filter_,
set_filter_pass(oidn_filter_,
"albedo",
d_guiding_buffer,
OIDN_FORMAT_FLOAT3,
context.buffer_params.width,
context.buffer_params.height,
context.guiding_params.pass_albedo * sizeof(float),
pixel_stride_in_bytes,
row_stride_in_bytes);
if (params_.prefilter == DENOISER_PREFILTER_ACCURATE) {
set_filter_pass(albedo_filter_,
"albedo",
d_guiding_buffer,
OIDN_FORMAT_FLOAT3,
@ -322,17 +363,7 @@ bool OIDNDenoiserGPU::denoise_run(const DenoiseContext &context, const DenoisePa
context.guiding_params.pass_albedo * sizeof(float),
pixel_stride_in_bytes,
row_stride_in_bytes);
}
else {
set_filter_pass(albedo_filter_,
"color",
d_guiding_buffer,
OIDN_FORMAT_FLOAT3,
context.buffer_params.width,
context.buffer_params.height,
context.guiding_params.pass_albedo * sizeof(float),
pixel_stride_in_bytes,
row_stride_in_bytes);
set_filter_pass(albedo_filter_,
"output",
d_guiding_buffer,
@ -342,36 +373,27 @@ bool OIDNDenoiserGPU::denoise_run(const DenoiseContext &context, const DenoisePa
context.guiding_params.pass_albedo * sizeof(float),
pixel_stride_in_bytes,
row_stride_in_bytes);
oidnCommitFilter(albedo_filter_);
oidnExecuteFilterAsync(albedo_filter_);
set_filter_pass(oidn_filter_,
"albedo",
d_guiding_buffer,
OIDN_FORMAT_FLOAT3,
context.buffer_params.width,
context.buffer_params.height,
context.guiding_params.pass_albedo * sizeof(float),
pixel_stride_in_bytes,
row_stride_in_bytes);
if (!commit_and_execute_filter(albedo_filter_, ExecMode::ASYNC)) {
return false;
}
}
}
if (context.use_pass_normal) {
if (params_.prefilter == DENOISER_PREFILTER_NONE) {
set_filter_pass(oidn_filter_,
"normal",
d_guiding_buffer,
OIDN_FORMAT_FLOAT3,
context.buffer_params.width,
context.buffer_params.height,
context.guiding_params.pass_normal * sizeof(float),
pixel_stride_in_bytes,
row_stride_in_bytes);
}
else {
set_filter_pass(oidn_filter_,
"normal",
d_guiding_buffer,
OIDN_FORMAT_FLOAT3,
context.buffer_params.width,
context.buffer_params.height,
context.guiding_params.pass_normal * sizeof(float),
pixel_stride_in_bytes,
row_stride_in_bytes);
if (params_.prefilter == DENOISER_PREFILTER_ACCURATE) {
set_filter_pass(normal_filter_,
"color",
"normal",
d_guiding_buffer,
OIDN_FORMAT_FLOAT3,
context.buffer_params.width,
@ -390,47 +412,15 @@ bool OIDNDenoiserGPU::denoise_run(const DenoiseContext &context, const DenoisePa
pixel_stride_in_bytes,
row_stride_in_bytes);
oidnCommitFilter(normal_filter_);
oidnExecuteFilterAsync(normal_filter_);
set_filter_pass(oidn_filter_,
"normal",
d_guiding_buffer,
OIDN_FORMAT_FLOAT3,
context.buffer_params.width,
context.buffer_params.height,
context.guiding_params.pass_normal * sizeof(float),
pixel_stride_in_bytes,
row_stride_in_bytes);
if (!commit_and_execute_filter(normal_filter_, ExecMode::ASYNC)) {
return false;
}
}
}
}
oidnCommitFilter(oidn_filter_);
oidnExecuteFilter(oidn_filter_);
const char *out_message = nullptr;
OIDNError err = oidnGetDeviceError(oidn_device_, (const char **)&out_message);
if (OIDN_ERROR_NONE != err) {
/* If OIDN runs out of memory, reduce mem limit and retry */
while (err == OIDN_ERROR_OUT_OF_MEMORY && max_mem_ > 200) {
max_mem_ = max_mem_ / 2;
oidnSetFilterInt(oidn_filter_, "maxMemoryMB", max_mem_);
oidnCommitFilter(oidn_filter_);
oidnExecuteFilter(oidn_filter_);
err = oidnGetDeviceError(oidn_device_, &out_message);
}
if (out_message) {
LOG(ERROR) << "OIDN error: " << out_message;
denoiser_device_->set_error(out_message);
}
else {
LOG(ERROR) << "OIDN error: unspecified";
denoiser_device_->set_error("Unspecified OIDN error");
}
return false;
}
return true;
oidnSetFilterInt(oidn_filter_, "cleanAux", params_.prefilter != DENOISER_PREFILTER_FAST);
return commit_and_execute_filter(oidn_filter_);
}
void OIDNDenoiserGPU::set_filter_pass(OIDNFilter filter,

View File

@ -36,6 +36,11 @@ class OIDNDenoiserGPU : public DenoiserGPU {
static bool is_device_supported(const DeviceInfo &device);
protected:
enum class ExecMode {
SYNC,
ASYNC,
};
virtual uint get_device_type_mask() const override;
/* Create OIDN denoiser descriptor if needed.
@ -50,6 +55,7 @@ class OIDNDenoiserGPU : public DenoiserGPU {
virtual bool denoise_run(const DenoiseContext &context, const DenoisePass &pass) override;
OIDNFilter create_filter();
bool commit_and_execute_filter(OIDNFilter filter, ExecMode mode = ExecMode::SYNC);
void set_filter_pass(OIDNFilter filter,
const char *name,
@ -73,7 +79,8 @@ class OIDNDenoiserGPU : public DenoiserGPU {
bool use_pass_normal_ = false;
DenoiserQuality quality_ = DENOISER_QUALITY_HIGH;
int max_mem_ = 3000;
/* Filter memory usage limit if we ran out of memory with OIDN's default limit. */
int max_mem_ = 768;
};
CCL_NAMESPACE_END