Compositor: Add OIDN Quality to denoise node

This commit exposes the "Quality" option of the Open Image Denoiser
to the user for the denoise node in the compositor.

There are a few quality modes:
- High - Highest quality, but takes the longest to process.
- Balanced - Slightly lower quality, but usually halves
the processing time compared to High.
- Fast - Further reduce the quality, for a small increase in
speed over Balanced.

Along with that there is a `Follow Scene` option which will use the
quality set in the scene settings.

This allows users that have multiple denoise nodes
(E.g. For multi-pass denoising), to quickly switch all nodes between
different quality modes.

Performance (denoising time):
High: 13 seconds
Balanced: 6 seconds
Fast: 5 seconds

Test setup:
CPU: AMD Ryzen 9 5950X
Denoising a 3840x2160 render

---

Follow ups:
Ideally the "Denoise Nodes" UI panel in the render properties panel
would be hidden if the compositor setup does not contain any
denoise nodes.

However implementing this efficiently can be difficult and so it was
decided this task was outside the scope of this commit.

Pull Request: https://projects.blender.org/blender/blender/pulls/130252
This commit is contained in:
Alaska
2024-12-28 01:44:49 +01:00
committed by Alaska
parent 02b1e5a48f
commit 8efd41271d
13 changed files with 204 additions and 2 deletions

View File

@@ -11,7 +11,7 @@ from bl_ui.utils import PresetPanel
from bpy.types import Panel, Menu
from bl_ui.properties_grease_pencil_common import GreasePencilSimplifyPanel
from bl_ui.properties_render import draw_curves_settings, CompositorPerformanceButtonsPanel
from bl_ui.properties_render import draw_curves_settings, CompositorPerformanceButtonsPanel, CompositorDenoisePerformanceButtonsPanel
from bl_ui.properties_view_layer import (
ViewLayerCryptomattePanelHelper,
ViewLayerAOVPanelHelper,
@@ -798,6 +798,12 @@ class CYCLES_RENDER_PT_performance_compositor(CyclesButtonsPanel, CompositorPerf
bl_options = {'DEFAULT_CLOSED'}
class CYCLES_RENDER_PT_performance_compositor_denoise_settings(
CyclesButtonsPanel, CompositorDenoisePerformanceButtonsPanel, Panel):
bl_parent_id = "CYCLES_RENDER_PT_performance_compositor"
bl_options = {'DEFAULT_CLOSED'}
class CYCLES_RENDER_PT_performance_threads(CyclesButtonsPanel, Panel):
bl_label = "Threads"
bl_parent_id = "CYCLES_RENDER_PT_performance"
@@ -2464,6 +2470,7 @@ classes = (
CYCLES_RENDER_PT_film_transparency,
CYCLES_RENDER_PT_performance,
CYCLES_RENDER_PT_performance_compositor,
CYCLES_RENDER_PT_performance_compositor_denoise_settings,
CYCLES_RENDER_PT_performance_threads,
CYCLES_RENDER_PT_performance_memory,
CYCLES_RENDER_PT_performance_acceleration_structure,

View File

@@ -763,6 +763,24 @@ class CompositorPerformanceButtonsPanel:
col.prop(rd, "compositor_precision", text="Precision")
class CompositorDenoisePerformanceButtonsPanel:
bl_label = "Denoise Nodes"
def draw(self, context):
layout = self.layout
scene = context.scene
rd = scene.render
layout.use_property_split = True
layout.use_property_decorate = False
col = layout.column()
row = col.row()
col.prop(rd, "compositor_denoise_preview_quality", text="Preview Quality")
col.prop(rd, "compositor_denoise_final_quality", text="Final Quality")
class RENDER_PT_eevee_performance_compositor(RenderButtonsPanel, CompositorPerformanceButtonsPanel, Panel):
bl_options = {'DEFAULT_CLOSED'}
bl_parent_id = "RENDER_PT_eevee_performance"
@@ -772,6 +790,16 @@ class RENDER_PT_eevee_performance_compositor(RenderButtonsPanel, CompositorPerfo
}
class RENDER_PT_eevee_performance_compositor_denoise_settings(
RenderButtonsPanel, CompositorDenoisePerformanceButtonsPanel, Panel):
bl_options = {'DEFAULT_CLOSED'}
bl_parent_id = "RENDER_PT_eevee_performance_compositor"
COMPAT_ENGINES = {
'BLENDER_EEVEE_NEXT',
'BLENDER_WORKBENCH',
}
class RENDER_PT_eevee_performance_memory(RenderButtonsPanel, Panel):
bl_label = "Memory"
bl_parent_id = "RENDER_PT_eevee_performance"
@@ -1044,6 +1072,7 @@ classes = (
RENDER_PT_eevee_performance_memory,
RENDER_PT_eevee_performance_viewport,
RENDER_PT_eevee_performance_compositor,
RENDER_PT_eevee_performance_compositor_denoise_settings,
RENDER_PT_gpencil,

View File

@@ -31,7 +31,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
#define BLENDER_FILE_SUBVERSION 16
#define BLENDER_FILE_SUBVERSION 17
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and cancel loading the file, showing a warning to

View File

@@ -5387,6 +5387,23 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 404, 17)) {
if (!DNA_struct_member_exists(
fd->filesdna, "RenderData", "RenderSettings", "compositor_denoise_preview_quality"))
{
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
scene->r.compositor_denoise_preview_quality = SCE_COMPOSITOR_DENOISE_BALANCED;
}
}
if (!DNA_struct_member_exists(
fd->filesdna, "RenderData", "RenderSettings", "compositor_denoise_final_quality"))
{
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
scene->r.compositor_denoise_final_quality = SCE_COMPOSITOR_DENOISE_HIGH;
}
}
}
/* Always run this versioning; meshes are written with the legacy format which always needs to
* be converted to the new format on file load. Can be moved to a subversion check in a larger
* breaking release. */

