From ef566186be9b43f55c2422fe9f037f0f2b45055c Mon Sep 17 00:00:00 2001 From: ADAS2024 <73618406+ADAS2024@users.noreply.github.com> Date: Sun, 6 Jul 2025 05:50:03 -0400 Subject: [PATCH] [Linux] Implement Feral Interactive's Gamemode for Potential Performance Increases (#17325) Currently, this is a draft PR to implement Feral Interactive's Gamemode into RPCS3, which can improve game performance on certain Linux systems. At the moment, I am running into various compiler warnings when trying to include "gamemode_client.h" due to the file not using strict typings and old C-style casts. I know I can disable these flags during RPCS3's compilation but I wanted to check with the maintainers before going forward and if this feature should be implemented. It should be noted that RPCS3 only fails to compile and run if I include Feral's header file, but everything else compiles if I comment out the include. Targets Issue #11299 --- .gitmodules | 4 ++++ 3rdparty/CMakeLists.txt | 4 ++++ 3rdparty/feralinteractive/CMakeLists.txt | 9 ++++++++ 3rdparty/feralinteractive/feralinteractive | 1 + CMakeLists.txt | 9 ++++++++ rpcs3/Emu/System.cpp | 11 ++++++++++ rpcs3/Emu/System.h | 1 + rpcs3/Emu/system_config.h | 1 + rpcs3/gamemode_control.cpp | 24 ++++++++++++++++++++++ rpcs3/gamemode_control.h | 3 +++ rpcs3/main_application.cpp | 3 +++ rpcs3/rpcs3.vcxproj | 4 +++- rpcs3/rpcs3qt/CMakeLists.txt | 1 + rpcs3/rpcs3qt/emu_settings_type.h | 2 ++ rpcs3/rpcs3qt/settings_dialog.cpp | 11 ++++++++++ rpcs3/rpcs3qt/settings_dialog.ui | 13 ++++++++++++ rpcs3/rpcs3qt/tooltips.h | 2 ++ 17 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 3rdparty/feralinteractive/CMakeLists.txt create mode 160000 3rdparty/feralinteractive/feralinteractive create mode 100644 rpcs3/gamemode_control.cpp create mode 100644 rpcs3/gamemode_control.h diff --git a/.gitmodules b/.gitmodules index 427c61ffbd..6f0cd78a5b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -108,3 +108,7 @@ path = 3rdparty/GPUOpen/VulkanMemoryAllocator url = ../../GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git ignore = dirty +[submodule "3rdparty/feralinteractive/feralinteractive"] + path = 3rdparty/feralinteractive/feralinteractive + url = ../../FeralInteractive/gamemode.git + ignore = dirty diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt index 6c49a889ba..94c6069271 100644 --- a/3rdparty/CMakeLists.txt +++ b/3rdparty/CMakeLists.txt @@ -357,6 +357,9 @@ add_subdirectory(opencv EXCLUDE_FROM_ALL) # FUSION add_subdirectory(fusion EXCLUDE_FROM_ALL) +# FERAL INTERACTIVE +add_subdirectory(feralinteractive EXCLUDE_FROM_ALL) + # add nice ALIAS targets for ease of use if(USE_SYSTEM_LIBUSB) add_library(3rdparty::libusb ALIAS usb-1.0-shared) @@ -389,3 +392,4 @@ add_library(3rdparty::miniupnpc ALIAS libminiupnpc-static) add_library(3rdparty::rtmidi ALIAS rtmidi) add_library(3rdparty::opencv ALIAS ${OPENCV_TARGET}) add_library(3rdparty::fusion ALIAS Fusion) +add_library(3rdparty::feralinteractive ALIAS 3rdparty_feralinteractive) diff --git a/3rdparty/feralinteractive/CMakeLists.txt b/3rdparty/feralinteractive/CMakeLists.txt new file mode 100644 index 0000000000..c7b136e5f0 --- /dev/null +++ b/3rdparty/feralinteractive/CMakeLists.txt @@ -0,0 +1,9 @@ +# Feral Interactive + +add_library(3rdparty_feralinteractive INTERFACE) + +if (CMAKE_SYSTEM MATCHES "Linux") + target_include_directories(3rdparty_feralinteractive INTERFACE feralinteractive/lib) + target_compile_definitions(3rdparty_feralinteractive INTERFACE -DGAMEMODE_AVAILABLE) + target_link_libraries(3rdparty_feralinteractive INTERFACE feralinteractive) +endif() diff --git a/3rdparty/feralinteractive/feralinteractive b/3rdparty/feralinteractive/feralinteractive new file mode 160000 index 0000000000..c54d6d4243 --- /dev/null +++ b/3rdparty/feralinteractive/feralinteractive @@ -0,0 +1 @@ +Subproject commit c54d6d4243b0dd0afcb49f2c9836d432da171a2b diff --git a/CMakeLists.txt b/CMakeLists.txt index 2f633730fc..81b2a84ee6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -152,6 +152,15 @@ if(NOT WIN32) add_compile_options(-pthread) endif() +## Look for Gamemode if its installed on Linux +if(LINUX) + find_program(GAMEMODE_FOUND gamemoded) ## Only works if gamemode is installed on system (might include lib32 case) + if(GAMEMODE_FOUND) + add_compile_definitions(GAMEMODE_AVAILABLE) + endif() + message(GAMEMODE_AVAILABLE="${GAMEMODE_AVAILABLE}") +endif() + # TODO: do real installation, including copying directory structure set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE "${PROJECT_BINARY_DIR}/bin") set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${PROJECT_BINARY_DIR}/bin") diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index 002075b5fa..2e8561ad9a 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -2477,6 +2477,11 @@ void Emulator::Run(bool start_playtime) { Emu.GetCallbacks().enable_display_sleep(false); } + + if (g_cfg.misc.enable_gamemode) + { + Emu.GetCallbacks().enable_gamemode(true); + } } void Emulator::RunPPU() @@ -3237,6 +3242,12 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s sys_log.notice("Stopping emulator..."); + // Calling Gamemode Exit on Stop + if (g_cfg.misc.enable_gamemode) + { + Emu.GetCallbacks().enable_gamemode(false); + } + const bool continuous_savestate_mode = savestate && !g_cfg.savestate.suspend_emu; // Show visual feedback to the user in case that stopping takes a while. diff --git a/rpcs3/Emu/System.h b/rpcs3/Emu/System.h index 417bef41b8..fcdb60cf8d 100644 --- a/rpcs3/Emu/System.h +++ b/rpcs3/Emu/System.h @@ -112,6 +112,7 @@ struct EmuCallbacks std::function enable_display_sleep; std::function check_microphone_permissions; std::function()> make_video_source; + std::function enable_gamemode; }; namespace utils diff --git a/rpcs3/Emu/system_config.h b/rpcs3/Emu/system_config.h index 92031a51db..2b35e70cb9 100644 --- a/rpcs3/Emu/system_config.h +++ b/rpcs3/Emu/system_config.h @@ -358,6 +358,7 @@ struct cfg_root : cfg::node cfg::_bool silence_all_logs{ this, "Silence All Logs", false, true }; cfg::string title_format{ this, "Window Title Format", "FPS: %F | %R | %V | %T [%t]", true }; cfg::_bool pause_during_home_menu{this, "Pause Emulation During Home Menu", false, false }; + cfg::_bool enable_gamemode{ this, "Enable GameMode", false, false }; } misc{ this }; diff --git a/rpcs3/gamemode_control.cpp b/rpcs3/gamemode_control.cpp new file mode 100644 index 0000000000..3879ebaccf --- /dev/null +++ b/rpcs3/gamemode_control.cpp @@ -0,0 +1,24 @@ +#include "gamemode_control.h" + +#ifdef GAMEMODE_AVAILABLE +#pragma GCC diagnostic ignored "-Wold-style-cast" +extern "C" { + #include "3rdparty/feralinteractive/feralinteractive/lib/gamemode_client.h" +} +#endif + +// Enables and Disables GameMode based on user settings and system +void enable_gamemode([[maybe_unused]] bool enabled) +{ +#if defined(GAMEMODE_AVAILABLE) + // Enable and Disable Gamemode + if (enabled) + { + gamemode_request_start(); + } + else + { + gamemode_request_end(); + } +#endif +} diff --git a/rpcs3/gamemode_control.h b/rpcs3/gamemode_control.h new file mode 100644 index 0000000000..cef63a5a6c --- /dev/null +++ b/rpcs3/gamemode_control.h @@ -0,0 +1,3 @@ +#pragma once + +void enable_gamemode(bool enabled); diff --git a/rpcs3/main_application.cpp b/rpcs3/main_application.cpp index 65a8117b7f..a1bc443cd0 100644 --- a/rpcs3/main_application.cpp +++ b/rpcs3/main_application.cpp @@ -1,6 +1,7 @@ #include "stdafx.h" #include "main_application.h" #include "display_sleep_control.h" +#include "gamemode_control.h" #include "util/types.hpp" #include "util/logs.hpp" @@ -374,5 +375,7 @@ EmuCallbacks main_application::CreateCallbacks() return true; }; + callbacks.enable_gamemode = [](bool enabled){ enable_gamemode(enabled); }; + return callbacks; } diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index 0e4dab8f27..ce80d45af6 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -189,6 +189,7 @@ + @@ -921,6 +922,7 @@ + @@ -2176,4 +2178,4 @@ - \ No newline at end of file + diff --git a/rpcs3/rpcs3qt/CMakeLists.txt b/rpcs3/rpcs3qt/CMakeLists.txt index c49daf0346..7f7605cb74 100644 --- a/rpcs3/rpcs3qt/CMakeLists.txt +++ b/rpcs3/rpcs3qt/CMakeLists.txt @@ -133,6 +133,7 @@ add_library(rpcs3_ui STATIC welcome_dialog.ui ../display_sleep_control.cpp + ../gamemode_control.cpp ../headless_application.cpp ../main_application.cpp ../module_verifier.cpp diff --git a/rpcs3/rpcs3qt/emu_settings_type.h b/rpcs3/rpcs3qt/emu_settings_type.h index 0fda096705..24e7b2fcdd 100644 --- a/rpcs3/rpcs3qt/emu_settings_type.h +++ b/rpcs3/rpcs3qt/emu_settings_type.h @@ -186,6 +186,7 @@ enum class emu_settings_type ShowMouseAndKeyboardToggleHint, WindowTitleFormat, PauseDuringHomeMenu, + EnableGamemode, // Network InternetStatus, @@ -388,6 +389,7 @@ inline static const std::map settings_location { emu_settings_type::SilenceAllLogs, { "Miscellaneous", "Silence All Logs" }}, { emu_settings_type::WindowTitleFormat, { "Miscellaneous", "Window Title Format" }}, { emu_settings_type::PauseDuringHomeMenu, { "Miscellaneous", "Pause Emulation During Home Menu" }}, + { emu_settings_type::EnableGamemode, { "Miscellaneous", "Enable GameMode" }}, // Networking { emu_settings_type::InternetStatus, { "Net", "Internet enabled"}}, diff --git a/rpcs3/rpcs3qt/settings_dialog.cpp b/rpcs3/rpcs3qt/settings_dialog.cpp index 7ad9023f4e..7569e5adca 100644 --- a/rpcs3/rpcs3qt/settings_dialog.cpp +++ b/rpcs3/rpcs3qt/settings_dialog.cpp @@ -1844,6 +1844,17 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std m_emu_settings->EnhanceCheckBox(ui->useNativeInterface, emu_settings_type::UseNativeInterface); SubscribeTooltip(ui->useNativeInterface, tooltips.settings.use_native_interface); +#if defined(__linux__) + ui->enableGamemode->setVisible(true); +#endif +#if defined(GAMEMODE_AVAILABLE) + ui->enableGamemode->setEnabled(true); + m_emu_settings->EnhanceCheckBox(ui->enableGamemode, emu_settings_type::EnableGamemode); + SubscribeTooltip(ui->enableGamemode, tooltips.settings.enable_gamemode); +#else + SubscribeTooltip(ui->enableGamemode, tooltips.settings.no_gamemode); +#endif + m_emu_settings->EnhanceCheckBox(ui->showShaderCompilationHint, emu_settings_type::ShowShaderCompilationHint); SubscribeTooltip(ui->showShaderCompilationHint, tooltips.settings.show_shader_compilation_hint); diff --git a/rpcs3/rpcs3qt/settings_dialog.ui b/rpcs3/rpcs3qt/settings_dialog.ui index 7a3d4951a8..f13b8344cd 100644 --- a/rpcs3/rpcs3qt/settings_dialog.ui +++ b/rpcs3/rpcs3qt/settings_dialog.ui @@ -3022,6 +3022,19 @@ + + + + false + + + false + + + Enable GameMode + + + diff --git a/rpcs3/rpcs3qt/tooltips.h b/rpcs3/rpcs3qt/tooltips.h index 41cfc1e6d8..0ef5ae76ec 100644 --- a/rpcs3/rpcs3qt/tooltips.h +++ b/rpcs3/rpcs3qt/tooltips.h @@ -129,6 +129,8 @@ public: // emulator + const QString enable_gamemode = tr("Activate Feral Interactive's GameMode.\nThis is a series of CPU and GPU optimizations and can potentially benefit game performance on some systems."); + const QString no_gamemode = tr("This requires Feral Interactive's GameMode to be installed.\nGameMode is a series of CPU and GPU optimizations and can potentially benefit game performance on some systems.\nTo install GameMode for your specific Linux distribution, go to the GitHub page:https://github.com/FeralInteractive/gamemode."); const QString exit_on_stop = tr("Automatically close RPCS3 when closing a game, or when a game closes itself."); const QString pause_on_focus_loss = tr("Automatically pause emulation when RPCS3 loses its focus or the application is inactive in order to save power and reduce CPU usage.\nDo note that emulation pausing in general is not perfect and may not be compatible with all games.\nAlthough it currently also pauses gameplay, it is not recommended to rely on it as this behavior may be changed in the future and it is not the purpose of this setting."); const QString start_game_fullscreen = tr("Automatically puts the game window in fullscreen.\nDouble click on the game window or press Alt+Enter to toggle fullscreen and windowed mode.");