From 63e87a22c60394bc4f427ee5f8d6ddbce7b0ac7f Mon Sep 17 00:00:00 2001 From: Duck912 <350528965+Duck912@users.noreply.github.com> Date: Tue, 3 Nov 2026 12:13:35 -0500 Subject: video_core: NVIDIA DLSS through NVIDIA Streamline in place of a game's DLSS Adds a Graphics setting, "Use NVIDIA DLSS in place of a game's DLSS" (nvidia_dlss, off by default). When it is on, in Windows builds configured with EGLANTINE_STREAMLINE_SDK, the evaluation Eglantine already replaces with FSR 3.1 goes to NVIDIA DLSS on GeForce RTX cards, with the same inputs (colour, depth, motion vectors, jitter). When DLSS can't run, FSR 3.1 runs as before. Only Streamline's MIT-licensed headers are compiled in; nothing of NVIDIA's is linked or shipped. At run time Eglantine looks for sl.interposer.dll in the user folder's "streamline" folder (or EDEN_STREAMLINE_DIR, or "streamline" next to the executable), checks its Streamline signature and starts Streamline with manual hooking. Streamline's plugins come from that folder or the NVIDIA app's downloads. DLSS itself is nvngx_dlss.dll in the same folder, else the newest DLSS 310 or later that the NVIDIA app has downloaded, checked for NVIDIA's Authenticode signature and copied as streamline/nvidia-app/nvngx_dlss.dll. Like other tools that call DLSS outside a game, it uses its own fixed project id with the custom engine type. - Preset K for all modes (EDEN_NX2_DLSS_PRESET overrides it): the default Performance model (M) and L draw thin dark horizontal lines in some frames at 2x resolution scale; K doesn't. - HDR input with auto exposure; the mode follows the render/output ratio. - Buffer device address stays enabled when DLSS needs it, for DLSS only. - The upscaler waits for the device instead of submitting when it is destroyed: by then the game's channels are released, and a submit read the state tracker's stale flags (a crash when stopping a game). With the setting off, or in builds without the SDK path (the setting is hidden there), nothing changes. Testing: Windows 11, RTX 5080, Fire Emblem: Fortune's Weave docked at 1x (720p -> 1440p) and 2x (1440p -> 2880p), CLI runs and GUI play, stopping and restarting games with DLSS on. Not tested on other GPUs or games. Assisted-by: Claude Opus 5.5 (implementation, debugging) Signed-off-by: Duck912 <350528965+Duck912@users.noreply.github.com> --- src/video_core/vulkan_common/streamline.cpp | 463 ++++++++++++++++++++++++++++ 1 file changed, 463 insertions(+) create mode 100644 src/video_core/vulkan_common/streamline.cpp (limited to 'src/video_core/vulkan_common/streamline.cpp') diff --git a/src/video_core/vulkan_common/streamline.cpp b/src/video_core/vulkan_common/streamline.cpp new file mode 100644 index 0000000000..966de82cb5 --- /dev/null +++ b/src/video_core/vulkan_common/streamline.cpp @@ -0,0 +1,463 @@ +// 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/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", + files.app_model.string()); + 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 {}: {}", + target.string(), 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", + path.string()); + return false; + } + state.module = LoadLibraryW(path.c_str()); + if (!state.module) { + LOG_ERROR(Render_Vulkan, "Could not load {} (error {})", path.string(), 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", + files.folder.string()); + 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", + files.folder.string()); + } + } + + // 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(), std::filesystem::path{state.plugin_dir}.string(), + 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 -- cgit v1.2.3 From f4f076fe447b2ddc158fcc12ee81ed2af2aecad7 Mon Sep 17 00:00:00 2001 From: Duck912 <350528965+Duck912@users.noreply.github.com> Date: Tue, 3 Nov 2026 12:56:27 -0500 Subject: video_core, yuzu: write NVIDIA DLSS file paths to logs as UTF-8 The Streamline start-up and DLSS download code formatted paths with std::filesystem::path::string(), which on MSVC converts to the active code page and throws when a character has no mapping there, for example a user name in another script under %APPDATA%. The start-up log runs inside the renderer's constructor, which only catches vk::Exception, so such a user would crash at game start with the setting on. Use Common::FS::PathToUTF8String for every path in these logs and messages. Found by CodeRabbit review. Assisted-by: Claude Opus 5.5 (implementation) Signed-off-by: Duck912 <350528965+Duck912@users.noreply.github.com> --- src/video_core/vulkan_common/streamline.cpp | 17 ++++++++++------- src/yuzu/configuration/nvidia_dlss_panel.cpp | 14 ++++++++------ 2 files changed, 18 insertions(+), 13 deletions(-) (limited to 'src/video_core/vulkan_common/streamline.cpp') diff --git a/src/video_core/vulkan_common/streamline.cpp b/src/video_core/vulkan_common/streamline.cpp index 966de82cb5..4f8fd796c1 100644 --- a/src/video_core/vulkan_common/streamline.cpp +++ b/src/video_core/vulkan_common/streamline.cpp @@ -15,6 +15,7 @@ #include +#include "common/fs/fs_util.h" #include "common/fs/path_util.h" #include "common/logging.h" #include "common/settings.h" @@ -168,7 +169,7 @@ std::filesystem::path UseAppModel(const DlssFiles& files) { if (!current) { if (!IsSignedByNvidia(files.app_model)) { LOG_WARNING(Render_Vulkan, "The NVIDIA app's DLSS {} is not signed by NVIDIA", - files.app_model.string()); + Common::FS::PathToUTF8String(files.app_model)); return {}; } std::filesystem::create_directories(dir, ec); @@ -176,7 +177,7 @@ std::filesystem::path UseAppModel(const DlssFiles& files) { std::filesystem::copy_options::overwrite_existing, ec); if (ec) { LOG_WARNING(Render_Vulkan, "Could not copy the NVIDIA app's DLSS to {}: {}", - target.string(), ec.message()); + Common::FS::PathToUTF8String(target), ec.message()); return {}; } } @@ -186,12 +187,13 @@ std::filesystem::path UseAppModel(const DlssFiles& files) { bool Load(State& state, const std::filesystem::path& path) { if (!IsStreamlineModule(path)) { LOG_ERROR(Render_Vulkan, "{} is not signed by NVIDIA; not loading it", - path.string()); + Common::FS::PathToUTF8String(path)); return false; } state.module = LoadLibraryW(path.c_str()); if (!state.module) { - LOG_ERROR(Render_Vulkan, "Could not load {} (error {})", path.string(), GetLastError()); + LOG_ERROR(Render_Vulkan, "Could not load {} (error {})", Common::FS::PathToUTF8String(path), + GetLastError()); return false; } Api& api = state.api; @@ -298,7 +300,7 @@ bool Start() { 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", - files.folder.string()); + Common::FS::PathToUTF8String(files.folder)); return false; } if (!Load(state, files.interposer)) { @@ -313,7 +315,7 @@ bool Start() { } } else if (files.model.empty()) { LOG_WARNING(Render_Vulkan, "No nvngx_dlss.dll in {} and no DLSS from the NVIDIA app", - files.folder.string()); + Common::FS::PathToUTF8String(files.folder)); } } @@ -371,7 +373,8 @@ bool Start() { sl::FeatureVersion version{}; api.get_feature_version(sl::kFeatureDLSS, version); LOG_INFO(Render_Vulkan, "NVIDIA Streamline {} started from {} (DLSS {})", - version.versionSL.toStr(), std::filesystem::path{state.plugin_dir}.string(), + version.versionSL.toStr(), + Common::FS::PathToUTF8String(std::filesystem::path{state.plugin_dir}), version.versionNGX.toStr()); state.started = true; return true; diff --git a/src/yuzu/configuration/nvidia_dlss_panel.cpp b/src/yuzu/configuration/nvidia_dlss_panel.cpp index fa1af23c4a..2329693a03 100644 --- a/src/yuzu/configuration/nvidia_dlss_panel.cpp +++ b/src/yuzu/configuration/nvidia_dlss_panel.cpp @@ -33,6 +33,7 @@ #include #include "common/common_types.h" +#include "common/fs/fs_util.h" #include "common/logging.h" #include "video_core/vulkan_common/streamline.h" #include "yuzu/configuration/nvidia_dlss_panel.h" @@ -441,7 +442,8 @@ void NvidiaDlssPanel::Download() { QStringList names; for (const std::string& entry : wanted) { - names << QString::fromStdString(std::filesystem::path{entry}.filename().string()); + names << QString::fromStdString( + Common::FS::PathToUTF8String(std::filesystem::path{entry}.filename())); } QDialog dialog(this); dialog.setWindowTitle(Tr("Download NVIDIA DLSS")); @@ -517,7 +519,7 @@ void NvidiaDlssPanel::Download() { file.write(contents->data(), static_cast(contents->size())); if (!file) { progress.close(); - fail("could not write " + partial.string()); + fail("could not write " + Common::FS::PathToUTF8String(partial)); return; } } @@ -526,18 +528,18 @@ void NvidiaDlssPanel::Download() { if (!signed_ok) { std::filesystem::remove(partial, ec); progress.close(); - fail(target.filename().string() + " is not signed by NVIDIA"); + fail(Common::FS::PathToUTF8String(target.filename()) + " is not signed by NVIDIA"); return; } std::filesystem::rename(partial, target, ec); if (ec) { std::filesystem::remove(partial, ec); progress.close(); - fail("could not save " + target.string()); + fail("could not save " + Common::FS::PathToUTF8String(target)); return; } - LOG_INFO(Frontend, "Downloaded {} from NVIDIA's Streamline {} release", target.string(), - RELEASE_VERSION); + LOG_INFO(Frontend, "Downloaded {} from NVIDIA's Streamline {} release", + Common::FS::PathToUTF8String(target), RELEASE_VERSION); } progress.setValue(1000); progress.close(); -- cgit v1.2.3