View File

@@ -54,6 +54,10 @@ class Context {
/* True if the compositor should use GPU acceleration. */
virtual bool use_gpu() const = 0;
/* Get the OIDN denoiser quality which should be used if the user doesn't explicitly set
* denoising quality on a node. */
virtual eCompositorDenoiseQaulity get_denoise_quality() const = 0;
/* True if the compositor should write file outputs, false otherwise. */
virtual bool use_file_output() const = 0;

View File

@@ -75,6 +75,12 @@ class Context : public compositor::Context {
return true;
}
eCompositorDenoiseQaulity get_denoise_quality() const override
{
return static_cast<eCompositorDenoiseQaulity>(
this->get_render_data().compositor_denoise_preview_quality);
}
bool use_file_output() const override
{
return false;

View File

@@ -1571,6 +1571,8 @@ typedef struct NodeCryptomatte {
typedef struct NodeDenoise {
char hdr;
char prefilter;
char quality;
char _pad[1];
} NodeDenoise;
typedef struct NodeMapRange {
@@ -2802,6 +2804,14 @@ typedef enum CMPNodeDenoisePrefilter {
CMP_NODE_DENOISE_PREFILTER_ACCURATE = 2
} CMPNodeDenoisePrefilter;
/** #NodeDenoise.quality */
typedef enum CMPNodeDenoiseQuality {
CMP_NODE_DENOISE_QUALITY_SCENE = 0,
CMP_NODE_DENOISE_QUALITY_HIGH = 1,
CMP_NODE_DENOISE_QUALITY_BALANCED = 2,
CMP_NODE_DENOISE_QUALITY_FAST = 3,
} CMPNodeDenoiseQuality;
/* Color combine/separate modes */
typedef enum CMPNodeCombSepColorMode {

View File

@@ -129,6 +129,9 @@
.ffcodecdata = _DNA_DEFAULT_FFMpegCodecData, \
\
.motion_blur_shutter = 0.5f, \
\
.compositor_denoise_final_quality = SCE_COMPOSITOR_DENOISE_HIGH, \
.compositor_denoise_preview_quality = SCE_COMPOSITOR_DENOISE_BALANCED, \
}
#define _DNA_DEFAULT_AudioData \

View File

@@ -829,6 +829,10 @@ typedef struct RenderData {
/** Precision used by the GPU execution of the compositor tree. */
int compositor_precision; /* eCompositorPrecision */
/** Global configuration for denoise compositor nodes. */
int compositor_denoise_preview_quality; /* eCompositorDenoiseQaulity */
int compositor_denoise_final_quality; /* eCompositorDenoiseQaulity */
} RenderData;
/** #RenderData::quality_flag */
@@ -861,6 +865,14 @@ typedef enum eCompositorPrecision {
SCE_COMPOSITOR_PRECISION_FULL = 1,
} eCompositorPrecision;
/** #RenderData::compositor_denoise_preview_quality */
/** #RenderData::compositor_denoise_final_quality */
typedef enum eCompositorDenoiseQaulity {
SCE_COMPOSITOR_DENOISE_HIGH = 0,
SCE_COMPOSITOR_DENOISE_BALANCED = 1,
SCE_COMPOSITOR_DENOISE_FAST = 2,
} eCompositorDenoiseQaulity;
/** \} */
/* -------------------------------------------------------------------- */

View File

@@ -9217,6 +9217,21 @@ static void def_cmp_denoise(StructRNA *srna)
"passes are noisy using extra processing time."},
{0, nullptr, 0, nullptr, nullptr}};
static const EnumPropertyItem quality_items[] = {
{CMP_NODE_DENOISE_QUALITY_SCENE,
"FOLLOW_SCENE",
0,
"Follow Scene",
"Use the scene's denoising quality setting"},
{CMP_NODE_DENOISE_QUALITY_HIGH, "HIGH", 0, "High", "High quality"},
{CMP_NODE_DENOISE_QUALITY_BALANCED,
"BALANCED",
0,
"Balanced",
"Balanced between performance and quality"},
{CMP_NODE_DENOISE_QUALITY_FAST, "FAST", 0, "Fast", "High perfomance"},
{0, nullptr, 0, nullptr, nullptr}};
RNA_def_struct_sdna_from(srna, "NodeDenoise", "storage");
prop = RNA_def_property(srna, "use_hdr", PROP_BOOLEAN, PROP_NONE);
@@ -9230,6 +9245,12 @@ static void def_cmp_denoise(StructRNA *srna)
RNA_def_property_enum_default(prop, CMP_NODE_DENOISE_PREFILTER_ACCURATE);
RNA_def_property_ui_text(prop, "", "Denoising prefilter");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "quality", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, quality_items);
RNA_def_property_enum_default(prop, CMP_NODE_DENOISE_QUALITY_SCENE);
RNA_def_property_ui_text(prop, "", "Denoising quality");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
static void def_cmp_kuwahara(StructRNA *srna)

View File

@@ -6785,6 +6785,17 @@ static void rna_def_scene_render_data(BlenderRNA *brna)
{0, nullptr, 0, nullptr, nullptr},
};
static const EnumPropertyItem compositor_denoise_quality_items[] = {
{SCE_COMPOSITOR_DENOISE_HIGH, "HIGH", 0, "High", "High quality"},
{SCE_COMPOSITOR_DENOISE_BALANCED,
"BALANCED",
0,
"Balanced",
"Balanced between performance and quality"},
{SCE_COMPOSITOR_DENOISE_FAST, "FAST", 0, "Fast", "High perfomance"},
{0, nullptr, 0, nullptr, nullptr},
};
rna_def_scene_ffmpeg_settings(brna);
srna = RNA_def_struct(brna, "RenderSettings", nullptr);
@@ -7526,6 +7537,26 @@ static void rna_def_scene_render_data(BlenderRNA *brna)
prop, "Compositor Precision", "The precision of compositor intermediate result");
RNA_def_property_update(prop, NC_NODE | ND_DISPLAY, "rna_Scene_compositor_update");
prop = RNA_def_property(srna, "compositor_denoise_preview_quality", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, nullptr, "compositor_denoise_preview_quality");
RNA_def_property_enum_items(prop, compositor_denoise_quality_items);
RNA_def_property_enum_default(prop, SCE_COMPOSITOR_DENOISE_BALANCED);
RNA_def_property_ui_text(prop,
"Compositor Preview Denoise Quality",
"The quality used by denoise nodes during viewport and interactive "
"compositing if the nodes' quality option is set to Follow Scene");
RNA_def_property_update(prop, NC_NODE | ND_DISPLAY, "rna_Scene_compositor_update");
prop = RNA_def_property(srna, "compositor_denoise_final_quality", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, nullptr, "compositor_denoise_final_quality");
RNA_def_property_enum_items(prop, compositor_denoise_quality_items);
RNA_def_property_enum_default(prop, SCE_COMPOSITOR_DENOISE_HIGH);
RNA_def_property_ui_text(prop,
"Compositor Final Denoise Quality",
"The quality used by denoise nodes during the compositing of final "
"renders if the nodes' quality option is set to Follow Scene");
RNA_def_property_update(prop, NC_NODE | ND_DISPLAY, "rna_Scene_compositor_update");
/* Nestled Data. */
/* *** Non-Animated *** */
RNA_define_animate_sdna(false);

