From b90bacba4870534dfc501a51119b5913337a5e95 Mon Sep 17 00:00:00 2001 From: Ani Date: Mon, 21 Jul 2025 12:35:40 +0200 Subject: [PATCH 001/149] vk: Fix textureCompressionBC check for v3dv/panvk --- rpcs3/Emu/RSX/VK/vkutils/device.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpcs3/Emu/RSX/VK/vkutils/device.cpp b/rpcs3/Emu/RSX/VK/vkutils/device.cpp index 907d692e85..f5f2745ab4 100644 --- a/rpcs3/Emu/RSX/VK/vkutils/device.cpp +++ b/rpcs3/Emu/RSX/VK/vkutils/device.cpp @@ -586,7 +586,7 @@ namespace vk // enabled_features.shaderCullDistance = VK_TRUE; // Alt notation of clip distance enabled_features.samplerAnisotropy = VK_TRUE; - enabled_features.textureCompressionBC = pgpu->optional_features_support.texture_compression_bc; + enabled_features.textureCompressionBC = pgpu->features.textureCompressionBC; enabled_features.shaderStorageBufferArrayDynamicIndexing = VK_TRUE; // Optionally disable unsupported stuff From 3fd417e230ee8cd7b78f998df79fad2062fd90dc Mon Sep 17 00:00:00 2001 From: Megamouse Date: Mon, 28 Jul 2025 20:40:36 +0200 Subject: [PATCH 002/149] Update curl to 8.15.0 --- 3rdparty/curl/curl | 2 +- 3rdparty/curl/libcurl.vcxproj | 11 ++++----- 3rdparty/curl/libcurl.vcxproj.filters | 33 ++++++++++++--------------- 3 files changed, 21 insertions(+), 25 deletions(-) diff --git a/3rdparty/curl/curl b/3rdparty/curl/curl index fdb8a789d2..cfbfb65047 160000 --- a/3rdparty/curl/curl +++ b/3rdparty/curl/curl @@ -1 +1 @@ -Subproject commit fdb8a789d2b446b77bd7cdd2eff95f6cbc814cf4 +Subproject commit cfbfb65047e85e6b08af65fe9cdbcf68e9ad496a diff --git a/3rdparty/curl/libcurl.vcxproj b/3rdparty/curl/libcurl.vcxproj index c4a96abdc4..be87a73c8d 100644 --- a/3rdparty/curl/libcurl.vcxproj +++ b/3rdparty/curl/libcurl.vcxproj @@ -79,6 +79,7 @@ + @@ -86,6 +87,7 @@ + @@ -144,7 +146,6 @@ - @@ -225,7 +226,6 @@ - @@ -233,7 +233,6 @@ - @@ -271,8 +270,10 @@ + + @@ -280,6 +281,7 @@ + @@ -345,7 +347,6 @@ - @@ -415,14 +416,12 @@ - - diff --git a/3rdparty/curl/libcurl.vcxproj.filters b/3rdparty/curl/libcurl.vcxproj.filters index 32eb05f40e..b3e2104a46 100644 --- a/3rdparty/curl/libcurl.vcxproj.filters +++ b/3rdparty/curl/libcurl.vcxproj.filters @@ -159,9 +159,6 @@ Source Files - - Source Files - Source Files @@ -339,9 +336,6 @@ Source Files - - Source Files - Source Files @@ -360,9 +354,6 @@ Source Files - - Source Files - Source Files @@ -549,6 +540,12 @@ Source Files + + Source Files + + + Source Files + @@ -758,9 +755,6 @@ Header Files - - Header Files - Header Files @@ -899,9 +893,6 @@ Header Files - - Header Files - Header Files @@ -917,9 +908,6 @@ Header Files - - Header Files - Header Files @@ -1112,6 +1100,15 @@ Header Files + + Header Files + + + Header Files + + + Header Files + From 99b3c45f05845de13f7136414a7e70c44e9fcd70 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Mon, 28 Jul 2025 20:41:00 +0200 Subject: [PATCH 003/149] Update wolfssl to 5.8.2 --- 3rdparty/wolfssl/wolfssl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/wolfssl/wolfssl b/3rdparty/wolfssl/wolfssl index b077c81eb6..decea12e22 160000 --- a/3rdparty/wolfssl/wolfssl +++ b/3rdparty/wolfssl/wolfssl @@ -1 +1 @@ -Subproject commit b077c81eb635392e694ccedbab8b644297ec0285 +Subproject commit decea12e223869c8f8f3ab5a53dc90b69f436eb2 From e319ca299820cb4009386059f2ba830a9b698274 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Mon, 28 Jul 2025 20:41:15 +0200 Subject: [PATCH 004/149] Update SDL to 3.2.18 --- 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 c9a6709bd2..68bfcb6c54 160000 --- a/3rdparty/libsdl-org/SDL +++ b/3rdparty/libsdl-org/SDL @@ -1 +1 @@ -Subproject commit c9a6709bd21750f1ad9597be21abace78c6378c9 +Subproject commit 68bfcb6c5419f51104e74e72ea0f8d405a4615b0 From daad30b2b3969ebc1b7c0e5ff490ca93c3edcae3 Mon Sep 17 00:00:00 2001 From: Takuya Wakazono Date: Mon, 5 May 2025 01:14:03 +0900 Subject: [PATCH 005/149] Fix USE_SYSTEM_OPENAL to use system headers Even when USE_SYSTEM_OPENAL was enabled, the code directly included the bundled OpenAL headers, bypassing the system headers. Since `#include ` is not portable, use `#include "al.h"` instead. https://cmake.org/cmake/help/latest/module/FindOpenAL.html --- rpcs3/Emu/Cell/Modules/cellMic.cpp | 2 +- rpcs3/Emu/Cell/Modules/cellMic.h | 3 ++- rpcs3/rpcs3qt/microphone_creator.cpp | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellMic.cpp b/rpcs3/Emu/Cell/Modules/cellMic.cpp index a5ece1be59..0724b48927 100644 --- a/rpcs3/Emu/Cell/Modules/cellMic.cpp +++ b/rpcs3/Emu/Cell/Modules/cellMic.cpp @@ -11,7 +11,7 @@ #include #ifndef WITHOUT_OPENAL -#include "3rdparty/OpenAL/openal-soft/include/AL/alext.h" +#include "alext.h" #endif LOG_CHANNEL(cellMic); diff --git a/rpcs3/Emu/Cell/Modules/cellMic.h b/rpcs3/Emu/Cell/Modules/cellMic.h index e4b416fa6a..88a2f4d937 100644 --- a/rpcs3/Emu/Cell/Modules/cellMic.h +++ b/rpcs3/Emu/Cell/Modules/cellMic.h @@ -1,9 +1,10 @@ #pragma once #include "Utilities/Thread.h" -#include "3rdparty/OpenAL/openal-soft/include/AL/alc.h" #include "Utilities/mutex.h" +#include "alc.h" + // Error Codes enum CellMicInError : u32 { diff --git a/rpcs3/rpcs3qt/microphone_creator.cpp b/rpcs3/rpcs3qt/microphone_creator.cpp index 9ea04defde..2821fd82e4 100644 --- a/rpcs3/rpcs3qt/microphone_creator.cpp +++ b/rpcs3/rpcs3qt/microphone_creator.cpp @@ -3,8 +3,8 @@ #include "Utilities/StrUtil.h" -#include "3rdparty/OpenAL/openal-soft/include/AL/al.h" -#include "3rdparty/OpenAL/openal-soft/include/AL/alc.h" +#include "al.h" +#include "alc.h" LOG_CHANNEL(cfg_log, "CFG"); From 81b33625f9e0f27d11247837ab5806ea3562e4f1 Mon Sep 17 00:00:00 2001 From: Takuya Wakazono Date: Wed, 7 May 2025 01:10:23 +0900 Subject: [PATCH 006/149] Set USE_SYSTEM_OPENAL default to OFF on macOS and Windows On Linux, using system libraries is generally preferred, but this is less relevant on macOS and Windows. In particular, macOS does not provide "alext.h" in its OpenAL framework, which causes the build to fail. --- CMakeLists.txt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 81b2a84ee6..a3c14bcd87 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,6 +22,12 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") endif() endif() +if(APPLE OR WIN32) + set(USE_SYSTEM_OPENAL_DEFAULT OFF) +else() + set(USE_SYSTEM_OPENAL_DEFAULT ON) +endif() + option(USE_NATIVE_INSTRUCTIONS "USE_NATIVE_INSTRUCTIONS makes rpcs3 compile with -march=native, which is useful for local builds, but not good for packages." ON) option(WITH_LLVM "Enable usage of LLVM library" ON) option(BUILD_LLVM "Build LLVM from git submodule" OFF) @@ -40,7 +46,7 @@ option(USE_SYSTEM_FLATBUFFERS "Prefer system flatbuffers instead of the builtin option(USE_SYSTEM_LIBPNG "Prefer system libpng instead of the builtin one" OFF) option(USE_SYSTEM_LIBUSB "Prefer system libusb instead of the builtin one" OFF) option(USE_SYSTEM_MVK "Prefer system MoltenVK instead of the builtin one" OFF) -option(USE_SYSTEM_OPENAL "Prefer system OpenAL instead of the prebuild one" ON) +option(USE_SYSTEM_OPENAL "Prefer system OpenAL instead of the prebuild one" ${USE_SYSTEM_OPENAL_DEFAULT}) option(USE_SYSTEM_OPENCV "Prefer system OpenCV instead of the builtin one" ON) option(USE_SYSTEM_PUGIXML "Prefer system pugixml instead of the builtin one" OFF) option(USE_SYSTEM_SDL "Prefer system SDL instead of the builtin one" ON) From aa7758a20fec5cf26b8bab43fb8fc6804d821763 Mon Sep 17 00:00:00 2001 From: Marcin Serwin Date: Sat, 2 Aug 2025 12:28:40 +0200 Subject: [PATCH 007/149] Add option to use system MiniUPnPc Signed-off-by: Marcin Serwin --- 3rdparty/CMakeLists.txt | 2 +- 3rdparty/miniupnp/CMakeLists.txt | 27 ++++++++++++++++++++------- CMakeLists.txt | 1 + 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt index 94c6069271..81a81cb204 100644 --- a/3rdparty/CMakeLists.txt +++ b/3rdparty/CMakeLists.txt @@ -388,7 +388,7 @@ add_library(3rdparty::wolfssl ALIAS wolfssl) add_library(3rdparty::libcurl ALIAS 3rdparty_libcurl) add_library(3rdparty::soundtouch ALIAS soundtouch) add_library(3rdparty::sdl3 ALIAS ${SDL3_TARGET}) -add_library(3rdparty::miniupnpc ALIAS libminiupnpc-static) +add_library(3rdparty::miniupnpc ALIAS 3rdparty_miniupnpc) add_library(3rdparty::rtmidi ALIAS rtmidi) add_library(3rdparty::opencv ALIAS ${OPENCV_TARGET}) add_library(3rdparty::fusion ALIAS Fusion) diff --git a/3rdparty/miniupnp/CMakeLists.txt b/3rdparty/miniupnp/CMakeLists.txt index 93c3dc55cd..c40d4a5ebd 100644 --- a/3rdparty/miniupnp/CMakeLists.txt +++ b/3rdparty/miniupnp/CMakeLists.txt @@ -1,8 +1,21 @@ -option (UPNPC_BUILD_STATIC "Build static library" TRUE) -option (UPNPC_BUILD_SHARED "Build shared library" FALSE) -option (UPNPC_BUILD_TESTS "Build test executables" FALSE) -option (UPNPC_BUILD_SAMPLE "Build sample executables" FALSE) -option (NO_GETADDRINFO "Define NO_GETADDRINFO" FALSE) -option (UPNPC_NO_INSTALL "Disable installation" TRUE) +if(USE_SYSTEM_MINIUPNPC) + message(STATUS "RPCS3: using shared MiniUPnPc") + pkg_check_modules(MiniUPnPc REQUIRED IMPORTED_TARGET miniupnpc>=2.3.3) + add_library(3rdparty_miniupnpc INTERFACE) + target_link_libraries(3rdparty_miniupnpc INTERFACE PkgConfig::MiniUPnPc) + target_include_directories(3rdparty_miniupnpc INTERFACE PkgConfig::MiniUPnPc) + list(TRANSFORM MiniUPnPc_INCLUDE_DIRS APPEND "/miniupnpc") + target_include_directories(3rdparty_miniupnpc INTERFACE ${MiniUPnPc_INCLUDE_DIRS}) +else() + option (UPNPC_BUILD_STATIC "Build static library" TRUE) + option (UPNPC_BUILD_SHARED "Build shared library" FALSE) + option (UPNPC_BUILD_TESTS "Build test executables" FALSE) + option (UPNPC_BUILD_SAMPLE "Build sample executables" FALSE) + option (NO_GETADDRINFO "Define NO_GETADDRINFO" FALSE) + option (UPNPC_NO_INSTALL "Disable installation" TRUE) -add_subdirectory(miniupnp/miniupnpc EXCLUDE_FROM_ALL) + add_subdirectory(miniupnp/miniupnpc EXCLUDE_FROM_ALL) + add_library(3rdparty_miniupnpc INTERFACE) + target_link_libraries(3rdparty_miniupnpc INTERFACE libminiupnpc-static) + target_include_directories(3rdparty_miniupnpc INTERFACE libminiupnpc-static) +endif() diff --git a/CMakeLists.txt b/CMakeLists.txt index a3c14bcd87..8f0c1c9443 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,6 +45,7 @@ option(USE_SYSTEM_FFMPEG "Prefer system ffmpeg instead of the prebuild one" OFF) option(USE_SYSTEM_FLATBUFFERS "Prefer system flatbuffers instead of the builtin one" OFF) option(USE_SYSTEM_LIBPNG "Prefer system libpng instead of the builtin one" OFF) option(USE_SYSTEM_LIBUSB "Prefer system libusb instead of the builtin one" OFF) +option(USE_SYSTEM_MINIUPNPC "Prefer system MiniUPnPc instead of the builtin one" OFF) option(USE_SYSTEM_MVK "Prefer system MoltenVK instead of the builtin one" OFF) option(USE_SYSTEM_OPENAL "Prefer system OpenAL instead of the prebuild one" ${USE_SYSTEM_OPENAL_DEFAULT}) option(USE_SYSTEM_OPENCV "Prefer system OpenCV instead of the builtin one" ON) From 9126d617c534ca521e87ed0170a87ae59331933d Mon Sep 17 00:00:00 2001 From: Marcin Serwin Date: Sat, 2 Aug 2025 12:33:20 +0200 Subject: [PATCH 008/149] Add option to use system RtMidi Signed-off-by: Marcin Serwin --- 3rdparty/rtmidi/CMakeLists.txt | 16 ++++++++++++---- CMakeLists.txt | 1 + 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/3rdparty/rtmidi/CMakeLists.txt b/3rdparty/rtmidi/CMakeLists.txt index 610f858c6d..b9dd286ce0 100644 --- a/3rdparty/rtmidi/CMakeLists.txt +++ b/3rdparty/rtmidi/CMakeLists.txt @@ -1,4 +1,12 @@ -option(RTMIDI_API_JACK "Compile with JACK support." OFF) -option(RTMIDI_BUILD_TESTING "Build test programs" OFF) -set(RTMIDI_TARGETNAME_UNINSTALL "uninstall-rpcs3-rtmidi") -add_subdirectory(rtmidi EXCLUDE_FROM_ALL) +if(USE_SYSTEM_RTMIDI) + message(STATUS "RPCS3: using shared RtMidi") + pkg_check_modules(RtMidi REQUIRED IMPORTED_TARGET rtmidi>=6.0.0) + add_library(rtmidi INTERFACE) + target_link_libraries(rtmidi INTERFACE PkgConfig::RtMidi) + target_include_directories(rtmidi INTERFACE PkgConfig::RtMidi) +else() + option(RTMIDI_API_JACK "Compile with JACK support." OFF) + option(RTMIDI_BUILD_TESTING "Build test programs" OFF) + set(RTMIDI_TARGETNAME_UNINSTALL "uninstall-rpcs3-rtmidi") + add_subdirectory(rtmidi EXCLUDE_FROM_ALL) +endif() diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f0c1c9443..ee6c96d980 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,6 +50,7 @@ option(USE_SYSTEM_MVK "Prefer system MoltenVK instead of the builtin one" OFF) option(USE_SYSTEM_OPENAL "Prefer system OpenAL instead of the prebuild one" ${USE_SYSTEM_OPENAL_DEFAULT}) option(USE_SYSTEM_OPENCV "Prefer system OpenCV instead of the builtin one" ON) option(USE_SYSTEM_PUGIXML "Prefer system pugixml instead of the builtin one" OFF) +option(USE_SYSTEM_RTMIDI "Prefer system RtMidi instead of the builtin one" OFF) option(USE_SYSTEM_SDL "Prefer system SDL instead of the builtin one" ON) option(USE_SYSTEM_WOLFSSL "Prefer system MoltenVK instead of the builtin one" OFF) option(USE_SYSTEM_ZLIB "Prefer system ZLIB instead of the builtin one" ON) From 1b72868abf9b7e62cce2bd7c381fc56a63f63015 Mon Sep 17 00:00:00 2001 From: Marcin Serwin Date: Sat, 2 Aug 2025 17:15:31 +0200 Subject: [PATCH 009/149] Add option to use system glslang Signed-off-by: Marcin Serwin --- 3rdparty/CMakeLists.txt | 3 --- 3rdparty/glslang/CMakeLists.txt | 30 +++++++++++++++++++-------- CMakeLists.txt | 1 + rpcs3/Emu/RSX/Program/SPIRVCommon.cpp | 6 +++--- 4 files changed, 25 insertions(+), 15 deletions(-) diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt index 81a81cb204..41742bceeb 100644 --- a/3rdparty/CMakeLists.txt +++ b/3rdparty/CMakeLists.txt @@ -92,9 +92,6 @@ add_subdirectory(hidapi) # glslang add_subdirectory(glslang EXCLUDE_FROM_ALL) -add_library(3rdparty_glslang INTERFACE) -target_link_libraries(3rdparty_glslang INTERFACE SPIRV) - # yaml-cpp add_subdirectory(yaml-cpp) diff --git a/3rdparty/glslang/CMakeLists.txt b/3rdparty/glslang/CMakeLists.txt index 5b6aa8e962..c86d0b384c 100644 --- a/3rdparty/glslang/CMakeLists.txt +++ b/3rdparty/glslang/CMakeLists.txt @@ -1,11 +1,23 @@ #glslang -set(ENABLE_PCH OFF CACHE BOOL "Enables Precompiled header" FORCE) -set(BUILD_EXTERNAL OFF CACHE BOOL "Build external dependencies in /External" FORCE) -set(SKIP_GLSLANG_INSTALL ON CACHE BOOL "Skip installation" FORCE) -set(ENABLE_SPVREMAPPER OFF CACHE BOOL "Enables building of SPVRemapper" FORCE) -set(ENABLE_GLSLANG_BINARIES OFF CACHE BOOL "Builds glslangValidator and spirv-remap" FORCE) -set(ENABLE_HLSL OFF CACHE BOOL "Enables HLSL input support" FORCE) -set(ENABLE_OPT OFF CACHE BOOL "Enables spirv-opt capability if present" FORCE) -set(ENABLE_CTEST OFF CACHE BOOL "Enables testing" FORCE) -add_subdirectory(glslang) +if(USE_SYSTEM_GLSLANG) + message(STATUS "RPCS3: using shared glslang") + find_package(glslang REQUIRED GLOBAL) + add_library(3rdparty_glslang INTERFACE) + target_link_libraries(3rdparty_glslang INTERFACE glslang::SPIRV) + get_target_property(SPIRV_INCLUDE_DIRS glslang::SPIRV INTERFACE_INCLUDE_DIRECTORIES) + list(TRANSFORM SPIRV_INCLUDE_DIRS APPEND "/glslang") + target_include_directories(3rdparty_glslang INTERFACE ${SPIRV_INCLUDE_DIRS}) +else() + set(ENABLE_PCH OFF CACHE BOOL "Enables Precompiled header" FORCE) + set(BUILD_EXTERNAL OFF CACHE BOOL "Build external dependencies in /External" FORCE) + set(SKIP_GLSLANG_INSTALL ON CACHE BOOL "Skip installation" FORCE) + set(ENABLE_SPVREMAPPER OFF CACHE BOOL "Enables building of SPVRemapper" FORCE) + set(ENABLE_GLSLANG_BINARIES OFF CACHE BOOL "Builds glslangValidator and spirv-remap" FORCE) + set(ENABLE_HLSL OFF CACHE BOOL "Enables HLSL input support" FORCE) + set(ENABLE_OPT OFF CACHE BOOL "Enables spirv-opt capability if present" FORCE) + set(ENABLE_CTEST OFF CACHE BOOL "Enables testing" FORCE) + add_subdirectory(glslang) + add_library(3rdparty_glslang INTERFACE) + target_link_libraries(3rdparty_glslang INTERFACE SPIRV) +endif() diff --git a/CMakeLists.txt b/CMakeLists.txt index ee6c96d980..02cc27b7fc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,6 +43,7 @@ option(USE_SYSTEM_CURL "Prefer system Curl instead of the prebuild one" ON) option(USE_SYSTEM_FAUDIO "Prefer system FAudio instead of the builtin one" OFF) option(USE_SYSTEM_FFMPEG "Prefer system ffmpeg instead of the prebuild one" OFF) option(USE_SYSTEM_FLATBUFFERS "Prefer system flatbuffers instead of the builtin one" OFF) +option(USE_SYSTEM_GLSLANG "Prefer system glslang instead of the builtin one" OFF) option(USE_SYSTEM_LIBPNG "Prefer system libpng instead of the builtin one" OFF) option(USE_SYSTEM_LIBUSB "Prefer system libusb instead of the builtin one" OFF) option(USE_SYSTEM_MINIUPNPC "Prefer system MiniUPnPc instead of the builtin one" OFF) diff --git a/rpcs3/Emu/RSX/Program/SPIRVCommon.cpp b/rpcs3/Emu/RSX/Program/SPIRVCommon.cpp index 501c0e6b51..c32d3542f2 100644 --- a/rpcs3/Emu/RSX/Program/SPIRVCommon.cpp +++ b/rpcs3/Emu/RSX/Program/SPIRVCommon.cpp @@ -12,9 +12,9 @@ #pragma clang diagnostic ignored "-Winconsistent-missing-override" #endif #endif -#include "3rdparty/glslang/glslang/SPIRV/GlslangToSpv.h" -#include "3rdparty/glslang/glslang/glslang/Include/ResourceLimits.h" -#include "3rdparty/glslang/glslang/glslang/Public/ShaderLang.h" +#include +#include +#include #ifdef _MSC_VER #pragma warning(pop) #else From cb5411440d6e1bc300f4d62b9425957173dcff90 Mon Sep 17 00:00:00 2001 From: Marcin Serwin Date: Sat, 2 Aug 2025 17:29:47 +0200 Subject: [PATCH 010/149] Add option to use system zstd Signed-off-by: Marcin Serwin --- 3rdparty/zstd/CMakeLists.txt | 22 +++++++++++++++------- CMakeLists.txt | 1 + 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/3rdparty/zstd/CMakeLists.txt b/3rdparty/zstd/CMakeLists.txt index 5a8b6f3d90..f4c97d3eeb 100644 --- a/3rdparty/zstd/CMakeLists.txt +++ b/3rdparty/zstd/CMakeLists.txt @@ -1,8 +1,16 @@ -option(ZSTD_BUILD_PROGRAMS "BUILD PROGRAMS" OFF) -option(ZSTD_BUILD_SHARED "BUILD SHARED LIBRARIES" OFF) -option(ZSTD_BUILD_STATIC "BUILD STATIC LIBRARIES" ON) -option(ZSTD_BUILD_TESTS "BUILD TESTS" OFF) +if(USE_SYSTEM_ZSTD) + message(STATUS "RPCS3: using shared zstd") + pkg_check_modules(zstd REQUIRED IMPORTED_TARGET libzstd) + add_library(3rdparty_zstd INTERFACE) + target_link_libraries(3rdparty_zstd INTERFACE PkgConfig::zstd) + target_include_directories(3rdparty_zstd INTERFACE PkgConfig::RtMidi) +else() + option(ZSTD_BUILD_PROGRAMS "BUILD PROGRAMS" OFF) + option(ZSTD_BUILD_SHARED "BUILD SHARED LIBRARIES" OFF) + option(ZSTD_BUILD_STATIC "BUILD STATIC LIBRARIES" ON) + option(ZSTD_BUILD_TESTS "BUILD TESTS" OFF) -add_subdirectory(zstd/build/cmake EXLUDE_FROM_ALL) -add_library(3rdparty_zstd INTERFACE) -target_link_libraries(3rdparty_zstd INTERFACE libzstd_static) + add_subdirectory(zstd/build/cmake EXLUDE_FROM_ALL) + add_library(3rdparty_zstd INTERFACE) + target_link_libraries(3rdparty_zstd INTERFACE libzstd_static) +endif() diff --git a/CMakeLists.txt b/CMakeLists.txt index 02cc27b7fc..1b132da0b1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,6 +55,7 @@ option(USE_SYSTEM_RTMIDI "Prefer system RtMidi instead of the builtin one" OFF) option(USE_SYSTEM_SDL "Prefer system SDL instead of the builtin one" ON) option(USE_SYSTEM_WOLFSSL "Prefer system MoltenVK instead of the builtin one" OFF) option(USE_SYSTEM_ZLIB "Prefer system ZLIB instead of the builtin one" ON) +option(USE_SYSTEM_ZSTD "Prefer system zstd instead of the builtin one" OFF) option(HAS_MEMORY_BREAKPOINTS "Add support for memory breakpoints to the interpreter" OFF) option(USE_LTO "Use LTO for building" ON) option(BUILD_RPCS3_TESTS "Build RPCS3 unit tests." OFF) From 2211876b571036a1a0e4fcbecaef4c7d095c2762 Mon Sep 17 00:00:00 2001 From: Marcin Serwin Date: Sat, 2 Aug 2025 17:33:28 +0200 Subject: [PATCH 011/149] Add option to use system hidapi Signed-off-by: Marcin Serwin --- 3rdparty/hidapi/CMakeLists.txt | 46 ++++++++++++++++++++-------------- CMakeLists.txt | 1 + 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/3rdparty/hidapi/CMakeLists.txt b/3rdparty/hidapi/CMakeLists.txt index e1e36ac13e..2d043d6936 100644 --- a/3rdparty/hidapi/CMakeLists.txt +++ b/3rdparty/hidapi/CMakeLists.txt @@ -1,22 +1,30 @@ # hidapi -set(BUILD_SHARED_LIBS FALSE CACHE BOOL "Don't build shared libs") -set(HIDAPI_INSTALL_TARGETS FALSE CACHE BOOL "Don't install anything") - -if(CMAKE_SYSTEM MATCHES "Linux") - set(HIDAPI_WITH_LIBUSB FALSE CACHE BOOL "Don't build with libusb for linux") -endif() - -add_library(3rdparty_hidapi INTERFACE) -add_subdirectory(hidapi EXCLUDE_FROM_ALL) - -if(APPLE) - target_link_libraries(3rdparty_hidapi INTERFACE hidapi_darwin "-framework CoreFoundation" "-framework IOKit") -elseif(CMAKE_SYSTEM MATCHES "Linux") - target_link_libraries(3rdparty_hidapi INTERFACE hidapi-hidraw udev) -elseif(WIN32) - target_link_libraries(3rdparty_hidapi INTERFACE hidapi::hidapi hidapi::include Shlwapi.lib) -elseif(ANDROID) - target_link_libraries(3rdparty_hidapi INTERFACE hidapi::libusb) +if(USE_SYSTEM_HIDAPI) + message(STATUS "RPCS3: using shared hidapi") + pkg_check_modules(hidapi-hidraw REQUIRED IMPORTED_TARGET hidapi-hidraw) + add_library(3rdparty_hidapi INTERFACE) + target_link_libraries(3rdparty_hidapi INTERFACE PkgConfig::hidapi-hidraw) + target_include_directories(3rdparty_hidapi INTERFACE PkgConfig::hidapi-hidraw) else() - target_link_libraries(3rdparty_hidapi INTERFACE hidapi-libusb usb) + set(BUILD_SHARED_LIBS FALSE CACHE BOOL "Don't build shared libs") + set(HIDAPI_INSTALL_TARGETS FALSE CACHE BOOL "Don't install anything") + + if(CMAKE_SYSTEM MATCHES "Linux") + set(HIDAPI_WITH_LIBUSB FALSE CACHE BOOL "Don't build with libusb for linux") + endif() + + add_library(3rdparty_hidapi INTERFACE) + add_subdirectory(hidapi EXCLUDE_FROM_ALL) + + if(APPLE) + target_link_libraries(3rdparty_hidapi INTERFACE hidapi_darwin "-framework CoreFoundation" "-framework IOKit") + elseif(CMAKE_SYSTEM MATCHES "Linux") + target_link_libraries(3rdparty_hidapi INTERFACE hidapi-hidraw udev) + elseif(WIN32) + target_link_libraries(3rdparty_hidapi INTERFACE hidapi::hidapi hidapi::include Shlwapi.lib) + elseif(ANDROID) + target_link_libraries(3rdparty_hidapi INTERFACE hidapi::libusb) + else() + target_link_libraries(3rdparty_hidapi INTERFACE hidapi-libusb usb) + endif() endif() diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b132da0b1..04e95a3ee4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,6 +44,7 @@ option(USE_SYSTEM_FAUDIO "Prefer system FAudio instead of the builtin one" OFF) option(USE_SYSTEM_FFMPEG "Prefer system ffmpeg instead of the prebuild one" OFF) option(USE_SYSTEM_FLATBUFFERS "Prefer system flatbuffers instead of the builtin one" OFF) option(USE_SYSTEM_GLSLANG "Prefer system glslang instead of the builtin one" OFF) +option(USE_SYSTEM_HIDAPI "Prefer system hidapi instead of the builtin one" OFF) option(USE_SYSTEM_LIBPNG "Prefer system libpng instead of the builtin one" OFF) option(USE_SYSTEM_LIBUSB "Prefer system libusb instead of the builtin one" OFF) option(USE_SYSTEM_MINIUPNPC "Prefer system MiniUPnPc instead of the builtin one" OFF) From 69bf7452462276f322d3203f7cf1d348f1347968 Mon Sep 17 00:00:00 2001 From: Marcin Serwin Date: Sat, 2 Aug 2025 18:34:46 +0200 Subject: [PATCH 012/149] Add option to use system Vulkan Memory Allocator Signed-off-by: Marcin Serwin --- 3rdparty/CMakeLists.txt | 8 ++++++++ CMakeLists.txt | 1 + rpcs3/Emu/CMakeLists.txt | 1 + rpcs3/Emu/RSX/VK/VKMemAlloc.cpp | 2 +- rpcs3/Emu/RSX/VK/vkutils/memory.h | 2 +- rpcs3/VKGSRender.vcxproj | 4 ++-- rpcs3/rpcs3.vcxproj | 4 ++-- 7 files changed, 16 insertions(+), 6 deletions(-) diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt index 41742bceeb..46307746a3 100644 --- a/3rdparty/CMakeLists.txt +++ b/3rdparty/CMakeLists.txt @@ -51,6 +51,14 @@ else() add_subdirectory(pugixml EXCLUDE_FROM_ALL) endif() +if (USE_SYSTEM_VULKAN_MEMORY_ALLOCATOR) + find_package(VulkanMemoryAllocator REQUIRED GLOBAL) + add_library(3rdparty::vulkanmemoryallocator ALIAS GPUOpen::VulkanMemoryAllocator) +else() + add_library(3rdparty_vulkanmemoryallocator INTERFACE) + target_include_directories(3rdparty_vulkanmemoryallocator INTERFACE GPUOpen/VulkanMemoryAllocator/include) + add_library(3rdparty::vulkanmemoryallocator ALIAS 3rdparty_vulkanmemoryallocator) +endif() # libusb if(CMAKE_SYSTEM_NAME MATCHES "DragonFly|FreeBSD") diff --git a/CMakeLists.txt b/CMakeLists.txt index 04e95a3ee4..30d9659914 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,6 +54,7 @@ option(USE_SYSTEM_OPENCV "Prefer system OpenCV instead of the builtin one" ON) option(USE_SYSTEM_PUGIXML "Prefer system pugixml instead of the builtin one" OFF) option(USE_SYSTEM_RTMIDI "Prefer system RtMidi instead of the builtin one" OFF) option(USE_SYSTEM_SDL "Prefer system SDL instead of the builtin one" ON) +option(USE_SYSTEM_VULKAN_MEMORY_ALLOCATOR "Prefer system Vulkan Memory Allocator instead of the builtin one" OFF) option(USE_SYSTEM_WOLFSSL "Prefer system MoltenVK instead of the builtin one" OFF) option(USE_SYSTEM_ZLIB "Prefer system ZLIB instead of the builtin one" ON) option(USE_SYSTEM_ZSTD "Prefer system zstd instead of the builtin one" OFF) diff --git a/rpcs3/Emu/CMakeLists.txt b/rpcs3/Emu/CMakeLists.txt index 3793c94ea1..b7d372e8bd 100644 --- a/rpcs3/Emu/CMakeLists.txt +++ b/rpcs3/Emu/CMakeLists.txt @@ -642,6 +642,7 @@ target_link_libraries(rpcs3_emu 3rdparty::libevdev 3rdparty::flatbuffers 3rdparty::pugixml + 3rdparty::vulkanmemoryallocator Threads::Threads PRIVATE 3rdparty::glslang diff --git a/rpcs3/Emu/RSX/VK/VKMemAlloc.cpp b/rpcs3/Emu/RSX/VK/VKMemAlloc.cpp index 6f55268ffb..4689e910d2 100644 --- a/rpcs3/Emu/RSX/VK/VKMemAlloc.cpp +++ b/rpcs3/Emu/RSX/VK/VKMemAlloc.cpp @@ -55,7 +55,7 @@ private: #pragma GCC diagnostic ignored "-Wsuggest-attribute=noreturn" #endif #endif -#include "3rdparty/GPUOpen/VulkanMemoryAllocator/include/vk_mem_alloc.h" +#include #ifdef _MSC_VER #pragma warning(pop) #else diff --git a/rpcs3/Emu/RSX/VK/vkutils/memory.h b/rpcs3/Emu/RSX/VK/vkutils/memory.h index d2b894cfa2..eda4035ade 100644 --- a/rpcs3/Emu/RSX/VK/vkutils/memory.h +++ b/rpcs3/Emu/RSX/VK/vkutils/memory.h @@ -9,7 +9,7 @@ #pragma clang diagnostic ignored "-Wnullability-completeness" #endif #define VMA_VULKAN_VERSION 1002000 -#include "3rdparty/GPUOpen/VulkanMemoryAllocator/include/vk_mem_alloc.h" +#include #ifdef __clang__ #pragma clang diagnostic pop #endif diff --git a/rpcs3/VKGSRender.vcxproj b/rpcs3/VKGSRender.vcxproj index 3470c82ed1..65420231f3 100644 --- a/rpcs3/VKGSRender.vcxproj +++ b/rpcs3/VKGSRender.vcxproj @@ -155,7 +155,7 @@ - $(VULKAN_SDK)\Include;$(SolutionDir)\3rdparty\glslang\glslang\Public;..\3rdparty\glslang\glslang;%(AdditionalIncludeDirectories) + $(VULKAN_SDK)\Include;$(SolutionDir)\3rdparty\glslang\glslang\Public;..\3rdparty\glslang\glslang;%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\GPUOpen\VulkanMemoryAllocator\include /bigobj %(AdditionalOptions) MaxSpeed @@ -163,4 +163,4 @@ - \ No newline at end of file + diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index 8e056e8d23..dc09c27b25 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -60,12 +60,12 @@ true - $(SolutionDir)3rdparty\7zip\7zip\C;$(SolutionDir)3rdparty\hidapi\hidapi\hidapi;.\;$(SolutionDir);$(SolutionDir)3rdparty\asmjit\asmjit\src;$(SolutionDir)3rdparty\yaml-cpp\yaml-cpp\include;$(SolutionDir)3rdparty\ffmpeg\include;$(VC_IncludePath);$(WindowsSDK_IncludePath);$(UniversalCRT_IncludePath);$(SolutionDir)3rdparty\libpng\libpng;$(SolutionDir)3rdparty\GL;$(SolutionDir)3rdparty\stblib\stb;$(SolutionDir)3rdparty\openal\openal-soft\include\AL;$(SolutionDir)3rdparty\pugixml\src;$(SolutionDir)3rdparty\Optional;$(SolutionDir)3rdparty\discord-rpc\discord-rpc\include;$(SolutionDir)3rdparty\zlib\zlib;$(SolutionDir)3rdparty\libsdl-org\SDL\include;$(SolutionDir)3rdparty\fusion\fusion\Fusion + $(SolutionDir)3rdparty\7zip\7zip\C;$(SolutionDir)3rdparty\hidapi\hidapi\hidapi;.\;$(SolutionDir);$(SolutionDir)3rdparty\asmjit\asmjit\src;$(SolutionDir)3rdparty\yaml-cpp\yaml-cpp\include;$(SolutionDir)3rdparty\ffmpeg\include;$(VC_IncludePath);$(WindowsSDK_IncludePath);$(UniversalCRT_IncludePath);$(SolutionDir)3rdparty\libpng\libpng;$(SolutionDir)3rdparty\GL;$(SolutionDir)3rdparty\stblib\stb;$(SolutionDir)3rdparty\openal\openal-soft\include\AL;$(SolutionDir)3rdparty\pugixml\src;$(SolutionDir)3rdparty\Optional;$(SolutionDir)3rdparty\discord-rpc\discord-rpc\include;$(SolutionDir)3rdparty\zlib\zlib;$(SolutionDir)3rdparty\libsdl-org\SDL\include;$(SolutionDir)3rdparty\fusion\fusion\Fusion;$(SolutionDir)3rdparty\GPUOpen\VulkanMemoryAllocator\include $(SolutionDir)build\lib\$(Configuration)-$(Platform)\;$(UniversalCRT_LibraryPath_x64);$(LibraryPath) $(SolutionDir)build\lib\$(Configuration)-$(Platform)\;$(UniversalCRT_LibraryPath_x64);$(LibraryPath) - $(SolutionDir)3rdparty\7zip\7zip\C;$(SolutionDir)3rdparty\hidapi\hidapi\hidapi;.\;$(SolutionDir);$(SolutionDir)3rdparty\asmjit\asmjit\src;$(SolutionDir)3rdparty\yaml-cpp\yaml-cpp\include;$(SolutionDir)3rdparty\ffmpeg\include;$(VC_IncludePath);$(WindowsSDK_IncludePath);$(UniversalCRT_IncludePath);$(SolutionDir)3rdparty\libpng\libpng;$(SolutionDir)3rdparty\GL;$(SolutionDir)3rdparty\stblib\stb;$(SolutionDir)3rdparty\openal\openal-soft\include\AL;$(SolutionDir)3rdparty\pugixml\src;$(SolutionDir)3rdparty\Optional;$(SolutionDir)3rdparty\discord-rpc\discord-rpc\include;$(SolutionDir)3rdparty\zlib\zlib;$(SolutionDir)3rdparty\libsdl-org\SDL\include;$(SolutionDir)3rdparty\fusion\fusion\Fusion + $(SolutionDir)3rdparty\7zip\7zip\C;$(SolutionDir)3rdparty\hidapi\hidapi\hidapi;.\;$(SolutionDir);$(SolutionDir)3rdparty\asmjit\asmjit\src;$(SolutionDir)3rdparty\yaml-cpp\yaml-cpp\include;$(SolutionDir)3rdparty\ffmpeg\include;$(VC_IncludePath);$(WindowsSDK_IncludePath);$(UniversalCRT_IncludePath);$(SolutionDir)3rdparty\libpng\libpng;$(SolutionDir)3rdparty\GL;$(SolutionDir)3rdparty\stblib\stb;$(SolutionDir)3rdparty\openal\openal-soft\include\AL;$(SolutionDir)3rdparty\pugixml\src;$(SolutionDir)3rdparty\Optional;$(SolutionDir)3rdparty\discord-rpc\discord-rpc\include;$(SolutionDir)3rdparty\zlib\zlib;$(SolutionDir)3rdparty\libsdl-org\SDL\include;$(SolutionDir)3rdparty\fusion\fusion\Fusion;$(SolutionDir)3rdparty\GPUOpen\VulkanMemoryAllocator\include From e49a086edf0f382f3ae83448b3ef6d662b5d1436 Mon Sep 17 00:00:00 2001 From: Marcin Serwin Date: Sat, 2 Aug 2025 19:57:33 +0200 Subject: [PATCH 013/149] Fix description of system wolfSSL option Signed-off-by: Marcin Serwin --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 30d9659914..8b0475c899 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,7 +55,7 @@ option(USE_SYSTEM_PUGIXML "Prefer system pugixml instead of the builtin one" OFF option(USE_SYSTEM_RTMIDI "Prefer system RtMidi instead of the builtin one" OFF) option(USE_SYSTEM_SDL "Prefer system SDL instead of the builtin one" ON) option(USE_SYSTEM_VULKAN_MEMORY_ALLOCATOR "Prefer system Vulkan Memory Allocator instead of the builtin one" OFF) -option(USE_SYSTEM_WOLFSSL "Prefer system MoltenVK instead of the builtin one" OFF) +option(USE_SYSTEM_WOLFSSL "Prefer system wolfSSL instead of the builtin one" OFF) option(USE_SYSTEM_ZLIB "Prefer system ZLIB instead of the builtin one" ON) option(USE_SYSTEM_ZSTD "Prefer system zstd instead of the builtin one" OFF) option(HAS_MEMORY_BREAKPOINTS "Add support for memory breakpoints to the interpreter" OFF) From e5537c1cb09500bbe34525cb41c933db6d0edd82 Mon Sep 17 00:00:00 2001 From: Marcin Serwin Date: Fri, 1 Aug 2025 18:24:11 +0200 Subject: [PATCH 014/149] Remove alternative default device detection in Cubeb backend Signed-off-by: Marcin Serwin --- rpcs3/Emu/Audio/Cubeb/CubebBackend.cpp | 84 +------------------------- rpcs3/Emu/Audio/Cubeb/CubebBackend.h | 1 - 2 files changed, 3 insertions(+), 82 deletions(-) diff --git a/rpcs3/Emu/Audio/Cubeb/CubebBackend.cpp b/rpcs3/Emu/Audio/Cubeb/CubebBackend.cpp index dbaf4aa8db..fc5b24da45 100644 --- a/rpcs3/Emu/Audio/Cubeb/CubebBackend.cpp +++ b/rpcs3/Emu/Audio/Cubeb/CubebBackend.cpp @@ -90,11 +90,6 @@ bool CubebBackend::DefaultDeviceChanged() } device_handle device = GetDevice(); - if (!device.handle) - { - Cubeb.error("Selected device not found. Trying alternative approach..."); - device = GetDefaultDeviceAlt(m_sampling_rate, m_sample_size, m_channels); - } return !device.handle || device.id != m_default_device; } @@ -119,20 +114,9 @@ bool CubebBackend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize if (!device.handle) { - if (use_default_device) - { - device = GetDefaultDeviceAlt(freq, sample_size, static_cast(ch_cnt)); - - if (!device.handle) - { - Cubeb.error("Cannot detect default device. Channel count detection unavailable."); - } - } - else - { - Cubeb.error("Device with id=%s not found", dev_id); - return false; - } + if (use_default_device) Cubeb.error("Opening default device failed"); + else Cubeb.error("Device with id=%s not found", dev_id); + return false; } if (device.ch_cnt == 0) @@ -358,68 +342,6 @@ CubebBackend::device_handle CubebBackend::GetDevice(std::string_view dev_id) return result; }; -CubebBackend::device_handle CubebBackend::GetDefaultDeviceAlt(AudioFreq freq, AudioSampleSize sample_size, u32 ch_cnt) -{ - Cubeb.notice("Starting alternative search for default device with freq=%d, sample_size=%d and ch_cnt=%d", static_cast(freq), static_cast(sample_size), static_cast(ch_cnt)); - - cubeb_stream_params param = - { - .format = sample_size == AudioSampleSize::S16 ? CUBEB_SAMPLE_S16NE : CUBEB_SAMPLE_FLOAT32NE, - .rate = static_cast(freq), - .channels = static_cast(ch_cnt), - .layout = CUBEB_LAYOUT_UNDEFINED, - .prefs = CUBEB_STREAM_PREF_DISABLE_DEVICE_SWITCHING - }; - - u32 min_latency{}; - if (int err = cubeb_get_min_latency(m_ctx, ¶m, &min_latency)) - { - Cubeb.error("cubeb_get_min_latency() failed: %i", err); - min_latency = 100; - } - - cubeb_stream* tmp_stream{}; - static auto dummy_data_cb = [](cubeb_stream*, void*, void const*, void*, long) -> long { return 0; }; - static auto dummy_state_cb = [](cubeb_stream*, void*, cubeb_state) {}; - - if (int err = cubeb_stream_init(m_ctx, &tmp_stream, "Default device detector", nullptr, nullptr, nullptr, ¶m, min_latency, dummy_data_cb, dummy_state_cb, nullptr)) - { - Cubeb.error("cubeb_stream_init() failed: %i", err); - return {}; - } - - cubeb_device* crnt_dev{}; - - if (int err = cubeb_stream_get_current_device(tmp_stream, &crnt_dev); err != CUBEB_OK || !crnt_dev) - { - Cubeb.error("cubeb_stream_get_current_device() failed: err=%i, crnt_dev=%d", err, !!crnt_dev); - cubeb_stream_destroy(tmp_stream); - return {}; - } - - std::string out_dev_name; - - if (crnt_dev->output_name) - { - out_dev_name = crnt_dev->output_name; - } - - if (int err = cubeb_stream_device_destroy(tmp_stream, crnt_dev)) - { - Cubeb.error("cubeb_stream_device_destroy() failed: %i", err); - } - - cubeb_stream_destroy(tmp_stream); - - if (out_dev_name.empty()) - { - Cubeb.notice("No default device available"); - return {}; - } - - return GetDevice(out_dev_name); -} - long CubebBackend::data_cb(cubeb_stream* stream, void* user_ptr, void const* /* input_buffer */, void* output_buffer, long nframes) { if (nframes <= 0) diff --git a/rpcs3/Emu/Audio/Cubeb/CubebBackend.h b/rpcs3/Emu/Audio/Cubeb/CubebBackend.h index 1f230ec238..9641b46006 100644 --- a/rpcs3/Emu/Audio/Cubeb/CubebBackend.h +++ b/rpcs3/Emu/Audio/Cubeb/CubebBackend.h @@ -61,5 +61,4 @@ private: }; device_handle GetDevice(std::string_view dev_id = ""); - device_handle GetDefaultDeviceAlt(AudioFreq freq, AudioSampleSize sample_size, u32 ch_cnt); }; From cda683df632562cf551828f87f21ea80fc576314 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sun, 3 Aug 2025 10:15:49 +0200 Subject: [PATCH 015/149] Update FAudio to 25.08 --- 3rdparty/FAudio | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/FAudio b/3rdparty/FAudio index e6ddfabab2..8da412ed6c 160000 --- a/3rdparty/FAudio +++ b/3rdparty/FAudio @@ -1 +1 @@ -Subproject commit e6ddfabab2efbc8765750039634fe5e24ac31205 +Subproject commit 8da412ed6c78c6e225b873bb12cbc903d5389192 From bf3501b737af7533a7268e6d18065c17d624be53 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Wed, 6 Aug 2025 21:35:39 +0200 Subject: [PATCH 016/149] Update 7zip to 25.01 --- 3rdparty/7zip/7zip | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/7zip/7zip b/3rdparty/7zip/7zip index 395149956d..5e96a82794 160000 --- a/3rdparty/7zip/7zip +++ b/3rdparty/7zip/7zip @@ -1 +1 @@ -Subproject commit 395149956d696e6e3099d8b76d797437f94a6942 +Subproject commit 5e96a8279489832924056b1fa82f29d5837c9469 From f474533447ef118e669a73bbc5d403ba3f0b4fa5 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Wed, 6 Aug 2025 21:39:28 +0200 Subject: [PATCH 017/149] Update SDL to 3.2.20 --- 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 68bfcb6c54..96292a5b46 160000 --- a/3rdparty/libsdl-org/SDL +++ b/3rdparty/libsdl-org/SDL @@ -1 +1 @@ -Subproject commit 68bfcb6c5419f51104e74e72ea0f8d405a4615b0 +Subproject commit 96292a5b464258a2b926e0a3d72f8b98c2a81aa6 From 48d3b3020409c14f4c4388fa5c2d72e3da8bfc65 Mon Sep 17 00:00:00 2001 From: oltolm Date: Fri, 8 Aug 2025 21:52:58 +0200 Subject: [PATCH 018/149] llvm: use CreatePtrAdd use CreatePtrAdd instead of CreateGEP(get_type(), ...) or CreateIntToPtr(CreateAdd, CreatePtrToInt(...)) --- rpcs3/Emu/Cell/PPUTranslator.cpp | 20 ++++---- rpcs3/Emu/Cell/SPULLVMRecompiler.cpp | 75 ++++++++++++++-------------- 2 files changed, 46 insertions(+), 49 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUTranslator.cpp b/rpcs3/Emu/Cell/PPUTranslator.cpp index 7380d8123f..b7a6a263ce 100644 --- a/rpcs3/Emu/Cell/PPUTranslator.cpp +++ b/rpcs3/Emu/Cell/PPUTranslator.cpp @@ -413,12 +413,11 @@ Function* PPUTranslator::GetSymbolResolver(const ppu_module& info) const auto faddr_int = m_ir->CreatePtrToInt(faddr, get_type()); const auto pos_32 = m_reloc ? m_ir->CreateAdd(func_pc, m_seg0) : func_pc; const auto pos = m_ir->CreateShl(pos_32, 1); - const auto ptr = dyn_cast(m_ir->CreateGEP(get_type(), m_exec, pos)); + const auto ptr = m_ir->CreatePtrAdd(m_exec, pos); - const auto seg_base_ptr = m_ir->CreateIntToPtr(m_ir->CreateAdd( - m_ir->CreatePtrToInt(m_exec, get_type()), m_ir->getInt64(vm::g_exec_addr_seg_offset)), m_exec->getType()); + const auto seg_base_ptr = m_ir->CreatePtrAdd(m_exec, m_ir->getInt64(vm::g_exec_addr_seg_offset)); const auto seg_pos = m_ir->CreateLShr(pos_32, 1); - const auto seg_ptr = dyn_cast(m_ir->CreateGEP(get_type(), seg_base_ptr, seg_pos)); + const auto seg_ptr = m_ir->CreatePtrAdd(seg_base_ptr, seg_pos); const auto seg_val = m_ir->CreateTrunc(m_ir->CreateLShr(m_seg0, 13), get_type()); // Store to jumptable @@ -610,15 +609,14 @@ void PPUTranslator::CallFunction(u64 target, Value* indirect) } const auto pos = m_ir->CreateShl(indirect, 1); - const auto ptr = dyn_cast(m_ir->CreateGEP(get_type(), m_exec, pos)); + const auto ptr = m_ir->CreatePtrAdd(m_exec, pos); const auto val = m_ir->CreateLoad(get_type(), ptr); callee = FunctionCallee(type, m_ir->CreateIntToPtr(val, m_ir->getPtrTy())); // Load new segment address - const auto seg_base_ptr = m_ir->CreateIntToPtr(m_ir->CreateAdd( - m_ir->CreatePtrToInt(m_exec, get_type()), m_ir->getInt64(vm::g_exec_addr_seg_offset)), m_exec->getType()); + const auto seg_base_ptr = m_ir->CreatePtrAdd(m_exec, m_ir->getInt64(vm::g_exec_addr_seg_offset)); const auto seg_pos = m_ir->CreateLShr(indirect, 1); - const auto seg_ptr = dyn_cast(m_ir->CreateGEP(get_type(), seg_base_ptr, seg_pos)); + const auto seg_ptr = m_ir->CreatePtrAdd(seg_base_ptr, seg_pos); const auto seg_val = m_ir->CreateZExt(m_ir->CreateLoad(get_type(), seg_ptr), get_type()); seg0 = m_ir->CreateShl(seg_val, 13); } @@ -824,7 +822,7 @@ void PPUTranslator::UseCondition(MDNode* hint, Value* cond) llvm::Value* PPUTranslator::GetMemory(llvm::Value* addr) { - return m_ir->CreateGEP(get_type(), m_base, addr); + return m_ir->CreatePtrAdd(m_base, addr); } void PPUTranslator::TestAborted() @@ -2794,8 +2792,8 @@ void PPUTranslator::MFOCRF(ppu_opcode_t op) else if (std::none_of(m_cr + 0, m_cr + 32, [](auto* p) { return p; })) { // MFCR (optimized) - Value* ln0 = m_ir->CreateIntToPtr(m_ir->CreatePtrToInt(m_ir->CreateStructGEP(m_thread_type, m_thread, 99), GetType()), m_ir->getPtrTy()); - Value* ln1 = m_ir->CreateIntToPtr(m_ir->CreatePtrToInt(m_ir->CreateStructGEP(m_thread_type, m_thread, 115), GetType()), m_ir->getPtrTy()); + Value* ln0 = m_ir->CreateStructGEP(m_thread_type, m_thread, 99); + Value* ln1 = m_ir->CreateStructGEP(m_thread_type, m_thread, 115); ln0 = m_ir->CreateLoad(GetType(), ln0); ln1 = m_ir->CreateLoad(GetType(), ln1); diff --git a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp index c226e678ad..7ffdec12a3 100644 --- a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp +++ b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp @@ -530,13 +530,13 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator template llvm::Value* _ptr(llvm::Value* base, u32 offset) { - return m_ir->CreateGEP(get_type(), base, m_ir->getInt64(offset)); + return m_ir->CreatePtrAdd(base, m_ir->getInt64(offset)); } template llvm::Value* _ptr(llvm::Value* base, llvm::Value* offset) { - return m_ir->CreateGEP(get_type(), base, offset); + return m_ir->CreatePtrAdd(base, offset); } template @@ -548,7 +548,7 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator template llvm::Value* spu_ptr(value_t add, Args... offset_args) { - const auto off = m_ir->CreateGEP(get_type(), m_thread, m_ir->getInt64(::offset32(offset_args...))); + const auto off = m_ir->CreatePtrAdd(m_thread, m_ir->getInt64(::offset32(offset_args...))); return m_ir->CreateAdd(off, add.value); } @@ -631,7 +631,7 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator const auto idx = m_ir->CreateAnd(I > 4 ? isr : isl, m_interp_7f0); // Pointer to the register - return m_ir->CreateGEP(get_type(), m_interp_regs, m_ir->CreateZExt(idx, get_type())); + return m_ir->CreatePtrAdd(m_interp_regs, m_ir->CreateZExt(idx, get_type())); } llvm::Value* double_as_uint64(llvm::Value* val) @@ -1645,13 +1645,13 @@ public: } else if (func.data.size() == 1) { - const auto pu32 = m_ir->CreateGEP(get_type(), m_lsptr, m_base_pc); + const auto pu32 = m_ir->CreatePtrAdd(m_lsptr, m_base_pc); const auto cond = m_ir->CreateICmpNE(m_ir->CreateLoad(get_type(), pu32), m_ir->getInt32(func.data[0])); m_ir->CreateCondBr(cond, label_diff, label_body, m_md_unlikely); } else if (func.data.size() == 2) { - const auto pu64 = m_ir->CreateGEP(get_type(), m_lsptr, m_base_pc); + const auto pu64 = m_ir->CreatePtrAdd(m_lsptr, m_base_pc); const auto cond = m_ir->CreateICmpNE(m_ir->CreateLoad(get_type(), pu64), m_ir->getInt64(static_cast(func.data[1]) << 32 | func.data[0])); m_ir->CreateCondBr(cond, label_diff, label_body, m_md_unlikely); } @@ -1697,7 +1697,7 @@ public: // Get actual pc corresponding to the found beginning of the data llvm::Value* starta_pc = m_ir->CreateAnd(get_pc(starta), 0x3fffc); - llvm::Value* data_addr = m_ir->CreateGEP(get_type(), m_lsptr, starta_pc); + llvm::Value* data_addr = m_ir->CreatePtrAdd(m_lsptr, starta_pc); llvm::Value* acc0 = nullptr; llvm::Value* acc1 = nullptr; @@ -1855,7 +1855,7 @@ public: } vls = m_ir->CreateXor(vls, ConstantDataVector::get(m_context, llvm::ArrayRef(words, elements))); - + // Interleave accumulators for more performance if (toggle) { @@ -2398,7 +2398,7 @@ public: worked_on[p] = true; work2_list.push_back(std::make_pair(p, found_user)); } - // Enqueue a second iteration for found_user=true if only found with found_user=false + // Enqueue a second iteration for found_user=true if only found with found_user=false else if (found_user && !std::find_if(work2_list.rbegin(), work2_list.rend(), [&](auto& it){ return it.first == p; })->second) { work2_list.push_back(std::make_pair(p, true)); @@ -2543,7 +2543,7 @@ public: worked_on[target] = true; work_list.emplace_back(target, found_barrier); } - // Enqueue a second iteration for found_barrier=true if only found with found_barrier=false + // Enqueue a second iteration for found_barrier=true if only found with found_barrier=false else if (found_barrier && !std::find_if(work_list.rbegin(), work_list.rend(), [&](auto& it){ return it.first == target; })->second) { work_list.emplace_back(target, true); @@ -2908,7 +2908,7 @@ public: // Load pc and opcode m_interp_pc = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::pc)); - m_interp_op = m_ir->CreateLoad(get_type(), m_ir->CreateGEP(get_type(), m_lsptr, m_ir->CreateZExt(m_interp_pc, get_type()))); + m_interp_op = m_ir->CreateLoad(get_type(), m_ir->CreatePtrAdd(m_lsptr, m_ir->CreateZExt(m_interp_pc, get_type()))); m_interp_op = m_ir->CreateCall(get_intrinsic(Intrinsic::bswap), {m_interp_op}); // Pinned constant, address of interpreter table @@ -3072,7 +3072,7 @@ public: // Decode next instruction. const auto next_pc = itype & spu_itype::branch ? m_interp_pc : m_interp_pc_next; - const auto be32_op = m_ir->CreateLoad(get_type(), m_ir->CreateGEP(get_type(), m_lsptr, m_ir->CreateZExt(next_pc, get_type()))); + const auto be32_op = m_ir->CreateLoad(get_type(), m_ir->CreatePtrAdd(m_lsptr, m_ir->CreateZExt(next_pc, get_type()))); const auto next_op = m_ir->CreateCall(get_intrinsic(Intrinsic::bswap), {be32_op}); const auto next_if = m_ir->CreateLoad(m_ir->getPtrTy(), m_ir->CreateGEP(m_ir->getPtrTy(), m_interp_table, m_ir->CreateLShr(next_op, 32u - m_interp_magn))); llvm::cast(next_if)->setVolatile(true); @@ -4075,8 +4075,8 @@ public: csize = -1; } - llvm::Value* src = m_ir->CreateGEP(get_type(), m_lsptr, zext(lsa).eval(m_ir)); - llvm::Value* dst = m_ir->CreateGEP(get_type(), m_memptr, zext(eal).eval(m_ir)); + llvm::Value* src = m_ir->CreatePtrAdd(m_lsptr, zext(lsa).eval(m_ir)); + llvm::Value* dst = m_ir->CreatePtrAdd(m_memptr, zext(eal).eval(m_ir)); if (cmd & MFC_GET_CMD) { @@ -4173,8 +4173,8 @@ public: // Generate fixed sequence of copy operations for (u32 i = 0; i < csize; i += stride) { - const auto _src = m_ir->CreateGEP(get_type(), src, m_ir->getInt32(i)); - const auto _dst = m_ir->CreateGEP(get_type(), dst, m_ir->getInt32(i)); + const auto _src = m_ir->CreatePtrAdd(src, m_ir->getInt32(i)); + const auto _dst = m_ir->CreatePtrAdd(dst, m_ir->getInt32(i)); if (csize - i < stride) { m_ir->CreateStore(m_ir->CreateLoad(get_type(), _src), _dst); @@ -4228,8 +4228,8 @@ public: // Get MFC slot, redirect to invalid memory address const auto slot = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::mfc_size)); const auto off0 = m_ir->CreateAdd(m_ir->CreateMul(slot, m_ir->getInt32(sizeof(spu_mfc_cmd))), m_ir->getInt32(::offset32(&spu_thread::mfc_queue))); - const auto ptr0 = m_ir->CreateGEP(get_type(), m_thread, m_ir->CreateZExt(off0, get_type())); - const auto ptr1 = m_ir->CreateGEP(get_type(), m_memptr, m_ir->getInt64(0xffdeadf0)); + const auto ptr0 = m_ir->CreatePtrAdd(m_thread, m_ir->CreateZExt(off0, get_type())); + const auto ptr1 = m_ir->CreatePtrAdd(m_memptr, m_ir->getInt64(0xffdeadf0)); const auto pmfc = m_ir->CreateSelect(m_ir->CreateICmpULT(slot, m_ir->getInt32(16)), ptr0, ptr1); m_ir->CreateStore(ci, _ptr(pmfc, ::offset32(&spu_mfc_cmd::cmd))); @@ -4974,7 +4974,7 @@ public: { minusb = eval(x); } - + const auto bx = splat_scalar(minusb) & 0x7; set_vr(op.rt, fshr(zshuffle(a, 1, 2, 3, 4), a, bx)); } @@ -6860,12 +6860,12 @@ public: const auto div_result = the_one / div; return vfixupimmps(div_result, div_result, splat(0x00220088u), 0, 0xff); - }); + }); } else { register_intrinsic("spu_re_acc", [&](llvm::CallInst* ci) - { + { const auto div = value(ci->getOperand(0)); const auto the_one = value(ci->getOperand(1)); @@ -6882,7 +6882,7 @@ public: }); } - + const auto [a, b, c] = get_vrs(op.ra, op.rb, op.rc); static const auto MT = match(); @@ -6928,7 +6928,7 @@ public: erase_stores(a, b, c, a3); set_vr(op.rt4, fsqrt(fabs(src))); return true; - } + } } else if (auto [ok_fm1, a3, b3] = match_expr(c, fm(MT, MT)); ok_fm1 && b3.eq(a1)) { @@ -6937,7 +6937,7 @@ public: erase_stores(a, b, c, b3); set_vr(op.rt4, fsqrt(fabs(src))); return true; - } + } } } else if (fm_half_mul.eq(a1)) @@ -6949,7 +6949,7 @@ public: erase_stores(a, b, c, a3); set_vr(op.rt4, fsqrt(fabs(src))); return true; - } + } } else if (auto [ok_fm1, a3, b3] = match_expr(c, fm(MT, MT)); ok_fm1 && b3.eq(b1)) { @@ -6958,7 +6958,7 @@ public: erase_stores(a, b, c, b3); set_vr(op.rt4, fsqrt(fabs(src))); return true; - } + } } } } @@ -7504,13 +7504,13 @@ public: void make_store_ls(value_t addr, value_t data) { const auto bswapped = byteswap(data); - m_ir->CreateStore(bswapped.eval(m_ir), m_ir->CreateGEP(get_type(), m_lsptr, addr.value)); + m_ir->CreateStore(bswapped.eval(m_ir), m_ir->CreatePtrAdd(m_lsptr, addr.value)); } auto make_load_ls(value_t addr) { value_t data; - data.value = m_ir->CreateLoad(get_type(), m_ir->CreateGEP(get_type(), m_lsptr, addr.value)); + data.value = m_ir->CreateLoad(get_type(), m_ir->CreatePtrAdd(m_lsptr, addr.value)); return byteswap(data); } @@ -7793,22 +7793,21 @@ public: // Compare address stored in stack mirror with addr const auto stack0 = eval(zext(sp) + ::offset32(&spu_thread::stack_mirror)); const auto stack1 = eval(stack0 + 8); - const auto _ret = m_ir->CreateLoad(get_type(), m_ir->CreateGEP(get_type(), m_thread, stack0.value)); - const auto link = m_ir->CreateLoad(get_type(), m_ir->CreateGEP(get_type(), m_thread, stack1.value)); + const auto _ret = m_ir->CreateLoad(get_type(), m_ir->CreatePtrAdd(m_thread, stack0.value)); + const auto link = m_ir->CreateLoad(get_type(), m_ir->CreatePtrAdd(m_thread, stack1.value)); const auto fail = llvm::BasicBlock::Create(m_context, "", m_function); const auto done = llvm::BasicBlock::Create(m_context, "", m_function); const auto next = llvm::BasicBlock::Create(m_context, "", m_function); m_ir->CreateCondBr(m_ir->CreateICmpEQ(addr.value, m_ir->CreateTrunc(link, get_type())), next, fail, m_md_likely); m_ir->SetInsertPoint(next); - const auto cmp2 = m_ir->CreateLoad(get_type(), m_ir->CreateGEP(get_type(), m_lsptr, addr.value)); + const auto cmp2 = m_ir->CreateLoad(get_type(), m_ir->CreatePtrAdd(m_lsptr, addr.value)); m_ir->CreateCondBr(m_ir->CreateICmpEQ(cmp2, m_ir->CreateTrunc(_ret, get_type())), done, fail, m_md_likely); m_ir->SetInsertPoint(done); // Clear stack mirror and return by tail call to the provided return address - m_ir->CreateStore(splat(-1).eval(m_ir), m_ir->CreateGEP(get_type(), m_thread, stack0.value)); - const auto targ = m_ir->CreateAdd(m_ir->CreateLShr(_ret, 32), get_segment_base()); + m_ir->CreateStore(splat(-1).eval(m_ir), m_ir->CreatePtrAdd(m_thread, stack0.value)); const auto type = m_finfo->chunk->getFunctionType(); - const auto fval = m_ir->CreateIntToPtr(targ, m_ir->getPtrTy()); + const auto fval = m_ir->CreatePtrAdd(get_segment_base(), m_ir->CreateLShr(_ret, 32)); tail_chunk({type, fval}, m_ir->CreateTrunc(m_ir->CreateLShr(link, 32), get_type())); m_ir->SetInsertPoint(fail); } @@ -8488,11 +8487,11 @@ public: const auto pfunc = add_function(m_pos + 4); const auto stack0 = eval(zext(extract(get_reg_fixed(1), 3) & 0x3fff0) + ::offset32(&spu_thread::stack_mirror)); const auto stack1 = eval(stack0 + 8); - const auto rel_ptr = m_ir->CreateSub(m_ir->CreatePtrToInt(pfunc->chunk, get_type()), get_segment_base()); + const auto rel_ptr = m_ir->CreateSub(m_ir->CreatePtrToInt(pfunc->chunk, get_type()), m_ir->CreatePtrToInt(get_segment_base(), get_type())); const auto ptr_plus_op = m_ir->CreateOr(m_ir->CreateShl(rel_ptr, 32), m_ir->getInt64(m_next_op)); const auto base_plus_pc = m_ir->CreateOr(m_ir->CreateShl(m_ir->CreateZExt(m_base_pc, get_type()), 32), m_ir->getInt64(m_pos + 4)); - m_ir->CreateStore(ptr_plus_op, m_ir->CreateGEP(get_type(), m_thread, stack0.value)); - m_ir->CreateStore(base_plus_pc, m_ir->CreateGEP(get_type(), m_thread, stack1.value)); + m_ir->CreateStore(ptr_plus_op, m_ir->CreatePtrAdd(m_thread, stack0.value)); + m_ir->CreateStore(base_plus_pc, m_ir->CreatePtrAdd(m_thread, stack1.value)); } } @@ -8501,7 +8500,7 @@ public: const auto type = llvm::FunctionType::get(get_type(), {}, false); const auto func = llvm::cast(m_module->getOrInsertFunction("spu_segment_base", type).getCallee()); m_engine->updateGlobalMapping("spu_segment_base", reinterpret_cast(jit_runtime::alloc(0, 0))); - return m_ir->CreatePtrToInt(func, get_type()); + return func; } static decltype(&spu_llvm_recompiler::UNK) decode(u32 op); From 1902c5f3f433f16d912ff4e044a5f8099c69e862 Mon Sep 17 00:00:00 2001 From: oltolm Date: Sat, 9 Aug 2025 01:57:38 +0200 Subject: [PATCH 019/149] SPULLVMRecompiler: remove unused parameters from spu_ptr and _ptr --- rpcs3/Emu/Cell/SPULLVMRecompiler.cpp | 243 +++++++++++++-------------- 1 file changed, 117 insertions(+), 126 deletions(-) diff --git a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp index 7ffdec12a3..32c58a8b24 100644 --- a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp +++ b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp @@ -329,9 +329,9 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator if (!m_finfo->fn && !m_block) { - lr = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::gpr, +s_reg_lr, &v128::_u32, 3)); - sp = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::gpr, +s_reg_sp)); - r3 = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::gpr, 3)); + lr = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::gpr, +s_reg_lr, &v128::_u32, 3)); + sp = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::gpr, +s_reg_sp)); + r3 = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::gpr, 3)); } else { @@ -348,8 +348,8 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator if (!m_finfo->fn) { lr = m_ir->CreateAnd(lr, 0x3fffc); - m_ir->CreateStore(lr, spu_ptr(&spu_thread::pc)); - m_ir->CreateStore(_call, spu_ptr(&spu_thread::gpr, 3)); + m_ir->CreateStore(lr, spu_ptr(&spu_thread::pc)); + m_ir->CreateStore(_call, spu_ptr(&spu_thread::gpr, 3)); m_ir->CreateBr(add_block_indirect({}, value(lr))); } else if (tail) @@ -392,7 +392,7 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator m_blocks.clear(); m_block_queue.clear(); m_ir->SetInsertPoint(llvm::BasicBlock::Create(m_context, "", m_function)); - m_memptr = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::memory_base_addr)); + m_memptr = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::memory_base_addr)); } // Add block with current block as a predecessor @@ -415,7 +415,7 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator m_lsptr = fn->getArg(1); m_base_pc = fn->getArg(2); m_ir->SetInsertPoint(llvm::BasicBlock::Create(m_context, "", fn)); - m_memptr = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::memory_base_addr)); + m_memptr = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::memory_base_addr)); // Load registers at the entry chunk for (u32 i = 0; i < s_reg_max; i++) @@ -452,7 +452,7 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator const auto fail = llvm::BasicBlock::Create(m_context, "", m_function); m_ir->CreateCondBr(m_ir->CreateICmpEQ(m_base_pc, m_ir->getInt32(m_base)), next, fail); m_ir->SetInsertPoint(fail); - m_ir->CreateStore(m_ir->getInt32(target), spu_ptr(&spu_thread::pc)); + m_ir->CreateStore(m_ir->getInt32(target), spu_ptr(&spu_thread::pc)); tail_chunk(nullptr); m_ir->SetInsertPoint(next); } @@ -490,7 +490,7 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator { ensure(!m_finfo->fn); - m_ir->CreateStore(m_ir->getInt32(target), spu_ptr(&spu_thread::pc)); + m_ir->CreateStore(m_ir->getInt32(target), spu_ptr(&spu_thread::pc)); } else { @@ -527,29 +527,20 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator return result; } - template llvm::Value* _ptr(llvm::Value* base, u32 offset) { return m_ir->CreatePtrAdd(base, m_ir->getInt64(offset)); } - template llvm::Value* _ptr(llvm::Value* base, llvm::Value* offset) { return m_ir->CreatePtrAdd(base, offset); } - template + template llvm::Value* spu_ptr(Args... offset_args) { - return _ptr(m_thread, ::offset32(offset_args...)); - } - - template - llvm::Value* spu_ptr(value_t add, Args... offset_args) - { - const auto off = m_ir->CreatePtrAdd(m_thread, m_ir->getInt64(::offset32(offset_args...))); - return m_ir->CreateAdd(off, add.value); + return _ptr(m_thread, ::offset32(offset_args...)); } // Return default register type @@ -596,7 +587,7 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator { if (!m_block) { - return _ptr(m_thread, get_reg_offset(index)); + return _ptr(m_thread, get_reg_offset(index)); } auto& ptr = ::at32(m_reg_addr, index); @@ -608,7 +599,7 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator // Emit register pointer at the beginning of the function chunk m_ir->SetInsertPoint(m_function->getEntryBlock().getTerminator()); - ptr = _ptr(m_thread, get_reg_offset(index)); + ptr = _ptr(m_thread, get_reg_offset(index)); m_ir->SetInsertPoint(block_cur); } @@ -616,7 +607,7 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator } // Get pointer to the vector register (interpreter only) - template + template llvm::Value* init_vr(const bf_t&) { if (!m_interp_magn) @@ -758,11 +749,11 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator // Load reg if (get_type() == get_type()) { - r.value = xfloat_to_double(m_ir->CreateLoad(get_type(), init_vr(index))); + r.value = xfloat_to_double(m_ir->CreateLoad(get_type(), init_vr(index))); } else { - r.value = m_ir->CreateLoad(get_type(), init_vr(index)); + r.value = m_ir->CreateLoad(get_type(), init_vr(index)); } } else @@ -962,7 +953,7 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator const auto saved_value = is_xfloat && fixup ? xfloat_in_double(value) : value; // Store value - m_ir->CreateStore(is_xfloat ? double_to_xfloat(saved_value) : m_ir->CreateBitCast(value, get_type()), init_vr(index)); + m_ir->CreateStore(is_xfloat ? double_to_xfloat(saved_value) : m_ir->CreateBitCast(value, get_type()), init_vr(index)); return; } @@ -1049,13 +1040,13 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator // Update PC for current or explicitly specified instruction address void update_pc(u32 target = -1) { - m_ir->CreateStore(m_ir->CreateAnd(get_pc(target + 1 ? target : m_pos), 0x3fffc), spu_ptr(&spu_thread::pc))->setVolatile(true); + m_ir->CreateStore(m_ir->CreateAnd(get_pc(target + 1 ? target : m_pos), 0x3fffc), spu_ptr(&spu_thread::pc))->setVolatile(true); } // Call cpu_thread::check_state if necessary and return or continue (full check) void check_state(u32 addr, bool may_be_unsafe_for_savestate = true) { - const auto pstate = spu_ptr(&spu_thread::state); + const auto pstate = spu_ptr(&spu_thread::state); const auto _body = llvm::BasicBlock::Create(m_context, "", m_function); const auto check = llvm::BasicBlock::Create(m_context, "", m_function); m_ir->CreateCondBr(m_ir->CreateICmpEQ(m_ir->CreateLoad(get_type(), pstate, true), m_ir->getInt32(0)), _body, check, m_md_likely); @@ -1069,14 +1060,14 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator if (may_be_unsafe_for_savestate) { - m_ir->CreateStore(m_ir->getInt8(1), spu_ptr(&spu_thread::unsavable))->setVolatile(true); + m_ir->CreateStore(m_ir->getInt8(1), spu_ptr(&spu_thread::unsavable))->setVolatile(true); } m_ir->CreateCall(m_test_state, {m_thread}); if (may_be_unsafe_for_savestate) { - m_ir->CreateStore(m_ir->getInt8(0), spu_ptr(&spu_thread::unsavable))->setVolatile(true); + m_ir->CreateStore(m_ir->getInt8(0), spu_ptr(&spu_thread::unsavable))->setVolatile(true); } m_ir->CreateBr(_body); @@ -1145,7 +1136,7 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator const auto _final = llvm::BasicBlock::Create(m_context, "__putllc16_final", m_function); const auto _eal = (get_reg_fixed(s_reg_mfc_eal) & -128).eval(m_ir); - const auto _raddr = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::raddr)); + const auto _raddr = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::raddr)); m_ir->CreateCondBr(m_ir->CreateAnd(m_ir->CreateICmpEQ(_eal, _raddr), m_ir->CreateIsNotNull(_raddr)), _raddr_match, _fail, m_md_likely); m_ir->SetInsertPoint(_raddr_match); @@ -1259,7 +1250,7 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator m_ir->SetInsertPoint(_fail); call("PUTLLC16_fail", +on_fail, m_thread, _eal); - m_ir->CreateStore(m_ir->getInt64(spu_channel::bit_count | MFC_PUTLLC_FAILURE), spu_ptr(&spu_thread::ch_atomic_stat)); + m_ir->CreateStore(m_ir->getInt64(spu_channel::bit_count | MFC_PUTLLC_FAILURE), spu_ptr(&spu_thread::ch_atomic_stat)); m_ir->CreateBr(_final); m_ir->SetInsertPoint(_final); @@ -1268,8 +1259,8 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator const auto diff = m_ir->CreateZExt(m_ir->CreateSub(dest, _lsa), get_type()); - const auto _new = m_ir->CreateAlignedLoad(get_type(), _ptr(m_lsptr, dest), llvm::MaybeAlign{16}); - const auto _rdata = m_ir->CreateAlignedLoad(get_type(), _ptr(spu_ptr(&spu_thread::rdata), m_ir->CreateAnd(diff, 0x70)), llvm::MaybeAlign{16}); + const auto _new = m_ir->CreateAlignedLoad(get_type(), _ptr(m_lsptr, dest), llvm::MaybeAlign{16}); + const auto _rdata = m_ir->CreateAlignedLoad(get_type(), _ptr(spu_ptr(&spu_thread::rdata), m_ir->CreateAnd(diff, 0x70)), llvm::MaybeAlign{16}); const bool is_accurate_op = !!g_cfg.core.spu_accurate_reservations; @@ -1287,10 +1278,10 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator m_ir->SetInsertPoint(_begin_op); // Touch memory (on the opposite side of the page) - m_ir->CreateAtomicRMW(llvm::AtomicRMWInst::Or, _ptr(m_memptr, m_ir->CreateXor(_eal, 4096 / 2)), m_ir->getInt8(0), llvm::MaybeAlign{16}, llvm::AtomicOrdering::SequentiallyConsistent); + m_ir->CreateAtomicRMW(llvm::AtomicRMWInst::Or, _ptr(m_memptr, m_ir->CreateXor(_eal, 4096 / 2)), m_ir->getInt8(0), llvm::MaybeAlign{16}, llvm::AtomicOrdering::SequentiallyConsistent); - const auto rptr = _ptr(m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::reserv_base_addr)), ((eal_val & 0xff80) >> 1).eval(m_ir)); - const auto rtime = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::rtime)); + const auto rptr = _ptr(m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::reserv_base_addr)), ((eal_val & 0xff80) >> 1).eval(m_ir)); + const auto rtime = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::rtime)); m_ir->CreateBr(_repeat_lock); m_ir->SetInsertPoint(_repeat_lock); @@ -1313,10 +1304,10 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator m_ir->SetInsertPoint(_lock_success); // Commit 16 bytes compare-exchange - const auto sudo_ptr = _ptr(m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::memory_sudo_addr)), _eal); + const auto sudo_ptr = _ptr(m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::memory_sudo_addr)), _eal); m_ir->CreateCondBr( - m_ir->CreateExtractValue(m_ir->CreateAtomicCmpXchg(_ptr(sudo_ptr, diff), _rdata, _new, llvm::MaybeAlign{16}, llvm::AtomicOrdering::SequentiallyConsistent, llvm::AtomicOrdering::SequentiallyConsistent), 1) + m_ir->CreateExtractValue(m_ir->CreateAtomicCmpXchg(_ptr(sudo_ptr, diff), _rdata, _new, llvm::MaybeAlign{16}, llvm::AtomicOrdering::SequentiallyConsistent, llvm::AtomicOrdering::SequentiallyConsistent), 1) , _success_and_unlock , _fail_and_unlock); @@ -1333,13 +1324,13 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator // Perform unlocked vm::reservation_update if no physical memory changes needed m_ir->SetInsertPoint(_inc_res); - const auto rptr2 = _ptr(m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::reserv_base_addr)), ((eal_val & 0xff80) >> 1).eval(m_ir)); + const auto rptr2 = _ptr(m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::reserv_base_addr)), ((eal_val & 0xff80) >> 1).eval(m_ir)); llvm::Value* old_val{}; if (true || is_accurate_op) { - old_val = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::rtime)); + old_val = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::rtime)); } else { @@ -1360,8 +1351,8 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator } m_ir->SetInsertPoint(_success); - m_ir->CreateStore(m_ir->getInt64(spu_channel::bit_count | MFC_PUTLLC_SUCCESS), spu_ptr(&spu_thread::ch_atomic_stat)); - m_ir->CreateStore(m_ir->getInt32(0), spu_ptr(&spu_thread::raddr)); + m_ir->CreateStore(m_ir->getInt64(spu_channel::bit_count | MFC_PUTLLC_SUCCESS), spu_ptr(&spu_thread::ch_atomic_stat)); + m_ir->CreateStore(m_ir->getInt32(0), spu_ptr(&spu_thread::raddr)); m_ir->CreateBr(_final); m_ir->SetInsertPoint(_fail_and_unlock); @@ -1370,7 +1361,7 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator m_ir->SetInsertPoint(_fail); call("PUTLLC16_fail", +on_fail, m_thread, _eal); - m_ir->CreateStore(m_ir->getInt64(spu_channel::bit_count | MFC_PUTLLC_FAILURE), spu_ptr(&spu_thread::ch_atomic_stat)); + m_ir->CreateStore(m_ir->getInt64(spu_channel::bit_count | MFC_PUTLLC_FAILURE), spu_ptr(&spu_thread::ch_atomic_stat)); m_ir->CreateBr(_final); m_ir->SetInsertPoint(_final); @@ -1410,7 +1401,7 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator const auto _final = llvm::BasicBlock::Create(m_context, "", m_function); const auto _eal = (get_reg_fixed(s_reg_mfc_eal) & -128).eval(m_ir); - const auto _raddr = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::raddr)); + const auto _raddr = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::raddr)); m_ir->CreateCondBr(m_ir->CreateAnd(m_ir->CreateICmpEQ(_eal, _raddr), m_ir->CreateIsNotNull(_raddr)), _next, _fail, m_md_likely); m_ir->SetInsertPoint(_next); @@ -1418,8 +1409,8 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator value_t eal_val; eal_val.value = _eal; - const auto rptr = _ptr(m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::reserv_base_addr)), ((eal_val & 0xff80) >> 1).eval(m_ir)); - const auto rval = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::rtime)); + const auto rptr = _ptr(m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::reserv_base_addr)), ((eal_val & 0xff80) >> 1).eval(m_ir)); + const auto rval = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::rtime)); m_ir->CreateCondBr( m_ir->CreateExtractValue(m_ir->CreateAtomicCmpXchg(rptr, rval, m_ir->CreateAdd(rval, m_ir->getInt64(128)), llvm::MaybeAlign{16}, llvm::AtomicOrdering::SequentiallyConsistent, llvm::AtomicOrdering::SequentiallyConsistent), 1) , _next0 @@ -1427,16 +1418,16 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator m_ir->SetInsertPoint(_next0); //call("atomic_wait_engine::notify_all", static_cast(atomic_wait_engine::notify_all), rptr); - m_ir->CreateStore(m_ir->getInt64(spu_channel::bit_count | MFC_PUTLLC_SUCCESS), spu_ptr(&spu_thread::ch_atomic_stat)); + m_ir->CreateStore(m_ir->getInt64(spu_channel::bit_count | MFC_PUTLLC_SUCCESS), spu_ptr(&spu_thread::ch_atomic_stat)); m_ir->CreateBr(_final); m_ir->SetInsertPoint(_fail); call("PUTLLC0_fail", +on_fail, m_thread, _eal); - m_ir->CreateStore(m_ir->getInt64(spu_channel::bit_count | MFC_PUTLLC_FAILURE), spu_ptr(&spu_thread::ch_atomic_stat)); + m_ir->CreateStore(m_ir->getInt64(spu_channel::bit_count | MFC_PUTLLC_FAILURE), spu_ptr(&spu_thread::ch_atomic_stat)); m_ir->CreateBr(_final); m_ir->SetInsertPoint(_final); - m_ir->CreateStore(m_ir->getInt32(0), spu_ptr(&spu_thread::raddr)); + m_ir->CreateStore(m_ir->getInt32(0), spu_ptr(&spu_thread::raddr)); } public: @@ -1624,10 +1615,10 @@ public: const auto label_stop = BasicBlock::Create(m_context, "", m_function); // Load PC, which will be the actual value of 'm_base' - m_base_pc = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::pc)); + m_base_pc = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::pc)); // Emit state check - const auto pstate = spu_ptr(&spu_thread::state); + const auto pstate = spu_ptr(&spu_thread::state); m_ir->CreateCondBr(m_ir->CreateICmpNE(m_ir->CreateLoad(get_type(), pstate), m_ir->getInt32(0)), label_stop, label_test, m_md_unlikely); // Emit code check @@ -1636,7 +1627,7 @@ public: // Set block hash for profiling (if enabled) if (g_cfg.core.spu_prof && g_cfg.core.spu_verification) - m_ir->CreateStore(m_ir->getInt64((m_hash_start & -65536)), spu_ptr(&spu_thread::block_hash)); + m_ir->CreateStore(m_ir->getInt64((m_hash_start & -65536)), spu_ptr(&spu_thread::block_hash)); if (!g_cfg.core.spu_verification) { @@ -1739,7 +1730,7 @@ public: llvm::Value* vls = nullptr; // Load unaligned code block from LS - vls = m_ir->CreateAlignedLoad(get_type(), _ptr(data_addr, j - starta), llvm::MaybeAlign{4}); + vls = m_ir->CreateAlignedLoad(get_type(), _ptr(data_addr, j - starta), llvm::MaybeAlign{4}); // Mask if necessary if (holes) @@ -1828,15 +1819,15 @@ public: // Load unaligned code block from LS if (m_use_avx512) { - vls = m_ir->CreateAlignedLoad(get_type(), _ptr(data_addr, j - starta), llvm::MaybeAlign{4}); + vls = m_ir->CreateAlignedLoad(get_type(), _ptr(data_addr, j - starta), llvm::MaybeAlign{4}); } else if (m_use_avx) { - vls = m_ir->CreateAlignedLoad(get_type(), _ptr(data_addr, j - starta), llvm::MaybeAlign{4}); + vls = m_ir->CreateAlignedLoad(get_type(), _ptr(data_addr, j - starta), llvm::MaybeAlign{4}); } else { - vls = m_ir->CreateAlignedLoad(get_type(), _ptr(data_addr, j - starta), llvm::MaybeAlign{4}); + vls = m_ir->CreateAlignedLoad(get_type(), _ptr(data_addr, j - starta), llvm::MaybeAlign{4}); } // Mask if necessary @@ -1899,7 +1890,7 @@ public: // Increase block counter with statistics m_ir->SetInsertPoint(label_body); - const auto pbcount = spu_ptr(&spu_thread::block_counter); + const auto pbcount = spu_ptr(&spu_thread::block_counter); m_ir->CreateStore(m_ir->CreateAdd(m_ir->CreateLoad(get_type(), pbcount), m_ir->getInt64(check_iterations)), pbcount); // Call the entry function chunk @@ -1933,7 +1924,7 @@ public: if (g_cfg.core.spu_verification) { - const auto pbfail = spu_ptr(&spu_thread::block_failure); + const auto pbfail = spu_ptr(&spu_thread::block_failure); m_ir->CreateStore(m_ir->CreateAdd(m_ir->CreateLoad(get_type(), pbfail), m_ir->getInt64(1)), pbfail); const auto dispci = call("spu_dispatch", spu_runtime::tr_dispatch, m_thread, m_lsptr, main_arg2); dispci->setCallingConv(CallingConv::GHC); @@ -1993,7 +1984,7 @@ public: // Set block hash for profiling (if enabled) if (g_cfg.core.spu_prof) - m_ir->CreateStore(m_ir->getInt64((m_hash_start & -65536) | (m_entry >> 2)), spu_ptr(&spu_thread::block_hash)); + m_ir->CreateStore(m_ir->getInt64((m_hash_start & -65536) | (m_entry >> 2)), spu_ptr(&spu_thread::block_hash)); m_finfo = &m_functions[m_entry]; m_ir->CreateBr(add_block(m_entry)); @@ -2907,7 +2898,7 @@ public: set_function(main_func); // Load pc and opcode - m_interp_pc = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::pc)); + m_interp_pc = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::pc)); m_interp_op = m_ir->CreateLoad(get_type(), m_ir->CreatePtrAdd(m_lsptr, m_ir->CreateZExt(m_interp_pc, get_type()))); m_interp_op = m_ir->CreateCall(get_intrinsic(Intrinsic::bswap), {m_interp_op}); @@ -2921,7 +2912,7 @@ public: m_interp_regs = _ptr(m_thread, get_reg_offset(0)); // Save host thread's stack pointer - const auto native_sp = spu_ptr(&spu_thread::hv_ctx, &rpcs3::hypervisor_context_t::regs); + const auto native_sp = spu_ptr(&spu_thread::hv_ctx, &rpcs3::hypervisor_context_t::regs); #if defined(ARCH_X64) const auto rsp_name = MetadataAsValue::get(m_context, MDNode::get(m_context, {MDString::get(m_context, "rsp")})); #elif defined(ARCH_ARM64) @@ -3007,7 +2998,7 @@ public: m_interp_regs = f->getArg(6); m_ir->SetInsertPoint(BasicBlock::Create(m_context, "", f)); - m_memptr = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::memory_base_addr)); + m_memptr = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::memory_base_addr)); switch (itype) { @@ -3023,7 +3014,7 @@ public: case spu_itype::WRCH: { // Invalid or abortable instruction. Save current address. - m_ir->CreateStore(m_interp_pc, spu_ptr(&spu_thread::pc)); + m_ir->CreateStore(m_interp_pc, spu_ptr(&spu_thread::pc)); [[fallthrough]]; } default: @@ -3067,7 +3058,7 @@ public: { if (check) { - m_ir->CreateStore(m_interp_pc, spu_ptr(&spu_thread::pc)); + m_ir->CreateStore(m_interp_pc, spu_ptr(&spu_thread::pc)); } // Decode next instruction. @@ -3104,9 +3095,9 @@ public: { const auto _stop = BasicBlock::Create(m_context, "", f); const auto _next = BasicBlock::Create(m_context, "", f); - m_ir->CreateCondBr(m_ir->CreateIsNotNull(m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::state))), _stop, _next, m_md_unlikely); + m_ir->CreateCondBr(m_ir->CreateIsNotNull(m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::state))), _stop, _next, m_md_unlikely); m_ir->SetInsertPoint(_stop); - m_ir->CreateStore(m_interp_pc, spu_ptr(&spu_thread::pc)); + m_ir->CreateStore(m_interp_pc, spu_ptr(&spu_thread::pc)); const auto escape_yes = BasicBlock::Create(m_context, "", f); const auto escape_no = BasicBlock::Create(m_context, "", f); @@ -3160,7 +3151,7 @@ public: // Call next instruction. const auto _stop = BasicBlock::Create(m_context, "", f); const auto _next = BasicBlock::Create(m_context, "", f); - m_ir->CreateCondBr(m_ir->CreateIsNotNull(m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::state))), _stop, _next, m_md_unlikely); + m_ir->CreateCondBr(m_ir->CreateIsNotNull(m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::state))), _stop, _next, m_md_unlikely); m_ir->SetInsertPoint(_next); if (itype == spu_itype::WRCH || @@ -3178,7 +3169,7 @@ public: ncall->setTailCall(); m_ir->CreateRetVoid(); m_ir->SetInsertPoint(_stop); - m_ir->CreateStore(m_interp_pc, spu_ptr(&spu_thread::pc)); + m_ir->CreateStore(m_interp_pc, spu_ptr(&spu_thread::pc)); call("spu_escape", spu_runtime::g_escape, m_thread)->setTailCall(); m_ir->CreateRetVoid(); } @@ -3303,7 +3294,7 @@ public: { if (m_interp_magn) { - m_ir->CreateStore(m_interp_pc, spu_ptr(&spu_thread::pc)); + m_ir->CreateStore(m_interp_pc, spu_ptr(&spu_thread::pc)); call("spu_unknown", &exec_unk, m_thread, m_ir->getInt32(op_unk.opcode)); return; } @@ -3410,7 +3401,7 @@ public: llvm::Value* get_rdch(spu_opcode_t op, u32 off, bool atomic) { - const auto ptr = _ptr(m_thread, off); + const auto ptr = _ptr(m_thread, off); llvm::Value* val0; if (atomic) @@ -3458,7 +3449,7 @@ public: { case SPU_RdSRR0: { - res.value = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::srr0)); + res.value = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::srr0)); break; } case SPU_RdInMbox: @@ -3475,7 +3466,7 @@ public: } case MFC_RdTagMask: { - res.value = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::ch_tag_mask)); + res.value = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::ch_tag_mask)); break; } case SPU_RdSigNotify1: @@ -3508,13 +3499,13 @@ public: if (utils::get_tsc_freq() && !(g_cfg.core.spu_loop_detection) && (g_cfg.core.clocks_scale == 100)) { const auto timebase_offs = m_ir->CreateLoad(get_type(), m_ir->CreateIntToPtr(m_ir->getInt64(reinterpret_cast(&g_timebase_offs)), get_type())); - const auto timestamp = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::ch_dec_start_timestamp)); - const auto dec_value = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::ch_dec_value)); + const auto timestamp = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::ch_dec_start_timestamp)); + const auto dec_value = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::ch_dec_value)); const auto tsc = m_ir->CreateCall(get_intrinsic(llvm::Intrinsic::x86_rdtsc)); const auto tscx = m_ir->CreateMul(m_ir->CreateUDiv(tsc, m_ir->getInt64(utils::get_tsc_freq())), m_ir->getInt64(80000000)); const auto tscm = m_ir->CreateUDiv(m_ir->CreateMul(m_ir->CreateURem(tsc, m_ir->getInt64(utils::get_tsc_freq())), m_ir->getInt64(80000000)), m_ir->getInt64(utils::get_tsc_freq())); const auto tsctb = m_ir->CreateSub(m_ir->CreateAdd(tscx, tscm), timebase_offs); - const auto frz = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::is_dec_frozen)); + const auto frz = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::is_dec_frozen)); const auto frzev = m_ir->CreateICmpEQ(frz, m_ir->getInt8(0)); const auto delta = m_ir->CreateTrunc(m_ir->CreateSub(tsctb, timestamp), get_type()); @@ -3528,7 +3519,7 @@ public: } case SPU_RdEventMask: { - const auto value = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::ch_events)); + const auto value = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::ch_events)); value->setAtomic(llvm::AtomicOrdering::Acquire); res.value = m_ir->CreateTrunc(m_ir->CreateLShr(value, 32), get_type()); break; @@ -3543,22 +3534,22 @@ public: } else { - m_ir->CreateStore(m_ir->getInt8(1), spu_ptr(&spu_thread::unsavable)); + m_ir->CreateStore(m_ir->getInt8(1), spu_ptr(&spu_thread::unsavable)); } res.value = call("spu_read_events", &exec_read_events, m_thread); if (!g_cfg.savestate.compatible_mode) { - m_ir->CreateStore(m_ir->getInt8(0), spu_ptr(&spu_thread::unsavable)); + m_ir->CreateStore(m_ir->getInt8(0), spu_ptr(&spu_thread::unsavable)); } break; } case SPU_RdMachStat: { - res.value = m_ir->CreateZExt(m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::interrupts_enabled)), get_type()); - res.value = m_ir->CreateOr(res.value, m_ir->CreateAnd(m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::thread_type)), m_ir->getInt32(2))); + res.value = m_ir->CreateZExt(m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::interrupts_enabled)), get_type()); + res.value = m_ir->CreateOr(res.value, m_ir->CreateAnd(m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::thread_type)), m_ir->getInt32(2))); break; } @@ -3586,7 +3577,7 @@ public: llvm::Value* get_rchcnt(u32 off, u64 inv = 0) { - const auto val = m_ir->CreateLoad(get_type(), _ptr(m_thread, off)); + const auto val = m_ir->CreateLoad(get_type(), _ptr(m_thread, off)); val->setAtomic(llvm::AtomicOrdering::Acquire); const auto shv = m_ir->CreateLShr(val, spu_channel::off_count); return m_ir->CreateTrunc(m_ir->CreateXor(shv, inv), get_type()); @@ -3608,7 +3599,7 @@ public: return ch->get_count(); }; - return m_ir->CreateXor(call("wait_on_spu_channel", +wait_on_channel, m_thread, _ptr(m_thread, off), m_ir->getInt32(inv == 0u)), m_ir->getInt32(inv)); + return m_ir->CreateXor(call("wait_on_spu_channel", +wait_on_channel, m_thread, _ptr(m_thread, off), m_ir->getInt32(inv == 0u)), m_ir->getInt32(inv)); } void RCHCNT(spu_opcode_t op) // @@ -3687,7 +3678,7 @@ public: return ch->pop_wait(*_spu, false), ch->get_count(); }; - res.value = call("wait_spu_inbox", +wait_inbox, m_thread, spu_ptr(&spu_thread::ch_in_mbox)); + res.value = call("wait_spu_inbox", +wait_inbox, m_thread, spu_ptr(&spu_thread::ch_in_mbox)); break; } default: break; @@ -3744,13 +3735,13 @@ public: } case MFC_Cmd: { - res.value = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::mfc_size)); + res.value = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::mfc_size)); res.value = m_ir->CreateSub(m_ir->getInt32(16), res.value); break; } case SPU_RdInMbox: { - const auto value = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::ch_in_mbox)); + const auto value = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::ch_in_mbox)); value->setAtomic(llvm::AtomicOrdering::Acquire); res.value = value; res.value = m_ir->CreateLShr(res.value, 8); @@ -3759,7 +3750,7 @@ public: } case SPU_RdEventStat: { - const auto mask = m_ir->CreateTrunc(m_ir->CreateLShr(m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::ch_events)), 32), get_type()); + const auto mask = m_ir->CreateTrunc(m_ir->CreateLShr(m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::ch_events)), 32), get_type()); res.value = call("spu_get_events", &exec_get_events, m_thread, mask); break; } @@ -3857,7 +3848,7 @@ public: { case SPU_WrSRR0: { - m_ir->CreateStore(eval(val & 0x3fffc).value, spu_ptr(&spu_thread::srr0)); + m_ir->CreateStore(eval(val & 0x3fffc).value, spu_ptr(&spu_thread::srr0)); return; } case SPU_WrOutIntrMbox: @@ -3873,10 +3864,10 @@ public: case MFC_WrTagMask: { // TODO - m_ir->CreateStore(val.value, spu_ptr(&spu_thread::ch_tag_mask)); + m_ir->CreateStore(val.value, spu_ptr(&spu_thread::ch_tag_mask)); const auto next = llvm::BasicBlock::Create(m_context, "", m_function); const auto _mfc = llvm::BasicBlock::Create(m_context, "", m_function); - m_ir->CreateCondBr(m_ir->CreateICmpNE(m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::ch_tag_upd)), m_ir->getInt32(MFC_TAG_UPDATE_IMMEDIATE)), _mfc, next); + m_ir->CreateCondBr(m_ir->CreateICmpNE(m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::ch_tag_upd)), m_ir->getInt32(MFC_TAG_UPDATE_IMMEDIATE)), _mfc, next); m_ir->SetInsertPoint(_mfc); update_pc(); call("spu_write_channel", &exec_wrch, m_thread, m_ir->getInt32(op.ra), val.value); @@ -3888,11 +3879,11 @@ public: { if (true) { - const auto tag_mask = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::ch_tag_mask)); - const auto mfc_fence = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::mfc_fence)); + const auto tag_mask = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::ch_tag_mask)); + const auto mfc_fence = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::mfc_fence)); const auto completed = m_ir->CreateAnd(tag_mask, m_ir->CreateNot(mfc_fence)); - const auto upd_ptr = spu_ptr(&spu_thread::ch_tag_upd); - const auto stat_ptr = spu_ptr(&spu_thread::ch_tag_stat); + const auto upd_ptr = spu_ptr(&spu_thread::ch_tag_upd); + const auto stat_ptr = spu_ptr(&spu_thread::ch_tag_stat); const auto stat_val = m_ir->CreateOr(m_ir->CreateZExt(completed, get_type()), s64{smin}); const auto next = llvm::BasicBlock::Create(m_context, "", m_function); @@ -3912,7 +3903,7 @@ public: // Illegal update, access violate with special address m_ir->SetInsertPoint(fail); - const auto ptr = _ptr(m_memptr, 0xffdead04); + const auto ptr = _ptr(m_memptr, 0xffdead04); m_ir->CreateStore(m_ir->getInt32("TAG\0"_u32), ptr); m_ir->CreateBr(next); @@ -3945,7 +3936,7 @@ public: } spu_log.warning("[0x%x] MFC_EAH: $%u is not a zero constant", m_pos, +op.rt); - //m_ir->CreateStore(val.value, spu_ptr(&spu_thread::ch_mfc_cmd, &spu_mfc_cmd::eah)); + //m_ir->CreateStore(val.value, spu_ptr(&spu_thread::ch_mfc_cmd, &spu_mfc_cmd::eah)); return; } case MFC_EAL: @@ -3999,8 +3990,8 @@ public: const auto fail = llvm::BasicBlock::Create(m_context, "", m_function); const auto next = llvm::BasicBlock::Create(m_context, "", m_function); - const auto pf = spu_ptr(&spu_thread::mfc_fence); - const auto pb = spu_ptr(&spu_thread::mfc_barrier); + const auto pf = spu_ptr(&spu_thread::mfc_fence); + const auto pb = spu_ptr(&spu_thread::mfc_barrier); switch (u64 cmd = ci->getZExtValue()) { @@ -4025,7 +4016,7 @@ public: m_ir->SetInsertPoint(fail); m_ir->CreateUnreachable(); m_ir->SetInsertPoint(next); - m_ir->CreateStore(ci, spu_ptr(&spu_thread::ch_mfc_cmd, &spu_mfc_cmd::cmd)); + m_ir->CreateStore(ci, spu_ptr(&spu_thread::ch_mfc_cmd, &spu_mfc_cmd::cmd)); update_pc(); ensure_gpr_stores(); call("spu_exec_mfc_cmd_saveable", &exec_mfc_cmd, m_thread); @@ -4044,7 +4035,7 @@ public: m_ir->SetInsertPoint(fail); m_ir->CreateUnreachable(); m_ir->SetInsertPoint(next); - m_ir->CreateStore(ci, spu_ptr(&spu_thread::ch_mfc_cmd, &spu_mfc_cmd::cmd)); + m_ir->CreateStore(ci, spu_ptr(&spu_thread::ch_mfc_cmd, &spu_mfc_cmd::cmd)); update_pc(); call("spu_exec_mfc_cmd", &exec_mfc_cmd, m_thread); return; @@ -4104,7 +4095,7 @@ public: m_ir->SetInsertPoint(mmio); } - m_ir->CreateStore(ci, spu_ptr(&spu_thread::ch_mfc_cmd, &spu_mfc_cmd::cmd)); + m_ir->CreateStore(ci, spu_ptr(&spu_thread::ch_mfc_cmd, &spu_mfc_cmd::cmd)); call("spu_exec_mfc_cmd", &exec_mfc_cmd, m_thread); m_ir->CreateBr(next); m_ir->SetInsertPoint(copy); @@ -4196,7 +4187,7 @@ public: } // Disable certain thing - m_ir->CreateStore(m_ir->getInt32(0), spu_ptr(&spu_thread::last_faddr)); + m_ir->CreateStore(m_ir->getInt32(0), spu_ptr(&spu_thread::last_faddr)); m_ir->CreateBr(next); break; } @@ -4204,7 +4195,7 @@ public: case MFC_EIEIO_CMD: case MFC_SYNC_CMD: { - const auto cond = m_ir->CreateIsNull(m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::mfc_size))); + const auto cond = m_ir->CreateIsNull(m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::mfc_size))); m_ir->CreateCondBr(cond, exec, fail, m_md_likely); m_ir->SetInsertPoint(exec); m_ir->CreateFence(llvm::AtomicOrdering::SequentiallyConsistent); @@ -4226,12 +4217,12 @@ public: m_ir->SetInsertPoint(fail); // Get MFC slot, redirect to invalid memory address - const auto slot = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::mfc_size)); + const auto slot = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::mfc_size)); const auto off0 = m_ir->CreateAdd(m_ir->CreateMul(slot, m_ir->getInt32(sizeof(spu_mfc_cmd))), m_ir->getInt32(::offset32(&spu_thread::mfc_queue))); const auto ptr0 = m_ir->CreatePtrAdd(m_thread, m_ir->CreateZExt(off0, get_type())); const auto ptr1 = m_ir->CreatePtrAdd(m_memptr, m_ir->getInt64(0xffdeadf0)); const auto pmfc = m_ir->CreateSelect(m_ir->CreateICmpULT(slot, m_ir->getInt32(16)), ptr0, ptr1); - m_ir->CreateStore(ci, _ptr(pmfc, ::offset32(&spu_mfc_cmd::cmd))); + m_ir->CreateStore(ci, _ptr(pmfc, ::offset32(&spu_mfc_cmd::cmd))); switch (u64 cmd = ci->getZExtValue()) { @@ -4271,10 +4262,10 @@ public: case MFC_GETB_CMD: case MFC_GETF_CMD: { - m_ir->CreateStore(tag.value, _ptr(pmfc, ::offset32(&spu_mfc_cmd::tag))); - m_ir->CreateStore(size.value, _ptr(pmfc, ::offset32(&spu_mfc_cmd::size))); - m_ir->CreateStore(lsa.value, _ptr(pmfc, ::offset32(&spu_mfc_cmd::lsa))); - m_ir->CreateStore(eal.value, _ptr(pmfc, ::offset32(&spu_mfc_cmd::eal))); + m_ir->CreateStore(tag.value, _ptr(pmfc, ::offset32(&spu_mfc_cmd::tag))); + m_ir->CreateStore(size.value, _ptr(pmfc, ::offset32(&spu_mfc_cmd::size))); + m_ir->CreateStore(lsa.value, _ptr(pmfc, ::offset32(&spu_mfc_cmd::lsa))); + m_ir->CreateStore(eal.value, _ptr(pmfc, ::offset32(&spu_mfc_cmd::eal))); m_ir->CreateStore(m_ir->CreateOr(m_ir->CreateLoad(get_type(), pf), mask), pf); if (cmd & MFC_BARRIER_MASK) m_ir->CreateStore(m_ir->CreateOr(m_ir->CreateLoad(get_type(), pb), mask), pb); @@ -4295,7 +4286,7 @@ public: } } - m_ir->CreateStore(m_ir->CreateAdd(slot, m_ir->getInt32(1)), spu_ptr(&spu_thread::mfc_size)); + m_ir->CreateStore(m_ir->CreateAdd(slot, m_ir->getInt32(1)), spu_ptr(&spu_thread::mfc_size)); m_ir->CreateBr(next); m_ir->SetInsertPoint(next); return; @@ -4308,7 +4299,7 @@ public: case MFC_WrListStallAck: { const auto mask = eval(splat(1) << (val & 0x1f)); - const auto _ptr = spu_ptr(&spu_thread::ch_stall_mask); + const auto _ptr = spu_ptr(&spu_thread::ch_stall_mask); const auto _old = m_ir->CreateLoad(get_type(), _ptr); const auto _new = m_ir->CreateAnd(_old, m_ir->CreateNot(mask.value)); m_ir->CreateStore(_new, _ptr); @@ -4335,16 +4326,16 @@ public: const auto tscx = m_ir->CreateMul(m_ir->CreateUDiv(tsc, m_ir->getInt64(utils::get_tsc_freq())), m_ir->getInt64(80000000)); const auto tscm = m_ir->CreateUDiv(m_ir->CreateMul(m_ir->CreateURem(tsc, m_ir->getInt64(utils::get_tsc_freq())), m_ir->getInt64(80000000)), m_ir->getInt64(utils::get_tsc_freq())); const auto tsctb = m_ir->CreateSub(m_ir->CreateAdd(tscx, tscm), timebase_offs); - m_ir->CreateStore(tsctb, spu_ptr(&spu_thread::ch_dec_start_timestamp)); + m_ir->CreateStore(tsctb, spu_ptr(&spu_thread::ch_dec_start_timestamp)); } else #endif { - m_ir->CreateStore(call("get_timebased_time", &get_timebased_time), spu_ptr(&spu_thread::ch_dec_start_timestamp)); + m_ir->CreateStore(call("get_timebased_time", &get_timebased_time), spu_ptr(&spu_thread::ch_dec_start_timestamp)); } - m_ir->CreateStore(val.value, spu_ptr(&spu_thread::ch_dec_value)); - m_ir->CreateStore(m_ir->getInt8(0), spu_ptr(&spu_thread::is_dec_frozen)); + m_ir->CreateStore(val.value, spu_ptr(&spu_thread::ch_dec_value)); + m_ir->CreateStore(m_ir->getInt8(0), spu_ptr(&spu_thread::is_dec_frozen)); return; } case SPU_Set_Bkmk_Tag: @@ -7625,10 +7616,10 @@ public: m_ir->CreateCondBr(cond.value, halt, next, m_md_unlikely); m_ir->SetInsertPoint(halt); if (m_interp_magn) - m_ir->CreateStore(m_function->getArg(2), spu_ptr(&spu_thread::pc)); + m_ir->CreateStore(m_function->getArg(2), spu_ptr(&spu_thread::pc)); else update_pc(); - const auto ptr = _ptr(m_memptr, 0xffdead00); + const auto ptr = _ptr(m_memptr, 0xffdead00); m_ir->CreateStore(m_ir->getInt32("HALT"_u32), ptr); m_ir->CreateBr(next); m_ir->SetInsertPoint(next); @@ -7732,7 +7723,7 @@ public: target->addIncoming(e_addr, e_exec); m_ir->CreateCondBr(get_imm(op.d).value, d_exec, d_done, m_md_unlikely); m_ir->SetInsertPoint(d_exec); - m_ir->CreateStore(m_ir->getFalse(), spu_ptr(&spu_thread::interrupts_enabled)); + m_ir->CreateStore(m_ir->getFalse(), spu_ptr(&spu_thread::interrupts_enabled)); m_ir->CreateBr(d_done); m_ir->SetInsertPoint(d_done); m_ir->CreateBr(m_interp_bblock); @@ -7768,7 +7759,7 @@ public: } else { - sp.value = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::gpr, 1, &v128::_u32, 3)); + sp.value = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::gpr, 1, &v128::_u32, 3)); } } @@ -7783,10 +7774,10 @@ public: if (op.d) { - m_ir->CreateStore(m_ir->getFalse(), spu_ptr(&spu_thread::interrupts_enabled)); + m_ir->CreateStore(m_ir->getFalse(), spu_ptr(&spu_thread::interrupts_enabled)); } - m_ir->CreateStore(addr.value, spu_ptr(&spu_thread::pc)); + m_ir->CreateStore(addr.value, spu_ptr(&spu_thread::pc)); if (ret && g_cfg.core.spu_block_size >= spu_block_size_type::mega) { @@ -8050,7 +8041,7 @@ public: if (op.d && tfound != m_targets.end() && tfound->second.size() == 1 && tfound->second[0] == spu_branch_target(m_pos, 1)) { // Interrupts-disable pattern - m_ir->CreateStore(m_ir->getFalse(), spu_ptr(&spu_thread::interrupts_enabled)); + m_ir->CreateStore(m_ir->getFalse(), spu_ptr(&spu_thread::interrupts_enabled)); return; } @@ -8110,12 +8101,12 @@ public: // Exit function on unexpected target m_ir->SetInsertPoint(sw->getDefaultDest()); - m_ir->CreateStore(addr.value, spu_ptr(&spu_thread::pc)); + m_ir->CreateStore(addr.value, spu_ptr(&spu_thread::pc)); if (m_finfo && m_finfo->fn) { // Can't afford external tail call in true functions - m_ir->CreateStore(m_ir->getInt32("BIJT"_u32), _ptr(m_memptr, 0xffdead20)); + m_ir->CreateStore(m_ir->getInt32("BIJT"_u32), _ptr(m_memptr, 0xffdead20)); m_ir->CreateCall(m_test_state, {m_thread}); m_ir->CreateBr(sw->getDefaultDest()); } @@ -8143,7 +8134,7 @@ public: { if (m_block) m_block->block_end = m_ir->GetInsertBlock(); value_t srr0; - srr0.value = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::srr0)); + srr0.value = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::srr0)); m_ir->CreateBr(add_block_indirect(op, srr0)); } @@ -8152,7 +8143,7 @@ public: if (m_block) m_block->block_end = m_ir->GetInsertBlock(); const auto addr = eval(extract(get_vr(op.ra), 3) & 0x3fffc); set_link(op); - const auto mask = m_ir->CreateTrunc(m_ir->CreateLShr(m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::ch_events), true), 32), get_type()); + const auto mask = m_ir->CreateTrunc(m_ir->CreateLShr(m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::ch_events), true), 32), get_type()); const auto res = call("spu_get_events", &exec_get_events, m_thread, mask); const auto target = add_block_indirect(op, addr); m_ir->CreateCondBr(m_ir->CreateICmpNE(res, m_ir->getInt32(0)), target, add_block_next()); From 8e34d7885c325ebb20e01ceef1d2faf47a55609d Mon Sep 17 00:00:00 2001 From: oltolm Date: Sat, 9 Aug 2025 18:48:15 +0200 Subject: [PATCH 020/149] SPULLVMRecompiler: add overload for _ptr --- rpcs3/Emu/Cell/SPULLVMRecompiler.cpp | 58 +++++++++++++++------------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp index 32c58a8b24..b8f30de43f 100644 --- a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp +++ b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp @@ -537,6 +537,12 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator return m_ir->CreatePtrAdd(base, offset); } + template + llvm::Value* _ptr(llvm::Value* base, Args... offset_args) + { + return m_ir->CreatePtrAdd(base, m_ir->getInt64(::offset32(offset_args...))); + } + template llvm::Value* spu_ptr(Args... offset_args) { @@ -622,7 +628,7 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator const auto idx = m_ir->CreateAnd(I > 4 ? isr : isl, m_interp_7f0); // Pointer to the register - return m_ir->CreatePtrAdd(m_interp_regs, m_ir->CreateZExt(idx, get_type())); + return _ptr(m_interp_regs, m_ir->CreateZExt(idx, get_type())); } llvm::Value* double_as_uint64(llvm::Value* val) @@ -1636,13 +1642,13 @@ public: } else if (func.data.size() == 1) { - const auto pu32 = m_ir->CreatePtrAdd(m_lsptr, m_base_pc); + const auto pu32 = _ptr(m_lsptr, m_base_pc); const auto cond = m_ir->CreateICmpNE(m_ir->CreateLoad(get_type(), pu32), m_ir->getInt32(func.data[0])); m_ir->CreateCondBr(cond, label_diff, label_body, m_md_unlikely); } else if (func.data.size() == 2) { - const auto pu64 = m_ir->CreatePtrAdd(m_lsptr, m_base_pc); + const auto pu64 = _ptr(m_lsptr, m_base_pc); const auto cond = m_ir->CreateICmpNE(m_ir->CreateLoad(get_type(), pu64), m_ir->getInt64(static_cast(func.data[1]) << 32 | func.data[0])); m_ir->CreateCondBr(cond, label_diff, label_body, m_md_unlikely); } @@ -1688,7 +1694,7 @@ public: // Get actual pc corresponding to the found beginning of the data llvm::Value* starta_pc = m_ir->CreateAnd(get_pc(starta), 0x3fffc); - llvm::Value* data_addr = m_ir->CreatePtrAdd(m_lsptr, starta_pc); + llvm::Value* data_addr = _ptr(m_lsptr, starta_pc); llvm::Value* acc0 = nullptr; llvm::Value* acc1 = nullptr; @@ -2899,7 +2905,7 @@ public: // Load pc and opcode m_interp_pc = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::pc)); - m_interp_op = m_ir->CreateLoad(get_type(), m_ir->CreatePtrAdd(m_lsptr, m_ir->CreateZExt(m_interp_pc, get_type()))); + m_interp_op = m_ir->CreateLoad(get_type(), _ptr(m_lsptr, m_ir->CreateZExt(m_interp_pc, get_type()))); m_interp_op = m_ir->CreateCall(get_intrinsic(Intrinsic::bswap), {m_interp_op}); // Pinned constant, address of interpreter table @@ -3063,7 +3069,7 @@ public: // Decode next instruction. const auto next_pc = itype & spu_itype::branch ? m_interp_pc : m_interp_pc_next; - const auto be32_op = m_ir->CreateLoad(get_type(), m_ir->CreatePtrAdd(m_lsptr, m_ir->CreateZExt(next_pc, get_type()))); + const auto be32_op = m_ir->CreateLoad(get_type(), _ptr(m_lsptr, m_ir->CreateZExt(next_pc, get_type()))); const auto next_op = m_ir->CreateCall(get_intrinsic(Intrinsic::bswap), {be32_op}); const auto next_if = m_ir->CreateLoad(m_ir->getPtrTy(), m_ir->CreateGEP(m_ir->getPtrTy(), m_interp_table, m_ir->CreateLShr(next_op, 32u - m_interp_magn))); llvm::cast(next_if)->setVolatile(true); @@ -4066,8 +4072,8 @@ public: csize = -1; } - llvm::Value* src = m_ir->CreatePtrAdd(m_lsptr, zext(lsa).eval(m_ir)); - llvm::Value* dst = m_ir->CreatePtrAdd(m_memptr, zext(eal).eval(m_ir)); + llvm::Value* src = _ptr(m_lsptr, zext(lsa).eval(m_ir)); + llvm::Value* dst = _ptr(m_memptr, zext(eal).eval(m_ir)); if (cmd & MFC_GET_CMD) { @@ -4164,8 +4170,8 @@ public: // Generate fixed sequence of copy operations for (u32 i = 0; i < csize; i += stride) { - const auto _src = m_ir->CreatePtrAdd(src, m_ir->getInt32(i)); - const auto _dst = m_ir->CreatePtrAdd(dst, m_ir->getInt32(i)); + const auto _src = _ptr(src, i); + const auto _dst = _ptr(dst, i); if (csize - i < stride) { m_ir->CreateStore(m_ir->CreateLoad(get_type(), _src), _dst); @@ -4219,10 +4225,10 @@ public: // Get MFC slot, redirect to invalid memory address const auto slot = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::mfc_size)); const auto off0 = m_ir->CreateAdd(m_ir->CreateMul(slot, m_ir->getInt32(sizeof(spu_mfc_cmd))), m_ir->getInt32(::offset32(&spu_thread::mfc_queue))); - const auto ptr0 = m_ir->CreatePtrAdd(m_thread, m_ir->CreateZExt(off0, get_type())); - const auto ptr1 = m_ir->CreatePtrAdd(m_memptr, m_ir->getInt64(0xffdeadf0)); + const auto ptr0 = _ptr(m_thread, m_ir->CreateZExt(off0, get_type())); + const auto ptr1 = _ptr(m_memptr, 0xffdeadf0); const auto pmfc = m_ir->CreateSelect(m_ir->CreateICmpULT(slot, m_ir->getInt32(16)), ptr0, ptr1); - m_ir->CreateStore(ci, _ptr(pmfc, ::offset32(&spu_mfc_cmd::cmd))); + m_ir->CreateStore(ci, _ptr(pmfc, &spu_mfc_cmd::cmd)); switch (u64 cmd = ci->getZExtValue()) { @@ -4262,10 +4268,10 @@ public: case MFC_GETB_CMD: case MFC_GETF_CMD: { - m_ir->CreateStore(tag.value, _ptr(pmfc, ::offset32(&spu_mfc_cmd::tag))); - m_ir->CreateStore(size.value, _ptr(pmfc, ::offset32(&spu_mfc_cmd::size))); - m_ir->CreateStore(lsa.value, _ptr(pmfc, ::offset32(&spu_mfc_cmd::lsa))); - m_ir->CreateStore(eal.value, _ptr(pmfc, ::offset32(&spu_mfc_cmd::eal))); + m_ir->CreateStore(tag.value, _ptr(pmfc, &spu_mfc_cmd::tag)); + m_ir->CreateStore(size.value, _ptr(pmfc, &spu_mfc_cmd::size)); + m_ir->CreateStore(lsa.value, _ptr(pmfc, &spu_mfc_cmd::lsa)); + m_ir->CreateStore(eal.value, _ptr(pmfc, &spu_mfc_cmd::eal)); m_ir->CreateStore(m_ir->CreateOr(m_ir->CreateLoad(get_type(), pf), mask), pf); if (cmd & MFC_BARRIER_MASK) m_ir->CreateStore(m_ir->CreateOr(m_ir->CreateLoad(get_type(), pb), mask), pb); @@ -7495,13 +7501,13 @@ public: void make_store_ls(value_t addr, value_t data) { const auto bswapped = byteswap(data); - m_ir->CreateStore(bswapped.eval(m_ir), m_ir->CreatePtrAdd(m_lsptr, addr.value)); + m_ir->CreateStore(bswapped.eval(m_ir), _ptr(m_lsptr, addr.value)); } auto make_load_ls(value_t addr) { value_t data; - data.value = m_ir->CreateLoad(get_type(), m_ir->CreatePtrAdd(m_lsptr, addr.value)); + data.value = m_ir->CreateLoad(get_type(), _ptr(m_lsptr, addr.value)); return byteswap(data); } @@ -7784,21 +7790,21 @@ public: // Compare address stored in stack mirror with addr const auto stack0 = eval(zext(sp) + ::offset32(&spu_thread::stack_mirror)); const auto stack1 = eval(stack0 + 8); - const auto _ret = m_ir->CreateLoad(get_type(), m_ir->CreatePtrAdd(m_thread, stack0.value)); - const auto link = m_ir->CreateLoad(get_type(), m_ir->CreatePtrAdd(m_thread, stack1.value)); + const auto _ret = m_ir->CreateLoad(get_type(), _ptr(m_thread, stack0.value)); + const auto link = m_ir->CreateLoad(get_type(), _ptr(m_thread, stack1.value)); const auto fail = llvm::BasicBlock::Create(m_context, "", m_function); const auto done = llvm::BasicBlock::Create(m_context, "", m_function); const auto next = llvm::BasicBlock::Create(m_context, "", m_function); m_ir->CreateCondBr(m_ir->CreateICmpEQ(addr.value, m_ir->CreateTrunc(link, get_type())), next, fail, m_md_likely); m_ir->SetInsertPoint(next); - const auto cmp2 = m_ir->CreateLoad(get_type(), m_ir->CreatePtrAdd(m_lsptr, addr.value)); + const auto cmp2 = m_ir->CreateLoad(get_type(), _ptr(m_lsptr, addr.value)); m_ir->CreateCondBr(m_ir->CreateICmpEQ(cmp2, m_ir->CreateTrunc(_ret, get_type())), done, fail, m_md_likely); m_ir->SetInsertPoint(done); // Clear stack mirror and return by tail call to the provided return address - m_ir->CreateStore(splat(-1).eval(m_ir), m_ir->CreatePtrAdd(m_thread, stack0.value)); + m_ir->CreateStore(splat(-1).eval(m_ir), _ptr(m_thread, stack0.value)); const auto type = m_finfo->chunk->getFunctionType(); - const auto fval = m_ir->CreatePtrAdd(get_segment_base(), m_ir->CreateLShr(_ret, 32)); + const auto fval = _ptr(get_segment_base(), m_ir->CreateLShr(_ret, 32)); tail_chunk({type, fval}, m_ir->CreateTrunc(m_ir->CreateLShr(link, 32), get_type())); m_ir->SetInsertPoint(fail); } @@ -8481,8 +8487,8 @@ public: const auto rel_ptr = m_ir->CreateSub(m_ir->CreatePtrToInt(pfunc->chunk, get_type()), m_ir->CreatePtrToInt(get_segment_base(), get_type())); const auto ptr_plus_op = m_ir->CreateOr(m_ir->CreateShl(rel_ptr, 32), m_ir->getInt64(m_next_op)); const auto base_plus_pc = m_ir->CreateOr(m_ir->CreateShl(m_ir->CreateZExt(m_base_pc, get_type()), 32), m_ir->getInt64(m_pos + 4)); - m_ir->CreateStore(ptr_plus_op, m_ir->CreatePtrAdd(m_thread, stack0.value)); - m_ir->CreateStore(base_plus_pc, m_ir->CreatePtrAdd(m_thread, stack1.value)); + m_ir->CreateStore(ptr_plus_op, _ptr(m_thread, stack0.value)); + m_ir->CreateStore(base_plus_pc, _ptr(m_thread, stack1.value)); } } From 246519c5e15ecc7e60d72cee909a1b5e12fb3cb7 Mon Sep 17 00:00:00 2001 From: ADAS2024 <73618406+ADAS2024@users.noreply.github.com> Date: Sun, 17 Aug 2025 18:03:31 -0400 Subject: [PATCH 021/149] Fixes for AppImage not targeting Gamemode (#17398) Targets #17386 --- CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8b0475c899..0492d13184 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,6 +62,7 @@ option(HAS_MEMORY_BREAKPOINTS "Add support for memory breakpoints to the interpr option(USE_LTO "Use LTO for building" ON) option(BUILD_RPCS3_TESTS "Build RPCS3 unit tests." OFF) option(RUN_RPCS3_TESTS "Run RPCS3 unit tests. Requires BUILD_RPCS3_TESTS" OFF) +option(USE_GAMEMODE "Choose whether to enable GameMode features or not." ON) set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/buildfiles/cmake") @@ -166,11 +167,10 @@ endif() ## Look for Gamemode if its installed on Linux if(LINUX) - find_program(GAMEMODE_FOUND gamemoded) ## Only works if gamemode is installed on system (might include lib32 case) - if(GAMEMODE_FOUND) + ## User chooses whether to Enable GameMode features or not + if(USE_GAMEMODE) add_compile_definitions(GAMEMODE_AVAILABLE) endif() - message(GAMEMODE_AVAILABLE="${GAMEMODE_AVAILABLE}") endif() # TODO: do real installation, including copying directory structure From 6e7c3362b3dee6dd2355585eab7f2107f1545485 Mon Sep 17 00:00:00 2001 From: Elad <18193363+knight4u32@users.noreply.github.com> Date: Tue, 19 Aug 2025 12:04:35 +0300 Subject: [PATCH 022/149] SPU: Update RdEventStat Busy Wait detection to updated setting behavior --- rpcs3/Emu/Cell/SPUThread.cpp | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index dbf3b695f4..c083ce97d0 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -4652,7 +4652,7 @@ u32 evaluate_spin_optimization(std::span stats, u64 evaluate_time, const cfg add_count = 0; } - if (inclined_for_responsiveness && std::count(old_stats.data(), old_stats.data() + 3, 0) >= 2) + if (stats.size() == 16 && inclined_for_responsiveness && std::count(old_stats.data(), old_stats.data() + 3, 0) >= 2) { add_count = 0; } @@ -6056,11 +6056,6 @@ s64 spu_thread::get_ch_value(u32 ch) const usz seed = (utils::get_tsc() >> 8) % 100; -#ifdef __linux__ - const bool reservation_busy_waiting = false; -#else - const bool reservation_busy_waiting = (seed + ((raddr == spurs_addr) ? 50u : 0u)) < g_cfg.core.spu_reservation_busy_waiting_percentage; -#endif usz cache_line_waiter_index = umax; auto check_cache_line_waiter = [&]() @@ -6246,8 +6241,7 @@ s64 spu_thread::get_ch_value(u32 ch) } } - // Don't busy-wait with TSX - memory is sensitive - if (g_use_rtm || !reservation_busy_waiting) + if (true) { if (u32 work_count = g_spu_work_count) { @@ -6374,10 +6368,6 @@ s64 spu_thread::get_ch_value(u32 ch) } #endif } - else - { - busy_wait(); - } continue; } From d087c8624352af198a6954d45098d3b5b66f98e9 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sun, 10 Aug 2025 08:47:00 +0200 Subject: [PATCH 023/149] Audio: simplify apply_volume --- rpcs3/Emu/Audio/AudioBackend.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rpcs3/Emu/Audio/AudioBackend.cpp b/rpcs3/Emu/Audio/AudioBackend.cpp index e0d96c8c2b..def9ebfbab 100644 --- a/rpcs3/Emu/Audio/AudioBackend.cpp +++ b/rpcs3/Emu/Audio/AudioBackend.cpp @@ -65,7 +65,7 @@ f32 AudioBackend::apply_volume(const VolumeParam& param, u32 sample_cnt, const f { for (sample_idx = 0; sample_idx < sample_cnt && crnt_vol != param.target_volume; sample_idx += param.ch_cnt) { - crnt_vol = std::min(param.current_volume + (sample_idx + 1) / param.ch_cnt * vol_incr, param.target_volume); + crnt_vol = std::min(crnt_vol + vol_incr, param.target_volume); for (u32 i = 0; i < param.ch_cnt; i++) { @@ -77,7 +77,7 @@ f32 AudioBackend::apply_volume(const VolumeParam& param, u32 sample_cnt, const f { for (sample_idx = 0; sample_idx < sample_cnt && crnt_vol != param.target_volume; sample_idx += param.ch_cnt) { - crnt_vol = std::max(param.current_volume + (sample_idx + 1) / param.ch_cnt * vol_incr, param.target_volume); + crnt_vol = std::max(crnt_vol + vol_incr, param.target_volume); for (u32 i = 0; i < param.ch_cnt; i++) { From d72f95677e92fd6031214d20861379ed005207a5 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Tue, 19 Aug 2025 20:17:22 +0200 Subject: [PATCH 024/149] overlays: add settings for screenshot and recording hints --- rpcs3/Emu/Io/pad_types.cpp | 22 ++++++------------- rpcs3/Emu/system_config.h | 1 + rpcs3/rpcs3qt/emu_settings_type.h | 4 +++- rpcs3/rpcs3qt/gs_frame.cpp | 36 +++++++++++++++++++++++-------- rpcs3/rpcs3qt/settings_dialog.cpp | 3 +++ rpcs3/rpcs3qt/settings_dialog.ui | 9 +++++++- rpcs3/rpcs3qt/tooltips.h | 1 + rpcs3/util/video_provider.cpp | 7 +++++- 8 files changed, 55 insertions(+), 28 deletions(-) diff --git a/rpcs3/Emu/Io/pad_types.cpp b/rpcs3/Emu/Io/pad_types.cpp index 428e0d6cea..9c67cc20f5 100644 --- a/rpcs3/Emu/Io/pad_types.cpp +++ b/rpcs3/Emu/Io/pad_types.cpp @@ -195,14 +195,9 @@ bool Pad::get_pressure_intensity_button_active(bool is_toggle_mode, u32 player_i if (g_cfg.misc.show_pressure_intensity_toggle_hint) { const std::string player_id_string = std::to_string(player_id + 1); - if (m_pressure_intensity_toggled) - { - rsx::overlays::queue_message(get_localized_string(localized_string_id::RSX_OVERLAYS_PRESSURE_INTENSITY_TOGGLED_ON, player_id_string.c_str()), 3'000'000); - } - else - { - rsx::overlays::queue_message(get_localized_string(localized_string_id::RSX_OVERLAYS_PRESSURE_INTENSITY_TOGGLED_OFF, player_id_string.c_str()), 3'000'000); - } + rsx::overlays::queue_message(get_localized_string( + m_pressure_intensity_toggled ? localized_string_id::RSX_OVERLAYS_PRESSURE_INTENSITY_TOGGLED_ON : localized_string_id::RSX_OVERLAYS_PRESSURE_INTENSITY_TOGGLED_OFF, + player_id_string.c_str()), 3'000'000); } } } @@ -235,14 +230,9 @@ bool Pad::get_analog_limiter_button_active(bool is_toggle_mode, u32 player_id) if (g_cfg.misc.show_analog_limiter_toggle_hint) { const std::string player_id_string = std::to_string(player_id + 1); - if (m_analog_limiter_toggled) - { - rsx::overlays::queue_message(get_localized_string(localized_string_id::RSX_OVERLAYS_ANALOG_LIMITER_TOGGLED_ON, player_id_string.c_str()), 3'000'000); - } - else - { - rsx::overlays::queue_message(get_localized_string(localized_string_id::RSX_OVERLAYS_ANALOG_LIMITER_TOGGLED_OFF, player_id_string.c_str()), 3'000'000); - } + rsx::overlays::queue_message(get_localized_string( + m_analog_limiter_toggled ? localized_string_id::RSX_OVERLAYS_ANALOG_LIMITER_TOGGLED_ON : localized_string_id::RSX_OVERLAYS_ANALOG_LIMITER_TOGGLED_OFF, + player_id_string.c_str()), 3'000'000); } } } diff --git a/rpcs3/Emu/system_config.h b/rpcs3/Emu/system_config.h index 2b35e70cb9..c62e56b491 100644 --- a/rpcs3/Emu/system_config.h +++ b/rpcs3/Emu/system_config.h @@ -353,6 +353,7 @@ struct cfg_root : cfg::node 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_capture_hints{ this, "Show capture hints", true, 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 24e7b2fcdd..d0f7019c8a 100644 --- a/rpcs3/rpcs3qt/emu_settings_type.h +++ b/rpcs3/rpcs3qt/emu_settings_type.h @@ -184,6 +184,7 @@ enum class emu_settings_type ShowPressureIntensityToggleHint, ShowAnalogLimiterToggleHint, ShowMouseAndKeyboardToggleHint, + ShowCaptureHints, WindowTitleFormat, PauseDuringHomeMenu, EnableGamemode, @@ -386,10 +387,11 @@ inline static const std::map settings_location { 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::ShowCaptureHints, { "Miscellaneous", "Show capture hints" }}, { emu_settings_type::SilenceAllLogs, { "Miscellaneous", "Silence All Logs" }}, { emu_settings_type::WindowTitleFormat, { "Miscellaneous", "Window Title Format" }}, { emu_settings_type::PauseDuringHomeMenu, { "Miscellaneous", "Pause Emulation During Home Menu" }}, - { emu_settings_type::EnableGamemode, { "Miscellaneous", "Enable GameMode" }}, + { emu_settings_type::EnableGamemode, { "Miscellaneous", "Enable GameMode" }}, // Networking { emu_settings_type::InternetStatus, { "Net", "Internet enabled"}}, diff --git a/rpcs3/rpcs3qt/gs_frame.cpp b/rpcs3/rpcs3qt/gs_frame.cpp index 8cd2a2bc9b..ee50a4fb05 100644 --- a/rpcs3/rpcs3qt/gs_frame.cpp +++ b/rpcs3/rpcs3qt/gs_frame.cpp @@ -434,9 +434,12 @@ void gs_frame::toggle_recording() QApplication::beep(); } - ensure(m_video_encoder->path().starts_with(fs::get_config_dir())); - const std::string shortpath = m_video_encoder->path().substr(fs::get_config_dir().size() - 1); // -1 for / - rsx::overlays::queue_message(tr("Recording saved: %0").arg(QString::fromStdString(shortpath)).toStdString()); + if (g_cfg.misc.show_capture_hints) + { + ensure(m_video_encoder->path().starts_with(fs::get_config_dir())); + const std::string shortpath = m_video_encoder->path().substr(fs::get_config_dir().size() - 1); // -1 for / + rsx::overlays::queue_message(tr("Recording saved: %0").arg(QString::fromStdString(shortpath)).toStdString()); + } } else { @@ -508,7 +511,11 @@ void gs_frame::toggle_recording() if (m_video_encoder->has_error) { - rsx::overlays::queue_message(tr("Recording not possible").toStdString()); + if (g_cfg.misc.show_capture_hints) + { + rsx::overlays::queue_message(tr("Recording not possible").toStdString()); + } + m_video_encoder->stop(); return; } @@ -516,7 +523,12 @@ void gs_frame::toggle_recording() if (!video_provider.set_video_sink(m_video_encoder, recording_mode::rpcs3)) { gui_log.warning("The video provider could not set the video sink. A sink with higher priority must have been set."); - rsx::overlays::queue_message(tr("Recording not possible").toStdString()); + + if (g_cfg.misc.show_capture_hints) + { + rsx::overlays::queue_message(tr("Recording not possible").toStdString()); + } + m_video_encoder->stop(); return; } @@ -525,7 +537,10 @@ void gs_frame::toggle_recording() g_recording_mode = recording_mode::rpcs3; - rsx::overlays::queue_message(tr("Recording started").toStdString()); + if (g_cfg.misc.show_capture_hints) + { + rsx::overlays::queue_message(tr("Recording started").toStdString()); + } } } @@ -1043,9 +1058,12 @@ void gs_frame::take_screenshot(std::vector&& data, u32 sshot_width, u32 ssho } }); - ensure(filename.starts_with(fs::get_config_dir())); - const std::string shortpath = filename.substr(fs::get_config_dir().size() - 1); // -1 for / - rsx::overlays::queue_message(tr("Screenshot saved: %0").arg(QString::fromStdString(shortpath)).toStdString()); + if (g_cfg.misc.show_capture_hints) + { + ensure(filename.starts_with(fs::get_config_dir())); + const std::string shortpath = filename.substr(fs::get_config_dir().size() - 1); // -1 for / + rsx::overlays::queue_message(tr("Screenshot saved: %0").arg(QString::fromStdString(shortpath)).toStdString()); + } return; }, diff --git a/rpcs3/rpcs3qt/settings_dialog.cpp b/rpcs3/rpcs3qt/settings_dialog.cpp index 7569e5adca..ddb26e3144 100644 --- a/rpcs3/rpcs3qt/settings_dialog.cpp +++ b/rpcs3/rpcs3qt/settings_dialog.cpp @@ -1873,6 +1873,9 @@ 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->showCaptureHints, emu_settings_type::ShowCaptureHints); + SubscribeTooltip(ui->showCaptureHints, tooltips.settings.show_capture_hints); + 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 f13b8344cd..4ebc9bb8e8 100644 --- a/rpcs3/rpcs3qt/settings_dialog.ui +++ b/rpcs3/rpcs3qt/settings_dialog.ui @@ -3008,6 +3008,13 @@ + + + + Show capture hints + + + @@ -3028,7 +3035,7 @@ false - false + false Enable GameMode diff --git a/rpcs3/rpcs3qt/tooltips.h b/rpcs3/rpcs3qt/tooltips.h index 0ef5ae76ec..cc2278d5ec 100644 --- a/rpcs3/rpcs3qt/tooltips.h +++ b/rpcs3/rpcs3qt/tooltips.h @@ -151,6 +151,7 @@ public: 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_capture_hints = tr("Shows screenshot and recording hints 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."); diff --git a/rpcs3/util/video_provider.cpp b/rpcs3/util/video_provider.cpp index 82f133b91e..fe305c11de 100644 --- a/rpcs3/util/video_provider.cpp +++ b/rpcs3/util/video_provider.cpp @@ -2,6 +2,7 @@ #include "video_provider.h" #include "Emu/RSX/Overlays/overlay_message.h" #include "Emu/Cell/timers.hpp" +#include "Emu/system_config.h" extern "C" { @@ -89,7 +90,11 @@ namespace utils if (!m_video_sink || m_video_sink->has_error) { g_recording_mode = recording_mode::stopped; - rsx::overlays::queue_message(localized_string_id::RECORDING_ABORTED); + + if (g_cfg.misc.show_capture_hints) + { + rsx::overlays::queue_message(localized_string_id::RECORDING_ABORTED); + } } if (g_recording_mode == recording_mode::stopped) From ce4edf9cb14257c58a9b804922c75305ebcc7181 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Tue, 19 Aug 2025 22:46:00 +0200 Subject: [PATCH 025/149] patch_manager: make patches selectable by double click --- rpcs3/rpcs3.vcxproj | 3 +- rpcs3/rpcs3.vcxproj.filters | 3 ++ rpcs3/rpcs3qt/custom_tree_widget.h | 48 ++++++++++++++++++++++++++ rpcs3/rpcs3qt/patch_manager_dialog.cpp | 6 ++++ rpcs3/rpcs3qt/patch_manager_dialog.ui | 41 +++++++++++++--------- 5 files changed, 83 insertions(+), 18 deletions(-) create mode 100644 rpcs3/rpcs3qt/custom_tree_widget.h diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index dc09c27b25..fbe424d154 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -1374,6 +1374,7 @@ .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWIN32_LEAN_AND_MEAN -DHAVE_VULKAN -DMINIUPNP_STATICLIB -DHAVE_SDL3 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_LIB -DQT_MULTIMEDIAWIDGETS_LIB -DQT_SVG_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\SoundTouch\soundtouch\include" "-I.\..\3rdparty\cubeb\extra" "-I.\..\3rdparty\cubeb\cubeb\include" "-I.\..\3rdparty\flatbuffers\include" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\rtmidi\rtmidi" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\libsdl-org\SDL\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtMultimedia" "-I$(QTDIR)\include\QtMultimediaWidgets" "-I$(QTDIR)\include\QtSvg" + Moc%27ing %(Identity)... @@ -2178,4 +2179,4 @@ - + \ No newline at end of file diff --git a/rpcs3/rpcs3.vcxproj.filters b/rpcs3/rpcs3.vcxproj.filters index ea8a978c8a..2ffaca09b5 100644 --- a/rpcs3/rpcs3.vcxproj.filters +++ b/rpcs3/rpcs3.vcxproj.filters @@ -1439,6 +1439,9 @@ rpcs3 + + Gui\widgets + diff --git a/rpcs3/rpcs3qt/custom_tree_widget.h b/rpcs3/rpcs3qt/custom_tree_widget.h new file mode 100644 index 0000000000..5f7a6cb01f --- /dev/null +++ b/rpcs3/rpcs3qt/custom_tree_widget.h @@ -0,0 +1,48 @@ +#pragma once + +#include +#include + +// Allows to double click the items in order to de/select them +class custom_tree_widget : public QTreeWidget +{ + using QTreeWidget::QTreeWidget; + +public: + void set_checkable_by_double_click_callback(std::function cb) + { + m_checkable_by_dc_cb = std::move(cb); + } + + void mouseDoubleClickEvent(QMouseEvent* event) + { + QTreeWidget::mouseDoubleClickEvent(event); + + if (!event || !m_checkable_by_dc_cb) return; + + const QPoint pos = event->pos(); + QTreeWidgetItem* item = itemAt(pos); + + if (!item || !(item->flags() & Qt::ItemIsUserCheckable)) return; + + const int column = columnAt(pos.x()); + if (!m_checkable_by_dc_cb(item, column)) return; + + const QModelIndex index = indexFromItem(item, column); + if (!index.isValid()) return; + + QStyleOptionViewItem option; + option.initFrom(this); + option.rect = visualRect(index); + option.features = QStyleOptionViewItem::HasCheckIndicator; + + const QRect checkbox_region = style()->subElementRect(QStyle::SE_ItemViewItemCheckIndicator, &option, this); + if (checkbox_region.contains(pos)) return; + + const Qt::CheckState current_state = item->checkState(column); + item->setCheckState(column, (current_state == Qt::CheckState::Checked) ? Qt::CheckState::Unchecked : Qt::CheckState::Checked); + } + +private: + std::function m_checkable_by_dc_cb; +}; diff --git a/rpcs3/rpcs3qt/patch_manager_dialog.cpp b/rpcs3/rpcs3qt/patch_manager_dialog.cpp index 930c999e9e..a58c4b8662 100644 --- a/rpcs3/rpcs3qt/patch_manager_dialog.cpp +++ b/rpcs3/rpcs3qt/patch_manager_dialog.cpp @@ -95,6 +95,12 @@ patch_manager_dialog::patch_manager_dialog(std::shared_ptr gui_set ui->configurable_double_spin_box->setEnabled(false); ui->configurable_double_spin_box->setVisible(false); + // Allow to double click the patches in order to de/select them + ui->patch_tree->set_checkable_by_double_click_callback([](QTreeWidgetItem* item, int column) + { + return item && !item->isDisabled() && (item->flags() & Qt::ItemIsUserCheckable) && static_cast(item->data(column, node_level_role).toInt()) == node_level::patch_level; + }); + // Create connects connect(ui->patch_filter, &QLineEdit::textChanged, this, &patch_manager_dialog::filter_patches); connect(ui->patch_tree, &QTreeWidget::currentItemChanged, this, &patch_manager_dialog::handle_item_selected); diff --git a/rpcs3/rpcs3qt/patch_manager_dialog.ui b/rpcs3/rpcs3qt/patch_manager_dialog.ui index 6daeea160c..a9b224a0a7 100644 --- a/rpcs3/rpcs3qt/patch_manager_dialog.ui +++ b/rpcs3/rpcs3qt/patch_manager_dialog.ui @@ -20,7 +20,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -49,9 +49,9 @@ - + - Qt::CustomContextMenu + Qt::ContextMenuPolicy::CustomContextMenu 20 @@ -70,10 +70,10 @@ - QFrame::NoFrame + QFrame::Shape::NoFrame - QAbstractScrollArea::AdjustToContents + QAbstractScrollArea::SizeAdjustPolicy::AdjustToContents true @@ -121,7 +121,7 @@ true - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByMouse @@ -143,7 +143,7 @@ true - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByMouse @@ -162,7 +162,7 @@ - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByMouse @@ -184,7 +184,7 @@ true - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByMouse @@ -206,7 +206,7 @@ true - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByMouse @@ -228,7 +228,7 @@ true - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByMouse @@ -250,7 +250,7 @@ true - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByMouse @@ -269,13 +269,13 @@ - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop true - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByMouse @@ -324,10 +324,10 @@ - Qt::Vertical + Qt::Orientation::Vertical - QSizePolicy::MinimumExpanding + QSizePolicy::Policy::MinimumExpanding @@ -348,12 +348,19 @@ - QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::RestoreDefaults|QDialogButtonBox::Save + QDialogButtonBox::StandardButton::Apply|QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::RestoreDefaults|QDialogButtonBox::StandardButton::Save + + + custom_tree_widget + QTreeWidget +
rpcs3qt/custom_tree_widget.h
+
+
From e56bc9d15f62cd40f5ac327bbc1daf475a9d7f49 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sun, 24 Aug 2025 09:15:40 +0200 Subject: [PATCH 026/149] vfs_dialog: allow to check list items with double-click --- rpcs3/rpcs3qt/vfs_dialog_path_widget.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/rpcs3/rpcs3qt/vfs_dialog_path_widget.cpp b/rpcs3/rpcs3qt/vfs_dialog_path_widget.cpp index a540278133..c0cfc7bb82 100644 --- a/rpcs3/rpcs3qt/vfs_dialog_path_widget.cpp +++ b/rpcs3/rpcs3qt/vfs_dialog_path_widget.cpp @@ -82,6 +82,12 @@ vfs_dialog_path_widget::vfs_dialog_path_widget(const QString& name, const QStrin } }); + connect(m_dir_list, &QListWidget::itemDoubleClicked, this, [](QListWidgetItem* item) + { + if (!item) return; + item->setCheckState(Qt::CheckState::Checked); + }); + connect(m_dir_list, &QListWidget::currentRowChanged, this, [this, button_remove_dir](int row) { button_remove_dir->setEnabled(m_dir_list->item(row) && row > 0); From 4332d2151e109208623922b4befe2dc2f4504a9b Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sun, 24 Aug 2025 09:27:14 +0200 Subject: [PATCH 027/149] vfs_dialog: ensure that one item is always selected --- rpcs3/rpcs3qt/vfs_dialog_path_widget.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rpcs3/rpcs3qt/vfs_dialog_path_widget.cpp b/rpcs3/rpcs3qt/vfs_dialog_path_widget.cpp index c0cfc7bb82..3685599977 100644 --- a/rpcs3/rpcs3qt/vfs_dialog_path_widget.cpp +++ b/rpcs3/rpcs3qt/vfs_dialog_path_widget.cpp @@ -77,9 +77,10 @@ vfs_dialog_path_widget::vfs_dialog_path_widget(const QString& name, const QStrin // Select new path m_selected_config_label->setText(path.isEmpty() ? EmptyPath : path); - update_selection(); break; } + + update_selection(); }); connect(m_dir_list, &QListWidget::itemDoubleClicked, this, [](QListWidgetItem* item) From 3a13da32019a10d7f2da703bc38467666e5b9efb Mon Sep 17 00:00:00 2001 From: Antonino Di Guardo <64427768+digant73@users.noreply.github.com> Date: Sun, 24 Aug 2025 18:12:35 +0200 Subject: [PATCH 028/149] asm.hpp: Make utils::rational_mul() consistent (#17452) --- rpcs3/util/asm.hpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/rpcs3/util/asm.hpp b/rpcs3/util/asm.hpp index 14eaa1409d..3ba99bf851 100644 --- a/rpcs3/util/asm.hpp +++ b/rpcs3/util/asm.hpp @@ -413,13 +413,6 @@ namespace utils return static_cast(value * u64{numerator} / u64{denominator}); } -#if is_u128_emulated - if constexpr (sizeof(T) <= sizeof(u128) / 2) - { - return static_cast(u128_from_mul(value, numerator) / u64{denominator}); - } -#endif - return static_cast(value / denominator * numerator + (value % denominator) * numerator / denominator); } From 6b238d847fd7576773d1d5653a90dbc1acadd313 Mon Sep 17 00:00:00 2001 From: schm1dtmac Date: Mon, 25 Aug 2025 16:29:21 +0100 Subject: [PATCH 029/149] Fix mvk formulae causing builds to fail --- .ci/build-mac-arm64.sh | 2 ++ .ci/build-mac.sh | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.ci/build-mac-arm64.sh b/.ci/build-mac-arm64.sh index 9a23fdee55..2e3531532e 100755 --- a/.ci/build-mac-arm64.sh +++ b/.ci/build-mac-arm64.sh @@ -12,8 +12,10 @@ export HOMEBREW_NO_INSTALL_CLEANUP=1 /opt/homebrew/bin/brew link -f --quiet "llvm@$LLVM_COMPILER_VER" ffmpeg@5 # moltenvk based on commit for 1.3.0 release +export HOMEBREW_DEVELOPER=1 # Prevents blocking of local formulae wget https://raw.githubusercontent.com/Homebrew/homebrew-core/7255441cbcafabaa8950f67c7ec55ff499dbb2d3/Formula/m/molten-vk.rb /opt/homebrew/bin/brew install -f --overwrite --formula --quiet ./molten-vk.rb +export HOMEBREW_DEVELOPER=0 export CXX=clang++ export CC=clang diff --git a/.ci/build-mac.sh b/.ci/build-mac.sh index bf520891ce..0c5e070dfc 100755 --- a/.ci/build-mac.sh +++ b/.ci/build-mac.sh @@ -16,8 +16,10 @@ arch -x86_64 /usr/local/bin/brew install --quiet "llvm@$LLVM_COMPILER_VER" glew arch -x86_64 /usr/local/bin/brew link -f --quiet "llvm@$LLVM_COMPILER_VER" ffmpeg@5 # moltenvk based on commit for 1.3.0 release +export HOMEBREW_DEVELOPER=1 # Prevents blocking of local formulae wget https://raw.githubusercontent.com/Homebrew/homebrew-core/7255441cbcafabaa8950f67c7ec55ff499dbb2d3/Formula/m/molten-vk.rb arch -x86_64 /usr/local/bin/brew install -f --overwrite --formula --quiet ./molten-vk.rb +export HOMEBREW_DEVELOPER=0 export CXX=clang++ export CC=clang From 92a30b4e41b59ddd538b1da0e875ff62b271b3b9 Mon Sep 17 00:00:00 2001 From: RunnerFaith <6189413+RunnerFaith@users.noreply.github.com> Date: Tue, 26 Aug 2025 23:17:27 +0100 Subject: [PATCH 030/149] Fix raw mouse handler keypress logic --- rpcs3/Input/raw_mouse_handler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpcs3/Input/raw_mouse_handler.cpp b/rpcs3/Input/raw_mouse_handler.cpp index 874eccdde6..ec1670d9dd 100644 --- a/rpcs3/Input/raw_mouse_handler.cpp +++ b/rpcs3/Input/raw_mouse_handler.cpp @@ -268,7 +268,7 @@ void raw_mouse::update_values(s32 scan_code, bool pressed) // Get mouse buttons for (const auto& [button, btn] : m_buttons) { - if (!btn.is_key || btn.scan_code != scan_code) return; + if (!btn.is_key || btn.scan_code != scan_code) continue; m_handler->Button(m_index, button, pressed); } From 4800aa9bfc01eff958345874186eaf4163f12f6a Mon Sep 17 00:00:00 2001 From: Megamouse Date: Fri, 29 Aug 2025 20:08:55 +0200 Subject: [PATCH 031/149] Update Qt to 6.9.2 --- .ci/build-mac-arm64.sh | 4 ++-- .ci/build-mac.sh | 4 ++-- .github/workflows/rpcs3.yml | 4 ++-- BUILDING.md | 10 +++++----- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.ci/build-mac-arm64.sh b/.ci/build-mac-arm64.sh index 2e3531532e..fd08bca0d7 100755 --- a/.ci/build-mac-arm64.sh +++ b/.ci/build-mac-arm64.sh @@ -34,7 +34,7 @@ if [ ! -d "/tmp/Qt/$QT_VER" ]; then git clone https://github.com/engnr/qt-downloader.git cd qt-downloader git checkout f52efee0f18668c6d6de2dec0234b8c4bc54c597 - # nested Qt 6.9.1 URL workaround + # nested Qt 6.9.2 URL workaround # sed -i '' "s/'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/'qt{0}_{0}{1}{2}'.format(major, minor, patch), 'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/g" qt-downloader # sed -i '' "s/'{}\/{}\/qt{}_{}\/'/'{0}\/{1}\/qt{2}_{3}\/qt{2}_{3}\/'/g" qt-downloader # archived Qt 6.7.3 URL workaround @@ -43,7 +43,7 @@ if [ ! -d "/tmp/Qt/$QT_VER" ]; then arch -arm64 "$BREW_PATH/bin/pipenv" run pip3 uninstall py7zr requests semantic_version lxml arch -arm64 "$BREW_PATH/bin/pipenv" run pip3 install py7zr requests semantic_version lxml --no-cache mkdir -p "$QT_VER/macos" ; ln -s "macos" "$QT_VER/clang_64" - # sed -i '' 's/args\.version \/ derive_toolchain_dir(args) \/ //g' "$WORKDIR/qt-downloader/qt-downloader" # Qt 6.9.1 workaround + # sed -i '' 's/args\.version \/ derive_toolchain_dir(args) \/ //g' "$WORKDIR/qt-downloader/qt-downloader" # Qt 6.9.2 workaround arch -arm64 "$BREW_PATH/bin/pipenv" run "$WORKDIR/qt-downloader/qt-downloader" macos desktop "$QT_VER" clang_64 --opensource --addons qtmultimedia qtimageformats # -o "$QT_VER/clang_64" fi diff --git a/.ci/build-mac.sh b/.ci/build-mac.sh index 0c5e070dfc..33c5dd87bc 100755 --- a/.ci/build-mac.sh +++ b/.ci/build-mac.sh @@ -38,7 +38,7 @@ if [ ! -d "/tmp/Qt/$QT_VER" ]; then git clone https://github.com/engnr/qt-downloader.git cd qt-downloader git checkout f52efee0f18668c6d6de2dec0234b8c4bc54c597 - # nested Qt 6.9.1 URL workaround + # nested Qt 6.9.2 URL workaround # sed -i '' "s/'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/'qt{0}_{0}{1}{2}'.format(major, minor, patch), 'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/g" qt-downloader # sed -i '' "s/'{}\/{}\/qt{}_{}\/'/'{0}\/{1}\/qt{2}_{3}\/qt{2}_{3}\/'/g" qt-downloader # archived Qt 6.7.3 URL workaround @@ -46,7 +46,7 @@ if [ ! -d "/tmp/Qt/$QT_VER" ]; then cd "/tmp/Qt" arch -x86_64 "$BREW_X64_PATH/bin/pipenv" --python "$BREW_X64_PATH/bin/python3" run pip3 install py7zr requests semantic_version lxml mkdir -p "$QT_VER/macos" ; ln -s "macos" "$QT_VER/clang_64" - # sed -i '' 's/args\.version \/ derive_toolchain_dir(args) \/ //g' "$WORKDIR/qt-downloader/qt-downloader" # Qt 6.9.1 workaround + # sed -i '' 's/args\.version \/ derive_toolchain_dir(args) \/ //g' "$WORKDIR/qt-downloader/qt-downloader" # Qt 6.9.2 workaround arch -x86_64 "$BREW_X64_PATH/bin/pipenv" --python "$BREW_X64_PATH/bin/python3" run "$WORKDIR/qt-downloader/qt-downloader" macos desktop "$QT_VER" clang_64 --opensource --addons qtmultimedia qtimageformats # -o "$QT_VER/clang_64" fi diff --git a/.github/workflows/rpcs3.yml b/.github/workflows/rpcs3.yml index 1981f78edb..e17cb5eb95 100644 --- a/.github/workflows/rpcs3.yml +++ b/.github/workflows/rpcs3.yml @@ -212,9 +212,9 @@ jobs: env: COMPILER: msvc QT_VER_MAIN: '6' - QT_VER: '6.9.1' + QT_VER: '6.9.2' QT_VER_MSVC: 'msvc2022' - QT_DATE: '202505291653' + QT_DATE: '202508181147' LLVM_VER: '19.1.7' VULKAN_VER: '1.3.268.0' VULKAN_SDK_SHA: '8459ef49bd06b697115ddd3d97c9aec729e849cd775f5be70897718a9b3b9db5' diff --git a/BUILDING.md b/BUILDING.md index 311bbddeee..68ebde706c 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -19,26 +19,26 @@ The following tools are required to build RPCS3 on Windows 10 or later: with standalone **CMake** tool. - [Python 3.6+](https://www.python.org/downloads/) (add to PATH) -- [Qt 6.9.1](https://www.qt.io/download-qt-installer) In case you can't download from the official installer, you can use [Another Qt installer](https://github.com/miurahr/aqtinstall) (In that case you will need to manually add the "qtmultimedia" module when installing Qt) +- [Qt 6.9.2](https://www.qt.io/download-qt-installer) In case you can't download from the official installer, you can use [Another Qt installer](https://github.com/miurahr/aqtinstall) (In that case you will need to manually add the "qtmultimedia" module when installing Qt) - [Vulkan SDK 1.3.268.0](https://vulkan.lunarg.com/sdk/home) (see "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/windows/getting_started.html)) for now future SDKs don't work. You need precisely 1.3.268.0. The `sln` solution available only on **Visual Studio** is the preferred building solution. It easily allows to build the **RPCS3** application in `Release` and `Debug` mode. In order to build **RPCS3** with the `sln` solution (with **Visual Studio**), **Qt** libs need to be detected. To detect the libs: -- add and set the `QTDIR` environment variable, e.g. `\6.9.1\msvc2022_64\` +- add and set the `QTDIR` environment variable, e.g. `\6.9.2\msvc2022_64\` - or use the [Visual Studio Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools2022) **NOTE:** If you have issues with the **Visual Studio Qt Plugin**, you may want to uninstall it and install the [Legacy Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.LEGACYQtVisualStudioTools2022) instead. In order to build **RPCS3** with the `CMake` solution (with both **Visual Studio** and standalone **CMake** tool): -- add and set the `Qt6_ROOT` environment variable to the **Qt** libs path, e.g. `\6.9.1\msvc2022_64\` +- add and set the `Qt6_ROOT` environment variable to the **Qt** libs path, e.g. `\6.9.2\msvc2022_64\` ### Linux These are the essentials tools to build RPCS3 on Linux. Some of them can be installed through your favorite package manager: - Clang 17+ or GCC 13+ - [CMake 3.28.0+](https://www.cmake.org/download/) -- [Qt 6.9.1](https://www.qt.io/download-qt-installer) +- [Qt 6.9.2](https://www.qt.io/download-qt-installer) - [Vulkan SDK 1.3.268.0](https://vulkan.lunarg.com/sdk/home) (See "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/linux/getting_started.html)) for now future SDKs don't work. You need precisely 1.3.268.0. - [SDL3](https://github.com/libsdl-org/SDL/releases) (for the FAudio backend) @@ -121,7 +121,7 @@ Start **Visual Studio**, click on `Open a project or solution` and select the `r ##### Configuring the Qt Plugin (if used) 1) go to `Extensions->Qt VS Tools->Qt Versions` -2) add the path to your Qt installation with compiler e.g. `\6.9.1\msvc2022_64`, version will fill in automatically +2) add the path to your Qt installation with compiler e.g. `\6.9.2\msvc2022_64`, version will fill in automatically 3) go to `Extensions->Qt VS Tools->Options->Legacy Project Format`. (Only available in the **Legacy Qt Plugin**) 4) set `Build: Run pre-build setup` to `true`. (Only available in the **Legacy Qt Plugin**) From 6d1a85b9477011bf81849e08c141fab7eeeb3e10 Mon Sep 17 00:00:00 2001 From: Antonino Di Guardo <64427768+digant73@users.noreply.github.com> Date: Mon, 1 Sep 2025 20:44:00 +0200 Subject: [PATCH 032/149] Minor improvements and cleanup on VFS panel (#17459) --- rpcs3/rpcs3qt/vfs_dialog_path_widget.cpp | 35 ++++++++++++------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/rpcs3/rpcs3qt/vfs_dialog_path_widget.cpp b/rpcs3/rpcs3qt/vfs_dialog_path_widget.cpp index 3685599977..f7d2f81dd9 100644 --- a/rpcs3/rpcs3qt/vfs_dialog_path_widget.cpp +++ b/rpcs3/rpcs3qt/vfs_dialog_path_widget.cpp @@ -12,21 +12,14 @@ vfs_dialog_path_widget::vfs_dialog_path_widget(const QString& name, const QStrin const QStringList all_dirs = m_gui_settings->GetValue(m_list_location).toStringList(); - QListWidgetItem* selected_item = nullptr; - for (const QString& dir : all_dirs) { - QListWidgetItem* item = add_directory(dir); - - if (dir == current_path) - selected_item = item; + add_directory(dir); } - // We must show the currently selected config. - if (!selected_item) - selected_item = add_directory(current_path); - - selected_item->setSelected(true); + // Add current path if missing; That should never happen if the list is managed only by the use of the GUI. + // Code made robust even in case the path was manually removed from the list stored in "CurrentSettings.ini" file + add_directory(current_path); m_dir_list->setMinimumWidth(m_dir_list->sizeHintForColumn(0)); @@ -55,8 +48,6 @@ vfs_dialog_path_widget::vfs_dialog_path_widget(const QString& name, const QStrin setLayout(vbox); - update_selection(); - connect(m_dir_list->model(), &QAbstractItemModel::dataChanged, this, [this](const QModelIndex&, const QModelIndex&, const QList& roles) { if (m_is_changing_data || (!roles.empty() && !roles.contains(Qt::ItemDataRole::CheckStateRole))) @@ -91,8 +82,10 @@ vfs_dialog_path_widget::vfs_dialog_path_widget(const QString& name, const QStrin connect(m_dir_list, &QListWidget::currentRowChanged, this, [this, button_remove_dir](int row) { - button_remove_dir->setEnabled(m_dir_list->item(row) && row > 0); + button_remove_dir->setEnabled(row > 0); }); + + update_selection(); // As last (just to make also use of the defined connections), update the widget } void vfs_dialog_path_widget::reset() @@ -129,7 +122,11 @@ void vfs_dialog_path_widget::add_new_directory() if (!dir.endsWith("/")) dir += '/'; - m_dir_list->setCurrentItem(add_directory(dir)); + if (QListWidgetItem* item = add_directory(dir)) + m_dir_list->setCurrentItem(item); // Set both the new current item and new current row + + // Not really necessary an update here due to the added item is not also set as the new checked row. + // Keeping just in case of further changes in the current logic update_selection(); } @@ -140,6 +137,7 @@ void vfs_dialog_path_widget::remove_directory() QListWidgetItem* item = m_dir_list->takeItem(row); delete item; + m_dir_list->setCurrentRow(std::min(row, m_dir_list->count() - 1)); // Set current row, if needed update_selection(); } } @@ -163,18 +161,19 @@ void vfs_dialog_path_widget::update_selection() if (is_selected) { - m_dir_list->setCurrentItem(item); + if (m_dir_list->currentRow() < 0) // If no current row is set (e.g. at startup) + m_dir_list->setCurrentRow(i); // Set both the new current item and new current row + found_path = true; } } } - if (!found_path) + if (!found_path) // If no path is selected (no row is checked) { QListWidgetItem* item = m_dir_list->item(0); m_selected_config_label->setText(item->text().isEmpty() ? EmptyPath : item->text()); item->setCheckState(Qt::CheckState::Checked); - m_dir_list->setCurrentItem(item); } m_is_changing_data = false; From c7de9053b4a5e0f02f4a5b9e13bb5643c70b8c53 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Mon, 1 Sep 2025 20:44:36 +0200 Subject: [PATCH 033/149] Qt: fix saved gs_frame visibility --- rpcs3/rpcs3qt/gs_frame.cpp | 14 +++++++++----- rpcs3/rpcs3qt/gs_frame.h | 5 +++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/rpcs3/rpcs3qt/gs_frame.cpp b/rpcs3/rpcs3qt/gs_frame.cpp index ee50a4fb05..2c9e573331 100644 --- a/rpcs3/rpcs3qt/gs_frame.cpp +++ b/rpcs3/rpcs3qt/gs_frame.cpp @@ -122,8 +122,12 @@ gs_frame::gs_frame(QScreen* screen, const QRect& geometry, const QIcon& appIcon, load_gui_settings(); // Change cursor when in fullscreen. - connect(this, &QWindow::visibilityChanged, this, [this](QWindow::Visibility visibility) + connect(this, &QWindow::visibilityChanged, this, [this](Visibility visibility) { + if (visibility != Visibility::Minimized && visibility != Visibility::Hidden) + { + m_visibility = visibility; // Only save "visible" visibility + } handle_cursor(visibility, true, false, true); }); @@ -567,7 +571,7 @@ void gs_frame::update_cursor() // Hide the mouse if the idle timeout was reached (which means that the timer isn't running) show_mouse = false; } - else if (visibility() == QWindow::Visibility::FullScreen) + else if (visibility() == Visibility::FullScreen) { // Fullscreen: Show or hide the mouse depending on the settings. show_mouse = m_show_mouse_in_fullscreen; @@ -596,7 +600,7 @@ void gs_frame::hide_on_close() { // Make sure not to save the hidden state, which is useless to us. const Visibility current_visibility = visibility(); - m_gui_settings->SetValue(gui::gs_visibility, current_visibility == Visibility::Hidden ? Visibility::AutomaticVisibility : current_visibility, false); + m_gui_settings->SetValue(gui::gs_visibility, current_visibility == Visibility::Hidden ? m_visibility : current_visibility, false); m_gui_settings->SetValue(gui::gs_geometry, geometry(), true); if (!g_progr_text) @@ -1096,7 +1100,7 @@ void gs_frame::mouseDoubleClickEvent(QMouseEvent* ev) } } -void gs_frame::handle_cursor(QWindow::Visibility visibility, bool visibility_changed, bool active_changed, bool start_idle_timer) +void gs_frame::handle_cursor(Visibility visibility, bool visibility_changed, bool active_changed, bool start_idle_timer) { // Let's reload the gui settings when the visibility or the active window changes. if (visibility_changed || active_changed) @@ -1107,7 +1111,7 @@ void gs_frame::handle_cursor(QWindow::Visibility visibility, bool visibility_cha if (visibility_changed) { // In fullscreen we default to hiding and locking. In windowed mode we do not want the lock by default. - m_mouse_hide_and_lock = (visibility == QWindow::Visibility::FullScreen) && m_lock_mouse_in_fullscreen; + m_mouse_hide_and_lock = (visibility == Visibility::FullScreen) && m_lock_mouse_in_fullscreen; } } diff --git a/rpcs3/rpcs3qt/gs_frame.h b/rpcs3/rpcs3qt/gs_frame.h index 38bbaa0a3e..efcc131396 100644 --- a/rpcs3/rpcs3qt/gs_frame.h +++ b/rpcs3/rpcs3qt/gs_frame.h @@ -34,7 +34,8 @@ private: u64 m_frames = 0; std::string m_window_title; - QWindow::Visibility m_last_visibility = Visibility::Windowed; + Visibility m_last_visibility = Visibility::Windowed; + Visibility m_visibility = Visibility::Windowed; atomic_t m_is_closing = false; atomic_t m_show_mouse = true; bool m_disable_mouse = false; @@ -111,7 +112,7 @@ private: void toggle_recording(); void toggle_mouselock(); void update_cursor(); - void handle_cursor(QWindow::Visibility visibility, bool visibility_changed, bool active_changed, bool start_idle_timer); + void handle_cursor(Visibility visibility, bool visibility_changed, bool active_changed, bool start_idle_timer); private Q_SLOTS: void mouse_hide_timeout(); From 79fa06f9d6e6ed3b7858781c7de998cd011a8613 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Tue, 2 Sep 2025 20:51:31 +0200 Subject: [PATCH 034/149] Update FAudio to 25.09 --- 3rdparty/FAudio | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/FAudio b/3rdparty/FAudio index 8da412ed6c..8a87fdc924 160000 --- a/3rdparty/FAudio +++ b/3rdparty/FAudio @@ -1 +1 @@ -Subproject commit 8da412ed6c78c6e225b873bb12cbc903d5389192 +Subproject commit 8a87fdc9242a7b507cf5c96c539334a1760c1ec7 From 34a9d03d3d3d76b7dc87e806b16297ff30090e70 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Tue, 2 Sep 2025 20:55:18 +0200 Subject: [PATCH 035/149] Update SDL to 3.2.22 --- 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 96292a5b46..a96677bdf6 160000 --- a/3rdparty/libsdl-org/SDL +++ b/3rdparty/libsdl-org/SDL @@ -1 +1 @@ -Subproject commit 96292a5b464258a2b926e0a3d72f8b98c2a81aa6 +Subproject commit a96677bdf6b4acb84af4ec294e5f60a4e8cbbe03 From cfe1eca18558f829857068a3f481436d9a96f076 Mon Sep 17 00:00:00 2001 From: Elad <18193363+knight4u32@users.noreply.github.com> Date: Wed, 27 Aug 2025 02:52:27 +0300 Subject: [PATCH 036/149] SPU: Reimplement reservation notifications --- rpcs3/Emu/CPU/CPUThread.cpp | 2 +- rpcs3/Emu/Cell/PPUThread.cpp | 111 +++++++++++++------------ rpcs3/Emu/Cell/SPUThread.cpp | 125 ++++++++++++++++++---------- rpcs3/Emu/Cell/lv2/lv2.cpp | 75 ++++++++--------- rpcs3/Emu/Cell/lv2/sys_mutex.cpp | 4 + rpcs3/Emu/Cell/lv2/sys_spu.cpp | 8 +- rpcs3/Emu/Cell/lv2/sys_sync.h | 7 ++ rpcs3/Emu/Memory/vm.cpp | 41 ++++++++-- rpcs3/Emu/Memory/vm_reservation.h | 132 +++++++++++------------------- 9 files changed, 269 insertions(+), 236 deletions(-) diff --git a/rpcs3/Emu/CPU/CPUThread.cpp b/rpcs3/Emu/CPU/CPUThread.cpp index 242dcb9d9f..9710af94f5 100644 --- a/rpcs3/Emu/CPU/CPUThread.cpp +++ b/rpcs3/Emu/CPU/CPUThread.cpp @@ -1158,7 +1158,7 @@ cpu_thread& cpu_thread::operator=(thread_state) { if (u32 resv = atomic_storage::load(thread->raddr)) { - vm::reservation_notifier_notify(resv); + vm::reservation_notifier_notify(resv, thread->rtime); } } } diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 784cf27829..3aa3cca8ed 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -2327,9 +2327,9 @@ void ppu_thread::cpu_wait(bs_t old) { res_notify = 0; - if (res_notify_time == vm::reservation_notifier_count_index(addr).second) + if (res_notify_time + 128 == (vm::reservation_acquire(addr) & -128)) { - vm::reservation_notifier_notify(addr); + vm::reservation_notifier_notify(addr, res_notify_time); } } @@ -3117,7 +3117,6 @@ static T ppu_load_acquire_reservation(ppu_thread& ppu, u32 addr) { // Reload "cached" reservation of previous succeeded conditional store // This seems like a hardware feature according to cellSpursAddUrgentCommand function - ppu.rtime -= 128; } else { @@ -3614,67 +3613,68 @@ static bool ppu_store_reservation(ppu_thread& ppu, u32 addr, u64 reg_value) { extern atomic_t liblv2_begin, liblv2_end; - // Avoid notifications from lwmutex or sys_spinlock - if (new_data != old_data && (ppu.cia < liblv2_begin || ppu.cia >= liblv2_end)) - { - u32 notify = ppu.res_notify; + const u32 notify = ppu.res_notify; - if (notify) + if (notify) + { + if (auto waiter = vm::reservation_notifier_notify(notify, ppu.res_notify_time, true)) { - if (ppu.res_notify_time == vm::reservation_notifier_count_index(notify).second) + ppu.state += cpu_flag::wait; + waiter->notify_all(); + } + + ppu.res_notify = 0; + } + + // Avoid notifications from lwmutex or sys_spinlock + const bool is_liblv2_or_null = (ppu.cia >= liblv2_begin && ppu.cia < liblv2_end); + + if (!is_liblv2_or_null && (addr ^ notify) & -128) + { + // Try to postpone notification to when PPU is asleep or join notifications on the same address + // This also optimizes a mutex - won't notify after lock is aqcuired (prolonging the critical section duration), only notifies on unlock + const u32 count = vm::reservation_notifier_count(addr & -128, rtime); + + if (cpu_flag::wait - ppu.state) { ppu.state += cpu_flag::wait; - vm::reservation_notifier_notify(notify); - } - else - { - notify = 0; } - ppu.res_notify = 0; - } - - if ((addr ^ notify) & -128) + vm::reservation_notifier_notify(addr & -128, rtime); + switch (count) { - // Try to postpone notification to when PPU is asleep or join notifications on the same address - // This also optimizes a mutex - won't notify after lock is aqcuired (prolonging the critical section duration), only notifies on unlock - const auto [count, index] = vm::reservation_notifier_count_index(addr); - - switch (count) - { - case 0: - { - // Nothing to do - break; - } - case 1: - { - if (!notify) - { - ppu.res_notify = addr; - ppu.res_notify_time = index; - break; - } - - // Notify both - [[fallthrough]]; - } - default: - { - if (!notify) - { - ppu.state += cpu_flag::wait; - } - - vm::reservation_notifier_notify(addr); - break; - } - } + case 0: + { + // Nothing to do + break; } + case 1: + { + if (!notify) + { + ppu.res_notify = addr & -128; + ppu.res_notify_time = rtime; + break; + } - static_cast(ppu.test_stopped()); + // Notify both + [[fallthrough]]; + } + default: + { + if (cpu_flag::wait - ppu.state) + { + ppu.state += cpu_flag::wait; + } + + vm::reservation_notifier_notify(addr & -128, rtime); + break; + } + } } + static_cast(ppu.test_stopped()); + if (addr == ppu.last_faddr) { ppu.last_succ++; @@ -3682,7 +3682,6 @@ static bool ppu_store_reservation(ppu_thread& ppu, u32 addr, u64 reg_value) ppu.last_faddr = 0; ppu.res_cached = ppu.raddr; - ppu.rtime += 128; ppu.raddr = 0; return true; } @@ -3693,10 +3692,10 @@ static bool ppu_store_reservation(ppu_thread& ppu, u32 addr, u64 reg_value) // And on failure it has some time to do something else if (notify && ((addr ^ notify) & -128)) { - if (ppu.res_notify_time == vm::reservation_notifier_count_index(notify).second) + if (auto waiter = vm::reservation_notifier_notify(notify, ppu.res_notify_time, true)) { ppu.state += cpu_flag::wait; - vm::reservation_notifier_notify(notify); + waiter->notify_all(); static_cast(ppu.test_stopped()); } diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index c083ce97d0..3a59902fd2 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -461,7 +461,7 @@ waitpkg_func static void __tpause(u32 cycles, u32 cstate) namespace vm { - std::array, 1024> g_resrv_waiters_count{}; + std::array, 2048> g_resrv_waiters_count{}; } void do_cell_atomic_128_store(u32 addr, const void* to_write); @@ -3979,7 +3979,7 @@ bool spu_thread::do_putllc(const spu_mfc_cmd& args) { if (raddr != spurs_addr || pc != 0x11e4) { - vm::reservation_notifier_notify(addr); + vm::reservation_notifier_notify(addr, rtime); } else { @@ -3990,7 +3990,7 @@ bool spu_thread::do_putllc(const spu_mfc_cmd& args) if (switched_from_running_to_idle) { - vm::reservation_notifier_notify(addr); + vm::reservation_notifier_notify(addr, rtime); } } @@ -4199,7 +4199,10 @@ void spu_thread::do_putlluc(const spu_mfc_cmd& args) } do_cell_atomic_128_store(addr, _ptr(args.lsa & 0x3ff80)); - vm::reservation_notifier_notify(addr); + + // TODO: Implement properly (notifying previous two times) + vm::reservation_notifier_notify(addr, vm::reservation_acquire(addr) - 128); + vm::reservation_notifier_notify(addr, vm::reservation_acquire(addr) - 256); } bool spu_thread::do_mfc(bool can_escape, bool must_finish) @@ -4914,10 +4917,10 @@ bool spu_thread::process_mfc_cmd() usz cache_line_waiter_index = umax; - if (auto wait_var = vm::reservation_notifier_begin_wait(addr, rtime)) + if (auto [wait_var, flag_val] = vm::reservation_notifier_begin_wait(addr, rtime); wait_var) { cache_line_waiter_index = register_cache_line_waiter(addr); - utils::bless>(&wait_var->raw().wait_flag)->wait(1, atomic_wait_timeout{100'000}); + utils::bless>(&wait_var->raw().wait_flag)->wait(flag_val, atomic_wait_timeout{100'000}); vm::reservation_notifier_end_wait(*wait_var); } @@ -4964,9 +4967,9 @@ bool spu_thread::process_mfc_cmd() g_unchanged++; // Notify threads manually, memory data has likely changed and broke the reservation for others - if (vm::reservation_notifier_count(addr) && res == new_time) + if (vm::reservation_notifier_count(addr, new_time) && res == new_time) { - vm::reservation_notifier_notify(addr); + vm::reservation_notifier_notify(addr, new_time); } } else @@ -4984,9 +4987,9 @@ bool spu_thread::process_mfc_cmd() if (this_time == rtime) { // Notify threads manually, memory data has likely changed and broke the reservation for others - if (vm::reservation_notifier_count(addr) && res == this_time) + if (vm::reservation_notifier_count(addr, this_time) && res == this_time) { - vm::reservation_notifier_notify(addr); + vm::reservation_notifier_notify(addr, this_time); } } @@ -5645,28 +5648,46 @@ usz spu_thread::register_cache_line_waiter(u32 addr) { const u64 value = u64{compute_rdata_hash32(rdata)} << 32 | addr; - for (usz i = 0; i < std::size(g_spu_waiters_by_value); i++) + for (usz attempts = 0; attempts < 2; attempts++) { - auto [old, ok] = g_spu_waiters_by_value[i].fetch_op([value](u64& x) + // First, scan for a matching address waiter + // Remembering a potentially empty spot + usz empty_it = umax; + + for (usz i = 0; i < std::size(g_spu_waiters_by_value); i++) { - if (x == 0) + auto [old, ok] = g_spu_waiters_by_value[i].fetch_op([&](u64& x) { - x = value + 1; - return true; - } + if (x == 0) + { + empty_it = i; + return false; + } - if ((x & -128) == value) + if ((x & -128) == value) + { + x++; + return true; + } + + return false; + }); + + if (ok) { - x++; - return true; + return i; } + } - return false; - }); - - if (ok) + if (empty_it == umax) { - return i; + continue; + } + + // If we did not find existing an waiter, try to occupy an empty spot + if (g_spu_waiters_by_value[empty_it].compare_and_swap_test(0, value + 1)) + { + return empty_it; } } @@ -5681,7 +5702,7 @@ void spu_thread::deregister_cache_line_waiter(usz index) return; } - g_spu_waiters_by_value[index].fetch_op([](u64& x) + g_spu_waiters_by_value[index].atomic_op([](u64& x) { x--; @@ -5689,8 +5710,6 @@ void spu_thread::deregister_cache_line_waiter(usz index) { x = 0; } - - return false; }); } @@ -6151,9 +6170,9 @@ s64 spu_thread::get_ch_value(u32 ch) else if (!cmp_rdata(rdata, *resrv_mem)) { // Notify threads manually, memory data has likely changed and broke the reservation for others - if (vm::reservation_notifier_count(raddr) && vm::reservation_acquire(raddr) == rtime) + if (vm::reservation_notifier_count(raddr, rtime) && vm::reservation_acquire(raddr) == rtime) { - vm::reservation_notifier_notify(raddr); + vm::reservation_notifier_notify(raddr, rtime); } set_lr = true; @@ -6268,11 +6287,16 @@ s64 spu_thread::get_ch_value(u32 ch) { // Wait with extended timeout, in this situation we have notifications for nearly all writes making it possible // Abort notifications are handled specially for performance reasons - if (auto wait_var = vm::reservation_notifier_begin_wait(raddr, rtime)) + if (auto [wait_var, flag_val] = vm::reservation_notifier_begin_wait(raddr, rtime); wait_var) { - if (check_cache_line_waiter()) + if (!cmp_rdata(rdata, *resrv_mem)) { - utils::bless>(&wait_var->raw().wait_flag)->wait(1, atomic_wait_timeout{300'000}); + raddr = 0; + set_events(SPU_EVENT_LR); + } + else if (check_cache_line_waiter()) + { + utils::bless>(&wait_var->raw().wait_flag)->wait(flag_val, atomic_wait_timeout{200'000}); } vm::reservation_notifier_end_wait(*wait_var); @@ -6283,12 +6307,16 @@ s64 spu_thread::get_ch_value(u32 ch) const u32 _raddr = this->raddr; #ifdef __linux__ - - if (auto wait_var = vm::reservation_notifier_begin_wait(_raddr, rtime)) + if (auto [wait_var, flag_val] = vm::reservation_notifier_begin_wait(_raddr, rtime); wait_var) { - if (check_cache_line_waiter()) + if (!cmp_rdata(rdata, *resrv_mem)) { - utils::bless>(&wait_var->raw().wait_flag)->wait(1, atomic_wait_timeout{50'000}); + raddr = 0; + set_events(SPU_EVENT_LR); + } + else if (check_cache_line_waiter()) + { + utils::bless>(&wait_var->raw().wait_flag)->wait(flag_val, atomic_wait_timeout{50'000}); } vm::reservation_notifier_end_wait(*wait_var); @@ -6330,7 +6358,7 @@ s64 spu_thread::get_ch_value(u32 ch) else if (!cmp_rdata(_this->rdata, *_this->resrv_mem)) { // Notify threads manually, memory data has likely changed and broke the reservation for others - if (vm::reservation_notifier_count(raddr) >= 2 && vm::reservation_acquire(raddr) == _this->rtime) + if (vm::reservation_notifier_count(raddr, _this->rtime) >= 2 && vm::reservation_acquire(raddr) == _this->rtime) { s_tls_try_notify = true; } @@ -6351,20 +6379,25 @@ s64 spu_thread::get_ch_value(u32 ch) return true; }; - if (auto wait_var = vm::reservation_notifier_begin_wait(_raddr, rtime)) + if (auto [wait_var, flag_val] = vm::reservation_notifier_begin_wait(_raddr, rtime); wait_var) { - if (check_cache_line_waiter()) + if (!cmp_rdata(rdata, *resrv_mem)) + { + raddr = 0; + set_events(SPU_EVENT_LR); + } + else if (check_cache_line_waiter()) { atomic_wait_engine::set_one_time_use_wait_callback(wait_cb); - utils::bless>(&wait_var->raw().wait_flag)->wait(1, atomic_wait_timeout{80'000}); + utils::bless>(&wait_var->raw().wait_flag)->wait(flag_val, atomic_wait_timeout{100'000}); } vm::reservation_notifier_end_wait(*wait_var); } - if (s_tls_try_notify && vm::reservation_notifier_count(_raddr) && vm::reservation_acquire(_raddr) == rtime) + if (s_tls_try_notify && vm::reservation_notifier_count(_raddr, rtime) && vm::reservation_acquire(_raddr) == rtime) { - vm::reservation_notifier_notify(_raddr); + vm::reservation_notifier_notify(_raddr, rtime); } #endif } @@ -7273,6 +7306,7 @@ bool spu_thread::stop_and_signal(u32 code) } u32 prev_resv = 0; + u64 prev_rtime = 0; for (auto& thread : group->threads) { @@ -7285,13 +7319,14 @@ bool spu_thread::stop_and_signal(u32 code) if (u32 resv = atomic_storage::load(thread->raddr)) { - if (prev_resv && prev_resv != resv) + if (prev_resv && (prev_resv != resv || thread->rtime != prev_rtime)) { // Batch reservation notifications if possible - vm::reservation_notifier_notify(prev_resv); + vm::reservation_notifier_notify(prev_resv, thread->rtime); } prev_resv = resv; + prev_rtime = thread->rtime; } } } @@ -7299,7 +7334,7 @@ bool spu_thread::stop_and_signal(u32 code) if (prev_resv) { - vm::reservation_notifier_notify(prev_resv); + vm::reservation_notifier_notify(prev_resv, prev_rtime); } check_state(); diff --git a/rpcs3/Emu/Cell/lv2/lv2.cpp b/rpcs3/Emu/Cell/lv2/lv2.cpp index 8d4db2bc91..eccd8bba6c 100644 --- a/rpcs3/Emu/Cell/lv2/lv2.cpp +++ b/rpcs3/Emu/Cell/lv2/lv2.cpp @@ -1340,23 +1340,20 @@ bool lv2_obj::sleep(cpu_thread& cpu, const u64 timeout) { static_cast(cpu).res_notify = 0; - if (static_cast(cpu).res_notify_time != vm::reservation_notifier_count_index(addr).second) + if (auto it = std::find(g_to_notify, std::end(g_to_notify), std::add_pointer_t{}); it != std::end(g_to_notify)) { - // Ignore outdated notification request - } - else if (auto it = std::find(g_to_notify, std::end(g_to_notify), std::add_pointer_t{}); it != std::end(g_to_notify)) - { - *it++ = vm::reservation_notifier_notify(addr, true); - - if (it < std::end(g_to_notify)) + if ((*it++ = vm::reservation_notifier_notify(addr, static_cast(cpu).res_notify_time, true))) { - // Null-terminate the list if it ends before last slot - *it = nullptr; + if (it < std::end(g_to_notify)) + { + // Null-terminate the list if it ends before last slot + *it = nullptr; + } } } else { - vm::reservation_notifier_notify(addr); + vm::reservation_notifier_notify(addr, static_cast(cpu).res_notify_time); } } } @@ -1393,23 +1390,20 @@ bool lv2_obj::awake(cpu_thread* thread, s32 prio) { ppu->res_notify = 0; - if (ppu->res_notify_time != vm::reservation_notifier_count_index(addr).second) + if (auto it = std::find(g_to_notify, std::end(g_to_notify), std::add_pointer_t{}); it != std::end(g_to_notify)) { - // Ignore outdated notification request - } - else if (auto it = std::find(g_to_notify, std::end(g_to_notify), std::add_pointer_t{}); it != std::end(g_to_notify)) - { - *it++ = vm::reservation_notifier_notify(addr, true); - - if (it < std::end(g_to_notify)) + if ((*it++ = vm::reservation_notifier_notify(addr, ppu->res_notify_time, true))) { - // Null-terminate the list if it ends before last slot - *it = nullptr; + if (it < std::end(g_to_notify)) + { + // Null-terminate the list if it ends before last slot + *it = nullptr; + } } } else { - vm::reservation_notifier_notify(addr); + vm::reservation_notifier_notify(addr, ppu->res_notify_time); } } } @@ -2232,18 +2226,8 @@ void lv2_obj::notify_all() noexcept if (cpu != &g_to_notify) { - const auto res_start = vm::reservation_notifier(0).second; - const auto res_end = vm::reservation_notifier(umax).second; - - if (cpu >= res_start && cpu <= res_end) - { - atomic_wait_engine::notify_all(cpu); - } - else - { - // Note: by the time of notification the thread could have been deallocated which is why the direct function is used - atomic_wait_engine::notify_one(cpu); - } + // Note: by the time of notification the thread could have been deallocated which is why the direct function is used + atomic_wait_engine::notify_all(cpu); } } @@ -2267,19 +2251,28 @@ void lv2_obj::notify_all() noexcept constexpr usz total_waiters = std::size(spu_thread::g_spu_waiters_by_value); u32 notifies[total_waiters]{}; + u64 notifies_time[total_waiters]{}; // There may be 6 waiters, but checking them all may be performance expensive // Instead, check 2 at max, but use the CPU ID index to tell which index to start checking so the work would be distributed across all threads atomic_t* range_lock = nullptr; - for (usz i = 0, checked = 0; checked < 3 && i < total_waiters; i++) + u32 current_raddr = 0; + + if (cpu->get_class() == thread_class::spu) + { + range_lock = static_cast(cpu)->range_lock; + current_raddr = static_cast(cpu)->raddr; + } + + for (usz i = 0, checked = 0; checked < 4 && i < total_waiters; i++) { auto& waiter = spu_thread::g_spu_waiters_by_value[(i + cpu->id) % total_waiters]; const u64 value = waiter.load(); u32 raddr = static_cast(value) & -128; - if (vm::check_addr(raddr)) + if (vm::check_addr(raddr) && (raddr != current_raddr || (value % 128) > 1)) { if (((raddr >> 28) < 2 || (raddr >> 28) == 0xd)) { @@ -2300,6 +2293,7 @@ void lv2_obj::notify_all() noexcept }).second) { notifies[i] = raddr; + notifies_time[i] = vm::reservation_acquire(raddr); } } @@ -2328,21 +2322,22 @@ void lv2_obj::notify_all() noexcept }).second) { notifies[i] = raddr; + notifies_time[i] = vm::reservation_acquire(raddr); } } } } - if (range_lock) + if (range_lock && cpu->get_class() != thread_class::spu) { vm::free_range_lock(range_lock); } - for (u32 addr : notifies) + for (u32 i = 0; i < total_waiters; i++) { - if (addr) + if (notifies[i]) { - vm::reservation_notifier_notify(addr); + vm::reservation_notifier_notify(notifies[i], notifies_time[i]); } } } diff --git a/rpcs3/Emu/Cell/lv2/sys_mutex.cpp b/rpcs3/Emu/Cell/lv2/sys_mutex.cpp index 92bf877a8a..63c0c16af9 100644 --- a/rpcs3/Emu/Cell/lv2/sys_mutex.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_mutex.cpp @@ -4,6 +4,7 @@ #include "Emu/Cell/ErrorCodes.h" #include "Emu/Cell/PPUThread.h" +#include "Emu/Memory/vm_reservation.h" #include "util/asm.hpp" @@ -345,6 +346,9 @@ error_code sys_mutex_unlock(ppu_thread& ppu, u32 mutex_id) const auto mutex = idm::check(mutex_id, [&, notify = lv2_obj::notify_all_t()](lv2_mutex& mutex) -> CellError { + // At unlock, we have some time to do other jobs when the thread is unlikely to be in other critical sections + notify.enqueue_on_top(vm::reservation_notifier_notify(ppu.res_notify, ppu.res_notify_time)); + auto result = mutex.try_unlock(ppu); if (result == CELL_EBUSY) diff --git a/rpcs3/Emu/Cell/lv2/sys_spu.cpp b/rpcs3/Emu/Cell/lv2/sys_spu.cpp index 77fbcb8505..3c2489c9b2 100644 --- a/rpcs3/Emu/Cell/lv2/sys_spu.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_spu.cpp @@ -1501,6 +1501,7 @@ error_code sys_spu_thread_group_terminate(ppu_thread& ppu, u32 id, s32 value) } u32 prev_resv = 0; + u64 prev_time = 0; for (auto& thread : group->threads) { @@ -1510,20 +1511,21 @@ error_code sys_spu_thread_group_terminate(ppu_thread& ppu, u32 id, s32 value) if (u32 resv = atomic_storage::load(thread->raddr)) { - if (prev_resv && prev_resv != resv) + if (prev_resv && (prev_resv != resv || prev_time != thread->rtime)) { // Batch reservation notifications if possible - vm::reservation_notifier_notify(prev_resv); + vm::reservation_notifier_notify(prev_resv, prev_time); } prev_resv = resv; + prev_time = thread->rtime; } } } if (prev_resv) { - vm::reservation_notifier_notify(prev_resv); + vm::reservation_notifier_notify(prev_resv, prev_time); } group->exit_status = value; diff --git a/rpcs3/Emu/Cell/lv2/sys_sync.h b/rpcs3/Emu/Cell/lv2/sys_sync.h index bf99bcb056..7dea0fed63 100644 --- a/rpcs3/Emu/Cell/lv2/sys_sync.h +++ b/rpcs3/Emu/Cell/lv2/sys_sync.h @@ -483,6 +483,13 @@ public: } } + static void enqueue_on_top(const void* waiter) + { + return; + g_to_notify[0] = waiter; + g_to_notify[1] = nullptr; + } + ~notify_all_t() noexcept { lv2_obj::notify_all(); diff --git a/rpcs3/Emu/Memory/vm.cpp b/rpcs3/Emu/Memory/vm.cpp index d7109bf78a..669661ee9a 100644 --- a/rpcs3/Emu/Memory/vm.cpp +++ b/rpcs3/Emu/Memory/vm.cpp @@ -112,12 +112,9 @@ namespace vm { const auto [ok, rtime] = try_reservation_update(addr); - if (ok || (old & -128) < (rtime & -128)) + if (ok) { - if (ok) - { - reservation_notifier_notify(addr); - } + reservation_notifier_notify(addr, rtime); if (cpu && !had_wait && cpu->test_stopped()) { @@ -126,7 +123,7 @@ namespace vm return; } - + old = rtime; } } @@ -599,6 +596,38 @@ namespace vm } } + atomic_t* reservation_notifier_notify(u32 raddr, u64 rtime, bool postpone) + { + const auto waiter = reservation_notifier(raddr, rtime); + + if (waiter->load().wait_flag % 2 == 1) + { + if (!waiter->fetch_op([](reservation_waiter_t& value) + { + if (value.wait_flag % 2 == 1) + { + // Notify and make it even + value.wait_flag++; + return true; + } + + return false; + }).second) + { + return nullptr; + } + + if (postpone) + { + return utils::bless>(&waiter->raw().wait_flag); + } + + utils::bless>(&waiter->raw().wait_flag)->notify_all(); + } + + return nullptr; + } + u64 reservation_lock_internal(u32 addr, atomic_t& res) { for (u64 i = 0;; i++) diff --git a/rpcs3/Emu/Memory/vm_reservation.h b/rpcs3/Emu/Memory/vm_reservation.h index 15e6c51320..fe190aab65 100644 --- a/rpcs3/Emu/Memory/vm_reservation.h +++ b/rpcs3/Emu/Memory/vm_reservation.h @@ -40,111 +40,71 @@ namespace vm struct reservation_waiter_t { u32 wait_flag = 0; - u8 waiters_count = 0; - u8 waiters_index = 0; + u32 waiters_count = 0; }; - static inline std::pair*, atomic_t*> reservation_notifier(u32 raddr) + static inline atomic_t* reservation_notifier(u32 raddr, u64 rtime) { - extern std::array, 1024> g_resrv_waiters_count; + rtime = 0; + constexpr u32 wait_vars_for_each = 64; + constexpr u32 unique_address_bit_mask = 0b111; + constexpr u32 unique_rtime_bit_mask = 0b11; + + extern std::array, wait_vars_for_each * (unique_address_bit_mask + 1) * (unique_rtime_bit_mask + 1)> g_resrv_waiters_count; // Storage efficient method to distinguish different nearby addresses (which are likely) - constexpr u32 wait_vars_for_each = 8; - constexpr u32 unique_address_bit_mask = 0b11; - const usz index = std::popcount(raddr & -1024) + ((raddr / 128) & unique_address_bit_mask) * 32; - auto& waiter = g_resrv_waiters_count[index * wait_vars_for_each]; - return { &g_resrv_waiters_count[index * wait_vars_for_each + waiter.load().waiters_index % wait_vars_for_each], &waiter }; - } - - // Returns waiter count and index - static inline std::pair reservation_notifier_count_index(u32 raddr) - { - const auto notifiers = reservation_notifier(raddr); - return { notifiers.first->load().waiters_count, static_cast(notifiers.first - notifiers.second) }; + const usz index = std::popcount(raddr & -1024) * (1 << 5) + ((rtime / 128) & unique_rtime_bit_mask) * (1 << 3) + ((raddr / 128) & unique_address_bit_mask); + return &g_resrv_waiters_count[index]; } // Returns waiter count - static inline u32 reservation_notifier_count(u32 raddr) + static inline u32 reservation_notifier_count(u32 raddr, u64 rtime) { - return reservation_notifier(raddr).first->load().waiters_count; + return reservation_notifier(raddr, rtime)->load().waiters_count; } static inline void reservation_notifier_end_wait(atomic_t& waiter) { waiter.atomic_op([](reservation_waiter_t& value) { - if (value.waiters_count-- == 1) + if (value.waiters_count == 1 && value.wait_flag % 2 == 1) { - value.wait_flag = 0; + // Make wait_flag even (disabling notification on last waiter) + value.wait_flag++; } + + value.waiters_count--; }); } - static inline atomic_t* reservation_notifier_begin_wait(u32 raddr, u64 rtime) + static inline std::pair*, u32> reservation_notifier_begin_wait(u32 raddr, u64 rtime) { - const auto notifiers = reservation_notifier(raddr); - atomic_t& waiter = *notifiers.first; + atomic_t& waiter = *reservation_notifier(raddr, rtime); - waiter.atomic_op([](reservation_waiter_t& value) + u32 wait_flag = 0; + + waiter.atomic_op([&](reservation_waiter_t& value) { - value.wait_flag = 1; + if (value.wait_flag % 2 == 0) + { + // Make wait_flag odd (for notification deduplication detection) + value.wait_flag++; + } + + wait_flag = value.wait_flag; value.waiters_count++; }); if ((reservation_acquire(raddr) & -128) != rtime) { reservation_notifier_end_wait(waiter); - return nullptr; + return {}; } - return &waiter; + return { &waiter, wait_flag }; } - static inline atomic_t* reservation_notifier_notify(u32 raddr, bool pospone = false) - { - const auto notifiers = reservation_notifier(raddr); - - if (notifiers.first->load().wait_flag) - { - if (notifiers.first == notifiers.second) - { - if (!notifiers.first->fetch_op([](reservation_waiter_t& value) - { - if (value.waiters_index == 0) - { - value.wait_flag = 0; - value.waiters_count = 0; - value.waiters_index++; - return true; - } - - return false; - }).second) - { - return nullptr; - } - } - else - { - u8 old_index = static_cast(notifiers.first - notifiers.second); - if (!atomic_storage::compare_exchange(notifiers.second->raw().waiters_index, old_index, (old_index + 1) % 4)) - { - return nullptr; - } - - notifiers.first->release(reservation_waiter_t{}); - } - - if (pospone) - { - return utils::bless>(¬ifiers.first->raw().wait_flag); - } - - utils::bless>(¬ifiers.first->raw().wait_flag)->notify_all(); - } - - return nullptr; - } + atomic_t* reservation_notifier_notify(u32 raddr, u64 rtime, bool postpone = false); u64 reservation_lock_internal(u32, atomic_t&); @@ -232,28 +192,28 @@ namespace vm if constexpr (std::is_void_v>) { std::invoke(op, *sptr); - res += 128; + const u64 old_time = res.fetch_add(128); #ifndef _MSC_VER __asm__ volatile ("xend;" ::: "memory"); #else _xend(); #endif if constexpr (Ack) - res.notify_all(); + reservation_notifier_notify(addr, old_time); return; } else { if (auto result = std::invoke(op, *sptr)) { - res += 128; + const u64 old_time = res.fetch_add(128); #ifndef _MSC_VER __asm__ volatile ("xend;" ::: "memory"); #else _xend(); #endif if constexpr (Ack) - res.notify_all(); + reservation_notifier_notify(addr, old_time); return result; } else @@ -308,7 +268,7 @@ namespace vm #endif res += 127; if (Ack) - res.notify_all(); + reservation_notifier_notify(addr, _old); return; } else @@ -322,7 +282,7 @@ namespace vm #endif res += 127; if (Ack) - res.notify_all(); + reservation_notifier_notify(addr, _old); return result; } else @@ -357,7 +317,7 @@ namespace vm }); if constexpr (Ack) - res.notify_all(); + reservation_notifier_notify(addr, _old); return; } else @@ -377,7 +337,7 @@ namespace vm }); if (Ack && result) - res.notify_all(); + reservation_notifier_notify(addr, _old); return result; } } @@ -388,16 +348,18 @@ namespace vm // Lock reservation and perform heavyweight lock reservation_shared_lock_internal(res); + u64 old_time = umax; + if constexpr (std::is_void_v>) { { vm::writer_lock lock(addr); std::invoke(op, *sptr); - res += 127; + old_time = res.fetch_add(127); } if constexpr (Ack) - res.notify_all(); + reservation_notifier_notify(addr, old_time); return; } else @@ -408,16 +370,16 @@ namespace vm if ((result = std::invoke(op, *sptr))) { - res += 127; + old_time = res.fetch_add(127); } else { - res -= 1; + old_time = res.fetch_sub(1); } } if (Ack && result) - res.notify_all(); + reservation_notifier_notify(addr, old_time); return result; } } From fc62733d65cf705e13eccc4604ae780ebc192678 Mon Sep 17 00:00:00 2001 From: Elad <18193363+knight4u32@users.noreply.github.com> Date: Wed, 3 Sep 2025 17:15:22 +0300 Subject: [PATCH 037/149] SPU: Self-notifying SPU waiters --- rpcs3/Emu/Cell/SPUThread.cpp | 53 ++++++++++++++++++++++++++++++++---- rpcs3/Emu/Cell/SPUThread.h | 1 + rpcs3/Emu/Cell/lv2/lv2.cpp | 1 + 3 files changed, 50 insertions(+), 5 deletions(-) diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 3a59902fd2..5a9f645d2b 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -6094,6 +6094,9 @@ s64 spu_thread::get_ch_value(u32 ch) return true; }; + static atomic_t s_is_reservation_data_checking_thread = false; + bool is_reservation_data_checking_thread = false; + const bool is_LR_wait = raddr && mask1 & SPU_EVENT_LR; auto& history = eventstat_wait_time[(raddr % SPU_LS_SIZE) / 128]; @@ -6118,6 +6121,16 @@ s64 spu_thread::get_ch_value(u32 ch) u8& val = history.front(); val = static_cast(std::min(val + 1, u8{umax})); } + + if (!s_is_reservation_data_checking_thread)// && std::find(raddr_busy_wait_addr.begin(), raddr_busy_wait_addr.end(), raddr) != raddr_busy_wait_addr.end()) + { + if (s_is_reservation_data_checking_thread.compare_and_swap_test(0, 1)) + { + eventstat_busy_waiting_switch = 1; + is_reservation_data_checking_thread = true; + eventstat_raddr = 1; + } + } } else { @@ -6145,11 +6158,15 @@ s64 spu_thread::get_ch_value(u32 ch) if (is_stopped(old)) { - if (cache_line_waiter_index != umax) + if (is_reservation_data_checking_thread) { - g_spu_waiters_by_value[cache_line_waiter_index].release(0); + s_is_reservation_data_checking_thread = 0; + + // Check again other reservations in other threads + lv2_obj::notify_all(); } + deregister_cache_line_waiter(cache_line_waiter_index); return -1; } @@ -6169,6 +6186,14 @@ s64 spu_thread::get_ch_value(u32 ch) } else if (!cmp_rdata(rdata, *resrv_mem)) { + if (vm::reservation_acquire(raddr) == rtime) + { + // Confirm change in data only, register address for busy waiting + std::rotate(raddr_busy_wait_addr.rbegin(), raddr_busy_wait_addr.rbegin() + 1, raddr_busy_wait_addr.rend()); + raddr_busy_wait_addr[0] = raddr; + vm::reservation_update(raddr); + } + // Notify threads manually, memory data has likely changed and broke the reservation for others if (vm::reservation_notifier_count(raddr, rtime) && vm::reservation_acquire(raddr) == rtime) { @@ -6198,7 +6223,7 @@ s64 spu_thread::get_ch_value(u32 ch) // Don't be stubborn, force operating sleep if too much time has passed const u64 time_since = get_system_time() - eventstat_evaluate_time; - if (time_since >= (utils::get_thread_count() >= 9 ? 50'000 : 3000)) + if (!is_reservation_data_checking_thread && time_since >= (utils::get_thread_count() >= 9 ? 50'000 : 3000)) { spu_log.trace("SPU RdEventStat wait for 0x%x failed", raddr); history.front() = 2; @@ -6357,6 +6382,12 @@ s64 spu_thread::get_ch_value(u32 ch) } else if (!cmp_rdata(_this->rdata, *_this->resrv_mem)) { + if (vm::reservation_acquire(raddr) == _this->rtime) + { + std::rotate(_this->raddr_busy_wait_addr.rbegin(), _this->raddr_busy_wait_addr.rbegin() + 1, _this->raddr_busy_wait_addr.rend()); + _this->raddr_busy_wait_addr[0] = raddr; + } + // Notify threads manually, memory data has likely changed and broke the reservation for others if (vm::reservation_notifier_count(raddr, _this->rtime) >= 2 && vm::reservation_acquire(raddr) == _this->rtime) { @@ -6408,11 +6439,19 @@ s64 spu_thread::get_ch_value(u32 ch) thread_ctrl::wait_on(state, old, 100); } + if (is_reservation_data_checking_thread) + { + s_is_reservation_data_checking_thread = 0; + + // Check again other reservations in other threads + lv2_obj::notify_all(); + } + deregister_cache_line_waiter(cache_line_waiter_index); - wakeup_delay(); + const auto old = +state; - if (is_paused(state - cpu_flag::suspend)) + if (is_paused(old - cpu_flag::suspend)) { if (!raddr && old_raddr) { @@ -6422,6 +6461,10 @@ s64 spu_thread::get_ch_value(u32 ch) raddr = 0; } } + else if (!is_stopped(old)) + { + wakeup_delay(); + } check_state(); return events.events & mask1; diff --git a/rpcs3/Emu/Cell/SPUThread.h b/rpcs3/Emu/Cell/SPUThread.h index 5a0d9bab31..5c23e1f502 100644 --- a/rpcs3/Emu/Cell/SPUThread.h +++ b/rpcs3/Emu/Cell/SPUThread.h @@ -824,6 +824,7 @@ public: u8 cpu_work_iteration_count = 0; std::array stack_mirror; // Return address information + std::array raddr_busy_wait_addr{}; // Return address information const char* current_func{}; // Current STOP or RDCH blocking function u64 start_time{}; // Starting time of STOP or RDCH bloking function diff --git a/rpcs3/Emu/Cell/lv2/lv2.cpp b/rpcs3/Emu/Cell/lv2/lv2.cpp index eccd8bba6c..284768d8bb 100644 --- a/rpcs3/Emu/Cell/lv2/lv2.cpp +++ b/rpcs3/Emu/Cell/lv2/lv2.cpp @@ -2337,6 +2337,7 @@ void lv2_obj::notify_all() noexcept { if (notifies[i]) { + vm::reservation_update(notifies[i]); vm::reservation_notifier_notify(notifies[i], notifies_time[i]); } } From f84df036515ad079926a22e03e489a56ee06cd4f Mon Sep 17 00:00:00 2001 From: Elad <18193363+knight4u32@users.noreply.github.com> Date: Fri, 5 Sep 2025 10:31:23 +0300 Subject: [PATCH 038/149] SPU: Limit SPU self notifying thread to 12+ threaded CPUs --- rpcs3/Emu/Cell/SPUThread.cpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 5a9f645d2b..8873089cf4 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -6122,13 +6122,14 @@ s64 spu_thread::get_ch_value(u32 ch) val = static_cast(std::min(val + 1, u8{umax})); } - if (!s_is_reservation_data_checking_thread)// && std::find(raddr_busy_wait_addr.begin(), raddr_busy_wait_addr.end(), raddr) != raddr_busy_wait_addr.end()) + if (!s_is_reservation_data_checking_thread && utils::get_thread_count() >= 12)// && std::find(raddr_busy_wait_addr.begin(), raddr_busy_wait_addr.end(), raddr) != raddr_busy_wait_addr.end()) { if (s_is_reservation_data_checking_thread.compare_and_swap_test(0, 1)) { eventstat_busy_waiting_switch = 1; is_reservation_data_checking_thread = true; eventstat_raddr = 1; + eventstat_spin_count = 0; } } } @@ -6189,8 +6190,12 @@ s64 spu_thread::get_ch_value(u32 ch) if (vm::reservation_acquire(raddr) == rtime) { // Confirm change in data only, register address for busy waiting - std::rotate(raddr_busy_wait_addr.rbegin(), raddr_busy_wait_addr.rbegin() + 1, raddr_busy_wait_addr.rend()); - raddr_busy_wait_addr[0] = raddr; + if (std::find(raddr_busy_wait_addr.begin(), raddr_busy_wait_addr.end(), raddr) == raddr_busy_wait_addr.end()) + { + std::rotate(raddr_busy_wait_addr.rbegin(), raddr_busy_wait_addr.rbegin() + 1, raddr_busy_wait_addr.rend()); + raddr_busy_wait_addr[0] = raddr; + } + vm::reservation_update(raddr); } @@ -6382,10 +6387,12 @@ s64 spu_thread::get_ch_value(u32 ch) } else if (!cmp_rdata(_this->rdata, *_this->resrv_mem)) { - if (vm::reservation_acquire(raddr) == _this->rtime) + auto& wait_addrs = _this->raddr_busy_wait_addr; + + if (vm::reservation_acquire(raddr) == _this->rtime && std::find(wait_addrs.begin(), wait_addrs.end(), raddr) == wait_addrs.end()) { - std::rotate(_this->raddr_busy_wait_addr.rbegin(), _this->raddr_busy_wait_addr.rbegin() + 1, _this->raddr_busy_wait_addr.rend()); - _this->raddr_busy_wait_addr[0] = raddr; + std::rotate(wait_addrs.rbegin(), wait_addrs.rbegin() + 1, wait_addrs.rend()); + wait_addrs[0] = raddr; } // Notify threads manually, memory data has likely changed and broke the reservation for others From 05648428715b9f1f54707290eb9fc1b1a124744e Mon Sep 17 00:00:00 2001 From: Elad <18193363+knight4u32@users.noreply.github.com> Date: Fri, 5 Sep 2025 14:12:17 +0300 Subject: [PATCH 039/149] SPU: Restore postponed LR notifications (with tweaks) --- rpcs3/Emu/Cell/PPUThread.cpp | 54 +++++++++++++++++-------------- rpcs3/Emu/Cell/PPUThread.h | 1 + rpcs3/Emu/Cell/SPUThread.cpp | 9 ++++-- rpcs3/Emu/Cell/lv2/lv2.cpp | 20 ++++++------ rpcs3/Emu/Cell/lv2/sys_sync.h | 1 - rpcs3/Emu/Memory/vm.cpp | 3 ++ rpcs3/Emu/Memory/vm_reservation.h | 7 ++-- 7 files changed, 53 insertions(+), 42 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 3aa3cca8ed..ad5545ba62 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -2326,6 +2326,7 @@ void ppu_thread::cpu_wait(bs_t old) if (u32 addr = res_notify) { res_notify = 0; + res_notify_postpone_streak = 0; if (res_notify_time + 128 == (vm::reservation_acquire(addr) & -128)) { @@ -3613,34 +3614,17 @@ static bool ppu_store_reservation(ppu_thread& ppu, u32 addr, u64 reg_value) { extern atomic_t liblv2_begin, liblv2_end; - const u32 notify = ppu.res_notify; - - if (notify) - { - if (auto waiter = vm::reservation_notifier_notify(notify, ppu.res_notify_time, true)) - { - ppu.state += cpu_flag::wait; - waiter->notify_all(); - } - - ppu.res_notify = 0; - } + u32 notify = ppu.res_notify; // Avoid notifications from lwmutex or sys_spinlock const bool is_liblv2_or_null = (ppu.cia >= liblv2_begin && ppu.cia < liblv2_end); - if (!is_liblv2_or_null && (addr ^ notify) & -128) + if (!is_liblv2_or_null) { // Try to postpone notification to when PPU is asleep or join notifications on the same address // This also optimizes a mutex - won't notify after lock is aqcuired (prolonging the critical section duration), only notifies on unlock - const u32 count = vm::reservation_notifier_count(addr & -128, rtime); + const u32 count = vm::reservation_notifier_count(addr, rtime); - if (cpu_flag::wait - ppu.state) - { - ppu.state += cpu_flag::wait; - } - - vm::reservation_notifier_notify(addr & -128, rtime); switch (count) { case 0: @@ -3650,11 +3634,18 @@ static bool ppu_store_reservation(ppu_thread& ppu, u32 addr, u64 reg_value) } case 1: { - if (!notify) + // Postpone notifications if there is no pending one OR if there is likely a complex operation on reservation going on + // Which consists of multiple used addresses + if (ppu.res_notify_postpone_streak <= 4) { - ppu.res_notify = addr & -128; - ppu.res_notify_time = rtime; - break; + if (!notify || ((notify & -128) == (addr & -128) && new_data != old_data)) + { + ppu.res_notify = addr; + ppu.res_notify_time = rtime; + ppu.res_notify_postpone_streak++; + notify = 0; + break; + } } // Notify both @@ -3667,12 +3658,24 @@ static bool ppu_store_reservation(ppu_thread& ppu, u32 addr, u64 reg_value) ppu.state += cpu_flag::wait; } - vm::reservation_notifier_notify(addr & -128, rtime); + vm::reservation_notifier_notify(addr, rtime); break; } } } + if (notify) + { + if (auto waiter = vm::reservation_notifier_notify(notify, ppu.res_notify_time, true)) + { + ppu.state += cpu_flag::wait; + waiter->notify_all(); + } + + ppu.res_notify = 0; + ppu.res_notify_postpone_streak = 0; + } + static_cast(ppu.test_stopped()); if (addr == ppu.last_faddr) @@ -3700,6 +3703,7 @@ static bool ppu_store_reservation(ppu_thread& ppu, u32 addr, u64 reg_value) } ppu.res_notify = 0; + ppu.res_notify_postpone_streak = 0; } ppu.raddr = 0; diff --git a/rpcs3/Emu/Cell/PPUThread.h b/rpcs3/Emu/Cell/PPUThread.h index 322cc13ebe..97c705aed5 100644 --- a/rpcs3/Emu/Cell/PPUThread.h +++ b/rpcs3/Emu/Cell/PPUThread.h @@ -267,6 +267,7 @@ public: u32 res_cached{0}; // Reservation "cached" addresss u32 res_notify{0}; u64 res_notify_time{0}; + u32 res_notify_postpone_streak{0}; union ppu_prio_t { diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 8873089cf4..877f07e6c1 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -4200,9 +4200,10 @@ void spu_thread::do_putlluc(const spu_mfc_cmd& args) do_cell_atomic_128_store(addr, _ptr(args.lsa & 0x3ff80)); - // TODO: Implement properly (notifying previous two times) - vm::reservation_notifier_notify(addr, vm::reservation_acquire(addr) - 128); - vm::reservation_notifier_notify(addr, vm::reservation_acquire(addr) - 256); + // Cover all waiters (TODO: Get reservation time atomically) + const u64 rtime = vm::reservation_acquire(addr); + vm::reservation_notifier_notify(addr, rtime - 128); + vm::reservation_notifier_notify(addr, rtime - 256); } bool spu_thread::do_mfc(bool can_escape, bool must_finish) @@ -6132,6 +6133,8 @@ s64 spu_thread::get_ch_value(u32 ch) eventstat_spin_count = 0; } } + + lv2_obj::notify_all(); } else { diff --git a/rpcs3/Emu/Cell/lv2/lv2.cpp b/rpcs3/Emu/Cell/lv2/lv2.cpp index 284768d8bb..c405b98a2c 100644 --- a/rpcs3/Emu/Cell/lv2/lv2.cpp +++ b/rpcs3/Emu/Cell/lv2/lv2.cpp @@ -1336,13 +1336,16 @@ bool lv2_obj::sleep(cpu_thread& cpu, const u64 timeout) if (cpu.get_class() == thread_class::ppu) { - if (u32 addr = static_cast(cpu).res_notify) + ppu_thread& ppu = static_cast(cpu); + + if (u32 addr = ppu.res_notify) { - static_cast(cpu).res_notify = 0; + ppu.res_notify = 0; + ppu.res_notify_postpone_streak = 0; if (auto it = std::find(g_to_notify, std::end(g_to_notify), std::add_pointer_t{}); it != std::end(g_to_notify)) { - if ((*it++ = vm::reservation_notifier_notify(addr, static_cast(cpu).res_notify_time, true))) + if ((*it++ = vm::reservation_notifier_notify(addr, ppu.res_notify_time, true))) { if (it < std::end(g_to_notify)) { @@ -1353,7 +1356,7 @@ bool lv2_obj::sleep(cpu_thread& cpu, const u64 timeout) } else { - vm::reservation_notifier_notify(addr, static_cast(cpu).res_notify_time); + vm::reservation_notifier_notify(addr, ppu.res_notify_time); } } } @@ -1389,6 +1392,7 @@ bool lv2_obj::awake(cpu_thread* thread, s32 prio) if (u32 addr = ppu->res_notify) { ppu->res_notify = 0; + ppu->res_notify_postpone_streak = 0; if (auto it = std::find(g_to_notify, std::end(g_to_notify), std::add_pointer_t{}); it != std::end(g_to_notify)) { @@ -2258,12 +2262,9 @@ void lv2_obj::notify_all() noexcept atomic_t* range_lock = nullptr; - u32 current_raddr = 0; - if (cpu->get_class() == thread_class::spu) { range_lock = static_cast(cpu)->range_lock; - current_raddr = static_cast(cpu)->raddr; } for (usz i = 0, checked = 0; checked < 4 && i < total_waiters; i++) @@ -2272,7 +2273,7 @@ void lv2_obj::notify_all() noexcept const u64 value = waiter.load(); u32 raddr = static_cast(value) & -128; - if (vm::check_addr(raddr) && (raddr != current_raddr || (value % 128) > 1)) + if (vm::check_addr(raddr)) { if (((raddr >> 28) < 2 || (raddr >> 28) == 0xd)) { @@ -2337,8 +2338,9 @@ void lv2_obj::notify_all() noexcept { if (notifies[i]) { - vm::reservation_update(notifies[i]); + // Cover all waiters for an address vm::reservation_notifier_notify(notifies[i], notifies_time[i]); + vm::reservation_notifier_notify(notifies[i], notifies_time[i] - 128); } } } diff --git a/rpcs3/Emu/Cell/lv2/sys_sync.h b/rpcs3/Emu/Cell/lv2/sys_sync.h index 7dea0fed63..0aff5e1e7a 100644 --- a/rpcs3/Emu/Cell/lv2/sys_sync.h +++ b/rpcs3/Emu/Cell/lv2/sys_sync.h @@ -485,7 +485,6 @@ public: static void enqueue_on_top(const void* waiter) { - return; g_to_notify[0] = waiter; g_to_notify[1] = nullptr; } diff --git a/rpcs3/Emu/Memory/vm.cpp b/rpcs3/Emu/Memory/vm.cpp index 669661ee9a..de0bda933a 100644 --- a/rpcs3/Emu/Memory/vm.cpp +++ b/rpcs3/Emu/Memory/vm.cpp @@ -114,7 +114,10 @@ namespace vm if (ok) { + // Notify all waiters for an address + // This is because reservation_update is often brought after the actual reservation update and not by a fused operation reservation_notifier_notify(addr, rtime); + reservation_notifier_notify(addr, rtime - 128); if (cpu && !had_wait && cpu->test_stopped()) { diff --git a/rpcs3/Emu/Memory/vm_reservation.h b/rpcs3/Emu/Memory/vm_reservation.h index fe190aab65..6ff9aa2376 100644 --- a/rpcs3/Emu/Memory/vm_reservation.h +++ b/rpcs3/Emu/Memory/vm_reservation.h @@ -45,15 +45,14 @@ namespace vm static inline atomic_t* reservation_notifier(u32 raddr, u64 rtime) { - rtime = 0; constexpr u32 wait_vars_for_each = 64; - constexpr u32 unique_address_bit_mask = 0b111; - constexpr u32 unique_rtime_bit_mask = 0b11; + constexpr u32 unique_address_bit_mask = 0b1111; + constexpr u32 unique_rtime_bit_mask = 0b1; extern std::array, wait_vars_for_each * (unique_address_bit_mask + 1) * (unique_rtime_bit_mask + 1)> g_resrv_waiters_count; // Storage efficient method to distinguish different nearby addresses (which are likely) - const usz index = std::popcount(raddr & -1024) * (1 << 5) + ((rtime / 128) & unique_rtime_bit_mask) * (1 << 3) + ((raddr / 128) & unique_address_bit_mask); + const usz index = std::popcount(raddr & -2048) * (1 << 5) + ((rtime / 128) & unique_rtime_bit_mask) * (1 << 4) + ((raddr / 128) & unique_address_bit_mask); return &g_resrv_waiters_count[index]; } From c76a3366ec45a5b566bf9b8248232a88f18e3729 Mon Sep 17 00:00:00 2001 From: Elad <18193363+elad335@users.noreply.github.com> Date: Sun, 7 Sep 2025 13:03:36 +0300 Subject: [PATCH 040/149] Network: Increase P2P listener thread rest duration (#17474) 1 milisecond is not enough time for WSAPoll to be CPU time efficient, 5 miliseconds seems to address it. --- rpcs3/Emu/Cell/lv2/sys_net/network_context.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rpcs3/Emu/Cell/lv2/sys_net/network_context.cpp b/rpcs3/Emu/Cell/lv2/sys_net/network_context.cpp index 377da5bf31..18a1c2a935 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net/network_context.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net/network_context.cpp @@ -295,7 +295,8 @@ void p2p_thread::operator()() } #ifdef _WIN32 - const auto ret_p2p = WSAPoll(p2p_fd.data(), num_p2p_sockets, 1); + // WSAPoll seems to consume a lot of CPU time relative to its waiting duration, upping the timeout solves it + const auto ret_p2p = WSAPoll(p2p_fd.data(), num_p2p_sockets, 5); #else const auto ret_p2p = ::poll(p2p_fd.data(), num_p2p_sockets, 1); #endif From e457dbdea3ace99a10e18e2ab584241786322b52 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sun, 7 Sep 2025 16:42:30 +0200 Subject: [PATCH 041/149] Update opencv to 4.12.0 --- 3rdparty/opencv/opencv | 2 +- rpcs3/rpcs3.vcxproj | 16 ++++++++-------- rpcs3/tests/rpcs3_test.vcxproj | 12 ++++++------ 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/3rdparty/opencv/opencv b/3rdparty/opencv/opencv index 50fb5e701d..67f53c26a7 160000 --- a/3rdparty/opencv/opencv +++ b/3rdparty/opencv/opencv @@ -1 +1 @@ -Subproject commit 50fb5e701d8b0d3fe8262ed84668a94cc8cbf0b1 +Subproject commit 67f53c26a701c2aeefd8033ec2f2079e04c438ca diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index fbe424d154..bca9d45f6a 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -69,7 +69,7 @@ - $(SolutionDir)3rdparty\opencv\opencv\opencv411\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) + $(SolutionDir)3rdparty\opencv\opencv\opencv412\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 @@ -87,8 +87,8 @@ TurnOffAllWarnings - opencv_world4110.lib;DbgHelp.lib;Ole32.lib;gdi32.lib;hidapi.lib;libusb-1.0.lib;winmm.lib;miniupnpc_static.lib;rtmidi.lib;imm32.lib;ksuser.lib;version.lib;OpenAL32.lib;XAudio.lib;GLGSRender.lib;shlwapi.lib;VKGSRender.lib;vulkan-1.lib;wolfssl.lib;libcurl.lib;Wldap32.lib;glslang.lib;OSDependent.lib;SPIRV.lib;MachineIndependent.lib;GenericCodeGen.lib;Advapi32.lib;user32.lib;zlib.lib;zstd.lib;libpng16.lib;asmjit.lib;yaml-cpp.lib;discord-rpc.lib;emucore.lib;dxgi.lib;shell32.lib;Qt6Core.lib;Qt6Gui.lib;Qt6Widgets.lib;Qt6Concurrent.lib;Qt6Multimedia.lib;Qt6MultimediaWidgets.lib;Qt6Svg.lib;Qt6SvgWidgets.lib;7zip.lib;libcubeb.lib;cubeb.lib;soundtouch.lib;Avrt.lib;SDL.lib;fusion.lib;%(AdditionalDependencies) - $(SolutionDir)3rdparty\opencv\opencv\opencv411\build\x64\lib;$(SolutionDir)build\lib\$(Configuration)-$(Platform)\glslang;$(SolutionDir)build\lib_ext\$(CONFIGURATION)-$(PLATFORM);$(QTDIR)\lib;$(VULKAN_SDK)\Lib;%(AdditionalLibraryDirectories) + opencv_world4120.lib;DbgHelp.lib;Ole32.lib;gdi32.lib;hidapi.lib;libusb-1.0.lib;winmm.lib;miniupnpc_static.lib;rtmidi.lib;imm32.lib;ksuser.lib;version.lib;OpenAL32.lib;XAudio.lib;GLGSRender.lib;shlwapi.lib;VKGSRender.lib;vulkan-1.lib;wolfssl.lib;libcurl.lib;Wldap32.lib;glslang.lib;OSDependent.lib;SPIRV.lib;MachineIndependent.lib;GenericCodeGen.lib;Advapi32.lib;user32.lib;zlib.lib;zstd.lib;libpng16.lib;asmjit.lib;yaml-cpp.lib;discord-rpc.lib;emucore.lib;dxgi.lib;shell32.lib;Qt6Core.lib;Qt6Gui.lib;Qt6Widgets.lib;Qt6Concurrent.lib;Qt6Multimedia.lib;Qt6MultimediaWidgets.lib;Qt6Svg.lib;Qt6SvgWidgets.lib;7zip.lib;libcubeb.lib;cubeb.lib;soundtouch.lib;Avrt.lib;SDL.lib;fusion.lib;%(AdditionalDependencies) + $(SolutionDir)3rdparty\opencv\opencv\opencv412\build\x64\lib;$(SolutionDir)build\lib\$(Configuration)-$(Platform)\glslang;$(SolutionDir)build\lib_ext\$(CONFIGURATION)-$(PLATFORM);$(QTDIR)\lib;$(VULKAN_SDK)\Lib;%(AdditionalLibraryDirectories) "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions) true Debug @@ -111,7 +111,7 @@ $(QTDIR)\bin\windeployqt6 --no-compiler-runtime --no-opengl-sw --no-patchqt --no-translations --no-quick --no-system-d3d-compiler --no-system-dxc-compiler --no-quick-import --plugindir "$(TargetDir)qt6\plugins" --release "$(TargetPath)" - xcopy /y /d "$(SolutionDir)3rdparty\opencv\opencv\opencv411\build\x64\bin\opencv_world4110.dll" "$(OutDir)" + xcopy /y /d "$(SolutionDir)3rdparty\opencv\opencv\opencv412\build\x64\bin\opencv_world4120.dll" "$(OutDir)" @@ -130,7 +130,7 @@ - $(SolutionDir)3rdparty\opencv\opencv\opencv411\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) + $(SolutionDir)3rdparty\opencv\opencv\opencv412\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 @@ -147,8 +147,8 @@ $(IntDir)vc$(PlatformToolsetVersion).pdb - opencv_world4110.lib;DbgHelp.lib;Ole32.lib;gdi32.lib;hidapi.lib;libusb-1.0.lib;winmm.lib;miniupnpc_static.lib;rtmidi.lib;imm32.lib;ksuser.lib;version.lib;OpenAL32.lib;XAudio.lib;GLGSRender.lib;shlwapi.lib;VKGSRender.lib;vulkan-1.lib;wolfssl.lib;libcurl.lib;Wldap32.lib;glslangd.lib;OSDependentd.lib;SPIRVd.lib;MachineIndependentd.lib;GenericCodeGend.lib;Advapi32.lib;user32.lib;zlib.lib;zstd.lib;libpng16.lib;asmjit.lib;yaml-cpp.lib;discord-rpc.lib;emucore.lib;dxgi.lib;shell32.lib;Qt6Cored.lib;Qt6Guid.lib;Qt6Widgetsd.lib;Qt6Concurrentd.lib;Qt6Multimediad.lib;Qt6MultimediaWidgetsd.lib;Qt6Svgd.lib;Qt6SvgWidgetsd.lib;7zip.lib;libcubeb.lib;cubeb.lib;soundtouch.lib;Avrt.lib;SDL.lib;fusion.lib;%(AdditionalDependencies) - $(SolutionDir)3rdparty\opencv\opencv\opencv411\build\x64\lib;$(SolutionDir)build\lib\$(Configuration)-$(Platform)\glslang;$(SolutionDir)build\lib\$(CONFIGURATION)-$(PLATFORM);$(QTDIR)\lib;$(VULKAN_SDK)\Lib;%(AdditionalLibraryDirectories) + opencv_world4120.lib;DbgHelp.lib;Ole32.lib;gdi32.lib;hidapi.lib;libusb-1.0.lib;winmm.lib;miniupnpc_static.lib;rtmidi.lib;imm32.lib;ksuser.lib;version.lib;OpenAL32.lib;XAudio.lib;GLGSRender.lib;shlwapi.lib;VKGSRender.lib;vulkan-1.lib;wolfssl.lib;libcurl.lib;Wldap32.lib;glslangd.lib;OSDependentd.lib;SPIRVd.lib;MachineIndependentd.lib;GenericCodeGend.lib;Advapi32.lib;user32.lib;zlib.lib;zstd.lib;libpng16.lib;asmjit.lib;yaml-cpp.lib;discord-rpc.lib;emucore.lib;dxgi.lib;shell32.lib;Qt6Cored.lib;Qt6Guid.lib;Qt6Widgetsd.lib;Qt6Concurrentd.lib;Qt6Multimediad.lib;Qt6MultimediaWidgetsd.lib;Qt6Svgd.lib;Qt6SvgWidgetsd.lib;7zip.lib;libcubeb.lib;cubeb.lib;soundtouch.lib;Avrt.lib;SDL.lib;fusion.lib;%(AdditionalDependencies) + $(SolutionDir)3rdparty\opencv\opencv\opencv412\build\x64\lib;$(SolutionDir)build\lib\$(Configuration)-$(Platform)\glslang;$(SolutionDir)build\lib\$(CONFIGURATION)-$(PLATFORM);$(QTDIR)\lib;$(VULKAN_SDK)\Lib;%(AdditionalLibraryDirectories) "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" /VERBOSE %(AdditionalOptions) true Debug @@ -170,7 +170,7 @@ $(QTDIR)\bin\windeployqt6 --no-compiler-runtime --no-opengl-sw --no-patchqt --no-translations --no-quick --no-system-d3d-compiler --no-system-dxc-compiler --no-quick-import --plugindir "$(TargetDir)qt6\plugins" --debug "$(TargetPath)" - xcopy /y /d "$(SolutionDir)3rdparty\opencv\opencv\opencv411\build\x64\bin\opencv_world4110.dll" "$(OutDir)" + xcopy /y /d "$(SolutionDir)3rdparty\opencv\opencv\opencv412\build\x64\bin\opencv_world4120.dll" "$(OutDir)" diff --git a/rpcs3/tests/rpcs3_test.vcxproj b/rpcs3/tests/rpcs3_test.vcxproj index 5e2ba06644..c458653fda 100644 --- a/rpcs3/tests/rpcs3_test.vcxproj +++ b/rpcs3/tests/rpcs3_test.vcxproj @@ -48,15 +48,15 @@ $(IntDir)vc$(PlatformToolsetVersion).pdb
- rpcs3.lib;opencv_world4110.lib;DbgHelp.lib;Ole32.lib;gdi32.lib;hidapi.lib;libusb-1.0.lib;winmm.lib;miniupnpc_static.lib;rtmidi.lib;imm32.lib;ksuser.lib;version.lib;OpenAL32.lib;XAudio.lib;GLGSRender.lib;shlwapi.lib;VKGSRender.lib;vulkan-1.lib;wolfssl.lib;libcurl.lib;Wldap32.lib;glslang.lib;OSDependent.lib;SPIRV.lib;MachineIndependent.lib;GenericCodeGen.lib;Advapi32.lib;user32.lib;zlib.lib;zstd.lib;libpng16.lib;asmjit.lib;yaml-cpp.lib;discord-rpc.lib;emucore.lib;dxgi.lib;shell32.lib;Qt6Core.lib;Qt6Gui.lib;Qt6Widgets.lib;Qt6Concurrent.lib;Qt6Multimedia.lib;Qt6MultimediaWidgets.lib;Qt6Svg.lib;Qt6SvgWidgets.lib;7zip.lib;libcubeb.lib;cubeb.lib;soundtouch.lib;Avrt.lib;SDL.lib;fusion.lib;%(AdditionalDependencies) - $(SolutionDir)3rdparty\opencv\opencv\opencv411\build\x64\lib;$(SolutionDir)build\lib\$(Configuration)-$(Platform)\glslang;$(SolutionDir)build\lib_ext\$(CONFIGURATION)-$(PLATFORM);$(QTDIR)\lib;$(VULKAN_SDK)\Lib;%(AdditionalLibraryDirectories) + rpcs3.lib;opencv_world4120.lib;DbgHelp.lib;Ole32.lib;gdi32.lib;hidapi.lib;libusb-1.0.lib;winmm.lib;miniupnpc_static.lib;rtmidi.lib;imm32.lib;ksuser.lib;version.lib;OpenAL32.lib;XAudio.lib;GLGSRender.lib;shlwapi.lib;VKGSRender.lib;vulkan-1.lib;wolfssl.lib;libcurl.lib;Wldap32.lib;glslang.lib;OSDependent.lib;SPIRV.lib;MachineIndependent.lib;GenericCodeGen.lib;Advapi32.lib;user32.lib;zlib.lib;zstd.lib;libpng16.lib;asmjit.lib;yaml-cpp.lib;discord-rpc.lib;emucore.lib;dxgi.lib;shell32.lib;Qt6Core.lib;Qt6Gui.lib;Qt6Widgets.lib;Qt6Concurrent.lib;Qt6Multimedia.lib;Qt6MultimediaWidgets.lib;Qt6Svg.lib;Qt6SvgWidgets.lib;7zip.lib;libcubeb.lib;cubeb.lib;soundtouch.lib;Avrt.lib;SDL.lib;fusion.lib;%(AdditionalDependencies) + $(SolutionDir)3rdparty\opencv\opencv\opencv412\build\x64\lib;$(SolutionDir)build\lib\$(Configuration)-$(Platform)\glslang;$(SolutionDir)build\lib_ext\$(CONFIGURATION)-$(PLATFORM);$(QTDIR)\lib;$(VULKAN_SDK)\Lib;%(AdditionalLibraryDirectories) Console true $(QTDIR)\bin\windeployqt6 --no-compiler-runtime --no-opengl-sw --no-patchqt --no-translations --no-quick --no-system-d3d-compiler --no-system-dxc-compiler --no-ffmpeg --no-quick-import --plugindir "$(TargetDir)qt6\plugins" --release "$(TargetPath)" - xcopy /y /d "$(SolutionDir)3rdparty\opencv\opencv\opencv411\build\x64\bin\opencv_world4110.dll" "$(OutDir)" + xcopy /y /d "$(SolutionDir)3rdparty\opencv\opencv\opencv412\build\x64\bin\opencv_world4120.dll" "$(OutDir)" @@ -70,15 +70,15 @@ $(IntDir)vc$(PlatformToolsetVersion).pdb
- rpcs3d.lib;opencv_world4110.lib;DbgHelp.lib;Ole32.lib;gdi32.lib;hidapi.lib;libusb-1.0.lib;winmm.lib;miniupnpc_static.lib;rtmidi.lib;imm32.lib;ksuser.lib;version.lib;OpenAL32.lib;XAudio.lib;GLGSRender.lib;shlwapi.lib;VKGSRender.lib;vulkan-1.lib;wolfssl.lib;libcurl.lib;Wldap32.lib;glslangd.lib;OSDependentd.lib;SPIRVd.lib;MachineIndependentd.lib;GenericCodeGend.lib;Advapi32.lib;user32.lib;zlib.lib;zstd.lib;libpng16.lib;asmjit.lib;yaml-cpp.lib;discord-rpc.lib;emucore.lib;dxgi.lib;shell32.lib;Qt6Cored.lib;Qt6Guid.lib;Qt6Widgetsd.lib;Qt6Concurrentd.lib;Qt6Multimediad.lib;Qt6MultimediaWidgetsd.lib;Qt6Svgd.lib;Qt6SvgWidgetsd.lib;7zip.lib;libcubeb.lib;cubeb.lib;soundtouch.lib;Avrt.lib;SDL.lib;fusion.lib;%(AdditionalDependencies) - $(SolutionDir)3rdparty\opencv\opencv\opencv411\build\x64\lib;$(SolutionDir)build\lib\$(Configuration)-$(Platform)\glslang;$(SolutionDir)build\lib\$(CONFIGURATION)-$(PLATFORM);$(QTDIR)\lib;$(VULKAN_SDK)\Lib;%(AdditionalLibraryDirectories) + rpcs3d.lib;opencv_world4120.lib;DbgHelp.lib;Ole32.lib;gdi32.lib;hidapi.lib;libusb-1.0.lib;winmm.lib;miniupnpc_static.lib;rtmidi.lib;imm32.lib;ksuser.lib;version.lib;OpenAL32.lib;XAudio.lib;GLGSRender.lib;shlwapi.lib;VKGSRender.lib;vulkan-1.lib;wolfssl.lib;libcurl.lib;Wldap32.lib;glslangd.lib;OSDependentd.lib;SPIRVd.lib;MachineIndependentd.lib;GenericCodeGend.lib;Advapi32.lib;user32.lib;zlib.lib;zstd.lib;libpng16.lib;asmjit.lib;yaml-cpp.lib;discord-rpc.lib;emucore.lib;dxgi.lib;shell32.lib;Qt6Cored.lib;Qt6Guid.lib;Qt6Widgetsd.lib;Qt6Concurrentd.lib;Qt6Multimediad.lib;Qt6MultimediaWidgetsd.lib;Qt6Svgd.lib;Qt6SvgWidgetsd.lib;7zip.lib;libcubeb.lib;cubeb.lib;soundtouch.lib;Avrt.lib;SDL.lib;fusion.lib;%(AdditionalDependencies) + $(SolutionDir)3rdparty\opencv\opencv\opencv412\build\x64\lib;$(SolutionDir)build\lib\$(Configuration)-$(Platform)\glslang;$(SolutionDir)build\lib\$(CONFIGURATION)-$(PLATFORM);$(QTDIR)\lib;$(VULKAN_SDK)\Lib;%(AdditionalLibraryDirectories) Console true $(QTDIR)\bin\windeployqt6 --no-compiler-runtime --no-opengl-sw --no-patchqt --no-translations --no-quick --no-system-d3d-compiler --no-system-dxc-compiler --no-ffmpeg --no-quick-import --plugindir "$(TargetDir)qt6\plugins" --release "$(TargetPath)" - xcopy /y /d "$(SolutionDir)3rdparty\opencv\opencv\opencv411\build\x64\bin\opencv_world4110.dll" "$(OutDir)" + xcopy /y /d "$(SolutionDir)3rdparty\opencv\opencv\opencv412\build\x64\bin\opencv_world4120.dll" "$(OutDir)" From 28920f178171ca79c9fa7c5cda4dde5edcf8a345 Mon Sep 17 00:00:00 2001 From: Antonino Di Guardo <64427768+digant73@users.noreply.github.com> Date: Sun, 7 Sep 2025 21:21:35 +0200 Subject: [PATCH 042/149] Fix emulator crash when a pad gets disconnected (e.g. due to inactivity) (#17478) --- rpcs3/Input/hid_pad_handler.cpp | 35 +++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/rpcs3/Input/hid_pad_handler.cpp b/rpcs3/Input/hid_pad_handler.cpp index 9c9ca6ba48..9d040a0a73 100644 --- a/rpcs3/Input/hid_pad_handler.cpp +++ b/rpcs3/Input/hid_pad_handler.cpp @@ -21,10 +21,13 @@ LOG_CHANNEL(hid_log, "HID"); #ifdef ANDROID std::vector g_android_usb_devices; std::mutex g_android_usb_devices_mutex; -#elif defined(__APPLE__) -std::mutex g_hid_mutex; #endif +// Global mutex to allow "hid_enumerate()" and "hid_open_path()" are accessed by one thread at a time +// (e.g. thread running "process()" and thread running enumerate_devices()). +// It avoids the emulation crash in case the controller gets disconnected (e.g. due to inactivity) +std::mutex g_hid_mutex; + struct hid_instance { public: @@ -90,15 +93,18 @@ private: hid_device* HidDevice::open() { -#ifdef ANDROID - hidDevice = hid_libusb_wrap_sys_device(path, -1); -#elif defined(__APPLE__) - std::unique_lock static_lock(g_hid_mutex, std::defer_lock); - if (!static_lock.try_lock()) + // Lock before calling "hid_open_path()" + std::unique_lock lock(g_hid_mutex, std::defer_lock); + + if (!lock.try_lock()) { // The enumeration thread is busy. If we lock and open the device, we might get input stutter on other devices. return nullptr; } + +#ifdef ANDROID + hidDevice = hid_libusb_wrap_sys_device(path, -1); +#elif defined(__APPLE__) Emu.BlockingCallFromMainThread([this]() { hidDevice = hid_open_path(path.c_str()); @@ -170,9 +176,8 @@ hid_pad_handler::~hid_pad_handler() // Join thread m_enumeration_thread.reset(); -#if defined(__APPLE__) - std::lock_guard static_lock(g_hid_mutex); -#endif + // Lock before accessing any controller (e.g. just to close it with "close()") + std::lock_guard lock(g_hid_mutex); for (auto& controller : m_controllers) { @@ -268,9 +273,10 @@ void hid_pad_handler::enumerate_devices() #else for (const auto& [vid, pid] : m_ids) { -#if defined(__APPLE__) // Let's make sure hid_enumerate is only done one thread at a time - std::lock_guard static_lock(g_hid_mutex); + std::lock_guard lock(g_hid_mutex); + +#if defined(__APPLE__) Emu.BlockingCallFromMainThread([&]() { #endif @@ -345,9 +351,8 @@ void hid_pad_handler::update_devices() m_enumerated_serials = std::move(m_new_enumerated_serials); } -#if defined(__APPLE__) - std::lock_guard static_lock(g_hid_mutex); -#endif + // Lock before accessing any controller (e.g. just to close it with "close()") or before calling "hid_open_path()" + std::lock_guard lock(g_hid_mutex); // Scrap devices that are not in the new list for (auto& controller : m_controllers) From f63bb4c7fb68c4d599595f328c902c91405195c0 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Mon, 8 Sep 2025 20:23:28 +0200 Subject: [PATCH 043/149] input: fix sdl pad handler LED and rumble properties --- rpcs3/Input/sdl_pad_handler.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rpcs3/Input/sdl_pad_handler.cpp b/rpcs3/Input/sdl_pad_handler.cpp index ef73c81a20..2d8e511a28 100644 --- a/rpcs3/Input/sdl_pad_handler.cpp +++ b/rpcs3/Input/sdl_pad_handler.cpp @@ -265,11 +265,11 @@ SDLDevice::sdl_info sdl_pad_handler::get_sdl_info(SDL_JoystickID id) info.pid = SDL_GetGamepadProduct(info.gamepad); info.product_version = SDL_GetGamepadProductVersion(info.gamepad); info.firmware_version = SDL_GetGamepadFirmwareVersion(info.gamepad); - info.has_led = SDL_HasProperty(property_id, SDL_PROP_GAMEPAD_CAP_RGB_LED_BOOLEAN); - info.has_mono_led = SDL_HasProperty(property_id, SDL_PROP_GAMEPAD_CAP_MONO_LED_BOOLEAN); - info.has_player_led = SDL_HasProperty(property_id, SDL_PROP_GAMEPAD_CAP_PLAYER_LED_BOOLEAN); - info.has_rumble = SDL_HasProperty(property_id, SDL_PROP_GAMEPAD_CAP_RUMBLE_BOOLEAN); - info.has_rumble_triggers = SDL_HasProperty(property_id, SDL_PROP_GAMEPAD_CAP_TRIGGER_RUMBLE_BOOLEAN); + info.has_led = SDL_GetBooleanProperty(property_id, SDL_PROP_GAMEPAD_CAP_RGB_LED_BOOLEAN, false); + info.has_mono_led = SDL_GetBooleanProperty(property_id, SDL_PROP_GAMEPAD_CAP_MONO_LED_BOOLEAN, false); + info.has_player_led = SDL_GetBooleanProperty(property_id, SDL_PROP_GAMEPAD_CAP_PLAYER_LED_BOOLEAN, false); + info.has_rumble = SDL_GetBooleanProperty(property_id, SDL_PROP_GAMEPAD_CAP_RUMBLE_BOOLEAN, false); + info.has_rumble_triggers = SDL_GetBooleanProperty(property_id, SDL_PROP_GAMEPAD_CAP_TRIGGER_RUMBLE_BOOLEAN, false); info.has_accel = SDL_GamepadHasSensor(info.gamepad, SDL_SENSOR_ACCEL); info.has_gyro = SDL_GamepadHasSensor(info.gamepad, SDL_SENSOR_GYRO); From 09f8c6ddcd8421f57570091c319d24b0fc0f11db Mon Sep 17 00:00:00 2001 From: Antonino Di Guardo <64427768+digant73@users.noreply.github.com> Date: Wed, 10 Sep 2025 16:25:47 +0200 Subject: [PATCH 044/149] Add selection on firmware libraries panel by double-clicking (#17481) I minor, and last, follow up of #17459 and #17442 allowing to check/uncheck an entry in the firmware libraries panel by double-clicking. For that panel, a solution based on single-clicking was preferrable but it seems it was conflicting with the existing itemChanged (single click was triggered twice so producing no state change). IMO, better than nothing. --------- Co-authored-by: Megamouse --- rpcs3/rpcs3qt/settings_dialog.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/rpcs3/rpcs3qt/settings_dialog.cpp b/rpcs3/rpcs3qt/settings_dialog.cpp index ddb26e3144..1dcba8b9d2 100644 --- a/rpcs3/rpcs3qt/settings_dialog.cpp +++ b/rpcs3/rpcs3qt/settings_dialog.cpp @@ -1787,6 +1787,14 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std } }); + connect(ui->lleList, &QListWidget::itemDoubleClicked, this, [](QListWidgetItem* item) + { + if (!item) + return; + + item->setCheckState(item->checkState() != Qt::CheckState::Unchecked ? Qt::CheckState::Unchecked : Qt::CheckState::Checked); + }); + connect(ui->hleList, &QListWidget::itemChanged, [this](QListWidgetItem* item) { for (auto cb : ui->hleList->selectedItems()) @@ -1795,6 +1803,14 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std } }); + connect(ui->hleList, &QListWidget::itemDoubleClicked, this, [](QListWidgetItem* item) + { + if (!item) + return; + + item->setCheckState(item->checkState() != Qt::CheckState::Unchecked ? Qt::CheckState::Unchecked : Qt::CheckState::Checked); + }); + connect(this, &settings_dialog::signal_restore_dependant_defaults, this, reset_library_lists); // ______ _ _ _______ _ From eb6d13a8c0b642b590eff9d06e9a71b33d4e1fd1 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Wed, 10 Sep 2025 03:15:17 +0200 Subject: [PATCH 045/149] System: expose date format and time format as settings --- rpcs3/Emu/Cell/Modules/cellSysutil.cpp | 4 ++-- rpcs3/Emu/system_config.h | 2 ++ rpcs3/Emu/system_config_types.cpp | 31 ++++++++++++++++++++++++++ rpcs3/Emu/system_config_types.h | 13 +++++++++++ rpcs3/rpcs3qt/emu_settings.cpp | 15 +++++++++++++ rpcs3/rpcs3qt/emu_settings_type.h | 13 ++++++++--- rpcs3/rpcs3qt/settings_dialog.cpp | 6 +++++ rpcs3/rpcs3qt/settings_dialog.ui | 24 ++++++++++++++++++++ rpcs3/rpcs3qt/tooltips.h | 2 ++ 9 files changed, 105 insertions(+), 5 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellSysutil.cpp b/rpcs3/Emu/Cell/Modules/cellSysutil.cpp index a7d15af2d5..13e4b0dd83 100644 --- a/rpcs3/Emu/Cell/Modules/cellSysutil.cpp +++ b/rpcs3/Emu/Cell/Modules/cellSysutil.cpp @@ -426,11 +426,11 @@ error_code cellSysutilGetSystemParamInt(CellSysutilParamId id, vm::ptr valu break; case CELL_SYSUTIL_SYSTEMPARAM_ID_DATE_FORMAT: - *value = CELL_SYSUTIL_DATE_FMT_DDMMYYYY; + *value = static_cast(g_cfg.sys.date_fmt.get()); break; case CELL_SYSUTIL_SYSTEMPARAM_ID_TIME_FORMAT: - *value = CELL_SYSUTIL_TIME_FMT_CLOCK24; + *value = static_cast(g_cfg.sys.time_fmt.get()); break; case CELL_SYSUTIL_SYSTEMPARAM_ID_TIMEZONE: diff --git a/rpcs3/Emu/system_config.h b/rpcs3/Emu/system_config.h index c62e56b491..21f1e550bc 100644 --- a/rpcs3/Emu/system_config.h +++ b/rpcs3/Emu/system_config.h @@ -301,6 +301,8 @@ struct cfg_root : cfg::node cfg::_enum language{ this, "Language", CellSysutilLang{1} }; // CELL_SYSUTIL_LANG_ENGLISH_US cfg::_enum keyboard_type{ this, "Keyboard Type", CellKbMappingType{0} }; // CELL_KB_MAPPING_101 = US cfg::_enum enter_button_assignment{ this, "Enter button assignment", enter_button_assign::cross }; + cfg::_enum date_fmt{ this, "Date Format", date_format::ddmmyyyy }; + cfg::_enum time_fmt{ this, "Time Format", time_format::clock24 }; cfg::_int<-60*60*24*365*100LL, 60*60*24*365*100LL> console_time_offset{ this, "Console time offset (s)", 0 }; // console time offset, limited to +/-100years cfg::string system_name{this, "System Name", get_random_system_name()}; cfg::uint<0, umax> console_psid_high{this, "PSID high"}; diff --git a/rpcs3/Emu/system_config_types.cpp b/rpcs3/Emu/system_config_types.cpp index c01692b8a5..20c6f7151c 100644 --- a/rpcs3/Emu/system_config_types.cpp +++ b/rpcs3/Emu/system_config_types.cpp @@ -716,3 +716,34 @@ void fmt_class_string::format(std::string& out, u64 arg) return unknown; }); } + +template <> +void fmt_class_string::format(std::string& out, u64 arg) +{ + format_enum(out, arg, [](date_format value) + { + switch (value) + { + case date_format::yyyymmdd: return "yyyymmdd"; + case date_format::ddmmyyyy: return "ddmmyyyy"; + case date_format::mmddyyyy: return "mmddyyyy"; + } + + return unknown; + }); +} + +template <> +void fmt_class_string::format(std::string& out, u64 arg) +{ + format_enum(out, arg, [](time_format value) + { + switch (value) + { + case time_format::clock12: return "clock12"; + case time_format::clock24: return "clock24"; + } + + return unknown; + }); +} diff --git a/rpcs3/Emu/system_config_types.h b/rpcs3/Emu/system_config_types.h index 9459ba698d..6099b700a9 100644 --- a/rpcs3/Emu/system_config_types.h +++ b/rpcs3/Emu/system_config_types.h @@ -261,6 +261,19 @@ enum class enter_button_assign cross // CELL_SYSUTIL_ENTER_BUTTON_ASSIGN_CROSS }; +enum class date_format +{ + yyyymmdd, // CELL_SYSUTIL_DATE_FMT_YYYYMMDD + ddmmyyyy, // CELL_SYSUTIL_DATE_FMT_DDMMYYYY + mmddyyyy // CELL_SYSUTIL_DATE_FMT_MMDDYYYY +}; + +enum class time_format +{ + clock12, // CELL_SYSUTIL_TIME_FMT_CLOCK12 + clock24 // CELL_SYSUTIL_TIME_FMT_CLOCK24 +}; + enum class np_internet_status { disabled, diff --git a/rpcs3/rpcs3qt/emu_settings.cpp b/rpcs3/rpcs3qt/emu_settings.cpp index 0dc672bc41..dc837569e3 100644 --- a/rpcs3/rpcs3qt/emu_settings.cpp +++ b/rpcs3/rpcs3qt/emu_settings.cpp @@ -1336,6 +1336,21 @@ QString emu_settings::GetLocalizedSetting(const QString& original, emu_settings_ case vk_gpu_scheduler_mode::fast: return tr("Fast", "Asynchronous Queue Scheduler"); } break; + case emu_settings_type::DateFormat: + switch (static_cast(index)) + { + case date_format::yyyymmdd: return tr("Year/Month/Day", "Date Format"); + case date_format::ddmmyyyy: return tr("Day/Month/Year", "Date Format"); + case date_format::mmddyyyy: return tr("Month/Day/Year", "Date Format"); + } + break; + case emu_settings_type::TimeFormat: + switch (static_cast(index)) + { + case time_format::clock12: return tr("12-hour clock", "Time Format"); + case time_format::clock24: return tr("24-hour clock", "Time Format"); + } + break; case emu_settings_type::Language: switch (static_cast(index)) { diff --git a/rpcs3/rpcs3qt/emu_settings_type.h b/rpcs3/rpcs3qt/emu_settings_type.h index d0f7019c8a..68a158df7a 100644 --- a/rpcs3/rpcs3qt/emu_settings_type.h +++ b/rpcs3/rpcs3qt/emu_settings_type.h @@ -203,11 +203,15 @@ enum class emu_settings_type Language, KeyboardType, EnterButtonAssignment, + DateFormat, + TimeFormat, + ConsoleTimeOffset, + + // VFS EnableHostRoot, EmptyHdd0Tmp, LimitCacheSize, MaximumCacheSize, - ConsoleTimeOffset, }; /** A helper map that keeps track of where a given setting type is located*/ @@ -405,13 +409,16 @@ inline static const std::map settings_location // System { emu_settings_type::LicenseArea, { "System", "License Area"}}, { emu_settings_type::Language, { "System", "Language"}}, - { emu_settings_type::KeyboardType, { "System", "Keyboard Type"} }, + { emu_settings_type::KeyboardType, { "System", "Keyboard Type"}}, { emu_settings_type::EnterButtonAssignment, { "System", "Enter button assignment"}}, + { emu_settings_type::DateFormat, { "System", "Date Format"}}, + { emu_settings_type::TimeFormat, { "System", "Time Format"}}, + { emu_settings_type::ConsoleTimeOffset, { "System", "Console time offset (s)"}}, + { emu_settings_type::EnableHostRoot, { "VFS", "Enable /host_root/"}}, { emu_settings_type::EmptyHdd0Tmp, { "VFS", "Empty /dev_hdd0/tmp/"}}, { emu_settings_type::LimitCacheSize, { "VFS", "Limit disk cache size"}}, { emu_settings_type::MaximumCacheSize, { "VFS", "Disk cache maximum size (MB)"}}, - { emu_settings_type::ConsoleTimeOffset, { "System", "Console time offset (s)"}}, // Savestates { emu_settings_type::SuspendEmulationSavestateMode, { "Savestate", "Suspend Emulation Savestate Mode" }}, diff --git a/rpcs3/rpcs3qt/settings_dialog.cpp b/rpcs3/rpcs3qt/settings_dialog.cpp index 1dcba8b9d2..61052d3379 100644 --- a/rpcs3/rpcs3qt/settings_dialog.cpp +++ b/rpcs3/rpcs3qt/settings_dialog.cpp @@ -1412,6 +1412,12 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std m_emu_settings->EnhanceComboBox(ui->keyboardType, emu_settings_type::KeyboardType, false, false, 0, true); SubscribeTooltip(ui->gb_keyboardType, tooltips.settings.keyboard_type); + m_emu_settings->EnhanceComboBox(ui->dateFormat, emu_settings_type::DateFormat); + SubscribeTooltip(ui->gb_dateFormat, tooltips.settings.date_format); + + m_emu_settings->EnhanceComboBox(ui->timeFormat, emu_settings_type::TimeFormat); + SubscribeTooltip(ui->gb_timeFormat, tooltips.settings.time_format); + // Checkboxes m_emu_settings->EnhanceCheckBox(ui->enableHostRoot, emu_settings_type::EnableHostRoot); diff --git a/rpcs3/rpcs3qt/settings_dialog.ui b/rpcs3/rpcs3qt/settings_dialog.ui index 4ebc9bb8e8..1e8e5a8eef 100644 --- a/rpcs3/rpcs3qt/settings_dialog.ui +++ b/rpcs3/rpcs3qt/settings_dialog.ui @@ -2008,6 +2008,30 @@ + + + + Date Format + + + + + + + + + + + + Time Format + + + + + + + + diff --git a/rpcs3/rpcs3qt/tooltips.h b/rpcs3/rpcs3qt/tooltips.h index cc2278d5ec..5bf2e91d87 100644 --- a/rpcs3/rpcs3qt/tooltips.h +++ b/rpcs3/rpcs3qt/tooltips.h @@ -263,6 +263,8 @@ public: const QString license_area = tr("The console region defines the license area of the PS3.\nDepending on the license area, some games may not work."); const QString system_language = tr("Some games may fail to boot if the system language is not available in the game itself.\nOther games will switch language automatically to what is selected here.\nIt is recommended leaving this on a language supported by the game."); + const QString date_format = tr("Select the PS3's date format."); + const QString time_format = tr("Select the PS3's time format."); const QString keyboard_type = tr("Sets the used keyboard layout.\nCurrently only US, Japanese and German layouts are fully supported at this moment."); const QString enter_button_assignment = tr("The button used for enter/accept/confirm in system dialogs.\nChange this to use the Circle button instead, which is the default configuration on Japanese systems and in many Japanese games.\nIn these cases having the cross button assigned can often lead to confusion."); const QString enable_host_root = tr("Required for some Homebrew.\nIf unsure, do not use this option."); From 6911d0b5e15dc1ed929683d142710b3e52b4eb2f Mon Sep 17 00:00:00 2001 From: Megamouse Date: Wed, 10 Sep 2025 01:10:57 +0200 Subject: [PATCH 046/149] cellPad: Add DS3 motor speed threshold Also add motor speeds to pad debug overlay --- rpcs3/Emu/Cell/Modules/cellPad.cpp | 4 ++++ rpcs3/Emu/Io/PadHandler.cpp | 5 +++-- rpcs3/Emu/Io/PadHandler.h | 21 +++++++++++---------- rpcs3/Emu/Io/pad_config.cpp | 27 +++++++++++++++++++-------- rpcs3/Emu/Io/pad_config.h | 5 +++-- rpcs3/Emu/Io/pad_types.h | 3 +++ rpcs3/Input/ds3_pad_handler.cpp | 1 + 7 files changed, 44 insertions(+), 22 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellPad.cpp b/rpcs3/Emu/Cell/Modules/cellPad.cpp index 086a341854..8d717ab87f 100644 --- a/rpcs3/Emu/Cell/Modules/cellPad.cpp +++ b/rpcs3/Emu/Cell/Modules/cellPad.cpp @@ -86,6 +86,8 @@ void show_debug_overlay(const CellPadData& data, const Pad& pad, const pad_info& "> Digital: %5s %5s\n" "> Press: %5s %5s\n" "> Sensor: %5s %5s\n" + "> Large Motor: %5d %5d\n" + "> Small Motor: %5d %5d\n" ">\n" "> Digital 1: 0x%04x 0x%04x\n" "> Digital 2: 0x%04x 0x%04x\n" @@ -126,6 +128,8 @@ void show_debug_overlay(const CellPadData& data, const Pad& pad, const pad_info& "on", data.len >= CELL_PAD_LEN_CHANGE_DEFAULT ? "on" : "off", (setting & CELL_PAD_SETTING_PRESS_ON) ? "on" : "off", data.len >= CELL_PAD_LEN_CHANGE_PRESS_ON ? "on" : "off", (setting & CELL_PAD_SETTING_SENSOR_ON) ? "on" : "off", data.len >= CELL_PAD_LEN_CHANGE_SENSOR_ON ? "on" : "off", + pad.m_vibrateMotors[0].m_value, pad.m_vibrateMotors[0].m_adjusted_value, + pad.m_vibrateMotors[1].m_value, pad.m_vibrateMotors[1].m_adjusted_value, pad.m_digital_1, d1, pad.m_digital_2, d2, pad.m_press_up, !!(d1 & CELL_PAD_CTRL_UP), data.button[CELL_PAD_BTN_OFFSET_PRESS_UP], diff --git a/rpcs3/Emu/Io/PadHandler.cpp b/rpcs3/Emu/Io/PadHandler.cpp index 19b9347da6..561c9b15d3 100644 --- a/rpcs3/Emu/Io/PadHandler.cpp +++ b/rpcs3/Emu/Io/PadHandler.cpp @@ -40,7 +40,7 @@ f32 PadHandlerBase::ScaledInput(f32 raw_value, f32 minimum, f32 maximum, f32 dea } // convert [min, max] to [0, 1] - const f32 val = static_cast(std::clamp(raw_value, minimum, maximum) - minimum) / (maximum - minimum); + const f32 val = static_cast(std::max(minimum, std::min(raw_value, maximum)) - minimum) / (maximum - minimum); // convert [0, 1] to [0, range] return range * val; @@ -50,7 +50,7 @@ f32 PadHandlerBase::ScaledInput(f32 raw_value, f32 minimum, f32 maximum, f32 dea f32 PadHandlerBase::ScaledAxisInput(f32 raw_value, f32 minimum, f32 maximum, f32 deadzone, f32 range) { // convert [min, max] to [0, 1] - f32 val = static_cast(std::clamp(raw_value, minimum, maximum) - minimum) / (maximum - minimum); + f32 val = static_cast(std::max(minimum, std::min(raw_value, maximum)) - minimum) / (maximum - minimum); if (deadzone > 0) { @@ -762,6 +762,7 @@ void PadHandlerBase::process() for (VibrateMotor& motor : pad->m_vibrateMotors) { motor.m_value = 0; + motor.m_adjusted_value = 0; } pad->m_last_rumble_time_us = 0; diff --git a/rpcs3/Emu/Io/PadHandler.h b/rpcs3/Emu/Io/PadHandler.h index 99c085b015..b10df8ebdb 100644 --- a/rpcs3/Emu/Io/PadHandler.h +++ b/rpcs3/Emu/Io/PadHandler.h @@ -255,15 +255,6 @@ protected: return {}; } - // Get new multiplied value based on the multiplier - static s32 MultipliedInput(s32 raw_value, s32 multiplier); - - // Get new scaled value between 0 and 255 based on its minimum and maximum - static f32 ScaledInput(f32 raw_value, f32 minimum, f32 maximum, f32 deadzone, f32 range = 255.0f); - - // Get new scaled value between -255 and 255 based on its minimum and maximum - static f32 ScaledAxisInput(f32 raw_value, f32 minimum, f32 maximum, f32 deadzone, f32 range = 255.0f); - // Get normalized trigger value based on the range defined by a threshold u16 NormalizeTriggerInput(u16 value, u32 threshold) const; @@ -276,6 +267,17 @@ protected: // return is new x and y values in 0-255 range std::tuple NormalizeStickDeadzone(s32 inX, s32 inY, u32 deadzone, u32 anti_deadzone) const; +public: + + // Get new multiplied value based on the multiplier + static s32 MultipliedInput(s32 raw_value, s32 multiplier); + + // Get new scaled value between 0 and 255 based on its minimum and maximum + static f32 ScaledInput(f32 raw_value, f32 minimum, f32 maximum, f32 deadzone, f32 range = 255.0f); + + // Get new scaled value between -255 and 255 based on its minimum and maximum + static f32 ScaledAxisInput(f32 raw_value, f32 minimum, f32 maximum, f32 deadzone, f32 range = 255.0f); + // get clamped value between 0 and 255 static u16 Clamp0To255(f32 input); @@ -291,7 +293,6 @@ protected: // This function assumes inX and inY is already in 0-255 static void ConvertToSquirclePoint(u16& inX, u16& inY, u32 squircle_factor); -public: // u32 thumb_min = 0; // Unused. Make sure all handlers report 0+ values for sticks in get_button_values. u32 thumb_max = 255; u32 trigger_min = 0; diff --git a/rpcs3/Emu/Io/pad_config.cpp b/rpcs3/Emu/Io/pad_config.cpp index fb0b140aeb..8d1ec42953 100644 --- a/rpcs3/Emu/Io/pad_config.cpp +++ b/rpcs3/Emu/Io/pad_config.cpp @@ -1,8 +1,7 @@ #include "stdafx.h" #include "pad_config.h" #include "Emu/system_utils.hpp" - -LOG_CHANNEL(input_log, "Input"); +#include "Emu/Io/PadHandler.h" extern std::string g_input_config_override; @@ -32,18 +31,30 @@ std::string cfg_pad::get_buttons(std::vector vec) return fmt::merge(vec, ","); } -u8 cfg_pad::get_large_motor_speed(const std::array& motor_speed) const +u8 cfg_pad::get_large_motor_speed(std::array& motors) const { - const u8 idx = switch_vibration_motors ? 1 : 0; + VibrateMotor& motor = motors[switch_vibration_motors ? 1 : 0]; const f32 multiplier = multiplier_vibration_motor_large / 100.0f; - return static_cast(std::clamp(motor_speed[idx].m_value * multiplier, 0.0f, 255.0f)); + + // Ignore lower range. Scale remaining range to full range. + const f32 adjusted = PadHandlerBase::ScaledInput(motor.m_value, static_cast(vibration_threshold.get()), 255.0f, 0.0f, 255.0f); + + // Apply multiplier + motor.m_adjusted_value = static_cast(std::clamp(adjusted * multiplier, 0.0f, 255.0f)); + return motor.m_adjusted_value; } -u8 cfg_pad::get_small_motor_speed(const std::array& motor_speed) const +u8 cfg_pad::get_small_motor_speed(std::array& motors) const { - const u8 idx = switch_vibration_motors ? 0 : 1; + VibrateMotor& motor = motors[switch_vibration_motors ? 0 : 1]; const f32 multiplier = multiplier_vibration_motor_small / 100.0f; - return static_cast(std::clamp(motor_speed[idx].m_value * multiplier, 0.0f, 255.0f)); + + // Ignore lower range. Scale remaining range to full range. + const f32 adjusted = PadHandlerBase::ScaledInput(motor.m_value, static_cast(vibration_threshold.get()), 255.0f, 0.0f, 255.0f); + + // Apply multiplier + motor.m_adjusted_value = static_cast(std::clamp(adjusted * multiplier, 0.0f, 255.0f)); + return motor.m_adjusted_value; } bool cfg_input::load(const std::string& title_id, const std::string& config_file, bool strict) diff --git a/rpcs3/Emu/Io/pad_config.h b/rpcs3/Emu/Io/pad_config.h index 7c39a79411..7f5aa1081d 100644 --- a/rpcs3/Emu/Io/pad_config.h +++ b/rpcs3/Emu/Io/pad_config.h @@ -28,8 +28,8 @@ struct cfg_pad final : cfg::node static std::vector get_buttons(const std::string& str); static std::string get_buttons(std::vector vec); - u8 get_large_motor_speed(const std::array& motor_speed) const; - u8 get_small_motor_speed(const std::array& motor_speed) const; + u8 get_large_motor_speed(std::array& motors) const; + u8 get_small_motor_speed(std::array& motors) const; cfg::string ls_left{ this, "Left Stick Left", "" }; cfg::string ls_down{ this, "Left Stick Down", "" }; @@ -102,6 +102,7 @@ struct cfg_pad final : cfg::node cfg::uint<0, 200> multiplier_vibration_motor_large{ this, "Large Vibration Motor Multiplier", 100 }; cfg::uint<0, 200> multiplier_vibration_motor_small{ this, "Small Vibration Motor Multiplier", 100 }; cfg::_bool switch_vibration_motors{ this, "Switch Vibration Motors", false }; + cfg::uint<0, 255> vibration_threshold{ this, "Vibration Threshold", MOTOR_THRESHOLD }; cfg::_enum mouse_move_mode{ this, "Mouse Movement Mode", mouse_movement_mode::relative }; cfg::uint<0, 255> mouse_deadzone_x{ this, "Mouse Deadzone X Axis", 60 }; diff --git a/rpcs3/Emu/Io/pad_types.h b/rpcs3/Emu/Io/pad_types.h index ecbadff3bf..45790f5a00 100644 --- a/rpcs3/Emu/Io/pad_types.h +++ b/rpcs3/Emu/Io/pad_types.h @@ -344,6 +344,8 @@ struct CellPadData be_t button[CELL_PAD_MAX_CODES]; }; +static constexpr u8 MOTOR_THRESHOLD = 63; // The DS3 does not seem to respond to values <= 63. So we should ignore those in other handlers as well. + static constexpr u16 MOTION_ONE_G = 113; static constexpr u16 DEFAULT_MOTION_X = 512; static constexpr u16 DEFAULT_MOTION_Y = 399; // 512 - 113 (113 is 1G gravity) @@ -459,6 +461,7 @@ struct VibrateMotor { bool m_is_large_motor = false; u8 m_value = 0; + u8 m_adjusted_value = 0; VibrateMotor() {} VibrateMotor(bool is_large_motor, u8 value) diff --git a/rpcs3/Input/ds3_pad_handler.cpp b/rpcs3/Input/ds3_pad_handler.cpp index 2bc41ccead..00501b81c4 100644 --- a/rpcs3/Input/ds3_pad_handler.cpp +++ b/rpcs3/Input/ds3_pad_handler.cpp @@ -209,6 +209,7 @@ void ds3_pad_handler::init_config(cfg_pad* cfg) cfg->rtriggerthreshold.def = 0; // between 0 and 255 cfg->lpadsquircling.def = 0; cfg->rpadsquircling.def = 0; + cfg->vibration_threshold.def = 0; // Set default LED options cfg->led_battery_indicator.def = false; From aca9d28a7abdb2785158899dbc71f0a6f3cddc10 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Wed, 10 Sep 2025 17:32:59 +0200 Subject: [PATCH 047/149] input: simplify VibrateMotor constructor --- rpcs3/Emu/Io/PadHandler.cpp | 4 ++-- rpcs3/Emu/Io/pad_types.h | 3 +-- rpcs3/Input/evdev_joystick_handler.cpp | 4 ++-- rpcs3/Input/keyboard_pad_handler.cpp | 4 ++-- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/rpcs3/Emu/Io/PadHandler.cpp b/rpcs3/Emu/Io/PadHandler.cpp index 561c9b15d3..a37d6b69da 100644 --- a/rpcs3/Emu/Io/PadHandler.cpp +++ b/rpcs3/Emu/Io/PadHandler.cpp @@ -538,8 +538,8 @@ bool PadHandlerBase::bindPadToDevice(std::shared_ptr pad) pad->m_sensors[2] = AnalogSensor(CELL_PAD_BTN_OFFSET_SENSOR_Z, 0, 0, 0, DEFAULT_MOTION_Z); pad->m_sensors[3] = AnalogSensor(CELL_PAD_BTN_OFFSET_SENSOR_G, 0, 0, 0, DEFAULT_MOTION_G); - pad->m_vibrateMotors[0] = VibrateMotor(true, 0); - pad->m_vibrateMotors[1] = VibrateMotor(false, 0); + pad->m_vibrateMotors[0] = VibrateMotor(true); + pad->m_vibrateMotors[1] = VibrateMotor(false); m_bindings.emplace_back(pad, pad_device, nullptr); diff --git a/rpcs3/Emu/Io/pad_types.h b/rpcs3/Emu/Io/pad_types.h index 45790f5a00..9a3ef1ff68 100644 --- a/rpcs3/Emu/Io/pad_types.h +++ b/rpcs3/Emu/Io/pad_types.h @@ -464,9 +464,8 @@ struct VibrateMotor u8 m_adjusted_value = 0; VibrateMotor() {} - VibrateMotor(bool is_large_motor, u8 value) + VibrateMotor(bool is_large_motor) : m_is_large_motor(is_large_motor) - , m_value(value) {} }; diff --git a/rpcs3/Input/evdev_joystick_handler.cpp b/rpcs3/Input/evdev_joystick_handler.cpp index 3b8677afd8..b485c9c13e 100644 --- a/rpcs3/Input/evdev_joystick_handler.cpp +++ b/rpcs3/Input/evdev_joystick_handler.cpp @@ -1478,8 +1478,8 @@ bool evdev_joystick_handler::bindPadToDevice(std::shared_ptr pad) pad->m_sensors[2] = AnalogSensor(CELL_PAD_BTN_OFFSET_SENSOR_Z, m_dev->axis_motion[2].code, m_dev->axis_motion[2].mirrored, m_dev->axis_motion[2].shift, DEFAULT_MOTION_Z); pad->m_sensors[3] = AnalogSensor(CELL_PAD_BTN_OFFSET_SENSOR_G, m_dev->axis_motion[3].code, m_dev->axis_motion[3].mirrored, m_dev->axis_motion[3].shift, DEFAULT_MOTION_G); - pad->m_vibrateMotors[0] = VibrateMotor(true, 0); - pad->m_vibrateMotors[1] = VibrateMotor(false, 0); + pad->m_vibrateMotors[0] = VibrateMotor(true); + pad->m_vibrateMotors[1] = VibrateMotor(false); if (std::shared_ptr evdev_device = add_device(player_config->device, false)) { diff --git a/rpcs3/Input/keyboard_pad_handler.cpp b/rpcs3/Input/keyboard_pad_handler.cpp index 11901800c2..57c72f28ad 100644 --- a/rpcs3/Input/keyboard_pad_handler.cpp +++ b/rpcs3/Input/keyboard_pad_handler.cpp @@ -1072,8 +1072,8 @@ bool keyboard_pad_handler::bindPadToDevice(std::shared_ptr pad) pad->m_sensors[2] = AnalogSensor(CELL_PAD_BTN_OFFSET_SENSOR_Z, 0, 0, 0, DEFAULT_MOTION_Z); pad->m_sensors[3] = AnalogSensor(CELL_PAD_BTN_OFFSET_SENSOR_G, 0, 0, 0, DEFAULT_MOTION_G); - pad->m_vibrateMotors[0] = VibrateMotor(true, 0); - pad->m_vibrateMotors[1] = VibrateMotor(false, 0); + pad->m_vibrateMotors[0] = VibrateMotor(true); + pad->m_vibrateMotors[1] = VibrateMotor(false); m_bindings.emplace_back(pad, nullptr, nullptr); m_pads_internal.push_back(*pad); From 0876a7d37e507b9087c0e1f99e23f5064ab53545 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Wed, 10 Sep 2025 17:49:09 +0200 Subject: [PATCH 048/149] input: Memorize original small motor value for display purposes --- rpcs3/Emu/Cell/Modules/cellGem.cpp | 2 +- rpcs3/Emu/Cell/Modules/cellPad.cpp | 6 +++--- rpcs3/Emu/Io/PadHandler.cpp | 10 +++++----- rpcs3/Emu/Io/pad_config.cpp | 27 ++++++++++++-------------- rpcs3/Emu/Io/pad_config.h | 1 + rpcs3/Emu/Io/pad_types.h | 10 +++++----- rpcs3/Input/ds3_pad_handler.cpp | 4 ++-- rpcs3/Input/ds4_pad_handler.cpp | 4 ++-- rpcs3/Input/dualsense_pad_handler.cpp | 4 ++-- rpcs3/Input/evdev_joystick_handler.cpp | 8 ++++---- rpcs3/Input/keyboard_pad_handler.cpp | 4 ++-- rpcs3/Input/pad_thread.cpp | 10 +++++----- rpcs3/Input/pad_thread.h | 2 +- rpcs3/Input/ps_move_handler.cpp | 2 +- rpcs3/Input/sdl_pad_handler.cpp | 4 ++-- rpcs3/Input/xinput_pad_handler.cpp | 4 ++-- 16 files changed, 50 insertions(+), 52 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellGem.cpp b/rpcs3/Emu/Cell/Modules/cellGem.cpp index ba796be28e..9d39b3c7eb 100644 --- a/rpcs3/Emu/Cell/Modules/cellGem.cpp +++ b/rpcs3/Emu/Cell/Modules/cellGem.cpp @@ -3482,7 +3482,7 @@ error_code cellGemSetRumble(u32 gem_num, u8 rumble) { if (!binding.device || binding.device->player_id != pad_index) continue; - handler->SetRumble(pad_index, rumble, rumble > 0); + handler->SetRumble(pad_index, rumble, rumble); break; } } diff --git a/rpcs3/Emu/Cell/Modules/cellPad.cpp b/rpcs3/Emu/Cell/Modules/cellPad.cpp index 8d717ab87f..be9194fd84 100644 --- a/rpcs3/Emu/Cell/Modules/cellPad.cpp +++ b/rpcs3/Emu/Cell/Modules/cellPad.cpp @@ -128,8 +128,8 @@ void show_debug_overlay(const CellPadData& data, const Pad& pad, const pad_info& "on", data.len >= CELL_PAD_LEN_CHANGE_DEFAULT ? "on" : "off", (setting & CELL_PAD_SETTING_PRESS_ON) ? "on" : "off", data.len >= CELL_PAD_LEN_CHANGE_PRESS_ON ? "on" : "off", (setting & CELL_PAD_SETTING_SENSOR_ON) ? "on" : "off", data.len >= CELL_PAD_LEN_CHANGE_SENSOR_ON ? "on" : "off", - pad.m_vibrateMotors[0].m_value, pad.m_vibrateMotors[0].m_adjusted_value, - pad.m_vibrateMotors[1].m_value, pad.m_vibrateMotors[1].m_adjusted_value, + pad.m_vibrate_motors[0].value, pad.m_vibrate_motors[0].adjusted_value, + pad.m_vibrate_motors[1].value, pad.m_vibrate_motors[1].adjusted_value, pad.m_digital_1, d1, pad.m_digital_2, d2, pad.m_press_up, !!(d1 & CELL_PAD_CTRL_UP), data.button[CELL_PAD_BTN_OFFSET_PRESS_UP], @@ -905,7 +905,7 @@ error_code cellPadSetActDirect(u32 port_no, vm::ptr param) if (!(pad->m_device_capability & CELL_PAD_CAPABILITY_ACTUATOR)) return CELL_PAD_ERROR_UNSUPPORTED_GAMEPAD; - handler->SetRumble(port_no, param->motor[1], param->motor[0] > 0); + handler->SetRumble(port_no, param->motor[1], param->motor[0]); return CELL_OK; } diff --git a/rpcs3/Emu/Io/PadHandler.cpp b/rpcs3/Emu/Io/PadHandler.cpp index a37d6b69da..c4c636cfa7 100644 --- a/rpcs3/Emu/Io/PadHandler.cpp +++ b/rpcs3/Emu/Io/PadHandler.cpp @@ -538,8 +538,8 @@ bool PadHandlerBase::bindPadToDevice(std::shared_ptr pad) pad->m_sensors[2] = AnalogSensor(CELL_PAD_BTN_OFFSET_SENSOR_Z, 0, 0, 0, DEFAULT_MOTION_Z); pad->m_sensors[3] = AnalogSensor(CELL_PAD_BTN_OFFSET_SENSOR_G, 0, 0, 0, DEFAULT_MOTION_G); - pad->m_vibrateMotors[0] = VibrateMotor(true); - pad->m_vibrateMotors[1] = VibrateMotor(false); + pad->m_vibrate_motors[0] = VibrateMotor(true); + pad->m_vibrate_motors[1] = VibrateMotor(false); m_bindings.emplace_back(pad, pad_device, nullptr); @@ -759,10 +759,10 @@ void PadHandlerBase::process() if ((get_system_time() - pad->m_last_rumble_time_us) > 3'000'000) { - for (VibrateMotor& motor : pad->m_vibrateMotors) + for (VibrateMotor& motor : pad->m_vibrate_motors) { - motor.m_value = 0; - motor.m_adjusted_value = 0; + motor.value = 0; + motor.adjusted_value = 0; } pad->m_last_rumble_time_us = 0; diff --git a/rpcs3/Emu/Io/pad_config.cpp b/rpcs3/Emu/Io/pad_config.cpp index 8d1ec42953..614d972716 100644 --- a/rpcs3/Emu/Io/pad_config.cpp +++ b/rpcs3/Emu/Io/pad_config.cpp @@ -31,30 +31,27 @@ std::string cfg_pad::get_buttons(std::vector vec) return fmt::merge(vec, ","); } -u8 cfg_pad::get_large_motor_speed(std::array& motors) const +u8 cfg_pad::get_motor_speed(VibrateMotor& motor, f32 multiplier) const { - VibrateMotor& motor = motors[switch_vibration_motors ? 1 : 0]; - const f32 multiplier = multiplier_vibration_motor_large / 100.0f; + // If motor is small, use either 0 or 255. + const u8 value = motor.is_large_motor ? motor.value : (motor.value > 0 ? 255 : 0); // Ignore lower range. Scale remaining range to full range. - const f32 adjusted = PadHandlerBase::ScaledInput(motor.m_value, static_cast(vibration_threshold.get()), 255.0f, 0.0f, 255.0f); + const f32 adjusted = PadHandlerBase::ScaledInput(value, static_cast(vibration_threshold.get()), 255.0f, 0.0f, 255.0f); // Apply multiplier - motor.m_adjusted_value = static_cast(std::clamp(adjusted * multiplier, 0.0f, 255.0f)); - return motor.m_adjusted_value; + motor.adjusted_value = static_cast(std::clamp(adjusted * multiplier, 0.0f, 255.0f)); + return motor.adjusted_value; +} + +u8 cfg_pad::get_large_motor_speed(std::array& motors) const +{ + return get_motor_speed(motors[switch_vibration_motors ? 1 : 0], multiplier_vibration_motor_large / 100.0f); } u8 cfg_pad::get_small_motor_speed(std::array& motors) const { - VibrateMotor& motor = motors[switch_vibration_motors ? 0 : 1]; - const f32 multiplier = multiplier_vibration_motor_small / 100.0f; - - // Ignore lower range. Scale remaining range to full range. - const f32 adjusted = PadHandlerBase::ScaledInput(motor.m_value, static_cast(vibration_threshold.get()), 255.0f, 0.0f, 255.0f); - - // Apply multiplier - motor.m_adjusted_value = static_cast(std::clamp(adjusted * multiplier, 0.0f, 255.0f)); - return motor.m_adjusted_value; + return get_motor_speed(motors[switch_vibration_motors ? 0 : 1], multiplier_vibration_motor_small / 100.0f); } bool cfg_input::load(const std::string& title_id, const std::string& config_file, bool strict) diff --git a/rpcs3/Emu/Io/pad_config.h b/rpcs3/Emu/Io/pad_config.h index 7f5aa1081d..5f8ea18a74 100644 --- a/rpcs3/Emu/Io/pad_config.h +++ b/rpcs3/Emu/Io/pad_config.h @@ -28,6 +28,7 @@ struct cfg_pad final : cfg::node static std::vector get_buttons(const std::string& str); static std::string get_buttons(std::vector vec); + u8 get_motor_speed(VibrateMotor& motor, f32 multiplier) const; u8 get_large_motor_speed(std::array& motors) const; u8 get_small_motor_speed(std::array& motors) const; diff --git a/rpcs3/Emu/Io/pad_types.h b/rpcs3/Emu/Io/pad_types.h index 9a3ef1ff68..c903f2fae4 100644 --- a/rpcs3/Emu/Io/pad_types.h +++ b/rpcs3/Emu/Io/pad_types.h @@ -459,13 +459,13 @@ struct AnalogSensor struct VibrateMotor { - bool m_is_large_motor = false; - u8 m_value = 0; - u8 m_adjusted_value = 0; + bool is_large_motor = false; + u8 value = 0; + u8 adjusted_value = 0; VibrateMotor() {} VibrateMotor(bool is_large_motor) - : m_is_large_motor(is_large_motor) + : is_large_motor(is_large_motor) {} }; @@ -546,7 +546,7 @@ struct Pad std::vector