Cycles: Option to disable OpenImageDenoise GPU per scene

To reduce memory usage if needed.

Pull Request: https://projects.blender.org/blender/blender/pulls/117874
This commit is contained in:
Nikita Sirgienko 2024-02-06 17:46:21 +01:00 committed by Brecht Van Lommel
parent acdc3deaea
commit bc886857f3
8 changed files with 59 additions and 4 deletions

@ -352,6 +352,11 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
items=enum_denoising_input_passes,
default='RGB_ALBEDO_NORMAL',
)
denoising_use_gpu: BoolProperty(
name="Denoise on GPU",
description="Perform denoising on GPU devices, if available. 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,
)
use_preview_denoising: BoolProperty(
name="Use Viewport Denoising",
@ -382,6 +387,11 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
min=0, max=(1 << 24),
default=1,
)
preview_denoising_use_gpu: BoolProperty(
name="Denoise Preview on GPU",
description="Perform denoising on GPU devices, if available. 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,
)
samples: IntProperty(
name="Samples",
@ -1591,6 +1601,22 @@ class CyclesPreferences(bpy.types.AddonPreferences):
def has_active_device(self):
return self.get_num_gpu_devices() > 0
def has_oidn_gpu_devices(self):
import _cycles
compute_device_type = context.preferences.addons[__package__].preferences.get_compute_device_type()
# We need non-CPU devices, used for rendering and supporting OIDN GPU denoising
for device in _cycles.available_devices(compute_device_type):
device_type = device[1]
if device_type == 'CPU':
continue
has_device_oidn_support = device[5]
if has_device_oidn_support and self.find_existing_device_entry(device).use:
return True
return False
def _draw_devices(self, layout, device_type, devices):
box = layout.box()

@ -123,7 +123,6 @@ def use_optix(context):
return (get_device_type(context) == 'OPTIX' and cscene.device == 'GPU' and backend_has_active_gpu(context))
def use_oneapi(context):
cscene = context.scene.cycles
@ -156,6 +155,9 @@ def get_effective_preview_denoiser(context):
return 'OIDN'
def has_oidn_gpu_devices(context):
return context.preferences.addons[__package__].preferences.has_oidn_gpu_devices()
def use_mnee(context):
# The MNEE kernel doesn't compile on macOS < 13.
@ -236,6 +238,11 @@ class CYCLES_RENDER_PT_sampling_viewport_denoise(CyclesButtonsPanel, Panel):
col.prop(cscene, "preview_denoising_start_sample", text="Start Sample")
if effective_preview_denoiser == 'OPENIMAGEDENOISE':
row = col.row()
row.active = not use_cpu(context) and has_oidn_gpu_devices(context)
row.prop(cscene, "preview_denoising_use_gpu", text="Use GPU")
class CYCLES_RENDER_PT_sampling_render(CyclesButtonsPanel, Panel):
bl_label = "Render"
@ -295,6 +302,11 @@ class CYCLES_RENDER_PT_sampling_render_denoise(CyclesButtonsPanel, Panel):
if cscene.denoiser == 'OPENIMAGEDENOISE':
col.prop(cscene, "denoising_prefilter", text="Prefilter")
if cscene.denoiser == 'OPENIMAGEDENOISE':
row = col.row()
row.active = not use_cpu(context) and has_oidn_gpu_devices(context)
row.prop(cscene, "denoising_use_gpu", text="Use GPU")
class CYCLES_RENDER_PT_sampling_path_guiding(CyclesButtonsPanel, Panel):
bl_label = "Path Guiding"

@ -417,12 +417,14 @@ static PyObject *available_devices_func(PyObject * /*self*/, PyObject *args)
for (size_t i = 0; i < devices.size(); i++) {
DeviceInfo &device = devices[i];
string type_name = Device::string_from_type(device.type);
PyObject *device_tuple = PyTuple_New(5);
PyObject *device_tuple = PyTuple_New(6);
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()));
PyTuple_SET_ITEM(device_tuple, 3, PyBool_FromLong(device.has_peer_memory));
PyTuple_SET_ITEM(device_tuple, 4, PyBool_FromLong(device.use_hardware_raytracing));
PyTuple_SET_ITEM(
device_tuple, 5, PyBool_FromLong(device.denoisers & DENOISER_OPENIMAGEDENOISE));
PyTuple_SET_ITEM(ret, i, device_tuple);
}

