Skip to content

Commit 37aabd0

Browse files
committed
Re-add optional OIDN denoise as an external dynamic library.
1 parent c2b9167 commit 37aabd0

File tree

4 files changed

+196
-2
lines changed

4 files changed

+196
-2
lines changed

doc/classes/EditorSettings.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,9 @@
500500
If [code]true[/code], when saving a file, the editor will rename the old file to a different name, save a new file, then only remove the old file once the new file has been saved. This makes loss of data less likely to happen if the editor or operating system exits unexpectedly while saving (e.g. due to a crash or power outage).
501501
[b]Note:[/b] On Windows, this feature can interact negatively with certain antivirus programs. In this case, you may have to set this to [code]false[/code] to prevent file locking issues.
502502
</member>
503+
<member name="filesystem/tools/oidn/oidn_library_path" type="String" setter="" getter="">
504+
The path to the directory containing the OIDN denoise dynamic library.
505+
</member>
503506
<member name="interface/editor/accept_dialog_cancel_ok_buttons" type="int" setter="" getter="">
504507
How to position the Cancel and OK buttons in the editor's [AcceptDialog]s. Different platforms have different standard behaviors for this, which can be overridden using this setting. This is useful if you use Godot both on Windows and macOS/Linux and your Godot muscle memory is stronger than your OS specific one.
505508
- [b]Auto[/b] follows the platform convention: Cancel first on macOS and Linux, OK first on Windows.

