OverviewFilesCommitsIssuesDocsReleasesPeers

nvidia-rtx-godot commits / b322b43

Add NGX DLSS backend for Linux (DLSS-SR and DLSS-RR)
Adds a direct NGX DLSS backend as an alternative to Streamline, which cannot be ported to Linux (relies on Windows DLL interposition). Key changes: - dlss.cpp: new #elif defined(NGX_DLSS_ENABLED) branch with NGX init, DLSS-SR/RR feature creation, and per-frame evaluate - SConstruct: use_ngx_dlss=yes build option (Linux x86_64 only) - rendering_device_driver_vulkan.cpp: register VK_NVX_binary_import, VK_NVX_image_view_handle, VK_KHR_push_descriptor extensions - renderer_viewport.cpp: viewport scaling mode for NGX path Linux-specific engineering notes: - volk defines vkGetInstanceProcAddr as a global variable (function pointer), not a real function. When libnvsdk_ngx.a (statically linked) resolves this ELF symbol it gets the variable address and tries to call it as code, causing SIGSEGV. Fix: use dlsym(libvulkan.so.1) to get real driver functions and pass them to NVSDK_NGX_VULKAN_Init. - wchar_t is 4 bytes on Linux (UTF-32), not 2 as on Windows. NGX search paths must be widened char-by-char from UTF-8, not reinterpreted. - NGX .so search paths are resolved relative to the binary location (thirdparty/ngx/lib/linux_x86_64 alongside bin/), not hardcoded. - DLSS-SR/RR feature creation omits the AutoExposure flag; Godot's auto_exposure handles it via tonemapping. Motion vectors are scaled from UV space to pixel space (render resolution) as DLSS expects. Build: scons platform=linuxbsd use_streamline=no use_ngx_dlss=yes Tested on RTX 2070 (Turing), driver 570.211.01, DLSS SDK 310.5.3. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
b322b43af381777a690e063afe780af3e9c69902
by plankatron <fleeky@gmail.com> · 2026-04-21T01:21:27.000Z

Changed files (4) +554 -3

SConstruct +13 -0
……@@ -201,6 +201,7 @@ opts.Add(BoolVariable("metal", "Enable the Metal rendering driver on supported p
201201opts.Add(BoolVariable("use_volk", "Use the volk library to load the Vulkan loader dynamically", True))
202202opts.Add(BoolVariable("use_streamline", "Enable Streamline support", True))
203203opts.Add(BoolVariable("use_aftermath", "Enable NVIDIA Nsight Aftermath GPU crash dump support", False))
204opts.Add(BoolVariable("use_ngx_dlss", "Enable NVIDIA NGX DLSS on Linux (bypasses Streamline)", True))
204205opts.Add(BoolVariable("accesskit", "Enable the AccessKit driver for screen reader support", True))
205206opts.Add(BoolVariable("angle", "Enable the ANGLE rendering driver for OpenGL ES 3.0 on supported platforms", True))
206207opts.Add(BoolVariable("sdl", "Enable the SDL3 input driver", True))
……@@ -633,6 +634,18 @@ if env["use_aftermath"]:
633634 if env["use_aftermath"]:
634635 env.AppendUnique(CPPDEFINES=["AFTERMATH_ENABLED"])
635636
637if env["use_ngx_dlss"]:
638 if env["platform"] == "linuxbsd" and env["arch"] == "x86_64":
639 ngx_dir = "#thirdparty/ngx"
640 env.AppendUnique(CPPDEFINES=["NGX_DLSS_ENABLED"])
641 env.Append(CPPPATH=[ngx_dir + "/include"])
642 # NGX headers include <vulkan/vulkan.h> — ensure Godot's bundled Vulkan headers are findable
643 env.Append(CPPPATH=["#thirdparty/vulkan/include"])
644 env.Append(LIBPATH=[ngx_dir + "/lib/linux_x86_64"])
645 env.Append(LIBS=["nvsdk_ngx"])
646 else:
647 env["use_ngx_dlss"] = False
648
636649# Library Support
637650if env["library_type"] != "executable":
638651 if "library" not in env.get("supported", []):
drivers/vulkan/rendering_device_driver_vulkan.cpp +9 -0
……@@ -617,6 +617,15 @@ Error RenderingDeviceDriverVulkan::_initialize_device_extensions() {
617617#endif
618618 _register_requested_device_extension(VK_EXT_DEVICE_FAULT_EXTENSION_NAME, false);
619619
620#ifdef NGX_DLSS_ENABLED
621 // DLSS requires these NVIDIA-specific Vulkan extensions for binary shader loading
622 // and image view handle access. On Windows, Streamline's interposer adds these
623 // automatically; on Linux with direct NGX, we must register them explicitly.
624 _register_requested_device_extension(VK_NVX_BINARY_IMPORT_EXTENSION_NAME, false);
625 _register_requested_device_extension(VK_NVX_IMAGE_VIEW_HANDLE_EXTENSION_NAME, false);
626 _register_requested_device_extension(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME, false);
627#endif
628
620629 {
621630 // Debug marker extensions.
622631 // Should be last element in the array.
servers/rendering/renderer_rd/effects/dlss.cpp +529 -0
……@@ -39,6 +39,16 @@
3939
4040#ifdef ENABLE_DLSS
4141#include "drivers/streamline/streamline_context.h"
42#elif defined(NGX_DLSS_ENABLED)
43#define ENABLE_NGX_DLSS 1
44#include <dlfcn.h>
45#include <string>
46#include "core/io/dir_access.h"
47#include "core/os/os.h"
48#include "drivers/vulkan/godot_vulkan.h"
49#include "nvsdk_ngx_vk.h"
50#include "nvsdk_ngx_helpers_vk.h"
51#include "nvsdk_ngx_helpers_dlssd_vk.h"
4252#endif
4353
4454using namespace RendererRD;
……@@ -579,7 +589,526 @@ bool DLSSEffect::is_ready(DLSSContext *p_context) {
579589 }
580590 return true;
581591}
592#elif defined(ENABLE_NGX_DLSS)
593
594// ============================================================================
595// NGX DLSS backend for Linux — bypasses Streamline, talks directly to NGX SDK
596// ============================================================================
597
598static bool g_ngx_initialized = false;
599static NVSDK_NGX_Parameter *g_ngx_params = nullptr;
600static bool g_ngx_dlss_rr_available = false;
601static bool g_ngx_dlss_sr_available = false;
602
603namespace RendererRD {
604class NGXDLSSContext : public DLSSContext {
605public:
606 NVSDK_NGX_Handle *dlss_rr_handle = nullptr;
607 NVSDK_NGX_Handle *dlss_sr_handle = nullptr;
608 bool feature_created = false;
609 NVSDK_NGX_PerfQuality_Value perf_quality = NVSDK_NGX_PerfQuality_Value_Balanced;
610 uint32_t render_width = 0;
611 uint32_t render_height = 0;
612 uint32_t output_width = 0;
613 uint32_t output_height = 0;
614
615 virtual ~NGXDLSSContext() {
616 if (dlss_rr_handle) {
617 NVSDK_NGX_VULKAN_ReleaseFeature(dlss_rr_handle);
618 dlss_rr_handle = nullptr;
619 }
620 if (dlss_sr_handle) {
621 NVSDK_NGX_VULKAN_ReleaseFeature(dlss_sr_handle);
622 dlss_sr_handle = nullptr;
623 }
624 }
625};
626} // namespace RendererRD
627
628static void _ngx_log_callback(const char *message, NVSDK_NGX_Logging_Level level, NVSDK_NGX_Feature source) {
629 print_line(vformat("[NGX LOG] [%d] %s", (int)level, String(message)));
630}
631
632static bool _ngx_ensure_init() {
633 if (g_ngx_initialized) {
634 return true;
635 }
636
637 RenderingDevice *rd = RD::get_singleton();
638 if (!rd) {
639 return false;
640 }
641
642 VkInstance vk_instance = (VkInstance)rd->get_driver_resource(RD::DriverResource::DRIVER_RESOURCE_TOPMOST_OBJECT, RID(), 0);
643 VkPhysicalDevice vk_phys_device = (VkPhysicalDevice)rd->get_driver_resource(RD::DriverResource::DRIVER_RESOURCE_PHYSICAL_DEVICE, RID(), 0);
644 VkDevice vk_device = (VkDevice)rd->get_driver_resource(RD::DriverResource::DRIVER_RESOURCE_LOGICAL_DEVICE, RID(), 0);
645
646 if (!vk_instance || !vk_phys_device || !vk_device) {
647 ERR_PRINT("[NGX DLSS] Failed to get Vulkan handles from Godot RenderingDevice");
648 return false;
649 }
650
651 print_line("[NGX DLSS] Vulkan handles acquired, initializing NGX...");
652
653 // Create the log/data directory
654 DirAccess::make_dir_absolute("/tmp/ngx_dlss_logs");
655
656 // Tell NGX where to find DLSS feature .so files (libnvidia-ngx-dlss*.so)
657 // Search order:
658 // 1) next to executable (shipped game: game.x86_64 + .so in same dir)
659 // 2) ../thirdparty/ngx/lib/linux_x86_64 (dev build: binary in bin/, .so at repo root)
660 String exe_dir = OS::get_singleton()->get_executable_path().get_base_dir();
661 String ngx_shipped = exe_dir;
662 String ngx_dev = exe_dir.path_join("../thirdparty/ngx/lib/linux_x86_64").simplify_path();
663
664 // Convert to wchar_t for NGX API
665 // On Linux wchar_t is 4 bytes (UTF-32), so widen char-by-char from UTF-8
666 auto to_wstring = [](const String &s) -> std::wstring {
667 CharString utf8 = s.utf8();
668 std::wstring result;
669 result.reserve(utf8.length());
670 for (int i = 0; i < utf8.length(); i++) {
671 result.push_back((wchar_t)(unsigned char)utf8[i]);
672 }
673 return result;
674 };
675 static thread_local std::wstring shipped_ws, dev_ws;
676 shipped_ws = to_wstring(ngx_shipped);
677 dev_ws = to_wstring(ngx_dev);
678 const wchar_t *ngx_search_paths[] = {
679 shipped_ws.c_str(),
680 dev_ws.c_str(),
681 };
682 print_line(vformat("[NGX DLSS] Searching for DLSS .so in: %s, %s", ngx_shipped, ngx_dev));
683
684 NVSDK_NGX_FeatureCommonInfo feature_info = {};
685 memset(&feature_info, 0, sizeof(feature_info));
686 feature_info.PathListInfo.Path = ngx_search_paths;
687 feature_info.PathListInfo.Length = 2;
688 feature_info.LoggingInfo.MinimumLoggingLevel = NVSDK_NGX_LOGGING_LEVEL_VERBOSE;
689 feature_info.LoggingInfo.LoggingCallback = _ngx_log_callback;
690 feature_info.LoggingInfo.DisableOtherLoggingSinks = false;
691
692 // Get the REAL Vulkan function pointers from the driver, not volk globals.
693 // volk defines vkGetInstanceProcAddr as a global variable (function pointer),
694 // but libnvsdk_ngx.a (statically linked) resolves the symbol to the variable
695 // address and tries to call it as code — crashing on the pointer bytes.
696 // dlsym gives us the actual driver function address.
697 void *vulkan_lib = dlopen("libvulkan.so.1", RTLD_NOW | RTLD_NOLOAD);
698 if (!vulkan_lib) {
699 vulkan_lib = dlopen("libvulkan.so", RTLD_NOW | RTLD_NOLOAD);
700 }
701 ERR_FAIL_COND_V_MSG(!vulkan_lib, false, "[NGX DLSS] Cannot find loaded libvulkan.so for proc addr");
702
703 PFN_vkGetInstanceProcAddr real_vkGetInstanceProcAddr =
704 (PFN_vkGetInstanceProcAddr)dlsym(vulkan_lib, "vkGetInstanceProcAddr");
705 PFN_vkGetDeviceProcAddr real_vkGetDeviceProcAddr =
706 (PFN_vkGetDeviceProcAddr)dlsym(vulkan_lib, "vkGetDeviceProcAddr");
707 dlclose(vulkan_lib);
708
709 ERR_FAIL_COND_V_MSG(!real_vkGetInstanceProcAddr || !real_vkGetDeviceProcAddr, false,
710 "[NGX DLSS] dlsym failed for Vulkan proc addrs");
711
712 print_verbose(vformat("[NGX DLSS] Real vkGetInstanceProcAddr: %d, volk global: %d",
713 (uint64_t)real_vkGetInstanceProcAddr, (uint64_t)vkGetInstanceProcAddr));
714
715 // Use Init_with_ProjectID as the nvpro-samples reference does
716 NVSDK_NGX_Result result = NVSDK_NGX_VULKAN_Init_with_ProjectID(
717 "a0f57b54-1daf-4934-90ae-c4035c19df04", // Project ID (GUID format)
718 NVSDK_NGX_ENGINE_TYPE_CUSTOM,
719 "4.7.0", // Engine version
720 L"/tmp/ngx_dlss_logs",
721 vk_instance, vk_phys_device, vk_device,
722 real_vkGetInstanceProcAddr, real_vkGetDeviceProcAddr,
723 &feature_info,
724 NVSDK_NGX_Version_API);
725
726 if (NVSDK_NGX_FAILED(result)) {
727 ERR_PRINT(vformat("[NGX DLSS] NVSDK_NGX_VULKAN_Init_Ext2 failed: 0x%x", (uint32_t)result));
728 return false;
729 }
730
731 // Query required extensions (for diagnostics)
732 {
733 unsigned int inst_count = 0, dev_count = 0;
734 const char **inst_exts = nullptr, **dev_exts = nullptr;
735 NVSDK_NGX_Result ext_result = NVSDK_NGX_VULKAN_RequiredExtensions(&inst_count, &inst_exts, &dev_count, &dev_exts);
736 if (NVSDK_NGX_SUCCEED(ext_result)) {
737 print_line(vformat("[NGX DLSS] Required instance extensions: %d", inst_count));
738 for (unsigned int i = 0; i < inst_count; i++) {
739 print_line(vformat("[NGX DLSS] Instance: %s", String(inst_exts[i])));
740 }
741 print_line(vformat("[NGX DLSS] Required device extensions: %d", dev_count));
742 for (unsigned int i = 0; i < dev_count; i++) {
743 print_line(vformat("[NGX DLSS] Device: %s", String(dev_exts[i])));
744 }
745 } else {
746 print_line(vformat("[NGX DLSS] RequiredExtensions query failed: 0x%x", (uint32_t)ext_result));
747 }
748 }
749
750 // Allocate parameters (needed for feature creation/evaluation)
751 result = NVSDK_NGX_VULKAN_AllocateParameters(&g_ngx_params);
752 if (NVSDK_NGX_FAILED(result)) {
753 ERR_PRINT(vformat("[NGX DLSS] AllocateParameters failed: 0x%x", (uint32_t)result));
754 NVSDK_NGX_VULKAN_Shutdown1(nullptr);
755 return false;
756 }
757
758 // Skip capability parameter checks — they're unreliable on Linux.
759 // NGX loaded both dlss and dlssd .so files (confirmed via nvngx.log).
760 // We'll try creating features at render time and handle errors there.
761 g_ngx_dlss_sr_available = true;
762 g_ngx_dlss_rr_available = true;
763
764 g_ngx_initialized = true;
765 print_line("[NGX DLSS] Initialized successfully. Will attempt DLSS-RR then fall back to SR.");
766
767 return true;
768}
769
770static NVSDK_NGX_Resource_VK _ngx_make_resource(RID p_texture_rid, bool p_read_write) {
771 VkImageView vk_view = (VkImageView)RD::get_singleton()->get_driver_resource(RD::DriverResource::DRIVER_RESOURCE_TEXTURE_VIEW, p_texture_rid);
772 VkImage vk_image = (VkImage)RD::get_singleton()->get_driver_resource(RD::DriverResource::DRIVER_RESOURCE_TEXTURE, p_texture_rid);
773 VkFormat vk_format = (VkFormat)RD::get_singleton()->get_driver_resource(RD::DriverResource::DRIVER_RESOURCE_TEXTURE_DATA_FORMAT, p_texture_rid);
774 RD::TextureFormat tex_fmt = RD::get_singleton()->texture_get_format(p_texture_rid);
775
776 VkImageSubresourceRange subresource = {};
777 subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
778 subresource.baseMipLevel = 0;
779 subresource.levelCount = tex_fmt.mipmaps;
780 subresource.baseArrayLayer = 0;
781 subresource.layerCount = tex_fmt.array_layers;
782
783 return NVSDK_NGX_Create_ImageView_Resource_VK(
784 vk_view, vk_image, subresource, vk_format,
785 tex_fmt.width, tex_fmt.height, p_read_write);
786}
787
788DLSSEffect::DLSSEffect() {
789 Vector<String> modes;
790 modes.push_back("\n");
791 shaders.mvec_decode_shader.initialize(modes, "");
792 shaders.mvec_decode_version = shaders.mvec_decode_shader.version_create();
793 shaders.mvec_decode_pipeline = RD::get_singleton()->compute_pipeline_create(shaders.mvec_decode_shader.version_get_shader(shaders.mvec_decode_version, 0));
794}
795
796DLSSEffect::~DLSSEffect() {
797 shaders.mvec_decode_shader.version_free(shaders.mvec_decode_version);
798 if (g_ngx_initialized) {
799 if (g_ngx_params) {
800 NVSDK_NGX_VULKAN_DestroyParameters(g_ngx_params);
801 g_ngx_params = nullptr;
802 }
803 NVSDK_NGX_VULKAN_Shutdown1(nullptr);
804 g_ngx_initialized = false;
805 }
806}
807
808DLSSContext *DLSSEffect::create_context(Size2i p_internal_size, Size2i p_target_size) {
809 if (!_ngx_ensure_init()) {
810 ERR_PRINT("[NGX DLSS] Cannot create context — NGX init failed");
811 return nullptr;
812 }
813
814 RendererRD::NGXDLSSContext *ctx = memnew(RendererRD::NGXDLSSContext);
815 ctx->render_width = p_internal_size.width;
816 ctx->render_height = p_internal_size.height;
817 ctx->output_width = p_target_size.width;
818 ctx->output_height = p_target_size.height;
819
820 // Determine quality mode based on render/output ratio
821 float ratio = (float)p_internal_size.width / (float)p_target_size.width;
822 if (ratio >= 0.95f) {
823 ctx->perf_quality = NVSDK_NGX_PerfQuality_Value_DLAA;
824 } else if (ratio >= 0.66f) {
825 ctx->perf_quality = NVSDK_NGX_PerfQuality_Value_MaxQuality;
826 } else if (ratio >= 0.5f) {
827 ctx->perf_quality = NVSDK_NGX_PerfQuality_Value_Balanced;
828 } else if (ratio >= 0.33f) {
829 ctx->perf_quality = NVSDK_NGX_PerfQuality_Value_MaxPerf;
830 } else {
831 ctx->perf_quality = NVSDK_NGX_PerfQuality_Value_UltraPerformance;
832 }
833
834 print_line(vformat("[NGX DLSS] Context created: %dx%d -> %dx%d (quality: %d)",
835 p_internal_size.width, p_internal_size.height,
836 p_target_size.width, p_target_size.height, (int)ctx->perf_quality));
837
838 return ctx;
839}
840
841void DLSSEffect::upscale(const DLSSContext::Parameters &p_params) {
842 RendererRD::NGXDLSSContext *ctx = (RendererRD::NGXDLSSContext *)p_params.context;
843 if (!ctx) {
844 return;
845 }
846
847 // Delay warmup frames
848 if (ctx->delay > 0) {
849 --ctx->delay;
850 return;
851 }
852
853 ctx->last_parameters = p_params;
854 ctx->last_effect = this;
855
856 // Decode motion vectors (same as Streamline path)
857 {
858 RD::get_singleton()->draw_command_begin_label("Decode Invalid Motion Vectors");
859 UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
860 ERR_FAIL_NULL(uniform_set_cache);
861 MaterialStorage *material_storage = MaterialStorage::get_singleton();
862 ERR_FAIL_NULL(material_storage);
863
864 RD::Uniform u_velocity_image(RD::UNIFORM_TYPE_IMAGE, 0, p_params.velocity);
865 RD::Uniform u_depth_texture(RD::UNIFORM_TYPE_TEXTURE, 0, p_params.depth);
866
867 RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
868 RID shader = shaders.mvec_decode_shader.version_get_shader(shaders.mvec_decode_version, 0);
869 ERR_FAIL_COND(shader.is_null());
870
871 RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, shaders.mvec_decode_pipeline);
872 RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_velocity_image), 0);
873 RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_depth_texture), 1);
874
875 auto texture_format = RD::get_singleton()->texture_get_format(p_params.velocity);
876
877 float push_constants[20];
878 push_constants[0] = texture_format.width;
879 push_constants[1] = texture_format.height;
880 push_constants[2] = 0.0f;
881 push_constants[3] = 0.0f;
882 memcpy(push_constants + 4, &p_params.reprojection.columns[0].x, sizeof(float) * 16);
883 RD::get_singleton()->compute_list_set_push_constant(compute_list, push_constants, sizeof(push_constants));
884
885 RD::get_singleton()->compute_list_dispatch_threads(compute_list, texture_format.width, texture_format.height, 1);
886 RD::get_singleton()->compute_list_add_barrier(compute_list);
887
888 RD::get_singleton()->compute_list_end();
889 RD::get_singleton()->draw_command_end_label();
890 }
891
892 // Inject NGX DLSS into the render graph via callback
893 RD::CallbackResource res[8];
894 int num_resources = 0;
895 res[num_resources++].rid = p_params.color;
896 res[num_resources++].rid = p_params.output;
897 res[num_resources++].rid = p_params.depth;
898 res[num_resources++].rid = p_params.velocity;
899
900 if (p_params.dlss_rr) {
901 if (p_params.dlss_rr_diffuse_albedo.is_valid()) {
902 res[num_resources++].rid = p_params.dlss_rr_diffuse_albedo;
903 }
904 if (p_params.dlss_rr_specular_albedo.is_valid()) {
905 res[num_resources++].rid = p_params.dlss_rr_specular_albedo;
906 }
907 if (p_params.dlss_rr_normal_roughness.is_valid()) {
908 res[num_resources++].rid = p_params.dlss_rr_normal_roughness;
909 }
910 if (p_params.dlss_rr_specular_hit_dist.is_valid()) {
911 res[num_resources++].rid = p_params.dlss_rr_specular_hit_dist;
912 }
913 }
914
915 for (int i = 0; i < num_resources; i++) {
916 res[i].usage = RD::CALLBACK_RESOURCE_USAGE_TEXTURE_SAMPLE;
917 }
918 RD::get_singleton()->driver_callback_add((RDD::DriverCallback)DLSSEffect::_upscale_internal_graph_callback, p_params.context, VectorView<RD::CallbackResource>(res, num_resources));
919}
920
921void DLSSEffect::_upscale_internal(RDD::CommandBufferID cmdid, const DLSSContext::Parameters &p_params) {
922 RendererRD::NGXDLSSContext *ctx = (RendererRD::NGXDLSSContext *)p_params.context;
923
924 VkCommandBuffer vk_cmd = (VkCommandBuffer)RD::get_singleton()->get_device_driver()->command_buffer_get_native_handle(cmdid);
925 VkDevice vk_device = (VkDevice)RD::get_singleton()->get_driver_resource(RD::DriverResource::DRIVER_RESOURCE_LOGICAL_DEVICE, RID(), 0);
926
927 bool use_dlss_rr = p_params.dlss_rr && g_ngx_dlss_rr_available;
928
929 // Create NGX feature on first use
930 if (!ctx->feature_created) {
931 NVSDK_NGX_Result result;
932
933 // Try DLSS-RR (Ray Reconstruction / denoiser) first if requested
934 if (use_dlss_rr && g_ngx_dlss_rr_available) {
935 NVSDK_NGX_DLSSD_Create_Params dlssd_create = {};
936 dlssd_create.InWidth = ctx->render_width;
937 dlssd_create.InHeight = ctx->render_height;
938 dlssd_create.InTargetWidth = ctx->output_width;
939 dlssd_create.InTargetHeight = ctx->output_height;
940 dlssd_create.InPerfQualityValue = ctx->perf_quality;
941 // No AutoExposure: Godot's auto_exposure handles it via tonemapping.
942 // Double-exposure (DLSS internal + Godot) causes progressive white blowout.
943 dlssd_create.InFeatureCreateFlags =
944 NVSDK_NGX_DLSS_Feature_Flags_IsHDR |
945 NVSDK_NGX_DLSS_Feature_Flags_MVLowRes |
946 NVSDK_NGX_DLSS_Feature_Flags_DepthInverted;
947
948 result = NGX_VULKAN_CREATE_DLSSD_EXT1(
949 VK_NULL_HANDLE, vk_cmd, 1, 1,
950 &ctx->dlss_rr_handle, g_ngx_params, &dlssd_create);
951
952 if (NVSDK_NGX_SUCCEED(result)) {
953 ctx->feature_created = true;
954 print_line("[NGX DLSS] DLSS-RR feature created successfully");
955 } else {
956 WARN_PRINT(vformat("[NGX DLSS] DLSS-RR creation failed (0x%x), falling back to DLSS-SR", (uint32_t)result));
957 ctx->dlss_rr_handle = nullptr;
958 use_dlss_rr = false;
959 }
960 }
961
962 // Fall back to DLSS-SR (Super Resolution / upscaler only)
963 if (!ctx->feature_created && g_ngx_dlss_sr_available) {
964 NVSDK_NGX_DLSS_Create_Params dlss_create = {};
965 dlss_create.Feature.InWidth = ctx->render_width;
966 dlss_create.Feature.InHeight = ctx->render_height;
967 dlss_create.Feature.InTargetWidth = ctx->output_width;
968 dlss_create.Feature.InTargetHeight = ctx->output_height;
969 dlss_create.Feature.InPerfQualityValue = ctx->perf_quality;
970 dlss_create.InFeatureCreateFlags =
971 NVSDK_NGX_DLSS_Feature_Flags_IsHDR |
972 NVSDK_NGX_DLSS_Feature_Flags_MVLowRes |
973 NVSDK_NGX_DLSS_Feature_Flags_DepthInverted;
974
975 result = NGX_VULKAN_CREATE_DLSS_EXT(
976 vk_cmd, 1, 1,
977 &ctx->dlss_sr_handle, g_ngx_params, &dlss_create);
978
979 if (NVSDK_NGX_FAILED(result)) {
980 ERR_PRINT(vformat("[NGX DLSS] DLSS-SR feature creation failed: 0x%x", (uint32_t)result));
981 return;
982 }
983 ctx->feature_created = true;
984 use_dlss_rr = false;
985 print_line("[NGX DLSS] DLSS-SR feature created successfully");
986 }
987
988 if (!ctx->feature_created) {
989 return;
990 }
991 }
992
993 // Evaluate DLSS
994 if (use_dlss_rr && ctx->dlss_rr_handle) {
995 // DLSS Ray Reconstruction evaluation
996 NVSDK_NGX_Resource_VK color_res = _ngx_make_resource(p_params.color, false);
997 NVSDK_NGX_Resource_VK output_res = _ngx_make_resource(p_params.output, true);
998 NVSDK_NGX_Resource_VK depth_res = _ngx_make_resource(p_params.depth, false);
999 NVSDK_NGX_Resource_VK mvec_res = _ngx_make_resource(p_params.velocity, false);
1000
1001 NVSDK_NGX_VK_DLSSD_Eval_Params eval = {};
1002 memset(&eval, 0, sizeof(eval));
1003 eval.pInColor = &color_res;
1004 eval.pInOutput = &output_res;
1005 eval.pInDepth = &depth_res;
1006 eval.pInMotionVectors = &mvec_res;
1007 eval.InJitterOffsetX = p_params.jitter.x;
1008 eval.InJitterOffsetY = p_params.jitter.y;
1009 eval.InRenderSubrectDimensions.Width = ctx->render_width;
1010 eval.InRenderSubrectDimensions.Height = ctx->render_height;
1011 eval.InReset = p_params.reset_accumulation ? 1 : 0;
1012 // Motion vectors are in UV space (0-1), DLSS expects pixel space — scale by render resolution
1013 eval.InMVScaleX = (float)ctx->render_width;
1014 eval.InMVScaleY = (float)ctx->render_height;
1015 eval.InFrameTimeDeltaInMsec = p_params.delta_time * 1000.0f;
1016
1017 // G-buffer inputs for DLSS-RR (stack-allocated, valid for duration of evaluate call)
1018 NVSDK_NGX_Resource_VK diffuse_res, specular_res, normal_res, hit_dist_res;
1019 if (p_params.dlss_rr_diffuse_albedo.is_valid()) {
1020 diffuse_res = _ngx_make_resource(p_params.dlss_rr_diffuse_albedo, false);
1021 eval.pInDiffuseAlbedo = &diffuse_res;
1022 }
1023 if (p_params.dlss_rr_specular_albedo.is_valid()) {
1024 specular_res = _ngx_make_resource(p_params.dlss_rr_specular_albedo, false);
1025 eval.pInSpecularAlbedo = &specular_res;
1026 }
1027 if (p_params.dlss_rr_normal_roughness.is_valid()) {
1028 normal_res = _ngx_make_resource(p_params.dlss_rr_normal_roughness, false);
1029 eval.pInNormals = &normal_res;
1030 eval.pInRoughness = &normal_res; // Packed mode: roughness in W channel of normals
1031 }
1032 if (p_params.dlss_rr_specular_hit_dist.is_valid()) {
1033 hit_dist_res = _ngx_make_resource(p_params.dlss_rr_specular_hit_dist, false);
1034 eval.pInSpecularHitDistance = &hit_dist_res;
1035 }
1036
1037 // View/projection matrices for DLSS-RR
1038 Transform3D view_matrix = p_params.cam_transform.affine_inverse();
1039 Projection view_proj(view_matrix);
1040 float world_to_view[16];
1041 float view_to_clip[16];
1042 for (int i = 0; i < 4; i++) {
1043 world_to_view[i * 4 + 0] = view_proj.columns[0][i];
1044 world_to_view[i * 4 + 1] = view_proj.columns[1][i];
1045 world_to_view[i * 4 + 2] = view_proj.columns[2][i];
1046 world_to_view[i * 4 + 3] = view_proj.columns[3][i];
1047 }
1048 for (int i = 0; i < 4; i++) {
1049 view_to_clip[i * 4 + 0] = p_params.cam_projection.columns[0][i];
1050 view_to_clip[i * 4 + 1] = p_params.cam_projection.columns[1][i];
1051 view_to_clip[i * 4 + 2] = p_params.cam_projection.columns[2][i];
1052 view_to_clip[i * 4 + 3] = p_params.cam_projection.columns[3][i];
1053 }
1054 eval.pInWorldToViewMatrix = world_to_view;
1055 eval.pInViewToClipMatrix = view_to_clip;
1056
1057 NVSDK_NGX_Result result = NGX_VULKAN_EVALUATE_DLSSD_EXT(vk_cmd, ctx->dlss_rr_handle, g_ngx_params, &eval);
1058
1059 if (NVSDK_NGX_FAILED(result)) {
1060 ERR_PRINT(vformat("[NGX DLSS] DLSS-RR evaluate failed: 0x%x", (uint32_t)result));
1061 }
1062 } else if (ctx->dlss_sr_handle) {
1063 // DLSS Super Resolution evaluation
1064 NVSDK_NGX_Resource_VK color_res = _ngx_make_resource(p_params.color, false);
1065 NVSDK_NGX_Resource_VK output_res = _ngx_make_resource(p_params.output, true);
1066 NVSDK_NGX_Resource_VK depth_res = _ngx_make_resource(p_params.depth, false);
1067 NVSDK_NGX_Resource_VK mvec_res = _ngx_make_resource(p_params.velocity, false);
1068
1069 NVSDK_NGX_VK_DLSS_Eval_Params eval = {};
1070 memset(&eval, 0, sizeof(eval));
1071 eval.Feature.pInColor = &color_res;
1072 eval.Feature.pInOutput = &output_res;
1073 eval.Feature.InSharpness = p_params.sharpness;
1074 eval.pInDepth = &depth_res;
1075 eval.pInMotionVectors = &mvec_res;
1076 eval.InJitterOffsetX = p_params.jitter.x;
1077 eval.InJitterOffsetY = p_params.jitter.y;
1078 eval.InRenderSubrectDimensions.Width = ctx->render_width;
1079 eval.InRenderSubrectDimensions.Height = ctx->render_height;
1080 eval.InReset = p_params.reset_accumulation ? 1 : 0;
1081 // Motion vectors are in UV space (0-1), DLSS expects pixel space — scale by render resolution
1082 eval.InMVScaleX = (float)ctx->render_width;
1083 eval.InMVScaleY = (float)ctx->render_height;
1084
1085 NVSDK_NGX_Result result = NGX_VULKAN_EVALUATE_DLSS_EXT(vk_cmd, ctx->dlss_sr_handle, g_ngx_params, &eval);
1086 if (NVSDK_NGX_FAILED(result)) {
1087 ERR_PRINT(vformat("[NGX DLSS] DLSS-SR evaluate failed: 0x%x", (uint32_t)result));
1088 }
1089 }
1090}
1091
1092void RendererRD::DLSSEffect::_upscale_internal_graph_callback(RenderingDeviceDriver *p_driver, RDD::CommandBufferID p_command_buffer, void *p_userdata) {
1093 RendererRD::NGXDLSSContext *self = (RendererRD::NGXDLSSContext *)p_userdata;
1094 self->last_effect->_upscale_internal(p_command_buffer, self->last_parameters);
1095}
1096
1097bool DLSSEffect::is_ready(DLSSContext *p_context) {
1098 RendererRD::NGXDLSSContext *ctx = (RendererRD::NGXDLSSContext *)p_context;
1099 if (!ctx || !g_ngx_initialized) {
1100 return false;
1101 }
1102 if (ctx->delay > 0) {
1103 return false;
1104 }
1105 return true;
1106}
1107
5821108#else
1109// ============================================================================
1110// No DLSS backend available — stub implementation
1111// ============================================================================
5831112DLSSEffect::DLSSEffect() {}
5841113DLSSEffect::~DLSSEffect() {}
5851114DLSSContext *DLSSEffect::create_context(Size2i p_internal_size, Size2i p_target_size) {
servers/rendering/renderer_viewport.cpp +3 -3
……@@ -1050,9 +1050,9 @@ void RendererViewport::viewport_set_scaling_3d_mode(RID p_viewport, RSE::Viewpor
10501050 WARN_PRINT_ONCE_ED("MetalFX Spatial 3D scaling is only available when using the Forward+ or Mobile renderer.");
10511051 }
10521052 if (p_mode == RSE::VIEWPORT_SCALING_3D_MODE_DLSS) {
1053#ifndef STREAMLINE_ENABLED
1054 ERR_PRINT_ONCE_ED("DLSS is not available because Streamline support was not compiled into the engine.");
1055#else
1053#if !defined(STREAMLINE_ENABLED) && !defined(NGX_DLSS_ENABLED)
1054 ERR_PRINT_ONCE_ED("DLSS is not available because neither Streamline nor NGX DLSS support was compiled into the engine.");
1055#elif defined(STREAMLINE_ENABLED)
10561056 if (StreamlineContext::get().slInit == nullptr) {
10571057 ERR_PRINT_ONCE_ED("DLSS is not available because Streamline failed to initialize (sl.interposer.dll not found or failed to load). Please download the Streamline SDK from GitHub (NVIDIA-RTX/Streamline) and make sure the Streamline SDK binaries are in the same directory as the Godot executable, and restart Godot.");
10581058 }