// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // NVIDIA DLSS through NVIDIA Streamline, with Streamline's manual hooking. #include #include #include #include #include #include #include #include #include #include #include "common/fs/fs_util.h" #include "common/fs/path_util.h" #include "common/logging.h" #include "common/settings.h" #include "video_core/vulkan_common/streamline.h" #include "video_core/vulkan_common/streamline_api.h" #include #include #include namespace Vulkan::Streamline { namespace { // A custom engine names itself with its own project id; an application id from NVIDIA is only // needed without an engine type. Keep it fixed: the driver may key behaviour off it. constexpr const char* PROJECT_ID = "8d3e2f9a-6b41-4c7e-9a52-1f0c3b7d5e64"; constexpr const wchar_t* INTERPOSER = L"sl.interposer.dll"; constexpr const wchar_t* MODEL = L"nvngx_dlss.dll"; // The first DLSS with preset K. constexpr u32 MIN_DLSS_MAJOR = 310; using Version = std::array; struct State { HMODULE module{}; Api api{}; std::wstring plugin_dir; std::wstring model_dir; std::vector instance_extensions; std::vector device_extensions; bool started{}; bool attached{}; }; State& GetState() { static State state; return state; } int LogLevel() { const char* const value = std::getenv("EDEN_NX2_DLSS_LOG"); return value ? std::atoi(value) : 0; } void LogFromStreamline(sl::LogType type, const char* message) { std::string_view text{message ? message : ""}; while (!text.empty() && (text.back() == '\n' || text.back() == '\r')) { text.remove_suffix(1); } switch (type) { case sl::LogType::eError: LOG_ERROR(Render_Vulkan, "Streamline: {}", text); break; case sl::LogType::eWarn: LOG_WARNING(Render_Vulkan, "Streamline: {}", text); break; default: if (LogLevel() > 0) { LOG_INFO(Render_Vulkan, "Streamline: {}", text); } break; } } /// Where the user may put sl.interposer.dll: EDEN_STREAMLINE_DIR, the user folder's "streamline" /// folder, then a "streamline" folder next to the executable. std::vector SearchDirs() { std::vector dirs; if (const char* const dir = std::getenv("EDEN_STREAMLINE_DIR"); dir && *dir) { dirs.emplace_back(dir); } dirs.push_back(UserFolder()); dirs.push_back(Common::FS::GetExeDirectory() / "streamline"); return dirs; } template bool Resolve(HMODULE module, const char* name, T*& function) { function = reinterpret_cast(GetProcAddress(module, name)); if (!function) { LOG_ERROR(Render_Vulkan, "sl.interposer.dll has no {}", name); } return function != nullptr; } std::optional ReadVersion(const std::filesystem::path& path) { DWORD handle = 0; const DWORD size = GetFileVersionInfoSizeW(path.c_str(), &handle); if (size == 0) { return std::nullopt; } std::vector data(size); VS_FIXEDFILEINFO* info{}; UINT length = 0; if (!GetFileVersionInfoW(path.c_str(), 0, size, data.data()) || !VerQueryValueW(data.data(), L"\\", reinterpret_cast(&info), &length) || !info) { return std::nullopt; } return Version{HIWORD(info->dwFileVersionMS), LOWORD(info->dwFileVersionMS), HIWORD(info->dwFileVersionLS), LOWORD(info->dwFileVersionLS)}; } std::string VersionText(const std::filesystem::path& path) { const std::optional version = path.empty() ? std::nullopt : ReadVersion(path); if (!version) { return {}; } const Version& v = *version; return v[3] == 0 ? fmt::format("{}.{}.{}", v[0], v[1], v[2]) : fmt::format("{}.{}.{}.{}", v[0], v[1], v[2], v[3]); } /// The NVIDIA app keeps every DLSS it downloads under ProgramData; pick the newest. std::filesystem::path FindAppModel() { const wchar_t* const program_data = _wgetenv(L"ProgramData"); if (!program_data) { return {}; } const std::filesystem::path root = std::filesystem::path{program_data} / L"NVIDIA" / L"NGX" / L"models" / L"dlss" / L"versions"; std::filesystem::path best_path; Version best{}; std::error_code ec; for (const auto& version_dir : std::filesystem::directory_iterator(root, ec)) { std::error_code files_ec; for (const auto& file : std::filesystem::directory_iterator(version_dir.path() / L"files", files_ec)) { if (file.path().extension() != L".bin") { continue; } const std::optional version = ReadVersion(file.path()); if (version && (*version)[0] >= MIN_DLSS_MAJOR && *version > best) { best = *version; best_path = file.path(); } } } return best_path; } /// Copies the NVIDIA app's DLSS into our folder as nvngx_dlss.dll, the name Streamline loads. std::filesystem::path UseAppModel(const DlssFiles& files) { const std::filesystem::path dir = UserFolder() / L"nvidia-app"; const std::filesystem::path target = dir / MODEL; std::error_code ec; const bool current = std::filesystem::is_regular_file(target, ec) && std::filesystem::file_size(target, ec) == std::filesystem::file_size(files.app_model, ec) && ReadVersion(target) == ReadVersion(files.app_model); if (!current) { if (!IsSignedByNvidia(files.app_model)) { LOG_WARNING(Render_Vulkan, "The NVIDIA app's DLSS {} is not signed by NVIDIA", Common::FS::PathToUTF8String(files.app_model)); return {}; } std::filesystem::create_directories(dir, ec); std::filesystem::copy_file(files.app_model, target, std::filesystem::copy_options::overwrite_existing, ec); if (ec) { LOG_WARNING(Render_Vulkan, "Could not copy the NVIDIA app's DLSS to {}: {}", Common::FS::PathToUTF8String(target), ec.message()); return {}; } } return dir; } bool Load(State& state, const std::filesystem::path& path) { if (!IsStreamlineModule(path)) { LOG_ERROR(Render_Vulkan, "{} is not signed by NVIDIA; not loading it", Common::FS::PathToUTF8String(path)); return false; } state.module = LoadLibraryW(path.c_str()); if (!state.module) { LOG_ERROR(Render_Vulkan, "Could not load {} (error {})", Common::FS::PathToUTF8String(path), GetLastError()); return false; } Api& api = state.api; const HMODULE m = state.module; if (Resolve(m, "slInit", api.init) && Resolve(m, "slShutdown", api.shutdown) && Resolve(m, "slIsFeatureSupported", api.is_feature_supported) && Resolve(m, "slGetFeatureRequirements", api.get_feature_requirements) && Resolve(m, "slGetFeatureVersion", api.get_feature_version) && Resolve(m, "slGetFeatureFunction", api.get_feature_function) && Resolve(m, "slGetNewFrameToken", api.get_new_frame_token) && Resolve(m, "slSetConstants", api.set_constants) && Resolve(m, "slEvaluateFeature", api.evaluate_feature) && Resolve(m, "slFreeResources", api.free_resources) && Resolve(m, "slSetVulkanInfo", api.set_vulkan_info)) { return true; } FreeLibrary(state.module); state.module = nullptr; state.api = {}; return false; } std::vector Names(uint32_t count, const char** names) { std::vector result; for (uint32_t i = 0; i < count; ++i) { result.emplace_back(names[i]); } return result; } } // Anonymous namespace std::filesystem::path UserFolder() { return Common::FS::GetEdenPath(Common::FS::EdenPath::StreamlineDir); } bool IsStreamlineModule(const std::filesystem::path& path) { return sl::security::verifyEmbeddedSignature(path.c_str()); } bool IsSignedByNvidia(const std::filesystem::path& path) { const std::wstring file_path = std::filesystem::absolute(path).wstring(); WINTRUST_FILE_INFO file{}; file.cbStruct = sizeof(file); file.pcwszFilePath = file_path.c_str(); WINTRUST_DATA data{}; data.cbStruct = sizeof(data); data.dwUIChoice = WTD_UI_NONE; data.fdwRevocationChecks = WTD_REVOKE_NONE; data.dwUnionChoice = WTD_CHOICE_FILE; data.pFile = &file; data.dwStateAction = WTD_STATEACTION_VERIFY; data.dwProvFlags = WTD_CACHE_ONLY_URL_RETRIEVAL; GUID action = WINTRUST_ACTION_GENERIC_VERIFY_V2; bool valid = WinVerifyTrust(nullptr, &action, &data) == ERROR_SUCCESS; if (valid) { CRYPT_PROVIDER_DATA* const provider = WTHelperProvDataFromStateData(data.hWVTStateData); CRYPT_PROVIDER_SGNR* const signer = provider ? WTHelperGetProvSignerFromChain(provider, 0, FALSE, 0) : nullptr; CRYPT_PROVIDER_CERT* const cert = signer ? WTHelperGetProvCertFromChain(signer, 0) : nullptr; wchar_t name[128]{}; valid = cert && CertGetNameStringW(cert->pCert, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, nullptr, name, static_cast(std::size(name))) > 1 && std::wstring_view{name} == L"NVIDIA Corporation"; } data.dwStateAction = WTD_STATEACTION_CLOSE; WinVerifyTrust(nullptr, &action, &data); return valid; } DlssFiles FindDlssFiles() { DlssFiles files; files.folder = UserFolder(); std::error_code ec; for (const std::filesystem::path& dir : SearchDirs()) { if (std::filesystem::is_regular_file(dir / INTERPOSER, ec)) { files.folder = dir; files.interposer = dir / INTERPOSER; break; } } if (std::filesystem::is_regular_file(files.folder / MODEL, ec)) { files.model = files.folder / MODEL; } files.app_model = FindAppModel(); files.interposer_version = VersionText(files.interposer); files.model_version = VersionText(files.model); files.app_model_version = VersionText(files.app_model); return files; } bool Start() { State& state = GetState(); if (state.started) { return true; } if (!Settings::values.nvidia_dlss.GetValue()) { return false; } if (!state.module) { const DlssFiles files = FindDlssFiles(); if (files.interposer.empty()) { LOG_WARNING(Render_Vulkan, "NVIDIA DLSS is on, but sl.interposer.dll (NVIDIA Streamline) is not in " "{} (the Graphics settings can download it); using FSR 3.1", Common::FS::PathToUTF8String(files.folder)); return false; } if (!Load(state, files.interposer)) { return false; } state.plugin_dir = files.folder.wstring(); state.model_dir.clear(); if (files.model.empty() && !files.app_model.empty()) { if (const std::filesystem::path dir = UseAppModel(files); !dir.empty()) { state.model_dir = dir.wstring(); LOG_INFO(Render_Vulkan, "Using the NVIDIA app's DLSS {}", files.app_model_version); } } else if (files.model.empty()) { LOG_WARNING(Render_Vulkan, "No nvngx_dlss.dll in {} and no DLSS from the NVIDIA app", Common::FS::PathToUTF8String(files.folder)); } } // Streamline's plugins come from the loader's folder or the NVIDIA app's downloads (OTA); the // DLSS model from the loader's folder, else the NVIDIA app's copy. const wchar_t* plugin_paths[] = {state.plugin_dir.c_str(), state.model_dir.c_str()}; const sl::Feature features[] = {sl::kFeatureDLSS}; sl::Preferences preferences{}; preferences.logLevel = LogLevel() >= 2 ? sl::LogLevel::eVerbose : sl::LogLevel::eDefault; preferences.logMessageCallback = LogFromStreamline; preferences.pathsToPlugins = plugin_paths; preferences.numPathsToPlugins = state.model_dir.empty() ? 1 : 2; preferences.flags = sl::PreferenceFlags::eDisableCLStateTracking | sl::PreferenceFlags::eUseManualHooking | sl::PreferenceFlags::eAllowOTA | sl::PreferenceFlags::eLoadDownloadedPlugins; preferences.featuresToLoad = features; preferences.numFeaturesToLoad = 1; preferences.engine = sl::EngineType::eCustom; preferences.engineVersion = "1.0"; preferences.projectId = PROJECT_ID; preferences.renderAPI = sl::RenderAPI::eVulkan; const Api& api = state.api; if (const sl::Result result = api.init(preferences, sl::kSDKVersion); result != sl::Result::eOk) { LOG_ERROR(Render_Vulkan, "NVIDIA Streamline failed to start (result {}); using FSR 3.1", static_cast(result)); return false; } sl::FeatureRequirements requirements{}; if (const sl::Result result = api.get_feature_requirements(sl::kFeatureDLSS, requirements); result != sl::Result::eOk) { LOG_WARNING(Render_Vulkan, "Streamline found no DLSS plugin (result {}); put sl.dlss.dll next to " "sl.interposer.dll or install the NVIDIA app. Using FSR 3.1", static_cast(result)); api.shutdown(); return false; } if (requirements.vkNumComputeQueuesRequired != 0 || requirements.vkNumGraphicsQueuesRequired != 0) { LOG_WARNING(Render_Vulkan, "This DLSS wants extra Vulkan queues; using FSR 3.1"); api.shutdown(); return false; } state.instance_extensions = Names(requirements.vkNumInstanceExtensions, requirements.vkInstanceExtensions); state.device_extensions = Names(requirements.vkNumDeviceExtensions, requirements.vkDeviceExtensions); // The KHR and EXT buffer device address extensions can't both be enabled. const auto& device_extensions = state.device_extensions; if (std::ranges::find(device_extensions, VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME) != device_extensions.end()) { std::erase(state.device_extensions, VK_EXT_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME); } sl::FeatureVersion version{}; api.get_feature_version(sl::kFeatureDLSS, version); LOG_INFO(Render_Vulkan, "NVIDIA Streamline {} started from {} (DLSS {})", version.versionSL.toStr(), Common::FS::PathToUTF8String(std::filesystem::path{state.plugin_dir}), version.versionNGX.toStr()); state.started = true; return true; } bool WantsDevice() { const State& state = GetState(); return state.started && !state.attached; } std::vector InstanceExtensions() { const State& state = GetState(); return state.started ? state.instance_extensions : std::vector{}; } std::vector DeviceExtensions() { const State& state = GetState(); return state.started ? state.device_extensions : std::vector{}; } bool AttachDevice(VkInstance instance, VkPhysicalDevice physical, VkDevice device, u32 graphics_family, vk::DeviceDispatch& dld) { State& state = GetState(); if (!WantsDevice()) { return false; } sl::VulkanInfo info{}; info.device = device; info.instance = instance; info.physicalDevice = physical; info.computeQueueFamily = graphics_family; info.graphicsQueueFamily = graphics_family; if (const sl::Result result = state.api.set_vulkan_info(info); result != sl::Result::eOk) { LOG_ERROR(Render_Vulkan, "Streamline did not take the Vulkan device (result {})", static_cast(result)); Stop(); return false; } sl::AdapterInfo adapter{}; adapter.vkPhysicalDevice = physical; if (const sl::Result result = state.api.is_feature_supported(sl::kFeatureDLSS, adapter); result != sl::Result::eOk) { LOG_WARNING(Render_Vulkan, "NVIDIA DLSS is not supported on this GPU or driver (Streamline result {}); " "using FSR 3.1", static_cast(result)); Stop(); return false; } // Manual hooking: Streamline does its per-frame work in these. const auto hook = [&](auto& function, const char* name) { if (const FARPROC proxy = GetProcAddress(state.module, name)) { function = reinterpret_cast>(proxy); } else { LOG_WARNING(Render_Vulkan, "sl.interposer.dll has no {}", name); } }; hook(dld.vkCreateSwapchainKHR, "vkCreateSwapchainKHR"); hook(dld.vkDestroySwapchainKHR, "vkDestroySwapchainKHR"); hook(dld.vkGetSwapchainImagesKHR, "vkGetSwapchainImagesKHR"); hook(dld.vkAcquireNextImageKHR, "vkAcquireNextImageKHR"); hook(dld.vkQueuePresentKHR, "vkQueuePresentKHR"); hook(dld.vkDeviceWaitIdle, "vkDeviceWaitIdle"); state.attached = true; LOG_INFO(Render_Vulkan, "NVIDIA DLSS is available through Streamline"); return true; } bool IsDlssAvailable() { const State& state = GetState(); return state.started && state.attached; } void Stop() { State& state = GetState(); if (!state.started) { return; } const sl::Result result = state.api.shutdown(); state.started = false; state.attached = false; LOG_INFO(Render_Vulkan, "NVIDIA Streamline stopped (result {})", static_cast(result)); } const Api& GetApi() { return GetState().api; } } // namespace Vulkan::Streamline