mirror of
https://github.com/blender/blender
synced 2026-09-27 01:34:15 +03:00
Cycles: Add NVIDIA DLSS support for viewport denoising
Adds the option to use DLSS Ray Reconstruction for viewport denoising in Cycles. For this to work, scheduling is adjusted to continuously reset samples (so that independent frames are rendered), pixel jitter is forced on and the required denoising passes (color, depth, diffuse albedo, specular albedo, normals, roughness, motion vectors, specular motion vectors) are enabled. DLSS expects those inputs in the form of CUDA textures, while Cycles keeps passes in an interleaved buffer layout. The data therefore has to be converted, for which specialized versions of the existing denoising filter kernels are introduced, which read/write directly to temporary CUDA textures that are managed in denoiser_dlss.cpp. The integration of DLSS itself is done in a similar fashion to OptiX: The DLSS SDK is pulled in for the type definitions, but the DLSS implementation is loaded by the NVIDIA driver installed on the system. Pull Request: https://projects.blender.org/blender/blender/pulls/153077
This commit is contained in:
parent
f8a67ec427
commit
e1fa0c2805
29 changed files with 1130 additions and 31 deletions
|
|
@ -311,6 +311,8 @@ option(WITH_MANIFOLD "Enable features depending on Manifold (Fast Robust Boolean
|
|||
|
||||
option(WITH_OPENIMAGEDENOISE "Enable the OpenImageDenoise compositing node" ON)
|
||||
|
||||
option(WITH_DLSS "Enable NVIDIA DLSS Ray Reconstruction support" OFF)
|
||||
|
||||
option(WITH_OPENSUBDIV "Enable OpenSubdiv for surface subdivision" ON)
|
||||
|
||||
option(WITH_POTRACE "Enable features relying on Potrace" ON)
|
||||
|
|
|
|||
19
build_files/cmake/Modules/FindDLSS.cmake
Normal file
19
build_files/cmake/Modules/FindDLSS.cmake
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
# SPDX-FileCopyrightText: 2026 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
# Find the DLSS SDK. This modules defines
|
||||
# DLSS_INCLUDE_DIR, where to find the NGX headers for DLSS
|
||||
# DLSS_FOUND, if the DLSS SDK is found.
|
||||
|
||||
find_path(DLSS_INCLUDE_DIR
|
||||
NAMES
|
||||
"nvsdk_ngx.h"
|
||||
PATHS
|
||||
"${DLSS_SDK_ROOT}/include"
|
||||
"$ENV{DLSS_SDK_ROOT}/include"
|
||||
)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
find_package_handle_standard_args(DLSS REQUIRED_VARS DLSS_INCLUDE_DIR)
|
||||
|
|
@ -311,6 +311,15 @@ if(WITH_OPENIMAGEDENOISE)
|
|||
add_definitions(-DWITH_OPENIMAGEDENOISE)
|
||||
endif()
|
||||
|
||||
if(WITH_DLSS)
|
||||
find_package(DLSS)
|
||||
if(DLSS_FOUND)
|
||||
add_definitions(-DWITH_DLSS)
|
||||
else()
|
||||
set_and_warn_library_found("DLSS" DLSS_FOUND WITH_DLSS)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WITH_OPENVDB)
|
||||
add_definitions(-DWITH_OPENVDB)
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -280,6 +280,15 @@ def enum_openimagedenoise_denoiser(self, context):
|
|||
return []
|
||||
|
||||
|
||||
def enum_dlss_denoiser(self, context):
|
||||
import _cycles
|
||||
if _cycles.with_dlss and (not context or bool(
|
||||
context.preferences.addons[__package__].preferences.get_devices_for_type('CUDA'))):
|
||||
return [('DLSS', "DLSS",
|
||||
n_("Use NVIDIA DLSS Ray Reconstruction"), 8)]
|
||||
return []
|
||||
|
||||
|
||||
def enum_optix_denoiser(self, context):
|
||||
if not context or bool(context.preferences.addons[__package__].preferences.get_devices_for_type('OPTIX')):
|
||||
return [('OPTIX', "OptiX", n_(
|
||||
|
|
@ -290,8 +299,9 @@ def enum_optix_denoiser(self, context):
|
|||
def enum_preview_denoiser(self, context):
|
||||
optix_items = enum_optix_denoiser(self, context)
|
||||
oidn_items = enum_openimagedenoise_denoiser(self, context)
|
||||
dlss_items = enum_dlss_denoiser(self, context)
|
||||
|
||||
if len(optix_items) or len(oidn_items):
|
||||
if len(optix_items) or len(oidn_items) or len(dlss_items):
|
||||
items = [
|
||||
('AUTO',
|
||||
"Automatic",
|
||||
|
|
@ -303,6 +313,7 @@ def enum_preview_denoiser(self, context):
|
|||
|
||||
items += optix_items
|
||||
items += oidn_items
|
||||
items += dlss_items
|
||||
return items
|
||||
|
||||
|
||||
|
|
@ -348,6 +359,28 @@ enum_denoising_quality = (
|
|||
"High performance",
|
||||
3),
|
||||
)
|
||||
enum_denoising_upscale_quality = (
|
||||
('NONE',
|
||||
"None",
|
||||
"Highest quality without upscaling",
|
||||
0),
|
||||
('QUALITY',
|
||||
"Quality",
|
||||
"Offers higher image quality than balanced mode",
|
||||
1),
|
||||
('BALANCED',
|
||||
"Balanced",
|
||||
"Offers both optimized performance and image quality",
|
||||
2),
|
||||
('PERFORMANCE',
|
||||
"Performance",
|
||||
"Offers a higher performance boost than balanced mode",
|
||||
3),
|
||||
('ULTRA_PERFORMANCE',
|
||||
"Ultra Performance",
|
||||
"Offers the highest performance boost",
|
||||
4),
|
||||
)
|
||||
|
||||
enum_direct_light_sampling_type = (
|
||||
('MULTIPLE_IMPORTANCE_SAMPLING',
|
||||
|
|
@ -490,6 +523,12 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
|
|||
description="Perform denoising on GPU devices configured in the system tab in the user preferences. This is significantly faster than on CPU, but requires additional GPU memory. When large scenes need more GPU memory, this option can be disabled",
|
||||
default=True,
|
||||
)
|
||||
preview_denoising_upscale_quality: EnumProperty(
|
||||
name="Viewport Denoising Upscale Quality",
|
||||
description="Overall upscale factor and denoising quality when using DLSS",
|
||||
items=enum_denoising_upscale_quality,
|
||||
default='BALANCED',
|
||||
)
|
||||
|
||||
samples: IntProperty(
|
||||
name="Samples",
|
||||
|
|
@ -1850,6 +1889,26 @@ class CyclesPreferences(bpy.types.AddonPreferences):
|
|||
|
||||
return False
|
||||
|
||||
def has_dlss_gpu_devices(self):
|
||||
compute_device_type = self.get_compute_device_type()
|
||||
|
||||
# We need non-CPU devices, used for rendering and supporting DLSS
|
||||
if compute_device_type != 'NONE':
|
||||
for device in self.get_device_list(compute_device_type):
|
||||
device_type = device[1]
|
||||
if device_type == 'CPU':
|
||||
continue
|
||||
|
||||
# Skip devices that do not meet the driver requirement.
|
||||
if not device[8]:
|
||||
continue
|
||||
|
||||
has_device_dlss_support = device[9]
|
||||
if has_device_dlss_support and self.find_existing_device_entry(device).use:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def has_optixdenoiser_gpu_devices(self):
|
||||
compute_device_type = self.get_compute_device_type()
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,9 @@ from bl_ui.properties_view_layer import (
|
|||
)
|
||||
|
||||
from bl_ui.properties_object import has_geometry_visibility
|
||||
from bpy.app.translations import (
|
||||
pgettext_rpt as rpt_,
|
||||
)
|
||||
|
||||
|
||||
class CyclesPresetPanel(PresetPanel, Panel):
|
||||
|
|
@ -151,6 +154,9 @@ def show_preview_denoise_active(context):
|
|||
if not cscene.use_preview_denoising:
|
||||
return False
|
||||
|
||||
if cscene.preview_denoiser == 'DLSS':
|
||||
return has_dlss_gpu_devices(context)
|
||||
|
||||
if cscene.preview_denoiser == 'OPTIX':
|
||||
return has_optixdenoiser_gpu_devices(context)
|
||||
|
||||
|
|
@ -190,6 +196,10 @@ def has_oidn_gpu_devices(context):
|
|||
return context.preferences.addons[__package__].preferences.has_oidn_gpu_devices()
|
||||
|
||||
|
||||
def has_dlss_gpu_devices(context):
|
||||
return context.preferences.addons[__package__].preferences.has_dlss_gpu_devices()
|
||||
|
||||
|
||||
def has_optixdenoiser_gpu_devices(context):
|
||||
return context.preferences.addons[__package__].preferences.has_optixdenoiser_gpu_devices()
|
||||
|
||||
|
|
@ -214,6 +224,8 @@ class CYCLES_RENDER_PT_sampling_viewport(CyclesButtonsPanel, Panel):
|
|||
scene = context.scene
|
||||
cscene = scene.cycles
|
||||
|
||||
layout.active = not (cscene.use_preview_denoising and cscene.preview_denoiser == 'DLSS')
|
||||
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
|
|
@ -224,12 +236,12 @@ class CYCLES_RENDER_PT_sampling_viewport(CyclesButtonsPanel, Panel):
|
|||
sub.active = cscene.use_preview_adaptive_sampling
|
||||
sub.prop(cscene, "preview_adaptive_threshold", text="")
|
||||
|
||||
col = layout.column(align=True)
|
||||
if cscene.use_preview_adaptive_sampling:
|
||||
col = layout.column(align=True)
|
||||
col.prop(cscene, "preview_samples", text="Max Samples")
|
||||
col.prop(cscene, "preview_adaptive_min_samples", text="Min Samples")
|
||||
else:
|
||||
layout.prop(cscene, "preview_samples", text="Samples")
|
||||
col.prop(cscene, "preview_samples", text="Samples")
|
||||
|
||||
|
||||
class CYCLES_RENDER_PT_sampling_viewport_denoise(CyclesButtonsPanel, Panel):
|
||||
|
|
@ -258,10 +270,21 @@ class CYCLES_RENDER_PT_sampling_viewport_denoise(CyclesButtonsPanel, Panel):
|
|||
sub.active = show_preview_denoise_active(context)
|
||||
sub.prop(cscene, "preview_denoiser", text="Denoiser")
|
||||
|
||||
col.prop(cscene, "preview_denoising_input_passes", text="Passes")
|
||||
|
||||
has_oidn_gpu = has_oidn_gpu_devices(context)
|
||||
effective_preview_denoiser = get_effective_preview_denoiser(context, has_oidn_gpu)
|
||||
|
||||
if effective_preview_denoiser == 'DLSS':
|
||||
if has_dlss_gpu_devices(context):
|
||||
col.prop(cscene, "preview_denoising_upscale_quality", text="Upscale Mode")
|
||||
else:
|
||||
col.label(text=rpt_("Requires NVIDIA GPU with compute capability %s") % "7.5",
|
||||
icon='INFO', translate=False)
|
||||
col.label(text=rpt_("and NVIDIA driver version %s or newer") % "590",
|
||||
icon='BLANK1', translate=False)
|
||||
return
|
||||
|
||||
col.prop(cscene, "preview_denoising_input_passes", text="Passes")
|
||||
|
||||
if effective_preview_denoiser == 'OPENIMAGEDENOISE':
|
||||
col.prop(cscene, "preview_denoising_prefilter", text="Prefilter")
|
||||
col.prop(cscene, "preview_denoising_quality", text="Quality")
|
||||
|
|
|
|||
|
|
@ -486,7 +486,7 @@ static PyObject *available_devices_func(PyObject * /*self*/, PyObject *args)
|
|||
for (size_t i = 0; i < devices.size(); i++) {
|
||||
const DeviceInfo &device = devices[i];
|
||||
const string type_name = Device::string_from_type(device.type);
|
||||
PyObject *device_tuple = PyTuple_New(9);
|
||||
PyObject *device_tuple = PyTuple_New(10);
|
||||
PyTuple_SET_ITEM(device_tuple, 0, pyunicode_from_string(device.description.c_str()));
|
||||
PyTuple_SET_ITEM(device_tuple, 1, pyunicode_from_string(type_name.c_str()));
|
||||
PyTuple_SET_ITEM(device_tuple, 2, pyunicode_from_string(device.id.c_str()));
|
||||
|
|
@ -497,6 +497,7 @@ static PyObject *available_devices_func(PyObject * /*self*/, PyObject *args)
|
|||
PyTuple_SET_ITEM(device_tuple, 6, PyBool_FromLong(device.denoisers & DENOISER_OPTIX));
|
||||
PyTuple_SET_ITEM(device_tuple, 7, PyBool_FromLong(device.has_execution_optimization));
|
||||
PyTuple_SET_ITEM(device_tuple, 8, PyBool_FromLong(device.meets_driver_requirement));
|
||||
PyTuple_SET_ITEM(device_tuple, 9, PyBool_FromLong(device.denoisers & DENOISER_DLSS));
|
||||
PyTuple_SET_ITEM(ret, i, device_tuple);
|
||||
}
|
||||
|
||||
|
|
@ -1007,6 +1008,12 @@ void *blender::CCL_python_module_init()
|
|||
PyModule_AddObjectRef(mod, "with_openimagedenoise", Py_False);
|
||||
}
|
||||
|
||||
#ifdef WITH_DLSS
|
||||
PyModule_AddObjectRef(mod, "with_dlss", Py_True);
|
||||
#else
|
||||
PyModule_AddObjectRef(mod, "with_dlss", Py_False);
|
||||
#endif
|
||||
|
||||
#ifdef WITH_CYCLES_DEBUG
|
||||
PyModule_AddObjectRef(mod, "with_debug", Py_True);
|
||||
#else /* WITH_CYCLES_DEBUG */
|
||||
|
|
|
|||
|
|
@ -1148,6 +1148,16 @@ DenoiseParams BlenderSync::get_denoise_params(blender::Scene &b_scene,
|
|||
DENOISER_INPUT_NUM,
|
||||
};
|
||||
|
||||
enum DenoiserUpscaleQuality {
|
||||
DENOISER_UPSCALE_NONE = 0,
|
||||
DENOISER_UPSCALE_QUALITY = 1,
|
||||
DENOISER_UPSCALE_BALANCED = 2,
|
||||
DENOISER_UPSCALE_PERFORMANCE = 3,
|
||||
DENOISER_UPSCALE_ULTRA_PERFORMANCE = 4,
|
||||
|
||||
DENOISER_UPSCALE_NUM,
|
||||
};
|
||||
|
||||
DenoiseParams denoising;
|
||||
blender::PointerRNA scene_rna_ptr = RNA_id_pointer_create(&b_scene.id);
|
||||
blender::PointerRNA cscene = RNA_pointer_get(&scene_rna_ptr, "cycles");
|
||||
|
|
@ -1198,6 +1208,48 @@ DenoiseParams BlenderSync::get_denoise_params(blender::Scene &b_scene,
|
|||
denoising.use = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (denoising.type == DENOISER_DLSS) {
|
||||
/* Disable denoising when DLSS is not supported. */
|
||||
if (!Denoiser::is_device_supported(denoising.type, denoise_device_info)) {
|
||||
denoising.use = false;
|
||||
}
|
||||
|
||||
denoising.start_sample = 0;
|
||||
|
||||
switch ((DenoiserUpscaleQuality)get_enum(cscene,
|
||||
"preview_denoising_upscale_quality",
|
||||
DENOISER_UPSCALE_NUM,
|
||||
DENOISER_UPSCALE_BALANCED))
|
||||
{
|
||||
case DENOISER_UPSCALE_NONE:
|
||||
denoising.quality = DENOISER_QUALITY_HIGH;
|
||||
denoising.upscale_factor = 1.0f;
|
||||
break;
|
||||
case DENOISER_UPSCALE_QUALITY:
|
||||
denoising.quality = DENOISER_QUALITY_HIGH;
|
||||
denoising.upscale_factor = 1.0f / 0.66666667f;
|
||||
break;
|
||||
default:
|
||||
case DENOISER_UPSCALE_BALANCED:
|
||||
denoising.quality = DENOISER_QUALITY_BALANCED;
|
||||
denoising.upscale_factor = 1.0f / 0.58f;
|
||||
break;
|
||||
case DENOISER_UPSCALE_PERFORMANCE:
|
||||
denoising.quality = DENOISER_QUALITY_FAST;
|
||||
denoising.upscale_factor = 2.0f;
|
||||
break;
|
||||
case DENOISER_UPSCALE_ULTRA_PERFORMANCE:
|
||||
denoising.quality = DENOISER_QUALITY_FAST;
|
||||
denoising.upscale_factor = 3.0f;
|
||||
break;
|
||||
}
|
||||
|
||||
denoising.passes = DENOISER_PASS_ALBEDO | DENOISER_PASS_SPECULAR_ALBEDO |
|
||||
DENOISER_PASS_NORMAL | DENOISER_PASS_ROUGHNESS | DENOISER_PASS_DEPTH |
|
||||
DENOISER_PASS_MOTION | DENOISER_PASS_SPECULAR_MOTION;
|
||||
return denoising;
|
||||
}
|
||||
}
|
||||
|
||||
switch (input_passes) {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#ifdef WITH_CUDA
|
||||
# include "device/cuda/device_impl.h"
|
||||
|
||||
# include "integrator/denoiser_dlss.h"
|
||||
# include "integrator/denoiser_oidn_gpu.h" // IWYU pragma: keep
|
||||
|
||||
# include "util/string.h"
|
||||
|
|
@ -178,6 +179,11 @@ void device_cuda_info(vector<DeviceInfo> &devices)
|
|||
info.denoisers |= DENOISER_OPENIMAGEDENOISE;
|
||||
}
|
||||
# endif
|
||||
# if defined(WITH_DLSS)
|
||||
if (DLSSDenoiser::is_device_supported(info)) {
|
||||
info.denoisers |= DENOISER_DLSS;
|
||||
}
|
||||
# endif
|
||||
|
||||
/* If device has a kernel timeout and no compute preemption, we assume
|
||||
* it is connected to a display and will freeze the display while doing
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ const char *denoiserTypeToHumanReadable(DenoiserType type)
|
|||
return "OptiX";
|
||||
case DENOISER_OPENIMAGEDENOISE:
|
||||
return "OpenImageDenoise";
|
||||
case DENOISER_DLSS:
|
||||
return "DLSS";
|
||||
|
||||
case DENOISER_NUM:
|
||||
case DENOISER_NONE:
|
||||
|
|
@ -29,7 +31,8 @@ const NodeEnum *DenoiseParams::get_type_enum()
|
|||
|
||||
if (type_enum.empty()) {
|
||||
type_enum.insert("optix", DENOISER_OPTIX);
|
||||
type_enum.insert("openimageio", DENOISER_OPENIMAGEDENOISE);
|
||||
type_enum.insert("openimagedenoise", DENOISER_OPENIMAGEDENOISE);
|
||||
type_enum.insert("dlss", DENOISER_DLSS);
|
||||
}
|
||||
|
||||
return &type_enum;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ CCL_NAMESPACE_BEGIN
|
|||
enum DenoiserType {
|
||||
DENOISER_OPTIX = 2,
|
||||
DENOISER_OPENIMAGEDENOISE = 4,
|
||||
DENOISER_DLSS = 8,
|
||||
DENOISER_NUM,
|
||||
|
||||
DENOISER_NONE = 0,
|
||||
|
|
|
|||
|
|
@ -162,12 +162,18 @@ const char *device_kernel_as_string(DeviceKernel kernel)
|
|||
/* Denoising. */
|
||||
case DEVICE_KERNEL_FILTER_GUIDING_PREPROCESS:
|
||||
return "filter_guiding_preprocess";
|
||||
case DEVICE_KERNEL_FILTER_GUIDING_PREPROCESS_TO_SURFACE:
|
||||
return "filter_guiding_preprocess_to_surface";
|
||||
case DEVICE_KERNEL_FILTER_GUIDING_SET_FAKE_ALBEDO:
|
||||
return "filter_guiding_set_fake_albedo";
|
||||
case DEVICE_KERNEL_FILTER_COLOR_PREPROCESS:
|
||||
return "filter_color_preprocess";
|
||||
case DEVICE_KERNEL_FILTER_COLOR_PREPROCESS_TO_SURFACE:
|
||||
return "filter_color_preprocess_to_surface";
|
||||
case DEVICE_KERNEL_FILTER_COLOR_POSTPROCESS:
|
||||
return "filter_color_postprocess";
|
||||
case DEVICE_KERNEL_FILTER_COLOR_POSTPROCESS_FROM_SURFACE:
|
||||
return "filter_color_postprocess_from_surface";
|
||||
case DEVICE_KERNEL_FILTER_COLOR_FLIP_Y:
|
||||
return "filter_color_flip_y";
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ struct DeviceKernelArguments {
|
|||
HIPRT_GLOBAL_STACK,
|
||||
};
|
||||
|
||||
static const int MAX_ARGS = 19;
|
||||
static const int MAX_ARGS = 23;
|
||||
Type types[MAX_ARGS];
|
||||
void *values[MAX_ARGS];
|
||||
size_t sizes[MAX_ARGS];
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ set(INC_SYS
|
|||
set(SRC
|
||||
adaptive_sampling.cpp
|
||||
denoiser.cpp
|
||||
denoiser_dlss.cpp
|
||||
denoiser_gpu.cpp
|
||||
denoiser_oidn.cpp
|
||||
denoiser_oidn_base.cpp
|
||||
|
|
@ -36,6 +37,7 @@ set(SRC
|
|||
set(SRC_HEADERS
|
||||
adaptive_sampling.h
|
||||
denoiser.h
|
||||
denoiser_dlss.h
|
||||
denoiser_gpu.h
|
||||
denoiser_oidn.h
|
||||
denoiser_oidn_base.h
|
||||
|
|
@ -70,6 +72,12 @@ set(LIB
|
|||
PRIVATE bf::dependencies::optional::openpgl
|
||||
)
|
||||
|
||||
if(WITH_DLSS)
|
||||
list(APPEND INC_SYS
|
||||
${DLSS_INCLUDE_DIR}
|
||||
)
|
||||
endif()
|
||||
|
||||
include_directories(${INC})
|
||||
include_directories(SYSTEM ${INC_SYS})
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "device/device.h"
|
||||
|
||||
#include "integrator/denoiser_dlss.h"
|
||||
#include "integrator/denoiser_oidn.h"
|
||||
#include "session/display_driver.h"
|
||||
#ifdef WITH_OPENIMAGEDENOISE
|
||||
|
|
@ -102,6 +103,18 @@ bool use_gpu_oidn_denoiser(Device *denoiser_device, const DenoiseParams ¶ms)
|
|||
#endif
|
||||
}
|
||||
|
||||
bool use_dlss_denoiser(Device *denoiser_device, const DenoiseParams ¶ms)
|
||||
{
|
||||
#ifdef WITH_DLSS
|
||||
return (params.type == DENOISER_DLSS &&
|
||||
DLSSDenoiser::is_device_supported(denoiser_device->info));
|
||||
#else
|
||||
(void)denoiser_device;
|
||||
(void)params;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
DenoiseParams get_effective_denoise_params(Device *denoiser_device,
|
||||
Device *cpu_fallback_device,
|
||||
const DenoiseParams ¶ms,
|
||||
|
|
@ -131,7 +144,8 @@ DenoiseParams get_effective_denoise_params(Device *denoiser_device,
|
|||
const bool is_cpu_denoiser_device = single_denoiser_device->info.type == DEVICE_CPU;
|
||||
if (is_cpu_denoiser_device == false) {
|
||||
if (use_optix_denoiser(single_denoiser_device, effective_denoise_params) ||
|
||||
use_gpu_oidn_denoiser(single_denoiser_device, effective_denoise_params))
|
||||
use_gpu_oidn_denoiser(single_denoiser_device, effective_denoise_params) ||
|
||||
use_dlss_denoiser(single_denoiser_device, effective_denoise_params))
|
||||
{
|
||||
/* Denoising parameters are correct and there is no need to fall back to CPU OIDN. */
|
||||
return effective_denoise_params;
|
||||
|
|
@ -170,6 +184,12 @@ unique_ptr<Denoiser> Denoiser::create(Device *denoiser_device,
|
|||
return make_unique<OIDNDenoiserGPU>(single_denoiser_device, effective_denoiser_params);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef WITH_DLSS
|
||||
if (use_dlss_denoiser(single_denoiser_device, effective_denoiser_params)) {
|
||||
return make_unique<DLSSDenoiser>(single_denoiser_device, effective_denoiser_params);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!openimagedenoise_supported()) {
|
||||
|
|
@ -182,6 +202,26 @@ unique_ptr<Denoiser> Denoiser::create(Device *denoiser_device,
|
|||
effective_denoiser_params);
|
||||
}
|
||||
|
||||
bool Denoiser::is_device_supported(DenoiserType type, const DeviceInfo &denoise_device_info)
|
||||
{
|
||||
switch (type) {
|
||||
#ifdef WITH_OPTIX
|
||||
case DENOISER_OPTIX:
|
||||
return OptiXDenoiser::is_device_supported(denoise_device_info);
|
||||
#endif
|
||||
#ifdef WITH_OPENIMAGEDENOISE
|
||||
case DENOISER_OPENIMAGEDENOISE:
|
||||
return OIDNDenoiserGPU::is_device_supported(denoise_device_info);
|
||||
#endif
|
||||
#ifdef WITH_DLSS
|
||||
case DENOISER_DLSS:
|
||||
return DLSSDenoiser::is_device_supported(denoise_device_info);
|
||||
#endif
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
DenoiserType Denoiser::automatic_viewport_denoiser_type(const DeviceInfo &denoise_device_info)
|
||||
{
|
||||
#ifdef WITH_OPENIMAGEDENOISE
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ bool use_optix_denoiser(Device *denoiser_device, const DenoiseParams ¶ms);
|
|||
|
||||
bool use_gpu_oidn_denoiser(Device *denoiser_device, const DenoiseParams ¶ms);
|
||||
|
||||
bool use_dlss_denoiser(Device *denoiser_device, const DenoiseParams ¶ms);
|
||||
|
||||
DenoiseParams get_effective_denoise_params(Device *denoiser_device,
|
||||
Device *cpu_fallback_device,
|
||||
const DenoiseParams ¶ms,
|
||||
|
|
@ -58,6 +60,8 @@ class Denoiser {
|
|||
void set_params(const DenoiseParams ¶ms);
|
||||
const DenoiseParams &get_params() const;
|
||||
|
||||
static bool is_device_supported(DenoiserType type, const DeviceInfo &denoise_device_info);
|
||||
|
||||
/* Recommended type for viewport denoising. */
|
||||
static DenoiserType automatic_viewport_denoiser_type(const DeviceInfo &denoise_device_info);
|
||||
|
||||
|
|
|
|||
453
intern/cycles/integrator/denoiser_dlss.cpp
Normal file
453
intern/cycles/integrator/denoiser_dlss.cpp
Normal file
|
|
@ -0,0 +1,453 @@
|
|||
/* SPDX-FileCopyrightText: 2025 NVIDIA Corporation
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifdef WITH_DLSS
|
||||
|
||||
# include "integrator/denoiser_dlss.h"
|
||||
# include "integrator/pass_accessor_gpu.h"
|
||||
|
||||
# include "device/cuda/device_impl.h"
|
||||
|
||||
# include "util/path.h"
|
||||
|
||||
# define NVSDK_NGX_HEADER_ONLY
|
||||
# include <nvsdk_ngx.h>
|
||||
# include <nvsdk_ngx_defs.h>
|
||||
# include <nvsdk_ngx_defs_dlssd.h>
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Application ID for Blender from NVIDIA. */
|
||||
static const int NGX_APPLICATION_ID = 100334311;
|
||||
|
||||
void DLSSDenoiser::CUDATexture::init(Device *device, int width, int height, int num_components)
|
||||
{
|
||||
CUDA_ARRAY_DESCRIPTOR desc = {};
|
||||
desc.Width = width;
|
||||
desc.Height = height;
|
||||
desc.Format = CU_AD_FORMAT_FLOAT;
|
||||
desc.NumChannels = num_components;
|
||||
|
||||
cuda_device_assert(device, cuArrayCreate((CUarray *)&array, &desc));
|
||||
|
||||
CUDA_TEXTURE_DESC tex_desc = {};
|
||||
tex_desc.addressMode[0] = CU_TR_ADDRESS_MODE_CLAMP;
|
||||
tex_desc.addressMode[1] = CU_TR_ADDRESS_MODE_CLAMP;
|
||||
tex_desc.addressMode[2] = CU_TR_ADDRESS_MODE_CLAMP;
|
||||
tex_desc.flags = CU_TRSF_NORMALIZED_COORDINATES;
|
||||
|
||||
CUDA_RESOURCE_DESC res_desc = {};
|
||||
res_desc.resType = CU_RESOURCE_TYPE_ARRAY;
|
||||
res_desc.res.array.hArray = (CUarray)array;
|
||||
|
||||
cuda_device_assert(
|
||||
device, cuTexObjectCreate((CUtexObject *)&texture_handle, &res_desc, &tex_desc, nullptr));
|
||||
cuda_device_assert(device, cuSurfObjectCreate((CUsurfObject *)&surface_handle, &res_desc));
|
||||
}
|
||||
void DLSSDenoiser::CUDATexture::destroy()
|
||||
{
|
||||
cuSurfObjectDestroy((CUsurfObject)surface_handle);
|
||||
surface_handle = 0;
|
||||
cuTexObjectDestroy((CUtexObject)texture_handle);
|
||||
texture_handle = 0;
|
||||
|
||||
cuArrayDestroy((CUarray)array);
|
||||
array = 0;
|
||||
}
|
||||
|
||||
DLSSDenoiser::DLSSDenoiser(Device *denoiser_device, const DenoiseParams ¶ms)
|
||||
: DenoiserGPU(denoiser_device, params)
|
||||
{
|
||||
CUDADevice *const cuda_device = static_cast<CUDADevice *>(denoiser_device_);
|
||||
const CUDAContextScope scope(cuda_device);
|
||||
|
||||
/* Path to optionally search for features, in addition to the executable directory and driver. */
|
||||
const wstring app_path = string_to_wstring(path_get());
|
||||
/* Path to write NGX logs to. */
|
||||
const wstring app_data_path = string_to_wstring(path_cache_get());
|
||||
|
||||
const wchar_t *const app_paths[] = {app_path.c_str()};
|
||||
|
||||
NVSDK_NGX_FeatureCommonInfo feature_info = {};
|
||||
feature_info.PathListInfo.Path = app_paths;
|
||||
feature_info.PathListInfo.Length = 1;
|
||||
feature_info.LoggingInfo.LoggingCallback =
|
||||
[](const char *message, NVSDK_NGX_Logging_Level loggingLevel, NVSDK_NGX_Feature) {
|
||||
switch (loggingLevel) {
|
||||
case NVSDK_NGX_LOGGING_LEVEL_OFF:
|
||||
case NVSDK_NGX_LOGGING_LEVEL_NUM:
|
||||
assert(false);
|
||||
break;
|
||||
case NVSDK_NGX_LOGGING_LEVEL_ON:
|
||||
LOG_INFO << message;
|
||||
break;
|
||||
case NVSDK_NGX_LOGGING_LEVEL_VERBOSE:
|
||||
LOG_INFO << message;
|
||||
break;
|
||||
}
|
||||
};
|
||||
feature_info.LoggingInfo.MinimumLoggingLevel = NVSDK_NGX_LOGGING_LEVEL_ON;
|
||||
|
||||
/* Create a fixed handle for this particular CUDA context and queue combination, since NGX uses
|
||||
* it for lookup internally. */
|
||||
ngx_device_ = new NVSDK_NGX_CUDADevice{
|
||||
cuda_device->cuContext, static_cast<CUDADeviceQueue *>(denoiser_queue_.get())->stream()};
|
||||
|
||||
const NVSDK_NGX_Result result = NVSDK_NGX_CUDA_Init1(
|
||||
NGX_APPLICATION_ID, app_data_path.c_str(), ngx_device_, &feature_info);
|
||||
|
||||
if (result == NVSDK_NGX_Result_FAIL_FeatureNotSupported) {
|
||||
delete ngx_device_;
|
||||
ngx_device_ = nullptr;
|
||||
set_error("Failed to load NGX driver");
|
||||
}
|
||||
else if (NVSDK_NGX_FAILED(result)) {
|
||||
set_error("Failed to initialize NGX driver");
|
||||
}
|
||||
}
|
||||
|
||||
DLSSDenoiser::~DLSSDenoiser()
|
||||
{
|
||||
CUDADevice *const cuda_device = static_cast<CUDADevice *>(denoiser_device_);
|
||||
const CUDAContextScope scope(cuda_device);
|
||||
|
||||
tex_color_.destroy();
|
||||
tex_depth_.destroy();
|
||||
tex_diffuse_albedo_.destroy();
|
||||
tex_specular_albedo_.destroy();
|
||||
tex_normal_roughness_.destroy();
|
||||
tex_motion_.destroy();
|
||||
tex_specular_motion_.destroy();
|
||||
tex_output_.destroy();
|
||||
|
||||
if (ngx_device_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (handle_ != nullptr) {
|
||||
NVSDK_NGX_CUDA_ReleaseFeature(handle_);
|
||||
}
|
||||
|
||||
const NVSDK_NGX_Result result = NVSDK_NGX_CUDA_Shutdown1(ngx_device_);
|
||||
|
||||
if (NVSDK_NGX_FAILED(result)) {
|
||||
set_error("Failed to shutdown NGX driver");
|
||||
}
|
||||
|
||||
delete ngx_device_;
|
||||
}
|
||||
|
||||
bool DLSSDenoiser::is_device_supported(const DeviceInfo &device)
|
||||
{
|
||||
if (device.type != DEVICE_CUDA && device.type != DEVICE_OPTIX) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* 'NVSDK_NGX_CUDA_GetFeatureRequirements' is an expensive call, so cache the result (since
|
||||
* 'is_device_supported' is called a lot). */
|
||||
static ccl::map<int, NVSDK_NGX_Feature_Support_Result> supported_cache;
|
||||
if (const auto it = supported_cache.find(device.num); it != supported_cache.end()) {
|
||||
return it->second == NVSDK_NGX_FeatureSupportResult_Supported;
|
||||
}
|
||||
|
||||
/* Path to write NGX logs to. */
|
||||
const wstring app_data_path = string_to_wstring(path_cache_get());
|
||||
|
||||
CUdevice cuDevice = 0;
|
||||
cuDeviceGet(&cuDevice, device.num);
|
||||
|
||||
/* Query driver whether the DLSS-RR feature is supported on this particular device. */
|
||||
NVSDK_NGX_FeatureDiscoveryInfo discovery_info = {};
|
||||
discovery_info.SDKVersion = NVSDK_NGX_Version_API;
|
||||
discovery_info.FeatureID = NVSDK_NGX_Feature_RayReconstruction;
|
||||
discovery_info.Identifier.IdentifierType = NVSDK_NGX_Application_Identifier_Type_Application_Id;
|
||||
discovery_info.Identifier.v.ApplicationId = NGX_APPLICATION_ID;
|
||||
discovery_info.ApplicationDataPath = app_data_path.c_str();
|
||||
|
||||
NVSDK_NGX_FeatureRequirement requirement = {NVSDK_NGX_FeatureSupportResult_Supported};
|
||||
|
||||
const NVSDK_NGX_Result result = NVSDK_NGX_CUDA_GetFeatureRequirements(
|
||||
cuDevice, &discovery_info, &requirement);
|
||||
|
||||
if (NVSDK_NGX_SUCCEED(result)) {
|
||||
supported_cache.emplace(device.num, requirement.FeatureSupported);
|
||||
return requirement.FeatureSupported == NVSDK_NGX_FeatureSupportResult_Supported;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool DLSSDenoiser::denoise_create_if_needed(DenoiseContext &context)
|
||||
{
|
||||
const bool recreate_denoiser = last_width_ != context.denoised_buffer_params.width ||
|
||||
last_height_ != context.denoised_buffer_params.height ||
|
||||
last_upscale_factor_ != context.denoise_params.upscale_factor;
|
||||
if (handle_ != nullptr && !recreate_denoiser) {
|
||||
return true;
|
||||
}
|
||||
|
||||
CUDADevice *const cuda_device = static_cast<CUDADevice *>(denoiser_device_);
|
||||
const CUDAContextScope scope(cuda_device);
|
||||
|
||||
if (handle_ != nullptr) {
|
||||
denoiser_queue_->synchronize();
|
||||
|
||||
NVSDK_NGX_CUDA_ReleaseFeature(handle_);
|
||||
handle_ = nullptr;
|
||||
}
|
||||
|
||||
tex_color_.destroy();
|
||||
tex_depth_.destroy();
|
||||
tex_diffuse_albedo_.destroy();
|
||||
tex_specular_albedo_.destroy();
|
||||
tex_normal_roughness_.destroy();
|
||||
tex_motion_.destroy();
|
||||
tex_specular_motion_.destroy();
|
||||
tex_output_.destroy();
|
||||
|
||||
/* Feature creation fails below these dimensions.
|
||||
* Avoid hard error that stops rendering and only disable denoising for very small viewports. */
|
||||
if (context.denoised_buffer_params.width < 32 || context.denoised_buffer_params.height < 32) {
|
||||
last_width_ = 0;
|
||||
last_height_ = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
NVSDK_NGX_Parameter *params = nullptr;
|
||||
if (NVSDK_NGX_FAILED(NVSDK_NGX_CUDA_AllocateParameters(¶ms))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Input params (with resolution divider applied). */
|
||||
params->Set(NVSDK_NGX_Parameter_Width, context.buffer_params.width);
|
||||
params->Set(NVSDK_NGX_Parameter_Height, context.buffer_params.height);
|
||||
/* Output params. */
|
||||
params->Set(NVSDK_NGX_Parameter_OutWidth, context.denoised_buffer_params.width);
|
||||
params->Set(NVSDK_NGX_Parameter_OutHeight, context.denoised_buffer_params.height);
|
||||
|
||||
/* Usually the DLSS quality mode is set first and then DLSS should be queried for the optimal
|
||||
* upscale factor to go along with it. In Cycles the upscale factor is already determined before
|
||||
* the denoiser is initialized though, so we do it backwards and instead try to find a
|
||||
* reasonable quality mode to match the denoiser settings here. */
|
||||
NVSDK_NGX_PerfQuality_Value perf_quality_value = NVSDK_NGX_PerfQuality_Value_DLAA;
|
||||
if (context.denoise_params.upscale_factor > 1) {
|
||||
switch (context.denoise_params.quality) {
|
||||
default:
|
||||
case DENOISER_QUALITY_HIGH:
|
||||
perf_quality_value = NVSDK_NGX_PerfQuality_Value_MaxQuality;
|
||||
break;
|
||||
case DENOISER_QUALITY_BALANCED:
|
||||
perf_quality_value = NVSDK_NGX_PerfQuality_Value_Balanced;
|
||||
break;
|
||||
case DENOISER_QUALITY_FAST:
|
||||
perf_quality_value = NVSDK_NGX_PerfQuality_Value_MaxPerf;
|
||||
if (context.denoise_params.upscale_factor >= 3) {
|
||||
perf_quality_value = NVSDK_NGX_PerfQuality_Value_UltraPerformance;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
params->Set(NVSDK_NGX_Parameter_PerfQualityValue, perf_quality_value);
|
||||
|
||||
params->Set(NVSDK_NGX_Parameter_DLSS_Denoise_Mode, NVSDK_NGX_DLSS_Denoise_Mode_DLUnified);
|
||||
params->Set(NVSDK_NGX_Parameter_DLSS_Feature_Create_Flags,
|
||||
NVSDK_NGX_DLSS_Feature_Flags_IsHDR | NVSDK_NGX_DLSS_Feature_Flags_MVLowRes);
|
||||
params->Set(NVSDK_NGX_Parameter_DLSS_Enable_Output_Subrects, 0);
|
||||
params->Set(NVSDK_NGX_Parameter_Use_HW_Depth, NVSDK_NGX_DLSS_Depth_Type_Linear);
|
||||
/* Normals and roughness are packed into one texture in 'denoise_filter_guiding_preprocess'. */
|
||||
params->Set(NVSDK_NGX_Parameter_DLSS_Roughness_Mode, NVSDK_NGX_DLSS_Roughness_Mode_Packed);
|
||||
|
||||
const NVSDK_NGX_Result result = NVSDK_NGX_CUDA_CreateFeature1(
|
||||
ngx_device_, NVSDK_NGX_Feature_RayReconstruction, params, &handle_);
|
||||
|
||||
NVSDK_NGX_CUDA_DestroyParameters(params);
|
||||
|
||||
if (NVSDK_NGX_FAILED(result)) {
|
||||
set_error("Failed to create DLSS instance");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* DLSS requires inputs and outputs in separate CUDA textures/surfaces, while Cycles stores them
|
||||
* in an interleaved buffer, so need to create these temporary textures and convert between the
|
||||
* two storage variants (in 'denoise_filter_color_preprocess',
|
||||
* 'denoise_filter_guiding_preprocess' and 'denoise_filter_color_postprocess'). */
|
||||
tex_color_.init(cuda_device, context.buffer_params.width, context.buffer_params.height, 4);
|
||||
tex_depth_.init(cuda_device, context.buffer_params.width, context.buffer_params.height, 1);
|
||||
tex_diffuse_albedo_.init(
|
||||
cuda_device, context.buffer_params.width, context.buffer_params.height, 4);
|
||||
tex_specular_albedo_.init(
|
||||
cuda_device, context.buffer_params.width, context.buffer_params.height, 4);
|
||||
tex_normal_roughness_.init(
|
||||
cuda_device, context.buffer_params.width, context.buffer_params.height, 4);
|
||||
tex_motion_.init(cuda_device, context.buffer_params.width, context.buffer_params.height, 2);
|
||||
tex_specular_motion_.init(
|
||||
cuda_device, context.buffer_params.width, context.buffer_params.height, 2);
|
||||
|
||||
tex_output_.init(
|
||||
cuda_device, context.denoised_buffer_params.width, context.denoised_buffer_params.height, 4);
|
||||
|
||||
last_width_ = context.denoised_buffer_params.width;
|
||||
last_height_ = context.denoised_buffer_params.height;
|
||||
last_upscale_factor_ = context.denoise_params.upscale_factor;
|
||||
|
||||
return !cuda_device->have_error();
|
||||
}
|
||||
|
||||
bool DLSSDenoiser::denoise_configure_if_needed(DenoiseContext & /*context*/)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DLSSDenoiser::denoise_filter_color_preprocess(const DenoiseContext &context,
|
||||
const DenoisePass &pass)
|
||||
{
|
||||
if (pass.type != PASS_COMBINED) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Input params (with resolution divider applied). */
|
||||
const BufferParams &buffer_params = context.buffer_params;
|
||||
|
||||
const int work_size = buffer_params.width * buffer_params.height;
|
||||
|
||||
const DeviceKernelArguments args(&tex_color_.surface_handle,
|
||||
&context.render_buffers->buffer.device_pointer,
|
||||
&buffer_params.full_x,
|
||||
&buffer_params.full_y,
|
||||
&buffer_params.width,
|
||||
&buffer_params.height,
|
||||
&buffer_params.offset,
|
||||
&buffer_params.stride,
|
||||
&buffer_params.pass_stride,
|
||||
&pass.denoised_offset);
|
||||
|
||||
return denoiser_queue_->enqueue(
|
||||
DEVICE_KERNEL_FILTER_COLOR_PREPROCESS_TO_SURFACE, work_size, args);
|
||||
}
|
||||
bool DLSSDenoiser::denoise_filter_color_postprocess(const DenoiseContext &context,
|
||||
const DenoisePass &pass)
|
||||
{
|
||||
if (pass.type != PASS_COMBINED) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Output params. */
|
||||
const BufferParams &buffer_params = context.denoised_buffer_params;
|
||||
|
||||
const int work_size = buffer_params.width * buffer_params.height;
|
||||
|
||||
const DeviceKernelArguments args(&tex_output_.surface_handle,
|
||||
&context.render_buffers->buffer.device_pointer,
|
||||
&buffer_params.full_x,
|
||||
&buffer_params.full_y,
|
||||
&buffer_params.width,
|
||||
&buffer_params.height,
|
||||
&buffer_params.offset,
|
||||
&buffer_params.stride,
|
||||
&context.buffer_params.full_x,
|
||||
&context.buffer_params.full_y,
|
||||
&context.buffer_params.offset,
|
||||
&context.buffer_params.stride,
|
||||
&buffer_params.pass_stride,
|
||||
&context.num_samples,
|
||||
&pass.noisy_offset,
|
||||
&pass.denoised_offset,
|
||||
&context.pass_sample_count,
|
||||
&pass.num_components,
|
||||
&pass.use_compositing,
|
||||
¶ms_.upscale_factor);
|
||||
|
||||
return denoiser_queue_->enqueue(
|
||||
DEVICE_KERNEL_FILTER_COLOR_POSTPROCESS_FROM_SURFACE, work_size, args);
|
||||
}
|
||||
|
||||
bool DLSSDenoiser::denoise_filter_guiding_preprocess(DenoiseContext &context)
|
||||
{
|
||||
const BufferParams &buffer_params = context.buffer_params;
|
||||
|
||||
const int work_size = buffer_params.width * buffer_params.height;
|
||||
|
||||
const int pass_depth = context.buffer_params.get_pass_offset(PASS_DENOISING_DEPTH);
|
||||
const int pass_specular_albedo = context.buffer_params.get_pass_offset(
|
||||
PASS_DENOISING_SPECULAR_ALBEDO);
|
||||
const int pass_roughness = context.buffer_params.get_pass_offset(PASS_DENOISING_ROUGHNESS);
|
||||
const int pass_specular_motion = context.buffer_params.get_pass_offset(
|
||||
PASS_DENOISING_SPECULAR_MOTION);
|
||||
|
||||
const DeviceKernelArguments args(&tex_depth_.surface_handle,
|
||||
&tex_diffuse_albedo_.surface_handle,
|
||||
&tex_specular_albedo_.surface_handle,
|
||||
&tex_normal_roughness_.surface_handle,
|
||||
&tex_motion_.surface_handle,
|
||||
&tex_specular_motion_.surface_handle,
|
||||
&context.render_buffers->buffer.device_pointer,
|
||||
&buffer_params.offset,
|
||||
&buffer_params.stride,
|
||||
&buffer_params.pass_stride,
|
||||
&context.pass_sample_count,
|
||||
&pass_depth,
|
||||
&context.pass_denoising_albedo,
|
||||
&pass_specular_albedo,
|
||||
&context.pass_denoising_normal,
|
||||
&pass_roughness,
|
||||
&context.pass_motion,
|
||||
&pass_specular_motion,
|
||||
&buffer_params.full_x,
|
||||
&buffer_params.full_y,
|
||||
&buffer_params.width,
|
||||
&buffer_params.height,
|
||||
&context.num_samples);
|
||||
|
||||
return denoiser_queue_->enqueue(
|
||||
DEVICE_KERNEL_FILTER_GUIDING_PREPROCESS_TO_SURFACE, work_size, args);
|
||||
}
|
||||
|
||||
bool DLSSDenoiser::denoise_run(const DenoiseContext &context, const DenoisePass &pass)
|
||||
{
|
||||
if (pass.type != PASS_COMBINED) {
|
||||
return true;
|
||||
}
|
||||
|
||||
NVSDK_NGX_Parameter *params = nullptr;
|
||||
if (NVSDK_NGX_FAILED(NVSDK_NGX_CUDA_AllocateParameters(¶ms))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CUDADevice *const cuda_device = static_cast<CUDADevice *>(denoiser_device_);
|
||||
const CUDAContextScope scope(cuda_device);
|
||||
|
||||
params->Set(NVSDK_NGX_Parameter_Reset, 0);
|
||||
|
||||
params->Set(NVSDK_NGX_Parameter_Jitter_Offset_X, context.pixel_jitter.x);
|
||||
params->Set(NVSDK_NGX_Parameter_Jitter_Offset_Y, context.pixel_jitter.y);
|
||||
|
||||
params->Set(NVSDK_NGX_Parameter_Color, &tex_color_.texture_handle);
|
||||
params->Set(NVSDK_NGX_Parameter_Depth, &tex_depth_.texture_handle);
|
||||
params->Set(NVSDK_NGX_Parameter_DiffuseAlbedo, &tex_diffuse_albedo_.texture_handle);
|
||||
params->Set(NVSDK_NGX_Parameter_SpecularAlbedo, &tex_specular_albedo_.texture_handle);
|
||||
params->Set(NVSDK_NGX_Parameter_GBuffer_Normals, &tex_normal_roughness_.texture_handle);
|
||||
params->Set(NVSDK_NGX_Parameter_GBuffer_Roughness, &tex_normal_roughness_.texture_handle);
|
||||
params->Set(NVSDK_NGX_Parameter_MotionVectors, &tex_motion_.texture_handle);
|
||||
params->Set(NVSDK_NGX_Parameter_GBuffer_SpecularMvec, &tex_specular_motion_.texture_handle);
|
||||
params->Set(NVSDK_NGX_Parameter_Output, &tex_output_.surface_handle);
|
||||
|
||||
params->Set(NVSDK_NGX_Parameter_DLSS_Render_Subrect_Dimensions_Width,
|
||||
context.buffer_params.width);
|
||||
params->Set(NVSDK_NGX_Parameter_DLSS_Render_Subrect_Dimensions_Height,
|
||||
context.buffer_params.height);
|
||||
|
||||
params->Set(NVSDK_NGX_Parameter_DLSS_Indicator_Invert_Y_Axis, 1);
|
||||
|
||||
const NVSDK_NGX_Result result = NVSDK_NGX_CUDA_EvaluateFeature(handle_, params, nullptr);
|
||||
|
||||
NVSDK_NGX_CUDA_DestroyParameters(params);
|
||||
|
||||
return NVSDK_NGX_SUCCEED(result);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
66
intern/cycles/integrator/denoiser_dlss.h
Normal file
66
intern/cycles/integrator/denoiser_dlss.h
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
/* SPDX-FileCopyrightText: 2025 NVIDIA Corporation
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef WITH_DLSS
|
||||
|
||||
# include "integrator/denoiser_gpu.h"
|
||||
|
||||
struct NVSDK_NGX_Handle;
|
||||
struct NVSDK_NGX_CUDADevice;
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Implementation of denoising API which uses DLSS. */
|
||||
class DLSSDenoiser : public DenoiserGPU {
|
||||
public:
|
||||
DLSSDenoiser(Device *denoiser_device, const DenoiseParams ¶ms);
|
||||
~DLSSDenoiser();
|
||||
|
||||
static bool is_device_supported(const DeviceInfo &device);
|
||||
|
||||
private:
|
||||
bool denoise_create_if_needed(DenoiseContext &context) override;
|
||||
|
||||
bool denoise_configure_if_needed(DenoiseContext &context) override;
|
||||
|
||||
bool denoise_filter_color_preprocess(const DenoiseContext &context,
|
||||
const DenoisePass &pass) override;
|
||||
bool denoise_filter_color_postprocess(const DenoiseContext &context,
|
||||
const DenoisePass &pass) override;
|
||||
|
||||
bool denoise_filter_guiding_preprocess(DenoiseContext &context) override;
|
||||
|
||||
bool denoise_run(const DenoiseContext &context, const DenoisePass &pass) override;
|
||||
|
||||
NVSDK_NGX_Handle *handle_ = nullptr;
|
||||
NVSDK_NGX_CUDADevice *ngx_device_ = nullptr;
|
||||
|
||||
struct CUDATexture {
|
||||
void init(Device *device, int width, int height, int num_components);
|
||||
void destroy();
|
||||
|
||||
void *array = nullptr;
|
||||
uint64_t texture_handle = 0;
|
||||
uint64_t surface_handle = 0;
|
||||
};
|
||||
CUDATexture tex_color_;
|
||||
CUDATexture tex_depth_;
|
||||
CUDATexture tex_diffuse_albedo_;
|
||||
CUDATexture tex_specular_albedo_;
|
||||
CUDATexture tex_normal_roughness_;
|
||||
CUDATexture tex_motion_;
|
||||
CUDATexture tex_specular_motion_;
|
||||
CUDATexture tex_output_;
|
||||
|
||||
int last_width_ = 0;
|
||||
int last_height_ = 0;
|
||||
float last_upscale_factor_ = 0.0f;
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
|
|
@ -134,10 +134,18 @@ bool DenoiserGPU::denoise_ensure(DenoiseContext &context)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool DenoiserGPU::denoise_filter_guiding_preprocess(const DenoiseContext &context)
|
||||
bool DenoiserGPU::denoise_filter_guiding_preprocess(DenoiseContext &context)
|
||||
{
|
||||
const BufferParams &buffer_params = context.buffer_params;
|
||||
|
||||
/* Delay the allocation of the guiding buffer to first use, in case it's not actually needed
|
||||
* (e.g. with the DLSS denoiser, which overrides this implementation). */
|
||||
if (context.use_guiding_passes && !context.guiding_params.device_pointer) {
|
||||
context.guiding_buffer.alloc_to_device(buffer_params.width * buffer_params.height *
|
||||
context.guiding_params.pass_stride);
|
||||
context.guiding_params.device_pointer = context.guiding_buffer.device_pointer;
|
||||
}
|
||||
|
||||
const int work_size = buffer_params.width * buffer_params.height;
|
||||
|
||||
const DeviceKernelArguments args(&context.guiding_params.device_pointer,
|
||||
|
|
@ -226,10 +234,6 @@ DenoiserGPU::DenoiseContext::DenoiseContext(Device *device,
|
|||
}
|
||||
|
||||
guiding_params.stride = buffer_params.width;
|
||||
|
||||
guiding_buffer.alloc_to_device(buffer_params.width * buffer_params.height *
|
||||
guiding_params.pass_stride);
|
||||
guiding_params.device_pointer = guiding_buffer.device_pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -361,10 +365,16 @@ bool DenoiserGPU::denoise_filter_guiding_flip_y(const DenoiseContext &context)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool DenoiserGPU::denoise_filter_guiding_set_fake_albedo(const DenoiseContext &context)
|
||||
bool DenoiserGPU::denoise_filter_guiding_set_fake_albedo(DenoiseContext &context)
|
||||
{
|
||||
const BufferParams &buffer_params = context.buffer_params;
|
||||
|
||||
if (context.use_guiding_passes && !context.guiding_params.device_pointer) {
|
||||
context.guiding_buffer.alloc_to_device(buffer_params.width * buffer_params.height *
|
||||
context.guiding_params.pass_stride);
|
||||
context.guiding_params.device_pointer = context.guiding_buffer.device_pointer;
|
||||
}
|
||||
|
||||
const int work_size = buffer_params.width * buffer_params.height;
|
||||
|
||||
const DeviceKernelArguments args(&context.guiding_params.device_pointer,
|
||||
|
|
|
|||
|
|
@ -47,20 +47,22 @@ class DenoiserGPU : public Denoiser {
|
|||
|
||||
/* Run corresponding filter kernels, preparing data for the denoiser or copying data from the
|
||||
* denoiser result to the render buffer. */
|
||||
bool denoise_filter_color_preprocess(const DenoiseContext &context, const DenoisePass &pass);
|
||||
bool denoise_filter_color_postprocess(const DenoiseContext &context, const DenoisePass &pass);
|
||||
virtual bool denoise_filter_color_preprocess(const DenoiseContext &context,
|
||||
const DenoisePass &pass);
|
||||
virtual bool denoise_filter_color_postprocess(const DenoiseContext &context,
|
||||
const DenoisePass &pass);
|
||||
bool denoise_filter_color_flip_y(const DenoiseContext &context,
|
||||
const BufferParams &buffer_params,
|
||||
const DenoisePass &pass);
|
||||
bool denoise_filter_guiding_flip_y(const DenoiseContext &context);
|
||||
bool denoise_filter_guiding_set_fake_albedo(const DenoiseContext &context);
|
||||
bool denoise_filter_guiding_set_fake_albedo(DenoiseContext &context);
|
||||
|
||||
/* Read guiding passes from the render buffers, preprocess them in a way which is expected by
|
||||
* the GPU denoiser and store in the guiding passes memory within the given context.
|
||||
*
|
||||
* Pre-processing of the guiding passes is to only happen once per context lifetime. DO not
|
||||
* preprocess them for every pass which is being denoised. */
|
||||
bool denoise_filter_guiding_preprocess(const DenoiseContext &context);
|
||||
virtual bool denoise_filter_guiding_preprocess(DenoiseContext &context);
|
||||
|
||||
bool denoise_pass(DenoiseContext &context, PassType pass_type);
|
||||
|
||||
|
|
|
|||
|
|
@ -584,7 +584,9 @@ void PathTrace::set_denoiser_params(const DenoiseParams ¶ms)
|
|||
|
||||
const bool is_cpu_denoising = old_denoiser_params.type == DENOISER_OPENIMAGEDENOISE &&
|
||||
old_denoiser_params.use_gpu == false;
|
||||
const bool requested_gpu_denoising = effective_denoise_params.type == DENOISER_OPTIX ||
|
||||
const bool always_gpu_denoising = effective_denoise_params.type == DENOISER_DLSS ||
|
||||
effective_denoise_params.type == DENOISER_OPTIX;
|
||||
const bool requested_gpu_denoising = always_gpu_denoising ||
|
||||
(effective_denoise_params.type ==
|
||||
DENOISER_OPENIMAGEDENOISE &&
|
||||
effective_denoise_params.use_gpu == true);
|
||||
|
|
@ -602,7 +604,7 @@ void PathTrace::set_denoiser_params(const DenoiseParams ¶ms)
|
|||
/* Optix Denoiser is not supporting CPU devices, so use_gpu option is not
|
||||
* shown in the UI and changes in the option value should not be checked. */
|
||||
if (old_denoiser_params.type == effective_denoise_params.type &&
|
||||
(is_same_denoising_device_type || effective_denoise_params.type == DENOISER_OPTIX))
|
||||
(is_same_denoising_device_type || always_gpu_denoising))
|
||||
{
|
||||
denoiser_->set_params(effective_denoise_params);
|
||||
}
|
||||
|
|
@ -949,7 +951,11 @@ void PathTrace::cancel()
|
|||
{
|
||||
thread_scoped_lock lock(render_cancel_.mutex);
|
||||
|
||||
render_cancel_.is_requested = true;
|
||||
/* Only cancel in the middle of rendering when there is at least one sample in the output.
|
||||
* Otherwise interactivity becomes bad. */
|
||||
if (get_num_samples_in_buffer() > 1) {
|
||||
render_cancel_.is_requested = true;
|
||||
}
|
||||
|
||||
while (render_cancel_.is_rendering) {
|
||||
render_cancel_.condition.wait(lock);
|
||||
|
|
|
|||
|
|
@ -46,6 +46,10 @@ bool RenderScheduler::is_background() const
|
|||
void RenderScheduler::set_denoiser_params(const DenoiseParams ¶ms)
|
||||
{
|
||||
denoiser_params_ = params;
|
||||
|
||||
if (is_denoiser_interactive()) {
|
||||
state_.resolution_divider = pixel_size_;
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderScheduler::is_denoiser_gpu_used() const
|
||||
|
|
@ -90,6 +94,11 @@ void RenderScheduler::set_sample_params(const int num_samples,
|
|||
|
||||
int RenderScheduler::get_num_samples() const
|
||||
{
|
||||
/* Do continuous rendering when DLSS is active. */
|
||||
if (is_denoiser_interactive()) {
|
||||
return Integrator::MAX_SAMPLES;
|
||||
}
|
||||
|
||||
return num_samples_;
|
||||
}
|
||||
|
||||
|
|
@ -288,7 +297,7 @@ bool RenderScheduler::done() const
|
|||
return true;
|
||||
}
|
||||
|
||||
return get_num_rendered_samples() >= num_samples_;
|
||||
return get_num_rendered_samples() >= get_num_samples();
|
||||
}
|
||||
|
||||
RenderWork RenderScheduler::get_render_work()
|
||||
|
|
@ -345,6 +354,9 @@ RenderWork RenderScheduler::get_render_work()
|
|||
if (denoiser_params_.use) {
|
||||
render_work.resolution_divider *= denoiser_params_.upscale_factor;
|
||||
}
|
||||
if (is_denoiser_interactive()) {
|
||||
state_.num_rendered_samples = 0;
|
||||
}
|
||||
|
||||
render_work.path_trace.start_sample = get_start_sample_to_path_trace();
|
||||
render_work.path_trace.num_samples = get_num_samples_to_path_trace();
|
||||
|
|
@ -871,7 +883,7 @@ int RenderScheduler::get_num_samples_to_path_trace() const
|
|||
/* Always start full resolution render with a single sample. Gives more instant feedback to
|
||||
* artists, and allows to gather information for a subsequent path tracing works. Do it in the
|
||||
* headless mode as well, to give some estimate of how long samples are taking. */
|
||||
if (state_.num_rendered_samples == 0) {
|
||||
if (state_.num_rendered_samples == 0 && state_.last_display_update_sample == -1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
@ -886,7 +898,8 @@ int RenderScheduler::get_num_samples_to_path_trace() const
|
|||
* more than N samples. */
|
||||
const int num_samples_pot = round_num_samples_to_power_of_2(num_samples_per_update);
|
||||
|
||||
const int max_num_samples_to_render = sample_offset_ + num_samples_ - path_trace_start_sample;
|
||||
const int max_num_samples_to_render = sample_offset_ + get_num_samples() -
|
||||
path_trace_start_sample;
|
||||
|
||||
int num_samples_to_render = min(num_samples_pot, max_num_samples_to_render);
|
||||
|
||||
|
|
@ -958,7 +971,7 @@ int RenderScheduler::get_num_samples_to_path_trace() const
|
|||
min(num_samples_to_occupy, max_num_samples_to_render));
|
||||
}
|
||||
|
||||
if (limit_samples_per_update_) {
|
||||
if (limit_samples_per_update_ && !is_denoiser_interactive()) {
|
||||
num_samples_to_render = min(limit_samples_per_update_, num_samples_to_render);
|
||||
}
|
||||
|
||||
|
|
@ -1044,6 +1057,10 @@ bool RenderScheduler::work_need_denoise(bool &delayed, bool &ready_to_display)
|
|||
|
||||
/* Viewport render. */
|
||||
|
||||
if (is_denoiser_interactive()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Navigation might render multiple samples at a lower resolution. Those are not to be counted as
|
||||
* final samples. */
|
||||
const int num_samples_finished = state_.resolution_divider == pixel_size_ ?
|
||||
|
|
@ -1151,6 +1168,11 @@ bool RenderScheduler::work_need_rebalance()
|
|||
|
||||
void RenderScheduler::update_start_resolution_divider()
|
||||
{
|
||||
if (is_denoiser_interactive()) {
|
||||
start_resolution_divider_ = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (default_start_resolution_divider_ == 0) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -1232,6 +1254,11 @@ bool RenderScheduler::is_denoise_active_during_update() const
|
|||
return true;
|
||||
}
|
||||
|
||||
bool RenderScheduler::is_denoiser_interactive() const
|
||||
{
|
||||
return denoiser_params_.use && denoiser_params_.type == DENOISER_DLSS;
|
||||
}
|
||||
|
||||
bool RenderScheduler::work_is_usable_for_first_render_estimation(const RenderWork &render_work)
|
||||
{
|
||||
return render_work.resolution_divider == pixel_size_ &&
|
||||
|
|
|
|||
|
|
@ -249,6 +249,9 @@ class RenderScheduler {
|
|||
* unit. */
|
||||
bool is_denoise_active_during_update() const;
|
||||
|
||||
/* Check whether a real-time denoiser like DLSS is active. */
|
||||
bool is_denoiser_interactive() const;
|
||||
|
||||
/* Heuristic which aims to give perceptually pleasant update of display interval in a way that at
|
||||
* lower samples and near the beginning of rendering, updates happen more often, but with higher
|
||||
* number of samples and later in the render, updates happen less often but device occupancy
|
||||
|
|
|
|||
|
|
@ -1066,6 +1066,43 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
|
|||
}
|
||||
ccl_gpu_kernel_postfix
|
||||
|
||||
ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
|
||||
ccl_gpu_kernel_signature(filter_color_preprocess_to_surface,
|
||||
const uint64_t color_surface,
|
||||
ccl_global float *render_buffer,
|
||||
const int full_x,
|
||||
const int full_y,
|
||||
const int width,
|
||||
const int height,
|
||||
const int offset,
|
||||
const int stride,
|
||||
const int pass_stride,
|
||||
const int pass_denoised)
|
||||
{
|
||||
#ifdef __KERNEL_CUDA__
|
||||
const int work_index = ccl_gpu_global_id_x();
|
||||
const int y = work_index / width;
|
||||
const int x = work_index - y * width;
|
||||
|
||||
if (x >= width || y >= height) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint64_t render_pixel_index = offset + (x + full_x) + (y + full_y) * stride;
|
||||
ccl_global float *denoised_pixel = render_buffer + render_pixel_index * pass_stride +
|
||||
pass_denoised;
|
||||
|
||||
float4 color_value;
|
||||
color_value.x = denoised_pixel[0];
|
||||
color_value.y = denoised_pixel[1];
|
||||
color_value.z = denoised_pixel[2];
|
||||
color_value.w = 1.0;
|
||||
|
||||
surf2Dwrite(color_value, color_surface, x * sizeof(float4), y);
|
||||
#endif
|
||||
}
|
||||
ccl_gpu_kernel_postfix
|
||||
|
||||
ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
|
||||
ccl_gpu_kernel_signature(filter_guiding_preprocess,
|
||||
ccl_global float *guiding_buffer,
|
||||
|
|
@ -1146,6 +1183,131 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
|
|||
}
|
||||
ccl_gpu_kernel_postfix
|
||||
|
||||
ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
|
||||
ccl_gpu_kernel_signature(filter_guiding_preprocess_to_surface,
|
||||
const uint64_t depth_surface,
|
||||
const uint64_t albedo_surface,
|
||||
const uint64_t specular_albedo_surface,
|
||||
const uint64_t normal_roughness_surface,
|
||||
const uint64_t motion_surface,
|
||||
const uint64_t specular_motion_surface,
|
||||
const ccl_global float *render_buffer,
|
||||
const int render_offset,
|
||||
const int render_stride,
|
||||
const int render_pass_stride,
|
||||
const int render_pass_sample_count,
|
||||
const int render_pass_depth,
|
||||
const int render_pass_albedo,
|
||||
const int render_pass_specular_albedo,
|
||||
const int render_pass_normal,
|
||||
const int render_pass_roughness,
|
||||
const int render_pass_motion,
|
||||
const int render_pass_specular_motion,
|
||||
const int full_x,
|
||||
const int full_y,
|
||||
const int width,
|
||||
const int height,
|
||||
const int num_samples)
|
||||
{
|
||||
#ifdef __KERNEL_CUDA__
|
||||
const int work_index = ccl_gpu_global_id_x();
|
||||
const int y = work_index / width;
|
||||
const int x = work_index - y * width;
|
||||
|
||||
if (x >= width || y >= height) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint64_t render_pixel_index = render_offset + (x + full_x) + (y + full_y) * render_stride;
|
||||
const ccl_global float *buffer = render_buffer + render_pixel_index * render_pass_stride;
|
||||
|
||||
float pixel_scale;
|
||||
if (render_pass_sample_count == PASS_UNUSED) {
|
||||
pixel_scale = 1.0f / num_samples;
|
||||
}
|
||||
else {
|
||||
pixel_scale = 1.0f / __float_as_uint(buffer[render_pass_sample_count]);
|
||||
}
|
||||
|
||||
/* Depth pass. */
|
||||
if (render_pass_depth != PASS_UNUSED) {
|
||||
const ccl_global float *depth_in = buffer + render_pass_depth;
|
||||
|
||||
const float depth_value = depth_in[0] * pixel_scale;
|
||||
|
||||
surf2Dwrite(depth_value, depth_surface, x * sizeof(float), y);
|
||||
}
|
||||
|
||||
/* Diffuse albedo pass. */
|
||||
if (render_pass_albedo != PASS_UNUSED) {
|
||||
const ccl_global float *albedo_in = buffer + render_pass_albedo;
|
||||
|
||||
float4 albedo_value;
|
||||
albedo_value.x = albedo_in[0] * pixel_scale;
|
||||
albedo_value.y = albedo_in[1] * pixel_scale;
|
||||
albedo_value.z = albedo_in[2] * pixel_scale;
|
||||
albedo_value.w = 1.0;
|
||||
|
||||
/* Tonemap the albedo with simple reinhard operator. */
|
||||
albedo_value.x = clamp(albedo_value.x / (1.0f + albedo_value.x), 0.0f, 1.0f);
|
||||
albedo_value.y = clamp(albedo_value.y / (1.0f + albedo_value.y), 0.0f, 1.0f);
|
||||
albedo_value.z = clamp(albedo_value.z / (1.0f + albedo_value.z), 0.0f, 1.0f);
|
||||
|
||||
surf2Dwrite(albedo_value, albedo_surface, x * sizeof(float4), y);
|
||||
}
|
||||
|
||||
/* Specular albedo pass. */
|
||||
if (render_pass_specular_albedo != PASS_UNUSED) {
|
||||
const ccl_global float *albedo_in = buffer + render_pass_specular_albedo;
|
||||
|
||||
float4 specular_albedo_value;
|
||||
specular_albedo_value.x = albedo_in[0] * pixel_scale;
|
||||
specular_albedo_value.y = albedo_in[1] * pixel_scale;
|
||||
specular_albedo_value.z = albedo_in[2] * pixel_scale;
|
||||
specular_albedo_value.w = 1.0;
|
||||
|
||||
surf2Dwrite(specular_albedo_value, specular_albedo_surface, x * sizeof(float4), y);
|
||||
}
|
||||
|
||||
/* Normal and roughness pass. */
|
||||
if (render_pass_normal != PASS_UNUSED && render_pass_roughness != PASS_UNUSED) {
|
||||
const ccl_global float *normal_in = buffer + render_pass_normal;
|
||||
const ccl_global float *roughness_in = buffer + render_pass_roughness;
|
||||
|
||||
float4 normal_roughness_value;
|
||||
normal_roughness_value.x = normal_in[0] * pixel_scale;
|
||||
normal_roughness_value.y = normal_in[1] * pixel_scale;
|
||||
normal_roughness_value.z = normal_in[2] * pixel_scale;
|
||||
normal_roughness_value.w = roughness_in[0] * pixel_scale;
|
||||
|
||||
surf2Dwrite(normal_roughness_value, normal_roughness_surface, x * sizeof(float4), y);
|
||||
}
|
||||
|
||||
/* Motion pass. */
|
||||
if (render_pass_motion != PASS_UNUSED) {
|
||||
const ccl_global float *motion_in = buffer + render_pass_motion;
|
||||
|
||||
float2 motion_value;
|
||||
motion_value.x = motion_in[0] * pixel_scale;
|
||||
motion_value.y = motion_in[1] * pixel_scale;
|
||||
|
||||
surf2Dwrite(motion_value, motion_surface, x * sizeof(float2), y);
|
||||
}
|
||||
|
||||
/* Specular motion pass. */
|
||||
if (render_pass_specular_motion != PASS_UNUSED) {
|
||||
const ccl_global float *motion_in = buffer + render_pass_specular_motion;
|
||||
|
||||
float2 motion_value;
|
||||
motion_value.x = motion_in[0] * pixel_scale;
|
||||
motion_value.y = motion_in[1] * pixel_scale;
|
||||
|
||||
surf2Dwrite(motion_value, specular_motion_surface, x * sizeof(float2), y);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
ccl_gpu_kernel_postfix
|
||||
|
||||
ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
|
||||
ccl_gpu_kernel_signature(filter_guiding_set_fake_albedo,
|
||||
ccl_global float *guiding_buffer,
|
||||
|
|
@ -1249,6 +1411,90 @@ ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
|
|||
}
|
||||
ccl_gpu_kernel_postfix
|
||||
|
||||
ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
|
||||
ccl_gpu_kernel_signature(filter_color_postprocess_from_surface,
|
||||
const uint64_t color_surface,
|
||||
ccl_global float *render_buffer,
|
||||
const int full_x,
|
||||
const int full_y,
|
||||
const int width,
|
||||
const int height,
|
||||
const int offset,
|
||||
const int stride,
|
||||
const int render_full_x,
|
||||
const int render_full_y,
|
||||
const int render_offset,
|
||||
const int render_stride,
|
||||
const int pass_stride,
|
||||
const int num_samples,
|
||||
const int pass_noisy,
|
||||
const int pass_denoised,
|
||||
const int pass_sample_count,
|
||||
const int num_components,
|
||||
const int use_compositing,
|
||||
const float upscale_factor)
|
||||
{
|
||||
#ifdef __KERNEL_CUDA__
|
||||
const int work_index = ccl_gpu_global_id_x();
|
||||
const int y = work_index / width;
|
||||
const int x = work_index - y * width;
|
||||
|
||||
if (x >= width || y >= height) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint64_t render_pixel_index = render_offset + (int(x / upscale_factor) + render_full_x) +
|
||||
(int(y / upscale_factor) + render_full_y) * render_stride;
|
||||
ccl_global float *buffer = render_buffer + render_pixel_index * pass_stride;
|
||||
|
||||
float pixel_scale;
|
||||
if (pass_sample_count == PASS_UNUSED) {
|
||||
pixel_scale = num_samples;
|
||||
}
|
||||
else {
|
||||
pixel_scale = __float_as_uint(buffer[pass_sample_count]);
|
||||
}
|
||||
|
||||
const uint64_t denoised_pixel_index = offset + (x + full_x) + (y + full_y) * stride;
|
||||
ccl_global float *denoised_pixel = render_buffer + denoised_pixel_index * pass_stride +
|
||||
pass_denoised;
|
||||
|
||||
float4 color_value;
|
||||
surf2Dread(&color_value, color_surface, x * sizeof(float4), y);
|
||||
|
||||
denoised_pixel[0] = color_value.x;
|
||||
denoised_pixel[1] = color_value.y;
|
||||
denoised_pixel[2] = color_value.z;
|
||||
|
||||
if (pass_sample_count == PASS_UNUSED || upscale_factor == 1.0f) {
|
||||
denoised_pixel[0] *= pixel_scale;
|
||||
denoised_pixel[1] *= pixel_scale;
|
||||
denoised_pixel[2] *= pixel_scale;
|
||||
}
|
||||
|
||||
if (num_components == 3) {
|
||||
/* Pass without alpha channel. */
|
||||
}
|
||||
else if (!use_compositing) {
|
||||
/* Currently compositing passes are either 3-component (derived by dividing light passes)
|
||||
* or do not have transparency (shadow catcher). Implicitly rely on this logic, as it
|
||||
* simplifies logic and avoids extra memory allocation. */
|
||||
const ccl_global float *noisy_pixel = buffer + pass_noisy;
|
||||
denoised_pixel[3] = noisy_pixel[3];
|
||||
|
||||
if (pass_sample_count != PASS_UNUSED && upscale_factor != 1.0f) {
|
||||
denoised_pixel[3] /= pixel_scale;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Assigning to zero since this is a default alpha value for 3-component passes, and it
|
||||
* is an opaque pixel for 4 component passes. */
|
||||
denoised_pixel[3] = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
ccl_gpu_kernel_postfix
|
||||
|
||||
ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS)
|
||||
ccl_gpu_kernel_signature(filter_color_flip_y,
|
||||
ccl_global float *render_buffer,
|
||||
|
|
|
|||
|
|
@ -104,8 +104,12 @@
|
|||
#define FN18(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17, p18) p1; p2; p3; p4; p5; p6; p7; p8; p9; p10; p11; p12; p13; p14; p15; p16; p17; p18;
|
||||
#define FN19(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17, p18, p19) p1; p2; p3; p4; p5; p6; p7; p8; p9; p10; p11; p12; p13; p14; p15; p16; p17; p18; p19;
|
||||
#define FN20(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17, p18, p19, p20) p1; p2; p3; p4; p5; p6; p7; p8; p9; p10; p11; p12; p13; p14; p15; p16; p17; p18; p19; p20;
|
||||
#define GET_LAST_ARG(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17, p18, p19, p20, ...) p20
|
||||
#define PARAMS_MAKER(...) GET_LAST_ARG(__VA_ARGS__, FN20, FN19, FN18, FN17, FN16, FN15, FN14, FN13, FN12, FN11, FN10, FN9, FN8, FN7, FN6, FN5, FN4, FN3, FN2, FN1, FN0)
|
||||
#define FN21(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17, p18, p19, p20, p21) p1; p2; p3; p4; p5; p6; p7; p8; p9; p10; p11; p12; p13; p14; p15; p16; p17; p18; p19; p20; p21;
|
||||
#define FN22(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17, p18, p19, p20, p21, p22) p1; p2; p3; p4; p5; p6; p7; p8; p9; p10; p11; p12; p13; p14; p15; p16; p17; p18; p19; p20; p21; p22;
|
||||
#define FN23(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17, p18, p19, p20, p21, p22, p23) p1; p2; p3; p4; p5; p6; p7; p8; p9; p10; p11; p12; p13; p14; p15; p16; p17; p18; p19; p20; p21; p22; p23;
|
||||
#define FN24(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17, p18, p19, p20, p21, p22, p23, p24) p1; p2; p3; p4; p5; p6; p7; p8; p9; p10; p11; p12; p13; p14; p15; p16; p17; p18; p19; p20; p21; p22; p23; p24;
|
||||
#define GET_LAST_ARG(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, ...) p24
|
||||
#define PARAMS_MAKER(...) GET_LAST_ARG(__VA_ARGS__, FN24, FN23, FN22, FN21, FN20, FN19, FN18, FN17, FN16, FN15, FN14, FN13, FN12, FN11, FN10, FN9, FN8, FN7, FN6, FN5, FN4, FN3, FN2, FN1, FN0)
|
||||
|
||||
/* Generate a struct containing the entry-point parameters and a "run"
|
||||
* method which can access them implicitly via this-> */
|
||||
|
|
|
|||
|
|
@ -735,6 +735,9 @@ bool oneapi_enqueue_kernel(KernelContext *kernel_context,
|
|||
case DEVICE_KERNEL_NUM:
|
||||
case DEVICE_KERNEL_INTEGRATOR_MEGAKERNEL:
|
||||
case DEVICE_KERNEL_INTEGRATOR_SHADOW_PATH_MNEE_PENDING:
|
||||
case DEVICE_KERNEL_FILTER_GUIDING_PREPROCESS_TO_SURFACE:
|
||||
case DEVICE_KERNEL_FILTER_COLOR_PREPROCESS_TO_SURFACE:
|
||||
case DEVICE_KERNEL_FILTER_COLOR_POSTPROCESS_FROM_SURFACE:
|
||||
kernel_assert(0);
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1831,9 +1831,12 @@ enum DeviceKernel : int {
|
|||
DEVICE_KERNEL_ADAPTIVE_SAMPLING_CONVERGENCE_FILTER_Y,
|
||||
|
||||
DEVICE_KERNEL_FILTER_GUIDING_PREPROCESS,
|
||||
DEVICE_KERNEL_FILTER_GUIDING_PREPROCESS_TO_SURFACE,
|
||||
DEVICE_KERNEL_FILTER_GUIDING_SET_FAKE_ALBEDO,
|
||||
DEVICE_KERNEL_FILTER_COLOR_PREPROCESS,
|
||||
DEVICE_KERNEL_FILTER_COLOR_PREPROCESS_TO_SURFACE,
|
||||
DEVICE_KERNEL_FILTER_COLOR_POSTPROCESS,
|
||||
DEVICE_KERNEL_FILTER_COLOR_POSTPROCESS_FROM_SURFACE,
|
||||
DEVICE_KERNEL_FILTER_COLOR_FLIP_Y,
|
||||
|
||||
DEVICE_KERNEL_VOLUME_GUIDING_FILTER_X,
|
||||
|
|
|
|||
|
|
@ -166,6 +166,7 @@ NODE_DEFINE(Integrator)
|
|||
denoiser_type_enum.insert("none", DENOISER_NONE);
|
||||
denoiser_type_enum.insert("optix", DENOISER_OPTIX);
|
||||
denoiser_type_enum.insert("openimagedenoise", DENOISER_OPENIMAGEDENOISE);
|
||||
denoiser_type_enum.insert("dlss", DENOISER_DLSS);
|
||||
|
||||
static NodeEnum denoiser_prefilter_enum;
|
||||
denoiser_prefilter_enum.insert("none", DENOISER_PREFILTER_NONE);
|
||||
|
|
@ -211,6 +212,10 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene
|
|||
}
|
||||
});
|
||||
|
||||
if (use_denoise && denoiser_type == DENOISER_DLSS) {
|
||||
use_pixel_jitter = true;
|
||||
}
|
||||
|
||||
KernelIntegrator *kintegrator = &dscene->data.integrator;
|
||||
|
||||
device_free(device, dscene);
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@
|
|||
# ifndef vsnprintf
|
||||
# define vsnprintf _vsnprintf
|
||||
# endif
|
||||
#else
|
||||
# include <locale.h>
|
||||
#endif /* _WIN32 */
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
|
@ -239,26 +241,56 @@ string string_remove_gpu_from_cpu_name(const string &s)
|
|||
|
||||
/* Wide char strings helpers for Windows. */
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
wstring string_to_wstring(const string &str)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
const int length_wc = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.length(), nullptr, 0);
|
||||
wstring str_wc(length_wc, 0);
|
||||
MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.length(), &str_wc[0], length_wc);
|
||||
#else
|
||||
locale_t new_locale = newlocale(LC_CTYPE_MASK, "en_US.UTF-8", (locale_t)0);
|
||||
locale_t old_locale = uselocale(new_locale);
|
||||
mbstate_t state = {};
|
||||
const char *str_p = str.c_str();
|
||||
const size_t length_wc = mbsrtowcs(nullptr, &str_p, 0, &state);
|
||||
wstring str_wc;
|
||||
if (length_wc != (size_t)-1) {
|
||||
str_wc.resize(length_wc, 0);
|
||||
mbsrtowcs(str_wc.data(), &str_p, str_wc.size(), &state);
|
||||
}
|
||||
uselocale(old_locale);
|
||||
freelocale(new_locale);
|
||||
#endif
|
||||
return str_wc;
|
||||
}
|
||||
|
||||
string string_from_wstring(const wstring &str)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
int length_mb = WideCharToMultiByte(
|
||||
CP_UTF8, 0, str.c_str(), str.size(), nullptr, 0, nullptr, nullptr);
|
||||
string str_mb(length_mb, 0);
|
||||
WideCharToMultiByte(
|
||||
CP_UTF8, 0, str.c_str(), str.size(), &str_mb[0], length_mb, nullptr, nullptr);
|
||||
#else
|
||||
locale_t new_locale = newlocale(LC_CTYPE_MASK, "en_US.UTF-8", (locale_t)0);
|
||||
locale_t old_locale = uselocale(new_locale);
|
||||
const wchar_t *str_p = str.c_str();
|
||||
mbstate_t state = {};
|
||||
const size_t length_mb = wcsrtombs(nullptr, &str_p, 0, &state);
|
||||
string str_mb;
|
||||
if (length_mb != (size_t)-1) {
|
||||
str_mb.resize(length_mb, 0);
|
||||
wcsrtombs(str_mb.data(), &str_p, str_mb.size(), &state);
|
||||
}
|
||||
uselocale(old_locale);
|
||||
freelocale(new_locale);
|
||||
#endif
|
||||
return str_mb;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
string string_to_ansi(const string &str)
|
||||
{
|
||||
const int length_wc = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.length(), nullptr, 0);
|
||||
|
|
|
|||
|
|
@ -58,10 +58,10 @@ string string_remove_gpu_from_cpu_name(const string &s);
|
|||
* Please note that strings are expected to be in UTF8 code-page, and
|
||||
* if ANSI is needed then explicit conversion required.
|
||||
*/
|
||||
#ifdef _WIN32
|
||||
using std::wstring;
|
||||
wstring string_to_wstring(const string &path);
|
||||
string string_from_wstring(const wstring &path);
|
||||
#ifdef _WIN32
|
||||
string string_to_ansi(const string &str);
|
||||
#endif
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue