diff --git a/.ci/build-freebsd.sh b/.ci/build-freebsd.sh index 3244f1a3cf..7e10691203 100755 --- a/.ci/build-freebsd.sh +++ b/.ci/build-freebsd.sh @@ -1,9 +1,10 @@ #!/bin/sh -ex -# Pull all the submodules except llvm, opencv, libpng, sdl and curl +# Pull all the submodules except some # Note: Tried to use git submodule status, but it takes over 20 seconds # shellcheck disable=SC2046 -git submodule -q update --init --depth 1 $(awk '/path/ && !/llvm/ && !/opencv/ && !/libpng/ && !/libsdl-org/ && !/curl/ { print $3 }' .gitmodules) +git config --global --add safe.directory . +git submodule -q update --init --depth 1 $(awk '/path/ && !/llvm/ && !/opencv/ && !/libpng/ && !/libsdl-org/ && !/curl/ && !/zlib/ && !/libusb/ && !/feralinteractive/ { print $3 }' .gitmodules) CONFIGURE_ARGS=" -DWITH_LLVM=ON @@ -13,6 +14,7 @@ CONFIGURE_ARGS=" -DUSE_SYSTEM_FFMPEG=ON -DUSE_SYSTEM_CURL=ON -DUSE_SYSTEM_LIBPNG=ON + -DUSE_SYSTEM_LIBUSB=ON -DUSE_SYSTEM_OPENCV=ON " diff --git a/.ci/build-linux-aarch64.sh b/.ci/build-linux-aarch64.sh index e067f23445..1fe640809c 100755 --- a/.ci/build-linux-aarch64.sh +++ b/.ci/build-linux-aarch64.sh @@ -1,16 +1,14 @@ #!/bin/sh -ex -if [ -z "$CIRRUS_CI" ]; then - cd rpcs3 || exit 1 -fi +cd rpcs3 || exit 1 shellcheck .ci/*.sh git config --global --add safe.directory '*' -# Pull all the submodules except llvm, opencv, sdl and curl +# Pull all the submodules except some # shellcheck disable=SC2046 -git submodule -q update --init $(awk '/path/ && !/llvm/ && !/opencv/ && !/libsdl-org/ && !/curl/ { print $3 }' .gitmodules) +git submodule -q update --init $(awk '/path/ && !/llvm/ && !/opencv/ && !/libsdl-org/ && !/curl/ && !/zlib/ { print $3 }' .gitmodules) mkdir build && cd build || exit 1 @@ -52,10 +50,6 @@ ninja; build_status=$?; cd .. # If it compiled succesfully let's deploy. -# Azure and Cirrus publish PRs as artifacts only. -{ [ "$CI_HAS_ARTIFACTS" = "true" ]; -} && SHOULD_DEPLOY="true" || SHOULD_DEPLOY="false" - -if [ "$build_status" -eq 0 ] && [ "$SHOULD_DEPLOY" = "true" ]; then +if [ "$build_status" -eq 0 ]; then .ci/deploy-linux.sh "aarch64" fi diff --git a/.ci/build-linux.sh b/.ci/build-linux.sh index 4d9e9d0d09..13a9e802f3 100755 --- a/.ci/build-linux.sh +++ b/.ci/build-linux.sh @@ -1,17 +1,15 @@ #!/bin/sh -ex -if [ -z "$CIRRUS_CI" ]; then - cd rpcs3 || exit 1 -fi +cd rpcs3 || exit 1 shellcheck .ci/*.sh git config --global --add safe.directory '*' -# Pull all the submodules except llvm, opencv, sdl and curl +# Pull all the submodules except some # Note: Tried to use git submodule status, but it takes over 20 seconds # shellcheck disable=SC2046 -git submodule -q update --init $(awk '/path/ && !/llvm/ && !/opencv/ && !/libsdl-org/ && !/curl/ { print $3 }' .gitmodules) +git submodule -q update --init $(awk '/path/ && !/llvm/ && !/opencv/ && !/libsdl-org/ && !/curl/ && !/zlib/ { print $3 }' .gitmodules) mkdir build && cd build || exit 1 @@ -63,10 +61,6 @@ ninja; build_status=$?; cd .. # If it compiled succesfully let's deploy. -# Azure and Cirrus publish PRs as artifacts only. -{ [ "$CI_HAS_ARTIFACTS" = "true" ]; -} && SHOULD_DEPLOY="true" || SHOULD_DEPLOY="false" - -if [ "$build_status" -eq 0 ] && [ "$SHOULD_DEPLOY" = "true" ]; then +if [ "$build_status" -eq 0 ]; then .ci/deploy-linux.sh "x86_64" fi diff --git a/.ci/build-mac-arm64.sh b/.ci/build-mac-arm64.sh old mode 100644 new mode 100755 index 0b7d554504..b69c634f48 --- a/.ci/build-mac-arm64.sh +++ b/.ci/build-mac-arm64.sh @@ -1,122 +1,90 @@ #!/bin/sh -ex # shellcheck disable=SC2086 -brew_arm64_install_packages() { - for pkg in "$@"; do - echo "Fetching bottle for $pkg (arm64)..." - bottle_path="$("$BREW_ARM64_PATH/bin/brew" --cache --bottle-tag=arm64_sonoma "$pkg")" - if [ ! -f "$bottle_path" ]; then - if ! "$BREW_ARM64_PATH/bin/brew" fetch --force --verbose --debug --bottle-tag=arm64_sonoma "$pkg"; then - echo "Failed to fetch bottle for $pkg" - return 1 - fi - bottle_path="$("$BREW_ARM64_PATH/bin/brew" --cache --bottle-tag=arm64_sonoma "$pkg")" - fi - - echo "Installing $pkg (arm64)..." - "$BREW_ARM64_PATH/bin/brew" install --force --force-bottle --ignore-dependencies "$bottle_path" || true - done -} - export HOMEBREW_NO_AUTO_UPDATE=1 export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 +export HOMEBREW_NO_ENV_HINTS=1 export HOMEBREW_NO_INSTALL_CLEANUP=1 -/usr/local/bin/brew update -sudo rm -rf /usr/local/Cellar/curl /usr/local/opt/curl -/usr/local/bin/brew install -f --overwrite curl -/usr/local/bin/brew uninstall -f --ignore-dependencies ffmpeg -/usr/local/bin/brew install -f --build-from-source ffmpeg@5 || true -/usr/local/bin/brew install -f --overwrite python || true -/usr/local/bin/brew link --overwrite python || true -/usr/local/bin/brew install -f --overwrite nasm ninja p7zip ccache pipenv #create-dmg -/usr/local/bin/brew link -f curl || true -/usr/local/bin/brew install llvm@$LLVM_COMPILER_VER glew cmake sdl3 vulkan-headers coreutils -/usr/local/bin/brew link -f llvm@$LLVM_COMPILER_VER ffmpeg@5 || true +brew install -f --overwrite --quiet pipenv googletest ffmpeg@5 "llvm@$LLVM_COMPILER_VER" glew sdl3 vulkan-headers +brew link -f --quiet "llvm@$LLVM_COMPILER_VER" ffmpeg@5 -export BREW_ARM64_PATH="/opt/homebrew1" -sudo mkdir -p "$BREW_ARM64_PATH" -sudo chmod 777 "$BREW_ARM64_PATH" -curl -L https://github.com/Homebrew/brew/tarball/master | tar xz --strip 1 -C "$BREW_ARM64_PATH" +# moltenvk based on commit for 1.4.0 release +export HOMEBREW_DEVELOPER=1 # Prevents blocking of local formulae +wget https://raw.githubusercontent.com/Homebrew/homebrew-core/ea2bec5f1f4384e188d7fc0702ab21a20a2ced08/Formula/m/molten-vk.rb +/opt/homebrew/bin/brew install -f --overwrite --formula --quiet ./molten-vk.rb +export HOMEBREW_DEVELOPER=0 -#"$BREW_ARM64_PATH/bin/brew" update -# libvorbis requires Homebrew-installed curl, but we can't run it on x64, and we also need the aarch64 libs, so we swap the binary -brew_arm64_install_packages curl -mv /opt/homebrew1/opt/curl/bin/curl /opt/homebrew1/opt/curl/bin/curl.bak -ln -s /usr/local/opt/curl/bin/curl /opt/homebrew1/opt/curl/bin/curl - -brew_arm64_install_packages 0mq aom aribb24 ca-certificates cjson dav1d ffmpeg@5 fontconfig freetype freetype2 gettext glew gmp gnutls lame libbluray libidn2 libnettle libogg libpng librist libsodium libsoxr libtasn libtasn1 libunistring libvmaf libvorbis libvpx libx11 libxau libxcb libxdmcp llvm@$LLVM_COMPILER_VER mbedtls molten-vk nettle opencore-amr openjpeg openssl opus p11-kit pkg-config pkgconfig pzstd rav1e sdl3 snappy speex srt svt-av1 theora vulkan-headers webp x264 x265 xz z3 zeromq zmq zstd -"$BREW_ARM64_PATH/bin/brew" link -f ffmpeg@5 -ln -s "/opt/homebrew1/opt/llvm@$LLVM_COMPILER_VER/lib/unwind/libunwind.1.dylib" "/opt/homebrew1/opt/llvm@$LLVM_COMPILER_VER/lib/libunwind.1.dylib" - -# moltenvk based on commit for 1.3.0 release -wget https://raw.githubusercontent.com/Homebrew/homebrew-core/7255441cbcafabaa8950f67c7ec55ff499dbb2d3/Formula/m/molten-vk.rb -/usr/local/bin/brew install -f --overwrite ./molten-vk.rb export CXX=clang++ export CC=clang export BREW_PATH; BREW_PATH="$(brew --prefix)" -export BREW_X64_PATH; -BREW_X64_PATH="$("/usr/local/bin/brew" --prefix)" -export BREW_BIN="/usr/local/bin" -export BREW_SBIN="/usr/local/sbin" +export BREW_BIN="/opt/homebrew/bin" +export BREW_SBIN="/opt/homebrew/sbin" export CMAKE_EXTRA_OPTS='-DLLVM_TARGETS_TO_BUILD=arm64' export WORKDIR; WORKDIR="$(pwd)" +# Setup ccache +if [ ! -d "$CCACHE_DIR" ]; then + mkdir -p "$CCACHE_DIR" +fi + # Get Qt if [ ! -d "/tmp/Qt/$QT_VER" ]; then mkdir -p "/tmp/Qt" git clone https://github.com/engnr/qt-downloader.git cd qt-downloader git checkout f52efee0f18668c6d6de2dec0234b8c4bc54c597 - # nested Qt 6.9.0 URL workaround + # nested Qt 6.10.0 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 + sed -i '' "s/official_releases/archive/g" qt-downloader cd "/tmp/Qt" - "$BREW_X64_PATH/bin/pipenv" run pip3 install py7zr requests semantic_version lxml + "$BREW_PATH/bin/pipenv" run pip3 uninstall py7zr requests semantic_version lxml + "$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.0 workaround - "$BREW_X64_PATH/bin/pipenv" run "$WORKDIR/qt-downloader/qt-downloader" macos desktop "$QT_VER" clang_64 --opensource --addons qtmultimedia qtimageformats # -o "$QT_VER/clang_64" + # sed -i '' 's/args\.version \/ derive_toolchain_dir(args) \/ //g' "$WORKDIR/qt-downloader/qt-downloader" # Qt 6.10.0 workaround + "$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 cd "$WORKDIR" ditto "/tmp/Qt/$QT_VER" "qt-downloader/$QT_VER" export Qt6_DIR="$WORKDIR/qt-downloader/$QT_VER/clang_64/lib/cmake/Qt$QT_VER_MAIN" -export SDL3_DIR="$BREW_ARM64_PATH/opt/sdl3/lib/cmake/SDL3" +export SDL3_DIR="$BREW_PATH/opt/sdl3/lib/cmake/SDL3" -export PATH="$BREW_X64_PATH/opt/llvm@$LLVM_COMPILER_VER/bin:$WORKDIR/qt-downloader/$QT_VER/clang_64/bin:$BREW_BIN:$BREW_SBIN:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/Library/Apple/usr/bin:$PATH" -export LDFLAGS="-L$BREW_ARM64_PATH/lib $BREW_ARM64_PATH/opt/ffmpeg@5/lib/libavcodec.dylib $BREW_ARM64_PATH/opt/ffmpeg@5/lib/libavformat.dylib $BREW_ARM64_PATH/opt/ffmpeg@5/lib/libavutil.dylib $BREW_ARM64_PATH/opt/ffmpeg@5/lib/libswscale.dylib $BREW_ARM64_PATH/opt/ffmpeg@5/lib/libswresample.dylib $BREW_ARM64_PATH/opt/llvm@$LLVM_COMPILER_VER/lib/c++/libc++.1.dylib $BREW_ARM64_PATH/lib/libSDL3.dylib $BREW_ARM64_PATH/lib/libGLEW.dylib $BREW_ARM64_PATH/opt/llvm@$LLVM_COMPILER_VER/lib/libunwind.1.dylib -Wl,-rpath,$BREW_ARM64_PATH/lib" -export CPPFLAGS="-I$BREW_ARM64_PATH/include -I$BREW_X64_PATH/include -no-pie -D__MAC_OS_X_VERSION_MIN_REQUIRED=140000" +export PATH="$BREW_PATH/opt/llvm@$LLVM_COMPILER_VER/bin:$WORKDIR/qt-downloader/$QT_VER/clang_64/bin:$BREW_BIN:$BREW_SBIN:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/Library/Apple/usr/bin:$PATH" +export LDFLAGS="-L$BREW_PATH/lib $BREW_PATH/opt/ffmpeg@5/lib/libavcodec.dylib $BREW_PATH/opt/ffmpeg@5/lib/libavformat.dylib $BREW_PATH/opt/ffmpeg@5/lib/libavutil.dylib $BREW_PATH/opt/ffmpeg@5/lib/libswscale.dylib $BREW_PATH/opt/ffmpeg@5/lib/libswresample.dylib $BREW_PATH/opt/llvm@$LLVM_COMPILER_VER/lib/c++/libc++.1.dylib $BREW_PATH/lib/libSDL3.dylib $BREW_PATH/lib/libGLEW.dylib $BREW_PATH/opt/llvm@$LLVM_COMPILER_VER/lib/unwind/libunwind.1.dylib -Wl,-rpath,$BREW_PATH/lib" +export CPPFLAGS="-I$BREW_PATH/include -I$BREW_PATH/include -no-pie -D__MAC_OS_X_VERSION_MIN_REQUIRED=140000" export CFLAGS="-D__MAC_OS_X_VERSION_MIN_REQUIRED=140000" -export LIBRARY_PATH="$BREW_ARM64_PATH/lib" -export LD_LIBRARY_PATH="$BREW_ARM64_PATH/lib" +export LIBRARY_PATH="$BREW_PATH/lib" +export LD_LIBRARY_PATH="$BREW_PATH/lib" export VULKAN_SDK -VULKAN_SDK="$BREW_ARM64_PATH/opt/molten-vk" +VULKAN_SDK="$BREW_PATH/opt/molten-vk" ln -s "$VULKAN_SDK/lib/libMoltenVK.dylib" "$VULKAN_SDK/lib/libvulkan.dylib" || true export VK_ICD_FILENAMES="$VULKAN_SDK/share/vulkan/icd.d/MoltenVK_icd.json" export LLVM_DIR -LLVM_DIR="$BREW_ARM64_PATH/opt/llvm@$LLVM_COMPILER_VER" -# exclude ffmpeg, LLVM, and sdl from submodule update +LLVM_DIR="$BREW_PATH/opt/llvm@$LLVM_COMPILER_VER" +# exclude ffmpeg, LLVM, opencv, and sdl from submodule update # shellcheck disable=SC2046 -git submodule -q update --init --depth=1 --jobs=8 $(awk '/path/ && !/ffmpeg/ && !/llvm/ && !/SDL/ { print $3 }' .gitmodules) +git submodule -q update --init --depth=1 --jobs=8 $(awk '/path/ && !/ffmpeg/ && !/llvm/ && !/opencv/ && !/SDL/ && !/feralinteractive/ { print $3 }' .gitmodules) # 3rdparty fixes sed -i '' "s/extern const double NSAppKitVersionNumber;/const double NSAppKitVersionNumber = 1343;/g" 3rdparty/hidapi/hidapi/mac/hid.c -rm -rf build mkdir build && cd build || exit 1 export MACOSX_DEPLOYMENT_TARGET=14.0 -"$BREW_X64_PATH/bin/cmake" .. \ - -DBUILD_RPCS3_TESTS=OFF \ - -DRUN_RPCS3_TESTS=OFF \ +"$BREW_PATH/bin/cmake" .. \ + -DBUILD_RPCS3_TESTS="${RUN_UNIT_TESTS}" \ + -DRUN_RPCS3_TESTS="${RUN_UNIT_TESTS}" \ -DUSE_SDL=ON \ -DUSE_DISCORD_RPC=ON \ -DUSE_VULKAN=ON \ @@ -139,13 +107,11 @@ export MACOSX_DEPLOYMENT_TARGET=14.0 -DUSE_SYSTEM_FAUDIO=OFF \ -DUSE_SYSTEM_SDL=ON \ -DUSE_SYSTEM_OPENCV=ON \ - $CMAKE_EXTRA_OPTS \ + "$CMAKE_EXTRA_OPTS" \ -DLLVM_TARGET_ARCH=arm64 \ -DCMAKE_OSX_ARCHITECTURES=arm64 \ - -DCMAKE_IGNORE_PATH="$BREW_X64_PATH/lib" \ - -DCMAKE_IGNORE_PREFIX_PATH=/usr/local/opt \ - -DCMAKE_SYSTEM_PROCESSOR=arm64 \ - -DCMAKE_TOOLCHAIN_FILE=buildfiles/cmake/TCDarwinARM64.cmake \ + -DCMAKE_IGNORE_PATH="$BREW_PATH/lib" \ + -DCMAKE_IGNORE_PREFIX_PATH=/opt/homebrew/opt \ -DCMAKE_CXX_FLAGS="-D__MAC_OS_X_VERSION_MIN_REQUIRED=140000" \ -DCMAKE_POLICY_VERSION_MINIMUM=3.5 \ -DCMAKE_OSX_SYSROOT="$(xcrun --sdk macosx --show-sdk-path)" \ @@ -155,9 +121,7 @@ export MACOSX_DEPLOYMENT_TARGET=14.0 cd .. -{ [ "$CI_HAS_ARTIFACTS" = "true" ]; -} && SHOULD_DEPLOY="true" || SHOULD_DEPLOY="false" - -if [ "$build_status" -eq 0 ] && [ "$SHOULD_DEPLOY" = "true" ]; then +# If it compiled succesfully let's deploy. +if [ "$build_status" -eq 0 ]; then .ci/deploy-mac-arm64.sh fi diff --git a/.ci/build-mac.sh b/.ci/build-mac.sh old mode 100644 new mode 100755 index 83ab07c1d9..202e2f3a86 --- a/.ci/build-mac.sh +++ b/.ci/build-mac.sh @@ -3,27 +3,26 @@ # shellcheck disable=SC2086 export HOMEBREW_NO_AUTO_UPDATE=1 export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 -brew unlink certifi -brew install -f --overwrite nasm ninja p7zip ccache pipenv #create-dmg +export HOMEBREW_NO_ENV_HINTS=1 +export HOMEBREW_NO_INSTALL_CLEANUP=1 -#/usr/sbin/softwareupdate --install-rosetta --agree-to-license +brew install -f --overwrite --quiet ccache pipenv "llvm@$LLVM_COMPILER_VER" +brew link -f --overwrite --quiet "llvm@$LLVM_COMPILER_VER" +# shellcheck disable=SC3009 +rm /usr/local/bin/{idle3.14,pip3.14,pydoc3.14,python3.14,python3.14-config} && \ +rm /usr/local/bin/{idle3,pip3,pydoc3,python3,python3-config} arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" -arch -x86_64 /usr/local/bin/brew update -arch -x86_64 /usr/local/bin/brew install -f --overwrite python || arch -x86_64 /usr/local/bin/brew link --overwrite python -arch -x86_64 /usr/local/bin/brew uninstall -f --ignore-dependencies ffmpeg -arch -x86_64 /usr/local/bin/brew install -f --build-from-source ffmpeg@5 -arch -x86_64 /usr/local/bin/brew reinstall -f --build-from-source gnutls freetype -arch -x86_64 /usr/local/bin/brew install llvm@$LLVM_COMPILER_VER glew cmake sdl3 vulkan-headers coreutils -arch -x86_64 /usr/local/bin/brew link -f llvm@$LLVM_COMPILER_VER ffmpeg@5 +arch -x86_64 /usr/local/bin/brew install -f --overwrite --quiet ffmpeg@5 "llvm@$LLVM_COMPILER_VER" glew sdl3 vulkan-headers +arch -x86_64 /usr/local/bin/brew link -f --overwrite --quiet "llvm@$LLVM_COMPILER_VER" ffmpeg@5 -# moltenvk based on commit for 1.3.0 release -wget https://raw.githubusercontent.com/Homebrew/homebrew-core/7255441cbcafabaa8950f67c7ec55ff499dbb2d3/Formula/m/molten-vk.rb -arch -x86_64 /usr/local/bin/brew install -f --overwrite ./molten-vk.rb +# moltenvk based on commit for 1.4.0 release +export HOMEBREW_DEVELOPER=1 # Prevents blocking of local formulae +wget https://raw.githubusercontent.com/Homebrew/homebrew-core/ea2bec5f1f4384e188d7fc0702ab21a20a2ced08/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 -export BREW_PATH; -BREW_PATH="$(brew --prefix)" export BREW_X64_PATH; BREW_X64_PATH="$("/usr/local/bin/brew" --prefix)" export BREW_BIN="/usr/local/bin" @@ -39,14 +38,16 @@ 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.0 URL workaround + # nested Qt 6.10.0 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 + sed -i '' "s/official_releases/archive/g" qt-downloader cd "/tmp/Qt" - "$BREW_X64_PATH/bin/pipenv" run pip3 install py7zr requests semantic_version lxml + "/opt/homebrew/bin/pipenv" --python "/opt/homebrew/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.0 workaround - "$BREW_X64_PATH/bin/pipenv" run "$WORKDIR/qt-downloader/qt-downloader" macos desktop "$QT_VER" clang_64 --opensource --addons qtmultimedia qtimageformats # -o "$QT_VER/clang_64" + # sed -i '' 's/args\.version \/ derive_toolchain_dir(args) \/ //g' "$WORKDIR/qt-downloader/qt-downloader" # Qt 6.10.0 workaround + "/opt/homebrew/bin/pipenv" --python "/opt/homebrew/bin/python3" run "$WORKDIR/qt-downloader/qt-downloader" macos desktop "$QT_VER" clang_64 --opensource --addons qtmultimedia qtimageformats # -o "$QT_VER/clang_64" fi cd "$WORKDIR" @@ -55,12 +56,13 @@ ditto "/tmp/Qt/$QT_VER" "qt-downloader/$QT_VER" export Qt6_DIR="$WORKDIR/qt-downloader/$QT_VER/clang_64/lib/cmake/Qt$QT_VER_MAIN" export SDL3_DIR="$BREW_X64_PATH/opt/sdl3/lib/cmake/SDL3" -export PATH="$BREW_X64_PATH/opt/llvm@$LLVM_COMPILER_VER/bin:$WORKDIR/qt-downloader/$QT_VER/clang_64/bin:$BREW_BIN:$BREW_SBIN:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/Library/Apple/usr/bin:$PATH" -export LDFLAGS="-L$BREW_X64_PATH/lib -Wl,-rpath,$BREW_X64_PATH/lib" +export PATH="/opt/homebrew/opt/llvm@$LLVM_COMPILER_VER/bin:$WORKDIR/qt-downloader/$QT_VER/clang_64/bin:$BREW_BIN:$BREW_SBIN:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/Library/Apple/usr/bin:$PATH" +# shellcheck disable=SC2155 +export LDFLAGS="-L$BREW_X64_PATH/lib -Wl,-rpath,$BREW_X64_PATH/lib,-L$(brew --prefix llvm)/lib/c++" export CPPFLAGS="-I$BREW_X64_PATH/include -msse -msse2 -mcx16 -no-pie -D__MAC_OS_X_VERSION_MIN_REQUIRED=140000" export CFLAGS="-D__MAC_OS_X_VERSION_MIN_REQUIRED=140000" -export LIBRARY_PATH="$BREW_X64_PATH/lib" -export LD_LIBRARY_PATH="$BREW_X64_PATH/lib" +export LIBRARY_PATH="$BREW_X64_PATH/opt/llvm@$LLVM_COMPILER_VER/lib:$BREW_X64_PATH/lib" +export LD_LIBRARY_PATH="$BREW_X64_PATH/opt/llvm@$LLVM_COMPILER_VER/lib:$BREW_X64_PATH/lib" export VULKAN_SDK VULKAN_SDK="$BREW_X64_PATH/opt/molten-vk" @@ -68,10 +70,10 @@ ln -s "$VULKAN_SDK/lib/libMoltenVK.dylib" "$VULKAN_SDK/lib/libvulkan.dylib" export VK_ICD_FILENAMES="$VULKAN_SDK/share/vulkan/icd.d/MoltenVK_icd.json" export LLVM_DIR -LLVM_DIR="BREW_X64_PATH/opt/llvm@$LLVM_COMPILER_VER" +LLVM_DIR="$BREW_X64_PATH/opt/llvm@$LLVM_COMPILER_VER" # exclude ffmpeg, LLVM, opencv, and sdl from submodule update # shellcheck disable=SC2046 -git submodule -q update --init --depth=1 --jobs=8 $(awk '/path/ && !/ffmpeg/ && !/llvm/ && !/opencv/ && !/SDL/ { print $3 }' .gitmodules) +git submodule -q update --init --depth=1 --jobs=8 $(awk '/path/ && !/ffmpeg/ && !/llvm/ && !/opencv/ && !/SDL/ && !/feralinteractive/ { print $3 }' .gitmodules) # 3rdparty fixes sed -i '' "s/extern const double NSAppKitVersionNumber;/const double NSAppKitVersionNumber = 1343;/g" 3rdparty/hidapi/hidapi/mac/hid.c @@ -80,7 +82,7 @@ mkdir build && cd build || exit 1 export MACOSX_DEPLOYMENT_TARGET=14.0 -"$BREW_X64_PATH/bin/cmake" .. \ +"/opt/homebrew/bin/cmake" .. \ -DBUILD_RPCS3_TESTS=OFF \ -DRUN_RPCS3_TESTS=OFF \ -DUSE_SDL=ON \ @@ -105,22 +107,23 @@ export MACOSX_DEPLOYMENT_TARGET=14.0 -DUSE_SYSTEM_FAUDIO=OFF \ -DUSE_SYSTEM_SDL=ON \ -DUSE_SYSTEM_OPENCV=ON \ - $CMAKE_EXTRA_OPTS \ + "$CMAKE_EXTRA_OPTS" \ -DLLVM_TARGET_ARCH=X86_64 \ -DCMAKE_OSX_ARCHITECTURES=x86_64 \ - -DCMAKE_IGNORE_PATH="$BREW_PATH/lib" \ + -DCMAKE_SYSTEM_PROCESSOR=x86_64 \ + -DCMAKE_TOOLCHAIN_FILE=buildfiles/cmake/TCDarwinX86_64.cmake \ + -DCMAKE_IGNORE_PATH="$BREW_X64_PATH/lib" \ + -DCMAKE_IGNORE_PREFIX_PATH=/usr/local/opt \ -DCMAKE_CXX_FLAGS="-D__MAC_OS_X_VERSION_MIN_REQUIRED=140000" \ -DCMAKE_POLICY_VERSION_MINIMUM=3.5 \ -DCMAKE_OSX_SYSROOT="$(xcrun --sdk macosx --show-sdk-path)" \ -G Ninja -"$BREW_PATH/bin/ninja"; build_status=$?; +"/opt/homebrew/bin/ninja"; build_status=$?; cd .. -{ [ "$CI_HAS_ARTIFACTS" = "true" ]; -} && SHOULD_DEPLOY="true" || SHOULD_DEPLOY="false" - -if [ "$build_status" -eq 0 ] && [ "$SHOULD_DEPLOY" = "true" ]; then +# If it compiled succesfully let's deploy. +if [ "$build_status" -eq 0 ]; then .ci/deploy-mac.sh fi diff --git a/.ci/build-windows-clang.sh b/.ci/build-windows-clang.sh new file mode 100644 index 0000000000..0880e7f5ed --- /dev/null +++ b/.ci/build-windows-clang.sh @@ -0,0 +1,61 @@ +#!/bin/sh -ex + +git config --global --add safe.directory '*' + +# Pull all the submodules except some +# Note: Tried to use git submodule status, but it takes over 20 seconds +# shellcheck disable=SC2046 +git submodule -q update --init $(awk '/path/ && !/llvm/ && !/opencv/ && !/ffmpeg/ && !/curl/ && !/FAudio/ && !/zlib/ { print $3 }' .gitmodules) + +mkdir build && cd build || exit 1 + +export CC="clang" +export CXX="clang++" +export LINKER=lld +export LINKER_FLAG="-fuse-ld=${LINKER}" + +if [ -n "$LLVMVER" ]; then + export AR="llvm-ar-$LLVMVER" + export RANLIB="llvm-ranlib-$LLVMVER" +else + export AR="llvm-ar" + export RANLIB="llvm-ranlib" +fi + +cmake .. \ + -DCMAKE_PREFIX_PATH=/clang64 \ + -DCMAKE_INSTALL_PREFIX=/usr \ + -DUSE_NATIVE_INSTRUCTIONS=OFF \ + -DUSE_PRECOMPILED_HEADERS=OFF \ + -DCMAKE_C_FLAGS="$CFLAGS" \ + -DCMAKE_CXX_FLAGS="$CFLAGS" \ + -DCMAKE_EXE_LINKER_FLAGS="${LINKER_FLAG}" \ + -DCMAKE_MODULE_LINKER_FLAGS="${LINKER_FLAG}" \ + -DCMAKE_SHARED_LINKER_FLAGS="${LINKER_FLAG}" \ + -DCMAKE_AR="$AR" \ + -DCMAKE_RANLIB="$RANLIB" \ + -DUSE_SYSTEM_CURL=ON \ + -DUSE_FAUDIO=OFF \ + -DUSE_SDL=ON \ + -DUSE_SYSTEM_SDL=OFF \ + -DUSE_SYSTEM_FFMPEG=ON \ + -DUSE_SYSTEM_OPENCV=ON \ + -DUSE_SYSTEM_OPENAL=OFF \ + -DUSE_DISCORD_RPC=ON \ + -DOpenGL_GL_PREFERENCE=LEGACY \ + -DWITH_LLVM=ON \ + -DLLVM_DIR=/clang64/lib/cmake/llvm \ + -DVulkan_LIBRARY=/clang64/lib/libvulkan-1.dll.a \ + -DSTATIC_LINK_LLVM=ON \ + -DBUILD_RPCS3_TESTS=OFF \ + -DRUN_RPCS3_TESTS=OFF \ + -G Ninja + +ninja; build_status=$?; + +cd .. + +# If it compiled succesfully let's deploy. +if [ "$build_status" -eq 0 ]; then + .ci/deploy-windows-clang.sh "x86_64" +fi diff --git a/.ci/deploy-linux.sh b/.ci/deploy-linux.sh index 5f0fe2ef8c..b0599400d2 100755 --- a/.ci/deploy-linux.sh +++ b/.ci/deploy-linux.sh @@ -14,7 +14,7 @@ if [ "$DEPLOY_APPIMAGE" = "true" ]; then curl -fsSLo linuxdeploy-plugin-checkrt.sh https://github.com/darealshinji/linuxdeploy-plugin-checkrt/releases/download/continuous/linuxdeploy-plugin-checkrt.sh chmod +x ./linuxdeploy-plugin-checkrt.sh - export EXTRA_PLATFORM_PLUGINS="libqwayland-egl.so;libqwayland-generic.so" + export EXTRA_PLATFORM_PLUGINS="libqwayland.so" export EXTRA_QT_PLUGINS="svg;wayland-decoration-client;wayland-graphics-integration-client;wayland-shell-integration;waylandcompositor" APPIMAGE_EXTRACT_AND_RUN=1 linuxdeploy --appdir AppDir --plugin qt --plugin checkrt @@ -26,15 +26,31 @@ if [ "$DEPLOY_APPIMAGE" = "true" ]; then rm -f ./AppDir/usr/lib/libvulkan.so* # Remove unused Qt6 libraries - rm -f ./AppDir/usr/lib/libQt6OpenGL.so* - rm -f ./AppDir/usr/lib/libQt6Qml*.so* - rm -f ./AppDir/usr/lib/libQt6Quick.so* rm -f ./AppDir/usr/lib/libQt6VirtualKeyboard.so* rm -f ./AppDir/usr/plugins/platforminputcontexts/libqtvirtualkeyboardplugin.so* # Remove git directory containing local commit history file rm -rf ./AppDir/usr/share/rpcs3/git + # Download translations + mkdir -p "./AppDir/usr/translations" + ZIP_URL=$(curl -fsSL "https://api.github.com/repos/RPCS3/rpcs3_translations/releases/latest" \ + | grep "browser_download_url" \ + | grep "RPCS3-languages.zip" \ + | cut -d '"' -f 4) + if [ -z "$ZIP_URL" ]; then + echo "Failed to find RPCS3-languages.zip in the latest release. Continuing without translations." + else + echo "Downloading translations from: $ZIP_URL" + curl -L -o translations.zip "$ZIP_URL" || { + echo "Failed to download translations.zip. Continuing without translations." + exit 0 + } + unzip -o translations.zip -d "./AppDir/usr/translations" >/dev/null 2>&1 || \ + echo "Failed to extract translations.zip. Continuing without translations." + rm -f translations.zip + fi + curl -fsSLo /uruntime "https://github.com/VHSgunzo/uruntime/releases/download/v0.3.4/uruntime-appimage-dwarfs-$CPU_ARCH" chmod +x /uruntime /uruntime --appimage-mkdwarfs -f --set-owner 0 --set-group 0 --no-history --no-create-timestamp \ diff --git a/.ci/deploy-llvm.sh b/.ci/deploy-llvm.sh new file mode 100644 index 0000000000..35e5d780af --- /dev/null +++ b/.ci/deploy-llvm.sh @@ -0,0 +1,20 @@ +#!/bin/sh -ex + +# First let's print some info about our caches +"$(cygpath -u "$CCACHE_BIN_DIR")"/ccache.exe --show-stats -v + +# BUILD_blablabla is Azure specific, so we wrap it for portability +ARTIFACT_DIR="$BUILD_ARTIFACTSTAGINGDIRECTORY" +BUILD="llvmlibs_mt.7z" + +# Package artifacts +7z a -m0=LZMA2 -mx9 "$BUILD" ./build/lib/Release-x64/llvm_build + +# Generate sha256 hashes +# Write to file for GitHub releases +sha256sum "$BUILD" | awk '{ print $1 }' | tee "$BUILD.sha256" +echo "$(cat "$BUILD.sha256");$(stat -c %s "$BUILD")B" > GitHubReleaseMessage.txt + +# Move files to publishing directory +cp -- "$BUILD" "$ARTIFACT_DIR" +cp -- "$BUILD.sha256" "$ARTIFACT_DIR" diff --git a/.ci/deploy-mac-arm64.sh b/.ci/deploy-mac-arm64.sh old mode 100644 new mode 100755 index da1fbe9165..6e067bde9f --- a/.ci/deploy-mac-arm64.sh +++ b/.ci/deploy-mac-arm64.sh @@ -16,9 +16,9 @@ echo "AVVER=$AVVER" >> ../.ci/ci-vars.env cd bin mkdir "rpcs3.app/Contents/lib/" || true -cp "$(realpath /opt/homebrew1/opt/llvm@$LLVM_COMPILER_VER/lib/c++/libc++abi.1.0.dylib)" "rpcs3.app/Contents/Frameworks/libc++abi.1.dylib" -cp "$(realpath /opt/homebrew1/lib/libsharpyuv.0.dylib)" "rpcs3.app/Contents/lib/libsharpyuv.0.dylib" -cp "$(realpath /opt/homebrew1/lib/libintl.8.dylib)" "rpcs3.app/Contents/lib/libintl.8.dylib" +cp "$(realpath /opt/homebrew/opt/llvm@$LLVM_COMPILER_VER/lib/c++/libc++abi.1.0.dylib)" "rpcs3.app/Contents/Frameworks/libc++abi.1.dylib" +cp "$(realpath /opt/homebrew/lib/libsharpyuv.0.dylib)" "rpcs3.app/Contents/lib/libsharpyuv.0.dylib" +cp "$(realpath /opt/homebrew/lib/libintl.8.dylib)" "rpcs3.app/Contents/lib/libintl.8.dylib" rm -rf "rpcs3.app/Contents/Frameworks/QtPdf.framework" \ "rpcs3.app/Contents/Frameworks/QtQml.framework" \ @@ -31,13 +31,28 @@ rm -rf "rpcs3.app/Contents/Frameworks/QtPdf.framework" \ ../../.ci/optimize-mac.sh rpcs3.app +# Download translations +mkdir -p "rpcs3.app/Contents/translations" +ZIP_URL=$(curl -fsSL "https://api.github.com/repos/RPCS3/rpcs3_translations/releases/latest" \ + | grep "browser_download_url" \ + | grep "RPCS3-languages.zip" \ + | cut -d '"' -f 4) +if [ -z "$ZIP_URL" ]; then + echo "Failed to find RPCS3-languages.zip in the latest release. Continuing without translations." +else + echo "Downloading translations from: $ZIP_URL" + curl -L -o translations.zip "$ZIP_URL" || { + echo "Failed to download translations.zip. Continuing without translations." + exit 0 + } + unzip -o translations.zip -d "rpcs3.app/Contents/translations" >/dev/null 2>&1 || \ + echo "Failed to extract translations.zip. Continuing without translations." + rm -f translations.zip +fi + # Hack -install_name_tool \ --delete_rpath /opt/homebrew1/lib \ --delete_rpath /opt/homebrew/lib \ --delete_rpath /opt/homebrew1/opt/llvm@$LLVM_COMPILER_VER/lib \ --delete_rpath /usr/local/lib RPCS3.app/Contents/MacOS/rpcs3 -#-delete_rpath /opt/homebrew1/Cellar/sdl3/3.2.8/lib +install_name_tool -delete_rpath /opt/homebrew/lib RPCS3.app/Contents/MacOS/rpcs3 || echo "Hack for deleting rpath /opt/homebrew/lib not needed" +install_name_tool -delete_rpath /opt/homebrew/opt/llvm@$LLVM_COMPILER_VER/lib RPCS3.app/Contents/MacOS/rpcs3 || echo "Hack for deleting rpath /opt/homebrew/opt/llvm@$LLVM_COMPILER_VER/lib not needed" # Need to do this rename hack due to case insensitive filesystem mv rpcs3.app RPCS3_.app @@ -50,24 +65,8 @@ echo "[InternetShortcut]" > Quickstart.url echo "URL=https://rpcs3.net/quickstart" >> Quickstart.url echo "IconIndex=0" >> Quickstart.url -#DMG_FILEPATH="$BUILD_ARTIFACTSTAGINGDIRECTORY/rpcs3-v${COMM_TAG}-${COMM_COUNT}-${COMM_HASH}_macos_arm64.dmg" -#"$BREW_X64_PATH/bin/create-dmg" --volname RPCS3 \ -#--window-size 800 400 \ -#--icon-size 100 \ -#--icon rpcs3.app 200 190 \ -#--add-file Quickstart.url Quickstart.url 400 20 \ -#--hide-extension rpcs3.app \ -#--hide-extension Quickstart.url \ -#--app-drop-link 600 185 \ -#--skip-jenkins \ -#--format ULMO \ -#"$DMG_FILEPATH" \ -#RPCS3.app -#FILESIZE=$(stat -f %z "$DMG_FILEPATH") -#SHA256SUM=$(shasum -a 256 "$DMG_FILEPATH" | awk '{ print $1 }') - ARCHIVE_FILEPATH="$BUILD_ARTIFACTSTAGINGDIRECTORY/rpcs3-v${COMM_TAG}-${COMM_COUNT}-${COMM_HASH}_macos_arm64.7z" -"$BREW_X64_PATH/bin/7z" a -mx9 "$ARCHIVE_FILEPATH" RPCS3.app Quickstart.url +"/opt/homebrew/bin/7z" a -mx9 "$ARCHIVE_FILEPATH" RPCS3.app Quickstart.url FILESIZE=$(stat -f %z "$ARCHIVE_FILEPATH") SHA256SUM=$(shasum -a 256 "$ARCHIVE_FILEPATH" | awk '{ print $1 }') diff --git a/.ci/deploy-mac.sh b/.ci/deploy-mac.sh old mode 100644 new mode 100755 index c293358748..4b54267539 --- a/.ci/deploy-mac.sh +++ b/.ci/deploy-mac.sh @@ -16,7 +16,8 @@ echo "AVVER=$AVVER" >> ../.ci/ci-vars.env cd bin mkdir "rpcs3.app/Contents/lib/" -cp "/usr/local/opt/llvm@$LLVM_COMPILER_VER/lib/c++/libc++abi.1.0.dylib" "rpcs3.app/Contents/lib/libc++abi.1.dylib" +cp "/usr/local/opt/llvm@$LLVM_COMPILER_VER/lib/c++/libc++abi.1.0.dylib" "rpcs3.app/Contents/Frameworks/libc++abi.1.dylib" +cp "/usr/local/opt/llvm@$LLVM_COMPILER_VER/lib/unwind/libunwind.1.dylib" "rpcs3.app/Contents/Frameworks/libunwind.1.dylib" cp "$(realpath /usr/local/lib/libsharpyuv.0.dylib)" "rpcs3.app/Contents/lib/libsharpyuv.0.dylib" cp "$(realpath /usr/local/lib/libintl.8.dylib)" "rpcs3.app/Contents/lib/libintl.8.dylib" @@ -31,14 +32,31 @@ rm -rf "rpcs3.app/Contents/Frameworks/QtPdf.framework" \ ../../.ci/optimize-mac.sh rpcs3.app +# Download translations +mkdir -p "rpcs3.app/Contents/translations" +ZIP_URL=$(curl -fsSL "https://api.github.com/repos/RPCS3/rpcs3_translations/releases/latest" \ + | grep "browser_download_url" \ + | grep "RPCS3-languages.zip" \ + | cut -d '"' -f 4) +if [ -z "$ZIP_URL" ]; then + echo "Failed to find RPCS3-languages.zip in the latest release. Continuing without translations." +else + echo "Downloading translations from: $ZIP_URL" + curl -L -o translations.zip "$ZIP_URL" || { + echo "Failed to download translations.zip. Continuing without translations." + exit 0 + } + unzip -o translations.zip -d "rpcs3.app/Contents/translations" >/dev/null 2>&1 || \ + echo "Failed to extract translations.zip. Continuing without translations." + rm -f translations.zip +fi + # Need to do this rename hack due to case insensitive filesystem mv rpcs3.app RPCS3_.app mv RPCS3_.app RPCS3.app # Hack -install_name_tool \ --delete_rpath /usr/local/lib \ --delete_rpath /usr/local/opt/llvm@$LLVM_COMPILER_VER/lib RPCS3.app/Contents/MacOS/rpcs3 +install_name_tool -delete_rpath /usr/local/lib RPCS3.app/Contents/MacOS/rpcs3 #-delete_rpath /usr/local/Cellar/sdl3/3.2.8/lib # NOTE: "--deep" is deprecated @@ -48,24 +66,8 @@ echo "[InternetShortcut]" > Quickstart.url echo "URL=https://rpcs3.net/quickstart" >> Quickstart.url echo "IconIndex=0" >> Quickstart.url -#DMG_FILEPATH="$BUILD_ARTIFACTSTAGINGDIRECTORY/rpcs3-v${COMM_TAG}-${COMM_COUNT}-${COMM_HASH}_macos.dmg" -#"$BREW_X64_PATH/bin/create-dmg" --volname RPCS3 \ -#--window-size 800 400 \ -#--icon-size 100 \ -#--icon rpcs3.app 200 190 \ -#--add-file Quickstart.url Quickstart.url 400 20 \ -#--hide-extension rpcs3.app \ -#--hide-extension Quickstart.url \ -#--app-drop-link 600 185 \ -#--skip-jenkins \ -#--format ULMO \ -#"$DMG_FILEPATH" \ -#RPCS3.app -#FILESIZE=$(stat -f %z "$DMG_FILEPATH") -#SHA256SUM=$(shasum -a 256 "$DMG_FILEPATH" | awk '{ print $1 }') - ARCHIVE_FILEPATH="$BUILD_ARTIFACTSTAGINGDIRECTORY/rpcs3-v${COMM_TAG}-${COMM_COUNT}-${COMM_HASH}_macos.7z" -"$BREW_X64_PATH/bin/7z" a -mx9 "$ARCHIVE_FILEPATH" RPCS3.app Quickstart.url +"/opt/homebrew/bin/7z" a -mx9 "$ARCHIVE_FILEPATH" RPCS3.app Quickstart.url FILESIZE=$(stat -f %z "$ARCHIVE_FILEPATH") SHA256SUM=$(shasum -a 256 "$ARCHIVE_FILEPATH" | awk '{ print $1 }') diff --git a/.ci/deploy-windows-clang.sh b/.ci/deploy-windows-clang.sh new file mode 100644 index 0000000000..c95f82e7b8 --- /dev/null +++ b/.ci/deploy-windows-clang.sh @@ -0,0 +1,57 @@ +#!/bin/sh -ex + +# source ci-vars.env +# shellcheck disable=SC1091 +. .ci/ci-vars.env + +cd build || exit 1 + +CPU_ARCH="${1:-x86_64}" + +echo "Deploying rpcs3 windows clang $CPU_ARCH" + +# BUILD_blablabla is CI specific, so we wrap it for portability +ARTIFACT_DIR=$(cygpath -u "$BUILD_ARTIFACTSTAGINGDIRECTORY") +MSYS2_CLANG_BIN=$(cygpath -w /clang64/bin) +MSYS2_USR_BIN=$(cygpath -w /usr/bin) + +echo "Installing dependencies of: ./bin/rpcs3.exe (MSYS2 dir is '$MSYS2_CLANG_BIN', usr dir is '$MSYS2_USR_BIN')" +cmake -DMSYS2_CLANG_BIN="$MSYS2_CLANG_BIN" -DMSYS2_USR_BIN="$MSYS2_USR_BIN" -Dexe=./bin/rpcs3.exe -P ../buildfiles/cmake/CopyRuntimeDependencies.cmake + +# Prepare compatibility and SDL database for packaging +mkdir ./bin/config +mkdir ./bin/config/input_configs +curl -fsSL 'https://raw.githubusercontent.com/gabomdq/SDL_GameControllerDB/master/gamecontrollerdb.txt' 1> ./bin/config/input_configs/gamecontrollerdb.txt +curl -fsSL 'https://rpcs3.net/compatibility?api=v1&export' | iconv -t UTF-8 1> ./bin/GuiConfigs/compat_database.dat + +# Download translations +mkdir -p ./bin/share/qt6/translations +ZIP_URL=$(curl -fsSL "https://api.github.com/repos/RPCS3/rpcs3_translations/releases/latest" \ + | grep "browser_download_url" \ + | grep "RPCS3-languages.zip" \ + | cut -d '"' -f 4) +if [ -z "$ZIP_URL" ]; then + echo "Failed to find RPCS3-languages.zip in the latest release. Continuing without translations." +else + echo "Downloading translations from: $ZIP_URL" + curl -L -o translations.zip "$ZIP_URL" || { + echo "Failed to download translations.zip. Continuing without translations." + exit 0 + } + unzip -o translations.zip -d "./bin/share/qt6/translations" >/dev/null 2>&1 || \ + echo "Failed to extract translations.zip. Continuing without translations." + rm -f translations.zip +fi + +# Package artifacts +7z a -m0=LZMA2 -mx9 "$BUILD" ./bin/* + +# Generate sha256 hashes +# Write to file for GitHub releases +sha256sum "$BUILD" | awk '{ print $1 }' | tee "$BUILD.sha256" +echo "$(cat "$BUILD.sha256");$(stat -c %s "$BUILD")B" > GitHubReleaseMessage.txt + +# Move files to publishing directory +mkdir -p "$ARTIFACT_DIR" +cp -- "$BUILD" "$ARTIFACT_DIR" +cp -- "$BUILD.sha256" "$ARTIFACT_DIR" diff --git a/.ci/deploy-windows.sh b/.ci/deploy-windows.sh index bb1212f586..069f8fb637 100755 --- a/.ci/deploy-windows.sh +++ b/.ci/deploy-windows.sh @@ -1,9 +1,9 @@ #!/bin/sh -ex -# First let's see print some info about our caches +# First let's print some info about our caches "$(cygpath -u "$CCACHE_BIN_DIR")"/ccache.exe --show-stats -v -# BUILD_blablabla is Azure specific, so we wrap it for portability +# BUILD_blablabla is CI specific, so we wrap it for portability ARTIFACT_DIR="$BUILD_ARTIFACTSTAGINGDIRECTORY" # Remove unecessary files @@ -15,6 +15,25 @@ mkdir ./bin/config/input_configs curl -fsSL 'https://raw.githubusercontent.com/gabomdq/SDL_GameControllerDB/master/gamecontrollerdb.txt' 1> ./bin/config/input_configs/gamecontrollerdb.txt curl -fsSL 'https://rpcs3.net/compatibility?api=v1&export' | iconv -t UTF-8 1> ./bin/GuiConfigs/compat_database.dat +# Download translations +mkdir -p ./bin/qt6/translations +ZIP_URL=$(curl -fsSL "https://api.github.com/repos/RPCS3/rpcs3_translations/releases/latest" \ + | grep "browser_download_url" \ + | grep "RPCS3-languages.zip" \ + | cut -d '"' -f 4) +if [ -z "$ZIP_URL" ]; then + echo "Failed to find RPCS3-languages.zip in the latest release. Continuing without translations." +else + echo "Downloading translations from: $ZIP_URL" + curl -L -o translations.zip "$ZIP_URL" || { + echo "Failed to download translations.zip. Continuing without translations." + exit 0 + } + unzip -o translations.zip -d "./bin/qt6/translations" >/dev/null 2>&1 || \ + echo "Failed to extract translations.zip. Continuing without translations." + rm -f translations.zip +fi + # Download SSL certificate (not needed with CURLSSLOPT_NATIVE_CA) #curl -fsSL 'https://curl.haxx.se/ca/cacert.pem' 1> ./bin/cacert.pem diff --git a/.ci/docker.env b/.ci/docker.env index ee037bfb4d..eb70b68c18 100644 --- a/.ci/docker.env +++ b/.ci/docker.env @@ -1,5 +1,4 @@ -# Variables set by Azure Pipelines -CI_HAS_ARTIFACTS +# Variables set by CI BUILD_REASON BUILD_SOURCEVERSION BUILD_ARTIFACTSTAGINGDIRECTORY diff --git a/.ci/export-azure-vars.sh b/.ci/export-azure-vars.sh deleted file mode 100755 index 033dd41cc8..0000000000 --- a/.ci/export-azure-vars.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh -e - -# Export variables for later stages of the Azure pipeline -# Values done in this manner will appear as environment variables -# in later stages. - -# From pure-sh-bible -# Setting 'IFS' tells 'read' where to split the string. -while IFS='=' read -r key val; do - # Skip over lines containing comments. - [ "${key##\#*}" ] || continue - echo "##vso[task.setvariable variable=$key]$val" -done < ".ci/ci-vars.env" diff --git a/.ci/export-cirrus-vars.sh b/.ci/export-cirrus-vars.sh deleted file mode 100644 index 561e77e92f..0000000000 --- a/.ci/export-cirrus-vars.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh -e - -# Export variables for later stages of the Cirrus pipeline -# Values done in this manner will appear as environment variables -# in later stages. - -# From pure-sh-bible -# Setting 'IFS' tells 'read' where to split the string. -while IFS='=' read -r key val; do - # Skip over lines containing comments. - [ "${key##\#*}" ] || continue - export "$key"="$val" -done < ".ci/ci-vars.env" diff --git a/.ci/get_keys-windows.sh b/.ci/get_keys-windows.sh old mode 100644 new mode 100755 diff --git a/.ci/install-freebsd.sh b/.ci/install-freebsd.sh index de10561dfa..04efc6c0e9 100755 --- a/.ci/install-freebsd.sh +++ b/.ci/install-freebsd.sh @@ -15,4 +15,4 @@ pkg install "llvm$LLVM_COMPILER_VER" pkg install git ccache cmake ninja "qt$QT_VER_MAIN-multimedia" "qt$QT_VER_MAIN-svg" glew openal-soft ffmpeg # Optional dependencies (libevdev is pulled by qtX-base) -pkg install pkgconf alsa-lib pulseaudio sdl3 evdev-proto vulkan-headers vulkan-loader +pkg install pkgconf alsa-lib pulseaudio sdl3 evdev-proto vulkan-headers vulkan-loader opencv diff --git a/.ci/optimize-mac.sh b/.ci/optimize-mac.sh old mode 100644 new mode 100755 diff --git a/.ci/setup-llvm.sh b/.ci/setup-llvm.sh new file mode 100644 index 0000000000..a54901309e --- /dev/null +++ b/.ci/setup-llvm.sh @@ -0,0 +1,63 @@ +#!/bin/sh -ex + +# Resource/dependency URLs +CCACHE_URL="https://github.com/ccache/ccache/releases/download/v4.11.2/ccache-4.11.2-windows-x86_64.zip" + +DEP_URLS=" \ + $CCACHE_URL" + +# CI doesn't make a cache dir if it doesn't exist, so we do it manually +[ -d "$DEPS_CACHE_DIR" ] || mkdir "$DEPS_CACHE_DIR" + +# Pull the llvm submodule +# shellcheck disable=SC2046 +git submodule -q update --init --depth=1 -- 3rdparty/llvm + +# Git bash doesn't have rev, so here it is +rev() +{ + echo "$1" | awk '{ for(i = length($0); i != 0; --i) { a = a substr($0, i, 1); } } END { print a }' +} + +# Usage: download_and_verify url checksum algo file +# Check to see if a file is already cached, and the checksum matches. If not, download it. +# Tries up to 3 times +download_and_verify() +{ + url="$1" + correctChecksum="$2" + algo="$3" + fileName="$4" + + for _ in 1 2 3; do + [ -e "$DEPS_CACHE_DIR/$fileName" ] || curl -fLo "$DEPS_CACHE_DIR/$fileName" "$url" + fileChecksum=$("${algo}sum" "$DEPS_CACHE_DIR/$fileName" | awk '{ print $1 }') + [ "$fileChecksum" = "$correctChecksum" ] && return 0 + done + + return 1; +} + +# Some dependencies install here +[ -d "./build/lib_ext/Release-x64" ] || mkdir -p "./build/lib_ext/Release-x64" + +for url in $DEP_URLS; do + # Get the filename from the URL and remove query strings (?arg=something). + fileName="$(rev "$(rev "$url" | cut -d'/' -f1)" | cut -d'?' -f1)" + [ -z "$fileName" ] && echo "Unable to parse url: $url" && exit 1 + + # shellcheck disable=SC1003 + case "$url" in + *ccache*) checksum=$CCACHE_SHA; algo="sha256"; outDir="$CCACHE_BIN_DIR" ;; + *) echo "Unknown url resource: $url"; exit 1 ;; + esac + + download_and_verify "$url" "$checksum" "$algo" "$fileName" + 7z x -y "$DEPS_CACHE_DIR/$fileName" -aos -o"$outDir" +done + +# Setup ccache tool +[ -d "$CCACHE_DIR" ] || mkdir -p "$(cygpath -u "$CCACHE_DIR")" +CCACHE_SH_DIR=$(cygpath -u "$CCACHE_BIN_DIR") +mv "$CCACHE_SH_DIR"/ccache-*/* "$CCACHE_SH_DIR" +cp "$CCACHE_SH_DIR"/ccache.exe "$CCACHE_SH_DIR"/cl.exe diff --git a/.ci/setup-windows-ci-vars.sh b/.ci/setup-windows-ci-vars.sh new file mode 100644 index 0000000000..11373e0716 --- /dev/null +++ b/.ci/setup-windows-ci-vars.sh @@ -0,0 +1,39 @@ +#!/bin/sh -ex + +CPU_ARCH="${1:-win64}" +COMPILER="${2:-msvc}" + +# These are CI specific, so we wrap them for portability +REPO_NAME="$BUILD_REPOSITORY_NAME" +REPO_BRANCH="$BUILD_SOURCEBRANCHNAME" +PR_NUMBER="$BUILD_PR_NUMBER" + +# Gather explicit version number and number of commits +COMM_TAG=$(awk '/version{.*}/ { printf("%d.%d.%d", $5, $6, $7) }' ./rpcs3/rpcs3_version.cpp) +COMM_COUNT=$(git rev-list --count HEAD) +COMM_HASH=$(git rev-parse --short=8 HEAD) + +# Format the above into filenames +if [ -n "$PR_NUMBER" ]; then + AVVER="${COMM_TAG}-${COMM_HASH}" + BUILD_RAW="rpcs3-v${AVVER}_${CPU_ARCH}_${COMPILER}" + BUILD="${BUILD_RAW}.7z" +else + AVVER="${COMM_TAG}-${COMM_COUNT}" + BUILD_RAW="rpcs3-v${AVVER}-${COMM_HASH}_${CPU_ARCH}_${COMPILER}" + BUILD="${BUILD_RAW}.7z" +fi + +# BRANCH is used for experimental build warnings for pr builds, used in main_window.cpp. +# BUILD is the name of the release artifact +# BUILD_RAW is just filename +# AVVER is used for GitHub releases, it is the version number. +BRANCH="${REPO_NAME}/${REPO_BRANCH}" + +# SC2129 +{ + echo "BRANCH=$BRANCH" + echo "BUILD=$BUILD" + echo "BUILD_RAW=$BUILD_RAW" + echo "AVVER=$AVVER" +} >> .ci/ci-vars.env diff --git a/.ci/setup-windows.sh b/.ci/setup-windows.sh index 8b6aaa6156..f637cec9ad 100755 --- a/.ci/setup-windows.sh +++ b/.ci/setup-windows.sh @@ -1,10 +1,5 @@ #!/bin/sh -ex -# These are Azure specific, so we wrap them for portability -REPO_NAME="$BUILD_REPOSITORY_NAME" -REPO_BRANCH="$SYSTEM_PULLREQUEST_SOURCEBRANCH" -PR_NUMBER="$SYSTEM_PULLREQUEST_PULLREQUESTID" - # Resource/dependency URLs # Qt mirrors can be volatile and slow, so we list 2 #QT_HOST="http://mirrors.ocf.berkeley.edu/qt/" @@ -13,7 +8,7 @@ QT_URL_VER=$(echo "$QT_VER" | sed "s/\.//g") QT_VER_MSVC_UP=$(echo "${QT_VER_MSVC}" | tr '[:lower:]' '[:upper:]') QT_PREFIX="online/qtsdkrepository/windows_x86/desktop/qt${QT_VER_MAIN}_${QT_URL_VER}/qt${QT_VER_MAIN}_${QT_URL_VER}/qt.qt${QT_VER_MAIN}.${QT_URL_VER}." QT_PREFIX_2="win64_${QT_VER_MSVC}_64/${QT_VER}-0-${QT_DATE}" -QT_SUFFIX="-Windows-Windows_11_23H2-${QT_VER_MSVC_UP}-Windows-Windows_11_23H2-X86_64.7z" +QT_SUFFIX="-Windows-Windows_11_24H2-${QT_VER_MSVC_UP}-Windows-Windows_11_24H2-X86_64.7z" QT_BASE_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qtbase${QT_SUFFIX}" QT_DECL_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qtdeclarative${QT_SUFFIX}" QT_TOOL_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qttools${QT_SUFFIX}" @@ -33,13 +28,13 @@ DEP_URLS=" \ $VULKAN_SDK_URL\ $CCACHE_URL" -# Azure pipelines doesn't make a cache dir if it doesn't exist, so we do it manually +# CI doesn't make a cache dir if it doesn't exist, so we do it manually [ -d "$DEPS_CACHE_DIR" ] || mkdir "$DEPS_CACHE_DIR" # Pull all the submodules except llvm, since it is built separately and we just download that build # Note: Tried to use git submodule status, but it takes over 20 seconds # shellcheck disable=SC2046 -git submodule -q update --init --depth=1 --jobs=8 $(awk '/path/ && !/FAudio/ && !/llvm/ { print $3 }' .gitmodules) +git submodule -q update --init --depth=1 --jobs=8 $(awk '/path/ && !/FAudio/ && !/llvm/ && !/feralinteractive/ { print $3 }' .gitmodules) # Git bash doesn't have rev, so here it is rev() @@ -99,33 +94,3 @@ done CCACHE_SH_DIR=$(cygpath -u "$CCACHE_BIN_DIR") mv "$CCACHE_SH_DIR"/ccache-*/* "$CCACHE_SH_DIR" cp "$CCACHE_SH_DIR"/ccache.exe "$CCACHE_SH_DIR"/cl.exe - -# Gather explicit version number and number of commits -COMM_TAG=$(awk '/version{.*}/ { printf("%d.%d.%d", $5, $6, $7) }' ./rpcs3/rpcs3_version.cpp) -COMM_COUNT=$(git rev-list --count HEAD) -COMM_HASH=$(git rev-parse --short=8 HEAD) - -# Format the above into filenames -if [ -n "$PR_NUMBER" ]; then - AVVER="${COMM_TAG}-${COMM_HASH}" - BUILD_RAW="rpcs3-v${AVVER}_win64" - BUILD="${BUILD_RAW}.7z" -else - AVVER="${COMM_TAG}-${COMM_COUNT}" - BUILD_RAW="rpcs3-v${AVVER}-${COMM_HASH}_win64" - BUILD="${BUILD_RAW}.7z" -fi - -# BRANCH is used for experimental build warnings for pr builds, used in main_window.cpp. -# BUILD is the name of the release artifact -# BUILD_RAW is just filename -# AVVER is used for GitHub releases, it is the version number. -BRANCH="${REPO_NAME}/${REPO_BRANCH}" - -# SC2129 -{ - echo "BRANCH=$BRANCH" - echo "BUILD=$BUILD" - echo "BUILD_RAW=$BUILD_RAW" - echo "AVVER=$AVVER" -} >> .ci/ci-vars.env diff --git a/.cirrus.yml b/.cirrus.yml deleted file mode 100644 index 41132c0bce..0000000000 --- a/.cirrus.yml +++ /dev/null @@ -1,153 +0,0 @@ -env: - CIRRUS_CLONE_DEPTH: 0 # Unshallow clone to obtain proper GIT_VERSION - BUILD_REPOSITORY_NAME: $CIRRUS_REPO_FULL_NAME - SYSTEM_PULLREQUEST_SOURCEBRANCH: $CIRRUS_BRANCH - SYSTEM_PULLREQUEST_PULLREQUESTID: $CIRRUS_PR - BUILD_SOURCEVERSION: $CIRRUS_CHANGE_IN_REPO - BUILD_SOURCEBRANCHNAME: $CIRRUS_BRANCH - RPCS3_TOKEN: ENCRYPTED[100ebb8e3552bf2021d0ef55dccda3e58d27be5b6cab0b0b92843ef490195d3c4edaefa087e4a3b425caa6392300b9b1] - QT_VER_MAIN: '6' - QT_VER: '6.9.0' - LLVM_COMPILER_VER: '19' - LLVM_VER: '19.1.7' - -# windows_task: -# matrix: -# - name: Cirrus Windows -# windows_container: -# image: cirrusci/windowsservercore:visualstudio2019 -# cpu: 8 -# memory: 16G -# env: -# CIRRUS_SHELL: "bash" -# COMPILER: msvc -# BUILD_ARTIFACTSTAGINGDIRECTORY: ${CIRRUS_WORKING_DIR}\artifacts\ -# QT_VER_MSVC: 'msvc2022' -# QT_DATE: '202503301022' -# QTDIR: C:\Qt\${QT_VER}\${QT_VER_MSVC}_64 -# VULKAN_VER: '1.3.268.0' -# VULKAN_SDK_SHA: '8459ef49bd06b697115ddd3d97c9aec729e849cd775f5be70897718a9b3b9db5' -# VULKAN_SDK: C:\VulkanSDK\${VULKAN_VER} -# CACHE_DIR: "./cache" -# UPLOAD_COMMIT_HASH: 7d09e3be30805911226241afbb14f8cdc2eb054e -# UPLOAD_REPO_FULL_NAME: "rpcs3/rpcs3-binaries-win" -# deps_cache: -# folder: "./cache" -# #obj_cache: -# # folder: "./tmp" -# #obj2_cache: -# # folder: "./rpcs3/x64" -# setup_script: -# - './.ci/get_keys-windows.sh' -# - './.ci/setup-windows.sh' -# rpcs3_script: -# - export PATH=${PATH}:"C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\MSBuild\Current\Bin" -# - msbuild.exe rpcs3.sln //p:Configuration=Release //m -# deploy_script: -# - mkdir artifacts -# - source './.ci/export-cirrus-vars.sh' -# - './.ci/deploy-windows.sh' -# artifacts: -# name: Artifact -# path: "*.7z*" -# push_script: | -# if [ "$CIRRUS_REPO_OWNER" = "RPCS3" ] && [ -z "$CIRRUS_PR" ] && [ "$CIRRUS_BRANCH" = "master" ]; then -# source './.ci/export-cirrus-vars.sh' -# './.ci/github-upload.sh' -# fi; - -# linux_task: -# container: -# image: rpcs3/rpcs3-ci-jammy:1.6 -# cpu: 4 -# memory: 16G -# env: -# BUILD_ARTIFACTSTAGINGDIRECTORY: ${CIRRUS_WORKING_DIR}/artifacts -# ARTDIR: ${CIRRUS_WORKING_DIR}/artifacts/ -# CCACHE_DIR: "/tmp/ccache_dir" -# CCACHE_MAXSIZE: 300M -# CI_HAS_ARTIFACTS: true -# UPLOAD_COMMIT_HASH: d812f1254a1157c80fd402f94446310560f54e5f -# UPLOAD_REPO_FULL_NAME: "rpcs3/rpcs3-binaries-linux" -# DEPLOY_APPIMAGE: true -# APPDIR: "./appdir" -# RELEASE_MESSAGE: "../GitHubReleaseMessage.txt" -# ccache_cache: -# folder: "/tmp/ccache_dir" -# matrix: -# - name: Cirrus Linux GCC -# env: -# COMPILER: gcc -# gcc_script: -# - mkdir artifacts -# - ".ci/build-linux.sh" -# - name: Cirrus Linux Clang -# env: -# COMPILER: clang -# clang_script: -# - mkdir artifacts -# - ".ci/build-linux.sh" -# artifacts: -# name: Artifact -# path: "artifacts/*" -# push_script: | -# if [ "$CIRRUS_REPO_OWNER" = "RPCS3" ] && [ -z "$CIRRUS_PR" ] && [ "$CIRRUS_BRANCH" = "master" ] && [ "$COMPILER" = "gcc" ]; then -# COMM_TAG=$(awk '/version{.*}/ { printf("%d.%d.%d", $5, $6, $7) }' ./rpcs3/rpcs3_version.cpp) -# COMM_COUNT=$(git rev-list --count HEAD) -# COMM_HASH=$(git rev-parse --short=8 HEAD) - -# export AVVER="${COMM_TAG}-${COMM_COUNT}" - -# .ci/github-upload.sh -# fi; - -freebsd_task: - matrix: - - name: Cirrus FreeBSD - freebsd_instance: - image_family: freebsd-13-5 - cpu: 8 - memory: 8G - env: - CCACHE_MAXSIZE: 300M # 3x clean build, rounded - CCACHE_DIR: /tmp/ccache_dir - ccache_cache: - folder: /tmp/ccache_dir - install_script: "sh -ex ./.ci/install-freebsd.sh" - script: "./.ci/build-freebsd.sh" - -# linux_aarch64_task: -# env: -# BUILD_ARTIFACTSTAGINGDIRECTORY: ${CIRRUS_WORKING_DIR}/artifacts -# ARTDIR: ${CIRRUS_WORKING_DIR}/artifacts/ -# CCACHE_DIR: "/tmp/ccache_dir" -# CCACHE_MAXSIZE: 300M -# CI_HAS_ARTIFACTS: true -# UPLOAD_COMMIT_HASH: a1d35836e8d45bfc6f63c26f0a3e5d46ef622fe1 -# UPLOAD_REPO_FULL_NAME: "rpcs3/rpcs3-binaries-linux-arm64" -# DEPLOY_APPIMAGE: true -# APPDIR: "./appdir" -# RELEASE_MESSAGE: "../GitHubReleaseMessage.txt" -# COMPILER: clang -# ccache_cache: -# folder: "/tmp/ccache_dir" -# matrix: -# - name: Cirrus Linux AArch64 Clang -# arm_container: -# image: 'docker.io/rpcs3/rpcs3-ci-jammy-aarch64:1.6' -# cpu: 8 -# memory: 8G -# clang_script: -# - mkdir artifacts -# - "sh -ex ./.ci/build-linux-aarch64.sh" -# artifacts: -# name: Artifact -# path: "artifacts/*" -# push_script: | -# if [ "$CIRRUS_REPO_OWNER" = "RPCS3" ] && [ -z "$CIRRUS_PR" ] && [ "$CIRRUS_BRANCH" = "master" ]; then -# COMM_TAG=$(awk '/version{.*}/ { printf("%d.%d.%d", $5, $6, $7) }' ./rpcs3/rpcs3_version.cpp) -# COMM_COUNT=$(git rev-list --count HEAD) -# COMM_HASH=$(git rev-parse --short=8 HEAD) -# export AVVER="${COMM_TAG}-${COMM_COUNT}" -# .ci/github-upload.sh -# fi; diff --git a/.github/workflows/llvm.yml b/.github/workflows/llvm.yml new file mode 100644 index 0000000000..e3e3e76c50 --- /dev/null +++ b/.github/workflows/llvm.yml @@ -0,0 +1,72 @@ +name: Build LLVM + +defaults: + run: + shell: bash +on: + workflow_dispatch: + +concurrency: + group: ${{ github.ref }}-${{ github.event_name }} + cancel-in-progress: true + +env: + BUILD_ARTIFACTSTAGINGDIRECTORY: ${{ github.workspace }}/artifacts/ + +jobs: + Windows_Build: + if: github.event_name == 'workflow_dispatch' + name: LLVM Windows (MSVC) + runs-on: windows-2025 + env: + COMPILER: msvc + CCACHE_SHA: '1f39f3ad5aae3fe915e99ad1302633bc8f6718e58fa7c0de2b0ba7e080f0f08c' + CCACHE_BIN_DIR: 'C:\ccache_bin' + CCACHE_DIR: 'C:\ccache' + CCACHE_INODECACHE: 'true' + CCACHE_SLOPPINESS: 'time_macros' + DEPS_CACHE_DIR: ./dependency_cache + steps: + + - name: Checkout repository + uses: actions/checkout@main + with: + fetch-depth: 0 + + - name: Restore Dependencies Cache + uses: actions/cache/restore@main + id: restore-dependencies-cache + with: + path: ${{ env.DEPS_CACHE_DIR }} + key: "${{ runner.os }}-${{ env.COMPILER }}-llvm-${{ env.CCACHE_SHA }}" + restore-keys: ${{ runner.os }}-${{ env.COMPILER }}-llvm + + - name: Download and unpack dependencies + run: .ci/setup-llvm.sh + + - name: Add msbuild to PATH + uses: microsoft/setup-msbuild@main + + - name: Compile LLVM + shell: pwsh + run: msbuild 3rdparty\llvm\llvm_build.vcxproj /p:SolutionDir="$(pwd)/" /p:Configuration=Release /v:minimal /p:Platform=x64 /p:PreferredToolArchitecture=x64 /p:CLToolPath=${{ env.CCACHE_BIN_DIR }} /p:UseMultiToolTask=true /p:CustomAfterMicrosoftCommonTargets="${{ github.workspace }}\buildfiles\msvc\ci_only.targets" + + - name: Pack up build artifacts + run: | + mkdir -p "${{ env.BUILD_ARTIFACTSTAGINGDIRECTORY }}" + .ci/deploy-llvm.sh + + - name: Upload artifacts (7z) + uses: actions/upload-artifact@main + with: + name: LLVM for Windows (MSVC) + path: ${{ env.BUILD_ARTIFACTSTAGINGDIRECTORY }} + compression-level: 0 + if-no-files-found: error + + - name: Save Dependencies Cache + if: github.ref == 'refs/heads/master' + uses: actions/cache/save@main + with: + path: ${{ env.DEPS_CACHE_DIR }} + key: ${{ steps.restore-dependencies-cache.outputs.cache-primary-key }} diff --git a/.github/workflows/rpcs3.yml b/.github/workflows/rpcs3.yml index f09655cb76..9964c045f5 100644 --- a/.github/workflows/rpcs3.yml +++ b/.github/workflows/rpcs3.yml @@ -17,6 +17,7 @@ concurrency: env: BUILD_REPOSITORY_NAME: ${{ github.repository }} BUILD_SOURCEBRANCHNAME: ${{ github.ref_name }} + BUILD_PR_NUMBER: ${{ github.event.pull_request.number }} BUILD_SOURCEVERSION: ${{ github.sha }} BUILD_ARTIFACTSTAGINGDIRECTORY: ${{ github.workspace }}/artifacts/ @@ -29,26 +30,29 @@ jobs: matrix: include: - os: ubuntu-24.04 - docker_img: "rpcs3/rpcs3-ci-jammy:1.6" + docker_img: "rpcs3/rpcs3-ci-jammy:1.7" build_sh: "/rpcs3/.ci/build-linux.sh" compiler: clang UPLOAD_COMMIT_HASH: d812f1254a1157c80fd402f94446310560f54e5f UPLOAD_REPO_FULL_NAME: "rpcs3/rpcs3-binaries-linux" - os: ubuntu-24.04 - docker_img: "rpcs3/rpcs3-ci-jammy:1.6" + docker_img: "rpcs3/rpcs3-ci-jammy:1.7" build_sh: "/rpcs3/.ci/build-linux.sh" compiler: gcc - os: ubuntu-24.04-arm - docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.6" + docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.7" build_sh: "/rpcs3/.ci/build-linux-aarch64.sh" compiler: clang UPLOAD_COMMIT_HASH: a1d35836e8d45bfc6f63c26f0a3e5d46ef622fe1 UPLOAD_REPO_FULL_NAME: "rpcs3/rpcs3-binaries-linux-arm64" + - os: ubuntu-24.04-arm + docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.7" + build_sh: "/rpcs3/.ci/build-linux-aarch64.sh" + compiler: gcc name: RPCS3 Linux ${{ matrix.os }} ${{ matrix.compiler }} runs-on: ${{ matrix.os }} env: CCACHE_DIR: ${{ github.workspace }}/ccache - CI_HAS_ARTIFACTS: true DEPLOY_APPIMAGE: true APPDIR: "/rpcs3/build/appdir" ARTDIR: "/root/artifacts" @@ -63,19 +67,19 @@ jobs: with: fetch-depth: 0 - - name: Setup Cache - uses: actions/cache@main + - name: Restore build Ccache + uses: actions/cache/restore@main + id: restore-build-ccache with: path: ${{ env.CCACHE_DIR }} key: ${{ runner.os }}-ccache-${{ matrix.compiler }}-${{ runner.arch }}-${{github.run_id}} - restore-keys: | - ${{ runner.os }}-ccache-${{ matrix.compiler }}-${{ runner.arch }}- + restore-keys: ${{ runner.os }}-ccache-${{ matrix.compiler }}-${{ runner.arch }}- - name: Docker setup and build run: | docker pull --quiet ${{ matrix.docker_img }} - docker run \ - -v $PWD:/rpcs3 \ + docker run \ + -v $PWD:/rpcs3 \ --env-file .ci/docker.env \ -v ${{ env.CCACHE_DIR }}:/root/.ccache \ -v ${{ env.BUILD_ARTIFACTSTAGINGDIRECTORY }}:${{ env.ARTDIR }} \ @@ -104,6 +108,102 @@ jobs: export AVVER="${COMM_TAG}-${COMM_COUNT}" .ci/github-upload.sh + - name: Save build Ccache + if: github.ref == 'refs/heads/master' + uses: actions/cache/save@main + with: + path: ${{ env.CCACHE_DIR }} + key: ${{ steps.restore-build-ccache.outputs.cache-primary-key }} + + Mac_Build: + # Only run push event on master branch of main repo, but run all PRs + if: github.event_name != 'push' || (github.repository == 'RPCS3/rpcs3' && github.ref_name == 'master') + strategy: + fail-fast: false + matrix: + include: + - name: Intel + build_sh: .ci/build-mac.sh + UPLOAD_COMMIT_HASH: 51ae32f468089a8169aaf1567de355ff4a3e0842 + UPLOAD_REPO_FULL_NAME: rpcs3/rpcs3-binaries-mac + - name: Apple Silicon + build_sh: .ci/build-mac-arm64.sh + UPLOAD_COMMIT_HASH: 8e21bdbc40711a3fccd18fbf17b742348b0f4281 + UPLOAD_REPO_FULL_NAME: rpcs3/rpcs3-binaries-mac-arm64 + name: RPCS3 Mac ${{ matrix.name }} + runs-on: macos-14 + env: + CCACHE_DIR: /tmp/ccache_dir + QT_VER: '6.7.3' + QT_VER_MAIN: '6' + LLVM_COMPILER_VER: '21' + RELEASE_MESSAGE: ../GitHubReleaseMessage.txt + UPLOAD_COMMIT_HASH: ${{ matrix.UPLOAD_COMMIT_HASH }} + UPLOAD_REPO_FULL_NAME: ${{ matrix.UPLOAD_REPO_FULL_NAME }} + RUN_UNIT_TESTS: github.event_name == 'pull_request' && 'ON' || 'OFF' + steps: + - name: Checkout repository + uses: actions/checkout@main + with: + fetch-depth: 0 + + - name: Restore Build Ccache + uses: actions/cache/restore@main + id: restore-build-ccache + with: + path: ${{ env.CCACHE_DIR }} + key: ${{ runner.os }}-ccache-${{ matrix.name }}-${{github.run_id}} + restore-keys: ${{ runner.os }}-ccache-${{ matrix.name }}- + + - name: Restore Qt Cache + uses: actions/cache/restore@main + id: restore-qt-cache + with: + path: /tmp/Qt + key: ${{ runner.os }}-qt-${{ matrix.name }}-${{ env.QT_VER }} + restore-keys: ${{ runner.os }}-qt-${{ matrix.name }}-${{ env.QT_VER }} + + - name: Build + run: ${{ matrix.build_sh }} + + - name: Upload artifacts + uses: actions/upload-artifact@main + with: + name: RPCS3 for Mac (${{ matrix.name }}) + path: ${{ env.BUILD_ARTIFACTSTAGINGDIRECTORY }} + compression-level: 0 + + - name: Export Variables + run: | + while IFS='=' read -r key val; do + # Skip lines that are empty or start with '#' + [[ -z "$key" || "$key" =~ ^# ]] && continue + echo "$key=$val" >> "${{ github.env }}" + done < .ci/ci-vars.env + + - name: Deploy master build to GitHub Releases + if: | + github.event_name != 'pull_request' && + github.repository == 'RPCS3/rpcs3' && + github.ref == 'refs/heads/master' + env: + RPCS3_TOKEN: ${{ secrets.RPCS3_TOKEN }} + run: .ci/github-upload.sh + + - name: Save Build Ccache + if: github.ref == 'refs/heads/master' + uses: actions/cache/save@main + with: + path: ${{ env.CCACHE_DIR }} + key: ${{ steps.restore-build-ccache.outputs.cache-primary-key }} + + - name: Save Qt Cache + if: github.ref == 'refs/heads/master' + uses: actions/cache/save@main + with: + path: /tmp/Qt + key: ${{ steps.restore-qt-cache.outputs.cache-primary-key }} + Windows_Build: # Only run push event on master branch of main repo, but run all PRs if: github.event_name != 'push' || (github.repository == 'RPCS3/rpcs3' && github.ref_name == 'master') @@ -112,9 +212,9 @@ jobs: env: COMPILER: msvc QT_VER_MAIN: '6' - QT_VER: '6.9.0' + QT_VER: '6.10.0' QT_VER_MSVC: 'msvc2022' - QT_DATE: '202503301022' + QT_DATE: '202510021201' LLVM_VER: '19.1.7' VULKAN_VER: '1.3.268.0' VULKAN_SDK_SHA: '8459ef49bd06b697115ddd3d97c9aec729e849cd775f5be70897718a9b3b9db5' @@ -148,22 +248,26 @@ jobs: - name: Get Cache Keys run: .ci/get_keys-windows.sh - - name: Setup Build Ccache - uses: actions/cache@main + - name: Restore Build Ccache + uses: actions/cache/restore@main + id: restore-build-ccache with: path: ${{ env.CCACHE_DIR }} key: "${{ runner.os }}-ccache-${{ env.COMPILER }}-${{github.run_id}}" restore-keys: ${{ runner.os }}-ccache-${{ env.COMPILER }}- - - name: Setup Dependencies Cache - uses: actions/cache@main + - name: Restore Dependencies Cache + uses: actions/cache/restore@main + id: restore-dependencies-cache with: path: ${{ env.DEPS_CACHE_DIR }} key: "${{ runner.os }}-${{ env.COMPILER }}-${{ env.QT_VER }}-${{ env.VULKAN_SDK_SHA }}-${{ env.CCACHE_SHA }}-${{ hashFiles('llvm.lock') }}" restore-keys: ${{ runner.os }}-${{ env.COMPILER }}- - name: Download and unpack dependencies - run: .ci/setup-windows.sh + run: | + .ci/setup-windows.sh + .ci/setup-windows-ci-vars.sh win64 msvc - name: Export Variables run: | @@ -206,3 +310,135 @@ jobs: env: RPCS3_TOKEN: ${{ secrets.RPCS3_TOKEN }} run: .ci/github-upload.sh + + - name: Save Build Ccache + if: github.ref == 'refs/heads/master' + uses: actions/cache/save@main + with: + path: ${{ env.CCACHE_DIR }} + key: ${{ steps.restore-build-ccache.outputs.cache-primary-key }} + + - name: Save Dependencies Cache + if: github.ref == 'refs/heads/master' + uses: actions/cache/save@main + with: + path: ${{ env.DEPS_CACHE_DIR }} + key: ${{ steps.restore-dependencies-cache.outputs.cache-primary-key }} + + Windows_Build_Clang: + # Only run push event on master branch of main repo, but run all PRs + if: github.event_name != 'push' || (github.repository == 'RPCS3/rpcs3' && github.ref_name == 'master') + name: RPCS3 Windows Clang + runs-on: windows-2025 + strategy: + matrix: + include: + - msys2: clang64 + compiler: clang + arch: win64 + env: + CCACHE_DIR: 'C:\ccache' + steps: + - name: Checkout repository + uses: actions/checkout@main + with: + fetch-depth: 0 + + - name: Setup msys2 + uses: msys2/setup-msys2@v2 + with: + msystem: ${{ matrix.msys2 }} + update: true + cache: true + install: | + mingw-w64-clang-x86_64-clang + mingw-w64-clang-x86_64-ccache + mingw-w64-clang-x86_64-cmake + mingw-w64-clang-x86_64-lld + mingw-w64-clang-x86_64-ninja + mingw-w64-clang-x86_64-llvm + mingw-w64-clang-x86_64-ffmpeg + mingw-w64-clang-x86_64-opencv + mingw-w64-clang-x86_64-glew + mingw-w64-clang-x86_64-vulkan + mingw-w64-clang-x86_64-vulkan-headers + mingw-w64-clang-x86_64-vulkan-loader + mingw-w64-clang-x86_64-gtest + mingw-w64-clang-x86_64-qt6-base + mingw-w64-clang-x86_64-qt6-declarative + mingw-w64-clang-x86_64-qt6-multimedia + mingw-w64-clang-x86_64-qt6-svg + base-devel + curl + git + p7zip + + - name: Restore build Ccache + uses: actions/cache/restore@main + id: restore-build-ccache + with: + path: ${{ env.CCACHE_DIR }} + key: ${{ runner.os }}-ccache-${{ matrix.compiler }}-${{ runner.arch }}-${{ github.run_id }} + restore-keys: ${{ runner.os }}-ccache-${{ matrix.compiler }}-${{ runner.arch }}- + + - name: Build RPCS3 + shell: msys2 {0} + run: | + export CCACHE_DIR=$(cygpath -u "$CCACHE_DIR") + echo "CCACHE_DIR=$CCACHE_DIR" + .ci/setup-windows-ci-vars.sh ${{ matrix.arch }} ${{ matrix.compiler }} + .ci/build-windows-clang.sh + + - name: Save build Ccache + if: github.ref == 'refs/heads/master' + uses: actions/cache/save@main + with: + path: ${{ env.CCACHE_DIR }} + key: ${{ steps.restore-build-ccache.outputs.cache-primary-key }} + + - name: Upload artifacts + uses: actions/upload-artifact@main + with: + name: RPCS3 for Windows (${{ runner.arch }}, ${{ matrix.compiler }}) + path: ${{ env.BUILD_ARTIFACTSTAGINGDIRECTORY }} + compression-level: 0 + if-no-files-found: error + + FreeBSD_Build: + # Only run push event on master branch of main repo, but run all PRs + if: github.event_name != 'push' || (github.repository == 'RPCS3/rpcs3' && github.ref_name == 'master') + name: RPCS3 FreeBSD + runs-on: ubuntu-latest + timeout-minutes: 60 + env: + CCACHE_DIR: ${{ github.workspace }}/ccache + QT_VER_MAIN: '6' + LLVM_COMPILER_VER: '19' + steps: + - name: Checkout repository + uses: actions/checkout@main + with: + fetch-depth: 0 + + - name: Restore Build Ccache + uses: actions/cache/restore@main + id: restore-build-ccache + with: + path: ${{ env.CCACHE_DIR }} + key: FreeBSD-ccache-${{github.run_id}} + restore-keys: FreeBSD-ccache- + + - name: FreeBSD build + id: root + uses: vmactions/freebsd-vm@v1 + with: + envs: 'QT_VER_MAIN LLVM_COMPILER_VER CCACHE_DIR' + usesh: true + run: .ci/install-freebsd.sh && .ci/build-freebsd.sh + + - name: Save Build Ccache + if: github.ref == 'refs/heads/master' + uses: actions/cache/save@main + with: + path: ${{ env.CCACHE_DIR }} + key: ${{ steps.restore-build-ccache.outputs.cache-primary-key }} diff --git a/.gitmodules b/.gitmodules index 427c61ffbd..6f0cd78a5b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -108,3 +108,7 @@ path = 3rdparty/GPUOpen/VulkanMemoryAllocator url = ../../GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git ignore = dirty +[submodule "3rdparty/feralinteractive/feralinteractive"] + path = 3rdparty/feralinteractive/feralinteractive + url = ../../FeralInteractive/gamemode.git + ignore = dirty diff --git a/3rdparty/7zip/7zip b/3rdparty/7zip/7zip index e5431fa6f5..5e96a82794 160000 --- a/3rdparty/7zip/7zip +++ b/3rdparty/7zip/7zip @@ -1 +1 @@ -Subproject commit e5431fa6f5505e385c6f9367260717e9c47dc2ee +Subproject commit 5e96a8279489832924056b1fa82f29d5837c9469 diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt index 044fd464eb..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") @@ -92,9 +100,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) @@ -131,7 +136,14 @@ add_subdirectory(stblib) add_subdirectory(discord-rpc) # Cubeb -add_subdirectory(cubeb EXCLUDE_FROM_ALL) +if(USE_SYSTEM_CUBEB) + find_package(cubeb REQUIRED GLOBAL) + message(STATUS "Using system cubeb version '${cubeb_VERSION}'") + add_library(3rdparty::cubeb ALIAS cubeb::cubeb) +else() + message(STATUS "Using static cubeb from 3rdparty") + add_subdirectory(cubeb EXCLUDE_FROM_ALL) +endif() # SoundTouch add_subdirectory(SoundTouch EXCLUDE_FROM_ALL) @@ -350,6 +362,9 @@ add_subdirectory(opencv EXCLUDE_FROM_ALL) # FUSION add_subdirectory(fusion EXCLUDE_FROM_ALL) +# FERAL INTERACTIVE +add_subdirectory(feralinteractive EXCLUDE_FROM_ALL) + # add nice ALIAS targets for ease of use if(USE_SYSTEM_LIBUSB) add_library(3rdparty::libusb ALIAS usb-1.0-shared) @@ -378,7 +393,8 @@ 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) +add_library(3rdparty::feralinteractive ALIAS 3rdparty_feralinteractive) diff --git a/3rdparty/FAudio b/3rdparty/FAudio index 6077ea740a..8de3616b5b 160000 --- a/3rdparty/FAudio +++ b/3rdparty/FAudio @@ -1 +1 @@ -Subproject commit 6077ea740a7114a54f76ed9b7abe08cffc0034b6 +Subproject commit 8de3616b5b204260fe639e76587731d8a73b8d2c diff --git a/3rdparty/MoltenVK/CMakeLists.txt b/3rdparty/MoltenVK/CMakeLists.txt index ec9c2b802b..a4d8b02a8c 100644 --- a/3rdparty/MoltenVK/CMakeLists.txt +++ b/3rdparty/MoltenVK/CMakeLists.txt @@ -3,7 +3,7 @@ include(ExternalProject) ExternalProject_Add(moltenvk GIT_REPOSITORY https://github.com/KhronosGroup/MoltenVK.git - GIT_TAG 49b97f2 + GIT_TAG 4588705 BUILD_IN_SOURCE 1 SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK CONFIGURE_COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK/fetchDependencies" --macos diff --git a/3rdparty/OpenAL/CMakeLists.txt b/3rdparty/OpenAL/CMakeLists.txt index f37a776a1f..b9fee23ce5 100644 --- a/3rdparty/OpenAL/CMakeLists.txt +++ b/3rdparty/OpenAL/CMakeLists.txt @@ -11,6 +11,7 @@ if(USE_SYSTEM_OPENAL) else() option(ALSOFT_UTILS "Build utility programs" OFF) option(ALSOFT_EXAMPLES "Build example programs" OFF) + set(LIBTYPE "STATIC") add_subdirectory(openal-soft EXCLUDE_FROM_ALL) add_library(3rdparty_openal INTERFACE) target_link_libraries(3rdparty_openal INTERFACE OpenAL::OpenAL) diff --git a/3rdparty/OpenAL/openal-soft b/3rdparty/OpenAL/openal-soft index dc7d7054a5..0e5e98e4ac 160000 --- a/3rdparty/OpenAL/openal-soft +++ b/3rdparty/OpenAL/openal-soft @@ -1 +1 @@ -Subproject commit dc7d7054a5b4f3bec1dc23a42fd616a0847af948 +Subproject commit 0e5e98e4ac8adae92e4f7653dd6eee17aa9c8791 diff --git a/3rdparty/OpenAL/openal-soft.vcxproj b/3rdparty/OpenAL/openal-soft.vcxproj index d309093527..7782ddc940 100644 --- a/3rdparty/OpenAL/openal-soft.vcxproj +++ b/3rdparty/OpenAL/openal-soft.vcxproj @@ -49,11 +49,11 @@ call vsdevcmd.bat -arch=amd64 cd "$(SolutionDir)build\tmp\$(ProjectName)-$(Configuration)-$(Platform)" - cmake -G Ninja -DCMAKE_CXX_COMPILER="cl.exe" -DCMAKE_C_COMPILER="cl.exe" -DCMAKE_BUILD_TYPE="Release" -DCMAKE_INSTALL_PREFIX="./Release" -DCMAKE_SYSTEM_VERSION=10.0 -DLIBTYPE=STATIC -DFORCE_STATIC_VCRT=true -DALSOFT_UTILS=false -DALSOFT_EXAMPLES=false -DALSOFT_INSTALL=false -DALSOFT_INSTALL_CONFIG=false -DALSOFT_INSTALL_HRTF_DATA=false -DALSOFT_INSTALL_AMBDEC_PRESETS=false -DALSOFT_INSTALL_EXAMPLES=false -DALSOFT_INSTALL_UTILS=false "$(SolutionDir)3rdparty\OpenAL\openal-soft" + cmake -G Ninja -DCMAKE_CXX_COMPILER="cl.exe" -DCMAKE_C_COMPILER="cl.exe" -DCMAKE_BUILD_TYPE="Release" -DCMAKE_INSTALL_PREFIX="./Release" -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded -DCMAKE_SYSTEM_VERSION=10.0 -DLIBTYPE=STATIC -DFORCE_STATIC_VCRT=true -DALSOFT_UTILS=false -DALSOFT_EXAMPLES=false -DALSOFT_INSTALL=false -DALSOFT_INSTALL_CONFIG=false -DALSOFT_INSTALL_HRTF_DATA=false -DALSOFT_INSTALL_AMBDEC_PRESETS=false -DALSOFT_INSTALL_EXAMPLES=false -DALSOFT_INSTALL_UTILS=false "$(SolutionDir)3rdparty\OpenAL\openal-soft" call vsdevcmd.bat -arch=amd64 cd "$(SolutionDir)build\tmp\$(ProjectName)-$(Configuration)-$(Platform)" - cmake -G Ninja -DCMAKE_CXX_COMPILER="cl.exe" -DCMAKE_C_COMPILER="cl.exe" -DCMAKE_BUILD_TYPE="Debug" -DCMAKE_INSTALL_PREFIX="./Debug" -DCMAKE_SYSTEM_VERSION=10.0 -DLIBTYPE=STATIC -DALSOFT_UTILS=false -DALSOFT_EXAMPLES=false -DALSOFT_INSTALL=false -DALSOFT_INSTALL_CONFIG=false -DALSOFT_INSTALL_HRTF_DATA=false -DALSOFT_INSTALL_AMBDEC_PRESETS=false -DALSOFT_INSTALL_EXAMPLES=false -DALSOFT_INSTALL_UTILS=false "$(SolutionDir)3rdparty\OpenAL\openal-soft" + cmake -G Ninja -DCMAKE_CXX_COMPILER="cl.exe" -DCMAKE_C_COMPILER="cl.exe" -DCMAKE_BUILD_TYPE="Debug" -DCMAKE_INSTALL_PREFIX="./Debug" -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDebug -DCMAKE_SYSTEM_VERSION=10.0 -DLIBTYPE=STATIC -DALSOFT_UTILS=false -DALSOFT_EXAMPLES=false -DALSOFT_INSTALL=false -DALSOFT_INSTALL_CONFIG=false -DALSOFT_INSTALL_HRTF_DATA=false -DALSOFT_INSTALL_AMBDEC_PRESETS=false -DALSOFT_INSTALL_EXAMPLES=false -DALSOFT_INSTALL_UTILS=false "$(SolutionDir)3rdparty\OpenAL\openal-soft" echo Copying.. @@ -106,4 +106,4 @@ - \ No newline at end of file + diff --git a/3rdparty/cubeb/cubeb b/3rdparty/cubeb/cubeb index 70b4e3db78..e495bee4cd 160000 --- a/3rdparty/cubeb/cubeb +++ b/3rdparty/cubeb/cubeb @@ -1 +1 @@ -Subproject commit 70b4e3db7822de4d534959885cda109d6edbee36 +Subproject commit e495bee4cd630c9f99907a764e16edba37a4b564 diff --git a/3rdparty/curl/CMakeLists.txt b/3rdparty/curl/CMakeLists.txt index d8fc5790fc..b20763af65 100644 --- a/3rdparty/curl/CMakeLists.txt +++ b/3rdparty/curl/CMakeLists.txt @@ -26,8 +26,8 @@ else() endif() set(CURL_USE_LIBSSH2 OFF CACHE BOOL "Use libSSH2") set(CURL_USE_LIBPSL OFF CACHE BOOL "Use libPSL") - - set(CURL_DISABLE_TESTS ON) + option(BUILD_TESTING "Build tests" OFF) + option(BUILD_EXAMPLES "Build libcurl examples" OFF) add_subdirectory(curl EXCLUDE_FROM_ALL) diff --git a/3rdparty/curl/curl b/3rdparty/curl/curl index 1c31498817..400fffa90f 160000 --- a/3rdparty/curl/curl +++ b/3rdparty/curl/curl @@ -1 +1 @@ -Subproject commit 1c3149881769e7bd79b072e48374e4c2b3678b2f +Subproject commit 400fffa90f30c7a2dc762fa33009d24851bd2016 diff --git a/3rdparty/curl/libcurl.vcxproj b/3rdparty/curl/libcurl.vcxproj index 6c8cd23ffb..dae28be346 100644 --- a/3rdparty/curl/libcurl.vcxproj +++ b/3rdparty/curl/libcurl.vcxproj @@ -62,14 +62,15 @@ - - + + + @@ -77,15 +78,29 @@ + + + + + + + + + + + + + + + - + - @@ -99,15 +114,14 @@ - + - @@ -115,14 +129,11 @@ - - - @@ -137,9 +148,6 @@ - - - @@ -151,8 +159,8 @@ + - @@ -182,28 +190,27 @@ - - - + + + + - - + - @@ -220,8 +227,6 @@ - - @@ -229,7 +234,6 @@ - @@ -254,6 +258,7 @@ + @@ -266,24 +271,38 @@ - + + + + + + + + + + + + + + + + + - - + - - + @@ -300,15 +319,14 @@ - + - @@ -316,7 +334,6 @@ - @@ -333,8 +350,6 @@ - - @@ -343,8 +358,8 @@ + - @@ -374,23 +389,23 @@ - - - + + + + - - + @@ -399,21 +414,18 @@ - - - diff --git a/3rdparty/curl/libcurl.vcxproj.filters b/3rdparty/curl/libcurl.vcxproj.filters index 15fe93ce39..17f760c54b 100644 --- a/3rdparty/curl/libcurl.vcxproj.filters +++ b/3rdparty/curl/libcurl.vcxproj.filters @@ -24,12 +24,6 @@ Source Files - - Source Files - - - Source Files - Source Files @@ -48,9 +42,6 @@ Source Files - - Source Files - Source Files @@ -69,9 +60,6 @@ Source Files - - Source Files - Source Files @@ -132,9 +120,6 @@ Source Files - - Source Files - Source Files @@ -144,9 +129,6 @@ Source Files - - Source Files - Source Files @@ -174,15 +156,6 @@ Source Files - - Source Files - - - Source Files - - - Source Files - Source Files @@ -210,9 +183,6 @@ Source Files - - Source Files - Source Files @@ -300,9 +270,6 @@ Source Files - - Source Files - Source Files @@ -315,9 +282,6 @@ Source Files - - Source Files - Source Files @@ -363,12 +327,6 @@ Source Files - - Source Files - - - Source Files - Source Files @@ -387,24 +345,15 @@ Source Files - - Source Files - Source Files Source Files - - Source Files - Source Files - - Source Files - Source Files @@ -438,12 +387,6 @@ Source Files - - Source Files - - - Source Files - Source Files @@ -462,9 +405,6 @@ Source Files - - Source Files - Source Files @@ -504,9 +444,6 @@ Source Files - - Source Files - Source Files @@ -525,9 +462,6 @@ Source Files - - Source Files - Source Files @@ -537,6 +471,84 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + @@ -608,21 +620,12 @@ Header Files - - Header Files - Header Files - - Header Files - Header Files - - Header Files - Header Files @@ -656,9 +659,6 @@ Header Files - - Header Files - Header Files @@ -755,12 +755,6 @@ Header Files - - Header Files - - - Header Files - Header Files @@ -779,9 +773,6 @@ Header Files - - Header Files - Header Files @@ -875,9 +866,6 @@ Header Files - - Header Files - Header Files @@ -890,9 +878,6 @@ Header Files - - Header Files - Header Files @@ -908,9 +893,6 @@ Header Files - - Header Files - Header Files @@ -926,36 +908,24 @@ Header Files - - Header Files - Header Files Header Files - - Header Files - Header Files Header Files - - Header Files - Header Files Header Files - - Header Files - Header Files @@ -977,15 +947,9 @@ Header Files - - Header Files - Header Files - - Header Files - Header Files @@ -1007,9 +971,6 @@ Header Files - - Header Files - Header Files @@ -1052,9 +1013,6 @@ Header Files - - Header Files - Header Files @@ -1076,15 +1034,93 @@ Header Files - - Header Files - Header Files Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + diff --git a/3rdparty/feralinteractive/CMakeLists.txt b/3rdparty/feralinteractive/CMakeLists.txt new file mode 100644 index 0000000000..c7b136e5f0 --- /dev/null +++ b/3rdparty/feralinteractive/CMakeLists.txt @@ -0,0 +1,9 @@ +# Feral Interactive + +add_library(3rdparty_feralinteractive INTERFACE) + +if (CMAKE_SYSTEM MATCHES "Linux") + target_include_directories(3rdparty_feralinteractive INTERFACE feralinteractive/lib) + target_compile_definitions(3rdparty_feralinteractive INTERFACE -DGAMEMODE_AVAILABLE) + target_link_libraries(3rdparty_feralinteractive INTERFACE feralinteractive) +endif() diff --git a/3rdparty/feralinteractive/feralinteractive b/3rdparty/feralinteractive/feralinteractive new file mode 160000 index 0000000000..c54d6d4243 --- /dev/null +++ b/3rdparty/feralinteractive/feralinteractive @@ -0,0 +1 @@ +Subproject commit c54d6d4243b0dd0afcb49f2c9836d432da171a2b diff --git a/3rdparty/fusion/fusion b/3rdparty/fusion/fusion index 066d4a63b2..759ac5d698 160000 --- a/3rdparty/fusion/fusion +++ b/3rdparty/fusion/fusion @@ -1 +1 @@ -Subproject commit 066d4a63b2c714b20b0a8073a01fda7c5c6763f6 +Subproject commit 759ac5d698baefca53f1975a0bb1d2dcbdb9f836 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/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/3rdparty/libpng/libpng b/3rdparty/libpng/libpng index ea12796820..2b978915d8 160000 --- a/3rdparty/libpng/libpng +++ b/3rdparty/libpng/libpng @@ -1 +1 @@ -Subproject commit ea127968204cc5d10f3fc9250c306b9e8cbd9b80 +Subproject commit 2b978915d82377df13fcbb1fb56660195ded868a diff --git a/3rdparty/libsdl-org/SDL b/3rdparty/libsdl-org/SDL index 8d604353a5..badbf8da4e 160000 --- a/3rdparty/libsdl-org/SDL +++ b/3rdparty/libsdl-org/SDL @@ -1 +1 @@ -Subproject commit 8d604353a53853fa56d1bdce0363535605ca868f +Subproject commit badbf8da4ee72b3ef599c721ffc9899e8d7c8d90 diff --git a/3rdparty/libsdl-org/SDL.vcxproj b/3rdparty/libsdl-org/SDL.vcxproj index 81b7d853a5..f0b38ca09f 100644 --- a/3rdparty/libsdl-org/SDL.vcxproj +++ b/3rdparty/libsdl-org/SDL.vcxproj @@ -384,7 +384,6 @@ - diff --git a/3rdparty/libsdl-org/SDL.vcxproj.filters b/3rdparty/libsdl-org/SDL.vcxproj.filters index 44d4e4ec9f..5839899c0d 100644 --- a/3rdparty/libsdl-org/SDL.vcxproj.filters +++ b/3rdparty/libsdl-org/SDL.vcxproj.filters @@ -172,9 +172,6 @@ {f48c2b17-1bee-4fec-a7c8-24cf619abe08} - - {653672cc-90ae-4eba-a256-6479f2c31804} - {00001967ea2801028a046a722a070000} @@ -1460,9 +1457,6 @@ sensor\windows - - render - render diff --git a/3rdparty/libusb/libusb b/3rdparty/libusb/libusb index a61afe5f75..15a7ebb4d4 160000 --- a/3rdparty/libusb/libusb +++ b/3rdparty/libusb/libusb @@ -1 +1 @@ -Subproject commit a61afe5f75d969c4561a1d0ad753aa23cee6329a +Subproject commit 15a7ebb4d426c5ce196684347d2b7cafad862626 diff --git a/3rdparty/llvm/CMakeLists.txt b/3rdparty/llvm/CMakeLists.txt index 8723728f4a..d1295886d8 100644 --- a/3rdparty/llvm/CMakeLists.txt +++ b/3rdparty/llvm/CMakeLists.txt @@ -66,12 +66,10 @@ if(WITH_LLVM) find_package(LLVM CONFIG) if (NOT LLVM_FOUND) - message(FATAL_ERROR "Can't find LLVM libraries from the CMAKE_PREFIX_PATH path or LLVM_DIR. \ - Enable BUILD_LLVM option to build LLVM from included as a git submodule.") + message(FATAL_ERROR "Can't find LLVM libraries from the CMAKE_PREFIX_PATH path or LLVM_DIR. Enable BUILD_LLVM option to build LLVM from included as a git submodule.") endif() if (LLVM_VERSION VERSION_LESS 18) - message(FATAL_ERROR "Found LLVM version ${LLVM_VERSION}. Required version 18 or above. \ - Enable BUILD_LLVM option to build LLVM from included as a git submodule.") + message(FATAL_ERROR "Found LLVM version ${LLVM_VERSION}. Required version 18 or above. Enable BUILD_LLVM option to build LLVM from included as a git submodule.") endif() endif() 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/3rdparty/opencv/opencv b/3rdparty/opencv/opencv index f76628fb5b..67f53c26a7 160000 --- a/3rdparty/opencv/opencv +++ b/3rdparty/opencv/opencv @@ -1 +1 @@ -Subproject commit f76628fb5b25746fcb75a7ce85be0d8c6439fc57 +Subproject commit 67f53c26a701c2aeefd8033ec2f2079e04c438ca diff --git a/3rdparty/qt6.cmake b/3rdparty/qt6.cmake index 259727879e..e15e0abdcb 100644 --- a/3rdparty/qt6.cmake +++ b/3rdparty/qt6.cmake @@ -6,14 +6,15 @@ find_package(Qt6 ${QT_MIN_VER} CONFIG COMPONENTS Widgets Concurrent Multimedia M if(WIN32) target_link_libraries(3rdparty_qt6 INTERFACE Qt6::Widgets Qt6::Concurrent Qt6::Multimedia Qt6::MultimediaWidgets Qt6::Svg Qt6::SvgWidgets) else() - find_package(Qt6 ${QT_MIN_VER} COMPONENTS DBus Gui) + set(QT_NO_PRIVATE_MODULE_WARNING ON) + find_package(Qt6 ${QT_MIN_VER} COMPONENTS DBus Gui GuiPrivate) if(Qt6DBus_FOUND) target_link_libraries(3rdparty_qt6 INTERFACE Qt6::Widgets Qt6::DBus Qt6::Concurrent Qt6::Multimedia Qt6::MultimediaWidgets Qt6::Svg Qt6::SvgWidgets) target_compile_definitions(3rdparty_qt6 INTERFACE -DHAVE_QTDBUS) else() target_link_libraries(3rdparty_qt6 INTERFACE Qt6::Widgets Qt6::Concurrent Qt6::Multimedia Qt6::MultimediaWidgets Qt6::Svg Qt6::SvgWidgets) endif() - target_include_directories(3rdparty_qt6 INTERFACE ${Qt6Gui_PRIVATE_INCLUDE_DIRS}) + target_link_libraries(3rdparty_qt6 INTERFACE Qt6::GuiPrivate) endif() if(Qt6Widgets_FOUND) @@ -32,8 +33,7 @@ Find the correct ppa at https://launchpad.net/~beineri and follow the instructio else() message("CMake was unable to find Qt6!") if(WIN32) - message(FATAL_ERROR "Make sure the QTDIR env variable has been set properly. (for example C:\\Qt\\${QT_MIN_VER}\\msvc2019_64\\) -You can also try setting the Qt6_DIR preprocessor definiton.") + message(FATAL_ERROR "Make sure the Qt6_ROOT environment variable has been set properly. (for example C:\\Qt\\${QT_MIN_VER}\\msvc2022_64\\)") elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") message(FATAL_ERROR "Make sure to install your distro's qt6 package!") else() 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/3rdparty/version_check.sh b/3rdparty/version_check.sh index 2b721fe28f..e0632da3e7 100644 --- a/3rdparty/version_check.sh +++ b/3rdparty/version_check.sh @@ -1,7 +1,11 @@ -#!/bin/sh -ex +#!/bin/bash -ex verbose=0 git_verbose=0 +max_jobs=16 + +lockfile="$(pwd)/version_check.lock" +resultfile="$(pwd)/version_check_results.txt" if [ "$1" = "-v" ]; then verbose=1 @@ -10,9 +14,13 @@ elif [ "$1" = "-vv" ]; then git_verbose=1 fi -max_dir_length=0 -result_dirs=() -result_msgs=() +# Limit concurrent jobs +job_control() +{ + while [ "$(jobs | wc -l)" -ge "$max_jobs" ]; do + sleep 0.1 + done +} git_call() { @@ -27,47 +35,48 @@ git_call() check_tags() { - path=$(echo "$1" | sed 's:/*$::') + local path=$(echo "$1" | sed 's:/*$::') echo "Checking $path" git_call fetch --prune --all # Get the latest tag (by commit date, not tag name) - tag_list=$(git_call rev-list --tags --max-count=1) - latest_tag=$(git_call describe --tags "$tag_list") + local tag_list=$(git_call rev-list --tags --max-count=1) + local latest_tag=$(git_call describe --tags "$tag_list") + local highest_tag=$(git_call tag -l | sort -V | tail -n1) - if [ -n "$latest_tag" ]; then + if [ -n "$latest_tag" ] || [ -n "$highest_tag" ]; then # Get the current tag - current_tag=$(git_call describe --tags --abbrev=0) + local current_tag=$(git_call describe --tags --abbrev=0) if [ -n "$current_tag" ]; then if [ "$verbose" -eq 1 ]; then - echo "$path -> latest: $latest_tag, current: $current_tag" + echo "$path -> latest: $latest_tag, highest: $highest_tag, current: $current_tag" fi - ts1=$(git_call log -1 --format=%ct $latest_tag) - ts2=$(git_call log -1 --format=%ct $current_tag) + local ts0=$(git_call log -1 --format=%ct $highest_tag) + local ts1=$(git_call log -1 --format=%ct $latest_tag) + local ts2=$(git_call log -1 --format=%ct $current_tag) - if (( ts1 > ts2 )); then + if (( ts0 > ts2 )) || (( ts1 > ts2 )); then if [ "$verbose" -eq 1 ]; then echo -e "\t $path: latest is newer" elif [ "$verbose" -eq 0 ]; then - echo "$path -> latest: $latest_tag, current: $current_tag" + echo "$path -> latest: $latest_tag, highest: $highest_tag, current: $current_tag" fi - path_length=${#path} - if (( $path_length > $max_dir_length )); then - max_dir_length=$path_length - fi - result_dirs+=("$path") - result_msgs+=("latest: $latest_tag, current: $current_tag") + # Critical section guarded by flock + ( + flock 200 + echo "$path -> latest: $latest_tag, highest: $highest_tag, current: $current_tag" >> "$resultfile" + ) 200>"$lockfile" fi elif [ "$verbose" -eq 1 ]; then - echo "$path -> latest: $latest_tag" + echo "$path -> latest: $latest_tag, highest: $highest_tag" fi elif [ "$verbose" -eq 1 ]; then @@ -79,19 +88,21 @@ check_tags() fi } +# Fetch and check repositories multi threaded for submoduledir in */ ; do cd "$submoduledir" || continue if [ -e ".git" ]; then - check_tags "$submoduledir" + job_control + check_tags "$submoduledir" & else - # find */ -mindepth 1 -maxdepth 1 -type d | while read -r sub; for sub in */ ; do if [ -e "$sub/.git" ]; then cd "$sub" || continue - check_tags "$submoduledir$sub" + job_control + check_tags "$submoduledir$sub" & cd .. || exit fi done @@ -100,16 +111,27 @@ do cd .. || exit done +# Wait for all background jobs to finish +wait + +# Print results echo -e "\n\nResult:\n" -i=0 -for result_dir in "${result_dirs[@]}"; do - msg="" - diff=$(($max_dir_length - ${#result_dir})) - if (( $diff > 0 )); then - msg+=$(printf "%${diff}s" "") + +# Find the max length of the paths (before '->') +max_len=0 +while IFS='->' read -r left _; do + len=$(echo -n "$left" | wc -c) + if (( len > max_len )); then + max_len=$len fi - msg+="$result_dir" - echo "$msg -> ${result_msgs[$i]}" - ((i++)) -done -echo "" +done < "$resultfile" + +# Print with padding so '->' lines up +while IFS='->' read -r left right; do + right=$(echo "$right" | sed 's/^[[:space:]]*>*[[:space:]]*//') + printf "%-${max_len}s -> %s\n" "$left" "$right" +done < "$resultfile" + +# Remove tmp files +rm -f "$resultfile" +rm -f "$lockfile" diff --git a/3rdparty/zstd/CMakeLists.txt b/3rdparty/zstd/CMakeLists.txt index 5a8b6f3d90..431272966d 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 EXCLUDE_FROM_ALL) + add_library(3rdparty_zstd INTERFACE) + target_link_libraries(3rdparty_zstd INTERFACE libzstd_static) +endif() diff --git a/BUILDING.md b/BUILDING.md index 09f523d567..2de1dc1c50 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.0](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.10.0](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.0\msvc2022_64\` +- add and set the `QTDIR` environment variable, e.g. `\6.10.0\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.0\msvc2022_64\` +- add and set the `Qt6_ROOT` environment variable to the **Qt** libs path, e.g. `\6.10.0\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.0](https://www.qt.io/download-qt-installer) +- [Qt 6.10.0](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.0\msvc2022_64`, version will fill in automatically +2) add the path to your Qt installation with compiler e.g. `\6.10.0\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**) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9d2edd8365..0492d13184 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,12 @@ cmake_minimum_required(VERSION 3.28) +find_program(CCACHE_PATH ccache HINTS ENV PATH) +if(CCACHE_PATH) + message(STATUS "Using ccache: ${CCACHE_PATH}") + set(CMAKE_C_COMPILER_LAUNCHER "${CCACHE_PATH}") + set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_PATH}") +endif() + project(rpcs3 LANGUAGES C CXX) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) @@ -15,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) @@ -22,19 +35,34 @@ option(STATIC_LINK_LLVM "Link against LLVM statically. This will get set to ON i option(USE_FAUDIO "FAudio audio backend" ON) option(USE_LIBEVDEV "libevdev-based joystick support" ON) option(USE_DISCORD_RPC "Discord rich presence integration" OFF) -option(USE_SYSTEM_ZLIB "Prefer system ZLIB instead of the builtin one" ON) option(USE_VULKAN "Vulkan render backend" ON) option(USE_PRECOMPILED_HEADERS "Use precompiled headers" OFF) option(USE_SDL "Enables SDL input handler" OFF) -option(USE_SYSTEM_SDL "Prefer system SDL instead of the builtin one" ON) -option(USE_SYSTEM_FFMPEG "Prefer system ffmpeg instead of the prebuild one" OFF) -option(USE_SYSTEM_OPENAL "Prefer system OpenAL instead of the prebuild one" ON) +option(USE_SYSTEM_CUBEB "Prefer system cubeb instead of the builtin one" OFF) 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_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) +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_VULKAN_MEMORY_ALLOCATOR "Prefer system Vulkan Memory Allocator 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) 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") @@ -108,11 +136,6 @@ if(NOT CMAKE_SIZEOF_VOID_P EQUAL 8) message( FATAL_ERROR "RPCS3 can only be compiled on 64-bit platforms." ) endif() -find_program(CCACHE_FOUND ccache) -if(CCACHE_FOUND) - set(CMAKE_CXX_COMPILER_LAUNCHER ccache) -endif() - if(APPLE AND CMAKE_OSX_ARCHITECTURES STREQUAL "arm64") include_directories(/opt/homebrew/include) link_directories(/opt/homebrew/lib) @@ -142,11 +165,22 @@ if(NOT WIN32) add_compile_options(-pthread) endif() +## Look for Gamemode if its installed on Linux +if(LINUX) + ## User chooses whether to Enable GameMode features or not + if(USE_GAMEMODE) + add_compile_definitions(GAMEMODE_AVAILABLE) + endif() +endif() + # TODO: do real installation, including copying directory structure set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE "${PROJECT_BINARY_DIR}/bin") set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${PROJECT_BINARY_DIR}/bin") set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${PROJECT_BINARY_DIR}/bin") +if(BUILD_RPCS3_TESTS) + enable_testing() +endif() add_subdirectory(rpcs3) set_directory_properties(PROPERTIES VS_STARTUP_PROJECT rpcs3) diff --git a/README.md b/README.md index 669a0879a1..ac6e3cd594 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,6 @@ RPCS3 ===== -[![Azure Build Status](https://dev.azure.com/nekotekina/nekotekina/_apis/build/status/RPCS3.rpcs3?branchName=master)](https://dev.azure.com/nekotekina/nekotekina/_build?definitionId=8&_a=summary&repositoryFilter=4) -[![Cirrus CI - Base Branch Build Status](https://img.shields.io/cirrus/github/RPCS3/rpcs3/master?label=Cirrus%20CI&logo=cirrus-ci)](https://cirrus-ci.com/github/RPCS3/rpcs3) [![GitHub Actions](https://img.shields.io/github/actions/workflow/status/RPCS3/rpcs3/rpcs3.yml?branch=master&logo=github&label=Actions)](https://github.com/RPCS3/rpcs3/actions/workflows/rpcs3.yml) [![RPCS3 Discord Server](https://img.shields.io/discord/272035812277878785?color=5865F2&label=RPCS3%20Discord&logo=discord&logoColor=white)](https://discord.gg/rpcs3) @@ -22,7 +20,6 @@ If you want to contribute as a developer, please take a look at the following pa * [Coding Style](https://github.com/RPCS3/rpcs3/wiki/Coding-Style) * [Developer Information](https://github.com/RPCS3/rpcs3/wiki/Developer-Information) -* [Roadmap](https://rpcs3.net/roadmap) You should also contact any of the developers in the forums or in the Discord server to learn more about the current state of the emulator. diff --git a/Utilities/Config.cpp b/Utilities/Config.cpp index 0c2a1fa2aa..cee928def7 100644 --- a/Utilities/Config.cpp +++ b/Utilities/Config.cpp @@ -166,6 +166,55 @@ bool try_to_uint64(u64* out, std::string_view value, u64 min, u64 max) return true; } +bool try_to_uint128(u128* out, std::string_view value) +{ + if (value.empty()) + { + if (out) cfg_log.error("cfg::try_to_uint128(): called with an empty string"); + return false; + } + + u64 result_low = 0, result_high = 0; + const char* start_high64 = value.data(); + const char* end = value.data() + value.size(); + + if (start_high64[0] == '0' && value.size() >= 2 && (start_high64[1] == 'x' || start_high64[1] == 'X')) + { + // Hex support + start_high64 += 2; + } + + const char* start_low64 = end - std::min(end - start_high64, 16); + + // Hexadecimal-only + constexpr int base = 16; + + auto ret = std::from_chars(start_low64, end, result_low, base); + + if (ret.ec != std::errc() || ret.ptr != end) + { + if (out) cfg_log.error("cfg::try_to_uint128('%s'): invalid integer", value); + return false; + } + + if (start_high64 == start_low64) + { + if (out) *out = result_low; + return true; + } + + ret = std::from_chars(start_high64, start_low64, result_high, base); + + if (ret.ec != std::errc() || ret.ptr != start_low64) + { + if (out) cfg_log.error("cfg::try_to_uint128('%s'): invalid integer", value); + return false; + } + + if (out) *out = result_low + (u128{result_high} << 64); + return true; +} + std::vector cfg::make_float_range(f64 min, f64 max) { return {std::to_string(min), std::to_string(max)}; @@ -278,6 +327,19 @@ bool cfg::try_to_enum_value(u64* out, decltype(&fmt_class_string::format) f return true; } +std::string cfg::uint128::to_string(u128 value) noexcept +{ + std::string result = "0x"; + result.resize(result.size() + 32); + + for (u32 i = 0; i < 32; i++) + { + result[result.size() - 1 - i] = "0123456789ABCDEF"[static_cast(value >> (i * 4)) % 16]; + } + + return result; +} + std::vector cfg::try_to_enum_list(decltype(&fmt_class_string::format) func) { std::vector result; @@ -553,19 +615,12 @@ void cfg::node::from_default() } } -void cfg::_bool::from_default() +void cfg::node::restore_defaults() { - m_value = def; -} - -void cfg::string::from_default() -{ - m_value = def; -} - -void cfg::set_entry::from_default() -{ - m_set = {}; + for (auto& node : m_nodes) + { + node->restore_defaults(); + } } std::string cfg::map_entry::get_value(std::string_view key) diff --git a/Utilities/Config.h b/Utilities/Config.h index bd05c25220..35dcddc1a6 100644 --- a/Utilities/Config.h +++ b/Utilities/Config.h @@ -38,6 +38,7 @@ namespace cfg _enum, // cfg::_enum type _int, // cfg::_int type uint, // cfg::uint type + uint128, // cfg::uint128 type string, // cfg::string type set, // cfg::set_entry type map, // cfg::map_entry type @@ -90,6 +91,9 @@ namespace cfg // Reset defaults virtual void from_default() = 0; + // Restore default members + virtual void restore_defaults() = 0; + // Convert to string (optional) virtual std::string to_string() const { @@ -151,11 +155,15 @@ namespace cfg // Set default values void from_default() override; + + // Restore default members + void restore_defaults() override; }; class _bool final : public _base { atomic_t m_value; + bool original_def; public: bool def; @@ -163,6 +171,7 @@ namespace cfg _bool(node* owner, std::string name, bool def = false, bool dynamic = false) : _base(type::_bool, owner, std::move(name), dynamic) , m_value(def) + , original_def(def) , def(def) { } @@ -177,7 +186,15 @@ namespace cfg return m_value; } - void from_default() override; + void from_default() override + { + m_value = def; + } + + void restore_defaults() override + { + def = original_def; + } std::string to_string() const override { @@ -220,14 +237,16 @@ namespace cfg class _enum : public _base { atomic_t m_value; + T original_def; public: - const T def; + T def; - _enum(node* owner, const std::string& name, T value = {}, bool dynamic = false) + _enum(node* owner, const std::string& name, T def = {}, bool dynamic = false) : _base(type::_enum, owner, name, dynamic) - , m_value(value) - , def(value) + , m_value(def) + , original_def(def) + , def(def) { } @@ -256,6 +275,11 @@ namespace cfg m_value = def; } + void restore_defaults() override + { + def = original_def; + } + std::string to_string() const override { std::string result; @@ -300,6 +324,7 @@ namespace cfg using int_type = std::conditional_t= s32{smin} && Max <= s32{smax}, s32, s64>; atomic_t m_value; + int_type original_def; public: int_type def; @@ -311,6 +336,7 @@ namespace cfg _int(node* owner, const std::string& name, int_type def = std::min(Max, std::max(Min, 0)), bool dynamic = false) : _base(type::_int, owner, name, dynamic) , m_value(def) + , original_def(def) , def(def) { } @@ -330,6 +356,11 @@ namespace cfg m_value = def; } + void restore_defaults() override + { + def = original_def; + } + std::string to_string() const override { return std::to_string(m_value); @@ -372,6 +403,7 @@ namespace cfg using float_type = f64; atomic_t m_value; + float_type original_def; public: float_type def; @@ -383,6 +415,7 @@ namespace cfg _float(node* owner, const std::string& name, float_type def = std::min(Max, std::max(Min, 0)), bool dynamic = false) : _base(type::_int, owner, name, dynamic) , m_value(def) + , original_def(def) , def(def) { } @@ -402,6 +435,11 @@ namespace cfg m_value = def; } + void restore_defaults() override + { + def = original_def; + } + std::string to_string() const override { std::string result; @@ -464,6 +502,7 @@ namespace cfg using int_type = std::conditional_t; atomic_t m_value; + int_type original_def; public: int_type def; @@ -475,6 +514,7 @@ namespace cfg uint(node* owner, const std::string& name, int_type def = std::max(Min, 0), bool dynamic = false) : _base(type::uint, owner, name, dynamic) , m_value(def) + , original_def(def) , def(def) { } @@ -494,6 +534,11 @@ namespace cfg m_value = def; } + void restore_defaults() override + { + def = original_def; + } + std::string to_string() const override { return std::to_string(m_value); @@ -534,10 +579,91 @@ namespace cfg // Alias for 64 bit int using uint64 = uint<0, u64{umax}>; + // Unsigned 128-bit integer entry. + class uint128 final : public _base + { + using int_type = u128; + + atomic_t m_value{}; + int_type original_def = 0; + + public: + int_type def; + + uint128(node* owner, const std::string& name, int_type def = 0, bool dynamic = false) + : _base(type::uint128, owner, name, dynamic) + , m_value(def) + , original_def(def) + , def(def) + { + } + + operator int_type() const + { + return m_value; + } + + operator ullong() const + { + return static_cast(m_value.load()); + } + + int_type get() const + { + return m_value; + } + + void from_default() override + { + m_value = def; + } + + void restore_defaults() override + { + def = original_def; + } + + static std::string to_string(u128 value) noexcept; + + std::string to_string() const override + { + return to_string(m_value.load()); + } + + std::string def_to_string() const override + { + return to_string(def); + } + + bool from_string(std::string_view value, bool /*dynamic*/ = false) override + { + u128 result; + if (try_to_uint128(&result, value)) + { + m_value = result; + return true; + } + + return false; + } + + void set(u128 value) + { + m_value = value; + } + + std::vector to_list() const override + { + // Should not be used + return make_uint_range(0, 1); + } + }; + // Simple string entry with mutex class string : public _base { atomic_ptr m_value; + std::string original_def; public: std::string def; @@ -545,6 +671,7 @@ namespace cfg string(node* owner, std::string name, std::string def = {}, bool dynamic = false) : _base(type::string, owner, std::move(name), dynamic) , m_value(def) + , original_def(def) , def(std::move(def)) { } @@ -554,7 +681,15 @@ namespace cfg return *m_value.load().get(); } - void from_default() override; + void from_default() override + { + m_value = def; + } + + void restore_defaults() override + { + def = original_def; + } std::string to_string() const override { @@ -595,7 +730,14 @@ namespace cfg m_set = std::move(set); } - void from_default() override; + void from_default() override + { + m_set = {}; + } + + void restore_defaults() override + { + } std::vector to_list() const override { @@ -636,6 +778,10 @@ namespace cfg void erase(std::string_view key); void from_default() override; + + void restore_defaults() override + { + } }; class node_map_entry final : public map_entry @@ -665,6 +811,10 @@ namespace cfg void set_map(map_of_type&& map); void from_default() override; + + void restore_defaults() override + { + } }; struct device_info @@ -702,5 +852,9 @@ namespace cfg void set_map(map_of_type&& map); void from_default() override; + + void restore_defaults() override + { + } }; } diff --git a/Utilities/JITLLVM.cpp b/Utilities/JITLLVM.cpp index 2b82d57d54..166ee7cec2 100644 --- a/Utilities/JITLLVM.cpp +++ b/Utilities/JITLLVM.cpp @@ -658,7 +658,11 @@ jit_compiler::jit_compiler(const std::unordered_map& _link, co std::string result; auto null_mod = std::make_unique ("null_", *m_context); +#if LLVM_VERSION_MAJOR >= 21 && (LLVM_VERSION_MINOR >= 1 || LLVM_VERSION_MAJOR >= 22) + null_mod->setTargetTriple(llvm::Triple(jit_compiler::triple1())); +#else null_mod->setTargetTriple(jit_compiler::triple1()); +#endif std::unique_ptr mem; @@ -672,7 +676,11 @@ jit_compiler::jit_compiler(const std::unordered_map& _link, co else { mem = std::make_unique(std::move(symbols_cement)); +#if LLVM_VERSION_MAJOR >= 21 && (LLVM_VERSION_MINOR >= 1 || LLVM_VERSION_MAJOR >= 22) + null_mod->setTargetTriple(llvm::Triple(jit_compiler::triple2())); +#else null_mod->setTargetTriple(jit_compiler::triple2()); +#endif } } else diff --git a/Utilities/StrFmt.h b/Utilities/StrFmt.h index 05d98fdfab..ac503de277 100644 --- a/Utilities/StrFmt.h +++ b/Utilities/StrFmt.h @@ -392,10 +392,6 @@ namespace fmt { raw_throw_exception(src_loc, reinterpret_cast(fmt), type_info_v, fmt_args_t{fmt_unveil::get(args)...}); } - -#ifndef _MSC_VER - [[noreturn]] ~throw_exception(); -#endif }; template diff --git a/Utilities/StrUtil.h b/Utilities/StrUtil.h index c28efda863..285718ac21 100644 --- a/Utilities/StrUtil.h +++ b/Utilities/StrUtil.h @@ -28,6 +28,9 @@ bool try_to_int64(s64* out, std::string_view value, s64 min, s64 max); // Convert string to unsigned integer bool try_to_uint64(u64* out, std::string_view value, u64 min, u64 max); +// Convert string to unsigned int128_t +bool try_to_uint128(u128* out, std::string_view value); + // Convert string to float bool try_to_float(f64* out, std::string_view value, f64 min, f64 max); diff --git a/Utilities/Thread.cpp b/Utilities/Thread.cpp index c67e720b6f..9b6d250c19 100644 --- a/Utilities/Thread.cpp +++ b/Utilities/Thread.cpp @@ -106,6 +106,11 @@ thread_local u64 g_tls_wait_fail = 0; thread_local bool g_tls_access_violation_recovered = false; extern thread_local std::string(*g_tls_log_prefix)(); +namespace stx +{ + atomic_t g_launch_retainer{0}; +} + // Report error and call std::abort(), defined in main.cpp [[noreturn]] void report_fatal_error(std::string_view text, bool is_html = false, bool include_help_text = true); diff --git a/Utilities/Thread.h b/Utilities/Thread.h index 52096a2702..02e5db56ff 100644 --- a/Utilities/Thread.h +++ b/Utilities/Thread.h @@ -96,7 +96,7 @@ class thread_future thread_future* prev{}; protected: - atomic_t exec{}; + atomic_t exec{}; atomic_t done{0}; @@ -374,13 +374,23 @@ private: static const u64 process_affinity_mask; }; +#if defined(__has_cpp_attribute) +#if __has_cpp_attribute(no_unique_address) +#define NO_UNIQUE_ADDRESS [[no_unique_address]] +#else +#define NO_UNIQUE_ADDRESS +#endif +#else +#define NO_UNIQUE_ADDRESS +#endif + // Used internally template class thread_future_t : public thread_future, result_storage, Args...> { - [[no_unique_address]] decltype(std::make_tuple(std::forward(std::declval())...)) m_args; + NO_UNIQUE_ADDRESS decltype(std::make_tuple(std::forward(std::declval())...)) m_args; - [[no_unique_address]] Ctx m_func; + NO_UNIQUE_ADDRESS Ctx m_func; using future = thread_future_t; @@ -389,7 +399,7 @@ public: : m_args(std::forward(args)...) , m_func(std::forward(func)) { - thread_future::exec.raw() = +[](thread_base* tb, thread_future* tf) + thread_future::exec.raw() = +[](const thread_base* tb, thread_future* tf) { const auto _this = static_cast(tf); @@ -455,6 +465,8 @@ public: namespace stx { struct launch_retainer; + + extern atomic_t g_launch_retainer; } // Derived from the callable object Context, possibly a lambda @@ -471,6 +483,11 @@ class named_thread final : public Context, result_storage, thread_base u64 entry_point2() { + while (u32 value = stx::g_launch_retainer) + { + stx::g_launch_retainer.wait(value); + } + thread::initialize([]() { if constexpr (!result::empty) diff --git a/Utilities/address_range.h b/Utilities/address_range.h index 71fbd4a382..8da0279ef1 100644 --- a/Utilities/address_range.h +++ b/Utilities/address_range.h @@ -8,125 +8,134 @@ namespace utils { + template class address_range_vector; /** * Helpers */ - static inline u32 page_start(u32 addr) + template + T page_start(T addr) { - return addr & ~(get_page_size() - 1); + return addr & ~static_cast(get_page_size() - 1); } - static inline u32 next_page(u32 addr) + template + static inline T next_page(T addr) { - return page_start(addr) + get_page_size(); + return page_start(addr) + static_cast(get_page_size()); } - static inline u32 page_end(u32 addr) + template + static inline T page_end(T addr) { return next_page(addr) - 1; } - static inline u32 is_page_aligned(u32 val) + template + static inline T is_page_aligned(T val) { - return (val & (get_page_size() - 1)) == 0; + return (val & static_cast(get_page_size() - 1)) == 0; } /** * Address Range utility class */ + template class address_range { public: - u32 start = umax; // First address in range - u32 end = 0; // Last address + T start = umax; // First address in range + T end = 0; // Last address + + using signed_type_t = std::make_signed::type; private: // Helper constexprs - static constexpr inline bool range_overlaps(u32 start1, u32 end1, u32 start2, u32 end2) + static constexpr inline bool range_overlaps(T start1, T end1, T start2, T end2) { return (start1 <= end2 && start2 <= end1); } - static constexpr inline bool address_overlaps(u32 address, u32 start, u32 end) + static constexpr inline bool address_overlaps(T address, T start, T end) { return (start <= address && address <= end); } - static constexpr inline bool range_inside_range(u32 start1, u32 end1, u32 start2, u32 end2) + static constexpr inline bool range_inside_range(T start1, T end1, T start2, T end2) { return (start1 >= start2 && end1 <= end2); } - constexpr address_range(u32 _start, u32 _end) : start(_start), end(_end) {} + constexpr address_range(T _start, T _end) : start(_start), end(_end) {} public: // Constructors constexpr address_range() = default; - static constexpr address_range start_length(u32 _start, u32 _length) + static constexpr address_range start_length(T _start, T _length) { if (!_length) { return {}; } - return {_start, _start + (_length - 1)}; + const T _end = static_cast(_start + _length - 1); + return {_start, _end}; } - static constexpr address_range start_end(u32 _start, u32 _end) + static constexpr address_range start_end(T _start, T _end) { return {_start, _end}; } // Length - u32 length() const + T length() const { AUDIT(valid()); return end - start + 1; } - void set_length(const u32 new_length) + void set_length(const T new_length) { end = start + new_length - 1; ensure(valid()); } - u32 next_address() const + T next_address() const { return end + 1; } - u32 prev_address() const + T prev_address() const { return start - 1; } // Overlapping checks - bool overlaps(const address_range &other) const + bool overlaps(const address_range& other) const { AUDIT(valid() && other.valid()); return range_overlaps(start, end, other.start, other.end); } - bool overlaps(const u32 addr) const + bool overlaps(const T addr) const { AUDIT(valid()); return address_overlaps(addr, start, end); } - bool inside(const address_range &other) const + bool inside(const address_range& other) const { AUDIT(valid() && other.valid()); return range_inside_range(start, end, other.start, other.end); } - inline bool inside(const address_range_vector &vec) const; - inline bool overlaps(const address_range_vector &vec) const; + inline bool inside(const address_range_vector& vec) const; + inline bool overlaps(const address_range_vector& vec) const; - bool touches(const address_range &other) const + bool touches(const address_range& other) const { AUDIT(valid() && other.valid()); // returns true if there is overlap, or if sections are side-by-side @@ -134,7 +143,7 @@ namespace utils } // Utilities - s32 signed_distance(const address_range &other) const + signed_type_t signed_distance(const address_range& other) const { if (touches(other)) { @@ -144,15 +153,15 @@ namespace utils // other after this if (other.start > end) { - return static_cast(other.start - end - 1); + return static_cast(other.start - end - 1); } // this after other AUDIT(start > other.end); - return -static_cast(start - other.end - 1); + return -static_cast(start - other.end - 1); } - u32 distance(const address_range &other) const + T distance(const address_range& other) const { if (touches(other)) { @@ -170,7 +179,7 @@ namespace utils return (start - other.end - 1); } - address_range get_min_max(const address_range &other) const + address_range get_min_max(const address_range& other) const { return { std::min(valid() ? start : umax, other.valid() ? other.start : umax), @@ -178,7 +187,7 @@ namespace utils }; } - void set_min_max(const address_range &other) + void set_min_max(const address_range& other) { *this = get_min_max(other); } @@ -188,7 +197,7 @@ namespace utils return (valid() && is_page_aligned(start) && is_page_aligned(length())); } - address_range to_page_range() const + address_range to_page_range() const { AUDIT(valid()); return { page_start(start), page_end(end) }; @@ -202,7 +211,7 @@ namespace utils AUDIT(is_page_range()); } - address_range get_intersect(const address_range &clamp) const + address_range get_intersect(const address_range& clamp) const { if (!valid() || !clamp.valid()) { @@ -212,7 +221,7 @@ namespace utils return { std::max(start, clamp.start), std::min(end, clamp.end) }; } - void intersect(const address_range &clamp) + void intersect(const address_range& clamp) { if (!clamp.valid()) { @@ -238,7 +247,7 @@ namespace utils } // Comparison Operators - bool operator ==(const address_range& other) const + bool operator ==(const address_range& other) const { return (start == other.start && end == other.end); } @@ -252,21 +261,27 @@ namespace utils } }; - static inline address_range page_for(u32 addr) + using address_range16 = address_range; + using address_range32 = address_range; + using address_range64 = address_range; + + template + static inline address_range page_for(T addr) { - return address_range::start_end(page_start(addr), page_end(addr)); + return address_range::start_end(page_start(addr), page_end(addr)); } /** * Address Range Vector utility class * - * Collection of address_range objects. Allows for merging and removing ranges from the set. + * Collection of address_range objects. Allows for merging and removing ranges from the set. */ + template class address_range_vector { public: - using vector_type = std::vector; + using vector_type = std::vector>; using iterator = vector_type::iterator; using const_iterator = vector_type::const_iterator; using size_type = vector_type::size_type; @@ -280,8 +295,8 @@ namespace utils inline void clear() { data.clear(); } inline size_type size() const { return data.size(); } inline bool empty() const { return data.empty(); } - inline address_range& operator[](size_type n) { return data[n]; } - inline const address_range& operator[](size_type n) const { return data[n]; } + inline address_range& operator[](size_type n) { return data[n]; } + inline const address_range& operator[](size_type n) const { return data[n]; } inline iterator begin() { return data.begin(); } inline const_iterator begin() const { return data.begin(); } inline iterator end() { return data.end(); } @@ -289,7 +304,7 @@ namespace utils // Search for ranges that touch new_range. If found, merge instead of adding new_range. // When adding a new range, re-use invalid ranges whenever possible - void merge(const address_range &new_range) + void merge(const address_range& new_range) { // Note the case where we have // AAAA BBBB @@ -301,8 +316,8 @@ namespace utils return; } - address_range *found = nullptr; - address_range *invalid = nullptr; + address_range *found = nullptr; + address_range *invalid = nullptr; for (auto &existing : data) { @@ -347,22 +362,22 @@ namespace utils AUDIT(check_consistency()); } - void merge(const address_range_vector &other) + void merge(const address_range_vector& other) { - for (const address_range &new_range : other) + for (const address_range& new_range : other) { merge(new_range); } } // Exclude a given range from data - void exclude(const address_range &exclusion) + void exclude(const address_range& exclusion) { // Note the case where we have // AAAAAAA // EEE // where data={A} and exclusion=E. - // In this case, we need to reduce A to the head (before E starts), and then create a new address_range B for the tail (after E ends), i.e. + // In this case, we need to reduce A to the head (before E starts), and then create a new address_range B for the tail (after E ends), i.e. // AA BB // EEE @@ -371,13 +386,13 @@ namespace utils return; } - address_range *invalid = nullptr; // try to re-use an invalid range instead of calling push_back + address_range *invalid = nullptr; // try to re-use an invalid range instead of calling push_back // We use index access because we might have to push_back within the loop, which could invalidate the iterators size_type _size = data.size(); for (size_type n = 0; n < _size; ++n) { - address_range &existing = data[n]; + address_range& existing = data[n]; if (!existing.valid()) { @@ -430,7 +445,7 @@ namespace utils else { // IMPORTANT: adding to data invalidates "existing". This must be done last! - data.push_back(address_range::start_end(exclusion.next_address(), tail_end)); + data.push_back(address_range::start_end(exclusion.next_address(), tail_end)); } } } @@ -438,9 +453,9 @@ namespace utils AUDIT(!overlaps(exclusion)); } - void exclude(const address_range_vector &other) + void exclude(const address_range_vector& other) { - for (const address_range &exclusion : other) + for (const address_range& exclusion : other) { exclude(exclusion); } @@ -478,25 +493,25 @@ namespace utils } // Test for overlap with a given range - bool overlaps(const address_range &range) const + bool overlaps(const address_range& range) const { - return std::any_of(data.cbegin(), data.cend(), [&range](const address_range& cur) + return std::any_of(data.cbegin(), data.cend(), [&range](const address_range& cur) { return cur.valid() && cur.overlaps(range); }); } - // Test for overlap with a given address_range vector - bool overlaps(const address_range_vector &other) const + // Test for overlap with a given address_range vector + bool overlaps(const address_range_vector& other) const { - for (const address_range &rng1 : data) + for (const address_range& rng1 : data) { if (!rng1.valid()) { continue; } - for (const address_range &rng2 : other.data) + for (const address_range& rng2 : other.data) { if (!rng2.valid()) { @@ -513,36 +528,56 @@ namespace utils } // Test if a given range is fully contained inside this vector - bool contains(const address_range &range) const + bool contains(const address_range& range) const { - return std::any_of(this->begin(), this->end(), [&range](const address_range& cur) + return std::any_of(this->begin(), this->end(), [&range](const address_range& cur) { return cur.valid() && cur.inside(range); }); } // Test if all ranges in this vector are full contained inside a specific range - bool inside(const address_range &range) const + bool inside(const address_range& range) const { - return std::all_of(this->begin(), this->end(), [&range](const address_range& cur) + return std::all_of(this->begin(), this->end(), [&range](const address_range& cur) { return !cur.valid() || cur.inside(range); }); } + + // Count valid entries + usz valid_count() const + { + usz count = 0; + for (const auto& e : data) + { + if (e.valid()) + { + count++; + } + } + return count; + } }; // These declarations must be done after address_range_vector has been defined - bool address_range::inside(const address_range_vector &vec) const + template + bool address_range::inside(const address_range_vector& vec) const { return vec.contains(*this); } - bool address_range::overlaps(const address_range_vector &vec) const + template + bool address_range::overlaps(const address_range_vector& vec) const { return vec.overlaps(*this); } + using address_range_vector16 = address_range_vector; + using address_range_vector32 = address_range_vector; + using address_range_vector64 = address_range_vector; + } // namespace utils @@ -551,9 +586,9 @@ namespace std static_assert(sizeof(usz) >= 2 * sizeof(u32), "usz must be at least twice the size of u32"); template <> - struct hash + struct hash { - usz operator()(const utils::address_range& k) const + usz operator()(const utils::address_range32& k) const { // we can guarantee a unique hash since our type is 64 bits and usz as well return (usz{ k.start } << 32) | usz{ k.end }; diff --git a/Utilities/deferred_op.hpp b/Utilities/deferred_op.hpp new file mode 100644 index 0000000000..7186cea2d4 --- /dev/null +++ b/Utilities/deferred_op.hpp @@ -0,0 +1,27 @@ +#pragma once + +// Generic deferred routine wrapper +// Use-case is similar to "defer" statement in other languages, just invokes a callback when the object goes out of scope + +#include + +namespace utils +{ + template + requires std::is_invocable_v + class deferred_op + { + public: + deferred_op(F&& callback) + : m_callback(callback) + {} + + ~deferred_op() + { + m_callback(); + } + + private: + F m_callback; + }; +} diff --git a/Utilities/git-version-gen.cmd b/Utilities/git-version-gen.cmd index 7de2ebf28f..5d9254171d 100644 --- a/Utilities/git-version-gen.cmd +++ b/Utilities/git-version-gen.cmd @@ -94,8 +94,6 @@ if defined BUILD_SOURCEBRANCHNAME ( rem // This must be a CI build - echo SYSTEM_PULLREQUEST_SOURCEBRANCH: %SYSTEM_PULLREQUEST_SOURCEBRANCH% - if defined BUILD_REPOSITORY_NAME ( echo BUILD_REPOSITORY_NAME: %BUILD_REPOSITORY_NAME% ) else ( @@ -110,9 +108,7 @@ if defined BUILD_SOURCEBRANCHNAME ( rem // These environment variables are defined by CI rem // BUILD_REPOSITORY_NAME will look like "RPCS3/rpcs3" - rem // SYSTEM_PULLREQUEST_SOURCEBRANCH will look like "master" rem // BUILD_SOURCEBRANCHNAME will look like "master" - rem // See https://docs.microsoft.com/en-us/azure/devops/pipelines/build/variables set GIT_FULL_BRANCH=%BUILD_REPOSITORY_NAME%/%BUILD_SOURCEBRANCHNAME% echo GIT_FULL_BRANCH: !GIT_FULL_BRANCH! @@ -129,12 +125,12 @@ if defined BUILD_SOURCEBRANCHNAME ( rem // This must be a pull request or a build from a fork. echo Assuming pull request build - if "%SYSTEM_PULLREQUEST_SOURCEBRANCH%"=="master" ( + if "%BUILD_SOURCEBRANCHNAME%"=="master" ( rem // If pull request comes from a master branch, GIT_BRANCH = username/branch in order to distinguish from upstream/master for /f "tokens=1* delims=/" %%a in ("%BUILD_REPOSITORY_NAME%") do set user=%%a - set "GIT_BRANCH=!user!/%SYSTEM_PULLREQUEST_SOURCEBRANCH%" + set "GIT_BRANCH=!user!/%BUILD_SOURCEBRANCHNAME%" ) else ( - set GIT_BRANCH=%SYSTEM_PULLREQUEST_SOURCEBRANCH% + set GIT_BRANCH=%BUILD_SOURCEBRANCHNAME% ) rem // Make GIT_VERSION the last commit (shortened); Don't include commit count on non-release builds diff --git a/Utilities/transactional_storage.h b/Utilities/transactional_storage.h index 55e8aa9f95..1be923233e 100644 --- a/Utilities/transactional_storage.h +++ b/Utilities/transactional_storage.h @@ -84,9 +84,8 @@ public: transactional_storage& operator=(const transactional_storage&) = delete; transactional_storage(transactional_storage&& other) + : pool(std::move(other.pool)) { - pool = std::move(other.pool); - std::unique_lock lock_other{other.current_mutex}; const std::shared_ptr other_current = other.current; other.current = nullptr; diff --git a/azure-pipelines.yml b/azure-pipelines.yml deleted file mode 100644 index 97970a521d..0000000000 --- a/azure-pipelines.yml +++ /dev/null @@ -1,260 +0,0 @@ -trigger: - branches: - include: - - master - tags: - exclude: - - '*' -pr: - branches: - include: - - master -jobs: -# - job: Linux_Build -# strategy: -# matrix: -# Clang: -# COMPILER: clang -# GCC: -# COMPILER: gcc -# variables: -# CCACHE_DIR: $(Pipeline.Workspace)/ccache -# CI_HAS_ARTIFACTS: true -# UPLOAD_COMMIT_HASH: d812f1254a1157c80fd402f94446310560f54e5f -# UPLOAD_REPO_FULL_NAME: "RPCS3/rpcs3-binaries-linux" -# DEPLOY_APPIMAGE: true -# APPDIR: "/rpcs3/build/appdir" -# ARTDIR: "/root/artifacts" -# RELEASE_MESSAGE: "/rpcs3/GitHubReleaseMessage.txt" - -# pool: -# vmImage: 'ubuntu-latest' - -# steps: -# - task: Cache@2 -# inputs: -# key: ccache | $(Agent.OS) | $(COMPILER) | $(Build.SourceVersion) -# restoreKeys: | -# ccache | $(Agent.OS) | $(COMPILER) -# path: $(CCACHE_DIR) -# displayName: ccache - -# - bash: | -# docker pull --quiet rpcs3/rpcs3-ci-jammy:1.6 -# docker run \ -# -v $(pwd):/rpcs3 \ -# --env-file .ci/docker.env \ -# -v $CCACHE_DIR:/root/.ccache \ -# -v $BUILD_ARTIFACTSTAGINGDIRECTORY:/root/artifacts \ -# rpcs3/rpcs3-ci-jammy:1.6 \ -# /rpcs3/.ci/build-linux.sh -# displayName: Docker setup and build - -# - publish: $(Build.ArtifactStagingDirectory) -# condition: succeeded() -# artifact: RPCS3 for Linux ($(COMPILER)) - -# - bash: | -# COMM_TAG=$(awk '/version{.*}/ { printf("%d.%d.%d", $5, $6, $7) }' ./rpcs3/rpcs3_version.cpp) -# COMM_COUNT=$(git rev-list --count HEAD) -# COMM_HASH=$(git rev-parse --short=8 HEAD) - -# export AVVER="${COMM_TAG}-${COMM_COUNT}" - -# .ci/github-upload.sh -# condition: and(ne(variables['Build.Reason'], 'PullRequest'), eq(variables['Build.Repository.Name'], 'RPCS3/rpcs3'), eq(variables['Build.SourceBranch'], 'refs/heads/master'), eq(variables['COMPILER'], 'clang')) -# displayName: Push build to GitHub -# env: -# RPCS3_TOKEN: $(RPCS3-Token) - -# - job: Windows_Build -# variables: -# COMPILER: msvc -# QT_VER_MAIN: '6' -# QT_VER: '6.9.0' -# QT_VER_MSVC: 'msvc2022' -# QT_DATE: '202503301022' -# QTDIR: C:\Qt\$(QT_VER)\$(QT_VER_MSVC)_64 -# LLVM_VER: '19.1.7' -# VULKAN_VER: '1.3.268.0' -# VULKAN_SDK_SHA: '8459ef49bd06b697115ddd3d97c9aec729e849cd775f5be70897718a9b3b9db5' -# VULKAN_SDK: C:\VulkanSDK\$(VULKAN_VER) -# CCACHE_SHA: '6252f081876a9a9f700fae13a5aec5d0d486b28261d7f1f72ac11c7ad9df4da9' -# CCACHE_BIN_DIR: 'C:\ccache_bin' -# CCACHE_DIR: 'C:\ccache' -# CCACHE_INODECACHE: 'true' -# CCACHE_SLOPPINESS: 'time_macros' -# DEPS_CACHE_DIR: ./dependency_cache -# UPLOAD_COMMIT_HASH: 7d09e3be30805911226241afbb14f8cdc2eb054e -# UPLOAD_REPO_FULL_NAME: "RPCS3/rpcs3-binaries-win" - -# pool: -# vmImage: "windows-latest" - -# steps: -# - bash: .ci/get_keys-windows.sh -# displayName: Get Cache Keys - -# - task: Cache@2 -# inputs: -# key: ccache | $(Agent.OS) | $(COMPILER) | "$(Build.SourceVersion)" -# path: $(CCACHE_DIR) -# restoreKeys: -# ccache | $(Agent.OS) | $(COMPILER) -# displayName: Build Ccache - -# - task: Cache@2 -# inputs: -# key: $(Agent.OS) | $(COMPILER) | "$(QT_VER)" | $(VULKAN_SDK_SHA) | $(CCACHE_SHA) | llvm.lock -# path: $(DEPS_CACHE_DIR) -# displayName: Dependencies Cache - -# - bash: .ci/setup-windows.sh -# displayName: Download and unpack dependencies - -# - bash: .ci/export-azure-vars.sh -# displayName: Export Variables - -# - task: VSBuild@1 -# inputs: -# solution: 'rpcs3.sln' -# maximumCpuCount: true -# platform: x64 -# configuration: 'Release' -# msbuildArgs: /p:CLToolPath=$(CCACHE_BIN_DIR) /p:UseMultiToolTask=true /p:CustomAfterMicrosoftCommonTargets="$(Build.SourcesDirectory)\buildfiles\msvc\ci_only.targets" -# displayName: Compile RPCS3 - -# - bash: .ci/deploy-windows.sh -# displayName: Pack up build artifacts - -# - publish: $(Build.ArtifactStagingDirectory) -# condition: succeeded() -# artifact: RPCS3 for Windows - -# - bash: .ci/github-upload.sh -# condition: and(ne(variables['Build.Reason'], 'PullRequest'), eq(variables['Build.Repository.Name'], 'RPCS3/rpcs3'), eq(variables['Build.SourceBranch'], 'refs/heads/master')) -# displayName: Push build to GitHub -# env: -# RPCS3_TOKEN: $(RPCS3-Token) - -- job: Mac_Build_x86_64 - timeoutInMinutes: 180 - variables: - CCACHE_DIR: "/tmp/ccache_dir" - CCACHE_MAXSIZE: 300M - CI_HAS_ARTIFACTS: true - UPLOAD_COMMIT_HASH: 51ae32f468089a8169aaf1567de355ff4a3e0842 - UPLOAD_REPO_FULL_NAME: "RPCS3/rpcs3-binaries-mac" - RELEASE_MESSAGE: "../GitHubReleaseMessage.txt" - ARTDIR: $(Build.ArtifactStagingDirectory) - QT_VER: '6.7.3' - QT_VER_MAIN: '6' - LLVM_COMPILER_VER: '19' - - pool: - vmImage: "macOS-14" - - steps: - - task: Cache@2 - inputs: - key: ccache | "$(Agent.OS)" | "$(Agent.OSArchitecture)" | "$(Build.SourceVersion)" - path: $(CCACHE_DIR) - restoreKeys: | - ccache | "$(Agent.OS)" | "$(Agent.OSArchitecture)" - displayName: Ccache cache - - - task: Cache@2 - inputs: - key: qt | "$(Agent.OS)" | "$(Agent.OSArchitecture)" | "$(QT_VER)" - path: /tmp/Qt - restoreKeys: | - qt | "$(Agent.OS)" | "$(Agent.OSArchitecture)" | "$(QT_VER)" - displayName: Qt cache - - # - task: Cache@2 - # inputs: - # key: brew | "$(Agent.OS)" - # path: /Users/runner/Library/Caches/Homebrew - # restoreKeys: | - # brew | "$(Agent.OS)" - # displayName: Homebrew cache - - - bash: | - chmod +x ".ci/build-mac.sh" - chmod +x ".ci/deploy-mac.sh" - chmod +x ".ci/optimize-mac.sh" - ".ci/build-mac.sh" - displayName: Build macOS (x86_64) - - - publish: $(Build.ArtifactStagingDirectory) - condition: succeeded() - artifact: RPCS3 for Mac (Intel) - - - bash: | - source './.ci/export-cirrus-vars.sh' - .ci/github-upload.sh - condition: and(ne(variables['Build.Reason'], 'PullRequest'), eq(variables['Build.Repository.Name'], 'RPCS3/rpcs3'), eq(variables['Build.SourceBranch'], 'refs/heads/master')) - displayName: Push build to GitHub - env: - RPCS3_TOKEN: $(RPCS3-Token) - -- job: Mac_Build_arm64 - timeoutInMinutes: 180 - variables: - CCACHE_DIR: "/tmp/ccache_dir" - CCACHE_MAXSIZE: 300M - CI_HAS_ARTIFACTS: true - UPLOAD_COMMIT_HASH: 8e21bdbc40711a3fccd18fbf17b742348b0f4281 - UPLOAD_REPO_FULL_NAME: "RPCS3/rpcs3-binaries-mac-arm64" - RELEASE_MESSAGE: "../GitHubReleaseMessage.txt" - ARTDIR: $(Build.ArtifactStagingDirectory) - QT_VER: '6.7.3' - QT_VER_MAIN: '6' - LLVM_COMPILER_VER: '19' - - pool: - vmImage: "macOS-14" - - steps: - - task: Cache@2 - inputs: - key: ccache | "$(Agent.OS)" | "$(Agent.OSArchitecture)" | "$(Build.SourceVersion)" - path: $(CCACHE_DIR) - restoreKeys: | - ccache | "$(Agent.OS)" | "$(Agent.OSArchitecture)" - displayName: Ccache cache - - - task: Cache@2 - inputs: - key: qt | "$(Agent.OS)" | "$(Agent.OSArchitecture)" | "$(QT_VER)" - path: /tmp/Qt - restoreKeys: | - qt | "$(Agent.OS)" | "$(Agent.OSArchitecture)" | "$(QT_VER)" - displayName: Qt cache - - # - task: Cache@2 - # inputs: - # key: brew | "$(Agent.OS)" - # path: /Users/runner/Library/Caches/Homebrew - # restoreKeys: | - # brew | "$(Agent.OS)" - # displayName: Homebrew cache - - - bash: | - chmod +x ".ci/build-mac-arm64.sh" - chmod +x ".ci/deploy-mac-arm64.sh" - chmod +x ".ci/optimize-mac.sh" - ".ci/build-mac-arm64.sh" - displayName: Build macOS (arm64) - - - publish: $(Build.ArtifactStagingDirectory) - condition: succeeded() - artifact: RPCS3 for Mac (Apple Silicon) - - - bash: | - source './.ci/export-cirrus-vars.sh' - .ci/github-upload.sh - condition: and(ne(variables['Build.Reason'], 'PullRequest'), eq(variables['Build.Repository.Name'], 'RPCS3/rpcs3'), eq(variables['Build.SourceBranch'], 'refs/heads/master')) - displayName: Push build to GitHub - env: - RPCS3_TOKEN: $(RPCS3-Token) diff --git a/bin/GuiConfigs/Windows 11 (Dark Mode) by GustavoGraziano.qss b/bin/GuiConfigs/Windows 11 (Dark Mode) by GustavoGraziano.qss new file mode 100644 index 0000000000..20b8638862 --- /dev/null +++ b/bin/GuiConfigs/Windows 11 (Dark Mode) by GustavoGraziano.qss @@ -0,0 +1,1090 @@ +/* + Windows 11 (Dark Mode) + by GustavoGraziano + GitHub: "https://github.com/GustavoGraziano" + (2025/09/18) +*/ + + + + + +/* ----- GLOBAL QWIDGET ----- */ + +/* RESET */ +QWidget { + font-family: "Segoe UI"; + color: #FFFFFF; + background-color: transparent; + alternate-background-color: #2B2B2B; + border: none; +} + +QDockWidget QWidget, QTabWidget::pane { + border: none; +} + +QMainWindow { + background-color: #191919; +} + +QDialog { + background-color: #191919; +} + + +QWidget#cg_disasm, QWidget#trophy_manager { + background-color: #222222; +} + +QWidget#trophy_notification_frame { + background-color: #191919; + color: #FFFFFF; +} + + +/* TOOLTIP */ +QToolTip { + background-color: #2C2C2C; + color: #FFFFFF; + border: 1px solid #1C1C1C; + padding: 2px 6px; +} + + +/* DOCK */ +QDockWidget { + color: #FFFFFF; + titlebar-normal-icon: url("GuiConfigs/dark/window-undock.svg"); + titlebar-close-icon: url("GuiConfigs/dark/window-close.svg"); +} + +QDockWidget::title { + background-color: #222222; + border-width: 1px 0px; + border-style: solid; + border-color: #3A3A3A; + padding: 6px 0px; +} + +QDockWidget::float-button, QDockWidget::close-button { + background-color: transparent; +} + +QDockWidget::float-button:hover { + background-color: #424242; + border: none; + border-radius: 4px; +} + +QDockWidget::close-button:hover { + background-color: #E81123; + border: none; + border-radius: 4px; +} + + +/* TAB BAR */ +QTabBar::tab { + color: #FFFFFF; + padding: 8px 16px; + border: none; + border-radius: 6px; + margin: 0 8px 16px 0; + min-width: 65px; +} + +QTabBar::tab:hover { + background-color: #2D2D2D; +} + +QTabBar::tab:selected { + font-weight: bold; + color: #4CC2FF; + background-color: #2D2D2D; +} + +QTabBar::tab:selected:hover { + background-color: #292929; +} + + +/* SCROOL BAR */ +QScrollBar:vertical { + background-color: transparent; + width: 6px; + margin: 0px; +} + +QScrollBar::groove:vertical { + background-color: transparent; + border-radius: 3px; + margin: 0px; + width: 6px; +} + +QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { + height: 0px; + width: 0px; + background: none; + border: none; +} + +QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { + background-color: transparent; +} + +QScrollBar::handle:vertical { + background-color: #959595; + min-height: 20px; + border-radius: 2px; + border: none; + margin: 0px 1px; +} + +QScrollBar::handle:vertical:hover { + background-color: #959595; + border-radius: 3px; + min-width: 12px; + margin: 0px; +} + + +QScrollBar:horizontal { + background-color: transparent; + height: 6px; + margin: 0px; +} + +QScrollBar::groove:horizontal { + background-color: transparent; + border-radius: 3px; + margin: 0px; + height: 6px; +} + +QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal { + height: 0px; + width: 0px; + background: none; + border: none; +} + +QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { + background-color: transparent; +} + +QScrollBar::handle:horizontal { + background-color: #959595; + min-width: 20px; + border-radius: 2px; + border: none; + margin: 1px 0px; +} + +QScrollBar::handle:horizontal:hover { + background-color: #959595; + border-radius: 3px; + min-height: 12px; + margin: 0px; +} + + +/* PROGRESS BAR */ +QProgressBar { + font-size: 10px; + font-weight: bold; + color: #FFFFFF; + background-color: #373737; + border-radius: 4px; + text-align: center; +} + +QProgressBar::chunk { + background-color: #4CAF50; + border-radius: 4px; +} + +QProgressBar:disabled { + background-color: #3F3F3F; + border: 1px solid transparent; + color: #7E7E7E; +} + +QProgressBar::chunk:disabled { + background-color: #555555; +} + +QProgressBar:indeterminate { + background-color: #373737; +} + +QProgressBar::chunk:indeterminate { + background-color: #4CAF50; +} + + +/* CALENDAR */ +QCalendarWidget QWidget#qt_calendar_navigationbar { + background: #222222; + border-bottom: 1px solid #3A3A3A; +} + +QCalendarWidget QToolButton { + background-color: transparent; + border: none; + border-radius: 4px; +} + +QCalendarWidget QToolButton:hover { + color: #FFFFFF; + background-color: #383838; +} + +QCalendarWidget QToolButton#qt_calendar_prevmonth, QCalendarWidget QToolButton#qt_calendar_nextmonth { + width: 6px; + height: 6px; + padding: 0px 10px; +} + +QCalendarWidget QToolButton#qt_calendar_prevmonth { + qproperty-icon: url("GuiConfigs/dark/arrow-prev.svg"); +} + +QCalendarWidget QToolButton#qt_calendar_nextmonth { + qproperty-icon: url("GuiConfigs/dark/arrow-next.svg"); +} + +QCalendarWidget QAbstractItemView { + color: #FFFFFF; + background-color: #222222; + alternate-background-color: #222222; +} + +QCalendarWidget QAbstractItemView::item:selected { + color: #4CC2FF; + background-color: #383838; +} + +QCalendarWidget QAbstractItemView::item:hover { + color: #4CC2FF; +} + +QCalendarWidget QAbstractItemView:focus { + outline: none; +} + +QCalendarWidget QToolButton#qt_calendar_monthbutton::menu-indicator, QCalendarWidget QToolButton#qt_calendar_yearbutton::menu-indicator { + image: none; +} + + +/* TABLE / TABLE HEADER */ +QTableView { + color: #FFFFFF; + background-color: #191919; + border: none; + border-radius: 6px; + selection-background-color: #4D4D4D; +} + +QTableView::item:selected { + color: #FFFFFF; + background-color: #4D4D4D; +} + + +QHeaderView::section { + color: #DEDEDE; + background-color: #191919; + border: none; + padding: 6px 8px 0px; + border-left: 1px solid #636363; +} + +QHeaderView::section:first { + border-left: none; + border-top-left-radius: 6px; +} + +QHeaderView::section:hover { + background-color: #4D4D4D; +} + +QHeaderView::section:last { + border-top-right-radius: 6px; +} + + + + + +/* ----- GLOBAL FORM ----- */ + +/* BUTTONS */ +QPushButton { + color: #FFFFFF; + background-color: #373737; + border: 2px solid #3F3F3F; + border-radius: 4px; + min-width: 120px; + padding: 4px 8px; +} + +QPushButton:hover { + background-color: #3C3C3C; + border-color: #3F3F3F; +} + +QPushButton:pressed { + color: #D0D0D0; + background-color: #323232; + border-color: #3A3A3A; +} + +QPushButton:disabled { + color: #7E7E7E; + background-color: #343434; + border-color: #3A3A3A; +} + + +QDialog QDialogButtonBox QPushButton { + color: #000000; + background-color: #4CC2FF; + border-color: #5AC7FF; +} + +QDialog QDialogButtonBox QPushButton:hover { + background-color: #48B2E9; + border-color: #56B8EB; +} + +QDialog QDialogButtonBox QPushButton:pressed { + color: #22526A; + background-color: #45A4D5; + border-color: #45A4D5; +} + +QDialog QDialogButtonBox QPushButton:disabled { + color: #ABABAB; + background-color: #4C4C4C; + border-color: #4C4C4C; +} + + +/* RADIO BUTTON / CHECKBOX / LIST / TREE VIEW */ +QRadioButton, +QCheckBox, +QListWidget { + spacing: 6px; +} + + +QRadioButton::indicator, +QCheckBox::indicator, +QListWidget::indicator { + width: 14px; + height: 14px; +} + + +QListWidget, +QTreeView { + background-color: #222222; + border: 1px solid #555555; + border-radius: 4px; + padding: 4px; + outline: none; +} + +QListWidget::item, +QTreeWidget::item { + color: #FFFFFF; + border-radius: 4px; + padding: 4px; +} + +QListWidget::item:selected, QListWidget::item:selected:hover, +QTreeWidget::item:selected, QTreeWidget::item:selected:hover { + color: #FFFFFF; + background-color: #555555; +} + +QListWidget::item:hover, +QTreeWidget::item:hover { + background-color: #333333; +} + +QListWidget::item:disabled, +QTreeWidget::item:disabled { + color: #7E7E7E; +} + +QListWidget::item:alternate, +QTreeWidget::item:alternate { + background-color: #2A2A2A; +} + + +QTreeView::branch { + background: #222222; +} + + +QRadioButton::indicator:unchecked { + image: url("GuiConfigs/dark/radio-unchecked.svg"); +} + +QRadioButton::indicator:unchecked:hover { + image: url("GuiConfigs/dark/radio-unchecked-hover.svg"); +} + +QRadioButton::indicator:unchecked:pressed { + image: url("GuiConfigs/dark/radio-unchecked-pressed.svg"); +} + +QRadioButton::indicator:checked { + image: url("GuiConfigs/dark/radio-checked.svg"); +} + +QRadioButton::indicator:checked:hover { + image: url("GuiConfigs/dark/radio-checked-hover.svg"); +} + +QRadioButton::indicator:checked:pressed { + image: url("GuiConfigs/dark/radio-checked-pressed.svg"); +} + +QRadioButton::indicator:unchecked:disabled { + image: url("GuiConfigs/dark/radio-disabled.svg"); +} + +QRadioButton::indicator:checked:disabled { + image: url("GuiConfigs/dark/radio-checked-disabled.svg"); +} + + +QCheckBox::indicator:unchecked, +QListWidget::indicator:unchecked { + image: url("GuiConfigs/dark/checkbox-unchecked.svg"); +} + +QCheckBox::indicator:unchecked:hover, +QListWidget::indicator:unchecked:hover { + image: url("GuiConfigs/dark/checkbox-unchecked-hover.svg"); +} + +QCheckBox::indicator:unchecked:pressed, +QListWidget::indicator:unchecked:pressed { + image: url("GuiConfigs/dark/checkbox-unchecked-pressed.svg"); +} + +QCheckBox::indicator:checked, +QListWidget::indicator:checked { + image: url("GuiConfigs/dark/checkbox-checked.svg"); +} + +QCheckBox::indicator:checked:hover, +QListWidget::indicator:checked:hover { + image: url("GuiConfigs/dark/checkbox-checked-hover.svg"); +} + +QCheckBox::indicator:checked:pressed, +QListWidget::indicator:checked:pressed { + image: url("GuiConfigs/dark/checkbox-checked-pressed.svg"); +} + +QCheckBox::indicator:indeterminate, +QListWidget::indicator:indeterminate { + image: url("GuiConfigs/dark/checkbox-indeterminate.svg"); +} + +QCheckBox::indicator:indeterminate:hover, +QListWidget::indicator:indeterminate:hover { + image: url("GuiConfigs/dark/checkbox-indeterminate-hover.svg"); +} + +QCheckBox::indicator:indeterminate:pressed, +QListWidget::indicator:indeterminate:pressed { + image: url("GuiConfigs/dark/checkbox-indeterminate-pressed.svg"); +} + +QCheckBox::indicator:unchecked:disabled, +QListWidget::indicator:unchecked:disabled { + image: url("GuiConfigs/dark/checkbox-disabled.svg"); +} + +QCheckBox::indicator:checked:disabled, +QListWidget::indicator:checked:disabled { + image: url("GuiConfigs/dark/checkbox-checked-disabled.svg"); +} + +QCheckBox::indicator:indeterminate:disabled, +QListWidget::indicator:indeterminate:disabled { + image: url("GuiConfigs/dark/checkbox-indeterminate-disabled.svg"); +} + + +QTreeView::branch:has-children:!has-siblings:closed, +QTreeView::branch:closed:has-children:has-siblings { + image: url("GuiConfigs/dark/list-arrow-closed.svg"); +} + +QTreeView::branch:open:has-children:!has-siblings, +QTreeView::branch:open:has-children:has-siblings { + image: url("GuiConfigs/dark/list-arrow-open.svg"); +} + +QTreeView:disabled::branch:has-children:!has-siblings:closed, +QTreeView:disabled::branch:closed:has-children:has-siblings { + image: url("GuiConfigs/dark/list-arrow-closed-disabled.svg"); +} + +QTreeView:disabled::branch:open:has-children:!has-siblings, +QTreeView:disabled::branch:open:has-children:has-siblings { + image: url("GuiConfigs/dark/list-arrow-open-disabled.svg"); +} + + +/* COMBO BOX / DATE TIME */ +QComboBox, +QDateTimeEdit { + color: #FFFFFF; + background-color: #3E3E3E; + border: 1px solid #454545; + border-radius: 4px; + padding: 4px 8px; +} + +QComboBox:hover, +QDateTimeEdit:hover { + background-color: #434343; + border: 1px solid #454545; +} + +QComboBox:pressed, +QDateTimeEdit:pressed { + color: #D2D2D2; + background-color: #383838; + border: 1px solid #404040; + border-bottom: none; + border-bottom-left-radius: 0px; + border-bottom-right-radius: 0px; +} + +QComboBox:disabled, +QDateTimeEdit:disabled { + color: #7E7E7E; + background-color: #343434; + border: 1px solid #3A3A3A; +} + +QComboBox::drop-down, +QDateTimeEdit::drop-down { + subcontrol-origin: padding; + subcontrol-position: center right; + width: 10px; + height: 10px; + background: none; + border: none; + padding-right: 8px; + image: url("GuiConfigs/dark/arrow-down.svg"); +} + +QComboBox::drop-down:on, +QDateTimeEdit::drop-down:on { + image: url("GuiConfigs/dark/arrow-up.svg"); +} + +QComboBox::drop-down:disabled, +QDateTimeEdit::drop-down:disabled { + image: url("GuiConfigs/dark/arrow-down-disabled.svg"); +} + + +QComboBox QAbstractItemView { + color: #FFFFFF; + background-color: #222222; + border: 1px solid #3A3A3A; + border-radius: 4px; + padding: 4px; +} + +QComboBox QAbstractItemView::item { + color: #FFFFFF; + padding: 4px 6px; + background-color: transparent; + border-radius: 4px; +} + +QComboBox QAbstractItemView::item:selected { + background-color: #383838; +} + +QComboBox QAbstractItemView:focus { + outline: none; +} + + +/* SPIN BOX / DOUBLE SPIN BOX */ +QSpinBox, +QDoubleSpinBox { + color: #FFFFFF; + background-color: #3E3E3E; + border: 1px solid #454545; + border-radius: 4px; + padding: 5px 8px; +} + +QSpinBox::up-button, QSpinBox::down-button, +QDoubleSpinBox::up-button, QDoubleSpinBox::down-button { + border-radius: 4px; + width: 8px; + height: 8px; + padding: 3px 4px; +} + +QSpinBox::up-button:hover, QSpinBox::down-button:hover, +QDoubleSpinBox::up-button:hover, QDoubleSpinBox::down-button:hover { + background-color: #383838; +} + +QSpinBox:disabled, +QDoubleSpinBox:disabled { + color: #7E7E7E; + background-color: #343434; + border: 1px solid #3A3A3A; +} + +QSpinBox::up-button, +QDoubleSpinBox::up-button { + image: url("GuiConfigs/dark/arrow-up.svg"); +} + +QSpinBox::down-button, +QDoubleSpinBox::down-button { + image: url("GuiConfigs/dark/arrow-down.svg"); +} + +QSpinBox::up-button:disabled, +QDoubleSpinBox::up-button:disabled { + image: url("GuiConfigs/dark/arrow-up-disabled.svg"); +} + +QSpinBox::down-button:disabled, +QDoubleSpinBox::down-button:disabled { + image: url("GuiConfigs/dark/arrow-down-disabled.svg"); +} + + +/* SLIDER */ +QSlider::groove:horizontal { + background-color: #9F9F9F; + border-radius: 2px; + height: 4px; +} + +QSlider::groove:horizontal:disabled { + background-color: #5F5F5F; +} + +QSlider::handle:horizontal { + border-radius: 9px; + width: 18px; + height: 18px; + margin: -7px 0px; + image: url("GuiConfigs/dark/slider-handle.svg"); +} + +QSlider::handle:horizontal:hover { + image: url("GuiConfigs/dark/slider-handle-hover.svg"); +} + +QSlider::handle:horizontal:pressed { + image: url("GuiConfigs/dark/slider-handle-pressed.svg"); +} + +QSlider::handle:horizontal:disabled { + image: url("GuiConfigs/dark/slider-handle-disabled.svg"); +} + + +/* LINE EDIT */ +QLineEdit { + color: #FFFFFF; + background-color: #383838; + border: 1px solid transparent; + border-radius: 4px; + padding: 5px 8px; +} + +QLineEdit:hover { + background-color: #3D3D3D; + border-color: transparent; +} + +QLineEdit:focus { + background-color: #222222; + border: 1px solid #3A3A3A; + border-bottom: 2px solid #4CC2FF; +} + + +QDockWidget QLineEdit { + border: 1px solid transparent; + border-radius: 0; +} + +QDockWidget QLineEdit:focus { + background: #383838; + border: none; +} + + +/* GROUP BOX */ +QGroupBox { + background-color: #2B2B2B; + border: 1px solid #1D1D1D; + border-radius: 6px; + padding-top: 32px; +} + +QGroupBox::title { + subcontrol-origin: margin; + subcontrol-position: top center; + background-color: transparent; + border-bottom: 1px solid #1D1D1D; + padding: 8px 16px; + min-width: 9999px; +} + +QGroupBox QGroupBox::title { + border: none; +} + + + + + +/* ----- MAIN WINDOW ----- */ + +/* MENU BAR / MENU*/ +QMenuBar { + background-color: #2C2C2C; +} + +QMenuBar::item { + color: #FFFFFF; + background-color: transparent; + padding: 4px 8px; +} + +QMenuBar::item:selected { + background-color: #383838; +} + +QMenuBar::item:pressed { + background-color: #343434; +} + + +QMenu { + background-color: #2C2C2C; + border: 1px solid #131313; + padding: 4px 0px; +} + +QMenu::item { + color: #FFFFFF; + background-color: transparent; + border-radius: 4px; + padding: 8px 20px; + margin: 0px 4px; +} + +QMenu::item:selected { + background-color: #383838; +} + +QMenu::item:disabled { + color: #666666; + background-color: transparent; +} + +QMenu::separator { + height: 1px; + background: #131313; + margin: 4px 0px; +} + +QMenu::indicator, QMenu::right-arrow { + width: 8px; + height: 8px; +} + +QMenu::indicator { + padding-left: 6px; +} + +QMenu::indicator:checked { + image: url("GuiConfigs/dark/check-mark.svg"); +} + +QMenu::indicator:unchecked { + image: url("GuiConfigs/dark/empty.svg"); +} + +QMenu::right-arrow { + image: url("GuiConfigs/dark/arrow-right.svg"); + padding-right: 6px; +} + + +/* TOOLBAR */ +QToolBar { + background-color: #2C2C2C; + border-width: 1px 0px; + border-style: solid; + border-color: #3A3A3A; +} + + +/* TOOLBAR BUTTON */ +QToolButton { + color: #FFFFFF; + background-color: transparent; + border: none; + border-radius: 6px; + padding: 4px 0px; + margin: 4px; +} + +QToolButton:hover { + background-color: #383838; +} + +QToolButton:pressed { + background-color: #343434; +} + +QToolButton:disabled { + color: #666666; +} + + +/* TOOLBAR SLIDER */ +QToolBar#mw_toolbar QSlider { + border: 1px solid transparent; + border-radius: 6px; + margin: 16px 0px; + padding: 0px 16px; +} + +QToolBar#mw_toolbar QSlider:hover { + background-color: #383838; + border-color: transparent; +} + +QToolBar#mw_toolbar QSlider:pressed { + background-color: #343434; + border-color: transparent; +} + +QSlider#sizeSlider::groove:horizontal { + background-color: #9F9F9F; + border-radius: 2px; + height: 4px; +} + +QSlider#sizeSlider::handle:horizontal { + image: url("GuiConfigs/dark/slider-handle.svg"); + border-radius: 9px; + width: 18px; + height: 18px; + margin: -7px 0px; +} + +QSlider#sizeSlider::handle:horizontal:hover { + image: url("GuiConfigs/dark/slider-handle-hover.svg"); +} + +QSlider#sizeSlider::handle:horizontal:pressed { + image: url("GuiConfigs/dark/slider-handle-pressed.svg"); +} + + +/* TOOLBAR SEARCH */ +QLineEdit#mw_searchbar { + color: #FFFFFF; + background-color: #383838; + border: 1px solid transparent; + border-radius: 6px; + padding: 6px 12px; + margin: 16px 0px; +} + +QLineEdit#mw_searchbar:hover { + background-color: #3D3D3D; + border-color: transparent; +} + +QLineEdit#mw_searchbar:focus { + background-color: #222222; + border-color: #3A3A3A; + border-bottom: 2px solid #4CC2FF; +} + + +/* GAME LIST TABLE */ +QMainWindow QTableView, +QMainWindow QHeaderView::section:first, +QMainWindow QHeaderView::section:last { + border-radius: 0px; +} + + +/* GAME GRID BODY */ +#game_list_grid_item { + background-color: transparent; + border-radius: 6px; +} + +#game_list_grid_item:hover { + background-color: #4D4D4D; +} + +#game_list_grid_item:focus { + background-color: #292929; +} + +#game_list_grid_item_title_label { + background-color: transparent; +} + + +/* LOG TAB */ +QTabBar#tab_bar_log::tab { + text-transform: uppercase; + color: #FFFFFF; + background-color: transparent; + border: none; + border-top-left-radius: 6px; + border-top-right-radius: 6px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; + min-width: 80px; + padding: 8px 16px; + margin: 0; + margin-top: 8px; +} + +QTabBar#tab_bar_log::tab:selected, QTabBar#tab_bar_log::tab:selected:hover { + font-weight: normal; + color: #FFFFFF; + background-color: #0C0C0C; +} + +QTabBar#tab_bar_log::tab:hover { + background-color: #252525; +} + + +/* LOG CONSOLE / TTY CONSOLE */ +QPlainTextEdit#log_frame, QPlainTextEdit#tty_frame { + background-color: #0C0C0C; + border: none; + padding: 8px; +} + + +/* LOG CONSOLE TEXT */ +QLabel#log_level_fatal { + color: #D61A1B; +} + +QLabel#log_level_error { + color: #FF6D7E; +} + +QLabel#log_level_warning { + color: #FFED72; +} + +QLabel#log_level_todo { + color: #FFB270; +} + +QLabel#log_level_notice { + color: #BAA0F8; +} + +QLabel#log_level_success { + color: #A2E57B; +} + +QLabel#log_level_always { + color: #7CD5F1; +} + +QLabel#log_level_trace { + color: #576265; +} + +QLabel#log_stack { + color: #F2FFFC; +} + + + + + +/* ----- SETTINGS DIALOG ----- */ +QDialog#settings_dialog QTabWidget::pane { + margin-left: -8px; +} + + + + + +/* ----- PAD SETTINGS DIALOG ----- */ +QDialog#pad_settings_dialog QTabWidget::pane { + margin-left: -4px; +} + + +/* SOME CONTROLLER BUTTONS GROUP BOX */ +QGroupBox#gb_l1, QGroupBox#gb_select, QGroupBox#gb_start, QGroupBox#gb_r1, QGroupBox#gb_l3, QGroupBox#gb_r3, QGroupBox#gb_choose_class, QGroupBox#gb_battery { + margin-bottom: 6px; +} + + +/* CONTROLLER ICON */ +#l_controller { + color: #FFFFFF; + padding: 20px 0; +} + + + + + +/* PATCH MANAGER DIALOG */ +QGroupBox#gb_patch_info QGroupBox { + background-color: #202020; + border: none; + border-radius: 6px; + padding-top: 0; + margin-top: 32px; +} + +QGroupBox#gb_patch_info QGroupBox::title { + subcontrol-origin: margin; + subcontrol-position: top left; + background-color: transparent; + border: none; + padding: 0; + min-width: 0; + margin-top: 12px; +} diff --git a/bin/GuiConfigs/Windows 11 (Light Mode) by GustavoGraziano.qss b/bin/GuiConfigs/Windows 11 (Light Mode) by GustavoGraziano.qss new file mode 100644 index 0000000000..692691856f --- /dev/null +++ b/bin/GuiConfigs/Windows 11 (Light Mode) by GustavoGraziano.qss @@ -0,0 +1,1090 @@ +/* + Windows 11 (Light Mode) + by GustavoGraziano + GitHub: "https://github.com/GustavoGraziano" + (2025/09/18) +*/ + + + + + +/* ----- GLOBAL QWIDGET ----- */ + +/* RESET */ +QWidget { + font-family: "Segoe UI"; + color: #1B1B1B; + background-color: transparent; + alternate-background-color: #EDEDED; + border: none; +} + +QDockWidget QWidget, QTabWidget::pane { + border: none; +} + +QMainWindow { + background-color: #FFFFFF; +} + +QDialog { + background-color: #F3F3F3; +} + + +QWidget#cg_disasm, QWidget#trophy_manager { + background-color: #F3F3F3; +} + +QWidget#trophy_notification_frame { + background-color: #F3F3F3; + color: #1B1B1B; +} + + +/* TOOLTIP */ +QToolTip { + background-color: #F9F9F9; + color: #1A1A1A; + border: 1px solid #DEDEDE; + padding: 2px 6px; +} + + +/* DOCK */ +QDockWidget { + color: #1B1B1B; + titlebar-normal-icon: url("GuiConfigs/light/window-undock.svg"); + titlebar-close-icon: url("GuiConfigs/light/window-close.svg"); +} + +QDockWidget::title { + background-color: #EEEEEE; + border-width: 1px 0px; + border-style: solid; + border-color: #DEDEDE; + padding: 6px 0px; +} + +QDockWidget::float-button, QDockWidget::close-button { + background-color: transparent; +} + +QDockWidget::float-button:hover { + background-color: #DDDDDD; + border: none; + border-radius: 4px; +} + +QDockWidget::close-button:hover { + background-color: #FF4B3A; + border: none; + border-radius: 4px; +} + + +/* TAB BAR */ +QTabBar::tab { + color: #1A1A1A; + padding: 8px 16px; + border: none; + border-radius: 6px; + margin: 0 8px 16px 0; + min-width: 65px; +} + +QTabBar::tab:hover { + background-color: #EAEAEA; +} + +QTabBar::tab:selected { + font-weight: bold; + color: #0067C0; + background-color: #EAEAEA; +} + +QTabBar::tab:selected:hover { + background-color: #EDEDED; +} + + +/* SCROOL BAR */ +QScrollBar:vertical { + background-color: transparent; + width: 6px; + margin: 0px; +} + +QScrollBar::groove:vertical { + background-color: transparent; + border-radius: 3px; + margin: 0px; + width: 6px; +} + +QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { + height: 0px; + width: 0px; + background: none; + border: none; +} + +QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { + background-color: transparent; +} + +QScrollBar::handle:vertical { + background-color: #858585; + min-height: 20px; + border-radius: 2px; + border: none; + margin: 0px 1px; +} + +QScrollBar::handle:vertical:hover { + background-color: #858585; + border-radius: 3px; + min-width: 12px; + margin: 0px; +} + + +QScrollBar:horizontal { + background-color: transparent; + height: 6px; + margin: 0px; +} + +QScrollBar::groove:horizontal { + background-color: transparent; + border-radius: 3px; + margin: 0px; + height: 6px; +} + +QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal { + height: 0px; + width: 0px; + background: none; + border: none; +} + +QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { + background-color: transparent; +} + +QScrollBar::handle:horizontal { + background-color: #858585; + min-width: 20px; + border-radius: 2px; + border: none; + margin: 1px 0px; +} + +QScrollBar::handle:horizontal:hover { + background-color: #858585; + border-radius: 3px; + min-height: 12px; + margin: 0px; +} + + +/* PROGRESS BAR */ +QProgressBar { + font-size: 10px; + font-weight: bold; + color: #1B1B1B; + background-color: #EDEDED; + border-radius: 4px; + text-align: center; +} + +QProgressBar::chunk { + background-color: #4CAF50; + border-radius: 4px; +} + +QProgressBar:disabled { + background-color: #FAFAFA; + border: 1px solid #ECECEC; + color: #A0A0A0; +} + +QProgressBar::chunk:disabled { + background-color: #579c7e; +} + +QProgressBar:indeterminate { + background-color: #373737; +} + +QProgressBar::chunk:indeterminate { + background-color: #4CAF50; +} + + +/* CALENDAR */ +QCalendarWidget QWidget#qt_calendar_navigationbar { + background-color: #F9F9F9; + border-bottom: 1px solid #E5E5E5; +} + +QCalendarWidget QToolButton { + background-color: transparent; + border: none; + border-radius: 4px; +} + +QCalendarWidget QToolButton:hover { + color: #1B1B1B; + background-color: #F0F0F0; +} + +QCalendarWidget QToolButton#qt_calendar_prevmonth, QCalendarWidget QToolButton#qt_calendar_nextmonth { + width: 6px; + height: 6px; + padding: 0px 10px; +} + +QCalendarWidget QToolButton#qt_calendar_prevmonth { + qproperty-icon: url("GuiConfigs/light/arrow-prev.svg"); +} + +QCalendarWidget QToolButton#qt_calendar_nextmonth { + qproperty-icon: url("GuiConfigs/light/arrow-next.svg"); +} + +QCalendarWidget QAbstractItemView { + color: #1B1B1B; + background-color: #F9F9F9; + alternate-background-color: #F9F9F9; +} + +QCalendarWidget QAbstractItemView::item:selected { + color: #0067C0; + background-color: #F0F0F0; +} + +QCalendarWidget QAbstractItemView::item:hover { + color: #0067C0; +} + +QCalendarWidget QAbstractItemView:focus { + outline: none; +} + +QCalendarWidget QToolButton#qt_calendar_monthbutton::menu-indicator, QCalendarWidget QToolButton#qt_calendar_yearbutton::menu-indicator { + image: none; +} + + +/* TABLE / TABLE HEADER */ +QTableView { + color: #1B1B1B; + background-color: #FFFFFF; + border: none; + border-radius: 6px; + selection-background-color: #CCE8FF; +} + +QTableView::item:selected { + color: #1B1B1B; + background-color: #CCE8FF; +} + + +QHeaderView::section { + background-color: #FFFFFF; + color: #4C607A; + padding: 6px 8px 0px; + border: none; + border-left: 1px solid #E5E5E5; +} + +QHeaderView::section:hover { + background-color: #D9EBF9; +} + +QHeaderView::section:first { + border-left: none; + border-top-left-radius: 6px; +} + +QHeaderView::section:last { + border-top-right-radius: 6px; +} + + + + + +/* ----- GLOBAL FORM ----- */ + +/* BUTTONS */ +QPushButton { + color: #1B1B1B; + background-color: #FEFEFE; + border: 2px solid #ECECEC; + border-radius: 4px; + min-width: 120px; + padding: 4px 8px; +} + +QPushButton:hover { + background-color: #FAFAFA; + border-color: #ECECEC; +} + +QPushButton:pressed { + color: #5F5F5F; + background-color: #FAFAFA; + border-color: #ECECEC; +} + +QPushButton:disabled { + color: #A0A0A0; + background-color: #FAFAFA; + border-color: #ECECEC; +} + + +QDialog QDialogButtonBox QPushButton { + color: #FFFFFF; + background-color: #0067C0; + border-color: #1473C5; +} + +QDialog QDialogButtonBox QPushButton:hover { + background-color: #1A76C6; + border-color: #2C80CA; +} + +QDialog QDialogButtonBox QPushButton:pressed { + color: #C2DAEF; + background-color: #3284CB; + border-color: #3284CB; +} + +QDialog QDialogButtonBox QPushButton:disabled { + color: #FFFFFF; + background-color: #C5C5C5; + border-color: #C5C5C5; +} + + +/* RADIO BUTTON / CHECKBOX / LIST / TREE VIEW */ +QRadioButton, +QCheckBox, +QListWidget { + spacing: 6px; +} + + +QRadioButton::indicator, +QCheckBox::indicator, +QListWidget::indicator { + width: 14px; + height: 14px; +} + + +QListWidget, +QTreeView { + background-color: #F9F9F9; + border: 1px solid #EEEEEE; + border-radius: 4px; + padding: 4px; + outline: none; +} + +QListWidget::item, +QTreeWidget::item { + color: #1B1B1B; + border-radius: 4px; + padding: 4px; +} + +QListWidget::item:selected, QListWidget::item:selected:hover, +QTreeWidget::item:selected, QTreeWidget::item:selected:hover { + color: #1B1B1B; + background-color: #CCE8FF; +} + +QListWidget::item:hover, +QTreeWidget::item:hover { + background-color: #E5F3FF; +} + +QListWidget::item:disabled, +QTreeWidget::item:disabled { + color: #A0A0A0; +} + +QListWidget::item:alternate, +QTreeWidget::item:alternate { + background-color: #F5F5F5; +} + + +QTreeView::branch { + background: #F9F9F9; +} + + +QRadioButton::indicator:unchecked { + image: url("GuiConfigs/light/radio-unchecked.svg"); +} + +QRadioButton::indicator:unchecked:hover { + image: url("GuiConfigs/light/radio-unchecked-hover.svg"); +} + +QRadioButton::indicator:unchecked:pressed { + image: url("GuiConfigs/light/radio-unchecked-pressed.svg"); +} + +QRadioButton::indicator:checked { + image: url("GuiConfigs/light/radio-checked.svg"); +} + +QRadioButton::indicator:checked:hover { + image: url("GuiConfigs/light/radio-checked-hover.svg"); +} + +QRadioButton::indicator:checked:pressed { + image: url("GuiConfigs/light/radio-checked-pressed.svg"); +} + +QRadioButton::indicator:unchecked:disabled { + image: url("GuiConfigs/light/radio-disabled.svg"); +} + +QRadioButton::indicator:checked:disabled { + image: url("GuiConfigs/light/radio-checked-disabled.svg"); +} + + +QCheckBox::indicator:unchecked, +QListWidget::indicator:unchecked { + image: url("GuiConfigs/light/checkbox-unchecked.svg"); +} + +QCheckBox::indicator:unchecked:hover, +QListWidget::indicator:unchecked:hover { + image: url("GuiConfigs/light/checkbox-unchecked-hover.svg"); +} + +QCheckBox::indicator:unchecked:pressed, +QListWidget::indicator:unchecked:pressed { + image: url("GuiConfigs/light/checkbox-unchecked-pressed.svg"); +} + +QCheckBox::indicator:checked, +QListWidget::indicator:checked { + image: url("GuiConfigs/light/checkbox-checked.svg"); +} + +QCheckBox::indicator:checked:hover, +QListWidget::indicator:checked:hover { + image: url("GuiConfigs/light/checkbox-checked-hover.svg"); +} + +QCheckBox::indicator:checked:pressed, +QListWidget::indicator:checked:pressed { + image: url("GuiConfigs/light/checkbox-checked-pressed.svg"); +} + +QCheckBox::indicator:indeterminate, +QListWidget::indicator:indeterminate { + image: url("GuiConfigs/light/checkbox-indeterminate.svg"); +} + +QCheckBox::indicator:indeterminate:hover, +QListWidget::indicator:indeterminate:hover { + image: url("GuiConfigs/light/checkbox-indeterminate-hover.svg"); +} + +QCheckBox::indicator:indeterminate:pressed, +QListWidget::indicator:indeterminate:pressed { + image: url("GuiConfigs/light/checkbox-indeterminate-pressed.svg"); +} + +QCheckBox::indicator:unchecked:disabled, +QListWidget::indicator:unchecked:disabled { + image: url("GuiConfigs/light/checkbox-disabled.svg"); +} + +QCheckBox::indicator:checked:disabled, +QListWidget::indicator:checked:disabled { + image: url("GuiConfigs/light/checkbox-checked-disabled.svg"); +} + +QCheckBox::indicator:indeterminate:disabled, +QListWidget::indicator:indeterminate:disabled { + image: url("GuiConfigs/light/checkbox-indeterminate-disabled.svg"); +} + + +QTreeView::branch:has-children:!has-siblings:closed, +QTreeView::branch:closed:has-children:has-siblings { + image: url("GuiConfigs/light/list-arrow-closed.svg"); +} + +QTreeView::branch:open:has-children:!has-siblings, +QTreeView::branch:open:has-children:has-siblings { + image: url("GuiConfigs/light/list-arrow-open.svg"); +} + +QTreeView:disabled::branch:has-children:!has-siblings:closed, +QTreeView:disabled::branch:closed:has-children:has-siblings { + image: url("GuiConfigs/light/list-arrow-closed-disabled.svg"); +} + +QTreeView:disabled::branch:open:has-children:!has-siblings, +QTreeView:disabled::branch:open:has-children:has-siblings { + image: url("GuiConfigs/light/list-arrow-open-disabled.svg"); +} + + +/* COMBO BOX / DATE TIME */ +QComboBox, +QDateTimeEdit { + color: #1B1B1B; + background-color: #FEFEFE; + border: 1px solid #E5E5E5; + border-radius: 4px; + padding: 4px 8px; +} + +QComboBox:hover, +QDateTimeEdit:hover { + background-color: #F8F8F8; + border: 1px solid #E8E8E8; +} + +QComboBox:pressed, +QDateTimeEdit:pressed { + color: #5E5E5E; + background-color: #F7F7F7; + border: 1px solid #E8E8E8; + border-bottom: none; + border-bottom-left-radius: 0px; + border-bottom-right-radius: 0px; +} + +QComboBox:disabled, +QDateTimeEdit:disabled { + color: #A0A0A0; + background-color: #FAFAFA; + border: 1px solid #ECECEC; +} + +QComboBox::drop-down, +QDateTimeEdit::drop-down { + subcontrol-origin: padding; + subcontrol-position: center right; + width: 10px; + height: 10px; + background: none; + border: none; + padding-right: 8px; + image: url("GuiConfigs/light/arrow-down.svg"); +} + +QComboBox::drop-down:on, +QDateTimeEdit::drop-down:on { + image: url("GuiConfigs/light/arrow-up.svg"); +} + +QComboBox::drop-down:disabled, +QDateTimeEdit::drop-down:disabled { + image: url("GuiConfigs/light/arrow-down-disabled.svg"); +} + + +QComboBox QAbstractItemView { + color: #1B1B1B; + background-color: #F9F9F9; + border: 1px solid #E5E5E5; + border-radius: 4px; + padding: 4px; +} + +QComboBox QAbstractItemView::item { + color: #1B1B1B; + padding: 4px 6px; + background-color: transparent; + border-radius: 4px; +} + +QComboBox QAbstractItemView::item:selected { + background-color: #F0F0F0; +} + +QComboBox QAbstractItemView:focus { + outline: none; +} + + +/* SPIN BOX / DOUBLE SPIN BOX */ +QSpinBox, +QDoubleSpinBox { + color: #1B1B1B; + background-color: #FEFEFE; + border: 1px solid #ECECEC; + border-radius: 4px; + padding: 5px 8px; +} + +QSpinBox::up-button, QSpinBox::down-button, +QDoubleSpinBox::up-button, QDoubleSpinBox::down-button { + border-radius: 4px; + width: 8px; + height: 8px; + padding: 3px 4px; +} + +QSpinBox::up-button:hover, QSpinBox::down-button:hover, +QDoubleSpinBox::up-button:hover, QDoubleSpinBox::down-button:hover { + background-color: #ECECEC; +} + +QSpinBox:disabled, +QDoubleSpinBox:disabled { + color: #A0A0A0; + background-color: #FAFAFA; + border: 1px solid #ECECEC; +} + +QSpinBox::up-button, +QDoubleSpinBox::up-button { + image: url("GuiConfigs/light/arrow-up.svg"); +} + +QSpinBox::down-button, +QDoubleSpinBox::down-button { + image: url("GuiConfigs/light/arrow-down.svg"); +} + +QSpinBox::up-button:disabled, +QDoubleSpinBox::up-button:disabled { + image: url("GuiConfigs/light/arrow-up-disabled.svg"); +} + +QSpinBox::down-button:disabled, +QDoubleSpinBox::down-button:disabled { + image: url("GuiConfigs/light/arrow-down-disabled.svg"); +} + + +/* SLIDER */ +QSlider::groove:horizontal { + background-color: #8B8B8B; + border-radius: 2px; + height: 4px; +} + +QSlider::groove:horizontal:disabled { + background-color: #ABABAB; +} + +QSlider::handle:horizontal { + image: url("GuiConfigs/light/slider-handle.svg"); + border-radius: 9px; + width: 18px; + height: 18px; + margin: -7px 0px; +} + +QSlider::handle:horizontal:hover { + image: url("GuiConfigs/light/slider-handle-hover.svg"); +} + +QSlider::handle:horizontal:pressed { + image: url("GuiConfigs/light/slider-handle-pressed.svg"); +} + +QSlider::handle:horizontal:disabled { + image: url("GuiConfigs/light/slider-handle-disabled.svg"); +} + + +/* LINE EDIT */ +QLineEdit { + color: #1B1B1B; + background-color: #FDFDFD; + border: 1px solid #EAEAEA; + border-radius: 4px; + padding: 5px 8px; +} + +QLineEdit:hover { + background-color: #F9F9F9; + border-color: #EAEAEA; +} + +QLineEdit:focus { + background-color: #FFFFFF; + border-color: #EAEAEA; + border-bottom: 2px solid #0067C0; +} + + +QDockWidget QLineEdit { + border: 1px solid transparent; + border-radius: 0; +} + +QDockWidget QLineEdit:focus { + background: #FDFDFD; + border: none; +} + + +/* GROUP BOX */ +QGroupBox { + background-color: #FBFBFB; + border: 1px solid #E5E5E5; + border-radius: 6px; + padding-top: 32px; +} + +QGroupBox::title { + subcontrol-origin: margin; + subcontrol-position: top center; + background-color: transparent; + border-bottom: 1px solid #E5E5E5; + padding: 8px 16px; + min-width: 9999px; +} + +QGroupBox QGroupBox::title { + border: none; +} + + + + + +/* ----- MAIN WINDOW ----- */ + +/* MENU BAR / MENU */ +QMenuBar { + background-color: #F8F8F8; +} + +QMenuBar::item { + color: #1B1B1B; + background-color: transparent; + padding: 4px 8px; +} + +QMenuBar::item:selected { + background-color: #EFEFEF; +} + +QMenuBar::item:pressed { + background-color: #F2F2F2; +} + + +QMenu { + background-color: #F9F9F9; + border: 1px solid #EEEEEE; + padding: 4px 0px; +} + +QMenu::item { + color: #1B1B1B; + background-color: transparent; + border-radius: 4px; + padding: 8px 20px; + margin: 0px 4px; +} + +QMenu::item:selected { + background-color: #F0F0F0; +} + +QMenu::item:disabled { + color: #A0A0A0; + background-color: transparent; +} + +QMenu::separator { + height: 1px; + background: #EEEEEE; + margin: 4px 0px; +} + +QMenu::indicator, QMenu::right-arrow { + width: 8px; + height: 8px; +} + +QMenu::indicator { + padding-left: 6px; +} + +QMenu::indicator:checked { + image: url("GuiConfigs/light/check-mark.svg"); +} + +QMenu::indicator:unchecked { + image: url("GuiConfigs/light/empty.svg"); +} + +QMenu::right-arrow { + image: url("GuiConfigs/light/arrow-right.svg"); + padding-right: 6px; +} + + +/* TOOLBAR */ +QToolBar { + background-color: #F8F8F8; + border-width: 1px 0px; + border-style: solid; + border-color: #DEDEDE; +} + + +/* TOOLBAR BUTTON */ +QToolButton { + color: #1B1B1B; + background-color: transparent; + border: none; + border-radius: 6px; + padding: 4px 0px; + margin: 4px; +} + +QToolButton:hover { + background-color: #EFEFEF; +} + +QToolButton:pressed { + background-color: #F2F2F2; +} + +QToolButton:disabled { + color: #A0A0A0; +} + + +/* TOOLBAR SLIDER */ +QToolBar#mw_toolbar QSlider { + border: 1px solid transparent; + border-radius: 6px; + margin: 16px 0px; + padding: 0px 16px; +} + +QToolBar#mw_toolbar QSlider:hover { + background-color: #FDFDFD; + border-color: #EAEAEA; +} + +QToolBar#mw_toolbar QSlider:pressed { + background-color: #F9F9F9; + border-color: #EAEAEA; +} + +QSlider#sizeSlider::groove:horizontal { + background-color: #8B8B8B; + border-radius: 2px; + height: 4px; +} + +QSlider#sizeSlider::handle:horizontal { + image: url("GuiConfigs/light/slider-handle.svg"); + border-radius: 9px; + width: 18px; + height: 18px; + margin: -7px 0px; +} + +QSlider#sizeSlider::handle:horizontal:hover { + image: url("GuiConfigs/light/slider-handle-hover.svg"); +} + +QSlider#sizeSlider::handle:horizontal:pressed { + image: url("GuiConfigs/light/slider-handle-pressed.svg"); +} + + +/* TOOLBAR SEARCH */ +QLineEdit#mw_searchbar { + color: #1B1B1B; + background-color: #FDFDFD; + border: 1px solid transparent; + border-radius: 6px; + padding: 6px 12px; + margin: 16px 0px; +} + +QLineEdit#mw_searchbar:hover { + background-color: #F9F9F9; + border-color: #EAEAEA; +} + +QLineEdit#mw_searchbar:focus { + background-color: #FFFFFF; + border-color: #EAEAEA; + border-bottom: 2px solid #0067C0; +} + + +/* GAME LIST TABLE */ +QMainWindow QTableView, +QMainWindow QHeaderView::section:first, +QMainWindow QHeaderView::section:last { + border-radius: 0px; +} + + +/* GAME GRID BODY */ +#game_list_grid_item { + background-color: transparent; + border-radius: 6px; +} + +#game_list_grid_item:hover { + background-color: #E5F3FF; +} + +#game_list_grid_item:focus { + background-color: #CCE8FF; +} + +#game_list_grid_item_title_label { + background-color: transparent; +} + + +/* LOG TAB */ +QTabBar#tab_bar_log::tab { + text-transform: uppercase; + color: #1B1B1B; + background-color: transparent; + border: none; + border-top-left-radius: 6px; + border-top-right-radius: 6px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; + min-width: 80px; + padding: 8px 16px; + margin: 0; + margin-top: 8px; +} + +QTabBar#tab_bar_log::tab:selected, QTabBar#tab_bar_log::tab:selected:hover { + font-weight: normal; + color: #1B1B1B; + background-color: #FAF4F2; +} + +QTabBar#tab_bar_log::tab:hover { + background-color: #ede5e7; +} + + +/* LOG CONSOLE / TTY CONSOLE */ +QPlainTextEdit#log_frame, QPlainTextEdit#tty_frame { + background-color: #FAF4F2; + border: none; + padding: 8px; +} + + +/* LOG CONSOLE TEXT */ +QLabel#log_level_fatal { + color: #FC3D3C; +} + +QLabel#log_level_error { + color: #E14775; +} + +QLabel#log_level_warning { + color: #CC7A0A; +} + +QLabel#log_level_todo { + color: #E16032; +} + +QLabel#log_level_notice { + color: #7058BE; +} + +QLabel#log_level_success { + color: #269D69; +} + +QLabel#log_level_always { + color: #1C8CA8; +} + +QLabel#log_level_trace { + color: #A59FA0; +} + +QLabel#log_stack { + color: #29242A; +} + + + + + +/* ----- SETTINGS DIALOG ----- */ +QDialog#settings_dialog QTabWidget::pane { + margin-left: -8px; +} + + + + + +/* ----- PAD SETTINGS DIALOG ----- */ +QDialog#pad_settings_dialog QTabWidget::pane { + margin-left: -4px; +} + + +/* SOME CONTROLLER BUTTONS GROUP BOX */ +QGroupBox#gb_l1, QGroupBox#gb_select, QGroupBox#gb_start, QGroupBox#gb_r1, QGroupBox#gb_l3, QGroupBox#gb_r3, QGroupBox#gb_choose_class, QGroupBox#gb_battery { + margin-bottom: 6px; +} + + +/* CONTROLLER ICON */ +#l_controller { + color: #1B1B1B; + padding: 20px 0; +} + + + + + +/* PATCH MANAGER DIALOG */ +QGroupBox#gb_patch_info QGroupBox { + background-color: #F3F3F3; + border: none; + border-radius: 6px; + padding-top: 0; + margin-top: 32px; +} + +QGroupBox#gb_patch_info QGroupBox::title { + subcontrol-origin: margin; + subcontrol-position: top left; + background-color: transparent; + border: none; + padding: 0; + min-width: 0; + margin-top: 12px; +} diff --git a/bin/GuiConfigs/YoRHa by Ani.qss b/bin/GuiConfigs/YoRHa by Ani.qss index c3345d4cd9..bcfd059c2e 100644 --- a/bin/GuiConfigs/YoRHa by Ani.qss +++ b/bin/GuiConfigs/YoRHa by Ani.qss @@ -4,24 +4,28 @@ by Ani @ https://github.com/AniLeo r1 (2018.02.27) r2 (2021.08.28) r3 (2022.08.20) +r4 (2025.09.14) */ /* Color Scheme - Pod Programs -8c806a -bd9d86 -c1b398 -eadfb1 -ebe4d2 +#8c806a +#bd9d86 +#c1b398 +#eadfb1 +#ebe4d2 - Light -b3ac98 -aea993 +#b3ac98 +#aea993 - Dark -4d4940 +#4d4940 + +- Disabled +#828790 */ @@ -55,31 +59,6 @@ QTextEdit, QPlainTextEdit { font-size: 8.50pt; } -/* - QListWidget, QTreeWidget: Style checkboxes and rows - - RPCS3: LLE/HLE Selector, Debugger -*/ -QListWidget::item { - margin-top: 0.05em; - margin-bottom: 0.05em; -} -QListWidget::indicator, QTreeWidget::indicator { - border: 0.05em solid #4d4940; -} -QListWidget::indicator::unchecked, QTreeWidget::indicator::unchecked { - background-color: #b3ac98; -} -QListWidget::indicator::checked, QTreeWidget::indicator::checked { - background-color: #4d4940; -} -QListWidget::indicator::disabled, QTreeWidget::indicator::disabled { - background-color: #828790; -} -QListWidget::item::selected, QTreeWidget::item::selected { - background-color: #4d4940; -} - /* QTableView: Style selected row @@ -141,31 +120,38 @@ QTabBar::tab::selected { color: #aea993; } -/* Checkboxes */ -QCheckBox::indicator { +/* Checkboxes + Radio Buttons + + QListWidget, QTreeWidget: Style indicators (checkboxes) and selected rows + + RPCS3: LLE/HLE Selector, Debugger, Game Patches +*/ +QCheckBox::indicator, QListWidget::indicator, QTreeWidget::indicator { border-radius: 0.1em; } -/* Radio Buttons */ QRadioButton::indicator { border-radius: 0.4em; } -/* Checkboxes and Radio Buttons */ -QCheckBox::indicator, QRadioButton::indicator { +QCheckBox::indicator, QRadioButton::indicator, QListWidget::indicator, QTreeWidget::indicator { border: 0.05em solid #4d4940; margin-top: 0.05em; margin-bottom: 0.05em; width: 0.8em; height: 0.8em; } -QCheckBox::indicator:checked, QRadioButton::indicator:checked { +QCheckBox::indicator:checked, QRadioButton::indicator:checked, QListWidget::indicator::checked, QTreeWidget::indicator::checked{ background-color: #4d4940; /* Dark */ } -QCheckBox::indicator:unchecked, QRadioButton::indicator:unchecked { +QCheckBox::indicator:unchecked, QRadioButton::indicator:unchecked, QListWidget::indicator::unchecked, QTreeWidget::indicator::unchecked { background-color: #b3ac98; /* Light */ } -QCheckBox::indicator::disabled, QRadioButton::indicator::disabled { +QCheckBox::indicator::disabled, QRadioButton::indicator::disabled, QListWidget::indicator::disabled, QTreeWidget::indicator::disabled { background-color: #828790; /* Gray */ } +QListWidget::item::selected, QTreeWidget::item::selected { + background-color: #4d4940; +} /* Combo Boxes, Datetime dropdown */ QComboBox, QDateTimeEdit, QLineEdit { @@ -215,6 +201,7 @@ QPushButton::disabled { /* QSpinBox (Settings -> Emulator -> width/height) */ /* QDoubleSpinBox (Pads -> Mouse Acceleration -> x/y) */ QSpinBox, QDoubleSpinBox { + height: 0.1em; background-color: #b3ac98; } QSpinBox::disabled, QDoubleSpinBox::disabled { @@ -247,6 +234,7 @@ QDockWidget { QDockWidget::title { background: #4d4940; padding-top: 0.2em; + padding-left: 0.2em; } QDockWidget::close-button, QDockWidget::float-button { background-color: #b3ac98; @@ -262,7 +250,7 @@ QTabWidget::tab-bar { } /* Top menu bar */ -QMenuBar { +QMenuBar#menuBar { height:1.50em; text-transform: uppercase; } @@ -314,12 +302,27 @@ QCalendarWidget QWidget{ color: #4d4940; } +/* Scrollbars */ +QScrollBar::handle, QTableView QScrollBar::handle { + background: #4d4940; +} +QScrollBar::handle:disabled { + background: #828790; +} +QScrollBar::add-page, QScrollBar::sub-page { + background: #8c806a; +} +QScrollBar::up-arrow, QScrollBar::down-arrow, QScrollBar::up-button:vertical, QScrollBar::down-button, QScrollBar::sub-line, QScrollBar::add-line { + background: transparent; + color: transparent; +} + /*** RPCS3 Specifics ***/ /* Main Window, Dialogs and some Dialogs that are actually widgets */ -QWidget#trophy_manager, QWidget#cg_disasm, QWidget#log_viewer, QMainWindow#main_window { +QWidget#trophy_manager, QWidget#cg_disasm, QWidget#log_viewer, QWidget#savestate_manager, QMainWindow#main_window { border-image: url("GuiConfigs/YoRHa-background.jpg"); } diff --git a/bin/GuiConfigs/dark/arrow-down-disabled.svg b/bin/GuiConfigs/dark/arrow-down-disabled.svg new file mode 100644 index 0000000000..c057e16738 --- /dev/null +++ b/bin/GuiConfigs/dark/arrow-down-disabled.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/GuiConfigs/dark/arrow-down.svg b/bin/GuiConfigs/dark/arrow-down.svg new file mode 100644 index 0000000000..10ce5497fd --- /dev/null +++ b/bin/GuiConfigs/dark/arrow-down.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/GuiConfigs/dark/arrow-left-disabled.svg b/bin/GuiConfigs/dark/arrow-left-disabled.svg new file mode 100644 index 0000000000..0303633436 --- /dev/null +++ b/bin/GuiConfigs/dark/arrow-left-disabled.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/GuiConfigs/dark/arrow-left.svg b/bin/GuiConfigs/dark/arrow-left.svg new file mode 100644 index 0000000000..6d85039905 --- /dev/null +++ b/bin/GuiConfigs/dark/arrow-left.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/GuiConfigs/dark/arrow-next.svg b/bin/GuiConfigs/dark/arrow-next.svg new file mode 100644 index 0000000000..d70f87f999 --- /dev/null +++ b/bin/GuiConfigs/dark/arrow-next.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/bin/GuiConfigs/dark/arrow-prev.svg b/bin/GuiConfigs/dark/arrow-prev.svg new file mode 100644 index 0000000000..afa3358313 --- /dev/null +++ b/bin/GuiConfigs/dark/arrow-prev.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/bin/GuiConfigs/dark/arrow-right-disabled.svg b/bin/GuiConfigs/dark/arrow-right-disabled.svg new file mode 100644 index 0000000000..eaa0f2487c --- /dev/null +++ b/bin/GuiConfigs/dark/arrow-right-disabled.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/GuiConfigs/dark/arrow-right.svg b/bin/GuiConfigs/dark/arrow-right.svg new file mode 100644 index 0000000000..b6e2fac21e --- /dev/null +++ b/bin/GuiConfigs/dark/arrow-right.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/GuiConfigs/dark/arrow-up-disabled.svg b/bin/GuiConfigs/dark/arrow-up-disabled.svg new file mode 100644 index 0000000000..05e0ab2406 --- /dev/null +++ b/bin/GuiConfigs/dark/arrow-up-disabled.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/GuiConfigs/dark/arrow-up.svg b/bin/GuiConfigs/dark/arrow-up.svg new file mode 100644 index 0000000000..95657aa0d0 --- /dev/null +++ b/bin/GuiConfigs/dark/arrow-up.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/GuiConfigs/dark/check-mark-disabled.svg b/bin/GuiConfigs/dark/check-mark-disabled.svg new file mode 100644 index 0000000000..dbe8f7e724 --- /dev/null +++ b/bin/GuiConfigs/dark/check-mark-disabled.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/GuiConfigs/dark/check-mark.svg b/bin/GuiConfigs/dark/check-mark.svg new file mode 100644 index 0000000000..3768cda4c1 --- /dev/null +++ b/bin/GuiConfigs/dark/check-mark.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/GuiConfigs/dark/checkbox-checked-disabled.svg b/bin/GuiConfigs/dark/checkbox-checked-disabled.svg new file mode 100644 index 0000000000..a6323d57d0 --- /dev/null +++ b/bin/GuiConfigs/dark/checkbox-checked-disabled.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/bin/GuiConfigs/dark/checkbox-checked-hover.svg b/bin/GuiConfigs/dark/checkbox-checked-hover.svg new file mode 100644 index 0000000000..9279168411 --- /dev/null +++ b/bin/GuiConfigs/dark/checkbox-checked-hover.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/bin/GuiConfigs/dark/checkbox-checked-pressed.svg b/bin/GuiConfigs/dark/checkbox-checked-pressed.svg new file mode 100644 index 0000000000..4ad4e0f10c --- /dev/null +++ b/bin/GuiConfigs/dark/checkbox-checked-pressed.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/bin/GuiConfigs/dark/checkbox-checked.svg b/bin/GuiConfigs/dark/checkbox-checked.svg new file mode 100644 index 0000000000..860b73323e --- /dev/null +++ b/bin/GuiConfigs/dark/checkbox-checked.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/bin/GuiConfigs/dark/checkbox-disabled.svg b/bin/GuiConfigs/dark/checkbox-disabled.svg new file mode 100644 index 0000000000..4c0e28af8d --- /dev/null +++ b/bin/GuiConfigs/dark/checkbox-disabled.svg @@ -0,0 +1,4 @@ + + + + diff --git a/bin/GuiConfigs/dark/checkbox-indeterminate-disabled.svg b/bin/GuiConfigs/dark/checkbox-indeterminate-disabled.svg new file mode 100644 index 0000000000..28ffa34410 --- /dev/null +++ b/bin/GuiConfigs/dark/checkbox-indeterminate-disabled.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/bin/GuiConfigs/dark/checkbox-indeterminate-hover.svg b/bin/GuiConfigs/dark/checkbox-indeterminate-hover.svg new file mode 100644 index 0000000000..b43d5384b0 --- /dev/null +++ b/bin/GuiConfigs/dark/checkbox-indeterminate-hover.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/bin/GuiConfigs/dark/checkbox-indeterminate-pressed.svg b/bin/GuiConfigs/dark/checkbox-indeterminate-pressed.svg new file mode 100644 index 0000000000..e46ae55ddc --- /dev/null +++ b/bin/GuiConfigs/dark/checkbox-indeterminate-pressed.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/bin/GuiConfigs/dark/checkbox-indeterminate.svg b/bin/GuiConfigs/dark/checkbox-indeterminate.svg new file mode 100644 index 0000000000..55a4824043 --- /dev/null +++ b/bin/GuiConfigs/dark/checkbox-indeterminate.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/bin/GuiConfigs/dark/checkbox-unchecked-hover.svg b/bin/GuiConfigs/dark/checkbox-unchecked-hover.svg new file mode 100644 index 0000000000..5159b70312 --- /dev/null +++ b/bin/GuiConfigs/dark/checkbox-unchecked-hover.svg @@ -0,0 +1,4 @@ + + + + diff --git a/bin/GuiConfigs/dark/checkbox-unchecked-pressed.svg b/bin/GuiConfigs/dark/checkbox-unchecked-pressed.svg new file mode 100644 index 0000000000..4b1b2a3639 --- /dev/null +++ b/bin/GuiConfigs/dark/checkbox-unchecked-pressed.svg @@ -0,0 +1,4 @@ + + + + diff --git a/bin/GuiConfigs/dark/checkbox-unchecked.svg b/bin/GuiConfigs/dark/checkbox-unchecked.svg new file mode 100644 index 0000000000..24b1b1b211 --- /dev/null +++ b/bin/GuiConfigs/dark/checkbox-unchecked.svg @@ -0,0 +1,4 @@ + + + + diff --git a/bin/GuiConfigs/dark/empty.svg b/bin/GuiConfigs/dark/empty.svg new file mode 100644 index 0000000000..d216ff7f65 --- /dev/null +++ b/bin/GuiConfigs/dark/empty.svg @@ -0,0 +1,2 @@ + + diff --git a/bin/GuiConfigs/dark/list-arrow-closed-disabled.svg b/bin/GuiConfigs/dark/list-arrow-closed-disabled.svg new file mode 100644 index 0000000000..60843e0801 --- /dev/null +++ b/bin/GuiConfigs/dark/list-arrow-closed-disabled.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/GuiConfigs/dark/list-arrow-closed.svg b/bin/GuiConfigs/dark/list-arrow-closed.svg new file mode 100644 index 0000000000..674c19e989 --- /dev/null +++ b/bin/GuiConfigs/dark/list-arrow-closed.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/GuiConfigs/dark/list-arrow-open-disabled.svg b/bin/GuiConfigs/dark/list-arrow-open-disabled.svg new file mode 100644 index 0000000000..af22abdb93 --- /dev/null +++ b/bin/GuiConfigs/dark/list-arrow-open-disabled.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/GuiConfigs/dark/list-arrow-open.svg b/bin/GuiConfigs/dark/list-arrow-open.svg new file mode 100644 index 0000000000..3f8652412c --- /dev/null +++ b/bin/GuiConfigs/dark/list-arrow-open.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/GuiConfigs/dark/radio-checked-disabled.svg b/bin/GuiConfigs/dark/radio-checked-disabled.svg new file mode 100644 index 0000000000..7314ca7e58 --- /dev/null +++ b/bin/GuiConfigs/dark/radio-checked-disabled.svg @@ -0,0 +1,4 @@ + + + + diff --git a/bin/GuiConfigs/dark/radio-checked-hover.svg b/bin/GuiConfigs/dark/radio-checked-hover.svg new file mode 100644 index 0000000000..4a1a733688 --- /dev/null +++ b/bin/GuiConfigs/dark/radio-checked-hover.svg @@ -0,0 +1,4 @@ + + + + diff --git a/bin/GuiConfigs/dark/radio-checked-pressed.svg b/bin/GuiConfigs/dark/radio-checked-pressed.svg new file mode 100644 index 0000000000..6c08461297 --- /dev/null +++ b/bin/GuiConfigs/dark/radio-checked-pressed.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/bin/GuiConfigs/dark/radio-checked.svg b/bin/GuiConfigs/dark/radio-checked.svg new file mode 100644 index 0000000000..40226c2ec9 --- /dev/null +++ b/bin/GuiConfigs/dark/radio-checked.svg @@ -0,0 +1,4 @@ + + + + diff --git a/bin/GuiConfigs/dark/radio-disabled.svg b/bin/GuiConfigs/dark/radio-disabled.svg new file mode 100644 index 0000000000..d9b45f6275 --- /dev/null +++ b/bin/GuiConfigs/dark/radio-disabled.svg @@ -0,0 +1,4 @@ + + + + diff --git a/bin/GuiConfigs/dark/radio-unchecked-hover.svg b/bin/GuiConfigs/dark/radio-unchecked-hover.svg new file mode 100644 index 0000000000..301d8c3414 --- /dev/null +++ b/bin/GuiConfigs/dark/radio-unchecked-hover.svg @@ -0,0 +1,4 @@ + + + + diff --git a/bin/GuiConfigs/dark/radio-unchecked-pressed.svg b/bin/GuiConfigs/dark/radio-unchecked-pressed.svg new file mode 100644 index 0000000000..a8334fde9e --- /dev/null +++ b/bin/GuiConfigs/dark/radio-unchecked-pressed.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/bin/GuiConfigs/dark/radio-unchecked.svg b/bin/GuiConfigs/dark/radio-unchecked.svg new file mode 100644 index 0000000000..334ce6ca2c --- /dev/null +++ b/bin/GuiConfigs/dark/radio-unchecked.svg @@ -0,0 +1,4 @@ + + + + diff --git a/bin/GuiConfigs/dark/slider-handle-disabled.svg b/bin/GuiConfigs/dark/slider-handle-disabled.svg new file mode 100644 index 0000000000..2d3d865bb4 --- /dev/null +++ b/bin/GuiConfigs/dark/slider-handle-disabled.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/GuiConfigs/dark/slider-handle-hover.svg b/bin/GuiConfigs/dark/slider-handle-hover.svg new file mode 100644 index 0000000000..2f0f8abb98 --- /dev/null +++ b/bin/GuiConfigs/dark/slider-handle-hover.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/GuiConfigs/dark/slider-handle-pressed.svg b/bin/GuiConfigs/dark/slider-handle-pressed.svg new file mode 100644 index 0000000000..9ca2a54ad6 --- /dev/null +++ b/bin/GuiConfigs/dark/slider-handle-pressed.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/GuiConfigs/dark/slider-handle.svg b/bin/GuiConfigs/dark/slider-handle.svg new file mode 100644 index 0000000000..c49e8e25aa --- /dev/null +++ b/bin/GuiConfigs/dark/slider-handle.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/GuiConfigs/dark/window-close.svg b/bin/GuiConfigs/dark/window-close.svg new file mode 100644 index 0000000000..337b067f44 --- /dev/null +++ b/bin/GuiConfigs/dark/window-close.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/GuiConfigs/dark/window-undock.svg b/bin/GuiConfigs/dark/window-undock.svg new file mode 100644 index 0000000000..684a8f87fe --- /dev/null +++ b/bin/GuiConfigs/dark/window-undock.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/GuiConfigs/light/arrow-down-disabled.svg b/bin/GuiConfigs/light/arrow-down-disabled.svg new file mode 100644 index 0000000000..6a35cff63b --- /dev/null +++ b/bin/GuiConfigs/light/arrow-down-disabled.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/GuiConfigs/light/arrow-down.svg b/bin/GuiConfigs/light/arrow-down.svg new file mode 100644 index 0000000000..0820bdb8c9 --- /dev/null +++ b/bin/GuiConfigs/light/arrow-down.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/GuiConfigs/light/arrow-left-disabled.svg b/bin/GuiConfigs/light/arrow-left-disabled.svg new file mode 100644 index 0000000000..02e5395fbf --- /dev/null +++ b/bin/GuiConfigs/light/arrow-left-disabled.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/GuiConfigs/light/arrow-left.svg b/bin/GuiConfigs/light/arrow-left.svg new file mode 100644 index 0000000000..6502056f9f --- /dev/null +++ b/bin/GuiConfigs/light/arrow-left.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/GuiConfigs/light/arrow-next.svg b/bin/GuiConfigs/light/arrow-next.svg new file mode 100644 index 0000000000..58a8176040 --- /dev/null +++ b/bin/GuiConfigs/light/arrow-next.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/bin/GuiConfigs/light/arrow-prev.svg b/bin/GuiConfigs/light/arrow-prev.svg new file mode 100644 index 0000000000..233a70c3f5 --- /dev/null +++ b/bin/GuiConfigs/light/arrow-prev.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/bin/GuiConfigs/light/arrow-right-disabled.svg b/bin/GuiConfigs/light/arrow-right-disabled.svg new file mode 100644 index 0000000000..486e6c0691 --- /dev/null +++ b/bin/GuiConfigs/light/arrow-right-disabled.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/GuiConfigs/light/arrow-right.svg b/bin/GuiConfigs/light/arrow-right.svg new file mode 100644 index 0000000000..651e7adf4f --- /dev/null +++ b/bin/GuiConfigs/light/arrow-right.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/GuiConfigs/light/arrow-up-disabled.svg b/bin/GuiConfigs/light/arrow-up-disabled.svg new file mode 100644 index 0000000000..1651415127 --- /dev/null +++ b/bin/GuiConfigs/light/arrow-up-disabled.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/GuiConfigs/light/arrow-up.svg b/bin/GuiConfigs/light/arrow-up.svg new file mode 100644 index 0000000000..8b1065f702 --- /dev/null +++ b/bin/GuiConfigs/light/arrow-up.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/GuiConfigs/light/check-mark-disabled.svg b/bin/GuiConfigs/light/check-mark-disabled.svg new file mode 100644 index 0000000000..5d42a7aa9e --- /dev/null +++ b/bin/GuiConfigs/light/check-mark-disabled.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/GuiConfigs/light/check-mark.svg b/bin/GuiConfigs/light/check-mark.svg new file mode 100644 index 0000000000..f0dd920b06 --- /dev/null +++ b/bin/GuiConfigs/light/check-mark.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/GuiConfigs/light/checkbox-checked-disabled.svg b/bin/GuiConfigs/light/checkbox-checked-disabled.svg new file mode 100644 index 0000000000..49dd8ce5e7 --- /dev/null +++ b/bin/GuiConfigs/light/checkbox-checked-disabled.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/bin/GuiConfigs/light/checkbox-checked-hover.svg b/bin/GuiConfigs/light/checkbox-checked-hover.svg new file mode 100644 index 0000000000..183bf80fc7 --- /dev/null +++ b/bin/GuiConfigs/light/checkbox-checked-hover.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/bin/GuiConfigs/light/checkbox-checked-pressed.svg b/bin/GuiConfigs/light/checkbox-checked-pressed.svg new file mode 100644 index 0000000000..e71a14b93a --- /dev/null +++ b/bin/GuiConfigs/light/checkbox-checked-pressed.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/bin/GuiConfigs/light/checkbox-checked.svg b/bin/GuiConfigs/light/checkbox-checked.svg new file mode 100644 index 0000000000..7a2acc4fb7 --- /dev/null +++ b/bin/GuiConfigs/light/checkbox-checked.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/bin/GuiConfigs/light/checkbox-disabled.svg b/bin/GuiConfigs/light/checkbox-disabled.svg new file mode 100644 index 0000000000..700da26462 --- /dev/null +++ b/bin/GuiConfigs/light/checkbox-disabled.svg @@ -0,0 +1,4 @@ + + + + diff --git a/bin/GuiConfigs/light/checkbox-indeterminate-disabled.svg b/bin/GuiConfigs/light/checkbox-indeterminate-disabled.svg new file mode 100644 index 0000000000..a430d6a8c0 --- /dev/null +++ b/bin/GuiConfigs/light/checkbox-indeterminate-disabled.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/bin/GuiConfigs/light/checkbox-indeterminate-hover.svg b/bin/GuiConfigs/light/checkbox-indeterminate-hover.svg new file mode 100644 index 0000000000..c4fbf20574 --- /dev/null +++ b/bin/GuiConfigs/light/checkbox-indeterminate-hover.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/bin/GuiConfigs/light/checkbox-indeterminate-pressed.svg b/bin/GuiConfigs/light/checkbox-indeterminate-pressed.svg new file mode 100644 index 0000000000..b8c4d2644a --- /dev/null +++ b/bin/GuiConfigs/light/checkbox-indeterminate-pressed.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/bin/GuiConfigs/light/checkbox-indeterminate.svg b/bin/GuiConfigs/light/checkbox-indeterminate.svg new file mode 100644 index 0000000000..0a89706c5e --- /dev/null +++ b/bin/GuiConfigs/light/checkbox-indeterminate.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/bin/GuiConfigs/light/checkbox-unchecked-hover.svg b/bin/GuiConfigs/light/checkbox-unchecked-hover.svg new file mode 100644 index 0000000000..4789c03a08 --- /dev/null +++ b/bin/GuiConfigs/light/checkbox-unchecked-hover.svg @@ -0,0 +1,4 @@ + + + + diff --git a/bin/GuiConfigs/light/checkbox-unchecked-pressed.svg b/bin/GuiConfigs/light/checkbox-unchecked-pressed.svg new file mode 100644 index 0000000000..709e2686c5 --- /dev/null +++ b/bin/GuiConfigs/light/checkbox-unchecked-pressed.svg @@ -0,0 +1,4 @@ + + + + diff --git a/bin/GuiConfigs/light/checkbox-unchecked.svg b/bin/GuiConfigs/light/checkbox-unchecked.svg new file mode 100644 index 0000000000..2a4cfd581e --- /dev/null +++ b/bin/GuiConfigs/light/checkbox-unchecked.svg @@ -0,0 +1,4 @@ + + + + diff --git a/bin/GuiConfigs/light/empty.svg b/bin/GuiConfigs/light/empty.svg new file mode 100644 index 0000000000..d216ff7f65 --- /dev/null +++ b/bin/GuiConfigs/light/empty.svg @@ -0,0 +1,2 @@ + + diff --git a/bin/GuiConfigs/light/list-arrow-closed-disabled.svg b/bin/GuiConfigs/light/list-arrow-closed-disabled.svg new file mode 100644 index 0000000000..d4fbdf3b40 --- /dev/null +++ b/bin/GuiConfigs/light/list-arrow-closed-disabled.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/GuiConfigs/light/list-arrow-closed.svg b/bin/GuiConfigs/light/list-arrow-closed.svg new file mode 100644 index 0000000000..94f388d3e3 --- /dev/null +++ b/bin/GuiConfigs/light/list-arrow-closed.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/GuiConfigs/light/list-arrow-open-disabled.svg b/bin/GuiConfigs/light/list-arrow-open-disabled.svg new file mode 100644 index 0000000000..243df7618d --- /dev/null +++ b/bin/GuiConfigs/light/list-arrow-open-disabled.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/GuiConfigs/light/list-arrow-open.svg b/bin/GuiConfigs/light/list-arrow-open.svg new file mode 100644 index 0000000000..9ee4cfb85b --- /dev/null +++ b/bin/GuiConfigs/light/list-arrow-open.svg @@ -0,0 +1,3 @@ + + + diff --git a/bin/GuiConfigs/light/radio-checked-disabled.svg b/bin/GuiConfigs/light/radio-checked-disabled.svg new file mode 100644 index 0000000000..13084c94a7 --- /dev/null +++ b/bin/GuiConfigs/light/radio-checked-disabled.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/bin/GuiConfigs/light/radio-checked-hover.svg b/bin/GuiConfigs/light/radio-checked-hover.svg new file mode 100644 index 0000000000..abfd016a75 --- /dev/null +++ b/bin/GuiConfigs/light/radio-checked-hover.svg @@ -0,0 +1,4 @@ + + + + diff --git a/bin/GuiConfigs/light/radio-checked-pressed.svg b/bin/GuiConfigs/light/radio-checked-pressed.svg new file mode 100644 index 0000000000..f842bd4701 --- /dev/null +++ b/bin/GuiConfigs/light/radio-checked-pressed.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/bin/GuiConfigs/light/radio-checked.svg b/bin/GuiConfigs/light/radio-checked.svg new file mode 100644 index 0000000000..18e8c3bfc4 --- /dev/null +++ b/bin/GuiConfigs/light/radio-checked.svg @@ -0,0 +1,4 @@ + + + + diff --git a/bin/GuiConfigs/light/radio-disabled.svg b/bin/GuiConfigs/light/radio-disabled.svg new file mode 100644 index 0000000000..c7a50333c9 --- /dev/null +++ b/bin/GuiConfigs/light/radio-disabled.svg @@ -0,0 +1,4 @@ + + + + diff --git a/bin/GuiConfigs/light/radio-unchecked-hover.svg b/bin/GuiConfigs/light/radio-unchecked-hover.svg new file mode 100644 index 0000000000..61ac41e26c --- /dev/null +++ b/bin/GuiConfigs/light/radio-unchecked-hover.svg @@ -0,0 +1,4 @@ + + + + diff --git a/bin/GuiConfigs/light/radio-unchecked-pressed.svg b/bin/GuiConfigs/light/radio-unchecked-pressed.svg new file mode 100644 index 0000000000..909797e62d --- /dev/null +++ b/bin/GuiConfigs/light/radio-unchecked-pressed.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/bin/GuiConfigs/light/radio-unchecked.svg b/bin/GuiConfigs/light/radio-unchecked.svg new file mode 100644 index 0000000000..41a5f5a1d7 --- /dev/null +++ b/bin/GuiConfigs/light/radio-unchecked.svg @@ -0,0 +1,4 @@ + + + + diff --git a/bin/GuiConfigs/light/slider-handle-disabled.svg b/bin/GuiConfigs/light/slider-handle-disabled.svg new file mode 100644 index 0000000000..0d2ddb8d7b --- /dev/null +++ b/bin/GuiConfigs/light/slider-handle-disabled.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/bin/GuiConfigs/light/slider-handle-hover.svg b/bin/GuiConfigs/light/slider-handle-hover.svg new file mode 100644 index 0000000000..9eea790adf --- /dev/null +++ b/bin/GuiConfigs/light/slider-handle-hover.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/bin/GuiConfigs/light/slider-handle-pressed.svg b/bin/GuiConfigs/light/slider-handle-pressed.svg new file mode 100644 index 0000000000..7989e9d376 --- /dev/null +++ b/bin/GuiConfigs/light/slider-handle-pressed.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/bin/GuiConfigs/light/slider-handle.svg b/bin/GuiConfigs/light/slider-handle.svg new file mode 100644 index 0000000000..2c5df2c491 --- /dev/null +++ b/bin/GuiConfigs/light/slider-handle.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/bin/GuiConfigs/light/window-close.svg b/bin/GuiConfigs/light/window-close.svg new file mode 100644 index 0000000000..5a1ef8d6e9 --- /dev/null +++ b/bin/GuiConfigs/light/window-close.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/bin/GuiConfigs/light/window-undock.svg b/bin/GuiConfigs/light/window-undock.svg new file mode 100644 index 0000000000..df9a297b96 --- /dev/null +++ b/bin/GuiConfigs/light/window-undock.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/buildfiles/cmake/ConfigureCompiler.cmake b/buildfiles/cmake/ConfigureCompiler.cmake index c1a30d4f93..be900d13c2 100644 --- a/buildfiles/cmake/ConfigureCompiler.cmake +++ b/buildfiles/cmake/ConfigureCompiler.cmake @@ -5,7 +5,6 @@ if(MSVC) add_compile_definitions( _CRT_SECURE_NO_DEPRECATE=1 _CRT_NON_CONFORMING_SWPRINTFS=1 _SCL_SECURE_NO_WARNINGS=1 NOMINMAX _ENABLE_EXTENDED_ALIGNED_STORAGE=1 _HAS_EXCEPTIONS=0) - add_link_options(/DYNAMICBASE:YES) #TODO: Some of these could be cleaned up add_compile_options(/wd4805) # Comparing boolean and int diff --git a/buildfiles/cmake/CopyRuntimeDependencies.cmake b/buildfiles/cmake/CopyRuntimeDependencies.cmake new file mode 100644 index 0000000000..6724d332da --- /dev/null +++ b/buildfiles/cmake/CopyRuntimeDependencies.cmake @@ -0,0 +1,51 @@ +# Copy remaining DLLS + +cmake_path(GET exe PARENT_PATH exe_dir) +cmake_path(CONVERT "${MSYS2_CLANG_BIN}" TO_CMAKE_PATH_LIST msys2_clang_bin) +cmake_path(CONVERT "${MSYS2_USR_BIN}" TO_CMAKE_PATH_LIST msys2_usr_bin) + +message(STATUS "Getting runtime dependencies for '${exe}' in '${exe_dir}'") +message(STATUS "Dependency dirs: '${msys2_clang_bin}', '${msys2_usr_bin}'") + +file(GET_RUNTIME_DEPENDENCIES EXECUTABLES ${exe} + RESOLVED_DEPENDENCIES_VAR resolved_deps + UNRESOLVED_DEPENDENCIES_VAR unresolved_deps + DIRECTORIES ${msys2_clang_bin} ${msys2_usr_bin}) + +foreach(dep IN LISTS resolved_deps) + cmake_path(GET dep FILENAME dep_filename) + cmake_path(GET dep PARENT_PATH dep_dir_raw) + cmake_path(CONVERT "${dep_dir_raw}" TO_CMAKE_PATH_LIST dep_dir) + + string(COMPARE EQUAL "${dep_dir}" "${msys2_clang_bin}" is_clang_path) + string(COMPARE EQUAL "${dep_dir}" "${msys2_usr_bin}" is_usr_path) + + set(same_path FALSE) + if(is_clang_path OR is_usr_path) + set(same_path TRUE) + endif() + + if(same_path) + set(dest "${exe_dir}/${dep_filename}") + if(NOT EXISTS "${dest}") + file(COPY "${dep}" DESTINATION "${exe_dir}") + message(STATUS "Copied '${dep_filename}' to '${exe_dir}'") + else() + message(STATUS "Already exists: '${dest}', skipping.") + endif() + else() + message(STATUS "Found and ignored '${dep}' in '${dep_dir}'. Is clang path: ${is_clang_path}, is usr path: ${is_usr_path}") + endif() +endforeach() + +# Warn about unresolved dependencies +if(unresolved_deps) + message(WARNING "Unresolved dependencies:") + foreach(dep IN LISTS unresolved_deps) + cmake_path(GET dep PARENT_PATH dep_dir) + string(TOLOWER "${dep_dir}" dep_dir_lower) + if(NOT dep_dir_lower MATCHES ".*(windows[/\\]system32|windows[/\\]winsxs|program files).*") + message(STATUS " - ${dep_dir}/${dep}") + endif() + endforeach() +endif() diff --git a/buildfiles/cmake/FindFFMPEG.cmake b/buildfiles/cmake/FindFFMPEG.cmake index 79144eb94c..55e3fd8d88 100644 --- a/buildfiles/cmake/FindFFMPEG.cmake +++ b/buildfiles/cmake/FindFFMPEG.cmake @@ -21,7 +21,7 @@ if (FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR) # in cache already set(FFMPEG_FOUND TRUE) -else (FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR) +else () # use pkg-config to get the directories and then use these values # in the FIND_PATH() and FIND_LIBRARY() calls find_package(PkgConfig) @@ -31,7 +31,7 @@ else (FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR) pkg_check_modules(_FFMPEG_AVUTIL libavutil) pkg_check_modules(_FFMPEG_SWSCALE libswscale) pkg_check_modules(_FFMPEG_SWRESAMPLE libswresample) - endif (PKG_CONFIG_FOUND) + endif () find_path(FFMPEG_AVCODEC_INCLUDE_DIR NAMES libavcodec/avcodec.h @@ -64,9 +64,10 @@ else (FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR) PATHS ${_FFMPEG_SWRESAMPLE_LIBRARY_DIRS} /usr/lib /usr/local/lib /opt/local/lib /sw/lib ) - if (FFMPEG_LIBAVCODEC AND FFMPEG_LIBAVFORMAT AND FFMPEG_LIBSWSCALE AND FFMPEG_LIBSWRESAMPLE) - set(FFMPEG_FOUND TRUE) - endif() + find_package_handle_standard_args(FFMPEG + DEFAULT_MSG + FFMPEG_LIBAVCODEC FFMPEG_LIBAVFORMAT FFMPEG_LIBAVUTIL FFMPEG_LIBSWSCALE FFMPEG_LIBSWRESAMPLE + ) if (FFMPEG_FOUND) set(FFMPEG_INCLUDE_DIR ${FFMPEG_AVCODEC_INCLUDE_DIR}) @@ -79,17 +80,6 @@ else (FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR) ${FFMPEG_LIBSWRESAMPLE} ) - endif (FFMPEG_FOUND) - - if (FFMPEG_FOUND) - if (NOT FFMPEG_FIND_QUIETLY) - message(STATUS "Found FFMPEG or Libav: ${FFMPEG_LIBRARIES}, ${FFMPEG_INCLUDE_DIR}") - endif (NOT FFMPEG_FIND_QUIETLY) - else (FFMPEG_FOUND) - if (FFMPEG_FIND_REQUIRED) - message(FATAL_ERROR "Could not find libavcodec or libavformat or libavutil or libswscale or libswresample") - endif (FFMPEG_FIND_REQUIRED) - endif (FFMPEG_FOUND) - -endif (FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR) + endif () +endif () diff --git a/buildfiles/cmake/FindWayland.cmake b/buildfiles/cmake/FindWayland.cmake index f93218b873..6ace12a523 100644 --- a/buildfiles/cmake/FindWayland.cmake +++ b/buildfiles/cmake/FindWayland.cmake @@ -49,11 +49,11 @@ IF (NOT WIN32) include(FindPackageHandleStandardArgs) - FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND_CLIENT DEFAULT_MSG WAYLAND_CLIENT_LIBRARIES WAYLAND_CLIENT_INCLUDE_DIR) - FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND_SERVER DEFAULT_MSG WAYLAND_SERVER_LIBRARIES WAYLAND_SERVER_INCLUDE_DIR) - FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND_EGL DEFAULT_MSG WAYLAND_EGL_LIBRARIES WAYLAND_EGL_INCLUDE_DIR) - FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND_CURSOR DEFAULT_MSG WAYLAND_CURSOR_LIBRARIES WAYLAND_CURSOR_INCLUDE_DIR) - FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND DEFAULT_MSG WAYLAND_LIBRARIES WAYLAND_INCLUDE_DIR) + FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND_CLIENT REQUIRED_VARS WAYLAND_CLIENT_LIBRARIES WAYLAND_CLIENT_INCLUDE_DIR NAME_MISMATCHED) + FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND_SERVER REQUIRED_VARS WAYLAND_SERVER_LIBRARIES WAYLAND_SERVER_INCLUDE_DIR NAME_MISMATCHED) + FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND_EGL REQUIRED_VARS WAYLAND_EGL_LIBRARIES WAYLAND_EGL_INCLUDE_DIR NAME_MISMATCHED) + FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND_CURSOR REQUIRED_VARS WAYLAND_CURSOR_LIBRARIES WAYLAND_CURSOR_INCLUDE_DIR NAME_MISMATCHED) + FIND_PACKAGE_HANDLE_STANDARD_ARGS(WAYLAND REQUIRED_VARS WAYLAND_LIBRARIES WAYLAND_INCLUDE_DIR NAME_MISMATCHED) MARK_AS_ADVANCED( WAYLAND_INCLUDE_DIR WAYLAND_LIBRARIES diff --git a/buildfiles/cmake/TCDarwinARM64.cmake b/buildfiles/cmake/TCDarwinARM64.cmake deleted file mode 100644 index d38b237910..0000000000 --- a/buildfiles/cmake/TCDarwinARM64.cmake +++ /dev/null @@ -1,2 +0,0 @@ -set(CMAKE_SYSTEM_NAME Darwin) -set(CMAKE_SYSTEM_PROCESSOR arm64) diff --git a/buildfiles/cmake/TCDarwinX86_64.cmake b/buildfiles/cmake/TCDarwinX86_64.cmake new file mode 100644 index 0000000000..0aa93407fb --- /dev/null +++ b/buildfiles/cmake/TCDarwinX86_64.cmake @@ -0,0 +1,2 @@ +set(CMAKE_SYSTEM_NAME Darwin) +set(CMAKE_SYSTEM_PROCESSOR x86_64) diff --git a/objdump.cpp b/objdump.cpp index 76caef1c9b..7955462557 100644 --- a/objdump.cpp +++ b/objdump.cpp @@ -25,6 +25,7 @@ #include #include #include +#include std::string to_hex(std::uint64_t value, bool prfx = true) { @@ -85,7 +86,7 @@ int main(int argc, char* argv[]) // Decode address and try to find the object std::uint64_t addr = -1; - std::from_chars(arg.data() + strlen("--start-address=0x"), arg.data() + arg.size(), addr, 16); + std::from_chars(arg.data() + ("--start-address=0x"sv).size(), arg.data() + arg.size(), addr, 16); for (int j = 0; j < 0x100'0000; j++) { diff --git a/rpcs3/CMakeLists.txt b/rpcs3/CMakeLists.txt index 727bee8cae..3217cc38f5 100644 --- a/rpcs3/CMakeLists.txt +++ b/rpcs3/CMakeLists.txt @@ -10,26 +10,16 @@ include(CheckFunctionExists) set(CMAKE_CXX_STANDARD 20) -set(ADDITIONAL_LIBS "") -if(CMAKE_SYSTEM_NAME STREQUAL "Linux") - #on some Linux distros shm_unlink and similar functions are in librt only - set(ADDITIONAL_LIBS ${ADDITIONAL_LIBS} "rt") -elseif(NOT WIN32 AND NOT CMAKE_CXX_FLAGS MATCHES "LIBICONV_PLUG") - #it seems like glibc includes the iconv functions we use but other libc - #implementations like the one on OSX don't seem implement them - set(ADDITIONAL_LIBS ${ADDITIONAL_LIBS} "iconv") -endif() - if(UNIX AND NOT APPLE AND NOT ANDROID) - add_definitions(-DDATADIR="${CMAKE_INSTALL_FULL_DATADIR}/rpcs3") + add_compile_definitions(DATADIR="${CMAKE_INSTALL_FULL_DATADIR}/rpcs3") # Optionally enable X11 for window management find_package(X11) if(X11_FOUND) - add_definitions(-DHAVE_X11) + add_compile_definitions(HAVE_X11) endif() find_package(Wayland) if(WAYLAND_FOUND) - add_definitions(-DHAVE_WAYLAND) + add_compile_definitions(HAVE_WAYLAND) endif() endif() @@ -78,8 +68,16 @@ if (NOT ANDROID) 3rdparty::libcurl 3rdparty::zlib 3rdparty::opencv - 3rdparty::fusion - ${ADDITIONAL_LIBS}) + 3rdparty::fusion) + + if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + #on some Linux distros shm_unlink and similar functions are in librt only + target_link_libraries(rpcs3_lib PRIVATE rt) + elseif(NOT WIN32 AND NOT CMAKE_CXX_FLAGS MATCHES "LIBICONV_PLUG") + #it seems like glibc includes the iconv functions we use but other libc + #implementations like the one on OSX don't seem implement them + target_link_libraries(rpcs3_lib PRIVATE iconv) + endif() # Unix display manager if(X11_FOUND) @@ -106,19 +104,16 @@ if (NOT ANDROID) endif() # Build rpcs3 executable + add_executable(rpcs3 WIN32 MACOSX_BUNDLE) if(WIN32) - add_executable(rpcs3 WIN32) target_sources(rpcs3 PRIVATE rpcs3.rc) target_compile_definitions(rpcs3 PRIVATE UNICODE _UNICODE) elseif(APPLE) - add_executable(rpcs3 MACOSX_BUNDLE) target_sources(rpcs3 PRIVATE rpcs3.icns update_helper.sh) set_source_files_properties(update_helper.sh PROPERTIES MACOSX_PACKAGE_LOCATION Resources) set_target_properties(rpcs3 PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/rpcs3.plist.in") - else() - add_executable(rpcs3) endif() target_sources(rpcs3 @@ -137,28 +132,23 @@ if (NOT ANDROID) # Copy icons to executable directory if(APPLE) - if (CMAKE_BUILD_TYPE MATCHES "Debug" OR CMAKE_BUILD_TYPE MATCHES "RelWithDebInfo") - set(QT_DEPLOY_FLAGS "-no-strip") - else() - set(QT_DEPLOY_FLAGS "") - endif() qt_finalize_target(rpcs3) add_custom_command(TARGET rpcs3 POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/rpcs3.icns $/../Resources/rpcs3.icns COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/bin/Icons $/../Resources/Icons COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/bin/GuiConfigs $/../Resources/GuiConfigs - COMMAND "${MACDEPLOYQT_EXECUTABLE}" "${PROJECT_BINARY_DIR}/bin/rpcs3.app" "${QT_DEPLOY_FLAGS}") + COMMAND "${MACDEPLOYQT_EXECUTABLE}" "${PROJECT_BINARY_DIR}/bin/rpcs3.app" "$<$:-no-strip>") elseif(UNIX) add_custom_command(TARGET rpcs3 POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/bin/Icons $/Icons COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/bin/GuiConfigs $/GuiConfigs) elseif(WIN32) add_custom_command(TARGET rpcs3 POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy $ $ COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/bin/Icons $/Icons COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/bin/GuiConfigs $/GuiConfigs + COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/bin/test $/test COMMAND "${WINDEPLOYQT_EXECUTABLE}" --no-compiler-runtime --no-opengl-sw --no-patchqt - --no-translations --no-system-d3d-compiler --no-system-dxc-compiler --no-ffmpeg --no-quick-import + --no-translations --no-system-d3d-compiler --no-system-dxc-compiler --no-quick-import --plugindir "$,$/plugins,$/share/qt6/plugins>" --verbose 0 "$") endif() @@ -188,7 +178,6 @@ endif() # Unit tests if(BUILD_RPCS3_TESTS) - enable_testing() find_package(GTest REQUIRED) message(STATUS "Building unit tests...") @@ -199,12 +188,16 @@ if(BUILD_RPCS3_TESTS) PRIVATE tests/test.cpp tests/test_fmt.cpp + tests/test_pair.cpp + tests/test_tuple.cpp tests/test_simple_array.cpp + tests/test_address_range.cpp ) target_link_libraries(rpcs3_test PRIVATE rpcs3_lib + rpcs3_emu GTest::gtest ) diff --git a/rpcs3/Crypto/key_vault.cpp b/rpcs3/Crypto/key_vault.cpp index 2533b4d7fc..6fa9aa0684 100644 --- a/rpcs3/Crypto/key_vault.cpp +++ b/rpcs3/Crypto/key_vault.cpp @@ -11,10 +11,10 @@ SELF_KEY::SELF_KEY(u64 ver_start, u64 ver_end, u16 rev, u32 type, const std::str version_end = ver_end; revision = rev; self_type = type; - hex_to_bytes(erk, e.c_str(), 0); - hex_to_bytes(riv, r.c_str(), 0); - hex_to_bytes(pub, pb.c_str(), 0); - hex_to_bytes(priv, pr.c_str(), 0); + hex_to_bytes(erk, e, 0); + hex_to_bytes(riv, r, 0); + hex_to_bytes(pub, pb, 0); + hex_to_bytes(priv, pr, 0); curve_type = ct; } diff --git a/rpcs3/Crypto/utils.cpp b/rpcs3/Crypto/utils.cpp index 1858fb1fa0..7432acbf62 100644 --- a/rpcs3/Crypto/utils.cpp +++ b/rpcs3/Crypto/utils.cpp @@ -7,9 +7,11 @@ #include "sha1.h" #include "sha256.h" #include "key_vault.h" +#include +#include #include -#include -#include +#include +#include #include "Utilities/StrUtil.h" #include "Utilities/File.h" @@ -21,50 +23,24 @@ // Auxiliary functions (endian swap, xor). // Hex string conversion auxiliary functions. -u64 hex_to_u64(const char* hex_str) +void hex_to_bytes(unsigned char* data, std::string_view hex_str, unsigned int str_length) { - auto length = std::strlen(hex_str); - u64 tmp = 0; - u64 result = 0; - char c; - - while (length--) - { - c = *hex_str++; - if((c >= '0') && (c <= '9')) - tmp = c - '0'; - else if((c >= 'a') && (c <= 'f')) - tmp = c - 'a' + 10; - else if((c >= 'A') && (c <= 'F')) - tmp = c - 'A' + 10; - else - tmp = 0; - result |= (tmp << (length * 4)); - } - - return result; -} - -void hex_to_bytes(unsigned char* data, const char* hex_str, unsigned int str_length) -{ - const auto strn_length = (str_length > 0) ? str_length : std::strlen(hex_str); - auto data_length = strn_length / 2; - char tmp_buf[3] = {0, 0, 0}; + const auto strn_length = (str_length > 0) ? str_length : hex_str.size(); // Don't convert if the string length is odd. if ((strn_length % 2) == 0) { - while (data_length--) + for (size_t i = 0; i < strn_length; i += 2) { - tmp_buf[0] = *hex_str++; - tmp_buf[1] = *hex_str++; - - *data++ = static_cast(hex_to_u64(tmp_buf) & 0xFF); + const auto [ptr, err] = std::from_chars(hex_str.data() + i, hex_str.data() + i + 2, *data++, 16); + if (err != std::errc()) + { + fmt::throw_exception("Failed to read hex string: %s", std::make_error_code(err).message()); + } } } } - // Crypto functions (AES128-CBC, AES128-ECB, SHA1-HMAC and AES-CMAC). void aescbc128_decrypt(unsigned char *key, unsigned char *iv, unsigned char *in, unsigned char *out, usz len) { diff --git a/rpcs3/Crypto/utils.h b/rpcs3/Crypto/utils.h index 25cdeb7193..6d9bb8092b 100644 --- a/rpcs3/Crypto/utils.h +++ b/rpcs3/Crypto/utils.h @@ -6,7 +6,8 @@ #include "util/types.hpp" -#include +#include +#include enum { CRYPTO_MAX_PATH = 4096 }; @@ -15,8 +16,7 @@ char* extract_file_name(const char* file_path, char real_file_name[CRYPTO_MAX_PA std::string sha256_get_hash(const char* data, usz size, bool lower_case); // Hex string conversion auxiliary functions. -u64 hex_to_u64(const char* hex_str); -void hex_to_bytes(unsigned char *data, const char *hex_str, unsigned int str_length); +void hex_to_bytes(unsigned char* data, std::string_view hex_str, unsigned int str_length); // Crypto functions (AES128-CBC, AES128-ECB, SHA1-HMAC and AES-CMAC). void aescbc128_decrypt(unsigned char *key, unsigned char *iv, unsigned char *in, unsigned char *out, usz len); diff --git a/rpcs3/Emu/Audio/AudioBackend.cpp b/rpcs3/Emu/Audio/AudioBackend.cpp index e0d96c8c2b..80af260414 100644 --- a/rpcs3/Emu/Audio/AudioBackend.cpp +++ b/rpcs3/Emu/Audio/AudioBackend.cpp @@ -2,6 +2,8 @@ #include "AudioBackend.h" #include "Emu/IdManager.h" #include "Emu//Cell/Modules/cellAudioOut.h" +#include +#include AudioBackend::AudioBackend() {} @@ -57,15 +59,25 @@ f32 AudioBackend::apply_volume(const VolumeParam& param, u32 sample_cnt, const f { ensure(param.ch_cnt > 1 && param.ch_cnt % 2 == 0); // Tends to produce faster code + // Fast path when no volume change is needed + if (param.current_volume == param.target_volume) + { + apply_volume_static(param.target_volume, sample_cnt, src, dst); + return param.target_volume; + } + const f32 vol_incr = (param.target_volume - param.initial_volume) / (VOLUME_CHANGE_DURATION * param.freq); f32 crnt_vol = param.current_volume; u32 sample_idx = 0; + // Use epsilon for float comparison to avoid infinite loops + constexpr f32 epsilon = 1e-6f; + if (vol_incr >= 0) { - for (sample_idx = 0; sample_idx < sample_cnt && crnt_vol != param.target_volume; sample_idx += param.ch_cnt) + for (sample_idx = 0; sample_idx < sample_cnt && (param.target_volume - crnt_vol) > epsilon; 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++) { @@ -75,9 +87,9 @@ f32 AudioBackend::apply_volume(const VolumeParam& param, u32 sample_cnt, const f } else { - for (sample_idx = 0; sample_idx < sample_cnt && crnt_vol != param.target_volume; sample_idx += param.ch_cnt) + for (sample_idx = 0; sample_idx < sample_cnt && (crnt_vol - param.target_volume) > epsilon; 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++) { @@ -96,6 +108,25 @@ f32 AudioBackend::apply_volume(const VolumeParam& param, u32 sample_cnt, const f void AudioBackend::apply_volume_static(f32 vol, u32 sample_cnt, const f32* src, f32* dst) { + // Improved volume application with better precision + if (vol == 1.0f) + { + // Fast path for unity gain - no multiplication needed + if (src != dst) + { + std::memcpy(dst, src, sample_cnt * sizeof(f32)); + } + return; + } + + if (vol == 0.0f) + { + // Fast path for mute + std::memset(dst, 0, sample_cnt * sizeof(f32)); + return; + } + + // Process samples with improved precision for (u32 i = 0; i < sample_cnt; i++) { dst[i] = src[i] * vol; @@ -104,9 +135,37 @@ void AudioBackend::apply_volume_static(f32 vol, u32 sample_cnt, const f32* src, void AudioBackend::normalize(u32 sample_cnt, const f32* src, f32* dst) { + // Improved normalization with soft clipping and better dynamic range handling + constexpr f32 soft_clip_threshold = 0.95f; + constexpr f32 hard_clip_limit = 1.0f; + for (u32 i = 0; i < sample_cnt; i++) { - dst[i] = std::clamp(src[i], -1.0f, 1.0f); + f32 sample = src[i]; + f32 abs_sample = std::abs(sample); + + if (abs_sample > soft_clip_threshold) + { + // Apply soft clipping for smoother distortion + f32 sign = std::copysign(1.0f, sample); + if (abs_sample > hard_clip_limit) + { + // Hard limit to prevent overflow + dst[i] = sign * hard_clip_limit; + } + else + { + // Soft clipping using tanh-like curve + f32 excess = (abs_sample - soft_clip_threshold) / (hard_clip_limit - soft_clip_threshold); + f32 soft_factor = soft_clip_threshold + (hard_clip_limit - soft_clip_threshold) * std::tanh(excess); + dst[i] = sign * soft_factor; + } + } + else + { + // No clipping needed + dst[i] = sample; + } } } diff --git a/rpcs3/Emu/Audio/AudioBackend.h b/rpcs3/Emu/Audio/AudioBackend.h index cbd8e045c9..bc07a1e46f 100644 --- a/rpcs3/Emu/Audio/AudioBackend.h +++ b/rpcs3/Emu/Audio/AudioBackend.h @@ -17,11 +17,11 @@ enum : u32 enum class AudioFreq : u32 { - FREQ_32K = 32000, - FREQ_44K = 44100, - FREQ_48K = 48000, - FREQ_88K = 88200, - FREQ_96K = 96000, + FREQ_32K = 32000, + FREQ_44K = 44100, + FREQ_48K = 48000, + FREQ_88K = 88200, + FREQ_96K = 96000, FREQ_176K = 176400, FREQ_192K = 192000, }; @@ -35,7 +35,7 @@ enum class AudioSampleSize : u32 // This enum is only used for emulation enum class AudioChannelCnt : u32 { - STEREO = 2, + STEREO = 2, SURROUND_5_1 = 6, SURROUND_7_1 = 8, }; @@ -49,7 +49,6 @@ enum class AudioStateEvent : u32 class AudioBackend { public: - struct VolumeParam { f32 initial_volume = 1.0f; @@ -93,7 +92,10 @@ public: virtual f64 GetCallbackFrameLen() = 0; // Returns true if audio is currently being played, false otherwise. Reflects end result of Play() and Pause() calls. - virtual bool IsPlaying() { return m_playing; } + virtual bool IsPlaying() + { + return m_playing; + } // Start playing enqueued data. virtual void Play() = 0; @@ -105,17 +107,26 @@ public: * This virtual method should be reimplemented if backend can fail to be initialized under non-error conditions * eg. when there is no audio devices attached */ - virtual bool Initialized() { return true; } + virtual bool Initialized() + { + return true; + } /* * This virtual method should be reimplemented if backend can fail during normal operation */ - virtual bool Operational() { return true; } + virtual bool Operational() + { + return true; + } /* * This virtual method should be reimplemented if backend can report device changes */ - virtual bool DefaultDeviceChanged() { return false; } + virtual bool DefaultDeviceChanged() + { + return false; + } /* * Helper methods @@ -180,20 +191,21 @@ public: /* * Downmix audio stream. */ - template + template static void downmix(u32 sample_cnt, const f32* src, f32* dst) { const u32 dst_ch_cnt = default_layout_channel_count(dst_layout); - if (static_cast(src_ch_cnt) <= dst_ch_cnt) fmt::throw_exception("src channel count must be bigger than dst channel count"); + if (static_cast(src_ch_cnt) <= dst_ch_cnt) + fmt::throw_exception("src channel count must be bigger than dst channel count"); static constexpr f32 center_coef = std::numbers::sqrt2_v / 2; static constexpr f32 surround_coef = std::numbers::sqrt2_v / 2; for (u32 src_sample = 0, dst_sample = 0; src_sample < sample_cnt; src_sample += static_cast(src_ch_cnt), dst_sample += dst_ch_cnt) { - const f32 left = src[src_sample + 0]; + const f32 left = src[src_sample + 0]; const f32 right = src[src_sample + 1]; - + if constexpr (src_ch_cnt == AudioChannelCnt::STEREO) { if constexpr (dst_layout == audio_channel_layout::mono) @@ -203,9 +215,9 @@ public: } else if constexpr (src_ch_cnt == AudioChannelCnt::SURROUND_5_1) { - const f32 center = src[src_sample + 2]; - const f32 low_freq = src[src_sample + 3]; - const f32 side_left = src[src_sample + 4]; + const f32 center = src[src_sample + 2]; + const f32 low_freq = src[src_sample + 3]; + const f32 side_left = src[src_sample + 4]; const f32 side_right = src[src_sample + 5]; if constexpr (dst_layout == audio_channel_layout::quadraphonic || dst_layout == audio_channel_layout::quadraphonic_lfe) @@ -239,11 +251,11 @@ public: } else if constexpr (src_ch_cnt == AudioChannelCnt::SURROUND_7_1) { - const f32 center = src[src_sample + 2]; - const f32 low_freq = src[src_sample + 3]; - const f32 rear_left = src[src_sample + 4]; + const f32 center = src[src_sample + 2]; + const f32 low_freq = src[src_sample + 3]; + const f32 rear_left = src[src_sample + 4]; const f32 rear_right = src[src_sample + 5]; - const f32 side_left = src[src_sample + 6]; + const f32 side_left = src[src_sample + 6]; const f32 side_right = src[src_sample + 7]; if constexpr (dst_layout == audio_channel_layout::surround_5_1) @@ -372,12 +384,12 @@ protected: void setup_channel_layout(u32 input_channel_count, u32 output_channel_count, audio_channel_layout layout, logs::channel& log); AudioSampleSize m_sample_size = AudioSampleSize::FLOAT; - AudioFreq m_sampling_rate = AudioFreq::FREQ_48K; - u32 m_channels = 2; + AudioFreq m_sampling_rate = AudioFreq::FREQ_48K; + u32 m_channels = 2; audio_channel_layout m_layout = audio_channel_layout::automatic; std::timed_mutex m_cb_mutex{}; - std::function m_write_callback{}; + std::function m_write_callback{}; shared_mutex m_state_cb_mutex{}; std::function m_state_callback{}; @@ -385,6 +397,5 @@ protected: bool m_playing = false; private: - - static constexpr f32 VOLUME_CHANGE_DURATION = 0.016f; // sec + static constexpr f32 VOLUME_CHANGE_DURATION = 0.032f; // sec - Increased for smoother transitions }; 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); }; diff --git a/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.cpp b/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.cpp index 0a84d747bf..b9a4daeecc 100644 --- a/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.cpp +++ b/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.cpp @@ -337,7 +337,7 @@ f64 XAudio2Backend::GetCallbackFrameLen() return std::max(min_latency, _10ms); // 10ms is the minimum for XAudio } -void XAudio2Backend::OnVoiceProcessingPassStart(UINT32 BytesRequired) +void XAudio2Backend::OnVoiceProcessingPassStart(UINT32 BytesRequired) noexcept { std::unique_lock lock(m_cb_mutex, std::defer_lock); if (BytesRequired && !m_reset_req.observe() && lock.try_lock_for(std::chrono::microseconds{50}) && m_write_callback && m_playing) @@ -366,7 +366,7 @@ void XAudio2Backend::OnVoiceProcessingPassStart(UINT32 BytesRequired) } } -void XAudio2Backend::OnCriticalError(HRESULT Error) +void XAudio2Backend::OnCriticalError(HRESULT Error) noexcept { XAudio.error("OnCriticalError() called: %s (0x%08x)", std::system_category().message(Error), static_cast(Error)); diff --git a/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.h b/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.h index 0312e7f633..1bab624d67 100644 --- a/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.h +++ b/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.h @@ -54,18 +54,18 @@ private: atomic_t m_reset_req = false; // XAudio voice callbacks - void OnVoiceProcessingPassStart(UINT32 BytesRequired) override; - void OnVoiceProcessingPassEnd() override {} - void OnStreamEnd() override {} - void OnBufferStart(void* /* pBufferContext */) override {} - void OnBufferEnd(void* /* pBufferContext*/) override {} - void OnLoopEnd(void* /* pBufferContext */) override {} - void OnVoiceError(void* /* pBufferContext */, HRESULT /* Error */) override {} + void OnVoiceProcessingPassStart(UINT32 BytesRequired) noexcept override; + void OnVoiceProcessingPassEnd() noexcept override {} + void OnStreamEnd() noexcept override {} + void OnBufferStart(void* /* pBufferContext */) noexcept override {} + void OnBufferEnd(void* /* pBufferContext*/) noexcept override {} + void OnLoopEnd(void* /* pBufferContext */) noexcept override {} + void OnVoiceError(void* /* pBufferContext */, HRESULT /* Error */) noexcept override {} // XAudio engine callbacks - void OnProcessingPassStart() override {}; - void OnProcessingPassEnd() override {}; - void OnCriticalError(HRESULT Error) override; + void OnProcessingPassStart() noexcept override {}; + void OnProcessingPassEnd() noexcept override {}; + void OnCriticalError(HRESULT Error) noexcept override; // IMMNotificationClient callbacks IFACEMETHODIMP_(ULONG) AddRef() override { return 1; }; diff --git a/rpcs3/Emu/Audio/audio_resampler.cpp b/rpcs3/Emu/Audio/audio_resampler.cpp index 32c7109b4c..108c63e66d 100644 --- a/rpcs3/Emu/Audio/audio_resampler.cpp +++ b/rpcs3/Emu/Audio/audio_resampler.cpp @@ -4,8 +4,12 @@ audio_resampler::audio_resampler() { - resampler.setSetting(SETTING_SEQUENCE_MS, 20); // Resampler frame size (reduce latency at cost of slight sound quality degradation) - resampler.setSetting(SETTING_USE_QUICKSEEK, 1); // Use fast quick seeking algorithm (substantally reduces computation time) + // Improved quality settings for better audio output + resampler.setSetting(SETTING_SEQUENCE_MS, 40); // Increased sequence length for better quality (was 20) + resampler.setSetting(SETTING_SEEKWINDOW_MS, 15); // Better seeking window for smoother transitions + resampler.setSetting(SETTING_OVERLAP_MS, 8); // Improved overlap for better quality + resampler.setSetting(SETTING_USE_QUICKSEEK, 0); // Disable quick seek for higher quality (was 1) + resampler.setSetting(SETTING_USE_AA_FILTER, 1); // Enable anti-aliasing filter for cleaner sound } audio_resampler::~audio_resampler() @@ -35,7 +39,7 @@ std::pair audio_resampler::get_samples(u32 { // NOTE: Make sure to get the buffer first because receiveSamples advances its position internally // and std::make_pair evaluates the second parameter first... - f32 *const buf = resampler.bufBegin(); + f32* const buf = resampler.bufBegin(); return std::make_pair(buf, resampler.receiveSamples(sample_cnt)); } diff --git a/rpcs3/Emu/Audio/audio_utils.cpp b/rpcs3/Emu/Audio/audio_utils.cpp index bcff7bf947..9df1d6ff1d 100644 --- a/rpcs3/Emu/Audio/audio_utils.cpp +++ b/rpcs3/Emu/Audio/audio_utils.cpp @@ -4,6 +4,7 @@ #include "Emu/System.h" #include "Emu/IdManager.h" #include "Emu/RSX/Overlays/overlay_message.h" +#include namespace audio { @@ -24,16 +25,33 @@ namespace audio void change_volume(s32 delta) { // Ignore if muted - if (g_fxo->get().audio_muted) return; + if (g_fxo->get().audio_muted) + return; const s32 old_volume = g_cfg.audio.volume; - const s32 new_volume = old_volume + delta; - if (old_volume == new_volume) return; + // Apply non-linear volume scaling for better perceived volume control + // Use smaller steps at lower volumes for finer control + s32 adjusted_delta = delta; + if (old_volume < 25 && abs(delta) > 1) + { + // Smaller steps at low volume for better control + adjusted_delta = delta > 0 ? 1 : -1; + } + else if (old_volume > 75 && abs(delta) < 5) + { + // Larger steps at high volume for faster adjustment + adjusted_delta = delta > 0 ? std::min(delta * 2, 5) : std::max(delta * 2, -5); + } + + const s32 new_volume = old_volume + adjusted_delta; + + if (old_volume == new_volume) + return; g_cfg.audio.volume.set(std::clamp(new_volume, g_cfg.audio.volume.min, g_cfg.audio.volume.max)); Emu.GetCallbacks().update_emu_settings(); - rsx::overlays::queue_message(get_localized_string(localized_string_id::AUDIO_CHANGED, fmt::format("%d%%", g_cfg.audio.volume.get()).c_str()), 3'000'000); + rsx::overlays::queue_message(localized_string(localized_string_id::AUDIO_CHANGED, "%d%%", g_cfg.audio.volume.get()), 3'000'000, {}, rsx::overlays::message_pin_location::top_left, {}, true, true); } -} +} // namespace audio diff --git a/rpcs3/Emu/CMakeLists.txt b/rpcs3/Emu/CMakeLists.txt index 0164376dff..139688947d 100644 --- a/rpcs3/Emu/CMakeLists.txt +++ b/rpcs3/Emu/CMakeLists.txt @@ -29,6 +29,10 @@ if(USE_ASAN) set_source_files_properties(../../Utilities/Thread.cpp PROPERTIES COMPILE_DEFINITIONS USE_ASAN) endif() +if(NOT USE_SYSTEM_OPENAL) + target_compile_definitions(rpcs3_emu PUBLIC AL_LIBTYPE_STATIC) +endif() + if(HAS_MEMORY_BREAKPOINTS) target_compile_definitions(rpcs3_emu PRIVATE RPCS3_HAS_MEMORY_BREAKPOINTS) endif() @@ -398,6 +402,7 @@ target_sources(rpcs3_emu PRIVATE Io/GunCon3.cpp Io/Infinity.cpp Io/interception.cpp + Io/KamenRider.cpp Io/KeyboardHandler.cpp Io/midi_config_types.cpp Io/mouse_config.cpp @@ -573,6 +578,7 @@ if(TARGET 3rdparty_vulkan) RSX/VK/vkutils/buffer_object.cpp RSX/VK/vkutils/chip_class.cpp RSX/VK/vkutils/commands.cpp + RSX/VK/vkutils/ex.cpp RSX/VK/vkutils/data_heap.cpp RSX/VK/vkutils/descriptors.cpp RSX/VK/vkutils/image.cpp @@ -585,6 +591,7 @@ if(TARGET 3rdparty_vulkan) RSX/VK/vkutils/device.cpp RSX/VK/vkutils/sampler.cpp RSX/VK/vkutils/shared.cpp + RSX/VK/vkutils/unique_resource.cpp RSX/VK/VKAsyncScheduler.cpp RSX/VK/VKCommandStream.cpp RSX/VK/VKCommonDecompiler.cpp @@ -638,6 +645,7 @@ target_link_libraries(rpcs3_emu 3rdparty::libevdev 3rdparty::flatbuffers 3rdparty::pugixml + 3rdparty::vulkanmemoryallocator Threads::Threads PRIVATE 3rdparty::glslang diff --git a/rpcs3/Emu/CPU/CPUThread.cpp b/rpcs3/Emu/CPU/CPUThread.cpp index 242dcb9d9f..d858735edf 100644 --- a/rpcs3/Emu/CPU/CPUThread.cpp +++ b/rpcs3/Emu/CPU/CPUThread.cpp @@ -659,7 +659,7 @@ void cpu_thread::operator()() } case thread_class::spu: { - if (g_cfg.core.spu_prof) + if (g_cfg.core.spu_prof || g_cfg.core.spu_debug) { g_fxo->get().registered.push(id); } @@ -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); } } } @@ -1546,7 +1546,7 @@ void cpu_thread::flush_profilers() noexcept return; } - if (g_cfg.core.spu_prof) + if (g_cfg.core.spu_prof || g_cfg.core.spu_debug) { g_fxo->get().registered.push(0); } @@ -1585,22 +1585,30 @@ extern bool try_lock_spu_threads_in_a_state_compatible_with_savestates(bool reve idm::select>([&](u32 id, spu_thread& spu) { - spu_list.emplace_back(ensure(idm::get_unlocked>(id))); + if (give_up) + { + return; + } - if (spu.current_func && spu.unsavable) + if (spu.current_func && spu.unsavable && !force_collect) { const u64 start = spu.start_time; - // Automatically give up if it is asleep 15 seconds or more - if (start && current > start && current - start >= 15'000'000) + // Automatically give up if it is asleep 5 seconds or more + if (start && current > start && current - start >= 5'000'000) { give_up = true; + return; } } + + spu_list.emplace_back(ensure(idm::get_unlocked>(id))); }); - if (!force_collect && give_up) + if (give_up) { + spu_list.clear(); + old_counter = umax; return decltype(&spu_list){}; } @@ -1625,6 +1633,7 @@ extern bool try_lock_spu_threads_in_a_state_compatible_with_savestates(bool reve } else if (get_system_time() - start >= 150'000) { + std::this_thread::sleep_for(1ms); passed_count++; start = 0; continue; @@ -1636,37 +1645,29 @@ extern bool try_lock_spu_threads_in_a_state_compatible_with_savestates(bool reve if (!spu_list) { // Give up for now - std::this_thread::sleep_for(10ms); + std::this_thread::sleep_for(50ms); passed_count++; start = 0; continue; } // Avoid using suspend_all when more than 2 threads known to be unsavable - u32 unsavable_threads = 0; + u32 savable_threads = 0; for (auto& spu : *spu_list) { - if (spu->unsavable) + if (!spu->unsavable) { - unsavable_threads++; - - if (unsavable_threads >= 3) - { - break; - } + savable_threads++; } } - if (unsavable_threads >= 3) + if (!savable_threads) { std::this_thread::yield(); continue; } - // Flag for optimization - bool paused_anyone = false; - if (cpu_thread::suspend_all(nullptr, {}, [&]() { if (!get_spus(false, true)) @@ -1695,19 +1696,13 @@ extern bool try_lock_spu_threads_in_a_state_compatible_with_savestates(bool reve break; } } - else - { - paused_anyone = true; - ensure(!spu->state.test_and_set(cpu_flag::dbg_global_pause)); - } } - if (failed && paused_anyone) + for (auto& spu : *spu_list) { - // For faster signalling, first remove state flags then batch notifications - for (auto& spu : *spu_list) + if (!failed && !is_emu_paused) { - spu->state -= cpu_flag::dbg_global_pause; + ensure(!spu->state.test_and_set(cpu_flag::dbg_global_pause)); } } @@ -1719,13 +1714,6 @@ extern bool try_lock_spu_threads_in_a_state_compatible_with_savestates(bool reve return false; } - if (!paused_anyone) - { - // Need not do anything - std::this_thread::yield(); - continue; - } - for (auto& spu : *spu_list) { if (spu->state & cpu_flag::wait) @@ -1755,7 +1743,7 @@ extern bool try_lock_spu_threads_in_a_state_compatible_with_savestates(bool reve { spu->state.notify_one(); } - }; + } return false; } diff --git a/rpcs3/Emu/CPU/CPUTranslator.cpp b/rpcs3/Emu/CPU/CPUTranslator.cpp index f6893358ff..d17b1ec977 100644 --- a/rpcs3/Emu/CPU/CPUTranslator.cpp +++ b/rpcs3/Emu/CPU/CPUTranslator.cpp @@ -135,7 +135,10 @@ void cpu_translator::initialize(llvm::LLVMContext& context, llvm::ExecutionEngin cpu == "bdver4" || cpu == "znver1" || cpu == "znver2" || - cpu == "znver3") + cpu == "znver3" || + cpu == "arrowlake" || + cpu == "arrowlake-s" || + cpu == "lunarlake") { m_use_fma = true; m_use_avx = true; @@ -157,7 +160,10 @@ void cpu_translator::initialize(llvm::LLVMContext& context, llvm::ExecutionEngin cpu == "cooperlake" || cpu == "alderlake" || cpu == "raptorlake" || - cpu == "meteorlake") + cpu == "meteorlake" || + cpu == "arrowlake" || + cpu == "arrowlake-s" || + cpu == "lunarlake") { m_use_vnni = true; } @@ -167,7 +173,10 @@ void cpu_translator::initialize(llvm::LLVMContext& context, llvm::ExecutionEngin cpu == "gracemont" || cpu == "alderlake" || cpu == "raptorlake" || - cpu == "meteorlake") + cpu == "meteorlake" || + cpu == "arrowlake" || + cpu == "arrowlake-s" || + cpu == "lunarlake") { m_use_gfni = true; } diff --git a/rpcs3/Emu/CPU/CPUTranslator.h b/rpcs3/Emu/CPU/CPUTranslator.h index 088bd0801b..8d1a5d42aa 100644 --- a/rpcs3/Emu/CPU/CPUTranslator.h +++ b/rpcs3/Emu/CPU/CPUTranslator.h @@ -434,7 +434,7 @@ struct llvm_value_t : llvm_value_t static llvm::Type* get_type(llvm::LLVMContext& context) { - return llvm_value_t::get_type(context)->getPointerTo(); + return llvm::PointerType::getUnqual(context); } }; diff --git a/rpcs3/Emu/Cell/Modules/cellAtracXdec.cpp b/rpcs3/Emu/Cell/Modules/cellAtracXdec.cpp index 36228d8a5e..c55cf7b60f 100644 --- a/rpcs3/Emu/Cell/Modules/cellAtracXdec.cpp +++ b/rpcs3/Emu/Cell/Modules/cellAtracXdec.cpp @@ -111,7 +111,41 @@ void AtracXdecDecoder::alloc_avcodec() fmt::throw_exception("avcodec_find_decoder() failed"); } - ensure(!(codec->capabilities & AV_CODEC_CAP_SUBFRAMES)); + packet = av_packet_alloc(); + if (!packet) + { + fmt::throw_exception("av_packet_alloc() failed"); + } + + frame = av_frame_alloc(); + if (!frame) + { + fmt::throw_exception("av_frame_alloc() failed"); + } +} + +void AtracXdecDecoder::free_avcodec() +{ + if (packet) + { + av_packet_free(&packet); + } + if (frame) + { + av_frame_free(&frame); + } + if (ctx) + { + avcodec_free_context(&ctx); + } +} + +void AtracXdecDecoder::init_avcodec() +{ + if (ctx) + { + avcodec_free_context(&ctx); + } ctx = avcodec_alloc_context3(codec); if (!ctx) @@ -133,34 +167,6 @@ void AtracXdecDecoder::alloc_avcodec() frame->buf[0] = av_buffer_create(frame->data[0], ATXDEC_SAMPLES_PER_FRAME * sizeof(f32) * frame->ch_layout.nb_channels, [](void*, uint8_t*){}, nullptr, 0); return 0; }; - - packet = av_packet_alloc(); - if (!packet) - { - fmt::throw_exception("av_packet_alloc() failed"); - } - - frame = av_frame_alloc(); - if (!frame) - { - fmt::throw_exception("av_frame_alloc() failed"); - } -} - -void AtracXdecDecoder::free_avcodec() -{ - av_packet_free(&packet); - av_frame_free(&frame); - avcodec_free_context(&ctx); -} - -void AtracXdecDecoder::init_avcodec() -{ - if (int err = avcodec_close(ctx); err) - { - fmt::throw_exception("avcodec_close() failed (err=0x%x='%s')", err, utils::av_error_to_string(err)); - } - ctx->block_align = nbytes; ctx->ch_layout.nb_channels = nch_in; ctx->sample_rate = sampling_freq; diff --git a/rpcs3/Emu/Cell/Modules/cellAtracXdec.h b/rpcs3/Emu/Cell/Modules/cellAtracXdec.h index a670a242c2..7fc5e49956 100644 --- a/rpcs3/Emu/Cell/Modules/cellAtracXdec.h +++ b/rpcs3/Emu/Cell/Modules/cellAtracXdec.h @@ -192,10 +192,10 @@ struct AtracXdecDecoder // HLE exclusive b8 config_is_set = false; // For savestates - const AVCodec* codec; - AVCodecContext* ctx; - AVPacket* packet; - AVFrame* frame; + const AVCodec* codec = nullptr; + AVCodecContext* ctx = nullptr; + AVPacket* packet = nullptr; + AVFrame* frame = nullptr; u8 spurs_stuff[84]; // 120 bytes on LLE, pointers to CellSpurs, CellSpursTaskset, etc. diff --git a/rpcs3/Emu/Cell/Modules/cellAvconfExt.cpp b/rpcs3/Emu/Cell/Modules/cellAvconfExt.cpp index b4508ada5b..4851895537 100644 --- a/rpcs3/Emu/Cell/Modules/cellAvconfExt.cpp +++ b/rpcs3/Emu/Cell/Modules/cellAvconfExt.cpp @@ -520,7 +520,7 @@ error_code cellVideoOutGetScreenSize(u32 videoOut, vm::ptr screenSize) return CELL_VIDEO_OUT_ERROR_UNSUPPORTED_VIDEO_OUT; } - if (g_cfg.video.stereo_render_mode != stereo_render_mode_options::disabled) + if (g_cfg.video.stereo_enabled) { // Return Playstation 3D display value // Some games call this function when 3D is enabled diff --git a/rpcs3/Emu/Cell/Modules/cellGame.cpp b/rpcs3/Emu/Cell/Modules/cellGame.cpp index 17e190898d..371aa2a7b3 100644 --- a/rpcs3/Emu/Cell/Modules/cellGame.cpp +++ b/rpcs3/Emu/Cell/Modules/cellGame.cpp @@ -464,6 +464,8 @@ error_code cellHddGameCheck(ppu_thread& ppu, u32 version, vm::cptr dirName { get->isNewData = CELL_HDDGAME_ISNEWDATA_NODIR; get->getParam = {}; + + cellGame.warning("cellHddGameCheck(): New data."); } else { @@ -476,13 +478,22 @@ error_code cellHddGameCheck(ppu_thread& ppu, u32 version, vm::cptr dirName if (psf.contains("RESOLUTION")) get->getParam.resolution = ::at32(psf, "RESOLUTION").as_integer(); if (psf.contains("SOUND_FORMAT")) get->getParam.soundFormat = ::at32(psf, "SOUND_FORMAT").as_integer(); if (psf.contains("TITLE")) strcpy_trunc(get->getParam.title, ::at32(psf, "TITLE").as_string()); - if (psf.contains("APP_VER")) strcpy_trunc(get->getParam.dataVersion, ::at32(psf, "APP_VER").as_string()); - if (psf.contains("TITLE_ID")) strcpy_trunc(get->getParam.titleId, ::at32(psf, "TITLE_ID").as_string()); + + // Old games do not have APP_VER key + strcpy_trunc(get->getParam.dataVersion, psf::get_string(psf, "APP_VER", psf::get_string(sfo, "VERSION", ""))); + + if (psf.contains("TITLE_ID")) + { + strcpy_trunc(get->getParam.titleId, ::at32(psf, "TITLE_ID").as_string()); + } for (u32 i = 0; i < CELL_HDDGAME_SYSP_LANGUAGE_NUM; i++) { strcpy_trunc(get->getParam.titleLang[i], psf::get_string(psf, fmt::format("TITLE_%02d", i))); } + + cellGame.warning("cellHddGameCheck(): Data exists:\nATTRIBUTE: 0x%x, RESOLUTION: 0x%x, RESOLUTION: 0x%x, SOUND_FORMAT: 0x%x, dataVersion: %s" + , get->getParam.attribute, get->getParam.resolution, get->getParam.soundFormat, get->getParam.soundFormat, std::span(reinterpret_cast(get->getParam.dataVersion), 6)); } // TODO ? @@ -550,7 +561,7 @@ error_code cellHddGameCheck(ppu_thread& ppu, u32 version, vm::cptr dirName case CELL_HDDGAME_CBRESULT_ERR_NOSPACE: cellGame.error("cellHddGameCheck(): callback returned CELL_HDDGAME_CBRESULT_ERR_NOSPACE. Space Needed: %d KB", result->errNeedSizeKB); - error_msg = get_localized_string(localized_string_id::CELL_HDD_GAME_CHECK_NOSPACE, fmt::format("%d", result->errNeedSizeKB).c_str()); + error_msg = get_localized_string(localized_string_id::CELL_HDD_GAME_CHECK_NOSPACE, "%d", result->errNeedSizeKB); break; case CELL_HDDGAME_CBRESULT_ERR_BROKEN: @@ -565,12 +576,12 @@ error_code cellHddGameCheck(ppu_thread& ppu, u32 version, vm::cptr dirName case CELL_HDDGAME_CBRESULT_ERR_INVALID: cellGame.error("cellHddGameCheck(): callback returned CELL_HDDGAME_CBRESULT_ERR_INVALID. Error message: %s", result->invalidMsg); - error_msg = get_localized_string(localized_string_id::CELL_HDD_GAME_CHECK_INVALID, fmt::format("%s", result->invalidMsg).c_str()); + error_msg = get_localized_string(localized_string_id::CELL_HDD_GAME_CHECK_INVALID, "%s", result->invalidMsg); break; default: cellGame.error("cellHddGameCheck(): callback returned unknown error (code=0x%x). Error message: %s", result->invalidMsg); - error_msg = get_localized_string(localized_string_id::CELL_HDD_GAME_CHECK_INVALID, fmt::format("%s", result->invalidMsg).c_str()); + error_msg = get_localized_string(localized_string_id::CELL_HDD_GAME_CHECK_INVALID, "%s", result->invalidMsg); break; } @@ -1169,7 +1180,7 @@ error_code cellGameDataCheckCreate2(ppu_thread& ppu, u32 version, vm::cptr } case CELL_GAMEDATA_CBRESULT_ERR_NOSPACE: cellGame.error("cellGameDataCheckCreate2(): callback returned CELL_GAMEDATA_CBRESULT_ERR_NOSPACE. Space Needed: %d KB", cbResult->errNeedSizeKB); - error_msg = get_localized_string(localized_string_id::CELL_GAMEDATA_CHECK_NOSPACE, fmt::format("%d", cbResult->errNeedSizeKB).c_str()); + error_msg = get_localized_string(localized_string_id::CELL_GAMEDATA_CHECK_NOSPACE, "%d", cbResult->errNeedSizeKB); break; case CELL_GAMEDATA_CBRESULT_ERR_BROKEN: @@ -1184,12 +1195,12 @@ error_code cellGameDataCheckCreate2(ppu_thread& ppu, u32 version, vm::cptr case CELL_GAMEDATA_CBRESULT_ERR_INVALID: cellGame.error("cellGameDataCheckCreate2(): callback returned CELL_GAMEDATA_CBRESULT_ERR_INVALID. Error message: %s", cbResult->invalidMsg); - error_msg = get_localized_string(localized_string_id::CELL_GAMEDATA_CHECK_INVALID, fmt::format("%s", cbResult->invalidMsg).c_str()); + error_msg = get_localized_string(localized_string_id::CELL_GAMEDATA_CHECK_INVALID, "%s", cbResult->invalidMsg); break; default: cellGame.error("cellGameDataCheckCreate2(): callback returned unknown error (code=0x%x). Error message: %s", cbResult->invalidMsg); - error_msg = get_localized_string(localized_string_id::CELL_GAMEDATA_CHECK_INVALID, fmt::format("%s", cbResult->invalidMsg).c_str()); + error_msg = get_localized_string(localized_string_id::CELL_GAMEDATA_CHECK_INVALID, "%s", cbResult->invalidMsg); break; } @@ -1686,7 +1697,7 @@ error_code cellGameContentErrorDialog(s32 type, s32 errNeedSizeKB, vm::cptr CELL_GAME_ERRDIALOG_NOSPACE, msg_dialog_source::_cellGame); diff --git a/rpcs3/Emu/Cell/Modules/cellGame.h b/rpcs3/Emu/Cell/Modules/cellGame.h index a0506cef9d..4121b7fd83 100644 --- a/rpcs3/Emu/Cell/Modules/cellGame.h +++ b/rpcs3/Emu/Cell/Modules/cellGame.h @@ -224,8 +224,8 @@ enum struct CellGameDataSystemFileParam { char title[CELL_GAMEDATA_SYSP_TITLE_SIZE]; - char titleLang[CELL_GAMEDATA_SYSP_LANGUAGE_NUM][CELL_GAMEDATA_SYSP_TITLE_SIZE]; - char titleId[CELL_GAMEDATA_SYSP_TITLEID_SIZE]; + char titleLang[CELL_GAMEDATA_SYSP_LANGUAGE_NUM][CELL_GAMEDATA_SYSP_TITLE_SIZE]; // 0x80 + char titleId[CELL_GAMEDATA_SYSP_TITLEID_SIZE]; // 0xA80 char reserved0[2]; char dataVersion[CELL_GAMEDATA_SYSP_VERSION_SIZE]; char reserved1[2]; @@ -248,13 +248,13 @@ struct CellGameDataStatGet { be_t hddFreeSizeKB; be_t isNewData; - char contentInfoPath[CELL_GAMEDATA_PATH_MAX]; - char gameDataPath[CELL_GAMEDATA_PATH_MAX]; - char reserved0[2]; - be_t st_atime_; - be_t st_mtime_; - be_t st_ctime_; - CellGameDataSystemFileParam getParam; + char contentInfoPath[CELL_GAMEDATA_PATH_MAX]; // 0x8 + char gameDataPath[CELL_GAMEDATA_PATH_MAX]; // 0x427 + char reserved0[2]; // 0x846 + be_t st_atime_; // 0x848 + be_t st_mtime_; // 0x850 + be_t st_ctime_; // 0x858 + CellGameDataSystemFileParam getParam; // 0x860 be_t sizeKB; be_t sysSizeKB; char reserved1[68]; diff --git a/rpcs3/Emu/Cell/Modules/cellGem.cpp b/rpcs3/Emu/Cell/Modules/cellGem.cpp index ddb582c5d8..f7fbf90fae 100644 --- a/rpcs3/Emu/Cell/Modules/cellGem.cpp +++ b/rpcs3/Emu/Cell/Modules/cellGem.cpp @@ -351,7 +351,7 @@ public: for (u32 i = 0; i < CELL_GEM_MAX_NUM; i++) { const auto& pad = ::at32(handler->GetPads(), pad_num(i)); - const bool connected = pad && (pad->m_port_status & CELL_PAD_STATUS_CONNECTED) && i < attribute.max_connect; + const bool connected = pad && pad->is_connected() && !pad->is_copilot() && i < attribute.max_connect; const bool is_real_move = g_cfg.io.move != move_handler::real || pad->m_pad_handler == pad_handler::move; update_connection(i, connected && is_real_move); @@ -361,7 +361,7 @@ public: case move_handler::mouse: case move_handler::raw_mouse: { - auto& handler = g_fxo->get(); + auto& handler = *ensure(g_fxo->try_get()); std::lock_guard mouse_lock(handler.mutex); const MouseInfo& info = handler.GetInfo(); @@ -374,7 +374,7 @@ public: #ifdef HAVE_LIBEVDEV case move_handler::gun: { - gun_thread& gun = g_fxo->get(); + gun_thread& gun = *ensure(g_fxo->try_get()); std::scoped_lock lock(gun.handler.mutex); gun.num_devices = gun.handler.init() ? gun.handler.get_num_guns() : 0; @@ -407,7 +407,7 @@ public: std::lock_guard pad_lock(pad::g_pad_mutex); const auto handler = pad::get_pad_thread(); const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num)); - if (pad && pad->m_pad_handler == pad_handler::move) + if (pad && pad->m_pad_handler == pad_handler::move && !pad->is_copilot()) { if (!pad->move_data.calibration_requested || !pad->move_data.calibration_succeeded) { @@ -437,7 +437,7 @@ public: std::lock_guard pad_lock(pad::g_pad_mutex); const auto handler = pad::get_pad_thread(); const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num)); - if (pad && pad->m_pad_handler == pad_handler::move) + if (pad && pad->m_pad_handler == pad_handler::move && !pad->is_copilot()) { pad->move_data.calibration_requested = false; pad->move_data.calibration_succeeded = false; @@ -469,7 +469,7 @@ public: for (u32 i = 0; i < std::min(attribute.max_connect, CELL_GEM_MAX_NUM); i++) { const auto& pad = ::at32(handler->GetPads(), pad_num(i)); - if (pad && pad->m_pad_handler == pad_handler::move && (pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (pad && pad->m_pad_handler == pad_handler::move && pad->is_connected() && !pad->is_copilot()) { connected_controllers++; @@ -490,7 +490,7 @@ public: for (u32 i = 0; i < std::min(attribute.max_connect, CELL_GEM_MAX_NUM); i++) { const auto& pad = ::at32(handler->GetPads(), pad_num(i)); - if (pad && (pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (pad && pad->is_connected() && !pad->is_copilot()) { connected_controllers++; @@ -505,7 +505,7 @@ public: case move_handler::mouse: case move_handler::raw_mouse: { - auto& handler = g_fxo->get(); + auto& handler = *ensure(g_fxo->try_get()); std::lock_guard mouse_lock(handler.mutex); // Make sure that the mouse handler is initialized @@ -522,7 +522,7 @@ public: #ifdef HAVE_LIBEVDEV case move_handler::gun: { - gun_thread& gun = g_fxo->get(); + gun_thread& gun = *ensure(g_fxo->try_get()); std::scoped_lock lock(gun.handler.mutex); gun.num_devices = gun.handler.init() ? gun.handler.get_num_guns() : 0; connected_controllers = std::min(std::min(attribute.max_connect, CELL_GEM_MAX_NUM), gun.num_devices); @@ -1776,7 +1776,7 @@ static void ds3_input_to_pad(const u32 gem_num, be_t& digital_buttons, be_t const auto handler = pad::get_pad_thread(); const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num)); - if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (!pad->is_connected() || pad->is_copilot()) { return; } @@ -1864,7 +1864,7 @@ static void ds3_pos_to_gem_state(u32 gem_num, gem_config::gem_controller& contro const auto handler = pad::get_pad_thread(); const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num)); - if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (!pad->is_connected() || pad->is_copilot()) { return; } @@ -1895,7 +1895,7 @@ static void ps_move_pos_to_gem_state(u32 gem_num, gem_config::gem_controller& co const auto handler = pad::get_pad_thread(); const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num)); - if (pad->m_pad_handler != pad_handler::move || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (pad->m_pad_handler != pad_handler::move || !pad->is_connected() || pad->is_copilot()) { return; } @@ -1940,7 +1940,7 @@ static void ds3_input_to_ext(u32 gem_num, gem_config::gem_controller& controller const auto handler = pad::get_pad_thread(); const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num)); - if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (!pad->is_connected() || pad->is_copilot()) { return; } @@ -1952,7 +1952,7 @@ static void ds3_input_to_ext(u32 gem_num, gem_config::gem_controller& controller ext.status = controller.ext_status; - for (const AnalogStick& stick : pad->m_sticks) + for (const AnalogStick& stick : pad->m_sticks_external) { switch (stick.m_offset) { @@ -1964,7 +1964,7 @@ static void ds3_input_to_ext(u32 gem_num, gem_config::gem_controller& controller } } - for (const Button& button : pad->m_buttons) + for (const Button& button : pad->m_buttons_external) { if (!button.m_pressed) continue; @@ -2284,6 +2284,8 @@ error_code cellGemClearStatusFlags(u32 gem_num, u64 mask) error_code cellGemConvertVideoFinish(ppu_thread& ppu) { + ppu.state += cpu_flag::wait; + cellGem.warning("cellGemConvertVideoFinish()"); auto& gem = g_fxo->get(); @@ -2400,7 +2402,7 @@ error_code cellGemEnableMagnetometer(u32 gem_num, u32 enable) const auto handler = pad::get_pad_thread(); const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num)); - if (pad && pad->m_pad_handler == pad_handler::move) + if (pad && pad->m_pad_handler == pad_handler::move && !pad->is_copilot()) { pad->move_data.magnetometer_enabled = controller.enabled_magnetometer; } @@ -2448,7 +2450,7 @@ error_code cellGemEnableMagnetometer2(u32 gem_num, u32 enable) const auto handler = pad::get_pad_thread(); const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num)); - if (pad && pad->m_pad_handler == pad_handler::move) + if (pad && pad->m_pad_handler == pad_handler::move && !pad->is_copilot()) { pad->move_data.magnetometer_enabled = controller.enabled_magnetometer; } @@ -2777,7 +2779,7 @@ error_code cellGemGetInertialState(u32 gem_num, u32 state_flag, u64 timestamp, v const auto handler = pad::get_pad_thread(); const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num)); - if (pad && (pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (pad && pad->is_connected() && !pad->is_copilot()) { inertial_state->temperature = pad->move_data.temperature; inertial_state->accelerometer[0] = pad->move_data.accelerometer_x; @@ -3392,7 +3394,7 @@ error_code cellGemReadExternalPortDeviceInfo(u32 gem_num, vm::ptr ext_id, v const auto handler = pad::get_pad_thread(); const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num)); - if (pad->m_pad_handler != pad_handler::move || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (pad->m_pad_handler != pad_handler::move || !pad->is_connected() || pad->is_copilot()) { return CELL_GEM_NOT_CONNECTED; } @@ -3482,7 +3484,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; } } @@ -3706,7 +3708,7 @@ error_code cellGemWriteExternalPort(u32 gem_num, vm::ptrGetPads(), pad_num(gem_num)); - if (pad->m_pad_handler != pad_handler::move || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (pad->m_pad_handler != pad_handler::move || !pad->is_connected() || pad->is_copilot()) { return CELL_GEM_NOT_CONNECTED; } diff --git a/rpcs3/Emu/Cell/Modules/cellL10n.cpp b/rpcs3/Emu/Cell/Modules/cellL10n.cpp index 6b3ffc4448..0f47c682e6 100644 --- a/rpcs3/Emu/Cell/Modules/cellL10n.cpp +++ b/rpcs3/Emu/Cell/Modules/cellL10n.cpp @@ -2498,7 +2498,7 @@ s32 UCS2stoSBCSs(vm::cptr src, vm::ptr src_len, vm::ptr dst, vm::p for (u32 src_pos = 0; src_pos < *src_len; src_pos++) { - const s16 ucs2 = src[src_pos]; + const u16 ucs2 = src[src_pos]; if (ucs2 >= 0xfffe) { 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/Emu/Cell/Modules/cellMouse.cpp b/rpcs3/Emu/Cell/Modules/cellMouse.cpp index 8bf550a633..750b463c0c 100644 --- a/rpcs3/Emu/Cell/Modules/cellMouse.cpp +++ b/rpcs3/Emu/Cell/Modules/cellMouse.cpp @@ -1,8 +1,9 @@ #include "stdafx.h" #include "Emu/IdManager.h" +#include "Emu/system_config.h" #include "Emu/Cell/PPUModule.h" - #include "Emu/Io/MouseHandler.h" +#include "Emu/RSX/Overlays/overlay_debug_overlay.h" #include "cellMouse.h" @@ -13,6 +14,54 @@ extern bool is_input_allowed(); LOG_CHANNEL(cellMouse); +void show_debug_overlay(const CellMouseData& data, const MouseData* _mouse) +{ + // The cell mouse can be set empty without any new mouse input. + // Only update our mouse input if there is new data. + static MouseData mouse {}; + if (_mouse) + { + mouse = *_mouse; + } + + std::string text = fmt::format( + "> Name: Raw Value Pixel\n" + ">\n" + "> Update: %5d %5d\n" + "> Wheel: %5d %5d\n" + "> Tilt: %5d %5d\n" + "> X: %5d %5d %5d\n" + "> Y: %5d %5d %5d\n" + ">\n" + "> Buttons: 0x%04x 0x%04x\n" + "> Button 1: %5d %5d\n" + "> Button 2: %5d %5d\n" + "> Button 3: %5d %5d\n" + "> Button 4: %5d %5d\n" + "> Button 5: %5d %5d\n" + "> Button 6: %5d %5d\n" + "> Button 7: %5d %5d\n" + "> Button 8: %5d %5d\n" + , + mouse.update, data.update, + mouse.wheel, data.wheel, + mouse.tilt, data.tilt, + mouse.x_axis, data.x_axis, mouse.pixel_x, + mouse.y_axis, data.y_axis, mouse.pixel_y, + mouse.buttons, data.buttons, + !!(mouse.buttons & CELL_MOUSE_BUTTON_1), !!(data.buttons & CELL_MOUSE_BUTTON_1), + !!(mouse.buttons & CELL_MOUSE_BUTTON_2), !!(data.buttons & CELL_MOUSE_BUTTON_2), + !!(mouse.buttons & CELL_MOUSE_BUTTON_3), !!(data.buttons & CELL_MOUSE_BUTTON_3), + !!(mouse.buttons & CELL_MOUSE_BUTTON_4), !!(data.buttons & CELL_MOUSE_BUTTON_4), + !!(mouse.buttons & CELL_MOUSE_BUTTON_5), !!(data.buttons & CELL_MOUSE_BUTTON_5), + !!(mouse.buttons & CELL_MOUSE_BUTTON_6), !!(data.buttons & CELL_MOUSE_BUTTON_6), + !!(mouse.buttons & CELL_MOUSE_BUTTON_7), !!(data.buttons & CELL_MOUSE_BUTTON_7), + !!(mouse.buttons & CELL_MOUSE_BUTTON_8), !!(data.buttons & CELL_MOUSE_BUTTON_8) + ); + + rsx::overlays::set_debug_overlay_text(std::move(text)); +} + template<> void fmt_class_string::format(std::string& out, u64 arg) { @@ -214,10 +263,16 @@ error_code cellMouseGetData(u32 port_no, vm::ptr data) if (data_list.empty() || current_info.is_null_handler || (current_info.info & CELL_MOUSE_INFO_INTERCEPTED) || !is_input_allowed()) { data_list.clear(); + + if (port_no == 0 && g_cfg.io.mouse_debug_overlay && !g_cfg.io.pad_debug_overlay && !g_cfg.video.debug_overlay) + { + show_debug_overlay(*data, nullptr); + } + return CELL_OK; } - const MouseData current_data = data_list.front(); + const MouseData& current_data = data_list.front(); data->update = current_data.update; data->buttons = current_data.buttons; data->x_axis = current_data.x_axis; @@ -225,6 +280,11 @@ error_code cellMouseGetData(u32 port_no, vm::ptr data) data->wheel = current_data.wheel; data->tilt = current_data.tilt; + if (port_no == 0 && g_cfg.io.mouse_debug_overlay && !g_cfg.io.pad_debug_overlay && !g_cfg.video.debug_overlay) + { + show_debug_overlay(*data, ¤t_data); + } + data_list.pop_front(); return CELL_OK; @@ -264,20 +324,31 @@ error_code cellMouseGetDataList(u32 port_no, vm::ptr data) if (list.empty() || current_info.is_null_handler || (current_info.info & CELL_MOUSE_INFO_INTERCEPTED) || !is_input_allowed()) { list.clear(); + + if (port_no == 0 && g_cfg.io.mouse_debug_overlay && !g_cfg.io.pad_debug_overlay && !g_cfg.video.debug_overlay) + { + show_debug_overlay(data->list[0], nullptr); + } + return CELL_OK; } data->list_num = std::min(CELL_MOUSE_MAX_DATA_LIST_NUM, static_cast(list.size())); - int i = 0; - for (auto it = list.begin(); it != list.end() && i < CELL_MOUSE_MAX_DATA_LIST_NUM; ++it, ++i) + for (size_t i = 0; i < list.size() && i < CELL_MOUSE_MAX_DATA_LIST_NUM; ++i) { - data->list[i].update = it->update; - data->list[i].buttons = it->buttons; - data->list[i].x_axis = it->x_axis; - data->list[i].y_axis = it->y_axis; - data->list[i].wheel = it->wheel; - data->list[i].tilt = it->tilt; + const MouseData& current_data = list[i]; + data->list[i].update = current_data.update; + data->list[i].buttons = current_data.buttons; + data->list[i].x_axis = current_data.x_axis; + data->list[i].y_axis = current_data.y_axis; + data->list[i].wheel = current_data.wheel; + data->list[i].tilt = current_data.tilt; + + if (port_no == 0 && g_cfg.io.mouse_debug_overlay && !g_cfg.io.pad_debug_overlay && !g_cfg.video.debug_overlay) + { + show_debug_overlay(data->list[i], ¤t_data); + } } list.clear(); diff --git a/rpcs3/Emu/Cell/Modules/cellMsgDialog.cpp b/rpcs3/Emu/Cell/Modules/cellMsgDialog.cpp index 2b433e999c..875472a352 100644 --- a/rpcs3/Emu/Cell/Modules/cellMsgDialog.cpp +++ b/rpcs3/Emu/Cell/Modules/cellMsgDialog.cpp @@ -506,7 +506,7 @@ error_code cellMsgDialogOpenErrorCode(u32 errorCode, vm::ptr 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,33 +131,35 @@ 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_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], - pad.m_press_down, !!(d1 & CELL_PAD_CTRL_DOWN), data.button[CELL_PAD_BTN_OFFSET_PRESS_DOWN], - pad.m_press_left, !!(d1 & CELL_PAD_CTRL_LEFT), data.button[CELL_PAD_BTN_OFFSET_PRESS_LEFT], - pad.m_press_right, !!(d1 & CELL_PAD_CTRL_RIGHT), data.button[CELL_PAD_BTN_OFFSET_PRESS_RIGHT], - pad.m_press_cross, !!(d2 & CELL_PAD_CTRL_CROSS), data.button[CELL_PAD_BTN_OFFSET_PRESS_CROSS], - pad.m_press_square, !!(d2 & CELL_PAD_CTRL_SQUARE), data.button[CELL_PAD_BTN_OFFSET_PRESS_SQUARE], - pad.m_press_circle, !!(d2 & CELL_PAD_CTRL_CIRCLE), data.button[CELL_PAD_BTN_OFFSET_PRESS_CIRCLE], - pad.m_press_triangle, !!(d2 & CELL_PAD_CTRL_TRIANGLE), data.button[CELL_PAD_BTN_OFFSET_PRESS_TRIANGLE], - !!(pad.m_digital_1 & CELL_PAD_CTRL_START), !!(d1 & CELL_PAD_CTRL_START), - !!(pad.m_digital_1 & CELL_PAD_CTRL_SELECT), !!(d1 & CELL_PAD_CTRL_SELECT), - !!(pad.m_digital_1 & CELL_PAD_CTRL_PS), !!(d1 & CELL_PAD_CTRL_PS), - pad.m_press_L1, !!(d2 & CELL_PAD_CTRL_L1), data.button[CELL_PAD_BTN_OFFSET_PRESS_L1], - pad.m_press_L2, !!(d2 & CELL_PAD_CTRL_L2), data.button[CELL_PAD_BTN_OFFSET_PRESS_L2], - !!(pad.m_digital_1 & CELL_PAD_CTRL_L3), !!(d1 & CELL_PAD_CTRL_L3), - pad.m_press_R1, !!(d2 & CELL_PAD_CTRL_R1), data.button[CELL_PAD_BTN_OFFSET_PRESS_R1], - pad.m_press_R2, !!(d2 & CELL_PAD_CTRL_R2), data.button[CELL_PAD_BTN_OFFSET_PRESS_R2], - !!(pad.m_digital_1 & CELL_PAD_CTRL_R3), !!(d1 & CELL_PAD_CTRL_R3), - pad.m_analog_left_x, data.button[CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X], - pad.m_analog_left_y, data.button[CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y], - pad.m_analog_right_x, data.button[CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X], - pad.m_analog_right_y, data.button[CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y], - pad.m_sensor_x, data.button[CELL_PAD_BTN_OFFSET_SENSOR_X], - pad.m_sensor_y, data.button[CELL_PAD_BTN_OFFSET_SENSOR_Y], - pad.m_sensor_z, data.button[CELL_PAD_BTN_OFFSET_SENSOR_Z], - pad.m_sensor_g, data.button[CELL_PAD_BTN_OFFSET_SENSOR_G], + 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, + raw_d1, d1, + raw_d2, d2, + raw.button[CELL_PAD_BTN_OFFSET_PRESS_UP], !!(d1 & CELL_PAD_CTRL_UP), data.button[CELL_PAD_BTN_OFFSET_PRESS_UP], + raw.button[CELL_PAD_BTN_OFFSET_PRESS_DOWN], !!(d1 & CELL_PAD_CTRL_DOWN), data.button[CELL_PAD_BTN_OFFSET_PRESS_DOWN], + raw.button[CELL_PAD_BTN_OFFSET_PRESS_LEFT], !!(d1 & CELL_PAD_CTRL_LEFT), data.button[CELL_PAD_BTN_OFFSET_PRESS_LEFT], + raw.button[CELL_PAD_BTN_OFFSET_PRESS_RIGHT], !!(d1 & CELL_PAD_CTRL_RIGHT), data.button[CELL_PAD_BTN_OFFSET_PRESS_RIGHT], + raw.button[CELL_PAD_BTN_OFFSET_PRESS_CROSS], !!(d2 & CELL_PAD_CTRL_CROSS), data.button[CELL_PAD_BTN_OFFSET_PRESS_CROSS], + raw.button[CELL_PAD_BTN_OFFSET_PRESS_SQUARE], !!(d2 & CELL_PAD_CTRL_SQUARE), data.button[CELL_PAD_BTN_OFFSET_PRESS_SQUARE], + raw.button[CELL_PAD_BTN_OFFSET_PRESS_CIRCLE], !!(d2 & CELL_PAD_CTRL_CIRCLE), data.button[CELL_PAD_BTN_OFFSET_PRESS_CIRCLE], + raw.button[CELL_PAD_BTN_OFFSET_PRESS_TRIANGLE], !!(d2 & CELL_PAD_CTRL_TRIANGLE), data.button[CELL_PAD_BTN_OFFSET_PRESS_TRIANGLE], + !!(raw_d1 & CELL_PAD_CTRL_START), !!(d1 & CELL_PAD_CTRL_START), + !!(raw_d1 & CELL_PAD_CTRL_SELECT), !!(d1 & CELL_PAD_CTRL_SELECT), + !!(raw_d1 & CELL_PAD_CTRL_PS), !!(d1 & CELL_PAD_CTRL_PS), + raw.button[CELL_PAD_BTN_OFFSET_PRESS_L1], !!(d2 & CELL_PAD_CTRL_L1), data.button[CELL_PAD_BTN_OFFSET_PRESS_L1], + raw.button[CELL_PAD_BTN_OFFSET_PRESS_L2], !!(d2 & CELL_PAD_CTRL_L2), data.button[CELL_PAD_BTN_OFFSET_PRESS_L2], + !!(raw_d1 & CELL_PAD_CTRL_L3), !!(d1 & CELL_PAD_CTRL_L3), + raw.button[CELL_PAD_BTN_OFFSET_PRESS_R1], !!(d2 & CELL_PAD_CTRL_R1), data.button[CELL_PAD_BTN_OFFSET_PRESS_R1], + raw.button[CELL_PAD_BTN_OFFSET_PRESS_R2], !!(d2 & CELL_PAD_CTRL_R2), data.button[CELL_PAD_BTN_OFFSET_PRESS_R2], + !!(raw_d1 & CELL_PAD_CTRL_R3), !!(d1 & CELL_PAD_CTRL_R3), + raw.button[CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X], data.button[CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X], + raw.button[CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y], data.button[CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y], + raw.button[CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X], data.button[CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X], + raw.button[CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y], data.button[CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y], + raw.button[CELL_PAD_BTN_OFFSET_SENSOR_X], data.button[CELL_PAD_BTN_OFFSET_SENSOR_X], + raw.button[CELL_PAD_BTN_OFFSET_SENSOR_Y], data.button[CELL_PAD_BTN_OFFSET_SENSOR_Y], + raw.button[CELL_PAD_BTN_OFFSET_SENSOR_Z], data.button[CELL_PAD_BTN_OFFSET_SENSOR_Z], + raw.button[CELL_PAD_BTN_OFFSET_SENSOR_G], data.button[CELL_PAD_BTN_OFFSET_SENSOR_G], pad.m_product_id, pad.m_vendor_id, pad.m_device_type, @@ -272,7 +279,7 @@ error_code cellPadInit(ppu_thread& ppu, u32 max_connect) for (usz i = 0; i < config.get_max_connect(); ++i) { - if (!pads[i]->is_fake_pad && (pads[i]->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (!pads[i]->is_fake_pad && pads[i]->is_connected()) { send_sys_io_connect_event(i, CELL_PAD_STATUS_CONNECTED); } @@ -305,17 +312,15 @@ void clear_pad_buffer(const std::shared_ptr& pad) // might as well also reset everything in our pad 'buffer' to nothing as well pad->m_buffer_cleared = true; - pad->m_analog_left_x = pad->m_analog_left_y = pad->m_analog_right_x = pad->m_analog_right_y = 128; - - pad->m_digital_1 = pad->m_digital_2 = 0; - pad->m_press_right = pad->m_press_left = pad->m_press_up = pad->m_press_down = 0; - pad->m_press_triangle = pad->m_press_circle = pad->m_press_cross = pad->m_press_square = 0; - pad->m_press_L1 = pad->m_press_L2 = pad->m_press_R1 = pad->m_press_R2 = 0; - - pad->m_sensor_x = DEFAULT_MOTION_X; - pad->m_sensor_y = DEFAULT_MOTION_Y; - pad->m_sensor_z = DEFAULT_MOTION_Z; - pad->m_sensor_g = DEFAULT_MOTION_G; + pad->data = {}; + pad->data.button[CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X] = 128; + pad->data.button[CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y] = 128; + pad->data.button[CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X] = 128; + pad->data.button[CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y] = 128; + pad->data.button[CELL_PAD_BTN_OFFSET_SENSOR_X] = DEFAULT_MOTION_X; + pad->data.button[CELL_PAD_BTN_OFFSET_SENSOR_Y] = DEFAULT_MOTION_Y; + pad->data.button[CELL_PAD_BTN_OFFSET_SENSOR_Z] = DEFAULT_MOTION_Z; + pad->data.button[CELL_PAD_BTN_OFFSET_SENSOR_G] = DEFAULT_MOTION_G; } error_code cellPadClearBuf(u32 port_no) @@ -339,7 +344,7 @@ error_code cellPadClearBuf(u32 port_no) const auto& pads = handler->GetPads(); const auto& pad = pads[port_no]; - if (pad->is_fake_pad || !config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (pad->is_fake_pad || !config.is_reportedly_connected(port_no) || !pad->is_connected()) return not_an_error(CELL_PAD_ERROR_NO_DEVICE); clear_pad_buffer(pad); @@ -363,6 +368,8 @@ void pad_get_data(u32 port_no, CellPadData* data, bool get_periph_data = false) const u32 setting = config.port_setting[port_no]; bool btnChanged = false; + CellPadData& output = pad->data; + if (rinfo.ignore_input || !is_input_allowed()) { // Needed for Hotline Miami and Ninja Gaiden Sigma after dialogs were closed and buttons are still pressed. @@ -380,14 +387,14 @@ void pad_get_data(u32 port_no, CellPadData* data, bool get_periph_data = false) } else { - const u16 d1Initial = pad->m_digital_1; - const u16 d2Initial = pad->m_digital_2; + const u16 d1_initial = output.button[CELL_PAD_BTN_OFFSET_DIGITAL1]; + const u16 d2_initial = output.button[CELL_PAD_BTN_OFFSET_DIGITAL2]; // Check if this pad is configured as a skateboard which ignores sticks and pressure button values. // Curiously it maps infrared on the press value of the face buttons for some reason. const bool use_piggyback = pad->m_class_type == CELL_PAD_PCLASS_TYPE_SKATEBOARD; - const auto set_value = [&btnChanged, use_piggyback, &pad](u16& value, u16 new_value, bool force_processing = false, u16 old_max_value = 255, u16 new_max_value = 255) + const auto set_value = [&btnChanged, use_piggyback, &pad](auto& value, u16 new_value, bool force_processing = false, u16 old_max_value = 255, u16 new_max_value = 255) { if (use_piggyback) { @@ -411,7 +418,7 @@ void pad_get_data(u32 port_no, CellPadData* data, bool get_periph_data = false) } }; - for (Button& button : pad->m_buttons) + for (const Button& button : pad->m_buttons_external) { // here we check btns, and set pad accordingly, // if something changed, set btnChanged @@ -421,17 +428,17 @@ void pad_get_data(u32 port_no, CellPadData* data, bool get_periph_data = false) case CELL_PAD_BTN_OFFSET_DIGITAL1: { if (button.m_pressed) - pad->m_digital_1 |= button.m_outKeyCode; + output.button[CELL_PAD_BTN_OFFSET_DIGITAL1] |= button.m_outKeyCode; else - pad->m_digital_1 &= ~button.m_outKeyCode; + output.button[CELL_PAD_BTN_OFFSET_DIGITAL1] &= ~button.m_outKeyCode; switch (button.m_outKeyCode) { - case CELL_PAD_CTRL_LEFT: set_value(pad->m_press_left, button.m_value); break; - case CELL_PAD_CTRL_DOWN: set_value(pad->m_press_down, button.m_value); break; - case CELL_PAD_CTRL_RIGHT: set_value(pad->m_press_right, button.m_value); break; - case CELL_PAD_CTRL_UP: set_value(pad->m_press_up, button.m_value); break; - // These arent pressure btns + case CELL_PAD_CTRL_LEFT: set_value(output.button[CELL_PAD_BTN_OFFSET_PRESS_LEFT], button.m_value); break; + case CELL_PAD_CTRL_DOWN: set_value(output.button[CELL_PAD_BTN_OFFSET_PRESS_DOWN], button.m_value); break; + case CELL_PAD_CTRL_RIGHT: set_value(output.button[CELL_PAD_BTN_OFFSET_PRESS_RIGHT], button.m_value); break; + case CELL_PAD_CTRL_UP: set_value(output.button[CELL_PAD_BTN_OFFSET_PRESS_UP], button.m_value); break; + // These aren't pressure btns case CELL_PAD_CTRL_R3: case CELL_PAD_CTRL_L3: case CELL_PAD_CTRL_START: @@ -443,20 +450,20 @@ void pad_get_data(u32 port_no, CellPadData* data, bool get_periph_data = false) case CELL_PAD_BTN_OFFSET_DIGITAL2: { if (button.m_pressed) - pad->m_digital_2 |= button.m_outKeyCode; + output.button[CELL_PAD_BTN_OFFSET_DIGITAL2] |= button.m_outKeyCode; else - pad->m_digital_2 &= ~button.m_outKeyCode; + output.button[CELL_PAD_BTN_OFFSET_DIGITAL2] &= ~button.m_outKeyCode; switch (button.m_outKeyCode) { - case CELL_PAD_CTRL_SQUARE: set_value(pad->m_press_square, button.m_value); break; - case CELL_PAD_CTRL_CROSS: set_value(pad->m_press_cross, button.m_value); break; - case CELL_PAD_CTRL_CIRCLE: set_value(pad->m_press_circle, button.m_value); break; - case CELL_PAD_CTRL_TRIANGLE: set_value(pad->m_press_triangle, button.m_value); break; - case CELL_PAD_CTRL_R1: set_value(pad->m_press_R1, button.m_value); break; - case CELL_PAD_CTRL_L1: set_value(pad->m_press_L1, button.m_value); break; - case CELL_PAD_CTRL_R2: set_value(pad->m_press_R2, button.m_value); break; - case CELL_PAD_CTRL_L2: set_value(pad->m_press_L2, button.m_value); break; + case CELL_PAD_CTRL_SQUARE: set_value(output.button[CELL_PAD_BTN_OFFSET_PRESS_SQUARE], button.m_value); break; + case CELL_PAD_CTRL_CROSS: set_value(output.button[CELL_PAD_BTN_OFFSET_PRESS_CROSS], button.m_value); break; + case CELL_PAD_CTRL_CIRCLE: set_value(output.button[CELL_PAD_BTN_OFFSET_PRESS_CIRCLE], button.m_value); break; + case CELL_PAD_CTRL_TRIANGLE: set_value(output.button[CELL_PAD_BTN_OFFSET_PRESS_TRIANGLE], button.m_value); break; + case CELL_PAD_CTRL_R1: set_value(output.button[CELL_PAD_BTN_OFFSET_PRESS_R1], button.m_value); break; + case CELL_PAD_CTRL_L1: set_value(output.button[CELL_PAD_BTN_OFFSET_PRESS_L1], button.m_value); break; + case CELL_PAD_CTRL_R2: set_value(output.button[CELL_PAD_BTN_OFFSET_PRESS_R2], button.m_value); break; + case CELL_PAD_CTRL_L2: set_value(output.button[CELL_PAD_BTN_OFFSET_PRESS_L2], button.m_value); break; default: break; } break; @@ -465,18 +472,22 @@ void pad_get_data(u32 port_no, CellPadData* data, bool get_periph_data = false) { switch (button.m_outKeyCode) { - case CELL_PAD_CTRL_PRESS_RIGHT: set_value(pad->m_press_right, button.m_value, true); break; - case CELL_PAD_CTRL_PRESS_LEFT: set_value(pad->m_press_left, button.m_value, true); break; - case CELL_PAD_CTRL_PRESS_UP: set_value(pad->m_press_up, button.m_value, true); break; - case CELL_PAD_CTRL_PRESS_DOWN: set_value(pad->m_press_down, button.m_value, true); break; - case CELL_PAD_CTRL_PRESS_TRIANGLE: set_value(pad->m_press_triangle, button.m_value, true, 255, 63); break; // Infrared on RIDE Skateboard - case CELL_PAD_CTRL_PRESS_CIRCLE: set_value(pad->m_press_circle, button.m_value, true, 255, 63); break; // Infrared on RIDE Skateboard - case CELL_PAD_CTRL_PRESS_CROSS: set_value(pad->m_press_cross, button.m_value, true, 255, 63); break; // Infrared on RIDE Skateboard - case CELL_PAD_CTRL_PRESS_SQUARE: set_value(pad->m_press_square, button.m_value, true, 255, 63); break; // Infrared on RIDE Skateboard - case CELL_PAD_CTRL_PRESS_L1: set_value(pad->m_press_L1, button.m_value, true); break; - case CELL_PAD_CTRL_PRESS_R1: set_value(pad->m_press_R1, button.m_value, true); break; - case CELL_PAD_CTRL_PRESS_L2: set_value(pad->m_press_L2, button.m_value, true); break; - case CELL_PAD_CTRL_PRESS_R2: set_value(pad->m_press_R2, button.m_value, true); break; + case CELL_PAD_CTRL_PRESS_RIGHT: + case CELL_PAD_CTRL_PRESS_LEFT: + case CELL_PAD_CTRL_PRESS_UP: + case CELL_PAD_CTRL_PRESS_DOWN: + case CELL_PAD_CTRL_PRESS_L1: + case CELL_PAD_CTRL_PRESS_R1: + case CELL_PAD_CTRL_PRESS_L2: + case CELL_PAD_CTRL_PRESS_R2: + set_value(output.button[button.m_outKeyCode], button.m_value, true); + break; + case CELL_PAD_CTRL_PRESS_TRIANGLE: + case CELL_PAD_CTRL_PRESS_CIRCLE: + case CELL_PAD_CTRL_PRESS_CROSS: + case CELL_PAD_CTRL_PRESS_SQUARE: + set_value(output.button[button.m_outKeyCode], button.m_value, true, 255, 63); // Infrared on RIDE Skateboard + break; default: break; } break; @@ -486,15 +497,18 @@ void pad_get_data(u32 port_no, CellPadData* data, bool get_periph_data = false) } } - for (const AnalogStick& stick : pad->m_sticks) + for (const AnalogStick& stick : pad->m_sticks_external) { switch (stick.m_offset) { - case CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X: set_value(pad->m_analog_left_x, stick.m_value); break; - case CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y: set_value(pad->m_analog_left_y, stick.m_value); break; - case CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X: set_value(pad->m_analog_right_x, stick.m_value); break; - case CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y: set_value(pad->m_analog_right_y, stick.m_value); break; - default: break; + case CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X: + case CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y: + case CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X: + case CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y: + set_value(output.button[stick.m_offset], stick.m_value); + break; + default: + break; } } @@ -504,16 +518,19 @@ void pad_get_data(u32 port_no, CellPadData* data, bool get_periph_data = false) { switch (sensor.m_offset) { - case CELL_PAD_BTN_OFFSET_SENSOR_X: set_value(pad->m_sensor_x, sensor.m_value, true); break; - case CELL_PAD_BTN_OFFSET_SENSOR_Y: set_value(pad->m_sensor_y, sensor.m_value, true); break; - case CELL_PAD_BTN_OFFSET_SENSOR_Z: set_value(pad->m_sensor_z, sensor.m_value, true); break; - case CELL_PAD_BTN_OFFSET_SENSOR_G: set_value(pad->m_sensor_g, sensor.m_value, true); break; - default: break; + case CELL_PAD_BTN_OFFSET_SENSOR_X: + case CELL_PAD_BTN_OFFSET_SENSOR_Y: + case CELL_PAD_BTN_OFFSET_SENSOR_Z: + case CELL_PAD_BTN_OFFSET_SENSOR_G: + set_value(output.button[sensor.m_offset], sensor.m_value, true); + break; + default: + break; } } } - if (d1Initial != pad->m_digital_1 || d2Initial != pad->m_digital_2) + if (d1_initial != output.button[CELL_PAD_BTN_OFFSET_DIGITAL1] || d2_initial != output.button[CELL_PAD_BTN_OFFSET_DIGITAL2]) { btnChanged = true; } @@ -556,41 +573,25 @@ void pad_get_data(u32 port_no, CellPadData* data, bool get_periph_data = false) // bits 15-8 reserved, 7-4 = 0x7, 3-0: data->len/2; data->button[1] = (0x7 << 4) | std::min(data->len / 2, 15); - data->button[CELL_PAD_BTN_OFFSET_DIGITAL1] = pad->m_digital_1; - data->button[CELL_PAD_BTN_OFFSET_DIGITAL2] = pad->m_digital_2; - data->button[CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X] = pad->m_analog_right_x; - data->button[CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y] = pad->m_analog_right_y; - data->button[CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X] = pad->m_analog_left_x; - data->button[CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y] = pad->m_analog_left_y; - if (setting & CELL_PAD_SETTING_PRESS_ON) { - data->button[CELL_PAD_BTN_OFFSET_PRESS_RIGHT] = pad->m_press_right; - data->button[CELL_PAD_BTN_OFFSET_PRESS_LEFT] = pad->m_press_left; - data->button[CELL_PAD_BTN_OFFSET_PRESS_UP] = pad->m_press_up; - data->button[CELL_PAD_BTN_OFFSET_PRESS_DOWN] = pad->m_press_down; - data->button[CELL_PAD_BTN_OFFSET_PRESS_TRIANGLE] = pad->m_press_triangle; - data->button[CELL_PAD_BTN_OFFSET_PRESS_CIRCLE] = pad->m_press_circle; - data->button[CELL_PAD_BTN_OFFSET_PRESS_CROSS] = pad->m_press_cross; - data->button[CELL_PAD_BTN_OFFSET_PRESS_SQUARE] = pad->m_press_square; - data->button[CELL_PAD_BTN_OFFSET_PRESS_L1] = pad->m_press_L1; - data->button[CELL_PAD_BTN_OFFSET_PRESS_R1] = pad->m_press_R1; - data->button[CELL_PAD_BTN_OFFSET_PRESS_L2] = pad->m_press_L2; - data->button[CELL_PAD_BTN_OFFSET_PRESS_R2] = pad->m_press_R2; + constexpr u32 copy_size = (static_cast(CELL_PAD_LEN_CHANGE_PRESS_ON) - static_cast(CELL_PAD_BTN_OFFSET_DIGITAL1)) * sizeof(u16); + std::memcpy(&data->button[CELL_PAD_BTN_OFFSET_DIGITAL1], &output.button[CELL_PAD_BTN_OFFSET_DIGITAL1], copy_size); } else { + constexpr u32 copy_size = (static_cast(CELL_PAD_LEN_CHANGE_DEFAULT) - static_cast(CELL_PAD_BTN_OFFSET_DIGITAL1)) * sizeof(u16); + std::memcpy(&data->button[CELL_PAD_BTN_OFFSET_DIGITAL1], &output.button[CELL_PAD_BTN_OFFSET_DIGITAL1], copy_size); + // Clear area if setting is not used - constexpr u32 area_lengh = (CELL_PAD_LEN_CHANGE_PRESS_ON - CELL_PAD_LEN_CHANGE_DEFAULT) * sizeof(u16); - std::memset(&data->button[CELL_PAD_LEN_CHANGE_DEFAULT], 0, area_lengh); + constexpr u32 area_size = (CELL_PAD_LEN_CHANGE_PRESS_ON - CELL_PAD_LEN_CHANGE_DEFAULT) * sizeof(u16); + std::memset(&data->button[CELL_PAD_LEN_CHANGE_DEFAULT], 0, area_size); } if (data->len == CELL_PAD_LEN_CHANGE_SENSOR_ON) { - data->button[CELL_PAD_BTN_OFFSET_SENSOR_X] = pad->m_sensor_x; - data->button[CELL_PAD_BTN_OFFSET_SENSOR_Y] = pad->m_sensor_y; - data->button[CELL_PAD_BTN_OFFSET_SENSOR_Z] = pad->m_sensor_z; - data->button[CELL_PAD_BTN_OFFSET_SENSOR_G] = pad->m_sensor_g; + constexpr u32 copy_size = (static_cast(CELL_PAD_LEN_CHANGE_SENSOR_ON) - static_cast(CELL_PAD_BTN_OFFSET_SENSOR_X)) * sizeof(u16); + std::memcpy(&data->button[CELL_PAD_BTN_OFFSET_SENSOR_X], &output.button[CELL_PAD_BTN_OFFSET_SENSOR_X], copy_size); } } @@ -712,12 +713,12 @@ error_code cellPadGetData(u32 port_no, vm::ptr data) const auto& pads = handler->GetPads(); const auto& pad = pads[port_no]; - if (pad->is_fake_pad || !config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (pad->is_fake_pad || !config.is_reportedly_connected(port_no) || !pad->is_connected()) return not_an_error(CELL_PAD_ERROR_NO_DEVICE); pad_get_data(port_no, data.get_ptr()); - if (g_cfg.io.debug_overlay && !g_cfg.video.debug_overlay && port_no == 0) + if (g_cfg.io.pad_debug_overlay && !g_cfg.video.debug_overlay && port_no == 0) { show_debug_overlay(*data, *pad, config); } @@ -798,7 +799,7 @@ error_code cellPadPeriphGetData(u32 port_no, vm::ptr data) const auto& pads = handler->GetPads(); const auto& pad = pads[port_no]; - if (pad->is_fake_pad || !config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (pad->is_fake_pad || !config.is_reportedly_connected(port_no) || !pad->is_connected()) return not_an_error(CELL_PAD_ERROR_NO_DEVICE); pad_get_data(port_no, &data->cellpad_data, true); @@ -830,10 +831,11 @@ error_code cellPadGetRawData(u32 port_no, vm::ptr data) const auto& pads = handler->GetPads(); const auto& pad = pads[port_no]; - if (pad->is_fake_pad || !config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (pad->is_fake_pad || !config.is_reportedly_connected(port_no) || !pad->is_connected()) return not_an_error(CELL_PAD_ERROR_NO_DEVICE); // ? + // std::memcpy(data.get_ptr(), &pad->data, sizeof(CellPadData)); return CELL_OK; } @@ -843,7 +845,7 @@ error_code cellPadGetDataExtra(u32 port_no, vm::ptr device_type, vm::ptr param) const auto& pads = handler->GetPads(); const auto& pad = pads[port_no]; - if (pad->is_fake_pad || !config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (pad->is_fake_pad || !config.is_reportedly_connected(port_no) || !pad->is_connected()) return not_an_error(CELL_PAD_ERROR_NO_DEVICE); // TODO: find out if this is checked here or later or at all 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; } @@ -1021,7 +1023,7 @@ error_code cellPadGetCapabilityInfo(u32 port_no, vm::ptr const auto& pads = handler->GetPads(); const auto& pad = pads[port_no]; - if (pad->is_fake_pad || !config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (pad->is_fake_pad || !config.is_reportedly_connected(port_no) || !pad->is_connected()) return not_an_error(CELL_PAD_ERROR_NO_DEVICE); // Should return the same as device capability mask, psl1ght has it backwards in pad->h @@ -1077,7 +1079,7 @@ error_code cellPadInfoPressMode(u32 port_no) const auto& pads = handler->GetPads(); const auto& pad = pads[port_no]; - if (pad->is_fake_pad || !config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (pad->is_fake_pad || !config.is_reportedly_connected(port_no) || !pad->is_connected()) return not_an_error(CELL_PAD_ERROR_NO_DEVICE); return not_an_error((pad->m_device_capability & CELL_PAD_CAPABILITY_PRESS_MODE) ? 1 : 0); @@ -1104,7 +1106,7 @@ error_code cellPadInfoSensorMode(u32 port_no) const auto& pads = handler->GetPads(); const auto& pad = pads[port_no]; - if (pad->is_fake_pad || !config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (pad->is_fake_pad || !config.is_reportedly_connected(port_no) || !pad->is_connected()) return not_an_error(CELL_PAD_ERROR_NO_DEVICE); return not_an_error((pad->m_device_capability & CELL_PAD_CAPABILITY_SENSOR_MODE) ? 1 : 0); @@ -1240,7 +1242,7 @@ error_code cellPadLddGetPortNo(s32 handle) return CELL_PAD_ERROR_UNINITIALIZED; const auto handler = pad::get_pad_thread(); - auto& pads = handler->GetPads(); + const auto& pads = handler->GetPads(); if (handle < 0 || static_cast(handle) >= pads.size()) return CELL_PAD_ERROR_INVALID_PARAMETER; diff --git a/rpcs3/Emu/Cell/Modules/cellPad.h b/rpcs3/Emu/Cell/Modules/cellPad.h index 0fdd4b23a7..0d199e488c 100644 --- a/rpcs3/Emu/Cell/Modules/cellPad.h +++ b/rpcs3/Emu/Cell/Modules/cellPad.h @@ -3,7 +3,6 @@ #include "Emu/Io/pad_types.h" #include -#include "util/types.hpp" enum CellPadError : u32 { @@ -42,13 +41,13 @@ enum struct pad_data_internal { - u16 vendor_id; - u16 product_id; - u32 port_status; - u32 device_capability; - u32 device_type; - u32 pclass_type; - u32 pclass_profile; + u16 vendor_id = 0; + u16 product_id = 0; + u32 port_status = 0; + u32 device_capability = 0; + u32 device_type = 0; + u32 pclass_type = 0; + u32 pclass_profile = 0; ENABLE_BITWISE_SERIALIZATION; }; diff --git a/rpcs3/Emu/Cell/Modules/cellSaveData.cpp b/rpcs3/Emu/Cell/Modules/cellSaveData.cpp index eec2652f21..bb99a1f6d6 100644 --- a/rpcs3/Emu/Cell/Modules/cellSaveData.cpp +++ b/rpcs3/Emu/Cell/Modules/cellSaveData.cpp @@ -396,7 +396,7 @@ static error_code display_callback_result_error_message(ppu_thread& ppu, const C switch (result.result) { case CELL_SAVEDATA_CBRESULT_ERR_NOSPACE: - msg = get_localized_string(localized_string_id::CELL_SAVEDATA_CB_NO_SPACE, fmt::format("%d", result.errNeedSizeKB).c_str()); + msg = get_localized_string(localized_string_id::CELL_SAVEDATA_CB_NO_SPACE, "%d", result.errNeedSizeKB); break; case CELL_SAVEDATA_CBRESULT_ERR_FAILURE: msg = get_localized_string(localized_string_id::CELL_SAVEDATA_CB_FAILURE); @@ -700,11 +700,32 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v u32 errDialog, PSetList setList, PSetBuf setBuf, PFuncList funcList, PFuncFixed funcFixed, PFuncStat funcStat, PFuncFile funcFile, u32 container, u32 unk_op_flags /*TODO*/, vm::ptr userdata, u32 userId, PFuncDone funcDone) { - if (const auto& [ok, list] = setList.try_read(); ok) - cellSaveData.notice("savedata_op(): setList = { .sortType=%d, .sortOrder=%d, .dirNamePrefix='%s' }", list.sortType, list.sortOrder, list.dirNamePrefix); + if (setList) + { + if (const auto& [ok, list] = setList.try_read(); ok) + { + cellSaveData.notice("savedata_op(): setList = { .sortType=%d, .sortOrder=%d, .dirNamePrefix='%s' }", list.sortType, list.sortOrder, list.dirNamePrefix); + } + else + { + cellSaveData.error("savedata_op(): Failed to read setList!"); + } + } - if (const auto& [ok, buf] = setBuf.try_read(); ok) - cellSaveData.notice("savedata_op(): setBuf = { .dirListMax=%d, .fileListMax=%d, .bufSize=%d }", buf.dirListMax, buf.fileListMax, buf.bufSize); + if (setBuf) + { + if (const auto& [ok, buf] = setBuf.try_read(); ok) + { + cellSaveData.notice("savedata_op(): setBuf = { .dirListMax=%d, .fileListMax=%d, .bufSize=%d }", buf.dirListMax, buf.fileListMax, buf.bufSize); + } + else + { + cellSaveData.error("savedata_op(): Failed to read setBuf!"); + } + } + + // There is a lot going on in this function, ensure function log and past log commands have completed for ease of debugging + logs::listener::sync_all(); if (const auto ecode = savedata_check_args(operation, version, dirName, errDialog, setList, setBuf, funcList, funcFixed, funcStat, funcFile, container, unk_op_flags, userdata, userId, funcDone)) @@ -858,25 +879,34 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v const u32 order = setList->sortOrder; const u32 type = setList->sortType; - std::sort(save_entries.begin(), save_entries.end(), [=](const SaveDataEntry& entry1, const SaveDataEntry& entry2) + std::sort(save_entries.begin(), save_entries.end(), [order, type](const SaveDataEntry& entry1, const SaveDataEntry& entry2) -> bool { - if (order == CELL_SAVEDATA_SORTORDER_DESCENT && type == CELL_SAVEDATA_SORTTYPE_MODIFIEDTIME) + const bool mtime_lower = entry1.mtime < entry2.mtime; + const bool mtime_equal = entry1.mtime == entry2.mtime; + const bool subtitle_lower = entry1.subtitle < entry2.subtitle; + const bool subtitle_equal = entry1.subtitle == entry2.subtitle; + const bool revert_order = order == CELL_SAVEDATA_SORTORDER_DESCENT; + + if (type == CELL_SAVEDATA_SORTTYPE_MODIFIEDTIME) { - return entry1.mtime >= entry2.mtime; + if (mtime_equal) + { + return subtitle_lower != revert_order; + } + + return mtime_lower != revert_order; } - if (order == CELL_SAVEDATA_SORTORDER_DESCENT && type == CELL_SAVEDATA_SORTTYPE_SUBTITLE) + else if (type == CELL_SAVEDATA_SORTTYPE_SUBTITLE) { - return entry1.subtitle >= entry2.subtitle; - } - if (order == CELL_SAVEDATA_SORTORDER_ASCENT && type == CELL_SAVEDATA_SORTTYPE_MODIFIEDTIME) - { - return entry1.mtime < entry2.mtime; - } - if (order == CELL_SAVEDATA_SORTORDER_ASCENT && type == CELL_SAVEDATA_SORTTYPE_SUBTITLE) - { - return entry1.subtitle < entry2.subtitle; + if (subtitle_equal) + { + return mtime_lower != revert_order; + } + + return subtitle_lower != revert_order; } + ensure(false); return true; }); } diff --git a/rpcs3/Emu/Cell/Modules/cellSpurs.cpp b/rpcs3/Emu/Cell/Modules/cellSpurs.cpp index c09c808e92..c5defbd048 100644 --- a/rpcs3/Emu/Cell/Modules/cellSpurs.cpp +++ b/rpcs3/Emu/Cell/Modules/cellSpurs.cpp @@ -1230,7 +1230,7 @@ s32 _spurs::initialize(ppu_thread& ppu, vm::ptr spurs, u32 revision, if (flags & SAF_UNKNOWN_FLAG_9) spuTgAttr->type |= 0x0800; if (flags & SAF_SYSTEM_WORKLOAD_ENABLED) spuTgAttr->type |= SYS_SPU_THREAD_GROUP_TYPE_COOPERATE_WITH_SYSTEM; - if (s32 rc = sys_spu_thread_group_create(ppu, spurs.ptr(&CellSpurs::spuTG), nSpus, spuPriority, spuTgAttr)) + if (s32 rc = sys_spu_thread_group_create(ppu, spurs.ptr(&CellSpurs::spuTG), nSpus, spuPriority, vm::unsafe_ptr_cast(spuTgAttr))) { ppu_execute<&sys_spu_image_close>(ppu, spurs.ptr(&CellSpurs::spuImg)); return rollback(), rc; 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/Cell/Modules/cellVdec.cpp b/rpcs3/Emu/Cell/Modules/cellVdec.cpp index 4574d99d59..84bb74ad28 100644 --- a/rpcs3/Emu/Cell/Modules/cellVdec.cpp +++ b/rpcs3/Emu/Cell/Modules/cellVdec.cpp @@ -1220,7 +1220,7 @@ error_code cellVdecGetPictureExt(ppu_thread& ppu, u32 handle, vm::cptrunk0 || format->unk1) { - fmt::throw_exception("cellVdecGetPictureExt: Unknown arguments (arg4=*0x%x, unk0=0x%x, unk1=0x%x)", arg4, format->unk0, format->unk1); + cellVdec.todo("cellVdecGetPictureExt: Unknown arguments (arg4=*0x%x, unk0=0x%x, unk1=0x%x)", arg4, format->unk0, format->unk1); } vdec_frame frame; diff --git a/rpcs3/Emu/Cell/Modules/cellVideoOut.cpp b/rpcs3/Emu/Cell/Modules/cellVideoOut.cpp index e0db365933..a51ffbf756 100644 --- a/rpcs3/Emu/Cell/Modules/cellVideoOut.cpp +++ b/rpcs3/Emu/Cell/Modules/cellVideoOut.cpp @@ -202,7 +202,7 @@ error_code cellVideoOutConfigure(u32 videoOut, vm::ptrresolutionId, &res) != CELL_OK || - (config->resolutionId >= CELL_VIDEO_OUT_RESOLUTION_720_3D_FRAME_PACKING && g_cfg.video.stereo_render_mode == stereo_render_mode_options::disabled)) + (config->resolutionId >= CELL_VIDEO_OUT_RESOLUTION_720_3D_FRAME_PACKING && !g_cfg.video.stereo_enabled)) { // Resolution not supported cellSysutil.error("Unusual resolution requested: 0x%x", config->resolutionId); @@ -211,7 +211,7 @@ error_code cellVideoOutConfigure(u32 videoOut, vm::ptrget(); conf.resolution_id = config->resolutionId; - conf.stereo_mode = (config->resolutionId >= CELL_VIDEO_OUT_RESOLUTION_720_3D_FRAME_PACKING) ? g_cfg.video.stereo_render_mode.get() : stereo_render_mode_options::disabled; + conf.stereo_enabled = (config->resolutionId >= CELL_VIDEO_OUT_RESOLUTION_720_3D_FRAME_PACKING) && g_cfg.video.stereo_enabled; conf.aspect = config->aspect; conf.format = config->format; conf.scanline_pitch = config->pitch; @@ -393,7 +393,7 @@ error_code cellVideoOutGetDeviceInfo(u32 videoOut, u32 deviceIndex, vm::ptr communicationId, vm::cptr selfNpId) { - sceNp.warning("sceNpLookupCreateTitleCtx(communicationId=*0x%x(%s), selfNpId=0x%x)", communicationId, communicationId ? communicationId->data : "", selfNpId); + sceNp.warning("sceNpLookupCreateTitleCtx(communicationId=*0x%x(%s), selfNpId=0x%x)", communicationId, communicationId ? std::string_view(communicationId->data, 9) : "", selfNpId); auto& nph = g_fxo->get>(); @@ -3930,8 +3930,8 @@ error_code sceNpManagerGetMyLanguages(vm::ptr myLanguages) return SCE_NP_ERROR_INVALID_STATE; } - myLanguages->language1 = SCE_NP_LANG_ENGLISH_US; - myLanguages->language2 = g_cfg.sys.language != CELL_SYSUTIL_LANG_ENGLISH_US ? g_cfg.sys.language : -1; + myLanguages->language1 = g_cfg.sys.language; + myLanguages->language2 = g_cfg.sys.language != CELL_SYSUTIL_LANG_ENGLISH_US ? CELL_SYSUTIL_LANG_ENGLISH_US : -1; myLanguages->language3 = -1; return CELL_OK; @@ -3968,7 +3968,7 @@ error_code sceNpManagerGetAccountRegion(vm::ptr countryCode, v ensure(ccode.size() == sizeof(SceNpCountryCode::data)); std::memcpy(countryCode->data, ccode.data(), sizeof(SceNpCountryCode::data)); - *language = CELL_SYSUTIL_LANG_ENGLISH_US; + *language = g_cfg.sys.language; return CELL_OK; } @@ -4265,7 +4265,7 @@ error_code sceNpManagerGetEntitlementById(vm::cptr entId, vm::ptr signInId) @@ -6962,7 +6962,7 @@ error_code sceNpSignalingGetConnectionFromPeerAddress(u32 ctx_id, np_in_addr_t p return CELL_OK; } -error_code sceNpSignalingGetLocalNetInfo(u32 ctx_id, vm::ptr info) +error_code sceNpSignalingGetLocalNetInfo(u32 ctx_id, vm::ptr info) { sceNp.warning("sceNpSignalingGetLocalNetInfo(ctx_id=%d, info=*0x%x)", ctx_id, info); @@ -6973,7 +6973,8 @@ error_code sceNpSignalingGetLocalNetInfo(u32 ctx_id, vm::ptrsize != sizeof(SceNpSignalingNetInfo)) + // Library has backward support for a version of SceNpSignalingNetInfo without npport + if (!info || (info->size != sizeof(SceNpSignalingNetInfo) && info->size != sizeof(SceNpSignalingNetInfoDeprecated))) { return SCE_NP_SIGNALING_ERROR_INVALID_ARGUMENT; } @@ -6985,7 +6986,12 @@ error_code sceNpSignalingGetLocalNetInfo(u32 ctx_id, vm::ptrnat_status = SCE_NP_SIGNALING_NETINFO_NAT_STATUS_TYPE2; info->upnp_status = nph.get_upnp_status(); info->npport_status = SCE_NP_SIGNALING_NETINFO_NPPORT_STATUS_OPEN; - info->npport = SCE_NP_PORT; + + if (info->size == sizeof(SceNpSignalingNetInfo)) + { + auto new_info = vm::unsafe_ptr_cast(info); + new_info->npport = SCE_NP_PORT; + } return CELL_OK; } diff --git a/rpcs3/Emu/Cell/Modules/sceNp.h b/rpcs3/Emu/Cell/Modules/sceNp.h index bc2ab7c6e2..12ca388ba2 100644 --- a/rpcs3/Emu/Cell/Modules/sceNp.h +++ b/rpcs3/Emu/Cell/Modules/sceNp.h @@ -1584,6 +1584,16 @@ struct SceNpSignalingNetInfo be_t npport; }; +struct SceNpSignalingNetInfoDeprecated +{ + be_t size; + be_t local_addr; // in_addr + be_t mapped_addr; // in_addr + be_t nat_status; + be_t upnp_status; + be_t npport_status; +}; + struct SceNpCustomMenuAction { be_t options; diff --git a/rpcs3/Emu/Cell/Modules/sceNp2.cpp b/rpcs3/Emu/Cell/Modules/sceNp2.cpp index ed4655ccd4..7a2ee50bd8 100644 --- a/rpcs3/Emu/Cell/Modules/sceNp2.cpp +++ b/rpcs3/Emu/Cell/Modules/sceNp2.cpp @@ -1300,7 +1300,7 @@ error_code sceNpMatching2GrantRoomOwner( error_code sceNpMatching2CreateContext( vm::cptr npId, vm::cptr commId, vm::cptr passPhrase, vm::ptr ctxId, s32 option) { - sceNp2.warning("sceNpMatching2CreateContext(npId=*0x%x, commId=*0x%x(%s), passPhrase=*0x%x, ctxId=*0x%x, option=%d)", npId, commId, commId ? commId->data : "", passPhrase, ctxId, option); + sceNp2.warning("sceNpMatching2CreateContext(npId=*0x%x, commId=*0x%x(%s), passPhrase=*0x%x, ctxId=*0x%x, option=%d)", npId, commId, commId ? std::string_view(commId->data, 9) : "", passPhrase, ctxId, option); auto& nph = g_fxo->get>(); diff --git a/rpcs3/Emu/Cell/PPUAnalyser.h b/rpcs3/Emu/Cell/PPUAnalyser.h index 693ad8c7ea..8d6486c9a4 100644 --- a/rpcs3/Emu/Cell/PPUAnalyser.h +++ b/rpcs3/Emu/Cell/PPUAnalyser.h @@ -145,6 +145,7 @@ struct ppu_module : public Type std::shared_ptr> jit_bounds; // JIT instance modules addresses range std::unordered_map imports; // Imports information for release upon unload (TODO: OVL implementation!) std::map>> stub_addr_to_constant_state_of_registers; // Tells possible constant states of registers of functions + std::vector excluded_funcs; // Function code not be overwritten bool is_relocatable = false; // Is code relocatable(?) template diff --git a/rpcs3/Emu/Cell/PPUDisAsm.cpp b/rpcs3/Emu/Cell/PPUDisAsm.cpp index 569b60b612..484688ee12 100644 --- a/rpcs3/Emu/Cell/PPUDisAsm.cpp +++ b/rpcs3/Emu/Cell/PPUDisAsm.cpp @@ -221,7 +221,7 @@ std::pair PPUDisAsm::try_get_const_op_gpr_value(u32 re GET_CONST_REG(reg_rs, op.rs); - return { form, utils::rol64(reg_rs, op.sh64) & (~0ull << (op.mbe64 ^ 63)) }; + return {form, std::rotl(reg_rs, op.sh64) & (~0ull << (op.mbe64 ^ 63))}; } case ppu_itype::OR: { diff --git a/rpcs3/Emu/Cell/PPUInterpreter.cpp b/rpcs3/Emu/Cell/PPUInterpreter.cpp index 69d11864d1..a3c6611b15 100644 --- a/rpcs3/Emu/Cell/PPUInterpreter.cpp +++ b/rpcs3/Emu/Cell/PPUInterpreter.cpp @@ -3483,7 +3483,7 @@ auto RLWIMI() static const auto exec = [](ppu_thread& ppu, ppu_opcode_t op) { const u64 mask = ppu_rotate_mask(32 + op.mb32, 32 + op.me32); - ppu.gpr[op.ra] = (ppu.gpr[op.ra] & ~mask) | (dup32(utils::rol32(static_cast(ppu.gpr[op.rs]), op.sh32)) & mask); + ppu.gpr[op.ra] = (ppu.gpr[op.ra] & ~mask) | (dup32(std::rotl(static_cast(ppu.gpr[op.rs]), op.sh32)) & mask); if constexpr (((Flags == has_rc) || ...)) ppu_cr_set(ppu, 0, ppu.gpr[op.ra], 0); }; @@ -3497,7 +3497,7 @@ auto RLWINM() return ppu_exec_select::template select<>(); static const auto exec = [](ppu_thread& ppu, ppu_opcode_t op) { - ppu.gpr[op.ra] = dup32(utils::rol32(static_cast(ppu.gpr[op.rs]), op.sh32)) & ppu_rotate_mask(32 + op.mb32, 32 + op.me32); + ppu.gpr[op.ra] = dup32(std::rotl(static_cast(ppu.gpr[op.rs]), op.sh32)) & ppu_rotate_mask(32 + op.mb32, 32 + op.me32); if constexpr (((Flags == has_rc) || ...)) ppu_cr_set(ppu, 0, ppu.gpr[op.ra], 0); }; @@ -3511,7 +3511,7 @@ auto RLWNM() return ppu_exec_select::template select<>(); static const auto exec = [](ppu_thread& ppu, ppu_opcode_t op) { - ppu.gpr[op.ra] = dup32(utils::rol32(static_cast(ppu.gpr[op.rs]), ppu.gpr[op.rb] & 0x1f)) & ppu_rotate_mask(32 + op.mb32, 32 + op.me32); + ppu.gpr[op.ra] = dup32(std::rotl(static_cast(ppu.gpr[op.rs]), ppu.gpr[op.rb] & 0x1f)) & ppu_rotate_mask(32 + op.mb32, 32 + op.me32); if constexpr (((Flags == has_rc) || ...)) ppu_cr_set(ppu, 0, ppu.gpr[op.ra], 0); }; @@ -3599,7 +3599,7 @@ auto RLDICL() return ppu_exec_select::template select<>(); static const auto exec = [](ppu_thread& ppu, ppu_opcode_t op) { - ppu.gpr[op.ra] = utils::rol64(ppu.gpr[op.rs], op.sh64) & (~0ull >> op.mbe64); + ppu.gpr[op.ra] = std::rotl(ppu.gpr[op.rs], op.sh64) & (~0ull >> op.mbe64); if constexpr (((Flags == has_rc) || ...)) ppu_cr_set(ppu, 0, ppu.gpr[op.ra], 0); }; @@ -3613,7 +3613,7 @@ auto RLDICR() return ppu_exec_select::template select<>(); static const auto exec = [](ppu_thread& ppu, ppu_opcode_t op) { - ppu.gpr[op.ra] = utils::rol64(ppu.gpr[op.rs], op.sh64) & (~0ull << (op.mbe64 ^ 63)); + ppu.gpr[op.ra] = std::rotl(ppu.gpr[op.rs], op.sh64) & (~0ull << (op.mbe64 ^ 63)); if constexpr (((Flags == has_rc) || ...)) ppu_cr_set(ppu, 0, ppu.gpr[op.ra], 0); }; @@ -3627,7 +3627,7 @@ auto RLDIC() return ppu_exec_select::template select<>(); static const auto exec = [](ppu_thread& ppu, ppu_opcode_t op) { - ppu.gpr[op.ra] = utils::rol64(ppu.gpr[op.rs], op.sh64) & ppu_rotate_mask(op.mbe64, op.sh64 ^ 63); + ppu.gpr[op.ra] = std::rotl(ppu.gpr[op.rs], op.sh64) & ppu_rotate_mask(op.mbe64, op.sh64 ^ 63); if constexpr (((Flags == has_rc) || ...)) ppu_cr_set(ppu, 0, ppu.gpr[op.ra], 0); }; @@ -3642,7 +3642,7 @@ auto RLDIMI() static const auto exec = [](ppu_thread& ppu, ppu_opcode_t op) { const u64 mask = ppu_rotate_mask(op.mbe64, op.sh64 ^ 63); - ppu.gpr[op.ra] = (ppu.gpr[op.ra] & ~mask) | (utils::rol64(ppu.gpr[op.rs], op.sh64) & mask); + ppu.gpr[op.ra] = (ppu.gpr[op.ra] & ~mask) | (std::rotl(ppu.gpr[op.rs], op.sh64) & mask); if constexpr (((Flags == has_rc) || ...)) ppu_cr_set(ppu, 0, ppu.gpr[op.ra], 0); }; @@ -3656,7 +3656,7 @@ auto RLDCL() return ppu_exec_select::template select<>(); static const auto exec = [](ppu_thread& ppu, ppu_opcode_t op) { - ppu.gpr[op.ra] = utils::rol64(ppu.gpr[op.rs], ppu.gpr[op.rb] & 0x3f) & (~0ull >> op.mbe64); + ppu.gpr[op.ra] = std::rotl(ppu.gpr[op.rs], ppu.gpr[op.rb] & 0x3f) & (~0ull >> op.mbe64); if constexpr (((Flags == has_rc) || ...)) ppu_cr_set(ppu, 0, ppu.gpr[op.ra], 0); }; @@ -3670,7 +3670,7 @@ auto RLDCR() return ppu_exec_select::template select<>(); static const auto exec = [](ppu_thread& ppu, ppu_opcode_t op) { - ppu.gpr[op.ra] = utils::rol64(ppu.gpr[op.rs], ppu.gpr[op.rb] & 0x3f) & (~0ull << (op.mbe64 ^ 63)); + ppu.gpr[op.ra] = std::rotl(ppu.gpr[op.rs], ppu.gpr[op.rb] & 0x3f) & (~0ull << (op.mbe64 ^ 63)); if constexpr (((Flags == has_rc) || ...)) ppu_cr_set(ppu, 0, ppu.gpr[op.ra], 0); }; @@ -7659,8 +7659,8 @@ ppu_interpreter_rt_base::ppu_interpreter_rt_base() noexcept INIT(LBZUX); INIT_RC(NOR); INIT(STVEBX); - INIT_OV(SUBFE); - INIT_OV(ADDE); + INIT_RC_OV(SUBFE); + INIT_RC_OV(ADDE); INIT(MTOCRF); INIT(STDX); INIT(STWCX); diff --git a/rpcs3/Emu/Cell/PPUModule.cpp b/rpcs3/Emu/Cell/PPUModule.cpp index eca1177768..65ab0fe18d 100644 --- a/rpcs3/Emu/Cell/PPUModule.cpp +++ b/rpcs3/Emu/Cell/PPUModule.cpp @@ -2088,7 +2088,7 @@ bool ppu_load_exec(const ppu_exec_object& elf, bool virtual_load, const std::str { if (prog.p_type == 0x1u /* LOAD */ && prog.p_memsz) { - using addr_range = utils::address_range; + using addr_range = utils::address_range32; const addr_range r = addr_range::start_length(static_cast(prog.p_vaddr), static_cast(prog.p_memsz)); @@ -2852,7 +2852,7 @@ std::pair, CellError> ppu_load_overlay(const ppu_exec_ob { if (prog.p_type == 0x1u /* LOAD */ && prog.p_memsz) { - using addr_range = utils::address_range; + using addr_range = utils::address_range32; const addr_range r = addr_range::start_length(::narrow(prog.p_vaddr), ::narrow(prog.p_memsz)); diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index b385829f96..456715ccf9 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -195,17 +195,17 @@ const auto ppu_gateway = build_function_asm("ppu_gateway", c.push(x86::rdi); c.push(x86::rbp); c.push(x86::rbx); - c.sub(x86::rsp, 0xa8); - c.movaps(x86::oword_ptr(x86::rsp, 0x90), x86::xmm15); - c.movaps(x86::oword_ptr(x86::rsp, 0x80), x86::xmm14); - c.movaps(x86::oword_ptr(x86::rsp, 0x70), x86::xmm13); - c.movaps(x86::oword_ptr(x86::rsp, 0x60), x86::xmm12); - c.movaps(x86::oword_ptr(x86::rsp, 0x50), x86::xmm11); - c.movaps(x86::oword_ptr(x86::rsp, 0x40), x86::xmm10); - c.movaps(x86::oword_ptr(x86::rsp, 0x30), x86::xmm9); - c.movaps(x86::oword_ptr(x86::rsp, 0x20), x86::xmm8); - c.movaps(x86::oword_ptr(x86::rsp, 0x10), x86::xmm7); - c.movaps(x86::oword_ptr(x86::rsp, 0), x86::xmm6); + c.sub(x86::rsp, 0xc8); + c.movaps(x86::oword_ptr(x86::rsp, 0xb0), x86::xmm15); + c.movaps(x86::oword_ptr(x86::rsp, 0xa0), x86::xmm14); + c.movaps(x86::oword_ptr(x86::rsp, 0x90), x86::xmm13); + c.movaps(x86::oword_ptr(x86::rsp, 0x80), x86::xmm12); + c.movaps(x86::oword_ptr(x86::rsp, 0x70), x86::xmm11); + c.movaps(x86::oword_ptr(x86::rsp, 0x60), x86::xmm10); + c.movaps(x86::oword_ptr(x86::rsp, 0x50), x86::xmm9); + c.movaps(x86::oword_ptr(x86::rsp, 0x40), x86::xmm8); + c.movaps(x86::oword_ptr(x86::rsp, 0x30), x86::xmm7); + c.movaps(x86::oword_ptr(x86::rsp, 0x20), x86::xmm6); #else c.push(x86::rbp); c.push(x86::r15); @@ -252,17 +252,17 @@ const auto ppu_gateway = build_function_asm("ppu_gateway", } #ifdef _WIN32 - c.movaps(x86::xmm6, x86::oword_ptr(x86::rsp, 0)); - c.movaps(x86::xmm7, x86::oword_ptr(x86::rsp, 0x10)); - c.movaps(x86::xmm8, x86::oword_ptr(x86::rsp, 0x20)); - c.movaps(x86::xmm9, x86::oword_ptr(x86::rsp, 0x30)); - c.movaps(x86::xmm10, x86::oword_ptr(x86::rsp, 0x40)); - c.movaps(x86::xmm11, x86::oword_ptr(x86::rsp, 0x50)); - c.movaps(x86::xmm12, x86::oword_ptr(x86::rsp, 0x60)); - c.movaps(x86::xmm13, x86::oword_ptr(x86::rsp, 0x70)); - c.movaps(x86::xmm14, x86::oword_ptr(x86::rsp, 0x80)); - c.movaps(x86::xmm15, x86::oword_ptr(x86::rsp, 0x90)); - c.add(x86::rsp, 0xa8); + c.movaps(x86::xmm6, x86::oword_ptr(x86::rsp, 0x20)); + c.movaps(x86::xmm7, x86::oword_ptr(x86::rsp, 0x30)); + c.movaps(x86::xmm8, x86::oword_ptr(x86::rsp, 0x40)); + c.movaps(x86::xmm9, x86::oword_ptr(x86::rsp, 0x50)); + c.movaps(x86::xmm10, x86::oword_ptr(x86::rsp, 0x60)); + c.movaps(x86::xmm11, x86::oword_ptr(x86::rsp, 0x70)); + c.movaps(x86::xmm12, x86::oword_ptr(x86::rsp, 0x80)); + c.movaps(x86::xmm13, x86::oword_ptr(x86::rsp, 0x90)); + c.movaps(x86::xmm14, x86::oword_ptr(x86::rsp, 0xa0)); + c.movaps(x86::xmm15, x86::oword_ptr(x86::rsp, 0xb0)); + c.add(x86::rsp, 0xc8); c.pop(x86::rbx); c.pop(x86::rbp); c.pop(x86::rdi); @@ -934,13 +934,40 @@ struct ppu_far_jumps_t ppu_far_jumps_t(int) noexcept {} std::map vals; + std::pair vals_range{0, 0}; ::jit_runtime rt; mutable shared_mutex mutex; + void add_value(u32 addr, all_info_t info) + { + vals.insert_or_assign(addr, std::move(info)); + + if (vals.size() == 1) + { + vals_range.first = addr; + vals_range.second = addr; + } + else + { + vals_range.first = std::min(vals_range.first, addr); + vals_range.second = std::max(vals_range.second, addr); + } + } + // Get target address, 'ppu' is used in ppu_far_jump in order to modify registers u32 get_target(u32 pc, ppu_thread* ppu = nullptr) { + if (vals_range.first > pc) + { + return 0; + } + + if (vals_range.second < pc) + { + return 0; + } + reader_lock lock(mutex); if (auto it = vals.find(pc); it != vals.end()) @@ -949,7 +976,7 @@ struct ppu_far_jumps_t return all_info.get_target(pc, ppu); } - return {}; + return 0; } // Get function patches in range (entry -> target) @@ -957,6 +984,16 @@ struct ppu_far_jumps_t { std::vector> targets; + if (vals_range.first >= pc + size) + { + return targets; + } + + if (vals_range.second < pc) + { + return targets; + } + reader_lock lock(mutex); auto it = vals.lower_bound(pc); @@ -1005,22 +1042,21 @@ struct ppu_far_jumps_t #ifdef ARCH_X64 c.mov(args[0], x86::rbp); - c.mov(x86::dword_ptr(args[0], ::offset32(&ppu_thread::cia)), pc); + c.mov(args[2], vm::g_base_addr + pc); c.jmp(ppu_far_jump); #else Label jmp_address = c.newLabel(); - Label imm_address = c.newLabel(); + Label this_op_address = c.newLabel(); - c.ldr(args[1].w(), arm::ptr(imm_address)); - c.str(args[1].w(), arm::Mem(args[0], ::offset32(&ppu_thread::cia))); + c.ldr(args[2], arm::ptr(this_op_address)); c.ldr(args[1], arm::ptr(jmp_address)); c.br(args[1]); c.align(AlignMode::kCode, 16); c.bind(jmp_address); c.embedUInt64(reinterpret_cast(ppu_far_jump)); - c.bind(imm_address); - c.embedUInt32(pc); + c.bind(this_op_address); + c.embedUInt64(reinterpret_cast(vm::g_base_addr) + pc); #endif }, &rt); } @@ -1039,9 +1075,9 @@ u32 ppu_get_far_jump(u32 pc) return g_fxo->get().get_target(pc); } -static void ppu_far_jump(ppu_thread& ppu, ppu_opcode_t, be_t*, ppu_intrp_func*) +static void ppu_far_jump(ppu_thread& ppu, ppu_opcode_t, be_t* this_op, ppu_intrp_func*) { - const u32 cia = g_fxo->get().get_target(ppu.cia, &ppu); + const u32 cia = g_fxo->get().get_target(vm::get_addr(this_op), &ppu); if (!vm::check_addr(cia, vm::page_executable)) { @@ -1110,7 +1146,7 @@ bool ppu_form_branch_to_code(u32 entry, u32 target, bool link, bool with_toc, st auto& jumps = g_fxo->get(); std::lock_guard lock(jumps.mutex); - jumps.vals.insert_or_assign(entry, ppu_far_jumps_t::all_info_t{target, link, with_toc, std::move(module_name)}); + jumps.add_value(entry, ppu_far_jumps_t::all_info_t{target, link, with_toc, std::move(module_name)}); ppu_register_function_at(entry, 4, g_cfg.core.ppu_decoder == ppu_decoder_type::_static ? &ppu_far_jump : ensure(g_fxo->get().gen_jump(entry))); return true; @@ -2326,10 +2362,11 @@ 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 == 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 +3154,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 +3650,70 @@ static bool ppu_store_reservation(ppu_thread& ppu, u32 addr, u64 reg_value) { extern atomic_t liblv2_begin, liblv2_end; + u32 notify = ppu.res_notify; + // Avoid notifications from lwmutex or sys_spinlock - if (new_data != old_data && (ppu.cia < liblv2_begin || ppu.cia >= liblv2_end)) + const bool is_liblv2_or_null = (ppu.cia >= liblv2_begin && ppu.cia < liblv2_end); + + if (!is_liblv2_or_null) { - u32 notify = ppu.res_notify; + // 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, rtime); - if (notify) + switch (count) { - if (ppu.res_notify_time == vm::reservation_notifier_count_index(notify).second) - { - ppu.state += cpu_flag::wait; - vm::reservation_notifier_notify(notify); - } - else - { - notify = 0; - } - - ppu.res_notify = 0; + case 0: + { + // Nothing to do + break; } - - if ((addr ^ notify) & -128) + case 1: { - // 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) + // 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) { - case 0: - { - // Nothing to do - break; - } - case 1: - { - if (!notify) + if (!notify || ((notify & -128) == (addr & -128) && new_data != old_data)) { ppu.res_notify = addr; - ppu.res_notify_time = index; + ppu.res_notify_time = rtime; + ppu.res_notify_postpone_streak++; + notify = 0; break; } - - // Notify both - [[fallthrough]]; } - default: + + // Notify both + [[fallthrough]]; + } + default: + { + if (cpu_flag::wait - ppu.state) { - if (!notify) - { - ppu.state += cpu_flag::wait; - } + ppu.state += cpu_flag::wait; + } - vm::reservation_notifier_notify(addr); - break; - } - } + 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(); } - static_cast(ppu.test_stopped()); + ppu.res_notify = 0; + ppu.res_notify_postpone_streak = 0; } + static_cast(ppu.test_stopped()); + if (addr == ppu.last_faddr) { ppu.last_succ++; @@ -3682,7 +3721,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,14 +3731,15 @@ 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()); } ppu.res_notify = 0; + ppu.res_notify_postpone_streak = 0; } ppu.raddr = 0; @@ -5211,6 +5250,7 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_s { // Replace the function with ppu_far_jump fpos++; + part.excluded_funcs.emplace_back(func.addr); continue; } } @@ -5243,6 +5283,11 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_s continue; } + if (std::count(part.excluded_funcs.begin(), part.excluded_funcs.end(), func.addr)) + { + continue; + } + const be_t addr = func.addr - reloc; const be_t size = func.size; sha1_update(&ctx, reinterpret_cast(&addr), sizeof(addr)); @@ -5340,6 +5385,13 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_s continue; } + if (g_fxo->is_init() && !g_fxo->get().get_targets(func.addr, func.size).empty()) + { + // Filter out functions with patches + part.excluded_funcs.emplace_back(func.addr); + continue; + } + addrs.emplace_back(func.addr - reloc); } @@ -5751,7 +5803,11 @@ static void ppu_initialize2(jit_compiler& jit, const ppu_module& module std::unique_ptr _module = std::make_unique(obj_name, jit.get_context()); // Initialize target +#if LLVM_VERSION_MAJOR >= 21 && (LLVM_VERSION_MINOR >= 1 || LLVM_VERSION_MAJOR >= 22) + _module->setTargetTriple(Triple(jit_compiler::triple1())); +#else _module->setTargetTriple(jit_compiler::triple1()); +#endif _module->setDataLayout(jit.get_engine().getTargetMachine()->createDataLayout()); // Initialize translator @@ -5760,7 +5816,7 @@ static void ppu_initialize2(jit_compiler& jit, const ppu_module& module // Define some types const auto _func = FunctionType::get(translator.get_type(), { translator.get_type(), // Exec base - translator.GetContextType()->getPointerTo(), // PPU context + translator.get_type(), // PPU context translator.get_type(), // Segment address (for PRX) translator.get_type(), // Memory base translator.get_type(), // r0 @@ -5776,6 +5832,11 @@ static void ppu_initialize2(jit_compiler& jit, const ppu_module& module { if (func.size) { + if (std::count(module_part.excluded_funcs.begin(), module_part.excluded_funcs.end(), func.addr)) + { + continue; + } + const auto f = cast(_module->getOrInsertFunction(fmt::format("__0x%x", func.addr - reloc), _func).getCallee()); f->setCallingConv(CallingConv::GHC); f->addParamAttr(1, llvm::Attribute::NoAlias); @@ -5831,6 +5892,11 @@ static void ppu_initialize2(jit_compiler& jit, const ppu_module& module if (mod_func.size) { + if (std::count(module_part.excluded_funcs.begin(), module_part.excluded_funcs.end(), mod_func.addr)) + { + continue; + } + num_func++; guest_code_size += mod_func.size; max_addr = std::max(max_addr, mod_func.addr + mod_func.size); 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/PPUTranslator.cpp b/rpcs3/Emu/Cell/PPUTranslator.cpp index 4d0cb528ca..3e0959807b 100644 --- a/rpcs3/Emu/Cell/PPUTranslator.cpp +++ b/rpcs3/Emu/Cell/PPUTranslator.cpp @@ -340,7 +340,7 @@ Function* PPUTranslator::GetSymbolResolver(const ppu_module& info) const auto ftype = FunctionType::get(get_type(), { get_type(), // Exec base - GetContextType()->getPointerTo(), // PPU context + m_ir->getPtrTy(), // PPU context get_type(), // Segment address (for PRX) get_type(), // Memory base get_type(), // r0 @@ -364,6 +364,12 @@ Function* PPUTranslator::GetSymbolResolver(const ppu_module& info) continue; } + if (std::count(info.excluded_funcs.begin(), info.excluded_funcs.end(), f.addr)) + { + // Excluded function (possibly patched) + continue; + } + vec_addrs.push_back(static_cast(f.addr - base)); functions.push_back(cast(m_module->getOrInsertFunction(fmt::format("__0x%x", f.addr - base), ftype).getCallee())); } @@ -380,7 +386,7 @@ Function* PPUTranslator::GetSymbolResolver(const ppu_module& info) const auto addr_array = new GlobalVariable(*m_module, addr_array_type, false, GlobalValue::PrivateLinkage, ConstantDataArray::get(m_context, vec_addrs)); // Create an array of function pointers - const auto func_table_type = ArrayType::get(ftype->getPointerTo(), functions.size()); + const auto func_table_type = ArrayType::get(m_ir->getPtrTy(), functions.size()); const auto init_func_table = ConstantArray::get(func_table_type, functions); const auto func_table = new GlobalVariable(*m_module, func_table_type, false, GlobalVariable::PrivateLinkage, init_func_table); @@ -407,18 +413,17 @@ Function* PPUTranslator::GetSymbolResolver(const ppu_module& info) const auto func_pc = ZExt(m_ir->CreateLoad(ptr_inst->getResultElementType(), ptr_inst), get_type()); ptr_inst = dyn_cast(m_ir->CreateGEP(func_table->getValueType(), func_table, {m_ir->getInt64(0), index_value})); - assert(ptr_inst->getResultElementType() == ftype->getPointerTo()); + assert(ptr_inst->getResultElementType() == m_ir->getPtrTy()); const auto faddr = m_ir->CreateLoad(ptr_inst->getResultElementType(), ptr_inst); 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 +615,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, type->getPointerTo())); + 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 +828,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() @@ -1111,7 +1115,24 @@ void PPUTranslator::VCFSX(ppu_opcode_t op) void PPUTranslator::VCFUX(ppu_opcode_t op) { const auto b = get_vr(op.vb); - set_vr(op.vd, fpcast(b) * fsplat(std::pow(2, -static_cast(op.vuimm)))); + +#ifdef ARCH_ARM64 + return set_vr(op.vd, fpcast(b) * fsplat(std::pow(2, -static_cast(op.vuimm)))); +#else + if (m_use_avx512) + { + return set_vr(op.vd, fpcast(b) * fsplat(std::pow(2, -static_cast(op.vuimm)))); + } + + constexpr int bit_shift = 9; + const auto shifted = (b >> bit_shift); + const auto cleared = shifted << bit_shift; + const auto low_bits = b - cleared; + const auto high_part = fpcast(noncast(shifted)) * fsplat(static_cast(1u << bit_shift)); + const auto low_part = fpcast(noncast(low_bits)); + const auto temp = high_part + low_part; + set_vr(op.vd, temp * fsplat(std::pow(2, -static_cast(op.vuimm)))); +#endif } void PPUTranslator::VCMPBFP(ppu_opcode_t op) @@ -2794,8 +2815,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()), GetType()->getPointerTo()); - Value* ln1 = m_ir->CreateIntToPtr(m_ir->CreatePtrToInt(m_ir->CreateStructGEP(m_thread_type, m_thread, 115), GetType()), GetType()->getPointerTo()); + 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); @@ -5384,7 +5405,7 @@ MDNode* PPUTranslator::CheckBranchProbability(u32 bo) void PPUTranslator::build_interpreter() { #define BUILD_VEC_INST(i) { \ - m_function = llvm::cast(m_module->getOrInsertFunction("op_" #i, get_type(), m_thread_type->getPointerTo()).getCallee()); \ + m_function = llvm::cast(m_module->getOrInsertFunction("op_" #i, get_type(), m_ir->getPtrTy()).getCallee()); \ std::fill(std::begin(m_globals), std::end(m_globals), nullptr); \ std::fill(std::begin(m_locals), std::end(m_locals), nullptr); \ IRBuilder<> irb(BasicBlock::Create(m_context, "__entry", m_function)); \ diff --git a/rpcs3/Emu/Cell/RawSPUThread.cpp b/rpcs3/Emu/Cell/RawSPUThread.cpp index 01041555e1..81bb6fd89e 100644 --- a/rpcs3/Emu/Cell/RawSPUThread.cpp +++ b/rpcs3/Emu/Cell/RawSPUThread.cpp @@ -130,6 +130,18 @@ bool spu_thread::read_reg(const u32 addr, u32& value) return true; } + case Prxy_QueryMask_offs: + { + value = mfc_prxy_mask; + return true; + } + + case Prxy_QueryType_offs: + { + value = 0; + return true; + } + case SPU_Out_MBox_offs: { value = ch_out_mbox.pop(); @@ -138,7 +150,29 @@ bool spu_thread::read_reg(const u32 addr, u32& value) case SPU_MBox_Status_offs: { - value = (ch_out_mbox.get_count() & 0xff) | ((4 - ch_in_mbox.get_count()) << 8 & 0xff00) | (ch_out_intr_mbox.get_count() << 16 & 0xff0000); + // Load channel counts atomically + auto counts = std::make_tuple(ch_out_mbox.get_count(), ch_in_mbox.get_count(), ch_out_intr_mbox.get_count()); + + while (true) + { + atomic_fence_acquire(); + + const auto counts_check = std::make_tuple(ch_out_mbox.get_count(), ch_in_mbox.get_count(), ch_out_intr_mbox.get_count()); + + if (counts_check == counts) + { + break; + } + + // Update and reload + counts = counts_check; + } + + const u32 out_mbox = std::get<0>(counts); + const u32 in_mbox = 4 - std::get<1>(counts); + const u32 intr_mbox = std::get<2>(counts); + + value = (out_mbox & 0xff) | ((in_mbox << 8) & 0xff00) | ((intr_mbox << 16) & 0xff0000); return true; } @@ -345,6 +379,8 @@ bool spu_thread::test_is_problem_state_register_offset(u32 offset, bool for_read case MFC_QStatus_offs: case SPU_Out_MBox_offs: case SPU_MBox_Status_offs: + case Prxy_QueryType_offs: + case Prxy_QueryMask_offs: case SPU_Status_offs: case Prxy_TagStatus_offs: case SPU_NPC_offs: diff --git a/rpcs3/Emu/Cell/SPUASMJITRecompiler.cpp b/rpcs3/Emu/Cell/SPUASMJITRecompiler.cpp index 6b4b456f3d..5100bc3ded 100644 --- a/rpcs3/Emu/Cell/SPUASMJITRecompiler.cpp +++ b/rpcs3/Emu/Cell/SPUASMJITRecompiler.cpp @@ -186,7 +186,7 @@ spu_function_t spu_recompiler::compile(spu_program&& _func) c->cmp(SPU_OFF_32(state), 0); c->jnz(label_stop); - if (g_cfg.core.spu_prof && g_cfg.core.spu_verification) + if ((g_cfg.core.spu_prof || g_cfg.core.spu_debug) && g_cfg.core.spu_verification) { c->mov(x86::rax, m_hash_start & -0xffff); c->mov(SPU_OFF_64(block_hash), x86::rax); @@ -755,7 +755,7 @@ spu_function_t spu_recompiler::compile(spu_program&& _func) c->add(SPU_OFF_64(block_counter), ::size32(words) / (words_align / 4)); // Set block hash for profiling (if enabled) - if (g_cfg.core.spu_prof) + if (g_cfg.core.spu_prof || g_cfg.core.spu_debug) { c->mov(x86::rax, m_hash_start | 0xffff); c->mov(SPU_OFF_64(block_hash), x86::rax); @@ -1199,7 +1199,7 @@ void spu_recompiler::branch_set_link(u32 target) c->movdqa(x86::dqword_ptr(*cpu, *qw1, 0, ::offset32(&spu_thread::stack_mirror)), x86::xmm0); // Set block hash for profiling (if enabled) - if (g_cfg.core.spu_prof) + if (g_cfg.core.spu_prof || g_cfg.core.spu_debug) { c->mov(x86::rax, m_hash_start | 0xffff); c->mov(SPU_OFF_64(block_hash), x86::rax); @@ -3212,7 +3212,7 @@ void spu_recompiler::ROTQBYI(spu_opcode_t op) } else if (s == 4 || s == 8 || s == 12) { - c->pshufd(va, va, utils::rol8(0xE4, s / 2)); + c->pshufd(va, va, std::rotl(0xE4, s / 2)); } else if (utils::has_ssse3()) { diff --git a/rpcs3/Emu/Cell/SPUCommonRecompiler.cpp b/rpcs3/Emu/Cell/SPUCommonRecompiler.cpp index d73efc8e84..11eb124eae 100644 --- a/rpcs3/Emu/Cell/SPUCommonRecompiler.cpp +++ b/rpcs3/Emu/Cell/SPUCommonRecompiler.cpp @@ -38,26 +38,9 @@ constexpr u32 s_reg_max = spu_recompiler_base::s_reg_max; template struct span_less { - static int compare(const std::span& lhs, const std::span& rhs) noexcept + static auto compare(const std::span& lhs, const std::span& rhs) noexcept { - // TODO: Replace with std::lexicographical_compare_three_way when it becomes available to all compilers - for (usz i = 0, last = std::min(lhs.size(), rhs.size()); i != last; i++) - { - const T vl = lhs[i]; - const T vr = rhs[i]; - - if (vl != vr) - { - return vl < vr ? -1 : 1; - } - } - - if (lhs.size() != rhs.size()) - { - return lhs.size() < rhs.size() ? -1 : 1; - } - - return 0; + return std::lexicographical_compare_three_way(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); } bool operator()(const std::span& lhs, const std::span& rhs) const noexcept @@ -305,17 +288,17 @@ DECLARE(spu_runtime::g_gateway) = build_function_asm("spu_gatewa c.push(x86::rdi); c.push(x86::rbp); c.push(x86::rbx); - c.sub(x86::rsp, 0xa8); - c.movaps(x86::oword_ptr(x86::rsp, 0x90), x86::xmm15); - c.movaps(x86::oword_ptr(x86::rsp, 0x80), x86::xmm14); - c.movaps(x86::oword_ptr(x86::rsp, 0x70), x86::xmm13); - c.movaps(x86::oword_ptr(x86::rsp, 0x60), x86::xmm12); - c.movaps(x86::oword_ptr(x86::rsp, 0x50), x86::xmm11); - c.movaps(x86::oword_ptr(x86::rsp, 0x40), x86::xmm10); - c.movaps(x86::oword_ptr(x86::rsp, 0x30), x86::xmm9); - c.movaps(x86::oword_ptr(x86::rsp, 0x20), x86::xmm8); - c.movaps(x86::oword_ptr(x86::rsp, 0x10), x86::xmm7); - c.movaps(x86::oword_ptr(x86::rsp, 0), x86::xmm6); + c.sub(x86::rsp, 0xc8); + c.movaps(x86::oword_ptr(x86::rsp, 0xb0), x86::xmm15); + c.movaps(x86::oword_ptr(x86::rsp, 0xa0), x86::xmm14); + c.movaps(x86::oword_ptr(x86::rsp, 0x90), x86::xmm13); + c.movaps(x86::oword_ptr(x86::rsp, 0x80), x86::xmm12); + c.movaps(x86::oword_ptr(x86::rsp, 0x70), x86::xmm11); + c.movaps(x86::oword_ptr(x86::rsp, 0x60), x86::xmm10); + c.movaps(x86::oword_ptr(x86::rsp, 0x50), x86::xmm9); + c.movaps(x86::oword_ptr(x86::rsp, 0x40), x86::xmm8); + c.movaps(x86::oword_ptr(x86::rsp, 0x30), x86::xmm7); + c.movaps(x86::oword_ptr(x86::rsp, 0x20), x86::xmm6); #else c.push(x86::rbp); c.push(x86::r15); @@ -348,17 +331,17 @@ DECLARE(spu_runtime::g_gateway) = build_function_asm("spu_gatewa } #ifdef _WIN32 - c.movaps(x86::xmm6, x86::oword_ptr(x86::rsp, 0)); - c.movaps(x86::xmm7, x86::oword_ptr(x86::rsp, 0x10)); - c.movaps(x86::xmm8, x86::oword_ptr(x86::rsp, 0x20)); - c.movaps(x86::xmm9, x86::oword_ptr(x86::rsp, 0x30)); - c.movaps(x86::xmm10, x86::oword_ptr(x86::rsp, 0x40)); - c.movaps(x86::xmm11, x86::oword_ptr(x86::rsp, 0x50)); - c.movaps(x86::xmm12, x86::oword_ptr(x86::rsp, 0x60)); - c.movaps(x86::xmm13, x86::oword_ptr(x86::rsp, 0x70)); - c.movaps(x86::xmm14, x86::oword_ptr(x86::rsp, 0x80)); - c.movaps(x86::xmm15, x86::oword_ptr(x86::rsp, 0x90)); - c.add(x86::rsp, 0xa8); + c.movaps(x86::xmm6, x86::oword_ptr(x86::rsp, 0x20)); + c.movaps(x86::xmm7, x86::oword_ptr(x86::rsp, 0x30)); + c.movaps(x86::xmm8, x86::oword_ptr(x86::rsp, 0x40)); + c.movaps(x86::xmm9, x86::oword_ptr(x86::rsp, 0x50)); + c.movaps(x86::xmm10, x86::oword_ptr(x86::rsp, 0x60)); + c.movaps(x86::xmm11, x86::oword_ptr(x86::rsp, 0x70)); + c.movaps(x86::xmm12, x86::oword_ptr(x86::rsp, 0x80)); + c.movaps(x86::xmm13, x86::oword_ptr(x86::rsp, 0x90)); + c.movaps(x86::xmm14, x86::oword_ptr(x86::rsp, 0xa0)); + c.movaps(x86::xmm15, x86::oword_ptr(x86::rsp, 0xb0)); + c.add(x86::rsp, 0xc8); c.pop(x86::rbx); c.pop(x86::rbp); c.pop(x86::rdi); @@ -1309,7 +1292,7 @@ bool spu_program::operator<(const spu_program& rhs) const noexcept std::span lhs_data(data.data() + lhs_offs, data.size() - lhs_offs); std::span rhs_data(rhs.data.data() + rhs_offs, rhs.data.size() - rhs_offs); - const int cmp0 = span_less::compare(lhs_data, rhs_data); + const auto cmp0 = span_less::compare(lhs_data, rhs_data); if (cmp0 < 0) return true; @@ -1320,7 +1303,7 @@ bool spu_program::operator<(const spu_program& rhs) const noexcept lhs_data = {data.data(), lhs_offs}; rhs_data = {rhs.data.data(), rhs_offs}; - const int cmp1 = span_less::compare(lhs_data, rhs_data); + const auto cmp1 = span_less::compare(lhs_data, rhs_data); if (cmp1 < 0) return true; @@ -2333,7 +2316,7 @@ std::vector spu_thread::discover_functions(u32 base_addr, std::span>(ls.data(), i - base_addr); - const v128 cleared_i16 = gv_and32(inst, v128::from32p(utils::rol32(~0xffff, 7))); + const v128 cleared_i16 = gv_and32(inst, v128::from32p(std::rotl(~0xffff, 7))); const v128 eq_brsl = gv_eq32(cleared_i16, v128::from32p(0x66u << 23)); const v128 eq_brasl = gv_eq32(cleared_i16, brasl_mask); const v128 eq_br = gv_eq32(cleared_i16, v128::from32p(0x64u << 23)); @@ -5396,7 +5379,7 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s const usz block_tail = duplicate_positions[it_begin - it_tail]; // Check if the distance is precisely two times from the end - if (reg_state_it.size() - block_start != utils::rol64(reg_state_it.size() - block_tail, 1)) + if (reg_state_it.size() - block_start != std::rotl(reg_state_it.size() - block_tail, 1)) { continue; } @@ -8314,7 +8297,7 @@ void spu_recompiler_base::add_pattern(bool fill_all, inst_attr attr, u32 start, end = start; } - m_patterns[start] = pattern_info{utils::address_range::start_end(start, end)}; + m_patterns[start] = pattern_info{utils::address_range32::start_end(start, end)}; for (u32 i = start; i <= (fill_all ? end : start); i += 4) { diff --git a/rpcs3/Emu/Cell/SPUInterpreter.cpp b/rpcs3/Emu/Cell/SPUInterpreter.cpp index a781da5e8f..546d94b354 100644 --- a/rpcs3/Emu/Cell/SPUInterpreter.cpp +++ b/rpcs3/Emu/Cell/SPUInterpreter.cpp @@ -291,7 +291,7 @@ bool ROT(spu_thread& spu, spu_opcode_t op) for (u32 i = 0; i < 4; i++) { - spu.gpr[op.rt]._u32[i] = utils::rol32(a._u32[i], b._u32[i]); + spu.gpr[op.rt]._u32[i] = std::rotl(a._u32[i], b._u32[i]); } return true; } @@ -346,7 +346,7 @@ bool ROTH(spu_thread& spu, spu_opcode_t op) for (u32 i = 0; i < 8; i++) { - spu.gpr[op.rt]._u16[i] = utils::rol16(a._u16[i], b._u16[i]); + spu.gpr[op.rt]._u16[i] = std::rotl(a._u16[i], b._u16[i]); } return true; } diff --git a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp index 0df3e3a854..b275773e8b 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,26 @@ 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->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 + 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) { - return _ptr(m_thread, ::offset32(offset_args...)); - } - - 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...))); - return m_ir->CreateAdd(off, add.value); + return _ptr(m_thread, ::offset32(offset_args...)); } // Return default register type @@ -596,7 +593,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 +605,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 +613,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) @@ -631,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->CreateGEP(get_type(), 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) @@ -758,11 +755,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 +959,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 +1046,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,21 +1066,21 @@ 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); m_ir->SetInsertPoint(_body); } - void putllc16_pattern(const spu_program& /*prog*/, utils::address_range range) + void putllc16_pattern(const spu_program& /*prog*/, utils::address_range32 range) { // Prevent store elimination m_block->store_context_ctr[s_reg_mfc_eal]++; @@ -1145,7 +1142,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 +1256,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 +1265,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 +1284,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 +1310,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 +1330,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 +1357,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,13 +1367,13 @@ 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); } - void putllc0_pattern(const spu_program& /*prog*/, utils::address_range /*range*/) + void putllc0_pattern(const spu_program& /*prog*/, utils::address_range32 /*range*/) { // Prevent store elimination m_block->store_context_ctr[s_reg_mfc_eal]++; @@ -1410,7 +1407,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 +1415,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 +1424,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: @@ -1601,7 +1598,11 @@ public: // Create LLVM module std::unique_ptr _module = std::make_unique(m_hash + ".obj", m_context); +#if LLVM_VERSION_MAJOR >= 21 && (LLVM_VERSION_MINOR >= 1 || LLVM_VERSION_MAJOR >= 22) + _module->setTargetTriple(Triple(jit_compiler::triple2())); +#else _module->setTargetTriple(jit_compiler::triple2()); +#endif _module->setDataLayout(m_jit.get_engine().getTargetMachine()->createDataLayout()); m_module = _module.get(); @@ -1624,10 +1625,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 @@ -1635,8 +1636,8 @@ public: m_ir->SetInsertPoint(label_test); // 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)); + if ((g_cfg.core.spu_prof || g_cfg.core.spu_debug) && g_cfg.core.spu_verification) + m_ir->CreateStore(m_ir->getInt64((m_hash_start & -65536)), spu_ptr(&spu_thread::block_hash)); if (!g_cfg.core.spu_verification) { @@ -1645,13 +1646,13 @@ public: } else if (func.data.size() == 1) { - const auto pu32 = m_ir->CreateGEP(get_type(), 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->CreateGEP(get_type(), 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); } @@ -1697,7 +1698,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 = _ptr(m_lsptr, starta_pc); llvm::Value* acc0 = nullptr; llvm::Value* acc1 = nullptr; @@ -1739,7 +1740,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 +1829,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 @@ -1855,7 +1856,7 @@ public: } vls = m_ir->CreateXor(vls, ConstantDataVector::get(m_context, llvm::ArrayRef(words, elements))); - + // Interleave accumulators for more performance if (toggle) { @@ -1899,7 +1900,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 +1934,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); @@ -1992,8 +1993,8 @@ public: set_function(m_functions[m_entry].chunk); // 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)); + if (g_cfg.core.spu_prof || g_cfg.core.spu_debug) + 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)); @@ -2230,6 +2231,12 @@ public: { for (auto& [a, b] : m_blocks) { + if (has_gpr_memory_barriers) + { + // Dive deeper and inspect GPR store barriers + break; + } + // Check if the store occurs before any barrier in the block if (b.store[i] && b.store[i] != bs && b.store_context_first_id[i] == 1) { @@ -2398,7 +2405,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 +2550,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); @@ -2876,7 +2883,11 @@ public: // Create LLVM module std::unique_ptr _module = std::make_unique("spu_interpreter.obj", m_context); +#if LLVM_VERSION_MAJOR >= 21 && (LLVM_VERSION_MINOR >= 1 || LLVM_VERSION_MAJOR >= 22) + _module->setTargetTriple(Triple(jit_compiler::triple2())); +#else _module->setTargetTriple(jit_compiler::triple2()); +#endif _module->setDataLayout(m_jit.get_engine().getTargetMachine()->createDataLayout()); m_module = _module.get(); @@ -2886,7 +2897,7 @@ public: // Create interpreter table const auto if_type = get_ftype(); - m_function_table = new GlobalVariable(*m_module, ArrayType::get(if_type->getPointerTo(), 1ull << m_interp_magn), true, GlobalValue::InternalLinkage, nullptr); + m_function_table = new GlobalVariable(*m_module, ArrayType::get(m_ir->getPtrTy(), 1ull << m_interp_magn), true, GlobalValue::InternalLinkage, nullptr); init_luts(); @@ -2907,8 +2918,8 @@ public: set_function(main_func); // 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_pc = m_ir->CreateLoad(get_type(), spu_ptr(&spu_thread::pc)); + 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 @@ -2921,7 +2932,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) @@ -2930,7 +2941,7 @@ public: m_ir->CreateStore(m_ir->CreateCall(get_intrinsic(Intrinsic::read_register), {rsp_name}), native_sp); // Decode (shift) and load function pointer - const auto first = m_ir->CreateLoad(if_type->getPointerTo(), m_ir->CreateGEP(if_type->getPointerTo(), m_interp_table, m_ir->CreateLShr(m_interp_op, 32u - m_interp_magn))); + const auto first = m_ir->CreateLoad(m_ir->getPtrTy(), m_ir->CreateGEP(m_ir->getPtrTy(), m_interp_table, m_ir->CreateLShr(m_interp_op, 32u - m_interp_magn))); const auto call0 = m_ir->CreateCall(if_type, first, {m_lsptr, m_thread, m_interp_pc, m_interp_op, m_interp_table, m_interp_7f0, m_interp_regs}); call0->setCallingConv(CallingConv::GHC); m_ir->CreateRetVoid(); @@ -3007,7 +3018,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 +3034,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,14 +3078,14 @@ 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. 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(), _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(if_type->getPointerTo(), m_ir->CreateGEP(if_type->getPointerTo(), m_interp_table, m_ir->CreateLShr(next_op, 32u - m_interp_magn))); + 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); if (!(itype & spu_itype::branch)) @@ -3104,9 +3115,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 +3171,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 +3189,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(); } @@ -3199,7 +3210,7 @@ public: } } - m_function_table->setInitializer(ConstantArray::get(ArrayType::get(if_type->getPointerTo(), 1ull << m_interp_magn), iptrs)); + m_function_table->setInitializer(ConstantArray::get(ArrayType::get(m_ir->getPtrTy(), 1ull << m_interp_magn), iptrs)); m_function_table = nullptr; for (auto& f : *_module) @@ -3303,7 +3314,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 +3421,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 +3469,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 +3486,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 +3519,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 +3539,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 +3554,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 +3597,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 +3619,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 +3698,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 +3755,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 +3770,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 +3868,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 +3884,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 +3899,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 +3923,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 +3956,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 +4010,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 +4036,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 +4055,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; @@ -4075,8 +4086,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 = _ptr(m_lsptr, zext(lsa).eval(m_ir)); + llvm::Value* dst = _ptr(m_memptr, zext(eal).eval(m_ir)); if (cmd & MFC_GET_CMD) { @@ -4104,7 +4115,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); @@ -4173,8 +4184,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 = _ptr(src, i); + const auto _dst = _ptr(dst, i); if (csize - i < stride) { m_ir->CreateStore(m_ir->CreateLoad(get_type(), _src), _dst); @@ -4196,7 +4207,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 +4215,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 +4237,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->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 = _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()) { @@ -4271,10 +4282,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); @@ -4295,7 +4306,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 +4319,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 +4346,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: @@ -4974,7 +4985,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 +6871,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 +6893,7 @@ public: }); } - + const auto [a, b, c] = get_vrs(op.ra, op.rb, op.rc); static const auto MT = match(); @@ -6928,7 +6939,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 +6948,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 +6960,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 +6969,7 @@ public: erase_stores(a, b, c, b3); set_vr(op.rt4, fsqrt(fabs(src))); return true; - } + } } } } @@ -7504,13 +7515,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), _ptr(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(), _ptr(m_lsptr, addr.value)); return byteswap(data); } @@ -7625,10 +7636,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 +7743,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 +7779,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,32 +7794,31 @@ 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) { // 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(), _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->CreateGEP(get_type(), 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->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), _ptr(m_thread, stack0.value)); const auto type = m_finfo->chunk->getFunctionType(); - const auto fval = m_ir->CreateIntToPtr(targ, type->getPointerTo()); + 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); } @@ -8051,7 +8061,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; } @@ -8111,12 +8121,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()); } @@ -8144,7 +8154,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)); } @@ -8153,7 +8163,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()); @@ -8488,11 +8498,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, _ptr(m_thread, stack0.value)); + m_ir->CreateStore(base_plus_pc, _ptr(m_thread, stack1.value)); } } @@ -8501,7 +8511,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); diff --git a/rpcs3/Emu/Cell/SPURecompiler.h b/rpcs3/Emu/Cell/SPURecompiler.h index 03b69583d8..ddee888b1e 100644 --- a/rpcs3/Emu/Cell/SPURecompiler.h +++ b/rpcs3/Emu/Cell/SPURecompiler.h @@ -397,7 +397,7 @@ protected: struct pattern_info { - utils::address_range range; + utils::address_range32 range; }; std::unordered_map m_patterns; diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index dc6e6f364a..a441f29e6b 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -303,6 +303,33 @@ static FORCE_INLINE void mov_rdata_avx(__m256i* dst, const __m256i* src) } #endif +// Check if only a single 16-bytes block has changed +// Returning its position, or -1 if that is not the situation +static inline usz scan16_rdata(const decltype(spu_thread::rdata)& _lhs, const decltype(spu_thread::rdata)& _rhs) +{ + const auto lhs = reinterpret_cast(_lhs); + const auto rhs = reinterpret_cast(_rhs); + + u32 mask = 0; + + for (usz i = 0; i < 8; i += 4) + { + const u32 a = (lhs[i + 0] != rhs[i + 0]) ? 1 : 0; + const u32 b = (lhs[i + 1] != rhs[i + 1]) ? 1 : 0; + const u32 c = (lhs[i + 2] != rhs[i + 2]) ? 1 : 0; + const u32 d = (lhs[i + 3] != rhs[i + 3]) ? 1 : 0; + + mask |= ((a << 0) + (b << 1) + (c << 2) + (d << 3)) << i; + } + + if (mask && (mask & (mask - 1)) == 0) + { + return std::countr_zero(mask); + } + + return umax; +} + #ifdef _MSC_VER __forceinline #endif @@ -461,7 +488,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); @@ -1612,10 +1639,14 @@ std::string spu_thread::dump_misc() const fmt::append(ret, "Block Weight: %u (Retreats: %u)", block_counter, block_failure); - if (g_cfg.core.spu_prof) + if (u64 hash = atomic_storage::load(block_hash)) { // Get short function hash and position in chunk - fmt::append(ret, "\nCurrent block: %s", spu_block_hash{atomic_storage::load(block_hash)}); + fmt::append(ret, "\nCurrent block: %s", spu_block_hash{hash}); + } + else if (g_cfg.core.spu_prof || g_cfg.core.spu_debug) + { + fmt::append(ret, "\nCurrent block: N/A"); } const u32 offset = group ? SPU_FAKE_BASE_ADDR + (id & 0xffffff) * SPU_LS_SIZE : RAW_SPU_BASE_ADDR + index * RAW_SPU_OFFSET; @@ -1899,6 +1930,12 @@ void spu_thread::cpu_task() return fmt::format("%sSPU[0x%07x] Thread (%s) [0x%05x]", type >= spu_type::raw ? type == spu_type::isolated ? "Iso" : "Raw" : "", cpu->lv2_id, *name_cache.get(), cpu->pc); }; + if (get_type() == spu_type::threaded) + { + // Update thread name (spu_thread::lv2_id update) + thread_ctrl::set_name(*group->threads[group->threads_map[index]], thread_name); + } + constexpr u32 invalid_spurs = 0u - 0x80; if (spurs_addr == 0) @@ -1910,7 +1947,7 @@ void spu_thread::cpu_task() } else { - const u32 arg = static_cast(group->args[index][1]); + const u32 arg = static_cast(group->args[group->threads_map[index]][1]); if (group->name.ends_with("CellSpursKernelGroup"sv) && vm::check_addr(arg)) { @@ -3183,7 +3220,7 @@ plain_access: bool spu_thread::do_dma_check(const spu_mfc_cmd& args) { - const u32 mask = utils::rol32(1, args.tag); + const u32 mask = std::rotl(1, args.tag); if (mfc_barrier & mask || (args.cmd & (MFC_BARRIER_MASK | MFC_FENCE_MASK) && mfc_fence & mask)) [[unlikely]] { @@ -3199,13 +3236,13 @@ bool spu_thread::do_dma_check(const spu_mfc_cmd& args) if ((mfc_queue[i].cmd & ~0xc) == MFC_BARRIER_CMD) { mfc_barrier |= -1; - mfc_fence |= utils::rol32(1, mfc_queue[i].tag); + mfc_fence |= std::rotl(1, mfc_queue[i].tag); continue; } if (true) { - const u32 _mask = utils::rol32(1u, mfc_queue[i].tag); + const u32 _mask = std::rotl(1u, mfc_queue[i].tag); // A command with barrier hard blocks that tag until it's been dealt with if (mfc_queue[i].cmd & MFC_BARRIER_MASK) @@ -3768,14 +3805,14 @@ bool spu_thread::do_list_transfer(spu_mfc_cmd& args) { range_lock->release(0); - ch_stall_mask |= utils::rol32(1, args.tag); + ch_stall_mask |= std::rotl(1, args.tag); if (!ch_stall_stat.get_count()) { set_events(SPU_EVENT_SN); } - ch_stall_stat.set_value(utils::rol32(1, args.tag) | ch_stall_stat.get_value()); + ch_stall_stat.set_value(std::rotl(1, args.tag) | ch_stall_stat.get_value()); args.tag |= 0x80; // Set stalled status args.eal = ::narrow(reinterpret_cast(item_ptr) - this->ls); @@ -3834,7 +3871,8 @@ bool spu_thread::do_putllc(const spu_mfc_cmd& args) } // Writeback of unchanged data. Only check memory change - if (cmp_rdata(rdata, vm::_ref(addr)) && res.compare_and_swap_test(rtime, rtime + 128)) + // For the comparison, load twice for atomicity + if (cmp_rdata(rdata, vm::_ref(addr)) && res == rtime && cmp_rdata(rdata, vm::_ref(addr)) && res.compare_and_swap_test(rtime, rtime + 128)) { raddr = 0; // Disable notification return true; @@ -3843,6 +3881,11 @@ bool spu_thread::do_putllc(const spu_mfc_cmd& args) return false; } + static const auto cast_as = [](void* ptr, usz pos){ return reinterpret_cast(ptr) + pos; }; + static const auto cast_as_const = [](const void* ptr, usz pos){ return reinterpret_cast(ptr) + pos; }; + + const usz diff16_pos = scan16_rdata(to_write, rdata); + auto [_oldd, _ok] = res.fetch_op([&](u64& r) { if ((r & -128) != rtime || (r & 127)) @@ -3964,8 +4007,19 @@ bool spu_thread::do_putllc(const spu_mfc_cmd& args) if (cmp_rdata(rdata, super_data)) { - mov_rdata(super_data, to_write); - return true; + if (diff16_pos != umax) + { + // Do it with CMPXCHG16B if possible, this allows to improve accuracy whenever "RSX Accurate Reservations" is off + if (atomic_storage::compare_exchange(*cast_as(super_data, diff16_pos), *cast_as(rdata, diff16_pos), *cast_as_const(to_write, diff16_pos))) + { + return true; + } + } + else + { + mov_rdata(super_data, to_write); + return true; + } } return false; @@ -3979,7 +4033,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 +4044,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); } } @@ -4047,6 +4101,22 @@ void do_cell_atomic_128_store(u32 addr, const void* to_write) auto& sdata = *vm::get_super_ptr(addr); auto& res = *utils::bless>(vm::g_reservations + (addr & 0xff80) / 2); + if (std::memcmp(static_cast(to_write), &sdata, 16) == 0 && std::memcmp(static_cast(to_write) + 64, &sdata[64], 16) == 0) + { + const auto& write_data = *static_cast(to_write); + const u64 at_read_time = vm::reservation_acquire(addr); + + if (!(at_read_time & 127)) + { + if (cmp_rdata(sdata, write_data) && at_read_time == vm::reservation_acquire(addr) && cmp_rdata(sdata, write_data)) + { + // Write of the same data (verified atomically) + vm::try_reservation_update(addr); + return; + } + } + } + for (u64 j = 0;; j++) { auto [_oldd, _ok] = res.fetch_op([&](u128& r) @@ -4183,7 +4253,11 @@ 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); + + // 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) @@ -4197,7 +4271,7 @@ bool spu_thread::do_mfc(bool can_escape, bool must_finish) auto process_command = [&](spu_mfc_cmd& args) { // Select tag bit in the tag mask or the stall mask - const u32 mask = utils::rol32(1, args.tag); + const u32 mask = std::rotl(1, args.tag); if ((args.cmd & ~0xc) == MFC_BARRIER_CMD) { @@ -4636,7 +4710,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; } @@ -4898,10 +4972,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); } @@ -4948,9 +5022,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 @@ -4968,9 +5042,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); } } @@ -5299,7 +5373,7 @@ bool spu_thread::process_mfc_cmd() std::memcpy(dump.data, _ptr(ch_mfc_cmd.lsa & 0x3ff80), 128); } - const u32 mask = utils::rol32(1, ch_mfc_cmd.tag); + const u32 mask = std::rotl(1, ch_mfc_cmd.tag); if ((mfc_barrier | mfc_fence) & mask) [[unlikely]] { @@ -5354,11 +5428,11 @@ bool spu_thread::process_mfc_cmd() } mfc_queue[mfc_size++] = ch_mfc_cmd; - mfc_fence |= utils::rol32(1, ch_mfc_cmd.tag); + mfc_fence |= std::rotl(1, ch_mfc_cmd.tag); if (ch_mfc_cmd.cmd & MFC_BARRIER_MASK) { - mfc_barrier |= utils::rol32(1, ch_mfc_cmd.tag); + mfc_barrier |= std::rotl(1, ch_mfc_cmd.tag); } return true; @@ -5407,11 +5481,11 @@ bool spu_thread::process_mfc_cmd() } mfc_size++; - mfc_fence |= utils::rol32(1, cmd.tag); + mfc_fence |= std::rotl(1, cmd.tag); if (cmd.cmd & MFC_BARRIER_MASK) { - mfc_barrier |= utils::rol32(1, cmd.tag); + mfc_barrier |= std::rotl(1, cmd.tag); } if (check_mfc_interrupts(pc + 4)) @@ -5437,7 +5511,7 @@ bool spu_thread::process_mfc_cmd() { mfc_queue[mfc_size++] = ch_mfc_cmd; mfc_barrier |= -1; - mfc_fence |= utils::rol32(1, ch_mfc_cmd.tag); + mfc_fence |= std::rotl(1, ch_mfc_cmd.tag); } return true; @@ -5629,28 +5703,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; } } @@ -5665,7 +5757,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--; @@ -5673,8 +5765,6 @@ void spu_thread::deregister_cache_line_waiter(usz index) { x = 0; } - - return false; }); } @@ -6040,11 +6130,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 = [&]() @@ -6064,6 +6149,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]; @@ -6088,6 +6176,19 @@ 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 && 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; + } + } + + lv2_obj::notify_all(); } else { @@ -6108,18 +6209,22 @@ s64 spu_thread::get_ch_value(u32 ch) eventstat_busy_waiting_switch = value ? 1 : 0; } - + for (bool is_first = true; !events.count; events = get_events(mask1 & ~SPU_EVENT_LR, true, true), is_first = false) { const auto old = +state; 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; } @@ -6139,10 +6244,22 @@ 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_acquire(raddr) == rtime) { - vm::reservation_notifier_notify(raddr); + // Confirm change in data only, register address for busy waiting + 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); + } + + // 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) + { + vm::reservation_notifier_notify(raddr, rtime); } set_lr = true; @@ -6168,7 +6285,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; @@ -6230,8 +6347,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) { @@ -6258,11 +6374,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); @@ -6273,12 +6394,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); @@ -6319,8 +6444,16 @@ s64 spu_thread::get_ch_value(u32 ch) } else if (!cmp_rdata(_this->rdata, *_this->resrv_mem)) { + 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(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 - 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; } @@ -6341,27 +6474,28 @@ 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 } - else - { - busy_wait(); - } continue; } @@ -6369,11 +6503,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) { @@ -6383,6 +6525,10 @@ s64 spu_thread::get_ch_value(u32 ch) raddr = 0; } } + else if (!is_stopped(old)) + { + wakeup_delay(); + } check_state(); return events.events & mask1; @@ -6726,7 +6872,7 @@ bool spu_thread::set_ch_value(u32 ch, u32 value) value &= 0x1f; // Reset stall status for specified tag - const u32 tag_mask = utils::rol32(1, value); + const u32 tag_mask = std::rotl(1, value); if (ch_stall_mask & tag_mask) { @@ -6835,7 +6981,7 @@ bool spu_thread::set_ch_value(u32 ch, u32 value) fmt::throw_exception("Unknown/illegal channel in WRCH (ch=%d [%s], value=0x%x)", ch, ch < 128 ? spu_ch_name[ch] : "???", value); } -extern void resume_spu_thread_group_from_waiting(spu_thread& spu) +extern void resume_spu_thread_group_from_waiting(spu_thread& spu, std::array>, 8>& notify_spus) { const auto group = spu.group; @@ -6849,7 +6995,7 @@ extern void resume_spu_thread_group_from_waiting(spu_thread& spu) { group->run_state = SPU_THREAD_GROUP_STATUS_SUSPENDED; spu.state += cpu_flag::signal; - spu.state.notify_one(); + ensure(spu.state & cpu_flag::suspend); return; } @@ -6867,7 +7013,7 @@ extern void resume_spu_thread_group_from_waiting(spu_thread& spu) thread->state -= cpu_flag::suspend; } - thread->state.notify_one(); + notify_spus[group->threads_map[thread->index]] = thread; } } } @@ -7267,6 +7413,7 @@ bool spu_thread::stop_and_signal(u32 code) } u32 prev_resv = 0; + u64 prev_rtime = 0; for (auto& thread : group->threads) { @@ -7279,13 +7426,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; } } } @@ -7293,7 +7441,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/SPUThread.h b/rpcs3/Emu/Cell/SPUThread.h index 5a0d9bab31..36d24ff55e 100644 --- a/rpcs3/Emu/Cell/SPUThread.h +++ b/rpcs3/Emu/Cell/SPUThread.h @@ -768,7 +768,7 @@ public: std::shared_ptr shm; // SPU memory const std::add_pointer_t ls; // SPU LS pointer const u32 option; // sys_spu_thread_initialize option - const u32 lv2_id; // The actual id that is used by syscalls + u32 lv2_id; // The actual id that is used by syscalls u32 spurs_addr = 0; bool spurs_waited = false; bool spurs_entered_wait = false; @@ -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 8d4db2bc91..c405b98a2c 100644 --- a/rpcs3/Emu/Cell/lv2/lv2.cpp +++ b/rpcs3/Emu/Cell/lv2/lv2.cpp @@ -1336,27 +1336,27 @@ 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 (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, 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); } } } @@ -1392,24 +1392,22 @@ 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 (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 +2230,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,13 +2255,19 @@ 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++) + if (cpu->get_class() == thread_class::spu) + { + range_lock = static_cast(cpu)->range_lock; + } + + 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(); @@ -2300,6 +2294,7 @@ void lv2_obj::notify_all() noexcept }).second) { notifies[i] = raddr; + notifies_time[i] = vm::reservation_acquire(raddr); } } @@ -2328,21 +2323,24 @@ 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); + // 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_event.cpp b/rpcs3/Emu/Cell/lv2/sys_event.cpp index db72972b91..8b3de14bf7 100644 --- a/rpcs3/Emu/Cell/lv2/sys_event.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_event.cpp @@ -117,7 +117,7 @@ shared_ptr lv2_event_queue::find(u64 ipc_key) return g_fxo->get>().get(ipc_key); } -extern void resume_spu_thread_group_from_waiting(spu_thread& spu); +extern void resume_spu_thread_group_from_waiting(spu_thread& spu, std::array>, 8>& notify_spus); CellError lv2_event_queue::send(lv2_event event, bool* notified_thread, lv2_event_port* port) { @@ -126,6 +126,22 @@ CellError lv2_event_queue::send(lv2_event event, bool* notified_thread, lv2_even *notified_thread = false; } + struct notify_spus_t + { + std::array>, 8> spus; + + ~notify_spus_t() noexcept + { + for (auto& spu : spus) + { + if (spu && spu->state & cpu_flag::wait) + { + spu->state.notify_one(); + } + } + } + } notify_spus{}; + std::lock_guard lock(mutex); if (!exists) @@ -199,7 +215,7 @@ CellError lv2_event_queue::send(lv2_event event, bool* notified_thread, lv2_even const u32 data2 = static_cast(std::get<2>(event)); const u32 data3 = static_cast(std::get<3>(event)); spu.ch_in_mbox.set_values(4, CELL_OK, data1, data2, data3); - resume_spu_thread_group_from_waiting(spu); + resume_spu_thread_group_from_waiting(spu, notify_spus.spus); } return {}; @@ -260,6 +276,22 @@ error_code sys_event_queue_destroy(ppu_thread& ppu, u32 equeue_id, s32 mode) return CELL_EINVAL; } + struct notify_spus_t + { + std::array>, 8> spus; + + ~notify_spus_t() noexcept + { + for (auto& spu : spus) + { + if (spu && spu->state & cpu_flag::wait) + { + spu->state.notify_one(); + } + } + } + } notify_spus{}; + std::vector events; std::unique_lock qlock; @@ -357,7 +389,7 @@ error_code sys_event_queue_destroy(ppu_thread& ppu, u32 equeue_id, s32 mode) for (auto cpu = +queue->sq; cpu; cpu = cpu->next_cpu) { cpu->ch_in_mbox.set_values(1, CELL_ECANCELED); - resume_spu_thread_group_from_waiting(*cpu); + resume_spu_thread_group_from_waiting(*cpu, notify_spus.spus); } atomic_storage::release(queue->sq, nullptr); diff --git a/rpcs3/Emu/Cell/lv2/sys_memory.cpp b/rpcs3/Emu/Cell/lv2/sys_memory.cpp index 9f726e994d..fb8c5b51ac 100644 --- a/rpcs3/Emu/Cell/lv2/sys_memory.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_memory.cpp @@ -213,6 +213,8 @@ error_code sys_memory_allocate_from_container(cpu_thread& cpu, u64 size, u32 cid if (alloc_addr) { + sys_memory.notice("sys_memory_allocate_from_container(): Allocated 0x%x address (size=0x%x)", addr, size); + vm::lock_sudo(addr, static_cast(size)); cpu.check_state(); *alloc_addr = addr; 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_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 diff --git a/rpcs3/Emu/Cell/lv2/sys_ppu_thread.cpp b/rpcs3/Emu/Cell/lv2/sys_ppu_thread.cpp index 1aa9409f46..e2463ca101 100644 --- a/rpcs3/Emu/Cell/lv2/sys_ppu_thread.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_ppu_thread.cpp @@ -468,11 +468,11 @@ error_code sys_ppu_thread_restart(ppu_thread& ppu) return CELL_OK; } -error_code _sys_ppu_thread_create(ppu_thread& ppu, vm::ptr thread_id, vm::ptr param, u64 arg, u64 unk, s32 prio, u32 _stacksz, u64 flags, vm::cptr threadname) +error_code _sys_ppu_thread_create(ppu_thread& ppu, vm::ptr thread_id, vm::ptr param, u64 arg, u64 unk, s32 prio, u64 _stacksz, u64 flags, vm::cptr threadname) { ppu.state += cpu_flag::wait; - sys_ppu_thread.warning("_sys_ppu_thread_create(thread_id=*0x%x, param=*0x%x, arg=0x%llx, unk=0x%llx, prio=%d, stacksize=0x%x, flags=0x%llx, threadname=*0x%x)", + sys_ppu_thread.warning("_sys_ppu_thread_create(thread_id=*0x%x, param=*0x%x, arg=0x%llx, unk=0x%llx, prio=%d, stacksize=0x%llx, flags=0x%llx, threadname=*0x%x)", thread_id, param, arg, unk, prio, _stacksz, flags, threadname); // thread_id is checked for null in stub -> CELL_ENOMEM @@ -497,7 +497,8 @@ error_code _sys_ppu_thread_create(ppu_thread& ppu, vm::ptr thread_id, vm::p const u32 tls = param->tls; // Compute actual stack size and allocate - const u32 stack_size = utils::align(std::max(_stacksz, 4096), 4096); + // 0 and UINT64_MAX both convert to 4096 + const u64 stack_size = FN(x ? x : 4096)(utils::align(_stacksz, 4096)); auto& dct = g_fxo->get(); @@ -507,7 +508,7 @@ error_code _sys_ppu_thread_create(ppu_thread& ppu, vm::ptr thread_id, vm::p return {CELL_ENOMEM, dct.size - dct.used}; } - const vm::addr_t stack_base{vm::alloc(stack_size, vm::stack, 4096)}; + const vm::addr_t stack_base{vm::alloc(static_cast(stack_size), vm::stack, 4096)}; if (!stack_base) { @@ -532,7 +533,7 @@ error_code _sys_ppu_thread_create(ppu_thread& ppu, vm::ptr thread_id, vm::p { ppu_thread_params p; p.stack_addr = stack_base; - p.stack_size = stack_size; + p.stack_size = static_cast(stack_size); p.tls_addr = tls; p.entry = entry; p.arg0 = arg; diff --git a/rpcs3/Emu/Cell/lv2/sys_ppu_thread.h b/rpcs3/Emu/Cell/lv2/sys_ppu_thread.h index 81598288ea..2e3f8c524e 100644 --- a/rpcs3/Emu/Cell/lv2/sys_ppu_thread.h +++ b/rpcs3/Emu/Cell/lv2/sys_ppu_thread.h @@ -53,7 +53,7 @@ error_code sys_ppu_thread_get_priority(ppu_thread& ppu, u32 thread_id, vm::ptr sp); error_code sys_ppu_thread_stop(ppu_thread& ppu, u32 thread_id); error_code sys_ppu_thread_restart(ppu_thread& ppu); -error_code _sys_ppu_thread_create(ppu_thread& ppu, vm::ptr thread_id, vm::ptr param, u64 arg, u64 arg4, s32 prio, u32 stacksize, u64 flags, vm::cptr threadname); +error_code _sys_ppu_thread_create(ppu_thread& ppu, vm::ptr thread_id, vm::ptr param, u64 arg, u64 arg4, s32 prio, u64 stacksize, u64 flags, vm::cptr threadname); error_code sys_ppu_thread_start(ppu_thread& ppu, u32 thread_id); error_code sys_ppu_thread_rename(ppu_thread& ppu, u32 thread_id, vm::cptr name); error_code sys_ppu_thread_recover_page_fault(ppu_thread& ppu, u32 thread_id); diff --git a/rpcs3/Emu/Cell/lv2/sys_rsx.cpp b/rpcs3/Emu/Cell/lv2/sys_rsx.cpp index 0c912490bd..4ef4e04d85 100644 --- a/rpcs3/Emu/Cell/lv2/sys_rsx.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_rsx.cpp @@ -716,7 +716,7 @@ error_code sys_rsx_context_attribute(u32 context_id, u32 package_id, u64 a3, u64 //const u32 bank = (((a4 >> 32) & 0xFFFFFFFF) >> 4) & 0xF; const bool bound = ((a4 >> 32) & 0x3) != 0; - const auto range = utils::address_range::start_length(offset, size); + const auto range = utils::address_range32::start_length(offset, size); if (bound) { @@ -800,7 +800,7 @@ error_code sys_rsx_context_attribute(u32 context_id, u32 package_id, u64 a3, u64 if (bound) { - const auto cull_range = utils::address_range::start_length(cullStart, width * height); + const auto cull_range = utils::address_range32::start_length(cullStart, width * height); // cullStart is an offset inside ZCULL RAM which is 3MB long, check bounds // width and height are not allowed to be zero (checked by range.valid()) diff --git a/rpcs3/Emu/Cell/lv2/sys_spu.cpp b/rpcs3/Emu/Cell/lv2/sys_spu.cpp index 77fbcb8505..3665efcd0a 100644 --- a/rpcs3/Emu/Cell/lv2/sys_spu.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_spu.cpp @@ -767,7 +767,12 @@ error_code sys_spu_thread_initialize(ppu_thread& ppu, vm::ptr thread, u32 g } // Read thread name - const std::string thread_name(attr_data.name.get_ptr(), std::max(attr_data.name_len, 1) - 1); + std::string thread_name; + + if (attr_data.name_len && !vm::read_string(attr_data.name.addr(), attr_data.name_len - 1, thread_name, true)) + { + return { CELL_EFAULT, attr_data.name.addr() }; + } const auto group = idm::get_unlocked(group_id); @@ -776,8 +781,6 @@ error_code sys_spu_thread_initialize(ppu_thread& ppu, vm::ptr thread, u32 g return CELL_ESRCH; } - std::unique_lock lock(group->mutex); - if (auto state = +group->run_state; state != SPU_THREAD_GROUP_STATUS_NOT_INITIALIZED) { if (state == SPU_THREAD_GROUP_STATUS_DESTROYED) @@ -793,6 +796,34 @@ error_code sys_spu_thread_initialize(ppu_thread& ppu, vm::ptr thread, u32 g return CELL_EBUSY; } + const u32 inited_before_lock = group->init; + + u32 tid = (inited_before_lock << 24) | (group_id & 0xffffff); + + const auto spu_ptr = ensure(idm::make_ptr>(group.get(), spu_num, thread_name, tid, false, option)); + + std::unique_lock lock(group->mutex); + + if (auto state = +group->run_state; state != SPU_THREAD_GROUP_STATUS_NOT_INITIALIZED) + { + lock.unlock(); + idm::remove>(idm::last_id()); + + if (state == SPU_THREAD_GROUP_STATUS_DESTROYED) + { + return CELL_ESRCH; + } + + return CELL_EBUSY; + } + + if (group->threads_map[spu_num] != -1) + { + lock.unlock(); + idm::remove>(idm::last_id()); + return CELL_EBUSY; + } + if (option & SYS_SPU_THREAD_OPTION_ASYNC_INTR_ENABLE) { sys_spu.warning("Unimplemented SPU Thread options (0x%x)", option); @@ -800,15 +831,13 @@ error_code sys_spu_thread_initialize(ppu_thread& ppu, vm::ptr thread, u32 g const u32 inited = group->init; - const u32 tid = (inited << 24) | (group_id & 0xffffff); + tid = (inited << 24) | (group_id & 0xffffff); - ensure(idm::import>([&]() - { - const auto spu = stx::make_shared>(group.get(), spu_num, thread_name, tid, false, option); - group->threads[inited] = spu; - group->threads_map[spu_num] = static_cast(inited); - return spu; - })); + // Update lv2_id (potentially changed after locking) + spu_ptr->lv2_id = tid; + + group->threads[inited] = spu_ptr; + group->threads_map[spu_num] = static_cast(inited); // alloc_hidden indicates falloc to allocate page with no access rights in base memory auto& spu = group->threads[inited]; @@ -882,7 +911,7 @@ error_code sys_spu_thread_get_exit_status(ppu_thread& ppu, u32 id, vm::ptr return CELL_ESTAT; } -error_code sys_spu_thread_group_create(ppu_thread& ppu, vm::ptr id, u32 num, s32 prio, vm::ptr attr) +error_code sys_spu_thread_group_create(ppu_thread& ppu, vm::ptr id, u32 num, s32 prio, vm::ptr attr) { ppu.state += cpu_flag::wait; @@ -890,13 +919,32 @@ error_code sys_spu_thread_group_create(ppu_thread& ppu, vm::ptr id, u32 num const s32 min_prio = g_ps3_process_info.has_root_perm() ? 0 : 16; - const sys_spu_thread_group_attribute attr_data = *attr; + sys_spu_thread_group_attribute attr_data{}; + { + const reduced_sys_spu_thread_group_attribute attr_reduced = *attr; + attr_data.name = attr_reduced.name; + attr_data.nsize = attr_reduced.nsize; + attr_data.type = attr_reduced.type; + + // Read container-id member at offset 12 bytes conditionally (that's what LV2 does) + if (attr_data.type & SYS_SPU_THREAD_GROUP_TYPE_MEMORY_FROM_CONTAINER) + { + attr_data.ct = vm::unsafe_ptr_cast(attr)->ct; + } + } if (attr_data.nsize > 0x80 || !num) { return CELL_EINVAL; } + std::string group_name; + + if (attr_data.nsize && !vm::read_string(attr_data.name.addr(), attr_data.nsize - 1, group_name, true)) + { + return { CELL_EFAULT, attr_data.name.addr() }; + } + const s32 type = attr_data.type; bool use_scheduler = true; @@ -1051,7 +1099,7 @@ error_code sys_spu_thread_group_create(ppu_thread& ppu, vm::ptr id, u32 num return CELL_EBUSY; } - const auto group = idm::make_ptr(std::string(attr_data.name.get_ptr(), std::max(attr_data.nsize, 1) - 1), num, prio, type, ct, use_scheduler, mem_size); + const auto group = idm::make_ptr(std::move(group_name), num, prio, type, ct, use_scheduler, mem_size); if (!group) { @@ -1501,6 +1549,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 +1559,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_spu.h b/rpcs3/Emu/Cell/lv2/sys_spu.h index 06e21e067b..3a0012b760 100644 --- a/rpcs3/Emu/Cell/lv2/sys_spu.h +++ b/rpcs3/Emu/Cell/lv2/sys_spu.h @@ -82,11 +82,18 @@ enum spu_stop_syscall : u32 SYS_SPU_THREAD_STOP_SWITCH_SYSTEM_MODULE = 0x0120, }; -struct sys_spu_thread_group_attribute +struct reduced_sys_spu_thread_group_attribute { be_t nsize; // name length including NULL terminator vm::bcptr name; be_t type; +}; + +struct sys_spu_thread_group_attribute +{ + be_t nsize; + vm::bcptr name; + be_t type; be_t ct; // memory container id }; @@ -360,7 +367,7 @@ error_code _sys_spu_image_close(ppu_thread&, vm::ptr img); error_code _sys_spu_image_get_segments(ppu_thread&, vm::ptr img, vm::ptr segments, s32 nseg); error_code sys_spu_thread_initialize(ppu_thread&, vm::ptr thread, u32 group, u32 spu_num, vm::ptr, vm::ptr, vm::ptr); error_code sys_spu_thread_set_argument(ppu_thread&, u32 id, vm::ptr arg); -error_code sys_spu_thread_group_create(ppu_thread&, vm::ptr id, u32 num, s32 prio, vm::ptr attr); +error_code sys_spu_thread_group_create(ppu_thread&, vm::ptr id, u32 num, s32 prio, vm::ptr attr); error_code sys_spu_thread_group_destroy(ppu_thread&, u32 id); error_code sys_spu_thread_group_start(ppu_thread&, u32 id); error_code sys_spu_thread_group_suspend(ppu_thread&, u32 id); diff --git a/rpcs3/Emu/Cell/lv2/sys_ss.cpp b/rpcs3/Emu/Cell/lv2/sys_ss.cpp index 0315c3f152..2c4b1282fd 100644 --- a/rpcs3/Emu/Cell/lv2/sys_ss.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_ss.cpp @@ -210,8 +210,10 @@ error_code sys_ss_get_open_psid(vm::ptr psid) { sys_ss.notice("sys_ss_get_open_psid(psid=*0x%x)", psid); - psid->high = g_cfg.sys.console_psid_high; - psid->low = g_cfg.sys.console_psid_low; + const u128 configured_psid = g_cfg.sys.console_psid.get(); + + psid->high = static_cast(configured_psid >> 64); + psid->low = static_cast(configured_psid); return CELL_OK; } @@ -259,8 +261,8 @@ error_code sys_ss_appliance_info_manager(u32 code, vm::ptr buffer) case 0x19005: { // AIM_get_open_ps_id - be_t psid[2] = { +g_cfg.sys.console_psid_high, +g_cfg.sys.console_psid_low }; - std::memcpy(buffer.get_ptr(), psid, 16); + const be_t psid = g_cfg.sys.console_psid.get(); + std::memcpy(buffer.get_ptr(), &psid, 16); break; } case 0x19006: @@ -268,7 +270,11 @@ error_code sys_ss_appliance_info_manager(u32 code, vm::ptr buffer) // qa values (dex only) ?? [[fallthrough]]; } - default: sys_ss.todo("sys_ss_appliance_info_manager(code=0x%x, buffer=*0x%x)", code, buffer); + default: + { + sys_ss.todo("sys_ss_appliance_info_manager(code=0x%x, buffer=*0x%x)", code, buffer); + break; + } } return CELL_OK; diff --git a/rpcs3/Emu/Cell/lv2/sys_sync.h b/rpcs3/Emu/Cell/lv2/sys_sync.h index bf99bcb056..0aff5e1e7a 100644 --- a/rpcs3/Emu/Cell/lv2/sys_sync.h +++ b/rpcs3/Emu/Cell/lv2/sys_sync.h @@ -483,6 +483,12 @@ public: } } + static void enqueue_on_top(const void* waiter) + { + g_to_notify[0] = waiter; + g_to_notify[1] = nullptr; + } + ~notify_all_t() noexcept { lv2_obj::notify_all(); diff --git a/rpcs3/Emu/Cell/lv2/sys_usbd.cpp b/rpcs3/Emu/Cell/lv2/sys_usbd.cpp index 0696481efa..fd5257b03a 100644 --- a/rpcs3/Emu/Cell/lv2/sys_usbd.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_usbd.cpp @@ -18,6 +18,7 @@ #include "Emu/Io/Skylander.h" #include "Emu/Io/Infinity.h" #include "Emu/Io/Dimensions.h" +#include "Emu/Io/KamenRider.h" #include "Emu/Io/GHLtar.h" #include "Emu/Io/ghltar_config.h" #include "Emu/Io/guncon3_config.h" @@ -175,7 +176,7 @@ private: {0x1430, 0x0150, 0x0150, "Skylanders Portal", &usb_device_skylander::get_num_emu_devices, &usb_device_skylander::make_instance}, {0x0E6F, 0x0129, 0x0129, "Disney Infinity Base", &usb_device_infinity::get_num_emu_devices, &usb_device_infinity::make_instance}, {0x0E6F, 0x0241, 0x0241, "Lego Dimensions Portal", &usb_device_dimensions::get_num_emu_devices, &usb_device_dimensions::make_instance}, - {0x0E6F, 0x200A, 0x200A, "Kamen Rider Summonride Portal", nullptr, nullptr}, + {0x0E6F, 0x200A, 0x200A, "Kamen Rider Summonride Portal", &usb_device_kamen_rider::get_num_emu_devices, &usb_device_kamen_rider::make_instance}, // Cameras // {0x1415, 0x0020, 0x2000, "Sony Playstation Eye", nullptr, nullptr}, // TODO: verifiy @@ -285,8 +286,10 @@ private: libusb_context* ctx = nullptr; +#ifndef _WIN32 #if LIBUSB_API_VERSION >= 0x01000102 libusb_hotplug_callback_handle callback_handle {}; +#endif #endif bool hotplug_supported = false; @@ -302,6 +305,7 @@ void LIBUSB_CALL callback_transfer(struct libusb_transfer* transfer) usbh.transfer_complete(transfer); } +#ifndef _WIN32 #if LIBUSB_API_VERSION >= 0x01000102 static int LIBUSB_CALL hotplug_callback(libusb_context* /*ctx*/, libusb_device * /*dev*/, libusb_hotplug_event event, void * /*user_data*/) { @@ -309,6 +313,7 @@ static int LIBUSB_CALL hotplug_callback(libusb_context* /*ctx*/, libusb_device * return 0; } #endif +#endif #if LIBUSB_API_VERSION >= 0x0100010A static void LIBUSB_CALL log_cb(libusb_context* /*ctx*/, enum libusb_log_level level, const char* str) @@ -597,9 +602,11 @@ usb_handler_thread::~usb_handler_thread() libusb_free_transfer(transfers[index].transfer); } +#ifndef _WIN32 #if LIBUSB_API_VERSION >= 0x01000102 if (ctx && hotplug_supported) libusb_hotplug_deregister_callback(ctx, callback_handle); +#endif #endif if (ctx) diff --git a/rpcs3/Emu/Io/Buzz.cpp b/rpcs3/Emu/Io/Buzz.cpp index 7c7890d41e..840220b967 100644 --- a/rpcs3/Emu/Io/Buzz.cpp +++ b/rpcs3/Emu/Io/Buzz.cpp @@ -173,7 +173,7 @@ void usb_device_buzz::interrupt_transfer(u32 buf_size, u8* buf, u32 /*endpoint*/ { const auto& pad = pads[i]; - if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (!pad->is_connected() || pad->is_copilot()) { continue; } diff --git a/rpcs3/Emu/Io/Dimensions.cpp b/rpcs3/Emu/Io/Dimensions.cpp index 158f310bdb..e80a64c0c9 100644 --- a/rpcs3/Emu/Io/Dimensions.cpp +++ b/rpcs3/Emu/Io/Dimensions.cpp @@ -544,9 +544,7 @@ std::optional> dimensions_toypad::pop_added_removed_response( std::lock_guard lock(m_dimensions_mutex); if (m_figure_added_removed_responses.empty()) - { return std::nullopt; - } std::array response = m_figure_added_removed_responses.front(); m_figure_added_removed_responses.pop(); @@ -597,7 +595,6 @@ void usb_device_dimensions::interrupt_transfer(u32 buf_size, u8* buf, u32 endpoi { // Read Endpoint, if a request has not been sent via the write endpoint, set expected result as // EHCI_CC_HALTED so the game doesn't report the Toypad as being disconnected. - std::lock_guard lock(m_query_mutex); std::optional> response = g_dimensionstoypad.pop_added_removed_response(); if (response) { @@ -696,7 +693,6 @@ void usb_device_dimensions::interrupt_transfer(u32 buf_size, u8* buf, u32 endpoi break; } } - std::lock_guard lock(m_query_mutex); m_queries.push(q_result); break; } diff --git a/rpcs3/Emu/Io/Dimensions.h b/rpcs3/Emu/Io/Dimensions.h index e4886c11f1..e2bfbd1e7f 100644 --- a/rpcs3/Emu/Io/Dimensions.h +++ b/rpcs3/Emu/Io/Dimensions.h @@ -79,6 +79,5 @@ public: void isochronous_transfer(UsbTransfer* transfer) override; protected: - shared_mutex m_query_mutex; std::queue> m_queries; }; diff --git a/rpcs3/Emu/Io/GHLtar.cpp b/rpcs3/Emu/Io/GHLtar.cpp index e520e53c7f..0c99c6aabf 100644 --- a/rpcs3/Emu/Io/GHLtar.cpp +++ b/rpcs3/Emu/Io/GHLtar.cpp @@ -152,7 +152,7 @@ void usb_device_ghltar::interrupt_transfer(u32 buf_size, u8* buf, u32 /*endpoint const auto handler = pad::get_pad_thread(); const auto& pad = ::at32(handler->GetPads(), m_controller_index); - if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (!pad->is_connected() || pad->is_copilot()) { return; } diff --git a/rpcs3/Emu/Io/GameTablet.cpp b/rpcs3/Emu/Io/GameTablet.cpp index 31ed81f3af..4c2330d908 100644 --- a/rpcs3/Emu/Io/GameTablet.cpp +++ b/rpcs3/Emu/Io/GameTablet.cpp @@ -198,9 +198,9 @@ void usb_device_gametablet::interrupt_transfer(u32 buf_size, u8* buf, u32 /*endp const auto gamepad_handler = pad::get_pad_thread(); const auto& pads = gamepad_handler->GetPads(); const auto& pad = ::at32(pads, m_controller_index); - if (pad->m_port_status & CELL_PAD_STATUS_CONNECTED) + if (pad->is_connected() && !pad->is_copilot()) { - for (Button& button : pad->m_buttons) + for (Button& button : pad->m_buttons_external) { if (!button.m_pressed) { diff --git a/rpcs3/Emu/Io/GunCon3.cpp b/rpcs3/Emu/Io/GunCon3.cpp index d229693907..522369d8a4 100644 --- a/rpcs3/Emu/Io/GunCon3.cpp +++ b/rpcs3/Emu/Io/GunCon3.cpp @@ -258,7 +258,7 @@ void usb_device_guncon3::interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, const auto gamepad_handler = pad::get_pad_thread(); const auto& pads = gamepad_handler->GetPads(); const auto& pad = ::at32(pads, m_controller_index); - if (pad->m_port_status & CELL_PAD_STATUS_CONNECTED) + if (pad->is_connected() && !pad->is_copilot()) { cfg->handle_input(pad, true, input_callback); } diff --git a/rpcs3/Emu/Io/Infinity.cpp b/rpcs3/Emu/Io/Infinity.cpp index ff4ce9e85b..b35879207d 100644 --- a/rpcs3/Emu/Io/Infinity.cpp +++ b/rpcs3/Emu/Io/Infinity.cpp @@ -258,13 +258,13 @@ void infinity_base::get_figure_identifier(u8 fig_num, u8 sequence, std::array> infinity_base::pop_added_removed_response() { - return !m_figure_added_removed_responses.empty(); -} + std::lock_guard lock(infinity_mutex); + + if (m_figure_added_removed_responses.empty()) + return std::nullopt; -std::array infinity_base::pop_added_removed_response() -{ std::array response = m_figure_added_removed_responses.front(); m_figure_added_removed_responses.pop(); return response; @@ -399,9 +399,10 @@ void usb_device_infinity::interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint { // Respond after FF command transfer->expected_time = get_timestamp() + 1000; - if (g_infinitybase.has_figure_been_added_removed()) + std::optional> response = g_infinitybase.pop_added_removed_response(); + if (response) { - memcpy(buf, g_infinitybase.pop_added_removed_response().data(), 0x20); + memcpy(buf, response.value().data(), 0x20); } else if (!m_queries.empty()) { diff --git a/rpcs3/Emu/Io/Infinity.h b/rpcs3/Emu/Io/Infinity.h index 2bfcff245f..c8bb4c6e38 100644 --- a/rpcs3/Emu/Io/Infinity.h +++ b/rpcs3/Emu/Io/Infinity.h @@ -3,6 +3,7 @@ #include "Emu/Io/usb_device.h" #include "Utilities/mutex.h" #include +#include #include struct infinity_figure @@ -24,8 +25,7 @@ public: void query_block(u8 fig_num, u8 block, std::array& reply_buf, u8 sequence); void write_block(u8 fig_num, u8 block, const u8* to_write_buf, std::array& reply_buf, u8 sequence); void get_figure_identifier(u8 fig_num, u8 sequence, std::array& reply_buf); - bool has_figure_been_added_removed() const; - std::array pop_added_removed_response(); + std::optional> pop_added_removed_response(); bool remove_figure(u8 position); u32 load_figure(const std::array& buf, fs::file in_file, u8 position); diff --git a/rpcs3/Emu/Io/KamenRider.cpp b/rpcs3/Emu/Io/KamenRider.cpp new file mode 100644 index 0000000000..aaa4836f08 --- /dev/null +++ b/rpcs3/Emu/Io/KamenRider.cpp @@ -0,0 +1,291 @@ +#include "stdafx.h" +#include "KamenRider.h" + +LOG_CHANNEL(kamen_rider_log, "kamen_rider"); + +rider_gate g_ridergate; + +void kamen_rider_figure::save() +{ + if (!kamen_file) + { + kamen_rider_log.error("Tried to save kamen rider figure to file but no kamen rider figure is active!"); + return; + } + kamen_file.seek(0, fs::seek_set); + kamen_file.write(data.data(), 0x14 * 0x10); +} + +u8 rider_gate::generate_checksum(const std::array& data, u32 num_of_bytes) const +{ + ensure(num_of_bytes <= data.size()); + int checksum = 0; + for (u32 i = 0; i < num_of_bytes; i++) + { + checksum += data[i]; + } + return (checksum & 0xFF); +} + +kamen_rider_figure& rider_gate::get_figure_by_uid(const std::array uid) +{ + for (kamen_rider_figure& figure : figures) + { + if (figure.uid == uid) + { + return figure; + } + } + return figures[7]; +} + +void rider_gate::get_blank_response(u8 command, u8 sequence, std::array& reply_buf) +{ + reply_buf = {0x55, 0x02, command, sequence}; + reply_buf[4] = generate_checksum(reply_buf, 4); +} + +void rider_gate::wake_rider_gate(std::array& reply_buf, u8 command, u8 sequence) +{ + std::lock_guard lock(kamen_mutex); + + m_is_awake = true; + reply_buf = {0x55, 0x1a, command, sequence, 0x00, 0x07, 0x00, 0x03, 0x02, + 0x09, 0x20, 0x03, 0xf5, 0x00, 0x19, 0x42, 0x52, 0xb7, + 0xb9, 0xa1, 0xae, 0x2b, 0x88, 0x42, 0x05, 0xfe, 0xe0, 0x1c, 0xac}; +} + +void rider_gate::get_list_tags(std::array& reply_buf, u8 command, u8 sequence) +{ + std::lock_guard lock(kamen_mutex); + + reply_buf = {0x55, 0x02, command, sequence}; + u8 index = 4; + for (const kamen_rider_figure& figure : figures) + { + if (figure.present) + { + reply_buf[index] = 0x09; + memcpy(&reply_buf[index + 1], figure.data.data(), 7); + index += 8; + reply_buf[1] += 8; + } + } + reply_buf[index] = generate_checksum(reply_buf, index); +} + +void rider_gate::query_block(std::array& reply_buf, u8 command, u8 sequence, const u8* uid, u8 sector, u8 block) +{ + std::lock_guard lock(kamen_mutex); + + reply_buf = {0x55, 0x13, command, sequence, 0x00}; + + const std::array uid_array = {uid[0], uid[1], uid[2], uid[3], uid[4], uid[5], uid[6]}; + + const kamen_rider_figure& figure = get_figure_by_uid(uid_array); + if (figure.present) + { + if (sector < 5 && block < 4) + { + memcpy(&reply_buf[5], &figure.data[(sector * 4 * 16) + (block * 16)], 16); + } + } + reply_buf[21] = generate_checksum(reply_buf, 21); +} + +void rider_gate::write_block(std::array& replyBuf, u8 command, u8 sequence, const u8* uid, u8 sector, u8 block, const u8* to_write_buf) +{ + std::lock_guard lock(kamen_mutex); + + const std::array uid_array = {uid[0], uid[1], uid[2], uid[3], uid[4], uid[5], uid[6]}; + + kamen_rider_figure& figure = get_figure_by_uid(uid_array); + if (figure.present) + { + if (sector < 5 && block < 4) + { + memcpy(&figure.data[(sector * 4 * 16) + (block * 16)], to_write_buf, 16); + } + } + + get_blank_response(command, sequence, replyBuf); +} + +std::optional> rider_gate::pop_added_removed_response() +{ + std::lock_guard lock(kamen_mutex); + + if (m_figure_added_removed_responses.empty()) + { + return std::nullopt; + } + + std::array response = m_figure_added_removed_responses.front(); + m_figure_added_removed_responses.pop(); + return response; +} + +bool rider_gate::remove_figure(u8 index) +{ + std::lock_guard lock(kamen_mutex); + + auto& figure = figures[index]; + + if (figure.present) + { + figure.present = false; + figure.save(); + figure.kamen_file.close(); + if (m_is_awake) + { + std::array figure_removed_response = {0x56, 0x09, 0x09, 0x00}; + memcpy(&figure_removed_response[4], figure.uid.data(), figure.uid.size()); + figure_removed_response[11] = generate_checksum(figure_removed_response, 11); + m_figure_added_removed_responses.push(std::move(figure_removed_response)); + } + figure.uid = {}; + return true; + } + + return false; +} + +u8 rider_gate::load_figure(const std::array& buf, fs::file in_file) +{ + std::lock_guard lock(kamen_mutex); + + u8 found_slot = 0xFF; + + // mimics spot retaining on the portal + for (auto i = 0; i < 7; i++) + { + if (!figures[i].present) + { + if (i < found_slot) + { + found_slot = i; + } + } + } + + if (found_slot != 0xFF) + { + auto& figure = figures[found_slot]; + memcpy(figure.data.data(), buf.data(), buf.size()); + figure.kamen_file = std::move(in_file); + figure.uid = {buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]}; + figure.present = true; + + if (m_is_awake) + { + std::array figure_added_response = {0x56, 0x09, 0x09, 0x01}; + memcpy(&figure_added_response[4], figure.uid.data(), figure.uid.size()); + figure_added_response[11] = generate_checksum(figure_added_response, 11); + m_figure_added_removed_responses.push(std::move(figure_added_response)); + } + } + return found_slot; +} + +usb_device_kamen_rider::usb_device_kamen_rider(const std::array& location) + : usb_device_emulated(location) +{ + device = UsbDescriptorNode(USB_DESCRIPTOR_DEVICE, UsbDeviceDescriptor{0x200, 0x0, 0x0, 0x0, 0x40, 0x0E6F, 0x200A, 0x100, 0x1, 0x2, 0x3, 0x1}); + auto& config0 = device.add_node(UsbDescriptorNode(USB_DESCRIPTOR_CONFIG, UsbDeviceConfiguration{0x29, 0x1, 0x1, 0x0, 0x80, 0xFA})); + config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_INTERFACE, UsbDeviceInterface{0x0, 0x0, 0x2, 0x3, 0x0, 0x0, 0x0})); + config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_ENDPOINT, UsbDeviceEndpoint{0x81, 0x3, 0x40, 0x1})); + config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_ENDPOINT, UsbDeviceEndpoint{0x1, 0x3, 0x40, 0x1})); +} + +usb_device_kamen_rider::~usb_device_kamen_rider() +{ +} + +std::shared_ptr usb_device_kamen_rider::make_instance(u32, const std::array& location) +{ + return std::make_shared(location); +} + +u16 usb_device_kamen_rider::get_num_emu_devices() +{ + return 1; +} + +void usb_device_kamen_rider::control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer) +{ + usb_device_emulated::control_transfer(bmRequestType, bRequest, wValue, wIndex, wLength, buf_size, buf, transfer); +} + +void usb_device_kamen_rider::interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, UsbTransfer* transfer) +{ + ensure(buf_size == 0x40); + + transfer->fake = true; + transfer->expected_count = buf_size; + transfer->expected_result = HC_CC_NOERR; + + if (endpoint == 0x81) + { + // Respond after FF command + transfer->expected_time = get_timestamp() + 1000; + std::optional> response = g_ridergate.pop_added_removed_response(); + if (response) + { + memcpy(buf, response.value().data(), 0x40); + } + else if (!m_queries.empty()) + { + memcpy(buf, m_queries.front().data(), 0x20); + m_queries.pop(); + } + else + { + transfer->expected_count = 0; + transfer->expected_result = EHCI_CC_HALTED; + } + } + else if (endpoint == 0x01) + { + const u8 command = buf[2]; + const u8 sequence = buf[3]; + + std::array q_result{}; + + switch (command) + { + case 0xB0: // Wake + { + g_ridergate.wake_rider_gate(q_result, command, sequence); + break; + } + case 0xC0: + case 0xC3: // Color Commands + { + g_ridergate.get_blank_response(command, sequence, q_result); + break; + } + case 0xD0: // Tag List + { + // Return list of figure UIDs, separated by an 09 + g_ridergate.get_list_tags(q_result, command, sequence); + break; + } + case 0xD2: // Read + { + // Read 16 bytes from figure with UID buf[4] - buf[10] + g_ridergate.query_block(q_result, command, sequence, &buf[4], buf[11], buf[12]); + break; + } + case 0xD3: + { + // Write 16 bytes to figure with UID buf[4] - buf[10] + g_ridergate.write_block(q_result, command, sequence, &buf[4], buf[11], buf[12], &buf[13]); + break; + } + default: + kamen_rider_log.error("Unhandled Query Type: 0x%02X", command); + break; + } + m_queries.push(std::move(q_result)); + } +} diff --git a/rpcs3/Emu/Io/KamenRider.h b/rpcs3/Emu/Io/KamenRider.h new file mode 100644 index 0000000000..0e30024b06 --- /dev/null +++ b/rpcs3/Emu/Io/KamenRider.h @@ -0,0 +1,60 @@ +#pragma once + +#include "Emu/Io/usb_device.h" +#include "Utilities/mutex.h" +#include +#include +#include + +struct kamen_rider_figure +{ + fs::file kamen_file; + std::array data{}; + std::array uid{}; + bool present = false; + void save(); +}; + +class rider_gate +{ +public: + void get_blank_response(u8 command, u8 sequence, std::array& reply_buf); + void wake_rider_gate(std::array& replyBuf, u8 command, u8 sequence); + void get_list_tags(std::array& replyBuf, u8 command, u8 sequence); + void query_block(std::array& replyBuf, u8 command, u8 sequence, const u8* uid, u8 sector, u8 block); + void write_block(std::array& replyBuf, u8 command, u8 sequence, const u8* uid, u8 sector, u8 block, const u8* to_write_buf); + std::optional> pop_added_removed_response(); + + bool remove_figure(u8 position); + u8 load_figure(const std::array& buf, fs::file in_file); + +protected: + shared_mutex kamen_mutex; + std::array figures{}; + +private: + u8 generate_checksum(const std::array& data, u32 num_of_bytes) const; + kamen_rider_figure& get_figure_by_uid(const std::array uid); + + std::queue> m_figure_added_removed_responses; + + bool m_is_awake = false; +}; + +extern rider_gate g_ridergate; + +class usb_device_kamen_rider : public usb_device_emulated +{ +public: + usb_device_kamen_rider(const std::array& location); + ~usb_device_kamen_rider(); + + static std::shared_ptr make_instance(u32 controller_index, const std::array& location); + static u16 get_num_emu_devices(); + + void control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer) override; + void interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, UsbTransfer* transfer) override; + +protected: + std::queue> m_queries; +}; diff --git a/rpcs3/Emu/Io/MouseHandler.cpp b/rpcs3/Emu/Io/MouseHandler.cpp index c80d7556dc..9fd37463cd 100644 --- a/rpcs3/Emu/Io/MouseHandler.cpp +++ b/rpcs3/Emu/Io/MouseHandler.cpp @@ -71,6 +71,8 @@ void MouseHandlerBase::Button(u32 index, u8 button, bool pressed) MouseData new_data{}; new_data.update = CELL_MOUSE_DATA_UPDATE; new_data.buttons = mouse.buttons; + new_data.pixel_x = mouse.x_pos; + new_data.pixel_y = mouse.y_pos; datalist.push_back(std::move(new_data)); } @@ -100,6 +102,8 @@ void MouseHandlerBase::Scroll(u32 index, s8 x, s8 y) new_data.buttons = mouse.buttons; new_data.wheel = y; new_data.tilt = x; + new_data.pixel_x = mouse.x_pos; + new_data.pixel_y = mouse.y_pos; datalist.push_back(std::move(new_data)); } @@ -137,6 +141,8 @@ void MouseHandlerBase::Move(u32 index, s32 x_pos_new, s32 y_pos_new, s32 x_max, new_data.x_axis = static_cast(std::clamp(x_delta, -127, 128)); new_data.y_axis = static_cast(std::clamp(y_delta, -127, 128)); + new_data.pixel_x = x_pos_new; + new_data.pixel_y = y_pos_new; mouse.x_max = x_max; mouse.y_max = y_max; diff --git a/rpcs3/Emu/Io/MouseHandler.h b/rpcs3/Emu/Io/MouseHandler.h index 8a88241523..6a77f44d0e 100644 --- a/rpcs3/Emu/Io/MouseHandler.h +++ b/rpcs3/Emu/Io/MouseHandler.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include "Utilities/StrFmt.h" #include "Utilities/mutex.h" @@ -96,6 +96,9 @@ struct MouseData s8 y_axis = 0; s8 wheel = 0; s8 tilt = 0; + + s32 pixel_x = 0; + s32 pixel_y = 0; }; struct MouseTabletData @@ -104,8 +107,8 @@ struct MouseTabletData u8 data[MOUSE_MAX_CODES]{}; }; -using MouseTabletDataList = std::list; -using MouseDataList = std::list; +using MouseTabletDataList = std::deque; +using MouseDataList = std::deque; struct Mouse { diff --git a/rpcs3/Emu/Io/PadHandler.cpp b/rpcs3/Emu/Io/PadHandler.cpp index 19b9347da6..4fc8fa6376 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) { @@ -170,7 +170,7 @@ u16 PadHandlerBase::ConvertAxis(f32 value) // The DS3, (and i think xbox controllers) give a 'square-ish' type response, so that the corners will give (almost)max x/y instead of the ~30x30 from a perfect circle // using a simple scale/sensitivity increase would *work* although it eats a chunk of our usable range in exchange -// this might be the best for now, in practice it seems to push the corners to max of 20x20, with a squircle_factor of 8000 +// this might be the best for now, in practice it seems to push the corners to max of 20x20, with a squircle_factor of ~4000 // This function assumes inX and inY is already in 0-255 void PadHandlerBase::ConvertToSquirclePoint(u16& inX, u16& inY, u32 squircle_factor) { @@ -200,10 +200,29 @@ void PadHandlerBase::init_configs() { for (u32 i = 0; i < MAX_GAMEPADS; i++) { + // We need to restore the original defaults first. + m_pad_configs[i].restore_defaults(); + + // Set and apply actual defaults depending on pad handler init_config(&m_pad_configs[i]); } } +pad_capabilities PadHandlerBase::get_capabilities(const std::string& /*pad_id*/) +{ + return pad_capabilities + { + .has_led = b_has_rgb, + .has_mono_led = b_has_led, + .has_player_led = b_has_player_led, + .has_battery_led = b_has_battery_led, + .has_rumble = b_has_rumble, + .has_accel = b_has_motion, + .has_gyro = b_has_motion, + .has_pressure_sensitivity = b_has_pressure_intensity_button + }; +} + cfg_pad* PadHandlerBase::get_config(const std::string& pad_id) { int index = 0; @@ -327,12 +346,13 @@ PadHandlerBase::connection PadHandlerBase::get_next_button_press(const std::stri if (callback) { pad_preview_values preview_values = get_preview_values(data); + pad_capabilities capabilities = get_capabilities(pad_id); const u32 battery_level = get_battery_level(pad_id); if (pressed_button.value > 0) - callback(pressed_button.value, pressed_button.name, pad_id, battery_level, std::move(preview_values)); + callback(pressed_button.value, pressed_button.name, pad_id, battery_level, std::move(preview_values), std::move(capabilities)); else - callback(0, "", pad_id, battery_level, std::move(preview_values)); + callback(0, "", pad_id, battery_level, std::move(preview_values), std::move(capabilities)); } return status; @@ -538,8 +558,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_vibrate_motors[0] = VibrateMotor(true); + pad->m_vibrate_motors[1] = VibrateMotor(false); m_bindings.emplace_back(pad, pad_device, nullptr); @@ -759,9 +779,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.value = 0; + motor.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..e9fc166d93 100644 --- a/rpcs3/Emu/Io/PadHandler.h +++ b/rpcs3/Emu/Io/PadHandler.h @@ -81,8 +81,20 @@ struct pad_list_entry {} }; +struct pad_capabilities +{ + bool has_led = false; + bool has_mono_led = false; + bool has_player_led = false; + bool has_battery_led = false; + bool has_rumble = false; + bool has_accel = false; + bool has_gyro = false; + bool has_pressure_sensitivity = false; +}; + using pad_preview_values = std::array; -using pad_callback = std::function; +using pad_callback = std::function; using pad_fail_callback = std::function; using motion_preview_values = std::array; @@ -255,15 +267,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 +279,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 +305,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; @@ -325,6 +338,8 @@ public: bool has_analog_limiter_button() const { return b_has_analog_limiter_button; } bool has_orientation() const { return b_has_orientation; } + virtual pad_capabilities get_capabilities(const std::string& /*pad_id*/); + u16 NormalizeStickInput(u16 raw_value, s32 threshold, s32 multiplier, bool ignore_threshold = false) const; void convert_stick_values(u16& x_out, u16& y_out, s32 x_in, s32 y_in, u32 deadzone, u32 anti_deadzone, u32 padsquircling) const; void set_trigger_recognition_mode(trigger_recognition_mode mode) { m_trigger_recognition_mode = mode; } diff --git a/rpcs3/Emu/Io/TopShotElite.cpp b/rpcs3/Emu/Io/TopShotElite.cpp index 06cddab9f0..c241fe1ed4 100644 --- a/rpcs3/Emu/Io/TopShotElite.cpp +++ b/rpcs3/Emu/Io/TopShotElite.cpp @@ -318,7 +318,7 @@ void usb_device_topshotelite::interrupt_transfer(u32 buf_size, u8* buf, u32 /*en const auto gamepad_handler = pad::get_pad_thread(); const auto& pads = gamepad_handler->GetPads(); const auto& pad = ::at32(pads, m_controller_index); - if (pad->m_port_status & CELL_PAD_STATUS_CONNECTED) + if (pad->is_connected() && !pad->is_copilot()) { cfg->handle_input(pad, true, input_callback); } diff --git a/rpcs3/Emu/Io/TopShotFearmaster.cpp b/rpcs3/Emu/Io/TopShotFearmaster.cpp index 98b54700d9..eed7ade977 100644 --- a/rpcs3/Emu/Io/TopShotFearmaster.cpp +++ b/rpcs3/Emu/Io/TopShotFearmaster.cpp @@ -342,7 +342,7 @@ void usb_device_topshotfearmaster::interrupt_transfer(u32 buf_size, u8* buf, u32 const auto gamepad_handler = pad::get_pad_thread(); const auto& pads = gamepad_handler->GetPads(); const auto& pad = ::at32(pads, m_controller_index); - if (pad->m_port_status & CELL_PAD_STATUS_CONNECTED) + if (pad->is_connected() && !pad->is_copilot()) { cfg->handle_input(pad, true, input_callback); } diff --git a/rpcs3/Emu/Io/Turntable.cpp b/rpcs3/Emu/Io/Turntable.cpp index eca1926825..261af88d16 100644 --- a/rpcs3/Emu/Io/Turntable.cpp +++ b/rpcs3/Emu/Io/Turntable.cpp @@ -166,7 +166,7 @@ void usb_device_turntable::interrupt_transfer(u32 buf_size, u8* buf, u32 /*endpo const auto& pads = handler->GetPads(); const auto& pad = ::at32(pads, m_controller_index); - if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (!pad->is_connected() || pad->is_copilot()) return; const auto& cfg = ::at32(g_cfg_turntable.players, m_controller_index); diff --git a/rpcs3/Emu/Io/emulated_pad_config.h b/rpcs3/Emu/Io/emulated_pad_config.h index e2b46d3daa..b9bf68a223 100644 --- a/rpcs3/Emu/Io/emulated_pad_config.h +++ b/rpcs3/Emu/Io/emulated_pad_config.h @@ -90,10 +90,10 @@ public: void handle_input(std::shared_ptr pad, bool press_only, const std::function& func) const { - if (!pad) + if (!pad || pad->is_copilot()) return; - for (const Button& button : pad->m_buttons) + for (const Button& button : pad->m_buttons_external) { if (button.m_pressed || !press_only) { @@ -104,7 +104,7 @@ public: } } - for (const AnalogStick& stick : pad->m_sticks) + for (const AnalogStick& stick : pad->m_sticks_external) { if (handle_input(func, stick.m_offset, get_axis_keycode(stick.m_offset, stick.m_value), stick.m_value, true, true)) { diff --git a/rpcs3/Emu/Io/evdev_gun_handler.cpp b/rpcs3/Emu/Io/evdev_gun_handler.cpp index e5069203a8..a428964f9f 100644 --- a/rpcs3/Emu/Io/evdev_gun_handler.cpp +++ b/rpcs3/Emu/Io/evdev_gun_handler.cpp @@ -56,7 +56,7 @@ bool event_is_number(const char* s) } // compare /dev/input/eventX and /dev/input/eventY where X and Y are numbers -int event_strcmp_events(const char* x, const char* y) +bool event_cmp_less_than(const char* x, const char* y) { // find a common string int n = 0; @@ -71,14 +71,10 @@ int event_strcmp_events(const char* x, const char* y) const int a = atoi(x + n); const int b = atoi(y + n); - if (a == b) - return 0; - if (a < b) - return -1; - return 1; + return a < b; } - return strcmp(x, y); + return strcmp(x, y) < 0; } evdev_gun_handler::evdev_gun_handler() @@ -243,7 +239,7 @@ bool evdev_gun_handler::init() // Sort the udev entries by devnode name so that they are created in the proper order std::sort(sorted_devices.begin(), sorted_devices.end(), [](const event_udev_entry& a, const event_udev_entry& b) { - return event_strcmp_events(a.devnode, b.devnode); + return event_cmp_less_than(a.devnode, b.devnode); }); for (const event_udev_entry& entry : sorted_devices) diff --git a/rpcs3/Emu/Io/pad_config.cpp b/rpcs3/Emu/Io/pad_config.cpp index fb0b140aeb..614d972716 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,27 @@ 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_motor_speed(VibrateMotor& motor, f32 multiplier) const { - const u8 idx = 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)); + // 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(value, static_cast(vibration_threshold.get()), 255.0f, 0.0f, 255.0f); + + // Apply multiplier + motor.adjusted_value = static_cast(std::clamp(adjusted * multiplier, 0.0f, 255.0f)); + return motor.adjusted_value; } -u8 cfg_pad::get_small_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 ? 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)); + 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 +{ + 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 7c39a79411..fa695c4941 100644 --- a/rpcs3/Emu/Io/pad_config.h +++ b/rpcs3/Emu/Io/pad_config.h @@ -28,8 +28,9 @@ 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_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; cfg::string ls_left{ this, "Left Stick Left", "" }; cfg::string ls_down{ this, "Left Stick Down", "" }; @@ -87,8 +88,8 @@ struct cfg_pad final : cfg::node cfg::uint<0, 1000000> rstick_anti_deadzone{ this, "Right Stick Anti-Deadzone", 0 }; cfg::uint<0, 1000000> ltriggerthreshold{ this, "Left Trigger Threshold", 0 }; cfg::uint<0, 1000000> rtriggerthreshold{ this, "Right Trigger Threshold", 0 }; - cfg::uint<0, 1000000> lpadsquircling{ this, "Left Pad Squircling Factor", 8000 }; - cfg::uint<0, 1000000> rpadsquircling{ this, "Right Pad Squircling Factor", 8000 }; + cfg::uint<0, 1000000> lpadsquircling{ this, "Left Pad Squircling Factor", 4000 }; + cfg::uint<0, 1000000> rpadsquircling{ this, "Right Pad Squircling Factor", 4000 }; cfg::uint<0, 255> colorR{ this, "Color Value R", 0 }; cfg::uint<0, 255> colorG{ this, "Color Value G", 0 }; @@ -102,6 +103,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.cpp b/rpcs3/Emu/Io/pad_types.cpp index 428e0d6cea..9005bc7fe5 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); } } } @@ -222,6 +217,12 @@ bool Pad::get_analog_limiter_button_active(bool is_toggle_mode, u32 player_id) const Button& analog_limiter_button = m_buttons[m_analog_limiter_button_index]; + if (analog_limiter_button.m_key_codes.empty()) + { + // Active by default if no button was assigned + return true; + } + if (is_toggle_mode) { const bool pressed = analog_limiter_button.m_pressed; @@ -235,14 +236,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/Io/pad_types.h b/rpcs3/Emu/Io/pad_types.h index 710edb2743..8c669a9413 100644 --- a/rpcs3/Emu/Io/pad_types.h +++ b/rpcs3/Emu/Io/pad_types.h @@ -151,6 +151,13 @@ enum CELL_PAD_FAKE_TYPE_TOP_SHOT_ELITE = 0xa001, CELL_PAD_FAKE_TYPE_TOP_SHOT_FEARMASTER = 0xa002, CELL_PAD_FAKE_TYPE_GAMETABLET = 0xa003, + CELL_PAD_FAKE_TYPE_COPILOT_1 = 0xa004, + CELL_PAD_FAKE_TYPE_COPILOT_2 = 0xa005, + CELL_PAD_FAKE_TYPE_COPILOT_3 = 0xa006, + CELL_PAD_FAKE_TYPE_COPILOT_4 = 0xa007, + CELL_PAD_FAKE_TYPE_COPILOT_5 = 0xa008, + CELL_PAD_FAKE_TYPE_COPILOT_6 = 0xa009, + CELL_PAD_FAKE_TYPE_COPILOT_7 = 0xa00a, CELL_PAD_FAKE_TYPE_LAST, CELL_PAD_PCLASS_TYPE_MAX // last item @@ -337,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) @@ -372,20 +381,22 @@ enum special_button_value struct Button { u32 m_offset = 0; - std::set m_key_codes{}; u32 m_outKeyCode = 0; u16 m_value = 0; bool m_pressed = false; + std::set m_key_codes{}; + u16 m_actual_value = 0; // only used in keyboard_pad_handler bool m_analog = false; // only used in keyboard_pad_handler bool m_trigger = false; // only used in keyboard_pad_handler std::map m_pressed_keys{}; // only used in keyboard_pad_handler + Button(){} Button(u32 offset, std::set key_codes, u32 outKeyCode) : m_offset(offset) - , m_key_codes(std::move(key_codes)) , m_outKeyCode(outKeyCode) + , m_key_codes(std::move(key_codes)) { if (offset == CELL_PAD_BTN_OFFSET_DIGITAL1) { @@ -412,9 +423,10 @@ struct Button struct AnalogStick { u32 m_offset = 0; + u16 m_value = 128; + std::set m_key_codes_min{}; std::set m_key_codes_max{}; - u16 m_value = 128; std::map m_pressed_keys_min{}; // only used in keyboard_pad_handler std::map m_pressed_keys_max{}; // only used in keyboard_pad_handler @@ -447,23 +459,23 @@ struct AnalogSensor struct VibrateMotor { - bool m_is_large_motor = false; - u8 m_value = 0; + bool is_large_motor = false; + u8 value = 0; + u8 adjusted_value = 0; VibrateMotor() {} - VibrateMotor(bool is_large_motor, u8 value) - : m_is_large_motor(is_large_motor) - , m_value(value) + VibrateMotor(bool is_large_motor) + : is_large_motor(is_large_motor) {} }; struct ps_move_data { - bool external_device_connected = false; u32 external_device_id = 0; - std::array external_device_data{}; std::array external_device_read{}; // CELL_GEM_EXTERNAL_PORT_DEVICE_INFO_SIZE std::array external_device_write{}; // CELL_GEM_EXTERNAL_PORT_OUTPUT_SIZE + std::array external_device_data{}; + bool external_device_connected = false; bool external_device_read_requested = false; bool external_device_write_requested = false; @@ -534,36 +546,14 @@ struct Pad std::vector