diff --git a/.ci/build-linux.sh b/.ci/build-linux.sh index 2728f2faad..af43e6b0ab 100755 --- a/.ci/build-linux.sh +++ b/.ci/build-linux.sh @@ -1,14 +1,11 @@ #!/bin/sh -ex -# Setup Qt variables -export QT_BASE_DIR=/opt/qt"${QTVERMIN}" -export PATH="$QT_BASE_DIR"/bin:"$PATH" -export LD_LIBRARY_PATH="$QT_BASE_DIR"/lib/x86_64-linux-gnu:"$QT_BASE_DIR"/lib - if [ -z "$CIRRUS_CI" ]; then cd rpcs3 || exit 1 fi +git config --global --add safe.directory '*' + # Pull all the submodules except llvm # Note: Tried to use git submodule status, but it takes over 20 seconds # shellcheck disable=SC2046 @@ -45,6 +42,7 @@ cmake .. \ -DCMAKE_RANLIB="$RANLIB" \ -DUSE_SYSTEM_CURL=ON \ -DUSE_SDL=ON \ + -DUSE_SYSTEM_FFMPEG=OFF \ -DOpenGL_GL_PREFERENCE=LEGACY \ -DLLVM_DIR=/opt/llvm/lib/cmake/llvm \ -DSTATIC_LINK_LLVM=ON \ diff --git a/.ci/build-mac.sh b/.ci/build-mac.sh index 445df88ae2..f3c48781b7 100755 --- a/.ci/build-mac.sh +++ b/.ci/build-mac.sh @@ -6,11 +6,12 @@ brew install -f --overwrite nasm ninja git p7zip create-dmg ccache pipenv #/usr/sbin/softwareupdate --install-rosetta --agree-to-license 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 llvm@16 sdl2 glew cmake faudio vulkan-headers +arch -x86_64 /usr/local/bin/brew install --build-from-source ffmpeg gnutls freetype +arch -x86_64 /usr/local/bin/brew install -f --overwrite llvm@16 glew cmake sdl2 vulkan-headers coreutils arch -x86_64 /usr/local/bin/brew link -f llvm@16 -# moltenvk based on commit for 1.2.4 release -wget https://raw.githubusercontent.com/Homebrew/homebrew-core/b233d4f9f40f26d81da11140defbfd578cfe4a69/Formula/molten-vk.rb +# moltenvk based on commit for 1.2.5 release +wget https://raw.githubusercontent.com/Homebrew/homebrew-core/b0bba05b617ef0fd796b3727be46addfd098a491/Formula/m/molten-vk.rb arch -x86_64 /usr/local/bin/brew install -f --overwrite ./molten-vk.rb #export MACOSX_DEPLOYMENT_TARGET=12.0 export CXX=clang++ @@ -28,23 +29,24 @@ export WORKDIR; WORKDIR="$(pwd)" # Get Qt -if [ ! -d "/tmp/Qt/5.15.2" ]; then +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 cd "/tmp/Qt" "/opt/homebrew/bin/pipenv" run pip3 install py7zr requests semantic_version lxml - "/opt/homebrew/bin/pipenv" run "$WORKDIR/qt-downloader/qt-downloader" macos desktop 5.15.2 clang_64 --opensource + mkdir -p "$QT_VER/macos" ; ln -s "macos" "$QT_VER/clang_64" + "/opt/homebrew/bin/pipenv" run "$WORKDIR/qt-downloader/qt-downloader" macos desktop "$QT_VER" clang_64 --opensource --addons qtmultimedia fi cd "$WORKDIR" -ditto "/tmp/Qt/5.15.2" "qt-downloader/5.15.2" +ditto "/tmp/Qt/$QT_VER" "qt-downloader/$QT_VER" -export Qt5_DIR="$WORKDIR/qt-downloader/5.15.2/clang_64/lib/cmake/Qt5" +export Qt6_DIR="$WORKDIR/qt-downloader/$QT_VER/clang_64/lib/cmake/Qt$QT_VER_MAIN" export SDL2_DIR="$BREW_X64_PATH/opt/sdl2/lib/cmake/SDL2" -export PATH="$BREW_X64_PATH/opt/llvm@16/bin:$WORKDIR/qt-downloader/5.15.2/clang_64/bin:$BREW_BIN:$BREW_SBIN:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/Library/Apple/usr/bin:$PATH" +export PATH="$BREW_X64_PATH/opt/llvm@16/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 CPPFLAGS="-I$BREW_X64_PATH/include -msse -msse2 -mcx16 -no-pie" export LIBRARY_PATH="$BREW_X64_PATH/lib" @@ -57,9 +59,9 @@ export VK_ICD_FILENAMES="$VULKAN_SDK/share/vulkan/icd.d/MoltenVK_icd.json" export LLVM_DIR LLVM_DIR="BREW_X64_PATH/opt/llvm@16" -# exclude FAudio, SPIRV and LLVM, and sdl from submodule update +# exclude ffmpeg, SPIRV and LLVM, and sdl from submodule update # shellcheck disable=SC2046 -git submodule -q update --init --depth=1 --jobs=8 $(awk '/path/ && !/FAudio/ && !/llvm/ && !/SPIRV/ && !/SDL/ { print $3 }' .gitmodules) +git submodule -q update --init --depth=1 --jobs=8 $(awk '/path/ && !/ffmpeg/ && !/llvm/ && !/SPIRV/ && !/SDL/ { print $3 }' .gitmodules) # 3rdparty fixes sed -i '' "s/extern const double NSAppKitVersionNumber;/const double NSAppKitVersionNumber = 1343;/g" 3rdparty/hidapi/hidapi/mac/hid.c @@ -67,16 +69,31 @@ sed -i '' "s/extern const double NSAppKitVersionNumber;/const double NSAppKitVer mkdir build && cd build || exit 1 "$BREW_X64_PATH/bin/cmake" .. \ - -DUSE_SDL=ON -DUSE_DISCORD_RPC=ON -DUSE_VULKAN=ON -DUSE_ALSA=OFF -DUSE_PULSE=OFF -DUSE_AUDIOUNIT=ON \ - -DLLVM_CCACHE_BUILD=OFF -DLLVM_BUILD_RUNTIME=OFF -DLLVM_BUILD_TOOLS=OFF \ - -DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_TOOLS=OFF \ - -DLLVM_INCLUDE_UTILS=OFF -DLLVM_USE_PERF=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF \ + -DUSE_SDL=ON \ + -DUSE_DISCORD_RPC=ON \ + -DUSE_VULKAN=ON \ + -DUSE_ALSA=OFF \ + -DUSE_PULSE=OFF \ + -DUSE_AUDIOUNIT=ON \ + -DUSE_SYSTEM_FFMPEG=ON \ + -DLLVM_CCACHE_BUILD=OFF \ + -DLLVM_BUILD_RUNTIME=OFF \ + -DLLVM_BUILD_TOOLS=OFF \ + -DLLVM_INCLUDE_DOCS=OFF \ + -DLLVM_INCLUDE_EXAMPLES=OFF \ + -DLLVM_INCLUDE_TESTS=OFF \ + -DLLVM_INCLUDE_TOOLS=OFF \ + -DLLVM_INCLUDE_UTILS=OFF \ + -DLLVM_USE_PERF=OFF \ + -DLLVM_ENABLE_Z3_SOLVER=OFF \ -DUSE_NATIVE_INSTRUCTIONS=OFF \ -DUSE_SYSTEM_MVK=ON \ - -DUSE_SYSTEM_FAUDIO=ON \ + -DUSE_SYSTEM_FAUDIO=OFF \ -DUSE_SYSTEM_SDL=ON \ $CMAKE_EXTRA_OPTS \ - -DLLVM_TARGET_ARCH=X86_64 -DCMAKE_OSX_ARCHITECTURES=x86_64 -DCMAKE_IGNORE_PATH="$BREW_PATH/lib" \ + -DLLVM_TARGET_ARCH=X86_64 \ + -DCMAKE_OSX_ARCHITECTURES=x86_64 \ + -DCMAKE_IGNORE_PATH="$BREW_PATH/lib" \ -G Ninja "$BREW_PATH/bin/ninja"; build_status=$?; diff --git a/.ci/deploy-linux.sh b/.ci/deploy-linux.sh index fe50a81312..f43b0b021a 100755 --- a/.ci/deploy-linux.sh +++ b/.ci/deploy-linux.sh @@ -5,11 +5,11 @@ cd build || exit 1 if [ "$DEPLOY_APPIMAGE" = "true" ]; then DESTDIR=AppDir ninja install - curl -sL -o /usr/bin/linuxdeploy https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage + curl -fsSLo /usr/bin/linuxdeploy https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage chmod +x /usr/bin/linuxdeploy - curl -sL -o /usr/bin/linuxdeploy-plugin-qt https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage + curl -fsSLo /usr/bin/linuxdeploy-plugin-qt https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage chmod +x /usr/bin/linuxdeploy-plugin-qt - curl -sL -o linuxdeploy-plugin-checkrt.sh https://github.com/linuxdeploy/linuxdeploy-plugin-checkrt/releases/download/continuous/linuxdeploy-plugin-checkrt-x86_64.sh + curl -fsSLo linuxdeploy-plugin-checkrt.sh https://github.com/linuxdeploy/linuxdeploy-plugin-checkrt/releases/download/continuous/linuxdeploy-plugin-checkrt-x86_64.sh chmod +x ./linuxdeploy-plugin-checkrt.sh EXTRA_QT_PLUGINS="svg;" APPIMAGE_EXTRACT_AND_RUN=1 linuxdeploy --appdir AppDir --plugin qt diff --git a/.ci/deploy-mac.sh b/.ci/deploy-mac.sh index 08697c3c8b..286aed42f3 100755 --- a/.ci/deploy-mac.sh +++ b/.ci/deploy-mac.sh @@ -16,6 +16,8 @@ cd bin mkdir "rpcs3.app/Contents/lib/" cp "/usr/local/opt/llvm@16/lib/c++/libc++abi.1.0.dylib" "rpcs3.app/Contents/lib/libc++abi.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" rm -rf "rpcs3.app/Contents/Frameworks/QtPdf.framework" \ "rpcs3.app/Contents/Frameworks/QtQml.framework" \ @@ -45,6 +47,7 @@ DMG_FILEPATH="$BUILD_ARTIFACTSTAGINGDIRECTORY/rpcs3-v${COMM_TAG}-${COMM_COUNT}-$ --hide-extension Quickstart.url \ --app-drop-link 600 185 \ --skip-jenkins \ +--format ULMO \ "$DMG_FILEPATH" \ RPCS3.app diff --git a/.ci/deploy-windows.sh b/.ci/deploy-windows.sh index 48bd36661d..dd54ec4649 100755 --- a/.ci/deploy-windows.sh +++ b/.ci/deploy-windows.sh @@ -7,10 +7,13 @@ ARTIFACT_DIR="$BUILD_ARTIFACTSTAGINGDIRECTORY" rm -f ./bin/rpcs3.exp ./bin/rpcs3.lib ./bin/rpcs3.pdb ./bin/vc_redist.x64.exe rm -rf ./bin/git -# Prepare compatibility database for packaging, as well as +# Prepare compatibility and SDL database for packaging, as well as # certificate for ssl (auto-updater) -curl -sL 'https://rpcs3.net/compatibility?api=v1&export' | iconv -t UTF-8 > ./bin/GuiConfigs/compat_database.dat -curl -sL 'https://curl.haxx.se/ca/cacert.pem' > ./bin/cacert.pem +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 +curl -fsSL 'https://curl.haxx.se/ca/cacert.pem' 1> ./bin/cacert.pem # Package artifacts 7z a -m0=LZMA2 -mx9 "$BUILD" ./bin/* diff --git a/.ci/get_keys-windows.sh b/.ci/get_keys-windows.sh index a6201f54a3..9ef56dda62 100644 --- a/.ci/get_keys-windows.sh +++ b/.ci/get_keys-windows.sh @@ -1,4 +1,4 @@ #!/bin/sh -ex -curl -L -o "./llvm.lock" "https://github.com/RPCS3/llvm-mirror/releases/download/custom-build-win-16.0.1/llvmlibs_mt.7z.sha256" -curl -L -o "./glslang.lock" "https://github.com/RPCS3/glslang/releases/download/custom-build-win/glslanglibs_mt.7z.sha256" +curl -fLo "./llvm.lock" "https://github.com/RPCS3/llvm-mirror/releases/download/custom-build-win-16.0.1/llvmlibs_mt.7z.sha256" +curl -fLo "./glslang.lock" "https://github.com/RPCS3/glslang/releases/download/custom-build-win/glslanglibs_mt.7z.sha256" diff --git a/.ci/github-upload.sh b/.ci/github-upload.sh index 53d3d09895..8cc89314d5 100755 --- a/.ci/github-upload.sh +++ b/.ci/github-upload.sh @@ -16,7 +16,7 @@ generate_post_data() EOF } -curl -s \ +curl -fsS \ -H "Authorization: token ${RPCS3_TOKEN}" \ -H "Accept: application/vnd.github.v3+json" \ --data "$(generate_post_data)" "https://api.github.com/repos/$UPLOAD_REPO_FULL_NAME/releases" >> release.json @@ -28,7 +28,7 @@ echo "${id:?}" upload_file() { - curl -s \ + curl -fsS \ -H "Authorization: token ${RPCS3_TOKEN}" \ -H "Accept: application/vnd.github.v3+json" \ -H "Content-Type: application/octet-stream" \ diff --git a/.ci/install-freebsd.sh b/.ci/install-freebsd.sh index 2a63e5ddfc..0692efc267 100755 --- a/.ci/install-freebsd.sh +++ b/.ci/install-freebsd.sh @@ -2,7 +2,7 @@ # NOTE: this script is run under root permissions # shellcheck shell=sh disable=SC2096 -# RPCS3 often needs recent Qt5 and Vulkan-Headers +# RPCS3 often needs recent Qt and Vulkan-Headers sed -i '' 's/quarterly/latest/' /etc/pkg/FreeBSD.conf export ASSUME_ALWAYS_YES=true @@ -11,8 +11,8 @@ pkg info # debug # Prefer newer Clang than in base system (see also .ci/build-freebsd.sh) pkg install llvm16 -# Mandatory dependencies (qt5-dbus and qt5-gui are pulled via qt5-widgets) -pkg install git ccache cmake ninja qt5-qmake qt5-buildtools qt5-widgets qt5-concurrent qt5-multimedia qt5-svg glew openal-soft ffmpeg +# Mandatory dependencies (qt6-base and qt6-svg are pulled via qt6-multimedia) +pkg install git ccache cmake ninja qt6-multimedia glew openal-soft ffmpeg -# Optional dependencies (libevdev is pulled by qt5-gui) +# Optional dependencies (libevdev is pulled by qt6-base) pkg install pkgconf alsa-lib pulseaudio sdl2 evdev-proto vulkan-headers vulkan-loader diff --git a/.ci/setup-windows.sh b/.ci/setup-windows.sh index adbf1e332f..616dabbf31 100755 --- a/.ci/setup-windows.sh +++ b/.ci/setup-windows.sh @@ -11,25 +11,26 @@ PR_NUMBER="$SYSTEM_PULLREQUEST_PULLREQUESTID" QT_HOST="http://qt.mirror.constant.com/" 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${QT_VER_MAIN}.${QT_URL_VER}.win64_${QT_VER_MSVC}_64/${QT_VER}-0-${QT_DATE}" -QT_SUFFIX="-Windows-Windows_10-${QT_VER_MSVC_UP}-Windows-Windows_10-X86_64.7z" -QT_BASE_URL="${QT_HOST}${QT_PREFIX}qtbase${QT_SUFFIX}" -QT_WINE_URL="${QT_HOST}${QT_PREFIX}qtwinextras${QT_SUFFIX}" -QT_DECL_URL="${QT_HOST}${QT_PREFIX}qtdeclarative${QT_SUFFIX}" -QT_TOOL_URL="${QT_HOST}${QT_PREFIX}qttools${QT_SUFFIX}" -QT_MM_URL="${QT_HOST}${QT_PREFIX}qtmultimedia${QT_SUFFIX}" -QT_SVG_URL="${QT_HOST}${QT_PREFIX}qtsvg${QT_SUFFIX}" +QT_PREFIX="online/qtsdkrepository/windows_x86/desktop/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_10_22H2-${QT_VER_MSVC_UP}-Windows-Windows_10_22H2-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}" +QT_MM_URL="${QT_HOST}${QT_PREFIX}addons.qtmultimedia.${QT_PREFIX_2}qtmultimedia${QT_SUFFIX}" +QT_SVG_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qtsvg${QT_SUFFIX}" +QT_5CMP_URL="${QT_HOST}${QT_PREFIX}qt5compat.${QT_PREFIX_2}qt5compat${QT_SUFFIX}" LLVMLIBS_URL='https://github.com/RPCS3/llvm-mirror/releases/download/custom-build-win-16.0.1/llvmlibs_mt.7z' GLSLANG_URL='https://github.com/RPCS3/glslang/releases/download/custom-build-win/glslanglibs_mt.7z' VULKAN_SDK_URL="https://www.dropbox.com/s/cs77c3iv5mbo0bt/VulkanSDK-${VULKAN_VER}-Installer.exe" DEP_URLS=" \ $QT_BASE_URL \ - $QT_WINE_URL \ $QT_DECL_URL \ $QT_TOOL_URL \ $QT_MM_URL \ $QT_SVG_URL \ + $QT_5CMP_URL \ $LLVMLIBS_URL \ $GLSLANG_URL \ $VULKAN_SDK_URL" @@ -59,7 +60,7 @@ download_and_verify() fileName="$4" for _ in 1 2 3; do - [ -e "$CACHE_DIR/$fileName" ] || curl -L -o "$CACHE_DIR/$fileName" "$url" + [ -e "$CACHE_DIR/$fileName" ] || curl -fLo "$CACHE_DIR/$fileName" "$url" fileChecksum=$("${algo}sum" "$CACHE_DIR/$fileName" | awk '{ print $1 }') [ "$fileChecksum" = "$correctChecksum" ] && return 0 rm "$CACHE_DIR/$fileName" @@ -78,9 +79,9 @@ for url in $DEP_URLS; do # shellcheck disable=SC1003 case "$url" in - *qt*) checksum=$(curl -L "${url}.sha1"); algo="sha1"; outDir='C:\Qt\' ;; - *llvm*) checksum=$(curl -L "${url}.sha256"); algo="sha256"; outDir="./3rdparty/llvm" ;; - *glslang*) checksum=$(curl -L "${url}.sha256"); algo="sha256"; outDir="./lib/Release-x64" ;; + *qt*) checksum=$(curl -fL "${url}.sha1"); algo="sha1"; outDir='C:\Qt\' ;; + *llvm*) checksum=$(curl -fL "${url}.sha256"); algo="sha256"; outDir="./3rdparty/llvm" ;; + *glslang*) checksum=$(curl -fL "${url}.sha256"); algo="sha256"; outDir="./lib/Release-x64" ;; *Vulkan*) # Vulkan setup needs to be run in batch environment # Need to subshell this or else it doesn't wait diff --git a/.cirrus.yml b/.cirrus.yml index 8e8ff80d1f..9662ce54e9 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -6,103 +6,98 @@ env: BUILD_SOURCEVERSION: $CIRRUS_CHANGE_IN_REPO BUILD_SOURCEBRANCHNAME: $CIRRUS_BRANCH RPCS3_TOKEN: ENCRYPTED[!a4c3850e29ab150692286a74bec29819d25971a7ec431b86de2a35f7ed90c5b2ab3c93469f9298e30924d843599110e9!] + QT_VER_MAIN: '6' + QT_VER: '6.5.2' -windows_task: - matrix: - - name: Cirrus Windows - windows_container: - image: cirrusci/windowsservercore:visualstudio2019 - cpu: 8 - memory: 16G - env: - CIRRUS_SHELL: "bash" - COMPILER: msvc - QT_VER_MAIN: '5' - BUILD_ARTIFACTSTAGINGDIRECTORY: ${CIRRUS_WORKING_DIR}\artifacts\ - QT_VER: '5.15.2' - QT_VER_MSVC: 'msvc2019' - QT_DATE: '202011130602' - QTDIR: C:\Qt\${QT_VER}\${QT_VER_MSVC}_64 - VULKAN_VER: '1.3.224.1' - VULKAN_SDK_SHA: '2029e652e39ee6a6036cff3765da31e1e6c595fd2413d3cd111dfab7855621ea' - 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' -# - choco install -y python # Needed for SPIRV, use either this or make a new Docker image -# spirv_script: -# - export PATH=${PATH}:"C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\MSBuild\Current\Bin" -# - cd "${CIRRUS_WORKING_DIR}/3rdparty/SPIRV" -# - msbuild.exe spirv.vcxproj //p:Configuration=Release //m - 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; +# 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: 'msvc2019' +# QT_DATE: '202307080351' +# QTDIR: C:\Qt\${QT_VER}\${QT_VER_MSVC}_64 +# VULKAN_VER: '1.3.224.1' +# VULKAN_SDK_SHA: '2029e652e39ee6a6036cff3765da31e1e6c595fd2413d3cd111dfab7855621ea' +# 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-bionic:1.8 - 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) +# linux_task: +# container: +# image: rpcs3/rpcs3-ci-focal:1.1 +# 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}" +# export AVVER="${COMM_TAG}-${COMM_COUNT}" - .ci/github-upload.sh - fi; +# .ci/github-upload.sh +# fi; freebsd_task: matrix: diff --git a/.gitmodules b/.gitmodules index b9aa5ccf26..cf1e922050 100644 --- a/.gitmodules +++ b/.gitmodules @@ -87,3 +87,4 @@ [submodule "3rdparty/rtmidi/rtmidi"] path = 3rdparty/rtmidi/rtmidi url = ../../thestk/rtmidi + ignore = dirty diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt index 021a9f16ab..1fc11fb4d9 100644 --- a/3rdparty/CMakeLists.txt +++ b/3rdparty/CMakeLists.txt @@ -267,32 +267,17 @@ if(USE_SYSTEM_FFMPEG) target_link_libraries(3rdparty_ffmpeg INTERFACE ${FFMPEG_LIBRARIES}) else() if (NOT MSVC AND WIN32) - message(STATUS "RPCS3: building ffmpeg submodule") - - include(ProcessorCount) - ProcessorCount(N) - - ExternalProject_Add(ffmpeg-mingw - DOWNLOAD_COMMAND "" - SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg - BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/ffmpeg - CONFIGURE_COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg/configure --prefix=./windows/x86_64 --arch=x86_64 --disable-avdevice --disable-programs --disable-avfilter --disable-postproc --disable-doc --disable-pthreads --enable-w32threads --disable-network --disable-everything --disable-encoders --disable-muxers --disable-hwaccels --disable-parsers --disable-protocols --enable-dxva2 --enable-static --disable-shared --enable-decoder=aac --enable-decoder=aac_latm --enable-decoder=atrac3 --enable-decoder=atrac3p --enable-decoder=mp3 --enable-decoder=pcm_s16le --enable-decoder=pcm_s8 --enable-decoder=h264 --enable-decoder=mpeg4 --enable-decoder=mpeg2video --enable-decoder=mjpeg --enable-decoder=mjpegb --enable-encoder=pcm_s16le --enable-encoder=ffv1 --enable-encoder=mpeg4 --enable-parser=h264 --enable-parser=mpeg4video --enable-parser=mpegaudio --enable-parser=mpegvideo --enable-parser=mjpeg --enable-parser=aac --enable-parser=aac_latm --enable-muxer=avi --enable-demuxer=h264 --enable-demuxer=m4v --enable-demuxer=mp3 --enable-demuxer=mpegvideo --enable-demuxer=mpegps --enable-demuxer=mjpeg --enable-demuxer=avi --enable-demuxer=aac --enable-demuxer=pmp --enable-demuxer=oma --enable-demuxer=pcm_s16le --enable-demuxer=pcm_s8 --enable-demuxer=wav --enable-hwaccel=h264_dxva2 --enable-indev=dshow --enable-protocol=file - BUILD_COMMAND make -j ${N} - INSTALL_COMMAND make install - ) - - target_link_directories(3rdparty_ffmpeg INTERFACE "${CMAKE_CURRENT_BINARY_DIR}/ffmpeg/windows/x86_64/lib") - target_link_libraries(3rdparty_ffmpeg INTERFACE avformat avcodec avutil swscale swresample iconv) + message(FATAL_ERROR "-- RPCS3: building ffmpeg submodule is currently not supported") else() message(STATUS "RPCS3: using builtin ffmpeg") if (WIN32) - set(FFMPEG_LIB_DIR "ffmpeg/windows/x86_64") + set(FFMPEG_LIB_DIR "ffmpeg/lib/windows/x86_64") target_link_libraries(3rdparty_ffmpeg INTERFACE "Bcrypt.lib") elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") - set(FFMPEG_LIB_DIR "ffmpeg/linux/x86_64") + set(FFMPEG_LIB_DIR "ffmpeg/lib/linux/ubuntu-20.04/x86_64") elseif(APPLE) - set(FFMPEG_LIB_DIR "ffmpeg/macos/x86_64") + set(FFMPEG_LIB_DIR "ffmpeg/lib/macos/x86_64") else() message(FATAL_ERROR "Prebuilt ffmpeg is not available on this platform! Try USE_SYSTEM_FFMPEG=ON.") endif() diff --git a/3rdparty/FAudio b/3rdparty/FAudio index 58cf606b5f..29a7d3a726 160000 --- a/3rdparty/FAudio +++ b/3rdparty/FAudio @@ -1 +1 @@ -Subproject commit 58cf606b5f718883e5dffbafdec44859d4e304ec +Subproject commit 29a7d3a726383a3907baf4930d2c4d4da773b023 diff --git a/3rdparty/MoltenVK/CMakeLists.txt b/3rdparty/MoltenVK/CMakeLists.txt index ec0a9e2edf..1b799ae0b0 100644 --- a/3rdparty/MoltenVK/CMakeLists.txt +++ b/3rdparty/MoltenVK/CMakeLists.txt @@ -4,7 +4,7 @@ include(ExternalProject) ExternalProject_Add(moltenvk GIT_REPOSITORY https://github.com/KhronosGroup/MoltenVK.git - GIT_TAG 4c6bfbe + GIT_TAG 02a8c01 BUILD_IN_SOURCE 1 SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK CONFIGURE_COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK/fetchDependencies" --macos diff --git a/3rdparty/curl/curl b/3rdparty/curl/curl index 7ab9d43720..50490c0679 160000 --- a/3rdparty/curl/curl +++ b/3rdparty/curl/curl @@ -1 +1 @@ -Subproject commit 7ab9d43720bc34d9aa351c7ca683c1668ebf8335 +Subproject commit 50490c0679fcd0e50bb3a8fbf2d9244845652cf0 diff --git a/3rdparty/ffmpeg b/3rdparty/ffmpeg index bf019f8c88..4f8bcb1555 160000 --- a/3rdparty/ffmpeg +++ b/3rdparty/ffmpeg @@ -1 +1 @@ -Subproject commit bf019f8c88bc64638fccef62840e935ab2689a4a +Subproject commit 4f8bcb1555767522c2b21bfb08e5dcadad99ff62 diff --git a/3rdparty/libsdl-org/SDL b/3rdparty/libsdl-org/SDL index ffa78e6bea..031912c4b6 160000 --- a/3rdparty/libsdl-org/SDL +++ b/3rdparty/libsdl-org/SDL @@ -1 +1 @@ -Subproject commit ffa78e6bead23e2ba3adf8ec2367ff2218d4343c +Subproject commit 031912c4b6c5db80b443f04aa56fec3e4e645153 diff --git a/3rdparty/llvm/CMakeLists.txt b/3rdparty/llvm/CMakeLists.txt index 7194533a45..679cb23a61 100644 --- a/3rdparty/llvm/CMakeLists.txt +++ b/3rdparty/llvm/CMakeLists.txt @@ -14,8 +14,8 @@ if(WITH_LLVM) option(LLVM_INCLUDE_TESTS OFF) option(LLVM_INCLUDE_TOOLS OFF) option(LLVM_INCLUDE_UTILS OFF) - # we globally enable ccache - set(LLVM_CCACHE_BUILD OFF CACHE BOOL "Set to ON for a ccache enabled build") + option(LLVM_CCACHE_BUILD ON) + set(LLVM_ENABLE_WARNINGS OFF CACHE BOOL "Enable compiler warnings.") if(WIN32) set(LLVM_USE_INTEL_JITEVENTS ON) diff --git a/3rdparty/qt5.cmake b/3rdparty/qt5.cmake deleted file mode 100644 index d46e149ce4..0000000000 --- a/3rdparty/qt5.cmake +++ /dev/null @@ -1,45 +0,0 @@ -add_library(3rdparty_qt5 INTERFACE) - -set(QT_MIN_VER 5.15.2) - -find_package(Qt5 ${QT_MIN_VER} CONFIG COMPONENTS Widgets Concurrent Multimedia MultimediaWidgets Svg) -if(WIN32) - find_package(Qt5 ${QT_MIN_VER} COMPONENTS WinExtras REQUIRED) - target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::WinExtras Qt5::Concurrent Qt5::Multimedia Qt5::MultimediaWidgets Qt5::Svg) -else() - find_package(Qt5 ${QT_MIN_VER} COMPONENTS DBus Gui) - if(Qt5DBus_FOUND) - target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::DBus Qt5::Concurrent Qt5::Multimedia Qt5::MultimediaWidgets Qt5::Svg) - target_compile_definitions(3rdparty_qt5 INTERFACE -DHAVE_QTDBUS) - else() - target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::Concurrent Qt5::Multimedia Qt5::MultimediaWidgets Qt5::Svg) - endif() - target_include_directories(3rdparty_qt5 INTERFACE ${Qt5Gui_PRIVATE_INCLUDE_DIRS}) -endif() - -if(NOT Qt5Widgets_FOUND) - if(Qt5Widgets_VERSION VERSION_LESS ${QT_MIN_VER}) - message("Minimum supported Qt5 version is ${QT_MIN_VER}! You have version ${Qt5Widgets_VERSION} installed, please upgrade!") - if(CMAKE_SYSTEM_NAME STREQUAL "Linux") - message(FATAL_ERROR "Most distros do not provide an up-to-date version of Qt. -If you're on Ubuntu or Linux Mint, there are PPAs you can use to install one of the latest qt5 versions. -Find the correct ppa at https://launchpad.net/~beineri and follow the instructions.") - elseif(WIN32) - message(FATAL_ERROR "You can download the latest version of Qt5 here: https://www.qt.io/download-open-source/") - else() - message(FATAL_ERROR "Look online for instructions on installing an up-to-date Qt5 on ${CMAKE_SYSTEM}.") - endif() - endif() - - message("CMake was unable to find Qt5!") - 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 Qt5_DIR preprocessor definiton.") - elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") - message(FATAL_ERROR "Make sure to install your distro's qt5 package!") - else() - message(FATAL_ERROR "You need to have Qt5 installed, look online for instructions on installing Qt5 on ${CMAKE_SYSTEM}.") - endif() -endif() - -add_library(3rdparty::qt5 ALIAS 3rdparty_qt5) diff --git a/3rdparty/qt6.cmake b/3rdparty/qt6.cmake new file mode 100644 index 0000000000..7fce290470 --- /dev/null +++ b/3rdparty/qt6.cmake @@ -0,0 +1,44 @@ +add_library(3rdparty_qt6 INTERFACE) + +set(QT_MIN_VER 6.2.4) + +find_package(Qt6 ${QT_MIN_VER} CONFIG COMPONENTS Widgets Concurrent Multimedia MultimediaWidgets Svg SvgWidgets) +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) + 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}) +endif() + +if(Qt6Widgets_FOUND) + if(Qt6Widgets_VERSION VERSION_LESS ${QT_MIN_VER}) + message("Minimum supported Qt version is ${QT_MIN_VER}! You have version ${Qt6Widgets_VERSION} installed, please upgrade!") + if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + message(FATAL_ERROR "Most distros do not provide an up-to-date version of Qt. +If you're on Ubuntu or Linux Mint, there are PPAs you can use to install one of the latest qt6 versions. +Find the correct ppa at https://launchpad.net/~beineri and follow the instructions.") + elseif(WIN32) + message(FATAL_ERROR "You can download the latest version of Qt6 here: https://www.qt.io/download-open-source/") + else() + message(FATAL_ERROR "Look online for instructions on installing an up-to-date Qt6 on ${CMAKE_SYSTEM}.") + endif() + endif() +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.") + elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") + message(FATAL_ERROR "Make sure to install your distro's qt6 package!") + else() + message(FATAL_ERROR "You need to have Qt6 installed, look online for instructions on installing Qt6 on ${CMAKE_SYSTEM}.") + endif() +endif() + +add_library(3rdparty::qt6 ALIAS 3rdparty_qt6) diff --git a/3rdparty/rtmidi/rtmidi b/3rdparty/rtmidi/rtmidi index 84a99422a3..1e5b49925a 160000 --- a/3rdparty/rtmidi/rtmidi +++ b/3rdparty/rtmidi/rtmidi @@ -1 +1 @@ -Subproject commit 84a99422a3faf1ab417fe71c0903a48debb9376a +Subproject commit 1e5b49925aa60065db52de44c366d446a902547b diff --git a/3rdparty/xxHash b/3rdparty/xxHash index 35b0373c69..bbb27a5efb 160000 --- a/3rdparty/xxHash +++ b/3rdparty/xxHash @@ -1 +1 @@ -Subproject commit 35b0373c697b5f160d3db26b1cbb45a0d5ba788c +Subproject commit bbb27a5efb85b92a0486cf361a8635715a53f6ba diff --git a/3rdparty/zlib/zlib b/3rdparty/zlib/zlib index 04f42ceca4..09155eaa2f 160000 --- a/3rdparty/zlib/zlib +++ b/3rdparty/zlib/zlib @@ -1 +1 @@ -Subproject commit 04f42ceca40f73e2978b50e93806c2a18c1281fc +Subproject commit 09155eaa2f9270dc4ed1fa13e2b4b2613e6e4851 diff --git a/BUILDING.md b/BUILDING.md index e141ed189b..e816f4b2ac 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -9,11 +9,11 @@ Other instructions may be found [here](https://wiki.rpcs3.net/index.php?title=Bu * [CMake 3.16.9+](https://www.cmake.org/download/) (add to PATH) * [Python 3.6+](https://www.python.org/downloads/) (add to PATH) -* [Qt 5.15.2](https://www.qt.io/download-qt-installer) +* [Qt 6.5.2](https://www.qt.io/download-qt-installer) * [Visual Studio 2019](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community) -* [Vulkan SDK 1.3.224+](https://vulkan.lunarg.com/sdk/home) (See "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/windows/getting_started.html)) +* [Vulkan SDK 1.3.224](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.224. -**Either add the** `QTDIR` **environment variable, e.g.** `\5.15.2\msvc2019_64\` **, or use the [Visual Studio Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools2019)** +**Either add the** `QTDIR` **environment variable, e.g.** `\6.5.2\msvc2019_64\` **, or use the [Visual Studio Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools2019)** ### Linux @@ -21,15 +21,15 @@ These are the essentials tools to build RPCS3 on Linux. Some of them can be inst * Clang 12+ or GCC 11+ * [CMake 3.16.9+](https://www.cmake.org/download/) -* [Qt 5.15.2](https://www.qt.io/download-qt-installer) -* [Vulkan SDK 1.3.224+](https://vulkan.lunarg.com/sdk/home) (See "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/linux/getting_started.html)) +* [Qt 6.5.2](https://www.qt.io/download-qt-installer) +* [Vulkan SDK 1.3.224](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.224. * [SDL2](https://github.com/libsdl-org/SDL/releases) (for the FAudio backend) **If you have an NVIDIA GPU, you may need to install the libglvnd package.** #### Arch Linux - sudo pacman -S glew openal cmake vulkan-validation-layers qt5-base qt5-declarative qt5-multimedia sdl2 sndio jack2 base-devel + sudo pacman -S glew openal cmake vulkan-validation-layers qt6-base qt6-declarative qt6-multimedia sdl2 sndio jack2 base-devel #### Debian & Ubuntu @@ -38,14 +38,7 @@ These are the essentials tools to build RPCS3 on Linux. Some of them can be inst Ubuntu is usually horrendously out of date, and some packages need to be downloaded by hand. This part is for Qt, GCC, Vulkan, and CMake ##### Qt PPA -Ubuntu usually does not have a new enough Qt package to suit rpcs3's needs. There is a PPA available to work around this. Run the following: -``` -. /etc/os-release -sudo add-apt-repository ppa:beineri/opt-qt-5.15.2-$UBUNTU_CODENAME -sudo apt-get update -sudo apt-get install qt515base qt515svg -. /opt/qt515/bin/qt515-env.sh >/dev/null 2>&1 -``` +Ubuntu usually does not have a new enough Qt package to suit rpcs3's needs. There is currently no PPA available to work around this. ##### GCC 11.x installation @@ -64,7 +57,7 @@ For Ubuntu systems, it is strongly recommended to use the PPA from [LunarG](http ``` . /etc/os-release wget -qO - https://packages.lunarg.com/lunarg-signing-key-pub.asc | sudo apt-key add - -sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-1.2.198-$UBUNTU_CODENAME.list https://packages.lunarg.com/vulkan/1.2.198/lunarg-vulkan-1.2.198-$UBUNTU_CODENAME.list +sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-1.3.224-$UBUNTU_CODENAME.list https://packages.lunarg.com/vulkan/1.3.224/lunarg-vulkan-1.3.224-$UBUNTU_CODENAME.list sudo apt update sudo apt install vulkan-sdk ``` @@ -82,11 +75,11 @@ sudo apt-get install cmake #### Fedora - sudo dnf install alsa-lib-devel cmake glew glew-devel libatomic libevdev-devel libudev-devel openal-devel qt5-qtbase-devel qt5-qtbase-private-devel vulkan-devel pipewire-jack-audio-connection-kit-devel qt5-qtmultimedia-devel qt5-qtsvg-devel + sudo dnf install alsa-lib-devel cmake glew glew-devel libatomic libevdev-devel libudev-devel openal-devel qt6-qtbase-devel qt6-qtbase-private-devel vulkan-devel pipewire-jack-audio-connection-kit-devel qt6-qtmultimedia-devel qt6-qtsvg-devel #### OpenSUSE - sudo zypper install git cmake libasound2 libpulse-devel openal-soft-devel glew-devel zlib-devel libedit-devel vulkan-devel libudev-devel libqt5-qtbase-devel libqt5-qtmultimedia-devel libqt5-qtsvg-devel libQt5Gui-private-headers-devel libevdev-devel libsndio7_1 libjack-devel + sudo zypper install git cmake libasound2 libpulse-devel openal-soft-devel glew-devel zlib-devel libedit-devel vulkan-devel libudev-devel libqt6-qtbase-devel libqt6-qtmultimedia-devel libqt6-qtsvg-devel libQt6Gui-private-headers-devel libevdev-devel libsndio7_1 libjack-devel ## Setup the project @@ -103,7 +96,7 @@ git submodule update --init #### 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. `\5.15.2\msvc2019_64`, version will fill in automatically. +2) Add the path to your Qt installation with compiler e.g. `\6.5.2\msvc2019_64`, version will fill in automatically. 3) Go to `Extensions->Qt VS Tools->Options->Legacy Project Format`. 4) Set `Build: Run pre-build setup` to `true`. diff --git a/CMakeLists.txt b/CMakeLists.txt index 4c6eadc999..4aa13efff4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,7 @@ 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" OFF) +option(USE_SYSTEM_FFMPEG "Prefer system ffmpeg instead of the prebuild one" OFF) set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/buildfiles/cmake") @@ -104,18 +105,11 @@ if(CCACHE_FOUND) set(CMAKE_CXX_COMPILER_LAUNCHER ccache) endif() -if(WIN32) - add_compile_definitions(UNICODE) - add_compile_definitions(_WIN32_WINNT=0x0602) -endif() - if(APPLE AND CMAKE_OSX_ARCHITECTURES STREQUAL "arm64") include_directories(/opt/homebrew/include) link_directories(/opt/homebrew/lib) endif() -set(LLVM_ENABLE_WARNINGS OFF CACHE BOOL "Enable compiler warnings.") - if(MSVC) add_compile_options(/wd4530 /utf-8) # C++ exception handler used, but unwind semantics are not enabled endif() diff --git a/Utilities/Config.h b/Utilities/Config.h index 361100dd7f..ee22a20d96 100644 --- a/Utilities/Config.h +++ b/Utilities/Config.h @@ -6,6 +6,7 @@ #include "util/atomic.hpp" #include "util/shared_ptr.hpp" +#include #include #include #include diff --git a/Utilities/File.cpp b/Utilities/File.cpp index 4f0094d23d..4d1d9acd2a 100644 --- a/Utilities/File.cpp +++ b/Utilities/File.cpp @@ -1303,12 +1303,12 @@ fs::file::file(const std::string& path, bs_t mode) DWORD nwritten = 0; OVERLAPPED ovl{}; - const u64 pos = m_pos; + const u64 pos = m_pos.fetch_add(size); ovl.Offset = DWORD(pos); ovl.OffsetHigh = DWORD(pos >> 32); ensure(WriteFile(m_handle, data, size, &nwritten, &ovl)); // "file::write" + ensure(nwritten == size); nwritten_sum += nwritten; - m_pos += nwritten; if (nwritten < size) { diff --git a/Utilities/Thread.cpp b/Utilities/Thread.cpp index 6332b25d2f..cd2391ea43 100644 --- a/Utilities/Thread.cpp +++ b/Utilities/Thread.cpp @@ -1637,7 +1637,7 @@ bool handle_access_violation(u32 addr, bool is_writing, ucontext_t* context) noe if (!g_tls_access_violation_recovered) { vm_log.notice("\n%s", dump_useful_thread_info()); - vm_log.error("[%s] Access violation %s location 0x%x (%s)", is_writing ? "writing" : "reading", cpu->get_name(), addr, (is_writing && vm::check_addr(addr)) ? "read-only memory" : "unmapped memory"); + vm_log.error("[%s] Access violation %s location 0x%x (%s)", cpu->get_name(), is_writing ? "writing" : "reading", addr, (is_writing && vm::check_addr(addr)) ? "read-only memory" : "unmapped memory"); } // TODO: @@ -1663,6 +1663,11 @@ bool handle_access_violation(u32 addr, bool is_writing, ucontext_t* context) noe } } + if (cpu) + { + cpu->state += cpu_flag::wait; + } + Emu.Pause(true); if (!g_tls_access_violation_recovered) @@ -2010,37 +2015,8 @@ thread_local DECLARE(thread_ctrl::g_tls_error_callback) = nullptr; DECLARE(thread_ctrl::g_native_core_layout) { native_core_arrangement::undefined }; -static atomic_t s_thread_bits{0}; - -static atomic_t s_thread_pool[128]{}; - void thread_base::start() { - for (u128 bits = s_thread_bits.load(); bits; bits &= bits - 1) - { - const u32 pos = utils::ctz128(bits); - - if (!s_thread_pool[pos]) - { - continue; - } - - thread_base** tls = s_thread_pool[pos].exchange(nullptr); - - if (!tls) - { - continue; - } - - // Receive "that" native thread handle, sent "this" thread_base - const u64 _self = reinterpret_cast(atomic_storage::load(*tls)); - m_thread.release(_self); - ensure(_self != reinterpret_cast(this)); - atomic_storage::store(*tls, this); - s_thread_pool[pos].notify_one(); - return; - } - #ifdef _WIN32 m_thread = ::_beginthreadex(nullptr, 0, entry_point, this, CREATE_SUSPENDED, nullptr); ensure(m_thread); @@ -2203,14 +2179,14 @@ u64 thread_base::finalize(thread_state result_state) noexcept const u64 _self = m_thread; // Set result state (errored or finalized) - m_sync.fetch_op([&](u64& v) + m_sync.fetch_op([&](u32& v) { v &= -4; v |= static_cast(result_state); }); // Signal waiting threads - m_sync.notify_all(2); + m_sync.notify_all(); return _self; } @@ -2234,112 +2210,13 @@ thread_base::native_entry thread_base::finalize(u64 _self) noexcept return nullptr; } - // Try to add self to thread pool - set_name("..pool"); - - thread_ctrl::set_native_priority(0); - - thread_ctrl::set_thread_affinity_mask(0); - - std::fesetround(FE_TONEAREST); - - gv_unset_zeroing_denormals(); - - static constexpr u64 s_stop_bit = 0x8000'0000'0000'0000ull; - - static atomic_t s_pool_ctr = [] - { - std::atexit([] - { - s_pool_ctr |= s_stop_bit; - - while (/*u64 remains = */s_pool_ctr & ~s_stop_bit) - { - for (u32 i = 0; i < std::size(s_thread_pool); i++) - { - if (thread_base** ptls = s_thread_pool[i].exchange(nullptr)) - { - // Extract thread handle - const u64 _self = reinterpret_cast(*ptls); - - // Wake up a thread and make sure it's joined - s_thread_pool[i].notify_one(); - #ifdef _WIN32 - const HANDLE handle = reinterpret_cast(_self); - WaitForSingleObject(handle, INFINITE); - CloseHandle(handle); + _endthreadex(0); #else - pthread_join(reinterpret_cast(_self), nullptr); + pthread_exit(nullptr); #endif - } - } - } - }); - return 0; - }(); - - s_pool_ctr++; - - u32 pos = -1; - - while (true) - { - const auto [bits, ok] = s_thread_bits.fetch_op([](u128& bits) - { - if (~bits) [[likely]] - { - // Set lowest clear bit - bits |= bits + 1; - return true; - } - - return false; - }); - - if (ok) [[likely]] - { - pos = utils::ctz128(~bits); - break; - } - - s_thread_bits.wait(bits); - } - - const auto tls = &thread_ctrl::g_tls_this_thread; - s_thread_pool[pos] = tls; - - atomic_wait::list<2> list{}; - list.set<0>(s_pool_ctr, 0, s_stop_bit); - list.set<1>(s_thread_pool[pos], tls); - - while (s_thread_pool[pos] == tls || atomic_storage::load(*tls) == fake_self) - { - list.wait(); - - if (s_pool_ctr & s_stop_bit) - { - break; - } - } - - // Free thread pool slot - s_thread_bits.atomic_op([pos](u128& val) - { - val &= ~(u128(1) << pos); - }); - - s_thread_bits.notify_one(); - - if (--s_pool_ctr & s_stop_bit) - { - return nullptr; - } - - // Return new entry point - utils::prefetch_exec((*tls)->entry_point); - return (*tls)->entry_point; + return nullptr; } thread_base::native_entry thread_base::make_trampoline(u64(*entry)(thread_base* _base)) @@ -2396,8 +2273,18 @@ thread_state thread_ctrl::state() void thread_ctrl::wait_for(u64 usec, [[maybe_unused]] bool alert /* true */) { + if (!usec) + { + return; + } + auto _this = g_tls_this_thread; + if (!alert && usec > 50000) + { + usec = 50000; + } + #ifdef __linux__ static thread_local struct linux_timer_handle_t { @@ -2426,13 +2313,13 @@ void thread_ctrl::wait_for(u64 usec, [[maybe_unused]] bool alert /* true */) } } fd_timer; - if (!alert && usec > 0 && usec <= 1000 && fd_timer != -1) + if (!alert && fd_timer != -1) { struct itimerspec timeout; u64 missed; - timeout.it_value.tv_nsec = usec * 1'000ull; - timeout.it_value.tv_sec = 0; + timeout.it_value.tv_nsec = usec % 1'000'000 * 1'000ull; + timeout.it_value.tv_sec = usec / 1'000'000; timeout.it_interval.tv_sec = 0; timeout.it_interval.tv_nsec = 0; timerfd_settime(fd_timer, 0, &timeout, NULL); @@ -2442,15 +2329,27 @@ void thread_ctrl::wait_for(u64 usec, [[maybe_unused]] bool alert /* true */) } #endif - if (_this->m_sync.bit_test_reset(2) || _this->m_taskq) + if (alert) { - return; + if (_this->m_sync.bit_test_reset(2) || _this->m_taskq) + { + return; + } } // Wait for signal and thread state abort atomic_wait::list<2> list{}; - list.set<0>(_this->m_sync, 0, 4 + 1); - list.set<1>(_this->m_taskq, nullptr); + + if (alert) + { + list.set<0>(_this->m_sync, 0); + list.set<1>(utils::bless>(&_this->m_taskq)[1], 0); + } + else + { + list.set<0>(_this->m_dummy, 0); + } + list.wait(atomic_wait_timeout{usec <= 0xffff'ffff'ffff'ffff / 1000 ? usec * 1000 : 0xffff'ffff'ffff'ffff}); } @@ -2461,29 +2360,27 @@ void thread_ctrl::wait_for_accurate(u64 usec) return; } + if (usec > 50000) + { + fmt::throw_exception("thread_ctrl::wait_for_accurate: unsupported amount"); + } + +#ifdef __linux__ + return wait_for(usec, false); +#else using namespace std::chrono_literals; const auto until = std::chrono::steady_clock::now() + 1us * usec; while (true) { -#ifdef __linux__ - // NOTE: Assumption that timer initialization has succeeded - u64 host_min_quantum = usec <= 1000 ? 10 : 50; -#else // Host scheduler quantum for windows (worst case) - // NOTE: On ps3 this function has very high accuracy constexpr u64 host_min_quantum = 500; -#endif + if (usec >= host_min_quantum) { -#ifdef __linux__ - // Do not wait for the last quantum to avoid loss of accuracy - wait_for(usec - ((usec % host_min_quantum) + host_min_quantum), false); -#else // Wait on multiple of min quantum for large durations to avoid overloading low thread cpus wait_for(usec - (usec % host_min_quantum), false); -#endif } // TODO: Determine best value for yield delay else if (usec >= host_min_quantum / 2) @@ -2504,6 +2401,7 @@ void thread_ctrl::wait_for_accurate(u64 usec) usec = (until - current).count(); } +#endif } std::string thread_ctrl::get_name_cached() @@ -2570,7 +2468,7 @@ bool thread_base::join(bool dtor) const for (u64 i = 0; (m_sync & 3) <= 1; i++) { - m_sync.wait(0, 2, timeout); + m_sync.wait(m_sync & ~2, timeout); if (m_sync & 2) { @@ -2590,7 +2488,7 @@ void thread_base::notify() { // Set notification m_sync |= 4; - m_sync.notify_one(4); + m_sync.notify_all(); } u64 thread_base::get_native_id() const @@ -2627,7 +2525,7 @@ u64 thread_base::get_cycles() { cycles = static_cast(thread_time.tv_sec) * 1'000'000'000 + thread_time.tv_nsec; #endif - if (const u64 old_cycles = m_sync.fetch_op([&](u64& v){ v &= 7; v |= (cycles << 3); }) >> 3) + if (const u64 old_cycles = m_cycles.exchange(cycles)) { return cycles - old_cycles; } @@ -2637,7 +2535,7 @@ u64 thread_base::get_cycles() } else { - return m_sync >> 3; + return m_cycles; } } @@ -2690,8 +2588,8 @@ void thread_base::exec() } // Notify waiters - ptr->exec.release(nullptr); - ptr->exec.notify_all(); + ptr->done.release(1); + ptr->done.notify_all(); } if (ptr->next) diff --git a/Utilities/Thread.h b/Utilities/Thread.h index a64d64cb99..f85f94194e 100644 --- a/Utilities/Thread.h +++ b/Utilities/Thread.h @@ -100,17 +100,19 @@ class thread_future protected: atomic_t exec{}; + atomic_t done{0}; + public: // Get reference to the atomic variable for inspection and waiting for const auto& get_wait() const { - return exec; + return done; } // Wait (preset) void wait() const { - exec.wait(nullptr); + done.wait(0); } }; @@ -131,8 +133,13 @@ private: // Thread handle (platform-specific) atomic_t m_thread{0}; - // Thread state and cycles - atomic_t m_sync{0}; + // Thread cycles + atomic_t m_cycles{0}; + + atomic_t m_dummy{0}; + + // Thread state + atomic_t m_sync{0}; // Thread name atomic_ptr m_tname; @@ -284,16 +291,22 @@ public: } atomic_wait::list list{}; - list.template set(_this->m_sync, 0, 4 + 1); - list.template set(_this->m_taskq, nullptr); + list.template set(_this->m_sync, 0); + list.template set(_this->m_taskq); setter(list); list.wait(atomic_wait_timeout{usec <= 0xffff'ffff'ffff'ffff / 1000 ? usec * 1000 : 0xffff'ffff'ffff'ffff}); } - template + template static inline void wait_on(T& wait, U old, u64 usec = -1) { - wait_on_custom<1>([&](atomic_wait::list<3>& list){ list.set<0, Op>(wait, old); }, usec); + wait_on_custom<1>([&](atomic_wait::list<3>& list) { list.template set<0>(wait, old); }, usec); + } + + template + static inline void wait_on(T& wait) + { + wait_on_custom<1>([&](atomic_wait::list<3>& list) { list.template set<0>(wait); }); } // Exit. @@ -637,7 +650,7 @@ public: { bool notify_sync = false; - if (s >= thread_state::aborting && thread::m_sync.fetch_op([](u64& v){ return !(v & 3) && (v |= 1); }).second) + if (s >= thread_state::aborting && thread::m_sync.fetch_op([](u32& v) { return !(v & 3) && (v |= 1); }).second) { notify_sync = true; } @@ -650,7 +663,7 @@ public: if (notify_sync) { // Notify after context abortion has been made so all conditions for wake-up be satisfied by the time of notification - thread::m_sync.notify_one(1); + thread::m_sync.notify_all(); } if (s == thread_state::finished) diff --git a/Utilities/bin_patch.cpp b/Utilities/bin_patch.cpp index a7ea0a6c94..5d89beb79d 100644 --- a/Utilities/bin_patch.cpp +++ b/Utilities/bin_patch.cpp @@ -378,13 +378,21 @@ bool patch_engine::load(patch_map& patches_map, const std::string& path, std::st { for (const auto note : notes_node) { - if (note && note.IsScalar()) + if (note) { - info.notes += note.Scalar(); + if (note.IsScalar()) + { + info.notes += note.Scalar(); + } + else + { + append_log_message(log_messages, fmt::format("Error: Skipping sequenced Note (patch: %s, key: %s, location: %s, file: %s)", description, main_key, get_yaml_node_location(note), path), &patch_log.error); + is_valid = false; + } } else { - append_log_message(log_messages, fmt::format("Error: Skipping sequenced Note (patch: %s, key: %s, location: %s, file: %s)", description, main_key, get_yaml_node_location(note), path), &patch_log.error); + append_log_message(log_messages, fmt::format("Error: Skipping sequenced Note (patch: %s, key: %s, location: %s, file: %s)", description, main_key, get_yaml_node_location(notes_node), path), &patch_log.error); is_valid = false; } } @@ -726,7 +734,7 @@ bool patch_engine::add_patch_data(YAML::Node node, patch_info& info, u32 modifie break; default: { - get_yaml_node_value(addr_node, error_message); + [[maybe_unused]] const u32 offset = get_yaml_node_value(addr_node, error_message); if (!error_message.empty()) { error_message = fmt::format("Skipping patch data entry: [ %s, 0x%.8x, %s ] (key: %s, location: %s) Invalid patch offset '%s' (not a valid u32 or overflow)", @@ -1752,9 +1760,9 @@ bool patch_engine::save_patches(const patch_map& patches, const std::string& pat { out << serial << YAML::BeginSeq; - for (const auto& app_version : app_versions) + for (const auto& [app_version, patch_config] : app_versions) { - out << app_version.first; + out << app_version; } out << YAML::EndSeq; @@ -1891,7 +1899,7 @@ patch_engine::patch_map patch_engine::load_config() for (const auto pair : root) { - const auto& hash = pair.first.Scalar(); + const std::string& hash = pair.first.Scalar(); if (const auto yml_type = pair.second.Type(); yml_type != YAML::NodeType::Map) { @@ -1901,7 +1909,7 @@ patch_engine::patch_map patch_engine::load_config() for (const auto patch : pair.second) { - const auto& description = patch.first.Scalar(); + const std::string& description = patch.first.Scalar(); if (const auto yml_type = patch.second.Type(); yml_type != YAML::NodeType::Map) { @@ -1911,7 +1919,7 @@ patch_engine::patch_map patch_engine::load_config() for (const auto title_node : patch.second) { - const auto& title = title_node.first.Scalar(); + const std::string& title = title_node.first.Scalar(); if (const auto yml_type = title_node.second.Type(); yml_type != YAML::NodeType::Map) { @@ -1921,7 +1929,7 @@ patch_engine::patch_map patch_engine::load_config() for (const auto serial_node : title_node.second) { - const auto& serial = serial_node.first.Scalar(); + const std::string& serial = serial_node.first.Scalar(); if (const auto yml_type = serial_node.second.Type(); yml_type != YAML::NodeType::Map) { diff --git a/Utilities/cond.cpp b/Utilities/cond.cpp index 69eb2d47c6..af572d191e 100644 --- a/Utilities/cond.cpp +++ b/Utilities/cond.cpp @@ -9,7 +9,7 @@ void cond_variable::imp_wait(u32 _old, u64 _timeout) noexcept ensure(_old); // Wait with timeout - m_value.wait(_old, c_signal_mask, atomic_wait_timeout{_timeout > max_timeout ? umax : _timeout * 1000}); + m_value.wait(_old, atomic_wait_timeout{_timeout > max_timeout ? umax : _timeout * 1000}); // Cleanup m_value.atomic_op([](u32& value) @@ -47,10 +47,10 @@ void cond_variable::imp_wake(u32 _count) noexcept if (_count > 1 || ((_old + (c_signal_mask & (0 - c_signal_mask))) & c_signal_mask) == c_signal_mask) { // Resort to notify_all if signal count reached max - m_value.notify_all(c_signal_mask); + m_value.notify_all(); } else { - m_value.notify_one(c_signal_mask); + m_value.notify_one(); } } diff --git a/Utilities/git-version-gen.cmd b/Utilities/git-version-gen.cmd index a135055c59..7de2ebf28f 100644 --- a/Utilities/git-version-gen.cmd +++ b/Utilities/git-version-gen.cmd @@ -90,8 +90,7 @@ rem // Get commit count from (unshallowed) HEAD for /F %%I IN ('call %GIT% rev-list HEAD --count') do set COMMIT_COUNT=%%I rem // Check if the current build system sets the git branch and version. -rem // The name is misleading. This is also used for master builds. -if defined SYSTEM_PULLREQUEST_SOURCEBRANCH ( +if defined BUILD_SOURCEBRANCHNAME ( rem // This must be a CI build @@ -125,6 +124,7 @@ if defined SYSTEM_PULLREQUEST_SOURCEBRANCH ( for /F %%I IN ('call %GIT% rev-parse --short^=8 HEAD') do set GIT_VERSION=%COMMIT_COUNT%-%%I for /F %%I IN ('call %GIT% rev-parse --abbrev-ref HEAD') do set GIT_BRANCH=%%I + set GIT_BRANCH=%BUILD_SOURCEBRANCHNAME% ) else ( rem // This must be a pull request or a build from a fork. echo Assuming pull request build @@ -142,8 +142,8 @@ if defined SYSTEM_PULLREQUEST_SOURCEBRANCH ( ) ) else ( - rem // The name is misleading. This is also used for master builds. - echo SYSTEM_PULLREQUEST_SOURCEBRANCH undefined + echo BUILD_SOURCEBRANCHNAME undefined + echo Assuming local build rem // Make GIT_VERSION the last commit (shortened); Don't include commit count on non-release builds for /F %%I IN ('call %GIT% rev-parse --short^=8 HEAD') do set GIT_VERSION=%%I diff --git a/Utilities/lockless.h b/Utilities/lockless.h index 41b24fe739..f49544ea3d 100644 --- a/Utilities/lockless.h +++ b/Utilities/lockless.h @@ -2,6 +2,7 @@ #include "util/types.hpp" #include "util/atomic.hpp" +#include "util/bless.hpp" //! Simple unshrinkable array base for concurrent access. Only growths automatically. //! There is no way to know the current size. The smaller index is, the faster it's accessed. @@ -269,6 +270,30 @@ public: return {}; } + const T& operator[](usz index) const noexcept + { + lf_queue_iterator result = begin(); + + while (--index != umax) + { + result++; + } + + return *result; + } + + T& operator[](usz index) noexcept + { + lf_queue_iterator result = begin(); + + while (--index != umax) + { + result++; + } + + return *result; + } + lf_queue_slice& pop_front() { delete std::exchange(m_head, std::exchange(m_head->m_link, nullptr)); @@ -280,12 +305,17 @@ public: template class lf_queue final { - atomic_t*> m_head{nullptr}; + atomic_t m_head{0}; + + lf_queue_item* load(u64 value) const noexcept + { + return reinterpret_cast*>(value >> 16); + } // Extract all elements and reverse element order (FILO to FIFO) lf_queue_item* reverse() noexcept { - if (auto* head = m_head.load() ? m_head.exchange(nullptr) : nullptr) + if (auto* head = load(m_head) ? load(m_head.exchange(0)) : nullptr) { if (auto* prev = head->m_link) { @@ -309,45 +339,61 @@ class lf_queue final public: constexpr lf_queue() = default; - ~lf_queue() + lf_queue(lf_queue&& other) noexcept { - delete m_head.load(); + m_head.release(other.m_head.exchange(0)); + } + + lf_queue& operator=(lf_queue&& other) noexcept + { + if (this == std::addressof(other)) + { + return *this; + } + + delete load(m_head); + m_head.release(other.m_head.exchange(0)); + return *this; + } + + ~lf_queue() + { + delete load(m_head); } - template void wait(std::nullptr_t /*null*/ = nullptr) noexcept { - if (m_head == nullptr) + if (m_head == 0) { - m_head.template wait(nullptr); + utils::bless>(&m_head)[1].wait(0); } } const volatile void* observe() const noexcept { - return m_head.load(); + return load(m_head); } explicit operator bool() const noexcept { - return m_head != nullptr; + return m_head != 0; } template void push(Args&&... args) { - auto _old = m_head.load(); - auto item = new lf_queue_item(_old, std::forward(args)...); + auto oldv = m_head.load(); + auto item = new lf_queue_item(load(oldv), std::forward(args)...); - while (!m_head.compare_exchange(_old, item)) + while (!m_head.compare_exchange(oldv, reinterpret_cast(item) << 16)) { - item->m_link = _old; + item->m_link = load(oldv); } - if (!_old) + if (!oldv) { // Notify only if queue was empty - m_head.notify_one(); + utils::bless>(&m_head)[1].notify_one(); } } @@ -363,7 +409,7 @@ public: lf_queue_slice pop_all_reversed() { lf_queue_slice result; - result.m_head = m_head.exchange(nullptr); + result.m_head = load(m_head.exchange(0)); return result; } diff --git a/Utilities/mutex.cpp b/Utilities/mutex.cpp index e84113d607..0cbd0df30e 100644 --- a/Utilities/mutex.cpp +++ b/Utilities/mutex.cpp @@ -74,14 +74,14 @@ void shared_mutex::imp_wait() break; } - m_value.wait(old, c_sig); + m_value.wait(old); } } void shared_mutex::imp_signal() { m_value += c_sig; - m_value.notify_one(c_sig); + m_value.notify_one(); } void shared_mutex::imp_lock(u32 val) diff --git a/Utilities/sync.h b/Utilities/sync.h index df2481108e..7264ba9248 100644 --- a/Utilities/sync.h +++ b/Utilities/sync.h @@ -38,7 +38,28 @@ constexpr NTSTATUS NTSTATUS_ALERTED = 0x101; constexpr NTSTATUS NTSTATUS_TIMEOUT = 0x102; #endif -#ifndef __linux__ +#ifdef __linux__ +#ifndef SYS_futex_waitv +#if defined(ARCH_X64) || defined(ARCH_ARM64) +#define SYS_futex_waitv 449 +#endif +#endif + +#ifndef FUTEX_32 +#define FUTEX_32 2 +#endif + +#ifndef FUTEX_WAITV_MAX +#define FUTEX_WAITV_MAX 128 +struct futex_waitv +{ + __u64 val; + __u64 uaddr; + __u32 flags; + __u32 __reserved; +}; +#endif +#else enum { FUTEX_PRIVATE_FLAG = 0, @@ -113,7 +134,7 @@ inline int futex(volatile void* uaddr, int futex_op, uint val, const timespec* t } else { - // TODO + // TODO: absolute timeout } map.erase(std::find(map.find(uaddr), map.end(), ref)); diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 67d18fe449..f19a4e927f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -5,7 +5,10 @@ trigger: tags: exclude: - '*' -pr: none +pr: + branches: + include: + - master jobs: - job: Linux_Build strategy: @@ -35,13 +38,13 @@ jobs: displayName: ccache - bash: | - docker pull --quiet rpcs3/rpcs3-ci-bionic:1.3 + docker pull --quiet rpcs3/rpcs3-ci-focal:1.1 docker run \ -v $(pwd):/rpcs3 \ --env-file .ci/docker.env \ -v $CCACHE_DIR:/root/.ccache \ -v $BUILD_ARTIFACTSTAGINGDIRECTORY:/root/artifacts \ - rpcs3/rpcs3-ci-bionic:1.3 \ + rpcs3/rpcs3-ci-focal:1.1 \ /rpcs3/.ci/build-linux.sh displayName: Docker setup and build @@ -49,26 +52,26 @@ jobs: 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) + - 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}" + 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'], 'gcc')) - # displayName: Push build to GitHub - # env: - # RPCS3_TOKEN: $(RPCS3-Token) + .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'], 'gcc')) + displayName: Push build to GitHub + env: + RPCS3_TOKEN: $(RPCS3-Token) - job: Windows_Build variables: COMPILER: msvc - QT_VER_MAIN: '5' - QT_VER: '5.15.2' + QT_VER_MAIN: '6' + QT_VER: '6.5.2' QT_VER_MSVC: 'msvc2019' - QT_DATE: '202011130602' + QT_DATE: '202307080351' QTDIR: C:\Qt\$(QT_VER)\$(QT_VER_MSVC)_64 VULKAN_VER: '1.3.224.1' VULKAN_SDK_SHA: '2029e652e39ee6a6036cff3765da31e1e6c595fd2413d3cd111dfab7855621ea' @@ -98,14 +101,6 @@ jobs: - bash: .ci/export-azure-vars.sh displayName: Export Variables - - task: MSBuild@1 - inputs: - solution: './3rdparty/SPIRV/spirv.vcxproj' - maximumCpuCount: true - platform: x64 - configuration: 'Release' - displayName: Compile SPIRV-Tools - - task: VSBuild@1 inputs: solution: 'rpcs3.sln' @@ -121,8 +116,8 @@ jobs: 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) + - 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) diff --git a/bin/GuiConfigs/Classic (Bright).qss b/bin/GuiConfigs/Classic (Bright).qss index 72c7ea7041..9c4c9d441e 100644 --- a/bin/GuiConfigs/Classic (Bright).qss +++ b/bin/GuiConfigs/Classic (Bright).qss @@ -44,13 +44,13 @@ QLabel#gamelist_icon_background_color { } /* log stylesheet */ -QTextEdit#tty_frame { +QPlainTextEdit#tty_frame { background-color:#ffffff; } QLabel#tty_text { color:#000000; } -QTextEdit#log_frame { +QPlainTextEdit#log_frame { background-color:#ffffff; } QLabel#log_level_always { diff --git a/bin/GuiConfigs/Darker Style by TheMitoSan.qss b/bin/GuiConfigs/Darker Style by TheMitoSan.qss index 15be0a5fd3..14a4c1e327 100644 --- a/bin/GuiConfigs/Darker Style by TheMitoSan.qss +++ b/bin/GuiConfigs/Darker Style by TheMitoSan.qss @@ -253,7 +253,7 @@ QLabel#thumbnail_icon_color { } /* Set Log colors */ -QTextEdit#log_frame { +QPlainTextEdit#log_frame { background-color: #000; /* Black */ } QLabel#log_level_always { @@ -285,7 +285,7 @@ QLabel#log_stack { } /* Set TTY colors */ -QTextEdit#tty_frame { +QPlainTextEdit#tty_frame { background-color: #000; /* Black */ } QLabel#tty_text { diff --git a/bin/GuiConfigs/Envy.qss b/bin/GuiConfigs/Envy.qss index fbdda99643..a78ba04f58 100644 --- a/bin/GuiConfigs/Envy.qss +++ b/bin/GuiConfigs/Envy.qss @@ -579,7 +579,7 @@ QLabel#thumbnail_icon_color { } /* Log colors */ -QTextEdit#log_frame { +QPlainTextEdit#log_frame { background-color: #23262d; } @@ -620,7 +620,7 @@ QLabel#log_stack { } /* TTY colors */ -QTextEdit#tty_frame { +QPlainTextEdit#tty_frame { background-color: #23262d; } diff --git a/bin/GuiConfigs/Kuroi (Dark) by Ani.qss b/bin/GuiConfigs/Kuroi (Dark) by Ani.qss index 7e7f5d4b84..54c667213b 100644 --- a/bin/GuiConfigs/Kuroi (Dark) by Ani.qss +++ b/bin/GuiConfigs/Kuroi (Dark) by Ani.qss @@ -292,7 +292,7 @@ QLabel#debugger_frame_pc { } /* Set Log colors */ -QTextEdit#log_frame { +QPlainTextEdit#log_frame { background-color: #000000; /* Black */ } QLabel#log_level_always { @@ -323,7 +323,7 @@ QLabel#log_stack { color: #ffffff; /* White */ } /* Set TTY colors */ -QTextEdit#tty_frame { +QPlainTextEdit#tty_frame { background-color: #000000; /* Black */ } QLabel#tty_text { diff --git a/bin/GuiConfigs/ModernBlue Theme by TheMitoSan.qss b/bin/GuiConfigs/ModernBlue Theme by TheMitoSan.qss index f8d41c3af0..410db682f7 100644 --- a/bin/GuiConfigs/ModernBlue Theme by TheMitoSan.qss +++ b/bin/GuiConfigs/ModernBlue Theme by TheMitoSan.qss @@ -250,7 +250,7 @@ QLabel#thumbnail_icon_color { } /* Set Log colors */ -QTextEdit#log_frame { +QPlainTextEdit#log_frame { background-color: #181d24; /* Black */ } QLabel#log_level_always { @@ -282,7 +282,7 @@ QLabel#log_stack { } /* Set TTY colors */ -QTextEdit#tty_frame { +QPlainTextEdit#tty_frame { background-color: #181d24; /* Black */ } QLabel#tty_text { diff --git a/bin/GuiConfigs/Skyline (Nightfall).qss b/bin/GuiConfigs/Skyline (Nightfall).qss index fbd6b25194..625a6a28b6 100644 --- a/bin/GuiConfigs/Skyline (Nightfall).qss +++ b/bin/GuiConfigs/Skyline (Nightfall).qss @@ -611,7 +611,7 @@ QLineEdit:focus { } /* Log colors */ -QTextEdit#log_frame { +QPlainTextEdit#log_frame { background-color: #111525; } diff --git a/bin/GuiConfigs/Skyline.qss b/bin/GuiConfigs/Skyline.qss index c1339e96ea..ef3c7c6857 100644 --- a/bin/GuiConfigs/Skyline.qss +++ b/bin/GuiConfigs/Skyline.qss @@ -619,7 +619,7 @@ QLineEdit:focus { } /* Log colors */ -QTextEdit#log_frame { +QPlainTextEdit#log_frame { background-color: #FFFFFF; } diff --git a/buildfiles/msvc/rpcs3_default.props b/buildfiles/msvc/rpcs3_default.props index b3ac8aae1c..6f5030c91d 100644 --- a/buildfiles/msvc/rpcs3_default.props +++ b/buildfiles/msvc/rpcs3_default.props @@ -24,7 +24,7 @@ xxhash.lib;ws2_32.lib;Iphlpapi.lib;Bcrypt.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib - ..\3rdparty\ffmpeg\windows\x86_64 + ..\3rdparty\ffmpeg\lib\windows\x86_64 8388608 1048576 diff --git a/rpcs3/CMakeLists.txt b/rpcs3/CMakeLists.txt index a857f7bb0b..ebad9bfeb3 100644 --- a/rpcs3/CMakeLists.txt +++ b/rpcs3/CMakeLists.txt @@ -29,11 +29,11 @@ if(UNIX AND NOT APPLE) endif() endif() -# Qt5 +# Qt # finds Qt libraries and setups custom commands for MOC and UIC # Must be done here because generated MOC and UIC targets cant # be found otherwise -include(${CMAKE_SOURCE_DIR}/3rdparty/qt5.cmake) +include(${CMAKE_SOURCE_DIR}/3rdparty/qt6.cmake) # subdirectories add_subdirectory(Emu) @@ -42,6 +42,7 @@ add_subdirectory(rpcs3qt) 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) @@ -84,7 +85,7 @@ set_target_properties(rpcs3 AUTOUIC ON) target_link_libraries(rpcs3 PRIVATE rpcs3_emu rpcs3_ui) -target_link_libraries(rpcs3 PRIVATE 3rdparty::discordRPC 3rdparty::qt5 3rdparty::hidapi 3rdparty::libusb 3rdparty::wolfssl 3rdparty::libcurl 3rdparty::zlib) +target_link_libraries(rpcs3 PRIVATE 3rdparty::discordRPC 3rdparty::qt6 3rdparty::hidapi 3rdparty::libusb 3rdparty::wolfssl 3rdparty::libcurl 3rdparty::zlib) target_link_libraries(rpcs3 PRIVATE ${ADDITIONAL_LIBS}) # Unix display manager @@ -112,14 +113,6 @@ if(USE_PRECOMPILED_HEADERS) target_precompile_headers(rpcs3 PRIVATE stdafx.h) endif() -get_target_property(_qmake_executable Qt5::qmake IMPORTED_LOCATION) -get_filename_component(_qt_bin_dir "${_qmake_executable}" DIRECTORY) -if(APPLE) - find_program(MACDEPLOYQT_EXECUTABLE macdeployqt HINTS "${_qt_bin_dir}") -elseif(WIN32) - find_program(WINDEPLOYQT_EXECUTABLE windeployqt HINTS "${_qt_bin_dir}") -endif() - # Copy icons to executable directory if(APPLE) if (CMAKE_BUILD_TYPE MATCHES "Debug" OR CMAKE_BUILD_TYPE MATCHES "RelWithDebInfo") @@ -148,35 +141,9 @@ elseif(UNIX) COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/bin/git $/git) elseif(WIN32) - # TODO(cjj19970505@live.cn) - # offical Qt binaries are built with -MD(d) only as stated in offical wiki - # https://wiki.qt.io/Technical_FAQ#Why_does_a_statically_built_Qt_use_the_dynamic_Visual_Studio_runtime_libraries_.3F_Do_I_need_to_deploy_those_with_my_application_.3F - # If we build our libs with /MT(d), we might encounter some issues. - # https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-library-features?view=msvc-160#what-problems-exist-if-an-application-uses-more-than-one-crt-version - - # Qt installed from Qt installer has following hierarchy: - # bin/ for release and debug dlls and windeployqt tools - # lib/cmake/Qt5/ for Qt5_Dir - # Qt installed from vcpkg has following hierarchy: - # bin/ for release dlls - # debug/bin/ for debug dlls - # tools/qt5/bin/ for tools including windeployqt - # tools/qt5/debug/bin/ for tools with debug build including windeployqt - # share/cmake/Qt5/ for Qt5_Dir - - # If Qt5 is installed from official Qt installer - # list(APPEND _QT5_TOOLS_PATHS "${Qt5_DIR}/../../../bin/") - - # If Qt5 is installed from vcpkg - # list(APPEND _QT5_TOOLS_PATHS "${Qt5_DIR}/../../../tools/qt5$<$:/debug>/bin/") - add_custom_command(TARGET rpcs3 POST_BUILD - # COMMAND set PATH=${_QT5_TOOLS_PATHS}$%PATH% COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_SOURCE_DIR}/bin" "$" - # If Qt5 is installed from vcpkg, add binary path to PATH - # otherwise windeployqt tool won't be able to locate necessary dlls - # COMMAND set PATH=${Qt5_DIR}/../../../$<$:debug/>bin/$%PATH% - COMMAND "${WINDEPLOYQT_EXECUTABLE}" --no-angle --no-compiler-runtime --no-opengl-sw --no-patchqt --no-translations --no-quick --plugindir "$/qt/plugins" --verbose 0 "$") + COMMAND "${WINDEPLOYQT_EXECUTABLE}" --no-compiler-runtime --no-opengl-sw --no-patchqt --no-translations --no-quick --no-system-d3d-compiler --no-quick-import --plugindir "$,$/plugins,$/share/qt6/plugins>" --verbose 0 "$") endif() # Unix installation diff --git a/rpcs3/Crypto/decrypt_binaries.h b/rpcs3/Crypto/decrypt_binaries.h index d9887b3d5f..be80c31d42 100644 --- a/rpcs3/Crypto/decrypt_binaries.h +++ b/rpcs3/Crypto/decrypt_binaries.h @@ -16,7 +16,7 @@ public: bool done() const { - return m_index >= m_klics.size(); + return m_index >= m_modules.size(); } const std::string& operator[](usz index) const diff --git a/rpcs3/Crypto/unedat.cpp b/rpcs3/Crypto/unedat.cpp index 74e9bd43af..5ffec82fd8 100644 --- a/rpcs3/Crypto/unedat.cpp +++ b/rpcs3/Crypto/unedat.cpp @@ -135,9 +135,9 @@ std::tuple dec_section(unsigned char* metadata) dec[0x0E] = (metadata[0x6] ^ metadata[0x2] ^ metadata[0x1E]); dec[0x0F] = (metadata[0x7] ^ metadata[0x3] ^ metadata[0x1F]); - u64 offset = swap64(*reinterpret_cast(&dec[0])); - s32 length = swap32(*reinterpret_cast(&dec[8])); - s32 compression_end = swap32(*reinterpret_cast(&dec[12])); + u64 offset = read_from_ptr>(dec, 0); + s32 length = read_from_ptr>(dec, 8); + s32 compression_end = read_from_ptr>(dec, 12); return std::make_tuple(offset, length, compression_end); } @@ -149,7 +149,7 @@ u128 get_block_key(int block, NPD_HEADER *npd) u128 dest_key{}; std::memcpy(&dest_key, src_key, 0xC); - s32 swappedBlock = swap32(block); + s32 swappedBlock = std::bit_cast>(block); std::memcpy(reinterpret_cast(&dest_key) + 0xC, &swappedBlock, sizeof(swappedBlock)); return dest_key; } @@ -193,9 +193,9 @@ s64 decrypt_block(const fs::file* in, u8* out, EDAT_HEADER *edat, NPD_HEADER *np // NOTE: For NPD version 1 the metadata is not encrypted. if (npd->version <= 1) { - offset = swap64(*reinterpret_cast(&metadata[0x10])); - length = swap32(*reinterpret_cast(&metadata[0x18])); - compression_end = swap32(*reinterpret_cast(&metadata[0x1C])); + offset = read_from_ptr>(metadata, 0x10); + length = read_from_ptr>(metadata, 0x18); + compression_end = read_from_ptr>(metadata, 0x1C); } else { @@ -433,17 +433,26 @@ int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs: edat_log.warning("COMPRESSED data detected!"); } - const int block_num = static_cast((edat->file_size + edat->block_size - 1) / edat->block_size); - const int metadata_offset = 0x100; - const int metadata_size = metadata_section_size * block_num; + if (!edat->block_size) + { + return 1; + } + + const usz block_num = utils::aligned_div(edat->file_size, edat->block_size); + constexpr usz metadata_offset = 0x100; + const usz metadata_size = utils::mul_saturate(metadata_section_size, block_num); u64 metadata_section_offset = metadata_offset; - long bytes_read = 0; - long bytes_to_read = metadata_size; - std::unique_ptr metadata(new u8[metadata_size]); - std::unique_ptr empty_metadata(new u8[metadata_size]); + if (utils::add_saturate(utils::add_saturate(file_offset, metadata_section_offset), metadata_size) > f->size()) + { + return 1; + } - while (bytes_to_read > 0) + u64 bytes_read = 0; + const auto metadata = std::make_unique(metadata_size); + const auto empty_metadata = std::make_unique(metadata_size); + + while (bytes_read < metadata_size) { // Locate the metadata blocks. f->seek(file_offset + metadata_section_offset); @@ -453,7 +462,6 @@ int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs: // Adjust sizes. bytes_read += metadata_section_size; - bytes_to_read -= metadata_section_size; if (((edat->flags & EDAT_FLAG_0x20) != 0)) // Metadata block before each data block. metadata_section_offset += (metadata_section_size + edat->block_size); @@ -553,18 +561,18 @@ bool validate_dev_klic(const u8* klicensee, NPD_HEADER *npd) return true; } - unsigned char dev[0x60] = { 0 }; + unsigned char dev[0x60]{}; // Build the dev buffer (first 0x60 bytes of NPD header in big-endian). - memcpy(dev, npd, 0x60); + std::memcpy(dev, npd, 0x60); // Fix endianness. - int version = swap32(npd->version); - int license = swap32(npd->license); - int type = swap32(npd->type); - memcpy(dev + 0x4, &version, 4); - memcpy(dev + 0x8, &license, 4); - memcpy(dev + 0xC, &type, 4); + s32 version = std::bit_cast>(npd->version); + s32 license = std::bit_cast>(npd->license); + s32 type = std::bit_cast>(npd->type); + std::memcpy(dev + 0x4, &version, 4); + std::memcpy(dev + 0x8, &license, 4); + std::memcpy(dev + 0xC, &type, 4); // Check for an empty dev_hash (can't validate if devklic is NULL); u128 klic; @@ -638,20 +646,20 @@ void read_npd_edat_header(const fs::file* input, NPD_HEADER& NPD, EDAT_HEADER& E input->read(npd_header, sizeof(npd_header)); input->read(edat_header, sizeof(edat_header)); - memcpy(&NPD.magic, npd_header, 4); - NPD.version = swap32(*reinterpret_cast(&npd_header[4])); - NPD.license = swap32(*reinterpret_cast(&npd_header[8])); - NPD.type = swap32(*reinterpret_cast(&npd_header[12])); - memcpy(NPD.content_id, &npd_header[16], 0x30); - memcpy(NPD.digest, &npd_header[64], 0x10); - memcpy(NPD.title_hash, &npd_header[80], 0x10); - memcpy(NPD.dev_hash, &npd_header[96], 0x10); - NPD.activate_time = swap64(*reinterpret_cast(&npd_header[112])); - NPD.expire_time = swap64(*reinterpret_cast(&npd_header[120])); + std::memcpy(&NPD.magic, npd_header, 4); + NPD.version = read_from_ptr>(npd_header, 4); + NPD.license = read_from_ptr>(npd_header, 8); + NPD.type = read_from_ptr>(npd_header, 12); + std::memcpy(NPD.content_id, &npd_header[16], 0x30); + std::memcpy(NPD.digest, &npd_header[64], 0x10); + std::memcpy(NPD.title_hash, &npd_header[80], 0x10); + std::memcpy(NPD.dev_hash, &npd_header[96], 0x10); + NPD.activate_time = read_from_ptr>(npd_header, 112); + NPD.expire_time = read_from_ptr>(npd_header, 120); - EDAT.flags = swap32(*reinterpret_cast(&edat_header[0])); - EDAT.block_size = swap32(*reinterpret_cast(&edat_header[4])); - EDAT.file_size = swap64(*reinterpret_cast(&edat_header[8])); + EDAT.flags = read_from_ptr>(edat_header, 0); + EDAT.block_size = read_from_ptr>(edat_header, 4); + EDAT.file_size = read_from_ptr>(edat_header, 8); } bool extract_all_data(const fs::file* input, const fs::file* output, const char* input_file_name, unsigned char* devklic, bool verbose) diff --git a/rpcs3/Crypto/unself.cpp b/rpcs3/Crypto/unself.cpp index 16cbf0cb1c..a553bca1bd 100644 --- a/rpcs3/Crypto/unself.cpp +++ b/rpcs3/Crypto/unself.cpp @@ -365,22 +365,14 @@ void MetadataInfo::Show() const void MetadataHeader::Load(u8* in) { - memcpy(&signature_input_length, in, 8); - memcpy(&unknown1, in + 8, 4); - memcpy(§ion_count, in + 12, 4); - memcpy(&key_count, in + 16, 4); - memcpy(&opt_header_size, in + 20, 4); - memcpy(&unknown2, in + 24, 4); - memcpy(&unknown3, in + 28, 4); - // Endian swap. - signature_input_length = swap64(signature_input_length); - unknown1 = swap32(unknown1); - section_count = swap32(section_count); - key_count = swap32(key_count); - opt_header_size = swap32(opt_header_size); - unknown2 = swap32(unknown2); - unknown3 = swap32(unknown3); + signature_input_length = read_from_ptr>(in); + unknown1 = read_from_ptr>(in, 8); + section_count = read_from_ptr>(in, 12); + key_count = read_from_ptr>(in, 16); + opt_header_size = read_from_ptr>(in, 20); + unknown2 = read_from_ptr>(in, 24); + unknown3 = read_from_ptr>(in, 28); } void MetadataHeader::Show() const @@ -396,28 +388,17 @@ void MetadataHeader::Show() const void MetadataSectionHeader::Load(u8* in) { - memcpy(&data_offset, in, 8); - memcpy(&data_size, in + 8, 8); - memcpy(&type, in + 16, 4); - memcpy(&program_idx, in + 20, 4); - memcpy(&hashed, in + 24, 4); - memcpy(&sha1_idx, in + 28, 4); - memcpy(&encrypted, in + 32, 4); - memcpy(&key_idx, in + 36, 4); - memcpy(&iv_idx, in + 40, 4); - memcpy(&compressed, in + 44, 4); - // Endian swap. - data_offset = swap64(data_offset); - data_size = swap64(data_size); - type = swap32(type); - program_idx = swap32(program_idx); - hashed = swap32(hashed); - sha1_idx = swap32(sha1_idx); - encrypted = swap32(encrypted); - key_idx = swap32(key_idx); - iv_idx = swap32(iv_idx); - compressed = swap32(compressed); + data_offset = read_from_ptr>(in); + data_size = read_from_ptr>(in, 8); + type = read_from_ptr>(in, 16); + program_idx = read_from_ptr>(in, 20); + hashed = read_from_ptr>(in, 24); + sha1_idx = read_from_ptr>(in, 28); + encrypted = read_from_ptr>(in, 32); + key_idx = read_from_ptr>(in, 36); + iv_idx = read_from_ptr>(in, 40); + compressed = read_from_ptr>(in, 44); } void MetadataSectionHeader::Show() const @@ -936,19 +917,29 @@ bool SELFDecrypter::LoadHeaders(bool isElf32, SelfAdditionalInfo* out_info) } } - // Read section info. m_seg_ext_hdr.clear(); self_f.seek(m_ext_hdr.segment_ext_hdr_offset); - for(u32 i = 0; i < ((isElf32) ? elf32_hdr.e_phnum : elf64_hdr.e_phnum); ++i) + for(u32 i = 0; i < (isElf32 ? elf32_hdr.e_phnum : elf64_hdr.e_phnum); ++i) { + if (self_f.pos() >= self_f.size()) + { + return false; + } + m_seg_ext_hdr.emplace_back(); m_seg_ext_hdr.back().Load(self_f); } + if (m_ext_hdr.version_hdr_offset == 0 || utils::add_saturate(m_ext_hdr.version_hdr_offset, sizeof(version_header)) > self_f.size()) + { + return false; + } + // Read SCE version info. self_f.seek(m_ext_hdr.version_hdr_offset); + m_version_hdr.Load(self_f); // Read control info. @@ -957,6 +948,11 @@ bool SELFDecrypter::LoadHeaders(bool isElf32, SelfAdditionalInfo* out_info) for (u64 i = 0; i < m_ext_hdr.supplemental_hdr_size;) { + if (self_f.pos() >= self_f.size()) + { + return false; + } + m_supplemental_hdr_arr.emplace_back(); supplemental_header& cinfo = m_supplemental_hdr_arr.back(); cinfo.Load(self_f); @@ -1407,7 +1403,7 @@ static bool CheckDebugSelf(fs::file& s) return false; } -fs::file decrypt_self(fs::file elf_or_self, u8* klic_key, SelfAdditionalInfo* out_info) +fs::file decrypt_self(fs::file elf_or_self, u8* klic_key, SelfAdditionalInfo* out_info, bool require_encrypted) { if (out_info) { @@ -1422,8 +1418,14 @@ fs::file decrypt_self(fs::file elf_or_self, u8* klic_key, SelfAdditionalInfo* ou elf_or_self.seek(0); // Check SELF header first. Check for a debug SELF. - if (elf_or_self.size() >= 4 && elf_or_self.read() == "SCE\0"_u32 && !CheckDebugSelf(elf_or_self)) + if (elf_or_self.size() >= 4 && elf_or_self.read() == "SCE\0"_u32) { + if (CheckDebugSelf(elf_or_self)) + { + // TODO: Decrypt + return elf_or_self; + } + // Check the ELF file class (32 or 64 bit). const bool isElf32 = IsSelfElf32(elf_or_self); @@ -1455,6 +1457,11 @@ fs::file decrypt_self(fs::file elf_or_self, u8* klic_key, SelfAdditionalInfo* ou return self_dec.MakeElf(isElf32); } + if (require_encrypted) + { + return {}; + } + return elf_or_self; } diff --git a/rpcs3/Crypto/unself.h b/rpcs3/Crypto/unself.h index 4cc5d90ee3..afa3416095 100644 --- a/rpcs3/Crypto/unself.h +++ b/rpcs3/Crypto/unself.h @@ -559,7 +559,7 @@ private: } }; -fs::file decrypt_self(fs::file elf_or_self, u8* klic_key = nullptr, SelfAdditionalInfo* additional_info = nullptr); +fs::file decrypt_self(fs::file elf_or_self, u8* klic_key = nullptr, SelfAdditionalInfo* additional_info = nullptr, bool require_encrypted = false); bool verify_npdrm_self_headers(const fs::file& self, u8* klic_key = nullptr, NPD_HEADER* npd_out = nullptr); bool get_npdrm_self_header(const fs::file& self, NPD_HEADER& npd); diff --git a/rpcs3/Crypto/utils.h b/rpcs3/Crypto/utils.h index 156ba2437b..3e36989dfe 100644 --- a/rpcs3/Crypto/utils.h +++ b/rpcs3/Crypto/utils.h @@ -5,39 +5,12 @@ // http://www.gnu.org/licenses/gpl-2.0.txt #include "util/types.hpp" +#include "util/asm.hpp" #include enum { CRYPTO_MAX_PATH = 4096 }; -// Auxiliary functions (endian swap, xor, and file name). -inline u16 swap16(u16 i) -{ -#if defined(__GNUG__) - return __builtin_bswap16(i); -#else - return _byteswap_ushort(i); -#endif -} - -inline u32 swap32(u32 i) -{ -#if defined(__GNUG__) - return __builtin_bswap32(i); -#else - return _byteswap_ulong(i); -#endif -} - -inline u64 swap64(u64 i) -{ -#if defined(__GNUG__) - return __builtin_bswap64(i); -#else - return _byteswap_uint64(i); -#endif -} - char* extract_file_name(const char* file_path, char real_file_name[CRYPTO_MAX_PATH]); std::string sha256_get_hash(const char* data, usz size, bool lower_case); diff --git a/rpcs3/Emu/Audio/AudioBackend.cpp b/rpcs3/Emu/Audio/AudioBackend.cpp index d7d03658e1..320ec753bc 100644 --- a/rpcs3/Emu/Audio/AudioBackend.cpp +++ b/rpcs3/Emu/Audio/AudioBackend.cpp @@ -158,6 +158,6 @@ AudioChannelCnt AudioBackend::convert_channel_count(u64 raw) case 1: return AudioChannelCnt::STEREO; case 0: - fmt::throw_exception("Usupported channel count"); + fmt::throw_exception("Unsupported channel count"); } } diff --git a/rpcs3/Emu/Audio/Cubeb/CubebBackend.cpp b/rpcs3/Emu/Audio/Cubeb/CubebBackend.cpp index 4c89ff8fbc..0ca8f0b1ea 100644 --- a/rpcs3/Emu/Audio/Cubeb/CubebBackend.cpp +++ b/rpcs3/Emu/Audio/Cubeb/CubebBackend.cpp @@ -543,7 +543,7 @@ void CubebBackend::device_collection_changed_cb(cubeb* context, void* user_ptr) if (context != cubeb->m_ctx) { - Cubeb.error("device_collection_changed_cb called with unkown context"); + Cubeb.error("device_collection_changed_cb called with unknown context"); return; } diff --git a/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.cpp b/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.cpp index c8fb8aa2e3..178d23989e 100644 --- a/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.cpp +++ b/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.cpp @@ -317,12 +317,9 @@ f64 XAudio2Backend::GetCallbackFrameLen() return _10ms; } -#if _MSC_VER Microsoft::WRL::ComPtr xaudio_ext{}; -#endif f64 min_latency{}; -#if _MSC_VER if (HRESULT hr = m_xaudio2_instance->QueryInterface(IID_IXAudio2Extension, std::bit_cast(xaudio_ext.GetAddressOf())); FAILED(hr)) { XAudio.error("QueryInterface() failed: %s (0x%08x)", std::system_category().message(hr), static_cast(hr)); @@ -337,7 +334,6 @@ f64 XAudio2Backend::GetCallbackFrameLen() min_latency = static_cast(samples_per_q) / freq; } } -#endif return std::max(min_latency, _10ms); // 10ms is the minimum for XAudio } diff --git a/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.h b/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.h index 4a0e68977a..8308ce0eaf 100644 --- a/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.h +++ b/rpcs3/Emu/Audio/XAudio2/XAudio2Backend.h @@ -11,6 +11,7 @@ #ifdef _MSC_VER #include #else +#include #include #endif #include diff --git a/rpcs3/Emu/CMakeLists.txt b/rpcs3/Emu/CMakeLists.txt index 4c3e348ccc..b1673a8077 100644 --- a/rpcs3/Emu/CMakeLists.txt +++ b/rpcs3/Emu/CMakeLists.txt @@ -163,6 +163,7 @@ if(WIN32) Audio/XAudio2/XAudio2Backend.cpp Audio/XAudio2/xaudio2_enumerator.cpp ) + target_compile_definitions(rpcs3_emu PRIVATE UNICODE _UNICODE _WIN32_WINNT=0x0602) endif() target_link_libraries(rpcs3_emu diff --git a/rpcs3/Emu/CPU/CPUThread.cpp b/rpcs3/Emu/CPU/CPUThread.cpp index 031176a948..aecf9f2a24 100644 --- a/rpcs3/Emu/CPU/CPUThread.cpp +++ b/rpcs3/Emu/CPU/CPUThread.cpp @@ -261,7 +261,7 @@ struct cpu_prof if (threads.empty()) { // Wait for messages if no work (don't waste CPU) - thread_ctrl::wait_on(registered, nullptr); + thread_ctrl::wait_on(registered); continue; } @@ -939,7 +939,7 @@ bool cpu_thread::check_state() noexcept else { // TODO: fix the workaround - g_suspend_counter.wait(ctr, -4, atomic_wait_timeout{100}); + g_suspend_counter.wait(ctr, atomic_wait_timeout{10'000}); } } else @@ -972,8 +972,7 @@ bool cpu_thread::check_state() noexcept } // Short sleep when yield flag is present alone (makes no sense when other methods which can stop thread execution have been done) - // Pass a mask of a single bit which is often unused to avoid notifications - s_dummy_atomic.wait(0, 1u << 30, atomic_wait_timeout{80'000}); + s_dummy_atomic.wait(0, atomic_wait_timeout{80'000}); } } } @@ -1010,13 +1009,13 @@ cpu_thread& cpu_thread::operator=(thread_state) if (old & cpu_flag::wait && old.none_of(cpu_flag::again + cpu_flag::exit)) { - state.notify_one(cpu_flag::exit); + state.notify_one(); if (auto thread = try_get()) { if (u32 resv = atomic_storage::load(thread->raddr)) { - vm::reservation_notifier(resv).notify_all(-128); + vm::reservation_notifier(resv).notify_all(); } } } diff --git a/rpcs3/Emu/Cell/Modules/cellAdec.cpp b/rpcs3/Emu/Cell/Modules/cellAdec.cpp index 77deedc265..5a62e03b11 100644 --- a/rpcs3/Emu/Cell/Modules/cellAdec.cpp +++ b/rpcs3/Emu/Cell/Modules/cellAdec.cpp @@ -588,10 +588,10 @@ public: frame.auAddr = task.au.addr; frame.auSize = task.au.size; frame.userdata = task.au.userdata; - frame.size = frame.data->nb_samples * frame.data->channels * nbps; + frame.size = frame.data->nb_samples * frame.data->ch_layout.nb_channels * nbps; //cellAdec.notice("got audio frame (pts=0x%llx, nb_samples=%d, ch=%d, sample_rate=%d, nbps=%d)", - //frame.pts, frame.data->nb_samples, frame.data->channels, frame.data->sample_rate, nbps); + //frame.pts, frame.data->nb_samples, frame.data->ch_layout.nb_channels, frame.data->sample_rate, nbps); if (frames.push(frame, &is_closed)) { @@ -944,7 +944,7 @@ error_code cellAdecGetPcm(u32 handle, vm::ptr outBuffer) if (outBuffer) { // reverse byte order: - if (frame->format == AV_SAMPLE_FMT_FLTP && frame->channels == 1) + if (frame->format == AV_SAMPLE_FMT_FLTP && frame->ch_layout.nb_channels == 1) { float* in_f = reinterpret_cast(frame->extended_data[0]); for (u32 i = 0; i < af.size / 4; i++) @@ -952,7 +952,7 @@ error_code cellAdecGetPcm(u32 handle, vm::ptr outBuffer) outBuffer[i] = in_f[i]; } } - else if (frame->format == AV_SAMPLE_FMT_FLTP && frame->channels == 2) + else if (frame->format == AV_SAMPLE_FMT_FLTP && frame->ch_layout.nb_channels == 2) { float* in_f[2]; in_f[0] = reinterpret_cast(frame->extended_data[0]); @@ -963,7 +963,7 @@ error_code cellAdecGetPcm(u32 handle, vm::ptr outBuffer) outBuffer[i * 2 + 1] = in_f[1][i]; } } - else if (frame->format == AV_SAMPLE_FMT_FLTP && frame->channels == 6) + else if (frame->format == AV_SAMPLE_FMT_FLTP && frame->ch_layout.nb_channels == 6) { float* in_f[6]; in_f[0] = reinterpret_cast(frame->extended_data[0]); @@ -982,7 +982,7 @@ error_code cellAdecGetPcm(u32 handle, vm::ptr outBuffer) outBuffer[i * 6 + 5] = in_f[5][i]; } } - else if (frame->format == AV_SAMPLE_FMT_FLTP && frame->channels == 8) + else if (frame->format == AV_SAMPLE_FMT_FLTP && frame->ch_layout.nb_channels == 8) { float* in_f[8]; in_f[0] = reinterpret_cast(frame->extended_data[0]); @@ -1005,7 +1005,7 @@ error_code cellAdecGetPcm(u32 handle, vm::ptr outBuffer) outBuffer[i * 8 + 7] = in_f[7][i]; } } - else if (frame->format == AV_SAMPLE_FMT_S16P && frame->channels == 1) + else if (frame->format == AV_SAMPLE_FMT_S16P && frame->ch_layout.nb_channels == 1) { s16* in_i = reinterpret_cast(frame->extended_data[0]); for (u32 i = 0; i < af.size / 2; i++) @@ -1013,7 +1013,7 @@ error_code cellAdecGetPcm(u32 handle, vm::ptr outBuffer) outBuffer[i] = in_i[i] / 32768.f; } } - else if (frame->format == AV_SAMPLE_FMT_S16P && frame->channels == 2) + else if (frame->format == AV_SAMPLE_FMT_S16P && frame->ch_layout.nb_channels == 2) { s16* in_i[2]; in_i[0] = reinterpret_cast(frame->extended_data[0]); @@ -1026,7 +1026,7 @@ error_code cellAdecGetPcm(u32 handle, vm::ptr outBuffer) } else { - fmt::throw_exception("Unsupported frame format (channels=%d, format=%d)", frame->channels, frame->format); + fmt::throw_exception("Unsupported frame format (channels=%d, format=%d)", frame->ch_layout.nb_channels, frame->format); } } @@ -1078,25 +1078,26 @@ error_code cellAdecGetPcmItem(u32 handle, vm::pptr pcmItem) atx->samplingFreq = frame->sample_rate; atx->nbytes = frame->nb_samples * u32{sizeof(float)}; - if (frame->channels == 1) + + switch (frame->ch_layout.nb_channels) { - atx->channelConfigIndex = 1; - } - else if (frame->channels == 2) + case 1: + case 2: + case 6: { - atx->channelConfigIndex = 2; + atx->channelConfigIndex = frame->ch_layout.nb_channels; + break; } - else if (frame->channels == 6) - { - atx->channelConfigIndex = 6; - } - else if (frame->channels == 8) + case 8: { atx->channelConfigIndex = 7; + break; } - else + default: { - cellAdec.fatal("cellAdecGetPcmItem(): unsupported channel count (%d)", frame->channels); + cellAdec.fatal("cellAdecGetPcmItem(): unsupported channel count (%d)", frame->ch_layout.nb_channels); + break; + } } } else if (adec->type == CELL_ADEC_TYPE_MP3) diff --git a/rpcs3/Emu/Cell/Modules/cellAudio.cpp b/rpcs3/Emu/Cell/Modules/cellAudio.cpp index dfc3d6ea24..1dcbb6ceae 100644 --- a/rpcs3/Emu/Cell/Modules/cellAudio.cpp +++ b/rpcs3/Emu/Cell/Modules/cellAudio.cpp @@ -10,6 +10,8 @@ LOG_CHANNEL(cellAudio); +extern void lv2_sleep(u64 timeout, ppu_thread* ppu = nullptr); + vm::gvar g_audio_buffer; struct alignas(16) aligned_index_t @@ -1252,8 +1254,6 @@ error_code cellAudioPortOpen(vm::ptr audioParam, vm::ptrget(); - std::lock_guard lock(g_audio.mutex); - if (!g_audio.init) { return CELL_AUDIO_ERROR_NOT_INIT; @@ -1319,6 +1319,16 @@ error_code cellAudioPortOpen(vm::ptr audioParam, vm::ptrget(); - std::lock_guard lock(g_audio.mutex); - if (!g_audio.init) { return CELL_AUDIO_ERROR_NOT_INIT; @@ -1422,6 +1430,16 @@ error_code cellAudioPortStart(u32 portNum) return CELL_AUDIO_ERROR_PARAM; } + // Waiting for VSH + lv2_sleep(30); + + std::lock_guard lock(g_audio.mutex); + + if (!g_audio.init) + { + return CELL_AUDIO_ERROR_NOT_INIT; + } + switch (audio_port_state state = g_audio.ports[portNum].state.compare_and_swap(audio_port_state::opened, audio_port_state::started)) { case audio_port_state::closed: return CELL_AUDIO_ERROR_PORT_NOT_OPEN; @@ -1650,10 +1668,69 @@ error_code cellAudioCreateNotifyEventQueueEx(ppu_thread& ppu, vm::ptr id, v return AudioCreateNotifyEventQueue(ppu, id, key, queue_type); } -error_code AudioSetNotifyEventQueue(u64 key, u32 iFlags) +error_code AudioSetNotifyEventQueue(ppu_thread& ppu, u64 key, u32 iFlags) { auto& g_audio = g_fxo->get(); + if (!g_audio.init) + { + return CELL_AUDIO_ERROR_NOT_INIT; + } + + // Waiting for VSH + lv2_sleep(20, &ppu); + + // Dirty hack for sound: confirm the creation of _mxr000 event queue by _cellsurMixerMain thread + constexpr u64 c_mxr000 = 0x8000cafe0246030; + + if (key == c_mxr000 || key == 0) + { + bool has_sur_mixer_thread = false; + + for (usz count = 0; !lv2_event_queue::find(c_mxr000) && count < 100; count++) + { + if (has_sur_mixer_thread || idm::select>([&](u32 id, named_thread& test_ppu) + { + // Confirm thread existence + if (id == ppu.id) + { + return false; + } + + const auto ptr = test_ppu.ppu_tname.load(); + + if (!ptr) + { + return false; + } + + return *ptr == "_cellsurMixerMain"sv; + }).ret) + { + has_sur_mixer_thread = true; + } + else + { + break; + } + + if (ppu.is_stopped()) + { + ppu.state += cpu_flag::again; + return {}; + } + + cellAudio.error("AudioSetNotifyEventQueue(): Waiting for _mxr000. x%d", count); + + lv2_sleep(50'000, &ppu); + } + + if (has_sur_mixer_thread && lv2_event_queue::find(c_mxr000)) + { + key = c_mxr000; + } + } + std::lock_guard lock(g_audio.mutex); if (!g_audio.init) @@ -1687,27 +1764,33 @@ error_code AudioSetNotifyEventQueue(u64 key, u32 iFlags) } // Set unique source associated with the key - g_audio.keys.push_back({ + g_audio.keys.push_back + ({ .start_period = g_audio.event_period, .flags = iFlags, .source = ((process_getpid() + u64{}) << 32) + lv2_event_port::id_base + (g_audio.key_count++ * lv2_event_port::id_step), .ack_timestamp = 0, .port = std::move(q) }); + g_audio.key_count %= lv2_event_port::id_count; return CELL_OK; } -error_code cellAudioSetNotifyEventQueue(u64 key) +error_code cellAudioSetNotifyEventQueue(ppu_thread& ppu, u64 key) { + ppu.state += cpu_flag::wait; + cellAudio.warning("cellAudioSetNotifyEventQueue(key=0x%llx)", key); - return AudioSetNotifyEventQueue(key, 0); + return AudioSetNotifyEventQueue(ppu, key, 0); } -error_code cellAudioSetNotifyEventQueueEx(u64 key, u32 iFlags) +error_code cellAudioSetNotifyEventQueueEx(ppu_thread& ppu, u64 key, u32 iFlags) { + ppu.state += cpu_flag::wait; + cellAudio.todo("cellAudioSetNotifyEventQueueEx(key=0x%llx, iFlags=0x%x)", key, iFlags); if (iFlags & (~0u >> 5)) @@ -1715,7 +1798,7 @@ error_code cellAudioSetNotifyEventQueueEx(u64 key, u32 iFlags) return CELL_AUDIO_ERROR_PARAM; } - return AudioSetNotifyEventQueue(key, iFlags); + return AudioSetNotifyEventQueue(ppu, key, iFlags); } error_code AudioRemoveNotifyEventQueue(u64 key, u32 iFlags) diff --git a/rpcs3/Emu/Cell/Modules/cellAvconfExt.cpp b/rpcs3/Emu/Cell/Modules/cellAvconfExt.cpp index 1ea8aa5f39..ed0e3d7526 100644 --- a/rpcs3/Emu/Cell/Modules/cellAvconfExt.cpp +++ b/rpcs3/Emu/Cell/Modules/cellAvconfExt.cpp @@ -202,7 +202,7 @@ error_code cellVideoOutSetupDisplay(u32 videoOut) error_code cellAudioInGetDeviceInfo(u32 deviceNumber, u32 deviceIndex, vm::ptr info) { - cellAvconfExt.todo("cellAudioInGetDeviceInfo(deviceNumber=0x%x, deviceIndex=0x%x, info=*0x%x)", deviceNumber, deviceIndex, info); + cellAvconfExt.trace("cellAudioInGetDeviceInfo(deviceNumber=0x%x, deviceIndex=0x%x, info=*0x%x)", deviceNumber, deviceIndex, info); if (deviceIndex != 0 || !info) { @@ -277,7 +277,7 @@ error_code cellVideoOutGetGamma(u32 videoOut, vm::ptr gamma) error_code cellAudioInGetAvailableDeviceInfo(u32 count, vm::ptr device_info) { - cellAvconfExt.todo("cellAudioInGetAvailableDeviceInfo(count=%d, info=*0x%x)", count, device_info); + cellAvconfExt.trace("cellAudioInGetAvailableDeviceInfo(count=%d, info=*0x%x)", count, device_info); if (count > 16 || !device_info) { diff --git a/rpcs3/Emu/Cell/Modules/cellCamera.cpp b/rpcs3/Emu/Cell/Modules/cellCamera.cpp index 7e6a631d47..76a59c11c4 100644 --- a/rpcs3/Emu/Cell/Modules/cellCamera.cpp +++ b/rpcs3/Emu/Cell/Modules/cellCamera.cpp @@ -1891,7 +1891,6 @@ bool camera_context::on_handler_state(camera_handler_base::camera_handler_state { switch (state) { - case camera_handler_base::camera_handler_state::not_available: case camera_handler_base::camera_handler_state::closed: { if (is_attached) diff --git a/rpcs3/Emu/Cell/Modules/cellGame.cpp b/rpcs3/Emu/Cell/Modules/cellGame.cpp index 8b5ffb8765..d34539c2f0 100644 --- a/rpcs3/Emu/Cell/Modules/cellGame.cpp +++ b/rpcs3/Emu/Cell/Modules/cellGame.cpp @@ -349,6 +349,29 @@ void disc_change_manager::insert_disc(u32 disc_type, std::string title_id) }); } +extern void lv2_sleep(u64 timeout, ppu_thread* ppu = nullptr) +{ + if (!ppu) + { + ppu = ensure(cpu_thread::get_current()); + } + + if (!timeout) + { + return; + } + + const bool had_wait = ppu->state.test_and_set(cpu_flag::wait); + + lv2_obj::sleep(*ppu); + lv2_obj::wait_timeout(timeout); + ppu->check_state(); + + if (had_wait) + { + ppu->state += cpu_flag::wait; + } +} error_code cellHddGameCheck(ppu_thread& ppu, u32 version, vm::cptr dirName, u32 errDialog, vm::ptr funcStat, u32 container) { @@ -434,6 +457,8 @@ error_code cellHddGameCheck(ppu_thread& ppu, u32 version, vm::cptr dirName // TODO ? + lv2_sleep(5000, &ppu); + funcStat(ppu, result, get, set); std::string error_msg; @@ -445,6 +470,8 @@ error_code cellHddGameCheck(ppu_thread& ppu, u32 version, vm::cptr dirName // Game confirmed that it wants to create directory const auto setParam = set->setParam; + lv2_sleep(2000, &ppu); + if (new_data) { if (!setParam) @@ -536,6 +563,10 @@ error_code cellHddGameCheck(ppu_thread& ppu, u32 version, vm::cptr dirName return CELL_GAMEDATA_ERROR_INTERNAL; } } + else + { + lv2_sleep(2000, &ppu); + } return CELL_HDDGAME_ERROR_CBRESULT; } @@ -548,14 +579,24 @@ error_code cellHddGameCheck2(ppu_thread& ppu, u32 version, vm::cptr dirNam return cellHddGameCheck(ppu, version, dirName, errDialog, funcStat, container); } -error_code cellHddGameGetSizeKB(vm::ptr size) +error_code cellHddGameGetSizeKB(ppu_thread& ppu, vm::ptr size) { + ppu.state += cpu_flag::wait; + cellGame.warning("cellHddGameGetSizeKB(size=*0x%x)", size); + lv2_obj::sleep(ppu); + + const u64 start_sleep = ppu.start_time; + const std::string local_dir = vfs::get(Emu.GetDir()); const auto dirsz = fs::get_dir_size(local_dir, 1024); + // This function is very slow by nature + // TODO: Check if after first use the result is being cached so the sleep can be reduced in this case + lv2_sleep(utils::sub_saturate(dirsz == umax ? 2000 : 200000, get_guest_system_time() - start_sleep), &ppu); + if (dirsz == umax) { const auto error = fs::g_tls_error; @@ -568,7 +609,8 @@ error_code cellHddGameGetSizeKB(vm::ptr size) return CELL_HDDGAME_ERROR_FAILURE; } - *size = ::narrow(dirsz / 1024); + ppu.check_state(); + *size = ::narrow(dirsz / 1024); return CELL_OK; } @@ -591,8 +633,10 @@ error_code cellHddGameExitBroken() return open_exit_dialog(get_localized_string(localized_string_id::CELL_HDD_GAME_EXIT_BROKEN), true); } -error_code cellGameDataGetSizeKB(vm::ptr size) +error_code cellGameDataGetSizeKB(ppu_thread& ppu, vm::ptr size) { + ppu.state += cpu_flag::wait; + cellGame.warning("cellGameDataGetSizeKB(size=*0x%x)", size); if (!size) @@ -600,10 +644,18 @@ error_code cellGameDataGetSizeKB(vm::ptr size) return CELL_GAMEDATA_ERROR_PARAM; } + lv2_obj::sleep(ppu); + + const u64 start_sleep = ppu.start_time; + const std::string local_dir = vfs::get(Emu.GetDir()); const auto dirsz = fs::get_dir_size(local_dir, 1024); + // This function is very slow by nature + // TODO: Check if after first use the result is being cached so the sleep can be reduced in this case + lv2_sleep(utils::sub_saturate(dirsz == umax ? 2000 : 200000, get_guest_system_time() - start_sleep), &ppu); + if (dirsz == umax) { const auto error = fs::g_tls_error; @@ -616,7 +668,8 @@ error_code cellGameDataGetSizeKB(vm::ptr size) return CELL_GAMEDATA_ERROR_FAILURE; } - *size = ::narrow(dirsz / 1024); + ppu.check_state(); + *size = ::narrow(dirsz / 1024); return CELL_OK; } @@ -650,6 +703,8 @@ error_code cellGameBootCheck(vm::ptr type, vm::ptr attributes, vm::ptr auto& perm = g_fxo->get(); + lv2_sleep(500); + const auto init = perm.init.init(); if (!init) @@ -662,11 +717,13 @@ error_code cellGameBootCheck(vm::ptr type, vm::ptr attributes, vm::ptr const std::string& cat = Emu.GetFakeCat(); + u32 _type{}; + if (cat == "DG") { perm.mode = content_permission::check_mode::disc_game; - *type = CELL_GAME_GAMETYPE_DISC; + _type = CELL_GAME_GAMETYPE_DISC; *attributes = 0; // TODO // TODO: dirName might be a read only string when BootCheck is called on a disc game. (e.g. Ben 10 Ultimate Alien: Cosmic Destruction) @@ -676,7 +733,7 @@ error_code cellGameBootCheck(vm::ptr type, vm::ptr attributes, vm::ptr { perm.mode = content_permission::check_mode::patch; - *type = CELL_GAME_GAMETYPE_DISC; + _type = CELL_GAME_GAMETYPE_DISC; *attributes = CELL_GAME_ATTRIBUTE_PATCH; // TODO sfo = psf::load_object(vfs::get(Emu.GetDir() + "PARAM.SFO")); @@ -685,13 +742,15 @@ error_code cellGameBootCheck(vm::ptr type, vm::ptr attributes, vm::ptr { perm.mode = content_permission::check_mode::hdd_game; - *type = CELL_GAME_GAMETYPE_HDD; + _type = CELL_GAME_GAMETYPE_HDD; *attributes = 0; // TODO sfo = psf::load_object(vfs::get(Emu.GetDir() + "PARAM.SFO")); dir = Emu.GetTitleID(); } + *type = _type; + if (size) { // TODO: Use the free space of the computer's HDD where RPCS3 is being run. @@ -702,7 +761,7 @@ error_code cellGameBootCheck(vm::ptr type, vm::ptr attributes, vm::ptr size->sysSizeKB = 4; } - if (*type == u32{CELL_GAME_GAMETYPE_HDD} && dirName) + if (_type == u32{CELL_GAME_GAMETYPE_HDD} && dirName) { strcpy_trunc(*dirName, Emu.GetTitleID()); } @@ -718,6 +777,8 @@ error_code cellGamePatchCheck(vm::ptr size, vm::ptr r { cellGame.warning("cellGamePatchCheck(size=*0x%x, reserved=*0x%x)", size, reserved); + lv2_sleep(5000); + if (Emu.GetCat() != "GD") { return CELL_GAME_ERROR_NOTPATCH; @@ -778,9 +839,14 @@ error_code cellGameDataCheck(u32 type, vm::cptr dirName, vm::ptr dirName, vm::ptrhddFreeSizeKB = 40 * 1024 * 1024 - 1; // Read explanation in cellHddGameCheck // TODO: Calculate data size for game data, if necessary. - size->sizeKB = CELL_GAME_SIZEKB_NOTCALC; + size->sizeKB = sfo.empty() ? 0 : CELL_GAME_SIZEKB_NOTCALC; size->sysSizeKB = 0; // TODO } @@ -826,7 +892,7 @@ error_code cellGameDataCheck(u32 type, vm::cptr dirName, vm::ptr contentInfoPath, vm::ptr usrdirPath) +error_code cellGameContentPermit(ppu_thread& ppu, vm::ptr contentInfoPath, vm::ptr usrdirPath) { cellGame.warning("cellGameContentPermit(contentInfoPath=*0x%x, usrdirPath=*0x%x)", contentInfoPath, usrdirPath); @@ -854,6 +920,10 @@ error_code cellGameContentPermit(vm::ptr contentInfoPa return CELL_OK; } + lv2_obj::sleep(ppu); + + const u64 start_sleep = ppu.start_time; + if (!perm.temp.empty()) { // Create PARAM.SFO @@ -882,6 +952,9 @@ error_code cellGameContentPermit(vm::ptr contentInfoPa ensure(temp.commit()); } + // This function is very slow by nature + lv2_sleep(utils::sub_saturate(!perm.temp.empty() || perm.can_create ? 200000 : 2000, get_guest_system_time() - start_sleep), &ppu); + // Cleanup perm.reset(); @@ -892,7 +965,7 @@ error_code cellGameContentPermit(vm::ptr contentInfoPa error_code cellGameDataCheckCreate2(ppu_thread& ppu, u32 version, vm::cptr dirName, u32 errDialog, vm::ptr funcStat, u32 container) { - cellGame.error("cellGameDataCheckCreate2(version=0x%x, dirName=%s, errDialog=0x%x, funcStat=*0x%x, container=%d)", version, dirName, errDialog, funcStat, container); + cellGame.success("cellGameDataCheckCreate2(version=0x%x, dirName=%s, errDialog=0x%x, funcStat=*0x%x, container=%d)", version, dirName, errDialog, funcStat, container); //older sdk. it might not care about game type. @@ -954,6 +1027,8 @@ error_code cellGameDataCheckCreate2(ppu_thread& ppu, u32 version, vm::cptr strcpy_trunc(cbGet->getParam.titleLang[i], psf::get_string(sfo, fmt::format("TITLE_%02d", i))); } + lv2_sleep(5000, &ppu); + funcStat(ppu, cbResult, cbGet, cbSet); std::string error_msg; @@ -970,6 +1045,8 @@ error_code cellGameDataCheckCreate2(ppu_thread& ppu, u32 version, vm::cptr // Game confirmed that it wants to create directory const auto setParam = cbSet->setParam; + lv2_sleep(2000, &ppu); + if (new_data) { if (!setParam) @@ -1065,6 +1142,10 @@ error_code cellGameDataCheckCreate2(ppu_thread& ppu, u32 version, vm::cptr return CELL_GAMEDATA_ERROR_INTERNAL; } } + else + { + lv2_sleep(2000, &ppu); + } return CELL_GAMEDATA_ERROR_CBRESULT; } @@ -1079,7 +1160,7 @@ error_code cellGameDataCheckCreate(ppu_thread& ppu, u32 version, vm::cptr error_code cellGameCreateGameData(vm::ptr init, vm::ptr tmp_contentInfoPath, vm::ptr tmp_usrdirPath) { - cellGame.error("cellGameCreateGameData(init=*0x%x, tmp_contentInfoPath=*0x%x, tmp_usrdirPath=*0x%x)", init, tmp_contentInfoPath, tmp_usrdirPath); + cellGame.success("cellGameCreateGameData(init=*0x%x, tmp_contentInfoPath=*0x%x, tmp_usrdirPath=*0x%x)", init, tmp_contentInfoPath, tmp_usrdirPath); if (!init) { @@ -1090,6 +1171,8 @@ error_code cellGameCreateGameData(vm::ptr init, vm::ptr init, vm::ptr value) return CELL_GAME_ERROR_PARAM; } + lv2_sleep(2000); + auto& perm = g_fxo->get(); const auto init = perm.init.access(); @@ -1359,6 +1447,8 @@ error_code cellGameGetParamString(s32 id, vm::ptr buf, u32 bufsize) auto& perm = g_fxo->get(); + lv2_sleep(2000); + const auto init = perm.init.access(); if (!init || perm.mode == content_permission::check_mode::not_set) @@ -1401,6 +1491,8 @@ error_code cellGameSetParamString(s32 id, vm::cptr buf) return CELL_GAME_ERROR_PARAM; } + lv2_sleep(2000); + auto& perm = g_fxo->get(); const auto init = perm.init.access(); @@ -1427,7 +1519,7 @@ error_code cellGameSetParamString(s32 id, vm::cptr buf) return CELL_OK; } -error_code cellGameGetSizeKB(vm::ptr size) +error_code cellGameGetSizeKB(ppu_thread& ppu, vm::ptr size) { cellGame.warning("cellGameGetSizeKB(size=*0x%x)", size); @@ -1438,6 +1530,7 @@ error_code cellGameGetSizeKB(vm::ptr size) // Always reset to 0 at start *size = 0; + ppu.state += cpu_flag::wait; auto& perm = g_fxo->get(); @@ -1448,10 +1541,18 @@ error_code cellGameGetSizeKB(vm::ptr size) return CELL_GAME_ERROR_FAILURE; } + lv2_obj::sleep(ppu); + + const u64 start_sleep = ppu.start_time; + const std::string local_dir = !perm.temp.empty() ? perm.temp : vfs::get("/dev_hdd0/game/" + perm.dir); const auto dirsz = fs::get_dir_size(local_dir, 1024); + // This function is very slow by nature + // TODO: Check if after first use the result is being cached so the sleep can be reduced in this case + lv2_sleep(utils::sub_saturate(dirsz == umax ? 1000 : 200000, get_guest_system_time() - start_sleep), &ppu); + if (dirsz == umax) { const auto error = fs::g_tls_error; @@ -1467,7 +1568,8 @@ error_code cellGameGetSizeKB(vm::ptr size) } } - *size = ::narrow(dirsz / 1024); + ppu.check_state(); + *size = ::narrow(dirsz / 1024); return CELL_OK; } @@ -1708,6 +1810,8 @@ error_code cellDiscGameGetBootDiscInfo(vm::ptr getP // Always sets 0 at first dword write_to_ptr(getParam->titleId, 0); + lv2_sleep(2000); + // This is also called by non-disc games, see NPUB90029 static const std::string dir = "/dev_bdvd/PS3_GAME"s; diff --git a/rpcs3/Emu/Cell/Modules/cellHttpUtil.cpp b/rpcs3/Emu/Cell/Modules/cellHttpUtil.cpp index e57872679e..1d1cd2fd8e 100644 --- a/rpcs3/Emu/Cell/Modules/cellHttpUtil.cpp +++ b/rpcs3/Emu/Cell/Modules/cellHttpUtil.cpp @@ -132,7 +132,7 @@ error_code cellHttpUtilParseUri(vm::ptr uri, vm::cptr str, vm parseError = "Error, URI didn't contain a slash"; break; default: - parseError = "Error, unkown error #" + std::to_string(static_cast(URL.m_ErrorCode)); + parseError = "Error, unknown error #" + std::to_string(static_cast(URL.m_ErrorCode)); break; } cellHttpUtil.error("%s, while parsing URI, %s.", parseError, str.get_ptr()); diff --git a/rpcs3/Emu/Cell/Modules/cellKb.cpp b/rpcs3/Emu/Cell/Modules/cellKb.cpp index b90eef2f12..f6ae9433d2 100644 --- a/rpcs3/Emu/Cell/Modules/cellKb.cpp +++ b/rpcs3/Emu/Cell/Modules/cellKb.cpp @@ -6,8 +6,8 @@ #include "Emu/Io/KeyboardHandler.h" #include "cellKb.h" -extern void libio_sys_config_init(); -extern void libio_sys_config_end(); +error_code sys_config_start(ppu_thread& ppu); +error_code sys_config_stop(ppu_thread& ppu); extern bool is_input_allowed(); @@ -61,7 +61,7 @@ void KeyboardHandlerBase::save(utils::serial& ar) ar(inited ? m_info.max_connect : 0); } -error_code cellKbInit(u32 max_connect) +error_code cellKbInit(ppu_thread& ppu, u32 max_connect) { sys_io.warning("cellKbInit(max_connect=%d)", max_connect); @@ -78,13 +78,13 @@ error_code cellKbInit(u32 max_connect) return CELL_KB_ERROR_INVALID_PARAMETER; } - libio_sys_config_init(); + sys_config_start(ppu); handler.Init(std::min(max_connect, 7u)); return CELL_OK; } -error_code cellKbEnd() +error_code cellKbEnd(ppu_thread& ppu) { sys_io.notice("cellKbEnd()"); @@ -96,7 +96,7 @@ error_code cellKbEnd() return CELL_KB_ERROR_UNINITIALIZED; // TODO - libio_sys_config_end(); + sys_config_stop(ppu); return CELL_OK; } diff --git a/rpcs3/Emu/Cell/Modules/cellMic.cpp b/rpcs3/Emu/Cell/Modules/cellMic.cpp index 60fd57734f..cfd6e427ec 100644 --- a/rpcs3/Emu/Cell/Modules/cellMic.cpp +++ b/rpcs3/Emu/Cell/Modules/cellMic.cpp @@ -73,7 +73,7 @@ void mic_context::operator()() // Timestep in microseconds constexpr u64 TIMESTEP = 256ull * 1'000'000ull / 48000ull; u64 timeout = 0; - u64 oldvalue = 0; + u32 oldvalue = 0; while (thread_ctrl::state() != thread_state::aborting) { diff --git a/rpcs3/Emu/Cell/Modules/cellMic.h b/rpcs3/Emu/Cell/Modules/cellMic.h index aba0d8bbfa..0bd637fa90 100644 --- a/rpcs3/Emu/Cell/Modules/cellMic.h +++ b/rpcs3/Emu/Cell/Modules/cellMic.h @@ -374,7 +374,7 @@ public: static constexpr auto thread_name = "Microphone Thread"sv; protected: - atomic_t wakey = 0; + atomic_t wakey = 0; // u32 signalStateLocalTalk = 9; // value is in range 0-10. 10 indicates talking, 0 indicating none. // u32 signalStateFarTalk = 0; // value is in range 0-10. 10 indicates talking from far away, 0 indicating none. diff --git a/rpcs3/Emu/Cell/Modules/cellMouse.cpp b/rpcs3/Emu/Cell/Modules/cellMouse.cpp index 76437c2f46..f7ae70bfb4 100644 --- a/rpcs3/Emu/Cell/Modules/cellMouse.cpp +++ b/rpcs3/Emu/Cell/Modules/cellMouse.cpp @@ -7,8 +7,8 @@ #include "cellMouse.h" -extern void libio_sys_config_init(); -extern void libio_sys_config_end(); +error_code sys_config_start(ppu_thread& ppu); +error_code sys_config_stop(ppu_thread& ppu); extern bool is_input_allowed(); @@ -61,7 +61,7 @@ void MouseHandlerBase::save(utils::serial& ar) ar(inited ? m_info.max_connect : 0); } -error_code cellMouseInit(u32 max_connect) +error_code cellMouseInit(ppu_thread& ppu, u32 max_connect) { sys_io.notice("cellMouseInit(max_connect=%d)", max_connect); @@ -78,7 +78,7 @@ error_code cellMouseInit(u32 max_connect) return CELL_MOUSE_ERROR_INVALID_PARAMETER; } - libio_sys_config_init(); + sys_config_start(ppu); handler.Init(std::min(max_connect, 7u)); return CELL_OK; @@ -121,7 +121,7 @@ error_code cellMouseClearBuf(u32 port_no) return CELL_OK; } -error_code cellMouseEnd() +error_code cellMouseEnd(ppu_thread& ppu) { sys_io.notice("cellMouseEnd()"); @@ -133,7 +133,7 @@ error_code cellMouseEnd() return CELL_MOUSE_ERROR_UNINITIALIZED; // TODO - libio_sys_config_end(); + sys_config_stop(ppu); return CELL_OK; } diff --git a/rpcs3/Emu/Cell/Modules/cellMsgDialog.cpp b/rpcs3/Emu/Cell/Modules/cellMsgDialog.cpp index 290c185e17..1204ff1773 100644 --- a/rpcs3/Emu/Cell/Modules/cellMsgDialog.cpp +++ b/rpcs3/Emu/Cell/Modules/cellMsgDialog.cpp @@ -164,7 +164,7 @@ error_code open_msg_dialog(bool is_blocking, u32 type, vm::cptr msgString, return CellSysutilError{ret + 0u}; } - const auto notify = std::make_shared>(false); + const auto notify = std::make_shared>(0); const auto res = manager->create()->show(is_blocking, msgString.get_ptr(), _type, [callback, userData, &return_code, is_blocking, notify](s32 status) { @@ -186,7 +186,7 @@ error_code open_msg_dialog(bool is_blocking, u32 type, vm::cptr msgString, if (is_blocking && notify) { - *notify = true; + *notify = 1; notify->notify_one(); } }); @@ -533,13 +533,15 @@ error_code cellMsgDialogAbort() sysutil_send_system_cmd(CELL_SYSUTIL_DRAWING_END, 0); return CELL_OK; } + + return CELL_OK; // Not CELL_MSGDIALOG_ERROR_DIALOG_NOT_OPENED, tested on HW. } const auto dlg = g_fxo->get().get(); if (!dlg) { - return CELL_MSGDIALOG_ERROR_DIALOG_NOT_OPENED; + return CELL_OK; // Not CELL_MSGDIALOG_ERROR_DIALOG_NOT_OPENED, tested on HW. } if (!dlg->state.compare_and_swap_test(MsgDialogState::Open, MsgDialogState::Abort)) diff --git a/rpcs3/Emu/Cell/Modules/cellMusicDecode.cpp b/rpcs3/Emu/Cell/Modules/cellMusicDecode.cpp index 1ad732df55..0ae0cd80b4 100644 --- a/rpcs3/Emu/Cell/Modules/cellMusicDecode.cpp +++ b/rpcs3/Emu/Cell/Modules/cellMusicDecode.cpp @@ -256,7 +256,7 @@ error_code cell_music_decode_read(vm::ptr buf, vm::ptr startTime, u64 { dec.read_pos = 0; dec.decoder.clear(); - dec.decoder.track_fully_consumed = true; + dec.decoder.track_fully_consumed = 1; dec.decoder.track_fully_consumed.notify_one(); break; } diff --git a/rpcs3/Emu/Cell/Modules/cellPad.cpp b/rpcs3/Emu/Cell/Modules/cellPad.cpp index 629a7e3c93..b960db944d 100644 --- a/rpcs3/Emu/Cell/Modules/cellPad.cpp +++ b/rpcs3/Emu/Cell/Modules/cellPad.cpp @@ -3,13 +3,14 @@ #include "Emu/system_config.h" #include "Emu/Cell/PPUModule.h" #include "Emu/Cell/lv2/sys_process.h" +#include "Emu/Cell/lv2/sys_sync.h" #include "Emu/Io/pad_types.h" #include "Input/pad_thread.h" #include "Input/product_info.h" #include "cellPad.h" -extern void libio_sys_config_init(); -extern void libio_sys_config_end(); +error_code sys_config_start(ppu_thread& ppu); +error_code sys_config_stop(ppu_thread& ppu); extern bool is_input_allowed(); @@ -52,19 +53,112 @@ void fmt_class_string::format(std::string& out, u64 arg) }); } +extern void sys_io_serialize(utils::serial& ar); + pad_info::pad_info(utils::serial& ar) : max_connect(ar) , port_setting(ar) { + sys_io_serialize(ar); } void pad_info::save(utils::serial& ar) { + USING_SERIALIZATION_VERSION(sys_io); + ar(max_connect, port_setting); + + sys_io_serialize(ar); } +extern void send_sys_io_connect_event(usz index, u32 state); -error_code cellPadInit(u32 max_connect) +void cellPad_NotifyStateChange(usz index, u32 /*state*/) +{ + auto info = g_fxo->try_get(); + + if (!info) + { + return; + } + + std::lock_guard lock(pad::g_pad_mutex); + + if (index >= info->get_max_connect()) + { + return; + } + + const auto handler = pad::get_current_handler(); + const auto& pads = handler->GetPads(); + const auto& pad = pads[index]; + + pad_data_internal& reported_info = info->reported_info[index]; + const u32 old_status = reported_info.port_status; + + // Ignore sent status for now, use the latest instead + // NOTE 1: The state's CONNECTED bit should currently be identical to the current + // m_port_status CONNECTED bit when called from our pad handlers. + // NOTE 2: Make sure to propagate all other status bits to the reported status. + const u32 new_status = pads[index]->m_port_status; + + if (~(old_status ^ new_status) & CELL_PAD_STATUS_CONNECTED) + { + // old and new have the same connection status + return; + } + + reported_info.port_status = new_status | CELL_PAD_STATUS_ASSIGN_CHANGES; + reported_info.device_capability = pad->m_device_capability; + reported_info.device_type = pad->m_device_type; + reported_info.pclass_type = pad->m_class_type; + reported_info.pclass_profile = pad->m_class_profile; + + if (pad->m_vendor_id == 0 || pad->m_product_id == 0) + { + // Fallback to defaults + + input::product_info product; + + switch (pad->m_class_type) + { + case CELL_PAD_PCLASS_TYPE_GUITAR: + product = input::get_product_info(input::product_type::red_octane_gh_guitar); + break; + case CELL_PAD_PCLASS_TYPE_DRUM: + product = input::get_product_info(input::product_type::red_octane_gh_drum_kit); + break; + case CELL_PAD_PCLASS_TYPE_DJ: + product = input::get_product_info(input::product_type::dj_hero_turntable); + break; + case CELL_PAD_PCLASS_TYPE_DANCEMAT: + product = input::get_product_info(input::product_type::dance_dance_revolution_mat); + break; + case CELL_PAD_PCLASS_TYPE_NAVIGATION: + product = input::get_product_info(input::product_type::ps_move_navigation); + break; + case CELL_PAD_PCLASS_TYPE_STANDARD: + default: + product = input::get_product_info(input::product_type::playstation_3_controller); + break; + } + + reported_info.vendor_id = product.vendor_id; + reported_info.product_id = product.product_id; + } + else + { + reported_info.vendor_id = pad->m_vendor_id; + reported_info.product_id = pad->m_product_id; + } +} + +extern void pad_state_notify_state_change(usz index, u32 state) +{ + cellPad_NotifyStateChange(index, state); +} + +error_code cellPadInit(ppu_thread& ppu, u32 max_connect) { sys_io.warning("cellPadInit(max_connect=%d)", max_connect); @@ -78,13 +172,33 @@ error_code cellPadInit(u32 max_connect) if (max_connect == 0 || max_connect > CELL_MAX_PADS) return CELL_PAD_ERROR_INVALID_PARAMETER; - libio_sys_config_init(); - config.max_connect = std::min(max_connect, CELL_PAD_MAX_PORT_NUM); + sys_config_start(ppu); + + config.max_connect = max_connect; config.port_setting.fill(CELL_PAD_SETTING_PRESS_OFF | CELL_PAD_SETTING_SENSOR_OFF); + config.reported_info = {}; + + std::array statuses{}; + + const auto handler = pad::get_current_handler(); + + const auto& pads = handler->GetPads(); + + for (usz i = 0; i < statuses.size(); ++i) + { + if (i >= config.get_max_connect()) + break; + + if (pads[i]->m_port_status & CELL_PAD_STATUS_CONNECTED) + { + send_sys_io_connect_event(i, CELL_PAD_STATUS_CONNECTED); + } + } + return CELL_OK; } -error_code cellPadEnd() +error_code cellPadEnd(ppu_thread& ppu) { sys_io.notice("cellPadEnd()"); @@ -95,7 +209,7 @@ error_code cellPadEnd() if (!config.max_connect.exchange(0)) return CELL_PAD_ERROR_UNINITIALIZED; - libio_sys_config_end(); + sys_config_stop(ppu); return CELL_OK; } @@ -138,12 +252,12 @@ error_code cellPadClearBuf(u32 port_no) const auto& pads = handler->GetPads(); - if (port_no >= config.max_connect) + if (port_no >= config.get_max_connect()) return CELL_PAD_ERROR_NO_DEVICE; const auto& pad = pads[port_no]; - if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (!config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) return not_an_error(CELL_PAD_ERROR_NO_DEVICE); clear_pad_buffer(pad); @@ -151,39 +265,7 @@ error_code cellPadClearBuf(u32 port_no) return CELL_OK; } -void pad_get_data(u32 port_no, CellPadData* data); - -error_code cellPadGetData(u32 port_no, vm::ptr data) -{ - sys_io.trace("cellPadGetData(port_no=%d, data=*0x%x)", port_no, data); - - std::lock_guard lock(pad::g_pad_mutex); - - auto& config = g_fxo->get(); - - if (!config.max_connect) - return CELL_PAD_ERROR_UNINITIALIZED; - - const auto handler = pad::get_current_handler(); - - if (port_no >= CELL_MAX_PADS || !data) - return CELL_PAD_ERROR_INVALID_PARAMETER; - - const auto& pads = handler->GetPads(); - - if (port_no >= config.max_connect) - return CELL_PAD_ERROR_NO_DEVICE; - - const auto& pad = pads[port_no]; - - if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) - return not_an_error(CELL_PAD_ERROR_NO_DEVICE); - - pad_get_data(port_no, data.get_ptr()); - return CELL_OK; -} - -void pad_get_data(u32 port_no, CellPadData* data) +void pad_get_data(u32 port_no, CellPadData* data, bool get_periph_data = false) { auto& config = g_fxo->get(); const auto handler = pad::get_current_handler(); @@ -196,7 +278,7 @@ void pad_get_data(u32 port_no, CellPadData* data) return; } - const auto setting = config.port_setting[port_no]; + const u32 setting = config.port_setting[port_no]; bool btnChanged = false; if (rinfo.ignore_input || !is_input_allowed()) @@ -383,6 +465,131 @@ void pad_get_data(u32 port_no, CellPadData* data) data->button[CELL_PAD_BTN_OFFSET_SENSOR_G] = pad->m_sensor_g; } } + + if (!get_periph_data || data->len <= CELL_PAD_LEN_CHANGE_SENSOR_ON) + { + return; + } + + const auto get_pressure_value = [setting](u16 val, u16 min, u16 max) -> u16 + { + if (setting & CELL_PAD_SETTING_PRESS_ON) + { + return std::clamp(val, min, max); + } + + if (val > 0) + { + return max; + } + + return 0; + }; + + // TODO: support for 'unique' controllers, which goes in offsets 24+ in padData (CELL_PAD_PCLASS_BTN_OFFSET) + // TODO: update data->len accordingly + + switch (pad->m_class_profile) + { + default: + case CELL_PAD_PCLASS_TYPE_STANDARD: + case CELL_PAD_PCLASS_TYPE_NAVIGATION: + { + break; + } + case CELL_PAD_PCLASS_TYPE_GUITAR: + { + data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_FRET_1] = get_pressure_value(0, 0x0, 0xFF); + data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_FRET_2] = get_pressure_value(0, 0x0, 0xFF); + data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_FRET_3] = get_pressure_value(0, 0x0, 0xFF); + data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_FRET_4] = get_pressure_value(0, 0x0, 0xFF); + data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_FRET_5] = get_pressure_value(0, 0x0, 0xFF); + data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_STRUM_UP] = get_pressure_value(0, 0x0, 0xFF); + data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_STRUM_DOWN] = get_pressure_value(0, 0x0, 0xFF); + data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_WHAMMYBAR] = 0x80; // 0x80 – 0xFF + data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_FRET_H1] = get_pressure_value(0, 0x0, 0xFF); + data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_FRET_H2] = get_pressure_value(0, 0x0, 0xFF); + data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_FRET_H3] = get_pressure_value(0, 0x0, 0xFF); + data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_FRET_H4] = get_pressure_value(0, 0x0, 0xFF); + data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_FRET_H5] = get_pressure_value(0, 0x0, 0xFF); + data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_5WAY_EFFECT] = 0x0019; // One of 5 values: 0x0019, 0x004C, 0x007F (or 0x0096), 0x00B2, 0x00E5 (or 0x00E2) + data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_TILT_SENS] = get_pressure_value(0, 0x0, 0xFF); + break; + } + case CELL_PAD_PCLASS_TYPE_DRUM: + { + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DRUM_SNARE] = get_pressure_value(0, 0x0, 0xFF); + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DRUM_TOM] = get_pressure_value(0, 0x0, 0xFF); + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DRUM_TOM2] = get_pressure_value(0, 0x0, 0xFF); + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DRUM_TOM_FLOOR] = get_pressure_value(0, 0x0, 0xFF); + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DRUM_KICK] = get_pressure_value(0, 0x0, 0xFF); + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DRUM_CYM_HiHAT] = get_pressure_value(0, 0x0, 0xFF); + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DRUM_CYM_CRASH] = get_pressure_value(0, 0x0, 0xFF); + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DRUM_CYM_RIDE] = get_pressure_value(0, 0x0, 0xFF); + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DRUM_KICK2] = get_pressure_value(0, 0x0, 0xFF); + break; + } + case CELL_PAD_PCLASS_TYPE_DJ: + { + // First deck + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DJ_MIXER_ATTACK] = 0; // 0x0 or 0xFF + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DJ_MIXER_CROSSFADER] = 0; // 0x0 - 0x3FF + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DJ_MIXER_DSP_DIAL] = 0; // 0x0 - 0x3FF + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DJ_DECK1_STREAM1] = 0; // 0x0 or 0xFF + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DJ_DECK1_STREAM2] = 0; // 0x0 or 0xFF + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DJ_DECK1_STREAM3] = 0; // 0x0 or 0xFF + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DJ_DECK1_PLATTER] = 0x80; // 0x0 - 0xFF (neutral: 0x80) + + // Second deck + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DJ_DECK2_STREAM1] = 0; // 0x0 or 0xFF + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DJ_DECK2_STREAM2] = 0; // 0x0 or 0xFF + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DJ_DECK2_STREAM3] = 0; // 0x0 or 0xFF + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DJ_DECK2_PLATTER] = 0x80; // 0x0 - 0xFF (neutral: 0x80) + break; + } + case CELL_PAD_PCLASS_TYPE_DANCEMAT: + { + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DANCEMAT_CIRCLE] = 0; // 0x0 or 0xFF + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DANCEMAT_CROSS] = 0; // 0x0 or 0xFF + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DANCEMAT_TRIANGLE] = 0; // 0x0 or 0xFF + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DANCEMAT_SQUARE] = 0; // 0x0 or 0xFF + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DANCEMAT_RIGHT] = 0; // 0x0 or 0xFF + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DANCEMAT_LEFT] = 0; // 0x0 or 0xFF + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DANCEMAT_UP] = 0; // 0x0 or 0xFF + data->button[CELL_PAD_PCLASS_BTN_OFFSET_DANCEMAT_DOWN] = 0; // 0x0 or 0xFF + break; + } + } +} + +error_code cellPadGetData(u32 port_no, vm::ptr data) +{ + sys_io.trace("cellPadGetData(port_no=%d, data=*0x%x)", port_no, data); + + std::lock_guard lock(pad::g_pad_mutex); + + auto& config = g_fxo->get(); + + if (!config.max_connect) + return CELL_PAD_ERROR_UNINITIALIZED; + + const auto handler = pad::get_current_handler(); + + if (port_no >= CELL_MAX_PADS || !data) + return CELL_PAD_ERROR_INVALID_PARAMETER; + + const auto& pads = handler->GetPads(); + + if (port_no >= config.get_max_connect()) + return CELL_PAD_ERROR_NO_DEVICE; + + const auto& pad = pads[port_no]; + + if (!config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + return not_an_error(CELL_PAD_ERROR_NO_DEVICE); + + pad_get_data(port_no, data.get_ptr()); + return CELL_OK; } error_code cellPadPeriphGetInfo(vm::ptr info) @@ -406,25 +613,37 @@ error_code cellPadPeriphGetInfo(vm::ptr info) std::memset(info.get_ptr(), 0, sizeof(CellPadPeriphInfo)); info->max_connect = config.max_connect; - info->now_connect = rinfo.now_connect; info->system_info = rinfo.system_info; - const auto& pads = handler->GetPads(); + u32 now_connect = 0; for (u32 i = 0; i < CELL_PAD_MAX_PORT_NUM; ++i) { - if (i >= config.max_connect) + if (i >= config.get_max_connect()) break; - info->port_status[i] = pads[i]->m_port_status; - pads[i]->m_port_status &= ~CELL_PAD_STATUS_ASSIGN_CHANGES; + pad_data_internal& reported_info = config.reported_info[i]; + + info->port_status[i] = reported_info.port_status; info->port_setting[i] = config.port_setting[i]; - info->device_capability[i] = pads[i]->m_device_capability; - info->device_type[i] = pads[i]->m_device_type; - info->pclass_type[i] = pads[i]->m_class_type; - info->pclass_profile[i] = pads[i]->m_class_profile; + + reported_info.port_status &= ~CELL_PAD_STATUS_ASSIGN_CHANGES; + + if (~reported_info.port_status & CELL_PAD_STATUS_CONNECTED) + { + continue; + } + + info->device_capability[i] = reported_info.device_capability; + info->device_type[i] = reported_info.device_type; + info->pclass_type[i] = reported_info.pclass_type; + info->pclass_profile[i] = reported_info.pclass_profile; + + now_connect++; } + info->now_connect = now_connect; + return CELL_OK; } @@ -447,20 +666,19 @@ error_code cellPadPeriphGetData(u32 port_no, vm::ptr data) const auto& pads = handler->GetPads(); - if (port_no >= config.max_connect) + if (port_no >= config.get_max_connect()) return CELL_PAD_ERROR_NO_DEVICE; const auto& pad = pads[port_no]; - if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (!config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) return not_an_error(CELL_PAD_ERROR_NO_DEVICE); - pad_get_data(port_no, &data->cellpad_data); + pad_get_data(port_no, &data->cellpad_data, true); data->pclass_type = pad->m_class_type; data->pclass_profile = pad->m_class_profile; - // TODO: support for 'unique' controllers, which goes in offsets 24+ in padData (CELL_PAD_PCLASS_BTN_OFFSET) return CELL_OK; } @@ -482,12 +700,12 @@ error_code cellPadGetRawData(u32 port_no, vm::ptr data) const auto& pads = handler->GetPads(); - if (port_no >= config.max_connect) + if (port_no >= config.get_max_connect()) return CELL_PAD_ERROR_NO_DEVICE; const auto& pad = pads[port_no]; - if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (!config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) return not_an_error(CELL_PAD_ERROR_NO_DEVICE); // ? @@ -548,12 +766,12 @@ error_code cellPadSetActDirect(u32 port_no, vm::ptr param) const auto& pads = handler->GetPads(); - if (port_no >= config.max_connect) + if (port_no >= config.get_max_connect()) return CELL_PAD_ERROR_NO_DEVICE; const auto& pad = pads[port_no]; - if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (!config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) return not_an_error(CELL_PAD_ERROR_NO_DEVICE); // TODO: find out if this is checked here or later or at all @@ -585,56 +803,32 @@ error_code cellPadGetInfo(vm::ptr info) const PadInfo& rinfo = handler->GetInfo(); info->max_connect = config.max_connect; - info->now_connect = rinfo.now_connect; info->system_info = rinfo.system_info; - const auto& pads = handler->GetPads(); + u32 now_connect = 0; for (u32 i = 0; i < CELL_MAX_PADS; ++i) { - if (i >= config.max_connect) + if (i >= config.get_max_connect()) break; - pads[i]->m_port_status &= ~CELL_PAD_STATUS_ASSIGN_CHANGES; // TODO: should ASSIGN flags be cleared here? - info->status[i] = pads[i]->m_port_status; + pad_data_internal& reported_info = config.reported_info[i]; + reported_info.port_status &= ~CELL_PAD_STATUS_ASSIGN_CHANGES; // TODO: should ASSIGN flags be cleared here? - if (pads[i]->m_vendor_id == 0 || pads[i]->m_product_id == 0) + info->status[i] = reported_info.port_status; + + if (~reported_info.port_status & CELL_PAD_STATUS_CONNECTED) { - // Fallback to defaults - - input::product_info product; - - switch (pads[i]->m_class_type) - { - case CELL_PAD_PCLASS_TYPE_GUITAR: - product = input::get_product_info(input::product_type::red_octane_gh_guitar); - break; - case CELL_PAD_PCLASS_TYPE_DRUM: - product = input::get_product_info(input::product_type::red_octane_gh_drum_kit); - break; - case CELL_PAD_PCLASS_TYPE_DJ: - product = input::get_product_info(input::product_type::dj_hero_turntable); - break; - case CELL_PAD_PCLASS_TYPE_DANCEMAT: - product = input::get_product_info(input::product_type::dance_dance_revolution_mat); - break; - case CELL_PAD_PCLASS_TYPE_NAVIGATION: - case CELL_PAD_PCLASS_TYPE_STANDARD: - default: - product = input::get_product_info(input::product_type::playstation_3_controller); - break; - } - - info->vendor_id[i] = product.vendor_id; - info->product_id[i] = product.product_id; - } - else - { - info->vendor_id[i] = pads[i]->m_vendor_id; - info->product_id[i] = pads[i]->m_product_id; + continue; } + + info->vendor_id[i] = reported_info.vendor_id; + info->product_id[i] = reported_info.product_id; + + now_connect++; } + info->now_connect = now_connect; return CELL_OK; } @@ -657,24 +851,37 @@ error_code cellPadGetInfo2(vm::ptr info) std::memset(info.get_ptr(), 0, sizeof(CellPadInfo2)); const PadInfo& rinfo = handler->GetInfo(); - info->max_connect = config.max_connect; - info->now_connect = rinfo.now_connect; + info->max_connect = config.get_max_connect(); // Here it is forcibly clamped info->system_info = rinfo.system_info; + u32 now_connect = 0; + const auto& pads = handler->GetPads(); for (u32 i = 0; i < CELL_PAD_MAX_PORT_NUM; ++i) { - if (i >= config.max_connect) + if (i >= config.get_max_connect()) break; - info->port_status[i] = pads[i]->m_port_status; - pads[i]->m_port_status &= ~CELL_PAD_STATUS_ASSIGN_CHANGES; + pad_data_internal& reported_info = config.reported_info[i]; + + info->port_status[i] = reported_info.port_status; info->port_setting[i] = config.port_setting[i]; + + reported_info.port_status &= ~CELL_PAD_STATUS_ASSIGN_CHANGES; + + if (~reported_info.port_status & CELL_PAD_STATUS_CONNECTED) + { + continue; + } + info->device_capability[i] = pads[i]->m_device_capability; info->device_type[i] = pads[i]->m_device_type; + + now_connect++; } + info->now_connect = now_connect; return CELL_OK; } @@ -696,12 +903,12 @@ error_code cellPadGetCapabilityInfo(u32 port_no, vm::ptr const auto& pads = handler->GetPads(); - if (port_no >= config.max_connect) + if (port_no >= config.get_max_connect()) return CELL_PAD_ERROR_NO_DEVICE; const auto& pad = pads[port_no]; - if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (!config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_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 @@ -754,12 +961,12 @@ error_code cellPadInfoPressMode(u32 port_no) const auto& pads = handler->GetPads(); - if (port_no >= config.max_connect) + if (port_no >= config.get_max_connect()) return CELL_PAD_ERROR_NO_DEVICE; const auto& pad = pads[port_no]; - if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (!config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_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); @@ -783,12 +990,12 @@ error_code cellPadInfoSensorMode(u32 port_no) const auto& pads = handler->GetPads(); - if (port_no >= config.max_connect) + if (port_no >= config.get_max_connect()) return CELL_PAD_ERROR_NO_DEVICE; const auto& pad = pads[port_no]; - if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (!config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_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); diff --git a/rpcs3/Emu/Cell/Modules/cellPad.h b/rpcs3/Emu/Cell/Modules/cellPad.h index 5b7c713b19..6219ada00a 100644 --- a/rpcs3/Emu/Cell/Modules/cellPad.h +++ b/rpcs3/Emu/Cell/Modules/cellPad.h @@ -24,92 +24,6 @@ enum CellPadFilterError : u32 CELL_PADFILTER_ERROR_INVALID_PARAMETER = 0x80121401, }; -// Controller types -enum -{ - CELL_PAD_PCLASS_TYPE_STANDARD = 0x00, - CELL_PAD_PCLASS_TYPE_GUITAR = 0x01, - CELL_PAD_PCLASS_TYPE_DRUM = 0x02, - CELL_PAD_PCLASS_TYPE_DJ = 0x03, - CELL_PAD_PCLASS_TYPE_DANCEMAT = 0x04, - CELL_PAD_PCLASS_TYPE_NAVIGATION = 0x05, -}; - -// Profile of a Standard Type Controller -// Profile of a Navigation Type Controller -// Bits 0 – 31 All 0s - -// Profile of a Guitar Type Controller -enum -{ - // Basic - CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_1 = 0x00000001, - CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_2 = 0x00000002, - CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_3 = 0x00000004, - CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_4 = 0x00000008, - CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_5 = 0x00000010, - CELL_PAD_PCLASS_PROFILE_GUITAR_STRUM_UP = 0x00000020, - CELL_PAD_PCLASS_PROFILE_GUITAR_STRUM_DOWN = 0x00000040, - CELL_PAD_PCLASS_PROFILE_GUITAR_WHAMMYBAR = 0x00000080, - // All Basic = 0x000000FF - - // Optional - CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_H1 = 0x00000100, - CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_H2 = 0x00000200, - CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_H3 = 0x00000400, - CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_H4 = 0x00000800, - CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_H5 = 0x00001000, - CELL_PAD_PCLASS_PROFILE_GUITAR_5WAY_EFFECT = 0x00002000, - CELL_PAD_PCLASS_PROFILE_GUITAR_TILT_SENS = 0x00004000, - // All = 0x00007FFF -}; - -// Profile of a Drum Type Controller -enum -{ - CELL_PAD_PCLASS_PROFILE_DRUM_SNARE = 0x00000001, - CELL_PAD_PCLASS_PROFILE_DRUM_TOM = 0x00000002, - CELL_PAD_PCLASS_PROFILE_DRUM_TOM2 = 0x00000004, - CELL_PAD_PCLASS_PROFILE_DRUM_TOM_FLOOR = 0x00000008, - CELL_PAD_PCLASS_PROFILE_DRUM_KICK = 0x00000010, - CELL_PAD_PCLASS_PROFILE_DRUM_CYM_HiHAT = 0x00000020, - CELL_PAD_PCLASS_PROFILE_DRUM_CYM_CRASH = 0x00000040, - CELL_PAD_PCLASS_PROFILE_DRUM_CYM_RIDE = 0x00000080, - CELL_PAD_PCLASS_PROFILE_DRUM_KICK2 = 0x00000100, - // All = 0x000001FF -}; - -// Profile of a DJ Deck Type Controller -enum -{ - CELL_PAD_PCLASS_PROFILE_DJ_MIXER_ATTACK = 0x00000001, - CELL_PAD_PCLASS_PROFILE_DJ_MIXER_CROSSFADER = 0x00000002, - CELL_PAD_PCLASS_PROFILE_DJ_MIXER_DSP_DIAL = 0x00000004, - CELL_PAD_PCLASS_PROFILE_DJ_DECK1_STREAM1 = 0x00000008, - CELL_PAD_PCLASS_PROFILE_DJ_DECK1_STREAM2 = 0x00000010, - CELL_PAD_PCLASS_PROFILE_DJ_DECK1_STREAM3 = 0x00000020, - CELL_PAD_PCLASS_PROFILE_DJ_DECK1_PLATTER = 0x00000040, - CELL_PAD_PCLASS_PROFILE_DJ_DECK2_STREAM1 = 0x00000080, - CELL_PAD_PCLASS_PROFILE_DJ_DECK2_STREAM2 = 0x00000100, - CELL_PAD_PCLASS_PROFILE_DJ_DECK2_STREAM3 = 0x00000200, - CELL_PAD_PCLASS_PROFILE_DJ_DECK2_PLATTER = 0x00000400, - // All = 0x000007FF -}; - -// Profile of a Dance Mat Type Controller -enum -{ - CELL_PAD_PCLASS_PROFILE_DANCEMAT_CIRCLE = 0x00000001, - CELL_PAD_PCLASS_PROFILE_DANCEMAT_CROSS = 0x00000002, - CELL_PAD_PCLASS_PROFILE_DANCEMAT_TRIANGLE = 0x00000004, - CELL_PAD_PCLASS_PROFILE_DANCEMAT_SQUARE = 0x00000008, - CELL_PAD_PCLASS_PROFILE_DANCEMAT_RIGHT = 0x00000010, - CELL_PAD_PCLASS_PROFILE_DANCEMAT_LEFT = 0x00000020, - CELL_PAD_PCLASS_PROFILE_DANCEMAT_UP = 0x00000040, - CELL_PAD_PCLASS_PROFILE_DANCEMAT_DOWN = 0x00000080, - // All = 0x000000FF -}; - // Length returned in CellPadData struct enum { @@ -126,6 +40,17 @@ enum CELL_PADFILTER_IIR_CUTOFF_2ND_LPF_BT_010 = 2, // 10% Nyquist frequency }; +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; +}; + struct CellPadInfo { be_t max_connect; @@ -192,14 +117,27 @@ struct pad_info { atomic_t max_connect = 0; std::array port_setting{ 0 }; + std::array reported_info{}; SAVESTATE_INIT_POS(11); pad_info() = default; pad_info(utils::serial& ar); void save(utils::serial& ar); + + u32 get_max_connect() const + { + return std::min(max_connect, CELL_PAD_MAX_PORT_NUM); + } + + // Unreliable way the firmware uses to optimize away pad calls for disconnected pads + // This result relies on data updates from config events on a dedicated thread to receive them + bool is_reportedly_connected(u32 port_no) const + { + return port_no < get_max_connect() && !!(reported_info[port_no].port_status & CELL_PAD_STATUS_CONNECTED); + } }; error_code cellPadGetData(u32 port_no, vm::ptr data); -error_code cellPadInit(u32 max_connect); +error_code cellPadInit(ppu_thread& ppu, u32 max_connect); error_code cellPadSetPortSetting(u32 port_no, u32 port_setting); diff --git a/rpcs3/Emu/Cell/Modules/cellRec.cpp b/rpcs3/Emu/Cell/Modules/cellRec.cpp index 77064e248f..53165ef6a5 100644 --- a/rpcs3/Emu/Cell/Modules/cellRec.cpp +++ b/rpcs3/Emu/Cell/Modules/cellRec.cpp @@ -158,14 +158,14 @@ public: has_error = false; } - void add_frame(std::vector& frame, const u32 width, const u32 height, s32 pixel_format, usz timestamp_ms) override + void add_frame(std::vector& frame, u32 pitch, u32 width, u32 height, s32 pixel_format, usz timestamp_ms) override { std::lock_guard lock(m_mtx); if (m_flush) return; - m_frames_to_encode.emplace_back(timestamp_ms, width, height, pixel_format, std::move(frame)); + m_frames_to_encode.emplace_back(timestamp_ms, pitch, width, height, pixel_format, std::move(frame)); } encoder_frame get_frame() @@ -445,6 +445,7 @@ void rec_info::set_audio_params(s32 audio_format) case CELL_REC_PARAM_AUDIO_FMT_PCM_1536K: audio_codec_id = 65556; // AV_CODEC_ID_PCM_F32BE //audio_codec_id = 65557; // AV_CODEC_ID_PCM_F32LE // TODO: maybe this one? + break; default: audio_codec_id = 86018; // AV_CODEC_ID_AAC break; @@ -587,7 +588,7 @@ void rec_info::start_image_provider() { std::vector frame(frame_size); std::memcpy(frame.data(), video_input_buffer.get_ptr(), frame.size()); - encoder->add_frame(frame, input_format.pitch, input_format.height, input_format.av_pixel_format, timestamp_ms); + encoder->add_frame(frame, input_format.pitch, input_format.width, input_format.height, input_format.av_pixel_format, timestamp_ms); } } @@ -680,7 +681,7 @@ void rec_info::stop_image_provider(bool flush) { const usz pos = (start_offset + i) % video_ringbuffer.size(); utils::image_sink::encoder_frame& frame_data = video_ringbuffer[pos]; - encoder->add_frame(frame_data.data, frame_data.width, frame_data.height, frame_data.av_pixel_format, encoder->get_timestamp_ms(frame_data.pts - start_pts)); + encoder->add_frame(frame_data.data, frame_data.pitch, frame_data.width, frame_data.height, frame_data.av_pixel_format, encoder->get_timestamp_ms(frame_data.pts - start_pts)); // TODO: add audio data to encoder } diff --git a/rpcs3/Emu/Cell/Modules/cellSaveData.cpp b/rpcs3/Emu/Cell/Modules/cellSaveData.cpp index 648c94b225..6e078fe650 100644 --- a/rpcs3/Emu/Cell/Modules/cellSaveData.cpp +++ b/rpcs3/Emu/Cell/Modules/cellSaveData.cpp @@ -1547,21 +1547,34 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v return {CELL_SAVEDATA_ERROR_PARAM, "57 (attribute=0x%x)", statSet->setParam->attribute}; } - if (g_ps3_process_info.sdk_ver > 0x36FFFF) + if (statSet->setParam->parental_level > 11) { - // In firmware 3.70 or higher parental_level was changed to reserved2 and has to zeroes - if (statSet->setParam->parental_level) - { - // ****** sysutil savedata parameter error : 58 ****** - return {CELL_SAVEDATA_ERROR_PARAM, "58 (sdk_ver=0x%x, parental_level=%d)", g_ps3_process_info.sdk_ver, statSet->setParam->parental_level}; - } + // ****** sysutil savedata parameter error : 58 ****** + return {CELL_SAVEDATA_ERROR_PARAM, "58 (sdk_ver=0x%x, parental_level=%d)", g_ps3_process_info.sdk_ver, statSet->setParam->parental_level}; } - else + + // Note: in firmware 3.70 or higher parental_level was changed to reserved2 + + for (usz index = 0;; index++) { - if (statSet->setParam->parental_level > 11) + // Convert to pointer to avoid UB when accessing out of range + const u8 c = (+statSet->setParam->listParam)[index]; + + if (c == 0 || index >= (g_ps3_process_info.sdk_ver > 0x36FFFF ? std::size(statSet->setParam->listParam) - 1 : std::size(statSet->setParam->listParam))) { - // ****** sysutil savedata parameter error : 58 ****** - return {CELL_SAVEDATA_ERROR_PARAM, "58 (sdk_ver=0x%x, parental_level=%d)", g_ps3_process_info.sdk_ver, statSet->setParam->parental_level}; + if (c) + { + // ****** sysutil savedata parameter error : 76 ****** + return {CELL_SAVEDATA_ERROR_PARAM, "76 (listParam=0x%016x)", std::bit_cast>(statSet->setParam->listParam)}; + } + + break; + } + + if ((c < 'A' || c > 'Z') && (c < '0' || c > '9') && c != '-' && c != '_') + { + // ****** sysutil savedata parameter error : 77 ****** + return {CELL_SAVEDATA_ERROR_PARAM, "77 (listParam=0x%016x)", std::bit_cast>(statSet->setParam->listParam)}; } } diff --git a/rpcs3/Emu/Cell/Modules/cellSpurs.cpp b/rpcs3/Emu/Cell/Modules/cellSpurs.cpp index 721a1c82a3..3d5c535058 100644 --- a/rpcs3/Emu/Cell/Modules/cellSpurs.cpp +++ b/rpcs3/Emu/Cell/Modules/cellSpurs.cpp @@ -2477,8 +2477,8 @@ s32 _spurs::add_workload(ppu_thread& ppu, vm::ptr spurs, vm::ptr spurs_res += 127; spurs_res2 += 127; - spurs_res.notify_all(-128); - spurs_res2.notify_all(-128); + spurs_res.notify_all(); + spurs_res2.notify_all(); u32 res_wkl; const auto wkl = &spurs->wklInfo(wnum); diff --git a/rpcs3/Emu/Cell/Modules/cellVdec.cpp b/rpcs3/Emu/Cell/Modules/cellVdec.cpp index d7f4fd2f0e..cb73b918c0 100644 --- a/rpcs3/Emu/Cell/Modules/cellVdec.cpp +++ b/rpcs3/Emu/Cell/Modules/cellVdec.cpp @@ -303,7 +303,7 @@ struct vdec_context final return; } - thread_ctrl::wait_on(in_cmd, nullptr); + thread_ctrl::wait_on(in_cmd); slice = in_cmd.pop_all(); // Pop new command list }()) { @@ -921,7 +921,7 @@ static error_code vdecOpen(ppu_thread& ppu, T type, U res, vm::cptr }); thrd->state -= cpu_flag::stop; - thrd->state.notify_one(cpu_flag::stop); + thrd->state.notify_one(); return CELL_OK; } diff --git a/rpcs3/Emu/Cell/Modules/sceNp.cpp b/rpcs3/Emu/Cell/Modules/sceNp.cpp index da84bb05a5..a4ef65114d 100644 --- a/rpcs3/Emu/Cell/Modules/sceNp.cpp +++ b/rpcs3/Emu/Cell/Modules/sceNp.cpp @@ -17,6 +17,7 @@ #include "Emu/Cell/lv2/sys_time.h" #include "Emu/Cell/lv2/sys_fs.h" +#include "Emu/Cell/lv2/sys_sync.h" #include "Emu/NP/np_handler.h" #include "Emu/NP/np_contexts.h" #include "Emu/NP/np_helpers.h" @@ -408,6 +409,8 @@ void message_data::print() const sceNp.notice("commId: %s, msgId: %d, mainType: %d, subType: %d, subject: %s, body: %s, data_size: %d", static_cast(commId.data), msgId, mainType, subType, subject, body, data.size()); } +extern void lv2_sleep(u64 timeout, ppu_thread* ppu = nullptr); + error_code sceNpInit(u32 poolsize, vm::ptr poolptr) { sceNp.warning("sceNpInit(poolsize=0x%x, poolptr=*0x%x)", poolsize, poolptr); @@ -553,18 +556,40 @@ error_code npDrmIsAvailable(vm::cptr k_licensee_addr, vm::cptr drm_pat return CELL_OK; } -error_code sceNpDrmIsAvailable(vm::cptr k_licensee_addr, vm::cptr drm_path) +error_code sceNpDrmIsAvailable(ppu_thread& ppu, vm::cptr k_licensee_addr, vm::cptr drm_path) { sceNp.warning("sceNpDrmIsAvailable(k_licensee=*0x%x, drm_path=*0x%x)", k_licensee_addr, drm_path); - return npDrmIsAvailable(k_licensee_addr, drm_path); + if (!drm_path) + { + return SCE_NP_DRM_ERROR_INVALID_PARAM; + } + + lv2_obj::sleep(ppu); + + const auto ret = npDrmIsAvailable(k_licensee_addr, drm_path); + lv2_sleep(100000, &ppu); + + return ret; } -error_code sceNpDrmIsAvailable2(vm::cptr k_licensee_addr, vm::cptr drm_path) +error_code sceNpDrmIsAvailable2(ppu_thread& ppu, vm::cptr k_licensee_addr, vm::cptr drm_path) { sceNp.warning("sceNpDrmIsAvailable2(k_licensee=*0x%x, drm_path=*0x%x)", k_licensee_addr, drm_path); - return npDrmIsAvailable(k_licensee_addr, drm_path); + if (!drm_path) + { + return SCE_NP_DRM_ERROR_INVALID_PARAM; + } + + lv2_obj::sleep(ppu); + + const auto ret = npDrmIsAvailable(k_licensee_addr, drm_path); + + // TODO: Accurate sleep time + //lv2_sleep(20000, &ppu); + + return ret; } error_code npDrmVerifyUpgradeLicense(vm::cptr content_id) diff --git a/rpcs3/Emu/Cell/Modules/sys_io_.cpp b/rpcs3/Emu/Cell/Modules/sys_io_.cpp index abbdb53cd5..fd69113e43 100644 --- a/rpcs3/Emu/Cell/Modules/sys_io_.cpp +++ b/rpcs3/Emu/Cell/Modules/sys_io_.cpp @@ -3,6 +3,10 @@ #include "Emu/IdManager.h" #include "Emu/Cell/PPUModule.h" +#include "Emu/Cell/lv2/sys_event.h" +#include "Emu/Cell/lv2/sys_ppu_thread.h" +#include "Emu/Cell/Modules/sysPrxForUser.h" + LOG_CHANNEL(sys_io); extern void cellPad_init(); @@ -13,53 +17,157 @@ struct libio_sys_config { shared_mutex mtx; s32 init_ctr = 0; - u32 stack_addr = 0; + u32 ppu_id = 0; + u32 queue_id = 0; ~libio_sys_config() noexcept { - if (stack_addr) - { - ensure(vm::dealloc(stack_addr, vm::stack)); - } + } + + void save_or_load(utils::serial& ar) + { + ar(init_ctr, ppu_id, queue_id); } }; -// Only exists internally (has no name) -extern void libio_sys_config_init() +extern void sys_io_serialize(utils::serial& ar) +{ + // Do not assign a serialization tag for now, call it from cellPad serialization + g_fxo->get().save_or_load(ar); +} + +extern void cellPad_NotifyStateChange(usz index, u32 state); + +void config_event_entry(ppu_thread& ppu) { auto& cfg = g_fxo->get(); - std::lock_guard lock(cfg.mtx); + if (!ppu.loaded_from_savestate) + { + // Ensure awake + ppu.check_state(); + } + + while (!sys_event_queue_receive(ppu, cfg.queue_id, vm::null, 0)) + { + if (ppu.is_stopped()) + { + return; + } + + // Some delay + thread_ctrl::wait_for(10000); + + // Wakeup + ppu.check_state(); + + const u64 arg1 = ppu.gpr[5]; + const u64 arg2 = ppu.gpr[6]; + const u64 arg3 = ppu.gpr[7]; + + // TODO: Reverse-engineer proper event system + + if (arg1 == 1) + { + cellPad_NotifyStateChange(arg2, arg3); + } + } + + ppu_execute<&sys_ppu_thread_exit>(ppu, 0); +} + +std::unique_lock lock_lv2_mutex_alike(shared_mutex& mtx, ppu_thread* ppu) +{ + std::unique_lock lock(mtx, std::defer_lock); + + while (!lock.try_lock()) + { + if (ppu) + { + // Could not be acquired, put PPU to sleep + lv2_obj::sleep(*ppu); + } + + // Wait for unlock without owning the lock + mtx.lock_unlock(); + + if (ppu) + { + // Awake, still not owning + ppu->check_state(); + } + } + + return lock; +} + +extern void send_sys_io_connect_event(usz index, u32 state) +{ + auto& cfg = g_fxo->get(); + + auto lock = lock_lv2_mutex_alike(cfg.mtx, cpu_thread::get_current()); + + if (cfg.init_ctr) + { + if (auto port = idm::get(cfg.queue_id)) + { + port->send(0, 1, index, state); + } + } +} + +error_code sys_config_start(ppu_thread& ppu) +{ + sys_io.warning("sys_config_start()"); + + auto& cfg = g_fxo->get(); + + auto lock = lock_lv2_mutex_alike(cfg.mtx, &ppu); if (cfg.init_ctr++ == 0) { - // Belongs to "_cfg_evt_hndlr" thread (8k stack) - cfg.stack_addr = ensure(vm::alloc(0x2000, vm::stack, 4096)); + // Run thread + vm::var _tid; + vm::var queue_id; + vm::var _name = vm::make_str("_cfg_evt_hndlr"); + + vm::var attr; + attr->protocol = SYS_SYNC_PRIORITY; + attr->type = SYS_PPU_QUEUE; + attr->name_u64 = 0; + + ensure(CELL_OK == sys_event_queue_create(ppu, queue_id, attr, 0, 0x20)); + ppu.check_state(); + cfg.queue_id = *queue_id; + + ensure(CELL_OK == ppu_execute<&sys_ppu_thread_create>(ppu, +_tid, g_fxo->get().func_addr(FIND_FUNC(config_event_entry)), 0, 512, 0x2000, SYS_PPU_THREAD_CREATE_JOINABLE, +_name)); + ppu.check_state(); + + cfg.ppu_id = static_cast(*_tid); } -} - -extern void libio_sys_config_end() -{ - auto& cfg = g_fxo->get(); - - std::lock_guard lock(cfg.mtx); - - if (cfg.init_ctr-- == 1) - { - ensure(vm::dealloc(std::exchange(cfg.stack_addr, 0), vm::stack)); - } -} - -error_code sys_config_start() -{ - sys_io.todo("sys_config_start()"); return CELL_OK; } -error_code sys_config_stop() +error_code sys_config_stop(ppu_thread& ppu) { - sys_io.todo("sys_config_stop()"); + sys_io.warning("sys_config_stop()"); + + auto& cfg = g_fxo->get(); + + auto lock = lock_lv2_mutex_alike(cfg.mtx, &ppu); + + if (cfg.init_ctr && cfg.init_ctr-- == 1) + { + ensure(CELL_OK == sys_event_queue_destroy(ppu, cfg.queue_id, SYS_EVENT_QUEUE_DESTROY_FORCE)); + ppu.check_state(); + ensure(CELL_OK == sys_ppu_thread_join(ppu, cfg.ppu_id, +vm::var{})); + } + else + { + // TODO: Unknown error + } + return CELL_OK; } @@ -114,4 +222,6 @@ DECLARE(ppu_module_manager::sys_io)("sys_io", []() REG_FUNC(sys_io, sys_config_register_service); REG_FUNC(sys_io, sys_config_unregister_io_error_handler); REG_FUNC(sys_io, sys_config_unregister_service); + + REG_HIDDEN_FUNC(config_event_entry); }); diff --git a/rpcs3/Emu/Cell/PPUAnalyser.cpp b/rpcs3/Emu/Cell/PPUAnalyser.cpp index ebda9c2580..2968190f41 100644 --- a/rpcs3/Emu/Cell/PPUAnalyser.cpp +++ b/rpcs3/Emu/Cell/PPUAnalyser.cpp @@ -532,6 +532,11 @@ namespace ppu_patterns bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::basic_string& applied, std::function check_aborted) { + if (segs.empty()) + { + return false; + } + // Assume first segment is executable const u32 start = segs[0].addr; @@ -549,7 +554,38 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b std::vector> func_queue; // Known references (within segs, addr and value alignment = 4) - std::set addr_heap{entry}; + std::set addr_heap; + + if (entry) + { + addr_heap.emplace(entry); + } + + auto verify_func = [&](u32 addr) + { + if (entry) + { + // Fixed addresses + return true; + } + + // Check if the storage address exists within relocations + + for (auto& rel : this->relocs) + { + if ((rel.addr & -8) == (addr & -8)) + { + if (rel.type != 38 && rel.type != 44 && (rel.addr & -4) != (addr & -4)) + { + continue; + } + + return true; + } + } + + return false; + }; // Register new function auto add_func = [&](u32 addr, u32 toc, u32 caller) -> ppu_function& @@ -612,7 +648,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b for (; _ptr <= seg_end;) { - if (ptr[1] == toc && ptr[0] >= start && ptr[0] < end && ptr[0] % 4 == 0) + if (ptr[1] == toc && FN(x >= start && x < end && x % 4 == 0)(ptr[0]) && verify_func(_ptr.addr())) { // New function ppu_log.trace("OPD*: [0x%x] 0x%x (TOC=0x%x)", _ptr, ptr[0], ptr[1]); @@ -668,6 +704,11 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b // Find OPD section for (const auto& sec : secs) { + if (sec.size % 8) + { + continue; + } + vm::cptr sec_end = vm::cast(sec.addr + sec.size); // Probe @@ -691,17 +732,17 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b const u32 _toc = ptr[1]; // Rough Table of Contents borders - //const u32 _toc_begin = _toc - 0x8000; - //const u32 _toc_end = _toc + 0x8000; + const u32 toc_begin = _toc - 0x8000; + //const u32 toc_end = _toc + 0x7ffc; // TODO: improve TOC constraints - if (_toc % 4 || !get_ptr(_toc) || _toc >= 0x40000000 || (_toc >= start && _toc < end)) + if (toc_begin % 4 || !get_ptr(toc_begin) || toc_begin >= 0x40000000 || (toc_begin >= start && toc_begin < end)) { sec_end.set(0); break; } - if (addr % 4 || addr < start || addr >= end || addr == _toc) + if (addr % 4 || addr < start || addr >= end || !verify_func(_ptr.addr())) { sec_end.set(0); break; @@ -754,6 +795,11 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b // Find .eh_frame section for (const auto& sec : secs) { + if (sec.size % 4) + { + continue; + } + vm::cptr sec_end = vm::cast(sec.addr + sec.size); // Probe @@ -923,7 +969,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b continue; } - if (target >= start && target < end) + if (target >= start && target < end && (~ptr[0] & 0x2 || verify_func(_ptr.addr()))) { auto& new_func = add_func(target, func.toc, func.addr); @@ -951,7 +997,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b // Simple trampoline const u32 target = (ptr[0] << 16) + ppu_opcode_t{ptr[1]}.simm16; - if (target >= start && target < end) + if (target >= start && target < end && verify_func(_ptr.addr())) { auto& new_func = add_func(target, func.toc, func.addr); @@ -1022,7 +1068,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b const u32 target = (ptr[3] << 16) + s16(ptr[4]); const u32 toc_add = (ptr[1] << 16) + s16(ptr[2]); - if (target >= start && target < end) + if (target >= start && target < end && verify_func((_ptr + 3).addr())) { auto& new_func = add_func(target, 0, func.addr); @@ -1069,7 +1115,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b const u32 toc_add = (ptr[1] << 16) + s16(ptr[2]); const u32 target = (ptr[3] & 0x2 ? 0 : (_ptr + 3).addr()) + ppu_opcode_t{ptr[3]}.bt24; - if (target >= start && target < end) + if (target >= start && target < end && (~ptr[3] & 0x2 || verify_func((_ptr + 3).addr()))) { auto& new_func = add_func(target, 0, func.addr); @@ -1349,9 +1395,9 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b block.second = _ptr.addr() - block.first; break; } - else if (type == ppu_itype::TW || type == ppu_itype::TWI || type == ppu_itype::TD || type == ppu_itype::TDI) + else if (type & ppu_itype::trap) { - if (op.opcode != ppu_instructions::TRAP()) + if (op.bo != 31) { add_block(_ptr.addr()); } @@ -1432,7 +1478,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b { const u32 target = (op.aa ? 0 : iaddr) + (type == ppu_itype::B ? +op.bt24 : +op.bt14); - if (target >= start && target < end) + if (target >= start && target < end && (!op.aa || verify_func(iaddr))) { if (target < func.addr || target >= func.addr + func.size) { @@ -1577,6 +1623,8 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b end = 0; } + u32 per_instruction_bytes = 0; + for (auto&& [_, func] : as_rvalue(fmap)) { if (func.attr & ppu_attr::no_size && entry) @@ -1595,6 +1643,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b block.attr = ppu_attr::no_size; } + per_instruction_bytes += utils::sub_saturate(lim, func.addr); continue; } @@ -1675,11 +1724,8 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b u32 exp = start; u32 lim = end; - // Start with full scan (disabled for PRX for now) - if (entry) - { - block_queue.emplace_back(exp, lim); - } + // Start with full scan + block_queue.emplace_back(exp, lim); // Add entries from patches (on per-instruction basis) for (u32 addr : applied) @@ -1713,14 +1759,17 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b { u32 i_pos = exp; + u32 block_edges[16]; + u32 edge_count = 0; + bool is_good = true; bool is_fallback = true; for (; i_pos < lim; i_pos += 4) { - const u32 opc = get_ref(i_pos); + const ppu_opcode_t op{get_ref(i_pos)}; - switch (auto type = s_ppu_itype.decode(opc)) + switch (auto type = s_ppu_itype.decode(op.opcode)) { case ppu_itype::UNK: case ppu_itype::ECIWX: @@ -1730,10 +1779,20 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b is_good = false; break; } - case ppu_itype::TD: case ppu_itype::TDI: - case ppu_itype::TW: case ppu_itype::TWI: + { + if (op.ra == 1u || op.ra == 13u || op.ra == 2u) + { + // Non-user registers, checking them against a constant value makes no sense + is_good = false; + break; + } + + [[fallthrough]]; + } + case ppu_itype::TD: + case ppu_itype::TW: case ppu_itype::B: case ppu_itype::BC: { @@ -1744,14 +1803,14 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b if (type == ppu_itype::B || type == ppu_itype::BC) { - if (entry == 0 && ppu_opcode_t{opc}.aa) + if (entry == 0 && op.aa) { // Ignore absolute branches in PIC (PRX) is_good = false; break; } - const u32 target = (opc & 2 ? 0 : i_pos) + (type == ppu_itype::B ? +ppu_opcode_t{opc}.bt24 : +ppu_opcode_t{opc}.bt14); + const u32 target = (op.aa ? 0 : i_pos) + (type == ppu_itype::B ? +op.bt24 : +op.bt14); if (target < segs[0].addr || target >= segs[0].addr + segs[0].size) { @@ -1760,9 +1819,43 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b break; } + const ppu_opcode_t test_op{get_ref(target)}; + const auto type0 = s_ppu_itype.decode(test_op.opcode); + + if (type0 == ppu_itype::UNK) + { + is_good = false; + break; + } + + // Test another instruction just in case (testing more is unlikely to improve results by much) + if (!(type0 & ppu_itype::branch)) + { + if (target + 4 >= segs[0].addr + segs[0].size) + { + is_good = false; + break; + } + + const auto type1 = s_ppu_itype.decode(get_ref(target + 4)); + + if (type1 == ppu_itype::UNK) + { + is_good = false; + break; + } + } + else if (u32 target0 = (test_op.aa ? 0 : target) + (type == ppu_itype::B ? +test_op.bt24 : +test_op.bt14); + target0 < segs[0].addr || target0 >= segs[0].addr + segs[0].size) + { + // Sanity check + is_good = false; + break; + } + if (target != i_pos && !fmap.contains(target)) { - if (block_set.count(target) == 0) + if (block_set.count(target) == 0 && std::count(block_edges, block_edges + edge_count, target) == 0) { ppu_log.trace("Block target found: 0x%x (i_pos=0x%x)", target, i_pos); block_queue.emplace_back(target, 0); @@ -1777,27 +1870,38 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b case ppu_itype::BCLR: case ppu_itype::SC: { - if (type == ppu_itype::SC && opc != ppu_instructions::SC(0)) + if (type == ppu_itype::SC && op.opcode != ppu_instructions::SC(0)) { // Strict garbage filter is_good = false; break; } - if (type == ppu_itype::BCCTR && opc & 0xe000) + if (type == ppu_itype::BCCTR && op.opcode & 0xe000) { // Garbage filter is_good = false; break; } - if (type == ppu_itype::BCLR && opc & 0xe000) + if (type == ppu_itype::BCLR && op.opcode & 0xe000) { // Garbage filter is_good = false; break; } + if ((type & ppu_itype::branch && op.lk) || type & ppu_itype::trap || type == ppu_itype::BC) + { + // if farther instructions are valid: register all blocks + // Otherwise, register none (all or nothing) + if (edge_count < std::size(block_edges)) + { + block_edges[edge_count++] = i_pos + 4; + continue; + } + } + // Good block terminator found, add single block break; } @@ -1828,17 +1932,23 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b if (is_good) { - auto& block = fmap[exp]; - - if (!block.addr) + for (u32 it = 0, prev_addr = exp; it <= edge_count; it++) { - block.addr = exp; - block.size = i_pos - exp; - ppu_log.trace("Block __0x%x added (size=0x%x)", block.addr, block.size); + const u32 block_end = it < edge_count ? block_edges[it] : i_pos; + const u32 block_begin = std::exchange(prev_addr, block_end); - if (get_limit(exp) == end) + auto& block = fmap[block_begin]; + + if (!block.addr) { - block.attr += ppu_attr::no_size; + block.addr = block_begin; + block.size = block_end - block_begin; + ppu_log.trace("Block __0x%x added (size=0x%x)", block.addr, block.size); + + if (get_limit(block_begin) == end) + { + block.attr += ppu_attr::no_size; + } } } } @@ -1861,9 +1971,8 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b // Convert map to vector (destructive) for (auto&& [_, block] : as_rvalue(std::move(fmap))) { - if (block.attr & ppu_attr::no_size && block.size > 4 && entry) + if (block.attr & ppu_attr::no_size && block.size > 4) { - // Disabled for PRX for now ppu_log.warning("Block 0x%x will be compiled on per-instruction basis (size=0x%x)", block.addr, block.size); for (u32 addr = block.addr; addr < block.addr + block.size; addr += 4) @@ -1875,12 +1984,19 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b i.attr = ppu_attr::no_size; } + per_instruction_bytes += block.size; continue; } funcs.emplace_back(std::move(block)); } + if (per_instruction_bytes) + { + const bool error = per_instruction_bytes >= 200 && per_instruction_bytes / 4 >= utils::aligned_div(funcs.size(), 128); + (error ? ppu_log.error : ppu_log.notice)("%d instructions will be compiled on per-instruction basis in total", per_instruction_bytes / 4); + } + ppu_log.notice("Block analysis: %zu blocks (%zu enqueued)", funcs.size(), block_queue.size()); return true; } diff --git a/rpcs3/Emu/Cell/PPUAnalyser.h b/rpcs3/Emu/Cell/PPUAnalyser.h index cde7fb7949..ccbd743f92 100644 --- a/rpcs3/Emu/Cell/PPUAnalyser.h +++ b/rpcs3/Emu/Cell/PPUAnalyser.h @@ -168,19 +168,19 @@ struct ppu_module } template requires requires (const U& obj) { +obj.size() * 0; } - to_be_t& get_ref(U&& addr, + to_be_t& get_ref(U&& addr, u32 index = 0, u32 line = __builtin_LINE(), u32 col = __builtin_COLUMN(), const char* file = __builtin_FILE(), const char* func = __builtin_FUNCTION()) const { constexpr usz size_element = std::is_void_v ? 0 : sizeof(std::conditional_t, char, T>); - if (auto ptr = get_ptr(addr.addr(), u32{size_element})) + if (auto ptr = get_ptr((addr + index).addr(), u32{size_element})) { return *ptr; } - fmt::throw_exception("get_ref(): Failure! (addr=0x%x)%s", addr.addr(), src_loc{line, col, file, func}); + fmt::throw_exception("get_ref(): Failure! (addr=0x%x)%s", (addr + index).addr(), src_loc{line, col, file, func}); return *std::add_pointer_t>{}; } }; @@ -262,6 +262,9 @@ struct ppu_pattern_matrix // PPU Instruction Type struct ppu_itype { + static constexpr struct branch_tag{} branch{}; // Branch Instructions + static constexpr struct trap_tag{} trap{}; // Branch Instructions + enum type { UNK = 0, @@ -423,8 +426,6 @@ struct ppu_itype VUPKLSB, VUPKLSH, VXOR, - TDI, - TWI, MULLI, SUBFIC, CMPLI, @@ -432,11 +433,8 @@ struct ppu_itype ADDIC, ADDI, ADDIS, - BC, SC, - B, MCRF, - BCLR, CRNOR, CRANDC, ISYNC, @@ -446,7 +444,6 @@ struct ppu_itype CREQV, CRORC, CROR, - BCCTR, RLWIMI, RLWINM, RLWNM, @@ -463,7 +460,6 @@ struct ppu_itype RLDCL, RLDCR, CMP, - TW, LVSL, LVEBX, SUBFC, @@ -490,7 +486,6 @@ struct ppu_itype LWZUX, CNTLZD, ANDC, - TD, LVEWX, MULHD, MULHW, @@ -781,6 +776,16 @@ struct ppu_itype FCTID_, FCTIDZ_, FCFID_, + + B, // branch_tag first + BC, + BCLR, + BCCTR, // branch_tag last + + TD, // trap_tag first + TW, + TDI, + TWI, // trap_tag last }; // Enable address-of operator for ppu_decoder<> @@ -788,6 +793,16 @@ struct ppu_itype { return value; } + + friend constexpr bool operator &(type value, branch_tag) + { + return value >= B && value <= BCCTR; + } + + friend constexpr bool operator &(type value, trap_tag) + { + return value >= TD && value <= TWI; + } }; struct ppu_iname diff --git a/rpcs3/Emu/Cell/PPUDisAsm.cpp b/rpcs3/Emu/Cell/PPUDisAsm.cpp index 3f3564bbe5..6505bc44a1 100644 --- a/rpcs3/Emu/Cell/PPUDisAsm.cpp +++ b/rpcs3/Emu/Cell/PPUDisAsm.cpp @@ -106,12 +106,7 @@ std::pair PPUDisAsm::try_get_const_op_gpr_value(u32 re const auto type = s_ppu_itype.decode(opcode); - auto is_branch = [](enum ppu_itype::type itype) - { - return itype == ppu_itype::BC || itype == ppu_itype::B || itype == ppu_itype::BCLR || itype == ppu_itype::BCCTR; - }; - - if (is_branch(type) || type == ppu_itype::UNK) + if (type & ppu_itype::branch || type == ppu_itype::UNK) { // TODO: Detect calls, ignore them if reg is a non-volatile register return {}; diff --git a/rpcs3/Emu/Cell/PPUInterpreter.cpp b/rpcs3/Emu/Cell/PPUInterpreter.cpp index 53e18db82b..f5d06ed57a 100644 --- a/rpcs3/Emu/Cell/PPUInterpreter.cpp +++ b/rpcs3/Emu/Cell/PPUInterpreter.cpp @@ -5204,7 +5204,9 @@ auto LVRX() static const auto exec = [](ppu_thread& ppu, ppu_opcode_t op) { const u64 addr = op.ra ? ppu.gpr[op.ra] + ppu.gpr[op.rb] : ppu.gpr[op.rb]; - const u128 data = ppu_feed_data(ppu, addr & -16); + + // Read from instruction address if offset is 0, this prevents accessing potentially bad memory from addr (because no actual memory is dereferenced) + const u128 data = ppu_feed_data(ppu, ((addr & 15) == 0 ? ppu.cia : addr) & -16); ppu.vr[op.vd] = data >> ((~addr & 15) * 8) >> 8; }; diff --git a/rpcs3/Emu/Cell/PPUModule.cpp b/rpcs3/Emu/Cell/PPUModule.cpp index f311763a42..bfd25b2297 100644 --- a/rpcs3/Emu/Cell/PPUModule.cpp +++ b/rpcs3/Emu/Cell/PPUModule.cpp @@ -13,6 +13,7 @@ #include "Emu/VFS.h" #include "Emu/Cell/PPUOpcodes.h" +#include "Emu/Cell/SPUThread.h" #include "Emu/Cell/PPUAnalyser.h" #include "Emu/Cell/lv2/sys_process.h" @@ -552,7 +553,7 @@ extern const std::unordered_map& get_exported_function_na } // Resolve relocations for variable/function linkage. -static void ppu_patch_refs(std::vector* out_relocs, u32 fref, u32 faddr) +static void ppu_patch_refs(const ppu_module& _module, std::vector* out_relocs, u32 fref, u32 faddr) { struct ref_t { @@ -561,7 +562,7 @@ static void ppu_patch_refs(std::vector* out_relocs, u32 fref, u32 fad be_t addend; // Note: Treating it as addend seems to be correct for now, but still unknown if theres more in this variable }; - for (auto ref = vm::ptr::make(fref); ref->type; ref++) + for (const ref_t* ref = &_module.get_ref(fref); ref->type; fref += sizeof(ref_t), ref = &_module.get_ref(fref)) { if (ref->addend) ppu_loader.warning("**** REF(%u): Addend value(0x%x, 0x%x)", ref->type, ref->addr, ref->addend); @@ -584,28 +585,28 @@ static void ppu_patch_refs(std::vector* out_relocs, u32 fref, u32 fad { case 1: { - const u32 value = vm::_ref(ref->addr) = rdata; + const u32 value = _module.get_ref(ref->addr) = rdata; ppu_loader.trace("**** REF(1): 0x%x <- 0x%x", ref->addr, value); break; } case 4: { - const u16 value = vm::_ref(ref->addr) = static_cast(rdata); + const u16 value = _module.get_ref(ref->addr) = static_cast(rdata); ppu_loader.trace("**** REF(4): 0x%x <- 0x%04x (0x%llx)", ref->addr, value, faddr); break; } case 6: { - const u16 value = vm::_ref(ref->addr) = static_cast(rdata >> 16) + (rdata & 0x8000 ? 1 : 0); + const u16 value = _module.get_ref(ref->addr) = static_cast(rdata >> 16) + (rdata & 0x8000 ? 1 : 0); ppu_loader.trace("**** REF(6): 0x%x <- 0x%04x (0x%llx)", ref->addr, value, faddr); break; } case 57: { - const u16 value = vm::_ref, 0, 14>>(ref->addr) = static_cast(rdata) >> 2; + const u16 value = _module.get_ref, 0, 14>>(ref->addr) = static_cast(rdata) >> 2; ppu_loader.trace("**** REF(57): 0x%x <- 0x%04x (0x%llx)", ref->addr, value, faddr); break; } @@ -680,7 +681,7 @@ extern bool ppu_register_library_lock(std::string_view libname, bool lock_lib) } // Load and register exports; return special exports found (nameless module) -static auto ppu_load_exports(ppu_linkage_info* link, u32 exports_start, u32 exports_end, bool for_observing_callbacks = false, std::basic_string* loaded_flags = nullptr) +static auto ppu_load_exports(const ppu_module& _module, ppu_linkage_info* link, u32 exports_start, u32 exports_end, bool for_observing_callbacks = false, std::basic_string* loaded_flags = nullptr) { std::unordered_map result; @@ -694,7 +695,7 @@ static auto ppu_load_exports(ppu_linkage_info* link, u32 exports_start, u32 expo for (u32 addr = exports_start; addr < exports_end; unload_index++, addr += lib.size ? lib.size : sizeof(ppu_prx_module_info)) { - std::memcpy(&lib, vm::base(addr), sizeof(lib)); + std::memcpy(&lib, &_module.get_ref(addr), sizeof(lib)); const bool is_library = !!(lib.attributes & PRX_EXPORT_LIBRARY_FLAG); const bool is_management = !is_library && !!(lib.attributes & PRX_EXPORT_PRX_MANAGEMENT_FUNCTIONS_FLAG); @@ -709,12 +710,12 @@ static auto ppu_load_exports(ppu_linkage_info* link, u32 exports_start, u32 expo // Set special exports for (u32 i = 0, end = lib.num_func + lib.num_var; i < end; i++) { - const u32 nid = lib.nids[i]; - const u32 addr = lib.addrs[i]; + const u32 nid = _module.get_ref(lib.nids, i); + const u32 addr = _module.get_ref(lib.addrs, i); if (i < lib.num_func) { - ppu_loader.notice("** Special: [%s] at 0x%x [0x%x, 0x%x]", ppu_get_function_name({}, nid), addr, vm::_ref(addr), vm::_ref(addr + 4)); + ppu_loader.notice("** Special: [%s] at 0x%x [0x%x, 0x%x]", ppu_get_function_name({}, nid), addr, _module.get_ref(addr), _module.get_ref(addr + 4)); } else { @@ -738,7 +739,7 @@ static auto ppu_load_exports(ppu_linkage_info* link, u32 exports_start, u32 expo continue; } - const std::string module_name(lib.name.get_ptr()); + const std::string module_name(&_module.get_ref(lib.name)); if (unload_exports) { @@ -782,9 +783,9 @@ static auto ppu_load_exports(ppu_linkage_info* link, u32 exports_start, u32 expo // Get functions for (u32 i = 0, end = lib.num_func; i < end; i++) { - const u32 fnid = fnids[i]; - const u32 faddr = faddrs[i]; - ppu_loader.notice("**** %s export: [%s] (0x%08x) at 0x%x [at:0x%x]", module_name, ppu_get_function_name(module_name, fnid), fnid, faddr, vm::read32(faddr)); + const u32 fnid = _module.get_ref(fnids, i); + const u32 faddr = _module.get_ref(faddrs, i); + ppu_loader.notice("**** %s export: [%s] (0x%08x) at 0x%x [at:0x%x]", module_name, ppu_get_function_name(module_name, fnid), fnid, faddr, _module.get_ref(faddr)); // Function linkage info auto& flink = mlink.functions[fnid]; @@ -811,7 +812,10 @@ static auto ppu_load_exports(ppu_linkage_info* link, u32 exports_start, u32 expo // Set exported function flink.export_addr = target - 4; - ppu_form_branch_to_code(vm::read32(faddr), target); + if (auto ptr = _module.get_ptr(faddr); vm::try_get_addr(ptr).first) + { + ppu_form_branch_to_code(*ptr, target); + } } else { @@ -821,13 +825,13 @@ static auto ppu_load_exports(ppu_linkage_info* link, u32 exports_start, u32 expo // Fix imports for (const u32 addr : flink.imports) { - vm::write32(addr, faddr); + _module.get_ref(addr) = faddr; //ppu_loader.warning("Exported function '%s' in module '%s'", ppu_get_function_name(module_name, fnid), module_name); } for (const u32 fref : flink.frefss) { - ppu_patch_refs(nullptr, fref, faddr); + ppu_patch_refs(_module, nullptr, fref, faddr); } } } @@ -839,8 +843,8 @@ static auto ppu_load_exports(ppu_linkage_info* link, u32 exports_start, u32 expo // Get variables for (u32 i = 0, end = lib.num_var; i < end; i++) { - const u32 vnid = vnids[i]; - const u32 vaddr = vaddrs[i]; + const u32 vnid = _module.get_ref(vnids, i); + const u32 vaddr = _module.get_ref(vaddrs, i); ppu_loader.notice("**** %s export: &[%s] at 0x%x", module_name, ppu_get_variable_name(module_name, vnid), vaddr); // Variable linkage info @@ -863,7 +867,7 @@ static auto ppu_load_exports(ppu_linkage_info* link, u32 exports_start, u32 expo // Fix imports for (const auto vref : vlink.imports) { - ppu_patch_refs(nullptr, vref, vaddr); + ppu_patch_refs(_module, nullptr, vref, vaddr); //ppu_loader.warning("Exported variable '%s' in module '%s'", ppu_get_variable_name(module_name, vnid), module_name); } } @@ -873,17 +877,17 @@ static auto ppu_load_exports(ppu_linkage_info* link, u32 exports_start, u32 expo return result; } -static auto ppu_load_imports(std::vector& relocs, ppu_linkage_info* link, u32 imports_start, u32 imports_end) +static auto ppu_load_imports(const ppu_module& _module, std::vector& relocs, ppu_linkage_info* link, u32 imports_start, u32 imports_end) { std::unordered_map result; - reader_lock lock(link->mutex); + std::lock_guard lock(link->mutex); for (u32 addr = imports_start; addr < imports_end;) { - const auto& lib = vm::_ref(addr); + const auto& lib = _module.get_ref(addr); - const std::string module_name(lib.name.get_ptr()); + const std::string module_name(&_module.get_ref(lib.name)); ppu_loader.notice("** Imported module '%s' (ver=0x%x, attr=0x%x, 0x%x, 0x%x) [0x%x]", module_name, lib.version, lib.attributes, lib.unk4, lib.unk5, addr); @@ -903,8 +907,8 @@ static auto ppu_load_imports(std::vector& relocs, ppu_linkage_info* l for (u32 i = 0, end = lib.num_func; i < end; i++) { - const u32 fnid = fnids[i]; - const u32 fstub = faddrs[i]; + const u32 fnid = _module.get_ref(fnids, i); + const u32 fstub = _module.get_ref(faddrs, i); const u32 faddr = (faddrs + i).addr(); ppu_loader.notice("**** %s import: [%s] (0x%08x) -> 0x%x", module_name, ppu_get_function_name(module_name, fnid), fnid, fstub); @@ -920,14 +924,14 @@ static auto ppu_load_imports(std::vector& relocs, ppu_linkage_info* l const u32 link_addr = flink.export_addr ? flink.export_addr : g_fxo->get().addr; // Write import table - vm::write32(faddr, link_addr); + _module.get_ref(faddr) = link_addr; // Patch refs if necessary (0x2000 seems to be correct flag indicating the presence of additional info) - if (const u32 frefs = (lib.attributes & 0x2000) ? +fnids[i + lib.num_func] : 0) + if (const u32 frefs = (lib.attributes & 0x2000) ? +_module.get_ref(fnids, i + lib.num_func) : 0) { result.emplace(frefs, &flink); flink.frefss.emplace(frefs); - ppu_patch_refs(&relocs, frefs, link_addr); + ppu_patch_refs(_module, &relocs, frefs, link_addr); } //ppu_loader.warning("Imported function '%s' in module '%s' (0x%x)", ppu_get_function_name(module_name, fnid), module_name, faddr); @@ -938,8 +942,8 @@ static auto ppu_load_imports(std::vector& relocs, ppu_linkage_info* l for (u32 i = 0, end = lib.num_var; i < end; i++) { - const u32 vnid = vnids[i]; - const u32 vref = vstubs[i]; + const u32 vnid = _module.get_ref(vnids, i); + const u32 vref = _module.get_ref(vstubs, i); ppu_loader.notice("**** %s import: &[%s] (ref=*0x%x)", module_name, ppu_get_variable_name(module_name, vnid), vref); // Variable linkage info @@ -951,7 +955,7 @@ static auto ppu_load_imports(std::vector& relocs, ppu_linkage_info* l mlink.imported = true; // Link if available - ppu_patch_refs(&relocs, vref, vlink.export_addr); + ppu_patch_refs(_module, &relocs, vref, vlink.export_addr); //ppu_loader.warning("Imported variable '%s' in module '%s' (0x%x)", ppu_get_variable_name(module_name, vnid), module_name, vlink.first); } @@ -968,14 +972,18 @@ void ppu_manual_load_imports_exports(u32 imports_start, u32 imports_size, u32 ex auto& _main = g_fxo->get(); auto& link = g_fxo->get(); - ppu_load_exports(&link, exports_start, exports_start + exports_size, false, &loaded_flags); + ppu_module vm_all_fake_module{}; + vm_all_fake_module.segs.emplace_back(ppu_segment{0x10000, -0x10000u, 1 /*LOAD*/, 0, -0x1000u, vm::base(0x10000)}); + vm_all_fake_module.addr_to_seg_index.emplace(0x10000, 0); + + ppu_load_exports(vm_all_fake_module, &link, exports_start, exports_start + exports_size, false, &loaded_flags); if (!imports_size) { return; } - ppu_load_imports(_main.relocs, &link, imports_start, imports_start + imports_size); + ppu_load_imports(vm_all_fake_module, _main.relocs, &link, imports_start, imports_start + imports_size); } // For savestates @@ -1063,12 +1071,188 @@ static void ppu_check_patch_spu_images(const ppu_module& mod, const ppu_segment& return; } + const bool is_firmware = mod.path.starts_with(vfs::get("/dev_flash/")); + + const auto _main = g_fxo->try_get(); + const std::string_view seg_view{ensure(mod.get_ptr(seg.addr)), seg.size}; - for (usz i = seg_view.find("\177ELF"); i < seg.size; i = seg_view.find("\177ELF", i + 4)) + auto find_first_of_multiple = [](std::string_view data, std::initializer_list values, usz index) + { + usz pos = umax; + + for (std::string_view value : values) + { + if (usz pos0 = data.substr(index, pos - index).find(value); pos0 != umax && pos0 + index < pos) + { + pos = pos0 + index; + } + } + + return pos; + }; + + extern void utilize_spu_data_segment(u32 vaddr, const void* ls_data_vaddr, u32 size); + + // Search for [stqd lr,0x10(sp)] instruction or ELF file signature, whichever comes first + const std::initializer_list prefixes = {"\177ELF"sv, "\x24\0\x40\x80"sv}; + + usz prev_bound = 0; + + for (usz i = find_first_of_multiple(seg_view, prefixes, 0); i < seg.size; i = find_first_of_multiple(seg_view, prefixes, utils::align(i + 1, 4))) { const auto elf_header = ensure(mod.get_ptr(seg.addr + i)); + if (i % 4 == 0 && std::memcmp(elf_header, "\x24\0\x40\x80", 4) == 0) + { + bool next = true; + const u32 old_i = i; + + for (u32 search = i & -128, tries = 10; tries && search >= prev_bound; tries--, search = utils::sub_saturate(search, 128)) + { + if (seg_view[search] != 0x42 && seg_view[search] != 0x43) + { + continue; + } + + const u32 inst1 = read_from_ptr>(seg_view, search); + const u32 inst2 = read_from_ptr>(seg_view, search + 4); + const u32 inst3 = read_from_ptr>(seg_view, search + 8); + const u32 inst4 = read_from_ptr>(seg_view, search + 12); + + if ((inst1 & 0xfe'00'00'7f) != 0x42000002 || (inst2 & 0xfe'00'00'7f) != 0x42000002 || (inst3 & 0xfe'00'00'7f) != 0x42000002 || (inst4 & 0xfe'00'00'7f) != 0x42000002) + { + continue; + } + + ppu_log.success("Found SPURS GUID Pattern at 0x%05x", search + seg.addr); + i = search; + next = false; + break; + } + + if (next) + { + continue; + } + + std::string_view ls_segment = seg_view.substr(i); + + // Bound to a bit less than LS size + ls_segment = ls_segment.substr(0, 0x38000); + + for (usz addr_last = 0, valid_count = 0, invalid_count = 0;;) + { + usz instruction = ls_segment.find("\x24\0\x40\x80"sv, addr_last); + + if (instruction != umax) + { + if (instruction % 4 != i % 4) + { + // Unaligned, continue + addr_last = instruction + (i % 4 - instruction % 4) % 4; + continue; + } + + // FIXME: This seems to terminate SPU code prematurely in some cases + // Likely due to absolute branches + if (spu_thread::is_exec_code(instruction, {reinterpret_cast(ls_segment.data()), ls_segment.size()}, 0)) + { + addr_last = instruction + 4; + valid_count++; + invalid_count = 0; + continue; + } + + if (invalid_count == 0) + { + // Allow a single case of invalid data + addr_last = instruction + 4; + invalid_count++; + continue; + } + + addr_last = instruction; + } + + if (addr_last >= 0x80 && valid_count >= 2) + { + const u32 begin = i & -128; + u32 end = std::min(seg.size, utils::align(i + addr_last + 256, 128)); + + u32 guessed_ls_addr = 0; + + // Try to guess LS address by observing the pattern for disable/enable interrupts + // ILA R2, PC + 8 + // BIE/BID R2 + + for (u32 found = 0, last_vaddr = 0, it = begin + 16; it < end - 16; it += 4) + { + const u32 inst1 = read_from_ptr>(seg_view, it); + const u32 inst2 = read_from_ptr>(seg_view, it + 4); + const u32 inst3 = read_from_ptr>(seg_view, it + 8); + const u32 inst4 = read_from_ptr>(seg_view, it + 12); + + if ((inst1 & 0xfe'00'00'7f) == 0x42000002 && (inst2 & 0xfe'00'00'7f) == 0x42000002 && (inst3 & 0xfe'00'00'7f) == 0x42000002 && (inst4 & 0xfe'00'00'7f) == 0x42000002) + { + // SPURS GUID pattern + end = it; + ppu_log.success("Found SPURS GUID Pattern for terminator at 0x%05x", end + seg.addr); + break; + } + + if ((inst1 >> 7) % 4 == 0 && (inst1 & 0xfe'00'00'7f) == 0x42000002 && (inst2 == 0x35040100 || inst2 == 0x35080100)) + { + const u32 addr_inst = (inst1 >> 7) % 0x40000; + + if (u32 addr_seg = addr_inst - std::min(it + 8 - begin, addr_inst)) + { + if (last_vaddr != addr_seg) + { + guessed_ls_addr = 0; + found = 0; + } + + found++; + last_vaddr = addr_seg; + + if (found >= 2) + { + // Good segment address + guessed_ls_addr = last_vaddr; + ppu_log.notice("Found IENABLE/IDSIABLE Pattern at 0x%05x", it + seg.addr); + } + } + } + } + + if (guessed_ls_addr) + { + end = begin + std::min(end - begin, SPU_LS_SIZE - guessed_ls_addr); + } + + ppu_log.success("Found valid roaming SPU code at 0x%x..0x%x (guessed_ls_addr=0x%x)", seg.addr + begin, seg.addr + end, guessed_ls_addr); + + if (!is_firmware && _main == &mod) + { + // Siginify that the base address is unknown by passing 0 + utilize_spu_data_segment(guessed_ls_addr ? guessed_ls_addr : 0x4000, seg_view.data() + begin, end - begin); + } + + i = std::max(end, i + 4) - 4; + prev_bound = i + 4; + } + else + { + i = old_i; + } + + break; + } + + continue; + } + // Try to load SPU image const spu_exec_object obj(fs::file(elf_header, seg.size - i)); @@ -1100,6 +1284,13 @@ static void ppu_check_patch_spu_images(const ppu_module& mod, const ppu_segment& if (prog.p_type == 0x1u /* LOAD */ && prog.p_filesz > 0u) { + if (prog.p_vaddr && !is_firmware && _main == &mod) + { + extern void utilize_spu_data_segment(u32 vaddr, const void* ls_data_vaddr, u32 size); + + utilize_spu_data_segment(prog.p_vaddr, (elf_header + prog.p_offset), prog.p_filesz); + } + sha1_update(&sha2, (elf_header + prog.p_offset), prog.p_filesz); } @@ -1117,6 +1308,8 @@ static void ppu_check_patch_spu_images(const ppu_module& mod, const ppu_segment& } } + fmt::append(dump, " (image addr: 0x%x, size: 0x%x)", seg.addr + i, obj.highest_offset); + sha1_finish(&sha2, sha1_hash); // Format patch name @@ -1159,6 +1352,9 @@ static void ppu_check_patch_spu_images(const ppu_module& mod, const ppu_segment& { ppu_loader.success("SPU executable hash: %s (<- %u)%s", hash, applied.size(), dump); } + + i += obj.highest_offset - 4; + prev_bound = i + 4; } } @@ -1349,12 +1545,6 @@ std::shared_ptr ppu_load_prx(const ppu_prx_object& elf, bool virtual_lo // Initialize executable code if necessary if (prog.p_flags & 0x1 && !virtual_load) { - if (ar) - { - // Disable analysis optimization for savestates (it's not compatible with savestate with patches applied) - end = std::max(end, utils::align(addr + mem_size, 0x10000)); - } - ppu_register_range(addr, mem_size); } } @@ -1569,10 +1759,14 @@ std::shared_ptr ppu_load_prx(const ppu_prx_object& elf, bool virtual_lo ppu_loader.warning("Library %s (rtoc=0x%x):", lib_name, lib_info->toc); - if (!virtual_load) + ppu_linkage_info dummy{}; + + prx->specials = ppu_load_exports(*prx, virtual_load ? &dummy : &link, prx->exports_start, prx->exports_end, true); + prx->imports = ppu_load_imports(*prx, prx->relocs, virtual_load ? &dummy : &link, lib_info->imports_start, lib_info->imports_end); + + if (virtual_load) { - prx->specials = ppu_load_exports(&link, prx->exports_start, prx->exports_end, true); - prx->imports = ppu_load_imports(prx->relocs, &link, lib_info->imports_start, lib_info->imports_end); + prx->imports.clear(); } std::stable_sort(prx->relocs.begin(), prx->relocs.end()); @@ -1640,6 +1834,36 @@ std::shared_ptr ppu_load_prx(const ppu_prx_object& elf, bool virtual_lo } } + // Disabled for PRX for now (problematic and does not seem to have any benefit) + end = 0; + + if (!applied.empty() || ar) + { + // Compare memory changes in memory after executable code sections end + if (end >= prx->segs[0].addr && end < prx->segs[0].addr + prx->segs[0].size) + { + for (const auto& prog : elf.progs) + { + // Find the first segment + if (prog.p_type == 0x1u /* LOAD */ && prog.p_memsz) + { + std::basic_string_view elf_memory{prog.bin.data(), prog.bin.size()}; + elf_memory.remove_prefix(end - prx->segs[0].addr); + + if (elf_memory != std::basic_string_view{&prx->get_ref(end), elf_memory.size()}) + { + // There are changes, disable analysis optimization + ppu_loader.notice("Disabling analysis optimization due to memory changes from original file"); + + end = 0; + } + + break; + } + } + } + } + // Embedded SPU elf patching for (const auto& seg : prx->segs) { @@ -1658,6 +1882,11 @@ std::shared_ptr ppu_load_prx(const ppu_prx_object& elf, bool virtual_lo void ppu_unload_prx(const lv2_prx& prx) { + if (prx.segs.empty() || prx.segs[0].ptr != vm::base(prx.segs[0].addr)) + { + return; + } + std::unique_lock lock(g_fxo->get().mutex, std::defer_lock); // Clean linkage info @@ -1708,10 +1937,7 @@ void ppu_unload_prx(const lv2_prx& prx) { if (!seg.size) continue; - if (seg.ptr == vm::base(seg.addr)) - { - vm::dealloc(seg.addr, vm::main); - } + vm::dealloc(seg.addr, vm::main); const std::string hash_seg = fmt::format("%s-%u", hash, &seg - prx.segs.data()); @@ -1847,15 +2073,30 @@ bool ppu_load_exec(const ppu_exec_object& elf, bool virtual_load, const std::str else if (already_loaded) { } - else if (!vm::falloc(addr, size, vm::main)) + else if (![&]() -> bool { - ppu_loader.error("vm::falloc(vm::main) failed (addr=0x%x, memsz=0x%x)", addr, size); // TODO + // 1M pages if it is RSX shared + const u32 area_flags = (_seg.flags >> 28) ? vm::page_size_1m : vm::page_size_64k; + const u32 alloc_at = std::max(addr & -0x10000000, 0x10000); - if (!vm::falloc(addr, size)) + const auto area = vm::reserve_map(vm::any, std::max(addr & -0x10000000, 0x10000), 0x10000000, area_flags); + + if (!area) { - ppu_loader.error("ppu_load_exec(): vm::falloc() failed (addr=0x%x, memsz=0x%x)", addr, size); return false; } + + if (area->addr != alloc_at || (area->flags & 0xf00) != area_flags) + { + ppu_loader.error("Failed to allocate memory at 0x%x - conflicting memory area exists: area->addr=0x%x, area->flags=0x%x", addr, area->addr, area->flags); + return false; + } + + return area->falloc(addr, size); + }()) + { + ppu_loader.error("ppu_load_exec(): vm::falloc() failed (addr=0x%x, memsz=0x%x)", addr, size); + return false; } // Store only LOAD segments (TODO) @@ -1882,12 +2123,6 @@ bool ppu_load_exec(const ppu_exec_object& elf, bool virtual_load, const std::str // Initialize executable code if necessary if (prog.p_flags & 0x1 && !virtual_load) { - if (already_loaded && ar) - { - // Disable analysis optimization for savestates (it's not compatible with savestate with patches applied) - end = std::max(end, utils::align(addr + size, 0x10000)); - } - ppu_register_range(addr, size); } } @@ -1941,6 +2176,33 @@ bool ppu_load_exec(const ppu_exec_object& elf, bool virtual_load, const std::str applied += g_fxo->get().apply(Emu.GetTitleID() + '-' + hash, [&](u32 addr, u32 size) { return _main.get_ptr(addr, size); }); } + if (!applied.empty() || ar) + { + // Compare memory changes in memory after executable code sections end + if (end >= _main.segs[0].addr && end < _main.segs[0].addr + _main.segs[0].size) + { + for (const auto& prog : elf.progs) + { + // Find the first segment + if (prog.p_type == 0x1u /* LOAD */ && prog.p_memsz) + { + std::basic_string_view elf_memory{prog.bin.data(), prog.bin.size()}; + elf_memory.remove_prefix(end - _main.segs[0].addr); + + if (elf_memory != std::basic_string_view{&_main.get_ref(end), elf_memory.size()}) + { + // There are changes, disable analysis optimization + ppu_loader.notice("Disabling analysis optimization due to memory changes from original file"); + + end = 0; + } + + break; + } + } + } + } + if (applied.empty()) { ppu_loader.warning("PPU executable hash: %s", hash); @@ -2103,11 +2365,10 @@ bool ppu_load_exec(const ppu_exec_object& elf, bool virtual_load, const std::str return false; } - if (!virtual_load) - { - ppu_load_exports(&link, proc_prx_param.libent_start, proc_prx_param.libent_end); - ppu_load_imports(_main.relocs, &link, proc_prx_param.libstub_start, proc_prx_param.libstub_end); - } + ppu_linkage_info dummy{}; + + ppu_load_exports(_main, virtual_load ? &dummy : &link, proc_prx_param.libent_start, proc_prx_param.libent_end); + ppu_load_imports(_main, _main.relocs, virtual_load ? &dummy : &link, proc_prx_param.libstub_start, proc_prx_param.libstub_end); std::stable_sort(_main.relocs.begin(), _main.relocs.end()); } @@ -2224,16 +2485,15 @@ bool ppu_load_exec(const ppu_exec_object& elf, bool virtual_load, const std::str void init_fxo_for_exec(utils::serial* ar, bool full); init_fxo_for_exec(ar, false); + + liblv2_begin = 0; + liblv2_end = 0; } else { g_ps3_process_info = old_process_info; - Emu.ConfigurePPUCache(); } - liblv2_begin = 0; - liblv2_end = 0; - if (!load_libs.empty()) { for (const auto& name : load_libs) @@ -2295,33 +2555,6 @@ bool ppu_load_exec(const ppu_exec_object& elf, bool virtual_load, const std::str } } - // Initialize process arguments - auto args = vm::ptr::make(vm::alloc(u32{sizeof(u64)} * (::size32(Emu.argv) + ::size32(Emu.envp) + 2), vm::main)); - auto argv = args; - - for (const auto& arg : Emu.argv) - { - const u32 arg_size = ::size32(arg) + 1; - const u32 arg_addr = vm::alloc(arg_size, vm::main); - - std::memcpy(vm::base(arg_addr), arg.data(), arg_size); - - *args++ = arg_addr; - } - - *args++ = 0; - auto envp = args; - - for (const auto& arg : Emu.envp) - { - const u32 arg_size = ::size32(arg) + 1; - const u32 arg_addr = vm::alloc(arg_size, vm::main); - - std::memcpy(vm::base(arg_addr), arg.data(), arg_size); - - *args++ = arg_addr; - } - // Fix primary stack size switch (u32 sz = primary_stacksize) { @@ -2352,9 +2585,62 @@ bool ppu_load_exec(const ppu_exec_object& elf, bool virtual_load, const std::str if (!Emu.data.empty()) { std::memcpy(vm::base(ppu->stack_addr + ppu->stack_size - ::size32(Emu.data)), Emu.data.data(), Emu.data.size()); - ppu->gpr[1] -= Emu.data.size(); + ppu->gpr[1] -= utils::align(::size32(Emu.data), 0x10); } + // Initialize process arguments + + // Calculate storage requirements on the stack + const u32 pointers_storage_size = u32{sizeof(u64)} * utils::align(::size32(Emu.envp) + ::size32(Emu.argv) + 2, 2); + + u32 stack_alloc_size = pointers_storage_size; + + for (const auto& arg : Emu.argv) + { + stack_alloc_size += utils::align(::size32(arg) + 1, 0x10); + } + + for (const auto& arg : Emu.envp) + { + stack_alloc_size += utils::align(::size32(arg) + 1, 0x10); + } + + ensure(ppu->stack_size > stack_alloc_size); + + vm::ptr args = vm::cast(static_cast(ppu->stack_addr + ppu->stack_size - stack_alloc_size - utils::align(Emu.data.size(), 0x10))); + vm::ptr args_data = vm::cast(args.addr() + pointers_storage_size); + + const vm::ptr argv = args; + + for (const auto& arg : Emu.argv) + { + const u32 arg_size = ::size32(arg) + 1; + + std::memcpy(args_data.get_ptr(), arg.data(), arg_size); + + *args++ = args_data.addr(); + args_data = vm::cast(args_data.addr() + utils::align(arg_size, 0x10)); + } + + *args++ = 0; + + const vm::ptr envp = args; + args = envp; + + for (const auto& arg : Emu.envp) + { + const u32 arg_size = ::size32(arg) + 1; + + std::memcpy(args_data.get_ptr(), arg.data(), arg_size); + + *args++ = args_data.addr(); + args_data = vm::cast(args_data.addr() + utils::align(arg_size, 0x10)); + } + + *args++ = 0; + + ppu->gpr[1] -= stack_alloc_size; + ensure(g_fxo->get().take(primary_stacksize)); ppu->cmd_push({ppu_cmd::initialize, 0}); @@ -2388,7 +2674,7 @@ bool ppu_load_exec(const ppu_exec_object& elf, bool virtual_load, const std::str // Set command line arguments, run entry function ppu->cmd_list ({ - { ppu_cmd::set_args, 8 }, u64{Emu.argv.size()}, u64{argv.addr()}, u64{envp.addr()}, u64{0}, u64{ppu->id}, u64{tls_vaddr}, u64{tls_fsize}, u64{tls_vsize}, + { ppu_cmd::set_args, 8 }, u64{Emu.argv.size()}, u64{argv.addr()}, u64{envp.addr()}, u64{Emu.envp.size()}, u64{ppu->id}, u64{tls_vaddr}, u64{tls_fsize}, u64{tls_vsize}, { ppu_cmd::set_gpr, 11 }, u64{elf.header.e_entry}, { ppu_cmd::set_gpr, 12 }, u64{malloc_pagesize}, { ppu_cmd::entry_call, 0 }, @@ -2442,7 +2728,7 @@ std::pair, CellError> ppu_load_overlay(const ppu_ex } } - const auto ovlm = std::make_shared(); + std::shared_ptr ovlm = std::make_shared(); // Set path (TODO) ovlm->name = path.substr(path.find_last_of('/') + 1); @@ -2522,12 +2808,6 @@ std::pair, CellError> ppu_load_overlay(const ppu_ex // Initialize executable code if necessary if (prog.p_flags & 0x1 && !virtual_load) { - if (ar) - { - // Disable analysis optimization for savestates (it's not compatible with savestate with patches applied) - end = std::max(end, utils::align(addr + size, 0x10000)); - } - ppu_register_range(addr, size); } } @@ -2579,6 +2859,33 @@ std::pair, CellError> ppu_load_overlay(const ppu_ex applied += g_fxo->get().apply(Emu.GetTitleID() + '-' + hash, [ovlm](u32 addr, u32 size) { return ovlm->get_ptr(addr, size); }); } + if (!applied.empty() || ar) + { + // Compare memory changes in memory after executable code sections end + if (end >= ovlm->segs[0].addr && end < ovlm->segs[0].addr + ovlm->segs[0].size) + { + for (const auto& prog : elf.progs) + { + // Find the first segment + if (prog.p_type == 0x1u /* LOAD */ && prog.p_memsz) + { + std::basic_string_view elf_memory{prog.bin.data(), prog.bin.size()}; + elf_memory.remove_prefix(end - ovlm->segs[0].addr); + + if (elf_memory != std::basic_string_view{&ovlm->get_ref(end), elf_memory.size()}) + { + // There are changes, disable analysis optimization + ppu_loader.notice("Disabling analysis optimization due to memory changes from original file"); + + end = 0; + } + + break; + } + } + } + } + // Embedded SPU elf patching for (const auto& seg : ovlm->segs) { @@ -2665,11 +2972,10 @@ std::pair, CellError> ppu_load_overlay(const ppu_ex fmt::throw_exception("Bad magic! (0x%x)", proc_prx_param.magic); } - if (!virtual_load) - { - ppu_load_exports(&link, proc_prx_param.libent_start, proc_prx_param.libent_end); - ppu_load_imports(ovlm->relocs, &link, proc_prx_param.libstub_start, proc_prx_param.libstub_end); - } + ppu_linkage_info dummy{}; + + ppu_load_exports(*ovlm, virtual_load ? &dummy : &link, proc_prx_param.libent_start, proc_prx_param.libent_end); + ppu_load_imports(*ovlm, ovlm->relocs, virtual_load ? &dummy : &link, proc_prx_param.libstub_start, proc_prx_param.libstub_end); } break; } @@ -2783,7 +3089,7 @@ bool ppu_load_rel_exec(const ppu_rel_object& elf) _sec.addr = addr; relm.secs.emplace_back(_sec); - std::memcpy(vm::base(addr), s.bin.data(), size); + std::memcpy(vm::base(addr), s.get_bin().data(), size); addr = utils::align(addr + size, 128); } } diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index c50e8c1b1a..deba0ece06 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -11,6 +11,7 @@ #include "Emu/Memory/vm_locking.h" #include "Emu/RSX/Core/RSXReservationLock.hpp" #include "Emu/VFS.h" +#include "Emu/vfs_config.h" #include "Emu/system_progress.hpp" #include "Emu/system_utils.hpp" #include "PPUThread.h" @@ -1206,9 +1207,7 @@ std::array op_branch_targets(u32 pc, ppu_opcode_t op) { std::array res{pc + 4, umax}; - g_fxo->need(); - - if (u32 target = g_fxo->get().get_target(pc)) + if (u32 target = g_fxo->is_init() ? g_fxo->get().get_target(pc) : 0) { res[0] = target; return res; @@ -1564,16 +1563,19 @@ std::vector> ppu_thread::dump_callstack_list() const std::vector> call_stack_list; - bool first = true; + bool is_first = true; + bool skip_single_frame = false; + + const u64 _lr = this->lr; + const u32 _cia = this->cia; + const u64 gpr0 = this->gpr[0]; for ( u64 sp = r1; sp % 0x10 == 0u && sp >= stack_min && sp <= stack_max - ppu_stack_start_offset; - first = false + is_first = false ) { - u64 addr = *vm::get_super_ptr(static_cast(sp + 16)); - auto is_invalid = [](u64 addr) { if (addr > u32{umax} || addr % 4 || !vm::check_addr(static_cast(addr), vm::page_executable)) @@ -1585,28 +1587,328 @@ std::vector> ppu_thread::dump_callstack_list() const return addr == g_fxo->get().func_addr(1, true); }; - if (is_invalid(addr)) + if (is_first && !is_invalid(_lr)) { - if (first) - { - // Function hasn't saved LR, could be because it's a leaf function - // Use LR directly instead - addr = lr; + // Detect functions with no stack or before LR has been stored - if (is_invalid(addr)) + // Tracking if instruction has already been passed through + // Instead of using map or set, use two vectors relative to CIA and resize as needed + std::vector> inst_neg; + std::vector> inst_pos; + + auto get_inst = [&](u32 pos) -> be_t& + { + static be_t s_inst_empty{}; + + if (pos < _cia) { - // Skip it, workaround - continue; + const u32 neg_dist = (_cia - pos - 4) / 4; + + if (neg_dist >= inst_neg.size()) + { + const u32 inst_bound = pos & -256; + + const usz old_size = inst_neg.size(); + const usz new_size = neg_dist + (pos - inst_bound) / 4 + 1; + + if (new_size >= 0x8000) + { + // Gross lower limit for the function (if it is that size it is unlikely that it is even a leaf function) + return s_inst_empty; + } + + inst_neg.resize(new_size); + + if (!vm::try_access(inst_bound, &inst_neg[old_size], (new_size - old_size) * sizeof(be_t), false)) + { + // Failure (this would be detected as failure by zeroes) + } + + // Reverse the array (because this buffer directs backwards in address) + + for (usz start = old_size, end = new_size - 1; start < end; start++, end--) + { + std::swap(inst_neg[start], inst_neg[end]); + } + } + + return inst_neg[neg_dist]; + } + + const u32 pos_dist = (pos - _cia) / 4; + + if (pos_dist >= inst_pos.size()) + { + const u32 inst_bound = utils::align(pos, 256); + + const usz old_size = inst_pos.size(); + const usz new_size = pos_dist + (inst_bound - pos) / 4 + 1; + + if (new_size >= 0x8000) + { + // Gross upper limit for the function (if it is that size it is unlikely that it is even a leaf function) + return s_inst_empty; + } + + inst_pos.resize(new_size); + + if (!vm::try_access(pos, &inst_pos[old_size], (new_size - old_size) * sizeof(be_t), false)) + { + // Failure (this would be detected as failure by zeroes) + } + } + + return inst_pos[pos_dist]; + }; + + bool upper_abort = false; + + struct context_t + { + u32 start_point; + bool maybe_leaf = false; // True if the function is leaf or at the very end/start of non-leaf + bool non_leaf = false; // Absolutely not a leaf + bool about_to_push_frame = false; // STDU incoming + bool about_to_store_lr = false; // Link is about to be stored on stack + bool about_to_pop_frame = false; // ADDI R1 is about to be issued + bool about_to_load_link = false; // MTLR is about to be issued + bool maybe_use_reg0_instead_of_lr = false; // Use R0 at the end of a non-leaf function if ADDI has been issued before MTLR + }; + + // Start with CIA + std::deque workload{context_t{_cia}}; + + usz start = 0; + + for (; start < workload.size(); start++) + { + for (u32 wa = workload[start].start_point; vm::check_addr(wa, vm::page_executable);) + { + be_t& opcode = get_inst(wa); + + auto& [_, maybe_leaf, non_leaf, about_to_push_frame, about_to_store_lr, + about_to_pop_frame, about_to_load_link, maybe_use_reg0_instead_of_lr] = workload[start]; + + if (!opcode) + { + // Already passed or failure of reading + break; + } + + const ppu_opcode_t op{opcode}; + + // Mark as passed through + opcode = 0; + + const auto type = g_ppu_itype.decode(op.opcode); + + if (workload.size() == 1 && type == ppu_itype::STDU && op.rs == 1u && op.ra == 1u) + { + if (op.simm16 >= 0) + { + // Against ABI + non_leaf = true; + upper_abort = true; + break; + } + + // Saving LR to register: this is indeed a new function (ok because LR has not been saved yet) + maybe_leaf = true; + about_to_push_frame = true; + about_to_pop_frame = false; + upper_abort = true; + break; + } + + if (workload.size() == 1 && type == ppu_itype::STD && op.ra == 1u && op.rs == 0u) + { + bool found_matching_stdu = false; + + for (u32 back = 1; back < 20; back++) + { + be_t& opcode = get_inst(utils::sub_saturate(_cia, back * 4)); + + if (!opcode) + { + // Already passed or failure of reading + break; + } + + const ppu_opcode_t test_op{opcode}; + + const auto type = g_ppu_itype.decode(test_op.opcode); + + if (type == ppu_itype::BCLR) + { + break; + } + + if (type == ppu_itype::STDU && test_op.rs == 1u && test_op.ra == 1u) + { + if (0 - (test_op.ds << 2) == (op.ds << 2) - 0x10) + { + found_matching_stdu = true; + } + + break; + } + } + + if (found_matching_stdu) + { + // Saving LR to stack: this is indeed a new function (ok because LR has not been saved yet) + maybe_leaf = true; + about_to_store_lr = true; + about_to_pop_frame = true; + upper_abort = true; + break; + } + } + + const u32 spr = ((op.spr >> 5) | ((op.spr & 0x1f) << 5)); + + // It can be placed before or after STDU, ignore for now + // if (workload.size() == 1 && type == ppu_itype::MFSPR && op.rs == 0u && spr == 0x8) + // { + // // Saving LR to register: this is indeed a new function (ok because LR has not been saved yet) + // maybe_leaf = true; + // about_to_store_lr = true; + // about_to_pop_frame = true; + // } + + if (type == ppu_itype::MTSPR && spr == 0x8 && op.rs == 0u) + { + // Test for special case: if ADDI R1 is not found later in code, it means that LR is not restored and R0 should be used instead + // Can also search for ADDI R1 backwards and pull the value from stack (needs more research if it is more reliable) + maybe_use_reg0_instead_of_lr = true; + } + + if (type == ppu_itype::UNK) + { + // Ignore for now + break; + } + + if ((type & ppu_itype::branch) && op.lk) + { + // Gave up on LR before saving + non_leaf = true; + about_to_pop_frame = true; + upper_abort = true; + break; + } + + // Even if BCLR is conditional, it still counts because LR value is ready for return + if (type == ppu_itype::BCLR) + { + // Returned + maybe_leaf = true; + upper_abort = true; + break; + } + + if (type == ppu_itype::ADDI && op.ra == 1u && op.rd == 1u) + { + if (op.simm16 < 0) + { + // Against ABI + non_leaf = true; + upper_abort = true; + break; + } + else if (op.simm16 > 0) + { + // Remember that SP is about to be restored + about_to_pop_frame = true; + non_leaf = true; + upper_abort = true; + break; + } + } + + const auto results = op_branch_targets(wa, op); + + bool proceeded = false; + + for (usz res_i = 0; res_i < results.size(); res_i++) + { + const u32 route_pc = results[res_i]; + + if (route_pc == umax) + { + continue; + } + + if (vm::check_addr(route_pc, vm::page_executable) && get_inst(route_pc)) + { + if (proceeded) + { + // Remember next route start point + workload.push_back(context_t{route_pc}); + } + else + { + // Next PC + wa = route_pc; + proceeded = true; + } + } + } + } + + if (upper_abort) + { + break; } } - else + + const context_t& res = workload[std::min(start, workload.size() - 1)]; + + if (res.maybe_leaf && !res.non_leaf) { - break; + const u32 result = res.maybe_use_reg0_instead_of_lr ? static_cast(gpr0) : static_cast(_lr); + + // Same stack as far as we know + call_stack_list.emplace_back(result, static_cast(sp)); + + if (res.about_to_store_lr) + { + // LR has yet to be stored on stack, ignore the stack value + skip_single_frame = true; + } + } + + if (res.about_to_pop_frame || (res.maybe_leaf && !res.non_leaf)) + { + const u64 temp_sp = *vm::get_super_ptr(static_cast(sp)); + + if (temp_sp <= sp) + { + // Ensure inequality and that the old stack pointer is higher than current + break; + } + + // Read the first stack frame so caller addresses can be obtained + sp = temp_sp; + continue; } } - // TODO: function addresses too - call_stack_list.emplace_back(static_cast(addr), static_cast(sp)); + u64 addr = *vm::get_super_ptr(static_cast(sp + 16)); + + if (skip_single_frame) + { + skip_single_frame = false; + } + else if (!is_invalid(addr)) + { + // TODO: function addresses too + call_stack_list.emplace_back(static_cast(addr), static_cast(sp)); + } + else if (!is_first) + { + break; + } const u64 temp_sp = *vm::get_super_ptr(static_cast(sp)); @@ -1617,6 +1919,8 @@ std::vector> ppu_thread::dump_callstack_list() const } sp = temp_sp; + + is_first = false; } return call_stack_list; @@ -1808,7 +2112,14 @@ void ppu_thread::cpu_task() #endif cmd_pop(); - ppu_initialize(), spu_cache::initialize(); + ppu_initialize(); + + if (Emu.IsStopped()) + { + return; + } + + spu_cache::initialize(); #ifdef __APPLE__ pthread_jit_write_protect_np(true); @@ -1821,7 +2132,16 @@ void ppu_thread::cpu_task() // Wait until the progress dialog is closed. // We don't want to open a cell dialog while a native progress dialog is still open. - thread_ctrl::wait_on(g_progr_ptotal, 0); + while (u32 v = g_progr_ptotal) + { + if (Emu.IsStopped()) + { + return; + } + + g_progr_ptotal.wait(v); + } + g_fxo->get().show_overlay_message_only = true; // Sadly we can't postpone initializing guest time because we need to run PPU threads @@ -1839,7 +2159,7 @@ void ppu_thread::cpu_task() } ensure(spu.state.test_and_reset(cpu_flag::stop)); - spu.state.notify_one(cpu_flag::stop); + spu.state.notify_one(); } }); @@ -1952,7 +2272,7 @@ void ppu_thread::exec_task() ppu_thread::~ppu_thread() { - perf_log.notice("Perf stats for STCX reload: successs %u, failure %u", last_succ, last_fail); + perf_log.notice("Perf stats for STCX reload: success %u, failure %u", last_succ, last_fail); perf_log.notice("Perf stats for instructions: total %u", exec_bytes / 4); } @@ -2051,7 +2371,7 @@ ppu_thread::ppu_thread(utils::serial& ar) struct init_pushed { bool pushed = false; - atomic_t inited = false; + atomic_t inited = false; }; call_history.data.resize(g_cfg.core.ppu_call_history ? call_history_max_size : 1); @@ -2100,7 +2420,7 @@ ppu_thread::ppu_thread(utils::serial& ar) { while (!Emu.IsStopped() && !g_fxo->get().inited) { - thread_ctrl::wait_on(g_fxo->get().inited, false); + thread_ctrl::wait_on(g_fxo->get().inited, 0); } return false; } @@ -2117,7 +2437,7 @@ ppu_thread::ppu_thread(utils::serial& ar) {ppu_cmd::ptr_call, 0}, +[](ppu_thread&) -> bool { auto& inited = g_fxo->get().inited; - inited = true; + inited = 1; inited.notify_all(); return true; } @@ -2453,10 +2773,10 @@ static void ppu_check(ppu_thread& ppu, u64 addr) { ppu.cia = ::narrow(addr); - // ppu_check() shall not return directly if (ppu.test_stopped()) - {} - ppu_escape(&ppu); + { + return; + } } static void ppu_trace(u64 addr) @@ -3041,12 +3361,29 @@ static bool ppu_store_reservation(ppu_thread& ppu, u32 addr, u64 reg_value) return false; }()) { - // Test a common pattern in lwmutex extern atomic_t liblv2_begin, liblv2_end; + const u32 notify = ppu.res_notify; + + if (notify) + { + vm::reservation_notifier(notify).notify_all(); + ppu.res_notify = 0; + } + + // Avoid notifications from lwmutex or sys_spinlock if (ppu.cia < liblv2_begin || ppu.cia >= liblv2_end) { - res.notify_all(-128); + if (!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 + ppu.res_notify = addr; + } + else if ((addr ^ notify) & -128) + { + res.notify_all(); + } } if (addr == ppu.last_faddr) @@ -3058,6 +3395,16 @@ static bool ppu_store_reservation(ppu_thread& ppu, u32 addr, u64 reg_value) return true; } + const u32 notify = ppu.res_notify; + + // Do not risk postponing too much (because this is probably an indefinite loop) + // And on failure it has some time to do something else + if (notify && ((addr ^ notify) & -128)) + { + vm::reservation_notifier(notify).notify_all(); + ppu.res_notify = 0; + } + return false; } @@ -3222,7 +3569,7 @@ extern void ppu_finalize(const ppu_module& info) fmt::append(cache_path, "ppu-%s-%s/", fmt::base57(info.sha1), info.path.substr(info.path.find_last_of('/') + 1)); #ifdef LLVM_AVAILABLE - g_fxo->get().remove(cache_path + info.name + "_" + std::to_string(info.segs[0].addr)); + g_fxo->get().remove(cache_path + "_" + std::to_string(std::bit_cast(info.segs[0].ptr))); #endif } @@ -3320,8 +3667,8 @@ extern void ppu_precompile(std::vector& dir_queue, std::vector& dir_queue, std::vector& dir_queue, std::vector& dir_queue, std::vector& dir_queue, std::vector& dir_queue, std::vector& dir_queue, std::vector& dir_queue, std::vector& dir_queue, std::vectorget(); _main = {}; + auto current_cache = std::move(g_fxo->get()); + if (!ppu_load_exec(obj, true, path)) { // Abort @@ -3568,16 +3914,27 @@ extern void ppu_precompile(std::vector& dir_queue, std::vectorget() = std::move(current_cache); + break; + } + if (!_main.analyse(0, _main.elf_entry, _main.seg0_code_end, _main.applied_pathes, [](){ return Emu.IsStopped(); })) { + g_fxo->get() = std::move(current_cache); break; } obj.clear(), src.close(); // Clear decrypted file and elf object memory + _main.name = ' '; // Make ppu_finalize work + Emu.ConfigurePPUCache(!Emu.IsPathInsideDir(_main.path, g_cfg_vfs.get_dev_flash())); ppu_initialize(_main); + spu_cache::initialize(false); ppu_finalize(_main); _main = {}; + g_fxo->get() = std::move(current_cache); break; } @@ -3588,10 +3945,11 @@ extern void ppu_precompile(std::vector& dir_queue, std::vectorget() = std::move(main_module); + g_fxo->get().collect_funcs_to_precompile = true; + Emu.ConfigurePPUCache(); }); exec_worker(); @@ -3622,7 +3980,7 @@ extern void ppu_initialize() // Validate analyser results (not required) _main.validate(0); - g_progr = "Scanning PPU Modules..."; + progr = "Scanning PPU Modules..."; bool compile_main = false; @@ -3637,7 +3995,7 @@ extern void ppu_initialize() const std::string firmware_sprx_path = vfs::get("/dev_flash/sys/external/"); // If empty we have no indication for firmware cache state, check everything - bool compile_fw = true; + bool compile_fw = !Emu.IsVsh(); idm::select([&](u32, lv2_prx& _module) { @@ -3683,7 +4041,7 @@ extern void ppu_initialize() const std::string mount_point = vfs::get("/dev_flash/"); - bool dev_flash_located = !Emu.GetCat().ends_with('P') && Emu.IsPathInsideDir(Emu.GetBoot(), mount_point); + bool dev_flash_located = !Emu.GetCat().ends_with('P') && Emu.IsPathInsideDir(Emu.GetBoot(), mount_point) && g_cfg.core.llvm_precompilation; if (compile_fw || dev_flash_located) { @@ -3695,8 +4053,6 @@ extern void ppu_initialize() { // Check if cache exists for this infinitesimally small prx dev_flash_located = ppu_initialize(*prx, true); - idm::remove(idm::last_id()); - ppu_unload_prx(*prx); } } @@ -3705,7 +4061,7 @@ extern void ppu_initialize() } // Avoid compilation if main's cache exists or it is a standalone SELF with no PARAM.SFO - if (compile_main && g_cfg.core.ppu_llvm_precompilation && !Emu.GetTitleID().empty() && !Emu.IsChildProcess()) + if (compile_main && g_cfg.core.llvm_precompilation && !Emu.GetTitleID().empty() && !Emu.IsChildProcess()) { // Try to add all related directories const std::set dirs = Emu.GetGameDirs(); @@ -3756,10 +4112,16 @@ bool ppu_initialize(const ppu_module& info, bool check_only) { for (auto& block : func.blocks) { + if (g_fxo->is_init() && !g_fxo->get().get_targets(block.first, block.second).empty()) + { + // Replace the block with ppu_far_jump + continue; + } + ppu_register_function_at(block.first, block.second); } - if (g_cfg.core.ppu_debug && func.size && func.toc != umax) + if (g_cfg.core.ppu_debug && func.size && func.toc != umax && !ppu_get_far_jump(func.addr)) { ppu_toc[func.addr] = func.toc; ppu_ref(func.addr) = &ppu_check_toc; @@ -3808,7 +4170,7 @@ bool ppu_initialize(const ppu_module& info, bool check_only) // Get cache path for this executable std::string cache_path; - if (info.name.empty()) + if (!info.cache.empty()) { cache_path = info.cache; } @@ -3858,7 +4220,7 @@ bool ppu_initialize(const ppu_module& info, bool check_only) }; // Permanently loaded compiled PPU modules (name -> data) - jit_module& jit_mod = g_fxo->get().get(cache_path + info.name + "_" + std::to_string(info.segs[0].addr)); + jit_module& jit_mod = g_fxo->get().get(cache_path + "_" + std::to_string(std::bit_cast(info.segs[0].ptr))); // Compiler instance (deferred initialization) std::shared_ptr& jit = jit_mod.pjit; @@ -4203,7 +4565,7 @@ bool ppu_initialize(const ppu_module& info, bool check_only) if (!workload.empty()) { - g_progr = "Compiling PPU modules..."; + *progr = "Compiling PPU modules..."; } // Create worker threads for compilation (TODO: how many threads) @@ -4271,10 +4633,10 @@ bool ppu_initialize(const ppu_module& info, bool check_only) if (workload.size() < link_workload.size()) { // Only show this message if this task is relevant - g_progr = "Linking PPU modules..."; + *progr = "Linking PPU modules..."; } - for (auto [obj_name, is_compiled] : link_workload) + for (const auto& [obj_name, is_compiled] : link_workload) { if (cpu ? cpu->state.all_of(cpu_flag::exit) : Emu.IsStopped()) { diff --git a/rpcs3/Emu/Cell/PPUThread.h b/rpcs3/Emu/Cell/PPUThread.h index 2bb03c412c..72b99eae61 100644 --- a/rpcs3/Emu/Cell/PPUThread.h +++ b/rpcs3/Emu/Cell/PPUThread.h @@ -262,6 +262,7 @@ public: u64 rtime{0}; alignas(64) std::byte rdata[128]{}; // Reservation data bool use_full_rdata{}; + u32 res_notify{}; union ppu_prio_t { diff --git a/rpcs3/Emu/Cell/PPUTranslator.cpp b/rpcs3/Emu/Cell/PPUTranslator.cpp index 83f0309f5d..8b3ac0b402 100644 --- a/rpcs3/Emu/Cell/PPUTranslator.cpp +++ b/rpcs3/Emu/Cell/PPUTranslator.cpp @@ -201,8 +201,12 @@ Function* PPUTranslator::Translate(const ppu_function& info) const auto vcheck = BasicBlock::Create(m_context, "__test", m_function); m_ir->CreateCondBr(m_ir->CreateIsNull(vstate), body, vcheck, m_md_likely); - // Create tail call to the check function m_ir->SetInsertPoint(vcheck); + + // Raise wait flag as soon as possible + m_ir->CreateAtomicRMW(llvm::AtomicRMWInst::Or, ptr, m_ir->getInt32((+cpu_flag::wait).operator u32()), llvm::MaybeAlign{4}, llvm::AtomicOrdering::AcquireRelease); + + // Create tail call to the check function Call(GetType(), "__check", m_thread, GetAddr())->setTailCall(); m_ir->CreateRetVoid(); } @@ -3306,8 +3310,11 @@ void PPUTranslator::SRD(ppu_opcode_t op) void PPUTranslator::LVRX(ppu_opcode_t op) { const auto addr = op.ra ? m_ir->CreateAdd(GetGpr(op.ra), GetGpr(op.rb)) : GetGpr(op.rb); - const auto data = ReadMemory(m_ir->CreateAnd(addr, ~0xfull), GetType(), m_is_be, 16); - set_vr(op.vd, pshufb(value(data), build(255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245, 244, 243, 242, 241, 240) + vsplat(trunc(value(addr) & 0xf)))); + const auto offset = eval(trunc(value(addr) & 0xf)); + + // Read from instruction address if offset is 0, this prevents accessing potentially bad memory from addr (because no actual memory is dereferenced) + const auto data = ReadMemory(m_ir->CreateAnd(m_ir->CreateSelect(m_ir->CreateIsNull(offset.value), m_reloc ? m_seg0 : GetAddr(0), addr), ~0xfull), GetType(), m_is_be, 16); + set_vr(op.vd, pshufb(value(data), build(255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245, 244, 243, 242, 241, 240) + vsplat(offset))); } void PPUTranslator::LSWI(ppu_opcode_t op) @@ -3613,11 +3620,11 @@ void PPUTranslator::LWZ(ppu_opcode_t op) m_may_be_mmio &= (op.ra != 1u && op.ra != 13u); // Stack register and TLS address register are unlikely to be used in MMIO address calculation m_may_be_mmio &= op.simm16 == 0 || spu_thread::test_is_problem_state_register_offset(op.uimm16, true, false); // Either exact MMIO address or MMIO base with completing s16 address offset - if (m_may_be_mmio && !op.simm16) + if (m_may_be_mmio) { struct instructions_data { - be_t insts[2]; + be_t insts[3]; }; // Quick invalidation: expect exact MMIO address, so if the register is being reused with different offset than it's likely not MMIO @@ -3633,6 +3640,12 @@ void PPUTranslator::LWZ(ppu_opcode_t op) continue; } + if (op.simm16 && spu_thread::test_is_problem_state_register_offset(test_op.uimm16, true, false)) + { + // Found register reuse with different MMIO offset + continue; + } + switch (g_ppu_itype.decode(inst)) { case ppu_itype::LWZ: @@ -3710,7 +3723,7 @@ void PPUTranslator::STW(ppu_opcode_t op) m_may_be_mmio &= (op.ra != 1u && op.ra != 13u); // Stack register and TLS address register are unlikely to be used in MMIO address calculation m_may_be_mmio &= op.simm16 == 0 || spu_thread::test_is_problem_state_register_offset(op.uimm16, false, true); // Either exact MMIO address or MMIO base with completing s16 address offset - if (m_may_be_mmio && !op.simm16) + if (m_may_be_mmio) { struct instructions_data { @@ -3730,6 +3743,12 @@ void PPUTranslator::STW(ppu_opcode_t op) continue; } + if (op.simm16 && spu_thread::test_is_problem_state_register_offset(test_op.uimm16, false, true)) + { + // Found register reuse with different MMIO offset + continue; + } + switch (g_ppu_itype.decode(inst)) { case ppu_itype::LWZ: diff --git a/rpcs3/Emu/Cell/RawSPUThread.cpp b/rpcs3/Emu/Cell/RawSPUThread.cpp index 580ee319a9..b322ab017f 100644 --- a/rpcs3/Emu/Cell/RawSPUThread.cpp +++ b/rpcs3/Emu/Cell/RawSPUThread.cpp @@ -21,7 +21,7 @@ inline void try_start(spu_thread& spu) }).second) { spu.state -= cpu_flag::stop; - spu.state.notify_one(cpu_flag::stop); + spu.state.notify_one(); } }; @@ -273,7 +273,7 @@ bool spu_thread::write_reg(const u32 addr, const u32 value) for (status_npc_sync_var old; (old = status_npc).status & SPU_STATUS_RUNNING;) { - status_npc.wait(old); + utils::bless>(&status_npc)[0].wait(old.status); } } } @@ -382,6 +382,18 @@ void spu_load_exec(const spu_exec_object& elf) spu->status_npc = {SPU_STATUS_RUNNING, elf.header.e_entry}; atomic_storage::release(spu->pc, elf.header.e_entry); + + const auto funcs = spu->discover_functions(0, { spu->ls , SPU_LS_SIZE }, true, umax); + + for (u32 addr : funcs) + { + spu_log.success("Found SPU function at: 0x%08x", addr); + } + + if (!funcs.empty()) + { + spu_log.success("Found %u SPU functions", funcs.size()); + } } void spu_load_rel_exec(const spu_rel_object& elf) @@ -410,7 +422,7 @@ void spu_load_rel_exec(const spu_rel_object& elf) { if (shdr.sh_type == sec_type::sht_progbits && shdr.sh_flags().all_of(sh_flag::shf_alloc)) { - std::memcpy(spu->_ptr(offs), shdr.bin.data(), shdr.sh_size); + std::memcpy(spu->_ptr(offs), shdr.get_bin().data(), shdr.sh_size); offs = utils::align(offs + shdr.sh_size, 4); } } diff --git a/rpcs3/Emu/Cell/SPUASMJITRecompiler.cpp b/rpcs3/Emu/Cell/SPUASMJITRecompiler.cpp index c76aba5d4f..1b64c00530 100644 --- a/rpcs3/Emu/Cell/SPUASMJITRecompiler.cpp +++ b/rpcs3/Emu/Cell/SPUASMJITRecompiler.cpp @@ -2607,6 +2607,12 @@ void spu_recompiler::BI(spu_opcode_t op) { spu_log.error("[0x%x] BI: no targets", m_pos); } + else if (op.d && found->second.size() == 1 && found->second[0] == spu_branch_target(m_pos, 1)) + { + // Interrupts-disable pattern + c->mov(SPU_OFF_8(interrupts_enabled), 0); + return; + } c->mov(*addr, SPU_OFF_32(gpr, op.ra, &v128::_u32, 3)); c->and_(*addr, 0x3fffc); diff --git a/rpcs3/Emu/Cell/SPUAnalyser.h b/rpcs3/Emu/Cell/SPUAnalyser.h index d448fca473..1d33fc7156 100644 --- a/rpcs3/Emu/Cell/SPUAnalyser.h +++ b/rpcs3/Emu/Cell/SPUAnalyser.h @@ -14,12 +14,13 @@ struct spu_itype static constexpr struct floating_tag{} floating{}; // Floating-Point Instructions static constexpr struct quadrop_tag{} _quadrop{}; // 4-op Instructions static constexpr struct xfloat_tag{} xfloat{}; // Instructions producing xfloat values + static constexpr struct zregmod_tag{} zregmod{}; // Instructions not modifying any GPR enum type : unsigned char { UNK = 0, - HEQ, + HEQ, // zregmod_tag first HEQI, HGT, HGTI, @@ -36,11 +37,21 @@ struct spu_itype NOP, SYNC, DSYNC, - MFSPR, MTSPR, + WRCH, + + STQD, // memory_tag first + STQX, + STQA, + STQR, // zregmod_tag last + LQD, + LQX, + LQA, + LQR, // memory_tag last + + MFSPR, RDCH, RCHCNT, - WRCH, BR, // branch_tag first BRA, @@ -59,15 +70,6 @@ struct spu_itype BIHZ, BIHNZ, // branch_tag last - LQD, // memory_tag first - LQX, - LQA, - LQR, - STQD, - STQX, - STQA, - STQR, // memory_tag last - ILH, // constant_tag_first ILHU, IL, @@ -267,7 +269,7 @@ struct spu_itype // Test for memory instruction friend constexpr bool operator &(type value, memory_tag) { - return value >= LQD && value <= STQR; + return value >= STQD && value <= LQR; } // Test for compare instruction @@ -293,6 +295,12 @@ struct spu_itype { return value >= ILH && value <= FSMBI; } + + // Test for non register-modifying instruction + friend constexpr bool operator &(type value, zregmod_tag) + { + return value >= HEQ && value <= STQR; + } }; struct spu_iflag diff --git a/rpcs3/Emu/Cell/SPUDisAsm.h b/rpcs3/Emu/Cell/SPUDisAsm.h index e891ed6fcc..0d5862025b 100644 --- a/rpcs3/Emu/Cell/SPUDisAsm.h +++ b/rpcs3/Emu/Cell/SPUDisAsm.h @@ -851,6 +851,13 @@ public: } void BR(spu_opcode_t op) { + if (op.rt && op.rt != 127u) + { + // Valid but makes no sense + DisAsm("br??", DisAsmBranchTarget(op.i16)); + return; + } + DisAsm("br", DisAsmBranchTarget(op.i16)); } void FSMBI(spu_opcode_t op) diff --git a/rpcs3/Emu/Cell/SPUOpcodes.h b/rpcs3/Emu/Cell/SPUOpcodes.h index 60e3d0d1b5..cea4513e3f 100644 --- a/rpcs3/Emu/Cell/SPUOpcodes.h +++ b/rpcs3/Emu/Cell/SPUOpcodes.h @@ -26,17 +26,17 @@ union spu_opcode_t bf_t i18; // 7..24 }; -inline u32 spu_branch_target(u32 pc, u32 imm = 0) +constexpr u32 spu_branch_target(u32 pc, u32 imm = 0) { return (pc + (imm << 2)) & 0x3fffc; } -inline u32 spu_ls_target(u32 pc, u32 imm = 0) +constexpr u32 spu_ls_target(u32 pc, u32 imm = 0) { return (pc + (imm << 2)) & 0x3fff0; } -inline u32 spu_decode(u32 inst) +constexpr u32 spu_decode(u32 inst) { return inst >> 21; } diff --git a/rpcs3/Emu/Cell/SPURecompiler.cpp b/rpcs3/Emu/Cell/SPURecompiler.cpp index 6df6380485..578d68bcf5 100644 --- a/rpcs3/Emu/Cell/SPURecompiler.cpp +++ b/rpcs3/Emu/Cell/SPURecompiler.cpp @@ -516,6 +516,70 @@ spu_cache::~spu_cache() { } +extern void utilize_spu_data_segment(u32 vaddr, const void* ls_data_vaddr, u32 size) +{ + if (vaddr % 4) + { + return; + } + + size &= -4; + + if (!size || vaddr + size > SPU_LS_SIZE) + { + return; + } + + if (!g_cfg.core.llvm_precompilation) + { + return; + } + + g_fxo->need(); + + if (!g_fxo->get().collect_funcs_to_precompile) + { + return; + } + + std::basic_string data(size / 4, 0); + std::memcpy(data.data(), ls_data_vaddr, size); + + spu_cache::precompile_data_t obj{vaddr, std::move(data)}; + + obj.funcs = spu_thread::discover_functions(vaddr, { reinterpret_cast(ls_data_vaddr), size }, vaddr != 0, umax); + + if (obj.funcs.empty()) + { + // Nothing to add + return; + } + + for (u32 addr : obj.funcs) + { + spu_log.notice("Found SPU function at: 0x%05x", addr); + } + + spu_log.notice("Found %u SPU functions", obj.funcs.size()); + + g_fxo->get().precompile_funcs.push(std::move(obj)); +} + +// For SPU cache validity check +static u16 calculate_crc16(const uchar* data, usz length) +{ + u16 crc = umax; + + while (length--) + { + u8 x = (crc >> 8) ^ *data++; + x ^= (x >> 4); + crc = static_cast((crc << 8) ^ (x << 12) ^ (x << 5) ^ x); + } + + return crc; +} + std::deque spu_cache::get() { std::deque result; @@ -530,18 +594,30 @@ std::deque spu_cache::get() // TODO: signal truncated or otherwise broken file while (true) { - be_t size; - be_t addr; - std::vector func; + struct block_info_t + { + be_t crc; + be_t size; + be_t addr; + } block_info{}; - if (!m_file.read(size) || !m_file.read(addr)) + if (!m_file.read(block_info)) { break; } - func.resize(size); + const u32 crc = block_info.crc; + const u32 size = block_info.size; + const u32 addr = block_info.addr; - if (m_file.read(func.data(), func.size() * 4) != func.size() * 4) + if (utils::add_saturate(addr, size * 4) > SPU_LS_SIZE) + { + break; + } + + std::vector func; + + if (!m_file.read(func, size)) { break; } @@ -552,6 +628,13 @@ std::deque spu_cache::get() continue; } + // CRC check is optional to be compatible with old format + if (crc && std::max(calculate_crc16(reinterpret_cast(func.data()), size * 4), 1) != crc) + { + // Invalid, but continue anyway + continue; + } + spu_program res; res.entry_point = addr; res.lower_bound = addr; @@ -572,6 +655,9 @@ void spu_cache::add(const spu_program& func) be_t size = ::size32(func.data); be_t addr = func.entry_point; + // Add CRC (forced non-zero) + size |= std::max(calculate_crc16(reinterpret_cast(func.data.data()), size * 4), 1) << 16; + const fs::iovec_clone gather[3] { {&size, sizeof(size)}, @@ -583,7 +669,7 @@ void spu_cache::add(const spu_program& func) m_file.write_gather(gather, 3); } -void spu_cache::initialize() +void spu_cache::initialize(bool build_existing_cache) { spu_runtime::g_interpreter = spu_runtime::g_gateway; @@ -618,6 +704,35 @@ void spu_cache::initialize() atomic_t fnext{}; atomic_t fail_flag{0}; + auto data_list = g_fxo->get().precompile_funcs.pop_all(); + g_fxo->get().collect_funcs_to_precompile = false; + + u32 total_precompile = 0; + + for (auto& sec : data_list) + { + total_precompile += sec.funcs.size(); + } + + const bool spu_precompilation_enabled = func_list.empty() && g_cfg.core.spu_cache && g_cfg.core.llvm_precompilation; + + if (spu_precompilation_enabled) + { + // What compiles in this case goes straight to disk + g_fxo->get() = std::move(cache); + } + else if (!build_existing_cache) + { + return; + } + else + { + total_precompile = 0; + data_list = {}; + } + + atomic_t data_indexer = 0; + if (g_cfg.core.spu_decoder == spu_decoder_type::dynamic || g_cfg.core.spu_decoder == spu_decoder_type::llvm) { if (auto compiler = spu_recompiler_base::make_llvm_recompiler(11)) @@ -647,10 +762,24 @@ void spu_cache::initialize() if (g_cfg.core.spu_decoder == spu_decoder_type::asmjit || g_cfg.core.spu_decoder == spu_decoder_type::llvm) { // Initialize progress dialog (wait for previous progress done) - thread_ctrl::wait_on(g_progr_ptotal, 0); + while (u32 v = g_progr_ptotal) + { + if (Emu.IsStopped() || !build_existing_cache) + { + // Workaround: disable progress dialog updates in the case of sole SPU precompilation + break; + } - g_progr_ptotal += ::size32(func_list); - progr.emplace("Building SPU cache..."); + thread_ctrl::wait_on(g_progr_ptotal, v); + } + + const u32 add_count = ::size32(func_list) + total_precompile; + + if (add_count) + { + g_progr_ptotal += build_existing_cache ? add_count : 0; + progr.emplace("Building SPU cache..."); + } worker_count = rpcs3::utils::get_max_threads(); } @@ -684,7 +813,7 @@ void spu_cache::initialize() std::vector> ls(0x10000); // Build functions - for (usz func_i = fnext++; func_i < func_list.size(); func_i = fnext++, g_progr_pdone++) + for (usz func_i = fnext++; func_i < func_list.size(); func_i = fnext++, g_progr_pdone += build_existing_cache ? 1 : 0) { const spu_program& func = std::as_const(func_list)[func_i]; @@ -736,6 +865,7 @@ void spu_cache::initialize() { // Likely, out of JIT memory. Signal to prevent further building. fail_flag |= 1; + continue; } // Clear fake LS @@ -744,15 +874,171 @@ void spu_cache::initialize() result++; } + u32 last_sec_idx = umax; + + for (usz func_i = data_indexer++;; func_i = data_indexer++, g_progr_pdone += build_existing_cache ? 1 : 0) + { + u32 passed_count = 0; + u32 func_addr = 0; + u32 next_func = 0; + u32 sec_addr = umax; + u32 sec_idx = 0; + std::basic_string_view inst_data; + + // Try to get the data this index points to + for (auto& sec : data_list) + { + if (func_i < passed_count + sec.funcs.size()) + { + const u32 func_idx = func_i - passed_count; + sec_addr = sec.vaddr; + func_addr = ::at32(sec.funcs, func_idx); + next_func = sec.funcs.size() >= func_idx + 1 ? SPU_LS_SIZE : sec.funcs[func_idx]; + inst_data = sec.inst_data; + break; + } + + passed_count += sec.funcs.size(); + sec_idx++; + } + + if (sec_addr == umax) + { + // End of compilation for thread + break; + } + + if (Emu.IsStopped() || fail_flag) + { + continue; + } + + if (last_sec_idx != sec_idx) + { + if (last_sec_idx != umax) + { + // Clear fake LS of previous section + auto& sec = data_list[last_sec_idx]; + std::memset(ls.data() + sec.vaddr / 4, 0, sec.inst_data.size() * 4); + } + + // Initialize LS with the entire section data + for (u32 i = 0, pos = sec_addr; i < inst_data.size(); i++, pos += 4) + { + ls[pos / 4] = std::bit_cast>(inst_data[i]); + } + + last_sec_idx = sec_idx; + } + + u32 block_addr = func_addr; + + // Call analyser + spu_program func2 = compiler->analyse(ls.data(), block_addr); + + std::map> targets; + + while (!func2.data.empty()) + { + const u32 last_inst = std::bit_cast>(func2.data.back()); + const u32 prog_size = func2.data.size(); + + if (!compiler->compile(std::move(func2))) + { + // Likely, out of JIT memory. Signal to prevent further building. + fail_flag |= 1; + break; + } + + result++; + + const u32 start_new = block_addr + prog_size * 4; + + if (start_new >= next_func || (start_new == next_func - 4 && ls[start_new / 4] == 0x200000u)) + { + // Completed + break; + } + + targets.insert(compiler->get_targets().begin(), compiler->get_targets().end()); + + if (auto type = g_spu_itype.decode(last_inst); + type == spu_itype::BRSL || type == spu_itype::BRASL || type == spu_itype::BISL || type == spu_itype::SYNC) + { + if (ls[start_new / 4] && g_spu_itype.decode(ls[start_new / 4]) != spu_itype::UNK) + { + spu_log.notice("Precompiling fallthrough to 0x%05x", start_new); + func2 = compiler->analyse(ls.data(), start_new); + block_addr = start_new; + continue; + } + } + + if (targets.empty()) + { + break; + } + + const auto upper = targets.upper_bound(func_addr); + + if (upper == targets.begin()) + { + break; + } + + u32 new_entry = umax; + + // Find the lowest target in the space in-between + for (auto it = std::prev(upper); it != targets.end() && it->first < start_new && new_entry > start_new; it++) + { + for (u32 target : it->second) + { + if (target >= start_new && target < next_func) + { + if (target < new_entry) + { + new_entry = target; + + if (new_entry == start_new) + { + // Cannot go lower + break; + } + } + } + } + } + + if (new_entry == umax) + { + break; + } + + if (!spu_thread::is_exec_code(new_entry, { reinterpret_cast(ls.data()), SPU_LS_SIZE })) + { + break; + } + + spu_log.notice("Precompiling filler space at 0x%05x (next=0x%05x)", new_entry, next_func); + func2 = compiler->analyse(ls.data(), new_entry); + block_addr = new_entry; + } + } + return result; }); + u32 built_total = 0; + // Join (implicitly) and print individual results for (u32 i = 0; i < workers.size(); i++) { spu_log.notice("SPU Runtime: Worker %u built %u programs.", i + 1, workers[i]); + built_total += workers[i]; } + spu_log.notice("SPU Runtime: Workers built %u programs.", built_total); + if (Emu.IsStopped()) { spu_log.error("SPU Runtime: Cache building aborted."); @@ -873,7 +1159,7 @@ void spu_cache::initialize() } // Initialize global cache instance - if (g_cfg.core.spu_cache) + if (g_cfg.core.spu_cache && cache) { g_fxo->get() = std::move(cache); } @@ -1896,6 +2182,149 @@ void spu_recompiler_base::old_interpreter(spu_thread& spu, void* ls, u8* /*rip*/ } } +std::vector spu_thread::discover_functions(u32 base_addr, std::span ls, bool is_known_addr, u32 /*entry*/) +{ + std::vector calls; + std::vector branches; + + calls.reserve(100); + + // Discover functions + // Use the most simple method: search for instructions that calls them + // And then filter invalid cases + // TODO: Does not detect jumptables or fixed-addr indirect calls + const v128 brasl_mask = is_known_addr ? v128::from32p(0x62u << 23) : v128::from32p(umax); + + for (u32 i = utils::align(base_addr, 0x10); i < std::min(base_addr + ls.size(), 0x3FFF0); i += 0x10) + { + // Search for BRSL LR and BRASL LR or BR + // TODO: BISL + const v128 inst = read_from_ptr>(ls.data(), i - base_addr); + const v128 cleared_i16 = gv_and32(inst, v128::from32p(utils::rol32(~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)); + const v128 result = eq_brsl | eq_brasl; + + if (!gv_testz(result)) + { + for (u32 j = 0; j < 4; j++) + { + if (result.u32r[j]) + { + calls.push_back(i + j * 4); + } + } + } + + if (!gv_testz(eq_br)) + { + for (u32 j = 0; j < 4; j++) + { + if (eq_br.u32r[j]) + { + branches.push_back(i + j * 4); + } + } + } + } + + calls.erase(std::remove_if(calls.begin(), calls.end(), [&](u32 caller) + { + // Check the validity of both the callee code and the following caller code + return !is_exec_code(caller, ls, base_addr) || !is_exec_code(caller + 4, ls, base_addr); + }), calls.end()); + + branches.erase(std::remove_if(branches.begin(), branches.end(), [&](u32 caller) + { + // Check the validity of the callee code + return !is_exec_code(caller, ls, base_addr); + }), branches.end()); + + std::vector addrs; + + for (u32 addr : calls) + { + const spu_opcode_t op{read_from_ptr>(ls, addr - base_addr)}; + + const u32 func = op_branch_targets(addr, op)[0]; + + if (func == umax || addr + 4 == func || func == addr || std::count(addrs.begin(), addrs.end(), func)) + { + continue; + } + + addrs.push_back(func); + } + + for (u32 addr : branches) + { + const spu_opcode_t op{read_from_ptr>(ls, addr - base_addr)}; + + const u32 func = op_branch_targets(addr, op)[0]; + + if (func == umax || addr + 4 == func || func == addr || !addr) + { + continue; + } + + // Search for AI R1, +x or OR R3/4, Rx, 0 + // Reasoning: AI R1, +x means stack pointer restoration, branch after that is likely a tail call + // R3 and R4 are common function arguments because they are the first two + for (u32 back = addr - 4, it = 10; it && back >= base_addr && back < std::min(base_addr + ls.size(), 0x3FFF0); it--, back -= 4) + { + const spu_opcode_t test_op{read_from_ptr>(ls, back - base_addr)}; + const auto type = g_spu_itype.decode(test_op.opcode); + + if (type & spu_itype::branch) + { + break; + } + + bool is_tail = false; + + if (type == spu_itype::AI && test_op.rt == 1u && test_op.ra == 1u) + { + if (test_op.si10 <= 0) + { + break; + } + + is_tail = true; + } + else if (!(type & spu_itype::zregmod)) + { + const u32 op_rt = type & spu_itype::_quadrop ? +test_op.rt4 : +test_op.rt; + + if (op_rt >= 80u && (type != spu_itype::LQD || test_op.ra != 1u)) + { + // Modifying non-volatile registers, not a call (and not context restoration) + break; + } + + //is_tail = op_rt == 3u || op_rt == 4u; + } + + if (!is_tail) + { + continue; + } + + if (std::count(addrs.begin(), addrs.end(), func)) + { + break; + } + + addrs.push_back(func); + break; + } + } + + std::sort(addrs.begin(), addrs.end()); + + return addrs; +} + spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point) { // Result: addr + raw instruction data @@ -2105,6 +2534,12 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point) spu_log.warning("[0x%x] At 0x%x: indirect branch to 0x%x%s", entry_point, pos, target, op.d ? " (D)" : op.e ? " (E)" : ""); + if (type == spu_itype::BI && target == pos + 4 && op.d) + { + // Disable interrupts idiom + break; + } + m_targets[pos].push_back(target); if (g_cfg.core.spu_block_size == spu_block_size_type::giga) @@ -5313,7 +5748,7 @@ public: if (src > 0x40000) { // Use the xfloat hint to create 256-bit (4x double) PHI - llvm::Type* type = g_cfg.core.spu_accurate_xfloat && bb.reg_maybe_xf[i] ? get_type() : get_reg_type(i); + llvm::Type* type = g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::accurate && bb.reg_maybe_xf[i] ? get_type() : get_reg_type(i); const auto _phi = m_ir->CreatePHI(type, ::size32(bb.preds), fmt::format("phi0x%05x_r%u", baddr, i)); m_block->phi[i] = _phi; @@ -6513,6 +6948,24 @@ public: } case SPU_RdDec: { + if (utils::get_tsc_freq() && !(g_cfg.core.spu_loop_detection) && (g_cfg.core.clocks_scale == 100)) + { + 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->CreateAdd(tscx, tscm); + + 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()); + const auto deltax = m_ir->CreateSelect(frzev, delta, m_ir->getInt32(0)); + res.value = m_ir->CreateSub(dec_value, deltax); + break; + } + res.value = call("spu_read_decrementer", &exec_read_dec, m_thread); break; } @@ -7180,7 +7633,20 @@ public: case SPU_WrDec: { call("spu_get_events", &exec_get_events, m_thread, m_ir->getInt32(SPU_EVENT_TM)); - m_ir->CreateStore(call("get_timebased_time", &get_timebased_time), spu_ptr(&spu_thread::ch_dec_start_timestamp)); + + if (utils::get_tsc_freq() && !(g_cfg.core.spu_loop_detection) && (g_cfg.core.clocks_scale == 100)) + { + 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->CreateAdd(tscx, tscm); + m_ir->CreateStore(tsctb, spu_ptr(&spu_thread::ch_dec_start_timestamp)); + } + else + { + 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)); return; @@ -7788,19 +8254,27 @@ public: void ROTQMBY(spu_opcode_t op) { const auto a = get_vr(op.ra); - const auto b = get_vr(op.rb); + const auto b = get_vr(op.rb); + + auto minusb = eval(-b); + if (auto [ok, x] = match_expr(b, -match()); ok) + { + minusb = eval(x); + } + + const auto minusbx = bitcast(minusb); // Data with swapped endian from a load instruction if (auto [ok, as] = match_expr(a, byteswap(match())); ok) { const auto sc = build(15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0); - const auto sh = sc - (-splat_scalar(b) & 0x1f); + const auto sh = sc - (splat_scalar(minusbx) & 0x1f); set_vr(op.rt, pshufb(as, sh)); return; } const auto sc = build(112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127); - const auto sh = sc + (-splat_scalar(b) & 0x1f); + const auto sh = sc + (splat_scalar(minusbx) & 0x1f); set_vr(op.rt, pshufb(a, sh)); } @@ -8627,6 +9101,20 @@ public: { if (data == v128::from8p(data._u8[0])) { + if (m_use_avx512_icl) + { + if (perm_only) + { + set_vr(op.rt4, vperm2b256to128(as, b, c)); + return; + } + + const auto m = gf2p8affineqb(c, build(0x40, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x40, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20), 0x7f); + const auto mm = select(noncast(m) >= 0, splat(0), m); + const auto ab = vperm2b256to128(as, b, c); + set_vr(op.rt4, select(noncast(c) >= 0, ab, mm)); + return; + } // See above const auto x = pshufb(build(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x80, 0x80), (c >> 4)); const auto ax = pshufb(as, c); @@ -8661,6 +9149,42 @@ public: if (m_use_avx512_icl && (op.ra != op.rb || m_interp_magn)) { + if (auto [ok, data] = get_const_vector(b.value, m_pos); ok) + { + if (data == v128::from8p(data._u8[0])) + { + if (perm_only) + { + set_vr(op.rt4, vperm2b256to128(a, b, eval(c ^ 0xf))); + return; + } + + const auto m = gf2p8affineqb(c, build(0x40, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x40, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20), 0x7f); + const auto mm = select(noncast(m) >= 0, splat(0), m); + const auto ab = vperm2b256to128(a, b, eval(c ^ 0xf)); + set_vr(op.rt4, select(noncast(c) >= 0, ab, mm)); + return; + } + } + + if (auto [ok, data] = get_const_vector(a.value, m_pos); ok) + { + if (data == v128::from8p(data._u8[0])) + { + if (perm_only) + { + set_vr(op.rt4, vperm2b256to128(b, a, eval(c ^ 0x1f))); + return; + } + + const auto m = gf2p8affineqb(c, build(0x40, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x40, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20), 0x7f); + const auto mm = select(noncast(m) >= 0, splat(0), m); + const auto ab = vperm2b256to128(b, a, eval(c ^ 0x1f)); + set_vr(op.rt4, select(noncast(c) >= 0, ab, mm)); + return; + } + } + if (perm_only) { set_vr(op.rt4, vperm2b(a, b, eval(c ^ 0xf))); @@ -8865,7 +9389,7 @@ public: void FREST(spu_opcode_t op) { // TODO - if (g_cfg.core.spu_accurate_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::accurate) { const auto a = get_vr(op.ra); const auto mask_ov = sext(bitcast(fabs(a)) > splat(0x7e7fffff)); @@ -8874,7 +9398,7 @@ public: return; } - if (g_cfg.core.spu_approx_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::approximate) { register_intrinsic("spu_frest", [&](llvm::CallInst* ci) { @@ -8907,13 +9431,13 @@ public: void FRSQEST(spu_opcode_t op) { // TODO - if (g_cfg.core.spu_accurate_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::accurate) { set_vr(op.rt, fsplat(1.0) / fsqrt(fabs(get_vr(op.ra)))); return; } - if (g_cfg.core.spu_approx_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::approximate) { register_intrinsic("spu_frsqest", [&](llvm::CallInst* ci) { @@ -8945,7 +9469,7 @@ public: void FCGT(spu_opcode_t op) { - if (g_cfg.core.spu_accurate_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::accurate) { set_vr(op.rt, sext(fcmp_ord(get_vr(op.ra) > get_vr(op.rb)))); return; @@ -8992,7 +9516,7 @@ public: return eval(sext(bitcast(a) > bitcast(b))); } - if (g_cfg.core.spu_approx_xfloat || g_cfg.core.spu_relaxed_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::approximate || g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::relaxed) { const auto ai = eval(bitcast(a)); const auto bi = eval(bitcast(b)); @@ -9023,7 +9547,7 @@ public: void FCMGT(spu_opcode_t op) { - if (g_cfg.core.spu_accurate_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::accurate) { set_vr(op.rt, sext(fcmp_ord(fabs(get_vr(op.ra)) > fabs(get_vr(op.rb))))); return; @@ -9069,7 +9593,7 @@ public: return eval(sext(mai > mbi)); } - if (g_cfg.core.spu_approx_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::approximate) { return eval(sext(fcmp_uno(ma > mb) & (mai > mbi))); } @@ -9090,7 +9614,7 @@ public: void FA(spu_opcode_t op) { - if (g_cfg.core.spu_accurate_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::accurate) { set_vr(op.rt, get_vr(op.ra) + get_vr(op.rb)); return; @@ -9115,7 +9639,7 @@ public: void FS(spu_opcode_t op) { - if (g_cfg.core.spu_accurate_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::accurate) { set_vr(op.rt, get_vr(op.ra) - get_vr(op.rb)); return; @@ -9126,7 +9650,7 @@ public: const auto a = value(ci->getOperand(0)); const auto b = value(ci->getOperand(1)); - if (g_cfg.core.spu_approx_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::approximate) { const auto bc = clamp_smax(b); // for #4478 return eval(a - bc); @@ -9148,7 +9672,7 @@ public: void FM(spu_opcode_t op) { - if (g_cfg.core.spu_accurate_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::accurate) { set_vr(op.rt, get_vr(op.ra) * get_vr(op.rb)); return; @@ -9159,7 +9683,7 @@ public: const auto a = value(ci->getOperand(0)); const auto b = value(ci->getOperand(1)); - if (g_cfg.core.spu_approx_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::approximate) { if (a.value == b.value) { @@ -9195,7 +9719,7 @@ public: void FESD(spu_opcode_t op) { - if (g_cfg.core.spu_accurate_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::accurate) { const auto r = zshuffle(get_vr(op.ra), 1, 3); const auto d = bitcast(r); @@ -9225,7 +9749,7 @@ public: void FRDS(spu_opcode_t op) { - if (g_cfg.core.spu_accurate_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::accurate) { const auto r = get_vr(op.ra); const auto d = bitcast(r); @@ -9256,7 +9780,7 @@ public: void FCEQ(spu_opcode_t op) { - if (g_cfg.core.spu_accurate_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::accurate) { set_vr(op.rt, sext(fcmp_ord(get_vr(op.ra) == get_vr(op.rb)))); return; @@ -9309,7 +9833,7 @@ public: return eval(sext(bitcast(a) == bitcast(b))); } - if (g_cfg.core.spu_approx_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::approximate) { return eval(sext(fcmp_ord(a == b)) | sext(bitcast(a) == bitcast(b))); } @@ -9330,7 +9854,7 @@ public: void FCMEQ(spu_opcode_t op) { - if (g_cfg.core.spu_accurate_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::accurate) { set_vr(op.rt, sext(fcmp_ord(fabs(get_vr(op.ra)) == fabs(get_vr(op.rb))))); return; @@ -9386,7 +9910,7 @@ public: return eval(sext(bitcast(fa) == bitcast(fb))); } - if (g_cfg.core.spu_approx_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::approximate) { return eval(sext(fcmp_ord(fa == fb)) | sext(bitcast(fa) == bitcast(fb))); } @@ -9479,7 +10003,7 @@ public: void FNMS(spu_opcode_t op) { // See FMA. - if (g_cfg.core.spu_accurate_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::accurate) { const auto [a, b, c] = get_vrs(op.ra, op.rb, op.rc); set_vr(op.rt4, fmuladd(-a, b, c)); @@ -9492,7 +10016,7 @@ public: const auto b = value(ci->getOperand(1)); const auto c = value(ci->getOperand(2)); - if (g_cfg.core.spu_approx_xfloat || g_cfg.core.spu_relaxed_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::approximate || g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::relaxed) { return fma32x4(eval(-clamp_smax(a)), clamp_smax(b), c); } @@ -9514,7 +10038,7 @@ public: void FMA(spu_opcode_t op) { // Hardware FMA produces the same result as multiple + add on the limited double range (xfloat). - if (g_cfg.core.spu_accurate_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::accurate) { const auto [a, b, c] = get_vrs(op.ra, op.rb, op.rc); set_vr(op.rt4, fmuladd(a, b, c)); @@ -9527,7 +10051,7 @@ public: const auto b = value(ci->getOperand(1)); const auto c = value(ci->getOperand(2)); - if (g_cfg.core.spu_approx_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::approximate) { const auto ma = sext(fcmp_uno(a != fsplat(0.))); const auto mb = sext(fcmp_uno(b != fsplat(0.))); @@ -9588,7 +10112,7 @@ public: void FMS(spu_opcode_t op) { // See FMA. - if (g_cfg.core.spu_accurate_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::accurate) { const auto [a, b, c] = get_vrs(op.ra, op.rb, op.rc); set_vr(op.rt4, fmuladd(a, b, -c)); @@ -9601,7 +10125,7 @@ public: const auto b = value(ci->getOperand(1)); const auto c = value(ci->getOperand(2)); - if (g_cfg.core.spu_approx_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::approximate) { return fma32x4(clamp_smax(a), clamp_smax(b), eval(-c)); } @@ -9635,7 +10159,7 @@ public: void FI(spu_opcode_t op) { // TODO - if (g_cfg.core.spu_accurate_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::accurate) { set_vr(op.rt, get_vr(op.rb)); // const auto [a, b] = get_vrs(op.ra, op.rb); @@ -9663,7 +10187,7 @@ public: return bitcast((b & 0xff800000u) | (bitcast(fpcast(bnew)) & ~0xff800000u)); // Inject old sign and exponent }); - if (g_cfg.core.spu_approx_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::approximate) { register_intrinsic("spu_re", [&](llvm::CallInst* ci) { @@ -9722,7 +10246,7 @@ public: void CFLTS(spu_opcode_t op) { - if (g_cfg.core.spu_accurate_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::accurate) { value_t a = get_vr(op.ra); value_t s; @@ -9796,7 +10320,7 @@ public: void CFLTU(spu_opcode_t op) { - if (g_cfg.core.spu_accurate_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::accurate) { value_t a = get_vr(op.ra); value_t s; @@ -9863,6 +10387,15 @@ public: a = eval(a * s); value_t r; + + if (m_use_avx512) + { + const auto sc = eval(bitcast(max(bitcast(a),splat(0x0)))); + r.value = m_ir->CreateFPToUI(sc.value, get_type()); + set_vr(op.rt, r); + return; + } + r.value = m_ir->CreateFPToUI(a.value, get_type()); set_vr(op.rt, select(bitcast(a) > splat(((32 + 127) << 23) - 1), splat(-1), r & ~(bitcast(a) >> 31))); } @@ -9870,7 +10403,7 @@ public: void CSFLT(spu_opcode_t op) { - if (g_cfg.core.spu_accurate_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::accurate) { value_t a = get_vr(op.ra); value_t r; @@ -9910,7 +10443,7 @@ public: void CUFLT(spu_opcode_t op) { - if (g_cfg.core.spu_accurate_xfloat) + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::accurate) { value_t a = get_vr(op.ra); value_t r; @@ -10495,6 +11028,13 @@ public: // Create jump table if necessary (TODO) const auto tfound = m_targets.find(m_pos); + 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)); + return; + } + if (!op.d && !op.e && tfound != m_targets.end() && tfound->second.size() > 1) { // Shift aligned address for switch @@ -10994,7 +11534,7 @@ struct spu_llvm_worker return; } - thread_ctrl::wait_on(registered, nullptr); + thread_ctrl::wait_on(utils::bless>(®istered)[1], 0); slice = registered.pop_all(); }()) { @@ -11161,7 +11701,7 @@ struct spu_llvm { // Interrupt profiler thread and put it to sleep static_cast(prof_mutex.reset()); - thread_ctrl::wait_on(registered, nullptr); + thread_ctrl::wait_on(utils::bless>(®istered)[1], 0); continue; } diff --git a/rpcs3/Emu/Cell/SPURecompiler.h b/rpcs3/Emu/Cell/SPURecompiler.h index 551e73a13a..02870a4105 100644 --- a/rpcs3/Emu/Cell/SPURecompiler.h +++ b/rpcs3/Emu/Cell/SPURecompiler.h @@ -34,7 +34,18 @@ public: void add(const struct spu_program& func); - static void initialize(); + static void initialize(bool build_existing_cache = true); + + struct precompile_data_t + { + u32 vaddr; + std::basic_string inst_data; + std::vector funcs; + }; + + bool collect_funcs_to_precompile = true; + + lf_queue precompile_funcs; }; struct spu_program @@ -331,6 +342,11 @@ public: return *m_spurt; } + const auto& get_targets() const + { + return m_targets; + } + // Create recompiler instance (ASMJIT) static std::unique_ptr make_asmjit_recompiler(); diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 7a36b46565..9531304fdf 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -37,6 +37,15 @@ #include "util/sysinfo.hpp" #include "util/serialization.hpp" +#if defined(ARCH_X64) +#ifdef _MSC_VER +#include +#include +#else +#include +#endif +#endif + using spu_rdata_t = decltype(spu_thread::rdata); template <> @@ -320,6 +329,40 @@ extern void mov_rdata_nt(spu_rdata_t& _dst, const spu_rdata_t& _src) #endif } +#if defined(_MSC_VER) +#define mwaitx_func +#define waitpkg_func +#else +#define mwaitx_func __attribute__((__target__("mwaitx"))) +#define waitpkg_func __attribute__((__target__("waitpkg"))) +#endif + +#if defined(ARCH_X64) +// Waits for a number of TSC clock cycles in power optimized state +// Cstate is represented in bits [7:4]+1 cstate. So C0 requires bits [7:4] to be set to 0xf, C1 requires bits [7:4] to be set to 0. +template +mwaitx_func static void __mwaitx(u32 cycles, u32 cstate, const void* cline, const Args&... args) +{ + constexpr u32 timer_enable = 0x2; + + // monitorx will wake if the cache line is written to, use it for reservations which fits it almost perfectly + _mm_monitorx(const_cast(cline), 0, 0); + + // Use static function to force inline + if (T::needs_wait(args...)) + { + _mm_mwaitx(timer_enable, cstate, cycles); + } +} + +// First bit indicates cstate, 0x0 for C.02 state (lower power) or 0x1 for C.01 state (higher power) +waitpkg_func static void __tpause(u32 cycles, u32 cstate) +{ + const u64 tsc = utils::get_tsc() + cycles; + _tpause(cstate, tsc); +} +#endif + void do_cell_atomic_128_store(u32 addr, const void* to_write); extern thread_local u64 g_tls_fault_spu; @@ -445,6 +488,12 @@ std::array op_branch_targets(u32 pc, spu_opcode_t op) { const int index = (type == spu_itype::BR || type == spu_itype::BRA || type == spu_itype::BRSL || type == spu_itype::BRASL ? 0 : 1); res[index] = (spu_branch_target(type == spu_itype::BRASL || type == spu_itype::BRA ? 0 : pc, op.i16)); + + if (res[0] == res[1]) + { + res[1] = umax; + } + break; } case spu_itype::IRET: @@ -1147,7 +1196,7 @@ void spu_thread::dump_regs(std::string& ret, std::any& /*custom_data*/) const } } - if (i3 >= 0x80 && is_exec_code(i3, ls)) + if (i3 >= 0x80 && is_exec_code(i3, { ls, SPU_LS_SIZE })) { dis_asm.disasm(i3); fmt::append(ret, " -> %s", dis_asm.last_opcode); @@ -1233,6 +1282,7 @@ std::vector> spu_thread::dump_callstack_list() const bool first = true; const v128 gpr0 = gpr[0]; + const u32 _pc = pc; // Declare first 128-bytes as invalid for stack (common values such as 0 do not make sense here) for (u32 sp = gpr[1]._u32[3]; (sp & 0xF) == 0u && sp >= 0x80u && sp <= 0x3FFE0u; first = false) @@ -1250,14 +1300,16 @@ std::vector> spu_thread::dump_callstack_list() const return true; } - return !addr || !is_exec_code(addr, ls); + return !addr || !is_exec_code(addr, { ls, SPU_LS_SIZE }); }; if (first && lr._u32[3] != gpr0._u32[3] && !is_invalid(gpr0)) { // Detect functions with no stack or before LR has been stored - std::vector passed(SPU_LS_SIZE / 4); - std::vector start_points{pc}; + std::vector passed(_pc / 4); + + // Start with PC + std::basic_string start_points{_pc}; bool is_ok = false; bool all_failed = false; @@ -1266,7 +1318,11 @@ std::vector> spu_thread::dump_callstack_list() const { for (u32 i = start_points[start]; i < SPU_LS_SIZE;) { - if (passed[i / 4]) + if (i / 4 >= passed.size()) + { + passed.resize(i / 4 + 1); + } + else if (passed[i / 4]) { // Already passed break; @@ -1279,7 +1335,7 @@ std::vector> spu_thread::dump_callstack_list() const if (start == 0 && type == spu_itype::STQD && op.ra == 1u && op.rt == 0u) { - // Saving LR to stack: this is indeed a new function + // Saving LR to stack: this is indeed a new function (ok because LR has not been saved yet) is_ok = true; break; } @@ -1317,12 +1373,23 @@ std::vector> spu_thread::dump_callstack_list() const for (usz res_i = 0; res_i < results.size(); res_i++) { const u32 route_pc = results[res_i]; - if (route_pc < SPU_LS_SIZE && !passed[route_pc / 4]) + + if (route_pc >= SPU_LS_SIZE) + { + continue; + } + + if (route_pc / 4 >= passed.size()) + { + passed.resize(route_pc / 4 + 1); + } + + if (!passed[route_pc / 4]) { if (proceeded) { // Remember next route start point - start_points.emplace_back(route_pc); + start_points.push_back(route_pc); } else { @@ -1821,7 +1888,7 @@ spu_thread::~spu_thread() utils::memory_release(ls - SPU_LS_SIZE * 2, SPU_LS_SIZE * 5); perf_log.notice("Perf stats for transactions: success %u, failure %u", stx, ftx); - perf_log.notice("Perf stats for PUTLLC reload: successs %u, failure %u", last_succ, last_fail); + perf_log.notice("Perf stats for PUTLLC reload: success %u, failure %u", last_succ, last_fail); } u8* spu_thread::map_ls(utils::shm& shm, void* ptr) @@ -2418,7 +2485,7 @@ void spu_thread::do_dma_transfer(spu_thread* _this, const spu_mfc_cmd& args, u8* } } - if (++i < 10) + if (true || ++i < 10) { busy_wait(500); } @@ -2426,7 +2493,7 @@ void spu_thread::do_dma_transfer(spu_thread* _this, const spu_mfc_cmd& args, u8* { // Wait _cpu->state += cpu_flag::wait + cpu_flag::temp; - bits->wait(old, wmask); + // bits->wait(old, wmask); _cpu->check_state(); } }()) @@ -2542,7 +2609,7 @@ void spu_thread::do_dma_transfer(spu_thread* _this, const spu_mfc_cmd& args, u8* v &= ~wmask; }); - bits->notify_all(wmask); + // bits->notify_all(wmask); if (size == size0) { @@ -3588,7 +3655,7 @@ bool spu_thread::do_putllc(const spu_mfc_cmd& args) { if (raddr) { - vm::reservation_notifier(addr).notify_all(-128); + vm::reservation_notifier(addr).notify_all(); raddr = 0; } @@ -3775,7 +3842,7 @@ void spu_thread::do_putlluc(const spu_mfc_cmd& args) } do_cell_atomic_128_store(addr, _ptr(args.lsa & 0x3ff80)); - vm::reservation_notifier(addr).notify_all(-128); + vm::reservation_notifier(addr).notify_all(); } bool spu_thread::do_mfc(bool can_escape, bool must_finish) @@ -3952,29 +4019,97 @@ bool spu_thread::check_mfc_interrupts(u32 next_pc) return false; } -bool spu_thread::is_exec_code(u32 addr, const u8* ls_ptr) +bool spu_thread::is_exec_code(u32 addr, std::span ls_ptr, u32 base_addr) { - if (addr & ~0x3FFFC) - { - return false; - } - for (u32 i = 0; i < 30; i++) { - const u32 addr0 = addr + (i * 4); - const u32 op = read_from_ptr>(ls_ptr + addr0); - const auto type = s_spu_itype.decode(op); + if (addr & ~0x3FFFC) + { + return false; + } - if (type == spu_itype::UNK || !op) + if (addr < base_addr || addr >= base_addr + ls_ptr.size()) + { + return false; + } + + const u32 addr0 = spu_branch_target(addr); + const spu_opcode_t op{read_from_ptr>(ls_ptr, addr0 - base_addr)}; + const auto type = s_spu_itype.decode(op.opcode); + + if (type == spu_itype::UNK || !op.opcode) + { + return false; + } + + if (type == spu_itype::STOP && op.rb) { return false; } if (type & spu_itype::branch) { - // TODO - break; + if (type == spu_itype::BR && op.rt && op.rt != 127u) + { + return false; + } + + auto results = op_branch_targets(addr, op); + + if (results[0] == umax) + { + switch (type) + { + case spu_itype::BIZ: + case spu_itype::BINZ: + case spu_itype::BIHZ: + case spu_itype::BIHNZ: + { + results[0] = addr + 4; + break; + } + default: + { + break; + } + } + + if (results[0] == umax) + { + break; + } + } + + for (usz res_i = 1; res_i < results.size(); res_i++) + { + const u32 route_pc = results[res_i]; + + if (route_pc >= SPU_LS_SIZE) + { + continue; + } + + if (route_pc < base_addr || route_pc >= base_addr + ls_ptr.size()) + { + return false; + } + + // Test the validity of a single instruction of the optional target + // This function can't be too slow and is unlikely to improve results by a great deal + const u32 op0 = read_from_ptr>(ls_ptr, route_pc - base_addr); + const spu_itype::type type0 = s_spu_itype.decode(op0); + + if (type0 == spu_itype::UNK || !op0) + { + return false; + } + } + + addr = spu_branch_target(results[0]); + continue; } + + addr += 4; } return true; @@ -4113,7 +4248,32 @@ bool spu_thread::process_mfc_cmd() if (getllar_busy_waiting_switch == 1) { - busy_wait(300); +#if defined(ARCH_X64) + if (utils::has_um_wait()) + { + if (utils::has_waitpkg()) + { + __tpause(std::min(getllar_spin_count, 10) * 500, 0x1); + } + else + { + struct check_wait_t + { + static FORCE_INLINE bool needs_wait(u64 rtime, const atomic_t& mem_rtime) noexcept + { + return rtime == mem_rtime; + } + }; + + // Provide the first X64 cache line of the reservation to be tracked + __mwaitx(std::min(getllar_spin_count, 17) * 500, 0xf0, std::addressof(data), +rtime, vm::reservation_acquire(addr)); + } + } + else +#endif + { + busy_wait(300); + } } return true; @@ -4908,7 +5068,11 @@ s64 spu_thread::get_ch_value(u32 ch) } } +#ifdef __linux__ + const bool reservation_busy_waiting = false; +#else const bool reservation_busy_waiting = ((utils::get_tsc() >> 8) % 100 + ((raddr == spurs_addr) ? 50 : 0)) < g_cfg.core.spu_reservation_busy_waiting_percentage; +#endif for (; !events.count; events = get_events(mask1 & ~SPU_EVENT_LR, true, true)) { @@ -4930,8 +5094,11 @@ s64 spu_thread::get_ch_value(u32 ch) if (raddr && (mask1 & ~SPU_EVENT_TM) == SPU_EVENT_LR) { // Don't busy-wait with TSX - memory is sensitive - if (!reservation_busy_waiting) + if (g_use_rtm || !reservation_busy_waiting) { +#ifdef __linux__ + vm::reservation_notifier(raddr).wait(rtime, atomic_wait_timeout{50'000}); +#else if (raddr - spurs_addr <= 0x80 && !g_cfg.core.spu_accurate_reservations && mask1 == SPU_EVENT_LR) { atomic_wait_engine::set_one_time_use_wait_callback(+[](u64) -> bool @@ -4944,7 +5111,7 @@ s64 spu_thread::get_ch_value(u32 ch) // Wait without timeout, in this situation we have notifications for all writes making it possible // Abort notifications are handled specially for performance reasons - vm::reservation_notifier(raddr).wait(rtime, -128); + vm::reservation_notifier(raddr).wait(rtime); continue; } @@ -4976,7 +5143,8 @@ s64 spu_thread::get_ch_value(u32 ch) return true; }); - vm::reservation_notifier(raddr).wait(rtime, -128, atomic_wait_timeout{80'000}); + vm::reservation_notifier(raddr).wait(rtime, atomic_wait_timeout{80'000}); +#endif } else { @@ -5464,7 +5632,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(cpu_flag::signal); + spu.state.notify_one(); return; } @@ -5482,7 +5650,7 @@ extern void resume_spu_thread_group_from_waiting(spu_thread& spu) thread->state -= cpu_flag::suspend; } - thread->state.notify_one(cpu_flag::suspend + cpu_flag::signal); + thread->state.notify_one(); } } } @@ -6022,12 +6190,12 @@ spu_exec_object spu_thread::capture_memory_as_elf(std::span>(all_data.data(), pc0 - 4); + const u32 op = read_from_ptr>(all_data, pc0 - 4); // Try to find function entry (if they are placed sequentially search for BI $LR of previous function) if (!op || op == 0x35000000u || s_spu_itype.decode(op) == spu_itype::UNK) { - if (is_exec_code(pc0, all_data.data())) + if (is_exec_code(pc0, { all_data.data(), SPU_LS_SIZE })) break; } } @@ -6037,7 +6205,7 @@ spu_exec_object spu_thread::capture_memory_as_elf(std::span>(&data)[1], u32{bit_wait >> 32}); old = data; if (!(old & bit_wait)) @@ -6325,7 +6493,7 @@ bool spu_channel::push_wait(cpu_thread& spu, u32 value, bool push) return false; } - thread_ctrl::wait_on(data, state); + thread_ctrl::wait_on(utils::bless>(&data)[1], u32(state >> 32)); state = data; } } @@ -6369,7 +6537,7 @@ std::pair spu_channel_4_t::pop_wait(cpu_thread& spu) while (true) { - thread_ctrl::wait_on(values, old); + thread_ctrl::wait_on(utils::bless>(&values)[0], u32(u64(std::bit_cast(old)))); old = values; if (!old.waiting) diff --git a/rpcs3/Emu/Cell/SPUThread.h b/rpcs3/Emu/Cell/SPUThread.h index 4801c7671c..28fc40f7da 100644 --- a/rpcs3/Emu/Cell/SPUThread.h +++ b/rpcs3/Emu/Cell/SPUThread.h @@ -235,7 +235,7 @@ public: // Turn off waiting bit manually (must succeed because waiting bit can only be resetted by the thread pushed to jostling_value) ensure(this->data.bit_test_reset(off_wait)); - data.notify_one(); + utils::bless>(&data)[1].notify_one(); } // Return true if count has changed from 0 to 1, this condition is considered satisfied even if we pushed a value directly to the special storage for waiting SPUs @@ -294,7 +294,7 @@ public: if ((old & mask) == mask) { - data.notify_one(); + utils::bless>(&data)[1].notify_one(); } return static_cast(old); @@ -386,7 +386,7 @@ struct spu_channel_4_t // Turn off waiting bit manually (must succeed because waiting bit can only be resetted by the thread pushing to jostling_value) ensure(atomic_storage::exchange(values.raw().waiting, 0)); - values.notify_one(); + utils::bless>(&values)[0].notify_one(); } return; @@ -825,7 +825,8 @@ public: void set_events(u32 bits); void set_interrupt_status(bool enable); bool check_mfc_interrupts(u32 next_pc); - static bool is_exec_code(u32 addr, const u8* ls_ptr); // Only a hint, do not rely on it other than debugging purposes + static bool is_exec_code(u32 addr, std::span ls_ptr, u32 base_addr = 0); // Only a hint, do not rely on it other than debugging purposes + static std::vector discover_functions(u32 base_addr, std::span ls, bool is_known_addr, u32 /*entry*/); u32 get_ch_count(u32 ch); s64 get_ch_value(u32 ch); bool set_ch_value(u32 ch, u32 value); diff --git a/rpcs3/Emu/Cell/lv2/lv2.cpp b/rpcs3/Emu/Cell/lv2/lv2.cpp index 4b775867d3..f49e070d03 100644 --- a/rpcs3/Emu/Cell/lv2/lv2.cpp +++ b/rpcs3/Emu/Cell/lv2/lv2.cpp @@ -2,6 +2,7 @@ #include "Emu/System.h" #include "Emu/system_config.h" #include "Emu/Memory/vm_ptr.h" +#include "Emu/Memory/vm_reservation.h" #include "Emu/Memory/vm_locking.h" #include "Emu/Cell/PPUFunction.h" @@ -53,6 +54,17 @@ #include #include #include "util/tsc.hpp" +#include "util/sysinfo.hpp" + +#if defined(ARCH_X64) +#ifdef _MSC_VER +#include +#include +#else +#include +#endif +#endif + extern std::string ppu_get_syscall_name(u64 code); @@ -1257,6 +1269,31 @@ bool lv2_obj::sleep(cpu_thread& cpu, const u64 timeout) prepare_for_sleep(cpu); } + if (cpu.id_type() == 1) + { + if (u32 addr = static_cast(cpu).res_notify) + { + static_cast(cpu).res_notify = 0; + + const usz notify_later_idx = std::basic_string_view{g_to_notify, std::size(g_to_notify)}.find_first_of(std::add_pointer_t{}); + + if (notify_later_idx != umax) + { + g_to_notify[notify_later_idx] = &vm::reservation_notifier(addr); + + if (notify_later_idx < std::size(g_to_notify) - 1) + { + // Null-terminate the list if it ends before last slot + g_to_notify[notify_later_idx + 1] = nullptr; + } + } + else + { + vm::reservation_notifier(addr).notify_all(); + } + } + } + bool result = false; const u64 current_time = get_guest_system_time(); { @@ -1283,6 +1320,31 @@ bool lv2_obj::sleep(cpu_thread& cpu, const u64 timeout) bool lv2_obj::awake(cpu_thread* thread, s32 prio) { + if (ppu_thread* ppu = cpu_thread::get_current()) + { + if (u32 addr = ppu->res_notify) + { + ppu->res_notify = 0; + + const usz notify_later_idx = std::basic_string_view{g_to_notify, std::size(g_to_notify)}.find_first_of(std::add_pointer_t{}); + + if (notify_later_idx != umax) + { + g_to_notify[notify_later_idx] = &vm::reservation_notifier(addr); + + if (notify_later_idx < std::size(g_to_notify) - 1) + { + // Null-terminate the list if it ends before last slot + g_to_notify[notify_later_idx + 1] = nullptr; + } + } + else + { + vm::reservation_notifier(addr).notify_all(); + } + } + } + bool result = false; { std::lock_guard lock(g_mutex); @@ -1631,7 +1693,7 @@ bool lv2_obj::awake_unlocked(cpu_thread* cpu, s32 prio) if (is_paused(target->state - cpu_flag::suspend)) { - target->state.notify_one(cpu_flag::suspend); + target->state.notify_one(); } } } @@ -1662,7 +1724,7 @@ void lv2_obj::cleanup() void lv2_obj::schedule_all(u64 current_time) { - usz notify_later_idx = 0; + usz notify_later_idx = std::basic_string_view{g_to_notify, std::size(g_to_notify)}.find_first_of(std::add_pointer_t{}); if (!g_pending && g_scheduler_ready) { @@ -1681,10 +1743,10 @@ void lv2_obj::schedule_all(u64 current_time) continue; } - if (notify_later_idx == std::size(g_to_notify)) + if (notify_later_idx >= std::size(g_to_notify)) { // Out of notification slots, notify locally (resizable container is not worth it) - target->state.notify_one(cpu_flag::signal + cpu_flag::suspend); + target->state.notify_one(); } else { @@ -1715,10 +1777,10 @@ void lv2_obj::schedule_all(u64 current_time) ensure(!target->state.test_and_set(cpu_flag::notify)); // Otherwise notify it to wake itself - if (notify_later_idx == std::size(g_to_notify)) + if (notify_later_idx >= std::size(g_to_notify)) { // Out of notification slots, notify locally (resizable container is not worth it) - target->state.notify_one(cpu_flag::notify); + target->state.notify_one(); } else { @@ -1880,6 +1942,35 @@ void lv2_obj::set_yield_frequency(u64 freq, u64 max_allowed_tsc) g_lv2_preempts_taken.release(0); } +#if defined(_MSC_VER) +#define mwaitx_func +#define waitpkg_func +#else +#define mwaitx_func __attribute__((__target__("mwaitx"))) +#define waitpkg_func __attribute__((__target__("waitpkg"))) +#endif + +#if defined(ARCH_X64) +// Waits for a number of TSC clock cycles in power optimized state +// Cstate is represented in bits [7:4]+1 cstate. So C0 requires bits [7:4] to be set to 0xf, C1 requires bits [7:4] to be set to 0. +mwaitx_func static void __mwaitx(u32 cycles, u32 cstate) +{ + constexpr u32 timer_enable = 0x2; + + // monitorx will wake if the cache line is written to. We don't want this, so place the monitor value on it's own cache line. + alignas(64) u64 monitor_var{}; + _mm_monitorx(&monitor_var, 0, 0); + _mm_mwaitx(timer_enable, cstate, cycles); +} + +// First bit indicates cstate, 0x0 for C.02 state (lower power) or 0x1 for C.01 state (higher power) +waitpkg_func static void __tpause(u32 cycles, u32 cstate) +{ + const u64 tsc = utils::get_tsc() + cycles; + _tpause(cstate, tsc); +} +#endif + bool lv2_obj::wait_timeout(u64 usec, ppu_thread* cpu, bool scale, bool is_usleep) { static_assert(u64{umax} / max_timeout >= 100, "max timeout is not valid for scaling"); @@ -1948,7 +2039,7 @@ bool lv2_obj::wait_timeout(u64 usec, ppu_thread* cpu, bool scale, bool is_usleep u64 remaining = usec - passed; #ifdef __linux__ // NOTE: Assumption that timer initialization has succeeded - u64 host_min_quantum = is_usleep && remaining <= 1000 ? 10 : 50; + constexpr u64 host_min_quantum = 10; #else // Host scheduler quantum for windows (worst case) // NOTE: On ps3 this function has very high accuracy @@ -1965,14 +2056,29 @@ bool lv2_obj::wait_timeout(u64 usec, ppu_thread* cpu, bool scale, bool is_usleep if (remaining > host_min_quantum) { #ifdef __linux__ - // Do not wait for the last quantum to avoid loss of accuracy - wait_for(remaining - ((remaining % host_min_quantum) + host_min_quantum)); + // With timerslack set low, Linux is precise for all values above + wait_for(remaining); #else // Wait on multiple of min quantum for large durations to avoid overloading low thread cpus wait_for(remaining - (remaining % host_min_quantum)); #endif } // TODO: Determine best value for yield delay +#if defined(ARCH_X64) + else if (utils::has_appropriate_um_wait()) + { + u32 us_in_tsc_clocks = remaining * (utils::get_tsc_freq() / 1000000); + + if (utils::has_waitpkg()) + { + __tpause(us_in_tsc_clocks, 0x1); + } + else + { + __mwaitx(us_in_tsc_clocks, 0xf0); + } + } +#endif else { // Try yielding. May cause long wake latency but helps weaker CPUs a lot by alleviating resource pressure diff --git a/rpcs3/Emu/Cell/lv2/sys_fs.cpp b/rpcs3/Emu/Cell/lv2/sys_fs.cpp index bfcf0c09ec..dabbb03268 100644 --- a/rpcs3/Emu/Cell/lv2/sys_fs.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_fs.cpp @@ -2773,6 +2773,62 @@ error_code sys_fs_chmod(ppu_thread&, vm::cptr path, s32 mode) { sys_fs.todo("sys_fs_chmod(path=%s, mode=%#o)", path, mode); + const auto [path_error, vpath] = translate_to_sv(path); + + if (path_error) + { + return {path_error, vpath}; + } + + const std::string local_path = vfs::get(vpath); + + const auto mp = lv2_fs_object::get_mp(vpath); + + if (local_path.empty()) + { + return {CELL_ENOTMOUNTED, path}; + } + + if (mp->flags & lv2_mp_flag::read_only) + { + return {CELL_EROFS, path}; + } + + std::unique_lock lock(mp->mutex); + + fs::stat_t info{}; + + if (!fs::get_stat(local_path, info)) + { + switch (auto error = fs::g_tls_error) + { + case fs::error::noent: + { + // Try to locate split files + + for (u32 i = 66601; i <= 66699; i++) + { + if (!fs::get_stat(fmt::format("%s.%u", local_path, i), info) && !info.is_directory) + { + break; + } + } + + if (fs::get_stat(local_path + ".66600", info) && !info.is_directory) + { + break; + } + + return {CELL_ENOENT, path}; + } + default: + { + sys_fs.error("sys_fs_chmod(): unknown error %s", error); + return {CELL_EIO, path}; + } + } + } + return CELL_OK; } diff --git a/rpcs3/Emu/Cell/lv2/sys_hid.cpp b/rpcs3/Emu/Cell/lv2/sys_hid.cpp index 0f297eaafb..e5b5a310aa 100644 --- a/rpcs3/Emu/Cell/lv2/sys_hid.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_hid.cpp @@ -12,7 +12,7 @@ LOG_CHANNEL(sys_hid); -error_code sys_hid_manager_open(u64 device_type, u64 port_no, vm::ptr handle) +error_code sys_hid_manager_open(ppu_thread& ppu, u64 device_type, u64 port_no, vm::ptr handle) { sys_hid.todo("sys_hid_manager_open(device_type=0x%llx, port_no=0x%llx, handle=*0x%llx)", device_type, port_no, handle); @@ -34,7 +34,7 @@ error_code sys_hid_manager_open(u64 device_type, u64 port_no, vm::ptr handl if (device_type == 1) { - cellPadInit(7); + cellPadInit(ppu, 7); cellPadSetPortSetting(::narrow(port_no) /* 0 */, CELL_PAD_SETTING_LDD | CELL_PAD_SETTING_PRESS_ON | CELL_PAD_SETTING_SENSOR_ON); } diff --git a/rpcs3/Emu/Cell/lv2/sys_hid.h b/rpcs3/Emu/Cell/lv2/sys_hid.h index c1c6754a94..66acb78006 100644 --- a/rpcs3/Emu/Cell/lv2/sys_hid.h +++ b/rpcs3/Emu/Cell/lv2/sys_hid.h @@ -34,7 +34,7 @@ struct sys_hid_manager_514_pkg_d // SysCalls -error_code sys_hid_manager_open(u64 device_type, u64 port_no, vm::ptr handle); +error_code sys_hid_manager_open(ppu_thread& ppu, u64 device_type, u64 port_no, vm::ptr handle); error_code sys_hid_manager_ioctl(u32 hid_handle, u32 pkg_id, vm::ptr buf, u64 buf_size); error_code sys_hid_manager_add_hot_key_observer(u32 event_queue, vm::ptr unk); error_code sys_hid_manager_check_focus(); diff --git a/rpcs3/Emu/Cell/lv2/sys_interrupt.cpp b/rpcs3/Emu/Cell/lv2/sys_interrupt.cpp index f18fb96502..c2e4ebcd9a 100644 --- a/rpcs3/Emu/Cell/lv2/sys_interrupt.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_interrupt.cpp @@ -183,7 +183,7 @@ error_code _sys_interrupt_thread_establish(ppu_thread& ppu, vm::ptr ih, u32 }); it->state -= cpu_flag::stop; - it->state.notify_one(cpu_flag::stop); + it->state.notify_one(); return result; }); diff --git a/rpcs3/Emu/Cell/lv2/sys_memory.cpp b/rpcs3/Emu/Cell/lv2/sys_memory.cpp index 09a935afac..d8b9ff6823 100644 --- a/rpcs3/Emu/Cell/lv2/sys_memory.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_memory.cpp @@ -85,6 +85,12 @@ struct sys_memory_address_table } }; +std::shared_ptr reserve_map(u32 alloc_size, u32 align) +{ + return vm::reserve_map(align == 0x10000 ? vm::user64k : vm::user1m, 0, align == 0x10000 ? 0x20000000 : utils::align(alloc_size, 0x10000000) + , align == 0x10000 ? (vm::page_size_64k | vm::bf0_0x1) : (vm::page_size_1m | vm::bf0_0x1)); +} + // Todo: fix order of error checks error_code sys_memory_allocate(cpu_thread& cpu, u32 size, u64 flags, vm::ptr alloc_addr) @@ -123,7 +129,7 @@ error_code sys_memory_allocate(cpu_thread& cpu, u32 size, u64 flags, vm::ptralloc(size, nullptr, align)) { @@ -131,7 +137,7 @@ error_code sys_memory_allocate(cpu_thread& cpu, u32 size, u64 flags, vm::ptralloc(size)) { diff --git a/rpcs3/Emu/Cell/lv2/sys_mmapper.cpp b/rpcs3/Emu/Cell/lv2/sys_mmapper.cpp index 5ff9b62ccd..87a5d89d86 100644 --- a/rpcs3/Emu/Cell/lv2/sys_mmapper.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_mmapper.cpp @@ -862,7 +862,7 @@ error_code mmapper_thread_recover_page_fault(cpu_thread* cpu) if (cpu->state & cpu_flag::signal) { - cpu->state.notify_one(cpu_flag::signal); + cpu->state.notify_one(); } return CELL_OK; diff --git a/rpcs3/Emu/Cell/lv2/sys_net.cpp b/rpcs3/Emu/Cell/lv2/sys_net.cpp index 387cb3f623..026ab67559 100644 --- a/rpcs3/Emu/Cell/lv2/sys_net.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_net.cpp @@ -257,7 +257,7 @@ lv2_socket::lv2_socket(utils::serial& ar, lv2_socket_type _type) ar.pos += 8; #endif - const s32 version = GET_SERIALIZATION_VERSION(lv2_net); + [[maybe_unused]] const s32 version = GET_SERIALIZATION_VERSION(lv2_net); ar(so_rcvtimeo, so_sendtimeo); diff --git a/rpcs3/Emu/Cell/lv2/sys_overlay.cpp b/rpcs3/Emu/Cell/lv2/sys_overlay.cpp index 2cfb7b2b55..2c53b39cbe 100644 --- a/rpcs3/Emu/Cell/lv2/sys_overlay.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_overlay.cpp @@ -36,7 +36,7 @@ static error_code overlay_load_module(vm::ptr ovlmid, const std::string& vp u128 klic = g_fxo->get().last_key(); - ppu_exec_object obj = decrypt_self(std::move(src), reinterpret_cast(&klic)); + ppu_exec_object obj = decrypt_self(std::move(src), reinterpret_cast(&klic), nullptr, true); if (obj != elf_error::ok) { diff --git a/rpcs3/Emu/Cell/lv2/sys_ppu_thread.cpp b/rpcs3/Emu/Cell/lv2/sys_ppu_thread.cpp index 1440a32883..e933549c65 100644 --- a/rpcs3/Emu/Cell/lv2/sys_ppu_thread.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_ppu_thread.cpp @@ -591,32 +591,6 @@ error_code sys_ppu_thread_start(ppu_thread& ppu, u32 thread_id) { thread->cmd_notify++; thread->cmd_notify.notify_one(); - - // Dirty hack for sound: confirm the creation of _mxr000 event queue - if (*thread->ppu_tname.load() == "_cellsurMixerMain"sv) - { - ppu.check_state(); - lv2_obj::sleep(ppu); - - while (!idm::select([](u32, lv2_event_queue& eq) - { - //some games do not set event queue name, though key seems constant for them - return (eq.name == "_mxr000\0"_u64) || (eq.key == 0x8000cafe02460300); - })) - { - if (ppu.is_stopped()) - { - return {}; - } - - thread_ctrl::wait_for(50000); - } - - if (ppu.test_stopped()) - { - return 0; - } - } } return CELL_OK; diff --git a/rpcs3/Emu/Cell/lv2/sys_prx.cpp b/rpcs3/Emu/Cell/lv2/sys_prx.cpp index 4cde5d52e9..4a9b7d4ee5 100644 --- a/rpcs3/Emu/Cell/lv2/sys_prx.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_prx.cpp @@ -263,11 +263,11 @@ static error_code prx_load_module(const std::string& vpath, u64 flags, vm::ptrget().last_key(); - ppu_prx_object obj = decrypt_self(std::move(src), reinterpret_cast(&klic)); + ppu_prx_object obj = decrypt_self(std::move(src), reinterpret_cast(&klic), nullptr, true); if (obj != elf_error::ok) { - return CELL_PRX_ERROR_ILLEGAL_LIBRARY; + return CELL_PRX_ERROR_UNSUPPORTED_PRX_TYPE; } const auto prx = ppu_load_prx(obj, false, path, file_offset); diff --git a/rpcs3/Emu/Cell/lv2/sys_rsx.cpp b/rpcs3/Emu/Cell/lv2/sys_rsx.cpp index 1785043d6f..7479da7f99 100644 --- a/rpcs3/Emu/Cell/lv2/sys_rsx.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_rsx.cpp @@ -177,6 +177,12 @@ error_code sys_rsx_memory_allocate(cpu_thread& cpu, vm::ptr mem_handle, vm: if (vm::falloc(rsx::constants::local_mem_base, size, vm::video)) { rsx::get_current_renderer()->local_mem_size = size; + + if (u32 addr = rsx::get_current_renderer()->driver_info) + { + vm::_ref(addr).memory_size = size; + } + *mem_addr = rsx::constants::local_mem_base; *mem_handle = 0x5a5a5a5b; return CELL_OK; @@ -285,6 +291,7 @@ error_code sys_rsx_context_allocate(cpu_thread& cpu, vm::ptr context_id, vm driverInfo.version_driver = 0x211; driverInfo.version_gpu = 0x5c; + driverInfo.memory_size = render->local_mem_size; driverInfo.nvcore_frequency = 500000000; // 0x1DCD6500 driverInfo.memory_frequency = 650000000; // 0x26BE3680 driverInfo.reportsNotifyOffset = 0x1000; diff --git a/rpcs3/Emu/Cell/lv2/sys_spu.cpp b/rpcs3/Emu/Cell/lv2/sys_spu.cpp index 7ccbc6acb1..d69d20c2b7 100644 --- a/rpcs3/Emu/Cell/lv2/sys_spu.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_spu.cpp @@ -1042,7 +1042,7 @@ error_code sys_spu_thread_group_start(ppu_thread& ppu, u32 id) { for (; index != umax; index--) { - threads[index]->state.notify_one(cpu_flag::stop); + threads[index]->state.notify_one(); } } } notify_threads; @@ -1216,7 +1216,7 @@ error_code sys_spu_thread_group_resume(ppu_thread& ppu, u32 id) { for (; index != umax; index--) { - threads[index]->state.notify_one(cpu_flag::suspend); + threads[index]->state.notify_one(); } } } notify_threads; @@ -1397,7 +1397,7 @@ error_code sys_spu_thread_group_terminate(ppu_thread& ppu, u32 id, s32 value) if (prev_resv && prev_resv != resv) { // Batch reservation notifications if possible - vm::reservation_notifier(prev_resv).notify_all(-128); + vm::reservation_notifier(prev_resv).notify_all(); } prev_resv = resv; @@ -1407,7 +1407,7 @@ error_code sys_spu_thread_group_terminate(ppu_thread& ppu, u32 id, s32 value) if (prev_resv) { - vm::reservation_notifier(prev_resv).notify_all(-128); + vm::reservation_notifier(prev_resv).notify_all(); } group->exit_status = value; diff --git a/rpcs3/Emu/Cell/lv2/sys_sync.h b/rpcs3/Emu/Cell/lv2/sys_sync.h index 98df7cd729..bd09d26a61 100644 --- a/rpcs3/Emu/Cell/lv2/sys_sync.h +++ b/rpcs3/Emu/Cell/lv2/sys_sync.h @@ -60,6 +60,11 @@ enum enum ppu_thread_status : u32; +namespace vm +{ + extern u8 g_reservations[65536 / 128 * 64]; +} + // Base class for some kernel objects (shared set of 8192 objects). struct lv2_obj { @@ -432,9 +437,15 @@ public: if (cpu != &g_to_notify) { - // Note: by the time of notification the thread could have been deallocated which is why the direct function is used - // TODO: Pass a narrower mask - atomic_wait_engine::notify_one(cpu, 4, atomic_wait::default_mask>); + if (cpu >= vm::g_reservations && cpu <= vm::g_reservations + (std::size(vm::g_reservations) - 1)) + { + 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); + } } } @@ -465,7 +476,8 @@ public: // While IDM mutex is still locked (this function assumes so) check if the notification is still needed // Pending flag is meant for forced notification (if the CPU really has pending work it can restore the flag in theory) - if (cpu != &g_to_notify && static_cast(cpu)->none_of(cpu_flag::signal + cpu_flag::pending)) + // Disabled to allow reservation notifications from here + if (false && cpu != &g_to_notify && static_cast(cpu)->none_of(cpu_flag::signal + cpu_flag::pending)) { // Omit it (this is a void pointer, it can hold anything) cpu = &g_to_notify; diff --git a/rpcs3/Emu/Io/PadHandler.cpp b/rpcs3/Emu/Io/PadHandler.cpp index 4f98110a87..665bee4b63 100644 --- a/rpcs3/Emu/Io/PadHandler.cpp +++ b/rpcs3/Emu/Io/PadHandler.cpp @@ -7,6 +7,8 @@ cfg_input g_cfg_input; +extern void pad_state_notify_state_change(usz index, u32 state); + PadHandlerBase::PadHandlerBase(pad_handler type) : m_type(type) { } @@ -53,15 +55,14 @@ u16 PadHandlerBase::NormalizeTriggerInput(u16 value, int threshold) const { return static_cast(0); } - else if (threshold <= trigger_min) + + if (threshold <= trigger_min) { return static_cast(ScaledInput(value, trigger_min, trigger_max)); } - else - { - const s32 val = static_cast(static_cast(trigger_max) * (value - threshold) / (trigger_max - threshold)); - return static_cast(ScaledInput(val, trigger_min, trigger_max)); - } + + const s32 val = static_cast(static_cast(trigger_max) * (value - threshold) / (trigger_max - threshold)); + return static_cast(ScaledInput(val, trigger_min, trigger_max)); } // normalizes a directed input, meaning it will correspond to a single "button" and not an axis with two directions @@ -79,11 +80,9 @@ u16 PadHandlerBase::NormalizeDirectedInput(s32 raw_value, s32 threshold, s32 max { return static_cast(255.0f * val); } - else - { - const f32 thresh = static_cast(threshold) / maximum; // threshold converted to [0, 1] - return static_cast(255.0f * std::min(1.0f, (val - thresh) / (1.0f - thresh))); - } + + const f32 thresh = static_cast(threshold) / maximum; // threshold converted to [0, 1] + return static_cast(255.0f * std::clamp((val - thresh) / (1.0f - thresh), 0.0f, 1.0f)); } u16 PadHandlerBase::NormalizeStickInput(u16 raw_value, int threshold, int multiplier, bool ignore_threshold) const @@ -94,10 +93,8 @@ u16 PadHandlerBase::NormalizeStickInput(u16 raw_value, int threshold, int multip { return static_cast(ScaledInput(scaled_value, 0, thumb_max)); } - else - { - return NormalizeDirectedInput(scaled_value, threshold, thumb_max); - } + + return NormalizeDirectedInput(scaled_value, threshold, thumb_max); } // This function normalizes stick deadzone based on the DS3's deadzone, which is ~13% @@ -581,7 +578,8 @@ void PadHandlerBase::get_mapping(const pad_ensemble& binding) // Find out if special buttons are pressed (introduced by RPCS3). // These buttons will have a delay of one cycle, but whatever. - const bool adjust_pressure = pad->get_pressure_intensity_enabled(cfg->pressure_intensity_toggle_mode.get()); + const bool adjust_pressure = pad->get_pressure_intensity_button_active(cfg->pressure_intensity_toggle_mode.get()); + const u32 pressure_intensity_deadzone = cfg->pressure_intensity_deadzone.get(); // Translate any corresponding keycodes to our normal DS3 buttons and triggers for (Button& button : pad->m_buttons) @@ -603,9 +601,17 @@ void PadHandlerBase::get_mapping(const pad_ensemble& binding) { val = pad->m_pressure_intensity; } + else if (pressure_intensity_deadzone > 0) + { + // Ignore triggers, since they have their own deadzones + if (!get_is_left_trigger(device, code) && !get_is_right_trigger(device, code)) + { + val = NormalizeDirectedInput(val, pressure_intensity_deadzone, 255); + } + } value = std::max(value, val); - pressed = true; + pressed = value > 0; } } @@ -617,7 +623,7 @@ void PadHandlerBase::get_mapping(const pad_ensemble& binding) s32 stick_val[4]{}; // Translate any corresponding keycodes to our two sticks. (ignoring thresholds for now) - for (int i = 0; i < static_cast(pad->m_sticks.size()); i++) + for (usz i = 0; i < pad->m_sticks.size(); i++) { bool pressed{}; u16 val_min{}; @@ -701,8 +707,10 @@ void PadHandlerBase::process() if (!last_connection_status[i]) { input_log.success("%s device %d connected", m_type, i); - pad->m_port_status |= CELL_PAD_STATUS_CONNECTED; - pad->m_port_status |= CELL_PAD_STATUS_ASSIGN_CHANGES; + + pad->m_port_status |= CELL_PAD_STATUS_CONNECTED + CELL_PAD_STATUS_ASSIGN_CHANGES; + pad_state_notify_state_change(i, CELL_PAD_STATUS_CONNECTED); + last_connection_status[i] = true; connected_devices++; } @@ -723,8 +731,10 @@ void PadHandlerBase::process() if (!last_connection_status[i]) { input_log.success("%s device %d connected by force", m_type, i); - pad->m_port_status |= CELL_PAD_STATUS_CONNECTED; - pad->m_port_status |= CELL_PAD_STATUS_ASSIGN_CHANGES; + + pad->m_port_status |= CELL_PAD_STATUS_CONNECTED + CELL_PAD_STATUS_ASSIGN_CHANGES; + pad_state_notify_state_change(i, CELL_PAD_STATUS_CONNECTED); + last_connection_status[i] = true; connected_devices++; } @@ -734,8 +744,12 @@ void PadHandlerBase::process() if (last_connection_status[i]) { input_log.error("%s device %d disconnected", m_type, i); + pad->m_port_status &= ~CELL_PAD_STATUS_CONNECTED; pad->m_port_status |= CELL_PAD_STATUS_ASSIGN_CHANGES; + + pad_state_notify_state_change(i, CELL_PAD_STATUS_DISCONNECTED); + last_connection_status[i] = false; connected_devices--; } diff --git a/rpcs3/Emu/Io/RB3MidiGuitar.h b/rpcs3/Emu/Io/RB3MidiGuitar.h index 3dbc712541..3a93f95d61 100644 --- a/rpcs3/Emu/Io/RB3MidiGuitar.h +++ b/rpcs3/Emu/Io/RB3MidiGuitar.h @@ -7,7 +7,7 @@ class usb_device_rb3_midi_guitar : public usb_device_emulated { private: - u32 response_pos = 0; + usz response_pos = 0; bool buttons_enabled = false; RtMidiInPtr midi_in{}; diff --git a/rpcs3/Emu/Io/RB3MidiKeyboard.cpp b/rpcs3/Emu/Io/RB3MidiKeyboard.cpp index 4d515cc090..dd53eef0ec 100644 --- a/rpcs3/Emu/Io/RB3MidiKeyboard.cpp +++ b/rpcs3/Emu/Io/RB3MidiKeyboard.cpp @@ -199,41 +199,43 @@ void usb_device_rb3_midi_keyboard::parse_midi_message(u8* msg, usz size) // handle note on/off messages if (size == 3 && (msg[0] == 0x80 || msg[0] == 0x90)) { + const bool note_on = (0x10 & msg[0]) == 0x10 && msg[2] != 0; + // handle navigation buttons switch (msg[1]) { case 44: // G#2 - button_state.cross = ((0x10 & msg[0]) == 0x10); + button_state.cross = note_on; break; case 42: // F#2 - button_state.circle = ((0x10 & msg[0]) == 0x10); + button_state.circle = note_on; break; case 39: // D#2 - button_state.square = ((0x10 & msg[0]) == 0x10); + button_state.square = note_on; break; case 37: // C#2 - button_state.triangle = ((0x10 & msg[0]) == 0x10); + button_state.triangle = note_on; break; case 46: // A#2 - button_state.start = ((0x10 & msg[0]) == 0x10); + button_state.start = note_on; break; case 36: // C2 - button_state.select = ((0x10 & msg[0]) == 0x10); + button_state.select = note_on; break; case 45: // A2 - button_state.overdrive = ((0x10 & msg[0]) == 0x10); + button_state.overdrive = note_on; break; case 41: // F2 - button_state.dpad_up = ((0x10 & msg[0]) == 0x10); + button_state.dpad_up = note_on; break; case 43: // G2 - button_state.dpad_down = ((0x10 & msg[0]) == 0x10); + button_state.dpad_down = note_on; break; case 38: // D2 - button_state.dpad_left = ((0x10 & msg[0]) == 0x10); + button_state.dpad_left = note_on; break; case 40: // E2 - button_state.dpad_right = ((0x10 & msg[0]) == 0x10); + button_state.dpad_right = note_on; break; default: break; @@ -243,7 +245,7 @@ void usb_device_rb3_midi_keyboard::parse_midi_message(u8* msg, usz size) if (msg[1] >= 48 && msg[1] <= (48 + button_state.keys.size())) { const u32 key = msg[1] - 48; - button_state.keys[key] = ((0x10 & msg[0]) == 0x10); + button_state.keys[key] = note_on; button_state.velocities[key] = msg[2]; } } diff --git a/rpcs3/Emu/Io/RB3MidiKeyboard.h b/rpcs3/Emu/Io/RB3MidiKeyboard.h index 13fb63f5a1..c5042af239 100644 --- a/rpcs3/Emu/Io/RB3MidiKeyboard.h +++ b/rpcs3/Emu/Io/RB3MidiKeyboard.h @@ -7,7 +7,7 @@ class usb_device_rb3_midi_keyboard : public usb_device_emulated { private: - u32 response_pos = 0; + usz response_pos = 0; bool buttons_enabled = false; RtMidiInPtr midi_in{}; diff --git a/rpcs3/Emu/Io/camera_config.cpp b/rpcs3/Emu/Io/camera_config.cpp index bcc19c5786..dce03e8cd7 100644 --- a/rpcs3/Emu/Io/camera_config.cpp +++ b/rpcs3/Emu/Io/camera_config.cpp @@ -67,7 +67,7 @@ void cfg_camera::set_camera_setting(const std::string& camera, const camera_sett std::string cfg_camera::camera_setting::to_string() const { - return fmt::format("%d,%d,%f,%f,%d,%d,%d", width, height, min_fps, max_fps, format, pixel_aspect_width, pixel_aspect_height); + return fmt::format("%d,%d,%f,%f,%d", width, height, min_fps, max_fps, format); } void cfg_camera::camera_setting::from_string(const std::string& text) @@ -112,16 +112,12 @@ void cfg_camera::camera_setting::from_string(const std::string& text) !to_integer(::at32(list, 1), height) || !to_double(::at32(list, 2), min_fps) || !to_double(::at32(list, 3), max_fps) || - !to_integer(::at32(list, 4), format) || - !to_integer(::at32(list, 5), pixel_aspect_width) || - !to_integer(::at32(list, 6), pixel_aspect_height)) + !to_integer(::at32(list, 4), format)) { width = 0; height = 0; min_fps = 0; max_fps = 0; format = 0; - pixel_aspect_width = 0; - pixel_aspect_height = 0; } } diff --git a/rpcs3/Emu/Io/camera_config.h b/rpcs3/Emu/Io/camera_config.h index ca737f120e..07836a064d 100644 --- a/rpcs3/Emu/Io/camera_config.h +++ b/rpcs3/Emu/Io/camera_config.h @@ -15,8 +15,6 @@ struct cfg_camera final : cfg::node double min_fps = 0; double max_fps = 0; int format = 0; - int pixel_aspect_width = 0; - int pixel_aspect_height = 0; static const u32 member_count = 7; diff --git a/rpcs3/Emu/Io/camera_handler_base.h b/rpcs3/Emu/Io/camera_handler_base.h index adcd2653f1..1c0eb5588b 100644 --- a/rpcs3/Emu/Io/camera_handler_base.h +++ b/rpcs3/Emu/Io/camera_handler_base.h @@ -8,7 +8,6 @@ class camera_handler_base public: enum class camera_handler_state { - not_available, closed, open, running @@ -33,7 +32,7 @@ public: protected: std::mutex m_mutex; - atomic_t m_state = camera_handler_state::not_available; + atomic_t m_state = camera_handler_state::closed; bool m_mirrored = false; s32 m_format = 2; // CELL_CAMERA_RAW8 u32 m_bytesize = 0; diff --git a/rpcs3/Emu/Io/ghltar_config.h b/rpcs3/Emu/Io/ghltar_config.h index 8ed23ead1d..524542fa9f 100644 --- a/rpcs3/Emu/Io/ghltar_config.h +++ b/rpcs3/Emu/Io/ghltar_config.h @@ -43,7 +43,7 @@ struct cfg_ghltar final : public emulated_pad_config cfg_pad_btn dpad_left{ this, "D-Pad Left", ghltar_btn::dpad_left, pad_button::dpad_left }; cfg_pad_btn dpad_right{ this, "D-Pad Right", ghltar_btn::dpad_right, pad_button::dpad_right }; cfg_pad_btn whammy{ this, "Whammy", ghltar_btn::whammy, pad_button::rs_y }; - cfg_pad_btn tilt{ this, "tilt", ghltar_btn::whammy, pad_button::rs_x }; + cfg_pad_btn tilt{ this, "tilt", ghltar_btn::tilt, pad_button::rs_x }; }; struct cfg_ghltars final : public emulated_pads_config diff --git a/rpcs3/Emu/Io/pad_config.cpp b/rpcs3/Emu/Io/pad_config.cpp index 3e42ed684b..4fd5ec080d 100644 --- a/rpcs3/Emu/Io/pad_config.cpp +++ b/rpcs3/Emu/Io/pad_config.cpp @@ -4,7 +4,7 @@ LOG_CHANNEL(input_log, "Input"); -extern std::string g_pad_profile_override; +extern std::string g_input_config_override; std::vector cfg_pad::get_buttons(const std::string& str) { @@ -26,16 +26,16 @@ std::string cfg_pad::get_buttons(std::vector vec) return fmt::merge(vec, ","); } -bool cfg_input::load(const std::string& title_id, const std::string& profile, bool strict) +bool cfg_input::load(const std::string& title_id, const std::string& config_file, bool strict) { - input_log.notice("Loading pad config (title_id='%s', profile='%s', strict=%d)", title_id, profile, strict); + input_log.notice("Loading pad config (title_id='%s', config_file='%s', strict=%d)", title_id, config_file, strict); std::string cfg_name; - // Check profile override first - if (!strict && !g_pad_profile_override.empty()) + // Check configuration override first + if (!strict && !g_input_config_override.empty()) { - cfg_name = rpcs3::utils::get_input_config_dir() + g_pad_profile_override + ".yml"; + cfg_name = rpcs3::utils::get_input_config_dir() + g_input_config_override + ".yml"; } // Check custom config next @@ -44,23 +44,23 @@ bool cfg_input::load(const std::string& title_id, const std::string& profile, bo cfg_name = rpcs3::utils::get_custom_input_config_path(title_id); } - // Check active global profile next - if ((title_id.empty() || !strict) && !profile.empty() && !fs::is_file(cfg_name)) + // Check active global configuration next + if ((title_id.empty() || !strict) && !config_file.empty() && !fs::is_file(cfg_name)) { - cfg_name = rpcs3::utils::get_input_config_dir() + profile + ".yml"; + cfg_name = rpcs3::utils::get_input_config_dir() + config_file + ".yml"; } - // Fallback to default profile + // Fallback to default configuration if (!strict && !fs::is_file(cfg_name)) { - cfg_name = rpcs3::utils::get_input_config_dir() + g_cfg_profile.default_profile + ".yml"; + cfg_name = rpcs3::utils::get_input_config_dir() + g_cfg_input_configs.default_config + ".yml"; } from_default(); if (fs::file cfg_file{ cfg_name, fs::read }) { - input_log.notice("Loading pad profile: '%s'", cfg_name); + input_log.notice("Loading input configuration: '%s'", cfg_name); if (std::string content = cfg_file.to_string(); !content.empty()) { @@ -69,7 +69,7 @@ bool cfg_input::load(const std::string& title_id, const std::string& profile, bo } // Add keyboard by default - input_log.notice("Pad profile empty. Adding default keyboard pad handler"); + input_log.notice("Input configuration empty. Adding default keyboard pad handler"); player[0]->handler.from_string(fmt::format("%s", pad_handler::keyboard)); player[0]->device.from_string(pad::keyboard_device_name.data()); player[0]->buddy_device.from_string(""sv); @@ -77,14 +77,14 @@ bool cfg_input::load(const std::string& title_id, const std::string& profile, bo return false; } -void cfg_input::save(const std::string& title_id, const std::string& profile) const +void cfg_input::save(const std::string& title_id, const std::string& config_file) const { std::string cfg_name; if (title_id.empty()) { - cfg_name = rpcs3::utils::get_input_config_dir() + profile + ".yml"; - input_log.notice("Saving pad config profile '%s' to '%s'", profile, cfg_name); + cfg_name = rpcs3::utils::get_input_config_dir() + config_file + ".yml"; + input_log.notice("Saving input configuration '%s' to '%s'", config_file, cfg_name); } else { @@ -105,12 +105,12 @@ void cfg_input::save(const std::string& title_id, const std::string& profile) co } } -cfg_profile::cfg_profile() - : path(rpcs3::utils::get_input_config_root() + "/active_profiles.yml") +cfg_input_configurations::cfg_input_configurations() + : path(rpcs3::utils::get_input_config_root() + "/active_input_configurations.yml") { } -bool cfg_profile::load() +bool cfg_input_configurations::load() { if (fs::file cfg_file{ path, fs::read }) { @@ -121,14 +121,14 @@ bool cfg_profile::load() return false; } -void cfg_profile::save() const +void cfg_input_configurations::save() const { - input_log.notice("Saving pad profile config to '%s'", path); + input_log.notice("Saving input configurations config to '%s'", path); fs::pending_file cfg_file(path); if (!cfg_file.file || (cfg_file.file.write(to_string()), !cfg_file.commit())) { - input_log.error("Failed to save pad profile config to '%s' (error=%s)", path, fs::g_tls_error); + input_log.error("Failed to save input configurations config to '%s' (error=%s)", path, fs::g_tls_error); } } diff --git a/rpcs3/Emu/Io/pad_config.h b/rpcs3/Emu/Io/pad_config.h index bf9bcf7e91..b2d34bf7c2 100644 --- a/rpcs3/Emu/Io/pad_config.h +++ b/rpcs3/Emu/Io/pad_config.h @@ -1,6 +1,6 @@ #pragma once -#include "pad_config_types.h" +#include "pad_types.h" #include "Utilities/Config.h" @@ -62,6 +62,7 @@ struct cfg_pad final : cfg::node cfg::string pressure_intensity_button{ this, "Pressure Intensity Button", "" }; cfg::uint<0, 100> pressure_intensity{ this, "Pressure Intensity Percent", 50 }; cfg::_bool pressure_intensity_toggle_mode{ this, "Pressure Intensity Toggle Mode", false }; + cfg::uint<0, 255> pressure_intensity_deadzone{ this, "Pressure Intensity Deadzone", 0 }; cfg::uint<0, 200> lstickmultiplier{ this, "Left Stick Multiplier", 100 }; cfg::uint<0, 200> rstickmultiplier{ this, "Right Stick Multiplier", 100 }; @@ -96,7 +97,7 @@ struct cfg_pad final : cfg::node cfg::uint<0, 100> analog_lerp_factor{ this, "Analog Button Lerp Factor", 100 }; cfg::uint<0, 100> trigger_lerp_factor{ this, "Trigger Lerp Factor", 100 }; - cfg::uint<0, 5> device_class_type{ this, "Device Class Type", 0 }; + cfg::uint device_class_type{ this, "Device Class Type", 0 }; cfg::uint<0, 65535> vendor_id{ this, "Vendor ID", 0 }; cfg::uint<0, 65535> product_id{ this, "Product ID", 0 }; }; @@ -130,18 +131,18 @@ struct cfg_input final : cfg::node void save(const std::string& title_id, const std::string& profile = "") const; }; -struct cfg_profile final : cfg::node +struct cfg_input_configurations final : cfg::node { - cfg_profile(); + cfg_input_configurations(); bool load(); void save() const; const std::string path; const std::string global_key = "global"; - const std::string default_profile = "Default"; + const std::string default_config = "Default"; - cfg::map_entry active_profiles{ this, "Active Profiles" }; + cfg::map_entry active_configs{ this, "Active Configurations" }; }; extern cfg_input g_cfg_input; -extern cfg_profile g_cfg_profile; +extern cfg_input_configurations g_cfg_input_configs; diff --git a/rpcs3/Emu/Io/pad_types.cpp b/rpcs3/Emu/Io/pad_types.cpp index b62fee2f57..43ab5ffcb7 100644 --- a/rpcs3/Emu/Io/pad_types.cpp +++ b/rpcs3/Emu/Io/pad_types.cpp @@ -132,7 +132,7 @@ u32 get_axis_keycode(u32 offset, u16 value) } } -bool Pad::get_pressure_intensity_enabled(bool is_toggle_mode) +bool Pad::get_pressure_intensity_button_active(bool is_toggle_mode) { if (m_pressure_intensity_button_index < 0) { diff --git a/rpcs3/Emu/Io/pad_types.h b/rpcs3/Emu/Io/pad_types.h index 7d1f91d994..abc7dac077 100644 --- a/rpcs3/Emu/Io/pad_types.h +++ b/rpcs3/Emu/Io/pad_types.h @@ -124,6 +124,92 @@ enum DeviceType CELL_PAD_DEV_TYPE_LDD = 5, }; +// Controller types +enum +{ + CELL_PAD_PCLASS_TYPE_STANDARD = 0x00, + CELL_PAD_PCLASS_TYPE_GUITAR = 0x01, + CELL_PAD_PCLASS_TYPE_DRUM = 0x02, + CELL_PAD_PCLASS_TYPE_DJ = 0x03, + CELL_PAD_PCLASS_TYPE_DANCEMAT = 0x04, + CELL_PAD_PCLASS_TYPE_NAVIGATION = 0x05, +}; + +// Profile of a Standard Type Controller +// Profile of a Navigation Type Controller +// Bits 0 – 31 All 0s + +// Profile of a Guitar Type Controller +enum +{ + // Basic + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_1 = 0x00000001, + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_2 = 0x00000002, + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_3 = 0x00000004, + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_4 = 0x00000008, + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_5 = 0x00000010, + CELL_PAD_PCLASS_PROFILE_GUITAR_STRUM_UP = 0x00000020, + CELL_PAD_PCLASS_PROFILE_GUITAR_STRUM_DOWN = 0x00000040, + CELL_PAD_PCLASS_PROFILE_GUITAR_WHAMMYBAR = 0x00000080, + // All Basic = 0x000000FF + + // Optional + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_H1 = 0x00000100, + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_H2 = 0x00000200, + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_H3 = 0x00000400, + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_H4 = 0x00000800, + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_H5 = 0x00001000, + CELL_PAD_PCLASS_PROFILE_GUITAR_5WAY_EFFECT = 0x00002000, + CELL_PAD_PCLASS_PROFILE_GUITAR_TILT_SENS = 0x00004000, + // All = 0x00007FFF +}; + +// Profile of a Drum Type Controller +enum +{ + CELL_PAD_PCLASS_PROFILE_DRUM_SNARE = 0x00000001, + CELL_PAD_PCLASS_PROFILE_DRUM_TOM = 0x00000002, + CELL_PAD_PCLASS_PROFILE_DRUM_TOM2 = 0x00000004, + CELL_PAD_PCLASS_PROFILE_DRUM_TOM_FLOOR = 0x00000008, + CELL_PAD_PCLASS_PROFILE_DRUM_KICK = 0x00000010, + CELL_PAD_PCLASS_PROFILE_DRUM_CYM_HiHAT = 0x00000020, + CELL_PAD_PCLASS_PROFILE_DRUM_CYM_CRASH = 0x00000040, + CELL_PAD_PCLASS_PROFILE_DRUM_CYM_RIDE = 0x00000080, + CELL_PAD_PCLASS_PROFILE_DRUM_KICK2 = 0x00000100, + // All = 0x000001FF +}; + +// Profile of a DJ Deck Type Controller +enum +{ + CELL_PAD_PCLASS_PROFILE_DJ_MIXER_ATTACK = 0x00000001, + CELL_PAD_PCLASS_PROFILE_DJ_MIXER_CROSSFADER = 0x00000002, + CELL_PAD_PCLASS_PROFILE_DJ_MIXER_DSP_DIAL = 0x00000004, + CELL_PAD_PCLASS_PROFILE_DJ_DECK1_STREAM1 = 0x00000008, + CELL_PAD_PCLASS_PROFILE_DJ_DECK1_STREAM2 = 0x00000010, + CELL_PAD_PCLASS_PROFILE_DJ_DECK1_STREAM3 = 0x00000020, + CELL_PAD_PCLASS_PROFILE_DJ_DECK1_PLATTER = 0x00000040, + CELL_PAD_PCLASS_PROFILE_DJ_DECK2_STREAM1 = 0x00000080, + CELL_PAD_PCLASS_PROFILE_DJ_DECK2_STREAM2 = 0x00000100, + CELL_PAD_PCLASS_PROFILE_DJ_DECK2_STREAM3 = 0x00000200, + CELL_PAD_PCLASS_PROFILE_DJ_DECK2_PLATTER = 0x00000400, + // All = 0x000007FF +}; + +// Profile of a Dance Mat Type Controller +enum +{ + CELL_PAD_PCLASS_PROFILE_DANCEMAT_CIRCLE = 0x00000001, + CELL_PAD_PCLASS_PROFILE_DANCEMAT_CROSS = 0x00000002, + CELL_PAD_PCLASS_PROFILE_DANCEMAT_TRIANGLE = 0x00000004, + CELL_PAD_PCLASS_PROFILE_DANCEMAT_SQUARE = 0x00000008, + CELL_PAD_PCLASS_PROFILE_DANCEMAT_RIGHT = 0x00000010, + CELL_PAD_PCLASS_PROFILE_DANCEMAT_LEFT = 0x00000020, + CELL_PAD_PCLASS_PROFILE_DANCEMAT_UP = 0x00000040, + CELL_PAD_PCLASS_PROFILE_DANCEMAT_DOWN = 0x00000080, + // All = 0x000000FF +}; + enum ButtonDataOffset { CELL_PAD_BTN_OFFSET_DIGITAL1 = 2, @@ -344,7 +430,7 @@ struct Pad bool m_pressure_intensity_toggled{}; // Whether the sensitivity is toggled on or off. u8 m_pressure_intensity{127}; // 0-255 bool m_adjust_pressure_last{}; // only used in keyboard_pad_handler - bool get_pressure_intensity_enabled(bool is_toggle_mode); + bool get_pressure_intensity_button_active(bool is_toggle_mode); // Cable State: 0 - 1 plugged in ? u8 m_cable_state{0}; diff --git a/rpcs3/Emu/Memory/vm.cpp b/rpcs3/Emu/Memory/vm.cpp index 0c2c719957..79680c5c41 100644 --- a/rpcs3/Emu/Memory/vm.cpp +++ b/rpcs3/Emu/Memory/vm.cpp @@ -174,12 +174,11 @@ namespace vm range_lock->store(to_store); } - for (u64 i = 0, to_clear = umax;; i++) + for (u64 i = 0;; i++) { const u64 is_share = g_shmem[begin >> 16].load(); - to_clear &= get_range_lock_bits(true); - const u64 busy = for_all_range_locks(to_clear, [&](u64 addr_exec, u32 size_exec) + const u64 busy = for_all_range_locks(get_range_lock_bits(true), [&](u64 addr_exec, u32 size_exec) { u64 addr = begin; @@ -391,14 +390,19 @@ namespace vm return; } + if (!get_range_lock_bits(true)) [[likely]] + { + return; + } + if (i < 100) busy_wait(200); else std::this_thread::yield(); - if (!get_range_lock_bits(true)) [[likely]] + if (cpu_flag::wait - cpu.state) { - return; + cpu.state += cpu_flag::wait; } } } @@ -1799,12 +1803,12 @@ namespace vm { const u32 max = (0xC0000000 - size) & (0 - align); - if (size > 0xC0000000 - 0x20000000 || max < 0x20000000) + if (size > 0xC0000000 - 0x10000000 || max < 0x10000000) { return nullptr; } - for (u32 addr = utils::align(0x20000000, align);; addr += align) + for (u32 addr = utils::align(0x10000000, align);; addr += align) { if (_test_map(addr, size)) { @@ -2127,8 +2131,8 @@ namespace vm g_locations = { - std::make_shared(0x00010000, 0x1FFF0000, page_size_64k | preallocated), // main - std::make_shared(0x20000000, 0x10000000, page_size_64k | bf0_0x1), // user 64k pages + std::make_shared(0x00010000, 0x0FFF0000, page_size_64k | preallocated), // main + nullptr, // user 64k pages nullptr, // user 1m pages nullptr, // rsx context std::make_shared(0xC0000000, 0x10000000, page_size_64k | preallocated), // video diff --git a/rpcs3/Emu/Memory/vm.h b/rpcs3/Emu/Memory/vm.h index 34e3bbe032..f008c9aee9 100644 --- a/rpcs3/Emu/Memory/vm.h +++ b/rpcs3/Emu/Memory/vm.h @@ -22,7 +22,7 @@ namespace vm extern u8* const g_exec_addr; extern u8* const g_stat_addr; extern u8* const g_free_addr; - extern u8 g_reservations[]; + extern u8 g_reservations[65536 / 128 * 64]; struct writer_lock; diff --git a/rpcs3/Emu/Memory/vm_reservation.h b/rpcs3/Emu/Memory/vm_reservation.h index ec6af4f7ae..6330a08a4d 100644 --- a/rpcs3/Emu/Memory/vm_reservation.h +++ b/rpcs3/Emu/Memory/vm_reservation.h @@ -135,7 +135,7 @@ namespace vm _xend(); #endif if constexpr (Ack) - res.notify_all(-128); + res.notify_all(); return; } else @@ -149,7 +149,7 @@ namespace vm _xend(); #endif if constexpr (Ack) - res.notify_all(-128); + res.notify_all(); return result; } else @@ -204,7 +204,7 @@ namespace vm #endif res += 127; if (Ack) - res.notify_all(-128); + res.notify_all(); return; } else @@ -218,7 +218,7 @@ namespace vm #endif res += 127; if (Ack) - res.notify_all(-128); + res.notify_all(); return result; } else @@ -253,7 +253,7 @@ namespace vm }); if constexpr (Ack) - res.notify_all(-128); + res.notify_all(); return; } else @@ -273,7 +273,7 @@ namespace vm }); if (Ack && result) - res.notify_all(-128); + res.notify_all(); return result; } } @@ -293,7 +293,7 @@ namespace vm } if constexpr (Ack) - res.notify_all(-128); + res.notify_all(); return; } else @@ -313,7 +313,7 @@ namespace vm } if (Ack && result) - res.notify_all(-128); + res.notify_all(); return result; } } @@ -405,7 +405,7 @@ namespace vm if constexpr (Ack) { - res.notify_all(-128); + res.notify_all(); } } else @@ -415,7 +415,7 @@ namespace vm if constexpr (Ack) { - res.notify_all(-128); + res.notify_all(); } return result; diff --git a/rpcs3/Emu/NP/rpcn_client.cpp b/rpcs3/Emu/NP/rpcn_client.cpp index 9a46c73a1e..5d640a2261 100644 --- a/rpcs3/Emu/NP/rpcn_client.cpp +++ b/rpcs3/Emu/NP/rpcn_client.cpp @@ -292,8 +292,8 @@ namespace rpcn { if (msg.size() == 6) { - addr_sig = *utils::bless>(&msg[0]); - port_sig = *utils::bless>(&msg[4]); + addr_sig = read_from_ptr>(&msg[0]); + port_sig = read_from_ptr>(&msg[4]); last_pong_time = now; } @@ -308,11 +308,11 @@ namespace rpcn { std::vector ping(13); ping[0] = 1; - *utils::bless>(&ping[1]) = user_id; - *utils::bless>(&ping[9]) = local_addr_sig; + write_to_ptr>(ping, 1, user_id); + write_to_ptr>(ping, 9, +local_addr_sig); if (send_packet_from_p2p_port(ping, addr_rpcn_udp) == -1) { - rpcn_log.error("Failed to send ping to rpcn!"); + rpcn_log.error("Failed to send ping to RPCN!"); } last_ping_time = now; } @@ -354,9 +354,9 @@ namespace rpcn } const u8 packet_type = header[0]; - const u16 command = *utils::bless>(&header[1]); - const u32 packet_size = *utils::bless>(&header[3]); - const u64 packet_id = *utils::bless>(&header[7]); + const u16 command = read_from_ptr>(&header[1]); + const u32 packet_size = read_from_ptr>(&header[3]); + const u64 packet_id = read_from_ptr>(&header[7]); if (packet_size < RPCN_HEADER_SIZE) return error_and_disconnect("Invalid packet size"); @@ -397,7 +397,7 @@ namespace rpcn } else { - rpcn_log.error("Tried to forward a reply whose packet_id marks it as internal to rpcn"); + rpcn_log.error("Tried to forward a reply whose packet_id marks it as internal to RPCN"); } } @@ -1707,7 +1707,7 @@ namespace rpcn std::vector data(COMMUNICATION_ID_SIZE + sizeof(u64)); memcpy(data.data(), communication_id.data, COMMUNICATION_ID_SIZE); - *utils::bless>(&data[COMMUNICATION_ID_SIZE]) = room_id; + write_to_ptr>(data, COMMUNICATION_ID_SIZE, room_id); return forge_send(CommandType::PingRoomOwner, req_id, data); } @@ -1811,7 +1811,7 @@ namespace rpcn { std::vector data(COMMUNICATION_ID_SIZE + sizeof(u32)); memcpy(data.data(), communication_id.data, COMMUNICATION_ID_SIZE); - *utils::bless>(&data[COMMUNICATION_ID_SIZE]) = board_id; + write_to_ptr>(data, COMMUNICATION_ID_SIZE, board_id); return forge_send(CommandType::GetBoardInfos, req_id, data); } @@ -2126,7 +2126,7 @@ namespace rpcn if (!fb_mdata->communicationId() || fb_mdata->communicationId()->size() == 0 || fb_mdata->communicationId()->size() > 9 || !fb_mdata->subject() || !fb_mdata->body() || !fb_mdata->data()) { - rpcn_log.warning("Discarded invalid messaged!"); + rpcn_log.warning("Discarded invalid message!"); return; } diff --git a/rpcs3/Emu/NP/rpcn_client.h b/rpcs3/Emu/NP/rpcn_client.h index 046b27665f..7bbd908a4f 100644 --- a/rpcs3/Emu/NP/rpcn_client.h +++ b/rpcs3/Emu/NP/rpcn_client.h @@ -59,7 +59,7 @@ public: error = true; return 0; } - T res = *utils::bless>(&vec[i]); + T res = read_from_ptr>(&vec[i]); i += sizeof(T); return res; } diff --git a/rpcs3/Emu/RSX/Common/simple_array.hpp b/rpcs3/Emu/RSX/Common/simple_array.hpp index 4082447deb..ce587911a8 100644 --- a/rpcs3/Emu/RSX/Common/simple_array.hpp +++ b/rpcs3/Emu/RSX/Common/simple_array.hpp @@ -2,6 +2,7 @@ #include #include +#include namespace rsx { diff --git a/rpcs3/Emu/RSX/Common/texture_cache.h b/rpcs3/Emu/RSX/Common/texture_cache.h index 7c92dffc8d..676e489967 100644 --- a/rpcs3/Emu/RSX/Common/texture_cache.h +++ b/rpcs3/Emu/RSX/Common/texture_cache.h @@ -60,6 +60,7 @@ namespace rsx std::vector sections_to_unprotect; // These sections are to be unpotected and discarded by caller std::vector sections_to_exclude; // These sections are do be excluded from protection manipulation (subtracted from other sections) u32 num_flushable = 0; + u32 num_excluded = 0; // Sections-to-exclude + sections that would have been excluded but are false positives u64 cache_tag = 0; address_range fault_range; @@ -1013,6 +1014,7 @@ namespace rsx // Do not exclude hashed pages from unprotect! They will cause protection holes result.sections_to_exclude.push_back(&tex); } + result.num_excluded++; continue; } @@ -1100,7 +1102,7 @@ namespace rsx else { // This is a read and all overlapping sections were RO and were excluded (except for cause == superseded_by_fbo) - AUDIT(cause.skip_fbos() || (cause.is_read() && !result.sections_to_exclude.empty())); + AUDIT(cause.skip_fbos() || (cause.is_read() && result.num_excluded > 0)); // We did not handle this violation result.clear_sections(); diff --git a/rpcs3/Emu/RSX/Common/texture_cache_helpers.h b/rpcs3/Emu/RSX/Common/texture_cache_helpers.h index ea2196a876..629f40888a 100644 --- a/rpcs3/Emu/RSX/Common/texture_cache_helpers.h +++ b/rpcs3/Emu/RSX/Common/texture_cache_helpers.h @@ -11,8 +11,7 @@ namespace rsx enum surface_transform : u32 { identity = 0, // Nothing - argb_to_bgra = 1, // Swap ARGB to BGRA (endian swap) - coordinate_transform = 2 // Incoming source coordinates may generated based on the format of the secondary (dest) surface. Recalculate them before use. + coordinate_transform = 1 // Incoming source coordinates may generated based on the format of the secondary (dest) surface. Recalculate them before use. }; template diff --git a/rpcs3/Emu/RSX/Common/texture_cache_utils.h b/rpcs3/Emu/RSX/Common/texture_cache_utils.h index 7c3704312c..20f83c0c4b 100644 --- a/rpcs3/Emu/RSX/Common/texture_cache_utils.h +++ b/rpcs3/Emu/RSX/Common/texture_cache_utils.h @@ -472,7 +472,6 @@ namespace rsx block_type blocks[num_blocks]; texture_cache_type *m_tex_cache; std::unordered_set m_in_use; - bool m_purging = false; public: atomic_t m_unreleased_texture_objects = { 0 }; //Number of invalidated objects not yet freed from memory @@ -554,7 +553,6 @@ namespace rsx void purge_unreleased_sections() { - m_purging = true; std::vector textures_to_remove; // Reclaims all graphics memory consumed by dirty textures @@ -579,7 +577,6 @@ namespace rsx tex->destroy(); } - m_purging = false; AUDIT(m_unreleased_texture_objects == 0); } @@ -663,16 +660,12 @@ namespace rsx void on_ranged_block_first_section_created(block_type& block) { - AUDIT(!m_purging); AUDIT(m_in_use.find(&block) == m_in_use.end()); m_in_use.insert(&block); } void on_ranged_block_last_section_destroyed(block_type& block) { - if (m_purging) - return; - AUDIT(m_in_use.find(&block) != m_in_use.end()); m_in_use.erase(&block); } diff --git a/rpcs3/Emu/RSX/Core/RSXVertexTypes.h b/rpcs3/Emu/RSX/Core/RSXVertexTypes.h index e65c168a09..21f1770487 100644 --- a/rpcs3/Emu/RSX/Core/RSXVertexTypes.h +++ b/rpcs3/Emu/RSX/Core/RSXVertexTypes.h @@ -62,11 +62,12 @@ namespace rsx u32 real_offset_address = 0; u8 memory_location = 0; u8 attribute_stride = 0; + std::pair vertex_range{}; rsx::simple_array locations; // Check if we need to upload a full unoptimized range, i.e [0-max_index] - std::pair calculate_required_range(u32 first, u32 count) const; + std::pair calculate_required_range(u32 first, u32 count); }; enum attribute_buffer_placement : u8 @@ -100,6 +101,7 @@ namespace rsx result->single_vertex = false; result->locations.clear(); result->interleaved = true; + result->vertex_range.second = 0; return result; } diff --git a/rpcs3/Emu/RSX/GL/GLDraw.cpp b/rpcs3/Emu/RSX/GL/GLDraw.cpp index 360c252a83..3a8e47f4a7 100644 --- a/rpcs3/Emu/RSX/GL/GLDraw.cpp +++ b/rpcs3/Emu/RSX/GL/GLDraw.cpp @@ -341,23 +341,31 @@ void GLGSRender::load_texture_env() auto sampler_state = static_cast(fs_sampler_state[i].get()); const auto& tex = rsx::method_registers.fragment_textures[i]; + const auto previous_format_class = sampler_state->format_class; if (m_samplers_dirty || m_textures_dirty[i] || m_gl_texture_cache.test_if_descriptor_expired(cmd, m_rtts, sampler_state, tex)) { if (tex.enabled()) { *sampler_state = m_gl_texture_cache.upload_texture(cmd, tex, m_rtts); + + if (sampler_state->validate()) + { + if (m_textures_dirty[i]) + { + m_fs_sampler_states[i].apply(tex, fs_sampler_state[i].get()); + } + else if (sampler_state->format_class != previous_format_class) + { + m_graphics_state |= rsx::fragment_program_state_dirty; + } + } } else { *sampler_state = {}; } - if (m_textures_dirty[i] && sampler_state->validate()) - { - m_fs_sampler_states[i].apply(tex, fs_sampler_state[i].get()); - } - m_textures_dirty[i] = false; } } @@ -372,23 +380,31 @@ void GLGSRender::load_texture_env() auto sampler_state = static_cast(vs_sampler_state[i].get()); const auto& tex = rsx::method_registers.vertex_textures[i]; + const auto previous_format_class = sampler_state->format_class; if (m_samplers_dirty || m_vertex_textures_dirty[i] || m_gl_texture_cache.test_if_descriptor_expired(cmd, m_rtts, sampler_state, tex)) { if (rsx::method_registers.vertex_textures[i].enabled()) { *sampler_state = m_gl_texture_cache.upload_texture(cmd, rsx::method_registers.vertex_textures[i], m_rtts); + + if (sampler_state->validate()) + { + if (m_vertex_textures_dirty[i]) + { + m_vs_sampler_states[i].apply(tex, vs_sampler_state[i].get()); + } + else if (sampler_state->format_class != previous_format_class) + { + m_graphics_state |= rsx::vertex_program_state_dirty; + } + } } else { *sampler_state = {}; } - if (m_vertex_textures_dirty[i] && sampler_state->validate()) - { - m_vs_sampler_states[i].apply(tex, vs_sampler_state[i].get()); - } - m_vertex_textures_dirty[i] = false; } } @@ -498,10 +514,19 @@ void GLGSRender::emit_geometry(u32 sub_index) // Rebase vertex bases instead of for (auto& info : m_vertex_layout.interleaved_blocks) { + info->vertex_range.second = 0; const auto vertex_base_offset = rsx::method_registers.vertex_data_base_offset(); info->real_offset_address = rsx::get_address(rsx::get_vertex_offset_from_base(vertex_base_offset, info->base_offset), info->memory_location); } } + else + { + // Discard cached results + for (auto& info : m_vertex_layout.interleaved_blocks) + { + info->vertex_range.second = 0; + } + } if (vertex_state && !m_vertex_layout.validate()) { diff --git a/rpcs3/Emu/RSX/GL/GLPipelineCompiler.cpp b/rpcs3/Emu/RSX/GL/GLPipelineCompiler.cpp index ff20beb17f..445841b44b 100644 --- a/rpcs3/Emu/RSX/GL/GLPipelineCompiler.cpp +++ b/rpcs3/Emu/RSX/GL/GLPipelineCompiler.cpp @@ -55,7 +55,7 @@ namespace gl job.completion_callback(result); } - thread_ctrl::wait_on(m_work_queue, nullptr); + thread_ctrl::wait_on(m_work_queue); } } diff --git a/rpcs3/Emu/RSX/GL/GLPresent.cpp b/rpcs3/Emu/RSX/GL/GLPresent.cpp index b99a230c2a..5fdedf5ee8 100644 --- a/rpcs3/Emu/RSX/GL/GLPresent.cpp +++ b/rpcs3/Emu/RSX/GL/GLPresent.cpp @@ -259,7 +259,7 @@ void GLGSRender::flip(const rsx::display_flip_info_t& info) } else { - m_frame->present_frame(sshot_frame, buffer_width, buffer_height, false); + m_frame->present_frame(sshot_frame, buffer_width * 4, buffer_width, buffer_height, false); } } diff --git a/rpcs3/Emu/RSX/GL/GLTextureCache.cpp b/rpcs3/Emu/RSX/GL/GLTextureCache.cpp index e5a2888708..9ef77cb05c 100644 --- a/rpcs3/Emu/RSX/GL/GLTextureCache.cpp +++ b/rpcs3/Emu/RSX/GL/GLTextureCache.cpp @@ -193,7 +193,9 @@ namespace gl for (const auto &slice : sources) { if (!slice.src) + { continue; + } const bool typeless = !formats_are_bitcast_compatible(slice.src, dst_image); ensure(typeless || dst_aspect == slice.src->aspect()); @@ -258,25 +260,18 @@ namespace gl } else { - ensure(dst_image->get_target() == gl::texture::target::texture2D); - auto _blitter = gl::g_hw_blitter; const areai src_rect = { src_x, src_y, src_x + src_w, src_y + src_h }; const areai dst_rect = { slice.dst_x, slice.dst_y, slice.dst_x + slice.dst_w, slice.dst_y + slice.dst_h }; - gl::texture* _dst; - if (src_image->get_internal_format() == dst_image->get_internal_format() && slice.level == 0) - { - _dst = dst_image; - } - else + gl::texture* _dst = dst_image; + if (src_image->get_internal_format() != dst_image->get_internal_format() || slice.level != 0 || slice.dst_z != 0) [[ unlikely ]] { tmp = std::make_unique(GL_TEXTURE_2D, dst_rect.x2, dst_rect.y2, 1, 1, static_cast(slice.src->get_internal_format())); _dst = tmp.get(); } - _blitter->scale_image(cmd, src_image, _dst, - src_rect, dst_rect, false, {}); + _blitter->scale_image(cmd, src_image, _dst, src_rect, dst_rect, false, {}); if (_dst != dst_image) { diff --git a/rpcs3/Emu/RSX/GL/glutils/capabilities.cpp b/rpcs3/Emu/RSX/GL/glutils/capabilities.cpp index df92d7b920..3bc9cd37f2 100644 --- a/rpcs3/Emu/RSX/GL/glutils/capabilities.cpp +++ b/rpcs3/Emu/RSX/GL/glutils/capabilities.cpp @@ -39,7 +39,7 @@ namespace gl if (!ext_count) { - rsx_log.error("Coult not initialize GL driver capabilities. Is OpenGL initialized?"); + rsx_log.error("Could not initialize GL driver capabilities. Is OpenGL initialized?"); return; } diff --git a/rpcs3/Emu/RSX/GSFrameBase.h b/rpcs3/Emu/RSX/GSFrameBase.h index 680445dd6b..e81aa55253 100644 --- a/rpcs3/Emu/RSX/GSFrameBase.h +++ b/rpcs3/Emu/RSX/GSFrameBase.h @@ -30,6 +30,6 @@ public: virtual display_handle_t handle() const = 0; virtual bool can_consume_frame() const = 0; - virtual void present_frame(std::vector& data, const u32 width, const u32 height, bool is_bgra) const = 0; - virtual void take_screenshot(const std::vector sshot_data, const u32 sshot_width, const u32 sshot_height, bool is_bgra) = 0; + virtual void present_frame(std::vector& data, u32 pitch, u32 width, u32 height, bool is_bgra) const = 0; + virtual void take_screenshot(const std::vector sshot_data, u32 sshot_width, u32 sshot_height, bool is_bgra) = 0; }; diff --git a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu.cpp b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu.cpp index ebbcb88e9a..b7b255e780 100644 --- a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu.cpp +++ b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu.cpp @@ -161,7 +161,7 @@ namespace rsx this->on_close = std::move(on_close); visible = true; - const auto notify = std::make_shared>(false); + const auto notify = std::make_shared>(0); auto& overlayman = g_fxo->get(); overlayman.attach_thread_input( diff --git a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_settings.cpp b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_settings.cpp index 36f36c355c..b398ecf3b7 100644 --- a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_settings.cpp +++ b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_settings.cpp @@ -59,7 +59,6 @@ namespace rsx home_menu_settings_advanced::home_menu_settings_advanced(u16 x, u16 y, u16 width, u16 height, bool use_separators, home_menu_page* parent) : home_menu_settings_page(x, y, width, height, use_separators, parent, get_localized_string(localized_string_id::HOME_MENU_SETTINGS_ADVANCED)) { - add_checkbox(&g_cfg.core.spu_loop_detection, "SPU Loop Detection"); add_signed_slider(&g_cfg.core.preferred_spu_threads, "Preferred SPU Threads", "", 1); add_unsigned_slider(&g_cfg.core.max_cpu_preempt_count_per_frame, "Max Power Saving CPU-Preemptions", "", 1); add_checkbox(&g_cfg.core.rsx_accurate_res_access, "Accurate RSX reservation access"); diff --git a/rpcs3/Emu/RSX/Overlays/Shaders/shader_loading_dialog.cpp b/rpcs3/Emu/RSX/Overlays/Shaders/shader_loading_dialog.cpp index 4d20bddc9d..d6a9f74f6b 100644 --- a/rpcs3/Emu/RSX/Overlays/Shaders/shader_loading_dialog.cpp +++ b/rpcs3/Emu/RSX/Overlays/Shaders/shader_loading_dialog.cpp @@ -40,7 +40,7 @@ namespace rsx } } - void shader_loading_dialog::update_msg(u32 index, const std::string& msg) + void shader_loading_dialog::update_msg(u32 index, std::string msg) { if (!dlg) { @@ -49,9 +49,9 @@ namespace rsx ref_cnt++; - Emu.CallFromMainThread([&, index, msg]() + Emu.CallFromMainThread([&, index, message = std::move(msg)]() { - dlg->ProgressBarSetMsg(index, msg); + dlg->ProgressBarSetMsg(index, message); ref_cnt--; }); } diff --git a/rpcs3/Emu/RSX/Overlays/Shaders/shader_loading_dialog.h b/rpcs3/Emu/RSX/Overlays/Shaders/shader_loading_dialog.h index 7d141c49d2..ae95b4bda8 100644 --- a/rpcs3/Emu/RSX/Overlays/Shaders/shader_loading_dialog.h +++ b/rpcs3/Emu/RSX/Overlays/Shaders/shader_loading_dialog.h @@ -11,7 +11,7 @@ namespace rsx virtual ~shader_loading_dialog() = default; virtual void create(const std::string& msg, const std::string& title); - virtual void update_msg(u32 index, const std::string& msg); + virtual void update_msg(u32 index, std::string msg); virtual void inc_value(u32 index, u32 value); virtual void set_value(u32 index, u32 value); virtual void set_limit(u32 index, u32 limit); diff --git a/rpcs3/Emu/RSX/Overlays/Shaders/shader_loading_dialog_native.cpp b/rpcs3/Emu/RSX/Overlays/Shaders/shader_loading_dialog_native.cpp index 7b8ddd29c8..c0d0f4ff93 100644 --- a/rpcs3/Emu/RSX/Overlays/Shaders/shader_loading_dialog_native.cpp +++ b/rpcs3/Emu/RSX/Overlays/Shaders/shader_loading_dialog_native.cpp @@ -31,9 +31,9 @@ namespace rsx }); } - void shader_loading_dialog_native::update_msg(u32 index, const std::string& msg) + void shader_loading_dialog_native::update_msg(u32 index, std::string msg) { - dlg->progress_bar_set_message(index, msg); + dlg->progress_bar_set_message(index, std::move(msg)); owner->flip({}); } diff --git a/rpcs3/Emu/RSX/Overlays/Shaders/shader_loading_dialog_native.h b/rpcs3/Emu/RSX/Overlays/Shaders/shader_loading_dialog_native.h index 08ad43bfcf..0d73324562 100644 --- a/rpcs3/Emu/RSX/Overlays/Shaders/shader_loading_dialog_native.h +++ b/rpcs3/Emu/RSX/Overlays/Shaders/shader_loading_dialog_native.h @@ -21,7 +21,7 @@ namespace rsx shader_loading_dialog_native(GSRender* ptr); void create(const std::string& msg, const std::string&/* title*/) override; - void update_msg(u32 index, const std::string& msg) override; + void update_msg(u32 index, std::string msg) override; void inc_value(u32 index, u32 value) override; void set_value(u32 index, u32 value) override; void set_limit(u32 index, u32 limit) override; diff --git a/rpcs3/Emu/RSX/Overlays/overlay_manager.cpp b/rpcs3/Emu/RSX/Overlays/overlay_manager.cpp index 50517af3e2..1d8a6292f6 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_manager.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_manager.cpp @@ -295,7 +295,7 @@ namespace rsx } else if (!m_input_thread_abort) { - thread_ctrl::wait_on(m_input_token_stack, nullptr); + thread_ctrl::wait_on(m_input_token_stack); } } } diff --git a/rpcs3/Emu/RSX/Overlays/overlay_message_dialog.cpp b/rpcs3/Emu/RSX/Overlays/overlay_message_dialog.cpp index 821bf4c74a..ac8c359bdd 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_message_dialog.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_message_dialog.cpp @@ -295,7 +295,7 @@ namespace rsx { if (!m_stop_input_loop) { - const auto notify = std::make_shared>(false); + const auto notify = std::make_shared>(0); auto& overlayman = g_fxo->get(); if (interactive) @@ -342,9 +342,9 @@ namespace rsx return CELL_OK; } - void message_dialog::set_text(const std::string& text) + void message_dialog::set_text(std::string text) { - text_guard.set_text(text); + text_guard.set_text(std::move(text)); } void message_dialog::update_custom_background() @@ -411,12 +411,12 @@ namespace rsx taskbar_index = index; } - error_code message_dialog::progress_bar_set_message(u32 index, const std::string& msg) + error_code message_dialog::progress_bar_set_message(u32 index, std::string msg) { if (index >= num_progress_bars) return CELL_MSGDIALOG_ERROR_PARAM; - ::at32(bar_text_guard, index).set_text(msg); + ::at32(bar_text_guard, index).set_text(std::move(msg)); return CELL_OK; } diff --git a/rpcs3/Emu/RSX/Overlays/overlay_message_dialog.h b/rpcs3/Emu/RSX/Overlays/overlay_message_dialog.h index 989dec8c16..cfc5d44eba 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_message_dialog.h +++ b/rpcs3/Emu/RSX/Overlays/overlay_message_dialog.h @@ -72,12 +72,12 @@ namespace rsx error_code show(bool is_blocking, const std::string& text, const MsgDialogType& type, std::function on_close); - void set_text(const std::string& text); + void set_text(std::string text); void update_custom_background(); u32 progress_bar_count() const; void progress_bar_set_taskbar_index(s32 index); - error_code progress_bar_set_message(u32 index, const std::string& msg); + error_code progress_bar_set_message(u32 index, std::string msg); error_code progress_bar_increment(u32 index, f32 value); error_code progress_bar_set_value(u32 index, f32 value); error_code progress_bar_reset(u32 index); diff --git a/rpcs3/Emu/RSX/Overlays/overlay_osk.cpp b/rpcs3/Emu/RSX/Overlays/overlay_osk.cpp index 44c8ba6904..f4115720ba 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_osk.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_osk.cpp @@ -644,10 +644,10 @@ namespace rsx void osk_dialog::on_button_pressed(pad_button button_press, bool is_auto_repeat) { - if (!pad_input_enabled) + if (!pad_input_enabled || ignore_device_events) return; - if (!ignore_device_events && input_device.exchange(CELL_OSKDIALOG_INPUT_DEVICE_PAD) != CELL_OSKDIALOG_INPUT_DEVICE_PAD) + if (input_device.exchange(CELL_OSKDIALOG_INPUT_DEVICE_PAD) != CELL_OSKDIALOG_INPUT_DEVICE_PAD) { osk.notice("on_button_pressed: sending CELL_SYSUTIL_OSKDIALOG_INPUT_DEVICE_CHANGED with CELL_OSKDIALOG_INPUT_DEVICE_PAD"); sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_INPUT_DEVICE_CHANGED, CELL_OSKDIALOG_INPUT_DEVICE_PAD); @@ -900,10 +900,10 @@ namespace rsx void osk_dialog::on_key_pressed(u32 led, u32 mkey, u32 key_code, u32 out_key_code, bool pressed, std::u32string key) { - if (!pressed || !keyboard_input_enabled) + if (!pressed || !keyboard_input_enabled || ignore_device_events) return; - if (!ignore_device_events && input_device.exchange(CELL_OSKDIALOG_INPUT_DEVICE_KEYBOARD) != CELL_OSKDIALOG_INPUT_DEVICE_KEYBOARD) + if (input_device.exchange(CELL_OSKDIALOG_INPUT_DEVICE_KEYBOARD) != CELL_OSKDIALOG_INPUT_DEVICE_KEYBOARD) { osk.notice("on_key_pressed: sending CELL_SYSUTIL_OSKDIALOG_INPUT_DEVICE_CHANGED with CELL_OSKDIALOG_INPUT_DEVICE_KEYBOARD"); sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_INPUT_DEVICE_CHANGED, CELL_OSKDIALOG_INPUT_DEVICE_KEYBOARD); @@ -1621,7 +1621,7 @@ namespace rsx update_panel(); - const auto notify = std::make_shared>(false); + const auto notify = std::make_shared>(0); auto& overlayman = g_fxo->get(); overlayman.attach_thread_input( @@ -1631,7 +1631,7 @@ namespace rsx while (!Emu.IsStopped() && !*notify) { - notify->wait(false, atomic_wait_timeout{1'000'000}); + notify->wait(0, atomic_wait_timeout{1'000'000}); } } } diff --git a/rpcs3/Emu/RSX/Overlays/overlay_user_list_dialog.cpp b/rpcs3/Emu/RSX/Overlays/overlay_user_list_dialog.cpp index c7abe5fba2..3f8d296436 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_user_list_dialog.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_user_list_dialog.cpp @@ -240,7 +240,7 @@ namespace rsx this->on_close = std::move(on_close); visible = true; - const auto notify = std::make_shared>(false); + const auto notify = std::make_shared>(0); auto& overlayman = g_fxo->get(); overlayman.attach_thread_input( @@ -250,7 +250,7 @@ namespace rsx while (!Emu.IsStopped() && !*notify) { - notify->wait(false, atomic_wait_timeout{1'000'000}); + notify->wait(0, atomic_wait_timeout{1'000'000}); } return CELL_OK; diff --git a/rpcs3/Emu/RSX/Overlays/overlay_utils.cpp b/rpcs3/Emu/RSX/Overlays/overlay_utils.cpp index 4be5d07c79..65dde9a8a7 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_utils.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_utils.cpp @@ -60,10 +60,10 @@ static auto s_ascii_lowering_map = []() }(); template -void process_multibyte(const std::string s, F&& func) +void process_multibyte(const std::string& s, F&& func) { - const auto end = s.length(); - for (u32 index = 0; index < end; ++index) + const usz end = s.length(); + for (usz index = 0; index < end; ++index) { const u8 code = static_cast(s[index]); @@ -78,7 +78,7 @@ void process_multibyte(const std::string s, F&& func) continue; } - const auto extra_bytes = (code <= 0xDF) ? 1u : (code <= 0xEF) ? 2u : 3u; + const u32 extra_bytes = (code <= 0xDF) ? 1u : (code <= 0xEF) ? 2u : 3u; if ((index + extra_bytes) > end) { // Malformed string, abort diff --git a/rpcs3/Emu/RSX/Overlays/overlays.cpp b/rpcs3/Emu/RSX/Overlays/overlays.cpp index d4d3fb3511..a8064a8803 100644 --- a/rpcs3/Emu/RSX/Overlays/overlays.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlays.cpp @@ -18,9 +18,9 @@ namespace rsx { thread_local DECLARE(user_interface::g_thread_bit) = 0; - u64 user_interface::alloc_thread_bit() + u32 user_interface::alloc_thread_bit() { - auto [_old, ok] = this->thread_bits.fetch_op([](u64& bits) + auto [_old, ok] = this->thread_bits.fetch_op([](u32& bits) { if (~bits) { @@ -38,7 +38,7 @@ namespace rsx return 0; } - const u64 r = u64{1} << std::countr_one(_old); + const u32 r = u32{1} << std::countr_one(_old); ::overlays.trace("Bit allocated (%u)", r); return r; } @@ -385,7 +385,7 @@ namespace rsx m_stop_pad_interception.release(stop_pad_interception); m_stop_input_loop.release(true); - while (u64 b = thread_bits) + while (u32 b = thread_bits) { if (b == g_thread_bit) { diff --git a/rpcs3/Emu/RSX/Overlays/overlays.h b/rpcs3/Emu/RSX/Overlays/overlays.h index 05a2074e8d..f12d0ed209 100644 --- a/rpcs3/Emu/RSX/Overlays/overlays.h +++ b/rpcs3/Emu/RSX/Overlays/overlays.h @@ -85,14 +85,14 @@ namespace rsx bool m_start_pad_interception = true; atomic_t m_stop_pad_interception = false; atomic_t m_input_thread_detached = false; - atomic_t thread_bits = 0; + atomic_t thread_bits = 0; bool m_keyboard_input_enabled = false; // Allow keyboard input bool m_keyboard_pad_handler_active = true; // Initialized as true to prevent keyboard input until proven otherwise. bool m_allow_input_on_pause = false; - static thread_local u64 g_thread_bit; + static thread_local u32 g_thread_bit; - u64 alloc_thread_bit(); + u32 alloc_thread_bit(); std::function on_close = nullptr; @@ -114,7 +114,7 @@ namespace rsx private: user_interface* m_parent; - u64 m_thread_bit; + u32 m_thread_bit; }; public: s32 return_code = 0; // CELL_OK diff --git a/rpcs3/Emu/RSX/RSXDisAsm.cpp b/rpcs3/Emu/RSX/RSXDisAsm.cpp index 46ef4e4123..81ba45c586 100644 --- a/rpcs3/Emu/RSX/RSXDisAsm.cpp +++ b/rpcs3/Emu/RSX/RSXDisAsm.cpp @@ -43,6 +43,11 @@ u32 RSXDisAsm::disasm(u32 pc) if (m_op & RSX_METHOD_NON_METHOD_CMD_MASK) { + if (m_mode == cpu_disasm_mode::survey_cmd_size) + { + return 4; + } + if ((m_op & RSX_METHOD_OLD_JUMP_CMD_MASK) == RSX_METHOD_OLD_JUMP_CMD) { u32 jumpAddr = m_op & RSX_METHOD_OLD_JUMP_OFFSET_MASK; @@ -86,10 +91,13 @@ u32 RSXDisAsm::disasm(u32 pc) } } - if (i == 1) - Write("nop", 0); - else - Write(fmt::format("nop x%u", i), 0); + if (m_mode != cpu_disasm_mode::survey_cmd_size) + { + if (i == 1) + Write("nop", 0); + else + Write(fmt::format("nop x%u", i), 0); + } return i * 4; } @@ -109,6 +117,8 @@ u32 RSXDisAsm::disasm(u32 pc) pc += 4; + std::string str; + for (u32 i = 0; i < count; i++, pc += 4) { if (!try_read_op(pc)) @@ -137,7 +147,9 @@ u32 RSXDisAsm::disasm(u32 pc) continue; } - std::string str = rsx::get_pretty_printing_function(id)(id, m_op); + str.clear(); + rsx::get_pretty_printing_function(id)(str, id, m_op); + Write(str, m_mode == cpu_disasm_mode::list ? i : count, non_inc, id); } @@ -163,7 +175,11 @@ void RSXDisAsm::Write(std::string_view str, s32 count, bool is_non_inc, u32 id) { last_opcode.clear(); - if (count >= 0) + if (count == 1 && !is_non_inc) + { + fmt::append(last_opcode, "[%08x] ( )", dump_pc); + } + else if (count >= 0) { fmt::append(last_opcode, "[%08x] (%s%u)", dump_pc, is_non_inc ? "+" : "", count); } diff --git a/rpcs3/Emu/RSX/RSXFIFO.cpp b/rpcs3/Emu/RSX/RSXFIFO.cpp index 8dcc7dbad9..637db20913 100644 --- a/rpcs3/Emu/RSX/RSXFIFO.cpp +++ b/rpcs3/Emu/RSX/RSXFIFO.cpp @@ -195,7 +195,7 @@ namespace rsx } } - const auto ret = utils::bless>(&m_cache)[(addr - m_cache_addr) >> 2]; + const auto ret = read_from_ptr>(+m_cache[0], addr - m_cache_addr); return {true, ret}; } @@ -701,6 +701,24 @@ namespace rsx return; } + // Optimize returning to another CALL + if ((ctrl->put & ~3) != fifo_ret_addr) + { + if (u32 addr = iomap_table.get_addr(fifo_ret_addr); addr != umax) + { + const u32 cmd0 = vm::read32(addr); + + // Check for missing step flags, in case the user is single-stepping in the debugger + if ((cmd0 & RSX_METHOD_CALL_CMD_MASK) == RSX_METHOD_CALL_CMD && cpu_flag::dbg_step - state) + { + fifo_ctrl->set_get(cmd0 & RSX_METHOD_CALL_OFFSET_MASK); + last_known_code_start = ctrl->get; + fifo_ret_addr += 4; + return; + } + } + } + fifo_ctrl->set_get(std::exchange(fifo_ret_addr, RSX_CALL_STACK_EMPTY)); last_known_code_start = ctrl->get; return; diff --git a/rpcs3/Emu/RSX/RSXThread.cpp b/rpcs3/Emu/RSX/RSXThread.cpp index 6f82e38a39..8be464eb13 100644 --- a/rpcs3/Emu/RSX/RSXThread.cpp +++ b/rpcs3/Emu/RSX/RSXThread.cpp @@ -269,8 +269,14 @@ namespace rsx } } - std::pair interleaved_range_info::calculate_required_range(u32 first, u32 count) const + std::pair interleaved_range_info::calculate_required_range(u32 first, u32 count) { + if (vertex_range.second) + { + // Cached result + return vertex_range; + } + if (single_vertex) { return { 0, 1 }; @@ -280,10 +286,15 @@ namespace rsx u32 _max_index = 0; u32 _min_index = first; + u32 frequencies[rsx::limits::vertex_count]; + u32 freq_count = rsx::method_registers.current_draw_clause.command == rsx::draw_command::indexed ? 0 : u32{umax}; + u32 max_result_by_division = 0; // Guaranteed maximum + for (const auto &attrib : locations) { if (attrib.frequency <= 1) [[likely]] { + freq_count = umax; _max_index = max_index; } else @@ -294,12 +305,24 @@ namespace rsx { // Actually uses the modulo operator _min_index = 0; - _max_index = attrib.frequency - 1; + _max_index = std::max(_max_index, attrib.frequency - 1); + + if (max_result_by_division < _max_index) + { + if (freq_count != umax) + { + if (std::find(frequencies, frequencies + freq_count, attrib.frequency) == frequencies + freq_count) + { + frequencies[freq_count++] = attrib.frequency; + } + } + } } else { // Same as having no modulo _max_index = max_index; + freq_count = umax; } } else @@ -307,12 +330,106 @@ namespace rsx // Division operator _min_index = std::min(_min_index, first / attrib.frequency); _max_index = std::max(_max_index, utils::aligned_div(max_index, attrib.frequency)); + + if (freq_count > 0 && freq_count != umax) + { + const u32 max = utils::aligned_div(max_index, attrib.frequency); + max_result_by_division = std::max(max_result_by_division, max); + + // Discard lower frequencies because it has been proven that there are indices higher than them + freq_count -= frequencies + freq_count - std::remove_if(frequencies, frequencies + freq_count, [&max_result_by_division](u32 freq) + { + return freq <= max_result_by_division; + }); + } } } } + while (freq_count > 0 && freq_count != umax) + { + const rsx::index_array_type index_type = rsx::method_registers.current_draw_clause.is_immediate_draw ? + rsx::index_array_type::u32 : + rsx::method_registers.index_type(); + + const u32 index_size = index_type == rsx::index_array_type::u32 ? 4 : 2; + + const auto render = rsx::get_current_renderer(); + + // If we can access a bit a more memory than required - do it + // The alternative would be re-iterating again over all of them + if (get_location(real_offset_address) == CELL_GCM_LOCATION_LOCAL) + { + if (utils::add_saturate(real_offset_address - rsx::constants::local_mem_base, (_max_index + 1) * attribute_stride) <= render->local_mem_size) + { + break; + } + } + else if (real_offset_address % 0x100000 + (_max_index + 1) * attribute_stride <= 0x100000)//(vm::check_addr(real_offset_address, vm::page_readable, (_max_index + 1) * attribute_stride)) + { + break; + } + + _max_index = 0; + + auto re_evaluate = [&] (const std::byte* ptr, T) + { + const u64 restart = rsx::method_registers.restart_index_enabled() ? rsx::method_registers.restart_index() : u64{umax}; + + for (u32 _index = first; _index < first + count; _index++) + { + const auto value = read_from_ptr>(ptr, _index * sizeof(T)); + + if (value == restart) + { + continue; + } + + for (u32 freq_it = 0; freq_it < freq_count; freq_it++) + { + const auto res = value % frequencies[freq_it]; + + if (res > _max_index) + { + _max_index = res; + } + } + } + }; + + if (index_size == 4) + { + if (!render->element_push_buffer.empty()) [[unlikely]] + { + // Indices provided via immediate mode + re_evaluate(reinterpret_cast(render->element_push_buffer.data()), u32{}); + } + else + { + const u32 address = (0 - index_size) & get_address(rsx::method_registers.index_array_address(), rsx::method_registers.index_array_location()); + re_evaluate(vm::get_super_ptr(address), u32{}); + } + } + else + { + if (!render->element_push_buffer.empty()) [[unlikely]] + { + // Indices provided via immediate mode + re_evaluate(reinterpret_cast(render->element_push_buffer.data()), u16{}); + } + else + { + const u32 address = (0 - index_size) & get_address(rsx::method_registers.index_array_address(), rsx::method_registers.index_array_location()); + re_evaluate(vm::get_super_ptr(address), u16{}); + } + } + + break; + } + ensure(_max_index >= _min_index); - return { _min_index, (_max_index - _min_index) + 1 }; + vertex_range = { _min_index, (_max_index - _min_index) + 1 }; + return vertex_range; } u32 get_vertex_type_size_on_host(vertex_base_type type, u32 size) @@ -853,9 +970,8 @@ namespace rsx g_fxo->get().set_thread(std::shared_ptr>>(new named_thread>("VBlank Thread"sv, [this]() -> void { - // See sys_timer_usleep for details #ifdef __linux__ - constexpr u32 host_min_quantum = 50; + constexpr u32 host_min_quantum = 10; #else constexpr u32 host_min_quantum = 500; #endif @@ -878,8 +994,12 @@ namespace rsx // Calculate time remaining to that time (0 if we passed it) const u64 wait_for = current >= post_event_time ? 0 : post_event_time - current; +#ifdef __linux__ + const u64 wait_sleep = wait_for; +#else // Substract host operating system min sleep quantom to get sleep time const u64 wait_sleep = wait_for - u64{wait_for >= host_min_quantum} * host_min_quantum; +#endif if (!wait_for) { @@ -2795,7 +2915,7 @@ namespace rsx if (persistent != nullptr) { - for (const auto &block : layout.interleaved_blocks) + for (interleaved_range_info* block : layout.interleaved_blocks) { auto range = block->calculate_required_range(first_vertex, vertex_count); @@ -2986,9 +3106,9 @@ namespace rsx fifo_ctrl->invalidate_cache(); } - std::pair thread::try_get_pc_of_x_cmds_backwards(u32 count, u32 get) const + std::pair thread::try_get_pc_of_x_cmds_backwards(s32 count, u32 get) const { - if (!ctrl) + if (!ctrl || state & cpu_flag::exit) { return {0, umax}; } @@ -3004,31 +3124,61 @@ namespace rsx RSXDisAsm disasm(cpu_disasm_mode::survey_cmd_size, vm::g_sudo_addr, 0, this); std::vector pcs_of_valid_cmds; - pcs_of_valid_cmds.reserve(std::min((get - start) / 16, 0x4000)); // Rough estimation of final array size + + if (get > start) + { + pcs_of_valid_cmds.reserve(std::min((get - start) / 16, 0x4000)); // Rough estimation of final array size + } auto probe_code_region = [&](u32 probe_start) -> std::pair { - pcs_of_valid_cmds.clear(); - pcs_of_valid_cmds.push_back(probe_start); - - while (pcs_of_valid_cmds.back() < get) - { - if (u32 advance = disasm.disasm(pcs_of_valid_cmds.back())) - { - pcs_of_valid_cmds.push_back(pcs_of_valid_cmds.back() + advance); - } - else - { - return {0, get}; - } - } - - if (pcs_of_valid_cmds.size() == 1u || pcs_of_valid_cmds.back() != get) + if (probe_start > get) { return {0, get}; } - u32 found_cmds_count = std::min(count, ::size32(pcs_of_valid_cmds) - 1); + pcs_of_valid_cmds.clear(); + pcs_of_valid_cmds.push_back(probe_start); + + usz index_of_get = umax; + usz until = umax; + + while (pcs_of_valid_cmds.size() < until) + { + if (u32 advance = disasm.disasm(pcs_of_valid_cmds.back())) + { + pcs_of_valid_cmds.push_back(utils::add_saturate(pcs_of_valid_cmds.back(), advance)); + } + else + { + break; + } + + if (index_of_get == umax && pcs_of_valid_cmds.back() >= get) + { + index_of_get = pcs_of_valid_cmds.size() - 1; + until = index_of_get + 1; + + if (count < 0 && pcs_of_valid_cmds.back() == get) + { + until -= count; + } + } + } + + if (index_of_get == umax || pcs_of_valid_cmds[index_of_get] != get) + { + return {0, get}; + } + + if (count < 0) + { + const u32 found_cmds_count = std::min(-count, ::size32(pcs_of_valid_cmds) - 1 - index_of_get); + + return {found_cmds_count, pcs_of_valid_cmds[index_of_get + found_cmds_count]}; + } + + const u32 found_cmds_count = std::min(count, ::size32(pcs_of_valid_cmds) - 1); return {found_cmds_count, *(pcs_of_valid_cmds.end() - 1 - found_cmds_count)}; }; @@ -3086,6 +3236,25 @@ namespace rsx recovered_fifo_cmds_history.push({fifo_ctrl->last_cmd(), current_time}); } + std::string thread::dump_misc() const + { + std::string ret = cpu_thread::dump_misc(); + + const auto flags = +state; + + if (is_paused(flags) && flags & cpu_flag::wait) + { + fmt::append(ret, "\nFragment Program Hash: %X.fp", current_fragment_program.get_data() ? program_hash_util::fragment_program_utils::get_fragment_program_ucode_hash(current_fragment_program) : 0); + fmt::append(ret, "\nVertex Program Hash: %X.vp", current_vertex_program.data.empty() ? 0 : program_hash_util::vertex_program_utils::get_vertex_program_ucode_hash(current_vertex_program)); + } + else + { + fmt::append(ret, "\n"); + } + + return ret; + } + std::vector> thread::dump_callstack_list() const { std::vector> result; @@ -3116,7 +3285,7 @@ namespace rsx { #ifdef __linux__ // NOTE: Assumption that timer initialization has succeeded - u64 host_min_quantum = remaining <= 1000 ? 10 : 50; + constexpr u64 host_min_quantum = 10; #else // Host scheduler quantum for windows (worst case) // NOTE: On ps3 this function has very high accuracy @@ -3125,8 +3294,7 @@ namespace rsx if (remaining >= host_min_quantum) { #ifdef __linux__ - // Do not wait for the last quantum to avoid loss of accuracy - thread_ctrl::wait_for(remaining - ((remaining % host_min_quantum) + host_min_quantum), false); + thread_ctrl::wait_for(remaining, false); #else // Wait on multiple of min quantum for large durations to avoid overloading low thread cpus thread_ctrl::wait_for(remaining - (remaining % host_min_quantum), false); @@ -3198,7 +3366,9 @@ namespace rsx } } - fmt::append(result, "[%04x] %s\n", i, ensure(rsx::get_pretty_printing_function(i))(i, method_registers.registers[i])); + fmt::append(result, "[%04x] ", i); + ensure(rsx::get_pretty_printing_function(i))(result, i, method_registers.registers[i]); + result += '\n'; } } diff --git a/rpcs3/Emu/RSX/RSXThread.h b/rpcs3/Emu/RSX/RSXThread.h index dde75f3e53..a1be7e35ec 100644 --- a/rpcs3/Emu/RSX/RSXThread.h +++ b/rpcs3/Emu/RSX/RSXThread.h @@ -159,7 +159,6 @@ namespace rsx protected: std::array vertex_push_buffers; - rsx::simple_array element_push_buffer; s32 m_skip_frame_ctr = 0; bool skip_current_frame = false; @@ -173,6 +172,7 @@ namespace rsx std::unique_ptr fifo_ctrl; atomic_t rsx_thread_running{ false }; std::vector> dump_callstack_list() const override; + std::string dump_misc() const override; protected: FIFO::flattening_helper m_flattener; @@ -213,12 +213,13 @@ namespace rsx u32 last_known_code_start = 0; atomic_t external_interrupt_lock{ 0 }; atomic_t external_interrupt_ack{ false }; - atomic_t is_initialized{ false }; + atomic_t is_initialized{0}; + rsx::simple_array element_push_buffer; bool is_fifo_idle() const; void flush_fifo(); // Returns [count of found commands, PC of their start] - std::pair try_get_pc_of_x_cmds_backwards(u32 count, u32 get) const; + std::pair try_get_pc_of_x_cmds_backwards(s32 count, u32 get) const; void recover_fifo(u32 line = __builtin_LINE(), u32 col = __builtin_COLUMN(), diff --git a/rpcs3/Emu/RSX/VK/VKDraw.cpp b/rpcs3/Emu/RSX/VK/VKDraw.cpp index 3b9c11fb19..164e62e300 100644 --- a/rpcs3/Emu/RSX/VK/VKDraw.cpp +++ b/rpcs3/Emu/RSX/VK/VKDraw.cpp @@ -256,6 +256,7 @@ void VKGSRender::load_texture_env() auto sampler_state = static_cast(fs_sampler_state[i].get()); const auto& tex = rsx::method_registers.fragment_textures[i]; + const auto previous_format_class = fs_sampler_state[i]->format_class; if (m_samplers_dirty || m_textures_dirty[i] || !check_surface_cache_sampler(sampler_state, tex)) { @@ -276,6 +277,12 @@ void VKGSRender::load_texture_env() check_for_cyclic_refs |= true; } + if (!m_textures_dirty[i] && sampler_state->format_class != previous_format_class) + { + // Host details changed but RSX is not aware + m_graphics_state |= rsx::fragment_program_state_dirty; + } + bool replace = !fs_sampler_handles[i]; VkFilter mag_filter; vk::minification_filter min_filter; @@ -403,6 +410,7 @@ void VKGSRender::load_texture_env() auto sampler_state = static_cast(vs_sampler_state[i].get()); const auto& tex = rsx::method_registers.vertex_textures[i]; + const auto previous_format_class = sampler_state->format_class; if (m_samplers_dirty || m_vertex_textures_dirty[i] || !check_surface_cache_sampler(sampler_state, tex)) { @@ -423,6 +431,12 @@ void VKGSRender::load_texture_env() check_for_cyclic_refs |= true; } + if (!m_vertex_textures_dirty[i] && sampler_state->format_class != previous_format_class) + { + // Host details changed but RSX is not aware + m_graphics_state |= rsx::vertex_program_state_dirty; + } + bool replace = !vs_sampler_handles[i]; const VkBool32 unnormalized_coords = !!(tex.format() & CELL_GCM_TEXTURE_UN); const auto min_lod = tex.min_lod(); @@ -704,10 +718,19 @@ void VKGSRender::emit_geometry(u32 sub_index) // Rebase vertex bases instead of for (auto& info : m_vertex_layout.interleaved_blocks) { + info->vertex_range.second = 0; const auto vertex_base_offset = rsx::method_registers.vertex_data_base_offset(); info->real_offset_address = rsx::get_address(rsx::get_vertex_offset_from_base(vertex_base_offset, info->base_offset), info->memory_location); } } + else + { + // Discard cached results + for (auto& info : m_vertex_layout.interleaved_blocks) + { + info->vertex_range.second = 0; + } + } if (vertex_state && !m_vertex_layout.validate()) { @@ -1015,10 +1038,17 @@ void VKGSRender::end() // Now bind the shader resources. It is important that this takes place after the barriers so that we don't end up with stale descriptors for (int retry = 0; retry < 3; ++retry) { - if (m_samplers_dirty) [[ unlikely ]] + if (retry > 0 && m_samplers_dirty) [[ unlikely ]] { // Reload texture env if referenced objects were invalidated during OOM handling. load_texture_env(); + + // Do not trust fragment/vertex texture state after a texture state reset. + // NOTE: We don't want to change the program - it's too late for that now. We just need to harmonize the state. + m_graphics_state |= rsx::vertex_program_state_dirty | rsx::fragment_program_state_dirty; + get_current_fragment_program(fs_sampler_state); + get_current_vertex_program(vs_sampler_state); + m_graphics_state.clear(rsx::pipeline_state::invalidate_pipeline_bits); } const bool out_of_memory = m_shader_interpreter.is_interpreter(m_program) diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.cpp b/rpcs3/Emu/RSX/VK/VKGSRender.cpp index 56a38e6345..0cff656119 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.cpp +++ b/rpcs3/Emu/RSX/VK/VKGSRender.cpp @@ -529,7 +529,7 @@ VKGSRender::VKGSRender(utils::serial* ar) noexcept : GSRender(ar) } else { - rsx_log.fatal("Could not find a vulkan compatible GPU driver. Your GPU(s) may not support Vulkan, or you need to install the vulkan runtime and drivers"); + rsx_log.fatal("Could not find a Vulkan compatible GPU driver. Your GPU(s) may not support Vulkan, or you need to install the Vulkan runtime and drivers"); m_device = VK_NULL_HANDLE; return; } @@ -799,7 +799,7 @@ VKGSRender::VKGSRender(utils::serial* ar) noexcept : GSRender(ar) #endif if (backend_config.supports_passthrough_dma) { - rsx_log.error("AMDGPU kernel driver on linux and INTEL driver on some platforms cannot support passthrough DMA buffers."); + rsx_log.error("AMDGPU kernel driver on Linux and INTEL driver on some platforms cannot support passthrough DMA buffers."); backend_config.supports_passthrough_dma = false; } break; @@ -1442,7 +1442,7 @@ void VKGSRender::on_init_thread() { if (m_device == VK_NULL_HANDLE) { - fmt::throw_exception("No vulkan device was created"); + fmt::throw_exception("No Vulkan device was created"); } GSRender::on_init_thread(); @@ -3010,7 +3010,7 @@ void VKGSRender::begin_conditional_rendering(const std::vectorpending); + rsx_log.warning("Dubious query data pushed to cond render! Please report to developers(q.pending=%d)", sources.front()->pending); } rsx::thread::begin_conditional_rendering(sources); diff --git a/rpcs3/Emu/RSX/VK/VKPipelineCompiler.cpp b/rpcs3/Emu/RSX/VK/VKPipelineCompiler.cpp index f83d564393..6b6882f616 100644 --- a/rpcs3/Emu/RSX/VK/VKPipelineCompiler.cpp +++ b/rpcs3/Emu/RSX/VK/VKPipelineCompiler.cpp @@ -48,7 +48,7 @@ namespace vk } } - thread_ctrl::wait_on(m_work_queue, nullptr); + thread_ctrl::wait_on(m_work_queue); } } diff --git a/rpcs3/Emu/RSX/VK/VKPresent.cpp b/rpcs3/Emu/RSX/VK/VKPresent.cpp index 9ff2dee4f7..2f69505b96 100644 --- a/rpcs3/Emu/RSX/VK/VKPresent.cpp +++ b/rpcs3/Emu/RSX/VK/VKPresent.cpp @@ -712,7 +712,7 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info) } else { - m_frame->present_frame(sshot_frame, buffer_width, buffer_height, is_bgra); + m_frame->present_frame(sshot_frame, buffer_width * 4, buffer_width, buffer_height, is_bgra); } } } diff --git a/rpcs3/Emu/RSX/VK/VKTextureCache.cpp b/rpcs3/Emu/RSX/VK/VKTextureCache.cpp index b0344fc9e6..a22783addf 100644 --- a/rpcs3/Emu/RSX/VK/VKTextureCache.cpp +++ b/rpcs3/Emu/RSX/VK/VKTextureCache.cpp @@ -276,7 +276,32 @@ namespace vk for (const auto& section : sections_to_transfer) { if (!section.src) + { continue; + } + + // Generates a region to write data to the final destination + const auto get_output_region = [&](s32 in_x, s32 in_y, u32 w, u32 h, vk::image* data_src) + { + VkImageCopy copy_rgn = { + .srcSubresource = { data_src->aspect(), 0, 0, 1}, + .srcOffset = { in_x, in_y, 0 }, + .dstSubresource = { dst_aspect, section.level, 0, 1 }, + .dstOffset = { section.dst_x, section.dst_y, 0 }, + .extent = { w, h, 1 } + }; + + if (dst->info.imageType == VK_IMAGE_TYPE_3D) + { + copy_rgn.dstOffset.z = section.dst_z; + } + else + { + copy_rgn.dstSubresource.baseArrayLayer = section.dst_z; + } + + return copy_rgn; + }; const bool typeless = section.src->aspect() != dst_aspect || !formats_are_bitcast_compatible(dst, section.src); @@ -333,129 +358,55 @@ namespace vk src_image->change_layout(cmd, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); const areai src_rect = coordi{{ src_x, src_y }, { src_w, src_h }}; - const areai dst_rect = coordi{{ convert_x, src_y }, { convert_w, src_h }}; + const areai dst_rect = coordi{{ 0, 0 }, { convert_w, src_h }}; vk::copy_image_typeless(cmd, section.src, src_image, src_rect, dst_rect, 1); src_image->change_layout(cmd, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); - src_x = convert_x; + src_x = 0; + src_y = 0; src_w = convert_w; } ensure(src_image->current_layout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL || src_image->current_layout == VK_IMAGE_LAYOUT_GENERAL); + ensure(transform == rsx::surface_transform::identity); - // Final aspect mask of the 'final' transfer source - const auto new_src_aspect = src_image->aspect(); - - if (src_w == section.dst_w && src_h == section.dst_h && transform == rsx::surface_transform::identity) [[likely]] + if (src_w == section.dst_w && src_h == section.dst_h) [[likely]] { - VkImageCopy copy_rgn; - copy_rgn.srcOffset = { src_x, src_y, 0 }; - copy_rgn.dstOffset = { section.dst_x, section.dst_y, 0 }; - copy_rgn.dstSubresource = { dst_aspect, 0, 0, 1 }; - copy_rgn.srcSubresource = { new_src_aspect, 0, 0, 1 }; - copy_rgn.extent = { src_w, src_h, 1 }; - - if (dst->info.imageType == VK_IMAGE_TYPE_3D) - { - copy_rgn.dstOffset.z = section.dst_z; - } - else - { - copy_rgn.dstSubresource.baseArrayLayer = section.dst_z; - copy_rgn.dstSubresource.mipLevel = section.level; - } - + const auto copy_rgn = get_output_region(src_x, src_y, src_w, src_h, src_image); vkCmdCopyImage(cmd, src_image->value, src_image->current_layout, dst->value, dst->current_layout, 1, ©_rgn); } else { - ensure(section.dst_z == 0); - u16 dst_x = section.dst_x, dst_y = section.dst_y; - vk::image* _dst; + vk::image* _dst = dst; - if (src_image->info.format == dst->info.format && section.level == 0) [[likely]] + if (src_image->info.format != dst->info.format || section.level != 0 || section.dst_z != 0) [[ unlikely ]] { - _dst = dst; - } - else - { - // Either a bitcast is required or a scale+copy to mipmap level - _dst = vk::get_typeless_helper(src_image->format(), src_image->format_class(), dst->width(), dst->height() * 2); + // Either a bitcast is required or a scale+copy to mipmap level / layer + const u32 requested_width = dst->width(); + const u32 requested_height = src_y + src_h + section.dst_h; + _dst = vk::get_typeless_helper(src_image->format(), src_image->format_class(), requested_width, requested_height); _dst->change_layout(cmd, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); } - if (transform == rsx::surface_transform::identity) + if (_dst == src_image) { - vk::copy_scaled_image(cmd, src_image, _dst, - coordi{ { src_x, src_y }, { src_w, src_h } }, - coordi{ { section.dst_x, section.dst_y }, { section.dst_w, section.dst_h } }, - 1, src_image->format() == _dst->format(), - VK_FILTER_NEAREST); - } - else if (transform == rsx::surface_transform::argb_to_bgra) - { - VkBufferImageCopy copy{}; - copy.imageExtent = { src_w, src_h, 1 }; - copy.imageOffset = { src_x, src_y, 0 }; - copy.imageSubresource = { src_image->aspect(), 0, 0, 1 }; - - const auto mem_length = src_w * src_h * dst_bpp; - auto scratch_buf = vk::get_scratch_buffer(cmd, mem_length); - vkCmdCopyImageToBuffer(cmd, src_image->value, src_image->current_layout, scratch_buf->value, 1, ©); - - vk::insert_buffer_memory_barrier(cmd, scratch_buf->value, 0, mem_length, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, - VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT); - - auto shuffle_kernel = vk::get_compute_task(); - shuffle_kernel->run(cmd, scratch_buf, mem_length); - - vk::insert_buffer_memory_barrier(cmd, scratch_buf->value, 0, mem_length, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT); - - auto tmp = vk::get_typeless_helper(src_image->format(), src_image->format_class(), section.dst_x + section.dst_w, section.dst_y + section.dst_h); - tmp->change_layout(cmd, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - - copy.imageOffset = { 0, 0, 0 }; - vkCmdCopyBufferToImage(cmd, scratch_buf->value, tmp->value, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©); - + // Write-to-self situation. Account for the initial typeless copy. + ensure(dst != _dst); dst_x = 0; - dst_y = 0; - - if (src_w != section.dst_w || src_h != section.dst_h) - { - // Optionally scale if needed - if (tmp == _dst) [[unlikely]] - { - dst_y = src_h; - } - - vk::copy_scaled_image(cmd, tmp, _dst, - areai{ 0, 0, src_w, static_cast(src_h) }, - coordi{ { dst_x, dst_y }, { section.dst_w, section.dst_h } }, - 1, tmp->info.format == _dst->info.format, - VK_FILTER_NEAREST); - } - else - { - _dst = tmp; - } - } - else - { - fmt::throw_exception("Unreachable"); + dst_y = src_y + src_h; } + vk::copy_scaled_image(cmd, src_image, _dst, + coordi{ { src_x, src_y }, { src_w, src_h } }, + coordi{ { dst_x, dst_y }, { section.dst_w, section.dst_h } }, + 1, src_image->format() == _dst->format(), + VK_FILTER_NEAREST); + if (_dst != dst) [[unlikely]] { // Casting comes after the scaling! - VkImageCopy copy_rgn; - copy_rgn.srcOffset = { s32(dst_x), s32(dst_y), 0 }; - copy_rgn.dstOffset = { section.dst_x, section.dst_y, 0 }; - copy_rgn.dstSubresource = { dst_aspect, section.level, 0, 1 }; - copy_rgn.srcSubresource = { _dst->aspect(), 0, 0, 1 }; - copy_rgn.extent = { section.dst_w, section.dst_h, 1 }; - + const auto copy_rgn = get_output_region(dst_x, dst_y, section.dst_w, section.dst_h, _dst); _dst->change_layout(cmd, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); vkCmdCopyImage(cmd, _dst->value, _dst->current_layout, dst->value, dst->current_layout, 1, ©_rgn); } diff --git a/rpcs3/Emu/RSX/VK/vkutils/device.cpp b/rpcs3/Emu/RSX/VK/vkutils/device.cpp index 2b56591d58..885060b3e2 100644 --- a/rpcs3/Emu/RSX/VK/vkutils/device.cpp +++ b/rpcs3/Emu/RSX/VK/vkutils/device.cpp @@ -207,7 +207,7 @@ namespace vk get_physical_device_features(allow_extensions); get_physical_device_properties(allow_extensions); - rsx_log.always()("Found vulkan-compatible GPU: '%s' running on driver %s", get_name(), get_driver_version()); + rsx_log.always()("Found Vulkan-compatible GPU: '%s' running on driver %s", get_name(), get_driver_version()); if (get_driver_vendor() == driver_vendor::RADV && get_name().find("LLVM 8.0.0") != umax) { @@ -255,6 +255,11 @@ namespace vk { const auto gpu_name = get_name(); + if (gpu_name.find("RADV") != umax) + { + return driver_vendor::RADV; + } + if (gpu_name.find("Radeon") != umax) { return driver_vendor::AMD; @@ -265,11 +270,6 @@ namespace vk return driver_vendor::NVIDIA; } - if (gpu_name.find("RADV") != umax) - { - return driver_vendor::RADV; - } - if (gpu_name.find("Intel") != umax) { #ifdef _WIN32 diff --git a/rpcs3/Emu/RSX/display.h b/rpcs3/Emu/RSX/display.h index e6d5fa657e..8881275668 100644 --- a/rpcs3/Emu/RSX/display.h +++ b/rpcs3/Emu/RSX/display.h @@ -5,7 +5,7 @@ #elif defined(__APPLE__) // nothing #elif defined(HAVE_X11) -// Cannot include Xlib.h before Qt5 +// Cannot include Xlib.h before Qt // and we don't need all of Xlib anyway using Display = struct _XDisplay; using Window = unsigned long; diff --git a/rpcs3/Emu/RSX/gcm_enums.cpp b/rpcs3/Emu/RSX/gcm_enums.cpp index af0a8ab93b..b6a0292fd9 100644 --- a/rpcs3/Emu/RSX/gcm_enums.cpp +++ b/rpcs3/Emu/RSX/gcm_enums.cpp @@ -612,8 +612,21 @@ void fmt_class_string::format(std::string& out, u64 arg) namespace rsx { - std::string print_boolean(bool b) + enum class boolean_to_string_t : u8; +} + +template <> +void fmt_class_string::format(std::string& out, u64 arg) +{ + format_enum(out, arg, [](boolean_to_string_t value) { - return b ? "enabled" : "disabled"; - } -} // end namespace rsx + switch (value) + { + case boolean_to_string_t{+true}: return "true"; + case boolean_to_string_t{+false}: return "false"; + default: break; // TODO: This is technically unreachable but need needs to be reachable when value is not 1 or 0 + } + + return unknown; + }); +} diff --git a/rpcs3/Emu/RSX/gcm_printing.cpp b/rpcs3/Emu/RSX/gcm_printing.cpp index b0df9a18cb..d7e5e6c811 100644 --- a/rpcs3/Emu/RSX/gcm_printing.cpp +++ b/rpcs3/Emu/RSX/gcm_printing.cpp @@ -902,18 +902,20 @@ namespace #undef KEY_STR } -std::string rsx::get_method_name(const u32 id) +std::pair rsx::get_method_name(u32 id, std::string& string_name) { const auto found = methods_name.find(id); if (found != methods_name.end()) { - std::string prefix("CELL_GCM_"sv); - prefix.append(found->second.data(), found->second.size()); - return prefix; + constexpr std::string_view prefix = "CELL_GCM_"; + + return {prefix, found->second}; } - return fmt::format("Unnamed method 0x%04x", id); + string_name.clear(); + fmt::append(string_name, "Unnamed method 0x%04x", id); + return {}; } // Various parameter pretty printing function @@ -984,15 +986,15 @@ namespace namespace { template - std::string register_pretty_function(u32 /*id*/, u32 arg) + void register_pretty_function(std::string& out, u32 /*id*/, u32 arg) { - return rsx::registers_decoder::dump(arg); + rsx::registers_decoder::dump(out, arg); } template - std::array create_printing_table(std::integer_sequence) + std::array create_printing_table(std::integer_sequence) { - std::array result{}; + std::array result{}; ((result[opcode_list[Index * 5 + 0]] = ®ister_pretty_function, result[opcode_list[Index * 5 + 1]] = ®ister_pretty_function, @@ -1022,7 +1024,7 @@ namespace };*/ } -std::add_pointer_t rsx::get_pretty_printing_function(u32 id) +std::add_pointer_t rsx::get_pretty_printing_function(u32 id) { const auto found = id < printing_functions.size() ? printing_functions[id] : nullptr; @@ -1031,11 +1033,17 @@ std::add_pointer_t rsx::get_pretty_printing_function(u32 return found; } - return [](u32 id, u32 v) + return [](std::string& result, u32 id, u32 v) { - const std::string name = rsx::get_method_name(id); - const std::string_view view = name, prefix = "CELL_GCM_"sv; + std::string string_name; + const auto [name_prefix, name] = rsx::get_method_name(id, string_name); - return fmt::format("%s: 0x%08x", name.starts_with("CELL_GCM_"sv) ? view.substr(prefix.size()) : view, v); + if (!string_name.empty()) + { + fmt::append(result, "%s: 0x%08x", string_name, v); + return; + } + + fmt::append(result, "%s: 0x%08x", name, v); }; } diff --git a/rpcs3/Emu/RSX/gcm_printing.h b/rpcs3/Emu/RSX/gcm_printing.h index eb803f93b6..46c1b7ea1a 100644 --- a/rpcs3/Emu/RSX/gcm_printing.h +++ b/rpcs3/Emu/RSX/gcm_printing.h @@ -6,7 +6,7 @@ namespace rsx { - std::string get_method_name(u32 id); + std::pair get_method_name(u32 id, std::string& result_str); - std::add_pointer_t get_pretty_printing_function(u32 id); + std::add_pointer_t get_pretty_printing_function(u32 id); } diff --git a/rpcs3/Emu/RSX/rsx_decode.h b/rpcs3/Emu/RSX/rsx_decode.h index edbef500f6..10b58d59c4 100644 --- a/rpcs3/Emu/RSX/rsx_decode.h +++ b/rpcs3/Emu/RSX/rsx_decode.h @@ -10,7 +10,12 @@ namespace rsx { - std::string print_boolean(bool b); + enum class boolean_to_string_t : u8 {}; + + constexpr boolean_to_string_t print_boolean(bool b) + { + return boolean_to_string_t{static_cast(b)}; + } template struct registers_decoder @@ -30,12 +35,12 @@ struct registers_decoder { const u32 value; - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV406E Ref: 0x%08x", decoded.value); + fmt::append(out, "NV406E Ref: 0x%08x", decoded.value); } }; @@ -48,7 +53,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u16 origin_x() const { @@ -61,9 +66,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Viewport: x: %u width: %u", decoded.origin_x(), decoded.width()); + fmt::append(out, "Viewport: x: %u width: %u", decoded.origin_x(), decoded.width()); } }; @@ -76,7 +81,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u16 origin_y() const { @@ -89,9 +94,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Viewport: y: %u height: %u", decoded.origin_y(), decoded.height()); + fmt::append(out, "Viewport: y: %u height: %u", decoded.origin_y(), decoded.height()); } }; @@ -104,7 +109,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u16 origin_x() const { @@ -117,9 +122,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Scissor: x = " + std::to_string(decoded.origin_x()) + " width = " + std::to_string(decoded.width()); + fmt::append(out, "Scissor: x: %u width: %u", decoded.origin_x(), decoded.width()); } }; @@ -132,7 +137,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u16 origin_y() const { @@ -145,9 +150,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Scissor: y = " + std::to_string(decoded.origin_y()) + " height = " + std::to_string(decoded.height()); + fmt::append(out, "Scissor: y: %u height: %u", decoded.origin_y(), decoded.height()); } }; @@ -160,7 +165,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u16 origin_x() const { @@ -173,9 +178,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Surface: clip x = " + std::to_string(decoded.origin_x()) + " width = " + std::to_string(decoded.width()); + fmt::append(out, "Surface: clip x: %u width: %u", decoded.origin_x(), decoded.width()); } }; @@ -188,7 +193,7 @@ struct registers_decoder< NV4097_SET_SURFACE_CLIP_VERTICAL> u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u16 origin_y() const { @@ -201,9 +206,9 @@ struct registers_decoder< NV4097_SET_SURFACE_CLIP_VERTICAL> } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Surface: clip y = " + std::to_string(decoded.origin_y()) + " height = " + std::to_string(decoded.height()); + fmt::append(out, "Surface: clip y: %u height: %u", decoded.origin_y(), decoded.height()); } }; @@ -216,7 +221,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u16 origin_x() const { @@ -229,9 +234,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Clear: rect x = " + std::to_string(decoded.origin_x()) + " width = " + std::to_string(decoded.width()); + fmt::append(out, "Clear: rect x: %u width: %u", decoded.origin_x(), decoded.width()); } }; @@ -244,7 +249,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u16 origin_y() const { @@ -257,9 +262,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Clear: rect y = " + std::to_string(decoded.origin_y()) + " height = " + std::to_string(decoded.height()); + fmt::append(out, "Clear: rect y: %u height: %u", decoded.origin_y(), decoded.height()); } }; @@ -272,7 +277,7 @@ struct registers_decoder< NV3089_CLIP_POINT> u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u16 clip_x() const { @@ -285,9 +290,9 @@ struct registers_decoder< NV3089_CLIP_POINT> } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Blit engine: clip x = " + std::to_string(decoded.clip_x()) + " y = " + std::to_string(decoded.clip_y()); + fmt::append(out, "Blit engine: clip x: %u y: %u", decoded.clip_x(), decoded.clip_y()); } }; @@ -300,7 +305,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u16 clip_width() const { @@ -313,9 +318,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Blit engine: clip width = " + std::to_string(decoded.clip_width()) + " height = " + std::to_string(decoded.clip_height()); + fmt::append(out, "Blit engine: clip width: %u height: %u", decoded.clip_width(), decoded.clip_height()); } }; @@ -328,7 +333,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u16 x() const { @@ -341,9 +346,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Blit engine: output x = " + std::to_string(decoded.x()) + " y = " + std::to_string(decoded.y()); + fmt::append(out, "Blit engine: output x: %u y: %u", decoded.x(), decoded.y()); } }; @@ -356,7 +361,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u16 window_offset_x() const { @@ -369,9 +374,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Window: offset x: %u y: %u", decoded.window_offset_x(), decoded.window_offset_y()); + fmt::append(out, "Window: offset x: %u y: %u", decoded.window_offset_x(), decoded.window_offset_y()); } }; @@ -385,7 +390,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u16 width() const { @@ -398,9 +403,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Blit engine: output width = " + std::to_string(decoded.width()) + " height = " + std::to_string(decoded.height()); + fmt::append(out, "Blit engine: output width: %u height: %u", decoded.width(), decoded.height()); } }; @@ -413,7 +418,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u16 width() const { @@ -426,9 +431,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Blit engine: input width = " + std::to_string(decoded.width()) + " height = " + std::to_string(decoded.height()); + fmt::append(out, "Blit engine: input width: %u height: %u", decoded.width(), decoded.height()); } }; @@ -441,7 +446,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u16 alignment() const { @@ -454,9 +459,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Blit engine: output alignment = " + std::to_string(decoded.alignment()) + " pitch = " + std::to_string(decoded.pitch()); + fmt::append(out, "Blit engine: output alignment: %u pitch: %u", decoded.alignment(), decoded.pitch()); } }; @@ -469,7 +474,7 @@ struct registers_decoder< NV308A_POINT> u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u16 x() const { @@ -482,9 +487,9 @@ struct registers_decoder< NV308A_POINT> } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "NV308A: x = " + std::to_string(decoded.x()) + " y = " + std::to_string(decoded.y()); + fmt::append(out, "NV308A: x: %u y: %u", decoded.x(), decoded.y()); } }; @@ -497,7 +502,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 mask() const { @@ -505,10 +510,10 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - std::string result = "Transform program enabled inputs:"; - const std::string input_names[] = + out += "Transform program enabled inputs:"; + constexpr std::string_view input_names[] = { "in_pos", "in_weight", "in_normal", "in_diff_color", "in_spec_color", @@ -517,10 +522,15 @@ struct registers_decoder "in_tc0", "in_tc1", "in_tc2", "in_tc3", "in_tc4", "in_tc5", "in_tc6", "in_tc7" }; - for (unsigned i = 0; i < 16; i++) + + for (u32 i = 0; i < 16; i++) + { if (decoded.mask() & (1 << i)) - result += input_names[i] + " "; - return result; + { + out += ' '; + out += input_names[i]; + } + } } }; @@ -533,7 +543,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 frequency_divider_operation_mask() const { @@ -541,13 +551,26 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - std::string result = "Frequency divider: "; - for (unsigned i = 0; i < 16; i++) - if (decoded.frequency_divider_operation_mask() & (1 << i)) - result += std::to_string(i) + " "; - return result; + out += "Frequency divider:"; + + const u32 mask = decoded.frequency_divider_operation_mask(); + + if (!mask) + { + out += " (none)"; + return; + } + + for (u32 i = 0; i < 16; i++) + { + if (mask & (1 << i)) + { + out += ' '; + fmt::append(out, "%u", i); + } + } } }; @@ -568,9 +591,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Depth: test " + print_boolean(decoded.depth_test_enabled()); + fmt::append(out, "Depth: test %s", print_boolean(decoded.depth_test_enabled())); } }; @@ -591,9 +614,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Depth: write " + print_boolean(decoded.depth_write_enabled()); + fmt::append(out, "Depth: write: %s", print_boolean(decoded.depth_write_enabled())); } }; @@ -606,7 +629,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} bool depth_clip_enabled() const { @@ -624,11 +647,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Depth: clip_enabled " + print_boolean(decoded.depth_clip_enabled()) + - " clamp " + print_boolean(decoded.depth_clamp_enabled()) + - " ignore_w " + print_boolean(decoded.depth_clip_ignore_w()); + fmt::append(out, "Depth: clip_enabled: %s, clamp: %s, ignore_w: %s", print_boolean(decoded.depth_clip_enabled()), print_boolean(decoded.depth_clamp_enabled()) , print_boolean(decoded.depth_clip_ignore_w())); } }; @@ -649,9 +670,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Alpha: test " + print_boolean(decoded.alpha_test_enabled()); + fmt::append(out, "Alpha: test %s", print_boolean(decoded.alpha_test_enabled())); } }; @@ -672,9 +693,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Stencil: test " + print_boolean(decoded.stencil_test_enabled()); + fmt::append(out, "Stencil: test %s", print_boolean(decoded.stencil_test_enabled())); } }; @@ -695,9 +716,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Restart Index: " + print_boolean(decoded.restart_index_enabled()); + fmt::append(out, "Restart Index: %s", print_boolean(decoded.restart_index_enabled())); } }; @@ -718,9 +739,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Depth: bound test " + print_boolean(decoded.depth_bound_enabled()); + fmt::append(out, "Depth: bound test %s", print_boolean(decoded.depth_bound_enabled())); } }; @@ -741,9 +762,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Logic: " + print_boolean(decoded.logic_op_enabled()); + fmt::append(out, "Logic: %s", print_boolean(decoded.logic_op_enabled())); } }; @@ -764,9 +785,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Dither: " + print_boolean(decoded.dither_enabled()); + fmt::append(out, "Dither: %s", print_boolean(decoded.dither_enabled())); } }; @@ -787,9 +808,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Blend: " + print_boolean(decoded.blend_enabled()); + fmt::append(out, "Blend: %s", print_boolean(decoded.blend_enabled())); } }; @@ -810,9 +831,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Line: smooth " + print_boolean(decoded.line_smooth_enabled()); + fmt::append(out, "Line smooth: %s", print_boolean(decoded.line_smooth_enabled())); } }; @@ -833,9 +854,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Polygon: offset point " + print_boolean(decoded.poly_offset_point_enabled()); + fmt::append(out, "Polygon: offset point: %s", print_boolean(decoded.poly_offset_point_enabled())); } }; @@ -856,9 +877,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Polygon: offset line " + print_boolean(decoded.poly_offset_line_enabled()); + fmt::append(out, "Polygon: offset line: %s", print_boolean(decoded.poly_offset_line_enabled())); } }; @@ -879,9 +900,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Polygon: offset fill " + print_boolean(decoded.poly_offset_fill_enabled()); + fmt::append(out, "Polygon: offset fill: %s", print_boolean(decoded.poly_offset_fill_enabled())); } }; @@ -902,9 +923,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Cull face: " + print_boolean(decoded.cull_face_enabled()); + fmt::append(out, "Cull face: %s", print_boolean(decoded.cull_face_enabled())); } }; @@ -925,9 +946,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Polygon: smooth " + print_boolean(decoded.poly_smooth_enabled()); + fmt::append(out, "Polygon: smooth: %s", print_boolean(decoded.poly_smooth_enabled())); } }; @@ -948,9 +969,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Stencil: per side " + print_boolean(decoded.two_sided_stencil_test_enabled()); + fmt::append(out, "Stencil: per side: %s", print_boolean(decoded.two_sided_stencil_test_enabled())); } }; @@ -971,9 +992,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Light: per side " + print_boolean(decoded.two_sided_lighting_enabled()); + fmt::append(out, "Light: per side: %s", print_boolean(decoded.two_sided_lighting_enabled())); } }; @@ -986,7 +1007,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} f32 depth_bound_min() const { @@ -994,9 +1015,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Depth: bound min = " + std::to_string(decoded.depth_bound_min()); + fmt::append(out, "Depth: bound min: %g", decoded.depth_bound_min()); } }; @@ -1009,7 +1030,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} f32 depth_bound_max() const { @@ -1017,9 +1038,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Depth: bound max = " + std::to_string(decoded.depth_bound_max()); + fmt::append(out, "Depth: bound max: %g", decoded.depth_bound_max()); } }; @@ -1032,7 +1053,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} f32 fog_param_0() const { @@ -1040,9 +1061,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Fog: param 0 = " + std::to_string(decoded.fog_param_0()); + fmt::append(out, "Fog: param 0: %g", decoded.fog_param_0()); } }; @@ -1055,7 +1076,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} f32 fog_param_1() const { @@ -1063,9 +1084,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Fog: param 1 = " + std::to_string(decoded.fog_param_1()); + fmt::append(out, "Fog: param 1: %g", decoded.fog_param_1()); } }; @@ -1078,7 +1099,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} f32 clip_min() const { @@ -1086,9 +1107,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Depth: clip min = " + std::to_string(decoded.clip_min()); + fmt::append(out, "Depth: clip min: %g", decoded.clip_min()); } }; @@ -1101,7 +1122,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} f32 clip_max() const { @@ -1109,9 +1130,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Depth: clip max = " + std::to_string(decoded.clip_max()); + fmt::append(out, "Depth: clip max: %g", decoded.clip_max()); } }; @@ -1124,7 +1145,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} f32 polygon_offset_scale_factor() const { @@ -1132,9 +1153,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Polygon: offset scale = " + std::to_string(decoded.polygon_offset_scale_factor()); + fmt::append(out, "Polygon: offset scale: %g", decoded.polygon_offset_scale_factor()); } }; @@ -1147,7 +1168,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} f32 polygon_offset_scale_bias() const { @@ -1155,9 +1176,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Polygon: offset bias = " + std::to_string(decoded.polygon_offset_scale_bias()); + fmt::append(out, "Polygon: offset bias: %g", decoded.polygon_offset_scale_bias()); } }; @@ -1170,7 +1191,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} f32 viewport_scale_x() const { @@ -1178,9 +1199,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Viewport: scale x = " + std::to_string(decoded.viewport_scale_x()); + fmt::append(out, "Viewport: scale x: %g", decoded.viewport_scale_x()); } }; @@ -1193,7 +1214,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} f32 viewport_scale_y() const { @@ -1201,9 +1222,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Viewport: scale y = " + std::to_string(decoded.viewport_scale_y()); + fmt::append(out, "Viewport: scale y: %g", decoded.viewport_scale_y()); } }; @@ -1216,7 +1237,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} f32 viewport_scale_z() const { @@ -1224,9 +1245,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Viewport: scale z = " + std::to_string(decoded.viewport_scale_z()); + fmt::append(out, "Viewport: scale z: %g", decoded.viewport_scale_z()); } }; @@ -1239,7 +1260,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} f32 viewport_scale_w() const { @@ -1247,9 +1268,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Viewport: scale w = " + std::to_string(decoded.viewport_scale_w()); + fmt::append(out, "Viewport: scale w: %g", decoded.viewport_scale_w()); } }; @@ -1262,7 +1283,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} f32 viewport_offset_x() const { @@ -1270,9 +1291,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Viewport: offset x = " + std::to_string(decoded.viewport_offset_x()); + fmt::append(out, "Viewport: offset x: %g", decoded.viewport_offset_x()); } }; @@ -1285,7 +1306,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} f32 viewport_offset_y() const { @@ -1293,9 +1314,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Viewport: offset y = " + std::to_string(decoded.viewport_offset_y()); + fmt::append(out, "Viewport: offset y: %g", decoded.viewport_offset_y()); } }; @@ -1308,7 +1329,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} f32 viewport_offset_z() const { @@ -1316,9 +1337,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Viewport: offset z = " + std::to_string(decoded.viewport_offset_z()); + fmt::append(out, "Viewport: offset z: %g", decoded.viewport_offset_z()); } }; @@ -1331,7 +1352,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} f32 viewport_offset_w() const { @@ -1339,9 +1360,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Viewport: offset w = " + std::to_string(decoded.viewport_offset_w()); + fmt::append(out, "Viewport: offset w: %g", decoded.viewport_offset_w()); } }; @@ -1354,7 +1375,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 restart_index() const { @@ -1362,9 +1383,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Restart index: " + std::to_string(decoded.restart_index()); + fmt::append(out, "Restart index: %u", decoded.restart_index()); } }; @@ -1377,7 +1398,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 surface_a_offset() const { @@ -1385,9 +1406,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Surface: A offset 0x%x", decoded.surface_a_offset()); + fmt::append(out, "Surface: A offset 0x%x", decoded.surface_a_offset()); } }; @@ -1400,7 +1421,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 surface_b_offset() const { @@ -1408,9 +1429,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Surface: B offset 0x%x", decoded.surface_b_offset()); + fmt::append(out, "Surface: B offset 0x%x", decoded.surface_b_offset()); } }; @@ -1423,7 +1444,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 surface_c_offset() const { @@ -1431,9 +1452,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Surface: C offset 0x%x", decoded.surface_c_offset()); + fmt::append(out, "Surface: C offset 0x%x", decoded.surface_c_offset()); } }; @@ -1446,7 +1467,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 surface_d_offset() const { @@ -1454,9 +1475,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Surface: D offset 0x%x", decoded.surface_d_offset()); + fmt::append(out, "Surface: D offset 0x%x", decoded.surface_d_offset()); } }; @@ -1469,7 +1490,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 surface_a_pitch() const { @@ -1477,9 +1498,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Surface: A pitch " + std::to_string(decoded.surface_a_pitch()); + fmt::append(out, "Surface: A pitch: %u", decoded.surface_a_pitch()); } }; @@ -1492,7 +1513,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 surface_b_pitch() const { @@ -1500,9 +1521,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Surface: B pitch " + std::to_string(decoded.surface_b_pitch()); + fmt::append(out, "Surface: B pitch: %u", decoded.surface_b_pitch()); } }; @@ -1515,7 +1536,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 surface_c_pitch() const { @@ -1523,9 +1544,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Surface: C pitch " + std::to_string(decoded.surface_c_pitch()); + fmt::append(out, "Surface: C pitch: %u", decoded.surface_c_pitch()); } }; @@ -1538,7 +1559,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 surface_d_pitch() const { @@ -1546,9 +1567,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Surface: D pitch " + std::to_string(decoded.surface_d_pitch()); + fmt::append(out, "Surface: D pitch: %u", decoded.surface_d_pitch()); } }; @@ -1561,7 +1582,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 surface_z_offset() const { @@ -1569,9 +1590,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Surface: Z offset " + std::to_string(decoded.surface_z_offset()); + fmt::append(out, "Surface: Z offset: 0x%x", decoded.surface_z_offset()); } }; @@ -1584,7 +1605,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 surface_z_pitch() const { @@ -1592,9 +1613,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Surface: Z pitch " + std::to_string(decoded.surface_z_pitch()); + fmt::append(out, "Surface: Z pitch: %u", decoded.surface_z_pitch()); } }; @@ -1607,7 +1628,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 output_mask() const { @@ -1615,9 +1636,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - const std::string output_names[] = + static constexpr std::string_view output_names[] = { "diffuse_color", "specular_color", @@ -1642,11 +1663,19 @@ struct registers_decoder "tc6", "tc7" }; - std::string result = "Transform program outputs:"; - for (unsigned i = 0; i < 22; i++) - if (decoded.output_mask() & (1 << i)) - result += output_names[i] + " "; - return result; + + out += "Transform program outputs:"; + + const u32 mask = decoded.output_mask(); + + for (u32 i = 0; i < 22; i++) + { + if (mask & (1 << i)) + { + out += ' '; + out += output_names[i]; + } + } } }; @@ -1659,7 +1688,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 shader_ctrl() const { @@ -1667,12 +1696,11 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Shader control: raw_value =" + std::to_string(decoded.shader_ctrl()) + - " reg_count = " + std::to_string((decoded.shader_ctrl() >> 24) & 0xFF) + - ((decoded.shader_ctrl() & CELL_GCM_SHADER_CONTROL_DEPTH_EXPORT) ? " depth_replace " : "") + - ((decoded.shader_ctrl() & CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS) ? " 32b_exports " : ""); + fmt::append(out, "Shader control: raw_value: 0x%x reg_count: %u%s%s", + decoded.shader_ctrl(), ((decoded.shader_ctrl() >> 24) & 0xFF), ((decoded.shader_ctrl() & CELL_GCM_SHADER_CONTROL_DEPTH_EXPORT) ? " depth_replace" : ""), + ((decoded.shader_ctrl() & CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS) ? " 32b_exports" : "")); } }; @@ -1685,7 +1713,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} bool srgb_output_enabled() const { @@ -1693,9 +1721,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Shader packer control: srgb_enabled = " + std::to_string(decoded.srgb_output_enabled()); + fmt::append(out, "Shader packer control: srgb_enabled: %s", print_boolean(decoded.srgb_output_enabled())); } }; @@ -1708,7 +1736,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 vertex_data_base_offset() const { @@ -1716,9 +1744,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Vertex: base offset 0x%x", decoded.vertex_data_base_offset()); + fmt::append(out, "Vertex: base offset 0x%x", decoded.vertex_data_base_offset()); } }; @@ -1731,7 +1759,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 index_array_offset() const { @@ -1739,9 +1767,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Index: array offset 0x%x", decoded.index_array_offset()); + fmt::append(out, "Index: array offset 0x%x", decoded.index_array_offset()); } }; @@ -1754,7 +1782,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 vertex_data_base_index() const { @@ -1762,9 +1790,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Vertex: base index " + std::to_string(decoded.vertex_data_base_index()); + fmt::append(out, "Vertex: base index: %u", decoded.vertex_data_base_index()); } }; @@ -1777,7 +1805,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 shader_program_address() const { @@ -1785,10 +1813,10 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { const u32 address = decoded.shader_program_address(); - return fmt::format("Shader: %s, offset: 0x%x", CellGcmLocation{(address & 3) - 1}, address & ~3); + fmt::append(out, "Shader: %s, offset: 0x%x", CellGcmLocation{(address & 3) - 1}, address & ~3); } }; @@ -1801,7 +1829,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 transform_program_start() const { @@ -1809,9 +1837,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Transform program: start = " + std::to_string(decoded.transform_program_start()); + fmt::append(out, "Transform program: start: %u", decoded.transform_program_start()); } }; @@ -1824,7 +1852,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} CellGcmLocation context_dma() const { @@ -1832,9 +1860,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV406E semaphore: context: %s", decoded.context_dma()); + fmt::append(out, "NV406E semaphore: context: %s", decoded.context_dma()); } }; @@ -1848,7 +1876,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 semaphore_offset() const { @@ -1856,9 +1884,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV406E semaphore: offset: 0x%x", decoded.semaphore_offset()); + fmt::append(out, "NV406E semaphore: offset: 0x%x", decoded.semaphore_offset()); } }; @@ -1869,12 +1897,12 @@ struct registers_decoder { const u32 value; - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV406E semaphore: release: 0x%x", decoded.value); + fmt::append(out, "NV406E semaphore: release: 0x%x", decoded.value); } }; @@ -1885,12 +1913,12 @@ struct registers_decoder { const u32 value; - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV406E semaphore: acquire: 0x%x", decoded.value); + fmt::append(out, "NV406E semaphore: acquire: 0x%x", decoded.value); } }; @@ -1903,7 +1931,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} CellGcmLocation context_dma() const { @@ -1911,9 +1939,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV4097 semaphore: context: %s", decoded.context_dma()); + fmt::append(out, "NV4097 semaphore: context: %s", decoded.context_dma()); } }; @@ -1926,7 +1954,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 semaphore_offset() const { @@ -1934,9 +1962,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV4097 semaphore: offset: 0x%x", decoded.semaphore_offset()); + fmt::append(out, "NV4097 semaphore: offset: 0x%x", decoded.semaphore_offset()); } }; @@ -1949,7 +1977,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 input_offset() const { @@ -1957,9 +1985,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV3089: input offset: 0x%x", decoded.input_offset()); + fmt::append(out, "NV3089: input offset: 0x%x", decoded.input_offset()); } }; @@ -1972,7 +2000,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 output_offset() const { @@ -1980,9 +2008,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV3062: output offset: 0x%x", decoded.output_offset()); + fmt::append(out, "NV3062: output offset: 0x%x", decoded.output_offset()); } }; @@ -1995,7 +2023,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 offset() const { @@ -2003,9 +2031,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV309E: offset: 0x%x", decoded.offset()); + fmt::append(out, "NV309E: offset: 0x%x", decoded.offset()); } }; @@ -2018,7 +2046,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} // Convert signed fixed point 32-bit format f32 ds_dx() const @@ -2035,9 +2063,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "NV3089: dsdx = " + std::to_string(decoded.ds_dx()); + fmt::append(out, "NV3089: DS DX: %g", decoded.ds_dx()); } }; @@ -2050,7 +2078,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} // Convert signed fixed point 32-bit format f32 dt_dy() const @@ -2067,9 +2095,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "NV3089: dtdy = " + std::to_string(decoded.dt_dy()); + fmt::append(out, "NV3089: DT DY: %g", decoded.dt_dy()); } }; @@ -2082,7 +2110,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 input_pitch() const { @@ -2090,9 +2118,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "NV0039: input pitch = " + std::to_string(decoded.input_pitch()); + fmt::append(out, "NV0039: input pitch: %u", decoded.input_pitch()); } }; @@ -2105,7 +2133,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 output_pitch() const { @@ -2113,9 +2141,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "NV0039: output pitch = " + std::to_string(decoded.output_pitch()); + fmt::append(out, "NV0039: output pitch: %u", decoded.output_pitch()); } }; @@ -2128,7 +2156,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 input_line_length() const { @@ -2136,9 +2164,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "NV0039: line length input = " + std::to_string(decoded.input_line_length()); + fmt::append(out, "NV0039: line length input: %u", decoded.input_line_length()); } }; @@ -2151,7 +2179,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 line_count() const { @@ -2159,9 +2187,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "NV0039: line count = " + std::to_string(decoded.line_count()); + fmt::append(out, "NV0039: line count: %u", decoded.line_count()); } }; @@ -2174,7 +2202,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 output_offset() const { @@ -2182,9 +2210,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV0039: output offset: 0x%x", decoded.output_offset()); + fmt::append(out, "NV0039: output offset: 0x%x", decoded.output_offset()); } }; @@ -2197,7 +2225,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 input_offset() const { @@ -2205,9 +2233,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV0039: input offset: 00x%x", decoded.input_offset()); + fmt::append(out, "NV0039: input offset: 00x%x", decoded.input_offset()); } }; @@ -2220,7 +2248,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto depth_func() const { @@ -2228,9 +2256,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Depth: compare_function: %s", decoded.depth_func()); + fmt::append(out, "Depth: compare_function: %s", decoded.depth_func()); } }; @@ -2243,7 +2271,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto stencil_func() const { @@ -2251,9 +2279,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Stencil: (front) compare_function: %s", decoded.stencil_func()); + fmt::append(out, "Stencil: (front) compare_function: %s", decoded.stencil_func()); } }; @@ -2266,7 +2294,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto back_stencil_func() const { @@ -2274,9 +2302,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Stencil: back compare_function: %s", decoded.back_stencil_func()); + fmt::append(out, "Stencil: back compare_function: %s", decoded.back_stencil_func()); } }; @@ -2289,7 +2317,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto alpha_func() const { @@ -2297,9 +2325,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Alpha: compare_function: %s", decoded.alpha_func()); + fmt::append(out, "Alpha: compare_function: %s", decoded.alpha_func()); } }; @@ -2312,7 +2340,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto fail() const { @@ -2320,9 +2348,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Stencil: (front) fail op: %s", decoded.fail()); + fmt::append(out, "Stencil: (front) fail op: %s", decoded.fail()); } }; @@ -2335,7 +2363,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto zfail() const { @@ -2343,9 +2371,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Stencil: (front) zfail op: %s", decoded.zfail()); + fmt::append(out, "Stencil: (front) zfail op: %s", decoded.zfail()); } }; @@ -2358,7 +2386,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto zpass() const { @@ -2366,9 +2394,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Stencil: (front) zpass op: %s", decoded.zpass()); + fmt::append(out, "Stencil: (front) zpass op: %s", decoded.zpass()); } }; @@ -2381,7 +2409,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto back_fail() const { @@ -2389,9 +2417,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Stencil: (back) fail op: %s", decoded.back_fail()); + fmt::append(out, "Stencil: (back) fail op: %s", decoded.back_fail()); } }; @@ -2404,7 +2432,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto back_zfail() const { @@ -2412,9 +2440,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Stencil: (back) zfail op: %s", decoded.back_zfail()); + fmt::append(out, "Stencil: (back) zfail op: %s", decoded.back_zfail()); } }; @@ -2427,7 +2455,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto back_zpass() const { @@ -2435,9 +2463,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Stencil: (back) zpass op: %s", decoded.back_zpass()); + fmt::append(out, "Stencil: (back) zpass op: %s", decoded.back_zpass()); } }; @@ -2450,7 +2478,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u8 stencil_func_ref() const { @@ -2458,9 +2486,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Stencil: (front) func ref = " + std::to_string(decoded.stencil_func_ref()); + fmt::append(out, "Stencil: (front) func ref: %u", decoded.stencil_func_ref()); } }; @@ -2473,7 +2501,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u8 back_stencil_func_ref() const { @@ -2481,9 +2509,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Stencil: (back) func ref = " + std::to_string(decoded.back_stencil_func_ref()); + fmt::append(out, "Stencil: (back) func ref: %u", decoded.back_stencil_func_ref()); } }; @@ -2496,7 +2524,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u8 stencil_func_mask() const { @@ -2504,9 +2532,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Stencil: (front) func mask = " + std::to_string(decoded.stencil_func_mask()); + fmt::append(out, "Stencil: (front) func mask: %u", decoded.stencil_func_mask()); } }; @@ -2519,7 +2547,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u8 back_stencil_func_mask() const { @@ -2527,9 +2555,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Stencil: (back) func mask = " + std::to_string(decoded.back_stencil_func_mask()); + fmt::append(out, "Stencil: (back) func mask: %u", decoded.back_stencil_func_mask()); } }; @@ -2542,7 +2570,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} f32 alpha_ref8() const { @@ -2560,10 +2588,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Alpha: ref unorm8 = " + std::to_string(decoded.alpha_ref8()) + - " f16 = " + std::to_string(decoded.alpha_ref16()); + fmt::append(out, "Alpha: ref unorm8: %g, f16: %g", decoded.alpha_ref8(), decoded.alpha_ref16()); } }; @@ -2576,7 +2603,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u8 blue() const { return bf_decoder<0, 8>(value); } u8 green() const { return bf_decoder<8, 8>(value); } @@ -2584,12 +2611,9 @@ struct registers_decoder u8 alpha() const { return bf_decoder<24, 8>(value); } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Clear: R = " + std::to_string(decoded.red()) + - " G = " + std::to_string(decoded.green()) + - " B = " + std::to_string(decoded.blue()) + - " A = " + std::to_string(decoded.alpha()); + fmt::append(out, "Clear: R = %u G = %u B = %u A = %u", decoded.red(), decoded.green(), decoded.blue(), decoded.alpha()); } }; @@ -2602,7 +2626,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u8 stencil_mask() const { @@ -2610,9 +2634,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Stencil: (front) mask = " + std::to_string(decoded.stencil_mask()); + fmt::append(out, "Stencil: (front) mask: %u", decoded.stencil_mask()); } }; @@ -2625,7 +2649,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u8 back_stencil_mask() const { @@ -2633,9 +2657,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Stencil: (back) mask = " + std::to_string(decoded.back_stencil_mask()); + fmt::append(out, "Stencil: (back) mask: %u", decoded.back_stencil_mask()); } }; @@ -2648,7 +2672,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto logic_operation() const { @@ -2656,9 +2680,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Logic: op: %s", decoded.logic_operation()); + fmt::append(out, "Logic: op: %s", decoded.logic_operation()); } }; @@ -2671,7 +2695,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto front_face_mode() const { @@ -2679,9 +2703,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Front Face: %s", decoded.front_face_mode()); + fmt::append(out, "Front Face: %s", decoded.front_face_mode()); } }; @@ -2695,7 +2719,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} cull_face cull_face_mode() const { @@ -2703,9 +2727,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Cull Face: %s", decoded.cull_face_mode()); + fmt::append(out, "Cull Face: %s", decoded.cull_face_mode()); } }; @@ -2718,7 +2742,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto target() const { @@ -2726,9 +2750,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Surface: Color target(s): %s", decoded.target()); + fmt::append(out, "Surface: Color target(s): %s", decoded.target()); } }; @@ -2741,7 +2765,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto fog_equation() const { @@ -2749,9 +2773,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Fog: %s", decoded.fog_equation()); + fmt::append(out, "Fog: %s", decoded.fog_equation()); } }; @@ -2764,7 +2788,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto primitive() const { @@ -2772,9 +2796,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Primitive: %s", decoded.primitive()); + fmt::append(out, "Primitive: %s", decoded.primitive()); } }; @@ -2787,7 +2811,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto transfer_op() const { @@ -2795,9 +2819,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV3089: op: %s", decoded.transfer_op()); + fmt::append(out, "NV3089: op: %s", decoded.transfer_op()); } }; @@ -2810,7 +2834,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto transfer_source_fmt() const { @@ -2818,9 +2842,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV3089: source fmt: %s", decoded.transfer_source_fmt()); + fmt::append(out, "NV3089: source fmt: %s", decoded.transfer_source_fmt()); } }; @@ -2833,7 +2857,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto ctx_surface() const { @@ -2841,9 +2865,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV3089: context surface: %s", decoded.ctx_surface()); + fmt::append(out, "NV3089: context surface: %s", decoded.ctx_surface()); } }; @@ -2856,7 +2880,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto transfer_dest_fmt() const { @@ -2864,9 +2888,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV3062: output fmt: %s", decoded.transfer_dest_fmt()); + fmt::append(out, "NV3062: output fmt: %s", decoded.transfer_dest_fmt()); } }; @@ -2888,7 +2912,7 @@ struct registers_decoder return bf_decoder<16, 16>(value); } public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto blend_rgb() const { @@ -2901,9 +2925,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Blend: equation rgb: %s a: %s", decoded.blend_rgb(), decoded.blend_a()); + fmt::append(out, "Blend: equation rgb: %s a: %s", decoded.blend_rgb(), decoded.blend_a()); } }; @@ -2925,7 +2949,7 @@ struct registers_decoder return bf_decoder<16, 16>(value); } public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto src_blend_rgb() const { @@ -2938,9 +2962,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Blend: sfactor rgb: %s a: %s", decoded.src_blend_rgb(), decoded.src_blend_a()); + fmt::append(out, "Blend: sfactor rgb: %s a: %s", decoded.src_blend_rgb(), decoded.src_blend_a()); } }; @@ -2962,7 +2986,7 @@ struct registers_decoder return bf_decoder<16, 16>(value); } public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto dst_blend_rgb() const { @@ -2975,9 +2999,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Blend: dfactor rgb: %s a: %s", decoded.dst_blend_rgb(), decoded.dst_blend_a()); + fmt::append(out, "Blend: dfactor rgb: %s a: %s", decoded.dst_blend_rgb(), decoded.dst_blend_a()); } }; @@ -2990,7 +3014,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} bool color_b() const { @@ -3016,14 +3040,23 @@ struct registers_decoder { return value != 0; } + + u32 is_invalid() const + { + return (value & 0xfefefefe) ? value : 0; + } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Surface: color mask A = " + print_boolean(decoded.color_a()) + - " R = " + print_boolean(decoded.color_r()) + - " G = " + print_boolean(decoded.color_g()) + - " B = " + print_boolean(decoded.color_b()); + if (u32 invalid_value = decoded.is_invalid()) + { + fmt::append(out, "Surface: color mask: invalid = 0x%08x", invalid_value); + return; + } + + fmt::append(out, "Surface: color mask A = %s R = %s G = %s B = %s" + , print_boolean(decoded.color_a()), print_boolean(decoded.color_r()), print_boolean(decoded.color_g()), print_boolean(decoded.color_b())); } }; @@ -3036,7 +3069,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} bool color_b(int index) const { @@ -3064,20 +3097,19 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - std::string result; + out += "Color Mask MRT:\n"; + for (int index = 1; index < 4; ++index) { - result += fmt::format("Surface[%d]: A:%d R:%d G:%d B:%d\n", + fmt::append(out, "Surface[%d]: A:%d R:%d G:%d B:%d\n", index, decoded.color_a(index), decoded.color_r(index), decoded.color_g(index), decoded.color_b(index)); } - - return "Color Mask MRT:\n" + result; } }; @@ -3093,7 +3125,7 @@ struct registers_decoder u8 window_shader_pixel_center_raw() const { return bf_decoder<16, 4>(value); } public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto window_shader_origin() const { @@ -3111,9 +3143,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Viewport: height: %u origin: %s pixel center: %s", decoded.window_shader_height() + fmt::append(out, "Viewport: height: %u origin: %s pixel center: %s", decoded.window_shader_height() , decoded.window_shader_origin(), decoded.window_shader_pixel_center()); } }; @@ -3127,7 +3159,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} bool blend_surface_b() const { @@ -3145,11 +3177,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Blend: mrt1 = " + print_boolean(decoded.blend_surface_b()) + - " mrt2 = " + print_boolean(decoded.blend_surface_c()) + - " mrt3 = " + print_boolean(decoded.blend_surface_d()); + fmt::append(out, "Blend: mrt1 = %s, mrt2 = %s, mrt3 = %s", print_boolean(decoded.blend_surface_b()), print_boolean(decoded.blend_surface_c()), print_boolean(decoded.blend_surface_d())); } }; @@ -3170,7 +3200,7 @@ struct registers_decoder u8 clip_plane5_raw() const { return bf_decoder<20, 4>(value); } public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto clip_plane0() const { @@ -3203,9 +3233,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("User clip: UC0: %s UC1: %s UC2: %s UC3: %s UC4: %s" + fmt::append(out, "User clip: UC0: %s UC1: %s UC2: %s UC3: %s UC4: %s" , decoded.clip_plane0() , decoded.clip_plane1() , decoded.clip_plane2() @@ -3224,7 +3254,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} f32 line_width() const { @@ -3232,9 +3262,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Line width: " + std::to_string(decoded.line_width()); + fmt::append(out, "Line width: %g", decoded.line_width()); } }; @@ -3247,7 +3277,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} f32 point_size() const { @@ -3255,9 +3285,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Point size: " + std::to_string(decoded.point_size()); + fmt::append(out, "Point size: %g", decoded.point_size()); } }; @@ -3270,7 +3300,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} bool enabled() const { @@ -3283,10 +3313,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Point sprite: enabled = " + print_boolean(decoded.enabled()) + - "override mask = " + fmt::format("0x%x", decoded.texcoord_mask()); + fmt::append(out, "Point sprite: enabled = %s, override mask = 0x%x", print_boolean(decoded.enabled()), decoded.texcoord_mask()); } }; @@ -3304,7 +3333,7 @@ struct registers_decoder u8 antialias_raw() const { return bf_decoder<12, 4>(value); } public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto color_fmt() const { @@ -3342,9 +3371,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Surface: Color format: %s DepthStencil format: %s Anti aliasing: %s w: %u h: %u", decoded.color_fmt() + fmt::append(out, "Surface: Color format: %s DepthStencil format: %s Anti aliasing: %s w: %u h: %u", decoded.color_fmt() , decoded.depth_fmt(), decoded.antialias(), decoded.log2width(), decoded.log2height()); } }; @@ -3361,7 +3390,7 @@ struct registers_decoder u32 clear_z24() const { return bf_decoder<8, 24>(value); } public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u8 clear_stencil() const { @@ -3377,11 +3406,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Clear: Z24 = " + std::to_string(decoded.clear_z(true)) + - " z16 = " + std::to_string(decoded.clear_z(false)) + - " Stencil = " + std::to_string(decoded.clear_stencil()); + fmt::append(out, "Clear: Z24 = %u, z16 = %u, Stencil = %u", decoded.clear_z(true), decoded.clear_z(false), decoded.clear_stencil()); } }; @@ -3396,7 +3423,7 @@ struct registers_decoder u8 type_raw() const { return bf_decoder<4, 8>(value); } public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} CellGcmLocation index_dma() const { @@ -3410,9 +3437,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Index: type: %s dma: %s", decoded.type(), decoded.index_dma()); + fmt::append(out, "Index: type: %s dma: %s", decoded.type(), decoded.index_dma()); } }; @@ -3425,7 +3452,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} CellGcmLocation dma_surface_a() const { @@ -3433,9 +3460,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Surface: A DMA: %s", decoded.dma_surface_a()); + fmt::append(out, "Surface: A DMA: %s", decoded.dma_surface_a()); } }; @@ -3448,7 +3475,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} CellGcmLocation dma_surface_b() const { @@ -3456,9 +3483,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Surface: B DMA: %s", decoded.dma_surface_b()); + fmt::append(out, "Surface: B DMA: %s", decoded.dma_surface_b()); } }; @@ -3471,7 +3498,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} CellGcmLocation dma_surface_c() const { @@ -3479,9 +3506,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Surface: C DMA: %s", decoded.dma_surface_c()); + fmt::append(out, "Surface: C DMA: %s", decoded.dma_surface_c()); } }; @@ -3494,7 +3521,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} CellGcmLocation dma_surface_d() const { @@ -3502,9 +3529,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Surface: D DMA: %s", decoded.dma_surface_d()); + fmt::append(out, "Surface: D DMA: %s", decoded.dma_surface_d()); } }; @@ -3517,7 +3544,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} CellGcmLocation dma_surface_z() const { @@ -3525,9 +3552,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Surface: Z DMA: %s", decoded.dma_surface_z()); + fmt::append(out, "Surface: Z DMA: %s", decoded.dma_surface_z()); } }; @@ -3540,7 +3567,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} CellGcmLocation context_dma() const { @@ -3548,9 +3575,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV3089: input DMA: %s", decoded.context_dma()); + fmt::append(out, "NV3089: input DMA: %s", decoded.context_dma()); } }; @@ -3563,7 +3590,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} CellGcmLocation output_dma() const { @@ -3571,9 +3598,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV3062: output DMA: %s", decoded.output_dma()); + fmt::append(out, "NV3062: output DMA: %s", decoded.output_dma()); } }; @@ -3586,7 +3613,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} CellGcmLocation context_dma() const { @@ -3594,9 +3621,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV309E: output DMA: %s", decoded.context_dma()); + fmt::append(out, "NV309E: output DMA: %s", decoded.context_dma()); } }; @@ -3609,7 +3636,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} CellGcmLocation output_dma() const { @@ -3617,9 +3644,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV0039: output DMA: %s", decoded.output_dma()); + fmt::append(out, "NV0039: output DMA: %s", decoded.output_dma()); } }; @@ -3632,7 +3659,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} CellGcmLocation input_dma() const { @@ -3640,9 +3667,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV0039: input DMA: %s", decoded.input_dma()); + fmt::append(out, "NV0039: input DMA: %s", decoded.input_dma()); } }; @@ -3655,7 +3682,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto context_dma_report() const { @@ -3663,9 +3690,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("REPORT: context DMA: %s", decoded.context_dma_report()); + fmt::append(out, "REPORT: context DMA: %s", decoded.context_dma_report()); } }; @@ -3678,7 +3705,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} CellGcmLocation context_dma_notify() const { @@ -3686,9 +3713,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NOTIFY: context DMA: %s, index: %d", decoded.context_dma_notify(), (decoded.context_dma_notify() & 7) ^ 7); + fmt::append(out, "NOTIFY: context DMA: %s, index: %u", decoded.context_dma_notify(), (decoded.context_dma_notify() & 7) ^ 7); } }; @@ -3704,7 +3731,7 @@ struct registers_decoder u8 transfer_interpolator_raw() const { return bf_decoder<24, 8>(value); } public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u16 format() const { @@ -3722,9 +3749,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV3089: input fmt: %u origin: %s interp: %s", decoded.format() + fmt::append(out, "NV3089: input fmt: %u origin: %s interp: %s", decoded.format() , decoded.transfer_origin(), decoded.transfer_interpolator()); } }; @@ -3739,7 +3766,7 @@ struct registers_decoder u32 transfer_destination_fmt() const { return bf_decoder<0, 16, u32>(value); } public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto format() const { @@ -3757,9 +3784,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("NV309E: output fmt: %s log2-width: %u log2-height: %u", decoded.format(), + fmt::append(out, "NV309E: output fmt: %s log2-width: %u log2-height: %u", decoded.format(), decoded.sw_width_log2(), decoded.sw_height_log2()); } }; @@ -3773,7 +3800,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u8 input_format() const { @@ -3790,10 +3817,9 @@ struct registers_decoder return std::make_tuple(static_cast(value & 0xff), static_cast(value >> 8)); } - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "NV0039: input format = " + std::to_string(decoded.input_format()) + - " output format = " + std::to_string(decoded.output_format()); + fmt::append(out, "NV0039: input format = %u, output format = %u", decoded.input_format(), decoded.output_format()); } }; @@ -3806,7 +3832,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u16 blue() const { @@ -3819,10 +3845,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Blend color: 16b BA = " + std::to_string(decoded.blue()) + - ", " + std::to_string(decoded.alpha()); + fmt::append(out, "Blend color: 16b BA = %u, %u", decoded.blue(), decoded.alpha()); } }; @@ -3835,7 +3860,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u16 red16() const { @@ -3868,11 +3893,10 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Blend color: 8b BGRA = " + - std::to_string(decoded.blue8()) + ", " + std::to_string(decoded.green8()) + ", " + std::to_string(decoded.red8()) + ", " + std::to_string(decoded.alpha8()) + - " 16b RG = " + std::to_string(decoded.red16()) + ", " + std::to_string(decoded.green16()); + fmt::append(out, "Blend color: 8b BGRA = %u, %u, %u, %u 16b RG = %u , %u" + , decoded.blue8(), decoded.green8(), decoded.red8(), decoded.alpha8(), decoded.red16(), decoded.green16()); } }; @@ -3895,7 +3919,7 @@ struct registers_decoder } public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} // x and y given as 16 bit fixed point @@ -3910,10 +3934,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "NV3089: in x = " + std::to_string(decoded.x()) + - " y = " + std::to_string(decoded.y()); + fmt::append(out, "NV3089: in x = %u y = %u", decoded.x(), decoded.y()); } }; @@ -3925,9 +3948,9 @@ struct registers_decoder decoded_type(u32) {} }; - static std::string dump(u32) + static void dump(std::string& out, u32) { - return "(nop)"; + out += "(nop)"; } }; @@ -3939,9 +3962,9 @@ struct registers_decoder decoded_type(u32) {} }; - static std::string dump(u32) + static void dump(std::string& out, u32) { - return "(invalidate vertex cache file)"; + out += "(invalidate vertex cache file)"; } }; @@ -3953,9 +3976,9 @@ struct registers_decoder decoded_type(u32) {} }; - static std::string dump(u32) + static void dump(std::string& out, u32) { - return "(invalidate vertex file)"; + out += "(invalidate vertex file)"; } }; @@ -3968,7 +3991,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} bool msaa_enabled() const { @@ -3991,12 +4014,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Anti_aliasing: " + print_boolean(decoded.msaa_enabled()) + - " alpha_to_coverage = " + print_boolean(decoded.msaa_alpha_to_coverage()) + - " alpha_to_one = " + print_boolean(decoded.msaa_alpha_to_one()) + - " sample_mask = " + std::to_string(decoded.msaa_sample_mask()); + fmt::append(out, "Anti_aliasing: %s alpha_to_coverage: %s alpha_to_one: %s sample_mask: %u", print_boolean(decoded.msaa_enabled()), print_boolean(decoded.msaa_alpha_to_coverage()), print_boolean(decoded.msaa_alpha_to_one()), decoded.msaa_sample_mask()); } }; @@ -4009,7 +4029,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto shading() const { @@ -4017,9 +4037,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Shading mode: %s", decoded.shading()); + fmt::append(out, "Shading mode: %s", decoded.shading()); } }; @@ -4032,7 +4052,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto front_polygon_mode() const { @@ -4040,9 +4060,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Front polygon mode: %s", decoded.front_polygon_mode()); + fmt::append(out, "Front polygon mode: %s", decoded.front_polygon_mode()); } }; @@ -4055,7 +4075,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} auto back_polygon_mode() const { @@ -4063,9 +4083,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("back polygon mode: %s", decoded.back_polygon_mode()); + fmt::append(out, "back polygon mode: %s", decoded.back_polygon_mode()); } }; @@ -4078,7 +4098,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 transform_constant_load() const { @@ -4086,9 +4106,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Transform constant load: %u", decoded.transform_constant_load()); + fmt::append(out, "Transform constant load: %u", decoded.transform_constant_load()); } }; @@ -4101,7 +4121,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} bool enabled() const { @@ -4109,9 +4129,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("polygon_stipple: %s", print_boolean(decoded.enabled())); + fmt::append(out, "polygon_stipple: %s", print_boolean(decoded.enabled())); } }; @@ -4124,7 +4144,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} bool enabled() const { @@ -4132,9 +4152,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("ZCULL: %s", print_boolean(decoded.enabled())); + fmt::append(out, "ZCULL: %s", print_boolean(decoded.enabled())); } }; @@ -4147,7 +4167,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} bool enabled() const { @@ -4155,9 +4175,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("ZCULL: stats %s", print_boolean(decoded.enabled())); + fmt::append(out, "ZCULL: stats %s", print_boolean(decoded.enabled())); } }; @@ -4170,7 +4190,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} bool enabled() const { @@ -4178,9 +4198,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("ZCULL: pixel count %s", print_boolean(decoded.enabled())); + fmt::append(out, "ZCULL: pixel count %s", print_boolean(decoded.enabled())); } }; @@ -4232,7 +4252,7 @@ struct transform_constant_helper u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} f32 constant_value() const { @@ -4243,7 +4263,7 @@ struct transform_constant_helper static constexpr u32 reg = index / 4; static constexpr u8 subreg = index % 4; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { auto get_subreg_name = [](u8 subreg) -> std::string_view { @@ -4253,7 +4273,7 @@ struct transform_constant_helper "w"sv; }; - return fmt::format("TransformConstant[%u].%s: %g (0x%08x)", reg, get_subreg_name(subreg), decoded.constant_value(), std::bit_cast(decoded.constant_value())); + fmt::append(out, "TransformConstant[%u].%s: %g (0x%08x)", reg, get_subreg_name(subreg), decoded.constant_value(), std::bit_cast(decoded.constant_value())); } }; @@ -4269,12 +4289,12 @@ struct transform_program_helper { const u32 value; - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Transform Program (%u): 0x%08x", index, decoded.value); + fmt::append(out, "Transform Program (%u): 0x%08x", index, decoded.value); } }; @@ -4287,7 +4307,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 transform_program_load() const { @@ -4295,9 +4315,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Transform Program Load: %u", decoded.transform_program_load()); + fmt::append(out, "Transform Program Load: %u", decoded.transform_program_load()); } }; @@ -4315,7 +4335,7 @@ struct registers_decoder } public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 start() const { @@ -4328,10 +4348,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Draw vertexes range [" + std::to_string(decoded.start()) + ", " + - std::to_string(decoded.start() + decoded.count()) + "]"; + fmt::append(out, "Draw vertexes range [%u, %u]", decoded.start(), decoded.start() + decoded.count()); } }; @@ -4344,7 +4363,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 start() const { @@ -4357,10 +4376,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "Draw vertexes range [IdxArray[" + std::to_string(decoded.start()) + - "], IdxArray[" + std::to_string(decoded.start() + decoded.count()) + "}]"; + fmt::append(out, "Draw vertexes range {IdxArray[%u], IdxArray[%u]}", decoded.start(), decoded.start() + decoded.count()); } }; @@ -4373,7 +4391,7 @@ struct registers_decoder u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} bool depth_float() const { @@ -4381,9 +4399,9 @@ struct registers_decoder } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Depth float: %s", print_boolean(decoded.depth_float())); + fmt::append(out, "Depth float: %s", print_boolean(decoded.depth_float())); } }; @@ -4404,7 +4422,7 @@ struct vertex_array_helper return bf_decoder<0, 3>(value); } public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u16 frequency() const { @@ -4427,12 +4445,9 @@ struct vertex_array_helper } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - if (decoded.size() == 0) - return fmt::format("Vertex Data Array %u (disabled)", index); - - auto print_vertex_attribute_format = [](rsx::vertex_base_type type) -> std::string + auto print_vertex_attribute_format = [](rsx::vertex_base_type type) -> std::string_view { switch (type) { @@ -4447,10 +4462,7 @@ struct vertex_array_helper fmt::throw_exception("Unexpected enum found"); }; - return "Vertex array " + std::to_string(index) + ": Type = " + print_vertex_attribute_format(decoded.type()) + - " size = " + std::to_string(decoded.size()) + - " stride = " + std::to_string(decoded.stride()) + - " frequency = " + std::to_string(decoded.frequency()); + fmt::append(out, "Vertex Data Array %u%s: Type: %s, size: %u, stride: %u, frequency: %u", index, decoded.size() ? "" : " (disabled)", print_vertex_attribute_format(decoded.type()), decoded.size(), decoded.stride(), decoded.frequency()); } }; @@ -4467,7 +4479,7 @@ struct vertex_array_offset_helper private: u32 value; public: - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 offset() const { @@ -4475,9 +4487,9 @@ struct vertex_array_offset_helper } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Vertex array %u: Offset: 0x%x", index, decoded.offset()); + fmt::append(out, "Vertex Array %u: Offset: 0x%x", index, decoded.offset()); } }; @@ -4494,12 +4506,12 @@ struct register_vertex_printer { static std::string type() { - return "float" + std::to_string(count); + return fmt::format("float%u", count); } static std::string value(u32 v) { - return std::to_string(std::bit_cast(v)); + return fmt::format("%g", std::bit_cast(v)); } }; @@ -4508,26 +4520,26 @@ struct register_vertex_printer { static std::string type() { - return "short" + std::to_string(count); + return fmt::format("short%u", count); } static std::string value(u32 v) { - return std::to_string(v & 0xffff) + std::to_string(v >> 16); + return fmt::format("%u %u", (v & 0xffff), (v >> 16)); } }; template<> struct register_vertex_printer { - static std::string type() + static std::string_view type() { return "uchar4"; } static std::string value(u32 v) { - return std::to_string(v & 0xff) + std::to_string((v >> 8) & 0xff) + std::to_string((v >> 16) & 0xff) + std::to_string((v >> 24) & 0xff); + return fmt::format("%u %u %u %u", (v & 0xff), ((v >> 8) & 0xff), ((v >> 16) & 0xff), ((v >> 24) & 0xff)); } }; @@ -4538,17 +4550,16 @@ struct register_vertex_helper { const u32 value; - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} }; static constexpr usz increment_per_array_index = (count * sizeof(type)) / sizeof(u32); static constexpr usz attribute_index = index / increment_per_array_index; static constexpr usz vertex_subreg = index % increment_per_array_index; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return "register vertex " + std::to_string(attribute_index) + " as " + register_vertex_printer::type() + ": " + - register_vertex_printer::value(decoded.value); + fmt::append(out, "register vertex: %u as %u: %s", attribute_index, register_vertex_printer::type(), register_vertex_printer::value(decoded.value)); } }; @@ -4597,7 +4608,7 @@ struct texture_offset_helper { const u32 value; - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u32 offset() const { @@ -4605,9 +4616,9 @@ struct texture_offset_helper } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Texture %u: Offset: 0x%x", index, decoded.offset()); + fmt::append(out, "Texture %u: Offset: 0x%x", index, decoded.offset()); } }; @@ -4618,7 +4629,7 @@ struct texture_format_helper { const u32 value; - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} CellGcmLocation location() const { @@ -4653,9 +4664,9 @@ struct texture_format_helper } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Texture %u: %s, Cubemap: %s, %s, %s, Mipmap: %u", index, + fmt::append(out, "Texture %u: %s, Cubemap: %s, %s, %s, Mipmap: %u", index, decoded.location(), decoded.cubemap(), decoded.dimension(), decoded.format(), decoded.mipmap()); } }; @@ -4667,7 +4678,7 @@ struct texture_image_rect_helper { const u32 value; - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u16 height() const { @@ -4680,9 +4691,9 @@ struct texture_image_rect_helper } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Texture %u: W: %u, H: %u", index, decoded.width(), decoded.height()); + fmt::append(out, "Texture %u: W: %u, H: %u", index, decoded.width(), decoded.height()); } }; @@ -4693,7 +4704,7 @@ struct texture_control0_helper { const u32 value; - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} bool enabled() const { @@ -4721,9 +4732,9 @@ struct texture_control0_helper } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Texture %u: %s, Min/Max LOD: %g/%g, Max Aniso: %s, AKill: %s", index, print_boolean(decoded.enabled()) + fmt::append(out, "Texture %u: %s, Min/Max LOD: %g/%g, Max Aniso: %s, AKill: %s", index, print_boolean(decoded.enabled()) , decoded.min_lod(), decoded.max_lod(), decoded.max_aniso(), print_boolean(decoded.alpha_kill_enabled())); } }; @@ -4735,7 +4746,7 @@ struct texture_control3_helper { const u32 value; - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} u16 depth() const { @@ -4748,9 +4759,9 @@ struct texture_control3_helper } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("Texture %u: Pitch: %u, Depth: %u", index, decoded.pitch(), decoded.depth()); + fmt::append(out, "Texture %u: Pitch: %u, Depth: %u", index, decoded.pitch(), decoded.depth()); } }; @@ -4789,7 +4800,7 @@ struct vertex_texture_control0_helper { const u32 value; - decoded_type(u32 value) : value(value) {} + constexpr decoded_type(u32 value) noexcept : value(value) {} bool enabled() const { @@ -4807,9 +4818,9 @@ struct vertex_texture_control0_helper } }; - static std::string dump(const decoded_type& decoded) + static void dump(std::string& out, const decoded_type& decoded) { - return fmt::format("VTexture %u: %s, Min/Max LOD: %g/%g", index, print_boolean(decoded.enabled()) + fmt::append(out, "VTexture %u: %s, Min/Max LOD: %g/%g", index, print_boolean(decoded.enabled()) , decoded.min_lod(), decoded.max_lod()); } }; diff --git a/rpcs3/Emu/RSX/rsx_methods.cpp b/rpcs3/Emu/RSX/rsx_methods.cpp index fb78fd790d..6598e69f62 100644 --- a/rpcs3/Emu/RSX/rsx_methods.cpp +++ b/rpcs3/Emu/RSX/rsx_methods.cpp @@ -849,6 +849,23 @@ namespace rsx } } + void set_color_mask(thread* rsx, u32 reg, u32 arg) + { + if (arg == method_registers.register_previous_value) + { + return; + } + + if (method_registers.decode(arg).is_invalid()) [[ unlikely ]] + { + method_registers.decode(reg, method_registers.register_previous_value); + } + else + { + set_surface_options_dirty_bit(rsx, reg, arg); + } + } + void set_stencil_op(thread* rsx, u32 reg, u32 arg) { if (arg == method_registers.register_previous_value) @@ -3550,7 +3567,7 @@ namespace rsx bind(NV4097_SET_DEPTH_TEST_ENABLE, nv4097::set_surface_options_dirty_bit); bind(NV4097_SET_DEPTH_FUNC, nv4097::set_surface_options_dirty_bit); bind(NV4097_SET_DEPTH_MASK, nv4097::set_surface_options_dirty_bit); - bind(NV4097_SET_COLOR_MASK, nv4097::set_surface_options_dirty_bit); + bind(NV4097_SET_COLOR_MASK, nv4097::set_color_mask); bind(NV4097_SET_COLOR_MASK_MRT, nv4097::set_surface_options_dirty_bit); bind(NV4097_SET_TWO_SIDED_STENCIL_TEST_ENABLE, nv4097::set_surface_options_dirty_bit); bind(NV4097_SET_STENCIL_TEST_ENABLE, nv4097::set_surface_options_dirty_bit); diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index 9eff4d8162..7b0aa144d6 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -15,6 +15,7 @@ #include "Emu/Cell/PPUDisAsm.h" #include "Emu/Cell/PPUAnalyser.h" #include "Emu/Cell/SPUThread.h" +#include "Emu/Cell/SPURecompiler.h" #include "Emu/RSX/RSXThread.h" #include "Emu/Cell/lv2/sys_process.h" #include "Emu/Cell/lv2/sys_sync.h" @@ -128,7 +129,7 @@ void fmt_class_string::format(std::string& out, u64 arg) case game_boot_result::firmware_missing: return "Firmware is missing"; case game_boot_result::unsupported_disc_type: return "This disc type is not supported yet"; case game_boot_result::savestate_corrupted: return "Savestate data is corrupted or it's not an RPCS3 savestate"; - case game_boot_result::savestate_version_unsupported: return "Savestate versioning data differes from your RPCS3 build"; + case game_boot_result::savestate_version_unsupported: return "Savestate versioning data differs from your RPCS3 build"; case game_boot_result::still_running: return "Game is still running"; } return unknown; @@ -153,7 +154,7 @@ void fmt_class_string::format(std::string& out, u64 arg) }); } -void Emulator::CallFromMainThread(std::function&& func, atomic_t* wake_up, bool track_emu_state, u64 stop_ctr) const +void Emulator::CallFromMainThread(std::function&& func, atomic_t* wake_up, bool track_emu_state, u64 stop_ctr) const { if (!track_emu_state) { @@ -174,14 +175,14 @@ void Emulator::CallFromMainThread(std::function&& func, atomic_t* void Emulator::BlockingCallFromMainThread(std::function&& func) const { - atomic_t wake_up = false; + atomic_t wake_up = 0; CallFromMainThread(std::move(func), &wake_up); while (!wake_up) { ensure(thread_ctrl::get_current()); - wake_up.wait(false); + wake_up.wait(0); } } @@ -243,6 +244,39 @@ void fixup_ppu_settings() } } +void dump_executable(std::span data, main_ppu_module* _main, std::string_view title_id) +{ + // Format filename and directory name + // Make each directory for each file so tools like IDA can work on it cleanly + const std::string dir_path = fs::get_cache_dir() + "ppu_progs/" + std::string{!title_id.empty() ? title_id : "untitled"} + fmt::format("-%s-%s", fmt::base57(_main->sha1), _main->path.substr(_main->path.find_last_of('/') + 1)) + '/'; + const std::string filename = dir_path + "exec.elf"; + + if (fs::create_dir(dir_path) || fs::g_tls_error == fs::error::exist) + { + if (fs::file out{filename, fs::create + fs::write}) + { + if (out.size() == data.size()) + { + // Risky optimization: assume if file size match they are equal and does not need to rewrite it + // But it is a debug option and if there are problems the user/developer can remove the previous file + } + else + { + out.trunc(0); + out.write(data.data(), data.size()); + } + } + else + { + sys_log.error("Failed to save decrypted executable of \"%s\": Failure to create file \"%s\" (%s)", Emu.GetBoot(), filename, fs::g_tls_error); + } + } + else + { + sys_log.error("Failed to save decrypted executable of \"%s\": Failure to create directory \"%s\" (%s)", Emu.GetBoot(), dir_path, fs::g_tls_error); + } +} + void Emulator::Init() { jit_runtime::initialize(); @@ -424,7 +458,7 @@ void Emulator::Init() make_path_verbose(dev_flash, true); make_path_verbose(dev_flash2, true); make_path_verbose(dev_flash3, true); - + if (make_path_verbose(dev_usb, true)) { make_path_verbose(dev_usb + "MUSIC/", false); @@ -492,6 +526,7 @@ void Emulator::Init() make_path_verbose(fs::get_cache_dir() + "shaderlog/", false); make_path_verbose(fs::get_cache_dir() + "spu_progs/", false); + make_path_verbose(fs::get_cache_dir() + "ppu_progs/", false); make_path_verbose(fs::get_parent_dir(get_savestate_file("NO_ID", "/NO_FILE", -1, -1)), false); make_path_verbose(fs::get_config_dir() + "captures/", false); make_path_verbose(fs::get_config_dir() + "sounds/", false); @@ -1351,6 +1386,10 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch, // Force LLVM recompiler g_cfg.core.ppu_decoder.from_default(); + // Force SPU cache and precompilation + g_cfg.core.llvm_precompilation.set(true); + g_cfg.core.spu_cache.set(true); + // Disable incompatible settings fixup_ppu_settings(); @@ -1428,7 +1467,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch, if (obj == elf_error::ok && ppu_load_exec(obj, true, path)) { - g_fxo->get().path = path; + ensure(g_fxo->try_get())->path = path; } else { @@ -1439,13 +1478,14 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch, g_fxo->init("SPRX Loader"sv, [this, dir_queue]() mutable { - if (auto& _main = g_fxo->get(); !_main.path.empty()) + if (auto& _main = *ensure(g_fxo->try_get()); !_main.path.empty()) { if (!_main.analyse(0, _main.elf_entry, _main.seg0_code_end, _main.applied_pathes, [](){ return Emu.IsStopped(); })) { return; } + Emu.ConfigurePPUCache(); ppu_initialize(_main); } @@ -1456,6 +1496,13 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch, ppu_precompile(dir_queue, nullptr); + if (Emu.IsStopped()) + { + return; + } + + spu_cache::initialize(false); + // Exit "process" CallFromMainThread([this] { @@ -1884,10 +1931,13 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch, return game_boot_result::invalid_file_or_folder; } + bool had_been_decrypted = false; + // Check SELF header if (elf_file.size() >= 4 && elf_file.read() == "SCE\0"_u32) { // Decrypt SELF + had_been_decrypted = true; elf_file = decrypt_self(std::move(elf_file), klic.empty() ? nullptr : reinterpret_cast(&klic[0]), &g_ps3_process_info.self_info); } else @@ -2007,10 +2057,18 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch, sys_log.error("Booting HG category outside of HDD0!"); } - g_fxo->init(); + const auto _main = g_fxo->init(); if (ppu_load_exec(ppu_exec, false, m_path, DeserialManager())) { + if (g_cfg.core.ppu_debug && had_been_decrypted) + { + // Auto-dump decrypted binaries if PPU debug is enabled + + const auto exec_bin = elf_file.to_vector(); + + dump_executable({exec_bin.data(), exec_bin.size()}, _main, GetTitleID()); + } } // Overlay (OVL) executable (only load it) else if (vm::map(0x3000'0000, 0x1000'0000, 0x200); !ppu_load_overlay(ppu_exec, false, m_path).first) @@ -2152,7 +2210,7 @@ void Emulator::RunPPU() } ensure(cpu.state.test_and_reset(cpu_flag::stop)); - cpu.state.notify_one(cpu_flag::stop); + cpu.state.notify_one(); signalled_thread = true; }); @@ -2165,7 +2223,7 @@ void Emulator::RunPPU() if (auto thr = g_fxo->try_get>()) { thr->state -= cpu_flag::stop; - thr->state.notify_one(cpu_flag::stop); + thr->state.notify_one(); } } @@ -2234,7 +2292,7 @@ void Emulator::FinalizeRunRequest() } ensure(spu.state.test_and_reset(cpu_flag::stop)); - spu.state.notify_one(cpu_flag::stop); + spu.state.notify_one(); }; if (m_savestate_extension_flags1 & SaveStateExtentionFlags1::ShouldCloseMenu) @@ -2437,7 +2495,7 @@ void Emulator::Resume() auto on_select = [](u32, cpu_thread& cpu) { cpu.state -= cpu_flag::dbg_global_pause; - cpu.state.notify_one(cpu_flag::dbg_global_pause); + cpu.state.notify_one(); }; idm::select>(on_select); @@ -2576,9 +2634,9 @@ void Emulator::Kill(bool allow_autoexit, bool savestate) try_lock_spu_threads_in_a_state_compatible_with_savestates(true); sys_log.error("Failed to savestate: HLE VDEC (video decoder) context(s) exist." - "\nLLE libvdec.sprx by selecting it in Adavcned tab -> Firmware Libraries." - "\nYou need to close the game for to take effect." - "\nIf you cannot close the game due to losing important progress your best chance is to skip the current cutscenes if any are played and retry."); + "\nLLE libvdec.sprx by selecting it in Advanced tab -> Firmware Libraries." + "\nYou need to close the game for it to take effect." + "\nIf you cannot close the game due to losing important progress, your best chance is to skip the current cutscenes if any are played and retry."); return; } @@ -2633,7 +2691,7 @@ void Emulator::Kill(bool allow_autoexit, bool savestate) { // Show visual feedback to the user in case that stopping takes a while. // This needs to be done before actually stopping, because otherwise the necessary threads will be terminated before we can show an image. - if (auto progress_dialog = g_fxo->try_get>(); progress_dialog && +g_progr) + if (auto progress_dialog = g_fxo->try_get>(); progress_dialog && g_progr.load()) { // We are currently showing a progress dialog. Notify it that we are going to stop emulation. g_system_progress_stopping = true; @@ -2896,11 +2954,72 @@ void Emulator::Kill(bool allow_autoexit, bool savestate) ar.set_reading_state(); } - // Final termination from main thread (move the last ownership of join thread in order to destroy it) - CallFromMainThread([join_thread = std::move(join_thread), allow_autoexit, this]() mutable + // Log additional debug information - do not do it on the main thread due to the concern of halting UI events + + if (g_tty && sys_log.notice) + { + // Write merged TTY output after emulation has been safely stopped + + if (usz attempted_read_size = utils::sub_saturate(g_tty.pos(), m_tty_file_init_pos)) + { + if (fs::file tty_read_fd{fs::get_cache_dir() + "TTY.log"}) + { + // Enfore an arbitrary limit for now to avoid OOM in case the guest code has bombarded TTY + // 3MB, this should be enough + constexpr usz c_max_tty_spill_size = 0x30'0000; + + std::string tty_buffer(std::min(attempted_read_size, c_max_tty_spill_size), '\0'); + tty_buffer.resize(tty_read_fd.read_at(m_tty_file_init_pos, tty_buffer.data(), tty_buffer.size())); + tty_read_fd.close(); + + if (!tty_buffer.empty()) + { + // Mark start and end very clearly with RPCS3 put in it + sys_log.notice("\nAccumulated RPCS3 TTY:\n\n\n%s\n\n\nEnd RPCS3 TTY Section.\n", tty_buffer); + } + } + } + } + + if (g_cfg.core.spu_debug && sys_log.notice) { const std::string cache_path = rpcs3::cache::get_ppu_cache(); + if (fs::file spu_log{cache_path + "/spu.log"}) + { + // 96MB limit, this may be a lot but this only has an effect when enabling the debug option + constexpr usz c_max_spu_log_spill_size = 0x600'0000; + const usz total_size = spu_log.size(); + + std::string log_buffer(std::min(spu_log.size(), c_max_spu_log_spill_size), '\0'); + log_buffer.resize(spu_log.read(log_buffer.data(), log_buffer.size())); + spu_log.close(); + + if (!log_buffer.empty()) + { + usz to_remove = 0; + usz part_ctr = 1; + + for (std::string_view not_logged = log_buffer; !not_logged.empty(); part_ctr++, not_logged.remove_prefix(to_remove)) + { + std::string_view to_log = not_logged; + to_log = to_log.substr(0, 0x2'0000); + to_log = to_log.substr(0, utils::add_saturate(to_log.rfind("\n========== SPU BLOCK"sv), 1)); + to_remove = to_log.size(); + + // Cannot log it all at once due to technical reasons, split it to 8MB at maximum of whole functions + // Assume the block prefix exists because it is created by RPCS3 (or log it in an ugly manner if it does not exist) + sys_log.notice("Logging spu.log part %u:\n\n%s\n", part_ctr, to_log); + } + + sys_log.notice("End spu.log (%u bytes)", total_size); + } + } + } + + // Final termination from main thread (move the last ownership of join thread in order to destroy it) + CallFromMainThread([join_thread = std::move(join_thread), allow_autoexit, this]() mutable + { cpu_thread::cleanup(); initialize_timebased_time(0, true); @@ -2961,64 +3080,6 @@ void Emulator::Kill(bool allow_autoexit, bool savestate) m_state = system_state::stopped; GetCallbacks().on_stop(); - if (g_tty && sys_log.notice) - { - // Write merged TTY output after emulation has been safely stopped - - if (usz attempted_read_size = utils::sub_saturate(g_tty.pos(), m_tty_file_init_pos)) - { - if (fs::file tty_read_fd{fs::get_cache_dir() + "TTY.log"}) - { - // Enfore an arbitrary limit for now to avoid OOM in case the guest code has bombarded TTY - // 16MB, this should be enough - constexpr usz c_max_tty_spill_size = 0x10'0000; - - std::string tty_buffer(std::min(attempted_read_size, c_max_tty_spill_size), '\0'); - tty_buffer.resize(tty_read_fd.read_at(m_tty_file_init_pos, tty_buffer.data(), tty_buffer.size())); - tty_read_fd.close(); - - if (!tty_buffer.empty()) - { - // Mark start and end very clearly with RPCS3 put in it - sys_log.notice("\nAccumulated RPCS3 TTY:\n\n\n%s\n\n\nEnd RPCS3 TTY Section.\n", tty_buffer); - } - } - } - } - - if (g_cfg.core.spu_debug && sys_log.notice) - { - if (fs::file spu_log{cache_path + "/spu.log"}) - { - // 96MB limit, this may be a lot but this only has an effect when enabling the debug option - constexpr usz c_max_tty_spill_size = 0x60'0000; - - std::string log_buffer(std::min(spu_log.size(), c_max_tty_spill_size), '\0'); - log_buffer.resize(spu_log.read(log_buffer.data(), log_buffer.size())); - spu_log.close(); - - if (!log_buffer.empty()) - { - usz to_remove = 0; - usz part_ctr = 1; - - for (std::string_view not_logged = log_buffer; !not_logged.empty(); part_ctr++, not_logged.remove_prefix(to_remove)) - { - std::string_view to_log = not_logged; - to_log = to_log.substr(0, 0x8'0000); - to_log = to_log.substr(0, utils::add_saturate(to_log.rfind("\n========== SPU BLOCK"sv), 1)); - to_remove = to_log.size(); - - // Cannot log it all at once due to technical reasons, split it to 8MB at maximum of whole functions - // Assume the block prefix exists because it is created by RPCS3 (or log it in an ugly manner if it does not exist) - sys_log.notice("Logging spu.log part %u:\n\n%s\n", part_ctr, to_log); - } - - sys_log.notice("End spu.log"); - } - } - } - // Always Enable display sleep, not only if it was prevented. enable_display_sleep(); @@ -3195,13 +3256,13 @@ s32 error_code::error_report(s32 result, const logs::message* channel, const cha return result; } -void Emulator::ConfigurePPUCache() const +void Emulator::ConfigurePPUCache(bool with_title_id) const { auto& _main = g_fxo->get(); _main.cache = rpcs3::utils::get_cache_dir(); - if (!m_title_id.empty() && m_cat != "1P") + if (with_title_id && !m_title_id.empty() && m_cat != "1P") { _main.cache += GetTitleID(); _main.cache += '/'; diff --git a/rpcs3/Emu/System.h b/rpcs3/Emu/System.h index 15753b3631..7512b54047 100644 --- a/rpcs3/Emu/System.h +++ b/rpcs3/Emu/System.h @@ -54,7 +54,7 @@ constexpr bool is_error(game_boot_result res) struct EmuCallbacks { - std::function, atomic_t*)> call_from_main_thread; + std::function, atomic_t*)> call_from_main_thread; std::function on_run; // (start_playtime) continuing or going ingame, so start the clock std::function on_pause; std::function on_resume; @@ -180,7 +180,7 @@ public: } // Call from the GUI thread - void CallFromMainThread(std::function&& func, atomic_t* wake_up = nullptr, bool track_emu_state = true, u64 stop_ctr = umax) const; + void CallFromMainThread(std::function&& func, atomic_t* wake_up = nullptr, bool track_emu_state = true, u64 stop_ctr = umax) const; // Blocking call from the GUI thread void BlockingCallFromMainThread(std::function&& func) const; @@ -353,7 +353,7 @@ public: std::string GetFormattedTitle(double fps) const; - void ConfigurePPUCache() const; + void ConfigurePPUCache(bool with_title_id = true) const; std::set GetGameDirs() const; void AddGamesFromDir(const std::string& path); diff --git a/rpcs3/Emu/savestate_utils.cpp b/rpcs3/Emu/savestate_utils.cpp index e76bff203f..d2ac671cc3 100644 --- a/rpcs3/Emu/savestate_utils.cpp +++ b/rpcs3/Emu/savestate_utils.cpp @@ -18,7 +18,7 @@ struct serial_ver_t std::set compatible_versions; }; -static std::array s_serial_versions; +static std::array s_serial_versions; #define SERIALIZATION_VER(name, identifier, ...) \ \ @@ -35,7 +35,7 @@ static std::array s_serial_versions; return ::s_serial_versions[identifier].current_version;\ } -SERIALIZATION_VER(global_version, 0, 13) // For stuff not listed here +SERIALIZATION_VER(global_version, 0, 14) // For stuff not listed here SERIALIZATION_VER(ppu, 1, 1) SERIALIZATION_VER(spu, 2, 1) SERIALIZATION_VER(lv2_sync, 3, 1) @@ -73,6 +73,11 @@ SERIALIZATION_VER(cellGcm, 19, 1) SERIALIZATION_VER(sysPrxForUser, 20, 1) SERIALIZATION_VER(cellSaveData, 21, 1) SERIALIZATION_VER(cellAudioOut, 22, 1) +SERIALIZATION_VER(sys_io, 23, 1) + +// Misc versions for HLE/LLE not included so main version would not invalidated +SERIALIZATION_VER(LLE, 24, 1) +SERIALIZATION_VER(HLE, 25, 1) std::vector> get_savestate_versioning_data(const fs::file& file) { @@ -121,7 +126,7 @@ bool is_savestate_version_compatible(const std::vector>& dat { if (identifier >= s_serial_versions.size()) { - (is_boot_check ? sys_log.error : sys_log.trace)("Savestate version identider is unknown! (category=%u, version=%u)", identifier, version); + (is_boot_check ? sys_log.error : sys_log.trace)("Savestate version identifier is unknown! (category=%u, version=%u)", identifier, version); ok = false; // Log all mismatches } else if (!s_serial_versions[identifier].compatible_versions.count(version)) @@ -234,7 +239,7 @@ bool boot_last_savestate(bool testing) if (game_boot_result error = Emu.BootGame(savestate_path, "", true); error != game_boot_result::no_errors) { - sys_log.error("Failed to booting savestate \'%s\' using the Reload shortcut. (error: %s)", savestate_path, error); + sys_log.error("Failed to boot savestate \'%s\' using the Reload shortcut. (error: %s)", savestate_path, error); } else { diff --git a/rpcs3/Emu/system_config.h b/rpcs3/Emu/system_config.h index a49a7b9c9c..7964a6b195 100644 --- a/rpcs3/Emu/system_config.h +++ b/rpcs3/Emu/system_config.h @@ -28,7 +28,7 @@ struct cfg_root : cfg::node cfg::string llvm_cpu{ this, "Use LLVM CPU" }; cfg::_int<0, 1024> llvm_threads{ this, "Max LLVM Compile Threads", 0 }; cfg::_bool ppu_llvm_greedy_mode{ this, "PPU LLVM Greedy Mode", false, false }; - cfg::_bool ppu_llvm_precompilation{ this, "PPU LLVM Precompilation", true }; + cfg::_bool llvm_precompilation{ this, "LLVM Precompilation", true }; cfg::_enum thread_scheduler{this, "Thread Scheduler Mode", thread_scheduler_mode::os}; cfg::_bool set_daz_and_ftz{ this, "Set DAZ and FTZ", false }; cfg::_enum spu_decoder{ this, "SPU Decoder", spu_decoder_type::llvm }; @@ -38,7 +38,7 @@ struct cfg_root : cfg::node cfg::_bool mfc_debug{ this, "MFC Debug" }; cfg::_int<0, 6> preferred_spu_threads{ this, "Preferred SPU Threads", 0, true }; // Number of hardware threads dedicated to heavy simultaneous spu tasks cfg::_int<0, 16> spu_delay_penalty{ this, "SPU delay penalty", 3 }; // Number of milliseconds to block a thread if a virtual 'core' isn't free - cfg::_bool spu_loop_detection{ this, "SPU loop detection", false, true }; // Try to detect wait loops and trigger thread yield + cfg::_bool spu_loop_detection{ this, "SPU loop detection", false }; // Try to detect wait loops and trigger thread yield cfg::_int<0, 6> max_spurs_threads{ this, "Max SPURS Threads", 6 }; // HACK. If less then 6, max number of running SPURS threads in each thread group. cfg::_enum spu_block_size{ this, "SPU Block Size", spu_block_size_type::safe }; cfg::_bool spu_accurate_getllar{ this, "Accurate GETLLAR", false, true }; @@ -66,9 +66,7 @@ struct cfg_root : cfg::node cfg::uint<0, 10000> mfc_transfers_timeout{ this, "MFC Commands Timeout", 0, true }; cfg::_bool mfc_shuffling_in_steps{ this, "MFC Commands Shuffling In Steps", false, true }; cfg::_enum enable_TSX{ this, "Enable TSX", enable_tsx_by_default() ? tsx_usage::enabled : tsx_usage::disabled }; // Enable TSX. Forcing this on Haswell/Broadwell CPUs should be used carefully - cfg::_bool spu_accurate_xfloat{ this, "Accurate xfloat", false }; - cfg::_bool spu_approx_xfloat{ this, "Approximate xfloat", true }; - cfg::_bool spu_relaxed_xfloat{ this, "Relaxed xfloat", true }; // Approximate accuracy for only the "FCGT", "FNMS", "FREST" AND "FRSQEST" instructions + cfg::_enum spu_xfloat_accuracy{ this, "XFloat Accuracy", xfloat_accuracy::approximate, false }; cfg::_int<-1, 14> ppu_128_reservations_loop_max_length{ this, "Accurate PPU 128-byte Reservation Op Max Length", 0, true }; // -1: Always accurate, 0: Never accurate, 1-14: max accurate loop length cfg::_int<-64, 64> stub_ppu_traps{ this, "Stub PPU Traps", 0, true }; // Hack, skip PPU traps for rare cases where the trap is continueable (specify relative instructions to skip) cfg::_bool full_width_avx512{ this, "Full Width AVX-512", true }; @@ -280,6 +278,7 @@ struct cfg_root : cfg::node cfg::_bool show_move_cursor{this, "Show move cursor", false, true}; cfg::_bool lock_overlay_input_to_player_one{this, "Lock overlay input to player one", false, true}; cfg::string midi_devices{ this, "Emulated Midi devices", "ßßß@@@ßßß@@@ßßß@@@" }; + cfg::_bool load_sdl_mappings{ this, "Load SDL GameController Mappings", true }; } io{ this }; struct node_sys : cfg::node diff --git a/rpcs3/Emu/system_config_types.cpp b/rpcs3/Emu/system_config_types.cpp index 7324101a89..e785ef8814 100644 --- a/rpcs3/Emu/system_config_types.cpp +++ b/rpcs3/Emu/system_config_types.cpp @@ -664,3 +664,20 @@ void fmt_class_string::format(std::string& out, u64 arg) return unknown; }); } + +template <> +void fmt_class_string::format(std::string& out, u64 arg) +{ + format_enum(out, arg, [](xfloat_accuracy value) + { + switch (value) + { + case xfloat_accuracy::accurate: return "Accurate"; + case xfloat_accuracy::approximate: return "Approximate"; + case xfloat_accuracy::relaxed: return "Relaxed"; + case xfloat_accuracy::inaccurate: return "Inaccurate"; + } + + return unknown; + }); +} diff --git a/rpcs3/Emu/system_config_types.h b/rpcs3/Emu/system_config_types.h index cf1214a967..90862eee69 100644 --- a/rpcs3/Emu/system_config_types.h +++ b/rpcs3/Emu/system_config_types.h @@ -320,3 +320,11 @@ enum class stereo_render_mode_options side_by_side, over_under }; + +enum class xfloat_accuracy +{ + accurate, + approximate, + relaxed, // Approximate accuracy for only the "FCGT", "FNMS", "FREST" AND "FRSQEST" instructions + inaccurate +}; diff --git a/rpcs3/Emu/system_progress.cpp b/rpcs3/Emu/system_progress.cpp index ea8c0086e9..6d5287d163 100644 --- a/rpcs3/Emu/system_progress.cpp +++ b/rpcs3/Emu/system_progress.cpp @@ -11,7 +11,7 @@ LOG_CHANNEL(sys_log, "SYS"); // Progress display server synchronization variables -atomic_t g_progr{nullptr}; +atomic_t g_progr{}; atomic_t g_progr_ftotal{0}; atomic_t g_progr_fdone{0}; atomic_t g_progr_ptotal{0}; @@ -37,10 +37,30 @@ void progress_dialog_server::operator()() std::shared_ptr native_dlg; g_system_progress_stopping = false; + const auto get_state = []() + { + auto whole_state = std::make_tuple(+g_progr.load(), +g_progr_ftotal, +g_progr_fdone, +g_progr_ptotal, +g_progr_pdone); + + while (true) + { + auto new_state = std::make_tuple(+g_progr.load(), +g_progr_ftotal, +g_progr_fdone, +g_progr_ptotal, +g_progr_pdone); + + if (new_state == whole_state) + { + // Only leave while it has a complete (atomic) state + return whole_state; + } + + whole_state = std::move(new_state); + } + + return whole_state; + }; + while (!g_system_progress_stopping && thread_ctrl::state() != thread_state::aborting) { // Wait for the start condition - auto text0 = +g_progr; + const char* text0 = g_progr.load(); while (!text0) { @@ -49,8 +69,29 @@ void progress_dialog_server::operator()() break; } + if (g_progr_ftotal || g_progr_fdone || g_progr_ptotal || g_progr_pdone) + { + const auto& [text_new, ftotal, fdone, ptotal, pdone] = get_state(); + + if (text_new) + { + text0 = text_new; + break; + } + + if ((ftotal || ptotal) && ftotal == fdone && ptotal == pdone) + { + // Cleanup (missed message but do not cry over spilt milk) + g_progr_fdone -= fdone; + g_progr_pdone -= pdone; + g_progr_ftotal -= ftotal; + g_progr_ptotal -= ptotal; + g_progr_ptotal.notify_all(); + } + } + thread_ctrl::wait_for(5000); - text0 = +g_progr; + text0 = g_progr.load(); } if (g_system_progress_stopping || thread_ctrl::state() == thread_state::aborting) @@ -68,7 +109,7 @@ void progress_dialog_server::operator()() { // Some backends like OpenGL actually initialize a lot of driver objects in the "on_init" method. // Wait for init to complete within reasonable time. Abort just in case we have hardware/driver issues. - renderer->is_initialized.wait(false, atomic_wait_timeout(5 * 1000000000ull)); + renderer->is_initialized.wait(0, atomic_wait_timeout(5 * 1000000000ull)); auto manager = g_fxo->try_get(); show_overlay_message = g_fxo->get().show_overlay_message_only; @@ -115,17 +156,14 @@ void progress_dialog_server::operator()() u32 fdone = 0; u32 ptotal = 0; u32 pdone = 0; - auto text1 = text0; + const char* text1 = nullptr; + + const u64 start_time = get_system_time(); // Update progress while (!g_system_progress_stopping && thread_ctrl::state() != thread_state::aborting) { - const auto text_new = g_progr.load(); - - const u32 ftotal_new = g_progr_ftotal; - const u32 fdone_new = g_progr_fdone; - const u32 ptotal_new = g_progr_ptotal; - const u32 pdone_new = g_progr_pdone; + const auto& [text_new, ftotal_new, fdone_new, ptotal_new, pdone_new] = get_state(); if (ftotal != ftotal_new || fdone != fdone_new || ptotal != ptotal_new || pdone != pdone_new || text_new != text1) { @@ -133,21 +171,35 @@ void progress_dialog_server::operator()() fdone = fdone_new; ptotal = ptotal_new; pdone = pdone_new; - text1 = text_new; - if (!text_new) + const bool text_changed = text_new && text_new != text1; + + if (text_new) { - // Close dialog - break; + text1 = text_new; + } + + if (!text1) + { + // Cannot do anything + continue; } if (show_overlay_message) { - // Show a message instead - if (g_cfg.misc.show_ppu_compilation_hint) + // Show a message instead (if compilation period is estimated to be lengthy) + if (pdone < ptotal && g_cfg.misc.show_ppu_compilation_hint) { - rsx::overlays::show_ppu_compile_notification(); + const u64 passed_usec = (get_system_time() - start_time); + const u64 remaining_usec = passed_usec * (pdone ? ((static_cast(ptotal) - pdone) / pdone) : ptotal); + + // Only show compile notification if we estimate at least 100ms + if (remaining_usec >= 100'000ULL) + { + rsx::overlays::show_ppu_compile_notification(); + } } + thread_ctrl::wait_for(10000); continue; } @@ -156,29 +208,44 @@ void progress_dialog_server::operator()() // Assume not all programs were found if files were not compiled (as it may contain more) const u64 total = std::max(ptotal, 1) * std::max(ftotal, 1); const u64 done = pdone * std::max(fdone, 1); - const f32 value = static_cast(std::fmin(done * 100. / total, 100.f)); + const u32 value = static_cast(done >= total ? 100 : done * 100 / total); std::string progr = "Progress:"; - if (ftotal) - fmt::append(progr, " file %u of %u%s", fdone, ftotal, ptotal ? "," : ""); - if (ptotal) - fmt::append(progr, " module %u of %u", pdone, ptotal); + if (ftotal || ptotal) + { + if (ftotal) + fmt::append(progr, " file %u of %u%s", fdone, ftotal, ptotal ? "," : ""); + if (ptotal) + fmt::append(progr, " module %u of %u", pdone, ptotal); + } + else + { + fmt::append(progr, " analysing..."); + } // Changes detected, send update if (native_dlg) { - native_dlg->set_text(text_new); - native_dlg->progress_bar_set_message(0, progr); - native_dlg->progress_bar_set_value(0, std::floor(value)); + if (text_changed) + { + native_dlg->set_text(text1); + } + + native_dlg->progress_bar_set_message(0, std::move(progr)); + native_dlg->progress_bar_set_value(0, static_cast(value)); } else if (dlg) { Emu.CallFromMainThread([=]() { - dlg->SetMsg(text_new); + if (text_changed) + { + dlg->SetMsg(text1); + } + dlg->ProgressBarSetMsg(0, progr); - dlg->ProgressBarSetValue(0, static_cast(std::floor(value))); + dlg->ProgressBarSetValue(0, value); }); } } @@ -189,6 +256,13 @@ void progress_dialog_server::operator()() rsx::overlays::refresh_message_queue(); } + // Leave only if total count is equal to done count + if (ftotal == fdone && ptotal == pdone && !text_new) + { + // Complete state, empty message: close dialog + break; + } + thread_ctrl::wait_for(10000); } @@ -226,6 +300,11 @@ void progress_dialog_server::operator()() native_dlg->set_text("Stopping. Please wait..."); native_dlg->refresh(); } + + if (g_progr_ptotal.exchange(0)) + { + g_progr_ptotal.notify_all(); + } } progress_dialog_server::~progress_dialog_server() @@ -234,5 +313,5 @@ progress_dialog_server::~progress_dialog_server() g_progr_fdone.release(0); g_progr_ptotal.release(0); g_progr_pdone.release(0); - g_progr.release(nullptr); + g_progr.release(progress_dialog_string_t{}); } diff --git a/rpcs3/Emu/system_progress.hpp b/rpcs3/Emu/system_progress.hpp index 92139b4afe..76e8e38ca7 100644 --- a/rpcs3/Emu/system_progress.hpp +++ b/rpcs3/Emu/system_progress.hpp @@ -3,7 +3,19 @@ #include "util/types.hpp" #include "util/atomic.hpp" -extern atomic_t g_progr; +struct alignas(16) progress_dialog_string_t +{ + const char* m_text; + u32 m_user_count; + u32 m_update_id; + + operator const char*() const noexcept + { + return m_text; + } +}; + +extern atomic_t g_progr; extern atomic_t g_progr_ftotal; extern atomic_t g_progr_fdone; extern atomic_t g_progr_ptotal; @@ -15,21 +27,68 @@ extern atomic_t g_system_progress_stopping; class scoped_progress_dialog final { // Saved previous value - const char* const m_prev; + const char* m_prev; + u32 m_prev_id; + u32 m_id; public: scoped_progress_dialog(const char* text) noexcept - : m_prev(g_progr.exchange(text ? text : "")) { + std::tie(m_prev, m_prev_id, m_id) = g_progr.atomic_op([this, text = ensure(text)](progress_dialog_string_t& progr) + { + const char* old = progr.m_text; + progr.m_user_count++; + progr.m_update_id++; + progr.m_text = text; + + ensure(progr.m_user_count > 1 || !old); // Ensure it was nullptr before first use + return std::make_tuple(old, progr.m_update_id - 1, progr.m_update_id); + }); } scoped_progress_dialog(const scoped_progress_dialog&) = delete; scoped_progress_dialog& operator=(const scoped_progress_dialog&) = delete; + scoped_progress_dialog& operator=(const char* text) noexcept + { + // This method is destroying the previous value and replacing it with a new one + std::tie(m_prev, m_prev_id, m_id) = g_progr.atomic_op([this, text = ensure(text)](progress_dialog_string_t& progr) + { + if (m_id == progr.m_update_id) + { + progr.m_update_id = m_prev_id; + progr.m_text = m_prev; + } + + const char* old = progr.m_text; + progr.m_text = text; + progr.m_update_id++; + + ensure(progr.m_user_count > 0); + return std::make_tuple(old, progr.m_update_id - 1, progr.m_update_id); + }); + + return *this; + } + ~scoped_progress_dialog() noexcept { - g_progr.release(m_prev); + g_progr.atomic_op([this](progress_dialog_string_t& progr) + { + if (progr.m_user_count-- == 1) + { + // Clean text only on last user + progr.m_text = nullptr; + progr.m_update_id = 0; + } + else if (m_id == progr.m_update_id) + { + // Restore text only if no other updates were made by other threads + progr.m_text = ensure(m_prev); + progr.m_update_id = m_prev_id; + } + }); } }; diff --git a/rpcs3/Emu/system_utils.cpp b/rpcs3/Emu/system_utils.cpp index 3538c1a2be..75f4c199da 100644 --- a/rpcs3/Emu/system_utils.cpp +++ b/rpcs3/Emu/system_utils.cpp @@ -379,6 +379,6 @@ namespace rpcs3::utils std::string get_custom_input_config_path(const std::string& title_id) { if (title_id.empty()) return ""; - return get_input_config_dir(title_id) + g_cfg_profile.default_profile + ".yml"; + return get_input_config_dir(title_id) + g_cfg_input_configs.default_config + ".yml"; } } diff --git a/rpcs3/Input/basic_mouse_handler.cpp b/rpcs3/Input/basic_mouse_handler.cpp index ac1c061aa9..9e06552244 100644 --- a/rpcs3/Input/basic_mouse_handler.cpp +++ b/rpcs3/Input/basic_mouse_handler.cpp @@ -128,6 +128,7 @@ void basic_mouse_handler::MouseMove(QMouseEvent* event) { // get the screen dimensions const QSize screen = m_target->size(); + const QPoint e_pos = event->pos(); if (m_target && m_target->isActive() && get_mouse_lock_state()) { @@ -144,7 +145,7 @@ void basic_mouse_handler::MouseMove(QMouseEvent* event) static QPoint p_real(p_center); // get the delta of the mouse position to the screen center - const QPoint p_delta = event->pos() - p_center; + const QPoint p_delta = e_pos - p_center; // update the current position without leaving the screen borders p_real.setX(std::clamp(p_real.x() + p_delta.x(), 0, screen.width())); @@ -155,7 +156,7 @@ void basic_mouse_handler::MouseMove(QMouseEvent* event) } else { - MouseHandlerBase::Move(event->x(), event->y(), screen.width(), screen.height()); + MouseHandlerBase::Move(e_pos.x(), e_pos.y(), screen.width(), screen.height()); } } } diff --git a/rpcs3/Input/ds4_pad_handler.cpp b/rpcs3/Input/ds4_pad_handler.cpp index 45007ff4a7..a7a4849200 100644 --- a/rpcs3/Input/ds4_pad_handler.cpp +++ b/rpcs3/Input/ds4_pad_handler.cpp @@ -16,8 +16,8 @@ namespace constexpr u32 DS4_GYRO_RES_PER_DEG_S = 86; // technically this could be 1024, but keeping it at 86 keeps us within 16 bits of precision constexpr u32 DS4_FEATURE_REPORT_0x02_SIZE = 37; constexpr u32 DS4_FEATURE_REPORT_0x05_SIZE = 41; - constexpr u32 DS4_FEATURE_REPORT_0x12_SIZE = 16; - constexpr u32 DS4_FEATURE_REPORT_0x81_SIZE = 7; + //constexpr u32 DS4_FEATURE_REPORT_0x12_SIZE = 16; + //constexpr u32 DS4_FEATURE_REPORT_0x81_SIZE = 7; constexpr u32 DS4_FEATURE_REPORT_0xA3_SIZE = 49; constexpr u32 DS4_INPUT_REPORT_0x11_SIZE = 78; constexpr u32 DS4_OUTPUT_REPORT_0x05_SIZE = 32; @@ -514,38 +514,18 @@ void ds4_pad_handler::check_add_device(hid_device* hidDevice, std::string_view p } std::string serial; + for (wchar_t ch : wide_serial) + serial += static_cast(ch); - // There isnt a nice 'portable' way with hidapi to detect bt vs wired as the pid/vid's are the same - // Let's try getting 0x81 feature report, which should return the mac address on wired, and should an error on bluetooth - std::array buf{}; - buf[0] = 0x81; - int res = hid_get_feature_report(hidDevice, buf.data(), DS4_FEATURE_REPORT_0x81_SIZE); - if (res > 0) + const hid_device_info* devinfo = hid_get_device_info(hidDevice); + if (!devinfo) { - if (res != DS4_FEATURE_REPORT_0x81_SIZE || buf[0] != 0x81) - { - // Controller may not be genuine. These controllers do not have feature 0x81 implemented and calibration data is in bluetooth format even in USB mode! - ds4_log.warning("check_add_device: DS4 controller may not be genuine. Workaround enabled. (result=%d, buf[0]=0x%x)", res, buf[0]); - - // Read feature report 0x12 instead which is what the console uses. - buf = {}; - buf[0] = 0x12; - if (res = hid_get_feature_report(hidDevice, buf.data(), DS4_FEATURE_REPORT_0x12_SIZE); res != DS4_FEATURE_REPORT_0x12_SIZE || buf[0] != 0x12) - { - ds4_log.error("check_add_device: hid_get_feature_report 0x12 failed! result=%d, buf[0]=0x%x, error=%s", res, buf[0], hid_error(hidDevice)); - } - } - - serial = fmt::format("%x%x%x%x%x%x", buf[6], buf[5], buf[4], buf[3], buf[2], buf[1]); - } - else - { - ds4_log.warning("check_add_device: DS4 Bluetooth controller detected. (hid_get_feature_report 0x81 failed, result=%d, buf[0]=0x%x, error=%s)", res, buf[0], hid_error(hidDevice)); - device->bt_controller = true; - for (wchar_t ch : wide_serial) - serial += static_cast(ch); + ds4_log.error("check_add_device: hid_get_device_info failed! error=%s", hid_error(hidDevice)); + hid_close(hidDevice); + return; } + device->bt_controller = (devinfo->bus_type == HID_API_BUS_BLUETOOTH); device->hidDevice = hidDevice; if (!GetCalibrationData(device)) @@ -559,10 +539,10 @@ void ds4_pad_handler::check_add_device(hid_device* hidDevice, std::string_view p u32 hw_version{}; u32 fw_version{}; - buf = {}; + std::array buf{}; buf[0] = 0xA3; - res = hid_get_feature_report(hidDevice, buf.data(), DS4_FEATURE_REPORT_0xA3_SIZE); + int res = hid_get_feature_report(hidDevice, buf.data(), DS4_FEATURE_REPORT_0xA3_SIZE); if (res != DS4_FEATURE_REPORT_0xA3_SIZE || buf[0] != 0xA3) { ds4_log.error("check_add_device: hid_get_feature_report 0xA3 failed! Could not retrieve firmware version! result=%d, buf[0]=0x%x, error=%s", res, buf[0], hid_error(hidDevice)); diff --git a/rpcs3/Input/dualsense_pad_handler.cpp b/rpcs3/Input/dualsense_pad_handler.cpp index e9e2627a3c..80712f9021 100644 --- a/rpcs3/Input/dualsense_pad_handler.cpp +++ b/rpcs3/Input/dualsense_pad_handler.cpp @@ -195,6 +195,7 @@ void dualsense_pad_handler::check_add_device(hid_device* hidDevice, std::string_ if (res < 0 || buf[0] != 0x09) { dualsense_log.error("check_add_device: hid_get_feature_report 0x09 failed! result=%d, buf[0]=0x%x, error=%s", res, buf[0], hid_error(hidDevice)); + hid_close(hidDevice); return; } @@ -955,14 +956,12 @@ int dualsense_pad_handler::send_output_report(DualSenseDevice* device) return hid_write(device->hidDevice, &report.report_id, DUALSENSE_BLUETOOTH_REPORT_SIZE); } - else - { - output_report_usb report{}; - report.report_id = 0x02; // report id for usb - report.common = common; - return hid_write(device->hidDevice, &report.report_id, DUALSENSE_USB_REPORT_SIZE); - } + output_report_usb report{}; + report.report_id = 0x02; // report id for usb + report.common = common; + + return hid_write(device->hidDevice, &report.report_id, DUALSENSE_USB_REPORT_SIZE); } void dualsense_pad_handler::apply_pad_data(const pad_ensemble& binding) diff --git a/rpcs3/Input/evdev_joystick_handler.cpp b/rpcs3/Input/evdev_joystick_handler.cpp index 474c92ed3e..c0f616aa3b 100644 --- a/rpcs3/Input/evdev_joystick_handler.cpp +++ b/rpcs3/Input/evdev_joystick_handler.cpp @@ -1079,7 +1079,8 @@ void evdev_joystick_handler::apply_input_events(const std::shared_ptr& pad) // Find out if special buttons are pressed (introduced by RPCS3). // These buttons will have a delay of one cycle, but whatever. - const bool adjust_pressure = pad->get_pressure_intensity_enabled(cfg->pressure_intensity_toggle_mode.get()); + const bool adjust_pressure = pad->get_pressure_intensity_button_active(cfg->pressure_intensity_toggle_mode.get()); + const u32 pressure_intensity_deadzone = cfg->pressure_intensity_deadzone.get(); const auto update_values = [&](bool& pressed, u16& final_value, bool is_stick_value, u32 code, u16 val) { @@ -1095,10 +1096,18 @@ void evdev_joystick_handler::apply_input_events(const std::shared_ptr& pad) { val = pad->m_pressure_intensity; } + else if (pressure_intensity_deadzone > 0) + { + // Ignore triggers, since they have their own deadzones + if (!get_is_left_trigger(m_dev, code) && !get_is_right_trigger(m_dev, code)) + { + val = NormalizeDirectedInput(val, pressure_intensity_deadzone, 255); + } + } } - pressed = true; final_value = std::max(final_value, val); + pressed = final_value > 0; } }; diff --git a/rpcs3/Input/hid_pad_handler.cpp b/rpcs3/Input/hid_pad_handler.cpp index 0ed9a06d3e..f31c12e4c5 100644 --- a/rpcs3/Input/hid_pad_handler.cpp +++ b/rpcs3/Input/hid_pad_handler.cpp @@ -214,7 +214,7 @@ void hid_pad_handler::update_devices() { hid_log.error("One or more %s pads were detected but couldn't be interacted with directly", m_type); #if defined(_WIN32) || defined(__linux__) - hid_log.error("Check https://wiki.rpcs3.net/index.php?title=Help:Controller_Configuration for intructions on how to solve this issue"); + hid_log.error("Check https://wiki.rpcs3.net/index.php?title=Help:Controller_Configuration for instructions on how to solve this issue"); #endif } else diff --git a/rpcs3/Input/keyboard_pad_handler.cpp b/rpcs3/Input/keyboard_pad_handler.cpp index ad1a725a21..283605bd31 100644 --- a/rpcs3/Input/keyboard_pad_handler.cpp +++ b/rpcs3/Input/keyboard_pad_handler.cpp @@ -112,7 +112,7 @@ void keyboard_pad_handler::Key(const u32 code, bool pressed, u16 value) } } - const bool adjust_pressure = pad.get_pressure_intensity_enabled(m_pressure_intensity_toggle_mode); + const bool adjust_pressure = pad.get_pressure_intensity_button_active(m_pressure_intensity_toggle_mode); const bool adjust_pressure_changed = pad.m_adjust_pressure_last != adjust_pressure; if (adjust_pressure_changed) @@ -153,16 +153,28 @@ void keyboard_pad_handler::Key(const u32 code, bool pressed, u16 value) if (update_button) { - button.m_pressed = button.m_actual_value > 0; - - if (button.m_pressed) + if (button.m_actual_value > 0) { // Modify pressure if necessary if the button was pressed - button.m_value = adjust_pressure ? pad.m_pressure_intensity : button.m_actual_value; + if (adjust_pressure) + { + button.m_value = pad.m_pressure_intensity; + } + else if (m_pressure_intensity_deadzone > 0) + { + button.m_value = NormalizeDirectedInput(button.m_actual_value, m_pressure_intensity_deadzone, 255); + } + else + { + button.m_value = button.m_actual_value; + } + + button.m_pressed = button.m_value > 0; } else { button.m_value = 0; + button.m_pressed = false; } } } @@ -505,12 +517,13 @@ void keyboard_pad_handler::mouseMoveEvent(QMouseEvent* event) { static int last_pos_x = 0; static int last_pos_y = 0; + const QPoint e_pos = event->pos(); - movement_x = event->x() - last_pos_x; - movement_y = event->y() - last_pos_y; + movement_x = e_pos.x() - last_pos_x; + movement_y = e_pos.y() - last_pos_y; - last_pos_x = event->x(); - last_pos_y = event->y(); + last_pos_x = e_pos.x(); + last_pos_y = e_pos.y(); } else if (m_target && m_target->isActive()) { @@ -807,8 +820,8 @@ u32 keyboard_pad_handler::GetKeyCode(const QString& keyName) const QKeySequence seq(keyName); u32 key_code = Qt::NoButton; - if (seq.count() == 1 && seq[0] != Qt::Key_unknown) - key_code = seq[0]; + if (seq.count() == 1 && seq[0].key() != Qt::Key_unknown) + key_code = seq[0].key(); else input_log.notice("GetKeyCode(%s): seq.count() = %d", keyName, seq.count()); @@ -885,6 +898,7 @@ bool keyboard_pad_handler::bindPadToDevice(std::shared_ptr pad, u8 player_i m_l_stick_multiplier = cfg->lstickmultiplier; m_r_stick_multiplier = cfg->rstickmultiplier; m_pressure_intensity_toggle_mode = cfg->pressure_intensity_toggle_mode.get(); + m_pressure_intensity_deadzone = cfg->pressure_intensity_deadzone.get(); const auto find_keys = [this](const cfg::string& name) { @@ -1048,7 +1062,7 @@ void keyboard_pad_handler::process() { if (update_sticks) { - for (int j = 0; j < static_cast(pad.m_sticks.size()); j++) + for (usz j = 0; j < pad.m_sticks.size(); j++) { const f32 stick_lerp_factor = (j < 2) ? m_l_stick_lerp_factor : m_r_stick_lerp_factor; diff --git a/rpcs3/Input/keyboard_pad_handler.h b/rpcs3/Input/keyboard_pad_handler.h index 444b772bd4..3fa69286e2 100644 --- a/rpcs3/Input/keyboard_pad_handler.h +++ b/rpcs3/Input/keyboard_pad_handler.h @@ -116,7 +116,8 @@ private: steady_clock::time_point m_button_time; f32 m_analog_lerp_factor = 1.0f; f32 m_trigger_lerp_factor = 1.0f; - bool m_pressure_intensity_toggle_mode{}; + bool m_pressure_intensity_toggle_mode = false; + u32 m_pressure_intensity_deadzone = 0; // Stick Movements steady_clock::time_point m_stick_time; @@ -124,9 +125,9 @@ private: f32 m_r_stick_lerp_factor = 1.0f; u32 m_l_stick_multiplier = 100; u32 m_r_stick_multiplier = 100; - u8 m_stick_min[4] = { 0, 0, 0, 0 }; - u8 m_stick_max[4] = { 128, 128, 128, 128 }; - u8 m_stick_val[4] = { 128, 128, 128, 128 }; + std::array m_stick_min{ 0, 0, 0, 0 }; + std::array m_stick_max{ 128, 128, 128, 128 }; + std::array m_stick_val{ 128, 128, 128, 128 }; // Mouse Movements steady_clock::time_point m_last_mouse_move_left; diff --git a/rpcs3/Input/pad_thread.cpp b/rpcs3/Input/pad_thread.cpp index 1afc9c54db..77187883b4 100644 --- a/rpcs3/Input/pad_thread.cpp +++ b/rpcs3/Input/pad_thread.cpp @@ -27,7 +27,7 @@ LOG_CHANNEL(sys_log, "SYS"); extern bool is_input_allowed(); -extern std::string g_pad_profile_override; +extern std::string g_input_config_override; namespace pad { @@ -100,19 +100,19 @@ void pad_thread::Init() handlers.clear(); - g_cfg_profile.load(); + g_cfg_input_configs.load(); - std::string active_profile = g_cfg_profile.active_profiles.get_value(pad::g_title_id); + std::string active_config = g_cfg_input_configs.active_configs.get_value(pad::g_title_id); - if (active_profile.empty()) + if (active_config.empty()) { - active_profile = g_cfg_profile.active_profiles.get_value(g_cfg_profile.global_key); + active_config = g_cfg_input_configs.active_configs.get_value(g_cfg_input_configs.global_key); } - input_log.notice("Using pad profile: '%s' (override='%s')", active_profile, g_pad_profile_override); + input_log.notice("Using input configuration: '%s' (override='%s')", active_config, g_input_config_override); // Load in order to get the pad handlers - if (!g_cfg_input.load(pad::g_title_id, active_profile)) + if (!g_cfg_input.load(pad::g_title_id, active_config)) { input_log.notice("Loaded empty pad config"); } @@ -125,7 +125,7 @@ void pad_thread::Init() } // Reload with proper defaults - if (!g_cfg_input.load(pad::g_title_id, active_profile)) + if (!g_cfg_input.load(pad::g_title_id, active_config)) { input_log.notice("Reloaded empty pad config"); } diff --git a/rpcs3/Input/product_info.h b/rpcs3/Input/product_info.h index dfa30d189a..9fb337fa27 100644 --- a/rpcs3/Input/product_info.h +++ b/rpcs3/Input/product_info.h @@ -1,6 +1,7 @@ #pragma once #include +#include "Emu/Io/pad_types.h" namespace input { @@ -14,7 +15,8 @@ namespace input harmonix_rockband_guitar, harmonix_rockband_drum_kit, harmonix_rockband_drum_kit_2, - rock_revolution_drum_kit + rock_revolution_drum_kit, + ps_move_navigation }; enum vendor_id @@ -28,21 +30,22 @@ namespace input { red_octane_gh_guitar = 0x0100, // RedOctane Guitar (Guitar Hero 4 Guitar Controller) red_octane_gh_drum_kit = 0x0120, // RedOctane Drum Kit (Guitar Hero 4 Drum Controller) - dance_dance_revolution_mat = 0x0140, // Dance Dance Revolution Dance Mat Controller dj_hero_turntable = 0x0140, // DJ Hero Turntable Controller harmonix_rockband_guitar = 0x0200, // Harmonix Guitar (Rock Band II Guitar Controller) harmonix_rockband_drum_kit = 0x0210, // Harmonix Drum Kit (Rock Band II Drum Controller) harmonix_rockband_drum_kit_2 = 0x0218, // Harmonix Pro-Drum Kit (Rock Band III Pro-Drum Controller) playstation_3_controller = 0x0268, // PlayStation 3 Controller rock_revolution_drum_kit = 0x0300, // Rock Revolution Drum Controller + ps_move_navigation = 0x042F, // PlayStation Move navigation controller + dance_dance_revolution_mat = 0x1010, // Dance Dance Revolution Dance Mat Controller }; struct product_info { product_type type; - unsigned short vendor_id; - unsigned short product_id; - unsigned int pclass_profile; // See CELL_PAD_PCLASS_PROFILE flags + u16 vendor_id; + u16 product_id; + u32 pclass_profile; // See CELL_PAD_PCLASS_PROFILE flags }; inline product_info get_product_info(product_type type) @@ -51,40 +54,172 @@ namespace input { case product_type::dance_dance_revolution_mat: { - return product_info{ type, vendor_id::konami_de, product_id::dance_dance_revolution_mat, 0x000000FF }; + constexpr u32 profile = + CELL_PAD_PCLASS_PROFILE_DANCEMAT_CIRCLE | + CELL_PAD_PCLASS_PROFILE_DANCEMAT_CROSS | + CELL_PAD_PCLASS_PROFILE_DANCEMAT_TRIANGLE | + CELL_PAD_PCLASS_PROFILE_DANCEMAT_SQUARE | + CELL_PAD_PCLASS_PROFILE_DANCEMAT_RIGHT | + CELL_PAD_PCLASS_PROFILE_DANCEMAT_LEFT | + CELL_PAD_PCLASS_PROFILE_DANCEMAT_UP | + CELL_PAD_PCLASS_PROFILE_DANCEMAT_DOWN; + return product_info{ + .type = type, + .vendor_id = vendor_id::konami_de, + .product_id = product_id::dance_dance_revolution_mat, + .pclass_profile = profile + }; } case product_type::dj_hero_turntable: { - return product_info{ type, vendor_id::sony_cea, product_id::dj_hero_turntable, 0x000007FF }; + constexpr u32 profile = + CELL_PAD_PCLASS_PROFILE_DJ_MIXER_ATTACK | + CELL_PAD_PCLASS_PROFILE_DJ_MIXER_CROSSFADER | + CELL_PAD_PCLASS_PROFILE_DJ_MIXER_DSP_DIAL | + CELL_PAD_PCLASS_PROFILE_DJ_DECK1_STREAM1 | + CELL_PAD_PCLASS_PROFILE_DJ_DECK1_STREAM2 | + CELL_PAD_PCLASS_PROFILE_DJ_DECK1_STREAM3 | + CELL_PAD_PCLASS_PROFILE_DJ_DECK1_PLATTER | + CELL_PAD_PCLASS_PROFILE_DJ_DECK2_STREAM1 | + CELL_PAD_PCLASS_PROFILE_DJ_DECK2_STREAM2 | + CELL_PAD_PCLASS_PROFILE_DJ_DECK2_STREAM3 | + CELL_PAD_PCLASS_PROFILE_DJ_DECK2_PLATTER; + return product_info{ + .type = type, + .vendor_id = vendor_id::sony_cea, + .product_id = product_id::dj_hero_turntable, + .pclass_profile = profile + }; } case product_type::harmonix_rockband_drum_kit: { - return product_info{ type, vendor_id::sony_cea, product_id::harmonix_rockband_drum_kit, 0x000000FF }; + constexpr u32 profile = + CELL_PAD_PCLASS_PROFILE_DRUM_SNARE | + CELL_PAD_PCLASS_PROFILE_DRUM_TOM | + CELL_PAD_PCLASS_PROFILE_DRUM_TOM2 | + CELL_PAD_PCLASS_PROFILE_DRUM_TOM_FLOOR | + CELL_PAD_PCLASS_PROFILE_DRUM_KICK | + CELL_PAD_PCLASS_PROFILE_DRUM_CYM_HiHAT | + CELL_PAD_PCLASS_PROFILE_DRUM_CYM_CRASH | + CELL_PAD_PCLASS_PROFILE_DRUM_CYM_RIDE; + return product_info{ + .type = type, + .vendor_id = vendor_id::sony_cea, + .product_id = product_id::harmonix_rockband_drum_kit, + .pclass_profile = profile + }; } case product_type::harmonix_rockband_drum_kit_2: { - return product_info{ type, vendor_id::sony_cea, product_id::harmonix_rockband_drum_kit_2, 0x000000BF }; + constexpr u32 profile = + CELL_PAD_PCLASS_PROFILE_DRUM_SNARE | + CELL_PAD_PCLASS_PROFILE_DRUM_TOM | + CELL_PAD_PCLASS_PROFILE_DRUM_TOM2 | + CELL_PAD_PCLASS_PROFILE_DRUM_TOM_FLOOR | + CELL_PAD_PCLASS_PROFILE_DRUM_KICK | + CELL_PAD_PCLASS_PROFILE_DRUM_CYM_HiHAT | + CELL_PAD_PCLASS_PROFILE_DRUM_CYM_RIDE; + return product_info{ + .type = type, + .vendor_id = vendor_id::sony_cea, + .product_id = product_id::harmonix_rockband_drum_kit_2, + .pclass_profile = profile + }; } case product_type::harmonix_rockband_guitar: { - return product_info{ type, vendor_id::sony_cea, product_id::harmonix_rockband_guitar, 0x00007FFF }; + constexpr u32 profile = + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_1 | + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_2 | + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_3 | + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_4 | + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_5 | + CELL_PAD_PCLASS_PROFILE_GUITAR_STRUM_UP | + CELL_PAD_PCLASS_PROFILE_GUITAR_STRUM_DOWN | + CELL_PAD_PCLASS_PROFILE_GUITAR_WHAMMYBAR | + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_H1 | + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_H2 | + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_H3 | + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_H4 | + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_H5 | + CELL_PAD_PCLASS_PROFILE_GUITAR_5WAY_EFFECT | + CELL_PAD_PCLASS_PROFILE_GUITAR_TILT_SENS; + return product_info{ + .type = type, + .vendor_id = vendor_id::sony_cea, + .product_id = product_id::harmonix_rockband_guitar, + .pclass_profile = profile + }; } case product_type::red_octane_gh_drum_kit: { - return product_info{ type, vendor_id::sony_cea, product_id::red_octane_gh_drum_kit, 0x000000BB }; + constexpr u32 profile = + CELL_PAD_PCLASS_PROFILE_DRUM_SNARE | + CELL_PAD_PCLASS_PROFILE_DRUM_TOM | + CELL_PAD_PCLASS_PROFILE_DRUM_TOM_FLOOR | + CELL_PAD_PCLASS_PROFILE_DRUM_KICK | + CELL_PAD_PCLASS_PROFILE_DRUM_CYM_HiHAT | + CELL_PAD_PCLASS_PROFILE_DRUM_CYM_RIDE; + return product_info{ + .type = type, + .vendor_id = vendor_id::sony_cea, + .product_id = product_id::red_octane_gh_drum_kit, + .pclass_profile = profile + }; } case product_type::red_octane_gh_guitar: { - return product_info{ type, vendor_id::sony_cea, product_id::red_octane_gh_guitar, 0x000000FF }; + constexpr u32 profile = + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_1 | + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_2 | + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_3 | + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_4 | + CELL_PAD_PCLASS_PROFILE_GUITAR_FRET_5 | + CELL_PAD_PCLASS_PROFILE_GUITAR_STRUM_UP | + CELL_PAD_PCLASS_PROFILE_GUITAR_STRUM_DOWN | + CELL_PAD_PCLASS_PROFILE_GUITAR_WHAMMYBAR; + return product_info{ + .type = type, + .vendor_id = vendor_id::sony_cea, + .product_id = product_id::red_octane_gh_guitar, + .pclass_profile = profile + }; } case product_type::rock_revolution_drum_kit: { - return product_info{ type, vendor_id::sony_cea, product_id::rock_revolution_drum_kit, 0x000000FB }; + constexpr u32 profile = + CELL_PAD_PCLASS_PROFILE_DRUM_SNARE | + CELL_PAD_PCLASS_PROFILE_DRUM_TOM | + CELL_PAD_PCLASS_PROFILE_DRUM_TOM_FLOOR | + CELL_PAD_PCLASS_PROFILE_DRUM_KICK | + CELL_PAD_PCLASS_PROFILE_DRUM_CYM_HiHAT | + CELL_PAD_PCLASS_PROFILE_DRUM_CYM_CRASH | + CELL_PAD_PCLASS_PROFILE_DRUM_CYM_RIDE; + return product_info{ + .type = type, + .vendor_id = vendor_id::sony_cea, + .product_id = product_id::rock_revolution_drum_kit, + .pclass_profile = profile + }; + } + case product_type::ps_move_navigation: + { + return product_info{ + .type = type, + .vendor_id = vendor_id::sony_corp, + .product_id = product_id::ps_move_navigation, + .pclass_profile = 0x0 + }; } case product_type::playstation_3_controller: default: // GCC doesn't like it when there is no return value if if all enum values are covered { - return product_info{ type, vendor_id::sony_corp, product_id::playstation_3_controller, 0x0 }; + return product_info{ + .type = type, + .vendor_id = vendor_id::sony_corp, + .product_id = product_id::playstation_3_controller, + .pclass_profile = 0x0 + }; } } } @@ -94,14 +229,14 @@ namespace input switch (class_id) { default: - case 0: // CELL_PAD_PCLASS_TYPE_STANDARD + case CELL_PAD_PCLASS_TYPE_STANDARD: { return { get_product_info(product_type::playstation_3_controller) }; } - case 1: // CELL_PAD_PCLASS_TYPE_GUITAR + case CELL_PAD_PCLASS_TYPE_GUITAR: { return { @@ -109,7 +244,7 @@ namespace input get_product_info(product_type::harmonix_rockband_guitar) }; } - case 2: // CELL_PAD_PCLASS_TYPE_DRUM + case CELL_PAD_PCLASS_TYPE_DRUM: { return { @@ -119,25 +254,25 @@ namespace input get_product_info(product_type::rock_revolution_drum_kit) }; } - case 3: // CELL_PAD_PCLASS_TYPE_DJ + case CELL_PAD_PCLASS_TYPE_DJ: { return { get_product_info(product_type::dj_hero_turntable) }; } - case 4: // CELL_PAD_PCLASS_TYPE_DANCEMAT + case CELL_PAD_PCLASS_TYPE_DANCEMAT: { return { get_product_info(product_type::dance_dance_revolution_mat) }; } - case 5: // CELL_PAD_PCLASS_TYPE_NAVIGATION + case CELL_PAD_PCLASS_TYPE_NAVIGATION: { return { - get_product_info(product_type::playstation_3_controller) + get_product_info(product_type::ps_move_navigation) }; } } diff --git a/rpcs3/Input/sdl_pad_handler.cpp b/rpcs3/Input/sdl_pad_handler.cpp index 925eeb7f29..f25ab21125 100644 --- a/rpcs3/Input/sdl_pad_handler.cpp +++ b/rpcs3/Input/sdl_pad_handler.cpp @@ -2,6 +2,8 @@ #include "stdafx.h" #include "sdl_pad_handler.h" +#include "Emu/system_utils.hpp" +#include "Emu/system_config.h" LOG_CHANNEL(sdl_log, "SDL"); @@ -217,6 +219,24 @@ bool sdl_pad_handler::Init() } }, nullptr); + if (g_cfg.io.load_sdl_mappings) + { + const std::string db_path = rpcs3::utils::get_input_config_root() + "gamecontrollerdb.txt"; + sdl_log.notice("Adding mappings from file '%s'", db_path); + + if (fs::is_file(db_path)) + { + if (SDL_GameControllerAddMappingsFromFile(db_path.c_str()) < 0) + { + sdl_log.error("Could not add mappings from file '%s'! SDL Error: %s", db_path, SDL_GetError()); + } + } + else + { + sdl_log.error("Could not add mappings from file '%s'! File does not exist!", db_path); + } + } + m_is_init = true; enumerate_devices(); diff --git a/rpcs3/Loader/ELF.h b/rpcs3/Loader/ELF.h index c7340b7115..34bb06c267 100644 --- a/rpcs3/Loader/ELF.h +++ b/rpcs3/Loader/ELF.h @@ -4,6 +4,8 @@ #include "../../Utilities/File.h" #include "../../Utilities/bit_set.h" +#include + enum class elf_os : u8 { none = 0, @@ -191,10 +193,21 @@ template class en_t, typename sz_t> struct elf_shdata final : elf_shdr { std::vector bin{}; + std::span bin_view{}; using base = elf_shdr; elf_shdata() = default; + + std::span get_bin() const + { + if (!bin_view.empty()) + { + return bin_view; + } + + return {bin.data(), bin.size()}; + } }; // ELF loading options @@ -247,6 +260,8 @@ public: std::vector progs{}; std::vector shdrs{}; + usz highest_offset = 0; + public: elf_object() = default; @@ -257,6 +272,8 @@ public: elf_error open(const fs::file& stream, u64 offset = 0, bs_t opts = {}) { + highest_offset = 0; + // Check stream if (!stream) return set_error(elf_error::stream); @@ -309,6 +326,7 @@ public: stream.seek(offset + header.e_phoff); if (!stream.read(_phdrs, header.e_phnum)) return set_error(elf_error::stream_phdrs); + highest_offset = std::max(highest_offset, stream.pos()); } if (!(opts & elf_opt::no_sections)) @@ -316,6 +334,7 @@ public: stream.seek(offset + header.e_shoff); if (!stream.read(_shdrs, header.e_shnum)) return set_error(elf_error::stream_shdrs); + highest_offset = std::max(highest_offset, stream.pos()); } progs.clear(); @@ -329,6 +348,7 @@ public: stream.seek(offset + hdr.p_offset); if (!stream.read(progs.back().bin, hdr.p_filesz)) return set_error(elf_error::stream_data); + highest_offset = std::max(highest_offset, stream.pos()); } } @@ -340,6 +360,26 @@ public: if (!(opts & elf_opt::no_data) && is_memorizable_section(shdr.sh_type, shdr.sh_flags())) { + usz p_index = umax; + + for (const auto& hdr : _phdrs) + { + // Try to find it in phdr data instead of allocating new section + p_index++; + + if (hdr.p_offset <= shdr.sh_offset && shdr.sh_offset + shdr.sh_size - 1 <= hdr.p_offset + hdr.p_filesz - 1) + { + const auto& prog = ::at32(progs, p_index); + shdrs.back().bin_view = {prog.bin.data() + shdr.sh_offset - hdr.p_offset, shdr.sh_size}; + } + } + + if (!shdrs.back().bin_view.empty()) + { + // Optimized + continue; + } + stream.seek(offset + shdr.sh_offset); if (!stream.read(shdrs.back().bin, shdr.sh_size)) return set_error(elf_error::stream_data); @@ -400,14 +440,48 @@ public: stream.write(shdr_t{}); } - for (shdr_t shdr : shdrs) + for (const auto& shdr : shdrs) { + shdr_t out = static_cast(shdr); + if (is_memorizable_section(shdr.sh_type, shdr.sh_flags())) { - shdr.sh_offset = std::exchange(off, off + shdr.sh_size); + usz p_index = umax; + usz data_base = header.e_shoff + u32{sizeof(shdr_t)} * ::size32(shdrs); + bool result = false; + + for (const auto& hdr : progs) + { + if (shdr.bin_view.empty()) + { + break; + } + + // Try to find it in phdr data instead of writing new section + p_index++; + + // Rely on previous sh_offset value! + if (hdr.p_offset <= shdr.sh_offset && shdr.sh_offset + shdr.sh_size - 1 <= hdr.p_offset + hdr.p_filesz - 1) + { + out.sh_offset = data_base + shdr.sh_offset - hdr.p_offset; + result = true; + break; + } + + data_base += ::at32(progs, p_index).p_filesz; + } + + if (result) + { + // Optimized + } + else + { + out.sh_offset = std::exchange(off, off + shdr.sh_size); + } } - stream.write(shdr); + stream.write(static_cast(out)); } // Write data @@ -418,7 +492,7 @@ public: for (const auto& shdr : shdrs) { - if (!is_memorizable_section(shdr.sh_type, shdr.sh_flags())) + if (!is_memorizable_section(shdr.sh_type, shdr.sh_flags()) || !shdr.bin_view.empty()) { continue; } diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index 17051eb305..4aaaf7ef2d 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -611,6 +611,7 @@ + diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index 0adf76dedf..fb5bda5d41 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -2371,6 +2371,9 @@ Emu\GPU\RSX\Common + + Utilities + diff --git a/rpcs3/headless_application.cpp b/rpcs3/headless_application.cpp index a7bbdee44b..763987be3a 100644 --- a/rpcs3/headless_application.cpp +++ b/rpcs3/headless_application.cpp @@ -60,7 +60,7 @@ void headless_application::InitializeCallbacks() return false; }; - callbacks.call_from_main_thread = [this](std::function func, atomic_t* wake_up) + callbacks.call_from_main_thread = [this](std::function func, atomic_t* wake_up) { RequestCallFromMainThread(std::move(func), wake_up); }; @@ -166,7 +166,7 @@ void headless_application::InitializeCallbacks() /** * Using connects avoids timers being unable to be used in a non-qt thread. So, even if this looks stupid to just call func, it's succinct. */ -void headless_application::CallFromMainThread(const std::function& func, atomic_t* wake_up) +void headless_application::CallFromMainThread(const std::function& func, atomic_t* wake_up) { func(); diff --git a/rpcs3/headless_application.h b/rpcs3/headless_application.h index aa283b01fd..5208861236 100644 --- a/rpcs3/headless_application.h +++ b/rpcs3/headless_application.h @@ -30,8 +30,8 @@ private: } Q_SIGNALS: - void RequestCallFromMainThread(std::function func, atomic_t* wake_up); + void RequestCallFromMainThread(std::function func, atomic_t* wake_up); private Q_SLOTS: - static void CallFromMainThread(const std::function& func, atomic_t* wake_up); + static void CallFromMainThread(const std::function& func, atomic_t* wake_up); }; diff --git a/rpcs3/main.cpp b/rpcs3/main.cpp index 08716c4f42..e5c4928b84 100644 --- a/rpcs3/main.cpp +++ b/rpcs3/main.cpp @@ -1,4 +1,4 @@ -// Qt5.10+ frontend implementation for rpcs3. Known to work on Windows, Linux, Mac +// Qt6 frontend implementation for rpcs3. Known to work on Windows, Linux, Mac // by Sacha Refshauge, Megamouse and flash-fire #include @@ -55,6 +55,7 @@ DYNAMIC_IMPORT("ntdll.dll", NtSetTimerResolution, NTSTATUS(ULONG DesiredResoluti #ifdef __linux__ #include #include +#include #endif #if defined(__APPLE__) @@ -87,7 +88,7 @@ static atomic_t s_headless = false; static atomic_t s_no_gui = false; static atomic_t s_argv0; -std::string g_pad_profile_override; +std::string g_input_config_override; extern thread_local std::string(*g_tls_log_prefix)(); extern thread_local std::string_view g_tls_serialize_name; @@ -290,7 +291,7 @@ constexpr auto arg_styles = "styles"; constexpr auto arg_style = "style"; constexpr auto arg_stylesheet = "stylesheet"; constexpr auto arg_config = "config"; -constexpr auto arg_pad_profile = "pad-profile"; // only useful with no-gui +constexpr auto arg_input_config = "input-config"; // only useful with no-gui constexpr auto arg_q_debug = "qDebug"; constexpr auto arg_error = "error"; constexpr auto arg_updating = "updating"; @@ -349,9 +350,6 @@ QCoreApplication* create_application(int& argc, char* argv[]) use_high_dpi = "1" == qEnvironmentVariable("QT_ENABLE_HIGHDPI_SCALING", high_dpi_setting); } - // AA_EnableHighDpiScaling has to be set before creating a QApplication - QApplication::setAttribute(use_high_dpi ? Qt::AA_EnableHighDpiScaling : Qt::AA_DisableHighDpiScaling); - if (use_high_dpi) { // Set QT_SCALE_FACTOR_ROUNDING_POLICY from environment. Defaults to cli argument, which defaults to PassThrough. @@ -424,7 +422,7 @@ template <> void fmt_class_string>::format(std::string& out, u64 arg) { const std::time_t dateTime = std::chrono::system_clock::to_time_t(get_object(arg)); - out += date_time::fmt_time("%Y-%m-%eT%H:%M:%S", dateTime); + out += date_time::fmt_time("%Y-%m-%dT%H:%M:%S", dateTime); } void run_platform_sanity_checks() @@ -446,6 +444,11 @@ int main(int argc, char** argv) const u64 intro_time = (intro_stats.ru_utime.tv_sec + intro_stats.ru_stime.tv_sec) * 1000000000ull + (intro_stats.ru_utime.tv_usec + intro_stats.ru_stime.tv_usec) * 1000ull; #endif +#ifdef __linux__ + // Set timerslack value for Linux. The default value is 50,000ns. Change this to just 1 since we value precise timers. + prctl(PR_SET_TIMERSLACK, 1, 0, 0, 0); +#endif + s_argv0 = argv[0]; // Save for report_fatal_error // Only run RPCS3 to display an error @@ -649,8 +652,8 @@ int main(int argc, char** argv) parser.addOption(QCommandLineOption(arg_stylesheet, "Loads a custom stylesheet.", "path", "")); const QCommandLineOption config_option(arg_config, "Forces the emulator to use this configuration file for CLI-booted game.", "path", ""); parser.addOption(config_option); - const QCommandLineOption pad_profile_option(arg_pad_profile, "Forces the emulator to use this pad profile file for CLI-booted game.", "name", ""); - parser.addOption(pad_profile_option); + const QCommandLineOption input_config_option(arg_input_config, "Forces the emulator to use this input config file for CLI-booted game.", "name", ""); + parser.addOption(input_config_option); const QCommandLineOption installfw_option(arg_installfw, "Forces the emulator to install this firmware file.", "path", ""); parser.addOption(installfw_option); const QCommandLineOption installpkg_option(arg_installpkg, "Forces the emulator to install this pkg file.", "path", ""); @@ -974,8 +977,6 @@ int main(int argc, char** argv) if (gui_application* gui_app = qobject_cast(app.data())) { - gui_app->setAttribute(Qt::AA_UseHighDpiPixmaps); - gui_app->setAttribute(Qt::AA_DisableWindowContextHelpButton); gui_app->setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); gui_app->SetShowGui(!s_no_gui); @@ -1048,6 +1049,11 @@ int main(int argc, char** argv) } } +// Set timerslack value for Linux. The default value is 50,000ns. Change this to just 1 since we value precise timers. +#ifdef __linux__ + prctl(PR_SET_TIMERSLACK, 1, 0, 0, 0); +#endif + #ifdef _WIN32 // Create dummy permanent low resolution timer to workaround messing with system timer resolution QTimer* dummy_timer = new QTimer(app.data()); @@ -1057,7 +1063,7 @@ int main(int argc, char** argv) bool got_timer_resolution = NtQueryTimerResolution(&min_res, &max_res, &orig_res) == 0; // Set 0.5 msec timer resolution for best performance - // - As QT5 timers (QTimer) sets the timer resolution to 1 msec, override it here. + // - As QT timers (QTimer) sets the timer resolution to 1 msec, override it here. if (parser.value(arg_timer).toStdString() == "1") { ULONG new_res; @@ -1283,18 +1289,18 @@ int main(int argc, char** argv) } } - if (parser.isSet(arg_pad_profile)) + if (parser.isSet(arg_input_config)) { if (!s_no_gui) { - report_fatal_error(fmt::format("The option '%s' can only be used in combination with '%s'.", arg_pad_profile, arg_no_gui)); + report_fatal_error(fmt::format("The option '%s' can only be used in combination with '%s'.", arg_input_config, arg_no_gui)); } - g_pad_profile_override = parser.value(pad_profile_option).toStdString(); + g_input_config_override = parser.value(input_config_option).toStdString(); - if (g_pad_profile_override.empty()) + if (g_input_config_override.empty()) { - report_fatal_error(fmt::format("Pad profile name is empty")); + report_fatal_error(fmt::format("Input config file name is empty")); } } diff --git a/rpcs3/main_application.cpp b/rpcs3/main_application.cpp index 35100e327e..c3d37658ba 100644 --- a/rpcs3/main_application.cpp +++ b/rpcs3/main_application.cpp @@ -315,7 +315,7 @@ EmuCallbacks main_application::CreateCallbacks() image = image.convertToFormat(QImage::Format::Format_RGBA8888); } - std::memcpy(dst, image.constBits(), std::min(4 * target_width * target_height, image.height() * image.bytesPerLine())); + std::memcpy(dst, image.constBits(), std::min(target_width * target_height * 4LL, image.height() * image.bytesPerLine())); success = true; sys_log.notice("get_scaled_image scaled image: path='%s', width=%d, height=%d", path, width, height); } diff --git a/rpcs3/qt/etc/qt.conf b/rpcs3/qt/etc/qt.conf index 8a60d8561e..af6127460f 100644 --- a/rpcs3/qt/etc/qt.conf +++ b/rpcs3/qt/etc/qt.conf @@ -1,4 +1,4 @@ [Paths] -Prefix = qt/ +Prefix = qt6/ Plugins = plugins Translations = translations diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index dd6818ca48..afe710087c 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -1,4 +1,4 @@ - + @@ -71,8 +71,8 @@ - ..\3rdparty\SoundTouch\soundtouch\include;..\3rdparty\cubeb\extra;..\3rdparty\cubeb\cubeb\include\;..\3rdparty\flatbuffers\include;..\3rdparty\wolfssl\wolfssl;..\3rdparty\curl\curl\include;..\3rdparty\rtmidi\rtmidi;..\3rdparty\libusb\libusb\libusb;$(VULKAN_SDK)\Include;..\3rdparty\libsdl-org\SDL\include;..\3rdparty\XAudio2Redist\include;$(QTDIR)\include;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtCore;.\release;$(QTDIR)\mkspecs\win32-msvc2015;.\QTGeneratedFiles\$(ConfigurationName);.\QTGeneratedFiles;$(QTDIR)\include\QtWinExtras;$(QTDIR)\include\QtConcurrent;$(QTDIR)\include\QtMultimedia;$(QTDIR)\include\QtMultimediaWidgets;$(QTDIR)\include\QtSvg;%(AdditionalIncludeDirectories) - -Zc:strictStrings -Zc:throwingNew- -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) + ..\3rdparty\SoundTouch\soundtouch\include;..\3rdparty\cubeb\extra;..\3rdparty\cubeb\cubeb\include\;..\3rdparty\flatbuffers\include;..\3rdparty\wolfssl\wolfssl;..\3rdparty\curl\curl\include;..\3rdparty\rtmidi\rtmidi;..\3rdparty\libusb\libusb\libusb;$(VULKAN_SDK)\Include;..\3rdparty\libsdl-org\SDL\include;..\3rdparty\XAudio2Redist\include;$(QTDIR)\include;$(QTDIR)\include\QtCore;$(QTDIR)\include\QtCore5Compat;$(QTDIR)\include\QtConcurrent;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtSvg;$(QTDIR)\include\QtSvgWidgets;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtMultimedia;$(QTDIR)\mkspecs\win32-msvc;.\release;.\QTGeneratedFiles\$(ConfigurationName);.\QTGeneratedFiles;%(AdditionalIncludeDirectories) + /Zc:__cplusplus -Zc:strictStrings -Zc:throwingNew- -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) release\ false ProgramDatabase @@ -89,7 +89,7 @@ TurnOffAllWarnings - DbgHelp.lib;Ole32.lib;gdi32.lib;..\hidapi.lib;..\libusb-1.0.lib;winmm.lib;miniupnpc_static.lib;rtmidi.lib;imm32.lib;ksuser.lib;version.lib;OpenAL32.lib;XAudio.lib;GLGSRender.lib;shlwapi.lib;VKGSRender.lib;vulkan-1.lib;wolfssl.lib;libcurl.lib;Wldap32.lib;glslang.lib;OSDependent.lib;OGLCompiler.lib;SPIRV.lib;MachineIndependent.lib;GenericCodeGen.lib;Advapi32.lib;user32.lib;zlib.lib;..\libpng16.lib;asmjit.lib;yaml-cpp.lib;discord-rpc.lib;emucore.lib;dxgi.lib;$(QTDIR)\lib\qtmain.lib;shell32.lib;$(QTDIR)\lib\Qt5Widgets.lib;$(QTDIR)\lib\Qt5Gui.lib;$(QTDIR)\lib\Qt5Core.lib;Qt5Core.lib;Qt5Gui.lib;Qt5Widgets.lib;Qt5WinExtras.lib;Qt5Concurrent.lib;7zlib.lib;SPIRV-Tools.lib;SPIRV-Tools-opt.lib;Qt5Multimedia.lib;Qt5MultimediaWidgets.lib;Qt5Svg.lib;libcubeb.lib;cubeb.lib;soundtouch.lib;Avrt.lib;SDL.lib;%(AdditionalDependencies) + DbgHelp.lib;Ole32.lib;gdi32.lib;..\hidapi.lib;..\libusb-1.0.lib;winmm.lib;miniupnpc_static.lib;rtmidi.lib;imm32.lib;ksuser.lib;version.lib;OpenAL32.lib;XAudio.lib;GLGSRender.lib;shlwapi.lib;VKGSRender.lib;vulkan-1.lib;wolfssl.lib;libcurl.lib;Wldap32.lib;glslang.lib;OSDependent.lib;OGLCompiler.lib;SPIRV.lib;MachineIndependent.lib;GenericCodeGen.lib;Advapi32.lib;user32.lib;zlib.lib;..\libpng16.lib;asmjit.lib;yaml-cpp.lib;discord-rpc.lib;emucore.lib;dxgi.lib;shell32.lib;Qt6Core.lib;Qt6Gui.lib;Qt6Widgets.lib;Qt6Concurrent.lib;Qt6Core5Compat.lib;Qt6Multimedia.lib;Qt6MultimediaWidgets.lib;Qt6Svg.lib;Qt6SvgWidgets.lib;7zlib.lib;SPIRV-Tools.lib;SPIRV-Tools-opt.lib;libcubeb.lib;cubeb.lib;soundtouch.lib;Avrt.lib;SDL.lib;%(AdditionalDependencies) ..\3rdparty\OpenAL\libs\Win64;..\3rdparty\glslang\build\hlsl\Release;..\3rdparty\glslang\build\SPIRV\Release;..\3rdparty\glslang\build\OGLCompilersDLL\Release;..\3rdparty\glslang\build\glslang\OSDependent\Windows\Release;..\3rdparty\glslang\build\glslang\Release;..\3rdparty\SPIRV\build\source\Release;..\3rdparty\SPIRV\build\source\opt\Release;..\lib\$(CONFIGURATION)-$(PLATFORM);..\3rdparty\XAudio2Redist\libs;..\3rdparty\discord-rpc\lib;$(QTDIR)\lib;%(AdditionalLibraryDirectories);$(VULKAN_SDK)\Lib "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions) true @@ -102,6 +102,7 @@ true 0x10000 xaudio2_9redist.dll + mainCRTStartup Unsigned @@ -113,7 +114,7 @@ - $(QTDIR)\bin\windeployqt --no-angle --no-opengl-sw --no-translations --no-quick --plugindir "$(TargetDir)qt\plugins" --release "$(TargetPath)" + $(QTDIR)\bin\windeployqt --no-compiler-runtime --no-opengl-sw --no-patchqt --no-translations --no-quick --no-system-d3d-compiler --no-quick-import --plugindir "$(TargetDir)qt6\plugins" --release "$(TargetPath)" @@ -123,7 +124,7 @@ - ..\3rdparty\SoundTouch\soundtouch\include;..\3rdparty\cubeb\extra;..\3rdparty\cubeb\cubeb\include\;..\3rdparty\flatbuffers\include;..\3rdparty\wolfssl\wolfssl;..\3rdparty\curl\curl\include;..\3rdparty\rtmidi\rtmidi;..\3rdparty\libusb\libusb\libusb;$(VULKAN_SDK)\Include;..\3rdparty\XAudio2Redist\include;$(QTDIR)\include;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtCore;.\debug;$(QTDIR)\mkspecs\win32-msvc2015;.\QTGeneratedFiles\$(ConfigurationName);.\QTGeneratedFiles;$(QTDIR)\include\QtWinExtras;$(QTDIR)\include\QtConcurrent;$(QTDIR)\include\QtMultimedia;$(QTDIR)\include\QtMultimediaWidgets;$(QTDIR)\include\QtSvg;%(AdditionalIncludeDirectories) + ..\3rdparty\SoundTouch\soundtouch\include;..\3rdparty\cubeb\extra;..\3rdparty\cubeb\cubeb\include\;..\3rdparty\flatbuffers\include;..\3rdparty\wolfssl\wolfssl;..\3rdparty\curl\curl\include;..\3rdparty\rtmidi\rtmidi;..\3rdparty\libusb\libusb\libusb;$(VULKAN_SDK)\Include;..\3rdparty\XAudio2Redist\include;$(QTDIR)\include;$(QTDIR)\include\QtCore;$(QTDIR)\include\QtCore5Compat;$(QTDIR)\include\QtConcurrent;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtSvg;$(QTDIR)\include\QtSvgWidgets;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtMultimedia;$(QTDIR)\mkspecs\win32-msvc;.\debug;.\QTGeneratedFiles\$(ConfigurationName);.\QTGeneratedFiles;%(AdditionalIncludeDirectories) -Zc:strictStrings -Zc:throwingNew- -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) debug\ false @@ -140,7 +141,7 @@ $(IntDir)vc$(PlatformToolsetVersion).pdb - DbgHelp.lib;Ole32.lib;gdi32.lib;..\hidapi.lib;..\libusb-1.0.lib;winmm.lib;miniupnpc_static.lib;rtmidi.lib;ksuser.lib;OpenAL32.lib;XAudio.lib;GLGSRender.lib;shlwapi.lib;VKGSRender.lib;vulkan-1.lib;wolfssl.lib;libcurl.lib;Wldap32.lib;glslangd.lib;OSDependentd.lib;OGLCompilerd.lib;SPIRVd.lib;MachineIndependentd.lib;GenericCodeGend.lib;Advapi32.lib;user32.lib;zlib.lib;..\libpng16.lib;asmjit.lib;yaml-cpp.lib;discord-rpc.lib;emucore.lib;dxgi.lib;$(QTDIR)\lib\qtmaind.lib;shell32.lib;$(QTDIR)\lib\Qt5Widgetsd.lib;$(QTDIR)\lib\Qt5Guid.lib;$(QTDIR)\lib\Qt5Cored.lib;Qt5Cored.lib;Qt5Guid.lib;Qt5Widgetsd.lib;Qt5WinExtrasd.lib;Qt5Concurrentd.lib;7zlib.lib;SPIRV-Tools.lib;SPIRV-Tools-opt.lib;Qt5Multimediad.lib;Qt5MultimediaWidgetsd.lib;Qt5Svgd.lib;libcubeb.lib;cubeb.lib;soundtouch.lib;Avrt.lib;%(AdditionalDependencies) + DbgHelp.lib;Ole32.lib;gdi32.lib;..\hidapi.lib;..\libusb-1.0.lib;winmm.lib;miniupnpc_static.lib;rtmidi.lib;ksuser.lib;OpenAL32.lib;XAudio.lib;GLGSRender.lib;shlwapi.lib;VKGSRender.lib;vulkan-1.lib;wolfssl.lib;libcurl.lib;Wldap32.lib;glslangd.lib;OSDependentd.lib;OGLCompilerd.lib;SPIRVd.lib;MachineIndependentd.lib;GenericCodeGend.lib;Advapi32.lib;user32.lib;zlib.lib;..\libpng16.lib;asmjit.lib;yaml-cpp.lib;discord-rpc.lib;emucore.lib;dxgi.lib;shell32.lib;qtmaind.lib;Qt6Cored.lib;Qt6Guid.lib;Qt6Widgetsd.lib;Qt6Concurrentd.lib;Qt6Multimediad.lib;Qt6MultimediaWidgetsd.lib;Qt6Svgd.lib;Qt6SvgWidgetsd.lib;7zlib.lib;SPIRV-Tools.lib;SPIRV-Tools-opt.lib;libcubeb.lib;cubeb.lib;soundtouch.lib;Avrt.lib;%(AdditionalDependencies) ..\3rdparty\OpenAL\libs\Win64;..\3rdparty\glslang\build\hlsl\Debug;..\3rdparty\glslang\build\SPIRV\Debug;..\3rdparty\glslang\build\OGLCompilersDLL\Debug;..\3rdparty\glslang\build\glslang\OSDependent\Windows\Debug;..\3rdparty\glslang\build\glslang\Debug;..\3rdparty\SPIRV\build\source\opt\Debug;..\3rdparty\XAudio2Redist\libs;..\3rdparty\discord-rpc\lib;..\lib\$(CONFIGURATION)-$(PLATFORM);$(QTDIR)\lib;%(AdditionalLibraryDirectories);$(VULKAN_SDK)\Lib "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" /VERBOSE %(AdditionalOptions) true @@ -165,7 +166,7 @@ - $(QTDIR)\bin\windeployqt --no-angle --no-opengl-sw --no-translations --no-quick --plugindir "$(TargetDir)qt\plugins" --debug "$(TargetPath)" + $(QTDIR)\bin\windeployqt --no-compiler-runtime --no-opengl-sw --no-patchqt --no-translations --no-quick --no-system-d3d-compiler --no-quick-import --plugindir "$(TargetDir)qt6\plugins" --debug "$(TargetPath)" @@ -763,7 +764,7 @@ - + @@ -1482,7 +1483,7 @@ .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent" - + $(QTDIR)\bin\moc.exe;%(FullPath) Moc%27ing %(Identity)... @@ -1892,6 +1893,20 @@ + + + + + + + + + + + + + + @@ -1901,6 +1916,7 @@ + diff --git a/rpcs3/rpcs3.vcxproj.filters b/rpcs3/rpcs3.vcxproj.filters index 76d205b086..d758cdee03 100644 --- a/rpcs3/rpcs3.vcxproj.filters +++ b/rpcs3/rpcs3.vcxproj.filters @@ -160,6 +160,12 @@ {b927140a-5214-4628-a648-8380ae793179} + + {f4ff05a1-e8c9-44f8-ba05-2c731919a878} + + + {b3bba7ee-4f23-41b6-8ddf-e40f848015de} + @@ -807,7 +813,7 @@ Io\camera - + Io\camera @@ -1166,7 +1172,7 @@ Generated Files - + Io\camera @@ -1554,5 +1560,50 @@ StyleSheets + + Scripts + + + CI + + + CI + + + CI + + + CI + + + CI + + + CI + + + CI + + + CI + + + CI + + + CI + + + CI + + + CI + + + CI + + + CI + \ No newline at end of file diff --git a/rpcs3/rpcs3_version.cpp b/rpcs3/rpcs3_version.cpp index ed709d1f1a..a271cdc16a 100644 --- a/rpcs3/rpcs3_version.cpp +++ b/rpcs3/rpcs3_version.cpp @@ -28,7 +28,7 @@ namespace rpcs3 // Currently accessible by Windows and Linux build scripts, see implementations when doing MACOSX const utils::version& get_version() { - static constexpr utils::version version{ 0, 0, 28, utils::version_type::alpha, 1, RPCS3_GIT_VERSION }; + static constexpr utils::version version{ 0, 0, 29, utils::version_type::alpha, 1, RPCS3_GIT_VERSION }; return version; } diff --git a/rpcs3/rpcs3qt/CMakeLists.txt b/rpcs3/rpcs3qt/CMakeLists.txt index 72afb6893f..7408976176 100644 --- a/rpcs3/rpcs3qt/CMakeLists.txt +++ b/rpcs3/rpcs3qt/CMakeLists.txt @@ -65,7 +65,7 @@ add_library(rpcs3_ui STATIC progress_indicator.cpp qt_camera_error_handler.cpp qt_camera_handler.cpp - qt_camera_video_surface.cpp + qt_camera_video_sink.cpp qt_music_error_handler.cpp qt_music_handler.cpp qt_utils.cpp @@ -124,6 +124,7 @@ add_library(rpcs3_ui STATIC if(WIN32) target_sources(rpcs3_ui PUBLIC "../windows.qrc") + target_compile_definitions(rpcs3_ui PRIVATE UNICODE _UNICODE) endif() set_target_properties(rpcs3_ui @@ -140,7 +141,7 @@ target_compile_definitions(rpcs3_ui PRIVATE WIN32_LEAN_AND_MEAN) target_link_libraries(rpcs3_ui PUBLIC - 3rdparty::qt5 3rdparty::yaml-cpp + 3rdparty::qt6 3rdparty::yaml-cpp PRIVATE rpcs3_emu diff --git a/rpcs3/rpcs3qt/camera_settings_dialog.cpp b/rpcs3/rpcs3qt/camera_settings_dialog.cpp index db2d4c6b28..3e2ae3caab 100644 --- a/rpcs3/rpcs3qt/camera_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/camera_settings_dialog.cpp @@ -3,61 +3,56 @@ #include "ui_camera_settings_dialog.h" #include "Emu/Io/camera_config.h" -#include +#include +#include #include #include LOG_CHANNEL(camera_log, "Camera"); template <> -void fmt_class_string::format(std::string& out, u64 arg) +void fmt_class_string::format(std::string& out, u64 arg) { - format_enum(out, arg, [](QVideoFrame::PixelFormat value) + format_enum(out, arg, [](QVideoFrameFormat::PixelFormat value) { switch (value) { - case QVideoFrame::Format_Invalid: return "Invalid"; - case QVideoFrame::Format_ARGB32: return "ARGB32"; - case QVideoFrame::Format_ARGB32_Premultiplied: return "ARGB32_Premultiplied"; - case QVideoFrame::Format_RGB32: return "RGB32"; - case QVideoFrame::Format_RGB24: return "RGB24"; - case QVideoFrame::Format_RGB565: return "RGB565"; - case QVideoFrame::Format_RGB555: return "RGB555"; - case QVideoFrame::Format_ARGB8565_Premultiplied: return "ARGB8565_Premultiplied"; - case QVideoFrame::Format_BGRA32: return "BGRA32"; - case QVideoFrame::Format_BGRA32_Premultiplied: return "BGRA32_Premultiplied"; - case QVideoFrame::Format_BGR32: return "BGR32"; - case QVideoFrame::Format_BGR24: return "BGR24"; - case QVideoFrame::Format_BGR565: return "BGR565"; - case QVideoFrame::Format_BGR555: return "BGR555"; - case QVideoFrame::Format_BGRA5658_Premultiplied: return "BGRA5658_Premultiplied"; - case QVideoFrame::Format_AYUV444: return "AYUV444"; - case QVideoFrame::Format_AYUV444_Premultiplied: return "AYUV444_Premultiplied"; - case QVideoFrame::Format_YUV444: return "YUV444"; - case QVideoFrame::Format_YUV420P: return "YUV420P"; - case QVideoFrame::Format_YV12: return "YV12"; - case QVideoFrame::Format_UYVY: return "UYVY"; - case QVideoFrame::Format_YUYV: return "YUYV"; - case QVideoFrame::Format_NV12: return "NV12"; - case QVideoFrame::Format_NV21: return "NV21"; - case QVideoFrame::Format_IMC1: return "IMC1"; - case QVideoFrame::Format_IMC2: return "IMC2"; - case QVideoFrame::Format_IMC3: return "IMC3"; - case QVideoFrame::Format_IMC4: return "IMC4"; - case QVideoFrame::Format_Y8: return "Y8"; - case QVideoFrame::Format_Y16: return "Y16"; - case QVideoFrame::Format_Jpeg: return "Jpeg"; - case QVideoFrame::Format_CameraRaw: return "CameraRaw"; - case QVideoFrame::Format_AdobeDng: return "AdobeDng"; - case QVideoFrame::Format_ABGR32: return "ABGR32"; - case QVideoFrame::Format_YUV422P: return "YUV422P"; - case QVideoFrame::Format_User: return "User"; + case QVideoFrameFormat::Format_ARGB8888: return "ARGB8888"; + case QVideoFrameFormat::Format_ARGB8888_Premultiplied: return "ARGB8888_Premultiplied"; + case QVideoFrameFormat::Format_XRGB8888: return "XRGB8888"; + case QVideoFrameFormat::Format_BGRA8888: return "BGRA8888"; + case QVideoFrameFormat::Format_BGRA8888_Premultiplied: return "BGRA8888_Premultiplied"; + case QVideoFrameFormat::Format_BGRX8888: return "BGRX8888"; + case QVideoFrameFormat::Format_ABGR8888: return "ABGR8888"; + case QVideoFrameFormat::Format_XBGR8888: return "XBGR8888"; + case QVideoFrameFormat::Format_RGBA8888: return "RGBA8888"; + case QVideoFrameFormat::Format_RGBX8888: return "RGBX8888"; + case QVideoFrameFormat::Format_AYUV: return "AYUV"; + case QVideoFrameFormat::Format_AYUV_Premultiplied: return "AYUV_Premultiplied"; + case QVideoFrameFormat::Format_YUV420P: return "YUV420P"; + case QVideoFrameFormat::Format_YUV422P: return "YUV422P"; + case QVideoFrameFormat::Format_YV12: return "YV12"; + case QVideoFrameFormat::Format_UYVY: return "UYVY"; + case QVideoFrameFormat::Format_YUYV: return "YUYV"; + case QVideoFrameFormat::Format_NV12: return "NV12"; + case QVideoFrameFormat::Format_NV21: return "NV21"; + case QVideoFrameFormat::Format_IMC1: return "IMC1"; + case QVideoFrameFormat::Format_IMC2: return "IMC2"; + case QVideoFrameFormat::Format_IMC3: return "IMC3"; + case QVideoFrameFormat::Format_IMC4: return "IMC4"; + case QVideoFrameFormat::Format_Y8: return "Y8"; + case QVideoFrameFormat::Format_Y16: return "Y16"; + case QVideoFrameFormat::Format_P010: return "P010"; + case QVideoFrameFormat::Format_P016: return "P016"; + case QVideoFrameFormat::Format_SamplerExternalOES: return "SamplerExternalOES"; + case QVideoFrameFormat::Format_Jpeg: return "Jpeg"; + case QVideoFrameFormat::Format_SamplerRect: return "SamplerRect"; default: return unknown; } }); } -Q_DECLARE_METATYPE(QCameraInfo); +Q_DECLARE_METATYPE(QCameraDevice); camera_settings_dialog::camera_settings_dialog(QWidget* parent) : QDialog(parent) @@ -67,10 +62,11 @@ camera_settings_dialog::camera_settings_dialog(QWidget* parent) load_config(); - for (const QCameraInfo& camera_info : QCameraInfo::availableCameras()) + for (const QCameraDevice& camera_info : QMediaDevices::videoInputs()) { if (camera_info.isNull()) continue; ui->combo_camera->addItem(camera_info.description(), QVariant::fromValue(camera_info)); + camera_log.notice("Found camera: '%s'", camera_info.description()); } connect(ui->combo_camera, QOverload::of(&QComboBox::currentIndexChanged), this, &camera_settings_dialog::handle_camera_change); @@ -108,13 +104,13 @@ camera_settings_dialog::~camera_settings_dialog() void camera_settings_dialog::handle_camera_change(int index) { - if (index < 0 || !ui->combo_camera->itemData(index).canConvert()) + if (index < 0 || !ui->combo_camera->itemData(index).canConvert()) { ui->combo_settings->clear(); return; } - const QCameraInfo camera_info = ui->combo_camera->itemData(index).value(); + const QCameraDevice camera_info = ui->combo_camera->itemData(index).value(); if (camera_info.isNull()) { @@ -123,7 +119,9 @@ void camera_settings_dialog::handle_camera_change(int index) } m_camera.reset(new QCamera(camera_info)); - m_camera->setViewfinder(ui->viewfinder); + m_media_capture_session.reset(new QMediaCaptureSession(nullptr)); + m_media_capture_session->setCamera(m_camera.get()); + m_media_capture_session->setVideoSink(ui->videoWidget->videoSink()); if (!m_camera->isAvailable()) { @@ -132,42 +130,34 @@ void camera_settings_dialog::handle_camera_change(int index) return; } - m_camera->load(); - ui->combo_settings->blockSignals(true); ui->combo_settings->clear(); - QList settings = m_camera->supportedViewfinderSettings(); - std::sort(settings.begin(), settings.end(), [](const QCameraViewfinderSettings& l, const QCameraViewfinderSettings& r) -> bool + QList settings = camera_info.videoFormats(); + std::sort(settings.begin(), settings.end(), [](const QCameraFormat& l, const QCameraFormat& r) -> bool { if (l.resolution().width() > r.resolution().width()) return true; if (l.resolution().width() < r.resolution().width()) return false; if (l.resolution().height() > r.resolution().height()) return true; if (l.resolution().height() < r.resolution().height()) return false; - if (l.minimumFrameRate() > r.minimumFrameRate()) return true; - if (l.minimumFrameRate() < r.minimumFrameRate()) return false; - if (l.maximumFrameRate() > r.maximumFrameRate()) return true; - if (l.maximumFrameRate() < r.maximumFrameRate()) return false; + if (l.minFrameRate() > r.minFrameRate()) return true; + if (l.minFrameRate() < r.minFrameRate()) return false; + if (l.maxFrameRate() > r.maxFrameRate()) return true; + if (l.maxFrameRate() < r.maxFrameRate()) return false; if (l.pixelFormat() > r.pixelFormat()) return true; if (l.pixelFormat() < r.pixelFormat()) return false; - if (l.pixelAspectRatio().width() > r.pixelAspectRatio().width()) return true; - if (l.pixelAspectRatio().width() < r.pixelAspectRatio().width()) return false; - if (l.pixelAspectRatio().height() > r.pixelAspectRatio().height()) return true; - if (l.pixelAspectRatio().height() < r.pixelAspectRatio().height()) return false; return false; }); - for (const QCameraViewfinderSettings& setting : settings) + for (const QCameraFormat& setting : settings) { if (setting.isNull()) continue; - const QString description = tr("%0x%1, %2-%3 FPS, Format=%4, PixelAspectRatio=%5x%6") + const QString description = tr("%0x%1, %2-%3 FPS, Format=%4") .arg(setting.resolution().width()) .arg(setting.resolution().height()) - .arg(setting.minimumFrameRate()) - .arg(setting.maximumFrameRate()) - .arg(QString::fromStdString(fmt::format("%s", setting.pixelFormat()))) - .arg(setting.pixelAspectRatio().width()) - .arg(setting.pixelAspectRatio().height()); + .arg(setting.minFrameRate()) + .arg(setting.maxFrameRate()) + .arg(QString::fromStdString(fmt::format("%s", setting.pixelFormat()))); ui->combo_settings->addItem(description, QVariant::fromValue(setting)); } ui->combo_settings->blockSignals(false); @@ -181,37 +171,27 @@ void camera_settings_dialog::handle_camera_change(int index) // Load selected settings from config file int index = 0; bool success = false; - const std::string key = camera_info.deviceName().toStdString(); + const std::string key = camera_info.id().toStdString(); cfg_camera::camera_setting cfg_setting = g_cfg_camera.get_camera_setting(key, success); if (success) { camera_log.notice("Found config entry for camera \"%s\"", key); - // Convert to Qt data - QCameraViewfinderSettings setting; - setting.setResolution(cfg_setting.width, cfg_setting.height); - setting.setMinimumFrameRate(cfg_setting.min_fps); - setting.setMaximumFrameRate(cfg_setting.max_fps); - setting.setPixelFormat(static_cast(cfg_setting.format)); - setting.setPixelAspectRatio(cfg_setting.pixel_aspect_width, cfg_setting.pixel_aspect_height); - // Select matching drowdown entry const double epsilon = 0.001; for (int i = 0; i < ui->combo_settings->count(); i++) { - const QCameraViewfinderSettings tmp = ui->combo_settings->itemData(i).value(); + const QCameraFormat tmp = ui->combo_settings->itemData(i).value(); - if (tmp.resolution().width() == setting.resolution().width() && - tmp.resolution().height() == setting.resolution().height() && - tmp.minimumFrameRate() >= (setting.minimumFrameRate() - epsilon) && - tmp.minimumFrameRate() <= (setting.minimumFrameRate() + epsilon) && - tmp.maximumFrameRate() >= (setting.maximumFrameRate() - epsilon) && - tmp.maximumFrameRate() <= (setting.maximumFrameRate() + epsilon) && - tmp.pixelFormat() == setting.pixelFormat() && - tmp.pixelAspectRatio().width() == setting.pixelAspectRatio().width() && - tmp.pixelAspectRatio().height() == setting.pixelAspectRatio().height()) + if (tmp.resolution().width() == cfg_setting.width && + tmp.resolution().height() == cfg_setting.height && + tmp.minFrameRate() >= (cfg_setting.min_fps - epsilon) && + tmp.minFrameRate() <= (cfg_setting.min_fps + epsilon) && + tmp.maxFrameRate() >= (cfg_setting.max_fps - epsilon) && + tmp.maxFrameRate() <= (cfg_setting.max_fps + epsilon) && + tmp.pixelFormat() == static_cast(cfg_setting.format)) { index = i; break; @@ -223,14 +203,12 @@ void camera_settings_dialog::handle_camera_change(int index) ui->combo_settings->setEnabled(true); // Update config to match user interface outcome - const QCameraViewfinderSettings setting = ui->combo_settings->currentData().value(); + const QCameraFormat setting = ui->combo_settings->currentData().value(); cfg_setting.width = setting.resolution().width(); cfg_setting.height = setting.resolution().height(); - cfg_setting.min_fps = setting.minimumFrameRate(); - cfg_setting.max_fps = setting.maximumFrameRate(); + cfg_setting.min_fps = setting.minFrameRate(); + cfg_setting.max_fps = setting.maxFrameRate(); cfg_setting.format = static_cast(setting.pixelFormat()); - cfg_setting.pixel_aspect_width = setting.pixelAspectRatio().width(); - cfg_setting.pixel_aspect_height = setting.pixelAspectRatio().height(); g_cfg_camera.set_camera_setting(key, cfg_setting); } } @@ -248,23 +226,21 @@ void camera_settings_dialog::handle_settings_change(int index) return; } - if (index >= 0 && ui->combo_settings->itemData(index).canConvert() && ui->combo_camera->currentData().canConvert()) + if (index >= 0 && ui->combo_settings->itemData(index).canConvert() && ui->combo_camera->currentData().canConvert()) { - const QCameraViewfinderSettings setting = ui->combo_settings->itemData(index).value(); + const QCameraFormat setting = ui->combo_settings->itemData(index).value(); if (!setting.isNull()) { - m_camera->setViewfinderSettings(setting); + m_camera->setCameraFormat(setting); } cfg_camera::camera_setting cfg_setting; cfg_setting.width = setting.resolution().width(); cfg_setting.height = setting.resolution().height(); - cfg_setting.min_fps = setting.minimumFrameRate(); - cfg_setting.max_fps = setting.maximumFrameRate(); + cfg_setting.min_fps = setting.minFrameRate(); + cfg_setting.max_fps = setting.maxFrameRate(); cfg_setting.format = static_cast(setting.pixelFormat()); - cfg_setting.pixel_aspect_width = setting.pixelAspectRatio().width(); - cfg_setting.pixel_aspect_height = setting.pixelAspectRatio().height(); - g_cfg_camera.set_camera_setting(ui->combo_camera->currentData().value().deviceName().toStdString(), cfg_setting); + g_cfg_camera.set_camera_setting(ui->combo_camera->currentData().value().id().toStdString(), cfg_setting); } m_camera->start(); diff --git a/rpcs3/rpcs3qt/camera_settings_dialog.h b/rpcs3/rpcs3qt/camera_settings_dialog.h index 56aa29a6cf..da18f64cca 100644 --- a/rpcs3/rpcs3qt/camera_settings_dialog.h +++ b/rpcs3/rpcs3qt/camera_settings_dialog.h @@ -2,6 +2,7 @@ #include #include +#include namespace Ui { @@ -25,5 +26,6 @@ private: void save_config(); std::unique_ptr ui; - std::shared_ptr m_camera; + std::unique_ptr m_camera; + std::unique_ptr m_media_capture_session; }; diff --git a/rpcs3/rpcs3qt/camera_settings_dialog.ui b/rpcs3/rpcs3qt/camera_settings_dialog.ui index 6deef8173e..8afe262f22 100644 --- a/rpcs3/rpcs3qt/camera_settings_dialog.ui +++ b/rpcs3/rpcs3qt/camera_settings_dialog.ui @@ -57,7 +57,7 @@ - + 64 @@ -86,9 +86,9 @@ - QCameraViewfinder + QVideoWidget QWidget -
qcameraviewfinder.h
+
qvideowidget.h
1
diff --git a/rpcs3/rpcs3qt/cheat_manager.cpp b/rpcs3/rpcs3qt/cheat_manager.cpp index 218522be0e..36f797c932 100644 --- a/rpcs3/rpcs3qt/cheat_manager.cpp +++ b/rpcs3/rpcs3qt/cheat_manager.cpp @@ -852,7 +852,7 @@ cheat_manager_dialog::cheat_manager_dialog(QWidget* parent) { if (g_cheat.exist(name, offset)) { - if (QMessageBox::question(this, tr("Cheat already exist"), tr("Do you want to overwrite the existing cheat?"), QMessageBox::Ok | QMessageBox::Cancel) != QMessageBox::Ok) + if (QMessageBox::question(this, tr("Cheat already exists"), tr("Do you want to overwrite the existing cheat?"), QMessageBox::Ok | QMessageBox::Cancel) != QMessageBox::Ok) return; } diff --git a/rpcs3/rpcs3qt/config_checker.cpp b/rpcs3/rpcs3qt/config_checker.cpp index c838b8831c..3cf34955c8 100644 --- a/rpcs3/rpcs3qt/config_checker.cpp +++ b/rpcs3/rpcs3qt/config_checker.cpp @@ -64,12 +64,12 @@ bool config_checker::check_config(QString content, QString& result, bool is_log) const QString start_token = "SYS: Used configuration:\n"; const QString end_token = "\n·"; - int start = content.indexOf(start_token); - int end = -1; + qsizetype start = content.indexOf(start_token); + qsizetype end = -1; if (start >= 0) { - start += start_token.count(); + start += start_token.size(); end = content.indexOf(end_token, start); } diff --git a/rpcs3/rpcs3qt/custom_table_widget_item.cpp b/rpcs3/rpcs3qt/custom_table_widget_item.cpp index 83235e395a..d679797134 100644 --- a/rpcs3/rpcs3qt/custom_table_widget_item.cpp +++ b/rpcs3/rpcs3qt/custom_table_widget_item.cpp @@ -30,35 +30,35 @@ bool custom_table_widget_item::operator<(const QTableWidgetItem& other) const const QVariant data_l = data(m_sort_role); const QVariant data_r = other.data(m_sort_role); - const QVariant::Type type_l = data_l.type(); - const QVariant::Type type_r = data_r.type(); + const int type_l = data_l.metaType().id(); + const int type_r = data_r.metaType().id(); ensure(type_l == type_r); switch (type_l) { - case QVariant::Type::Bool: - case QVariant::Type::Int: + case QMetaType::Type::Bool: + case QMetaType::Type::Int: return data_l.toInt() < data_r.toInt(); - case QVariant::Type::UInt: + case QMetaType::Type::UInt: return data_l.toUInt() < data_r.toUInt(); - case QVariant::Type::LongLong: + case QMetaType::Type::LongLong: return data_l.toLongLong() < data_r.toLongLong(); - case QVariant::Type::ULongLong: + case QMetaType::Type::ULongLong: return data_l.toULongLong() < data_r.toULongLong(); - case QVariant::Type::Double: + case QMetaType::Type::Double: return data_l.toDouble() < data_r.toDouble(); - case QVariant::Type::Date: + case QMetaType::Type::QDate: return data_l.toDate() < data_r.toDate(); - case QVariant::Type::Time: + case QMetaType::Type::QTime: return data_l.toTime() < data_r.toTime(); - case QVariant::Type::DateTime: + case QMetaType::Type::QDateTime: return data_l.toDateTime() < data_r.toDateTime(); - case QVariant::Type::Char: - case QVariant::Type::String: + case QMetaType::Type::Char: + case QMetaType::Type::QString: return data_l.toString() < data_r.toString(); default: - fmt::throw_exception("Unimplemented type %s", QVariant::typeToName(type_l)); + fmt::throw_exception("Unimplemented type %s", QMetaType(type_l).name()); } } diff --git a/rpcs3/rpcs3qt/debugger_frame.cpp b/rpcs3/rpcs3qt/debugger_frame.cpp index b529e38359..19bbe6fa2c 100644 --- a/rpcs3/rpcs3qt/debugger_frame.cpp +++ b/rpcs3/rpcs3qt/debugger_frame.cpp @@ -122,13 +122,13 @@ debugger_frame::debugger_frame(std::shared_ptr gui_settings, QWidg hbox_b_main->addStretch(); // Misc state - m_misc_state = new QTextEdit(this); - m_misc_state->setLineWrapMode(QTextEdit::NoWrap); + m_misc_state = new QPlainTextEdit(this); + m_misc_state->setLineWrapMode(QPlainTextEdit::NoWrap); m_misc_state->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); // Registers - m_regs = new QTextEdit(this); - m_regs->setLineWrapMode(QTextEdit::NoWrap); + m_regs = new QPlainTextEdit(this); + m_regs->setLineWrapMode(QPlainTextEdit::NoWrap); m_regs->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); m_debugger_list->setFont(m_mono); @@ -699,7 +699,7 @@ void debugger_frame::keyPressEvent(QKeyEvent* event) if (!cpu->state.all_of(cpu_flag::wait + cpu_flag::dbg_pause)) { - QMessageBox::warning(this, QObject::tr("Pause the SPU Thread!"), QObject::tr("Cannot perform SPU capture due to the thread need manual pausing!")); + QMessageBox::warning(this, QObject::tr("Pause the SPU Thread!"), QObject::tr("Cannot perform SPU capture due to the thread needing manual pausing!")); return; } @@ -1147,7 +1147,7 @@ void debugger_frame::WritePanels() int loc = m_misc_state->verticalScrollBar()->value(); int hloc = m_misc_state->horizontalScrollBar()->value(); m_misc_state->clear(); - m_misc_state->setText(qstr(cpu->dump_misc())); + m_misc_state->setPlainText(qstr(cpu->dump_misc())); m_misc_state->verticalScrollBar()->setValue(loc); m_misc_state->horizontalScrollBar()->setValue(hloc); @@ -1156,7 +1156,7 @@ void debugger_frame::WritePanels() m_regs->clear(); m_last_reg_state.clear(); cpu->dump_regs(m_last_reg_state, m_dump_reg_func_data); - m_regs->setText(qstr(m_last_reg_state)); + m_regs->setPlainText(qstr(m_last_reg_state)); m_regs->verticalScrollBar()->setValue(loc); m_regs->horizontalScrollBar()->setValue(hloc); @@ -1330,7 +1330,7 @@ void debugger_frame::DoStep(bool step_over) ppu_opcode_t ppu_op{result}; const ppu_itype::type itype = g_ppu_itype.decode(ppu_op.opcode); - should_step_over = (itype == ppu_itype::BC || itype == ppu_itype::B || itype == ppu_itype::BCCTR || itype == ppu_itype::BCLR) && ppu_op.lk; + should_step_over = (itype & ppu_itype::branch && ppu_op.lk); } } @@ -1366,7 +1366,7 @@ void debugger_frame::DoStep(bool step_over) } }); - cpu->state.notify_one(s_pause_flags); + cpu->state.notify_one(); } } @@ -1412,7 +1412,7 @@ void debugger_frame::RunBtnPress() Emu.Resume(); } - cpu->state.notify_one(s_pause_flags); + cpu->state.notify_one(); m_debugger_list->EnableThreadFollowing(); } } diff --git a/rpcs3/rpcs3qt/debugger_frame.h b/rpcs3/rpcs3qt/debugger_frame.h index 4ccc62a1f3..a0381f9898 100644 --- a/rpcs3/rpcs3qt/debugger_frame.h +++ b/rpcs3/rpcs3qt/debugger_frame.h @@ -5,7 +5,7 @@ #include "custom_dock_widget.h" #include -#include +#include #include #include @@ -42,8 +42,8 @@ class debugger_frame : public custom_dock_widget debugger_list* m_debugger_list; QSplitter* m_right_splitter; QFont m_mono; - QTextEdit* m_misc_state; - QTextEdit* m_regs; + QPlainTextEdit* m_misc_state; + QPlainTextEdit* m_regs; QPushButton* m_go_to_addr; QPushButton* m_go_to_pc; QPushButton* m_btn_step; diff --git a/rpcs3/rpcs3qt/debugger_list.cpp b/rpcs3/rpcs3qt/debugger_list.cpp index 011c59146e..002ae1e0bb 100644 --- a/rpcs3/rpcs3qt/debugger_list.cpp +++ b/rpcs3/rpcs3qt/debugger_list.cpp @@ -71,8 +71,12 @@ void debugger_list::UpdateCPUData(cpu_thread* cpu, CPUDisAsm* disasm) u32 debugger_list::GetStartAddress(u32 address) { const u32 steps = m_item_count / 3; + const u32 inst_count_jump_on_step = std::min(steps, 4); - u32 result = address; + const bool is_spu = m_cpu && m_cpu->id_type() == 2; + const u32 address_mask = (is_spu ? 0x3fffc : ~3); + + u32 result = address & address_mask; if (m_cpu && m_cpu->id_type() == 0x55) { @@ -83,13 +87,43 @@ u32 debugger_list::GetStartAddress(u32 address) } else { - result = address - (steps * 4); + result = (address - (steps * 4)) & address_mask; } - if (address > m_pc || m_start_addr > address) + u32 upper_bound = (m_start_addr + (steps * 4)) & address_mask; + + if (m_cpu && m_cpu->id_type() == 0x55) + { + if (auto [count, res] = static_cast(m_cpu)->try_get_pc_of_x_cmds_backwards(0 - steps, m_start_addr); count == steps) + { + upper_bound = res; + } + } + + bool goto_addr = false; + + if (upper_bound > m_start_addr) + { + goto_addr = address < m_start_addr || address >= upper_bound; + } + else + { + // Overflowing bounds case + goto_addr = address < m_start_addr && address >= upper_bound; + } + + if (goto_addr) { m_pc = address; - m_start_addr = result; + + if (address > upper_bound && address - upper_bound < inst_count_jump_on_step * 4) + { + m_start_addr = result + inst_count_jump_on_step * 4; + } + else + { + m_start_addr = result; + } } return m_start_addr; @@ -294,8 +328,8 @@ void debugger_list::keyPressEvent(QKeyEvent* event) { case Qt::Key_PageUp: scroll(0 - m_item_count); return; case Qt::Key_PageDown: scroll(m_item_count); return; - case Qt::Key_Up: scroll(1); return; - case Qt::Key_Down: scroll(-1); return; + case Qt::Key_Up: scroll(-1); return; + case Qt::Key_Down: scroll(1); return; case Qt::Key_I: { if (event->isAutoRepeat()) diff --git a/rpcs3/rpcs3qt/elf_memory_dumping_dialog.cpp b/rpcs3/rpcs3qt/elf_memory_dumping_dialog.cpp index 0eb7f41844..b96699197a 100644 --- a/rpcs3/rpcs3qt/elf_memory_dumping_dialog.cpp +++ b/rpcs3/rpcs3qt/elf_memory_dumping_dialog.cpp @@ -49,7 +49,7 @@ elf_memory_dumping_dialog::elf_memory_dumping_dialog(u32 ppu_debugger_addr, std: m_ls_address_input = make_hex_edit(5); m_segment_flags_input = make_hex_edit(1); m_segment_flags_input->setText("0x7"); // READ WRITE EXEC - m_ppu_address_input->setText(QStringLiteral("0x%x").arg(ppu_debugger_addr & -0x10000, 2, 16)); // SPU code segments are usually 128 bytes aligned, let's make it even 64k so the user would have to type himself the lower part to avoid human errors. + m_ppu_address_input->setText(QStringLiteral("0x%1").arg(ppu_debugger_addr & -0x10000, 1, 16)); // SPU code segments are usually 128 bytes aligned, let's make it even 64k so the user would have to type himself the lower part to avoid human errors. QPushButton* add_segment_button = new QPushButton(QStringLiteral("+")); add_segment_button->setToolTip(tr("Add new segment")); @@ -170,7 +170,7 @@ void elf_memory_dumping_dialog::add_new_segment() } } - auto item = new QListWidgetItem(tr("PPU Address: 0x%0, LS Address: 0x%1, Segment Size: 0x%2, Flags: 0x%3").arg(+vm::try_get_addr(data.src_addr).first, 2, 16).arg(data.ls_addr, 2, 16).arg(data.segment_size, 2, 16).arg(data.flags, 2, 16), m_seg_list); + auto item = new QListWidgetItem(tr("PPU Address: 0x%0, LS Address: 0x%1, Segment Size: 0x%2, Flags: 0x%3").arg(+vm::try_get_addr(data.src_addr).first, 5, 16).arg(data.ls_addr, 2, 16).arg(data.segment_size, 2, 16).arg(data.flags, 2, 16), m_seg_list); item->setData(Qt::UserRole, QVariant::fromValue(data)); m_seg_list->setCurrentItem(item); } diff --git a/rpcs3/rpcs3qt/emu_settings.cpp b/rpcs3/rpcs3qt/emu_settings.cpp index 871fdf5f9c..03735f8662 100644 --- a/rpcs3/rpcs3qt/emu_settings.cpp +++ b/rpcs3/rpcs3qt/emu_settings.cpp @@ -1011,7 +1011,7 @@ QString emu_settings::GetLocalizedSetting(const QString& original, emu_settings_ case camera_flip::none: return tr("No", "Camera flip"); case camera_flip::horizontal: return tr("Flip horizontally", "Camera flip"); case camera_flip::vertical: return tr("Flip vertically", "Camera flip"); - case camera_flip::both: return tr("Flip both axis", "Camera flip"); + case camera_flip::both: return tr("Flip both axes", "Camera flip"); } break; case emu_settings_type::Camera: @@ -1283,6 +1283,14 @@ QString emu_settings::GetLocalizedSetting(const QString& original, emu_settings_ case midi_device_type::keyboard: return tr("Keyboard", "Midi Device Type"); } break; + case emu_settings_type::XFloatAccuracy: + switch (static_cast(index)) + { + case xfloat_accuracy::accurate: return tr("Accurate XFloat"); + case xfloat_accuracy::approximate: return tr("Approximate XFloat"); + case xfloat_accuracy::relaxed: return tr("Relaxed XFloat"); + case xfloat_accuracy::inaccurate: return tr("Inaccurate XFloat"); + } default: break; } diff --git a/rpcs3/rpcs3qt/emu_settings_type.h b/rpcs3/rpcs3qt/emu_settings_type.h index f4600d5b87..edf9d6cc8e 100644 --- a/rpcs3/rpcs3qt/emu_settings_type.h +++ b/rpcs3/rpcs3qt/emu_settings_type.h @@ -19,15 +19,14 @@ enum class emu_settings_type SPUDebug, MFCDebug, MaxLLVMThreads, - PPULLVMPrecompilation, + LLVMPrecompilation, EnableTSX, AccurateGETLLAR, AccurateSpuDMA, AccurateClineStores, AccurateRSXAccess, FIFOAccuracy, - AccurateXFloat, - ApproximateXFloat, + XFloatAccuracy, AccuratePPU128Loop, MFCCommandsShuffling, NumPPUThreads, @@ -158,6 +157,7 @@ enum class emu_settings_type Turntable, GHLtar, MidiDevices, + SDLMappings, // Misc ExitRPCS3OnFinish, @@ -205,15 +205,14 @@ inline static const QMap settings_location = { emu_settings_type::SPUDebug, { "Core", "SPU Debug"}}, { emu_settings_type::MFCDebug, { "Core", "MFC Debug"}}, { emu_settings_type::MaxLLVMThreads, { "Core", "Max LLVM Compile Threads"}}, - { emu_settings_type::PPULLVMPrecompilation, { "Core", "PPU LLVM Precompilation"}}, + { emu_settings_type::LLVMPrecompilation, { "Core", "LLVM Precompilation"}}, { emu_settings_type::EnableTSX, { "Core", "Enable TSX"}}, { emu_settings_type::AccurateGETLLAR, { "Core", "Accurate GETLLAR"}}, { emu_settings_type::AccurateSpuDMA, { "Core", "Accurate SPU DMA"}}, { emu_settings_type::AccurateClineStores, { "Core", "Accurate Cache Line Stores"}}, { emu_settings_type::AccurateRSXAccess, { "Core", "Accurate RSX reservation access"}}, { emu_settings_type::FIFOAccuracy, { "Core", "RSX FIFO Accuracy"}}, - { emu_settings_type::AccurateXFloat, { "Core", "Accurate xfloat"}}, - { emu_settings_type::ApproximateXFloat, { "Core", "Approximate xfloat"}}, + { emu_settings_type::XFloatAccuracy, { "Core", "XFloat Accuracy"}}, { emu_settings_type::MFCCommandsShuffling, { "Core", "MFC Commands Shuffling Limit"}}, { emu_settings_type::SetDAZandFTZ, { "Core", "Set DAZ and FTZ"}}, { emu_settings_type::SPUBlockSize, { "Core", "SPU Block Size"}}, @@ -343,6 +342,7 @@ inline static const QMap settings_location = { emu_settings_type::Turntable, { "Input/Output", "Turntable emulated controller" }}, { emu_settings_type::GHLtar, { "Input/Output", "GHLtar emulated controller" }}, { emu_settings_type::MidiDevices, { "Input/Output", "Emulated Midi devices" }}, + { emu_settings_type::SDLMappings, { "Input/Output", "Load SDL GameController Mappings" }}, // Misc { emu_settings_type::ExitRPCS3OnFinish, { "Miscellaneous", "Exit RPCS3 when process finishes" }}, diff --git a/rpcs3/rpcs3qt/find_dialog.cpp b/rpcs3/rpcs3qt/find_dialog.cpp index 69b7a2f1f4..9c5e805050 100644 --- a/rpcs3/rpcs3qt/find_dialog.cpp +++ b/rpcs3/rpcs3qt/find_dialog.cpp @@ -2,7 +2,7 @@ #include -find_dialog::find_dialog(QTextEdit* edit, QWidget *parent, Qt::WindowFlags f) : QDialog(parent, f), m_text_edit(edit) +find_dialog::find_dialog(QPlainTextEdit* edit, QWidget *parent, Qt::WindowFlags f) : QDialog(parent, f), m_text_edit(edit) { setWindowTitle(tr("Find string")); diff --git a/rpcs3/rpcs3qt/find_dialog.h b/rpcs3/rpcs3qt/find_dialog.h index d99363b654..27baf03c75 100644 --- a/rpcs3/rpcs3qt/find_dialog.h +++ b/rpcs3/rpcs3qt/find_dialog.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #include #include #include @@ -11,14 +11,14 @@ class find_dialog : public QDialog Q_OBJECT public: - find_dialog(QTextEdit* edit, QWidget *parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags()); + find_dialog(QPlainTextEdit* edit, QWidget *parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags()); private: int m_count_lines = 0; int m_count_total = 0; QLabel* m_label_count_lines; QLabel* m_label_count_total; - QTextEdit* m_text_edit; + QPlainTextEdit* m_text_edit; QLineEdit* m_find_bar; QPushButton* m_find_first; QPushButton* m_find_last; diff --git a/rpcs3/rpcs3qt/game_list_frame.cpp b/rpcs3/rpcs3qt/game_list_frame.cpp index 4acf9649da..1e902fe03f 100644 --- a/rpcs3/rpcs3qt/game_list_frame.cpp +++ b/rpcs3/rpcs3qt/game_list_frame.cpp @@ -1101,7 +1101,7 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) ? tr("&Change Custom Gamepad Configuration") : tr("&Create Custom Gamepad Configuration")); QAction* configure_patches = menu.addAction(tr("&Manage Game Patches")); - QAction* create_ppu_cache = menu.addAction(tr("&Create PPU Cache")); + QAction* create_ppu_cache = menu.addAction(tr("&Create PPU/SPU Cache")); menu.addSeparator(); @@ -1440,7 +1440,7 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) { if (m_gui_settings->GetBootConfirmation(this)) { - CreatePPUCache(gameinfo); + CreateCPUCaches(gameinfo); } }); connect(remove_game, &QAction::triggered, this, [this, current_game, gameinfo, cache_base_dir, name] @@ -1612,23 +1612,24 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) menu.exec(global_pos); } -bool game_list_frame::CreatePPUCache(const std::string& path, const std::string& serial) +bool game_list_frame::CreateCPUCaches(const std::string& path, const std::string& serial) { Emu.GracefulShutdown(false); Emu.SetForceBoot(true); if (const auto error = Emu.BootGame(fs::is_file(path) ? fs::get_parent_dir(path) : path, serial, true); error != game_boot_result::no_errors) { - game_list_log.error("Could not create PPU Cache for %s, error: %s", path, error); + game_list_log.error("Could not create PPU and SPU caches for %s, error: %s", path, error); return false; } - game_list_log.warning("Creating PPU Cache for %s", path); + + game_list_log.warning("Creating PPU/SPU Caches for %s", path); return true; } -bool game_list_frame::CreatePPUCache(const game_info& game) +bool game_list_frame::CreateCPUCaches(const game_info& game) { - return game && CreatePPUCache(game->info.path, game->info.serial); + return game && CreateCPUCaches(game->info.path, game->info.serial); } bool game_list_frame::RemoveCustomConfiguration(const std::string& title_id, const game_info& game, bool is_interactive) @@ -1683,10 +1684,10 @@ bool game_list_frame::RemoveCustomPadConfiguration(const std::string& title_id, : tr("Remove custom pad configuration?")) != QMessageBox::Yes) return true; - g_cfg_profile.load(); - g_cfg_profile.active_profiles.erase(title_id); - g_cfg_profile.save(); - game_list_log.notice("Removed active pad profile entry for key '%s'", title_id); + g_cfg_input_configs.load(); + g_cfg_input_configs.active_configs.erase(title_id); + g_cfg_input_configs.save(); + game_list_log.notice("Removed active input configuration entry for key '%s'", title_id); if (QDir(qstr(config_dir)).removeRecursively()) { @@ -1907,7 +1908,7 @@ void game_list_frame::RemoveHDD1Cache(const std::string& base_dir, const std::st game_list_log.fatal("Only %d/%d HDD1 cache directories could be removed in %s (%s)", dirs_removed, dirs_total, base_dir, title_id); } -void game_list_frame::BatchCreatePPUCaches() +void game_list_frame::BatchCreateCPUCaches() { const std::string vsh_path = g_cfg_vfs.get_dev_flash() + "vsh/module/"; const bool vsh_exists = fs::is_file(vsh_path + "vsh.self"); @@ -1952,7 +1953,7 @@ void game_list_frame::BatchCreatePPUCaches() pdlg->setLabelText(tr("%0\nProgress: %1/%2. Compiling caches for VSH...", "Second line after main label").arg(main_label).arg(created).arg(total)); QApplication::processEvents(); - if (CreatePPUCache(vsh_path) && wait_until_compiled()) + if (CreateCPUCaches(vsh_path) && wait_until_compiled()) { pdlg->SetValue(++created); } @@ -1968,7 +1969,7 @@ void game_list_frame::BatchCreatePPUCaches() pdlg->setLabelText(tr("%0\nProgress: %1/%2. Compiling caches for %3...", "Second line after main label").arg(main_label).arg(created).arg(total).arg(qstr(game->info.serial))); QApplication::processEvents(); - if (CreatePPUCache(game) && wait_until_compiled()) + if (CreateCPUCaches(game) && wait_until_compiled()) { pdlg->SetValue(++created); } @@ -2446,6 +2447,30 @@ bool game_list_frame::SearchMatchesApp(const QString& name, const QString& seria { return true; } + + // Initials-only search + if (search_text.size() >= 2 && search_text.count(QRegularExpression(QStringLiteral("[a-z0-9]"))) >= 2 && !search_text.contains(QRegularExpression(QStringLiteral("[^a-z0-9 ]")))) + { + QString initials = QStringLiteral("\\b"); + + for (auto it = search_text.begin(); it != search_text.end(); it++) + { + if (it->isSpace()) + { + continue; + } + + initials += *it; + initials += QStringLiteral("\\w*\\b "); + } + + initials += QChar('?'); + + if (title_name_replaced_trademarks_with_spaces.contains(QRegularExpression(initials))) + { + return true; + } + } } return title_name.contains(search_text) || serial.toLower().contains(search_text); diff --git a/rpcs3/rpcs3qt/game_list_frame.h b/rpcs3/rpcs3qt/game_list_frame.h index 582d407a52..ecc7e106c7 100644 --- a/rpcs3/rpcs3qt/game_list_frame.h +++ b/rpcs3/rpcs3qt/game_list_frame.h @@ -63,7 +63,7 @@ public: bool IsEntryVisible(const game_info& game, bool search_fallback = false) const; public Q_SLOTS: - void BatchCreatePPUCaches(); + void BatchCreateCPUCaches(); void BatchRemovePPUCaches(); void BatchRemoveSPUCaches(); void BatchRemoveCustomConfigurations(); @@ -108,8 +108,8 @@ private: bool RemovePPUCache(const std::string& base_dir, bool is_interactive = false); bool RemoveSPUCache(const std::string& base_dir, bool is_interactive = false); void RemoveHDD1Cache(const std::string& base_dir, const std::string& title_id, bool is_interactive = false); - static bool CreatePPUCache(const std::string& path, const std::string& serial = {}); - static bool CreatePPUCache(const game_info& game); + static bool CreateCPUCaches(const std::string& path, const std::string& serial = {}); + static bool CreateCPUCaches(const game_info& game); static std::string GetCacheDirBySerial(const std::string& serial); static std::string GetDataDirBySerial(const std::string& serial); diff --git a/rpcs3/rpcs3qt/gs_frame.cpp b/rpcs3/rpcs3qt/gs_frame.cpp index 44b927738a..c0c4b60a6d 100644 --- a/rpcs3/rpcs3qt/gs_frame.cpp +++ b/rpcs3/rpcs3qt/gs_frame.cpp @@ -579,7 +579,7 @@ bool gs_frame::get_mouse_lock_state() void gs_frame::hide_on_close() { - if (!(+g_progr)) + if (!g_progr.load()) { // Hide the dialog before stopping if no progress bar is being shown. // Otherwise users might think that the game softlocked if stopping takes too long. @@ -762,13 +762,13 @@ bool gs_frame::can_consume_frame() const return video_provider.can_consume_frame(); } -void gs_frame::present_frame(std::vector& data, const u32 width, const u32 height, bool is_bgra) const +void gs_frame::present_frame(std::vector& data, u32 pitch, u32 width, u32 height, bool is_bgra) const { utils::video_provider& video_provider = g_fxo->get(); - video_provider.present_frame(data, width, height, is_bgra); + video_provider.present_frame(data, pitch, width, height, is_bgra); } -void gs_frame::take_screenshot(std::vector data, const u32 sshot_width, const u32 sshot_height, bool is_bgra) +void gs_frame::take_screenshot(std::vector data, u32 sshot_width, u32 sshot_height, bool is_bgra) { std::thread( [sshot_width, sshot_height, is_bgra](std::vector sshot_data) diff --git a/rpcs3/rpcs3qt/gs_frame.h b/rpcs3/rpcs3qt/gs_frame.h index feaaf88f23..4872de1dad 100644 --- a/rpcs3/rpcs3qt/gs_frame.h +++ b/rpcs3/rpcs3qt/gs_frame.h @@ -69,11 +69,11 @@ public: bool get_mouse_lock_state(); bool can_consume_frame() const override; - void present_frame(std::vector& data, const u32 width, const u32 height, bool is_bgra) const override; - void take_screenshot(std::vector data, const u32 sshot_width, const u32 sshot_height, bool is_bgra) override; + void present_frame(std::vector& data, u32 pitch, u32 width, u32 height, bool is_bgra) const override; + void take_screenshot(std::vector data, u32 sshot_width, u32 sshot_height, bool is_bgra) override; protected: - virtual void paintEvent(QPaintEvent *event); + void paintEvent(QPaintEvent *event) override; void showEvent(QShowEvent *event) override; void keyPressEvent(QKeyEvent *keyEvent) override; diff --git a/rpcs3/rpcs3qt/gui_application.cpp b/rpcs3/rpcs3qt/gui_application.cpp index a36efa071a..982e1632ed 100644 --- a/rpcs3/rpcs3qt/gui_application.cpp +++ b/rpcs3/rpcs3qt/gui_application.cpp @@ -34,7 +34,6 @@ #include #include #include -#include #include #include @@ -74,6 +73,7 @@ bool gui_application::Init() gui_log.warning("Experimental Build Warning! Build origin: %s", branch_name); QMessageBox msg; + msg.setWindowModality(Qt::WindowModal); msg.setWindowTitle(tr("Experimental Build Warning")); msg.setIcon(QMessageBox::Critical); msg.setTextFormat(Qt::RichText); @@ -173,7 +173,7 @@ void gui_application::SwitchTranslator(QTranslator& translator, const QString& f // remove the old translator removeTranslator(&translator); - const QString lang_path = QLibraryInfo::location(QLibraryInfo::TranslationsPath) + QStringLiteral("/"); + const QString lang_path = QLibraryInfo::path(QLibraryInfo::TranslationsPath) + QStringLiteral("/"); const QString file_path = lang_path + filename; if (QFileInfo(file_path).isFile()) @@ -236,7 +236,7 @@ QStringList gui_application::GetAvailableLanguageCodes() { QStringList language_codes; - const QString language_path = QLibraryInfo::location(QLibraryInfo::TranslationsPath); + const QString language_path = QLibraryInfo::path(QLibraryInfo::TranslationsPath); if (QFileInfo(language_path).isDir()) { @@ -422,7 +422,7 @@ void gui_application::InitializeCallbacks() return false; }; - callbacks.call_from_main_thread = [this](std::function func, atomic_t* wake_up) + callbacks.call_from_main_thread = [this](std::function func, atomic_t* wake_up) { RequestCallFromMainThread(std::move(func), wake_up); }; @@ -546,13 +546,17 @@ void gui_application::InitializeCallbacks() return localized_emu::get_u32string(id, args); }; - callbacks.play_sound = [](const std::string& path) + callbacks.play_sound = [this](const std::string& path) { - Emu.CallFromMainThread([path]() + Emu.CallFromMainThread([this, path]() { if (fs::is_file(path)) { - QSound::play(qstr(path)); + m_sound_effect.stop(); + m_sound_effect.setSource(QUrl::fromLocalFile(qstr(path))); + m_sound_effect.setVolume(g_cfg.audio.volume * 0.01f); + m_sound_effect.setLoopCount(1); + m_sound_effect.play(); } }); }; @@ -789,7 +793,7 @@ void gui_application::OnChangeStyleSheetRequest() /** * Using connects avoids timers being unable to be used in a non-qt thread. So, even if this looks stupid to just call func, it's succinct. */ -void gui_application::CallFromMainThread(const std::function& func, atomic_t* wake_up) +void gui_application::CallFromMainThread(const std::function& func, atomic_t* wake_up) { func(); diff --git a/rpcs3/rpcs3qt/gui_application.h b/rpcs3/rpcs3qt/gui_application.h index ff86ea56d3..eac5e19d95 100644 --- a/rpcs3/rpcs3qt/gui_application.h +++ b/rpcs3/rpcs3qt/gui_application.h @@ -7,6 +7,7 @@ #include #include #include +#include #include "main_application.h" @@ -87,6 +88,8 @@ private: QTimer m_timer; QElapsedTimer m_timer_playtime; + QSoundEffect m_sound_effect{}; + std::shared_ptr m_emu_settings; std::shared_ptr m_gui_settings; std::shared_ptr m_persistent_settings; @@ -115,8 +118,8 @@ Q_SIGNALS: void OnEnableDiscEject(bool enabled); void OnEnableDiscInsert(bool enabled); - void RequestCallFromMainThread(std::function func, atomic_t* wake_up); + void RequestCallFromMainThread(std::function func, atomic_t* wake_up); private Q_SLOTS: - static void CallFromMainThread(const std::function& func, atomic_t* wake_up); + static void CallFromMainThread(const std::function& func, atomic_t* wake_up); }; diff --git a/rpcs3/rpcs3qt/localized_emu.h b/rpcs3/rpcs3qt/localized_emu.h index 9553c6fefd..0fbcb00d62 100644 --- a/rpcs3/rpcs3qt/localized_emu.h +++ b/rpcs3/rpcs3qt/localized_emu.h @@ -86,7 +86,7 @@ private: case localized_string_id::CELL_MSG_DIALOG_ERROR_80010004: return tr("Memory allocation failed.\n(%0)", "Error code").arg(std::forward(args)...); case localized_string_id::CELL_MSG_DIALOG_ERROR_80010005: return tr("The resource with the specified identifier does not exist.\n(%0)", "Error code").arg(std::forward(args)...); case localized_string_id::CELL_MSG_DIALOG_ERROR_80010006: return tr("The file does not exist.\n(%0)", "Error code").arg(std::forward(args)...); - case localized_string_id::CELL_MSG_DIALOG_ERROR_80010007: return tr("The file is in unrecognized format / The file is not a valid ELF file.\n(%0)", "Error code").arg(std::forward(args)...); + case localized_string_id::CELL_MSG_DIALOG_ERROR_80010007: return tr("The file is in an unrecognized format / The file is not a valid ELF file.\n(%0)", "Error code").arg(std::forward(args)...); case localized_string_id::CELL_MSG_DIALOG_ERROR_80010008: return tr("Resource deadlock is avoided.\n(%0)", "Error code").arg(std::forward(args)...); case localized_string_id::CELL_MSG_DIALOG_ERROR_80010009: return tr("Operation not permitted.\n(%0)", "Error code").arg(std::forward(args)...); case localized_string_id::CELL_MSG_DIALOG_ERROR_8001000A: return tr("The device or resource is busy.\n(%0)", "Error code").arg(std::forward(args)...); @@ -135,7 +135,7 @@ private: case localized_string_id::CELL_MSG_DIALOG_ERROR_80010036: return tr("Not empty.\n(%0)", "Error code").arg(std::forward(args)...); case localized_string_id::CELL_MSG_DIALOG_ERROR_80010037: return tr("Not supported.\n(%0)", "Error code").arg(std::forward(args)...); case localized_string_id::CELL_MSG_DIALOG_ERROR_80010038: return tr("File-system specific error.\n(%0)", "Error code").arg(std::forward(args)...); - case localized_string_id::CELL_MSG_DIALOG_ERROR_80010039: return tr("Overflow occured.\n(%0)", "Error code").arg(std::forward(args)...); + case localized_string_id::CELL_MSG_DIALOG_ERROR_80010039: return tr("Overflow occurred.\n(%0)", "Error code").arg(std::forward(args)...); case localized_string_id::CELL_MSG_DIALOG_ERROR_8001003A: return tr("Filesystem not mounted.\n(%0)", "Error code").arg(std::forward(args)...); case localized_string_id::CELL_MSG_DIALOG_ERROR_8001003B: return tr("Not SData.\n(%0)", "Error code").arg(std::forward(args)...); case localized_string_id::CELL_MSG_DIALOG_ERROR_8001003C: return tr("Incorrect version in sys_load_param.\n(%0)", "Error code").arg(std::forward(args)...); diff --git a/rpcs3/rpcs3qt/log_frame.cpp b/rpcs3/rpcs3qt/log_frame.cpp index d32de0f26c..663878efed 100644 --- a/rpcs3/rpcs3qt/log_frame.cpp +++ b/rpcs3/rpcs3qt/log_frame.cpp @@ -897,7 +897,7 @@ bool log_frame::eventFilter(QObject* object, QEvent* event) if (m_find_dialog && m_find_dialog->isVisible()) m_find_dialog->close(); - m_find_dialog.reset(new find_dialog(static_cast(object), this)); + m_find_dialog.reset(new find_dialog(static_cast(object), this)); } } diff --git a/rpcs3/rpcs3qt/log_viewer.cpp b/rpcs3/rpcs3qt/log_viewer.cpp index dea9da3533..d39bfd5367 100644 --- a/rpcs3/rpcs3qt/log_viewer.cpp +++ b/rpcs3/rpcs3qt/log_viewer.cpp @@ -235,8 +235,11 @@ void log_viewer::show_log() { m_gui_settings->SetValue(gui::fd_log_viewer, m_path_last); - QTextStream stream(&file); - m_full_log = stream.readAll(); + // TODO: Due to a bug in Qt 6.5.2 QTextStream::readAll is ridiculously slow to the point where it gets stuck on large files. + // In Qt 5.15.2 this was much faster than QFile::readAll. Use QTextStream again once this bug is fixed upstream. + //QTextStream stream(&file); + //m_full_log = stream.readAll(); + m_full_log = file.readAll(); m_full_log.replace('\0', '0'); file.close(); } @@ -455,7 +458,7 @@ bool log_viewer::eventFilter(QObject* object, QEvent* event) if (m_find_dialog && m_find_dialog->isVisible()) m_find_dialog->close(); - m_find_dialog.reset(new find_dialog(static_cast(object), this)); + m_find_dialog.reset(new find_dialog(static_cast(object), this)); } } diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index 71e897a54b..2313e9f9b6 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -155,7 +155,7 @@ bool main_window::Init([[maybe_unused]] bool with_cli_boot) ui->toolbar_start->setEnabled(enable_play_last); // create tool buttons for the taskbar thumbnail -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF m_thumb_bar = new QWinThumbnailToolBar(this); m_thumb_bar->setWindow(windowHandle()); @@ -445,7 +445,7 @@ void main_window::show_boot_error(game_boot_result status) message = tr("Savestate data is corrupted or it's not an RPCS3 savestate."); break; case game_boot_result::savestate_version_unsupported: - message = tr("Savestate versioning data differes from your RPCS3 build."); + message = tr("Savestate versioning data differs from your RPCS3 build."); break; case game_boot_result::still_running: message = tr("A game or PS3 application is still running or has yet to be fully stopped."); @@ -743,75 +743,94 @@ bool main_window::InstallPackages(QStringList file_paths, bool from_boot) m_gui_settings->SetValue(gui::fd_install_pkg, file_info.path()); } - if (file_paths.count() == 1 && file_paths.front().endsWith(".pkg", Qt::CaseInsensitive)) + if (file_paths.count() == 1) { const QString file_path = file_paths.front(); const QFileInfo file_info(file_path); - compat::package_info info = game_compatibility::GetPkgInfo(file_path, m_game_list_frame ? m_game_list_frame->GetGameCompatibility() : nullptr); - - if (!info.is_valid) + if (file_info.isDir()) { - QMessageBox::warning(this, tr("Invalid package!"), tr("The selected package is invalid!\n\nPath:\n%0").arg(file_path)); - return false; - } + gui_log.notice("PKG: Trying to install packages from dir: '%s'", file_path); - if (info.type != compat::package_type::other) - { - if (info.type == compat::package_type::dlc) + const QDir dir(file_path); + const QStringList dir_file_paths = gui::utils::get_dir_entries(dir, {}, true); + + if (dir_file_paths.empty()) { - info.local_cat = tr("\nDLC", "Block for package type (DLC)"); - } - else - { - info.local_cat = tr("\nUpdate", "Block for package type (Update)"); - } - } - else if (!info.local_cat.isEmpty()) - { - info.local_cat = tr("\n%0", "Block for package type").arg(info.local_cat); - } - - if (!info.title_id.isEmpty()) - { - info.title_id = tr("\n%0", "Block for Title ID").arg(info.title_id); - } - - if (!info.version.isEmpty()) - { - info.version = tr("\nVersion %0", "Block for Version").arg(info.version); - } - - if (!info.changelog.isEmpty()) - { - info.changelog = tr("Changelog:\n%0", "Block for Changelog").arg(info.changelog); - } - - const QString info_string = QStringLiteral("%0\n\n%1%2%3%4").arg(file_info.fileName()).arg(info.title).arg(info.local_cat).arg(info.title_id).arg(info.version); - QString message = tr("Do you want to install this package?\n\n%0").arg(info_string); - - QMessageBox mb(QMessageBox::Icon::Question, tr("PKG Decrypter / Installer"), message, QMessageBox::Yes | QMessageBox::No, this); - mb.setDefaultButton(QMessageBox::No); - - if (!info.changelog.isEmpty()) - { - mb.setInformativeText(tr("To see the changelog, please click \"Show Details\".")); - mb.setDetailedText(tr("%0").arg(info.changelog)); - - // Smartass hack to make the unresizeable message box wide enough for the changelog - const int log_width = QLabel(info.changelog).sizeHint().width(); - while (QLabel(message).sizeHint().width() < log_width) - { - message += " "; + gui_log.notice("PKG: Could not find any files in dir: '%s'", file_path); + return true; } - mb.setText(message); + return InstallPackages(dir_file_paths, from_boot); } - if (mb.exec() != QMessageBox::Yes) + if (file_info.suffix().compare("pkg", Qt::CaseInsensitive) == 0) { - gui_log.notice("PKG: Cancelled installation from drop.\n%s\n%s", info_string, info.changelog); - return true; + compat::package_info info = game_compatibility::GetPkgInfo(file_path, m_game_list_frame ? m_game_list_frame->GetGameCompatibility() : nullptr); + + if (!info.is_valid) + { + QMessageBox::warning(this, tr("Invalid package!"), tr("The selected package is invalid!\n\nPath:\n%0").arg(file_path)); + return false; + } + + if (info.type != compat::package_type::other) + { + if (info.type == compat::package_type::dlc) + { + info.local_cat = tr("\nDLC", "Block for package type (DLC)"); + } + else + { + info.local_cat = tr("\nUpdate", "Block for package type (Update)"); + } + } + else if (!info.local_cat.isEmpty()) + { + info.local_cat = tr("\n%0", "Block for package type").arg(info.local_cat); + } + + if (!info.title_id.isEmpty()) + { + info.title_id = tr("\n%0", "Block for Title ID").arg(info.title_id); + } + + if (!info.version.isEmpty()) + { + info.version = tr("\nVersion %0", "Block for Version").arg(info.version); + } + + if (!info.changelog.isEmpty()) + { + info.changelog = tr("Changelog:\n%0", "Block for Changelog").arg(info.changelog); + } + + const QString info_string = QStringLiteral("%0\n\n%1%2%3%4").arg(file_info.fileName()).arg(info.title).arg(info.local_cat).arg(info.title_id).arg(info.version); + QString message = tr("Do you want to install this package?\n\n%0").arg(info_string); + + QMessageBox mb(QMessageBox::Icon::Question, tr("PKG Decrypter / Installer"), message, QMessageBox::Yes | QMessageBox::No, this); + mb.setDefaultButton(QMessageBox::No); + + if (!info.changelog.isEmpty()) + { + mb.setInformativeText(tr("To see the changelog, please click \"Show Details\".")); + mb.setDetailedText(tr("%0").arg(info.changelog)); + + // Smartass hack to make the unresizeable message box wide enough for the changelog + const int log_width = QLabel(info.changelog).sizeHint().width(); + while (QLabel(message).sizeHint().width() < log_width) + { + message += " "; + } + + mb.setText(message); + } + + if (mb.exec() != QMessageBox::Yes) + { + gui_log.notice("PKG: Cancelled installation from drop.\n%s\n%s", info_string, info.changelog); + return true; + } } } @@ -821,7 +840,7 @@ bool main_window::InstallPackages(QStringList file_paths, bool from_boot) const auto install_filetype = [&installed_rap_and_edat_count, &file_paths](const std::string extension) { const QString pattern = QString(".*\\.%1").arg(QString::fromStdString(extension)); - for (const auto& file : file_paths.filter(QRegularExpression(pattern, QRegularExpression::PatternOption::CaseInsensitiveOption))) + for (const QString& file : file_paths.filter(QRegularExpression(pattern, QRegularExpression::PatternOption::CaseInsensitiveOption))) { const QFileInfo file_info(file); const std::string filename = sstr(file_info.fileName()); @@ -1317,7 +1336,7 @@ void main_window::ExtractTar() if (!error.isEmpty()) { pdlg.hide(); - QMessageBox::critical(this, tr("Tar extraction failed"), error); + QMessageBox::critical(this, tr("TAR extraction failed"), error); } } @@ -1700,7 +1719,7 @@ void main_window::RepaintThumbnailIcons() return gui::utils::get_colorized_icon(QPixmap::fromImage(gui::utils::get_opaque_image_area(path)), Qt::black, new_color); }; -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF if (!m_thumb_bar) return; m_icon_thumb_play = icon(":/Icons/play.png"); @@ -1787,14 +1806,6 @@ void main_window::RepaintToolBarIcons() // resize toolbar elements - // for highdpi resize toolbar icons and height dynamically - // choose factors to mimic Gui-Design in main_window.ui - // TODO: delete this in case Qt::AA_EnableHighDpiScaling is enabled in main.cpp -#ifdef _WIN32 - const int tool_icon_height = menuBar()->sizeHint().height() * 1.5; - ui->toolBar->setIconSize(QSize(tool_icon_height, tool_icon_height)); -#endif - const int tool_bar_height = ui->toolBar->sizeHint().height(); for (const auto& act : ui->toolBar->actions()) @@ -1820,7 +1831,7 @@ void main_window::OnEmuRun(bool /*start_playtime*/) const m_debugger_frame->EnableButtons(true); -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF m_thumb_stop->setToolTip(stop_tooltip); m_thumb_restart->setToolTip(restart_tooltip); m_thumb_playPause->setToolTip(pause_tooltip); @@ -1843,7 +1854,7 @@ void main_window::OnEmuResume() const const QString pause_tooltip = tr("Pause %0").arg(title); const QString stop_tooltip = tr("Stop %0").arg(title); -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF m_thumb_stop->setToolTip(stop_tooltip); m_thumb_restart->setToolTip(restart_tooltip); m_thumb_playPause->setToolTip(pause_tooltip); @@ -1862,7 +1873,7 @@ void main_window::OnEmuPause() const const QString title = GetCurrentTitle(); const QString resume_tooltip = tr("Resume %0").arg(title); -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF m_thumb_playPause->setToolTip(resume_tooltip); m_thumb_playPause->setIcon(m_icon_thumb_play); #endif @@ -1886,7 +1897,7 @@ void main_window::OnEmuStop() ui->sysPauseAct->setText(tr("&Play")); ui->sysPauseAct->setIcon(m_icon_play); -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF m_thumb_playPause->setToolTip(play_tooltip); m_thumb_playPause->setIcon(m_icon_thumb_play); #endif @@ -1908,7 +1919,7 @@ void main_window::OnEmuStop() ui->toolbar_start->setText(tr("Restart")); ui->toolbar_start->setToolTip(restart_tooltip); ui->sysRebootAct->setEnabled(true); -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF m_thumb_restart->setToolTip(restart_tooltip); m_thumb_restart->setEnabled(true); #endif @@ -1947,7 +1958,7 @@ void main_window::OnEmuReady() const const QString play_tooltip = tr("Play %0").arg(title); m_debugger_frame->EnableButtons(true); -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF m_thumb_playPause->setToolTip(play_tooltip); m_thumb_playPause->setIcon(m_icon_thumb_play); #endif @@ -1971,7 +1982,7 @@ void main_window::OnEmuReady() const void main_window::EnableMenus(bool enabled) const { // Thumbnail Buttons -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF m_thumb_playPause->setEnabled(enabled); m_thumb_stop->setEnabled(enabled); m_thumb_restart->setEnabled(enabled); @@ -2495,7 +2506,7 @@ void main_window::CreateConnects() }); connect(ui->exitAct, &QAction::triggered, this, &QWidget::close); - connect(ui->batchCreatePPUCachesAct, &QAction::triggered, m_game_list_frame, &game_list_frame::BatchCreatePPUCaches); + connect(ui->batchCreateCPUCachesAct, &QAction::triggered, m_game_list_frame, &game_list_frame::BatchCreateCPUCaches); connect(ui->batchRemovePPUCachesAct, &QAction::triggered, m_game_list_frame, &game_list_frame::BatchRemovePPUCaches); connect(ui->batchRemoveSPUCachesAct, &QAction::triggered, m_game_list_frame, &game_list_frame::BatchRemoveSPUCaches); connect(ui->batchRemoveShaderCachesAct, &QAction::triggered, m_game_list_frame, &game_list_frame::BatchRemoveShaderCaches); @@ -3139,14 +3150,14 @@ void main_window::CreateDockWindows() ui->toolbar_start->setEnabled(enable_play_buttons); ui->sysPauseAct->setEnabled(enable_play_buttons); -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF m_thumb_playPause->setEnabled(enable_play_buttons); #endif if (!tooltip.isEmpty()) { ui->toolbar_start->setToolTip(tooltip); -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF m_thumb_playPause->setToolTip(tooltip); #endif } diff --git a/rpcs3/rpcs3qt/main_window.h b/rpcs3/rpcs3qt/main_window.h index 17ec7ea30e..a776e60412 100644 --- a/rpcs3/rpcs3qt/main_window.h +++ b/rpcs3/rpcs3qt/main_window.h @@ -1,6 +1,6 @@ #pragma once -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF #include #include #endif @@ -60,7 +60,7 @@ class main_window : public QMainWindow QIcon m_icon_fullscreen_on; QIcon m_icon_fullscreen_off; -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF QIcon m_icon_thumb_play; QIcon m_icon_thumb_pause; QIcon m_icon_thumb_stop; diff --git a/rpcs3/rpcs3qt/main_window.ui b/rpcs3/rpcs3qt/main_window.ui index 9c93bf5c79..35422d94fc 100644 --- a/rpcs3/rpcs3qt/main_window.ui +++ b/rpcs3/rpcs3qt/main_window.ui @@ -182,7 +182,7 @@ All Titles - + @@ -1094,9 +1094,9 @@ Show Title Bars - + - Create PPU Caches + Create PPU/SPU Caches diff --git a/rpcs3/rpcs3qt/memory_viewer_panel.cpp b/rpcs3/rpcs3qt/memory_viewer_panel.cpp index f8ee6fcebe..19200c8249 100644 --- a/rpcs3/rpcs3qt/memory_viewer_panel.cpp +++ b/rpcs3/rpcs3qt/memory_viewer_panel.cpp @@ -1150,7 +1150,7 @@ void memory_viewer_panel::ShowImage(QWidget* parent, u32 addr, color_format form { if (object == m_canvas && (event->type() == QEvent::HoverMove || event->type() == QEvent::HoverEnter || event->type() == QEvent::HoverLeave)) { - const QPoint xy = static_cast(event)->pos() / m_canvas_scale; + const QPointF xy = static_cast(event)->position() / m_canvas_scale; set_window_name_by_coordinates(xy.x(), xy.y()); return false; } @@ -1159,7 +1159,7 @@ void memory_viewer_panel::ShowImage(QWidget* parent, u32 addr, color_format form { QLineEdit* addr_line = static_cast(parent())->m_addr_line; - const QPoint xy = static_cast(event)->pos() / m_canvas_scale; + const QPointF xy = static_cast(event)->position() / m_canvas_scale; addr_line->setText(qstr(fmt::format("%08x", get_pointed_addr(xy.x(), xy.y())))); Q_EMIT addr_line->returnPressed(); close(); diff --git a/rpcs3/rpcs3qt/midi_creator.cpp b/rpcs3/rpcs3qt/midi_creator.cpp index f7c5ef1816..ff83461fec 100644 --- a/rpcs3/rpcs3qt/midi_creator.cpp +++ b/rpcs3/rpcs3qt/midi_creator.cpp @@ -15,7 +15,7 @@ midi_creator::midi_creator() // We need to recreate the localized string because the midi creator is currently only created once. QString midi_creator::get_none() { - return tr("None", "Midi device"); + return tr("None", "MIDI device"); } void midi_creator::refresh_list() @@ -49,11 +49,11 @@ void midi_creator::refresh_list() s32 size = sizeof(buf); if (rtmidi_get_port_name(midi_in.get(), port_number, buf, &size) == -1) { - cfg_log.error("Error getting midi port name for port %d: %s", port_number, midi_in->msg); + cfg_log.error("Error getting MIDI port name for port %d: %s", port_number, midi_in->msg); continue; } - cfg_log.notice("Found midi device with name: %s", buf); + cfg_log.notice("Found MIDI device with name: %s", buf); m_midi_list.append(QString::fromUtf8(buf)); } } diff --git a/rpcs3/rpcs3qt/msg_dialog_frame.cpp b/rpcs3/rpcs3qt/msg_dialog_frame.cpp index e05c1f87e0..32bb19c6f8 100644 --- a/rpcs3/rpcs3qt/msg_dialog_frame.cpp +++ b/rpcs3/rpcs3qt/msg_dialog_frame.cpp @@ -5,8 +5,6 @@ #include #include -constexpr auto qstr = QString::fromStdString; - void msg_dialog_frame::Create(const std::string& msg, const std::string& title) { state = MsgDialogState::Open; @@ -16,10 +14,10 @@ void msg_dialog_frame::Create(const std::string& msg, const std::string& title) Close(true); m_dialog = new custom_dialog(type.disable_cancel); - m_dialog->setWindowTitle(title.empty() ? (type.se_normal ? tr("Normal dialog") : tr("Error dialog")) : qstr(title)); + m_dialog->setWindowTitle(title.empty() ? (type.se_normal ? tr("Normal dialog") : tr("Error dialog")) : QString::fromStdString(title)); m_dialog->setWindowOpacity(type.bg_invisible ? 1. : 0.75); - m_text = new QLabel(qstr(msg)); + m_text = new QLabel(QString::fromStdString(msg)); m_text->setAlignment(Qt::AlignCenter); // Layout @@ -159,7 +157,7 @@ void msg_dialog_frame::SetMsg(const std::string& msg) { if (m_dialog) { - m_text->setText(qstr(msg)); + m_text->setText(QString::fromStdString(msg)); } } @@ -171,14 +169,14 @@ void msg_dialog_frame::ProgressBarSetMsg(u32 index, const std::string& msg) { if (m_text1) { - m_text1->setText(qstr(msg)); + m_text1->setText(QString::fromStdString(msg)); } } else if (index == 1) { if (m_text2) { - m_text2->setText(qstr(msg)); + m_text2->setText(QString::fromStdString(msg)); } } } diff --git a/rpcs3/rpcs3qt/osk_dialog_frame.cpp b/rpcs3/rpcs3qt/osk_dialog_frame.cpp index 031199789a..32336f44c5 100644 --- a/rpcs3/rpcs3qt/osk_dialog_frame.cpp +++ b/rpcs3/rpcs3qt/osk_dialog_frame.cpp @@ -182,7 +182,7 @@ void osk_dialog_frame::Create(const osk_params& params) void osk_dialog_frame::SetOskText(const QString& text) { - std::memcpy(osk_text.data(), utils::bless(text.constData()), std::min(osk_text.size(), text.size() + usz{1}) * sizeof(char16_t)); + std::memcpy(osk_text.data(), utils::bless(text.constData()), std::min(osk_text.size(), text.size() + usz{1}) * sizeof(char16_t)); } void osk_dialog_frame::Close(s32 status) diff --git a/rpcs3/rpcs3qt/pad_settings_dialog.cpp b/rpcs3/rpcs3qt/pad_settings_dialog.cpp index 761f32d94d..3311ea188b 100644 --- a/rpcs3/rpcs3qt/pad_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/pad_settings_dialog.cpp @@ -30,7 +30,7 @@ LOG_CHANNEL(cfg_log, "CFG"); inline std::string sstr(const QString& _in) { return _in.toStdString(); } constexpr auto qstr = QString::fromStdString; -cfg_profile g_cfg_profile; +cfg_input_configurations g_cfg_input_configs; inline bool CreateConfigFile(const QString& dir, const QString& name) { @@ -85,38 +85,38 @@ pad_settings_dialog::pad_settings_dialog(std::shared_ptr gui_setti setWindowTitle(tr("Gamepad Settings")); } - // Load profiles - g_cfg_profile.load(); + // Load input configs + g_cfg_input_configs.load(); if (m_title_id.empty()) { - const QString profile_dir = qstr(rpcs3::utils::get_input_config_dir(m_title_id)); - QStringList profiles = gui::utils::get_dir_entries(QDir(profile_dir), QStringList() << "*.yml"); - QString active_profile = qstr(g_cfg_profile.active_profiles.get_value(g_cfg_profile.global_key)); + const QString input_config_dir = qstr(rpcs3::utils::get_input_config_dir(m_title_id)); + QStringList config_files = gui::utils::get_dir_entries(QDir(input_config_dir), QStringList() << "*.yml"); + QString active_config_file = qstr(g_cfg_input_configs.active_configs.get_value(g_cfg_input_configs.global_key)); - if (!profiles.contains(active_profile)) + if (!config_files.contains(active_config_file)) { - const QString default_profile = qstr(g_cfg_profile.default_profile); + const QString default_config_file = qstr(g_cfg_input_configs.default_config); - if (!profiles.contains(default_profile) && CreateConfigFile(profile_dir, default_profile)) + if (!config_files.contains(default_config_file) && CreateConfigFile(input_config_dir, default_config_file)) { - profiles.prepend(default_profile); + config_files.prepend(default_config_file); } - active_profile = default_profile; + active_config_file = default_config_file; } - for (const QString& profile : profiles) + for (const QString& profile : config_files) { - ui->chooseProfile->addItem(profile); + ui->chooseConfig->addItem(profile); } - ui->chooseProfile->setCurrentText(active_profile); + ui->chooseConfig->setCurrentText(active_config_file); } else { - ui->chooseProfile->addItem(qstr(m_title_id)); - ui->gb_profiles->setEnabled(false); + ui->chooseConfig->addItem(qstr(m_title_id)); + ui->gb_config_files->setEnabled(false); } // Create tab widget for 7 players @@ -143,11 +143,11 @@ pad_settings_dialog::pad_settings_dialog(std::shared_ptr gui_setti // Combobox: Devices connect(ui->chooseDevice, QOverload::of(&QComboBox::currentIndexChanged), this, &pad_settings_dialog::ChangeDevice); - // Combobox: Profiles - connect(ui->chooseProfile, &QComboBox::currentTextChanged, this, &pad_settings_dialog::ChangeProfile); + // Combobox: Configs + connect(ui->chooseConfig, &QComboBox::currentTextChanged, this, &pad_settings_dialog::ChangeConfig); - // Pushbutton: Add Profile - connect(ui->b_addProfile, &QAbstractButton::clicked, this, &pad_settings_dialog::AddProfile); + // Pushbutton: Add config file + connect(ui->b_addConfig, &QAbstractButton::clicked, this, &pad_settings_dialog::AddConfigFile); ui->buttonBox->button(QDialogButtonBox::Reset)->setText(tr("Filter Noise")); @@ -174,14 +174,18 @@ pad_settings_dialog::pad_settings_dialog(std::shared_ptr gui_setti // Refresh Button connect(ui->b_refresh, &QPushButton::clicked, this, &pad_settings_dialog::RefreshHandlers); - ui->chooseClass->addItem(tr("Standard (Pad)")); // CELL_PAD_PCLASS_TYPE_STANDARD = 0x00, - ui->chooseClass->addItem(tr("Guitar")); // CELL_PAD_PCLASS_TYPE_GUITAR = 0x01, - ui->chooseClass->addItem(tr("Drum")); // CELL_PAD_PCLASS_TYPE_DRUM = 0x02, - ui->chooseClass->addItem(tr("DJ")); // CELL_PAD_PCLASS_TYPE_DJ = 0x03, - ui->chooseClass->addItem(tr("Dance Mat")); // CELL_PAD_PCLASS_TYPE_DANCEMAT = 0x04, - ui->chooseClass->addItem(tr("Navigation")); // CELL_PAD_PCLASS_TYPE_NAVIGATION = 0x05, + ui->chooseClass->addItem(tr("Standard (Pad)"), u32{CELL_PAD_PCLASS_TYPE_STANDARD}); + ui->chooseClass->addItem(tr("Guitar"), u32{CELL_PAD_PCLASS_TYPE_GUITAR}); + ui->chooseClass->addItem(tr("Drum"), u32{CELL_PAD_PCLASS_TYPE_DRUM}); + ui->chooseClass->addItem(tr("DJ"), u32{CELL_PAD_PCLASS_TYPE_DJ}); + ui->chooseClass->addItem(tr("Dance Mat"), u32{CELL_PAD_PCLASS_TYPE_DANCEMAT}); + ui->chooseClass->addItem(tr("Navigation"), u32{CELL_PAD_PCLASS_TYPE_NAVIGATION}); - connect(ui->chooseClass, QOverload::of(&QComboBox::currentIndexChanged), this, &pad_settings_dialog::HandleDeviceClassChange); + connect(ui->chooseClass, QOverload::of(&QComboBox::currentIndexChanged), this, [this](int index) + { + if (index < 0) return; + HandleDeviceClassChange(ui->chooseClass->currentData().toUInt()); + }); ui->chb_show_emulated_values->setChecked(m_gui_settings->GetValue(gui::pads_show_emulated).toBool()); @@ -217,7 +221,7 @@ pad_settings_dialog::pad_settings_dialog(std::shared_ptr gui_setti // Set up first tab OnTabChanged(0); - ChangeProfile(ui->chooseProfile->currentText()); + ChangeConfig(ui->chooseConfig->currentText()); } void pad_settings_dialog::closeEvent(QCloseEvent* event) @@ -304,7 +308,7 @@ void pad_settings_dialog::InitButtons() insert_button(button_ids::id_pressure_intensity, ui->b_pressure_intensity); m_pad_buttons->addButton(ui->b_refresh, button_ids::id_refresh); - m_pad_buttons->addButton(ui->b_addProfile, button_ids::id_add_profile); + m_pad_buttons->addButton(ui->b_addConfig, button_ids::id_add_config_file); connect(m_pad_buttons, &QButtonGroup::idClicked, this, &pad_settings_dialog::OnPadButtonClicked); @@ -747,7 +751,7 @@ void pad_settings_dialog::ReactivateButtons() ui->tabWidget->setFocusPolicy(Qt::TabFocus); ui->scrollArea->setFocusPolicy(Qt::StrongFocus); - ui->chooseProfile->setFocusPolicy(Qt::WheelFocus); + ui->chooseConfig->setFocusPolicy(Qt::WheelFocus); ui->chooseHandler->setFocusPolicy(Qt::WheelFocus); ui->chooseDevice->setFocusPolicy(Qt::WheelFocus); ui->chooseClass->setFocusPolicy(Qt::WheelFocus); @@ -1075,10 +1079,11 @@ void pad_settings_dialog::UpdateLabels(bool is_reset) const cfg_pad& cfg = GetPlayerConfig(); // Update device class - ui->chooseClass->setCurrentIndex(cfg.device_class_type); + const int index = ui->chooseClass->findData(cfg.device_class_type.get()); + ui->chooseClass->setCurrentIndex(index); // Trigger the change manually in case that the class dropdown didn't fire an event - HandleDeviceClassChange(ui->chooseClass->currentIndex()); + HandleDeviceClassChange(cfg.device_class_type); const auto products = input::get_products_by_class(cfg.device_class_type); @@ -1178,6 +1183,11 @@ void pad_settings_dialog::UpdateLabels(bool is_reset) // Update pressure sensitivity toggle mode ui->cb_pressure_intensity_toggle_mode->setChecked(cfg.pressure_intensity_toggle_mode.get()); + // Update pressure sensitivity deadzone + range = cfg.pressure_intensity_deadzone.to_list(); + ui->pressure_intensity_deadzone->setRange(std::stoi(range.front()), std::stoi(range.back())); + ui->pressure_intensity_deadzone->setValue(cfg.pressure_intensity_deadzone.get()); + // Apply stored/default LED settings to the device m_enable_led = m_handler->has_led(); SetPadData(0, 0); @@ -1211,6 +1221,7 @@ void pad_settings_dialog::SwitchButtons(bool is_enabled) ui->kb_stick_multi_right->setEnabled(is_enabled); ui->squircle_left->setEnabled(is_enabled); ui->squircle_right->setEnabled(is_enabled); + ui->gb_pressure_intensity_deadzone->setEnabled(is_enabled); ui->gb_pressure_intensity->setEnabled(is_enabled && m_enable_pressure_intensity_button); ui->gb_vibration->setEnabled(is_enabled && m_enable_rumble); ui->gb_motion_controls->setEnabled(is_enabled && m_enable_motion); @@ -1240,7 +1251,7 @@ void pad_settings_dialog::OnPadButtonClicked(int id) case button_ids::id_led: case button_ids::id_pad_begin: case button_ids::id_pad_end: - case button_ids::id_add_profile: + case button_ids::id_add_config_file: case button_ids::id_refresh: case button_ids::id_ok: case button_ids::id_cancel: @@ -1279,7 +1290,7 @@ void pad_settings_dialog::OnPadButtonClicked(int id) ui->tabWidget->setFocusPolicy(Qt::ClickFocus); ui->scrollArea->setFocusPolicy(Qt::ClickFocus); - ui->chooseProfile->setFocusPolicy(Qt::ClickFocus); + ui->chooseConfig->setFocusPolicy(Qt::ClickFocus); ui->chooseHandler->setFocusPolicy(Qt::ClickFocus); ui->chooseDevice->setFocusPolicy(Qt::ClickFocus); ui->chooseClass->setFocusPolicy(Qt::ClickFocus); @@ -1540,15 +1551,15 @@ void pad_settings_dialog::ChangeHandler() } } -void pad_settings_dialog::ChangeProfile(const QString& profile) +void pad_settings_dialog::ChangeConfig(const QString& config_file) { - if (profile.isEmpty()) + if (config_file.isEmpty()) return; - m_profile = sstr(profile); + m_config_file = sstr(config_file); // Load in order to get the pad handlers - if (!g_cfg_input.load(m_title_id, m_profile, true)) + if (!g_cfg_input.load(m_title_id, m_config_file, true)) { cfg_log.notice("Loaded empty pad config"); } @@ -1561,7 +1572,7 @@ void pad_settings_dialog::ChangeProfile(const QString& profile) } // Reload with proper defaults - if (!g_cfg_input.load(m_title_id, m_profile, true)) + if (!g_cfg_input.load(m_title_id, m_config_file, true)) { cfg_log.notice("Reloaded empty pad config"); } @@ -1603,16 +1614,11 @@ void pad_settings_dialog::ChangeDevice(int index) } } -void pad_settings_dialog::HandleDeviceClassChange(int index) const +void pad_settings_dialog::HandleDeviceClassChange(u32 class_id) const { - if (index < 0) - { - return; - } - ui->chooseProduct->clear(); - for (const auto& product : input::get_products_by_class(index)) + for (const input::product_info& product : input::get_products_by_class(class_id)) { switch (product.type) { @@ -1661,40 +1667,45 @@ void pad_settings_dialog::HandleDeviceClassChange(int index) const ui->chooseProduct->addItem(tr("Rock Revolution", "Rock Revolution Drum Controller"), static_cast(product.type)); break; } + case input::product_type::ps_move_navigation: + { + ui->chooseProduct->addItem(tr("PS Move Navigation", "PS Move Navigation Controller"), static_cast(product.type)); + break; + } } } } -void pad_settings_dialog::AddProfile() +void pad_settings_dialog::AddConfigFile() { QInputDialog* dialog = new QInputDialog(this); dialog->setWindowTitle(tr("Choose a unique name")); - dialog->setLabelText(tr("Profile Name: ")); + dialog->setLabelText(tr("Configuration Name: ")); dialog->setFixedSize(500, 100); while (dialog->exec() != QDialog::Rejected) { - const QString profile_name = dialog->textValue(); + const QString config_name = dialog->textValue(); - if (profile_name.isEmpty()) + if (config_name.isEmpty()) { QMessageBox::warning(this, tr("Error"), tr("Name cannot be empty")); continue; } - if (profile_name.contains(".")) + if (config_name.contains(".")) { QMessageBox::warning(this, tr("Error"), tr("Must choose a name without '.'")); continue; } - if (ui->chooseProfile->findText(profile_name) != -1) + if (ui->chooseConfig->findText(config_name) != -1) { QMessageBox::warning(this, tr("Error"), tr("Please choose a non-existing name")); continue; } - if (CreateConfigFile(qstr(rpcs3::utils::get_input_config_dir(m_title_id)), profile_name)) + if (CreateConfigFile(qstr(rpcs3::utils::get_input_config_dir(m_title_id)), config_name)) { - ui->chooseProfile->addItem(profile_name); - ui->chooseProfile->setCurrentText(profile_name); + ui->chooseConfig->addItem(config_name); + ui->chooseConfig->setCurrentText(config_name); } break; } @@ -1808,6 +1819,8 @@ void pad_settings_dialog::ApplyCurrentPlayerConfig(int new_player_id) cfg.pressure_intensity_toggle_mode.set(ui->cb_pressure_intensity_toggle_mode->isChecked()); } + cfg.pressure_intensity_deadzone.set(ui->pressure_intensity_deadzone->value()); + if (m_handler->m_type == pad_handler::keyboard) { const int mouse_move_mode = ui->mouse_movement->currentData().toInt(); @@ -1821,7 +1834,7 @@ void pad_settings_dialog::ApplyCurrentPlayerConfig(int new_player_id) cfg.r_stick_lerp_factor.set(ui->right_stick_lerp->value() * 100); } - cfg.device_class_type.set(ui->chooseClass->currentIndex()); + cfg.device_class_type.set(ui->chooseClass->currentData().toUInt()); const auto info = input::get_product_info(static_cast(ui->chooseProduct->currentData().toInt())); @@ -1851,12 +1864,12 @@ void pad_settings_dialog::SaveExit() } } - const std::string profile_key = m_title_id.empty() ? g_cfg_profile.global_key : m_title_id; + const std::string config_file_key = m_title_id.empty() ? g_cfg_input_configs.global_key : m_title_id; - g_cfg_profile.active_profiles.set_value(profile_key, m_profile); - g_cfg_profile.save(); + g_cfg_input_configs.active_configs.set_value(config_file_key, m_config_file); + g_cfg_input_configs.save(); - g_cfg_input.save(m_title_id, m_profile); + g_cfg_input.save(m_title_id, m_config_file); QDialog::accept(); } @@ -1864,7 +1877,7 @@ void pad_settings_dialog::SaveExit() void pad_settings_dialog::CancelExit() { // Reloads configs from file or defaults - g_cfg_profile.load(); + g_cfg_input_configs.load(); g_cfg_input.from_default(); QDialog::reject(); @@ -1956,6 +1969,7 @@ void pad_settings_dialog::SubscribeTooltips() const Tooltips tooltips; SubscribeTooltip(ui->gb_pressure_intensity, tooltips.gamepad_settings.pressure_intensity); + SubscribeTooltip(ui->gb_pressure_intensity_deadzone, tooltips.gamepad_settings.pressure_deadzone); SubscribeTooltip(ui->gb_squircle, tooltips.gamepad_settings.squircle_factor); SubscribeTooltip(ui->gb_stick_multi, tooltips.gamepad_settings.stick_multiplier); SubscribeTooltip(ui->gb_kb_stick_multi, tooltips.gamepad_settings.stick_multiplier); diff --git a/rpcs3/rpcs3qt/pad_settings_dialog.h b/rpcs3/rpcs3qt/pad_settings_dialog.h index 5d92b83285..2a4f15959f 100644 --- a/rpcs3/rpcs3qt/pad_settings_dialog.h +++ b/rpcs3/rpcs3qt/pad_settings_dialog.h @@ -72,7 +72,7 @@ class pad_settings_dialog : public QDialog id_reset_parameters, id_blacklist, id_refresh, - id_add_profile, + id_add_config_file, id_ok, id_cancel }; @@ -97,10 +97,10 @@ private Q_SLOTS: void OnTabChanged(int index); void RefreshHandlers(); void ChangeHandler(); - void ChangeProfile(const QString& profile); + void ChangeConfig(const QString& config_file); void ChangeDevice(int index); - void HandleDeviceClassChange(int index) const; - void AddProfile(); + void HandleDeviceClassChange(u32 class_id) const; + void AddConfigFile(); /** Update the current player config with the GUI values. */ void ApplyCurrentPlayerConfig(int new_player_id); void RefreshPads(); @@ -147,7 +147,7 @@ private: std::mutex m_handler_mutex; std::string m_device_name; std::string m_buddy_device_name; - std::string m_profile; + std::string m_config_file; QTimer m_timer_pad_refresh; int m_last_player_id = 0; diff --git a/rpcs3/rpcs3qt/pad_settings_dialog.ui b/rpcs3/rpcs3qt/pad_settings_dialog.ui index 6000014044..bac66e50e4 100644 --- a/rpcs3/rpcs3qt/pad_settings_dialog.ui +++ b/rpcs3/rpcs3qt/pad_settings_dialog.ui @@ -147,11 +147,11 @@
- + - Profiles + Configuration Files - + 5 @@ -165,12 +165,12 @@ 5 - + - + - Add Profile + Add Configuration false @@ -717,6 +717,22 @@ + + + + Pressure Sensitivity Deadzone + + + + + + Qt::Horizontal + + + + + + @@ -850,34 +866,6 @@
- - - - Stick Preview - - - - 5 - - - 5 - - - 5 - - - 5 - - - - - Show Emulated Values - - - - - - @@ -2369,6 +2357,34 @@ + + + + Stick Preview + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + Show Emulated Values + + + + + + diff --git a/rpcs3/rpcs3qt/progress_indicator.cpp b/rpcs3/rpcs3qt/progress_indicator.cpp index 335b5b0985..3130444fbf 100644 --- a/rpcs3/rpcs3qt/progress_indicator.cpp +++ b/rpcs3/rpcs3qt/progress_indicator.cpp @@ -1,6 +1,6 @@ #include "progress_indicator.h" -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF #include #include #elif HAVE_QTDBUS @@ -10,7 +10,7 @@ progress_indicator::progress_indicator(int minimum, int maximum) { -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF m_tb_button = std::make_unique(); m_tb_button->progress()->setRange(minimum, maximum); m_tb_button->progress()->setVisible(false); @@ -25,7 +25,7 @@ progress_indicator::progress_indicator(int minimum, int maximum) progress_indicator::~progress_indicator() { -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF // QWinTaskbarProgress::hide() will crash if the application is already about to close, even if the object is not null. if (!QCoreApplication::closingDown()) { @@ -38,7 +38,7 @@ progress_indicator::~progress_indicator() void progress_indicator::show(QWindow* window) { -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF m_tb_button->setWindow(window); m_tb_button->progress()->show(); #else @@ -48,7 +48,7 @@ void progress_indicator::show(QWindow* window) int progress_indicator::value() const { -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF return m_tb_button->progress()->value(); #else return m_value; @@ -57,7 +57,7 @@ int progress_indicator::value() const void progress_indicator::set_value(int value) { -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF m_tb_button->progress()->setValue(std::clamp(value, m_tb_button->progress()->minimum(), m_tb_button->progress()->maximum())); #else m_value = std::clamp(value, m_minimum, m_maximum); @@ -69,7 +69,7 @@ void progress_indicator::set_value(int value) void progress_indicator::set_range(int minimum, int maximum) { -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF m_tb_button->progress()->setRange(minimum, maximum); #else m_minimum = minimum; @@ -79,7 +79,7 @@ void progress_indicator::set_range(int minimum, int maximum) void progress_indicator::reset() { -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF m_tb_button->progress()->reset(); #else m_value = m_minimum; @@ -91,7 +91,7 @@ void progress_indicator::reset() void progress_indicator::signal_failure() { -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF m_tb_button->progress()->stop(); #elif HAVE_QTDBUS update_progress(0, false, true); diff --git a/rpcs3/rpcs3qt/progress_indicator.h b/rpcs3/rpcs3qt/progress_indicator.h index 1eccced1db..9e27f40ab4 100644 --- a/rpcs3/rpcs3qt/progress_indicator.h +++ b/rpcs3/rpcs3qt/progress_indicator.h @@ -2,7 +2,7 @@ #include -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF #include #endif @@ -23,7 +23,7 @@ public: private: -#ifdef _WIN32 +#ifdef HAS_QT_WIN_STUFF std::unique_ptr m_tb_button; #else int m_value = 0; diff --git a/rpcs3/rpcs3qt/qt_camera_error_handler.cpp b/rpcs3/rpcs3qt/qt_camera_error_handler.cpp index 9a05816268..f7bec4fee8 100644 --- a/rpcs3/rpcs3qt/qt_camera_error_handler.cpp +++ b/rpcs3/rpcs3qt/qt_camera_error_handler.cpp @@ -3,40 +3,14 @@ LOG_CHANNEL(camera_log, "Camera"); -template <> -void fmt_class_string::format(std::string& out, u64 arg) -{ - format_enum(out, arg, [](QCamera::Status value) - { - switch (value) - { - case QCamera::Status::UnavailableStatus: return "Unavailable"; - case QCamera::Status::UnloadedStatus: return "Unloaded"; - case QCamera::Status::LoadingStatus: return "Loading"; - case QCamera::Status::UnloadingStatus: return "Unloading"; - case QCamera::Status::LoadedStatus: return "Loaded"; - case QCamera::Status::StandbyStatus: return "Standby"; - case QCamera::Status::StartingStatus: return "Starting"; - case QCamera::Status::StoppingStatus: return "Stopping"; - case QCamera::Status::ActiveStatus: return "Active"; - } - - return unknown; - }); -} - -qt_camera_error_handler::qt_camera_error_handler(std::shared_ptr camera, std::function status_callback) +qt_camera_error_handler::qt_camera_error_handler(std::shared_ptr camera, std::function status_callback) : m_camera(std::move(camera)) , m_status_callback(std::move(status_callback)) { if (m_camera) { - connect(m_camera.get(), QOverload::of(&QCamera::availabilityChanged), this, &qt_camera_error_handler::handle_availability); - connect(m_camera.get(), &QCamera::stateChanged, this, &qt_camera_error_handler::handle_camera_state); - connect(m_camera.get(), &QCamera::statusChanged, this, &qt_camera_error_handler::handle_camera_status); + connect(m_camera.get(), &QCamera::activeChanged, this, &qt_camera_error_handler::handle_camera_active); connect(m_camera.get(), &QCamera::errorOccurred, this, &qt_camera_error_handler::handle_camera_error); - connect(m_camera.get(), &QCamera::captureModeChanged, this, &qt_camera_error_handler::handle_capture_modes); - connect(m_camera.get(), QOverload::of(&QCamera::lockStatusChanged), this, &qt_camera_error_handler::handle_lock_status); } } @@ -44,37 +18,17 @@ qt_camera_error_handler::~qt_camera_error_handler() { } -void qt_camera_error_handler::handle_availability(QMultimedia::AvailabilityStatus availability) +void qt_camera_error_handler::handle_camera_active(bool is_active) { - camera_log.notice("Camera availability changed to %d", static_cast(availability)); -} - -void qt_camera_error_handler::handle_camera_state(QCamera::State state) -{ - camera_log.notice("Camera state changed to %d", static_cast(state)); -} - -void qt_camera_error_handler::handle_camera_status(QCamera::Status status) -{ - camera_log.notice("Camera status changed to %s", status); + camera_log.notice("Camera active status changed to %d", is_active); if (m_status_callback) { - m_status_callback(status); + m_status_callback(is_active); } } -void qt_camera_error_handler::handle_lock_status(QCamera::LockStatus status, QCamera::LockChangeReason reason) +void qt_camera_error_handler::handle_camera_error(QCamera::Error error, const QString& errorString) { - camera_log.notice("Camera lock status changed to %d (reason=%d)", static_cast(status), static_cast(reason)); -} - -void qt_camera_error_handler::handle_capture_modes(QCamera::CaptureModes capture_modes) -{ - camera_log.notice("Camera capture modes changed to %d", static_cast(capture_modes)); -} - -void qt_camera_error_handler::handle_camera_error(QCamera::Error error) -{ - camera_log.error("Error event: \"%s\" (error=%d)", m_camera ? m_camera->errorString() : "", static_cast(error)); + camera_log.error("Error event: \"%s\" (error=%d)", errorString, static_cast(error)); } diff --git a/rpcs3/rpcs3qt/qt_camera_error_handler.h b/rpcs3/rpcs3qt/qt_camera_error_handler.h index cf97a120be..a2d2c81bac 100644 --- a/rpcs3/rpcs3qt/qt_camera_error_handler.h +++ b/rpcs3/rpcs3qt/qt_camera_error_handler.h @@ -8,18 +8,14 @@ class qt_camera_error_handler : public QObject Q_OBJECT public: - qt_camera_error_handler(std::shared_ptr camera, std::function status_callback); + qt_camera_error_handler(std::shared_ptr camera, std::function status_callback); virtual ~qt_camera_error_handler(); private Q_SLOTS: - void handle_availability(QMultimedia::AvailabilityStatus availability); - void handle_lock_status(QCamera::LockStatus, QCamera::LockChangeReason); - void handle_capture_modes(QCamera::CaptureModes capture_modes); - void handle_camera_state(QCamera::State state); - void handle_camera_status(QCamera::Status status); - void handle_camera_error(QCamera::Error error); + void handle_camera_active(bool is_active); + void handle_camera_error(QCamera::Error error, const QString& errorString); private: std::shared_ptr m_camera; - std::function m_status_callback = nullptr; + std::function m_status_callback = nullptr; }; diff --git a/rpcs3/rpcs3qt/qt_camera_handler.cpp b/rpcs3/rpcs3qt/qt_camera_handler.cpp index 8a7d0c55ea..500f9eb22d 100644 --- a/rpcs3/rpcs3qt/qt_camera_handler.cpp +++ b/rpcs3/rpcs3qt/qt_camera_handler.cpp @@ -5,17 +5,16 @@ #include "Emu/Io/camera_config.h" #include "Emu/Cell/lv2/sys_event.h" -#include -#include +#include LOG_CHANNEL(camera_log, "Camera"); qt_camera_handler::qt_camera_handler() : camera_handler_base() { // List available cameras - for (const QCameraInfo& cameraInfo : QCameraInfo::availableCameras()) + for (const QCameraDevice& camera_device : QMediaDevices::videoInputs()) { - camera_log.success("Found camera: name=%s, description=%s", cameraInfo.deviceName(), cameraInfo.description()); + camera_log.success("Found camera: id=%s, description=%s", camera_device.id().toStdString(), camera_device.description()); } if (!g_cfg_camera.load()) @@ -29,60 +28,53 @@ qt_camera_handler::~qt_camera_handler() Emu.BlockingCallFromMainThread([&]() { close_camera(); - m_surface.reset(); - m_camera.reset(); - m_error_handler.reset(); + reset(); }); } -void qt_camera_handler::set_camera(const QCameraInfo& camera_info) +void qt_camera_handler::reset() +{ + m_camera.reset(); + m_error_handler.reset(); + m_video_sink.reset(); + m_media_capture_session.reset(); +} + +void qt_camera_handler::set_camera(const QCameraDevice& camera_info) { if (camera_info.isNull()) { - m_surface.reset(); - m_camera.reset(); - m_error_handler.reset(); + reset(); return; } // Determine if the camera is front facing, in which case we will need to flip the image horizontally. - const bool front_facing = camera_info.position() == QCamera::Position::FrontFace; + const bool front_facing = camera_info.position() == QCameraDevice::Position::FrontFace; - camera_log.success("Using camera: name=\"%s\", description=\"%s\", front_facing=%d", camera_info.deviceName(), camera_info.description(), front_facing); + camera_log.success("Using camera: id=\"%s\", description=\"%s\", front_facing=%d", camera_info.id().toStdString(), camera_info.description(), front_facing); // Create camera and video surface - m_surface.reset(new qt_camera_video_surface(front_facing, nullptr)); + m_media_capture_session.reset(new QMediaCaptureSession(nullptr)); + m_video_sink.reset(new qt_camera_video_sink(front_facing, nullptr)); m_camera.reset(new QCamera(camera_info)); m_error_handler.reset(new qt_camera_error_handler(m_camera, - [this](QCamera::Status status) + [this](bool is_active) { - switch (status) + if (is_active) { - case QCamera::UnavailableStatus: - m_state = camera_handler_state::not_available; - break; - case QCamera::UnloadedStatus: - case QCamera::UnloadingStatus: - m_state = camera_handler_state::closed; - break; - case QCamera::StandbyStatus: - case QCamera::StoppingStatus: - case QCamera::LoadedStatus: - case QCamera::LoadingStatus: - m_state = camera_handler_state::open; - break; - case QCamera::StartingStatus: - case QCamera::ActiveStatus: m_state = camera_handler_state::running; - break; - default: - camera_log.error("Ignoring unknown status %d", static_cast(status)); - break; + } + else + { + m_state = camera_handler_state::closed; } })); - // Set view finder and update the settings - m_camera->setViewfinder(m_surface.get()); + // Setup video sink + m_media_capture_session->setCamera(m_camera.get()); + m_media_capture_session->setVideoSink(m_video_sink.get()); + + // Update the settings update_camera_settings(); } @@ -94,25 +86,25 @@ void qt_camera_handler::open_camera() m_camera_id != camera_id) { camera_log.notice("Switching camera from %s to %s", m_camera_id, camera_id); - camera_log.notice("Unloading old camera..."); - if (m_camera) m_camera->unload(); + camera_log.notice("Stopping old camera..."); + if (m_camera) m_camera->stop(); m_camera_id = camera_id; } - QCameraInfo selected_camera; + QCameraDevice selected_camera{}; if (m_camera_id == g_cfg.io.camera_id.def) { - selected_camera = QCameraInfo::defaultCamera(); + selected_camera = QMediaDevices::defaultVideoInput(); } else if (!m_camera_id.empty()) { const QString camera_id = QString::fromStdString(m_camera_id); - for (const QCameraInfo& camera_info : QCameraInfo::availableCameras()) + for (const QCameraDevice& camera_device : QMediaDevices::videoInputs()) { - if (camera_id == camera_info.deviceName()) + if (camera_id == camera_device.id()) { - selected_camera = camera_info; + selected_camera = camera_device; break; } } @@ -124,35 +116,26 @@ void qt_camera_handler::open_camera() { if (m_camera_id.empty()) camera_log.notice("Camera disabled"); else camera_log.error("No camera found"); - m_state = camera_handler_state::not_available; + m_state = camera_handler_state::closed; return; } - if (m_camera->state() != QCamera::State::UnloadedState) + if (m_camera->isActive()) { - camera_log.notice("Camera already loaded"); + camera_log.notice("Camera already active"); return; } - // Load/open camera - m_camera->load(); - // List all supported formats for debugging - for (const QCamera::FrameRateRange& frame_rate : m_camera->supportedViewfinderFrameRateRanges()) + for (const QCameraFormat& format : m_camera->cameraDevice().videoFormats()) { - camera_log.notice("Supported frame rate range: %f-%f", frame_rate.minimumFrameRate, frame_rate.maximumFrameRate); - } - for (const QVideoFrame::PixelFormat& pixel_format : m_camera->supportedViewfinderPixelFormats()) - { - camera_log.notice("Supported pixel format: %d", static_cast(pixel_format)); - } - for (const QSize& resolution : m_camera->supportedViewfinderResolutions()) - { - camera_log.notice("Supported resolution: %dx%d", resolution.width(), resolution.height()); + camera_log.notice("Supported format: pixelformat=%s, resolution=%dx%d framerate=%f-%f", format.pixelFormat(), format.resolution().width(), format.resolution().height(), format.minFrameRate(), format.maxFrameRate()); } // Update camera and view finder settings update_camera_settings(); + + m_state = camera_handler_state::open; } void qt_camera_handler::close_camera() @@ -163,18 +146,12 @@ void qt_camera_handler::close_camera() { if (m_camera_id.empty()) camera_log.notice("Camera disabled"); else camera_log.error("No camera found"); - m_state = camera_handler_state::not_available; - return; - } - - if (m_camera->state() == QCamera::State::UnloadedState) - { - camera_log.notice("Camera already unloaded"); + m_state = camera_handler_state::closed; return; } // Unload/close camera - m_camera->unload(); + m_camera->stop(); } void qt_camera_handler::start_camera() @@ -185,22 +162,16 @@ void qt_camera_handler::start_camera() { if (m_camera_id.empty()) camera_log.notice("Camera disabled"); else camera_log.error("No camera found"); - m_state = camera_handler_state::not_available; + m_state = camera_handler_state::closed; return; } - if (m_camera->state() == QCamera::State::ActiveState) + if (m_camera->isActive()) { camera_log.notice("Camera already started"); return; } - if (m_camera->state() == QCamera::State::UnloadedState) - { - camera_log.notice("Camera not open"); - open_camera(); - } - // Start camera. We will start receiving frames now. m_camera->start(); } @@ -213,11 +184,11 @@ void qt_camera_handler::stop_camera() { if (m_camera_id.empty()) camera_log.notice("Camera disabled"); else camera_log.error("No camera found"); - m_state = camera_handler_state::not_available; + m_state = camera_handler_state::closed; return; } - if (m_camera->state() == QCamera::State::LoadedState) + if (!m_camera->isActive()) { camera_log.notice("Camera already stopped"); return; @@ -232,9 +203,9 @@ void qt_camera_handler::set_format(s32 format, u32 bytesize) m_format = format; m_bytesize = bytesize; - if (m_surface) + if (m_video_sink) { - m_surface->set_format(m_format, m_bytesize); + m_video_sink->set_format(m_format, m_bytesize); } } @@ -248,9 +219,9 @@ void qt_camera_handler::set_resolution(u32 width, u32 height) m_width = width; m_height = height; - if (m_surface) + if (m_video_sink) { - m_surface->set_resolution(m_width, m_height); + m_video_sink->set_resolution(m_width, m_height); } } @@ -258,15 +229,15 @@ void qt_camera_handler::set_mirrored(bool mirrored) { m_mirrored = mirrored; - if (m_surface) + if (m_video_sink) { - m_surface->set_mirrored(m_mirrored); + m_video_sink->set_mirrored(m_mirrored); } } u64 qt_camera_handler::frame_number() const { - return m_surface ? m_surface->frame_number() : 0; + return m_video_sink ? m_video_sink->frame_number() : 0; } camera_handler_base::camera_handler_state qt_camera_handler::get_image(u8* buf, u64 size, u32& width, u32& height, u64& frame_number, u64& bytes_read) @@ -280,22 +251,22 @@ camera_handler_base::camera_handler_state qt_camera_handler::get_image(u8* buf, m_camera_id != camera_id) { camera_log.notice("Switching cameras"); - m_state = camera_handler_state::not_available; - return camera_handler_state::not_available; + m_state = camera_handler_state::closed; + return camera_handler_state::closed; } if (m_camera_id.empty()) { camera_log.notice("Camera disabled"); - m_state = camera_handler_state::not_available; - return camera_handler_state::not_available; + m_state = camera_handler_state::closed; + return camera_handler_state::closed; } - if (!m_camera || !m_surface) + if (!m_camera || !m_video_sink) { camera_log.fatal("Error: camera invalid"); - m_state = camera_handler_state::not_available; - return camera_handler_state::not_available; + m_state = camera_handler_state::closed; + return camera_handler_state::closed; } // Backup current state. State may change through events. @@ -304,7 +275,7 @@ camera_handler_base::camera_handler_state qt_camera_handler::get_image(u8* buf, if (current_state == camera_handler_state::running) { // Copy latest image into out buffer. - m_surface->get_image(buf, size, width, height, frame_number, bytes_read); + m_video_sink->get_image(buf, size, width, height, frame_number, bytes_read); } else { @@ -317,7 +288,7 @@ camera_handler_base::camera_handler_state qt_camera_handler::get_image(u8* buf, void qt_camera_handler::update_camera_settings() { // Update camera if possible. We can only do this if it is already loaded. - if (m_camera && m_camera->state() != QCamera::State::UnloadedState) + if (m_camera && m_camera->isAvailable()) { // Load selected settings from config file bool success = false; @@ -327,32 +298,23 @@ void qt_camera_handler::update_camera_settings() { camera_log.notice("Found config entry for camera \"%s\"", m_camera_id); - QCameraViewfinderSettings setting; - setting.setResolution(cfg_setting.width, cfg_setting.height); - setting.setMinimumFrameRate(cfg_setting.min_fps); - setting.setMaximumFrameRate(cfg_setting.max_fps); - setting.setPixelFormat(static_cast(cfg_setting.format)); - setting.setPixelAspectRatio(cfg_setting.pixel_aspect_width, cfg_setting.pixel_aspect_height); - // List all available settings and choose the proper value if possible. const double epsilon = 0.001; success = false; - for (const QCameraViewfinderSettings& supported_setting : m_camera->supportedViewfinderSettings(setting)) + for (const QCameraFormat& supported_setting : m_camera->cameraDevice().videoFormats()) { - if (supported_setting.resolution().width() == setting.resolution().width() && - supported_setting.resolution().height() == setting.resolution().height() && - supported_setting.minimumFrameRate() >= (setting.minimumFrameRate() - epsilon) && - supported_setting.minimumFrameRate() <= (setting.minimumFrameRate() + epsilon) && - supported_setting.maximumFrameRate() >= (setting.maximumFrameRate() - epsilon) && - supported_setting.maximumFrameRate() <= (setting.maximumFrameRate() + epsilon) && - supported_setting.pixelFormat() == setting.pixelFormat() && - supported_setting.pixelAspectRatio().width() == setting.pixelAspectRatio().width() && - supported_setting.pixelAspectRatio().height() == setting.pixelAspectRatio().height()) + if (supported_setting.resolution().width() == cfg_setting.width && + supported_setting.resolution().height() == cfg_setting.height && + supported_setting.minFrameRate() >= (cfg_setting.min_fps - epsilon) && + supported_setting.minFrameRate() <= (cfg_setting.min_fps + epsilon) && + supported_setting.maxFrameRate() >= (cfg_setting.max_fps - epsilon) && + supported_setting.maxFrameRate() <= (cfg_setting.max_fps + epsilon) && + supported_setting.pixelFormat() == static_cast(cfg_setting.format)) { // Apply settings. camera_log.notice("Setting view finder settings: frame_rate=%f, width=%d, height=%d, pixel_format=%s", - supported_setting.maximumFrameRate(), supported_setting.resolution().width(), supported_setting.resolution().height(), supported_setting.pixelFormat()); - m_camera->setViewfinderSettings(supported_setting); + supported_setting.maxFrameRate(), supported_setting.resolution().width(), supported_setting.resolution().height(), supported_setting.pixelFormat()); + m_camera->setCameraFormat(supported_setting); success = true; break; } @@ -372,10 +334,10 @@ void qt_camera_handler::update_camera_settings() } // Update video surface if possible - if (m_surface) + if (m_video_sink) { - m_surface->set_resolution(m_width, m_height); - m_surface->set_format(m_format, m_bytesize); - m_surface->set_mirrored(m_mirrored); + m_video_sink->set_resolution(m_width, m_height); + m_video_sink->set_format(m_format, m_bytesize); + m_video_sink->set_mirrored(m_mirrored); } } diff --git a/rpcs3/rpcs3qt/qt_camera_handler.h b/rpcs3/rpcs3qt/qt_camera_handler.h index 0b4f707354..4cf1f01a7e 100644 --- a/rpcs3/rpcs3qt/qt_camera_handler.h +++ b/rpcs3/rpcs3qt/qt_camera_handler.h @@ -1,12 +1,12 @@ #pragma once #include "Emu/Io/camera_handler_base.h" -#include "qt_camera_video_surface.h" +#include "qt_camera_video_sink.h" #include "qt_camera_error_handler.h" #include -#include -#include +#include +#include class qt_camera_handler final : public camera_handler_base { @@ -14,7 +14,7 @@ public: qt_camera_handler(); virtual ~qt_camera_handler(); - void set_camera(const QCameraInfo& camera_info); + void set_camera(const QCameraDevice& camera_info); void open_camera() override; void close_camera() override; @@ -28,10 +28,12 @@ public: camera_handler_state get_image(u8* buf, u64 size, u32& width, u32& height, u64& frame_number, u64& bytes_read) override; private: + void reset(); void update_camera_settings(); std::string m_camera_id; std::shared_ptr m_camera; - std::unique_ptr m_surface; + std::unique_ptr m_media_capture_session; + std::unique_ptr m_video_sink; std::unique_ptr m_error_handler; }; diff --git a/rpcs3/rpcs3qt/qt_camera_video_surface.cpp b/rpcs3/rpcs3qt/qt_camera_video_sink.cpp similarity index 78% rename from rpcs3/rpcs3qt/qt_camera_video_surface.cpp rename to rpcs3/rpcs3qt/qt_camera_video_sink.cpp index 459c632d0d..4f19155be9 100644 --- a/rpcs3/rpcs3qt/qt_camera_video_surface.cpp +++ b/rpcs3/rpcs3qt/qt_camera_video_sink.cpp @@ -1,5 +1,5 @@ #include "stdafx.h" -#include "qt_camera_video_surface.h" +#include "qt_camera_video_sink.h" #include "Emu/Cell/Modules/cellCamera.h" #include "Emu/system_config.h" @@ -8,12 +8,13 @@ LOG_CHANNEL(camera_log, "Camera"); -qt_camera_video_surface::qt_camera_video_surface(bool front_facing, QObject *parent) - : QAbstractVideoSurface(parent), m_front_facing(front_facing) +qt_camera_video_sink::qt_camera_video_sink(bool front_facing, QObject *parent) + : QVideoSink(parent), m_front_facing(front_facing) { + connect(this, &QVideoSink::videoFrameChanged, this, &qt_camera_video_sink::present); } -qt_camera_video_surface::~qt_camera_video_surface() +qt_camera_video_sink::~qt_camera_video_sink() { std::lock_guard lock(m_mutex); @@ -28,51 +29,7 @@ qt_camera_video_surface::~qt_camera_video_surface() } } -QList qt_camera_video_surface::supportedPixelFormats(QAbstractVideoBuffer::HandleType type) const -{ - Q_UNUSED(type) - - // Support all cameras - QList result; - result - << QVideoFrame::Format_ARGB32 - << QVideoFrame::Format_ARGB32_Premultiplied - << QVideoFrame::Format_RGB32 - << QVideoFrame::Format_RGB24 - << QVideoFrame::Format_RGB565 - << QVideoFrame::Format_RGB555 - << QVideoFrame::Format_ARGB8565_Premultiplied - << QVideoFrame::Format_BGRA32 - << QVideoFrame::Format_BGRA32_Premultiplied - << QVideoFrame::Format_BGR32 - << QVideoFrame::Format_BGR24 - << QVideoFrame::Format_BGR565 - << QVideoFrame::Format_BGR555 - << QVideoFrame::Format_BGRA5658_Premultiplied - << QVideoFrame::Format_AYUV444 - << QVideoFrame::Format_AYUV444_Premultiplied - << QVideoFrame::Format_YUV444 - << QVideoFrame::Format_YUV420P - << QVideoFrame::Format_YV12 - << QVideoFrame::Format_UYVY - << QVideoFrame::Format_YUYV - << QVideoFrame::Format_NV12 - << QVideoFrame::Format_NV21 - << QVideoFrame::Format_IMC1 - << QVideoFrame::Format_IMC2 - << QVideoFrame::Format_IMC3 - << QVideoFrame::Format_IMC4 - << QVideoFrame::Format_Y8 - << QVideoFrame::Format_Y16 - << QVideoFrame::Format_Jpeg - << QVideoFrame::Format_CameraRaw - << QVideoFrame::Format_AdobeDng - << QVideoFrame::Format_ABGR32 - << QVideoFrame::Format_YUV422P; - return result; -} - -bool qt_camera_video_surface::present(const QVideoFrame& frame) +bool qt_camera_video_sink::present(const QVideoFrame& frame) { if (!frame.isValid()) { @@ -82,18 +39,18 @@ bool qt_camera_video_surface::present(const QVideoFrame& frame) // Get video image. Map frame for faster read operations. QVideoFrame tmp(frame); - if (!tmp.map(QAbstractVideoBuffer::ReadOnly)) + if (!tmp.map(QVideoFrame::ReadOnly)) { camera_log.error("Failed to map video frame"); return false; } // Get image. This usually also converts the image to ARGB32. - QImage image = frame.image(); + QImage image = frame.toImage(); if (image.isNull()) { - camera_log.warning("Image is invalid: pixel_format=%s, format=%d", tmp.pixelFormat(), static_cast(QVideoFrame::imageFormatFromPixelFormat(tmp.pixelFormat()))); + camera_log.warning("Image is invalid: pixel_format=%s, format=%d", tmp.pixelFormat(), static_cast(QVideoFrameFormat::imageFormatFromPixelFormat(tmp.pixelFormat()))); } else { @@ -293,7 +250,7 @@ bool qt_camera_video_surface::present(const QVideoFrame& frame) return true; } -void qt_camera_video_surface::set_format(s32 format, u32 bytesize) +void qt_camera_video_sink::set_format(s32 format, u32 bytesize) { camera_log.notice("Setting format: format=%d, bytesize=%d", format, bytesize); @@ -301,7 +258,7 @@ void qt_camera_video_surface::set_format(s32 format, u32 bytesize) m_bytesize = bytesize; } -void qt_camera_video_surface::set_resolution(u32 width, u32 height) +void qt_camera_video_sink::set_resolution(u32 width, u32 height) { camera_log.notice("Setting resolution: width=%d, height=%d", width, height); @@ -309,19 +266,19 @@ void qt_camera_video_surface::set_resolution(u32 width, u32 height) m_height = height; } -void qt_camera_video_surface::set_mirrored(bool mirrored) +void qt_camera_video_sink::set_mirrored(bool mirrored) { camera_log.notice("Setting mirrored: mirrored=%d", mirrored); m_mirrored = mirrored; } -u64 qt_camera_video_surface::frame_number() const +u64 qt_camera_video_sink::frame_number() const { return m_frame_number.load(); } -void qt_camera_video_surface::get_image(u8* buf, u64 size, u32& width, u32& height, u64& frame_number, u64& bytes_read) +void qt_camera_video_sink::get_image(u8* buf, u64 size, u32& width, u32& height, u64& frame_number, u64& bytes_read) { // Lock read buffer std::lock_guard lock(m_mutex); @@ -348,7 +305,7 @@ void qt_camera_video_surface::get_image(u8* buf, u64 size, u32& width, u32& heig } } -u32 qt_camera_video_surface::read_index() const +u32 qt_camera_video_sink::read_index() const { // The read buffer index cannot be the same as the write index return (m_write_index + 1u) % ::narrow(m_image_buffer.size()); diff --git a/rpcs3/rpcs3qt/qt_camera_video_surface.h b/rpcs3/rpcs3qt/qt_camera_video_sink.h similarity index 66% rename from rpcs3/rpcs3qt/qt_camera_video_surface.h rename to rpcs3/rpcs3qt/qt_camera_video_sink.h index 4cd42a4cd5..3385a5048f 100644 --- a/rpcs3/rpcs3qt/qt_camera_video_surface.h +++ b/rpcs3/rpcs3qt/qt_camera_video_sink.h @@ -1,18 +1,18 @@ #pragma once -#include +#include +#include #include #include -class qt_camera_video_surface final : public QAbstractVideoSurface +class qt_camera_video_sink final : public QVideoSink { public: - qt_camera_video_surface(bool front_facing, QObject *parent = nullptr); - virtual ~qt_camera_video_surface(); + qt_camera_video_sink(bool front_facing, QObject *parent = nullptr); + virtual ~qt_camera_video_sink(); - QList supportedPixelFormats(QAbstractVideoBuffer::HandleType type = QAbstractVideoBuffer::NoHandle) const override; - bool present(const QVideoFrame& frame) override; + bool present(const QVideoFrame& frame); void set_format(s32 format, u32 bytesize); void set_resolution(u32 width, u32 height); diff --git a/rpcs3/rpcs3qt/qt_music_error_handler.cpp b/rpcs3/rpcs3qt/qt_music_error_handler.cpp index a75bde94b8..347e52934a 100644 --- a/rpcs3/rpcs3qt/qt_music_error_handler.cpp +++ b/rpcs3/rpcs3qt/qt_music_error_handler.cpp @@ -15,8 +15,6 @@ void fmt_class_string::format(std::string& out, u64 arg) case QMediaPlayer::Error::FormatError: return "FormatError"; case QMediaPlayer::Error::NetworkError: return "NetworkError"; case QMediaPlayer::Error::AccessDeniedError: return "AccessDeniedError"; - case QMediaPlayer::Error::ServiceMissingError: return "ServiceMissingError"; - case QMediaPlayer::Error::MediaIsPlaylist: return "MediaIsPlaylist"; } return unknown; @@ -30,7 +28,6 @@ void fmt_class_string::format(std::string& out, u64 a { switch (value) { - case QMediaPlayer::MediaStatus::UnknownMediaStatus: return "UnknownMediaStatus"; case QMediaPlayer::MediaStatus::NoMedia: return "NoMedia"; case QMediaPlayer::MediaStatus::LoadingMedia: return "LoadingMedia"; case QMediaPlayer::MediaStatus::LoadedMedia: return "LoadedMedia"; @@ -46,15 +43,15 @@ void fmt_class_string::format(std::string& out, u64 a } template <> -void fmt_class_string::format(std::string& out, u64 arg) +void fmt_class_string::format(std::string& out, u64 arg) { - format_enum(out, arg, [](QMediaPlayer::State value) + format_enum(out, arg, [](QMediaPlayer::PlaybackState value) { switch (value) { - case QMediaPlayer::State::StoppedState: return "StoppedState"; - case QMediaPlayer::State::PlayingState: return "PlayingState"; - case QMediaPlayer::State::PausedState: return "PausedState"; + case QMediaPlayer::PlaybackState::StoppedState: return "StoppedState"; + case QMediaPlayer::PlaybackState::PlayingState: return "PlayingState"; + case QMediaPlayer::PlaybackState::PausedState: return "PausedState"; } return unknown; @@ -68,8 +65,8 @@ qt_music_error_handler::qt_music_error_handler(std::shared_ptr med if (m_media_player) { connect(m_media_player.get(), &QMediaPlayer::mediaStatusChanged, this, &qt_music_error_handler::handle_media_status); - connect(m_media_player.get(), &QMediaPlayer::stateChanged, this, &qt_music_error_handler::handle_music_state); - connect(m_media_player.get(), QOverload::of(&QMediaPlayer::error), this, &qt_music_error_handler::handle_music_error); + connect(m_media_player.get(), &QMediaPlayer::playbackStateChanged, this, &qt_music_error_handler::handle_music_state); + connect(m_media_player.get(), &QMediaPlayer::errorOccurred, this, &qt_music_error_handler::handle_music_error); } } @@ -87,12 +84,12 @@ void qt_music_error_handler::handle_media_status(QMediaPlayer::MediaStatus statu } } -void qt_music_error_handler::handle_music_state(QMediaPlayer::State state) +void qt_music_error_handler::handle_music_state(QMediaPlayer::PlaybackState state) { music_log.notice("New playback state: %s (state=%d)", state, static_cast(state)); } -void qt_music_error_handler::handle_music_error(QMediaPlayer::Error error) +void qt_music_error_handler::handle_music_error(QMediaPlayer::Error error, const QString& errorString) { - music_log.error("Error event: \"%s\" (error=%s)", m_media_player ? m_media_player->errorString() : "", error); + music_log.error("Error event: \"%s\" (error=%s)", errorString, error); } diff --git a/rpcs3/rpcs3qt/qt_music_error_handler.h b/rpcs3/rpcs3qt/qt_music_error_handler.h index 11c5ff3fbd..7d19c22d5c 100644 --- a/rpcs3/rpcs3qt/qt_music_error_handler.h +++ b/rpcs3/rpcs3qt/qt_music_error_handler.h @@ -13,8 +13,8 @@ public: private Q_SLOTS: void handle_media_status(QMediaPlayer::MediaStatus status); - void handle_music_state(QMediaPlayer::State state); - void handle_music_error(QMediaPlayer::Error error); + void handle_music_state(QMediaPlayer::PlaybackState state); + void handle_music_error(QMediaPlayer::Error error, const QString& errorString); private: std::shared_ptr m_media_player; diff --git a/rpcs3/rpcs3qt/qt_music_handler.cpp b/rpcs3/rpcs3qt/qt_music_handler.cpp index 39c40a5915..d92d379cd9 100644 --- a/rpcs3/rpcs3qt/qt_music_handler.cpp +++ b/rpcs3/rpcs3qt/qt_music_handler.cpp @@ -4,6 +4,7 @@ #include "Utilities/Thread.h" #include "util/logs.hpp" +#include #include LOG_CHANNEL(music_log, "Music"); @@ -13,7 +14,7 @@ qt_music_handler::qt_music_handler() music_log.notice("Constructing Qt music handler..."); m_media_player = std::make_shared(); - m_media_player->setAudioRole(QAudio::Role::MusicRole); + m_media_player->setAudioOutput(new QAudioOutput()); m_error_handler = std::make_unique(m_media_player, [this](QMediaPlayer::MediaStatus status) @@ -25,7 +26,6 @@ qt_music_handler::qt_music_handler() switch (status) { - case QMediaPlayer::MediaStatus::UnknownMediaStatus: case QMediaPlayer::MediaStatus::NoMedia: case QMediaPlayer::MediaStatus::LoadingMedia: case QMediaPlayer::MediaStatus::LoadedMedia: @@ -90,7 +90,7 @@ void qt_music_handler::play(const std::string& path) if (m_path != path) { m_path = path; - m_media_player->setMedia(QUrl(QString::fromStdString(path))); + m_media_player->setSource(QUrl::fromLocalFile(QString::fromStdString(path))); } music_log.notice("Playing music: %s", path); @@ -110,7 +110,7 @@ void qt_music_handler::fast_forward(const std::string& path) if (m_path != path) { m_path = path; - m_media_player->setMedia(QUrl(QString::fromStdString(path))); + m_media_player->setSource(QUrl::fromLocalFile(QString::fromStdString(path))); } music_log.notice("Fast-forwarding music..."); @@ -130,7 +130,7 @@ void qt_music_handler::fast_reverse(const std::string& path) if (m_path != path) { m_path = path; - m_media_player->setMedia(QUrl(QString::fromStdString(path))); + m_media_player->setSource(QUrl::fromLocalFile(QString::fromStdString(path))); } music_log.notice("Fast-reversing music..."); @@ -149,7 +149,7 @@ void qt_music_handler::set_volume(f32 volume) { const int new_volume = std::max(0, std::min(volume * 100, 100)); music_log.notice("Setting volume to %d%%", new_volume); - m_media_player->setVolume(new_volume); + m_media_player->audioOutput()->setVolume(new_volume); }); } @@ -160,9 +160,8 @@ f32 qt_music_handler::get_volume() const Emu.BlockingCallFromMainThread([&volume, this]() { - const int current_volume = std::max(0, std::min(m_media_player->volume(), 100)); - music_log.notice("Getting volume: %d%%", current_volume); - volume = current_volume / 100.0f; + volume = std::max(0.f, std::min(m_media_player->audioOutput()->volume(), 1.f)); + music_log.notice("Getting volume: %d%%", volume); }); return volume; diff --git a/rpcs3/rpcs3qt/qt_utils.cpp b/rpcs3/rpcs3qt/qt_utils.cpp index 3f2023e393..261676f919 100644 --- a/rpcs3/rpcs3qt/qt_utils.cpp +++ b/rpcs3/rpcs3qt/qt_utils.cpp @@ -164,13 +164,13 @@ namespace gui return icon; } - QStringList get_dir_entries(const QDir& dir, const QStringList& name_filters) + QStringList get_dir_entries(const QDir& dir, const QStringList& name_filters, bool full_path) { QFileInfoList entries = dir.entryInfoList(name_filters, QDir::Files); QStringList res; - for (const QFileInfo &entry : entries) + for (const QFileInfo& entry : entries) { - res.append(entry.baseName()); + res.append(full_path ? entry.absoluteFilePath() : entry.baseName()); } return res; } diff --git a/rpcs3/rpcs3qt/qt_utils.h b/rpcs3/rpcs3qt/qt_utils.h index 6aa6032242..6607dca350 100644 --- a/rpcs3/rpcs3qt/qt_utils.h +++ b/rpcs3/rpcs3qt/qt_utils.h @@ -63,7 +63,7 @@ namespace gui QIcon get_colorized_icon(const QIcon& old_icon, const QColor& old_color, const std::map& new_colors, bool use_special_masks = false, bool colorize_all = false); // Returns a list of all base names of files in dir whose complete file names contain one of the given name_filters - QStringList get_dir_entries(const QDir& dir, const QStringList& name_filters); + QStringList get_dir_entries(const QDir& dir, const QStringList& name_filters, bool full_path = false); // Returns the color specified by its color_role for the QLabels with object_name QColor get_label_color(const QString& object_name, QPalette::ColorRole color_role = QPalette::WindowText); @@ -142,7 +142,7 @@ namespace gui template void stop_future_watcher(QFutureWatcher& watcher, bool cancel, std::shared_ptr> cancel_flag = nullptr) { - if (watcher.isPaused() || watcher.isRunning()) + if (watcher.isSuspended() || watcher.isRunning()) { watcher.resume(); diff --git a/rpcs3/rpcs3qt/register_editor_dialog.cpp b/rpcs3/rpcs3qt/register_editor_dialog.cpp index d941ee7762..98da5fe7da 100644 --- a/rpcs3/rpcs3qt/register_editor_dialog.cpp +++ b/rpcs3/rpcs3qt/register_editor_dialog.cpp @@ -156,7 +156,7 @@ register_editor_dialog::register_editor_dialog(QWidget *parent, CPUDisAsm* _disa connect(button_cancel, &QAbstractButton::clicked, this, ®ister_editor_dialog::reject); connect(m_register_combo, &QComboBox::currentTextChanged, this, [this](const QString&) { - if (const auto qvar = m_register_combo->currentData(); qvar.canConvert(QMetaType::Int)) + if (const auto qvar = m_register_combo->currentData(); qvar.canConvert()) { updateRegister(qvar.toInt()); } diff --git a/rpcs3/rpcs3qt/rpcn_settings_dialog.cpp b/rpcs3/rpcs3qt/rpcn_settings_dialog.cpp index 2dff073574..106f60cee1 100644 --- a/rpcs3/rpcs3qt/rpcn_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/rpcn_settings_dialog.cpp @@ -257,10 +257,10 @@ rpcn_account_dialog::rpcn_account_dialog(QWidget* parent) case rpcn::ErrorType::CreationExistingUsername: error_message = tr("An account with that username already exists!"); break; case rpcn::ErrorType::CreationBannedEmailProvider: error_message = tr("This email provider is banned!"); break; case rpcn::ErrorType::CreationExistingEmail: error_message = tr("An account with that email already exists!"); break; - case rpcn::ErrorType::CreationError: error_message = tr("Unknown creation error"); break; + case rpcn::ErrorType::CreationError: error_message = tr("Unknown creation error!"); break; default: error_message = tr("Unknown error"); break; } - QMessageBox::critical(this, tr("Error Creating Account"), tr("Failed to create the account:\n%0").arg(error_message), QMessageBox::Ok); + QMessageBox::critical(this, tr("Error Creating Account!"), tr("Failed to create the account:\n%0").arg(error_message), QMessageBox::Ok); return; } } @@ -361,12 +361,12 @@ rpcn_add_server_dialog::rpcn_add_server_dialog(QWidget* parent) if (description.isEmpty()) { - QMessageBox::critical(this, tr("Missing Description"), tr("You must enter a description!"), QMessageBox::Ok); + QMessageBox::critical(this, tr("Missing Description!"), tr("You must enter a description!"), QMessageBox::Ok); return; } if (host.isEmpty()) { - QMessageBox::critical(this, tr("Missing Hostname"), tr("You must enter a hostname for the server!"), QMessageBox::Ok); + QMessageBox::critical(this, tr("Missing Hostname!"), tr("You must enter a hostname for the server!"), QMessageBox::Ok); return; } @@ -413,12 +413,12 @@ rpcn_ask_username_dialog::rpcn_ask_username_dialog(QWidget* parent, const QStrin if (username.empty()) { - QMessageBox::critical(this, tr("Missing Username"), tr("You must enter a username!"), QMessageBox::Ok); + QMessageBox::critical(this, tr("Missing Username!"), tr("You must enter a username!"), QMessageBox::Ok); return; } if (!validate_rpcn_username(username)) { - QMessageBox::critical(this, tr("Invalid Username"), tr("Please enter a valid username!"), QMessageBox::Ok); + QMessageBox::critical(this, tr("Invalid Username!"), tr("Please enter a valid username!"), QMessageBox::Ok); } m_username = username; @@ -729,7 +729,7 @@ void rpcn_account_edit_dialog::resend_token() if (auto result = rpcn->wait_for_connection(); result != rpcn::rpcn_state::failure_no_failure) { const QString error_message = tr("Failed to connect to RPCN server:\n%0").arg(QString::fromStdString(rpcn::rpcn_state_to_string(result))); - QMessageBox::critical(this, tr("Error Connecting"), error_message, QMessageBox::Ok); + QMessageBox::critical(this, tr("Error Connecting!"), error_message, QMessageBox::Ok); return; } @@ -745,7 +745,7 @@ void rpcn_account_edit_dialog::resend_token() case rpcn::ErrorType::LoginError: error_message = tr("The username/password pair is invalid!"); break; default: error_message = tr("Unknown error"); break; } - QMessageBox::critical(this, tr("Error Sending Token"), tr("Failed to send the token:\n%0").arg(error_message), QMessageBox::Ok); + QMessageBox::critical(this, tr("Error Sending Token!"), tr("Failed to send the token:\n%0").arg(error_message), QMessageBox::Ok); return; } @@ -778,7 +778,7 @@ void rpcn_account_edit_dialog::change_password() if (auto result = rpcn->wait_for_connection(); result != rpcn::rpcn_state::failure_no_failure) { const QString error_message = tr("Failed to connect to RPCN server:\n%0").arg(QString::fromStdString(rpcn::rpcn_state_to_string(result))); - QMessageBox::critical(this, tr("Error Connecting"), error_message, QMessageBox::Ok); + QMessageBox::critical(this, tr("Error Connecting!"), error_message, QMessageBox::Ok); return; } @@ -792,9 +792,9 @@ void rpcn_account_edit_dialog::change_password() case rpcn::ErrorType::TooSoon: error_message = tr("You can only ask for a reset password token once every 24 hours!"); break; case rpcn::ErrorType::EmailFail: error_message = tr("The mail couldn't be sent successfully!"); break; case rpcn::ErrorType::LoginError: error_message = tr("The username/email pair is invalid!"); break; - default: error_message = tr("Unknown error"); break; + default: error_message = tr("Unknown error!"); break; } - QMessageBox::critical(this, tr("Error Sending Password Reset Token"), tr("Failed to send the password reset token:\n%0").arg(error_message), QMessageBox::Ok); + QMessageBox::critical(this, tr("Error Sending Password Reset Token!"), tr("Failed to send the password reset token:\n%0").arg(error_message), QMessageBox::Ok); return; } @@ -822,7 +822,7 @@ void rpcn_account_edit_dialog::change_password() if (auto result = rpcn->wait_for_connection(); result != rpcn::rpcn_state::failure_no_failure) { const QString error_message = tr("Failed to connect to RPCN server:\n%0").arg(QString::fromStdString(rpcn::rpcn_state_to_string(result))); - QMessageBox::critical(this, tr("Error Connecting"), error_message, QMessageBox::Ok); + QMessageBox::critical(this, tr("Error Connecting!"), error_message, QMessageBox::Ok); return; } @@ -964,7 +964,7 @@ rpcn_friends_dialog::rpcn_friends_dialog(QWidget* parent) { if (!m_rpcn->remove_friend(str_sel_friend)) { - QMessageBox::critical(this, tr("Error removing a friend!"), tr("An error occured trying to remove a friend!"), QMessageBox::Ok); + QMessageBox::critical(this, tr("Error removing a friend!"), tr("An error occurred while trying to remove a friend!"), QMessageBox::Ok); } else { @@ -1000,7 +1000,7 @@ rpcn_friends_dialog::rpcn_friends_dialog(QWidget* parent) { if (!m_rpcn->add_friend(str_sel_friend)) { - QMessageBox::critical(this, tr("Error adding a friend!"), tr("An error occured trying to add a friend!"), QMessageBox::Ok); + QMessageBox::critical(this, tr("Error adding a friend!"), tr("An error occurred while trying to add a friend!"), QMessageBox::Ok); } else { @@ -1031,17 +1031,17 @@ rpcn_friends_dialog::rpcn_friends_dialog(QWidget* parent) break; } - QMessageBox::critical(this, tr("Error validating username"), tr("The username you entered is invalid"), QMessageBox::Ok); + QMessageBox::critical(this, tr("Error validating username!"), tr("The username you entered is invalid!"), QMessageBox::Ok); } if (!m_rpcn->add_friend(str_friend_username)) { - QMessageBox::critical(this, tr("Error adding friend"), tr("An error occured adding friend"), QMessageBox::Ok); + QMessageBox::critical(this, tr("Error adding friend!"), tr("An error occurred while adding a friend!"), QMessageBox::Ok); } else { add_update_list(m_lst_requests, QString::fromStdString(str_friend_username), m_orange_icon, QVariant(false)); - QMessageBox::information(this, tr("Friend added"), tr("Friend was successfully added!"), QMessageBox::Ok); + QMessageBox::information(this, tr("Friend added!"), tr("Friend was successfully added!"), QMessageBox::Ok); } }); diff --git a/rpcs3/rpcs3qt/rsx_debugger.cpp b/rpcs3/rpcs3qt/rsx_debugger.cpp index 5d269d2761..ea06da2508 100644 --- a/rpcs3/rpcs3qt/rsx_debugger.cpp +++ b/rpcs3/rpcs3qt/rsx_debugger.cpp @@ -426,12 +426,12 @@ namespace { case rsx::surface_color_format::b8: { - const u8 value = utils::bless(orig_buffer)[idx]; + const u8 value = read_from_ptr(orig_buffer, idx); return{ value, value, value }; } case rsx::surface_color_format::x32: { - const be_t stored_val = utils::bless>(orig_buffer)[idx]; + const be_t stored_val = read_from_ptr>(orig_buffer, idx); const u32 swapped_val = stored_val; const f32 float_val = std::bit_cast(swapped_val); const u8 val = float_val * 255.f; @@ -441,14 +441,14 @@ namespace case rsx::surface_color_format::x8b8g8r8_o8b8g8r8: case rsx::surface_color_format::x8b8g8r8_z8b8g8r8: { - const auto ptr = utils::bless(orig_buffer); + const auto ptr = reinterpret_cast(orig_buffer.data()); return{ ptr[1 + idx * 4], ptr[2 + idx * 4], ptr[3 + idx * 4] }; } case rsx::surface_color_format::a8r8g8b8: case rsx::surface_color_format::x8r8g8b8_o8r8g8b8: case rsx::surface_color_format::x8r8g8b8_z8r8g8b8: { - const auto ptr = utils::bless(orig_buffer); + const auto ptr = reinterpret_cast(orig_buffer.data()); return{ ptr[3 + idx * 4], ptr[2 + idx * 4], ptr[1 + idx * 4] }; } case rsx::surface_color_format::w16z16y16x16: @@ -581,7 +581,7 @@ void rsx_debugger::OnClickDrawCalls() { for (u32 col = 0; col < width; col++) { - const u8 stencil_val = utils::bless(orig_buffer)[row * width + col]; + const u8 stencil_val = reinterpret_cast(orig_buffer.data())[row * width + col]; buffer[4 * col + 0 + width * row * 4] = stencil_val; buffer[4 * col + 1 + width * row * 4] = stencil_val; buffer[4 * col + 2 + width * row * 4] = stencil_val; @@ -629,18 +629,21 @@ void rsx_debugger::GetMemory() const std::string dump; u32 cmd_i = 0; + std::string str; + for (const auto& command : frame_debug.command_queue) { - const std::string str = rsx::get_pretty_printing_function(command.first)(command.first, command.second); + str.clear(); + rsx::get_pretty_printing_function(command.first)(str, command.first, command.second); m_list_captured_frame->setItem(cmd_i++, 0, new QTableWidgetItem(qstr(str))); dump += str; dump += '\n'; } - if (fs::file file = fs::file(fs::get_cache_dir() + "command_dump.log", fs::rewrite)) + if (!dump.empty()) { - file.write(dump); + fs::write_file(fs::get_cache_dir() + "command_dump.log", fs::rewrite, dump); } for (u32 i = 0; i < frame_debug.draw_calls.size(); i++) diff --git a/rpcs3/rpcs3qt/save_manager_dialog.cpp b/rpcs3/rpcs3qt/save_manager_dialog.cpp index c6dda8f9b6..c07568fe94 100644 --- a/rpcs3/rpcs3qt/save_manager_dialog.cpp +++ b/rpcs3/rpcs3qt/save_manager_dialog.cpp @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include diff --git a/rpcs3/rpcs3qt/settings_dialog.cpp b/rpcs3/rpcs3qt/settings_dialog.cpp index e4edcd4681..6936211946 100644 --- a/rpcs3/rpcs3qt/settings_dialog.cpp +++ b/rpcs3/rpcs3qt/settings_dialog.cpp @@ -1,5 +1,6 @@ #include -#include +#include +#include #include #include #include @@ -10,7 +11,6 @@ #include #include #include -#include #include "gui_settings.h" #include "display_sleep_control.h" @@ -265,30 +265,9 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std SubscribeTooltip(ui->spuLoopDetection, tooltips.settings.spu_loop_detection); // Comboboxes + m_emu_settings->EnhanceComboBox(ui->xfloatAccuracy, emu_settings_type::XFloatAccuracy); SubscribeTooltip(ui->gb_xfloat_accuracy, tooltips.settings.xfloat); - ui->xfloatAccuracy->addItem(tr("Accurate XFloat")); - ui->xfloatAccuracy->addItem(tr("Approximate XFloat")); - ui->xfloatAccuracy->addItem(tr("Relaxed XFloat")); - - connect(ui->xfloatAccuracy, QOverload::of(&QComboBox::currentIndexChanged), this, [this](int index) - { - if (index < 0) return; - - m_emu_settings->SetSetting(emu_settings_type::AccurateXFloat, index == 0 ? "true" : "false"); - m_emu_settings->SetSetting(emu_settings_type::ApproximateXFloat, index == 1 ? "true" : "false"); - }); - - connect(m_emu_settings.get(), &emu_settings::RestoreDefaultsSignal, this, [this]() - { - ui->xfloatAccuracy->setCurrentIndex(1); - }); - - if (m_emu_settings->GetSetting(emu_settings_type::AccurateXFloat) == "true") - ui->xfloatAccuracy->setCurrentIndex(0); - else if (m_emu_settings->GetSetting(emu_settings_type::ApproximateXFloat) == "true") - ui->xfloatAccuracy->setCurrentIndex(1); - else - ui->xfloatAccuracy->setCurrentIndex(2); + remove_item(ui->xfloatAccuracy, static_cast(xfloat_accuracy::inaccurate), static_cast(g_cfg.core.spu_xfloat_accuracy.def)); m_emu_settings->EnhanceComboBox(ui->spuBlockSize, emu_settings_type::SPUBlockSize); SubscribeTooltip(ui->gb_spuBlockSize, tooltips.settings.spu_block_size); @@ -1191,10 +1170,10 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std const std::string selected_camera = m_emu_settings->GetSetting(emu_settings_type::CameraID); ui->cameraIdBox->addItem(tr("None", "Camera Device"), ""); ui->cameraIdBox->addItem(tr("Default", "Camera Device"), qstr(default_camera)); - for (const QCameraInfo& camera_info : QCameraInfo::availableCameras()) + for (const QCameraDevice& camera_info : QMediaDevices::videoInputs()) { if (!camera_info.isNull()) - ui->cameraIdBox->addItem(camera_info.description(), camera_info.deviceName()); + ui->cameraIdBox->addItem(camera_info.description(), camera_info.id()); } if (const int index = ui->cameraIdBox->findData(qstr(selected_camera)); index >= 0) { @@ -1247,6 +1226,13 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std m_emu_settings->EnhanceCheckBox(ui->lockOverlayInputToPlayerOne, emu_settings_type::LockOvlIptToP1); SubscribeTooltip(ui->lockOverlayInputToPlayerOne, tooltips.settings.lock_overlay_input_to_player_one); +#if HAVE_SDL2 + m_emu_settings->EnhanceCheckBox(ui->loadSdlMappings, emu_settings_type::SDLMappings); + SubscribeTooltip(ui->loadSdlMappings, tooltips.settings.sdl_mappings); +#else + ui->loadSdlMappings->setVisible(false); +#endif + // Midi const QString midi_none = m_emu_settings->m_midi_creator.get_none(); const midi_device def_midi_device{ .type = midi_device_type::keyboard, .name = midi_none.toStdString() }; @@ -1473,8 +1459,8 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std m_emu_settings->EnhanceCheckBox(ui->fixupPPUVNAN, emu_settings_type::FixupPPUVNAN); SubscribeTooltip(ui->fixupPPUVNAN, tooltips.settings.fixup_ppuvnan); - m_emu_settings->EnhanceCheckBox(ui->ppuPrecompilation, emu_settings_type::PPULLVMPrecompilation); - SubscribeTooltip(ui->ppuPrecompilation, tooltips.settings.ppu_precompilation); + m_emu_settings->EnhanceCheckBox(ui->llvmPrecompilation, emu_settings_type::LLVMPrecompilation); + SubscribeTooltip(ui->llvmPrecompilation, tooltips.settings.llvm_precompilation); m_emu_settings->EnhanceCheckBox(ui->suspendSavestates, emu_settings_type::SuspendEmulationSavestateMode); SubscribeTooltip(ui->suspendSavestates, tooltips.settings.suspend_savestates); diff --git a/rpcs3/rpcs3qt/settings_dialog.ui b/rpcs3/rpcs3qt/settings_dialog.ui index e96cac9ac6..152cfda615 100644 --- a/rpcs3/rpcs3qt/settings_dialog.ui +++ b/rpcs3/rpcs3qt/settings_dialog.ui @@ -1590,7 +1590,7 @@ - Guitar Hero Live emulated guitar + Guitar Hero Live Emulated Guitar @@ -1614,7 +1614,7 @@ - DJ Hero emulated turntable + DJ Hero Emulated Turntable @@ -1638,7 +1638,7 @@ - Buzz! emulated controller + Buzz! Emulated Controller @@ -1722,7 +1722,7 @@ - Emulated Midi device 1 + Emulated MIDI Device 1 @@ -1741,7 +1741,7 @@ - Emulated Midi device 3 + Emulated MIDI Device 3 @@ -1760,7 +1760,7 @@ - Emulated Midi device 2 + Emulated MIDI Device 2 @@ -1810,6 +1810,13 @@ + + + + Use SDL GameController Database + + + @@ -2040,7 +2047,7 @@ - Disk cache + Disk Cache @@ -2394,9 +2401,9 @@ - + - PPU LLVM Precompilation + PPU/SPU LLVM Precompilation diff --git a/rpcs3/rpcs3qt/skylander_dialog.cpp b/rpcs3/rpcs3qt/skylander_dialog.cpp index c994c549ab..68cc771207 100644 --- a/rpcs3/rpcs3qt/skylander_dialog.cpp +++ b/rpcs3/rpcs3qt/skylander_dialog.cpp @@ -643,17 +643,17 @@ skylander_creator_dialog::skylander_creator_dialog(QWidget* parent) std::array buf{}; const auto data = buf.data(); // Set the block permissions - *utils::bless>(&data[0x36]) = 0x690F0F0F; + write_to_ptr>(data, 0x36, 0x690F0F0F); for (u32 index = 1; index < 0x10; index++) { - *utils::bless>(&data[(index * 0x40) + 0x36]) = 0x69080F7F; + write_to_ptr>(data, (index * 0x40) + 0x36, 0x69080F7F); } // Set the skylander infos - *utils::bless>(&data[0]) = (sky_id | sky_var) + 1; - *utils::bless>(&data[0x10]) = sky_id; - *utils::bless>(&data[0x1C]) = sky_var; + write_to_ptr>(data, (sky_id | sky_var) + 1); + write_to_ptr>(data, 0x10, sky_id); + write_to_ptr>(data, 0x1C, sky_var); // Set checksum - *utils::bless>(&data[0x1E]) = skylander_crc16(0xFFFF, data, 0x1E); + write_to_ptr>(data, 0x1E, skylander_crc16(0xFFFF, data, 0x1E)); sky_file.write(buf.data(), buf.size()); sky_file.close(); diff --git a/rpcs3/rpcs3qt/stylesheets.h b/rpcs3/rpcs3qt/stylesheets.h index 15c32b7473..b3300cb98e 100644 --- a/rpcs3/rpcs3qt/stylesheets.h +++ b/rpcs3/rpcs3qt/stylesheets.h @@ -65,11 +65,11 @@ namespace gui "QDockWidget::close-button, QDockWidget::float-button{ background-color: #e3e3e3; }" // log frame tty - "QTextEdit#tty_frame { background-color: #ffffff; }" + "QPlainTextEdit#tty_frame { background-color: #ffffff; }" "QLabel#tty_text { color: #000000; }" // log frame log - "QTextEdit#log_frame { background-color: #ffffff; }" + "QPlainTextEdit#log_frame { background-color: #ffffff; }" "QLabel#log_level_always { color: #107896; }" "QLabel#log_level_fatal { color: #ff00ff; }" "QLabel#log_level_error { color: #C02F1D; }" diff --git a/rpcs3/rpcs3qt/syntax_highlighter.cpp b/rpcs3/rpcs3qt/syntax_highlighter.cpp index da33f40c60..2f889d3fa4 100644 --- a/rpcs3/rpcs3qt/syntax_highlighter.cpp +++ b/rpcs3/rpcs3qt/syntax_highlighter.cpp @@ -67,7 +67,7 @@ LogHighlighter::LogHighlighter(QTextDocument* parent) : Highlighter(parent) AsmHighlighter::AsmHighlighter(QTextDocument *parent) : Highlighter(parent) { - addRule("^[A-Z0-9]+", Qt::darkBlue); // Instructions + addRule("^\\b[A-Z0-9]+\\b", Qt::darkBlue); // Instructions addRule("-?R\\d[^,;\\s]*", Qt::darkRed); // -R0.* addRule("-?H\\d[^,;\\s]*", Qt::red); // -H1.* addRule("-?v\\[\\d\\]*[^,;\\s]*", Qt::darkCyan); // -v[xyz].* diff --git a/rpcs3/rpcs3qt/tooltips.h b/rpcs3/rpcs3qt/tooltips.h index 638fc6a289..ce20096ec9 100644 --- a/rpcs3/rpcs3qt/tooltips.h +++ b/rpcs3/rpcs3qt/tooltips.h @@ -49,13 +49,13 @@ public: const QString wake_up_delay = tr("Controls how much time it takes for RSX to start processing after waking up by the Cell processor.\nIncreasing wakeup delay improves stability, but very high values can lower RSX/GPU performance.\nIt is recommend to adjust this at 20µs to 40µs increments until the best value for optimal stability is reached."); const QString disabled_from_global = tr("Do not change this setting globally.\nRight-click a game in the game list and choose \"Configure\" instead."); const QString vulkan_async_scheduler = tr("Determines how to schedule GPU async compute jobs when using asynchronous streaming.\nUse 'Safe' mode for more spec compliant behavior at the cost of some CPU overhead. This setting works with all devices.\nUse 'Fast' to use a faster but hacky version. This option is internally disabled for NVIDIA GPUs due to causing GPU hangs."); - const QString disable_msl_fast_math = tr("Disables Fast Math for MSL shaders, which may violate the IEEE 754 standard.\nDisabling it may fix some artefacts especially on Apple GPUs, at the cost of performance."); - const QString suspend_savestates = tr("When this mode is on, emulation exits when saving and the savestate file is concealed after loading it, preventing reuse by RPCS3.\nThis mode is like hibernation of emulation: if you don't want to be able to cheat using savestates when playing the game, consider using this mode.\nDo note that the savestate file is not gone completely just ignored by RPCS3, you can manually relaunch it if needed."); + const QString disable_msl_fast_math = tr("Disables Fast Math for MSL shaders, which may violate the IEEE 754 standard.\nDisabling it may fix some artifacts, especially on Apple GPUs, at the cost of performance."); + const QString suspend_savestates = tr("When this mode is on, emulation exits when saving and the savestate file is concealed after loading it, preventing reuse by RPCS3.\nThis mode is like hibernation of emulation: if you don't want to be able to cheat using savestates when playing the game, consider using this mode.\nDo note that the savestate file is not gone completely, just ignored by RPCS3. You can manually relaunch it if needed."); const QString paused_savestates = tr("When this mode is on, savestates are loaded and paused on the first frame.\nThis allows players to prepare for gameplay without being thrown into the action immediately."); // audio - const QString audio_out = tr("Cubeb uses a cross-platform approach and supports audio buffering, so it is the recommended option.\nXAudio2 uses native Windows sounds system, is the next best alternative."); + const QString audio_out = tr("Cubeb uses a cross-platform approach and supports audio buffering, so it is the recommended option.\nXAudio2 uses native Windows sounds system and is the next best alternative."); const QString audio_out_linux = tr("Cubeb uses a cross-platform approach and supports audio buffering, so it is the recommended option.\nIf it's not available, FAudio could be used instead."); const QString audio_provider = tr("Controls which PS3 audio API is used.\nGames use CellAudio, while VSH requires RSXAudio."); const QString audio_avport = tr("Controls which avport is used to sample audio data from."); @@ -75,7 +75,7 @@ public: const QString ppu__static = tr("Interpreter (slow). Try this if PPU Recompiler (LLVM) doesn't work."); const QString ppu_dynamic = tr("Alternative interpreter (slow). May be faster than static interpreter. Try this if PPU Recompiler (LLVM) doesn't work."); const QString ppu_llvm = tr("Recompiles and caches the game's PPU code using the LLVM Recompiler once before running it for the first time.\nThis is by far the fastest option and should always be used.\nShould you face compatibility issues, fall back to one of the Interpreters and retry.\nIf unsure, use this option."); - const QString ppu_precompilation = tr("Searches the game's directory and precompiles extra PPU modules during boot.\nIf disabled, these modules will only be compiled when needed. Depending on the game, this might interrupt the gameplay unexpectedly and possibly frequently.\nOnly disable this if you want to get ingame more quickly."); + const QString llvm_precompilation = tr("Searches the game's directory and precompiles extra PPU and SPU modules during boot.\nIf disabled, these modules will only be compiled when needed. Depending on the game, this might interrupt the gameplay unexpectedly and possibly frequently.\nOnly disable this if you want to get ingame more quickly."); const QString spu__static = tr("Interpreter (slow). Try this if SPU Recompiler (LLVM) doesn't work."); const QString spu_dynamic = tr("Alternative interpreter (slow). May be faster than static interpreter. Try this if SPU Recompiler (LLVM) doesn't work."); const QString spu_asmjit = tr("Recompiles the game's SPU code using the ASMJIT Recompiler.\nThis is the fast option with very good compatibility.\nIf unsure, use this option."); @@ -86,7 +86,7 @@ public: const QString enable_tsx = tr("Enable usage of TSX instructions.\nNeeds to be forced on some Haswell or Broadwell CPUs or CPUs with the TSX-FA instruction set.\nForcing TSX in these cases may lead to system and performance instability, use it with caution."); const QString spu_block_size = tr("This option controls the SPU analyser, particularly the size of compiled units. The Mega and Giga modes may improve performance by tying smaller units together, decreasing the number of compiled units but increasing their size.\nUse the Safe mode for maximum compatibility."); const QString preferred_spu_threads = tr("Some SPU stages are sensitive to race conditions and allowing a limited number at a time helps alleviate performance stalls.\nSetting this to a smaller value might improve performance and reduce stuttering in some games.\nLeave this on auto if performance is negatively affected when setting a small value."); - const QString max_cpu_preempt = tr("Reduces CPU usage and power consumption, on mobile devices improves battery life. (0 means disabled)\nHigher values cause a more pronounced effect, but may cause audio or performance issues. A value of 50 or less is recommended.\nThis option forces an FPS limit because it's active when framerate is stable.\nThe lighter the game is on the hardware, the more power is saved by it. (until the preemption count barrier is reached)"); + const QString max_cpu_preempt = tr("Reduces CPU usage and power consumption, improving battery life on mobile devices. (0 means disabled)\nHigher values cause a more pronounced effect, but may cause audio or performance issues. A value of 50 or less is recommended.\nThis option forces an FPS limit because it's active when framerate is stable.\nThe lighter the game is on the hardware, the more power is saved by it. (until the preemption count barrier is reached)"); // debug @@ -102,7 +102,7 @@ public: const QString accurate_cache_line_stores = tr("Accurately processes PPU DCBZ instruction.\nIn addition, when combined with Accurate SPU DMA, SPU PUT cache line accesses will be processed atomically."); const QString mfc_delay_command = tr("Forces delaying any odd MFC command, waits for at least 2 pending commands to execute them in a random order.\nMust be used with either SPU interpreters currently.\nSeverely degrades performance! If unsure, don't use this option."); const QString hook_static_functions = tr("Allows to hook some functions like 'memcpy' replacing them with high-level implementations. May do nothing or break things. Experimental."); - const QString renderdoc_compatibility = tr("Enables use of classic OpenGL buffers which allows capturing tools to work with RPCS3 e.g RenderDoc.\nAlso allows vulkan to use debug markers for nicer Renderdoc captures.\nIf unsure, don't use this option."); + const QString renderdoc_compatibility = tr("Enables use of classic OpenGL buffers which allows capturing tools to work with RPCS3 e.g RenderDoc.\nAlso allows Vulkan to use debug markers for nicer Renderdoc captures.\nIf unsure, don't use this option."); const QString force_high_pz = tr("Only useful when debugging differences in GPU hardware.\nNot necessary for average users.\nIf unsure, don't use this option."); const QString debug_output = tr("Enables the selected API's inbuilt debugging functionality.\nWill cause severe performance degradation especially with Vulkan.\nOnly useful to developers.\nIf unsure, don't use this option."); const QString debug_overlay = tr("Provides a graphical overlay of various debugging information.\nIf unsure, don't use this option."); @@ -132,7 +132,7 @@ public: const QString disable_kb_hotkeys = tr("Disables keyboard hotkeys such as Ctrl+S, Ctrl+E, Ctrl+R, Ctrl+P while the game screen is active.\nThis does not include Ctrl+L (hide and lock mouse) and Alt+Enter (toggle fullscreen).\nCheck this if you want to play with mouse and keyboard."); const QString max_llvm_threads = tr("Limits the maximum number of threads used for the initial PPU and SPU module compilation.\nLower this in order to increase performance of other open applications.\nThe default uses all available threads."); const QString show_mouse_in_fullscreen = tr("Shows the mouse cursor when the fullscreen mode is active.\nCurrently this may not work every time."); - const QString lock_mouse_in_fullscreen = tr("Locks the mouse cursor at center when the fullscreen mode is active."); + const QString lock_mouse_in_fullscreen = tr("Locks the mouse cursor to the center when the fullscreen mode is active."); const QString hide_mouse_on_idle = tr("Hides the mouse cursor if no mouse movement is detected for the configured time."); const QString show_shader_compilation_hint = tr("Shows 'Compiling shaders' hint using the native overlay."); const QString show_ppu_compilation_hint = tr("Shows 'Compiling PPU modules' hint using the native overlay."); @@ -164,13 +164,13 @@ public: const QString resolution = tr("This setting will be ignored if the Resolution Scale is set to anything other than 100%!\nLeave this on 1280x720. Every PS3 game is compatible with this resolution.\nOnly use 1920x1080 if the game supports it.\nRarely due to emulation bugs some games will only render at low resolutions like 480p."); const QString graphics_adapter = tr("On multi GPU systems select which GPU to use in RPCS3 when using Vulkan.\nThis is not needed when using OpenGL."); const QString aspect_ratio = tr("Leave this on 16:9 unless you have a 4:3 monitor."); - const QString frame_limit = tr("Off is the fastest option.\nUsing the frame limiter will add extra overhead and slow down the game. However, some games will crash if the frame rate is too high.\nPS3 native should only be used if Auto is not working correctly as it can introduce frame-pacing issues.\nInfinite adds a positive feedback loop which adds another vblank signal per frame allowing more games to be fps limitless."); + const QString frame_limit = tr("Off is the fastest option.\nUsing the frame limiter will add extra overhead and slow down the game. However, some games will crash if the framerate is too high.\nPS3 native should only be used if Auto is not working correctly as it can introduce frame-pacing issues.\nInfinite adds a positive feedback loop which adds another vblank signal per frame allowing more games to be fps limitless."); const QString anti_aliasing = tr("Emulate PS3 multisampling layout.\nCan fix some otherwise difficult to solve graphics glitches.\nLow to moderate performance hit depending on your GPU hardware."); const QString anisotropic_filter = tr("Higher values increase sharpness of textures on sloped surfaces at the cost of GPU resources.\nModern GPUs can handle this setting just fine, even at 16x.\nKeep this on Automatic if you want to use the original setting used by a real PS3."); const QString resolution_scale = tr("Scales the game's resolution by the given percentage.\nThe base resolution is always 1280x720.\nSet this value to 100% if you want to use the normal Resolution options.\nValues below 100% will usually not improve performance."); const QString minimum_scalable_dimension = tr("Only framebuffers greater than this size will be upscaled.\nIncreasing this value might fix problems with missing graphics when upscaling, especially when Write Color Buffers is enabled.\nIf unsure, don't change this option."); const QString dump_color = tr("Enable this option if you get missing graphics or broken lighting ingame.\nMight degrade performance and introduce stuttering in some cases.\nRequired for Demon's Souls."); - const QString vsync = tr("By having this off you might obtain a higher frame rate at the cost of tearing artifacts in the game."); + const QString vsync = tr("By having this off you might obtain a higher framerate at the cost of tearing artifacts in the game."); const QString strict_rendering_mode = tr("Enforces strict compliance to the API specification.\nMight result in degraded performance in some games.\nCan resolve rare cases of missing graphics and flickering.\nIf unsure, don't use this option."); const QString stretch_to_display_area = tr("Overrides the aspect ratio and stretches the image to the full display area."); const QString multithreaded_rsx = tr("Offloads some RSX operations to a secondary thread.\nImproves performance for high-core processors.\nMay cause slowdown in weaker CPUs due to the extra worker thread load."); @@ -218,7 +218,7 @@ public: const QString music_handler = tr("Currently only used for cellMusic emulation.\nSelect Qt to use the default output device of your operating system.\nThis may not be able to play all audio formats."); const QString camera = tr("Select Qt Camera to use the default camera device of your operating system."); const QString camera_type = tr("Depending on the game, you may need to select a specific camera type."); - const QString camera_flip = tr("Flips the camera image either horizontally, vertically, or on both axis."); + const QString camera_flip = tr("Flips the camera image either horizontally, vertically, or on both axes."); const QString camera_id = tr("Select the camera that you want to use during gameplay."); const QString move = tr("PlayStation Move support.\nFake: Experimental! This maps Move controls to DS3 controller mappings.\nMouse: Emulate PSMove with Mouse handler."); const QString buzz = tr("Buzz! support.\nSelect 1 or 2 controllers if the game requires Buzz! controllers and you don't have real controllers.\nSelect Null if the game has support for DualShock or if you have real Buzz! controllers."); @@ -226,7 +226,8 @@ public: const QString ghltar = tr("Guitar Hero Live (GHL) Guitar controller support.\nSelect 1 or 2 controllers if the game requires GHL Guitar controllers and you don't have real guitar controllers.\nSelect Null if the game has support for DualShock or if you have real guitar controllers.\nA real guitar controller can be used at the same time as an emulated guitar controller."); const QString background_input = tr("Allows pad and keyboard input while the game window is unfocused."); const QString show_move_cursor = tr("Shows the raw position of the PS Move input.\nThis can be very helpful during calibration screens."); - const QString midi_devices = tr("Select up to 3 emulated midi devices and their type."); + const QString midi_devices = tr("Select up to 3 emulated MIDI devices and their types."); + const QString sdl_mappings = tr("Loads the SDL GameController database for improved gamepad compatibility. Only used in the SDL pad handler."); const QString lock_overlay_input_to_player_one = tr("Locks the native overlay input to the first player."); @@ -247,7 +248,7 @@ public: const QString enter_button_assignment = tr("The button used for enter/accept/confirm in system dialogs.\nChange this to use the Circle button instead, which is the default configuration on Japanese systems and in many Japanese games.\nIn these cases having the cross button assigned can often lead to confusion."); const QString enable_host_root = tr("Required for some Homebrew.\nIf unsure, don't use this option."); const QString limit_cache_size = tr("Automatically removes older files from disk cache on boot if it grows larger than the specified value.\nGames can use the cache folder to temporarily store data outside of system memory. It is not used for long-term storage.\n\nThis setting is only available in the global configuration."); - const QString console_time_offset = tr("Sets the time to be used within the console. This will be applied as an offset that tracks wall clock time.\nCan be reset to current wallclock time by clicking \"Set to Now\"."); + const QString console_time_offset = tr("Sets the time to be used within the console. This will be applied as an offset that tracks wall clock time.\nCan be reset to current wall clock time by clicking \"Set to Now\"."); } settings; const struct gamepad_settings @@ -265,11 +266,12 @@ public: const QString dualsense_linux = tr("The DualSense handler is recommended for official DualSense controllers."); const QString dualsense_other = tr("The DualSense handler is recommended for official DualSense controllers."); const QString xinput = tr("The XInput handler will work with Xbox controllers and many third-party PC-compatible controllers. Pressure sensitive buttons from SCP are supported when SCP's XInput1_3.dll is placed in the main RPCS3 directory. For more details, see the RPCS3 Wiki.").arg(gui::utils::get_link_style()); - const QString evdev = tr("The evdev handler should work with any controller that has linux support.
If your joystick is not being centered properly, read the RPCS3 Wiki for instructions.").arg(gui::utils::get_link_style()); + const QString evdev = tr("The evdev handler should work with any controller that has Linux support.
If your joystick is not being centered properly, read the RPCS3 Wiki for instructions.").arg(gui::utils::get_link_style()); const QString mmjoy = tr("The MMJoystick handler should work with almost any controller recognized by Windows. However, it is recommended that you use the more specific handlers if you have a controller that supports them."); const QString sdl = tr("The SDL handler supports a variety of controllers across different platforms."); const QString pressure_intensity = tr("Controls the intensity of pressure sensitive buttons while this special button is pressed.
Enable \"Toggle\" if you want to toggle the intensity on button press instead.
Use the percentage to change how hard you want to press a button."); + const QString pressure_deadzone = tr("Controls the deadzone of pressure sensitive buttons. It determines how far the button has to be pressed until it is recognized by the game. The resulting range will be projected onto the full button sensitivity range."); const QString squircle_factor = tr("The actual DualShock 3's stick range is not circular but formed like a rounded square (or squircle) which represents the maximum range of the emulated sticks. You can use the squircle values to modify the stick input if your sticks can't reach the corners of that range. A value of 0 does not apply any so called squircling. A value of 8000 is usually recommended."); const QString stick_multiplier = tr("The stick multipliers can be used to change the sensitivity of your stick movements.
The default setting is 1 and represents normal input."); const QString stick_deadzones = tr("A stick's deadzone determines how far the stick has to be moved until it is fully recognized by the game. The resulting range will be projected onto the full input range in order to give you a smooth experience. Movement inside the deadzone is actually simulated as a real DualShock 3's deadzone of ~13%, so don't worry if there is still movement shown in the emulated stick preview."); @@ -281,7 +283,7 @@ public: const QString mouse_deadzones = tr("The mouse deadzones represent the games' own deadzones on the x and y axes. Games usually enforce their own deadzones to filter out small unwanted stick movements. In consequence, mouse input feels unintuitive since it relies on immediate responsiveness. You can change these values temporarily during gameplay in order to find out the optimal values for your game (Alt+T and Alt+Y for x, Alt+U and Alt+I for y)."); const QString mouse_acceleration = tr("The mouse acceleration can be used to amplify your mouse movements on the x and y axes. Increase these values if your mouse movements feel too slow while playing a game. You can change these values temporarily during gameplay in order to find out the optimal values (Alt+G and Alt+H for x, Alt+J and Alt+K for y). Keep in mind that modern mice usually provide different modes and settings that can be used to change mouse movement speeds as well."); const QString mouse_movement = tr("The mouse movement mode determines how the mouse movement is translated to pad input.
Use the relative mode for traditional mouse movement.
Use the absolute mode to use the mouse's distance to the center of the screen as input value."); - const QString button_assignment = tr("Left-click: remap this button.
Shift + Left-click: add an addition button mapping.
Right-click: clear this button mapping."); + const QString button_assignment = tr("Left-click: remap this button.
Shift + Left-click: add an additional button mapping.
Right-click: clear this button mapping."); } gamepad_settings; }; diff --git a/rpcs3/rpcs3qt/trophy_manager_dialog.cpp b/rpcs3/rpcs3qt/trophy_manager_dialog.cpp index 3dd700274c..8e0fcf1ec0 100644 --- a/rpcs3/rpcs3qt/trophy_manager_dialog.cpp +++ b/rpcs3/rpcs3qt/trophy_manager_dialog.cpp @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include diff --git a/rpcs3/rpcs3qt/user_manager_dialog.cpp b/rpcs3/rpcs3qt/user_manager_dialog.cpp index c1a2f1d941..8314e6182c 100644 --- a/rpcs3/rpcs3qt/user_manager_dialog.cpp +++ b/rpcs3/rpcs3qt/user_manager_dialog.cpp @@ -10,7 +10,6 @@ #include #include #include -#include #include "user_manager_dialog.h" #include "table_item_delegate.h" diff --git a/rpcs3/util/asm.hpp b/rpcs3/util/asm.hpp index 6afc032390..da0be9f1f6 100644 --- a/rpcs3/util/asm.hpp +++ b/rpcs3/util/asm.hpp @@ -410,23 +410,6 @@ namespace utils return factor1 > 0 && T{umax} / factor1 < factor2 ? T{umax} : static_cast(factor1 * factor2); } - // Hack. Pointer cast util to workaround UB. Use with extreme care. - template - [[nodiscard]] T* bless(U* ptr) - { -#ifdef _MSC_VER - return (T*)ptr; -#elif defined(ARCH_X64) - T* result; - __asm__("movq %1, %0;" : "=r" (result) : "r" (ptr) : "memory"); - return result; -#elif defined(ARCH_ARM64) - T* result; - __asm__("mov %0, %1" : "=r" (result) : "r" (ptr) : "memory"); - return result; -#endif - } - inline void trap() { #ifdef _M_X64 diff --git a/rpcs3/util/atomic.cpp b/rpcs3/util/atomic.cpp index a1612b5626..34b726c3b1 100644 --- a/rpcs3/util/atomic.cpp +++ b/rpcs3/util/atomic.cpp @@ -28,6 +28,21 @@ namespace utils #include "Utilities/sync.h" #include "Utilities/StrFmt.h" +#ifdef __linux__ +static bool has_waitv() +{ + static const bool s_has_waitv = [] + { + syscall(SYS_futex_waitv, 0, 0, 0, 0, 0); + if (errno == ENOSYS) + return false; + return true; + }(); + + return s_has_waitv; +} +#endif + #include #include #include @@ -40,8 +55,8 @@ namespace utils // Total number of entries. static constexpr usz s_hashtable_size = 1u << 17; -// Reference counter combined with shifted pointer (which is assumed to be 47 bit) -static constexpr uptr s_ref_mask = (1u << 17) - 1; +// Reference counter combined with shifted pointer (which is assumed to be 48 bit) +static constexpr uptr s_ref_mask = 0xffff; // Fix for silly on-first-use initializer static bool s_null_wait_cb(const void*, u64, u64){ return true; }; @@ -55,163 +70,17 @@ static thread_local bool(*s_tls_one_time_wait_cb)(u64 attempts) = nullptr; // Callback for notification functions for optimizations static thread_local void(*s_tls_notify_cb)(const void* data, u64 progress) = nullptr; -static inline bool operator &(atomic_wait::op lhs, atomic_wait::op_flag rhs) -{ - return !!(static_cast(lhs) & static_cast(rhs)); -} - // Compare data in memory with old value, and return true if they are equal -static NEVER_INLINE bool ptr_cmp(const void* data, u32 _size, u128 old128, u128 mask128, atomic_wait::info* ext = nullptr) +static NEVER_INLINE bool ptr_cmp(const void* data, u32 old, atomic_wait::info* ext = nullptr) { - using atomic_wait::op; - using atomic_wait::op_flag; - - const u8 size = static_cast(_size); - const op flag{static_cast(_size >> 8)}; - - bool result = false; - - if (size <= 8) - { - u64 new_value = 0; - u64 old_value = static_cast(old128); - u64 mask = static_cast(mask128) & (u64{umax} >> ((64 - size * 8) & 63)); - - // Don't load memory on empty mask - switch (mask ? size : 0) - { - case 0: break; - case 1: new_value = reinterpret_cast*>(data)->load(); break; - case 2: new_value = reinterpret_cast*>(data)->load(); break; - case 4: new_value = reinterpret_cast*>(data)->load(); break; - case 8: new_value = reinterpret_cast*>(data)->load(); break; - default: - { - fmt::throw_exception("Bad size (arg=0x%x)", _size); - } - } - - if (flag & op_flag::bit_not) - { - new_value = ~new_value; - } - - if (!mask) [[unlikely]] - { - new_value = 0; - old_value = 0; - } - else - { - if (flag & op_flag::byteswap) - { - switch (size) - { - case 2: - { - new_value = stx::se_storage::swap(static_cast(new_value)); - old_value = stx::se_storage::swap(static_cast(old_value)); - mask = stx::se_storage::swap(static_cast(mask)); - break; - } - case 4: - { - new_value = stx::se_storage::swap(static_cast(new_value)); - old_value = stx::se_storage::swap(static_cast(old_value)); - mask = stx::se_storage::swap(static_cast(mask)); - break; - } - case 8: - { - new_value = stx::se_storage::swap(new_value); - old_value = stx::se_storage::swap(old_value); - mask = stx::se_storage::swap(mask); - break; - } - default: - { - break; - } - } - } - - // Make most significant bit sign bit - const auto shv = std::countl_zero(mask); - new_value &= mask; - old_value &= mask; - new_value <<= shv; - old_value <<= shv; - } - - s64 news = new_value; - s64 olds = old_value; - - u64 newa = news < 0 ? (0ull - new_value) : new_value; - u64 olda = olds < 0 ? (0ull - old_value) : old_value; - - switch (op{static_cast(static_cast(flag) & 0xf)}) - { - case op::eq: result = old_value == new_value; break; - case op::slt: result = olds < news; break; - case op::sgt: result = olds > news; break; - case op::ult: result = old_value < new_value; break; - case op::ugt: result = old_value > new_value; break; - case op::alt: result = olda < newa; break; - case op::agt: result = olda > newa; break; - case op::pop: - { - // Count is taken from least significant byte and ignores some flags - const u64 count = static_cast(old128) & 0xff; - - result = count < utils::popcnt64(new_value); - break; - } - default: - { - fmt::throw_exception("ptr_cmp(): unrecognized atomic wait operation."); - } - } - } - else if (size == 16 && (flag == op::eq || flag == (op::eq | op_flag::inverse))) - { - u128 new_value = 0; - u128 old_value = old128; - u128 mask = mask128; - - // Don't load memory on empty mask - if (mask) [[likely]] - { - new_value = atomic_storage::load(*reinterpret_cast(data)); - } - - // TODO - result = !((old_value ^ new_value) & mask); - } - else if (size > 16 && !~mask128 && (flag == op::eq || flag == (op::eq | op_flag::inverse))) - { - // Interpret old128 as a pointer to the old value - ensure(!(old128 >> (64 + 17))); - - result = std::memcmp(data, reinterpret_cast(static_cast(old128)), size) == 0; - } - else - { - fmt::throw_exception("ptr_cmp(): no alternative operations are supported for non-standard atomic wait yet."); - } - - if (flag & op_flag::inverse) - { - result = !result; - } - // Check other wait variables if provided - if (result) + if (reinterpret_cast*>(data)->load() == old) { if (ext) [[unlikely]] { for (auto e = ext; e->data; e++) { - if (!ptr_cmp(e->data, e->size, e->old, e->mask)) + if (!ptr_cmp(e->data, e->old)) { return false; } @@ -283,18 +152,15 @@ namespace #endif // Essentially a fat semaphore - struct cond_handle + struct alignas(64) cond_handle { - // Combined pointer (most significant 47 bits) and ref counter (17 least significant bits) + // Combined pointer (most significant 48 bits) and ref counter (16 least significant bits) atomic_t ptr_ref; u64 tid; - u128 mask; - u128 oldv; + u32 oldv; u64 tsc0; u16 link; - u8 size; - u8 flag; atomic_t sync; #ifdef USE_STD @@ -316,7 +182,7 @@ namespace mtx.init(mtx); #endif - ensure(!ptr_ref.exchange((iptr << 17) | 1)); + ensure(!ptr_ref.exchange((iptr << 16) | 1)); } void destroy() @@ -324,10 +190,7 @@ namespace tid = 0; tsc0 = 0; link = 0; - size = 0; - flag = 0; sync.release(0); - mask = 0; oldv = 0; #ifdef USE_STD @@ -517,7 +380,7 @@ namespace // TLS storage for few allocaded "semaphores" to allow skipping initialization static thread_local tls_cond_handler s_tls_conds{}; -static u32 cond_alloc(uptr iptr, u128 mask, u32 tls_slot = -1) +static u32 cond_alloc(uptr iptr, u32 tls_slot = -1) { // Try to get cond from tls slot instead u16* ptls = tls_slot >= std::size(s_tls_conds.cond) ? nullptr : s_tls_conds.cond + tls_slot; @@ -526,8 +389,7 @@ static u32 cond_alloc(uptr iptr, u128 mask, u32 tls_slot = -1) { // Fast reinitialize const u32 id = std::exchange(*ptls, 0); - s_cond_list[id].mask = mask; - s_cond_list[id].ptr_ref.release((iptr << 17) | 1); + s_cond_list[id].ptr_ref.release((iptr << 16) | 1); return id; } @@ -581,7 +443,6 @@ static u32 cond_alloc(uptr iptr, u128 mask, u32 tls_slot = -1) const u32 id = level3 * 64 + std::countr_one(bits); // Initialize new "semaphore" - s_cond_list[id].mask = mask; s_cond_list[id].init(iptr); return id; } @@ -625,8 +486,6 @@ static void cond_free(u32 cond_id, u32 tls_slot = -1) { // Fast finalization cond->sync.release(0); - cond->size = 0; - cond->mask = 0; *ptls = static_cast(cond_id); return; } @@ -652,7 +511,7 @@ static void cond_free(u32 cond_id, u32 tls_slot = -1) s_cond_sem1.atomic_op(FN(x -= u128{1} << (level1 * 14))); } -static cond_handle* cond_id_lock(u32 cond_id, u128 mask, uptr iptr = 0) +static cond_handle* cond_id_lock(u32 cond_id, uptr iptr = 0) { bool did_ref = false; @@ -673,7 +532,7 @@ static cond_handle* cond_id_lock(u32 cond_id, u128 mask, uptr iptr = 0) return false; } - if (iptr && (val >> 17) != iptr) + if (iptr && (val >> 16) != iptr) { // Pointer mismatch return false; @@ -686,11 +545,6 @@ static cond_handle* cond_id_lock(u32 cond_id, u128 mask, uptr iptr = 0) return false; } - if (!(mask & cond->mask) && cond->size) - { - return false; - } - if (!did_ref) { val++; @@ -702,7 +556,7 @@ static cond_handle* cond_id_lock(u32 cond_id, u128 mask, uptr iptr = 0) if (ok) { // Check other fields again - if (const u32 sync_val = cond->sync; sync_val == 0 || sync_val == 3 || (cond->size && !(mask & cond->mask))) + if (const u32 sync_val = cond->sync; sync_val == 0 || sync_val == 3) { did_ref = true; continue; @@ -713,7 +567,7 @@ static cond_handle* cond_id_lock(u32 cond_id, u128 mask, uptr iptr = 0) if ((old & s_ref_mask) == s_ref_mask) { - fmt::throw_exception("Reference count limit (131071) reached in an atomic notifier."); + fmt::throw_exception("Reference count limit (%u) reached in an atomic notifier.", s_ref_mask); } break; @@ -736,8 +590,8 @@ namespace u64 bits: 24; // Allocated bits u64 prio: 24; // Reserved - u64 ref : 17; // Ref counter - u64 iptr: 47; // First pointer to use slot (to count used slots) + u64 ref : 16; // Ref counter + u64 iptr: 48; // First pointer to use slot (to count used slots) }; // Need to spare 16 bits for ref counter @@ -760,7 +614,7 @@ namespace static void slot_free(uptr ptr, atomic_t* slot, u32 tls_slot) noexcept; template - static auto slot_search(uptr iptr, u128 mask, F func) noexcept; + static auto slot_search(uptr iptr, F func) noexcept; }; static_assert(sizeof(root_info) == 64); @@ -944,7 +798,7 @@ void root_info::slot_free(uptr iptr, atomic_t* slot, u32 tls_slot) noexcept } template -FORCE_INLINE auto root_info::slot_search(uptr iptr, u128 mask, F func) noexcept +FORCE_INLINE auto root_info::slot_search(uptr iptr, F func) noexcept { u32 index = 0; [[maybe_unused]] u32 total = 0; @@ -974,7 +828,7 @@ FORCE_INLINE auto root_info::slot_search(uptr iptr, u128 mask, F func) noexcept for (u32 i = 0; i < cond_count; i++) { - if (cond_id_lock(cond_ids[i], mask, iptr)) + if (cond_id_lock(cond_ids[i], iptr)) { if (func(cond_ids[i])) { @@ -994,18 +848,84 @@ FORCE_INLINE auto root_info::slot_search(uptr iptr, u128 mask, F func) noexcept } } -SAFE_BUFFERS(void) atomic_wait_engine::wait(const void* data, u32 size, u128 old_value, u64 timeout, u128 mask, atomic_wait::info* ext) +SAFE_BUFFERS(void) +atomic_wait_engine::wait(const void* data, u32 old_value, u64 timeout, atomic_wait::info* ext) { - const auto stamp0 = utils::get_unique_tsc(); + uint ext_size = 0; - if (!s_tls_wait_cb(data, 0, stamp0)) +#ifdef __linux__ + ::timespec ts{}; + if (timeout + 1) + { + if (ext && ext->data) [[unlikely]] + { + // futex_waitv uses absolute timeout + ::clock_gettime(CLOCK_MONOTONIC, &ts); + } + + ts.tv_sec += timeout / 1'000'000'000; + ts.tv_nsec += timeout % 1'000'000'000; + if (ts.tv_nsec >= 1'000'000'000) + { + ts.tv_sec++; + ts.tv_nsec -= 1'000'000'000; + } + } + + futex_waitv vec[atomic_wait::max_list]{}; + vec[0].flags = FUTEX_32 | FUTEX_PRIVATE_FLAG; + vec[0].uaddr = reinterpret_cast<__u64>(data); + vec[0].val = old_value; + + if (ext) [[unlikely]] + { + for (auto e = ext; e->data; e++) + { + ext_size++; + vec[ext_size].flags = FUTEX_32 | FUTEX_PRIVATE_FLAG; + vec[ext_size].uaddr = reinterpret_cast<__u64>(e->data); + vec[ext_size].val = e->old; + } + } + + if (ext_size && has_waitv()) [[unlikely]] + { + if (syscall(SYS_futex_waitv, +vec, ext_size + 1, 0, timeout + 1 ? &ts : nullptr, CLOCK_MONOTONIC) == -1) + { + if (errno == ENOSYS) + { + fmt::throw_exception("futex_waitv is not supported (Linux kernel is too old)"); + } + if (errno == EINVAL) + { + fmt::throw_exception("futex_waitv: bad param"); + } + } + return; + } + else if (has_waitv()) + { + if (futex(const_cast(data), FUTEX_WAIT_PRIVATE, old_value, timeout + 1 ? &ts : nullptr) == -1) + { + if (errno == EINVAL) + { + fmt::throw_exception("futex: bad param"); + } + } + return; + } + + ext_size = 0; +#endif + + if (!s_tls_wait_cb(data, 0, 0)) { return; } - const uptr iptr = reinterpret_cast(data) & (~s_ref_mask >> 17); + const auto stamp0 = utils::get_unique_tsc(); - uint ext_size = 0; + const uptr iptr = reinterpret_cast(data) & (~s_ref_mask >> 16); uptr iptr_ext[atomic_wait::max_list - 1]{}; @@ -1026,18 +946,18 @@ SAFE_BUFFERS(void) atomic_wait_engine::wait(const void* data, u32 size, u128 old } } - iptr_ext[ext_size] = reinterpret_cast(e->data) & (~s_ref_mask >> 17); + iptr_ext[ext_size] = reinterpret_cast(e->data) & (~s_ref_mask >> 16); ext_size++; } } - const u32 cond_id = cond_alloc(iptr, mask, 0); + const u32 cond_id = cond_alloc(iptr, 0); u32 cond_id_ext[atomic_wait::max_list - 1]{}; for (u32 i = 0; i < ext_size; i++) { - cond_id_ext[i] = cond_alloc(iptr_ext[i], ext[i].mask, i + 1); + cond_id_ext[i] = cond_alloc(iptr_ext[i], i + 1); } const auto slot = root_info::slot_alloc(iptr); @@ -1060,8 +980,6 @@ SAFE_BUFFERS(void) atomic_wait_engine::wait(const void* data, u32 size, u128 old // Store some info for notifiers (some may be unused) cond->link = 0; - cond->size = static_cast(size); - cond->flag = static_cast(size >> 8); cond->oldv = old_value; cond->tsc0 = stamp0; @@ -1071,8 +989,6 @@ SAFE_BUFFERS(void) atomic_wait_engine::wait(const void* data, u32 size, u128 old { // Extensions point to original cond_id, copy remaining info cond_ext[i]->link = cond_id; - cond_ext[i]->size = static_cast(ext[i].size); - cond_ext[i]->flag = static_cast(ext[i].size >> 8); cond_ext[i]->oldv = ext[i].old; cond_ext[i]->tsc0 = stamp0; @@ -1105,7 +1021,7 @@ SAFE_BUFFERS(void) atomic_wait_engine::wait(const void* data, u32 size, u128 old u64 attempts = 0; - while (ptr_cmp(data, size, old_value, mask, ext)) + while (ptr_cmp(data, old_value, ext)) { if (s_tls_one_time_wait_cb) { @@ -1263,7 +1179,7 @@ SAFE_BUFFERS(void) atomic_wait_engine::wait(const void* data, u32 size, u128 old } template -static u32 alert_sema(u32 cond_id, u32 size, u128 mask) +static u32 alert_sema(u32 cond_id, u32 size) { ensure(cond_id); @@ -1271,11 +1187,11 @@ static u32 alert_sema(u32 cond_id, u32 size, u128 mask) u32 ok = 0; - if (!cond->size || mask & cond->mask) + if (true) { // Redirect if necessary const auto _old = cond; - const auto _new = _old->link ? cond_id_lock(_old->link, u128(-1)) : _old; + const auto _new = _old->link ? cond_id_lock(_old->link) : _old; if (_new && _new->tsc0 == _old->tsc0) { @@ -1336,16 +1252,25 @@ void atomic_wait_engine::set_notify_callback(void(*cb)(const void*, u64)) s_tls_notify_cb = cb; } -void atomic_wait_engine::notify_one(const void* data, u32 size, u128 mask) +void atomic_wait_engine::notify_one(const void* data) { - const uptr iptr = reinterpret_cast(data) & (~s_ref_mask >> 17); - if (s_tls_notify_cb) s_tls_notify_cb(data, 0); - root_info::slot_search(iptr, mask, [&](u32 cond_id) +#ifdef __linux__ + if (has_waitv()) { - if (alert_sema(cond_id, size, mask)) + futex(const_cast(data), FUTEX_WAKE_PRIVATE, 1); + if (s_tls_notify_cb) + s_tls_notify_cb(data, -1); + return; + } +#endif + const uptr iptr = reinterpret_cast(data) & (~s_ref_mask >> 16); + + root_info::slot_search(iptr, [&](u32 cond_id) + { + if (alert_sema(cond_id, 4)) { return true; } @@ -1357,29 +1282,39 @@ void atomic_wait_engine::notify_one(const void* data, u32 size, u128 mask) s_tls_notify_cb(data, -1); } -SAFE_BUFFERS(void) atomic_wait_engine::notify_all(const void* data, u32 size, u128 mask) +SAFE_BUFFERS(void) +atomic_wait_engine::notify_all(const void* data) { - const uptr iptr = reinterpret_cast(data) & (~s_ref_mask >> 17); - if (s_tls_notify_cb) s_tls_notify_cb(data, 0); +#ifdef __linux__ + if (has_waitv()) + { + futex(const_cast(data), FUTEX_WAKE_PRIVATE, INT_MAX); + if (s_tls_notify_cb) + s_tls_notify_cb(data, -1); + return; + } +#endif + const uptr iptr = reinterpret_cast(data) & (~s_ref_mask >> 16); + // Array count for batch notification u32 count = 0; // Array itself. u32 cond_ids[128]; - root_info::slot_search(iptr, mask, [&](u32 cond_id) + root_info::slot_search(iptr, [&](u32 cond_id) { if (count >= 128) { // Unusual big amount of sema: fallback to notify_one alg - alert_sema(cond_id, size, mask); + alert_sema(cond_id, 4); return false; } - u32 res = alert_sema(cond_id, size, mask); + u32 res = alert_sema(cond_id, 4); if (~res <= u16{umax}) { @@ -1395,7 +1330,7 @@ SAFE_BUFFERS(void) atomic_wait_engine::notify_all(const void* data, u32 size, u1 { const u32 cond_id = *(std::end(cond_ids) - i - 1); - if (!s_cond_list[cond_id].wakeup(size ? 1 : 2)) + if (!s_cond_list[cond_id].wakeup(1)) { *(std::end(cond_ids) - i - 1) = ~cond_id; } diff --git a/rpcs3/util/atomic.hpp b/rpcs3/util/atomic.hpp index c3132584cd..d546a9c434 100644 --- a/rpcs3/util/atomic.hpp +++ b/rpcs3/util/atomic.hpp @@ -129,54 +129,21 @@ enum class atomic_wait_timeout : u64 inf = 0xffffffffffffffff, }; +template +class lf_queue; + +namespace stx +{ + template + class atomic_ptr; +} + // Various extensions for atomic_t::wait namespace atomic_wait { // Max number of simultaneous atomic variables to wait on (can be extended if really necessary) constexpr uint max_list = 8; - enum class op : u8 - { - eq, // Wait while value is bitwise equal to - slt, // Wait while signed value is less than - sgt, // Wait while signed value is greater than - ult, // Wait while unsigned value is less than - ugt, // Wait while unsigned value is greater than - alt, // Wait while absolute value is less than - agt, // Wait while absolute value is greater than - pop, // Wait while set bit count of the value is less than - __max - }; - - static_assert(static_cast(op::__max) == 8); - - enum class op_flag : u8 - { - inverse = 1 << 4, // Perform inverse operation (negate the result) - bit_not = 1 << 5, // Perform bitwise NOT on loaded value before operation - byteswap = 1 << 6, // Perform byteswap on both arguments and masks when applicable - }; - - constexpr op_flag op_be = std::endian::native == std::endian::little ? op_flag::byteswap : op_flag{0}; - constexpr op_flag op_le = std::endian::native == std::endian::little ? op_flag{0} : op_flag::byteswap; - - constexpr op operator |(op_flag lhs, op_flag rhs) - { - return op{static_cast(static_cast(lhs) | static_cast(rhs))}; - } - - constexpr op operator |(op_flag lhs, op rhs) - { - return op{static_cast(static_cast(lhs) | static_cast(rhs))}; - } - - constexpr op operator |(op lhs, op_flag rhs) - { - return op{static_cast(static_cast(lhs) | static_cast(rhs))}; - } - - constexpr op op_ne = op::eq | op_flag::inverse; - constexpr struct any_value_t { template @@ -186,46 +153,10 @@ namespace atomic_wait } } any_value; - template - using payload_type = decltype(std::declval().observe()); - - template > - constexpr u128 default_mask = sizeof(T) <= 8 ? u128{u64{umax} >> ((64 - sizeof(T) * 8) & 63)} : u128(-1); - - template > - constexpr u128 get_value(X&, T value = T{}, ...) - { - static_assert((sizeof(T) & (sizeof(T) - 1)) == 0); - static_assert(sizeof(T) <= 16); - return std::bit_cast, T>(value); - } - struct info { const void* data; - u32 size; - u128 old; - u128 mask; - - template > - constexpr void set_value(X& a, T value = T{}) - { - old = get_value(a, value); - } - - template > - constexpr void set_mask(T value) - { - static_assert((sizeof(T) & (sizeof(T) - 1)) == 0); - static_assert(sizeof(T) <= 16); - mask = std::bit_cast, T>(value); - } - - template > - constexpr void set_mask() - { - mask = default_mask; - } + u32 old; }; template @@ -243,9 +174,9 @@ namespace atomic_wait constexpr list& operator=(const list&) noexcept = default; - template ().template wait(any_value))...>> + template ().wait(any_value))...>> constexpr list(U&... vars) - : m_info{{&vars, sizeof(vars.observe()), get_value(vars), default_mask}...} + : m_info{{&vars, 0}...} { static_assert(sizeof...(U) == Max, "Inconsistent amount of atomics."); } @@ -256,40 +187,37 @@ namespace atomic_wait static_assert(sizeof...(U) == Max, "Inconsistent amount of values."); auto* ptr = m_info; - ((ptr->template set_value(*static_cast(ptr->data), values), ptr++), ...); + (((ptr->old = std::bit_cast(values)), ptr++), ...); return *this; } - template - constexpr list& masks(U... masks) - { - static_assert(sizeof...(U) <= Max, "Too many masks."); - - auto* ptr = m_info; - ((ptr++)->template set_mask(masks), ...); - return *this; - } - - template ().template wait(any_value))>> + template ().wait(any_value))>> constexpr void set(T2& var, U value) { static_assert(Index < Max); m_info[Index].data = &var; - m_info[Index].size = sizeof(var.observe()) | (static_cast(Flags) << 8); - m_info[Index].template set_value(var, value); - m_info[Index].template set_mask(); + m_info[Index].old = std::bit_cast(value); } - template ().template wait(any_value))>> - constexpr void set(T2& var, U value, V mask) + template + constexpr void set(lf_queue& var, std::nullptr_t = nullptr) { static_assert(Index < Max); + static_assert(sizeof(var) == sizeof(uptr)); - m_info[Index].data = &var; - m_info[Index].size = sizeof(var.observe()) | (static_cast(Flags) << 8); - m_info[Index].template set_value(var, value); - m_info[Index].template set_mask(mask); + m_info[Index].data = reinterpret_cast(&var) + sizeof(u32); + m_info[Index].old = 0; + } + + template + constexpr void set(stx::atomic_ptr& var, std::nullptr_t = nullptr) + { + static_assert(Index < Max); + static_assert(sizeof(var) == sizeof(uptr)); + + m_info[Index].data = reinterpret_cast(&var) + sizeof(u32); + m_info[Index].old = 0; } // Timeout is discouraged @@ -302,7 +230,7 @@ namespace atomic_wait } }; - template ().template wait(any_value))...>> + template ().wait(any_value))...>> list(T&... vars) -> list; } @@ -322,20 +250,15 @@ private: template friend class atomic_wait::list; - static void wait(const void* data, u32 size, u128 old_value, u64 timeout, u128 mask, atomic_wait::info* ext = nullptr); + static void wait(const void* data, u32 old_value, u64 timeout, atomic_wait::info* ext = nullptr); public: - static void notify_one(const void* data, u32 size, u128 mask128); - static void notify_all(const void* data, u32 size, u128 mask128); + static void notify_one(const void* data); + static void notify_all(const void* data); static void set_wait_callback(bool(*cb)(const void* data, u64 attempts, u64 stamp0)); static void set_notify_callback(void(*cb)(const void* data, u64 progress)); - static void set_one_time_use_wait_callback(bool(*cb)(u64 progress)); - - static void notify_all(const void* data) - { - notify_all(data, 0, u128(-1)); - } + static void set_one_time_use_wait_callback(bool (*cb)(u64 progress)); }; template @@ -343,7 +266,7 @@ void atomic_wait::list::wait(atomic_wait_timeout timeout) { static_assert(!!Max, "Cannot initiate atomic wait with empty list."); - atomic_wait_engine::wait(m_info[0].data, m_info[0].size, m_info[0].old, static_cast(timeout), m_info[0].mask, m_info + 1); + atomic_wait_engine::wait(m_info[0].data, m_info[0].old, static_cast(timeout), m_info + 1); } // Helper class, provides access to compiler-specific atomic intrinsics @@ -1759,46 +1682,31 @@ public: }); } - // Timeout is discouraged - template - void wait(type old_value, atomic_wait_timeout timeout = atomic_wait_timeout::inf) const noexcept + void wait(type old_value, atomic_wait_timeout timeout = atomic_wait_timeout::inf) const + requires(sizeof(type) == 4) { - const u128 old = std::bit_cast>(old_value); - const u128 mask = atomic_wait::default_mask; - atomic_wait_engine::wait(&m_data, sizeof(T) | (static_cast(Flags) << 8), old, static_cast(timeout), mask); + atomic_wait_engine::wait(&m_data, std::bit_cast(old_value), static_cast(timeout)); } - // Overload with mask (only selected bits are checked), timeout is discouraged - template - void wait(type old_value, type mask_value, atomic_wait_timeout timeout = atomic_wait_timeout::inf) const noexcept + [[deprecated]] void wait(type old_value, atomic_wait_timeout timeout = atomic_wait_timeout::inf) const + requires(sizeof(type) == 8) { - const u128 old = std::bit_cast>(old_value); - const u128 mask = std::bit_cast>(mask_value); - atomic_wait_engine::wait(&m_data, sizeof(T) | (static_cast(Flags) << 8), old, static_cast(timeout), mask); + atomic_wait::info ext[2]{}; + ext[0].data = reinterpret_cast(&m_data) + 4; + ext[0].old = std::bit_cast(old_value) >> 32; + atomic_wait_engine::wait(&m_data, std::bit_cast(old_value), static_cast(timeout), ext); } - void notify_one() noexcept + void notify_one() + requires(sizeof(type) == 4 || sizeof(type) == 8) { - atomic_wait_engine::notify_one(&m_data, sizeof(T), atomic_wait::default_mask); + atomic_wait_engine::notify_one(&m_data); } - // Notify with mask, allowing to not wake up thread which doesn't wait on this mask - void notify_one(type mask_value) noexcept + void notify_all() + requires(sizeof(type) == 4 || sizeof(type) == 8) { - const u128 mask = std::bit_cast>(mask_value); - atomic_wait_engine::notify_one(&m_data, sizeof(T), mask); - } - - void notify_all() noexcept - { - atomic_wait_engine::notify_all(&m_data, sizeof(T), atomic_wait::default_mask); - } - - // Notify all threads with mask, allowing to not wake up threads which don't wait on them - void notify_all(type mask_value) noexcept - { - const u128 mask = std::bit_cast>(mask_value); - atomic_wait_engine::notify_all(&m_data, sizeof(T), mask); + atomic_wait_engine::notify_all(&m_data); } }; @@ -1874,23 +1782,6 @@ public: { return base::fetch_xor(1) != 0; } - - // Timeout is discouraged - template - void wait(bool old_value, atomic_wait_timeout timeout = atomic_wait_timeout::inf) const noexcept - { - base::template wait(old_value, 1, timeout); - } - - void notify_one() noexcept - { - base::notify_one(1); - } - - void notify_all() noexcept - { - base::notify_all(1); - } }; // Specializations @@ -1904,12 +1795,6 @@ struct std::common_type, T2> : std::common_type struct std::common_type> : std::common_type, T2> {}; -namespace atomic_wait -{ - template - constexpr u128 default_mask> = 1; -} - #ifndef _MSC_VER #pragma GCC diagnostic pop #pragma GCC diagnostic pop diff --git a/rpcs3/util/bless.hpp b/rpcs3/util/bless.hpp new file mode 100644 index 0000000000..49ffa8facb --- /dev/null +++ b/rpcs3/util/bless.hpp @@ -0,0 +1,21 @@ +#pragma once + +namespace utils +{ + // Hack. Pointer cast util to workaround UB. Use with extreme care. + template + [[nodiscard]] T* bless(U* ptr) + { +#ifdef _MSC_VER + return (T*)ptr; +#elif defined(ARCH_X64) + T* result; + __asm__("movq %1, %0;" : "=r" (result) : "r" (ptr) : "memory"); + return result; +#elif defined(ARCH_ARM64) + T* result; + __asm__("mov %0, %1" : "=r" (result) : "r" (ptr) : "memory"); + return result; +#endif + } +} diff --git a/rpcs3/util/endian.hpp b/rpcs3/util/endian.hpp index b9d1b19d0c..c001419ea1 100644 --- a/rpcs3/util/endian.hpp +++ b/rpcs3/util/endian.hpp @@ -35,7 +35,9 @@ namespace stx static constexpr u16 swap(u16 src) noexcept { -#if defined(__GNUG__) +#if __cpp_lib_byteswap >= 202110L + return std::byteswap(src); +#elif defined(__GNUG__) return __builtin_bswap16(src); #else if (std::is_constant_evaluated()) @@ -55,7 +57,9 @@ namespace stx static constexpr u32 swap(u32 src) noexcept { -#if defined(__GNUG__) +#if __cpp_lib_byteswap >= 202110L + return std::byteswap(src); +#elif defined(__GNUG__) return __builtin_bswap32(src); #else if (std::is_constant_evaluated()) @@ -76,7 +80,9 @@ namespace stx static constexpr u64 swap(u64 src) noexcept { -#if defined(__GNUG__) +#if __cpp_lib_byteswap >= 202110L + return std::byteswap(src); +#elif defined(__GNUG__) return __builtin_bswap64(src); #else if (std::is_constant_evaluated()) diff --git a/rpcs3/util/fifo_mutex.hpp b/rpcs3/util/fifo_mutex.hpp index cf2d9b0d0e..cddb7b11cf 100644 --- a/rpcs3/util/fifo_mutex.hpp +++ b/rpcs3/util/fifo_mutex.hpp @@ -6,33 +6,35 @@ // Mutex that tries to maintain the order of acquisition class fifo_mutex { - // Low 8 bits are incremented on acquisition, high 8 bits are incremented on release - atomic_t m_value{0}; + // Low 16 bits are incremented on acquisition, high 16 bits are incremented on release + atomic_t m_value{0}; public: constexpr fifo_mutex() noexcept = default; void lock() noexcept { - const u16 val = m_value.fetch_op([](u16& val) + // clang-format off + const u32 val = m_value.fetch_op([](u32& val) { - val = (val & 0xff00) | ((val + 1) & 0xff); + val = (val & 0xffff0000) | ((val + 1) & 0xffff); }); + // clang-format on - if (val >> 8 != (val & 0xff)) [[unlikely]] + if (val >> 16 != (val & 0xffff)) [[unlikely]] { // TODO: implement busy waiting along with moving to cpp file - m_value.wait(((val + 1) & 0xff) << 8, 0xff00); + m_value.wait((val & 0xffff0000) | ((val + 1) & 0xffff)); } } bool try_lock() noexcept { - const u16 val = m_value.load(); + const u32 val = m_value.load(); - if (val >> 8 == (val & 0xff)) + if (val >> 16 == (val & 0xffff)) { - if (m_value.compare_and_swap(val, ((val + 1) & 0xff) | (val & 0xff00))) + if (m_value.compare_and_swap(val, ((val + 1) & 0xffff) | (val & 0xffff0000))) { return true; } @@ -43,9 +45,9 @@ public: void unlock() noexcept { - const u16 val = m_value.add_fetch(0x100); + const u32 val = m_value.add_fetch(0x10000); - if (val >> 8 != (val & 0xff)) + if (val >> 16 != (val & 0xffff)) { m_value.notify_one(); } @@ -53,9 +55,9 @@ public: bool is_free() const noexcept { - const u16 val = m_value.load(); + const u32 val = m_value.load(); - return (val >> 8) == (val & 0xff); + return (val >> 16) == (val & 0xffff); } void lock_unlock() noexcept diff --git a/rpcs3/util/fixed_typemap.hpp b/rpcs3/util/fixed_typemap.hpp index 896d8ae779..3c2ee8e7f2 100644 --- a/rpcs3/util/fixed_typemap.hpp +++ b/rpcs3/util/fixed_typemap.hpp @@ -347,7 +347,7 @@ namespace stx } // Check if object is not initialized but shall be initialized first (to use in initializing other objects) - template + template requires (std::is_constructible_v || std::is_default_constructible_v) void need() noexcept { if (!m_init[stx::typeindex>()]) diff --git a/rpcs3/util/image_sink.h b/rpcs3/util/image_sink.h index 831232fa30..3c23eca514 100644 --- a/rpcs3/util/image_sink.h +++ b/rpcs3/util/image_sink.h @@ -15,7 +15,7 @@ namespace utils image_sink() = default; virtual void stop(bool flush = true) = 0; - virtual void add_frame(std::vector& frame, const u32 width, const u32 height, s32 pixel_format, usz timestamp_ms) = 0; + virtual void add_frame(std::vector& frame, u32 pitch, u32 width, u32 height, s32 pixel_format, usz timestamp_ms) = 0; s64 get_pts(usz timestamp_ms) const { @@ -32,12 +32,13 @@ namespace utils struct encoder_frame { encoder_frame() = default; - encoder_frame(usz timestamp_ms, u32 width, u32 height, s32 av_pixel_format, std::vector&& data) - : timestamp_ms(timestamp_ms), width(width), height(height), av_pixel_format(av_pixel_format), data(std::move(data)) + encoder_frame(usz timestamp_ms, u32 pitch, u32 width, u32 height, s32 av_pixel_format, std::vector&& data) + : timestamp_ms(timestamp_ms), pitch(pitch), width(width), height(height), av_pixel_format(av_pixel_format), data(std::move(data)) {} s64 pts = -1; // Optional usz timestamp_ms = 0; + u32 pitch = 0; u32 width = 0; u32 height = 0; s32 av_pixel_format = 0; // NOTE: Make sure this is a valid AVPixelFormat diff --git a/rpcs3/util/media_utils.cpp b/rpcs3/util/media_utils.cpp index 7eb16a7878..3dddd5b0f8 100644 --- a/rpcs3/util/media_utils.cpp +++ b/rpcs3/util/media_utils.cpp @@ -259,8 +259,8 @@ namespace utils void audio_decoder::clear() { - track_fully_decoded = false; - track_fully_consumed = false; + track_fully_decoded = 0; + track_fully_consumed = 0; has_error = false; m_size = 0; duration_ms = 0; @@ -274,7 +274,7 @@ namespace utils { auto& thread = *m_thread; thread = thread_state::aborting; - track_fully_consumed = true; + track_fully_consumed = 1; track_fully_consumed.notify_one(); thread(); m_thread.reset(); @@ -363,14 +363,14 @@ namespace utils } const int dst_channels = 2; - const u64 dst_channel_layout = AV_CH_LAYOUT_STEREO; + const AVChannelLayout dst_channel_layout = AV_CHANNEL_LAYOUT_STEREO; const AVSampleFormat dst_format = AV_SAMPLE_FMT_FLT; int set_err = 0; - if ((set_err = av_opt_set_int(av.swr, "in_channel_count", stream->codecpar->channels, 0)) || + if ((set_err = av_opt_set_int(av.swr, "in_channel_count", stream->codecpar->ch_layout.nb_channels, 0)) || (set_err = av_opt_set_int(av.swr, "out_channel_count", dst_channels, 0)) || - (set_err = av_opt_set_channel_layout(av.swr, "in_channel_layout", stream->codecpar->channel_layout, 0)) || - (set_err = av_opt_set_channel_layout(av.swr, "out_channel_layout", dst_channel_layout, 0)) || + (set_err = av_opt_set_chlayout(av.swr, "in_channel_layout", &stream->codecpar->ch_layout, 0)) || + (set_err = av_opt_set_chlayout(av.swr, "out_channel_layout", &dst_channel_layout, 0)) || (set_err = av_opt_set_int(av.swr, "in_sample_rate", stream->codecpar->sample_rate, 0)) || (set_err = av_opt_set_int(av.swr, "out_sample_rate", sample_rate, 0)) || (set_err = av_opt_set_sample_fmt(av.swr, "in_sample_fmt", static_cast(stream->codecpar->format), 0)) || @@ -511,7 +511,7 @@ namespace utils media_log.notice("audio_decoder: about to decode: %s (index=%d)", ::at32(m_context.playlist, m_context.current_track), m_context.current_track); decode_track(::at32(m_context.playlist, m_context.current_track)); - track_fully_decoded = true; + track_fully_decoded = 1; if (has_error) { @@ -521,7 +521,7 @@ namespace utils // Let's only decode one track at a time. Wait for the consumer to finish reading the track. media_log.notice("audio_decoder: waiting until track is consumed..."); - thread_ctrl::wait_on(track_fully_consumed, false); + thread_ctrl::wait_on(track_fully_consumed, 0); track_fully_consumed = false; } @@ -604,14 +604,14 @@ namespace utils m_audio_codec_id = codec_id; } - void video_encoder::add_frame(std::vector& frame, const u32 width, const u32 height, s32 pixel_format, usz timestamp_ms) + void video_encoder::add_frame(std::vector& frame, u32 pitch, u32 width, u32 height, s32 pixel_format, usz timestamp_ms) { // Do not allow new frames while flushing if (m_flush) return; std::lock_guard lock(m_mtx); - m_frames_to_encode.emplace_back(timestamp_ms, width, height, pixel_format, std::move(frame)); + m_frames_to_encode.emplace_back(timestamp_ms, pitch, width, height, pixel_format, std::move(frame)); } void video_encoder::pause(bool flush) @@ -710,15 +710,42 @@ namespace utils if (!codec) return nullptr; + // Try to find a preferable output format + std::vector oformats; + void* opaque = nullptr; for (const AVOutputFormat* oformat = av_muxer_iterate(&opaque); !!oformat; oformat = av_muxer_iterate(&opaque)) { if (avformat_query_codec(oformat, codec->id, FF_COMPLIANCE_STRICT) == 1) { - return oformat->name; + media_log.notice("video_encoder: Found output format '%s'", oformat->name); + + switch (codec->id) + { + case AV_CODEC_ID_MPEG4: + if (strcmp(oformat->name, "avi") == 0) + return oformat->name; + break; + case AV_CODEC_ID_H264: + case AV_CODEC_ID_MJPEG: + // TODO + break; + default: + break; + } + + oformats.push_back(oformat); } } + // Fallback to first found format + if (!oformats.empty() && oformats.front()) + { + const AVOutputFormat* oformat = oformats.front(); + media_log.notice("video_encoder: Falling back to output format '%s'", oformat->name); + return oformat->name; + } + return nullptr; }; diff --git a/rpcs3/util/media_utils.h b/rpcs3/util/media_utils.h index 6c450a89d3..2718a80617 100644 --- a/rpcs3/util/media_utils.h +++ b/rpcs3/util/media_utils.h @@ -77,8 +77,8 @@ namespace utils std::vector data; atomic_t m_size = 0; atomic_t duration_ms = 0; - atomic_t track_fully_decoded{false}; - atomic_t track_fully_consumed{false}; + atomic_t track_fully_decoded{0}; + atomic_t track_fully_consumed{0}; atomic_t has_error{false}; std::deque> timestamps_ms; @@ -120,7 +120,7 @@ namespace utils void set_sample_rate(u32 sample_rate); void set_audio_bitrate(u32 bitrate); void set_audio_codec(s32 codec_id); - void add_frame(std::vector& frame, const u32 width, const u32 height, s32 pixel_format, usz timestamp_ms) override; + void add_frame(std::vector& frame, u32 pitch, u32 width, u32 height, s32 pixel_format, usz timestamp_ms) override; void pause(bool flush = true); void stop(bool flush = true) override; void encode(); diff --git a/rpcs3/util/shared_ptr.hpp b/rpcs3/util/shared_ptr.hpp index 6e3e9dbfb9..109e28216a 100644 --- a/rpcs3/util/shared_ptr.hpp +++ b/rpcs3/util/shared_ptr.hpp @@ -3,6 +3,7 @@ #include #include #include "atomic.hpp" +#include "bless.hpp" namespace stx { @@ -21,7 +22,7 @@ namespace stx // Basic assumption of userspace pointer size constexpr uint c_ptr_size = 48; - // Use lower 17 bits as atomic_ptr internal counter of borrowed refs (pointer itself is shifted) + // Use lower 16 bits as atomic_ptr internal counter of borrowed refs (pointer itself is shifted) constexpr uint c_ref_mask = 0xffff, c_ref_size = 16; // Remaining pointer bits @@ -1054,20 +1055,19 @@ namespace stx return observe() == r.get(); } - template - void wait(const volatile void* value, atomic_wait_timeout timeout = atomic_wait_timeout::inf) + void wait(std::nullptr_t, atomic_wait_timeout timeout = atomic_wait_timeout::inf) { - m_val.wait(reinterpret_cast(value) << c_ref_size, c_ptr_mask, timeout); + utils::bless>(&m_val)[1].wait(0, timeout); } void notify_one() { - m_val.notify_one(c_ptr_mask); + utils::bless>(&m_val)[1].notify_one(); } void notify_all() { - m_val.notify_all(c_ptr_mask); + utils::bless>(&m_val)[1].notify_all(); } }; @@ -1110,18 +1110,6 @@ namespace stx } null_ptr; } -namespace atomic_wait -{ - template - constexpr u128 default_mask> = stx::c_ptr_mask; - - template - constexpr u128 get_value(stx::atomic_ptr&, const volatile void* value = nullptr) - { - return reinterpret_cast(value) << stx::c_ref_size; - } -} - using stx::null_ptr; using stx::single_ptr; using stx::shared_ptr; diff --git a/rpcs3/util/simd.hpp b/rpcs3/util/simd.hpp index e915015cd2..eb08de5aa0 100644 --- a/rpcs3/util/simd.hpp +++ b/rpcs3/util/simd.hpp @@ -2988,6 +2988,24 @@ inline v128 gv_rol32(const v128& a, const v128& b) #endif } +// For each 32-bit element, r = rotate a by count +inline v128 gv_rol32(const v128& a, u32 count) +{ + count %= 32; +#if defined(ARCH_X64) + return _mm_or_epi32(_mm_srli_epi32(a, 32 - count), _mm_slli_epi32(a, count)); +#elif defined(ARCH_ARM64) + const auto amt1 = vdupq_n_s32(count); + const auto amt2 = vdupq_n_s32(count - 32); + return vorrq_u32(vshlq_u32(a, amt1), vshlq_u32(a, amt2)); +#else + v128 r; + for (u32 i = 0; i < 4; i++) + r._u32[i] = utils::rol32(a._u32[i], count); + return r; +#endif +} + // For each 8-bit element, r = (a << (c & 7)) | (b >> (~c & 7) >> 1) template inline auto gv_fshl8(A&& a, B&& b, C&& c) diff --git a/rpcs3/util/sysinfo.cpp b/rpcs3/util/sysinfo.cpp index d6aff4f78d..108450ab30 100755 --- a/rpcs3/util/sysinfo.cpp +++ b/rpcs3/util/sysinfo.cpp @@ -177,6 +177,71 @@ bool utils::has_avx512_vnni() #endif } +bool utils::has_avx10() +{ +#if defined(ARCH_X64) + // Implies support for most AVX-512 instructions + static const bool g_value = get_cpuid(0, 0)[0] >= 0x7 && get_cpuid(7, 1)[3] & 0x80000; + return g_value; +#else + return false; +#endif +} + +bool utils::has_avx10_512() +{ +#if defined(ARCH_X64) + // AVX10 with 512 wide vectors + static const bool g_value = has_avx10() && get_cpuid(24, 0)[2] & 0x40000; + return g_value; +#else + return false; +#endif +} + +u32 utils::avx10_isa_version() +{ +#if defined(ARCH_X64) + // 8bit value + static const u32 g_value = []() + { + u32 isa_version = 0; + if (has_avx10()) + { + isa_version = get_cpuid(24, 0)[2] & 0x000ff; + } + + return isa_version; + }(); + + return g_value; +#else + return 0; +#endif +} + +bool utils::has_avx512_256() +{ +#if defined(ARCH_X64) + // Either AVX10 or AVX512 implies support for 256-bit length AVX-512 SKL-X tier instructions + static const bool g_value = (has_avx512() || has_avx10()); + return g_value; +#else + return false; +#endif +} + +bool utils::has_avx512_icl_256() +{ +#if defined(ARCH_X64) + // Check for AVX512_ICL or check for AVX10, together with GFNI, VAES, and VPCLMULQDQ, implies support for the same instructions that AVX-512_icl does at 256 bit length + static const bool g_value = (has_avx512_icl() || (has_avx10() && get_cpuid(7, 0)[2] & 0x00000700)); + return g_value; +#else + return false; +#endif +} + bool utils::has_xop() { #if defined(ARCH_X64) @@ -233,7 +298,7 @@ bool utils::has_fma4() bool utils::has_fast_vperm2b() { #if defined(ARCH_X64) - static const bool g_value = has_avx512() && (get_cpuid(7, 0)[2] & 0x2) == 0x2 && get_cpuid(0, 0)[0] >= 0x7 && (get_cpuid(0x80000001, 0)[2] & 0x20) == 0x20; + static const bool g_value = has_avx512() && (get_cpuid(7, 0)[2] & 0x2) == 0x2 && get_cpuid(0, 0)[0] >= 0x7 && (get_cpuid(0x80000001, 0)[2] & 0x40) == 0x40; return g_value; #else return false; @@ -260,6 +325,46 @@ bool utils::has_fsrm() #endif } +bool utils::has_waitx() +{ +#if defined(ARCH_X64) + static const bool g_value = get_cpuid(0, 0)[0] >= 0x7 && (get_cpuid(0x80000001, 0)[2] & 0x20000000) == 0x20000000; + return g_value; +#else + return false; +#endif +} + +bool utils::has_waitpkg() +{ +#if defined(ARCH_X64) + static const bool g_value = get_cpuid(0, 0)[0] >= 0x7 && (get_cpuid(7, 0)[2] & 0x20) == 0x20; + return g_value; +#else + return false; +#endif +} + +// User mode waits may be unfriendly to low thread CPUs +// Filter out systems with less than 8 threads for linux and less than 12 threads for other platforms +bool utils::has_appropriate_um_wait() +{ +#ifdef __linux__ + static const bool g_value = (has_waitx() || has_waitpkg()) && (get_thread_count() >= 8) && get_tsc_freq(); + return g_value; +#else + static const bool g_value = (has_waitx() || has_waitpkg()) && (get_thread_count() >= 12) && get_tsc_freq(); + return g_value; +#endif +} + +// Similar to the above function but allow execution if alternatives such as yield are not wanted +bool utils::has_um_wait() +{ + static const bool g_value = (has_waitx() || has_waitpkg()) && get_tsc_freq(); + return g_value; +} + u32 utils::get_rep_movsb_threshold() { static const u32 g_value = []() @@ -335,7 +440,21 @@ std::string utils::get_system_info() { result += " | AVX"; - if (has_avx512()) + if (has_avx10()) + { + const u32 avx10_version = avx10_isa_version(); + fmt::append(result, "10.%d", avx10_version); + + if (has_avx10_512()) + { + result += "-512"; + } + else + { + result += "-256"; + } + } + else if (has_avx512()) { result += "-512"; diff --git a/rpcs3/util/sysinfo.hpp b/rpcs3/util/sysinfo.hpp index 951c961bb6..d7c7c2ec0e 100755 --- a/rpcs3/util/sysinfo.hpp +++ b/rpcs3/util/sysinfo.hpp @@ -27,6 +27,16 @@ namespace utils bool has_avx512_vnni(); + bool has_avx10(); + + bool has_avx10_512(); + + u32 avx10_isa_version(); + + bool has_avx512_256(); + + bool has_avx512_icl_256(); + bool has_xop(); bool has_clwb(); @@ -43,6 +53,14 @@ namespace utils bool has_fsrm(); + bool has_waitx(); + + bool has_waitpkg(); + + bool has_appropriate_um_wait(); + + bool has_um_wait(); + std::string get_cpu_brand(); std::string get_system_info(); diff --git a/rpcs3/util/video_provider.cpp b/rpcs3/util/video_provider.cpp index cf3e910f93..d919137733 100644 --- a/rpcs3/util/video_provider.cpp +++ b/rpcs3/util/video_provider.cpp @@ -92,7 +92,7 @@ namespace utils return pts > m_last_pts_incoming; } - void video_provider::present_frame(std::vector& data, const u32 width, const u32 height, bool is_bgra) + void video_provider::present_frame(std::vector& data, u32 pitch, u32 width, u32 height, bool is_bgra) { std::lock_guard lock(m_mutex); @@ -132,6 +132,6 @@ namespace utils m_last_pts_incoming = pts; m_current_encoder_frame++; - m_image_sink->add_frame(data, width, height, is_bgra ? AVPixelFormat::AV_PIX_FMT_BGRA : AVPixelFormat::AV_PIX_FMT_RGBA, timestamp_ms); + m_image_sink->add_frame(data, pitch, width, height, is_bgra ? AVPixelFormat::AV_PIX_FMT_BGRA : AVPixelFormat::AV_PIX_FMT_RGBA, timestamp_ms); } } diff --git a/rpcs3/util/video_provider.h b/rpcs3/util/video_provider.h index 8fd4e12483..31a051a112 100644 --- a/rpcs3/util/video_provider.h +++ b/rpcs3/util/video_provider.h @@ -20,7 +20,7 @@ namespace utils bool set_image_sink(std::shared_ptr sink, recording_mode type); void set_pause_time(usz pause_time_ms); bool can_consume_frame(); - void present_frame(std::vector& data, const u32 width, const u32 height, bool is_bgra); + void present_frame(std::vector& data, u32 pitch, u32 width, u32 height, bool is_bgra); private: recording_mode m_type = recording_mode::stopped; diff --git a/rpcs3/util/vm_native.cpp b/rpcs3/util/vm_native.cpp index 14bff5cdb4..6474ef68fd 100644 --- a/rpcs3/util/vm_native.cpp +++ b/rpcs3/util/vm_native.cpp @@ -86,14 +86,6 @@ namespace utils constexpr int c_mfd_huge_2mb = 0; #endif -#ifndef MEM_RESERVE_PLACEHOLDER -#define MEM_RESERVE_PLACEHOLDER 0x00040000 -#endif - -#ifndef MEM_REPLACE_PLACEHOLDER -#define MEM_REPLACE_PLACEHOLDER 0x00004000 -#endif - #ifdef _WIN32 DYNAMIC_IMPORT("KernelBase.dll", VirtualAlloc2, PVOID(HANDLE Process, PVOID Base, SIZE_T Size, ULONG AllocType, ULONG Prot, MEM_EXTENDED_PARAMETER*, ULONG)); DYNAMIC_IMPORT("KernelBase.dll", MapViewOfFile3, PVOID(HANDLE Handle, HANDLE Process, PVOID Base, ULONG64 Off, SIZE_T ViewSize, ULONG AllocType, ULONG Prot, MEM_EXTENDED_PARAMETER*, ULONG));