editor/editor_settings.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,9 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
519519
EDITOR_SETTING_USAGE(Variant::FLOAT, PROPERTY_HINT_RANGE, "filesystem/import/blender/rpc_server_uptime", 5, "0,300,1,or_greater,suffix:s", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)
520520
EDITOR_SETTING_USAGE(Variant::STRING, PROPERTY_HINT_GLOBAL_FILE, "filesystem/import/fbx/fbx2gltf_path", "", "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)
521521

522+
// Tools (denoise)
523+
EDITOR_SETTING_USAGE(Variant::STRING, PROPERTY_HINT_GLOBAL_DIR, "filesystem/tools/oidn/oidn_library_path", "", "", PROPERTY_USAGE_DEFAULT)
524+
522525
/* Docks */
523526

524527
// SceneTree

modules/lightmapper_rd/lightmapper_rd.cpp

Lines changed: 125 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636

3737
#include "core/config/project_settings.h"
3838
#include "core/math/geometry_2d.h"
39+
#include "editor/editor_settings.h"
3940
#include "servers/rendering/rendering_device_binds.h"
4041

4142
//uncomment this if you want to see textures from all the process saved
@@ -671,6 +672,118 @@ LightmapperRD::BakeError LightmapperRD::_dilate(RenderingDevice *rd, Ref<RDShade
671672
return BAKE_OK;
672673
}
673674

675+
bool LightmapperRD::_load_oidn(const String &p_library_path) {
676+
if (oidn_lib_path == p_library_path) {
677+
return oidn_lib_handle != nullptr;
678+
}
679+
oidn_lib_path = p_library_path;
680+
681+
String lib_path;
682+
if (OS::get_singleton()->get_name() == "macOS") {
683+
lib_path = oidn_lib_path.path_join("libOpenImageDenoise.dylib");
684+
} else if (OS::get_singleton()->get_name() == "Windows") {
685+
lib_path = oidn_lib_path.path_join("OpenImageDenoise.dll");
686+
} else {
687+
lib_path = oidn_lib_path.path_join("libOpenImageDenoise.so");
688+
}
689+
690+
_unload_oidn();
691+
692+
if (OS::get_singleton()->open_dynamic_library(lib_path, oidn_lib_handle, true) != OK) {
693+
oidn_lib_handle = nullptr;
694+
ERR_PRINT(vformat("Failed to load %s.", lib_path));
695+
return false;
696+
}
697+
bool symbols_ok = true;
698+
symbols_ok = symbols_ok && (OS::get_singleton()->get_dynamic_library_symbol_handle(oidn_lib_handle, "oidnNewDevice", (void *&)oidnNewDevice) == OK);
699+
symbols_ok = symbols_ok && (OS::get_singleton()->get_dynamic_library_symbol_handle(oidn_lib_handle, "oidnCommitDevice", (void *&)oidnCommitDevice) == OK);
700+
symbols_ok = symbols_ok && (OS::get_singleton()->get_dynamic_library_symbol_handle(oidn_lib_handle, "oidnNewFilter", (void *&)oidnNewFilter) == OK);
701+
symbols_ok = symbols_ok && (OS::get_singleton()->get_dynamic_library_symbol_handle(oidn_lib_handle, "oidnSetSharedFilterImage", (void *&)oidnSetSharedFilterImage) == OK);
702+
symbols_ok = symbols_ok && (OS::get_singleton()->get_dynamic_library_symbol_handle(oidn_lib_handle, "oidnSetFilterBool", (void *&)oidnSetFilterBool) == OK);
703+
symbols_ok = symbols_ok && (OS::get_singleton()->get_dynamic_library_symbol_handle(oidn_lib_handle, "oidnCommitFilter", (void *&)oidnCommitFilter) == OK);
704+
symbols_ok = symbols_ok && (OS::get_singleton()->get_dynamic_library_symbol_handle(oidn_lib_handle, "oidnExecuteFilter", (void *&)oidnExecuteFilter) == OK);
705+
symbols_ok = symbols_ok && (OS::get_singleton()->get_dynamic_library_symbol_handle(oidn_lib_handle, "oidnGetDeviceError", (void *&)oidnGetDeviceError) == OK);
706+
symbols_ok = symbols_ok && (OS::get_singleton()->get_dynamic_library_symbol_handle(oidn_lib_handle, "oidnReleaseFilter", (void *&)oidnReleaseFilter) == OK);
707+
symbols_ok = symbols_ok && (OS::get_singleton()->get_dynamic_library_symbol_handle(oidn_lib_handle, "oidnReleaseDevice", (void *&)oidnReleaseDevice) == OK);
708+
if (!symbols_ok) {
709+
ERR_PRINT("Failed to load OIDN symbols.");
710+
_unload_oidn();
711+
return false;
712+
}
713+
714+
oidn_device = oidnNewDevice(OIDN_DEVICE_TYPE_CPU);
715+
oidnCommitDevice(oidn_device);
716+
const char *msg = nullptr;
717+
if (oidnGetDeviceError(oidn_device, &msg) != OIDN_ERROR_NONE) {
718+
ERR_PRINT("Failed to load OIDN device.");
719+
_unload_oidn();
720+
return false;
721+
}
722+
723+
return true;
724+
}
725+
726+
void LightmapperRD::_unload_oidn() {
727+
if (oidn_lib_handle) {
728+
if (oidn_device) {
729+
oidnReleaseDevice(oidn_device);
730+
oidn_device = nullptr;
731+
}
732+
733+
OS::get_singleton()->close_dynamic_library(oidn_lib_handle);
734+
oidn_lib_handle = nullptr;
735+
}
736+
}
737+
738+
LightmapperRD::BakeError LightmapperRD::_denoise_oidn(RenderingDevice *p_rd, RID p_source_light_tex, RID p_source_normal_tex, RID p_dest_light_tex, const Size2i &p_atlas_size, int p_atlas_slices, bool p_bake_sh) {
739+
for (int i = 0; i < p_atlas_slices; i++) {
740+
Vector<uint8_t> sn = p_rd->texture_get_data(p_source_normal_tex, i);
741+
Ref<Image> imgn = Image::create_from_data(p_atlas_size.width, p_atlas_size.height, false, Image::FORMAT_RGBAH, sn);
742+
imgn->convert(Image::FORMAT_RGBF);
743+
Vector<uint8_t> datan = imgn->get_data();
744+
745+
for (int j = 0; j < (p_bake_sh ? 4 : 1); j++) {
746+
int index = i * (p_bake_sh ? 4 : 1) + j;
747+
748+
Vector<uint8_t> sl = p_rd->texture_get_data(p_source_light_tex, index);
749+
750+
Ref<Image> imgl = Image::create_from_data(p_atlas_size.width, p_atlas_size.height, false, Image::FORMAT_RGBAH, sl);
751+
imgl->convert(Image::FORMAT_RGBF);
752+
Vector<uint8_t> datal = imgl->get_data();
753+
754+
void *filter = oidnNewFilter(oidn_device, "RTLightmap");
755+
oidnSetSharedFilterImage(filter, "color", (void *)datal.ptrw(), OIDN_FORMAT_FLOAT3, imgl->get_width(), imgl->get_height(), 0, 0, 0);
756+
oidnSetSharedFilterImage(filter, "normal", (void *)datan.ptrw(), OIDN_FORMAT_FLOAT3, imgn->get_width(), imgn->get_height(), 0, 0, 0);
757+
oidnSetSharedFilterImage(filter, "output", (void *)datal.ptrw(), OIDN_FORMAT_FLOAT3, imgl->get_width(), imgl->get_height(), 0, 0, 0);
758+
oidnSetFilterBool(filter, "hdr", true);
759+
oidnCommitFilter(filter);
760+
oidnExecuteFilter(filter);
761+
762+
const char *msg = nullptr;
763+
if (oidnGetDeviceError(oidn_device, &msg) != OIDN_ERROR_NONE) {
764+
oidnReleaseFilter(filter);
765+
ERR_FAIL_V_MSG(BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES, String(msg));
766+
}
767+
oidnReleaseFilter(filter);
768+
769+
imgl->set_data(imgl->get_width(), imgl->get_height(), false, imgl->get_format(), datal);
770+
imgl->convert(Image::FORMAT_RGBAH);
771+
Vector<uint8_t> ds = imgl->get_data();
772+
imgl.unref(); // Avoid copy on write.
773+
{ // Restore alpha.
774+
uint32_t count = sl.size() / 2;
775+
const uint16_t *src = (const uint16_t *)sl.ptr();
776+
uint16_t *dst = (uint16_t *)ds.ptrw();
777+
for (uint32_t k = 0; k < count; k += 4) {
778+
dst[k + 3] = src[k + 3];
779+
}
780+
}
781+
p_rd->texture_update(p_dest_light_tex, index, ds);
782+
}
783+
}
784+
return BAKE_OK;
785+
}
786+
674787
LightmapperRD::BakeError LightmapperRD::_denoise(RenderingDevice *p_rd, Ref<RDShaderFile> &p_compute_shader, const RID &p_compute_base_uniform_set, PushConstant &p_push_constant, RID p_source_light_tex, RID p_source_normal_tex, RID p_dest_light_tex, float p_denoiser_strength, const Size2i &p_atlas_size, int p_atlas_slices, bool p_bake_sh, BakeStepFunc p_step_function) {
675788
RID denoise_params_buffer = p_rd->uniform_buffer_create(sizeof(DenoiseParams));
676789
DenoiseParams denoise_params;
@@ -1501,8 +1614,14 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
15011614
}
15021615

15031616
{
1504-
SWAP(light_accum_tex, light_accum_tex2);
1505-
BakeError error = _denoise(rd, compute_shader, compute_base_uniform_set, push_constant, light_accum_tex2, normal_tex, light_accum_tex, p_denoiser_strength, atlas_size, atlas_slices, p_bake_sh, p_step_function);
1617+
String oidn_path = EDITOR_GET("filesystem/tools/oidn/oidn_library_path");
1618+
BakeError error;
1619+
if (!oidn_path.is_empty() && _load_oidn(oidn_path)) {
1620+
error = _denoise_oidn(rd, light_accum_tex, normal_tex, light_accum_tex, atlas_size, atlas_slices, p_bake_sh);
1621+
} else {
1622+
SWAP(light_accum_tex, light_accum_tex2);
1623+
error = _denoise(rd, compute_shader, compute_base_uniform_set, push_constant, light_accum_tex2, normal_tex, light_accum_tex, p_denoiser_strength, atlas_size, atlas_slices, p_bake_sh, p_step_function);
1624+
}
15061625
if (unlikely(error != BAKE_OK)) {
15071626
return error;
15081627
}
@@ -1766,3 +1885,7 @@ Vector<Color> LightmapperRD::get_bake_probe_sh(int p_probe) const {
17661885

17671886
LightmapperRD::LightmapperRD() {
17681887
}
1888+
1889+
LightmapperRD::~LightmapperRD() {
1890+
_unload_oidn();
1891+
}

modules/lightmapper_rd/lightmapper_rd.h

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,70 @@
3636
#include "scene/resources/mesh.h"
3737
#include "servers/rendering/rendering_device.h"
3838

39+
typedef enum {
40+
OIDN_DEVICE_TYPE_DEFAULT = 0, // select device automatically
41+
42+
OIDN_DEVICE_TYPE_CPU = 1, // CPU device
43+
OIDN_DEVICE_TYPE_SYCL = 2, // SYCL device
44+
OIDN_DEVICE_TYPE_CUDA = 3, // CUDA device
45+
OIDN_DEVICE_TYPE_HIP = 4, // HIP device
46+
} OIDNDeviceType;
47+
48+
typedef enum {
49+
OIDN_FORMAT_UNDEFINED = 0,
50+
51+
// 32-bit single-precision floating-point scalar and vector formats
52+
OIDN_FORMAT_FLOAT = 1,
53+
OIDN_FORMAT_FLOAT2,
54+
OIDN_FORMAT_FLOAT3,
55+
OIDN_FORMAT_FLOAT4,
56+
57+
// 16-bit half-precision floating-point scalar and vector formats
58+
OIDN_FORMAT_HALF = 257,
59+
OIDN_FORMAT_HALF2,
60+
OIDN_FORMAT_HALF3,
61+
OIDN_FORMAT_HALF4,
62+
} OIDNFormat;
63+
64+
typedef enum {
65+
OIDN_ERROR_NONE = 0, // no error occurred
66+
OIDN_ERROR_UNKNOWN = 1, // an unknown error occurred
67+
OIDN_ERROR_INVALID_ARGUMENT = 2, // an invalid argument was specified
68+
OIDN_ERROR_INVALID_OPERATION = 3, // the operation is not allowed
69+
OIDN_ERROR_OUT_OF_MEMORY = 4, // not enough memory to execute the operation
70+
OIDN_ERROR_UNSUPPORTED_HARDWARE = 5, // the hardware (e.g. CPU) is not supported
71+
OIDN_ERROR_CANCELLED = 6, // the operation was cancelled by the user
72+
} OIDNError;
73+
74+
typedef void *(*oidnNewDevicePtr)(OIDNDeviceType type);
75+
typedef void (*oidnCommitDevicePtr)(void *device);
76+
typedef void *(*oidnNewFilterPtr)(void *device, const char *type);
77+
typedef void (*oidnSetSharedFilterImagePtr)(void *filter, const char *name, void *devPtr, OIDNFormat format, size_t width, size_t height, size_t byteOffset, size_t pixelByteStride, size_t rowByteStride);
78+
typedef void (*oidnSetFilterBoolPtr)(void *filter, const char *name, bool value);
79+
typedef void (*oidnCommitFilterPtr)(void *filter);
80+
typedef void (*oidnExecuteFilterPtr)(void *filter);
81+
typedef OIDNError (*oidnGetDeviceErrorPtr)(void *device, const char **outMessage);
82+
typedef void (*oidnReleaseFilterPtr)(void *filter);
83+
typedef void (*oidnReleaseDevicePtr)(void *device);
84+
3985
class RDShaderFile;
4086
class LightmapperRD : public Lightmapper {
4187
GDCLASS(LightmapperRD, Lightmapper)
4288

89+
String oidn_lib_path;
90+
void *oidn_lib_handle = nullptr;
91+
void *oidn_device = nullptr;
92+
oidnNewDevicePtr oidnNewDevice = nullptr;
93+
oidnCommitDevicePtr oidnCommitDevice = nullptr;
94+
oidnNewFilterPtr oidnNewFilter = nullptr;
95+
oidnSetSharedFilterImagePtr oidnSetSharedFilterImage = nullptr;
96+
oidnSetFilterBoolPtr oidnSetFilterBool = nullptr;
97+
oidnCommitFilterPtr oidnCommitFilter = nullptr;
98+
oidnExecuteFilterPtr oidnExecuteFilter = nullptr;
99+
oidnGetDeviceErrorPtr oidnGetDeviceError = nullptr;
100+
oidnReleaseFilterPtr oidnReleaseFilter = nullptr;
101+
oidnReleaseDevicePtr oidnReleaseDevice = nullptr;
102+
43103
struct MeshInstance {
44104
MeshData data;
45105
int slice = 0;
@@ -246,6 +306,10 @@ class LightmapperRD : public Lightmapper {
246306
BakeError _dilate(RenderingDevice *rd, Ref<RDShaderFile> &compute_shader, RID &compute_base_uniform_set, PushConstant &push_constant, RID &source_light_tex, RID &dest_light_tex, const Size2i &atlas_size, int atlas_slices);
247307
BakeError _denoise(RenderingDevice *p_rd, Ref<RDShaderFile> &p_compute_shader, const RID &p_compute_base_uniform_set, PushConstant &p_push_constant, RID p_source_light_tex, RID p_source_normal_tex, RID p_dest_light_tex, float p_denoiser_strength, const Size2i &p_atlas_size, int p_atlas_slices, bool p_bake_sh, BakeStepFunc p_step_function);
248308

309+
bool _load_oidn(const String &p_library_path);
310+
void _unload_oidn();
311+
BakeError _denoise_oidn(RenderingDevice *p_rd, RID p_source_light_tex, RID p_source_normal_tex, RID p_dest_light_tex, const Size2i &p_atlas_size, int p_atlas_slices, bool p_bake_sh);
312+
249313
public:
250314
virtual void add_mesh(const MeshData &p_mesh) override;
251315
virtual void add_directional_light(bool p_static, const Vector3 &p_direction, const Color &p_color, float p_energy, float p_angular_distance, float p_shadow_blur) override;
@@ -265,6 +329,7 @@ class LightmapperRD : public Lightmapper {
265329
Vector<Color> get_bake_probe_sh(int p_probe) const override;
266330

267331
LightmapperRD();
332+
~LightmapperRD();
268333
};
269334

270335
#endif // LIGHTMAPPER_RD_H

0 commit comments

Comments
 (0)