From 83b084ab3d5e6721a96c249ce0d418c627769ece Mon Sep 17 00:00:00 2001 From: Nikita Sirgienko Date: Wed, 28 Feb 2024 10:16:07 +0100 Subject: [PATCH] Fix: Respect Blender Cycles setting for GPU denoising Previously, GPU denoisers were ignoring settings about render configuration and were using any available GPU. With these changes, GPU denoisers will use the device selected in Blender Cycles settings. This allows any GPU denoiser to be used with CPU rendering. --- intern/cycles/blender/addon/properties.py | 24 ++- intern/cycles/blender/addon/ui.py | 41 ++++- intern/cycles/blender/device.cpp | 178 ++++++++++++---------- intern/cycles/blender/device.h | 7 +- intern/cycles/blender/python.cpp | 12 +- intern/cycles/blender/sync.cpp | 2 +- intern/cycles/integrator/denoiser.cpp | 38 +++-- intern/cycles/integrator/denoiser.h | 2 +- intern/cycles/integrator/path_trace.cpp | 48 ++++-- intern/cycles/integrator/path_trace.h | 5 + intern/cycles/session/denoising.cpp | 4 +- intern/cycles/session/denoising.h | 2 +- intern/cycles/session/session.cpp | 16 +- intern/cycles/session/session.h | 6 + 14 files changed, 266 insertions(+), 119 deletions(-) diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index 049c50e143d..45f3f98f946 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -223,7 +223,7 @@ def enum_openimagedenoise_denoiser(self, context): def enum_optix_denoiser(self, context): if not context or bool(context.preferences.addons[__package__].preferences.get_devices_for_type('OPTIX')): - return [('OPTIX', "OptiX", "Use the OptiX AI denoiser with GPU acceleration, only available on NVIDIA GPUs", 2)] + return [('OPTIX', "OptiX", "Use the OptiX AI denoiser with GPU acceleration, only available on NVIDIA GPUs when configured in the system tab in the user preferences", 2)] return [] @@ -354,7 +354,7 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): ) 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", + 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=False, ) @@ -389,7 +389,7 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): ) 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", + 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, ) @@ -1608,6 +1608,8 @@ class CyclesPreferences(bpy.types.AddonPreferences): # 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 != compute_device_type: + continue if device_type == 'CPU': continue @@ -1617,6 +1619,22 @@ class CyclesPreferences(bpy.types.AddonPreferences): return False + def has_optixdenoiser_gpu_devices(self): + import _cycles + compute_device_type = self.get_compute_device_type() + + # We need any OptiX devices, used for rendering + for device in _cycles.available_devices(compute_device_type): + device_type = device[1] + if device_type != compute_device_type: + continue + + has_device_optixdenoiser_support = device[6] + if has_device_optixdenoiser_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 b9614a46a7c..60f51df2aa1 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -143,6 +143,28 @@ def show_device_active(context): return True return backend_has_active_gpu(context) +def show_preview_denoise_active(context): + cscene = context.scene.cycles + if not cscene.use_preview_denoising: + return False + + if cscene.preview_denoiser == 'OPTIX': + return has_optixdenoiser_gpu_devices(context) + + # OIDN is always available, thanks to CPU support + return True + +def show_denoise_active(context): + cscene = context.scene.cycles + if not cscene.use_denoising: + return False + + if cscene.denoiser == 'OPTIX': + return has_optixdenoiser_gpu_devices(context) + + # OIDN is always available, thanks to CPU support + return True + def get_effective_preview_denoiser(context): scene = context.scene @@ -160,6 +182,9 @@ def get_effective_preview_denoiser(context): def has_oidn_gpu_devices(context): return context.preferences.addons[__package__].preferences.has_oidn_gpu_devices() +def has_optixdenoiser_gpu_devices(context): + return context.preferences.addons[__package__].preferences.has_optixdenoiser_gpu_devices() + def use_mnee(context): # The MNEE kernel doesn't compile on macOS < 13. @@ -231,7 +256,11 @@ class CYCLES_RENDER_PT_sampling_viewport_denoise(CyclesButtonsPanel, Panel): col = layout.column() col.active = cscene.use_preview_denoising - col.prop(cscene, "preview_denoiser", text="Denoiser") + + sub = col.column() + sub.active = show_preview_denoise_active(context) + sub.prop(cscene, "preview_denoiser", text="Denoiser") + col.prop(cscene, "preview_denoising_input_passes", text="Passes") effective_preview_denoiser = get_effective_preview_denoiser(context) @@ -242,7 +271,7 @@ class CYCLES_RENDER_PT_sampling_viewport_denoise(CyclesButtonsPanel, Panel): if effective_preview_denoiser == 'OPENIMAGEDENOISE': row = col.row() - row.active = not use_cpu(context) and has_oidn_gpu_devices(context) + row.active = has_oidn_gpu_devices(context) row.prop(cscene, "preview_denoising_use_gpu", text="Use GPU") @@ -299,14 +328,18 @@ class CYCLES_RENDER_PT_sampling_render_denoise(CyclesButtonsPanel, Panel): col = layout.column() col.active = cscene.use_denoising - col.prop(cscene, "denoiser", text="Denoiser") + + sub = col.column() + sub.active = show_denoise_active(context) + sub.prop(cscene, "denoiser", text="Denoiser") + col.prop(cscene, "denoising_input_passes", text="Passes") 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.active = has_oidn_gpu_devices(context) row.prop(cscene, "denoising_use_gpu", text="Use GPU") diff --git a/intern/cycles/blender/device.cpp b/intern/cycles/blender/device.cpp index 18c4026bffe..f39f90d5646 100644 --- a/intern/cycles/blender/device.cpp +++ b/intern/cycles/blender/device.cpp @@ -61,84 +61,8 @@ void static adjust_device_info_from_preferences(DeviceInfo &info, PointerRNA cpr } } -DeviceInfo blender_device_info(BL::Preferences &b_preferences, - BL::Scene &b_scene, - bool background, - bool preview) +void static adjust_device_info(DeviceInfo &device, PointerRNA cpreferences, bool preview) { - PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles"); - - /* Find cycles preferences. */ - PointerRNA cpreferences; - for (BL::Addon &b_addon : b_preferences.addons) { - if (b_addon.module() == "cycles") { - cpreferences = b_addon.preferences().ptr; - break; - } - } - - /* Default to CPU device. */ - DeviceInfo device = Device::available_devices(DEVICE_MASK_CPU).front(); - - if (BlenderSession::device_override != DEVICE_MASK_ALL) { - vector devices = Device::available_devices(BlenderSession::device_override); - - if (devices.empty()) { - device = Device::dummy_device("Found no Cycles device of the specified type"); - } - else { - int threads = blender_device_threads(b_scene); - device = Device::get_multi_device(devices, threads, background); - } - } - else if (get_enum(cscene, "device") == 1) { - /* Test if we are using GPU devices. */ - ComputeDevice compute_device = (ComputeDevice)get_enum( - cpreferences, "compute_device_type", COMPUTE_DEVICE_NUM, COMPUTE_DEVICE_CPU); - - if (compute_device != COMPUTE_DEVICE_CPU) { - /* Query GPU devices with matching types. */ - uint mask = DEVICE_MASK_CPU; - if (compute_device == COMPUTE_DEVICE_CUDA) { - mask |= DEVICE_MASK_CUDA; - } - else if (compute_device == COMPUTE_DEVICE_OPTIX) { - mask |= DEVICE_MASK_OPTIX; - } - else if (compute_device == COMPUTE_DEVICE_HIP) { - mask |= DEVICE_MASK_HIP; - } - else if (compute_device == COMPUTE_DEVICE_METAL) { - mask |= DEVICE_MASK_METAL; - } - else if (compute_device == COMPUTE_DEVICE_ONEAPI) { - mask |= DEVICE_MASK_ONEAPI; - } - vector devices = Device::available_devices(mask); - - /* Match device preferences and available devices. */ - vector used_devices; - RNA_BEGIN (&cpreferences, device, "devices") { - if (get_boolean(device, "use")) { - string id = get_string(device, "id"); - foreach (DeviceInfo &info, devices) { - if (info.id == id) { - used_devices.push_back(info); - break; - } - } - } - } - RNA_END; - - if (!used_devices.empty()) { - int threads = blender_device_threads(b_scene); - device = Device::get_multi_device(used_devices, threads, background); - } - /* Else keep using the CPU device that was set before. */ - } - } - adjust_device_info_from_preferences(device, cpreferences); foreach (DeviceInfo &info, device.multi_devices) { adjust_device_info_from_preferences(info, cpreferences); @@ -162,6 +86,106 @@ DeviceInfo blender_device_info(BL::Preferences &b_preferences, KERNEL_OPTIMIZATION_NUM_LEVELS, KERNEL_OPTIMIZATION_LEVEL_FULL); } +} + +DeviceInfo blender_device_info(BL::Preferences &b_preferences, + BL::Scene &b_scene, + bool background, + bool preview, + DeviceInfo &preferences_device) +{ + PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles"); + + /* Find cycles preferences. */ + PointerRNA cpreferences; + for (BL::Addon &b_addon : b_preferences.addons) { + if (b_addon.module() == "cycles") { + cpreferences = b_addon.preferences().ptr; + break; + } + } + + /* Default to CPU device. */ + DeviceInfo cpu_device = Device::available_devices(DEVICE_MASK_CPU).front(); + + /* Device, which is choosen in the Blender Preferences. */ + preferences_device = DeviceInfo(); + + /* Test if we are using GPU devices. */ + ComputeDevice compute_device = (ComputeDevice)get_enum( + cpreferences, "compute_device_type", COMPUTE_DEVICE_NUM, COMPUTE_DEVICE_CPU); + + if (compute_device != COMPUTE_DEVICE_CPU) { + /* Query GPU devices with matching types. */ + uint mask = DEVICE_MASK_CPU; + if (compute_device == COMPUTE_DEVICE_CUDA) { + mask |= DEVICE_MASK_CUDA; + } + else if (compute_device == COMPUTE_DEVICE_OPTIX) { + mask |= DEVICE_MASK_OPTIX; + } + else if (compute_device == COMPUTE_DEVICE_HIP) { + mask |= DEVICE_MASK_HIP; + } + else if (compute_device == COMPUTE_DEVICE_METAL) { + mask |= DEVICE_MASK_METAL; + } + else if (compute_device == COMPUTE_DEVICE_ONEAPI) { + mask |= DEVICE_MASK_ONEAPI; + } + vector devices = Device::available_devices(mask); + + /* Match device preferences and available devices. */ + vector used_devices; + RNA_BEGIN (&cpreferences, device, "devices") { + if (get_boolean(device, "use")) { + string id = get_string(device, "id"); + foreach (DeviceInfo &info, devices) { + if (info.id == id) { + used_devices.push_back(info); + break; + } + } + } + } + RNA_END; + + if (!used_devices.empty()) { + int threads = blender_device_threads(b_scene); + preferences_device = Device::get_multi_device(used_devices, threads, background); + } + } + else { + preferences_device = cpu_device; + } + adjust_device_info(preferences_device, cpreferences, preview); + adjust_device_info(cpu_device, cpreferences, preview); + + /* Device, which will be used, according to Settings, Scene preferences and command line + * parameters. */ + DeviceInfo device; + + if (BlenderSession::device_override != DEVICE_MASK_ALL) { + vector devices = Device::available_devices(BlenderSession::device_override); + + if (devices.empty()) { + device = Device::dummy_device("Found no Cycles device of the specified type"); + } + else { + int threads = blender_device_threads(b_scene); + device = Device::get_multi_device(devices, threads, background); + } + adjust_device_info(device, cpreferences, preview); + } + else { + /* 1 is a "GPU compute" in properties.py for Scene settings. */ + if (get_enum(cscene, "device") == 1) { + device = preferences_device; + } + else { + device = cpu_device; + } + } return device; } diff --git a/intern/cycles/blender/device.h b/intern/cycles/blender/device.h index e6adbb2dc6c..fbe9e22fcb9 100644 --- a/intern/cycles/blender/device.h +++ b/intern/cycles/blender/device.h @@ -17,11 +17,14 @@ CCL_NAMESPACE_BEGIN /* Get number of threads to use for rendering. */ int blender_device_threads(BL::Scene &b_scene); -/* Convert Blender settings to device specification. */ +/* Convert Blender settings to device specification. In addition, preferences_device contains the + * device chosen in Cycles global preferences, which is useful for the denoiser device selection. + */ DeviceInfo blender_device_info(BL::Preferences &b_preferences, BL::Scene &b_scene, bool background, - bool preview); + bool preview, + DeviceInfo &preferences_device); CCL_NAMESPACE_END diff --git a/intern/cycles/blender/python.cpp b/intern/cycles/blender/python.cpp index 6bff3ee7aba..cf20db4f385 100644 --- a/intern/cycles/blender/python.cpp +++ b/intern/cycles/blender/python.cpp @@ -417,7 +417,7 @@ 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(6); + PyObject *device_tuple = PyTuple_New(7); 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())); @@ -425,6 +425,7 @@ static PyObject *available_devices_func(PyObject * /*self*/, PyObject *args) 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(device_tuple, 6, PyBool_FromLong(device.denoisers & DENOISER_OPTIX)); PyTuple_SET_ITEM(ret, i, device_tuple); } @@ -754,7 +755,9 @@ static PyObject *denoise_func(PyObject * /*self*/, PyObject *args, PyObject *key PointerRNA sceneptr = RNA_id_pointer_create((ID *)PyLong_AsVoidPtr(pyscene)); BL::Scene b_scene(sceneptr); - DeviceInfo device = blender_device_info(b_preferences, b_scene, true, true); + DeviceInfo preferences_device; + DeviceInfo pathtrace_device = blender_device_info( + b_preferences, b_scene, true, true, preferences_device); /* Get denoising parameters from view layer. */ PointerRNA viewlayerptr = RNA_pointer_create( @@ -790,7 +793,10 @@ static PyObject *denoise_func(PyObject * /*self*/, PyObject *args, PyObject *key } /* Create denoiser. */ - DenoiserPipeline denoiser(device, params); + /* We are using preference device here, because path trace device will be identical to it unless + * scene is setting CPU render or command line override render device. But both of this options + * are for render, not for denoising. */ + DenoiserPipeline denoiser(preferences_device, params); denoiser.input = input; denoiser.output = output; diff --git a/intern/cycles/blender/sync.cpp b/intern/cycles/blender/sync.cpp index 2f177731fd6..689ee8de285 100644 --- a/intern/cycles/blender/sync.cpp +++ b/intern/cycles/blender/sync.cpp @@ -882,7 +882,7 @@ SessionParams BlenderSync::get_session_params(BL::RenderEngine &b_engine, /* Device */ params.threads = blender_device_threads(b_scene); params.device = blender_device_info( - b_preferences, b_scene, params.background, b_engine.is_preview()); + b_preferences, b_scene, params.background, b_engine.is_preview(), params.preferences_device); /* samples */ int samples = get_int(cscene, "samples"); diff --git a/intern/cycles/integrator/denoiser.cpp b/intern/cycles/integrator/denoiser.cpp index 99c1bbf0bf7..08bd12a08a0 100644 --- a/intern/cycles/integrator/denoiser.cpp +++ b/intern/cycles/integrator/denoiser.cpp @@ -16,30 +16,32 @@ CCL_NAMESPACE_BEGIN -unique_ptr Denoiser::create(Device *path_trace_device, const DenoiseParams ¶ms) +unique_ptr Denoiser::create(Device *denoise_device, const DenoiseParams ¶ms) { DCHECK(params.use); + if (denoise_device->info.type != DEVICE_CPU) { #ifdef WITH_OPTIX - if (params.type == DENOISER_OPTIX && Device::available_devices(DEVICE_MASK_OPTIX).size()) { - return make_unique(path_trace_device, params); - } + if (params.type == DENOISER_OPTIX) { + return make_unique(denoise_device, params); + } #endif #ifdef WITH_OPENIMAGEDENOISE - /* 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); - } + /* If available and allowed, then we will use OpenImageDenoise on GPU, otherwise on CPU. */ + if (params.type == DENOISER_OPENIMAGEDENOISE && params.use_gpu && + OIDNDenoiserGPU::is_device_supported(denoise_device->info)) + { + return make_unique(denoise_device, params); + } #endif + } /* Always fallback to OIDN. */ DenoiseParams oidn_params = params; oidn_params.type = DENOISER_OPENIMAGEDENOISE; - return make_unique(path_trace_device, oidn_params); + oidn_params.use_gpu = false; + return make_unique(denoise_device, oidn_params); } Denoiser::Denoiser(Device *path_trace_device, const DenoiseParams ¶ms) @@ -67,13 +69,23 @@ const DenoiseParams &Denoiser::get_params() const bool Denoiser::load_kernels(Progress *progress) { - const Device *denoiser_device = ensure_denoiser_device(progress); + Device *denoiser_device = ensure_denoiser_device(progress); if (!denoiser_device) { path_trace_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"; + } + path_trace_device_->set_error(message); + return false; + } + VLOG_WORK << "Will denoise on " << denoiser_device->info.description << " (" << denoiser_device->info.id << ")"; diff --git a/intern/cycles/integrator/denoiser.h b/intern/cycles/integrator/denoiser.h index 04d161cea39..6b2308b651e 100644 --- a/intern/cycles/integrator/denoiser.h +++ b/intern/cycles/integrator/denoiser.h @@ -33,7 +33,7 @@ class Denoiser { * - The denoiser must be configured. This means that `params.use` must be true. * This is checked in debug builds. * - The device might be MultiDevice. */ - static unique_ptr create(Device *path_trace_device, const DenoiseParams ¶ms); + static unique_ptr create(Device *denoise_device, const DenoiseParams ¶ms); virtual ~Denoiser() = default; diff --git a/intern/cycles/integrator/path_trace.cpp b/intern/cycles/integrator/path_trace.cpp index f627466c9a7..f67869ab2fb 100644 --- a/intern/cycles/integrator/path_trace.cpp +++ b/intern/cycles/integrator/path_trace.cpp @@ -22,11 +22,13 @@ CCL_NAMESPACE_BEGIN PathTrace::PathTrace(Device *device, + Device *denoise_device, Film *film, DeviceScene *device_scene, RenderScheduler &render_scheduler, TileManager &tile_manager) : device_(device), + denoise_device_(denoise_device), film_(film), device_scene_(device_scene), render_scheduler_(render_scheduler), @@ -483,15 +485,25 @@ void PathTrace::adaptive_sample(RenderWork &render_work) void PathTrace::set_denoiser_params(const DenoiseParams ¶ms) { - render_scheduler_.set_denoiser_params(params); - + bool need_to_recreate_denoiser = false; if (!params.use) { denoiser_.reset(); - return; } - - if (denoiser_) { + else if (denoiser_) { const DenoiseParams old_denoiser_params = denoiser_->get_params(); + + const bool is_cpu_denoising = old_denoiser_params.type == DENOISER_OPENIMAGEDENOISE && + old_denoiser_params.use_gpu == false; + const bool requested_gpu_denoising = params.type == DENOISER_OPTIX || + (params.type == DENOISER_OPENIMAGEDENOISE && + params.use_gpu == true); + if (requested_gpu_denoising && is_cpu_denoising && denoise_device_->info.type == DEVICE_CPU) { + /* It won't be possible to use GPU denoising when according to user settings we have + * only CPU as available denoising device. So we just exiting early to avoid + * unnecessary denoiser recreation or parameters update. */ + return; + } + const bool is_same_denoising_device_type = old_denoiser_params.use_gpu == params.use_gpu; /* 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. */ @@ -499,16 +511,30 @@ void PathTrace::set_denoiser_params(const DenoiseParams ¶ms) (is_same_denoising_device_type || params.type == DENOISER_OPTIX)) { denoiser_->set_params(params); - return; + } + else { + need_to_recreate_denoiser = true; } } + else { + /* if there is no denoiser and param.use is true, then we need to create it. */ + need_to_recreate_denoiser = true; + } - denoiser_ = Denoiser::create(device_, params); + if (need_to_recreate_denoiser) { + denoiser_ = Denoiser::create(denoise_device_, params); - /* Only take into account the "immediate" cancel to have interactive rendering responding to - * navigation as quickly as possible, but allow to run denoiser after user hit Escape key while - * doing offline rendering. */ - denoiser_->is_cancelled_cb = [this]() { return render_cancel_.is_requested; }; + /* Only take into account the "immediate" cancel to have interactive rendering responding to + * navigation as quickly as possible, but allow to run denoiser after user hit Escape key while + * doing offline rendering. */ + denoiser_->is_cancelled_cb = [this]() { return render_cancel_.is_requested; }; + } + + /* Use actual parameters, if available */ + if (denoise_device_) + render_scheduler_.set_denoiser_params(denoiser_->get_params()); + else + render_scheduler_.set_denoiser_params(params); } void PathTrace::set_adaptive_sampling(const AdaptiveSampling &adaptive_sampling) diff --git a/intern/cycles/integrator/path_trace.h b/intern/cycles/integrator/path_trace.h index 5473235dc5a..0c880509c84 100644 --- a/intern/cycles/integrator/path_trace.h +++ b/intern/cycles/integrator/path_trace.h @@ -45,6 +45,7 @@ class PathTrace { /* Render scheduler is used to report timing information and access things like start/finish * sample. */ PathTrace(Device *device, + Device *denoiser_device, Film *film, DeviceScene *device_scene, RenderScheduler &render_scheduler, @@ -251,6 +252,10 @@ class PathTrace { * are configured this is a `MultiDevice`. */ Device *device_ = nullptr; + /* Pointer to a device which is configured to be used for denoising. Can be identical + * to the device */ + Device *denoise_device_ = nullptr; + /* CPU device for creating temporary render buffers on the CPU side. */ unique_ptr cpu_device_; diff --git a/intern/cycles/session/denoising.cpp b/intern/cycles/session/denoising.cpp index d4f14ccbe3a..1b86c4547bc 100644 --- a/intern/cycles/session/denoising.cpp +++ b/intern/cycles/session/denoising.cpp @@ -599,13 +599,13 @@ bool DenoiseImage::save_output(const string &out_filepath, string &error) /* File pattern handling and outer loop over frames */ -DenoiserPipeline::DenoiserPipeline(DeviceInfo &device_info, const DenoiseParams ¶ms) +DenoiserPipeline::DenoiserPipeline(DeviceInfo &denoiser_device_info, const DenoiseParams ¶ms) { /* Initialize task scheduler. */ TaskScheduler::init(); /* Initialize device. */ - device = Device::create(device_info, stats, profiler); + device = Device::create(denoiser_device_info, stats, profiler); device->load_kernels(KERNEL_FEATURE_DENOISING); denoiser = Denoiser::create(device, params); diff --git a/intern/cycles/session/denoising.h b/intern/cycles/session/denoising.h index 12b32c1144c..9b3b2568cc8 100644 --- a/intern/cycles/session/denoising.h +++ b/intern/cycles/session/denoising.h @@ -25,7 +25,7 @@ CCL_NAMESPACE_BEGIN class DenoiserPipeline { public: - DenoiserPipeline(DeviceInfo &device_info, const DenoiseParams ¶ms); + DenoiserPipeline(DeviceInfo &denoiser_device_info, const DenoiseParams ¶ms); ~DenoiserPipeline(); bool run(); diff --git a/intern/cycles/session/session.cpp b/intern/cycles/session/session.cpp index 81b507db2d3..7b0d605edfb 100644 --- a/intern/cycles/session/session.cpp +++ b/intern/cycles/session/session.cpp @@ -50,9 +50,20 @@ Session::Session(const SessionParams ¶ms_, const SceneParams &scene_params) scene = new Scene(scene_params, device); + if (params.device == params.preferences_device) { + denoise_device = device; + } + else { + denoise_device = Device::create(params.preferences_device, stats, profiler); + + if (denoise_device->have_error()) { + progress.set_error(denoise_device->error_message()); + } + } + /* Configure path tracer. */ path_trace_ = make_unique( - device, scene->film, &scene->dscene, render_scheduler_, tile_manager_); + device, denoise_device, scene->film, &scene->dscene, render_scheduler_, tile_manager_); path_trace_->set_progress(&progress); path_trace_->progress_update_cb = [&]() { update_status_time(); }; @@ -91,6 +102,9 @@ Session::~Session() /* Destroy scene and device. */ delete scene; + if (denoise_device != device) { + delete denoise_device; + } delete device; /* Stop task scheduler. */ diff --git a/intern/cycles/session/session.h b/intern/cycles/session/session.h index 2a8b2481b48..636dcbe92c2 100644 --- a/intern/cycles/session/session.h +++ b/intern/cycles/session/session.h @@ -35,7 +35,11 @@ class SceneParams; class SessionParams { public: + /* Device, which is choosen based on Blender Cycles preferences, as well as Scene settings and + * command line arguments. */ DeviceInfo device; + /* Device from Cycles preferences. */ + DeviceInfo preferences_device; bool headless; bool background; @@ -104,6 +108,8 @@ class SessionParams { class Session { public: Device *device; + /* Denoiser device. Could be the same as the path trace device. */ + Device *denoise_device; Scene *scene; Progress progress; SessionParams params; -- 2.30.2