From 91a2cebe6c68703601e7f6ccad42b743baf1f8c9 Mon Sep 17 00:00:00 2001 From: GPUCode Date: Thu, 4 May 2023 14:15:03 +0300 Subject: [PATCH] code: Add nvapi profile support --- src/core/CMakeLists.txt | 11 +++ src/core/core.cpp | 13 ++++ src/core/core.h | 2 + src/core/tools/nvapi.cpp | 164 +++++++++++++++++++++++++++++++++++++++ src/core/tools/nvapi.h | 25 ++++++ 5 files changed, 215 insertions(+) create mode 100644 src/core/tools/nvapi.cpp create mode 100644 src/core/tools/nvapi.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 7863e8ba7..c5c277176 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -466,6 +466,8 @@ add_library(citra_core STATIC tracer/citrace.h tracer/recorder.cpp tracer/recorder.h + tools/nvapi.cpp + tools/nvapi.h ) if (ENABLE_FFMPEG_VIDEO_DUMPER) @@ -511,3 +513,12 @@ endif() if (CITRA_USE_PRECOMPILED_HEADERS) target_precompile_headers(citra_core PRIVATE precompiled_headers.h) endif() + +if (WIN32) + target_sources(citra_core PUBLIC + ${PROJECT_SOURCE_DIR}/externals/nvapi/nvapi.h + ${PROJECT_SOURCE_DIR}/externals/nvapi/NvApiDriverSettings.h + ) + target_link_libraries(citra_core PRIVATE ${PROJECT_SOURCE_DIR}/externals/nvapi/amd64/nvapi64.lib) +else() +endif() diff --git a/src/core/core.cpp b/src/core/core.cpp index 60cd7a61c..ceca839bd 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -46,6 +46,7 @@ #include "core/loader/loader.h" #include "core/movie.h" #include "core/rpc/rpc_server.h" +#include "core/tools/nvapi.h" #include "network/network.h" #include "video_core/custom_textures/custom_tex_manager.h" #include "video_core/renderer_base.h" @@ -70,6 +71,8 @@ Core::Timing& Global() { return System::GetInstance().CoreTiming(); } +std::unique_ptr nvapi; + System::~System() = default; System::ResultStatus System::RunLoop(bool tight_loop) { @@ -440,6 +443,8 @@ System::ResultStatus System::Init(Frontend::EmuWindow& emu_window, VideoCore::Init(emu_window, secondary_window, *this); + ToggleNvidiaProfile(true); + LOG_DEBUG(Core, "Initialized OK"); is_powered_on = true; @@ -671,4 +676,12 @@ void System::serialize(Archive& ar, const unsigned int file_version) { SERIALIZE_IMPL(System) +void System::ToggleNvidiaProfile(bool enable) const { + if (!nvapi) { + nvapi = std::make_unique(); + } + nvapi->Initialize(); + nvapi->SetSettings(enable); +} + } // namespace Core diff --git a/src/core/core.h b/src/core/core.h index d4cab87e3..d03352962 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -328,6 +328,8 @@ public: return false; } + void ToggleNvidiaProfile(bool enable) const; + private: /** * Initialize the emulated system. diff --git a/src/core/tools/nvapi.cpp b/src/core/tools/nvapi.cpp new file mode 100644 index 000000000..52e8a8558 --- /dev/null +++ b/src/core/tools/nvapi.cpp @@ -0,0 +1,164 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include + +#include "common/string_util.h" +#include "core/tools/nvapi.h" + +namespace { +constexpr char PROFILE_NAME[] = "Citra (Auto)"; +constexpr char APPLICATION_NAME[] = "citra.exe"; + +// TODO: orig_value should be read from the global profile instead of hard-coded here, +// but trying to read from the global or base profile throws an error. +struct SettingInfo { + NvU32 id; + NVDRS_SETTING_TYPE type; + NvU32 orig_value; + NvU32 target_value; +}; +constexpr std::array SETTINGS{ + /* SettingInfo{ + .id = PREFERRED_PSTATE_ID, + .type = NVDRS_DWORD_TYPE, + .orig_value = PREFERRED_PSTATE_OPTIMAL_POWER, + .target_value = PREFERRED_PSTATE_PREFER_MAX, + }, + */ + SettingInfo{ + .id = OGL_THREAD_CONTROL_ID, + .type = NVDRS_DWORD_TYPE, + .orig_value = OGL_THREAD_CONTROL_DEFAULT, + .target_value = OGL_THREAD_CONTROL_ENABLE, + }, +}; +} // namespace + +namespace Core { + +NvApi::NvApi() {} + +void NvApi::Initialize() { +#ifndef _WIN32 + return; +#else + if (initialized) { + return; + } + + if (NvAPI_Initialize() != NVAPI_OK) { + // Non-nVidia GPU + return; + } + + if (NvAPI_DRS_CreateSession(&session_handle) != NVAPI_OK) { + LOG_ERROR(Config, "Failed to create Nvidia profile session"); + return; + } + + if (NvAPI_DRS_LoadSettings(session_handle) != NVAPI_OK) { + LOG_ERROR(Config, "Failed to load Nvidia profile settings"); + return; + } + + auto nv_profile_name = Common::UTF8ToUTF16W(PROFILE_NAME); + auto nv_profile_name_size = (nv_profile_name.size()) * 2; + + auto nv_app_name = Common::UTF8ToUTF16W(APPLICATION_NAME); + auto nv_app_name_size = (nv_app_name.size()) * 2; + + switch (NvAPI_DRS_FindProfileByName(session_handle, (NvU16*)nv_profile_name.c_str(), + &profile_handle)) { + case NVAPI_PROFILE_NOT_FOUND: { + LOG_ERROR(Config, "NVAPI_PROFILE_NOT_FOUND"); + break; + } + case NVAPI_ERROR: + LOG_ERROR(Config, "Nvidia profile FindProfileByName failed"); + break; + case NVAPI_OK: + default: + LOG_INFO(Config, "Successfully loaded existing yuzu Nvidia profile: {}", PROFILE_NAME); + break; + } + + NVDRS_PROFILE nv_profile = { + .version = NVDRS_PROFILE_VER1, + .gpuSupport = std::numeric_limits::max(), + .isPredefined = false, + /* .numOfApps = 1, + .numOfSettings = 0,*/ + }; + std::memcpy(&nv_profile.profileName, nv_profile_name.data(), nv_profile_name_size); + + if (NvAPI_DRS_CreateProfile(session_handle, &nv_profile, &profile_handle) != NVAPI_OK) { + LOG_ERROR(Config, "Failed to create nVidia profile"); + // return; + } + + NVDRS_APPLICATION nv_app{ + .version = NVDRS_APPLICATION_VER_V4, + .isPredefined = false, + .isMetro = false, + .isCommandLine = false, + }; + + std::memcpy(&nv_app.appName, nv_app_name.data(), nv_app_name_size); + std::memcpy(&nv_app.userFriendlyName, nv_profile_name.data(), nv_profile_name_size); + if (NvAPI_DRS_CreateApplication(session_handle, profile_handle, &nv_app) != NVAPI_OK) { + LOG_ERROR(Config, "Failed to create Nvidia application"); + return; + } + + initialized = true; + LOG_CRITICAL(Frontend, "Inited"); +#endif +} + +NvApi::~NvApi() { +#ifndef _WIN32 + return; +#else + if (session_handle) { + NvAPI_DRS_DestroySession(session_handle); + } + NvAPI_Unload(); +#endif +} + +void NvApi::SetSettings(bool enable) const { +#ifndef _WIN32 + return; +#else + if (!initialized) { + return; + } + + for (auto& setting : SETTINGS) { + NVDRS_SETTING nv_setting{ + .version = NVDRS_SETTING_VER1, + .settingId = setting.id, + .settingType = setting.type, + .settingLocation = NVDRS_CURRENT_PROFILE_LOCATION, + .isCurrentPredefined = false, + .isPredefinedValid = false, + .u32CurrentValue = enable ? setting.target_value : setting.orig_value, + }; + NvAPI_DRS_GetSettingNameFromId(setting.id, &nv_setting.settingName); + if (NvAPI_DRS_SetSetting(session_handle, profile_handle, &nv_setting) != NVAPI_OK) { + auto id_name{Common::UTF16ToUTF8((wchar_t*)nv_setting.settingName)}; + LOG_ERROR(Config, "Failed to set Nvidia setting {}", id_name); + return; + } + } + + if (NvAPI_DRS_SaveSettings(session_handle) != NVAPI_OK) { + LOG_ERROR(Config, "Failed to save Nvidia profile settings"); + return; + } + LOG_CRITICAL(Frontend, "Settings set"); +#endif +} + +} // namespace Core diff --git a/src/core/tools/nvapi.h b/src/core/tools/nvapi.h new file mode 100644 index 000000000..ed04627d4 --- /dev/null +++ b/src/core/tools/nvapi.h @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +// nvapi.h must be included first +// clang-format off +#include "../externals/nvapi/nvapi.h" +#include "../externals/nvapi/NvApiDriverSettings.h" +// clang-format on + +namespace Core { + +class NvApi { +public: + NvApi(); + ~NvApi(); + + void Initialize(); + void SetSettings(bool enable) const; + +private: + NvDRSSessionHandle session_handle{}; + NvDRSProfileHandle profile_handle{}; + bool initialized{false}; +}; +} // namespace Core