From 796189ce68b93dcece9415bd3bda76c1f3bdc953 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Fri, 3 Jan 2025 10:08:03 +0100 Subject: [PATCH 01/60] Update SDL to 2.30.11 --- 3rdparty/libsdl-org/SDL | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/libsdl-org/SDL b/3rdparty/libsdl-org/SDL index 9c821dc21c..fa24d868ac 160000 --- a/3rdparty/libsdl-org/SDL +++ b/3rdparty/libsdl-org/SDL @@ -1 +1 @@ -Subproject commit 9c821dc21ccbd69b2bda421fdb35cb4ae2da8f5e +Subproject commit fa24d868ac2f8fd558e4e914c9863411245db8fd From 4b4399f63f548f0884cc1c4cf61baa935dbbe92c Mon Sep 17 00:00:00 2001 From: Megamouse Date: Fri, 3 Jan 2025 10:09:09 +0100 Subject: [PATCH 02/60] Update FAudio to 25.01 --- 3rdparty/FAudio | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/FAudio b/3rdparty/FAudio index b7c2e109ea..707114aef2 160000 --- a/3rdparty/FAudio +++ b/3rdparty/FAudio @@ -1 +1 @@ -Subproject commit b7c2e109ea86b82109244c9c4569ce9ad0c884df +Subproject commit 707114aef2907793644d4067a6e7b09b51502ca9 From e76bd11f53cf717722935148c7774245462e087e Mon Sep 17 00:00:00 2001 From: Megamouse Date: Fri, 3 Jan 2025 10:11:47 +0100 Subject: [PATCH 03/60] Update Wolfssl to 5.7.6 --- 3rdparty/wolfssl/wolfssl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/wolfssl/wolfssl b/3rdparty/wolfssl/wolfssl index bdd62314f0..239b85c804 160000 --- a/3rdparty/wolfssl/wolfssl +++ b/3rdparty/wolfssl/wolfssl @@ -1 +1 @@ -Subproject commit bdd62314f00fca0e216bf8c963c8eeff6327e0cb +Subproject commit 239b85c80438bf60d9a5b9e0ebe9ff097a760d0d From dfdb33befbfa929a096e30140333632991a67b76 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Fri, 3 Jan 2025 10:13:50 +0100 Subject: [PATCH 04/60] Update xxHash to 0.8.3 --- 3rdparty/xxHash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/xxHash b/3rdparty/xxHash index bbb27a5efb..e626a72bc2 160000 --- a/3rdparty/xxHash +++ b/3rdparty/xxHash @@ -1 +1 @@ -Subproject commit bbb27a5efb85b92a0486cf361a8635715a53f6ba +Subproject commit e626a72bc2321cd320e953a0ccf1584cad60f363 From 4f76aec6b33bcb92cec8202b9e7236950cc2b00f Mon Sep 17 00:00:00 2001 From: Megamouse Date: Fri, 3 Jan 2025 11:42:47 +0100 Subject: [PATCH 05/60] VS: Fix compilation with new wolfssl version --- 3rdparty/wolfssl/wolfssl.vcxproj | 91 ++++++++++++++++++++++++++++++++ rpcs3/emucore.vcxproj | 6 +-- rpcs3/rpcs3.vcxproj | 8 +-- 3 files changed, 98 insertions(+), 7 deletions(-) diff --git a/3rdparty/wolfssl/wolfssl.vcxproj b/3rdparty/wolfssl/wolfssl.vcxproj index e3da6588f8..90da8b7f38 100644 --- a/3rdparty/wolfssl/wolfssl.vcxproj +++ b/3rdparty/wolfssl/wolfssl.vcxproj @@ -139,6 +139,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false false @@ -160,6 +248,9 @@ true + + + diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index 3da2dc1872..0c54d02978 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -40,10 +40,10 @@ Use - ..\3rdparty\miniupnp\miniupnp\miniupnpc\include;..\3rdparty\wolfssl\wolfssl;..\3rdparty\flatbuffers\include;..\3rdparty\libusb\libusb\libusb;..\3rdparty\yaml-cpp\yaml-cpp\include;..\3rdparty\SoundTouch\soundtouch\include;..\3rdparty\rtmidi\rtmidi;..\3rdparty\zlib\zlib;$(SolutionDir)build\lib\$(Configuration)-$(Platform)\llvm_build\include;$(SolutionDir)build\lib_ext\$(Configuration)-$(Platform)\llvm_build\include;$(SolutionDir)build\lib_ext\$(Configuration)-$(Platform)\llvm\include;$(VULKAN_SDK)\Include;..\3rdparty\zstd\zstd\lib;$(SolutionDir)3rdparty\fusion\fusion\Fusion + ..\3rdparty\miniupnp\miniupnp\miniupnpc\include;..\3rdparty\wolfssl\wolfssl;..\3rdparty\flatbuffers\include;..\3rdparty\libusb\libusb\libusb;..\3rdparty\yaml-cpp\yaml-cpp\include;..\3rdparty\SoundTouch\soundtouch\include;..\3rdparty\rtmidi\rtmidi;..\3rdparty\zlib\zlib;$(SolutionDir)build\lib\$(Configuration)-$(Platform)\llvm_build\include;$(SolutionDir)build\lib_ext\$(Configuration)-$(Platform)\llvm_build\include;$(SolutionDir)build\lib_ext\$(Configuration)-$(Platform)\llvm\include;$(VULKAN_SDK)\Include;..\3rdparty\zstd\zstd\lib;$(SolutionDir)3rdparty\fusion\fusion\Fusion;$(SolutionDir)3rdparty\wolfssl\extra\win32 MaxSpeed - AL_LIBTYPE_STATIC;MINIUPNP_STATICLIB;HAVE_VULKAN;HAVE_SDL2;ZLIB_CONST;%(PreprocessorDefinitions) - AL_LIBTYPE_STATIC;MINIUPNP_STATICLIB;HAVE_VULKAN;HAVE_SDL2;ZLIB_CONST;%(PreprocessorDefinitions) + AL_LIBTYPE_STATIC;MINIUPNP_STATICLIB;HAVE_VULKAN;HAVE_SDL2;ZLIB_CONST;WOLFSSL_USER_SETTINGS;%(PreprocessorDefinitions) + AL_LIBTYPE_STATIC;MINIUPNP_STATICLIB;HAVE_VULKAN;HAVE_SDL2;ZLIB_CONST;WOLFSSL_USER_SETTINGS;%(PreprocessorDefinitions) %(AdditionalModuleDependencies) %(AdditionalModuleDependencies) diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index 755370a4c7..adb7e376be 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -71,7 +71,7 @@ - $(SolutionDir)3rdparty\opencv\opencv\opencv410\build\include;$(SolutionDir)3rdparty\SoundTouch\soundtouch\include;$(SolutionDir)3rdparty\cubeb\extra;$(SolutionDir)3rdparty\cubeb\cubeb\include\;$(SolutionDir)3rdparty\flatbuffers\include;$(SolutionDir)3rdparty\wolfssl\wolfssl;$(SolutionDir)3rdparty\curl\curl\include;$(SolutionDir)3rdparty\rtmidi\rtmidi;$(SolutionDir)3rdparty\libusb\libusb\libusb;$(VULKAN_SDK)\Include;$(SolutionDir)3rdparty\libsdl-org\SDL\include;$(QTDIR)\include;$(QTDIR)\include\QtCore;$(QTDIR)\include\QtConcurrent;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtSvg;$(QTDIR)\include\QtSvgWidgets;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtMultimedia;$(QTDIR)\mkspecs\win32-msvc;.\release;.\QTGeneratedFiles\$(ConfigurationName);.\QTGeneratedFiles;%(AdditionalIncludeDirectories) + $(SolutionDir)3rdparty\opencv\opencv\opencv410\build\include;$(SolutionDir)3rdparty\SoundTouch\soundtouch\include;$(SolutionDir)3rdparty\cubeb\extra;$(SolutionDir)3rdparty\cubeb\cubeb\include\;$(SolutionDir)3rdparty\flatbuffers\include;$(SolutionDir)3rdparty\wolfssl\wolfssl;$(SolutionDir)3rdparty\wolfssl\extra\win32;$(SolutionDir)3rdparty\curl\curl\include;$(SolutionDir)3rdparty\rtmidi\rtmidi;$(SolutionDir)3rdparty\libusb\libusb\libusb;$(VULKAN_SDK)\Include;$(SolutionDir)3rdparty\libsdl-org\SDL\include;$(QTDIR)\include;$(QTDIR)\include\QtCore;$(QTDIR)\include\QtConcurrent;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtSvg;$(QTDIR)\include\QtSvgWidgets;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtMultimedia;$(QTDIR)\mkspecs\win32-msvc;.\release;.\QTGeneratedFiles\$(ConfigurationName);.\QTGeneratedFiles;%(AdditionalIncludeDirectories) /Zc:__cplusplus -Zc:strictStrings -Zc:throwingNew- -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) release\ false @@ -79,7 +79,7 @@ 4577;4467;4281;%(DisableSpecificWarnings) $(IntDir) MaxSpeed - _WINDOWS;UNICODE;WIN32;WIN64;WIN32_LEAN_AND_MEAN;HAVE_VULKAN;HAVE_OPENCV;CV_IGNORE_DEBUG_BUILD_GUARD;MINIUPNP_STATICLIB;ZLIB_CONST;AL_LIBTYPE_STATIC;HAVE_SDL2;WITH_DISCORD_RPC;QT_NO_DEBUG;QT_WIDGETS_LIB;QT_GUI_LIB;QT_CORE_LIB;NDEBUG;QT_WINEXTRAS_LIB;QT_CONCURRENT_LIB;QT_MULTIMEDIA_LIB;QT_MULTIMEDIAWIDGETS_LIB;QT_SVG_LIB;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;WIN32;WIN64;WIN32_LEAN_AND_MEAN;HAVE_VULKAN;HAVE_OPENCV;CV_IGNORE_DEBUG_BUILD_GUARD;MINIUPNP_STATICLIB;ZLIB_CONST;AL_LIBTYPE_STATIC;WOLFSSL_USER_SETTINGS;HAVE_SDL2;WITH_DISCORD_RPC;QT_NO_DEBUG;QT_WIDGETS_LIB;QT_GUI_LIB;QT_CORE_LIB;NDEBUG;QT_WINEXTRAS_LIB;QT_CONCURRENT_LIB;QT_MULTIMEDIA_LIB;QT_MULTIMEDIAWIDGETS_LIB;QT_SVG_LIB;%(PreprocessorDefinitions) false $(IntDir)vc$(PlatformToolsetVersion).pdb true @@ -124,7 +124,7 @@ - $(SolutionDir)3rdparty\opencv\opencv\opencv410\build\include;$(SolutionDir)3rdparty\SoundTouch\soundtouch\include;$(SolutionDir)3rdparty\cubeb\extra;$(SolutionDir)3rdparty\cubeb\cubeb\include\;$(SolutionDir)3rdparty\flatbuffers\include;$(SolutionDir)3rdparty\wolfssl\wolfssl;$(SolutionDir)3rdparty\curl\curl\include;$(SolutionDir)3rdparty\rtmidi\rtmidi;$(SolutionDir)3rdparty\libusb\libusb\libusb;$(VULKAN_SDK)\Include;$(SolutionDir)3rdparty\libsdl-org\SDL\include;$(QTDIR)\include;$(QTDIR)\include\QtCore;$(QTDIR)\include\QtConcurrent;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtSvg;$(QTDIR)\include\QtSvgWidgets;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtMultimedia;$(QTDIR)\mkspecs\win32-msvc;.\debug;.\QTGeneratedFiles\$(ConfigurationName);.\QTGeneratedFiles;%(AdditionalIncludeDirectories) + $(SolutionDir)3rdparty\opencv\opencv\opencv410\build\include;$(SolutionDir)3rdparty\SoundTouch\soundtouch\include;$(SolutionDir)3rdparty\cubeb\extra;$(SolutionDir)3rdparty\cubeb\cubeb\include\;$(SolutionDir)3rdparty\flatbuffers\include;$(SolutionDir)3rdparty\wolfssl\wolfssl;$(SolutionDir)3rdparty\wolfssl\extra\win32;$(SolutionDir)3rdparty\curl\curl\include;$(SolutionDir)3rdparty\rtmidi\rtmidi;$(SolutionDir)3rdparty\libusb\libusb\libusb;$(VULKAN_SDK)\Include;$(SolutionDir)3rdparty\libsdl-org\SDL\include;$(QTDIR)\include;$(QTDIR)\include\QtCore;$(QTDIR)\include\QtConcurrent;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtSvg;$(QTDIR)\include\QtSvgWidgets;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtMultimedia;$(QTDIR)\mkspecs\win32-msvc;.\debug;.\QTGeneratedFiles\$(ConfigurationName);.\QTGeneratedFiles;%(AdditionalIncludeDirectories) /Zc:__cplusplus -Zc:strictStrings -Zc:throwingNew- -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) debug\ false @@ -132,7 +132,7 @@ 4577;4467;4281;%(DisableSpecificWarnings) $(IntDir) Disabled - _WINDOWS;UNICODE;WIN32;WIN64;WIN32_LEAN_AND_MEAN;HAVE_VULKAN;HAVE_OPENCV;CV_IGNORE_DEBUG_BUILD_GUARD;MINIUPNP_STATICLIB;ZLIB_CONST;AL_LIBTYPE_STATIC;QT_WIDGETS_LIB;QT_GUI_LIB;QT_CORE_LIB;QT_WINEXTRAS_LIB;QT_CONCURRENT_LIB;QT_MULTIMEDIA_LIB;QT_MULTIMEDIAWIDGETS_LIB;QT_SVG_LIB;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;WIN32;WIN64;WIN32_LEAN_AND_MEAN;HAVE_VULKAN;HAVE_OPENCV;CV_IGNORE_DEBUG_BUILD_GUARD;MINIUPNP_STATICLIB;ZLIB_CONST;AL_LIBTYPE_STATIC;WOLFSSL_USER_SETTINGS;QT_WIDGETS_LIB;QT_GUI_LIB;QT_CORE_LIB;QT_WINEXTRAS_LIB;QT_CONCURRENT_LIB;QT_MULTIMEDIA_LIB;QT_MULTIMEDIAWIDGETS_LIB;QT_SVG_LIB;%(PreprocessorDefinitions) false true true From 8ea27b0aca38f78faa4445286e8b8fb43693a209 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Fri, 3 Jan 2025 19:05:06 +0100 Subject: [PATCH 06/60] Qt: workaround for crash in long accepted slot --- rpcs3/rpcs3qt/main_window.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index d3206b7bdb..3526cce235 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -405,7 +405,7 @@ void main_window::OnMissingFw() connect(mb, &QDialog::accepted, this, [this]() { - InstallPup(); + QTimer::singleShot(1, [this](){ InstallPup(); }); // singleShot to avoid a Qt bug that causes a deletion of the sender during long slots. }); } From 2614450e4b2f7d9d49dfad9e769c4ad4b99e46ba Mon Sep 17 00:00:00 2001 From: Elad <18193363+elad335@users.noreply.github.com> Date: Sat, 4 Jan 2025 08:28:19 +0200 Subject: [PATCH 07/60] Fix IDM image serialization --- rpcs3/Emu/Cell/lv2/sys_cond.cpp | 2 +- rpcs3/Emu/Cell/lv2/sys_event.cpp | 4 ++-- rpcs3/Emu/Cell/lv2/sys_event_flag.cpp | 2 +- rpcs3/Emu/Cell/lv2/sys_memory.cpp | 2 +- rpcs3/Emu/Cell/lv2/sys_mmapper.cpp | 2 +- rpcs3/Emu/Cell/lv2/sys_mutex.cpp | 2 +- rpcs3/Emu/Cell/lv2/sys_net.cpp | 2 +- rpcs3/Emu/Cell/lv2/sys_overlay.cpp | 2 +- rpcs3/Emu/Cell/lv2/sys_prx.cpp | 2 +- rpcs3/Emu/Cell/lv2/sys_sync.h | 2 +- rpcs3/Emu/IdManager.h | 2 +- 11 files changed, 12 insertions(+), 12 deletions(-) diff --git a/rpcs3/Emu/Cell/lv2/sys_cond.cpp b/rpcs3/Emu/Cell/lv2/sys_cond.cpp index bcfe83aed3..4387d27306 100644 --- a/rpcs3/Emu/Cell/lv2/sys_cond.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_cond.cpp @@ -71,7 +71,7 @@ CellError lv2_cond::on_id_create() std::function lv2_cond::load(utils::serial& ar) { - return load_func(make_shared(ar)); + return load_func(make_shared(stx::exact_t(ar))); } void lv2_cond::save(utils::serial& ar) diff --git a/rpcs3/Emu/Cell/lv2/sys_event.cpp b/rpcs3/Emu/Cell/lv2/sys_event.cpp index 0428fd0f11..d5ad126142 100644 --- a/rpcs3/Emu/Cell/lv2/sys_event.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_event.cpp @@ -37,8 +37,8 @@ lv2_event_queue::lv2_event_queue(utils::serial& ar) noexcept std::function lv2_event_queue::load(utils::serial& ar) { - auto queue = make_shared(ar); - return [ptr = lv2_obj::load(queue->key, queue)](void* storage) { *static_cast*>(storage) = ptr; }; + auto queue = make_shared(stx::exact_t(ar)); + return [ptr = lv2_obj::load(queue->key, queue)](void* storage) { *static_cast*>(storage) = ptr; }; } void lv2_event_queue::save(utils::serial& ar) diff --git a/rpcs3/Emu/Cell/lv2/sys_event_flag.cpp b/rpcs3/Emu/Cell/lv2/sys_event_flag.cpp index 6c630e9261..971408e042 100644 --- a/rpcs3/Emu/Cell/lv2/sys_event_flag.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_event_flag.cpp @@ -24,7 +24,7 @@ lv2_event_flag::lv2_event_flag(utils::serial& ar) std::function lv2_event_flag::load(utils::serial& ar) { - return load_func(make_shared(ar)); + return load_func(make_shared(stx::exact_t(ar))); } void lv2_event_flag::save(utils::serial& ar) diff --git a/rpcs3/Emu/Cell/lv2/sys_memory.cpp b/rpcs3/Emu/Cell/lv2/sys_memory.cpp index de5590426e..6cfc3a1860 100644 --- a/rpcs3/Emu/Cell/lv2/sys_memory.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_memory.cpp @@ -33,7 +33,7 @@ std::function lv2_memory_container::load(utils::serial& ar) // Use idm::last_id() only for the instances at IDM return [ptr = make_shared(stx::exact_t(ar), true)](void* storage) { - *static_cast*>(storage) = ptr; + *static_cast*>(storage) = ptr; }; } diff --git a/rpcs3/Emu/Cell/lv2/sys_mmapper.cpp b/rpcs3/Emu/Cell/lv2/sys_mmapper.cpp index d134102a73..805d5db0ad 100644 --- a/rpcs3/Emu/Cell/lv2/sys_mmapper.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_mmapper.cpp @@ -84,7 +84,7 @@ CellError lv2_memory::on_id_create() std::function lv2_memory::load(utils::serial& ar) { - auto mem = make_shared(ar); + auto mem = make_shared(stx::exact_t(ar)); mem->exists++; // Disable on_id_create() auto func = load_func(mem, +mem->pshared); mem->exists--; diff --git a/rpcs3/Emu/Cell/lv2/sys_mutex.cpp b/rpcs3/Emu/Cell/lv2/sys_mutex.cpp index c8ac190c25..f2d23291ff 100644 --- a/rpcs3/Emu/Cell/lv2/sys_mutex.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_mutex.cpp @@ -27,7 +27,7 @@ lv2_mutex::lv2_mutex(utils::serial& ar) std::function lv2_mutex::load(utils::serial& ar) { - return load_func(make_shared(ar)); + return load_func(make_shared(stx::exact_t(ar))); } void lv2_mutex::save(utils::serial& ar) diff --git a/rpcs3/Emu/Cell/lv2/sys_net.cpp b/rpcs3/Emu/Cell/lv2/sys_net.cpp index 97d40d6d47..aae2454619 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net.cpp @@ -293,7 +293,7 @@ std::function lv2_socket::load(utils::serial& ar) sock_lv2->bind(sock_lv2->last_bound_addr); } - return [ptr = sock_lv2](void* storage) { *static_cast*>(storage) = ptr; };; + return [ptr = sock_lv2](void* storage) { *static_cast*>(storage) = ptr; };; } void lv2_socket::save(utils::serial& ar, bool save_only_this_class) diff --git a/rpcs3/Emu/Cell/lv2/sys_overlay.cpp b/rpcs3/Emu/Cell/lv2/sys_overlay.cpp index 99b5981795..66481ed280 100644 --- a/rpcs3/Emu/Cell/lv2/sys_overlay.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_overlay.cpp @@ -112,7 +112,7 @@ std::function lv2_overlay::load(utils::serial& ar) return [ovlm](void* storage) { - *static_cast*>(storage) = ovlm; + *static_cast*>(storage) = ovlm; }; } diff --git a/rpcs3/Emu/Cell/lv2/sys_prx.cpp b/rpcs3/Emu/Cell/lv2/sys_prx.cpp index 42903d7454..f918070a4e 100644 --- a/rpcs3/Emu/Cell/lv2/sys_prx.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_prx.cpp @@ -372,7 +372,7 @@ std::function lv2_prx::load(utils::serial& ar) return [prx](void* storage) { - *static_cast*>(storage) = prx; + *static_cast*>(storage) = prx; }; } diff --git a/rpcs3/Emu/Cell/lv2/sys_sync.h b/rpcs3/Emu/Cell/lv2/sys_sync.h index 644e0c90f2..bd6004dfaa 100644 --- a/rpcs3/Emu/Cell/lv2/sys_sync.h +++ b/rpcs3/Emu/Cell/lv2/sys_sync.h @@ -449,7 +449,7 @@ public: static std::function load_func(shared_ptr make, u64 pshared = umax) { const u64 key = make->key; - return [ptr = load(key, make, pshared)](void* storage) { *static_cast*>(storage) = ptr; }; + return [ptr = load(key, make, pshared)](void* storage) { *static_cast*>(storage) = ptr; }; } static bool wait_timeout(u64 usec, ppu_thread* cpu = {}, bool scale = true, bool is_usleep = false); diff --git a/rpcs3/Emu/IdManager.h b/rpcs3/Emu/IdManager.h index 99082462f9..26162045b7 100644 --- a/rpcs3/Emu/IdManager.h +++ b/rpcs3/Emu/IdManager.h @@ -123,7 +123,7 @@ namespace id_manager ptr = stx::make_shared(stx::exact_t(ar)); } - return [ptr](void* storage) { *static_cast*>(storage) = ptr; }; + return [ptr](void* storage) { *static_cast*>(storage) = ptr; }; }; }; From 51417cc8c36eda525574c231a2a826e8c6705e6b Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sat, 4 Jan 2025 02:11:19 +0100 Subject: [PATCH 08/60] Qt: stop batch compilation immediately on cancel --- rpcs3/rpcs3qt/game_list_frame.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/rpcs3/rpcs3qt/game_list_frame.cpp b/rpcs3/rpcs3qt/game_list_frame.cpp index 12886b8664..aa004211b9 100644 --- a/rpcs3/rpcs3qt/game_list_frame.cpp +++ b/rpcs3/rpcs3qt/game_list_frame.cpp @@ -2414,6 +2414,14 @@ void game_list_frame::BatchCreateCPUCaches(const std::vector& game_da pdlg->setAutoReset(false); pdlg->open(); + connect(pdlg, &progress_dialog::canceled, this, []() + { + if (!Emu.IsStopped()) + { + Emu.GracefulShutdown(false, true); + } + }); + BatchActionBySerials(pdlg, serials, tr("%0\nProgress: %1/%2 caches compiled").arg(main_label), [&, game_data](const std::string& serial) { From 635eac7704b674ba78e8a122fb99cc7b689fa747 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sat, 4 Jan 2025 02:13:39 +0100 Subject: [PATCH 09/60] Qt: fix batch compilation after a game was already running Batch compilation wasn't working if g_system_progress_canceled wasn't reset --- rpcs3/Emu/system_progress.cpp | 1 + rpcs3/rpcs3qt/game_list_frame.cpp | 14 ++++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/rpcs3/Emu/system_progress.cpp b/rpcs3/Emu/system_progress.cpp index feeaea352d..66d59af65b 100644 --- a/rpcs3/Emu/system_progress.cpp +++ b/rpcs3/Emu/system_progress.cpp @@ -41,6 +41,7 @@ void progress_dialog_server::operator()() { std::shared_ptr native_dlg; g_system_progress_stopping = false; + g_system_progress_canceled = false; const auto get_state = []() { diff --git a/rpcs3/rpcs3qt/game_list_frame.cpp b/rpcs3/rpcs3qt/game_list_frame.cpp index aa004211b9..a5fc1f4525 100644 --- a/rpcs3/rpcs3qt/game_list_frame.cpp +++ b/rpcs3/rpcs3qt/game_list_frame.cpp @@ -2277,11 +2277,13 @@ void game_list_frame::RemoveHDD1Cache(const std::string& base_dir, const std::st game_list_log.fatal("Only %d/%d HDD1 cache directories could be removed in %s (%s)", dirs_removed, dirs_total, base_dir, title_id); } -void game_list_frame::BatchActionBySerials(progress_dialog* pdlg, const std::set& serials, QString progressLabel, std::function action, std::function cancel_log, bool refresh_on_finish, bool can_be_concurrent, std::function should_wait_cb) +void game_list_frame::BatchActionBySerials(progress_dialog* pdlg, const std::set& serials, QString progressLabel, std::function action, std::function cancel_log, bool refresh_on_finish, bool can_be_concurrent, std::function should_wait_cb) { // Concurrent tasks should not wait (at least not in current implementation) ensure(!should_wait_cb || !can_be_concurrent); + g_system_progress_canceled = false; + const std::shared_ptr> iterate_over_serial = std::make_shared>(); const std::shared_ptr> index = std::make_shared>(0); @@ -2297,12 +2299,16 @@ void game_list_frame::BatchActionBySerials(progress_dialog* pdlg, const std::set const std::string& serial = *std::next(serials.begin(), index); - if (pdlg->wasCanceled() || g_system_progress_canceled) + if (pdlg->wasCanceled() || g_system_progress_canceled.exchange(false)) { - cancel_log(index, serials_size); + if (cancel_log) + { + cancel_log(index, serials_size); + } return false; } - else if (action(serial)) + + if (action(serial)) { const int done = index_ptr->load(); pdlg->setLabelText(progressLabel.arg(done + 1).arg(serials_size)); From d2b96636db1d76c8ea00064a5dbd3c120b69afdf Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sat, 4 Jan 2025 02:13:59 +0100 Subject: [PATCH 10/60] Qt: delete batch progress dialog after user closed it (as originally intended) --- rpcs3/rpcs3qt/game_list_frame.cpp | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/rpcs3/rpcs3qt/game_list_frame.cpp b/rpcs3/rpcs3qt/game_list_frame.cpp index a5fc1f4525..02ca16716e 100644 --- a/rpcs3/rpcs3qt/game_list_frame.cpp +++ b/rpcs3/rpcs3qt/game_list_frame.cpp @@ -2366,19 +2366,17 @@ void game_list_frame::BatchActionBySerials(progress_dialog* pdlg, const std::set if ((*iterate_over_serial)(*index)) { QTimer::singleShot(1, this, *periodic_func); + return; } - else + + pdlg->setLabelText(progressLabel.arg(*index).arg(serials_size)); + pdlg->setCancelButtonText(tr("OK")); + connect(pdlg, &progress_dialog::canceled, this, [pdlg](){ pdlg->deleteLater(); }); + QApplication::beep(); + + if (refresh_on_finish && index) { - pdlg->setLabelText(progressLabel.arg(*index).arg(serials_size)); - pdlg->setCancelButtonText(tr("OK")); - QApplication::beep(); - - if (refresh_on_finish && index) - { - Refresh(true); - } - - pdlg->deleteLater(); + Refresh(true); } }; From af3154a0d42be778d861a06fac2fc1374190f38c Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sat, 4 Jan 2025 14:58:15 +0100 Subject: [PATCH 11/60] Qt: delete settings_dialog --- rpcs3/rpcs3qt/main_window.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index 3526cce235..f499cd6474 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -394,7 +394,7 @@ void main_window::OnMissingFw() { const QString title = tr("Missing Firmware Detected!"); const QString message = tr("Commercial games require the firmware (PS3UPDAT.PUP file) to be installed." - "\n
For information about how to obtain the required firmware read the quickstart guide.").arg(gui::utils::get_link_style()); + "\n
For information about how to obtain the required firmware read the quickstart guide.").arg(gui::utils::get_link_style()); QMessageBox* mb = new QMessageBox(QMessageBox::Question, title, message, QMessageBox::Ok | QMessageBox::Cancel, this, Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint | Qt::WindowStaysOnTopHint); mb->setTextFormat(Qt::RichText); @@ -2852,6 +2852,7 @@ void main_window::CreateConnects() connect(dlg, &settings_dialog::EmuSettingsApplied, this, &main_window::NotifyEmuSettingsChange); connect(dlg, &settings_dialog::EmuSettingsApplied, this, &main_window::update_gui_pad_thread); connect(dlg, &settings_dialog::EmuSettingsApplied, m_log_frame, &log_frame::LoadSettings); + dlg->setAttribute(Qt::WA_DeleteOnClose); dlg->open(); }; From f255392a75ad6474b6fb879789b57097831aebcb Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sat, 4 Jan 2025 15:00:14 +0100 Subject: [PATCH 12/60] patches: create patch path on update --- rpcs3/rpcs3qt/patch_manager_dialog.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/rpcs3/rpcs3qt/patch_manager_dialog.cpp b/rpcs3/rpcs3qt/patch_manager_dialog.cpp index 74b25a0b5c..a504e5790f 100644 --- a/rpcs3/rpcs3qt/patch_manager_dialog.cpp +++ b/rpcs3/rpcs3qt/patch_manager_dialog.cpp @@ -1247,7 +1247,16 @@ bool patch_manager_dialog::handle_json(const QByteArray& data) if (patch_engine::load(patches, "From Download", content, true, &log_message)) { patch_log.notice("Successfully validated downloaded patch file"); - const std::string path = patch_engine::get_patches_path() + "patch.yml"; + + const std::string patches_path = patch_engine::get_patches_path(); + + if (!fs::create_path(patches_path)) + { + patch_log.fatal("Failed to create path: %s (%s)", patches_path, fs::g_tls_error); + return false; + } + + const std::string path = patches_path + "patch.yml"; // Back up current patch file if possible if (fs::is_file(path)) From da84326bd0b01aecdd907fe812492e77f194e8a5 Mon Sep 17 00:00:00 2001 From: Aleksey Komarov Date: Wed, 1 Jan 2025 22:25:43 +0300 Subject: [PATCH 13/60] vk: Add driver vendor PANVK (Mali GPU on Mesa) --- rpcs3/Emu/RSX/VK/VKCompute.cpp | 1 + rpcs3/Emu/RSX/VK/VKHelpers.cpp | 3 +++ rpcs3/Emu/RSX/VK/VKRenderTargets.h | 1 + rpcs3/Emu/RSX/VK/vkutils/chip_class.h | 3 ++- rpcs3/Emu/RSX/VK/vkutils/device.cpp | 7 +++++++ 5 files changed, 14 insertions(+), 1 deletion(-) diff --git a/rpcs3/Emu/RSX/VK/VKCompute.cpp b/rpcs3/Emu/RSX/VK/VKCompute.cpp index 499976b660..e7cfb617ba 100644 --- a/rpcs3/Emu/RSX/VK/VKCompute.cpp +++ b/rpcs3/Emu/RSX/VK/VKCompute.cpp @@ -78,6 +78,7 @@ namespace vk break; case vk::driver_vendor::LAVAPIPE: case vk::driver_vendor::V3DV: + case vk::driver_vendor::PANVK: // TODO: Actually bench this. Using 32 for now to match other common configurations. case vk::driver_vendor::DOZEN: // Actual optimal size depends on the D3D device. Use 32 since it should work well on both AMD and NVIDIA diff --git a/rpcs3/Emu/RSX/VK/VKHelpers.cpp b/rpcs3/Emu/RSX/VK/VKHelpers.cpp index 7789661ef2..2a31ffa51d 100644 --- a/rpcs3/Emu/RSX/VK/VKHelpers.cpp +++ b/rpcs3/Emu/RSX/VK/VKHelpers.cpp @@ -144,6 +144,9 @@ namespace vk case driver_vendor::HONEYKRISP: // Needs more testing break; + case driver_vendor::PANVK: + // Needs more testing + break; default: rsx_log.warning("Unsupported device: %s", gpu_name); } diff --git a/rpcs3/Emu/RSX/VK/VKRenderTargets.h b/rpcs3/Emu/RSX/VK/VKRenderTargets.h index 4bee28227a..19c9f04f63 100644 --- a/rpcs3/Emu/RSX/VK/VKRenderTargets.h +++ b/rpcs3/Emu/RSX/VK/VKRenderTargets.h @@ -190,6 +190,7 @@ namespace vk case driver_vendor::LAVAPIPE: case driver_vendor::V3DV: case driver_vendor::HONEYKRISP: + case driver_vendor::PANVK: break; } diff --git a/rpcs3/Emu/RSX/VK/vkutils/chip_class.h b/rpcs3/Emu/RSX/VK/vkutils/chip_class.h index 07b087d403..a905b7cc07 100644 --- a/rpcs3/Emu/RSX/VK/vkutils/chip_class.h +++ b/rpcs3/Emu/RSX/VK/vkutils/chip_class.h @@ -54,7 +54,8 @@ namespace vk LAVAPIPE, NVK, V3DV, - HONEYKRISP + HONEYKRISP, + PANVK }; driver_vendor get_driver_vendor(); diff --git a/rpcs3/Emu/RSX/VK/vkutils/device.cpp b/rpcs3/Emu/RSX/VK/vkutils/device.cpp index 25b2a57d62..ad80b3786f 100644 --- a/rpcs3/Emu/RSX/VK/vkutils/device.cpp +++ b/rpcs3/Emu/RSX/VK/vkutils/device.cpp @@ -302,6 +302,11 @@ namespace vk return driver_vendor::HONEYKRISP; } + if (gpu_name.find("Panfrost") != umax) + { + return driver_vendor::PANVK; + } + return driver_vendor::unknown; } else @@ -329,6 +334,8 @@ namespace vk return driver_vendor::V3DV; case VK_DRIVER_ID_MESA_HONEYKRISP: return driver_vendor::HONEYKRISP; + case VK_DRIVER_ID_MESA_PANVK: + return driver_vendor::PANVK; default: // Mobile? return driver_vendor::unknown; From c9d39ce7ae253f0d9c5859dbb2ec999d46118e5b Mon Sep 17 00:00:00 2001 From: Aleksey Komarov Date: Wed, 1 Jan 2025 22:28:04 +0300 Subject: [PATCH 14/60] vk: Support panvk, allow creating device without textureCompressionBC panvk supports BC1-BC3 which is all RPCS3 require, support is reported as false since not all formats are supported --- rpcs3/Emu/RSX/VK/vkutils/device.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/rpcs3/Emu/RSX/VK/vkutils/device.cpp b/rpcs3/Emu/RSX/VK/vkutils/device.cpp index ad80b3786f..aa326982f6 100644 --- a/rpcs3/Emu/RSX/VK/vkutils/device.cpp +++ b/rpcs3/Emu/RSX/VK/vkutils/device.cpp @@ -666,6 +666,12 @@ namespace vk enabled_features.textureCompressionBC = VK_FALSE; } + if (!pgpu->features.textureCompressionBC && pgpu->get_driver_vendor() == driver_vendor::PANVK) + { + rsx_log.error("Your GPU running on the PANVK driver does not support full texture block compression. Graphics may not render correctly."); + enabled_features.textureCompressionBC = VK_FALSE; + } + VkDeviceCreateInfo device = {}; device.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; device.pNext = nullptr; From 036693a14dd5003e436596b1ade7842a920aa210 Mon Sep 17 00:00:00 2001 From: Elad <18193363+elad335@users.noreply.github.com> Date: Sun, 5 Jan 2025 04:51:02 +0200 Subject: [PATCH 15/60] Fix sys_config_unregister_service --- rpcs3/Emu/Cell/lv2/sys_config.cpp | 17 +++++++++++++ rpcs3/Emu/Cell/lv2/sys_config.h | 6 +++-- rpcs3/util/shared_ptr.hpp | 40 ++++++++++++++++++------------- 3 files changed, 45 insertions(+), 18 deletions(-) diff --git a/rpcs3/Emu/Cell/lv2/sys_config.cpp b/rpcs3/Emu/Cell/lv2/sys_config.cpp index c39444ccd0..b254af8206 100644 --- a/rpcs3/Emu/Cell/lv2/sys_config.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_config.cpp @@ -133,6 +133,23 @@ lv2_config_service_event& lv2_config_service_event::operator=(thread_state s) no return *this; } +lv2_config_service_event::~lv2_config_service_event() noexcept +{ + operator=(thread_state::destroying_context); +} + +lv2_config::~lv2_config() noexcept +{ + for (auto& [key, event] : events) + { + if (event) + { + // Avoid collision with lv2_config_service_event destructor + event->m_destroyed = true; + } + } +} + // LV2 Config Service Listener bool lv2_config_service_listener::check_service(const lv2_config_service& service) const { diff --git a/rpcs3/Emu/Cell/lv2/sys_config.h b/rpcs3/Emu/Cell/lv2/sys_config.h index 3915dfc8cb..0d7fede8b1 100644 --- a/rpcs3/Emu/Cell/lv2/sys_config.h +++ b/rpcs3/Emu/Cell/lv2/sys_config.h @@ -161,6 +161,8 @@ public: return null_ptr; } + + ~lv2_config() noexcept; }; /* @@ -276,7 +278,7 @@ public: // Utilities usz get_size() const { return sizeof(sys_config_service_event_t)-1 + data.size(); } - shared_ptr get_shared_ptr () const { return idm::get_unlocked(idm_id); } + shared_ptr get_shared_ptr () const { return stx::make_shared_from_this(this); } u32 get_id() const { return idm_id; } }; @@ -342,7 +344,7 @@ public: // Utilities u32 get_id() const { return idm_id; } - shared_ptr get_shared_ptr() const { return idm::get_unlocked(idm_id); } + shared_ptr get_shared_ptr() const { return stx::make_shared_from_this(this); } }; /* diff --git a/rpcs3/util/shared_ptr.hpp b/rpcs3/util/shared_ptr.hpp index fb3c2c4019..e9bdf83b3b 100644 --- a/rpcs3/util/shared_ptr.hpp +++ b/rpcs3/util/shared_ptr.hpp @@ -361,13 +361,8 @@ namespace stx [[deprecated("Use null_ptr")]] shared_ptr(std::nullptr_t) = delete; // Not-so-aliasing constructor: emulates std::enable_shared_from_this without its overhead - explicit shared_ptr(T* _this) noexcept - : m_ptr(_this) - { - // Random checks which may fail on invalid pointer - ensure((reinterpret_cast(d()->destroy) - 0x10000) >> 47 == 0); - ensure((d()->refs++ - 1) >> 58 == 0); - } + template + friend shared_ptr make_shared_from_this(const Type* _this) noexcept; template requires same_ptr_implicit_v shared_ptr(const shared_ptr& r) noexcept @@ -562,11 +557,29 @@ namespace stx template requires (std::is_constructible_v, T&&>) - static shared_ptr> make_shared_value(T&& value) + static shared_ptr> make_shared_value(T&& value) noexcept { return make_single_value(std::forward(value)); } + // Not-so-aliasing constructor: emulates std::enable_shared_from_this without its overhead + template + static shared_ptr make_shared_from_this(const T* _this) noexcept + { + shared_ptr r; + r.m_ptr = const_cast(_this); + + if (!_this) [[unlikely]] + { + return r; + } + + // Random checks which may fail on invalid pointer + ensure((reinterpret_cast(r.d()->destroy.load()) - 0x10000) >> 47 == 0); + ensure((r.d()->refs++ - 1) >> 58 == 0); + return r; + } + // Atomic simplified shared pointer template class atomic_ptr @@ -1059,9 +1072,9 @@ namespace stx do { // Update old head with current value - next.m_ptr = reinterpret_cast(old.m_val.raw() >> c_ref_size); + next.m_ptr = std::launder(ptr_to(old.m_val.raw())); - } while (!m_val.compare_exchange(old.m_val.raw(), reinterpret_cast(exch.m_ptr) << c_ref_size)); + } while (!m_val.compare_exchange(old.m_val.raw(), to_val(exch.m_ptr))); // This argument is consumed (moved from) exch.m_ptr = nullptr; @@ -1076,7 +1089,7 @@ namespace stx // Simple atomic load is much more effective than load(), but it's a non-owning reference T* observe() const noexcept { - return reinterpret_cast(m_val >> c_ref_size); + return std::launder(ptr_to(m_val)); } explicit constexpr operator bool() const noexcept @@ -1138,11 +1151,6 @@ namespace stx return false; } - constexpr std::nullptr_t get() const noexcept - { - return nullptr; - } - } null_ptr; } From 6165c2d8b3db598bd7137c79f7916ff803a3fe3f Mon Sep 17 00:00:00 2001 From: Elad <18193363+elad335@users.noreply.github.com> Date: Sun, 5 Jan 2025 07:44:56 +0200 Subject: [PATCH 16/60] Thread.h: Fix a bug by adding thread_state::destroying_context --- Utilities/Thread.h | 10 +++++++--- rpcs3/Emu/Cell/lv2/sys_config.cpp | 2 +- rpcs3/Emu/Cell/lv2/sys_config.h | 7 +++++-- rpcs3/Emu/Cell/lv2/sys_net/lv2_socket.cpp | 2 +- rpcs3/Emu/IdManager.h | 8 ++++---- rpcs3/util/fixed_typemap.hpp | 13 +++++++++++++ 6 files changed, 31 insertions(+), 11 deletions(-) diff --git a/Utilities/Thread.h b/Utilities/Thread.h index 4350915d70..37c4a56f4c 100644 --- a/Utilities/Thread.h +++ b/Utilities/Thread.h @@ -33,7 +33,8 @@ enum class thread_state : u32 aborting = 1, // The thread has been joined in the destructor or explicitly aborted errored = 2, // Set after the emergency_exit call finished = 3, // Final state, always set at the end of thread execution - mask = 3 + mask = 3, + destroying_context = 7, // Special value assigned to destroy data explicitly before the destructor }; template @@ -702,14 +703,17 @@ public: thread::m_sync.notify_all(); } - if (s == thread_state::finished) + if (s == thread_state::finished || s == thread_state::destroying_context) { // This participates in emulation stopping, use destruction-alike semantics thread::join(true); + } + if (s == thread_state::destroying_context) + { if constexpr (std::is_assignable_v) { - static_cast(*this) = thread_state::finished; + static_cast(*this) = thread_state::destroying_context; } } diff --git a/rpcs3/Emu/Cell/lv2/sys_config.cpp b/rpcs3/Emu/Cell/lv2/sys_config.cpp index b254af8206..6740759d3b 100644 --- a/rpcs3/Emu/Cell/lv2/sys_config.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_config.cpp @@ -122,7 +122,7 @@ void lv2_config::remove_service_event(u32 id) lv2_config_service_event& lv2_config_service_event::operator=(thread_state s) noexcept { - if (s == thread_state::finished) + if (s == thread_state::destroying_context && !m_destroyed.exchange(true)) { if (auto global = g_fxo->try_get()) { diff --git a/rpcs3/Emu/Cell/lv2/sys_config.h b/rpcs3/Emu/Cell/lv2/sys_config.h index 0d7fede8b1..ea9b9da76d 100644 --- a/rpcs3/Emu/Cell/lv2/sys_config.h +++ b/rpcs3/Emu/Cell/lv2/sys_config.h @@ -362,6 +362,10 @@ class lv2_config_service_event return g_fxo->get().next_id++; } + atomic_t m_destroyed = false; + + friend class lv2_config; + public: const u32 id; @@ -393,8 +397,7 @@ public: // Destructor lv2_config_service_event& operator=(thread_state s) noexcept; - - ~lv2_config_service_event() noexcept = default; + ~lv2_config_service_event() noexcept; // Notify queue that this event exists bool notify() const; diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket.cpp b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket.cpp index d95c6935fc..7bdd18b3fd 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket.cpp @@ -178,7 +178,7 @@ void lv2_socket::queue_wake(ppu_thread* ppu) lv2_socket& lv2_socket::operator=(thread_state s) noexcept { - if (s == thread_state::finished) + if (s == thread_state::destroying_context) { close(); } diff --git a/rpcs3/Emu/IdManager.h b/rpcs3/Emu/IdManager.h index 26162045b7..7846d6f70f 100644 --- a/rpcs3/Emu/IdManager.h +++ b/rpcs3/Emu/IdManager.h @@ -805,8 +805,8 @@ public: { if (ptr) { - constexpr thread_state finished{3}; - *static_cast(ptr.get()) = finished; + constexpr thread_state destroying_context{7}; + *static_cast(ptr.get()) = destroying_context; } } @@ -837,8 +837,8 @@ public: { if (ptr) { - constexpr thread_state finished{3}; - *static_cast(ptr.get()) = finished; + constexpr thread_state destroying_context{7}; + *static_cast(ptr.get()) = destroying_context; } } diff --git a/rpcs3/util/fixed_typemap.hpp b/rpcs3/util/fixed_typemap.hpp index c742f8b03b..85d04c57a1 100644 --- a/rpcs3/util/fixed_typemap.hpp +++ b/rpcs3/util/fixed_typemap.hpp @@ -347,6 +347,19 @@ namespace stx } } + // Order semi-destructors before the actual destructors + // This allows to safely access data that may be deallocated or destroyed from other members of FXO regardless of their intialization time + for (u32 i = 0; i < _max; i++) + { + const auto info = (*std::prev(m_info, i + 1)); + + if (auto op = info->thread_op) + { + constexpr thread_state destroying_context{7}; + op(*std::prev(m_order, i + 1), destroying_context); + } + } + // Destroy objects in reverse order for (; _max; _max--) { From 1080969b51708735223375dcaec70c990779a518 Mon Sep 17 00:00:00 2001 From: oltolm Date: Tue, 3 Sep 2024 10:47:19 +0200 Subject: [PATCH 17/60] cmake: remove "--no-quick" from windeployqt --- rpcs3/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpcs3/CMakeLists.txt b/rpcs3/CMakeLists.txt index 059d0a4dbf..2b76f3c5e7 100644 --- a/rpcs3/CMakeLists.txt +++ b/rpcs3/CMakeLists.txt @@ -164,7 +164,7 @@ elseif(WIN32) COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/bin/GuiConfigs $/GuiConfigs COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/bin/git $/git COMMAND "${WINDEPLOYQT_EXECUTABLE}" --no-compiler-runtime --no-opengl-sw --no-patchqt - --no-translations --no-quick --no-system-d3d-compiler --no-quick-import + --no-translations --no-system-d3d-compiler --no-quick-import --plugindir "$,$/plugins,$/share/qt6/plugins>" --verbose 0 "$") endif() From 41a4dd4c1c8a5ecab4cb76d127f76edf45658244 Mon Sep 17 00:00:00 2001 From: oltolm Date: Sun, 5 Jan 2025 14:21:36 +0100 Subject: [PATCH 18/60] cmake: fix wolfssl --- 3rdparty/wolfssl/CMakeLists.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/3rdparty/wolfssl/CMakeLists.txt b/3rdparty/wolfssl/CMakeLists.txt index f74f604269..cf1a66a1f5 100644 --- a/3rdparty/wolfssl/CMakeLists.txt +++ b/3rdparty/wolfssl/CMakeLists.txt @@ -22,6 +22,5 @@ else() add_subdirectory(wolfssl EXCLUDE_FROM_ALL) - target_compile_definitions(wolfssl PUBLIC WOLFSSL_DES_ECB HAVE_WRITE_DUP) - target_compile_definitions(wolfssl PUBLIC FP_MAX_BITS=8192) + target_compile_definitions(wolfssl PUBLIC WOLFSSL_DES_ECB HAVE_WRITE_DUP FP_MAX_BITS=8192 WOLFSSL_NO_OPTIONS_H) endif() From 99f1f4c22a9f110dfcfcf441b33c742552b76d36 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sat, 4 Jan 2025 15:46:15 +0100 Subject: [PATCH 19/60] cheats: add float32 gui support --- Utilities/cheat_info.h | 1 + rpcs3/rpcs3qt/cheat_manager.cpp | 43 ++++++++++++++++++++------------- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/Utilities/cheat_info.h b/Utilities/cheat_info.h index 9c77d8dd7f..3ceb32716b 100644 --- a/Utilities/cheat_info.h +++ b/Utilities/cheat_info.h @@ -14,6 +14,7 @@ enum class cheat_type : u8 signed_16_cheat, signed_32_cheat, signed_64_cheat, + float_32_cheat, max }; diff --git a/rpcs3/rpcs3qt/cheat_manager.cpp b/rpcs3/rpcs3qt/cheat_manager.cpp index 27e0860232..d5af8a1a2f 100644 --- a/rpcs3/rpcs3qt/cheat_manager.cpp +++ b/rpcs3/rpcs3qt/cheat_manager.cpp @@ -43,6 +43,7 @@ void fmt_class_string::format(std::string& out, u64 arg) case cheat_type::signed_16_cheat: return "Signed 16 bits"; case cheat_type::signed_32_cheat: return "Signed 32 bits"; case cheat_type::signed_64_cheat: return "Signed 64 bits"; + case cheat_type::float_32_cheat: return "Float 32 bits"; case cheat_type::max: break; } @@ -612,8 +613,9 @@ cheat_manager_dialog::cheat_manager_dialog(QWidget* parent) return; } - bool success; - u64 result_value; + bool success = false; + u64 result_value {}; + f64 result_value_f {}; switch (cheat->type) { @@ -625,6 +627,7 @@ cheat_manager_dialog::cheat_manager_dialog(QWidget* parent) case cheat_type::signed_16_cheat: result_value = cheat_engine::get_value(final_offset, success); break; case cheat_type::signed_32_cheat: result_value = cheat_engine::get_value(final_offset, success); break; case cheat_type::signed_64_cheat: result_value = cheat_engine::get_value(final_offset, success); break; + case cheat_type::float_32_cheat: result_value_f = cheat_engine::get_value(final_offset, success); break; default: log_cheat.fatal("Unsupported cheat type"); return; } @@ -632,6 +635,8 @@ cheat_manager_dialog::cheat_manager_dialog(QWidget* parent) { if (cheat->type >= cheat_type::signed_8_cheat && cheat->type <= cheat_type::signed_64_cheat) edt_value_final->setText(tr("%1").arg(static_cast(result_value))); + else if (cheat->type == cheat_type::float_32_cheat) + edt_value_final->setText(tr("%1").arg(result_value_f)); else edt_value_final->setText(tr("%1").arg(result_value)); } @@ -795,6 +800,7 @@ cheat_manager_dialog::cheat_manager_dialog(QWidget* parent) case cheat_type::signed_16_cheat: results = convert_and_set(final_offset); break; case cheat_type::signed_32_cheat: results = convert_and_set(final_offset); break; case cheat_type::signed_64_cheat: results = convert_and_set(final_offset); break; + case cheat_type::float_32_cheat: results = convert_and_set(final_offset); break; default: log_cheat.fatal("Unsupported cheat type"); return; } @@ -888,8 +894,6 @@ cheat_manager_dialog* cheat_manager_dialog::get_dlg(QWidget* parent) template T cheat_manager_dialog::convert_from_QString(const QString& str, bool& success) { - T result; - if constexpr (std::is_same_v) { const u16 result_16 = str.toUShort(&success); @@ -897,17 +901,17 @@ T cheat_manager_dialog::convert_from_QString(const QString& str, bool& success) if (result_16 > 0xFF) success = false; - result = static_cast(result_16); + return static_cast(result_16); } if constexpr (std::is_same_v) - result = str.toUShort(&success); + return str.toUShort(&success); if constexpr (std::is_same_v) - result = str.toUInt(&success); + return str.toUInt(&success); if constexpr (std::is_same_v) - result = str.toULongLong(&success); + return str.toULongLong(&success); if constexpr (std::is_same_v) { @@ -915,28 +919,31 @@ T cheat_manager_dialog::convert_from_QString(const QString& str, bool& success) if (result_16 < -128 || result_16 > 127) success = false; - result = static_cast(result_16); + return static_cast(result_16); } if constexpr (std::is_same_v) - result = str.toShort(&success); + return str.toShort(&success); if constexpr (std::is_same_v) - result = str.toInt(&success); + return str.toInt(&success); if constexpr (std::is_same_v) - result = str.toLongLong(&success); + return str.toLongLong(&success); - return result; + if constexpr (std::is_same_v) + return str.toFloat(&success); + + return {}; } template bool cheat_manager_dialog::convert_and_search() { - bool res_conv; + bool res_conv = false; const QString to_search = edt_cheat_search_value->text(); - T value = convert_from_QString(to_search, res_conv); + const T value = convert_from_QString(to_search, res_conv); if (!res_conv) return false; @@ -948,10 +955,10 @@ bool cheat_manager_dialog::convert_and_search() template std::pair cheat_manager_dialog::convert_and_set(u32 offset) { - bool res_conv; + bool res_conv = false; const QString to_set = edt_value_final->text(); - T value = convert_from_QString(to_set, res_conv); + const T value = convert_from_QString(to_set, res_conv); if (!res_conv) return {false, false}; @@ -974,6 +981,7 @@ void cheat_manager_dialog::do_the_search() case cheat_type::signed_16_cheat: res_conv = convert_and_search(); break; case cheat_type::signed_32_cheat: res_conv = convert_and_search(); break; case cheat_type::signed_64_cheat: res_conv = convert_and_search(); break; + case cheat_type::float_32_cheat: res_conv = convert_and_search(); break; default: log_cheat.fatal("Unsupported cheat type"); break; } @@ -1065,6 +1073,7 @@ QString cheat_manager_dialog::get_localized_cheat_type(cheat_type type) case cheat_type::signed_16_cheat: return tr("Signed 16 bits"); case cheat_type::signed_32_cheat: return tr("Signed 32 bits"); case cheat_type::signed_64_cheat: return tr("Signed 64 bits"); + case cheat_type::float_32_cheat: return tr("Float 32 bits"); case cheat_type::max: break; } std::string type_formatted; From 1ab3a0bd7346f16df6a46863f3130e961b5d2fbe Mon Sep 17 00:00:00 2001 From: Megamouse Date: Fri, 3 Jan 2025 20:22:36 +0100 Subject: [PATCH 20/60] RSX/Qt: Reuse gs_frame if possible --- rpcs3/Emu/Cell/lv2/sys_process.cpp | 3 ++ rpcs3/Emu/RSX/GL/GLGSRender.cpp | 8 ++++ rpcs3/Emu/RSX/GL/GLGSRender.h | 1 + rpcs3/Emu/RSX/GSFrameBase.h | 1 + rpcs3/Emu/RSX/GSRender.cpp | 7 ++- rpcs3/Emu/RSX/GSRender.h | 3 ++ .../HomeMenu/overlay_home_menu_main_menu.cpp | 1 + .../HomeMenu/overlay_home_menu_savestate.cpp | 1 + rpcs3/Emu/System.cpp | 16 ++++++- rpcs3/Emu/System.h | 11 +++++ rpcs3/main_application.cpp | 4 +- rpcs3/main_application.h | 5 ++- rpcs3/rpcs3qt/gl_gs_frame.cpp | 5 +++ rpcs3/rpcs3qt/gl_gs_frame.h | 1 + rpcs3/rpcs3qt/gs_frame.cpp | 5 +++ rpcs3/rpcs3qt/gs_frame.h | 1 + rpcs3/rpcs3qt/gui_application.cpp | 43 +++++++++++++++---- rpcs3/rpcs3qt/main_window.cpp | 1 + 18 files changed, 101 insertions(+), 16 deletions(-) diff --git a/rpcs3/Emu/Cell/lv2/sys_process.cpp b/rpcs3/Emu/Cell/lv2/sys_process.cpp index 8038ffc248..45eb5c1858 100644 --- a/rpcs3/Emu/Cell/lv2/sys_process.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_process.cpp @@ -498,6 +498,9 @@ void lv2_exitspawn(ppu_thread& ppu, std::vector& argv, std::vector< }; signal_system_cache_can_stay(); + + // Make sure we keep the game window opened + Emu.SetContinuousMode(true); Emu.Kill(false); }); diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.cpp b/rpcs3/Emu/RSX/GL/GLGSRender.cpp index 3e60af9f68..a2d080c86e 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.cpp +++ b/rpcs3/Emu/RSX/GL/GLGSRender.cpp @@ -52,6 +52,14 @@ GLGSRender::GLGSRender(utils::serial* ar) noexcept : GSRender(ar) backend_config.supports_normalized_barycentrics = true; } +GLGSRender::~GLGSRender() +{ + if (m_frame) + { + m_frame->reset(); + } +} + extern CellGcmContextData current_context; void GLGSRender::set_viewport() diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.h b/rpcs3/Emu/RSX/GL/GLGSRender.h index 866fe288e6..c8c6ba89dd 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.h +++ b/rpcs3/Emu/RSX/GL/GLGSRender.h @@ -159,6 +159,7 @@ public: GLGSRender(utils::serial* ar) noexcept; GLGSRender() noexcept : GLGSRender(nullptr) {} + virtual ~GLGSRender(); private: diff --git a/rpcs3/Emu/RSX/GSFrameBase.h b/rpcs3/Emu/RSX/GSFrameBase.h index f345f6255e..fd91244ffe 100644 --- a/rpcs3/Emu/RSX/GSFrameBase.h +++ b/rpcs3/Emu/RSX/GSFrameBase.h @@ -14,6 +14,7 @@ public: virtual ~GSFrameBase() = default; virtual void close() = 0; + virtual void reset() = 0; virtual bool shown() = 0; virtual void hide() = 0; virtual void show() = 0; diff --git a/rpcs3/Emu/RSX/GSRender.cpp b/rpcs3/Emu/RSX/GSRender.cpp index 917a772a34..2025842dab 100644 --- a/rpcs3/Emu/RSX/GSRender.cpp +++ b/rpcs3/Emu/RSX/GSRender.cpp @@ -18,7 +18,7 @@ GSRender::~GSRender() { m_context = nullptr; - if (m_frame) + if (m_frame && !m_continuous_mode) { m_frame->close(); } @@ -39,7 +39,10 @@ void GSRender::on_exit() if (m_frame) { - m_frame->hide(); + if (!m_continuous_mode) + { + m_frame->hide(); + } m_frame->delete_context(m_context); m_context = nullptr; } diff --git a/rpcs3/Emu/RSX/GSRender.h b/rpcs3/Emu/RSX/GSRender.h index eea040bf29..d2a6fd9c5f 100644 --- a/rpcs3/Emu/RSX/GSRender.h +++ b/rpcs3/Emu/RSX/GSRender.h @@ -21,12 +21,15 @@ class GSRender : public rsx::thread protected: GSFrameBase* m_frame; draw_context_t m_context = nullptr; + bool m_continuous_mode = false; public: ~GSRender() override; GSRender(utils::serial* ar) noexcept; + void set_continuous_mode(bool continuous_mode) { m_continuous_mode = continuous_mode; } + void on_init_thread() override; void on_exit() override; diff --git a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_main_menu.cpp b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_main_menu.cpp index d02b3ffbe2..2bce50e829 100644 --- a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_main_menu.cpp +++ b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_main_menu.cpp @@ -114,6 +114,7 @@ namespace rsx Emu.CallFromMainThread([]() { + Emu.SetContinuousMode(true); Emu.Restart(false); }); return page_navigation::exit; diff --git a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_savestate.cpp b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_savestate.cpp index 2ff9711d5b..60fbbd8224 100644 --- a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_savestate.cpp +++ b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_savestate.cpp @@ -26,6 +26,7 @@ namespace rsx if (!suspend_mode) { Emu.after_kill_callback = []() { Emu.Restart(); }; + Emu.SetContinuousMode(true); } Emu.Kill(false, true); }); diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index 551e7de36d..704319d0a8 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -60,6 +60,8 @@ #include "Emu/RSX/VK/VulkanAPI.h" #endif +#include "Emu/RSX/GSRender.h" + LOG_CHANNEL(sys_log, "SYS"); // Preallocate 32 MiB @@ -1005,6 +1007,16 @@ void Emulator::SetForceBoot(bool force_boot) m_force_boot = force_boot; } +void Emulator::SetContinuousMode(bool continuous_mode) +{ + m_continuous_mode = continuous_mode; + + if (GSRender* render = static_cast(g_fxo->try_get())) + { + render->set_continuous_mode(continuous_mode); + } +} + game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch, usz recursion_count) { if (recursion_count == 0 && m_restrict_emu_state_change) @@ -2897,6 +2909,9 @@ void qt_events_aware_op(int repeat_duration_ms, std::function wrapped_op void Emulator::GracefulShutdown(bool allow_autoexit, bool async_op, bool savestate) { + // Make sure we close the game window + Emu.SetContinuousMode(false); + // Ensure no game has booted inbetween const auto guard = Emu.MakeEmulationStateGuard(); @@ -3278,7 +3293,6 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s thread_ctrl::wait_for(5'000); } - *closed_sucessfully = true; })); diff --git a/rpcs3/Emu/System.h b/rpcs3/Emu/System.h index d9d1991b3e..747d6d494b 100644 --- a/rpcs3/Emu/System.h +++ b/rpcs3/Emu/System.h @@ -154,6 +154,7 @@ class Emulator final // 2. It signifies that we don't want to exit on Kill(), for example if we want to transition to another application. bool m_force_boot = false; + bool m_continuous_mode = false; bool m_has_gui = true; bool m_state_inspection_savestate = false; @@ -346,6 +347,15 @@ public: return m_config_mode == cfg_mode::continuous; } + bool ContinuousModeEnabled(bool reset) + { + if (reset) + { + return std::exchange(m_continuous_mode, false); + } + return m_continuous_mode; + } + class emulation_state_guard_t { class Emulator* _this = nullptr; @@ -385,6 +395,7 @@ public: bool BootRsxCapture(const std::string& path); void SetForceBoot(bool force_boot); + void SetContinuousMode(bool continuous_mode); game_boot_result Load(const std::string& title_id = "", bool is_disc_patch = false, usz recursion_count = 0); void Run(bool start_playtime); diff --git a/rpcs3/main_application.cpp b/rpcs3/main_application.cpp index 0040bb235e..d86d6523b2 100644 --- a/rpcs3/main_application.cpp +++ b/rpcs3/main_application.cpp @@ -133,7 +133,7 @@ EmuCallbacks main_application::CreateCallbacks() basic_keyboard_handler* ret = g_fxo->init(Emu.DeserialManager()); ensure(ret); ret->moveToThread(get_thread()); - ret->SetTargetWindow(m_game_window); + ret->SetTargetWindow(reinterpret_cast(m_game_window)); break; } } @@ -170,7 +170,7 @@ EmuCallbacks main_application::CreateCallbacks() basic_mouse_handler* ret = g_fxo->init(Emu.DeserialManager()); ensure(ret); ret->moveToThread(get_thread()); - ret->SetTargetWindow(m_game_window); + ret->SetTargetWindow(reinterpret_cast(m_game_window)); break; } case mouse_handler::raw: diff --git a/rpcs3/main_application.h b/rpcs3/main_application.h index e378eb5c53..dd0806970d 100644 --- a/rpcs3/main_application.h +++ b/rpcs3/main_application.h @@ -1,9 +1,10 @@ #pragma once #include -#include +#include struct EmuCallbacks; +class gs_frame; class main_application { @@ -25,5 +26,5 @@ protected: EmuCallbacks CreateCallbacks(); std::string m_active_user; - QWindow* m_game_window = nullptr; // (Currently) only needed so that pad handlers have a valid target for event filtering. + gs_frame* m_game_window = nullptr; }; diff --git a/rpcs3/rpcs3qt/gl_gs_frame.cpp b/rpcs3/rpcs3qt/gl_gs_frame.cpp index 1e3f6f8fc4..94b6aa964f 100644 --- a/rpcs3/rpcs3qt/gl_gs_frame.cpp +++ b/rpcs3/rpcs3qt/gl_gs_frame.cpp @@ -28,6 +28,11 @@ gl_gs_frame::gl_gs_frame(QScreen* screen, const QRect& geometry, const QIcon& ap show(); } +void gl_gs_frame::reset() +{ + m_primary_context = nullptr; +} + draw_context_t gl_gs_frame::make_context() { auto context = new GLContext(); diff --git a/rpcs3/rpcs3qt/gl_gs_frame.h b/rpcs3/rpcs3qt/gl_gs_frame.h index bc6e9ad65e..d1129f8e3a 100644 --- a/rpcs3/rpcs3qt/gl_gs_frame.h +++ b/rpcs3/rpcs3qt/gl_gs_frame.h @@ -20,6 +20,7 @@ private: public: explicit gl_gs_frame(QScreen* screen, const QRect& geometry, const QIcon& appIcon, std::shared_ptr gui_settings, bool force_fullscreen); + void reset() override; draw_context_t make_context() override; void set_current(draw_context_t ctx) override; void delete_context(draw_context_t ctx) override; diff --git a/rpcs3/rpcs3qt/gs_frame.cpp b/rpcs3/rpcs3qt/gs_frame.cpp index 8af0b84757..ef4131c3b4 100644 --- a/rpcs3/rpcs3qt/gs_frame.cpp +++ b/rpcs3/rpcs3qt/gs_frame.cpp @@ -328,6 +328,7 @@ void gs_frame::handle_shortcut(gui::shortcuts::shortcut shortcut_key, const QKey { Emu.Restart(); }; + Emu.SetContinuousMode(true); } Emu.Kill(false, true); @@ -623,6 +624,10 @@ void gs_frame::close() }); } +void gs_frame::reset() +{ +} + bool gs_frame::shown() { return QWindow::isVisible(); diff --git a/rpcs3/rpcs3qt/gs_frame.h b/rpcs3/rpcs3qt/gs_frame.h index 97a777360c..a6d5f41b19 100644 --- a/rpcs3/rpcs3qt/gs_frame.h +++ b/rpcs3/rpcs3qt/gs_frame.h @@ -80,6 +80,7 @@ protected: void showEvent(QShowEvent *event) override; void close() override; + void reset() override; bool shown() override; void hide() override; diff --git a/rpcs3/rpcs3qt/gui_application.cpp b/rpcs3/rpcs3qt/gui_application.cpp index 0a3ff110ce..08af646b50 100644 --- a/rpcs3/rpcs3qt/gui_application.cpp +++ b/rpcs3/rpcs3qt/gui_application.cpp @@ -348,6 +348,28 @@ void gui_application::InitializeConnects() std::unique_ptr gui_application::get_gs_frame() { + // Load AppIcon + const QIcon app_icon = m_main_window ? m_main_window->GetAppIcon() : gui::utils::get_app_icon_from_path(Emu.GetBoot(), Emu.GetTitleID()); + + if (m_game_window) + { + // Check if the continuous mode is enabled. We reset the mode after each use in order to ensure that it is only used when explicitly needed. + const bool continuous_mode_enabled = Emu.ContinuousModeEnabled(true); + + if (Emu.IsChildProcess() || continuous_mode_enabled) + { + gui_log.notice("gui_application: Re-using old game window (IsChildProcess=%d, ContinuousModeEnabled=%d)", Emu.IsChildProcess(), continuous_mode_enabled); + + if (!app_icon.isNull()) + { + m_game_window->setIcon(app_icon); + } + return std::unique_ptr(m_game_window); + } + } + + gui_log.notice("gui_application: Creating new game window"); + extern const std::unordered_map, value_hash> g_video_out_resolution_map; auto [w, h] = ::at32(g_video_out_resolution_map, g_cfg.video.resolution); @@ -424,9 +446,6 @@ std::unique_ptr gui_application::get_gs_frame() frame_geometry.setSize(QSize(w, h)); } - // Load AppIcon - const QIcon app_icon = m_main_window ? m_main_window->GetAppIcon() : gui::utils::get_app_icon_from_path(Emu.GetBoot(), Emu.GetTitleID()); - gs_frame* frame = nullptr; switch (g_cfg.video.renderer.get()) @@ -446,6 +465,12 @@ std::unique_ptr gui_application::get_gs_frame() m_game_window = frame; + connect(m_game_window, &gs_frame::destroyed, this, [this]() + { + gui_log.notice("gui_application: Deleting old game window"); + m_game_window = nullptr; + }); + return std::unique_ptr(frame); } @@ -582,10 +607,10 @@ void gui_application::InitializeCallbacks() { switch (type) { - case 0: static_cast(m_game_window)->progress_reset(value); break; - case 1: static_cast(m_game_window)->progress_increment(value); break; - case 2: static_cast(m_game_window)->progress_set_limit(value); break; - case 3: static_cast(m_game_window)->progress_set_value(value); break; + case 0: m_game_window->progress_reset(value); break; + case 1: m_game_window->progress_increment(value); break; + case 2: m_game_window->progress_set_limit(value); break; + case 3: m_game_window->progress_set_value(value); break; default: gui_log.fatal("Unknown type in handle_taskbar_progress(type=%d, value=%d)", type, value); break; } } @@ -1045,7 +1070,7 @@ void gui_application::OnShortcutChange() { if (m_game_window) { - static_cast(m_game_window)->update_shortcuts(); + m_game_window->update_shortcuts(); } } @@ -1074,7 +1099,7 @@ void gui_application::OnAppStateChanged(Qt::ApplicationState state) } const auto emu_state = Emu.GetStatus(); - const bool is_active = state == Qt::ApplicationActive; + const bool is_active = state & Qt::ApplicationActive; if (emu_state != system_state::paused && emu_state != system_state::running) { diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index f499cd6474..0702e43020 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -2590,6 +2590,7 @@ void main_window::CreateConnects() { Emu.Restart(); }; + Emu.SetContinuousMode(true); } Emu.Kill(false, true); From 3ce4c95e6100b266e98c8be862d3c496effebd14 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sat, 4 Jan 2025 13:27:29 +0100 Subject: [PATCH 21/60] Show message while creating savestate in continuous mode --- rpcs3/Emu/System.cpp | 21 ++++---- rpcs3/Emu/localized_string_id.h | 1 + rpcs3/Emu/system_progress.cpp | 85 +++++++++++++++++++++---------- rpcs3/Emu/system_progress.hpp | 9 +++- rpcs3/rpcs3qt/gui_application.cpp | 2 +- rpcs3/rpcs3qt/localized_emu.h | 1 + 6 files changed, 80 insertions(+), 39 deletions(-) diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index 704319d0a8..d792e7caea 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -3200,15 +3200,15 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s sys_log.notice("Stopping emulator..."); + const bool continuous_savestate_mode = savestate && !g_cfg.savestate.suspend_emu; + + // Show visual feedback to the user in case that stopping takes a while. + // This needs to be done before actually stopping, because otherwise the necessary threads will be terminated before we can show an image. + if (g_fxo->try_get>() && (continuous_savestate_mode || g_progr_text.operator bool())) { - // Show visual feedback to the user in case that stopping takes a while. - // This needs to be done before actually stopping, because otherwise the necessary threads will be terminated before we can show an image. - if (auto progress_dialog = g_fxo->try_get>(); progress_dialog && g_progr_text.operator bool()) - { - // We are currently showing a progress dialog. Notify it that we are going to stop emulation. - g_system_progress_stopping = true; - std::this_thread::sleep_for(20ms); // Enough for one frame to be rendered - } + // Notify progress dialog that we are going to stop emulation + g_system_progress_stopping = continuous_savestate_mode ? system_progress_stop_state::stop_state_continuous_savestate : system_progress_stop_state::stop_state_stopping; + std::this_thread::sleep_for(30ms); // Enough for one frame to be rendered } // Signal threads @@ -3280,7 +3280,10 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s if (auto ar_ptr = to_ar->load()) { // Total amount of waiting: about 10s - GetCallbacks().on_save_state_progress(closed_sucessfully, ar_ptr, verbose_message.get(), init_mtx); + if (g_cfg.savestate.suspend_emu) + { + GetCallbacks().on_save_state_progress(closed_sucessfully, ar_ptr, verbose_message.get(), init_mtx); + } while (thread_ctrl::state() != thread_state::aborting) { diff --git a/rpcs3/Emu/localized_string_id.h b/rpcs3/Emu/localized_string_id.h index 21437762bb..d1f576051d 100644 --- a/rpcs3/Emu/localized_string_id.h +++ b/rpcs3/Emu/localized_string_id.h @@ -295,6 +295,7 @@ enum class localized_string_id PROGRESS_DIALOG_OF, PROGRESS_DIALOG_PLEASE_WAIT, PROGRESS_DIALOG_STOPPING_PLEASE_WAIT, + PROGRESS_DIALOG_SAVESTATE_PLEASE_WAIT, PROGRESS_DIALOG_SCANNING_PPU_EXECUTABLE, PROGRESS_DIALOG_ANALYZING_PPU_EXECUTABLE, PROGRESS_DIALOG_SCANNING_PPU_MODULES, diff --git a/rpcs3/Emu/system_progress.cpp b/rpcs3/Emu/system_progress.cpp index 66d59af65b..52e49516fb 100644 --- a/rpcs3/Emu/system_progress.cpp +++ b/rpcs3/Emu/system_progress.cpp @@ -26,7 +26,7 @@ atomic_t g_progr_pdone{0}; atomic_t g_system_progress_canceled{false}; // For showing feedback while stopping emulation -atomic_t g_system_progress_stopping{false}; +atomic_t g_system_progress_stopping{system_progress_stop_state::stop_state_disabled}; namespace rsx::overlays { @@ -40,7 +40,7 @@ namespace rsx::overlays void progress_dialog_server::operator()() { std::shared_ptr native_dlg; - g_system_progress_stopping = false; + g_system_progress_stopping = system_progress_stop_state::stop_state_disabled; g_system_progress_canceled = false; const auto get_state = []() @@ -63,6 +63,41 @@ void progress_dialog_server::operator()() return whole_state; }; + const auto create_native_dialog = [&native_dlg](const std::string& text, bool* show_overlay_message) + { + if (const auto renderer = rsx::get_current_renderer()) + { + // Some backends like OpenGL actually initialize a lot of driver objects in the "on_init" method. + // Wait for init to complete within reasonable time. Abort just in case we have hardware/driver issues. + renderer->is_initialized.wait(0, atomic_wait_timeout(5 * 1000000000ull)); + + auto manager = g_fxo->try_get(); + + if (show_overlay_message) + { + *show_overlay_message = g_fxo->get().show_overlay_message_only; + if (*show_overlay_message) + { + return; + } + } + + if (manager) + { + MsgDialogType type{}; + type.se_mute_on = true; + type.se_normal = true; + type.bg_invisible = true; + type.disable_cancel = true; + type.progress_bar_count = 1; + + native_dlg = manager->create(true); + native_dlg->show(false, text, type, msg_dialog_source::sys_progress, nullptr); + native_dlg->progress_bar_set_message(0, get_localized_string(localized_string_id::PROGRESS_DIALOG_PLEASE_WAIT)); + } + } + }; + while (!g_system_progress_stopping && thread_ctrl::state() != thread_state::aborting) { // Wait for the start condition @@ -113,29 +148,7 @@ void progress_dialog_server::operator()() bool show_overlay_message = false; // Only show an overlay message after initial loading is done. std::shared_ptr dlg; - if (const auto renderer = rsx::get_current_renderer()) - { - // Some backends like OpenGL actually initialize a lot of driver objects in the "on_init" method. - // Wait for init to complete within reasonable time. Abort just in case we have hardware/driver issues. - renderer->is_initialized.wait(0, atomic_wait_timeout(5 * 1000000000ull)); - - auto manager = g_fxo->try_get(); - show_overlay_message = g_fxo->get().show_overlay_message_only; - - if (manager && !show_overlay_message) - { - MsgDialogType type{}; - type.se_mute_on = true; - type.se_normal = true; - type.bg_invisible = true; - type.disable_cancel = true; - type.progress_bar_count = 1; - - native_dlg = manager->create(true); - native_dlg->show(false, text0, type, msg_dialog_source::sys_progress, nullptr); - native_dlg->progress_bar_set_message(0, get_localized_string(localized_string_id::PROGRESS_DIALOG_PLEASE_WAIT)); - } - } + create_native_dialog(text0, &show_overlay_message); if (!show_overlay_message && !native_dlg && (dlg = Emu.GetCallbacks().get_msg_dialog())) { @@ -392,6 +405,7 @@ void progress_dialog_server::operator()() else if (native_dlg) { native_dlg->close(false, false); + native_dlg.reset(); } else if (dlg) { @@ -411,10 +425,25 @@ void progress_dialog_server::operator()() g_progr_ptotal.notify_all(); } - if (native_dlg && g_system_progress_stopping) + if (g_system_progress_stopping) { - native_dlg->set_text(get_localized_string(localized_string_id::PROGRESS_DIALOG_STOPPING_PLEASE_WAIT)); - native_dlg->refresh(); + const std::string text = get_localized_string( + g_system_progress_stopping == system_progress_stop_state::stop_state_continuous_savestate + ? localized_string_id::PROGRESS_DIALOG_SAVESTATE_PLEASE_WAIT + : localized_string_id::PROGRESS_DIALOG_STOPPING_PLEASE_WAIT + ); + if (native_dlg) + { + native_dlg->set_text(text); + } + else + { + create_native_dialog(text, nullptr); + } + if (native_dlg) + { + native_dlg->refresh(); + } } if (g_progr_ptotal.exchange(0)) diff --git a/rpcs3/Emu/system_progress.hpp b/rpcs3/Emu/system_progress.hpp index 13ae8d13eb..67e4e68df1 100644 --- a/rpcs3/Emu/system_progress.hpp +++ b/rpcs3/Emu/system_progress.hpp @@ -36,6 +36,13 @@ struct alignas(16) progress_dialog_string_t } }; +enum system_progress_stop_state : u32 +{ + stop_state_disabled = 0, + stop_state_stopping, + stop_state_continuous_savestate +}; + extern progress_dialog_string_t g_progr_text; extern atomic_t g_progr_ftotal; extern atomic_t g_progr_fdone; @@ -44,7 +51,7 @@ extern atomic_t g_progr_fknown_bits; extern atomic_t g_progr_ptotal; extern atomic_t g_progr_pdone; extern atomic_t g_system_progress_canceled; -extern atomic_t g_system_progress_stopping; +extern atomic_t g_system_progress_stopping; // Initialize progress dialog (can be recursive) class scoped_progress_dialog final diff --git a/rpcs3/rpcs3qt/gui_application.cpp b/rpcs3/rpcs3qt/gui_application.cpp index 08af646b50..e16f97aff0 100644 --- a/rpcs3/rpcs3qt/gui_application.cpp +++ b/rpcs3/rpcs3qt/gui_application.cpp @@ -795,7 +795,7 @@ void gui_application::InitializeCallbacks() verbose_message += ". "; } - verbose_message += "If Stuck, Report To Developers"; + verbose_message += tr("If Stuck, Report To Developers").toStdString(); } else { diff --git a/rpcs3/rpcs3qt/localized_emu.h b/rpcs3/rpcs3qt/localized_emu.h index 8ea21f6d03..7e8ae3f803 100644 --- a/rpcs3/rpcs3qt/localized_emu.h +++ b/rpcs3/rpcs3qt/localized_emu.h @@ -314,6 +314,7 @@ private: case localized_string_id::PROGRESS_DIALOG_OF: return tr("of"); case localized_string_id::PROGRESS_DIALOG_PLEASE_WAIT: return tr("Please wait"); case localized_string_id::PROGRESS_DIALOG_STOPPING_PLEASE_WAIT: return tr("Stopping. Please wait..."); + case localized_string_id::PROGRESS_DIALOG_SAVESTATE_PLEASE_WAIT: return tr("Creating savestate. Please wait..."); case localized_string_id::PROGRESS_DIALOG_SCANNING_PPU_EXECUTABLE: return tr("Scanning PPU Executable..."); case localized_string_id::PROGRESS_DIALOG_ANALYZING_PPU_EXECUTABLE: return tr("Analyzing PPU Executable..."); case localized_string_id::PROGRESS_DIALOG_SCANNING_PPU_MODULES: return tr("Scanning PPU Modules..."); From cc7e7300ce1f3ef23c129751568edc5df2e2f30d Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sat, 4 Jan 2025 14:23:10 +0100 Subject: [PATCH 22/60] Clean up old game window in case of unexpected errors --- rpcs3/Emu/System.cpp | 5 +++++ rpcs3/Emu/System.h | 1 + rpcs3/headless_application.cpp | 1 + rpcs3/rpcs3qt/gs_frame.cpp | 10 ++++++++++ rpcs3/rpcs3qt/gs_frame.h | 3 +++ rpcs3/rpcs3qt/gui_application.cpp | 13 +++++++++++++ 6 files changed, 33 insertions(+) diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index d792e7caea..7494d512b9 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -966,6 +966,11 @@ game_boot_result Emulator::BootGame(const std::string& path, const std::string& std::tie(m_path, m_path_original, argv, envp, data, disc, klic, hdd1, m_config_mode, m_config_path) = std::move(save_args); }; } + + if (result != game_boot_result::no_errors) + { + GetCallbacks().close_gs_frame(); + } } return result; diff --git a/rpcs3/Emu/System.h b/rpcs3/Emu/System.h index 747d6d494b..fd48c199ae 100644 --- a/rpcs3/Emu/System.h +++ b/rpcs3/Emu/System.h @@ -86,6 +86,7 @@ struct EmuCallbacks std::function init_pad_handler; std::function update_emu_settings; std::function save_emu_settings; + std::function close_gs_frame; std::function()> get_gs_frame; std::function()> get_camera_handler; std::function()> get_music_handler; diff --git a/rpcs3/headless_application.cpp b/rpcs3/headless_application.cpp index b12b6d87e2..e82bcc12dc 100644 --- a/rpcs3/headless_application.cpp +++ b/rpcs3/headless_application.cpp @@ -120,6 +120,7 @@ void headless_application::InitializeCallbacks() return nullptr; }; + callbacks.close_gs_frame = [](){}; callbacks.get_gs_frame = []() -> std::unique_ptr { if (g_cfg.video.renderer != video_renderer::null) diff --git a/rpcs3/rpcs3qt/gs_frame.cpp b/rpcs3/rpcs3qt/gs_frame.cpp index ef4131c3b4..41421cfdcf 100644 --- a/rpcs3/rpcs3qt/gs_frame.cpp +++ b/rpcs3/rpcs3qt/gs_frame.cpp @@ -603,6 +603,11 @@ void gs_frame::close() gui_log.notice("Closing game window"); + if (m_ignore_stop_events) + { + return; + } + Emu.CallFromMainThread([this]() { // Hide window if necessary @@ -1139,6 +1144,11 @@ bool gs_frame::event(QEvent* ev) gui_log.notice("Game window close event issued"); + if (m_ignore_stop_events) + { + return QWindow::event(ev); + } + if (Emu.IsStopped()) { // This should be unreachable, but never say never. Properly close the window anyway. diff --git a/rpcs3/rpcs3qt/gs_frame.h b/rpcs3/rpcs3qt/gs_frame.h index a6d5f41b19..07f3527d61 100644 --- a/rpcs3/rpcs3qt/gs_frame.h +++ b/rpcs3/rpcs3qt/gs_frame.h @@ -45,6 +45,7 @@ private: u32 m_hide_mouse_idletime = 2000; // ms bool m_flip_showed_frame = false; bool m_start_games_fullscreen = false; + bool m_ignore_stop_events = false; std::shared_ptr m_video_encoder{}; @@ -52,6 +53,8 @@ public: explicit gs_frame(QScreen* screen, const QRect& geometry, const QIcon& appIcon, std::shared_ptr gui_settings, bool force_fullscreen); ~gs_frame(); + void ignore_stop_events() { m_ignore_stop_events = true; } + draw_context_t make_context() override; void set_current(draw_context_t context) override; void delete_context(draw_context_t context) override; diff --git a/rpcs3/rpcs3qt/gui_application.cpp b/rpcs3/rpcs3qt/gui_application.cpp index e16f97aff0..bacc530051 100644 --- a/rpcs3/rpcs3qt/gui_application.cpp +++ b/rpcs3/rpcs3qt/gui_application.cpp @@ -366,6 +366,9 @@ std::unique_ptr gui_application::get_gs_frame() } return std::unique_ptr(m_game_window); } + + // Clean-up old game window. This should only happen if there was an unexpected error during boot. + Emu.GetCallbacks().close_gs_frame(); } gui_log.notice("gui_application: Creating new game window"); @@ -564,6 +567,16 @@ void gui_application::InitializeCallbacks() return nullptr; }; + callbacks.close_gs_frame = [this]() + { + if (m_game_window) + { + gui_log.warning("gui_application: Closing old game window"); + m_game_window->ignore_stop_events(); + delete m_game_window; + m_game_window = nullptr; + } + }; callbacks.get_gs_frame = [this]() -> std::unique_ptr { return get_gs_frame(); }; callbacks.get_msg_dialog = [this]() -> std::shared_ptr { return m_show_gui ? std::make_shared() : nullptr; }; callbacks.get_osk_dialog = [this]() -> std::shared_ptr { return m_show_gui ? std::make_shared() : nullptr; }; From d5470d92ece3b20527bd103f7622b815db808421 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sat, 4 Jan 2025 16:14:09 +0100 Subject: [PATCH 23/60] Keep game window open when loading the last savestate --- .../RSX/Overlays/HomeMenu/overlay_home_menu_main_menu.cpp | 1 + .../RSX/Overlays/HomeMenu/overlay_home_menu_savestate.cpp | 2 ++ rpcs3/Emu/System.cpp | 7 +++++-- rpcs3/Emu/System.h | 2 +- rpcs3/Emu/savestate_utils.cpp | 5 ++++- rpcs3/rpcs3qt/gs_frame.cpp | 2 ++ rpcs3/rpcs3qt/main_window.cpp | 2 ++ 7 files changed, 17 insertions(+), 4 deletions(-) diff --git a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_main_menu.cpp b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_main_menu.cpp index 2bce50e829..6706e241c8 100644 --- a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_main_menu.cpp +++ b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_main_menu.cpp @@ -114,6 +114,7 @@ namespace rsx Emu.CallFromMainThread([]() { + // Make sure we keep the game window opened Emu.SetContinuousMode(true); Emu.Restart(false); }); diff --git a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_savestate.cpp b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_savestate.cpp index 60fbbd8224..e224bc5727 100644 --- a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_savestate.cpp +++ b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_savestate.cpp @@ -26,6 +26,8 @@ namespace rsx if (!suspend_mode) { Emu.after_kill_callback = []() { Emu.Restart(); }; + + // Make sure we keep the game window opened Emu.SetContinuousMode(true); } Emu.Kill(false, true); diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index 7494d512b9..ee0c8204c1 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -2912,10 +2912,13 @@ u64 get_sysutil_cb_manager_read_count(); void qt_events_aware_op(int repeat_duration_ms, std::function wrapped_op); -void Emulator::GracefulShutdown(bool allow_autoexit, bool async_op, bool savestate) +void Emulator::GracefulShutdown(bool allow_autoexit, bool async_op, bool savestate, bool continuous_mode) { // Make sure we close the game window - Emu.SetContinuousMode(false); + if (!continuous_mode) + { + Emu.SetContinuousMode(false); + } // Ensure no game has booted inbetween const auto guard = Emu.MakeEmulationStateGuard(); diff --git a/rpcs3/Emu/System.h b/rpcs3/Emu/System.h index fd48c199ae..2934253b39 100644 --- a/rpcs3/Emu/System.h +++ b/rpcs3/Emu/System.h @@ -419,7 +419,7 @@ public: bool Pause(bool freeze_emulation = false, bool show_resume_message = true); void Resume(); - void GracefulShutdown(bool allow_autoexit = true, bool async_op = false, bool savestate = false); + void GracefulShutdown(bool allow_autoexit = true, bool async_op = false, bool savestate = false, bool continuous_mode = false); void Kill(bool allow_autoexit = true, bool savestate = false, savestate_stage* stage = nullptr); game_boot_result Restart(bool graceful = true); bool Quit(bool force_quit); diff --git a/rpcs3/Emu/savestate_utils.cpp b/rpcs3/Emu/savestate_utils.cpp index 85a2a82574..a40cda95c3 100644 --- a/rpcs3/Emu/savestate_utils.cpp +++ b/rpcs3/Emu/savestate_utils.cpp @@ -322,7 +322,10 @@ bool boot_last_savestate(bool testing) if (result) { sys_log.success("Booting the most recent savestate \'%s\' using the Reload shortcut.", savestate_path); - Emu.GracefulShutdown(false); + + // Make sure we keep the game window opened + Emu.SetContinuousMode(true); + Emu.GracefulShutdown(false, false, false, true); if (game_boot_result error = Emu.BootGame(savestate_path, "", true); error != game_boot_result::no_errors) { diff --git a/rpcs3/rpcs3qt/gs_frame.cpp b/rpcs3/rpcs3qt/gs_frame.cpp index 41421cfdcf..f2e6e32b08 100644 --- a/rpcs3/rpcs3qt/gs_frame.cpp +++ b/rpcs3/rpcs3qt/gs_frame.cpp @@ -328,6 +328,8 @@ void gs_frame::handle_shortcut(gui::shortcuts::shortcut shortcut_key, const QKey { Emu.Restart(); }; + + // Make sure we keep the game window opened Emu.SetContinuousMode(true); } diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index 0702e43020..46e056e43a 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -2590,6 +2590,8 @@ void main_window::CreateConnects() { Emu.Restart(); }; + + // Make sure we keep the game window opened Emu.SetContinuousMode(true); } From 7369169331d4f4744be4560aac8ea8ac7b6b8bb3 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sat, 4 Jan 2025 16:14:28 +0100 Subject: [PATCH 24/60] Disable continuous mode if a savestate is not possible --- rpcs3/Emu/System.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index ee0c8204c1..ced67c8a7e 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -3063,6 +3063,22 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s *pause_thread = make_ptr(new named_thread("Savestate Prepare Thread"sv, [pause_thread, allow_autoexit, this]() mutable { + struct scoped_success_guard + { + bool save_state_success = false; + ~scoped_success_guard() + { + if (!save_state_success) + { + // Reset continuous mode on savestate error + Emu.SetContinuousMode(false); + + // Reset after_kill_callback (which is usually used for Emu.Restart in combination with savestates) + Emu.after_kill_callback = nullptr; + } + } + } success_guard {}; + std::vector>, u32>> paused_spus; if (!try_lock_spu_threads_in_a_state_compatible_with_savestates(false, &paused_spus)) @@ -3133,6 +3149,8 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s return; } + success_guard.save_state_success = true; + CallFromMainThread([allow_autoexit, this, paused_spus]() { savestate_stage stage{}; From 2ac171a30fed6a4e0932ae7cb05c97a4c06280f3 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sat, 4 Jan 2025 18:12:37 +0100 Subject: [PATCH 25/60] move error_report to ErrorCodes.cpp --- rpcs3/Emu/CMakeLists.txt | 1 + rpcs3/Emu/Cell/ErrorCodes.cpp | 91 +++++++++++++++++++++++++++++++++++ rpcs3/Emu/System.cpp | 85 -------------------------------- rpcs3/Emu/System.h | 2 - rpcs3/emucore.vcxproj | 1 + rpcs3/emucore.vcxproj.filters | 3 ++ 6 files changed, 96 insertions(+), 87 deletions(-) create mode 100644 rpcs3/Emu/Cell/ErrorCodes.cpp diff --git a/rpcs3/Emu/CMakeLists.txt b/rpcs3/Emu/CMakeLists.txt index 4cd083ab90..04ed28e10f 100644 --- a/rpcs3/Emu/CMakeLists.txt +++ b/rpcs3/Emu/CMakeLists.txt @@ -172,6 +172,7 @@ target_link_libraries(rpcs3_emu # Cell target_sources(rpcs3_emu PRIVATE + Cell/ErrorCodes.cpp Cell/MFC.cpp Cell/PPUAnalyser.cpp Cell/PPUDisAsm.cpp diff --git a/rpcs3/Emu/Cell/ErrorCodes.cpp b/rpcs3/Emu/Cell/ErrorCodes.cpp new file mode 100644 index 0000000000..f81fe16a61 --- /dev/null +++ b/rpcs3/Emu/Cell/ErrorCodes.cpp @@ -0,0 +1,91 @@ +#include "stdafx.h" +#include "ErrorCodes.h" +#include "PPUThread.h" +#include "SPUThread.h" + +LOG_CHANNEL(sys_log, "SYS"); + +bool g_log_all_errors = false; + +s32 error_code::error_report(s32 result, const logs::message* channel, const char* fmt, const fmt_type_info* sup, const u64* args) +{ + static thread_local std::string g_tls_error_str; + static thread_local std::unordered_map g_tls_error_stats; + + if (!channel) + { + channel = &sys_log.error; + } + + if (!sup && !args) + { + if (!fmt) + { + // Report and clean error state + for (auto&& pair : g_tls_error_stats) + { + if (pair.second > 3) + { + channel->operator()("Stat: %s [x%u]", pair.first, pair.second); + } + } + + g_tls_error_stats.clear(); + return 0; + } + } + + ensure(fmt); + + const char* func = "Unknown function"; + + if (auto ppu = get_current_cpu_thread()) + { + if (auto current = ppu->current_function) + { + func = current; + } + } + else if (auto spu = get_current_cpu_thread()) + { + if (auto current = spu->current_func; current && spu->start_time) + { + func = current; + } + } + + // Format log message (use preallocated buffer) + g_tls_error_str.clear(); + + fmt::append(g_tls_error_str, "'%s' failed with 0x%08x", func, result); + + // Add spacer between error and fmt if necessary + if (fmt[0] != ' ') + g_tls_error_str += " : "; + + fmt::raw_append(g_tls_error_str, fmt, sup, args); + + // Update stats and check log threshold + + if (g_log_all_errors) [[unlikely]] + { + if (!g_tls_error_stats.empty()) + { + // Report and clean error state + error_report(0, nullptr, nullptr, nullptr, nullptr); + } + + channel->operator()("%s", g_tls_error_str); + } + else + { + const auto stat = ++g_tls_error_stats[g_tls_error_str]; + + if (stat <= 3) + { + channel->operator()("%s [%u]", g_tls_error_str, stat); + } + } + + return result; +} diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index ced67c8a7e..e7c01608d3 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -67,8 +67,6 @@ LOG_CHANNEL(sys_log, "SYS"); // Preallocate 32 MiB stx::manual_typemap g_fixed_typemap; -bool g_log_all_errors = false; - bool g_use_rtm = false; u64 g_rtm_tx_limit1 = 0; u64 g_rtm_tx_limit2 = 0; @@ -3892,89 +3890,6 @@ std::string Emulator::GetFormattedTitle(double fps) const return rpcs3::get_formatted_title(title_data); } -s32 error_code::error_report(s32 result, const logs::message* channel, const char* fmt, const fmt_type_info* sup, const u64* args) -{ - static thread_local std::string g_tls_error_str; - static thread_local std::unordered_map g_tls_error_stats; - - if (!channel) - { - channel = &sys_log.error; - } - - if (!sup && !args) - { - if (!fmt) - { - // Report and clean error state - for (auto&& pair : g_tls_error_stats) - { - if (pair.second > 3) - { - channel->operator()("Stat: %s [x%u]", pair.first, pair.second); - } - } - - g_tls_error_stats.clear(); - return 0; - } - } - - ensure(fmt); - - const char* func = "Unknown function"; - - if (auto ppu = get_current_cpu_thread()) - { - if (auto current = ppu->current_function) - { - func = current; - } - } - else if (auto spu = get_current_cpu_thread()) - { - if (auto current = spu->current_func; current && spu->start_time) - { - func = current; - } - } - - // Format log message (use preallocated buffer) - g_tls_error_str.clear(); - - fmt::append(g_tls_error_str, "'%s' failed with 0x%08x", func, result); - - // Add spacer between error and fmt if necessary - if (fmt[0] != ' ') - g_tls_error_str += " : "; - - fmt::raw_append(g_tls_error_str, fmt, sup, args); - - // Update stats and check log threshold - - if (g_log_all_errors) [[unlikely]] - { - if (!g_tls_error_stats.empty()) - { - // Report and clean error state - error_report(0, nullptr, nullptr, nullptr, nullptr); - } - - channel->operator()("%s", g_tls_error_str); - } - else - { - const auto stat = ++g_tls_error_stats[g_tls_error_str]; - - if (stat <= 3) - { - channel->operator()("%s [%u]", g_tls_error_str, stat); - } - } - - return result; -} - void Emulator::ConfigurePPUCache() const { auto& _main = g_fxo->get>(); diff --git a/rpcs3/Emu/System.h b/rpcs3/Emu/System.h index 2934253b39..bd127aff4f 100644 --- a/rpcs3/Emu/System.h +++ b/rpcs3/Emu/System.h @@ -468,8 +468,6 @@ public: extern Emulator Emu; -extern bool g_log_all_errors; - extern bool g_use_rtm; extern u64 g_rtm_tx_limit1; extern u64 g_rtm_tx_limit2; diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index 0c54d02978..6834ed5530 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -66,6 +66,7 @@ true
+ diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index 90df56b77e..f36b57bb03 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -1327,6 +1327,9 @@ Emu\Audio + + Emu\Cell + From c443326fb182366f7455e3ad08ec62b7ecab6121 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sat, 4 Jan 2025 18:35:03 +0100 Subject: [PATCH 26/60] Do not re-use the old game window if the renderer changed --- rpcs3/Emu/System.cpp | 2 +- rpcs3/rpcs3qt/gs_frame.cpp | 1 + rpcs3/rpcs3qt/gs_frame.h | 5 +++++ rpcs3/rpcs3qt/gui_application.cpp | 7 +++++-- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index e7c01608d3..dd41a5f71d 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -1147,7 +1147,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch, bool resolve_path_as_vfs_path = false; - const bool from_dev_flash = IsPathInsideDir(m_path, g_cfg_vfs.get_dev_flash()); + const bool from_dev_flash = IsPathInsideDir(m_path, g_cfg_vfs.get_dev_flash()); std::string savestate_build_version; std::string savestate_creation_date; diff --git a/rpcs3/rpcs3qt/gs_frame.cpp b/rpcs3/rpcs3qt/gs_frame.cpp index f2e6e32b08..5a9da7dad6 100644 --- a/rpcs3/rpcs3qt/gs_frame.cpp +++ b/rpcs3/rpcs3qt/gs_frame.cpp @@ -73,6 +73,7 @@ gs_frame::gs_frame(QScreen* screen, const QRect& geometry, const QIcon& appIcon, , m_initial_geometry(geometry) , m_gui_settings(std::move(gui_settings)) , m_start_games_fullscreen(force_fullscreen) + , m_renderer(g_cfg.video.renderer) { load_gui_settings(); diff --git a/rpcs3/rpcs3qt/gs_frame.h b/rpcs3/rpcs3qt/gs_frame.h index 07f3527d61..8d6316bd88 100644 --- a/rpcs3/rpcs3qt/gs_frame.h +++ b/rpcs3/rpcs3qt/gs_frame.h @@ -15,6 +15,7 @@ #include class gui_settings; +enum class video_renderer; class gs_frame : public QWindow, public GSFrameBase { @@ -53,6 +54,8 @@ public: explicit gs_frame(QScreen* screen, const QRect& geometry, const QIcon& appIcon, std::shared_ptr gui_settings, bool force_fullscreen); ~gs_frame(); + video_renderer renderer() const { return m_renderer; }; + void ignore_stop_events() { m_ignore_stop_events = true; } draw_context_t make_context() override; @@ -79,6 +82,8 @@ public: void take_screenshot(std::vector data, u32 sshot_width, u32 sshot_height, bool is_bgra) override; protected: + video_renderer m_renderer; + void paintEvent(QPaintEvent *event) override; void showEvent(QShowEvent *event) override; diff --git a/rpcs3/rpcs3qt/gui_application.cpp b/rpcs3/rpcs3qt/gui_application.cpp index bacc530051..b5c2041527 100644 --- a/rpcs3/rpcs3qt/gui_application.cpp +++ b/rpcs3/rpcs3qt/gui_application.cpp @@ -356,7 +356,10 @@ std::unique_ptr gui_application::get_gs_frame() // Check if the continuous mode is enabled. We reset the mode after each use in order to ensure that it is only used when explicitly needed. const bool continuous_mode_enabled = Emu.ContinuousModeEnabled(true); - if (Emu.IsChildProcess() || continuous_mode_enabled) + // Make sure we run the same config + const bool is_same_renderer = m_game_window->renderer() == g_cfg.video.renderer; + + if (is_same_renderer && (Emu.IsChildProcess() || continuous_mode_enabled)) { gui_log.notice("gui_application: Re-using old game window (IsChildProcess=%d, ContinuousModeEnabled=%d)", Emu.IsChildProcess(), continuous_mode_enabled); @@ -367,7 +370,7 @@ std::unique_ptr gui_application::get_gs_frame() return std::unique_ptr(m_game_window); } - // Clean-up old game window. This should only happen if there was an unexpected error during boot. + // Clean-up old game window. This should only happen if the renderer changed or there was an unexpected error during boot. Emu.GetCallbacks().close_gs_frame(); } From 0b784ff2c132c053d45a6b1db488b33888b5e0a6 Mon Sep 17 00:00:00 2001 From: Elad <18193363+elad335@users.noreply.github.com> Date: Tue, 7 Jan 2025 13:41:41 +0200 Subject: [PATCH 27/60] PPU LLVM: Function table dependent resolver hashing --- Utilities/bin_patch.cpp | 5 +++++ rpcs3/Emu/Cell/PPUAnalyser.h | 2 +- rpcs3/Emu/Cell/PPUModule.cpp | 1 + rpcs3/Emu/Cell/PPUThread.cpp | 19 +++++++++++++++++++ rpcs3/Emu/Cell/lv2/sys_overlay.h | 1 - 5 files changed, 26 insertions(+), 2 deletions(-) diff --git a/Utilities/bin_patch.cpp b/Utilities/bin_patch.cpp index 49b19f5bda..7e4c0709cd 100644 --- a/Utilities/bin_patch.cpp +++ b/Utilities/bin_patch.cpp @@ -1449,6 +1449,8 @@ static usz apply_modification(std::vector& applied, patch_engine::patch_inf void patch_engine::apply(std::vector& applied_total, const std::string& name, std::function mem_translate, u32 filesz, u32 min_addr) { + applied_total.clear(); + if (!m_map.contains(name)) { return; @@ -1597,6 +1599,9 @@ void patch_engine::apply(std::vector& applied_total, const std::string& nam } } } + + // Ensure consistent order + std::sort(applied_total.begin(), applied_total.end()); } void patch_engine::unload(const std::string& name) diff --git a/rpcs3/Emu/Cell/PPUAnalyser.h b/rpcs3/Emu/Cell/PPUAnalyser.h index 9d6f4ef9ed..0b225bc821 100644 --- a/rpcs3/Emu/Cell/PPUAnalyser.h +++ b/rpcs3/Emu/Cell/PPUAnalyser.h @@ -96,6 +96,7 @@ struct ppu_module : public Type std::vector segs{}; std::vector secs{}; std::vector funcs{}; + std::vector applied_patches; std::deque> allocations; std::map addr_to_seg_index; @@ -185,7 +186,6 @@ struct main_ppu_module : public ppu_module { u32 elf_entry{}; u32 seg0_code_end{}; - std::vector applied_patches; // Disable inherited savestate ordering void save(utils::serial&) = delete; diff --git a/rpcs3/Emu/Cell/PPUModule.cpp b/rpcs3/Emu/Cell/PPUModule.cpp index d26f060b7d..48b9837ca7 100644 --- a/rpcs3/Emu/Cell/PPUModule.cpp +++ b/rpcs3/Emu/Cell/PPUModule.cpp @@ -1947,6 +1947,7 @@ shared_ptr ppu_load_prx(const ppu_prx_object& elf, bool virtual_load, c ppu_check_patch_spu_images(*prx, seg); } + prx->applied_patches = applied; prx->analyse(toc, 0, end, applied, exported_funcs); if (!ar && !virtual_load) diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 8001b95ac4..0f7b4bcabc 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -4898,6 +4898,25 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_s sha1_update(&ctx, ensure(info.get_ptr(func.addr)), func.size); } + if (!workload.empty() && fpos >= info.funcs.size()) + { + // Hash the entire function grouped addresses for the integrity of the symbol resolver function + // Potentially occuring during patches + // Avoid doing it for files with a single module such as most PRX + + std::vector> addrs(info.funcs.size() + 1); + usz addr_index = 0; + + for (const ppu_function& func : info.funcs) + { + addrs[addr_index++] = func.addr - reloc; + } + + addrs.back() = ::size32(info.funcs); + + sha1_update(&ctx, reinterpret_cast(addrs.data()), addrs.size() * sizeof(be_t)); + } + if (false) { const be_t forced_upd = 3; diff --git a/rpcs3/Emu/Cell/lv2/sys_overlay.h b/rpcs3/Emu/Cell/lv2/sys_overlay.h index ef1c1ffbd7..1c950a4d0c 100644 --- a/rpcs3/Emu/Cell/lv2/sys_overlay.h +++ b/rpcs3/Emu/Cell/lv2/sys_overlay.h @@ -11,7 +11,6 @@ struct lv2_overlay final : ppu_module u32 entry{}; u32 seg0_code_end{}; - std::vector applied_patches; lv2_overlay() = default; lv2_overlay(utils::serial&){} From ec77f2ab92d8afca640fb140479fe14373355b16 Mon Sep 17 00:00:00 2001 From: Antonino Di Guardo <64427768+digant73@users.noreply.github.com> Date: Tue, 7 Jan 2025 17:46:13 +0100 Subject: [PATCH 28/60] Minor cleanup (#16519) * Minor cleanup --- rpcs3/Emu/system_config.h | 2 +- rpcs3/rpcs3qt/emu_settings_type.h | 4 ++-- rpcs3/rpcs3qt/game_list_frame.cpp | 6 +++--- rpcs3/rpcs3qt/localized_emu.h | 4 ++-- rpcs3/rpcs3qt/settings_dialog.cpp | 6 +++--- rpcs3/rpcs3qt/settings_dialog.ui | 18 +++++++++--------- rpcs3/rpcs3qt/tooltips.h | 2 +- 7 files changed, 21 insertions(+), 21 deletions(-) diff --git a/rpcs3/Emu/system_config.h b/rpcs3/Emu/system_config.h index 3cb3e39851..d3dfab4ce8 100644 --- a/rpcs3/Emu/system_config.h +++ b/rpcs3/Emu/system_config.h @@ -347,10 +347,10 @@ struct cfg_root : cfg::node cfg::_bool show_rpcn_popups{ this, "Show RPCN popups", true, true }; cfg::_bool show_shader_compilation_hint{ this, "Show shader compilation hint", true, true }; cfg::_bool show_ppu_compilation_hint{ this, "Show PPU compilation hint", true, true }; + cfg::_bool show_autosave_autoload_hint{ this, "Show autosave/autoload hint", false, true }; cfg::_bool show_pressure_intensity_toggle_hint{ this, "Show pressure intensity toggle hint", true, true }; cfg::_bool show_analog_limiter_toggle_hint{ this, "Show analog limiter toggle hint", true, true }; cfg::_bool show_mouse_and_keyboard_toggle_hint{ this, "Show mouse and keyboard toggle hint", true, true }; - cfg::_bool show_autosave_autoload_hint{ this, "Show autosave/autoload hint", false, true }; cfg::_bool use_native_interface{ this, "Use native user interface", true }; cfg::string gdb_server{ this, "GDB Server", "127.0.0.1:2345" }; cfg::_bool silence_all_logs{ this, "Silence All Logs", false, true }; diff --git a/rpcs3/rpcs3qt/emu_settings_type.h b/rpcs3/rpcs3qt/emu_settings_type.h index a038fa8c84..1bc86e307f 100644 --- a/rpcs3/rpcs3qt/emu_settings_type.h +++ b/rpcs3/rpcs3qt/emu_settings_type.h @@ -178,10 +178,10 @@ enum class emu_settings_type UseNativeInterface, ShowShaderCompilationHint, ShowPPUCompilationHint, + ShowAutosaveAutoloadHint, ShowPressureIntensityToggleHint, ShowAnalogLimiterToggleHint, ShowMouseAndKeyboardToggleHint, - ShowAutosaveAutoloadHint, WindowTitleFormat, PauseDuringHomeMenu, @@ -377,13 +377,13 @@ inline static const std::map settings_location { emu_settings_type::UseNativeInterface, { "Miscellaneous", "Use native user interface"}}, { emu_settings_type::ShowShaderCompilationHint, { "Miscellaneous", "Show shader compilation hint"}}, { emu_settings_type::ShowPPUCompilationHint, { "Miscellaneous", "Show PPU compilation hint"}}, + { emu_settings_type::ShowAutosaveAutoloadHint, { "Miscellaneous", "Show autosave/autoload hint"}}, { emu_settings_type::ShowPressureIntensityToggleHint, { "Miscellaneous", "Show pressure intensity toggle hint"}}, { emu_settings_type::ShowAnalogLimiterToggleHint, { "Miscellaneous", "Show analog limiter toggle hint"}}, { emu_settings_type::ShowMouseAndKeyboardToggleHint, { "Miscellaneous", "Show mouse and keyboard toggle hint"}}, { 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::ShowAutosaveAutoloadHint, { "Miscellaneous", "Show autosave/autoload hint" }}, // Networking { emu_settings_type::InternetStatus, { "Net", "Internet enabled"}}, diff --git a/rpcs3/rpcs3qt/game_list_frame.cpp b/rpcs3/rpcs3qt/game_list_frame.cpp index 02ca16716e..1699837ae3 100644 --- a/rpcs3/rpcs3qt/game_list_frame.cpp +++ b/rpcs3/rpcs3qt/game_list_frame.cpp @@ -1315,14 +1315,14 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) { remove_menu->addSeparator(); - QAction* remove_savestate = remove_menu->addAction(tr("&Remove Savestate")); + QAction* remove_savestate = remove_menu->addAction(tr("&Remove Savestates")); remove_savestate->setEnabled(!is_current_running_game); connect(remove_savestate, &QAction::triggered, [this, current_game, savestate_dir]() { if (is_game_running(current_game.serial)) return; - if (QMessageBox::question(this, tr("Confirm Removal"), tr("Remove savestate?")) != QMessageBox::Yes) + if (QMessageBox::question(this, tr("Confirm Removal"), tr("Remove savestates?")) != QMessageBox::Yes) return; RemoveContentPath(savestate_dir, "savestate"); @@ -1768,7 +1768,7 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) QCheckBox* disc = new QCheckBox(tr("Remove title from game list (Disc Game path is not removed!)")); QCheckBox* caches = new QCheckBox(tr("Remove caches and custom configs")); QCheckBox* icons = new QCheckBox(tr("Remove icons and shortcuts")); - QCheckBox* savestate = new QCheckBox(tr("Remove savestate")); + QCheckBox* savestate = new QCheckBox(tr("Remove savestates")); QCheckBox* captures = new QCheckBox(tr("Remove captures")); QCheckBox* recordings = new QCheckBox(tr("Remove recordings")); QCheckBox* screenshots = new QCheckBox(tr("Remove screenshots")); diff --git a/rpcs3/rpcs3qt/localized_emu.h b/rpcs3/rpcs3qt/localized_emu.h index 7e8ae3f803..13541ce65a 100644 --- a/rpcs3/rpcs3qt/localized_emu.h +++ b/rpcs3/rpcs3qt/localized_emu.h @@ -165,8 +165,8 @@ private: case localized_string_id::CELL_SAVEDATA_DELETE: return tr("Delete this data?\n\n%0", "Savedata entry info").arg(std::forward(args)...); case localized_string_id::CELL_SAVEDATA_LOAD: return tr("Load this data?\n\n%0", "Savedata entry info").arg(std::forward(args)...); case localized_string_id::CELL_SAVEDATA_OVERWRITE: return tr("Do you want to overwrite the saved data?\n\n%0", "Savedata entry info").arg(std::forward(args)...); - case localized_string_id::CELL_SAVEDATA_AUTOSAVE: return tr("Saving"); - case localized_string_id::CELL_SAVEDATA_AUTOLOAD: return tr("Loading"); + case localized_string_id::CELL_SAVEDATA_AUTOSAVE: return tr("Saving..."); + case localized_string_id::CELL_SAVEDATA_AUTOLOAD: return tr("Loading..."); case localized_string_id::CELL_CROSS_CONTROLLER_MSG: return tr("Start [%0] on the PS Vita system.\nIf you have not installed [%0], go to [Remote Play] on the PS Vita system and start [Cross-Controller] from the LiveArea™ screen.", "Cross-Controller message").arg(std::forward(args)...); case localized_string_id::CELL_CROSS_CONTROLLER_FW_MSG: return tr("If your system software version on the PS Vita system is earlier than 1.80, you must update the system software to the latest version.", "Cross-Controller firmware message"); case localized_string_id::CELL_NP_RECVMESSAGE_DIALOG_TITLE: return tr("Select Message", "RECVMESSAGE_DIALOG"); diff --git a/rpcs3/rpcs3qt/settings_dialog.cpp b/rpcs3/rpcs3qt/settings_dialog.cpp index 399e4b72df..12c62c4dbf 100644 --- a/rpcs3/rpcs3qt/settings_dialog.cpp +++ b/rpcs3/rpcs3qt/settings_dialog.cpp @@ -1847,6 +1847,9 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std m_emu_settings->EnhanceCheckBox(ui->showPPUCompilationHint, emu_settings_type::ShowPPUCompilationHint); SubscribeTooltip(ui->showPPUCompilationHint, tooltips.settings.show_ppu_compilation_hint); + m_emu_settings->EnhanceCheckBox(ui->showAutosaveAutoloadHint, emu_settings_type::ShowAutosaveAutoloadHint); + SubscribeTooltip(ui->showAutosaveAutoloadHint, tooltips.settings.show_autosave_autoload_hint); + m_emu_settings->EnhanceCheckBox(ui->showPressureIntensityToggleHint, emu_settings_type::ShowPressureIntensityToggleHint); SubscribeTooltip(ui->showPressureIntensityToggleHint, tooltips.settings.show_pressure_intensity_toggle_hint); @@ -1856,9 +1859,6 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std m_emu_settings->EnhanceCheckBox(ui->showMouseAndKeyboardToggleHint, emu_settings_type::ShowMouseAndKeyboardToggleHint); SubscribeTooltip(ui->showMouseAndKeyboardToggleHint, tooltips.settings.show_mouse_and_keyboard_toggle_hint); - m_emu_settings->EnhanceCheckBox(ui->showAutosaveAutoloadHint, emu_settings_type::ShowAutosaveAutoloadHint); - SubscribeTooltip(ui->showAutosaveAutoloadHint, tooltips.settings.show_autosave_autoload_hint); - m_emu_settings->EnhanceCheckBox(ui->pauseDuringHomeMenu, emu_settings_type::PauseDuringHomeMenu); SubscribeTooltip(ui->pauseDuringHomeMenu, tooltips.settings.pause_during_home_menu); diff --git a/rpcs3/rpcs3qt/settings_dialog.ui b/rpcs3/rpcs3qt/settings_dialog.ui index 7f70c304aa..5eebe763ff 100644 --- a/rpcs3/rpcs3qt/settings_dialog.ui +++ b/rpcs3/rpcs3qt/settings_dialog.ui @@ -3010,6 +3010,13 @@ + + + + Show shader compilation hint + + + @@ -3018,9 +3025,9 @@ - + - Show shader compilation hint + Show autosave/autoload hint @@ -3045,13 +3052,6 @@ - - - - Show autosave/autoload hint - - - diff --git a/rpcs3/rpcs3qt/tooltips.h b/rpcs3/rpcs3qt/tooltips.h index d9576fb591..8a2e00b682 100644 --- a/rpcs3/rpcs3qt/tooltips.h +++ b/rpcs3/rpcs3qt/tooltips.h @@ -143,10 +143,10 @@ public: const QString hide_mouse_on_idle = tr("Hides the mouse cursor if no mouse movement is detected for the configured time."); const QString show_shader_compilation_hint = tr("Shows 'Compiling shaders' hint using the native overlay."); const QString show_ppu_compilation_hint = tr("Shows 'Compiling PPU modules' hint using the native overlay."); + const QString show_autosave_autoload_hint = tr("Shows autosave/autoload hint using the native overlay."); const QString show_pressure_intensity_toggle_hint = tr("Shows pressure intensity toggle hint using the native overlay."); const QString show_analog_limiter_toggle_hint = tr("Shows analog limiter toggle hint using the native overlay."); const QString show_mouse_and_keyboard_toggle_hint = tr("Shows mouse and keyboard toggle hint using the native overlay."); - const QString show_autosave_autoload_hint = tr("Shows autosave/autoload hint using the native overlay."); const QString use_native_interface = tr("Enables use of native HUD within the game window that can interact with game controllers.\nWhen disabled, regular Qt dialogs are used instead.\nCurrently, the on-screen keyboard only supports the English key layout."); const QString pause_during_home_menu = tr("When enabled, opening the home menu will also pause emulation.\nWhile most games pause themselves while the home menu is shown, some do not.\nIn that case it can be helpful to pause the emulation whenever the home menu is open."); From c6f3737c2aee98566adb30138975158c002d761e Mon Sep 17 00:00:00 2001 From: Elad <18193363+elad335@users.noreply.github.com> Date: Tue, 7 Jan 2025 19:39:37 +0200 Subject: [PATCH 29/60] PPU LLVM: Fixup patches enlisting in analyzer --- Utilities/bin_patch.cpp | 2 +- rpcs3/Emu/Cell/PPUThread.cpp | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/Utilities/bin_patch.cpp b/Utilities/bin_patch.cpp index 7e4c0709cd..933e148e2e 100644 --- a/Utilities/bin_patch.cpp +++ b/Utilities/bin_patch.cpp @@ -1449,7 +1449,7 @@ static usz apply_modification(std::vector& applied, patch_engine::patch_inf void patch_engine::apply(std::vector& applied_total, const std::string& name, std::function mem_translate, u32 filesz, u32 min_addr) { - applied_total.clear(); + // applied_total may be non-empty, do not clear it if (!m_map.contains(name)) { diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 0f7b4bcabc..8da9cca8f0 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -4904,15 +4904,20 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_s // Potentially occuring during patches // Avoid doing it for files with a single module such as most PRX - std::vector> addrs(info.funcs.size() + 1); - usz addr_index = 0; + std::vector> addrs; for (const ppu_function& func : info.funcs) { - addrs[addr_index++] = func.addr - reloc; + if (func.size == 0) + { + continue; + } + + addrs.emplace_back(func.addr - reloc); } - addrs.back() = ::size32(info.funcs); + // Hash its size too + addrs.emplace_back(::size32(addrs)); sha1_update(&ctx, reinterpret_cast(addrs.data()), addrs.size() * sizeof(be_t)); } From 92d07072915b99917892dd7833c06eb44a09e234 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Tue, 7 Jan 2025 18:21:10 +0100 Subject: [PATCH 30/60] Decrease mouse move assignment distance --- rpcs3/rpcs3qt/pad_settings_dialog.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/rpcs3/rpcs3qt/pad_settings_dialog.cpp b/rpcs3/rpcs3qt/pad_settings_dialog.cpp index 1fbd63aad5..c4e04cdc65 100644 --- a/rpcs3/rpcs3qt/pad_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/pad_settings_dialog.cpp @@ -996,25 +996,26 @@ void pad_settings_dialog::mouseMoveEvent(QMouseEvent* event) } else { + constexpr int delta_threshold = 20; const QPoint mouse_pos = QCursor::pos(); const int delta_x = mouse_pos.x() - m_last_pos.x(); const int delta_y = mouse_pos.y() - m_last_pos.y(); u32 key = 0; - if (delta_x > 100) + if (delta_x > delta_threshold) { key = mouse::move_right; } - else if (delta_x < -100) + else if (delta_x < -delta_threshold) { key = mouse::move_left; } - else if (delta_y > 100) + else if (delta_y > delta_threshold) { key = mouse::move_down; } - else if (delta_y < -100) + else if (delta_y < -delta_threshold) { key = mouse::move_up; } From 415c2d0795d4b7e0d4633c0db927ba88aaa9249b Mon Sep 17 00:00:00 2001 From: Megamouse Date: Wed, 8 Jan 2025 01:22:50 +0100 Subject: [PATCH 31/60] fake move: add gyro support --- rpcs3/Emu/Cell/Modules/cellGem.cpp | 19 ++- rpcs3/Emu/Io/Null/NullPadHandler.h | 1 + rpcs3/Emu/Io/PadHandler.cpp | 160 +++++++++++++++++++++++++ rpcs3/Emu/Io/PadHandler.h | 35 ++++++ rpcs3/Emu/Io/pad_config.h | 3 + rpcs3/Emu/Io/pad_types.cpp | 24 ++++ rpcs3/Emu/Io/pad_types.h | 12 +- rpcs3/Input/ds3_pad_handler.cpp | 5 + rpcs3/Input/ds4_pad_handler.cpp | 36 +++--- rpcs3/Input/dualsense_pad_handler.cpp | 33 ++--- rpcs3/Input/evdev_joystick_handler.cpp | 10 ++ rpcs3/Input/ps_move_handler.cpp | 159 ++++++++---------------- rpcs3/Input/ps_move_handler.h | 6 - rpcs3/Input/sdl_pad_handler.cpp | 28 +++-- rpcs3/Input/sdl_pad_handler.h | 8 +- rpcs3/Input/skateboard_pad_handler.cpp | 5 + rpcs3/Input/xinput_pad_handler.cpp | 6 + rpcs3/rpcs3qt/pad_settings_dialog.cpp | 21 +++- rpcs3/rpcs3qt/pad_settings_dialog.h | 2 + rpcs3/rpcs3qt/pad_settings_dialog.ui | 71 +++++++++-- rpcs3/rpcs3qt/tooltips.h | 1 + 21 files changed, 458 insertions(+), 187 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellGem.cpp b/rpcs3/Emu/Cell/Modules/cellGem.cpp index f7d274c4d0..292036197f 100644 --- a/rpcs3/Emu/Cell/Modules/cellGem.cpp +++ b/rpcs3/Emu/Cell/Modules/cellGem.cpp @@ -1142,7 +1142,7 @@ static inline void pos_to_gem_state(u32 gem_num, gem_config::gem_controller& con gem_state->handle_pos[3] = 0.f; // Calculate orientation - if (g_cfg.io.move == move_handler::real) + if (g_cfg.io.move == move_handler::real || (g_cfg.io.move == move_handler::fake && move_data.orientation_enabled)) { gem_state->quat[0] = move_data.quaternion[0]; // x gem_state->quat[1] = move_data.quaternion[1]; // y @@ -1151,14 +1151,11 @@ static inline void pos_to_gem_state(u32 gem_num, gem_config::gem_controller& con } else { - static constexpr f32 PI = 3.14159265f; - const auto degree_to_rad = [](f32 degree) -> f32 { return degree * PI / 180.0f; }; - const f32 max_angle_per_side_h = g_cfg.io.fake_move_rotation_cone_h / 2.0f; const f32 max_angle_per_side_v = g_cfg.io.fake_move_rotation_cone_v / 2.0f; - const f32 roll = -degree_to_rad((image_y - half_height) / half_height * max_angle_per_side_v); // This is actually the pitch - const f32 pitch = -degree_to_rad((image_x - half_width) / half_width * max_angle_per_side_h); // This is actually the yaw - const f32 yaw = degree_to_rad(0.0f); + const f32 roll = -PadHandlerBase::degree_to_rad((image_y - half_height) / half_height * max_angle_per_side_v); // This is actually the pitch + const f32 pitch = -PadHandlerBase::degree_to_rad((image_x - half_width) / half_width * max_angle_per_side_h); // This is actually the yaw + const f32 yaw = PadHandlerBase::degree_to_rad(0.0f); const f32 cr = std::cos(roll * 0.5f); const f32 sr = std::sin(roll * 0.5f); const f32 cp = std::cos(pitch * 0.5f); @@ -1318,7 +1315,7 @@ static void ds3_pos_to_gem_state(u32 gem_num, gem_config::gem_controller& contro if constexpr (std::is_same_v>) { - pos_to_gem_state(gem_num, controller, gem_state, ds3_pos_x, ds3_pos_y, ds3_max_x, ds3_max_y, {}); + pos_to_gem_state(gem_num, controller, gem_state, ds3_pos_x, ds3_pos_y, ds3_max_x, ds3_max_y, pad->move_data); } else if constexpr (std::is_same_v>) { @@ -2147,6 +2144,7 @@ error_code cellGemGetInertialState(u32 gem_num, u32 state_flag, u64 timestamp, v switch (g_cfg.io.move) { case move_handler::real: + case move_handler::fake: { // Get temperature and sensor data { @@ -2155,7 +2153,7 @@ error_code cellGemGetInertialState(u32 gem_num, u32 state_flag, u64 timestamp, v const auto handler = pad::get_current_handler(); const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num)); - if (pad && pad->m_pad_handler == pad_handler::move && (pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (pad && (pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) { inertial_state->temperature = pad->move_data.temperature; inertial_state->accelerometer[0] = pad->move_data.accelerometer_x; @@ -2170,9 +2168,6 @@ error_code cellGemGetInertialState(u32 gem_num, u32 state_flag, u64 timestamp, v ds3_input_to_pad(gem_num, inertial_state->pad.digitalbuttons, inertial_state->pad.analog_T); break; } - case move_handler::fake: - ds3_input_to_pad(gem_num, inertial_state->pad.digitalbuttons, inertial_state->pad.analog_T); - break; case move_handler::mouse: case move_handler::raw_mouse: mouse_input_to_pad(gem_num, inertial_state->pad.digitalbuttons, inertial_state->pad.analog_T); diff --git a/rpcs3/Emu/Io/Null/NullPadHandler.h b/rpcs3/Emu/Io/Null/NullPadHandler.h index 63b14a5c34..57e9767ccd 100644 --- a/rpcs3/Emu/Io/Null/NullPadHandler.h +++ b/rpcs3/Emu/Io/Null/NullPadHandler.h @@ -48,6 +48,7 @@ public: cfg->pressure_intensity_button.def = ""; cfg->analog_limiter_button.def = ""; + cfg->orientation_reset_button.def = ""; // Apply defaults cfg->from_default(); diff --git a/rpcs3/Emu/Io/PadHandler.cpp b/rpcs3/Emu/Io/PadHandler.cpp index a1aeeb7885..4dc9bcb546 100644 --- a/rpcs3/Emu/Io/PadHandler.cpp +++ b/rpcs3/Emu/Io/PadHandler.cpp @@ -2,6 +2,7 @@ #include "PadHandler.h" #include "Emu/system_utils.hpp" #include "Emu/system_config.h" +#include "Emu/Cell/timers.hpp" #include "Input/pad_thread.h" #include "Input/product_info.h" @@ -494,6 +495,12 @@ bool PadHandlerBase::bindPadToDevice(std::shared_ptr pad) pad->m_analog_limiter_button_index = static_cast(pad->m_buttons.size()) - 1; } + if (b_has_orientation) + { + pad->m_buttons.emplace_back(special_button_offset, mapping[button::orientation_reset_button], special_button_value::orientation_reset); + pad->m_orientation_reset_button_index = static_cast(pad->m_buttons.size()) - 1; + } + pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, mapping[button::up], CELL_PAD_CTRL_UP); pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, mapping[button::down], CELL_PAD_CTRL_DOWN); pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, mapping[button::left], CELL_PAD_CTRL_LEFT); @@ -600,6 +607,11 @@ std::array, PadHandlerBase::button::button_count> PadHandlerBase:: mapping[button::analog_limiter_button] = FindKeyCodes(button_list, cfg->analog_limiter_button); } + if (b_has_orientation) + { + mapping[button::orientation_reset_button] = FindKeyCodes(button_list, cfg->orientation_reset_button); + } + return mapping; } @@ -739,6 +751,8 @@ void PadHandlerBase::process() if (!device || !pad) continue; + pad->move_data.orientation_enabled = b_has_orientation && device->config && device->config->orientation_enabled.get(); + const connection status = update_connection(device); switch (status) @@ -754,6 +768,11 @@ void PadHandlerBase::process() last_connection_status[i] = true; connected_devices++; + + if (b_has_orientation) + { + device->reset_orientation(); + } } if (status == connection::no_data) @@ -790,6 +809,11 @@ void PadHandlerBase::process() last_connection_status[i] = false; connected_devices--; + + if (b_has_orientation) + { + device->reset_orientation(); + } } continue; } @@ -797,6 +821,142 @@ void PadHandlerBase::process() get_mapping(m_bindings[i]); get_extended_info(m_bindings[i]); + get_orientation(m_bindings[i]); apply_pad_data(m_bindings[i]); } } + +void PadHandlerBase::set_raw_orientation(ps_move_data& move_data, f32 accel_x, f32 accel_y, f32 accel_z, f32 gyro_x, f32 gyro_y, f32 gyro_z) +{ + if (!move_data.orientation_enabled) + { + move_data.reset_sensors(); + return; + } + + // This function expects DS3 sensor accel values in linear velocity (m/s²) and gyro values in angular velocity (degree/s) + // The default position is flat on the ground, pointing forward. + // The accelerometers constantly measure G forces. + // The gyros measure changes in orientation and will reset when the device isn't moved anymore. + move_data.accelerometer_x = -accel_x; // move_data: Increases if the device is rolled to the left + move_data.accelerometer_y = accel_z; // move_data: Increases if the device is pitched upwards + move_data.accelerometer_z = accel_y; // move_data: Increases if the device is moved upwards + move_data.gyro_x = degree_to_rad(-gyro_x); // move_data: Increases if the device is pitched upwards + move_data.gyro_y = degree_to_rad(gyro_z); // move_data: Increases if the device is rolled to the right + move_data.gyro_z = degree_to_rad(-gyro_y); // move_data: Increases if the device is yawed to the left +} + +void PadHandlerBase::set_raw_orientation(Pad& pad) +{ + if (!pad.move_data.orientation_enabled) + { + pad.move_data.reset_sensors(); + return; + } + + // acceleration (linear velocity in m/s²) + const f32 accel_x = (pad.m_sensors[0].m_value - 512) / static_cast(MOTION_ONE_G); + const f32 accel_y = (pad.m_sensors[1].m_value - 512) / static_cast(MOTION_ONE_G); + const f32 accel_z = (pad.m_sensors[2].m_value - 512) / static_cast(MOTION_ONE_G); + + // gyro (angular velocity in degree/s) + constexpr f32 gyro_x = 0.0f; + const f32 gyro_y = (pad.m_sensors[3].m_value - 512) / (123.f / 90.f); + constexpr f32 gyro_z = 0.0f; + + set_raw_orientation(pad.move_data, accel_x, accel_y, accel_z, gyro_x, gyro_y, gyro_z); +} + +void PadHandlerBase::get_orientation(const pad_ensemble& binding) const +{ + if (!b_has_orientation) return; + + const auto& pad = binding.pad; + const auto& device = binding.device; + if (!pad || !device) return; + + if (pad->move_data.calibration_requested) + { + device->reset_orientation(); + pad->move_data.quaternion = ps_move_data::default_quaternion; + pad->move_data.calibration_succeeded = true; + return; + } + + if (!pad->move_data.orientation_enabled || pad->get_orientation_reset_button_active(pad->m_player_id)) + { + // This can be called extensively in quick succession, so let's just reset the pointer instead of creating a new object. + device->ahrs.reset(); + pad->move_data.quaternion = ps_move_data::default_quaternion; + return; + } + + device->update_orientation(pad->move_data); +} + +void PadDevice::reset_orientation() +{ + // Initialize Fusion + ahrs = std::make_shared(); + FusionAhrsInitialise(ahrs.get()); + ahrs->settings.convention = FusionConvention::FusionConventionEnu; + ahrs->settings.gain = 0.0f; // If gain is set, the algorithm tries to adjust the orientation over time. + FusionAhrsSetSettings(ahrs.get(), &ahrs->settings); + FusionAhrsReset(ahrs.get()); +} + +void PadDevice::update_orientation(ps_move_data& move_data) +{ + if (!ahrs) + { + reset_orientation(); + } + + // Get elapsed time since last update + const u64 now_us = get_system_time(); + const float elapsed_sec = (last_ahrs_update_time_us == 0) ? 0.0f : ((now_us - last_ahrs_update_time_us) / 1'000'000.0f); + last_ahrs_update_time_us = now_us; + + // The ps move handler's axis may differ from the Fusion axis, so we have to map them correctly. + // Don't ask how the axis work. It's basically been trial and error. + ensure(ahrs->settings.convention == FusionConvention::FusionConventionEnu); // East-North-Up + + const FusionVector accelerometer{ + .axis { + .x = -move_data.accelerometer_x, + .y = +move_data.accelerometer_y, + .z = +move_data.accelerometer_z + } + }; + + const FusionVector gyroscope{ + .axis { + .x = +PadHandlerBase::rad_to_degree(move_data.gyro_x), + .y = +PadHandlerBase::rad_to_degree(move_data.gyro_z), + .z = -PadHandlerBase::rad_to_degree(move_data.gyro_y) + } + }; + + FusionVector magnetometer {}; + + if (move_data.magnetometer_enabled) + { + magnetometer = FusionVector{ + .axis { + .x = move_data.magnetometer_x, + .y = move_data.magnetometer_y, + .z = move_data.magnetometer_z + } + }; + } + + // Update Fusion + FusionAhrsUpdate(ahrs.get(), gyroscope, accelerometer, magnetometer, elapsed_sec); + + // Get quaternion + const FusionQuaternion quaternion = FusionAhrsGetQuaternion(ahrs.get()); + move_data.quaternion[0] = quaternion.array[1]; + move_data.quaternion[1] = quaternion.array[2]; + move_data.quaternion[2] = quaternion.array[3]; + move_data.quaternion[3] = quaternion.array[0]; +} diff --git a/rpcs3/Emu/Io/PadHandler.h b/rpcs3/Emu/Io/PadHandler.h index 55bc548620..eb536fde11 100644 --- a/rpcs3/Emu/Io/PadHandler.h +++ b/rpcs3/Emu/Io/PadHandler.h @@ -5,6 +5,15 @@ #include "pad_config_types.h" #include "util/types.hpp" +#ifndef _MSC_VER +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wold-style-cast" +#endif +#include "3rdparty/fusion/fusion/Fusion/Fusion.h" +#ifndef _MSC_VER +#pragma GCC diagnostic pop +#endif + #include #include #include @@ -38,6 +47,12 @@ public: }; color color_override{}; bool color_override_active{}; + + std::shared_ptr ahrs; // Used to calculate quaternions from sensor data + u64 last_ahrs_update_time_us = 0; // Last ahrs update + + void update_orientation(ps_move_data& move_data); + void reset_orientation(); }; struct pad_ensemble @@ -125,6 +140,7 @@ protected: pressure_intensity_button, analog_limiter_button, + orientation_reset_button, button_count }; @@ -153,6 +169,7 @@ protected: bool b_has_config = false; bool b_has_pressure_intensity_button = true; bool b_has_analog_limiter_button = true; + bool b_has_orientation = false; std::array m_pad_configs; std::vector m_bindings; @@ -301,6 +318,7 @@ public: bool has_battery_led() const { return b_has_battery_led; } bool has_pressure_intensity_button() const { return b_has_pressure_intensity_button; } bool has_analog_limiter_button() const { return b_has_analog_limiter_button; } + bool has_orientation() const { return b_has_orientation; } u16 NormalizeStickInput(u16 raw_value, s32 threshold, s32 multiplier, bool ignore_threshold = false) const; void convert_stick_values(u16& x_out, u16& y_out, s32 x_in, s32 y_in, u32 deadzone, u32 anti_deadzone, u32 padsquircling) const; @@ -323,6 +341,18 @@ public: virtual void get_motion_sensors(const std::string& pad_id, const motion_callback& callback, const motion_fail_callback& fail_callback, motion_preview_values preview_values, const std::array& sensors); virtual std::unordered_map get_motion_axis_list() const { return {}; } + static constexpr f32 PI = 3.14159265f; + + static f32 degree_to_rad(f32 degree) + { + return degree * PI / 180.0f; + } + + static f32 rad_to_degree(f32 radians) + { + return radians * 180.0f / PI; + }; + private: virtual std::shared_ptr get_device(const std::string& /*device*/) { return nullptr; } virtual bool get_is_left_trigger(const std::shared_ptr& /*device*/, u64 /*keyCode*/) { return false; } @@ -336,10 +366,15 @@ private: virtual std::unordered_map get_button_values(const std::shared_ptr& /*device*/) { return {}; } virtual pad_preview_values get_preview_values(const std::unordered_map& /*data*/) { return {}; } + void get_orientation(const pad_ensemble& binding) const; + protected: virtual std::array, PadHandlerBase::button::button_count> get_mapped_key_codes(const std::shared_ptr& device, const cfg_pad* cfg); virtual void get_mapping(const pad_ensemble& binding); void TranslateButtonPress(const std::shared_ptr& device, u64 keyCode, bool& pressed, u16& val, bool use_stick_multipliers, bool ignore_stick_threshold = false, bool ignore_trigger_threshold = false); void init_configs(); cfg_pad* get_config(const std::string& pad_id); + + static void set_raw_orientation(ps_move_data& move_data, f32 accel_x, f32 accel_y, f32 accel_z, f32 gyro_x, f32 gyro_y, f32 gyro_z); + static void set_raw_orientation(Pad& pad); }; diff --git a/rpcs3/Emu/Io/pad_config.h b/rpcs3/Emu/Io/pad_config.h index 662280d7b2..5a7517aa72 100644 --- a/rpcs3/Emu/Io/pad_config.h +++ b/rpcs3/Emu/Io/pad_config.h @@ -66,6 +66,9 @@ struct cfg_pad final : cfg::node cfg_sensor motion_sensor_z{ this, "Motion Sensor Z" }; cfg_sensor motion_sensor_g{ this, "Motion Sensor G" }; + cfg::string orientation_reset_button{ this, "Orientation Reset Button", "" }; + cfg::_bool orientation_enabled{ this, "Orientation Enabled", false }; + cfg::string pressure_intensity_button{ this, "Pressure Intensity Button", "" }; cfg::uint<0, 100> pressure_intensity{ this, "Pressure Intensity Percent", 50 }; cfg::_bool pressure_intensity_toggle_mode{ this, "Pressure Intensity Toggle Mode", false }; diff --git a/rpcs3/Emu/Io/pad_types.cpp b/rpcs3/Emu/Io/pad_types.cpp index 9100e28596..0e9c41bcaa 100644 --- a/rpcs3/Emu/Io/pad_types.cpp +++ b/rpcs3/Emu/Io/pad_types.cpp @@ -159,6 +159,20 @@ u32 get_axis_keycode(u32 offset, u16 value) } } +void ps_move_data::reset_sensors() +{ + quaternion = default_quaternion; + accelerometer_x = 0.0f; + accelerometer_y = 0.0f; + accelerometer_z = 0.0f; + gyro_x = 0.0f; + gyro_y = 0.0f; + gyro_z = 0.0f; + magnetometer_x = 0.0f; + magnetometer_y = 0.0f; + magnetometer_z = 0.0f; +} + bool Pad::get_pressure_intensity_button_active(bool is_toggle_mode, u32 player_id) { if (m_pressure_intensity_button_index < 0) @@ -238,3 +252,13 @@ bool Pad::get_analog_limiter_button_active(bool is_toggle_mode, u32 player_id) return analog_limiter_button.m_pressed; } + +bool Pad::get_orientation_reset_button_active(u32 player_id) +{ + if (m_orientation_reset_button_index < 0) + { + return false; + } + + return m_buttons[m_orientation_reset_button_index].m_pressed; +} diff --git a/rpcs3/Emu/Io/pad_types.h b/rpcs3/Emu/Io/pad_types.h index 1f9552a6f8..966b1a6b28 100644 --- a/rpcs3/Emu/Io/pad_types.h +++ b/rpcs3/Emu/Io/pad_types.h @@ -365,7 +365,8 @@ constexpr u32 special_button_offset = 666; // Must not conflict with other CELL enum special_button_value { pressure_intensity, - analog_limiter + analog_limiter, + orientation_reset }; struct Button @@ -470,8 +471,10 @@ struct ps_move_data bool calibration_succeeded = false; bool magnetometer_enabled = false; + bool orientation_enabled = false; - std::array quaternion { 1.0f, 0.0f, 0.0f, 0.0f }; // quaternion orientation (x,y,z,w) of controller relative to default (facing the camera with buttons up) + static constexpr std::array default_quaternion { 1.0f, 0.0f, 0.0f, 0.0f }; + std::array quaternion = default_quaternion; // quaternion orientation (x,y,z,w) of controller relative to default (facing the camera with buttons up) f32 accelerometer_x = 0.0f; // linear velocity in m/s² f32 accelerometer_y = 0.0f; // linear velocity in m/s² f32 accelerometer_z = 0.0f; // linear velocity in m/s² @@ -482,6 +485,8 @@ struct ps_move_data f32 magnetometer_y = 0.0f; f32 magnetometer_z = 0.0f; s16 temperature = 0; + + void reset_sensors(); }; struct Pad @@ -512,6 +517,9 @@ struct Pad bool m_analog_limiter_enabled_last{}; // only used in keyboard_pad_handler bool get_analog_limiter_button_active(bool is_toggle_mode, u32 player_id); + s32 m_orientation_reset_button_index{-1}; // Special button index. -1 if not set. + bool get_orientation_reset_button_active(u32 player_id); + // Cable State: 0 - 1 plugged in ? u8 m_cable_state{0}; diff --git a/rpcs3/Input/ds3_pad_handler.cpp b/rpcs3/Input/ds3_pad_handler.cpp index 41afccfad0..d5dc2ef7d8 100644 --- a/rpcs3/Input/ds3_pad_handler.cpp +++ b/rpcs3/Input/ds3_pad_handler.cpp @@ -58,6 +58,7 @@ ds3_pad_handler::ds3_pad_handler() b_has_rgb = false; b_has_player_led = true; b_has_pressure_intensity_button = false; // The DS3 obviously already has this feature natively. + b_has_orientation = true; m_name_string = "DS3 Pad #"; m_max_devices = CELL_PAD_MAX_PORT_NUM; @@ -199,6 +200,7 @@ void ds3_pad_handler::init_config(cfg_pad* cfg) cfg->pressure_intensity_button.def = ::at32(button_list, DS3KeyCodes::None); cfg->analog_limiter_button.def = ::at32(button_list, DS3KeyCodes::None); + cfg->orientation_reset_button.def = ::at32(button_list, DS3KeyCodes::None); // Set default misc variables cfg->lstick_anti_deadzone.def = 0; @@ -462,6 +464,9 @@ void ds3_pad_handler::get_extended_info(const pad_ensemble& binding) //pad->m_sensors[1].m_value = polish_value(pad->m_sensors[1].m_value, 226, 226, 512, 512, 0, 1023); //pad->m_sensors[2].m_value = polish_value(pad->m_sensors[2].m_value, 113, 113, 512, 512, 0, 1023); //pad->m_sensors[3].m_value = polish_value(pad->m_sensors[3].m_value, 1, 1, 512, 512, 0, 1023); + + // Set raw orientation + set_raw_orientation(*pad); } bool ds3_pad_handler::get_is_left_trigger(const std::shared_ptr& /*device*/, u64 keyCode) diff --git a/rpcs3/Input/ds4_pad_handler.cpp b/rpcs3/Input/ds4_pad_handler.cpp index 3cbd0fb61f..d7fc6dd9ae 100644 --- a/rpcs3/Input/ds4_pad_handler.cpp +++ b/rpcs3/Input/ds4_pad_handler.cpp @@ -118,6 +118,7 @@ ds4_pad_handler::ds4_pad_handler() b_has_rgb = true; b_has_battery = true; b_has_battery_led = true; + b_has_orientation = true; m_name_string = "DS4 Pad #"; m_max_devices = CELL_PAD_MAX_PORT_NUM; @@ -179,6 +180,7 @@ void ds4_pad_handler::init_config(cfg_pad* cfg) cfg->pressure_intensity_button.def = ::at32(button_list, DS4KeyCodes::None); cfg->analog_limiter_button.def = ::at32(button_list, DS4KeyCodes::None); + cfg->orientation_reset_button.def = ::at32(button_list, DS4KeyCodes::None); // Set default misc variables cfg->lstick_anti_deadzone.def = static_cast(0.13 * thumb_max); // 13% @@ -883,29 +885,27 @@ void ds4_pad_handler::get_extended_info(const pad_ensemble& binding) // these values come already calibrated, all we need to do is convert to ds3 range - // accel - f32 accelX = static_cast(input.accel[0]) / static_cast(DS4_ACC_RES_PER_G) * -1; - f32 accelY = static_cast(input.accel[1]) / static_cast(DS4_ACC_RES_PER_G) * -1; - f32 accelZ = static_cast(input.accel[2]) / static_cast(DS4_ACC_RES_PER_G) * -1; + // acceleration (linear velocity in m/s²) + const f32 accel_x = static_cast(input.accel[0]) / static_cast(DS4_ACC_RES_PER_G) * -1; + const f32 accel_y = static_cast(input.accel[1]) / static_cast(DS4_ACC_RES_PER_G) * -1; + const f32 accel_z = static_cast(input.accel[2]) / static_cast(DS4_ACC_RES_PER_G) * -1; + + // gyro (angular velocity in degree/s) + const f32 gyro_x = static_cast(input.gyro[0]) / static_cast(DS4_GYRO_RES_PER_DEG_S) * -1; + const f32 gyro_y = static_cast(input.gyro[1]) / static_cast(DS4_GYRO_RES_PER_DEG_S) * -1; + const f32 gyro_z = static_cast(input.gyro[2]) / static_cast(DS4_GYRO_RES_PER_DEG_S) * -1; // now just use formula from ds3 - accelX = accelX * 113 + 512; - accelY = accelY * 113 + 512; - accelZ = accelZ * 113 + 512; - - pad->m_sensors[0].m_value = Clamp0To1023(accelX); - pad->m_sensors[1].m_value = Clamp0To1023(accelY); - pad->m_sensors[2].m_value = Clamp0To1023(accelZ); - - // gyroY is yaw, which is all that we need - //f32 gyroX = static_cast(input.gyro[0]) / static_cast(DS4_GYRO_RES_PER_DEG_S) * -1; - f32 gyroY = static_cast(input.gyro[1]) / static_cast(DS4_GYRO_RES_PER_DEG_S) * -1; - //f32 gyroZ = static_cast(input.gyro[2]) / static_cast(DS4_GYRO_RES_PER_DEG_S) * -1; + pad->m_sensors[0].m_value = Clamp0To1023(accel_x * MOTION_ONE_G + 512); + pad->m_sensors[1].m_value = Clamp0To1023(accel_y * MOTION_ONE_G + 512); + pad->m_sensors[2].m_value = Clamp0To1023(accel_z * MOTION_ONE_G + 512); + // gyro_y is yaw, which is all that we need. // Convert to ds3. The ds3 resolution is 123/90°/sec. - gyroY = gyroY * (123.f / 90.f) + 512; + pad->m_sensors[3].m_value = Clamp0To1023(gyro_y * (123.f / 90.f) + 512); - pad->m_sensors[3].m_value = Clamp0To1023(gyroY); + // Set raw orientation + set_raw_orientation(pad->move_data, accel_x, accel_y, accel_z, gyro_x, gyro_y, gyro_z); } void ds4_pad_handler::apply_pad_data(const pad_ensemble& binding) diff --git a/rpcs3/Input/dualsense_pad_handler.cpp b/rpcs3/Input/dualsense_pad_handler.cpp index b9dbcd6d29..e7b34b45bf 100644 --- a/rpcs3/Input/dualsense_pad_handler.cpp +++ b/rpcs3/Input/dualsense_pad_handler.cpp @@ -90,6 +90,7 @@ dualsense_pad_handler::dualsense_pad_handler() b_has_player_led = true; b_has_battery = true; b_has_battery_led = true; + b_has_orientation = true; m_name_string = "DualSense Pad #"; m_max_devices = CELL_PAD_MAX_PORT_NUM; @@ -252,6 +253,7 @@ void dualsense_pad_handler::init_config(cfg_pad* cfg) cfg->pressure_intensity_button.def = ::at32(button_list, DualSenseKeyCodes::None); cfg->analog_limiter_button.def = ::at32(button_list, DualSenseKeyCodes::None); + cfg->orientation_reset_button.def = ::at32(button_list, DualSenseKeyCodes::None); // Set default misc variables cfg->lstick_anti_deadzone.def = static_cast(0.13 * thumb_max); // 13% @@ -614,28 +616,27 @@ void dualsense_pad_handler::get_extended_info(const pad_ensemble& binding) // these values come already calibrated, all we need to do is convert to ds3 range - // gyroY is yaw, which is all that we need - //f32 gyroX = static_cast(input.gyro[0]) / static_cast(DUALSENSE_GYRO_RES_PER_DEG_S) * -1.f; - f32 gyroY = static_cast(input.gyro[1]) / static_cast(DUALSENSE_GYRO_RES_PER_DEG_S) * -1.f; - //f32 gyroZ = static_cast(input.gyro[2]) / static_cast(DUALSENSE_GYRO_RES_PER_DEG_S) * -1.f; + // gyro (angular velocity in degree/s) + const f32 gyro_x = static_cast(input.gyro[0]) / static_cast(DUALSENSE_GYRO_RES_PER_DEG_S) * -1.f; + const f32 gyro_y = static_cast(input.gyro[1]) / static_cast(DUALSENSE_GYRO_RES_PER_DEG_S) * -1.f; + const f32 gyro_z = static_cast(input.gyro[2]) / static_cast(DUALSENSE_GYRO_RES_PER_DEG_S) * -1.f; - // accel - f32 accelX = static_cast(input.accel[0]) / static_cast(DUALSENSE_ACC_RES_PER_G) * -1; - f32 accelY = static_cast(input.accel[1]) / static_cast(DUALSENSE_ACC_RES_PER_G) * -1; - f32 accelZ = static_cast(input.accel[2]) / static_cast(DUALSENSE_ACC_RES_PER_G) * -1; + // acceleration (linear velocity in m/s²) + const f32 accel_x = static_cast(input.accel[0]) / static_cast(DUALSENSE_ACC_RES_PER_G) * -1; + const f32 accel_y = static_cast(input.accel[1]) / static_cast(DUALSENSE_ACC_RES_PER_G) * -1; + const f32 accel_z = static_cast(input.accel[2]) / static_cast(DUALSENSE_ACC_RES_PER_G) * -1; // now just use formula from ds3 - accelX = accelX * 113 + 512; - accelY = accelY * 113 + 512; - accelZ = accelZ * 113 + 512; + pad->m_sensors[0].m_value = Clamp0To1023(accel_x * MOTION_ONE_G + 512); + pad->m_sensors[1].m_value = Clamp0To1023(accel_y * MOTION_ONE_G + 512); + pad->m_sensors[2].m_value = Clamp0To1023(accel_z * MOTION_ONE_G + 512); + // gyro_y is yaw, which is all that we need // Convert to ds3. The ds3 resolution is 123/90°/sec. - gyroY = gyroY * (123.f / 90.f) + 512; + pad->m_sensors[3].m_value = Clamp0To1023(gyro_y * (123.f / 90.f) + 512); - pad->m_sensors[0].m_value = Clamp0To1023(accelX); - pad->m_sensors[1].m_value = Clamp0To1023(accelY); - pad->m_sensors[2].m_value = Clamp0To1023(accelZ); - pad->m_sensors[3].m_value = Clamp0To1023(gyroY); + // Set raw orientation + set_raw_orientation(pad->move_data, accel_x, accel_y, accel_z, gyro_x, gyro_y, gyro_z); } std::unordered_map dualsense_pad_handler::get_button_values(const std::shared_ptr& device) diff --git a/rpcs3/Input/evdev_joystick_handler.cpp b/rpcs3/Input/evdev_joystick_handler.cpp index 55ab002859..09872203d6 100644 --- a/rpcs3/Input/evdev_joystick_handler.cpp +++ b/rpcs3/Input/evdev_joystick_handler.cpp @@ -34,6 +34,7 @@ evdev_joystick_handler::evdev_joystick_handler() b_has_rumble = true; b_has_motion = true; b_has_deadzones = true; + b_has_orientation = true; m_trigger_threshold = trigger_max / 2; m_thumb_threshold = thumb_max / 2; @@ -84,6 +85,7 @@ void evdev_joystick_handler::init_config(cfg_pad* cfg) cfg->pressure_intensity_button.def = ::at32(button_list, NO_BUTTON); cfg->analog_limiter_button.def = ::at32(button_list, NO_BUTTON); + cfg->orientation_reset_button.def = ::at32(button_list, NO_BUTTON); // Set default misc variables cfg->lstick_anti_deadzone.def = static_cast(0.13 * thumb_max); // 13% @@ -1075,6 +1077,8 @@ void evdev_joystick_handler::get_extended_info(const pad_ensemble& binding) } } + set_raw_orientation(*pad); + if (ret < 0) { // -EAGAIN signifies no available events, not an actual *error*. @@ -1382,6 +1386,12 @@ bool evdev_joystick_handler::bindPadToDevice(std::shared_ptr pad) pad->m_analog_limiter_button_index = static_cast(pad->m_buttons.size()) - 1; } + if (b_has_orientation) + { + pad->m_buttons.emplace_back(special_button_offset, find_buttons(cfg->orientation_reset_button), special_button_value::orientation_reset); + pad->m_orientation_reset_button_index = static_cast(pad->m_buttons.size()) - 1; + } + pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2,find_buttons(cfg->triangle), CELL_PAD_CTRL_TRIANGLE); pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2,find_buttons(cfg->circle), CELL_PAD_CTRL_CIRCLE); pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2,find_buttons(cfg->cross), CELL_PAD_CTRL_CROSS); diff --git a/rpcs3/Input/ps_move_handler.cpp b/rpcs3/Input/ps_move_handler.cpp index 83049f5fd0..d405775764 100644 --- a/rpcs3/Input/ps_move_handler.cpp +++ b/rpcs3/Input/ps_move_handler.cpp @@ -127,6 +127,7 @@ ps_move_handler::ps_move_handler() b_has_battery = true; b_has_battery_led = false; b_has_pressure_intensity_button = false; + b_has_orientation = true; m_name_string = "PS Move #"; m_max_devices = 4; // CELL_GEM_MAX_NUM @@ -170,6 +171,8 @@ void ps_move_handler::init_config(cfg_pad* cfg) cfg->l2.def = ::at32(button_list, ps_move_key_codes::none); cfg->l3.def = ::at32(button_list, ps_move_key_codes::none); + cfg->orientation_reset_button.def = ::at32(button_list, ps_move_key_codes::none); + // Set default misc variables cfg->lstickdeadzone.def = 40; // between 0 and 255 cfg->rstickdeadzone.def = 40; // between 0 and 255 @@ -359,8 +362,6 @@ void ps_move_handler::check_add_device(hid_device* hidDevice, std::string_view p psmove_parse_calibration(calibration, *device); } - device->reset_orientation(); - // Activate if (send_output_report(device) == -1) { @@ -669,12 +670,12 @@ void ps_move_handler::get_extended_info(const pad_ensemble& binding) // The default position is flat on the ground, pointing forward. // The accelerometers constantly measure G forces. // The gyros measure changes in orientation and will reset when the device isn't moved anymore. - s16 accel_x = input.accel_x_1; // Increases if the device is rolled to the left - s16 accel_y = input.accel_y_1; // Increases if the device is pitched upwards - s16 accel_z = input.accel_z_1; // Increases if the device is moved upwards - s16 gyro_x = input.gyro_x_1; // Increases if the device is pitched upwards - s16 gyro_y = input.gyro_y_1; // Increases if the device is rolled to the right - s16 gyro_z = input.gyro_z_1; // Increases if the device is yawed to the left + f32 accel_x = input.accel_x_1; // Increases if the device is rolled to the left + f32 accel_y = input.accel_y_1; // Increases if the device is pitched upwards + f32 accel_z = input.accel_z_1; // Increases if the device is moved upwards + f32 gyro_x = input.gyro_x_1; // Increases if the device is pitched upwards + f32 gyro_y = input.gyro_y_1; // Increases if the device is rolled to the right + f32 gyro_z = input.gyro_z_1; // Increases if the device is yawed to the left if (dev->model == ps_move_model::ZCM1) { @@ -684,45 +685,60 @@ void ps_move_handler::get_extended_info(const pad_ensemble& binding) gyro_x -= zero_shift; gyro_y -= zero_shift; gyro_z -= zero_shift; - - const ps_move_input_report_ZCM1& input_zcm1 = dev->input_report_ZCM1; - - #define TWELVE_BIT_SIGNED(x) (((x) & 0x800) ? (-(((~(x)) & 0xFFF) + 1)) : (x)) - pad->move_data.magnetometer_x = static_cast(TWELVE_BIT_SIGNED(((input.magnetometer_x & 0x0F) << 8) | input_zcm1.magnetometer_x2)); - pad->move_data.magnetometer_y = static_cast(TWELVE_BIT_SIGNED((input_zcm1.magnetometer_y << 4) | (input_zcm1.magnetometer_yz & 0xF0) >> 4)); - pad->move_data.magnetometer_z = static_cast(TWELVE_BIT_SIGNED(((input_zcm1.magnetometer_yz & 0x0F) << 8) | input_zcm1.magnetometer_z)); } - // Apply calibration - if (dev->calibration.is_valid) + if (!device->config || !device->config->orientation_enabled) { - pad->move_data.accelerometer_x = accel_x * dev->calibration.accel_x_factor + dev->calibration.accel_x_offset; - pad->move_data.accelerometer_y = accel_y * dev->calibration.accel_y_factor + dev->calibration.accel_y_offset; - pad->move_data.accelerometer_z = accel_z * dev->calibration.accel_z_factor + dev->calibration.accel_z_offset; - pad->move_data.gyro_x = (gyro_x - dev->calibration.gyro_x_offset) * dev->calibration.gyro_x_gain; - pad->move_data.gyro_y = (gyro_y - dev->calibration.gyro_y_offset) * dev->calibration.gyro_y_gain; - pad->move_data.gyro_z = (gyro_z - dev->calibration.gyro_z_offset) * dev->calibration.gyro_z_gain; + pad->move_data.reset_sensors(); } else { - constexpr f32 MOVE_ONE_G = 4096.0f; // This is just a rough estimate and probably depends on the device + // Apply calibration + if (dev->calibration.is_valid) + { + accel_x = accel_x * dev->calibration.accel_x_factor + dev->calibration.accel_x_offset; + accel_y = accel_y * dev->calibration.accel_y_factor + dev->calibration.accel_y_offset; + accel_z = accel_z * dev->calibration.accel_z_factor + dev->calibration.accel_z_offset; + gyro_x = (gyro_x - dev->calibration.gyro_x_offset) * dev->calibration.gyro_x_gain; + gyro_y = (gyro_y - dev->calibration.gyro_y_offset) * dev->calibration.gyro_y_gain; + gyro_z = (gyro_z - dev->calibration.gyro_z_offset) * dev->calibration.gyro_z_gain; + } + else + { + constexpr f32 MOVE_ONE_G = 4096.0f; // This is just a rough estimate and probably depends on the device - pad->move_data.accelerometer_x = accel_x / MOVE_ONE_G; - pad->move_data.accelerometer_y = accel_y / MOVE_ONE_G; - pad->move_data.accelerometer_z = accel_z / MOVE_ONE_G; - pad->move_data.gyro_x = gyro_x / MOVE_ONE_G; - pad->move_data.gyro_y = gyro_y / MOVE_ONE_G; - pad->move_data.gyro_z = gyro_z / MOVE_ONE_G; + accel_x /= MOVE_ONE_G; + accel_y /= MOVE_ONE_G; + accel_z /= MOVE_ONE_G; + gyro_x /= MOVE_ONE_G; + gyro_y /= MOVE_ONE_G; + gyro_z /= MOVE_ONE_G; + } + + pad->move_data.accelerometer_x = accel_x; + pad->move_data.accelerometer_y = accel_y; + pad->move_data.accelerometer_z = accel_z; + pad->move_data.gyro_x = gyro_x; + pad->move_data.gyro_y = gyro_y; + pad->move_data.gyro_z = gyro_z; + + if (dev->model == ps_move_model::ZCM1) + { + const ps_move_input_report_ZCM1& input_zcm1 = dev->input_report_ZCM1; + + #define TWELVE_BIT_SIGNED(x) (((x) & 0x800) ? (-(((~(x)) & 0xFFF) + 1)) : (x)) + pad->move_data.magnetometer_x = static_cast(TWELVE_BIT_SIGNED(((input.magnetometer_x & 0x0F) << 8) | input_zcm1.magnetometer_x2)); + pad->move_data.magnetometer_y = static_cast(TWELVE_BIT_SIGNED((input_zcm1.magnetometer_y << 4) | (input_zcm1.magnetometer_yz & 0xF0) >> 4)); + pad->move_data.magnetometer_z = static_cast(TWELVE_BIT_SIGNED(((input_zcm1.magnetometer_yz & 0x0F) << 8) | input_zcm1.magnetometer_z)); + } } pad->move_data.temperature = ((input.temperature << 4) | ((input.magnetometer_x & 0xF0) >> 4)); - pad->m_sensors[0].m_value = Clamp0To1023(512.0f + (MOTION_ONE_G * pad->move_data.accelerometer_x * -1.0f)); - pad->m_sensors[1].m_value = Clamp0To1023(512.0f + (MOTION_ONE_G * pad->move_data.accelerometer_y * -1.0f)); - pad->m_sensors[2].m_value = Clamp0To1023(512.0f + (MOTION_ONE_G * pad->move_data.accelerometer_z)); - pad->m_sensors[3].m_value = Clamp0To1023(512.0f + (MOTION_ONE_G * pad->move_data.gyro_z * -1.0f)); - - dev->update_orientation(pad->move_data); + pad->m_sensors[0].m_value = Clamp0To1023(512.0f + (MOTION_ONE_G * accel_x * -1.0f)); + pad->m_sensors[1].m_value = Clamp0To1023(512.0f + (MOTION_ONE_G * accel_y * -1.0f)); + pad->m_sensors[2].m_value = Clamp0To1023(512.0f + (MOTION_ONE_G * accel_z)); + pad->m_sensors[3].m_value = Clamp0To1023(512.0f + (MOTION_ONE_G * gyro_z * -1.0f)); handle_external_device(binding); } @@ -863,74 +879,3 @@ u32 ps_move_handler::get_battery_level(const std::string& padId) // 0 to 5 return std::clamp(device->battery_level * 20, 0, 100); } - -void ps_move_device::reset_orientation() -{ - // Initialize Fusion - ahrs = {}; - FusionAhrsInitialise(&ahrs); - ahrs.settings.convention = FusionConvention::FusionConventionEnu; - ahrs.settings.gain = 0.0f; // If gain is set, the algorithm tries to adjust the orientation over time. - FusionAhrsSetSettings(&ahrs, &ahrs.settings); - FusionAhrsReset(&ahrs); -} - -void ps_move_device::update_orientation(ps_move_data& move_data) -{ - if (move_data.calibration_requested) - { - reset_orientation(); - - move_data.calibration_succeeded = true; - } - - // Get elapsed time since last update - const u64 now_us = get_system_time(); - const float elapsed_sec = (last_ahrs_update_time_us == 0) ? 0.0f : ((now_us - last_ahrs_update_time_us) / 1'000'000.0f); - last_ahrs_update_time_us = now_us; - - // The ps move handler's axis may differ from the Fusion axis, so we have to map them correctly. - // Don't ask how the axis work. It's basically been trial and error. - ensure(ahrs.settings.convention == FusionConvention::FusionConventionEnu); // East-North-Up - - const FusionVector accelerometer{ - .axis { - .x = -move_data.accelerometer_x, - .y = +move_data.accelerometer_y, - .z = +move_data.accelerometer_z - } - }; - - static constexpr f32 PI = 3.14159265f; - const auto rad_to_degree = [](f32 radians) -> f32 { return radians * 180.0f / PI; }; - const FusionVector gyroscope{ - .axis { - .x = +rad_to_degree(move_data.gyro_x), - .y = +rad_to_degree(move_data.gyro_z), - .z = -rad_to_degree(move_data.gyro_y) - } - }; - - FusionVector magnetometer {}; - - if (move_data.magnetometer_enabled) - { - magnetometer = FusionVector{ - .axis { - .x = move_data.magnetometer_x, - .y = move_data.magnetometer_y, - .z = move_data.magnetometer_z - } - }; - } - - // Update Fusion - FusionAhrsUpdate(&ahrs, gyroscope, accelerometer, magnetometer, elapsed_sec); - - // Get quaternion - const FusionQuaternion quaternion = FusionAhrsGetQuaternion(&ahrs); - move_data.quaternion[0] = quaternion.array[1]; - move_data.quaternion[1] = quaternion.array[2]; - move_data.quaternion[2] = quaternion.array[3]; - move_data.quaternion[3] = quaternion.array[0]; -} diff --git a/rpcs3/Input/ps_move_handler.h b/rpcs3/Input/ps_move_handler.h index 4c6097fecf..d8efc463c7 100644 --- a/rpcs3/Input/ps_move_handler.h +++ b/rpcs3/Input/ps_move_handler.h @@ -146,12 +146,6 @@ public: u32 external_device_id = 0; ps_move_calibration calibration{}; - FusionAhrs ahrs {}; // Used to calculate quaternions from sensor data - u64 last_ahrs_update_time_us = 0; // Last ahrs update - - void update_orientation(ps_move_data& move_data); - void reset_orientation(); - const reports::ps_move_input_report_common& input_report_common() const; }; diff --git a/rpcs3/Input/sdl_pad_handler.cpp b/rpcs3/Input/sdl_pad_handler.cpp index aaf95331f1..202eca4038 100644 --- a/rpcs3/Input/sdl_pad_handler.cpp +++ b/rpcs3/Input/sdl_pad_handler.cpp @@ -179,6 +179,7 @@ sdl_pad_handler::sdl_pad_handler() : PadHandlerBase(pad_handler::sdl) b_has_rgb = true; b_has_battery = true; b_has_battery_led = true; + b_has_orientation = true; m_trigger_threshold = trigger_max / 2; m_thumb_threshold = thumb_max / 2; @@ -233,6 +234,7 @@ void sdl_pad_handler::init_config(cfg_pad* cfg) cfg->pressure_intensity_button.def = ::at32(button_list, SDLKeyCodes::None); cfg->analog_limiter_button.def = ::at32(button_list, SDLKeyCodes::None); + cfg->orientation_reset_button.def = ::at32(button_list, SDLKeyCodes::None); // Set default misc variables cfg->lstick_anti_deadzone.def = static_cast(0.13 * thumb_max); // 13% @@ -732,14 +734,14 @@ void sdl_pad_handler::get_extended_info(const pad_ensemble& binding) } else { - const float& accel_x = dev->values_accel[0]; // Angular speed around the x axis (pitch) - const float& accel_y = dev->values_accel[1]; // Angular speed around the y axis (yaw) - const float& accel_z = dev->values_accel[2]; // Angular speed around the z axis (roll + const f32 accel_x = dev->values_accel[0]; // Angular speed around the x axis (pitch) + const f32 accel_y = dev->values_accel[1]; // Angular speed around the y axis (yaw) + const f32 accel_z = dev->values_accel[2]; // Angular speed around the z axis (roll // Convert to ds3. The ds3 resolution is 113/G. - pad->m_sensors[0].m_value = Clamp0To1023((accel_x / SDL_STANDARD_GRAVITY) * -1 * 113 + 512); - pad->m_sensors[1].m_value = Clamp0To1023((accel_y / SDL_STANDARD_GRAVITY) * -1 * 113 + 512); - pad->m_sensors[2].m_value = Clamp0To1023((accel_z / SDL_STANDARD_GRAVITY) * -1 * 113 + 512); + pad->m_sensors[0].m_value = Clamp0To1023((accel_x / SDL_STANDARD_GRAVITY) * -1 * MOTION_ONE_G + 512); + pad->m_sensors[1].m_value = Clamp0To1023((accel_y / SDL_STANDARD_GRAVITY) * -1 * MOTION_ONE_G + 512); + pad->m_sensors[2].m_value = Clamp0To1023((accel_z / SDL_STANDARD_GRAVITY) * -1 * MOTION_ONE_G + 512); } } @@ -751,16 +753,20 @@ void sdl_pad_handler::get_extended_info(const pad_ensemble& binding) } else { - //const float& gyro_x = dev->values_gyro[0]; // Angular speed around the x axis (pitch) - const float& gyro_y = dev->values_gyro[1]; // Angular speed around the y axis (yaw) - //const float& gyro_z = dev->values_gyro[2]; // Angular speed around the z axis (roll) + //const f32 gyro_x = dev->values_gyro[0]; // Angular speed around the x axis (pitch) + const f32 gyro_y = dev->values_gyro[1]; // Angular speed around the y axis (yaw) + //const f32 gyro_z = dev->values_gyro[2]; // Angular speed around the z axis (roll) // Convert to ds3. The ds3 resolution is 123/90°/sec. The SDL gyro is measured in rad/sec. - static constexpr f32 PI = 3.14159265f; - const float degree = (gyro_y * 180.0f / PI); + const f32 degree = rad_to_degree(gyro_y); pad->m_sensors[3].m_value = Clamp0To1023(degree * (123.f / 90.f) + 512); } } + + if (dev->sdl.has_accel || dev->sdl.has_gyro) + { + set_raw_orientation(*pad); + } } void sdl_pad_handler::get_motion_sensors(const std::string& pad_id, const motion_callback& callback, const motion_fail_callback& fail_callback, motion_preview_values preview_values, const std::array& sensors) diff --git a/rpcs3/Input/sdl_pad_handler.h b/rpcs3/Input/sdl_pad_handler.h index f4fa256d29..a375f8a07f 100644 --- a/rpcs3/Input/sdl_pad_handler.h +++ b/rpcs3/Input/sdl_pad_handler.h @@ -46,8 +46,8 @@ public: bool has_accel = false; bool has_gyro = false; - float data_rate_accel = 0.0f; - float data_rate_gyro = 0.0f; + f32 data_rate_accel = 0.0f; + f32 data_rate_gyro = 0.0f; std::set button_ids; std::set axis_ids; @@ -57,8 +57,8 @@ public: sdl_info sdl{}; - std::array values_accel{}; - std::array values_gyro{}; + std::array values_accel{}; + std::array values_gyro{}; bool led_needs_update = true; bool led_is_on = true; diff --git a/rpcs3/Input/skateboard_pad_handler.cpp b/rpcs3/Input/skateboard_pad_handler.cpp index fbf1937562..86996847d6 100644 --- a/rpcs3/Input/skateboard_pad_handler.cpp +++ b/rpcs3/Input/skateboard_pad_handler.cpp @@ -84,6 +84,7 @@ skateboard_pad_handler::skateboard_pad_handler() b_has_battery = false; b_has_battery_led = false; b_has_pressure_intensity_button = false; + b_has_orientation = true; m_name_string = "Skateboard #"; m_max_devices = CELL_PAD_MAX_PORT_NUM; @@ -134,6 +135,8 @@ void skateboard_pad_handler::init_config(cfg_pad* cfg) cfg->tilt_left.def = ::at32(button_list, skateboard_key_codes::tilt_left); cfg->tilt_right.def = ::at32(button_list, skateboard_key_codes::tilt_right); + cfg->orientation_reset_button.def = ::at32(button_list, skateboard_key_codes::none); + // Set default misc variables cfg->lstick_anti_deadzone.def = 0; cfg->rstick_anti_deadzone.def = 0; @@ -323,6 +326,8 @@ void skateboard_pad_handler::get_extended_info(const pad_ensemble& binding) pad->m_sensors[1].m_value = Clamp0To1023(input.large_axes[1]); pad->m_sensors[2].m_value = Clamp0To1023(input.large_axes[2]); pad->m_sensors[3].m_value = Clamp0To1023(input.large_axes[3]); + + set_raw_orientation(*pad); } pad_preview_values skateboard_pad_handler::get_preview_values(const std::unordered_map& /*data*/) diff --git a/rpcs3/Input/xinput_pad_handler.cpp b/rpcs3/Input/xinput_pad_handler.cpp index 5ee7c4fe23..d7d3c7991f 100644 --- a/rpcs3/Input/xinput_pad_handler.cpp +++ b/rpcs3/Input/xinput_pad_handler.cpp @@ -64,6 +64,7 @@ xinput_pad_handler::xinput_pad_handler() : PadHandlerBase(pad_handler::xinput) b_has_deadzones = true; b_has_battery = true; b_has_battery_led = false; + b_has_orientation = false; m_name_string = "XInput Pad #"; m_max_devices = XUSER_MAX_COUNT; @@ -119,6 +120,7 @@ void xinput_pad_handler::init_config(cfg_pad* cfg) cfg->pressure_intensity_button.def = ::at32(button_list, XInputKeyCodes::None); cfg->analog_limiter_button.def = ::at32(button_list, XInputKeyCodes::None); + cfg->orientation_reset_button.def = ::at32(button_list, XInputKeyCodes::None); // Set default misc variables cfg->lstick_anti_deadzone.def = static_cast(0.13 * thumb_max); // 13% @@ -413,6 +415,8 @@ bool xinput_pad_handler::Init() } } + b_has_orientation = !!xinputGetCustomData; + if (!m_is_init) return false; @@ -551,6 +555,8 @@ void xinput_pad_handler::get_extended_info(const pad_ensemble& binding) pad->m_sensors[1].m_value = sensors.SCP_ACCEL_Y; pad->m_sensors[2].m_value = sensors.SCP_ACCEL_Z; pad->m_sensors[3].m_value = sensors.SCP_GYRO; + + set_raw_orientation(*pad); } } } diff --git a/rpcs3/rpcs3qt/pad_settings_dialog.cpp b/rpcs3/rpcs3qt/pad_settings_dialog.cpp index c4e04cdc65..616d6577aa 100644 --- a/rpcs3/rpcs3qt/pad_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/pad_settings_dialog.cpp @@ -312,6 +312,7 @@ void pad_settings_dialog::InitButtons() insert_button(button_ids::id_pressure_intensity, ui->b_pressure_intensity); insert_button(button_ids::id_analog_limiter, ui->b_analog_limiter); + insert_button(button_ids::id_orientation_reset, ui->b_orientation_reset); m_pad_buttons->addButton(ui->b_refresh, button_ids::id_refresh); m_pad_buttons->addButton(ui->b_addConfig, button_ids::id_add_config_file); @@ -720,6 +721,7 @@ void pad_settings_dialog::ReloadButtons() updateButton(button_ids::id_pressure_intensity, ui->b_pressure_intensity, &cfg.pressure_intensity_button); updateButton(button_ids::id_analog_limiter, ui->b_analog_limiter, &cfg.analog_limiter_button); + updateButton(button_ids::id_orientation_reset, ui->b_orientation_reset, &cfg.orientation_reset_button); UpdateLabels(true); } @@ -1195,6 +1197,9 @@ void pad_settings_dialog::UpdateLabels(bool is_reset) RepaintPreviewLabel(ui->preview_stick_left, ui->slider_stick_left->value(), ui->anti_deadzone_slider_stick_left->value(), ui->slider_stick_left->size().width(), m_lx, m_ly, cfg.lpadsquircling, cfg.lstickmultiplier / 100.0); RepaintPreviewLabel(ui->preview_stick_right, ui->slider_stick_right->value(), ui->anti_deadzone_slider_stick_right->value(), ui->slider_stick_right->size().width(), m_rx, m_ry, cfg.rpadsquircling, cfg.rstickmultiplier / 100.0); + // Update orientation toggle + ui->cb_orientation_toggle->setChecked(cfg.orientation_enabled.get()); + // Update analog limiter toggle mode ui->cb_analog_limiter_toggle_mode->setChecked(cfg.analog_limiter_toggle_mode.get()); @@ -1249,6 +1254,7 @@ void pad_settings_dialog::SwitchButtons(bool is_enabled) ui->gb_pressure_intensity_deadzone->setEnabled(is_enabled); ui->gb_pressure_intensity->setEnabled(is_enabled && m_enable_pressure_intensity_button); ui->gb_analog_limiter->setEnabled(is_enabled && m_enable_analog_limiter_button); + ui->gb_orientation_reset->setEnabled(is_enabled && m_enable_orientation_reset_button); ui->gb_vibration->setEnabled(is_enabled && m_enable_rumble); ui->gb_motion_controls->setEnabled(is_enabled && m_enable_motion); ui->gb_stick_deadzones->setEnabled(is_enabled && m_enable_deadzones); @@ -1467,11 +1473,15 @@ void pad_settings_dialog::ChangeHandler() // Enable Analog Limiter Settings m_enable_analog_limiter_button = m_handler->has_analog_limiter_button(); + // Enable Orientation Reset Settings + m_enable_orientation_reset_button = m_handler->has_orientation(); + // Change our contextual widgets ui->left_stack->setCurrentIndex((m_handler->m_type == pad_handler::keyboard) ? 1 : 0); ui->right_stack->setCurrentIndex((m_handler->m_type == pad_handler::keyboard) ? 1 : 0); ui->gb_pressure_intensity->setVisible(m_handler->has_pressure_intensity_button()); ui->gb_analog_limiter->setVisible(m_handler->has_analog_limiter_button()); + ui->gb_orientation_reset->setVisible(m_handler->has_orientation()); // Update device dropdown and block signals while doing so ui->chooseDevice->blockSignals(true); @@ -1829,8 +1839,11 @@ void pad_settings_dialog::ApplyCurrentPlayerConfig(int new_player_id) for (const auto& [id, button] : m_cfg_entries) { // Let's ignore special keys, unless we're using a keyboard - if ((id == button_ids::id_pressure_intensity || id == button_ids::id_analog_limiter) && m_handler->m_type != pad_handler::keyboard) + if (m_handler->m_type != pad_handler::keyboard && + (id == button_ids::id_pressure_intensity || id == button_ids::id_analog_limiter || id == button_ids::id_orientation_reset)) + { continue; + } for (const std::string& key : cfg_pad::get_buttons(button.keys)) { @@ -1886,6 +1899,11 @@ void pad_settings_dialog::ApplyCurrentPlayerConfig(int new_player_id) cfg.pressure_intensity_toggle_mode.set(ui->cb_pressure_intensity_toggle_mode->isChecked()); } + if (m_handler->has_orientation()) + { + cfg.orientation_enabled.set(ui->cb_orientation_toggle->isChecked()); + } + cfg.pressure_intensity_deadzone.set(ui->pressure_intensity_deadzone->value()); if (m_handler->m_type == pad_handler::keyboard) @@ -2077,6 +2095,7 @@ void pad_settings_dialog::SubscribeTooltips() // Localized tooltips const Tooltips tooltips; + SubscribeTooltip(ui->gb_orientation_reset, tooltips.gamepad_settings.orientation_reset); SubscribeTooltip(ui->gb_analog_limiter, tooltips.gamepad_settings.analog_limiter); SubscribeTooltip(ui->gb_pressure_intensity, tooltips.gamepad_settings.pressure_intensity); SubscribeTooltip(ui->gb_pressure_intensity_deadzone, tooltips.gamepad_settings.pressure_deadzone); diff --git a/rpcs3/rpcs3qt/pad_settings_dialog.h b/rpcs3/rpcs3qt/pad_settings_dialog.h index 1e80ff65f7..9902dde9ff 100644 --- a/rpcs3/rpcs3qt/pad_settings_dialog.h +++ b/rpcs3/rpcs3qt/pad_settings_dialog.h @@ -66,6 +66,7 @@ class pad_settings_dialog : public QDialog id_pressure_intensity, // Special button for pressure intensity id_analog_limiter, // Special button for analog limiter + id_orientation_reset, // Special button for orientation reset id_pad_end, // end @@ -123,6 +124,7 @@ private: bool m_enable_motion{ false }; bool m_enable_pressure_intensity_button{ true }; bool m_enable_analog_limiter_button{ true }; + bool m_enable_orientation_reset_button{ true }; // Button Mapping QButtonGroup* m_pad_buttons = nullptr; diff --git a/rpcs3/rpcs3qt/pad_settings_dialog.ui b/rpcs3/rpcs3qt/pad_settings_dialog.ui index 86add99c28..a886f16b31 100644 --- a/rpcs3/rpcs3qt/pad_settings_dialog.ui +++ b/rpcs3/rpcs3qt/pad_settings_dialog.ui @@ -36,9 +36,9 @@ 0 - -71 - 1290 - 907 + 0 + 1292 + 902 @@ -669,6 +669,57 @@ + + + + Orientation Reset + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + - + + + + + + + Qt::Orientation::Horizontal + + + QSizePolicy::Policy::MinimumExpanding + + + + 0 + 0 + + + + + + + + Orientation + + + + + + @@ -695,12 +746,12 @@ - + - Qt::Horizontal + Qt::Orientation::Horizontal - QSizePolicy::MinimumExpanding + QSizePolicy::Policy::MinimumExpanding @@ -2286,7 +2337,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -2316,7 +2367,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -2364,7 +2415,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -2391,7 +2442,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal diff --git a/rpcs3/rpcs3qt/tooltips.h b/rpcs3/rpcs3qt/tooltips.h index 8a2e00b682..4e5de9d619 100644 --- a/rpcs3/rpcs3qt/tooltips.h +++ b/rpcs3/rpcs3qt/tooltips.h @@ -287,6 +287,7 @@ public: const QString mmjoy = tr("The MMJoystick handler should work with almost any controller recognized by Windows. However, it is recommended that you use the more specific handlers if you have a controller that supports them."); const QString sdl = tr("The SDL handler supports a variety of controllers across different platforms."); + const QString orientation_reset = tr("Resets the sensor orientation when pressed.
Toggle the checkbox to enable or disable the orientation feature.
Currently only used for PS Move interactions."); const QString analog_limiter = tr("Applies the stick multipliers while this special button is pressed.
Enable \"Toggle\" if you want to toggle the analog limiter on button press instead."); const QString pressure_intensity = tr("Controls the intensity of pressure sensitive buttons while this special button is pressed.
Enable \"Toggle\" if you want to toggle the intensity on button press instead.
Use the percentage to change how hard you want to press a button."); const QString pressure_deadzone = tr("Controls the deadzone of pressure sensitive buttons. It determines how far the button has to be pressed until it is recognized by the game. The resulting range will be projected onto the full button sensitivity range."); From 3381a884d08a91dd8e86b32e2b046355033f584a Mon Sep 17 00:00:00 2001 From: Megamouse Date: Wed, 8 Jan 2025 14:50:50 +0100 Subject: [PATCH 32/60] Fix some warnings --- rpcs3/Emu/Cell/SPUThread.cpp | 2 +- rpcs3/Emu/Io/PadHandler.cpp | 2 +- rpcs3/Emu/Io/pad_types.cpp | 2 +- rpcs3/Emu/Io/pad_types.h | 2 +- rpcs3/Input/ps_move_handler.cpp | 12 ++++++------ 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 8804d93138..54679dee41 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -5481,7 +5481,7 @@ bool spu_thread::reservation_check(u32 addr, u32 hash, atomic_t* range_ usz spu_thread::register_cache_line_waiter(u32 addr) { - const u64 value = u64{compute_rdata_hash32(rdata)} << 32 | raddr; + const u64 value = u64{compute_rdata_hash32(rdata)} << 32 | addr; for (usz i = 0; i < std::size(g_spu_waiters_by_value); i++) { diff --git a/rpcs3/Emu/Io/PadHandler.cpp b/rpcs3/Emu/Io/PadHandler.cpp index 4dc9bcb546..44648f252f 100644 --- a/rpcs3/Emu/Io/PadHandler.cpp +++ b/rpcs3/Emu/Io/PadHandler.cpp @@ -883,7 +883,7 @@ void PadHandlerBase::get_orientation(const pad_ensemble& binding) const return; } - if (!pad->move_data.orientation_enabled || pad->get_orientation_reset_button_active(pad->m_player_id)) + if (!pad->move_data.orientation_enabled || pad->get_orientation_reset_button_active()) { // This can be called extensively in quick succession, so let's just reset the pointer instead of creating a new object. device->ahrs.reset(); diff --git a/rpcs3/Emu/Io/pad_types.cpp b/rpcs3/Emu/Io/pad_types.cpp index 0e9c41bcaa..46d1f284d7 100644 --- a/rpcs3/Emu/Io/pad_types.cpp +++ b/rpcs3/Emu/Io/pad_types.cpp @@ -253,7 +253,7 @@ bool Pad::get_analog_limiter_button_active(bool is_toggle_mode, u32 player_id) return analog_limiter_button.m_pressed; } -bool Pad::get_orientation_reset_button_active(u32 player_id) +bool Pad::get_orientation_reset_button_active() { if (m_orientation_reset_button_index < 0) { diff --git a/rpcs3/Emu/Io/pad_types.h b/rpcs3/Emu/Io/pad_types.h index 966b1a6b28..f48fbf7850 100644 --- a/rpcs3/Emu/Io/pad_types.h +++ b/rpcs3/Emu/Io/pad_types.h @@ -518,7 +518,7 @@ struct Pad bool get_analog_limiter_button_active(bool is_toggle_mode, u32 player_id); s32 m_orientation_reset_button_index{-1}; // Special button index. -1 if not set. - bool get_orientation_reset_button_active(u32 player_id); + bool get_orientation_reset_button_active(); // Cable State: 0 - 1 plugged in ? u8 m_cable_state{0}; diff --git a/rpcs3/Input/ps_move_handler.cpp b/rpcs3/Input/ps_move_handler.cpp index d405775764..b53b4bcd7e 100644 --- a/rpcs3/Input/ps_move_handler.cpp +++ b/rpcs3/Input/ps_move_handler.cpp @@ -679,12 +679,12 @@ void ps_move_handler::get_extended_info(const pad_ensemble& binding) if (dev->model == ps_move_model::ZCM1) { - accel_x -= zero_shift; - accel_y -= zero_shift; - accel_z -= zero_shift; - gyro_x -= zero_shift; - gyro_y -= zero_shift; - gyro_z -= zero_shift; + accel_x -= static_cast(zero_shift); + accel_y -= static_cast(zero_shift); + accel_z -= static_cast(zero_shift); + gyro_x -= static_cast(zero_shift); + gyro_y -= static_cast(zero_shift); + gyro_z -= static_cast(zero_shift); } if (!device->config || !device->config->orientation_enabled) From 9407974c105037a0617bdefe97b235f6226d8198 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Thu, 9 Jan 2025 00:35:11 +0100 Subject: [PATCH 33/60] cellGem: Add more camera image conversions --- rpcs3/Emu/Cell/Modules/cellGem.cpp | 373 ++++++++++++++++++++++++++++- 1 file changed, 366 insertions(+), 7 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellGem.cpp b/rpcs3/Emu/Cell/Modules/cellGem.cpp index 292036197f..48d609120f 100644 --- a/rpcs3/Emu/Cell/Modules/cellGem.cpp +++ b/rpcs3/Emu/Cell/Modules/cellGem.cpp @@ -552,6 +552,24 @@ namespace gem std::array positions {}; + struct YUV + { + u8 y = 0; + u8 u = 0; + u8 v = 0; + + YUV(u8 r, u8 g, u8 b) + : y(Y(r, g, b)) + , u(U(r, g, b)) + , v(V(r, g, b)) + { + } + + static inline u8 Y(u8 r, u8 g, u8 b) { return static_cast(0.299f * r + 0.587f * g + 0.114f * b); } + static inline u8 U(u8 r, u8 g, u8 b) { return static_cast(-0.14713f * r - 0.28886f * g + 0.436f * b); } + static inline u8 V(u8 r, u8 g, u8 b) { return static_cast(0.615f * r - 0.51499f * g - 0.10001f * b); } + }; + bool convert_image_format(CellCameraFormat input_format, CellGemVideoConvertFormatEnum output_format, const std::vector& video_data_in, u32 width, u32 height, u8* video_data_out, u32 video_data_out_size) @@ -594,13 +612,11 @@ namespace gem for (u32 y = 0; y < height - 1; y += 2) { - const u8* src = &video_data_in[y * in_pitch]; - const u8* src0 = src; - const u8* src1 = src + in_pitch; + const u8* src0 = &video_data_in[y * in_pitch]; + const u8* src1 = src0 + in_pitch; - u8* dst_row = video_data_out + y * out_pitch; - u8* dst0 = dst_row; - u8* dst1 = dst_row + out_pitch; + u8* dst0 = video_data_out + y * out_pitch; + u8* dst1 = dst0 + out_pitch; for (u32 x = 0; x < width - 1; x += 2, src0 += 2, src1 += 2, dst0 += 8, dst1 += 8) { @@ -654,14 +670,357 @@ namespace gem } break; } - case CELL_GEM_RGBA_320x240: // RGBA output; 320*240*4-byte output buffer required case CELL_GEM_YUV_640x480: // YUV output; 640*480+640*480+640*480-byte output buffer required (contiguous) + { + const u32 yuv_pitch = width; + + u8* dst_y = video_data_out; + u8* dst_u = dst_y + yuv_pitch * height; + u8* dst_v = dst_u + yuv_pitch * height; + + switch (input_format) + { + case CELL_CAMERA_RAW8: + { + const u32 in_pitch = width; + + for (u32 y = 0; y < height - 1; y += 2) + { + const u8* src0 = &video_data_in[y * in_pitch]; + const u8* src1 = src0 + in_pitch; + + u8* dst_y0 = dst_y + y * yuv_pitch; + u8* dst_y1 = dst_y0 + yuv_pitch; + + u8* dst_u0 = dst_u + y * yuv_pitch; + u8* dst_u1 = dst_u0 + yuv_pitch; + + u8* dst_v0 = dst_v + y * yuv_pitch; + u8* dst_v1 = dst_v0 + yuv_pitch; + + for (u32 x = 0; x < width - 1; x += 2, src0 += 2, src1 += 2, dst_y0 += 2, dst_y1 += 2, dst_u0 += 2, dst_u1 += 2, dst_v0 += 2, dst_v1 += 2) + { + const u8 b = src0[0]; + const u8 g0 = src0[1]; + const u8 g1 = src1[0]; + const u8 r = src1[1]; + + // Convert RGBA to YUV + const YUV yuv_top = YUV(r, g0, b); + const YUV yuv_bottom = YUV(r, g1, b); + + dst_y0[0] = dst_y0[1] = yuv_top.y; + dst_y1[0] = dst_y1[1] = yuv_bottom.y; + + dst_u0[0] = dst_u0[1] = yuv_top.u; + dst_u1[0] = dst_u1[1] = yuv_bottom.u; + + dst_v0[0] = dst_v0[1] = yuv_top.v; + dst_v1[0] = dst_v1[1] = yuv_bottom.v; + } + } + break; + } + case CELL_CAMERA_RGBA: + { + const u32 in_pitch = width / 4; + + for (u32 y = 0; y < height; y++) + { + const u8* src = &video_data_in[y * in_pitch]; + + for (u32 x = 0; x < width; x++, src += 4) + { + const u8 r = src[0]; + const u8 g = src[1]; + const u8 b = src[2]; + + // Convert RGBA to YUV + const YUV yuv = YUV(r, g, b); + + *dst_y++ = yuv.y; + *dst_u++ = yuv.u; + *dst_v++ = yuv.v; + } + } + break; + } + default: + { + cellGem.error("Unimplemented: Converting %s to %s", input_format, output_format); + std::memcpy(video_data_out, video_data_in.data(), std::min(required_in_size, required_out_size)); + return false; + } + } + break; + } case CELL_GEM_YUV422_640x480: // YUV output; 640*480+320*480+320*480-byte output buffer required (contiguous) + { + const u32 y_pitch = width; + const u32 uv_pitch = width / 2; + + u8* dst_y = video_data_out; + u8* dst_u = dst_y + y_pitch * height; + u8* dst_v = dst_u + uv_pitch * height; + + switch (input_format) + { + case CELL_CAMERA_RAW8: + { + const u32 in_pitch = width; + + for (u32 y = 0; y < height - 1; y += 2) + { + const u8* src0 = &video_data_in[y * in_pitch]; + const u8* src1 = src0 + in_pitch; + + u8* dst_y0 = dst_y + y * y_pitch; + u8* dst_y1 = dst_y0 + y_pitch; + + u8* dst_u0 = dst_u + y * uv_pitch; + u8* dst_u1 = dst_u0 + uv_pitch; + + u8* dst_v0 = dst_v + y * uv_pitch; + u8* dst_v1 = dst_v0 + uv_pitch; + + for (u32 x = 0; x < width - 1; x += 2, src0 += 2, src1 += 2, dst_y0 += 2, dst_y1 += 2) + { + const u8 b = src0[0]; + const u8 g0 = src0[1]; + const u8 g1 = src1[0]; + const u8 r = src1[1]; + + // Convert RGBA to YUV + const YUV yuv_top = YUV(r, g0, b); + const YUV yuv_bottom = YUV(r, g1, b); + + dst_y0[0] = dst_y0[1] = yuv_top.y; + dst_y1[0] = dst_y1[1] = yuv_bottom.y; + + *dst_u0++ = yuv_top.u; + *dst_u1++ = yuv_bottom.u; + + *dst_v0++ = yuv_top.v; + *dst_v1++ = yuv_bottom.v; + } + } + break; + } + case CELL_CAMERA_RGBA: + { + const u32 in_pitch = width * 4; + + for (u32 y = 0; y < height; y++) + { + const u8* src = &video_data_in[y * in_pitch]; + + for (u32 x = 0; x < width - 1; x += 2, src += 8, dst_y += 2) + { + const u8 r_0 = src[0]; + const u8 g_0 = src[1]; + const u8 b_0 = src[2]; + const u8 r_1 = src[4]; + const u8 g_1 = src[5]; + const u8 b_1 = src[6]; + + // Convert RGBA to YUV + const YUV yuv_0 = YUV(r_0, g_0, b_0); + const u8 y_1 = YUV::Y(r_1, g_1, b_1); + + dst_y[0] = yuv_0.y; + dst_y[1] = y_1; + *dst_u++ = yuv_0.u; + *dst_v++ = yuv_0.v; + } + } + break; + } + default: + { + cellGem.error("Unimplemented: Converting %s to %s", input_format, output_format); + std::memcpy(video_data_out, video_data_in.data(), std::min(required_in_size, required_out_size)); + return false; + } + } + break; + } case CELL_GEM_YUV411_640x480: // YUV411 output; 640*480+320*240+320*240-byte output buffer required (contiguous) + { + const u32 y_pitch = width; + const u32 uv_pitch = width / 4; + + u8* dst_y = video_data_out; + u8* dst_u = dst_y + y_pitch * height; + u8* dst_v = dst_u + uv_pitch * height; + + switch (input_format) + { + case CELL_CAMERA_RAW8: + { + const u32 in_pitch = width; + + for (u32 y = 0; y < height - 1; y += 2) + { + const u8* src0 = &video_data_in[y * in_pitch]; + const u8* src1 = src0 + in_pitch; + + u8* dst_y0 = dst_y + y * y_pitch; + u8* dst_y1 = dst_y0 + y_pitch; + + u8* dst_u0 = dst_u + y * uv_pitch; + u8* dst_u1 = dst_u0 + uv_pitch; + + u8* dst_v0 = dst_v + y * uv_pitch; + u8* dst_v1 = dst_v0 + uv_pitch; + + for (u32 x = 0; x < width - 3; x += 4, src0 += 4, src1 += 4, dst_y0 += 4, dst_y1 += 4) + { + const u8 b_left = src0[0]; + const u8 g0_left = src0[1]; + const u8 b_right = src0[2]; + const u8 g0_right = src0[3]; + + const u8 g1_left = src1[0]; + const u8 r_left = src1[1]; + const u8 g1_right = src1[2]; + const u8 r_right = src1[3]; + + // Convert RGBA to YUV + const YUV yuv_top_left = YUV(r_left, g0_left, b_left); // Re-used for top-right + const u8 y_top_right = YUV::Y(r_right, g0_right, b_right); + const YUV yuv_bottom_left = YUV(r_left, g1_left, b_left); // Re-used for bottom-right + const u8 y_bottom_right = YUV::Y(r_right, g1_right, b_right); + + dst_y0[0] = dst_y0[1] = yuv_top_left.y; + dst_y0[2] = dst_y0[3] = y_top_right; + + dst_y1[0] = dst_y1[1] = yuv_bottom_left.y; + dst_y1[2] = dst_y1[3] = y_bottom_right; + + *dst_u0++ = yuv_top_left.u; + *dst_u1++ = yuv_bottom_left.u; + + *dst_v0++ = yuv_top_left.v; + *dst_v1++ = yuv_bottom_left.v; + } + } + break; + } + case CELL_CAMERA_RGBA: + { + const u32 in_pitch = width * 4; + + for (u32 y = 0; y < height; y++) + { + const u8* src = &video_data_in[y * in_pitch]; + + for (u32 x = 0; x < width - 3; x += 4, src += 16, dst_y += 4) + { + const u8 r_0 = src[0]; + const u8 g_0 = src[1]; + const u8 b_0 = src[2]; + const u8 r_1 = src[4]; + const u8 g_1 = src[5]; + const u8 b_1 = src[6]; + const u8 r_2 = src[8]; + const u8 g_2 = src[9]; + const u8 b_2 = src[10]; + const u8 r_3 = src[12]; + const u8 g_3 = src[13]; + const u8 b_3 = src[14]; + + // Convert RGBA to YUV + const YUV yuv_0 = YUV(r_0, g_0, b_0); + const u8 y_1 = YUV::Y(r_1, g_1, b_1); + const u8 y_2 = YUV::Y(r_2, g_2, b_2); + const u8 y_3 = YUV::Y(r_3, g_3, b_3); + + dst_y[0] = yuv_0.y; + dst_y[1] = y_1; + dst_y[2] = y_2; + dst_y[3] = y_3; + *dst_u++ = yuv_0.u; + *dst_v++ = yuv_0.v; + } + } + break; + } + default: + { + cellGem.error("Unimplemented: Converting %s to %s", input_format, output_format); + std::memcpy(video_data_out, video_data_in.data(), std::min(required_in_size, required_out_size)); + return false; + } + } + break; + } + case CELL_GEM_RGBA_320x240: // RGBA output; 320*240*4-byte output buffer required + { + switch (input_format) + { + case CELL_CAMERA_RAW8: + { + const u32 in_pitch = width; + const u32 out_pitch = width * 4 / 2; + + for (u32 y = 0; y < height - 1; y += 2) + { + const u8* src0 = &video_data_in[y * in_pitch]; + const u8* src1 = src0 + in_pitch; + + u8* dst0 = video_data_out + (y / 2) * out_pitch; + u8* dst1 = dst0 + out_pitch; + + for (u32 x = 0; x < width - 1; x += 2, src0 += 2, src1 += 2, dst0 += 4, dst1 += 4) + { + const u8 b = src0[0]; + const u8 g0 = src0[1]; + const u8 g1 = src1[0]; + const u8 r = src1[1]; + + const u8 top[4] = { r, g0, b, 255 }; + const u8 bottom[4] = { r, g1, b, 255 }; + + // Top-Left + std::memcpy(dst0, top, 4); + + // Bottom-Left Pixel + std::memcpy(dst1, bottom, 4); + } + } + break; + } + case CELL_CAMERA_RGBA: + { + const u32 in_pitch = width * 4; + const u32 out_pitch = width * 4 / 2; + + for (u32 y = 0; y < height / 2; y++) + { + const u8* src = &video_data_in[y * 2 * in_pitch]; + u8* dst = video_data_out + y * out_pitch; + + for (u32 x = 0; x < width / 2; x++, src += 4 * 2, dst += 4) + { + std::memcpy(dst, src, 4); + } + } + break; + } + default: + { + cellGem.error("Unimplemented: Converting %s to %s", input_format, output_format); + std::memcpy(video_data_out, video_data_in.data(), std::min(required_in_size, required_out_size)); + return false; + } + } + break; + } case CELL_GEM_BAYER_RESTORED_RGGB: // Restored Bayer output, 2x2 pixels rearranged into 320x240 RG1G2B case CELL_GEM_BAYER_RESTORED_RASTERIZED: // Restored Bayer output, R,G1,G2,B rearranged into 4 contiguous 320x240 1-channel rasters { cellGem.error("Unimplemented: Converting %s to %s", input_format, output_format); + std::memcpy(video_data_out, video_data_in.data(), std::min(required_in_size, required_out_size)); return false; } case CELL_GEM_NO_VIDEO_OUTPUT: // Disable video output From 7e03828f35f7447d9cca38739ecc52c6cd397c87 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Thu, 9 Jan 2025 18:20:33 +0100 Subject: [PATCH 34/60] input: Allow mapping keys to basic mouse buttons --- rpcs3/Emu/Io/MouseHandler.cpp | 2 +- rpcs3/Input/basic_mouse_handler.cpp | 44 ++++++++++++++----- rpcs3/Input/basic_mouse_handler.h | 14 ++++-- rpcs3/rpcs3qt/basic_mouse_settings_dialog.cpp | 20 +++++++++ rpcs3/rpcs3qt/basic_mouse_settings_dialog.h | 1 + 5 files changed, 64 insertions(+), 17 deletions(-) diff --git a/rpcs3/Emu/Io/MouseHandler.cpp b/rpcs3/Emu/Io/MouseHandler.cpp index 0194d8e7e9..b6c09bcb0d 100644 --- a/rpcs3/Emu/Io/MouseHandler.cpp +++ b/rpcs3/Emu/Io/MouseHandler.cpp @@ -34,7 +34,7 @@ void MouseHandlerBase::save(utils::serial& ar) bool MouseHandlerBase::is_time_for_update(double elapsed_time) { steady_clock::time_point now = steady_clock::now(); - double elapsed = (now - last_update).count() / 1000'000.; + const double elapsed = (now - last_update).count() / 1000'000.; if (elapsed > elapsed_time) { diff --git a/rpcs3/Input/basic_mouse_handler.cpp b/rpcs3/Input/basic_mouse_handler.cpp index d9435ef95e..30eed46cbd 100644 --- a/rpcs3/Input/basic_mouse_handler.cpp +++ b/rpcs3/Input/basic_mouse_handler.cpp @@ -108,10 +108,10 @@ bool basic_mouse_handler::eventFilter(QObject* target, QEvent* ev) switch (ev->type()) { case QEvent::MouseButtonPress: - MouseButtonDown(static_cast(ev)); + MouseButton(static_cast(ev), true); break; case QEvent::MouseButtonRelease: - MouseButtonUp(static_cast(ev)); + MouseButton(static_cast(ev), false); break; case QEvent::MouseMove: MouseMove(static_cast(ev)); @@ -119,6 +119,12 @@ bool basic_mouse_handler::eventFilter(QObject* target, QEvent* ev) case QEvent::Wheel: MouseScroll(static_cast(ev)); break; + case QEvent::KeyPress: + Key(static_cast(ev), true); + break; + case QEvent::KeyRelease: + Key(static_cast(ev), false); + break; default: return false; } @@ -126,22 +132,22 @@ bool basic_mouse_handler::eventFilter(QObject* target, QEvent* ev) return false; } -void basic_mouse_handler::MouseButtonDown(QMouseEvent* event) +void basic_mouse_handler::Key(QKeyEvent* event, bool pressed) { if (!event) [[unlikely]] { return; } - const int button = event->button(); - if (const auto it = std::find_if(m_buttons.cbegin(), m_buttons.cend(), [button](const auto& entry){ return entry.second == button; }); + const int key = event->key(); + if (const auto it = std::find_if(m_buttons.cbegin(), m_buttons.cend(), [key](const auto& entry){ return entry.second.code == key && entry.second.is_key; }); it != m_buttons.cend()) { - MouseHandlerBase::Button(0, it->first, true); + MouseHandlerBase::Button(0, it->first, pressed); } } -void basic_mouse_handler::MouseButtonUp(QMouseEvent* event) +void basic_mouse_handler::MouseButton(QMouseEvent* event, bool pressed) { if (!event) [[unlikely]] { @@ -149,10 +155,10 @@ void basic_mouse_handler::MouseButtonUp(QMouseEvent* event) } const int button = event->button(); - if (const auto it = std::find_if(m_buttons.cbegin(), m_buttons.cend(), [button](const auto& entry){ return entry.second == button; }); + if (const auto it = std::find_if(m_buttons.cbegin(), m_buttons.cend(), [button](const auto& entry){ return entry.second.code == button && !entry.second.is_key; }); it != m_buttons.cend()) { - MouseHandlerBase::Button(0, it->first, false); + MouseHandlerBase::Button(0, it->first, pressed); } } @@ -176,17 +182,31 @@ bool basic_mouse_handler::get_mouse_lock_state() const return false; } -int basic_mouse_handler::get_mouse_button(const cfg::string& button) +basic_mouse_handler::mouse_button basic_mouse_handler::get_mouse_button(const cfg::string& button) { const std::string name = button.to_string(); const auto it = std::find_if(mouse_list.cbegin(), mouse_list.cend(), [&name](const auto& entry){ return entry.second == name; }); if (it != mouse_list.cend()) { - return it->first; + return mouse_button{ + .code = static_cast(it->first), + .is_key = false + }; } - return Qt::MouseButton::NoButton; + if (const u32 key = keyboard_pad_handler::GetKeyCode(QString::fromStdString(name))) + { + return mouse_button{ + .code = static_cast(key), + .is_key = true + }; + } + + return mouse_button{ + .code = Qt::MouseButton::NoButton, + .is_key = false + }; } void basic_mouse_handler::MouseMove(QMouseEvent* event) diff --git a/rpcs3/Input/basic_mouse_handler.h b/rpcs3/Input/basic_mouse_handler.h index ccd4224bd8..2c5b58ab80 100644 --- a/rpcs3/Input/basic_mouse_handler.h +++ b/rpcs3/Input/basic_mouse_handler.h @@ -20,8 +20,8 @@ public: void Init(const u32 max_connect) override; void SetTargetWindow(QWindow* target); - void MouseButtonDown(QMouseEvent* event); - void MouseButtonUp(QMouseEvent* event); + void Key(QKeyEvent* event, bool pressed); + void MouseButton(QMouseEvent* event, bool pressed); void MouseScroll(QWheelEvent* event); void MouseMove(QMouseEvent* event); @@ -29,8 +29,14 @@ public: private: void reload_config(); bool get_mouse_lock_state() const; - static int get_mouse_button(const cfg::string& button); + + struct mouse_button + { + int code = Qt::MouseButton::NoButton; + bool is_key = false; + }; + static mouse_button get_mouse_button(const cfg::string& button); QWindow* m_target = nullptr; - std::map m_buttons; + std::map m_buttons; }; diff --git a/rpcs3/rpcs3qt/basic_mouse_settings_dialog.cpp b/rpcs3/rpcs3qt/basic_mouse_settings_dialog.cpp index f56ec6ec5c..e98d2798a4 100644 --- a/rpcs3/rpcs3qt/basic_mouse_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/basic_mouse_settings_dialog.cpp @@ -190,6 +190,26 @@ void basic_mouse_settings_dialog::on_button_click(int id) m_remap_timer.start(1000); } +void basic_mouse_settings_dialog::keyPressEvent(QKeyEvent* event) +{ + if (m_button_id < 0) + { + // We are not remapping a button, so pass the event to the base class. + QDialog::keyPressEvent(event); + return; + } + + const std::string name = keyboard_pad_handler::GetKeyName(event, false); + g_cfg_mouse.get_button(m_button_id).from_string(name); + + if (auto button = m_buttons->button(m_button_id)) + { + button->setText(QString::fromStdString(name)); + } + + reactivate_buttons(); +} + void basic_mouse_settings_dialog::mouseReleaseEvent(QMouseEvent* event) { if (m_button_id < 0) diff --git a/rpcs3/rpcs3qt/basic_mouse_settings_dialog.h b/rpcs3/rpcs3qt/basic_mouse_settings_dialog.h index ab7dd48450..c432143185 100644 --- a/rpcs3/rpcs3qt/basic_mouse_settings_dialog.h +++ b/rpcs3/rpcs3qt/basic_mouse_settings_dialog.h @@ -39,6 +39,7 @@ private: QTimer m_remap_timer; protected: + void keyPressEvent(QKeyEvent* event) override; void mouseReleaseEvent(QMouseEvent* event) override; bool eventFilter(QObject* object, QEvent* event) override; }; From a0df1e09a655f980e10a6a636b5916686b97cdcf Mon Sep 17 00:00:00 2001 From: Megamouse Date: Thu, 9 Jan 2025 21:10:28 +0100 Subject: [PATCH 35/60] ps move: allow to configure mouse move handler buttons --- rpcs3/Emu/Cell/Modules/cellGem.cpp | 134 +++++++++++++----- rpcs3/Emu/Io/Buzz.cpp | 2 +- rpcs3/Emu/Io/GHLtar.cpp | 2 +- rpcs3/Emu/Io/GunCon3.cpp | 2 +- rpcs3/Emu/Io/TopShotElite.cpp | 2 +- rpcs3/Emu/Io/TopShotFearmaster.cpp | 2 +- rpcs3/Emu/Io/Turntable.cpp | 2 +- rpcs3/Emu/Io/emulated_pad_config.h | 34 +++-- rpcs3/Emu/Io/gem_config.h | 43 +++++- rpcs3/Emu/Io/usio.cpp | 4 +- .../rpcs3qt/emulated_pad_settings_dialog.cpp | 98 +++++++++++-- rpcs3/rpcs3qt/emulated_pad_settings_dialog.h | 1 + rpcs3/rpcs3qt/main_window.cpp | 6 + rpcs3/rpcs3qt/main_window.ui | 6 + 14 files changed, 273 insertions(+), 65 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellGem.cpp b/rpcs3/Emu/Cell/Modules/cellGem.cpp index 48d609120f..22988d25e6 100644 --- a/rpcs3/Emu/Cell/Modules/cellGem.cpp +++ b/rpcs3/Emu/Cell/Modules/cellGem.cpp @@ -40,9 +40,18 @@ void fmt_class_string::format(std::string& out, u64 arg) case gem_btn::square: return "Square"; case gem_btn::move: return "Move"; case gem_btn::t: return "T"; - case gem_btn::count: return "Count"; case gem_btn::x_axis: return "X-Axis"; case gem_btn::y_axis: return "Y-Axis"; + case gem_btn::combo: return "Combo"; + case gem_btn::combo_start: return "Combo Start"; + case gem_btn::combo_select: return "Combo Select"; + case gem_btn::combo_triangle: return "Combo Triangle"; + case gem_btn::combo_circle: return "Combo Circle"; + case gem_btn::combo_cross: return "Combo Cross"; + case gem_btn::combo_square: return "Combo Square"; + case gem_btn::combo_move: return "Combo Move"; + case gem_btn::combo_t: return "Combo T"; + case gem_btn::count: return "Count"; } return unknown; @@ -177,6 +186,7 @@ using gun_thread = named_thread; cfg_gems g_cfg_gem_real; cfg_fake_gems g_cfg_gem_fake; +cfg_mouse_gems g_cfg_gem_mouse; struct gem_config_data { @@ -494,8 +504,14 @@ public: cellGem.notice("Could not load fake gem config. Using defaults."); } + if (!g_cfg_gem_mouse.load()) + { + cellGem.notice("Could not load mouse gem config. Using defaults."); + } + cellGem.notice("Real gem config=\n", g_cfg_gem_real.to_string()); cellGem.notice("Fake gem config=\n", g_cfg_gem_fake.to_string()); + cellGem.notice("Mouse gem config=\n", g_cfg_gem_mouse.to_string()); } }; @@ -1574,7 +1590,7 @@ static void ds3_input_to_pad(const u32 gem_num, be_t& digital_buttons, be_t return; } - const auto handle_input = [&](gem_btn btn, u16 value, bool pressed) + const auto handle_input = [&](gem_btn btn, pad_button /*pad_btn*/, u16 value, bool pressed, bool& /*abort*/) { if (!pressed) return; @@ -1606,9 +1622,7 @@ static void ds3_input_to_pad(const u32 gem_num, be_t& digital_buttons, be_t digital_buttons |= CELL_GEM_CTRL_T; analog_t = std::max(analog_t, value); break; - case gem_btn::x_axis: - case gem_btn::y_axis: - case gem_btn::count: + default: break; } }; @@ -1632,7 +1646,7 @@ static inline void ds3_get_stick_values(u32 gem_num, const std::shared_ptr& y_pos = 0; const auto& cfg = ::at32(g_cfg_gem_fake.players, gem_num); - cfg->handle_input(pad, true, [&](gem_btn btn, u16 value, bool pressed) + cfg->handle_input(pad, true, [&](gem_btn btn, pad_button /*pad_btn*/, u16 value, bool pressed, bool& /*abort*/) { if (!pressed) return; @@ -1828,41 +1842,97 @@ static bool mouse_input_to_pad(u32 mouse_no, be_t& digital_buttons, be_t pressed_buttons; const Mouse& mouse_data = ::at32(handler.GetMice(), mouse_no); - const auto is_pressed = [&mouse_data, &pressed_buttons](MouseButtonCodes button) -> bool + auto& cfg = ::at32(g_cfg_gem_mouse.players, mouse_no); + + bool combo_active = false; + std::set combos; + + static const std::unordered_map btn_map = { - // Only allow each button to be used for one action unless it's the combo button. - return (mouse_data.buttons & button) && (button == (CELL_MOUSE_BUTTON_3 + 0u/*fix warning*/) || pressed_buttons.insert(button).second); + { gem_btn::start, CELL_GEM_CTRL_START }, + { gem_btn::select, CELL_GEM_CTRL_SELECT }, + { gem_btn::triangle, CELL_GEM_CTRL_TRIANGLE }, + { gem_btn::circle, CELL_GEM_CTRL_CIRCLE }, + { gem_btn::cross, CELL_GEM_CTRL_CROSS }, + { gem_btn::square, CELL_GEM_CTRL_SQUARE }, + { gem_btn::move, CELL_GEM_CTRL_MOVE }, + { gem_btn::t, CELL_GEM_CTRL_T }, + { gem_btn::combo_start, CELL_GEM_CTRL_START }, + { gem_btn::combo_select, CELL_GEM_CTRL_SELECT }, + { gem_btn::combo_triangle, CELL_GEM_CTRL_TRIANGLE }, + { gem_btn::combo_circle, CELL_GEM_CTRL_CIRCLE }, + { gem_btn::combo_cross, CELL_GEM_CTRL_CROSS }, + { gem_btn::combo_square, CELL_GEM_CTRL_SQUARE }, + { gem_btn::combo_move, CELL_GEM_CTRL_MOVE }, + { gem_btn::combo_t, CELL_GEM_CTRL_T }, }; - digital_buttons = 0; + // Check combo button first + cfg->handle_input(mouse_data, [&combo_active](gem_btn btn, pad_button /*pad_btn*/, u16 /*value*/, bool pressed, bool& abort) + { + if (pressed && btn == gem_btn::combo) + { + combo_active = true; + abort = true; + } + }); - if ((is_pressed(CELL_MOUSE_BUTTON_3) && is_pressed(CELL_MOUSE_BUTTON_1)) || is_pressed(CELL_MOUSE_BUTTON_6)) - digital_buttons |= CELL_GEM_CTRL_SELECT; + // Check combos + if (combo_active) + { + cfg->handle_input(mouse_data, [&digital_buttons, &combos](gem_btn btn, pad_button pad_btn, u16 /*value*/, bool pressed, bool& /*abort*/) + { + if (!pressed) + return; - if ((is_pressed(CELL_MOUSE_BUTTON_3) && is_pressed(CELL_MOUSE_BUTTON_2)) || is_pressed(CELL_MOUSE_BUTTON_7)) - digital_buttons |= CELL_GEM_CTRL_START; + switch (btn) + { + case gem_btn::combo_start: + case gem_btn::combo_select: + case gem_btn::combo_triangle: + case gem_btn::combo_circle: + case gem_btn::combo_cross: + case gem_btn::combo_square: + case gem_btn::combo_move: + case gem_btn::combo_t: + digital_buttons |= ::at32(btn_map, btn); + combos.insert(pad_btn); + break; + default: + break; + } + }); + } - if ((is_pressed(CELL_MOUSE_BUTTON_3) && is_pressed(CELL_MOUSE_BUTTON_4)) || is_pressed(CELL_MOUSE_BUTTON_8)) - digital_buttons |= CELL_GEM_CTRL_TRIANGLE; + // Check normal buttons + cfg->handle_input(mouse_data, [&digital_buttons, &combos](gem_btn btn, pad_button pad_btn, u16 /*value*/, bool pressed, bool& /*abort*/) + { + if (!pressed) + return; - if (is_pressed(CELL_MOUSE_BUTTON_3) && is_pressed(CELL_MOUSE_BUTTON_5)) - digital_buttons |= CELL_GEM_CTRL_SQUARE; + switch (btn) + { + case gem_btn::start: + case gem_btn::select: + case gem_btn::square: + case gem_btn::cross: + case gem_btn::circle: + case gem_btn::triangle: + case gem_btn::move: + case gem_btn::t: + // Ignore this gem_btn if the same pad_button was already used in a combo + if (!combos.contains(pad_btn)) + { + digital_buttons |= ::at32(btn_map, btn); + } + break; + default: + break; + } + }); - if (is_pressed(CELL_MOUSE_BUTTON_1)) - digital_buttons |= CELL_GEM_CTRL_T; - - if (is_pressed(CELL_MOUSE_BUTTON_2)) - digital_buttons |= CELL_GEM_CTRL_MOVE; - - if (is_pressed(CELL_MOUSE_BUTTON_4)) - digital_buttons |= CELL_GEM_CTRL_CIRCLE; - - if (is_pressed(CELL_MOUSE_BUTTON_5)) - digital_buttons |= CELL_GEM_CTRL_CROSS; - - analog_t = (mouse_data.buttons & CELL_MOUSE_BUTTON_1) ? 0xFFFF : 0; + analog_t = (digital_buttons & CELL_GEM_CTRL_T) ? 0xFFFF : 0; return true; } diff --git a/rpcs3/Emu/Io/Buzz.cpp b/rpcs3/Emu/Io/Buzz.cpp index 06cdc2f064..108bdec0ab 100644 --- a/rpcs3/Emu/Io/Buzz.cpp +++ b/rpcs3/Emu/Io/Buzz.cpp @@ -161,7 +161,7 @@ void usb_device_buzz::interrupt_transfer(u32 buf_size, u8* buf, u32 /*endpoint*/ } const auto& cfg = g_cfg_buzz.players[i]; - cfg->handle_input(pad, true, [&buf, &index](buzz_btn btn, u16 /*value*/, bool pressed) + cfg->handle_input(pad, true, [&buf, &index](buzz_btn btn, pad_button /*pad_btn*/, u16 /*value*/, bool pressed, bool& /*abort*/) { if (!pressed) return; diff --git a/rpcs3/Emu/Io/GHLtar.cpp b/rpcs3/Emu/Io/GHLtar.cpp index 140c8e3a98..d6fe87e9fc 100644 --- a/rpcs3/Emu/Io/GHLtar.cpp +++ b/rpcs3/Emu/Io/GHLtar.cpp @@ -147,7 +147,7 @@ void usb_device_ghltar::interrupt_transfer(u32 buf_size, u8* buf, u32 /*endpoint } const auto& cfg = ::at32(g_cfg_ghltar.players, m_controller_index); - cfg->handle_input(pad, true, [&buf](ghltar_btn btn, u16 value, bool pressed) + cfg->handle_input(pad, true, [&buf](ghltar_btn btn, pad_button /*pad_btn*/, u16 value, bool pressed, bool& /*abort*/) { if (!pressed) return; diff --git a/rpcs3/Emu/Io/GunCon3.cpp b/rpcs3/Emu/Io/GunCon3.cpp index ee9694447c..7eef591ecc 100644 --- a/rpcs3/Emu/Io/GunCon3.cpp +++ b/rpcs3/Emu/Io/GunCon3.cpp @@ -227,7 +227,7 @@ void usb_device_guncon3::interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, return; } - const auto input_callback = [&gc](guncon3_btn btn, u16 value, bool pressed) + const auto input_callback = [&gc](guncon3_btn btn, pad_button /*pad_button*/, u16 value, bool pressed, bool& /*abort*/) { if (!pressed) return; diff --git a/rpcs3/Emu/Io/TopShotElite.cpp b/rpcs3/Emu/Io/TopShotElite.cpp index 0d85cfd5fa..b170027a9f 100644 --- a/rpcs3/Emu/Io/TopShotElite.cpp +++ b/rpcs3/Emu/Io/TopShotElite.cpp @@ -280,7 +280,7 @@ void usb_device_topshotelite::interrupt_transfer(u32 buf_size, u8* buf, u32 /*en } bool up = false, right = false, down = false, left = false; - const auto input_callback = [&ts, &up, &down, &left, &right](topshotelite_btn btn, u16 value, bool pressed) + const auto input_callback = [&ts, &up, &down, &left, &right](topshotelite_btn btn, pad_button /*pad_button*/, u16 value, bool pressed, bool& /*abort*/) { if (!pressed) return; diff --git a/rpcs3/Emu/Io/TopShotFearmaster.cpp b/rpcs3/Emu/Io/TopShotFearmaster.cpp index bfae945365..aba831fa9c 100644 --- a/rpcs3/Emu/Io/TopShotFearmaster.cpp +++ b/rpcs3/Emu/Io/TopShotFearmaster.cpp @@ -308,7 +308,7 @@ void usb_device_topshotfearmaster::interrupt_transfer(u32 buf_size, u8* buf, u32 } bool up = false, right = false, down = false, left = false; - const auto input_callback = [&ts, &up, &down, &left, &right](topshotfearmaster_btn btn, u16 value, bool pressed) + const auto input_callback = [&ts, &up, &down, &left, &right](topshotfearmaster_btn btn, pad_button /*pad_button*/, u16 value, bool pressed, bool& /*abort*/) { if (!pressed) return; diff --git a/rpcs3/Emu/Io/Turntable.cpp b/rpcs3/Emu/Io/Turntable.cpp index af5648d40b..63bb815a1d 100644 --- a/rpcs3/Emu/Io/Turntable.cpp +++ b/rpcs3/Emu/Io/Turntable.cpp @@ -159,7 +159,7 @@ void usb_device_turntable::interrupt_transfer(u32 buf_size, u8* buf, u32 /*endpo return; const auto& cfg = ::at32(g_cfg_turntable.players, m_controller_index); - cfg->handle_input(pad, true, [&buf](turntable_btn btn, u16 value, bool pressed) + cfg->handle_input(pad, true, [&buf](turntable_btn btn, pad_button /*pad_btn*/, u16 value, bool pressed, bool& /*abort*/) { if (!pressed) return; diff --git a/rpcs3/Emu/Io/emulated_pad_config.h b/rpcs3/Emu/Io/emulated_pad_config.h index b6703b01dd..3b0887459c 100644 --- a/rpcs3/Emu/Io/emulated_pad_config.h +++ b/rpcs3/Emu/Io/emulated_pad_config.h @@ -88,7 +88,7 @@ public: button_map.clear(); } - void handle_input(std::shared_ptr pad, bool press_only, const std::function& func) const + void handle_input(std::shared_ptr pad, bool press_only, const std::function& func) const { if (!pad) return; @@ -97,19 +97,25 @@ public: { if (button.m_pressed || !press_only) { - handle_input(func, button.m_offset, button.m_outKeyCode, button.m_value, button.m_pressed, true); + if (handle_input(func, button.m_offset, button.m_outKeyCode, button.m_value, button.m_pressed, true)) + { + return; + } } } for (const AnalogStick& stick : pad->m_sticks) { - handle_input(func, stick.m_offset, get_axis_keycode(stick.m_offset, stick.m_value), stick.m_value, true, true); + if (handle_input(func, stick.m_offset, get_axis_keycode(stick.m_offset, stick.m_value), stick.m_value, true, true)) + { + return; + } } } - void handle_input(const Mouse& mouse, const std::function& func) const + void handle_input(const Mouse& mouse, const std::function& func) const { - for (int i = 0; i < 7; i++) + for (int i = 0; i < 8; i++) { const MouseButtonCodes cell_code = get_mouse_button_code(i); if ((mouse.buttons & cell_code)) @@ -117,7 +123,11 @@ public: const pad_button button = static_cast(static_cast(pad_button::mouse_button_1) + i); const u32 offset = pad_button_offset(button); const u32 keycode = pad_button_keycode(button); - handle_input(func, offset, keycode, 255, true, true); + + if (handle_input(func, offset, keycode, 255, true, true)) + { + return; + } } } } @@ -163,10 +173,12 @@ protected: return empty_set; } - void handle_input(const std::function& func, u32 offset, u32 keycode, u16 value, bool pressed, bool check_axis) const + bool handle_input(const std::function& func, u32 offset, u32 keycode, u16 value, bool pressed, bool check_axis) const { m_mutex.lock(); + bool abort = false; + const auto& btns = find_button(offset, keycode); if (btns.empty()) { @@ -180,24 +192,26 @@ protected: case CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y: case CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X: case CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y: - handle_input(func, offset, static_cast(axis_direction::both), value, pressed, false); + abort = handle_input(func, offset, static_cast(axis_direction::both), value, pressed, false); break; default: break; } } - return; + return abort; } for (const auto& btn : btns) { if (btn && func) { - func(btn->btn_id(), value, pressed); + func(btn->btn_id(), btn->get(), value, pressed, abort); + if (abort) break; } } m_mutex.unlock(); + return abort; } }; diff --git a/rpcs3/Emu/Io/gem_config.h b/rpcs3/Emu/Io/gem_config.h index d737928790..2be86ff3c4 100644 --- a/rpcs3/Emu/Io/gem_config.h +++ b/rpcs3/Emu/Io/gem_config.h @@ -4,7 +4,7 @@ #include -enum class gem_btn +enum class gem_btn : u32 { start, select, @@ -17,6 +17,18 @@ enum class gem_btn x_axis, y_axis, + combo_begin, + combo = combo_begin, + combo_start, + combo_select, + combo_triangle, + combo_circle, + combo_cross, + combo_square, + combo_move, + combo_t, + combo_end = combo_t, + count }; @@ -41,6 +53,34 @@ struct cfg_fake_gems final : public emulated_pads_config cfg_fake_gems() : emulated_pads_config("gem") {}; }; +struct cfg_mouse_gem final : public emulated_pad_config +{ + cfg_mouse_gem(node* owner, const std::string& name) : emulated_pad_config(owner, name) {} + + cfg_pad_btn start{ this, "Start", gem_btn::start, pad_button::mouse_button_6 }; + cfg_pad_btn select{ this, "Select", gem_btn::select, pad_button::mouse_button_7 }; + cfg_pad_btn triangle{ this, "Triangle", gem_btn::triangle, pad_button::mouse_button_8 }; + cfg_pad_btn circle{ this, "Circle", gem_btn::circle, pad_button::mouse_button_4 }; + cfg_pad_btn cross{ this, "Cross", gem_btn::cross, pad_button::mouse_button_5 }; + cfg_pad_btn square{ this, "Square", gem_btn::square, pad_button::mouse_button_3 }; + cfg_pad_btn move{ this, "Move", gem_btn::move, pad_button::mouse_button_2 }; + cfg_pad_btn t{ this, "T", gem_btn::t, pad_button::mouse_button_1 }; + cfg_pad_btn combo{ this, "Combo", gem_btn::combo, pad_button::pad_button_max_enum }; + cfg_pad_btn combo_start{ this, "Combo Start", gem_btn::combo_start, pad_button::pad_button_max_enum }; + cfg_pad_btn combo_select{ this, "Combo Select", gem_btn::combo_select, pad_button::pad_button_max_enum }; + cfg_pad_btn combo_triangle{ this, "Combo Triangle", gem_btn::combo_triangle, pad_button::pad_button_max_enum }; + cfg_pad_btn combo_circle{ this, "Combo Circle", gem_btn::combo_circle, pad_button::pad_button_max_enum }; + cfg_pad_btn combo_cross{ this, "Combo Cross", gem_btn::combo_cross, pad_button::pad_button_max_enum }; + cfg_pad_btn combo_square{ this, "Combo Square", gem_btn::combo_square, pad_button::pad_button_max_enum }; + cfg_pad_btn combo_move{ this, "Combo Move", gem_btn::combo_move, pad_button::pad_button_max_enum }; + cfg_pad_btn combo_t{ this, "Combo T", gem_btn::combo_t, pad_button::pad_button_max_enum }; +}; + +struct cfg_mouse_gems final : public emulated_pads_config +{ + cfg_mouse_gems() : emulated_pads_config("gem_mouse") {}; +}; + struct cfg_gem final : public emulated_pad_config { cfg_gem(node* owner, const std::string& name) : emulated_pad_config(owner, name) {} @@ -62,3 +102,4 @@ struct cfg_gems final : public emulated_pads_config extern cfg_gems g_cfg_gem_real; extern cfg_fake_gems g_cfg_gem_fake; +extern cfg_mouse_gems g_cfg_gem_mouse; diff --git a/rpcs3/Emu/Io/usio.cpp b/rpcs3/Emu/Io/usio.cpp index 7dea19c8fb..faecec177d 100644 --- a/rpcs3/Emu/Io/usio.cpp +++ b/rpcs3/Emu/Io/usio.cpp @@ -203,7 +203,7 @@ void usb_device_usio::translate_input_taiko() if (const auto& pad = ::at32(handler->GetPads(), pad_number); (pad->m_port_status & CELL_PAD_STATUS_CONNECTED) && is_input_allowed()) { const auto& cfg = ::at32(g_cfg_usio.players, pad_number); - cfg->handle_input(pad, false, [&](usio_btn btn, u16 /*value*/, bool pressed) + cfg->handle_input(pad, false, [&](usio_btn btn, pad_button /*pad_btn*/, u16 /*value*/, bool pressed, bool& /*abort*/) { switch (btn) { @@ -288,7 +288,7 @@ void usb_device_usio::translate_input_tekken() if (const auto& pad = ::at32(handler->GetPads(), pad_number); (pad->m_port_status & CELL_PAD_STATUS_CONNECTED) && is_input_allowed()) { const auto& cfg = ::at32(g_cfg_usio.players, pad_number); - cfg->handle_input(pad, false, [&](usio_btn btn, u16 /*value*/, bool pressed) + cfg->handle_input(pad, false, [&](usio_btn btn, pad_button /*pad_btn*/, u16 /*value*/, bool pressed, bool& /*abort*/) { switch (btn) { diff --git a/rpcs3/rpcs3qt/emulated_pad_settings_dialog.cpp b/rpcs3/rpcs3qt/emulated_pad_settings_dialog.cpp index 835f940797..a3fcde1e82 100644 --- a/rpcs3/rpcs3qt/emulated_pad_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/emulated_pad_settings_dialog.cpp @@ -92,6 +92,10 @@ emulated_pad_settings_dialog::emulated_pad_settings_dialog(pad_type type, QWidge setWindowTitle(tr("Configure Emulated PS Move (Fake)")); add_tabs(tabs); break; + case emulated_pad_settings_dialog::pad_type::mousegem: + setWindowTitle(tr("Configure Emulated PS Move (Mouse)")); + add_tabs(tabs); + break; case emulated_pad_settings_dialog::pad_type::guncon3: setWindowTitle(tr("Configure Emulated GunCon 3")); add_tabs(tabs); @@ -116,8 +120,12 @@ void emulated_pad_settings_dialog::add_tabs(QTabWidget* tabs) { ensure(!!tabs); - constexpr u32 max_items_per_column = 6; - int count = static_cast(T::count); + std::set ignored_values; + + const auto remove_value = [&ignored_values](int value) + { + ignored_values.insert(static_cast(value)); + }; usz players = 0; switch (m_type) @@ -137,13 +145,29 @@ void emulated_pad_settings_dialog::add_tabs(QTabWidget* tabs) case pad_type::gem: players = g_cfg_gem_real.players.size(); - // Ignore x and y axis - static_assert(static_cast(gem_btn::y_axis) == static_cast(gem_btn::count) - 1); - static_assert(static_cast(gem_btn::x_axis) == static_cast(gem_btn::count) - 2); - count -= 2; + // Ignore combo, x and y axis + remove_value(static_cast(gem_btn::x_axis)); + remove_value(static_cast(gem_btn::y_axis)); + for (int i = static_cast(gem_btn::combo_begin); i <= static_cast(gem_btn::combo_end); i++) + { + remove_value(i); + } break; case pad_type::ds3gem: players = g_cfg_gem_fake.players.size(); + + // Ignore combo + for (int i = static_cast(gem_btn::combo_begin); i <= static_cast(gem_btn::combo_end); i++) + { + remove_value(i); + } + break; + case pad_type::mousegem: + players = g_cfg_gem_mouse.players.size(); + + // Ignore x and y axis + remove_value(static_cast(gem_btn::x_axis)); + remove_value(static_cast(gem_btn::y_axis)); break; case pad_type::guncon3: players = g_cfg_guncon3.players.size(); @@ -156,6 +180,8 @@ void emulated_pad_settings_dialog::add_tabs(QTabWidget* tabs) break; } + constexpr u32 max_items_per_column = 6; + const int count = static_cast(T::count) - static_cast(ignored_values.size()); int rows = count; for (u32 cols = 1; utils::aligned_div(static_cast(count), cols) > max_items_per_column;) @@ -170,8 +196,10 @@ void emulated_pad_settings_dialog::add_tabs(QTabWidget* tabs) QWidget* widget = new QWidget(this); QGridLayout* grid_layout = new QGridLayout(this); - for (int i = 0, row = 0, col = 0; i < count; i++, row++) + for (int i = 0, row = 0, col = 0; i < static_cast(T::count); i++) { + if (ignored_values.contains(i)) continue; + const T id = static_cast(i); const QString name = QString::fromStdString(fmt::format("%s", id)); @@ -179,16 +207,35 @@ void emulated_pad_settings_dialog::add_tabs(QTabWidget* tabs) QGroupBox* gb = new QGroupBox(name, this); QComboBox* combo = new QComboBox; - for (int p = 0; p < static_cast(pad_button::pad_button_max_enum); p++) + if constexpr (std::is_same_v) { - const QString translated = localized_emu::translated_pad_button(static_cast(p)); - combo->addItem(translated); - const int index = combo->findText(translated); - combo->setItemData(index, p, button_role::button); - combo->setItemData(index, i, button_role::emulated_button); + const gem_btn btn = static_cast(i); + if (btn >= gem_btn::combo_begin && btn <= gem_btn::combo_end) + { + gb->setToolTip(tr("Press the \"Combo\" button in combination with any of the other combo buttons to trigger their related PS Move button.\n" + "This can be useful if your device does not have enough regular buttons.")); + } } - if constexpr (std::is_same_v || std::is_same_v || std::is_same_v) + // Add empty value + combo->addItem(""); + const int index = combo->findText(""); + combo->setItemData(index, static_cast(pad_button::pad_button_max_enum), button_role::button); + combo->setItemData(index, i, button_role::emulated_button); + + if (m_type != pad_type::mousegem) + { + for (int p = 0; p < static_cast(pad_button::pad_button_max_enum); p++) + { + const QString translated = localized_emu::translated_pad_button(static_cast(p)); + combo->addItem(translated); + const int index = combo->findText(translated); + combo->setItemData(index, p, button_role::button); + combo->setItemData(index, i, button_role::emulated_button); + } + } + + if (std::is_same_v || std::is_same_v || std::is_same_v || m_type == pad_type::mousegem) { for (int p = static_cast(pad_button::mouse_button_1); p <= static_cast(pad_button::mouse_button_8); p++) { @@ -221,6 +268,9 @@ void emulated_pad_settings_dialog::add_tabs(QTabWidget* tabs) case pad_type::ds3gem: saved_btn_id = ::at32(g_cfg_gem_fake.players, player)->get_pad_button(static_cast(id)); break; + case pad_type::mousegem: + saved_btn_id = ::at32(g_cfg_gem_mouse.players, player)->get_pad_button(static_cast(id)); + break; case pad_type::guncon3: saved_btn_id = ::at32(g_cfg_guncon3.players, player)->get_pad_button(static_cast(id)); break; @@ -265,6 +315,9 @@ void emulated_pad_settings_dialog::add_tabs(QTabWidget* tabs) case pad_type::ds3gem: ::at32(g_cfg_gem_fake.players, player)->set_button(static_cast(id), btn_id); break; + case pad_type::mousegem: + ::at32(g_cfg_gem_mouse.players, player)->set_button(static_cast(id), btn_id); + break; case pad_type::guncon3: ::at32(g_cfg_guncon3.players, player)->set_button(static_cast(id), btn_id); break; @@ -287,6 +340,8 @@ void emulated_pad_settings_dialog::add_tabs(QTabWidget* tabs) h_layout->addWidget(combo); gb->setLayout(h_layout); grid_layout->addWidget(gb, row, col); + + row++; } widget->setLayout(grid_layout); @@ -334,6 +389,12 @@ void emulated_pad_settings_dialog::load_config() cfg_log.notice("Could not load fake gem config. Using defaults."); } break; + case emulated_pad_settings_dialog::pad_type::mousegem: + if (!g_cfg_gem_mouse.load()) + { + cfg_log.notice("Could not load mouse gem config. Using defaults."); + } + break; case emulated_pad_settings_dialog::pad_type::guncon3: if (!g_cfg_guncon3.load()) { @@ -377,6 +438,9 @@ void emulated_pad_settings_dialog::save_config() case emulated_pad_settings_dialog::pad_type::ds3gem: g_cfg_gem_fake.save(); break; + case emulated_pad_settings_dialog::pad_type::mousegem: + g_cfg_gem_mouse.save(); + break; case emulated_pad_settings_dialog::pad_type::guncon3: g_cfg_guncon3.save(); break; @@ -411,6 +475,9 @@ void emulated_pad_settings_dialog::reset_config() case emulated_pad_settings_dialog::pad_type::ds3gem: g_cfg_gem_fake.from_default(); break; + case emulated_pad_settings_dialog::pad_type::mousegem: + g_cfg_gem_mouse.from_default(); + break; case emulated_pad_settings_dialog::pad_type::guncon3: g_cfg_guncon3.from_default(); break; @@ -454,6 +521,9 @@ void emulated_pad_settings_dialog::reset_config() case pad_type::ds3gem: def_btn_id = ::at32(g_cfg_gem_fake.players, player)->default_pad_button(static_cast(data.toInt())); break; + case pad_type::mousegem: + def_btn_id = ::at32(g_cfg_gem_mouse.players, player)->default_pad_button(static_cast(data.toInt())); + break; case pad_type::guncon3: def_btn_id = ::at32(g_cfg_guncon3.players, player)->default_pad_button(static_cast(data.toInt())); break; diff --git a/rpcs3/rpcs3qt/emulated_pad_settings_dialog.h b/rpcs3/rpcs3qt/emulated_pad_settings_dialog.h index 772f467182..ad1b863d7e 100644 --- a/rpcs3/rpcs3qt/emulated_pad_settings_dialog.h +++ b/rpcs3/rpcs3qt/emulated_pad_settings_dialog.h @@ -21,6 +21,7 @@ public: usio, gem, ds3gem, + mousegem, guncon3, topshotelite, topshotfearmaster, diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index 46e056e43a..b4bc2af5d6 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -2911,6 +2911,12 @@ void main_window::CreateConnects() dlg->show(); }); + connect(ui->confPSMoveMouseAct, &QAction::triggered, this, [this] + { + emulated_pad_settings_dialog* dlg = new emulated_pad_settings_dialog(emulated_pad_settings_dialog::pad_type::mousegem, this); + dlg->show(); + }); + connect(ui->confPSMoveDS3Act, &QAction::triggered, this, [this] { emulated_pad_settings_dialog* dlg = new emulated_pad_settings_dialog(emulated_pad_settings_dialog::pad_type::ds3gem, this); diff --git a/rpcs3/rpcs3qt/main_window.ui b/rpcs3/rpcs3qt/main_window.ui index ba29696974..adb66d8bcf 100644 --- a/rpcs3/rpcs3qt/main_window.ui +++ b/rpcs3/rpcs3qt/main_window.ui @@ -244,6 +244,7 @@ + @@ -1333,6 +1334,11 @@ PS Move (Fake)
+ + + PS Move (Mouse) + + GunCon 3 From b8d0396f71719dbe52a88aa6faf030d0a5bf41d8 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Thu, 9 Jan 2025 22:00:31 +0100 Subject: [PATCH 36/60] Fix self assignment warning --- rpcs3/Emu/RSX/Overlays/Trophies/overlay_trophy_list_dialog.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/rpcs3/Emu/RSX/Overlays/Trophies/overlay_trophy_list_dialog.cpp b/rpcs3/Emu/RSX/Overlays/Trophies/overlay_trophy_list_dialog.cpp index 8213b29a2d..5d66ae87d0 100644 --- a/rpcs3/Emu/RSX/Overlays/Trophies/overlay_trophy_list_dialog.cpp +++ b/rpcs3/Emu/RSX/Overlays/Trophies/overlay_trophy_list_dialog.cpp @@ -265,7 +265,6 @@ namespace rsx fade_animation.end = color4f(1.f); fade_animation.active = true; - this->on_close = std::move(on_close); visible = true; const auto notify = std::make_shared>(0); From d6f2f66f2b38a4d068cff2877243337dc4f9e01c Mon Sep 17 00:00:00 2001 From: Megamouse Date: Thu, 9 Jan 2025 22:33:44 +0100 Subject: [PATCH 37/60] Qt/Input: use empty string for pad_button_max_enum Also shorten translated mouse button strings --- rpcs3/Emu/Io/pad_types.cpp | 2 +- rpcs3/rpcs3qt/localized_emu.cpp | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/rpcs3/Emu/Io/pad_types.cpp b/rpcs3/Emu/Io/pad_types.cpp index 46d1f284d7..428e0d6cea 100644 --- a/rpcs3/Emu/Io/pad_types.cpp +++ b/rpcs3/Emu/Io/pad_types.cpp @@ -39,7 +39,7 @@ void fmt_class_string::format(std::string& out, u64 arg) case pad_button::rs_right: return "Right Stick Right"; case pad_button::rs_x: return "Right Stick X-Axis"; case pad_button::rs_y: return "Right Stick Y-Axis"; - case pad_button::pad_button_max_enum: return "MAX_ENUM"; + case pad_button::pad_button_max_enum: return ""; case pad_button::mouse_button_1: return "Mouse Button 1"; case pad_button::mouse_button_2: return "Mouse Button 2"; case pad_button::mouse_button_3: return "Mouse Button 3"; diff --git a/rpcs3/rpcs3qt/localized_emu.cpp b/rpcs3/rpcs3qt/localized_emu.cpp index 4fb2b4b9f9..bfc0b52101 100644 --- a/rpcs3/rpcs3qt/localized_emu.cpp +++ b/rpcs3/rpcs3qt/localized_emu.cpp @@ -36,14 +36,14 @@ QString localized_emu::translated_pad_button(pad_button btn) case pad_button::rs_x: return tr("Right Stick X-Axis"); case pad_button::rs_y: return tr("Right Stick Y-Axis"); case pad_button::pad_button_max_enum: return ""; - case pad_button::mouse_button_1: return tr("Mouse Button 1"); - case pad_button::mouse_button_2: return tr("Mouse Button 2"); - case pad_button::mouse_button_3: return tr("Mouse Button 3"); - case pad_button::mouse_button_4: return tr("Mouse Button 4"); - case pad_button::mouse_button_5: return tr("Mouse Button 5"); - case pad_button::mouse_button_6: return tr("Mouse Button 6"); - case pad_button::mouse_button_7: return tr("Mouse Button 7"); - case pad_button::mouse_button_8: return tr("Mouse Button 8"); + case pad_button::mouse_button_1: return tr("Mouse 1"); + case pad_button::mouse_button_2: return tr("Mouse 2"); + case pad_button::mouse_button_3: return tr("Mouse 3"); + case pad_button::mouse_button_4: return tr("Mouse 4"); + case pad_button::mouse_button_5: return tr("Mouse 5"); + case pad_button::mouse_button_6: return tr("Mouse 6"); + case pad_button::mouse_button_7: return tr("Mouse 7"); + case pad_button::mouse_button_8: return tr("Mouse 8"); } return ""; } From 061be74cdb07ed4fdef8b7e649d76b5024661de5 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Thu, 9 Jan 2025 23:32:27 +0100 Subject: [PATCH 38/60] Qt: add mouse config legend to mouse move dialog --- rpcs3/Input/raw_mouse_config.cpp | 2 + rpcs3/Input/raw_mouse_config.h | 2 - .../rpcs3qt/emulated_pad_settings_dialog.cpp | 71 ++++++++++++++++++- rpcs3/rpcs3qt/raw_mouse_settings_dialog.cpp | 2 + 4 files changed, 73 insertions(+), 4 deletions(-) diff --git a/rpcs3/Input/raw_mouse_config.cpp b/rpcs3/Input/raw_mouse_config.cpp index b02f349dac..0d896fc1d8 100644 --- a/rpcs3/Input/raw_mouse_config.cpp +++ b/rpcs3/Input/raw_mouse_config.cpp @@ -2,6 +2,8 @@ #include "raw_mouse_config.h" #include "Emu/Io/MouseHandler.h" +LOG_CHANNEL(cfg_log, "CFG"); + std::string mouse_button_id(int code) { switch (code) diff --git a/rpcs3/Input/raw_mouse_config.h b/rpcs3/Input/raw_mouse_config.h index b1ae83883d..902e0ed42f 100644 --- a/rpcs3/Input/raw_mouse_config.h +++ b/rpcs3/Input/raw_mouse_config.h @@ -5,8 +5,6 @@ #include -LOG_CHANNEL(cfg_log, "CFG"); - std::string mouse_button_id(int code); struct raw_mouse_config : cfg::node diff --git a/rpcs3/rpcs3qt/emulated_pad_settings_dialog.cpp b/rpcs3/rpcs3qt/emulated_pad_settings_dialog.cpp index a3fcde1e82..00520a8c7c 100644 --- a/rpcs3/rpcs3qt/emulated_pad_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/emulated_pad_settings_dialog.cpp @@ -1,6 +1,8 @@ #include "stdafx.h" #include "emulated_pad_settings_dialog.h" #include "localized_emu.h" +#include "Input/raw_mouse_config.h" +#include "Emu/Io/mouse_config.h" #include "Emu/Io/buzz_config.h" #include "Emu/Io/gem_config.h" #include "Emu/Io/ghltar_config.h" @@ -13,6 +15,7 @@ #include #include +#include #include #include #include @@ -191,9 +194,24 @@ void emulated_pad_settings_dialog::add_tabs(QTabWidget* tabs) m_combos.resize(players); + const bool show_mouse_legend = m_type == pad_type::mousegem; + + if (show_mouse_legend) + { + if (!g_cfg_mouse.load()) + { + cfg_log.notice("Could not restore mouse config. Using defaults."); + } + + if (!g_cfg_raw_mouse.load()) + { + cfg_log.notice("Could not restore raw mouse config. Using defaults."); + } + } + for (usz player = 0; player < players; player++) { - QWidget* widget = new QWidget(this); + // Create grid with all buttons QGridLayout* grid_layout = new QGridLayout(this); for (int i = 0, row = 0, col = 0; i < static_cast(T::count); i++) @@ -344,7 +362,56 @@ void emulated_pad_settings_dialog::add_tabs(QTabWidget* tabs) row++; } - widget->setLayout(grid_layout); + QVBoxLayout* v_layout = new QVBoxLayout(this); + + // Create a legend of the current mouse settings + if (show_mouse_legend) + { + QHBoxLayout* legend_layout = new QHBoxLayout(this); + if (player == 0) + { + std::string basic_mouse_settings; + fmt::append(basic_mouse_settings, "1: %s\n", g_cfg_mouse.mouse_button_1.to_string()); + fmt::append(basic_mouse_settings, "2: %s\n", g_cfg_mouse.mouse_button_2.to_string()); + fmt::append(basic_mouse_settings, "3: %s\n", g_cfg_mouse.mouse_button_3.to_string()); + fmt::append(basic_mouse_settings, "4: %s\n", g_cfg_mouse.mouse_button_4.to_string()); + fmt::append(basic_mouse_settings, "5: %s\n", g_cfg_mouse.mouse_button_5.to_string()); + fmt::append(basic_mouse_settings, "6: %s\n", g_cfg_mouse.mouse_button_6.to_string()); + fmt::append(basic_mouse_settings, "7: %s\n", g_cfg_mouse.mouse_button_7.to_string()); + fmt::append(basic_mouse_settings, "8: %s", g_cfg_mouse.mouse_button_8.to_string()); + + QGroupBox* gb_legend_basic = new QGroupBox(tr("Current Basic Mouse Config"), this); + QVBoxLayout* gb_legend_basic_layout = new QVBoxLayout(this); + gb_legend_basic_layout->addWidget(new QLabel(QString::fromStdString(basic_mouse_settings), this)); + gb_legend_basic->setLayout(gb_legend_basic_layout); + legend_layout->addWidget(gb_legend_basic); + } + { + std::string raw_mouse_settings; + const auto& raw_cfg = *ensure(::at32(g_cfg_raw_mouse.players, player)); + fmt::append(raw_mouse_settings, "1: %s\n", raw_cfg.mouse_button_1.to_string()); + fmt::append(raw_mouse_settings, "2: %s\n", raw_cfg.mouse_button_2.to_string()); + fmt::append(raw_mouse_settings, "3: %s\n", raw_cfg.mouse_button_3.to_string()); + fmt::append(raw_mouse_settings, "4: %s\n", raw_cfg.mouse_button_4.to_string()); + fmt::append(raw_mouse_settings, "5: %s\n", raw_cfg.mouse_button_5.to_string()); + fmt::append(raw_mouse_settings, "6: %s\n", raw_cfg.mouse_button_6.to_string()); + fmt::append(raw_mouse_settings, "7: %s\n", raw_cfg.mouse_button_7.to_string()); + fmt::append(raw_mouse_settings, "8: %s", raw_cfg.mouse_button_8.to_string()); + + QGroupBox* gb_legend_raw = new QGroupBox(tr("Current Raw Mouse Config"), this); + QVBoxLayout* gb_legend_raw_layout = new QVBoxLayout(this); + gb_legend_raw_layout->addWidget(new QLabel(QString::fromStdString(raw_mouse_settings), this)); + gb_legend_raw->setLayout(gb_legend_raw_layout); + legend_layout->addWidget(gb_legend_raw); + } + v_layout->addLayout(legend_layout); + } + + v_layout->addLayout(grid_layout); + + QWidget* widget = new QWidget(this); + widget->setLayout(v_layout); + tabs->addTab(widget, tr("Player %0").arg(player + 1)); } } diff --git a/rpcs3/rpcs3qt/raw_mouse_settings_dialog.cpp b/rpcs3/rpcs3qt/raw_mouse_settings_dialog.cpp index 3cfccb9755..31151e098e 100644 --- a/rpcs3/rpcs3qt/raw_mouse_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/raw_mouse_settings_dialog.cpp @@ -11,6 +11,8 @@ #include #include +LOG_CHANNEL(cfg_log, "CFG"); + constexpr u32 button_count = 8; raw_mouse_settings_dialog::raw_mouse_settings_dialog(QWidget* parent) From 88e13d8326e7c13ee8ccddcfd840ca174f59b996 Mon Sep 17 00:00:00 2001 From: kd-11 Date: Sat, 4 Jan 2025 02:00:14 +0300 Subject: [PATCH 39/60] rsx: Don't crash when invalid buffer is allocated for a shader --- rpcs3/Emu/RSX/Program/ProgramStateCache.h | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/rpcs3/Emu/RSX/Program/ProgramStateCache.h b/rpcs3/Emu/RSX/Program/ProgramStateCache.h index f80f9eeeec..6a2c53ae93 100644 --- a/rpcs3/Emu/RSX/Program/ProgramStateCache.h +++ b/rpcs3/Emu/RSX/Program/ProgramStateCache.h @@ -293,10 +293,10 @@ public: bool compile_async, bool allow_notification, Args&& ...args - ) + ) { - const auto &vp_search = search_vertex_program(vertexShader); - const auto &fp_search = search_fragment_program(fragmentShader); + const auto& vp_search = search_vertex_program(vertexShader); + const auto& fp_search = search_fragment_program(fragmentShader); const bool already_existing_fragment_program = std::get<1>(fp_search); const bool already_existing_vertex_program = std::get<1>(vp_search); @@ -385,7 +385,13 @@ public: void fill_fragment_constants_buffer(std::span dst_buffer, const fragment_program_type& fragment_program, const RSXFragmentProgram& rsx_prog, bool sanitize = false) const { - ensure((dst_buffer.size_bytes() >= ::narrow(fragment_program.FragmentConstantOffsetCache.size()) * 16u)); + if (dst_buffer.size_bytes() < (fragment_program.FragmentConstantOffsetCache.size() * 16)) + { + // This can happen if CELL alters the shader after it has been loaded by RSX. + rsx_log.error("Insufficient constants buffer size passed to fragment program! Corrupt shader?"); + return; + } + rsx::write_fragment_constants_to_buffer(dst_buffer, rsx_prog, fragment_program.FragmentConstantOffsetCache, sanitize); } From dce0abc8b9690eb1290a6829091214b2777ec6de Mon Sep 17 00:00:00 2001 From: kd-11 Date: Sat, 4 Jan 2025 01:57:03 +0300 Subject: [PATCH 40/60] rsx/fp: Re-design register write tracking - Always collapse register writes when exporting FS outputs --- rpcs3/Emu/CMakeLists.txt | 1 + .../RSX/Program/FragmentProgramDecompiler.cpp | 82 ++++++-- .../RSX/Program/FragmentProgramDecompiler.h | 112 +--------- .../RSX/Program/FragmentProgramRegister.cpp | 196 ++++++++++++++++++ .../Emu/RSX/Program/FragmentProgramRegister.h | 111 ++++++++++ rpcs3/Emu/RSX/Program/ProgramStateCache.cpp | 12 +- rpcs3/Emu/RSX/Program/ShaderParam.h | 8 + rpcs3/emucore.vcxproj | 2 + rpcs3/emucore.vcxproj.filters | 6 + 9 files changed, 400 insertions(+), 130 deletions(-) create mode 100644 rpcs3/Emu/RSX/Program/FragmentProgramRegister.cpp create mode 100644 rpcs3/Emu/RSX/Program/FragmentProgramRegister.h diff --git a/rpcs3/Emu/CMakeLists.txt b/rpcs3/Emu/CMakeLists.txt index 04ed28e10f..97016dbb04 100644 --- a/rpcs3/Emu/CMakeLists.txt +++ b/rpcs3/Emu/CMakeLists.txt @@ -558,6 +558,7 @@ target_sources(rpcs3_emu PRIVATE RSX/Program/CgBinaryFragmentProgram.cpp RSX/Program/CgBinaryVertexProgram.cpp RSX/Program/FragmentProgramDecompiler.cpp + RSX/Program/FragmentProgramRegister.cpp RSX/Program/GLSLCommon.cpp RSX/Program/ProgramStateCache.cpp RSX/Program/program_util.cpp diff --git a/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.cpp b/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.cpp index b3f9b88df2..4de2100ff2 100644 --- a/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.cpp +++ b/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.cpp @@ -22,6 +22,15 @@ namespace rsx using namespace rsx::fragment_program; +// SIMD vector lanes +enum VectorLane : u8 +{ + X = 0, + Y = 1, + Z = 2, + W = 3, +}; + FragmentProgramDecompiler::FragmentProgramDecompiler(const RSXFragmentProgram &prog, u32& size) : m_size(size) , m_prog(prog) @@ -141,8 +150,7 @@ void FragmentProgramDecompiler::SetDst(std::string code, u32 flags) AddCode(m_parr.AddParam(PF_PARAM_NONE, getFloatTypeName(4), "cc" + std::to_string(src0.cond_mod_reg_index)) + "$m = " + dest + ";"); } - u32 reg_index = dst.fp16 ? dst.dest_reg >> 1 : dst.dest_reg; - + const u32 reg_index = dst.fp16 ? (dst.dest_reg >> 1) : dst.dest_reg; ensure(reg_index < temp_registers.size()); if (dst.opcode == RSX_FP_OPCODE_MOV && @@ -754,14 +762,26 @@ std::string FragmentProgramDecompiler::BuildCode() const std::string init_value = float4_type + "(0.)"; std::array output_register_names; std::array ouput_register_indices = { 0, 2, 3, 4 }; - bool shader_is_valid = false; + + // Holder for any "cleanup" before exiting main + std::stringstream main_epilogue; // Check depth export if (m_ctrl & CELL_GCM_SHADER_CONTROL_DEPTH_EXPORT) { // Hw tests show that the depth export register is default-initialized to 0 and not wpos.z!! m_parr.AddParam(PF_PARAM_NONE, getFloatTypeName(4), "r1", init_value); - shader_is_valid = (!!temp_registers[1].h1_writes); + + auto& r1 = temp_registers[1]; + if (r1.requires_gather(VectorLane::Z)) + { + // r1.zw was not written to + properties.has_gather_op = true; + main_epilogue << " r1.z = " << float4_type << r1.gather_r() << ".z;\n"; + + // Emit debug warning. Useful to diagnose regressions, but should be removed in future. + rsx_log.warning("ROP reads from shader depth without writing to it. Final value will be gathered."); + } } // Add the color output registers. They are statically written to and have guaranteed initialization (except r1.z which == wpos.z) @@ -777,26 +797,41 @@ std::string FragmentProgramDecompiler::BuildCode() for (int n = 0; n < 4; ++n) { - if (!m_parr.HasParam(PF_PARAM_NONE, float4_type, output_register_names[n])) + const auto& reg_name = output_register_names[n]; + if (!m_parr.HasParam(PF_PARAM_NONE, float4_type, reg_name)) { - m_parr.AddParam(PF_PARAM_NONE, float4_type, output_register_names[n], init_value); - continue; + m_parr.AddParam(PF_PARAM_NONE, float4_type, reg_name, init_value); } const auto block_index = ouput_register_indices[n]; - shader_is_valid |= (!!temp_registers[block_index].h0_writes); - } + auto& r = temp_registers[block_index]; - if (!shader_is_valid) - { - properties.has_no_output = true; - - if (!properties.has_discard_op) + if (fp16_out) { - // NOTE: Discard operation overrides output - rsx_log.warning("Shader does not write to any output register and will be NOPed"); - main = "/*" + main + "*/"; + // Check if we need a split/extract op + if (r.requires_split(0)) + { + main_epilogue << " " << reg_name << " = " << float4_type << r.split_h0() << ";\n"; + + // Emit debug warning. Useful to diagnose regressions, but should be removed in future. + rsx_log.warning("ROP reads from %s without writing to it. Final value will be extracted from the 32-bit register.", reg_name); + } + + continue; } + + if (!r.requires_gather128()) + { + // Nothing to do + continue; + } + + // We need to gather the data from existing registers + main_epilogue << " " << reg_name << " = " << float4_type << r.gather_r() << ";\n"; + properties.has_gather_op = true; + + // Emit debug warning. Useful to diagnose regressions, but should be removed in future. + rsx_log.warning("ROP reads from %s without writing to it. Final value will be gathered.", reg_name); } if (properties.has_dynamic_register_load) @@ -822,6 +857,9 @@ std::string FragmentProgramDecompiler::BuildCode() OS << "#endif\n"; OS << " discard;\n"; OS << "}\n"; + + // Don't consume any args + m_parr.Clear(); return OS.str(); } @@ -1019,6 +1057,12 @@ std::string FragmentProgramDecompiler::BuildCode() insertMainStart(OS); OS << main << std::endl; + + if (const auto epilogue = main_epilogue.str(); !epilogue.empty()) + { + OS << " // Epilogue\n"; + OS << epilogue << std::endl; + } insertMainEnd(OS); return OS.str(); @@ -1360,12 +1404,12 @@ std::string FragmentProgramDecompiler::Decompile() switch (opcode) { - case RSX_FP_OPCODE_NOP: break; + case RSX_FP_OPCODE_NOP: + break; case RSX_FP_OPCODE_KIL: properties.has_discard_op = true; AddFlowOp("_kill()"); break; - default: int prev_force_unit = forced_unit; diff --git a/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.h b/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.h index df3fbe5e16..dab539f9da 100644 --- a/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.h +++ b/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.h @@ -1,116 +1,10 @@ #pragma once #include "ShaderParam.h" +#include "FragmentProgramRegister.h" #include "RSXFragmentProgram.h" + #include -// Helper for GPR occupancy tracking -struct temp_register -{ - bool aliased_r0 = false; - bool aliased_h0 = false; - bool aliased_h1 = false; - bool last_write_half[4] = { false, false, false, false }; - - u32 real_index = -1; - - u32 h0_writes = 0u; // Number of writes to the first 64-bits of the register - u32 h1_writes = 0u; // Number of writes to the last 64-bits of the register - - void tag(u32 index, bool half_register, bool x, bool y, bool z, bool w) - { - if (half_register) - { - if (index & 1) - { - if (x) last_write_half[2] = true; - if (y) last_write_half[2] = true; - if (z) last_write_half[3] = true; - if (w) last_write_half[3] = true; - - aliased_h1 = true; - h1_writes++; - } - else - { - if (x) last_write_half[0] = true; - if (y) last_write_half[0] = true; - if (z) last_write_half[1] = true; - if (w) last_write_half[1] = true; - - aliased_h0 = true; - h0_writes++; - } - } - else - { - if (x) last_write_half[0] = false; - if (y) last_write_half[1] = false; - if (z) last_write_half[2] = false; - if (w) last_write_half[3] = false; - - aliased_r0 = true; - - h0_writes++; - h1_writes++; - } - - if (real_index == umax) - { - if (half_register) - real_index = index >> 1; - else - real_index = index; - } - } - - bool requires_gather(u8 channel) const - { - //Data fetched from the single precision register requires merging of the two half registers - ensure(channel < 4); - if (aliased_h0 && channel < 2) - { - return last_write_half[channel]; - } - - if (aliased_h1 && channel > 1) - { - return last_write_half[channel]; - } - - return false; - } - - bool requires_split(u32 /*index*/) const - { - //Data fetched from any of the two half registers requires sync with the full register - if (!(last_write_half[0] || last_write_half[1]) && aliased_r0) - { - //r0 has been written to - //TODO: Check for specific elements in real32 register - return true; - } - - return false; - } - - std::string gather_r() const - { - std::string h0 = "h" + std::to_string(real_index << 1); - std::string h1 = "h" + std::to_string(real_index << 1 | 1); - std::string reg = "r" + std::to_string(real_index); - std::string ret = "//Invalid gather"; - - if (aliased_h0 && aliased_h1) - ret = "(gather(" + h0 + ", " + h1 + "))"; - else if (aliased_h0) - ret = "(gather(" + h0 + "), " + reg + ".zw)"; - else if (aliased_h1) - ret = "(" + reg + ".xy, gather(" + h1 + "))"; - - return ret; - } -}; - /** * This class is used to translate RSX Fragment program to GLSL/HLSL code * Backend with text based shader can subclass this class and implement : @@ -157,7 +51,7 @@ class FragmentProgramDecompiler bool m_is_valid_ucode = true; - std::array temp_registers; + std::array temp_registers; std::string GetMask() const; diff --git a/rpcs3/Emu/RSX/Program/FragmentProgramRegister.cpp b/rpcs3/Emu/RSX/Program/FragmentProgramRegister.cpp new file mode 100644 index 0000000000..a14b142df6 --- /dev/null +++ b/rpcs3/Emu/RSX/Program/FragmentProgramRegister.cpp @@ -0,0 +1,196 @@ +#include "stdafx.h" +#include "FragmentProgramRegister.h" + +namespace rsx +{ + MixedPrecisionRegister::MixedPrecisionRegister() + { + std::fill(content_mask.begin(), content_mask.end(), data_type_bits::undefined); + } + + void MixedPrecisionRegister::tag_h0(bool x, bool y, bool z, bool w) + { + if (x) content_mask[0] = data_type_bits::f16; + if (y) content_mask[1] = data_type_bits::f16; + if (z) content_mask[2] = data_type_bits::f16; + if (w) content_mask[3] = data_type_bits::f16; + } + + void MixedPrecisionRegister::tag_h1(bool x, bool y, bool z, bool w) + { + if (x) content_mask[4] = data_type_bits::f16; + if (y) content_mask[5] = data_type_bits::f16; + if (z) content_mask[6] = data_type_bits::f16; + if (w) content_mask[7] = data_type_bits::f16; + } + + void MixedPrecisionRegister::tag_r(bool x, bool y, bool z, bool w) + { + if (x) content_mask[0] = content_mask[1] = data_type_bits::f32; + if (y) content_mask[2] = content_mask[3] = data_type_bits::f32; + if (z) content_mask[4] = content_mask[5] = data_type_bits::f32; + if (w) content_mask[6] = content_mask[7] = data_type_bits::f32; + } + + void MixedPrecisionRegister::tag(u32 index, bool is_fp16, bool x, bool y, bool z, bool w) + { + if (file_index == umax) + { + // First-time use. Initialize... + const u32 real_index = is_fp16 ? (index >> 1) : index; + file_index = real_index; + } + + if (is_fp16) + { + ensure((index / 2) == file_index); + + if (index & 1) + { + tag_h1(x, y, z, w); + return; + } + + tag_h0(x, y, z, w); + return; + } + + tag_r(x, y, z, w); + } + + std::string MixedPrecisionRegister::gather_r() const + { + const auto half_index = file_index << 1; + const std::string reg = "r" + std::to_string(file_index); + const std::string gather_half_regs[] = { + "gather(h" + std::to_string(half_index) + ")", + "gather(h" + std::to_string(half_index + 1) + ")" + }; + + std::string outputs[4]; + for (int ch = 0; ch < 4; ++ch) + { + // FIXME: This approach ignores mixed register bits. Not ideal!!!! + const auto channel0 = content_mask[ch * 2]; + const auto is_fp16_ch = channel0 == content_mask[ch * 2 + 1] && channel0 == data_type_bits::f16; + outputs[ch] = is_fp16_ch ? gather_half_regs[ch / 2] : reg; + } + + // Grouping. Only replace relevant bits... + if (outputs[0] == outputs[1]) outputs[0] = ""; + if (outputs[2] == outputs[3]) outputs[2] = ""; + + // Assemble + bool group = false; + std::string result = ""; + constexpr std::string_view swz_mask = "xyzw"; + + for (int ch = 0; ch < 4; ++ch) + { + if (outputs[ch].empty()) + { + group = true; + continue; + } + + if (!result.empty()) + { + result += ", "; + } + + if (group) + { + ensure(ch > 0); + group = false; + + if (outputs[ch] == reg) + { + result += reg + "." + swz_mask[ch - 1] + swz_mask[ch]; + continue; + } + + result += outputs[ch]; + continue; + } + + const int subch = outputs[ch] == reg ? ch : (ch % 2); // Avoid .xyxy.z and other such ugly swizzles + result += outputs[ch] + "." + swz_mask[subch]; + } + + // Optimize dual-gather (128-bit gather) to use special function + const std::string double_gather = gather_half_regs[0] + ", " + gather_half_regs[1]; + if (result == double_gather) + { + result = "gather(h" + std::to_string(half_index) + ", h" + std::to_string(half_index + 1) + ")"; + } + + return "(" + result + ")"; + } + + std::string MixedPrecisionRegister::fetch_halfreg(u32 word_index) const + { + // Reads half-word 0 (H16x4) from a full real (R32x4) register + constexpr std::string_view swz_mask = "xyzw"; + const std::string reg = "r" + std::to_string(file_index); + const std::string hreg = "h" + std::to_string(file_index * 2 + word_index); + + const std::string word0_bits = "floatBitsToUint(" + reg + "." + swz_mask[word_index * 2] + ")"; + const std::string word1_bits = "floatBitsToUint(" + reg + "." + swz_mask[word_index * 2 + 1] + ")"; + const std::string words[] = { + "unpackHalf2x16(" + word0_bits + ")", + "unpackHalf2x16(" + word1_bits + ")" + }; + + // Assemble + std::string outputs[4]; + + ensure(word_index <= 1); + const int word_offset = word_index * 4; + for (int ch = 0; ch < 4; ++ch) + { + outputs[ch] = content_mask[ch + word_offset] == data_type_bits::f32 + ? words[ch / 2] + : hreg; + } + + // Grouping. Only replace relevant bits... + if (outputs[0] == outputs[1]) outputs[0] = ""; + if (outputs[2] == outputs[3]) outputs[2] = ""; + + // Assemble + bool group = false; + std::string result = ""; + + for (int ch = 0; ch < 4; ++ch) + { + if (outputs[ch].empty()) + { + group = true; + continue; + } + + if (!result.empty()) + { + result += ", "; + } + + if (group) + { + ensure(ch > 0); + group = false; + result += outputs[ch]; + + if (outputs[ch] == hreg) + { + result += std::string(".") + swz_mask[ch - 1] + swz_mask[ch]; + } + continue; + } + + const int subch = outputs[ch] == hreg ? ch : (ch % 2); // Avoid .xyxy.z and other such ugly swizzles + result += outputs[ch] + "." + swz_mask[subch]; + } + + return "(" + result + ")"; + } +} diff --git a/rpcs3/Emu/RSX/Program/FragmentProgramRegister.h b/rpcs3/Emu/RSX/Program/FragmentProgramRegister.h new file mode 100644 index 0000000000..6cfc8e76c3 --- /dev/null +++ b/rpcs3/Emu/RSX/Program/FragmentProgramRegister.h @@ -0,0 +1,111 @@ +#pragma once + +#include + +namespace rsx +{ + class MixedPrecisionRegister + { + enum data_type_bits + { + undefined = 0, + f16 = 1, + f32 = 2 + }; + + std::array content_mask; // Content details for each half-word + u32 file_index = umax; + + void tag_h0(bool x, bool y, bool z, bool w); + + void tag_h1(bool x, bool y, bool z, bool w); + + void tag_r(bool x, bool y, bool z, bool w); + + std::string fetch_halfreg(u32 word_index) const; + + public: + MixedPrecisionRegister(); + + void tag(u32 index, bool is_fp16, bool x, bool y, bool z, bool w); + + std::string gather_r() const; + + std::string split_h0() const + { + return fetch_halfreg(0); + } + + std::string split_h1() const + { + return fetch_halfreg(1); + } + + // Getters + + // Return true if all values are unwritten to (undefined) + bool floating() const + { + return file_index == umax; + } + + // Return true if the first half register is all undefined + bool floating_h0() const + { + return content_mask[0] == content_mask[1] && + content_mask[1] == content_mask[2] && + content_mask[2] == content_mask[3] && + content_mask[3] == data_type_bits::undefined; + } + + // Return true if the second half register is all undefined + bool floating_h1() const + { + return content_mask[4] == content_mask[5] && + content_mask[5] == content_mask[6] && + content_mask[6] == content_mask[7] && + content_mask[7] == data_type_bits::undefined; + } + + // Return true if any of the half-words are 16-bit + bool requires_gather(u8 channel) const + { + // Data fetched from the single precision register requires merging of the two half registers + const auto channel_offset = channel * 2; + ensure(channel_offset <= 6); + + return (content_mask[channel_offset] == data_type_bits::f16 || content_mask[channel_offset + 1] == data_type_bits::f16); + } + + // Return true if the entire 128-bit register is filled with 2xfp16x4 data words + bool requires_gather128() const + { + // Full 128-bit check + for (const auto& ch : content_mask) + { + if (ch == data_type_bits::f16) + { + return true; + } + } + + return false; + } + + // Return true if the half-register is polluted with fp32 data + bool requires_split(u32 word_index) const + { + const u32 content_offset = word_index * 4; + for (u32 i = 0; i < 4; ++i) + { + if (content_mask[content_offset + i] == data_type_bits::f32) + { + return true; + } + } + + return false; + } + }; +} + diff --git a/rpcs3/Emu/RSX/Program/ProgramStateCache.cpp b/rpcs3/Emu/RSX/Program/ProgramStateCache.cpp index 2f7330f8fa..8bc851ec28 100644 --- a/rpcs3/Emu/RSX/Program/ProgramStateCache.cpp +++ b/rpcs3/Emu/RSX/Program/ProgramStateCache.cpp @@ -555,10 +555,14 @@ usz fragment_program_storage_hash::operator()(const RSXFragmentProgram& program) bool fragment_program_compare::operator()(const RSXFragmentProgram& binary1, const RSXFragmentProgram& binary2) const { - if (binary1.ctrl != binary2.ctrl || binary1.texture_state != binary2.texture_state || + if (binary1.ucode_length != binary2.ucode_length || + binary1.ctrl != binary2.ctrl || + binary1.texture_state != binary2.texture_state || binary1.texcoord_control_mask != binary2.texcoord_control_mask || binary1.two_sided_lighting != binary2.two_sided_lighting) + { return false; + } const void* instBuffer1 = binary1.get_data(); const void* instBuffer2 = binary2.get_data(); @@ -569,7 +573,9 @@ bool fragment_program_compare::operator()(const RSXFragmentProgram& binary1, con const auto inst2 = v128::loadu(instBuffer2, instIndex); if (inst1._u ^ inst2._u) + { return false; + } instIndex++; // Skip constants @@ -578,9 +584,11 @@ bool fragment_program_compare::operator()(const RSXFragmentProgram& binary1, con fragment_program_utils::is_constant(inst1._u32[3])) instIndex++; - bool end = ((inst1._u32[0] >> 8) & 0x1) && ((inst2._u32[0] >> 8) & 0x1); + const bool end = ((inst1._u32[0] >> 8) & 0x1) && ((inst2._u32[0] >> 8) & 0x1); if (end) + { return true; + } } } diff --git a/rpcs3/Emu/RSX/Program/ShaderParam.h b/rpcs3/Emu/RSX/Program/ShaderParam.h index 3308ba6d9f..01e4931869 100644 --- a/rpcs3/Emu/RSX/Program/ShaderParam.h +++ b/rpcs3/Emu/RSX/Program/ShaderParam.h @@ -227,6 +227,14 @@ struct ParamArray return name; } + + void Clear() + { + for (auto& param : params) + { + param.clear(); + } + } }; class ShaderVariable diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index 6834ed5530..bbb69c30b2 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -140,6 +140,7 @@ + @@ -669,6 +670,7 @@ + diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index f36b57bb03..23c7c34fb6 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -1330,6 +1330,9 @@ Emu\Cell + + Emu\GPU\RSX\Program +
@@ -2686,6 +2689,9 @@ Emu\Audio + + Emu\GPU\RSX\Program + From a635e24fc258b94bf1f0311054d54a955e9a5034 Mon Sep 17 00:00:00 2001 From: kd-11 Date: Wed, 8 Jan 2025 14:26:19 +0300 Subject: [PATCH 41/60] rsx: Default initialize vertex program fields - Fixes a crash when RSXVertexProgram is default-initialized. This probably also fixes a whole class of crashes that occur when shader interpreter is in use since that pipeline uses a default-initialized stub. --- rpcs3/Emu/RSX/Program/RSXVertexProgram.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rpcs3/Emu/RSX/Program/RSXVertexProgram.h b/rpcs3/Emu/RSX/Program/RSXVertexProgram.h index 6a25906e35..1277250b56 100644 --- a/rpcs3/Emu/RSX/Program/RSXVertexProgram.h +++ b/rpcs3/Emu/RSX/Program/RSXVertexProgram.h @@ -223,10 +223,10 @@ struct RSXVertexProgram { std::vector data; rsx::vertex_program_texture_state texture_state; - u32 ctrl; - u32 output_mask; - u32 base_address; - u32 entry; + u32 ctrl = 0; + u32 output_mask = 0; + u32 base_address = 0; + u32 entry = 0; std::bitset instruction_mask; std::set jump_table; From 27c56cde2241b0056df7433a8fbb040bdb95dc07 Mon Sep 17 00:00:00 2001 From: kd-11 Date: Thu, 9 Jan 2025 03:22:12 +0300 Subject: [PATCH 42/60] rsx/shaders: Track active MRT count per shader - Also use more robust hashing to avoid collisions --- rpcs3/Emu/RSX/Common/surface_store.cpp | 16 ++++++- rpcs3/Emu/RSX/Common/surface_store.h | 1 + rpcs3/Emu/RSX/GL/GLGSRender.cpp | 7 ++- rpcs3/Emu/RSX/GL/GLGSRender.h | 3 ++ rpcs3/Emu/RSX/GL/GLShaderInterpreter.cpp | 2 +- rpcs3/Emu/RSX/GL/GLShaderInterpreter.h | 2 +- .../RSX/Program/FragmentProgramDecompiler.cpp | 8 +++- rpcs3/Emu/RSX/Program/ProgramStateCache.cpp | 43 +++++++++++-------- rpcs3/Emu/RSX/Program/RSXFragmentProgram.h | 4 +- rpcs3/Emu/RSX/RSXThread.cpp | 17 ++++++-- rpcs3/Emu/RSX/RSXThread.h | 2 + rpcs3/Emu/RSX/VK/VKGSRender.cpp | 7 ++- rpcs3/Emu/RSX/VK/VKGSRender.h | 3 ++ rpcs3/Emu/RSX/rsx_cache.h | 38 +++++++++------- rpcs3/util/fnv_hash.hpp | 25 +++++++++++ 15 files changed, 135 insertions(+), 43 deletions(-) diff --git a/rpcs3/Emu/RSX/Common/surface_store.cpp b/rpcs3/Emu/RSX/Common/surface_store.cpp index 53981ac6db..43f4b4161f 100644 --- a/rpcs3/Emu/RSX/Common/surface_store.cpp +++ b/rpcs3/Emu/RSX/Common/surface_store.cpp @@ -18,7 +18,21 @@ namespace rsx case surface_target::surfaces_a_b_c: return{ 0, 1, 2 }; case surface_target::surfaces_a_b_c_d: return{ 0, 1, 2, 3 }; } - fmt::throw_exception("Wrong color_target"); + fmt::throw_exception("Invalid color target %d", static_cast(color_target)); + } + + u8 get_mrt_buffers_count(surface_target color_target) + { + switch (color_target) + { + case surface_target::none: return 0; + case surface_target::surface_a: return 1; + case surface_target::surface_b: return 1; + case surface_target::surfaces_a_b: return 2; + case surface_target::surfaces_a_b_c: return 3; + case surface_target::surfaces_a_b_c_d: return 4; + } + fmt::throw_exception("Invalid color target %d", static_cast(color_target)); } usz get_aligned_pitch(surface_color_format format, u32 width) diff --git a/rpcs3/Emu/RSX/Common/surface_store.h b/rpcs3/Emu/RSX/Common/surface_store.h index e4062707d0..e4d3f2d3fb 100644 --- a/rpcs3/Emu/RSX/Common/surface_store.h +++ b/rpcs3/Emu/RSX/Common/surface_store.h @@ -17,6 +17,7 @@ namespace rsx namespace utility { std::vector get_rtt_indexes(surface_target color_target); + u8 get_mrt_buffers_count(surface_target color_target); usz get_aligned_pitch(surface_color_format format, u32 width); usz get_packed_pitch(surface_color_format format, u32 width); } diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.cpp b/rpcs3/Emu/RSX/GL/GLGSRender.cpp index a2d080c86e..90bc7cacbd 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.cpp +++ b/rpcs3/Emu/RSX/GL/GLGSRender.cpp @@ -39,7 +39,7 @@ u64 GLGSRender::get_cycles() GLGSRender::GLGSRender(utils::serial* ar) noexcept : GSRender(ar) { - m_shaders_cache = std::make_unique(m_prog_buffer, "opengl", "v1.94"); + m_shaders_cache = std::make_unique(m_prog_buffer, "opengl", "v1.95"); if (g_cfg.video.disable_vertex_cache) m_vertex_cache = std::make_unique(); @@ -986,6 +986,11 @@ void GLGSRender::load_program_env() rsx::pipeline_state::fragment_texture_state_dirty); } +bool GLGSRender::is_current_program_interpreted() const +{ + return m_program && m_shader_interpreter.is_interpreter(m_program); +} + void GLGSRender::upload_transform_constants(const rsx::io_buffer& buffer) { const usz transform_constants_size = (!m_vertex_prog || m_vertex_prog->has_indexed_constants) ? 8192 : m_vertex_prog->constant_ids.size() * 16; diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.h b/rpcs3/Emu/RSX/GL/GLGSRender.h index c8c6ba89dd..01579a445b 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.h +++ b/rpcs3/Emu/RSX/GL/GLGSRender.h @@ -206,6 +206,9 @@ public: // GRAPH backend void patch_transform_constants(rsx::context* ctx, u32 index, u32 count) override; + // Misc + bool is_current_program_interpreted() const override; + protected: void clear_surface(u32 arg) override; void begin() override; diff --git a/rpcs3/Emu/RSX/GL/GLShaderInterpreter.cpp b/rpcs3/Emu/RSX/GL/GLShaderInterpreter.cpp index 1dfd0f4996..0687798efd 100644 --- a/rpcs3/Emu/RSX/GL/GLShaderInterpreter.cpp +++ b/rpcs3/Emu/RSX/GL/GLShaderInterpreter.cpp @@ -347,7 +347,7 @@ namespace gl return data; } - bool shader_interpreter::is_interpreter(const glsl::program* program) + bool shader_interpreter::is_interpreter(const glsl::program* program) const { return (program == &m_current_interpreter->prog); } diff --git a/rpcs3/Emu/RSX/GL/GLShaderInterpreter.h b/rpcs3/Emu/RSX/GL/GLShaderInterpreter.h index 6a46035742..2c15010192 100644 --- a/rpcs3/Emu/RSX/GL/GLShaderInterpreter.h +++ b/rpcs3/Emu/RSX/GL/GLShaderInterpreter.h @@ -84,6 +84,6 @@ namespace gl void update_fragment_textures(const std::array, 16>& descriptors, u16 reference_mask, u32* out); glsl::program* get(const interpreter::program_metadata& fp_metadata); - bool is_interpreter(const glsl::program* program); + bool is_interpreter(const glsl::program* program) const; }; } diff --git a/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.cpp b/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.cpp index 4de2100ff2..4a2215d9f9 100644 --- a/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.cpp +++ b/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.cpp @@ -795,7 +795,7 @@ std::string FragmentProgramDecompiler::BuildCode() output_register_names = { "h0", "h4", "h6", "h8" }; } - for (int n = 0; n < 4; ++n) + for (u32 n = 0; n < 4; ++n) { const auto& reg_name = output_register_names[n]; if (!m_parr.HasParam(PF_PARAM_NONE, float4_type, reg_name)) @@ -803,6 +803,12 @@ std::string FragmentProgramDecompiler::BuildCode() m_parr.AddParam(PF_PARAM_NONE, float4_type, reg_name, init_value); } + if (n >= m_prog.mrt_buffers_count) + { + // Skip gather + continue; + } + const auto block_index = ouput_register_indices[n]; auto& r = temp_registers[block_index]; diff --git a/rpcs3/Emu/RSX/Program/ProgramStateCache.cpp b/rpcs3/Emu/RSX/Program/ProgramStateCache.cpp index 8bc851ec28..6a1bcffc7a 100644 --- a/rpcs3/Emu/RSX/Program/ProgramStateCache.cpp +++ b/rpcs3/Emu/RSX/Program/ProgramStateCache.cpp @@ -340,12 +340,16 @@ vertex_program_utils::vertex_program_metadata vertex_program_utils::analyse_vert usz vertex_program_storage_hash::operator()(const RSXVertexProgram &program) const { - usz hash = vertex_program_utils::get_vertex_program_ucode_hash(program); - hash ^= program.ctrl; - hash ^= program.output_mask; - hash ^= program.texture_state.texture_dimensions; - hash ^= program.texture_state.multisampled_textures; - return hash; + const usz ucode_hash = vertex_program_utils::get_vertex_program_ucode_hash(program); + const u32 state_params[] = + { + program.ctrl, + program.output_mask, + program.texture_state.texture_dimensions, + program.texture_state.multisampled_textures, + }; + const usz metadata_hash = rpcs3::hash_array(state_params); + return rpcs3::hash64(ucode_hash, metadata_hash); } bool vertex_program_compare::operator()(const RSXVertexProgram &binary1, const RSXVertexProgram &binary2) const @@ -541,16 +545,20 @@ usz fragment_program_utils::get_fragment_program_ucode_hash(const RSXFragmentPro usz fragment_program_storage_hash::operator()(const RSXFragmentProgram& program) const { - usz hash = fragment_program_utils::get_fragment_program_ucode_hash(program); - hash ^= program.ctrl; - hash ^= +program.two_sided_lighting; - hash ^= program.texture_state.texture_dimensions; - hash ^= program.texture_state.shadow_textures; - hash ^= program.texture_state.redirected_textures; - hash ^= program.texture_state.multisampled_textures; - hash ^= program.texcoord_control_mask; - - return hash; + const usz ucode_hash = fragment_program_utils::get_fragment_program_ucode_hash(program); + const u32 state_params[] = + { + program.ctrl, + program.two_sided_lighting ? 1u : 0u, + program.texture_state.texture_dimensions, + program.texture_state.shadow_textures, + program.texture_state.redirected_textures, + program.texture_state.multisampled_textures, + program.texcoord_control_mask, + program.mrt_buffers_count + }; + const usz metadata_hash = rpcs3::hash_array(state_params); + return rpcs3::hash64(ucode_hash, metadata_hash); } bool fragment_program_compare::operator()(const RSXFragmentProgram& binary1, const RSXFragmentProgram& binary2) const @@ -559,7 +567,8 @@ bool fragment_program_compare::operator()(const RSXFragmentProgram& binary1, con binary1.ctrl != binary2.ctrl || binary1.texture_state != binary2.texture_state || binary1.texcoord_control_mask != binary2.texcoord_control_mask || - binary1.two_sided_lighting != binary2.two_sided_lighting) + binary1.two_sided_lighting != binary2.two_sided_lighting || + binary1.mrt_buffers_count != binary2.mrt_buffers_count) { return false; } diff --git a/rpcs3/Emu/RSX/Program/RSXFragmentProgram.h b/rpcs3/Emu/RSX/Program/RSXFragmentProgram.h index a9e95531aa..f834b7c7f5 100644 --- a/rpcs3/Emu/RSX/Program/RSXFragmentProgram.h +++ b/rpcs3/Emu/RSX/Program/RSXFragmentProgram.h @@ -300,8 +300,10 @@ struct RSXFragmentProgram u32 ucode_length = 0; u32 total_length = 0; u32 ctrl = 0; - bool two_sided_lighting = false; u32 texcoord_control_mask = 0; + u32 mrt_buffers_count = 0; + + bool two_sided_lighting = false; rsx::fragment_program_texture_state texture_state; rsx::fragment_program_texture_config texture_params; diff --git a/rpcs3/Emu/RSX/RSXThread.cpp b/rpcs3/Emu/RSX/RSXThread.cpp index 29c2d8e865..57ffc0e11f 100644 --- a/rpcs3/Emu/RSX/RSXThread.cpp +++ b/rpcs3/Emu/RSX/RSXThread.cpp @@ -1719,7 +1719,7 @@ namespace rsx for (uint i = 0; i < mrt_buffers.size(); ++i) { - if (rsx::method_registers.color_write_enabled(i)) + if (m_ctx->register_state->color_write_enabled(i)) { const auto real_index = mrt_buffers[i]; m_framebuffer_layout.color_write_enabled[real_index] = true; @@ -1727,6 +1727,14 @@ namespace rsx } } + if (::size32(mrt_buffers) != current_fragment_program.mrt_buffers_count && + !m_graphics_state.test(rsx::pipeline_state::fragment_program_dirty) && + !is_current_program_interpreted()) + { + // Notify that we should recompile the FS + m_graphics_state |= rsx::pipeline_state::fragment_program_state_dirty; + } + return any_found; }; @@ -2038,9 +2046,10 @@ namespace rsx m_graphics_state.clear(rsx::pipeline_state::fragment_program_dirty); - current_fragment_program.ctrl = rsx::method_registers.shader_control() & (CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS | CELL_GCM_SHADER_CONTROL_DEPTH_EXPORT); - current_fragment_program.texcoord_control_mask = rsx::method_registers.texcoord_control_mask(); - current_fragment_program.two_sided_lighting = rsx::method_registers.two_side_light_en(); + current_fragment_program.ctrl = m_ctx->register_state->shader_control() & (CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS | CELL_GCM_SHADER_CONTROL_DEPTH_EXPORT); + current_fragment_program.texcoord_control_mask = m_ctx->register_state->texcoord_control_mask(); + current_fragment_program.two_sided_lighting = m_ctx->register_state->two_side_light_en(); + current_fragment_program.mrt_buffers_count = rsx::utility::get_mrt_buffers_count(m_ctx->register_state->surface_color_target()); if (method_registers.current_draw_clause.classify_mode() == primitive_class::polygon) { diff --git a/rpcs3/Emu/RSX/RSXThread.h b/rpcs3/Emu/RSX/RSXThread.h index 4bcd08c9f4..257c70e4a5 100644 --- a/rpcs3/Emu/RSX/RSXThread.h +++ b/rpcs3/Emu/RSX/RSXThread.h @@ -436,6 +436,8 @@ namespace rsx bool is_current_vertex_program_instanced() const { return !!(current_vertex_program.ctrl & RSX_SHADER_CONTROL_INSTANCED_CONSTANTS); } + virtual bool is_current_program_interpreted() const { return false; } + public: void reset(); void init(u32 ctrlAddress); diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.cpp b/rpcs3/Emu/RSX/VK/VKGSRender.cpp index 51d0df3580..6e53891fbf 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.cpp +++ b/rpcs3/Emu/RSX/VK/VKGSRender.cpp @@ -730,7 +730,7 @@ VKGSRender::VKGSRender(utils::serial* ar) noexcept : GSRender(ar) else m_vertex_cache = std::make_unique(); - m_shaders_cache = std::make_unique(*m_prog_buffer, "vulkan", "v1.94"); + m_shaders_cache = std::make_unique(*m_prog_buffer, "vulkan", "v1.95"); for (u32 i = 0; i < m_swapchain->get_swap_image_count(); ++i) { @@ -2363,6 +2363,11 @@ void VKGSRender::load_program_env() m_graphics_state.clear(handled_flags); } +bool VKGSRender::is_current_program_interpreted() const +{ + return m_program && m_shader_interpreter.is_interpreter(m_program); +} + void VKGSRender::upload_transform_constants(const rsx::io_buffer& buffer) { const usz transform_constants_size = (!m_vertex_prog || m_vertex_prog->has_indexed_constants) ? 8192 : m_vertex_prog->constant_ids.size() * 16; diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.h b/rpcs3/Emu/RSX/VK/VKGSRender.h index 55c4b029bb..c245a2677f 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.h +++ b/rpcs3/Emu/RSX/VK/VKGSRender.h @@ -288,6 +288,9 @@ public: // GRAPH backend void patch_transform_constants(rsx::context* ctx, u32 index, u32 count) override; + // Misc + bool is_current_program_interpreted() const override; + protected: void clear_surface(u32 mask) override; void begin() override; diff --git a/rpcs3/Emu/RSX/rsx_cache.h b/rpcs3/Emu/RSX/rsx_cache.h index 843789d442..7f82c210d9 100644 --- a/rpcs3/Emu/RSX/rsx_cache.h +++ b/rpcs3/Emu/RSX/rsx_cache.h @@ -51,7 +51,10 @@ namespace rsx u16 fp_shadow_textures; u16 fp_redirected_textures; u16 fp_multisampled_textures; - u64 fp_reserved_0; + u8 fp_mrt_count; + u8 fp_reserved0; + u16 fp_reserved1; + u32 fp_reserved2; pipeline_storage_type pipeline_properties; }; @@ -306,20 +309,24 @@ namespace rsx fs::write_file(vp_name, fs::rewrite, vp.data); } - u64 state_hash = 0; - state_hash ^= rpcs3::hash_base(data.vp_ctrl0); - state_hash ^= rpcs3::hash_base(data.vp_ctrl1); - state_hash ^= rpcs3::hash_base(data.fp_ctrl); - state_hash ^= rpcs3::hash_base(data.vp_texture_dimensions); - state_hash ^= rpcs3::hash_base(data.fp_texture_dimensions); - state_hash ^= rpcs3::hash_base(data.fp_texcoord_control); - state_hash ^= rpcs3::hash_base(data.fp_height); - state_hash ^= rpcs3::hash_base(data.fp_pixel_layout); - state_hash ^= rpcs3::hash_base(data.fp_lighting_flags); - state_hash ^= rpcs3::hash_base(data.fp_shadow_textures); - state_hash ^= rpcs3::hash_base(data.fp_redirected_textures); - state_hash ^= rpcs3::hash_base(data.vp_multisampled_textures); - state_hash ^= rpcs3::hash_base(data.fp_multisampled_textures); + const u32 state_params[] = + { + data.vp_ctrl0, + data.vp_ctrl1, + data.fp_ctrl, + data.vp_texture_dimensions, + data.fp_texture_dimensions, + data.fp_texcoord_control, + data.fp_height, + data.fp_pixel_layout, + data.fp_lighting_flags, + data.fp_shadow_textures, + data.fp_redirected_textures, + data.vp_multisampled_textures, + data.fp_multisampled_textures, + data.fp_mrt_count, + }; + const usz state_hash = rpcs3::hash_array(state_params); const std::string pipeline_file_name = fmt::format("%llX+%llX+%llX+%llX.bin", data.vertex_program_hash, data.fragment_program_hash, data.pipeline_storage_hash, state_hash); const std::string pipeline_path = root_path + "/pipelines/" + pipeline_class_name + "/" + version_prefix + "/" + pipeline_file_name; @@ -439,6 +446,7 @@ namespace rsx data_block.fp_shadow_textures = fp.texture_state.shadow_textures; data_block.fp_redirected_textures = fp.texture_state.redirected_textures; data_block.fp_multisampled_textures = fp.texture_state.multisampled_textures; + data_block.fp_mrt_count = fp.mrt_buffers_count; return data_block; } diff --git a/rpcs3/util/fnv_hash.hpp b/rpcs3/util/fnv_hash.hpp index 42ff84abe2..dea3d8b480 100644 --- a/rpcs3/util/fnv_hash.hpp +++ b/rpcs3/util/fnv_hash.hpp @@ -61,4 +61,29 @@ namespace rpcs3 return hash_struct_base(value); } + + template + requires std::is_integral_v + static inline usz hash_array(const T(&arr)[N]) + { + usz hash = fnv_seed; + for (size_t i = 0; i < N; ++i) + { + hash = hash64(hash, arr[i]); + } + return hash; + } + + template + requires std::is_class_v + static inline usz hash_array(const T(&arr)[N]) + { + usz hash = fnv_seed; + for (size_t i = 0; i < N; ++i) + { + const u64 item_hash = hash_struct(arr[i]); + hash = hash64(hash, item_hash); + } + return hash; + } } From 6716d555ff3975a0d901dad4c39f34190521bff1 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Wed, 8 Jan 2025 02:56:20 +0100 Subject: [PATCH 43/60] cellGem: Update controllers at 10 Hz in separate thread Some games don't use cellGemGetInfo. Which means we had to reboot the game if the controller wasn't connected on boot. Updating controllers outside of the cell functions fixes this problem. --- rpcs3/Emu/Cell/Modules/cellGem.cpp | 145 ++++++++++++++++------------- 1 file changed, 79 insertions(+), 66 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellGem.cpp b/rpcs3/Emu/Cell/Modules/cellGem.cpp index 22988d25e6..28ae92e8d3 100644 --- a/rpcs3/Emu/Cell/Modules/cellGem.cpp +++ b/rpcs3/Emu/Cell/Modules/cellGem.cpp @@ -280,10 +280,74 @@ public: return controllers[gem_num].status == CELL_GEM_STATUS_READY; } + void update_connections() + { + switch (g_cfg.io.move) + { + case move_handler::real: + case move_handler::fake: + { + connected_controllers = 0; + + std::lock_guard lock(pad::g_pad_mutex); + const auto handler = pad::get_current_handler(); + + for (u32 i = 0; i < CELL_GEM_MAX_NUM; i++) + { + const auto& pad = ::at32(handler->GetPads(), pad_num(i)); + const bool connected = (pad && (pad->m_port_status & CELL_PAD_STATUS_CONNECTED) && i < attribute.max_connect); + const bool is_real_move = g_cfg.io.move != move_handler::real || pad->m_pad_handler == pad_handler::move; + + if (connected && is_real_move) + { + connected_controllers++; + controllers[i].status = CELL_GEM_STATUS_READY; + controllers[i].port = port_num(i); + } + else + { + controllers[i].status = CELL_GEM_STATUS_DISCONNECTED; + controllers[i].port = 0; + } + } + break; + } + case move_handler::raw_mouse: + { + connected_controllers = 0; + + auto& handler = g_fxo->get(); + std::lock_guard mouse_lock(handler.mutex); + + const MouseInfo& info = handler.GetInfo(); + + for (u32 i = 0; i < CELL_GEM_MAX_NUM; i++) + { + const bool connected = i < attribute.max_connect && info.status[i] == CELL_MOUSE_STATUS_CONNECTED; + + if (connected) + { + connected_controllers++; + controllers[i].status = CELL_GEM_STATUS_READY; + controllers[i].port = port_num(i); + } + else + { + controllers[i].status = CELL_GEM_STATUS_DISCONNECTED; + controllers[i].port = 0; + } + } + break; + } + default: + { + break; + } + } + } + void update_calibration_status() { - std::scoped_lock lock(mtx); - for (u32 gem_num = 0; gem_num < CELL_GEM_MAX_NUM; gem_num++) { gem_controller& controller = controllers[gem_num]; @@ -1154,13 +1218,25 @@ void gem_config_data::operator()() { cellGem.notice("Starting thread"); + u64 last_update_us = 0; + while (thread_ctrl::state() != thread_state::aborting && !Emu.IsStopped()) { while (!video_conversion_in_progress && thread_ctrl::state() != thread_state::aborting && !Emu.IsStopped()) { if (state) { - update_calibration_status(); + const u64 now_us = get_system_time(); + constexpr u64 update_timeout = 100000; // Update controllers at 10Hz + + if (now_us - last_update_us >= update_timeout) + { + last_update_us = now_us; + + std::scoped_lock lock(mtx); + update_connections(); + update_calibration_status(); + } } thread_ctrl::wait_for(1000); @@ -2632,69 +2708,6 @@ error_code cellGemGetInfo(vm::ptr info) return CELL_GEM_ERROR_INVALID_PARAMETER; } - switch (g_cfg.io.move) - { - case move_handler::real: - case move_handler::fake: - { - gem.connected_controllers = 0; - - std::lock_guard lock(pad::g_pad_mutex); - const auto handler = pad::get_current_handler(); - - for (u32 i = 0; i < CELL_GEM_MAX_NUM; i++) - { - const auto& pad = ::at32(handler->GetPads(), pad_num(i)); - const bool connected = (pad && (pad->m_port_status & CELL_PAD_STATUS_CONNECTED) && i < gem.attribute.max_connect); - const bool is_real_move = g_cfg.io.move != move_handler::real || pad->m_pad_handler == pad_handler::move; - - if (connected && is_real_move) - { - gem.connected_controllers++; - gem.controllers[i].status = CELL_GEM_STATUS_READY; - gem.controllers[i].port = port_num(i); - } - else - { - gem.controllers[i].status = CELL_GEM_STATUS_DISCONNECTED; - gem.controllers[i].port = 0; - } - } - break; - } - case move_handler::raw_mouse: - { - gem.connected_controllers = 0; - - auto& handler = g_fxo->get(); - std::lock_guard mouse_lock(handler.mutex); - - const MouseInfo& info = handler.GetInfo(); - - for (u32 i = 0; i < CELL_GEM_MAX_NUM; i++) - { - const bool connected = i < gem.attribute.max_connect && info.status[i] == CELL_MOUSE_STATUS_CONNECTED; - - if (connected) - { - gem.connected_controllers++; - gem.controllers[i].status = CELL_GEM_STATUS_READY; - gem.controllers[i].port = port_num(i); - } - else - { - gem.controllers[i].status = CELL_GEM_STATUS_DISCONNECTED; - gem.controllers[i].port = 0; - } - } - break; - } - default: - { - break; - } - } - info->max_connect = gem.attribute.max_connect; info->now_connect = gem.connected_controllers; From 3b8efecc5284533c2671655511470b28db529c7c Mon Sep 17 00:00:00 2001 From: Megamouse Date: Wed, 8 Jan 2025 03:19:35 +0100 Subject: [PATCH 44/60] cellGem: Use atomic wait instead of wait_for --- rpcs3/Emu/Cell/Modules/cellGem.cpp | 97 +++++++++++++++++++++++------- 1 file changed, 76 insertions(+), 21 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellGem.cpp b/rpcs3/Emu/Cell/Modules/cellGem.cpp index 28ae92e8d3..400b032ec7 100644 --- a/rpcs3/Emu/Cell/Modules/cellGem.cpp +++ b/rpcs3/Emu/Cell/Modules/cellGem.cpp @@ -274,6 +274,37 @@ public: u64 start_timestamp_us = 0; + atomic_t m_wake_up = 0; + atomic_t m_done = 0; + + void wake_up() + { + m_wake_up.release(1); + m_wake_up.notify_one(); + } + + void done() + { + m_done.release(1); + m_done.notify_one(); + } + + void wait_for_result() + { + if (!m_done) + { + m_done.wait(0); + m_done = 0; + } + } + + gem_config_data& operator=(thread_state) + { + wake_up(); + done(); + return *this; + } + // helper functions bool is_controller_ready(u32 gem_num) const { @@ -1222,24 +1253,39 @@ void gem_config_data::operator()() while (thread_ctrl::state() != thread_state::aborting && !Emu.IsStopped()) { - while (!video_conversion_in_progress && thread_ctrl::state() != thread_state::aborting && !Emu.IsStopped()) + atomic_wait_timeout timeout = atomic_wait_timeout::inf; + + if (state && !video_conversion_in_progress) { - if (state) + constexpr u64 update_timeout_us = 100'000; // Update controllers at 10Hz + const u64 now_us = get_system_time(); + const u64 elapsed_us = now_us - last_update_us; + + if (elapsed_us < update_timeout_us) { - const u64 now_us = get_system_time(); - constexpr u64 update_timeout = 100000; // Update controllers at 10Hz - - if (now_us - last_update_us >= update_timeout) - { - last_update_us = now_us; - - std::scoped_lock lock(mtx); - update_connections(); - update_calibration_status(); - } + timeout = atomic_wait_timeout{(update_timeout_us - elapsed_us) * 1000}; } + else + { + timeout = atomic_wait_timeout{update_timeout_us * 1000}; + last_update_us = now_us; - thread_ctrl::wait_for(1000); + std::scoped_lock lock(mtx); + update_connections(); + update_calibration_status(); + } + } + + if (!m_wake_up) + { + m_wake_up.wait(0, timeout); + } + + m_wake_up = 0; + + if (!video_conversion_in_progress) + { + continue; } if (thread_ctrl::state() == thread_state::aborting || Emu.IsStopped()) @@ -1256,6 +1302,7 @@ void gem_config_data::operator()() if (g_cfg.io.camera != camera_handler::qt) { video_conversion_in_progress = false; + done(); continue; } @@ -1272,6 +1319,7 @@ void gem_config_data::operator()() } video_conversion_in_progress = false; + done(); } } @@ -1295,12 +1343,18 @@ public: m_wake_up.notify_one(); } + void done() + { + m_done.release(1); + m_done.notify_one(); + } + void wait_for_result() { if (!m_done) { m_done.wait(0); - m_done.release(0); + m_done = 0; } } @@ -1359,6 +1413,7 @@ public: gem_tracker& operator=(thread_state) { wake_up(); + done(); return *this; } @@ -1468,8 +1523,7 @@ public: } // Notify that we are finished with this frame - m_done.release(1); - m_done.notify_one(); + done(); m_busy.release(false); } @@ -2188,10 +2242,7 @@ error_code cellGemConvertVideoFinish() return CELL_GEM_ERROR_CONVERT_NOT_STARTED; } - while (gem.video_conversion_in_progress && !Emu.IsStopped()) - { - thread_ctrl::wait_for(100); - } + gem.wait_for_result(); return CELL_OK; } @@ -2225,7 +2276,9 @@ error_code cellGemConvertVideoStart(vm::cptr video_frame) const auto& shared_data = g_fxo->get(); gem.video_data_in.resize(shared_data.size); std::memcpy(gem.video_data_in.data(), video_frame.get_ptr(), gem.video_data_in.size()); + gem.video_conversion_in_progress = true; + gem.wake_up(); return CELL_OK; } @@ -3063,6 +3116,8 @@ error_code cellGemInit(ppu_thread& ppu, vm::cptr attribute) // TODO: is this correct? gem.start_timestamp_us = get_guest_system_time(); + gem.wake_up(); + return CELL_OK; } From 3eb1e6303c21dc001b88e670c48562f13d8dc734 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Wed, 8 Jan 2025 19:08:21 +0100 Subject: [PATCH 45/60] cellGem: Use wait_on, fix fake/mouse move tracker wait --- rpcs3/Emu/Cell/Modules/cellGem.cpp | 103 +++++++++++++++++------------ 1 file changed, 60 insertions(+), 43 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellGem.cpp b/rpcs3/Emu/Cell/Modules/cellGem.cpp index 400b032ec7..3bc6c02a80 100644 --- a/rpcs3/Emu/Cell/Modules/cellGem.cpp +++ b/rpcs3/Emu/Cell/Modules/cellGem.cpp @@ -289,20 +289,21 @@ public: m_done.notify_one(); } - void wait_for_result() + bool wait_for_result(ppu_thread& ppu) { - if (!m_done) + while (!m_done && !ppu.is_stopped()) { - m_done.wait(0); - m_done = 0; + thread_ctrl::wait_on(m_done, 0); } - } - gem_config_data& operator=(thread_state) - { - wake_up(); - done(); - return *this; + if (ppu.is_stopped()) + { + ppu.state += cpu_flag::again; + return false; + } + + m_done = 0; + return true; } // helper functions @@ -1253,7 +1254,7 @@ void gem_config_data::operator()() while (thread_ctrl::state() != thread_state::aborting && !Emu.IsStopped()) { - atomic_wait_timeout timeout = atomic_wait_timeout::inf; + u64 timeout = umax; if (state && !video_conversion_in_progress) { @@ -1263,11 +1264,11 @@ void gem_config_data::operator()() if (elapsed_us < update_timeout_us) { - timeout = atomic_wait_timeout{(update_timeout_us - elapsed_us) * 1000}; + timeout = update_timeout_us - elapsed_us; } else { - timeout = atomic_wait_timeout{update_timeout_us * 1000}; + timeout = update_timeout_us; last_update_us = now_us; std::scoped_lock lock(mtx); @@ -1278,7 +1279,7 @@ void gem_config_data::operator()() if (!m_wake_up) { - m_wake_up.wait(0, timeout); + thread_ctrl::wait_on(m_wake_up, 0, timeout); } m_wake_up = 0; @@ -1337,25 +1338,39 @@ public: return m_busy; } - void wake_up() + void wake_up_tracker() { - m_wake_up.release(1); - m_wake_up.notify_one(); + m_wake_up_tracker.release(1); + m_wake_up_tracker.notify_one(); } - void done() + void tracker_done() { - m_done.release(1); - m_done.notify_one(); + m_tracker_done.release(1); + m_tracker_done.notify_one(); } - void wait_for_result() + bool wait_for_tracker_result(ppu_thread& ppu) { - if (!m_done) + if (g_cfg.io.move != move_handler::real) { - m_done.wait(0); - m_done = 0; + m_tracker_done = 0; + return true; } + + while (!m_tracker_done && !ppu.is_stopped()) + { + thread_ctrl::wait_on(m_tracker_done, 0); + } + + if (ppu.is_stopped()) + { + ppu.state += cpu_flag::again; + return false; + } + + m_tracker_done = 0; + return true; } bool set_image(u32 addr) @@ -1410,13 +1425,6 @@ public: return ::at32(m_info, gem_num); } - gem_tracker& operator=(thread_state) - { - wake_up(); - done(); - return *this; - } - void operator()() { if (g_cfg.io.move != move_handler::real) @@ -1434,10 +1442,10 @@ public: while (thread_ctrl::state() != thread_state::aborting) { // Check if we have a new frame - if (!m_wake_up) + if (!m_wake_up_tracker) { - m_wake_up.wait(0); - m_wake_up.release(0); + thread_ctrl::wait_on(m_wake_up_tracker, 0); + m_wake_up_tracker.release(0); if (thread_ctrl::state() == thread_state::aborting) { @@ -1523,7 +1531,7 @@ public: } // Notify that we are finished with this frame - done(); + tracker_done(); m_busy.release(false); } @@ -1534,8 +1542,8 @@ public: shared_mutex mutex; private: - atomic_t m_wake_up = 0; - atomic_t m_done = 1; + atomic_t m_wake_up_tracker = 0; + atomic_t m_tracker_done = 1; atomic_t m_busy = false; ps_move_tracker m_tracker{}; CellCameraInfoEx m_camera_info{}; @@ -2226,7 +2234,7 @@ error_code cellGemClearStatusFlags(u32 gem_num, u64 mask) return CELL_OK; } -error_code cellGemConvertVideoFinish() +error_code cellGemConvertVideoFinish(ppu_thread& ppu) { cellGem.warning("cellGemConvertVideoFinish()"); @@ -2242,7 +2250,10 @@ error_code cellGemConvertVideoFinish() return CELL_GEM_ERROR_CONVERT_NOT_STARTED; } - gem.wait_for_result(); + if (!gem.wait_for_result(ppu)) + { + return {}; + } return CELL_OK; } @@ -2417,7 +2428,10 @@ error_code cellGemEnd(ppu_thread& ppu) } auto& tracker = g_fxo->get>(); - tracker.wait_for_result(); + if (!tracker.wait_for_tracker_result(ppu)) + { + return {}; + } gem.updating = false; @@ -3507,7 +3521,7 @@ error_code cellGemTrackHues(vm::cptr req_hues, vm::ptr res_hues) return CELL_OK; } -error_code cellGemUpdateFinish() +error_code cellGemUpdateFinish(ppu_thread& ppu) { cellGem.warning("cellGemUpdateFinish()"); @@ -3526,7 +3540,10 @@ error_code cellGemUpdateFinish() } auto& tracker = g_fxo->get>(); - tracker.wait_for_result(); + if (!tracker.wait_for_tracker_result(ppu)) + { + return {}; + } gem.updating = false; @@ -3576,7 +3593,7 @@ error_code cellGemUpdateStart(vm::cptr camera_frame, u64 timestamp) return not_an_error(CELL_GEM_NO_VIDEO); } - tracker.wake_up(); + tracker.wake_up_tracker(); return CELL_OK; } From 1b87e186c45a21de83e9fdc859af3e0c92ae8509 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Fri, 10 Jan 2025 20:09:34 +0100 Subject: [PATCH 46/60] cellGem: fix initial tracker done state --- rpcs3/Emu/Cell/Modules/cellGem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpcs3/Emu/Cell/Modules/cellGem.cpp b/rpcs3/Emu/Cell/Modules/cellGem.cpp index 3bc6c02a80..627cf9a52d 100644 --- a/rpcs3/Emu/Cell/Modules/cellGem.cpp +++ b/rpcs3/Emu/Cell/Modules/cellGem.cpp @@ -1543,7 +1543,7 @@ public: private: atomic_t m_wake_up_tracker = 0; - atomic_t m_tracker_done = 1; + atomic_t m_tracker_done = 0; atomic_t m_busy = false; ps_move_tracker m_tracker{}; CellCameraInfoEx m_camera_info{}; From b65f977c5495fa9d7dd03e007acc1b662be3f30d Mon Sep 17 00:00:00 2001 From: Megamouse Date: Mon, 13 Jan 2025 22:12:26 +0100 Subject: [PATCH 47/60] Qt/input: fix raw mouse button assignment --- rpcs3/Input/raw_mouse_config.cpp | 38 +++++++------ rpcs3/Input/raw_mouse_config.h | 19 ++++++- rpcs3/Input/raw_mouse_handler.cpp | 57 ++++++++++++------- rpcs3/Input/raw_mouse_handler.h | 48 +++++++--------- .../rpcs3qt/emulated_pad_settings_dialog.cpp | 16 +++--- rpcs3/rpcs3qt/raw_mouse_settings_dialog.cpp | 24 ++++---- rpcs3/rpcs3qt/raw_mouse_settings_dialog.h | 2 +- 7 files changed, 120 insertions(+), 84 deletions(-) diff --git a/rpcs3/Input/raw_mouse_config.cpp b/rpcs3/Input/raw_mouse_config.cpp index 0d896fc1d8..5f9839ac72 100644 --- a/rpcs3/Input/raw_mouse_config.cpp +++ b/rpcs3/Input/raw_mouse_config.cpp @@ -4,22 +4,6 @@ LOG_CHANNEL(cfg_log, "CFG"); -std::string mouse_button_id(int code) -{ - switch (code) - { - case CELL_MOUSE_BUTTON_1: return "Button 1"; - case CELL_MOUSE_BUTTON_2: return "Button 2"; - case CELL_MOUSE_BUTTON_3: return "Button 3"; - case CELL_MOUSE_BUTTON_4: return "Button 4"; - case CELL_MOUSE_BUTTON_5: return "Button 5"; - case CELL_MOUSE_BUTTON_6: return "Button 6"; - case CELL_MOUSE_BUTTON_7: return "Button 7"; - case CELL_MOUSE_BUTTON_8: return "Button 8"; - } - return ""; -} - cfg::string& raw_mouse_config::get_button_by_index(int index) { switch (index) @@ -52,6 +36,28 @@ cfg::string& raw_mouse_config::get_button(int code) } } +std::string raw_mouse_config::get_button_name(std::string_view value) +{ + if (raw_mouse_button_map.contains(value)) + { + return std::string(value); + } + + return ""; +} + +std::string raw_mouse_config::get_button_name(s32 button_code) +{ + for (const auto& [name, code] : raw_mouse_button_map) + { + if (code == button_code) + { + return std::string(name); + } + } + return ""; +} + raw_mice_config::raw_mice_config() { for (u32 i = 0; i < ::size32(players); i++) diff --git a/rpcs3/Input/raw_mouse_config.h b/rpcs3/Input/raw_mouse_config.h index 902e0ed42f..a237ad2554 100644 --- a/rpcs3/Input/raw_mouse_config.h +++ b/rpcs3/Input/raw_mouse_config.h @@ -5,7 +5,21 @@ #include -std::string mouse_button_id(int code); +#ifdef _WIN32 +#include +#endif + +static const std::map raw_mouse_button_map +{ + { "", 0 }, +#ifdef _WIN32 + { "Button 1", RI_MOUSE_BUTTON_1_UP }, + { "Button 2", RI_MOUSE_BUTTON_2_UP }, + { "Button 3", RI_MOUSE_BUTTON_3_UP }, + { "Button 4", RI_MOUSE_BUTTON_4_UP }, + { "Button 5", RI_MOUSE_BUTTON_5_UP }, +#endif +}; struct raw_mouse_config : cfg::node { @@ -27,6 +41,9 @@ public: cfg::string& get_button_by_index(int index); cfg::string& get_button(int code); + + static std::string get_button_name(std::string_view value); + static std::string get_button_name(s32 button_code); }; struct raw_mice_config : cfg::node diff --git a/rpcs3/Input/raw_mouse_handler.cpp b/rpcs3/Input/raw_mouse_handler.cpp index 33e0410c11..7e7485c675 100644 --- a/rpcs3/Input/raw_mouse_handler.cpp +++ b/rpcs3/Input/raw_mouse_handler.cpp @@ -27,6 +27,18 @@ static inline void draw_overlay_cursor(u32 index, s32 x_pos, s32 y_pos, s32 x_ma [[maybe_unused]] static inline void draw_overlay_cursor(u32, s32, s32, s32, s32) {} #endif +#ifdef _WIN32 +const std::unordered_map raw_mouse::btn_pairs = +{ + { 0, {}}, + { RI_MOUSE_BUTTON_1_UP, mouse_button{ RI_MOUSE_BUTTON_1_DOWN, RI_MOUSE_BUTTON_1_UP }}, + { RI_MOUSE_BUTTON_2_UP, mouse_button{ RI_MOUSE_BUTTON_2_DOWN, RI_MOUSE_BUTTON_2_UP }}, + { RI_MOUSE_BUTTON_3_UP, mouse_button{ RI_MOUSE_BUTTON_3_DOWN, RI_MOUSE_BUTTON_3_UP }}, + { RI_MOUSE_BUTTON_4_UP, mouse_button{ RI_MOUSE_BUTTON_4_DOWN, RI_MOUSE_BUTTON_4_UP }}, + { RI_MOUSE_BUTTON_5_UP, mouse_button{ RI_MOUSE_BUTTON_5_DOWN, RI_MOUSE_BUTTON_5_UP }}, +}; +#endif + LOG_CHANNEL(input_log, "Input"); raw_mice_config g_cfg_raw_mouse; @@ -70,24 +82,14 @@ void raw_mouse::reload_config() void raw_mouse::set_index(u32 index) { m_index = index; - reload_requested = true; + m_reload_requested = true; } -std::pair raw_mouse::get_mouse_button(const cfg::string& button) +raw_mouse::mouse_button raw_mouse::get_mouse_button(const cfg::string& button) { const std::string value = button.to_string(); #ifdef _WIN32 - static const std::unordered_map> btn_pairs - { - { 0, {}}, - { RI_MOUSE_BUTTON_1_UP, { RI_MOUSE_BUTTON_1_DOWN, RI_MOUSE_BUTTON_1_UP }}, - { RI_MOUSE_BUTTON_2_UP, { RI_MOUSE_BUTTON_2_DOWN, RI_MOUSE_BUTTON_2_UP }}, - { RI_MOUSE_BUTTON_3_UP, { RI_MOUSE_BUTTON_3_DOWN, RI_MOUSE_BUTTON_3_UP }}, - { RI_MOUSE_BUTTON_4_UP, { RI_MOUSE_BUTTON_4_DOWN, RI_MOUSE_BUTTON_4_UP }}, - { RI_MOUSE_BUTTON_5_UP, { RI_MOUSE_BUTTON_5_DOWN, RI_MOUSE_BUTTON_5_UP }}, - }; - if (const auto it = raw_mouse_button_map.find(value); it != raw_mouse_button_map.cend()) { return ::at32(btn_pairs, it->second); @@ -134,28 +136,45 @@ void raw_mouse::update_values(const RAWMOUSE& state) // Update window handle and size update_window_handle(); - if (std::exchange(reload_requested, false)) + if (std::exchange(m_reload_requested, false)) { reload_config(); } + if (m_handler->is_for_gui()) + { + for (const auto& [up, btn] : btn_pairs) + { + // Only update the value if either down or up flags are present + if ((state.usButtonFlags & btn.down)) + { + m_handler->mouse_press_callback(m_device_name, 0, up, true); + } + else if ((state.usButtonFlags & btn.up)) + { + m_handler->mouse_press_callback(m_device_name, 0, up, false); + } + } + return; + } + const auto get_button_pressed = [this](u8 button, int button_flags) { const auto it = m_buttons.find(button); if (it == m_buttons.cend()) return; - const auto& [down, up] = it->second; + const mouse_button& btn = it->second; // Only update the value if either down or up flags are present - if ((button_flags & down)) + if ((button_flags & btn.down)) { m_handler->Button(m_index, button, true); - m_handler->mouse_press_callback(m_device_name, button, true); + m_handler->mouse_press_callback(m_device_name, button, btn.up, true); } - else if ((button_flags & up)) + else if ((button_flags & btn.up)) { m_handler->Button(m_index, button, false); - m_handler->mouse_press_callback(m_device_name, button, false); + m_handler->mouse_press_callback(m_device_name, button, btn.up, false); } }; @@ -576,7 +595,7 @@ void raw_mouse_handler::handle_native_event(const MSG& msg) RAWINPUT raw_input{}; UINT size = sizeof(RAWINPUT); - u32 res = GetRawInputData(reinterpret_cast(msg.lParam), RID_INPUT, &raw_input, &size, sizeof(RAWINPUTHEADER)); + const u32 res = GetRawInputData(reinterpret_cast(msg.lParam), RID_INPUT, &raw_input, &size, sizeof(RAWINPUTHEADER)); if (res == umax) { return; diff --git a/rpcs3/Input/raw_mouse_handler.h b/rpcs3/Input/raw_mouse_handler.h index 155587f11e..a66efd7286 100644 --- a/rpcs3/Input/raw_mouse_handler.h +++ b/rpcs3/Input/raw_mouse_handler.h @@ -6,22 +6,6 @@ #include "Utilities/mutex.h" #include "Utilities/Thread.h" -#ifdef _WIN32 -#include -#endif - -static const std::map raw_mouse_button_map -{ - { "", 0 }, -#ifdef _WIN32 - { "Button 1", RI_MOUSE_BUTTON_1_UP }, - { "Button 2", RI_MOUSE_BUTTON_2_UP }, - { "Button 3", RI_MOUSE_BUTTON_3_UP }, - { "Button 4", RI_MOUSE_BUTTON_4_UP }, - { "Button 5", RI_MOUSE_BUTTON_5_UP }, -#endif -}; - class raw_mouse_handler; class raw_mouse @@ -43,11 +27,21 @@ public: const std::string& device_name() const { return m_device_name; } u32 index() const { return m_index; } void set_index(u32 index); - void request_reload() { reload_requested = true; } + void request_reload() { m_reload_requested = true; } private: + struct mouse_button + { + int down = 0; + int up = 0; + }; + +#ifdef _WIN32 + static const std::unordered_map btn_pairs; +#endif + void reload_config(); - static std::pair get_mouse_button(const cfg::string& button); + static mouse_button get_mouse_button(const cfg::string& button); u32 m_index = 0; std::string m_device_name; @@ -61,8 +55,8 @@ private: int m_pos_y{}; float m_mouse_acceleration = 1.0f; raw_mouse_handler* m_handler{}; - std::map> m_buttons; - bool reload_requested = false; + std::map m_buttons; + bool m_reload_requested = false; }; class raw_mouse_handler final : public MouseHandlerBase @@ -74,23 +68,21 @@ public: void Init(const u32 max_connect) override; - void SetIsForGui(bool value) - { - m_is_for_gui = value; - } + void set_is_for_gui(bool value) { m_is_for_gui = value; } + bool is_for_gui() const { return m_is_for_gui; } const std::map& get_mice() const { return m_raw_mice; }; - void set_mouse_press_callback(std::function cb) + void set_mouse_press_callback(std::function cb) { m_mouse_press_callback = std::move(cb); } - void mouse_press_callback(const std::string& device_name, s32 cell_code, bool pressed) + void mouse_press_callback(const std::string& device_name, s32 cell_code, s32 button_code, bool pressed) { if (m_mouse_press_callback) { - m_mouse_press_callback(device_name, cell_code, pressed); + m_mouse_press_callback(device_name, cell_code, button_code, pressed); } } @@ -114,7 +106,7 @@ private: bool m_is_for_gui = false; std::map m_raw_mice; - std::function m_mouse_press_callback; + std::function m_mouse_press_callback; std::unique_ptr>> m_thread; }; diff --git a/rpcs3/rpcs3qt/emulated_pad_settings_dialog.cpp b/rpcs3/rpcs3qt/emulated_pad_settings_dialog.cpp index 00520a8c7c..aee4c44c77 100644 --- a/rpcs3/rpcs3qt/emulated_pad_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/emulated_pad_settings_dialog.cpp @@ -389,14 +389,14 @@ void emulated_pad_settings_dialog::add_tabs(QTabWidget* tabs) { std::string raw_mouse_settings; const auto& raw_cfg = *ensure(::at32(g_cfg_raw_mouse.players, player)); - fmt::append(raw_mouse_settings, "1: %s\n", raw_cfg.mouse_button_1.to_string()); - fmt::append(raw_mouse_settings, "2: %s\n", raw_cfg.mouse_button_2.to_string()); - fmt::append(raw_mouse_settings, "3: %s\n", raw_cfg.mouse_button_3.to_string()); - fmt::append(raw_mouse_settings, "4: %s\n", raw_cfg.mouse_button_4.to_string()); - fmt::append(raw_mouse_settings, "5: %s\n", raw_cfg.mouse_button_5.to_string()); - fmt::append(raw_mouse_settings, "6: %s\n", raw_cfg.mouse_button_6.to_string()); - fmt::append(raw_mouse_settings, "7: %s\n", raw_cfg.mouse_button_7.to_string()); - fmt::append(raw_mouse_settings, "8: %s", raw_cfg.mouse_button_8.to_string()); + fmt::append(raw_mouse_settings, "1: %s\n", raw_mouse_config::get_button_name(raw_cfg.mouse_button_1.to_string())); + fmt::append(raw_mouse_settings, "2: %s\n", raw_mouse_config::get_button_name(raw_cfg.mouse_button_2.to_string())); + fmt::append(raw_mouse_settings, "3: %s\n", raw_mouse_config::get_button_name(raw_cfg.mouse_button_3.to_string())); + fmt::append(raw_mouse_settings, "4: %s\n", raw_mouse_config::get_button_name(raw_cfg.mouse_button_4.to_string())); + fmt::append(raw_mouse_settings, "5: %s\n", raw_mouse_config::get_button_name(raw_cfg.mouse_button_5.to_string())); + fmt::append(raw_mouse_settings, "6: %s\n", raw_mouse_config::get_button_name(raw_cfg.mouse_button_6.to_string())); + fmt::append(raw_mouse_settings, "7: %s\n", raw_mouse_config::get_button_name(raw_cfg.mouse_button_7.to_string())); + fmt::append(raw_mouse_settings, "8: %s", raw_mouse_config::get_button_name(raw_cfg.mouse_button_8.to_string())); QGroupBox* gb_legend_raw = new QGroupBox(tr("Current Raw Mouse Config"), this); QVBoxLayout* gb_legend_raw_layout = new QVBoxLayout(this); diff --git a/rpcs3/rpcs3qt/raw_mouse_settings_dialog.cpp b/rpcs3/rpcs3qt/raw_mouse_settings_dialog.cpp index 31151e098e..7faf4e4c7a 100644 --- a/rpcs3/rpcs3qt/raw_mouse_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/raw_mouse_settings_dialog.cpp @@ -68,11 +68,11 @@ raw_mouse_settings_dialog::raw_mouse_settings_dialog(QWidget* parent) constexpr u32 max_devices = 16; g_raw_mouse_handler = std::make_unique(); - g_raw_mouse_handler->SetIsForGui(true); + g_raw_mouse_handler->set_is_for_gui(true); g_raw_mouse_handler->Init(std::max(max_devices, ::size32(g_cfg_raw_mouse.players))); - g_raw_mouse_handler->set_mouse_press_callback([this](const std::string& device_name, s32 cell_code, bool pressed) + g_raw_mouse_handler->set_mouse_press_callback([this](const std::string& device_name, s32 cell_code, s32 button_code, bool pressed) { - mouse_press(device_name, cell_code, pressed); + mouse_press(device_name, cell_code, button_code, pressed); }); m_buttons = new QButtonGroup(this); @@ -91,7 +91,7 @@ raw_mouse_settings_dialog::raw_mouse_settings_dialog(QWidget* parent) if (const int button_id = m_buttons->id(button); button_id >= 0) { auto& config = ::at32(g_cfg_raw_mouse.players, m_tab_widget->currentIndex()); - const std::string name = config->get_button_by_index(button_id % button_count).to_string(); + const std::string name = raw_mouse_config::get_button_name(config->get_button_by_index(button_id % button_count).to_string()); button->setText(name.empty() ? QStringLiteral("-") : QString::fromStdString(name)); } } @@ -255,7 +255,7 @@ void raw_mouse_settings_dialog::add_tabs(QTabWidget* tabs) insert_button(static_cast(player * button_count + i), pb); - const std::string saved_btn = config->get_button(cell_code); + const std::string saved_btn = raw_mouse_config::get_button_name(config->get_button(cell_code).to_string()); pb->setText(saved_btn.empty() ? QStringLiteral("-") : QString::fromStdString(saved_btn)); @@ -330,8 +330,8 @@ void raw_mouse_settings_dialog::reset_config() if (!pb) continue; - const QString text = QString::fromStdString(config->get_button(cell_code).def); - pb->setText(text.isEmpty() ? QStringLiteral("-") : text); + const std::string text = raw_mouse_config::get_button_name(config->get_button(cell_code).def); + pb->setText(text.empty() ? QStringLiteral("-") : QString::fromStdString(text)); } if (QComboBox* combo = ::at32(m_device_combos, player)) @@ -346,7 +346,7 @@ void raw_mouse_settings_dialog::reset_config() } } -void raw_mouse_settings_dialog::mouse_press(const std::string& device_name, s32 cell_code, bool pressed) +void raw_mouse_settings_dialog::mouse_press(const std::string& device_name, s32 /*cell_code*/, s32 button_code, bool pressed) { if (m_button_id < 0 || pressed) // Let's only react to mouse releases { @@ -361,12 +361,14 @@ void raw_mouse_settings_dialog::mouse_press(const std::string& device_name, s32 return; } - auto& config = ::at32(g_cfg_raw_mouse.players, m_tab_widget->currentIndex()); - config->get_button_by_index(m_button_id % button_count).from_string(mouse_button_id(cell_code)); + const std::string button_name = raw_mouse_config::get_button_name(button_code); + + auto& config = ::at32(g_cfg_raw_mouse.players, player); + config->get_button_by_index(m_button_id % button_count).from_string(button_name); if (auto button = m_buttons->button(m_button_id)) { - button->setText(localized_emu::translated_mouse_button(cell_code)); + button->setText(QString::fromStdString(button_name)); } reactivate_buttons(); diff --git a/rpcs3/rpcs3qt/raw_mouse_settings_dialog.h b/rpcs3/rpcs3qt/raw_mouse_settings_dialog.h index 1ed13efe99..206c6ec213 100644 --- a/rpcs3/rpcs3qt/raw_mouse_settings_dialog.h +++ b/rpcs3/rpcs3qt/raw_mouse_settings_dialog.h @@ -30,7 +30,7 @@ private: void on_enumeration(); void reset_config(); void on_button_click(int id); - void mouse_press(const std::string& device_name, s32 cell_code, bool pressed); + void mouse_press(const std::string& device_name, s32 cell_code, s32 button_code, bool pressed); void handle_device_change(const std::string& device_name); bool is_device_active(const std::string& device_name); std::string get_current_device_name(int player); From 78a661db79809ca8802472582069821c9eedf90c Mon Sep 17 00:00:00 2001 From: Megamouse Date: Mon, 13 Jan 2025 22:37:13 +0100 Subject: [PATCH 48/60] input: simplify raw mouse button press handling --- rpcs3/Input/raw_mouse_handler.cpp | 30 +++++---------------- rpcs3/Input/raw_mouse_handler.h | 8 +++--- rpcs3/rpcs3qt/raw_mouse_settings_dialog.cpp | 6 ++--- rpcs3/rpcs3qt/raw_mouse_settings_dialog.h | 2 +- 4 files changed, 15 insertions(+), 31 deletions(-) diff --git a/rpcs3/Input/raw_mouse_handler.cpp b/rpcs3/Input/raw_mouse_handler.cpp index 7e7485c675..6d24b46bf9 100644 --- a/rpcs3/Input/raw_mouse_handler.cpp +++ b/rpcs3/Input/raw_mouse_handler.cpp @@ -148,45 +148,29 @@ void raw_mouse::update_values(const RAWMOUSE& state) // Only update the value if either down or up flags are present if ((state.usButtonFlags & btn.down)) { - m_handler->mouse_press_callback(m_device_name, 0, up, true); + m_handler->mouse_press_callback(m_device_name, up, true); } else if ((state.usButtonFlags & btn.up)) { - m_handler->mouse_press_callback(m_device_name, 0, up, false); + m_handler->mouse_press_callback(m_device_name, up, false); } } return; } - const auto get_button_pressed = [this](u8 button, int button_flags) + // Get mouse buttons + for (const auto& [button, btn] : m_buttons) { - const auto it = m_buttons.find(button); - if (it == m_buttons.cend()) return; - - const mouse_button& btn = it->second; - // Only update the value if either down or up flags are present - if ((button_flags & btn.down)) + if ((state.usButtonFlags & btn.down)) { m_handler->Button(m_index, button, true); - m_handler->mouse_press_callback(m_device_name, button, btn.up, true); } - else if ((button_flags & btn.up)) + else if ((state.usButtonFlags & btn.up)) { m_handler->Button(m_index, button, false); - m_handler->mouse_press_callback(m_device_name, button, btn.up, false); } - }; - - // Get mouse buttons - get_button_pressed(CELL_MOUSE_BUTTON_1, state.usButtonFlags); - get_button_pressed(CELL_MOUSE_BUTTON_2, state.usButtonFlags); - get_button_pressed(CELL_MOUSE_BUTTON_3, state.usButtonFlags); - get_button_pressed(CELL_MOUSE_BUTTON_4, state.usButtonFlags); - get_button_pressed(CELL_MOUSE_BUTTON_5, state.usButtonFlags); - get_button_pressed(CELL_MOUSE_BUTTON_6, state.usButtonFlags); - get_button_pressed(CELL_MOUSE_BUTTON_7, state.usButtonFlags); - get_button_pressed(CELL_MOUSE_BUTTON_8, state.usButtonFlags); + } // Get mouse wheel if ((state.usButtonFlags & RI_MOUSE_WHEEL)) diff --git a/rpcs3/Input/raw_mouse_handler.h b/rpcs3/Input/raw_mouse_handler.h index a66efd7286..adef7c5624 100644 --- a/rpcs3/Input/raw_mouse_handler.h +++ b/rpcs3/Input/raw_mouse_handler.h @@ -73,16 +73,16 @@ public: const std::map& get_mice() const { return m_raw_mice; }; - void set_mouse_press_callback(std::function cb) + void set_mouse_press_callback(std::function cb) { m_mouse_press_callback = std::move(cb); } - void mouse_press_callback(const std::string& device_name, s32 cell_code, s32 button_code, bool pressed) + void mouse_press_callback(const std::string& device_name, s32 button_code, bool pressed) { if (m_mouse_press_callback) { - m_mouse_press_callback(device_name, cell_code, button_code, pressed); + m_mouse_press_callback(device_name, button_code, pressed); } } @@ -106,7 +106,7 @@ private: bool m_is_for_gui = false; std::map m_raw_mice; - std::function m_mouse_press_callback; + std::function m_mouse_press_callback; std::unique_ptr>> m_thread; }; diff --git a/rpcs3/rpcs3qt/raw_mouse_settings_dialog.cpp b/rpcs3/rpcs3qt/raw_mouse_settings_dialog.cpp index 7faf4e4c7a..3936598995 100644 --- a/rpcs3/rpcs3qt/raw_mouse_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/raw_mouse_settings_dialog.cpp @@ -70,9 +70,9 @@ raw_mouse_settings_dialog::raw_mouse_settings_dialog(QWidget* parent) g_raw_mouse_handler = std::make_unique(); g_raw_mouse_handler->set_is_for_gui(true); g_raw_mouse_handler->Init(std::max(max_devices, ::size32(g_cfg_raw_mouse.players))); - g_raw_mouse_handler->set_mouse_press_callback([this](const std::string& device_name, s32 cell_code, s32 button_code, bool pressed) + g_raw_mouse_handler->set_mouse_press_callback([this](const std::string& device_name, s32 button_code, bool pressed) { - mouse_press(device_name, cell_code, button_code, pressed); + mouse_press(device_name, button_code, pressed); }); m_buttons = new QButtonGroup(this); @@ -346,7 +346,7 @@ void raw_mouse_settings_dialog::reset_config() } } -void raw_mouse_settings_dialog::mouse_press(const std::string& device_name, s32 /*cell_code*/, s32 button_code, bool pressed) +void raw_mouse_settings_dialog::mouse_press(const std::string& device_name, s32 button_code, bool pressed) { if (m_button_id < 0 || pressed) // Let's only react to mouse releases { diff --git a/rpcs3/rpcs3qt/raw_mouse_settings_dialog.h b/rpcs3/rpcs3qt/raw_mouse_settings_dialog.h index 206c6ec213..d55abf8658 100644 --- a/rpcs3/rpcs3qt/raw_mouse_settings_dialog.h +++ b/rpcs3/rpcs3qt/raw_mouse_settings_dialog.h @@ -30,7 +30,7 @@ private: void on_enumeration(); void reset_config(); void on_button_click(int id); - void mouse_press(const std::string& device_name, s32 cell_code, s32 button_code, bool pressed); + void mouse_press(const std::string& device_name, s32 button_code, bool pressed); void handle_device_change(const std::string& device_name); bool is_device_active(const std::string& device_name); std::string get_current_device_name(int player); From d1ccadbac2818fbe7ab8361dd59cd632f87449fa Mon Sep 17 00:00:00 2001 From: Megamouse Date: Tue, 14 Jan 2025 03:03:18 +0100 Subject: [PATCH 49/60] cellGem: ignore intercepted input --- rpcs3/Emu/Cell/Modules/cellGem.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellGem.cpp b/rpcs3/Emu/Cell/Modules/cellGem.cpp index 627cf9a52d..da3c7f6261 100644 --- a/rpcs3/Emu/Cell/Modules/cellGem.cpp +++ b/rpcs3/Emu/Cell/Modules/cellGem.cpp @@ -8,6 +8,7 @@ #include "Emu/Io/MouseHandler.h" #include "Emu/Io/PadHandler.h" #include "Emu/Io/gem_config.h" +#include "Emu/Io/interception.h" #include "Emu/system_config.h" #include "Emu/System.h" #include "Emu/IdManager.h" @@ -1713,7 +1714,7 @@ static void ds3_input_to_pad(const u32 gem_num, be_t& digital_buttons, be_t digital_buttons = 0; analog_t = 0; - if (!is_input_allowed()) + if (!is_input_allowed() || input::g_pads_intercepted) // Let's intercept the PS Move just like a pad { return; } @@ -1806,7 +1807,7 @@ static inline void ds3_get_stick_values(u32 gem_num, const std::shared_ptr& template static void ds3_pos_to_gem_state(u32 gem_num, gem_config::gem_controller& controller, T& gem_state) { - if (!gem_state || !is_input_allowed()) + if (!gem_state || !is_input_allowed() || input::g_pads_intercepted) // Let's intercept the PS Move just like a pad { return; } @@ -1837,7 +1838,7 @@ static void ds3_pos_to_gem_state(u32 gem_num, gem_config::gem_controller& contro template static void ps_move_pos_to_gem_state(u32 gem_num, gem_config::gem_controller& controller, T& gem_state) { - if (!gem_state || !is_input_allowed()) + if (!gem_state || !is_input_allowed() || input::g_pads_intercepted) // Let's intercept the PS Move just like a pad { return; } @@ -1882,7 +1883,7 @@ static void ds3_input_to_ext(u32 gem_num, gem_config::gem_controller& controller { ext = {}; - if (!is_input_allowed()) + if (!is_input_allowed() || input::g_pads_intercepted) // Let's intercept the PS Move just like a pad { return; } @@ -1963,7 +1964,7 @@ static bool mouse_input_to_pad(u32 mouse_no, be_t& digital_buttons, be_t& digital_buttons, be_t static void mouse_pos_to_gem_state(u32 mouse_no, gem_config::gem_controller& controller, T& gem_state) { - if (!gem_state || !is_input_allowed()) + if (!gem_state || !is_input_allowed() || input::g_pads_intercepted) // Let's intercept the PS Move just like a pad { return; } From ae670c35f7bed462dba3b5106f045516723fef7b Mon Sep 17 00:00:00 2001 From: capriots <29807355+capriots@users.noreply.github.com> Date: Sun, 12 Jan 2025 12:25:06 +0100 Subject: [PATCH 50/60] cellAtracXdec: use lv2 mutexes + conds --- rpcs3/Emu/Cell/Modules/cellAtracXdec.cpp | 227 +++++++++++++++++------ rpcs3/Emu/Cell/Modules/cellAtracXdec.h | 29 ++- 2 files changed, 192 insertions(+), 64 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellAtracXdec.cpp b/rpcs3/Emu/Cell/Modules/cellAtracXdec.cpp index ff26b79473..36228d8a5e 100644 --- a/rpcs3/Emu/Cell/Modules/cellAtracXdec.cpp +++ b/rpcs3/Emu/Cell/Modules/cellAtracXdec.cpp @@ -270,35 +270,75 @@ void AtracXdecContext::exec(ppu_thread& ppu) decoder.init_avcodec(); } + switch (savestate) + { + case atracxdec_state::initial: break; + case atracxdec_state::waiting_for_cmd: goto label1_wait_for_cmd_state; + case atracxdec_state::checking_run_thread_1: goto label2_check_run_thread_1_state; + case atracxdec_state::executing_cmd: goto label3_execute_cmd_state; + case atracxdec_state::waiting_for_output: goto label4_wait_for_output_state; + case atracxdec_state::checking_run_thread_2: goto label5_check_run_thread_2_state; + case atracxdec_state::decoding: goto label6_decode_state; + } + for (;;cmd_counter++) { cellAtracXdec.trace("Command counter: %llu, waiting for next command...", cmd_counter); - if (!skip_getting_command) + for (;;) { - lv2_obj::sleep(ppu); - std::lock_guard lock{queue_mutex}; + savestate = atracxdec_state::initial; - while (cmd_queue.empty() && !ppu.is_stopped()) - { - lv2_obj::sleep(ppu); - queue_not_empty.wait(queue_mutex, 20000); - } + ensure(sys_mutex_lock(ppu, queue_mutex, 0) == CELL_OK); - if (ppu.is_stopped()) - { - ppu.state += cpu_flag::again; - return; - } - - cmd_queue.pop(cmd); - - if (!run_thread) + if (ppu.state & cpu_flag::again) { return; } + + if (!cmd_queue.empty()) + { + break; + } + + savestate = atracxdec_state::waiting_for_cmd; + label1_wait_for_cmd_state: + + ensure(sys_cond_wait(ppu, queue_not_empty, 0) == CELL_OK); + + if (ppu.state & cpu_flag::again) + { + return; + } + + ensure(sys_mutex_unlock(ppu, queue_mutex) == CELL_OK); } + cmd_queue.pop(cmd); + + ensure(sys_mutex_unlock(ppu, queue_mutex) == CELL_OK); + + savestate = atracxdec_state::checking_run_thread_1; + label2_check_run_thread_1_state: + + ensure(sys_mutex_lock(ppu, run_thread_mutex, 0) == CELL_OK); + + if (ppu.state & cpu_flag::again) + { + return; + } + + if (!run_thread) + { + ensure(sys_mutex_unlock(ppu, run_thread_mutex) == CELL_OK); + return; + } + + ensure(sys_mutex_unlock(ppu, run_thread_mutex) == CELL_OK); + + savestate = atracxdec_state::executing_cmd; + label3_execute_cmd_state: + cellAtracXdec.trace("Command type: %d", static_cast(cmd.type.get())); switch (cmd.type) @@ -327,8 +367,6 @@ void AtracXdecContext::exec(ppu_thread& ppu) } case AtracXdecCmdType::end_seq: { - skip_getting_command = true; - // Block savestate creation during callbacks std::unique_lock savestate_lock{g_fxo->get(), std::try_to_lock}; @@ -338,41 +376,59 @@ void AtracXdecContext::exec(ppu_thread& ppu) return; } - skip_getting_command = false; - // Doesn't do anything else notify_seq_done.cbFunc(ppu, notify_seq_done.cbArg); break; } case AtracXdecCmdType::decode_au: { - skip_getting_command = true; - ensure(!!cmd.au_start_addr); // Not checked on LLE cellAtracXdec.trace("Waiting for output to be consumed..."); - lv2_obj::sleep(ppu); - std::unique_lock output_mutex_lock{output_mutex}; + ensure(sys_mutex_lock(ppu, output_mutex, 0) == CELL_OK); - while (output_locked && !ppu.is_stopped()) + if (ppu.state & cpu_flag::again) { - lv2_obj::sleep(ppu); - output_consumed.wait(output_mutex, 20000); + return; } - if (ppu.is_stopped()) + while (output_locked) + { + savestate = atracxdec_state::waiting_for_output; + label4_wait_for_output_state: + + ensure(sys_cond_wait(ppu, output_consumed, 0) == CELL_OK); + + if (ppu.state & cpu_flag::again) + { + return; + } + } + + cellAtracXdec.trace("Output consumed"); + + savestate = atracxdec_state::checking_run_thread_2; + label5_check_run_thread_2_state: + + ensure(sys_mutex_lock(ppu, run_thread_mutex, 0) == CELL_OK); + + if (ppu.state & cpu_flag::again) { - ppu.state += cpu_flag::again; return; } if (!run_thread) { + ensure(sys_mutex_unlock(ppu, run_thread_mutex) == CELL_OK); + ensure(sys_mutex_unlock(ppu, output_mutex) == CELL_OK); return; } - cellAtracXdec.trace("Output consumed"); + ensure(sys_mutex_unlock(ppu, run_thread_mutex) == CELL_OK); + + savestate = atracxdec_state::decoding; + label6_decode_state: u32 error = CELL_OK; @@ -578,14 +634,12 @@ void AtracXdecContext::exec(ppu_thread& ppu) return; } - skip_getting_command = false; - // au_done and pcm_out callbacks are always called after a decode command, even if an error occurred // The output always has to be consumed as well notify_au_done.cbFunc(ppu, cmd.pcm_handle, notify_au_done.cbArg); output_locked = true; - output_mutex_lock.unlock(); + ensure(sys_mutex_unlock(ppu, output_mutex) == CELL_OK); const u32 output_size = decoded_samples_num * (decoder.bw_pcm & 0x7fu) * decoder.nch_out; @@ -614,29 +668,46 @@ void AtracXdecContext::exec(ppu_thread& ppu) template error_code AtracXdecContext::send_command(ppu_thread& ppu, auto&&... args) { - ppu.state += cpu_flag::wait; + auto& savestate = *ppu.optional_savestate_state; + const bool signal = savestate.try_read().second; + savestate.clear(); + if (!signal) { - std::lock_guard lock{queue_mutex}; + ensure(sys_mutex_lock(ppu, queue_mutex, 0) == CELL_OK); + + if (ppu.state & cpu_flag::again) + { + return {}; + } if constexpr (type == AtracXdecCmdType::close) { // Close command is only sent if the queue is empty on LLE if (!cmd_queue.empty()) { + ensure(sys_mutex_unlock(ppu, queue_mutex) == CELL_OK); return {}; } } if (cmd_queue.full()) { + ensure(sys_mutex_unlock(ppu, queue_mutex) == CELL_OK); return CELL_ADEC_ERROR_ATX_BUSY; } cmd_queue.emplace(std::forward(type), std::forward(args)...); + + ensure(sys_mutex_unlock(ppu, queue_mutex) == CELL_OK); } - queue_not_empty.notify_one(); + ensure(sys_cond_signal(ppu, queue_not_empty) == CELL_OK); + + if (ppu.state & cpu_flag::again) + { + savestate(true); + } return CELL_OK; } @@ -699,6 +770,29 @@ error_code _CellAdecCoreOpOpenExt_atracx(ppu_thread& ppu, vm::ptr::make(handle.addr() + utils::align(static_cast(sizeof(AtracXdecContext)), 0x80) + ATXDEC_SPURS_STRUCTS_SIZE))); + const vm::var mutex_attr{{ SYS_SYNC_PRIORITY, SYS_SYNC_NOT_RECURSIVE, SYS_SYNC_NOT_PROCESS_SHARED, SYS_SYNC_NOT_ADAPTIVE, 0, 0, 0, { "_atd001"_u64 } }}; + const vm::var cond_attr{{ SYS_SYNC_NOT_PROCESS_SHARED, 0, 0, { "_atd002"_u64 } }}; + + ensure(sys_mutex_create(ppu, handle.ptr(&AtracXdecContext::queue_mutex), mutex_attr) == CELL_OK); + ensure(sys_cond_create(ppu, handle.ptr(&AtracXdecContext::queue_not_empty), handle->queue_mutex, cond_attr) == CELL_OK); + + mutex_attr->name_u64 = "_atd003"_u64; + cond_attr->name_u64 = "_atd004"_u64; + + ensure(sys_mutex_create(ppu, handle.ptr(&AtracXdecContext::run_thread_mutex), mutex_attr) == CELL_OK); + ensure(sys_cond_create(ppu, handle.ptr(&AtracXdecContext::run_thread_cond), handle->run_thread_mutex, cond_attr) == CELL_OK); + + mutex_attr->name_u64 = "_atd005"_u64; + cond_attr->name_u64 = "_atd006"_u64; + + ensure(sys_mutex_create(ppu, handle.ptr(&AtracXdecContext::output_mutex), mutex_attr) == CELL_OK); + ensure(sys_cond_create(ppu, handle.ptr(&AtracXdecContext::output_consumed), handle->output_mutex, cond_attr) == CELL_OK); + + ensure(sys_mutex_lock(ppu, handle->output_mutex, 0) == CELL_OK); + handle->output_locked = false; + ensure(sys_cond_signal(ppu, handle->output_consumed) == CELL_OK); + ensure(sys_mutex_unlock(ppu, handle->output_mutex) == CELL_OK); + const vm::var _name = vm::make_str("HLE ATRAC3plus decoder"); const auto entry = g_fxo->get().func_addr(FIND_FUNC(atracXdecEntry)); ppu_execute<&sys_ppu_thread_create>(ppu, handle.ptr(&AtracXdecContext::thread_id), entry, handle.addr(), +res->ppuThreadPriority, +res->ppuThreadStackSize, SYS_PPU_THREAD_CREATE_JOINABLE, +_name); @@ -725,29 +819,32 @@ error_code _CellAdecCoreOpClose_atracx(ppu_thread& ppu, vm::ptrrun_thread_mutex, 0) == CELL_OK); handle->run_thread = false; + ensure(sys_mutex_unlock(ppu, handle->run_thread_mutex) == CELL_OK); + handle->send_command(ppu); - { - std::lock_guard lock{handle->output_mutex}; - handle->output_locked = false; - } + ensure(sys_mutex_lock(ppu, handle->output_mutex, 0) == CELL_OK); + handle->output_locked = false; + ensure(sys_mutex_unlock(ppu, handle->output_mutex) == CELL_OK); + ensure(sys_cond_signal(ppu, handle->output_consumed) == CELL_OK); - handle->output_consumed.notify_one(); + vm::var thread_ret; + ensure(sys_ppu_thread_join(ppu, static_cast(handle->thread_id), +thread_ret) == CELL_OK); - if (vm::var ret; sys_ppu_thread_join(ppu, static_cast(handle->thread_id), +ret) != CELL_OK) - { - // Other thread already closed the decoder - return CELL_ADEC_ERROR_FATAL; - } + error_code ret = sys_cond_destroy(ppu, handle->queue_not_empty); + ret = ret ? ret : sys_cond_destroy(ppu, handle->run_thread_cond); + ret = ret ? ret : sys_cond_destroy(ppu, handle->output_consumed); + ret = ret ? ret : sys_mutex_destroy(ppu, handle->queue_mutex); + ret = ret ? ret : sys_mutex_destroy(ppu, handle->run_thread_mutex); + ret = ret ? ret : sys_mutex_destroy(ppu, handle->output_mutex); - return CELL_OK; + return ret != CELL_OK ? static_cast(CELL_ADEC_ERROR_FATAL) : CELL_OK; } error_code _CellAdecCoreOpStartSeq_atracx(ppu_thread& ppu, vm::ptr handle, vm::cptr atracxParam) @@ -808,15 +905,35 @@ error_code _CellAdecCoreOpRealign_atracx(vm::ptr handle, vm::p error_code _CellAdecCoreOpReleasePcm_atracx(ppu_thread& ppu, vm::ptr handle, s32 pcmHandle, vm::cptr outBuffer) { - ppu.state += cpu_flag::wait; - cellAtracXdec.trace("_CellAdecCoreOpReleasePcm_atracx(handle=*0x%x, pcmHandle=%d, outBuffer=*0x%x)", handle, pcmHandle, outBuffer); ensure(!!handle); // Not checked on LLE - std::lock_guard lock{handle->output_mutex}; - handle->output_locked = false; - handle->output_consumed.notify_one(); + auto& savestate = *ppu.optional_savestate_state; + const bool signal = savestate.try_read().second; + savestate.clear(); + + if (!signal) + { + ensure(sys_mutex_lock(ppu, handle->output_mutex, 0) == CELL_OK); + + if (ppu.state & cpu_flag::again) + { + return {}; + } + + handle->output_locked = false; + } + + ensure(sys_cond_signal(ppu, handle->output_consumed) == CELL_OK); + + if (ppu.state & cpu_flag::again) + { + savestate(true); + return {}; + } + + ensure(sys_mutex_unlock(ppu, handle->output_mutex) == CELL_OK); return CELL_OK; } diff --git a/rpcs3/Emu/Cell/Modules/cellAtracXdec.h b/rpcs3/Emu/Cell/Modules/cellAtracXdec.h index fc96b6ef86..a670a242c2 100644 --- a/rpcs3/Emu/Cell/Modules/cellAtracXdec.h +++ b/rpcs3/Emu/Cell/Modules/cellAtracXdec.h @@ -20,7 +20,6 @@ constexpr int averror_eof = AVERROR_EOF; // Workaround for old-style-cast error #pragma GCC diagnostic pop #endif -#include "Utilities/cond.h" #include "cellPamf.h" #include "cellAdec.h" @@ -215,16 +214,28 @@ struct AtracXdecDecoder CHECK_SIZE(AtracXdecDecoder, 0xa8); +// HLE exclusive, for savestates +enum class atracxdec_state : u8 +{ + initial, + waiting_for_cmd, + checking_run_thread_1, + executing_cmd, + waiting_for_output, + checking_run_thread_2, + decoding +}; + struct AtracXdecContext { be_t thread_id; // sys_ppu_thread_t - shared_mutex queue_mutex; // sys_mutex_t - cond_variable queue_not_empty; // sys_cond_t + be_t queue_mutex; // sys_mutex_t + be_t queue_not_empty; // sys_cond_t AdecCmdQueue cmd_queue; - shared_mutex output_mutex; // sys_mutex_t - cond_variable output_consumed; // sys_cond_t + be_t output_mutex; // sys_mutex_t + be_t output_consumed; // sys_cond_t be_t output_locked = false; be_t run_thread_mutex; // sys_mutex_t @@ -239,10 +250,10 @@ struct AtracXdecContext const vm::bptr work_mem; // HLE exclusive - u64 cmd_counter = 0; // For debugging - AtracXdecCmd cmd; // For savestates; if savestate was created while processing a decode command, we need to save the current command - b8 skip_getting_command = false; // For savestates; skips getting a new command from the queue - b8 skip_next_frame; // Needed to emulate behavior of LLE SPU program, it doesn't output the first frame after a sequence reset or error + u64 cmd_counter = 0; // For debugging + AtracXdecCmd cmd; // For savestates; if savestate was created while processing a decode command, we need to save the current command + atracxdec_state savestate{}; // For savestates + b8 skip_next_frame; // Needed to emulate behavior of LLE SPU program, it doesn't output the first frame after a sequence reset or error u8 spurs_stuff[58]; // 120 bytes on LLE, pointers to CellSpurs, CellSpursTaskset, etc. From d91927ee9799bd6f338495bbd7bd2803eb8fa071 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Mon, 13 Jan 2025 22:00:50 +0100 Subject: [PATCH 51/60] Qt/input: allow keypresses in raw mouse handler --- rpcs3/Input/raw_mouse_config.cpp | 25 ++++ rpcs3/Input/raw_mouse_config.h | 3 + rpcs3/Input/raw_mouse_handler.cpp | 120 +++++++++++++++++--- rpcs3/Input/raw_mouse_handler.h | 19 +++- rpcs3/rpcs3qt/gui_application.cpp | 2 +- rpcs3/rpcs3qt/raw_mouse_settings_dialog.cpp | 30 +++++ rpcs3/rpcs3qt/raw_mouse_settings_dialog.h | 1 + 7 files changed, 179 insertions(+), 21 deletions(-) diff --git a/rpcs3/Input/raw_mouse_config.cpp b/rpcs3/Input/raw_mouse_config.cpp index 5f9839ac72..cc5506dd16 100644 --- a/rpcs3/Input/raw_mouse_config.cpp +++ b/rpcs3/Input/raw_mouse_config.cpp @@ -43,6 +43,15 @@ std::string raw_mouse_config::get_button_name(std::string_view value) return std::string(value); } + if (value.starts_with(key_prefix)) + { + s64 scan_code{}; + if (try_to_int64(&scan_code, value.substr(key_prefix.size()), s32{smin}, s32{smax})) + { + return get_key_name(static_cast(scan_code)); + } + } + return ""; } @@ -58,6 +67,22 @@ std::string raw_mouse_config::get_button_name(s32 button_code) return ""; } +std::string raw_mouse_config::get_key_name(s32 scan_code) +{ +#ifdef _WIN32 + TCHAR name_buf[MAX_PATH] {}; + if (!GetKeyNameTextW(scan_code, name_buf, MAX_PATH)) + { + cfg_log.error("raw_mouse_config: GetKeyNameText failed: %s", fmt::win_error{GetLastError(), nullptr}); + return {}; + } + return wchar_to_utf8(name_buf); +#else + static_cast(scan_code); + return ""; +#endif +} + raw_mice_config::raw_mice_config() { for (u32 i = 0; i < ::size32(players); i++) diff --git a/rpcs3/Input/raw_mouse_config.h b/rpcs3/Input/raw_mouse_config.h index a237ad2554..2791725637 100644 --- a/rpcs3/Input/raw_mouse_config.h +++ b/rpcs3/Input/raw_mouse_config.h @@ -42,8 +42,11 @@ public: cfg::string& get_button_by_index(int index); cfg::string& get_button(int code); + static constexpr std::string_view key_prefix = "Key "; + static std::string get_button_name(std::string_view value); static std::string get_button_name(s32 button_code); + static std::string get_key_name(s32 scan_code); }; struct raw_mice_config : cfg::node diff --git a/rpcs3/Input/raw_mouse_handler.cpp b/rpcs3/Input/raw_mouse_handler.cpp index 6d24b46bf9..d60dfaec0b 100644 --- a/rpcs3/Input/raw_mouse_handler.cpp +++ b/rpcs3/Input/raw_mouse_handler.cpp @@ -27,17 +27,17 @@ static inline void draw_overlay_cursor(u32 index, s32 x_pos, s32 y_pos, s32 x_ma [[maybe_unused]] static inline void draw_overlay_cursor(u32, s32, s32, s32, s32) {} #endif -#ifdef _WIN32 const std::unordered_map raw_mouse::btn_pairs = { { 0, {}}, - { RI_MOUSE_BUTTON_1_UP, mouse_button{ RI_MOUSE_BUTTON_1_DOWN, RI_MOUSE_BUTTON_1_UP }}, - { RI_MOUSE_BUTTON_2_UP, mouse_button{ RI_MOUSE_BUTTON_2_DOWN, RI_MOUSE_BUTTON_2_UP }}, - { RI_MOUSE_BUTTON_3_UP, mouse_button{ RI_MOUSE_BUTTON_3_DOWN, RI_MOUSE_BUTTON_3_UP }}, - { RI_MOUSE_BUTTON_4_UP, mouse_button{ RI_MOUSE_BUTTON_4_DOWN, RI_MOUSE_BUTTON_4_UP }}, - { RI_MOUSE_BUTTON_5_UP, mouse_button{ RI_MOUSE_BUTTON_5_DOWN, RI_MOUSE_BUTTON_5_UP }}, -}; +#ifdef _WIN32 + { RI_MOUSE_BUTTON_1_UP, mouse_button{ RI_MOUSE_BUTTON_1_DOWN, RI_MOUSE_BUTTON_1_UP, 0, false }}, + { RI_MOUSE_BUTTON_2_UP, mouse_button{ RI_MOUSE_BUTTON_2_DOWN, RI_MOUSE_BUTTON_2_UP, 0, false }}, + { RI_MOUSE_BUTTON_3_UP, mouse_button{ RI_MOUSE_BUTTON_3_DOWN, RI_MOUSE_BUTTON_3_UP, 0, false }}, + { RI_MOUSE_BUTTON_4_UP, mouse_button{ RI_MOUSE_BUTTON_4_DOWN, RI_MOUSE_BUTTON_4_UP, 0, false }}, + { RI_MOUSE_BUTTON_5_UP, mouse_button{ RI_MOUSE_BUTTON_5_DOWN, RI_MOUSE_BUTTON_5_UP, 0, false }}, #endif +}; LOG_CHANNEL(input_log, "Input"); @@ -89,12 +89,19 @@ raw_mouse::mouse_button raw_mouse::get_mouse_button(const cfg::string& button) { const std::string value = button.to_string(); -#ifdef _WIN32 if (const auto it = raw_mouse_button_map.find(value); it != raw_mouse_button_map.cend()) { return ::at32(btn_pairs, it->second); } -#endif + + if (value.starts_with(raw_mouse_config::key_prefix)) + { + s64 scan_code{}; + if (try_to_int64(&scan_code, value.substr(raw_mouse_config::key_prefix.size()), s32{smin}, s32{smax})) + { + return mouse_button{ 0, 0, static_cast(scan_code), true }; + } + } return {}; } @@ -161,6 +168,8 @@ void raw_mouse::update_values(const RAWMOUSE& state) // Get mouse buttons for (const auto& [button, btn] : m_buttons) { + if (btn.is_key) continue; + // Only update the value if either down or up flags are present if ((state.usButtonFlags & btn.down)) { @@ -240,6 +249,30 @@ void raw_mouse::update_values(const RAWMOUSE& state) } } } + +void raw_mouse::update_values(s32 scan_code, bool pressed) +{ + ensure(m_handler != nullptr); + + if (std::exchange(m_reload_requested, false)) + { + reload_config(); + } + + if (m_handler->is_for_gui()) + { + m_handler->key_press_callback(m_device_name, scan_code, pressed); + return; + } + + // Get mouse buttons + for (const auto& [button, btn] : m_buttons) + { + if (!btn.is_key || btn.scan_code != scan_code) return; + + m_handler->Button(m_index, button, pressed); + } +} #endif raw_mouse_handler::~raw_mouse_handler() @@ -419,6 +452,12 @@ void raw_mouse_handler::register_raw_input_devices() .dwFlags = 0, .hwndTarget = mouse.window_handle() }); + raw_input_devices.push_back(RAWINPUTDEVICE { + .usUsagePage = HID_USAGE_PAGE_GENERIC, + .usUsage = HID_USAGE_GENERIC_KEYBOARD, + .dwFlags = 0, + .hwndTarget = mouse.window_handle() + }); { std::lock_guard lock(g_registered_handlers_mutex); @@ -459,6 +498,13 @@ void raw_mouse_handler::unregister_raw_input_devices() const .dwFlags = RIDEV_REMOVE, .hwndTarget = nullptr }); + raw_input_devices.push_back(RAWINPUTDEVICE { + .usUsagePage = HID_USAGE_PAGE_GENERIC, + .usUsage = HID_USAGE_GENERIC_KEYBOARD, + .dwFlags = 0, + .hwndTarget = nullptr + }); + if (!RegisterRawInputDevices(raw_input_devices.data(), ::size32(raw_input_devices), sizeof(RAWINPUTDEVICE))) { input_log.error("raw_mouse_handler: RegisterRawInputDevices (unregister) failed: %s", fmt::win_error{GetLastError(), nullptr}); @@ -566,7 +612,7 @@ void raw_mouse_handler::handle_native_event(const MSG& msg) return; } - if (msg.message != WM_INPUT) + if (msg.message != WM_INPUT && msg.message != WM_KEYDOWN && msg.message != WM_KEYUP) { return; } @@ -585,26 +631,64 @@ void raw_mouse_handler::handle_native_event(const MSG& msg) return; } + if ((raw_input.header.dwType == RIM_TYPEMOUSE || raw_input.header.dwType == RIM_TYPEKEYBOARD) && + g_cfg_raw_mouse.reload_requested.exchange(false)) + { + std::lock_guard lock(m_raw_mutex); + + for (auto& [handle, mouse] : m_raw_mice) + { + mouse.request_reload(); + } + } + switch (raw_input.header.dwType) { case RIM_TYPEMOUSE: { std::lock_guard lock(m_raw_mutex); - if (g_cfg_raw_mouse.reload_requested.exchange(false)) - { - for (auto& [handle, mouse] : m_raw_mice) - { - mouse.request_reload(); - } - } - if (auto it = m_raw_mice.find(raw_input.header.hDevice); it != m_raw_mice.end()) { it->second.update_values(raw_input.data.mouse); } break; } + case RIM_TYPEKEYBOARD: + { + const RAWKEYBOARD& keyboard = raw_input.data.keyboard; + + // Ignore key overrun state and keys not mapped to any virtual key code + if (keyboard.MakeCode == KEYBOARD_OVERRUN_MAKE_CODE || keyboard.VKey >= UCHAR_MAX) + { + break; + } + + WORD scan_code; + + if (keyboard.MakeCode) + { + // Compose the full scan code value with its extended byte + scan_code = MAKEWORD(keyboard.MakeCode & 0x7f, ((keyboard.Flags & RI_KEY_E0) ? 0xe0 : ((keyboard.Flags & RI_KEY_E1) ? 0xe1 : 0x00))); + } + else + { + // Scan code value may be empty for some buttons (for example multimedia buttons) + // Try to get the scan code from the virtual key code + scan_code = LOWORD(MapVirtualKey(keyboard.VKey, MAPVK_VK_TO_VSC_EX)); + } + + const LONG scan_code_extended = static_cast(MAKELPARAM(0, (HIBYTE(scan_code) ? KF_EXTENDED : 0x00) | LOBYTE(scan_code))); + const bool pressed = !(keyboard.Flags & RI_KEY_BREAK); + + std::lock_guard lock(m_raw_mutex); + + for (auto& [handle, mouse] : m_raw_mice) + { + mouse.update_values(scan_code_extended, pressed); + } + break; + } default: { break; diff --git a/rpcs3/Input/raw_mouse_handler.h b/rpcs3/Input/raw_mouse_handler.h index adef7c5624..9ab7945f20 100644 --- a/rpcs3/Input/raw_mouse_handler.h +++ b/rpcs3/Input/raw_mouse_handler.h @@ -22,6 +22,7 @@ public: #ifdef _WIN32 void update_values(const RAWMOUSE& state); + void update_values(s32 scan_code, bool pressed); #endif const std::string& device_name() const { return m_device_name; } @@ -34,11 +35,11 @@ private: { int down = 0; int up = 0; + s32 scan_code = 0; + bool is_key = false; }; -#ifdef _WIN32 static const std::unordered_map btn_pairs; -#endif void reload_config(); static mouse_button get_mouse_button(const cfg::string& button); @@ -86,6 +87,19 @@ public: } } + void set_key_press_callback(std::function cb) + { + m_key_press_callback = std::move(cb); + } + + void key_press_callback(const std::string& device_name, s32 scan_code, bool pressed) + { + if (m_key_press_callback) + { + m_key_press_callback(device_name, scan_code, pressed); + } + } + void update_devices(); #ifdef _WIN32 @@ -107,6 +121,7 @@ private: bool m_is_for_gui = false; std::map m_raw_mice; std::function m_mouse_press_callback; + std::function m_key_press_callback; std::unique_ptr>> m_thread; }; diff --git a/rpcs3/rpcs3qt/gui_application.cpp b/rpcs3/rpcs3qt/gui_application.cpp index b5c2041527..9f5dc53cb7 100644 --- a/rpcs3/rpcs3qt/gui_application.cpp +++ b/rpcs3/rpcs3qt/gui_application.cpp @@ -1187,7 +1187,7 @@ bool gui_application::native_event_filter::nativeEventFilter([[maybe_unused]] co if (eventType == "windows_generic_MSG") { - if (MSG* msg = static_cast(message); msg && msg->message == WM_INPUT) + if (MSG* msg = static_cast(message); msg && (msg->message == WM_INPUT || msg->message == WM_KEYDOWN || msg->message == WM_KEYUP)) { if (auto* handler = g_fxo->try_get(); handler && handler->type == mouse_handler::raw) { diff --git a/rpcs3/rpcs3qt/raw_mouse_settings_dialog.cpp b/rpcs3/rpcs3qt/raw_mouse_settings_dialog.cpp index 3936598995..eaacc9c524 100644 --- a/rpcs3/rpcs3qt/raw_mouse_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/raw_mouse_settings_dialog.cpp @@ -74,6 +74,10 @@ raw_mouse_settings_dialog::raw_mouse_settings_dialog(QWidget* parent) { mouse_press(device_name, button_code, pressed); }); + g_raw_mouse_handler->set_key_press_callback([this](const std::string& device_name, s32 scan_code, bool pressed) + { + key_press(device_name, scan_code, pressed); + }); m_buttons = new QButtonGroup(this); connect(m_buttons, &QButtonGroup::idClicked, this, &raw_mouse_settings_dialog::on_button_click); @@ -374,6 +378,32 @@ void raw_mouse_settings_dialog::mouse_press(const std::string& device_name, s32 reactivate_buttons(); } +void raw_mouse_settings_dialog::key_press(const std::string& device_name, s32 scan_code, bool pressed) +{ + if (m_button_id < 0 || !pressed) // Let's only react to key presses + { + return; + } + + const int player = m_tab_widget->currentIndex(); + const std::string current_device_name = get_current_device_name(player); + + if (device_name != current_device_name) + { + return; + } + + auto& config = ::at32(g_cfg_raw_mouse.players, player); + config->get_button_by_index(m_button_id % button_count).from_string(fmt::format("%s%d", raw_mouse_config::key_prefix, scan_code)); + + if (auto button = m_buttons->button(m_button_id)) + { + button->setText(QString::fromStdString(raw_mouse_config::get_key_name(scan_code))); + } + + reactivate_buttons(); +} + void raw_mouse_settings_dialog::handle_device_change(const std::string& device_name) { if (is_device_active(device_name)) diff --git a/rpcs3/rpcs3qt/raw_mouse_settings_dialog.h b/rpcs3/rpcs3qt/raw_mouse_settings_dialog.h index d55abf8658..16aced5a77 100644 --- a/rpcs3/rpcs3qt/raw_mouse_settings_dialog.h +++ b/rpcs3/rpcs3qt/raw_mouse_settings_dialog.h @@ -31,6 +31,7 @@ private: void reset_config(); void on_button_click(int id); void mouse_press(const std::string& device_name, s32 button_code, bool pressed); + void key_press(const std::string& device_name, s32 scan_code, bool pressed); void handle_device_change(const std::string& device_name); bool is_device_active(const std::string& device_name); std::string get_current_device_name(int player); From 82bbb0cc0535e5a1eec6cf38e818a37588abe6a5 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Tue, 14 Jan 2025 03:03:57 +0100 Subject: [PATCH 52/60] Update libpng to 1.6.45 --- 3rdparty/libpng/libpng | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/libpng/libpng b/3rdparty/libpng/libpng index f5e92d7697..51f5bd68b9 160000 --- a/3rdparty/libpng/libpng +++ b/3rdparty/libpng/libpng @@ -1 +1 @@ -Subproject commit f5e92d76973a7a53f517579bc95d61483bf108c0 +Subproject commit 51f5bd68b9b806d2c92b4318164d28b49357da31 From 2643fbdea4b187a44481e89d3daf64d0a6618a74 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Tue, 14 Jan 2025 03:04:22 +0100 Subject: [PATCH 53/60] Update pugixml to 1.15 --- 3rdparty/pugixml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/pugixml b/3rdparty/pugixml index db78afc2b7..ee86beb30e 160000 --- a/3rdparty/pugixml +++ b/3rdparty/pugixml @@ -1 +1 @@ -Subproject commit db78afc2b7d8f043b4bc6b185635d949ea2ed2a8 +Subproject commit ee86beb30e4973f5feffe3ce63bfa4fbadf72f38 From 19661718387029410755f4b8e2ba347f9a1221ec Mon Sep 17 00:00:00 2001 From: Megamouse Date: Tue, 14 Jan 2025 03:05:13 +0100 Subject: [PATCH 54/60] Use string_view in rXml --- Utilities/File.cpp | 4 ++-- Utilities/rXml.cpp | 7 +++---- Utilities/rXml.h | 4 ++-- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Utilities/File.cpp b/Utilities/File.cpp index 3c4a8c769d..e2dd842ae6 100644 --- a/Utilities/File.cpp +++ b/Utilities/File.cpp @@ -21,7 +21,7 @@ using namespace std::literals::string_literals; #include #include -static std::unique_ptr to_wchar(const std::string& source) +static std::unique_ptr to_wchar(std::string_view source) { // String size + null terminator const usz buf_size = source.size() + 1; @@ -44,7 +44,7 @@ static std::unique_ptr to_wchar(const std::string& source) std::memcpy(buffer.get() + 32768 + 4, L"UNC\\", 4 * sizeof(wchar_t)); } - ensure(MultiByteToWideChar(CP_UTF8, 0, source.c_str(), size, buffer.get() + 32768 + (unc ? 8 : 4), size)); // "to_wchar" + ensure(MultiByteToWideChar(CP_UTF8, 0, source.data(), size, buffer.get() + 32768 + (unc ? 8 : 4), size)); // "to_wchar" // Canonicalize wide path (replace '/', ".", "..", \\ repetitions, etc) ensure(GetFullPathNameW(buffer.get() + 32768, 32768, buffer.get(), nullptr) - 1 < 32768 - 1); // "to_wchar" diff --git a/Utilities/rXml.cpp b/Utilities/rXml.cpp index db52fb38e3..14ac0659f6 100644 --- a/Utilities/rXml.cpp +++ b/Utilities/rXml.cpp @@ -49,12 +49,11 @@ std::string rXmlNode::GetName() return {}; } -std::string rXmlNode::GetAttribute(const std::string& name) +std::string rXmlNode::GetAttribute(std::string_view name) { if (handle) { - const auto pred = [&name](const pugi::xml_attribute& attr) { return (name == attr.name()); }; - if (const pugi::xml_attribute attr = handle.find_attribute(pred)) + if (const pugi::xml_attribute attr = handle.attribute(name)) { if (const pugi::char_t* value = attr.value()) { @@ -86,7 +85,7 @@ rXmlDocument::rXmlDocument() { } -pugi::xml_parse_result rXmlDocument::Read(const std::string& data) +pugi::xml_parse_result rXmlDocument::Read(std::string_view data) { if (handle) { diff --git a/Utilities/rXml.h b/Utilities/rXml.h index 71ca257902..8b70d06ee4 100644 --- a/Utilities/rXml.h +++ b/Utilities/rXml.h @@ -23,7 +23,7 @@ struct rXmlNode std::shared_ptr GetChildren(); std::shared_ptr GetNext(); std::string GetName(); - std::string GetAttribute(const std::string& name); + std::string GetAttribute(std::string_view name); std::string GetNodeContent(); pugi::xml_node handle{}; @@ -34,7 +34,7 @@ struct rXmlDocument rXmlDocument(); rXmlDocument(const rXmlDocument& other) = delete; rXmlDocument &operator=(const rXmlDocument& other) = delete; - pugi::xml_parse_result Read(const std::string& data); + pugi::xml_parse_result Read(std::string_view data); virtual std::shared_ptr GetRoot(); pugi::xml_document handle{}; From 0cc61d0ef0440c6edc6c60e90b55eafe3dd3243a Mon Sep 17 00:00:00 2001 From: Megamouse Date: Tue, 14 Jan 2025 04:24:02 +0100 Subject: [PATCH 55/60] windows: check for OneDrive paths --- rpcs3/main.cpp | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/rpcs3/main.cpp b/rpcs3/main.cpp index 404fa66eb9..f67670ccc9 100644 --- a/rpcs3/main.cpp +++ b/rpcs3/main.cpp @@ -111,6 +111,65 @@ extern char **environ; LOG_CHANNEL(sys_log, "SYS"); LOG_CHANNEL(q_debug, "QDEBUG"); +#ifdef _WIN32 +std::set get_one_drive_paths() +{ + std::set paths; + for (const char* key : { "OneDrive", "OneDriveConsumer", "OneDriveCommercial" }) + { + if (const char* env_path = std::getenv(key)) + { + sys_log.notice("get_one_drive_paths: Found OneDrive env path: '%s' (key='%s')", env_path, key); + paths.insert(env_path); + } + } + + for (const wchar_t* key : { L"Software\\Microsoft\\OneDrive\\Accounts\\Personal" }) + { + HKEY hkey = NULL; + LSTATUS status = RegOpenKeyW(HKEY_CURRENT_USER, key, &hkey); + if (status != ERROR_SUCCESS) + { + sys_log.trace("get_one_drive_paths: RegOpenKeyW failed: %s (key='%s')", fmt::win_error{static_cast(status), nullptr}, wchar_to_utf8(key)); + continue; + } + + std::wstring path_buffer; + static_cast(path_buffer.size() - 1); + DWORD type = 0U; + + do + { + path_buffer.resize(path_buffer.size() + MAX_PATH); + DWORD buffer_size = static_cast(path_buffer.size() - 1); + status = RegQueryValueExW(hkey, L"UserFolder", NULL, &type, reinterpret_cast(path_buffer.data()), &buffer_size); + } + while (status == ERROR_MORE_DATA); + + const LSTATUS close_status = RegCloseKey(hkey); + if (close_status != ERROR_SUCCESS) + { + sys_log.error("get_one_drive_paths: RegCloseKey failed: %s", fmt::win_error{static_cast(close_status), nullptr}); + } + + if (status != ERROR_SUCCESS) + { + sys_log.error("get_one_drive_paths: RegQueryValueExW failed: %s", fmt::win_error{static_cast(status), nullptr}); + continue; + } + + if ((type == REG_SZ) || (type == REG_EXPAND_SZ) || (type == REG_MULTI_SZ)) + { + const std::string path = wchar_to_utf8(path_buffer.data()); + sys_log.notice("get_one_drive_paths: Found OneDrive registry path: '%s' (key='%s')", path, wchar_to_utf8(key)); + paths.insert(path); + } + } + + return paths; +} +#endif + [[noreturn]] extern void report_fatal_error(std::string_view _text, bool is_html = false, bool include_help_text = true) { #ifdef __linux__ @@ -1135,6 +1194,21 @@ int main(int argc, char** argv) return 1; } } + +#ifdef _WIN32 + // Check OneDrive locations + for (const std::string& one_drive_path : get_one_drive_paths()) + { + if (Emu.IsPathInsideDir(emu_dir, one_drive_path)) + { + report_fatal_error(QObject::tr( + "RPCS3 should never be run from a OneDrive path!\n" + "Please move RPCS3 to a location not synced by OneDrive.\n" + "Current location:\n%0").arg(QString::fromStdString(emu_dir)).toStdString()); + return 1; + } + } +#endif } // Set timerslack value for Linux. The default value is 50,000ns. Change this to just 1 since we value precise timers. From 7965a0313cb70e1aa450492bb34cd2feaf9a9868 Mon Sep 17 00:00:00 2001 From: kd-11 Date: Sat, 11 Jan 2025 02:46:53 +0300 Subject: [PATCH 56/60] rsx: Improved shader interpreter support when hw instancing is active --- rpcs3/Emu/RSX/Core/RSXDrawCommands.cpp | 15 ++++++++------- rpcs3/Emu/RSX/Core/RSXDrawCommands.h | 2 +- rpcs3/Emu/RSX/GL/GLGSRender.cpp | 14 ++++++++++---- rpcs3/Emu/RSX/VK/VKGSRender.cpp | 21 +++++++++++++-------- 4 files changed, 32 insertions(+), 20 deletions(-) diff --git a/rpcs3/Emu/RSX/Core/RSXDrawCommands.cpp b/rpcs3/Emu/RSX/Core/RSXDrawCommands.cpp index 095903832d..7be332fc60 100644 --- a/rpcs3/Emu/RSX/Core/RSXDrawCommands.cpp +++ b/rpcs3/Emu/RSX/Core/RSXDrawCommands.cpp @@ -735,7 +735,7 @@ namespace rsx utils::stream_vector(dst + 4, 0u, fog_mode, std::bit_cast(wpos_scale), std::bit_cast(wpos_bias)); } - void draw_command_processor::fill_constants_instancing_buffer(rsx::io_buffer& indirection_table_buf, rsx::io_buffer& constants_data_array_buffer, const VertexProgramBase& prog) const + void draw_command_processor::fill_constants_instancing_buffer(rsx::io_buffer& indirection_table_buf, rsx::io_buffer& constants_data_array_buffer, const VertexProgramBase* prog) const { auto& draw_call = REGS(m_ctx)->current_draw_clause; @@ -745,8 +745,9 @@ namespace rsx // Temp indirection table. Used to track "running" updates. rsx::simple_array instancing_indirection_table; // indirection table size - const auto reloc_table = prog.has_indexed_constants ? decltype(prog.constant_ids){} : prog.constant_ids; - const auto redirection_table_size = prog.has_indexed_constants ? 468u : ::size32(prog.constant_ids); + const auto full_reupload = !prog || prog->has_indexed_constants; + const auto reloc_table = full_reupload ? decltype(prog->constant_ids){} : prog->constant_ids; + const auto redirection_table_size = full_reupload ? 468u : ::size32(prog->constant_ids); instancing_indirection_table.resize(redirection_table_size); // Temp constants data @@ -787,9 +788,9 @@ namespace rsx continue; } - const int translated_offset = prog.has_indexed_constants + const int translated_offset = full_reupload ? instance_config.patch_load_offset - : prog.TranslateConstantsRange(instance_config.patch_load_offset, instance_config.patch_load_count); + : prog->TranslateConstantsRange(instance_config.patch_load_offset, instance_config.patch_load_count); if (translated_offset >= 0) { @@ -809,14 +810,14 @@ namespace rsx continue; } - ensure(!prog.has_indexed_constants); + ensure(!full_reupload); // Sparse update. Update records individually instead of bulk // FIXME: Range batching optimization const auto load_end = instance_config.patch_load_offset + instance_config.patch_load_count; for (u32 i = 0; i < redirection_table_size; ++i) { - const auto read_index = prog.constant_ids[i]; + const auto read_index = prog->constant_ids[i]; if (read_index < instance_config.patch_load_offset || read_index >= load_end) { // Reading outside "hot" range. diff --git a/rpcs3/Emu/RSX/Core/RSXDrawCommands.h b/rpcs3/Emu/RSX/Core/RSXDrawCommands.h index b69a918401..5bc5991a18 100644 --- a/rpcs3/Emu/RSX/Core/RSXDrawCommands.h +++ b/rpcs3/Emu/RSX/Core/RSXDrawCommands.h @@ -105,6 +105,6 @@ namespace rsx // Fill instancing buffers. A single iobuf is used for both. 256byte alignment enforced to allow global bind // Returns offsets to the index redirection lookup table and constants field array - void fill_constants_instancing_buffer(rsx::io_buffer& indirection_table_buf, rsx::io_buffer& constants_data_array_buffer, const VertexProgramBase& prog) const; + void fill_constants_instancing_buffer(rsx::io_buffer& indirection_table_buf, rsx::io_buffer& constants_data_array_buffer, const VertexProgramBase* prog) const; }; } diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.cpp b/rpcs3/Emu/RSX/GL/GLGSRender.cpp index 90bc7cacbd..34f0380ad3 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.cpp +++ b/rpcs3/Emu/RSX/GL/GLGSRender.cpp @@ -878,7 +878,7 @@ void GLGSRender::load_program_env() } } - if (update_fragment_constants && !update_instruction_buffers) + if (update_fragment_constants && !m_shader_interpreter.is_interpreter(m_program)) { // Fragment constants auto mapping = m_fragment_constants_buffer->alloc_from_heap(fragment_constants_size, m_uniform_buffer_offset_align); @@ -978,12 +978,18 @@ void GLGSRender::load_program_env() } } - m_graphics_state.clear( + rsx::flags32_t handled_flags = rsx::pipeline_state::fragment_state_dirty | rsx::pipeline_state::vertex_state_dirty | rsx::pipeline_state::transform_constants_dirty | - rsx::pipeline_state::fragment_constants_dirty | - rsx::pipeline_state::fragment_texture_state_dirty); + rsx::pipeline_state::fragment_texture_state_dirty; + + if (update_fragment_constants && !m_shader_interpreter.is_interpreter(m_program)) + { + handled_flags |= rsx::pipeline_state::fragment_constants_dirty; + } + + m_graphics_state.clear(handled_flags); } bool GLGSRender::is_current_program_interpreted() const diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.cpp b/rpcs3/Emu/RSX/VK/VKGSRender.cpp index 6e53891fbf..ea3d83cdf3 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.cpp +++ b/rpcs3/Emu/RSX/VK/VKGSRender.cpp @@ -688,10 +688,10 @@ VKGSRender::VKGSRender(utils::serial* ar) noexcept : GSRender(ar) } // Initialize optional allocation information with placeholders - m_vertex_env_buffer_info = { m_vertex_env_ring_info.heap->value, 0, 32 }; - m_vertex_constants_buffer_info = { m_transform_constants_ring_info.heap->value, 0, 32 }; - m_fragment_env_buffer_info = { m_fragment_env_ring_info.heap->value, 0, 32 }; - m_fragment_texture_params_buffer_info = { m_fragment_texture_params_ring_info.heap->value, 0, 32 }; + m_vertex_env_buffer_info = { m_vertex_env_ring_info.heap->value, 0, 16 }; + m_vertex_constants_buffer_info = { m_transform_constants_ring_info.heap->value, 0, 16 }; + m_fragment_env_buffer_info = { m_fragment_env_ring_info.heap->value, 0, 16 }; + m_fragment_texture_params_buffer_info = { m_fragment_texture_params_ring_info.heap->value, 0, 16 }; m_raster_env_buffer_info = { m_raster_env_ring_info.heap->value, 0, 128 }; const auto limits = m_device->gpu().get_limits(); @@ -2192,7 +2192,7 @@ void VKGSRender::load_program_env() return std::make_pair(m_instancing_buffer_ring_info.map(constants_data_table_offset, size), size); }); - m_draw_processor.fill_constants_instancing_buffer(indirection_table_buf, constants_array_buf, *m_vertex_prog); + m_draw_processor.fill_constants_instancing_buffer(indirection_table_buf, constants_array_buf, m_vertex_prog); m_instancing_buffer_ring_info.unmap(); m_instancing_indirection_buffer_info = { m_instancing_buffer_ring_info.heap->value, indirection_table_offset, indirection_table_buf.size() }; @@ -2219,7 +2219,7 @@ void VKGSRender::load_program_env() } } - if (update_fragment_constants && !update_instruction_buffers) + if (update_fragment_constants && !m_shader_interpreter.is_interpreter(m_program)) { check_heap_status(VK_HEAP_CHECK_FRAGMENT_CONSTANTS_STORAGE); @@ -2350,9 +2350,9 @@ void VKGSRender::load_program_env() } // Clear flags - u32 handled_flags = rsx::pipeline_state::fragment_state_dirty | + rsx::flags32_t handled_flags = + rsx::pipeline_state::fragment_state_dirty | rsx::pipeline_state::vertex_state_dirty | - rsx::pipeline_state::fragment_constants_dirty | rsx::pipeline_state::fragment_texture_state_dirty; if (!update_instancing_data) @@ -2360,6 +2360,11 @@ void VKGSRender::load_program_env() handled_flags |= rsx::pipeline_state::transform_constants_dirty; } + if (update_fragment_constants && !m_shader_interpreter.is_interpreter(m_program)) + { + handled_flags |= rsx::pipeline_state::fragment_constants_dirty; + } + m_graphics_state.clear(handled_flags); } From e1c6df0df3d5cedbc053a0e8bdde9c4852ca6a68 Mon Sep 17 00:00:00 2001 From: kd-11 Date: Sat, 11 Jan 2025 21:02:13 +0300 Subject: [PATCH 57/60] rsx: Fix dynamic constants patching when draw call is skipped - Also adds an optimization to skip reuploads when patch occurs outside our critical range. --- rpcs3/Emu/RSX/Core/RSXDrawCommands.cpp | 2 +- rpcs3/Emu/RSX/GL/GLGSRender.cpp | 10 +++++++-- rpcs3/Emu/RSX/Program/program_util.cpp | 29 +++++++++++++++++++++++++- rpcs3/Emu/RSX/Program/program_util.h | 5 ++++- rpcs3/Emu/RSX/VK/VKGSRender.cpp | 10 +++++++-- 5 files changed, 49 insertions(+), 7 deletions(-) diff --git a/rpcs3/Emu/RSX/Core/RSXDrawCommands.cpp b/rpcs3/Emu/RSX/Core/RSXDrawCommands.cpp index 7be332fc60..389efe0a2e 100644 --- a/rpcs3/Emu/RSX/Core/RSXDrawCommands.cpp +++ b/rpcs3/Emu/RSX/Core/RSXDrawCommands.cpp @@ -790,7 +790,7 @@ namespace rsx const int translated_offset = full_reupload ? instance_config.patch_load_offset - : prog->TranslateConstantsRange(instance_config.patch_load_offset, instance_config.patch_load_count); + : prog->translate_constants_range(instance_config.patch_load_offset, instance_config.patch_load_count); if (translated_offset >= 0) { diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.cpp b/rpcs3/Emu/RSX/GL/GLGSRender.cpp index 34f0380ad3..c37228afe9 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.cpp +++ b/rpcs3/Emu/RSX/GL/GLGSRender.cpp @@ -1045,13 +1045,19 @@ void GLGSRender::update_vertex_env(const gl::vertex_upload_info& upload_info) void GLGSRender::patch_transform_constants(rsx::context* ctx, u32 index, u32 count) { - if (!m_vertex_prog) + if (!m_program || !m_vertex_prog) { // Shouldn't be reachable, but handle it correctly anyway m_graphics_state |= rsx::pipeline_state::transform_constants_dirty; return; } + if (!m_vertex_prog->overlaps_constants_range(index, count)) + { + // Nothing meaningful to us + return; + } + std::pair data_range {}; void* data_source = nullptr; const auto bound_range = m_transform_constants_buffer->bound_range(); @@ -1065,7 +1071,7 @@ void GLGSRender::patch_transform_constants(rsx::context* ctx, u32 index, u32 cou data_range = { bound_range.first + byte_offset, byte_count}; data_source = ®S(ctx)->transform_constants[index]; } - else if (auto xform_id = m_vertex_prog->TranslateConstantsRange(index, count); xform_id >= 0) + else if (auto xform_id = m_vertex_prog->translate_constants_range(index, count); xform_id >= 0) { const auto write_offset = xform_id * 16; const auto byte_count = count * 16; diff --git a/rpcs3/Emu/RSX/Program/program_util.cpp b/rpcs3/Emu/RSX/Program/program_util.cpp index 9f44f05505..e5daf319fc 100644 --- a/rpcs3/Emu/RSX/Program/program_util.cpp +++ b/rpcs3/Emu/RSX/Program/program_util.cpp @@ -110,7 +110,7 @@ namespace rsx multisampled_textures == other.multisampled_textures; } - int VertexProgramBase::TranslateConstantsRange(int first_index, int count) const + int VertexProgramBase::translate_constants_range(int first_index, int count) const { // The constant ids should be sorted, so just find the first one and check for continuity int index = -1; @@ -157,4 +157,31 @@ namespace rsx // OOB or partial match return -1; } + + bool VertexProgramBase::overlaps_constants_range(int first_index, int count) const + { + if (has_indexed_constants) + { + return true; + } + + const int last_index = first_index + count - 1; + + // Early rejection test + if (constant_ids.empty() || first_index > constant_ids.back() || last_index < first_index) + { + return false; + } + + // Check for any hits + for (auto& idx : constant_ids) + { + if (idx >= first_index && idx <= last_index) + { + return true; + } + } + + return false; + } } diff --git a/rpcs3/Emu/RSX/Program/program_util.h b/rpcs3/Emu/RSX/Program/program_util.h index c8011a3db2..8159b27845 100644 --- a/rpcs3/Emu/RSX/Program/program_util.h +++ b/rpcs3/Emu/RSX/Program/program_util.h @@ -67,6 +67,9 @@ namespace rsx // Translates an incoming range of constants against our mapping. // If there is no linear mapping available, return -1, otherwise returns the translated index of the first slot // TODO: Move this somewhere else during refactor - int TranslateConstantsRange(int first_index, int count) const; + int translate_constants_range(int first_index, int count) const; + + // Returns true if this program consumes any constants in the range [first, first + count - 1] + bool overlaps_constants_range(int first_index, int count) const; }; } diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.cpp b/rpcs3/Emu/RSX/VK/VKGSRender.cpp index ea3d83cdf3..8ebb41c099 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.cpp +++ b/rpcs3/Emu/RSX/VK/VKGSRender.cpp @@ -2443,13 +2443,19 @@ void VKGSRender::update_vertex_env(u32 id, const vk::vertex_upload_info& vertex_ void VKGSRender::patch_transform_constants(rsx::context* ctx, u32 index, u32 count) { - if (!m_vertex_prog) + if (!m_program || !m_vertex_prog) { // Shouldn't be reachable, but handle it correctly anyway m_graphics_state |= rsx::pipeline_state::transform_constants_dirty; return; } + if (!m_vertex_prog->overlaps_constants_range(index, count)) + { + // Nothing meaningful to us + return; + } + // Hot-patching transform constants mid-draw (instanced draw) std::pair data_range; void* data_source = nullptr; @@ -2463,7 +2469,7 @@ void VKGSRender::patch_transform_constants(rsx::context* ctx, u32 index, u32 cou data_range = { m_vertex_constants_buffer_info.offset + byte_offset, byte_count }; data_source = ®S(ctx)->transform_constants[index]; } - else if (auto xform_id = m_vertex_prog->TranslateConstantsRange(index, count); xform_id >= 0) + else if (auto xform_id = m_vertex_prog->translate_constants_range(index, count); xform_id >= 0) { const auto write_offset = xform_id * 16; const auto byte_count = count * 16; From 7da0822419db57700acfcb9bcb000955d591b979 Mon Sep 17 00:00:00 2001 From: kd-11 Date: Sun, 12 Jan 2025 15:51:15 +0300 Subject: [PATCH 58/60] rsx/shaders: Fix MSAA coords wrapping when input is negative --- .../GLSLSnippets/RSXProg/RSXFragmentTextureMSAAOps.glsl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXFragmentTextureMSAAOps.glsl b/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXFragmentTextureMSAAOps.glsl index d250e2efb4..8575508f54 100644 --- a/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXFragmentTextureMSAAOps.glsl +++ b/rpcs3/Emu/RSX/Program/GLSLSnippets/RSXProg/RSXFragmentTextureMSAAOps.glsl @@ -34,7 +34,8 @@ vec2 texture2DMSCoord(const in vec2 coords, const in uint flags) return coords; } - const vec2 wrapped_coords = mod(coords, vec2(1.0)); + const vec2 wrapped_coords_raw = mod(coords, vec2(1.0)); + const vec2 wrapped_coords = mod(wrapped_coords_raw + vec2(1.0), vec2(1.0)); const bvec2 wrap_control_mask = bvec2(uvec2(flags) & uvec2(WRAP_S_MASK, WRAP_T_MASK)); return _select(coords, wrapped_coords, wrap_control_mask); } From efc9ae98ed5ddf488b555fd84b318762e68483f6 Mon Sep 17 00:00:00 2001 From: kd-11 Date: Tue, 31 Dec 2024 00:36:31 +0300 Subject: [PATCH 59/60] vk: Fix out-of-bounds write when emitting MSAA unresolve clear command for NVIDIA fallback --- rpcs3/Emu/RSX/VK/VKResolveHelper.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/rpcs3/Emu/RSX/VK/VKResolveHelper.h b/rpcs3/Emu/RSX/VK/VKResolveHelper.h index ab9e827db9..6b83a5af9c 100644 --- a/rpcs3/Emu/RSX/VK/VKResolveHelper.h +++ b/rpcs3/Emu/RSX/VK/VKResolveHelper.h @@ -387,7 +387,7 @@ namespace vk struct stencilonly_unresolve : depth_resolve_base { - VkClearRect region{}; + VkClearRect clear_region{}; VkClearAttachment clear_info{}; stencilonly_unresolve() @@ -402,8 +402,8 @@ namespace vk renderpass_config.set_depth_mask(false); clear_info.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT; - region.baseArrayLayer = 0; - region.layerCount = 1; + clear_region.baseArrayLayer = 0; + clear_region.layerCount = 1; static_parameters_width = 3; @@ -425,7 +425,7 @@ namespace vk void emit_geometry(vk::command_buffer& cmd) override { - vkCmdClearAttachments(cmd, 1, &clear_info, 1, ®ion); + vkCmdClearAttachments(cmd, 1, &clear_info, 1, &clear_region); for (s32 write_mask = 0x1; write_mask <= 0x80; write_mask <<= 1) { @@ -444,8 +444,8 @@ namespace vk auto stencil_view = resolve_image->get_view(rsx::default_remap_vector.with_encoding(VK_REMAP_IDENTITY), VK_IMAGE_ASPECT_STENCIL_BIT); - region.rect.extent.width = resolve_image->width(); - region.rect.extent.height = resolve_image->height(); + clear_region.rect.extent.width = msaa_image->width(); + clear_region.rect.extent.height = msaa_image->height(); overlay_pass::run( cmd, From 09580594e68619dc6421b56557e640648f5b2546 Mon Sep 17 00:00:00 2001 From: kd-11 Date: Thu, 16 Jan 2025 01:03:42 +0300 Subject: [PATCH 60/60] rsx: Fix shader cache loading --- rpcs3/Emu/RSX/rsx_cache.h | 1 + 1 file changed, 1 insertion(+) diff --git a/rpcs3/Emu/RSX/rsx_cache.h b/rpcs3/Emu/RSX/rsx_cache.h index 7f82c210d9..f5eca2c85b 100644 --- a/rpcs3/Emu/RSX/rsx_cache.h +++ b/rpcs3/Emu/RSX/rsx_cache.h @@ -400,6 +400,7 @@ namespace rsx fp.texture_state.multisampled_textures = data.fp_multisampled_textures; fp.texcoord_control_mask = data.fp_texcoord_control; fp.two_sided_lighting = !!(data.fp_lighting_flags & 0x1); + fp.mrt_buffers_count = data.fp_mrt_count; return result; }