diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index 5ac3f280cbf..f8e61fa0114 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -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() diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index 05a0f6c03c7..16adc4b018e 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -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" diff --git a/intern/cycles/blender/python.cpp b/intern/cycles/blender/python.cpp index e7017282083..6bff3ee7aba 100644 --- a/intern/cycles/blender/python.cpp +++ b/intern/cycles/blender/python.cpp @@ -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); } diff --git a/intern/cycles/blender/sync.cpp b/intern/cycles/blender/sync.cpp index d82718d7cab..d165a4f9b09 100644 --- a/intern/cycles/blender/sync.cpp +++ b/intern/cycles/blender/sync.cpp @@ -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"); diff --git a/intern/cycles/device/denoise.h b/intern/cycles/device/denoise.h index 8beddac2524..edf5bebc6ea 100644 --- a/intern/cycles/device/denoise.h +++ b/intern/cycles/device/denoise.h @@ -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); } }; diff --git a/intern/cycles/integrator/denoiser.cpp b/intern/cycles/integrator/denoiser.cpp index 7106213d4cc..e526d3b0271 100644 --- a/intern/cycles/integrator/denoiser.cpp +++ b/intern/cycles/integrator/denoiser.cpp @@ -27,7 +27,9 @@ unique_ptr 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(path_trace_device, params); diff --git a/intern/cycles/scene/integrator.cpp b/intern/cycles/scene/integrator.cpp index a2d496d110f..c4ad20e26f4 100644 --- a/intern/cycles/scene/integrator.cpp +++ b/intern/cycles/scene/integrator.cpp @@ -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; diff --git a/intern/cycles/scene/integrator.h b/intern/cycles/scene/integrator.h index 7fecd733d14..749e37d4498 100644 --- a/intern/cycles/scene/integrator.h +++ b/intern/cycles/scene/integrator.h @@ -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),