View File

@@ -54,6 +54,7 @@ static void node_composit_init_denonise(bNodeTree * /*ntree*/, bNode *node)
NodeDenoise *ndg = MEM_cnew<NodeDenoise>(__func__);
ndg->hdr = true;
ndg->prefilter = CMP_NODE_DENOISE_PREFILTER_ACCURATE;
ndg->quality = CMP_NODE_DENOISE_QUALITY_SCENE;
node->storage = ndg;
}
@@ -72,6 +73,8 @@ static void node_composit_buts_denoise(uiLayout *layout, bContext * /*C*/, Point
uiItemL(layout, IFACE_("Prefilter:"), ICON_NONE);
uiItemR(layout, ptr, "prefilter", UI_ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
uiItemL(layout, IFACE_("Quality:"), ICON_NONE);
uiItemR(layout, ptr, "quality", UI_ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
uiItemR(layout, ptr, "use_hdr", UI_ITEM_R_SPLIT_EMPTY_NAME, std::nullopt, ICON_NONE);
}
@@ -132,6 +135,7 @@ class DenoiseOperation : public NodeOperation {
filter.setImage("output", output_color, oidn::Format::Float3, width, height, 0, pixel_stride);
filter.set("hdr", use_hdr());
filter.set("cleanAux", auxiliary_passes_are_clean());
this->set_filter_quality(filter);
filter.setProgressMonitorFunction(oidn_progress_monitor_function, &context());
/* If the albedo input is not a single value input, download the albedo texture, denoise it
@@ -148,6 +152,7 @@ class DenoiseOperation : public NodeOperation {
if (should_denoise_auxiliary_passes()) {
oidn::FilterRef albedoFilter = device.newFilter("RT");
this->set_filter_quality(albedoFilter);
albedoFilter.setImage(
"albedo", albedo, oidn::Format::Float3, width, height, 0, pixel_stride);
albedoFilter.setImage(
@@ -176,6 +181,7 @@ class DenoiseOperation : public NodeOperation {
if (should_denoise_auxiliary_passes()) {
oidn::FilterRef normalFilter = device.newFilter("RT");
this->set_filter_quality(normalFilter);
normalFilter.setImage(
"normal", normal, oidn::Format::Float3, width, height, 0, pixel_stride);
normalFilter.setImage(
@@ -246,6 +252,51 @@ class DenoiseOperation : public NodeOperation {
return static_cast<CMPNodeDenoisePrefilter>(node_storage(bnode()).prefilter);
}
#ifdef WITH_OPENIMAGEDENOISE
# if OIDN_VERSION_MAJOR >= 2
OIDNQuality get_quality()
{
const CMPNodeDenoiseQuality node_quality = static_cast<CMPNodeDenoiseQuality>(
node_storage(bnode()).quality);
if (node_quality == CMP_NODE_DENOISE_QUALITY_SCENE) {
const eCompositorDenoiseQaulity scene_quality = context().get_denoise_quality();
switch (scene_quality) {
# if OIDN_VERSION >= 20300
case SCE_COMPOSITOR_DENOISE_FAST:
return OIDN_QUALITY_FAST;
# endif
case SCE_COMPOSITOR_DENOISE_BALANCED:
return OIDN_QUALITY_BALANCED;
case SCE_COMPOSITOR_DENOISE_HIGH:
default:
return OIDN_QUALITY_HIGH;
}
}
switch (node_quality) {
# if OIDN_VERSION >= 20300
case CMP_NODE_DENOISE_QUALITY_FAST:
return OIDN_QUALITY_FAST;
# endif
case CMP_NODE_DENOISE_QUALITY_BALANCED:
return OIDN_QUALITY_BALANCED;
case CMP_NODE_DENOISE_QUALITY_HIGH:
default:
return OIDN_QUALITY_HIGH;
}
}
# endif /* OIDN_VERSION_MAJOR >= 2 */
void set_filter_quality([[maybe_unused]] oidn::FilterRef &filter)
{
# if OIDN_VERSION_MAJOR >= 2
OIDNQuality quality = this->get_quality();
filter.set("quality", quality);
# endif
}
#endif /* WITH_OPENIMAGEDENOISE */
/* OIDN can be disabled as a build option, so check WITH_OPENIMAGEDENOISE. Additionally, it is
* only supported at runtime for CPUs that supports SSE4.1, except for MacOS where it is always
* supported through the Accelerate framework BNNS on macOS. */

View File

@@ -207,6 +207,17 @@ class Context : public compositor::Context {
return this->get_render_data().compositor_device == SCE_COMPOSITOR_DEVICE_GPU;
}
eCompositorDenoiseQaulity get_denoise_quality() const override
{
if (this->render_context()) {
return static_cast<eCompositorDenoiseQaulity>(
this->get_render_data().compositor_denoise_final_quality);
}
return static_cast<eCompositorDenoiseQaulity>(
this->get_render_data().compositor_denoise_preview_quality);
}
bool use_file_output() const override
{
return this->render_context() != nullptr;