mirror of
https://github.com/blender/blender
synced 2026-11-01 03:16:58 +03:00
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
314 lines
9.4 KiB
C++
314 lines
9.4 KiB
C++
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0 */
|
|
|
|
#include "integrator/denoiser.h"
|
|
|
|
#include "device/device.h"
|
|
|
|
#include "integrator/denoiser_dlss.h"
|
|
#include "integrator/denoiser_oidn.h"
|
|
#include "session/display_driver.h"
|
|
#ifdef WITH_OPENIMAGEDENOISE
|
|
# include "integrator/denoiser_oidn_gpu.h"
|
|
#endif
|
|
#ifdef WITH_OPTIX
|
|
# include "integrator/denoiser_optix.h"
|
|
#endif
|
|
#include "session/buffers.h"
|
|
|
|
#include "util/log.h"
|
|
#include "util/openimagedenoise.h"
|
|
#include "util/progress.h"
|
|
|
|
CCL_NAMESPACE_BEGIN
|
|
|
|
/* Check whether given device is single (not a MultiDevice). */
|
|
static bool is_single_device(const Device *device)
|
|
{
|
|
if (device->info.type == DEVICE_MULTI) {
|
|
/* Assume multi-device is never created with a single sub-device.
|
|
* If one requests such configuration it should be checked on the session level. */
|
|
return false;
|
|
}
|
|
|
|
if (!device->info.multi_devices.empty()) {
|
|
/* Some configurations will use multi_devices, but keep the type of an individual device.
|
|
* This does simplify checks for homogeneous setups, but here we really need a single device.
|
|
*/
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/* Find best suitable device to perform denoiser on. Will iterate over possible sub-devices of
|
|
* multi-device. */
|
|
static Device *find_best_device(Device *device,
|
|
const DenoiserType type,
|
|
const GraphicsInteropDevice &interop_device)
|
|
{
|
|
Device *best_device = nullptr;
|
|
|
|
device->foreach_device([&](Device *sub_device) {
|
|
if ((sub_device->info.denoisers & type) == 0) {
|
|
return;
|
|
}
|
|
|
|
if (!best_device) {
|
|
best_device = sub_device;
|
|
}
|
|
else {
|
|
/* Prefer non-CPU devices over CPU for performance reasons. */
|
|
if (sub_device->info.type != DEVICE_CPU && best_device->info.type == DEVICE_CPU) {
|
|
best_device = sub_device;
|
|
}
|
|
|
|
/* Prefer a device that can use graphics interop for faster display update. */
|
|
if (sub_device->should_use_graphics_interop(interop_device) &&
|
|
!best_device->should_use_graphics_interop(interop_device))
|
|
{
|
|
best_device = sub_device;
|
|
}
|
|
|
|
/* TODO(sergey): Choose fastest device from available ones. Taking into account performance
|
|
* of the device and data transfer cost. */
|
|
}
|
|
});
|
|
|
|
return best_device;
|
|
}
|
|
|
|
bool use_optix_denoiser(Device *denoiser_device, const DenoiseParams ¶ms)
|
|
{
|
|
#ifdef WITH_OPTIX
|
|
return (params.type == DENOISER_OPTIX &&
|
|
OptiXDenoiser::is_device_supported(denoiser_device->info));
|
|
#else
|
|
(void)denoiser_device;
|
|
(void)params;
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
bool use_gpu_oidn_denoiser(Device *denoiser_device, const DenoiseParams ¶ms)
|
|
{
|
|
#ifdef WITH_OPENIMAGEDENOISE
|
|
return (params.type == DENOISER_OPENIMAGEDENOISE && params.use_gpu &&
|
|
OIDNDenoiserGPU::is_device_supported(denoiser_device->info));
|
|
#else
|
|
(void)denoiser_device;
|
|
(void)params;
|
|
return false;
|
|
#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,
|
|
const GraphicsInteropDevice &interop_device,
|
|
Device *&single_denoiser_device)
|
|
{
|
|
DCHECK(params.use);
|
|
|
|
DenoiseParams effective_denoise_params = params;
|
|
|
|
single_denoiser_device = nullptr;
|
|
if (is_single_device(denoiser_device)) {
|
|
/* Simple case: denoising happens on a single device. */
|
|
single_denoiser_device = denoiser_device;
|
|
}
|
|
else {
|
|
/* Find best device from the ones which are proposed for denoising.
|
|
* The choice is expected to be between a few GPUs, or between a GPU and a CPU
|
|
* or between a few GPUs and a CPU. */
|
|
single_denoiser_device = find_best_device(denoiser_device, params.type, interop_device);
|
|
}
|
|
/* Ensure that we have a device to be used later in the code below. */
|
|
if (single_denoiser_device == nullptr) {
|
|
single_denoiser_device = cpu_fallback_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_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;
|
|
}
|
|
}
|
|
|
|
/* Always fallback to OIDN on CPU. */
|
|
effective_denoise_params.type = DENOISER_OPENIMAGEDENOISE;
|
|
effective_denoise_params.use_gpu = false;
|
|
effective_denoise_params.upscale_factor = 1.0f;
|
|
|
|
return effective_denoise_params;
|
|
}
|
|
|
|
unique_ptr<Denoiser> Denoiser::create(Device *denoiser_device,
|
|
Device *cpu_fallback_device,
|
|
const DenoiseParams ¶ms,
|
|
const GraphicsInteropDevice &interop_device)
|
|
{
|
|
|
|
Device *single_denoiser_device;
|
|
const DenoiseParams effective_denoiser_params = get_effective_denoise_params(
|
|
denoiser_device, cpu_fallback_device, params, interop_device, single_denoiser_device);
|
|
|
|
const bool is_cpu_denoiser_device = single_denoiser_device->info.type == DEVICE_CPU;
|
|
if (is_cpu_denoiser_device == false) {
|
|
#ifdef WITH_OPTIX
|
|
if (use_optix_denoiser(single_denoiser_device, effective_denoiser_params)) {
|
|
return make_unique<OptiXDenoiser>(single_denoiser_device, effective_denoiser_params);
|
|
}
|
|
#endif
|
|
|
|
#ifdef WITH_OPENIMAGEDENOISE
|
|
/* If available and allowed, then we will use OpenImageDenoise on GPU, otherwise on CPU. */
|
|
if (use_gpu_oidn_denoiser(single_denoiser_device, effective_denoiser_params)) {
|
|
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()) {
|
|
return nullptr;
|
|
}
|
|
|
|
/* Used preference CPU when possible, and fallback on CPU fallback device otherwise. */
|
|
return make_unique<OIDNDenoiser>(is_cpu_denoiser_device ? single_denoiser_device :
|
|
cpu_fallback_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
|
|
if (denoise_device_info.type != DEVICE_CPU &&
|
|
OIDNDenoiserGPU::is_device_supported(denoise_device_info))
|
|
{
|
|
return DENOISER_OPENIMAGEDENOISE;
|
|
}
|
|
#else
|
|
(void)denoise_device_info;
|
|
#endif
|
|
|
|
#ifdef WITH_OPTIX
|
|
if (OptiXDenoiser::is_device_supported(denoise_device_info)) {
|
|
return DENOISER_OPTIX;
|
|
}
|
|
#endif
|
|
|
|
#ifdef WITH_OPENIMAGEDENOISE
|
|
if (openimagedenoise_supported()) {
|
|
return DENOISER_OPENIMAGEDENOISE;
|
|
}
|
|
#endif
|
|
|
|
return DENOISER_NONE;
|
|
}
|
|
|
|
Denoiser::Denoiser(Device *denoiser_device, const DenoiseParams ¶ms)
|
|
: denoiser_device_(denoiser_device), denoise_kernels_are_loaded_(false), params_(params)
|
|
{
|
|
DCHECK(denoiser_device_);
|
|
DCHECK(params.use);
|
|
}
|
|
|
|
void Denoiser::set_params(const DenoiseParams ¶ms)
|
|
{
|
|
DCHECK_EQ(params.type, params_.type);
|
|
|
|
if (params.type == params_.type) {
|
|
params_ = params;
|
|
}
|
|
else {
|
|
LOG_ERROR << "Attempt to change denoiser type.";
|
|
}
|
|
}
|
|
|
|
const DenoiseParams &Denoiser::get_params() const
|
|
{
|
|
return params_;
|
|
}
|
|
|
|
bool Denoiser::load_kernels(Progress *progress)
|
|
{
|
|
/* If we have successfully loaded kernels once, then there is no need to repeat this again. */
|
|
if (denoise_kernels_are_loaded_) {
|
|
return denoise_kernels_are_loaded_;
|
|
}
|
|
|
|
if (progress) {
|
|
progress->set_status("Loading denoising kernels (may take a few minutes the first time)");
|
|
}
|
|
|
|
if (!denoiser_device_) {
|
|
set_error("No device available to denoise on");
|
|
return false;
|
|
}
|
|
|
|
/* Only need denoising feature, everything else is unused. */
|
|
if (!denoiser_device_->load_kernels(KERNEL_FEATURE_DENOISING)) {
|
|
string message = denoiser_device_->error_message();
|
|
if (message.empty()) {
|
|
message = "Failed loading denoising kernel, see console for errors";
|
|
}
|
|
set_error(message);
|
|
return false;
|
|
}
|
|
|
|
LOG_DEBUG << "Will denoise on " << denoiser_device_->info.description << " ("
|
|
<< denoiser_device_->info.id << ")";
|
|
|
|
denoise_kernels_are_loaded_ = true;
|
|
return true;
|
|
}
|
|
|
|
Device *Denoiser::get_denoiser_device() const
|
|
{
|
|
return denoiser_device_;
|
|
}
|
|
|
|
CCL_NAMESPACE_END
|