Files
test/source/blender/compositor/operations/COM_DenoiseOperation.cc
Campbell Barton e955c94ed3 License Headers: Set copyright to "Blender Authors", add AUTHORS
Listing the "Blender Foundation" as copyright holder implied the Blender
Foundation holds copyright to files which may include work from many
developers.

While keeping copyright on headers makes sense for isolated libraries,
Blender's own code may be refactored or moved between files in a way
that makes the per file copyright holders less meaningful.

Copyright references to the "Blender Foundation" have been replaced with
"Blender Authors", with the exception of `./extern/` since these this
contains libraries which are more isolated, any changed to license
headers there can be handled on a case-by-case basis.

Some directories in `./intern/` have also been excluded:

- `./intern/cycles/` it's own `AUTHORS` file is planned.
- `./intern/opensubdiv/`.

An "AUTHORS" file has been added, using the chromium projects authors
file as a template.

Design task: #110784

Ref !110783.
2023-08-16 00:20:26 +10:00

308 lines
8.6 KiB
C++

/* SPDX-FileCopyrightText: 2019 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "COM_DenoiseOperation.h"
#include "BLI_system.h"
#ifdef WITH_OPENIMAGEDENOISE
# include "BLI_threads.h"
# include <OpenImageDenoise/oidn.hpp>
static pthread_mutex_t oidn_lock = BLI_MUTEX_INITIALIZER;
#endif
namespace blender::compositor {
bool COM_is_denoise_supported()
{
#ifdef WITH_OPENIMAGEDENOISE
/* Always supported through Accelerate framework BNNS on macOS. */
# ifdef __APPLE__
return true;
# else
return BLI_cpu_support_sse41();
# endif
#else
return false;
#endif
}
class DenoiseFilter {
private:
#ifdef WITH_OPENIMAGEDENOISE
oidn::DeviceRef device_;
oidn::FilterRef filter_;
bool initialized_ = false;
#endif
public:
#ifdef WITH_OPENIMAGEDENOISE
~DenoiseFilter()
{
BLI_assert(!initialized_);
}
void init_and_lock_denoiser(MemoryBuffer *output)
{
/* Since it's memory intensive, it's better to run only one instance of OIDN at a time.
* OpenImageDenoise is multithreaded internally and should use all available cores
* nonetheless. */
BLI_mutex_lock(&oidn_lock);
device_ = oidn::newDevice();
device_.set("setAffinity", false);
device_.commit();
filter_ = device_.newFilter("RT");
initialized_ = true;
set_image("output", output);
}
void deinit_and_unlock_denoiser()
{
BLI_mutex_unlock(&oidn_lock);
initialized_ = false;
}
void set_image(const StringRef name, MemoryBuffer *buffer)
{
BLI_assert(initialized_);
BLI_assert(!buffer->is_a_single_elem());
filter_.setImage(name.data(),
buffer->get_buffer(),
oidn::Format::Float3,
buffer->get_width(),
buffer->get_height(),
0,
buffer->get_elem_bytes_len());
}
template<typename T> void set(const StringRef option_name, T value)
{
BLI_assert(initialized_);
filter_.set(option_name.data(), value);
}
void execute()
{
BLI_assert(initialized_);
filter_.commit();
filter_.execute();
}
#else
void init_and_lock_denoiser(MemoryBuffer * /*output*/) {}
void deinit_and_unlock_denoiser() {}
void set_image(const StringRef /*name*/, MemoryBuffer * /*buffer*/) {}
template<typename T> void set(const StringRef /*option_name*/, T /*value*/) {}
void execute() {}
#endif
};
DenoiseBaseOperation::DenoiseBaseOperation()
{
flags_.is_fullframe_operation = true;
output_rendered_ = false;
}
bool DenoiseBaseOperation::determine_depending_area_of_interest(
rcti * /*input*/, ReadBufferOperation *read_operation, rcti *output)
{
if (is_cached()) {
return false;
}
rcti new_input;
new_input.xmax = this->get_width();
new_input.xmin = 0;
new_input.ymax = this->get_height();
new_input.ymin = 0;
return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output);
}
void DenoiseBaseOperation::get_area_of_interest(const int /*input_idx*/,
const rcti & /*output_area*/,
rcti &r_input_area)
{
r_input_area = this->get_canvas();
}
DenoiseOperation::DenoiseOperation()
{
this->add_input_socket(DataType::Color);
this->add_input_socket(DataType::Vector);
this->add_input_socket(DataType::Color);
this->add_output_socket(DataType::Color);
settings_ = nullptr;
}
void DenoiseOperation::init_execution()
{
SingleThreadedOperation::init_execution();
input_program_color_ = get_input_socket_reader(0);
input_program_normal_ = get_input_socket_reader(1);
input_program_albedo_ = get_input_socket_reader(2);
}
void DenoiseOperation::deinit_execution()
{
input_program_color_ = nullptr;
input_program_normal_ = nullptr;
input_program_albedo_ = nullptr;
SingleThreadedOperation::deinit_execution();
}
static bool are_guiding_passes_noise_free(const NodeDenoise *settings)
{
switch (settings->prefilter) {
case CMP_NODE_DENOISE_PREFILTER_NONE:
case CMP_NODE_DENOISE_PREFILTER_ACCURATE: /* Prefiltered with #DenoisePrefilterOperation. */
return true;
case CMP_NODE_DENOISE_PREFILTER_FAST:
default:
return false;
}
}
void DenoiseOperation::hash_output_params()
{
if (settings_) {
hash_params(int(settings_->hdr), are_guiding_passes_noise_free(settings_));
}
}
MemoryBuffer *DenoiseOperation::create_memory_buffer(rcti *rect2)
{
MemoryBuffer *tile_color = (MemoryBuffer *)input_program_color_->initialize_tile_data(rect2);
MemoryBuffer *tile_normal = (MemoryBuffer *)input_program_normal_->initialize_tile_data(rect2);
MemoryBuffer *tile_albedo = (MemoryBuffer *)input_program_albedo_->initialize_tile_data(rect2);
rcti rect;
rect.xmin = 0;
rect.ymin = 0;
rect.xmax = get_width();
rect.ymax = get_height();
MemoryBuffer *result = new MemoryBuffer(DataType::Color, rect);
this->generate_denoise(result, tile_color, tile_normal, tile_albedo, settings_);
return result;
}
void DenoiseOperation::generate_denoise(MemoryBuffer *output,
MemoryBuffer *input_color,
MemoryBuffer *input_normal,
MemoryBuffer *input_albedo,
const NodeDenoise *settings)
{
BLI_assert(input_color->get_buffer());
if (!input_color->get_buffer()) {
return;
}
BLI_assert(COM_is_denoise_supported());
/* OpenImageDenoise needs full buffers. */
MemoryBuffer *buf_color = input_color->is_a_single_elem() ? input_color->inflate() : input_color;
MemoryBuffer *buf_normal = input_normal && input_normal->is_a_single_elem() ?
input_normal->inflate() :
input_normal;
MemoryBuffer *buf_albedo = input_albedo && input_albedo->is_a_single_elem() ?
input_albedo->inflate() :
input_albedo;
DenoiseFilter filter;
filter.init_and_lock_denoiser(output);
filter.set_image("color", buf_color);
filter.set_image("normal", buf_normal);
filter.set_image("albedo", buf_albedo);
BLI_assert(settings);
if (settings) {
filter.set("hdr", settings->hdr);
filter.set("srgb", false);
filter.set("cleanAux", are_guiding_passes_noise_free(settings));
}
filter.execute();
filter.deinit_and_unlock_denoiser();
/* Copy the alpha channel, OpenImageDenoise currently only supports RGB. */
output->copy_from(input_color, input_color->get_rect(), 3, COM_DATA_TYPE_VALUE_CHANNELS, 3);
/* Delete inflated buffers. */
if (input_color->is_a_single_elem()) {
delete buf_color;
}
if (input_normal && input_normal->is_a_single_elem()) {
delete buf_normal;
}
if (input_albedo && input_albedo->is_a_single_elem()) {
delete buf_albedo;
}
}
void DenoiseOperation::update_memory_buffer(MemoryBuffer *output,
const rcti & /*area*/,
Span<MemoryBuffer *> inputs)
{
if (!output_rendered_) {
this->generate_denoise(output, inputs[0], inputs[1], inputs[2], settings_);
output_rendered_ = true;
}
}
DenoisePrefilterOperation::DenoisePrefilterOperation(DataType data_type)
{
this->add_input_socket(data_type);
this->add_output_socket(data_type);
image_name_ = "";
}
void DenoisePrefilterOperation::hash_output_params()
{
hash_param(image_name_);
}
MemoryBuffer *DenoisePrefilterOperation::create_memory_buffer(rcti *rect2)
{
MemoryBuffer *input = (MemoryBuffer *)this->get_input_operation(0)->initialize_tile_data(rect2);
rcti rect;
BLI_rcti_init(&rect, 0, get_width(), 0, get_height());
MemoryBuffer *result = new MemoryBuffer(get_output_socket()->get_data_type(), rect);
generate_denoise(result, input);
return result;
}
void DenoisePrefilterOperation::generate_denoise(MemoryBuffer *output, MemoryBuffer *input)
{
BLI_assert(COM_is_denoise_supported());
/* Denoising needs full buffers. */
MemoryBuffer *input_buf = input->is_a_single_elem() ? input->inflate() : input;
DenoiseFilter filter;
filter.init_and_lock_denoiser(output);
filter.set_image(image_name_, input_buf);
filter.execute();
filter.deinit_and_unlock_denoiser();
/* Delete inflated buffers. */
if (input->is_a_single_elem()) {
delete input_buf;
}
}
void DenoisePrefilterOperation::update_memory_buffer(MemoryBuffer *output,
const rcti & /*area*/,
Span<MemoryBuffer *> inputs)
{
if (!output_rendered_) {
this->generate_denoise(output, inputs[0]);
output_rendered_ = true;
}
}
} // namespace blender::compositor