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:
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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;
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user