@ -474,6 +474,7 @@ void BlenderSync::sync_integrator(BL::ViewLayer &b_view_layer, bool background)
* is that the interface and the integrator are technically out of sync. */
if (denoise_params.use) {
integrator->set_denoiser_type(denoise_params.type);
integrator->set_denoise_use_gpu(denoise_params.use_gpu);
integrator->set_denoise_start_sample(denoise_params.start_sample);
integrator->set_use_denoise_pass_albedo(denoise_params.use_pass_albedo);
integrator->set_use_denoise_pass_normal(denoise_params.use_pass_normal);
@ -970,6 +971,7 @@ DenoiseParams BlenderSync::get_denoise_params(BL::Scene &b_scene,
/* Final Render Denoising */
denoising.use = get_boolean(cscene, "use_denoising");
denoising.type = (DenoiserType)get_enum(cscene, "denoiser", DENOISER_NUM, DENOISER_NONE);
denoising.use_gpu = get_boolean(cscene, "denoising_use_gpu");
denoising.prefilter = (DenoiserPrefilter)get_enum(
cscene, "denoising_prefilter", DENOISER_PREFILTER_NUM, DENOISER_PREFILTER_NONE);
@ -988,6 +990,7 @@ DenoiseParams BlenderSync::get_denoise_params(BL::Scene &b_scene,
denoising.use = get_boolean(cscene, "use_preview_denoising");
denoising.type = (DenoiserType)get_enum(
cscene, "preview_denoiser", DENOISER_NUM, DENOISER_NONE);
denoising.use_gpu = get_boolean(cscene, "preview_denoising_use_gpu");
denoising.prefilter = (DenoiserPrefilter)get_enum(
cscene, "preview_denoising_prefilter", DENOISER_PREFILTER_NUM, DENOISER_PREFILTER_FAST);
denoising.start_sample = get_int(cscene, "preview_denoising_start_sample");

@ -63,6 +63,11 @@ class DenoiseParams : public Node {
/* Configure the denoiser to use motion vectors, previous image and a temporally stable model. */
bool temporally_stable = false;
/* If true, then allow, if supported, OpenImageDenoise to use GPU device.
* If false, then OpenImageDenoise will always use CPU regardless of GPU device
* precense. */
bool use_gpu = true;
DenoiserPrefilter prefilter = DENOISER_PREFILTER_FAST;
static const NodeEnum *get_type_enum();
@ -75,7 +80,8 @@ class DenoiseParams : public Node {
return !(use == other.use && type == other.type && start_sample == other.start_sample &&
use_pass_albedo == other.use_pass_albedo &&
use_pass_normal == other.use_pass_normal &&
temporally_stable == other.temporally_stable && prefilter == other.prefilter);
temporally_stable == other.temporally_stable && use_gpu == other.use_gpu &&
prefilter == other.prefilter);
}
};

@ -27,7 +27,9 @@ unique_ptr<Denoiser> Denoiser::create(Device *path_trace_device, const DenoisePa
#endif
#ifdef WITH_OPENIMAGEDENOISE
if (params.type == DENOISER_OPENIMAGEDENOISE && path_trace_device->info.type != DEVICE_CPU &&
/* If available and allowed, then we will use OpenImageDenoise on GPU, otherwise on CPU. */
if (params.type == DENOISER_OPENIMAGEDENOISE && params.use_gpu &&
path_trace_device->info.type != DEVICE_CPU &&
OIDNDenoiserGPU::is_device_supported(path_trace_device->info))
{
return make_unique<OIDNDenoiserGPU>(path_trace_device, params);

@ -148,6 +148,7 @@ NODE_DEFINE(Integrator)
"Denoiser Prefilter",
denoiser_prefilter_enum,
DENOISER_PREFILTER_ACCURATE);
SOCKET_BOOLEAN(denoise_use_gpu, "Denoise on GPU", true);
return type;
}
@ -393,6 +394,8 @@ DenoiseParams Integrator::get_denoise_params() const
denoise_params.type = denoiser_type;
denoise_params.use_gpu = denoise_use_gpu;
denoise_params.start_sample = denoise_start_sample;
denoise_params.use_pass_albedo = use_denoise_pass_albedo;

@ -98,6 +98,7 @@ class Integrator : public Node {
NODE_SOCKET_API(bool, use_denoise_pass_albedo);
NODE_SOCKET_API(bool, use_denoise_pass_normal);
NODE_SOCKET_API(DenoiserPrefilter, denoiser_prefilter);
NODE_SOCKET_API(bool, denoise_use_gpu);
enum : uint32_t {
AO_PASS_MODIFIED = (1 << 0),